diff --git a/trie/stacktrie_fuzzer_test.go b/trie/stacktrie_fuzzer_test.go index df487d16bf..7ff6ef0235 100644 --- a/trie/stacktrie_fuzzer_test.go +++ b/trie/stacktrie_fuzzer_test.go @@ -37,16 +37,13 @@ func FuzzStackTrie(f *testing.F) { } func fuzz(data []byte, debugging bool) { - // This spongeDb is used to check the sequence of disk-db-writes var ( - input = bytes.NewReader(data) - spongeA = &spongeDb{sponge: crypto.NewKeccakState()} - dbA = newTestDatabase(rawdb.NewDatabase(spongeA), rawdb.HashScheme) - trieA = NewEmpty(dbA) - spongeB = &spongeDb{sponge: crypto.NewKeccakState()} - dbB = newTestDatabase(rawdb.NewDatabase(spongeB), rawdb.HashScheme) - trieB = NewStackTrie(func(path []byte, hash common.Hash, blob []byte) { - rawdb.WriteTrieNode(spongeB, common.Hash{}, path, hash, blob, dbB.Scheme()) + input = bytes.NewReader(data) + dbA = newTestDatabase(rawdb.NewMemoryDatabase(), rawdb.HashScheme) + trieA = NewEmpty(dbA) + memDB = rawdb.NewMemoryDatabase() + trieB = NewStackTrie(func(path []byte, hash common.Hash, blob []byte) { + rawdb.WriteTrieNode(memDB, common.Hash{}, path, hash, blob, rawdb.HashScheme) }) vals []*kv maxElements = 10000 @@ -55,13 +52,17 @@ func fuzz(data []byte, debugging bool) { ) // Fill the trie with elements for i := 0; input.Len() > 0 && i < maxElements; i++ { + // Build the key k := make([]byte, 32) input.Read(k) + + // Build the val var a uint16 binary.Read(input, binary.LittleEndian, &a) a = 1 + a%100 v := make([]byte, a) input.Read(v) + if input.Len() == 0 { // If it was exhausted while reading, the value may be all zeroes, // thus 'deletion' which is not supported on stacktrie @@ -73,6 +74,7 @@ func fuzz(data []byte, debugging bool) { } keys[string(k)] = struct{}{} vals = append(vals, &kv{k: k, v: v}) + trieA.MustUpdate(k, v) } if len(vals) == 0 { @@ -99,11 +101,6 @@ func fuzz(data []byte, debugging bool) { if rootA != rootB { panic(fmt.Sprintf("roots differ: (trie) %x != %x (stacktrie)", rootA, rootB)) } - sumA := spongeA.sponge.Sum(nil) - sumB := spongeB.sponge.Sum(nil) - if !bytes.Equal(sumA, sumB) { - panic(fmt.Sprintf("sequence differ: (trie) %x != %x (stacktrie)", sumA, sumB)) - } // Ensure all the nodes are persisted correctly var ( diff --git a/trie/trie_test.go b/trie/trie_test.go index 423ed30fe8..77234d9d9b 100644 --- a/trie/trie_test.go +++ b/trie/trie_test.go @@ -779,6 +779,7 @@ func TestCommitAfterHash(t *testing.T) { func makeAccounts(size int) (addresses [][20]byte, accounts [][]byte) { // Make the random benchmark deterministic random := rand.New(rand.NewSource(0)) + // Create a realistic account trie to hash addresses = make([][20]byte, size) for i := 0; i < len(addresses); i++ { @@ -795,13 +796,18 @@ func makeAccounts(size int) (addresses [][20]byte, accounts [][]byte) { ) // The big.Rand function is not deterministic with regards to 64 vs 32 bit systems, // and will consume different amount of data from the rand source. - //balance = new(big.Int).Rand(random, new(big.Int).Exp(common.Big2, common.Big256, nil)) + // balance = new(big.Int).Rand(random, new(big.Int).Exp(common.Big2, common.Big256, nil)) // Therefore, we instead just read via byte buffer numBytes := random.Uint32() % 33 // [0, 32] bytes balanceBytes := make([]byte, numBytes) random.Read(balanceBytes) balance := new(uint256.Int).SetBytes(balanceBytes) - data, _ := rlp.EncodeToBytes(&types.StateAccount{Nonce: nonce, Balance: balance, Root: root, CodeHash: code}) + data, _ := rlp.EncodeToBytes(&types.StateAccount{ + Nonce: nonce, + Balance: balance, + Root: root, + CodeHash: code, + }) accounts[i] = data } return addresses, accounts @@ -856,6 +862,7 @@ func (s *spongeDb) Flush() { s.sponge.Write([]byte(key)) s.sponge.Write([]byte(s.values[key])) } + fmt.Println(len(s.keys)) } // spongeBatch is a dummy batch which immediately writes to the underlying spongedb @@ -873,10 +880,12 @@ func (b *spongeBatch) Write() error { return nil } func (b *spongeBatch) Reset() {} func (b *spongeBatch) Replay(w ethdb.KeyValueWriter) error { return nil } -// TestCommitSequence tests that the trie.Commit operation writes the elements of the trie -// in the expected order. -// The test data was based on the 'master' code, and is basically random. It can be used -// to check whether changes to the trie modifies the write order or data in any way. +// TestCommitSequence tests that the trie.Commit operation writes the elements +// of the trie in the expected order. +// +// The test data was based on the 'master' code, and is basically random. +// It can be used to check whether changes to the trie modifies the write order +// or data in any way. func TestCommitSequence(t *testing.T) { for i, tc := range []struct { count int @@ -887,19 +896,23 @@ func TestCommitSequence(t *testing.T) { {2000, common.FromHex("4574cd8e6b17f3fe8ad89140d1d0bf4f1bd7a87a8ac3fb623b33550544c77635")}, } { addresses, accounts := makeAccounts(tc.count) + // This spongeDb is used to check the sequence of disk-db-writes s := &spongeDb{sponge: crypto.NewKeccakState()} db := newTestDatabase(rawdb.NewDatabase(s), rawdb.HashScheme) - trie := NewEmpty(db) + // Fill the trie with elements + trie := NewEmpty(db) for i := 0; i < tc.count; i++ { trie.MustUpdate(crypto.Keccak256(addresses[i][:]), accounts[i]) } // Flush trie -> database root, nodes := trie.Commit(false) db.Update(root, types.EmptyRootHash, trienode.NewWithNodeSet(nodes)) + // Flush memdb -> disk (sponge) db.Commit(root) + if got, exp := s.sponge.Sum(nil), tc.expWriteSeqHash; !bytes.Equal(got, exp) { t.Errorf("test %d, disk write sequence wrong:\ngot %x exp %x\n", i, got, exp) } @@ -917,12 +930,13 @@ func TestCommitSequenceRandomBlobs(t *testing.T) { {200, common.FromHex("dde92ca9812e068e6982d04b40846dc65a61a9fd4996fc0f55f2fde172a8e13c")}, {2000, common.FromHex("ab553a7f9aff82e3929c382908e30ef7dd17a332933e92ba3fe873fc661ef382")}, } { - prng := rand.New(rand.NewSource(int64(i))) // This spongeDb is used to check the sequence of disk-db-writes + prng := rand.New(rand.NewSource(int64(i))) s := &spongeDb{sponge: crypto.NewKeccakState()} db := newTestDatabase(rawdb.NewDatabase(s), rawdb.HashScheme) - trie := NewEmpty(db) + // Fill the trie with elements + trie := NewEmpty(db) for i := 0; i < tc.count; i++ { key := make([]byte, 32) var val []byte @@ -939,6 +953,7 @@ func TestCommitSequenceRandomBlobs(t *testing.T) { // Flush trie -> database root, nodes := trie.Commit(false) db.Update(root, types.EmptyRootHash, trienode.NewWithNodeSet(nodes)) + // Flush memdb -> disk (sponge) db.Commit(root) if got, exp := s.sponge.Sum(nil), tc.expWriteSeqHash; !bytes.Equal(got, exp) { @@ -974,19 +989,22 @@ func TestCommitSequenceStackTrie(t *testing.T) { // For the stack trie, we need to do inserts in proper order key := make([]byte, 32) binary.BigEndian.PutUint64(key, uint64(i)) - var val []byte + // 50% short elements, 50% large elements + var val []byte if prng.Intn(2) == 0 { val = make([]byte, 1+prng.Intn(32)) } else { val = make([]byte, 1+prng.Intn(1024)) } prng.Read(val) + trie.Update(key, val) stTrie.Update(key, val) } // Flush trie -> database root, nodes := trie.Commit(false) + // Flush memdb -> disk (sponge) db.Update(root, types.EmptyRootHash, trienode.NewWithNodeSet(nodes)) db.Commit(root) @@ -1045,6 +1063,7 @@ func TestCommitSequenceSmallRoot(t *testing.T) { // Flush trie -> database root, nodes := trie.Commit(false) + // Flush memdb -> disk (sponge) db.Update(root, types.EmptyRootHash, trienode.NewWithNodeSet(nodes)) db.Commit(root)