Refactor: Add AccountKeys struct for static and dynamic message keys (#22960)

This commit is contained in:
Justin Starry 2022-02-05 20:00:31 +08:00 committed by GitHub
parent e05cf4bf97
commit ba215e94f6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
26 changed files with 917 additions and 442 deletions

View File

@ -6262,7 +6262,7 @@ pub mod tests {
.map(|transaction| { .map(|transaction| {
let mut pre_balances: Vec<u64> = vec![]; let mut pre_balances: Vec<u64> = vec![];
let mut post_balances: Vec<u64> = vec![]; let mut post_balances: Vec<u64> = vec![];
for i in 0..transaction.message.total_account_keys_len() { for i in 0..transaction.message.static_account_keys().len() {
pre_balances.push(i as u64 * 10); pre_balances.push(i as u64 * 10);
post_balances.push(i as u64 * 11); post_balances.push(i as u64 * 11);
} }
@ -7129,7 +7129,7 @@ pub mod tests {
.map(|transaction| { .map(|transaction| {
let mut pre_balances: Vec<u64> = vec![]; let mut pre_balances: Vec<u64> = vec![];
let mut post_balances: Vec<u64> = vec![]; let mut post_balances: Vec<u64> = vec![];
for i in 0..transaction.message.total_account_keys_len() { for i in 0..transaction.message.static_account_keys().len() {
pre_balances.push(i as u64 * 10); pre_balances.push(i as u64 * 10);
post_balances.push(i as u64 * 11); post_balances.push(i as u64 * 11);
} }
@ -7231,7 +7231,7 @@ pub mod tests {
.map(|transaction| { .map(|transaction| {
let mut pre_balances: Vec<u64> = vec![]; let mut pre_balances: Vec<u64> = vec![];
let mut post_balances: Vec<u64> = vec![]; let mut post_balances: Vec<u64> = vec![];
for i in 0..transaction.message.total_account_keys_len() { for i in 0..transaction.message.static_account_keys().len() {
pre_balances.push(i as u64 * 10); pre_balances.push(i as u64 * 10);
post_balances.push(i as u64 * 11); post_balances.push(i as u64 * 11);
} }
@ -7590,7 +7590,7 @@ pub mod tests {
.write_transaction_status( .write_transaction_status(
slot, slot,
transaction.signatures[0], transaction.signatures[0],
transaction.message.static_account_keys_iter().collect(), transaction.message.static_account_keys().iter().collect(),
vec![], vec![],
TransactionStatusMeta::default(), TransactionStatusMeta::default(),
) )
@ -7614,7 +7614,7 @@ pub mod tests {
.write_transaction_status( .write_transaction_status(
slot, slot,
transaction.signatures[0], transaction.signatures[0],
transaction.message.static_account_keys_iter().collect(), transaction.message.static_account_keys().iter().collect(),
vec![], vec![],
TransactionStatusMeta::default(), TransactionStatusMeta::default(),
) )

View File

@ -1,4 +1,4 @@
use {super::*, std::time::Instant}; use {super::*, solana_sdk::message::AccountKeys, std::time::Instant};
#[derive(Default)] #[derive(Default)]
pub struct PurgeStats { pub struct PurgeStats {
@ -334,16 +334,17 @@ impl Blockstore {
if let Some(&signature) = transaction.signatures.get(0) { if let Some(&signature) = transaction.signatures.get(0) {
batch.delete::<cf::TransactionStatus>((0, signature, slot))?; batch.delete::<cf::TransactionStatus>((0, signature, slot))?;
batch.delete::<cf::TransactionStatus>((1, signature, slot))?; batch.delete::<cf::TransactionStatus>((1, signature, slot))?;
for pubkey in transaction.message.into_static_account_keys() {
batch.delete::<cf::AddressSignatures>((0, pubkey, slot, signature))?;
batch.delete::<cf::AddressSignatures>((1, pubkey, slot, signature))?;
}
let meta = self.read_transaction_status((signature, slot))?; let meta = self.read_transaction_status((signature, slot))?;
let loaded_addresses = let loaded_addresses = meta.map(|meta| meta.loaded_addresses);
meta.map(|meta| meta.loaded_addresses).unwrap_or_default(); let account_keys = AccountKeys::new(
for address in loaded_addresses.into_ordered_iter() { transaction.message.static_account_keys(),
batch.delete::<cf::AddressSignatures>((0, address, slot, signature))?; loaded_addresses.as_ref(),
batch.delete::<cf::AddressSignatures>((1, address, slot, signature))?; );
for pubkey in account_keys.iter() {
batch.delete::<cf::AddressSignatures>((0, *pubkey, slot, signature))?;
batch.delete::<cf::AddressSignatures>((1, *pubkey, slot, signature))?;
} }
} }
} }

View File

@ -3591,7 +3591,7 @@ pub mod rpc_full {
if config.sig_verify { if config.sig_verify {
verify_transaction(&transaction, &bank.feature_set)?; verify_transaction(&transaction, &bank.feature_set)?;
} }
let number_of_accounts = transaction.message().account_keys_len(); let number_of_accounts = transaction.message().account_keys().len();
let TransactionSimulationResult { let TransactionSimulationResult {
result, result,

View File

@ -250,10 +250,11 @@ impl Accounts {
// If a fee can pay for execution then the program will be scheduled // If a fee can pay for execution then the program will be scheduled
let mut payer_index = None; let mut payer_index = None;
let mut tx_rent: TransactionRent = 0; let mut tx_rent: TransactionRent = 0;
let mut accounts = Vec::with_capacity(message.account_keys_len()); let account_keys = message.account_keys();
let mut account_deps = Vec::with_capacity(message.account_keys_len()); let mut accounts = Vec::with_capacity(account_keys.len());
let mut account_deps = Vec::with_capacity(account_keys.len());
let mut rent_debits = RentDebits::default(); let mut rent_debits = RentDebits::default();
for (i, key) in message.account_keys_iter().enumerate() { for (i, key) in account_keys.iter().enumerate() {
let account = if !message.is_non_loader_key(i) { let account = if !message.is_non_loader_key(i) {
// Fill in an empty account for the program slots. // Fill in an empty account for the program slots.
AccountSharedData::default() AccountSharedData::default()
@ -328,7 +329,7 @@ impl Accounts {
}; };
accounts.push((*key, account)); accounts.push((*key, account));
} }
debug_assert_eq!(accounts.len(), message.account_keys_len()); debug_assert_eq!(accounts.len(), account_keys.len());
// Appends the account_deps at the end of the accounts, // Appends the account_deps at the end of the accounts,
// this way they can be accessed in a uniform way. // this way they can be accessed in a uniform way.
// At places where only the accounts are needed, // At places where only the accounts are needed,
@ -1176,7 +1177,7 @@ impl Accounts {
let message = tx.message(); let message = tx.message();
let loaded_transaction = tx_load_result.as_mut().unwrap(); let loaded_transaction = tx_load_result.as_mut().unwrap();
let mut fee_payer_index = None; let mut fee_payer_index = None;
for (i, (address, account)) in (0..message.account_keys_len()) for (i, (address, account)) in (0..message.account_keys().len())
.zip(loaded_transaction.accounts.iter_mut()) .zip(loaded_transaction.accounts.iter_mut())
.filter(|(i, _)| message.is_non_loader_key(*i)) .filter(|(i, _)| message.is_non_loader_key(*i))
{ {

View File

@ -838,7 +838,7 @@ impl NonceFull {
accounts: &[TransactionAccount], accounts: &[TransactionAccount],
rent_debits: &RentDebits, rent_debits: &RentDebits,
) -> Result<Self> { ) -> Result<Self> {
let fee_payer = (0..message.account_keys_len()).find_map(|i| { let fee_payer = (0..message.account_keys().len()).find_map(|i| {
if let Some((k, a)) = &accounts.get(i) { if let Some((k, a)) = &accounts.get(i) {
if message.is_non_loader_key(i) { if message.is_non_loader_key(i) {
return Some((k, a)); return Some((k, a));
@ -3464,7 +3464,7 @@ impl Bank {
&self, &self,
transaction: SanitizedTransaction, transaction: SanitizedTransaction,
) -> TransactionSimulationResult { ) -> TransactionSimulationResult {
let number_of_accounts = transaction.message().account_keys_len(); let number_of_accounts = transaction.message().account_keys().len();
let batch = self.prepare_simulation_batch(transaction); let batch = self.prepare_simulation_batch(transaction);
let mut timings = ExecuteTimings::default(); let mut timings = ExecuteTimings::default();
@ -3648,7 +3648,7 @@ impl Bank {
let mut balances: TransactionBalances = vec![]; let mut balances: TransactionBalances = vec![];
for transaction in batch.sanitized_transactions() { for transaction in batch.sanitized_transactions() {
let mut transaction_balances: Vec<u64> = vec![]; let mut transaction_balances: Vec<u64> = vec![];
for account_key in transaction.message().account_keys_iter() { for account_key in transaction.message().account_keys().iter() {
transaction_balances.push(self.get_balance(account_key)); transaction_balances.push(self.get_balance(account_key));
} }
balances.push(transaction_balances); balances.push(transaction_balances);
@ -4045,7 +4045,7 @@ impl Bank {
for (execution_result, tx) in execution_results.iter().zip(sanitized_txs) { for (execution_result, tx) in execution_results.iter().zip(sanitized_txs) {
if let Some(debug_keys) = &self.transaction_debug_keys { if let Some(debug_keys) = &self.transaction_debug_keys {
for key in tx.message().account_keys_iter() { for key in tx.message().account_keys().iter() {
if debug_keys.contains(key) { if debug_keys.contains(key) {
let result = execution_result.flattened_result(); let result = execution_result.flattened_result();
info!("slot: {} result: {:?} tx: {:?}", self.slot, result, tx); info!("slot: {} result: {:?} tx: {:?}", self.slot, result, tx);
@ -4062,7 +4062,7 @@ impl Bank {
.mentioned_addresses .mentioned_addresses
.is_empty() .is_empty()
{ {
for key in tx.message().account_keys_iter() { for key in tx.message().account_keys().iter() {
if transaction_log_collector_config if transaction_log_collector_config
.mentioned_addresses .mentioned_addresses
.contains(key) .contains(key)
@ -5971,7 +5971,7 @@ impl Bank {
) { ) {
let message = tx.message(); let message = tx.message();
for (_i, (pubkey, account)) in for (_i, (pubkey, account)) in
(0..message.account_keys_len()).zip(loaded_transaction.accounts.iter()) (0..message.account_keys().len()).zip(loaded_transaction.accounts.iter())
{ {
self.stakes_cache.check_and_store(pubkey, account); self.stakes_cache.check_and_store(pubkey, account);
} }
@ -6764,16 +6764,16 @@ pub(crate) mod tests {
let message = new_sanitized_message(&instructions, Some(&from_address)); let message = new_sanitized_message(&instructions, Some(&from_address));
let accounts = [ let accounts = [
( (
*message.get_account_key(0).unwrap(), *message.account_keys().get(0).unwrap(),
rent_collected_from_account.clone(), rent_collected_from_account.clone(),
), ),
( (
*message.get_account_key(1).unwrap(), *message.account_keys().get(1).unwrap(),
rent_collected_nonce_account.clone(), rent_collected_nonce_account.clone(),
), ),
(*message.get_account_key(2).unwrap(), to_account.clone()), (*message.account_keys().get(2).unwrap(), to_account.clone()),
( (
*message.get_account_key(3).unwrap(), *message.account_keys().get(3).unwrap(),
recent_blockhashes_sysvar_account.clone(), recent_blockhashes_sysvar_account.clone(),
), ),
]; ];
@ -6795,16 +6795,16 @@ pub(crate) mod tests {
let message = new_sanitized_message(&instructions, Some(&nonce_address)); let message = new_sanitized_message(&instructions, Some(&nonce_address));
let accounts = [ let accounts = [
( (
*message.get_account_key(0).unwrap(), *message.account_keys().get(0).unwrap(),
rent_collected_nonce_account, rent_collected_nonce_account,
), ),
( (
*message.get_account_key(1).unwrap(), *message.account_keys().get(1).unwrap(),
rent_collected_from_account, rent_collected_from_account,
), ),
(*message.get_account_key(2).unwrap(), to_account), (*message.account_keys().get(2).unwrap(), to_account),
( (
*message.get_account_key(3).unwrap(), *message.account_keys().get(3).unwrap(),
recent_blockhashes_sysvar_account, recent_blockhashes_sysvar_account,
), ),
]; ];

View File

@ -19,7 +19,7 @@ impl Bank {
transaction_context: &TransactionContext, transaction_context: &TransactionContext,
message: &SanitizedMessage, message: &SanitizedMessage,
) -> Vec<TransactionAccountStateInfo> { ) -> Vec<TransactionAccountStateInfo> {
(0..message.account_keys_len()) (0..message.account_keys().len())
.map(|i| { .map(|i| {
let rent_state = if message.is_writable(i) { let rent_state = if message.is_writable(i) {
let state = if let Ok(account) = transaction_context.get_account_at_index(i) { let state = if let Ok(account) = transaction_context.get_account_at_index(i) {

View File

@ -157,14 +157,18 @@ impl CostModel {
transaction: &SanitizedTransaction, transaction: &SanitizedTransaction,
) { ) {
let message = transaction.message(); let message = transaction.message();
message.account_keys_iter().enumerate().for_each(|(i, k)| { message
let is_writable = message.is_writable(i); .account_keys()
.iter()
.enumerate()
.for_each(|(i, k)| {
let is_writable = message.is_writable(i);
if is_writable { if is_writable {
tx_cost.writable_accounts.push(*k); tx_cost.writable_accounts.push(*k);
tx_cost.write_lock_cost += WRITE_LOCK_UNITS; tx_cost.write_lock_cost += WRITE_LOCK_UNITS;
} }
}); });
} }
fn get_data_bytes_cost(&self, transaction: &SanitizedTransaction) -> u64 { fn get_data_bytes_cost(&self, transaction: &SanitizedTransaction) -> u64 {

View File

@ -44,7 +44,7 @@ pub fn parse_sanitized_vote_transaction(tx: &SanitizedTransaction) -> Option<Par
return None; return None;
} }
let first_account = usize::from(*first_instruction.accounts.first()?); let first_account = usize::from(*first_instruction.accounts.first()?);
let key = message.get_account_key(first_account)?; let key = message.account_keys().get(first_account)?;
let (vote, switch_proof_hash) = parse_vote_instruction_data(&first_instruction.data)?; let (vote, switch_proof_hash) = parse_vote_instruction_data(&first_instruction.data)?;
Some((*key, vote, switch_proof_hash)) Some((*key, vote, switch_proof_hash))
} }

View File

@ -0,0 +1,199 @@
use {
crate::{message::v0::LoadedAddresses, pubkey::Pubkey},
std::ops::Index,
};
/// Collection of static and dynamically loaded keys used to load accounts
/// during transaction processing.
pub struct AccountKeys<'a> {
static_keys: &'a [Pubkey],
dynamic_keys: Option<&'a LoadedAddresses>,
}
impl Index<usize> for AccountKeys<'_> {
type Output = Pubkey;
fn index(&self, index: usize) -> &Self::Output {
self.get(index).expect("index is invalid")
}
}
impl<'a> AccountKeys<'a> {
pub fn new(static_keys: &'a [Pubkey], dynamic_keys: Option<&'a LoadedAddresses>) -> Self {
Self {
static_keys,
dynamic_keys,
}
}
/// Returns an iterator of account key segments. The ordering of segments
/// affects how account indexes from compiled instructions are resolved and
/// so should not be changed.
fn key_segment_iter(&self) -> impl Iterator<Item = &'a [Pubkey]> {
if let Some(dynamic_keys) = self.dynamic_keys {
[
self.static_keys,
&dynamic_keys.writable,
&dynamic_keys.readonly,
]
.into_iter()
} else {
// empty segments added for branch type compatibility
[self.static_keys, &[], &[]].into_iter()
}
}
/// Returns the address of the account at the specified index of the list of
/// message account keys constructed from static keys, followed by dynamically
/// loaded writable addresses, and lastly the list of dynamically loaded
/// readonly addresses.
pub fn get(&self, mut index: usize) -> Option<&'a Pubkey> {
for key_segment in self.key_segment_iter() {
if index < key_segment.len() {
return Some(&key_segment[index]);
}
index = index.saturating_sub(key_segment.len());
}
None
}
/// Returns the total length of loaded accounts for a message
pub fn len(&self) -> usize {
let mut len = 0usize;
for key_segment in self.key_segment_iter() {
len = len.saturating_add(key_segment.len());
}
len
}
/// Returns true if this collection of account keys is empty
pub fn is_empty(&self) -> bool {
self.len() == 0
}
/// Iterator for the addresses of the loaded accounts for a message
pub fn iter(&self) -> impl Iterator<Item = &'a Pubkey> {
self.key_segment_iter().flatten()
}
}
#[cfg(test)]
mod tests {
use super::*;
fn test_account_keys() -> [Pubkey; 6] {
let key0 = Pubkey::new_unique();
let key1 = Pubkey::new_unique();
let key2 = Pubkey::new_unique();
let key3 = Pubkey::new_unique();
let key4 = Pubkey::new_unique();
let key5 = Pubkey::new_unique();
[key0, key1, key2, key3, key4, key5]
}
#[test]
fn test_key_segment_iter() {
let keys = test_account_keys();
let static_keys = vec![keys[0], keys[1], keys[2]];
let dynamic_keys = LoadedAddresses {
writable: vec![keys[3], keys[4]],
readonly: vec![keys[5]],
};
let account_keys = AccountKeys::new(&static_keys, Some(&dynamic_keys));
let expected_segments = vec![
vec![keys[0], keys[1], keys[2]],
vec![keys[3], keys[4]],
vec![keys[5]],
];
assert!(account_keys
.key_segment_iter()
.into_iter()
.eq(expected_segments.iter()));
}
#[test]
fn test_len() {
let keys = test_account_keys();
let static_keys = vec![keys[0], keys[1], keys[2], keys[3], keys[4], keys[5]];
let account_keys = AccountKeys::new(&static_keys, None);
assert_eq!(account_keys.len(), keys.len());
}
#[test]
fn test_len_with_dynamic_keys() {
let keys = test_account_keys();
let static_keys = vec![keys[0], keys[1], keys[2]];
let dynamic_keys = LoadedAddresses {
writable: vec![keys[3], keys[4]],
readonly: vec![keys[5]],
};
let account_keys = AccountKeys::new(&static_keys, Some(&dynamic_keys));
assert_eq!(account_keys.len(), keys.len());
}
#[test]
fn test_iter() {
let keys = test_account_keys();
let static_keys = vec![keys[0], keys[1], keys[2], keys[3], keys[4], keys[5]];
let account_keys = AccountKeys::new(&static_keys, None);
assert!(account_keys.iter().eq(keys.iter()));
}
#[test]
fn test_iter_with_dynamic_keys() {
let keys = test_account_keys();
let static_keys = vec![keys[0], keys[1], keys[2]];
let dynamic_keys = LoadedAddresses {
writable: vec![keys[3], keys[4]],
readonly: vec![keys[5]],
};
let account_keys = AccountKeys::new(&static_keys, Some(&dynamic_keys));
assert!(account_keys.iter().eq(keys.iter()));
}
#[test]
fn test_get() {
let keys = test_account_keys();
let static_keys = vec![keys[0], keys[1], keys[2], keys[3]];
let account_keys = AccountKeys::new(&static_keys, None);
assert_eq!(account_keys.get(0), Some(&keys[0]));
assert_eq!(account_keys.get(1), Some(&keys[1]));
assert_eq!(account_keys.get(2), Some(&keys[2]));
assert_eq!(account_keys.get(3), Some(&keys[3]));
assert_eq!(account_keys.get(4), None);
assert_eq!(account_keys.get(5), None);
}
#[test]
fn test_get_with_dynamic_keys() {
let keys = test_account_keys();
let static_keys = vec![keys[0], keys[1], keys[2]];
let dynamic_keys = LoadedAddresses {
writable: vec![keys[3], keys[4]],
readonly: vec![keys[5]],
};
let account_keys = AccountKeys::new(&static_keys, Some(&dynamic_keys));
assert_eq!(account_keys.get(0), Some(&keys[0]));
assert_eq!(account_keys.get(1), Some(&keys[1]));
assert_eq!(account_keys.get(2), Some(&keys[2]));
assert_eq!(account_keys.get(3), Some(&keys[3]));
assert_eq!(account_keys.get(4), Some(&keys[4]));
assert_eq!(account_keys.get(5), Some(&keys[5]));
}
}

View File

@ -5,10 +5,11 @@ pub mod legacy;
#[cfg(not(target_arch = "bpf"))] #[cfg(not(target_arch = "bpf"))]
#[path = ""] #[path = ""]
mod non_bpf_modules { mod non_bpf_modules {
mod account_keys;
mod sanitized; mod sanitized;
mod versions; mod versions;
pub use {sanitized::*, versions::*}; pub use {account_keys::*, sanitized::*, versions::*};
} }
pub use legacy::Message; pub use legacy::Message;

View File

@ -5,7 +5,7 @@ use {
message::{ message::{
legacy::Message as LegacyMessage, legacy::Message as LegacyMessage,
v0::{self, LoadedAddresses}, v0::{self, LoadedAddresses},
MessageHeader, AccountKeys, MessageHeader,
}, },
nonce::NONCED_TX_MARKER_IX_INDEX, nonce::NONCED_TX_MARKER_IX_INDEX,
program_utils::limited_deserialize, program_utils::limited_deserialize,
@ -85,7 +85,8 @@ impl SanitizedMessage {
/// Returns the fee payer for the transaction /// Returns the fee payer for the transaction
pub fn fee_payer(&self) -> &Pubkey { pub fn fee_payer(&self) -> &Pubkey {
self.get_account_key(0) self.account_keys()
.get(0)
.expect("sanitized message always has non-program fee payer at index 0") .expect("sanitized message always has non-program fee payer at index 0")
} }
@ -117,34 +118,19 @@ impl SanitizedMessage {
} }
.map(move |ix| { .map(move |ix| {
( (
self.get_account_key(usize::from(ix.program_id_index)) self.account_keys()
.get(usize::from(ix.program_id_index))
.expect("program id index is sanitized"), .expect("program id index is sanitized"),
ix, ix,
) )
}) })
} }
/// Iterator of all account keys referenced in this message, including dynamically loaded keys. /// Returns the list of account keys that are loaded for this message.
pub fn account_keys_iter(&self) -> Box<dyn Iterator<Item = &Pubkey> + '_> { pub fn account_keys(&self) -> AccountKeys {
match self { match self {
Self::Legacy(message) => Box::new(message.account_keys.iter()), Self::Legacy(message) => AccountKeys::new(&message.account_keys, None),
Self::V0(message) => Box::new(message.account_keys_iter()), Self::V0(message) => message.account_keys(),
}
}
/// Length of all account keys referenced in this message, including dynamically loaded keys.
pub fn account_keys_len(&self) -> usize {
match self {
Self::Legacy(message) => message.account_keys.len(),
Self::V0(message) => message.account_keys_len(),
}
}
/// Returns the address of the account at the specified index.
pub fn get_account_key(&self, index: usize) -> Option<&Pubkey> {
match self {
Self::Legacy(message) => message.account_keys.get(index),
Self::V0(message) => message.get_account_key(index),
} }
} }
@ -210,7 +196,7 @@ impl SanitizedMessage {
} }
fn try_position(&self, key: &Pubkey) -> Option<u8> { fn try_position(&self, key: &Pubkey) -> Option<u8> {
u8::try_from(self.account_keys_iter().position(|k| k == key)?).ok() u8::try_from(self.account_keys().iter().position(|k| k == key)?).ok()
} }
/// Try to compile an instruction using the account keys in this message. /// Try to compile an instruction using the account keys in this message.
@ -230,6 +216,7 @@ impl SanitizedMessage {
/// Decompile message instructions without cloning account keys /// Decompile message instructions without cloning account keys
pub fn decompile_instructions(&self) -> Vec<BorrowedInstruction> { pub fn decompile_instructions(&self) -> Vec<BorrowedInstruction> {
let account_keys = self.account_keys();
self.program_instructions_iter() self.program_instructions_iter()
.map(|(program_id, instruction)| { .map(|(program_id, instruction)| {
let accounts = instruction let accounts = instruction
@ -240,7 +227,7 @@ impl SanitizedMessage {
BorrowedAccountMeta { BorrowedAccountMeta {
is_signer: self.is_signer(account_index), is_signer: self.is_signer(account_index),
is_writable: self.is_writable(account_index), is_writable: self.is_writable(account_index),
pubkey: self.get_account_key(account_index).unwrap(), pubkey: account_keys.get(account_index).unwrap(),
} }
}) })
.collect(); .collect();
@ -267,7 +254,7 @@ impl SanitizedMessage {
self.instructions() self.instructions()
.get(NONCED_TX_MARKER_IX_INDEX as usize) .get(NONCED_TX_MARKER_IX_INDEX as usize)
.filter( .filter(
|ix| match self.get_account_key(ix.program_id_index as usize) { |ix| match self.account_keys().get(ix.program_id_index as usize) {
Some(program_id) => system_program::check_id(program_id), Some(program_id) => system_program::check_id(program_id),
_ => false, _ => false,
}, },
@ -284,7 +271,7 @@ impl SanitizedMessage {
if nonce_must_be_writable && !self.is_writable(idx) { if nonce_must_be_writable && !self.is_writable(idx) {
None None
} else { } else {
self.get_account_key(idx) self.account_keys().get(idx)
} }
}) })
}) })

View File

@ -50,45 +50,6 @@ impl VersionedMessage {
} }
} }
pub fn into_static_account_keys(self) -> Vec<Pubkey> {
match self {
Self::Legacy(message) => message.account_keys,
Self::V0(message) => message.account_keys,
}
}
pub fn static_account_keys_iter(&self) -> impl Iterator<Item = &Pubkey> {
match self {
Self::Legacy(message) => message.account_keys.iter(),
Self::V0(message) => message.account_keys.iter(),
}
}
pub fn static_account_keys_len(&self) -> usize {
match self {
Self::Legacy(message) => message.account_keys.len(),
Self::V0(message) => message.account_keys.len(),
}
}
pub fn total_account_keys_len(&self) -> usize {
match self {
Self::Legacy(message) => message.account_keys.len(),
Self::V0(message) => message.account_keys.len().saturating_add(
message
.address_table_lookups
.iter()
.map(|lookup| {
lookup
.writable_indexes
.len()
.saturating_add(lookup.readonly_indexes.len())
})
.sum(),
),
}
}
pub fn recent_blockhash(&self) -> &Hash { pub fn recent_blockhash(&self) -> &Hash {
match self { match self {
Self::Legacy(message) => &message.recent_blockhash, Self::Legacy(message) => &message.recent_blockhash,

View File

@ -1,7 +1,7 @@
use { use {
crate::{ crate::{
bpf_loader_upgradeable, bpf_loader_upgradeable,
message::{legacy::BUILTIN_PROGRAMS_KEYS, v0}, message::{legacy::BUILTIN_PROGRAMS_KEYS, v0, AccountKeys},
pubkey::Pubkey, pubkey::Pubkey,
sysvar, sysvar,
}, },
@ -57,66 +57,18 @@ impl LoadedAddresses {
pub fn len(&self) -> usize { pub fn len(&self) -> usize {
self.writable.len().saturating_add(self.readonly.len()) self.writable.len().saturating_add(self.readonly.len())
} }
/// Iterate over loaded addresses in the order that they are used
/// as account indexes
pub fn ordered_iter(&self) -> impl Iterator<Item = &Pubkey> {
self.writable.iter().chain(self.readonly.iter())
}
/// Iterate over loaded addresses in the order that they are used
/// as account indexes
pub fn into_ordered_iter(self) -> impl Iterator<Item = Pubkey> {
self.writable.into_iter().chain(self.readonly.into_iter())
}
} }
impl LoadedMessage { impl LoadedMessage {
/// Returns an iterator of account key segments. The ordering of segments /// Returns the list of account keys that are loaded for this message.
/// affects how account indexes from compiled instructions are resolved and pub fn account_keys(&self) -> AccountKeys {
/// so should not be changed. AccountKeys::new(&self.account_keys, Some(&self.loaded_addresses))
fn account_keys_segment_iter(&self) -> impl Iterator<Item = &Vec<Pubkey>> {
vec![
&self.message.account_keys,
&self.loaded_addresses.writable,
&self.loaded_addresses.readonly,
]
.into_iter()
}
/// Returns the total length of loaded accounts for this message
pub fn account_keys_len(&self) -> usize {
let mut len = 0usize;
for key_segment in self.account_keys_segment_iter() {
len = len.saturating_add(key_segment.len());
}
len
}
/// Iterator for the addresses of the loaded accounts for this message
pub fn account_keys_iter(&self) -> impl Iterator<Item = &Pubkey> {
self.account_keys_segment_iter().flatten()
} }
/// Returns true if any account keys are duplicates /// Returns true if any account keys are duplicates
pub fn has_duplicates(&self) -> bool { pub fn has_duplicates(&self) -> bool {
let mut uniq = HashSet::new(); let mut uniq = HashSet::new();
self.account_keys_iter().any(|x| !uniq.insert(x)) self.account_keys().iter().any(|x| !uniq.insert(x))
}
/// Returns the address of the account at the specified index of the list of
/// message account keys constructed from static keys, followed by dynamically
/// loaded writable addresses, and lastly the list of dynamically loaded
/// readonly addresses.
pub fn get_account_key(&self, mut index: usize) -> Option<&Pubkey> {
for key_segment in self.account_keys_segment_iter() {
if index < key_segment.len() {
return Some(&key_segment[index]);
}
index = index.saturating_sub(key_segment.len());
}
None
} }
/// Returns true if the account at the specified index was requested to be /// Returns true if the account at the specified index was requested to be
@ -144,7 +96,7 @@ impl LoadedMessage {
/// Returns true if the account at the specified index was loaded as writable /// Returns true if the account at the specified index was loaded as writable
pub fn is_writable(&self, key_index: usize) -> bool { pub fn is_writable(&self, key_index: usize) -> bool {
if self.is_writable_index(key_index) { if self.is_writable_index(key_index) {
if let Some(key) = self.get_account_key(key_index) { if let Some(key) = self.account_keys().get(key_index) {
let demote_program_id = self.is_key_called_as_program(key_index) let demote_program_id = self.is_key_called_as_program(key_index)
&& !self.is_upgradeable_loader_present(); && !self.is_upgradeable_loader_present();
return !(sysvar::is_sysvar_id(key) return !(sysvar::is_sysvar_id(key)
@ -169,7 +121,8 @@ impl LoadedMessage {
/// Returns true if any account is the bpf upgradeable loader /// Returns true if any account is the bpf upgradeable loader
pub fn is_upgradeable_loader_present(&self) -> bool { pub fn is_upgradeable_loader_present(&self) -> bool {
self.account_keys_iter() self.account_keys()
.iter()
.any(|&key| key == bpf_loader_upgradeable::id()) .any(|&key| key == bpf_loader_upgradeable::id())
} }
} }
@ -209,39 +162,6 @@ mod tests {
(message, [key0, key1, key2, key3, key4, key5]) (message, [key0, key1, key2, key3, key4, key5])
} }
#[test]
fn test_account_keys_segment_iter() {
let (message, keys) = check_test_loaded_message();
let expected_segments = vec![
vec![keys[0], keys[1], keys[2], keys[3]],
vec![keys[4]],
vec![keys[5]],
];
let mut iter = message.account_keys_segment_iter();
for expected_segment in expected_segments {
assert_eq!(iter.next(), Some(&expected_segment));
}
}
#[test]
fn test_account_keys_len() {
let (message, keys) = check_test_loaded_message();
assert_eq!(message.account_keys_len(), keys.len());
}
#[test]
fn test_account_keys_iter() {
let (message, keys) = check_test_loaded_message();
let mut iter = message.account_keys_iter();
for expected_key in keys {
assert_eq!(iter.next(), Some(&expected_key));
}
}
#[test] #[test]
fn test_has_duplicates() { fn test_has_duplicates() {
let message = check_test_loaded_message().0; let message = check_test_loaded_message().0;
@ -276,18 +196,6 @@ mod tests {
} }
} }
#[test]
fn test_get_account_key() {
let (message, keys) = check_test_loaded_message();
assert_eq!(message.get_account_key(0), Some(&keys[0]));
assert_eq!(message.get_account_key(1), Some(&keys[1]));
assert_eq!(message.get_account_key(2), Some(&keys[2]));
assert_eq!(message.get_account_key(3), Some(&keys[3]));
assert_eq!(message.get_account_key(4), Some(&keys[4]));
assert_eq!(message.get_account_key(5), Some(&keys[5]));
}
#[test] #[test]
fn test_is_writable_index() { fn test_is_writable_index() {
let message = check_test_loaded_message().0; let message = check_test_loaded_message().0;

View File

@ -156,7 +156,7 @@ impl SanitizedTransaction {
if self.message.has_duplicates() { if self.message.has_duplicates() {
Err(TransactionError::AccountLoadedTwice) Err(TransactionError::AccountLoadedTwice)
} else if feature_set.is_active(&feature_set::max_tx_account_locks::id()) } else if feature_set.is_active(&feature_set::max_tx_account_locks::id())
&& self.message.account_keys_len() > MAX_TX_ACCOUNT_LOCKS && self.message.account_keys().len() > MAX_TX_ACCOUNT_LOCKS
{ {
Err(TransactionError::TooManyAccountLocks) Err(TransactionError::TooManyAccountLocks)
} else { } else {
@ -167,17 +167,16 @@ impl SanitizedTransaction {
/// Return the list of accounts that must be locked during processing this transaction. /// Return the list of accounts that must be locked during processing this transaction.
pub fn get_account_locks_unchecked(&self) -> TransactionAccountLocks { pub fn get_account_locks_unchecked(&self) -> TransactionAccountLocks {
let message = &self.message; let message = &self.message;
let account_keys = message.account_keys();
let num_readonly_accounts = message.num_readonly_accounts(); let num_readonly_accounts = message.num_readonly_accounts();
let num_writable_accounts = message let num_writable_accounts = account_keys.len().saturating_sub(num_readonly_accounts);
.account_keys_len()
.saturating_sub(num_readonly_accounts);
let mut account_locks = TransactionAccountLocks { let mut account_locks = TransactionAccountLocks {
writable: Vec::with_capacity(num_writable_accounts), writable: Vec::with_capacity(num_writable_accounts),
readonly: Vec::with_capacity(num_readonly_accounts), readonly: Vec::with_capacity(num_readonly_accounts),
}; };
for (i, key) in message.account_keys_iter().enumerate() { for (i, key) in account_keys.iter().enumerate() {
if message.is_writable(i) { if message.is_writable(i) {
account_locks.writable.push(key); account_locks.writable.push(key);
} else { } else {
@ -215,7 +214,7 @@ impl SanitizedTransaction {
if self if self
.signatures .signatures
.iter() .iter()
.zip(self.message.account_keys_iter()) .zip(self.message.account_keys().iter())
.map(|(signature, pubkey)| signature.verify(pubkey.as_ref(), &message_bytes)) .map(|(signature, pubkey)| signature.verify(pubkey.as_ref(), &message_bytes))
.any(|verified| !verified) .any(|verified| !verified)
{ {

View File

@ -41,7 +41,7 @@ impl Sanitize for VersionedTransaction {
// Signatures are verified before message keys are loaded so all signers // Signatures are verified before message keys are loaded so all signers
// must correspond to static account keys. // must correspond to static account keys.
if self.signatures.len() > self.message.static_account_keys_len() { if self.signatures.len() > self.message.static_account_keys().len() {
return Err(SanitizeError::IndexOutOfBounds); return Err(SanitizeError::IndexOutOfBounds);
} }
@ -127,7 +127,7 @@ impl VersionedTransaction {
fn _verify_with_results(&self, message_bytes: &[u8]) -> Vec<bool> { fn _verify_with_results(&self, message_bytes: &[u8]) -> Vec<bool> {
self.signatures self.signatures
.iter() .iter()
.zip(self.message.static_account_keys_iter()) .zip(self.message.static_account_keys().iter())
.map(|(signature, pubkey)| signature.verify(pubkey.as_ref(), message_bytes)) .map(|(signature, pubkey)| signature.verify(pubkey.as_ref(), message_bytes))
.collect() .collect()
} }

View File

@ -643,7 +643,7 @@ impl LedgerStorage {
let signature = transaction.signatures[0]; let signature = transaction.signatures[0];
let memo = extract_and_fmt_memos(transaction_with_meta); let memo = extract_and_fmt_memos(transaction_with_meta);
for address in transaction_with_meta.account_keys_iter() { for address in transaction_with_meta.account_keys().iter() {
if !is_sysvar_id(address) { if !is_sysvar_id(address) {
by_addr by_addr
.entry(address) .entry(address)
@ -730,7 +730,7 @@ impl LedgerStorage {
let index = index as u32; let index = index as u32;
let err = meta.as_ref().and_then(|meta| meta.status.clone().err()); let err = meta.as_ref().and_then(|meta| meta.status.clone().err());
for address in transaction_with_meta.account_keys_iter() { for address in transaction_with_meta.account_keys().iter() {
if !is_sysvar_id(address) { if !is_sysvar_id(address) {
addresses.insert(address); addresses.insert(address);
} }

View File

@ -2,7 +2,7 @@ use {
crate::{parse_instruction::parse_memo_data, VersionedTransactionWithStatusMeta}, crate::{parse_instruction::parse_memo_data, VersionedTransactionWithStatusMeta},
solana_sdk::{ solana_sdk::{
instruction::CompiledInstruction, instruction::CompiledInstruction,
message::{Message, SanitizedMessage}, message::{AccountKeys, Message, SanitizedMessage},
pubkey::Pubkey, pubkey::Pubkey,
}, },
}; };
@ -45,20 +45,23 @@ pub trait ExtractMemos {
impl ExtractMemos for Message { impl ExtractMemos for Message {
fn extract_memos(&self) -> Vec<String> { fn extract_memos(&self) -> Vec<String> {
extract_memos_inner(self.account_keys.iter(), &self.instructions) extract_memos_inner(
&AccountKeys::new(&self.account_keys, None),
&self.instructions,
)
} }
} }
impl ExtractMemos for SanitizedMessage { impl ExtractMemos for SanitizedMessage {
fn extract_memos(&self) -> Vec<String> { fn extract_memos(&self) -> Vec<String> {
extract_memos_inner(self.account_keys_iter(), self.instructions()) extract_memos_inner(&self.account_keys(), self.instructions())
} }
} }
impl ExtractMemos for VersionedTransactionWithStatusMeta { impl ExtractMemos for VersionedTransactionWithStatusMeta {
fn extract_memos(&self) -> Vec<String> { fn extract_memos(&self) -> Vec<String> {
extract_memos_inner( extract_memos_inner(
self.account_keys_iter(), &self.account_keys(),
self.transaction.message.instructions(), self.transaction.message.instructions(),
) )
} }
@ -70,11 +73,11 @@ enum KeyType<'a> {
Unknown(&'a Pubkey), Unknown(&'a Pubkey),
} }
fn extract_memos_inner<'a>( fn extract_memos_inner(
account_keys: impl Iterator<Item = &'a Pubkey>, account_keys: &AccountKeys,
instructions: &[CompiledInstruction], instructions: &[CompiledInstruction],
) -> Vec<String> { ) -> Vec<String> {
let mut account_keys: Vec<KeyType> = account_keys.map(KeyType::Unknown).collect(); let mut account_keys: Vec<KeyType> = account_keys.iter().map(KeyType::Unknown).collect();
instructions instructions
.iter() .iter()
.filter_map(|ix| { .filter_map(|ix| {
@ -129,15 +132,16 @@ mod test {
data: memo1.as_bytes().to_vec(), data: memo1.as_bytes().to_vec(),
}, },
]; ];
let account_keys = vec![ let static_keys = vec![
fee_payer, fee_payer,
spl_memo_id_v1(), spl_memo_id_v1(),
another_program_id, another_program_id,
spl_memo_id_v3(), spl_memo_id_v3(),
]; ];
let account_keys = AccountKeys::new(&static_keys, None);
assert_eq!( assert_eq!(
extract_memos_inner(account_keys.iter(), &memo_instructions), extract_memos_inner(&account_keys, &memo_instructions),
expected_memos expected_memos
); );
} }

View File

@ -26,8 +26,7 @@ use {
clock::{Slot, UnixTimestamp}, clock::{Slot, UnixTimestamp},
commitment_config::CommitmentConfig, commitment_config::CommitmentConfig,
instruction::CompiledInstruction, instruction::CompiledInstruction,
message::{v0::LoadedAddresses, Message, MessageHeader}, message::{v0::LoadedAddresses, AccountKeys, Message, MessageHeader},
pubkey::Pubkey,
sanitize::Sanitize, sanitize::Sanitize,
signature::Signature, signature::Signature,
transaction::{Result, Transaction, TransactionError, VersionedTransaction}, transaction::{Result, Transaction, TransactionError, VersionedTransaction},
@ -82,8 +81,8 @@ pub enum UiInstruction {
} }
impl UiInstruction { impl UiInstruction {
fn parse(instruction: &CompiledInstruction, account_keys: &[Pubkey]) -> Self { fn parse(instruction: &CompiledInstruction, account_keys: &AccountKeys) -> Self {
let program_id = instruction.program_id(account_keys); let program_id = &account_keys[instruction.program_id_index as usize];
if let Ok(parsed_instruction) = parse(program_id, instruction, account_keys) { if let Ok(parsed_instruction) = parse(program_id, instruction, account_keys) {
UiInstruction::Parsed(UiParsedInstruction::Parsed(parsed_instruction)) UiInstruction::Parsed(UiParsedInstruction::Parsed(parsed_instruction))
} else { } else {
@ -130,7 +129,7 @@ pub struct UiPartiallyDecodedInstruction {
} }
impl UiPartiallyDecodedInstruction { impl UiPartiallyDecodedInstruction {
fn from(instruction: &CompiledInstruction, account_keys: &[Pubkey]) -> Self { fn from(instruction: &CompiledInstruction, account_keys: &AccountKeys) -> Self {
Self { Self {
program_id: account_keys[instruction.program_id_index as usize].to_string(), program_id: account_keys[instruction.program_id_index as usize].to_string(),
accounts: instruction accounts: instruction
@ -162,12 +161,13 @@ pub struct UiInnerInstructions {
impl UiInnerInstructions { impl UiInnerInstructions {
fn parse(inner_instructions: InnerInstructions, message: &Message) -> Self { fn parse(inner_instructions: InnerInstructions, message: &Message) -> Self {
let account_keys = AccountKeys::new(&message.account_keys, None);
Self { Self {
index: inner_instructions.index, index: inner_instructions.index,
instructions: inner_instructions instructions: inner_instructions
.instructions .instructions
.iter() .iter()
.map(|ix| UiInstruction::parse(ix, &message.account_keys)) .map(|ix| UiInstruction::parse(ix, &account_keys))
.collect(), .collect(),
} }
} }
@ -572,14 +572,11 @@ pub struct VersionedTransactionWithStatusMeta {
} }
impl VersionedTransactionWithStatusMeta { impl VersionedTransactionWithStatusMeta {
pub fn account_keys_iter(&self) -> impl Iterator<Item = &Pubkey> { pub fn account_keys(&self) -> AccountKeys {
let static_keys_iter = self.transaction.message.static_account_keys().iter(); AccountKeys::new(
let dynamic_keys_iter = self self.transaction.message.static_account_keys(),
.meta self.meta.as_ref().map(|meta| &meta.loaded_addresses),
.iter() )
.flat_map(|meta| meta.loaded_addresses.ordered_iter());
static_keys_iter.chain(dynamic_keys_iter)
} }
pub fn into_legacy_transaction_with_meta(self) -> Option<TransactionWithStatusMeta> { pub fn into_legacy_transaction_with_meta(self) -> Option<TransactionWithStatusMeta> {
@ -753,13 +750,14 @@ impl Encodable for &Message {
type Encoded = UiMessage; type Encoded = UiMessage;
fn encode(self, encoding: UiTransactionEncoding) -> Self::Encoded { fn encode(self, encoding: UiTransactionEncoding) -> Self::Encoded {
if encoding == UiTransactionEncoding::JsonParsed { if encoding == UiTransactionEncoding::JsonParsed {
let account_keys = AccountKeys::new(&self.account_keys, None);
UiMessage::Parsed(UiParsedMessage { UiMessage::Parsed(UiParsedMessage {
account_keys: parse_accounts(self), account_keys: parse_accounts(self),
recent_blockhash: self.recent_blockhash.to_string(), recent_blockhash: self.recent_blockhash.to_string(),
instructions: self instructions: self
.instructions .instructions
.iter() .iter()
.map(|instruction| UiInstruction::parse(instruction, &self.account_keys)) .map(|instruction| UiInstruction::parse(instruction, &account_keys))
.collect(), .collect(),
}) })
} else { } else {

View File

@ -3,7 +3,7 @@ use {
check_num_accounts, ParsableProgram, ParseInstructionError, ParsedInstructionEnum, check_num_accounts, ParsableProgram, ParseInstructionError, ParsedInstructionEnum,
}, },
serde_json::json, serde_json::json,
solana_sdk::{instruction::CompiledInstruction, pubkey::Pubkey}, solana_sdk::{instruction::CompiledInstruction, message::AccountKeys, pubkey::Pubkey},
}; };
// A helper function to convert spl_associated_token_account::id() as spl_sdk::pubkey::Pubkey // A helper function to convert spl_associated_token_account::id() as spl_sdk::pubkey::Pubkey
@ -14,7 +14,7 @@ pub fn spl_associated_token_id() -> Pubkey {
pub fn parse_associated_token( pub fn parse_associated_token(
instruction: &CompiledInstruction, instruction: &CompiledInstruction,
account_keys: &[Pubkey], account_keys: &AccountKeys,
) -> Result<ParsedInstructionEnum, ParseInstructionError> { ) -> Result<ParsedInstructionEnum, ParseInstructionError> {
match instruction.accounts.iter().max() { match instruction.accounts.iter().max() {
Some(index) if (*index as usize) < account_keys.len() => {} Some(index) if (*index as usize) < account_keys.len() => {}
@ -88,8 +88,9 @@ mod test {
); );
let message = Message::new(&[create_ix], None); let message = Message::new(&[create_ix], None);
let compiled_instruction = convert_compiled_instruction(&message.instructions[0]); let compiled_instruction = convert_compiled_instruction(&message.instructions[0]);
let account_keys = AccountKeys::new(&keys, None);
assert_eq!( assert_eq!(
parse_associated_token(&compiled_instruction, &keys).unwrap(), parse_associated_token(&compiled_instruction, &account_keys).unwrap(),
ParsedInstructionEnum { ParsedInstructionEnum {
instruction_type: "create".to_string(), instruction_type: "create".to_string(),
info: json!({ info: json!({

View File

@ -6,13 +6,13 @@ use {
serde_json::json, serde_json::json,
solana_sdk::{ solana_sdk::{
instruction::CompiledInstruction, loader_instruction::LoaderInstruction, instruction::CompiledInstruction, loader_instruction::LoaderInstruction,
loader_upgradeable_instruction::UpgradeableLoaderInstruction, pubkey::Pubkey, loader_upgradeable_instruction::UpgradeableLoaderInstruction, message::AccountKeys,
}, },
}; };
pub fn parse_bpf_loader( pub fn parse_bpf_loader(
instruction: &CompiledInstruction, instruction: &CompiledInstruction,
account_keys: &[Pubkey], account_keys: &AccountKeys,
) -> Result<ParsedInstructionEnum, ParseInstructionError> { ) -> Result<ParsedInstructionEnum, ParseInstructionError> {
let bpf_loader_instruction: LoaderInstruction = deserialize(&instruction.data) let bpf_loader_instruction: LoaderInstruction = deserialize(&instruction.data)
.map_err(|_| ParseInstructionError::InstructionNotParsable(ParsableProgram::BpfLoader))?; .map_err(|_| ParseInstructionError::InstructionNotParsable(ParsableProgram::BpfLoader))?;
@ -41,7 +41,7 @@ pub fn parse_bpf_loader(
pub fn parse_bpf_upgradeable_loader( pub fn parse_bpf_upgradeable_loader(
instruction: &CompiledInstruction, instruction: &CompiledInstruction,
account_keys: &[Pubkey], account_keys: &AccountKeys,
) -> Result<ParsedInstructionEnum, ParseInstructionError> { ) -> Result<ParsedInstructionEnum, ParseInstructionError> {
let bpf_upgradeable_loader_instruction: UpgradeableLoaderInstruction = let bpf_upgradeable_loader_instruction: UpgradeableLoaderInstruction =
deserialize(&instruction.data).map_err(|_| { deserialize(&instruction.data).map_err(|_| {
@ -159,7 +159,10 @@ mod test {
use { use {
super::*, super::*,
serde_json::Value, serde_json::Value,
solana_sdk::{message::Message, pubkey}, solana_sdk::{
message::Message,
pubkey::{self, Pubkey},
},
}; };
#[test] #[test]
@ -180,7 +183,11 @@ mod test {
); );
let message = Message::new(&[instruction], Some(&fee_payer)); let message = Message::new(&[instruction], Some(&fee_payer));
assert_eq!( assert_eq!(
parse_bpf_loader(&message.instructions[0], &account_keys).unwrap(), parse_bpf_loader(
&message.instructions[0],
&AccountKeys::new(&account_keys, None)
)
.unwrap(),
ParsedInstructionEnum { ParsedInstructionEnum {
instruction_type: "write".to_string(), instruction_type: "write".to_string(),
info: json!({ info: json!({
@ -190,12 +197,20 @@ mod test {
}), }),
} }
); );
assert!(parse_bpf_loader(&message.instructions[0], &missing_account_keys).is_err()); assert!(parse_bpf_loader(
&message.instructions[0],
&AccountKeys::new(&missing_account_keys, None)
)
.is_err());
let instruction = solana_sdk::loader_instruction::finalize(&account_pubkey, &program_id); let instruction = solana_sdk::loader_instruction::finalize(&account_pubkey, &program_id);
let message = Message::new(&[instruction], Some(&fee_payer)); let message = Message::new(&[instruction], Some(&fee_payer));
assert_eq!( assert_eq!(
parse_bpf_loader(&message.instructions[0], &account_keys).unwrap(), parse_bpf_loader(
&message.instructions[0],
&AccountKeys::new(&account_keys, None)
)
.unwrap(),
ParsedInstructionEnum { ParsedInstructionEnum {
instruction_type: "finalize".to_string(), instruction_type: "finalize".to_string(),
info: json!({ info: json!({
@ -203,21 +218,33 @@ mod test {
}), }),
} }
); );
assert!(parse_bpf_loader(&message.instructions[0], &missing_account_keys).is_err()); assert!(parse_bpf_loader(
&message.instructions[0],
&AccountKeys::new(&missing_account_keys, None)
)
.is_err());
let bad_compiled_instruction = CompiledInstruction { let bad_compiled_instruction = CompiledInstruction {
program_id_index: 3, program_id_index: 3,
accounts: vec![1, 2], accounts: vec![1, 2],
data: vec![2, 0, 0, 0], // LoaderInstruction enum only has 2 variants data: vec![2, 0, 0, 0], // LoaderInstruction enum only has 2 variants
}; };
assert!(parse_bpf_loader(&bad_compiled_instruction, &account_keys).is_err()); assert!(parse_bpf_loader(
&bad_compiled_instruction,
&AccountKeys::new(&account_keys, None)
)
.is_err());
let bad_compiled_instruction = CompiledInstruction { let bad_compiled_instruction = CompiledInstruction {
program_id_index: 3, program_id_index: 3,
accounts: vec![], accounts: vec![],
data: vec![1, 0, 0, 0], data: vec![1, 0, 0, 0],
}; };
assert!(parse_bpf_loader(&bad_compiled_instruction, &account_keys).is_err()); assert!(parse_bpf_loader(
&bad_compiled_instruction,
&AccountKeys::new(&account_keys, None)
)
.is_err());
} }
#[test] #[test]
@ -240,7 +267,11 @@ mod test {
.unwrap(); .unwrap();
let message = Message::new(&instructions, None); let message = Message::new(&instructions, None);
assert_eq!( assert_eq!(
parse_bpf_upgradeable_loader(&message.instructions[1], &keys[0..3]).unwrap(), parse_bpf_upgradeable_loader(
&message.instructions[1],
&AccountKeys::new(&keys[0..3], None)
)
.unwrap(),
ParsedInstructionEnum { ParsedInstructionEnum {
instruction_type: "initializeBuffer".to_string(), instruction_type: "initializeBuffer".to_string(),
info: json!({ info: json!({
@ -249,13 +280,21 @@ mod test {
}), }),
} }
); );
assert!(parse_bpf_upgradeable_loader(&message.instructions[1], &keys[0..2]).is_err()); assert!(parse_bpf_upgradeable_loader(
&message.instructions[1],
&AccountKeys::new(&keys[0..2], None)
)
.is_err());
let instruction = let instruction =
solana_sdk::bpf_loader_upgradeable::write(&keys[1], &keys[0], offset, bytes.clone()); solana_sdk::bpf_loader_upgradeable::write(&keys[1], &keys[0], offset, bytes.clone());
let message = Message::new(&[instruction], None); let message = Message::new(&[instruction], None);
assert_eq!( assert_eq!(
parse_bpf_upgradeable_loader(&message.instructions[0], &keys[0..2]).unwrap(), parse_bpf_upgradeable_loader(
&message.instructions[0],
&AccountKeys::new(&keys[0..2], None)
)
.unwrap(),
ParsedInstructionEnum { ParsedInstructionEnum {
instruction_type: "write".to_string(), instruction_type: "write".to_string(),
info: json!({ info: json!({
@ -266,7 +305,11 @@ mod test {
}), }),
} }
); );
assert!(parse_bpf_upgradeable_loader(&message.instructions[0], &keys[0..1]).is_err()); assert!(parse_bpf_upgradeable_loader(
&message.instructions[0],
&AccountKeys::new(&keys[0..1], None)
)
.is_err());
let instructions = solana_sdk::bpf_loader_upgradeable::deploy_with_max_program_len( let instructions = solana_sdk::bpf_loader_upgradeable::deploy_with_max_program_len(
&keys[0], &keys[0],
@ -279,7 +322,11 @@ mod test {
.unwrap(); .unwrap();
let message = Message::new(&instructions, None); let message = Message::new(&instructions, None);
assert_eq!( assert_eq!(
parse_bpf_upgradeable_loader(&message.instructions[1], &keys[0..8]).unwrap(), parse_bpf_upgradeable_loader(
&message.instructions[1],
&AccountKeys::new(&keys[0..8], None)
)
.unwrap(),
ParsedInstructionEnum { ParsedInstructionEnum {
instruction_type: "deployWithMaxDataLen".to_string(), instruction_type: "deployWithMaxDataLen".to_string(),
info: json!({ info: json!({
@ -295,13 +342,21 @@ mod test {
}), }),
} }
); );
assert!(parse_bpf_upgradeable_loader(&message.instructions[1], &keys[0..7]).is_err()); assert!(parse_bpf_upgradeable_loader(
&message.instructions[1],
&AccountKeys::new(&keys[0..7], None)
)
.is_err());
let instruction = let instruction =
solana_sdk::bpf_loader_upgradeable::upgrade(&keys[2], &keys[3], &keys[0], &keys[4]); solana_sdk::bpf_loader_upgradeable::upgrade(&keys[2], &keys[3], &keys[0], &keys[4]);
let message = Message::new(&[instruction], None); let message = Message::new(&[instruction], None);
assert_eq!( assert_eq!(
parse_bpf_upgradeable_loader(&message.instructions[0], &keys[0..7]).unwrap(), parse_bpf_upgradeable_loader(
&message.instructions[0],
&AccountKeys::new(&keys[0..7], None)
)
.unwrap(),
ParsedInstructionEnum { ParsedInstructionEnum {
instruction_type: "upgrade".to_string(), instruction_type: "upgrade".to_string(),
info: json!({ info: json!({
@ -315,13 +370,21 @@ mod test {
}), }),
} }
); );
assert!(parse_bpf_upgradeable_loader(&message.instructions[0], &keys[0..6]).is_err()); assert!(parse_bpf_upgradeable_loader(
&message.instructions[0],
&AccountKeys::new(&keys[0..6], None)
)
.is_err());
let instruction = let instruction =
solana_sdk::bpf_loader_upgradeable::set_buffer_authority(&keys[1], &keys[0], &keys[2]); solana_sdk::bpf_loader_upgradeable::set_buffer_authority(&keys[1], &keys[0], &keys[2]);
let message = Message::new(&[instruction], None); let message = Message::new(&[instruction], None);
assert_eq!( assert_eq!(
parse_bpf_upgradeable_loader(&message.instructions[0], &keys[0..3]).unwrap(), parse_bpf_upgradeable_loader(
&message.instructions[0],
&AccountKeys::new(&keys[0..3], None)
)
.unwrap(),
ParsedInstructionEnum { ParsedInstructionEnum {
instruction_type: "setAuthority".to_string(), instruction_type: "setAuthority".to_string(),
info: json!({ info: json!({
@ -331,7 +394,11 @@ mod test {
}), }),
} }
); );
assert!(parse_bpf_upgradeable_loader(&message.instructions[0], &keys[0..1]).is_err()); assert!(parse_bpf_upgradeable_loader(
&message.instructions[0],
&AccountKeys::new(&keys[0..1], None)
)
.is_err());
let instruction = solana_sdk::bpf_loader_upgradeable::set_upgrade_authority( let instruction = solana_sdk::bpf_loader_upgradeable::set_upgrade_authority(
&keys[1], &keys[1],
@ -340,7 +407,11 @@ mod test {
); );
let message = Message::new(&[instruction], None); let message = Message::new(&[instruction], None);
assert_eq!( assert_eq!(
parse_bpf_upgradeable_loader(&message.instructions[0], &keys[0..3]).unwrap(), parse_bpf_upgradeable_loader(
&message.instructions[0],
&AccountKeys::new(&keys[0..3], None)
)
.unwrap(),
ParsedInstructionEnum { ParsedInstructionEnum {
instruction_type: "setAuthority".to_string(), instruction_type: "setAuthority".to_string(),
info: json!({ info: json!({
@ -350,13 +421,21 @@ mod test {
}), }),
} }
); );
assert!(parse_bpf_upgradeable_loader(&message.instructions[0], &keys[0..1]).is_err()); assert!(parse_bpf_upgradeable_loader(
&message.instructions[0],
&AccountKeys::new(&keys[0..1], None)
)
.is_err());
let instruction = let instruction =
solana_sdk::bpf_loader_upgradeable::set_upgrade_authority(&keys[1], &keys[0], None); solana_sdk::bpf_loader_upgradeable::set_upgrade_authority(&keys[1], &keys[0], None);
let message = Message::new(&[instruction], None); let message = Message::new(&[instruction], None);
assert_eq!( assert_eq!(
parse_bpf_upgradeable_loader(&message.instructions[0], &keys[0..2]).unwrap(), parse_bpf_upgradeable_loader(
&message.instructions[0],
&AccountKeys::new(&keys[0..2], None)
)
.unwrap(),
ParsedInstructionEnum { ParsedInstructionEnum {
instruction_type: "setAuthority".to_string(), instruction_type: "setAuthority".to_string(),
info: json!({ info: json!({
@ -366,12 +445,20 @@ mod test {
}), }),
} }
); );
assert!(parse_bpf_upgradeable_loader(&message.instructions[0], &keys[0..1]).is_err()); assert!(parse_bpf_upgradeable_loader(
&message.instructions[0],
&AccountKeys::new(&keys[0..1], None)
)
.is_err());
let instruction = solana_sdk::bpf_loader_upgradeable::close(&keys[0], &keys[1], &keys[2]); let instruction = solana_sdk::bpf_loader_upgradeable::close(&keys[0], &keys[1], &keys[2]);
let message = Message::new(&[instruction], None); let message = Message::new(&[instruction], None);
assert_eq!( assert_eq!(
parse_bpf_upgradeable_loader(&message.instructions[0], &keys[..3]).unwrap(), parse_bpf_upgradeable_loader(
&message.instructions[0],
&AccountKeys::new(&keys[..3], None)
)
.unwrap(),
ParsedInstructionEnum { ParsedInstructionEnum {
instruction_type: "close".to_string(), instruction_type: "close".to_string(),
info: json!({ info: json!({
@ -381,6 +468,10 @@ mod test {
}), }),
} }
); );
assert!(parse_bpf_upgradeable_loader(&message.instructions[0], &keys[0..1]).is_err()); assert!(parse_bpf_upgradeable_loader(
&message.instructions[0],
&AccountKeys::new(&keys[0..1], None)
)
.is_err());
} }
} }

View File

@ -11,7 +11,10 @@ use {
inflector::Inflector, inflector::Inflector,
serde_json::Value, serde_json::Value,
solana_account_decoder::parse_token::spl_token_id, solana_account_decoder::parse_token::spl_token_id,
solana_sdk::{instruction::CompiledInstruction, pubkey::Pubkey, stake, system_program}, solana_sdk::{
instruction::CompiledInstruction, message::AccountKeys, pubkey::Pubkey, stake,
system_program,
},
std::{ std::{
collections::HashMap, collections::HashMap,
str::{from_utf8, Utf8Error}, str::{from_utf8, Utf8Error},
@ -98,7 +101,7 @@ pub enum ParsableProgram {
pub fn parse( pub fn parse(
program_id: &Pubkey, program_id: &Pubkey,
instruction: &CompiledInstruction, instruction: &CompiledInstruction,
account_keys: &[Pubkey], account_keys: &AccountKeys,
) -> Result<ParsedInstruction, ParseInstructionError> { ) -> Result<ParsedInstruction, ParseInstructionError> {
let program_name = PARSABLE_PROGRAM_IDS let program_name = PARSABLE_PROGRAM_IDS
.get(program_id) .get(program_id)
@ -156,13 +159,14 @@ mod test {
#[test] #[test]
fn test_parse() { fn test_parse() {
let no_keys = AccountKeys::new(&[], None);
let memo_instruction = CompiledInstruction { let memo_instruction = CompiledInstruction {
program_id_index: 0, program_id_index: 0,
accounts: vec![], accounts: vec![],
data: vec![240, 159, 166, 150], data: vec![240, 159, 166, 150],
}; };
assert_eq!( assert_eq!(
parse(&MEMO_V1_PROGRAM_ID, &memo_instruction, &[]).unwrap(), parse(&MEMO_V1_PROGRAM_ID, &memo_instruction, &no_keys).unwrap(),
ParsedInstruction { ParsedInstruction {
program: "spl-memo".to_string(), program: "spl-memo".to_string(),
program_id: MEMO_V1_PROGRAM_ID.to_string(), program_id: MEMO_V1_PROGRAM_ID.to_string(),
@ -170,7 +174,7 @@ mod test {
} }
); );
assert_eq!( assert_eq!(
parse(&MEMO_V3_PROGRAM_ID, &memo_instruction, &[]).unwrap(), parse(&MEMO_V3_PROGRAM_ID, &memo_instruction, &no_keys).unwrap(),
ParsedInstruction { ParsedInstruction {
program: "spl-memo".to_string(), program: "spl-memo".to_string(),
program_id: MEMO_V3_PROGRAM_ID.to_string(), program_id: MEMO_V3_PROGRAM_ID.to_string(),
@ -179,7 +183,7 @@ mod test {
); );
let non_parsable_program_id = Pubkey::new(&[1; 32]); let non_parsable_program_id = Pubkey::new(&[1; 32]);
assert!(parse(&non_parsable_program_id, &memo_instruction, &[]).is_err()); assert!(parse(&non_parsable_program_id, &memo_instruction, &no_keys).is_err());
} }
#[test] #[test]

View File

@ -5,13 +5,14 @@ use {
bincode::deserialize, bincode::deserialize,
serde_json::{json, Map}, serde_json::{json, Map},
solana_sdk::{ solana_sdk::{
instruction::CompiledInstruction, pubkey::Pubkey, stake::instruction::StakeInstruction, instruction::CompiledInstruction, message::AccountKeys,
stake::instruction::StakeInstruction,
}, },
}; };
pub fn parse_stake( pub fn parse_stake(
instruction: &CompiledInstruction, instruction: &CompiledInstruction,
account_keys: &[Pubkey], account_keys: &AccountKeys,
) -> Result<ParsedInstructionEnum, ParseInstructionError> { ) -> Result<ParsedInstructionEnum, ParseInstructionError> {
let stake_instruction: StakeInstruction = deserialize(&instruction.data) let stake_instruction: StakeInstruction = deserialize(&instruction.data)
.map_err(|_| ParseInstructionError::InstructionNotParsable(ParsableProgram::Stake))?; .map_err(|_| ParseInstructionError::InstructionNotParsable(ParsableProgram::Stake))?;
@ -312,7 +313,11 @@ mod test {
instruction::create_account(&keys[0], &keys[1], &authorized, &lockup, lamports); instruction::create_account(&keys[0], &keys[1], &authorized, &lockup, lamports);
let message = Message::new(&instructions, None); let message = Message::new(&instructions, None);
assert_eq!( assert_eq!(
parse_stake(&message.instructions[1], &keys[0..3]).unwrap(), parse_stake(
&message.instructions[1],
&AccountKeys::new(&keys[0..3], None)
)
.unwrap(),
ParsedInstructionEnum { ParsedInstructionEnum {
instruction_type: "initialize".to_string(), instruction_type: "initialize".to_string(),
info: json!({ info: json!({
@ -330,13 +335,21 @@ mod test {
}), }),
} }
); );
assert!(parse_stake(&message.instructions[1], &keys[0..2]).is_err()); assert!(parse_stake(
&message.instructions[1],
&AccountKeys::new(&keys[0..2], None)
)
.is_err());
let instruction = let instruction =
instruction::authorize(&keys[1], &keys[0], &keys[3], StakeAuthorize::Staker, None); instruction::authorize(&keys[1], &keys[0], &keys[3], StakeAuthorize::Staker, None);
let message = Message::new(&[instruction], None); let message = Message::new(&[instruction], None);
assert_eq!( assert_eq!(
parse_stake(&message.instructions[0], &keys[0..3]).unwrap(), parse_stake(
&message.instructions[0],
&AccountKeys::new(&keys[0..3], None)
)
.unwrap(),
ParsedInstructionEnum { ParsedInstructionEnum {
instruction_type: "authorize".to_string(), instruction_type: "authorize".to_string(),
info: json!({ info: json!({
@ -348,7 +361,11 @@ mod test {
}), }),
} }
); );
assert!(parse_stake(&message.instructions[0], &keys[0..2]).is_err()); assert!(parse_stake(
&message.instructions[0],
&AccountKeys::new(&keys[0..2], None)
)
.is_err());
let instruction = instruction::authorize( let instruction = instruction::authorize(
&keys[2], &keys[2],
@ -359,7 +376,11 @@ mod test {
); );
let message = Message::new(&[instruction], None); let message = Message::new(&[instruction], None);
assert_eq!( assert_eq!(
parse_stake(&message.instructions[0], &keys[0..4]).unwrap(), parse_stake(
&message.instructions[0],
&AccountKeys::new(&keys[0..4], None)
)
.unwrap(),
ParsedInstructionEnum { ParsedInstructionEnum {
instruction_type: "authorize".to_string(), instruction_type: "authorize".to_string(),
info: json!({ info: json!({
@ -372,12 +393,20 @@ mod test {
}), }),
} }
); );
assert!(parse_stake(&message.instructions[0], &keys[0..2]).is_err()); 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 instruction = instruction::delegate_stake(&keys[1], &keys[0], &keys[2]);
let message = Message::new(&[instruction], None); let message = Message::new(&[instruction], None);
assert_eq!( assert_eq!(
parse_stake(&message.instructions[0], &keys[0..6]).unwrap(), parse_stake(
&message.instructions[0],
&AccountKeys::new(&keys[0..6], None)
)
.unwrap(),
ParsedInstructionEnum { ParsedInstructionEnum {
instruction_type: "delegate".to_string(), instruction_type: "delegate".to_string(),
info: json!({ info: json!({
@ -390,7 +419,11 @@ mod test {
}), }),
} }
); );
assert!(parse_stake(&message.instructions[0], &keys[0..5]).is_err()); 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: // This looks wrong, but in an actual compiled instruction, the order is:
// * split account (signer, allocate + assign first) // * split account (signer, allocate + assign first)
@ -399,7 +432,11 @@ mod test {
let instructions = instruction::split(&keys[2], &keys[1], lamports, &keys[0]); let instructions = instruction::split(&keys[2], &keys[1], lamports, &keys[0]);
let message = Message::new(&instructions, None); let message = Message::new(&instructions, None);
assert_eq!( assert_eq!(
parse_stake(&message.instructions[2], &keys[0..3]).unwrap(), parse_stake(
&message.instructions[2],
&AccountKeys::new(&keys[0..3], None)
)
.unwrap(),
ParsedInstructionEnum { ParsedInstructionEnum {
instruction_type: "split".to_string(), instruction_type: "split".to_string(),
info: json!({ info: json!({
@ -410,12 +447,20 @@ mod test {
}), }),
} }
); );
assert!(parse_stake(&message.instructions[2], &keys[0..2]).is_err()); 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 instruction = instruction::withdraw(&keys[1], &keys[0], &keys[2], lamports, None);
let message = Message::new(&[instruction], None); let message = Message::new(&[instruction], None);
assert_eq!( assert_eq!(
parse_stake(&message.instructions[0], &keys[0..5]).unwrap(), parse_stake(
&message.instructions[0],
&AccountKeys::new(&keys[0..5], None)
)
.unwrap(),
ParsedInstructionEnum { ParsedInstructionEnum {
instruction_type: "withdraw".to_string(), instruction_type: "withdraw".to_string(),
info: json!({ info: json!({
@ -432,7 +477,11 @@ mod test {
instruction::withdraw(&keys[2], &keys[0], &keys[3], lamports, Some(&keys[1])); instruction::withdraw(&keys[2], &keys[0], &keys[3], lamports, Some(&keys[1]));
let message = Message::new(&[instruction], None); let message = Message::new(&[instruction], None);
assert_eq!( assert_eq!(
parse_stake(&message.instructions[0], &keys[0..6]).unwrap(), parse_stake(
&message.instructions[0],
&AccountKeys::new(&keys[0..6], None)
)
.unwrap(),
ParsedInstructionEnum { ParsedInstructionEnum {
instruction_type: "withdraw".to_string(), instruction_type: "withdraw".to_string(),
info: json!({ info: json!({
@ -446,12 +495,20 @@ mod test {
}), }),
} }
); );
assert!(parse_stake(&message.instructions[0], &keys[0..4]).is_err()); assert!(parse_stake(
&message.instructions[0],
&AccountKeys::new(&keys[0..4], None)
)
.is_err());
let instruction = instruction::deactivate_stake(&keys[1], &keys[0]); let instruction = instruction::deactivate_stake(&keys[1], &keys[0]);
let message = Message::new(&[instruction], None); let message = Message::new(&[instruction], None);
assert_eq!( assert_eq!(
parse_stake(&message.instructions[0], &keys[0..3]).unwrap(), parse_stake(
&message.instructions[0],
&AccountKeys::new(&keys[0..3], None)
)
.unwrap(),
ParsedInstructionEnum { ParsedInstructionEnum {
instruction_type: "deactivate".to_string(), instruction_type: "deactivate".to_string(),
info: json!({ info: json!({
@ -461,12 +518,20 @@ mod test {
}), }),
} }
); );
assert!(parse_stake(&message.instructions[0], &keys[0..2]).is_err()); 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 instructions = instruction::merge(&keys[1], &keys[0], &keys[2]);
let message = Message::new(&instructions, None); let message = Message::new(&instructions, None);
assert_eq!( assert_eq!(
parse_stake(&message.instructions[0], &keys[0..5]).unwrap(), parse_stake(
&message.instructions[0],
&AccountKeys::new(&keys[0..5], None)
)
.unwrap(),
ParsedInstructionEnum { ParsedInstructionEnum {
instruction_type: "merge".to_string(), instruction_type: "merge".to_string(),
info: json!({ info: json!({
@ -478,7 +543,11 @@ mod test {
}), }),
} }
); );
assert!(parse_stake(&message.instructions[0], &keys[0..4]).is_err()); assert!(parse_stake(
&message.instructions[0],
&AccountKeys::new(&keys[0..4], None)
)
.is_err());
let seed = "test_seed"; let seed = "test_seed";
let instruction = instruction::authorize_with_seed( let instruction = instruction::authorize_with_seed(
@ -492,7 +561,11 @@ mod test {
); );
let message = Message::new(&[instruction], None); let message = Message::new(&[instruction], None);
assert_eq!( assert_eq!(
parse_stake(&message.instructions[0], &keys[0..3]).unwrap(), parse_stake(
&message.instructions[0],
&AccountKeys::new(&keys[0..3], None)
)
.unwrap(),
ParsedInstructionEnum { ParsedInstructionEnum {
instruction_type: "authorizeWithSeed".to_string(), instruction_type: "authorizeWithSeed".to_string(),
info: json!({ info: json!({
@ -506,7 +579,11 @@ mod test {
}), }),
} }
); );
assert!(parse_stake(&message.instructions[0], &keys[0..2]).is_err()); assert!(parse_stake(
&message.instructions[0],
&AccountKeys::new(&keys[0..2], None)
)
.is_err());
let instruction = instruction::authorize_with_seed( let instruction = instruction::authorize_with_seed(
&keys[2], &keys[2],
@ -519,7 +596,11 @@ mod test {
); );
let message = Message::new(&[instruction], None); let message = Message::new(&[instruction], None);
assert_eq!( assert_eq!(
parse_stake(&message.instructions[0], &keys[0..4]).unwrap(), parse_stake(
&message.instructions[0],
&AccountKeys::new(&keys[0..4], None)
)
.unwrap(),
ParsedInstructionEnum { ParsedInstructionEnum {
instruction_type: "authorizeWithSeed".to_string(), instruction_type: "authorizeWithSeed".to_string(),
info: json!({ info: json!({
@ -534,7 +615,11 @@ mod test {
}), }),
} }
); );
assert!(parse_stake(&message.instructions[0], &keys[0..3]).is_err()); assert!(parse_stake(
&message.instructions[0],
&AccountKeys::new(&keys[0..3], None)
)
.is_err());
} }
#[test] #[test]
@ -556,7 +641,11 @@ mod test {
let instruction = instruction::set_lockup(&keys[1], &lockup, &keys[0]); let instruction = instruction::set_lockup(&keys[1], &lockup, &keys[0]);
let message = Message::new(&[instruction], None); let message = Message::new(&[instruction], None);
assert_eq!( assert_eq!(
parse_stake(&message.instructions[0], &keys[0..2]).unwrap(), parse_stake(
&message.instructions[0],
&AccountKeys::new(&keys[0..2], None)
)
.unwrap(),
ParsedInstructionEnum { ParsedInstructionEnum {
instruction_type: "setLockup".to_string(), instruction_type: "setLockup".to_string(),
info: json!({ info: json!({
@ -577,7 +666,11 @@ mod test {
let instruction = instruction::set_lockup(&keys[1], &lockup, &keys[0]); let instruction = instruction::set_lockup(&keys[1], &lockup, &keys[0]);
let message = Message::new(&[instruction], None); let message = Message::new(&[instruction], None);
assert_eq!( assert_eq!(
parse_stake(&message.instructions[0], &keys[0..2]).unwrap(), parse_stake(
&message.instructions[0],
&AccountKeys::new(&keys[0..2], None)
)
.unwrap(),
ParsedInstructionEnum { ParsedInstructionEnum {
instruction_type: "setLockup".to_string(), instruction_type: "setLockup".to_string(),
info: json!({ info: json!({
@ -599,7 +692,11 @@ mod test {
let instruction = instruction::set_lockup(&keys[1], &lockup, &keys[0]); let instruction = instruction::set_lockup(&keys[1], &lockup, &keys[0]);
let message = Message::new(&[instruction], None); let message = Message::new(&[instruction], None);
assert_eq!( assert_eq!(
parse_stake(&message.instructions[0], &keys[0..2]).unwrap(), parse_stake(
&message.instructions[0],
&AccountKeys::new(&keys[0..2], None)
)
.unwrap(),
ParsedInstructionEnum { ParsedInstructionEnum {
instruction_type: "setLockup".to_string(), instruction_type: "setLockup".to_string(),
info: json!({ info: json!({
@ -614,7 +711,11 @@ mod test {
} }
); );
assert!(parse_stake(&message.instructions[0], &keys[0..1]).is_err()); assert!(parse_stake(
&message.instructions[0],
&AccountKeys::new(&keys[0..1], None)
)
.is_err());
let lockup = LockupArgs { let lockup = LockupArgs {
unix_timestamp: Some(unix_timestamp), unix_timestamp: Some(unix_timestamp),
@ -624,7 +725,11 @@ mod test {
let instruction = instruction::set_lockup_checked(&keys[1], &lockup, &keys[0]); let instruction = instruction::set_lockup_checked(&keys[1], &lockup, &keys[0]);
let message = Message::new(&[instruction], None); let message = Message::new(&[instruction], None);
assert_eq!( assert_eq!(
parse_stake(&message.instructions[0], &keys[0..2]).unwrap(), parse_stake(
&message.instructions[0],
&AccountKeys::new(&keys[0..2], None)
)
.unwrap(),
ParsedInstructionEnum { ParsedInstructionEnum {
instruction_type: "setLockupChecked".to_string(), instruction_type: "setLockupChecked".to_string(),
info: json!({ info: json!({
@ -645,7 +750,11 @@ mod test {
let instruction = instruction::set_lockup_checked(&keys[1], &lockup, &keys[0]); let instruction = instruction::set_lockup_checked(&keys[1], &lockup, &keys[0]);
let message = Message::new(&[instruction], None); let message = Message::new(&[instruction], None);
assert_eq!( assert_eq!(
parse_stake(&message.instructions[0], &keys[0..2]).unwrap(), parse_stake(
&message.instructions[0],
&AccountKeys::new(&keys[0..2], None)
)
.unwrap(),
ParsedInstructionEnum { ParsedInstructionEnum {
instruction_type: "setLockupChecked".to_string(), instruction_type: "setLockupChecked".to_string(),
info: json!({ info: json!({
@ -658,7 +767,11 @@ mod test {
}), }),
} }
); );
assert!(parse_stake(&message.instructions[0], &keys[0..1]).is_err()); assert!(parse_stake(
&message.instructions[0],
&AccountKeys::new(&keys[0..1], None)
)
.is_err());
let lockup = LockupArgs { let lockup = LockupArgs {
unix_timestamp: Some(unix_timestamp), unix_timestamp: Some(unix_timestamp),
@ -668,7 +781,11 @@ mod test {
let instruction = instruction::set_lockup_checked(&keys[2], &lockup, &keys[0]); let instruction = instruction::set_lockup_checked(&keys[2], &lockup, &keys[0]);
let message = Message::new(&[instruction], None); let message = Message::new(&[instruction], None);
assert_eq!( assert_eq!(
parse_stake(&message.instructions[0], &keys[0..3]).unwrap(), parse_stake(
&message.instructions[0],
&AccountKeys::new(&keys[0..3], None)
)
.unwrap(),
ParsedInstructionEnum { ParsedInstructionEnum {
instruction_type: "setLockupChecked".to_string(), instruction_type: "setLockupChecked".to_string(),
info: json!({ info: json!({
@ -682,7 +799,11 @@ mod test {
}), }),
} }
); );
assert!(parse_stake(&message.instructions[0], &keys[0..2]).is_err()); assert!(parse_stake(
&message.instructions[0],
&AccountKeys::new(&keys[0..2], None)
)
.is_err());
} }
#[test] #[test]
@ -703,7 +824,11 @@ mod test {
instruction::create_account_checked(&keys[0], &keys[1], &authorized, lamports); instruction::create_account_checked(&keys[0], &keys[1], &authorized, lamports);
let message = Message::new(&instructions, None); let message = Message::new(&instructions, None);
assert_eq!( assert_eq!(
parse_stake(&message.instructions[1], &keys[0..4]).unwrap(), parse_stake(
&message.instructions[1],
&AccountKeys::new(&keys[0..4], None)
)
.unwrap(),
ParsedInstructionEnum { ParsedInstructionEnum {
instruction_type: "initializeChecked".to_string(), instruction_type: "initializeChecked".to_string(),
info: json!({ info: json!({
@ -714,7 +839,11 @@ mod test {
}), }),
} }
); );
assert!(parse_stake(&message.instructions[1], &keys[0..3]).is_err()); assert!(parse_stake(
&message.instructions[1],
&AccountKeys::new(&keys[0..3], None)
)
.is_err());
let instruction = instruction::authorize_checked( let instruction = instruction::authorize_checked(
&keys[2], &keys[2],
@ -725,7 +854,11 @@ mod test {
); );
let message = Message::new(&[instruction], None); let message = Message::new(&[instruction], None);
assert_eq!( assert_eq!(
parse_stake(&message.instructions[0], &keys[0..4]).unwrap(), parse_stake(
&message.instructions[0],
&AccountKeys::new(&keys[0..4], None)
)
.unwrap(),
ParsedInstructionEnum { ParsedInstructionEnum {
instruction_type: "authorizeChecked".to_string(), instruction_type: "authorizeChecked".to_string(),
info: json!({ info: json!({
@ -737,7 +870,11 @@ mod test {
}), }),
} }
); );
assert!(parse_stake(&message.instructions[0], &keys[0..3]).is_err()); assert!(parse_stake(
&message.instructions[0],
&AccountKeys::new(&keys[0..3], None)
)
.is_err());
let instruction = instruction::authorize_checked( let instruction = instruction::authorize_checked(
&keys[3], &keys[3],
@ -748,7 +885,11 @@ mod test {
); );
let message = Message::new(&[instruction], None); let message = Message::new(&[instruction], None);
assert_eq!( assert_eq!(
parse_stake(&message.instructions[0], &keys[0..5]).unwrap(), parse_stake(
&message.instructions[0],
&AccountKeys::new(&keys[0..5], None)
)
.unwrap(),
ParsedInstructionEnum { ParsedInstructionEnum {
instruction_type: "authorizeChecked".to_string(), instruction_type: "authorizeChecked".to_string(),
info: json!({ info: json!({
@ -761,7 +902,11 @@ mod test {
}), }),
} }
); );
assert!(parse_stake(&message.instructions[0], &keys[0..4]).is_err()); assert!(parse_stake(
&message.instructions[0],
&AccountKeys::new(&keys[0..4], None)
)
.is_err());
let seed = "test_seed"; let seed = "test_seed";
let instruction = instruction::authorize_checked_with_seed( let instruction = instruction::authorize_checked_with_seed(
@ -775,7 +920,11 @@ mod test {
); );
let message = Message::new(&[instruction], None); let message = Message::new(&[instruction], None);
assert_eq!( assert_eq!(
parse_stake(&message.instructions[0], &keys[0..4]).unwrap(), parse_stake(
&message.instructions[0],
&AccountKeys::new(&keys[0..4], None)
)
.unwrap(),
ParsedInstructionEnum { ParsedInstructionEnum {
instruction_type: "authorizeCheckedWithSeed".to_string(), instruction_type: "authorizeCheckedWithSeed".to_string(),
info: json!({ info: json!({
@ -789,7 +938,11 @@ mod test {
}), }),
} }
); );
assert!(parse_stake(&message.instructions[0], &keys[0..3]).is_err()); assert!(parse_stake(
&message.instructions[0],
&AccountKeys::new(&keys[0..3], None)
)
.is_err());
let instruction = instruction::authorize_checked_with_seed( let instruction = instruction::authorize_checked_with_seed(
&keys[3], &keys[3],
@ -802,7 +955,11 @@ mod test {
); );
let message = Message::new(&[instruction], None); let message = Message::new(&[instruction], None);
assert_eq!( assert_eq!(
parse_stake(&message.instructions[0], &keys[0..5]).unwrap(), parse_stake(
&message.instructions[0],
&AccountKeys::new(&keys[0..5], None)
)
.unwrap(),
ParsedInstructionEnum { ParsedInstructionEnum {
instruction_type: "authorizeCheckedWithSeed".to_string(), instruction_type: "authorizeCheckedWithSeed".to_string(),
info: json!({ info: json!({
@ -817,6 +974,10 @@ mod test {
}), }),
} }
); );
assert!(parse_stake(&message.instructions[0], &keys[0..4]).is_err()); assert!(parse_stake(
&message.instructions[0],
&AccountKeys::new(&keys[0..4], None)
)
.is_err());
} }
} }

View File

@ -5,13 +5,14 @@ use {
bincode::deserialize, bincode::deserialize,
serde_json::json, serde_json::json,
solana_sdk::{ solana_sdk::{
instruction::CompiledInstruction, pubkey::Pubkey, system_instruction::SystemInstruction, instruction::CompiledInstruction, message::AccountKeys,
system_instruction::SystemInstruction,
}, },
}; };
pub fn parse_system( pub fn parse_system(
instruction: &CompiledInstruction, instruction: &CompiledInstruction,
account_keys: &[Pubkey], account_keys: &AccountKeys,
) -> Result<ParsedInstructionEnum, ParseInstructionError> { ) -> Result<ParsedInstructionEnum, ParseInstructionError> {
let system_instruction: SystemInstruction = deserialize(&instruction.data) let system_instruction: SystemInstruction = deserialize(&instruction.data)
.map_err(|_| ParseInstructionError::InstructionNotParsable(ParsableProgram::System))?; .map_err(|_| ParseInstructionError::InstructionNotParsable(ParsableProgram::System))?;
@ -219,7 +220,11 @@ mod test {
system_instruction::create_account(&keys[0], &keys[1], lamports, space, &keys[2]); system_instruction::create_account(&keys[0], &keys[1], lamports, space, &keys[2]);
let message = Message::new(&[instruction], None); let message = Message::new(&[instruction], None);
assert_eq!( assert_eq!(
parse_system(&message.instructions[0], &keys[0..2]).unwrap(), parse_system(
&message.instructions[0],
&AccountKeys::new(&keys[0..2], None)
)
.unwrap(),
ParsedInstructionEnum { ParsedInstructionEnum {
instruction_type: "createAccount".to_string(), instruction_type: "createAccount".to_string(),
info: json!({ info: json!({
@ -231,12 +236,20 @@ mod test {
}), }),
} }
); );
assert!(parse_system(&message.instructions[0], &keys[0..1]).is_err()); assert!(parse_system(
&message.instructions[0],
&AccountKeys::new(&keys[0..1], None)
)
.is_err());
let instruction = system_instruction::assign(&keys[0], &keys[1]); let instruction = system_instruction::assign(&keys[0], &keys[1]);
let message = Message::new(&[instruction], None); let message = Message::new(&[instruction], None);
assert_eq!( assert_eq!(
parse_system(&message.instructions[0], &keys[0..1]).unwrap(), parse_system(
&message.instructions[0],
&AccountKeys::new(&keys[0..1], None)
)
.unwrap(),
ParsedInstructionEnum { ParsedInstructionEnum {
instruction_type: "assign".to_string(), instruction_type: "assign".to_string(),
info: json!({ info: json!({
@ -245,12 +258,16 @@ mod test {
}), }),
} }
); );
assert!(parse_system(&message.instructions[0], &[]).is_err()); assert!(parse_system(&message.instructions[0], &AccountKeys::new(&[], None)).is_err());
let instruction = system_instruction::transfer(&keys[0], &keys[1], lamports); let instruction = system_instruction::transfer(&keys[0], &keys[1], lamports);
let message = Message::new(&[instruction], None); let message = Message::new(&[instruction], None);
assert_eq!( assert_eq!(
parse_system(&message.instructions[0], &keys[0..2]).unwrap(), parse_system(
&message.instructions[0],
&AccountKeys::new(&keys[0..2], None)
)
.unwrap(),
ParsedInstructionEnum { ParsedInstructionEnum {
instruction_type: "transfer".to_string(), instruction_type: "transfer".to_string(),
info: json!({ info: json!({
@ -260,7 +277,11 @@ mod test {
}), }),
} }
); );
assert!(parse_system(&message.instructions[0], &keys[0..1]).is_err()); assert!(parse_system(
&message.instructions[0],
&AccountKeys::new(&keys[0..1], None)
)
.is_err());
let seed = "test_seed"; let seed = "test_seed";
let instruction = system_instruction::create_account_with_seed( let instruction = system_instruction::create_account_with_seed(
@ -268,7 +289,11 @@ mod test {
); );
let message = Message::new(&[instruction], None); let message = Message::new(&[instruction], None);
assert_eq!( assert_eq!(
parse_system(&message.instructions[0], &keys[0..3]).unwrap(), parse_system(
&message.instructions[0],
&AccountKeys::new(&keys[0..3], None)
)
.unwrap(),
ParsedInstructionEnum { ParsedInstructionEnum {
instruction_type: "createAccountWithSeed".to_string(), instruction_type: "createAccountWithSeed".to_string(),
info: json!({ info: json!({
@ -289,7 +314,11 @@ mod test {
); );
let message = Message::new(&[instruction], None); let message = Message::new(&[instruction], None);
assert_eq!( assert_eq!(
parse_system(&message.instructions[0], &keys[0..2]).unwrap(), parse_system(
&message.instructions[0],
&AccountKeys::new(&keys[0..2], None)
)
.unwrap(),
ParsedInstructionEnum { ParsedInstructionEnum {
instruction_type: "createAccountWithSeed".to_string(), instruction_type: "createAccountWithSeed".to_string(),
info: json!({ info: json!({
@ -303,12 +332,20 @@ mod test {
}), }),
} }
); );
assert!(parse_system(&message.instructions[0], &keys[0..1]).is_err()); assert!(parse_system(
&message.instructions[0],
&AccountKeys::new(&keys[0..1], None)
)
.is_err());
let instruction = system_instruction::allocate(&keys[0], space); let instruction = system_instruction::allocate(&keys[0], space);
let message = Message::new(&[instruction], None); let message = Message::new(&[instruction], None);
assert_eq!( assert_eq!(
parse_system(&message.instructions[0], &keys[0..1]).unwrap(), parse_system(
&message.instructions[0],
&AccountKeys::new(&keys[0..1], None)
)
.unwrap(),
ParsedInstructionEnum { ParsedInstructionEnum {
instruction_type: "allocate".to_string(), instruction_type: "allocate".to_string(),
info: json!({ info: json!({
@ -317,13 +354,17 @@ mod test {
}), }),
} }
); );
assert!(parse_system(&message.instructions[0], &[]).is_err()); assert!(parse_system(&message.instructions[0], &AccountKeys::new(&[], None)).is_err());
let instruction = let instruction =
system_instruction::allocate_with_seed(&keys[1], &keys[0], seed, space, &keys[2]); system_instruction::allocate_with_seed(&keys[1], &keys[0], seed, space, &keys[2]);
let message = Message::new(&[instruction], None); let message = Message::new(&[instruction], None);
assert_eq!( assert_eq!(
parse_system(&message.instructions[0], &keys[0..2]).unwrap(), parse_system(
&message.instructions[0],
&AccountKeys::new(&keys[0..2], None)
)
.unwrap(),
ParsedInstructionEnum { ParsedInstructionEnum {
instruction_type: "allocateWithSeed".to_string(), instruction_type: "allocateWithSeed".to_string(),
info: json!({ info: json!({
@ -335,12 +376,20 @@ mod test {
}), }),
} }
); );
assert!(parse_system(&message.instructions[0], &keys[0..1]).is_err()); assert!(parse_system(
&message.instructions[0],
&AccountKeys::new(&keys[0..1], None)
)
.is_err());
let instruction = system_instruction::assign_with_seed(&keys[1], &keys[0], seed, &keys[2]); let instruction = system_instruction::assign_with_seed(&keys[1], &keys[0], seed, &keys[2]);
let message = Message::new(&[instruction], None); let message = Message::new(&[instruction], None);
assert_eq!( assert_eq!(
parse_system(&message.instructions[0], &keys[0..2]).unwrap(), parse_system(
&message.instructions[0],
&AccountKeys::new(&keys[0..2], None)
)
.unwrap(),
ParsedInstructionEnum { ParsedInstructionEnum {
instruction_type: "assignWithSeed".to_string(), instruction_type: "assignWithSeed".to_string(),
info: json!({ info: json!({
@ -351,7 +400,11 @@ mod test {
}), }),
} }
); );
assert!(parse_system(&message.instructions[0], &keys[0..1]).is_err()); assert!(parse_system(
&message.instructions[0],
&AccountKeys::new(&keys[0..1], None)
)
.is_err());
let instruction = system_instruction::transfer_with_seed( let instruction = system_instruction::transfer_with_seed(
&keys[1], &keys[1],
@ -363,7 +416,11 @@ mod test {
); );
let message = Message::new(&[instruction], None); let message = Message::new(&[instruction], None);
assert_eq!( assert_eq!(
parse_system(&message.instructions[0], &keys[0..3]).unwrap(), parse_system(
&message.instructions[0],
&AccountKeys::new(&keys[0..3], None)
)
.unwrap(),
ParsedInstructionEnum { ParsedInstructionEnum {
instruction_type: "transferWithSeed".to_string(), instruction_type: "transferWithSeed".to_string(),
info: json!({ info: json!({
@ -376,7 +433,11 @@ mod test {
}), }),
} }
); );
assert!(parse_system(&message.instructions[0], &keys[0..2]).is_err()); assert!(parse_system(
&message.instructions[0],
&AccountKeys::new(&keys[0..2], None)
)
.is_err());
} }
#[test] #[test]
@ -390,7 +451,11 @@ mod test {
let instruction = system_instruction::advance_nonce_account(&keys[1], &keys[0]); let instruction = system_instruction::advance_nonce_account(&keys[1], &keys[0]);
let message = Message::new(&[instruction], None); let message = Message::new(&[instruction], None);
assert_eq!( assert_eq!(
parse_system(&message.instructions[0], &keys[0..3]).unwrap(), parse_system(
&message.instructions[0],
&AccountKeys::new(&keys[0..3], None)
)
.unwrap(),
ParsedInstructionEnum { ParsedInstructionEnum {
instruction_type: "advanceNonce".to_string(), instruction_type: "advanceNonce".to_string(),
info: json!({ info: json!({
@ -400,14 +465,22 @@ mod test {
}), }),
} }
); );
assert!(parse_system(&message.instructions[0], &keys[0..2]).is_err()); assert!(parse_system(
&message.instructions[0],
&AccountKeys::new(&keys[0..2], None)
)
.is_err());
let lamports = 55; let lamports = 55;
let instruction = let instruction =
system_instruction::withdraw_nonce_account(&keys[1], &keys[0], &keys[2], lamports); system_instruction::withdraw_nonce_account(&keys[1], &keys[0], &keys[2], lamports);
let message = Message::new(&[instruction], None); let message = Message::new(&[instruction], None);
assert_eq!( assert_eq!(
parse_system(&message.instructions[0], &keys[0..5]).unwrap(), parse_system(
&message.instructions[0],
&AccountKeys::new(&keys[0..5], None)
)
.unwrap(),
ParsedInstructionEnum { ParsedInstructionEnum {
instruction_type: "withdrawFromNonce".to_string(), instruction_type: "withdrawFromNonce".to_string(),
info: json!({ info: json!({
@ -420,13 +493,21 @@ mod test {
}), }),
} }
); );
assert!(parse_system(&message.instructions[0], &keys[0..4]).is_err()); assert!(parse_system(
&message.instructions[0],
&AccountKeys::new(&keys[0..4], None)
)
.is_err());
let instructions = let instructions =
system_instruction::create_nonce_account(&keys[0], &keys[1], &keys[4], lamports); system_instruction::create_nonce_account(&keys[0], &keys[1], &keys[4], lamports);
let message = Message::new(&instructions, None); let message = Message::new(&instructions, None);
assert_eq!( assert_eq!(
parse_system(&message.instructions[1], &keys[0..4]).unwrap(), parse_system(
&message.instructions[1],
&AccountKeys::new(&keys[0..4], None)
)
.unwrap(),
ParsedInstructionEnum { ParsedInstructionEnum {
instruction_type: "initializeNonce".to_string(), instruction_type: "initializeNonce".to_string(),
info: json!({ info: json!({
@ -437,12 +518,20 @@ mod test {
}), }),
} }
); );
assert!(parse_system(&message.instructions[1], &keys[0..3]).is_err()); assert!(parse_system(
&message.instructions[1],
&AccountKeys::new(&keys[0..3], None)
)
.is_err());
let instruction = system_instruction::authorize_nonce_account(&keys[1], &keys[0], &keys[2]); let instruction = system_instruction::authorize_nonce_account(&keys[1], &keys[0], &keys[2]);
let message = Message::new(&[instruction], None); let message = Message::new(&[instruction], None);
assert_eq!( assert_eq!(
parse_system(&message.instructions[0], &keys[0..2]).unwrap(), parse_system(
&message.instructions[0],
&AccountKeys::new(&keys[0..2], None)
)
.unwrap(),
ParsedInstructionEnum { ParsedInstructionEnum {
instruction_type: "authorizeNonce".to_string(), instruction_type: "authorizeNonce".to_string(),
info: json!({ info: json!({
@ -452,6 +541,10 @@ mod test {
}), }),
} }
); );
assert!(parse_system(&message.instructions[0], &keys[0..1]).is_err()); assert!(parse_system(
&message.instructions[0],
&AccountKeys::new(&keys[0..1], None)
)
.is_err());
} }
} }

View File

@ -6,7 +6,7 @@ use {
solana_account_decoder::parse_token::{pubkey_from_spl_token, token_amount_to_ui_amount}, solana_account_decoder::parse_token::{pubkey_from_spl_token, token_amount_to_ui_amount},
solana_sdk::{ solana_sdk::{
instruction::{AccountMeta, CompiledInstruction, Instruction}, instruction::{AccountMeta, CompiledInstruction, Instruction},
pubkey::Pubkey, message::AccountKeys,
}, },
spl_token::{ spl_token::{
instruction::{AuthorityType, TokenInstruction}, instruction::{AuthorityType, TokenInstruction},
@ -18,7 +18,7 @@ use {
pub fn parse_token( pub fn parse_token(
instruction: &CompiledInstruction, instruction: &CompiledInstruction,
account_keys: &[Pubkey], account_keys: &AccountKeys,
) -> Result<ParsedInstructionEnum, ParseInstructionError> { ) -> Result<ParsedInstructionEnum, ParseInstructionError> {
let token_instruction = TokenInstruction::unpack(&instruction.data) let token_instruction = TokenInstruction::unpack(&instruction.data)
.map_err(|_| ParseInstructionError::InstructionNotParsable(ParsableProgram::SplToken))?; .map_err(|_| ParseInstructionError::InstructionNotParsable(ParsableProgram::SplToken))?;
@ -411,7 +411,7 @@ impl From<AuthorityType> for UiAuthorityType {
fn parse_signers( fn parse_signers(
map: &mut Map<String, Value>, map: &mut Map<String, Value>,
last_nonsigner_index: usize, last_nonsigner_index: usize,
account_keys: &[Pubkey], account_keys: &AccountKeys,
accounts: &[u8], accounts: &[u8],
owner_field_name: &str, owner_field_name: &str,
multisig_field_name: &str, multisig_field_name: &str,
@ -458,7 +458,7 @@ pub fn spl_token_instruction(instruction: SplTokenInstruction) -> Instruction {
mod test { mod test {
use { use {
super::*, super::*,
solana_sdk::instruction::CompiledInstruction, solana_sdk::{instruction::CompiledInstruction, pubkey::Pubkey},
spl_token::{ spl_token::{
instruction::*, instruction::*,
solana_program::{ solana_program::{
@ -503,7 +503,7 @@ mod test {
let message = Message::new(&[initialize_mint_ix], None); let message = Message::new(&[initialize_mint_ix], None);
let compiled_instruction = convert_compiled_instruction(&message.instructions[0]); let compiled_instruction = convert_compiled_instruction(&message.instructions[0]);
assert_eq!( assert_eq!(
parse_token(&compiled_instruction, &keys).unwrap(), parse_token(&compiled_instruction, &AccountKeys::new(&keys, None)).unwrap(),
ParsedInstructionEnum { ParsedInstructionEnum {
instruction_type: "initializeMint".to_string(), instruction_type: "initializeMint".to_string(),
info: json!({ info: json!({
@ -527,7 +527,7 @@ mod test {
let message = Message::new(&[initialize_mint_ix], None); let message = Message::new(&[initialize_mint_ix], None);
let compiled_instruction = convert_compiled_instruction(&message.instructions[0]); let compiled_instruction = convert_compiled_instruction(&message.instructions[0]);
assert_eq!( assert_eq!(
parse_token(&compiled_instruction, &keys).unwrap(), parse_token(&compiled_instruction, &AccountKeys::new(&keys, None)).unwrap(),
ParsedInstructionEnum { ParsedInstructionEnum {
instruction_type: "initializeMint".to_string(), instruction_type: "initializeMint".to_string(),
info: json!({ info: json!({
@ -550,7 +550,7 @@ mod test {
let message = Message::new(&[initialize_account_ix], None); let message = Message::new(&[initialize_account_ix], None);
let compiled_instruction = convert_compiled_instruction(&message.instructions[0]); let compiled_instruction = convert_compiled_instruction(&message.instructions[0]);
assert_eq!( assert_eq!(
parse_token(&compiled_instruction, &keys).unwrap(), parse_token(&compiled_instruction, &AccountKeys::new(&keys, None)).unwrap(),
ParsedInstructionEnum { ParsedInstructionEnum {
instruction_type: "initializeAccount".to_string(), instruction_type: "initializeAccount".to_string(),
info: json!({ info: json!({
@ -577,7 +577,7 @@ mod test {
let message = Message::new(&[initialize_multisig_ix], None); let message = Message::new(&[initialize_multisig_ix], None);
let compiled_instruction = convert_compiled_instruction(&message.instructions[0]); let compiled_instruction = convert_compiled_instruction(&message.instructions[0]);
assert_eq!( assert_eq!(
parse_token(&compiled_instruction, &keys).unwrap(), parse_token(&compiled_instruction, &AccountKeys::new(&keys, None)).unwrap(),
ParsedInstructionEnum { ParsedInstructionEnum {
instruction_type: "initializeMultisig".to_string(), instruction_type: "initializeMultisig".to_string(),
info: json!({ info: json!({
@ -602,7 +602,7 @@ mod test {
let message = Message::new(&[transfer_ix], None); let message = Message::new(&[transfer_ix], None);
let compiled_instruction = convert_compiled_instruction(&message.instructions[0]); let compiled_instruction = convert_compiled_instruction(&message.instructions[0]);
assert_eq!( assert_eq!(
parse_token(&compiled_instruction, &keys).unwrap(), parse_token(&compiled_instruction, &AccountKeys::new(&keys, None)).unwrap(),
ParsedInstructionEnum { ParsedInstructionEnum {
instruction_type: "transfer".to_string(), instruction_type: "transfer".to_string(),
info: json!({ info: json!({
@ -626,7 +626,7 @@ mod test {
let message = Message::new(&[transfer_ix], None); let message = Message::new(&[transfer_ix], None);
let compiled_instruction = convert_compiled_instruction(&message.instructions[0]); let compiled_instruction = convert_compiled_instruction(&message.instructions[0]);
assert_eq!( assert_eq!(
parse_token(&compiled_instruction, &keys).unwrap(), parse_token(&compiled_instruction, &AccountKeys::new(&keys, None)).unwrap(),
ParsedInstructionEnum { ParsedInstructionEnum {
instruction_type: "transfer".to_string(), instruction_type: "transfer".to_string(),
info: json!({ info: json!({
@ -652,7 +652,7 @@ mod test {
let message = Message::new(&[approve_ix], None); let message = Message::new(&[approve_ix], None);
let compiled_instruction = convert_compiled_instruction(&message.instructions[0]); let compiled_instruction = convert_compiled_instruction(&message.instructions[0]);
assert_eq!( assert_eq!(
parse_token(&compiled_instruction, &keys).unwrap(), parse_token(&compiled_instruction, &AccountKeys::new(&keys, None)).unwrap(),
ParsedInstructionEnum { ParsedInstructionEnum {
instruction_type: "approve".to_string(), instruction_type: "approve".to_string(),
info: json!({ info: json!({
@ -676,7 +676,7 @@ mod test {
let message = Message::new(&[approve_ix], None); let message = Message::new(&[approve_ix], None);
let compiled_instruction = convert_compiled_instruction(&message.instructions[0]); let compiled_instruction = convert_compiled_instruction(&message.instructions[0]);
assert_eq!( assert_eq!(
parse_token(&compiled_instruction, &keys).unwrap(), parse_token(&compiled_instruction, &AccountKeys::new(&keys, None)).unwrap(),
ParsedInstructionEnum { ParsedInstructionEnum {
instruction_type: "approve".to_string(), instruction_type: "approve".to_string(),
info: json!({ info: json!({
@ -700,7 +700,7 @@ mod test {
let message = Message::new(&[revoke_ix], None); let message = Message::new(&[revoke_ix], None);
let compiled_instruction = convert_compiled_instruction(&message.instructions[0]); let compiled_instruction = convert_compiled_instruction(&message.instructions[0]);
assert_eq!( assert_eq!(
parse_token(&compiled_instruction, &keys).unwrap(), parse_token(&compiled_instruction, &AccountKeys::new(&keys, None)).unwrap(),
ParsedInstructionEnum { ParsedInstructionEnum {
instruction_type: "revoke".to_string(), instruction_type: "revoke".to_string(),
info: json!({ info: json!({
@ -723,7 +723,7 @@ mod test {
let message = Message::new(&[set_authority_ix], None); let message = Message::new(&[set_authority_ix], None);
let compiled_instruction = convert_compiled_instruction(&message.instructions[0]); let compiled_instruction = convert_compiled_instruction(&message.instructions[0]);
assert_eq!( assert_eq!(
parse_token(&compiled_instruction, &keys).unwrap(), parse_token(&compiled_instruction, &AccountKeys::new(&keys, None)).unwrap(),
ParsedInstructionEnum { ParsedInstructionEnum {
instruction_type: "setAuthority".to_string(), instruction_type: "setAuthority".to_string(),
info: json!({ info: json!({
@ -748,7 +748,7 @@ mod test {
let compiled_instruction = convert_compiled_instruction(&message.instructions[0]); let compiled_instruction = convert_compiled_instruction(&message.instructions[0]);
let new_authority: Option<String> = None; let new_authority: Option<String> = None;
assert_eq!( assert_eq!(
parse_token(&compiled_instruction, &keys).unwrap(), parse_token(&compiled_instruction, &AccountKeys::new(&keys, None)).unwrap(),
ParsedInstructionEnum { ParsedInstructionEnum {
instruction_type: "setAuthority".to_string(), instruction_type: "setAuthority".to_string(),
info: json!({ info: json!({
@ -773,7 +773,7 @@ mod test {
let message = Message::new(&[mint_to_ix], None); let message = Message::new(&[mint_to_ix], None);
let compiled_instruction = convert_compiled_instruction(&message.instructions[0]); let compiled_instruction = convert_compiled_instruction(&message.instructions[0]);
assert_eq!( assert_eq!(
parse_token(&compiled_instruction, &keys).unwrap(), parse_token(&compiled_instruction, &AccountKeys::new(&keys, None)).unwrap(),
ParsedInstructionEnum { ParsedInstructionEnum {
instruction_type: "mintTo".to_string(), instruction_type: "mintTo".to_string(),
info: json!({ info: json!({
@ -798,7 +798,7 @@ mod test {
let message = Message::new(&[burn_ix], None); let message = Message::new(&[burn_ix], None);
let compiled_instruction = convert_compiled_instruction(&message.instructions[0]); let compiled_instruction = convert_compiled_instruction(&message.instructions[0]);
assert_eq!( assert_eq!(
parse_token(&compiled_instruction, &keys).unwrap(), parse_token(&compiled_instruction, &AccountKeys::new(&keys, None)).unwrap(),
ParsedInstructionEnum { ParsedInstructionEnum {
instruction_type: "burn".to_string(), instruction_type: "burn".to_string(),
info: json!({ info: json!({
@ -822,7 +822,7 @@ mod test {
let message = Message::new(&[close_account_ix], None); let message = Message::new(&[close_account_ix], None);
let compiled_instruction = convert_compiled_instruction(&message.instructions[0]); let compiled_instruction = convert_compiled_instruction(&message.instructions[0]);
assert_eq!( assert_eq!(
parse_token(&compiled_instruction, &keys).unwrap(), parse_token(&compiled_instruction, &AccountKeys::new(&keys, None)).unwrap(),
ParsedInstructionEnum { ParsedInstructionEnum {
instruction_type: "closeAccount".to_string(), instruction_type: "closeAccount".to_string(),
info: json!({ info: json!({
@ -845,7 +845,7 @@ mod test {
let message = Message::new(&[freeze_account_ix], None); let message = Message::new(&[freeze_account_ix], None);
let compiled_instruction = convert_compiled_instruction(&message.instructions[0]); let compiled_instruction = convert_compiled_instruction(&message.instructions[0]);
assert_eq!( assert_eq!(
parse_token(&compiled_instruction, &keys).unwrap(), parse_token(&compiled_instruction, &AccountKeys::new(&keys, None)).unwrap(),
ParsedInstructionEnum { ParsedInstructionEnum {
instruction_type: "freezeAccount".to_string(), instruction_type: "freezeAccount".to_string(),
info: json!({ info: json!({
@ -868,7 +868,7 @@ mod test {
let message = Message::new(&[thaw_account_ix], None); let message = Message::new(&[thaw_account_ix], None);
let compiled_instruction = convert_compiled_instruction(&message.instructions[0]); let compiled_instruction = convert_compiled_instruction(&message.instructions[0]);
assert_eq!( assert_eq!(
parse_token(&compiled_instruction, &keys).unwrap(), parse_token(&compiled_instruction, &AccountKeys::new(&keys, None)).unwrap(),
ParsedInstructionEnum { ParsedInstructionEnum {
instruction_type: "thawAccount".to_string(), instruction_type: "thawAccount".to_string(),
info: json!({ info: json!({
@ -894,7 +894,7 @@ mod test {
let message = Message::new(&[transfer_ix], None); let message = Message::new(&[transfer_ix], None);
let compiled_instruction = convert_compiled_instruction(&message.instructions[0]); let compiled_instruction = convert_compiled_instruction(&message.instructions[0]);
assert_eq!( assert_eq!(
parse_token(&compiled_instruction, &keys).unwrap(), parse_token(&compiled_instruction, &AccountKeys::new(&keys, None)).unwrap(),
ParsedInstructionEnum { ParsedInstructionEnum {
instruction_type: "transferChecked".to_string(), instruction_type: "transferChecked".to_string(),
info: json!({ info: json!({
@ -926,7 +926,7 @@ mod test {
let message = Message::new(&[transfer_ix], None); let message = Message::new(&[transfer_ix], None);
let compiled_instruction = convert_compiled_instruction(&message.instructions[0]); let compiled_instruction = convert_compiled_instruction(&message.instructions[0]);
assert_eq!( assert_eq!(
parse_token(&compiled_instruction, &keys).unwrap(), parse_token(&compiled_instruction, &AccountKeys::new(&keys, None)).unwrap(),
ParsedInstructionEnum { ParsedInstructionEnum {
instruction_type: "transferChecked".to_string(), instruction_type: "transferChecked".to_string(),
info: json!({ info: json!({
@ -960,7 +960,7 @@ mod test {
let message = Message::new(&[approve_ix], None); let message = Message::new(&[approve_ix], None);
let compiled_instruction = convert_compiled_instruction(&message.instructions[0]); let compiled_instruction = convert_compiled_instruction(&message.instructions[0]);
assert_eq!( assert_eq!(
parse_token(&compiled_instruction, &keys).unwrap(), parse_token(&compiled_instruction, &AccountKeys::new(&keys, None)).unwrap(),
ParsedInstructionEnum { ParsedInstructionEnum {
instruction_type: "approveChecked".to_string(), instruction_type: "approveChecked".to_string(),
info: json!({ info: json!({
@ -992,7 +992,7 @@ mod test {
let message = Message::new(&[approve_ix], None); let message = Message::new(&[approve_ix], None);
let compiled_instruction = convert_compiled_instruction(&message.instructions[0]); let compiled_instruction = convert_compiled_instruction(&message.instructions[0]);
assert_eq!( assert_eq!(
parse_token(&compiled_instruction, &keys).unwrap(), parse_token(&compiled_instruction, &AccountKeys::new(&keys, None)).unwrap(),
ParsedInstructionEnum { ParsedInstructionEnum {
instruction_type: "approveChecked".to_string(), instruction_type: "approveChecked".to_string(),
info: json!({ info: json!({
@ -1025,7 +1025,7 @@ mod test {
let message = Message::new(&[mint_to_ix], None); let message = Message::new(&[mint_to_ix], None);
let compiled_instruction = convert_compiled_instruction(&message.instructions[0]); let compiled_instruction = convert_compiled_instruction(&message.instructions[0]);
assert_eq!( assert_eq!(
parse_token(&compiled_instruction, &keys).unwrap(), parse_token(&compiled_instruction, &AccountKeys::new(&keys, None)).unwrap(),
ParsedInstructionEnum { ParsedInstructionEnum {
instruction_type: "mintToChecked".to_string(), instruction_type: "mintToChecked".to_string(),
info: json!({ info: json!({
@ -1056,7 +1056,7 @@ mod test {
let message = Message::new(&[burn_ix], None); let message = Message::new(&[burn_ix], None);
let compiled_instruction = convert_compiled_instruction(&message.instructions[0]); let compiled_instruction = convert_compiled_instruction(&message.instructions[0]);
assert_eq!( assert_eq!(
parse_token(&compiled_instruction, &keys).unwrap(), parse_token(&compiled_instruction, &AccountKeys::new(&keys, None)).unwrap(),
ParsedInstructionEnum { ParsedInstructionEnum {
instruction_type: "burnChecked".to_string(), instruction_type: "burnChecked".to_string(),
info: json!({ info: json!({
@ -1078,7 +1078,7 @@ mod test {
let message = Message::new(&[sync_native_ix], None); let message = Message::new(&[sync_native_ix], None);
let compiled_instruction = convert_compiled_instruction(&message.instructions[0]); let compiled_instruction = convert_compiled_instruction(&message.instructions[0]);
assert_eq!( assert_eq!(
parse_token(&compiled_instruction, &keys).unwrap(), parse_token(&compiled_instruction, &AccountKeys::new(&keys, None)).unwrap(),
ParsedInstructionEnum { ParsedInstructionEnum {
instruction_type: "syncNative".to_string(), instruction_type: "syncNative".to_string(),
info: json!({ info: json!({
@ -1107,10 +1107,10 @@ mod test {
.unwrap(); .unwrap();
let message = Message::new(&[initialize_mint_ix], None); let message = Message::new(&[initialize_mint_ix], None);
let mut compiled_instruction = convert_compiled_instruction(&message.instructions[0]); let mut compiled_instruction = convert_compiled_instruction(&message.instructions[0]);
assert!(parse_token(&compiled_instruction, &keys[0..1]).is_err()); assert!(parse_token(&compiled_instruction, &AccountKeys::new(&keys[0..1], None)).is_err());
compiled_instruction.accounts = compiled_instruction.accounts =
compiled_instruction.accounts[0..compiled_instruction.accounts.len() - 1].to_vec(); compiled_instruction.accounts[0..compiled_instruction.accounts.len() - 1].to_vec();
assert!(parse_token(&compiled_instruction, &keys).is_err()); assert!(parse_token(&compiled_instruction, &AccountKeys::new(&keys, None)).is_err());
let initialize_mint_ix = initialize_mint( let initialize_mint_ix = initialize_mint(
&spl_token::id(), &spl_token::id(),
@ -1122,10 +1122,10 @@ mod test {
.unwrap(); .unwrap();
let message = Message::new(&[initialize_mint_ix], None); let message = Message::new(&[initialize_mint_ix], None);
let mut compiled_instruction = convert_compiled_instruction(&message.instructions[0]); let mut compiled_instruction = convert_compiled_instruction(&message.instructions[0]);
assert!(parse_token(&compiled_instruction, &keys[0..1]).is_err()); assert!(parse_token(&compiled_instruction, &AccountKeys::new(&keys[0..1], None)).is_err());
compiled_instruction.accounts = compiled_instruction.accounts =
compiled_instruction.accounts[0..compiled_instruction.accounts.len() - 1].to_vec(); compiled_instruction.accounts[0..compiled_instruction.accounts.len() - 1].to_vec();
assert!(parse_token(&compiled_instruction, &keys).is_err()); assert!(parse_token(&compiled_instruction, &AccountKeys::new(&keys, None)).is_err());
// Test InitializeAccount // Test InitializeAccount
let initialize_account_ix = initialize_account( let initialize_account_ix = initialize_account(
@ -1137,10 +1137,10 @@ mod test {
.unwrap(); .unwrap();
let message = Message::new(&[initialize_account_ix], None); let message = Message::new(&[initialize_account_ix], None);
let mut compiled_instruction = convert_compiled_instruction(&message.instructions[0]); let mut compiled_instruction = convert_compiled_instruction(&message.instructions[0]);
assert!(parse_token(&compiled_instruction, &keys[0..3]).is_err()); assert!(parse_token(&compiled_instruction, &AccountKeys::new(&keys[0..3], None)).is_err());
compiled_instruction.accounts = compiled_instruction.accounts =
compiled_instruction.accounts[0..compiled_instruction.accounts.len() - 1].to_vec(); compiled_instruction.accounts[0..compiled_instruction.accounts.len() - 1].to_vec();
assert!(parse_token(&compiled_instruction, &keys).is_err()); assert!(parse_token(&compiled_instruction, &AccountKeys::new(&keys, None)).is_err());
// Test InitializeMultisig // Test InitializeMultisig
let initialize_multisig_ix = initialize_multisig( let initialize_multisig_ix = initialize_multisig(
@ -1156,10 +1156,10 @@ mod test {
.unwrap(); .unwrap();
let message = Message::new(&[initialize_multisig_ix], None); let message = Message::new(&[initialize_multisig_ix], None);
let mut compiled_instruction = convert_compiled_instruction(&message.instructions[0]); let mut compiled_instruction = convert_compiled_instruction(&message.instructions[0]);
assert!(parse_token(&compiled_instruction, &keys[0..4]).is_err()); assert!(parse_token(&compiled_instruction, &AccountKeys::new(&keys[0..4], None)).is_err());
compiled_instruction.accounts = compiled_instruction.accounts =
compiled_instruction.accounts[0..compiled_instruction.accounts.len() - 3].to_vec(); compiled_instruction.accounts[0..compiled_instruction.accounts.len() - 3].to_vec();
assert!(parse_token(&compiled_instruction, &keys).is_err()); assert!(parse_token(&compiled_instruction, &AccountKeys::new(&keys, None)).is_err());
// Test Transfer, incl multisig // Test Transfer, incl multisig
let transfer_ix = transfer( let transfer_ix = transfer(
@ -1173,10 +1173,10 @@ mod test {
.unwrap(); .unwrap();
let message = Message::new(&[transfer_ix], None); let message = Message::new(&[transfer_ix], None);
let mut compiled_instruction = convert_compiled_instruction(&message.instructions[0]); let mut compiled_instruction = convert_compiled_instruction(&message.instructions[0]);
assert!(parse_token(&compiled_instruction, &keys[0..2]).is_err()); assert!(parse_token(&compiled_instruction, &AccountKeys::new(&keys[0..2], None)).is_err());
compiled_instruction.accounts = compiled_instruction.accounts =
compiled_instruction.accounts[0..compiled_instruction.accounts.len() - 1].to_vec(); compiled_instruction.accounts[0..compiled_instruction.accounts.len() - 1].to_vec();
assert!(parse_token(&compiled_instruction, &keys).is_err()); assert!(parse_token(&compiled_instruction, &AccountKeys::new(&keys, None)).is_err());
let transfer_ix = transfer( let transfer_ix = transfer(
&spl_token::id(), &spl_token::id(),
@ -1189,10 +1189,10 @@ mod test {
.unwrap(); .unwrap();
let message = Message::new(&[transfer_ix], None); let message = Message::new(&[transfer_ix], None);
let mut compiled_instruction = convert_compiled_instruction(&message.instructions[0]); let mut compiled_instruction = convert_compiled_instruction(&message.instructions[0]);
assert!(parse_token(&compiled_instruction, &keys[0..4]).is_err()); assert!(parse_token(&compiled_instruction, &AccountKeys::new(&keys[0..4], None)).is_err());
compiled_instruction.accounts = compiled_instruction.accounts =
compiled_instruction.accounts[0..compiled_instruction.accounts.len() - 3].to_vec(); compiled_instruction.accounts[0..compiled_instruction.accounts.len() - 3].to_vec();
assert!(parse_token(&compiled_instruction, &keys).is_err()); assert!(parse_token(&compiled_instruction, &AccountKeys::new(&keys, None)).is_err());
// Test Approve, incl multisig // Test Approve, incl multisig
let approve_ix = approve( let approve_ix = approve(
@ -1206,10 +1206,10 @@ mod test {
.unwrap(); .unwrap();
let message = Message::new(&[approve_ix], None); let message = Message::new(&[approve_ix], None);
let mut compiled_instruction = convert_compiled_instruction(&message.instructions[0]); let mut compiled_instruction = convert_compiled_instruction(&message.instructions[0]);
assert!(parse_token(&compiled_instruction, &keys[0..2]).is_err()); assert!(parse_token(&compiled_instruction, &AccountKeys::new(&keys[0..2], None)).is_err());
compiled_instruction.accounts = compiled_instruction.accounts =
compiled_instruction.accounts[0..compiled_instruction.accounts.len() - 1].to_vec(); compiled_instruction.accounts[0..compiled_instruction.accounts.len() - 1].to_vec();
assert!(parse_token(&compiled_instruction, &keys).is_err()); assert!(parse_token(&compiled_instruction, &AccountKeys::new(&keys, None)).is_err());
let approve_ix = approve( let approve_ix = approve(
&spl_token::id(), &spl_token::id(),
@ -1222,10 +1222,10 @@ mod test {
.unwrap(); .unwrap();
let message = Message::new(&[approve_ix], None); let message = Message::new(&[approve_ix], None);
let mut compiled_instruction = convert_compiled_instruction(&message.instructions[0]); let mut compiled_instruction = convert_compiled_instruction(&message.instructions[0]);
assert!(parse_token(&compiled_instruction, &keys[0..4]).is_err()); assert!(parse_token(&compiled_instruction, &AccountKeys::new(&keys[0..4], None)).is_err());
compiled_instruction.accounts = compiled_instruction.accounts =
compiled_instruction.accounts[0..compiled_instruction.accounts.len() - 3].to_vec(); compiled_instruction.accounts[0..compiled_instruction.accounts.len() - 3].to_vec();
assert!(parse_token(&compiled_instruction, &keys).is_err()); assert!(parse_token(&compiled_instruction, &AccountKeys::new(&keys, None)).is_err());
// Test Revoke // Test Revoke
let revoke_ix = revoke( let revoke_ix = revoke(
@ -1237,10 +1237,10 @@ mod test {
.unwrap(); .unwrap();
let message = Message::new(&[revoke_ix], None); let message = Message::new(&[revoke_ix], None);
let mut compiled_instruction = convert_compiled_instruction(&message.instructions[0]); let mut compiled_instruction = convert_compiled_instruction(&message.instructions[0]);
assert!(parse_token(&compiled_instruction, &keys[0..1]).is_err()); assert!(parse_token(&compiled_instruction, &AccountKeys::new(&keys[0..1], None)).is_err());
compiled_instruction.accounts = compiled_instruction.accounts =
compiled_instruction.accounts[0..compiled_instruction.accounts.len() - 1].to_vec(); compiled_instruction.accounts[0..compiled_instruction.accounts.len() - 1].to_vec();
assert!(parse_token(&compiled_instruction, &keys).is_err()); assert!(parse_token(&compiled_instruction, &AccountKeys::new(&keys, None)).is_err());
// Test SetAuthority // Test SetAuthority
let set_authority_ix = set_authority( let set_authority_ix = set_authority(
@ -1254,10 +1254,10 @@ mod test {
.unwrap(); .unwrap();
let message = Message::new(&[set_authority_ix], None); let message = Message::new(&[set_authority_ix], None);
let mut compiled_instruction = convert_compiled_instruction(&message.instructions[0]); let mut compiled_instruction = convert_compiled_instruction(&message.instructions[0]);
assert!(parse_token(&compiled_instruction, &keys[0..1]).is_err()); assert!(parse_token(&compiled_instruction, &AccountKeys::new(&keys[0..1], None)).is_err());
compiled_instruction.accounts = compiled_instruction.accounts =
compiled_instruction.accounts[0..compiled_instruction.accounts.len() - 1].to_vec(); compiled_instruction.accounts[0..compiled_instruction.accounts.len() - 1].to_vec();
assert!(parse_token(&compiled_instruction, &keys).is_err()); assert!(parse_token(&compiled_instruction, &AccountKeys::new(&keys, None)).is_err());
// Test MintTo // Test MintTo
let mint_to_ix = mint_to( let mint_to_ix = mint_to(
@ -1271,10 +1271,10 @@ mod test {
.unwrap(); .unwrap();
let message = Message::new(&[mint_to_ix], None); let message = Message::new(&[mint_to_ix], None);
let mut compiled_instruction = convert_compiled_instruction(&message.instructions[0]); let mut compiled_instruction = convert_compiled_instruction(&message.instructions[0]);
assert!(parse_token(&compiled_instruction, &keys[0..2]).is_err()); assert!(parse_token(&compiled_instruction, &AccountKeys::new(&keys[0..2], None)).is_err());
compiled_instruction.accounts = compiled_instruction.accounts =
compiled_instruction.accounts[0..compiled_instruction.accounts.len() - 1].to_vec(); compiled_instruction.accounts[0..compiled_instruction.accounts.len() - 1].to_vec();
assert!(parse_token(&compiled_instruction, &keys).is_err()); assert!(parse_token(&compiled_instruction, &AccountKeys::new(&keys, None)).is_err());
// Test Burn // Test Burn
let burn_ix = burn( let burn_ix = burn(
@ -1288,10 +1288,10 @@ mod test {
.unwrap(); .unwrap();
let message = Message::new(&[burn_ix], None); let message = Message::new(&[burn_ix], None);
let mut compiled_instruction = convert_compiled_instruction(&message.instructions[0]); let mut compiled_instruction = convert_compiled_instruction(&message.instructions[0]);
assert!(parse_token(&compiled_instruction, &keys[0..2]).is_err()); assert!(parse_token(&compiled_instruction, &AccountKeys::new(&keys[0..2], None)).is_err());
compiled_instruction.accounts = compiled_instruction.accounts =
compiled_instruction.accounts[0..compiled_instruction.accounts.len() - 1].to_vec(); compiled_instruction.accounts[0..compiled_instruction.accounts.len() - 1].to_vec();
assert!(parse_token(&compiled_instruction, &keys).is_err()); assert!(parse_token(&compiled_instruction, &AccountKeys::new(&keys, None)).is_err());
// Test CloseAccount // Test CloseAccount
let close_account_ix = close_account( let close_account_ix = close_account(
@ -1304,10 +1304,10 @@ mod test {
.unwrap(); .unwrap();
let message = Message::new(&[close_account_ix], None); let message = Message::new(&[close_account_ix], None);
let mut compiled_instruction = convert_compiled_instruction(&message.instructions[0]); let mut compiled_instruction = convert_compiled_instruction(&message.instructions[0]);
assert!(parse_token(&compiled_instruction, &keys[0..2]).is_err()); assert!(parse_token(&compiled_instruction, &AccountKeys::new(&keys[0..2], None)).is_err());
compiled_instruction.accounts = compiled_instruction.accounts =
compiled_instruction.accounts[0..compiled_instruction.accounts.len() - 1].to_vec(); compiled_instruction.accounts[0..compiled_instruction.accounts.len() - 1].to_vec();
assert!(parse_token(&compiled_instruction, &keys).is_err()); assert!(parse_token(&compiled_instruction, &AccountKeys::new(&keys, None)).is_err());
// Test FreezeAccount // Test FreezeAccount
let freeze_account_ix = freeze_account( let freeze_account_ix = freeze_account(
@ -1320,10 +1320,10 @@ mod test {
.unwrap(); .unwrap();
let message = Message::new(&[freeze_account_ix], None); let message = Message::new(&[freeze_account_ix], None);
let mut compiled_instruction = convert_compiled_instruction(&message.instructions[0]); let mut compiled_instruction = convert_compiled_instruction(&message.instructions[0]);
assert!(parse_token(&compiled_instruction, &keys[0..2]).is_err()); assert!(parse_token(&compiled_instruction, &AccountKeys::new(&keys[0..2], None)).is_err());
compiled_instruction.accounts = compiled_instruction.accounts =
compiled_instruction.accounts[0..compiled_instruction.accounts.len() - 1].to_vec(); compiled_instruction.accounts[0..compiled_instruction.accounts.len() - 1].to_vec();
assert!(parse_token(&compiled_instruction, &keys).is_err()); assert!(parse_token(&compiled_instruction, &AccountKeys::new(&keys, None)).is_err());
// Test ThawAccount // Test ThawAccount
let thaw_account_ix = thaw_account( let thaw_account_ix = thaw_account(
@ -1336,10 +1336,10 @@ mod test {
.unwrap(); .unwrap();
let message = Message::new(&[thaw_account_ix], None); let message = Message::new(&[thaw_account_ix], None);
let mut compiled_instruction = convert_compiled_instruction(&message.instructions[0]); let mut compiled_instruction = convert_compiled_instruction(&message.instructions[0]);
assert!(parse_token(&compiled_instruction, &keys[0..2]).is_err()); assert!(parse_token(&compiled_instruction, &AccountKeys::new(&keys[0..2], None)).is_err());
compiled_instruction.accounts = compiled_instruction.accounts =
compiled_instruction.accounts[0..compiled_instruction.accounts.len() - 1].to_vec(); compiled_instruction.accounts[0..compiled_instruction.accounts.len() - 1].to_vec();
assert!(parse_token(&compiled_instruction, &keys).is_err()); assert!(parse_token(&compiled_instruction, &AccountKeys::new(&keys, None)).is_err());
// Test TransferChecked, incl multisig // Test TransferChecked, incl multisig
let transfer_ix = transfer_checked( let transfer_ix = transfer_checked(
@ -1355,10 +1355,10 @@ mod test {
.unwrap(); .unwrap();
let message = Message::new(&[transfer_ix], None); let message = Message::new(&[transfer_ix], None);
let mut compiled_instruction = convert_compiled_instruction(&message.instructions[0]); let mut compiled_instruction = convert_compiled_instruction(&message.instructions[0]);
assert!(parse_token(&compiled_instruction, &keys[0..3]).is_err()); assert!(parse_token(&compiled_instruction, &AccountKeys::new(&keys[0..3], None)).is_err());
compiled_instruction.accounts = compiled_instruction.accounts =
compiled_instruction.accounts[0..compiled_instruction.accounts.len() - 1].to_vec(); compiled_instruction.accounts[0..compiled_instruction.accounts.len() - 1].to_vec();
assert!(parse_token(&compiled_instruction, &keys).is_err()); assert!(parse_token(&compiled_instruction, &AccountKeys::new(&keys, None)).is_err());
let transfer_ix = transfer_checked( let transfer_ix = transfer_checked(
&spl_token::id(), &spl_token::id(),
@ -1373,10 +1373,10 @@ mod test {
.unwrap(); .unwrap();
let message = Message::new(&[transfer_ix], None); let message = Message::new(&[transfer_ix], None);
let mut compiled_instruction = convert_compiled_instruction(&message.instructions[0]); let mut compiled_instruction = convert_compiled_instruction(&message.instructions[0]);
assert!(parse_token(&compiled_instruction, &keys[0..5]).is_err()); assert!(parse_token(&compiled_instruction, &AccountKeys::new(&keys[0..5], None)).is_err());
compiled_instruction.accounts = compiled_instruction.accounts =
compiled_instruction.accounts[0..compiled_instruction.accounts.len() - 3].to_vec(); compiled_instruction.accounts[0..compiled_instruction.accounts.len() - 3].to_vec();
assert!(parse_token(&compiled_instruction, &keys).is_err()); assert!(parse_token(&compiled_instruction, &AccountKeys::new(&keys, None)).is_err());
// Test ApproveChecked, incl multisig // Test ApproveChecked, incl multisig
let approve_ix = approve_checked( let approve_ix = approve_checked(
@ -1392,10 +1392,10 @@ mod test {
.unwrap(); .unwrap();
let message = Message::new(&[approve_ix], None); let message = Message::new(&[approve_ix], None);
let mut compiled_instruction = convert_compiled_instruction(&message.instructions[0]); let mut compiled_instruction = convert_compiled_instruction(&message.instructions[0]);
assert!(parse_token(&compiled_instruction, &keys[0..3]).is_err()); assert!(parse_token(&compiled_instruction, &AccountKeys::new(&keys[0..3], None)).is_err());
compiled_instruction.accounts = compiled_instruction.accounts =
compiled_instruction.accounts[0..compiled_instruction.accounts.len() - 1].to_vec(); compiled_instruction.accounts[0..compiled_instruction.accounts.len() - 1].to_vec();
assert!(parse_token(&compiled_instruction, &keys).is_err()); assert!(parse_token(&compiled_instruction, &AccountKeys::new(&keys, None)).is_err());
let approve_ix = approve_checked( let approve_ix = approve_checked(
&spl_token::id(), &spl_token::id(),
@ -1410,10 +1410,10 @@ mod test {
.unwrap(); .unwrap();
let message = Message::new(&[approve_ix], None); let message = Message::new(&[approve_ix], None);
let mut compiled_instruction = convert_compiled_instruction(&message.instructions[0]); let mut compiled_instruction = convert_compiled_instruction(&message.instructions[0]);
assert!(parse_token(&compiled_instruction, &keys[0..5]).is_err()); assert!(parse_token(&compiled_instruction, &AccountKeys::new(&keys[0..5], None)).is_err());
compiled_instruction.accounts = compiled_instruction.accounts =
compiled_instruction.accounts[0..compiled_instruction.accounts.len() - 3].to_vec(); compiled_instruction.accounts[0..compiled_instruction.accounts.len() - 3].to_vec();
assert!(parse_token(&compiled_instruction, &keys).is_err()); assert!(parse_token(&compiled_instruction, &AccountKeys::new(&keys, None)).is_err());
// Test MintToChecked // Test MintToChecked
let mint_to_ix = mint_to_checked( let mint_to_ix = mint_to_checked(
@ -1428,10 +1428,10 @@ mod test {
.unwrap(); .unwrap();
let message = Message::new(&[mint_to_ix], None); let message = Message::new(&[mint_to_ix], None);
let mut compiled_instruction = convert_compiled_instruction(&message.instructions[0]); let mut compiled_instruction = convert_compiled_instruction(&message.instructions[0]);
assert!(parse_token(&compiled_instruction, &keys[0..2]).is_err()); assert!(parse_token(&compiled_instruction, &AccountKeys::new(&keys[0..2], None)).is_err());
compiled_instruction.accounts = compiled_instruction.accounts =
compiled_instruction.accounts[0..compiled_instruction.accounts.len() - 1].to_vec(); compiled_instruction.accounts[0..compiled_instruction.accounts.len() - 1].to_vec();
assert!(parse_token(&compiled_instruction, &keys).is_err()); assert!(parse_token(&compiled_instruction, &AccountKeys::new(&keys, None)).is_err());
// Test BurnChecked // Test BurnChecked
let burn_ix = burn_checked( let burn_ix = burn_checked(
@ -1446,18 +1446,18 @@ mod test {
.unwrap(); .unwrap();
let message = Message::new(&[burn_ix], None); let message = Message::new(&[burn_ix], None);
let mut compiled_instruction = convert_compiled_instruction(&message.instructions[0]); let mut compiled_instruction = convert_compiled_instruction(&message.instructions[0]);
assert!(parse_token(&compiled_instruction, &keys[0..2]).is_err()); assert!(parse_token(&compiled_instruction, &AccountKeys::new(&keys[0..2], None)).is_err());
compiled_instruction.accounts = compiled_instruction.accounts =
compiled_instruction.accounts[0..compiled_instruction.accounts.len() - 1].to_vec(); compiled_instruction.accounts[0..compiled_instruction.accounts.len() - 1].to_vec();
assert!(parse_token(&compiled_instruction, &keys).is_err()); assert!(parse_token(&compiled_instruction, &AccountKeys::new(&keys, None)).is_err());
// Test SyncNative // Test SyncNative
let sync_native_ix = sync_native(&spl_token::id(), &convert_pubkey(keys[0])).unwrap(); let sync_native_ix = sync_native(&spl_token::id(), &convert_pubkey(keys[0])).unwrap();
let message = Message::new(&[sync_native_ix], None); let message = Message::new(&[sync_native_ix], None);
let mut compiled_instruction = convert_compiled_instruction(&message.instructions[0]); let mut compiled_instruction = convert_compiled_instruction(&message.instructions[0]);
assert!(parse_token(&compiled_instruction, &[]).is_err()); assert!(parse_token(&compiled_instruction, &AccountKeys::new(&[], None)).is_err());
compiled_instruction.accounts = compiled_instruction.accounts =
compiled_instruction.accounts[0..compiled_instruction.accounts.len() - 1].to_vec(); compiled_instruction.accounts[0..compiled_instruction.accounts.len() - 1].to_vec();
assert!(parse_token(&compiled_instruction, &keys).is_err()); assert!(parse_token(&compiled_instruction, &AccountKeys::new(&keys, None)).is_err());
} }
} }

View File

@ -4,13 +4,13 @@ use {
}, },
bincode::deserialize, bincode::deserialize,
serde_json::json, serde_json::json,
solana_sdk::{instruction::CompiledInstruction, pubkey::Pubkey}, solana_sdk::{instruction::CompiledInstruction, message::AccountKeys},
solana_vote_program::vote_instruction::VoteInstruction, solana_vote_program::vote_instruction::VoteInstruction,
}; };
pub fn parse_vote( pub fn parse_vote(
instruction: &CompiledInstruction, instruction: &CompiledInstruction,
account_keys: &[Pubkey], account_keys: &AccountKeys,
) -> Result<ParsedInstructionEnum, ParseInstructionError> { ) -> Result<ParsedInstructionEnum, ParseInstructionError> {
let vote_instruction: VoteInstruction = deserialize(&instruction.data) let vote_instruction: VoteInstruction = deserialize(&instruction.data)
.map_err(|_| ParseInstructionError::InstructionNotParsable(ParsableProgram::Vote))?; .map_err(|_| ParseInstructionError::InstructionNotParsable(ParsableProgram::Vote))?;
@ -227,7 +227,11 @@ mod test {
); );
let message = Message::new(&instructions, None); let message = Message::new(&instructions, None);
assert_eq!( assert_eq!(
parse_vote(&message.instructions[1], &keys[0..5]).unwrap(), parse_vote(
&message.instructions[1],
&AccountKeys::new(&keys[0..5], None)
)
.unwrap(),
ParsedInstructionEnum { ParsedInstructionEnum {
instruction_type: "initialize".to_string(), instruction_type: "initialize".to_string(),
info: json!({ info: json!({
@ -241,13 +245,21 @@ mod test {
}), }),
} }
); );
assert!(parse_vote(&message.instructions[1], &keys[0..3]).is_err()); assert!(parse_vote(
&message.instructions[1],
&AccountKeys::new(&keys[0..3], None)
)
.is_err());
let authority_type = VoteAuthorize::Voter; let authority_type = VoteAuthorize::Voter;
let instruction = vote_instruction::authorize(&keys[1], &keys[0], &keys[3], authority_type); let instruction = vote_instruction::authorize(&keys[1], &keys[0], &keys[3], authority_type);
let message = Message::new(&[instruction], None); let message = Message::new(&[instruction], None);
assert_eq!( assert_eq!(
parse_vote(&message.instructions[0], &keys[0..3]).unwrap(), parse_vote(
&message.instructions[0],
&AccountKeys::new(&keys[0..3], None)
)
.unwrap(),
ParsedInstructionEnum { ParsedInstructionEnum {
instruction_type: "authorize".to_string(), instruction_type: "authorize".to_string(),
info: json!({ info: json!({
@ -259,12 +271,20 @@ mod test {
}), }),
} }
); );
assert!(parse_vote(&message.instructions[0], &keys[0..2]).is_err()); assert!(parse_vote(
&message.instructions[0],
&AccountKeys::new(&keys[0..2], None)
)
.is_err());
let instruction = vote_instruction::vote(&keys[1], &keys[0], vote.clone()); let instruction = vote_instruction::vote(&keys[1], &keys[0], vote.clone());
let message = Message::new(&[instruction], None); let message = Message::new(&[instruction], None);
assert_eq!( assert_eq!(
parse_vote(&message.instructions[0], &keys[0..4]).unwrap(), parse_vote(
&message.instructions[0],
&AccountKeys::new(&keys[0..4], None)
)
.unwrap(),
ParsedInstructionEnum { ParsedInstructionEnum {
instruction_type: "vote".to_string(), instruction_type: "vote".to_string(),
info: json!({ info: json!({
@ -280,12 +300,20 @@ mod test {
}), }),
} }
); );
assert!(parse_vote(&message.instructions[0], &keys[0..3]).is_err()); assert!(parse_vote(
&message.instructions[0],
&AccountKeys::new(&keys[0..3], None)
)
.is_err());
let instruction = vote_instruction::withdraw(&keys[1], &keys[0], lamports, &keys[2]); let instruction = vote_instruction::withdraw(&keys[1], &keys[0], lamports, &keys[2]);
let message = Message::new(&[instruction], None); let message = Message::new(&[instruction], None);
assert_eq!( assert_eq!(
parse_vote(&message.instructions[0], &keys[0..3]).unwrap(), parse_vote(
&message.instructions[0],
&AccountKeys::new(&keys[0..3], None)
)
.unwrap(),
ParsedInstructionEnum { ParsedInstructionEnum {
instruction_type: "withdraw".to_string(), instruction_type: "withdraw".to_string(),
info: json!({ info: json!({
@ -296,12 +324,20 @@ mod test {
}), }),
} }
); );
assert!(parse_vote(&message.instructions[0], &keys[0..2]).is_err()); assert!(parse_vote(
&message.instructions[0],
&AccountKeys::new(&keys[0..2], None)
)
.is_err());
let instruction = vote_instruction::update_validator_identity(&keys[2], &keys[1], &keys[0]); let instruction = vote_instruction::update_validator_identity(&keys[2], &keys[1], &keys[0]);
let message = Message::new(&[instruction], None); let message = Message::new(&[instruction], None);
assert_eq!( assert_eq!(
parse_vote(&message.instructions[0], &keys[0..3]).unwrap(), parse_vote(
&message.instructions[0],
&AccountKeys::new(&keys[0..3], None)
)
.unwrap(),
ParsedInstructionEnum { ParsedInstructionEnum {
instruction_type: "updateValidatorIdentity".to_string(), instruction_type: "updateValidatorIdentity".to_string(),
info: json!({ info: json!({
@ -311,12 +347,20 @@ mod test {
}), }),
} }
); );
assert!(parse_vote(&message.instructions[0], &keys[0..2]).is_err()); assert!(parse_vote(
&message.instructions[0],
&AccountKeys::new(&keys[0..2], None)
)
.is_err());
let instruction = vote_instruction::update_commission(&keys[1], &keys[0], commission); let instruction = vote_instruction::update_commission(&keys[1], &keys[0], commission);
let message = Message::new(&[instruction], None); let message = Message::new(&[instruction], None);
assert_eq!( assert_eq!(
parse_vote(&message.instructions[0], &keys[0..2]).unwrap(), parse_vote(
&message.instructions[0],
&AccountKeys::new(&keys[0..2], None)
)
.unwrap(),
ParsedInstructionEnum { ParsedInstructionEnum {
instruction_type: "updateCommission".to_string(), instruction_type: "updateCommission".to_string(),
info: json!({ info: json!({
@ -326,13 +370,21 @@ mod test {
}), }),
} }
); );
assert!(parse_vote(&message.instructions[0], &keys[0..1]).is_err()); assert!(parse_vote(
&message.instructions[0],
&AccountKeys::new(&keys[0..1], None)
)
.is_err());
let proof_hash = Hash::new_from_array([2; 32]); 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(&keys[1], &keys[0], vote, proof_hash);
let message = Message::new(&[instruction], None); let message = Message::new(&[instruction], None);
assert_eq!( assert_eq!(
parse_vote(&message.instructions[0], &keys[0..4]).unwrap(), parse_vote(
&message.instructions[0],
&AccountKeys::new(&keys[0..4], None)
)
.unwrap(),
ParsedInstructionEnum { ParsedInstructionEnum {
instruction_type: "voteSwitch".to_string(), instruction_type: "voteSwitch".to_string(),
info: json!({ info: json!({
@ -349,14 +401,22 @@ mod test {
}), }),
} }
); );
assert!(parse_vote(&message.instructions[0], &keys[0..3]).is_err()); assert!(parse_vote(
&message.instructions[0],
&AccountKeys::new(&keys[0..3], None)
)
.is_err());
let authority_type = VoteAuthorize::Voter; let authority_type = VoteAuthorize::Voter;
let instruction = let instruction =
vote_instruction::authorize_checked(&keys[1], &keys[0], &keys[3], authority_type); vote_instruction::authorize_checked(&keys[1], &keys[0], &keys[3], authority_type);
let message = Message::new(&[instruction], None); let message = Message::new(&[instruction], None);
assert_eq!( assert_eq!(
parse_vote(&message.instructions[0], &keys[0..4]).unwrap(), parse_vote(
&message.instructions[0],
&AccountKeys::new(&keys[0..4], None)
)
.unwrap(),
ParsedInstructionEnum { ParsedInstructionEnum {
instruction_type: "authorizeChecked".to_string(), instruction_type: "authorizeChecked".to_string(),
info: json!({ info: json!({
@ -368,6 +428,10 @@ mod test {
}), }),
} }
); );
assert!(parse_vote(&message.instructions[0], &keys[0..3]).is_err()); assert!(parse_vote(
&message.instructions[0],
&AccountKeys::new(&keys[0..3], None)
)
.is_err());
} }
} }

View File

@ -62,14 +62,12 @@ pub fn collect_token_balances(
let mut collect_time = Measure::start("collect_token_balances"); let mut collect_time = Measure::start("collect_token_balances");
for transaction in batch.sanitized_transactions() { for transaction in batch.sanitized_transactions() {
let has_token_program = transaction let account_keys = transaction.message().account_keys();
.message() let has_token_program = account_keys.iter().any(is_token_program);
.account_keys_iter()
.any(is_token_program);
let mut transaction_balances: Vec<TransactionTokenBalance> = vec![]; let mut transaction_balances: Vec<TransactionTokenBalance> = vec![];
if has_token_program { if has_token_program {
for (index, account_id) in transaction.message().account_keys_iter().enumerate() { for (index, account_id) in account_keys.iter().enumerate() {
if transaction.message().is_invoked(index) || is_token_program(account_id) { if transaction.message().is_invoked(index) || is_token_program(account_id) {
continue; continue;
} }