Inline InstructionCompiler
The object-oriented paradigm isn't helpful here; go functional.
This commit is contained in:
parent
4efa144916
commit
5c536e423c
|
@ -1,4 +1,4 @@
|
|||
//! A library for compiling instructions
|
||||
//! A library for generating a message from a sequence of instructions
|
||||
|
||||
use crate::hash::Hash;
|
||||
use crate::instruction::{CompiledInstruction, Instruction};
|
||||
|
@ -10,7 +10,7 @@ fn position(keys: &[Pubkey], key: &Pubkey) -> u8 {
|
|||
}
|
||||
|
||||
fn compile_instruction(
|
||||
ix: &Instruction,
|
||||
ix: Instruction,
|
||||
keys: &[Pubkey],
|
||||
program_ids: &[Pubkey],
|
||||
) -> CompiledInstruction {
|
||||
|
@ -28,31 +28,19 @@ fn compile_instruction(
|
|||
}
|
||||
|
||||
fn compile_instructions(
|
||||
ixs: &[Instruction],
|
||||
ixs: Vec<Instruction>,
|
||||
keys: &[Pubkey],
|
||||
program_ids: &[Pubkey],
|
||||
) -> Vec<CompiledInstruction> {
|
||||
ixs.iter()
|
||||
ixs.into_iter()
|
||||
.map(|ix| compile_instruction(ix, keys, program_ids))
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// A utility for constructing transactions
|
||||
pub struct InstructionCompiler {
|
||||
instructions: Vec<Instruction>,
|
||||
}
|
||||
|
||||
impl InstructionCompiler {
|
||||
/// Create a new unsigned transaction from a single instruction
|
||||
pub fn new(instructions: Vec<Instruction>) -> Self {
|
||||
Self { instructions }
|
||||
}
|
||||
|
||||
/// Return pubkeys referenced by all instructions, with the ones needing signatures first.
|
||||
/// No duplicates and order is preserved.
|
||||
fn keys(&self) -> (Vec<Pubkey>, Vec<Pubkey>) {
|
||||
let mut keys_and_signed: Vec<_> = self
|
||||
.instructions
|
||||
fn get_keys(instructions: &[Instruction]) -> (Vec<Pubkey>, Vec<Pubkey>) {
|
||||
let mut keys_and_signed: Vec<_> = instructions
|
||||
.iter()
|
||||
.flat_map(|ix| ix.accounts.iter())
|
||||
.collect();
|
||||
|
@ -71,32 +59,14 @@ impl InstructionCompiler {
|
|||
}
|
||||
|
||||
/// Return program ids referenced by all instructions. No duplicates and order is preserved.
|
||||
fn program_ids(&self) -> Vec<Pubkey> {
|
||||
self.instructions
|
||||
fn get_program_ids(instructions: &[Instruction]) -> Vec<Pubkey> {
|
||||
instructions
|
||||
.iter()
|
||||
.map(|ix| ix.program_ids_index)
|
||||
.unique()
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// Return an unsigned transaction with space for requires signatures.
|
||||
pub fn compile(&self) -> Message {
|
||||
let program_ids = self.program_ids();
|
||||
let (mut signed_keys, unsigned_keys) = self.keys();
|
||||
let num_signatures = signed_keys.len() as u8;
|
||||
signed_keys.extend(&unsigned_keys);
|
||||
let instructions = compile_instructions(&self.instructions, &signed_keys, &program_ids);
|
||||
Message {
|
||||
num_signatures,
|
||||
account_keys: signed_keys,
|
||||
recent_blockhash: Hash::default(),
|
||||
fee: 0,
|
||||
program_ids,
|
||||
instructions,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Clone)]
|
||||
pub struct Message {
|
||||
pub num_signatures: u8,
|
||||
|
@ -108,8 +78,21 @@ pub struct Message {
|
|||
}
|
||||
|
||||
impl Message {
|
||||
/// Return an unsigned transaction with space for requires signatures.
|
||||
pub fn new(instructions: Vec<Instruction>) -> Self {
|
||||
InstructionCompiler::new(instructions).compile()
|
||||
let program_ids = get_program_ids(&instructions);
|
||||
let (mut signed_keys, unsigned_keys) = get_keys(&instructions);
|
||||
let num_signatures = signed_keys.len() as u8;
|
||||
signed_keys.extend(&unsigned_keys);
|
||||
let instructions = compile_instructions(instructions, &signed_keys, &program_ids);
|
||||
Self {
|
||||
num_signatures,
|
||||
account_keys: signed_keys,
|
||||
recent_blockhash: Hash::default(),
|
||||
fee: 0,
|
||||
program_ids,
|
||||
instructions,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -120,135 +103,135 @@ mod tests {
|
|||
use crate::signature::{Keypair, KeypairUtil};
|
||||
|
||||
#[test]
|
||||
fn test_transaction_builder_unique_program_ids() {
|
||||
fn test_message_unique_program_ids() {
|
||||
let program_id0 = Pubkey::default();
|
||||
let program_ids = InstructionCompiler::new(vec![
|
||||
let program_ids = get_program_ids(&[
|
||||
Instruction::new(program_id0, &0, vec![]),
|
||||
Instruction::new(program_id0, &0, vec![]),
|
||||
])
|
||||
.program_ids();
|
||||
]);
|
||||
assert_eq!(program_ids, vec![program_id0]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_transaction_builder_unique_program_ids_not_adjacent() {
|
||||
fn test_message_unique_program_ids_not_adjacent() {
|
||||
let program_id0 = Pubkey::default();
|
||||
let program_id1 = Keypair::new().pubkey();
|
||||
let program_ids = InstructionCompiler::new(vec![
|
||||
let program_ids = get_program_ids(&[
|
||||
Instruction::new(program_id0, &0, vec![]),
|
||||
Instruction::new(program_id1, &0, vec![]),
|
||||
Instruction::new(program_id0, &0, vec![]),
|
||||
])
|
||||
.program_ids();
|
||||
]);
|
||||
assert_eq!(program_ids, vec![program_id0, program_id1]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_transaction_builder_unique_program_ids_order_preserved() {
|
||||
fn test_message_unique_program_ids_order_preserved() {
|
||||
let program_id0 = Keypair::new().pubkey();
|
||||
let program_id1 = Pubkey::default(); // Key less than program_id0
|
||||
let program_ids = InstructionCompiler::new(vec![
|
||||
let program_ids = get_program_ids(&[
|
||||
Instruction::new(program_id0, &0, vec![]),
|
||||
Instruction::new(program_id1, &0, vec![]),
|
||||
Instruction::new(program_id0, &0, vec![]),
|
||||
])
|
||||
.program_ids();
|
||||
]);
|
||||
assert_eq!(program_ids, vec![program_id0, program_id1]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_transaction_builder_unique_keys_both_signed() {
|
||||
fn test_message_unique_keys_both_signed() {
|
||||
let program_id = Pubkey::default();
|
||||
let id0 = Pubkey::default();
|
||||
let keys = InstructionCompiler::new(vec![
|
||||
let keys = get_keys(&[
|
||||
Instruction::new(program_id, &0, vec![AccountMeta::new(id0, true)]),
|
||||
Instruction::new(program_id, &0, vec![AccountMeta::new(id0, true)]),
|
||||
])
|
||||
.keys();
|
||||
]);
|
||||
assert_eq!(keys, (vec![id0], vec![]));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_transaction_builder_unique_keys_one_signed() {
|
||||
fn test_message_unique_keys_one_signed() {
|
||||
let program_id = Pubkey::default();
|
||||
let id0 = Pubkey::default();
|
||||
let keys = InstructionCompiler::new(vec![
|
||||
let keys = get_keys(&[
|
||||
Instruction::new(program_id, &0, vec![AccountMeta::new(id0, false)]),
|
||||
Instruction::new(program_id, &0, vec![AccountMeta::new(id0, true)]),
|
||||
])
|
||||
.keys();
|
||||
]);
|
||||
assert_eq!(keys, (vec![id0], vec![]));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_transaction_builder_unique_keys_order_preserved() {
|
||||
fn test_message_unique_keys_order_preserved() {
|
||||
let program_id = Pubkey::default();
|
||||
let id0 = Keypair::new().pubkey();
|
||||
let id1 = Pubkey::default(); // Key less than id0
|
||||
let keys = InstructionCompiler::new(vec![
|
||||
let keys = get_keys(&[
|
||||
Instruction::new(program_id, &0, vec![AccountMeta::new(id0, false)]),
|
||||
Instruction::new(program_id, &0, vec![AccountMeta::new(id1, false)]),
|
||||
])
|
||||
.keys();
|
||||
]);
|
||||
assert_eq!(keys, (vec![], vec![id0, id1]));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_transaction_builder_unique_keys_not_adjacent() {
|
||||
fn test_message_unique_keys_not_adjacent() {
|
||||
let program_id = Pubkey::default();
|
||||
let id0 = Pubkey::default();
|
||||
let id1 = Keypair::new().pubkey();
|
||||
let keys = InstructionCompiler::new(vec![
|
||||
let keys = get_keys(&[
|
||||
Instruction::new(program_id, &0, vec![AccountMeta::new(id0, false)]),
|
||||
Instruction::new(program_id, &0, vec![AccountMeta::new(id1, false)]),
|
||||
Instruction::new(program_id, &0, vec![AccountMeta::new(id0, true)]),
|
||||
])
|
||||
.keys();
|
||||
]);
|
||||
assert_eq!(keys, (vec![id0], vec![id1]));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_transaction_builder_signed_keys_first() {
|
||||
fn test_message_signed_keys_first() {
|
||||
let program_id = Pubkey::default();
|
||||
let id0 = Pubkey::default();
|
||||
let id1 = Keypair::new().pubkey();
|
||||
let keys = InstructionCompiler::new(vec![
|
||||
let keys = get_keys(&[
|
||||
Instruction::new(program_id, &0, vec![AccountMeta::new(id0, false)]),
|
||||
Instruction::new(program_id, &0, vec![AccountMeta::new(id1, true)]),
|
||||
])
|
||||
.keys();
|
||||
]);
|
||||
assert_eq!(keys, (vec![id1], vec![id0]));
|
||||
}
|
||||
|
||||
#[test]
|
||||
// Ensure there's a way to calculate the number of required signatures.
|
||||
fn test_transaction_builder_signed_keys_len() {
|
||||
fn test_message_signed_keys_len() {
|
||||
let program_id = Pubkey::default();
|
||||
let id0 = Pubkey::default();
|
||||
let ix = Instruction::new(program_id, &0, vec![AccountMeta::new(id0, false)]);
|
||||
let message = InstructionCompiler::new(vec![ix]).compile();
|
||||
let message = Message::new(vec![ix]);
|
||||
assert_eq!(message.num_signatures, 0);
|
||||
|
||||
let ix = Instruction::new(program_id, &0, vec![AccountMeta::new(id0, true)]);
|
||||
let message = InstructionCompiler::new(vec![ix]).compile();
|
||||
let message = Message::new(vec![ix]);
|
||||
assert_eq!(message.num_signatures, 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_transaction_builder_kitchen_sink() {
|
||||
fn test_message_kitchen_sink() {
|
||||
let program_id0 = Pubkey::default();
|
||||
let program_id1 = Keypair::new().pubkey();
|
||||
let id0 = Pubkey::default();
|
||||
let keypair1 = Keypair::new();
|
||||
let id1 = keypair1.pubkey();
|
||||
let tx = InstructionCompiler::new(vec![
|
||||
let message = Message::new(vec![
|
||||
Instruction::new(program_id0, &0, vec![AccountMeta::new(id0, false)]),
|
||||
Instruction::new(program_id1, &0, vec![AccountMeta::new(id1, true)]),
|
||||
Instruction::new(program_id0, &0, vec![AccountMeta::new(id1, false)]),
|
||||
])
|
||||
.compile();
|
||||
assert_eq!(tx.instructions[0], CompiledInstruction::new(0, &0, vec![1]));
|
||||
assert_eq!(tx.instructions[1], CompiledInstruction::new(1, &0, vec![0]));
|
||||
assert_eq!(tx.instructions[2], CompiledInstruction::new(0, &0, vec![0]));
|
||||
]);
|
||||
assert_eq!(
|
||||
message.instructions[0],
|
||||
CompiledInstruction::new(0, &0, vec![1])
|
||||
);
|
||||
assert_eq!(
|
||||
message.instructions[1],
|
||||
CompiledInstruction::new(1, &0, vec![0])
|
||||
);
|
||||
assert_eq!(
|
||||
message.instructions[2],
|
||||
CompiledInstruction::new(0, &0, vec![0])
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue