celestia_types/
merkle_proof.rs1use celestia_proto::celestia::core::v1::proof::Proof as RawMerkleProof;
2use serde::{Deserialize, Serialize};
3use tendermint::crypto::default::Sha256;
4use tendermint::merkle::{Hash, MerkleHash};
5use tendermint_proto::Protobuf;
6
7use crate::{
8 bail_validation, bail_verification, validation_error, verification_error, Error, Result,
9};
10
11#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
13#[serde(try_from = "RawMerkleProof", into = "RawMerkleProof")]
14pub struct MerkleProof {
15 pub index: usize,
17 pub total: usize,
19 pub leaf_hash: Hash,
21 pub aunts: Vec<Hash>,
23}
24
25impl MerkleProof {
26 pub fn new(leaf_to_prove: usize, leaves: &[impl AsRef<[u8]>]) -> Result<(Self, Hash)> {
44 if leaf_to_prove >= leaves.len() {
45 return Err(Error::IndexOutOfRange(leaf_to_prove, leaves.len()));
46 }
47
48 let tree_height = leaves.len().next_power_of_two().ilog2() as usize;
49 let mut aunts = Vec::with_capacity(tree_height);
50
51 let root = hash_leaves_collecting_aunts(0, leaf_to_prove, leaves, &mut aunts);
52 let proof = Self {
53 index: leaf_to_prove,
54 total: leaves.len(),
55 leaf_hash: Sha256::default().leaf_hash(leaves[leaf_to_prove].as_ref()),
56 aunts,
57 };
58
59 Ok((proof, root))
60 }
61
62 pub fn verify(&self, leaf: impl AsRef<[u8]>, root: Hash) -> Result<()> {
72 let mut hasher = Sha256::default();
73 let leaf = hasher.leaf_hash(leaf.as_ref());
74
75 if leaf != self.leaf_hash {
76 return Err(verification_error!("proof created for a different leaf").into());
77 }
78
79 let computed_root = subtree_root_from_aunts(self.index, self.total, leaf, &self.aunts)?;
80
81 if computed_root != root {
82 return Err(Error::RootMismatch);
83 }
84
85 Ok(())
86 }
87}
88
89fn hash_leaves_collecting_aunts(
92 first_leaf_index: usize,
94 leaf_to_prove: usize,
95 leaves: &[impl AsRef<[u8]>],
96 aunts: &mut Vec<Hash>,
97) -> Hash {
98 let mut hasher = Sha256::default();
99 let total = leaves.len();
100
101 match total {
102 0 => hasher.empty_hash(),
104 1 => hasher.leaf_hash(leaves[0].as_ref()),
106 _ => {
108 let subtrees_split = total.next_power_of_two() / 2;
109 let left = hash_leaves_collecting_aunts(
110 first_leaf_index,
111 leaf_to_prove,
112 &leaves[..subtrees_split],
113 aunts,
114 );
115 let right = hash_leaves_collecting_aunts(
116 first_leaf_index + subtrees_split,
117 leaf_to_prove,
118 &leaves[subtrees_split..],
119 aunts,
120 );
121
122 if (first_leaf_index..first_leaf_index + total).contains(&leaf_to_prove) {
124 if leaf_to_prove < first_leaf_index + subtrees_split {
125 aunts.push(right)
127 } else {
128 aunts.push(left)
130 }
131 }
132
133 hasher.inner_hash(left, right)
134 }
135 }
136}
137
138fn subtree_root_from_aunts(index: usize, total: usize, leaf: Hash, aunts: &[Hash]) -> Result<Hash> {
141 debug_assert_ne!(total, 0);
142
143 let root = if total == 1 {
144 if !aunts.is_empty() {
146 bail_verification!("extra aunts in proof");
147 }
148 leaf
149 } else {
150 let mut hasher = Sha256::default();
151 let subtrees_split = total.next_power_of_two() / 2;
152
153 let (sibling, aunts) = aunts
155 .split_last()
156 .ok_or_else(|| verification_error!("aunts missing in proof"))?;
157
158 if index < subtrees_split {
159 let left_hash = subtree_root_from_aunts(index, subtrees_split, leaf, aunts)?;
161 hasher.inner_hash(left_hash, *sibling)
162 } else {
163 let right_hash = subtree_root_from_aunts(
165 index - subtrees_split,
166 total - subtrees_split,
167 leaf,
168 aunts,
169 )?;
170 hasher.inner_hash(*sibling, right_hash)
171 }
172 };
173
174 Ok(root)
175}
176
177impl Protobuf<RawMerkleProof> for MerkleProof {}
178
179impl TryFrom<RawMerkleProof> for MerkleProof {
180 type Error = Error;
181
182 fn try_from(value: RawMerkleProof) -> Result<Self, Self::Error> {
183 if value.index < 0 {
184 bail_validation!("negative index");
185 }
186 if value.total <= 0 {
187 bail_validation!("total <= 0");
188 }
189 Ok(Self {
190 index: value.index as usize,
191 total: value.total as usize,
192 leaf_hash: value
193 .leaf_hash
194 .try_into()
195 .map_err(|_| validation_error!("invalid hash size"))?,
196 aunts: value
197 .aunts
198 .into_iter()
199 .map(TryInto::try_into)
200 .collect::<Result<_, _>>()
201 .map_err(|_| validation_error!("invalid hash size"))?,
202 })
203 }
204}
205
206impl From<MerkleProof> for RawMerkleProof {
207 fn from(value: MerkleProof) -> Self {
208 Self {
209 index: value.index as i64,
210 total: value.total as i64,
211 leaf_hash: value.leaf_hash.into(),
212 aunts: value.aunts.into_iter().map(Into::into).collect(),
213 }
214 }
215}
216
217#[cfg(test)]
218mod tests {
219 use tendermint::crypto::default::Sha256;
220 use tendermint::merkle::simple_hash_from_byte_vectors;
221
222 use crate::test_utils::random_bytes;
223
224 use super::MerkleProof;
225
226 #[test]
227 fn create_and_verify() {
228 for _ in 0..100 {
229 let leaf_size = (rand::random::<usize>() % 1024) + 1;
230 let leaves_amount = 8;
231 let data = random_bytes(leaves_amount * leaf_size);
232
233 let leaves: Vec<_> = data.chunks(leaf_size).collect();
234 let leaf_to_prove = rand::random::<usize>() % leaves_amount;
235
236 let (proof, root) = MerkleProof::new(leaf_to_prove, &leaves).unwrap();
237
238 proof.verify(leaves[leaf_to_prove], root).unwrap();
239 proof.verify(random_bytes(leaf_size), root).unwrap_err();
240 proof
241 .verify(leaves[leaf_to_prove], rand::random())
242 .unwrap_err();
243 }
244 }
245
246 #[test]
247 fn tendermint_compatibility() {
248 for _ in 0..100 {
249 let leaves_amount = (rand::random::<usize>() % 500) + 1;
250 let leaves: Vec<_> = (0..leaves_amount)
251 .map(|_| {
252 let leaf_size = (rand::random::<usize>() % 1024) + 1;
253 random_bytes(leaf_size)
254 })
255 .collect();
256
257 let (_, root) = MerkleProof::new(0, &leaves).unwrap();
258 let tendermint_root = simple_hash_from_byte_vectors::<Sha256>(&leaves);
259
260 assert_eq!(root, tendermint_root);
261 }
262 }
263}