diff --git a/cli-output/src/cli_output.rs b/cli-output/src/cli_output.rs index 046ec3a087..606199d487 100644 --- a/cli-output/src/cli_output.rs +++ b/cli-output/src/cli_output.rs @@ -2776,10 +2776,10 @@ mod tests { let expected_msg = "AwECBwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDgTl3Dqh9\ F19Wo1Rmw0x+zMuNipG07jeiXfYPW4/Js5QEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQE\ - BAQEBAYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBQUFBQUFBQUFBQUFBQUFBQUF\ - BQUFBQUFBQUFBQUFBQUGp9UXGSxWjuCKhF9z0peIzwNcMUWyGrNE2AYuqUAAAAAAAAAAAAAA\ - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcH\ - BwcCBgMDBQIEBAAAAAYCAQQMAgAAACoAAAAAAAAA" + BAQEBAUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBgYGBgYGBgYGBgYGBgYGBgYG\ + BgYGBgYGBgYGBgYGBgYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAan1RcZLFaO\ + 4IqEX3PSl4jPA1wxRbIas0TYBi6pQAAABwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcH\ + BwcCBQMEBgIEBAAAAAUCAQMMAgAAACoAAAAAAAAA" .to_string(); let config = ReturnSignersConfig { dump_transaction_message: true, diff --git a/runtime/src/message_processor.rs b/runtime/src/message_processor.rs index 4d4645075c..8932e59d90 100644 --- a/runtime/src/message_processor.rs +++ b/runtime/src/message_processor.rs @@ -162,7 +162,7 @@ mod tests { solana_sdk::{ account::{AccountSharedData, ReadableAccount}, instruction::{AccountMeta, Instruction, InstructionError}, - message::Message, + message::{AccountKeys, Message}, native_loader::{self, create_loadable_account_for_test}, pubkey::Pubkey, secp256k1_instruction::new_secp256k1_instruction, @@ -219,7 +219,10 @@ mod tests { } } - let mock_system_program_id = Pubkey::new(&[2u8; 32]); + let writable_pubkey = Pubkey::new_unique(); + let readonly_pubkey = Pubkey::new_unique(); + let mock_system_program_id = Pubkey::new_unique(); + let rent_collector = RentCollector::default(); let builtin_programs = &[BuiltinProgram { program_id: mock_system_program_id, @@ -228,11 +231,11 @@ mod tests { let accounts = vec![ ( - solana_sdk::pubkey::new_rand(), + writable_pubkey, AccountSharedData::new(100, 1, &mock_system_program_id), ), ( - solana_sdk::pubkey::new_rand(), + readonly_pubkey, AccountSharedData::new(0, 1, &mock_system_program_id), ), ( @@ -243,24 +246,25 @@ mod tests { let mut transaction_context = TransactionContext::new(accounts, 1, 3); let program_indices = vec![vec![2]]; let executors = Rc::new(RefCell::new(Executors::default())); + let account_keys = transaction_context.get_keys_of_accounts().to_vec(); let account_metas = vec![ - AccountMeta::new( - *transaction_context.get_key_of_account_at_index(0).unwrap(), - true, - ), - AccountMeta::new_readonly( - *transaction_context.get_key_of_account_at_index(1).unwrap(), - false, - ), + AccountMeta::new(writable_pubkey, true), + AccountMeta::new_readonly(readonly_pubkey, false), ]; - let message = SanitizedMessage::Legacy(Message::new( - &[Instruction::new_with_bincode( - mock_system_program_id, - &MockSystemInstruction::Correct, - account_metas.clone(), - )], - Some(transaction_context.get_key_of_account_at_index(0).unwrap()), + let message = SanitizedMessage::Legacy(Message::new_with_compiled_instructions( + 1, + 0, + 2, + account_keys.clone(), + Hash::default(), + AccountKeys::new(&account_keys, None).compile_instructions(&[ + Instruction::new_with_bincode( + mock_system_program_id, + &MockSystemInstruction::Correct, + account_metas.clone(), + ), + ]), )); let sysvar_cache = SysvarCache::default(); let result = MessageProcessor::process_message( @@ -297,13 +301,19 @@ mod tests { 0 ); - let message = SanitizedMessage::Legacy(Message::new( - &[Instruction::new_with_bincode( - mock_system_program_id, - &MockSystemInstruction::TransferLamports { lamports: 50 }, - account_metas.clone(), - )], - Some(transaction_context.get_key_of_account_at_index(0).unwrap()), + let message = SanitizedMessage::Legacy(Message::new_with_compiled_instructions( + 1, + 0, + 2, + account_keys.clone(), + Hash::default(), + AccountKeys::new(&account_keys, None).compile_instructions(&[ + Instruction::new_with_bincode( + mock_system_program_id, + &MockSystemInstruction::TransferLamports { lamports: 50 }, + account_metas.clone(), + ), + ]), )); let result = MessageProcessor::process_message( builtin_programs, @@ -329,13 +339,19 @@ mod tests { )) ); - let message = SanitizedMessage::Legacy(Message::new( - &[Instruction::new_with_bincode( - mock_system_program_id, - &MockSystemInstruction::ChangeData { data: 50 }, - account_metas, - )], - Some(transaction_context.get_key_of_account_at_index(0).unwrap()), + let message = SanitizedMessage::Legacy(Message::new_with_compiled_instructions( + 1, + 0, + 2, + account_keys.clone(), + Hash::default(), + AccountKeys::new(&account_keys, None).compile_instructions(&[ + Instruction::new_with_bincode( + mock_system_program_id, + &MockSystemInstruction::ChangeData { data: 50 }, + account_metas, + ), + ]), )); let result = MessageProcessor::process_message( builtin_programs, diff --git a/sdk/program/src/message/account_keys.rs b/sdk/program/src/message/account_keys.rs index 7f67333374..5b6812eb9c 100644 --- a/sdk/program/src/message/account_keys.rs +++ b/sdk/program/src/message/account_keys.rs @@ -1,6 +1,10 @@ use { - crate::{message::v0::LoadedAddresses, pubkey::Pubkey}, - std::ops::Index, + crate::{ + instruction::{CompiledInstruction, Instruction}, + message::v0::LoadedAddresses, + pubkey::Pubkey, + }, + std::{collections::BTreeMap, ops::Index}, }; /// Collection of static and dynamically loaded keys used to load accounts @@ -75,6 +79,33 @@ impl<'a> AccountKeys<'a> { pub fn iter(&self) -> impl Iterator { self.key_segment_iter().flatten() } + + /// Compile instructions using the order of account keys to determine + /// compiled instruction account indexes. + pub fn compile_instructions(&self, instructions: &[Instruction]) -> Vec { + let account_index_map: BTreeMap<&Pubkey, u8> = BTreeMap::from_iter( + self.iter() + .enumerate() + .map(|(index, key)| (key, index as u8)), + ); + + instructions + .iter() + .map(|ix| { + let accounts: Vec = ix + .accounts + .iter() + .map(|account_meta| *account_index_map.get(&account_meta.pubkey).unwrap()) + .collect(); + + CompiledInstruction { + program_id_index: *account_index_map.get(&ix.program_id).unwrap(), + data: ix.data.clone(), + accounts, + } + }) + .collect() + } } #[cfg(test)] diff --git a/sdk/program/src/message/compiled_keys.rs b/sdk/program/src/message/compiled_keys.rs new file mode 100644 index 0000000000..9a716767ff --- /dev/null +++ b/sdk/program/src/message/compiled_keys.rs @@ -0,0 +1,231 @@ +use { + crate::{instruction::Instruction, message::MessageHeader, pubkey::Pubkey}, + std::collections::BTreeMap, +}; + +/// A helper struct to collect pubkeys compiled for a set of instructions +#[derive(Default, Debug, PartialEq, Eq)] +pub(crate) struct CompiledKeys { + writable_signer_keys: Vec, + readonly_signer_keys: Vec, + writable_non_signer_keys: Vec, + readonly_non_signer_keys: Vec, +} + +#[derive(Default, Debug)] +struct CompiledKeyMeta { + is_signer: bool, + is_writable: bool, +} + +impl CompiledKeys { + /// Compiles the pubkeys referenced by a list of instructions and organizes by + /// signer/non-signer and writable/readonly. + pub(crate) fn compile(instructions: &[Instruction], payer: Option) -> Self { + let mut key_meta_map = BTreeMap::<&Pubkey, CompiledKeyMeta>::new(); + for ix in instructions { + key_meta_map.entry(&ix.program_id).or_default(); + for account_meta in &ix.accounts { + let meta = key_meta_map.entry(&account_meta.pubkey).or_default(); + meta.is_signer |= account_meta.is_signer; + meta.is_writable |= account_meta.is_writable; + } + } + + if let Some(payer) = &payer { + key_meta_map.remove(payer); + } + + let writable_signer_keys: Vec = payer + .into_iter() + .chain( + key_meta_map + .iter() + .filter_map(|(key, meta)| (meta.is_signer && meta.is_writable).then(|| **key)), + ) + .collect(); + let readonly_signer_keys = key_meta_map + .iter() + .filter_map(|(key, meta)| (meta.is_signer && !meta.is_writable).then(|| **key)) + .collect(); + let writable_non_signer_keys = key_meta_map + .iter() + .filter_map(|(key, meta)| (!meta.is_signer && meta.is_writable).then(|| **key)) + .collect(); + let readonly_non_signer_keys = key_meta_map + .iter() + .filter_map(|(key, meta)| (!meta.is_signer && !meta.is_writable).then(|| **key)) + .collect(); + + CompiledKeys { + writable_signer_keys, + readonly_signer_keys, + writable_non_signer_keys, + readonly_non_signer_keys, + } + } + + pub(crate) fn try_into_message_components(self) -> Option<(MessageHeader, Vec)> { + let header = MessageHeader { + num_required_signatures: u8::try_from( + self.writable_signer_keys + .len() + .checked_add(self.readonly_signer_keys.len())?, + ) + .ok()?, + num_readonly_signed_accounts: u8::try_from(self.readonly_signer_keys.len()).ok()?, + num_readonly_unsigned_accounts: u8::try_from(self.readonly_non_signer_keys.len()) + .ok()?, + }; + + let static_account_keys = std::iter::empty() + .chain(self.writable_signer_keys) + .chain(self.readonly_signer_keys) + .chain(self.writable_non_signer_keys) + .chain(self.readonly_non_signer_keys) + .collect(); + + Some((header, static_account_keys)) + } +} + +#[cfg(test)] +mod tests { + use {super::*, crate::instruction::AccountMeta}; + + #[test] + fn test_compile_with_dups() { + let program_id = Pubkey::new_unique(); + let id0 = Pubkey::new_unique(); + let id1 = Pubkey::new_unique(); + let id2 = Pubkey::new_unique(); + let keys = CompiledKeys::compile( + &[Instruction::new_with_bincode( + program_id, + &0, + vec![ + AccountMeta::new(id0, true), + AccountMeta::new_readonly(id1, true), + AccountMeta::new(id2, false), + // duplicate the account inputs + AccountMeta::new(id0, true), + AccountMeta::new_readonly(id1, true), + AccountMeta::new(id2, false), + ], + )], + None, + ); + assert_eq!( + keys, + CompiledKeys { + writable_signer_keys: vec![id0], + readonly_signer_keys: vec![id1], + writable_non_signer_keys: vec![id2], + readonly_non_signer_keys: vec![program_id], + } + ); + } + + #[test] + fn test_compile_with_dup_payer() { + let program_id = Pubkey::new_unique(); + let payer = Pubkey::new_unique(); + let keys = CompiledKeys::compile( + &[Instruction::new_with_bincode( + program_id, + &0, + vec![AccountMeta::new_readonly(payer, false)], + )], + Some(payer), + ); + assert_eq!( + keys, + CompiledKeys { + writable_signer_keys: vec![payer], + readonly_non_signer_keys: vec![program_id], + ..CompiledKeys::default() + } + ); + } + + #[test] + fn test_compile_with_dup_signer_mismatch() { + let program_id = Pubkey::new_unique(); + let id0 = Pubkey::new_unique(); + let keys = CompiledKeys::compile( + &[Instruction::new_with_bincode( + program_id, + &0, + vec![AccountMeta::new(id0, false), AccountMeta::new(id0, true)], + )], + None, + ); + + // Ensure the dup writable key is a signer + assert_eq!( + keys, + CompiledKeys { + writable_signer_keys: vec![id0], + readonly_non_signer_keys: vec![program_id], + ..CompiledKeys::default() + } + ); + } + + #[test] + fn test_compile_with_dup_signer_writable_mismatch() { + let program_id = Pubkey::new_unique(); + let id0 = Pubkey::new_unique(); + let keys = CompiledKeys::compile( + &[Instruction::new_with_bincode( + program_id, + &0, + vec![ + AccountMeta::new_readonly(id0, true), + AccountMeta::new(id0, true), + ], + )], + None, + ); + + // Ensure the dup signer key is writable + assert_eq!( + keys, + CompiledKeys { + writable_signer_keys: vec![id0], + readonly_non_signer_keys: vec![program_id], + ..CompiledKeys::default() + } + ); + } + + #[test] + fn test_compile_with_dup_nonsigner_writable_mismatch() { + let program_id = Pubkey::new_unique(); + let id0 = Pubkey::new_unique(); + let keys = CompiledKeys::compile( + &[ + Instruction::new_with_bincode( + program_id, + &0, + vec![ + AccountMeta::new_readonly(id0, false), + AccountMeta::new(id0, false), + ], + ), + Instruction::new_with_bincode(program_id, &0, vec![AccountMeta::new(id0, false)]), + ], + None, + ); + + // Ensure the dup nonsigner key is writable + assert_eq!( + keys, + CompiledKeys { + writable_non_signer_keys: vec![id0], + readonly_non_signer_keys: vec![program_id], + ..CompiledKeys::default() + } + ); + } +} diff --git a/sdk/program/src/message/legacy.rs b/sdk/program/src/message/legacy.rs index a85dc7ecba..532e2226d7 100644 --- a/sdk/program/src/message/legacy.rs +++ b/sdk/program/src/message/legacy.rs @@ -15,14 +15,14 @@ use { crate::{ bpf_loader, bpf_loader_deprecated, bpf_loader_upgradeable, hash::Hash, - instruction::{AccountMeta, CompiledInstruction, Instruction}, - message::MessageHeader, + instruction::{CompiledInstruction, Instruction}, + message::{CompiledKeys, MessageHeader}, pubkey::Pubkey, sanitize::{Sanitize, SanitizeError}, short_vec, system_instruction, system_program, sysvar, wasm_bindgen, }, lazy_static::lazy_static, - std::{collections::BTreeSet, convert::TryFrom, str::FromStr}, + std::{convert::TryFrom, str::FromStr}, }; lazy_static! { @@ -66,113 +66,6 @@ fn compile_instructions(ixs: &[Instruction], keys: &[Pubkey]) -> Vec, - pub unsigned_keys: Vec, - pub num_readonly_signed_accounts: u8, - pub num_readonly_unsigned_accounts: u8, -} - -impl InstructionKeys { - fn new( - signed_keys: Vec, - unsigned_keys: Vec, - num_readonly_signed_accounts: u8, - num_readonly_unsigned_accounts: u8, - ) -> Self { - Self { - signed_keys, - unsigned_keys, - num_readonly_signed_accounts, - num_readonly_unsigned_accounts, - } - } -} - -/// 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, -/// including program ids, are placed last in the set. No duplicates and order is preserved. -fn get_keys(instructions: &[Instruction], payer: Option<&Pubkey>) -> InstructionKeys { - let programs: Vec<_> = get_program_ids(instructions) - .iter() - .map(|program_id| AccountMeta { - pubkey: *program_id, - is_signer: false, - is_writable: false, - }) - .collect(); - let mut keys_and_signed: Vec<_> = instructions - .iter() - .flat_map(|ix| ix.accounts.iter()) - .collect(); - keys_and_signed.extend(&programs); - keys_and_signed.sort_by(|x, y| { - y.is_signer - .cmp(&x.is_signer) - .then(y.is_writable.cmp(&x.is_writable)) - }); - - let payer_account_meta; - if let Some(payer) = payer { - payer_account_meta = AccountMeta { - pubkey: *payer, - is_signer: true, - is_writable: true, - }; - keys_and_signed.insert(0, &payer_account_meta); - } - - let mut unique_metas: Vec = vec![]; - for account_meta in keys_and_signed { - // Promote to writable if a later AccountMeta requires it - if let Some(x) = unique_metas - .iter_mut() - .find(|x| x.pubkey == account_meta.pubkey) - { - x.is_writable |= account_meta.is_writable; - continue; - } - unique_metas.push(account_meta.clone()); - } - - let mut signed_keys = vec![]; - let mut unsigned_keys = vec![]; - let mut num_readonly_signed_accounts = 0; - let mut num_readonly_unsigned_accounts = 0; - for account_meta in unique_metas { - if account_meta.is_signer { - signed_keys.push(account_meta.pubkey); - if !account_meta.is_writable { - num_readonly_signed_accounts += 1; - } - } else { - unsigned_keys.push(account_meta.pubkey); - if !account_meta.is_writable { - num_readonly_unsigned_accounts += 1; - } - } - } - InstructionKeys::new( - signed_keys, - unsigned_keys, - num_readonly_signed_accounts, - num_readonly_unsigned_accounts, - ) -} - -/// Return program ids referenced by all instructions. No duplicates and order is preserved. -fn get_program_ids(instructions: &[Instruction]) -> Vec { - let mut set = BTreeSet::new(); - instructions - .iter() - .map(|ix| ix.program_id) - .filter(|&program_id| set.insert(program_id)) - .collect() -} - /// A Solana transaction message (legacy). /// /// See the [`message`] module documentation for further description. @@ -397,20 +290,16 @@ impl Message { payer: Option<&Pubkey>, blockhash: &Hash, ) -> Self { - let InstructionKeys { - mut signed_keys, - unsigned_keys, - num_readonly_signed_accounts, - num_readonly_unsigned_accounts, - } = get_keys(instructions, payer); - let num_required_signatures = signed_keys.len() as u8; - signed_keys.extend(&unsigned_keys); - let instructions = compile_instructions(instructions, &signed_keys); + let compiled_keys = CompiledKeys::compile(instructions, payer.cloned()); + let (header, account_keys) = compiled_keys + .try_into_message_components() + .expect("overflow when compiling message keys"); + let instructions = compile_instructions(instructions, &account_keys); Self::new_with_compiled_instructions( - num_required_signatures, - num_readonly_signed_accounts, - num_readonly_unsigned_accounts, - signed_keys, + header.num_required_signatures, + header.num_readonly_signed_accounts, + header.num_readonly_unsigned_accounts, + account_keys, *blockhash, instructions, ) @@ -713,16 +602,6 @@ mod tests { std::collections::HashSet, }; - #[test] - fn test_message_unique_program_ids() { - let program_id0 = Pubkey::default(); - let program_ids = get_program_ids(&[ - Instruction::new_with_bincode(program_id0, &0, vec![]), - Instruction::new_with_bincode(program_id0, &0, vec![]), - ]); - assert_eq!(program_ids, vec![program_id0]); - } - #[test] fn test_builtin_program_keys() { let keys: HashSet = BUILTIN_PROGRAMS_KEYS.iter().copied().collect(); @@ -744,174 +623,6 @@ mod tests { ); } - #[test] - fn test_message_unique_program_ids_not_adjacent() { - let program_id0 = Pubkey::default(); - let program_id1 = Pubkey::new_unique(); - let program_ids = get_program_ids(&[ - Instruction::new_with_bincode(program_id0, &0, vec![]), - Instruction::new_with_bincode(program_id1, &0, vec![]), - Instruction::new_with_bincode(program_id0, &0, vec![]), - ]); - assert_eq!(program_ids, vec![program_id0, program_id1]); - } - - #[test] - fn test_message_unique_program_ids_order_preserved() { - let program_id0 = Pubkey::new_unique(); - let program_id1 = Pubkey::default(); // Key less than program_id0 - let program_ids = get_program_ids(&[ - Instruction::new_with_bincode(program_id0, &0, vec![]), - Instruction::new_with_bincode(program_id1, &0, vec![]), - Instruction::new_with_bincode(program_id0, &0, vec![]), - ]); - assert_eq!(program_ids, vec![program_id0, program_id1]); - } - - #[test] - fn test_message_unique_keys_both_signed() { - let program_id = Pubkey::default(); - let id0 = Pubkey::default(); - let keys = get_keys( - &[ - Instruction::new_with_bincode(program_id, &0, vec![AccountMeta::new(id0, true)]), - Instruction::new_with_bincode(program_id, &0, vec![AccountMeta::new(id0, true)]), - ], - None, - ); - assert_eq!(keys, InstructionKeys::new(vec![id0], vec![], 0, 0)); - } - - #[test] - fn test_message_unique_keys_signed_and_payer() { - let program_id = Pubkey::default(); - let id0 = Pubkey::default(); - let keys = get_keys( - &[Instruction::new_with_bincode( - program_id, - &0, - vec![AccountMeta::new(id0, true)], - )], - Some(&id0), - ); - assert_eq!(keys, InstructionKeys::new(vec![id0], vec![], 0, 0)); - } - - #[test] - fn test_message_unique_keys_unsigned_and_payer() { - let program_id = Pubkey::default(); - let id0 = Pubkey::default(); - let keys = get_keys( - &[Instruction::new_with_bincode( - program_id, - &0, - vec![AccountMeta::new(id0, false)], - )], - Some(&id0), - ); - assert_eq!(keys, InstructionKeys::new(vec![id0], vec![], 0, 0)); - } - - #[test] - fn test_message_unique_keys_one_signed() { - let program_id = Pubkey::default(); - let id0 = Pubkey::default(); - let keys = get_keys( - &[ - Instruction::new_with_bincode(program_id, &0, vec![AccountMeta::new(id0, false)]), - Instruction::new_with_bincode(program_id, &0, vec![AccountMeta::new(id0, true)]), - ], - None, - ); - assert_eq!(keys, InstructionKeys::new(vec![id0], vec![], 0, 0)); - } - - #[test] - fn test_message_unique_keys_one_readonly_signed() { - let program_id = Pubkey::default(); - let id0 = Pubkey::default(); - let keys = get_keys( - &[ - Instruction::new_with_bincode( - program_id, - &0, - vec![AccountMeta::new_readonly(id0, true)], - ), - Instruction::new_with_bincode(program_id, &0, vec![AccountMeta::new(id0, true)]), - ], - None, - ); - - // Ensure the key is no longer readonly - assert_eq!(keys, InstructionKeys::new(vec![id0], vec![], 0, 0)); - } - - #[test] - fn test_message_unique_keys_one_readonly_unsigned() { - let program_id = Pubkey::default(); - let id0 = Pubkey::default(); - let keys = get_keys( - &[ - Instruction::new_with_bincode( - program_id, - &0, - vec![AccountMeta::new_readonly(id0, false)], - ), - Instruction::new_with_bincode(program_id, &0, vec![AccountMeta::new(id0, false)]), - ], - None, - ); - - // Ensure the key is no longer readonly - assert_eq!(keys, InstructionKeys::new(vec![], vec![id0], 0, 0)); - } - - #[test] - fn test_message_unique_keys_order_preserved() { - let program_id = Pubkey::default(); - let id0 = Pubkey::new_unique(); - let id1 = Pubkey::default(); // Key less than id0 - let keys = get_keys( - &[ - Instruction::new_with_bincode(program_id, &0, vec![AccountMeta::new(id0, false)]), - Instruction::new_with_bincode(program_id, &0, vec![AccountMeta::new(id1, false)]), - ], - None, - ); - assert_eq!(keys, InstructionKeys::new(vec![], vec![id0, id1], 0, 0)); - } - - #[test] - fn test_message_unique_keys_not_adjacent() { - let program_id = Pubkey::default(); - let id0 = Pubkey::default(); - let id1 = Pubkey::new_unique(); - let keys = get_keys( - &[ - Instruction::new_with_bincode(program_id, &0, vec![AccountMeta::new(id0, false)]), - Instruction::new_with_bincode(program_id, &0, vec![AccountMeta::new(id1, false)]), - Instruction::new_with_bincode(program_id, &0, vec![AccountMeta::new(id0, true)]), - ], - None, - ); - assert_eq!(keys, InstructionKeys::new(vec![id0], vec![id1], 0, 0)); - } - - #[test] - fn test_message_signed_keys_first() { - let program_id = Pubkey::default(); - let id0 = Pubkey::default(); - let id1 = Pubkey::new_unique(); - let keys = get_keys( - &[ - Instruction::new_with_bincode(program_id, &0, vec![AccountMeta::new(id0, false)]), - Instruction::new_with_bincode(program_id, &0, vec![AccountMeta::new(id1, true)]), - ], - None, - ); - assert_eq!(keys, InstructionKeys::new(vec![id1], vec![id0], 0, 0)); - } - #[test] // Ensure there's a way to calculate the number of required signatures. fn test_message_signed_keys_len() { @@ -926,36 +637,6 @@ mod tests { assert_eq!(message.header.num_required_signatures, 1); } - #[test] - fn test_message_readonly_keys_last() { - let program_id = Pubkey::default(); - let id0 = Pubkey::default(); // Identical key/program_id should be de-duped - let id1 = Pubkey::new_unique(); - let id2 = Pubkey::new_unique(); - let id3 = Pubkey::new_unique(); - let keys = get_keys( - &[ - Instruction::new_with_bincode( - program_id, - &0, - vec![AccountMeta::new_readonly(id0, false)], - ), - Instruction::new_with_bincode( - program_id, - &0, - vec![AccountMeta::new_readonly(id1, true)], - ), - Instruction::new_with_bincode(program_id, &0, vec![AccountMeta::new(id2, false)]), - Instruction::new_with_bincode(program_id, &0, vec![AccountMeta::new(id3, true)]), - ], - None, - ); - assert_eq!( - keys, - InstructionKeys::new(vec![id3, id1], vec![id2, id0], 1, 1) - ); - } - #[test] fn test_message_kitchen_sink() { let program_id0 = Pubkey::new_unique(); @@ -1007,32 +688,6 @@ mod tests { assert_eq!(message.header.num_required_signatures, 2); } - #[test] - fn test_message_program_last() { - let program_id = Pubkey::default(); - let id0 = Pubkey::new_unique(); - let id1 = Pubkey::new_unique(); - let keys = get_keys( - &[ - Instruction::new_with_bincode( - program_id, - &0, - vec![AccountMeta::new_readonly(id0, false)], - ), - Instruction::new_with_bincode( - program_id, - &0, - vec![AccountMeta::new_readonly(id1, true)], - ), - ], - None, - ); - assert_eq!( - keys, - InstructionKeys::new(vec![id1], vec![id0, program_id], 1, 2) - ); - } - #[test] fn test_program_position() { let program_id0 = Pubkey::default(); @@ -1103,7 +758,7 @@ mod tests { ); assert_eq!( message.get_account_keys_by_lock_type(), - (vec![&id1, &id0], vec![&id3, &id2, &program_id]) + (vec![&id1, &id0], vec![&id3, &program_id, &id2]) ); } @@ -1199,7 +854,7 @@ mod tests { let message = Message::new(&instructions, Some(&id1)); assert_eq!( message.hash(), - Hash::from_str("CXRH7GHLieaQZRUjH1mpnNnUZQtU4V4RpJpAFgy77i3z").unwrap() + Hash::from_str("7VWCF4quo2CcWQFNUayZiorxpiR5ix8YzLebrXKf3fMF").unwrap() ) } } diff --git a/sdk/program/src/message/mod.rs b/sdk/program/src/message/mod.rs index 964c2f5b94..be1a0db4fe 100644 --- a/sdk/program/src/message/mod.rs +++ b/sdk/program/src/message/mod.rs @@ -37,6 +37,7 @@ //! types continue to be exposed to Solana programs, for backwards compatibility //! reasons. +mod compiled_keys; pub mod legacy; #[cfg(not(target_arch = "bpf"))] @@ -49,6 +50,7 @@ mod non_bpf_modules { pub use {account_keys::*, sanitized::*, versions::*}; } +use compiled_keys::*; pub use legacy::Message; #[cfg(not(target_arch = "bpf"))] pub use non_bpf_modules::*; diff --git a/sdk/src/transaction_context.rs b/sdk/src/transaction_context.rs index eb7b6eb4a3..59dd5e6411 100644 --- a/sdk/src/transaction_context.rs +++ b/sdk/src/transaction_context.rs @@ -102,6 +102,11 @@ impl TransactionContext { .ok_or(InstructionError::NotEnoughAccountKeys) } + /// Returns the keys for the accounts loaded in this Transaction + pub fn get_keys_of_accounts(&self) -> &[Pubkey] { + &self.account_keys + } + /// Searches for an account by its key pub fn get_account_at_index( &self, diff --git a/transaction-status/src/parse_bpf_loader.rs b/transaction-status/src/parse_bpf_loader.rs index 68a01c9ab7..307b2f3990 100644 --- a/transaction-status/src/parse_bpf_loader.rs +++ b/transaction-status/src/parse_bpf_loader.rs @@ -160,8 +160,10 @@ mod test { super::*, serde_json::Value, solana_sdk::{ + bpf_loader_upgradeable, message::Message, pubkey::{self, Pubkey}, + system_program, sysvar, }, }; @@ -248,19 +250,16 @@ mod test { } #[test] - fn test_parse_bpf_upgradeable_loader_instructions() { - let mut keys: Vec = vec![]; - for _ in 0..8 { - keys.push(Pubkey::new_unique()); - } - let offset = 4242; - let bytes = vec![8; 99]; + fn test_parse_bpf_upgradeable_loader_create_buffer_ix() { let max_data_len = 54321; - let instructions = solana_sdk::bpf_loader_upgradeable::create_buffer( - &keys[0], - &keys[1], - &keys[2], + let payer_address = Pubkey::new_unique(); + let buffer_address = Pubkey::new_unique(); + let authority_address = Pubkey::new_unique(); + let instructions = bpf_loader_upgradeable::create_buffer( + &payer_address, + &buffer_address, + &authority_address, 55, max_data_len, ) @@ -269,30 +268,42 @@ mod test { assert_eq!( parse_bpf_upgradeable_loader( &message.instructions[1], - &AccountKeys::new(&keys[0..3], None) + &AccountKeys::new(&message.account_keys, None) ) .unwrap(), ParsedInstructionEnum { instruction_type: "initializeBuffer".to_string(), info: json!({ - "account": keys[1].to_string(), - "authority": keys[2].to_string(), + "account": buffer_address.to_string(), + "authority": authority_address.to_string(), }), } ); assert!(parse_bpf_upgradeable_loader( &message.instructions[1], - &AccountKeys::new(&keys[0..2], None) + &AccountKeys::new(&message.account_keys[0..2], None) ) .is_err()); + } - let instruction = - solana_sdk::bpf_loader_upgradeable::write(&keys[1], &keys[0], offset, bytes.clone()); + #[test] + fn test_parse_bpf_upgradeable_loader_write_ix() { + let offset = 4242; + let bytes = vec![8; 99]; + + let buffer_address = Pubkey::new_unique(); + let authority_address = Pubkey::new_unique(); + let instruction = bpf_loader_upgradeable::write( + &buffer_address, + &authority_address, + offset, + bytes.clone(), + ); let message = Message::new(&[instruction], None); assert_eq!( parse_bpf_upgradeable_loader( &message.instructions[0], - &AccountKeys::new(&keys[0..2], None) + &AccountKeys::new(&message.account_keys, None) ) .unwrap(), ParsedInstructionEnum { @@ -300,22 +311,36 @@ mod test { info: json!({ "offset": offset, "bytes": base64::encode(&bytes), - "account": keys[1].to_string(), - "authority": keys[0].to_string(), + "account": buffer_address.to_string(), + "authority": authority_address.to_string(), }), } ); assert!(parse_bpf_upgradeable_loader( &message.instructions[0], - &AccountKeys::new(&keys[0..1], None) + &AccountKeys::new(&message.account_keys[0..1], None) ) .is_err()); + } - let instructions = solana_sdk::bpf_loader_upgradeable::deploy_with_max_program_len( - &keys[0], - &keys[1], - &keys[4], - &keys[2], + #[test] + fn test_parse_bpf_upgradeable_loader_deploy_ix() { + let max_data_len = 54321; + + let payer_address = Pubkey::new_unique(); + let program_address = Pubkey::new_unique(); + let buffer_address = Pubkey::new_unique(); + let upgrade_authority_address = Pubkey::new_unique(); + let programdata_address = Pubkey::find_program_address( + &[program_address.as_ref()], + &bpf_loader_upgradeable::id(), + ) + .0; + let instructions = bpf_loader_upgradeable::deploy_with_max_program_len( + &payer_address, + &program_address, + &buffer_address, + &upgrade_authority_address, 55, max_data_len, ) @@ -324,153 +349,198 @@ mod test { assert_eq!( parse_bpf_upgradeable_loader( &message.instructions[1], - &AccountKeys::new(&keys[0..8], None) + &AccountKeys::new(&message.account_keys, None) ) .unwrap(), ParsedInstructionEnum { instruction_type: "deployWithMaxDataLen".to_string(), info: json!({ "maxDataLen": max_data_len, - "payerAccount": keys[0].to_string(), - "programAccount": keys[1].to_string(), - "authority": keys[2].to_string(), - "programDataAccount": keys[3].to_string(), - "bufferAccount": keys[4].to_string(), - "rentSysvar": keys[5].to_string(), - "clockSysvar": keys[6].to_string(), - "systemProgram": keys[7].to_string(), + "payerAccount": payer_address.to_string(), + "programAccount": program_address.to_string(), + "authority": upgrade_authority_address.to_string(), + "programDataAccount": programdata_address.to_string(), + "bufferAccount": buffer_address.to_string(), + "rentSysvar": sysvar::rent::ID.to_string(), + "clockSysvar": sysvar::clock::ID.to_string(), + "systemProgram": system_program::ID.to_string(), }), } ); assert!(parse_bpf_upgradeable_loader( &message.instructions[1], - &AccountKeys::new(&keys[0..7], None) + &AccountKeys::new(&message.account_keys[0..7], None) ) .is_err()); + } - let instruction = - solana_sdk::bpf_loader_upgradeable::upgrade(&keys[2], &keys[3], &keys[0], &keys[4]); + #[test] + fn test_parse_bpf_upgradeable_loader_upgrade_ix() { + let program_address = Pubkey::new_unique(); + let buffer_address = Pubkey::new_unique(); + let authority_address = Pubkey::new_unique(); + let spill_address = Pubkey::new_unique(); + let programdata_address = Pubkey::find_program_address( + &[program_address.as_ref()], + &bpf_loader_upgradeable::id(), + ) + .0; + let instruction = bpf_loader_upgradeable::upgrade( + &program_address, + &buffer_address, + &authority_address, + &spill_address, + ); let message = Message::new(&[instruction], None); assert_eq!( parse_bpf_upgradeable_loader( &message.instructions[0], - &AccountKeys::new(&keys[0..7], None) + &AccountKeys::new(&message.account_keys, None) ) .unwrap(), ParsedInstructionEnum { instruction_type: "upgrade".to_string(), info: json!({ - "authority": keys[0].to_string(), - "programDataAccount": keys[1].to_string(), - "programAccount": keys[2].to_string(), - "bufferAccount": keys[3].to_string(), - "spillAccount": keys[4].to_string(), - "rentSysvar": keys[5].to_string(), - "clockSysvar": keys[6].to_string(), + "authority": authority_address.to_string(), + "programDataAccount": programdata_address.to_string(), + "programAccount": program_address.to_string(), + "bufferAccount": buffer_address.to_string(), + "spillAccount": spill_address.to_string(), + "rentSysvar": sysvar::rent::ID.to_string(), + "clockSysvar": sysvar::clock::ID.to_string(), }), } ); assert!(parse_bpf_upgradeable_loader( &message.instructions[0], - &AccountKeys::new(&keys[0..6], None) + &AccountKeys::new(&message.account_keys[0..6], None) ) .is_err()); + } - let instruction = - solana_sdk::bpf_loader_upgradeable::set_buffer_authority(&keys[1], &keys[0], &keys[2]); + #[test] + fn test_parse_bpf_upgradeable_loader_set_buffer_authority_ix() { + let buffer_address = Pubkey::new_unique(); + let current_authority_address = Pubkey::new_unique(); + let new_authority_address = Pubkey::new_unique(); + let instruction = bpf_loader_upgradeable::set_buffer_authority( + &buffer_address, + ¤t_authority_address, + &new_authority_address, + ); let message = Message::new(&[instruction], None); assert_eq!( parse_bpf_upgradeable_loader( &message.instructions[0], - &AccountKeys::new(&keys[0..3], None) + &AccountKeys::new(&message.account_keys, None) ) .unwrap(), ParsedInstructionEnum { instruction_type: "setAuthority".to_string(), info: json!({ - "account": keys[1].to_string(), - "authority": keys[0].to_string(), - "newAuthority": keys[2].to_string(), + "account": buffer_address.to_string(), + "authority": current_authority_address.to_string(), + "newAuthority": new_authority_address.to_string(), }), } ); assert!(parse_bpf_upgradeable_loader( &message.instructions[0], - &AccountKeys::new(&keys[0..1], None) + &AccountKeys::new(&message.account_keys[0..1], None) ) .is_err()); + } - let instruction = solana_sdk::bpf_loader_upgradeable::set_upgrade_authority( - &keys[1], - &keys[0], - Some(&keys[2]), + #[test] + fn test_parse_bpf_upgradeable_loader_set_upgrade_authority_ix() { + let program_address = Pubkey::new_unique(); + let current_authority_address = Pubkey::new_unique(); + let new_authority_address = Pubkey::new_unique(); + let (programdata_address, _) = Pubkey::find_program_address( + &[program_address.as_ref()], + &bpf_loader_upgradeable::id(), + ); + let instruction = bpf_loader_upgradeable::set_upgrade_authority( + &program_address, + ¤t_authority_address, + Some(&new_authority_address), ); let message = Message::new(&[instruction], None); assert_eq!( parse_bpf_upgradeable_loader( &message.instructions[0], - &AccountKeys::new(&keys[0..3], None) + &AccountKeys::new(&message.account_keys, None) ) .unwrap(), ParsedInstructionEnum { instruction_type: "setAuthority".to_string(), info: json!({ - "account": keys[1].to_string(), - "authority": keys[0].to_string(), - "newAuthority": keys[2].to_string(), + "account": programdata_address.to_string(), + "authority": current_authority_address.to_string(), + "newAuthority": new_authority_address.to_string(), }), } ); assert!(parse_bpf_upgradeable_loader( &message.instructions[0], - &AccountKeys::new(&keys[0..1], None) + &AccountKeys::new(&message.account_keys[0..1], None) ) .is_err()); - let instruction = - solana_sdk::bpf_loader_upgradeable::set_upgrade_authority(&keys[1], &keys[0], None); + let instruction = bpf_loader_upgradeable::set_upgrade_authority( + &program_address, + ¤t_authority_address, + None, + ); let message = Message::new(&[instruction], None); assert_eq!( parse_bpf_upgradeable_loader( &message.instructions[0], - &AccountKeys::new(&keys[0..2], None) + &AccountKeys::new(&message.account_keys, None) ) .unwrap(), ParsedInstructionEnum { instruction_type: "setAuthority".to_string(), info: json!({ - "account": keys[1].to_string(), - "authority": keys[0].to_string(), + "account": programdata_address.to_string(), + "authority": current_authority_address.to_string(), "newAuthority": Value::Null, }), } ); assert!(parse_bpf_upgradeable_loader( &message.instructions[0], - &AccountKeys::new(&keys[0..1], None) + &AccountKeys::new(&message.account_keys[0..1], None) ) .is_err()); + } - let instruction = solana_sdk::bpf_loader_upgradeable::close(&keys[0], &keys[1], &keys[2]); + #[test] + fn test_parse_bpf_upgradeable_loader_close_ix() { + let close_address = Pubkey::new_unique(); + let recipient_address = Pubkey::new_unique(); + let authority_address = Pubkey::new_unique(); + let instruction = + bpf_loader_upgradeable::close(&close_address, &recipient_address, &authority_address); let message = Message::new(&[instruction], None); assert_eq!( parse_bpf_upgradeable_loader( &message.instructions[0], - &AccountKeys::new(&keys[..3], None) + &AccountKeys::new(&message.account_keys, None) ) .unwrap(), ParsedInstructionEnum { instruction_type: "close".to_string(), info: json!({ - "account": keys[1].to_string(), - "recipient": keys[2].to_string(), - "authority": keys[0].to_string(), + "account": close_address.to_string(), + "recipient": recipient_address.to_string(), + "authority": authority_address.to_string(), }), } ); assert!(parse_bpf_upgradeable_loader( &message.instructions[0], - &AccountKeys::new(&keys[0..1], None) + &AccountKeys::new(&message.account_keys[0..1], None) ) .is_err()); } diff --git a/transaction-status/src/parse_stake.rs b/transaction-status/src/parse_stake.rs index 00022c5260..043f63c522 100644 --- a/transaction-status/src/parse_stake.rs +++ b/transaction-status/src/parse_stake.rs @@ -284,20 +284,18 @@ mod test { message::Message, pubkey::Pubkey, stake::{ + config, instruction::{self, LockupArgs}, state::{Authorized, Lockup, StakeAuthorize}, }, + sysvar, }, }; #[test] - #[allow(clippy::same_item_push)] - fn test_parse_stake_instruction() { - let mut keys: Vec = vec![]; - for _ in 0..6 { - keys.push(Pubkey::new_unique()); - } - + fn test_parse_stake_initialize_ix() { + let from_pubkey = Pubkey::new_unique(); + let stake_pubkey = Pubkey::new_unique(); let authorized = Authorized { staker: Pubkey::new_unique(), withdrawer: Pubkey::new_unique(), @@ -309,20 +307,25 @@ mod test { }; let lamports = 55; - let instructions = - instruction::create_account(&keys[0], &keys[1], &authorized, &lockup, lamports); + let instructions = instruction::create_account( + &from_pubkey, + &stake_pubkey, + &authorized, + &lockup, + lamports, + ); let message = Message::new(&instructions, None); assert_eq!( parse_stake( &message.instructions[1], - &AccountKeys::new(&keys[0..3], None) + &AccountKeys::new(&message.account_keys, None) ) .unwrap(), ParsedInstructionEnum { instruction_type: "initialize".to_string(), info: json!({ - "stakeAccount": keys[1].to_string(), - "rentSysvar": keys[2].to_string(), + "stakeAccount": stake_pubkey.to_string(), + "rentSysvar": sysvar::rent::ID.to_string(), "authorized": { "staker": authorized.staker.to_string(), "withdrawer": authorized.withdrawer.to_string(), @@ -337,225 +340,21 @@ mod test { ); assert!(parse_stake( &message.instructions[1], - &AccountKeys::new(&keys[0..2], None) - ) - .is_err()); - - let instruction = - instruction::authorize(&keys[1], &keys[0], &keys[3], StakeAuthorize::Staker, None); - let message = Message::new(&[instruction], None); - assert_eq!( - parse_stake( - &message.instructions[0], - &AccountKeys::new(&keys[0..3], None) - ) - .unwrap(), - ParsedInstructionEnum { - instruction_type: "authorize".to_string(), - info: json!({ - "stakeAccount": keys[1].to_string(), - "clockSysvar": keys[2].to_string(), - "authority": keys[0].to_string(), - "newAuthority": keys[3].to_string(), - "authorityType": StakeAuthorize::Staker, - }), - } - ); - assert!(parse_stake( - &message.instructions[0], - &AccountKeys::new(&keys[0..2], None) + &AccountKeys::new(&message.account_keys[0..2], None) ) .is_err()); + } + #[test] + fn test_parse_stake_authorize_ix() { + let stake_pubkey = Pubkey::new_unique(); + let authorized_pubkey = Pubkey::new_unique(); + let new_authorized_pubkey = Pubkey::new_unique(); + let custodian_pubkey = Pubkey::new_unique(); let instruction = instruction::authorize( - &keys[2], - &keys[0], - &keys[4], - StakeAuthorize::Withdrawer, - Some(&keys[1]), - ); - let message = Message::new(&[instruction], None); - assert_eq!( - parse_stake( - &message.instructions[0], - &AccountKeys::new(&keys[0..4], None) - ) - .unwrap(), - ParsedInstructionEnum { - instruction_type: "authorize".to_string(), - info: json!({ - "stakeAccount": keys[2].to_string(), - "clockSysvar": keys[3].to_string(), - "authority": keys[0].to_string(), - "newAuthority": keys[4].to_string(), - "authorityType": StakeAuthorize::Withdrawer, - "custodian": keys[1].to_string(), - }), - } - ); - assert!(parse_stake( - &message.instructions[0], - &AccountKeys::new(&keys[0..2], None) - ) - .is_err()); - - let instruction = instruction::delegate_stake(&keys[1], &keys[0], &keys[2]); - let message = Message::new(&[instruction], None); - assert_eq!( - parse_stake( - &message.instructions[0], - &AccountKeys::new(&keys[0..6], None) - ) - .unwrap(), - ParsedInstructionEnum { - instruction_type: "delegate".to_string(), - info: json!({ - "stakeAccount": keys[1].to_string(), - "voteAccount": keys[2].to_string(), - "clockSysvar": keys[3].to_string(), - "stakeHistorySysvar": keys[4].to_string(), - "stakeConfigAccount": keys[5].to_string(), - "stakeAuthority": keys[0].to_string(), - }), - } - ); - assert!(parse_stake( - &message.instructions[0], - &AccountKeys::new(&keys[0..5], None) - ) - .is_err()); - - // This looks wrong, but in an actual compiled instruction, the order is: - // * split account (signer, allocate + assign first) - // * stake authority (signer) - // * stake account - let instructions = instruction::split(&keys[2], &keys[1], lamports, &keys[0]); - let message = Message::new(&instructions, None); - assert_eq!( - parse_stake( - &message.instructions[2], - &AccountKeys::new(&keys[0..3], None) - ) - .unwrap(), - ParsedInstructionEnum { - instruction_type: "split".to_string(), - info: json!({ - "stakeAccount": keys[2].to_string(), - "newSplitAccount": keys[0].to_string(), - "stakeAuthority": keys[1].to_string(), - "lamports": lamports, - }), - } - ); - assert!(parse_stake( - &message.instructions[2], - &AccountKeys::new(&keys[0..2], None) - ) - .is_err()); - - let instruction = instruction::withdraw(&keys[1], &keys[0], &keys[2], lamports, None); - let message = Message::new(&[instruction], None); - assert_eq!( - parse_stake( - &message.instructions[0], - &AccountKeys::new(&keys[0..5], None) - ) - .unwrap(), - ParsedInstructionEnum { - instruction_type: "withdraw".to_string(), - info: json!({ - "stakeAccount": keys[1].to_string(), - "destination": keys[2].to_string(), - "clockSysvar": keys[3].to_string(), - "stakeHistorySysvar": keys[4].to_string(), - "withdrawAuthority": keys[0].to_string(), - "lamports": lamports, - }), - } - ); - let instruction = - instruction::withdraw(&keys[2], &keys[0], &keys[3], lamports, Some(&keys[1])); - let message = Message::new(&[instruction], None); - assert_eq!( - parse_stake( - &message.instructions[0], - &AccountKeys::new(&keys[0..6], None) - ) - .unwrap(), - ParsedInstructionEnum { - instruction_type: "withdraw".to_string(), - info: json!({ - "stakeAccount": keys[2].to_string(), - "destination": keys[3].to_string(), - "clockSysvar": keys[4].to_string(), - "stakeHistorySysvar": keys[5].to_string(), - "withdrawAuthority": keys[0].to_string(), - "custodian": keys[1].to_string(), - "lamports": lamports, - }), - } - ); - assert!(parse_stake( - &message.instructions[0], - &AccountKeys::new(&keys[0..4], None) - ) - .is_err()); - - let instruction = instruction::deactivate_stake(&keys[1], &keys[0]); - let message = Message::new(&[instruction], None); - assert_eq!( - parse_stake( - &message.instructions[0], - &AccountKeys::new(&keys[0..3], None) - ) - .unwrap(), - ParsedInstructionEnum { - instruction_type: "deactivate".to_string(), - info: json!({ - "stakeAccount": keys[1].to_string(), - "clockSysvar": keys[2].to_string(), - "stakeAuthority": keys[0].to_string(), - }), - } - ); - assert!(parse_stake( - &message.instructions[0], - &AccountKeys::new(&keys[0..2], None) - ) - .is_err()); - - let instructions = instruction::merge(&keys[1], &keys[0], &keys[2]); - let message = Message::new(&instructions, None); - assert_eq!( - parse_stake( - &message.instructions[0], - &AccountKeys::new(&keys[0..5], None) - ) - .unwrap(), - ParsedInstructionEnum { - instruction_type: "merge".to_string(), - info: json!({ - "destination": keys[1].to_string(), - "source": keys[2].to_string(), - "clockSysvar": keys[3].to_string(), - "stakeHistorySysvar": keys[4].to_string(), - "stakeAuthority": keys[0].to_string(), - }), - } - ); - assert!(parse_stake( - &message.instructions[0], - &AccountKeys::new(&keys[0..4], None) - ) - .is_err()); - - let seed = "test_seed"; - let instruction = instruction::authorize_with_seed( - &keys[1], - &keys[0], - seed.to_string(), - &keys[0], - &keys[3], + &stake_pubkey, + &authorized_pubkey, + &new_authorized_pubkey, StakeAuthorize::Staker, None, ); @@ -563,61 +362,335 @@ mod test { assert_eq!( parse_stake( &message.instructions[0], - &AccountKeys::new(&keys[0..3], None) + &AccountKeys::new(&message.account_keys, None) ) .unwrap(), ParsedInstructionEnum { - instruction_type: "authorizeWithSeed".to_string(), + instruction_type: "authorize".to_string(), info: json!({ - "stakeAccount": keys[1].to_string(), - "authorityOwner": keys[0].to_string(), - "newAuthorized": keys[3].to_string(), - "authorityBase": keys[0].to_string(), - "authoritySeed": seed, + "stakeAccount": stake_pubkey.to_string(), + "clockSysvar": sysvar::clock::ID.to_string(), + "authority": authorized_pubkey.to_string(), + "newAuthority": new_authorized_pubkey.to_string(), "authorityType": StakeAuthorize::Staker, - "clockSysvar": keys[2].to_string(), }), } ); assert!(parse_stake( &message.instructions[0], - &AccountKeys::new(&keys[0..2], None) + &AccountKeys::new(&message.account_keys[0..2], None) ) .is_err()); - let instruction = instruction::authorize_with_seed( - &keys[2], - &keys[0], - seed.to_string(), - &keys[0], - &keys[4], + let instruction = instruction::authorize( + &stake_pubkey, + &authorized_pubkey, + &new_authorized_pubkey, StakeAuthorize::Withdrawer, - Some(&keys[1]), + Some(&custodian_pubkey), ); let message = Message::new(&[instruction], None); assert_eq!( parse_stake( &message.instructions[0], - &AccountKeys::new(&keys[0..4], None) + &AccountKeys::new(&message.account_keys, None) ) .unwrap(), ParsedInstructionEnum { - instruction_type: "authorizeWithSeed".to_string(), + instruction_type: "authorize".to_string(), info: json!({ - "stakeAccount": keys[2].to_string(), - "authorityOwner": keys[0].to_string(), - "newAuthorized": keys[4].to_string(), - "authorityBase": keys[0].to_string(), - "authoritySeed": seed, + "stakeAccount": stake_pubkey.to_string(), + "clockSysvar": sysvar::clock::ID.to_string(), + "authority": authorized_pubkey.to_string(), + "newAuthority": new_authorized_pubkey.to_string(), "authorityType": StakeAuthorize::Withdrawer, - "clockSysvar": keys[3].to_string(), - "custodian": keys[1].to_string(), + "custodian": custodian_pubkey.to_string(), }), } ); assert!(parse_stake( &message.instructions[0], - &AccountKeys::new(&keys[0..3], None) + &AccountKeys::new(&message.account_keys[0..2], None) + ) + .is_err()); + } + + #[test] + fn test_parse_stake_delegate_ix() { + let stake_pubkey = Pubkey::new_unique(); + let authorized_pubkey = Pubkey::new_unique(); + let vote_pubkey = Pubkey::new_unique(); + let instruction = + instruction::delegate_stake(&stake_pubkey, &authorized_pubkey, &vote_pubkey); + let message = Message::new(&[instruction], None); + assert_eq!( + parse_stake( + &message.instructions[0], + &AccountKeys::new(&message.account_keys, None) + ) + .unwrap(), + ParsedInstructionEnum { + instruction_type: "delegate".to_string(), + info: json!({ + "stakeAccount": stake_pubkey.to_string(), + "voteAccount": vote_pubkey.to_string(), + "clockSysvar": sysvar::clock::ID.to_string(), + "stakeHistorySysvar": sysvar::stake_history::ID.to_string(), + "stakeConfigAccount": config::ID.to_string(), + "stakeAuthority": authorized_pubkey.to_string(), + }), + } + ); + assert!(parse_stake( + &message.instructions[0], + &AccountKeys::new(&message.account_keys[0..5], None) + ) + .is_err()); + } + + #[test] + fn test_parse_stake_split_ix() { + let lamports = 55; + let stake_pubkey = Pubkey::new_unique(); + let authorized_pubkey = Pubkey::new_unique(); + let split_stake_pubkey = Pubkey::new_unique(); + let instructions = instruction::split( + &stake_pubkey, + &authorized_pubkey, + lamports, + &split_stake_pubkey, + ); + let message = Message::new(&instructions, None); + assert_eq!( + parse_stake( + &message.instructions[2], + &AccountKeys::new(&message.account_keys, None) + ) + .unwrap(), + ParsedInstructionEnum { + instruction_type: "split".to_string(), + info: json!({ + "stakeAccount": stake_pubkey.to_string(), + "newSplitAccount": split_stake_pubkey.to_string(), + "stakeAuthority": authorized_pubkey.to_string(), + "lamports": lamports, + }), + } + ); + assert!(parse_stake( + &message.instructions[2], + &AccountKeys::new(&message.account_keys[0..2], None) + ) + .is_err()); + } + + #[test] + fn test_parse_stake_withdraw_ix() { + let lamports = 55; + let stake_pubkey = Pubkey::new_unique(); + let withdrawer_pubkey = Pubkey::new_unique(); + let to_pubkey = Pubkey::new_unique(); + let custodian_pubkey = Pubkey::new_unique(); + let instruction = instruction::withdraw( + &stake_pubkey, + &withdrawer_pubkey, + &to_pubkey, + lamports, + None, + ); + let message = Message::new(&[instruction], None); + assert_eq!( + parse_stake( + &message.instructions[0], + &AccountKeys::new(&message.account_keys, None) + ) + .unwrap(), + ParsedInstructionEnum { + instruction_type: "withdraw".to_string(), + info: json!({ + "stakeAccount": stake_pubkey.to_string(), + "destination": to_pubkey.to_string(), + "clockSysvar": sysvar::clock::ID.to_string(), + "stakeHistorySysvar": sysvar::stake_history::ID.to_string(), + "withdrawAuthority": withdrawer_pubkey.to_string(), + "lamports": lamports, + }), + } + ); + let instruction = instruction::withdraw( + &stake_pubkey, + &withdrawer_pubkey, + &to_pubkey, + lamports, + Some(&custodian_pubkey), + ); + let message = Message::new(&[instruction], None); + assert_eq!( + parse_stake( + &message.instructions[0], + &AccountKeys::new(&message.account_keys, None) + ) + .unwrap(), + ParsedInstructionEnum { + instruction_type: "withdraw".to_string(), + info: json!({ + "stakeAccount": stake_pubkey.to_string(), + "destination": to_pubkey.to_string(), + "clockSysvar": sysvar::clock::ID.to_string(), + "stakeHistorySysvar": sysvar::stake_history::ID.to_string(), + "withdrawAuthority": withdrawer_pubkey.to_string(), + "custodian": custodian_pubkey.to_string(), + "lamports": lamports, + }), + } + ); + assert!(parse_stake( + &message.instructions[0], + &AccountKeys::new(&message.account_keys[0..4], None) + ) + .is_err()); + } + + #[test] + fn test_parse_stake_deactivate_stake_ix() { + let stake_pubkey = Pubkey::new_unique(); + let authorized_pubkey = Pubkey::new_unique(); + let instruction = instruction::deactivate_stake(&stake_pubkey, &authorized_pubkey); + let message = Message::new(&[instruction], None); + assert_eq!( + parse_stake( + &message.instructions[0], + &AccountKeys::new(&message.account_keys, None) + ) + .unwrap(), + ParsedInstructionEnum { + instruction_type: "deactivate".to_string(), + info: json!({ + "stakeAccount": stake_pubkey.to_string(), + "clockSysvar": sysvar::clock::ID.to_string(), + "stakeAuthority": authorized_pubkey.to_string(), + }), + } + ); + assert!(parse_stake( + &message.instructions[0], + &AccountKeys::new(&message.account_keys[0..2], None) + ) + .is_err()); + } + + #[test] + fn test_parse_stake_merge_ix() { + let destination_stake_pubkey = Pubkey::new_unique(); + let source_stake_pubkey = Pubkey::new_unique(); + let authorized_pubkey = Pubkey::new_unique(); + let instructions = instruction::merge( + &destination_stake_pubkey, + &source_stake_pubkey, + &authorized_pubkey, + ); + let message = Message::new(&instructions, None); + assert_eq!( + parse_stake( + &message.instructions[0], + &AccountKeys::new(&message.account_keys, None) + ) + .unwrap(), + ParsedInstructionEnum { + instruction_type: "merge".to_string(), + info: json!({ + "destination": destination_stake_pubkey.to_string(), + "source": source_stake_pubkey.to_string(), + "clockSysvar": sysvar::clock::ID.to_string(), + "stakeHistorySysvar": sysvar::stake_history::ID.to_string(), + "stakeAuthority": authorized_pubkey.to_string(), + }), + } + ); + assert!(parse_stake( + &message.instructions[0], + &AccountKeys::new(&message.account_keys[0..4], None) + ) + .is_err()); + } + + #[test] + fn test_parse_stake_authorize_with_seed_ix() { + let stake_pubkey = Pubkey::new_unique(); + let authority_base_pubkey = Pubkey::new_unique(); + let authority_owner_pubkey = Pubkey::new_unique(); + let new_authorized_pubkey = Pubkey::new_unique(); + let custodian_pubkey = Pubkey::new_unique(); + + let seed = "test_seed"; + let instruction = instruction::authorize_with_seed( + &stake_pubkey, + &authority_base_pubkey, + seed.to_string(), + &authority_owner_pubkey, + &new_authorized_pubkey, + StakeAuthorize::Staker, + None, + ); + let message = Message::new(&[instruction], None); + assert_eq!( + parse_stake( + &message.instructions[0], + &AccountKeys::new(&message.account_keys, None) + ) + .unwrap(), + ParsedInstructionEnum { + instruction_type: "authorizeWithSeed".to_string(), + info: json!({ + "stakeAccount": stake_pubkey.to_string(), + "authorityOwner": authority_owner_pubkey.to_string(), + "newAuthorized": new_authorized_pubkey.to_string(), + "authorityBase": authority_base_pubkey.to_string(), + "authoritySeed": seed, + "authorityType": StakeAuthorize::Staker, + "clockSysvar": sysvar::clock::ID.to_string(), + }), + } + ); + assert!(parse_stake( + &message.instructions[0], + &AccountKeys::new(&message.account_keys[0..2], None) + ) + .is_err()); + + let instruction = instruction::authorize_with_seed( + &stake_pubkey, + &authority_base_pubkey, + seed.to_string(), + &authority_owner_pubkey, + &new_authorized_pubkey, + StakeAuthorize::Withdrawer, + Some(&custodian_pubkey), + ); + let message = Message::new(&[instruction], None); + assert_eq!( + parse_stake( + &message.instructions[0], + &AccountKeys::new(&message.account_keys, None) + ) + .unwrap(), + ParsedInstructionEnum { + instruction_type: "authorizeWithSeed".to_string(), + info: json!({ + "stakeAccount": stake_pubkey.to_string(), + "authorityOwner": authority_owner_pubkey.to_string(), + "newAuthorized": new_authorized_pubkey.to_string(), + "authorityBase": authority_base_pubkey.to_string(), + "authoritySeed": seed, + "authorityType": StakeAuthorize::Withdrawer, + "clockSysvar": sysvar::clock::ID.to_string(), + "custodian": custodian_pubkey.to_string(), + }), + } + ); + assert!(parse_stake( + &message.instructions[0], + &AccountKeys::new(&message.account_keys[0..3], None) ) .is_err()); } @@ -807,48 +880,53 @@ mod test { } #[test] - #[allow(clippy::same_item_push)] - fn test_parse_stake_checked_instructions() { - let mut keys: Vec = vec![]; - for _ in 0..6 { - keys.push(Pubkey::new_unique()); - } + fn test_parse_stake_create_account_checked_ix() { + let from_pubkey = Pubkey::new_unique(); + let stake_pubkey = Pubkey::new_unique(); let authorized = Authorized { - staker: keys[3], - withdrawer: keys[0], + staker: Pubkey::new_unique(), + withdrawer: Pubkey::new_unique(), }; let lamports = 55; let instructions = - instruction::create_account_checked(&keys[0], &keys[1], &authorized, lamports); + instruction::create_account_checked(&from_pubkey, &stake_pubkey, &authorized, lamports); let message = Message::new(&instructions, None); assert_eq!( parse_stake( &message.instructions[1], - &AccountKeys::new(&keys[0..4], None) + &AccountKeys::new(&message.account_keys, None) ) .unwrap(), ParsedInstructionEnum { instruction_type: "initializeChecked".to_string(), info: json!({ - "stakeAccount": keys[1].to_string(), - "rentSysvar": keys[2].to_string(), - "staker": keys[3].to_string(), - "withdrawer": keys[0].to_string(), + "stakeAccount": stake_pubkey.to_string(), + "rentSysvar": sysvar::rent::ID.to_string(), + "staker": authorized.staker.to_string(), + "withdrawer": authorized.withdrawer.to_string(), }), } ); assert!(parse_stake( &message.instructions[1], - &AccountKeys::new(&keys[0..3], None) + &AccountKeys::new(&message.account_keys[0..3], None) ) .is_err()); + } + + #[test] + fn test_parse_stake_authorize_checked_ix() { + let stake_pubkey = Pubkey::new_unique(); + let authorized_pubkey = Pubkey::new_unique(); + let new_authorized_pubkey = Pubkey::new_unique(); + let custodian_pubkey = Pubkey::new_unique(); let instruction = instruction::authorize_checked( - &keys[2], - &keys[0], - &keys[1], + &stake_pubkey, + &authorized_pubkey, + &new_authorized_pubkey, StakeAuthorize::Staker, None, ); @@ -856,65 +934,74 @@ mod test { assert_eq!( parse_stake( &message.instructions[0], - &AccountKeys::new(&keys[0..4], None) + &AccountKeys::new(&message.account_keys, None) ) .unwrap(), ParsedInstructionEnum { instruction_type: "authorizeChecked".to_string(), info: json!({ - "stakeAccount": keys[2].to_string(), - "clockSysvar": keys[3].to_string(), - "authority": keys[0].to_string(), - "newAuthority": keys[1].to_string(), + "stakeAccount": stake_pubkey.to_string(), + "clockSysvar": sysvar::clock::ID.to_string(), + "authority": authorized_pubkey.to_string(), + "newAuthority": new_authorized_pubkey.to_string(), "authorityType": StakeAuthorize::Staker, }), } ); assert!(parse_stake( &message.instructions[0], - &AccountKeys::new(&keys[0..3], None) + &AccountKeys::new(&message.account_keys[0..3], None) ) .is_err()); let instruction = instruction::authorize_checked( - &keys[3], - &keys[0], - &keys[1], + &stake_pubkey, + &authorized_pubkey, + &new_authorized_pubkey, StakeAuthorize::Withdrawer, - Some(&keys[2]), + Some(&custodian_pubkey), ); let message = Message::new(&[instruction], None); assert_eq!( parse_stake( &message.instructions[0], - &AccountKeys::new(&keys[0..5], None) + &AccountKeys::new(&message.account_keys, None) ) .unwrap(), ParsedInstructionEnum { instruction_type: "authorizeChecked".to_string(), info: json!({ - "stakeAccount": keys[3].to_string(), - "clockSysvar": keys[4].to_string(), - "authority": keys[0].to_string(), - "newAuthority": keys[1].to_string(), + "stakeAccount": stake_pubkey.to_string(), + "clockSysvar": sysvar::clock::ID.to_string(), + "authority": authorized_pubkey.to_string(), + "newAuthority": new_authorized_pubkey.to_string(), "authorityType": StakeAuthorize::Withdrawer, - "custodian": keys[2].to_string(), + "custodian": custodian_pubkey.to_string(), }), } ); assert!(parse_stake( &message.instructions[0], - &AccountKeys::new(&keys[0..4], None) + &AccountKeys::new(&message.account_keys[0..4], None) ) .is_err()); + } + + #[test] + fn test_parse_stake_authorize_checked_with_seed_ix() { + let stake_pubkey = Pubkey::new_unique(); + let authority_base_pubkey = Pubkey::new_unique(); + let authority_owner_pubkey = Pubkey::new_unique(); + let new_authorized_pubkey = Pubkey::new_unique(); + let custodian_pubkey = Pubkey::new_unique(); let seed = "test_seed"; let instruction = instruction::authorize_checked_with_seed( - &keys[2], - &keys[0], + &stake_pubkey, + &authority_base_pubkey, seed.to_string(), - &keys[0], - &keys[1], + &authority_owner_pubkey, + &new_authorized_pubkey, StakeAuthorize::Staker, None, ); @@ -922,61 +1009,61 @@ mod test { assert_eq!( parse_stake( &message.instructions[0], - &AccountKeys::new(&keys[0..4], None) + &AccountKeys::new(&message.account_keys, None) ) .unwrap(), ParsedInstructionEnum { instruction_type: "authorizeCheckedWithSeed".to_string(), info: json!({ - "stakeAccount": keys[2].to_string(), - "authorityOwner": keys[0].to_string(), - "newAuthorized": keys[1].to_string(), - "authorityBase": keys[0].to_string(), + "stakeAccount": stake_pubkey.to_string(), + "authorityOwner": authority_owner_pubkey.to_string(), + "newAuthorized": new_authorized_pubkey.to_string(), + "authorityBase": authority_base_pubkey.to_string(), "authoritySeed": seed, "authorityType": StakeAuthorize::Staker, - "clockSysvar": keys[3].to_string(), + "clockSysvar": sysvar::clock::ID.to_string(), }), } ); assert!(parse_stake( &message.instructions[0], - &AccountKeys::new(&keys[0..3], None) + &AccountKeys::new(&message.account_keys[0..3], None) ) .is_err()); let instruction = instruction::authorize_checked_with_seed( - &keys[3], - &keys[0], + &stake_pubkey, + &authority_base_pubkey, seed.to_string(), - &keys[0], - &keys[1], + &authority_owner_pubkey, + &new_authorized_pubkey, StakeAuthorize::Withdrawer, - Some(&keys[2]), + Some(&custodian_pubkey), ); let message = Message::new(&[instruction], None); assert_eq!( parse_stake( &message.instructions[0], - &AccountKeys::new(&keys[0..5], None) + &AccountKeys::new(&message.account_keys, None) ) .unwrap(), ParsedInstructionEnum { instruction_type: "authorizeCheckedWithSeed".to_string(), info: json!({ - "stakeAccount": keys[3].to_string(), - "authorityOwner": keys[0].to_string(), - "newAuthorized": keys[1].to_string(), - "authorityBase": keys[0].to_string(), + "stakeAccount": stake_pubkey.to_string(), + "authorityOwner": authority_owner_pubkey.to_string(), + "newAuthorized": new_authorized_pubkey.to_string(), + "authorityBase": authority_base_pubkey.to_string(), "authoritySeed": seed, "authorityType": StakeAuthorize::Withdrawer, - "clockSysvar": keys[4].to_string(), - "custodian": keys[2].to_string(), + "clockSysvar": sysvar::clock::ID.to_string(), + "custodian": custodian_pubkey.to_string(), }), } ); assert!(parse_stake( &message.instructions[0], - &AccountKeys::new(&keys[0..4], None) + &AccountKeys::new(&message.account_keys[0..4], None) ) .is_err()); } diff --git a/transaction-status/src/parse_system.rs b/transaction-status/src/parse_system.rs index c9da69003d..ab6953f8e5 100644 --- a/transaction-status/src/parse_system.rs +++ b/transaction-status/src/parse_system.rs @@ -202,348 +202,423 @@ fn check_num_system_accounts(accounts: &[u8], num: usize) -> Result<(), ParseIns mod test { use { super::*, - solana_sdk::{message::Message, pubkey::Pubkey, system_instruction}, + solana_sdk::{message::Message, pubkey::Pubkey, system_instruction, sysvar}, }; #[test] - #[allow(clippy::same_item_push)] - fn test_parse_system_instruction() { - let mut keys: Vec = vec![]; - for _ in 0..6 { - keys.push(solana_sdk::pubkey::new_rand()); - } - + fn test_parse_system_create_account_ix() { let lamports = 55; let space = 128; + let from_pubkey = Pubkey::new_unique(); + let to_pubkey = Pubkey::new_unique(); + let owner_pubkey = Pubkey::new_unique(); - let instruction = - system_instruction::create_account(&keys[0], &keys[1], lamports, space, &keys[2]); + let instruction = system_instruction::create_account( + &from_pubkey, + &to_pubkey, + lamports, + space, + &owner_pubkey, + ); let message = Message::new(&[instruction], None); assert_eq!( parse_system( &message.instructions[0], - &AccountKeys::new(&keys[0..2], None) + &AccountKeys::new(&message.account_keys, None) ) .unwrap(), ParsedInstructionEnum { instruction_type: "createAccount".to_string(), info: json!({ - "source": keys[0].to_string(), - "newAccount": keys[1].to_string(), + "source": from_pubkey.to_string(), + "newAccount": to_pubkey.to_string(), "lamports": lamports, - "owner": keys[2].to_string(), + "owner": owner_pubkey.to_string(), "space": space, }), } ); assert!(parse_system( &message.instructions[0], - &AccountKeys::new(&keys[0..1], None) + &AccountKeys::new(&message.account_keys[0..1], None) ) .is_err()); + } - let instruction = system_instruction::assign(&keys[0], &keys[1]); + #[test] + fn test_parse_system_assign_ix() { + let account_pubkey = Pubkey::new_unique(); + let owner_pubkey = Pubkey::new_unique(); + let instruction = system_instruction::assign(&account_pubkey, &owner_pubkey); let message = Message::new(&[instruction], None); assert_eq!( parse_system( &message.instructions[0], - &AccountKeys::new(&keys[0..1], None) + &AccountKeys::new(&message.account_keys, None) ) .unwrap(), ParsedInstructionEnum { instruction_type: "assign".to_string(), info: json!({ - "account": keys[0].to_string(), - "owner": keys[1].to_string(), + "account": account_pubkey.to_string(), + "owner": owner_pubkey.to_string(), }), } ); assert!(parse_system(&message.instructions[0], &AccountKeys::new(&[], None)).is_err()); + } - let instruction = system_instruction::transfer(&keys[0], &keys[1], lamports); + #[test] + fn test_parse_system_transfer_ix() { + let lamports = 55; + let from_pubkey = Pubkey::new_unique(); + let to_pubkey = Pubkey::new_unique(); + let instruction = system_instruction::transfer(&from_pubkey, &to_pubkey, lamports); let message = Message::new(&[instruction], None); assert_eq!( parse_system( &message.instructions[0], - &AccountKeys::new(&keys[0..2], None) + &AccountKeys::new(&message.account_keys, None) ) .unwrap(), ParsedInstructionEnum { instruction_type: "transfer".to_string(), info: json!({ - "source": keys[0].to_string(), - "destination": keys[1].to_string(), + "source": from_pubkey.to_string(), + "destination": to_pubkey.to_string(), "lamports": lamports, }), } ); assert!(parse_system( &message.instructions[0], - &AccountKeys::new(&keys[0..1], None) + &AccountKeys::new(&message.account_keys[0..1], None) ) .is_err()); + } + #[test] + fn test_parse_system_create_account_with_seed_ix() { + let lamports = 55; + let space = 128; let seed = "test_seed"; + let from_pubkey = Pubkey::new_unique(); + let to_pubkey = Pubkey::new_unique(); + let base_pubkey = Pubkey::new_unique(); + let owner_pubkey = Pubkey::new_unique(); let instruction = system_instruction::create_account_with_seed( - &keys[0], &keys[2], &keys[1], seed, lamports, space, &keys[3], + &from_pubkey, + &to_pubkey, + &base_pubkey, + seed, + lamports, + space, + &owner_pubkey, ); let message = Message::new(&[instruction], None); assert_eq!( parse_system( &message.instructions[0], - &AccountKeys::new(&keys[0..3], None) + &AccountKeys::new(&message.account_keys, None) ) .unwrap(), ParsedInstructionEnum { instruction_type: "createAccountWithSeed".to_string(), info: json!({ - "source": keys[0].to_string(), - "newAccount": keys[2].to_string(), + "source": from_pubkey.to_string(), + "newAccount": to_pubkey.to_string(), "lamports": lamports, - "base": keys[1].to_string(), + "base": base_pubkey.to_string(), "seed": seed, - "owner": keys[3].to_string(), + "owner": owner_pubkey.to_string(), "space": space, }), } ); - let seed = "test_seed"; - let instruction = system_instruction::create_account_with_seed( - &keys[0], &keys[1], &keys[0], seed, lamports, space, &keys[3], - ); - let message = Message::new(&[instruction], None); - assert_eq!( - parse_system( - &message.instructions[0], - &AccountKeys::new(&keys[0..2], None) - ) - .unwrap(), - ParsedInstructionEnum { - instruction_type: "createAccountWithSeed".to_string(), - info: json!({ - "source": keys[0].to_string(), - "newAccount": keys[1].to_string(), - "lamports": lamports, - "base": keys[0].to_string(), - "seed": seed, - "owner": keys[3].to_string(), - "space": space, - }), - } - ); assert!(parse_system( &message.instructions[0], - &AccountKeys::new(&keys[0..1], None) + &AccountKeys::new(&message.account_keys[0..1], None) ) .is_err()); + } - let instruction = system_instruction::allocate(&keys[0], space); + #[test] + fn test_parse_system_allocate_ix() { + let space = 128; + let account_pubkey = Pubkey::new_unique(); + let instruction = system_instruction::allocate(&account_pubkey, space); let message = Message::new(&[instruction], None); assert_eq!( parse_system( &message.instructions[0], - &AccountKeys::new(&keys[0..1], None) + &AccountKeys::new(&message.account_keys, None) ) .unwrap(), ParsedInstructionEnum { instruction_type: "allocate".to_string(), info: json!({ - "account": keys[0].to_string(), + "account": account_pubkey.to_string(), "space": space, }), } ); assert!(parse_system(&message.instructions[0], &AccountKeys::new(&[], None)).is_err()); + } - let instruction = - system_instruction::allocate_with_seed(&keys[1], &keys[0], seed, space, &keys[2]); + #[test] + fn test_parse_system_allocate_with_seed_ix() { + let space = 128; + let seed = "test_seed"; + let account_pubkey = Pubkey::new_unique(); + let base_pubkey = Pubkey::new_unique(); + let owner_pubkey = Pubkey::new_unique(); + let instruction = system_instruction::allocate_with_seed( + &account_pubkey, + &base_pubkey, + seed, + space, + &owner_pubkey, + ); let message = Message::new(&[instruction], None); assert_eq!( parse_system( &message.instructions[0], - &AccountKeys::new(&keys[0..2], None) + &AccountKeys::new(&message.account_keys, None) ) .unwrap(), ParsedInstructionEnum { instruction_type: "allocateWithSeed".to_string(), info: json!({ - "account": keys[1].to_string(), - "base": keys[0].to_string(), + "account": account_pubkey.to_string(), + "base": base_pubkey.to_string(), "seed": seed, - "owner": keys[2].to_string(), + "owner": owner_pubkey.to_string(), "space": space, }), } ); assert!(parse_system( &message.instructions[0], - &AccountKeys::new(&keys[0..1], None) + &AccountKeys::new(&message.account_keys[0..1], None) ) .is_err()); + } - let instruction = system_instruction::assign_with_seed(&keys[1], &keys[0], seed, &keys[2]); + #[test] + fn test_parse_system_assign_with_seed_ix() { + let seed = "test_seed"; + let account_pubkey = Pubkey::new_unique(); + let base_pubkey = Pubkey::new_unique(); + let owner_pubkey = Pubkey::new_unique(); + let instruction = system_instruction::assign_with_seed( + &account_pubkey, + &base_pubkey, + seed, + &owner_pubkey, + ); let message = Message::new(&[instruction], None); assert_eq!( parse_system( &message.instructions[0], - &AccountKeys::new(&keys[0..2], None) + &AccountKeys::new(&message.account_keys, None) ) .unwrap(), ParsedInstructionEnum { instruction_type: "assignWithSeed".to_string(), info: json!({ - "account": keys[1].to_string(), - "base": keys[0].to_string(), + "account": account_pubkey.to_string(), + "base": base_pubkey.to_string(), "seed": seed, - "owner": keys[2].to_string(), + "owner": owner_pubkey.to_string(), }), } ); assert!(parse_system( &message.instructions[0], - &AccountKeys::new(&keys[0..1], None) + &AccountKeys::new(&message.account_keys[0..1], None) ) .is_err()); + } + #[test] + fn test_parse_system_transfer_with_seed_ix() { + let lamports = 55; + let seed = "test_seed"; + let from_pubkey = Pubkey::new_unique(); + let from_base_pubkey = Pubkey::new_unique(); + let from_owner_pubkey = Pubkey::new_unique(); + let to_pubkey = Pubkey::new_unique(); let instruction = system_instruction::transfer_with_seed( - &keys[1], - &keys[0], + &from_pubkey, + &from_base_pubkey, seed.to_string(), - &keys[3], - &keys[2], + &from_owner_pubkey, + &to_pubkey, lamports, ); let message = Message::new(&[instruction], None); assert_eq!( parse_system( &message.instructions[0], - &AccountKeys::new(&keys[0..3], None) + &AccountKeys::new(&message.account_keys, None) ) .unwrap(), ParsedInstructionEnum { instruction_type: "transferWithSeed".to_string(), info: json!({ - "source": keys[1].to_string(), - "sourceBase": keys[0].to_string(), + "source": from_pubkey.to_string(), + "sourceBase": from_base_pubkey.to_string(), "sourceSeed": seed, - "sourceOwner": keys[3].to_string(), + "sourceOwner": from_owner_pubkey.to_string(), "lamports": lamports, - "destination": keys[2].to_string() + "destination": to_pubkey.to_string() }), } ); assert!(parse_system( &message.instructions[0], - &AccountKeys::new(&keys[0..2], None) + &AccountKeys::new(&message.account_keys[0..2], None) ) .is_err()); } #[test] - #[allow(clippy::same_item_push)] - fn test_parse_system_instruction_nonce() { - let mut keys: Vec = vec![]; - for _ in 0..5 { - keys.push(solana_sdk::pubkey::new_rand()); - } + fn test_parse_system_advance_nonce_account_ix() { + let nonce_pubkey = Pubkey::new_unique(); + let authorized_pubkey = Pubkey::new_unique(); - let instruction = system_instruction::advance_nonce_account(&keys[1], &keys[0]); + let instruction = + system_instruction::advance_nonce_account(&nonce_pubkey, &authorized_pubkey); let message = Message::new(&[instruction], None); assert_eq!( parse_system( &message.instructions[0], - &AccountKeys::new(&keys[0..3], None) + &AccountKeys::new(&message.account_keys, None) ) .unwrap(), ParsedInstructionEnum { instruction_type: "advanceNonce".to_string(), info: json!({ - "nonceAccount": keys[1].to_string(), - "recentBlockhashesSysvar": keys[2].to_string(), - "nonceAuthority": keys[0].to_string(), + "nonceAccount": nonce_pubkey.to_string(), + "recentBlockhashesSysvar": sysvar::recent_blockhashes::ID.to_string(), + "nonceAuthority": authorized_pubkey.to_string(), }), } ); assert!(parse_system( &message.instructions[0], - &AccountKeys::new(&keys[0..2], None) + &AccountKeys::new(&message.account_keys[0..2], None) ) .is_err()); + } + + #[test] + fn test_parse_system_withdraw_nonce_account_ix() { + let nonce_pubkey = Pubkey::new_unique(); + let authorized_pubkey = Pubkey::new_unique(); + let to_pubkey = Pubkey::new_unique(); let lamports = 55; - let instruction = - system_instruction::withdraw_nonce_account(&keys[1], &keys[0], &keys[2], lamports); + let instruction = system_instruction::withdraw_nonce_account( + &nonce_pubkey, + &authorized_pubkey, + &to_pubkey, + lamports, + ); let message = Message::new(&[instruction], None); assert_eq!( parse_system( &message.instructions[0], - &AccountKeys::new(&keys[0..5], None) + &AccountKeys::new(&message.account_keys, None) ) .unwrap(), ParsedInstructionEnum { instruction_type: "withdrawFromNonce".to_string(), info: json!({ - "nonceAccount": keys[1].to_string(), - "destination": keys[2].to_string(), - "recentBlockhashesSysvar": keys[3].to_string(), - "rentSysvar": keys[4].to_string(), - "nonceAuthority": keys[0].to_string(), + "nonceAccount": nonce_pubkey.to_string(), + "destination": to_pubkey.to_string(), + "recentBlockhashesSysvar": sysvar::recent_blockhashes::ID.to_string(), + "rentSysvar": sysvar::rent::ID.to_string(), + "nonceAuthority": authorized_pubkey.to_string(), "lamports": lamports }), } ); assert!(parse_system( &message.instructions[0], - &AccountKeys::new(&keys[0..4], None) + &AccountKeys::new(&message.account_keys[0..4], None) ) .is_err()); + } - let instructions = - system_instruction::create_nonce_account(&keys[0], &keys[1], &keys[4], lamports); + #[test] + fn test_parse_system_initialize_nonce_ix() { + let lamports = 55; + let from_pubkey = Pubkey::new_unique(); + let nonce_pubkey = Pubkey::new_unique(); + let authorized_pubkey = Pubkey::new_unique(); + + let instructions = system_instruction::create_nonce_account( + &from_pubkey, + &nonce_pubkey, + &authorized_pubkey, + lamports, + ); let message = Message::new(&instructions, None); assert_eq!( parse_system( &message.instructions[1], - &AccountKeys::new(&keys[0..4], None) + &AccountKeys::new(&message.account_keys, None) ) .unwrap(), ParsedInstructionEnum { instruction_type: "initializeNonce".to_string(), info: json!({ - "nonceAccount": keys[1].to_string(), - "recentBlockhashesSysvar": keys[2].to_string(), - "rentSysvar": keys[3].to_string(), - "nonceAuthority": keys[4].to_string(), + "nonceAccount": nonce_pubkey.to_string(), + "recentBlockhashesSysvar": sysvar::recent_blockhashes::ID.to_string(), + "rentSysvar": sysvar::rent::ID.to_string(), + "nonceAuthority": authorized_pubkey.to_string(), }), } ); assert!(parse_system( &message.instructions[1], - &AccountKeys::new(&keys[0..3], None) + &AccountKeys::new(&message.account_keys[0..3], None) ) .is_err()); + } - let instruction = system_instruction::authorize_nonce_account(&keys[1], &keys[0], &keys[2]); + #[test] + fn test_parse_system_authorize_nonce_account_ix() { + let nonce_pubkey = Pubkey::new_unique(); + let authorized_pubkey = Pubkey::new_unique(); + let new_authority_pubkey = Pubkey::new_unique(); + + let instruction = system_instruction::authorize_nonce_account( + &nonce_pubkey, + &authorized_pubkey, + &new_authority_pubkey, + ); let message = Message::new(&[instruction], None); assert_eq!( parse_system( &message.instructions[0], - &AccountKeys::new(&keys[0..2], None) + &AccountKeys::new(&message.account_keys, None) ) .unwrap(), ParsedInstructionEnum { instruction_type: "authorizeNonce".to_string(), info: json!({ - "nonceAccount": keys[1].to_string(), - "newAuthorized": keys[2].to_string(), - "nonceAuthority": keys[0].to_string(), + "nonceAccount": nonce_pubkey.to_string(), + "newAuthorized": new_authority_pubkey.to_string(), + "nonceAuthority": authorized_pubkey.to_string(), }), } ); assert!(parse_system( &message.instructions[0], - &AccountKeys::new(&keys[0..1], None) + &AccountKeys::new(&message.account_keys[0..1], None) ) .is_err()); } diff --git a/transaction-status/src/parse_vote.rs b/transaction-status/src/parse_vote.rs index f50336a597..de86490cd0 100644 --- a/transaction-status/src/parse_vote.rs +++ b/transaction-status/src/parse_vote.rs @@ -186,7 +186,7 @@ fn check_num_vote_accounts(accounts: &[u8], num: usize) -> Result<(), ParseInstr mod test { use { super::*, - solana_sdk::{hash::Hash, message::Message, pubkey::Pubkey}, + solana_sdk::{hash::Hash, message::Message, pubkey::Pubkey, sysvar}, solana_vote_program::{ vote_instruction, vote_state::{Vote, VoteAuthorize, VoteInit}, @@ -194,34 +194,24 @@ mod test { }; #[test] - #[allow(clippy::same_item_push)] - fn test_parse_vote_instruction() { - let mut keys: Vec = vec![]; - for _ in 0..5 { - keys.push(solana_sdk::pubkey::new_rand()); - } - + fn test_parse_vote_initialize_ix() { let lamports = 55; - let hash = Hash::new_from_array([1; 32]); - let vote = Vote { - slots: vec![1, 2, 4], - hash, - timestamp: Some(1_234_567_890), - }; let commission = 10; - let authorized_voter = solana_sdk::pubkey::new_rand(); - let authorized_withdrawer = solana_sdk::pubkey::new_rand(); + let node_pubkey = Pubkey::new_unique(); + let vote_pubkey = Pubkey::new_unique(); + let authorized_voter = Pubkey::new_unique(); + let authorized_withdrawer = Pubkey::new_unique(); let vote_init = VoteInit { - node_pubkey: keys[2], + node_pubkey, authorized_voter, authorized_withdrawer, commission, }; let instructions = vote_instruction::create_account( - &solana_sdk::pubkey::new_rand(), - &keys[1], + &Pubkey::new_unique(), + &vote_pubkey, &vote_init, lamports, ); @@ -229,16 +219,16 @@ mod test { assert_eq!( parse_vote( &message.instructions[1], - &AccountKeys::new(&keys[0..5], None) + &AccountKeys::new(&message.account_keys, None) ) .unwrap(), ParsedInstructionEnum { instruction_type: "initialize".to_string(), info: json!({ - "voteAccount": keys[1].to_string(), - "rentSysvar": keys[3].to_string(), - "clockSysvar": keys[4].to_string(), - "node": keys[2].to_string(), + "voteAccount": vote_pubkey.to_string(), + "rentSysvar": sysvar::rent::ID.to_string(), + "clockSysvar": sysvar::clock::ID.to_string(), + "node": node_pubkey.to_string(), "authorizedVoter": authorized_voter.to_string(), "authorizedWithdrawer": authorized_withdrawer.to_string(), "commission": commission, @@ -247,51 +237,74 @@ mod test { ); assert!(parse_vote( &message.instructions[1], - &AccountKeys::new(&keys[0..3], None) + &AccountKeys::new(&message.account_keys[0..3], None) ) .is_err()); + } + #[test] + fn test_parse_vote_authorize_ix() { + let vote_pubkey = Pubkey::new_unique(); + let authorized_pubkey = Pubkey::new_unique(); + let new_authorized_pubkey = Pubkey::new_unique(); let authority_type = VoteAuthorize::Voter; - let instruction = vote_instruction::authorize(&keys[1], &keys[0], &keys[3], authority_type); + let instruction = vote_instruction::authorize( + &vote_pubkey, + &authorized_pubkey, + &new_authorized_pubkey, + authority_type, + ); let message = Message::new(&[instruction], None); assert_eq!( parse_vote( &message.instructions[0], - &AccountKeys::new(&keys[0..3], None) + &AccountKeys::new(&message.account_keys, None) ) .unwrap(), ParsedInstructionEnum { instruction_type: "authorize".to_string(), info: json!({ - "voteAccount": keys[1].to_string(), - "clockSysvar": keys[2].to_string(), - "authority": keys[0].to_string(), - "newAuthority": keys[3].to_string(), + "voteAccount": vote_pubkey.to_string(), + "clockSysvar": sysvar::clock::ID.to_string(), + "authority": authorized_pubkey.to_string(), + "newAuthority": new_authorized_pubkey.to_string(), "authorityType": authority_type, }), } ); assert!(parse_vote( &message.instructions[0], - &AccountKeys::new(&keys[0..2], None) + &AccountKeys::new(&message.account_keys[0..2], None) ) .is_err()); + } - let instruction = vote_instruction::vote(&keys[1], &keys[0], vote.clone()); + #[test] + fn test_parse_vote_ix() { + let hash = Hash::new_from_array([1; 32]); + let vote = Vote { + slots: vec![1, 2, 4], + hash, + timestamp: Some(1_234_567_890), + }; + + let vote_pubkey = Pubkey::new_unique(); + let authorized_voter_pubkey = Pubkey::new_unique(); + let instruction = vote_instruction::vote(&vote_pubkey, &authorized_voter_pubkey, vote); let message = Message::new(&[instruction], None); assert_eq!( parse_vote( &message.instructions[0], - &AccountKeys::new(&keys[0..4], None) + &AccountKeys::new(&message.account_keys, None) ) .unwrap(), ParsedInstructionEnum { instruction_type: "vote".to_string(), info: json!({ - "voteAccount": keys[1].to_string(), - "slotHashesSysvar": keys[2].to_string(), - "clockSysvar": keys[3].to_string(), - "voteAuthority": keys[0].to_string(), + "voteAccount": vote_pubkey.to_string(), + "slotHashesSysvar": sysvar::slot_hashes::ID.to_string(), + "clockSysvar": sysvar::clock::ID.to_string(), + "voteAuthority": authorized_voter_pubkey.to_string(), "vote": { "slots": [1, 2, 4], "hash": hash.to_string(), @@ -302,96 +315,141 @@ mod test { ); assert!(parse_vote( &message.instructions[0], - &AccountKeys::new(&keys[0..3], None) + &AccountKeys::new(&message.account_keys[0..3], None) ) .is_err()); + } - let instruction = vote_instruction::withdraw(&keys[1], &keys[0], lamports, &keys[2]); + #[test] + fn test_parse_vote_withdraw_ix() { + let lamports = 55; + let vote_pubkey = Pubkey::new_unique(); + let authorized_withdrawer_pubkey = Pubkey::new_unique(); + let to_pubkey = Pubkey::new_unique(); + let instruction = vote_instruction::withdraw( + &vote_pubkey, + &authorized_withdrawer_pubkey, + lamports, + &to_pubkey, + ); let message = Message::new(&[instruction], None); assert_eq!( parse_vote( &message.instructions[0], - &AccountKeys::new(&keys[0..3], None) + &AccountKeys::new(&message.account_keys, None) ) .unwrap(), ParsedInstructionEnum { instruction_type: "withdraw".to_string(), info: json!({ - "voteAccount": keys[1].to_string(), - "destination": keys[2].to_string(), - "withdrawAuthority": keys[0].to_string(), + "voteAccount": vote_pubkey.to_string(), + "destination": to_pubkey.to_string(), + "withdrawAuthority": authorized_withdrawer_pubkey.to_string(), "lamports": lamports, }), } ); assert!(parse_vote( &message.instructions[0], - &AccountKeys::new(&keys[0..2], None) + &AccountKeys::new(&message.account_keys[0..2], None) ) .is_err()); + } - let instruction = vote_instruction::update_validator_identity(&keys[2], &keys[1], &keys[0]); + #[test] + fn test_parse_vote_update_validator_identity_ix() { + let vote_pubkey = Pubkey::new_unique(); + let authorized_withdrawer_pubkey = Pubkey::new_unique(); + let node_pubkey = Pubkey::new_unique(); + let instruction = vote_instruction::update_validator_identity( + &vote_pubkey, + &authorized_withdrawer_pubkey, + &node_pubkey, + ); let message = Message::new(&[instruction], None); assert_eq!( parse_vote( &message.instructions[0], - &AccountKeys::new(&keys[0..3], None) + &AccountKeys::new(&message.account_keys, None) ) .unwrap(), ParsedInstructionEnum { instruction_type: "updateValidatorIdentity".to_string(), info: json!({ - "voteAccount": keys[2].to_string(), - "newValidatorIdentity": keys[0].to_string(), - "withdrawAuthority": keys[1].to_string(), + "voteAccount": vote_pubkey.to_string(), + "newValidatorIdentity": node_pubkey.to_string(), + "withdrawAuthority": authorized_withdrawer_pubkey.to_string(), }), } ); assert!(parse_vote( &message.instructions[0], - &AccountKeys::new(&keys[0..2], None) + &AccountKeys::new(&message.account_keys[0..2], None) ) .is_err()); + } - let instruction = vote_instruction::update_commission(&keys[1], &keys[0], commission); + #[test] + fn test_parse_vote_update_commission_ix() { + let commission = 10; + let vote_pubkey = Pubkey::new_unique(); + let authorized_withdrawer_pubkey = Pubkey::new_unique(); + let instruction = vote_instruction::update_commission( + &vote_pubkey, + &authorized_withdrawer_pubkey, + commission, + ); let message = Message::new(&[instruction], None); assert_eq!( parse_vote( &message.instructions[0], - &AccountKeys::new(&keys[0..2], None) + &AccountKeys::new(&message.account_keys, None) ) .unwrap(), ParsedInstructionEnum { instruction_type: "updateCommission".to_string(), info: json!({ - "voteAccount": keys[1].to_string(), - "withdrawAuthority": keys[0].to_string(), + "voteAccount": vote_pubkey.to_string(), + "withdrawAuthority": authorized_withdrawer_pubkey.to_string(), "commission": commission, }), } ); assert!(parse_vote( &message.instructions[0], - &AccountKeys::new(&keys[0..1], None) + &AccountKeys::new(&message.account_keys[0..1], None) ) .is_err()); + } + #[test] + fn test_parse_vote_switch_ix() { + let hash = Hash::new_from_array([1; 32]); + let vote = Vote { + slots: vec![1, 2, 4], + hash, + timestamp: Some(1_234_567_890), + }; + + let vote_pubkey = Pubkey::new_unique(); + let authorized_voter_pubkey = Pubkey::new_unique(); let proof_hash = Hash::new_from_array([2; 32]); - let instruction = vote_instruction::vote_switch(&keys[1], &keys[0], vote, proof_hash); + let instruction = + vote_instruction::vote_switch(&vote_pubkey, &authorized_voter_pubkey, vote, proof_hash); let message = Message::new(&[instruction], None); assert_eq!( parse_vote( &message.instructions[0], - &AccountKeys::new(&keys[0..4], None) + &AccountKeys::new(&message.account_keys, None) ) .unwrap(), ParsedInstructionEnum { instruction_type: "voteSwitch".to_string(), info: json!({ - "voteAccount": keys[1].to_string(), - "slotHashesSysvar": keys[2].to_string(), - "clockSysvar": keys[3].to_string(), - "voteAuthority": keys[0].to_string(), + "voteAccount": vote_pubkey.to_string(), + "slotHashesSysvar": sysvar::slot_hashes::ID.to_string(), + "clockSysvar": sysvar::clock::ID.to_string(), + "voteAuthority": authorized_voter_pubkey.to_string(), "vote": { "slots": [1, 2, 4], "hash": hash.to_string(), @@ -403,34 +461,44 @@ mod test { ); assert!(parse_vote( &message.instructions[0], - &AccountKeys::new(&keys[0..3], None) + &AccountKeys::new(&message.account_keys[0..3], None) ) .is_err()); + } + #[test] + fn test_parse_vote_authorized_checked_ix() { + let vote_pubkey = Pubkey::new_unique(); + let authorized_pubkey = Pubkey::new_unique(); + let new_authorized_pubkey = Pubkey::new_unique(); let authority_type = VoteAuthorize::Voter; - let instruction = - vote_instruction::authorize_checked(&keys[1], &keys[0], &keys[3], authority_type); + let instruction = vote_instruction::authorize_checked( + &vote_pubkey, + &authorized_pubkey, + &new_authorized_pubkey, + authority_type, + ); let message = Message::new(&[instruction], None); assert_eq!( parse_vote( &message.instructions[0], - &AccountKeys::new(&keys[0..4], None) + &AccountKeys::new(&message.account_keys, None) ) .unwrap(), ParsedInstructionEnum { instruction_type: "authorizeChecked".to_string(), info: json!({ - "voteAccount": keys[2].to_string(), - "clockSysvar": keys[3].to_string(), - "authority": keys[0].to_string(), - "newAuthority": keys[1].to_string(), + "voteAccount": vote_pubkey.to_string(), + "clockSysvar": sysvar::clock::ID.to_string(), + "authority": authorized_pubkey.to_string(), + "newAuthority": new_authorized_pubkey.to_string(), "authorityType": authority_type, }), } ); assert!(parse_vote( &message.instructions[0], - &AccountKeys::new(&keys[0..3], None) + &AccountKeys::new(&message.account_keys[0..3], None) ) .is_err()); }