core, eth, light, trie: db storage reorg benchmark hack

This commit is contained in:
Péter Szilágyi 2018-05-24 12:20:19 +03:00
parent fbf57d53e2
commit 0b297787ac
No known key found for this signature in database
GPG key ID: E9AE538CEDF8293D
22 changed files with 199 additions and 172 deletions

View file

@ -319,7 +319,7 @@ func (bc *BlockChain) FastSyncCommitHead(hash common.Hash) error {
if block == nil {
return fmt.Errorf("non existent block [%x…]", hash[:4])
}
if _, err := trie.NewSecure(block.Root(), bc.stateCache.TrieDB(), 0); err != nil {
if _, err := trie.NewSecure(nil, block.Root(), bc.stateCache.TrieDB(), 0); err != nil {
return err
}
// If all checks out, manually set the head block
@ -636,8 +636,8 @@ func (bc *BlockChain) GetUnclesInChain(block *types.Block, length int) []*types.
// TrieNode retrieves a blob of data associated with a trie node (or code hash)
// either from ephemeral in-memory cache, or from persistent storage.
func (bc *BlockChain) TrieNode(hash common.Hash) ([]byte, error) {
return bc.stateCache.TrieDB().Node(hash)
func (bc *BlockChain) TrieNode(prefix []byte, hash common.Hash) ([]byte, error) {
return bc.stateCache.TrieDB().Node(prefix, hash)
}
// Stop stops the blockchain service. If any imports are currently in progress
@ -666,13 +666,13 @@ func (bc *BlockChain) Stop() {
recent := bc.GetBlockByNumber(number - offset)
log.Info("Writing cached state to disk", "block", recent.Number(), "hash", recent.Hash(), "root", recent.Root())
if err := triedb.Commit(recent.Root(), true); err != nil {
if err := triedb.Commit(nil, recent.Root(), true); err != nil {
log.Error("Failed to commit recent state trie", "err", err)
}
}
}
for !bc.triegc.Empty() {
triedb.Dereference(bc.triegc.PopItem().(common.Hash), common.Hash{})
triedb.Dereference(nil, bc.triegc.PopItem().(common.Hash), nil, common.Hash{})
}
if size := triedb.Size(); size != 0 {
log.Error("Dangling trie nodes after full cleanup")
@ -907,12 +907,12 @@ func (bc *BlockChain) WriteBlockWithState(block *types.Block, receipts []*types.
// If we're running an archive node, always flush
if bc.cacheConfig.Disabled {
if err := triedb.Commit(root, false); err != nil {
if err := triedb.Commit(nil, root, false); err != nil {
return NonStatTy, err
}
} else {
// Full but not archive node, do proper garbage collection
triedb.Reference(root, common.Hash{}) // metadata reference to keep trie alive
triedb.Reference(nil, root, nil, common.Hash{}) // metadata reference to keep trie alive
bc.triegc.Push(root, -float32(block.NumberU64()))
if current := block.NumberU64(); current > triesInMemory {
@ -939,7 +939,7 @@ func (bc *BlockChain) WriteBlockWithState(block *types.Block, receipts []*types.
}
// If optimum or critical limits reached, write to disk
if chosen >= lastWrite+triesInMemory || size >= 2*limit || bc.gcproc >= 2*bc.cacheConfig.TrieTimeLimit {
triedb.Commit(header.Root, true)
triedb.Commit(nil, header.Root, true)
lastWrite = chosen
bc.gcproc = 0
}
@ -951,7 +951,7 @@ func (bc *BlockChain) WriteBlockWithState(block *types.Block, receipts []*types.
bc.triegc.Push(root, number)
break
}
triedb.Dereference(root.(common.Hash), common.Hash{})
triedb.Dereference(nil, root.(common.Hash), nil, common.Hash{})
}
}
}

View file

@ -208,7 +208,7 @@ func GenerateChain(config *params.ChainConfig, parent *types.Block, engine conse
if err != nil {
panic(fmt.Sprintf("state write error: %v", err))
}
if err := statedb.Database().TrieDB().Commit(root, false); err != nil {
if err := statedb.Database().TrieDB().Commit(nil, root, false); err != nil {
panic(fmt.Sprintf("trie write error: %v", err))
}
return block, b.receipts

View file

@ -254,7 +254,7 @@ func (g *Genesis) ToBlock(db ethdb.Database) *types.Block {
head.Difficulty = params.GenesisDifficulty
}
statedb.Commit(false)
statedb.Database().TrieDB().Commit(root, true)
statedb.Database().TrieDB().Commit(nil, root, true)
return types.NewBlock(head, nil, nil, nil)
}

View file

@ -100,7 +100,7 @@ func (db *cachingDB) OpenTrie(root common.Hash) (Trie, error) {
return cachedTrie{db.pastTries[i].Copy(), db}, nil
}
}
tr, err := trie.NewSecure(root, db.db, MaxTrieCacheGen)
tr, err := trie.NewSecure(nil, root, db.db, MaxTrieCacheGen)
if err != nil {
return nil, err
}
@ -121,7 +121,7 @@ func (db *cachingDB) pushTrie(t *trie.SecureTrie) {
// OpenStorageTrie opens the storage trie of an account.
func (db *cachingDB) OpenStorageTrie(addrHash, root common.Hash) (Trie, error) {
return trie.NewSecure(root, db.db, 0)
return trie.NewSecure(addrHash[:], root, db.db, 0)
}
// CopyTrie returns an independent copy of the given trie.
@ -138,7 +138,7 @@ func (db *cachingDB) CopyTrie(t Trie) Trie {
// ContractCode retrieves a particular contract's code.
func (db *cachingDB) ContractCode(addrHash, codeHash common.Hash) ([]byte, error) {
code, err := db.db.Node(codeHash)
code, err := db.db.Node(nil, codeHash)
if err == nil {
db.codeSizeCache.Add(codeHash, len(code))
}

View file

@ -596,7 +596,7 @@ func (s *StateDB) Commit(deleteEmptyObjects bool) (root common.Hash, err error)
case isDirty:
// Write any contract code associated with the state object
if stateObject.code != nil && stateObject.dirtyCode {
s.db.TrieDB().Insert(common.BytesToHash(stateObject.CodeHash()), stateObject.code)
s.db.TrieDB().Insert(nil, common.BytesToHash(stateObject.CodeHash()), stateObject.code)
stateObject.dirtyCode = false
}
// Write any storage changes in the state object to its storage trie.
@ -609,17 +609,17 @@ func (s *StateDB) Commit(deleteEmptyObjects bool) (root common.Hash, err error)
delete(s.stateObjectsDirty, addr)
}
// Write trie changes.
root, err = s.trie.Commit(func(leaf []byte, parent common.Hash) error {
root, err = s.trie.Commit(func(path []byte, leaf []byte, parent common.Hash) error {
var account Account
if err := rlp.DecodeBytes(leaf, &account); err != nil {
return nil
}
if account.Root != emptyState {
s.db.TrieDB().Reference(account.Root, parent)
s.db.TrieDB().Reference(path, account.Root, nil, parent)
}
code := common.BytesToHash(account.CodeHash)
if code != emptyCode {
s.db.TrieDB().Reference(code, parent)
s.db.TrieDB().Reference(nil, code, nil, parent)
}
return nil
})

View file

@ -27,7 +27,7 @@ import (
// NewStateSync create a new state trie download scheduler.
func NewStateSync(root common.Hash, database trie.DatabaseReader) *trie.TrieSync {
var syncer *trie.TrieSync
callback := func(leaf []byte, parent common.Hash) error {
callback := func(path []byte, leaf []byte, parent common.Hash) error {
var obj Account
if err := rlp.Decode(bytes.NewReader(leaf), &obj); err != nil {
return err

View file

@ -466,11 +466,11 @@ func (api *PrivateDebugAPI) getModifiedAccounts(startBlock, endBlock *types.Bloc
return nil, fmt.Errorf("start block height (%d) must be less than end block height (%d)", startBlock.Number().Uint64(), endBlock.Number().Uint64())
}
oldTrie, err := trie.NewSecure(startBlock.Root(), trie.NewDatabase(api.eth.chainDb), 0)
oldTrie, err := trie.NewSecure(nil, startBlock.Root(), trie.NewDatabase(api.eth.chainDb), 0)
if err != nil {
return nil, err
}
newTrie, err := trie.NewSecure(endBlock.Root(), trie.NewDatabase(api.eth.chainDb), 0)
newTrie, err := trie.NewSecure(nil, endBlock.Root(), trie.NewDatabase(api.eth.chainDb), 0)
if err != nil {
return nil, err
}

View file

@ -291,12 +291,12 @@ func (api *PrivateDebugAPI) traceChain(ctx context.Context, start, end *types.Bl
break
}
// Reference the trie twice, once for us, once for the trancer
database.TrieDB().Reference(root, common.Hash{})
database.TrieDB().Reference(nil, root, nil, common.Hash{})
if number >= origin {
database.TrieDB().Reference(root, common.Hash{})
database.TrieDB().Reference(nil, root, nil, common.Hash{})
}
// Dereference all past tries we ourselves are done working with
database.TrieDB().Dereference(proot, common.Hash{})
database.TrieDB().Dereference(nil, proot, nil, common.Hash{})
proot = root
}
}()
@ -317,7 +317,7 @@ func (api *PrivateDebugAPI) traceChain(ctx context.Context, start, end *types.Bl
done[uint64(result.Block)] = result
// Dereference any paret tries held in memory by this task
database.TrieDB().Dereference(res.rootref, common.Hash{})
database.TrieDB().Dereference(nil, res.rootref, nil, common.Hash{})
// Stream completed traces to the user, aborting on the first error
for result, ok := done[next]; ok; result, ok = done[next] {
@ -522,8 +522,8 @@ func (api *PrivateDebugAPI) computeStateDB(block *types.Block, reexec uint64) (*
if err := statedb.Reset(root); err != nil {
return nil, err
}
database.TrieDB().Reference(root, common.Hash{})
database.TrieDB().Dereference(proot, common.Hash{})
database.TrieDB().Reference(nil, root, nil, common.Hash{})
database.TrieDB().Dereference(nil, proot, nil, common.Hash{})
proot = root
}
log.Info("Historical state regenerated", "block", block.NumberU64(), "elapsed", time.Since(start), "size", database.TrieDB().Size())

View file

@ -537,7 +537,7 @@ func (pm *ProtocolManager) handleMsg(p *peer) error {
return errResp(ErrDecode, "msg %v: %v", msg, err)
}
// Retrieve the requested state entry, stopping if enough was found
if entry, err := pm.blockchain.TrieNode(hash); err == nil {
if entry, err := pm.blockchain.TrieNode(nil, hash); err == nil {
data = append(data, entry)
bytes += len(entry)
}

View file

@ -592,7 +592,7 @@ func (pm *ProtocolManager) handleMsg(p *peer) error {
if err != nil {
continue
}
code, _ := statedb.Database().TrieDB().Node(common.BytesToHash(account.CodeHash))
code, _ := statedb.Database().TrieDB().Node(nil, common.BytesToHash(account.CodeHash))
data = append(data, code)
if bytes += len(code); bytes >= softResponseLimit {
@ -873,7 +873,7 @@ func (pm *ProtocolManager) handleMsg(p *peer) error {
if header := pm.blockchain.GetHeaderByNumber(req.BlockNum); header != nil {
sectionHead := rawdb.ReadCanonicalHash(pm.chainDb, req.ChtNum*light.CHTFrequencyServer-1)
if root := light.GetChtRoot(pm.chainDb, req.ChtNum-1, sectionHead); root != (common.Hash{}) {
trie, err := trie.New(root, trieDb)
trie, err := trie.New(nil, root, trieDb)
if err != nil {
continue
}
@ -927,7 +927,7 @@ func (pm *ProtocolManager) handleMsg(p *peer) error {
var prefix string
if root, prefix = pm.getHelperTrie(req.Type, req.TrieIdx); root != (common.Hash{}) {
auxTrie, _ = trie.New(root, trie.NewDatabase(ethdb.NewTable(pm.chainDb, prefix)))
auxTrie, _ = trie.New(nil, root, trie.NewDatabase(ethdb.NewTable(pm.chainDb, prefix)))
}
}
if req.AuxReq == auxRoot {
@ -1107,7 +1107,7 @@ func (pm *ProtocolManager) handleMsg(p *peer) error {
// getAccount retrieves an account from the state based at root.
func (pm *ProtocolManager) getAccount(statedb *state.StateDB, root, hash common.Hash) (state.Account, error) {
trie, err := trie.New(root, statedb.Database().TrieDB())
trie, err := trie.New(nil, root, statedb.Database().TrieDB())
if err != nil {
return state.Account{}, err
}

View file

@ -152,7 +152,7 @@ func (c *ChtIndexerBackend) Reset(section uint64, lastSectionHead common.Hash) e
root = GetChtRoot(c.diskdb, section-1, lastSectionHead)
}
var err error
c.trie, err = trie.New(root, c.triedb)
c.trie, err = trie.New(nil, root, c.triedb)
c.section = section
return err
}
@ -178,7 +178,7 @@ func (c *ChtIndexerBackend) Commit() error {
if err != nil {
return err
}
c.triedb.Commit(root, false)
c.triedb.Commit(nil, root, false)
if ((c.section+1)*c.sectionSize)%CHTFrequencyClient == 0 {
log.Info("Storing CHT", "section", c.section*c.sectionSize/CHTFrequencyClient, "head", c.lastHash, "root", root)
@ -250,7 +250,7 @@ func (b *BloomTrieIndexerBackend) Reset(section uint64, lastSectionHead common.H
root = GetBloomTrieRoot(b.diskdb, section-1, lastSectionHead)
}
var err error
b.trie, err = trie.New(root, b.triedb)
b.trie, err = trie.New(nil, root, b.triedb)
b.section = section
return err
}
@ -297,7 +297,7 @@ func (b *BloomTrieIndexerBackend) Commit() error {
if err != nil {
return err
}
b.triedb.Commit(root, false)
b.triedb.Commit(nil, root, false)
sectionHead := b.sectionHeads[b.bloomTrieRatio-1]
log.Info("Storing bloom trie", "section", b.section, "head", sectionHead, "root", root, "compression", float64(compSize)/float64(decompSize))

View file

@ -151,7 +151,7 @@ func (t *odrTrie) do(key []byte, fn func() error) error {
for {
var err error
if t.trie == nil {
t.trie, err = trie.New(t.id.Root, trie.NewDatabase(t.db.backend.Database()))
t.trie, err = trie.New(nil, t.id.Root, trie.NewDatabase(t.db.backend.Database()))
}
if err == nil {
err = fn()
@ -177,7 +177,7 @@ func newNodeIterator(t *odrTrie, startkey []byte) trie.NodeIterator {
// Open the actual non-ODR trie if that hasn't happened yet.
if t.trie == nil {
it.do(func() error {
t, err := trie.New(t.id.Root, trie.NewDatabase(t.db.backend.Database()))
t, err := trie.New(nil, t.id.Root, trie.NewDatabase(t.db.backend.Database()))
if err == nil {
it.t.trie = t
}

View file

@ -46,9 +46,9 @@ type DatabaseReader interface {
type Database struct {
diskdb ethdb.Database // Persistent storage for matured trie nodes
nodes map[common.Hash]*cachedNode // Data and references relationships of a node
preimages map[common.Hash][]byte // Preimages of nodes from the secure trie
seckeybuf [secureKeyLength]byte // Ephemeral buffer for calculating preimage keys
nodes map[string]*cachedNode // Data and references relationships of a node
preimages map[common.Hash][]byte // Preimages of nodes from the secure trie
seckeybuf [secureKeyLength]byte // Ephemeral buffer for calculating preimage keys
gctime time.Duration // Time spent on garbage collection since last commit
gcnodes uint64 // Nodes garbage collected since last commit
@ -63,9 +63,9 @@ type Database struct {
// cachedNode is all the information we know about a single cached node in the
// memory database write layer.
type cachedNode struct {
blob []byte // Cached data block of the trie node
parents int // Number of live nodes referencing this one
children map[common.Hash]int // Children referenced by this nodes
blob []byte // Cached data block of the trie node
parents int // Number of live nodes referencing this one
children map[string]int // Children referenced by this nodes
}
// NewDatabase creates a new trie database to store ephemeral trie content before
@ -73,8 +73,8 @@ type cachedNode struct {
func NewDatabase(diskdb ethdb.Database) *Database {
return &Database{
diskdb: diskdb,
nodes: map[common.Hash]*cachedNode{
{}: {children: make(map[common.Hash]int)},
nodes: map[string]*cachedNode{
dbkey(nil, common.Hash{}): {children: make(map[string]int)},
},
preimages: make(map[common.Hash][]byte),
}
@ -87,23 +87,29 @@ func (db *Database) DiskDB() DatabaseReader {
// Insert writes a new trie node to the memory database if it's yet unknown. The
// method will make a copy of the slice.
func (db *Database) Insert(hash common.Hash, blob []byte) {
func (db *Database) Insert(path []byte, hash common.Hash, blob []byte) {
db.lock.Lock()
defer db.lock.Unlock()
db.insert(hash, blob)
db.insert(path, hash, blob)
}
// dbkey returns the database key corresponding to a particular trie path and hash.
func dbkey(path []byte, hash common.Hash) string {
return string(append(path, hash[:]...))
}
// insert is the private locked version of Insert.
func (db *Database) insert(hash common.Hash, blob []byte) {
if _, ok := db.nodes[hash]; ok {
func (db *Database) insert(path []byte, hash common.Hash, blob []byte) {
key := dbkey(path, hash)
if _, ok := db.nodes[key]; ok {
return
}
db.nodes[hash] = &cachedNode{
db.nodes[key] = &cachedNode{
blob: common.CopyBytes(blob),
children: make(map[common.Hash]int),
children: make(map[string]int),
}
db.nodesSize += common.StorageSize(common.HashLength + len(blob))
db.nodesSize += common.StorageSize(len(key) + len(blob))
}
// insertPreimage writes a new trie node pre-image to the memory database if it's
@ -120,17 +126,19 @@ func (db *Database) insertPreimage(hash common.Hash, preimage []byte) {
// Node retrieves a cached trie node from memory. If it cannot be found cached,
// the method queries the persistent database for the content.
func (db *Database) Node(hash common.Hash) ([]byte, error) {
func (db *Database) Node(path []byte, hash common.Hash) ([]byte, error) {
// Retrieve the node from cache if available
key := dbkey(path, hash)
db.lock.RLock()
node := db.nodes[hash]
node := db.nodes[key]
db.lock.RUnlock()
if node != nil {
return node.blob, nil
}
// Content unavailable in memory, attempt to retrieve from disk
return db.diskdb.Get(hash[:])
return db.diskdb.Get([]byte(key))
}
// preimage retrieves a cached trie node pre-image from memory. If it cannot be
@ -157,39 +165,45 @@ func (db *Database) secureKey(key []byte) []byte {
return buf
}
// Nodes retrieves the hashes of all the nodes cached within the memory database.
// Nodes retrieves the keys of all the nodes cached within the memory database.
// This method is extremely expensive and should only be used to validate internal
// states in test code.
func (db *Database) Nodes() []common.Hash {
func (db *Database) Nodes() []string {
db.lock.RLock()
defer db.lock.RUnlock()
var hashes = make([]common.Hash, 0, len(db.nodes))
for hash := range db.nodes {
var keys = make([]string, 0, len(db.nodes))
var hash common.Hash
for key := range db.nodes {
copy(hash[:], key[len(key)-common.HashLength:])
if hash != (common.Hash{}) { // Special case for "root" references/nodes
hashes = append(hashes, hash)
keys = append(keys, key)
}
}
return hashes
return keys
}
// Reference adds a new reference from a parent node to a child node.
func (db *Database) Reference(child common.Hash, parent common.Hash) {
func (db *Database) Reference(childPath []byte, childHash common.Hash, parentPath []byte, parentHash common.Hash) {
db.lock.RLock()
defer db.lock.RUnlock()
db.reference(child, parent)
db.reference(childPath, childHash, parentPath, parentHash)
}
// reference is the private locked version of Reference.
func (db *Database) reference(child common.Hash, parent common.Hash) {
func (db *Database) reference(childPath []byte, childHash common.Hash, parentPath []byte, parentHash common.Hash) {
// If the node does not exist, it's a node pulled from disk, skip
child := dbkey(childPath, childHash)
node, ok := db.nodes[child]
if !ok {
return
}
// If the reference already exists, only duplicate for roots
if _, ok = db.nodes[parent].children[child]; ok && parent != (common.Hash{}) {
parent := dbkey(parentPath, parentHash)
if _, ok = db.nodes[parent].children[child]; ok && parentHash != (common.Hash{}) {
return
}
node.parents++
@ -197,12 +211,12 @@ func (db *Database) reference(child common.Hash, parent common.Hash) {
}
// Dereference removes an existing reference from a parent node to a child node.
func (db *Database) Dereference(child common.Hash, parent common.Hash) {
func (db *Database) Dereference(childPath []byte, childHash common.Hash, parentPath []byte, parentHash common.Hash) {
db.lock.Lock()
defer db.lock.Unlock()
nodes, storage, start := len(db.nodes), db.nodesSize, time.Now()
db.dereference(child, parent)
db.dereference(dbkey(childPath, childHash), dbkey(parentPath, parentHash))
db.gcnodes += uint64(nodes - len(db.nodes))
db.gcsize += storage - db.nodesSize
@ -213,7 +227,7 @@ func (db *Database) Dereference(child common.Hash, parent common.Hash) {
}
// dereference is the private locked version of Dereference.
func (db *Database) dereference(child common.Hash, parent common.Hash) {
func (db *Database) dereference(child string, parent string) {
// Dereference the parent-child
node := db.nodes[parent]
@ -229,11 +243,11 @@ func (db *Database) dereference(child common.Hash, parent common.Hash) {
// If there are no more references to the child, delete it and cascade
node.parents--
if node.parents == 0 {
for hash := range node.children {
db.dereference(hash, child)
for path := range node.children {
db.dereference(path, child)
}
delete(db.nodes, child)
db.nodesSize -= common.StorageSize(common.HashLength + len(node.blob))
db.nodesSize -= common.StorageSize(len(child) + len(node.blob))
}
}
@ -241,7 +255,7 @@ func (db *Database) dereference(child common.Hash, parent common.Hash) {
// to disk, forcefully tearing down all references in both directions.
//
// As a side effect, all pre-images accumulated up to this point are also written.
func (db *Database) Commit(node common.Hash, report bool) error {
func (db *Database) Commit(path []byte, node common.Hash, report bool) error {
// Create a database batch to flush persistent data out. It is important that
// outside code doesn't see an inconsistent state (referenced data removed from
// memory cache during commit but not yet in persistent storage). This is ensured
@ -267,7 +281,9 @@ func (db *Database) Commit(node common.Hash, report bool) error {
}
// Move the trie itself into the batch, flushing if enough data is accumulated
nodes, storage := len(db.nodes), db.nodesSize+db.preimagesSize
if err := db.commit(node, batch); err != nil {
key := dbkey(path, node)
if err := db.commit(key, batch); err != nil {
log.Error("Failed to commit trie from trie database", "err", err)
db.lock.RUnlock()
return err
@ -287,7 +303,7 @@ func (db *Database) Commit(node common.Hash, report bool) error {
db.preimages = make(map[common.Hash][]byte)
db.preimagesSize = 0
db.uncache(node)
db.uncache(key)
logger := log.Info
if !report {
@ -303,9 +319,9 @@ func (db *Database) Commit(node common.Hash, report bool) error {
}
// commit is the private locked version of Commit.
func (db *Database) commit(hash common.Hash, batch ethdb.Batch) error {
func (db *Database) commit(key string, batch ethdb.Batch) error {
// If the node does not exist, it's a previously committed node
node, ok := db.nodes[hash]
node, ok := db.nodes[key]
if !ok {
return nil
}
@ -314,7 +330,7 @@ func (db *Database) commit(hash common.Hash, batch ethdb.Batch) error {
return err
}
}
if err := batch.Put(hash[:], node.blob); err != nil {
if err := batch.Put([]byte(key), node.blob); err != nil {
return err
}
// If we've reached an optimal match size, commit and start over
@ -331,9 +347,9 @@ func (db *Database) commit(hash common.Hash, batch ethdb.Batch) error {
// persisted trie is removed from the cache. The reason behind the two-phase
// commit is to ensure consistent data availability while moving from memory
// to disk.
func (db *Database) uncache(hash common.Hash) {
func (db *Database) uncache(key string) {
// If the node does not exist, we're done on this path
node, ok := db.nodes[hash]
node, ok := db.nodes[key]
if !ok {
return
}
@ -341,8 +357,8 @@ func (db *Database) uncache(hash common.Hash) {
for child := range node.children {
db.uncache(child)
}
delete(db.nodes, hash)
db.nodesSize -= common.StorageSize(common.HashLength + len(node.blob))
delete(db.nodes, key)
db.nodesSize -= common.StorageSize(len(key) + len(node.blob))
}
// Size returns the current storage size of the memory cache in front of the

View file

@ -53,7 +53,7 @@ func returnHasherToPool(h *hasher) {
// hash collapses a node down into a hash node, also returning a copy of the
// original node initialized with the computed hash to replace the original one.
func (h *hasher) hash(n node, db *Database, force bool) (node, node, error) {
func (h *hasher) hash(prefix []byte, path []byte, n node, db *Database, force bool) (node, node, error) {
// If we're not storing the node, just hashing, use available cached data
if hash, dirty := n.cache(); hash != nil {
if db == nil {
@ -70,11 +70,11 @@ func (h *hasher) hash(n node, db *Database, force bool) (node, node, error) {
}
}
// Trie not processed yet or needs storage, walk the children
collapsed, cached, err := h.hashChildren(n, db)
collapsed, cached, err := h.hashChildren(prefix, path, n, db)
if err != nil {
return hashNode{}, n, err
}
hashed, err := h.store(collapsed, db, force)
hashed, err := h.store(prefix, path, collapsed, db, force)
if err != nil {
return hashNode{}, n, err
}
@ -100,7 +100,7 @@ func (h *hasher) hash(n node, db *Database, force bool) (node, node, error) {
// hashChildren replaces the children of a node with their hashes if the encoded
// size of the child is larger than a hash, returning the collapsed node as well
// as a replacement for the original node with the child hashes cached in.
func (h *hasher) hashChildren(original node, db *Database) (node, node, error) {
func (h *hasher) hashChildren(prefix []byte, path []byte, original node, db *Database) (node, node, error) {
var err error
switch n := original.(type) {
@ -111,7 +111,7 @@ func (h *hasher) hashChildren(original node, db *Database) (node, node, error) {
cached.Key = common.CopyBytes(n.Key)
if _, ok := n.Val.(valueNode); !ok {
collapsed.Val, cached.Val, err = h.hash(n.Val, db, false)
collapsed.Val, cached.Val, err = h.hash(prefix, append(path, n.Key...), n.Val, db, false)
if err != nil {
return original, original, err
}
@ -127,7 +127,7 @@ func (h *hasher) hashChildren(original node, db *Database) (node, node, error) {
for i := 0; i < 16; i++ {
if n.Children[i] != nil {
collapsed.Children[i], cached.Children[i], err = h.hash(n.Children[i], db, false)
collapsed.Children[i], cached.Children[i], err = h.hash(prefix, append(path, byte(i)), n.Children[i], db, false)
if err != nil {
return original, original, err
}
@ -150,7 +150,7 @@ func (h *hasher) hashChildren(original node, db *Database) (node, node, error) {
// store hashes the node n and if we have a storage layer specified, it writes
// the key/value pair to it and tracks any node->child references as well as any
// node->external trie references.
func (h *hasher) store(n node, db *Database, force bool) (node, error) {
func (h *hasher) store(prefix []byte, path []byte, n node, db *Database, force bool) (node, error) {
// Don't store hashes or empty nodes.
if _, isHash := n.(hashNode); n == nil || isHash {
return n, nil
@ -175,18 +175,18 @@ func (h *hasher) store(n node, db *Database, force bool) (node, error) {
db.lock.Lock()
hash := common.BytesToHash(hash)
db.insert(hash, h.tmp.Bytes())
db.insert(prefix, hash, h.tmp.Bytes())
// Track all direct parent->child node references
switch n := n.(type) {
case *shortNode:
if child, ok := n.Val.(hashNode); ok {
db.reference(common.BytesToHash(child), hash)
db.reference(prefix, common.BytesToHash(child), prefix, hash)
}
case *fullNode:
for i := 0; i < 16; i++ {
if child, ok := n.Children[i].(hashNode); ok {
db.reference(common.BytesToHash(child), hash)
db.reference(prefix, common.BytesToHash(child), prefix, hash)
}
}
}
@ -196,13 +196,13 @@ func (h *hasher) store(n node, db *Database, force bool) (node, error) {
if h.onleaf != nil {
switch n := n.(type) {
case *shortNode:
if child, ok := n.Val.(valueNode); ok {
h.onleaf(child, hash)
if child, ok := n.Val.(valueNode); ok && child != nil {
h.onleaf(hexToKeybytes(append(path, compactToHex(n.Key)...)), child, hash)
}
case *fullNode:
for i := 0; i < 16; i++ {
if child, ok := n.Children[i].(valueNode); ok {
h.onleaf(child, hash)
if child, ok := n.Children[i].(valueNode); ok && child != nil {
h.onleaf(hexToKeybytes(append(path, byte(i))), child, hash)
}
}
}

View file

@ -100,28 +100,34 @@ func TestNodeIteratorCoverage(t *testing.T) {
// Create some arbitrary test trie to iterate
db, trie, _ := makeTestTrie()
// Gather all the node hashes found by the iterator
hashes := make(map[common.Hash]struct{})
// Gather all the node keys found by the iterator
keys := make(map[string]struct{})
for it := trie.NodeIterator(nil); it.Next(true); {
if it.Hash() != (common.Hash{}) {
hashes[it.Hash()] = struct{}{}
keys[dbkey(nil, it.Hash())] = struct{}{}
}
}
// Cross check the hashes and the database itself
for hash := range hashes {
if _, err := db.Node(hash); err != nil {
for key := range keys {
var hash common.Hash
copy(hash[:], key[len(key)-common.HashLength:])
if _, err := db.Node(nil, hash); err != nil {
t.Errorf("failed to retrieve reported node %x: %v", hash, err)
}
}
for hash, obj := range db.nodes {
for key, obj := range db.nodes {
var hash common.Hash
copy(hash[:], key[len(key)-common.HashLength:])
if obj != nil && hash != (common.Hash{}) {
if _, ok := hashes[hash]; !ok {
t.Errorf("state entry not reported %x", hash)
if _, ok := keys[key]; !ok {
t.Errorf("state entry not reported %x", key)
}
}
}
for _, key := range db.diskdb.(*ethdb.MemDatabase).Keys() {
if _, ok := hashes[common.BytesToHash(key)]; !ok {
if _, ok := keys[string(key)]; !ok {
t.Errorf("state entry not reported %x", key)
}
}
@ -292,19 +298,19 @@ func testIteratorContinueAfterError(t *testing.T, memonly bool) {
diskdb := ethdb.NewMemDatabase()
triedb := NewDatabase(diskdb)
tr, _ := New(common.Hash{}, triedb)
tr, _ := New(nil, common.Hash{}, triedb)
for _, val := range testdata1 {
tr.Update([]byte(val.k), []byte(val.v))
}
tr.Commit(nil)
if !memonly {
triedb.Commit(tr.Hash(), true)
triedb.Commit(nil, tr.Hash(), true)
}
wantNodeCount := checkIteratorNoDups(t, tr.NodeIterator(nil), nil)
var (
diskKeys [][]byte
memKeys []common.Hash
memKeys []string
)
if memonly {
memKeys = triedb.Nodes()
@ -313,12 +319,14 @@ func testIteratorContinueAfterError(t *testing.T, memonly bool) {
}
for i := 0; i < 20; i++ {
// Create trie that will load all nodes from DB.
tr, _ := New(tr.Hash(), triedb)
tr, err := New(nil, tr.Hash(), triedb)
if err != nil {
panic(err)
}
// Remove a random node from the database. It can't be the root node
// because that one is already loaded.
var (
rkey common.Hash
rkey string
rval []byte
robj *cachedNode
)
@ -326,9 +334,9 @@ func testIteratorContinueAfterError(t *testing.T, memonly bool) {
if memonly {
rkey = memKeys[rand.Intn(len(memKeys))]
} else {
copy(rkey[:], diskKeys[rand.Intn(len(diskKeys))])
rkey = string(diskKeys[rand.Intn(len(diskKeys))])
}
if rkey != tr.Hash() {
if rkey != dbkey(nil, tr.Hash()) {
break
}
}
@ -336,15 +344,15 @@ func testIteratorContinueAfterError(t *testing.T, memonly bool) {
robj = triedb.nodes[rkey]
delete(triedb.nodes, rkey)
} else {
rval, _ = diskdb.Get(rkey[:])
diskdb.Delete(rkey[:])
rval, _ = diskdb.Get([]byte(rkey))
diskdb.Delete([]byte(rkey))
}
// Iterate until the error is hit.
seen := make(map[string]bool)
it := tr.NodeIterator(nil)
checkIteratorNoDups(t, it, seen)
missing, ok := it.Error().(*MissingNodeError)
if !ok || missing.NodeHash != rkey {
if !ok || !bytes.Equal(missing.NodeHash.Bytes(), []byte(rkey)[len(rkey)-common.HashLength:]) {
t.Fatal("didn't hit missing node, got", it.Error())
}
@ -352,7 +360,7 @@ func testIteratorContinueAfterError(t *testing.T, memonly bool) {
if memonly {
triedb.nodes[rkey] = robj
} else {
diskdb.Put(rkey[:], rval)
diskdb.Put([]byte(rkey), rval)
}
checkIteratorNoDups(t, it, seen)
if it.Error() != nil {
@ -379,41 +387,41 @@ func testIteratorContinueAfterSeekError(t *testing.T, memonly bool) {
diskdb := ethdb.NewMemDatabase()
triedb := NewDatabase(diskdb)
ctr, _ := New(common.Hash{}, triedb)
ctr, _ := New(nil, common.Hash{}, triedb)
for _, val := range testdata1 {
ctr.Update([]byte(val.k), []byte(val.v))
}
root, _ := ctr.Commit(nil)
if !memonly {
triedb.Commit(root, true)
triedb.Commit(nil, root, true)
}
barNodeHash := common.HexToHash("05041990364eb72fcb1127652ce40d8bab765f2bfe53225b1170d276cc101c2e")
barNodeKey := string(common.HexToHash("05041990364eb72fcb1127652ce40d8bab765f2bfe53225b1170d276cc101c2e").Bytes())
var (
barNodeBlob []byte
barNodeObj *cachedNode
)
if memonly {
barNodeObj = triedb.nodes[barNodeHash]
delete(triedb.nodes, barNodeHash)
barNodeObj = triedb.nodes[barNodeKey]
delete(triedb.nodes, barNodeKey)
} else {
barNodeBlob, _ = diskdb.Get(barNodeHash[:])
diskdb.Delete(barNodeHash[:])
barNodeBlob, _ = diskdb.Get([]byte(barNodeKey))
diskdb.Delete([]byte(barNodeKey))
}
// Create a new iterator that seeks to "bars". Seeking can't proceed because
// the node is missing.
tr, _ := New(root, triedb)
tr, _ := New(nil, root, triedb)
it := tr.NodeIterator([]byte("bars"))
missing, ok := it.Error().(*MissingNodeError)
if !ok {
t.Fatal("want MissingNodeError, got", it.Error())
} else if missing.NodeHash != barNodeHash {
} else if !bytes.Equal(missing.NodeHash.Bytes(), []byte(barNodeKey)[len(barNodeKey)-common.HashLength:]) {
t.Fatal("wrong node missing")
}
// Reinsert the missing node.
if memonly {
triedb.nodes[barNodeHash] = barNodeObj
triedb.nodes[barNodeKey] = barNodeObj
} else {
diskdb.Put(barNodeHash[:], barNodeBlob)
diskdb.Put([]byte(barNodeKey), barNodeBlob)
}
// Check that iteration produces the right set of values.
if err := checkIteratorOrder(testdata1[2:], NewIterator(it)); err != nil {

View file

@ -69,8 +69,8 @@ func (t *Trie) Prove(key []byte, fromLevel uint, proofDb ethdb.Putter) error {
for i, n := range nodes {
// Don't bother checking for errors here since hasher panics
// if encoding doesn't work and we're not writing to any database.
n, _, _ = hasher.hashChildren(n, nil)
hn, _ := hasher.store(n, nil, false)
n, _, _ = hasher.hashChildren(t.prefix, nil, n, nil)
hn, _ := hasher.store(t.prefix, nil, n, nil, false)
if hash, ok := hn.(hashNode); ok || i == 0 {
// If the node's database encoding is a hash (or is the
// root node), it becomes a proof element.

View file

@ -51,11 +51,11 @@ type SecureTrie struct {
// Loaded nodes are kept around until their 'cache generation' expires.
// A new cache generation is created by each call to Commit.
// cachelimit sets the number of past cache generations to keep.
func NewSecure(root common.Hash, db *Database, cachelimit uint16) (*SecureTrie, error) {
func NewSecure(prefix []byte, root common.Hash, db *Database, cachelimit uint16) (*SecureTrie, error) {
if db == nil {
panic("trie.NewSecure called without a database")
}
trie, err := New(root, db)
trie, err := New(prefix, root, db)
if err != nil {
return nil, err
}

View file

@ -28,7 +28,7 @@ import (
)
func newEmptySecure() *SecureTrie {
trie, _ := NewSecure(common.Hash{}, NewDatabase(ethdb.NewMemDatabase()), 0)
trie, _ := NewSecure(nil, common.Hash{}, NewDatabase(ethdb.NewMemDatabase()), 0)
return trie
}
@ -37,7 +37,7 @@ func makeTestSecureTrie() (*Database, *SecureTrie, map[string][]byte) {
// Create an empty trie
triedb := NewDatabase(ethdb.NewMemDatabase())
trie, _ := NewSecure(common.Hash{}, triedb, 0)
trie, _ := NewSecure(nil, common.Hash{}, triedb, 0)
// Fill it with some arbitrary data
content := make(map[string][]byte)

View file

@ -280,7 +280,7 @@ func (s *TrieSync) children(req *request, object node) ([]*request, error) {
// Notify any external watcher of a new key/value node
if req.callback != nil {
if node, ok := (child.node).(valueNode); ok {
if err := req.callback(node, req.hash); err != nil {
if err := req.callback(nil, node, req.hash); err != nil {
return nil, err
}
}

View file

@ -28,7 +28,7 @@ import (
func makeTestTrie() (*Database, *Trie, map[string][]byte) {
// Create an empty trie
triedb := NewDatabase(ethdb.NewMemDatabase())
trie, _ := New(common.Hash{}, triedb)
trie, _ := New(nil, common.Hash{}, triedb)
// Fill it with some arbitrary data
content := make(map[string][]byte)
@ -59,7 +59,7 @@ func makeTestTrie() (*Database, *Trie, map[string][]byte) {
// content map.
func checkTrieContents(t *testing.T, db *Database, root []byte, content map[string][]byte) {
// Check root availability and trie contents
trie, err := New(common.BytesToHash(root), db)
trie, err := New(nil, common.BytesToHash(root), db)
if err != nil {
t.Fatalf("failed to create trie at %x: %v", root, err)
}
@ -76,7 +76,7 @@ func checkTrieContents(t *testing.T, db *Database, root []byte, content map[stri
// checkTrieConsistency checks that all nodes in a trie are indeed present.
func checkTrieConsistency(db *Database, root common.Hash) error {
// Create and iterate a trie rooted in a subnode
trie, err := New(root, db)
trie, err := New(nil, root, db)
if err != nil {
return nil // Consider a non existent state consistent
}
@ -90,8 +90,8 @@ func checkTrieConsistency(db *Database, root common.Hash) error {
func TestEmptyTrieSync(t *testing.T) {
dbA := NewDatabase(ethdb.NewMemDatabase())
dbB := NewDatabase(ethdb.NewMemDatabase())
emptyA, _ := New(common.Hash{}, dbA)
emptyB, _ := New(emptyRoot, dbB)
emptyA, _ := New(nil, common.Hash{}, dbA)
emptyB, _ := New(nil, emptyRoot, dbB)
for i, trie := range []*Trie{emptyA, emptyB} {
if req := NewTrieSync(trie.Hash(), ethdb.NewMemDatabase(), nil).Missing(1); len(req) != 0 {
@ -118,7 +118,7 @@ func testIterativeTrieSync(t *testing.T, batch int) {
for len(queue) > 0 {
results := make([]SyncResult, len(queue))
for i, hash := range queue {
data, err := srcDb.Node(hash)
data, err := srcDb.Node(nil /*TODO*/, hash)
if err != nil {
t.Fatalf("failed to retrieve node data for %x: %v", hash, err)
}
@ -152,7 +152,7 @@ func TestIterativeDelayedTrieSync(t *testing.T) {
// Sync only half of the scheduled nodes
results := make([]SyncResult, len(queue)/2+1)
for i, hash := range queue[:len(results)] {
data, err := srcDb.Node(hash)
data, err := srcDb.Node(nil /*TODO*/, hash)
if err != nil {
t.Fatalf("failed to retrieve node data for %x: %v", hash, err)
}
@ -193,7 +193,7 @@ func testIterativeRandomTrieSync(t *testing.T, batch int) {
// Fetch all the queued nodes in a random order
results := make([]SyncResult, 0, len(queue))
for hash := range queue {
data, err := srcDb.Node(hash)
data, err := srcDb.Node(nil /*TODO*/, hash)
if err != nil {
t.Fatalf("failed to retrieve node data for %x: %v", hash, err)
}
@ -234,7 +234,7 @@ func TestIterativeRandomDelayedTrieSync(t *testing.T) {
// Sync only half of the scheduled nodes, even those in random order
results := make([]SyncResult, 0, len(queue)/2+1)
for hash := range queue {
data, err := srcDb.Node(hash)
data, err := srcDb.Node(nil /*TODO*/, hash)
if err != nil {
t.Fatalf("failed to retrieve node data for %x: %v", hash, err)
}
@ -279,7 +279,7 @@ func TestDuplicateAvoidanceTrieSync(t *testing.T) {
for len(queue) > 0 {
results := make([]SyncResult, len(queue))
for i, hash := range queue {
data, err := srcDb.Node(hash)
data, err := srcDb.Node(nil /*TODO*/, hash)
if err != nil {
t.Fatalf("failed to retrieve node data for %x: %v", hash, err)
}
@ -319,7 +319,7 @@ func TestIncompleteTrieSync(t *testing.T) {
// Fetch a batch of trie nodes
results := make([]SyncResult, len(queue))
for i, hash := range queue {
data, err := srcDb.Node(hash)
data, err := srcDb.Node(nil /*TODO*/, hash)
if err != nil {
t.Fatalf("failed to retrieve node data for %x: %v", hash, err)
}

View file

@ -57,7 +57,7 @@ func CacheUnloads() int64 {
// LeafCallback is a callback type invoked when a trie operation reaches a leaf
// node. It's used by state sync and commit to allow handling external references
// between account and storage tries.
type LeafCallback func(leaf []byte, parent common.Hash) error
type LeafCallback func(path []byte, leaf []byte, parent common.Hash) error
// Trie is a Merkle Patricia Trie.
// The zero value is an empty trie with no database.
@ -67,6 +67,7 @@ type LeafCallback func(leaf []byte, parent common.Hash) error
type Trie struct {
db *Database
root node
prefix []byte
originalRoot common.Hash
// Cache generation values.
@ -93,12 +94,13 @@ func (t *Trie) newFlag() nodeFlag {
// trie is initially empty and does not require a database. Otherwise,
// New will panic if db is nil and returns a MissingNodeError if root does
// not exist in the database. Accessing the trie loads nodes from db on demand.
func New(root common.Hash, db *Database) (*Trie, error) {
func New(prefix []byte, root common.Hash, db *Database) (*Trie, error) {
if db == nil {
panic("trie.New called without a database")
}
trie := &Trie{
db: db,
prefix: prefix,
originalRoot: root,
}
if (root != common.Hash{}) && root != emptyRoot {
@ -434,7 +436,7 @@ func (t *Trie) resolveHash(n hashNode, prefix []byte) (node, error) {
hash := common.BytesToHash(n)
enc, err := t.db.Node(hash)
enc, err := t.db.Node(t.prefix, hash)
if err != nil || enc == nil {
return nil, &MissingNodeError{NodeHash: hash, Path: prefix}
}
@ -474,5 +476,6 @@ func (t *Trie) hashRoot(db *Database, onleaf LeafCallback) (node, node, error) {
}
h := newHasher(t.cachegen, t.cachelimit, onleaf)
defer returnHasherToPool(h)
return h.hash(t.root, db, true)
return h.hash(t.prefix, nil, t.root, db, true)
}

View file

@ -43,7 +43,7 @@ func init() {
// Used for testing
func newEmpty() *Trie {
trie, _ := New(common.Hash{}, NewDatabase(ethdb.NewMemDatabase()))
trie, _ := New(nil, common.Hash{}, NewDatabase(ethdb.NewMemDatabase()))
return trie
}
@ -67,7 +67,7 @@ func TestNull(t *testing.T) {
}
func TestMissingRoot(t *testing.T) {
trie, err := New(common.HexToHash("0beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33"), NewDatabase(ethdb.NewMemDatabase()))
trie, err := New(nil, common.HexToHash("0beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33"), NewDatabase(ethdb.NewMemDatabase()))
if trie != nil {
t.Error("New returned non-nil trie for invalid root")
}
@ -83,35 +83,35 @@ func testMissingNode(t *testing.T, memonly bool) {
diskdb := ethdb.NewMemDatabase()
triedb := NewDatabase(diskdb)
trie, _ := New(common.Hash{}, triedb)
trie, _ := New(nil, common.Hash{}, triedb)
updateString(trie, "120000", "qwerqwerqwerqwerqwerqwerqwerqwer")
updateString(trie, "123456", "asdfasdfasdfasdfasdfasdfasdfasdf")
root, _ := trie.Commit(nil)
if !memonly {
triedb.Commit(root, true)
triedb.Commit(nil, root, true)
}
trie, _ = New(root, triedb)
trie, _ = New(nil, root, triedb)
_, err := trie.TryGet([]byte("120000"))
if err != nil {
t.Errorf("Unexpected error: %v", err)
}
trie, _ = New(root, triedb)
trie, _ = New(nil, root, triedb)
_, err = trie.TryGet([]byte("120099"))
if err != nil {
t.Errorf("Unexpected error: %v", err)
}
trie, _ = New(root, triedb)
trie, _ = New(nil, root, triedb)
_, err = trie.TryGet([]byte("123456"))
if err != nil {
t.Errorf("Unexpected error: %v", err)
}
trie, _ = New(root, triedb)
trie, _ = New(nil, root, triedb)
err = trie.TryUpdate([]byte("120099"), []byte("zxcvzxcvzxcvzxcvzxcvzxcvzxcvzxcv"))
if err != nil {
t.Errorf("Unexpected error: %v", err)
}
trie, _ = New(root, triedb)
trie, _ = New(nil, root, triedb)
err = trie.TryDelete([]byte("123456"))
if err != nil {
t.Errorf("Unexpected error: %v", err)
@ -119,32 +119,32 @@ func testMissingNode(t *testing.T, memonly bool) {
hash := common.HexToHash("0xe1d943cc8f061a0c0b98162830b970395ac9315654824bf21b73b891365262f9")
if memonly {
delete(triedb.nodes, hash)
delete(triedb.nodes, string(hash[:]))
} else {
diskdb.Delete(hash[:])
}
trie, _ = New(root, triedb)
trie, _ = New(nil, root, triedb)
_, err = trie.TryGet([]byte("120000"))
if _, ok := err.(*MissingNodeError); !ok {
t.Errorf("Wrong error: %v", err)
}
trie, _ = New(root, triedb)
trie, _ = New(nil, root, triedb)
_, err = trie.TryGet([]byte("120099"))
if _, ok := err.(*MissingNodeError); !ok {
t.Errorf("Wrong error: %v", err)
}
trie, _ = New(root, triedb)
trie, _ = New(nil, root, triedb)
_, err = trie.TryGet([]byte("123456"))
if err != nil {
t.Errorf("Unexpected error: %v", err)
}
trie, _ = New(root, triedb)
trie, _ = New(nil, root, triedb)
err = trie.TryUpdate([]byte("120099"), []byte("zxcv"))
if _, ok := err.(*MissingNodeError); !ok {
t.Errorf("Wrong error: %v", err)
}
trie, _ = New(root, triedb)
trie, _ = New(nil, root, triedb)
err = trie.TryDelete([]byte("123456"))
if _, ok := err.(*MissingNodeError); !ok {
t.Errorf("Wrong error: %v", err)
@ -272,7 +272,7 @@ func TestReplication(t *testing.T) {
}
// create a new trie on top of the database and check that lookups work.
trie2, err := New(exp, trie.db)
trie2, err := New(nil, exp, trie.db)
if err != nil {
t.Fatalf("can't recreate trie at %x: %v", exp, err)
}
@ -337,13 +337,13 @@ func TestCacheUnload(t *testing.T) {
updateString(trie, key2, "this is the branch of key2.")
root, _ := trie.Commit(nil)
trie.db.Commit(root, true)
trie.db.Commit(nil, root, true)
// Commit the trie repeatedly and access key1.
// The branch containing it is loaded from DB exactly two times:
// in the 0th and 6th iteration.
db := &countingDB{Database: trie.db.diskdb, gets: make(map[string]int)}
trie, _ = New(root, NewDatabase(db))
trie, _ = New(nil, root, NewDatabase(db))
trie.SetCacheLimit(5)
for i := 0; i < 12; i++ {
getString(trie, key1)
@ -413,7 +413,7 @@ func (randTest) Generate(r *rand.Rand, size int) reflect.Value {
func runRandTest(rt randTest) bool {
triedb := NewDatabase(ethdb.NewMemDatabase())
tr, _ := New(common.Hash{}, triedb)
tr, _ := New(nil, common.Hash{}, triedb)
values := make(map[string]string) // tracks content of the trie
for i, step := range rt {
@ -440,14 +440,14 @@ func runRandTest(rt randTest) bool {
rt[i].err = err
return false
}
newtr, err := New(hash, triedb)
newtr, err := New(nil, hash, triedb)
if err != nil {
rt[i].err = err
return false
}
tr = newtr
case opItercheckhash:
checktr, _ := New(common.Hash{}, triedb)
checktr, _ := New(nil, common.Hash{}, triedb)
it := NewIterator(tr.NodeIterator(nil))
for it.Next() {
checktr.Update(it.Key, it.Value)
@ -520,7 +520,7 @@ func benchGet(b *testing.B, commit bool) {
trie := new(Trie)
if commit {
_, tmpdb := tempDB()
trie, _ = New(common.Hash{}, tmpdb)
trie, _ = New(nil, common.Hash{}, tmpdb)
}
k := make([]byte, 32)
for i := 0; i < benchElemCount; i++ {