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