1#![cfg(feature = "agave-unstable-api")]
2#![cfg_attr(docsrs, feature(doc_cfg))]
3use {
4 agave_feature_set::FeatureSet, solana_message::compiled_instruction::CompiledInstruction,
5 solana_precompile_error::PrecompileError, solana_pubkey::Pubkey, std::sync::LazyLock,
6};
7
8pub mod ed25519;
9pub mod secp256k1;
10pub mod secp256r1;
11
12pub type Verify = fn(&[u8], &[&[u8]], &FeatureSet) -> std::result::Result<(), PrecompileError>;
14
15pub struct Precompile {
17 pub program_id: Pubkey,
19 pub feature: Option<Pubkey>,
21 pub verify_fn: Verify,
23}
24impl Precompile {
25 pub fn new(program_id: Pubkey, feature: Option<Pubkey>, verify_fn: Verify) -> Self {
27 Precompile {
28 program_id,
29 feature,
30 verify_fn,
31 }
32 }
33 pub fn check_id<F>(&self, program_id: &Pubkey, is_enabled: F) -> bool
35 where
36 F: Fn(&Pubkey) -> bool,
37 {
38 self.feature
39 .is_none_or(|ref feature_id| is_enabled(feature_id))
40 && self.program_id == *program_id
41 }
42 pub fn verify(
44 &self,
45 data: &[u8],
46 instruction_datas: &[&[u8]],
47 feature_set: &FeatureSet,
48 ) -> std::result::Result<(), PrecompileError> {
49 (self.verify_fn)(data, instruction_datas, feature_set)
50 }
51}
52
53static PRECOMPILES: LazyLock<Vec<Precompile>> = LazyLock::new(|| {
55 vec![
56 Precompile::new(
57 solana_sdk_ids::secp256k1_program::id(),
58 None, secp256k1::verify,
60 ),
61 Precompile::new(
62 solana_sdk_ids::ed25519_program::id(),
63 None, ed25519::verify,
65 ),
66 Precompile::new(
67 solana_sdk_ids::secp256r1_program::id(),
68 None, secp256r1::verify,
70 ),
71 ]
72});
73
74pub fn is_precompile<F>(program_id: &Pubkey, is_enabled: F) -> bool
76where
77 F: Fn(&Pubkey) -> bool,
78{
79 PRECOMPILES
80 .iter()
81 .any(|precompile| precompile.check_id(program_id, |feature_id| is_enabled(feature_id)))
82}
83
84pub fn get_precompile<F>(program_id: &Pubkey, is_enabled: F) -> Option<&Precompile>
86where
87 F: Fn(&Pubkey) -> bool,
88{
89 PRECOMPILES
90 .iter()
91 .find(|precompile| precompile.check_id(program_id, |feature_id| is_enabled(feature_id)))
92}
93
94pub fn get_precompiles<'a>() -> &'a [Precompile] {
95 &PRECOMPILES
96}
97
98pub fn verify_if_precompile(
100 program_id: &Pubkey,
101 precompile_instruction: &CompiledInstruction,
102 all_instructions: &[CompiledInstruction],
103 feature_set: &FeatureSet,
104) -> Result<(), PrecompileError> {
105 for precompile in PRECOMPILES.iter() {
106 if precompile.check_id(program_id, |feature_id| feature_set.is_active(feature_id)) {
107 let instruction_datas: Vec<_> = all_instructions
108 .iter()
109 .map(|instruction| instruction.data.as_ref())
110 .collect();
111 return precompile.verify(
112 &precompile_instruction.data,
113 &instruction_datas,
114 feature_set,
115 );
116 }
117 }
118 Ok(())
119}
120
121#[cfg(test)]
122pub(crate) fn test_verify_with_alignment(
123 verify: Verify,
124 instruction_data: &[u8],
125 instruction_datas: &[&[u8]],
126 feature_set: &FeatureSet,
127) -> Result<(), PrecompileError> {
128 let mut instruction_data_copy = vec![0u8; instruction_data.len().checked_add(1).unwrap()];
130 instruction_data_copy[0..instruction_data.len()].copy_from_slice(instruction_data);
131 let result = verify(
133 &instruction_data_copy[..instruction_data.len()],
134 instruction_datas,
135 feature_set,
136 );
137
138 instruction_data_copy[1..].copy_from_slice(instruction_data);
140 let result_shifted = verify(&instruction_data_copy[1..], instruction_datas, feature_set);
141 assert_eq!(result, result_shifted);
142 result
143}