Skip to main content

pessimistic_proof/local_exit_tree/
data.rs

1use agglayer_primitives::{keccak::keccak256_combine, Digest};
2use agglayer_tries::utils::{empty_hash_array_at_height, empty_hash_at_height};
3use serde::{Deserialize, Serialize};
4use serde_with::serde_as;
5use unified_bridge::{LETMerkleProof, LocalExitTreeError};
6
7/// Represents a local exit tree as defined by the LxLy bridge.
8#[serde_as]
9#[derive(Clone, Debug, Serialize, Deserialize)]
10pub struct LocalExitTreeData<const TREE_DEPTH: usize = 32> {
11    /// The layers of the Merkle tree from bottom to top (i.e., the leaves are
12    /// in `layers[0]`)
13    ///
14    /// This tree is contiguous: due to the structure of the LET, the empty
15    /// trees must be right children only.
16    ///
17    /// So if `layers[0]` has `n` exit leaf hashes, then `layers[1] has
18    /// `round_up(n/2)` hashes, etc.
19    #[serde_as(as = "[_; TREE_DEPTH]")]
20    pub layers: [Vec<Digest>; TREE_DEPTH],
21}
22
23impl<const TREE_DEPTH: usize> Default for LocalExitTreeData<TREE_DEPTH> {
24    fn default() -> Self {
25        Self::new()
26    }
27}
28
29impl<const TREE_DEPTH: usize> LocalExitTreeData<TREE_DEPTH> {
30    const MAX_NUM_LEAVES: u32 = ((1u64 << TREE_DEPTH) - 1) as u32;
31
32    /// Creates a new empty [`LocalExitTreeData`].
33    pub fn new() -> Self {
34        LocalExitTreeData {
35            layers: std::array::from_fn(|_| Vec::new()),
36        }
37    }
38
39    /// Creates a new [`LocalExitTreeData`] and populates its leaves.
40    pub fn from_leaves(leaves: impl Iterator<Item = Digest>) -> Result<Self, LocalExitTreeError> {
41        let mut tree = Self::new();
42
43        for leaf in leaves {
44            tree.add_leaf(leaf)?;
45        }
46
47        Ok(tree)
48    }
49
50    /// Appends a leaf to the tree.
51    pub fn add_leaf(&mut self, leaf: Digest) -> Result<u32, LocalExitTreeError> {
52        let leaf_index = self.layers[0].len();
53
54        if leaf_index >= Self::MAX_NUM_LEAVES as usize {
55            return Err(LocalExitTreeError::LeafIndexOverflow);
56        }
57        self.layers[0].push(leaf);
58        let mut index = leaf_index;
59        let mut entry = leaf;
60        for height in 0..TREE_DEPTH - 1 {
61            let sibling = self.get(height, index ^ 1)?;
62            entry = if index & 1 == 1 {
63                keccak256_combine([&sibling, &entry])
64            } else {
65                keccak256_combine([&entry, &sibling])
66            };
67            index >>= 1;
68            if index < self.layers[height + 1].len() {
69                self.layers[height + 1][index] = entry;
70            } else {
71                self.layers[height + 1].push(entry);
72            }
73        }
74
75        leaf_index
76            .try_into()
77            .map_err(|_| LocalExitTreeError::LeafIndexOverflow)
78    }
79
80    pub fn get(&self, height: usize, index: usize) -> Result<Digest, LocalExitTreeError> {
81        if index >= 1 << (TREE_DEPTH - height) {
82            return Err(LocalExitTreeError::IndexOutOfBounds);
83        }
84        Ok(*self.layers[height]
85            .get(index)
86            .unwrap_or(&empty_hash_array_at_height::<TREE_DEPTH>()[height]))
87    }
88
89    pub fn is_empty(&self) -> bool {
90        self.layers[0].is_empty()
91    }
92
93    /// Returns the root of the tree.
94    pub fn get_root(&self) -> Digest {
95        let empty_hash = empty_hash_at_height::<TREE_DEPTH>();
96        let get_last_layer = |i| self.layers[TREE_DEPTH - 1].get(i).unwrap_or(&empty_hash);
97        keccak256_combine([get_last_layer(0), get_last_layer(1)])
98    }
99
100    pub fn get_proof(
101        &self,
102        leaf_index: u32,
103    ) -> Result<LETMerkleProof<TREE_DEPTH>, LocalExitTreeError> {
104        let leaf_index: usize = leaf_index
105            .try_into()
106            .map_err(|_| LocalExitTreeError::LeafIndexOverflow)?;
107        if leaf_index >= self.layers[0].len() {
108            return Err(LocalExitTreeError::IndexOutOfBounds);
109        }
110        let mut siblings = [Default::default(); TREE_DEPTH];
111        let mut index = leaf_index;
112
113        for (height, sibling) in siblings.iter_mut().enumerate().take(TREE_DEPTH) {
114            *sibling = self.get(height, index ^ 1)?;
115            index >>= 1;
116        }
117
118        Ok(LETMerkleProof { siblings })
119    }
120}
121
122#[cfg(test)]
123mod tests {
124    use rand::{random, rng, Rng};
125
126    use crate::local_exit_tree::{data::LocalExitTreeData, LocalExitTree, LocalExitTreeError};
127
128    const TREE_DEPTH: usize = 32;
129
130    fn compare_let_data_let_frontier(num_leaves: usize) {
131        let leaves = (0..num_leaves).map(|_| random()).collect::<Vec<_>>();
132        let local_exit_tree_frontier: LocalExitTree<TREE_DEPTH> =
133            LocalExitTree::from_leaves(leaves.iter().cloned()).unwrap();
134        let local_exit_tree_data: LocalExitTreeData<TREE_DEPTH> =
135            LocalExitTreeData::from_leaves(leaves.into_iter()).unwrap();
136        assert_eq!(
137            local_exit_tree_frontier.get_root(),
138            local_exit_tree_data.get_root()
139        );
140    }
141
142    #[test]
143    fn test_data_vs_frontier_empty() {
144        compare_let_data_let_frontier(0)
145    }
146
147    #[test]
148    fn test_data_vs_frontier_root() {
149        let num_leaves = rng().random_range(1..100.min(1 << TREE_DEPTH));
150        compare_let_data_let_frontier(num_leaves)
151    }
152
153    #[test]
154    fn test_data_vs_frontier_add_leaf() -> Result<(), LocalExitTreeError> {
155        let num_leaves = rng().random_range(1usize..100.min(1 << TREE_DEPTH));
156        let leaves = (0..num_leaves).map(|_| random()).collect::<Vec<_>>();
157        let mut local_exit_tree_data: LocalExitTreeData<TREE_DEPTH> =
158            LocalExitTreeData::from_leaves(leaves.into_iter())?;
159        let mut local_exit_tree_frontier: LocalExitTree<TREE_DEPTH> =
160            LocalExitTree::try_from(&local_exit_tree_data)?;
161        assert_eq!(
162            local_exit_tree_data.get_root(),
163            local_exit_tree_frontier.get_root()
164        );
165        let leaf = random();
166        local_exit_tree_data.add_leaf(leaf)?;
167        local_exit_tree_frontier.add_leaf(leaf)?;
168        assert_eq!(
169            local_exit_tree_data.get_root(),
170            local_exit_tree_frontier.get_root()
171        );
172        Ok(())
173    }
174
175    #[test]
176    fn test_merkle_proofs() {
177        let num_leaves = rng().random_range(1..=100.min(1 << TREE_DEPTH));
178        let leaves = (0..num_leaves).map(|_| random()).collect::<Vec<_>>();
179        let leaf_index = rng().random_range(0..num_leaves);
180        let leaf = leaves[leaf_index];
181        let local_exit_tree_data: LocalExitTreeData<TREE_DEPTH> =
182            LocalExitTreeData::from_leaves(leaves.into_iter()).unwrap();
183        let root = local_exit_tree_data.get_root();
184        let proof = local_exit_tree_data.get_proof(leaf_index as u32).unwrap();
185        assert!(proof.verify(leaf, leaf_index as u32, root));
186    }
187}