Skip to content

Commit

Permalink
core/state: polish
Browse files Browse the repository at this point in the history
  • Loading branch information
rjl493456442 committed Nov 14, 2024
1 parent d8a5ca7 commit 851dd99
Show file tree
Hide file tree
Showing 7 changed files with 66 additions and 62 deletions.
5 changes: 1 addition & 4 deletions core/state/snapshot/difflayer.go
Original file line number Diff line number Diff line change
Expand Up @@ -399,10 +399,7 @@ func (dl *diffLayer) flatten() snapshot {
continue
}
// Storage exists in both parent and child, merge the slots
comboData := parent.storageData[accountHash]
for storageHash, data := range storage {
comboData[storageHash] = data
}
maps.Copy(parent.storageData[accountHash], storage)
}
// Return the combo parent
return &diffLayer{
Expand Down
12 changes: 5 additions & 7 deletions core/state/snapshot/iterator_binary.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,20 +67,17 @@ func (dl *diffLayer) initBinaryAccountIterator() Iterator {
func (dl *diffLayer) initBinaryStorageIterator(account common.Hash) Iterator {
parent, ok := dl.parent.(*diffLayer)
if !ok {
a := dl.StorageIterator(account, common.Hash{})
b := dl.Parent().StorageIterator(account, common.Hash{})
l := &binaryIterator{
a: a,
b: b,
a: dl.StorageIterator(account, common.Hash{}),
b: dl.Parent().StorageIterator(account, common.Hash{}),
account: account,
}
l.aDone = !l.a.Next()
l.bDone = !l.b.Next()
return l
}
a := dl.StorageIterator(account, common.Hash{})
l := &binaryIterator{
a: a,
a: dl.StorageIterator(account, common.Hash{}),
b: parent.initBinaryStorageIterator(account),
account: account,
}
Expand All @@ -93,7 +90,8 @@ func (dl *diffLayer) initBinaryStorageIterator(account common.Hash) Iterator {
// or an error if iteration failed for some reason (e.g. root being iterated
// becomes stale and garbage collected).
func (it *binaryIterator) Next() bool {
// verify ensures that the item at the iterator current position exists
// verify ensures that the item at the iterator current position exists.
// The `it.fail` will be set if any error occurs, check the error later.
verify := func() bool {
if it.accountIterator {
return len(it.Account()) != 0
Expand Down
12 changes: 8 additions & 4 deletions core/state/snapshot/snapshot.go
Original file line number Diff line number Diff line change
Expand Up @@ -543,10 +543,14 @@ func diffToDisk(bottom *diffLayer) *diskLayer {
continue
}
// Push the account to disk
rawdb.WriteAccountSnapshot(batch, hash, data)
base.cache.Set(hash[:], data)
snapshotCleanAccountWriteMeter.Mark(int64(len(data)))

if len(data) != 0 {
rawdb.WriteAccountSnapshot(batch, hash, data)
base.cache.Set(hash[:], data)
snapshotCleanAccountWriteMeter.Mark(int64(len(data)))
} else {
rawdb.DeleteAccountSnapshot(batch, hash)
base.cache.Set(hash[:], nil)
}
snapshotFlushAccountItemMeter.Mark(1)
snapshotFlushAccountSizeMeter.Mark(int64(len(data)))
}
Expand Down
66 changes: 37 additions & 29 deletions core/state/statedb.go
Original file line number Diff line number Diff line change
Expand Up @@ -932,59 +932,65 @@ func (s *StateDB) clearJournalAndRefund() {
// of a specific account. It leverages the associated state snapshot for fast
// storage iteration and constructs trie node deletion markers by creating
// 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{})
if err != nil {
return nil, nil, err
return nil, nil, nil, err
}
defer iter.Release()

var (
nodes = trienode.NewNodeSet(addrHash)
slots = make(map[common.Hash][]byte)
nodes = trienode.NewNodeSet(addrHash)
slots = make(map[common.Hash][]byte)
deletes = make(map[common.Hash][]byte)
)
stack := trie.NewStackTrie(func(path []byte, hash common.Hash, blob []byte) {
nodes.AddNode(path, trienode.NewDeleted())
})
for iter.Next() {
slot := common.CopyBytes(iter.Slot())
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 {
return nil, nil, err
if err := stack.Update(key.Bytes(), slot); err != nil {
return nil, nil, nil, err
}
}
if err := iter.Error(); err != nil { // error might occur during iteration
return nil, nil, err
return nil, nil, nil, err
}
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,"
// employed when the associated state snapshot is not available. It iterates the
// 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)
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)
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 (
nodes = trienode.NewNodeSet(addrHash)
slots = make(map[common.Hash][]byte)
nodes = trienode.NewNodeSet(addrHash)
slots = make(map[common.Hash][]byte)
deletes = make(map[common.Hash][]byte)
)
for it.Next(true) {
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
}
if it.Hash() == (common.Hash{}) {
Expand All @@ -993,35 +999,36 @@ func (s *StateDB) slowDeleteStorage(addr common.Address, addrHash common.Hash, r
nodes.AddNode(it.Path(), trienode.NewDeleted())
}
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.
// 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
// 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 (
err error
slots map[common.Hash][]byte
nodes *trienode.NodeSet
err error
slots map[common.Hash][]byte
deletes map[common.Hash][]byte
nodes *trienode.NodeSet
)
// The fast approach can be failed if the snapshot is not fully
// generated, or it's internally corrupted. Fallback to the slow
// one just in case.
snaps := s.db.Snapshot()
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 {
slots, nodes, err = s.slowDeleteStorage(addr, addrHash, root)
deletes, slots, nodes, err = s.slowDeleteStorage(addr, addrHash, root)
}
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
Expand Down Expand Up @@ -1072,11 +1079,12 @@ func (s *StateDB) handleDestruction() (map[common.Hash]*accountDelete, []*trieno
continue
}
// 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 {
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.
nodes = append(nodes, set)
Expand Down
5 changes: 2 additions & 3 deletions core/state/statedb_fuzz_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -353,8 +353,8 @@ func (test *stateTest) verifyAccountUpdate(next common.Hash, db *triedb.Database
if !bytes.Equal(full, oBlob) {
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")
}
} else {
Expand All @@ -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)
}
}

// Decode accounts
var (
oAcct types.StateAccount
Expand Down
4 changes: 2 additions & 2 deletions core/state/statedb_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1305,12 +1305,12 @@ func TestDeleteStorage(t *testing.T) {
obj := fastState.getOrNewStateObject(addr)
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 {
t.Fatal(err)
}

_, slowNodes, err := slowState.deleteStorage(addr, crypto.Keccak256Hash(addr[:]), storageRoot)
_, _, slowNodes, err := slowState.deleteStorage(addr, crypto.Keccak256Hash(addr[:]), storageRoot)
if err != nil {
t.Fatal(err)
}
Expand Down
24 changes: 11 additions & 13 deletions core/state/stateupdate.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ type contractCode struct {
type accountDelete struct {
address common.Address // address is the unique account identifier
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.
}

Expand Down Expand Up @@ -85,12 +86,10 @@ func newStateUpdate(originRoot common.Hash, root common.Hash, deletes map[common
accounts[addrHash] = nil
accountsOrigin[addr] = op.origin

if len(op.storages) > 0 {
storages[addrHash] = op.storages
}
if len(op.storagesOrigin) > 0 {
subset := make(map[common.Hash][]byte, len(op.storagesOrigin))
for key := range op.storagesOrigin {
subset[key] = nil
}
storages[addrHash] = subset
storagesOrigin[addr] = op.storagesOrigin
}
}
Expand Down Expand Up @@ -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
// in aggregated storagesOrigin set, skip it.
if len(op.storagesOrigin) > 0 {
origin := storagesOrigin[addr]
if origin == nil {
origin, exist := storagesOrigin[addr]
if !exist {
storagesOrigin[addr] = op.storagesOrigin
continue
}
for key, slot := range op.storagesOrigin {
if _, found := origin[key]; !found {
origin[key] = slot
} else {
for key, slot := range op.storagesOrigin {
if _, found := origin[key]; !found {
origin[key] = slot
}
}
}
storagesOrigin[addr] = origin
}
}
return &stateUpdate{
Expand Down

0 comments on commit 851dd99

Please sign in to comment.