Simplify TransactionBuilder
A stepping stone to replacing all Transaction constructors with TransactionBuilders.
This commit is contained in:
parent
c6cb3bb0bc
commit
94882418ab
|
@ -227,6 +227,18 @@ impl Transaction {
|
||||||
.collect();
|
.collect();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Check keys and keypair lengths, then sign this transaction.
|
||||||
|
/// Note: this presumes signatures.capacity() was set to the number of required signatures.
|
||||||
|
pub fn sign_checked<T: KeypairUtil>(&mut self, keypairs: &[&T], recent_blockhash: Hash) {
|
||||||
|
let signed_keys = &self.account_keys[0..self.signatures.capacity()];
|
||||||
|
for (i, keypair) in keypairs.iter().enumerate() {
|
||||||
|
assert_eq!(keypair.pubkey(), signed_keys[i], "keypair-pubkey mismatch");
|
||||||
|
}
|
||||||
|
assert_eq!(keypairs.len(), signed_keys.len(), "not enough keypairs");
|
||||||
|
|
||||||
|
self.sign(keypairs, recent_blockhash);
|
||||||
|
}
|
||||||
|
|
||||||
/// Verify only the transaction signature.
|
/// Verify only the transaction signature.
|
||||||
pub fn verify_signature(&self) -> bool {
|
pub fn verify_signature(&self) -> bool {
|
||||||
self.signatures
|
self.signatures
|
||||||
|
|
|
@ -12,8 +12,8 @@ fn position(keys: &[Pubkey], key: Pubkey) -> u8 {
|
||||||
keys.iter().position(|&k| k == key).unwrap() as u8
|
keys.iter().position(|&k| k == key).unwrap() as u8
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_indexed_instruction(
|
fn compile_instruction(
|
||||||
ix: &Instruction<Pubkey, (Pubkey, bool)>,
|
ix: &BuilderInstruction,
|
||||||
keys: &[Pubkey],
|
keys: &[Pubkey],
|
||||||
program_ids: &[Pubkey],
|
program_ids: &[Pubkey],
|
||||||
) -> Instruction<u8, u8> {
|
) -> Instruction<u8, u8> {
|
||||||
|
@ -29,6 +29,16 @@ fn create_indexed_instruction(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn compile_instructions(
|
||||||
|
ixs: &[BuilderInstruction],
|
||||||
|
keys: &[Pubkey],
|
||||||
|
program_ids: &[Pubkey],
|
||||||
|
) -> Vec<Instruction<u8, u8>> {
|
||||||
|
ixs.iter()
|
||||||
|
.map(|ix| compile_instruction(ix, keys, program_ids))
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
/// A utility for constructing transactions
|
/// A utility for constructing transactions
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct TransactionBuilder {
|
pub struct TransactionBuilder {
|
||||||
|
@ -82,25 +92,17 @@ impl TransactionBuilder {
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return the instructions, but indexing lists of keys and program ids.
|
/// Return an unsigned transaction with space for requires signatures.
|
||||||
fn instructions(&self, keys: &[Pubkey], program_ids: &[Pubkey]) -> Vec<Instruction<u8, u8>> {
|
pub fn compile(&self) -> Transaction {
|
||||||
self.instructions
|
|
||||||
.iter()
|
|
||||||
.map(|ix| create_indexed_instruction(ix, keys, program_ids))
|
|
||||||
.collect()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Return an unsigned transaction that requires signatures. To more safely return an unsigned
|
|
||||||
/// transaction that *doesn't* require signatures, pass `sign()` zero keypairs.
|
|
||||||
pub fn unsigned(&self, recent_blockhash: Hash) -> Transaction {
|
|
||||||
let program_ids = self.program_ids();
|
let program_ids = self.program_ids();
|
||||||
let (mut signed_keys, unsigned_keys) = self.keys();
|
let (mut signed_keys, unsigned_keys) = self.keys();
|
||||||
|
let signed_len = signed_keys.len();
|
||||||
signed_keys.extend(&unsigned_keys);
|
signed_keys.extend(&unsigned_keys);
|
||||||
let instructions = self.instructions(&signed_keys, &program_ids);
|
let instructions = compile_instructions(&self.instructions, &signed_keys, &program_ids);
|
||||||
Transaction {
|
Transaction {
|
||||||
signatures: vec![],
|
signatures: Vec::with_capacity(signed_len),
|
||||||
account_keys: signed_keys,
|
account_keys: signed_keys,
|
||||||
recent_blockhash,
|
recent_blockhash: Hash::default(),
|
||||||
fee: self.fee,
|
fee: self.fee,
|
||||||
program_ids,
|
program_ids,
|
||||||
instructions,
|
instructions,
|
||||||
|
@ -109,14 +111,8 @@ impl TransactionBuilder {
|
||||||
|
|
||||||
/// Return a signed transaction.
|
/// Return a signed transaction.
|
||||||
pub fn sign<T: KeypairUtil>(&self, keypairs: &[&T], recent_blockhash: Hash) -> Transaction {
|
pub fn sign<T: KeypairUtil>(&self, keypairs: &[&T], recent_blockhash: Hash) -> Transaction {
|
||||||
let signed_keys = self.keys().0;
|
let mut tx = self.compile();
|
||||||
for (i, keypair) in keypairs.iter().enumerate() {
|
tx.sign_checked(keypairs, recent_blockhash);
|
||||||
assert_eq!(keypair.pubkey(), signed_keys[i], "keypair-pubkey mismatch");
|
|
||||||
}
|
|
||||||
assert_eq!(keypairs.len(), signed_keys.len(), "not enough keypairs");
|
|
||||||
|
|
||||||
let mut tx = self.unsigned(Hash::default());
|
|
||||||
tx.sign(keypairs, recent_blockhash);
|
|
||||||
tx
|
tx
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -219,6 +215,22 @@ mod tests {
|
||||||
assert_eq!(keys, (vec![id1], vec![id0]));
|
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() {
|
||||||
|
let program_id = Pubkey::default();
|
||||||
|
let id0 = Pubkey::default();
|
||||||
|
let tx = TransactionBuilder::default()
|
||||||
|
.push(Instruction::new(program_id, &0, vec![(id0, false)]))
|
||||||
|
.compile();
|
||||||
|
assert_eq!(tx.signatures.capacity(), 0);
|
||||||
|
|
||||||
|
let tx = TransactionBuilder::default()
|
||||||
|
.push(Instruction::new(program_id, &0, vec![(id0, true)]))
|
||||||
|
.compile();
|
||||||
|
assert_eq!(tx.signatures.capacity(), 1);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[should_panic]
|
#[should_panic]
|
||||||
fn test_transaction_builder_missing_key() {
|
fn test_transaction_builder_missing_key() {
|
||||||
|
|
Loading…
Reference in New Issue