photon_indexer/api/method/
get_multiple_new_address_proofs.rs1use sea_orm::{
2 ConnectionTrait, DatabaseBackend, DatabaseConnection, DatabaseTransaction, Statement,
3 TransactionTrait,
4};
5use serde::{Deserialize, Serialize};
6use solana_program::pubkey;
7use solana_sdk::pubkey::Pubkey;
8use utoipa::ToSchema;
9
10use crate::api::error::PhotonApiError;
11use crate::common::typedefs::hash::Hash;
12use crate::common::typedefs::serializable_pubkey::SerializablePubkey;
13use crate::ingester::persist::persisted_indexed_merkle_tree::get_exclusion_range_with_proof;
14
15pub const ADDRESS_TREE_HEIGHT: u32 = 27;
16pub const ADDRESS_TREE_ADDRESS: Pubkey = pubkey!("amt1Ayt45jfbdw5YSo7iz6WZxUmnZsQTYXy82hVwyC2");
17pub const MAX_ADDRESSES: usize = 50;
18
19use super::utils::Context;
20
21#[derive(Debug, Clone, Serialize, Deserialize, ToSchema, PartialEq, Eq)]
22#[serde(deny_unknown_fields, rename_all = "camelCase")]
23#[allow(non_snake_case)]
24pub struct AddressWithTree {
25 pub address: SerializablePubkey,
26 pub tree: SerializablePubkey,
27}
28
29#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
30#[serde(deny_unknown_fields, rename_all = "camelCase")]
31#[allow(non_snake_case)]
32pub struct MerkleContextWithNewAddressProof {
33 pub root: Hash,
34 pub address: SerializablePubkey,
35 pub lowerRangeAddress: SerializablePubkey,
36 pub higherRangeAddress: SerializablePubkey,
37 pub nextIndex: u32,
38 pub proof: Vec<Hash>,
39 pub merkleTree: SerializablePubkey,
40 pub rootSeq: u64,
41 pub lowElementLeafIndex: u32,
42}
43
44#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
46pub struct GetMultipleNewAddressProofsResponse {
47 pub context: Context,
48 pub value: Vec<MerkleContextWithNewAddressProof>,
49}
50
51pub async fn get_multiple_new_address_proofs_helper(
52 txn: &DatabaseTransaction,
53 addresses: Vec<AddressWithTree>,
54) -> Result<Vec<MerkleContextWithNewAddressProof>, PhotonApiError> {
55 if addresses.is_empty() {
56 return Err(PhotonApiError::ValidationError(
57 "No addresses provided".to_string(),
58 ));
59 }
60
61 if addresses.len() > MAX_ADDRESSES {
62 return Err(PhotonApiError::ValidationError(
63 format!(
64 "Too many addresses requested {}. Maximum allowed: {}",
65 addresses.len(),
66 MAX_ADDRESSES
67 )
68 .to_string(),
69 ));
70 }
71
72 let mut new_address_proofs: Vec<MerkleContextWithNewAddressProof> = Vec::new();
73
74 for AddressWithTree { address, tree } in addresses {
75 let (model, proof) = get_exclusion_range_with_proof(
76 txn,
77 tree.to_bytes_vec(),
78 ADDRESS_TREE_HEIGHT,
79 address.to_bytes_vec(),
80 )
81 .await?;
82 let new_address_proof = MerkleContextWithNewAddressProof {
83 root: proof.root,
84 address,
85 lowerRangeAddress: SerializablePubkey::try_from(model.value)?,
86 higherRangeAddress: SerializablePubkey::try_from(model.next_value)?,
87 nextIndex: model.next_index as u32,
88 proof: proof.proof,
89 lowElementLeafIndex: model.leaf_index as u32,
90 merkleTree: tree,
91 rootSeq: proof.rootSeq,
92 };
93 new_address_proofs.push(new_address_proof);
94 }
95 Ok(new_address_proofs)
96}
97
98#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, ToSchema)]
99pub struct AddressList(pub Vec<SerializablePubkey>);
100
101pub async fn get_multiple_new_address_proofs(
102 conn: &DatabaseConnection,
103 addresses: AddressList,
104) -> Result<GetMultipleNewAddressProofsResponse, PhotonApiError> {
105 let addresses_with_trees = AddressListWithTrees(
106 addresses
107 .0
108 .into_iter()
109 .map(|address| AddressWithTree {
110 address,
111 tree: SerializablePubkey::from(ADDRESS_TREE_ADDRESS),
112 })
113 .collect(),
114 );
115
116 get_multiple_new_address_proofs_v2(conn, addresses_with_trees).await
117}
118
119#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, ToSchema)]
120pub struct AddressListWithTrees(pub Vec<AddressWithTree>);
121
122pub async fn get_multiple_new_address_proofs_v2(
124 conn: &DatabaseConnection,
125 addresses_with_trees: AddressListWithTrees,
126) -> Result<GetMultipleNewAddressProofsResponse, PhotonApiError> {
127 let context = Context::extract(conn).await?;
128 let tx = conn.begin().await?;
129 if tx.get_database_backend() == DatabaseBackend::Postgres {
130 tx.execute(Statement::from_string(
131 tx.get_database_backend(),
132 "SET TRANSACTION ISOLATION LEVEL REPEATABLE READ;".to_string(),
133 ))
134 .await?;
135 }
136
137 let new_address_proofs =
138 get_multiple_new_address_proofs_helper(&tx, addresses_with_trees.0).await?;
139 tx.commit().await?;
140
141 Ok(GetMultipleNewAddressProofsResponse {
142 value: new_address_proofs,
143 context,
144 })
145}