Skip to content

Commit 9e1bd0f

Browse files
authored
trie: fix range prover (ethereum#22210)
Fixes a special case when the trie only has a single trie node and the range proof only contains a single element.
1 parent 231040c commit 9e1bd0f

File tree

2 files changed

+48
-15
lines changed

2 files changed

+48
-15
lines changed

trie/proof.go

Lines changed: 29 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -216,7 +216,7 @@ func proofToPath(rootHash common.Hash, root node, key []byte, proofDb ethdb.KeyV
216216
//
217217
// Note we have the assumption here the given boundary keys are different
218218
// and right is larger than left.
219-
func unsetInternal(n node, left []byte, right []byte) error {
219+
func unsetInternal(n node, left []byte, right []byte) (bool, error) {
220220
left, right = keybytesToHex(left), keybytesToHex(right)
221221

222222
// Step down to the fork point. There are two scenarios can happen:
@@ -278,45 +278,55 @@ findFork:
278278
// - left proof points to the shortnode, but right proof is greater
279279
// - right proof points to the shortnode, but left proof is less
280280
if shortForkLeft == -1 && shortForkRight == -1 {
281-
return errors.New("empty range")
281+
return false, errors.New("empty range")
282282
}
283283
if shortForkLeft == 1 && shortForkRight == 1 {
284-
return errors.New("empty range")
284+
return false, errors.New("empty range")
285285
}
286286
if shortForkLeft != 0 && shortForkRight != 0 {
287+
// The fork point is root node, unset the entire trie
288+
if parent == nil {
289+
return true, nil
290+
}
287291
parent.(*fullNode).Children[left[pos-1]] = nil
288-
return nil
292+
return false, nil
289293
}
290294
// Only one proof points to non-existent key.
291295
if shortForkRight != 0 {
292-
// Unset left proof's path
293296
if _, ok := rn.Val.(valueNode); ok {
297+
// The fork point is root node, unset the entire trie
298+
if parent == nil {
299+
return true, nil
300+
}
294301
parent.(*fullNode).Children[left[pos-1]] = nil
295-
return nil
302+
return false, nil
296303
}
297-
return unset(rn, rn.Val, left[pos:], len(rn.Key), false)
304+
return false, unset(rn, rn.Val, left[pos:], len(rn.Key), false)
298305
}
299306
if shortForkLeft != 0 {
300-
// Unset right proof's path.
301307
if _, ok := rn.Val.(valueNode); ok {
308+
// The fork point is root node, unset the entire trie
309+
if parent == nil {
310+
return true, nil
311+
}
302312
parent.(*fullNode).Children[right[pos-1]] = nil
303-
return nil
313+
return false, nil
304314
}
305-
return unset(rn, rn.Val, right[pos:], len(rn.Key), true)
315+
return false, unset(rn, rn.Val, right[pos:], len(rn.Key), true)
306316
}
307-
return nil
317+
return false, nil
308318
case *fullNode:
309319
// unset all internal nodes in the forkpoint
310320
for i := left[pos] + 1; i < right[pos]; i++ {
311321
rn.Children[i] = nil
312322
}
313323
if err := unset(rn, rn.Children[left[pos]], left[pos:], 1, false); err != nil {
314-
return err
324+
return false, err
315325
}
316326
if err := unset(rn, rn.Children[right[pos]], right[pos:], 1, true); err != nil {
317-
return err
327+
return false, err
318328
}
319-
return nil
329+
return false, nil
320330
default:
321331
panic(fmt.Sprintf("%T: invalid node: %v", n, n))
322332
}
@@ -560,7 +570,8 @@ func VerifyRangeProof(rootHash common.Hash, firstKey []byte, lastKey []byte, key
560570
}
561571
// Remove all internal references. All the removed parts should
562572
// be re-filled(or re-constructed) by the given leaves range.
563-
if err := unsetInternal(root, firstKey, lastKey); err != nil {
573+
empty, err := unsetInternal(root, firstKey, lastKey)
574+
if err != nil {
564575
return nil, nil, nil, false, err
565576
}
566577
// Rebuild the trie with the leaf stream, the shape of trie
@@ -570,6 +581,9 @@ func VerifyRangeProof(rootHash common.Hash, firstKey []byte, lastKey []byte, key
570581
triedb = NewDatabase(diskdb)
571582
)
572583
tr := &Trie{root: root, db: triedb}
584+
if empty {
585+
tr.root = nil
586+
}
573587
for index, key := range keys {
574588
tr.TryUpdate(key, values[index])
575589
}

trie/proof_test.go

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -384,6 +384,25 @@ func TestOneElementRangeProof(t *testing.T) {
384384
if err != nil {
385385
t.Fatalf("Expected no error, got %v", err)
386386
}
387+
388+
// Test the mini trie with only a single element.
389+
tinyTrie := new(Trie)
390+
entry := &kv{randBytes(32), randBytes(20), false}
391+
tinyTrie.Update(entry.k, entry.v)
392+
393+
first = common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000000").Bytes()
394+
last = entry.k
395+
proof = memorydb.New()
396+
if err := tinyTrie.Prove(first, 0, proof); err != nil {
397+
t.Fatalf("Failed to prove the first node %v", err)
398+
}
399+
if err := tinyTrie.Prove(last, 0, proof); err != nil {
400+
t.Fatalf("Failed to prove the last node %v", err)
401+
}
402+
_, _, _, _, err = VerifyRangeProof(tinyTrie.Hash(), first, last, [][]byte{entry.k}, [][]byte{entry.v}, proof)
403+
if err != nil {
404+
t.Fatalf("Expected no error, got %v", err)
405+
}
387406
}
388407

389408
// TestAllElementsProof tests the range proof with all elements.

0 commit comments

Comments
 (0)