@@ -11,6 +11,18 @@ use alloy_rlp::{Decodable, EMPTY_STRING_CODE};
1111use core:: ops:: Deref ;
1212use nybbles:: Nibbles ;
1313
14+ /// Maximum allowed size (in bytes) of a single proof node.
15+ ///
16+ /// A fully populated branch node has 17 children x 33 bytes each, approximately 561 bytes of
17+ /// payload, plus RLP overhead. We use 1024 bytes as a safe upper bound.
18+ pub const MAX_PROOF_NODE_SIZE : usize = 1024 ;
19+
20+ /// Maximum allowed number of proof nodes.
21+ ///
22+ /// A valid Merkle-Patricia proof path is bounded by the key length. For Keccak256 keys,
23+ /// that is 64 nibbles, so at most 65 nodes (including the root).
24+ pub const MAX_PROOF_NODES : usize = 65 ;
25+
1426/// Verify the proof for given key value pair against the provided state root.
1527///
1628/// The expected node value can be either [Some] if it's expected to be present
@@ -25,6 +37,16 @@ pub fn verify_proof<'a, I>(
2537where
2638 I : IntoIterator < Item = & ' a Bytes > ,
2739{
40+ let proof: Vec < & ' a Bytes > = proof. into_iter ( ) . collect ( ) ;
41+
42+ // Enforce maximum proof node count.
43+ if proof. len ( ) > MAX_PROOF_NODES {
44+ return Err ( ProofVerificationError :: TooManyProofNodes {
45+ got : proof. len ( ) ,
46+ max : MAX_PROOF_NODES ,
47+ } ) ;
48+ }
49+
2850 let mut proof = proof. into_iter ( ) . peekable ( ) ;
2951
3052 // If the proof is empty or contains only an empty node, the expected value must be None.
5577 let mut last_decoded_node = Some ( NodeDecodingResult :: Node ( RlpNode :: word_rlp ( & root) ) ) ;
5678 let mut last_decoded_node_is_private = false ;
5779 for node in proof {
80+ // Enforce maximum proof node size.
81+ if node. len ( ) > MAX_PROOF_NODE_SIZE {
82+ return Err ( ProofVerificationError :: ProofNodeTooLarge {
83+ got : node. len ( ) ,
84+ max : MAX_PROOF_NODE_SIZE ,
85+ } ) ;
86+ }
5887 // Check if the node that we just decoded (or root node, if we just started) matches
5988 // the expected node from the proof.
6089 if Some ( RlpNode :: from_rlp ( node) . as_slice ( ) ) != last_decoded_node. as_deref ( ) {
@@ -910,4 +939,65 @@ mod tests {
910939 }
911940 } ) ;
912941 }
942+
943+ #[ test]
944+ fn reject_oversized_proof_node ( ) {
945+ let key = Nibbles :: unpack ( B256 :: repeat_byte ( 0x42 ) ) ;
946+ let root = B256 :: repeat_byte ( 0x01 ) ;
947+
948+ // Create a proof node that exceeds MAX_PROOF_NODE_SIZE.
949+ let oversized_node = Bytes :: from ( vec ! [ 0xaa ; MAX_PROOF_NODE_SIZE + 1 ] ) ;
950+ let proof = vec ! [ oversized_node] ;
951+
952+ let result = verify_proof ( root, key, Some ( vec ! [ 0x42 ] ) , false , proof. iter ( ) ) ;
953+ assert_eq ! (
954+ result,
955+ Err ( ProofVerificationError :: ProofNodeTooLarge {
956+ got: MAX_PROOF_NODE_SIZE + 1 ,
957+ max: MAX_PROOF_NODE_SIZE ,
958+ } )
959+ ) ;
960+ }
961+
962+ #[ test]
963+ fn reject_too_many_proof_nodes ( ) {
964+ let key = Nibbles :: unpack ( B256 :: repeat_byte ( 0x42 ) ) ;
965+ let root = B256 :: repeat_byte ( 0x01 ) ;
966+
967+ // Create a proof with more nodes than MAX_PROOF_NODES.
968+ // The nodes don't need to be valid RLP because the count check
969+ // happens before decoding.
970+ let dummy_node = Bytes :: from ( vec ! [ 0xc0 ] ) ; // minimal RLP empty list
971+ let proof: Vec < Bytes > = ( 0 ..MAX_PROOF_NODES + 1 ) . map ( |_| dummy_node. clone ( ) ) . collect ( ) ;
972+
973+ let result = verify_proof ( root, key, Some ( vec ! [ 0x42 ] ) , false , proof. iter ( ) ) ;
974+ assert_eq ! (
975+ result,
976+ Err ( ProofVerificationError :: TooManyProofNodes {
977+ got: MAX_PROOF_NODES + 1 ,
978+ max: MAX_PROOF_NODES ,
979+ } )
980+ ) ;
981+ }
982+
983+ #[ test]
984+ fn accept_proof_at_max_node_size ( ) {
985+ // A node exactly at MAX_PROOF_NODE_SIZE should NOT be rejected by the size check.
986+ // It will fail for other reasons (invalid RLP, root mismatch, etc.) but not size.
987+ let key = Nibbles :: unpack ( B256 :: repeat_byte ( 0x42 ) ) ;
988+ let root = B256 :: repeat_byte ( 0x01 ) ;
989+
990+ let node = Bytes :: from ( vec ! [ 0xaa ; MAX_PROOF_NODE_SIZE ] ) ;
991+ let proof = vec ! [ node] ;
992+
993+ let result = verify_proof ( root, key, Some ( vec ! [ 0x42 ] ) , false , proof. iter ( ) ) ;
994+ // Should not be ProofNodeTooLarge - it may fail for other reasons
995+ assert_ne ! (
996+ result,
997+ Err ( ProofVerificationError :: ProofNodeTooLarge {
998+ got: MAX_PROOF_NODE_SIZE ,
999+ max: MAX_PROOF_NODE_SIZE ,
1000+ } )
1001+ ) ;
1002+ }
9131003}
0 commit comments