aboutsummaryrefslogtreecommitdiff
path: root/core/state/statedb.go
diff options
context:
space:
mode:
Diffstat (limited to 'core/state/statedb.go')
-rw-r--r--core/state/statedb.go793
1 files changed, 793 insertions, 0 deletions
diff --git a/core/state/statedb.go b/core/state/statedb.go
new file mode 100644
index 0000000..12deebe
--- /dev/null
+++ b/core/state/statedb.go
@@ -0,0 +1,793 @@
+// 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 provides a caching layer atop the Ethereum state trie.
+package state
+
+import (
+ "errors"
+ "fmt"
+ "math/big"
+ "sort"
+ "time"
+
+ "github.com/ava-labs/coreth/core/types"
+ "github.com/ava-labs/go-ethereum/common"
+ "github.com/ava-labs/go-ethereum/crypto"
+ "github.com/ava-labs/go-ethereum/log"
+ "github.com/ava-labs/go-ethereum/metrics"
+ "github.com/ava-labs/go-ethereum/rlp"
+ "github.com/ava-labs/go-ethereum/trie"
+)
+
+type revision struct {
+ id int
+ journalIndex int
+}
+
+var (
+ // emptyRoot is the known root hash of an empty trie.
+ emptyRoot = common.HexToHash("56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421")
+
+ // emptyCode is the known hash of the empty EVM bytecode.
+ emptyCode = crypto.Keccak256Hash(nil)
+)
+
+type proofList [][]byte
+
+func (n *proofList) Put(key []byte, value []byte) error {
+ *n = append(*n, value)
+ return nil
+}
+
+func (n *proofList) Delete(key []byte) error {
+ panic("not supported")
+}
+
+// StateDBs within the ethereum protocol are used to store anything
+// within the merkle trie. StateDBs take care of caching and storing
+// nested states. It's the general query interface to retrieve:
+// * Contracts
+// * Accounts
+type StateDB struct {
+ db Database
+ trie Trie
+
+ // This map holds 'live' objects, which will get modified while processing a state transition.
+ stateObjects map[common.Address]*stateObject
+ stateObjectsDirty map[common.Address]struct{}
+
+ // DB error.
+ // State objects are used by the consensus core and VM which are
+ // unable to deal with database-level errors. Any error that occurs
+ // during a database read is memoized here and will eventually be returned
+ // by StateDB.Commit.
+ dbErr error
+
+ // The refund counter, also used by state transitioning.
+ refund uint64
+
+ thash, bhash common.Hash
+ txIndex int
+ logs map[common.Hash][]*types.Log
+ logSize uint
+
+ preimages map[common.Hash][]byte
+
+ // Journal of state modifications. This is the backbone of
+ // Snapshot and RevertToSnapshot.
+ journal *journal
+ validRevisions []revision
+ nextRevisionId int
+
+ // Measurements gathered during execution for debugging purposes
+ AccountReads time.Duration
+ AccountHashes time.Duration
+ AccountUpdates time.Duration
+ AccountCommits time.Duration
+ StorageReads time.Duration
+ StorageHashes time.Duration
+ StorageUpdates time.Duration
+ StorageCommits time.Duration
+}
+
+// Create a new state from a given trie.
+func New(root common.Hash, db Database) (*StateDB, error) {
+ tr, err := db.OpenTrie(root)
+ if err != nil {
+ return nil, err
+ }
+ return &StateDB{
+ db: db,
+ trie: tr,
+ stateObjects: make(map[common.Address]*stateObject),
+ stateObjectsDirty: make(map[common.Address]struct{}),
+ logs: make(map[common.Hash][]*types.Log),
+ preimages: make(map[common.Hash][]byte),
+ journal: newJournal(),
+ }, nil
+}
+
+// setError remembers the first non-nil error it is called with.
+func (self *StateDB) setError(err error) {
+ if self.dbErr == nil {
+ self.dbErr = err
+ }
+}
+
+func (self *StateDB) Error() error {
+ return self.dbErr
+}
+
+// Reset clears out all ephemeral state objects from the state db, but keeps
+// the underlying state trie to avoid reloading data for the next operations.
+func (self *StateDB) Reset(root common.Hash) error {
+ tr, err := self.db.OpenTrie(root)
+ if err != nil {
+ return err
+ }
+ self.trie = tr
+ self.stateObjects = make(map[common.Address]*stateObject)
+ self.stateObjectsDirty = make(map[common.Address]struct{})
+ self.thash = common.Hash{}
+ self.bhash = common.Hash{}
+ self.txIndex = 0
+ self.logs = make(map[common.Hash][]*types.Log)
+ self.logSize = 0
+ self.preimages = make(map[common.Hash][]byte)
+ self.clearJournalAndRefund()
+ return nil
+}
+
+func (self *StateDB) AddLog(log *types.Log) {
+ self.journal.append(addLogChange{txhash: self.thash})
+
+ log.TxHash = self.thash
+ log.BlockHash = self.bhash
+ log.TxIndex = uint(self.txIndex)
+ log.Index = self.logSize
+ self.logs[self.thash] = append(self.logs[self.thash], log)
+ self.logSize++
+}
+
+func (self *StateDB) GetLogs(hash common.Hash) []*types.Log {
+ return self.logs[hash]
+}
+
+func (self *StateDB) Logs() []*types.Log {
+ var logs []*types.Log
+ for _, lgs := range self.logs {
+ logs = append(logs, lgs...)
+ }
+ return logs
+}
+
+// AddPreimage records a SHA3 preimage seen by the VM.
+func (self *StateDB) AddPreimage(hash common.Hash, preimage []byte) {
+ if _, ok := self.preimages[hash]; !ok {
+ self.journal.append(addPreimageChange{hash: hash})
+ pi := make([]byte, len(preimage))
+ copy(pi, preimage)
+ self.preimages[hash] = pi
+ }
+}
+
+// Preimages returns a list of SHA3 preimages that have been submitted.
+func (self *StateDB) Preimages() map[common.Hash][]byte {
+ return self.preimages
+}
+
+// AddRefund adds gas to the refund counter
+func (self *StateDB) AddRefund(gas uint64) {
+ self.journal.append(refundChange{prev: self.refund})
+ self.refund += gas
+}
+
+// SubRefund removes gas from the refund counter.
+// This method will panic if the refund counter goes below zero
+func (self *StateDB) SubRefund(gas uint64) {
+ self.journal.append(refundChange{prev: self.refund})
+ if gas > self.refund {
+ panic("Refund counter below zero")
+ }
+ self.refund -= gas
+}
+
+// Exist reports whether the given account address exists in the state.
+// Notably this also returns true for suicided accounts.
+func (self *StateDB) Exist(addr common.Address) bool {
+ return self.getStateObject(addr) != nil
+}
+
+// Empty returns whether the state object is either non-existent
+// or empty according to the EIP161 specification (balance = nonce = code = 0)
+func (self *StateDB) Empty(addr common.Address) bool {
+ so := self.getStateObject(addr)
+ return so == nil || so.empty()
+}
+
+// Retrieve the balance from the given address or 0 if object not found
+func (self *StateDB) GetBalance(addr common.Address) *big.Int {
+ stateObject := self.getStateObject(addr)
+ if stateObject != nil {
+ return stateObject.Balance()
+ }
+ return common.Big0
+}
+
+// Retrieve the balance from the given address or 0 if object not found
+func (self *StateDB) GetBalanceMultiCoin(addr common.Address, coinID common.Hash) *big.Int {
+ stateObject := self.getStateObject(addr)
+ if stateObject != nil {
+ return stateObject.BalanceMultiCoin(coinID, self.db)
+ }
+ return common.Big0
+}
+
+func (self *StateDB) EnableMultiCoin(addr common.Address) error {
+ stateObject := self.GetOrNewStateObject(addr)
+ if stateObject.data.Root != emptyRoot {
+ return errors.New("not a fresh account")
+ }
+ if !stateObject.EnableMultiCoin() {
+ return errors.New("multi-coin mode already enabled")
+ }
+ return nil
+}
+
+func (self *StateDB) IsMultiCoin(addr common.Address) bool {
+ stateObject := self.getStateObject(addr)
+ if stateObject != nil {
+ return stateObject.IsMultiCoin()
+ }
+ return false
+}
+
+func (self *StateDB) GetNonce(addr common.Address) uint64 {
+ stateObject := self.getStateObject(addr)
+ if stateObject != nil {
+ return stateObject.Nonce()
+ }
+
+ return 0
+}
+
+// TxIndex returns the current transaction index set by Prepare.
+func (self *StateDB) TxIndex() int {
+ return self.txIndex
+}
+
+// BlockHash returns the current block hash set by Prepare.
+func (self *StateDB) BlockHash() common.Hash {
+ return self.bhash
+}
+
+func (self *StateDB) GetCode(addr common.Address) []byte {
+ stateObject := self.getStateObject(addr)
+ if stateObject != nil {
+ return stateObject.Code(self.db)
+ }
+ return nil
+}
+
+func (self *StateDB) GetCodeSize(addr common.Address) int {
+ stateObject := self.getStateObject(addr)
+ if stateObject == nil {
+ return 0
+ }
+ if stateObject.code != nil {
+ return len(stateObject.code)
+ }
+ size, err := self.db.ContractCodeSize(stateObject.addrHash, common.BytesToHash(stateObject.CodeHash()))
+ if err != nil {
+ self.setError(err)
+ }
+ return size
+}
+
+func (self *StateDB) GetCodeHash(addr common.Address) common.Hash {
+ stateObject := self.getStateObject(addr)
+ if stateObject == nil {
+ return common.Hash{}
+ }
+ return common.BytesToHash(stateObject.CodeHash())
+}
+
+// GetState retrieves a value from the given account's storage trie.
+func (self *StateDB) GetState(addr common.Address, hash common.Hash) common.Hash {
+ stateObject := self.getStateObject(addr)
+ if stateObject != nil {
+ return stateObject.GetState(self.db, hash)
+ }
+ return common.Hash{}
+}
+
+// GetProof returns the MerkleProof for a given Account
+func (self *StateDB) GetProof(a common.Address) ([][]byte, error) {
+ var proof proofList
+ err := self.trie.Prove(crypto.Keccak256(a.Bytes()), 0, &proof)
+ return [][]byte(proof), err
+}
+
+// GetProof returns the StorageProof for given key
+func (self *StateDB) GetStorageProof(a common.Address, key common.Hash) ([][]byte, error) {
+ var proof proofList
+ trie := self.StorageTrie(a)
+ if trie == nil {
+ return proof, errors.New("storage trie for requested address does not exist")
+ }
+ err := trie.Prove(crypto.Keccak256(key.Bytes()), 0, &proof)
+ return [][]byte(proof), err
+}
+
+// GetCommittedState retrieves a value from the given account's committed storage trie.
+func (self *StateDB) GetCommittedState(addr common.Address, hash common.Hash) common.Hash {
+ stateObject := self.getStateObject(addr)
+ if stateObject != nil {
+ return stateObject.GetCommittedState(self.db, hash)
+ }
+ return common.Hash{}
+}
+
+// Database retrieves the low level database supporting the lower level trie ops.
+func (self *StateDB) Database() Database {
+ return self.db
+}
+
+// StorageTrie returns the storage trie of an account.
+// The return value is a copy and is nil for non-existent accounts.
+func (self *StateDB) StorageTrie(addr common.Address) Trie {
+ stateObject := self.getStateObject(addr)
+ if stateObject == nil {
+ return nil
+ }
+ cpy := stateObject.deepCopy(self)
+ return cpy.updateTrie(self.db)
+}
+
+func (self *StateDB) HasSuicided(addr common.Address) bool {
+ stateObject := self.getStateObject(addr)
+ if stateObject != nil {
+ return stateObject.suicided
+ }
+ return false
+}
+
+/*
+ * SETTERS
+ */
+
+// AddBalance adds amount to the account associated with addr.
+func (self *StateDB) AddBalance(addr common.Address, amount *big.Int) {
+ stateObject := self.GetOrNewStateObject(addr)
+ if stateObject != nil {
+ stateObject.AddBalance(amount)
+ }
+}
+
+// SubBalance subtracts amount from the account associated with addr.
+func (self *StateDB) SubBalance(addr common.Address, amount *big.Int) {
+ stateObject := self.GetOrNewStateObject(addr)
+ if stateObject != nil {
+ stateObject.SubBalance(amount)
+ }
+}
+
+func (self *StateDB) SetBalance(addr common.Address, amount *big.Int) {
+ stateObject := self.GetOrNewStateObject(addr)
+ if stateObject != nil {
+ stateObject.SetBalance(amount)
+ }
+}
+
+// AddBalance adds amount to the account associated with addr.
+func (self *StateDB) AddBalanceMultiCoin(addr common.Address, coinID common.Hash, amount *big.Int) {
+ stateObject := self.GetOrNewStateObject(addr)
+ if stateObject != nil {
+ stateObject.AddBalanceMultiCoin(coinID, amount, self.db)
+ }
+}
+
+// SubBalance subtracts amount from the account associated with addr.
+func (self *StateDB) SubBalanceMultiCoin(addr common.Address, coinID common.Hash, amount *big.Int) {
+ stateObject := self.GetOrNewStateObject(addr)
+ if stateObject != nil {
+ stateObject.SubBalanceMultiCoin(coinID, amount, self.db)
+ }
+}
+
+func (self *StateDB) SetBalanceMultiCoin(addr common.Address, coinID common.Hash, amount *big.Int) {
+ stateObject := self.GetOrNewStateObject(addr)
+ if stateObject != nil {
+ stateObject.SetBalanceMultiCoin(coinID, amount, self.db)
+ }
+}
+
+func (self *StateDB) SetNonce(addr common.Address, nonce uint64) {
+ stateObject := self.GetOrNewStateObject(addr)
+ if stateObject != nil {
+ stateObject.SetNonce(nonce)
+ }
+}
+
+func (self *StateDB) SetCode(addr common.Address, code []byte) {
+ stateObject := self.GetOrNewStateObject(addr)
+ if stateObject != nil {
+ stateObject.SetCode(crypto.Keccak256Hash(code), code)
+ }
+}
+
+func (self *StateDB) SetState(addr common.Address, key, value common.Hash) (res error) {
+ res = nil
+ stateObject := self.GetOrNewStateObject(addr)
+ if stateObject != nil {
+ if stateObject.data.IsMultiCoin {
+ NormalizeStateKey(&key)
+ }
+ stateObject.SetState(self.db, key, value)
+ }
+ return
+}
+
+// SetStorage replaces the entire storage for the specified account with given
+// storage. This function should only be used for debugging.
+func (self *StateDB) SetStorage(addr common.Address, storage map[common.Hash]common.Hash) {
+ stateObject := self.GetOrNewStateObject(addr)
+ if stateObject != nil {
+ stateObject.SetStorage(storage)
+ }
+}
+
+// Suicide marks the given account as suicided.
+// This clears the account balance.
+//
+// The account's state object is still available until the state is committed,
+// getStateObject will return a non-nil account after Suicide.
+func (self *StateDB) Suicide(addr common.Address) bool {
+ stateObject := self.getStateObject(addr)
+ if stateObject == nil {
+ return false
+ }
+ self.journal.append(suicideChange{
+ account: &addr,
+ prev: stateObject.suicided,
+ prevbalance: new(big.Int).Set(stateObject.Balance()),
+ })
+ stateObject.markSuicided()
+ stateObject.data.Balance = new(big.Int)
+
+ return true
+}
+
+//
+// Setting, updating & deleting state object methods.
+//
+
+// updateStateObject writes the given object to the trie.
+func (s *StateDB) updateStateObject(stateObject *stateObject) {
+ // Track the amount of time wasted on updating the account from the trie
+ if metrics.EnabledExpensive {
+ defer func(start time.Time) { s.AccountUpdates += time.Since(start) }(time.Now())
+ }
+ // Encode the account and update the account trie
+ addr := stateObject.Address()
+
+ data, err := rlp.EncodeToBytes(stateObject)
+ if err != nil {
+ panic(fmt.Errorf("can't encode object at %x: %v", addr[:], err))
+ }
+ s.setError(s.trie.TryUpdate(addr[:], data))
+}
+
+// deleteStateObject removes the given object from the state trie.
+func (s *StateDB) deleteStateObject(stateObject *stateObject) {
+ // Track the amount of time wasted on deleting the account from the trie
+ if metrics.EnabledExpensive {
+ defer func(start time.Time) { s.AccountUpdates += time.Since(start) }(time.Now())
+ }
+ // Delete the account from the trie
+ stateObject.deleted = true
+
+ addr := stateObject.Address()
+ s.setError(s.trie.TryDelete(addr[:]))
+}
+
+// Retrieve a state object given by the address. Returns nil if not found.
+func (s *StateDB) getStateObject(addr common.Address) (stateObject *stateObject) {
+ // Prefer live objects
+ if obj := s.stateObjects[addr]; obj != nil {
+ if obj.deleted {
+ return nil
+ }
+ return obj
+ }
+ // Track the amount of time wasted on loading the object from the database
+ if metrics.EnabledExpensive {
+ defer func(start time.Time) { s.AccountReads += time.Since(start) }(time.Now())
+ }
+ // Load the object from the database
+ enc, err := s.trie.TryGet(addr[:])
+ if len(enc) == 0 {
+ s.setError(err)
+ return nil
+ }
+ var data Account
+ if err := rlp.DecodeBytes(enc, &data); err != nil {
+ log.Error("Failed to decode state object", "addr", addr, "err", err)
+ return nil
+ }
+ // Insert into the live set
+ obj := newObject(s, addr, data)
+ s.setStateObject(obj)
+ return obj
+}
+
+func (self *StateDB) setStateObject(object *stateObject) {
+ self.stateObjects[object.Address()] = object
+}
+
+// Retrieve a state object or create a new state object if nil.
+func (self *StateDB) GetOrNewStateObject(addr common.Address) *stateObject {
+ stateObject := self.getStateObject(addr)
+ if stateObject == nil || stateObject.deleted {
+ stateObject, _ = self.createObject(addr)
+ }
+ return stateObject
+}
+
+// createObject creates a new state object. If there is an existing account with
+// the given address, it is overwritten and returned as the second return value.
+func (self *StateDB) createObject(addr common.Address) (newobj, prev *stateObject) {
+ prev = self.getStateObject(addr)
+ newobj = newObject(self, addr, Account{})
+ newobj.setNonce(0) // sets the object to dirty
+ if prev == nil {
+ self.journal.append(createObjectChange{account: &addr})
+ } else {
+ self.journal.append(resetObjectChange{prev: prev})
+ }
+ self.setStateObject(newobj)
+ return newobj, prev
+}
+
+// CreateAccount explicitly creates a state object. If a state object with the address
+// already exists the balance is carried over to the new account.
+//
+// CreateAccount is called during the EVM CREATE operation. The situation might arise that
+// a contract does the following:
+//
+// 1. sends funds to sha(account ++ (nonce + 1))
+// 2. tx_create(sha(account ++ nonce)) (note that this gets the address of 1)
+//
+// Carrying over the balance ensures that Ether doesn't disappear.
+func (self *StateDB) CreateAccount(addr common.Address) {
+ newObj, prev := self.createObject(addr)
+ if prev != nil {
+ newObj.setBalance(prev.data.Balance)
+ }
+}
+
+func (db *StateDB) ForEachStorage(addr common.Address, cb func(key, value common.Hash) bool) error {
+ so := db.getStateObject(addr)
+ if so == nil {
+ return nil
+ }
+ 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
+}
+
+// Copy creates a deep, independent copy of the state.
+// Snapshots of the copied state cannot be applied to the copy.
+func (self *StateDB) Copy() *StateDB {
+ // Copy all the basic fields, initialize the memory ones
+ state := &StateDB{
+ db: self.db,
+ trie: self.db.CopyTrie(self.trie),
+ stateObjects: make(map[common.Address]*stateObject, len(self.journal.dirties)),
+ stateObjectsDirty: make(map[common.Address]struct{}, len(self.journal.dirties)),
+ refund: self.refund,
+ logs: make(map[common.Hash][]*types.Log, len(self.logs)),
+ logSize: self.logSize,
+ preimages: make(map[common.Hash][]byte, len(self.preimages)),
+ journal: newJournal(),
+ }
+ // Copy the dirty states, logs, and preimages
+ for addr := range self.journal.dirties {
+ // As documented [here](https://github.com/ethereum/go-ethereum/pull/16485#issuecomment-380438527),
+ // and in the Finalise-method, there is a case where an object is in the journal but not
+ // in the stateObjects: OOG after touch on ripeMD prior to Byzantium. Thus, we need to check for
+ // nil
+ if object, exist := self.stateObjects[addr]; exist {
+ state.stateObjects[addr] = object.deepCopy(state)
+ state.stateObjectsDirty[addr] = struct{}{}
+ }
+ }
+ // Above, we don't copy the actual journal. This means that if the copy is copied, the
+ // loop above will be a no-op, since the copy's journal is empty.
+ // Thus, here we iterate over stateObjects, to enable copies of copies
+ for addr := range self.stateObjectsDirty {
+ if _, exist := state.stateObjects[addr]; !exist {
+ state.stateObjects[addr] = self.stateObjects[addr].deepCopy(state)
+ state.stateObjectsDirty[addr] = struct{}{}
+ }
+ }
+ for hash, logs := range self.logs {
+ cpy := make([]*types.Log, len(logs))
+ for i, l := range logs {
+ cpy[i] = new(types.Log)
+ *cpy[i] = *l
+ }
+ state.logs[hash] = cpy
+ }
+ for hash, preimage := range self.preimages {
+ state.preimages[hash] = preimage
+ }
+ return state
+}
+
+// Snapshot returns an identifier for the current revision of the state.
+func (self *StateDB) Snapshot() int {
+ id := self.nextRevisionId
+ self.nextRevisionId++
+ self.validRevisions = append(self.validRevisions, revision{id, self.journal.length()})
+ return id
+}
+
+// RevertToSnapshot reverts all state changes made since the given revision.
+func (self *StateDB) RevertToSnapshot(revid int) {
+ // Find the snapshot in the stack of valid snapshots.
+ idx := sort.Search(len(self.validRevisions), func(i int) bool {
+ return self.validRevisions[i].id >= revid
+ })
+ if idx == len(self.validRevisions) || self.validRevisions[idx].id != revid {
+ panic(fmt.Errorf("revision id %v cannot be reverted", revid))
+ }
+ snapshot := self.validRevisions[idx].journalIndex
+
+ // Replay the journal to undo changes and remove invalidated snapshots
+ self.journal.revert(self, snapshot)
+ self.validRevisions = self.validRevisions[:idx]
+}
+
+// GetRefund returns the current value of the refund counter.
+func (self *StateDB) GetRefund() uint64 {
+ return self.refund
+}
+
+// Finalise finalises the state by removing the self destructed objects
+// and clears the journal as well as the refunds.
+func (s *StateDB) Finalise(deleteEmptyObjects bool) {
+ for addr := range s.journal.dirties {
+ stateObject, exist := s.stateObjects[addr]
+ if !exist {
+ // ripeMD is 'touched' at block 1714175, in tx 0x1237f737031e40bcde4a8b7e717b2d15e3ecadfe49bb1bbc71ee9deb09c6fcf2
+ // That tx goes out of gas, and although the notion of 'touched' does not exist there, the
+ // touch-event will still be recorded in the journal. Since ripeMD is a special snowflake,
+ // it will persist in the journal even though the journal is reverted. In this special circumstance,
+ // it may exist in `s.journal.dirties` but not in `s.stateObjects`.
+ // Thus, we can safely ignore it here
+ continue
+ }
+
+ if stateObject.suicided || (deleteEmptyObjects && stateObject.empty()) {
+ s.deleteStateObject(stateObject)
+ } else {
+ stateObject.updateRoot(s.db)
+ s.updateStateObject(stateObject)
+ }
+ s.stateObjectsDirty[addr] = struct{}{}
+ }
+ // Invalidate journal because reverting across transactions is not allowed.
+ s.clearJournalAndRefund()
+}
+
+// IntermediateRoot computes the current root hash of the state trie.
+// It is called in between transactions to get the root hash that
+// goes into transaction receipts.
+func (s *StateDB) IntermediateRoot(deleteEmptyObjects bool) common.Hash {
+ s.Finalise(deleteEmptyObjects)
+
+ // Track the amount of time wasted on hashing the account trie
+ if metrics.EnabledExpensive {
+ defer func(start time.Time) { s.AccountHashes += time.Since(start) }(time.Now())
+ }
+ return s.trie.Hash()
+}
+
+// Prepare sets the current transaction hash and index and block hash which is
+// used when the EVM emits new state logs.
+func (self *StateDB) Prepare(thash, bhash common.Hash, ti int) {
+ self.thash = thash
+ self.bhash = bhash
+ self.txIndex = ti
+}
+
+func (s *StateDB) clearJournalAndRefund() {
+ s.journal = newJournal()
+ s.validRevisions = s.validRevisions[:0]
+ s.refund = 0
+}
+
+// Commit writes the state to the underlying in-memory trie database.
+func (s *StateDB) Commit(deleteEmptyObjects bool) (root common.Hash, err error) {
+ defer s.clearJournalAndRefund()
+
+ for addr := range s.journal.dirties {
+ s.stateObjectsDirty[addr] = struct{}{}
+ }
+ // Commit objects to the trie, measuring the elapsed time
+ for addr, stateObject := range s.stateObjects {
+ _, isDirty := s.stateObjectsDirty[addr]
+ switch {
+ case stateObject.suicided || (isDirty && deleteEmptyObjects && stateObject.empty()):
+ // If the object has been removed, don't bother syncing it
+ // and just mark it for deletion in the trie.
+ s.deleteStateObject(stateObject)
+ case isDirty:
+ // Write any contract code associated with the state object
+ if stateObject.code != nil && stateObject.dirtyCode {
+ s.db.TrieDB().InsertBlob(common.BytesToHash(stateObject.CodeHash()), stateObject.code)
+ stateObject.dirtyCode = false
+ }
+ // Write any storage changes in the state object to its storage trie.
+ if err := stateObject.CommitTrie(s.db); err != nil {
+ return common.Hash{}, err
+ }
+ // Update the object in the main account trie.
+ s.updateStateObject(stateObject)
+ }
+ delete(s.stateObjectsDirty, addr)
+ }
+ // Write the account trie changes, measuing the amount of wasted time
+ if metrics.EnabledExpensive {
+ defer func(start time.Time) { s.AccountCommits += time.Since(start) }(time.Now())
+ }
+ root, err = s.trie.Commit(func(leaf []byte, parent common.Hash) error {
+ var account Account
+ if err := rlp.DecodeBytes(leaf, &account); err != nil {
+ return nil
+ }
+ if account.Root != emptyRoot {
+ s.db.TrieDB().Reference(account.Root, parent)
+ }
+ code := common.BytesToHash(account.CodeHash)
+ if code != emptyCode {
+ s.db.TrieDB().Reference(code, parent)
+ }
+ return nil
+ })
+ return root, err
+}