Skip to main content

auths_verifier/tlog/
proof.rs

1//! Merkle inclusion and consistency proofs.
2
3use serde::{Deserialize, Serialize};
4
5use super::error::TransparencyError;
6use super::types::MerkleHash;
7
8/// Merkle inclusion proof for a single entry in the log.
9///
10/// Proves that a leaf at `index` is included in the tree of `size` leaves
11/// with the given `root`.
12///
13/// Args:
14/// * `index` — Zero-based leaf index.
15/// * `size` — Tree size (number of leaves) when the proof was generated.
16/// * `root` — Merkle root at tree size `size`.
17/// * `hashes` — Sibling hashes from leaf to root.
18///
19/// Usage:
20/// ```ignore
21/// let proof = InclusionProof { index: 5, size: 16, root, hashes: vec![...] };
22/// proof.verify(&leaf_hash)?;
23/// ```
24#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
25#[allow(missing_docs)]
26pub struct InclusionProof {
27    pub index: u64,
28    pub size: u64,
29    pub root: MerkleHash,
30    pub hashes: Vec<MerkleHash>,
31}
32
33impl InclusionProof {
34    /// Verify that `leaf_hash` is included in the tree.
35    pub fn verify(&self, leaf_hash: &MerkleHash) -> Result<(), TransparencyError> {
36        super::merkle::verify_inclusion(leaf_hash, self.index, self.size, &self.hashes, &self.root)
37    }
38}
39
40/// Merkle consistency proof between two tree sizes.
41///
42/// Proves that the tree at `old_size` is a prefix of the tree at `new_size`.
43///
44/// Args:
45/// * `old_size` — Earlier tree size.
46/// * `new_size` — Later tree size.
47/// * `old_root` — Root at `old_size`.
48/// * `new_root` — Root at `new_size`.
49/// * `hashes` — Consistency proof hashes.
50///
51/// Usage:
52/// ```ignore
53/// let proof = ConsistencyProof { old_size: 8, new_size: 16, .. };
54/// proof.verify()?;
55/// ```
56#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
57#[allow(missing_docs)]
58pub struct ConsistencyProof {
59    pub old_size: u64,
60    pub new_size: u64,
61    pub old_root: MerkleHash,
62    pub new_root: MerkleHash,
63    pub hashes: Vec<MerkleHash>,
64}
65
66impl ConsistencyProof {
67    /// Verify that the old tree is a prefix of the new tree.
68    pub fn verify(&self) -> Result<(), TransparencyError> {
69        super::merkle::verify_consistency(
70            self.old_size,
71            self.new_size,
72            &self.hashes,
73            &self.old_root,
74            &self.new_root,
75        )
76    }
77}
78
79#[cfg(test)]
80mod tests {
81    use super::*;
82    use crate::tlog::merkle::{hash_children, hash_leaf};
83
84    #[test]
85    fn inclusion_proof_verify() {
86        let a = hash_leaf(b"a");
87        let b = hash_leaf(b"b");
88        let root = hash_children(&a, &b);
89
90        let proof = InclusionProof {
91            index: 0,
92            size: 2,
93            root,
94            hashes: vec![b],
95        };
96        proof.verify(&a).unwrap();
97    }
98
99    #[test]
100    fn inclusion_proof_json_roundtrip() {
101        let proof = InclusionProof {
102            index: 3,
103            size: 8,
104            root: MerkleHash::from_bytes([0xaa; 32]),
105            hashes: vec![
106                MerkleHash::from_bytes([0xbb; 32]),
107                MerkleHash::from_bytes([0xcc; 32]),
108            ],
109        };
110        let json = serde_json::to_string(&proof).unwrap();
111        let back: InclusionProof = serde_json::from_str(&json).unwrap();
112        assert_eq!(proof, back);
113    }
114
115    #[test]
116    fn consistency_proof_json_roundtrip() {
117        let proof = ConsistencyProof {
118            old_size: 4,
119            new_size: 8,
120            old_root: MerkleHash::from_bytes([0x11; 32]),
121            new_root: MerkleHash::from_bytes([0x22; 32]),
122            hashes: vec![MerkleHash::from_bytes([0x33; 32])],
123        };
124        let json = serde_json::to_string(&proof).unwrap();
125        let back: ConsistencyProof = serde_json::from_str(&json).unwrap();
126        assert_eq!(proof, back);
127    }
128}