Skip to content

Commit

Permalink
bugfix: fix trie update;
Browse files Browse the repository at this point in the history
  • Loading branch information
0xbundler committed Sep 20, 2023
1 parent cb4fea2 commit 2e0ee20
Show file tree
Hide file tree
Showing 9 changed files with 47 additions and 21 deletions.
3 changes: 3 additions & 0 deletions core/state/database.go
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,9 @@ type Trie interface {

// SetEpoch set current epoch in trie, it must set in initial period, or it will get error behavior.
SetEpoch(types.StateEpoch)

// Epoch get current epoch in trie
Epoch() types.StateEpoch
}

// NewDatabase creates a backing store for state. The returned database is safe for
Expand Down
9 changes: 0 additions & 9 deletions core/state/state_expiry.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,6 @@ var (

// fetchExpiredStorageFromRemote request expired state from remote full state node;
func fetchExpiredStorageFromRemote(fullDB ethdb.FullStateDB, stateRoot common.Hash, addr common.Address, root common.Hash, tr Trie, prefixKey []byte, key common.Hash) (map[string][]byte, error) {
// if no prefix, query from revive trie, got the newest expired info
if len(prefixKey) == 0 {
_, err := tr.GetStorage(addr, key.Bytes())
enErr, ok := err.(*trie.ExpiredNodeError)
if !ok {
return nil, fmt.Errorf("cannot find expired state from trie")
}
prefixKey = enErr.Path
}
proofs, err := fullDB.GetStorageReviveProof(stateRoot, addr, root, []string{common.Bytes2Hex(prefixKey)}, []string{common.Bytes2Hex(key[:])})
if err != nil {
return nil, err
Expand Down
32 changes: 23 additions & 9 deletions core/state/state_object.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ package state

import (
"bytes"
"errors"
"fmt"
"github.com/ethereum/go-ethereum/core/state/snapshot"
"github.com/ethereum/go-ethereum/log"
Expand Down Expand Up @@ -304,7 +303,12 @@ func (s *stateObject) GetCommittedState(key common.Hash) common.Hash {
if s.needLoadFromTrie(err, sv) {
getCommittedStorageTrieMeter.Mark(1)
start := time.Now()
tr, err := s.getTrie()
var tr Trie
if s.db.EnableExpire() {
tr, err = s.getPendingReviveTrie()
} else {
tr, err = s.getTrie()
}
if err != nil {
s.db.setError(fmt.Errorf("state object getTrie err, contract: %v, err: %v", s.address, err))
return common.Hash{}
Expand All @@ -316,7 +320,7 @@ func (s *stateObject) GetCommittedState(key common.Hash) common.Hash {
// handle state expiry situation
if s.db.EnableExpire() {
if enErr, ok := err.(*trie.ExpiredNodeError); ok {
val, err = s.fetchExpiredFromRemote(enErr.Path, key)
val, err = s.fetchExpiredFromRemote(enErr.Path, key, false)
}
// TODO(0xbundler): add epoch record cache for prevent frequency access epoch update, may implement later
//if err != nil {
Expand Down Expand Up @@ -446,6 +450,7 @@ func (s *stateObject) updateTrie() (Trie, error) {
// it must hit in cache
value := s.GetState(key)
dirtyStorage[key] = common.TrimLeftZeroes(value[:])
log.Debug("updateTrie access state", "contract", s.address, "key", key, "epoch", s.db.epoch)
}
}

Expand All @@ -464,6 +469,7 @@ func (s *stateObject) updateTrie() (Trie, error) {
s.db.setError(fmt.Errorf("state object update trie UpdateStorage err, contract: %v, key: %v, err: %v", s.address, key, err))
}
s.db.StorageUpdated += 1
log.Debug("updateTrie UpdateStorage", "contract", s.address, "key", key, "epoch", s.db.epoch, "value", value, "tr", tr.Epoch())
}
// Cache the items for preloading
usedStorage = append(usedStorage, common.CopyBytes(key[:]))
Expand Down Expand Up @@ -499,6 +505,7 @@ func (s *stateObject) updateTrie() (Trie, error) {
snapshotVal, _ = rlp.EncodeToBytes(value)
}
storage[khash] = snapshotVal // snapshotVal will be nil if it's deleted
log.Debug("updateTrie snapshot", "contract", s.address, "key", key, "epoch", s.db.epoch, "value", snapshotVal)

// Track the original value of slot only if it's mutated first time
prev := s.originStorage[key]
Expand Down Expand Up @@ -787,12 +794,22 @@ func (s *stateObject) queryFromReviveState(reviveState map[string]common.Hash, k
}

// fetchExpiredStorageFromRemote request expired state from remote full state node;
func (s *stateObject) fetchExpiredFromRemote(prefixKey []byte, key common.Hash) ([]byte, error) {
func (s *stateObject) fetchExpiredFromRemote(prefixKey []byte, key common.Hash, resolvePath bool) ([]byte, error) {
tr, err := s.getPendingReviveTrie()
if err != nil {
return nil, err
}

// if no prefix, query from revive trie, got the newest expired info
if resolvePath {
_, err := tr.GetStorage(s.address, key.Bytes())
enErr, ok := err.(*trie.ExpiredNodeError)
if !ok {
return nil, fmt.Errorf("cannot find expired state from trie, err: %v", err)
}
prefixKey = enErr.Path
}

log.Info("fetchExpiredStorageFromRemote in stateDB", "addr", s.address, "prefixKey", prefixKey, "key", key, "tr", fmt.Sprintf("%p", tr))
kvs, err := fetchExpiredStorageFromRemote(s.db.fullStateDB, s.db.originalRoot, s.address, s.data.Root, tr, prefixKey, key)

Expand All @@ -808,10 +825,7 @@ func (s *stateObject) fetchExpiredFromRemote(prefixKey []byte, key common.Hash)
}

getCommittedStorageRemoteMeter.Mark(1)
val, ok := s.pendingReviveState[string(crypto.Keccak256(key[:]))]
if !ok {
return nil, errors.New("cannot find revived state")
}
val := s.pendingReviveState[string(crypto.Keccak256(key[:]))]
return val.Bytes(), nil
}

Expand All @@ -837,7 +851,7 @@ func (s *stateObject) getExpirySnapStorage(key common.Hash) (snapshot.SnapValue,
}

// handle from remoteDB, if got err just setError, just return to revert in consensus version.
valRaw, err := s.fetchExpiredFromRemote(nil, key)
valRaw, err := s.fetchExpiredFromRemote(nil, key, true)
if err != nil {
return nil, nil, err
}
Expand Down
3 changes: 3 additions & 0 deletions core/state/trie_prefetcher.go
Original file line number Diff line number Diff line change
Expand Up @@ -548,6 +548,9 @@ func (sf *subfetcher) loop() {
} else {
// address is useless
sf.trie, err = sf.db.OpenStorageTrie(sf.state, sf.addr, sf.root)
if err == nil && sf.enableStateExpiry {
trie.SetEpoch(sf.epoch)
}
}
if err != nil {
continue
Expand Down
4 changes: 4 additions & 0 deletions light/trie.go
Original file line number Diff line number Diff line change
Expand Up @@ -228,6 +228,10 @@ func (t *odrTrie) ProvePath(key []byte, path []byte, proofDb ethdb.KeyValueWrite
return errors.New("not implemented, needs client/server interface split")
}

func (t *odrTrie) Epoch() types.StateEpoch {
return types.StateEpoch0
}

// do tries and retries to execute a function until it returns with no error or
// an error type other than MissingNodeError
func (t *odrTrie) do(key []byte, fn func() error) error {
Expand Down
4 changes: 4 additions & 0 deletions trie/dummy_trie.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,10 @@ import (

type EmptyTrie struct{}

func (t *EmptyTrie) Epoch() types.StateEpoch {
return types.StateEpoch0
}

// NewSecure creates a dummy trie
func NewEmptyTrie() *EmptyTrie {
return &EmptyTrie{}
Expand Down
4 changes: 4 additions & 0 deletions trie/secure_trie.go
Original file line number Diff line number Diff line change
Expand Up @@ -258,6 +258,10 @@ func (t *StateTrie) SetEpoch(epoch types.StateEpoch) {
t.trie.SetEpoch(epoch)
}

func (t *StateTrie) Epoch() types.StateEpoch {
return t.trie.currentEpoch
}

// Copy returns a copy of StateTrie.
func (t *StateTrie) Copy() *StateTrie {
return &StateTrie{
Expand Down
2 changes: 1 addition & 1 deletion trie/trie.go
Original file line number Diff line number Diff line change
Expand Up @@ -1385,7 +1385,7 @@ func (t *Trie) renewNode(epoch types.StateEpoch, childDirty bool, updateEpoch bo
}

// when no epoch update, same as before
if epoch == t.getRootEpoch() {
if epoch == t.currentEpoch {
return childDirty
}

Expand Down
7 changes: 5 additions & 2 deletions trie/trienode/node.go
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ func (set *NodeSet) AddAccountMeta(meta types.StateMeta) error {
}

// Merge adds a set of nodes into the set.
func (set *NodeSet) Merge(owner common.Hash, nodes map[string]*Node) error {
func (set *NodeSet) Merge(owner common.Hash, nodes map[string]*Node, metas map[string][]byte) error {
if set.Owner != owner {
return fmt.Errorf("nodesets belong to different owner are not mergeable %x-%x", set.Owner, owner)
}
Expand All @@ -146,6 +146,9 @@ func (set *NodeSet) Merge(owner common.Hash, nodes map[string]*Node) error {
}
set.AddNode([]byte(path), node)
}
for path, meta := range metas {
set.EpochMetas[path] = meta
}
return nil
}

Expand Down Expand Up @@ -213,7 +216,7 @@ func NewWithNodeSet(set *NodeSet) *MergedNodeSet {
func (set *MergedNodeSet) Merge(other *NodeSet) error {
subset, present := set.Sets[other.Owner]
if present {
return subset.Merge(other.Owner, other.Nodes)
return subset.Merge(other.Owner, other.Nodes, other.EpochMetas)
}
set.Sets[other.Owner] = other
return nil
Expand Down

0 comments on commit 2e0ee20

Please sign in to comment.