core,trie: use direct get for statedb

This commit is contained in:
Fynn 2024-05-11 14:48:42 +08:00
parent 462e2bf71b
commit f9fb58ee0c
18 changed files with 307 additions and 55 deletions

View file

@ -99,8 +99,8 @@ func DeleteAccountTrieNode(db ethdb.KeyValueWriter, path []byte) {
func EncodeNibbles(bytes []byte) []byte { func EncodeNibbles(bytes []byte) []byte {
nibbles := make([]byte, len(bytes)*2) nibbles := make([]byte, len(bytes)*2)
for i, b := range bytes { for i, b := range bytes {
nibbles[i*2] = b >> 4 // 取字节高4位 nibbles[i*2] = b >> 4
nibbles[i*2+1] = b & 0x0F // 取字节低4位 nibbles[i*2+1] = b & 0x0F
} }
return nibbles return nibbles
} }

View file

@ -85,12 +85,12 @@ type Trie interface {
// the trie, nil will be returned. If the trie is corrupted(e.g. some nodes // the trie, nil will be returned. If the trie is corrupted(e.g. some nodes
// are missing or the account blob is incorrect for decoding), an error will // are missing or the account blob is incorrect for decoding), an error will
// be returned. // be returned.
GetAccount(address common.Address) (*types.StateAccount, error) GetAccount(address common.Address, direct bool) (*types.StateAccount, error)
// GetStorage returns the value for key stored in the trie. The value bytes // GetStorage returns the value for key stored in the trie. The value bytes
// must not be modified by the caller. If a node was not found in the database, // must not be modified by the caller. If a node was not found in the database,
// a trie.MissingNodeError is returned. // a trie.MissingNodeError is returned.
GetStorage(addr common.Address, key []byte) ([]byte, error) GetStorage(addr common.Address, key []byte, direct bool) ([]byte, error)
// UpdateAccount abstracts an account write to the trie. It encodes the // UpdateAccount abstracts an account write to the trie. It encodes the
// provided account object with associated algorithm and then updates it // provided account object with associated algorithm and then updates it

View file

@ -202,7 +202,7 @@ func (s *stateObject) GetCommittedState(key common.Hash) common.Hash {
s.db.setError(err) s.db.setError(err)
return common.Hash{} return common.Hash{}
} }
val, err := tr.GetStorage(s.address, key.Bytes()) val, err := tr.GetStorage(s.address, key.Bytes(), true)
s.db.StorageReads += time.Since(start) s.db.StorageReads += time.Since(start)
if err != nil { if err != nil {

View file

@ -623,7 +623,7 @@ func (s *StateDB) getStateObject(addr common.Address) *stateObject {
if data == nil { if data == nil {
start := time.Now() start := time.Now()
var err error var err error
data, err = s.trie.GetAccount(addr) data, err = s.trie.GetAccount(addr, true)
s.AccountReads += time.Since(start) s.AccountReads += time.Since(start)
if err != nil { if err != nil {
@ -1292,7 +1292,7 @@ func (s *StateDB) Commit(block uint64, deleteEmptyObjects bool) (common.Hash, er
} }
if root != origin { if root != origin {
start = time.Now() start = time.Now()
set := triestate.New(s.accountsOrigin, s.storagesOrigin) set := triestate.New(s.accountsOrigin, s.storagesOrigin, s.mustConvertSlmAccount(s.accounts), s.storages, s.convertAccountSet(s.stateObjectsDestruct))
if err := s.db.TrieDB().Update(root, origin, block, nodes, set); err != nil { if err := s.db.TrieDB().Update(root, origin, block, nodes, set); err != nil {
return common.Hash{}, err return common.Hash{}, err
} }
@ -1313,6 +1313,18 @@ func (s *StateDB) Commit(block uint64, deleteEmptyObjects bool) (common.Hash, er
return root, nil return root, nil
} }
func (s *StateDB) mustConvertSlmAccount(slmAccounts map[common.Hash][]byte) map[common.Hash][]byte {
ret := make(map[common.Hash][]byte)
for h, acc := range slmAccounts {
fullAccount, err := types.FullAccountRLP(acc)
if err != nil {
panic(fmt.Sprintf("mustConvertSlmAccount, acc: %v", common.Bytes2Hex(acc)))
}
ret[h] = fullAccount
}
return ret
}
// Prepare handles the preparatory steps for executing a state transition with. // Prepare handles the preparatory steps for executing a state transition with.
// This method must be invoked before state transition. // This method must be invoked before state transition.
// //

View file

@ -344,9 +344,9 @@ func (sf *subfetcher) loop() {
sf.dups++ sf.dups++
} else { } else {
if len(task) == common.AddressLength { if len(task) == common.AddressLength {
sf.trie.GetAccount(common.BytesToAddress(task)) sf.trie.GetAccount(common.BytesToAddress(task), false)
} else { } else {
sf.trie.GetStorage(sf.addr, task) sf.trie.GetStorage(sf.addr, task, false)
} }
sf.seen[string(task)] = struct{}{} sf.seen[string(task)] = struct{}{}
} }

View file

@ -86,8 +86,16 @@ func (t *StateTrie) MustGet(key []byte) []byte {
// and slot key. The value bytes must not be modified by the caller. // and slot key. The value bytes must not be modified by the caller.
// If the specified storage slot is not in the trie, nil will be returned. // If the specified storage slot is not in the trie, nil will be returned.
// If a trie node is not found in the database, a MissingNodeError is returned. // If a trie node is not found in the database, a MissingNodeError is returned.
func (t *StateTrie) GetStorage(_ common.Address, key []byte) ([]byte, error) { func (t *StateTrie) GetStorage(_ common.Address, key []byte, direct bool) ([]byte, error) {
enc, err := t.trie.Get(t.hashKey(key)) var (
enc []byte
err error
)
if direct {
enc, err = t.trie.GetDirectly(t.hashKey(key))
} else {
enc, err = t.trie.Get(t.hashKey(key))
}
if err != nil || len(enc) == 0 { if err != nil || len(enc) == 0 {
return nil, err return nil, err
} }
@ -98,8 +106,16 @@ func (t *StateTrie) GetStorage(_ common.Address, key []byte) ([]byte, error) {
// GetAccount attempts to retrieve an account with provided account address. // GetAccount attempts to retrieve an account with provided account address.
// If the specified account is not in the trie, nil will be returned. // If the specified account is not in the trie, nil will be returned.
// If a trie node is not found in the database, a MissingNodeError is returned. // If a trie node is not found in the database, a MissingNodeError is returned.
func (t *StateTrie) GetAccount(address common.Address) (*types.StateAccount, error) { func (t *StateTrie) GetAccount(address common.Address, direct bool) (*types.StateAccount, error) {
res, err := t.trie.Get(t.hashKey(address.Bytes())) var (
res []byte
err error
)
if direct {
res, err = t.trie.GetDirectly(t.hashKey(address.Bytes()))
} else {
res, err = t.trie.Get(t.hashKey(address.Bytes()))
}
if res == nil || err != nil { if res == nil || err != nil {
return nil, err return nil, err
} }

View file

@ -112,10 +112,15 @@ func (t *tracer) deletedNodes() []string {
// It's possible a few deleted nodes were embedded // It's possible a few deleted nodes were embedded
// in their parent before, the deletions can be no // in their parent before, the deletions can be no
// effect by deleting nothing, filter them out. // effect by deleting nothing, filter them out.
_, ok := t.accessList[path]
if !ok { // Note: In order to read the account/storage
continue // directly from pathdb, redundant storage is made
} // for the embedded node in committer.store, so it
// cannot be filtered out here.
// _, ok := t.accessList[path]
// if !ok {
// continue
// }
paths = append(paths, path) paths = append(paths, path)
} }
return paths return paths

View file

@ -153,6 +153,17 @@ func (t *Trie) Get(key []byte) ([]byte, error) {
return value, err return value, err
} }
func (t *Trie) GetDirectly(key []byte) ([]byte, error) {
if t.reader.reader == nil {
return nil, nil
}
if t.owner == (common.Hash{}) {
return t.reader.reader.Account(common.BytesToHash(key))
} else {
return t.reader.reader.Storage(t.owner, common.BytesToHash(key))
}
}
func (t *Trie) get(origNode node, key []byte, pos int) (value []byte, newnode node, didResolve bool, err error) { func (t *Trie) get(origNode node, key []byte, pos int) (value []byte, newnode node, didResolve bool, err error) {
switch n := (origNode).(type) { switch n := (origNode).(type) {
case nil: case nil:

View file

@ -814,6 +814,11 @@ type spongeDb struct {
values map[string]string values map[string]string
} }
func (s *spongeDb) DeleteRange(start, end []byte) error {
// TODO implement me
panic("implement me")
}
func (s *spongeDb) Has(key []byte) (bool, error) { panic("implement me") } func (s *spongeDb) Has(key []byte) (bool, error) { panic("implement me") }
func (s *spongeDb) Get(key []byte) ([]byte, error) { return nil, errors.New("no such elem") } func (s *spongeDb) Get(key []byte) ([]byte, error) { return nil, errors.New("no such elem") }
func (s *spongeDb) Delete(key []byte) error { panic("implement me") } func (s *spongeDb) Delete(key []byte) error { panic("implement me") }
@ -861,6 +866,11 @@ type spongeBatch struct {
db *spongeDb db *spongeDb
} }
func (b *spongeBatch) DeleteRange(start, end []byte) error {
// TODO implement me
panic("implement me")
}
func (b *spongeBatch) Put(key, value []byte) error { func (b *spongeBatch) Put(key, value []byte) error {
b.db.Put(key, value) b.db.Put(key, value)
return nil return nil

View file

@ -68,10 +68,14 @@ type Set struct {
} }
// New constructs the state set with provided data. // New constructs the state set with provided data.
func New(accounts map[common.Address][]byte, storages map[common.Address]map[common.Hash][]byte) *Set { func New(accounts map[common.Address][]byte, storages map[common.Address]map[common.Hash][]byte,
latestAccount map[common.Hash][]byte, latestStorages map[common.Hash]map[common.Hash][]byte, destructSet map[common.Hash]struct{}) *Set {
return &Set{ return &Set{
Accounts: accounts, Accounts: accounts,
Storages: storages, Storages: storages,
LatestAccounts: latestAccount,
LatestStorages: latestStorages,
DestructSet: destructSet,
} }
} }

View file

@ -78,7 +78,7 @@ func (t *VerkleTrie) GetKey(key []byte) []byte {
// GetAccount implements state.Trie, retrieving the account with the specified // GetAccount implements state.Trie, retrieving the account with the specified
// account address. If the specified account is not in the verkle tree, nil will // account address. If the specified account is not in the verkle tree, nil will
// be returned. If the tree is corrupted, an error will be returned. // be returned. If the tree is corrupted, an error will be returned.
func (t *VerkleTrie) GetAccount(addr common.Address) (*types.StateAccount, error) { func (t *VerkleTrie) GetAccount(addr common.Address, _ bool) (*types.StateAccount, error) {
var ( var (
acc = &types.StateAccount{} acc = &types.StateAccount{}
values [][]byte values [][]byte
@ -118,7 +118,7 @@ func (t *VerkleTrie) GetAccount(addr common.Address) (*types.StateAccount, error
// GetStorage implements state.Trie, retrieving the storage slot with the specified // GetStorage implements state.Trie, retrieving the storage slot with the specified
// account address and storage key. If the specified slot is not in the verkle tree, // account address and storage key. If the specified slot is not in the verkle tree,
// nil will be returned. If the tree is corrupted, an error will be returned. // nil will be returned. If the tree is corrupted, an error will be returned.
func (t *VerkleTrie) GetStorage(addr common.Address, key []byte) ([]byte, error) { func (t *VerkleTrie) GetStorage(addr common.Address, key []byte, _ bool) ([]byte, error) {
k := utils.StorageSlotKeyWithEvaluatedAddress(t.cache.Get(addr.Bytes()), key) k := utils.StorageSlotKeyWithEvaluatedAddress(t.cache.Get(addr.Bytes()), key)
val, err := t.root.Get(k, t.nodeResolver) val, err := t.root.Get(k, t.nodeResolver)
if err != nil { if err != nil {

View file

@ -29,6 +29,12 @@ type Reader interface {
// Don't modify the returned byte slice since it's not deep-copied and // Don't modify the returned byte slice since it's not deep-copied and
// still be referenced by database. // still be referenced by database.
Node(owner common.Hash, path []byte, hash common.Hash) ([]byte, error) Node(owner common.Hash, path []byte, hash common.Hash) ([]byte, error)
// Account retrieves the account with the provided account hash,
Account(hash common.Hash) ([]byte, error)
// Storage retrieves the storage key-value with the provided account hash,
Storage(accountHash, storageHash common.Hash) ([]byte, error)
} }
// PreimageStore wraps the methods of a backing store for reading and writing // PreimageStore wraps the methods of a backing store for reading and writing

View file

@ -642,6 +642,14 @@ type reader struct {
db *Database db *Database
} }
func (reader reader) Account(hash common.Hash) ([]byte, error) {
panic("Not Supported")
}
func (reader reader) Storage(accountHash, storageHash common.Hash) ([]byte, error) {
panic("Not Supported")
}
// Node retrieves the trie node with the given node hash. No error will be // Node retrieves the trie node with the given node hash. No error will be
// returned if the node is not found. // returned if the node is not found.
func (reader *reader) Node(owner common.Hash, path []byte, hash common.Hash) ([]byte, error) { func (reader *reader) Node(owner common.Hash, path []byte, hash common.Hash) ([]byte, error) {

View file

@ -340,7 +340,7 @@ func (db *Database) Enable(root common.Hash) error {
} }
// Re-construct a new disk layer backed by persistent state // Re-construct a new disk layer backed by persistent state
// with **empty clean cache and node buffer**. // with **empty clean cache and node buffer**.
db.tree.reset(newDiskLayer(root, 0, db, nil, newNodeBuffer(db.bufferSize, nil, 0))) db.tree.reset(newDiskLayer(root, 0, db, nil, newNodeBuffer(db.bufferSize, nil, nil, nil, nil, 0)))
// Re-enable the database as the final step. // Re-enable the database as the final step.
db.waitSync = false db.waitSync = false

View file

@ -75,6 +75,24 @@ type journalStorage struct {
Slots [][]byte Slots [][]byte
} }
// journalDestruct is an account deletion entry in a diffLayer's disk journal.
type journalDestruct struct {
Hash common.Hash
}
// journalAccount is an account entry in a diffLayer's disk journal.
type journalLatestAccount struct {
Hash common.Hash
Blob []byte
}
// journalStorage is an account's storage map in a diffLayer's disk journal.
type journalLatestStorage struct {
Hash common.Hash
Keys []common.Hash
Vals [][]byte
}
// loadJournal tries to parse the layer journal from the disk. // loadJournal tries to parse the layer journal from the disk.
func (db *Database) loadJournal(diskRoot common.Hash) (layer, error) { func (db *Database) loadJournal(diskRoot common.Hash) (layer, error) {
journal := rawdb.ReadTrieJournal(db.diskdb) journal := rawdb.ReadTrieJournal(db.diskdb)
@ -136,7 +154,7 @@ func (db *Database) loadLayers() layer {
log.Info("Failed to load journal, discard it", "err", err) log.Info("Failed to load journal, discard it", "err", err)
} }
// Return single layer with persistent state. // Return single layer with persistent state.
return newDiskLayer(root, rawdb.ReadPersistentStateID(db.diskdb), db, nil, newNodeBuffer(db.bufferSize, nil, 0)) return newDiskLayer(root, rawdb.ReadPersistentStateID(db.diskdb), db, nil, newNodeBuffer(db.bufferSize, nil, nil, nil, nil, 0))
} }
// loadDiskLayer reads the binary blob from the layer journal, reconstructing // loadDiskLayer reads the binary blob from the layer journal, reconstructing
@ -175,8 +193,42 @@ func (db *Database) loadDiskLayer(r *rlp.Stream) (layer, error) {
} }
nodes[entry.Owner] = subset nodes[entry.Owner] = subset
} }
// Resolve latest states
var (
jdestructSet []journalDestruct
jlatestAccounts []journalLatestAccount
jlatestStorage []journalLatestStorage
latestAccounts = make(map[common.Hash][]byte)
latestStorages = make(map[common.Hash]map[common.Hash][]byte)
destructSet = make(map[common.Hash]struct{})
)
if err := r.Decode(&jdestructSet); err != nil {
return nil, fmt.Errorf("load destrctSet: %v", err)
}
for _, entry := range jdestructSet {
destructSet[entry.Hash] = struct{}{}
}
if err := r.Decode(&jlatestAccounts); err != nil {
return nil, fmt.Errorf("load latest accounts: %v", err)
}
for _, entry := range jlatestAccounts {
latestAccounts[entry.Hash] = entry.Blob
}
if err := r.Decode(&jlatestStorage); err != nil {
return nil, fmt.Errorf("load latest accounts: %v", err)
}
for _, entry := range jlatestStorage {
latestStorages[entry.Hash] = make(map[common.Hash][]byte)
for i, key := range entry.Keys {
latestStorages[entry.Hash][key] = entry.Vals[i]
}
}
// Calculate the internal state transitions by id difference. // Calculate the internal state transitions by id difference.
base := newDiskLayer(root, id, db, nil, newNodeBuffer(db.bufferSize, nodes, id-stored)) base := newDiskLayer(root, id, db, nil, newNodeBuffer(db.bufferSize, nodes, latestAccounts, latestStorages, destructSet, id-stored))
return base, nil return base, nil
} }
@ -219,6 +271,14 @@ func (db *Database) loadDiffLayer(parent layer, r *rlp.Stream) (layer, error) {
jstorages []journalStorage jstorages []journalStorage
accounts = make(map[common.Address][]byte) accounts = make(map[common.Address][]byte)
storages = make(map[common.Address]map[common.Hash][]byte) storages = make(map[common.Address]map[common.Hash][]byte)
jdestructSet []journalDestruct
jlatestAccounts []journalLatestAccount
jlatestStorage []journalLatestStorage
latestAccounts = make(map[common.Hash][]byte)
latestStorages = make(map[common.Hash]map[common.Hash][]byte)
destructSet = make(map[common.Hash]struct{})
) )
if err := r.Decode(&jaccounts); err != nil { if err := r.Decode(&jaccounts); err != nil {
return nil, fmt.Errorf("load diff accounts: %v", err) return nil, fmt.Errorf("load diff accounts: %v", err)
@ -240,7 +300,30 @@ func (db *Database) loadDiffLayer(parent layer, r *rlp.Stream) (layer, error) {
} }
storages[entry.Account] = set storages[entry.Account] = set
} }
return db.loadDiffLayer(newDiffLayer(parent, root, parent.stateID()+1, block, nodes, triestate.New(accounts, storages)), r) if err := r.Decode(&jdestructSet); err != nil {
return nil, fmt.Errorf("load destrctSet: %v", err)
}
for _, entry := range jdestructSet {
destructSet[entry.Hash] = struct{}{}
}
if err := r.Decode(&jlatestAccounts); err != nil {
return nil, fmt.Errorf("load latest accounts: %v", err)
}
for _, entry := range jlatestAccounts {
latestAccounts[entry.Hash] = entry.Blob
}
if err := r.Decode(&jlatestStorage); err != nil {
return nil, fmt.Errorf("load latest accounts: %v", err)
}
for _, entry := range jlatestStorage {
latestStorages[entry.Hash] = make(map[common.Hash][]byte)
for i, key := range entry.Keys {
latestStorages[entry.Hash][key] = entry.Vals[i]
}
}
return db.loadDiffLayer(newDiffLayer(parent, root, parent.stateID()+1, block, nodes, triestate.New(accounts, storages, latestAccounts, latestStorages, destructSet)), r)
} }
// journal implements the layer interface, marshaling the un-flushed trie nodes // journal implements the layer interface, marshaling the un-flushed trie nodes
@ -273,6 +356,36 @@ func (dl *diskLayer) journal(w io.Writer) error {
if err := rlp.Encode(w, nodes); err != nil { if err := rlp.Encode(w, nodes); err != nil {
return err return err
} }
// Step four Write latest accounts/storages/destructSet into buffer
destructs := make([]journalDestruct, 0, len(dl.buffer.destructSet))
for hash := range dl.buffer.destructSet {
destructs = append(destructs, journalDestruct{Hash: hash})
}
if err := rlp.Encode(w, destructs); err != nil {
return err
}
latestAccounts := make([]journalLatestAccount, 0, len(dl.buffer.latestAccounts))
for hash, blob := range dl.buffer.latestAccounts {
latestAccounts = append(latestAccounts, journalLatestAccount{Hash: hash, Blob: blob})
}
if err := rlp.Encode(w, latestAccounts); err != nil {
return err
}
latestStorage := make([]journalLatestStorage, 0, len(dl.buffer.latestStorages))
for hash, slots := range dl.buffer.latestStorages {
keys := make([]common.Hash, 0, len(slots))
vals := make([][]byte, 0, len(slots))
for key, val := range slots {
keys = append(keys, key)
vals = append(vals, val)
}
latestStorage = append(latestStorage, journalLatestStorage{Hash: hash, Keys: keys, Vals: vals})
}
if err := rlp.Encode(w, latestStorage); err != nil {
return err
}
log.Debug("Journaled pathdb disk layer", "root", dl.root, "nodes", len(dl.buffer.nodes)) log.Debug("Journaled pathdb disk layer", "root", dl.root, "nodes", len(dl.buffer.nodes))
return nil return nil
} }
@ -327,6 +440,36 @@ func (dl *diffLayer) journal(w io.Writer) error {
if err := rlp.Encode(w, storage); err != nil { if err := rlp.Encode(w, storage); err != nil {
return err return err
} }
// Write latest accounts/storages/destructSet into buffer
destructs := make([]journalDestruct, 0, len(dl.states.DestructSet))
for hash := range dl.states.DestructSet {
destructs = append(destructs, journalDestruct{Hash: hash})
}
if err := rlp.Encode(w, destructs); err != nil {
return err
}
latestAccounts := make([]journalLatestAccount, 0, len(dl.states.LatestAccounts))
for hash, blob := range dl.states.LatestAccounts {
latestAccounts = append(latestAccounts, journalLatestAccount{Hash: hash, Blob: blob})
}
if err := rlp.Encode(w, latestAccounts); err != nil {
return err
}
latestStorage := make([]journalLatestStorage, 0, len(dl.states.LatestStorages))
for hash, slots := range dl.states.LatestStorages {
keys := make([]common.Hash, 0, len(slots))
vals := make([][]byte, 0, len(slots))
for key, val := range slots {
keys = append(keys, key)
vals = append(vals, val)
}
latestStorage = append(latestStorage, journalLatestStorage{Hash: hash, Keys: keys, Vals: vals})
}
if err := rlp.Encode(w, latestStorage); err != nil {
return err
}
log.Debug("Journaled pathdb diff layer", "root", dl.root, "parent", dl.parent.rootHash(), "id", dl.stateID(), "block", dl.block, "nodes", len(dl.nodes)) log.Debug("Journaled pathdb diff layer", "root", dl.root, "parent", dl.parent.rootHash(), "id", dl.stateID(), "block", dl.block, "nodes", len(dl.nodes))
return nil return nil
} }

View file

@ -145,6 +145,7 @@ func (tree *layerTree) cap(root common.Hash, layers int) error {
return nil return nil
} }
} }
var persisted *diskLayer
// We're out of layers, flatten anything below, stopping if it's the disk or if // We're out of layers, flatten anything below, stopping if it's the disk or if
// the memory limit is not yet exceeded. // the memory limit is not yet exceeded.
switch parent := diff.parentLayer().(type) { switch parent := diff.parentLayer().(type) {
@ -163,6 +164,7 @@ func (tree *layerTree) cap(root common.Hash, layers int) error {
} }
tree.layers[base.rootHash()] = base tree.layers[base.rootHash()] = base
diff.parent = base diff.parent = base
persisted = base.(*diskLayer)
diff.lock.Unlock() diff.lock.Unlock()
@ -190,6 +192,19 @@ func (tree *layerTree) cap(root common.Hash, layers int) error {
remove(root) remove(root)
} }
} }
// If the disk layer was modified, regenerate all the cumulative blooms
if persisted != nil {
var rebloom func(root common.Hash)
rebloom = func(root common.Hash) {
if diff, ok := tree.layers[root].(*diffLayer); ok {
diff.rebloom(persisted)
}
for _, child := range children[root] {
rebloom(child)
}
}
rebloom(persisted.root)
}
return nil return nil
} }

View file

@ -42,16 +42,26 @@ type nodebuffer struct {
nodes map[common.Hash]map[string]*trienode.Node // The dirty node set, mapped by owner and path nodes map[common.Hash]map[string]*trienode.Node // The dirty node set, mapped by owner and path
// latest account and storage // latest account and storage
LatestAccounts map[common.Hash][]byte latestAccounts map[common.Hash][]byte
LatestStorages map[common.Hash]map[common.Hash][]byte latestStorages map[common.Hash]map[common.Hash][]byte
DestructSet map[common.Hash]struct{} destructSet map[common.Hash]struct{}
} }
// newNodeBuffer initializes the node buffer with the provided nodes. // newNodeBuffer initializes the node buffer with the provided nodes.
func newNodeBuffer(limit int, nodes map[common.Hash]map[string]*trienode.Node, layers uint64) *nodebuffer { func newNodeBuffer(limit int, nodes map[common.Hash]map[string]*trienode.Node,
latestAccounts map[common.Hash][]byte, latestStorages map[common.Hash]map[common.Hash][]byte, destructSet map[common.Hash]struct{}, layers uint64) *nodebuffer {
if nodes == nil { if nodes == nil {
nodes = make(map[common.Hash]map[string]*trienode.Node) nodes = make(map[common.Hash]map[string]*trienode.Node)
} }
if latestAccounts == nil {
latestAccounts = make(map[common.Hash][]byte)
}
if latestStorages == nil {
latestStorages = make(map[common.Hash]map[common.Hash][]byte)
}
if destructSet == nil {
destructSet = make(map[common.Hash]struct{})
}
var size uint64 var size uint64
for _, subset := range nodes { for _, subset := range nodes {
for path, n := range subset { for path, n := range subset {
@ -61,30 +71,33 @@ func newNodeBuffer(limit int, nodes map[common.Hash]map[string]*trienode.Node, l
return &nodebuffer{ return &nodebuffer{
layers: layers, layers: layers,
nodes: nodes, nodes: nodes,
latestAccounts: latestAccounts,
latestStorages: latestStorages,
destructSet: destructSet,
size: size, size: size,
limit: uint64(limit), limit: uint64(limit),
} }
} }
func (b *nodebuffer) account(hash common.Hash) ([]byte, bool) { func (b *nodebuffer) account(hash common.Hash) ([]byte, bool) {
if data, ok := b.LatestAccounts[hash]; ok { if data, ok := b.latestAccounts[hash]; ok {
return data, true return data, true
} }
if _, ok := b.DestructSet[hash]; ok { if _, ok := b.destructSet[hash]; ok {
return nil, true return nil, true
} }
return nil, false return nil, false
} }
func (b *nodebuffer) storage(accountHash, storageHash common.Hash) ([]byte, bool) { func (b *nodebuffer) storage(accountHash, storageHash common.Hash) ([]byte, bool) {
if storage, ok := b.LatestStorages[accountHash]; ok { if storage, ok := b.latestStorages[accountHash]; ok {
if data, ok := storage[storageHash]; ok { if data, ok := storage[storageHash]; ok {
return data, true return data, true
} }
} }
if _, ok := b.DestructSet[accountHash]; ok { if _, ok := b.destructSet[accountHash]; ok {
return nil, true return nil, true
} }
return nil, false return nil, false
@ -260,17 +273,17 @@ func (b *nodebuffer) flush(db ethdb.KeyValueStore, clean *cleanCache, id uint64,
batch = b.allocBatch(db) batch = b.allocBatch(db)
) )
// delete all kv for destructSet first to keep latest for disk nodes // delete all kv for destructSet first to keep latest for disk nodes
for h, _ := range b.DestructSet { for h, _ := range b.destructSet {
rawdb.DeleteStorageTrie(batch, h) rawdb.DeleteStorageTrie(batch, h)
clean.plainStates.Set(h.Bytes(), nil) clean.plainStates.Set(h.Bytes(), nil)
} }
var wg sync.WaitGroup var wg sync.WaitGroup
if len(b.DestructSet) != 0 { if len(b.destructSet) != 0 {
wg.Add(1) wg.Add(1)
go func() { go func() {
st := time.Now() st := time.Now()
nums := 0 nums := 0
for h := range b.DestructSet { for h := range b.destructSet {
// delete from the clean cache // delete from the clean cache
it := rawdb.IterateStorageTrieNodes(db, h) it := rawdb.IterateStorageTrieNodes(db, h)
for it.Next() { for it.Next() {
@ -287,10 +300,10 @@ func (b *nodebuffer) flush(db ethdb.KeyValueStore, clean *cleanCache, id uint64,
it.Release() it.Release()
} }
log.Info("handle deletion of plain storage", "elapsed", time.Since(st).String(), "deleted nums", nums) log.Info("handle deletion of plain storage", "elapsed", time.Since(st).String(), "deleted nums", nums)
for h, acc := range b.LatestAccounts { for h, acc := range b.latestAccounts {
clean.plainStates.Set(h.Bytes(), types.FullToSlimAccountRLP(acc)) clean.plainStates.Set(h.Bytes(), types.FullToSlimAccountRLP(acc))
} }
for h, storages := range b.LatestStorages { for h, storages := range b.latestStorages {
for k, v := range storages { for k, v := range storages {
clean.plainStates.Set(append(h.Bytes(), k.Bytes()...), v) clean.plainStates.Set(append(h.Bytes(), k.Bytes()...), v)
} }

View file

@ -52,6 +52,15 @@ type reader struct {
noHashCheck bool noHashCheck bool
} }
func (r *reader) Account(hash common.Hash) ([]byte, error) {
return r.layer.Account(hash)
}
func (r *reader) Storage(accountHash, storageHash common.Hash) ([]byte, error) {
return r.layer.Storage(accountHash, storageHash)
}
// Node implements database.Reader interface, retrieving the node with specified // Node implements database.Reader interface, retrieving the node with specified
// node info. Don't modify the returned byte slice since it's not deep-copied // node info. Don't modify the returned byte slice since it's not deep-copied
// and still be referenced by database. // and still be referenced by database.