Refactor instruction compilation and update message account key ordering (#23729)

* Refactor: Make instruction compilation usable for other message versions

* apply trents feedback

* Fix tests

* Fix bpf compatiblity
This commit is contained in:
Justin Starry 2022-03-21 20:53:32 +08:00 committed by GitHub
parent a1a29b0b86
commit 15357480ec
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 1212 additions and 972 deletions

View File

@ -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,

View File

@ -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,

View File

@ -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<Item = &'a Pubkey> {
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<CompiledInstruction> {
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<u8> = 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)]

View File

@ -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<Pubkey>,
readonly_signer_keys: Vec<Pubkey>,
writable_non_signer_keys: Vec<Pubkey>,
readonly_non_signer_keys: Vec<Pubkey>,
}
#[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<Pubkey>) -> 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<Pubkey> = 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<Pubkey>)> {
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()
}
);
}
}

View File

@ -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<CompiledIns
ixs.iter().map(|ix| compile_instruction(ix, keys)).collect()
}
/// A helper struct to collect pubkeys referenced by a set of instructions and read-only counts
#[derive(Debug, PartialEq, Eq)]
struct InstructionKeys {
pub signed_keys: Vec<Pubkey>,
pub unsigned_keys: Vec<Pubkey>,
pub num_readonly_signed_accounts: u8,
pub num_readonly_unsigned_accounts: u8,
}
impl InstructionKeys {
fn new(
signed_keys: Vec<Pubkey>,
unsigned_keys: Vec<Pubkey>,
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<AccountMeta> = 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<Pubkey> {
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<Pubkey> = 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()
)
}
}

View File

@ -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::*;

View File

@ -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,

View File

@ -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<Pubkey> = 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,
&current_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,
&current_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,
&current_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());
}

View File

@ -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<Pubkey> = 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<Pubkey> = 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());
}

View File

@ -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<Pubkey> = 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<Pubkey> = 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());
}

View File

@ -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<Pubkey> = 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());
}