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;
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;
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 async fn get_indexer_slot(&self, _config: Option<RetryConfig>) -> Result<u64, IndexerError> {
127 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 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 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 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 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 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 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 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 fn encode_node_index(level: u8, position: u64) -> u64 {
637 ((level as u64) << 56) | position
638 }
639
640 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 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 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 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 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 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 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 let mut node_map: HashMap<u64, [u8; 32]> = HashMap::new();
744
745 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 let old_leaves: Vec<[u8; 32]> = leaf_indices
762 .iter()
763 .map(|index| {
764 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 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 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 let current_leaves: Vec<[u8; 32]> = leaf_indices
816 .iter()
817 .map(|index| {
818 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 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 let state_queue = if output_queue.is_some() || input_queue.is_some() {
853 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 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 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 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 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 let mut state_merkle_tree_accounts = env.v1_state_trees.clone();
1256
1257 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 let mut address_merkle_tree_accounts = env.v1_address_trees.clone();
1269
1270 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 ) -> 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 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 ), &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 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 <TestIndexer as TestIndexerExtensions>::add_event_and_compressed_accounts(
1581 self, slot, &event,
1582 );
1583 }
1584
1585 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 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 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 match compressed_account.compressed_account.data.as_ref() {
1654 Some(data) => {
1655 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
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 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 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 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 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 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 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.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 match (inclusion_payload.is_some(), non_inclusion_payload.is_some()) {
2216 (true, true) | (false, false) => {
2217 }
2219 (false, true) => {
2220 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 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}