core/state: new state forking

The old state copy mechanism made deep copy for every object in state,
most of them not required by the new state and therefor wasting a ton of
resources. The copy of a state can be made using the `state.Fork`
function, which will allocate a new State and return it. This new object
holds a refernce to it's parent which can be used to query for any
information not currently available. When an object can not be found it
will recusively call the parent for the object until it reached the root
state and checks the state trie. Objects are now copied on demand and
will never be copied during the `state.Fork` function call.

State can also be flattened in to one single object, merging all the
state from root to current, leaving a single state with all recent
changes. State can be flattened using `state.Flatten`

In addition the following methods have changed to functions instead:

* state.Commit
* state.BatchCommit
* state.IntermediateRoot
This commit is contained in:
Jeffrey Wilcke 2016-10-03 00:21:00 +02:00
parent b4b5921dd0
commit 4728db84aa
14 changed files with 332 additions and 163 deletions

View file

@ -929,7 +929,8 @@ func (self *BlockChain) InsertChain(chain types.Blocks) (int, error) {
return i, err return i, err
} }
// Write state changes to database // Write state changes to database
_, err = self.stateCache.Commit() _, err = state.Commit(self.stateCache)
//_, err = self.stateCache.Commit()
if err != nil { if err != nil {
return i, err return i, err
} }

View file

@ -105,8 +105,8 @@ func (b *BlockGen) AddTx(tx *types.Transaction) {
if b.gasPool == nil { if b.gasPool == nil {
b.SetCoinbase(common.Address{}) b.SetCoinbase(common.Address{})
} }
b.statedb.StartRecord(tx.Hash(), common.Hash{}, len(b.txs)) b.statedb.TransitionState(tx.Hash(), common.Hash{}, len(b.txs))
receipt, _, _, err := ApplyTransaction(MakeChainConfig(), nil, b.gasPool, b.statedb, b.header, tx, b.header.GasUsed, vm.Config{}) _, receipt, _, _, err := ApplyTransaction(MakeChainConfig(), nil, b.gasPool, b.statedb, b.header, tx, b.header.GasUsed, vm.Config{})
if err != nil { if err != nil {
panic(err) panic(err)
} }
@ -203,7 +203,8 @@ func GenerateChain(config *ChainConfig, parent *types.Block, db ethdb.Database,
gen(i, b) gen(i, b)
} }
AccumulateRewards(statedb, h, b.uncles) AccumulateRewards(statedb, h, b.uncles)
root, err := statedb.Commit() //root, err := statedb.Commit()
root, err := state.Commit(statedb)
if err != nil { if err != nil {
panic(fmt.Sprintf("state write error: %v", err)) panic(fmt.Sprintf("state write error: %v", err))
} }

View file

@ -20,6 +20,7 @@ import (
"math/big" "math/big"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/params"
@ -169,7 +170,7 @@ func execDelegateCall(env vm.Environment, caller vm.ContractRef, originAddr, toA
} }
// generic transfer method // generic transfer method
func Transfer(from, to vm.Account, amount *big.Int) { func Transfer(statedb *state.StateDB, from, to common.Address, amount *big.Int) {
from.SubBalance(amount) statedb.SubBalance(from, amount)
to.AddBalance(amount) statedb.AddBalance(to, amount)
} }

View file

