Skip to content

Commit d8715fb

Browse files
authored
Merge pull request ethereum#3062 from fjl/trie-delete-bug
trie: fix delete bug for values contained in fullNode
2 parents b4cc8cb + c3a77d6 commit d8715fb

File tree

2 files changed

+133
-38
lines changed

2 files changed

+133
-38
lines changed

trie/trie.go

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,19 +22,22 @@ import (
2222
"fmt"
2323

2424
"github.com/ethereum/go-ethereum/common"
25-
"github.com/ethereum/go-ethereum/crypto"
25+
"github.com/ethereum/go-ethereum/crypto/sha3"
2626
"github.com/ethereum/go-ethereum/logger"
2727
"github.com/ethereum/go-ethereum/logger/glog"
2828
)
2929

3030
var (
3131
// This is the known root hash of an empty trie.
3232
emptyRoot = common.HexToHash("56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421")
33-
3433
// This is the known hash of an empty state trie entry.
35-
emptyState = crypto.Keccak256Hash(nil)
34+
emptyState common.Hash
3635
)
3736

37+
func init() {
38+
sha3.NewKeccak256().Sum(emptyState[:0])
39+
}
40+
3841
// Database must be implemented by backing stores for the trie.
3942
type Database interface {
4043
DatabaseWriter
@@ -374,6 +377,9 @@ func (t *Trie) delete(n node, prefix, key []byte) (bool, node, error) {
374377
// n still contains at least two values and cannot be reduced.
375378
return true, n, nil
376379

380+
case valueNode:
381+
return true, nil, nil
382+
377383
case nil:
378384
return false, nil, nil
379385

trie/trie_test.go

Lines changed: 124 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,11 @@ import (
2121
"encoding/binary"
2222
"fmt"
2323
"io/ioutil"
24+
"math/rand"
2425
"os"
26+
"reflect"
2527
"testing"
28+
"testing/quick"
2629

2730
"github.com/davecgh/go-spew/spew"
2831
"github.com/ethereum/go-ethereum/common"
@@ -297,41 +300,6 @@ func TestReplication(t *testing.T) {
297300
}
298301
}
299302

300-
func paranoiaCheck(t1 *Trie) (bool, *Trie) {
301-
t2 := new(Trie)
302-
it := NewIterator(t1)
303-
for it.Next() {
304-
t2.Update(it.Key, it.Value)
305-
}
306-
return t2.Hash() == t1.Hash(), t2
307-
}
308-
309-
func TestParanoia(t *testing.T) {
310-
t.Skip()
311-
trie := newEmpty()
312-
313-
vals := []struct{ k, v string }{
314-
{"do", "verb"},
315-
{"ether", "wookiedoo"},
316-
{"horse", "stallion"},
317-
{"shaman", "horse"},
318-
{"doge", "coin"},
319-
{"ether", ""},
320-
{"dog", "puppy"},
321-
{"shaman", ""},
322-
{"somethingveryoddindeedthis is", "myothernodedata"},
323-
}
324-
for _, val := range vals {
325-
updateString(trie, val.k, val.v)
326-
}
327-
trie.Commit()
328-
329-
ok, t2 := paranoiaCheck(trie)
330-
if !ok {
331-
t.Errorf("trie paranoia check failed %x %x", trie.Hash(), t2.Hash())
332-
}
333-
}
334-
335303
// Not an actual test
336304
func TestOutput(t *testing.T) {
337305
t.Skip()
@@ -356,7 +324,128 @@ func TestLargeValue(t *testing.T) {
356324
trie.Update([]byte("key1"), []byte{99, 99, 99, 99})
357325
trie.Update([]byte("key2"), bytes.Repeat([]byte{1}, 32))
358326
trie.Hash()
327+
}
328+
329+
type randTestStep struct {
330+
op int
331+
key []byte // for opUpdate, opDelete, opGet
332+
value []byte // for opUpdate
333+
}
334+
335+
type randTest []randTestStep
336+
337+
const (
338+
opUpdate = iota
339+
opDelete
340+
opGet
341+
opCommit
342+
opHash
343+
opReset
344+
opItercheckhash
345+
opMax // boundary value, not an actual op
346+
)
347+
348+
func (randTest) Generate(r *rand.Rand, size int) reflect.Value {
349+
var allKeys [][]byte
350+
genKey := func() []byte {
351+
if len(allKeys) < 2 || r.Intn(100) < 10 {
352+
// new key
353+
key := make([]byte, r.Intn(50))
354+
randRead(r, key)
355+
allKeys = append(allKeys, key)
356+
return key
357+
}
358+
// use existing key
359+
return allKeys[r.Intn(len(allKeys))]
360+
}
361+
362+
var steps randTest
363+
for i := 0; i < size; i++ {
364+
step := randTestStep{op: r.Intn(opMax)}
365+
switch step.op {
366+
case opUpdate:
367+
step.key = genKey()
368+
step.value = make([]byte, 8)
369+
binary.BigEndian.PutUint64(step.value, uint64(i))
370+
case opGet, opDelete:
371+
step.key = genKey()
372+
}
373+
steps = append(steps, step)
374+
}
375+
return reflect.ValueOf(steps)
376+
}
377+
378+
// rand.Rand provides a Read method in Go 1.7 and later, but
379+
// we can't use it yet.
380+
func randRead(r *rand.Rand, b []byte) {
381+
pos := 0
382+
val := 0
383+
for n := 0; n < len(b); n++ {
384+
if pos == 0 {
385+
val = r.Int()
386+
pos = 7
387+
}
388+
b[n] = byte(val)
389+
val >>= 8
390+
pos--
391+
}
392+
}
359393

394+
func runRandTest(rt randTest) bool {
395+
db, _ := ethdb.NewMemDatabase()
396+
tr, _ := New(common.Hash{}, db)
397+
values := make(map[string]string) // tracks content of the trie
398+
399+
for _, step := range rt {
400+
switch step.op {
401+
case opUpdate:
402+
tr.Update(step.key, step.value)
403+
values[string(step.key)] = string(step.value)
404+
case opDelete:
405+
tr.Delete(step.key)
406+
delete(values, string(step.key))
407+
case opGet:
408+
v := tr.Get(step.key)
409+
want := values[string(step.key)]
410+
if string(v) != want {
411+
fmt.Printf("mismatch for key 0x%x, got 0x%x want 0x%x", step.key, v, want)
412+
return false
413+
}
414+
case opCommit:
415+
if _, err := tr.Commit(); err != nil {
416+
panic(err)
417+
}
418+
case opHash:
419+
tr.Hash()
420+
case opReset:
421+
hash, err := tr.Commit()
422+
if err != nil {
423+
panic(err)
424+
}
425+
newtr, err := New(hash, db)
426+
if err != nil {
427+
panic(err)
428+
}
429+
tr = newtr
430+
case opItercheckhash:
431+
checktr, _ := New(common.Hash{}, nil)
432+
it := tr.Iterator()
433+
for it.Next() {
434+
checktr.Update(it.Key, it.Value)
435+
}
436+
if tr.Hash() != checktr.Hash() {
437+
fmt.Println("hashes not equal")
438+
return false
439+
}
440+
}
441+
}
442+
return true
443+
}
444+
445+
func TestRandom(t *testing.T) {
446+
if err := quick.Check(runRandTest, nil); err != nil {
447+
t.Fatal(err)
448+
}
360449
}
361450

362451
func BenchmarkGet(b *testing.B) { benchGet(b, false) }

0 commit comments

Comments
 (0)