remove remaining trie access, ForEachStorage is the only method which requires this access pattern and afaict it is only ever used in tests so we can leave it unimplemented
This commit is contained in:
parent
a99694749f
commit
1741e5f790
125
state_object.go
125
state_object.go
@ -6,16 +6,22 @@ import (
|
|||||||
"math/big"
|
"math/big"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/core/state"
|
"github.com/ethereum/go-ethereum/metrics"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
"github.com/ethereum/go-ethereum/metrics"
|
|
||||||
"github.com/ethereum/go-ethereum/rlp"
|
"github.com/ethereum/go-ethereum/rlp"
|
||||||
)
|
)
|
||||||
|
|
||||||
var emptyCodeHash = crypto.Keccak256(nil)
|
var (
|
||||||
|
// emptyRoot is the known root hash of an empty trie.
|
||||||
|
// this is calculated as: emptyRoot = crypto.Keccak256(rlp.Encode([][]byte{}))
|
||||||
|
// that is, the keccak356 hash of the rlp encoding of an empty trie node (empty byte slice array)
|
||||||
|
emptyRoot = common.HexToHash("56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421")
|
||||||
|
// emptyCodeHash is the CodeHash for an EOA, for an account without contract code deployed
|
||||||
|
emptyCodeHash = crypto.Keccak256(nil)
|
||||||
|
)
|
||||||
|
|
||||||
type Code []byte
|
type Code []byte
|
||||||
|
|
||||||
@ -61,8 +67,7 @@ type stateObject struct {
|
|||||||
dbErr error
|
dbErr error
|
||||||
|
|
||||||
// Write caches.
|
// Write caches.
|
||||||
trie state.Trie // storage trie, which becomes non-nil on first access
|
code Code // contract bytecode, which gets set when code is loaded
|
||||||
code Code // contract bytecode, which gets set when code is loaded
|
|
||||||
|
|
||||||
originStorage Storage // Storage cache of original entries to dedup rewrites, reset for every transaction
|
originStorage Storage // Storage cache of original entries to dedup rewrites, reset for every transaction
|
||||||
pendingStorage Storage // Storage entries that need to be flushed to disk, at the end of an entire block
|
pendingStorage Storage // Storage entries that need to be flushed to disk, at the end of an entire block
|
||||||
@ -126,18 +131,6 @@ func (s *stateObject) touch() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *stateObject) getTrie(db Database) state.Trie {
|
|
||||||
if s.trie == nil {
|
|
||||||
var err error
|
|
||||||
s.trie, err = db.OpenStorageTrie(s.addrHash, s.data.Root)
|
|
||||||
if err != nil {
|
|
||||||
s.trie, _ = db.OpenStorageTrie(s.addrHash, common.Hash{})
|
|
||||||
s.setError(fmt.Errorf("can't create storage trie: %v", err))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return s.trie
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetState retrieves a value from the account storage trie.
|
// GetState retrieves a value from the account storage trie.
|
||||||
func (s *stateObject) GetState(db Database, key common.Hash) common.Hash {
|
func (s *stateObject) GetState(db Database, key common.Hash) common.Hash {
|
||||||
// If the fake storage is set, only lookup the state here(in the debugging mode)
|
// If the fake storage is set, only lookup the state here(in the debugging mode)
|
||||||
@ -166,10 +159,16 @@ func (s *stateObject) GetCommittedState(db Database, key common.Hash) common.Has
|
|||||||
if value, cached := s.originStorage[key]; cached {
|
if value, cached := s.originStorage[key]; cached {
|
||||||
return value
|
return value
|
||||||
}
|
}
|
||||||
// If no live objects are available, attempt to use snapshots
|
// If no live objects are available, load from database
|
||||||
var (
|
start := time.Now()
|
||||||
enc []byte
|
enc, err := db.StorageSlot(s.addrHash, key)
|
||||||
)
|
if metrics.EnabledExpensive {
|
||||||
|
s.db.StorageReads += time.Since(start)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
s.setError(err)
|
||||||
|
return common.Hash{}
|
||||||
|
}
|
||||||
var value common.Hash
|
var value common.Hash
|
||||||
if len(enc) > 0 {
|
if len(enc) > 0 {
|
||||||
_, content, _, err := rlp.Split(enc)
|
_, content, _, err := rlp.Split(enc)
|
||||||
@ -207,75 +206,6 @@ func (s *stateObject) setState(key, value common.Hash) {
|
|||||||
s.dirtyStorage[key] = value
|
s.dirtyStorage[key] = value
|
||||||
}
|
}
|
||||||
|
|
||||||
// finalise moves all dirty storage slots into the pending area to be hashed or
|
|
||||||
// committed later. It is invoked at the end of every transaction.
|
|
||||||
func (s *stateObject) finalise() {
|
|
||||||
slotsToPrefetch := make([][]byte, 0, len(s.dirtyStorage))
|
|
||||||
for key, value := range s.dirtyStorage {
|
|
||||||
s.pendingStorage[key] = value
|
|
||||||
if value != s.originStorage[key] {
|
|
||||||
slotsToPrefetch = append(slotsToPrefetch, common.CopyBytes(key[:])) // Copy needed for closure
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if len(s.dirtyStorage) > 0 {
|
|
||||||
s.dirtyStorage = make(Storage)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// updateTrie writes cached storage modifications into the object's storage trie.
|
|
||||||
// It will return nil if the trie has not been loaded and no changes have been made
|
|
||||||
func (s *stateObject) updateTrie(db Database) state.Trie {
|
|
||||||
// Make sure all dirty slots are finalized into the pending storage area
|
|
||||||
s.finalise()
|
|
||||||
if len(s.pendingStorage) == 0 {
|
|
||||||
return s.trie
|
|
||||||
}
|
|
||||||
// Track the amount of time wasted on updating the storage trie
|
|
||||||
if metrics.EnabledExpensive {
|
|
||||||
defer func(start time.Time) { s.db.StorageUpdates += time.Since(start) }(time.Now())
|
|
||||||
}
|
|
||||||
// Insert all the pending updates into the trie
|
|
||||||
tr := s.getTrie(db)
|
|
||||||
|
|
||||||
usedStorage := make([][]byte, 0, len(s.pendingStorage))
|
|
||||||
for key, value := range s.pendingStorage {
|
|
||||||
// Skip noop changes, persist actual changes
|
|
||||||
if value == s.originStorage[key] {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
s.originStorage[key] = value
|
|
||||||
|
|
||||||
var v []byte
|
|
||||||
if (value == common.Hash{}) {
|
|
||||||
s.setError(tr.TryDelete(key[:]))
|
|
||||||
s.db.StorageDeleted += 1
|
|
||||||
} else {
|
|
||||||
// Encoding []byte cannot fail, ok to ignore the error.
|
|
||||||
v, _ = rlp.EncodeToBytes(common.TrimLeftZeroes(value[:]))
|
|
||||||
s.setError(tr.TryUpdate(key[:], v))
|
|
||||||
s.db.StorageUpdated += 1
|
|
||||||
}
|
|
||||||
usedStorage = append(usedStorage, common.CopyBytes(key[:])) // Copy needed for closure
|
|
||||||
}
|
|
||||||
if len(s.pendingStorage) > 0 {
|
|
||||||
s.pendingStorage = make(Storage)
|
|
||||||
}
|
|
||||||
return tr
|
|
||||||
}
|
|
||||||
|
|
||||||
// UpdateRoot sets the trie root to the current root hash of
|
|
||||||
func (s *stateObject) updateRoot(db Database) {
|
|
||||||
// If nothing changed, don't bother with hashing anything
|
|
||||||
if s.updateTrie(db) == nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
// Track the amount of time wasted on hashing the storage trie
|
|
||||||
if metrics.EnabledExpensive {
|
|
||||||
defer func(start time.Time) { s.db.StorageHashes += time.Since(start) }(time.Now())
|
|
||||||
}
|
|
||||||
s.data.Root = s.trie.Hash()
|
|
||||||
}
|
|
||||||
|
|
||||||
// AddBalance adds amount to s's balance.
|
// AddBalance adds amount to s's balance.
|
||||||
// It is used to add funds to the destination account of a transfer.
|
// It is used to add funds to the destination account of a transfer.
|
||||||
func (s *stateObject) AddBalance(amount *big.Int) {
|
func (s *stateObject) AddBalance(amount *big.Int) {
|
||||||
@ -311,21 +241,6 @@ func (s *stateObject) setBalance(amount *big.Int) {
|
|||||||
s.data.Balance = amount
|
s.data.Balance = amount
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *stateObject) deepCopy(db *StateDB) *stateObject {
|
|
||||||
stateObject := newObject(db, s.address, s.data)
|
|
||||||
if s.trie != nil {
|
|
||||||
stateObject.trie = db.db.CopyTrie(s.trie)
|
|
||||||
}
|
|
||||||
stateObject.code = s.code
|
|
||||||
stateObject.dirtyStorage = s.dirtyStorage.Copy()
|
|
||||||
stateObject.originStorage = s.originStorage.Copy()
|
|
||||||
stateObject.pendingStorage = s.pendingStorage.Copy()
|
|
||||||
stateObject.suicided = s.suicided
|
|
||||||
stateObject.dirtyCode = s.dirtyCode
|
|
||||||
stateObject.deleted = s.deleted
|
|
||||||
return stateObject
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
//
|
||||||
// Attribute accessors
|
// Attribute accessors
|
||||||
//
|
//
|
||||||
|
53
statedb.go
53
statedb.go
@ -7,14 +7,11 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/core/state"
|
|
||||||
"github.com/ethereum/go-ethereum/core/state/snapshot"
|
"github.com/ethereum/go-ethereum/core/state/snapshot"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/core/vm"
|
"github.com/ethereum/go-ethereum/core/vm"
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
"github.com/ethereum/go-ethereum/metrics"
|
"github.com/ethereum/go-ethereum/metrics"
|
||||||
"github.com/ethereum/go-ethereum/rlp"
|
|
||||||
"github.com/ethereum/go-ethereum/trie"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -44,20 +41,14 @@ type revision struct {
|
|||||||
journalIndex int
|
journalIndex int
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
|
||||||
// emptyRoot is the known root hash of an empty trie.
|
|
||||||
emptyRoot = common.HexToHash("56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421")
|
|
||||||
)
|
|
||||||
|
|
||||||
// StateDB structs within the ethereum protocol are used to store anything
|
// StateDB structs within the ethereum protocol are used to store anything
|
||||||
// within the merkle trie. StateDBs take care of caching and storing
|
// within the merkle trie. StateDBs take care of caching and storing
|
||||||
// nested states. It's the general query interface to retrieve:
|
// nested states. It's the general query interface to retrieve:
|
||||||
// * Contracts
|
// * Contracts
|
||||||
// * Accounts
|
// * Accounts
|
||||||
type StateDB struct {
|
type StateDB struct {
|
||||||
db Database
|
db Database
|
||||||
trie state.Trie
|
hasher crypto.KeccakState
|
||||||
hasher crypto.KeccakState
|
|
||||||
|
|
||||||
// originalRoot is the pre-state root, before any changes were made.
|
// originalRoot is the pre-state root, before any changes were made.
|
||||||
// It will be updated when the Commit is called.
|
// It will be updated when the Commit is called.
|
||||||
@ -120,13 +111,8 @@ type StateDB struct {
|
|||||||
|
|
||||||
// New creates a new state from a given trie.
|
// New creates a new state from a given trie.
|
||||||
func New(root common.Hash, db Database, snaps *snapshot.Tree) (*StateDB, error) {
|
func New(root common.Hash, db Database, snaps *snapshot.Tree) (*StateDB, error) {
|
||||||
tr, err := db.OpenTrie(root)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
sdb := &StateDB{
|
sdb := &StateDB{
|
||||||
db: db,
|
db: db,
|
||||||
trie: tr,
|
|
||||||
originalRoot: root,
|
originalRoot: root,
|
||||||
snaps: snaps,
|
snaps: snaps,
|
||||||
stateObjects: make(map[common.Address]*stateObject),
|
stateObjects: make(map[common.Address]*stateObject),
|
||||||
@ -362,7 +348,7 @@ func (s *StateDB) getDeletedStateObject(addr common.Address) *stateObject {
|
|||||||
// TODO: REPLACE TRIE ACCESS HERE
|
// TODO: REPLACE TRIE ACCESS HERE
|
||||||
// can add a fallback option to use ipfsethdb to do the trie access if direct access fails
|
// can add a fallback option to use ipfsethdb to do the trie access if direct access fails
|
||||||
start := time.Now()
|
start := time.Now()
|
||||||
data, err := s.trie.TryGetAccount(addr.Bytes())
|
data, err := s.db.StateAccount(addr)
|
||||||
if metrics.EnabledExpensive {
|
if metrics.EnabledExpensive {
|
||||||
s.AccountReads += time.Since(start)
|
s.AccountReads += time.Since(start)
|
||||||
}
|
}
|
||||||
@ -426,35 +412,12 @@ func (s *StateDB) CreateAccount(addr common.Address) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: not sure trie access can be replaced for this method, might need to use ipfs-ethdb
|
// ForEachStorage satisfies vm.StateDB but is not implemented
|
||||||
// but, as far as I can tell, this method is only ever used in tests
|
|
||||||
func (db *StateDB) ForEachStorage(addr common.Address, cb func(key, value common.Hash) bool) error {
|
func (db *StateDB) ForEachStorage(addr common.Address, cb func(key, value common.Hash) bool) error {
|
||||||
so := db.getStateObject(addr)
|
// NOTE: as far as I can tell this method is only ever used in tests
|
||||||
if so == nil {
|
// in that case, we can leave it unimplemented
|
||||||
return nil
|
// or if it needs to be implemented we can use iplfs-ethdb to do normal trie access
|
||||||
}
|
panic("ForEachStorage is not implemented")
|
||||||
it := trie.NewIterator(so.getTrie(db.db).NodeIterator(nil))
|
|
||||||
|
|
||||||
for it.Next() {
|
|
||||||
key := common.BytesToHash(db.trie.GetKey(it.Key))
|
|
||||||
if value, dirty := so.dirtyStorage[key]; dirty {
|
|
||||||
if !cb(key, value) {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(it.Value) > 0 {
|
|
||||||
_, content, _, err := rlp.Split(it.Value)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if !cb(key, common.BytesToHash(content)) {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Snapshot returns an identifier for the current revision of the state.
|
// Snapshot returns an identifier for the current revision of the state.
|
||||||
|
Loading…
Reference in New Issue
Block a user