@ -73,7 +73,8 @@ func WriteGenesisBlock(chainDb ethdb.Database, reader io.Reader) (*types.Block,
statedb.SetState(address, common.HexToHash(key), common.HexToHash(value)) statedb.SetState(address, common.HexToHash(key), common.HexToHash(value))
} }
} }
root, stateBatch := statedb.CommitBatch() root, stateBatch := state.CommitBatch(statedb)
//root, stateBatch := statedb.CommitBatch()
difficulty := common.String2Big(genesis.Difficulty) difficulty := common.String2Big(genesis.Difficulty)
block := types.NewBlock(&types.Header{ block := types.NewBlock(&types.Header{
@ -128,7 +129,8 @@ func GenesisBlockForTesting(db ethdb.Database, addr common.Address, balance *big
statedb, _ := state.New(common.Hash{}, db) statedb, _ := state.New(common.Hash{}, db)
obj := statedb.GetOrNewStateObject(addr) obj := statedb.GetOrNewStateObject(addr)
obj.SetBalance(balance) obj.SetBalance(balance)
root, err := statedb.Commit() root, err := state.Commit(statedb)
//root, err := statedb.Commit()
if err != nil { if err != nil {
panic(fmt.Sprintf("cannot write state: %v", err)) panic(fmt.Sprintf("cannot write state: %v", err))
} }

View file

@ -39,7 +39,7 @@ type ManagedState struct {
// ManagedState returns a new managed state with the statedb as it's backing layer // ManagedState returns a new managed state with the statedb as it's backing layer
func ManageState(statedb *StateDB) *ManagedState { func ManageState(statedb *StateDB) *ManagedState {
return &ManagedState{ return &ManagedState{
StateDB: statedb.Copy(), StateDB: Fork(statedb),
accounts: make(map[common.Address]*account), accounts: make(map[common.Address]*account),
} }
} }

View file

@ -46,6 +46,11 @@ const (
codeSizeCacheSize = 100000 codeSizeCacheSize = 100000
) )
type intermediateInfo struct {
txHash, blockHash common.Hash
txIdx uint
}
// StateDBs within the ethereum protocol are used to store anything // StateDBs within the ethereum protocol are used to store anything
// within the merkle trie. StateDBs take care of caching and storing // within the merkle trie. StateDBs take care of caching and storing
// nested states. It's the general query interface to retrieve: // nested states. It's the general query interface to retrieve:
@ -56,22 +61,70 @@ type StateDB struct {
trie *trie.SecureTrie trie *trie.SecureTrie
pastTries []*trie.SecureTrie pastTries []*trie.SecureTrie
codeSizeCache *lru.Cache codeSizeCache *lru.Cache
parent *StateDB
// This map holds 'live' objects, which will get modified while processing a state transition. // This map holds 'live' objects, which will get modified while processing a state transition.
stateObjects map[common.Address]*StateObject stateObjects map[common.Address]*StateObject
stateObjectsDirty map[common.Address]struct{} stateObjectsDirty map[common.Address]struct{}
localStateObjects map[common.Address]bool
// The refund counter, also used by state transitioning. // The refund counter, also used by state transitioning.
refund *big.Int refund *big.Int
thash, bhash common.Hash logIdx uint
txIndex int logs []*vm.Log
logs map[common.Hash]vm.Logs
logSize uint interInfo intermediateInfo
MarkedTransition bool // marks the transition between transactions
lock sync.Mutex lock sync.Mutex
} }
func (s *StateDB) TransitionState(txHash, blockHash common.Hash, txIdx int) {
if s.parent != nil {
s.parent.MarkedTransition = true
}
s.interInfo = intermediateInfo{txHash: txHash, blockHash: blockHash, txIdx: uint(txIdx)}
s.refund = new(big.Int)
}
// Read fetches the state object with the given address by first checking its own cache of state objects. If that didn't result in an object it will attempt to check it's line of ancestors.
func (s *StateDB) Read(address common.Address) (object *StateObject, inCache bool) {
stateObject := s.stateObjects[address]
if stateObject == nil && s.parent != nil {
stateObject, _ = s.parent.Read(address)
if stateObject != nil {
s.SetStateObject(stateObject)
}
} else if stateObject != nil {
inCache = true
}
if stateObject != nil {
if stateObject.deleted {
return nil, false
}
return stateObject, inCache
}
enc := s.trie.Get(address[:])
if len(enc) == 0 {
return nil, false
}
var data Account
if err := rlp.DecodeBytes(enc, &data); err != nil {
glog.Errorf("can't decode object at %x: %v", address[:], err)
return nil, false
}
// Insert into the live set.
obj := NewObject(address, data, s.MarkStateObjectDirty)
s.SetStateObject(obj)
//s.stateObjects[address] = obj
return obj, false
}
// Create a new state from a given trie // Create a new state from a given trie
func New(root common.Hash, db ethdb.Database) (*StateDB, error) { func New(root common.Hash, db ethdb.Database) (*StateDB, error) {
tr, err := trie.NewSecure(root, db) tr, err := trie.NewSecure(root, db)
@ -85,8 +138,8 @@ func New(root common.Hash, db ethdb.Database) (*StateDB, error) {
codeSizeCache: csc, codeSizeCache: csc,
stateObjects: make(map[common.Address]*StateObject), stateObjects: make(map[common.Address]*StateObject),
stateObjectsDirty: make(map[common.Address]struct{}), stateObjectsDirty: make(map[common.Address]struct{}),
localStateObjects: make(map[common.Address]bool),
refund: new(big.Int), refund: new(big.Int),
logs: make(map[common.Hash]vm.Logs),
}, nil }, nil
} }
@ -106,30 +159,27 @@ func (self *StateDB) New(root common.Hash) (*StateDB, error) {
codeSizeCache: self.codeSizeCache, codeSizeCache: self.codeSizeCache,
stateObjects: make(map[common.Address]*StateObject), stateObjects: make(map[common.Address]*StateObject),
stateObjectsDirty: make(map[common.Address]struct{}), stateObjectsDirty: make(map[common.Address]struct{}),
localStateObjects: make(map[common.Address]bool),
refund: new(big.Int), refund: new(big.Int),
logs: make(map[common.Hash]vm.Logs),
}, nil }, nil
} }
// Reset clears out all emphemeral state objects from the state db, but keeps // Reset clears out all emphemeral state objects from the state db, but keeps
// the underlying state trie to avoid reloading data for the next operations. // the underlying state trie to avoid reloading data for the next operations.
func (self *StateDB) Reset(root common.Hash) error { func (s *StateDB) Reset(root common.Hash) error {
self.lock.Lock() s.lock.Lock()
defer self.lock.Unlock() defer s.lock.Unlock()
tr, err := self.openTrie(root) tr, err := s.openTrie(root)
if err != nil { if err != nil {
return err return err
} }
self.trie = tr s.trie = tr
self.stateObjects = make(map[common.Address]*StateObject) s.stateObjects = make(map[common.Address]*StateObject)
self.stateObjectsDirty = make(map[common.Address]struct{}) s.stateObjectsDirty = make(map[common.Address]struct{})
self.refund = new(big.Int) s.localStateObjects = make(map[common.Address]bool)
self.thash = common.Hash{} s.refund = new(big.Int)
self.bhash = common.Hash{} s.logs = nil
self.txIndex = 0
self.logs = make(map[common.Hash]vm.Logs)
self.logSize = 0
return nil return nil
} }
@ -158,31 +208,27 @@ func (self *StateDB) pushTrie(t *trie.SecureTrie) {
} }
} }
func (self *StateDB) StartRecord(thash, bhash common.Hash, ti int) { func (s *StateDB) AddLog(log *vm.Log) {
self.thash = thash log.TxIndex = s.interInfo.txIdx
self.bhash = bhash log.TxHash = s.interInfo.txHash
self.txIndex = ti log.BlockHash = s.interInfo.blockHash
log.Index = s.logIdx
s.logs = append(s.logs, log)
s.logIdx++
} }
func (self *StateDB) AddLog(log *vm.Log) { // Logs returns the logs of it's entire ancestory chain or until
log.TxHash = self.thash // the marked transition is found (state between transactions).
log.BlockHash = self.bhash func (s *StateDB) Logs() []*vm.Log {
log.TxIndex = uint(self.txIndex) var logs []*vm.Log
log.Index = self.logSize if s.MarkedTransition {
self.logs[self.thash] = append(self.logs[self.thash], log) return nil
self.logSize++ } else if s.parent != nil {
} logs = s.parent.Logs()
func (self *StateDB) GetLogs(hash common.Hash) vm.Logs {
return self.logs[hash]
}
func (self *StateDB) Logs() vm.Logs {
var logs vm.Logs
for _, lgs := range self.logs {
logs = append(logs, lgs...)
} }
return logs return append(logs, s.logs...)
} }
func (self *StateDB) AddRefund(gas *big.Int) { func (self *StateDB) AddRefund(gas *big.Int) {
@ -282,6 +328,13 @@ func (self *StateDB) AddBalance(addr common.Address, amount *big.Int) {
} }
} }
func (self *StateDB) SubBalance(addr common.Address, amount *big.Int) {
stateObject := self.GetOrNewStateObject(addr)
if stateObject != nil {
stateObject.SubBalance(amount)
}
}
func (self *StateDB) SetNonce(addr common.Address, nonce uint64) { func (self *StateDB) SetNonce(addr common.Address, nonce uint64) {
stateObject := self.GetOrNewStateObject(addr) stateObject := self.GetOrNewStateObject(addr)
if stateObject != nil { if stateObject != nil {
@ -336,46 +389,56 @@ func (self *StateDB) DeleteStateObject(stateObject *StateObject) {
self.trie.Delete(addr[:]) self.trie.Delete(addr[:])
} }
// Retrieve a state object given my the address. Returns nil if not found.
func (self *StateDB) GetStateObject(addr common.Address) (stateObject *StateObject) {
// Prefer 'live' objects.
if obj := self.stateObjects[addr]; obj != nil {
if obj.deleted {
return nil
}
return obj
}
// Load the object from the database.
enc := self.trie.Get(addr[:])
if len(enc) == 0 {
return nil
}
var data Account
if err := rlp.DecodeBytes(enc, &data); err != nil {
glog.Errorf("can't decode object at %x: %v", addr[:], err)
return nil
}
// Insert into the live set.
obj := NewObject(addr, data, self.MarkStateObjectDirty)
self.SetStateObject(obj)
return obj
}
func (self *StateDB) SetStateObject(object *StateObject) { func (self *StateDB) SetStateObject(object *StateObject) {
if object == nil {
panic("is nil")
}
self.stateObjects[object.Address()] = object self.stateObjects[object.Address()] = object
} }
// Retrieve a state object or create a new state object if nil func (s *StateDB) SetOwnedStateObject(address common.Address, object *StateObject) {
func (self *StateDB) GetOrNewStateObject(addr common.Address) *StateObject { if object == nil {
stateObject := self.GetStateObject(addr) panic("is nil")
if stateObject == nil || stateObject.deleted { }
stateObject = self.CreateStateObject(addr) s.stateObjects[address] = object
s.localStateObjects[address] = true
}
func (s *StateDB) GetOrNewStateObject(address common.Address) *StateObject {
stateObject, _ := s.Read(address)
if stateObject != nil {
if !s.localStateObjects[address] {
stateObject = stateObject.Copy(s.db, s.MarkStateObjectDirty)
s.SetOwnedStateObject(address, stateObject)
//s.stateObjects[address] = stateObject
//s.localStateObjects[address] = true
}
return stateObject
} }
if stateObject == nil || stateObject.deleted {
stateObject = NewObject(address, Account{}, s.MarkStateObjectDirty)
stateObject.SetNonce(StartingNonce)
s.SetOwnedStateObject(address, stateObject)
//s.stateObjects[address] = stateObject
//s.localStateObjects[address] = true
}
return stateObject return stateObject
} }
func (s *StateDB) GetStateObject(address common.Address) *StateObject {
account, _ := s.Read(address)
if account != nil {
if s.stateObjects[address] == nil {
s.SetStateObject(account)
//s.stateObjects[address] = account
}
return account
}
return nil
}
// NewStateObject create a state object whether it exist in the trie or not // NewStateObject create a state object whether it exist in the trie or not
func (self *StateDB) newStateObject(addr common.Address) *StateObject { func (self *StateDB) newStateObject(addr common.Address) *StateObject {
if glog.V(logger.Core) { if glog.V(logger.Core) {
@ -383,7 +446,8 @@ func (self *StateDB) newStateObject(addr common.Address) *StateObject {
} }
obj := NewObject(addr, Account{}, self.MarkStateObjectDirty) obj := NewObject(addr, Account{}, self.MarkStateObjectDirty)
obj.SetNonce(StartingNonce) // sets the object to dirty obj.SetNonce(StartingNonce) // sets the object to dirty
self.stateObjects[addr] = obj //self.stateObjects[addr] = obj
self.SetOwnedStateObject(addr, obj)
return obj return obj
} }
@ -412,42 +476,12 @@ func (self *StateDB) CreateAccount(addr common.Address) vm.Account {
return self.CreateStateObject(addr) return self.CreateStateObject(addr)
} }
//
// Setting, copying of the state methods
//
func (self *StateDB) Copy() *StateDB {
self.lock.Lock()
defer self.lock.Unlock()
// Copy all the basic fields, initialize the memory ones
state := &StateDB{
db: self.db,
trie: self.trie,
pastTries: self.pastTries,
codeSizeCache: self.codeSizeCache,
stateObjects: make(map[common.Address]*StateObject, len(self.stateObjectsDirty)),
stateObjectsDirty: make(map[common.Address]struct{}, len(self.stateObjectsDirty)),
refund: new(big.Int).Set(self.refund),
logs: make(map[common.Hash]vm.Logs, len(self.logs)),
logSize: self.logSize,
}
// Copy the dirty states and logs
for addr, _ := range self.stateObjectsDirty {
state.stateObjects[addr] = self.stateObjects[addr].Copy(self.db, state.MarkStateObjectDirty)
state.stateObjectsDirty[addr] = struct{}{}
}
for hash, logs := range self.logs {
state.logs[hash] = make(vm.Logs, len(logs))
copy(state.logs[hash], logs)
}
return state
}
func (self *StateDB) Set(state *StateDB) { func (self *StateDB) Set(state *StateDB) {
self.lock.Lock() self.lock.Lock()
defer self.lock.Unlock() defer self.lock.Unlock()
*self = *state
/*
self.db = state.db self.db = state.db
self.trie = state.trie self.trie = state.trie
self.pastTries = state.pastTries self.pastTries = state.pastTries
@ -456,7 +490,7 @@ func (self *StateDB) Set(state *StateDB) {
self.codeSizeCache = state.codeSizeCache self.codeSizeCache = state.codeSizeCache
self.refund = state.refund self.refund = state.refund
self.logs = state.logs self.logs = state.logs
self.logSize = state.logSize */
} }
func (self *StateDB) GetRefund() *big.Int { func (self *StateDB) GetRefund() *big.Int {
@ -501,7 +535,8 @@ func (s *StateDB) DeleteSuicides() {
} }
} }
// Commit commits all state changes to the database. /*
// commit commits all state changes to the database.
func (s *StateDB) Commit() (root common.Hash, err error) { func (s *StateDB) Commit() (root common.Hash, err error) {
root, batch := s.CommitBatch() root, batch := s.CommitBatch()
return root, batch.Write() return root, batch.Write()
@ -549,7 +584,116 @@ func (s *StateDB) commit(dbw trie.DatabaseWriter) (root common.Hash, err error)
} }
return root, err return root, err
} }
*/
func (self *StateDB) Refunds() *big.Int { func (self *StateDB) Refunds() *big.Int {
return self.refund return self.refund
} }
// Fork preserve the given state and returns a handle to a new modifiable state
// that does not affect the preserved state.
func Fork(parent *StateDB) *StateDB {
return &StateDB{
db: parent.db,
trie: parent.trie,
pastTries: parent.pastTries,
codeSizeCache: parent.codeSizeCache,
parent: parent,
stateObjects: make(map[common.Address]*StateObject),
localStateObjects: make(map[common.Address]bool),
stateObjectsDirty: make(map[common.Address]struct{}),
refund: new(big.Int),
logIdx: parent.logIdx,
}
}
// Reduce flattens the state in to a single new state, including all changes of all ancestors.
func Reduce(s *StateDB) *StateDB {
if s.parent == nil {
return s
}
state := Reduce(s.parent)
for address, object := range s.stateObjects {
if s.localStateObjects[address] {
state.SetOwnedStateObject(address, object)
} else {
state.SetStateObject(object)
}
}
state.logs = append(state.logs, s.logs...)
state.refund.Add(state.refund, s.refund)
return state
}
func IntermediateRoot(state *StateDB) common.Hash {
for address, _ := range state.localStateObjects {
stateObject := state.stateObjects[address]
if _, isdirty := state.stateObjectsDirty[address]; isdirty {
if stateObject.remove {
state.DeleteStateObject(stateObject)
} else {
stateObject.UpdateRoot(state.db)
state.UpdateStateObject(stateObject)
}
}
}
state.stateObjectsDirty = make(map[common.Address]struct{})
return state.trie.Hash()
}
// Commit commits all state changes to the database.
func Commit(state *StateDB) (common.Hash, error) {
root, batch := CommitBatch(state)
return root, batch.Write()
}
// CommitBatch commits all state changes to a write batch but does not
// execute the batch. It is used to validate state changes against
// the root hash stored in a block.
func CommitBatch(state *StateDB) (common.Hash, ethdb.Batch) {
batch := state.db.NewBatch()
root, _ := stateCommit(state, batch)
return root, batch
}
func stateCommit(state *StateDB, dbw trie.DatabaseWriter) (root common.Hash, err error) {
// make sure the state is flattened before committing
state = Reduce(state)
state.refund = new(big.Int)
for address, _ := range state.localStateObjects {
stateObject := state.stateObjects[address]
//for _, stateObject := range state.stateObjects {
if stateObject.remove {
// If the object has been removed, don't bother syncing it
// and just mark it for deletion in the trie.
stateObject.deleted = true
state.trie.Delete(stateObject.Address().Bytes()[:])
} else {
// Write any contract code associated with the state object
if stateObject.code != nil && stateObject.dirtyCode {
if err := dbw.Put(stateObject.CodeHash(), stateObject.code); err != nil {
return common.Hash{}, err
}
stateObject.dirtyCode = false
}
// Write any storage changes in the state object to its storage trie.
if err := stateObject.CommitTrie(state.db, dbw); err != nil {
return common.Hash{}, err
}
// Update the object in the main account trie.
state.UpdateStateObject(stateObject)
}
}
state.stateObjectsDirty = make(map[common.Address]struct{})
// Write trie changes.
root, err = state.trie.CommitTo(dbw)
if err == nil {
state.pushTrie(state.trie)
}
return root, err
}

