Support transaction signing by heterogenous lists of keypairs (#8342)

automerge
This commit is contained in:
Greg Fitzgerald 2020-02-20 13:13:23 -07:00 committed by GitHub
parent 1720fe6a46
commit e8124324ff
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 196 additions and 81 deletions

View File

@ -384,7 +384,7 @@ impl Archiver {
); );
let message = let message =
Message::new_with_payer(vec![ix], Some(&archiver_keypair.pubkey())); Message::new_with_payer(vec![ix], Some(&archiver_keypair.pubkey()));
if let Err(e) = client.send_message(&[&archiver_keypair], message) { if let Err(e) = client.send_message(&[archiver_keypair.as_ref()], message) {
error!("unable to redeem reward, tx failed: {:?}", e); error!("unable to redeem reward, tx failed: {:?}", e);
} else { } else {
info!( info!(
@ -671,7 +671,7 @@ impl Archiver {
blockhash, blockhash,
); );
if let Err(err) = client.send_and_confirm_transaction( if let Err(err) = client.send_and_confirm_transaction(
&[&archiver_keypair, &storage_keypair], &[archiver_keypair.as_ref(), storage_keypair.as_ref()],
&mut transaction, &mut transaction,
10, 10,
0, 0,

View File

@ -701,7 +701,7 @@ fn verify_funding_transfer<T: SyncClient + ?Sized>(
false false
} }
pub fn fund_keys(client: &dyn Client, source: &Keypair, dests: &[Arc<Keypair>], lamports: u64) { pub fn fund_keys<T: Client>(client: &T, source: &Keypair, dests: &[Arc<Keypair>], lamports: u64) {
let total = lamports * (dests.len() as u64 + 1); let total = lamports * (dests.len() as u64 + 1);
let mut funded: Vec<(&Keypair, u64)> = vec![(source, total)]; let mut funded: Vec<(&Keypair, u64)> = vec![(source, total)];
let mut notfunded: Vec<&Arc<Keypair>> = dests.iter().collect(); let mut notfunded: Vec<&Arc<Keypair>> = dests.iter().collect();
@ -824,7 +824,11 @@ pub fn fund_keys(client: &dyn Client, source: &Keypair, dests: &[Arc<Keypair>],
} }
} }
pub fn create_token_accounts(client: &dyn Client, signers: &[Arc<Keypair>], accounts: &[Keypair]) { pub fn create_token_accounts<T: Client>(
client: &T,
signers: &[Arc<Keypair>],
accounts: &[Keypair],
) {
let mut notfunded: Vec<(&Arc<Keypair>, &Keypair)> = signers.iter().zip(accounts).collect(); let mut notfunded: Vec<(&Arc<Keypair>, &Keypair)> = signers.iter().zip(accounts).collect();
while !notfunded.is_empty() { while !notfunded.is_empty() {
@ -968,7 +972,12 @@ fn generate_keypairs(num: u64) -> Vec<Keypair> {
rnd.gen_n_keypairs(num) rnd.gen_n_keypairs(num)
} }
pub fn airdrop_lamports(client: &dyn Client, faucet_addr: &SocketAddr, id: &Keypair, amount: u64) { pub fn airdrop_lamports<T: Client>(
client: &T,
faucet_addr: &SocketAddr,
id: &Keypair,
amount: u64,
) {
let balance = client.get_balance_with_commitment(&id.pubkey(), CommitmentConfig::recent()); let balance = client.get_balance_with_commitment(&id.pubkey(), CommitmentConfig::recent());
let balance = balance.unwrap_or(0); let balance = balance.unwrap_or(0);
if balance >= amount { if balance >= amount {

View File

@ -22,7 +22,8 @@ use solana_sdk::{
hash::Hash, hash::Hash,
inflation::Inflation, inflation::Inflation,
pubkey::Pubkey, pubkey::Pubkey,
signature::{KeypairUtil, Signature}, signature::Signature,
signers::Signers,
transaction::{self, Transaction, TransactionError}, transaction::{self, Transaction, TransactionError},
}; };
use std::{ use std::{
@ -405,10 +406,10 @@ impl RpcClient {
}) })
} }
pub fn send_and_confirm_transaction<T: KeypairUtil>( pub fn send_and_confirm_transaction<T: Signers>(
&self, &self,
transaction: &mut Transaction, transaction: &mut Transaction,
signer_keys: &[&T], signer_keys: &T,
) -> Result<String, ClientError> { ) -> Result<String, ClientError> {
let mut send_retries = 20; let mut send_retries = 20;
loop { loop {
@ -456,10 +457,10 @@ impl RpcClient {
} }
} }
pub fn send_and_confirm_transactions<T: KeypairUtil>( pub fn send_and_confirm_transactions<T: Signers>(
&self, &self,
mut transactions: Vec<Transaction>, mut transactions: Vec<Transaction>,
signer_keys: &[&T], signer_keys: &T,
) -> Result<(), Box<dyn error::Error>> { ) -> Result<(), Box<dyn error::Error>> {
let mut send_retries = 5; let mut send_retries = 5;
loop { loop {
@ -526,10 +527,10 @@ impl RpcClient {
} }
} }
pub fn resign_transaction<T: KeypairUtil>( pub fn resign_transaction<T: Signers>(
&self, &self,
tx: &mut Transaction, tx: &mut Transaction,
signer_keys: &[&T], signer_keys: &T,
) -> Result<(), ClientError> { ) -> Result<(), ClientError> {
let (blockhash, _fee_calculator) = let (blockhash, _fee_calculator) =
self.get_new_blockhash(&tx.message().recent_blockhash)?; self.get_new_blockhash(&tx.message().recent_blockhash)?;

View File

@ -18,6 +18,7 @@ use solana_sdk::{
packet::PACKET_DATA_SIZE, packet::PACKET_DATA_SIZE,
pubkey::Pubkey, pubkey::Pubkey,
signature::{Keypair, KeypairUtil, Signature}, signature::{Keypair, KeypairUtil, Signature},
signers::Signers,
system_instruction, system_instruction,
timing::duration_as_ms, timing::duration_as_ms,
transaction::{self, Transaction}, transaction::{self, Transaction},
@ -202,9 +203,9 @@ impl ThinClient {
} }
/// Retry sending a signed Transaction to the server for processing /// Retry sending a signed Transaction to the server for processing
pub fn send_and_confirm_transaction( pub fn send_and_confirm_transaction<T: Signers>(
&self, &self,
keypairs: &[&Keypair], keypairs: &T,
transaction: &mut Transaction, transaction: &mut Transaction,
tries: usize, tries: usize,
pending_confirmations: usize, pending_confirmations: usize,
@ -351,9 +352,13 @@ impl Client for ThinClient {
} }
impl SyncClient for ThinClient { impl SyncClient for ThinClient {
fn send_message(&self, keypairs: &[&Keypair], message: Message) -> TransportResult<Signature> { fn send_message<T: Signers>(
&self,
keypairs: &T,
message: Message,
) -> TransportResult<Signature> {
let (blockhash, _fee_calculator) = self.get_recent_blockhash()?; let (blockhash, _fee_calculator) = self.get_recent_blockhash()?;
let mut transaction = Transaction::new(&keypairs, message, blockhash); let mut transaction = Transaction::new(keypairs, message, blockhash);
let signature = self.send_and_confirm_transaction(keypairs, &mut transaction, 5, 0)?; let signature = self.send_and_confirm_transaction(keypairs, &mut transaction, 5, 0)?;
Ok(signature) Ok(signature)
} }
@ -561,13 +566,13 @@ impl AsyncClient for ThinClient {
.send_to(&buf[..], &self.tpu_addr())?; .send_to(&buf[..], &self.tpu_addr())?;
Ok(transaction.signatures[0]) Ok(transaction.signatures[0])
} }
fn async_send_message( fn async_send_message<T: Signers>(
&self, &self,
keypairs: &[&Keypair], keypairs: &T,
message: Message, message: Message,
recent_blockhash: Hash, recent_blockhash: Hash,
) -> io::Result<Signature> { ) -> io::Result<Signature> {
let transaction = Transaction::new(&keypairs, message, recent_blockhash); let transaction = Transaction::new(keypairs, message, recent_blockhash);
self.async_send_transaction(transaction) self.async_send_transaction(transaction)
} }
fn async_send_instruction( fn async_send_instruction(

View File

@ -108,6 +108,7 @@ pub(crate) mod tests {
instruction::Instruction, instruction::Instruction,
pubkey::Pubkey, pubkey::Pubkey,
signature::{Keypair, KeypairUtil}, signature::{Keypair, KeypairUtil},
signers::Signers,
sysvar::{ sysvar::{
stake_history::{self, StakeHistory}, stake_history::{self, StakeHistory},
Sysvar, Sysvar,
@ -133,18 +134,14 @@ pub(crate) mod tests {
amount: u64, amount: u64,
) { ) {
let vote_pubkey = vote_account.pubkey(); let vote_pubkey = vote_account.pubkey();
fn process_instructions<T: KeypairUtil>( fn process_instructions<T: Signers>(bank: &Bank, keypairs: &T, ixs: Vec<Instruction>) {
bank: &Bank, let tx = Transaction::new_signed_with_payer(
keypairs: &[&T],
ixs: Vec<Instruction>,
) {
bank.process_transaction(&Transaction::new_signed_with_payer(
ixs, ixs,
Some(&keypairs[0].pubkey()), Some(&keypairs.pubkeys()[0]),
keypairs, keypairs,
bank.last_blockhash(), bank.last_blockhash(),
)) );
.unwrap(); bank.process_transaction(&tx).unwrap();
} }
process_instructions( process_instructions(

View File

@ -94,7 +94,7 @@ mod tests {
lamports, lamports,
); );
let message = Message::new(instructions); let message = Message::new(instructions);
bank_client.send_message(&[&payer_keypair, &account_keypair], message) bank_client.send_message(&[payer_keypair, account_keypair], message)
} }
fn send_set_owner( fn send_set_owner(
@ -110,7 +110,7 @@ mod tests {
new_owner_pubkey, new_owner_pubkey,
); );
let message = Message::new_with_payer(vec![instruction], Some(&payer_keypair.pubkey())); let message = Message::new_with_payer(vec![instruction], Some(&payer_keypair.pubkey()));
bank_client.send_message(&[&payer_keypair, &old_owner_keypair], message) bank_client.send_message(&[payer_keypair, old_owner_keypair], message)
} }
#[test] #[test]

View File

@ -188,7 +188,7 @@ mod tests {
instructions.push(date_instruction::store(&date_pubkey, date)); instructions.push(date_instruction::store(&date_pubkey, date));
let message = Message::new(instructions); let message = Message::new(instructions);
bank_client.send_message(&[&payer_keypair, &date_keypair], message) bank_client.send_message(&[payer_keypair, date_keypair], message)
} }
fn store_date( fn store_date(
@ -200,7 +200,7 @@ mod tests {
let date_pubkey = date_keypair.pubkey(); let date_pubkey = date_keypair.pubkey();
let instruction = date_instruction::store(&date_pubkey, date); let instruction = date_instruction::store(&date_pubkey, date);
let message = Message::new_with_payer(vec![instruction], Some(&payer_keypair.pubkey())); let message = Message::new_with_payer(vec![instruction], Some(&payer_keypair.pubkey()));
bank_client.send_message(&[&payer_keypair, &date_keypair], message) bank_client.send_message(&[payer_keypair, date_keypair], message)
} }
fn create_vest_account( fn create_vest_account(
@ -223,7 +223,7 @@ mod tests {
lamports, lamports,
); );
let message = Message::new(instructions); let message = Message::new(instructions);
bank_client.send_message(&[&payer_keypair, &contract_keypair], message) bank_client.send_message(&[payer_keypair, contract_keypair], message)
} }
fn send_set_terminator( fn send_set_terminator(
@ -258,7 +258,7 @@ mod tests {
let instruction = let instruction =
vest_instruction::redeem_tokens(&contract_pubkey, &date_pubkey, &payee_pubkey); vest_instruction::redeem_tokens(&contract_pubkey, &date_pubkey, &payee_pubkey);
let message = Message::new_with_payer(vec![instruction], Some(&payer_keypair.pubkey())); let message = Message::new_with_payer(vec![instruction], Some(&payer_keypair.pubkey()));
bank_client.send_message(&[&payer_keypair], message) bank_client.send_message(&[payer_keypair], message)
} }
#[test] #[test]

View File

@ -744,7 +744,7 @@ mod tests {
let mut error_counters = ErrorCounters::default(); let mut error_counters = ErrorCounters::default();
let instructions = vec![CompiledInstruction::new(0, &(), vec![0])]; let instructions = vec![CompiledInstruction::new(0, &(), vec![0])];
let tx = Transaction::new_with_compiled_instructions::<Keypair>( let tx = Transaction::new_with_compiled_instructions::<[&Keypair; 0]>(
&[], &[],
&[], &[],
Hash::default(), Hash::default(),

View File

@ -9,6 +9,7 @@ use solana_sdk::{
message::Message, message::Message,
pubkey::Pubkey, pubkey::Pubkey,
signature::{Keypair, KeypairUtil, Signature}, signature::{Keypair, KeypairUtil, Signature},
signers::Signers,
system_instruction, system_instruction,
transaction::{self, Transaction}, transaction::{self, Transaction},
transport::{Result, TransportError}, transport::{Result, TransportError},
@ -42,13 +43,13 @@ impl AsyncClient for BankClient {
Ok(signature) Ok(signature)
} }
fn async_send_message( fn async_send_message<T: Signers>(
&self, &self,
keypairs: &[&Keypair], keypairs: &T,
message: Message, message: Message,
recent_blockhash: Hash, recent_blockhash: Hash,
) -> io::Result<Signature> { ) -> io::Result<Signature> {
let transaction = Transaction::new(&keypairs, message, recent_blockhash); let transaction = Transaction::new(keypairs, message, recent_blockhash);
self.async_send_transaction(transaction) self.async_send_transaction(transaction)
} }
@ -77,9 +78,9 @@ impl AsyncClient for BankClient {
} }
impl SyncClient for BankClient { impl SyncClient for BankClient {
fn send_message(&self, keypairs: &[&Keypair], message: Message) -> Result<Signature> { fn send_message<T: Signers>(&self, keypairs: &T, message: Message) -> Result<Signature> {
let blockhash = self.bank.last_blockhash(); let blockhash = self.bank.last_blockhash();
let transaction = Transaction::new(&keypairs, message, blockhash); let transaction = Transaction::new(keypairs, message, blockhash);
self.bank.process_transaction(&transaction)?; self.bank.process_transaction(&transaction)?;
Ok(transaction.signatures.get(0).cloned().unwrap_or_default()) Ok(transaction.signatures.get(0).cloned().unwrap_or_default())
} }

View File

@ -61,7 +61,7 @@ fn fill_epoch_with_votes(
Some(&mint_pubkey), Some(&mint_pubkey),
); );
assert!(bank_client assert!(bank_client
.send_message(&[&mint_keypair, &vote_keypair], message) .send_message(&[mint_keypair, vote_keypair], message)
.is_ok()); .is_ok());
} }
bank bank

View File

@ -371,7 +371,7 @@ fn submit_proof(
); );
assert_matches!( assert_matches!(
bank_client.send_message(&[&mint_keypair, &storage_keypair], message), bank_client.send_message(&[mint_keypair, storage_keypair], message),
Ok(_) Ok(_)
); );
ProofStatus::Valid ProofStatus::Valid

View File

@ -428,7 +428,7 @@ pub unsafe extern "C" fn deserialize_transaction(
/// # Undefined Behavior /// # Undefined Behavior
/// ///
/// Causes UB if any of the pointers is `NULL`, or if `keypairs` does not point to a valid array of /// Causes UB if any of the pointers is `NULL`, or if `keypairs` does not point to a valid array of
/// `Keypairs` of length `num_keypairs` /// `Signers` of length `num_keypairs`
/// ///
/// # Safety /// # Safety
#[no_mangle] #[no_mangle]
@ -453,7 +453,11 @@ pub unsafe extern "C" fn transaction_partial_sign(
}; };
let keypairs_ref: Vec<&KeypairNative> = keypairs.iter().collect(); let keypairs_ref: Vec<&KeypairNative> = keypairs.iter().collect();
let positions = if let Ok(v) = tx_native.get_signing_keypair_positions(&keypairs_ref[..]) { let pubkeys: Vec<_> = keypairs_ref
.iter()
.map(|keypair| keypair.pubkey())
.collect();
let positions = if let Ok(v) = tx_native.get_signing_keypair_positions(&pubkeys) {
v v
} else { } else {
return 2; return 2;
@ -467,7 +471,7 @@ pub unsafe extern "C" fn transaction_partial_sign(
return 3; return 3;
}; };
tx_native.partial_sign_unchecked(&keypairs_ref[..], positions, *recent_blockhash); tx_native.partial_sign_unchecked(&keypairs_ref, positions, *recent_blockhash);
*tx = Transaction::from_native(tx_native); *tx = Transaction::from_native(tx_native);
Box::into_raw(tx); Box::into_raw(tx);
0 0

View File

@ -17,6 +17,7 @@ use crate::{
message::Message, message::Message,
pubkey::Pubkey, pubkey::Pubkey,
signature::{Keypair, Signature}, signature::{Keypair, Signature},
signers::Signers,
transaction, transaction,
transport::Result, transport::Result,
}; };
@ -29,7 +30,7 @@ pub trait Client: SyncClient + AsyncClient {
pub trait SyncClient { pub trait SyncClient {
/// Create a transaction from the given message, and send it to the /// Create a transaction from the given message, and send it to the
/// server, retrying as-needed. /// server, retrying as-needed.
fn send_message(&self, keypairs: &[&Keypair], message: Message) -> Result<Signature>; fn send_message<T: Signers>(&self, keypairs: &T, message: Message) -> Result<Signature>;
/// Create a transaction from a single instruction that only requires /// Create a transaction from a single instruction that only requires
/// a single signer. Then send it to the server, retrying as-needed. /// a single signer. Then send it to the server, retrying as-needed.
@ -121,9 +122,9 @@ pub trait AsyncClient {
/// Create a transaction from the given message, and send it to the /// Create a transaction from the given message, and send it to the
/// server, but don't wait for to see if the server accepted it. /// server, but don't wait for to see if the server accepted it.
fn async_send_message( fn async_send_message<T: Signers>(
&self, &self,
keypairs: &[&Keypair], keypairs: &T,
message: Message, message: Message,
recent_blockhash: Hash, recent_blockhash: Hash,
) -> io::Result<Signature>; ) -> io::Result<Signature>;

View File

@ -72,6 +72,8 @@ pub mod genesis_config;
#[cfg(not(feature = "program"))] #[cfg(not(feature = "program"))]
pub mod signature; pub mod signature;
#[cfg(not(feature = "program"))] #[cfg(not(feature = "program"))]
pub mod signers;
#[cfg(not(feature = "program"))]
pub mod system_transaction; pub mod system_transaction;
#[cfg(not(feature = "program"))] #[cfg(not(feature = "program"))]
pub mod transaction; pub mod transaction;

97
sdk/src/signers.rs Normal file
View File

@ -0,0 +1,97 @@
use crate::{
pubkey::Pubkey,
signature::{KeypairUtil, Signature},
};
pub trait Signers {
fn pubkeys(&self) -> Vec<Pubkey>;
fn sign_message(&self, message: &[u8]) -> Vec<Signature>;
}
macro_rules! default_keypairs_impl {
() => (
fn pubkeys(&self) -> Vec<Pubkey> {
self.iter().map(|keypair| keypair.pubkey()).collect()
}
fn sign_message(&self, message: &[u8]) -> Vec<Signature> {
self.iter()
.map(|keypair| keypair.sign_message(message))
.collect()
}
);
}
impl<T: KeypairUtil> Signers for [&T] {
default_keypairs_impl!();
}
impl Signers for [Box<dyn KeypairUtil>] {
default_keypairs_impl!();
}
impl<T: KeypairUtil> Signers for [&T; 0] {
default_keypairs_impl!();
}
impl<T: KeypairUtil> Signers for [&T; 1] {
default_keypairs_impl!();
}
impl<T: KeypairUtil> Signers for [&T; 2] {
default_keypairs_impl!();
}
impl<T: KeypairUtil> Signers for [&T; 3] {
default_keypairs_impl!();
}
impl<T: KeypairUtil> Signers for [&T; 4] {
default_keypairs_impl!();
}
impl<T: KeypairUtil> Signers for Vec<&T> {
default_keypairs_impl!();
}
#[cfg(test)]
mod tests {
use super::*;
use std::error;
struct Foo;
impl KeypairUtil for Foo {
fn try_pubkey(&self) -> Result<Pubkey, Box<dyn error::Error>> {
Ok(Pubkey::default())
}
fn try_sign_message(&self, _message: &[u8]) -> Result<Signature, Box<dyn error::Error>> {
Ok(Signature::default())
}
}
struct Bar;
impl KeypairUtil for Bar {
fn try_pubkey(&self) -> Result<Pubkey, Box<dyn error::Error>> {
Ok(Pubkey::default())
}
fn try_sign_message(&self, _message: &[u8]) -> Result<Signature, Box<dyn error::Error>> {
Ok(Signature::default())
}
}
#[test]
fn test_dyn_keypairs_compile() {
let xs: Vec<Box<dyn KeypairUtil>> = vec![Box::new(Foo {}), Box::new(Bar {})];
assert_eq!(
xs.sign_message(b""),
vec![Signature::default(), Signature::default()],
);
// Same as above, but less compiler magic.
let xs_ref: &[Box<dyn KeypairUtil>] = &xs;
assert_eq!(
Signers::sign_message(xs_ref, b""),
vec![Signature::default(), Signature::default()],
);
}
}

View File

@ -6,7 +6,8 @@ use crate::{
message::Message, message::Message,
pubkey::Pubkey, pubkey::Pubkey,
short_vec, short_vec,
signature::{KeypairUtil, Signature}, signature::Signature,
signers::Signers,
system_instruction, system_instruction,
}; };
use std::result; use std::result;
@ -95,20 +96,20 @@ impl Transaction {
Self::new_unsigned(message) Self::new_unsigned(message)
} }
pub fn new_signed_with_payer<T: KeypairUtil>( pub fn new_signed_with_payer<T: Signers>(
instructions: Vec<Instruction>, instructions: Vec<Instruction>,
payer: Option<&Pubkey>, payer: Option<&Pubkey>,
signing_keypairs: &[&T], signing_keypairs: &T,
recent_blockhash: Hash, recent_blockhash: Hash,
) -> Self { ) -> Self {
let message = Message::new_with_payer(instructions, payer); let message = Message::new_with_payer(instructions, payer);
Self::new(signing_keypairs, message, recent_blockhash) Self::new(signing_keypairs, message, recent_blockhash)
} }
pub fn new_signed_with_nonce<T: KeypairUtil>( pub fn new_signed_with_nonce<T: Signers>(
mut instructions: Vec<Instruction>, mut instructions: Vec<Instruction>,
payer: Option<&Pubkey>, payer: Option<&Pubkey>,
signing_keypairs: &[&T], signing_keypairs: &T,
nonce_account_pubkey: &Pubkey, nonce_account_pubkey: &Pubkey,
nonce_authority_pubkey: &Pubkey, nonce_authority_pubkey: &Pubkey,
nonce_hash: Hash, nonce_hash: Hash,
@ -126,8 +127,8 @@ impl Transaction {
Self::new_unsigned(message) Self::new_unsigned(message)
} }
pub fn new<T: KeypairUtil>( pub fn new<T: Signers>(
from_keypairs: &[&T], from_keypairs: &T,
message: Message, message: Message,
recent_blockhash: Hash, recent_blockhash: Hash,
) -> Transaction { ) -> Transaction {
@ -136,8 +137,8 @@ impl Transaction {
tx tx
} }
pub fn new_signed_instructions<T: KeypairUtil>( pub fn new_signed_instructions<T: Signers>(
from_keypairs: &[&T], from_keypairs: &T,
instructions: Vec<Instruction>, instructions: Vec<Instruction>,
recent_blockhash: Hash, recent_blockhash: Hash,
) -> Transaction { ) -> Transaction {
@ -152,21 +153,19 @@ impl Transaction {
/// * `recent_blockhash` - The PoH hash. /// * `recent_blockhash` - The PoH hash.
/// * `program_ids` - The keys that identify programs used in the `instruction` vector. /// * `program_ids` - The keys that identify programs used in the `instruction` vector.
/// * `instructions` - Instructions that will be executed atomically. /// * `instructions` - Instructions that will be executed atomically.
pub fn new_with_compiled_instructions<T: KeypairUtil>( pub fn new_with_compiled_instructions<T: Signers>(
from_keypairs: &[&T], from_keypairs: &T,
keys: &[Pubkey], keys: &[Pubkey],
recent_blockhash: Hash, recent_blockhash: Hash,
program_ids: Vec<Pubkey>, program_ids: Vec<Pubkey>,
instructions: Vec<CompiledInstruction>, instructions: Vec<CompiledInstruction>,
) -> Self { ) -> Self {
let mut account_keys: Vec<_> = from_keypairs let mut account_keys = from_keypairs.pubkeys();
.iter() let from_keypairs_len = account_keys.len();
.map(|keypair| (*keypair).pubkey())
.collect();
account_keys.extend_from_slice(keys); account_keys.extend_from_slice(keys);
account_keys.extend(&program_ids); account_keys.extend(&program_ids);
let message = Message::new_with_compiled_instructions( let message = Message::new_with_compiled_instructions(
from_keypairs.len() as u8, from_keypairs_len as u8,
0, 0,
program_ids.len() as u8, program_ids.len() as u8,
account_keys, account_keys,
@ -214,7 +213,7 @@ impl Transaction {
} }
/// Check keys and keypair lengths, then sign this transaction. /// Check keys and keypair lengths, then sign this transaction.
pub fn sign<T: KeypairUtil>(&mut self, keypairs: &[&T], recent_blockhash: Hash) { pub fn sign<T: Signers>(&mut self, keypairs: &T, recent_blockhash: Hash) {
self.partial_sign(keypairs, recent_blockhash); self.partial_sign(keypairs, recent_blockhash);
assert_eq!(self.is_signed(), true, "not enough keypairs"); assert_eq!(self.is_signed(), true, "not enough keypairs");
@ -223,9 +222,9 @@ impl Transaction {
/// Sign using some subset of required keys /// Sign using some subset of required keys
/// if recent_blockhash is not the same as currently in the transaction, /// if recent_blockhash is not the same as currently in the transaction,
/// clear any prior signatures and update recent_blockhash /// clear any prior signatures and update recent_blockhash
pub fn partial_sign<T: KeypairUtil>(&mut self, keypairs: &[&T], recent_blockhash: Hash) { pub fn partial_sign<T: Signers>(&mut self, keypairs: &T, recent_blockhash: Hash) {
let positions = self let positions = self
.get_signing_keypair_positions(keypairs) .get_signing_keypair_positions(&keypairs.pubkeys())
.expect("account_keys doesn't contain num_required_signatures keys"); .expect("account_keys doesn't contain num_required_signatures keys");
let positions: Vec<usize> = positions let positions: Vec<usize> = positions
.iter() .iter()
@ -236,9 +235,9 @@ impl Transaction {
/// Sign the transaction and place the signatures in their associated positions in `signatures` /// Sign the transaction and place the signatures in their associated positions in `signatures`
/// without checking that the positions are correct. /// without checking that the positions are correct.
pub fn partial_sign_unchecked<T: KeypairUtil>( pub fn partial_sign_unchecked<T: Signers>(
&mut self, &mut self,
keypairs: &[&T], keypairs: &T,
positions: Vec<usize>, positions: Vec<usize>,
recent_blockhash: Hash, recent_blockhash: Hash,
) { ) {
@ -250,8 +249,9 @@ impl Transaction {
.for_each(|signature| *signature = Signature::default()); .for_each(|signature| *signature = Signature::default());
} }
let signatures = keypairs.sign_message(&self.message_data());
for i in 0..positions.len() { for i in 0..positions.len() {
self.signatures[positions[i]] = keypairs[i].sign_message(&self.message_data()) self.signatures[positions[i]] = signatures[i];
} }
} }
@ -271,23 +271,16 @@ impl Transaction {
} }
/// Get the positions of the pubkeys in `account_keys` associated with signing keypairs /// Get the positions of the pubkeys in `account_keys` associated with signing keypairs
pub fn get_signing_keypair_positions<T: KeypairUtil>( pub fn get_signing_keypair_positions(&self, pubkeys: &[Pubkey]) -> Result<Vec<Option<usize>>> {
&self,
keypairs: &[&T],
) -> Result<Vec<Option<usize>>> {
if self.message.account_keys.len() < self.message.header.num_required_signatures as usize { if self.message.account_keys.len() < self.message.header.num_required_signatures as usize {
return Err(TransactionError::InvalidAccountIndex); return Err(TransactionError::InvalidAccountIndex);
} }
let signed_keys = let signed_keys =
&self.message.account_keys[0..self.message.header.num_required_signatures as usize]; &self.message.account_keys[0..self.message.header.num_required_signatures as usize];
Ok(keypairs Ok(pubkeys
.iter() .iter()
.map(|keypair| { .map(|pubkey| signed_keys.iter().position(|x| x == pubkey))
signed_keys
.iter()
.position(|pubkey| pubkey == &keypair.pubkey())
})
.collect()) .collect())
} }
@ -338,7 +331,12 @@ impl Transaction {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
use crate::{hash::hash, instruction::AccountMeta, signature::Keypair, system_instruction}; use crate::{
hash::hash,
instruction::AccountMeta,
signature::{Keypair, KeypairUtil},
system_instruction,
};
use bincode::{deserialize, serialize, serialized_size}; use bincode::{deserialize, serialize, serialized_size};
use std::mem::size_of; use std::mem::size_of;