light_program_test/indexer/
test_indexer.rs

1use std::{fmt::Debug, time::Duration};
2
3use account_compression::{
4    AddressMerkleTreeConfig, AddressQueueConfig, NullifierQueueConfig, StateMerkleTreeConfig,
5};
6use async_trait::async_trait;
7use borsh::BorshDeserialize;
8use light_batched_merkle_tree::{
9    constants::{
10        DEFAULT_BATCH_ADDRESS_TREE_HEIGHT, DEFAULT_BATCH_ROOT_HISTORY_LEN,
11        DEFAULT_BATCH_STATE_TREE_HEIGHT,
12    },
13    merkle_tree::BatchedMerkleTreeAccount,
14};
15use light_client::{
16    fee::FeeConfig,
17    indexer::{
18        AccountProofInputs, Address, AddressMerkleTreeAccounts, AddressProofInputs,
19        AddressWithTree, BatchAddressUpdateIndexerResponse, CompressedAccount,
20        CompressedTokenAccount, Context, GetCompressedAccountsByOwnerConfig,
21        GetCompressedTokenAccountsByOwnerOrDelegateOptions, Indexer, IndexerError,
22        IndexerRpcConfig, Items, ItemsWithCursor, MerkleProof, MerkleProofWithContext,
23        NewAddressProofWithContext, OwnerBalance, PaginatedOptions, QueueElementsResult, Response,
24        RetryConfig, RootIndex, SignatureWithMetadata, StateMerkleTreeAccounts, TokenBalance,
25        ValidityProofWithContext,
26    },
27    rpc::{Rpc, RpcError},
28};
29use light_compressed_account::{
30    compressed_account::{CompressedAccountWithMerkleContext, MerkleContext},
31    hash_chain::create_hash_chain_from_slice,
32    indexer_event::event::PublicTransactionEvent,
33    instruction_data::compressed_proof::CompressedProof,
34    tx_hash::create_tx_hash,
35    TreeType,
36};
37use light_hasher::{bigint::bigint_to_be_bytes_array, Poseidon};
38use light_merkle_tree_metadata::QueueType;
39use light_merkle_tree_reference::MerkleTree;
40use light_prover_client::{
41    constants::{PROVE_PATH, SERVER_ADDRESS},
42    helpers::{big_int_to_string, bigint_to_u8_32, string_to_big_int},
43    proof::{compress_proof, deserialize_gnark_proof_json, proof_from_json_struct},
44    proof_type::ProofType,
45    proof_types::{
46        combined::{v1::CombinedJsonStruct as CombinedJsonStructLegacy, v2::CombinedJsonStruct},
47        inclusion::{
48            v1::{
49                BatchInclusionJsonStruct as BatchInclusionJsonStructLegacy,
50                InclusionProofInputs as InclusionProofInputsLegacy,
51            },
52            v2::{BatchInclusionJsonStruct, InclusionMerkleProofInputs, InclusionProofInputs},
53        },
54        non_inclusion::{
55            v1::{
56                BatchNonInclusionJsonStruct as BatchNonInclusionJsonStructLegacy,
57                NonInclusionProofInputs as NonInclusionProofInputsLegacy,
58            },
59            v2::{BatchNonInclusionJsonStruct, NonInclusionProofInputs},
60        },
61    },
62};
63use light_sdk::{
64    light_hasher::Hash,
65    token::{TokenData, TokenDataWithMerkleContext},
66};
67use log::info;
68use num_bigint::{BigInt, BigUint};
69use num_traits::FromBytes;
70use reqwest::Client;
71use solana_sdk::{
72    bs58,
73    pubkey::Pubkey,
74    signature::{Keypair, Signer},
75};
76
77use super::{
78    address_tree::{AddressMerkleTreeBundle, IndexedMerkleTreeVersion},
79    state_tree::{LeafIndexInfo, StateMerkleTreeBundle},
80};
81#[cfg(feature = "devenv")]
82use crate::accounts::{
83    address_tree_v2::create_batch_address_merkle_tree,
84    state_tree_v2::create_batched_state_merkle_tree,
85};
86use crate::{
87    accounts::{
88        address_tree::create_address_merkle_tree_and_queue_account,
89        state_tree::create_state_merkle_tree_and_queue_account, test_accounts::TestAccounts,
90    },
91    indexer::TestIndexerExtensions,
92};
93
94#[derive(Debug)]
95pub struct TestIndexer {
96    pub state_merkle_trees: Vec<StateMerkleTreeBundle>,
97    pub address_merkle_trees: Vec<AddressMerkleTreeBundle>,
98    pub payer: Keypair,
99    pub group_pda: Pubkey,
100    pub compressed_accounts: Vec<CompressedAccountWithMerkleContext>,
101    pub nullified_compressed_accounts: Vec<CompressedAccountWithMerkleContext>,
102    pub token_compressed_accounts: Vec<TokenDataWithMerkleContext>,
103    pub token_nullified_compressed_accounts: Vec<TokenDataWithMerkleContext>,
104    pub events: Vec<PublicTransactionEvent>,
105}
106
107impl Clone for TestIndexer {
108    fn clone(&self) -> Self {
109        Self {
110            state_merkle_trees: self.state_merkle_trees.clone(),
111            address_merkle_trees: self.address_merkle_trees.clone(),
112            payer: self.payer.insecure_clone(),
113            group_pda: self.group_pda,
114            compressed_accounts: self.compressed_accounts.clone(),
115            nullified_compressed_accounts: self.nullified_compressed_accounts.clone(),
116            token_compressed_accounts: self.token_compressed_accounts.clone(),
117            token_nullified_compressed_accounts: self.token_nullified_compressed_accounts.clone(),
118            events: self.events.clone(),
119        }
120    }
121}
122
123#[async_trait]
124impl Indexer for TestIndexer {
125    // TODO: add slot to test indexer struct
126    async fn get_indexer_slot(&self, _config: Option<RetryConfig>) -> Result<u64, IndexerError> {
127        // test indexer is always up to date
128        Ok(u64::MAX)
129    }
130
131    async fn get_multiple_compressed_account_proofs(
132        &self,
133        hashes: Vec<[u8; 32]>,
134        _config: Option<IndexerRpcConfig>,
135    ) -> Result<Response<Items<MerkleProof>>, IndexerError> {
136        info!("Getting proofs for {:?}", hashes);
137        let mut proofs: Vec<MerkleProof> = Vec::new();
138        hashes.iter().for_each(|hash| {
139            self.state_merkle_trees.iter().for_each(|tree| {
140                if let Some(leaf_index) = tree.merkle_tree.get_leaf_index(hash) {
141                    let proof = tree
142                        .merkle_tree
143                        .get_proof_of_leaf(leaf_index, false)
144                        .unwrap();
145                    proofs.push(MerkleProof {
146                        hash: *hash,
147                        leaf_index: leaf_index as u64,
148                        merkle_tree: tree.accounts.merkle_tree,
149                        proof: proof.to_vec(),
150                        root_seq: tree.merkle_tree.sequence_number as u64,
151                        root: *tree.merkle_tree.roots.last().unwrap(),
152                    });
153                }
154            })
155        });
156        Ok(Response {
157            context: Context {
158                slot: self.get_current_slot(),
159            },
160            value: Items { items: proofs },
161        })
162    }
163
164    async fn get_compressed_accounts_by_owner(
165        &self,
166        owner: &Pubkey,
167        _options: Option<GetCompressedAccountsByOwnerConfig>,
168        _config: Option<IndexerRpcConfig>,
169    ) -> Result<Response<ItemsWithCursor<CompressedAccount>>, IndexerError> {
170        let accounts_with_context = <TestIndexer as TestIndexerExtensions>::get_compressed_accounts_with_merkle_context_by_owner(self, owner);
171        let accounts: Result<Vec<CompressedAccount>, IndexerError> = accounts_with_context
172            .into_iter()
173            .map(|acc| acc.try_into())
174            .collect();
175
176        Ok(Response {
177            context: Context {
178                slot: self.get_current_slot(),
179            },
180            value: ItemsWithCursor {
181                items: accounts?,
182                cursor: None,
183            },
184        })
185    }
186
187    async fn get_compressed_account(
188        &self,
189        address: Address,
190        _config: Option<IndexerRpcConfig>,
191    ) -> Result<Response<Option<CompressedAccount>>, IndexerError> {
192        let account = self
193            .compressed_accounts
194            .iter()
195            .find(|acc| acc.compressed_account.address == Some(address));
196
197        let account_data = match account {
198            Some(acc) => Some(acc.clone().try_into()?),
199            None => None,
200        };
201
202        Ok(Response {
203            context: Context {
204                slot: self.get_current_slot(),
205            },
206            value: account_data,
207        })
208    }
209
210    async fn get_compressed_account_by_hash(
211        &self,
212        hash: Hash,
213        _config: Option<IndexerRpcConfig>,
214    ) -> Result<Response<Option<CompressedAccount>>, IndexerError> {
215        let res = self
216            .compressed_accounts
217            .iter()
218            .find(|acc| acc.hash() == Ok(hash));
219
220        // TODO: unify token accounts with compressed accounts.
221        let account = if res.is_none() {
222            let res = self
223                .token_compressed_accounts
224                .iter()
225                .find(|acc| acc.compressed_account.hash() == Ok(hash));
226            res.map(|x| &x.compressed_account)
227        } else {
228            res
229        };
230
231        let account_data = match account {
232            Some(acc) => Some(acc.clone().try_into()?),
233            None => None,
234        };
235
236        Ok(Response {
237            context: Context {
238                slot: self.get_current_slot(),
239            },
240            value: account_data,
241        })
242    }
243
244    async fn get_compressed_token_accounts_by_owner(
245        &self,
246        owner: &Pubkey,
247        options: Option<GetCompressedTokenAccountsByOwnerOrDelegateOptions>,
248        _config: Option<IndexerRpcConfig>,
249    ) -> Result<Response<ItemsWithCursor<CompressedTokenAccount>>, IndexerError> {
250        let mint = options.as_ref().and_then(|opts| opts.mint);
251        let token_accounts: Result<Vec<CompressedTokenAccount>, IndexerError> = self
252            .token_compressed_accounts
253            .iter()
254            .filter(|acc| {
255                acc.token_data.owner == *owner && mint.is_none_or(|m| acc.token_data.mint == m)
256            })
257            .map(|acc| CompressedTokenAccount::try_from(acc.clone()))
258            .collect();
259        let token_accounts = token_accounts?;
260        let token_accounts = if let Some(options) = options {
261            if let Some(limit) = options.limit {
262                token_accounts.into_iter().take(limit as usize).collect()
263            } else {
264                token_accounts
265            }
266        } else {
267            token_accounts
268        };
269
270        Ok(Response {
271            context: Context {
272                slot: self.get_current_slot(),
273            },
274            value: ItemsWithCursor {
275                items: token_accounts,
276                cursor: None,
277            },
278        })
279    }
280
281    async fn get_compressed_balance(
282        &self,
283        address: Option<Address>,
284        hash: Option<Hash>,
285        _config: Option<IndexerRpcConfig>,
286    ) -> Result<Response<u64>, IndexerError> {
287        let account_response = match (address, hash) {
288            (Some(addr), _) => self.get_compressed_account(addr, None).await?,
289            (_, Some(h)) => self.get_compressed_account_by_hash(h, None).await?,
290            _ => {
291                return Err(IndexerError::InvalidParameters(
292                    "Either address or hash must be provided".to_string(),
293                ))
294            }
295        };
296        let account = account_response
297            .value
298            .ok_or(IndexerError::AccountNotFound)?;
299        Ok(Response {
300            context: Context {
301                slot: self.get_current_slot(),
302            },
303            value: account.lamports,
304        })
305    }
306
307    async fn get_compressed_token_account_balance(
308        &self,
309        address: Option<Address>,
310        hash: Option<Hash>,
311        _config: Option<IndexerRpcConfig>,
312    ) -> Result<Response<u64>, IndexerError> {
313        let account = match (address, hash) {
314            (Some(address), _) => self
315                .token_compressed_accounts
316                .iter()
317                .find(|acc| acc.compressed_account.compressed_account.address == Some(address)),
318            (_, Some(hash)) => self
319                .token_compressed_accounts
320                .iter()
321                .find(|acc| acc.compressed_account.hash() == Ok(hash)),
322            (None, None) => {
323                return Err(IndexerError::InvalidParameters(
324                    "Either address or hash must be provided".to_string(),
325                ))
326            }
327        };
328
329        let amount = account
330            .map(|acc| acc.token_data.amount)
331            .ok_or(IndexerError::AccountNotFound)?;
332
333        Ok(Response {
334            context: Context {
335                slot: self.get_current_slot(),
336            },
337            value: amount,
338        })
339    }
340
341    async fn get_multiple_compressed_accounts(
342        &self,
343        addresses: Option<Vec<Address>>,
344        hashes: Option<Vec<Hash>>,
345        _config: Option<IndexerRpcConfig>,
346    ) -> Result<Response<Items<Option<CompressedAccount>>>, IndexerError> {
347        match (addresses, hashes) {
348            (Some(addresses), _) => {
349                let accounts: Result<Vec<Option<CompressedAccount>>, IndexerError> = addresses
350                    .iter()
351                    .map(|addr| {
352                        self.compressed_accounts
353                            .iter()
354                            .find(|acc| acc.compressed_account.address == Some(*addr))
355                            .map(|acc| acc.clone().try_into())
356                            .transpose()
357                    })
358                    .collect();
359                Ok(Response {
360                    context: Context {
361                        slot: self.get_current_slot(),
362                    },
363                    value: Items { items: accounts? },
364                })
365            }
366            (_, Some(hashes)) => {
367                let accounts: Result<Vec<Option<CompressedAccount>>, IndexerError> = hashes
368                    .iter()
369                    .map(|hash| {
370                        self.compressed_accounts
371                            .iter()
372                            .find(|acc| acc.hash() == Ok(*hash))
373                            .map(|acc| acc.clone().try_into())
374                            .transpose()
375                    })
376                    .collect();
377                Ok(Response {
378                    context: Context {
379                        slot: self.get_current_slot(),
380                    },
381                    value: Items { items: accounts? },
382                })
383            }
384            (None, None) => Err(IndexerError::InvalidParameters(
385                "Either addresses or hashes must be provided".to_string(),
386            )),
387        }
388    }
389
390    async fn get_compressed_token_balances_by_owner_v2(
391        &self,
392        owner: &Pubkey,
393        _options: Option<GetCompressedTokenAccountsByOwnerOrDelegateOptions>,
394        _config: Option<IndexerRpcConfig>,
395    ) -> Result<Response<ItemsWithCursor<TokenBalance>>, IndexerError> {
396        let mint = _options.as_ref().and_then(|opts| opts.mint);
397        let balances: Vec<TokenBalance> = self
398            .token_compressed_accounts
399            .iter()
400            .filter(|acc| {
401                acc.token_data.owner == *owner && mint.is_none_or(|m| acc.token_data.mint == m)
402            })
403            .fold(std::collections::HashMap::new(), |mut map, acc| {
404                *map.entry(acc.token_data.mint).or_insert(0) += acc.token_data.amount;
405                map
406            })
407            .into_iter()
408            .map(|(mint, balance)| TokenBalance { balance, mint })
409            .collect();
410
411        Ok(Response {
412            context: Context {
413                slot: self.get_current_slot(),
414            },
415            value: ItemsWithCursor {
416                items: balances,
417                cursor: None,
418            },
419        })
420    }
421
422    async fn get_compression_signatures_for_account(
423        &self,
424        _hash: Hash,
425        _config: Option<IndexerRpcConfig>,
426    ) -> Result<Response<Items<SignatureWithMetadata>>, IndexerError> {
427        todo!()
428    }
429
430    async fn get_multiple_new_address_proofs(
431        &self,
432        merkle_tree_pubkey: [u8; 32],
433        addresses: Vec<[u8; 32]>,
434        _config: Option<IndexerRpcConfig>,
435    ) -> Result<Response<Items<NewAddressProofWithContext>>, IndexerError> {
436        let proofs = self
437            ._get_multiple_new_address_proofs(merkle_tree_pubkey, addresses, false)
438            .await?;
439        Ok(Response {
440            context: Context {
441                slot: self.get_current_slot(),
442            },
443            value: Items { items: proofs },
444        })
445    }
446
447    async fn get_validity_proof(
448        &self,
449        hashes: Vec<[u8; 32]>,
450        new_addresses_with_trees: Vec<AddressWithTree>,
451        _config: Option<IndexerRpcConfig>,
452    ) -> Result<Response<ValidityProofWithContext>, IndexerError> {
453        #[cfg(feature = "v2")]
454        {
455            // V2 implementation with queue handling
456            let mut state_merkle_tree_pubkeys = Vec::new();
457
458            for hash in hashes.iter() {
459                let account = self.get_compressed_account_by_hash(*hash, None).await?;
460                let account_data = account.value.ok_or(IndexerError::AccountNotFound)?;
461                state_merkle_tree_pubkeys.push(account_data.tree_info.tree);
462            }
463            let mut proof_inputs = vec![];
464
465            let mut indices_to_remove = Vec::new();
466            // for all accounts in batched trees, check whether values are in tree or queue
467            let compressed_accounts = if !hashes.is_empty() && !state_merkle_tree_pubkeys.is_empty()
468            {
469                let zipped_accounts = hashes.iter().zip(state_merkle_tree_pubkeys.iter());
470
471                for (i, (compressed_account, state_merkle_tree_pubkey)) in
472                    zipped_accounts.enumerate()
473                {
474                    let accounts = self.state_merkle_trees.iter().find(|x| {
475                        x.accounts.merkle_tree == *state_merkle_tree_pubkey
476                            && x.tree_type == TreeType::StateV2
477                    });
478
479                    if let Some(accounts) = accounts {
480                        let queue_element = accounts
481                            .output_queue_elements
482                            .iter()
483                            .find(|(hash, _)| hash == compressed_account);
484                        if let Some((_, index)) = queue_element {
485                            if accounts.output_queue_batch_size.is_some()
486                                && accounts.leaf_index_in_queue_range(*index as usize)?
487                            {
488                                use light_client::indexer::RootIndex;
489
490                                indices_to_remove.push(i);
491                                proof_inputs.push(AccountProofInputs {
492                                    hash: *compressed_account,
493                                    root: [0u8; 32],
494                                    root_index: RootIndex::new_none(),
495                                    leaf_index: accounts
496                                        .output_queue_elements
497                                        .iter()
498                                        .position(|(x, _)| x == compressed_account)
499                                        .unwrap()
500                                        as u64,
501                                    tree_info: light_client::indexer::TreeInfo {
502                                        cpi_context: Some(accounts.accounts.cpi_context),
503                                        tree: accounts.accounts.merkle_tree,
504                                        queue: accounts.accounts.nullifier_queue,
505                                        next_tree_info: None,
506                                        tree_type: accounts.tree_type,
507                                    },
508                                })
509                            }
510                        }
511                    }
512                }
513
514                let compress_accounts = hashes
515                    .iter()
516                    .enumerate()
517                    .filter(|(i, _)| !indices_to_remove.contains(i))
518                    .map(|(_, x)| *x)
519                    .collect::<Vec<[u8; 32]>>();
520
521                if compress_accounts.is_empty() {
522                    None
523                } else {
524                    Some(compress_accounts)
525                }
526            } else {
527                None
528            };
529
530            // Get the basic validity proof if needed
531            let rpc_result: Option<ValidityProofWithContext> = if (compressed_accounts.is_some()
532                && !compressed_accounts.as_ref().unwrap().is_empty())
533                || !new_addresses_with_trees.is_empty()
534            {
535                Some(
536                    self._get_validity_proof_v1_implementation(
537                        compressed_accounts.unwrap_or_default(),
538                        new_addresses_with_trees,
539                    )
540                    .await?,
541                )
542            } else {
543                None
544            };
545
546            // Handle root indices with queue considerations
547            let addresses = if let Some(rpc_result) = rpc_result.as_ref() {
548                rpc_result.addresses.to_vec()
549            } else {
550                Vec::new()
551            };
552            let accounts = {
553                let mut root_indices = if let Some(rpc_result) = rpc_result.as_ref() {
554                    rpc_result.accounts.to_vec()
555                } else {
556                    Vec::new()
557                };
558                #[cfg(debug_assertions)]
559                {
560                    if std::env::var("RUST_BACKTRACE").is_ok() {
561                        println!("get_validit_proof: rpc_result {:?}", rpc_result);
562                    }
563                }
564
565                // Reinsert proof_inputs at their original positions in forward order
566                for (proof_input, &index) in proof_inputs.iter().zip(indices_to_remove.iter()) {
567                    if root_indices.len() <= index {
568                        root_indices.push(proof_input.clone());
569                    } else {
570                        root_indices.insert(index, proof_input.clone());
571                    }
572                }
573                root_indices
574            };
575
576            Ok(Response {
577                context: Context {
578                    slot: self.get_current_slot(),
579                },
580                value: ValidityProofWithContext {
581                    accounts,
582                    addresses,
583                    proof: rpc_result
584                        .map(|rpc_result| rpc_result.proof.0.unwrap())
585                        .into(),
586                },
587            })
588        }
589
590        #[cfg(not(feature = "v2"))]
591        {
592            // V1 implementation - direct call to V1 logic
593            let result = self
594                ._get_validity_proof_v1_implementation(hashes, new_addresses_with_trees)
595                .await?;
596            Ok(Response {
597                context: Context {
598                    slot: self.get_current_slot(),
599                },
600                value: result,
601            })
602        }
603    }
604
605    async fn get_queue_elements(
606        &mut self,
607        _merkle_tree_pubkey: [u8; 32],
608        _queue_type: QueueType,
609        _num_elements: u16,
610        _start_offset: Option<u64>,
611        _config: Option<IndexerRpcConfig>,
612    ) -> Result<Response<QueueElementsResult>, IndexerError> {
613        #[cfg(not(feature = "v2"))]
614        unimplemented!("get_queue_elements");
615        #[cfg(feature = "v2")]
616        {
617            let merkle_tree_pubkey = _merkle_tree_pubkey;
618            let queue_type = _queue_type;
619            let num_elements = _num_elements;
620            let pubkey = Pubkey::new_from_array(merkle_tree_pubkey);
621            let address_tree_bundle = self
622                .address_merkle_trees
623                .iter()
624                .find(|x| x.accounts.merkle_tree == pubkey);
625            if let Some(address_tree_bundle) = address_tree_bundle {
626                let end_offset = std::cmp::min(
627                    num_elements as usize,
628                    address_tree_bundle.queue_elements.len(),
629                );
630                let queue_elements = address_tree_bundle.queue_elements[0..end_offset].to_vec();
631
632                let merkle_proofs_with_context = queue_elements
633                    .iter()
634                    .map(|element| MerkleProofWithContext {
635                        proof: Vec::new(),
636                        leaf: [0u8; 32],
637                        leaf_index: 0,
638                        merkle_tree: address_tree_bundle.accounts.merkle_tree.to_bytes(),
639                        root: address_tree_bundle.root(),
640                        tx_hash: None,
641                        root_seq: 0,
642                        account_hash: *element,
643                    })
644                    .collect();
645                return Ok(Response {
646                    context: Context {
647                        slot: self.get_current_slot(),
648                    },
649                    value: QueueElementsResult {
650                        elements: merkle_proofs_with_context,
651                        first_value_queue_index: None,
652                    },
653                });
654            }
655
656            let state_tree_bundle = self
657                .state_merkle_trees
658                .iter_mut()
659                .find(|x| x.accounts.merkle_tree == pubkey);
660            if queue_type == QueueType::InputStateV2 {
661                if let Some(state_tree_bundle) = state_tree_bundle {
662                    let end_offset = std::cmp::min(
663                        num_elements as usize,
664                        state_tree_bundle.input_leaf_indices.len(),
665                    );
666                    let queue_elements =
667                        state_tree_bundle.input_leaf_indices[0..end_offset].to_vec();
668                    let merkle_proofs = queue_elements
669                        .iter()
670                        .map(|leaf_info| {
671                            match state_tree_bundle
672                                .merkle_tree
673                                .get_proof_of_leaf(leaf_info.leaf_index as usize, true)
674                            {
675                                Ok(proof) => proof.to_vec(),
676                                Err(_) => {
677                                    let mut next_index =
678                                        state_tree_bundle.merkle_tree.get_next_index() as u64;
679                                    while next_index < leaf_info.leaf_index as u64 {
680                                        state_tree_bundle.merkle_tree.append(&[0u8; 32]).unwrap();
681                                        next_index =
682                                            state_tree_bundle.merkle_tree.get_next_index() as u64;
683                                    }
684                                    state_tree_bundle
685                                        .merkle_tree
686                                        .get_proof_of_leaf(leaf_info.leaf_index as usize, true)
687                                        .unwrap()
688                                        .to_vec();
689                                    Vec::new()
690                                }
691                            }
692                        })
693                        .collect::<Vec<_>>();
694                    let leaves = queue_elements
695                        .iter()
696                        .map(|leaf_info| {
697                            state_tree_bundle
698                                .merkle_tree
699                                .get_leaf(leaf_info.leaf_index as usize)
700                                .unwrap_or_default()
701                        })
702                        .collect::<Vec<_>>();
703                    let merkle_proofs_with_context = merkle_proofs
704                        .iter()
705                        .zip(queue_elements.iter())
706                        .zip(leaves.iter())
707                        .map(|((proof, element), leaf)| MerkleProofWithContext {
708                            proof: proof.clone(),
709                            leaf: *leaf,
710                            leaf_index: element.leaf_index as u64,
711                            merkle_tree: state_tree_bundle.accounts.merkle_tree.to_bytes(),
712                            root: state_tree_bundle.merkle_tree.root(),
713                            tx_hash: Some(element.tx_hash),
714                            root_seq: 0,
715                            account_hash: element.leaf,
716                        })
717                        .collect();
718
719                    return Ok(Response {
720                        context: Context {
721                            slot: self.get_current_slot(),
722                        },
723                        value: QueueElementsResult {
724                            elements: merkle_proofs_with_context,
725                            first_value_queue_index: None,
726                        },
727                    });
728                }
729            }
730
731            if queue_type == QueueType::OutputStateV2 {
732                if let Some(state_tree_bundle) = state_tree_bundle {
733                    let end_offset = std::cmp::min(
734                        num_elements as usize,
735                        state_tree_bundle.output_queue_elements.len(),
736                    );
737                    let queue_elements =
738                        state_tree_bundle.output_queue_elements[0..end_offset].to_vec();
739                    let indices = queue_elements
740                        .iter()
741                        .map(|(_, index)| index)
742                        .collect::<Vec<_>>();
743                    let merkle_proofs = indices
744                        .iter()
745                        .map(|index| {
746                            match state_tree_bundle
747                                .merkle_tree
748                                .get_proof_of_leaf(**index as usize, true)
749                            {
750                                Ok(proof) => proof.to_vec(),
751                                Err(_) => {
752                                    let mut next_index =
753                                        state_tree_bundle.merkle_tree.get_next_index() as u64;
754                                    while next_index < **index {
755                                        state_tree_bundle.merkle_tree.append(&[0u8; 32]).unwrap();
756                                        next_index =
757                                            state_tree_bundle.merkle_tree.get_next_index() as u64;
758                                    }
759                                    state_tree_bundle
760                                        .merkle_tree
761                                        .get_proof_of_leaf(**index as usize, true)
762                                        .unwrap()
763                                        .to_vec();
764                                    Vec::new()
765                                }
766                            }
767                        })
768                        .collect::<Vec<_>>();
769                    let leaves = indices
770                        .iter()
771                        .map(|index| {
772                            state_tree_bundle
773                                .merkle_tree
774                                .get_leaf(**index as usize)
775                                .unwrap_or_default()
776                        })
777                        .collect::<Vec<_>>();
778                    let merkle_proofs_with_context = merkle_proofs
779                        .iter()
780                        .zip(queue_elements.iter())
781                        .zip(leaves.iter())
782                        .map(|((proof, (element, index)), leaf)| MerkleProofWithContext {
783                            proof: proof.clone(),
784                            leaf: *leaf,
785                            leaf_index: *index,
786                            merkle_tree: state_tree_bundle.accounts.merkle_tree.to_bytes(),
787                            root: state_tree_bundle.merkle_tree.root(),
788                            tx_hash: None,
789                            root_seq: 0,
790                            account_hash: *element,
791                        })
792                        .collect();
793                    return Ok(Response {
794                        context: Context {
795                            slot: self.get_current_slot(),
796                        },
797                        value: QueueElementsResult {
798                            elements: merkle_proofs_with_context,
799                            first_value_queue_index: if queue_elements.is_empty() {
800                                None
801                            } else {
802                                Some(queue_elements[0].1)
803                            },
804                        },
805                    });
806                }
807            }
808
809            Err(IndexerError::InvalidParameters(
810                "Merkle tree not found".to_string(),
811            ))
812        }
813    }
814
815    async fn get_subtrees(
816        &self,
817        _merkle_tree_pubkey: [u8; 32],
818        _config: Option<IndexerRpcConfig>,
819    ) -> Result<Response<Items<[u8; 32]>>, IndexerError> {
820        #[cfg(not(feature = "v2"))]
821        unimplemented!("get_subtrees");
822        #[cfg(feature = "v2")]
823        {
824            let merkle_tree_pubkey = Pubkey::new_from_array(_merkle_tree_pubkey);
825            let address_tree_bundle = self
826                .address_merkle_trees
827                .iter()
828                .find(|x| x.accounts.merkle_tree == merkle_tree_pubkey);
829            if let Some(address_tree_bundle) = address_tree_bundle {
830                Ok(Response {
831                    context: Context {
832                        slot: self.get_current_slot(),
833                    },
834                    value: Items {
835                        items: address_tree_bundle.get_subtrees(),
836                    },
837                })
838            } else {
839                let state_tree_bundle = self
840                    .state_merkle_trees
841                    .iter()
842                    .find(|x| x.accounts.merkle_tree == merkle_tree_pubkey);
843                if let Some(state_tree_bundle) = state_tree_bundle {
844                    Ok(Response {
845                        context: Context {
846                            slot: self.get_current_slot(),
847                        },
848                        value: Items {
849                            items: state_tree_bundle.merkle_tree.get_subtrees(),
850                        },
851                    })
852                } else {
853                    Err(IndexerError::InvalidParameters(
854                        "Merkle tree not found".to_string(),
855                    ))
856                }
857            }
858        }
859    }
860
861    async fn get_address_queue_with_proofs(
862        &mut self,
863        _merkle_tree_pubkey: &Pubkey,
864        _zkp_batch_size: u16,
865        _start_offset: Option<u64>,
866        _config: Option<IndexerRpcConfig>,
867    ) -> Result<Response<BatchAddressUpdateIndexerResponse>, IndexerError> {
868        #[cfg(not(feature = "v2"))]
869        unimplemented!("get_address_queue_with_proofs");
870        #[cfg(feature = "v2")]
871        {
872            use light_client::indexer::AddressQueueIndex;
873            let merkle_tree_pubkey = _merkle_tree_pubkey;
874            let zkp_batch_size = _zkp_batch_size;
875
876            let batch_start_index = self
877                .get_address_merkle_trees()
878                .iter()
879                .find(|x| x.accounts.merkle_tree == *merkle_tree_pubkey)
880                .unwrap()
881                .get_v2_indexed_merkle_tree()
882                .ok_or(IndexerError::Unknown(
883                    "Failed to get v2 indexed merkle tree".into(),
884                ))?
885                .merkle_tree
886                .rightmost_index;
887
888            let address_proof_items = self
889                .get_queue_elements(
890                    merkle_tree_pubkey.to_bytes(),
891                    QueueType::AddressV2,
892                    zkp_batch_size,
893                    None,
894                    None,
895                )
896                .await
897                .map_err(|_| IndexerError::Unknown("Failed to get queue elements".into()))?
898                .value;
899
900            let addresses: Vec<AddressQueueIndex> = address_proof_items
901                .elements
902                .iter()
903                .enumerate()
904                .map(|(i, proof)| AddressQueueIndex {
905                    address: proof.account_hash,
906                    queue_index: proof.root_seq + i as u64,
907                })
908                .collect();
909            let non_inclusion_proofs = self
910                .get_multiple_new_address_proofs(
911                    merkle_tree_pubkey.to_bytes(),
912                    address_proof_items
913                        .elements
914                        .iter()
915                        .map(|x| x.account_hash)
916                        .collect(),
917                    None,
918                )
919                .await
920                .map_err(|_| {
921                    IndexerError::Unknown(
922                        "Failed to get get_multiple_new_address_proofs_full".into(),
923                    )
924                })?
925                .value;
926
927            let subtrees = self
928                .get_subtrees(merkle_tree_pubkey.to_bytes(), None)
929                .await
930                .map_err(|_| IndexerError::Unknown("Failed to get subtrees".into()))?
931                .value;
932
933            Ok(Response {
934                context: Context {
935                    slot: self.get_current_slot(),
936                },
937                value: BatchAddressUpdateIndexerResponse {
938                    batch_start_index: batch_start_index as u64,
939                    addresses,
940                    non_inclusion_proofs: non_inclusion_proofs.items,
941                    subtrees: subtrees.items,
942                },
943            })
944        }
945    }
946
947    // New required trait methods
948    async fn get_compressed_balance_by_owner(
949        &self,
950        _owner: &Pubkey,
951        _config: Option<IndexerRpcConfig>,
952    ) -> Result<Response<u64>, IndexerError> {
953        todo!("get_compressed_balance_by_owner not implemented")
954    }
955
956    async fn get_compressed_mint_token_holders(
957        &self,
958        _mint: &Pubkey,
959        _options: Option<PaginatedOptions>,
960        _config: Option<IndexerRpcConfig>,
961    ) -> Result<Response<ItemsWithCursor<OwnerBalance>>, IndexerError> {
962        todo!("get_compressed_mint_token_holders not implemented")
963    }
964
965    async fn get_compressed_token_accounts_by_delegate(
966        &self,
967        _delegate: &Pubkey,
968        _options: Option<GetCompressedTokenAccountsByOwnerOrDelegateOptions>,
969        _config: Option<IndexerRpcConfig>,
970    ) -> Result<Response<ItemsWithCursor<CompressedTokenAccount>>, IndexerError> {
971        todo!("get_compressed_token_accounts_by_delegate not implemented")
972    }
973
974    async fn get_compression_signatures_for_address(
975        &self,
976        _address: &[u8; 32],
977        _options: Option<PaginatedOptions>,
978        _config: Option<IndexerRpcConfig>,
979    ) -> Result<Response<ItemsWithCursor<SignatureWithMetadata>>, IndexerError> {
980        todo!("get_compression_signatures_for_address not implemented")
981    }
982
983    async fn get_compression_signatures_for_owner(
984        &self,
985        _owner: &Pubkey,
986        _options: Option<PaginatedOptions>,
987        _config: Option<IndexerRpcConfig>,
988    ) -> Result<Response<ItemsWithCursor<SignatureWithMetadata>>, IndexerError> {
989        todo!("get_compression_signatures_for_owner not implemented")
990    }
991
992    async fn get_compression_signatures_for_token_owner(
993        &self,
994        _owner: &Pubkey,
995        _options: Option<PaginatedOptions>,
996        _config: Option<IndexerRpcConfig>,
997    ) -> Result<Response<ItemsWithCursor<SignatureWithMetadata>>, IndexerError> {
998        todo!("get_compression_signatures_for_token_owner not implemented")
999    }
1000
1001    async fn get_indexer_health(&self, _config: Option<RetryConfig>) -> Result<bool, IndexerError> {
1002        todo!("get_indexer_health not implemented")
1003    }
1004}
1005
1006#[async_trait]
1007impl TestIndexerExtensions for TestIndexer {
1008    fn get_address_merkle_trees(&self) -> &Vec<AddressMerkleTreeBundle> {
1009        &self.address_merkle_trees
1010    }
1011
1012    fn get_address_merkle_tree(
1013        &self,
1014        merkle_tree_pubkey: Pubkey,
1015    ) -> Option<&AddressMerkleTreeBundle> {
1016        self.address_merkle_trees
1017            .iter()
1018            .find(|x| x.accounts.merkle_tree == merkle_tree_pubkey)
1019    }
1020
1021    /// deserializes an event
1022    /// adds the output_compressed_accounts to the compressed_accounts
1023    /// removes the input_compressed_accounts from the compressed_accounts
1024    /// adds the input_compressed_accounts to the nullified_compressed_accounts
1025    /// deserialiazes token data from the output_compressed_accounts
1026    /// adds the token_compressed_accounts to the token_compressed_accounts
1027    fn add_compressed_accounts_with_token_data(
1028        &mut self,
1029        slot: u64,
1030        event: &PublicTransactionEvent,
1031    ) {
1032        TestIndexer::add_event_and_compressed_accounts(self, slot, event);
1033    }
1034
1035    fn account_nullified(&mut self, merkle_tree_pubkey: Pubkey, account_hash: &str) {
1036        let decoded_hash: [u8; 32] = bs58::decode(account_hash)
1037            .into_vec()
1038            .unwrap()
1039            .as_slice()
1040            .try_into()
1041            .unwrap();
1042
1043        if let Some(state_tree_bundle) = self
1044            .state_merkle_trees
1045            .iter_mut()
1046            .find(|x| x.accounts.merkle_tree == merkle_tree_pubkey)
1047        {
1048            if let Some(leaf_index) = state_tree_bundle.merkle_tree.get_leaf_index(&decoded_hash) {
1049                state_tree_bundle
1050                    .merkle_tree
1051                    .update(&[0u8; 32], leaf_index)
1052                    .unwrap();
1053            }
1054        }
1055    }
1056
1057    fn address_tree_updated(
1058        &mut self,
1059        merkle_tree_pubkey: Pubkey,
1060        context: &NewAddressProofWithContext,
1061    ) {
1062        info!("Updating address tree...");
1063        let pos = self
1064            .address_merkle_trees
1065            .iter()
1066            .position(|x| x.accounts.merkle_tree == merkle_tree_pubkey)
1067            .unwrap();
1068        let new_low_element = context.new_low_element.clone().unwrap();
1069        let new_element = context.new_element.clone().unwrap();
1070        let new_element_next_value = context.new_element_next_value.clone().unwrap();
1071        // It can only be v1 address tree because proof with context has len 16.
1072        self.address_merkle_trees[pos]
1073            .get_v1_indexed_merkle_tree_mut()
1074            .expect("Failed to get v1 indexed merkle tree.")
1075            .update(&new_low_element, &new_element, &new_element_next_value)
1076            .unwrap();
1077        self.address_merkle_trees[pos]
1078            .append_with_low_element_index(new_low_element.index, &new_element.value)
1079            .unwrap();
1080        info!("Address tree updated");
1081    }
1082
1083    fn get_state_merkle_tree_accounts(&self, pubkeys: &[Pubkey]) -> Vec<StateMerkleTreeAccounts> {
1084        pubkeys
1085            .iter()
1086            .map(|x| {
1087                self.state_merkle_trees
1088                    .iter()
1089                    .find(|y| y.accounts.merkle_tree == *x || y.accounts.nullifier_queue == *x)
1090                    .unwrap()
1091                    .accounts
1092            })
1093            .collect::<Vec<_>>()
1094    }
1095
1096    fn get_state_merkle_trees(&self) -> &Vec<StateMerkleTreeBundle> {
1097        &self.state_merkle_trees
1098    }
1099
1100    fn get_state_merkle_trees_mut(&mut self) -> &mut Vec<StateMerkleTreeBundle> {
1101        &mut self.state_merkle_trees
1102    }
1103
1104    fn get_address_merkle_trees_mut(&mut self) -> &mut Vec<AddressMerkleTreeBundle> {
1105        &mut self.address_merkle_trees
1106    }
1107
1108    fn get_token_compressed_accounts(&self) -> &Vec<TokenDataWithMerkleContext> {
1109        &self.token_compressed_accounts
1110    }
1111
1112    fn get_group_pda(&self) -> &Pubkey {
1113        &self.group_pda
1114    }
1115
1116    fn add_address_merkle_tree_accounts(
1117        &mut self,
1118        merkle_tree_keypair: &Keypair,
1119        queue_keypair: &Keypair,
1120        _owning_program_id: Option<Pubkey>,
1121    ) -> AddressMerkleTreeAccounts {
1122        info!("Adding address merkle tree accounts...");
1123        let address_merkle_tree_accounts = AddressMerkleTreeAccounts {
1124            merkle_tree: merkle_tree_keypair.pubkey(),
1125            queue: queue_keypair.pubkey(),
1126        };
1127        self.address_merkle_trees
1128            .push(Self::add_address_merkle_tree_bundle(address_merkle_tree_accounts).unwrap());
1129        info!(
1130            "Address merkle tree accounts added. Total: {}",
1131            self.address_merkle_trees.len()
1132        );
1133        address_merkle_tree_accounts
1134    }
1135
1136    fn get_compressed_accounts_with_merkle_context_by_owner(
1137        &self,
1138        owner: &Pubkey,
1139    ) -> Vec<CompressedAccountWithMerkleContext> {
1140        self.compressed_accounts
1141            .iter()
1142            .filter(|x| x.compressed_account.owner.to_bytes() == owner.to_bytes())
1143            .cloned()
1144            .collect()
1145    }
1146
1147    fn add_state_bundle(&mut self, state_bundle: StateMerkleTreeBundle) {
1148        Self::get_state_merkle_trees_mut(self).push(state_bundle);
1149    }
1150
1151    fn add_event_and_compressed_accounts(
1152        &mut self,
1153        slot: u64,
1154        event: &PublicTransactionEvent,
1155    ) -> (
1156        Vec<CompressedAccountWithMerkleContext>,
1157        Vec<TokenDataWithMerkleContext>,
1158    ) {
1159        let mut compressed_accounts = Vec::new();
1160        let mut token_compressed_accounts = Vec::new();
1161        let event_inputs_len = event.input_compressed_account_hashes.len();
1162        let event_outputs_len = event.output_compressed_account_hashes.len();
1163        for i in 0..std::cmp::max(event_inputs_len, event_outputs_len) {
1164            self.process_v1_compressed_account(
1165                slot,
1166                event,
1167                i,
1168                &mut token_compressed_accounts,
1169                &mut compressed_accounts,
1170            );
1171        }
1172
1173        self.events.push(event.clone());
1174        (compressed_accounts, token_compressed_accounts)
1175    }
1176
1177    fn get_proof_by_index(&mut self, merkle_tree_pubkey: Pubkey, index: u64) -> MerkleProof {
1178        let bundle = self
1179            .state_merkle_trees
1180            .iter_mut()
1181            .find(|x| x.accounts.merkle_tree == merkle_tree_pubkey)
1182            .unwrap();
1183
1184        while bundle.merkle_tree.leaves().len() <= index as usize {
1185            bundle.merkle_tree.append(&[0u8; 32]).unwrap();
1186        }
1187
1188        let leaf = match bundle.merkle_tree.get_leaf(index as usize) {
1189            Ok(leaf) => leaf,
1190            Err(_) => {
1191                bundle.merkle_tree.append(&[0u8; 32]).unwrap();
1192                bundle.merkle_tree.get_leaf(index as usize).unwrap()
1193            }
1194        };
1195
1196        let proof = bundle
1197            .merkle_tree
1198            .get_proof_of_leaf(index as usize, true)
1199            .unwrap()
1200            .to_vec();
1201
1202        MerkleProof {
1203            hash: leaf,
1204            leaf_index: index,
1205            merkle_tree: merkle_tree_pubkey,
1206            proof,
1207            root_seq: bundle.merkle_tree.sequence_number as u64,
1208            root: bundle.merkle_tree.root(),
1209        }
1210    }
1211
1212    async fn finalize_batched_address_tree_update(
1213        &mut self,
1214        merkle_tree_pubkey: Pubkey,
1215        account_data: &mut [u8],
1216    ) {
1217        let onchain_account =
1218            BatchedMerkleTreeAccount::address_from_bytes(account_data, &merkle_tree_pubkey.into())
1219                .unwrap();
1220        let address_tree = self
1221            .address_merkle_trees
1222            .iter_mut()
1223            .find(|x| x.accounts.merkle_tree == merkle_tree_pubkey)
1224            .unwrap();
1225        let address_tree_index = address_tree.right_most_index();
1226        let onchain_next_index = onchain_account.next_index;
1227        let diff_onchain_indexer = onchain_next_index - address_tree_index as u64;
1228        let addresses = address_tree.queue_elements[0..diff_onchain_indexer as usize].to_vec();
1229        for _ in 0..diff_onchain_indexer {
1230            address_tree.queue_elements.remove(0);
1231        }
1232        for new_element_value in &addresses {
1233            address_tree
1234                .append(&BigUint::from_bytes_be(new_element_value))
1235                .unwrap();
1236        }
1237        match &mut address_tree.merkle_tree {
1238            IndexedMerkleTreeVersion::V2(tree) => tree.merkle_tree.num_root_updates += 1,
1239            IndexedMerkleTreeVersion::V1(_) => {
1240                unimplemented!("finalize_batched_address_tree_update not implemented for v1 trees.")
1241            }
1242        }
1243        let onchain_root = onchain_account.root_history.last().unwrap();
1244        let new_root = address_tree.root();
1245        assert_eq!(*onchain_root, new_root);
1246    }
1247}
1248
1249impl TestIndexer {
1250    fn get_current_slot(&self) -> u64 {
1251        // For testing, we can use a fixed slot or MAX
1252        u64::MAX
1253    }
1254
1255    pub async fn init_from_acounts(
1256        payer: &Keypair,
1257        env: &TestAccounts,
1258        output_queue_batch_size: usize,
1259    ) -> Self {
1260        // Create a vector of StateMerkleTreeAccounts from all v1 and v2 state trees
1261        let mut state_merkle_tree_accounts = env.v1_state_trees.clone();
1262
1263        // Add v2 state trees converting from StateMerkleTreeAccountsV2 to StateMerkleTreeAccounts
1264        for v2_state_tree in &env.v2_state_trees {
1265            state_merkle_tree_accounts.push(StateMerkleTreeAccounts {
1266                merkle_tree: v2_state_tree.merkle_tree,
1267                nullifier_queue: v2_state_tree.output_queue,
1268                cpi_context: v2_state_tree.cpi_context,
1269                tree_type: TreeType::StateV2,
1270            });
1271        }
1272
1273        // Create a vector of AddressMerkleTreeAccounts from all v1 address trees
1274        let mut address_merkle_tree_accounts = env.v1_address_trees.clone();
1275
1276        // Add v2 address trees (each entry is both the merkle tree and queue)
1277        for &v2_address_tree in &env.v2_address_trees {
1278            address_merkle_tree_accounts.push(AddressMerkleTreeAccounts {
1279                merkle_tree: v2_address_tree,
1280                queue: v2_address_tree,
1281            });
1282        }
1283
1284        Self::new(
1285            state_merkle_tree_accounts,
1286            address_merkle_tree_accounts,
1287            payer.insecure_clone(),
1288            env.protocol.group_pda,
1289            output_queue_batch_size,
1290        )
1291        .await
1292    }
1293
1294    pub async fn new(
1295        state_merkle_tree_accounts: Vec<StateMerkleTreeAccounts>,
1296        address_merkle_tree_accounts: Vec<AddressMerkleTreeAccounts>,
1297        payer: Keypair,
1298        group_pda: Pubkey,
1299        output_queue_batch_size: usize,
1300    ) -> Self {
1301        let mut state_merkle_trees = Vec::new();
1302        for state_merkle_tree_account in state_merkle_tree_accounts.iter() {
1303            let (tree_type, merkle_tree, output_queue_batch_size) =
1304                if state_merkle_tree_account.tree_type == TreeType::StateV2 {
1305                    let merkle_tree = Box::new(MerkleTree::<Poseidon>::new_with_history(
1306                        DEFAULT_BATCH_STATE_TREE_HEIGHT as usize,
1307                        0,
1308                        0,
1309                        DEFAULT_BATCH_ROOT_HISTORY_LEN as usize,
1310                    ));
1311                    (
1312                        TreeType::StateV2,
1313                        merkle_tree,
1314                        Some(output_queue_batch_size),
1315                    )
1316                } else {
1317                    let merkle_tree = Box::new(MerkleTree::<Poseidon>::new_with_history(
1318                        account_compression::utils::constants::STATE_MERKLE_TREE_HEIGHT as usize,
1319                        account_compression::utils::constants::STATE_MERKLE_TREE_CANOPY_DEPTH
1320                            as usize,
1321                        0,
1322                        account_compression::utils::constants::STATE_MERKLE_TREE_ROOTS as usize,
1323                    ));
1324                    (TreeType::StateV1, merkle_tree, None)
1325                };
1326
1327            state_merkle_trees.push(StateMerkleTreeBundle {
1328                accounts: *state_merkle_tree_account,
1329                merkle_tree,
1330                rollover_fee: FeeConfig::default().state_merkle_tree_rollover as i64,
1331                tree_type,
1332                output_queue_elements: vec![],
1333                input_leaf_indices: vec![],
1334                output_queue_batch_size,
1335                num_inserted_batches: 0,
1336            });
1337        }
1338
1339        let mut address_merkle_trees = Vec::new();
1340        for address_merkle_tree_account in address_merkle_tree_accounts {
1341            address_merkle_trees
1342                .push(Self::add_address_merkle_tree_bundle(address_merkle_tree_account).unwrap());
1343        }
1344
1345        Self {
1346            state_merkle_trees,
1347            address_merkle_trees,
1348            payer,
1349            compressed_accounts: vec![],
1350            nullified_compressed_accounts: vec![],
1351            events: vec![],
1352            token_compressed_accounts: vec![],
1353            token_nullified_compressed_accounts: vec![],
1354            group_pda,
1355        }
1356    }
1357
1358    pub fn add_address_merkle_tree_bundle(
1359        address_merkle_tree_accounts: AddressMerkleTreeAccounts,
1360        // TODO: add config here
1361    ) -> Result<AddressMerkleTreeBundle, IndexerError> {
1362        if address_merkle_tree_accounts.merkle_tree == address_merkle_tree_accounts.queue {
1363            AddressMerkleTreeBundle::new_v2(address_merkle_tree_accounts)
1364        } else {
1365            AddressMerkleTreeBundle::new_v1(address_merkle_tree_accounts)
1366        }
1367    }
1368
1369    async fn add_address_merkle_tree_v1<R: Rpc>(
1370        &mut self,
1371        rpc: &mut R,
1372        merkle_tree_keypair: &Keypair,
1373        queue_keypair: &Keypair,
1374        owning_program_id: Option<Pubkey>,
1375    ) -> Result<AddressMerkleTreeAccounts, RpcError> {
1376        create_address_merkle_tree_and_queue_account(
1377            &self.payer,
1378            true,
1379            rpc,
1380            merkle_tree_keypair,
1381            queue_keypair,
1382            owning_program_id,
1383            None,
1384            &AddressMerkleTreeConfig::default(),
1385            &AddressQueueConfig::default(),
1386            0,
1387        )
1388        .await?;
1389
1390        let accounts = <TestIndexer as TestIndexerExtensions>::add_address_merkle_tree_accounts(
1391            self,
1392            merkle_tree_keypair,
1393            queue_keypair,
1394            owning_program_id,
1395        );
1396        Ok(accounts)
1397    }
1398
1399    #[cfg(feature = "devenv")]
1400    async fn add_address_merkle_tree_v2<R: Rpc>(
1401        &mut self,
1402        rpc: &mut R,
1403        merkle_tree_keypair: &Keypair,
1404        queue_keypair: &Keypair,
1405        _owning_program_id: Option<Pubkey>,
1406    ) -> Result<AddressMerkleTreeAccounts, RpcError> {
1407        info!(
1408            "Adding address merkle tree accounts v2 {:?}",
1409            merkle_tree_keypair.pubkey()
1410        );
1411
1412        let params = light_batched_merkle_tree::initialize_address_tree::InitAddressTreeAccountsInstructionData::test_default();
1413
1414        info!(
1415            "Creating batched address merkle tree {:?}",
1416            merkle_tree_keypair.pubkey()
1417        );
1418        create_batch_address_merkle_tree(rpc, &self.payer, merkle_tree_keypair, params).await?;
1419        info!(
1420            "Batched address merkle tree created {:?}",
1421            merkle_tree_keypair.pubkey()
1422        );
1423
1424        let accounts = self.add_address_merkle_tree_accounts(
1425            merkle_tree_keypair,
1426            queue_keypair,
1427            _owning_program_id,
1428        );
1429        Ok(accounts)
1430    }
1431
1432    pub async fn add_address_merkle_tree<R: Rpc>(
1433        &mut self,
1434        rpc: &mut R,
1435        merkle_tree_keypair: &Keypair,
1436        queue_keypair: &Keypair,
1437        owning_program_id: Option<Pubkey>,
1438        tree_type: TreeType,
1439    ) -> Result<AddressMerkleTreeAccounts, RpcError> {
1440        if tree_type == TreeType::AddressV1 {
1441            self.add_address_merkle_tree_v1(
1442                rpc,
1443                merkle_tree_keypair,
1444                queue_keypair,
1445                owning_program_id,
1446            )
1447            .await
1448        } else if tree_type == TreeType::AddressV2 {
1449            #[cfg(not(feature = "devenv"))]
1450            panic!("Batched address merkle trees require the 'devenv' feature to be enabled");
1451            #[cfg(feature = "devenv")]
1452            self.add_address_merkle_tree_v2(
1453                rpc,
1454                merkle_tree_keypair,
1455                queue_keypair,
1456                owning_program_id,
1457            )
1458            .await
1459        } else {
1460            Err(RpcError::CustomError(format!(
1461                "add_address_merkle_tree: Version not supported, {}. Versions: AddressV1, AddressV2",
1462                tree_type
1463            )))
1464        }
1465    }
1466
1467    #[allow(clippy::too_many_arguments)]
1468    pub async fn add_state_merkle_tree<R: Rpc>(
1469        &mut self,
1470        rpc: &mut R,
1471        merkle_tree_keypair: &Keypair,
1472        queue_keypair: &Keypair,
1473        cpi_context_keypair: &Keypair,
1474        owning_program_id: Option<Pubkey>,
1475        forester: Option<Pubkey>,
1476        tree_type: TreeType,
1477    ) {
1478        let (rollover_fee, merkle_tree, output_queue_batch_size) = match tree_type {
1479            TreeType::StateV1 => {
1480                create_state_merkle_tree_and_queue_account(
1481                    &self.payer,
1482                    true,
1483                    rpc,
1484                    merkle_tree_keypair,
1485                    queue_keypair,
1486                    Some(cpi_context_keypair),
1487                    owning_program_id,
1488                    forester,
1489                    self.state_merkle_trees.len() as u64,
1490                    &StateMerkleTreeConfig::default(),
1491                    &NullifierQueueConfig::default(),
1492                )
1493                    .await
1494                    .unwrap();
1495                let merkle_tree = Box::new(MerkleTree::<Poseidon>::new_with_history(
1496                    account_compression::utils::constants::STATE_MERKLE_TREE_HEIGHT as usize,
1497                    account_compression::utils::constants::STATE_MERKLE_TREE_CANOPY_DEPTH as usize,
1498                    0,
1499                    account_compression::utils::constants::STATE_MERKLE_TREE_ROOTS as usize,
1500
1501                ));
1502                (FeeConfig::default().state_merkle_tree_rollover as i64,merkle_tree, None)
1503            }
1504            TreeType::StateV2 => {
1505                #[cfg(feature = "devenv")]
1506                {
1507                    let params =  light_batched_merkle_tree::initialize_state_tree::InitStateTreeAccountsInstructionData::test_default();
1508
1509                    create_batched_state_merkle_tree(
1510                        &self.payer,
1511                        true,
1512                        rpc,
1513                        merkle_tree_keypair,
1514                        queue_keypair,
1515                        cpi_context_keypair,
1516                        params,
1517                    ).await.unwrap();
1518                    let merkle_tree = Box::new(MerkleTree::<Poseidon>::new_with_history(
1519                        DEFAULT_BATCH_STATE_TREE_HEIGHT as usize,
1520                        0,
1521                        0,
1522                        DEFAULT_BATCH_ROOT_HISTORY_LEN as usize,
1523
1524                    ));
1525                    (FeeConfig::test_batched().state_merkle_tree_rollover as i64,merkle_tree, Some(params.output_queue_batch_size as usize))
1526                }
1527
1528                #[cfg(not(feature = "devenv"))]
1529                panic!("Batched state merkle trees require the 'devenv' feature to be enabled")
1530            }
1531            _ => panic!(
1532                "add_state_merkle_tree: tree_type not supported, {}. tree_type: 1 concurrent, 2 batched",
1533                tree_type
1534            ),
1535        };
1536        let state_merkle_tree_account = StateMerkleTreeAccounts {
1537            merkle_tree: merkle_tree_keypair.pubkey(),
1538            nullifier_queue: queue_keypair.pubkey(),
1539            cpi_context: cpi_context_keypair.pubkey(),
1540            tree_type,
1541        };
1542
1543        self.state_merkle_trees.push(StateMerkleTreeBundle {
1544            merkle_tree,
1545            accounts: state_merkle_tree_account,
1546            rollover_fee,
1547            tree_type,
1548            output_queue_elements: vec![],
1549            input_leaf_indices: vec![],
1550            num_inserted_batches: 0,
1551            output_queue_batch_size,
1552        });
1553        println!(
1554            "creating Merkle tree bundle {:?}",
1555            self.state_merkle_trees
1556                .iter()
1557                .map(|x| x.accounts.merkle_tree)
1558                .collect::<Vec<_>>()
1559        );
1560    }
1561
1562    /// deserializes an event
1563    /// adds the output_compressed_accounts to the compressed_accounts
1564    /// removes the input_compressed_accounts from the compressed_accounts
1565    /// adds the input_compressed_accounts to the nullified_compressed_accounts
1566    pub fn add_lamport_compressed_accounts(&mut self, slot: u64, event_bytes: Vec<u8>) {
1567        let event_bytes = event_bytes.clone();
1568        let event = PublicTransactionEvent::deserialize(&mut event_bytes.as_slice()).unwrap();
1569        // TODO: map event type
1570        <TestIndexer as TestIndexerExtensions>::add_event_and_compressed_accounts(
1571            self, slot, &event,
1572        );
1573    }
1574
1575    /// returns the compressed sol balance of the owner pubkey
1576    pub fn get_compressed_balance(&self, owner: &Pubkey) -> u64 {
1577        self.compressed_accounts
1578            .iter()
1579            .filter(|x| x.compressed_account.owner.to_bytes() == owner.to_bytes())
1580            .map(|x| x.compressed_account.lamports)
1581            .sum()
1582    }
1583
1584    /// returns the compressed token balance of the owner pubkey for a token by mint
1585    pub fn get_compressed_token_balance(&self, owner: &Pubkey, mint: &Pubkey) -> u64 {
1586        self.token_compressed_accounts
1587            .iter()
1588            .filter(|x| {
1589                x.compressed_account.compressed_account.owner.to_bytes() == owner.to_bytes()
1590                    && x.token_data.mint == *mint
1591            })
1592            .map(|x| x.token_data.amount)
1593            .sum()
1594    }
1595
1596    fn process_v1_compressed_account(
1597        &mut self,
1598        slot: u64,
1599        event: &PublicTransactionEvent,
1600        i: usize,
1601        token_compressed_accounts: &mut Vec<TokenDataWithMerkleContext>,
1602        compressed_accounts: &mut Vec<CompressedAccountWithMerkleContext>,
1603    ) {
1604        let mut input_addresses = vec![];
1605        let mut new_addresses = vec![];
1606        if event.output_compressed_accounts.len() > i {
1607            let compressed_account = &event.output_compressed_accounts[i];
1608            if let Some(address) = compressed_account.compressed_account.address {
1609                if !input_addresses.iter().any(|x| x == &address) {
1610                    new_addresses.push(address);
1611                }
1612            }
1613            let merkle_tree = self.state_merkle_trees.iter().find(|x| {
1614                x.accounts.merkle_tree
1615                    == solana_pubkey::Pubkey::from(
1616                        event.pubkey_array
1617                            [event.output_compressed_accounts[i].merkle_tree_index as usize]
1618                            .to_bytes(),
1619                    )
1620            });
1621            // Check for output queue
1622            let merkle_tree = if let Some(merkle_tree) = merkle_tree {
1623                merkle_tree
1624            } else {
1625                self.state_merkle_trees
1626                    .iter()
1627                    .find(|x| {
1628                        x.accounts.nullifier_queue
1629                            == solana_pubkey::Pubkey::from(
1630                                event.pubkey_array[event.output_compressed_accounts[i]
1631                                    .merkle_tree_index
1632                                    as usize]
1633                                    .to_bytes(),
1634                            )
1635                    })
1636                    .unwrap()
1637            };
1638            let nullifier_queue_pubkey = merkle_tree.accounts.nullifier_queue;
1639            let merkle_tree_pubkey = merkle_tree.accounts.merkle_tree;
1640            // if data is some, try to deserialize token data, if it fails, add to compressed_accounts
1641            // if data is none add to compressed_accounts
1642            // new accounts are inserted in front so that the newest accounts are found first
1643            match compressed_account.compressed_account.data.as_ref() {
1644                Some(data) => {
1645                    // Check for both V1 and V2 token account discriminators
1646                    let is_v1_token = data.discriminator == light_compressed_token::constants::TOKEN_COMPRESSED_ACCOUNT_DISCRIMINATOR; // [2, 0, 0, 0, 0, 0, 0, 0]
1647                    let is_v2_token = data.discriminator == [0, 0, 0, 0, 0, 0, 0, 3]; // V2 discriminator
1648
1649                    if compressed_account.compressed_account.owner
1650                        == light_compressed_token::ID.to_bytes()
1651                        && (is_v1_token || is_v2_token)
1652                    {
1653                        if let Ok(token_data) = TokenData::deserialize(&mut data.data.as_slice()) {
1654                            let token_account = TokenDataWithMerkleContext {
1655                                token_data,
1656                                compressed_account: CompressedAccountWithMerkleContext {
1657                                    compressed_account: compressed_account
1658                                        .compressed_account
1659                                        .clone(),
1660                                    merkle_context: MerkleContext {
1661                                        leaf_index: event.output_leaf_indices[i],
1662                                        merkle_tree_pubkey: merkle_tree_pubkey.into(),
1663                                        queue_pubkey: nullifier_queue_pubkey.into(),
1664                                        prove_by_index: false,
1665                                        tree_type: merkle_tree.tree_type,
1666                                    },
1667                                },
1668                            };
1669                            token_compressed_accounts.push(token_account.clone());
1670                            self.token_compressed_accounts.insert(0, token_account);
1671                        }
1672                    } else {
1673                        let compressed_account = CompressedAccountWithMerkleContext {
1674                            compressed_account: compressed_account.compressed_account.clone(),
1675                            merkle_context: MerkleContext {
1676                                leaf_index: event.output_leaf_indices[i],
1677                                merkle_tree_pubkey: merkle_tree_pubkey.into(),
1678                                queue_pubkey: nullifier_queue_pubkey.into(),
1679                                prove_by_index: false,
1680                                tree_type: merkle_tree.tree_type,
1681                            },
1682                        };
1683                        compressed_accounts.push(compressed_account.clone());
1684                        self.compressed_accounts.insert(0, compressed_account);
1685                    }
1686                }
1687                None => {
1688                    let compressed_account = CompressedAccountWithMerkleContext {
1689                        compressed_account: compressed_account.compressed_account.clone(),
1690                        merkle_context: MerkleContext {
1691                            leaf_index: event.output_leaf_indices[i],
1692                            merkle_tree_pubkey: merkle_tree_pubkey.into(),
1693                            queue_pubkey: nullifier_queue_pubkey.into(),
1694                            prove_by_index: false,
1695                            tree_type: merkle_tree.tree_type,
1696                        },
1697                    };
1698                    compressed_accounts.push(compressed_account.clone());
1699                    self.compressed_accounts.insert(0, compressed_account);
1700                }
1701            };
1702            let merkle_tree = &mut self.state_merkle_trees.iter_mut().find(|x| {
1703                x.accounts.merkle_tree
1704                    == solana_pubkey::Pubkey::from(
1705                        event.pubkey_array
1706                            [event.output_compressed_accounts[i].merkle_tree_index as usize]
1707                            .to_bytes(),
1708                    )
1709            });
1710            if merkle_tree.is_some() {
1711                let merkle_tree = merkle_tree.as_mut().unwrap();
1712                let leaf_hash = compressed_account
1713                    .compressed_account
1714                    .hash(
1715                        &event.pubkey_array
1716                            [event.output_compressed_accounts[i].merkle_tree_index as usize],
1717                        &event.output_leaf_indices[i],
1718                        false,
1719                    )
1720                    .unwrap();
1721                merkle_tree
1722                    .merkle_tree
1723                    .append(&leaf_hash)
1724                    .expect("insert failed");
1725            } else {
1726                let merkle_tree = &mut self
1727                    .state_merkle_trees
1728                    .iter_mut()
1729                    .find(|x| {
1730                        x.accounts.nullifier_queue
1731                            == solana_pubkey::Pubkey::from(
1732                                event.pubkey_array[event.output_compressed_accounts[i]
1733                                    .merkle_tree_index
1734                                    as usize]
1735                                    .to_bytes(),
1736                            )
1737                    })
1738                    .unwrap();
1739
1740                merkle_tree.output_queue_elements.push((
1741                    event.output_compressed_account_hashes[i],
1742                    event.output_leaf_indices[i].into(),
1743                ));
1744            }
1745        }
1746        if event.input_compressed_account_hashes.len() > i {
1747            let tx_hash: [u8; 32] = create_tx_hash(
1748                &event.input_compressed_account_hashes,
1749                &event.output_compressed_account_hashes,
1750                slot,
1751            )
1752            .unwrap();
1753            let hash = event.input_compressed_account_hashes[i];
1754            let index = self
1755                .compressed_accounts
1756                .iter()
1757                .position(|x| x.hash().unwrap() == hash);
1758            let (leaf_index, merkle_tree_pubkey) = if let Some(index) = index {
1759                self.nullified_compressed_accounts
1760                    .push(self.compressed_accounts[index].clone());
1761                let leaf_index = self.compressed_accounts[index].merkle_context.leaf_index;
1762                let merkle_tree_pubkey = self.compressed_accounts[index]
1763                    .merkle_context
1764                    .merkle_tree_pubkey;
1765                if let Some(address) = self.compressed_accounts[index].compressed_account.address {
1766                    input_addresses.push(address);
1767                }
1768                self.compressed_accounts.remove(index);
1769                (Some(leaf_index), Some(merkle_tree_pubkey))
1770            } else if let Some(index) = self
1771                .token_compressed_accounts
1772                .iter()
1773                .position(|x| x.compressed_account.hash().unwrap() == hash)
1774            {
1775                self.token_nullified_compressed_accounts
1776                    .push(self.token_compressed_accounts[index].clone());
1777                let leaf_index = self.token_compressed_accounts[index]
1778                    .compressed_account
1779                    .merkle_context
1780                    .leaf_index;
1781                let merkle_tree_pubkey = self.token_compressed_accounts[index]
1782                    .compressed_account
1783                    .merkle_context
1784                    .merkle_tree_pubkey;
1785                self.token_compressed_accounts.remove(index);
1786                (Some(leaf_index), Some(merkle_tree_pubkey))
1787            } else {
1788                (None, None)
1789            };
1790            if let Some(leaf_index) = leaf_index {
1791                let merkle_tree_pubkey = merkle_tree_pubkey.unwrap();
1792                let bundle =
1793                    &mut <TestIndexer as TestIndexerExtensions>::get_state_merkle_trees_mut(self)
1794                        .iter_mut()
1795                        .find(|x| {
1796                            x.accounts.merkle_tree
1797                                == solana_pubkey::Pubkey::from(merkle_tree_pubkey.to_bytes())
1798                        })
1799                        .unwrap();
1800                // Store leaf indices of input accounts for batched trees
1801                if bundle.tree_type == TreeType::StateV2 {
1802                    let leaf_hash = event.input_compressed_account_hashes[i];
1803                    bundle.input_leaf_indices.push(LeafIndexInfo {
1804                        leaf_index,
1805                        leaf: leaf_hash,
1806                        tx_hash,
1807                    });
1808                }
1809            } else {
1810                println!("Test indexer didn't find input compressed accounts to nullify");
1811            }
1812        }
1813        // checks whether there are addresses in outputs which don't exist in inputs.
1814        // if so check pubkey_array for the first address Merkle tree and append to the bundles queue elements.
1815        // Note:
1816        // - creating addresses in multiple address Merkle trees in one tx is not supported
1817        // TODO: reimplement this is not a good solution
1818        // - take addresses and address Merkle tree pubkeys from cpi to account compression program
1819        if !new_addresses.is_empty() {
1820            for pubkey in event.pubkey_array.iter() {
1821                if let Some((_, address_merkle_tree)) = self
1822                    .address_merkle_trees
1823                    .iter_mut()
1824                    .enumerate()
1825                    .find(|(_, x)| {
1826                        x.accounts.merkle_tree == solana_pubkey::Pubkey::from(pubkey.to_bytes())
1827                    })
1828                {
1829                    address_merkle_tree
1830                        .queue_elements
1831                        .append(&mut new_addresses);
1832                }
1833            }
1834        }
1835    }
1836
1837    async fn _get_multiple_new_address_proofs(
1838        &self,
1839        merkle_tree_pubkey: [u8; 32],
1840        addresses: Vec<[u8; 32]>,
1841        full: bool,
1842    ) -> Result<Vec<NewAddressProofWithContext>, IndexerError> {
1843        let mut proofs: Vec<NewAddressProofWithContext> = Vec::new();
1844
1845        for address in addresses.iter() {
1846            info!("Getting new address proof for {:?}", address);
1847            let pubkey = Pubkey::from(merkle_tree_pubkey);
1848            let address_tree_bundle = self
1849                .address_merkle_trees
1850                .iter()
1851                .find(|x| x.accounts.merkle_tree == pubkey)
1852                .unwrap();
1853
1854            let address_biguint = BigUint::from_bytes_be(address.as_slice());
1855            let (old_low_address, _old_low_address_next_value) =
1856                address_tree_bundle.find_low_element_for_nonexistent(&address_biguint)?;
1857            let address_bundle = address_tree_bundle
1858                .new_element_with_low_element_index(old_low_address.index, &address_biguint)?;
1859
1860            let (old_low_address, old_low_address_next_value) =
1861                address_tree_bundle.find_low_element_for_nonexistent(&address_biguint)?;
1862
1863            // Get the Merkle proof for updating low element.
1864            let low_address_proof =
1865                address_tree_bundle.get_proof_of_leaf(old_low_address.index, full)?;
1866
1867            let low_address_index: u64 = old_low_address.index as u64;
1868            let low_address_value: [u8; 32] =
1869                bigint_to_be_bytes_array(&old_low_address.value).unwrap();
1870            let low_address_next_index: u64 = old_low_address.next_index as u64;
1871            let low_address_next_value: [u8; 32] =
1872                bigint_to_be_bytes_array(&old_low_address_next_value).unwrap();
1873            let proof = NewAddressProofWithContext {
1874                merkle_tree: Pubkey::new_from_array(merkle_tree_pubkey),
1875                low_address_index,
1876                low_address_value,
1877                low_address_next_index,
1878                low_address_next_value,
1879                low_address_proof,
1880                root: address_tree_bundle.root(),
1881                root_seq: address_tree_bundle.sequence_number(),
1882                new_low_element: Some(address_bundle.new_low_element),
1883                new_element: Some(address_bundle.new_element),
1884                new_element_next_value: Some(address_bundle.new_element_next_value),
1885            };
1886            proofs.push(proof);
1887        }
1888        Ok(proofs)
1889    }
1890}
1891
1892impl TestIndexer {
1893    async fn process_inclusion_proofs(
1894        &self,
1895        merkle_tree_pubkeys: &[Pubkey],
1896        accounts: &[[u8; 32]],
1897    ) -> Result<
1898        (
1899            Option<BatchInclusionJsonStruct>,
1900            Option<BatchInclusionJsonStructLegacy>,
1901            Vec<AccountProofInputs>,
1902        ),
1903        IndexerError,
1904    > {
1905        let mut inclusion_proofs = Vec::new();
1906        let mut account_proof_inputs = Vec::new();
1907        let mut height = 0;
1908        let mut queues = vec![];
1909        let mut cpi_contextes = vec![];
1910        let mut tree_types = vec![];
1911        // Collect all proofs first before any await points
1912        let proof_data: Vec<_> = accounts
1913            .iter()
1914            .zip(merkle_tree_pubkeys.iter())
1915            .map(|(account, &pubkey)| {
1916                let bundle = self
1917                    .state_merkle_trees
1918                    .iter()
1919                    .find(|x| {
1920                        x.accounts.merkle_tree == pubkey || x.accounts.nullifier_queue == pubkey
1921                    })
1922                    .unwrap();
1923                let merkle_tree = &bundle.merkle_tree;
1924                queues.push(bundle.accounts.nullifier_queue);
1925                cpi_contextes.push(bundle.accounts.cpi_context);
1926                tree_types.push(bundle.tree_type);
1927                let leaf_index = merkle_tree.get_leaf_index(account).unwrap();
1928                let proof = merkle_tree.get_proof_of_leaf(leaf_index, true).unwrap();
1929
1930                // Convert proof to owned data that implements Send
1931                let proof: Vec<BigInt> = proof.iter().map(|x| BigInt::from_be_bytes(x)).collect();
1932
1933                if height == 0 {
1934                    height = merkle_tree.height;
1935                } else {
1936                    assert_eq!(height, merkle_tree.height);
1937                }
1938                let root_index = if bundle.tree_type == TreeType::StateV1 {
1939                    merkle_tree.get_history_root_index().unwrap()
1940                } else {
1941                    merkle_tree.get_history_root_index_v2().unwrap()
1942                };
1943
1944                Ok((leaf_index, proof, merkle_tree.root(), root_index))
1945            })
1946            .collect::<Result<_, IndexerError>>()?;
1947
1948        // Now handle the async operations with the collected data
1949        for (i, (leaf_index, proof, merkle_root, root_index)) in proof_data.into_iter().enumerate()
1950        {
1951            inclusion_proofs.push(InclusionMerkleProofInputs {
1952                root: BigInt::from_be_bytes(merkle_root.as_slice()),
1953                leaf: BigInt::from_be_bytes(&accounts[i]),
1954                path_index: BigInt::from_be_bytes(leaf_index.to_be_bytes().as_slice()),
1955                path_elements: proof,
1956            });
1957
1958            account_proof_inputs.push(AccountProofInputs {
1959                root_index: RootIndex::new_some(root_index),
1960                root: merkle_root,
1961                leaf_index: leaf_index as u64,
1962                hash: accounts[i],
1963                tree_info: light_client::indexer::TreeInfo {
1964                    cpi_context: Some(cpi_contextes[i]),
1965                    next_tree_info: None,
1966                    queue: queues[i],
1967                    tree: merkle_tree_pubkeys[i],
1968                    tree_type: tree_types[i],
1969                },
1970            });
1971        }
1972
1973        let (batch_inclusion_proof_inputs, legacy) = if height
1974            == DEFAULT_BATCH_STATE_TREE_HEIGHT as usize
1975        {
1976            let inclusion_proof_inputs =
1977                InclusionProofInputs::new(inclusion_proofs.as_slice()).unwrap();
1978            (
1979                Some(BatchInclusionJsonStruct::from_inclusion_proof_inputs(
1980                    &inclusion_proof_inputs,
1981                )),
1982                None,
1983            )
1984        } else if height == account_compression::utils::constants::STATE_MERKLE_TREE_HEIGHT as usize
1985        {
1986            let inclusion_proof_inputs = InclusionProofInputsLegacy(inclusion_proofs.as_slice());
1987            (
1988                None,
1989                Some(BatchInclusionJsonStructLegacy::from_inclusion_proof_inputs(
1990                    &inclusion_proof_inputs,
1991                )),
1992            )
1993        } else {
1994            return Err(IndexerError::CustomError(
1995                "Unsupported tree height".to_string(),
1996            ));
1997        };
1998
1999        Ok((batch_inclusion_proof_inputs, legacy, account_proof_inputs))
2000    }
2001
2002    async fn process_non_inclusion_proofs(
2003        &self,
2004        address_merkle_tree_pubkeys: &[Pubkey],
2005        addresses: Vec<[u8; 32]>,
2006    ) -> Result<
2007        (
2008            Option<BatchNonInclusionJsonStruct>,
2009            Option<BatchNonInclusionJsonStructLegacy>,
2010            Vec<AddressProofInputs>,
2011        ),
2012        IndexerError,
2013    > {
2014        let mut non_inclusion_proofs = Vec::new();
2015        let mut address_root_indices = Vec::new();
2016        let mut tree_heights = Vec::new();
2017        for (i, address) in addresses.iter().enumerate() {
2018            let address_tree = self
2019                .address_merkle_trees
2020                .iter()
2021                .find(|x| x.accounts.merkle_tree == address_merkle_tree_pubkeys[i])
2022                .unwrap();
2023            tree_heights.push(address_tree.height());
2024
2025            let proof_inputs = address_tree.get_non_inclusion_proof_inputs(address)?;
2026            non_inclusion_proofs.push(proof_inputs);
2027
2028            let (root_index, root, tree_type) = match &address_tree.merkle_tree {
2029                super::address_tree::IndexedMerkleTreeVersion::V1(tree) => (
2030                    tree.merkle_tree.get_history_root_index().unwrap() + 1,
2031                    tree.merkle_tree.root(),
2032                    TreeType::AddressV1,
2033                ),
2034                super::address_tree::IndexedMerkleTreeVersion::V2(tree) => (
2035                    tree.merkle_tree.get_history_root_index_v2().unwrap(),
2036                    tree.merkle_tree.root(),
2037                    TreeType::AddressV2,
2038                ),
2039            };
2040            address_root_indices.push(AddressProofInputs {
2041                root_index,
2042                root,
2043                address: *address,
2044                tree_info: light_client::indexer::TreeInfo {
2045                    cpi_context: None,
2046                    next_tree_info: None,
2047                    queue: address_tree.accounts.queue,
2048                    tree: address_tree.accounts.merkle_tree,
2049                    tree_type,
2050                },
2051            });
2052        }
2053        // if tree heights are not the same, panic
2054        if tree_heights.iter().any(|&x| x != tree_heights[0]) {
2055            return Err(IndexerError::CustomError(format!(
2056                "All address merkle trees must have the same height {:?}",
2057                tree_heights
2058            )));
2059        }
2060        let (batch_non_inclusion_proof_inputs, batch_non_inclusion_proof_inputs_legacy) =
2061            if tree_heights[0] == 26 {
2062                let non_inclusion_proof_inputs =
2063                    NonInclusionProofInputsLegacy::new(non_inclusion_proofs.as_slice());
2064                (
2065                    None,
2066                    Some(
2067                        BatchNonInclusionJsonStructLegacy::from_non_inclusion_proof_inputs(
2068                            &non_inclusion_proof_inputs,
2069                        ),
2070                    ),
2071                )
2072            } else if tree_heights[0] == 40 {
2073                let non_inclusion_proof_inputs =
2074                    NonInclusionProofInputs::new(non_inclusion_proofs.as_slice()).unwrap();
2075                (
2076                    Some(
2077                        BatchNonInclusionJsonStruct::from_non_inclusion_proof_inputs(
2078                            &non_inclusion_proof_inputs,
2079                        ),
2080                    ),
2081                    None,
2082                )
2083            } else {
2084                return Err(IndexerError::CustomError(
2085                    "Unsupported tree height".to_string(),
2086                ));
2087            };
2088        Ok((
2089            batch_non_inclusion_proof_inputs,
2090            batch_non_inclusion_proof_inputs_legacy,
2091            address_root_indices,
2092        ))
2093    }
2094}
2095
2096impl TestIndexer {
2097    async fn _get_validity_proof_v1_implementation(
2098        &self,
2099        hashes: Vec<[u8; 32]>,
2100        new_addresses_with_trees: Vec<AddressWithTree>,
2101    ) -> Result<ValidityProofWithContext, IndexerError> {
2102        let mut state_merkle_tree_pubkeys = Vec::new();
2103
2104        for hash in hashes.iter() {
2105            let account = self.get_compressed_account_by_hash(*hash, None).await?;
2106            let account_data = account.value.ok_or(IndexerError::AccountNotFound)?;
2107            state_merkle_tree_pubkeys.push(account_data.tree_info.tree);
2108        }
2109
2110        let state_merkle_tree_pubkeys = if state_merkle_tree_pubkeys.is_empty() {
2111            None
2112        } else {
2113            Some(state_merkle_tree_pubkeys)
2114        };
2115        let hashes = if hashes.is_empty() {
2116            None
2117        } else {
2118            Some(hashes)
2119        };
2120        let new_addresses = if new_addresses_with_trees.is_empty() {
2121            None
2122        } else {
2123            Some(
2124                new_addresses_with_trees
2125                    .iter()
2126                    .map(|x| x.address)
2127                    .collect::<Vec<[u8; 32]>>(),
2128            )
2129        };
2130        let address_merkle_tree_pubkeys = if new_addresses_with_trees.is_empty() {
2131            None
2132        } else {
2133            Some(
2134                new_addresses_with_trees
2135                    .iter()
2136                    .map(|x| x.tree)
2137                    .collect::<Vec<Pubkey>>(),
2138            )
2139        };
2140
2141        {
2142            let compressed_accounts = hashes;
2143            if compressed_accounts.is_some()
2144                && ![1usize, 2usize, 3usize, 4usize, 8usize]
2145                    .contains(&compressed_accounts.as_ref().unwrap().len())
2146            {
2147                return Err(IndexerError::CustomError(format!(
2148                    "compressed_accounts must be of length 1, 2, 3, 4 or 8 != {}",
2149                    compressed_accounts.unwrap().len()
2150                )));
2151            }
2152            if new_addresses.is_some()
2153                && ![1usize, 2usize, 3usize, 4usize, 8usize]
2154                    .contains(&new_addresses.as_ref().unwrap().len())
2155            {
2156                return Err(IndexerError::CustomError(format!(
2157                    "new_addresses must be of length 1, 2, 3, 4 or 8 != {}",
2158                    new_addresses.unwrap().len()
2159                )));
2160            }
2161            let client = Client::new();
2162            let (account_proof_inputs, address_proof_inputs, json_payload) =
2163                match (compressed_accounts, new_addresses) {
2164                    (Some(accounts), None) => {
2165                        let (payload, payload_legacy, indices) = self
2166                            .process_inclusion_proofs(
2167                                &state_merkle_tree_pubkeys.unwrap(),
2168                                &accounts,
2169                            )
2170                            .await?;
2171                        if let Some(payload) = payload {
2172                            (indices, Vec::new(), payload.to_string())
2173                        } else {
2174                            (indices, Vec::new(), payload_legacy.unwrap().to_string())
2175                        }
2176                    }
2177                    (None, Some(addresses)) => {
2178                        let (payload, payload_legacy, indices) = self
2179                            .process_non_inclusion_proofs(
2180                                address_merkle_tree_pubkeys.unwrap().as_slice(),
2181                                addresses,
2182                            )
2183                            .await?;
2184                        let payload_string = if let Some(payload) = payload {
2185                            payload.to_string()
2186                        } else {
2187                            payload_legacy.unwrap().to_string()
2188                        };
2189                        (Vec::new(), indices, payload_string)
2190                    }
2191                    (Some(accounts), Some(addresses)) => {
2192                        let (inclusion_payload, inclusion_payload_legacy, inclusion_indices) = self
2193                            .process_inclusion_proofs(
2194                                &state_merkle_tree_pubkeys.unwrap(),
2195                                &accounts,
2196                            )
2197                            .await?;
2198
2199                        let (
2200                            non_inclusion_payload,
2201                            non_inclusion_payload_legacy,
2202                            non_inclusion_indices,
2203                        ) = self
2204                            .process_non_inclusion_proofs(
2205                                address_merkle_tree_pubkeys.unwrap().as_slice(),
2206                                addresses,
2207                            )
2208                            .await?;
2209                        let json_payload = if let Some(non_inclusion_payload) =
2210                            non_inclusion_payload
2211                        {
2212                            let public_input_hash = BigInt::from_bytes_be(
2213                                num_bigint::Sign::Plus,
2214                                &create_hash_chain_from_slice(&[
2215                                    bigint_to_u8_32(
2216                                        &string_to_big_int(
2217                                            &inclusion_payload.as_ref().unwrap().public_input_hash,
2218                                        )
2219                                        .unwrap(),
2220                                    )
2221                                    .unwrap(),
2222                                    bigint_to_u8_32(
2223                                        &string_to_big_int(
2224                                            &non_inclusion_payload.public_input_hash,
2225                                        )
2226                                        .unwrap(),
2227                                    )
2228                                    .unwrap(),
2229                                ])
2230                                .unwrap(),
2231                            );
2232
2233                            CombinedJsonStruct {
2234                                circuit_type: ProofType::Combined.to_string(),
2235                                state_tree_height: DEFAULT_BATCH_STATE_TREE_HEIGHT,
2236                                address_tree_height: DEFAULT_BATCH_ADDRESS_TREE_HEIGHT,
2237                                public_input_hash: big_int_to_string(&public_input_hash),
2238                                inclusion: inclusion_payload.unwrap().inputs,
2239                                non_inclusion: non_inclusion_payload.inputs,
2240                            }
2241                            .to_string()
2242                        } else if let Some(non_inclusion_payload) = non_inclusion_payload_legacy {
2243                            CombinedJsonStructLegacy {
2244                                circuit_type: ProofType::Combined.to_string(),
2245                                state_tree_height: 26,
2246                                address_tree_height: 26,
2247                                inclusion: inclusion_payload_legacy.unwrap().inputs,
2248                                non_inclusion: non_inclusion_payload.inputs,
2249                            }
2250                            .to_string()
2251                        } else {
2252                            panic!("Unsupported tree height")
2253                        };
2254                        (inclusion_indices, non_inclusion_indices, json_payload)
2255                    }
2256                    _ => {
2257                        panic!(
2258                            "At least one of compressed_accounts or new_addresses must be provided"
2259                        )
2260                    }
2261                };
2262
2263            let mut retries = 3;
2264            while retries > 0 {
2265                let response_result = client
2266                    .post(format!("{}{}", SERVER_ADDRESS, PROVE_PATH))
2267                    .header("Content-Type", "text/plain; charset=utf-8")
2268                    .body(json_payload.clone())
2269                    .send()
2270                    .await;
2271                if let Ok(response_result) = response_result {
2272                    if response_result.status().is_success() {
2273                        let body = response_result.text().await.unwrap();
2274                        let proof_json = deserialize_gnark_proof_json(&body).unwrap();
2275                        let (proof_a, proof_b, proof_c) = proof_from_json_struct(proof_json);
2276                        let (proof_a, proof_b, proof_c) =
2277                            compress_proof(&proof_a, &proof_b, &proof_c);
2278                        return Ok(ValidityProofWithContext {
2279                            accounts: account_proof_inputs,
2280                            addresses: address_proof_inputs,
2281                            proof: CompressedProof {
2282                                a: proof_a,
2283                                b: proof_b,
2284                                c: proof_c,
2285                            }
2286                            .into(),
2287                        });
2288                    }
2289                } else {
2290                    println!("Error: {:#?}", response_result);
2291                    tokio::time::sleep(Duration::from_secs(5)).await;
2292                    retries -= 1;
2293                }
2294            }
2295            Err(IndexerError::CustomError(
2296                "Failed to get proof from server".to_string(),
2297            ))
2298        }
2299    }
2300}