mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-08-19 02:12:23 +00:00
core/state: add flag for inidicating the existence of storage
This commit is contained in:
parent
623b17ba20
commit
88dfd15522
7 changed files with 99 additions and 31 deletions
|
|
@ -87,10 +87,11 @@ type Trie interface {
|
||||||
// be returned.
|
// be returned.
|
||||||
GetAccount(address common.Address) (*types.StateAccount, error)
|
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,
|
// must not be modified by the caller. If a node was not found in the database,
|
||||||
// a trie.MissingNodeError is returned.
|
// 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
|
// UpdateAccount abstracts an account write to the trie. It encodes the
|
||||||
// provided account object with associated algorithm and then updates it
|
// provided account object with associated algorithm and then updates it
|
||||||
|
|
|
||||||
|
|
@ -41,12 +41,13 @@ type Reader interface {
|
||||||
Account(addr common.Address) (*types.StateAccount, error)
|
Account(addr common.Address) (*types.StateAccount, error)
|
||||||
|
|
||||||
// Storage retrieves the storage slot associated with a particular account
|
// Storage retrieves the storage slot associated with a particular account
|
||||||
// address and slot key.
|
// address and slot key along with a flag indicating whether the slot is
|
||||||
|
// existent or not.
|
||||||
//
|
//
|
||||||
// - Returns an empty slot if it does not exist
|
// - Returns an empty slot if it does not exist
|
||||||
// - Returns an error only if an unexpected issue occurs
|
// - Returns an error only if an unexpected issue occurs
|
||||||
// - The returned storage slot is safe to modify after the call
|
// - 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 returns a deep-copied state reader.
|
||||||
Copy() Reader
|
Copy() Reader
|
||||||
|
|
@ -101,31 +102,32 @@ func (r *stateReader) Account(addr common.Address) (*types.StateAccount, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Storage implements Reader, retrieving the storage slot specified by the
|
// 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
|
// 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 requested storage slot is not yet covered by the snapshot.
|
||||||
//
|
//
|
||||||
// The returned storage slot might be empty if it's not existent.
|
// 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())
|
addrHash := crypto.HashData(r.buff, addr.Bytes())
|
||||||
slotHash := crypto.HashData(r.buff, key.Bytes())
|
slotHash := crypto.HashData(r.buff, key.Bytes())
|
||||||
ret, err := r.snap.Storage(addrHash, slotHash)
|
ret, err := r.snap.Storage(addrHash, slotHash)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return common.Hash{}, err
|
return false, common.Hash{}, err
|
||||||
}
|
}
|
||||||
if len(ret) == 0 {
|
if len(ret) == 0 {
|
||||||
return common.Hash{}, nil
|
return false, common.Hash{}, nil
|
||||||
}
|
}
|
||||||
// Perform the rlp-decode as the slot value is RLP-encoded in the state
|
// Perform the rlp-decode as the slot value is RLP-encoded in the state
|
||||||
// snapshot.
|
// snapshot.
|
||||||
_, content, _, err := rlp.Split(ret)
|
_, content, _, err := rlp.Split(ret)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return common.Hash{}, err
|
return false, common.Hash{}, err
|
||||||
}
|
}
|
||||||
var value common.Hash
|
var value common.Hash
|
||||||
value.SetBytes(content)
|
value.SetBytes(content)
|
||||||
return value, nil
|
return true, value, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Copy implements Reader, returning a deep-copied snap reader.
|
// Copy implements Reader, returning a deep-copied snap reader.
|
||||||
|
|
@ -194,7 +196,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
|
// 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.
|
// 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 (
|
var (
|
||||||
tr Trie
|
tr Trie
|
||||||
found bool
|
found bool
|
||||||
|
|
@ -212,24 +214,24 @@ func (r *trieReader) Storage(addr common.Address, key common.Hash) (common.Hash,
|
||||||
if !ok {
|
if !ok {
|
||||||
_, err := r.Account(addr)
|
_, err := r.Account(addr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return common.Hash{}, err
|
return false, common.Hash{}, err
|
||||||
}
|
}
|
||||||
root = r.subRoots[addr]
|
root = r.subRoots[addr]
|
||||||
}
|
}
|
||||||
var err error
|
var err error
|
||||||
tr, err = trie.NewStateTrie(trie.StorageTrieID(r.root, crypto.HashData(r.buff, addr.Bytes()), root), r.db)
|
tr, err = trie.NewStateTrie(trie.StorageTrieID(r.root, crypto.HashData(r.buff, addr.Bytes()), root), r.db)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return common.Hash{}, err
|
return false, common.Hash{}, err
|
||||||
}
|
}
|
||||||
r.subTries[addr] = tr
|
r.subTries[addr] = tr
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ret, err := tr.GetStorage(addr, key.Bytes())
|
exist, ret, err := tr.GetStorage(addr, key.Bytes())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return common.Hash{}, err
|
return false, common.Hash{}, err
|
||||||
}
|
}
|
||||||
value.SetBytes(ret)
|
value.SetBytes(ret)
|
||||||
return value, nil
|
return exist, value, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Copy implements Reader, returning a deep-copied trie reader.
|
// Copy implements Reader, returning a deep-copied trie reader.
|
||||||
|
|
@ -291,16 +293,16 @@ func (r *multiReader) Account(addr common.Address) (*types.StateAccount, error)
|
||||||
// - Returns an empty slot if it does not exist
|
// - Returns an empty slot if it does not exist
|
||||||
// - Returns an error only if an unexpected issue occurs
|
// - Returns an error only if an unexpected issue occurs
|
||||||
// - The returned storage slot is safe to modify after the call
|
// - 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
|
var errs []error
|
||||||
for _, reader := range r.readers {
|
for _, reader := range r.readers {
|
||||||
slot, err := reader.Storage(addr, slot)
|
exist, slot, err := reader.Storage(addr, slot)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
return slot, nil
|
return exist, slot, nil
|
||||||
}
|
}
|
||||||
errs = append(errs, err)
|
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.
|
// 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++
|
s.db.StorageLoaded++
|
||||||
|
|
||||||
start := time.Now()
|
start := time.Now()
|
||||||
value, err := s.db.reader.Storage(s.address, key)
|
_, value, err := s.db.reader.Storage(s.address, key)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
s.db.setError(err)
|
s.db.setError(err)
|
||||||
return common.Hash{}
|
return common.Hash{}
|
||||||
|
|
@ -207,6 +207,37 @@ func (s *stateObject) GetCommittedState(key common.Hash) common.Hash {
|
||||||
return value
|
return value
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// StateExists returns the flag indicating the storage slot is existent or not.
|
||||||
|
func (s *stateObject) StateExists(key common.Hash) bool {
|
||||||
|
// Short circuit if the slot can be found in either dirty map (modified within
|
||||||
|
// the current transaction) or the pending storage (modified within the current
|
||||||
|
// block).
|
||||||
|
//
|
||||||
|
// Note, the slot is regarded as existent even they are marked as deleted,
|
||||||
|
// specifically the value is [32]byte{}, as there is no delete notion in verkle
|
||||||
|
// world anymore and the deletions will be translated to a special marker
|
||||||
|
// writing ultimately.
|
||||||
|
//
|
||||||
|
// Note, there is no account deletion since cancun fork (as for verkle
|
||||||
|
// world), the account destruct checking can be skipped.
|
||||||
|
if _, dirty := s.dirtyStorage[key]; dirty {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if _, pending := s.pendingStorage[key]; pending {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
start := time.Now()
|
||||||
|
exists, _, err := s.db.reader.Storage(s.address, key)
|
||||||
|
if err != nil {
|
||||||
|
s.db.setError(err)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
s.db.StorageLoaded++
|
||||||
|
s.db.StorageReads += time.Since(start)
|
||||||
|
|
||||||
|
return exists
|
||||||
|
}
|
||||||
|
|
||||||
// SetState updates a value in account storage.
|
// SetState updates a value in account storage.
|
||||||
func (s *stateObject) SetState(key, value common.Hash) {
|
func (s *stateObject) SetState(key, value common.Hash) {
|
||||||
// If the new value is the same as old, don't set. Otherwise, track only the
|
// 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/crypto"
|
||||||
"github.com/ethereum/go-ethereum/log"
|
"github.com/ethereum/go-ethereum/log"
|
||||||
"github.com/ethereum/go-ethereum/params"
|
"github.com/ethereum/go-ethereum/params"
|
||||||
|
"github.com/ethereum/go-ethereum/rlp"
|
||||||
"github.com/ethereum/go-ethereum/trie"
|
"github.com/ethereum/go-ethereum/trie"
|
||||||
"github.com/ethereum/go-ethereum/trie/trienode"
|
"github.com/ethereum/go-ethereum/trie/trienode"
|
||||||
"github.com/ethereum/go-ethereum/trie/triestate"
|
"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{}
|
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.
|
// Database retrieves the low level database supporting the lower level trie ops.
|
||||||
func (s *StateDB) Database() Database {
|
func (s *StateDB) Database() Database {
|
||||||
return s.db
|
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 snapshotting is enabled, update the snapshot tree with this new version
|
||||||
if snap := s.db.Snapshot(); snap != nil {
|
if snap := s.db.Snapshot(); snap != nil {
|
||||||
start := time.Now()
|
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)
|
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.
|
// 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.
|
// 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 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.
|
// 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))
|
enc, err := t.trie.Get(t.hashKey(key))
|
||||||
if err != nil || len(enc) == 0 {
|
if err != nil {
|
||||||
return nil, err
|
return false, nil, err
|
||||||
|
}
|
||||||
|
if len(enc) == 0 {
|
||||||
|
return false, nil, nil
|
||||||
}
|
}
|
||||||
_, content, _, err := rlp.Split(enc)
|
_, content, _, err := rlp.Split(enc)
|
||||||
return content, err
|
return true, content, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetAccount attempts to retrieve an account with provided account address.
|
// 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
|
// 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,
|
// 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.
|
// 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)
|
k := utils.StorageSlotKeyWithEvaluatedAddress(t.cache.Get(addr.Bytes()), key)
|
||||||
val, err := t.root.Get(k, t.nodeResolver)
|
val, err := t.root.Get(k, t.nodeResolver)
|
||||||
if err != nil {
|
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.
|
// 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")
|
t.Fatal("account is not matched")
|
||||||
}
|
}
|
||||||
for key, val := range storages[addr] {
|
for key, val := range storages[addr] {
|
||||||
stored, err := tr.GetStorage(addr, key.Bytes())
|
_, stored, err := tr.GetStorage(addr, key.Bytes())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Failed to get storage, %v", err)
|
t.Fatalf("Failed to get storage, %v", err)
|
||||||
}
|
}
|
||||||
|
|
@ -126,7 +126,7 @@ func TestVerkleRollBack(t *testing.T) {
|
||||||
t.Fatal("account is not matched")
|
t.Fatal("account is not matched")
|
||||||
}
|
}
|
||||||
for key, val := range storages[addr] {
|
for key, val := range storages[addr] {
|
||||||
stored, err := tr.GetStorage(addr, key.Bytes())
|
_, stored, err := tr.GetStorage(addr, key.Bytes())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Failed to get storage, %v", err)
|
t.Fatalf("Failed to get storage, %v", err)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue