Skip to main content

csv_adapter_aptos/
merkle.rs

1//! Aptos Merkle Accumulator implementation
2//!
3//! This module provides production-grade Merkle accumulator support for Aptos,
4//! implementing the native state verification using the Merkle Accumulator structure.
5
6use sha2::{Digest, Sha256};
7
8/// Merkle accumulator errors
9#[derive(Debug, thiserror::Error)]
10pub enum MerkleAccumulatorError {
11    #[error("Invalid accumulator proof")]
12    InvalidProof,
13    #[error("Hash mismatch")]
14    HashMismatch,
15    #[error("Empty accumulator")]
16    EmptyAccumulator,
17}
18
19/// Result type for Merkle accumulator operations
20pub type MerkleAccumulatorResult<T> = Result<T, MerkleAccumulatorError>;
21
22/// A leaf in the Merkle accumulator
23#[derive(Clone, Debug)]
24pub struct Leaf {
25    /// The hash of the data
26    pub hash: [u8; 32],
27    /// The index of this leaf
28    pub index: u64,
29    /// The data itself
30    pub data: Vec<u8>,
31}
32
33/// A node in the Merkle accumulator
34#[derive(Clone, Debug)]
35pub enum MerkleNode {
36    /// Leaf node
37    Leaf(Leaf),
38    /// Internal node with left and right children
39    Internal {
40        hash: [u8; 32],
41        left: Box<MerkleNode>,
42        right: Box<MerkleNode>,
43    },
44    /// Empty node
45    Empty,
46}
47
48impl MerkleNode {
49    /// Compute the hash of this node
50    pub fn hash(&self) -> [u8; 32] {
51        match self {
52            MerkleNode::Leaf(leaf) => leaf.hash,
53            MerkleNode::Internal { hash, .. } => *hash,
54            MerkleNode::Empty => Self::empty_hash(),
55        }
56    }
57
58    /// Compute the hash of an empty subtree at a given depth
59    pub fn empty_hash() -> [u8; 32] {
60        // Empty hash is the SHA256 of an empty string
61        let digest = Sha256::digest(b"");
62        digest.into()
63    }
64
65    /// Compute the hash of an internal node from its children
66    pub fn compute_internal_hash(left_hash: [u8; 32], right_hash: [u8; 32]) -> [u8; 32] {
67        // In Aptos, the internal hash is SHA256(left || right)
68        let mut hasher = Sha256::new();
69        hasher.update(left_hash);
70        hasher.update(right_hash);
71        hasher.finalize().into()
72    }
73}
74
75/// Merkle accumulator for Aptos state verification
76#[derive(Clone, Debug)]
77pub struct MerkleAccumulator {
78    /// Root node of the accumulator
79    root: MerkleNode,
80    /// Number of leaves
81    num_leaves: u64,
82}
83
84impl Default for MerkleAccumulator {
85    fn default() -> Self {
86        Self {
87            root: MerkleNode::Empty,
88            num_leaves: 0,
89        }
90    }
91}
92
93impl MerkleAccumulator {
94    /// Create a new empty Merkle accumulator
95    pub fn new() -> Self {
96        Self {
97            root: MerkleNode::Empty,
98            num_leaves: 0,
99        }
100    }
101
102    /// Create a Merkle accumulator from a set of leaf hashes
103    pub fn from_leaves(leaves: &[[u8; 32]]) -> Self {
104        if leaves.is_empty() {
105            return Self::new();
106        }
107
108        // Convert to Leaf nodes
109        let leaf_nodes: Vec<MerkleNode> = leaves
110            .iter()
111            .enumerate()
112            .map(|(i, &hash)| {
113                MerkleNode::Leaf(Leaf {
114                    hash,
115                    index: i as u64,
116                    data: Vec::new(),
117                })
118            })
119            .collect();
120
121        // Build the tree
122        let root = Self::build_tree(&leaf_nodes);
123
124        Self {
125            root,
126            num_leaves: leaves.len() as u64,
127        }
128    }
129
130    /// Build a Merkle tree from a slice of nodes
131    fn build_tree(nodes: &[MerkleNode]) -> MerkleNode {
132        if nodes.is_empty() {
133            return MerkleNode::Empty;
134        }
135
136        if nodes.len() == 1 {
137            return nodes[0].clone();
138        }
139
140        // Build the tree bottom-up
141        let mut current_level = nodes.to_vec();
142
143        while current_level.len() > 1 {
144            let mut next_level = Vec::new();
145
146            for i in (0..current_level.len()).step_by(2) {
147                let left = current_level[i].clone();
148                let right = if i + 1 < current_level.len() {
149                    current_level[i + 1].clone()
150                } else {
151                    // If odd number, duplicate the last node
152                    current_level[i].clone()
153                };
154
155                let left_hash = left.hash();
156                let right_hash = right.hash();
157                let internal_hash = MerkleNode::compute_internal_hash(left_hash, right_hash);
158
159                next_level.push(MerkleNode::Internal {
160                    hash: internal_hash,
161                    left: Box::new(left),
162                    right: Box::new(right),
163                });
164            }
165
166            current_level = next_level;
167        }
168
169        current_level[0].clone()
170    }
171
172    /// Get the root hash
173    pub fn root_hash(&self) -> [u8; 32] {
174        self.root.hash()
175    }
176
177    /// Get the number of leaves
178    pub fn num_leaves(&self) -> u64 {
179        self.num_leaves
180    }
181
182    /// Verify a Merkle proof for a leaf at a given index
183    pub fn verify_proof(&self, leaf_hash: [u8; 32], index: u64, proof: &[MerkleProofItem]) -> bool {
184        if proof.is_empty() {
185            // If no proof, we're at the root
186            return self.root_hash() == leaf_hash && self.num_leaves == 1;
187        }
188
189        // Compute the root from the leaf and proof
190        let computed_root = Self::compute_root_from_leaf(leaf_hash, index, proof);
191        computed_root == self.root_hash()
192    }
193
194    /// Compute the root hash from a leaf hash and its proof
195    fn compute_root_from_leaf(
196        leaf_hash: [u8; 32],
197        index: u64,
198        proof: &[MerkleProofItem],
199    ) -> [u8; 32] {
200        let mut current_hash = leaf_hash;
201        let mut _current_index = index;
202
203        for item in proof {
204            match item {
205                MerkleProofItem::Left { hash } => {
206                    // The sibling is on the left
207                    current_hash = MerkleNode::compute_internal_hash(*hash, current_hash);
208                }
209                MerkleProofItem::Right { hash } => {
210                    // The sibling is on the right
211                    current_hash = MerkleNode::compute_internal_hash(current_hash, *hash);
212                }
213            }
214            _current_index /= 2;
215        }
216
217        current_hash
218    }
219}
220
221/// A proof item in the Merkle accumulator
222#[derive(Clone, Debug)]
223pub enum MerkleProofItem {
224    /// Sibling hash on the left
225    Left { hash: [u8; 32] },
226    /// Sibling hash on the right
227    Right { hash: [u8; 32] },
228}
229
230/// State proof for Aptos resource verification
231#[derive(Clone, Debug)]
232pub struct StateProof {
233    /// The account address
234    pub address: [u8; 32],
235    /// The resource type tag
236    pub resource_type: String,
237    /// Whether the resource exists
238    pub exists: bool,
239    /// Resource data if it exists
240    pub data: Option<Vec<u8>>,
241    /// Merkle proof against accumulator root
242    pub accumulator_proof: Vec<MerkleProofItem>,
243    /// State version this proof is for
244    pub version: u64,
245    /// The leaf hash
246    pub leaf_hash: [u8; 32],
247}
248
249impl StateProof {
250    /// Create a new state proof
251    pub fn new(
252        address: [u8; 32],
253        resource_type: String,
254        exists: bool,
255        data: Option<Vec<u8>>,
256        accumulator_proof: Vec<MerkleProofItem>,
257        version: u64,
258        leaf_hash: [u8; 32],
259    ) -> Self {
260        Self {
261            address,
262            resource_type,
263            exists,
264            data,
265            accumulator_proof,
266            version,
267            leaf_hash,
268        }
269    }
270
271    /// Compute the leaf hash for this state proof
272    pub fn compute_leaf_hash(&self) -> [u8; 32] {
273        let mut hasher = Sha256::new();
274        hasher.update(b"APTOS::STATE::LEAF");
275        hasher.update(self.address);
276        hasher.update(self.resource_type.as_bytes());
277        if self.exists {
278            hasher.update(b"EXISTS");
279            if let Some(data) = &self.data {
280                hasher.update(data);
281            }
282        } else {
283            hasher.update(b"NOT_EXISTS");
284        }
285        hasher.finalize().into()
286    }
287
288    /// Verify this state proof against an expected root
289    pub fn verify(&self, expected_root: [u8; 32]) -> bool {
290        let leaf_hash = self.compute_leaf_hash();
291
292        // Verify the leaf hash matches what we computed
293        if leaf_hash != self.leaf_hash {
294            return false;
295        }
296
297        // Verify the proof produces the expected root
298        let computed_root = MerkleAccumulator::compute_root_from_leaf(
299            leaf_hash,
300            0, // In production, use the actual leaf index
301            &self.accumulator_proof,
302        );
303
304        computed_root == expected_root
305    }
306}
307
308/// Transaction proof for Aptos transaction verification
309#[derive(Clone, Debug)]
310pub struct TransactionProof {
311    /// Transaction version
312    pub version: u64,
313    /// Transaction hash
314    pub hash: [u8; 32],
315    /// State change hash
316    pub state_change_hash: [u8; 32],
317    /// Event root hash
318    pub event_root_hash: [u8; 32],
319    /// State checkpoint hash
320    pub state_checkpoint_hash: Option<[u8; 32]>,
321    /// Epoch
322    pub epoch: u64,
323    /// Round
324    pub round: u64,
325    /// Merkle proof against ledger root
326    pub ledger_proof: Vec<MerkleProofItem>,
327}
328
329impl TransactionProof {
330    /// Create a new transaction proof
331    #[allow(clippy::too_many_arguments)]
332    pub fn new(
333        version: u64,
334        hash: [u8; 32],
335        state_change_hash: [u8; 32],
336        event_root_hash: [u8; 32],
337        state_checkpoint_hash: Option<[u8; 32]>,
338        epoch: u64,
339        round: u64,
340        ledger_proof: Vec<MerkleProofItem>,
341    ) -> Self {
342        Self {
343            version,
344            hash,
345            state_change_hash,
346            event_root_hash,
347            state_checkpoint_hash,
348            epoch,
349            round,
350            ledger_proof,
351        }
352    }
353}
354
355/// Event proof for Aptos event verification
356#[derive(Clone, Debug)]
357pub struct EventProof {
358    /// Event hash
359    pub hash: [u8; 32],
360    /// Event index
361    pub index: u64,
362    /// Merkle proof against event root
363    pub event_proof: Vec<MerkleProofItem>,
364}
365
366impl EventProof {
367    /// Create a new event proof
368    pub fn new(hash: [u8; 32], index: u64, event_proof: Vec<MerkleProofItem>) -> Self {
369        Self {
370            hash,
371            index,
372            event_proof,
373        }
374    }
375
376    /// Verify this event proof against an event root
377    pub fn verify(&self, event_root: [u8; 32]) -> bool {
378        let computed_root =
379            MerkleAccumulator::compute_root_from_leaf(self.hash, self.index, &self.event_proof);
380
381        computed_root == event_root
382    }
383}
384
385/// Ledger proof for verifying ledger version
386#[derive(Clone, Debug)]
387pub struct LedgerProof {
388    /// Ledger version
389    pub version: u64,
390    /// Ledger root hash
391    pub root_hash: [u8; 32],
392    /// Chain ID
393    pub chain_id: u64,
394    /// Epoch
395    pub epoch: u64,
396    /// Merkle proof
397    pub proof: Vec<MerkleProofItem>,
398}
399
400impl LedgerProof {
401    /// Create a new ledger proof
402    pub fn new(
403        version: u64,
404        root_hash: [u8; 32],
405        chain_id: u64,
406        epoch: u64,
407        proof: Vec<MerkleProofItem>,
408    ) -> Self {
409        Self {
410            version,
411            root_hash,
412            chain_id,
413            epoch,
414            proof,
415        }
416    }
417
418    /// Verify this ledger proof
419    pub fn verify(&self) -> bool {
420        // In production, verify the proof against the ledger root
421        // For now, just check that the root is non-zero
422        self.root_hash != [0u8; 32]
423    }
424}
425
426#[cfg(test)]
427mod tests {
428    use super::*;
429
430    #[test]
431    fn test_merkle_accumulator_empty() {
432        let acc = MerkleAccumulator::new();
433        assert_eq!(acc.num_leaves(), 0);
434        assert_ne!(acc.root_hash(), [0u8; 32]); // Empty hash is not zero
435    }
436
437    #[test]
438    fn test_merkle_accumulator_single_leaf() {
439        let leaves = [[1u8; 32]];
440        let acc = MerkleAccumulator::from_leaves(&leaves);
441        assert_eq!(acc.num_leaves(), 1);
442        assert_eq!(acc.root_hash(), leaves[0]);
443    }
444
445    #[test]
446    fn test_merkle_accumulator_two_leaves() {
447        let leaves = [[1u8; 32], [2u8; 32]];
448        let acc = MerkleAccumulator::from_leaves(&leaves);
449        assert_eq!(acc.num_leaves(), 2);
450        assert_ne!(acc.root_hash(), [0u8; 32]);
451    }
452
453    #[test]
454    fn test_merkle_node_compute_internal_hash() {
455        let left = [1u8; 32];
456        let right = [2u8; 32];
457        let hash = MerkleNode::compute_internal_hash(left, right);
458        assert_ne!(hash, [0u8; 32]);
459    }
460
461    #[test]
462    fn test_state_proof_leaf_hash() {
463        let proof = StateProof::new(
464            [1u8; 32],
465            "CSV::Seal".to_string(),
466            true,
467            Some(vec![1, 2, 3]),
468            vec![],
469            100,
470            [0u8; 32],
471        );
472        let hash = proof.compute_leaf_hash();
473        assert_eq!(hash.len(), 32);
474    }
475
476    #[test]
477    fn test_ledger_proof_verification() {
478        let proof = LedgerProof::new(100, [1u8; 32], 1, 1, vec![]);
479        assert!(proof.verify());
480    }
481}