1use crate::{
4 nodes::{BranchNode, RlpNode, TrieNode, CHILD_INDEX_RANGE},
5 proof::ProofVerificationError,
6 EMPTY_ROOT_HASH,
7};
8use alloc::vec::Vec;
9use alloy_primitives::{Bytes, B256};
10use alloy_rlp::{Decodable, EMPTY_STRING_CODE};
11use core::ops::Deref;
12use nybbles::Nibbles;
13
14pub fn verify_proof<'a, I>(
19 root: B256,
20 key: Nibbles,
21 expected_value: Option<Vec<u8>>,
22 proof: I,
23) -> Result<(), ProofVerificationError>
24where
25 I: IntoIterator<Item = &'a Bytes>,
26{
27 let mut proof = proof.into_iter().peekable();
28
29 if proof.peek().map_or(true, |node| node.as_ref() == [EMPTY_STRING_CODE]) {
31 return if root == EMPTY_ROOT_HASH {
32 if expected_value.is_none() {
33 Ok(())
34 } else {
35 Err(ProofVerificationError::ValueMismatch {
36 path: key,
37 got: None,
38 expected: expected_value.map(Bytes::from),
39 })
40 }
41 } else {
42 Err(ProofVerificationError::RootMismatch { got: EMPTY_ROOT_HASH, expected: root })
43 };
44 }
45
46 let mut walked_path = Nibbles::with_capacity(key.len());
47 let mut last_decoded_node = Some(NodeDecodingResult::Node(RlpNode::word_rlp(&root)));
48 for node in proof {
49 if Some(RlpNode::from_rlp(node).as_slice()) != last_decoded_node.as_deref() {
52 let got = Some(Bytes::copy_from_slice(node));
53 let expected = last_decoded_node.as_deref().map(Bytes::copy_from_slice);
54 return Err(ProofVerificationError::ValueMismatch { path: walked_path, got, expected });
55 }
56
57 last_decoded_node =
59 process_trie_node(TrieNode::decode(&mut &node[..])?, &mut walked_path, &key)?;
60 }
61
62 last_decoded_node = last_decoded_node.filter(|_| walked_path == key);
64 if last_decoded_node.as_deref() == expected_value.as_deref() {
65 Ok(())
66 } else {
67 Err(ProofVerificationError::ValueMismatch {
68 path: key,
69 got: last_decoded_node.as_deref().map(Bytes::copy_from_slice),
70 expected: expected_value.map(Bytes::from),
71 })
72 }
73}
74
75#[derive(Debug, PartialEq, Eq)]
83enum NodeDecodingResult {
84 Node(RlpNode),
85 Value(Vec<u8>),
86}
87
88impl Deref for NodeDecodingResult {
89 type Target = [u8];
90
91 fn deref(&self) -> &Self::Target {
92 match self {
93 Self::Node(node) => node.as_slice(),
94 Self::Value(value) => value,
95 }
96 }
97}
98
99#[inline]
100fn process_trie_node(
101 node: TrieNode,
102 walked_path: &mut Nibbles,
103 key: &Nibbles,
104) -> Result<Option<NodeDecodingResult>, ProofVerificationError> {
105 let node = match node {
106 TrieNode::Branch(branch) => process_branch(branch, walked_path, key)?,
107 TrieNode::Extension(extension) => {
108 walked_path.extend_from_slice(&extension.key);
109 if extension.child.is_hash() {
110 Some(NodeDecodingResult::Node(extension.child))
111 } else {
112 process_trie_node(TrieNode::decode(&mut &extension.child[..])?, walked_path, key)?
113 }
114 }
115 TrieNode::Leaf(leaf) => {
116 walked_path.extend_from_slice(&leaf.key);
117 Some(NodeDecodingResult::Value(leaf.value))
118 }
119 TrieNode::EmptyRoot => return Err(ProofVerificationError::UnexpectedEmptyRoot),
120 };
121 Ok(node)
122}
123
124#[inline]
125fn process_branch(
126 mut branch: BranchNode,
127 walked_path: &mut Nibbles,
128 key: &Nibbles,
129) -> Result<Option<NodeDecodingResult>, ProofVerificationError> {
130 if let Some(next) = key.get(walked_path.len()) {
131 let mut stack_ptr = branch.as_ref().first_child_index();
132 for index in CHILD_INDEX_RANGE {
133 if branch.state_mask.is_bit_set(index) {
134 if index == *next {
135 walked_path.push(*next);
136
137 let child = branch.stack.remove(stack_ptr);
138 if child.len() == B256::len_bytes() + 1 {
139 return Ok(Some(NodeDecodingResult::Node(child)));
140 } else {
141 match TrieNode::decode(&mut &child[..])? {
143 TrieNode::Branch(child_branch) => {
144 return process_branch(child_branch, walked_path, key);
149 }
150 TrieNode::Extension(child_extension) => {
151 walked_path.extend_from_slice(&child_extension.key);
152
153 match TrieNode::decode(&mut &child_extension.child[..])? {
161 TrieNode::Branch(extension_child_branch) => {
162 return process_branch(
163 extension_child_branch,
164 walked_path,
165 key,
166 );
167 }
168 node @ (TrieNode::EmptyRoot
169 | TrieNode::Extension(_)
170 | TrieNode::Leaf(_)) => {
171 unreachable!("unexpected extension node child: {node:?}")
172 }
173 }
174 }
175 TrieNode::Leaf(child_leaf) => {
176 walked_path.extend_from_slice(&child_leaf.key);
177 return Ok(Some(NodeDecodingResult::Value(child_leaf.value)));
178 }
179 TrieNode::EmptyRoot => {
180 return Err(ProofVerificationError::UnexpectedEmptyRoot)
181 }
182 }
183 };
184 }
185 stack_ptr += 1;
186 }
187 }
188 }
189
190 Ok(None)
191}
192
193#[cfg(test)]
194mod tests {
195 use super::*;
196 use crate::{
197 nodes::{BranchNode, ExtensionNode, LeafNode},
198 proof::{ProofNodes, ProofRetainer},
199 triehash_trie_root, HashBuilder, TrieMask,
200 };
201 use alloy_primitives::hex;
202 use alloy_rlp::{Encodable, EMPTY_STRING_CODE};
203 use core::str::FromStr;
204
205 #[test]
206 fn empty_trie() {
207 let key = Nibbles::unpack(B256::repeat_byte(42));
208 let mut hash_builder = HashBuilder::default().with_proof_retainer(ProofRetainer::default());
209 let root = hash_builder.root();
210 let proof = hash_builder.take_proof_nodes();
211 assert_eq!(
212 proof,
213 ProofNodes::from_iter([(Nibbles::default(), Bytes::from([EMPTY_STRING_CODE]))])
214 );
215 assert_eq!(
216 verify_proof(
217 root,
218 key.clone(),
219 None,
220 proof.into_nodes_sorted().iter().map(|(_, node)| node)
221 ),
222 Ok(())
223 );
224
225 let mut dummy_proof = vec![];
226 BranchNode::default().encode(&mut dummy_proof);
227 assert_eq!(
228 verify_proof(root, key, None, [&Bytes::from(dummy_proof.clone())]),
229 Err(ProofVerificationError::ValueMismatch {
230 path: Nibbles::default(),
231 got: Some(Bytes::from(dummy_proof)),
232 expected: Some(Bytes::from(RlpNode::word_rlp(&EMPTY_ROOT_HASH)[..].to_vec()))
233 })
234 );
235 }
236
237 #[test]
238 fn inlined_trie_leaves() {
239 let root =
244 B256::from_str("8523a13fdb0aa86480a61e34443a951e85e618b5c9b23b9e74cf2754941ce061")
245 .unwrap();
246 let proof = [
247 Bytes::from_str("e48200a7a080389e2b58154f1b8756223ec9ac277b6a166417b4279f016cb86582afb5ae6c").unwrap(),
248 Bytes::from_str("f84080c7833135508234358080808080a0d03438e4f6601da47dab30f52e4325509012ebc1a1c8901fd10d37e05db48bf180808080808080c88339365083312e3180").unwrap(),
249 Bytes::from_str("e08200d3dc808080c4822070318080808080c782207083312e3280808080808080").unwrap()
250 ];
251
252 let first_key = Nibbles::unpack(hex!("a77d3370"));
253 let first_value = vec![0x31];
254 let second_key = Nibbles::unpack(hex!("a77d3970"));
255 let second_value = hex!("0x312e32").to_vec();
256
257 assert_eq!(
258 verify_proof(root, first_key.clone(), Some(first_value.clone()), &proof),
259 Ok(())
260 );
261 assert_eq!(
262 verify_proof(root, first_key.clone(), None, &proof),
263 Err(ProofVerificationError::ValueMismatch {
264 path: first_key,
265 got: Some(first_value.into()),
266 expected: None,
267 })
268 );
269
270 assert_eq!(
271 verify_proof(root, second_key.clone(), Some(second_value.clone()), &proof),
272 Ok(())
273 );
274 assert_eq!(
275 verify_proof(root, second_key.clone(), None, &proof),
276 Err(ProofVerificationError::ValueMismatch {
277 path: second_key,
278 got: Some(second_value.into()),
279 expected: None,
280 })
281 );
282 }
283
284 #[test]
285 fn single_leaf_trie_proof_verification() {
286 let target = Nibbles::unpack(B256::with_last_byte(0x2));
287 let target_value = B256::with_last_byte(0x2);
288 let non_existent_target = Nibbles::unpack(B256::with_last_byte(0x3));
289
290 let retainer = ProofRetainer::from_iter([target.clone(), non_existent_target]);
291 let mut hash_builder = HashBuilder::default().with_proof_retainer(retainer);
292 hash_builder.add_leaf(target.clone(), &target_value[..]);
293 let root = hash_builder.root();
294 assert_eq!(root, triehash_trie_root([(target.pack(), target.pack())]));
295
296 let proof = hash_builder.take_proof_nodes().into_nodes_sorted();
297 assert_eq!(
298 verify_proof(
299 root,
300 target,
301 Some(target_value.to_vec()),
302 proof.iter().map(|(_, node)| node)
303 ),
304 Ok(())
305 );
306 }
307
308 #[test]
309 fn non_existent_proof_verification() {
310 let range = 0..=0xf;
311 let target = Nibbles::unpack(B256::with_last_byte(0xff));
312
313 let retainer = ProofRetainer::from_iter([target.clone()]);
314 let mut hash_builder = HashBuilder::default().with_proof_retainer(retainer);
315 for key in range.clone() {
316 let hash = B256::with_last_byte(key);
317 hash_builder.add_leaf(Nibbles::unpack(hash), &hash[..]);
318 }
319 let root = hash_builder.root();
320 assert_eq!(
321 root,
322 triehash_trie_root(range.map(|b| (B256::with_last_byte(b), B256::with_last_byte(b))))
323 );
324
325 let proof = hash_builder.take_proof_nodes().into_nodes_sorted();
326 assert_eq!(verify_proof(root, target, None, proof.iter().map(|(_, node)| node)), Ok(()));
327 }
328
329 #[test]
330 fn proof_verification_with_divergent_node() {
331 let existing_keys = [
332 hex!("0000000000000000000000000000000000000000000000000000000000000000"),
333 hex!("3a00000000000000000000000000000000000000000000000000000000000000"),
334 hex!("3c15000000000000000000000000000000000000000000000000000000000000"),
335 ];
336 let target = Nibbles::unpack(
337 B256::from_str("0x3c19000000000000000000000000000000000000000000000000000000000000")
338 .unwrap(),
339 );
340 let value = B256::with_last_byte(1);
341
342 let retainer = ProofRetainer::from_iter([target.clone()]);
344 let mut hash_builder = HashBuilder::default().with_proof_retainer(retainer);
345 for key in &existing_keys {
346 hash_builder.add_leaf(Nibbles::unpack(B256::from_slice(key)), &value[..]);
347 }
348 let root = hash_builder.root();
349 assert_eq!(
350 root,
351 triehash_trie_root(existing_keys.map(|key| (B256::from_slice(&key), value)))
352 );
353 let proof = hash_builder.take_proof_nodes();
354 assert_eq!(proof, ProofNodes::from_iter([
355 (Nibbles::default(), Bytes::from_str("f851a0c530c099d779362b6bd0be05039b51ccd0a8ed39e0b2abacab8fe0e3441251878080a07d4ee4f073ae7ce32a6cbcdb015eb73dd2616f33ed2e9fb6ba51c1f9ad5b697b80808080808080808080808080").unwrap()),
356 (Nibbles::from_vec(vec![0x3]), Bytes::from_str("f85180808080808080808080a057fcbd3f97b1093cd39d0f58dafd5058e2d9f79a419e88c2498ff3952cb11a8480a07520d69a83a2bdad373a68b2c9c8c0e1e1c99b6ec80b4b933084da76d644081980808080").unwrap()),
357 (Nibbles::from_vec(vec![0x3, 0xc]), Bytes::from_str("f842a02015000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000001").unwrap())
358 ]));
359 assert_eq!(
360 verify_proof(
361 root,
362 target.clone(),
363 None,
364 proof.into_nodes_sorted().iter().map(|(_, node)| node)
365 ),
366 Ok(())
367 );
368
369 let retainer = ProofRetainer::from_iter([target.clone()]);
370 let mut hash_builder = HashBuilder::default().with_proof_retainer(retainer);
371 for key in &existing_keys {
372 hash_builder.add_leaf(Nibbles::unpack(B256::from_slice(key)), &value[..]);
373 }
374 hash_builder.add_leaf(target.clone(), &value[..]);
375 let root = hash_builder.root();
376 assert_eq!(
377 root,
378 triehash_trie_root(
379 existing_keys
380 .into_iter()
381 .map(|key| (B256::from_slice(&key), value))
382 .chain([(B256::from_slice(&target.pack()), value)])
383 )
384 );
385 let proof = hash_builder.take_proof_nodes();
386 assert_eq!(proof, ProofNodes::from_iter([
387 (Nibbles::default(), Bytes::from_str("f851a0c530c099d779362b6bd0be05039b51ccd0a8ed39e0b2abacab8fe0e3441251878080a0abd80d939392f6d222f8becc15f8c6f0dbbc6833dd7e54bfbbee0c589b7fd40380808080808080808080808080").unwrap()),
388 (Nibbles::from_vec(vec![0x3]), Bytes::from_str("f85180808080808080808080a057fcbd3f97b1093cd39d0f58dafd5058e2d9f79a419e88c2498ff3952cb11a8480a09e7b3788773773f15e26ad07b72a2c25a6374bce256d9aab6cea48fbc77d698180808080").unwrap()),
389 (Nibbles::from_vec(vec![0x3, 0xc]), Bytes::from_str("e211a0338ac0a453edb0e40a23a70aee59e02a6c11597c34d79a5ba94da8eb20dd4d52").unwrap()),
390 (Nibbles::from_vec(vec![0x3, 0xc, 0x1]), Bytes::from_str("f8518080808080a020dc5b33292bfad9013bf123f7faf1efcc5c8e00c894177fc0bfb447daef522f808080a020dc5b33292bfad9013bf123f7faf1efcc5c8e00c894177fc0bfb447daef522f80808080808080").unwrap()),
391 (Nibbles::from_vec(vec![0x3, 0xc, 0x1, 0x9]), Bytes::from_str("f8419f20000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000001").unwrap()),
392 ]));
393 assert_eq!(
394 verify_proof(
395 root,
396 target,
397 Some(value.to_vec()),
398 proof.into_nodes_sorted().iter().map(|(_, node)| node)
399 ),
400 Ok(())
401 );
402 }
403
404 #[test]
405 fn extension_root_trie_proof_verification() {
406 let range = 0..=0xff;
407 let target = Nibbles::unpack(B256::with_last_byte(0x42));
408 let target_value = B256::with_last_byte(0x42);
409
410 let retainer = ProofRetainer::from_iter([target.clone()]);
411 let mut hash_builder = HashBuilder::default().with_proof_retainer(retainer);
412 for key in range.clone() {
413 let hash = B256::with_last_byte(key);
414 hash_builder.add_leaf(Nibbles::unpack(hash), &hash[..]);
415 }
416 let root = hash_builder.root();
417 assert_eq!(
418 root,
419 triehash_trie_root(range.map(|b| (B256::with_last_byte(b), B256::with_last_byte(b))))
420 );
421
422 let proof = hash_builder.take_proof_nodes().into_nodes_sorted();
423 assert_eq!(
424 verify_proof(
425 root,
426 target,
427 Some(target_value.to_vec()),
428 proof.iter().map(|(_, node)| node)
429 ),
430 Ok(())
431 );
432 }
433
434 #[test]
435 fn wide_trie_proof_verification() {
436 let range = 0..=0xff;
437 let target1 = Nibbles::unpack(B256::repeat_byte(0x42));
438 let target1_value = B256::repeat_byte(0x42);
439 let target2 = Nibbles::unpack(B256::repeat_byte(0xff));
440 let target2_value = B256::repeat_byte(0xff);
441
442 let retainer = ProofRetainer::from_iter([target1.clone(), target2.clone()]);
443 let mut hash_builder = HashBuilder::default().with_proof_retainer(retainer);
444 for key in range.clone() {
445 let hash = B256::repeat_byte(key);
446 hash_builder.add_leaf(Nibbles::unpack(hash), &hash[..]);
447 }
448 let root = hash_builder.root();
449 assert_eq!(
450 root,
451 triehash_trie_root(range.map(|b| (B256::repeat_byte(b), B256::repeat_byte(b))))
452 );
453
454 let proof = hash_builder.take_proof_nodes();
455
456 assert_eq!(
457 verify_proof(
458 root,
459 target1.clone(),
460 Some(target1_value.to_vec()),
461 proof.matching_nodes_sorted(&target1).iter().map(|(_, node)| node)
462 ),
463 Ok(())
464 );
465
466 assert_eq!(
467 verify_proof(
468 root,
469 target2.clone(),
470 Some(target2_value.to_vec()),
471 proof.matching_nodes_sorted(&target2).iter().map(|(_, node)| node)
472 ),
473 Ok(())
474 );
475 }
476
477 #[test]
478 fn proof_verification_with_node_encoded_in_place() {
479 let mut buffer = vec![];
561
562 let value = vec![0x64];
563 let child_leaf = TrieNode::Leaf(LeafNode::new(Nibbles::from_nibbles([0xa]), value.clone()));
564
565 let child_branch = TrieNode::Branch(BranchNode::new(
566 vec![
567 {
568 buffer.clear();
569 TrieNode::Leaf(LeafNode::new(Nibbles::from_nibbles([0xa]), value.clone()))
570 .rlp(&mut buffer)
571 },
572 {
573 buffer.clear();
574 TrieNode::Leaf(LeafNode::new(Nibbles::from_nibbles([0xb]), value))
575 .rlp(&mut buffer)
576 },
577 ],
578 TrieMask::new(0b0000000000001100_u16),
579 ));
580
581 let child_extension =
582 TrieNode::Extension(ExtensionNode::new(Nibbles::from_nibbles([0x1]), {
583 buffer.clear();
584 child_branch.rlp(&mut buffer)
585 }));
586
587 let root_branch = TrieNode::Branch(BranchNode::new(
588 vec![
589 {
590 buffer.clear();
591 child_leaf.rlp(&mut buffer)
592 },
593 {
594 buffer.clear();
595 child_branch.rlp(&mut buffer)
596 },
597 {
598 buffer.clear();
599 child_extension.rlp(&mut buffer)
600 },
601 ],
602 TrieMask::new(0b0000000000011100_u16),
603 ));
604
605 let mut root_encoded = vec![];
606 root_branch.encode(&mut root_encoded);
607
608 assert_eq!(
610 root_encoded,
611 hex!(
612 "f83f8080c23a64d58080c23a64c23b6480808080808080808080808080d711d58080c23a64c23b6480808080808080808080808080808080808080808080808080"
613 )
614 );
615
616 let root_hash = B256::from_slice(&hex!(
617 "67dbae3a9cc1f4292b0739fa1bcb7f9e6603a6a138444656ec674e273417c918"
618 ));
619 let root_encoded = Bytes::from(root_encoded);
620 let proof = vec![&root_encoded];
621
622 verify_proof(root_hash, Nibbles::from_nibbles([0x2, 0xa]), Some(vec![0x64]), proof.clone())
624 .unwrap();
625
626 verify_proof(
628 root_hash,
629 Nibbles::from_nibbles([0x3, 0x2, 0xa]),
630 Some(vec![0x64]),
631 proof.clone(),
632 )
633 .unwrap();
634
635 verify_proof(
637 root_hash,
638 Nibbles::from_nibbles([0x3, 0x3, 0xb]),
639 Some(vec![0x64]),
640 proof.clone(),
641 )
642 .unwrap();
643
644 verify_proof(
646 root_hash,
647 Nibbles::from_nibbles([0x4, 0x1, 0x2, 0xa]),
648 Some(vec![0x64]),
649 proof.clone(),
650 )
651 .unwrap();
652
653 verify_proof(
655 root_hash,
656 Nibbles::from_nibbles([0x4, 0x1, 0x3, 0xb]),
657 Some(vec![0x64]),
658 proof.clone(),
659 )
660 .unwrap();
661 }
662
663 #[test]
664 #[cfg(feature = "arbitrary")]
665 #[cfg_attr(miri, ignore = "no proptest")]
666 fn arbitrary_proof_verification() {
667 use proptest::prelude::*;
668
669 proptest!(|(state: std::collections::BTreeMap<B256, alloy_primitives::U256>)| {
670 let hashed = state.into_iter()
671 .map(|(k, v)| (k, alloy_rlp::encode(v).to_vec()))
672 .collect::<std::collections::BTreeMap<_, _>>();
674
675 let retainer = ProofRetainer::from_iter(hashed.clone().into_keys().map(Nibbles::unpack));
676 let mut hash_builder = HashBuilder::default().with_proof_retainer(retainer);
677 for (key, value) in hashed.clone() {
678 hash_builder.add_leaf(Nibbles::unpack(key), &value);
679 }
680
681 let root = hash_builder.root();
682 assert_eq!(root, triehash_trie_root(&hashed));
683
684 let proofs = hash_builder.take_proof_nodes();
685 for (key, value) in hashed {
686 let nibbles = Nibbles::unpack(key);
687 assert_eq!(verify_proof(root, nibbles.clone(), Some(value), proofs.matching_nodes_sorted(&nibbles).iter().map(|(_, node)| node)), Ok(()));
688 }
689 });
690 }
691}