photon_indexer/api/method/
get_multiple_new_address_proofs.rs

1use 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// We do not use generics to simplify documentation generation.
45#[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
122// V2 is the same as V1, but it takes a list of AddressWithTree instead of AddressList.
123pub 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}