light_program_test/indexer/
test_indexer.rs

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