@@ -119,12 +119,129 @@ fn test_bad_range_proof_out_of_order() {
119119}
120120
121121#[ test]
122- // Tests malformed proof scenarios that require full trie reconstruction to detect:
123- // modified keys, modified values, gapped entries, empty keys, and nil values.
124- #[ ignore = "https://github.com/ava-labs/firewood/issues/738" ]
125- fn test_bad_range_proof_malformed ( ) {
126- // TODO: Re-enable once full range proof verification (trie reconstruction
127- // and root hash comparison) is implemented.
122+ // Detects a modified key via trie reconstruction and root hash mismatch.
123+ fn test_bad_range_proof_modified_key ( ) {
124+ let rng = firewood_storage:: SeededRng :: from_env_or_random ( ) ;
125+
126+ let set = fixed_and_pseudorandom_data ( & rng, 4096 ) ;
127+ let mut items = set. iter ( ) . collect :: < Vec < _ > > ( ) ;
128+ items. sort_unstable ( ) ;
129+ let merkle = init_merkle ( items. clone ( ) ) ;
130+ let root_hash = merkle. nodestore ( ) . root_hash ( ) . unwrap ( ) ;
131+
132+ let start = 100 ;
133+ let end = 200 ;
134+
135+ let start_proof = merkle. prove ( items[ start] . 0 ) . unwrap ( ) ;
136+ let end_proof = merkle. prove ( items[ end - 1 ] . 0 ) . unwrap ( ) ;
137+
138+ let mut kvs: KeyValuePairs = items[ start..end]
139+ . iter ( )
140+ . map ( |( k, v) | ( k. to_vec ( ) . into_boxed_slice ( ) , v. to_vec ( ) . into_boxed_slice ( ) ) )
141+ . collect ( ) ;
142+
143+ let mid = kvs. len ( ) / 2 ;
144+ let mut key = kvs[ mid] . 0 . to_vec ( ) ;
145+ key[ 0 ] ^= 0x01 ;
146+ kvs[ mid] . 0 = key. into_boxed_slice ( ) ;
147+ kvs. sort_by ( |( a, _) , ( b, _) | a. cmp ( b) ) ;
148+
149+ let range_proof = RangeProof :: new ( start_proof, end_proof, kvs. into_boxed_slice ( ) ) ;
150+ assert ! (
151+ verify_range_proof(
152+ Some ( items[ start] . 0 ) ,
153+ Some ( items[ end - 1 ] . 0 ) ,
154+ & root_hash,
155+ & range_proof,
156+ )
157+ . is_err( ) ,
158+ "modified key should be detected"
159+ ) ;
160+ }
161+
162+ #[ test]
163+ // Detects a modified value via trie reconstruction and root hash mismatch.
164+ // In ethhash mode, account values at depth 32 have their storageRoot field
165+ // replaced with a computed hash during hashing. A blind XOR on the raw value
166+ // may only affect the storageRoot (or the RLP header that wraps it), making
167+ // the modification invisible to the hash. This test is not meaningful under
168+ // ethhash because the hashing intentionally ignores part of the value.
169+ #[ cfg( not( feature = "ethhash" ) ) ]
170+ fn test_bad_range_proof_modified_value ( ) {
171+ let rng = firewood_storage:: SeededRng :: from_env_or_random ( ) ;
172+
173+ let set = fixed_and_pseudorandom_data ( & rng, 4096 ) ;
174+ let mut items = set. iter ( ) . collect :: < Vec < _ > > ( ) ;
175+ items. sort_unstable ( ) ;
176+ let merkle = init_merkle ( items. clone ( ) ) ;
177+ let root_hash = merkle. nodestore ( ) . root_hash ( ) . unwrap ( ) ;
178+
179+ let start = 100 ;
180+ let end = 200 ;
181+
182+ let start_proof = merkle. prove ( items[ start] . 0 ) . unwrap ( ) ;
183+ let end_proof = merkle. prove ( items[ end - 1 ] . 0 ) . unwrap ( ) ;
184+
185+ let mut kvs: KeyValuePairs = items[ start..end]
186+ . iter ( )
187+ . map ( |( k, v) | ( k. to_vec ( ) . into_boxed_slice ( ) , v. to_vec ( ) . into_boxed_slice ( ) ) )
188+ . collect ( ) ;
189+
190+ let mid = kvs. len ( ) / 2 ;
191+ let mut val = kvs[ mid] . 1 . to_vec ( ) ;
192+ val[ 0 ] ^= 0x01 ;
193+ kvs[ mid] . 1 = val. into_boxed_slice ( ) ;
194+
195+ let range_proof = RangeProof :: new ( start_proof, end_proof, kvs. into_boxed_slice ( ) ) ;
196+ assert ! (
197+ verify_range_proof(
198+ Some ( items[ start] . 0 ) ,
199+ Some ( items[ end - 1 ] . 0 ) ,
200+ & root_hash,
201+ & range_proof,
202+ )
203+ . is_err( ) ,
204+ "modified value should be detected"
205+ ) ;
206+ }
207+
208+ #[ test]
209+ // Detects gapped entries (missing middle element) via trie reconstruction
210+ // and root hash mismatch.
211+ fn test_bad_range_proof_gapped_entries ( ) {
212+ let rng = firewood_storage:: SeededRng :: from_env_or_random ( ) ;
213+
214+ let set = fixed_and_pseudorandom_data ( & rng, 4096 ) ;
215+ let mut items = set. iter ( ) . collect :: < Vec < _ > > ( ) ;
216+ items. sort_unstable ( ) ;
217+ let merkle = init_merkle ( items. clone ( ) ) ;
218+ let root_hash = merkle. nodestore ( ) . root_hash ( ) . unwrap ( ) ;
219+
220+ let start = 100 ;
221+ let end = 200 ;
222+
223+ let start_proof = merkle. prove ( items[ start] . 0 ) . unwrap ( ) ;
224+ let end_proof = merkle. prove ( items[ end - 1 ] . 0 ) . unwrap ( ) ;
225+
226+ let mut kvs: KeyValuePairs = items[ start..end]
227+ . iter ( )
228+ . map ( |( k, v) | ( k. to_vec ( ) . into_boxed_slice ( ) , v. to_vec ( ) . into_boxed_slice ( ) ) )
229+ . collect ( ) ;
230+
231+ let mid = kvs. len ( ) / 2 ;
232+ kvs. remove ( mid) ;
233+
234+ let range_proof = RangeProof :: new ( start_proof, end_proof, kvs. into_boxed_slice ( ) ) ;
235+ assert ! (
236+ verify_range_proof(
237+ Some ( items[ start] . 0 ) ,
238+ Some ( items[ end - 1 ] . 0 ) ,
239+ & root_hash,
240+ & range_proof,
241+ )
242+ . is_err( ) ,
243+ "gapped entries should be detected"
244+ ) ;
128245}
129246
130247#[ test]
@@ -204,7 +321,6 @@ fn test_range_proof_with_non_existent_proof() {
204321// - There exists a gap between the first element and the left edge proof
205322// - There exists a gap between the last element and the right edge proof
206323// Detecting gaps requires full trie reconstruction, not yet implemented.
207- #[ ignore = "https://github.com/ava-labs/firewood/issues/738" ]
208324fn test_range_proof_with_invalid_non_existent_proof ( ) {
209325 let rng = firewood_storage:: SeededRng :: from_env_or_random ( ) ;
210326
@@ -274,7 +390,6 @@ fn test_range_proof_with_invalid_non_existent_proof() {
274390#[ test]
275391// Tests the proof with only one element. The first edge proof can be existent one or
276392// non-existent one.
277- #[ ignore = "https://github.com/ava-labs/firewood/issues/738" ]
278393fn test_one_element_range_proof ( ) {
279394 let rng = firewood_storage:: SeededRng :: from_env_or_random ( ) ;
280395
@@ -391,7 +506,6 @@ fn test_one_element_range_proof() {
391506#[ test]
392507// Tests the range proof with all elements.
393508// The edge proofs can be nil.
394- #[ ignore = "https://github.com/ava-labs/firewood/issues/738" ]
395509fn test_all_elements_proof ( ) {
396510 let rng = firewood_storage:: SeededRng :: from_env_or_random ( ) ;
397511
@@ -444,7 +558,6 @@ fn test_all_elements_proof() {
444558#[ test]
445559// Tests the range proof with "no" element. The first edge proof must
446560// be a non-existent proof.
447- #[ ignore = "https://github.com/ava-labs/firewood/issues/738" ]
448561fn test_empty_range_proof ( ) {
449562 let rng = firewood_storage:: SeededRng :: from_env_or_random ( ) ;
450563
@@ -479,7 +592,6 @@ fn test_empty_range_proof() {
479592#[ test]
480593// Focuses on the small trie with embedded nodes. If the gapped
481594// node is embedded in the trie, it should be detected too.
482- #[ ignore = "https://github.com/ava-labs/firewood/issues/738" ]
483595fn test_gapped_range_proof ( ) {
484596 let mut items = Vec :: new ( ) ;
485597 // Sorted entries
@@ -527,7 +639,6 @@ fn test_gapped_range_proof() {
527639
528640#[ test]
529641// Tests the element is not in the range covered by proofs.
530- #[ ignore = "https://github.com/ava-labs/firewood/issues/738" ]
531642fn test_same_side_proof ( ) {
532643 let rng = firewood_storage:: SeededRng :: from_env_or_random ( ) ;
533644
@@ -575,7 +686,6 @@ fn test_same_side_proof() {
575686
576687#[ test]
577688// Tests the range starts from zero.
578- #[ ignore = "https://github.com/ava-labs/firewood/issues/738" ]
579689fn test_single_side_range_proof ( ) {
580690 let rng = firewood_storage:: SeededRng :: from_env_or_random ( ) ;
581691
@@ -614,7 +724,6 @@ fn test_single_side_range_proof() {
614724
615725#[ test]
616726// Tests the range ends with 0xffff...fff.
617- #[ ignore = "https://github.com/ava-labs/firewood/issues/738" ]
618727fn test_reverse_single_side_range_proof ( ) {
619728 let rng = firewood_storage:: SeededRng :: from_env_or_random ( ) ;
620729
@@ -654,7 +763,6 @@ fn test_reverse_single_side_range_proof() {
654763
655764#[ test]
656765// Tests the range starts with zero and ends with 0xffff...fff.
657- #[ ignore = "https://github.com/ava-labs/firewood/issues/738" ]
658766fn test_both_sides_range_proof ( ) {
659767 let rng = firewood_storage:: SeededRng :: from_env_or_random ( ) ;
660768
@@ -691,7 +799,6 @@ fn test_both_sides_range_proof() {
691799// Tests normal range proof with both edge proofs
692800// as the existent proof, but with an extra empty value included, which is a
693801// noop technically, but practically should be rejected.
694- #[ ignore = "https://github.com/ava-labs/firewood/issues/738" ]
695802fn test_empty_value_range_proof ( ) {
696803 let rng = firewood_storage:: SeededRng :: from_env_or_random ( ) ;
697804
@@ -738,7 +845,6 @@ fn test_empty_value_range_proof() {
738845// Tests the range proof with all elements,
739846// but with an extra empty value included, which is a noop technically, but
740847// practically should be rejected.
741- #[ ignore = "https://github.com/ava-labs/firewood/issues/738" ]
742848fn test_all_elements_empty_value_range_proof ( ) {
743849 let rng = firewood_storage:: SeededRng :: from_env_or_random ( ) ;
744850
@@ -780,7 +886,6 @@ fn test_all_elements_empty_value_range_proof() {
780886}
781887
782888#[ test]
783- #[ ignore = "https://github.com/ava-labs/firewood/issues/738" ]
784889fn test_range_proof_keys_with_shared_prefix ( ) {
785890 let items = vec ! [
786891 (
0 commit comments