mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-08-19 10:22:23 +00:00
core/state: add flag for inidicating the existence of storage
This commit is contained in:
parent
a4fdf71de0
commit
8c42044252
7 changed files with 99 additions and 30 deletions
|
|
@ -90,10 +90,11 @@ type Trie interface {
|
|||
// be returned.
|
||||
GetAccount(address common.Address) (*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 along with a flag
|
||||
// whether the requested storage slot is existent or not. 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) (bool, []byte, error)
|
||||
|
||||
// UpdateAccount abstracts an account write to the trie. It encodes the
|
||||
// provided account object with associated algorithm and then updates it
|
||||
|
|
|
|||
|
|
@ -46,7 +46,7 @@ type Reader interface {
|
|||
// - Returns an empty slot if it does not exist
|
||||
// - Returns an error only if an unexpected issue occurs
|
||||
// - The returned storage slot is safe to modify after the call
|
||||
Storage(addr common.Address, slot common.Hash) (common.Hash, error)
|
||||
Storage(addr common.Address, slot common.Hash) (bool, common.Hash, error)
|
||||
|
||||
// Copy returns a deep-copied state reader.
|
||||
Copy() Reader
|
||||
|
|
@ -101,29 +101,30 @@ func (r *stateReader) Account(addr common.Address) (*types.StateAccount, error)
|
|||
}
|
||||
|
||||
// Storage implements Reader, retrieving the storage slot specified by the
|
||||
// address and slot key.
|
||||
// address and slot key along with a flag whether the requested storage slot
|
||||
// is existent or not.
|
||||
//
|
||||
// An error will be returned if the associated snapshot is already stale or
|
||||
// the requested storage slot is not yet covered by the snapshot.
|
||||
//
|
||||
// The returned storage slot might be empty if it's not existent.
|
||||
func (r *stateReader) Storage(addr common.Address, key common.Hash) (common.Hash, error) {
|
||||
func (r *stateReader) Storage(addr common.Address, key common.Hash) (bool, common.Hash, error) {
|
||||
addrHash := crypto.HashData(r.buff, addr.Bytes())
|
||||
slotHash := crypto.HashData(r.buff, key.Bytes())
|
||||
ret, err := r.snap.Storage(addrHash, slotHash)
|
||||
if err != nil {
|
||||
return common.Hash{}, err
|
||||
return false, common.Hash{}, err
|
||||
}
|
||||
if len(ret) == 0 {
|
||||
return common.Hash{}, nil
|
||||
return false, common.Hash{}, nil
|
||||
}
|
||||
_, content, _, err := rlp.Split(ret)
|
||||
if err != nil {
|
||||
return common.Hash{}, err
|
||||
return false, common.Hash{}, err
|
||||
}
|
||||
var value common.Hash
|
||||
value.SetBytes(content)
|
||||
return value, nil
|
||||
return true, value, nil
|
||||
}
|
||||
|
||||
// Copy implements Reader, returning a deep-copied snap reader.
|
||||
|
|
@ -192,7 +193,7 @@ func (r *trieReader) Account(addr common.Address) (*types.StateAccount, error) {
|
|||
//
|
||||
// An error will be returned if the trie state is corrupted. An empty storage
|
||||
// slot will be returned if it's not existent in the trie.
|
||||
func (r *trieReader) Storage(addr common.Address, key common.Hash) (common.Hash, error) {
|
||||
func (r *trieReader) Storage(addr common.Address, key common.Hash) (bool, common.Hash, error) {
|
||||
var (
|
||||
tr Trie
|
||||
found bool
|
||||
|
|
@ -210,24 +211,24 @@ func (r *trieReader) Storage(addr common.Address, key common.Hash) (common.Hash,
|
|||
if !ok {
|
||||
_, err := r.Account(addr)
|
||||
if err != nil {
|
||||
return common.Hash{}, err
|
||||
return false, common.Hash{}, err
|
||||
}
|
||||
root = r.subRoots[addr]
|
||||
}
|
||||
var err error
|
||||
tr, err = trie.NewStateTrie(trie.StorageTrieID(r.root, crypto.HashData(r.buff, addr.Bytes()), root), r.db)
|
||||
if err != nil {
|
||||
return common.Hash{}, err
|
||||
return false, common.Hash{}, err
|
||||
}
|
||||
r.subTries[addr] = tr
|
||||
}
|
||||
}
|
||||
ret, err := tr.GetStorage(addr, key.Bytes())
|
||||
exist, ret, err := tr.GetStorage(addr, key.Bytes())
|
||||
if err != nil {
|
||||
return common.Hash{}, err
|
||||
return false, common.Hash{}, err
|
||||
}
|
||||
value.SetBytes(ret)
|
||||
return value, nil
|
||||
return exist, value, nil
|
||||
}
|
||||
|
||||
// Copy implements Reader, returning a deep-copied trie reader.
|
||||
|
|
@ -289,16 +290,16 @@ func (r *multiReader) Account(addr common.Address) (*types.StateAccount, error)
|
|||
// - Returns an empty slot if it does not exist
|
||||
// - Returns an error only if an unexpected issue occurs
|
||||
// - The returned storage slot is safe to modify after the call
|
||||
func (r *multiReader) Storage(addr common.Address, slot common.Hash) (common.Hash, error) {
|
||||
func (r *multiReader) Storage(addr common.Address, slot common.Hash) (bool, common.Hash, error) {
|
||||
var errs []error
|
||||
for _, reader := range r.readers {
|
||||
slot, err := reader.Storage(addr, slot)
|
||||
exist, slot, err := reader.Storage(addr, slot)
|
||||
if err == nil {
|
||||
return slot, nil
|
||||
return exist, slot, nil
|
||||
}
|
||||
errs = append(errs, err)
|
||||
}
|
||||
return common.Hash{}, errors.Join(errs...)
|
||||
return false, common.Hash{}, errors.Join(errs...)
|
||||
}
|
||||
|
||||
// Copy implementing Reader interface, returning a deep-copied state reader.
|
||||
|
|
|
|||
|
|
@ -190,7 +190,7 @@ func (s *stateObject) GetCommittedState(key common.Hash) common.Hash {
|
|||
s.db.StorageLoaded++
|
||||
|
||||
start := time.Now()
|
||||
value, err := s.db.reader.Storage(s.address, key)
|
||||
_, value, err := s.db.reader.Storage(s.address, key)
|
||||
if err != nil {
|
||||
s.db.setError(err)
|
||||
return common.Hash{}
|
||||
|
|
@ -207,6 +207,39 @@ func (s *stateObject) GetCommittedState(key common.Hash) common.Hash {
|
|||
return value
|
||||
}
|
||||
|
||||
// StateExists returns the flag indicating the storage slot is existent or not.
|
||||
func (s *stateObject) StateExists(key common.Hash) bool {
|
||||
_, dirty := s.dirtyStorage[key]
|
||||
if dirty {
|
||||
return true
|
||||
}
|
||||
if _, pending := s.pendingStorage[key]; pending {
|
||||
return true
|
||||
}
|
||||
// If the object was destructed in *this* block (and potentially resurrected),
|
||||
// the storage has been cleared out, and we should *not* consult the previous
|
||||
// database about any storage values. The only possible alternatives are:
|
||||
// 1) resurrect happened, and new slot values were set -- those should
|
||||
// have been handles via pendingStorage above.
|
||||
// 2) we don't have new values, and can deliver empty response back
|
||||
//
|
||||
// Note, there is no account deletion in verkle, this check can be literally
|
||||
// ignored.
|
||||
if _, destructed := s.db.stateObjectsDestruct[s.address]; destructed {
|
||||
return false
|
||||
}
|
||||
s.db.StorageLoaded++
|
||||
start := time.Now()
|
||||
exists, _, err := s.db.reader.Storage(s.address, key)
|
||||
if err != nil {
|
||||
s.db.setError(err)
|
||||
return false
|
||||
}
|
||||
s.db.StorageReads += time.Since(start)
|
||||
|
||||
return exists
|
||||
}
|
||||
|
||||
// SetState updates a value in account storage.
|
||||
func (s *stateObject) SetState(key, value common.Hash) {
|
||||
// If the new value is the same as old, don't set. Otherwise, track only the
|
||||
|
|
|
|||
|
|
@ -36,6 +36,7 @@ import (
|
|||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
"github.com/ethereum/go-ethereum/trie"
|
||||
"github.com/ethereum/go-ethereum/trie/trienode"
|
||||
"github.com/ethereum/go-ethereum/trie/triestate"
|
||||
|
|
@ -392,6 +393,16 @@ func (s *StateDB) GetCommittedState(addr common.Address, hash common.Hash) commo
|
|||
return common.Hash{}
|
||||
}
|
||||
|
||||
// StateExists returns the flag indicating whether the storage slot specified by
|
||||
// address and storage key is existent.
|
||||
func (s *StateDB) StateExists(addr common.Address, key common.Hash) bool {
|
||||
stateObject := s.getStateObject(addr)
|
||||
if stateObject != nil {
|
||||
return stateObject.StateExists(key)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Database retrieves the low level database supporting the lower level trie ops.
|
||||
func (s *StateDB) Database() Database {
|
||||
return s.db
|
||||
|
|
@ -1267,7 +1278,24 @@ func (s *StateDB) commitAndFlush(block uint64, deleteEmptyObjects bool) (*stateU
|
|||
// If snapshotting is enabled, update the snapshot tree with this new version
|
||||
if snap := s.db.Snapshot(); snap != nil {
|
||||
start := time.Now()
|
||||
if err := snap.Update(ret.root, ret.originRoot, ret.destructs, ret.accounts, ret.storages); err != nil {
|
||||
|
||||
storages := ret.storages
|
||||
if s.db.TrieDB().IsVerkle() {
|
||||
zero, _ := rlp.EncodeToBytes(common.Hash{}.Bytes())
|
||||
storages = make(map[common.Hash]map[common.Hash][]byte)
|
||||
for addrHash, slots := range ret.storages {
|
||||
subset := make(map[common.Hash][]byte)
|
||||
storages[addrHash] = subset
|
||||
for slotHash, slot := range slots {
|
||||
if len(slot) == 0 {
|
||||
subset[slotHash] = zero
|
||||
} else {
|
||||
subset[slotHash] = slot
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if err := snap.Update(ret.root, ret.originRoot, ret.destructs, ret.accounts, storages); err != nil {
|
||||
log.Warn("Failed to update snapshot tree", "from", ret.originRoot, "to", ret.root, "err", err)
|
||||
}
|
||||
// Keep 128 diff layers in the memory, persistent layer is 129th.
|
||||
|
|
|
|||
|
|
@ -104,13 +104,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) {
|
||||
func (t *StateTrie) GetStorage(_ common.Address, key []byte) (bool, []byte, error) {
|
||||
enc, err := t.trie.Get(t.hashKey(key))
|
||||
if err != nil || len(enc) == 0 {
|
||||
return nil, err
|
||||
if err != nil {
|
||||
return false, nil, err
|
||||
}
|
||||
if len(enc) == 0 {
|
||||
return false, nil, nil
|
||||
}
|
||||
_, content, _, err := rlp.Split(enc)
|
||||
return content, err
|
||||
return true, content, err
|
||||
}
|
||||
|
||||
// GetAccount attempts to retrieve an account with provided account address.
|
||||
|
|
|
|||
|
|
@ -112,13 +112,16 @@ 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 {
|
||||
return nil, err
|
||||
return false, nil, err
|
||||
}
|
||||
return common.TrimLeftZeroes(val), nil
|
||||
if len(val) == 0 {
|
||||
return false, nil, nil
|
||||
}
|
||||
return true, common.TrimLeftZeroes(val), nil
|
||||
}
|
||||
|
||||
// UpdateAccount implements state.Trie, writing the provided account into the tree.
|
||||
|
|
|
|||
|
|
@ -80,7 +80,7 @@ func TestVerkleTreeReadWrite(t *testing.T) {
|
|||
t.Fatal("account is not matched")
|
||||
}
|
||||
for key, val := range storages[addr] {
|
||||
stored, err := tr.GetStorage(addr, key.Bytes())
|
||||
_, stored, err := tr.GetStorage(addr, key.Bytes())
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to get storage, %v", err)
|
||||
}
|
||||
|
|
@ -126,7 +126,7 @@ func TestVerkleRollBack(t *testing.T) {
|
|||
t.Fatal("account is not matched")
|
||||
}
|
||||
for key, val := range storages[addr] {
|
||||
stored, err := tr.GetStorage(addr, key.Bytes())
|
||||
_, stored, err := tr.GetStorage(addr, key.Bytes())
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to get storage, %v", err)
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue