Skip to content
Open
82 changes: 82 additions & 0 deletions core/state/interface.go
Original file line number Diff line number Diff line change
@@ -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 <http://www.gnu.org/licenses/>.

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
}
52 changes: 30 additions & 22 deletions core/state/journal.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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)
Expand All @@ -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)
Expand All @@ -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)
Expand All @@ -190,54 +192,57 @@ 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
}

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)
Expand All @@ -251,15 +256,17 @@ 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)
}

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:
Expand All @@ -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)
}
Expand Down
22 changes: 11 additions & 11 deletions core/state/state_object.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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

}
Expand All @@ -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?
}
Expand All @@ -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,
Expand Down Expand Up @@ -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
}
Expand Down Expand Up @@ -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.
Expand All @@ -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,
Expand All @@ -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
Expand Down Expand Up @@ -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(),
Expand All @@ -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,
Expand Down
Loading