mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-08-18 18:02:24 +00:00
feat: add state trie p1
This commit is contained in:
parent
7c0ff05685
commit
f1dabaac3a
2 changed files with 681 additions and 20 deletions
447
core/state/proof_state_object.go
Normal file
447
core/state/proof_state_object.go
Normal file
|
|
@ -0,0 +1,447 @@
|
||||||
|
// Copyright 2014 The go-ethereum Authors
|
||||||
|
// This file is part of the go-ethereum library.
|
||||||
|
//
|
||||||
|
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU Lesser General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Lesser General Public License
|
||||||
|
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
package state
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
|
"github.com/ethereum/go-ethereum/log"
|
||||||
|
"github.com/ethereum/go-ethereum/rlp"
|
||||||
|
"github.com/ethereum/go-ethereum/trie/trienode"
|
||||||
|
"github.com/holiman/uint256"
|
||||||
|
)
|
||||||
|
|
||||||
|
// proofStateObject represents a proof commitment which is being modified.
|
||||||
|
//
|
||||||
|
// The usage pattern is as follows:
|
||||||
|
// - First you need to obtain a state object.
|
||||||
|
// - Next, call commit to return the changes.
|
||||||
|
type proofStateObject struct {
|
||||||
|
db *StateDB
|
||||||
|
address common.Address // address of ethereum account
|
||||||
|
addrHash common.Hash // hash of ethereum address of the account
|
||||||
|
origin *common.Hash // Account original data without any change applied, nil means it was not existent
|
||||||
|
data common.Hash // Account data with all mutations applied in the scope of block
|
||||||
|
}
|
||||||
|
|
||||||
|
// empty returns whether the proof data is empty.
|
||||||
|
func (s *proofStateObject) empty() bool {
|
||||||
|
return s.data.Cmp(common.Hash{}) == 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// newObject creates a state object.
|
||||||
|
func newProofStateObject(db *StateDB, address common.Address, data *common.Hash) *proofStateObject {
|
||||||
|
if data == nil {
|
||||||
|
data = &common.Hash{}
|
||||||
|
}
|
||||||
|
return &proofStateObject{
|
||||||
|
db: db,
|
||||||
|
address: address,
|
||||||
|
addrHash: crypto.Keccak256Hash(address[:]),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *proofStateObject) touch() {
|
||||||
|
s.db.proofJournal.touchChange(s.address)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *proofStateObject) GetState(key common.Hash) common.Hash {
|
||||||
|
s.db.ProofLoaded++
|
||||||
|
|
||||||
|
start := time.Now()
|
||||||
|
value, err := s.db.reader.Storage(s.address, key)
|
||||||
|
if err != nil {
|
||||||
|
s.db.setError(err)
|
||||||
|
return common.Hash{}
|
||||||
|
}
|
||||||
|
s.db.ProofReads += time.Since(start)
|
||||||
|
|
||||||
|
// Schedule the resolved storage slots for prefetching if it's enabled.
|
||||||
|
if s.db.proofPrefetcher != nil {
|
||||||
|
if err = s.db.proofPrefetcher.prefetch(s.addrHash, s.data, s.address, nil, []common.Hash{key}, true); err != nil {
|
||||||
|
log.Error("Failed to prefetch storage slot", "addr", s.address, "key", key, "err", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetState updates a value in account storage.
|
||||||
|
// It returns the previous value
|
||||||
|
func (s *proofStateObject) SetState(key, value common.Hash) common.Hash {
|
||||||
|
// If the new value is the same as old, don't set. Otherwise, track only the
|
||||||
|
// dirty changes, supporting reverting all of it back to no change.
|
||||||
|
prev := s.GetState(key)
|
||||||
|
// New value is different, update and journal the change
|
||||||
|
s.db.proofJournal.storageChange(s.address, key, prev, common.Hash{})
|
||||||
|
s.setState(key, value)
|
||||||
|
return prev
|
||||||
|
}
|
||||||
|
|
||||||
|
// setState updates a value in account dirty storage. The dirtiness will be
|
||||||
|
// removed if the value being set equals to the original value.
|
||||||
|
func (s *stateObject) setState(key common.Hash, value common.Hash, origin common.Hash) {
|
||||||
|
// Storage slot is set back to its original value, undo the dirty marker
|
||||||
|
if value == origin {
|
||||||
|
delete(s.dirtyStorage, key)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
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([]common.Hash, 0, len(s.dirtyStorage))
|
||||||
|
for key, value := range s.dirtyStorage {
|
||||||
|
if origin, exist := s.uncommittedStorage[key]; exist && origin == value {
|
||||||
|
// The slot is reverted to its original value, delete the entry
|
||||||
|
// to avoid thrashing the data structures.
|
||||||
|
delete(s.uncommittedStorage, key)
|
||||||
|
} else if exist {
|
||||||
|
// The slot is modified to another value and the slot has been
|
||||||
|
// tracked for commit, do nothing here.
|
||||||
|
} else {
|
||||||
|
// The slot is different from its original value and hasn't been
|
||||||
|
// tracked for commit yet.
|
||||||
|
s.uncommittedStorage[key] = s.GetCommittedState(key)
|
||||||
|
slotsToPrefetch = append(slotsToPrefetch, key) // Copy needed for closure
|
||||||
|
}
|
||||||
|
// Aggregate the dirty storage slots into the pending area. It might
|
||||||
|
// be possible that the value of tracked slot here is same with the
|
||||||
|
// one in originStorage (e.g. the slot was modified in tx_a and then
|
||||||
|
// modified back in tx_b). We can't blindly remove it from pending
|
||||||
|
// map as the dirty slot might have been committed already (before the
|
||||||
|
// byzantium fork) and entry is necessary to modify the value back.
|
||||||
|
s.pendingStorage[key] = value
|
||||||
|
}
|
||||||
|
if s.db.prefetcher != nil && len(slotsToPrefetch) > 0 && s.data.Root != types.EmptyRootHash {
|
||||||
|
if err := s.db.prefetcher.prefetch(s.addrHash, s.data.Root, s.address, nil, slotsToPrefetch, false); err != nil {
|
||||||
|
log.Error("Failed to prefetch slots", "addr", s.address, "slots", len(slotsToPrefetch), "err", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(s.dirtyStorage) > 0 {
|
||||||
|
s.dirtyStorage = make(Storage)
|
||||||
|
}
|
||||||
|
// Revoke the flag at the end of the transaction. It finalizes the status
|
||||||
|
// of the newly-created object as it's no longer eligible for self-destruct
|
||||||
|
// by EIP-6780. For non-newly-created objects, it's a no-op.
|
||||||
|
s.newContract = false
|
||||||
|
}
|
||||||
|
|
||||||
|
// updateTrie is responsible for persisting cached storage changes into the
|
||||||
|
// object's storage trie. In case the storage trie is not yet loaded, this
|
||||||
|
// function will load the trie automatically. If any issues arise during the
|
||||||
|
// loading or updating of the trie, an error will be returned. Furthermore,
|
||||||
|
// this function will return the mutated storage trie, or nil if there is no
|
||||||
|
// storage change at all.
|
||||||
|
//
|
||||||
|
// It assumes all the dirty storage slots have been finalized before.
|
||||||
|
func (s *stateObject) updateTrie() (Trie, error) {
|
||||||
|
// Short circuit if nothing was accessed, don't trigger a prefetcher warning
|
||||||
|
if len(s.uncommittedStorage) == 0 {
|
||||||
|
// Nothing was written, so we could stop early. Unless we have both reads
|
||||||
|
// and witness collection enabled, in which case we need to fetch the trie.
|
||||||
|
if s.db.witness == nil || len(s.originStorage) == 0 {
|
||||||
|
return s.trie, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Retrieve a pretecher populated trie, or fall back to the database. This will
|
||||||
|
// block until all prefetch tasks are done, which are needed for witnesses even
|
||||||
|
// for unmodified state objects.
|
||||||
|
tr := s.getPrefetchedTrie()
|
||||||
|
if tr != nil {
|
||||||
|
// Prefetcher returned a live trie, swap it out for the current one
|
||||||
|
s.trie = tr
|
||||||
|
} else {
|
||||||
|
// Fetcher not running or empty trie, fallback to the database trie
|
||||||
|
var err error
|
||||||
|
tr, err = s.getTrie()
|
||||||
|
if err != nil {
|
||||||
|
s.db.setError(err)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Short circuit if nothing changed, don't bother with hashing anything
|
||||||
|
if len(s.uncommittedStorage) == 0 {
|
||||||
|
return s.trie, nil
|
||||||
|
}
|
||||||
|
// Perform trie updates before deletions. This prevents resolution of unnecessary trie nodes
|
||||||
|
// in circumstances similar to the following:
|
||||||
|
//
|
||||||
|
// Consider nodes `A` and `B` who share the same full node parent `P` and have no other siblings.
|
||||||
|
// During the execution of a block:
|
||||||
|
// - `A` is deleted,
|
||||||
|
// - `C` is created, and also shares the parent `P`.
|
||||||
|
// If the deletion is handled first, then `P` would be left with only one child, thus collapsed
|
||||||
|
// into a shortnode. This requires `B` to be resolved from disk.
|
||||||
|
// Whereas if the created node is handled first, then the collapse is avoided, and `B` is not resolved.
|
||||||
|
var (
|
||||||
|
deletions []common.Hash
|
||||||
|
used = make([]common.Hash, 0, len(s.uncommittedStorage))
|
||||||
|
)
|
||||||
|
for key, origin := range s.uncommittedStorage {
|
||||||
|
// Skip noop changes, persist actual changes
|
||||||
|
value, exist := s.pendingStorage[key]
|
||||||
|
if value == origin {
|
||||||
|
log.Error("Storage update was noop", "address", s.address, "slot", key)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if !exist {
|
||||||
|
log.Error("Storage slot is not found in pending area", s.address, "slot", key)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if (value != common.Hash{}) {
|
||||||
|
if err := tr.UpdateStorage(s.address, key[:], common.TrimLeftZeroes(value[:])); err != nil {
|
||||||
|
s.db.setError(err)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
s.db.StorageUpdated.Add(1)
|
||||||
|
} else {
|
||||||
|
deletions = append(deletions, key)
|
||||||
|
}
|
||||||
|
// Cache the items for preloading
|
||||||
|
used = append(used, key) // Copy needed for closure
|
||||||
|
}
|
||||||
|
for _, key := range deletions {
|
||||||
|
if err := tr.DeleteStorage(s.address, key[:]); err != nil {
|
||||||
|
s.db.setError(err)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
s.db.StorageDeleted.Add(1)
|
||||||
|
}
|
||||||
|
if s.db.prefetcher != nil {
|
||||||
|
s.db.prefetcher.used(s.addrHash, s.data.Root, nil, used)
|
||||||
|
}
|
||||||
|
s.uncommittedStorage = make(Storage) // empties the commit markers
|
||||||
|
return tr, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// updateRoot flushes all cached storage mutations to trie, recalculating the
|
||||||
|
// new storage trie root.
|
||||||
|
func (s *stateObject) updateRoot() {
|
||||||
|
// Flush cached storage mutations into trie, short circuit if any error
|
||||||
|
// is occurred or there is no change in the trie.
|
||||||
|
tr, err := s.updateTrie()
|
||||||
|
if err != nil || tr == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
s.data.Root = tr.Hash()
|
||||||
|
}
|
||||||
|
|
||||||
|
// commitStorage overwrites the clean storage with the storage changes and
|
||||||
|
// fulfills the storage diffs into the given accountUpdate struct.
|
||||||
|
func (s *stateObject) commitStorage(op *accountUpdate) {
|
||||||
|
var (
|
||||||
|
buf = crypto.NewKeccakState()
|
||||||
|
encode = func(val common.Hash) []byte {
|
||||||
|
if val == (common.Hash{}) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
blob, _ := rlp.EncodeToBytes(common.TrimLeftZeroes(val[:]))
|
||||||
|
return blob
|
||||||
|
}
|
||||||
|
)
|
||||||
|
for key, val := range s.pendingStorage {
|
||||||
|
// Skip the noop storage changes, it might be possible the value
|
||||||
|
// of tracked slot is same in originStorage and pendingStorage
|
||||||
|
// map, e.g. the storage slot is modified in tx_a and then reset
|
||||||
|
// back in tx_b.
|
||||||
|
if val == s.originStorage[key] {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
hash := crypto.HashData(buf, key[:])
|
||||||
|
if op.storages == nil {
|
||||||
|
op.storages = make(map[common.Hash][]byte)
|
||||||
|
}
|
||||||
|
op.storages[hash] = encode(val)
|
||||||
|
if op.storagesOrigin == nil {
|
||||||
|
op.storagesOrigin = make(map[common.Hash][]byte)
|
||||||
|
}
|
||||||
|
op.storagesOrigin[hash] = encode(s.originStorage[key])
|
||||||
|
|
||||||
|
// Overwrite the clean value of storage slots
|
||||||
|
s.originStorage[key] = val
|
||||||
|
}
|
||||||
|
s.pendingStorage = make(Storage)
|
||||||
|
}
|
||||||
|
|
||||||
|
// commit obtains the account changes (metadata, storage slots, code) caused by
|
||||||
|
// state execution along with the dirty storage trie nodes.
|
||||||
|
//
|
||||||
|
// Note, commit may run concurrently across all the state objects. Do not assume
|
||||||
|
// thread-safe access to the statedb.
|
||||||
|
func (s *stateObject) commit() (*accountUpdate, *trienode.NodeSet, error) {
|
||||||
|
// commit the account metadata changes
|
||||||
|
op := &accountUpdate{
|
||||||
|
address: s.address,
|
||||||
|
data: types.SlimAccountRLP(s.data),
|
||||||
|
}
|
||||||
|
if s.origin != nil {
|
||||||
|
op.origin = types.SlimAccountRLP(*s.origin)
|
||||||
|
}
|
||||||
|
// commit the contract code if it's modified
|
||||||
|
if s.dirtyCode {
|
||||||
|
op.code = &contractCode{
|
||||||
|
hash: common.BytesToHash(s.CodeHash()),
|
||||||
|
blob: s.code,
|
||||||
|
}
|
||||||
|
s.dirtyCode = false // reset the dirty flag
|
||||||
|
}
|
||||||
|
// Commit storage changes and the associated storage trie
|
||||||
|
s.commitStorage(op)
|
||||||
|
if len(op.storages) == 0 {
|
||||||
|
// nothing changed, don't bother to commit the trie
|
||||||
|
s.origin = s.data.Copy()
|
||||||
|
return op, nil, nil
|
||||||
|
}
|
||||||
|
root, nodes := s.trie.Commit(false)
|
||||||
|
s.data.Root = root
|
||||||
|
s.origin = s.data.Copy()
|
||||||
|
return op, nodes, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddBalance adds amount to s's balance.
|
||||||
|
// It is used to add funds to the destination account of a transfer.
|
||||||
|
// returns the previous balance
|
||||||
|
func (s *stateObject) AddBalance(amount *uint256.Int) uint256.Int {
|
||||||
|
// EIP161: We must check emptiness for the objects such that the account
|
||||||
|
// clearing (0,0,0 objects) can take effect.
|
||||||
|
if amount.IsZero() {
|
||||||
|
if s.empty() {
|
||||||
|
s.touch()
|
||||||
|
}
|
||||||
|
return *(s.Balance())
|
||||||
|
}
|
||||||
|
return s.SetBalance(new(uint256.Int).Add(s.Balance(), amount))
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetBalance sets the balance for the object, and returns the previous balance.
|
||||||
|
func (s *stateObject) SetBalance(amount *uint256.Int) uint256.Int {
|
||||||
|
prev := *s.data.Balance
|
||||||
|
s.db.journal.balanceChange(s.address, s.data.Balance)
|
||||||
|
s.setBalance(amount)
|
||||||
|
return prev
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *stateObject) setBalance(amount *uint256.Int) {
|
||||||
|
s.data.Balance = amount
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *stateObject) deepCopy(db *StateDB) *stateObject {
|
||||||
|
obj := &stateObject{
|
||||||
|
db: db,
|
||||||
|
address: s.address,
|
||||||
|
addrHash: s.addrHash,
|
||||||
|
origin: s.origin,
|
||||||
|
data: s.data,
|
||||||
|
code: s.code,
|
||||||
|
originStorage: s.originStorage.Copy(),
|
||||||
|
pendingStorage: s.pendingStorage.Copy(),
|
||||||
|
dirtyStorage: s.dirtyStorage.Copy(),
|
||||||
|
uncommittedStorage: s.uncommittedStorage.Copy(),
|
||||||
|
dirtyCode: s.dirtyCode,
|
||||||
|
selfDestructed: s.selfDestructed,
|
||||||
|
newContract: s.newContract,
|
||||||
|
}
|
||||||
|
if s.trie != nil {
|
||||||
|
obj.trie = mustCopyTrie(s.trie)
|
||||||
|
}
|
||||||
|
return obj
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Attribute accessors
|
||||||
|
//
|
||||||
|
|
||||||
|
// Address returns the address of the contract/account
|
||||||
|
func (s *stateObject) Address() common.Address {
|
||||||
|
return s.address
|
||||||
|
}
|
||||||
|
|
||||||
|
// Code returns the contract code associated with this object, if any.
|
||||||
|
func (s *stateObject) Code() []byte {
|
||||||
|
if len(s.code) != 0 {
|
||||||
|
return s.code
|
||||||
|
}
|
||||||
|
if bytes.Equal(s.CodeHash(), types.EmptyCodeHash.Bytes()) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
code, err := s.db.db.ContractCode(s.address, common.BytesToHash(s.CodeHash()))
|
||||||
|
if err != nil {
|
||||||
|
s.db.setError(fmt.Errorf("can't load code hash %x: %v", s.CodeHash(), err))
|
||||||
|
}
|
||||||
|
s.code = code
|
||||||
|
return code
|
||||||
|
}
|
||||||
|
|
||||||
|
// CodeSize returns the size of the contract code associated with this object,
|
||||||
|
// or zero if none. This method is an almost mirror of Code, but uses a cache
|
||||||
|
// inside the database to avoid loading codes seen recently.
|
||||||
|
func (s *stateObject) CodeSize() int {
|
||||||
|
if len(s.code) != 0 {
|
||||||
|
return len(s.code)
|
||||||
|
}
|
||||||
|
if bytes.Equal(s.CodeHash(), types.EmptyCodeHash.Bytes()) {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
size, err := s.db.db.ContractCodeSize(s.address, common.BytesToHash(s.CodeHash()))
|
||||||
|
if err != nil {
|
||||||
|
s.db.setError(fmt.Errorf("can't load code size %x: %v", s.CodeHash(), err))
|
||||||
|
}
|
||||||
|
return size
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *stateObject) SetCode(codeHash common.Hash, code []byte) {
|
||||||
|
s.db.journal.setCode(s.address)
|
||||||
|
s.setCode(codeHash, code)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *stateObject) setCode(codeHash common.Hash, code []byte) {
|
||||||
|
s.code = code
|
||||||
|
s.data.CodeHash = codeHash[:]
|
||||||
|
s.dirtyCode = true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *stateObject) SetNonce(nonce uint64) {
|
||||||
|
s.db.journal.nonceChange(s.address, s.data.Nonce)
|
||||||
|
s.setNonce(nonce)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *stateObject) setNonce(nonce uint64) {
|
||||||
|
s.data.Nonce = nonce
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *stateObject) CodeHash() []byte {
|
||||||
|
return s.data.CodeHash
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *stateObject) Balance() *uint256.Int {
|
||||||
|
return s.data.Balance
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *stateObject) Nonce() uint64 {
|
||||||
|
return s.data.Nonce
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *stateObject) Root() common.Hash {
|
||||||
|
return s.data.Root
|
||||||
|
}
|
||||||
|
|
@ -71,25 +71,34 @@ func (m *mutation) isDelete() bool {
|
||||||
//
|
//
|
||||||
// * Contracts
|
// * Contracts
|
||||||
// * Accounts
|
// * Accounts
|
||||||
|
// * Proofs
|
||||||
//
|
//
|
||||||
// Once the state is committed, tries cached in stateDB (including account
|
// Once the state is committed, tries cached in stateDB (including account
|
||||||
// trie, storage tries) will no longer be functional. A new state instance
|
// trie, storage tries) will no longer be functional. A new state instance
|
||||||
// must be created with new root and updated database for accessing post-
|
// must be created with new root and updated database for accessing post-
|
||||||
// commit states.
|
// commit states.
|
||||||
type StateDB struct {
|
type StateDB struct {
|
||||||
db Database
|
db Database
|
||||||
prefetcher *triePrefetcher
|
prefetcher *triePrefetcher
|
||||||
trie Trie
|
trie Trie
|
||||||
reader Reader
|
reader Reader
|
||||||
|
proofPrefetcher *triePrefetcher
|
||||||
|
proofTrie Trie
|
||||||
|
proofReader Reader
|
||||||
|
|
||||||
// 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.
|
||||||
originalRoot common.Hash
|
originalRoot common.Hash
|
||||||
|
// originalProofRoot is the pre-state proof root, before any changes were made.
|
||||||
|
// It will be updated when the Commit is called.
|
||||||
|
originalProofRoot common.Hash
|
||||||
|
|
||||||
// This map holds 'live' objects, which will get modified while
|
// This map holds 'live' objects, which will get modified while
|
||||||
// processing a state transition.
|
// processing a state transition.
|
||||||
stateObjects map[common.Address]*stateObject
|
stateObjects map[common.Address]*stateObject
|
||||||
|
|
||||||
|
proofStateObjects map[common.Address]*stateObject
|
||||||
|
|
||||||
// This map holds 'deleted' objects. An object with the same address
|
// This map holds 'deleted' objects. An object with the same address
|
||||||
// might also occur in the 'stateObjects' map due to account
|
// might also occur in the 'stateObjects' map due to account
|
||||||
// resurrection. The account value is tracked as the original value
|
// resurrection. The account value is tracked as the original value
|
||||||
|
|
@ -97,12 +106,16 @@ type StateDB struct {
|
||||||
// boundaries.
|
// boundaries.
|
||||||
stateObjectsDestruct map[common.Address]*stateObject
|
stateObjectsDestruct map[common.Address]*stateObject
|
||||||
|
|
||||||
|
proofStateObjectsDestruct map[common.Address]*stateObject
|
||||||
|
|
||||||
// This map tracks the account mutations that occurred during the
|
// This map tracks the account mutations that occurred during the
|
||||||
// transition. Uncommitted mutations belonging to the same account
|
// transition. Uncommitted mutations belonging to the same account
|
||||||
// can be merged into a single one which is equivalent from database's
|
// can be merged into a single one which is equivalent from database's
|
||||||
// perspective. This map is populated at the transaction boundaries.
|
// perspective. This map is populated at the transaction boundaries.
|
||||||
mutations map[common.Address]*mutation
|
mutations map[common.Address]*mutation
|
||||||
|
|
||||||
|
proofMutations map[common.Address]*mutation
|
||||||
|
|
||||||
// DB error.
|
// DB error.
|
||||||
// State objects are used by the consensus core and VM which are
|
// State objects are used by the consensus core and VM which are
|
||||||
// unable to deal with database-level errors. Any error that occurs
|
// unable to deal with database-level errors. Any error that occurs
|
||||||
|
|
@ -121,6 +134,9 @@ type StateDB struct {
|
||||||
logs map[common.Hash][]*types.Log
|
logs map[common.Hash][]*types.Log
|
||||||
logSize uint
|
logSize uint
|
||||||
|
|
||||||
|
proofLogs map[common.Hash][]*types.Log
|
||||||
|
proofLogSize uint
|
||||||
|
|
||||||
// Preimages occurred seen by VM in the scope of block.
|
// Preimages occurred seen by VM in the scope of block.
|
||||||
preimages map[common.Hash][]byte
|
preimages map[common.Hash][]byte
|
||||||
|
|
||||||
|
|
@ -135,9 +151,14 @@ type StateDB struct {
|
||||||
// Snapshot and RevertToSnapshot.
|
// Snapshot and RevertToSnapshot.
|
||||||
journal *journal
|
journal *journal
|
||||||
|
|
||||||
|
proofJournal *journal
|
||||||
|
|
||||||
// State witness if cross validation is needed
|
// State witness if cross validation is needed
|
||||||
witness *stateless.Witness
|
witness *stateless.Witness
|
||||||
|
|
||||||
|
// Proof witness if cross validation is needed
|
||||||
|
proofWitness *stateless.Witness
|
||||||
|
|
||||||
// Measurements gathered during execution for debugging purposes
|
// Measurements gathered during execution for debugging purposes
|
||||||
AccountReads time.Duration
|
AccountReads time.Duration
|
||||||
AccountHashes time.Duration
|
AccountHashes time.Duration
|
||||||
|
|
@ -147,6 +168,10 @@ type StateDB struct {
|
||||||
StorageUpdates time.Duration
|
StorageUpdates time.Duration
|
||||||
StorageCommits time.Duration
|
StorageCommits time.Duration
|
||||||
SnapshotCommits time.Duration
|
SnapshotCommits time.Duration
|
||||||
|
ProofReads time.Duration
|
||||||
|
ProofHashes time.Duration
|
||||||
|
ProofUpdates time.Duration
|
||||||
|
ProofCommits time.Duration
|
||||||
TrieDBCommits time.Duration
|
TrieDBCommits time.Duration
|
||||||
|
|
||||||
AccountLoaded int // Number of accounts retrieved from the database during the state transition
|
AccountLoaded int // Number of accounts retrieved from the database during the state transition
|
||||||
|
|
@ -155,10 +180,13 @@ type StateDB struct {
|
||||||
StorageLoaded int // Number of storage slots retrieved from the database during the state transition
|
StorageLoaded int // Number of storage slots retrieved from the database during the state transition
|
||||||
StorageUpdated atomic.Int64 // Number of storage slots updated during the state transition
|
StorageUpdated atomic.Int64 // Number of storage slots updated during the state transition
|
||||||
StorageDeleted atomic.Int64 // Number of storage slots deleted during the state transition
|
StorageDeleted atomic.Int64 // Number of storage slots deleted during the state transition
|
||||||
|
ProofLoaded int // Number of proof nodes retrieved from the database during the state transition
|
||||||
|
ProofUpdated atomic.Int64 // Number of proof nodes updated during the state transition
|
||||||
|
ProofDeleted atomic.Int64 // Number of proof nodes deleted during the state transition
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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) (*StateDB, error) {
|
func New(root common.Hash, proofRoot common.Hash, db Database) (*StateDB, error) {
|
||||||
tr, err := db.OpenTrie(root)
|
tr, err := db.OpenTrie(root)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
@ -167,17 +195,29 @@ func New(root common.Hash, db Database) (*StateDB, error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
proofTrie, err := db.OpenTrie(proofRoot)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
proofReader, err := db.Reader(proofRoot)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
sdb := &StateDB{
|
sdb := &StateDB{
|
||||||
db: db,
|
db: db,
|
||||||
trie: tr,
|
trie: tr,
|
||||||
originalRoot: root,
|
originalRoot: root,
|
||||||
reader: reader,
|
reader: reader,
|
||||||
|
proofTrie: proofTrie,
|
||||||
|
originalProofRoot: proofRoot,
|
||||||
|
proofReader: proofReader,
|
||||||
stateObjects: make(map[common.Address]*stateObject),
|
stateObjects: make(map[common.Address]*stateObject),
|
||||||
stateObjectsDestruct: make(map[common.Address]*stateObject),
|
stateObjectsDestruct: make(map[common.Address]*stateObject),
|
||||||
mutations: make(map[common.Address]*mutation),
|
mutations: make(map[common.Address]*mutation),
|
||||||
logs: make(map[common.Hash][]*types.Log),
|
logs: make(map[common.Hash][]*types.Log),
|
||||||
preimages: make(map[common.Hash][]byte),
|
preimages: make(map[common.Hash][]byte),
|
||||||
journal: newJournal(),
|
journal: newJournal(),
|
||||||
|
proofJournal: newJournal(),
|
||||||
accessList: newAccessList(),
|
accessList: newAccessList(),
|
||||||
transientStorage: newTransientStorage(),
|
transientStorage: newTransientStorage(),
|
||||||
}
|
}
|
||||||
|
|
@ -190,13 +230,15 @@ func New(root common.Hash, db Database) (*StateDB, error) {
|
||||||
// StartPrefetcher initializes a new trie prefetcher to pull in nodes from the
|
// StartPrefetcher initializes a new trie prefetcher to pull in nodes from the
|
||||||
// state trie concurrently while the state is mutated so that when we reach the
|
// state trie concurrently while the state is mutated so that when we reach the
|
||||||
// commit phase, most of the needed data is already hot.
|
// commit phase, most of the needed data is already hot.
|
||||||
func (s *StateDB) StartPrefetcher(namespace string, witness *stateless.Witness) {
|
func (s *StateDB) StartPrefetcher(namespace string, witness *stateless.Witness, proofWitness *stateless.Witness) {
|
||||||
// Terminate any previously running prefetcher
|
// Terminate any previously running prefetcher
|
||||||
s.StopPrefetcher()
|
s.StopPrefetcher()
|
||||||
|
|
||||||
// Enable witness collection if requested
|
// Enable witness collection if requested
|
||||||
s.witness = witness
|
s.witness = witness
|
||||||
|
|
||||||
|
s.proofWitness = proofWitness
|
||||||
|
|
||||||
// With the switch to the Proof-of-Stake consensus algorithm, block production
|
// With the switch to the Proof-of-Stake consensus algorithm, block production
|
||||||
// rewards are now handled at the consensus layer. Consequently, a block may
|
// rewards are now handled at the consensus layer. Consequently, a block may
|
||||||
// have no state transitions if it contains no transactions and no withdrawals.
|
// have no state transitions if it contains no transactions and no withdrawals.
|
||||||
|
|
@ -210,6 +252,11 @@ func (s *StateDB) StartPrefetcher(namespace string, witness *stateless.Witness)
|
||||||
if err := s.prefetcher.prefetch(common.Hash{}, s.originalRoot, common.Address{}, nil, nil, false); err != nil {
|
if err := s.prefetcher.prefetch(common.Hash{}, s.originalRoot, common.Address{}, nil, nil, false); err != nil {
|
||||||
log.Error("Failed to prefetch account trie", "root", s.originalRoot, "err", err)
|
log.Error("Failed to prefetch account trie", "root", s.originalRoot, "err", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
s.proofPrefetcher = newTriePrefetcher(s.db, s.originalProofRoot, namespace, proofWitness == nil)
|
||||||
|
if err := s.proofPrefetcher.prefetch(common.Hash{}, s.originalProofRoot, common.Address{}, nil, nil, false); err != nil {
|
||||||
|
log.Error("Failed to prefetch proof trie", "root", s.originalProofRoot, "err", err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// StopPrefetcher terminates a running prefetcher and reports any leftover stats
|
// StopPrefetcher terminates a running prefetcher and reports any leftover stats
|
||||||
|
|
@ -222,6 +269,14 @@ func (s *StateDB) StopPrefetcher() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *StateDB) StopProofPrefetcher() {
|
||||||
|
if s.proofPrefetcher != nil {
|
||||||
|
s.proofPrefetcher.terminate(false)
|
||||||
|
s.proofPrefetcher.report()
|
||||||
|
s.proofPrefetcher = nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// setError remembers the first non-nil error it is called with.
|
// setError remembers the first non-nil error it is called with.
|
||||||
func (s *StateDB) setError(err error) {
|
func (s *StateDB) setError(err error) {
|
||||||
if s.dbErr == nil {
|
if s.dbErr == nil {
|
||||||
|
|
@ -244,6 +299,16 @@ func (s *StateDB) AddLog(log *types.Log) {
|
||||||
s.logSize++
|
s.logSize++
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *StateDB) AddProofLog(log *types.Log) {
|
||||||
|
s.proofJournal.logChange(s.thash)
|
||||||
|
|
||||||
|
log.TxHash = s.thash
|
||||||
|
log.TxIndex = uint(s.txIndex)
|
||||||
|
log.Index = s.logSize
|
||||||
|
s.proofLogs[s.thash] = append(s.proofLogs[s.thash], log)
|
||||||
|
s.proofLogSize++
|
||||||
|
}
|
||||||
|
|
||||||
// GetLogs returns the logs matching the specified transaction hash, and annotates
|
// GetLogs returns the logs matching the specified transaction hash, and annotates
|
||||||
// them with the given blockNumber and blockHash.
|
// them with the given blockNumber and blockHash.
|
||||||
func (s *StateDB) GetLogs(hash common.Hash, blockNumber uint64, blockHash common.Hash) []*types.Log {
|
func (s *StateDB) GetLogs(hash common.Hash, blockNumber uint64, blockHash common.Hash) []*types.Log {
|
||||||
|
|
@ -255,6 +320,15 @@ func (s *StateDB) GetLogs(hash common.Hash, blockNumber uint64, blockHash common
|
||||||
return logs
|
return logs
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *StateDB) GetProofLogs(hash common.Hash, blockNumber uint64, blockHash common.Hash) []*types.Log {
|
||||||
|
logs := s.proofLogs[hash]
|
||||||
|
for _, l := range logs {
|
||||||
|
l.BlockNumber = blockNumber
|
||||||
|
l.BlockHash = blockHash
|
||||||
|
}
|
||||||
|
return logs
|
||||||
|
}
|
||||||
|
|
||||||
func (s *StateDB) Logs() []*types.Log {
|
func (s *StateDB) Logs() []*types.Log {
|
||||||
var logs []*types.Log
|
var logs []*types.Log
|
||||||
for _, lgs := range s.logs {
|
for _, lgs := range s.logs {
|
||||||
|
|
@ -263,6 +337,14 @@ func (s *StateDB) Logs() []*types.Log {
|
||||||
return logs
|
return logs
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *StateDB) ProofLogs() []*types.Log {
|
||||||
|
var logs []*types.Log
|
||||||
|
for _, lgs := range s.proofLogs {
|
||||||
|
logs = append(logs, lgs...)
|
||||||
|
}
|
||||||
|
return logs
|
||||||
|
}
|
||||||
|
|
||||||
// AddPreimage records a SHA3 preimage seen by the VM.
|
// AddPreimage records a SHA3 preimage seen by the VM.
|
||||||
func (s *StateDB) AddPreimage(hash common.Hash, preimage []byte) {
|
func (s *StateDB) AddPreimage(hash common.Hash, preimage []byte) {
|
||||||
if _, ok := s.preimages[hash]; !ok {
|
if _, ok := s.preimages[hash]; !ok {
|
||||||
|
|
@ -291,12 +373,24 @@ func (s *StateDB) SubRefund(gas uint64) {
|
||||||
s.refund -= gas
|
s.refund -= gas
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *StateDB) SubProofRefund(gas uint64) {
|
||||||
|
s.proofJournal.refundChange(s.refund)
|
||||||
|
if gas > s.refund {
|
||||||
|
panic(fmt.Sprintf("Proof refund counter below zero (gas: %d > refund: %d)", gas, s.refund))
|
||||||
|
}
|
||||||
|
s.refund -= gas
|
||||||
|
}
|
||||||
|
|
||||||
// Exist reports whether the given account address exists in the state.
|
// Exist reports whether the given account address exists in the state.
|
||||||
// Notably this also returns true for self-destructed accounts.
|
// Notably this also returns true for self-destructed accounts.
|
||||||
func (s *StateDB) Exist(addr common.Address) bool {
|
func (s *StateDB) Exist(addr common.Address) bool {
|
||||||
return s.getStateObject(addr) != nil
|
return s.getStateObject(addr) != nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *StateDB) ProofExist(addr common.Address) bool {
|
||||||
|
return s.getStateObject(addr) != nil
|
||||||
|
}
|
||||||
|
|
||||||
// Empty returns whether the state object is either non-existent
|
// Empty returns whether the state object is either non-existent
|
||||||
// or empty according to the EIP161 specification (balance = nonce = code = 0)
|
// or empty according to the EIP161 specification (balance = nonce = code = 0)
|
||||||
func (s *StateDB) Empty(addr common.Address) bool {
|
func (s *StateDB) Empty(addr common.Address) bool {
|
||||||
|
|
@ -304,6 +398,11 @@ func (s *StateDB) Empty(addr common.Address) bool {
|
||||||
return so == nil || so.empty()
|
return so == nil || so.empty()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *StateDB) ProofEmpty(addr common.Address) bool {
|
||||||
|
so := s.getStateObject(addr)
|
||||||
|
return so == nil || so.empty()
|
||||||
|
}
|
||||||
|
|
||||||
// GetBalance retrieves the balance from the given address or 0 if object not found
|
// GetBalance retrieves the balance from the given address or 0 if object not found
|
||||||
func (s *StateDB) GetBalance(addr common.Address) *uint256.Int {
|
func (s *StateDB) GetBalance(addr common.Address) *uint256.Int {
|
||||||
stateObject := s.getStateObject(addr)
|
stateObject := s.getStateObject(addr)
|
||||||
|
|
@ -333,6 +432,24 @@ func (s *StateDB) GetStorageRoot(addr common.Address) common.Hash {
|
||||||
return common.Hash{}
|
return common.Hash{}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *StateDB) GetProofValue(addr common.Address, key common.Hash) common.Hash {
|
||||||
|
stateObject := s.getStateObject(addr)
|
||||||
|
if stateObject != nil {
|
||||||
|
return stateObject.GetProofState(key)
|
||||||
|
}
|
||||||
|
return common.Hash{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetProofRoot retrieves the proof root from the given address or empty
|
||||||
|
// if object not found.
|
||||||
|
func (s *StateDB) GetProofRoot(addr common.Address) common.Hash {
|
||||||
|
stateObject := s.getStateObject(addr)
|
||||||
|
if stateObject != nil {
|
||||||
|
return stateObject.Root()
|
||||||
|
}
|
||||||
|
return common.Hash{}
|
||||||
|
}
|
||||||
|
|
||||||
// TxIndex returns the current transaction index set by SetTxContext.
|
// TxIndex returns the current transaction index set by SetTxContext.
|
||||||
func (s *StateDB) TxIndex() int {
|
func (s *StateDB) TxIndex() int {
|
||||||
return s.txIndex
|
return s.txIndex
|
||||||
|
|
@ -558,6 +675,17 @@ func (s *StateDB) updateStateObject(obj *stateObject) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// updateProofStateObject writes the given object to the proof trie.
|
||||||
|
func (s *StateDB) updateProofStateObject(obj *stateObject) {
|
||||||
|
addr := obj.Address()
|
||||||
|
if err := s.proofTrie.UpdateAccount(addr, &obj.data, len(obj.code)); err != nil {
|
||||||
|
s.setError(fmt.Errorf("updateProofStateObject (%x) error: %v", addr[:], err))
|
||||||
|
}
|
||||||
|
if obj.dirtyCode {
|
||||||
|
s.proofTrie.UpdateContractCode(obj.Address(), common.BytesToHash(obj.CodeHash()), obj.code)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// deleteStateObject removes the given object from the state trie.
|
// deleteStateObject removes the given object from the state trie.
|
||||||
func (s *StateDB) deleteStateObject(addr common.Address) {
|
func (s *StateDB) deleteStateObject(addr common.Address) {
|
||||||
if err := s.trie.DeleteAccount(addr); err != nil {
|
if err := s.trie.DeleteAccount(addr); err != nil {
|
||||||
|
|
@ -565,6 +693,13 @@ func (s *StateDB) deleteStateObject(addr common.Address) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// deleteProofStateObject removes the given object from the proof trie.
|
||||||
|
func (s *StateDB) deleteProofStateObject(addr common.Address) {
|
||||||
|
if err := s.proofTrie.DeleteAccount(addr); err != nil {
|
||||||
|
s.setError(fmt.Errorf("deleteProofStateObject (%x) error: %v", addr[:], err))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// getStateObject retrieves a state object given by the address, returning nil if
|
// getStateObject retrieves a state object given by the address, returning nil if
|
||||||
// the object is not found or was deleted in this execution context.
|
// the object is not found or was deleted in this execution context.
|
||||||
func (s *StateDB) getStateObject(addr common.Address) *stateObject {
|
func (s *StateDB) getStateObject(addr common.Address) *stateObject {
|
||||||
|
|
@ -603,10 +738,52 @@ func (s *StateDB) getStateObject(addr common.Address) *stateObject {
|
||||||
return obj
|
return obj
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// getProofStateObject retrieves a proof state object given by the address, returning nil if
|
||||||
|
// the object is not found or was deleted in this execution context.
|
||||||
|
func (s *StateDB) getProofStateObject(addr common.Address) *stateObject {
|
||||||
|
// Prefer live objects if any is available
|
||||||
|
if obj := s.proofStateObjects[addr]; obj != nil {
|
||||||
|
return obj
|
||||||
|
}
|
||||||
|
// Short circuit if the account is already destructed in this block.
|
||||||
|
if _, ok := s.proofStateObjectsDestruct[addr]; ok {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
s.ProofLoaded++
|
||||||
|
|
||||||
|
start := time.Now()
|
||||||
|
acct, err := s.proofReader.Account(addr)
|
||||||
|
if err != nil {
|
||||||
|
s.setError(fmt.Errorf("getProofStateObject (%x) error: %w", addr.Bytes(), err))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
s.ProofReads += time.Since(start)
|
||||||
|
|
||||||
|
// Short circuit if the account is not found
|
||||||
|
if acct == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
// Schedule the resolved account for prefetching if it's enabled.
|
||||||
|
if s.proofPrefetcher != nil {
|
||||||
|
if err = s.proofPrefetcher.prefetch(common.Hash{}, s.originalProofRoot, common.Address{}, []common.Address{addr}, nil, true); err != nil {
|
||||||
|
log.Error("Failed to prefetch account", "addr", addr, "err", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Insert into the live set
|
||||||
|
obj := newObject(s, addr, acct)
|
||||||
|
s.setProofStateObject(obj)
|
||||||
|
s.ProofLoaded++
|
||||||
|
return obj
|
||||||
|
}
|
||||||
|
|
||||||
func (s *StateDB) setStateObject(object *stateObject) {
|
func (s *StateDB) setStateObject(object *stateObject) {
|
||||||
s.stateObjects[object.Address()] = object
|
s.stateObjects[object.Address()] = object
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *StateDB) setProofStateObject(object *stateObject) {
|
||||||
|
s.proofStateObjects[object.Address()] = object
|
||||||
|
}
|
||||||
|
|
||||||
// getOrNewStateObject retrieves a state object or create a new state object if nil.
|
// getOrNewStateObject retrieves a state object or create a new state object if nil.
|
||||||
func (s *StateDB) getOrNewStateObject(addr common.Address) *stateObject {
|
func (s *StateDB) getOrNewStateObject(addr common.Address) *stateObject {
|
||||||
obj := s.getStateObject(addr)
|
obj := s.getStateObject(addr)
|
||||||
|
|
@ -616,6 +793,14 @@ func (s *StateDB) getOrNewStateObject(addr common.Address) *stateObject {
|
||||||
return obj
|
return obj
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *StateDB) getOrNewProofStateObject(addr common.Address) *stateObject {
|
||||||
|
obj := s.getProofStateObject(addr)
|
||||||
|
if obj == nil {
|
||||||
|
obj = s.createProofObject(addr)
|
||||||
|
}
|
||||||
|
return obj
|
||||||
|
}
|
||||||
|
|
||||||
// createObject creates a new state object. The assumption is held there is no
|
// createObject creates a new state object. The assumption is held there is no
|
||||||
// existing account with the given address, otherwise it will be silently overwritten.
|
// existing account with the given address, otherwise it will be silently overwritten.
|
||||||
func (s *StateDB) createObject(addr common.Address) *stateObject {
|
func (s *StateDB) createObject(addr common.Address) *stateObject {
|
||||||
|
|
@ -625,6 +810,13 @@ func (s *StateDB) createObject(addr common.Address) *stateObject {
|
||||||
return obj
|
return obj
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *StateDB) createProofObject(addr common.Address) *stateObject {
|
||||||
|
obj := newObject(s, addr, nil)
|
||||||
|
s.journal.createObject(addr)
|
||||||
|
s.setProofStateObject(obj)
|
||||||
|
return obj
|
||||||
|
}
|
||||||
|
|
||||||
// CreateAccount explicitly creates a new state object, assuming that the
|
// CreateAccount explicitly creates a new state object, assuming that the
|
||||||
// account did not previously exist in the state. If the account already
|
// account did not previously exist in the state. If the account already
|
||||||
// exists, this function will silently overwrite it which might lead to a
|
// exists, this function will silently overwrite it which might lead to a
|
||||||
|
|
@ -633,6 +825,10 @@ func (s *StateDB) CreateAccount(addr common.Address) {
|
||||||
s.createObject(addr)
|
s.createObject(addr)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *StateDB) CreateProofAccount(addr common.Address) {
|
||||||
|
s.createProofObject(addr)
|
||||||
|
}
|
||||||
|
|
||||||
// CreateContract is used whenever a contract is created. This may be preceded
|
// CreateContract is used whenever a contract is created. This may be preceded
|
||||||
// by CreateAccount, but that is not required if it already existed in the
|
// by CreateAccount, but that is not required if it already existed in the
|
||||||
// state due to funds sent beforehand.
|
// state due to funds sent beforehand.
|
||||||
|
|
@ -651,20 +847,26 @@ func (s *StateDB) CreateContract(addr common.Address) {
|
||||||
func (s *StateDB) Copy() *StateDB {
|
func (s *StateDB) Copy() *StateDB {
|
||||||
// Copy all the basic fields, initialize the memory ones
|
// Copy all the basic fields, initialize the memory ones
|
||||||
state := &StateDB{
|
state := &StateDB{
|
||||||
db: s.db,
|
db: s.db,
|
||||||
trie: mustCopyTrie(s.trie),
|
trie: mustCopyTrie(s.trie),
|
||||||
reader: s.reader.Copy(),
|
reader: s.reader.Copy(),
|
||||||
originalRoot: s.originalRoot,
|
originalRoot: s.originalRoot,
|
||||||
stateObjects: make(map[common.Address]*stateObject, len(s.stateObjects)),
|
stateObjects: make(map[common.Address]*stateObject, len(s.stateObjects)),
|
||||||
stateObjectsDestruct: make(map[common.Address]*stateObject, len(s.stateObjectsDestruct)),
|
stateObjectsDestruct: make(map[common.Address]*stateObject, len(s.stateObjectsDestruct)),
|
||||||
mutations: make(map[common.Address]*mutation, len(s.mutations)),
|
mutations: make(map[common.Address]*mutation, len(s.mutations)),
|
||||||
dbErr: s.dbErr,
|
proofTrie: mustCopyTrie(s.proofTrie),
|
||||||
refund: s.refund,
|
proofReader: s.proofReader.Copy(),
|
||||||
thash: s.thash,
|
originalProofRoot: s.originalProofRoot,
|
||||||
txIndex: s.txIndex,
|
proofStateObjects: make(map[common.Address]*stateObject, len(s.proofStateObjects)),
|
||||||
logs: make(map[common.Hash][]*types.Log, len(s.logs)),
|
proofStateObjectsDestruct: make(map[common.Address]*stateObject, len(s.proofStateObjectsDestruct)),
|
||||||
logSize: s.logSize,
|
proofMutations: make(map[common.Address]*mutation, len(s.proofMutations)),
|
||||||
preimages: maps.Clone(s.preimages),
|
dbErr: s.dbErr,
|
||||||
|
refund: s.refund,
|
||||||
|
thash: s.thash,
|
||||||
|
txIndex: s.txIndex,
|
||||||
|
logs: make(map[common.Hash][]*types.Log, len(s.logs)),
|
||||||
|
logSize: s.logSize,
|
||||||
|
preimages: maps.Clone(s.preimages),
|
||||||
|
|
||||||
// Do we need to copy the access list and transient storage?
|
// Do we need to copy the access list and transient storage?
|
||||||
// In practice: No. At the start of a transaction, these two lists are empty.
|
// In practice: No. At the start of a transaction, these two lists are empty.
|
||||||
|
|
@ -679,6 +881,9 @@ func (s *StateDB) Copy() *StateDB {
|
||||||
if s.witness != nil {
|
if s.witness != nil {
|
||||||
state.witness = s.witness.Copy()
|
state.witness = s.witness.Copy()
|
||||||
}
|
}
|
||||||
|
if s.proofWitness != nil {
|
||||||
|
state.proofWitness = s.proofWitness.Copy()
|
||||||
|
}
|
||||||
if s.accessEvents != nil {
|
if s.accessEvents != nil {
|
||||||
state.accessEvents = s.accessEvents.Copy()
|
state.accessEvents = s.accessEvents.Copy()
|
||||||
}
|
}
|
||||||
|
|
@ -686,14 +891,23 @@ func (s *StateDB) Copy() *StateDB {
|
||||||
for addr, obj := range s.stateObjects {
|
for addr, obj := range s.stateObjects {
|
||||||
state.stateObjects[addr] = obj.deepCopy(state)
|
state.stateObjects[addr] = obj.deepCopy(state)
|
||||||
}
|
}
|
||||||
|
for addr, obj := range s.proofStateObjects {
|
||||||
|
state.proofStateObjects[addr] = obj.deepCopy(state)
|
||||||
|
}
|
||||||
// Deep copy destructed state objects.
|
// Deep copy destructed state objects.
|
||||||
for addr, obj := range s.stateObjectsDestruct {
|
for addr, obj := range s.stateObjectsDestruct {
|
||||||
state.stateObjectsDestruct[addr] = obj.deepCopy(state)
|
state.stateObjectsDestruct[addr] = obj.deepCopy(state)
|
||||||
}
|
}
|
||||||
|
for addr, obj := range s.proofStateObjectsDestruct {
|
||||||
|
state.proofStateObjectsDestruct[addr] = obj.deepCopy(state)
|
||||||
|
}
|
||||||
// Deep copy the object state markers.
|
// Deep copy the object state markers.
|
||||||
for addr, op := range s.mutations {
|
for addr, op := range s.mutations {
|
||||||
state.mutations[addr] = op.copy()
|
state.mutations[addr] = op.copy()
|
||||||
}
|
}
|
||||||
|
for addr, op := range s.proofMutations {
|
||||||
|
state.proofMutations[addr] = op.copy()
|
||||||
|
}
|
||||||
// Deep copy the logs occurred in the scope of block
|
// Deep copy the logs occurred in the scope of block
|
||||||
for hash, logs := range s.logs {
|
for hash, logs := range s.logs {
|
||||||
cpy := make([]*types.Log, len(logs))
|
cpy := make([]*types.Log, len(logs))
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue