diff --git a/clap-utils/src/keypair.rs b/clap-utils/src/keypair.rs index 5c9fd849d0..22532e5d46 100644 --- a/clap-utils/src/keypair.rs +++ b/clap-utils/src/keypair.rs @@ -12,6 +12,7 @@ use solana_remote_wallet::{ }; use solana_sdk::{ hash::Hash, + message::Message, pubkey::Pubkey, signature::{ keypair_from_seed, keypair_from_seed_phrase_and_passphrase, read_keypair, @@ -68,6 +69,18 @@ impl CliSignerInfo { None } } + pub fn signers_for_message(&self, message: &Message) -> Vec<&dyn Signer> { + self.signers + .iter() + .filter_map(|k| { + if message.signer_keys().contains(&&k.pubkey()) { + Some(k.as_ref()) + } else { + None + } + }) + .collect() + } } pub struct DefaultSigner { @@ -356,6 +369,7 @@ fn sanitize_seed_phrase(seed_phrase: &str) -> String { #[cfg(test)] mod tests { use super::*; + use solana_sdk::system_instruction; #[test] fn test_sanitize_seed_phrase() { @@ -365,4 +379,35 @@ mod tests { sanitize_seed_phrase(seed_phrase) ); } + + #[test] + fn test_signer_info_signers_for_message() { + let source = Keypair::new(); + let fee_payer = Keypair::new(); + let nonsigner1 = Keypair::new(); + let nonsigner2 = Keypair::new(); + let recipient = Pubkey::new_unique(); + let message = Message::new( + &[system_instruction::transfer( + &source.pubkey(), + &recipient, + 42, + )], + Some(&fee_payer.pubkey()), + ); + let signers = vec![ + Box::new(fee_payer) as Box, + Box::new(source) as Box, + Box::new(nonsigner1) as Box, + Box::new(nonsigner2) as Box, + ]; + let signer_info = CliSignerInfo { signers }; + let msg_signers = signer_info.signers_for_message(&message); + let signer_pubkeys = msg_signers.iter().map(|s| s.pubkey()).collect::>(); + let expect = vec![ + signer_info.signers[0].pubkey(), + signer_info.signers[1].pubkey(), + ]; + assert_eq!(signer_pubkeys, expect); + } } diff --git a/sdk/program/src/message.rs b/sdk/program/src/message.rs index 3eec6de474..d082c6858b 100644 --- a/sdk/program/src/message.rs +++ b/sdk/program/src/message.rs @@ -435,6 +435,15 @@ impl Message { accounts, }) } + + pub fn signer_keys(&self) -> Vec<&Pubkey> { + // Clamp in case we're working on un-`sanitize()`ed input + let last_key = self + .account_keys + .len() + .max(self.header.num_required_signatures as usize); + self.account_keys[..last_key].iter().collect() + } } #[cfg(test)]