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