Skip to content

Commit c8c8d6c

Browse files
authored
trie: add edgecase for rangeproof correctness (ethereum#31667)
This PR adds checking for an edgecase which theoretically can happen in the range-prover. Right now, we check that a key does not overwrite a previous one by checking that the key is increasing. However, if keys are of different lengths, it is possible to create a key which is increasing _and_ overwrites the previous key. Example: `0xaabbcc` followed by `0xaabbccdd`. This can not happen in go-ethereum, which always uses fixed-size paths for accounts and storage slot paths in the trie, but it might happen if the range prover is used without guaranteed fixed-size keys. This PR also adds some testcases for the errors that are expected.
1 parent 0045267 commit c8c8d6c

File tree

2 files changed

+43
-3
lines changed

2 files changed

+43
-3
lines changed

trie/proof.go

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -485,10 +485,18 @@ func VerifyRangeProof(rootHash common.Hash, firstKey []byte, keys [][]byte, valu
485485
if len(keys) != len(values) {
486486
return false, fmt.Errorf("inconsistent proof data, keys: %d, values: %d", len(keys), len(values))
487487
}
488-
// Ensure the received batch is monotonic increasing and contains no deletions
488+
// Ensure the received batch is
489+
// - monotonically increasing,
490+
// - not expanding down prefix-paths
491+
// - and contains no deletions
489492
for i := 0; i < len(keys); i++ {
490-
if i < len(keys)-1 && bytes.Compare(keys[i], keys[i+1]) >= 0 {
491-
return false, errors.New("range is not monotonically increasing")
493+
if i < len(keys)-1 {
494+
if bytes.Compare(keys[i], keys[i+1]) >= 0 {
495+
return false, errors.New("range is not monotonically increasing")
496+
}
497+
if bytes.HasPrefix(keys[i+1], keys[i]) {
498+
return false, errors.New("range contains path prefixes")
499+
}
492500
}
493501
if len(values[i]) == 0 {
494502
return false, errors.New("range contains deletion")

trie/proof_test.go

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1000,3 +1000,35 @@ func TestRangeProofKeysWithSharedPrefix(t *testing.T) {
10001000
t.Error("expected more to be false")
10011001
}
10021002
}
1003+
1004+
// TestRangeProofErrors tests a few cases where the prover is supposed
1005+
// to exit with errors
1006+
func TestRangeProofErrors(t *testing.T) {
1007+
// Different number of keys to values
1008+
_, err := VerifyRangeProof((common.Hash{}), []byte{}, make([][]byte, 5), make([][]byte, 4), nil)
1009+
if have, want := err.Error(), "inconsistent proof data, keys: 5, values: 4"; have != want {
1010+
t.Fatalf("wrong error, have %q, want %q", err.Error(), want)
1011+
}
1012+
// Non-increasing paths
1013+
_, err = VerifyRangeProof((common.Hash{}), []byte{},
1014+
[][]byte{[]byte{2, 1}, []byte{2, 1}}, make([][]byte, 2), nil)
1015+
if have, want := err.Error(), "range is not monotonically increasing"; have != want {
1016+
t.Fatalf("wrong error, have %q, want %q", err.Error(), want)
1017+
}
1018+
// A prefixed path is never motivated. Inserting the second element will
1019+
// require rewriting/overwriting the previous value-node, thus can only
1020+
// happen if the data is corrupt.
1021+
_, err = VerifyRangeProof((common.Hash{}), []byte{},
1022+
[][]byte{[]byte{2, 1}, []byte{2, 1, 2}},
1023+
[][]byte{[]byte{1}, []byte{1}}, nil)
1024+
if have, want := err.Error(), "range contains path prefixes"; have != want {
1025+
t.Fatalf("wrong error, have %q, want %q", err.Error(), want)
1026+
}
1027+
// Empty values (deletions)
1028+
_, err = VerifyRangeProof((common.Hash{}), []byte{},
1029+
[][]byte{[]byte{2, 1}, []byte{2, 2}},
1030+
[][]byte{[]byte{1}, []byte{}}, nil)
1031+
if have, want := err.Error(), "range contains deletion"; have != want {
1032+
t.Fatalf("wrong error, have %q, want %q", err.Error(), want)
1033+
}
1034+
}

0 commit comments

Comments
 (0)