Skip to main content

light_client/indexer/types/
queue.rs

1use super::super::IndexerError;
2
3#[derive(Debug, Clone, PartialEq, Default)]
4pub struct OutputQueueData {
5    pub leaf_indices: Vec<u64>,
6    pub account_hashes: Vec<[u8; 32]>,
7    pub old_leaves: Vec<[u8; 32]>,
8    pub first_queue_index: u64,
9    /// The tree's next_index - where new leaves will be appended
10    pub next_index: u64,
11    /// Pre-computed hash chains per ZKP batch (from on-chain)
12    pub leaves_hash_chains: Vec<[u8; 32]>,
13}
14
15/// V2 Input Queue Data
16#[derive(Debug, Clone, PartialEq, Default)]
17pub struct InputQueueData {
18    pub leaf_indices: Vec<u64>,
19    pub account_hashes: Vec<[u8; 32]>,
20    pub current_leaves: Vec<[u8; 32]>,
21    pub tx_hashes: Vec<[u8; 32]>,
22    /// Pre-computed nullifiers from indexer
23    pub nullifiers: Vec<[u8; 32]>,
24    pub first_queue_index: u64,
25    /// Pre-computed hash chains per ZKP batch (from on-chain)
26    pub leaves_hash_chains: Vec<[u8; 32]>,
27}
28
29/// State queue data with shared tree nodes for output and input queues
30#[derive(Debug, Clone, PartialEq, Default)]
31pub struct StateQueueData {
32    /// Shared deduplicated tree nodes for state queues (output + input)
33    /// node_index encoding: (level << 56) | position
34    pub nodes: Vec<u64>,
35    pub node_hashes: Vec<[u8; 32]>,
36    /// Initial root for the state tree (shared by output and input queues)
37    pub initial_root: [u8; 32],
38    /// Sequence number of the root
39    pub root_seq: u64,
40    /// Output queue data (if requested)
41    pub output_queue: Option<OutputQueueData>,
42    /// Input queue data (if requested)
43    pub input_queue: Option<InputQueueData>,
44}
45
46/// V2 Address Queue Data with deduplicated nodes
47/// Proofs are reconstructed from `nodes`/`node_hashes` using `low_element_indices`
48#[derive(Debug, Clone, PartialEq, Default)]
49pub struct AddressQueueData {
50    pub addresses: Vec<[u8; 32]>,
51    pub low_element_values: Vec<[u8; 32]>,
52    pub low_element_next_values: Vec<[u8; 32]>,
53    pub low_element_indices: Vec<u64>,
54    pub low_element_next_indices: Vec<u64>,
55    /// Deduplicated node indices - encoding: (level << 56) | position
56    pub nodes: Vec<u64>,
57    /// Hashes corresponding to each node index
58    pub node_hashes: Vec<[u8; 32]>,
59    pub initial_root: [u8; 32],
60    pub leaves_hash_chains: Vec<[u8; 32]>,
61    pub subtrees: Vec<[u8; 32]>,
62    pub start_index: u64,
63    pub root_seq: u64,
64}
65
66impl AddressQueueData {
67    /// Reconstruct a merkle proof for a given low_element_index from the deduplicated nodes.
68    /// The tree_height is needed to know how many levels to traverse.
69    pub fn reconstruct_proof(
70        &self,
71        address_idx: usize,
72        tree_height: u8,
73    ) -> Result<Vec<[u8; 32]>, IndexerError> {
74        let leaf_index = *self.low_element_indices.get(address_idx).ok_or_else(|| {
75            IndexerError::MissingResult {
76                context: "reconstruct_proof".to_string(),
77                message: format!(
78                    "address_idx {} out of bounds for low_element_indices (len {})",
79                    address_idx,
80                    self.low_element_indices.len(),
81                ),
82            }
83        })?;
84        let mut proof = Vec::with_capacity(tree_height as usize);
85        let mut pos = leaf_index;
86
87        for level in 0..tree_height {
88            let sibling_pos = if pos.is_multiple_of(2) {
89                pos + 1
90            } else {
91                pos - 1
92            };
93            let sibling_idx = Self::encode_node_index(level, sibling_pos);
94
95            let hash_idx = self
96                .nodes
97                .iter()
98                .position(|&n| n == sibling_idx)
99                .ok_or_else(|| IndexerError::MissingResult {
100                    context: "reconstruct_proof".to_string(),
101                    message: format!(
102                        "Missing proof node at level {} position {} (encoded: {})",
103                        level, sibling_pos, sibling_idx
104                    ),
105                })?;
106            let hash =
107                self.node_hashes
108                    .get(hash_idx)
109                    .ok_or_else(|| IndexerError::MissingResult {
110                        context: "reconstruct_proof".to_string(),
111                        message: format!(
112                            "node_hashes index {} out of bounds (len {})",
113                            hash_idx,
114                            self.node_hashes.len(),
115                        ),
116                    })?;
117            proof.push(*hash);
118            pos /= 2;
119        }
120
121        Ok(proof)
122    }
123
124    /// Reconstruct all proofs for all addresses
125    pub fn reconstruct_all_proofs(
126        &self,
127        tree_height: u8,
128    ) -> Result<Vec<Vec<[u8; 32]>>, IndexerError> {
129        (0..self.addresses.len())
130            .map(|i| self.reconstruct_proof(i, tree_height))
131            .collect()
132    }
133
134    /// Encode node index: (level << 56) | position
135    #[inline]
136    fn encode_node_index(level: u8, position: u64) -> u64 {
137        ((level as u64) << 56) | position
138    }
139}
140
141/// V2 Queue Elements Result with deduplicated node data
142#[derive(Debug, Clone, PartialEq, Default)]
143pub struct QueueElementsResult {
144    pub state_queue: Option<StateQueueData>,
145    pub address_queue: Option<AddressQueueData>,
146}