core/state: polish

This commit is contained in:
Gary Rong 2024-11-14 15:20:17 +08:00
parent 9f9b209277
commit 05ee450f2d
7 changed files with 64 additions and 61 deletions

View file

@ -399,10 +399,7 @@ func (dl *diffLayer) flatten() snapshot {
continue continue
} }
// Storage exists in both parent and child, merge the slots // Storage exists in both parent and child, merge the slots
comboData := parent.storageData[accountHash] maps.Copy(parent.storageData[accountHash], storage)
for storageHash, data := range storage {
comboData[storageHash] = data
}
} }
// Return the combo parent // Return the combo parent
return &diffLayer{ return &diffLayer{

View file

@ -67,20 +67,17 @@ func (dl *diffLayer) initBinaryAccountIterator(seek common.Hash) Iterator {
func (dl *diffLayer) initBinaryStorageIterator(account, seek common.Hash) Iterator { func (dl *diffLayer) initBinaryStorageIterator(account, seek common.Hash) Iterator {
parent, ok := dl.parent.(*diffLayer) parent, ok := dl.parent.(*diffLayer)
if !ok { if !ok {
a := dl.StorageIterator(account, seek)
b := dl.Parent().StorageIterator(account, seek)
l := &binaryIterator{ l := &binaryIterator{
a: a, a: dl.StorageIterator(account, seek),
b: b, b: dl.Parent().StorageIterator(account, seek),
account: account, account: account,
} }
l.aDone = !l.a.Next() l.aDone = !l.a.Next()
l.bDone = !l.b.Next() l.bDone = !l.b.Next()
return l return l
} }
a := dl.StorageIterator(account, seek)
l := &binaryIterator{ l := &binaryIterator{
a: a, a: dl.StorageIterator(account, seek),
b: parent.initBinaryStorageIterator(account, seek), b: parent.initBinaryStorageIterator(account, seek),
account: account, account: account,
} }

View file

@ -543,10 +543,14 @@ func diffToDisk(bottom *diffLayer) *diskLayer {
continue continue
} }
// Push the account to disk // Push the account to disk
if len(data) != 0 {
rawdb.WriteAccountSnapshot(batch, hash, data) rawdb.WriteAccountSnapshot(batch, hash, data)
base.cache.Set(hash[:], data) base.cache.Set(hash[:], data)
snapshotCleanAccountWriteMeter.Mark(int64(len(data))) snapshotCleanAccountWriteMeter.Mark(int64(len(data)))
} else {
rawdb.DeleteAccountSnapshot(batch, hash)
base.cache.Set(hash[:], nil)
}
snapshotFlushAccountItemMeter.Mark(1) snapshotFlushAccountItemMeter.Mark(1)
snapshotFlushAccountSizeMeter.Mark(int64(len(data))) snapshotFlushAccountSizeMeter.Mark(int64(len(data)))
} }

View file

@ -932,16 +932,17 @@ func (s *StateDB) clearJournalAndRefund() {
// of a specific account. It leverages the associated state snapshot for fast // of a specific account. It leverages the associated state snapshot for fast
// storage iteration and constructs trie node deletion markers by creating // storage iteration and constructs trie node deletion markers by creating
// stack trie with iterated slots. // stack trie with iterated slots.
func (s *StateDB) fastDeleteStorage(snaps *snapshot.Tree, addrHash common.Hash, root common.Hash) (map[common.Hash][]byte, *trienode.NodeSet, error) { func (s *StateDB) fastDeleteStorage(snaps *snapshot.Tree, addrHash common.Hash, root common.Hash) (map[common.Hash][]byte, map[common.Hash][]byte, *trienode.NodeSet, error) {
iter, err := snaps.StorageIterator(s.originalRoot, addrHash, common.Hash{}) iter, err := snaps.StorageIterator(s.originalRoot, addrHash, common.Hash{})
if err != nil { if err != nil {
return nil, nil, err return nil, nil, nil, err
} }
defer iter.Release() defer iter.Release()
var ( var (
nodes = trienode.NewNodeSet(addrHash) nodes = trienode.NewNodeSet(addrHash)
slots = make(map[common.Hash][]byte) slots = make(map[common.Hash][]byte)
deletes = make(map[common.Hash][]byte)
) )
stack := trie.NewStackTrie(func(path []byte, hash common.Hash, blob []byte) { stack := trie.NewStackTrie(func(path []byte, hash common.Hash, blob []byte) {
nodes.AddNode(path, trienode.NewDeleted()) nodes.AddNode(path, trienode.NewDeleted())
@ -949,42 +950,47 @@ func (s *StateDB) fastDeleteStorage(snaps *snapshot.Tree, addrHash common.Hash,
for iter.Next() { for iter.Next() {
slot := common.CopyBytes(iter.Slot()) slot := common.CopyBytes(iter.Slot())
if err := iter.Error(); err != nil { // error might occur after Slot function if err := iter.Error(); err != nil { // error might occur after Slot function
return nil, nil, err return nil, nil, nil, err
} }
slots[iter.Hash()] = slot key := iter.Hash()
slots[key] = slot
deletes[key] = nil
if err := stack.Update(iter.Hash().Bytes(), slot); err != nil { if err := stack.Update(key.Bytes(), slot); err != nil {
return nil, nil, err return nil, nil, nil, err
} }
} }
if err := iter.Error(); err != nil { // error might occur during iteration if err := iter.Error(); err != nil { // error might occur during iteration
return nil, nil, err return nil, nil, nil, err
} }
if stack.Hash() != root { if stack.Hash() != root {
return nil, nil, fmt.Errorf("snapshot is not matched, exp %x, got %x", root, stack.Hash()) return nil, nil, nil, fmt.Errorf("snapshot is not matched, exp %x, got %x", root, stack.Hash())
} }
return slots, nodes, nil return deletes, slots, nodes, nil
} }
// slowDeleteStorage serves as a less-efficient alternative to "fastDeleteStorage," // slowDeleteStorage serves as a less-efficient alternative to "fastDeleteStorage,"
// employed when the associated state snapshot is not available. It iterates the // employed when the associated state snapshot is not available. It iterates the
// storage slots along with all internal trie nodes via trie directly. // storage slots along with all internal trie nodes via trie directly.
func (s *StateDB) slowDeleteStorage(addr common.Address, addrHash common.Hash, root common.Hash) (map[common.Hash][]byte, *trienode.NodeSet, error) { func (s *StateDB) slowDeleteStorage(addr common.Address, addrHash common.Hash, root common.Hash) (map[common.Hash][]byte, map[common.Hash][]byte, *trienode.NodeSet, error) {
tr, err := s.db.OpenStorageTrie(s.originalRoot, addr, root, s.trie) tr, err := s.db.OpenStorageTrie(s.originalRoot, addr, root, s.trie)
if err != nil { if err != nil {
return nil, nil, fmt.Errorf("failed to open storage trie, err: %w", err) return nil, nil, nil, fmt.Errorf("failed to open storage trie, err: %w", err)
} }
it, err := tr.NodeIterator(nil) it, err := tr.NodeIterator(nil)
if err != nil { if err != nil {
return nil, nil, fmt.Errorf("failed to open storage iterator, err: %w", err) return nil, nil, nil, fmt.Errorf("failed to open storage iterator, err: %w", err)
} }
var ( var (
nodes = trienode.NewNodeSet(addrHash) nodes = trienode.NewNodeSet(addrHash)
slots = make(map[common.Hash][]byte) slots = make(map[common.Hash][]byte)
deletes = make(map[common.Hash][]byte)
) )
for it.Next(true) { for it.Next(true) {
if it.Leaf() { if it.Leaf() {
slots[common.BytesToHash(it.LeafKey())] = common.CopyBytes(it.LeafBlob()) key := common.BytesToHash(it.LeafKey())
slots[key] = common.CopyBytes(it.LeafBlob())
deletes[key] = nil
continue continue
} }
if it.Hash() == (common.Hash{}) { if it.Hash() == (common.Hash{}) {
@ -993,19 +999,20 @@ func (s *StateDB) slowDeleteStorage(addr common.Address, addrHash common.Hash, r
nodes.AddNode(it.Path(), trienode.NewDeleted()) nodes.AddNode(it.Path(), trienode.NewDeleted())
} }
if err := it.Error(); err != nil { if err := it.Error(); err != nil {
return nil, nil, err return nil, nil, nil, err
} }
return slots, nodes, nil return deletes, slots, nodes, nil
} }
// deleteStorage is designed to delete the storage trie of a designated account. // deleteStorage is designed to delete the storage trie of a designated account.
// The function will make an attempt to utilize an efficient strategy if the // The function will make an attempt to utilize an efficient strategy if the
// associated state snapshot is reachable; otherwise, it will resort to a less // associated state snapshot is reachable; otherwise, it will resort to a less
// efficient approach. // efficient approach.
func (s *StateDB) deleteStorage(addr common.Address, addrHash common.Hash, root common.Hash) (map[common.Hash][]byte, *trienode.NodeSet, error) { func (s *StateDB) deleteStorage(addr common.Address, addrHash common.Hash, root common.Hash) (map[common.Hash][]byte, map[common.Hash][]byte, *trienode.NodeSet, error) {
var ( var (
err error err error
slots map[common.Hash][]byte slots map[common.Hash][]byte
deletes map[common.Hash][]byte
nodes *trienode.NodeSet nodes *trienode.NodeSet
) )
// The fast approach can be failed if the snapshot is not fully // The fast approach can be failed if the snapshot is not fully
@ -1013,15 +1020,15 @@ func (s *StateDB) deleteStorage(addr common.Address, addrHash common.Hash, root
// one just in case. // one just in case.
snaps := s.db.Snapshot() snaps := s.db.Snapshot()
if snaps != nil { if snaps != nil {
slots, nodes, err = s.fastDeleteStorage(snaps, addrHash, root) deletes, slots, nodes, err = s.fastDeleteStorage(snaps, addrHash, root)
} }
if snaps == nil || err != nil { if snaps == nil || err != nil {
slots, nodes, err = s.slowDeleteStorage(addr, addrHash, root) deletes, slots, nodes, err = s.slowDeleteStorage(addr, addrHash, root)
} }
if err != nil { if err != nil {
return nil, nil, err return nil, nil, nil, err
} }
return slots, nodes, nil return deletes, slots, nodes, nil
} }
// handleDestruction processes all destruction markers and deletes the account // handleDestruction processes all destruction markers and deletes the account
@ -1072,11 +1079,12 @@ func (s *StateDB) handleDestruction() (map[common.Hash]*accountDelete, []*trieno
continue continue
} }
// Remove storage slots belonging to the account. // Remove storage slots belonging to the account.
slots, set, err := s.deleteStorage(addr, addrHash, prev.Root) storages, storagesOrigin, set, err := s.deleteStorage(addr, addrHash, prev.Root)
if err != nil { if err != nil {
return nil, nil, fmt.Errorf("failed to delete storage, err: %w", err) return nil, nil, fmt.Errorf("failed to delete storage, err: %w", err)
} }
op.storagesOrigin = slots op.storages = storages
op.storagesOrigin = storagesOrigin
// Aggregate the associated trie node changes. // Aggregate the associated trie node changes.
nodes = append(nodes, set) nodes = append(nodes, set)

View file

@ -353,8 +353,8 @@ func (test *stateTest) verifyAccountUpdate(next common.Hash, db *triedb.Database
if !bytes.Equal(full, oBlob) { if !bytes.Equal(full, oBlob) {
return fmt.Errorf("account value is not matched, %x", addrHash) return fmt.Errorf("account value is not matched, %x", addrHash)
} }
if len(account) == 0 { if len(nBlob) == 0 {
if len(nBlob) != 0 { if len(account) != 0 {
return errors.New("unexpected account data") return errors.New("unexpected account data")
} }
} else { } else {
@ -363,7 +363,6 @@ func (test *stateTest) verifyAccountUpdate(next common.Hash, db *triedb.Database
return fmt.Errorf("unexpected account data, %x, want %v, got: %v", addrHash, full, nBlob) return fmt.Errorf("unexpected account data, %x, want %v, got: %v", addrHash, full, nBlob)
} }
} }
// Decode accounts // Decode accounts
var ( var (
oAcct types.StateAccount oAcct types.StateAccount

View file

@ -1305,12 +1305,12 @@ func TestDeleteStorage(t *testing.T) {
obj := fastState.getOrNewStateObject(addr) obj := fastState.getOrNewStateObject(addr)
storageRoot := obj.data.Root storageRoot := obj.data.Root
_, fastNodes, err := fastState.deleteStorage(addr, crypto.Keccak256Hash(addr[:]), storageRoot) _, _, fastNodes, err := fastState.deleteStorage(addr, crypto.Keccak256Hash(addr[:]), storageRoot)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
_, slowNodes, err := slowState.deleteStorage(addr, crypto.Keccak256Hash(addr[:]), storageRoot) _, _, slowNodes, err := slowState.deleteStorage(addr, crypto.Keccak256Hash(addr[:]), storageRoot)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }

View file

@ -35,6 +35,7 @@ type contractCode struct {
type accountDelete struct { type accountDelete struct {
address common.Address // address is the unique account identifier address common.Address // address is the unique account identifier
origin []byte // origin is the original value of account data in slim-RLP encoding. origin []byte // origin is the original value of account data in slim-RLP encoding.
storages map[common.Hash][]byte // storages stores mutated slots, the value should be nil.
storagesOrigin map[common.Hash][]byte // storagesOrigin stores the original values of mutated slots in prefix-zero-trimmed RLP format. storagesOrigin map[common.Hash][]byte // storagesOrigin stores the original values of mutated slots in prefix-zero-trimmed RLP format.
} }
@ -85,12 +86,10 @@ func newStateUpdate(originRoot common.Hash, root common.Hash, deletes map[common
accounts[addrHash] = nil accounts[addrHash] = nil
accountsOrigin[addr] = op.origin accountsOrigin[addr] = op.origin
if len(op.storagesOrigin) > 0 { if len(op.storages) > 0 {
subset := make(map[common.Hash][]byte, len(op.storagesOrigin)) storages[addrHash] = op.storages
for key := range op.storagesOrigin {
subset[key] = nil
} }
storages[addrHash] = subset if len(op.storagesOrigin) > 0 {
storagesOrigin[addr] = op.storagesOrigin storagesOrigin[addr] = op.storagesOrigin
} }
} }
@ -121,17 +120,16 @@ func newStateUpdate(originRoot common.Hash, root common.Hash, deletes map[common
// Aggregate the storage original values. If the slot is already present // Aggregate the storage original values. If the slot is already present
// in aggregated storagesOrigin set, skip it. // in aggregated storagesOrigin set, skip it.
if len(op.storagesOrigin) > 0 { if len(op.storagesOrigin) > 0 {
origin := storagesOrigin[addr] origin, exist := storagesOrigin[addr]
if origin == nil { if !exist {
storagesOrigin[addr] = op.storagesOrigin storagesOrigin[addr] = op.storagesOrigin
continue } else {
}
for key, slot := range op.storagesOrigin { for key, slot := range op.storagesOrigin {
if _, found := origin[key]; !found { if _, found := origin[key]; !found {
origin[key] = slot origin[key] = slot
} }
} }
storagesOrigin[addr] = origin }
} }
} }
return &stateUpdate{ return &stateUpdate{