trie: reject deletions when verifying range proofs
This commit is contained in:
parent
d15e423562
commit
743769f48e
@ -472,12 +472,17 @@ func VerifyRangeProof(rootHash common.Hash, firstKey []byte, lastKey []byte, key
|
|||||||
if len(keys) != len(values) {
|
if len(keys) != len(values) {
|
||||||
return false, fmt.Errorf("inconsistent proof data, keys: %d, values: %d", len(keys), len(values))
|
return false, fmt.Errorf("inconsistent proof data, keys: %d, values: %d", len(keys), len(values))
|
||||||
}
|
}
|
||||||
// Ensure the received batch is monotonic increasing.
|
// Ensure the received batch is monotonic increasing and contains no deletions
|
||||||
for i := 0; i < len(keys)-1; i++ {
|
for i := 0; i < len(keys)-1; i++ {
|
||||||
if bytes.Compare(keys[i], keys[i+1]) >= 0 {
|
if bytes.Compare(keys[i], keys[i+1]) >= 0 {
|
||||||
return false, errors.New("range is not monotonically increasing")
|
return false, errors.New("range is not monotonically increasing")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
for _, value := range values {
|
||||||
|
if len(value) == 0 {
|
||||||
|
return false, errors.New("range contains deletion")
|
||||||
|
}
|
||||||
|
}
|
||||||
// Special case, there is no edge proof at all. The given range is expected
|
// Special case, there is no edge proof at all. The given range is expected
|
||||||
// to be the whole leaf-set in the trie.
|
// to be the whole leaf-set in the trie.
|
||||||
if proof == nil {
|
if proof == nil {
|
||||||
|
@ -813,6 +813,85 @@ func TestBloatedProof(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TestEmptyValueRangeProof tests normal range proof with both edge proofs
|
||||||
|
// as the existent proof, but with an extra empty value included, which is a
|
||||||
|
// noop technically, but practically should be rejected.
|
||||||
|
func TestEmptyValueRangeProof(t *testing.T) {
|
||||||
|
trie, values := randomTrie(512)
|
||||||
|
var entries entrySlice
|
||||||
|
for _, kv := range values {
|
||||||
|
entries = append(entries, kv)
|
||||||
|
}
|
||||||
|
sort.Sort(entries)
|
||||||
|
|
||||||
|
// Create a new entry with a slightly modified key
|
||||||
|
mid := len(entries) / 2
|
||||||
|
key := common.CopyBytes(entries[mid-1].k)
|
||||||
|
for n := len(key) - 1; n >= 0; n-- {
|
||||||
|
if key[n] < 0xff {
|
||||||
|
key[n]++
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
noop := &kv{key, []byte{}, false}
|
||||||
|
entries = append(append(append([]*kv{}, entries[:mid]...), noop), entries[mid:]...)
|
||||||
|
|
||||||
|
start, end := 1, len(entries)-1
|
||||||
|
|
||||||
|
proof := memorydb.New()
|
||||||
|
if err := trie.Prove(entries[start].k, 0, proof); err != nil {
|
||||||
|
t.Fatalf("Failed to prove the first node %v", err)
|
||||||
|
}
|
||||||
|
if err := trie.Prove(entries[end-1].k, 0, proof); err != nil {
|
||||||
|
t.Fatalf("Failed to prove the last node %v", err)
|
||||||
|
}
|
||||||
|
var keys [][]byte
|
||||||
|
var vals [][]byte
|
||||||
|
for i := start; i < end; i++ {
|
||||||
|
keys = append(keys, entries[i].k)
|
||||||
|
vals = append(vals, entries[i].v)
|
||||||
|
}
|
||||||
|
_, err := VerifyRangeProof(trie.Hash(), keys[0], keys[len(keys)-1], keys, vals, proof)
|
||||||
|
if err == nil {
|
||||||
|
t.Fatalf("Expected failure on noop entry")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestAllElementsEmptyValueRangeProof tests the range proof with all elements,
|
||||||
|
// but with an extra empty value included, which is a noop technically, but
|
||||||
|
// practically should be rejected.
|
||||||
|
func TestAllElementsEmptyValueRangeProof(t *testing.T) {
|
||||||
|
trie, values := randomTrie(512)
|
||||||
|
var entries entrySlice
|
||||||
|
for _, kv := range values {
|
||||||
|
entries = append(entries, kv)
|
||||||
|
}
|
||||||
|
sort.Sort(entries)
|
||||||
|
|
||||||
|
// Create a new entry with a slightly modified key
|
||||||
|
mid := len(entries) / 2
|
||||||
|
key := common.CopyBytes(entries[mid-1].k)
|
||||||
|
for n := len(key) - 1; n >= 0; n-- {
|
||||||
|
if key[n] < 0xff {
|
||||||
|
key[n]++
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
noop := &kv{key, []byte{}, false}
|
||||||
|
entries = append(append(append([]*kv{}, entries[:mid]...), noop), entries[mid:]...)
|
||||||
|
|
||||||
|
var keys [][]byte
|
||||||
|
var vals [][]byte
|
||||||
|
for i := 0; i < len(entries); i++ {
|
||||||
|
keys = append(keys, entries[i].k)
|
||||||
|
vals = append(vals, entries[i].v)
|
||||||
|
}
|
||||||
|
_, err := VerifyRangeProof(trie.Hash(), nil, nil, keys, vals, nil)
|
||||||
|
if err == nil {
|
||||||
|
t.Fatalf("Expected failure on noop entry")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// mutateByte changes one byte in b.
|
// mutateByte changes one byte in b.
|
||||||
func mutateByte(b []byte) {
|
func mutateByte(b []byte) {
|
||||||
for r := mrand.Intn(len(b)); ; {
|
for r := mrand.Intn(len(b)); ; {
|
||||||
|
Loading…
Reference in New Issue
Block a user