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 {
nibbles := make([]byte, len(bytes)*2)
for i, b := range bytes {
nibbles[i*2] = b >> 4 // 取字节高4位
nibbles[i*2+1] = b & 0x0F // 取字节低4位
nibbles[i*2] = b >> 4
nibbles[i*2+1] = b & 0x0F
}
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
// are missing or the account blob is incorrect for decoding), an error will
// 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
// must not be modified by the caller. If a node was not found in the database,
// 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
// 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)
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)
if err != nil {

View file

@ -623,7 +623,7 @@ func (s *StateDB) getStateObject(addr common.Address) *stateObject {
if data == nil {
start := time.Now()
var err error
data, err = s.trie.GetAccount(addr)
data, err = s.trie.GetAccount(addr, true)
s.AccountReads += time.Since(start)
if err != nil {
@ -1292,7 +1292,7 @@ func (s *StateDB) Commit(block uint64, deleteEmptyObjects bool) (common.Hash, er
}
if root != origin {
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 {
return common.Hash{}, err
}
@ -1313,6 +1313,18 @@ func (s *StateDB) Commit(block uint64, deleteEmptyObjects bool) (common.Hash, er
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.
// This method must be invoked before state transition.
//

View file

@ -344,9 +344,9 @@ func (sf *subfetcher) loop() {
sf.dups++
} else {
if len(task) == common.AddressLength {
sf.trie.GetAccount(common.BytesToAddress(task))
sf.trie.GetAccount(common.BytesToAddress(task), false)
} else {
sf.trie.GetStorage(sf.addr, task)
sf.trie.GetStorage(sf.addr, task, false)
}
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.
// 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.
func (t *StateTrie) GetStorage(_ common.Address, key []byte) ([]byte, error) {
enc, err := t.trie.Get(t.hashKey(key))
func (t *StateTrie) GetStorage(_ common.Address, key []byte, direct bool) ([]byte, error) {
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 {
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.
// 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.
func (t *StateTrie) GetAccount(address common.Address) (*types.StateAccount, error) {
res, err := t.trie.Get(t.hashKey(address.Bytes()))
func (t *StateTrie) GetAccount(address common.Address, direct bool) (*types.StateAccount, error) {
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 {
return nil, err
}

View file

@ -112,10 +112,15 @@ func (t *tracer) deletedNodes() []string {
// It's possible a few deleted nodes were embedded
// in their parent before, the deletions can be no
// effect by deleting nothing, filter them out.
_, ok := t.accessList[path]
if !ok {
continue
}
// Note: In order to read the account/storage
// 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)
}
return paths

View file

@ -153,6 +153,17 @@ func (t *Trie) Get(key []byte) ([]byte, error) {
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) {
switch n := (origNode).(type) {
case nil:

View file

@ -447,9 +447,9 @@ func verifyAccessList(old *Trie, new *Trie, set *trienode.NodeSet) error {
if !ok || n.IsDeleted() {
return errors.New("expect new node")
}
//if len(n.Prev) > 0 {
// if len(n.Prev) > 0 {
// return errors.New("unexpected origin value")
//}
// }
}
// Check deletion set
for path := range deletes {
@ -457,12 +457,12 @@ func verifyAccessList(old *Trie, new *Trie, set *trienode.NodeSet) error {
if !ok || !n.IsDeleted() {
return errors.New("expect deleted node")
}
//if len(n.Prev) == 0 {
// if len(n.Prev) == 0 {
// return errors.New("expect origin value")
//}
//if !bytes.Equal(n.Prev, blob) {
// }
// if !bytes.Equal(n.Prev, blob) {
// return errors.New("invalid origin value")
//}
// }
}
// Check update set
for path := range updates {
@ -470,12 +470,12 @@ func verifyAccessList(old *Trie, new *Trie, set *trienode.NodeSet) error {
if !ok || n.IsDeleted() {
return errors.New("expect updated node")
}
//if len(n.Prev) == 0 {
// if len(n.Prev) == 0 {
// return errors.New("expect origin value")
//}
//if !bytes.Equal(n.Prev, blob) {
// }
// if !bytes.Equal(n.Prev, blob) {
// return errors.New("invalid origin value")
//}
// }
}
return nil
}
@ -696,7 +696,7 @@ func BenchmarkHash(b *testing.B) {
}
b.ResetTimer()
b.ReportAllocs()
//trie.hashRoot(nil, nil)
// trie.hashRoot(nil, nil)
trie.Hash()
}
@ -793,7 +793,7 @@ 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)
@ -814,6 +814,11 @@ type spongeDb struct {
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) Get(key []byte) ([]byte, error) { return nil, errors.New("no such elem") }
func (s *spongeDb) Delete(key []byte) error { panic("implement me") }
@ -861,6 +866,11 @@ type spongeBatch struct {
db *spongeDb
}
func (b *spongeBatch) DeleteRange(start, end []byte) error {
// TODO implement me
panic("implement me")
}
func (b *spongeBatch) Put(key, value []byte) error {
b.db.Put(key, value)
return nil

View file

@ -68,10 +68,14 @@ type Set struct {
}
// 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{
Accounts: accounts,
Storages: storages,
Accounts: accounts,
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
// 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.
func (t *VerkleTrie) GetAccount(addr common.Address) (*types.StateAccount, error) {
func (t *VerkleTrie) GetAccount(addr common.Address, _ bool) (*types.StateAccount, error) {
var (
acc = &types.StateAccount{}
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
// 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.
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)
val, err := t.root.Get(k, t.nodeResolver)
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
// still be referenced by database.
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

View file

@ -642,6 +642,14 @@ type reader struct {
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
// returned if the node is not found.
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
// 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.
db.waitSync = false

View file

@ -75,6 +75,24 @@ type journalStorage struct {
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.
func (db *Database) loadJournal(diskRoot common.Hash) (layer, error) {
journal := rawdb.ReadTrieJournal(db.diskdb)
@ -136,7 +154,7 @@ func (db *Database) loadLayers() layer {
log.Info("Failed to load journal, discard it", "err", err)
}
// 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
@ -175,8 +193,42 @@ func (db *Database) loadDiskLayer(r *rlp.Stream) (layer, error) {
}
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.
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
}
@ -219,6 +271,14 @@ func (db *Database) loadDiffLayer(parent layer, r *rlp.Stream) (layer, error) {
jstorages []journalStorage
accounts = make(map[common.Address][]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 {
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
}
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
@ -273,6 +356,36 @@ func (dl *diskLayer) journal(w io.Writer) error {
if err := rlp.Encode(w, nodes); err != nil {
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))
return nil
}
@ -327,6 +440,36 @@ func (dl *diffLayer) journal(w io.Writer) error {
if err := rlp.Encode(w, storage); err != nil {
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))
return nil
}

View file

@ -145,6 +145,7 @@ func (tree *layerTree) cap(root common.Hash, layers int) error {
return nil
}
}
var persisted *diskLayer
// We're out of layers, flatten anything below, stopping if it's the disk or if
// the memory limit is not yet exceeded.
switch parent := diff.parentLayer().(type) {
@ -163,6 +164,7 @@ func (tree *layerTree) cap(root common.Hash, layers int) error {
}
tree.layers[base.rootHash()] = base
diff.parent = base
persisted = base.(*diskLayer)
diff.lock.Unlock()
@ -190,6 +192,19 @@ func (tree *layerTree) cap(root common.Hash, layers int) error {
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
}

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
// latest account and storage
LatestAccounts map[common.Hash][]byte
LatestStorages map[common.Hash]map[common.Hash][]byte
DestructSet map[common.Hash]struct{}
latestAccounts map[common.Hash][]byte
latestStorages map[common.Hash]map[common.Hash][]byte
destructSet map[common.Hash]struct{}
}
// 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 {
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
for _, subset := range nodes {
for path, n := range subset {
@ -59,32 +69,35 @@ func newNodeBuffer(limit int, nodes map[common.Hash]map[string]*trienode.Node, l
}
}
return &nodebuffer{
layers: layers,
nodes: nodes,
size: size,
limit: uint64(limit),
layers: layers,
nodes: nodes,
latestAccounts: latestAccounts,
latestStorages: latestStorages,
destructSet: destructSet,
size: size,
limit: uint64(limit),
}
}
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
}
if _, ok := b.DestructSet[hash]; ok {
if _, ok := b.destructSet[hash]; ok {
return nil, true
}
return nil, false
}
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 {
return data, true
}
}
if _, ok := b.DestructSet[accountHash]; ok {
if _, ok := b.destructSet[accountHash]; ok {
return nil, true
}
return nil, false
@ -260,17 +273,17 @@ func (b *nodebuffer) flush(db ethdb.KeyValueStore, clean *cleanCache, id uint64,
batch = b.allocBatch(db)
)
// 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)
clean.plainStates.Set(h.Bytes(), nil)
}
var wg sync.WaitGroup
if len(b.DestructSet) != 0 {
if len(b.destructSet) != 0 {
wg.Add(1)
go func() {
st := time.Now()
nums := 0
for h := range b.DestructSet {
for h := range b.destructSet {
// delete from the clean cache
it := rawdb.IterateStorageTrieNodes(db, h)
for it.Next() {
@ -287,10 +300,10 @@ func (b *nodebuffer) flush(db ethdb.KeyValueStore, clean *cleanCache, id uint64,
it.Release()
}
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))
}
for h, storages := range b.LatestStorages {
for h, storages := range b.latestStorages {
for k, v := range storages {
clean.plainStates.Set(append(h.Bytes(), k.Bytes()...), v)
}

View file

@ -52,6 +52,15 @@ type reader struct {
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 info. Don't modify the returned byte slice since it's not deep-copied
// and still be referenced by database.