diff --git a/trie/trie_test.go b/trie/trie_test.go index 6a32f2436..03ec0cab8 100644 --- a/trie/trie_test.go +++ b/trie/trie_test.go @@ -665,7 +665,9 @@ func makeAccounts(size int) (addresses [][20]byte, accounts [][]byte) { // spongeDb is a dummy db backend which accumulates writes in a sponge type spongeDb struct { - sponge hash.Hash + sponge hash.Hash + id string + journal []string } func (s *spongeDb) Has(key []byte) (bool, error) { panic("implement me") } @@ -676,6 +678,11 @@ func (s *spongeDb) Stat(property string) (string, error) { panic("implement func (s *spongeDb) Compact(start []byte, limit []byte) error { panic("implement me") } func (s *spongeDb) Close() error { return nil } func (s *spongeDb) Put(key []byte, value []byte) error { + valbrief := value + if len(valbrief) > 8 { + valbrief = valbrief[:8] + } + s.journal = append(s.journal, fmt.Sprintf("%v: PUT([%x...], [%d bytes] %x...)\n", s.id, key[:8], len(value), valbrief)) s.sponge.Write(key) s.sponge.Write(value) return nil @@ -793,6 +800,56 @@ func TestCommitSequenceRandomBlobs(t *testing.T) { } } +func TestCommitSequenceStackTrie(t *testing.T) { + for count := 1; count < 200; count++ { + prng := rand.New(rand.NewSource(int64(count))) + // This spongeDb is used to check the sequence of disk-db-writes + s := &spongeDb{sponge: sha3.NewLegacyKeccak256(), id: "a"} + db := NewDatabase(s) + trie, _ := New(common.Hash{}, db) + // Another sponge is used for the stacktrie commits + stackTrieSponge := &spongeDb{sponge: sha3.NewLegacyKeccak256(), id: "b"} + stTrie := NewStackTrie(stackTrieSponge) + // Fill the trie with elements + for i := 1; i < count; i++ { + // 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 + if prng.Intn(2) == 0 { + val = make([]byte, 1+prng.Intn(32)) + } else { + val = make([]byte, 1+prng.Intn(1024)) + } + prng.Read(val) + trie.TryUpdate(key, common.CopyBytes(val)) + stTrie.TryUpdate(key, common.CopyBytes(val)) + } + // Flush trie -> database + root, _ := trie.Commit(nil) + // Flush memdb -> disk (sponge) + db.Commit(root, false, nil) + // And flush stacktrie -> disk + stRoot := stTrie.Commit(stTrie.db) + if stRoot != root { + t.Fatalf("root wrong, got %x exp %x", stRoot, root) + } + if got, exp := stackTrieSponge.sponge.Sum(nil), s.sponge.Sum(nil); !bytes.Equal(got, exp) { + // Show the journal + t.Logf("Expected:") + for i, v := range s.journal { + t.Logf("op %d: %v", i, v) + } + t.Logf("Stacktrie:") + for i, v := range stackTrieSponge.journal { + t.Logf("op %d: %v", i, v) + } + t.Fatalf("test %d, disk write sequence wrong:\ngot %x exp %x\n", count, got, exp) + } + } +} + // BenchmarkCommitAfterHashFixedSize benchmarks the Commit (after Hash) of a fixed number of updates to a trie. // This benchmark is meant to capture the difference on efficiency of small versus large changes. Typically, // storage tries are small (a couple of entries), whereas the full post-block account trie update is large (a couple