View file

@ -64,6 +64,7 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg
header = block.Header() header = block.Header()
allLogs vm.Logs allLogs vm.Logs
gp = new(GasPool).AddGas(block.GasLimit()) gp = new(GasPool).AddGas(block.GasLimit())
forkState = state.Fork(statedb)
) )
// Mutate the the block and state according to any hard-fork specs // Mutate the the block and state according to any hard-fork specs
if p.config.DAOForkSupport && p.config.DAOForkBlock != nil && p.config.DAOForkBlock.Cmp(block.Number()) == 0 { if p.config.DAOForkSupport && p.config.DAOForkBlock != nil && p.config.DAOForkBlock.Cmp(block.Number()) == 0 {
@ -71,16 +72,20 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg
} }
// Iterate over and process the individual transactions // Iterate over and process the individual transactions
for i, tx := range block.Transactions() { for i, tx := range block.Transactions() {
statedb.StartRecord(tx.Hash(), block.Hash(), i) forkState.TransitionState(tx.Hash(), block.Hash(), i)
receipt, logs, _, err := ApplyTransaction(p.config, p.bc, gp, statedb, header, tx, totalUsedGas, cfg) txPostState, receipt, logs, _, err := ApplyTransaction(p.config, p.bc, gp, forkState, header, tx, totalUsedGas, cfg)
if err != nil { if err != nil {
return nil, nil, totalUsedGas, err return nil, nil, totalUsedGas, err
} }
receipts = append(receipts, receipt) receipts = append(receipts, receipt)
allLogs = append(allLogs, logs...) allLogs = append(allLogs, logs...)
forkState = state.Fork(txPostState)
} }
AccumulateRewards(statedb, header, block.Uncles()) AccumulateRewards(statedb, header, block.Uncles())
statedb.Set(state.Reduce(forkState))
return receipts, allLogs, totalUsedGas, err return receipts, allLogs, totalUsedGas, err
} }
@ -89,15 +94,18 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg
// //
// ApplyTransactions returns the generated receipts and vm logs during the // ApplyTransactions returns the generated receipts and vm logs during the
// execution of the state transition phase. // execution of the state transition phase.
func ApplyTransaction(config *ChainConfig, bc *BlockChain, gp *GasPool, statedb *state.StateDB, header *types.Header, tx *types.Transaction, usedGas *big.Int, cfg vm.Config) (*types.Receipt, vm.Logs, *big.Int, error) { func ApplyTransaction(config *ChainConfig, bc *BlockChain, gp *GasPool, statedb *state.StateDB, header *types.Header, tx *types.Transaction, usedGas *big.Int, cfg vm.Config) (*state.StateDB, *types.Receipt, vm.Logs, *big.Int, error) {
_, gas, err := ApplyMessage(NewEnv(statedb, config, bc, tx, header, cfg), tx, gp) env := NewEnv(statedb, config, bc, tx, header, cfg)
_, gas, err := ApplyMessage(env, tx, gp)
if err != nil { if err != nil {
return nil, nil, nil, err return statedb, nil, nil, nil, err
} }
statedb = env.Db().(*state.StateDB)
// Update the state with pending changes // Update the state with pending changes
usedGas.Add(usedGas, gas) usedGas.Add(usedGas, gas)
receipt := types.NewReceipt(statedb.IntermediateRoot().Bytes(), usedGas) receipt := types.NewReceipt(state.IntermediateRoot(statedb).Bytes(), usedGas)
receipt.TxHash = tx.Hash() receipt.TxHash = tx.Hash()
receipt.GasUsed = new(big.Int).Set(gas) receipt.GasUsed = new(big.Int).Set(gas)
if MessageCreatesContract(tx) { if MessageCreatesContract(tx) {
@ -105,13 +113,12 @@ func ApplyTransaction(config *ChainConfig, bc *BlockChain, gp *GasPool, statedb
receipt.ContractAddress = crypto.CreateAddress(from, tx.Nonce()) receipt.ContractAddress = crypto.CreateAddress(from, tx.Nonce())
} }
logs := statedb.GetLogs(tx.Hash()) receipt.Logs = statedb.Logs()
receipt.Logs = logs
receipt.Bloom = types.CreateBloom(types.Receipts{receipt}) receipt.Bloom = types.CreateBloom(types.Receipts{receipt})
glog.V(logger.Debug).Infoln(receipt) glog.V(logger.Debug).Infoln(receipt)
return receipt, logs, gas, err return statedb, receipt, receipt.Logs, gas, err
} }
// AccumulateRewards credits the coinbase of the given block with the // AccumulateRewards credits the coinbase of the given block with the

View file

@ -21,6 +21,7 @@ import (
"math/big" "math/big"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/logger" "github.com/ethereum/go-ethereum/logger"
"github.com/ethereum/go-ethereum/logger/glog" "github.com/ethereum/go-ethereum/logger/glog"
@ -273,24 +274,24 @@ func (self *StateTransition) TransitionDb() (ret []byte, requiredGas, usedGas *b
requiredGas = new(big.Int).Set(self.gasUsed()) requiredGas = new(big.Int).Set(self.gasUsed())
self.refundGas() self.refundGas(vmenv.Db().(*state.StateDB))
self.state.AddBalance(self.env.Coinbase(), new(big.Int).Mul(self.gasUsed(), self.gasPrice)) vmenv.Db().AddBalance(self.env.Coinbase(), new(big.Int).Mul(self.gasUsed(), self.gasPrice))
return ret, requiredGas, self.gasUsed(), err return ret, requiredGas, self.gasUsed(), err
} }
func (self *StateTransition) refundGas() { func (self *StateTransition) refundGas(state *state.StateDB) {
// Return eth for remaining gas to the sender account, // Return eth for remaining gas to the sender account,
// exchanged at the original rate. // exchanged at the original rate.
sender, _ := self.from() // err already checked sender, _ := self.from() // err already checked
remaining := new(big.Int).Mul(self.gas, self.gasPrice) remaining := new(big.Int).Mul(self.gas, self.gasPrice)
sender.AddBalance(remaining) state.AddBalance(sender.Address(), remaining)
// Apply refund counter, capped to half of the used gas. // Apply refund counter, capped to half of the used gas.
uhalf := remaining.Div(self.gasUsed(), common.Big2) uhalf := remaining.Div(self.gasUsed(), common.Big2)
refund := common.BigMin(uhalf, self.state.GetRefund()) refund := common.BigMin(uhalf, state.GetRefund())
self.gas.Add(self.gas, refund) self.gas.Add(self.gas, refund)
self.state.AddBalance(sender.Address(), refund.Mul(refund, self.gasPrice)) state.AddBalance(sender.Address(), refund.Mul(refund, self.gasPrice))
// Also return remaining gas to the block gas counter so it is // Also return remaining gas to the block gas counter so it is
// available for the next transaction. // available for the next transaction.

View file

@ -90,15 +90,17 @@ func (self *VMEnv) CanTransfer(from common.Address, balance *big.Int) bool {
} }
func (self *VMEnv) MakeSnapshot() vm.Database { func (self *VMEnv) MakeSnapshot() vm.Database {
return self.state.Copy() pstate := self.state
self.state = state.Fork(pstate)
return pstate
} }
func (self *VMEnv) SetSnapshot(copy vm.Database) { func (self *VMEnv) SetSnapshot(copy vm.Database) {
self.state.Set(copy.(*state.StateDB)) self.state = copy.(*state.StateDB)
} }
func (self *VMEnv) Transfer(from, to vm.Account, amount *big.Int) { func (self *VMEnv) Transfer(from, to vm.Account, amount *big.Int) {
Transfer(from, to, amount) Transfer(self.state, from.Address(), to.Address(), amount)
} }
func (self *VMEnv) Call(me vm.ContractRef, addr common.Address, data []byte, gas, price, value *big.Int) ([]byte, error) { func (self *VMEnv) Call(me vm.ContractRef, addr common.Address, data []byte, gas, price, value *big.Int) ([]byte, error) {

View file

@ -97,8 +97,8 @@ func (b *EthApiBackend) GetTd(blockHash common.Hash) *big.Int {
return b.eth.blockchain.GetTdByHash(blockHash) return b.eth.blockchain.GetTdByHash(blockHash)
} }
func (b *EthApiBackend) GetVMEnv(ctx context.Context, msg core.Message, state ethapi.State, header *types.Header) (vm.Environment, func() error, error) { func (b *EthApiBackend) GetVMEnv(ctx context.Context, msg core.Message, st ethapi.State, header *types.Header) (vm.Environment, func() error, error) {
stateDb := state.(EthApiState).state.Copy() stateDb := state.Fork(st.(EthApiState).state)
addr, _ := msg.From() addr, _ := msg.From()
from := stateDb.GetOrNewStateObject(addr) from := stateDb.GetOrNewStateObject(addr)
from.SetBalance(common.MaxBig) from.SetBalance(common.MaxBig)

View file

@ -583,7 +583,7 @@ func (env *Work) commitTransactions(mux *event.TypeMux, txs *types.TransactionsB
continue continue
} }
// Start executing the transaction // Start executing the transaction
env.state.StartRecord(tx.Hash(), common.Hash{}, env.tcount) env.state.TransitionState(tx.Hash(), common.Hash{}, env.tcount)
err, logs := env.commitTransaction(tx, bc, gp) err, logs := env.commitTransaction(tx, bc, gp)
switch { switch {
@ -618,8 +618,6 @@ func (env *Work) commitTransactions(mux *event.TypeMux, txs *types.TransactionsB
} }
func (env *Work) commitTransaction(tx *types.Transaction, bc *core.BlockChain, gp *core.GasPool) (error, vm.Logs) { func (env *Work) commitTransaction(tx *types.Transaction, bc *core.BlockChain, gp *core.GasPool) (error, vm.Logs) {
snap := env.state.Copy()
// this is a bit of a hack to force jit for the miners // this is a bit of a hack to force jit for the miners
config := env.config.VmConfig config := env.config.VmConfig
if !(config.EnableJit && config.ForceJit) { if !(config.EnableJit && config.ForceJit) {
@ -627,9 +625,10 @@ func (env *Work) commitTransaction(tx *types.Transaction, bc *core.BlockChain, g
} }
config.ForceJit = false // disable forcing jit config.ForceJit = false // disable forcing jit
receipt, logs, _, err := core.ApplyTransaction(env.config, bc, gp, env.state, env.header, tx, env.header.GasUsed, config) state, receipt, logs, _, err := core.ApplyTransaction(env.config, bc, gp, env.state, env.header, tx, env.header.GasUsed, config)
defer func() { env.state = state }()
if err != nil { if err != nil {
env.state.Set(snap)
return err, nil return err, nil
} }
env.txs = append(env.txs, tx) env.txs = append(env.txs, tx)

View file

@ -228,7 +228,8 @@ func (t *BlockTest) InsertPreState(db ethdb.Database) (*state.StateDB, error) {
} }
} }
root, err := statedb.Commit() //root, err := statedb.Commit()
root, err := state.Commit(statedb)
if err != nil { if err != nil {
return nil, fmt.Errorf("error writing state: %v", err) return nil, fmt.Errorf("error writing state: %v", err)
} }

View file

@ -98,7 +98,7 @@ func benchStateTest(ruleSet RuleSet, test VmTest, env map[string]string, b *test
statedb, _ := state.New(common.Hash{}, db) statedb, _ := state.New(common.Hash{}, db)
for addr, account := range test.Pre { for addr, account := range test.Pre {
obj := StateObjectFromAccount(db, addr, account, statedb.MarkStateObjectDirty) obj := StateObjectFromAccount(db, addr, account, statedb.MarkStateObjectDirty)
statedb.SetStateObject(obj) statedb.SetOwnedStateObject(common.HexToAddress(addr), obj)
for a, v := range account.Storage { for a, v := range account.Storage {
obj.SetState(common.HexToHash(a), common.HexToHash(v)) obj.SetState(common.HexToHash(a), common.HexToHash(v))
} }
@ -109,13 +109,16 @@ func benchStateTest(ruleSet RuleSet, test VmTest, env map[string]string, b *test
} }
func runStateTests(ruleSet RuleSet, tests map[string]VmTest, skipTests []string) error { func runStateTests(ruleSet RuleSet, tests map[string]VmTest, skipTests []string) error {
//glog.SetToStderr(true)
//glog.SetV(6)
skipTest := make(map[string]bool, len(skipTests)) skipTest := make(map[string]bool, len(skipTests))
for _, name := range skipTests { for _, name := range skipTests {
skipTest[name] = true skipTest[name] = true
} }
for name, test := range tests { for name, test := range tests {
if skipTest[name] /*|| name != "callcodecallcode_11" */ { if skipTest[name] /*|| name != "log1_logMemsizeTooHigh"*/ {
glog.Infoln("Skipping state test", name) glog.Infoln("Skipping state test", name)
continue continue
} }
@ -137,7 +140,7 @@ func runStateTest(ruleSet RuleSet, test VmTest) error {
statedb, _ := state.New(common.Hash{}, db) statedb, _ := state.New(common.Hash{}, db)
for addr, account := range test.Pre { for addr, account := range test.Pre {
obj := StateObjectFromAccount(db, addr, account, statedb.MarkStateObjectDirty) obj := StateObjectFromAccount(db, addr, account, statedb.MarkStateObjectDirty)
statedb.SetStateObject(obj) statedb.SetOwnedStateObject(common.HexToAddress(addr), obj)
for a, v := range account.Storage { for a, v := range account.Storage {
obj.SetState(common.HexToHash(a), common.HexToHash(v)) obj.SetState(common.HexToHash(a), common.HexToHash(v))
} }
@ -196,7 +199,7 @@ func runStateTest(ruleSet RuleSet, test VmTest) error {
} }
} }
root, _ := statedb.Commit() root, _ := state.Commit(statedb)
if common.HexToHash(test.PostStateRoot) != root { if common.HexToHash(test.PostStateRoot) != root {
return fmt.Errorf("Post state root error. Expected: %s have: %x", test.PostStateRoot, root) return fmt.Errorf("Post state root error. Expected: %s have: %x", test.PostStateRoot, root)
} }
@ -227,19 +230,22 @@ func RunState(ruleSet RuleSet, statedb *state.StateDB, env, tx map[string]string
} }
// Set pre compiled contracts // Set pre compiled contracts
vm.Precompiled = vm.PrecompiledContracts() vm.Precompiled = vm.PrecompiledContracts()
snapshot := statedb.Copy() snapshot := statedb
nstatedb := state.Fork(snapshot)
gaspool := new(core.GasPool).AddGas(common.Big(env["currentGasLimit"])) gaspool := new(core.GasPool).AddGas(common.Big(env["currentGasLimit"]))
key, _ := hex.DecodeString(tx["secretKey"]) key, _ := hex.DecodeString(tx["secretKey"])
addr := crypto.PubkeyToAddress(crypto.ToECDSA(key).PublicKey) addr := crypto.PubkeyToAddress(crypto.ToECDSA(key).PublicKey)
message := NewMessage(addr, to, data, value, gas, price, nonce) message := NewMessage(addr, to, data, value, gas, price, nonce)
vmenv := NewEnvFromMap(ruleSet, statedb, env, tx) vmenv := NewEnvFromMap(ruleSet, nstatedb, env, tx)
vmenv.origin = addr vmenv.origin = addr
ret, _, err := core.ApplyMessage(vmenv, message, gaspool) ret, _, err := core.ApplyMessage(vmenv, message, gaspool)
if core.IsNonceErr(err) || core.IsInvalidTxErr(err) || core.IsGasLimitErr(err) { if core.IsNonceErr(err) || core.IsInvalidTxErr(err) || core.IsGasLimitErr(err) {
statedb.Set(snapshot) vmenv.SetSnapshot(snapshot)
} }
statedb.Commit() statedb.Set(state.Reduce(vmenv.Db().(*state.StateDB)))
state.Commit(statedb)
return ret, vmenv.state.Logs(), vmenv.Gas, err return ret, statedb.Logs(), vmenv.Gas, err
} }

View file

@ -229,18 +229,22 @@ func (self *Env) CanTransfer(from common.Address, balance *big.Int) bool {
return self.state.GetBalance(from).Cmp(balance) >= 0 return self.state.GetBalance(from).Cmp(balance) >= 0
} }
func (self *Env) MakeSnapshot() vm.Database { func (self *Env) MakeSnapshot() vm.Database {
return self.state.Copy() pstate := self.state
self.state = state.Fork(pstate)
return pstate
} }
func (self *Env) SetSnapshot(copy vm.Database) { func (self *Env) SetSnapshot(copy vm.Database) {
self.state.Set(copy.(*state.StateDB)) self.state = copy.(*state.StateDB)
} }
func (self *Env) Transfer(from, to vm.Account, amount *big.Int) { func (self *Env) Transfer(from, to vm.Account, amount *big.Int) {
if self.skipTransfer { if self.skipTransfer {
return return
} }
core.Transfer(from, to, amount) core.Transfer(self.state, from.Address(), to.Address(), amount)
} }
func (self *Env) Call(caller vm.ContractRef, addr common.Address, data []byte, gas, price, value *big.Int) ([]byte, error) { func (self *Env) Call(caller vm.ContractRef, addr common.Address, data []byte, gas, price, value *big.Int) ([]byte, error) {