1use {
2 crate::{
3 error::IndexerResult, filter::Filter, metrics::MetricsCollection, processor::Processor,
4 transaction::TransactionMetadata,
5 },
6 arch_program::{account::AccountMeta, instruction::Instruction, pubkey::Pubkey},
7 async_trait::async_trait,
8 serde::{Deserialize, Serialize},
9 std::{
10 ops::{Deref, DerefMut},
11 sync::Arc,
12 },
13};
14
15#[derive(Debug, Clone, Deserialize, Serialize)]
16pub struct DecodedInstruction<T> {
17 pub program_id: Pubkey,
18 pub data: T,
19 pub accounts: Vec<AccountMeta>,
20}
21
22pub trait InstructionDecoder<'a> {
28 type InstructionType;
29
30 fn decode_instruction(
31 &self,
32 instruction: &'a Instruction,
33 ) -> Option<DecodedInstruction<Self::InstructionType>>;
34}
35
36pub type InstructionProcessorInputType<T> = (DecodedInstruction<T>, NestedInstruction);
37
38pub struct InstructionPipe<T: Send> {
39 pub decoder:
40 Box<dyn for<'a> InstructionDecoder<'a, InstructionType = T> + Send + Sync + 'static>,
41 pub processor: Box<
42 dyn Processor<InputType = InstructionProcessorInputType<T>, OutputType = ()>
43 + Send
44 + Sync
45 + 'static,
46 >,
47 pub filters: Vec<Box<dyn Filter + Send + Sync + 'static>>,
48}
49
50#[async_trait]
51pub trait InstructionPipes<'a>: Send + Sync {
57 async fn run(
58 &mut self,
59 instruction: &Vec<NestedInstruction>,
60 metrics: Arc<MetricsCollection>,
61 ) -> IndexerResult<()>;
62 fn filters(&self) -> &Vec<Box<dyn Filter + Send + Sync + 'static>>;
63}
64
65#[async_trait]
66impl<T: Send + 'static> InstructionPipes<'_> for InstructionPipe<T> {
67 async fn run(
68 &mut self,
69 nested_instructions: &Vec<NestedInstruction>,
70 metrics: Arc<MetricsCollection>,
71 ) -> IndexerResult<()> {
72 log::trace!(
73 "InstructionPipe::run(instructions: {:?}, metrics)",
74 nested_instructions,
75 );
76
77 let mut decoded_instructions: Vec<InstructionProcessorInputType<T>> = Vec::new();
78 for nested_instruction in nested_instructions {
79 if let Some(decoded_instruction) = self
80 .decoder
81 .decode_instruction(&nested_instruction.instruction)
82 {
83 decoded_instructions.push((decoded_instruction, nested_instruction.clone()));
84 }
85
86 for nested_inner_instruction in nested_instruction.inner_instructions.iter() {
87 if let Some(decoded_instruction) = self
88 .decoder
89 .decode_instruction(&nested_inner_instruction.instruction)
90 {
91 decoded_instructions
92 .push((decoded_instruction, nested_inner_instruction.clone()));
93 }
94 }
95 }
96
97 self.processor
98 .process(decoded_instructions, metrics.clone())
99 .await?;
100
101 Ok(())
102 }
103
104 fn filters(&self) -> &Vec<Box<dyn Filter + Send + Sync + 'static>> {
105 &self.filters
106 }
107}
108
109#[derive(Debug, Default)]
110pub struct Instructions(pub Vec<Instruction>);
111
112impl Instructions {
113 pub fn len(&self) -> usize {
115 self.0.len()
116 }
117
118 pub fn is_empty(&self) -> bool {
120 self.len() == 0
121 }
122
123 pub fn push(&mut self, instruction: Instruction) {
125 self.0.push(instruction);
126 }
127}
128
129impl Deref for Instructions {
130 type Target = [Instruction];
131
132 fn deref(&self) -> &[Instruction] {
133 &self.0[..]
134 }
135}
136
137impl DerefMut for Instructions {
138 fn deref_mut(&mut self) -> &mut [Instruction] {
139 &mut self.0[..]
140 }
141}
142
143impl Clone for Instructions {
144 fn clone(&self) -> Self {
145 Instructions(self.0.clone())
146 }
147}
148
149impl IntoIterator for Instructions {
150 type Item = Instruction;
151 type IntoIter = std::vec::IntoIter<Instruction>;
152
153 fn into_iter(self) -> Self::IntoIter {
154 self.0.into_iter()
155 }
156}
157
158#[derive(Debug, Clone)]
159pub struct InstructionMetadata {
160 pub transaction_metadata: Arc<TransactionMetadata>,
161 pub stack_height: u32,
162 pub index: u32,
163 pub absolute_path: Vec<u8>,
164}
165
166pub type InstructionsWithMetadata = Vec<(InstructionMetadata, Instruction)>;
167
168#[derive(Debug, Clone)]
182pub struct NestedInstruction {
183 pub metadata: InstructionMetadata,
184 pub instruction: Instruction,
185 pub inner_instructions: NestedInstructions,
186}
187
188#[derive(Debug, Default)]
189pub struct NestedInstructions(pub Vec<NestedInstruction>);
190
191impl NestedInstructions {
192 pub fn len(&self) -> usize {
193 self.0.len()
194 }
195
196 pub fn is_empty(&self) -> bool {
197 self.len() == 0
198 }
199
200 pub fn push(&mut self, nested_instruction: NestedInstruction) {
201 self.0.push(nested_instruction);
202 }
203}
204
205impl Deref for NestedInstructions {
206 type Target = [NestedInstruction];
207
208 fn deref(&self) -> &[NestedInstruction] {
209 &self.0[..]
210 }
211}
212
213impl DerefMut for NestedInstructions {
214 fn deref_mut(&mut self) -> &mut [NestedInstruction] {
215 &mut self.0[..]
216 }
217}
218
219impl Clone for NestedInstructions {
220 fn clone(&self) -> Self {
221 NestedInstructions(self.0.clone())
222 }
223}
224
225impl IntoIterator for NestedInstructions {
226 type Item = NestedInstruction;
227 type IntoIter = std::vec::IntoIter<NestedInstruction>;
228
229 fn into_iter(self) -> Self::IntoIter {
230 self.0.into_iter()
231 }
232}
233
234impl From<InstructionsWithMetadata> for NestedInstructions {
251 fn from(instructions: InstructionsWithMetadata) -> Self {
252 log::trace!("from(instructions: {:?})", instructions);
253
254 let estimated_capacity = instructions
257 .iter()
258 .filter(|(meta, _)| meta.stack_height == 1)
259 .count();
260
261 UnsafeNestedBuilder::new(estimated_capacity).build(instructions)
262 }
263}
264
265pub const MAX_INSTRUCTION_STACK_DEPTH: usize = 5;
267
268pub struct UnsafeNestedBuilder {
269 nested_ixs: Vec<NestedInstruction>,
270 level_ptrs: [Option<*mut NestedInstruction>; MAX_INSTRUCTION_STACK_DEPTH],
271}
272
273impl UnsafeNestedBuilder {
274 pub fn new(capacity: usize) -> Self {
278 Self {
279 nested_ixs: Vec::with_capacity(capacity),
280 level_ptrs: [None; MAX_INSTRUCTION_STACK_DEPTH],
281 }
282 }
283
284 pub fn build(mut self, instructions: InstructionsWithMetadata) -> NestedInstructions {
285 for (metadata, instruction) in instructions {
286 let stack_height = metadata.stack_height as usize;
287
288 assert!(stack_height > 0);
289 assert!(stack_height <= MAX_INSTRUCTION_STACK_DEPTH);
290
291 for ptr in &mut self.level_ptrs[stack_height..] {
292 *ptr = None;
293 }
294
295 let new_instruction = NestedInstruction {
296 metadata,
297 instruction,
298 inner_instructions: NestedInstructions::default(),
299 };
300
301 unsafe {
307 if stack_height == 1 {
308 self.nested_ixs.push(new_instruction);
309 let ptr = self.nested_ixs.last_mut().unwrap_unchecked() as *mut _;
310 self.level_ptrs[0] = Some(ptr);
311 } else if let Some(parent_ptr) = self.level_ptrs[stack_height - 2] {
312 (*parent_ptr).inner_instructions.push(new_instruction);
313 let ptr = (*parent_ptr)
314 .inner_instructions
315 .last_mut()
316 .unwrap_unchecked() as *mut _;
317 self.level_ptrs[stack_height - 1] = Some(ptr);
318 }
319 }
320 }
321
322 NestedInstructions(self.nested_ixs)
323 }
324}
325
326#[cfg(test)]
327mod tests {
328
329 use {
330 super::*,
331 arch_program::sanitized::ArchMessage,
332 arch_sdk::{RollbackStatus, Status},
333 Instruction,
334 };
335
336 fn create_instruction_with_metadata(
337 index: u32,
338 stack_height: u32,
339 ) -> (InstructionMetadata, Instruction) {
340 let metadata = InstructionMetadata {
341 transaction_metadata: Arc::new(TransactionMetadata {
342 id: String::from(" "),
343 fee_payer: Pubkey::default(),
344 message: ArchMessage::default(),
345 status: Status::Processed,
346 log_messages: vec![],
347 rollback_status: RollbackStatus::NotRolledback,
348 block_height: 0,
349 bitcoin_txid: None,
350 bitcoin_tx: None,
351 inner_instructions_list: vec![],
352 }),
353 stack_height,
354 index,
355 absolute_path: vec![],
356 };
357 let instruction = Instruction {
358 program_id: Pubkey::new_unique(),
359 accounts: vec![AccountMeta::new(Pubkey::new_unique(), false)],
360 data: vec![],
361 };
362 (metadata, instruction)
363 }
364
365 #[test]
366 fn test_nested_instructions_single_level() {
367 let instructions = vec![
368 create_instruction_with_metadata(1, 1),
369 create_instruction_with_metadata(2, 1),
370 ];
371 let nested_instructions: NestedInstructions = instructions.into();
372 assert_eq!(nested_instructions.len(), 2);
373 assert!(nested_instructions[0].inner_instructions.is_empty());
374 assert!(nested_instructions[1].inner_instructions.is_empty());
375 }
376
377 #[test]
378 fn test_nested_instructions_empty() {
379 let instructions: InstructionsWithMetadata = vec![];
380 let nested_instructions: NestedInstructions = instructions.into();
381 assert!(nested_instructions.is_empty());
382 }
383
384 #[test]
385 fn test_deep_nested_instructions() {
386 let instructions = vec![
387 create_instruction_with_metadata(0, 1),
388 create_instruction_with_metadata(0, 1),
389 create_instruction_with_metadata(1, 2),
390 create_instruction_with_metadata(1, 3),
391 create_instruction_with_metadata(1, 3),
392 create_instruction_with_metadata(1, 3),
393 ];
394
395 let nested_instructions: NestedInstructions = instructions.into();
396 assert_eq!(nested_instructions.len(), 2);
397 assert_eq!(nested_instructions.0[1].inner_instructions.len(), 1);
398 }
399}