solana_svm_transaction/
svm_message.rs1use {
2 crate::{
3 instruction::SVMInstruction, message_address_table_lookup::SVMMessageAddressTableLookup,
4 },
5 core::fmt::Debug,
6 solana_hash::Hash,
7 solana_message::AccountKeys,
8 solana_pubkey::Pubkey,
9 solana_sdk_ids::{ed25519_program, secp256k1_program, secp256r1_program, system_program},
10};
11
12mod sanitized_message;
13mod sanitized_transaction;
14#[cfg(test)]
16static_assertions::const_assert_eq!(
17 NONCED_TX_MARKER_IX_INDEX,
18 solana_nonce::NONCED_TX_MARKER_IX_INDEX
19);
20const NONCED_TX_MARKER_IX_INDEX: u8 = 0;
21
22pub trait SVMMessage: Debug {
24 fn num_transaction_signatures(&self) -> u64;
26 fn num_ed25519_signatures(&self) -> u64 {
28 default_precompile_signature_count(&ed25519_program::ID, self.program_instructions_iter())
29 }
30 fn num_secp256k1_signatures(&self) -> u64 {
32 default_precompile_signature_count(&secp256k1_program::ID, self.program_instructions_iter())
33 }
34 fn num_secp256r1_signatures(&self) -> u64 {
36 default_precompile_signature_count(&secp256r1_program::ID, self.program_instructions_iter())
37 }
38
39 fn num_write_locks(&self) -> u64;
42
43 fn recent_blockhash(&self) -> &Hash;
45
46 fn num_instructions(&self) -> usize;
48
49 fn instructions_iter(&self) -> impl Iterator<Item = SVMInstruction<'_>>;
51
52 fn program_instructions_iter(
55 &self,
56 ) -> impl Iterator<Item = (&Pubkey, SVMInstruction<'_>)> + Clone;
57
58 fn static_account_keys(&self) -> &[Pubkey];
60
61 fn account_keys(&self) -> AccountKeys<'_>;
63
64 fn fee_payer(&self) -> &Pubkey;
66
67 fn is_writable(&self, index: usize) -> bool;
69
70 fn is_signer(&self, index: usize) -> bool;
72
73 fn is_invoked(&self, key_index: usize) -> bool;
76
77 fn is_instruction_account(&self, key_index: usize) -> bool {
80 if let Ok(key_index) = u8::try_from(key_index) {
81 self.instructions_iter()
82 .any(|ix| ix.accounts.contains(&key_index))
83 } else {
84 false
85 }
86 }
87
88 fn get_durable_nonce(&self, require_static_nonce_account: bool) -> Option<&Pubkey> {
90 let account_keys = self.account_keys();
91 self.instructions_iter()
92 .nth(usize::from(NONCED_TX_MARKER_IX_INDEX))
93 .filter(
94 |ix| match account_keys.get(usize::from(ix.program_id_index)) {
95 Some(program_id) => system_program::check_id(program_id),
96 _ => false,
97 },
98 )
99 .filter(|ix| {
100 const SERIALIZED_ADVANCE_NONCE_ACCOUNT: [u8; 4] = 4u32.to_le_bytes();
102 const SERIALIZED_SIZE: usize = SERIALIZED_ADVANCE_NONCE_ACCOUNT.len();
103
104 ix.data
105 .get(..SERIALIZED_SIZE)
106 .map(|data| data == SERIALIZED_ADVANCE_NONCE_ACCOUNT)
107 .unwrap_or(false)
108 })
109 .and_then(|ix| {
110 ix.accounts.first().and_then(|idx| {
111 let index = usize::from(*idx);
112 if (require_static_nonce_account && index >= self.static_account_keys().len())
113 || !self.is_writable(index)
114 {
115 None
116 } else {
117 account_keys.get(index)
118 }
119 })
120 })
121 }
122
123 fn get_ix_signers(&self, index: usize) -> impl Iterator<Item = &Pubkey> {
126 self.instructions_iter()
127 .nth(index)
128 .into_iter()
129 .flat_map(|ix| {
130 ix.accounts
131 .iter()
132 .copied()
133 .map(usize::from)
134 .filter(|index| self.is_signer(*index))
135 .filter_map(|signer_index| self.account_keys().get(signer_index))
136 })
137 }
138
139 fn num_lookup_tables(&self) -> usize;
141
142 fn message_address_table_lookups(
144 &self,
145 ) -> impl Iterator<Item = SVMMessageAddressTableLookup<'_>>;
146}
147
148fn default_precompile_signature_count<'a>(
149 precompile: &Pubkey,
150 instructions: impl Iterator<Item = (&'a Pubkey, SVMInstruction<'a>)>,
151) -> u64 {
152 instructions
153 .filter(|(program_id, _)| *program_id == precompile)
154 .map(|(_, ix)| u64::from(ix.data.first().copied().unwrap_or(0)))
155 .sum()
156}