Support transaction signing by heterogenous lists of keypairs (#8342)
automerge
This commit is contained in:
parent
1720fe6a46
commit
e8124324ff
|
@ -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,
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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)?;
|
||||||
|
|
|
@ -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(
|
||||||
|
|
|
@ -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(
|
||||||
|
|
|
@ -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]
|
||||||
|
|
|
@ -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]
|
||||||
|
|
|
@ -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(),
|
||||||
|
|
|
@ -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())
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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>;
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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()],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -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;
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue