use {
crate::{
instruction::SVMInstruction, message_address_table_lookup::SVMMessageAddressTableLookup,
},
core::fmt::Debug,
solana_hash::Hash,
solana_message::AccountKeys,
solana_pubkey::Pubkey,
solana_sdk_ids::{ed25519_program, secp256k1_program, secp256r1_program, system_program},
};
mod sanitized_message;
mod sanitized_transaction;
#[cfg(test)]
static_assertions::const_assert_eq!(
NONCED_TX_MARKER_IX_INDEX,
solana_nonce::NONCED_TX_MARKER_IX_INDEX
);
const NONCED_TX_MARKER_IX_INDEX: u8 = 0;
pub trait SVMMessage: Debug {
fn num_transaction_signatures(&self) -> u64;
fn num_ed25519_signatures(&self) -> u64 {
default_precompile_signature_count(&ed25519_program::ID, self.program_instructions_iter())
}
fn num_secp256k1_signatures(&self) -> u64 {
default_precompile_signature_count(&secp256k1_program::ID, self.program_instructions_iter())
}
fn num_secp256r1_signatures(&self) -> u64 {
default_precompile_signature_count(&secp256r1_program::ID, self.program_instructions_iter())
}
fn num_write_locks(&self) -> u64;
fn recent_blockhash(&self) -> &Hash;
fn num_instructions(&self) -> usize;
fn instructions_iter(&self) -> impl Iterator<Item = SVMInstruction>;
fn program_instructions_iter(&self) -> impl Iterator<Item = (&Pubkey, SVMInstruction)> + Clone;
fn static_account_keys(&self) -> &[Pubkey];
fn account_keys(&self) -> AccountKeys;
fn fee_payer(&self) -> &Pubkey;
fn is_writable(&self, index: usize) -> bool;
fn is_signer(&self, index: usize) -> bool;
fn is_invoked(&self, key_index: usize) -> bool;
fn is_instruction_account(&self, key_index: usize) -> bool {
if let Ok(key_index) = u8::try_from(key_index) {
self.instructions_iter()
.any(|ix| ix.accounts.contains(&key_index))
} else {
false
}
}
fn get_durable_nonce(&self, require_static_nonce_account: bool) -> Option<&Pubkey> {
let account_keys = self.account_keys();
self.instructions_iter()
.nth(usize::from(NONCED_TX_MARKER_IX_INDEX))
.filter(
|ix| match account_keys.get(usize::from(ix.program_id_index)) {
Some(program_id) => system_program::check_id(program_id),
_ => false,
},
)
.filter(|ix| {
const SERIALIZED_ADVANCE_NONCE_ACCOUNT: [u8; 4] = 4u32.to_le_bytes();
const SERIALIZED_SIZE: usize = SERIALIZED_ADVANCE_NONCE_ACCOUNT.len();
ix.data
.get(..SERIALIZED_SIZE)
.map(|data| data == SERIALIZED_ADVANCE_NONCE_ACCOUNT)
.unwrap_or(false)
})
.and_then(|ix| {
ix.accounts.first().and_then(|idx| {
let index = usize::from(*idx);
if (require_static_nonce_account && index >= self.static_account_keys().len())
|| !self.is_writable(index)
{
None
} else {
account_keys.get(index)
}
})
})
}
fn get_ix_signers(&self, index: usize) -> impl Iterator<Item = &Pubkey> {
self.instructions_iter()
.nth(index)
.into_iter()
.flat_map(|ix| {
ix.accounts
.iter()
.copied()
.map(usize::from)
.filter(|index| self.is_signer(*index))
.filter_map(|signer_index| self.account_keys().get(signer_index))
})
}
fn num_lookup_tables(&self) -> usize;
fn message_address_table_lookups(&self) -> impl Iterator<Item = SVMMessageAddressTableLookup>;
}
fn default_precompile_signature_count<'a>(
precompile: &Pubkey,
instructions: impl Iterator<Item = (&'a Pubkey, SVMInstruction<'a>)>,
) -> u64 {
instructions
.filter(|(program_id, _)| *program_id == precompile)
.map(|(_, ix)| u64::from(ix.data.first().copied().unwrap_or(0)))
.sum()
}