1use crate::{
4 EMPTY_ROOT_HASH,
5 nodes::{BranchNode, CHILD_INDEX_RANGE, RlpNode, TrieNode},
6 proof::ProofVerificationError,
7};
8use alloc::vec::Vec;
9use alloy_primitives::{B256, Bytes};
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().is_none_or(|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::new();
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(&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(&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(&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(&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 HashBuilder, TrieMask,
198 nodes::{BranchNode, ExtensionNode, LeafNode},
199 proof::{ProofNodes, ProofRetainer},
200 triehash_trie_root,
201 };
202 use alloy_primitives::hex;
203 use alloy_rlp::{EMPTY_STRING_CODE, Encodable};
204 use core::str::FromStr;
205
206 #[test]
207 fn empty_trie() {
208 let key = Nibbles::unpack(B256::repeat_byte(42));
209 let mut hash_builder =
210 HashBuilder::default().with_proof_retainer(<ProofRetainer>::default());
211 let root = hash_builder.root();
212 let proof = hash_builder.take_proof_nodes();
213 assert_eq!(
214 proof,
215 ProofNodes::from_iter([(Nibbles::default(), Bytes::from([EMPTY_STRING_CODE]))])
216 );
217 assert_eq!(
218 verify_proof(root, key, None, proof.into_nodes_sorted().iter().map(|(_, node)| node)),
219 Ok(())
220 );
221
222 let mut dummy_proof = vec![];
223 BranchNode::default().encode(&mut dummy_proof);
224 assert_eq!(
225 verify_proof(root, key, None, [&Bytes::from(dummy_proof.clone())]),
226 Err(ProofVerificationError::ValueMismatch {
227 path: Nibbles::default(),
228 got: Some(Bytes::from(dummy_proof)),
229 expected: Some(Bytes::from(RlpNode::word_rlp(&EMPTY_ROOT_HASH)[..].to_vec()))
230 })
231 );
232 }
233
234 #[test]
235 fn inlined_trie_leaves() {
236 let root =
241 B256::from_str("8523a13fdb0aa86480a61e34443a951e85e618b5c9b23b9e74cf2754941ce061")
242 .unwrap();
243 let proof = [
244 Bytes::from_str("e48200a7a080389e2b58154f1b8756223ec9ac277b6a166417b4279f016cb86582afb5ae6c").unwrap(),
245 Bytes::from_str("f84080c7833135508234358080808080a0d03438e4f6601da47dab30f52e4325509012ebc1a1c8901fd10d37e05db48bf180808080808080c88339365083312e3180").unwrap(),
246 Bytes::from_str("e08200d3dc808080c4822070318080808080c782207083312e3280808080808080").unwrap()
247 ];
248
249 let first_key = Nibbles::unpack(hex!("a77d3370"));
250 let first_value = vec![0x31];
251 let second_key = Nibbles::unpack(hex!("a77d3970"));
252 let second_value = hex!("0x312e32").to_vec();
253
254 assert_eq!(verify_proof(root, first_key, Some(first_value.clone()), &proof), Ok(()));
255 assert_eq!(
256 verify_proof(root, first_key, None, &proof),
257 Err(ProofVerificationError::ValueMismatch {
258 path: first_key,
259 got: Some(first_value.into()),
260 expected: None,
261 })
262 );
263
264 assert_eq!(verify_proof(root, second_key, Some(second_value.clone()), &proof), Ok(()));
265 assert_eq!(
266 verify_proof(root, second_key, None, &proof),
267 Err(ProofVerificationError::ValueMismatch {
268 path: second_key,
269 got: Some(second_value.into()),
270 expected: None,
271 })
272 );
273 }
274
275 #[test]
276 fn single_leaf_trie_proof_verification() {
277 let target = Nibbles::unpack(B256::with_last_byte(0x2));
278 let target_value = B256::with_last_byte(0x2);
279 let non_existent_target = Nibbles::unpack(B256::with_last_byte(0x3));
280
281 let retainer = ProofRetainer::from_iter([target, non_existent_target]);
282 let mut hash_builder = HashBuilder::default().with_proof_retainer(retainer);
283 hash_builder.add_leaf(target, &target_value[..]);
284 let root = hash_builder.root();
285 assert_eq!(root, triehash_trie_root([(target.pack(), target.pack())]));
286
287 let proof = hash_builder.take_proof_nodes().into_nodes_sorted();
288 assert_eq!(
289 verify_proof(
290 root,
291 target,
292 Some(target_value.to_vec()),
293 proof.iter().map(|(_, node)| node)
294 ),
295 Ok(())
296 );
297 }
298
299 #[test]
300 fn non_existent_proof_verification() {
301 let range = 0..=0xf;
302 let target = Nibbles::unpack(B256::with_last_byte(0xff));
303
304 let retainer = ProofRetainer::from_iter([target]);
305 let mut hash_builder = HashBuilder::default().with_proof_retainer(retainer);
306 for key in range.clone() {
307 let hash = B256::with_last_byte(key);
308 hash_builder.add_leaf(Nibbles::unpack(hash), &hash[..]);
309 }
310 let root = hash_builder.root();
311 assert_eq!(
312 root,
313 triehash_trie_root(range.map(|b| (B256::with_last_byte(b), B256::with_last_byte(b))))
314 );
315
316 let proof = hash_builder.take_proof_nodes().into_nodes_sorted();
317 assert_eq!(verify_proof(root, target, None, proof.iter().map(|(_, node)| node)), Ok(()));
318 }
319
320 #[test]
321 fn proof_verification_with_divergent_node() {
322 let existing_keys = [
323 hex!("0000000000000000000000000000000000000000000000000000000000000000"),
324 hex!("3a00000000000000000000000000000000000000000000000000000000000000"),
325 hex!("3c15000000000000000000000000000000000000000000000000000000000000"),
326 ];
327 let target = Nibbles::unpack(
328 B256::from_str("0x3c19000000000000000000000000000000000000000000000000000000000000")
329 .unwrap()
330 .as_slice(),
331 );
332 let value = B256::with_last_byte(1);
333
334 let retainer = ProofRetainer::from_iter([target]);
336 let mut hash_builder = HashBuilder::default().with_proof_retainer(retainer);
337 for key in &existing_keys {
338 hash_builder.add_leaf(Nibbles::unpack(B256::from_slice(key)), &value[..]);
339 }
340 let root = hash_builder.root();
341 assert_eq!(
342 root,
343 triehash_trie_root(existing_keys.map(|key| (B256::from_slice(&key), value)))
344 );
345 let proof = hash_builder.take_proof_nodes();
346 assert_eq!(proof, ProofNodes::from_iter([
347 (Nibbles::default(), Bytes::from_str("f851a0c530c099d779362b6bd0be05039b51ccd0a8ed39e0b2abacab8fe0e3441251878080a07d4ee4f073ae7ce32a6cbcdb015eb73dd2616f33ed2e9fb6ba51c1f9ad5b697b80808080808080808080808080").unwrap()),
348 (Nibbles::from_iter_unchecked(vec![0x3]), Bytes::from_str("f85180808080808080808080a057fcbd3f97b1093cd39d0f58dafd5058e2d9f79a419e88c2498ff3952cb11a8480a07520d69a83a2bdad373a68b2c9c8c0e1e1c99b6ec80b4b933084da76d644081980808080").unwrap()),
349 (Nibbles::from_iter_unchecked(vec![0x3, 0xc]), Bytes::from_str("f842a02015000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000001").unwrap())
350 ]));
351 assert_eq!(
352 verify_proof(
353 root,
354 target,
355 None,
356 proof.into_nodes_sorted().iter().map(|(_, node)| node)
357 ),
358 Ok(())
359 );
360
361 let retainer = ProofRetainer::from_iter([target]);
362 let mut hash_builder = HashBuilder::default().with_proof_retainer(retainer);
363 for key in &existing_keys {
364 hash_builder.add_leaf(Nibbles::unpack(B256::from_slice(key)), &value[..]);
365 }
366 hash_builder.add_leaf(target, &value[..]);
367 let root = hash_builder.root();
368 assert_eq!(
369 root,
370 triehash_trie_root(
371 existing_keys
372 .into_iter()
373 .map(|key| (B256::from_slice(&key), value))
374 .chain([(B256::from_slice(&target.pack()), value)])
375 )
376 );
377 let proof = hash_builder.take_proof_nodes();
378 assert_eq!(proof, ProofNodes::from_iter([
379 (Nibbles::default(), Bytes::from_str("f851a0c530c099d779362b6bd0be05039b51ccd0a8ed39e0b2abacab8fe0e3441251878080a0abd80d939392f6d222f8becc15f8c6f0dbbc6833dd7e54bfbbee0c589b7fd40380808080808080808080808080").unwrap()),
380 (Nibbles::from_iter_unchecked(vec![0x3]), Bytes::from_str("f85180808080808080808080a057fcbd3f97b1093cd39d0f58dafd5058e2d9f79a419e88c2498ff3952cb11a8480a09e7b3788773773f15e26ad07b72a2c25a6374bce256d9aab6cea48fbc77d698180808080").unwrap()),
381 (Nibbles::from_iter_unchecked(vec![0x3, 0xc]), Bytes::from_str("e211a0338ac0a453edb0e40a23a70aee59e02a6c11597c34d79a5ba94da8eb20dd4d52").unwrap()),
382 (Nibbles::from_iter_unchecked(vec![0x3, 0xc, 0x1]), Bytes::from_str("f8518080808080a020dc5b33292bfad9013bf123f7faf1efcc5c8e00c894177fc0bfb447daef522f808080a020dc5b33292bfad9013bf123f7faf1efcc5c8e00c894177fc0bfb447daef522f80808080808080").unwrap()),
383 (Nibbles::from_iter_unchecked(vec![0x3, 0xc, 0x1, 0x9]), Bytes::from_str("f8419f20000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000001").unwrap()),
384 ]));
385 assert_eq!(
386 verify_proof(
387 root,
388 target,
389 Some(value.to_vec()),
390 proof.into_nodes_sorted().iter().map(|(_, node)| node)
391 ),
392 Ok(())
393 );
394 }
395
396 #[test]
397 fn extension_root_trie_proof_verification() {
398 let range = 0..=0xff;
399 let target = Nibbles::unpack(B256::with_last_byte(0x42));
400 let target_value = B256::with_last_byte(0x42);
401
402 let retainer = ProofRetainer::from_iter([target]);
403 let mut hash_builder = HashBuilder::default().with_proof_retainer(retainer);
404 for key in range.clone() {
405 let hash = B256::with_last_byte(key);
406 hash_builder.add_leaf(Nibbles::unpack(hash), &hash[..]);
407 }
408 let root = hash_builder.root();
409 assert_eq!(
410 root,
411 triehash_trie_root(range.map(|b| (B256::with_last_byte(b), B256::with_last_byte(b))))
412 );
413
414 let proof = hash_builder.take_proof_nodes().into_nodes_sorted();
415 assert_eq!(
416 verify_proof(
417 root,
418 target,
419 Some(target_value.to_vec()),
420 proof.iter().map(|(_, node)| node)
421 ),
422 Ok(())
423 );
424 }
425
426 #[test]
427 fn wide_trie_proof_verification() {
428 let range = 0..=0xff;
429 let target1 = Nibbles::unpack(B256::repeat_byte(0x42));
430 let target1_value = B256::repeat_byte(0x42);
431 let target2 = Nibbles::unpack(B256::repeat_byte(0xff));
432 let target2_value = B256::repeat_byte(0xff);
433
434 let retainer = ProofRetainer::from_iter([target1, target2]);
435 let mut hash_builder = HashBuilder::default().with_proof_retainer(retainer);
436 for key in range.clone() {
437 let hash = B256::repeat_byte(key);
438 hash_builder.add_leaf(Nibbles::unpack(hash), &hash[..]);
439 }
440 let root = hash_builder.root();
441 assert_eq!(
442 root,
443 triehash_trie_root(range.map(|b| (B256::repeat_byte(b), B256::repeat_byte(b))))
444 );
445
446 let proof = hash_builder.take_proof_nodes();
447
448 assert_eq!(
449 verify_proof(
450 root,
451 target1,
452 Some(target1_value.to_vec()),
453 proof.matching_nodes_sorted(&target1).iter().map(|(_, node)| node)
454 ),
455 Ok(())
456 );
457
458 assert_eq!(
459 verify_proof(
460 root,
461 target2,
462 Some(target2_value.to_vec()),
463 proof.matching_nodes_sorted(&target2).iter().map(|(_, node)| node)
464 ),
465 Ok(())
466 );
467 }
468
469 #[test]
470 fn proof_verification_with_node_encoded_in_place() {
471 let mut buffer = vec![];
553
554 let value = vec![0x64];
555 let child_leaf = TrieNode::Leaf(LeafNode::new(Nibbles::from_nibbles([0xa]), value.clone()));
556
557 let child_branch = TrieNode::Branch(BranchNode::new(
558 vec![
559 {
560 buffer.clear();
561 TrieNode::Leaf(LeafNode::new(Nibbles::from_nibbles([0xa]), value.clone()))
562 .rlp(&mut buffer)
563 },
564 {
565 buffer.clear();
566 TrieNode::Leaf(LeafNode::new(Nibbles::from_nibbles([0xb]), value))
567 .rlp(&mut buffer)
568 },
569 ],
570 TrieMask::new(0b0000000000001100_u16),
571 ));
572
573 let child_extension =
574 TrieNode::Extension(ExtensionNode::new(Nibbles::from_nibbles([0x1]), {
575 buffer.clear();
576 child_branch.rlp(&mut buffer)
577 }));
578
579 let root_branch = TrieNode::Branch(BranchNode::new(
580 vec![
581 {
582 buffer.clear();
583 child_leaf.rlp(&mut buffer)
584 },
585 {
586 buffer.clear();
587 child_branch.rlp(&mut buffer)
588 },
589 {
590 buffer.clear();
591 child_extension.rlp(&mut buffer)
592 },
593 ],
594 TrieMask::new(0b0000000000011100_u16),
595 ));
596
597 let mut root_encoded = vec![];
598 root_branch.encode(&mut root_encoded);
599
600 assert_eq!(
602 root_encoded,
603 hex!(
604 "f83f8080c23a64d58080c23a64c23b6480808080808080808080808080d711d58080c23a64c23b6480808080808080808080808080808080808080808080808080"
605 )
606 );
607
608 let root_hash = B256::from_slice(&hex!(
609 "67dbae3a9cc1f4292b0739fa1bcb7f9e6603a6a138444656ec674e273417c918"
610 ));
611 let root_encoded = Bytes::from(root_encoded);
612 let proof = vec![&root_encoded];
613
614 verify_proof(root_hash, Nibbles::from_nibbles([0x2, 0xa]), Some(vec![0x64]), proof.clone())
616 .unwrap();
617
618 verify_proof(
620 root_hash,
621 Nibbles::from_nibbles([0x3, 0x2, 0xa]),
622 Some(vec![0x64]),
623 proof.clone(),
624 )
625 .unwrap();
626
627 verify_proof(
629 root_hash,
630 Nibbles::from_nibbles([0x3, 0x3, 0xb]),
631 Some(vec![0x64]),
632 proof.clone(),
633 )
634 .unwrap();
635
636 verify_proof(
638 root_hash,
639 Nibbles::from_nibbles([0x4, 0x1, 0x2, 0xa]),
640 Some(vec![0x64]),
641 proof.clone(),
642 )
643 .unwrap();
644
645 verify_proof(
647 root_hash,
648 Nibbles::from_nibbles([0x4, 0x1, 0x3, 0xb]),
649 Some(vec![0x64]),
650 proof.clone(),
651 )
652 .unwrap();
653 }
654
655 #[test]
656 #[cfg(feature = "arbitrary")]
657 #[cfg_attr(miri, ignore = "no proptest")]
658 fn arbitrary_proof_verification() {
659 use proptest::prelude::*;
660
661 proptest!(|(state: std::collections::BTreeMap<B256, alloy_primitives::U256>)| {
662 let hashed = state.into_iter()
663 .map(|(k, v)| (k, alloy_rlp::encode(v).to_vec()))
664 .collect::<std::collections::BTreeMap<_, _>>();
666
667 let retainer = ProofRetainer::from_iter(hashed.clone().into_keys().map(Nibbles::unpack));
668 let mut hash_builder = HashBuilder::default().with_proof_retainer(retainer);
669 for (key, value) in hashed.clone() {
670 hash_builder.add_leaf(Nibbles::unpack(key), &value);
671 }
672
673 let root = hash_builder.root();
674 assert_eq!(root, triehash_trie_root(&hashed));
675
676 let proofs = hash_builder.take_proof_nodes();
677 for (key, value) in hashed {
678 let nibbles = Nibbles::unpack(key);
679 assert_eq!(verify_proof(root, nibbles, Some(value), proofs.matching_nodes_sorted(&nibbles).iter().map(|(_, node)| node)), Ok(()));
680 }
681 });
682 }
683}