Remove fee-payer guesswork from Message and Transaction (#10776)
* Make Message::new_with_payer the default constructor * Remove Transaction::new_[un]signed_instructions These guess the fee-payer instead of stating it explicitly
This commit is contained in:
@ -193,13 +193,13 @@ mod tests {
|
||||
let pubkey0 = Pubkey::new(&[0; 32]);
|
||||
let pubkey1 = Pubkey::new(&[1; 32]);
|
||||
let ix0 = system_instruction::transfer(&pubkey0, &pubkey1, 1);
|
||||
let message = Message::new(&[ix0]);
|
||||
let message = Message::new(&[ix0], Some(&pubkey0));
|
||||
assert_eq!(FeeCalculator::new(2).calculate_fee(&message), 2);
|
||||
|
||||
// Two signatures, double the fee.
|
||||
let ix0 = system_instruction::transfer(&pubkey0, &pubkey1, 1);
|
||||
let ix1 = system_instruction::transfer(&pubkey1, &pubkey0, 1);
|
||||
let message = Message::new(&[ix0, ix1]);
|
||||
let message = Message::new(&[ix0, ix1], Some(&pubkey0));
|
||||
assert_eq!(FeeCalculator::new(2).calculate_fee(&message), 4);
|
||||
}
|
||||
|
||||
|
@ -57,18 +57,6 @@ impl InstructionKeys {
|
||||
}
|
||||
}
|
||||
|
||||
/// Return the pubkey of the first writable signer in the given set of instructions.
|
||||
fn find_writable_signer(instructions: &[Instruction]) -> Option<&Pubkey> {
|
||||
for instruction in instructions {
|
||||
for account in &instruction.accounts {
|
||||
if account.is_signer && account.is_writable {
|
||||
return Some(&account.pubkey);
|
||||
}
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
/// Return pubkeys referenced by all instructions, with the ones needing signatures first. If the
|
||||
/// payer key is provided, it is always placed first in the list of signed keys. Read-only signed
|
||||
/// accounts are placed last in the set of signed accounts. Read-only unsigned accounts,
|
||||
@ -245,12 +233,7 @@ impl Message {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new(instructions: &[Instruction]) -> Self {
|
||||
let payer = find_writable_signer(instructions).expect("no suitable key for fee-payer");
|
||||
Self::new_with_payer(instructions, Some(payer))
|
||||
}
|
||||
|
||||
pub fn new_with_payer(instructions: &[Instruction], payer: Option<&Pubkey>) -> Self {
|
||||
pub fn new(instructions: &[Instruction], payer: Option<&Pubkey>) -> Self {
|
||||
let InstructionKeys {
|
||||
mut signed_keys,
|
||||
unsigned_keys,
|
||||
@ -281,7 +264,7 @@ impl Message {
|
||||
&nonce_authority_pubkey,
|
||||
);
|
||||
instructions.insert(0, nonce_ix);
|
||||
Self::new_with_payer(&instructions, payer)
|
||||
Self::new(&instructions, payer)
|
||||
}
|
||||
|
||||
pub fn serialize(&self) -> Vec<u8> {
|
||||
@ -523,11 +506,11 @@ mod tests {
|
||||
let program_id = Pubkey::default();
|
||||
let id0 = Pubkey::default();
|
||||
let ix = Instruction::new(program_id, &0, vec![AccountMeta::new(id0, false)]);
|
||||
let message = Message::new_with_payer(&[ix], None);
|
||||
let message = Message::new(&[ix], None);
|
||||
assert_eq!(message.header.num_required_signatures, 0);
|
||||
|
||||
let ix = Instruction::new(program_id, &0, vec![AccountMeta::new(id0, true)]);
|
||||
let message = Message::new(&[ix]);
|
||||
let message = Message::new(&[ix], Some(&id0));
|
||||
assert_eq!(message.header.num_required_signatures, 1);
|
||||
}
|
||||
|
||||
@ -560,11 +543,14 @@ mod tests {
|
||||
let id0 = Pubkey::default();
|
||||
let keypair1 = Keypair::new();
|
||||
let id1 = keypair1.pubkey();
|
||||
let message = Message::new(&[
|
||||
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)]),
|
||||
]);
|
||||
let message = Message::new(
|
||||
&[
|
||||
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)]),
|
||||
],
|
||||
Some(&id1),
|
||||
);
|
||||
assert_eq!(
|
||||
message.instructions[0],
|
||||
CompiledInstruction::new(2, &0, vec![1])
|
||||
@ -586,11 +572,11 @@ mod tests {
|
||||
let id0 = Pubkey::default();
|
||||
|
||||
let ix = Instruction::new(program_id, &0, vec![AccountMeta::new(id0, false)]);
|
||||
let message = Message::new_with_payer(&[ix], Some(&payer));
|
||||
let message = Message::new(&[ix], Some(&payer));
|
||||
assert_eq!(message.header.num_required_signatures, 1);
|
||||
|
||||
let ix = Instruction::new(program_id, &0, vec![AccountMeta::new(id0, true)]);
|
||||
let message = Message::new_with_payer(&[ix], Some(&payer));
|
||||
let message = Message::new(&[ix], Some(&payer));
|
||||
assert_eq!(message.header.num_required_signatures, 2);
|
||||
|
||||
let ix = Instruction::new(
|
||||
@ -598,7 +584,7 @@ mod tests {
|
||||
&0,
|
||||
vec![AccountMeta::new(payer, true), AccountMeta::new(id0, true)],
|
||||
);
|
||||
let message = Message::new_with_payer(&[ix], Some(&payer));
|
||||
let message = Message::new(&[ix], Some(&payer));
|
||||
assert_eq!(message.header.num_required_signatures, 2);
|
||||
}
|
||||
|
||||
@ -625,10 +611,13 @@ mod tests {
|
||||
let program_id0 = Pubkey::default();
|
||||
let program_id1 = Pubkey::new_rand();
|
||||
let id = Pubkey::new_rand();
|
||||
let message = Message::new(&[
|
||||
Instruction::new(program_id0, &0, vec![AccountMeta::new(id, false)]),
|
||||
Instruction::new(program_id1, &0, vec![AccountMeta::new(id, true)]),
|
||||
]);
|
||||
let message = Message::new(
|
||||
&[
|
||||
Instruction::new(program_id0, &0, vec![AccountMeta::new(id, false)]),
|
||||
Instruction::new(program_id1, &0, vec![AccountMeta::new(id, true)]),
|
||||
],
|
||||
Some(&id),
|
||||
);
|
||||
assert_eq!(message.program_position(0), None);
|
||||
assert_eq!(message.program_position(1), Some(0));
|
||||
assert_eq!(message.program_position(2), Some(1));
|
||||
@ -668,12 +657,15 @@ mod tests {
|
||||
let id1 = Pubkey::new_rand();
|
||||
let id2 = Pubkey::new_rand();
|
||||
let id3 = Pubkey::new_rand();
|
||||
let message = Message::new(&[
|
||||
Instruction::new(program_id, &0, vec![AccountMeta::new(id0, false)]),
|
||||
Instruction::new(program_id, &0, vec![AccountMeta::new(id1, true)]),
|
||||
Instruction::new(program_id, &0, vec![AccountMeta::new_readonly(id2, false)]),
|
||||
Instruction::new(program_id, &0, vec![AccountMeta::new_readonly(id3, true)]),
|
||||
]);
|
||||
let message = Message::new(
|
||||
&[
|
||||
Instruction::new(program_id, &0, vec![AccountMeta::new(id0, false)]),
|
||||
Instruction::new(program_id, &0, vec![AccountMeta::new(id1, true)]),
|
||||
Instruction::new(program_id, &0, vec![AccountMeta::new_readonly(id2, false)]),
|
||||
Instruction::new(program_id, &0, vec![AccountMeta::new_readonly(id3, true)]),
|
||||
],
|
||||
Some(&id1),
|
||||
);
|
||||
assert_eq!(
|
||||
message.get_account_keys_by_lock_type(),
|
||||
(vec![&id1, &id0], vec![&id3, &id2, &program_id])
|
||||
|
@ -2,6 +2,7 @@
|
||||
|
||||
use crate::{
|
||||
hash::Hash,
|
||||
message::Message,
|
||||
pubkey::Pubkey,
|
||||
signature::{Keypair, Signer},
|
||||
system_instruction,
|
||||
@ -19,20 +20,18 @@ pub fn create_account(
|
||||
) -> Transaction {
|
||||
let from_pubkey = from_keypair.pubkey();
|
||||
let to_pubkey = to_keypair.pubkey();
|
||||
let create_instruction =
|
||||
let instruction =
|
||||
system_instruction::create_account(&from_pubkey, &to_pubkey, lamports, space, program_id);
|
||||
Transaction::new_signed_instructions(
|
||||
&[from_keypair, to_keypair],
|
||||
&[create_instruction],
|
||||
recent_blockhash,
|
||||
)
|
||||
let message = Message::new(&[instruction], Some(&from_pubkey));
|
||||
Transaction::new(&[from_keypair, to_keypair], message, recent_blockhash)
|
||||
}
|
||||
|
||||
/// Create and sign new system_instruction::Assign transaction
|
||||
pub fn assign(from_keypair: &Keypair, recent_blockhash: Hash, program_id: &Pubkey) -> Transaction {
|
||||
let from_pubkey = from_keypair.pubkey();
|
||||
let assign_instruction = system_instruction::assign(&from_pubkey, program_id);
|
||||
Transaction::new_signed_instructions(&[from_keypair], &[assign_instruction], recent_blockhash)
|
||||
let instruction = system_instruction::assign(&from_pubkey, program_id);
|
||||
let message = Message::new(&[instruction], Some(&from_pubkey));
|
||||
Transaction::new(&[from_keypair], message, recent_blockhash)
|
||||
}
|
||||
|
||||
/// Create and sign new system_instruction::Transfer transaction
|
||||
@ -43,8 +42,9 @@ pub fn transfer(
|
||||
recent_blockhash: Hash,
|
||||
) -> Transaction {
|
||||
let from_pubkey = from_keypair.pubkey();
|
||||
let transfer_instruction = system_instruction::transfer(&from_pubkey, to, lamports);
|
||||
Transaction::new_signed_instructions(&[from_keypair], &[transfer_instruction], recent_blockhash)
|
||||
let instruction = system_instruction::transfer(&from_pubkey, to, lamports);
|
||||
let message = Message::new(&[instruction], Some(&from_pubkey));
|
||||
Transaction::new(&[from_keypair], message, recent_blockhash)
|
||||
}
|
||||
|
||||
/// Create and sign new nonced system_instruction::Transfer transaction
|
||||
@ -57,14 +57,12 @@ pub fn nonced_transfer(
|
||||
nonce_hash: Hash,
|
||||
) -> Transaction {
|
||||
let from_pubkey = from_keypair.pubkey();
|
||||
let transfer_instruction = system_instruction::transfer(&from_pubkey, to, lamports);
|
||||
let instructions = vec![transfer_instruction];
|
||||
Transaction::new_signed_with_nonce(
|
||||
instructions,
|
||||
let instruction = system_instruction::transfer(&from_pubkey, to, lamports);
|
||||
let message = Message::new_with_nonce(
|
||||
vec![instruction],
|
||||
Some(&from_pubkey),
|
||||
&[from_keypair, nonce_authority],
|
||||
nonce_account,
|
||||
&nonce_authority.pubkey(),
|
||||
nonce_hash,
|
||||
)
|
||||
);
|
||||
Transaction::new(&[from_keypair, nonce_authority], message, nonce_hash)
|
||||
}
|
||||
|
@ -9,7 +9,6 @@ use crate::{
|
||||
short_vec,
|
||||
signature::{Signature, SignerError},
|
||||
signers::Signers,
|
||||
system_instruction,
|
||||
};
|
||||
use std::result;
|
||||
use thiserror::Error;
|
||||
@ -122,7 +121,7 @@ impl Transaction {
|
||||
}
|
||||
|
||||
pub fn new_with_payer(instructions: &[Instruction], payer: Option<&Pubkey>) -> Self {
|
||||
let message = Message::new_with_payer(instructions, payer);
|
||||
let message = Message::new(instructions, payer);
|
||||
Self::new_unsigned(message)
|
||||
}
|
||||
|
||||
@ -132,31 +131,10 @@ impl Transaction {
|
||||
signing_keypairs: &T,
|
||||
recent_blockhash: Hash,
|
||||
) -> Self {
|
||||
let message = Message::new_with_payer(instructions, payer);
|
||||
let message = Message::new(instructions, payer);
|
||||
Self::new(signing_keypairs, message, recent_blockhash)
|
||||
}
|
||||
|
||||
pub fn new_signed_with_nonce<T: Signers>(
|
||||
mut instructions: Vec<Instruction>,
|
||||
payer: Option<&Pubkey>,
|
||||
signing_keypairs: &T,
|
||||
nonce_account_pubkey: &Pubkey,
|
||||
nonce_authority_pubkey: &Pubkey,
|
||||
nonce_hash: Hash,
|
||||
) -> Self {
|
||||
let nonce_ix = system_instruction::advance_nonce_account(
|
||||
&nonce_account_pubkey,
|
||||
&nonce_authority_pubkey,
|
||||
);
|
||||
instructions.insert(0, nonce_ix);
|
||||
Self::new_signed_with_payer(&instructions, payer, signing_keypairs, nonce_hash)
|
||||
}
|
||||
|
||||
pub fn new_unsigned_instructions(instructions: &[Instruction]) -> Self {
|
||||
let message = Message::new(instructions);
|
||||
Self::new_unsigned(message)
|
||||
}
|
||||
|
||||
pub fn new<T: Signers>(
|
||||
from_keypairs: &T,
|
||||
message: Message,
|
||||
@ -167,15 +145,6 @@ impl Transaction {
|
||||
tx
|
||||
}
|
||||
|
||||
pub fn new_signed_instructions<T: Signers>(
|
||||
from_keypairs: &T,
|
||||
instructions: &[Instruction],
|
||||
recent_blockhash: Hash,
|
||||
) -> Transaction {
|
||||
let message = Message::new(instructions);
|
||||
Self::new(from_keypairs, message, recent_blockhash)
|
||||
}
|
||||
|
||||
/// Create a signed transaction
|
||||
/// * `from_keypairs` - The keys used to sign the transaction.
|
||||
/// * `keys` - The keys for the transaction. These are the program state
|
||||
@ -563,7 +532,7 @@ mod tests {
|
||||
AccountMeta::new(to, false),
|
||||
];
|
||||
let instruction = Instruction::new(program_id, &(1u8, 2u8, 3u8), account_metas);
|
||||
let message = Message::new(&[instruction]);
|
||||
let message = Message::new(&[instruction], Some(&keypair.pubkey()));
|
||||
Transaction::new(&[&keypair], message, Hash::default())
|
||||
}
|
||||
|
||||
@ -594,7 +563,7 @@ mod tests {
|
||||
let expected_instruction_size = 1 + 1 + ix.accounts.len() + 1 + expected_data_size;
|
||||
assert_eq!(expected_instruction_size, 17);
|
||||
|
||||
let message = Message::new(&[ix]);
|
||||
let message = Message::new(&[ix], Some(&alice_pubkey));
|
||||
assert_eq!(
|
||||
serialized_size(&message.instructions[0]).unwrap() as usize,
|
||||
expected_instruction_size,
|
||||
@ -650,19 +619,22 @@ mod tests {
|
||||
#[should_panic]
|
||||
fn test_transaction_missing_key() {
|
||||
let keypair = Keypair::new();
|
||||
Transaction::new_unsigned_instructions(&[]).sign(&[&keypair], Hash::default());
|
||||
let message = Message::new(&[], None);
|
||||
Transaction::new_unsigned(message).sign(&[&keypair], Hash::default());
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn test_partial_sign_mismatched_key() {
|
||||
let keypair = Keypair::new();
|
||||
Transaction::new_unsigned_instructions(&[Instruction::new(
|
||||
let fee_payer = Pubkey::new_rand();
|
||||
let ix = Instruction::new(
|
||||
Pubkey::default(),
|
||||
&0,
|
||||
vec![AccountMeta::new(Pubkey::new_rand(), true)],
|
||||
)])
|
||||
.partial_sign(&[&keypair], Hash::default());
|
||||
vec![AccountMeta::new(fee_payer, true)],
|
||||
);
|
||||
let message = Message::new(&[ix], Some(&fee_payer));
|
||||
Transaction::new_unsigned(message).partial_sign(&[&keypair], Hash::default());
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -670,7 +642,7 @@ mod tests {
|
||||
let keypair0 = Keypair::new();
|
||||
let keypair1 = Keypair::new();
|
||||
let keypair2 = Keypair::new();
|
||||
let mut tx = Transaction::new_unsigned_instructions(&[Instruction::new(
|
||||
let ix = Instruction::new(
|
||||
Pubkey::default(),
|
||||
&0,
|
||||
vec![
|
||||
@ -678,7 +650,9 @@ mod tests {
|
||||
AccountMeta::new(keypair1.pubkey(), true),
|
||||
AccountMeta::new(keypair2.pubkey(), true),
|
||||
],
|
||||
)]);
|
||||
);
|
||||
let message = Message::new(&[ix], Some(&keypair0.pubkey()));
|
||||
let mut tx = Transaction::new_unsigned(message);
|
||||
|
||||
tx.partial_sign(&[&keypair0, &keypair2], Hash::default());
|
||||
assert!(!tx.is_signed());
|
||||
@ -699,8 +673,8 @@ mod tests {
|
||||
let keypair0 = Keypair::new();
|
||||
let id0 = keypair0.pubkey();
|
||||
let ix = Instruction::new(program_id, &0, vec![AccountMeta::new(id0, true)]);
|
||||
Transaction::new_unsigned_instructions(&[ix])
|
||||
.sign(&Vec::<&Keypair>::new(), Hash::default());
|
||||
let message = Message::new(&[ix], Some(&id0));
|
||||
Transaction::new_unsigned(message).sign(&Vec::<&Keypair>::new(), Hash::default());
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -710,7 +684,8 @@ mod tests {
|
||||
let keypair0 = Keypair::new();
|
||||
let wrong_id = Pubkey::default();
|
||||
let ix = Instruction::new(program_id, &0, vec![AccountMeta::new(wrong_id, true)]);
|
||||
Transaction::new_unsigned_instructions(&[ix]).sign(&[&keypair0], Hash::default());
|
||||
let message = Message::new(&[ix], Some(&wrong_id));
|
||||
Transaction::new_unsigned(message).sign(&[&keypair0], Hash::default());
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -719,7 +694,8 @@ mod tests {
|
||||
let keypair0 = Keypair::new();
|
||||
let id0 = keypair0.pubkey();
|
||||
let ix = Instruction::new(program_id, &0, vec![AccountMeta::new(id0, true)]);
|
||||
let mut tx = Transaction::new_unsigned_instructions(&[ix]);
|
||||
let message = Message::new(&[ix], Some(&id0));
|
||||
let mut tx = Transaction::new_unsigned(message);
|
||||
tx.sign(&[&keypair0], Hash::default());
|
||||
assert_eq!(
|
||||
tx.message.instructions[0],
|
||||
@ -744,7 +720,8 @@ mod tests {
|
||||
AccountMeta::new(id1, false),
|
||||
],
|
||||
);
|
||||
let mut tx = Transaction::new_unsigned_instructions(&[ix]);
|
||||
let message = Message::new(&[ix], Some(&id0));
|
||||
let mut tx = Transaction::new_unsigned(message);
|
||||
tx.sign(&[&keypair0], Hash::default());
|
||||
assert_eq!(
|
||||
tx.message.instructions[0],
|
||||
@ -769,7 +746,8 @@ mod tests {
|
||||
AccountMeta::new(presigner_pubkey, true),
|
||||
],
|
||||
);
|
||||
let mut tx = Transaction::new_unsigned_instructions(&[ix]);
|
||||
let message = Message::new(&[ix], Some(&pubkey));
|
||||
let mut tx = Transaction::new_unsigned(message);
|
||||
|
||||
let presigner_sig = presigner_keypair.sign_message(&tx.message_data());
|
||||
let presigner = Presigner::new(&presigner_pubkey, &presigner_sig);
|
||||
@ -791,7 +769,8 @@ mod tests {
|
||||
AccountMeta::new(presigner_pubkey, true),
|
||||
],
|
||||
);
|
||||
let mut tx = Transaction::new_unsigned_instructions(&[ix]);
|
||||
let message = Message::new(&[ix], Some(&another_pubkey));
|
||||
let mut tx = Transaction::new_unsigned(message);
|
||||
|
||||
let res = tx.try_sign(&signers, Hash::default());
|
||||
assert!(res.is_err());
|
||||
|
Reference in New Issue
Block a user