solana_svm_transaction/
svm_message.rs

1use {
2    crate::{
3        instruction::SVMInstruction, message_address_table_lookup::SVMMessageAddressTableLookup,
4    },
5    core::fmt::Debug,
6    solana_sdk::{
7        hash::Hash, message::AccountKeys, nonce::NONCED_TX_MARKER_IX_INDEX, pubkey::Pubkey,
8        system_program,
9    },
10};
11
12mod sanitized_message;
13mod sanitized_transaction;
14
15// - Debug to support legacy logging
16pub trait SVMMessage: Debug {
17    /// Returns the total number of signatures in the message.
18    /// This includes required transaction signatures as well as any
19    /// pre-compile signatures that are attached in instructions.
20    fn num_total_signatures(&self) -> u64;
21
22    /// Returns the number of requested write-locks in this message.
23    /// This does not consider if write-locks are demoted.
24    fn num_write_locks(&self) -> u64;
25
26    /// Return the recent blockhash.
27    fn recent_blockhash(&self) -> &Hash;
28
29    /// Return the number of instructions in the message.
30    fn num_instructions(&self) -> usize;
31
32    /// Return an iterator over the instructions in the message.
33    fn instructions_iter(&self) -> impl Iterator<Item = SVMInstruction>;
34
35    /// Return an iterator over the instructions in the message, paired with
36    /// the pubkey of the program.
37    fn program_instructions_iter(&self) -> impl Iterator<Item = (&Pubkey, SVMInstruction)> + Clone;
38
39    /// Return the account keys.
40    fn account_keys(&self) -> AccountKeys;
41
42    /// Return the fee-payer
43    fn fee_payer(&self) -> &Pubkey;
44
45    /// Returns `true` if the account at `index` is writable.
46    fn is_writable(&self, index: usize) -> bool;
47
48    /// Returns `true` if the account at `index` is signer.
49    fn is_signer(&self, index: usize) -> bool;
50
51    /// Returns true if the account at the specified index is invoked as a
52    /// program in top-level instructions of this message.
53    fn is_invoked(&self, key_index: usize) -> bool;
54
55    /// Returns true if the account at the specified index is an input to some
56    /// program instruction in this message.
57    fn is_instruction_account(&self, key_index: usize) -> bool {
58        if let Ok(key_index) = u8::try_from(key_index) {
59            self.instructions_iter()
60                .any(|ix| ix.accounts.contains(&key_index))
61        } else {
62            false
63        }
64    }
65
66    /// If the message uses a durable nonce, return the pubkey of the nonce account
67    fn get_durable_nonce(&self) -> Option<&Pubkey> {
68        let account_keys = self.account_keys();
69        self.instructions_iter()
70            .nth(usize::from(NONCED_TX_MARKER_IX_INDEX))
71            .filter(
72                |ix| match account_keys.get(usize::from(ix.program_id_index)) {
73                    Some(program_id) => system_program::check_id(program_id),
74                    _ => false,
75                },
76            )
77            .filter(|ix| {
78                /// Serialized value of [`SystemInstruction::AdvanceNonceAccount`].
79                const SERIALIZED_ADVANCE_NONCE_ACCOUNT: [u8; 4] = 4u32.to_le_bytes();
80                const SERIALIZED_SIZE: usize = SERIALIZED_ADVANCE_NONCE_ACCOUNT.len();
81
82                ix.data
83                    .get(..SERIALIZED_SIZE)
84                    .map(|data| data == SERIALIZED_ADVANCE_NONCE_ACCOUNT)
85                    .unwrap_or(false)
86            })
87            .and_then(|ix| {
88                ix.accounts.first().and_then(|idx| {
89                    let index = usize::from(*idx);
90                    if !self.is_writable(index) {
91                        None
92                    } else {
93                        account_keys.get(index)
94                    }
95                })
96            })
97    }
98
99    /// For the instruction at `index`, return an iterator over input accounts
100    /// that are signers.
101    fn get_ix_signers(&self, index: usize) -> impl Iterator<Item = &Pubkey> {
102        self.instructions_iter()
103            .nth(index)
104            .into_iter()
105            .flat_map(|ix| {
106                ix.accounts
107                    .iter()
108                    .copied()
109                    .map(usize::from)
110                    .filter(|index| self.is_signer(*index))
111                    .filter_map(|signer_index| self.account_keys().get(signer_index))
112            })
113    }
114
115    /// Get the number of lookup tables.
116    fn num_lookup_tables(&self) -> usize;
117
118    /// Get message address table lookups used in the message
119    fn message_address_table_lookups(&self) -> impl Iterator<Item = SVMMessageAddressTableLookup>;
120}