diff --git a/core/state/interface.go b/core/state/interface.go new file mode 100644 index 0000000000..2362ac828b --- /dev/null +++ b/core/state/interface.go @@ -0,0 +1,82 @@ +// Copyright 2016 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 . + +package state + +import ( + "math/big" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" +) + +// StateDBer is copied from vm/interface.go +// It is used by StateObject & Journal right now, to abstract StateDB & ParallelStateDB +type StateDBer interface { + getBaseStateDB() *StateDB + getStateObject(common.Address) *StateObject // only accessible for journal + storeStateObj(common.Address, *StateObject) // only accessible for journal + + CreateAccount(common.Address) + + SubBalance(common.Address, *big.Int) + AddBalance(common.Address, *big.Int) + GetBalance(common.Address) *big.Int + + GetNonce(common.Address) uint64 + SetNonce(common.Address, uint64) + + GetCodeHash(common.Address) common.Hash + GetCode(common.Address) []byte + SetCode(common.Address, []byte) + GetCodeSize(common.Address) int + + AddRefund(uint64) + SubRefund(uint64) + GetRefund() uint64 + + GetCommittedState(common.Address, common.Hash) common.Hash + GetState(common.Address, common.Hash) common.Hash + SetState(common.Address, common.Hash, common.Hash) + + Suicide(common.Address) bool + HasSuicided(common.Address) bool + + // Exist reports whether the given account exists in state. + // Notably this should also return true for suicided accounts. + Exist(common.Address) bool + // Empty returns whether the given account is empty. Empty + // is defined according to EIP161 (balance = nonce = code = 0). + Empty(common.Address) bool + + PrepareAccessList(sender common.Address, dest *common.Address, precompiles []common.Address, txAccesses types.AccessList) + AddressInAccessList(addr common.Address) bool + SlotInAccessList(addr common.Address, slot common.Hash) (addressOk bool, slotOk bool) + // AddAddressToAccessList adds the given address to the access list. This operation is safe to perform + // even if the feature/fork is not active yet + AddAddressToAccessList(addr common.Address) + // AddSlotToAccessList adds the given (address,slot) to the access list. This operation is safe to perform + // even if the feature/fork is not active yet + AddSlotToAccessList(addr common.Address, slot common.Hash) + + RevertToSnapshot(int) + Snapshot() int + + AddLog(*types.Log) + AddPreimage(common.Hash, []byte) + + ForEachStorage(common.Address, func(common.Hash, common.Hash) bool) error +} diff --git a/core/state/journal.go b/core/state/journal.go index 96655d007d..e267205688 100644 --- a/core/state/journal.go +++ b/core/state/journal.go @@ -26,7 +26,7 @@ import ( // reverted on demand. type journalEntry interface { // revert undoes the changes introduced by this journal entry. - revert(*StateDB) + revert(StateDBer) // dirtied returns the Ethereum address modified by this journal entry. dirtied() *common.Address @@ -58,10 +58,10 @@ func (j *journal) append(entry journalEntry) { // revert undoes a batch of journalled modifications along with any reverted // dirty handling too. -func (j *journal) revert(statedb *StateDB, snapshot int) { +func (j *journal) revert(dber StateDBer, snapshot int) { for i := len(j.entries) - 1; i >= snapshot; i-- { // Undo the changes made by the operation - j.entries[i].revert(statedb) + j.entries[i].revert(dber) // Drop any dirty tracking induced by the change if addr := j.entries[i].dirtied(); addr != nil { @@ -141,7 +141,8 @@ type ( } ) -func (ch createObjectChange) revert(s *StateDB) { +func (ch createObjectChange) revert(dber StateDBer) { + s := dber.getBaseStateDB() if s.parallel.isSlotDB { delete(s.parallel.dirtiedStateObjectsInSlot, *ch.account) delete(s.parallel.addrStateChangesInSlot, *ch.account) @@ -159,13 +160,14 @@ func (ch createObjectChange) dirtied() *common.Address { return ch.account } -func (ch resetObjectChange) revert(s *StateDB) { +func (ch resetObjectChange) revert(dber StateDBer) { + s := dber.getBaseStateDB() if s.parallel.isSlotDB { // ch.prev must be from dirtiedStateObjectsInSlot, put it back s.parallel.dirtiedStateObjectsInSlot[ch.prev.address] = ch.prev } else { // ch.prev was got from main DB, put it back to main DB. - s.SetStateObject(ch.prev) + s.storeStateObj(ch.prev.address, ch.prev) } if !ch.prevdestruct && s.snap != nil { delete(s.snapDestructs, ch.prev.address) @@ -176,8 +178,8 @@ func (ch resetObjectChange) dirtied() *common.Address { return nil } -func (ch suicideChange) revert(s *StateDB) { - obj := s.getStateObject(*ch.account) +func (ch suicideChange) revert(dber StateDBer) { + obj := dber.getStateObject(*ch.account) if obj != nil { obj.suicided = ch.prev obj.setBalance(ch.prevbalance) @@ -190,46 +192,47 @@ func (ch suicideChange) dirtied() *common.Address { var ripemd = common.HexToAddress("0000000000000000000000000000000000000003") -func (ch touchChange) revert(s *StateDB) { +func (ch touchChange) revert(dber StateDBer) { } func (ch touchChange) dirtied() *common.Address { return ch.account } -func (ch balanceChange) revert(s *StateDB) { - s.getStateObject(*ch.account).setBalance(ch.prev) +func (ch balanceChange) revert(dber StateDBer) { + dber.getStateObject(*ch.account).setBalance(ch.prev) } func (ch balanceChange) dirtied() *common.Address { return ch.account } -func (ch nonceChange) revert(s *StateDB) { - s.getStateObject(*ch.account).setNonce(ch.prev) +func (ch nonceChange) revert(dber StateDBer) { + dber.getStateObject(*ch.account).setNonce(ch.prev) } func (ch nonceChange) dirtied() *common.Address { return ch.account } -func (ch codeChange) revert(s *StateDB) { - s.getStateObject(*ch.account).setCode(common.BytesToHash(ch.prevhash), ch.prevcode) +func (ch codeChange) revert(dber StateDBer) { + dber.getStateObject(*ch.account).setCode(common.BytesToHash(ch.prevhash), ch.prevcode) } func (ch codeChange) dirtied() *common.Address { return ch.account } -func (ch storageChange) revert(s *StateDB) { - s.getStateObject(*ch.account).setState(ch.key, ch.prevalue) +func (ch storageChange) revert(dber StateDBer) { + dber.getStateObject(*ch.account).setState(ch.key, ch.prevalue) } func (ch storageChange) dirtied() *common.Address { return ch.account } -func (ch refundChange) revert(s *StateDB) { +func (ch refundChange) revert(dber StateDBer) { + s := dber.getBaseStateDB() s.refund = ch.prev } @@ -237,7 +240,9 @@ func (ch refundChange) dirtied() *common.Address { return nil } -func (ch addLogChange) revert(s *StateDB) { +func (ch addLogChange) revert(dber StateDBer) { + s := dber.getBaseStateDB() + logs := s.logs[ch.txhash] if len(logs) == 1 { delete(s.logs, ch.txhash) @@ -251,7 +256,8 @@ func (ch addLogChange) dirtied() *common.Address { return nil } -func (ch addPreimageChange) revert(s *StateDB) { +func (ch addPreimageChange) revert(dber StateDBer) { + s := dber.getBaseStateDB() delete(s.preimages, ch.hash) } @@ -259,7 +265,8 @@ func (ch addPreimageChange) dirtied() *common.Address { return nil } -func (ch accessListAddAccountChange) revert(s *StateDB) { +func (ch accessListAddAccountChange) revert(dber StateDBer) { + s := dber.getBaseStateDB() /* One important invariant here, is that whenever a (addr, slot) is added, if the addr is not already present, the add causes two journal entries: @@ -278,7 +285,8 @@ func (ch accessListAddAccountChange) dirtied() *common.Address { return nil } -func (ch accessListAddSlotChange) revert(s *StateDB) { +func (ch accessListAddSlotChange) revert(dber StateDBer) { + s := dber.getBaseStateDB() if s.accessList != nil { s.accessList.DeleteSlot(*ch.address, *ch.slot) } diff --git a/core/state/state_object.go b/core/state/state_object.go index b442954efe..b516db042a 100644 --- a/core/state/state_object.go +++ b/core/state/state_object.go @@ -150,6 +150,7 @@ type StateObject struct { addrHash common.Hash // hash of ethereum address of the account data Account db *StateDB + dbItf StateDBer // DB error. // State objects are used by the consensus core and VM which are @@ -218,13 +219,13 @@ func (s *StateObject) empty() bool { // Slot 0 tx 2: GetNonce, lightCopy based on main DB(balance = 100) , not empty // return s.db.GetNonce(s.address) == 0 && s.db.GetBalance(s.address).Sign() == 0 && bytes.Equal(s.db.GetCodeHash(s.address).Bytes(), emptyCodeHash) - if s.db.GetBalance(s.address).Sign() != 0 { // check balance first, since it is most likely not zero + if s.dbItf.GetBalance(s.address).Sign() != 0 { // check balance first, since it is most likely not zero return false } - if s.db.GetNonce(s.address) != 0 { + if s.dbItf.GetNonce(s.address) != 0 { return false } - codeHash := s.db.GetCodeHash(s.address) + codeHash := s.dbItf.GetCodeHash(s.address) return bytes.Equal(codeHash.Bytes(), emptyCodeHash) // code is empty, the object is empty } @@ -239,7 +240,8 @@ type Account struct { } // newObject creates a state object. -func newObject(db *StateDB, isParallel bool, address common.Address, data Account) *StateObject { +func newObject(dbItf StateDBer, isParallel bool, address common.Address, data Account) *StateObject { + db := dbItf.getBaseStateDB() if data.Balance == nil { data.Balance = new(big.Int) // todo: why not common.Big0? } @@ -257,6 +259,7 @@ func newObject(db *StateDB, isParallel bool, address common.Address, data Accoun return &StateObject{ db: db, + dbItf: dbItf, address: address, addrHash: crypto.Keccak256Hash(address[:]), data: data, @@ -448,7 +451,7 @@ func (s *StateObject) SetState(db Database, key, value common.Hash) { // this `SetState could be skipped` // d.Finally, the key's value will be `val_2`, while it should be `val_1` // such as: https://bscscan.com/txs?block=2491181 - prev := s.db.GetState(s.address, key) + prev := s.dbItf.GetState(s.address, key) // fixme: if it is for journal, may not necessary, we can remove this change record if prev == value { return } @@ -634,7 +637,6 @@ func (s *StateObject) AddBalance(amount *big.Int) { return } s.SetBalance(new(big.Int).Add(s.Balance(), amount)) - // s.SetBalance(new(big.Int).Add(s.db.GetBalance(s.address), amount)) } // SubBalance removes amount from s's balance. @@ -644,11 +646,9 @@ func (s *StateObject) SubBalance(amount *big.Int) { return } s.SetBalance(new(big.Int).Sub(s.Balance(), amount)) - // s.SetBalance(new(big.Int).Sub(s.db.GetBalance(s.address), amount)) } func (s *StateObject) SetBalance(amount *big.Int) { - // prevBalance := new(big.Int).Set(s.db.GetBalance(s.address)) s.db.journal.append(balanceChange{ account: &s.address, prev: new(big.Int).Set(s.data.Balance), // prevBalance, @@ -664,7 +664,7 @@ func (s *StateObject) setBalance(amount *big.Int) { // Return the gas back to the origin. Used by the Virtual machine or Closures func (s *StateObject) ReturnGas(gas *big.Int) {} -func (s *StateObject) lightCopy(db *StateDB) *StateObject { +func (s *StateObject) lightCopy(db *ParallelStateDB) *StateObject { stateObject := newObject(db, s.isParallel, s.address, s.data) if s.trie != nil { // fixme: no need to copy trie for light copy, since light copied object won't access trie DB @@ -744,7 +744,7 @@ func (s *StateObject) CodeSize(db Database) int { } func (s *StateObject) SetCode(codeHash common.Hash, code []byte) { - prevcode := s.db.GetCode(s.address) + prevcode := s.dbItf.GetCode(s.address) s.db.journal.append(codeChange{ account: &s.address, prevhash: s.CodeHash(), @@ -760,7 +760,7 @@ func (s *StateObject) setCode(codeHash common.Hash, code []byte) { } func (s *StateObject) SetNonce(nonce uint64) { - prevNonce := s.db.GetNonce(s.address) + prevNonce := s.dbItf.GetNonce(s.address) s.db.journal.append(nonceChange{ account: &s.address, prev: prevNonce, diff --git a/core/state/statedb.go b/core/state/statedb.go index 4e852cdbd8..217bdd6a1b 100644 --- a/core/state/statedb.go +++ b/core/state/statedb.go @@ -97,20 +97,9 @@ func (s *StateDB) storeStateObj(addr common.Address, stateObject *StateObject) { if s.isParallel { // When a state object is stored into s.parallel.stateObjects, // it belongs to base StateDB, it is confirmed and valid. - if s.parallel.isSlotDB { - // the object could be create in SlotDB, if it got the object from DB and - // update it to the shared `s.parallel.stateObjects`` - stateObject.db = s.parallel.baseStateDB - stateObject.db.storeParallelLock.Lock() - if _, ok := s.parallel.stateObjects.Load(addr); !ok { - s.parallel.stateObjects.Store(addr, stateObject) - } - stateObject.db.storeParallelLock.Unlock() - } else { - stateObject.db.storeParallelLock.Lock() - s.parallel.stateObjects.Store(addr, stateObject) - stateObject.db.storeParallelLock.Unlock() - } + stateObject.db.storeParallelLock.Lock() + s.parallel.stateObjects.Store(addr, stateObject) + stateObject.db.storeParallelLock.Unlock() } else { s.stateObjects[addr] = stateObject } @@ -127,7 +116,7 @@ func (s *StateDB) deleteStateObj(addr common.Address) { // For parallel mode only type ParallelState struct { - isSlotDB bool // isSlotDB denotes StateDB is used in slot + isSlotDB bool // denotes StateDB is used in slot, we will try to remove it SlotIndex int // fixme: to be removed // stateObjects holds the state objects in the base slot db // the reason for using stateObjects instead of stateObjects on the outside is @@ -139,7 +128,7 @@ type ParallelState struct { baseStateDB *StateDB // for parallel mode, there will be a base StateDB in dispatcher routine. baseTxIndex int // slotDB is created base on this tx index. dirtiedStateObjectsInSlot map[common.Address]*StateObject - unconfirmedDBInShot map[int]*StateDB // do unconfirmed reference in same slot. + unconfirmedDBInShot map[int]*ParallelStateDB // do unconfirmed reference in same slot. // we will record the read detail for conflict check and // the changed addr or key for object merge, the changed detail can be acheived from the dirty object @@ -262,40 +251,6 @@ func New(root common.Hash, db Database, snaps *snapshot.Tree) (*StateDB, error) return newStateDB(root, db, snaps) } -// NewSlotDB creates a new State DB based on the provided StateDB. -// With parallel, each execution slot would have its own StateDB. -func NewSlotDB(db *StateDB, systemAddr common.Address, txIndex int, baseTxIndex int, keepSystem bool, - unconfirmedDBs *sync.Map /*map[int]*StateDB*/) *StateDB { - slotDB := db.CopyForSlot() - slotDB.txIndex = txIndex - slotDB.originalRoot = db.originalRoot - slotDB.parallel.baseStateDB = db - slotDB.parallel.baseTxIndex = baseTxIndex - slotDB.parallel.systemAddress = systemAddr - slotDB.parallel.systemAddressOpsCount = 0 - slotDB.parallel.keepSystemAddressBalance = keepSystem - slotDB.storagePool = NewStoragePool() - slotDB.EnableWriteOnSharedStorage() - for index := baseTxIndex + 1; index < slotDB.txIndex; index++ { // txIndex - unconfirmedDB, ok := unconfirmedDBs.Load(index) - if ok { - slotDB.parallel.unconfirmedDBInShot[index] = unconfirmedDB.(*StateDB) - } - } - - // All transactions will pay gas fee to the systemAddr at the end, this address is - // deemed to conflict, we handle it specially, clear it now and set it back to the main - // StateDB later; - // But there are transactions that will try to read systemAddr's balance, such as: - // https://bscscan.com/tx/0xcd69755be1d2f55af259441ff5ee2f312830b8539899e82488a21e85bc121a2a. - // It will trigger transaction redo and keepSystem will be marked as true. - if !keepSystem { - slotDB.SetBalance(systemAddr, big.NewInt(0)) - } - - return slotDB -} - // NewWithSharedPool creates a new state with sharedStorge on layer 1.5 func NewWithSharedPool(root common.Hash, db Database, snaps *snapshot.Tree) (*StateDB, error) { statedb, err := newStateDB(root, db, snaps) @@ -342,200 +297,17 @@ func newStateDB(root common.Hash, db Database, snaps *snapshot.Tree) (*StateDB, return sdb, nil } -func (s *StateDB) getStateObjectFromStateObjects(addr common.Address) (*StateObject, bool) { - return s.loadStateObj(addr) -} - -// RevertSlotDB keep the Read list for conflict detect, -// discard all state changes except: -// - nonce and balance of from address -// - balance of system address: will be used on merge to update SystemAddress's balance -func (s *StateDB) RevertSlotDB(from common.Address) { - s.parallel.kvChangesInSlot = make(map[common.Address]StateKeys) - - // balance := s.parallel.balanceChangesInSlot[from] - s.parallel.nonceChangesInSlot = make(map[common.Address]struct{}) - s.parallel.balanceChangesInSlot = make(map[common.Address]struct{}, 1) - s.parallel.addrStateChangesInSlot = make(map[common.Address]bool) // 0: created, 1: deleted - - selfStateObject := s.parallel.dirtiedStateObjectsInSlot[from] - systemAddress := s.parallel.systemAddress - systemStateObject := s.parallel.dirtiedStateObjectsInSlot[systemAddress] - s.parallel.dirtiedStateObjectsInSlot = make(map[common.Address]*StateObject, 2) - // keep these elements - s.parallel.dirtiedStateObjectsInSlot[from] = selfStateObject - s.parallel.dirtiedStateObjectsInSlot[systemAddress] = systemStateObject - s.parallel.balanceChangesInSlot[from] = struct{}{} - s.parallel.balanceChangesInSlot[systemAddress] = struct{}{} - s.parallel.nonceChangesInSlot[from] = struct{}{} -} - -// PrepareForParallel prepares for state db to be used in parallel execution mode. -func (s *StateDB) PrepareForParallel() { - s.isParallel = true - s.parallel.stateObjects = &StateObjectSyncMap{} +func (s *StateDB) getBaseStateDB() *StateDB { + return s } -// MergeSlotDB is for Parallel execution mode, when the transaction has been -// finalized(dirty -> pending) on execution slot, the execution results should be -// merged back to the main StateDB. -// And it will return and keep the slot's change list for later conflict detect. -func (s *StateDB) MergeSlotDB(slotDb *StateDB, slotReceipt *types.Receipt, txIndex int) { - // receipt.Logs use unified log index within a block - // align slotDB's log index to the block stateDB's logSize - for _, l := range slotReceipt.Logs { - l.Index += s.logSize - } - s.logSize += slotDb.logSize - - // before merge, pay the gas fee first: AddBalance to consensus.SystemAddress - systemAddress := slotDb.parallel.systemAddress - if slotDb.parallel.keepSystemAddressBalance { - s.SetBalance(systemAddress, slotDb.GetBalance(systemAddress)) - } else { - s.AddBalance(systemAddress, slotDb.GetBalance(systemAddress)) - } - - // only merge dirty objects - addressesToPrefetch := make([][]byte, 0, len(slotDb.stateObjectsDirty)) - for addr := range slotDb.stateObjectsDirty { - if _, exist := s.stateObjectsDirty[addr]; !exist { - s.stateObjectsDirty[addr] = struct{}{} - } - // system address is EOA account, it should have no storage change - if addr == systemAddress { - continue - } - - // stateObjects: KV, balance, nonce... - dirtyObj, ok := slotDb.parallel.dirtiedStateObjectsInSlot[addr] - if !ok { - log.Error("parallel merge, but dirty object not exist!", "SlotIndex", slotDb.parallel.SlotIndex, "txIndex:", slotDb.txIndex, "addr", addr) - continue - } - mainObj, exist := s.loadStateObj(addr) - if !exist { // fixme: it is also state change - // addr not exist on main DB, do ownership transfer - // dirtyObj.db = s - // dirtyObj.finalise(true) // true: prefetch on dispatcher - mainObj = dirtyObj.deepCopy(s) - mainObj.finalise(true) - s.storeStateObj(addr, mainObj) - // fixme: should not delete, would cause unconfirmed DB incorrect? - // delete(slotDb.parallel.dirtiedStateObjectsInSlot, addr) // transfer ownership, fixme: shared read? - if dirtyObj.deleted { - // remove the addr from snapAccounts&snapStorage only when object is deleted. - // "deleted" is not equal to "snapDestructs", since createObject() will add an addr for - // snapDestructs to destroy previous object, while it will keep the addr in snapAccounts & snapAccounts - delete(s.snapAccounts, addr) - delete(s.snapStorage, addr) - } - } else { - // addr already in main DB, do merge: balance, KV, code, State(create, suicide) - // can not do copy or ownership transfer directly, since dirtyObj could have outdated - // data(may be updated within the conflict window) - - var newMainObj = mainObj // we don't need to copy the object since the storages are thread safe - if _, ok := slotDb.parallel.addrStateChangesInSlot[addr]; ok { - // there are 3 kinds of state change: - // 1.Suicide - // 2.Empty Delete - // 3.createObject - // a.AddBalance,SetState to an unexist or deleted(suicide, empty delete) address. - // b.CreateAccount: like DAO the fork, regenerate a account carry its balance without KV - // For these state change, do ownership transafer for efficiency: - // dirtyObj.db = s - // newMainObj = dirtyObj - newMainObj = dirtyObj.deepCopy(s) - // should not delete, would cause unconfirmed DB incorrect. - // delete(slotDb.parallel.dirtiedStateObjectsInSlot, addr) // transfer ownership, fixme: shared read? - if dirtyObj.deleted { - // remove the addr from snapAccounts&snapStorage only when object is deleted. - // "deleted" is not equal to "snapDestructs", since createObject() will add an addr for - // snapDestructs to destroy previous object, while it will keep the addr in snapAccounts & snapAccounts - delete(s.snapAccounts, addr) - delete(s.snapStorage, addr) - } - } else { - // deepCopy a temporary *StateObject for safety, since slot could read the address, - // dispatch should avoid overwrite the StateObject directly otherwise, it could - // crash for: concurrent map iteration and map write - - if _, balanced := slotDb.parallel.balanceChangesInSlot[addr]; balanced { - newMainObj.SetBalance(dirtyObj.Balance()) - } - if _, coded := slotDb.parallel.codeChangesInSlot[addr]; coded { - newMainObj.code = dirtyObj.code - newMainObj.data.CodeHash = dirtyObj.data.CodeHash - newMainObj.dirtyCode = true - } - if keys, stated := slotDb.parallel.kvChangesInSlot[addr]; stated { - newMainObj.MergeSlotObject(s.db, dirtyObj, keys) - } - if _, nonced := slotDb.parallel.nonceChangesInSlot[addr]; nonced { - // dirtyObj.Nonce() should not be less than newMainObj - newMainObj.setNonce(dirtyObj.Nonce()) - } - } - newMainObj.finalise(true) // true: prefetch on dispatcher - // update the object - s.storeStateObj(addr, newMainObj) - } - addressesToPrefetch = append(addressesToPrefetch, common.CopyBytes(addr[:])) // Copy needed for closure - } - - if s.prefetcher != nil && len(addressesToPrefetch) > 0 { - s.prefetcher.prefetch(s.originalRoot, addressesToPrefetch, emptyAddr) // prefetch for trie node of account - } - - for addr := range slotDb.stateObjectsPending { - if _, exist := s.stateObjectsPending[addr]; !exist { - s.stateObjectsPending[addr] = struct{}{} - } - } - - // slotDb.logs: logs will be kept in receipts, no need to do merge - - for hash, preimage := range slotDb.preimages { - s.preimages[hash] = preimage - } - if s.accessList != nil { - // fixme: accessList is not enabled yet, but it should use merge rather than overwrite Copy - s.accessList = slotDb.accessList.Copy() - } - - if slotDb.snaps != nil { - for k := range slotDb.snapDestructs { - // There could be a race condition for parallel transaction execution - // One transaction add balance 0 to an empty address, will delete it(delete empty is enabled). - // While another concurrent transaction could add a none-zero balance to it, make it not empty - // We fixed it by add a addr state read record for add balance 0 - s.snapParallelLock.Lock() - s.snapDestructs[k] = struct{}{} - s.snapParallelLock.Unlock() - } - - // slotDb.snapAccounts should be empty, comment out and to be deleted later - // for k, v := range slotDb.snapAccounts { - // s.snapAccounts[k] = v - // } - // slotDb.snapStorage should be empty, comment out and to be deleted later - // for k, v := range slotDb.snapStorage { - // temp := make(map[string][]byte) - // for kk, vv := range v { - // temp[kk] = vv - // } - // s.snapStorage[k] = temp - // } - } +func (s *StateDB) getStateObjectFromStateObjects(addr common.Address) (*StateObject, bool) { + return s.loadStateObj(addr) } func (s *StateDB) EnableWriteOnSharedStorage() { s.writeOnSharedStorage = true } -func (s *StateDB) SetSlotIndex(index int) { - s.parallel.SlotIndex = index -} // 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 @@ -674,1462 +446,806 @@ func (s *StateDB) AddRefund(gas uint64) { func (s *StateDB) SubRefund(gas uint64) { s.journal.append(refundChange{prev: s.refund}) if gas > s.refund { - if s.isParallel { - // we don't need to panic here if we read the wrong state, we just need to redo this transaction - log.Info(fmt.Sprintf("Refund counter below zero (gas: %d > refund: %d)", gas, s.refund), "tx", s.thash.String()) - s.parallel.needsRedo = true - return - } panic(fmt.Sprintf("Refund counter below zero (gas: %d > refund: %d)", gas, s.refund)) } s.refund -= gas } -// For Parallel Execution Mode, it can be seen as Penetrated Access: -// ------------------------------------------------------- -// | BaseTxIndex | Unconfirmed Txs... | Current TxIndex | -// ------------------------------------------------------- -// Access from the unconfirmed DB with range&priority: txIndex -1(previous tx) -> baseTxIndex + 1 -func (s *StateDB) getBalanceFromUnconfirmedDB(addr common.Address) *big.Int { - if addr == s.parallel.systemAddress { - // never get systemaddress from unconfirmed DB - return nil +// Exist reports whether the given account address exists in the state. +// Notably this also returns true for suicided accounts. +func (s *StateDB) Exist(addr common.Address) bool { + log.Debug("StateDB Exist", "SlotxIndex", s.parallel.SlotIndex, "txIndex", s.TxIndex()) + // 3.Try to get from main StateDB + exist := s.getStateObject(addr) != nil + return exist +} + +// Empty returns whether the state object is either non-existent +// or empty according to the EIP161 specification (balance = nonce = code = 0) +func (s *StateDB) Empty(addr common.Address) bool { + log.Debug("StateDB Empty", "SlotxIndex", s.parallel.SlotIndex, "txIndex", s.TxIndex()) + so := s.getStateObject(addr) + empty := (so == nil || so.empty()) + return empty +} + +// GetBalance retrieves the balance from the given address or 0 if object not found +// GetFrom the dirty list => from unconfirmed DB => get from main stateDB +func (s *StateDB) GetBalance(addr common.Address) *big.Int { + log.Debug("StateDB GetBalance", "SlotxIndex", s.parallel.SlotIndex, "txIndex", s.TxIndex()) + if s.parallel.SlotIndex != -1 { + log.Debug("StateDB GetBalance in slot", "SlotxIndex", s.parallel.SlotIndex, "txIndex", s.TxIndex()) + } + balance := common.Big0 + stateObject := s.getStateObject(addr) + if stateObject != nil { + balance = stateObject.Balance() } + return balance +} - for i := s.txIndex - 1; i > s.parallel.baseTxIndex; i-- { - if db, ok := s.parallel.unconfirmedDBInShot[i]; ok { - // 1.Refer the state of address, exist or not in dirtiedStateObjectsInSlot - if obj, exist := db.parallel.dirtiedStateObjectsInSlot[addr]; exist { - balanceHit := false - if _, exist := db.parallel.addrStateChangesInSlot[addr]; exist { - balanceHit = true - } - if _, exist := db.parallel.balanceChangesInSlot[addr]; exist { // only changed balance is reliable - balanceHit = true - } - if !balanceHit { - continue - } - balance := obj.Balance() - if obj.deleted { - balance = common.Big0 - } - return balance - } - } +func (s *StateDB) GetNonce(addr common.Address) uint64 { + log.Debug("StateDB GetNonce", "SlotxIndex", s.parallel.SlotIndex, "txIndex", s.TxIndex()) + + var nonce uint64 = 0 + stateObject := s.getStateObject(addr) + if stateObject != nil { + nonce = stateObject.Nonce() } - return nil + + return nonce } -// Similar to getBalanceFromUnconfirmedDB -func (s *StateDB) getNonceFromUnconfirmedDB(addr common.Address) (uint64, bool) { - if addr == s.parallel.systemAddress { - // never get systemaddress from unconfirmed DB - return 0, false - } +// TxIndex returns the current transaction index set by Prepare. +func (s *StateDB) TxIndex() int { + return s.txIndex +} - for i := s.txIndex - 1; i > s.parallel.baseTxIndex; i-- { - if unconfirmedDb, ok := s.parallel.unconfirmedDBInShot[i]; ok { - nonceHit := false - if _, ok := unconfirmedDb.parallel.addrStateChangesInSlot[addr]; ok { - nonceHit = true - } else if _, ok := unconfirmedDb.parallel.nonceChangesInSlot[addr]; ok { - nonceHit = true - } - if !nonceHit { - // nonce refer not hit, try next unconfirmedDb - continue - } - // nonce hit, return the nonce - obj := unconfirmedDb.parallel.dirtiedStateObjectsInSlot[addr] - if obj == nil { - // could not exist, if it is changed but reverted - // fixme: revert should remove the change record - log.Debug("Get nonce from UnconfirmedDB, changed but object not exist, ", - "txIndex", s.txIndex, "referred txIndex", i, "addr", addr) - continue - } - nonce := obj.Nonce() - // deleted object with nonce == 0 - if obj.deleted { - nonce = 0 - } - return nonce, true - } - } - return 0, false +// BlockHash returns the current block hash set by Prepare. +func (s *StateDB) BlockHash() common.Hash { + return s.bhash } -// Similar to getBalanceFromUnconfirmedDB -// It is not only for code, but also codeHash and codeSize, we return the *StateObject for convienence. -func (s *StateDB) getCodeFromUnconfirmedDB(addr common.Address) ([]byte, bool) { - if addr == s.parallel.systemAddress { - // never get systemaddress from unconfirmed DB - return nil, false - } +// BaseTxIndex returns the tx index that slot db based. +func (s *StateDB) BaseTxIndex() int { + return s.parallel.baseTxIndex +} - for i := s.txIndex - 1; i > s.parallel.baseTxIndex; i-- { - if db, ok := s.parallel.unconfirmedDBInShot[i]; ok { - codeHit := false - if _, exist := db.parallel.addrStateChangesInSlot[addr]; exist { - codeHit = true - } - if _, exist := db.parallel.codeChangesInSlot[addr]; exist { - codeHit = true - } - if !codeHit { - // try next unconfirmedDb - continue - } - obj := db.parallel.dirtiedStateObjectsInSlot[addr] - if obj == nil { - // could not exist, if it is changed but reverted - // fixme: revert should remove the change record - log.Debug("Get code from UnconfirmedDB, changed but object not exist, ", - "txIndex", s.txIndex, "referred txIndex", i, "addr", addr) - continue - } - code := obj.Code(s.db) - if obj.deleted { - code = nil - } - return code, true - } +func (s *StateDB) GetCode(addr common.Address) []byte { + log.Debug("StateDB GetCode", "SlotxIndex", s.parallel.SlotIndex, "txIndex", s.TxIndex()) + + stateObject := s.getStateObject(addr) + var code []byte + if stateObject != nil { + code = stateObject.Code(s.db) } - return nil, false + return code } -// Similar to getCodeFromUnconfirmedDB -// but differ when address is deleted or not exist -func (s *StateDB) getCodeHashFromUnconfirmedDB(addr common.Address) (common.Hash, bool) { - if addr == s.parallel.systemAddress { - // never get systemaddress from unconfirmed DB - return common.Hash{}, false +func (s *StateDB) GetCodeSize(addr common.Address) int { + log.Debug("StateDB GetCodeSize", "SlotxIndex", s.parallel.SlotIndex, "txIndex", s.TxIndex()) + + var codeSize int = 0 + stateObject := s.getStateObject(addr) + if stateObject != nil { + codeSize = stateObject.CodeSize(s.db) } + return codeSize +} - for i := s.txIndex - 1; i > s.parallel.baseTxIndex; i-- { - if db, ok := s.parallel.unconfirmedDBInShot[i]; ok { - hashHit := false - if _, exist := db.parallel.addrStateChangesInSlot[addr]; exist { - hashHit = true - } - if _, exist := db.parallel.codeChangesInSlot[addr]; exist { - hashHit = true - } - if !hashHit { - // try next unconfirmedDb - continue - } +// return value of GetCodeHash: +// - common.Hash{}: the address does not exist +// - emptyCodeHash: the address exist, but code is empty +// - others: the address exist, and code is not empty +func (s *StateDB) GetCodeHash(addr common.Address) common.Hash { + log.Debug("StateDB GetCodeHash", "SlotxIndex", s.parallel.SlotIndex, "txIndex", s.TxIndex()) - obj := db.parallel.dirtiedStateObjectsInSlot[addr] - if obj == nil { - // could not exist, if it is changed but reverted - // fixme: revert should remove the change record - log.Debug("Get codeHash from UnconfirmedDB, changed but object not exist, ", - "txIndex", s.txIndex, "referred txIndex", i, "addr", addr) - continue - } - codeHash := common.Hash{} - if !obj.deleted { - codeHash = common.BytesToHash(obj.CodeHash()) - } - return codeHash, true - } + stateObject := s.getStateObject(addr) + codeHash := common.Hash{} + if stateObject != nil { + codeHash = common.BytesToHash(stateObject.CodeHash()) } - return common.Hash{}, false + return codeHash } -// Similar to getCodeFromUnconfirmedDB -// It is for address state check of: Exist(), Empty() and HasSuicided() -// Since the unconfirmed DB should have done Finalise() with `deleteEmptyObjects = true` -// If the dirty address is empty or suicided, it will be marked as deleted, so we only need to return `deleted` or not. -func (s *StateDB) getAddrStateFromUnconfirmedDB(addr common.Address) (bool, bool) { - if addr == s.parallel.systemAddress { - // never get systemaddress from unconfirmed DB - return false, false +// GetState retrieves a value from the given account's storage trie. +func (s *StateDB) GetState(addr common.Address, hash common.Hash) common.Hash { + log.Debug("StateDB GetState", "SlotxIndex", s.parallel.SlotIndex, "txIndex", s.TxIndex()) + stateObject := s.getStateObject(addr) + val := common.Hash{} + if stateObject != nil { + val = stateObject.GetState(s.db, hash) } + return val +} - // check the unconfirmed DB with range: baseTxIndex -> txIndex -1(previous tx) - for i := s.txIndex - 1; i > s.parallel.baseTxIndex; i-- { - if db, ok := s.parallel.unconfirmedDBInShot[i]; ok { - if exist, ok := db.parallel.addrStateChangesInSlot[addr]; ok { - if _, ok := db.parallel.dirtiedStateObjectsInSlot[addr]; !ok { - // could not exist, if it is changed but reverted - // fixme: revert should remove the change record - log.Debug("Get addr State from UnconfirmedDB, changed but object not exist, ", - "txIndex", s.txIndex, "referred txIndex", i, "addr", addr) - continue - } +// GetProof returns the Merkle proof for a given account. +func (s *StateDB) GetProof(addr common.Address) ([][]byte, error) { + return s.GetProofByHash(crypto.Keccak256Hash(addr.Bytes())) +} - return exist, true - } - } +// GetProofByHash returns the Merkle proof for a given account. +func (s *StateDB) GetProofByHash(addrHash common.Hash) ([][]byte, error) { + var proof proofList + if _, err := s.Trie(); err != nil { + return nil, err } - return false, false + err := s.trie.Prove(addrHash[:], 0, &proof) + return proof, err } -func (s *StateDB) getKVFromUnconfirmedDB(addr common.Address, key common.Hash) (common.Hash, bool) { - // check the unconfirmed DB with range: baseTxIndex -> txIndex -1(previous tx) - for i := s.txIndex - 1; i > s.parallel.baseTxIndex; i-- { - if db, ok := s.parallel.unconfirmedDBInShot[i]; ok { - if obj, ok := db.parallel.dirtiedStateObjectsInSlot[addr]; ok { // if deleted on merge, can get from main StateDB, ok but fixme: concurrent safe - if obj.deleted { - return common.Hash{}, true - } - if _, ok := db.parallel.kvChangesInSlot[addr]; ok { - if val, exist := obj.dirtyStorage.GetValue(key); exist { - return val, true - } - if val, exist := obj.pendingStorage.GetValue(key); exist { // fixme: can be removed - log.Error("Get KV from Unconfirmed StateDB, in pending", - "my txIndex", s.txIndex, "DB's txIndex", i, "addr", addr, - "key", key, "val", val) - return val, true - } - } - } - } +// GetStorageProof returns the Merkle proof for given storage slot. +func (s *StateDB) GetStorageProof(a common.Address, key common.Hash) ([][]byte, error) { + var proof proofList + trie := s.StorageTrie(a) + if trie == nil { + return proof, errors.New("storage trie for requested address does not exist") } - return common.Hash{}, false + err := trie.Prove(crypto.Keccak256(key.Bytes()), 0, &proof) + return proof, err } -func (s *StateDB) getStateObjectFromUnconfirmedDB(addr common.Address) (*StateObject, bool) { - // check the unconfirmed DB with range: baseTxIndex -> txIndex -1(previous tx) - for i := s.txIndex - 1; i > s.parallel.baseTxIndex; i-- { - if db, ok := s.parallel.unconfirmedDBInShot[i]; ok { - if obj, ok := db.parallel.dirtiedStateObjectsInSlot[addr]; ok { // if deleted on merge, can get from main StateDB, ok but fixme: concurrent safe - return obj, true - } - } +// GetStorageProofByHash returns the Merkle proof for given storage slot. +func (s *StateDB) GetStorageProofByHash(a common.Address, key common.Hash) ([][]byte, error) { + var proof proofList + trie := s.StorageTrie(a) + if trie == nil { + return proof, errors.New("storage trie for requested address does not exist") } - return nil, false + err := trie.Prove(crypto.Keccak256(key.Bytes()), 0, &proof) + return proof, err } -// Exist reports whether the given account address exists in the state. -// Notably this also returns true for suicided accounts. -func (s *StateDB) Exist(addr common.Address) bool { - if s.parallel.isSlotDB { - // 1.Try to get from dirty - if obj, ok := s.parallel.dirtiedStateObjectsInSlot[addr]; ok { - // dirty object should not be deleted, since deleted is only flagged on finalise - // and if it is suicided in contract call, suicide is taken as exist until it is finalised - // todo: add a check here, to be removed later - if obj.deleted || obj.suicided { - log.Error("Exist in dirty, but marked as deleted or suicided", - "txIndex", s.txIndex, "baseTxIndex:", s.parallel.baseTxIndex) - } - return true - } - // 2.Try to get from uncomfirmed & main DB - // 2.1 Already read before - if exist, ok := s.parallel.addrStateReadsInSlot[addr]; ok { - return exist - } - // 2.2 Try to get from unconfirmed DB if exist - if exist, ok := s.getAddrStateFromUnconfirmedDB(addr); ok { - s.parallel.addrStateReadsInSlot[addr] = exist // update and cache - return exist - } - } - // 3.Try to get from main StateDB - exist := s.getStateObjectNoSlot(addr) != nil - if s.parallel.isSlotDB { - s.parallel.addrStateReadsInSlot[addr] = exist // update and cache +// GetCommittedState retrieves a value from the given account's committed storage trie. +func (s *StateDB) GetCommittedState(addr common.Address, hash common.Hash) common.Hash { + log.Debug("StateDB GetCommittedState", "SlotxIndex", s.parallel.SlotIndex, "txIndex", s.TxIndex()) + stateObject := s.getStateObject(addr) + val := common.Hash{} + if stateObject != nil { + val = stateObject.GetCommittedState(s.db, hash) } - return exist + return val } -// Empty returns whether the state object is either non-existent -// or empty according to the EIP161 specification (balance = nonce = code = 0) -func (s *StateDB) Empty(addr common.Address) bool { - if s.parallel.isSlotDB { - // 1.Try to get from dirty - if obj, ok := s.parallel.dirtiedStateObjectsInSlot[addr]; ok { - // dirty object is light copied and fixup on need, - // empty could be wrong, except it is created with this TX - if _, ok := s.parallel.addrStateChangesInSlot[addr]; ok { - return obj.empty() - } - // so we have to check it manually - // empty means: Nonce == 0 && Balance == 0 && CodeHash == emptyCodeHash - if s.GetBalance(addr).Sign() != 0 { // check balance first, since it is most likely not zero - return false - } - if s.GetNonce(addr) != 0 { - return false - } - codeHash := s.GetCodeHash(addr) - return bytes.Equal(codeHash.Bytes(), emptyCodeHash) // code is empty, the object is empty - } - // 2.Try to get from uncomfirmed & main DB - // 2.1 Already read before - if exist, ok := s.parallel.addrStateReadsInSlot[addr]; ok { - // exist means not empty - return !exist - } - // 2.2 Try to get from unconfirmed DB if exist - if exist, ok := s.getAddrStateFromUnconfirmedDB(addr); ok { - s.parallel.addrStateReadsInSlot[addr] = exist // update and cache - return !exist - } - } +// Database retrieves the low level database supporting the lower level trie ops. +func (s *StateDB) Database() Database { + return s.db +} - so := s.getStateObjectNoSlot(addr) - empty := (so == nil || so.empty()) - if s.parallel.isSlotDB { - s.parallel.addrStateReadsInSlot[addr] = !empty // update and cache +// StorageTrie returns the storage trie of an account. +// The return value is a copy and is nil for non-existent accounts. +func (s *StateDB) StorageTrie(addr common.Address) Trie { + stateObject := s.getStateObject(addr) + if stateObject == nil { + return nil } - return empty + cpy := stateObject.deepCopy(s) + cpy.updateTrie(s.db) + return cpy.getTrie(s.db) } -// GetBalance retrieves the balance from the given address or 0 if object not found -// GetFrom the dirty list => from unconfirmed DB => get from main stateDB -func (s *StateDB) GetBalance(addr common.Address) *big.Int { - if s.parallel.isSlotDB { - if addr == s.parallel.systemAddress { - s.parallel.systemAddressOpsCount++ - } - // 1.Try to get from dirty - if _, ok := s.parallel.balanceChangesInSlot[addr]; ok { - if obj, ok := s.parallel.dirtiedStateObjectsInSlot[addr]; ok { - // on balance fixup, addr may not exist in dirtiedStateObjectsInSlot - // we intend to fixup balance based on unconfirmed DB or main DB - return obj.Balance() - } - } - // 2.Try to get from uncomfirmed DB or main DB - // 2.1 Already read before - if balance, ok := s.parallel.balanceReadsInSlot[addr]; ok { - return balance - } - // 2.2 Try to get from unconfirmed DB if exist - if balance := s.getBalanceFromUnconfirmedDB(addr); balance != nil { - s.parallel.balanceReadsInSlot[addr] = balance - return balance - } - } - // 3. Try to get from main StateObejct - balance := common.Big0 - stateObject := s.getStateObjectNoSlot(addr) +func (s *StateDB) HasSuicided(addr common.Address) bool { + stateObject := s.getStateObject(addr) if stateObject != nil { - balance = stateObject.Balance() - } - if s.parallel.isSlotDB { - s.parallel.balanceReadsInSlot[addr] = balance + return stateObject.suicided } - return balance + return false } -func (s *StateDB) GetNonce(addr common.Address) uint64 { - if s.parallel.isSlotDB { - // 1.Try to get from dirty - if _, ok := s.parallel.nonceChangesInSlot[addr]; ok { - if obj, ok := s.parallel.dirtiedStateObjectsInSlot[addr]; ok { - // on nonce fixup, addr may not exist in dirtiedStateObjectsInSlot - // we intend to fixup nonce based on unconfirmed DB or main DB - return obj.Nonce() - } - } - // 2.Try to get from uncomfirmed DB or main DB - // 2.1 Already read before - if nonce, ok := s.parallel.nonceReadsInSlot[addr]; ok { - return nonce - } - // 2.2 Try to get from unconfirmed DB if exist - if nonce, ok := s.getNonceFromUnconfirmedDB(addr); ok { - s.parallel.nonceReadsInSlot[addr] = nonce - return nonce - } - } - // 3.Try to get from main StateDB - var nonce uint64 = 0 - stateObject := s.getStateObjectNoSlot(addr) +/* + * SETTERS + */ + +// AddBalance adds amount to the account associated with addr. +func (s *StateDB) AddBalance(addr common.Address, amount *big.Int) { + log.Debug("StateDB AddBalance", "SlotxIndex", s.parallel.SlotIndex, "txIndex", s.TxIndex()) + + stateObject := s.GetOrNewStateObject(addr) if stateObject != nil { - nonce = stateObject.Nonce() - } - if s.parallel.isSlotDB { - s.parallel.nonceReadsInSlot[addr] = nonce + stateObject.AddBalance(amount) } - - return nonce } -// TxIndex returns the current transaction index set by Prepare. -func (s *StateDB) TxIndex() int { - return s.txIndex -} +// SubBalance subtracts amount from the account associated with addr. +func (s *StateDB) SubBalance(addr common.Address, amount *big.Int) { + log.Debug("StateDB SubBalance", "SlotxIndex", s.parallel.SlotIndex, "txIndex", s.TxIndex()) -// BlockHash returns the current block hash set by Prepare. -func (s *StateDB) BlockHash() common.Hash { - return s.bhash + stateObject := s.GetOrNewStateObject(addr) + if stateObject != nil { + stateObject.SubBalance(amount) + } } -// BaseTxIndex returns the tx index that slot db based. -func (s *StateDB) BaseTxIndex() int { - return s.parallel.baseTxIndex -} +func (s *StateDB) SetBalance(addr common.Address, amount *big.Int) { + log.Debug("StateDB SetBalance", "SlotxIndex", s.parallel.SlotIndex, "txIndex", s.TxIndex()) -func (s *StateDB) IsParallelReadsValid() bool { - slotDB := s - if !slotDB.parallel.isSlotDB { - log.Error("IsSlotDBReadsValid slotDB should be slot DB", "SlotIndex", slotDB.parallel.SlotIndex, "txIndex", slotDB.txIndex) - return false + stateObject := s.GetOrNewStateObject(addr) + if stateObject != nil { + stateObject.SetBalance(amount) } +} - mainDB := slotDB.parallel.baseStateDB - if mainDB.parallel.isSlotDB { - log.Error("IsSlotDBReadsValid s should be main DB", "SlotIndex", slotDB.parallel.SlotIndex, "txIndex", slotDB.txIndex) - return false - } - // for nonce - for addr, nonceSlot := range slotDB.parallel.nonceReadsInSlot { - nonceMain := mainDB.GetNonce(addr) - if nonceSlot != nonceMain { - log.Debug("IsSlotDBReadsValid nonce read is invalid", "addr", addr, - "nonceSlot", nonceSlot, "nonceMain", nonceMain, "SlotIndex", slotDB.parallel.SlotIndex, - "txIndex", slotDB.txIndex, "baseTxIndex", slotDB.parallel.baseTxIndex) - return false - } - } - // balance - for addr, balanceSlot := range slotDB.parallel.balanceReadsInSlot { - if addr != s.parallel.systemAddress { // skip balance check for system address - balanceMain := mainDB.GetBalance(addr) - if balanceSlot.Cmp(balanceMain) != 0 { - log.Debug("IsSlotDBReadsValid balance read is invalid", "addr", addr, - "balanceSlot", balanceSlot, "balanceMain", balanceMain, "SlotIndex", slotDB.parallel.SlotIndex, - "txIndex", slotDB.txIndex, "baseTxIndex", slotDB.parallel.baseTxIndex) - return false - } - } - } - // check code - for addr, codeSlot := range slotDB.parallel.codeReadsInSlot { - codeMain := mainDB.GetCode(addr) - if !bytes.Equal(codeSlot, codeMain) { - log.Debug("IsSlotDBReadsValid code read is invalid", "addr", addr, - "len codeSlot", len(codeSlot), "len codeMain", len(codeMain), "SlotIndex", slotDB.parallel.SlotIndex, - "txIndex", slotDB.txIndex, "baseTxIndex", slotDB.parallel.baseTxIndex) - return false - } - } - // check codeHash - for addr, codeHashSlot := range slotDB.parallel.codeHashReadsInSlot { - codeHashMain := mainDB.GetCodeHash(addr) - if !bytes.Equal(codeHashSlot.Bytes(), codeHashMain.Bytes()) { - log.Debug("IsSlotDBReadsValid codehash read is invalid", "addr", addr, - "codeHashSlot", codeHashSlot, "codeHashMain", codeHashMain, "SlotIndex", slotDB.parallel.SlotIndex, - "txIndex", slotDB.txIndex, "baseTxIndex", slotDB.parallel.baseTxIndex) - return false - } - } - // check KV - for addr, slotStorage := range slotDB.parallel.kvReadsInSlot { - conflict := false - slotStorage.Range(func(keySlot, valSlot interface{}) bool { - valMain := mainDB.GetState(addr, keySlot.(common.Hash)) - if !bytes.Equal(valSlot.(common.Hash).Bytes(), valMain.Bytes()) { - log.Debug("IsSlotDBReadsValid KV read is invalid", "addr", addr, - "key", keySlot.(common.Hash), "valSlot", valSlot.(common.Hash), - "valMain", valMain, "SlotIndex", slotDB.parallel.SlotIndex, - "txIndex", slotDB.txIndex, "baseTxIndex", slotDB.parallel.baseTxIndex) - conflict = true - return false // return false, Range will be terminated. - } - return true // return true, Range will try next KV - }) - if conflict { - return false - } - } - // addr state check - for addr, stateSlot := range slotDB.parallel.addrStateReadsInSlot { - stateMain := false // addr not exist - if mainDB.getStateObjectNoSlot(addr) != nil { - stateMain = true // addr exist in main DB - } - if stateSlot != stateMain { - // skip addr state check for system address - if addr != s.parallel.systemAddress { - log.Debug("IsSlotDBReadsValid addrState read invalid(true: exist, false: not exist)", - "addr", addr, "stateSlot", stateSlot, "stateMain", stateMain, - "SlotIndex", slotDB.parallel.SlotIndex, - "txIndex", slotDB.txIndex, "baseTxIndex", slotDB.parallel.baseTxIndex) - return false - } - } - } - // snapshot destructs check +func (s *StateDB) SetNonce(addr common.Address, nonce uint64) { + log.Debug("StateDB SetNonce", "SlotxIndex", s.parallel.SlotIndex, "txIndex", s.TxIndex()) - for addr, destructRead := range slotDB.parallel.addrSnapDestructsReadsInSlot { - mainObj := mainDB.getStateObjectNoSlot(addr) - if mainObj == nil { - log.Debug("IsSlotDBReadsValid snapshot destructs read invalid, address should exist", - "addr", addr, "destruct", destructRead, - "SlotIndex", slotDB.parallel.SlotIndex, - "txIndex", slotDB.txIndex, "baseTxIndex", slotDB.parallel.baseTxIndex) - return false - } - _, destructMain := mainDB.snapDestructs[addr] // addr not exist - if destructRead != destructMain { - log.Debug("IsSlotDBReadsValid snapshot destructs read invalid", - "addr", addr, "destructRead", destructRead, "destructMain", destructMain, - "SlotIndex", slotDB.parallel.SlotIndex, - "txIndex", slotDB.txIndex, "baseTxIndex", slotDB.parallel.baseTxIndex) - return false - } + stateObject := s.GetOrNewStateObject(addr) + if stateObject != nil { + stateObject.SetNonce(nonce) } - - return true } -// For most of the transactions, systemAddressOpsCount should be 3: -// one for SetBalance(0) on NewSlotDB() -// the other is for AddBalance(GasFee) at the end. -// (systemAddressOpsCount > 3) means the transaction tries to access systemAddress, in -// this case, we should redo and keep its balance on NewSlotDB() -func (s *StateDB) SystemAddressRedo() bool { - return s.parallel.systemAddressOpsCount > 4 -} +func (s *StateDB) SetCode(addr common.Address, code []byte) { + log.Debug("StateDB SetCode", "SlotxIndex", s.parallel.SlotIndex, "txIndex", s.TxIndex()) -// NeedsRedo returns true if there is any clear reason that we need to redo this transaction -func (s *StateDB) NeedsRedo() bool { - return s.parallel.needsRedo + stateObject := s.GetOrNewStateObject(addr) + if stateObject != nil { + codeHash := crypto.Keccak256Hash(code) + stateObject.SetCode(codeHash, code) + } } -func (s *StateDB) GetCode(addr common.Address) []byte { - if s.parallel.isSlotDB { - // 1.Try to get from dirty - if _, ok := s.parallel.codeChangesInSlot[addr]; ok { - if obj, ok := s.parallel.dirtiedStateObjectsInSlot[addr]; ok { - // on code fixup, addr may not exist in dirtiedStateObjectsInSlot - // we intend to fixup code based on unconfirmed DB or main DB - code := obj.Code(s.db) - return code - } - } - // 2.Try to get from uncomfirmed DB or main DB - // 2.1 Already read before - if code, ok := s.parallel.codeReadsInSlot[addr]; ok { - return code - } - // 2.2 Try to get from unconfirmed DB if exist - if code, ok := s.getCodeFromUnconfirmedDB(addr); ok { - s.parallel.codeReadsInSlot[addr] = code - return code - } - } +func (s *StateDB) SetState(addr common.Address, key, value common.Hash) { + log.Debug("StateDB SetState", "SlotxIndex", s.parallel.SlotIndex, "txIndex", s.TxIndex()) - // 3. Try to get from main StateObejct - stateObject := s.getStateObjectNoSlot(addr) - var code []byte + stateObject := s.GetOrNewStateObject(addr) if stateObject != nil { - code = stateObject.Code(s.db) - } - if s.parallel.isSlotDB { - s.parallel.codeReadsInSlot[addr] = code + stateObject.SetState(s.db, key, value) } - return code } -func (s *StateDB) GetCodeSize(addr common.Address) int { - if s.parallel.isSlotDB { - // 1.Try to get from dirty - if _, ok := s.parallel.codeChangesInSlot[addr]; ok { - if obj, ok := s.parallel.dirtiedStateObjectsInSlot[addr]; ok { - // on code fixup, addr may not exist in dirtiedStateObjectsInSlot - // we intend to fixup code based on unconfirmed DB or main DB - return obj.CodeSize(s.db) - } - } - // 2.Try to get from uncomfirmed DB or main DB - // 2.1 Already read before - if code, ok := s.parallel.codeReadsInSlot[addr]; ok { - return len(code) // len(nil) is 0 too - } - // 2.2 Try to get from unconfirmed DB if exist - if code, ok := s.getCodeFromUnconfirmedDB(addr); ok { - s.parallel.codeReadsInSlot[addr] = code - return len(code) // len(nil) is 0 too - } - } - - // 3. Try to get from main StateObejct - var codeSize int = 0 - var code []byte - stateObject := s.getStateObjectNoSlot(addr) - +// SetStorage replaces the entire storage for the specified account with given +// storage. This function should only be used for debugging. +func (s *StateDB) SetStorage(addr common.Address, storage map[common.Hash]common.Hash) { + stateObject := s.GetOrNewStateObject(addr) // fixme: parallel mode? if stateObject != nil { - code = stateObject.Code(s.db) - codeSize = stateObject.CodeSize(s.db) - } - if s.parallel.isSlotDB { - s.parallel.codeReadsInSlot[addr] = code + stateObject.SetStorage(storage) } - return codeSize } -// return value of GetCodeHash: -// - common.Hash{}: the address does not exist -// - emptyCodeHash: the address exist, but code is empty -// - others: the address exist, and code is not empty -func (s *StateDB) GetCodeHash(addr common.Address) common.Hash { - if s.parallel.isSlotDB { - // 1.Try to get from dirty - if _, ok := s.parallel.codeChangesInSlot[addr]; ok { - if obj, ok := s.parallel.dirtiedStateObjectsInSlot[addr]; ok { - // on code fixup, addr may not exist in dirtiedStateObjectsInSlot - // we intend to fixup balance based on unconfirmed DB or main DB - return common.BytesToHash(obj.CodeHash()) - } - } - // 2.Try to get from uncomfirmed DB or main DB - // 2.1 Already read before - if codeHash, ok := s.parallel.codeHashReadsInSlot[addr]; ok { - return codeHash - } - // 2.2 Try to get from unconfirmed DB if exist - if codeHash, ok := s.getCodeHashFromUnconfirmedDB(addr); ok { - s.parallel.codeHashReadsInSlot[addr] = codeHash - return codeHash +// 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 (s *StateDB) Suicide(addr common.Address) bool { + log.Debug("StateDB Suicide", "SlotxIndex", s.parallel.SlotIndex, "txIndex", s.TxIndex()) + + var stateObject *StateObject + if stateObject == nil { + // 3.Try to get from main StateDB + stateObject = s.getStateObject(addr) + if stateObject == nil { + s.parallel.addrStateReadsInSlot[addr] = false // true: exist, false: deleted + log.Error("Suicide addr not exist", "txIndex", s.txIndex, "addr", addr) + return false } + s.parallel.addrStateReadsInSlot[addr] = true // true: exist, false: deleted } - // 3. Try to get from main StateObejct - stateObject := s.getStateObjectNoSlot(addr) - codeHash := common.Hash{} - if stateObject != nil { - codeHash = common.BytesToHash(stateObject.CodeHash()) - } - if s.parallel.isSlotDB { - s.parallel.codeHashReadsInSlot[addr] = codeHash - } - return codeHash + s.journal.append(suicideChange{ + account: &addr, + prev: stateObject.suicided, // todo: must be false? + prevbalance: new(big.Int).Set(s.GetBalance(addr)), + }) + + stateObject.markSuicided() + stateObject.data.Balance = new(big.Int) + return true } -// GetState retrieves a value from the given account's storage trie. -// For parallel mode wih, get from the state in order: -// -> self dirty, both Slot & MainProcessor -// -> pending of self: Slot on merge -// -> pending of unconfirmed DB -// -> pending of main StateDB -// -> origin -func (s *StateDB) GetState(addr common.Address, hash common.Hash) common.Hash { - if s.parallel.isSlotDB { +// +// Setting, updating & deleting state object methods. +// - // 1.Try to get from dirty - if exist, ok := s.parallel.addrStateChangesInSlot[addr]; ok { - if !exist { - return common.Hash{} - } - obj := s.parallel.dirtiedStateObjectsInSlot[addr] // addr must exist in dirtiedStateObjectsInSlot - return obj.GetState(s.db, hash) - } - if keys, ok := s.parallel.kvChangesInSlot[addr]; ok { - if _, ok := keys[hash]; ok { - obj := s.parallel.dirtiedStateObjectsInSlot[addr] // addr must exist in dirtiedStateObjectsInSlot - return obj.GetState(s.db, hash) - } - } - // 2.Try to get from uncomfirmed DB or main DB - // 2.1 Already read before - if storage, ok := s.parallel.kvReadsInSlot[addr]; ok { - if val, ok := storage.GetValue(hash); ok { - return val - } - } - // 2.2 Try to get from unconfirmed DB if exist - if val, ok := s.getKVFromUnconfirmedDB(addr, hash); ok { - if s.parallel.kvReadsInSlot[addr] == nil { - s.parallel.kvReadsInSlot[addr] = newStorage(false) - } - s.parallel.kvReadsInSlot[addr].StoreValue(hash, val) // update cache - return val - } - } +// updateStateObject writes the given object to the trie. +func (s *StateDB) updateStateObject(obj *StateObject) { + log.Debug("StateDB updateStateObject", "SlotxIndex", s.parallel.SlotIndex, "txIndex", s.TxIndex()) - // 3.Get from main StateDB - stateObject := s.getStateObjectNoSlot(addr) - val := common.Hash{} - if stateObject != nil { - val = stateObject.GetState(s.db, hash) + // 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()) } - if s.parallel.isSlotDB { - if s.parallel.kvReadsInSlot[addr] == nil { - s.parallel.kvReadsInSlot[addr] = newStorage(false) + // Encode the account and update the account trie + addr := obj.Address() + data := obj.encodeData + var err error + if data == nil { + data, err = rlp.EncodeToBytes(obj) + if err != nil { + panic(fmt.Errorf("can't encode object at %x: %v", addr[:], err)) } - s.parallel.kvReadsInSlot[addr].StoreValue(hash, val) // update cache } - return val -} - -// GetProof returns the Merkle proof for a given account. -func (s *StateDB) GetProof(addr common.Address) ([][]byte, error) { - return s.GetProofByHash(crypto.Keccak256Hash(addr.Bytes())) -} - -// GetProofByHash returns the Merkle proof for a given account. -func (s *StateDB) GetProofByHash(addrHash common.Hash) ([][]byte, error) { - var proof proofList - if _, err := s.Trie(); err != nil { - return nil, err + if err = s.trie.TryUpdate(addr[:], data); err != nil { + s.setError(fmt.Errorf("updateStateObject (%x) error: %v", addr[:], err)) } - err := s.trie.Prove(addrHash[:], 0, &proof) - return proof, err } -// GetStorageProof returns the Merkle proof for given storage slot. -func (s *StateDB) GetStorageProof(a common.Address, key common.Hash) ([][]byte, error) { - var proof proofList - trie := s.StorageTrie(a) - if trie == nil { - return proof, errors.New("storage trie for requested address does not exist") +// deleteStateObject removes the given object from the state trie. +func (s *StateDB) deleteStateObject(obj *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 + addr := obj.Address() + if err := s.trie.TryDelete(addr[:]); err != nil { + s.setError(fmt.Errorf("deleteStateObject (%x) error: %v", addr[:], err)) } - err := trie.Prove(crypto.Keccak256(key.Bytes()), 0, &proof) - return proof, err } -// GetStorageProofByHash returns the Merkle proof for given storage slot. -func (s *StateDB) GetStorageProofByHash(a common.Address, key common.Hash) ([][]byte, error) { - var proof proofList - trie := s.StorageTrie(a) - if trie == nil { - return proof, errors.New("storage trie for requested address does not exist") +// getStateObject retrieves a state object given by the address, returning nil if +// the object is not found or was deleted in this execution context. If you need +// to differentiate between non-existent/just-deleted, use getDeletedStateObject. +func (s *StateDB) getStateObject(addr common.Address) *StateObject { + if obj := s.getDeletedStateObject(addr); obj != nil && !obj.deleted { + return obj } - err := trie.Prove(crypto.Keccak256(key.Bytes()), 0, &proof) - return proof, err + return nil } -// GetCommittedState retrieves a value from the given account's committed storage trie. -func (s *StateDB) GetCommittedState(addr common.Address, hash common.Hash) common.Hash { - if s.parallel.isSlotDB { - // 1.No need to get from pending of itself even on merge, since stateobject in SlotDB won't do finalise - // 2.Try to get from uncomfirmed DB or main DB - // KVs in unconfirmed DB can be seen as pending storage - // KVs in main DB are merged from SlotDB and has done finalise() on merge, can be seen as pending storage too. - // 2.1 Already read before - if storage, ok := s.parallel.kvReadsInSlot[addr]; ok { - if val, ok := storage.GetValue(hash); ok { - return val - } - } - // 2.2 Try to get from unconfirmed DB if exist - if val, ok := s.getKVFromUnconfirmedDB(addr, hash); ok { - if s.parallel.kvReadsInSlot[addr] == nil { - s.parallel.kvReadsInSlot[addr] = newStorage(false) - } - s.parallel.kvReadsInSlot[addr].StoreValue(hash, val) // update cache - return val +func (s *StateDB) getStateObjectFromSnapshotOrTrie(addr common.Address) (data *Account, ok bool) { + var err error + // If no live objects are available, attempt to use snapshots + if s.snap != nil { + if metrics.EnabledExpensive { + defer func(start time.Time) { s.SnapshotAccountReads += time.Since(start) }(time.Now()) + } + var acc *snapshot.Account + if acc, err = s.snap.Account(crypto.HashData(s.hasher, addr.Bytes())); err == nil { + if acc == nil { + return nil, false + } + data = &Account{ + Nonce: acc.Nonce, + Balance: acc.Balance, + CodeHash: acc.CodeHash, + Root: common.BytesToHash(acc.Root), + } + if len(data.CodeHash) == 0 { + data.CodeHash = emptyCodeHash + } + if data.Root == (common.Hash{}) { + data.Root = emptyRoot + } } } - // 3. Try to get from main DB - stateObject := s.getStateObjectNoSlot(addr) - val := common.Hash{} - if stateObject != nil { - val = stateObject.GetCommittedState(s.db, hash) - } - if s.parallel.isSlotDB { - if s.parallel.kvReadsInSlot[addr] == nil { - s.parallel.kvReadsInSlot[addr] = newStorage(false) + // If snapshot unavailable or reading from it failed, load from the database + if s.snap == nil || err != nil { + if s.trie == nil { + tr, err := s.db.OpenTrie(s.originalRoot) + if err != nil { + s.setError(fmt.Errorf("failed to open trie tree")) + return nil, false + } + s.trie = tr + } + if metrics.EnabledExpensive { + defer func(start time.Time) { s.AccountReads += time.Since(start) }(time.Now()) + } + enc, err := s.trie.TryGet(addr.Bytes()) + if err != nil { + s.setError(fmt.Errorf("getDeleteStateObject (%x) error: %v", addr.Bytes(), err)) + return nil, false + } + if len(enc) == 0 { + return nil, false + } + data = new(Account) + if err := rlp.DecodeBytes(enc, data); err != nil { + log.Error("Failed to decode state object", "addr", addr, "err", err) + return nil, false } - s.parallel.kvReadsInSlot[addr].StoreValue(hash, val) // update cache } - return val -} - -// Database retrieves the low level database supporting the lower level trie ops. -func (s *StateDB) Database() Database { - return s.db + return data, true } -// StorageTrie returns the storage trie of an account. -// The return value is a copy and is nil for non-existent accounts. -func (s *StateDB) StorageTrie(addr common.Address) Trie { - stateObject := s.getStateObject(addr) - if stateObject == nil { +// getDeletedStateObject is similar to getStateObject, but instead of returning +// nil for a deleted state object, it returns the actual object with the deleted +// flag set. This is needed by the state journal to revert to the correct s- +// destructed object instead of wiping all knowledge about the state object. +func (s *StateDB) getDeletedStateObject(addr common.Address) *StateObject { + // Prefer live objects if any is available + if obj, _ := s.getStateObjectFromStateObjects(addr); obj != nil { + return obj + } + data, ok := s.getStateObjectFromSnapshotOrTrie(addr) + if !ok { return nil } - cpy := stateObject.deepCopy(s) - cpy.updateTrie(s.db) - return cpy.getTrie(s.db) + // Insert into the live set + // if obj, ok := s.loadStateObj(addr); ok { + // fixme: concurrent not safe, merge could update it... + // return obj + //} + obj := newObject(s, s.isParallel, addr, *data) + s.storeStateObj(addr, obj) + return obj } -func (s *StateDB) HasSuicided(addr common.Address) bool { - if s.parallel.isSlotDB { - // 1.Try to get from dirty - if obj, ok := s.parallel.dirtiedStateObjectsInSlot[addr]; ok { - return obj.suicided - } - // 2.Try to get from uncomfirmed - if exist, ok := s.getAddrStateFromUnconfirmedDB(addr); ok { - return !exist - } +// func (s *StateDB) SetStateObject(object *StateObject) { +// s.storeStateObj(object.Address(), object) +// } + +// GetOrNewStateObject retrieves a state object or create a new state object if nil. +// dirtyInSlot -> Unconfirmed DB -> main DB -> snapshot, no? create one +func (s *StateDB) GetOrNewStateObject(addr common.Address) *StateObject { + var stateObject *StateObject = nil + if stateObject == nil { + stateObject = s.getStateObject(addr) } - stateObject := s.getStateObjectNoSlot(addr) - if stateObject != nil { - return stateObject.suicided + if stateObject == nil || stateObject.deleted || stateObject.suicided { + stateObject = s.createObject(addr) } - return false + return stateObject } -/* - * SETTERS - */ +// 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. -// the source mainObj should be got from the main StateDB -// we have to update its nonce, balance, code if they have updated in the unconfirmed DBs -/* -func (s *StateDB) unconfirmedLightCopy(mainObj *StateObject) *StateObject { - newObj := mainObj.lightCopy(s) // copied nonce, balance, code from base DB +// prev is used for CreateAccount to get its balance +// Parallel mode: +// if prev in dirty: revert is ok +// if prev in unconfirmed DB: addr state read record, revert should not put it back +// if prev in main DB: addr state read record, revert should not put it back +// if pre no exist: addr state read record, - // do balance fixup only when it exist in unconfirmed DB - if nonce, ok := s.getNonceFromUnconfirmedDB(mainObj.address); ok { - // code got from unconfirmed DB - newObj.setNonce(nonce) - } +// `prev` is used to handle revert, to recover with the `prev` object +// In Parallel mode, we only need to recover to `prev` in SlotDB, +// a.if it is not in SlotDB, `revert` will remove it from the SlotDB +// b.if it is exist in SlotDB, `revert` will recover to the `prev` in SlotDB +// c.as `snapDestructs` it is the same +func (s *StateDB) createObject(addr common.Address) (newobj *StateObject) { + prev := s.getDeletedStateObject(addr) // Note, prev might have been deleted, we need that! + var prevdestruct bool - // do balance fixup - if balance := s.getBalanceFromUnconfirmedDB(mainObj.address); balance != nil { - // balance got from unconfirmed DB - newObj.setBalance(balance) + if s.snap != nil && prev != nil { + _, prevdestruct = s.snapDestructs[prev.address] + if !prevdestruct { + // To destroy the previous trie node first and update the trie tree + // with the new object on block commit. + s.snapDestructs[prev.address] = struct{}{} + } } - // do code fixup - if codeObj, ok := s.getCodeFromUnconfirmedDB(mainObj.address); ok { - newObj.setCode(crypto.Keccak256Hash(codeObj), codeObj) // fixme: to confirm if we should use "codeObj.Code(db)" - newObj.dirtyCode = false // copy does not make the code dirty, + newobj = newObject(s, s.isParallel, addr, Account{}) + newobj.setNonce(0) // sets the object to dirty + if prev == nil { + s.journal.append(createObjectChange{account: &addr}) + } else { + s.journal.append(resetObjectChange{prev: prev, prevdestruct: prevdestruct}) } - return newObj + + s.storeStateObj(addr, newobj) + return newobj } -*/ -// AddBalance adds amount to the account associated with addr. -func (s *StateDB) AddBalance(addr common.Address, amount *big.Int) { - // if s.parallel.isSlotDB { - // add balance will perform a read operation first - // s.parallel.balanceReadsInSlot[addr] = struct{}{} // fixme: to make the the balance valid, since unconfirmed would refer it. - // if amount.Sign() == 0 { - // if amount == 0, no balance change, but there is still an empty check. - // take this empty check as addr state read(create, suicide, empty delete) - // s.parallel.addrStateReadsInSlot[addr] = struct{}{} - // } - // } +// 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 (s *StateDB) CreateAccount(addr common.Address) { + // no matter it is got from dirty, unconfirmed or main DB + // if addr not exist, preBalance will be common.Big0, it is same as new(big.Int) which + // is the value newObject(), + preBalance := s.GetBalance(addr) + newObj := s.createObject(addr) + newObj.setBalance(new(big.Int).Set(preBalance)) // new big.Int for newObj +} - stateObject := s.GetOrNewStateObject(addr) - if stateObject != nil { - if s.parallel.isSlotDB { - if addr == s.parallel.systemAddress { - s.parallel.systemAddressOpsCount++ - } - // if amount.Sign() != 0 { // todo: to reenable it - if _, ok := s.parallel.dirtiedStateObjectsInSlot[addr]; !ok { - newStateObject := stateObject.lightCopy(s) // light copy from main DB - // do balance fixup from the confirmed DB, it could be more reliable than main DB - balance := s.GetBalance(addr) - newStateObject.setBalance(balance) - // s.parallel.balanceReadsInSlot[addr] = newStateObject.Balance() // could read from main DB or unconfirmed DB - newStateObject.AddBalance(amount) - s.parallel.dirtiedStateObjectsInSlot[addr] = newStateObject - s.parallel.balanceChangesInSlot[addr] = struct{}{} - return - } - // already dirty, make sure the balance if fixed up - // if stateObject.Balance() - if addr != s.parallel.systemAddress { - if stateObject.Balance().Cmp(s.GetBalance(addr)) != 0 { - log.Warn("AddBalance in dirty, but balance is incorrect", "txIndex", s.txIndex, "addr", addr, - "stateObject.Balance()", stateObject.Balance(), "s.GetBalance(addr)", s.GetBalance(addr)) - stateObject.setBalance(s.GetBalance(addr)) - } +func (s *StateDB) ForEachStorage(addr common.Address, cb func(key, value common.Hash) bool) error { + so := s.getStateObject(addr) + if so == nil { + return nil + } + it := trie.NewIterator(so.getTrie(s.db).NodeIterator(nil)) + + for it.Next() { + key := common.BytesToHash(s.trie.GetKey(it.Key)) + if value, dirty := so.dirtyStorage.GetValue(key); dirty { + if !cb(key, value) { + return nil } + continue } - stateObject.AddBalance(amount) - if s.parallel.isSlotDB { - s.parallel.balanceChangesInSlot[addr] = struct{}{} - } - } -} -// SubBalance subtracts amount from the account associated with addr. -func (s *StateDB) SubBalance(addr common.Address, amount *big.Int) { - // if s.parallel.isSlotDB { - // if amount.Sign() != 0 { - // unlike add, sub 0 balance will not touch empty object - // s.parallel.balanceReadsInSlot[addr] = struct{}{} - // } - // } - stateObject := s.GetOrNewStateObject(addr) - if stateObject != nil { - if s.parallel.isSlotDB { - if addr == s.parallel.systemAddress { - s.parallel.systemAddressOpsCount++ - } - - // if amount.Sign() != 0 { // todo: to reenable it - if _, ok := s.parallel.dirtiedStateObjectsInSlot[addr]; !ok { - newStateObject := stateObject.lightCopy(s) // light copy from main DB - // do balance fixup from the confirmed DB, it could be more reliable than main DB - balance := s.GetBalance(addr) - newStateObject.setBalance(balance) - // s.parallel.balanceReadsInSlot[addr] = newStateObject.Balance() - newStateObject.SubBalance(amount) - s.parallel.balanceChangesInSlot[addr] = struct{}{} - s.parallel.dirtiedStateObjectsInSlot[addr] = newStateObject - return + if len(it.Value) > 0 { + _, content, _, err := rlp.Split(it.Value) + if err != nil { + return err } - // already dirty, make sure the balance if fixed - // if stateObject.Balance() - if addr != s.parallel.systemAddress { - if stateObject.Balance().Cmp(s.GetBalance(addr)) != 0 { - log.Warn("SubBalance in dirty, but balance is incorrect", "txIndex", s.txIndex, "addr", addr, - "stateObject.Balance()", stateObject.Balance(), "s.GetBalance(addr)", s.GetBalance(addr)) - stateObject.setBalance(s.GetBalance(addr)) - } + if !cb(key, common.BytesToHash(content)) { + return nil } } - stateObject.SubBalance(amount) - if s.parallel.isSlotDB { - s.parallel.balanceChangesInSlot[addr] = struct{}{} - } - } + return nil } -func (s *StateDB) SetBalance(addr common.Address, amount *big.Int) { - stateObject := s.GetOrNewStateObject(addr) - if stateObject != nil { - if s.parallel.isSlotDB { - if addr == s.parallel.systemAddress { - s.parallel.systemAddressOpsCount++ - } - if _, ok := s.parallel.dirtiedStateObjectsInSlot[addr]; !ok { - newStateObject := stateObject.lightCopy(s) - // update balance for revert, in case child contract is revertted, - // it should revert to the previous balance - balance := s.GetBalance(addr) - newStateObject.setBalance(balance) - newStateObject.SetBalance(amount) - s.parallel.balanceChangesInSlot[addr] = struct{}{} - s.parallel.dirtiedStateObjectsInSlot[addr] = newStateObject - return - } +// Copy creates a deep, independent copy of the state. +// Snapshots of the copied state cannot be applied to the copy. +func (s *StateDB) Copy() *StateDB { + // Copy all the basic fields, initialize the memory ones + state := &StateDB{ + db: s.db, + trie: s.db.CopyTrie(s.trie), + stateObjects: make(map[common.Address]*StateObject, len(s.journal.dirties)), + stateObjectsPending: make(map[common.Address]struct{}, len(s.stateObjectsPending)), + stateObjectsDirty: make(map[common.Address]struct{}, len(s.journal.dirties)), + storagePool: s.storagePool, + refund: s.refund, + logs: make(map[common.Hash][]*types.Log, len(s.logs)), + logSize: s.logSize, + preimages: make(map[common.Hash][]byte, len(s.preimages)), + journal: newJournal(), + hasher: crypto.NewKeccakState(), + parallel: ParallelState{}, + } + // Copy the dirty states, logs, and preimages + for addr := range s.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 := s.getStateObjectFromStateObjects(addr); exist { + // Even though the original object is dirty, we are not copying the journal, + // so we need to make sure that anyside effect the journal would have caused + // during a commit (or similar op) is already applied to the copy. + state.storeStateObj(addr, object.deepCopy(state)) + state.stateObjectsDirty[addr] = struct{}{} // Mark the copy dirty to force internal (code/state) commits + state.stateObjectsPending[addr] = struct{}{} // Mark the copy pending to force external (account) commits } - stateObject.SetBalance(amount) - if s.parallel.isSlotDB { - s.parallel.balanceChangesInSlot[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 s.stateObjectsPending { + if _, exist := state.getStateObjectFromStateObjects(addr); !exist { + object, _ := s.getStateObjectFromStateObjects(addr) + state.storeStateObj(addr, object.deepCopy(state)) } + state.stateObjectsPending[addr] = struct{}{} } -} - -func (s *StateDB) SetNonce(addr common.Address, nonce uint64) { - stateObject := s.GetOrNewStateObject(addr) - if stateObject != nil { - if s.parallel.isSlotDB { - if _, ok := s.parallel.dirtiedStateObjectsInSlot[addr]; !ok { - newStateObject := stateObject.lightCopy(s) - noncePre := s.GetNonce(addr) - newStateObject.setNonce(noncePre) // nonce fixup - newStateObject.SetNonce(nonce) - s.parallel.nonceChangesInSlot[addr] = struct{}{} - s.parallel.dirtiedStateObjectsInSlot[addr] = newStateObject - return - } - noncePre := s.GetNonce(addr) - stateObject.setNonce(noncePre) // nonce fixup + for addr := range s.stateObjectsDirty { + if _, exist := state.getStateObjectFromStateObjects(addr); !exist { + object, _ := s.getStateObjectFromStateObjects(addr) + state.storeStateObj(addr, object.deepCopy(state)) } - stateObject.SetNonce(nonce) - if s.parallel.isSlotDB { - s.parallel.nonceChangesInSlot[addr] = struct{}{} + state.stateObjectsDirty[addr] = struct{}{} + } + for hash, logs := range s.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 s.preimages { + state.preimages[hash] = preimage + } + // Do we need to copy the access list? In practice: No. At the start of a + // transaction, the access list is empty. In practice, we only ever copy state + // _between_ transactions/blocks, never in the middle of a transaction. + // However, it doesn't cost us much to copy an empty list, so we do it anyway + // to not blow up if we ever decide copy it in the middle of a transaction + if s.accessList != nil { + state.accessList = s.accessList.Copy() } -} -func (s *StateDB) SetCode(addr common.Address, code []byte) { - stateObject := s.GetOrNewStateObject(addr) - if stateObject != nil { - codeHash := crypto.Keccak256Hash(code) - if s.parallel.isSlotDB { - if _, ok := s.parallel.dirtiedStateObjectsInSlot[addr]; !ok { - newStateObject := stateObject.lightCopy(s) - codePre := s.GetCode(addr) // code fixup - codeHashPre := crypto.Keccak256Hash(codePre) - newStateObject.setCode(codeHashPre, codePre) - - newStateObject.SetCode(codeHash, code) - s.parallel.dirtiedStateObjectsInSlot[addr] = newStateObject - s.parallel.codeChangesInSlot[addr] = struct{}{} - return - } - codePre := s.GetCode(addr) // code fixup - codeHashPre := crypto.Keccak256Hash(codePre) - stateObject.setCode(codeHashPre, codePre) + // If there's a prefetcher running, make an inactive copy of it that can + // only access data but does not actively preload (since the user will not + // know that they need to explicitly terminate an active copy). + if s.prefetcher != nil { + state.prefetcher = s.prefetcher.copy() + } + if s.snaps != nil { + // In order for the miner to be able to use and make additions + // to the snapshot tree, we need to copy that aswell. + // Otherwise, any block mined by ourselves will cause gaps in the tree, + // and force the miner to operate trie-backed only + state.snaps = s.snaps + state.snap = s.snap + // deep copy needed + state.snapDestructs = make(map[common.Address]struct{}) + for k, v := range s.snapDestructs { + state.snapDestructs[k] = v } - stateObject.SetCode(codeHash, code) - if s.parallel.isSlotDB { - s.parallel.codeChangesInSlot[addr] = struct{}{} + state.snapAccounts = make(map[common.Address][]byte) + for k, v := range s.snapAccounts { + state.snapAccounts[k] = v } - } -} - -func (s *StateDB) SetState(addr common.Address, key, value common.Hash) { - stateObject := s.GetOrNewStateObject(addr) // attention: if StateObject's lightCopy, its storage is only a part of the full storage, - if stateObject != nil { - if s.parallel.isSlotDB { - if s.parallel.baseTxIndex+1 == s.txIndex { - // we check if state is unchanged - // only when current transaction is the next transaction to be committed - // fixme: there is a bug, block: 14,962,284, - // stateObject is in dirty (light copy), but the key is in mainStateDB - // stateObject dirty -> committed, will skip mainStateDB dirty - if s.GetState(addr, key) == value { - log.Debug("Skip set same state", "baseTxIndex", s.parallel.baseTxIndex, - "txIndex", s.txIndex, "addr", addr, - "key", key, "value", value) - return - } - } - - if s.parallel.kvChangesInSlot[addr] == nil { - s.parallel.kvChangesInSlot[addr] = make(StateKeys) // make(Storage, defaultNumOfSlots) - } - - if _, ok := s.parallel.dirtiedStateObjectsInSlot[addr]; !ok { - newStateObject := stateObject.lightCopy(s) - newStateObject.SetState(s.db, key, value) - s.parallel.dirtiedStateObjectsInSlot[addr] = newStateObject - return + state.snapStorage = make(map[common.Address]map[string][]byte) + for k, v := range s.snapStorage { + temp := make(map[string][]byte) + for kk, vv := range v { + temp[kk] = vv } - // do State Update + state.snapStorage[k] = temp } - stateObject.SetState(s.db, key, value) } + return state } -// SetStorage replaces the entire storage for the specified account with given -// storage. This function should only be used for debugging. -func (s *StateDB) SetStorage(addr common.Address, storage map[common.Hash]common.Hash) { - stateObject := s.GetOrNewStateObject(addr) // fixme: parallel mode? - if stateObject != nil { - stateObject.SetStorage(storage) - } +/* +var addressStructPool = sync.Pool{ + New: func() interface{} { return make(map[common.Address]struct{}, defaultNumOfSlots) }, } -// 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 (s *StateDB) Suicide(addr common.Address) bool { - var stateObject *StateObject - if s.parallel.isSlotDB { - // 1.Try to get from dirty, it could be suicided inside of contract call - stateObject = s.parallel.dirtiedStateObjectsInSlot[addr] - if stateObject == nil { - // 2.Try to get from uncomfirmed, if deleted return false, since the address does not exist - if obj, ok := s.getStateObjectFromUnconfirmedDB(addr); ok { - stateObject = obj - s.parallel.addrStateReadsInSlot[addr] = !stateObject.deleted // true: exist, false: deleted - if stateObject.deleted { - log.Error("Suicide addr alreay deleted in confirmed DB", "txIndex", s.txIndex, "addr", addr) - return false - } - } - } - } - if stateObject == nil { - // 3.Try to get from main StateDB - stateObject = s.getStateObjectNoSlot(addr) - if stateObject == nil { - s.parallel.addrStateReadsInSlot[addr] = false // true: exist, false: deleted - log.Error("Suicide addr not exist", "txIndex", s.txIndex, "addr", addr) - return false +var journalPool = sync.Pool{ + New: func() interface{} { + return &journal{ + dirties: make(map[common.Address]int, defaultNumOfSlots), + entries: make([]journalEntry, 0, defaultNumOfSlots), } - s.parallel.addrStateReadsInSlot[addr] = true // true: exist, false: deleted - } + }, +} - s.journal.append(suicideChange{ - account: &addr, - prev: stateObject.suicided, // todo: must be false? - prevbalance: new(big.Int).Set(s.GetBalance(addr)), - }) +var stateKeysPool = sync.Pool{ + New: func() interface{} { return make(map[common.Address]StateKeys, defaultNumOfSlots) }, +} - if s.parallel.isSlotDB { - if _, ok := s.parallel.dirtiedStateObjectsInSlot[addr]; !ok { - // do copy-on-write for suicide "write" - newStateObject := stateObject.lightCopy(s) - newStateObject.markSuicided() - newStateObject.data.Balance = new(big.Int) - s.parallel.dirtiedStateObjectsInSlot[addr] = newStateObject - s.parallel.addrStateChangesInSlot[addr] = false // false: the address does not exist any more, - // s.parallel.nonceChangesInSlot[addr] = struct{}{} - s.parallel.balanceChangesInSlot[addr] = struct{}{} - s.parallel.codeChangesInSlot[addr] = struct{}{} - // s.parallel.kvChangesInSlot[addr] = make(StateKeys) // all key changes are discarded - return true - } - s.parallel.addrStateChangesInSlot[addr] = false // false: the address does not exist any more, - s.parallel.balanceChangesInSlot[addr] = struct{}{} - s.parallel.codeChangesInSlot[addr] = struct{}{} - } +var stateObjectsPool = sync.Pool{ + New: func() interface{} { return make(map[common.Address]*StateObject, defaultNumOfSlots) }, +} - stateObject.markSuicided() - stateObject.data.Balance = new(big.Int) - return true +var balancePool = sync.Pool{ + New: func() interface{} { return make(map[common.Address]*big.Int, defaultNumOfSlots) }, } -// -// Setting, updating & deleting state object methods. -// +var snapAccountPool = sync.Pool{ + New: func() interface{} { return make(map[common.Address][]byte, defaultNumOfSlots) }, +} -// updateStateObject writes the given object to the trie. -func (s *StateDB) updateStateObject(obj *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 := obj.Address() - data := obj.encodeData - var err error - if data == nil { - data, err = rlp.EncodeToBytes(obj) - if err != nil { - panic(fmt.Errorf("can't encode object at %x: %v", addr[:], err)) - } - } - if err = s.trie.TryUpdate(addr[:], data); err != nil { - s.setError(fmt.Errorf("updateStateObject (%x) error: %v", addr[:], err)) - } +var snapStoragePool = sync.Pool{ + New: func() interface{} { return make(map[common.Address]map[string][]byte, defaultNumOfSlots) }, } -// deleteStateObject removes the given object from the state trie. -func (s *StateDB) deleteStateObject(obj *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 - addr := obj.Address() - if err := s.trie.TryDelete(addr[:]); err != nil { - s.setError(fmt.Errorf("deleteStateObject (%x) error: %v", addr[:], err)) - } +var snapStorageValuePool = sync.Pool{ + New: func() interface{} { return make(map[string][]byte, defaultNumOfSlots) }, } -// getStateObject retrieves a state object given by the address, returning nil if -// the object is not found or was deleted in this execution context. If you need -// to differentiate between non-existent/just-deleted, use getDeletedStateObject. -// fixme: avoid getStateObjectNoSlot, may be we define a new struct SlotDB which inherit StateDB -func (s *StateDB) getStateObjectNoSlot(addr common.Address) *StateObject { - if obj := s.getDeletedStateObject(addr); obj != nil && !obj.deleted { - return obj - } - return nil +var logsPool = sync.Pool{ + New: func() interface{} { return make(map[common.Hash][]*types.Log, defaultNumOfSlots) }, } -// for parallel execution mode, try to get dirty StateObject in slot first. -func (s *StateDB) getStateObject(addr common.Address) *StateObject { - if s.parallel.isSlotDB { - if obj, ok := s.parallel.dirtiedStateObjectsInSlot[addr]; ok { - return obj - } +func (s *StateDB) SlotDBPutSyncPool() { + // for key := range s.parallel.codeReadsInSlot { + // delete(s.parallel.codeReadsInSlot, key) + //} + //addressStructPool.Put(s.parallel.codeReadsInSlot) + + for key := range s.parallel.codeChangesInSlot { + delete(s.parallel.codeChangesInSlot, key) } + addressStructPool.Put(s.parallel.codeChangesInSlot) - if obj := s.getDeletedStateObject(addr); obj != nil && !obj.deleted { - return obj + for key := range s.parallel.balanceChangesInSlot { + delete(s.parallel.balanceChangesInSlot, key) } - return nil -} + addressStructPool.Put(s.parallel.balanceChangesInSlot) -// getDeletedStateObject is similar to getStateObject, but instead of returning -// nil for a deleted state object, it returns the actual object with the deleted -// flag set. This is needed by the state journal to revert to the correct s- -// destructed object instead of wiping all knowledge about the state object. -func (s *StateDB) getDeletedStateObject(addr common.Address) *StateObject { - // Prefer live objects if any is available - if obj, _ := s.getStateObjectFromStateObjects(addr); obj != nil { - return obj + for key := range s.parallel.balanceReadsInSlot { + delete(s.parallel.balanceReadsInSlot, key) } - // If no live objects are available, attempt to use snapshots - var ( - data *Account - err error - ) - if s.snap != nil { - if metrics.EnabledExpensive { - defer func(start time.Time) { s.SnapshotAccountReads += time.Since(start) }(time.Now()) - } - var acc *snapshot.Account - if acc, err = s.snap.Account(crypto.HashData(s.hasher, addr.Bytes())); err == nil { - if acc == nil { - return nil - } - data = &Account{ - Nonce: acc.Nonce, - Balance: acc.Balance, - CodeHash: acc.CodeHash, - Root: common.BytesToHash(acc.Root), - } - if len(data.CodeHash) == 0 { - data.CodeHash = emptyCodeHash - } - if data.Root == (common.Hash{}) { - data.Root = emptyRoot - } - } + balancePool.Put(s.parallel.balanceReadsInSlot) + + // for key := range s.parallel.addrStateReadsInSlot { + // delete(s.parallel.addrStateReadsInSlot, key) + // } + // addressStructPool.Put(s.parallel.addrStateReadsInSlot) + + for key := range s.parallel.nonceChangesInSlot { + delete(s.parallel.nonceChangesInSlot, key) } - // If snapshot unavailable or reading from it failed, load from the database - if s.snap == nil || err != nil { - if s.trie == nil { - tr, err := s.db.OpenTrie(s.originalRoot) - if err != nil { - s.setError(fmt.Errorf("failed to open trie tree")) - return nil - } - s.trie = tr - } - if metrics.EnabledExpensive { - defer func(start time.Time) { s.AccountReads += time.Since(start) }(time.Now()) - } - enc, err := s.trie.TryGet(addr.Bytes()) - if err != nil { - s.setError(fmt.Errorf("getDeleteStateObject (%x) error: %v", addr.Bytes(), err)) - return nil - } - if len(enc) == 0 { - return nil - } - data = new(Account) - if err := rlp.DecodeBytes(enc, data); err != nil { - log.Error("Failed to decode state object", "addr", addr, "err", err) - return nil - } + addressStructPool.Put(s.parallel.nonceChangesInSlot) + + for key := range s.stateObjectsPending { + delete(s.stateObjectsPending, key) } - // Insert into the live set - // if obj, ok := s.loadStateObj(addr); ok { - // fixme: concurrent not safe, merge could update it... - // return obj - //} - obj := newObject(s, s.isParallel, addr, *data) - s.SetStateObject(obj) - return obj -} + addressStructPool.Put(s.stateObjectsPending) -func (s *StateDB) SetStateObject(object *StateObject) { - s.storeStateObj(object.Address(), object) -} + for key := range s.stateObjectsDirty { + delete(s.stateObjectsDirty, key) + } + addressStructPool.Put(s.stateObjectsDirty) -// GetOrNewStateObject retrieves a state object or create a new state object if nil. -// dirtyInSlot -> Unconfirmed DB -> main DB -> snapshot, no? create one -func (s *StateDB) GetOrNewStateObject(addr common.Address) *StateObject { - var stateObject *StateObject = nil - exist := true - if s.parallel.isSlotDB { - if stateObject, ok := s.parallel.dirtiedStateObjectsInSlot[addr]; ok { - return stateObject - } - stateObject, _ = s.getStateObjectFromUnconfirmedDB(addr) + for key := range s.journal.dirties { + delete(s.journal.dirties, key) } + s.journal.entries = s.journal.entries[:0] + journalPool.Put(s.journal) - if stateObject == nil { - stateObject = s.getStateObjectNoSlot(addr) + for key := range s.parallel.kvChangesInSlot { + delete(s.parallel.kvChangesInSlot, key) } - if stateObject == nil || stateObject.deleted || stateObject.suicided { - stateObject = s.createObject(addr) - exist = false + stateKeysPool.Put(s.parallel.kvChangesInSlot) + + // for key := range s.parallel.kvReadsInSlot { + // delete(s.parallel.kvReadsInSlot, key) + // } + // stateKeysPool.Put(s.parallel.kvReadsInSlot) + + for key := range s.parallel.dirtiedStateObjectsInSlot { + delete(s.parallel.dirtiedStateObjectsInSlot, key) } + stateObjectsPool.Put(s.parallel.dirtiedStateObjectsInSlot) - if s.parallel.isSlotDB { - s.parallel.addrStateReadsInSlot[addr] = exist // true: exist, false: not exist + for key := range s.snapDestructs { + delete(s.snapDestructs, key) } - return stateObject -} + addressStructPool.Put(s.snapDestructs) -// 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. - -// prev is used for CreateAccount to get its balance -// Parallel mode: -// if prev in dirty: revert is ok -// if prev in unconfirmed DB: addr state read record, revert should not put it back -// if prev in main DB: addr state read record, revert should not put it back -// if pre no exist: addr state read record, - -// `prev` is used to handle revert, to recover with the `prev` object -// In Parallel mode, we only need to recover to `prev` in SlotDB, -// a.if it is not in SlotDB, `revert` will remove it from the SlotDB -// b.if it is exist in SlotDB, `revert` will recover to the `prev` in SlotDB -// c.as `snapDestructs` it is the same -func (s *StateDB) createObject(addr common.Address) (newobj *StateObject) { - var prev *StateObject = nil - if s.parallel.isSlotDB { - // do not get from unconfirmed DB, since it will has problem on revert - prev = s.parallel.dirtiedStateObjectsInSlot[addr] - } else { - prev = s.getDeletedStateObject(addr) // Note, prev might have been deleted, we need that! - } - - var prevdestruct bool - - if s.snap != nil && prev != nil { - _, prevdestruct = s.snapDestructs[prev.address] // fixme, record the snapshot read for create Account - if s.parallel.isSlotDB { - s.parallel.addrSnapDestructsReadsInSlot[addr] = prevdestruct - } - if !prevdestruct { - // To destroy the previous trie node first and update the trie tree - // with the new object on block commit. - s.snapDestructs[prev.address] = struct{}{} + for key := range s.snapAccounts { + delete(s.snapAccounts, key) + } + snapAccountPool.Put(s.snapAccounts) + for key, storage := range s.snapStorage { + for key := range storage { + delete(storage, key) } + snapStorageValuePool.Put(storage) + delete(s.snapStorage, key) } - newobj = newObject(s, s.isParallel, addr, Account{}) - newobj.setNonce(0) // sets the object to dirty - if prev == nil { - s.journal.append(createObjectChange{account: &addr}) - } else { - s.journal.append(resetObjectChange{prev: prev, prevdestruct: prevdestruct}) - } - - if s.parallel.isSlotDB { - // s.parallel.dirtiedStateObjectsInSlot[addr] = newobj // would change the bahavior of AddBalance... - s.parallel.addrStateChangesInSlot[addr] = true // the object sis created - s.parallel.nonceChangesInSlot[addr] = struct{}{} - s.parallel.balanceChangesInSlot[addr] = struct{}{} - s.parallel.codeChangesInSlot[addr] = struct{}{} - // notice: all the KVs are cleared if any - s.parallel.kvChangesInSlot[addr] = make(StateKeys) - } else { - s.SetStateObject(newobj) - } - return newobj -} - -// 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 (s *StateDB) CreateAccount(addr common.Address) { - // no matter it is got from dirty, unconfirmed or main DB - // if addr not exist, preBalance will be common.Big0, it is same as new(big.Int) which - // is the value newObject(), - preBalance := s.GetBalance(addr) // parallel balance read will be recorded inside of GetBalance - newObj := s.createObject(addr) - newObj.setBalance(new(big.Int).Set(preBalance)) // new big.Int for newObj -} - -func (s *StateDB) ForEachStorage(addr common.Address, cb func(key, value common.Hash) bool) error { - so := s.getStateObject(addr) - if so == nil { - return nil - } - it := trie.NewIterator(so.getTrie(s.db).NodeIterator(nil)) - - for it.Next() { - key := common.BytesToHash(s.trie.GetKey(it.Key)) - if value, dirty := so.dirtyStorage.GetValue(key); dirty { - if !cb(key, value) { - return nil - } - continue - } + snapStoragePool.Put(s.snapStorage) - if len(it.Value) > 0 { - _, content, _, err := rlp.Split(it.Value) - if err != nil { - return err - } - if !cb(key, common.BytesToHash(content)) { - return nil - } - } + for key := range s.logs { + delete(s.logs, key) } - return nil + logsPool.Put(s.logs) } +*/ +// CopyForSlot copy all the basic fields, initialize the memory ones +func (s *StateDB) CopyForSlot() *ParallelStateDB { + parallel := ParallelState{ + // use base(dispatcher) slot db's stateObjects. + // It is a SyncMap, only readable to slot, not writable + stateObjects: s.parallel.stateObjects, + unconfirmedDBInShot: make(map[int]*ParallelStateDB, 100), -// Copy creates a deep, independent copy of the state. -// Snapshots of the copied state cannot be applied to the copy. -func (s *StateDB) Copy() *StateDB { - // Copy all the basic fields, initialize the memory ones - state := &StateDB{ - db: s.db, - trie: s.db.CopyTrie(s.trie), - stateObjects: make(map[common.Address]*StateObject, len(s.journal.dirties)), - stateObjectsPending: make(map[common.Address]struct{}, len(s.stateObjectsPending)), - stateObjectsDirty: make(map[common.Address]struct{}, len(s.journal.dirties)), - storagePool: s.storagePool, - refund: s.refund, - logs: make(map[common.Hash][]*types.Log, len(s.logs)), - logSize: s.logSize, - preimages: make(map[common.Hash][]byte, len(s.preimages)), - journal: newJournal(), - hasher: crypto.NewKeccakState(), - parallel: ParallelState{}, - } - // Copy the dirty states, logs, and preimages - for addr := range s.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 := s.getStateObjectFromStateObjects(addr); exist { - // Even though the original object is dirty, we are not copying the journal, - // so we need to make sure that anyside effect the journal would have caused - // during a commit (or similar op) is already applied to the copy. - state.storeStateObj(addr, object.deepCopy(state)) + codeReadsInSlot: make(map[common.Address][]byte, 10), // addressStructPool.Get().(map[common.Address]struct{}), + codeHashReadsInSlot: make(map[common.Address]common.Hash), + codeChangesInSlot: make(map[common.Address]struct{}), // addressStructPool.Get().(map[common.Address]struct{}), + kvChangesInSlot: make(map[common.Address]StateKeys), // stateKeysPool.Get().(map[common.Address]StateKeys), + kvReadsInSlot: make(map[common.Address]Storage, 100), // stateKeysPool.Get().(map[common.Address]Storage), + balanceChangesInSlot: make(map[common.Address]struct{}), // addressStructPool.Get().(map[common.Address]struct{}), + balanceReadsInSlot: make(map[common.Address]*big.Int), // addressStructPool.Get().(map[common.Address]struct{}), + addrStateReadsInSlot: make(map[common.Address]bool), // addressStructPool.Get().(map[common.Address]struct{}), + addrStateChangesInSlot: make(map[common.Address]bool), // addressStructPool.Get().(map[common.Address]struct{}), + nonceChangesInSlot: make(map[common.Address]struct{}), // addressStructPool.Get().(map[common.Address]struct{}), + nonceReadsInSlot: make(map[common.Address]uint64), + addrSnapDestructsReadsInSlot: make(map[common.Address]bool), - state.stateObjectsDirty[addr] = struct{}{} // Mark the copy dirty to force internal (code/state) commits - state.stateObjectsPending[addr] = struct{}{} // Mark the copy pending to force external (account) commits - } - } - // 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 s.stateObjectsPending { - if _, exist := state.getStateObjectFromStateObjects(addr); !exist { - object, _ := s.getStateObjectFromStateObjects(addr) - state.storeStateObj(addr, object.deepCopy(state)) - } - state.stateObjectsPending[addr] = struct{}{} - } - for addr := range s.stateObjectsDirty { - if _, exist := state.getStateObjectFromStateObjects(addr); !exist { - object, _ := s.getStateObjectFromStateObjects(addr) - state.storeStateObj(addr, object.deepCopy(state)) - } - state.stateObjectsDirty[addr] = struct{}{} + isSlotDB: true, + dirtiedStateObjectsInSlot: make(map[common.Address]*StateObject), // stateObjectsPool.Get().(map[common.Address]*StateObject), } - for hash, logs := range s.logs { - cpy := make([]*types.Log, len(logs)) - for i, l := range logs { - cpy[i] = new(types.Log) - *cpy[i] = *l - } - state.logs[hash] = cpy + state := &ParallelStateDB{ + StateDB{ + db: s.db, + trie: s.db.CopyTrie(s.trie), + stateObjects: make(map[common.Address]*StateObject), // replaced by parallel.stateObjects in parallel mode + stateObjectsPending: make(map[common.Address]struct{}), // addressStructPool.Get().(map[common.Address]struct{}), + stateObjectsDirty: make(map[common.Address]struct{}), //addressStructPool.Get().(map[common.Address]struct{}), + refund: s.refund, // should be 0 + logs: make(map[common.Hash][]*types.Log, defaultNumOfSlots), // logsPool.Get().(map[common.Hash][]*types.Log), + logSize: 0, + preimages: make(map[common.Hash][]byte, len(s.preimages)), + journal: newJournal(), // journalPool.Get().(*journal), + hasher: crypto.NewKeccakState(), + isParallel: true, + parallel: parallel, + }, } for hash, preimage := range s.preimages { state.preimages[hash] = preimage } - // Do we need to copy the access list? In practice: No. At the start of a - // transaction, the access list is empty. In practice, we only ever copy state - // _between_ transactions/blocks, never in the middle of a transaction. - // However, it doesn't cost us much to copy an empty list, so we do it anyway - // to not blow up if we ever decide copy it in the middle of a transaction - if s.accessList != nil { - state.accessList = s.accessList.Copy() - } - // If there's a prefetcher running, make an inactive copy of it that can - // only access data but does not actively preload (since the user will not - // know that they need to explicitly terminate an active copy). - if s.prefetcher != nil { - state.prefetcher = s.prefetcher.copy() - } if s.snaps != nil { // In order for the miner to be able to use and make additions // to the snapshot tree, we need to copy that aswell. @@ -2138,271 +1254,71 @@ func (s *StateDB) Copy() *StateDB { state.snaps = s.snaps state.snap = s.snap // deep copy needed - state.snapDestructs = make(map[common.Address]struct{}) + state.snapDestructs = make(map[common.Address]struct{}) //addressStructPool.Get().(map[common.Address]struct{}) + s.snapParallelLock.RLock() for k, v := range s.snapDestructs { state.snapDestructs[k] = v } - state.snapAccounts = make(map[common.Address][]byte) + s.snapParallelLock.RUnlock() + // + state.snapAccounts = make(map[common.Address][]byte) // snapAccountPool.Get().(map[common.Address][]byte) for k, v := range s.snapAccounts { state.snapAccounts[k] = v } - state.snapStorage = make(map[common.Address]map[string][]byte) + state.snapStorage = make(map[common.Address]map[string][]byte) // snapStoragePool.Get().(map[common.Address]map[string][]byte) for k, v := range s.snapStorage { - temp := make(map[string][]byte) + temp := make(map[string][]byte) // snapStorageValuePool.Get().(map[string][]byte) for kk, vv := range v { temp[kk] = vv } state.snapStorage[k] = temp } + // trie prefetch should be done by dispacther on StateObject Merge, + // disable it in parallel slot + // state.prefetcher = s.prefetcher } - return state -} -/* -var addressStructPool = sync.Pool{ - New: func() interface{} { return make(map[common.Address]struct{}, defaultNumOfSlots) }, + return state } -var journalPool = sync.Pool{ - New: func() interface{} { - return &journal{ - dirties: make(map[common.Address]int, defaultNumOfSlots), - entries: make([]journalEntry, 0, defaultNumOfSlots), - } - }, +// Snapshot returns an identifier for the current revision of the state. +func (s *StateDB) Snapshot() int { + id := s.nextRevisionId + s.nextRevisionId++ + s.validRevisions = append(s.validRevisions, revision{id, s.journal.length()}) + return id } -var stateKeysPool = sync.Pool{ - New: func() interface{} { return make(map[common.Address]StateKeys, defaultNumOfSlots) }, -} +// RevertToSnapshot reverts all state changes made since the given revision. +func (s *StateDB) RevertToSnapshot(revid int) { + // Find the snapshot in the stack of valid snapshots. + idx := sort.Search(len(s.validRevisions), func(i int) bool { + return s.validRevisions[i].id >= revid + }) + if idx == len(s.validRevisions) || s.validRevisions[idx].id != revid { + panic(fmt.Errorf("revision id %v cannot be reverted", revid)) + } + snapshot := s.validRevisions[idx].journalIndex -var stateObjectsPool = sync.Pool{ - New: func() interface{} { return make(map[common.Address]*StateObject, defaultNumOfSlots) }, + // Replay the journal to undo changes and remove invalidated snapshots + s.journal.revert(s, snapshot) + s.validRevisions = s.validRevisions[:idx] } -var balancePool = sync.Pool{ - New: func() interface{} { return make(map[common.Address]*big.Int, defaultNumOfSlots) }, +// GetRefund returns the current value of the refund counter. +func (s *StateDB) GetRefund() uint64 { + return s.refund } -var snapAccountPool = sync.Pool{ - New: func() interface{} { return make(map[common.Address][]byte, defaultNumOfSlots) }, -} - -var snapStoragePool = sync.Pool{ - New: func() interface{} { return make(map[common.Address]map[string][]byte, defaultNumOfSlots) }, -} - -var snapStorageValuePool = sync.Pool{ - New: func() interface{} { return make(map[string][]byte, defaultNumOfSlots) }, -} - -var logsPool = sync.Pool{ - New: func() interface{} { return make(map[common.Hash][]*types.Log, defaultNumOfSlots) }, -} - -func (s *StateDB) SlotDBPutSyncPool() { - // for key := range s.parallel.codeReadsInSlot { - // delete(s.parallel.codeReadsInSlot, key) - //} - //addressStructPool.Put(s.parallel.codeReadsInSlot) - - for key := range s.parallel.codeChangesInSlot { - delete(s.parallel.codeChangesInSlot, key) - } - addressStructPool.Put(s.parallel.codeChangesInSlot) - - for key := range s.parallel.balanceChangesInSlot { - delete(s.parallel.balanceChangesInSlot, key) - } - addressStructPool.Put(s.parallel.balanceChangesInSlot) - - for key := range s.parallel.balanceReadsInSlot { - delete(s.parallel.balanceReadsInSlot, key) - } - balancePool.Put(s.parallel.balanceReadsInSlot) - - // for key := range s.parallel.addrStateReadsInSlot { - // delete(s.parallel.addrStateReadsInSlot, key) - // } - // addressStructPool.Put(s.parallel.addrStateReadsInSlot) - - for key := range s.parallel.nonceChangesInSlot { - delete(s.parallel.nonceChangesInSlot, key) - } - addressStructPool.Put(s.parallel.nonceChangesInSlot) - - for key := range s.stateObjectsPending { - delete(s.stateObjectsPending, key) - } - addressStructPool.Put(s.stateObjectsPending) - - for key := range s.stateObjectsDirty { - delete(s.stateObjectsDirty, key) - } - addressStructPool.Put(s.stateObjectsDirty) - - for key := range s.journal.dirties { - delete(s.journal.dirties, key) - } - s.journal.entries = s.journal.entries[:0] - journalPool.Put(s.journal) - - for key := range s.parallel.kvChangesInSlot { - delete(s.parallel.kvChangesInSlot, key) - } - stateKeysPool.Put(s.parallel.kvChangesInSlot) - - // for key := range s.parallel.kvReadsInSlot { - // delete(s.parallel.kvReadsInSlot, key) - // } - // stateKeysPool.Put(s.parallel.kvReadsInSlot) - - for key := range s.parallel.dirtiedStateObjectsInSlot { - delete(s.parallel.dirtiedStateObjectsInSlot, key) - } - stateObjectsPool.Put(s.parallel.dirtiedStateObjectsInSlot) - - for key := range s.snapDestructs { - delete(s.snapDestructs, key) - } - addressStructPool.Put(s.snapDestructs) - - for key := range s.snapAccounts { - delete(s.snapAccounts, key) - } - snapAccountPool.Put(s.snapAccounts) - - for key, storage := range s.snapStorage { - for key := range storage { - delete(storage, key) - } - snapStorageValuePool.Put(storage) - delete(s.snapStorage, key) - } - snapStoragePool.Put(s.snapStorage) - - for key := range s.logs { - delete(s.logs, key) - } - logsPool.Put(s.logs) -} -*/ -// CopyForSlot copy all the basic fields, initialize the memory ones -func (s *StateDB) CopyForSlot() *StateDB { - parallel := ParallelState{ - // use base(dispatcher) slot db's stateObjects. - // It is a SyncMap, only readable to slot, not writable - stateObjects: s.parallel.stateObjects, - unconfirmedDBInShot: make(map[int]*StateDB, 100), - - codeReadsInSlot: make(map[common.Address][]byte, 10), // addressStructPool.Get().(map[common.Address]struct{}), - codeHashReadsInSlot: make(map[common.Address]common.Hash), - codeChangesInSlot: make(map[common.Address]struct{}), // addressStructPool.Get().(map[common.Address]struct{}), - kvChangesInSlot: make(map[common.Address]StateKeys), // stateKeysPool.Get().(map[common.Address]StateKeys), - kvReadsInSlot: make(map[common.Address]Storage, 100), // stateKeysPool.Get().(map[common.Address]Storage), - balanceChangesInSlot: make(map[common.Address]struct{}), // addressStructPool.Get().(map[common.Address]struct{}), - balanceReadsInSlot: make(map[common.Address]*big.Int), // addressStructPool.Get().(map[common.Address]struct{}), - addrStateReadsInSlot: make(map[common.Address]bool), // addressStructPool.Get().(map[common.Address]struct{}), - addrStateChangesInSlot: make(map[common.Address]bool), // addressStructPool.Get().(map[common.Address]struct{}), - nonceChangesInSlot: make(map[common.Address]struct{}), // addressStructPool.Get().(map[common.Address]struct{}), - nonceReadsInSlot: make(map[common.Address]uint64), - addrSnapDestructsReadsInSlot: make(map[common.Address]bool), - - isSlotDB: true, - dirtiedStateObjectsInSlot: make(map[common.Address]*StateObject), // stateObjectsPool.Get().(map[common.Address]*StateObject), - } - state := &StateDB{ - db: s.db, - trie: s.db.CopyTrie(s.trie), - stateObjects: make(map[common.Address]*StateObject), // replaced by parallel.stateObjects in parallel mode - stateObjectsPending: make(map[common.Address]struct{}), // addressStructPool.Get().(map[common.Address]struct{}), - stateObjectsDirty: make(map[common.Address]struct{}), //addressStructPool.Get().(map[common.Address]struct{}), - refund: s.refund, // should be 0 - logs: make(map[common.Hash][]*types.Log, defaultNumOfSlots), // logsPool.Get().(map[common.Hash][]*types.Log), - logSize: 0, - preimages: make(map[common.Hash][]byte, len(s.preimages)), - journal: newJournal(), // journalPool.Get().(*journal), - hasher: crypto.NewKeccakState(), - isParallel: true, - parallel: parallel, - } - - for hash, preimage := range s.preimages { - state.preimages[hash] = preimage - } - - if s.snaps != nil { - // In order for the miner to be able to use and make additions - // to the snapshot tree, we need to copy that aswell. - // Otherwise, any block mined by ourselves will cause gaps in the tree, - // and force the miner to operate trie-backed only - state.snaps = s.snaps - state.snap = s.snap - // deep copy needed - state.snapDestructs = make(map[common.Address]struct{}) //addressStructPool.Get().(map[common.Address]struct{}) - s.snapParallelLock.RLock() - for k, v := range s.snapDestructs { - state.snapDestructs[k] = v - } - s.snapParallelLock.RUnlock() - // - state.snapAccounts = make(map[common.Address][]byte) // snapAccountPool.Get().(map[common.Address][]byte) - for k, v := range s.snapAccounts { - state.snapAccounts[k] = v - } - state.snapStorage = make(map[common.Address]map[string][]byte) // snapStoragePool.Get().(map[common.Address]map[string][]byte) - for k, v := range s.snapStorage { - temp := make(map[string][]byte) // snapStorageValuePool.Get().(map[string][]byte) - for kk, vv := range v { - temp[kk] = vv - } - state.snapStorage[k] = temp - } - // trie prefetch should be done by dispacther on StateObject Merge, - // disable it in parallel slot - // state.prefetcher = s.prefetcher - } - return state -} - -// Snapshot returns an identifier for the current revision of the state. -func (s *StateDB) Snapshot() int { - id := s.nextRevisionId - s.nextRevisionId++ - s.validRevisions = append(s.validRevisions, revision{id, s.journal.length()}) - return id -} - -// RevertToSnapshot reverts all state changes made since the given revision. -func (s *StateDB) RevertToSnapshot(revid int) { - // Find the snapshot in the stack of valid snapshots. - idx := sort.Search(len(s.validRevisions), func(i int) bool { - return s.validRevisions[i].id >= revid - }) - if idx == len(s.validRevisions) || s.validRevisions[idx].id != revid { - panic(fmt.Errorf("revision id %v cannot be reverted", revid)) - } - snapshot := s.validRevisions[idx].journalIndex - - // Replay the journal to undo changes and remove invalidated snapshots - s.journal.revert(s, snapshot) - s.validRevisions = s.validRevisions[:idx] -} - -// GetRefund returns the current value of the refund counter. -func (s *StateDB) GetRefund() uint64 { - return s.refund -} - -// GetRefund returns the current value of the refund counter. -func (s *StateDB) WaitPipeVerification() error { - // We need wait for the parent trie to commit - if s.snap != nil { - if valid := s.snap.WaitAndGetVerifyRes(); !valid { - return fmt.Errorf("verification on parent snap failed") - } - } - return nil +// GetRefund returns the current value of the refund counter. +func (s *StateDB) WaitPipeVerification() error { + // We need wait for the parent trie to commit + if s.snap != nil { + if valid := s.snap.WaitAndGetVerifyRes(); !valid { + return fmt.Errorf("verification on parent snap failed") + } + } + return nil } // Finalise finalises the state by removing the s destructed objects and clears @@ -2955,149 +1871,1515 @@ func (s *StateDB) Commit(failPostCommitFunc func(), postCommitFuncs ...func() er return common.Hash{}, nil, r } } - root := s.stateRoot - if s.pipeCommit { - root = s.expectedRoot - } - - return root, diffLayer, nil + root := s.stateRoot + if s.pipeCommit { + root = s.expectedRoot + } + + return root, diffLayer, nil +} + +func (s *StateDB) DiffLayerToSnap(diffLayer *types.DiffLayer) (map[common.Address]struct{}, map[common.Address][]byte, map[common.Address]map[string][]byte, error) { + snapDestructs := make(map[common.Address]struct{}) + snapAccounts := make(map[common.Address][]byte) + snapStorage := make(map[common.Address]map[string][]byte) + + for _, des := range diffLayer.Destructs { + snapDestructs[des] = struct{}{} + } + for _, account := range diffLayer.Accounts { + snapAccounts[account.Account] = account.Blob + } + for _, storage := range diffLayer.Storages { + // should never happen + if len(storage.Keys) != len(storage.Vals) { + return nil, nil, nil, errors.New("invalid diffLayer: length of keys and values mismatch") + } + snapStorage[storage.Account] = make(map[string][]byte, len(storage.Keys)) + n := len(storage.Keys) + for i := 0; i < n; i++ { + snapStorage[storage.Account][storage.Keys[i]] = storage.Vals[i] + } + } + return snapDestructs, snapAccounts, snapStorage, nil +} + +func (s *StateDB) SnapToDiffLayer() ([]common.Address, []types.DiffAccount, []types.DiffStorage) { + destructs := make([]common.Address, 0, len(s.snapDestructs)) + for account := range s.snapDestructs { + destructs = append(destructs, account) + } + accounts := make([]types.DiffAccount, 0, len(s.snapAccounts)) + for accountHash, account := range s.snapAccounts { + accounts = append(accounts, types.DiffAccount{ + Account: accountHash, + Blob: account, + }) + } + storages := make([]types.DiffStorage, 0, len(s.snapStorage)) + for accountHash, storage := range s.snapStorage { + keys := make([]string, 0, len(storage)) + values := make([][]byte, 0, len(storage)) + for k, v := range storage { + keys = append(keys, k) + values = append(values, v) + } + storages = append(storages, types.DiffStorage{ + Account: accountHash, + Keys: keys, + Vals: values, + }) + } + return destructs, accounts, storages +} + +// PrepareAccessList handles the preparatory steps for executing a state transition with +// regards to both EIP-2929 and EIP-2930: +// +// - Add sender to access list (2929) +// - Add destination to access list (2929) +// - Add precompiles to access list (2929) +// - Add the contents of the optional tx access list (2930) +// +// This method should only be called if Yolov3/Berlin/2929+2930 is applicable at the current number. +func (s *StateDB) PrepareAccessList(sender common.Address, dst *common.Address, precompiles []common.Address, list types.AccessList) { + s.AddAddressToAccessList(sender) + if dst != nil { + s.AddAddressToAccessList(*dst) + // If it's a create-tx, the destination will be added inside evm.create + } + for _, addr := range precompiles { + s.AddAddressToAccessList(addr) + } + for _, el := range list { + s.AddAddressToAccessList(el.Address) + for _, key := range el.StorageKeys { + s.AddSlotToAccessList(el.Address, key) + } + } +} + +// AddAddressToAccessList adds the given address to the access list +func (s *StateDB) AddAddressToAccessList(addr common.Address) { + if s.accessList == nil { + s.accessList = newAccessList() + } + if s.accessList.AddAddress(addr) { + s.journal.append(accessListAddAccountChange{&addr}) + } +} + +// AddSlotToAccessList adds the given (address, slot)-tuple to the access list +func (s *StateDB) AddSlotToAccessList(addr common.Address, slot common.Hash) { + if s.accessList == nil { + s.accessList = newAccessList() + } + addrMod, slotMod := s.accessList.AddSlot(addr, slot) + if addrMod { + // In practice, this should not happen, since there is no way to enter the + // scope of 'address' without having the 'address' become already added + // to the access list (via call-variant, create, etc). + // Better safe than sorry, though + s.journal.append(accessListAddAccountChange{&addr}) + } + if slotMod { + s.journal.append(accessListAddSlotChange{ + address: &addr, + slot: &slot, + }) + } +} + +// AddressInAccessList returns true if the given address is in the access list. +func (s *StateDB) AddressInAccessList(addr common.Address) bool { + if s.accessList == nil { + return false + } + return s.accessList.ContainsAddress(addr) +} + +// SlotInAccessList returns true if the given (address, slot)-tuple is in the access list. +func (s *StateDB) SlotInAccessList(addr common.Address, slot common.Hash) (addressPresent bool, slotPresent bool) { + if s.accessList == nil { + return false, false + } + return s.accessList.Contains(addr, slot) +} + +func (s *StateDB) GetDirtyAccounts() []common.Address { + accounts := make([]common.Address, 0, len(s.stateObjectsDirty)) + for account := range s.stateObjectsDirty { + accounts = append(accounts, account) + } + return accounts +} + +func (s *StateDB) GetStorage(address common.Address) *sync.Map { + return s.storagePool.getStorage(address) +} + +// PrepareForParallel prepares for state db to be used in parallel execution mode. +func (s *StateDB) PrepareForParallel() { + s.isParallel = true + s.parallel.stateObjects = &StateObjectSyncMap{} +} + +// MergeSlotDB is for Parallel execution mode, when the transaction has been +// finalized(dirty -> pending) on execution slot, the execution results should be +// merged back to the main StateDB. +// And it will return and keep the slot's change list for later conflict detect. +func (s *StateDB) MergeSlotDB(slotDb *ParallelStateDB, slotReceipt *types.Receipt, txIndex int) { + // receipt.Logs use unified log index within a block + // align slotDB's log index to the block stateDB's logSize + for _, l := range slotReceipt.Logs { + l.Index += s.logSize + } + s.logSize += slotDb.logSize + + // before merge, pay the gas fee first: AddBalance to consensus.SystemAddress + systemAddress := slotDb.parallel.systemAddress + if slotDb.parallel.keepSystemAddressBalance { + s.SetBalance(systemAddress, slotDb.GetBalance(systemAddress)) + } else { + s.AddBalance(systemAddress, slotDb.GetBalance(systemAddress)) + } + + // only merge dirty objects + addressesToPrefetch := make([][]byte, 0, len(slotDb.stateObjectsDirty)) + for addr := range slotDb.stateObjectsDirty { + if _, exist := s.stateObjectsDirty[addr]; !exist { + s.stateObjectsDirty[addr] = struct{}{} + } + // system address is EOA account, it should have no storage change + if addr == systemAddress { + continue + } + + // stateObjects: KV, balance, nonce... + dirtyObj, ok := slotDb.parallel.dirtiedStateObjectsInSlot[addr] + if !ok { + log.Error("parallel merge, but dirty object not exist!", "SlotIndex", slotDb.parallel.SlotIndex, "txIndex:", slotDb.txIndex, "addr", addr) + continue + } + mainObj, exist := s.loadStateObj(addr) + if !exist { // fixme: it is also state change + // addr not exist on main DB, do ownership transfer + // dirtyObj.db = s + // dirtyObj.finalise(true) // true: prefetch on dispatcher + mainObj = dirtyObj.deepCopy(s) + mainObj.finalise(true) + s.storeStateObj(addr, mainObj) + // fixme: should not delete, would cause unconfirmed DB incorrect? + // delete(slotDb.parallel.dirtiedStateObjectsInSlot, addr) // transfer ownership, fixme: shared read? + if dirtyObj.deleted { + // remove the addr from snapAccounts&snapStorage only when object is deleted. + // "deleted" is not equal to "snapDestructs", since createObject() will add an addr for + // snapDestructs to destroy previous object, while it will keep the addr in snapAccounts & snapAccounts + delete(s.snapAccounts, addr) + delete(s.snapStorage, addr) + } + } else { + // addr already in main DB, do merge: balance, KV, code, State(create, suicide) + // can not do copy or ownership transfer directly, since dirtyObj could have outdated + // data(may be updated within the conflict window) + + var newMainObj = mainObj // we don't need to copy the object since the storages are thread safe + if _, ok := slotDb.parallel.addrStateChangesInSlot[addr]; ok { + // there are 3 kinds of state change: + // 1.Suicide + // 2.Empty Delete + // 3.createObject + // a.AddBalance,SetState to an unexist or deleted(suicide, empty delete) address. + // b.CreateAccount: like DAO the fork, regenerate a account carry its balance without KV + // For these state change, do ownership transafer for efficiency: + // dirtyObj.db = s + // newMainObj = dirtyObj + newMainObj = dirtyObj.deepCopy(s) + // should not delete, would cause unconfirmed DB incorrect. + // delete(slotDb.parallel.dirtiedStateObjectsInSlot, addr) // transfer ownership, fixme: shared read? + if dirtyObj.deleted { + // remove the addr from snapAccounts&snapStorage only when object is deleted. + // "deleted" is not equal to "snapDestructs", since createObject() will add an addr for + // snapDestructs to destroy previous object, while it will keep the addr in snapAccounts & snapAccounts + delete(s.snapAccounts, addr) + delete(s.snapStorage, addr) + } + } else { + // deepCopy a temporary *StateObject for safety, since slot could read the address, + // dispatch should avoid overwrite the StateObject directly otherwise, it could + // crash for: concurrent map iteration and map write + + if _, balanced := slotDb.parallel.balanceChangesInSlot[addr]; balanced { + newMainObj.SetBalance(dirtyObj.Balance()) + } + if _, coded := slotDb.parallel.codeChangesInSlot[addr]; coded { + newMainObj.code = dirtyObj.code + newMainObj.data.CodeHash = dirtyObj.data.CodeHash + newMainObj.dirtyCode = true + } + if keys, stated := slotDb.parallel.kvChangesInSlot[addr]; stated { + newMainObj.MergeSlotObject(s.db, dirtyObj, keys) + } + if _, nonced := slotDb.parallel.nonceChangesInSlot[addr]; nonced { + // dirtyObj.Nonce() should not be less than newMainObj + newMainObj.setNonce(dirtyObj.Nonce()) + } + } + newMainObj.finalise(true) // true: prefetch on dispatcher + // update the object + s.storeStateObj(addr, newMainObj) + } + addressesToPrefetch = append(addressesToPrefetch, common.CopyBytes(addr[:])) // Copy needed for closure + } + + if s.prefetcher != nil && len(addressesToPrefetch) > 0 { + s.prefetcher.prefetch(s.originalRoot, addressesToPrefetch, emptyAddr) // prefetch for trie node of account + } + + for addr := range slotDb.stateObjectsPending { + if _, exist := s.stateObjectsPending[addr]; !exist { + s.stateObjectsPending[addr] = struct{}{} + } + } + + // slotDb.logs: logs will be kept in receipts, no need to do merge + + for hash, preimage := range slotDb.preimages { + s.preimages[hash] = preimage + } + if s.accessList != nil { + // fixme: accessList is not enabled yet, but it should use merge rather than overwrite Copy + s.accessList = slotDb.accessList.Copy() + } + + if slotDb.snaps != nil { + for k := range slotDb.snapDestructs { + // There could be a race condition for parallel transaction execution + // One transaction add balance 0 to an empty address, will delete it(delete empty is enabled). + // While another concurrent transaction could add a none-zero balance to it, make it not empty + // We fixed it by add a addr state read record for add balance 0 + s.snapParallelLock.Lock() + s.snapDestructs[k] = struct{}{} + s.snapParallelLock.Unlock() + } + + // slotDb.snapAccounts should be empty, comment out and to be deleted later + // for k, v := range slotDb.snapAccounts { + // s.snapAccounts[k] = v + // } + // slotDb.snapStorage should be empty, comment out and to be deleted later + // for k, v := range slotDb.snapStorage { + // temp := make(map[string][]byte) + // for kk, vv := range v { + // temp[kk] = vv + // } + // s.snapStorage[k] = temp + // } + } +} + +type ParallelStateDB struct { + StateDB +} + +// NewSlotDB creates a new State DB based on the provided StateDB. +// With parallel, each execution slot would have its own StateDB. +func NewSlotDB(db *StateDB, systemAddr common.Address, txIndex int, baseTxIndex int, keepSystem bool, + unconfirmedDBs *sync.Map /*map[int]*ParallelStateDB*/) *ParallelStateDB { + slotDB := db.CopyForSlot() + slotDB.txIndex = txIndex + slotDB.originalRoot = db.originalRoot + slotDB.parallel.baseStateDB = db + slotDB.parallel.baseTxIndex = baseTxIndex + slotDB.parallel.systemAddress = systemAddr + slotDB.parallel.systemAddressOpsCount = 0 + slotDB.parallel.keepSystemAddressBalance = keepSystem + slotDB.storagePool = NewStoragePool() + slotDB.EnableWriteOnSharedStorage() + for index := baseTxIndex + 1; index < slotDB.txIndex; index++ { // txIndex + unconfirmedDB, ok := unconfirmedDBs.Load(index) + if ok { + slotDB.parallel.unconfirmedDBInShot[index] = unconfirmedDB.(*ParallelStateDB) + } + } + + // All transactions will pay gas fee to the systemAddr at the end, this address is + // deemed to conflict, we handle it specially, clear it now and set it back to the main + // StateDB later; + // But there are transactions that will try to read systemAddr's balance, such as: + // https://bscscan.com/tx/0xcd69755be1d2f55af259441ff5ee2f312830b8539899e82488a21e85bc121a2a. + // It will trigger transaction redo and keepSystem will be marked as true. + if !keepSystem { + slotDB.SetBalance(systemAddr, big.NewInt(0)) + } + + return slotDB +} + +// RevertSlotDB keep the Read list for conflict detect, +// discard all state changes except: +// - nonce and balance of from address +// - balance of system address: will be used on merge to update SystemAddress's balance +func (s *ParallelStateDB) RevertSlotDB(from common.Address) { + s.parallel.kvChangesInSlot = make(map[common.Address]StateKeys) + + // balance := s.parallel.balanceChangesInSlot[from] + s.parallel.nonceChangesInSlot = make(map[common.Address]struct{}) + s.parallel.balanceChangesInSlot = make(map[common.Address]struct{}, 1) + s.parallel.addrStateChangesInSlot = make(map[common.Address]bool) // 0: created, 1: deleted + + selfStateObject := s.parallel.dirtiedStateObjectsInSlot[from] + systemAddress := s.parallel.systemAddress + systemStateObject := s.parallel.dirtiedStateObjectsInSlot[systemAddress] + s.parallel.dirtiedStateObjectsInSlot = make(map[common.Address]*StateObject, 2) + // keep these elements + s.parallel.dirtiedStateObjectsInSlot[from] = selfStateObject + s.parallel.dirtiedStateObjectsInSlot[systemAddress] = systemStateObject + s.parallel.balanceChangesInSlot[from] = struct{}{} + s.parallel.balanceChangesInSlot[systemAddress] = struct{}{} + s.parallel.nonceChangesInSlot[from] = struct{}{} +} + +func (s *ParallelStateDB) getBaseStateDB() *StateDB { + return &s.StateDB +} + +func (s *ParallelStateDB) SetSlotIndex(index int) { + s.parallel.SlotIndex = index +} + +// for parallel execution mode, try to get dirty StateObject in slot first. +// it is mainly used by journal revert right now. +func (s *ParallelStateDB) getStateObject(addr common.Address) *StateObject { + if obj, ok := s.parallel.dirtiedStateObjectsInSlot[addr]; ok { + return obj + } + // can not call s.StateDB.getStateObject(), since `newObject` need ParallelStateDB as the interface + return s.getStateObjectNoSlot(addr) +} + +func (s *ParallelStateDB) storeStateObj(addr common.Address, stateObject *StateObject) { + // When a state object is stored into s.parallel.stateObjects, + // it belongs to base StateDB, it is confirmed and valid. + stateObject.db = s.parallel.baseStateDB + stateObject.dbItf = s.parallel.baseStateDB + // the object could be create in SlotDB, if it got the object from DB and + // update it to the shared `s.parallel.stateObjects`` + stateObject.db.storeParallelLock.Lock() + if _, ok := s.parallel.stateObjects.Load(addr); !ok { + s.parallel.stateObjects.Store(addr, stateObject) + } + stateObject.db.storeParallelLock.Unlock() +} + +func (s *ParallelStateDB) getStateObjectNoSlot(addr common.Address) *StateObject { + if obj := s.getDeletedStateObject(addr); obj != nil && !obj.deleted { + return obj + } + return nil +} + +// 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. + +// prev is used for CreateAccount to get its balance +// Parallel mode: +// if prev in dirty: revert is ok +// if prev in unconfirmed DB: addr state read record, revert should not put it back +// if prev in main DB: addr state read record, revert should not put it back +// if pre no exist: addr state read record, + +// `prev` is used to handle revert, to recover with the `prev` object +// In Parallel mode, we only need to recover to `prev` in SlotDB, +// a.if it is not in SlotDB, `revert` will remove it from the SlotDB +// b.if it is exist in SlotDB, `revert` will recover to the `prev` in SlotDB +// c.as `snapDestructs` it is the same +func (s *ParallelStateDB) createObject(addr common.Address) (newobj *StateObject) { + log.Debug("ParallelStateDB createObject", "SlotxIndex", s.parallel.SlotIndex, "txIndex", s.TxIndex()) + + // do not get from unconfirmed DB, since it will has problem on revert + prev := s.parallel.dirtiedStateObjectsInSlot[addr] + + var prevdestruct bool + + if s.snap != nil && prev != nil { + _, prevdestruct = s.snapDestructs[prev.address] // fixme, record the snapshot read for create Account + s.parallel.addrSnapDestructsReadsInSlot[addr] = prevdestruct + if !prevdestruct { + // To destroy the previous trie node first and update the trie tree + // with the new object on block commit. + s.snapDestructs[prev.address] = struct{}{} + } + } + newobj = newObject(s, s.isParallel, addr, Account{}) + newobj.setNonce(0) // sets the object to dirty + if prev == nil { + s.journal.append(createObjectChange{account: &addr}) + } else { + s.journal.append(resetObjectChange{prev: prev, prevdestruct: prevdestruct}) + } + + // s.parallel.dirtiedStateObjectsInSlot[addr] = newobj // would change the bahavior of AddBalance... + s.parallel.addrStateChangesInSlot[addr] = true // the object sis created + s.parallel.nonceChangesInSlot[addr] = struct{}{} + s.parallel.balanceChangesInSlot[addr] = struct{}{} + s.parallel.codeChangesInSlot[addr] = struct{}{} + // notice: all the KVs are cleared if any + s.parallel.kvChangesInSlot[addr] = make(StateKeys) + return newobj +} + +// getDeletedStateObject is similar to getStateObject, but instead of returning +// nil for a deleted state object, it returns the actual object with the deleted +// flag set. This is needed by the state journal to revert to the correct s- +// destructed object instead of wiping all knowledge about the state object. +func (s *ParallelStateDB) getDeletedStateObject(addr common.Address) *StateObject { + // Prefer live objects if any is available + if obj, _ := s.getStateObjectFromStateObjects(addr); obj != nil { + return obj + } + data, ok := s.getStateObjectFromSnapshotOrTrie(addr) + if !ok { + return nil + } + // Insert into the live set + // if obj, ok := s.loadStateObj(addr); ok { + // fixme: concurrent not safe, merge could update it... + // return obj + // } + // this is why we have to use a seperate getDeletedStateObject for ParallelStateDB + // `s` has to be the ParallelStateDB + obj := newObject(s, s.isParallel, addr, *data) + s.storeStateObj(addr, obj) + // s.SetStateObject(obj) + return obj +} + +// GetOrNewStateObject retrieves a state object or create a new state object if nil. +// dirtyInSlot -> Unconfirmed DB -> main DB -> snapshot, no? create one +func (s *ParallelStateDB) GetOrNewStateObject(addr common.Address) *StateObject { + log.Debug("ParallelStateDB GetOrNewStateObject", "SlotxIndex", s.parallel.SlotIndex, "txIndex", s.TxIndex()) + + var stateObject *StateObject = nil + exist := true + if stateObject, ok := s.parallel.dirtiedStateObjectsInSlot[addr]; ok { + return stateObject + } + stateObject, _ = s.getStateObjectFromUnconfirmedDB(addr) + + if stateObject == nil { + stateObject = s.getStateObjectNoSlot(addr) // try to get from base db + } + if stateObject == nil || stateObject.deleted || stateObject.suicided { + stateObject = s.createObject(addr) + exist = false + } + + s.parallel.addrStateReadsInSlot[addr] = exist // true: exist, false: not exist + return stateObject +} + +// Exist reports whether the given account address exists in the state. +// Notably this also returns true for suicided accounts. +func (s *ParallelStateDB) Exist(addr common.Address) bool { + log.Debug("ParallelStateDB Exist", "SlotxIndex", s.parallel.SlotIndex, "txIndex", s.TxIndex()) + // 1.Try to get from dirty + if obj, ok := s.parallel.dirtiedStateObjectsInSlot[addr]; ok { + // dirty object should not be deleted, since deleted is only flagged on finalise + // and if it is suicided in contract call, suicide is taken as exist until it is finalised + // todo: add a check here, to be removed later + if obj.deleted || obj.suicided { + log.Error("Exist in dirty, but marked as deleted or suicided", + "txIndex", s.txIndex, "baseTxIndex:", s.parallel.baseTxIndex) + } + return true + } + // 2.Try to get from uncomfirmed & main DB + // 2.1 Already read before + if exist, ok := s.parallel.addrStateReadsInSlot[addr]; ok { + return exist + } + // 2.2 Try to get from unconfirmed DB if exist + if exist, ok := s.getAddrStateFromUnconfirmedDB(addr); ok { + s.parallel.addrStateReadsInSlot[addr] = exist // update and cache + return exist + } + + // 3.Try to get from main StateDB + exist := s.getStateObjectNoSlot(addr) != nil + s.parallel.addrStateReadsInSlot[addr] = exist // update and cache + return exist +} + +// Empty returns whether the state object is either non-existent +// or empty according to the EIP161 specification (balance = nonce = code = 0) +func (s *ParallelStateDB) Empty(addr common.Address) bool { + log.Debug("ParallelStateDB Empty", "SlotxIndex", s.parallel.SlotIndex, "txIndex", s.TxIndex()) + // 1.Try to get from dirty + if obj, ok := s.parallel.dirtiedStateObjectsInSlot[addr]; ok { + // dirty object is light copied and fixup on need, + // empty could be wrong, except it is created with this TX + if _, ok := s.parallel.addrStateChangesInSlot[addr]; ok { + return obj.empty() + } + // so we have to check it manually + // empty means: Nonce == 0 && Balance == 0 && CodeHash == emptyCodeHash + if s.GetBalance(addr).Sign() != 0 { // check balance first, since it is most likely not zero + return false + } + if s.GetNonce(addr) != 0 { + return false + } + codeHash := s.GetCodeHash(addr) + return bytes.Equal(codeHash.Bytes(), emptyCodeHash) // code is empty, the object is empty + } + // 2.Try to get from uncomfirmed & main DB + // 2.1 Already read before + if exist, ok := s.parallel.addrStateReadsInSlot[addr]; ok { + // exist means not empty + return !exist + } + // 2.2 Try to get from unconfirmed DB if exist + if exist, ok := s.getAddrStateFromUnconfirmedDB(addr); ok { + s.parallel.addrStateReadsInSlot[addr] = exist // update and cache + return !exist + } + + so := s.getStateObjectNoSlot(addr) + empty := (so == nil || so.empty()) + s.parallel.addrStateReadsInSlot[addr] = !empty // update and cache + return empty +} + +// GetBalance retrieves the balance from the given address or 0 if object not found +// GetFrom the dirty list => from unconfirmed DB => get from main stateDB +func (s *ParallelStateDB) GetBalance(addr common.Address) *big.Int { + log.Debug("ParallelStateDB GetBalance", "SlotxIndex", s.parallel.SlotIndex, "txIndex", s.TxIndex()) + if addr == s.parallel.systemAddress { + s.parallel.systemAddressOpsCount++ + } + // 1.Try to get from dirty + if _, ok := s.parallel.balanceChangesInSlot[addr]; ok { + if obj, ok := s.parallel.dirtiedStateObjectsInSlot[addr]; ok { + // on balance fixup, addr may not exist in dirtiedStateObjectsInSlot + // we intend to fixup balance based on unconfirmed DB or main DB + return obj.Balance() + } + } + // 2.Try to get from uncomfirmed DB or main DB + // 2.1 Already read before + if balance, ok := s.parallel.balanceReadsInSlot[addr]; ok { + return balance + } + // 2.2 Try to get from unconfirmed DB if exist + if balance := s.getBalanceFromUnconfirmedDB(addr); balance != nil { + s.parallel.balanceReadsInSlot[addr] = balance + return balance + } + + // 3. Try to get from main StateObejct + balance := common.Big0 + stateObject := s.getStateObjectNoSlot(addr) + if stateObject != nil { + balance = stateObject.Balance() + } + s.parallel.balanceReadsInSlot[addr] = balance + return balance +} + +func (s *ParallelStateDB) GetNonce(addr common.Address) uint64 { + log.Debug("ParallelStateDB GetNonce", "SlotxIndex", s.parallel.SlotIndex, "txIndex", s.TxIndex()) + // 1.Try to get from dirty + if _, ok := s.parallel.nonceChangesInSlot[addr]; ok { + if obj, ok := s.parallel.dirtiedStateObjectsInSlot[addr]; ok { + // on nonce fixup, addr may not exist in dirtiedStateObjectsInSlot + // we intend to fixup nonce based on unconfirmed DB or main DB + return obj.Nonce() + } + } + // 2.Try to get from uncomfirmed DB or main DB + // 2.1 Already read before + if nonce, ok := s.parallel.nonceReadsInSlot[addr]; ok { + return nonce + } + // 2.2 Try to get from unconfirmed DB if exist + if nonce, ok := s.getNonceFromUnconfirmedDB(addr); ok { + s.parallel.nonceReadsInSlot[addr] = nonce + return nonce + } + + // 3.Try to get from main StateDB + var nonce uint64 = 0 + stateObject := s.getStateObjectNoSlot(addr) + if stateObject != nil { + nonce = stateObject.Nonce() + } + s.parallel.nonceReadsInSlot[addr] = nonce + + return nonce +} + +func (s *ParallelStateDB) GetCode(addr common.Address) []byte { + log.Debug("ParallelStateDB GetCode", "SlotxIndex", s.parallel.SlotIndex, "txIndex", s.TxIndex()) + // 1.Try to get from dirty + if _, ok := s.parallel.codeChangesInSlot[addr]; ok { + if obj, ok := s.parallel.dirtiedStateObjectsInSlot[addr]; ok { + // on code fixup, addr may not exist in dirtiedStateObjectsInSlot + // we intend to fixup code based on unconfirmed DB or main DB + code := obj.Code(s.db) + return code + } + } + // 2.Try to get from uncomfirmed DB or main DB + // 2.1 Already read before + if code, ok := s.parallel.codeReadsInSlot[addr]; ok { + return code + } + // 2.2 Try to get from unconfirmed DB if exist + if code, ok := s.getCodeFromUnconfirmedDB(addr); ok { + s.parallel.codeReadsInSlot[addr] = code + return code + } + + // 3. Try to get from main StateObejct + stateObject := s.getStateObjectNoSlot(addr) + var code []byte + if stateObject != nil { + code = stateObject.Code(s.db) + } + s.parallel.codeReadsInSlot[addr] = code + return code +} + +func (s *ParallelStateDB) GetCodeSize(addr common.Address) int { + log.Debug("ParallelStateDB GetCodeSize", "SlotxIndex", s.parallel.SlotIndex, "txIndex", s.TxIndex()) + + // 1.Try to get from dirty + if _, ok := s.parallel.codeChangesInSlot[addr]; ok { + if obj, ok := s.parallel.dirtiedStateObjectsInSlot[addr]; ok { + // on code fixup, addr may not exist in dirtiedStateObjectsInSlot + // we intend to fixup code based on unconfirmed DB or main DB + return obj.CodeSize(s.db) + } + } + // 2.Try to get from uncomfirmed DB or main DB + // 2.1 Already read before + if code, ok := s.parallel.codeReadsInSlot[addr]; ok { + return len(code) // len(nil) is 0 too + } + // 2.2 Try to get from unconfirmed DB if exist + if code, ok := s.getCodeFromUnconfirmedDB(addr); ok { + s.parallel.codeReadsInSlot[addr] = code + return len(code) // len(nil) is 0 too + } + + // 3. Try to get from main StateObejct + var codeSize int = 0 + var code []byte + stateObject := s.getStateObjectNoSlot(addr) + + if stateObject != nil { + code = stateObject.Code(s.db) + codeSize = stateObject.CodeSize(s.db) + } + s.parallel.codeReadsInSlot[addr] = code + return codeSize +} + +// return value of GetCodeHash: +// - common.Hash{}: the address does not exist +// - emptyCodeHash: the address exist, but code is empty +// - others: the address exist, and code is not empty +func (s *ParallelStateDB) GetCodeHash(addr common.Address) common.Hash { + log.Debug("ParallelStateDB GetCodeHash", "SlotxIndex", s.parallel.SlotIndex, "txIndex", s.TxIndex()) + + // 1.Try to get from dirty + if _, ok := s.parallel.codeChangesInSlot[addr]; ok { + if obj, ok := s.parallel.dirtiedStateObjectsInSlot[addr]; ok { + // on code fixup, addr may not exist in dirtiedStateObjectsInSlot + // we intend to fixup balance based on unconfirmed DB or main DB + return common.BytesToHash(obj.CodeHash()) + } + } + // 2.Try to get from uncomfirmed DB or main DB + // 2.1 Already read before + if codeHash, ok := s.parallel.codeHashReadsInSlot[addr]; ok { + return codeHash + } + // 2.2 Try to get from unconfirmed DB if exist + if codeHash, ok := s.getCodeHashFromUnconfirmedDB(addr); ok { + s.parallel.codeHashReadsInSlot[addr] = codeHash + return codeHash + } + // 3. Try to get from main StateObejct + stateObject := s.getStateObjectNoSlot(addr) + codeHash := common.Hash{} + if stateObject != nil { + codeHash = common.BytesToHash(stateObject.CodeHash()) + } + s.parallel.codeHashReadsInSlot[addr] = codeHash + return codeHash +} + +// GetState retrieves a value from the given account's storage trie. +// For parallel mode wih, get from the state in order: +// -> self dirty, both Slot & MainProcessor +// -> pending of self: Slot on merge +// -> pending of unconfirmed DB +// -> pending of main StateDB +// -> origin +func (s *ParallelStateDB) GetState(addr common.Address, hash common.Hash) common.Hash { + log.Debug("ParallelStateDB GetState", "SlotxIndex", s.parallel.SlotIndex, "txIndex", s.TxIndex()) + + // 1.Try to get from dirty + if exist, ok := s.parallel.addrStateChangesInSlot[addr]; ok { + if !exist { + return common.Hash{} + } + obj := s.parallel.dirtiedStateObjectsInSlot[addr] // addr must exist in dirtiedStateObjectsInSlot + return obj.GetState(s.db, hash) + } + if keys, ok := s.parallel.kvChangesInSlot[addr]; ok { + if _, ok := keys[hash]; ok { + obj := s.parallel.dirtiedStateObjectsInSlot[addr] // addr must exist in dirtiedStateObjectsInSlot + return obj.GetState(s.db, hash) + } + } + // 2.Try to get from uncomfirmed DB or main DB + // 2.1 Already read before + if storage, ok := s.parallel.kvReadsInSlot[addr]; ok { + if val, ok := storage.GetValue(hash); ok { + return val + } + } + // 2.2 Try to get from unconfirmed DB if exist + if val, ok := s.getKVFromUnconfirmedDB(addr, hash); ok { + if s.parallel.kvReadsInSlot[addr] == nil { + s.parallel.kvReadsInSlot[addr] = newStorage(false) + } + s.parallel.kvReadsInSlot[addr].StoreValue(hash, val) // update cache + return val + } + + // 3.Get from main StateDB + stateObject := s.getStateObjectNoSlot(addr) + val := common.Hash{} + if stateObject != nil { + val = stateObject.GetState(s.db, hash) + } + if s.parallel.kvReadsInSlot[addr] == nil { + s.parallel.kvReadsInSlot[addr] = newStorage(false) + } + s.parallel.kvReadsInSlot[addr].StoreValue(hash, val) // update cache + return val +} + +// GetCommittedState retrieves a value from the given account's committed storage trie. +func (s *ParallelStateDB) GetCommittedState(addr common.Address, hash common.Hash) common.Hash { + log.Debug("ParallelStateDB GetCommittedState", "SlotxIndex", s.parallel.SlotIndex, "txIndex", s.TxIndex()) + // 1.No need to get from pending of itself even on merge, since stateobject in SlotDB won't do finalise + // 2.Try to get from uncomfirmed DB or main DB + // KVs in unconfirmed DB can be seen as pending storage + // KVs in main DB are merged from SlotDB and has done finalise() on merge, can be seen as pending storage too. + // 2.1 Already read before + if storage, ok := s.parallel.kvReadsInSlot[addr]; ok { + if val, ok := storage.GetValue(hash); ok { + return val + } + } + // 2.2 Try to get from unconfirmed DB if exist + if val, ok := s.getKVFromUnconfirmedDB(addr, hash); ok { + if s.parallel.kvReadsInSlot[addr] == nil { + s.parallel.kvReadsInSlot[addr] = newStorage(false) + } + s.parallel.kvReadsInSlot[addr].StoreValue(hash, val) // update cache + return val + } + + // 3. Try to get from main DB + stateObject := s.getStateObjectNoSlot(addr) + val := common.Hash{} + if stateObject != nil { + val = stateObject.GetCommittedState(s.db, hash) + } + if s.parallel.kvReadsInSlot[addr] == nil { + s.parallel.kvReadsInSlot[addr] = newStorage(false) + } + s.parallel.kvReadsInSlot[addr].StoreValue(hash, val) // update cache + return val +} + +func (s *ParallelStateDB) HasSuicided(addr common.Address) bool { + log.Debug("ParallelStateDB HasSuicided", "SlotxIndex", s.parallel.SlotIndex, "txIndex", s.TxIndex()) + // 1.Try to get from dirty + if obj, ok := s.parallel.dirtiedStateObjectsInSlot[addr]; ok { + return obj.suicided + } + // 2.Try to get from uncomfirmed + if exist, ok := s.getAddrStateFromUnconfirmedDB(addr); ok { + return !exist + } + + stateObject := s.getStateObjectNoSlot(addr) + if stateObject != nil { + return stateObject.suicided + } + return false +} + +// AddBalance adds amount to the account associated with addr. +func (s *ParallelStateDB) AddBalance(addr common.Address, amount *big.Int) { + // add balance will perform a read operation first + // s.parallel.balanceReadsInSlot[addr] = struct{}{} // fixme: to make the the balance valid, since unconfirmed would refer it. + // if amount.Sign() == 0 { + // if amount == 0, no balance change, but there is still an empty check. + // take this empty check as addr state read(create, suicide, empty delete) + // s.parallel.addrStateReadsInSlot[addr] = struct{}{} + // } + log.Debug("ParallelStateDB AddBalance", "SlotxIndex", s.parallel.SlotIndex, "txIndex", s.TxIndex()) + + stateObject := s.GetOrNewStateObject(addr) + if stateObject != nil { + if addr == s.parallel.systemAddress { + s.parallel.systemAddressOpsCount++ + } + // if amount.Sign() != 0 { // todo: to reenable it + if _, ok := s.parallel.dirtiedStateObjectsInSlot[addr]; !ok { + newStateObject := stateObject.lightCopy(s) // light copy from main DB + // do balance fixup from the confirmed DB, it could be more reliable than main DB + balance := s.GetBalance(addr) + newStateObject.setBalance(balance) + // s.parallel.balanceReadsInSlot[addr] = newStateObject.Balance() // could read from main DB or unconfirmed DB + newStateObject.AddBalance(amount) + s.parallel.dirtiedStateObjectsInSlot[addr] = newStateObject + s.parallel.balanceChangesInSlot[addr] = struct{}{} + return + } + // already dirty, make sure the balance if fixed up + // if stateObject.Balance() + if addr != s.parallel.systemAddress { + if stateObject.Balance().Cmp(s.GetBalance(addr)) != 0 { + log.Warn("AddBalance in dirty, but balance is incorrect", "txIndex", s.txIndex, "addr", addr, + "stateObject.Balance()", stateObject.Balance(), "s.GetBalance(addr)", s.GetBalance(addr)) + stateObject.setBalance(s.GetBalance(addr)) + } + } + + stateObject.AddBalance(amount) + s.parallel.balanceChangesInSlot[addr] = struct{}{} + } +} + +// SubBalance subtracts amount from the account associated with addr. +func (s *ParallelStateDB) SubBalance(addr common.Address, amount *big.Int) { + // if amount.Sign() != 0 { + // unlike add, sub 0 balance will not touch empty object + // s.parallel.balanceReadsInSlot[addr] = struct{}{} + // } + log.Debug("ParallelStateDB SubBalance", "SlotxIndex", s.parallel.SlotIndex, "txIndex", s.TxIndex()) + + stateObject := s.GetOrNewStateObject(addr) + if stateObject != nil { + if addr == s.parallel.systemAddress { + s.parallel.systemAddressOpsCount++ + } + + // if amount.Sign() != 0 { // todo: to reenable it + if _, ok := s.parallel.dirtiedStateObjectsInSlot[addr]; !ok { + newStateObject := stateObject.lightCopy(s) // light copy from main DB + // do balance fixup from the confirmed DB, it could be more reliable than main DB + balance := s.GetBalance(addr) + newStateObject.setBalance(balance) + // s.parallel.balanceReadsInSlot[addr] = newStateObject.Balance() + newStateObject.SubBalance(amount) + s.parallel.balanceChangesInSlot[addr] = struct{}{} + s.parallel.dirtiedStateObjectsInSlot[addr] = newStateObject + return + } + // already dirty, make sure the balance if fixed + // if stateObject.Balance() + if addr != s.parallel.systemAddress { + if stateObject.Balance().Cmp(s.GetBalance(addr)) != 0 { + log.Warn("SubBalance in dirty, but balance is incorrect", "txIndex", s.txIndex, "addr", addr, + "stateObject.Balance()", stateObject.Balance(), "s.GetBalance(addr)", s.GetBalance(addr)) + stateObject.setBalance(s.GetBalance(addr)) + } + } + + stateObject.SubBalance(amount) + s.parallel.balanceChangesInSlot[addr] = struct{}{} + } +} + +func (s *ParallelStateDB) SetBalance(addr common.Address, amount *big.Int) { + log.Debug("ParallelStateDB SetBalance", "SlotxIndex", s.parallel.SlotIndex, "txIndex", s.TxIndex()) + + stateObject := s.GetOrNewStateObject(addr) + if stateObject != nil { + if addr == s.parallel.systemAddress { + s.parallel.systemAddressOpsCount++ + } + if _, ok := s.parallel.dirtiedStateObjectsInSlot[addr]; !ok { + newStateObject := stateObject.lightCopy(s) + // update balance for revert, in case child contract is revertted, + // it should revert to the previous balance + balance := s.GetBalance(addr) + newStateObject.setBalance(balance) + newStateObject.SetBalance(amount) + s.parallel.balanceChangesInSlot[addr] = struct{}{} + s.parallel.dirtiedStateObjectsInSlot[addr] = newStateObject + return + } + + stateObject.SetBalance(amount) + s.parallel.balanceChangesInSlot[addr] = struct{}{} + } +} + +func (s *ParallelStateDB) SetNonce(addr common.Address, nonce uint64) { + log.Debug("ParallelStateDB SetNonce", "SlotxIndex", s.parallel.SlotIndex, "txIndex", s.TxIndex()) + + stateObject := s.GetOrNewStateObject(addr) + if stateObject != nil { + if _, ok := s.parallel.dirtiedStateObjectsInSlot[addr]; !ok { + newStateObject := stateObject.lightCopy(s) + noncePre := s.GetNonce(addr) + newStateObject.setNonce(noncePre) // nonce fixup + newStateObject.SetNonce(nonce) + s.parallel.nonceChangesInSlot[addr] = struct{}{} + s.parallel.dirtiedStateObjectsInSlot[addr] = newStateObject + return + } + noncePre := s.GetNonce(addr) + stateObject.setNonce(noncePre) // nonce fixup + + stateObject.SetNonce(nonce) + s.parallel.nonceChangesInSlot[addr] = struct{}{} + } +} + +func (s *ParallelStateDB) SetCode(addr common.Address, code []byte) { + log.Debug("ParallelStateDB SetCode", "SlotxIndex", s.parallel.SlotIndex, "txIndex", s.TxIndex()) + + stateObject := s.GetOrNewStateObject(addr) + if stateObject != nil { + codeHash := crypto.Keccak256Hash(code) + if _, ok := s.parallel.dirtiedStateObjectsInSlot[addr]; !ok { + newStateObject := stateObject.lightCopy(s) + codePre := s.GetCode(addr) // code fixup + codeHashPre := crypto.Keccak256Hash(codePre) + newStateObject.setCode(codeHashPre, codePre) + + newStateObject.SetCode(codeHash, code) + s.parallel.dirtiedStateObjectsInSlot[addr] = newStateObject + s.parallel.codeChangesInSlot[addr] = struct{}{} + return + } + codePre := s.GetCode(addr) // code fixup + codeHashPre := crypto.Keccak256Hash(codePre) + stateObject.setCode(codeHashPre, codePre) + + stateObject.SetCode(codeHash, code) + s.parallel.codeChangesInSlot[addr] = struct{}{} + } +} + +func (s *ParallelStateDB) SetState(addr common.Address, key, value common.Hash) { + log.Debug("ParallelStateDB SetState", "SlotxIndex", s.parallel.SlotIndex, "txIndex", s.TxIndex()) + + stateObject := s.GetOrNewStateObject(addr) // attention: if StateObject's lightCopy, its storage is only a part of the full storage, + if stateObject != nil { + if s.parallel.baseTxIndex+1 == s.txIndex { + // we check if state is unchanged + // only when current transaction is the next transaction to be committed + // fixme: there is a bug, block: 14,962,284, + // stateObject is in dirty (light copy), but the key is in mainStateDB + // stateObject dirty -> committed, will skip mainStateDB dirty + if s.GetState(addr, key) == value { + log.Debug("Skip set same state", "baseTxIndex", s.parallel.baseTxIndex, + "txIndex", s.txIndex, "addr", addr, + "key", key, "value", value) + return + } + } + + if s.parallel.kvChangesInSlot[addr] == nil { + s.parallel.kvChangesInSlot[addr] = make(StateKeys) // make(Storage, defaultNumOfSlots) + } + + if _, ok := s.parallel.dirtiedStateObjectsInSlot[addr]; !ok { + newStateObject := stateObject.lightCopy(s) + newStateObject.SetState(s.db, key, value) + s.parallel.dirtiedStateObjectsInSlot[addr] = newStateObject + return + } + // do State Update + stateObject.SetState(s.db, key, value) + } +} + +// 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 (s *ParallelStateDB) Suicide(addr common.Address) bool { + log.Debug("ParallelStateDB Suicide", "SlotxIndex", s.parallel.SlotIndex, "txIndex", s.TxIndex()) + + var stateObject *StateObject + // 1.Try to get from dirty, it could be suicided inside of contract call + stateObject = s.parallel.dirtiedStateObjectsInSlot[addr] + if stateObject == nil { + // 2.Try to get from uncomfirmed, if deleted return false, since the address does not exist + if obj, ok := s.getStateObjectFromUnconfirmedDB(addr); ok { + stateObject = obj + s.parallel.addrStateReadsInSlot[addr] = !stateObject.deleted // true: exist, false: deleted + if stateObject.deleted { + log.Error("Suicide addr alreay deleted in confirmed DB", "txIndex", s.txIndex, "addr", addr) + return false + } + } + } + + if stateObject == nil { + // 3.Try to get from main StateDB + stateObject = s.getStateObjectNoSlot(addr) + if stateObject == nil { + s.parallel.addrStateReadsInSlot[addr] = false // true: exist, false: deleted + log.Error("Suicide addr not exist", "txIndex", s.txIndex, "addr", addr) + return false + } + s.parallel.addrStateReadsInSlot[addr] = true // true: exist, false: deleted + } + + s.journal.append(suicideChange{ + account: &addr, + prev: stateObject.suicided, // todo: must be false? + prevbalance: new(big.Int).Set(s.GetBalance(addr)), + }) + + if _, ok := s.parallel.dirtiedStateObjectsInSlot[addr]; !ok { + // do copy-on-write for suicide "write" + newStateObject := stateObject.lightCopy(s) + newStateObject.markSuicided() + newStateObject.data.Balance = new(big.Int) + s.parallel.dirtiedStateObjectsInSlot[addr] = newStateObject + s.parallel.addrStateChangesInSlot[addr] = false // false: the address does not exist any more, + // s.parallel.nonceChangesInSlot[addr] = struct{}{} + s.parallel.balanceChangesInSlot[addr] = struct{}{} + s.parallel.codeChangesInSlot[addr] = struct{}{} + // s.parallel.kvChangesInSlot[addr] = make(StateKeys) // all key changes are discarded + return true + } + s.parallel.addrStateChangesInSlot[addr] = false // false: the address does not exist any more, + s.parallel.balanceChangesInSlot[addr] = struct{}{} + s.parallel.codeChangesInSlot[addr] = struct{}{} + + stateObject.markSuicided() + stateObject.data.Balance = new(big.Int) + return true +} + +// 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 (s *ParallelStateDB) CreateAccount(addr common.Address) { + // no matter it is got from dirty, unconfirmed or main DB + // if addr not exist, preBalance will be common.Big0, it is same as new(big.Int) which + // is the value newObject(), + preBalance := s.GetBalance(addr) // parallel balance read will be recorded inside of GetBalance + newObj := s.createObject(addr) + newObj.setBalance(new(big.Int).Set(preBalance)) // new big.Int for newObj +} + +// RevertToSnapshot reverts all state changes made since the given revision. +func (s *ParallelStateDB) RevertToSnapshot(revid int) { + // Find the snapshot in the stack of valid snapshots. + idx := sort.Search(len(s.validRevisions), func(i int) bool { + return s.validRevisions[i].id >= revid + }) + if idx == len(s.validRevisions) || s.validRevisions[idx].id != revid { + panic(fmt.Errorf("revision id %v cannot be reverted", revid)) + } + snapshot := s.validRevisions[idx].journalIndex + + // Replay the journal to undo changes and remove invalidated snapshots + s.journal.revert(s, snapshot) + s.validRevisions = s.validRevisions[:idx] +} + +// AddRefund adds gas to the refund counter +// journal.append will use ParallelState for revert +func (s *ParallelStateDB) AddRefund(gas uint64) { // fixme: not needed + s.journal.append(refundChange{prev: s.refund}) + s.refund += gas +} + +// SubRefund removes gas from the refund counter. +// This method will panic if the refund counter goes below zero +func (s *ParallelStateDB) SubRefund(gas uint64) { // fixme: not needed + s.journal.append(refundChange{prev: s.refund}) + if gas > s.refund { + // we don't need to panic here if we read the wrong state in parallelm mode + // we just need to redo this transaction + log.Info(fmt.Sprintf("Refund counter below zero (gas: %d > refund: %d)", gas, s.refund), "tx", s.thash.String()) + s.parallel.needsRedo = true + return + } + s.refund -= gas +} + +// For Parallel Execution Mode, it can be seen as Penetrated Access: +// ------------------------------------------------------- +// | BaseTxIndex | Unconfirmed Txs... | Current TxIndex | +// ------------------------------------------------------- +// Access from the unconfirmed DB with range&priority: txIndex -1(previous tx) -> baseTxIndex + 1 +func (s *ParallelStateDB) getBalanceFromUnconfirmedDB(addr common.Address) *big.Int { + if addr == s.parallel.systemAddress { + // never get systemaddress from unconfirmed DB + return nil + } + + for i := s.txIndex - 1; i > s.parallel.baseTxIndex; i-- { + if db, ok := s.parallel.unconfirmedDBInShot[i]; ok { + // 1.Refer the state of address, exist or not in dirtiedStateObjectsInSlot + if obj, exist := db.parallel.dirtiedStateObjectsInSlot[addr]; exist { + balanceHit := false + if _, exist := db.parallel.addrStateChangesInSlot[addr]; exist { + balanceHit = true + } + if _, exist := db.parallel.balanceChangesInSlot[addr]; exist { // only changed balance is reliable + balanceHit = true + } + if !balanceHit { + continue + } + balance := obj.Balance() + if obj.deleted { + balance = common.Big0 + } + return balance + } + } + } + return nil +} + +// Similar to getBalanceFromUnconfirmedDB +func (s *ParallelStateDB) getNonceFromUnconfirmedDB(addr common.Address) (uint64, bool) { + if addr == s.parallel.systemAddress { + // never get systemaddress from unconfirmed DB + return 0, false + } + + for i := s.txIndex - 1; i > s.parallel.baseTxIndex; i-- { + if unconfirmedDb, ok := s.parallel.unconfirmedDBInShot[i]; ok { + nonceHit := false + if _, ok := unconfirmedDb.parallel.addrStateChangesInSlot[addr]; ok { + nonceHit = true + } else if _, ok := unconfirmedDb.parallel.nonceChangesInSlot[addr]; ok { + nonceHit = true + } + if !nonceHit { + // nonce refer not hit, try next unconfirmedDb + continue + } + // nonce hit, return the nonce + obj := unconfirmedDb.parallel.dirtiedStateObjectsInSlot[addr] + if obj == nil { + // could not exist, if it is changed but reverted + // fixme: revert should remove the change record + log.Debug("Get nonce from UnconfirmedDB, changed but object not exist, ", + "txIndex", s.txIndex, "referred txIndex", i, "addr", addr) + continue + } + nonce := obj.Nonce() + // deleted object with nonce == 0 + if obj.deleted { + nonce = 0 + } + return nonce, true + } + } + return 0, false +} + +// Similar to getBalanceFromUnconfirmedDB +// It is not only for code, but also codeHash and codeSize, we return the *StateObject for convienence. +func (s *ParallelStateDB) getCodeFromUnconfirmedDB(addr common.Address) ([]byte, bool) { + if addr == s.parallel.systemAddress { + // never get systemaddress from unconfirmed DB + return nil, false + } + + for i := s.txIndex - 1; i > s.parallel.baseTxIndex; i-- { + if db, ok := s.parallel.unconfirmedDBInShot[i]; ok { + codeHit := false + if _, exist := db.parallel.addrStateChangesInSlot[addr]; exist { + codeHit = true + } + if _, exist := db.parallel.codeChangesInSlot[addr]; exist { + codeHit = true + } + if !codeHit { + // try next unconfirmedDb + continue + } + obj := db.parallel.dirtiedStateObjectsInSlot[addr] + if obj == nil { + // could not exist, if it is changed but reverted + // fixme: revert should remove the change record + log.Debug("Get code from UnconfirmedDB, changed but object not exist, ", + "txIndex", s.txIndex, "referred txIndex", i, "addr", addr) + continue + } + code := obj.Code(s.db) + if obj.deleted { + code = nil + } + return code, true + } + } + return nil, false +} + +// Similar to getCodeFromUnconfirmedDB +// but differ when address is deleted or not exist +func (s *ParallelStateDB) getCodeHashFromUnconfirmedDB(addr common.Address) (common.Hash, bool) { + if addr == s.parallel.systemAddress { + // never get systemaddress from unconfirmed DB + return common.Hash{}, false + } + + for i := s.txIndex - 1; i > s.parallel.baseTxIndex; i-- { + if db, ok := s.parallel.unconfirmedDBInShot[i]; ok { + hashHit := false + if _, exist := db.parallel.addrStateChangesInSlot[addr]; exist { + hashHit = true + } + if _, exist := db.parallel.codeChangesInSlot[addr]; exist { + hashHit = true + } + if !hashHit { + // try next unconfirmedDb + continue + } + + obj := db.parallel.dirtiedStateObjectsInSlot[addr] + if obj == nil { + // could not exist, if it is changed but reverted + // fixme: revert should remove the change record + log.Debug("Get codeHash from UnconfirmedDB, changed but object not exist, ", + "txIndex", s.txIndex, "referred txIndex", i, "addr", addr) + continue + } + codeHash := common.Hash{} + if !obj.deleted { + codeHash = common.BytesToHash(obj.CodeHash()) + } + return codeHash, true + } + } + return common.Hash{}, false +} + +// Similar to getCodeFromUnconfirmedDB +// It is for address state check of: Exist(), Empty() and HasSuicided() +// Since the unconfirmed DB should have done Finalise() with `deleteEmptyObjects = true` +// If the dirty address is empty or suicided, it will be marked as deleted, so we only need to return `deleted` or not. +func (s *ParallelStateDB) getAddrStateFromUnconfirmedDB(addr common.Address) (bool, bool) { + if addr == s.parallel.systemAddress { + // never get systemaddress from unconfirmed DB + return false, false + } + + // check the unconfirmed DB with range: baseTxIndex -> txIndex -1(previous tx) + for i := s.txIndex - 1; i > s.parallel.baseTxIndex; i-- { + if db, ok := s.parallel.unconfirmedDBInShot[i]; ok { + if exist, ok := db.parallel.addrStateChangesInSlot[addr]; ok { + if _, ok := db.parallel.dirtiedStateObjectsInSlot[addr]; !ok { + // could not exist, if it is changed but reverted + // fixme: revert should remove the change record + log.Debug("Get addr State from UnconfirmedDB, changed but object not exist, ", + "txIndex", s.txIndex, "referred txIndex", i, "addr", addr) + continue + } + + return exist, true + } + } + } + return false, false } -func (s *StateDB) DiffLayerToSnap(diffLayer *types.DiffLayer) (map[common.Address]struct{}, map[common.Address][]byte, map[common.Address]map[string][]byte, error) { - snapDestructs := make(map[common.Address]struct{}) - snapAccounts := make(map[common.Address][]byte) - snapStorage := make(map[common.Address]map[string][]byte) - - for _, des := range diffLayer.Destructs { - snapDestructs[des] = struct{}{} - } - for _, account := range diffLayer.Accounts { - snapAccounts[account.Account] = account.Blob - } - for _, storage := range diffLayer.Storages { - // should never happen - if len(storage.Keys) != len(storage.Vals) { - return nil, nil, nil, errors.New("invalid diffLayer: length of keys and values mismatch") - } - snapStorage[storage.Account] = make(map[string][]byte, len(storage.Keys)) - n := len(storage.Keys) - for i := 0; i < n; i++ { - snapStorage[storage.Account][storage.Keys[i]] = storage.Vals[i] +func (s *ParallelStateDB) getKVFromUnconfirmedDB(addr common.Address, key common.Hash) (common.Hash, bool) { + // check the unconfirmed DB with range: baseTxIndex -> txIndex -1(previous tx) + for i := s.txIndex - 1; i > s.parallel.baseTxIndex; i-- { + if db, ok := s.parallel.unconfirmedDBInShot[i]; ok { + if obj, ok := db.parallel.dirtiedStateObjectsInSlot[addr]; ok { // if deleted on merge, can get from main StateDB, ok but fixme: concurrent safe + if obj.deleted { + return common.Hash{}, true + } + if _, ok := db.parallel.kvChangesInSlot[addr]; ok { + if val, exist := obj.dirtyStorage.GetValue(key); exist { + return val, true + } + if val, exist := obj.pendingStorage.GetValue(key); exist { // fixme: can be removed + log.Error("Get KV from Unconfirmed StateDB, in pending", + "my txIndex", s.txIndex, "DB's txIndex", i, "addr", addr, + "key", key, "val", val) + return val, true + } + } + } } } - return snapDestructs, snapAccounts, snapStorage, nil + return common.Hash{}, false } -func (s *StateDB) SnapToDiffLayer() ([]common.Address, []types.DiffAccount, []types.DiffStorage) { - destructs := make([]common.Address, 0, len(s.snapDestructs)) - for account := range s.snapDestructs { - destructs = append(destructs, account) - } - accounts := make([]types.DiffAccount, 0, len(s.snapAccounts)) - for accountHash, account := range s.snapAccounts { - accounts = append(accounts, types.DiffAccount{ - Account: accountHash, - Blob: account, - }) - } - storages := make([]types.DiffStorage, 0, len(s.snapStorage)) - for accountHash, storage := range s.snapStorage { - keys := make([]string, 0, len(storage)) - values := make([][]byte, 0, len(storage)) - for k, v := range storage { - keys = append(keys, k) - values = append(values, v) +func (s *ParallelStateDB) getStateObjectFromUnconfirmedDB(addr common.Address) (*StateObject, bool) { + // check the unconfirmed DB with range: baseTxIndex -> txIndex -1(previous tx) + for i := s.txIndex - 1; i > s.parallel.baseTxIndex; i-- { + if db, ok := s.parallel.unconfirmedDBInShot[i]; ok { + if obj, ok := db.parallel.dirtiedStateObjectsInSlot[addr]; ok { // if deleted on merge, can get from main StateDB, ok but fixme: concurrent safe + return obj, true + } } - storages = append(storages, types.DiffStorage{ - Account: accountHash, - Keys: keys, - Vals: values, - }) } - return destructs, accounts, storages + return nil, false } -// PrepareAccessList handles the preparatory steps for executing a state transition with -// regards to both EIP-2929 and EIP-2930: -// -// - Add sender to access list (2929) -// - Add destination to access list (2929) -// - Add precompiles to access list (2929) -// - Add the contents of the optional tx access list (2930) -// -// This method should only be called if Yolov3/Berlin/2929+2930 is applicable at the current number. -func (s *StateDB) PrepareAccessList(sender common.Address, dst *common.Address, precompiles []common.Address, list types.AccessList) { - s.AddAddressToAccessList(sender) - if dst != nil { - s.AddAddressToAccessList(*dst) - // If it's a create-tx, the destination will be added inside evm.create +func (s *ParallelStateDB) IsParallelReadsValid() bool { + slotDB := s + if !slotDB.parallel.isSlotDB { + log.Error("IsSlotDBReadsValid slotDB should be slot DB", "SlotIndex", slotDB.parallel.SlotIndex, "txIndex", slotDB.txIndex) + return false } - for _, addr := range precompiles { - s.AddAddressToAccessList(addr) + + mainDB := slotDB.parallel.baseStateDB + if mainDB.parallel.isSlotDB { + log.Error("IsSlotDBReadsValid s should be main DB", "SlotIndex", slotDB.parallel.SlotIndex, "txIndex", slotDB.txIndex) + return false } - for _, el := range list { - s.AddAddressToAccessList(el.Address) - for _, key := range el.StorageKeys { - s.AddSlotToAccessList(el.Address, key) + // for nonce + for addr, nonceSlot := range slotDB.parallel.nonceReadsInSlot { + nonceMain := mainDB.GetNonce(addr) + if nonceSlot != nonceMain { + log.Debug("IsSlotDBReadsValid nonce read is invalid", "addr", addr, + "nonceSlot", nonceSlot, "nonceMain", nonceMain, "SlotIndex", slotDB.parallel.SlotIndex, + "txIndex", slotDB.txIndex, "baseTxIndex", slotDB.parallel.baseTxIndex) + return false } } -} - -// AddAddressToAccessList adds the given address to the access list -func (s *StateDB) AddAddressToAccessList(addr common.Address) { - if s.accessList == nil { - s.accessList = newAccessList() - } - if s.accessList.AddAddress(addr) { - s.journal.append(accessListAddAccountChange{&addr}) + // balance + for addr, balanceSlot := range slotDB.parallel.balanceReadsInSlot { + if addr != s.parallel.systemAddress { // skip balance check for system address + balanceMain := mainDB.GetBalance(addr) + if balanceSlot.Cmp(balanceMain) != 0 { + log.Debug("IsSlotDBReadsValid balance read is invalid", "addr", addr, + "balanceSlot", balanceSlot, "balanceMain", balanceMain, "SlotIndex", slotDB.parallel.SlotIndex, + "txIndex", slotDB.txIndex, "baseTxIndex", slotDB.parallel.baseTxIndex) + return false + } + } } -} - -// AddSlotToAccessList adds the given (address, slot)-tuple to the access list -func (s *StateDB) AddSlotToAccessList(addr common.Address, slot common.Hash) { - if s.accessList == nil { - s.accessList = newAccessList() + // check code + for addr, codeSlot := range slotDB.parallel.codeReadsInSlot { + codeMain := mainDB.GetCode(addr) + if !bytes.Equal(codeSlot, codeMain) { + log.Debug("IsSlotDBReadsValid code read is invalid", "addr", addr, + "len codeSlot", len(codeSlot), "len codeMain", len(codeMain), "SlotIndex", slotDB.parallel.SlotIndex, + "txIndex", slotDB.txIndex, "baseTxIndex", slotDB.parallel.baseTxIndex) + return false + } } - addrMod, slotMod := s.accessList.AddSlot(addr, slot) - if addrMod { - // In practice, this should not happen, since there is no way to enter the - // scope of 'address' without having the 'address' become already added - // to the access list (via call-variant, create, etc). - // Better safe than sorry, though - s.journal.append(accessListAddAccountChange{&addr}) + // check codeHash + for addr, codeHashSlot := range slotDB.parallel.codeHashReadsInSlot { + codeHashMain := mainDB.GetCodeHash(addr) + if !bytes.Equal(codeHashSlot.Bytes(), codeHashMain.Bytes()) { + log.Debug("IsSlotDBReadsValid codehash read is invalid", "addr", addr, + "codeHashSlot", codeHashSlot, "codeHashMain", codeHashMain, "SlotIndex", slotDB.parallel.SlotIndex, + "txIndex", slotDB.txIndex, "baseTxIndex", slotDB.parallel.baseTxIndex) + return false + } } - if slotMod { - s.journal.append(accessListAddSlotChange{ - address: &addr, - slot: &slot, + // check KV + for addr, slotStorage := range slotDB.parallel.kvReadsInSlot { + conflict := false + slotStorage.Range(func(keySlot, valSlot interface{}) bool { + valMain := mainDB.GetState(addr, keySlot.(common.Hash)) + if !bytes.Equal(valSlot.(common.Hash).Bytes(), valMain.Bytes()) { + log.Debug("IsSlotDBReadsValid KV read is invalid", "addr", addr, + "key", keySlot.(common.Hash), "valSlot", valSlot.(common.Hash), + "valMain", valMain, "SlotIndex", slotDB.parallel.SlotIndex, + "txIndex", slotDB.txIndex, "baseTxIndex", slotDB.parallel.baseTxIndex) + conflict = true + return false // return false, Range will be terminated. + } + return true // return true, Range will try next KV }) + if conflict { + return false + } } -} - -// AddressInAccessList returns true if the given address is in the access list. -func (s *StateDB) AddressInAccessList(addr common.Address) bool { - if s.accessList == nil { - return false + // addr state check + for addr, stateSlot := range slotDB.parallel.addrStateReadsInSlot { + stateMain := false // addr not exist + if mainDB.getStateObject(addr) != nil { + stateMain = true // addr exist in main DB + } + if stateSlot != stateMain { + // skip addr state check for system address + if addr != s.parallel.systemAddress { + log.Debug("IsSlotDBReadsValid addrState read invalid(true: exist, false: not exist)", + "addr", addr, "stateSlot", stateSlot, "stateMain", stateMain, + "SlotIndex", slotDB.parallel.SlotIndex, + "txIndex", slotDB.txIndex, "baseTxIndex", slotDB.parallel.baseTxIndex) + return false + } + } } - return s.accessList.ContainsAddress(addr) -} + // snapshot destructs check -// SlotInAccessList returns true if the given (address, slot)-tuple is in the access list. -func (s *StateDB) SlotInAccessList(addr common.Address, slot common.Hash) (addressPresent bool, slotPresent bool) { - if s.accessList == nil { - return false, false + for addr, destructRead := range slotDB.parallel.addrSnapDestructsReadsInSlot { + mainObj := mainDB.getStateObject(addr) + if mainObj == nil { + log.Debug("IsSlotDBReadsValid snapshot destructs read invalid, address should exist", + "addr", addr, "destruct", destructRead, + "SlotIndex", slotDB.parallel.SlotIndex, + "txIndex", slotDB.txIndex, "baseTxIndex", slotDB.parallel.baseTxIndex) + return false + } + _, destructMain := mainDB.snapDestructs[addr] // addr not exist + if destructRead != destructMain { + log.Debug("IsSlotDBReadsValid snapshot destructs read invalid", + "addr", addr, "destructRead", destructRead, "destructMain", destructMain, + "SlotIndex", slotDB.parallel.SlotIndex, + "txIndex", slotDB.txIndex, "baseTxIndex", slotDB.parallel.baseTxIndex) + return false + } } - return s.accessList.Contains(addr, slot) + + return true } -func (s *StateDB) GetDirtyAccounts() []common.Address { - accounts := make([]common.Address, 0, len(s.stateObjectsDirty)) - for account := range s.stateObjectsDirty { - accounts = append(accounts, account) - } - return accounts +// For most of the transactions, systemAddressOpsCount should be 3: +// one for SetBalance(0) on NewSlotDB() +// the other is for AddBalance(GasFee) at the end. +// (systemAddressOpsCount > 3) means the transaction tries to access systemAddress, in +// this case, we should redo and keep its balance on NewSlotDB() +func (s *ParallelStateDB) SystemAddressRedo() bool { + return s.parallel.systemAddressOpsCount > 4 } -func (s *StateDB) GetStorage(address common.Address) *sync.Map { - return s.storagePool.getStorage(address) +// NeedsRedo returns true if there is any clear reason that we need to redo this transaction +func (s *ParallelStateDB) NeedsRedo() bool { + return s.parallel.needsRedo } diff --git a/core/state_processor.go b/core/state_processor.go index 9e6da7fee1..3398d9f32b 100644 --- a/core/state_processor.go +++ b/core/state_processor.go @@ -77,9 +77,10 @@ type ParallelStateProcessor struct { // txReqAccountSorted map[common.Address][]*ParallelTxRequest // fixme: *ParallelTxRequest => ParallelTxRequest? slotState []*SlotState // idle, or pending messages mergedTxIndex int // the latest finalized tx index - slotDBsToRelease []*state.StateDB + slotDBsToRelease []*state.ParallelStateDB debugErrorRedoNum int debugConflictRedoNum int + unconfirmedStateDBs *sync.Map // [int]*state.StateDB // fixme: concurrent safe, not use sync.Map? } func NewParallelStateProcessor(config *params.ChainConfig, bc *BlockChain, engine consensus.Engine, parallelNum int, queueSize int) *ParallelStateProcessor { @@ -398,10 +399,10 @@ func (p *LightStateProcessor) LightProcess(diffLayer *types.DiffLayer, block *ty type SlotState struct { pendingTxReqChan chan struct{} pendingConfirmChan chan *ParallelTxResult - pendingTxReqList []*ParallelTxRequest // maintained by dispatcher for dispatch policy - slotdbChan chan *state.StateDB // dispatch will create and send this slotDB to slot + pendingTxReqList []*ParallelTxRequest // maintained by dispatcher for dispatch policy + slotdbChan chan *state.ParallelStateDB // dispatch will create and send this slotDB to slot // txReqUnits []*ParallelDispatchUnit // only dispatch can accesssd - unconfirmedStateDBs *sync.Map // [int]*state.StateDB // fixme: concurrent safe, not use sync.Map? + // unconfirmedStateDBs *sync.Map // [int]*state.StateDB // fixme: concurrent safe, not use sync.Map? } type ParallelTxResult struct { @@ -411,7 +412,7 @@ type ParallelTxResult struct { err error // to describe error message? txReq *ParallelTxRequest receipt *types.Receipt - slotDB *state.StateDB // if updated, it is not equal to txReq.slotDB + slotDB *state.ParallelStateDB // if updated, it is not equal to txReq.slotDB gpSlot *GasPool evm *vm.EVM result *ExecutionResult @@ -420,7 +421,7 @@ type ParallelTxResult struct { type ParallelTxRequest struct { txIndex int tx *types.Transaction - slotDB *state.StateDB + slotDB *state.ParallelStateDB gasLimit uint64 msg types.Message block *types.Block @@ -441,7 +442,7 @@ func (p *ParallelStateProcessor) init() { for i := 0; i < p.parallelNum; i++ { p.slotState[i] = &SlotState{ - slotdbChan: make(chan *state.StateDB, 1), + slotdbChan: make(chan *state.ParallelStateDB, 1), pendingTxReqChan: make(chan struct{}, 1), pendingConfirmChan: make(chan *ParallelTxResult, p.queueSize), } @@ -679,7 +680,7 @@ func (p *ParallelStateProcessor) waitUntilNextTxDone(statedb *state.StateDB, gp // the target slot is waiting for new slotDB slotState := p.slotState[result.slotIndex] slotDB := state.NewSlotDB(statedb, consensus.SystemAddress, result.txReq.txIndex, - p.mergedTxIndex, result.keepSystem, slotState.unconfirmedStateDBs) + p.mergedTxIndex, result.keepSystem, p.unconfirmedStateDBs) slotDB.SetSlotIndex(result.slotIndex) p.slotDBsToRelease = append(p.slotDBsToRelease, slotDB) slotState.slotdbChan <- slotDB @@ -866,7 +867,7 @@ func (p *ParallelStateProcessor) runSlotLoop(slotIndex int) { txReq.slotDB = <-curSlot.slotdbChan } result := p.executeInSlot(slotIndex, txReq) - curSlot.unconfirmedStateDBs.Store(txReq.txIndex, txReq.slotDB) + p.unconfirmedStateDBs.Store(txReq.txIndex, txReq.slotDB) curSlot.pendingConfirmChan <- result } } @@ -894,7 +895,7 @@ func (p *ParallelStateProcessor) resetState(txNum int, statedb *state.StateDB) { statedb.PrepareForParallel() - p.slotDBsToRelease = make([]*state.StateDB, 0, txNum) + p.slotDBsToRelease = make([]*state.ParallelStateDB, 0, txNum) /* stateDBsToRelease := p.slotDBsToRelease go func() { @@ -905,8 +906,9 @@ func (p *ParallelStateProcessor) resetState(txNum int, statedb *state.StateDB) { */ for _, slot := range p.slotState { slot.pendingTxReqList = make([]*ParallelTxRequest, 0) - slot.unconfirmedStateDBs = new(sync.Map) // make(map[int]*state.StateDB), fixme: resue not new? + // slot.unconfirmedStateDBs = new(sync.Map) // make(map[int]*state.StateDB), fixme: resue not new? } + p.unconfirmedStateDBs = new(sync.Map) // make(map[int]*state.StateDB), fixme: resue not new? } // Implement BEP-130: Parallel Transaction Execution. @@ -916,10 +918,12 @@ func (p *ParallelStateProcessor) Process(block *types.Block, statedb *state.Stat header = block.Header() gp = new(GasPool).AddGas(block.GasLimit()) ) - log.Info("ProcessParallel", "block", header.Number) var receipts = make([]*types.Receipt, 0) txNum := len(block.Transactions()) p.resetState(txNum, statedb) + if txNum > 0 { + log.Info("ProcessParallel", "block", header.Number) + } // Iterate over and process the individual transactions posa, isPoSA := p.engine.(consensus.PoSA) @@ -1150,7 +1154,7 @@ func applyTransaction(msg types.Message, config *params.ChainConfig, bc ChainCon return receipt, err } -func applyTransactionStageExecution(msg types.Message, gp *GasPool, statedb *state.StateDB, evm *vm.EVM) (*vm.EVM, *ExecutionResult, error) { +func applyTransactionStageExecution(msg types.Message, gp *GasPool, statedb *state.ParallelStateDB, evm *vm.EVM) (*vm.EVM, *ExecutionResult, error) { // Create a new context to be used in the EVM environment. txContext := NewEVMTxContext(msg) evm.Reset(txContext, statedb) @@ -1164,7 +1168,7 @@ func applyTransactionStageExecution(msg types.Message, gp *GasPool, statedb *sta return evm, result, err } -func applyTransactionStageFinalization(evm *vm.EVM, result *ExecutionResult, msg types.Message, config *params.ChainConfig, statedb *state.StateDB, header *types.Header, tx *types.Transaction, usedGas *uint64, receiptProcessors ...ReceiptProcessor) (*types.Receipt, error) { +func applyTransactionStageFinalization(evm *vm.EVM, result *ExecutionResult, msg types.Message, config *params.ChainConfig, statedb *state.ParallelStateDB, header *types.Header, tx *types.Transaction, usedGas *uint64, receiptProcessors ...ReceiptProcessor) (*types.Receipt, error) { // Update the state with pending changes. var root []byte if config.IsByzantium(header.Number) {