package trie import ( "bytes" "math/big" "testing" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/ethdb/memorydb" ) func TestSizeBug(t *testing.T) { st := NewStackTrie(nil) nt, _ := New(common.Hash{}, NewDatabase(memorydb.New())) leaf := common.FromHex("290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e563") value := common.FromHex("94cf40d0d2b44f2b66e07cace1372ca42b73cf21a3") nt.TryUpdate(leaf, value) st.TryUpdate(leaf, value) if nt.Hash() != st.Hash() { t.Fatalf("error %x != %x", st.Hash(), nt.Hash()) } } func TestEmptyBug(t *testing.T) { st := NewStackTrie(nil) nt, _ := New(common.Hash{}, NewDatabase(memorydb.New())) //leaf := common.FromHex("290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e563") //value := common.FromHex("94cf40d0d2b44f2b66e07cace1372ca42b73cf21a3") kvs := []struct { K string V string }{ {K: "405787fa12a823e0f2b7631cc41b3ba8828b3321ca811111fa75cd3aa3bb5ace", V: "9496f4ec2bf9dab484cac6be589e8417d84781be08"}, {K: "40edb63a35fcf86c08022722aa3287cdd36440d671b4918131b2514795fefa9c", V: "01"}, {K: "b10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf6", V: "947a30f7736e48d6599356464ba4c150d8da0302ff"}, {K: "c2575a0e9e593c00f959f8c92f12db2869c3395a3b0502d05e2516446f71f85b", V: "02"}, } for _, kv := range kvs { nt.TryUpdate(common.FromHex(kv.K), common.FromHex(kv.V)) st.TryUpdate(common.FromHex(kv.K), common.FromHex(kv.V)) } if nt.Hash() != st.Hash() { t.Fatalf("error %x != %x", st.Hash(), nt.Hash()) } } func TestValLength56(t *testing.T) { st := NewStackTrie(nil) nt, _ := New(common.Hash{}, NewDatabase(memorydb.New())) //leaf := common.FromHex("290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e563") //value := common.FromHex("94cf40d0d2b44f2b66e07cace1372ca42b73cf21a3") kvs := []struct { K string V string }{ {K: "405787fa12a823e0f2b7631cc41b3ba8828b3321ca811111fa75cd3aa3bb5ace", V: "1111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111"}, } for _, kv := range kvs { nt.TryUpdate(common.FromHex(kv.K), common.FromHex(kv.V)) st.TryUpdate(common.FromHex(kv.K), common.FromHex(kv.V)) } if nt.Hash() != st.Hash() { t.Fatalf("error %x != %x", st.Hash(), nt.Hash()) } } // TestUpdateSmallNodes tests a case where the leaves are small (both key and value), // which causes a lot of node-within-node. This case was found via fuzzing. func TestUpdateSmallNodes(t *testing.T) { st := NewStackTrie(nil) nt, _ := New(common.Hash{}, NewDatabase(memorydb.New())) kvs := []struct { K string V string }{ {"63303030", "3041"}, // stacktrie.Update {"65", "3000"}, // stacktrie.Update } for _, kv := range kvs { nt.TryUpdate(common.FromHex(kv.K), common.FromHex(kv.V)) st.TryUpdate(common.FromHex(kv.K), common.FromHex(kv.V)) } if nt.Hash() != st.Hash() { t.Fatalf("error %x != %x", st.Hash(), nt.Hash()) } } // TestUpdateVariableKeys contains a case which stacktrie fails: when keys of different // sizes are used, and the second one has the same prefix as the first, then the // stacktrie fails, since it's unable to 'expand' on an already added leaf. // For all practical purposes, this is fine, since keys are fixed-size length // in account and storage tries. // // The test is marked as 'skipped', and exists just to have the behaviour documented. // This case was found via fuzzing. func TestUpdateVariableKeys(t *testing.T) { t.SkipNow() st := NewStackTrie(nil) nt, _ := New(common.Hash{}, NewDatabase(memorydb.New())) kvs := []struct { K string V string }{ {"0x33303534636532393561313031676174", "303030"}, {"0x3330353463653239356131303167617430", "313131"}, } for _, kv := range kvs { nt.TryUpdate(common.FromHex(kv.K), common.FromHex(kv.V)) st.TryUpdate(common.FromHex(kv.K), common.FromHex(kv.V)) } if nt.Hash() != st.Hash() { t.Fatalf("error %x != %x", st.Hash(), nt.Hash()) } } // TestStacktrieNotModifyValues checks that inserting blobs of data into the // stacktrie does not mutate the blobs func TestStacktrieNotModifyValues(t *testing.T) { st := NewStackTrie(nil) { // Test a very small trie // Give it the value as a slice with large backing alloc, // so if the stacktrie tries to append, it won't have to realloc value := make([]byte, 1, 100) value[0] = 0x2 want := common.CopyBytes(value) st.TryUpdate([]byte{0x01}, value) st.Hash() if have := value; !bytes.Equal(have, want) { t.Fatalf("tiny trie: have %#x want %#x", have, want) } st = NewStackTrie(nil) } // Test with a larger trie keyB := big.NewInt(1) keyDelta := big.NewInt(1) var vals [][]byte getValue := func(i int) []byte { if i%2 == 0 { // large return crypto.Keccak256(big.NewInt(int64(i)).Bytes()) } else { //small return big.NewInt(int64(i)).Bytes() } } for i := 0; i < 1000; i++ { key := common.BigToHash(keyB) value := getValue(i) st.TryUpdate(key.Bytes(), value) vals = append(vals, value) keyB = keyB.Add(keyB, keyDelta) keyDelta.Add(keyDelta, common.Big1) } st.Hash() for i := 0; i < 1000; i++ { want := getValue(i) have := vals[i] if !bytes.Equal(have, want) { t.Fatalf("item %d, have %#x want %#x", i, have, want) } } } // TestStacktrieSerialization tests that the stacktrie works well if we // serialize/unserialize it a lot func TestStacktrieSerialization(t *testing.T) { var ( st = NewStackTrie(nil) nt, _ = New(common.Hash{}, NewDatabase(memorydb.New())) keyB = big.NewInt(1) keyDelta = big.NewInt(1) vals [][]byte keys [][]byte ) getValue := func(i int) []byte { if i%2 == 0 { // large return crypto.Keccak256(big.NewInt(int64(i)).Bytes()) } else { //small return big.NewInt(int64(i)).Bytes() } } for i := 0; i < 10; i++ { vals = append(vals, getValue(i)) keys = append(keys, common.BigToHash(keyB).Bytes()) keyB = keyB.Add(keyB, keyDelta) keyDelta.Add(keyDelta, common.Big1) } for i, k := range keys { nt.TryUpdate(k, common.CopyBytes(vals[i])) } for i, k := range keys { blob, err := st.MarshalBinary() if err != nil { t.Fatal(err) } newSt, err := NewFromBinary(blob, nil) if err != nil { t.Fatal(err) } st = newSt st.TryUpdate(k, common.CopyBytes(vals[i])) } if have, want := st.Hash(), nt.Hash(); have != want { t.Fatalf("have %#x want %#x", have, want) } }