solana_runtime/
verify_precompiles.rs

1use {
2    solana_feature_set::FeatureSet,
3    solana_sdk::{
4        instruction::InstructionError,
5        precompiles::get_precompiles,
6        transaction::{Result, TransactionError},
7    },
8    solana_svm_transaction::svm_message::SVMMessage,
9};
10
11pub fn verify_precompiles(message: &impl SVMMessage, feature_set: &FeatureSet) -> Result<()> {
12    let mut all_instruction_data = None; // lazily collect this on first pre-compile
13
14    let precompiles = get_precompiles();
15    for (index, (program_id, instruction)) in message.program_instructions_iter().enumerate() {
16        for precompile in precompiles {
17            if precompile.check_id(program_id, |id| feature_set.is_active(id)) {
18                let all_instruction_data: &Vec<&[u8]> = all_instruction_data
19                    .get_or_insert_with(|| message.instructions_iter().map(|ix| ix.data).collect());
20                precompile
21                    .verify(instruction.data, all_instruction_data, feature_set)
22                    .map_err(|err| {
23                        TransactionError::InstructionError(
24                            index as u8,
25                            InstructionError::Custom(err as u32),
26                        )
27                    })?;
28                break;
29            }
30        }
31    }
32
33    Ok(())
34}
35
36#[cfg(test)]
37mod tests {
38    use {
39        super::*,
40        rand0_7::{thread_rng, Rng},
41        solana_sdk::{
42            ed25519_instruction::new_ed25519_instruction,
43            hash::Hash,
44            pubkey::Pubkey,
45            secp256k1_instruction::new_secp256k1_instruction,
46            signature::Keypair,
47            signer::Signer,
48            system_instruction, system_transaction,
49            transaction::{SanitizedTransaction, Transaction},
50        },
51    };
52
53    #[test]
54    fn test_verify_precompiles_simple_transaction() {
55        let tx = SanitizedTransaction::from_transaction_for_tests(system_transaction::transfer(
56            &Keypair::new(),
57            &Pubkey::new_unique(),
58            1,
59            Hash::default(),
60        ));
61        assert!(verify_precompiles(&tx, &FeatureSet::all_enabled()).is_ok());
62    }
63
64    #[test]
65    fn test_verify_precompiles_secp256k1() {
66        let secp_privkey = libsecp256k1::SecretKey::random(&mut thread_rng());
67        let message_arr = b"hello";
68        let mut secp_instruction = new_secp256k1_instruction(&secp_privkey, message_arr);
69        let mint_keypair = Keypair::new();
70        let feature_set = FeatureSet::all_enabled();
71
72        let tx =
73            SanitizedTransaction::from_transaction_for_tests(Transaction::new_signed_with_payer(
74                &[secp_instruction.clone()],
75                Some(&mint_keypair.pubkey()),
76                &[&mint_keypair],
77                Hash::default(),
78            ));
79
80        assert!(verify_precompiles(&tx, &feature_set).is_ok());
81
82        let index = thread_rng().gen_range(0, secp_instruction.data.len());
83        secp_instruction.data[index] = secp_instruction.data[index].wrapping_add(12);
84        let tx =
85            SanitizedTransaction::from_transaction_for_tests(Transaction::new_signed_with_payer(
86                &[secp_instruction],
87                Some(&mint_keypair.pubkey()),
88                &[&mint_keypair],
89                Hash::default(),
90            ));
91
92        assert!(verify_precompiles(&tx, &feature_set).is_err());
93    }
94
95    #[test]
96    fn test_verify_precompiles_ed25519() {
97        let privkey = ed25519_dalek::Keypair::generate(&mut thread_rng());
98        let message_arr = b"hello";
99        let mut instruction = new_ed25519_instruction(&privkey, message_arr);
100        let mint_keypair = Keypair::new();
101        let feature_set = FeatureSet::all_enabled();
102
103        let tx =
104            SanitizedTransaction::from_transaction_for_tests(Transaction::new_signed_with_payer(
105                &[instruction.clone()],
106                Some(&mint_keypair.pubkey()),
107                &[&mint_keypair],
108                Hash::default(),
109            ));
110
111        assert!(verify_precompiles(&tx, &feature_set).is_ok());
112
113        let index = loop {
114            let index = thread_rng().gen_range(0, instruction.data.len());
115            // byte 1 is not used, so this would not cause the verify to fail
116            if index != 1 {
117                break index;
118            }
119        };
120
121        instruction.data[index] = instruction.data[index].wrapping_add(12);
122        let tx =
123            SanitizedTransaction::from_transaction_for_tests(Transaction::new_signed_with_payer(
124                &[instruction],
125                Some(&mint_keypair.pubkey()),
126                &[&mint_keypair],
127                Hash::default(),
128            ));
129        assert!(verify_precompiles(&tx, &feature_set).is_err());
130    }
131
132    #[test]
133    fn test_verify_precompiles_mixed() {
134        let message_arr = b"hello";
135        let secp_privkey = libsecp256k1::SecretKey::random(&mut thread_rng());
136        let mut secp_instruction = new_secp256k1_instruction(&secp_privkey, message_arr);
137        let ed25519_privkey = ed25519_dalek::Keypair::generate(&mut thread_rng());
138        let ed25519_instruction = new_ed25519_instruction(&ed25519_privkey, message_arr);
139
140        let mint_keypair = Keypair::new();
141        let feature_set = FeatureSet::all_enabled();
142
143        let tx =
144            SanitizedTransaction::from_transaction_for_tests(Transaction::new_signed_with_payer(
145                &[
146                    secp_instruction.clone(),
147                    ed25519_instruction.clone(),
148                    system_instruction::transfer(&mint_keypair.pubkey(), &Pubkey::new_unique(), 1),
149                ],
150                Some(&mint_keypair.pubkey()),
151                &[&mint_keypair],
152                Hash::default(),
153            ));
154        assert!(verify_precompiles(&tx, &feature_set).is_ok());
155
156        let index = thread_rng().gen_range(0, secp_instruction.data.len());
157        secp_instruction.data[index] = secp_instruction.data[index].wrapping_add(12);
158        let tx =
159            SanitizedTransaction::from_transaction_for_tests(Transaction::new_signed_with_payer(
160                &[
161                    secp_instruction,
162                    ed25519_instruction,
163                    system_instruction::transfer(&mint_keypair.pubkey(), &Pubkey::new_unique(), 1),
164                ],
165                Some(&mint_keypair.pubkey()),
166                &[&mint_keypair],
167                Hash::default(),
168            ));
169        assert!(verify_precompiles(&tx, &feature_set).is_err());
170    }
171}