1use light_account::PackedAccounts;
2use light_compressed_account::instruction_data::compressed_proof::CompressedProof;
3use light_sdk::instruction::{PackedAddressTreeInfo, PackedStateTreeInfo, ValidityProof};
4use solana_pubkey::Pubkey;
5
6use super::{
7 super::{base58::decode_base58_to_fixed_array, tree_info::QUEUE_TREE_MAPPING, IndexerError},
8 tree::TreeInfo,
9};
10
11fn vec_i64_to_fixed_array<const N: usize>(v: &[i64]) -> Result<[u8; N], IndexerError> {
13 if v.len() != N {
14 return Err(IndexerError::decode_error(
15 "proof",
16 format!("expected {} bytes, got {}", N, v.len()),
17 ));
18 }
19 let mut arr = [0u8; N];
20 for (i, &val) in v.iter().enumerate() {
21 arr[i] = val as u8;
22 }
23 Ok(arr)
24}
25
26#[derive(Debug, Clone, PartialEq, Default)]
27pub struct MerkleProofWithContext {
28 pub proof: Vec<[u8; 32]>,
29 pub root: [u8; 32],
30 pub leaf_index: u64,
31 pub leaf: [u8; 32],
32 pub merkle_tree: [u8; 32],
33 pub root_seq: u64,
34 pub tx_hash: Option<[u8; 32]>,
35 pub account_hash: [u8; 32],
36}
37
38#[derive(Debug, Clone, PartialEq, Default)]
39pub struct MerkleProof {
40 pub hash: [u8; 32],
41 pub leaf_index: u64,
42 pub merkle_tree: Pubkey,
43 pub proof: Vec<[u8; 32]>,
44 pub root_seq: u64,
45 pub root: [u8; 32],
46}
47
48#[derive(Debug, Clone, Copy, PartialEq)]
49pub struct AddressWithTree {
50 pub address: super::Address,
51 pub tree: Pubkey,
52}
53
54#[derive(Clone, Default, Debug, PartialEq)]
55pub struct NewAddressProofWithContext {
56 pub merkle_tree: Pubkey,
57 pub root: [u8; 32],
58 pub root_seq: u64,
59 pub low_address_index: u64,
60 pub low_address_value: [u8; 32],
61 pub low_address_next_index: u64,
62 pub low_address_next_value: [u8; 32],
63 pub low_address_proof: Vec<[u8; 32]>,
64 pub new_low_element: Option<light_indexed_merkle_tree::array::IndexedElement<usize>>,
65 pub new_element: Option<light_indexed_merkle_tree::array::IndexedElement<usize>>,
66 pub new_element_next_value: Option<num_bigint::BigUint>,
67}
68
69#[derive(Debug, Default, Clone, PartialEq)]
70pub struct ValidityProofWithContext {
71 pub proof: ValidityProof,
72 pub accounts: Vec<AccountProofInputs>,
73 pub addresses: Vec<AddressProofInputs>,
74}
75
76impl ValidityProofWithContext {
79 pub fn get_root_indices(&self) -> Vec<Option<u16>> {
80 self.accounts
81 .iter()
82 .map(|account| account.root_index.root_index())
83 .collect()
84 }
85
86 pub fn get_address_root_indices(&self) -> Vec<u16> {
87 self.addresses
88 .iter()
89 .map(|address| address.root_index)
90 .collect()
91 }
92}
93
94#[derive(Clone, Default, Debug, PartialEq)]
95pub struct AccountProofInputs {
96 pub hash: [u8; 32],
97 pub root: [u8; 32],
98 pub root_index: RootIndex,
99 pub leaf_index: u64,
100 pub tree_info: TreeInfo,
101}
102
103#[derive(Clone, Default, Copy, Debug, PartialEq)]
104pub struct RootIndex {
105 proof_by_index: bool,
106 root_index: u16,
107}
108
109impl RootIndex {
110 pub fn new_none() -> Self {
111 Self {
112 proof_by_index: true,
113 root_index: 0,
114 }
115 }
116
117 pub fn new_some(root_index: u16) -> Self {
118 Self {
119 proof_by_index: false,
120 root_index,
121 }
122 }
123
124 pub fn proof_by_index(&self) -> bool {
125 self.proof_by_index
126 }
127
128 pub fn root_index(&self) -> Option<u16> {
129 if !self.proof_by_index {
130 Some(self.root_index)
131 } else {
132 None
133 }
134 }
135}
136
137impl AccountProofInputs {
138 pub fn from_api_model(
139 value: &photon_api::types::AccountProofInputs,
140 ) -> Result<Self, IndexerError> {
141 let root_index = {
142 if value.root_index.prove_by_index {
143 RootIndex::new_none()
144 } else {
145 RootIndex::new_some(value.root_index.root_index as u16)
146 }
147 };
148 Ok(Self {
149 hash: decode_base58_to_fixed_array(&value.hash)?,
150 root: decode_base58_to_fixed_array(&value.root)?,
151 root_index,
152 leaf_index: value.leaf_index,
153 tree_info: TreeInfo::from_api_model(&value.merkle_context)?,
154 })
155 }
156}
157
158#[derive(Clone, Default, Debug, PartialEq)]
159pub struct AddressProofInputs {
160 pub address: [u8; 32],
161 pub root: [u8; 32],
162 pub root_index: u16,
163 pub tree_info: TreeInfo,
164}
165
166impl AddressProofInputs {
167 pub fn from_api_model(
168 value: &photon_api::types::AddressProofInputs,
169 ) -> Result<Self, IndexerError> {
170 Ok(Self {
171 address: decode_base58_to_fixed_array(&value.address)?,
172 root: decode_base58_to_fixed_array(&value.root)?,
173 root_index: value.root_index,
174 tree_info: TreeInfo::from_api_model(&value.merkle_context)?,
175 })
176 }
177}
178
179#[derive(Clone, Default, Debug, PartialEq)]
180pub struct PackedStateTreeInfos {
181 pub packed_tree_infos: Vec<PackedStateTreeInfo>,
182 pub output_tree_index: u8,
183}
184
185#[derive(Clone, Default, Debug, PartialEq)]
186pub struct PackedTreeInfos {
187 pub state_trees: Option<PackedStateTreeInfos>,
188 pub address_trees: Vec<PackedAddressTreeInfo>,
189}
190
191impl ValidityProofWithContext {
192 pub fn pack_tree_infos(&self, packed_accounts: &mut PackedAccounts) -> PackedTreeInfos {
193 let mut packed_tree_infos = Vec::new();
194 let mut address_trees = Vec::new();
195 let mut output_tree_index = None;
196 for account in self.accounts.iter() {
197 let merkle_tree_pubkey_index = packed_accounts.insert_or_get(account.tree_info.tree);
199 let queue_pubkey_index = packed_accounts.insert_or_get(account.tree_info.queue);
200 let tree_info_packed = PackedStateTreeInfo {
201 root_index: account.root_index.root_index,
202 merkle_tree_pubkey_index,
203 queue_pubkey_index,
204 leaf_index: account.leaf_index as u32,
205 prove_by_index: account.root_index.proof_by_index(),
206 };
207 packed_tree_infos.push(tree_info_packed);
208
209 if let Some(next) = account.tree_info.next_tree_info {
212 let index = next.pack_output_tree_index(packed_accounts).unwrap();
215 if output_tree_index.is_none() {
216 output_tree_index = Some(index);
217 }
218 } else {
219 let index = account
222 .tree_info
223 .pack_output_tree_index(packed_accounts)
224 .unwrap();
225 if output_tree_index.is_none() {
226 output_tree_index = Some(index);
227 }
228 }
229 }
230
231 for address in self.addresses.iter() {
232 let address_merkle_tree_pubkey_index =
234 packed_accounts.insert_or_get(address.tree_info.tree);
235 let address_queue_pubkey_index = packed_accounts.insert_or_get(address.tree_info.queue);
236 address_trees.push(PackedAddressTreeInfo {
237 address_merkle_tree_pubkey_index,
238 address_queue_pubkey_index,
239 root_index: address.root_index,
240 });
241 }
242 let packed_tree_infos = if packed_tree_infos.is_empty() {
243 None
244 } else {
245 Some(PackedStateTreeInfos {
246 packed_tree_infos,
247 output_tree_index: output_tree_index.unwrap(),
248 })
249 };
250 PackedTreeInfos {
251 state_trees: packed_tree_infos,
252 address_trees,
253 }
254 }
255
256 pub fn from_api_model(
257 value: photon_api::types::CompressedProofWithContext,
258 num_hashes: usize,
259 ) -> Result<Self, IndexerError> {
260 let proof = ValidityProof::new(Some(CompressedProof {
261 a: vec_i64_to_fixed_array(&value.compressed_proof.a)?,
262 b: vec_i64_to_fixed_array(&value.compressed_proof.b)?,
263 c: vec_i64_to_fixed_array(&value.compressed_proof.c)?,
264 }));
265
266 let accounts = (0..num_hashes)
268 .map(|i| {
269 let tree_pubkey =
270 Pubkey::new_from_array(decode_base58_to_fixed_array(&value.merkle_trees[i])?);
271 let tree_info = QUEUE_TREE_MAPPING.get(&value.merkle_trees[i]).ok_or(
272 IndexerError::MissingResult {
273 context: "conversion".into(),
274 message: format!(
275 "tree not found in QUEUE_TREE_MAPPING: {}",
276 &value.merkle_trees[i]
277 ),
278 },
279 )?;
280
281 Ok(AccountProofInputs {
282 hash: decode_base58_to_fixed_array(&value.leaves[i])?,
283 root: decode_base58_to_fixed_array(&value.roots[i])?,
284 root_index: RootIndex::new_some(value.root_indices[i] as u16),
285 leaf_index: value.leaf_indices[i] as u64,
286 tree_info: TreeInfo {
287 tree_type: tree_info.tree_type,
288 tree: tree_pubkey,
289 queue: tree_info.queue,
290 cpi_context: tree_info.cpi_context,
291 next_tree_info: None,
292 },
293 })
294 })
295 .collect::<Result<Vec<_>, IndexerError>>()?;
296
297 let addresses = if value.root_indices.len() > num_hashes {
299 (num_hashes..value.root_indices.len())
300 .map(|i| {
301 let tree_pubkey = Pubkey::new_from_array(decode_base58_to_fixed_array(
302 &value.merkle_trees[i],
303 )?);
304 let tree_info = QUEUE_TREE_MAPPING.get(&value.merkle_trees[i]).ok_or(
305 IndexerError::MissingResult {
306 context: "conversion".into(),
307 message: "expected value was None".into(),
308 },
309 )?;
310
311 Ok(AddressProofInputs {
312 address: decode_base58_to_fixed_array(&value.leaves[i])?, root: decode_base58_to_fixed_array(&value.roots[i])?,
314 root_index: value.root_indices[i] as u16,
315 tree_info: TreeInfo {
316 tree_type: tree_info.tree_type,
317 tree: tree_pubkey,
318 queue: tree_info.queue,
319 cpi_context: tree_info.cpi_context,
320 next_tree_info: None,
321 },
322 })
323 })
324 .collect::<Result<Vec<_>, IndexerError>>()?
325 } else {
326 Vec::new()
327 };
328
329 Ok(Self {
330 proof,
331 accounts,
332 addresses,
333 })
334 }
335
336 pub fn from_api_model_v2(
337 value: photon_api::types::CompressedProofWithContextV2,
338 ) -> Result<Self, IndexerError> {
339 let proof = if let Some(proof) = value.compressed_proof {
340 ValidityProof::new(Some(CompressedProof {
341 a: vec_i64_to_fixed_array(&proof.a)?,
342 b: vec_i64_to_fixed_array(&proof.b)?,
343 c: vec_i64_to_fixed_array(&proof.c)?,
344 }))
345 } else {
346 ValidityProof::new(None)
347 };
348
349 let accounts = value
350 .accounts
351 .iter()
352 .map(AccountProofInputs::from_api_model)
353 .collect::<Result<Vec<_>, IndexerError>>()?;
354
355 let addresses = value
356 .addresses
357 .iter()
358 .map(AddressProofInputs::from_api_model)
359 .collect::<Result<Vec<_>, IndexerError>>()?;
360
361 Ok(Self {
362 proof,
363 accounts,
364 addresses,
365 })
366 }
367}