solana_runtime_transaction/
signature_details.rs1use {
3 agave_transaction_view::static_account_keys_frame::MAX_STATIC_ACCOUNTS_PER_PACKET as FILTER_SIZE,
4 solana_pubkey::Pubkey, solana_svm_transaction::instruction::SVMInstruction,
5};
6
7pub struct PrecompileSignatureDetails {
8 pub num_secp256k1_instruction_signatures: u64,
9 pub num_ed25519_instruction_signatures: u64,
10 pub num_secp256r1_instruction_signatures: u64,
11}
12
13pub fn get_precompile_signature_details<'a>(
15 instructions: impl Iterator<Item = (&'a Pubkey, SVMInstruction<'a>)>,
16) -> PrecompileSignatureDetails {
17 let mut filter = SignatureDetailsFilter::new();
18
19 let mut num_secp256k1_instruction_signatures: u64 = 0;
23 let mut num_ed25519_instruction_signatures: u64 = 0;
24 let mut num_secp256r1_instruction_signatures: u64 = 0;
25 for (program_id, instruction) in instructions {
26 let program_id_index = instruction.program_id_index;
27 match filter.is_signature(program_id_index, program_id) {
28 ProgramIdStatus::NotSignature => {}
29 ProgramIdStatus::Secp256k1 => {
30 num_secp256k1_instruction_signatures = num_secp256k1_instruction_signatures
31 .wrapping_add(get_num_signatures_in_instruction(&instruction));
32 }
33 ProgramIdStatus::Ed25519 => {
34 num_ed25519_instruction_signatures = num_ed25519_instruction_signatures
35 .wrapping_add(get_num_signatures_in_instruction(&instruction));
36 }
37 ProgramIdStatus::Secp256r1 => {
38 num_secp256r1_instruction_signatures = num_secp256r1_instruction_signatures
39 .wrapping_add(get_num_signatures_in_instruction(&instruction));
40 }
41 }
42 }
43
44 PrecompileSignatureDetails {
45 num_secp256k1_instruction_signatures,
46 num_ed25519_instruction_signatures,
47 num_secp256r1_instruction_signatures,
48 }
49}
50
51#[inline]
52fn get_num_signatures_in_instruction(instruction: &SVMInstruction) -> u64 {
53 u64::from(instruction.data.first().copied().unwrap_or(0))
54}
55
56#[derive(Copy, Clone)]
57enum ProgramIdStatus {
58 NotSignature,
59 Secp256k1,
60 Ed25519,
61 Secp256r1,
62}
63
64struct SignatureDetailsFilter {
65 flags: [Option<ProgramIdStatus>; FILTER_SIZE as usize],
69}
70
71impl SignatureDetailsFilter {
72 #[inline]
73 fn new() -> Self {
74 Self {
75 flags: [None; FILTER_SIZE as usize],
76 }
77 }
78
79 #[inline]
80 fn is_signature(&mut self, index: u8, program_id: &Pubkey) -> ProgramIdStatus {
81 let flag = &mut self.flags[usize::from(index)];
82 match flag {
83 Some(status) => *status,
84 None => {
85 *flag = Some(Self::check_program_id(program_id));
86 *flag.as_ref().unwrap()
87 }
88 }
89 }
90
91 #[inline]
92 fn check_program_id(program_id: &Pubkey) -> ProgramIdStatus {
93 if program_id == &solana_sdk_ids::secp256k1_program::ID {
94 ProgramIdStatus::Secp256k1
95 } else if program_id == &solana_sdk_ids::ed25519_program::ID {
96 ProgramIdStatus::Ed25519
97 } else if program_id == &solana_sdk_ids::secp256r1_program::ID {
98 ProgramIdStatus::Secp256r1
99 } else {
100 ProgramIdStatus::NotSignature
101 }
102 }
103}
104
105#[cfg(test)]
106mod tests {
107 use super::*;
108
109 fn make_instruction<'a>(
111 program_ids: &'a [Pubkey],
112 program_id_index: u8,
113 data: &'a [u8],
114 ) -> (&'a Pubkey, SVMInstruction<'a>) {
115 (
116 &program_ids[program_id_index as usize],
117 SVMInstruction {
118 program_id_index,
119 accounts: &[],
120 data,
121 },
122 )
123 }
124
125 #[test]
126 fn test_get_signature_details_no_instructions() {
127 let instructions = std::iter::empty();
128 let signature_details = get_precompile_signature_details(instructions);
129
130 assert_eq!(signature_details.num_secp256k1_instruction_signatures, 0);
131 assert_eq!(signature_details.num_ed25519_instruction_signatures, 0);
132 }
133
134 #[test]
135 fn test_get_signature_details_no_sigs_unique() {
136 let program_ids = [Pubkey::new_unique(), Pubkey::new_unique()];
137 let instructions = [
138 make_instruction(&program_ids, 0, &[]),
139 make_instruction(&program_ids, 1, &[]),
140 ];
141
142 let signature_details = get_precompile_signature_details(instructions.into_iter());
143 assert_eq!(signature_details.num_secp256k1_instruction_signatures, 0);
144 assert_eq!(signature_details.num_ed25519_instruction_signatures, 0);
145 }
146
147 #[test]
148 fn test_get_signature_details_signatures_mixed() {
149 let program_ids = [
150 Pubkey::new_unique(),
151 solana_sdk_ids::secp256k1_program::ID,
152 solana_sdk_ids::ed25519_program::ID,
153 solana_sdk_ids::secp256r1_program::ID,
154 ];
155 let instructions = [
156 make_instruction(&program_ids, 1, &[5]),
157 make_instruction(&program_ids, 2, &[3]),
158 make_instruction(&program_ids, 3, &[4]),
159 make_instruction(&program_ids, 0, &[]),
160 make_instruction(&program_ids, 2, &[2]),
161 make_instruction(&program_ids, 1, &[1]),
162 make_instruction(&program_ids, 0, &[]),
163 make_instruction(&program_ids, 3, &[3]),
164 ];
165
166 let signature_details = get_precompile_signature_details(instructions.into_iter());
167 assert_eq!(signature_details.num_secp256k1_instruction_signatures, 6);
168 assert_eq!(signature_details.num_ed25519_instruction_signatures, 5);
169 assert_eq!(signature_details.num_secp256r1_instruction_signatures, 7);
170 }
171
172 #[test]
173 fn test_get_signature_details_missing_num_signatures() {
174 let program_ids = [
175 solana_sdk_ids::secp256k1_program::ID,
176 solana_sdk_ids::ed25519_program::ID,
177 solana_sdk_ids::secp256r1_program::ID,
178 ];
179 let instructions = [
180 make_instruction(&program_ids, 0, &[]),
181 make_instruction(&program_ids, 1, &[]),
182 ];
183
184 let signature_details = get_precompile_signature_details(instructions.into_iter());
185 assert_eq!(signature_details.num_secp256k1_instruction_signatures, 0);
186 assert_eq!(signature_details.num_ed25519_instruction_signatures, 0);
187 assert_eq!(signature_details.num_secp256r1_instruction_signatures, 0);
188 }
189}