core, core/state: batch-based state sync
This commit is contained in:
parent
565d9f2306
commit
a2d5a60418
@ -100,10 +100,8 @@ func (self *BlockProcessor) ApplyTransaction(gp GasPool, statedb *state.StateDB,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Update the state with pending changes
|
// Update the state with pending changes
|
||||||
statedb.SyncIntermediate()
|
|
||||||
|
|
||||||
usedGas.Add(usedGas, gas)
|
usedGas.Add(usedGas, gas)
|
||||||
receipt := types.NewReceipt(statedb.Root().Bytes(), usedGas)
|
receipt := types.NewReceipt(statedb.IntermediateRoot().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) {
|
||||||
@ -265,16 +263,16 @@ func (sm *BlockProcessor) processWithParent(block, parent *types.Block) (logs st
|
|||||||
// Accumulate static rewards; block reward, uncle's and uncle inclusion.
|
// Accumulate static rewards; block reward, uncle's and uncle inclusion.
|
||||||
AccumulateRewards(state, header, uncles)
|
AccumulateRewards(state, header, uncles)
|
||||||
|
|
||||||
// Commit state objects/accounts to a temporary trie (does not save)
|
// Commit state objects/accounts to a database batch and calculate
|
||||||
// used to calculate the state root.
|
// the state root. The database is not modified if the root
|
||||||
state.SyncObjects()
|
// doesn't match.
|
||||||
if header.Root != state.Root() {
|
root, batch := state.CommitBatch()
|
||||||
err = fmt.Errorf("invalid merkle root. received=%x got=%x", header.Root, state.Root())
|
if header.Root != root {
|
||||||
return
|
return nil, nil, fmt.Errorf("invalid merkle root: header=%x computed=%x", header.Root, root)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sync the current block's state to the database
|
// Execute the database writes.
|
||||||
state.Sync()
|
batch.Write()
|
||||||
|
|
||||||
return state.Logs(), receipts, nil
|
return state.Logs(), receipts, nil
|
||||||
}
|
}
|
||||||
|
@ -17,6 +17,7 @@
|
|||||||
package core
|
package core
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"math/big"
|
"math/big"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
@ -94,9 +95,9 @@ func (b *BlockGen) AddTx(tx *types.Transaction) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
b.statedb.SyncIntermediate()
|
root := b.statedb.IntermediateRoot()
|
||||||
b.header.GasUsed.Add(b.header.GasUsed, gas)
|
b.header.GasUsed.Add(b.header.GasUsed, gas)
|
||||||
receipt := types.NewReceipt(b.statedb.Root().Bytes(), b.header.GasUsed)
|
receipt := types.NewReceipt(root.Bytes(), b.header.GasUsed)
|
||||||
logs := b.statedb.GetLogs(tx.Hash())
|
logs := b.statedb.GetLogs(tx.Hash())
|
||||||
receipt.SetLogs(logs)
|
receipt.SetLogs(logs)
|
||||||
receipt.Bloom = types.CreateBloom(types.Receipts{receipt})
|
receipt.Bloom = types.CreateBloom(types.Receipts{receipt})
|
||||||
@ -163,8 +164,11 @@ func GenerateChain(parent *types.Block, db ethdb.Database, n int, gen func(int,
|
|||||||
gen(i, b)
|
gen(i, b)
|
||||||
}
|
}
|
||||||
AccumulateRewards(statedb, h, b.uncles)
|
AccumulateRewards(statedb, h, b.uncles)
|
||||||
statedb.SyncIntermediate()
|
root, err := statedb.Commit()
|
||||||
h.Root = statedb.Root()
|
if err != nil {
|
||||||
|
panic(fmt.Sprintf("state write error: %v", err))
|
||||||
|
}
|
||||||
|
h.Root = root
|
||||||
return types.NewBlock(h, b.txs, b.uncles, b.receipts)
|
return types.NewBlock(h, b.txs, b.uncles, b.receipts)
|
||||||
}
|
}
|
||||||
for i := 0; i < n; i++ {
|
for i := 0; i < n; i++ {
|
||||||
@ -184,7 +188,7 @@ func makeHeader(parent *types.Block, state *state.StateDB) *types.Header {
|
|||||||
time = new(big.Int).Add(parent.Time(), big.NewInt(10)) // block time is fixed at 10 seconds
|
time = new(big.Int).Add(parent.Time(), big.NewInt(10)) // block time is fixed at 10 seconds
|
||||||
}
|
}
|
||||||
return &types.Header{
|
return &types.Header{
|
||||||
Root: state.Root(),
|
Root: state.IntermediateRoot(),
|
||||||
ParentHash: parent.Hash(),
|
ParentHash: parent.Hash(),
|
||||||
Coinbase: parent.Coinbase(),
|
Coinbase: parent.Coinbase(),
|
||||||
Difficulty: CalcDifficulty(time.Uint64(), new(big.Int).Sub(time, big.NewInt(10)).Uint64(), parent.Number(), parent.Difficulty()),
|
Difficulty: CalcDifficulty(time.Uint64(), new(big.Int).Sub(time, big.NewInt(10)).Uint64(), parent.Number(), parent.Difficulty()),
|
||||||
|
@ -839,8 +839,8 @@ out:
|
|||||||
}
|
}
|
||||||
|
|
||||||
func blockErr(block *types.Block, err error) {
|
func blockErr(block *types.Block, err error) {
|
||||||
h := block.Header()
|
if glog.V(logger.Error) {
|
||||||
glog.V(logger.Error).Infof("Bad block #%v (%x)\n", h.Number, h.Hash().Bytes())
|
glog.Errorf("Bad block #%v (%s)\n", block.Number(), block.Hash().Hex())
|
||||||
glog.V(logger.Error).Infoln(err)
|
glog.Errorf(" %v", err)
|
||||||
glog.V(logger.Debug).Infoln(verifyNonces)
|
}
|
||||||
}
|
}
|
||||||
|
@ -69,7 +69,7 @@ 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))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
statedb.SyncObjects()
|
root, stateBatch := statedb.CommitBatch()
|
||||||
|
|
||||||
difficulty := common.String2Big(genesis.Difficulty)
|
difficulty := common.String2Big(genesis.Difficulty)
|
||||||
block := types.NewBlock(&types.Header{
|
block := types.NewBlock(&types.Header{
|
||||||
@ -81,7 +81,7 @@ func WriteGenesisBlock(chainDb ethdb.Database, reader io.Reader) (*types.Block,
|
|||||||
Difficulty: difficulty,
|
Difficulty: difficulty,
|
||||||
MixDigest: common.HexToHash(genesis.Mixhash),
|
MixDigest: common.HexToHash(genesis.Mixhash),
|
||||||
Coinbase: common.HexToAddress(genesis.Coinbase),
|
Coinbase: common.HexToAddress(genesis.Coinbase),
|
||||||
Root: statedb.Root(),
|
Root: root,
|
||||||
}, nil, nil, nil)
|
}, nil, nil, nil)
|
||||||
|
|
||||||
if block := GetBlock(chainDb, block.Hash()); block != nil {
|
if block := GetBlock(chainDb, block.Hash()); block != nil {
|
||||||
@ -92,8 +92,10 @@ func WriteGenesisBlock(chainDb ethdb.Database, reader io.Reader) (*types.Block,
|
|||||||
}
|
}
|
||||||
return block, nil
|
return block, nil
|
||||||
}
|
}
|
||||||
statedb.Sync()
|
|
||||||
|
|
||||||
|
if err := stateBatch.Write(); err != nil {
|
||||||
|
return nil, fmt.Errorf("cannot write state: %v", err)
|
||||||
|
}
|
||||||
if err := WriteTd(chainDb, block.Hash(), difficulty); err != nil {
|
if err := WriteTd(chainDb, block.Hash(), difficulty); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -115,12 +117,14 @@ 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)
|
||||||
statedb.SyncObjects()
|
root, err := statedb.Commit()
|
||||||
statedb.Sync()
|
if err != nil {
|
||||||
|
panic(fmt.Sprintf("cannot write state: %v", err))
|
||||||
|
}
|
||||||
block := types.NewBlock(&types.Header{
|
block := types.NewBlock(&types.Header{
|
||||||
Difficulty: params.GenesisDifficulty,
|
Difficulty: params.GenesisDifficulty,
|
||||||
GasLimit: params.GenesisGasLimit,
|
GasLimit: params.GenesisGasLimit,
|
||||||
Root: statedb.Root(),
|
Root: root,
|
||||||
}, nil, nil, nil)
|
}, nil, nil, nil)
|
||||||
return block
|
return block
|
||||||
}
|
}
|
||||||
|
@ -218,6 +218,7 @@ func (c *StateObject) ReturnGas(gas, price *big.Int) {}
|
|||||||
|
|
||||||
func (self *StateObject) SetGasLimit(gasLimit *big.Int) {
|
func (self *StateObject) SetGasLimit(gasLimit *big.Int) {
|
||||||
self.gasPool = new(big.Int).Set(gasLimit)
|
self.gasPool = new(big.Int).Set(gasLimit)
|
||||||
|
self.dirty = true
|
||||||
|
|
||||||
if glog.V(logger.Core) {
|
if glog.V(logger.Core) {
|
||||||
glog.Infof("%x: gas (+ %v)", self.Address(), self.gasPool)
|
glog.Infof("%x: gas (+ %v)", self.Address(), self.gasPool)
|
||||||
@ -228,19 +229,14 @@ func (self *StateObject) SubGas(gas, price *big.Int) error {
|
|||||||
if self.gasPool.Cmp(gas) < 0 {
|
if self.gasPool.Cmp(gas) < 0 {
|
||||||
return GasLimitError(self.gasPool, gas)
|
return GasLimitError(self.gasPool, gas)
|
||||||
}
|
}
|
||||||
|
|
||||||
self.gasPool.Sub(self.gasPool, gas)
|
self.gasPool.Sub(self.gasPool, gas)
|
||||||
|
|
||||||
rGas := new(big.Int).Set(gas)
|
|
||||||
rGas.Mul(rGas, price)
|
|
||||||
|
|
||||||
self.dirty = true
|
self.dirty = true
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *StateObject) AddGas(gas, price *big.Int) {
|
func (self *StateObject) AddGas(gas, price *big.Int) {
|
||||||
self.gasPool.Add(self.gasPool, gas)
|
self.gasPool.Add(self.gasPool, gas)
|
||||||
|
self.dirty = true
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *StateObject) Copy() *StateObject {
|
func (self *StateObject) Copy() *StateObject {
|
||||||
|
@ -89,8 +89,7 @@ func TestNull(t *testing.T) {
|
|||||||
//value := common.FromHex("0x823140710bf13990e4500136726d8b55")
|
//value := common.FromHex("0x823140710bf13990e4500136726d8b55")
|
||||||
var value common.Hash
|
var value common.Hash
|
||||||
state.SetState(address, common.Hash{}, value)
|
state.SetState(address, common.Hash{}, value)
|
||||||
state.SyncIntermediate()
|
state.Commit()
|
||||||
state.Sync()
|
|
||||||
value = state.GetState(address, common.Hash{})
|
value = state.GetState(address, common.Hash{})
|
||||||
if !common.EmptyHash(value) {
|
if !common.EmptyHash(value) {
|
||||||
t.Errorf("expected empty hash. got %x", value)
|
t.Errorf("expected empty hash. got %x", value)
|
||||||
|
@ -35,7 +35,6 @@ import (
|
|||||||
type StateDB struct {
|
type StateDB struct {
|
||||||
db ethdb.Database
|
db ethdb.Database
|
||||||
trie *trie.SecureTrie
|
trie *trie.SecureTrie
|
||||||
root common.Hash
|
|
||||||
|
|
||||||
stateObjects map[string]*StateObject
|
stateObjects map[string]*StateObject
|
||||||
|
|
||||||
@ -56,7 +55,6 @@ func New(root common.Hash, db ethdb.Database) *StateDB {
|
|||||||
glog.Errorf("can't create state trie with root %x: %v", root[:], err)
|
glog.Errorf("can't create state trie with root %x: %v", root[:], err)
|
||||||
}
|
}
|
||||||
return &StateDB{
|
return &StateDB{
|
||||||
root: root,
|
|
||||||
db: db,
|
db: db,
|
||||||
trie: tr,
|
trie: tr,
|
||||||
stateObjects: make(map[string]*StateObject),
|
stateObjects: make(map[string]*StateObject),
|
||||||
@ -204,7 +202,6 @@ func (self *StateDB) UpdateStateObject(stateObject *StateObject) {
|
|||||||
if len(stateObject.CodeHash()) > 0 {
|
if len(stateObject.CodeHash()) > 0 {
|
||||||
self.db.Put(stateObject.CodeHash(), stateObject.code)
|
self.db.Put(stateObject.CodeHash(), stateObject.code)
|
||||||
}
|
}
|
||||||
|
|
||||||
addr := stateObject.Address()
|
addr := stateObject.Address()
|
||||||
self.trie.Update(addr[:], stateObject.RlpEncode())
|
self.trie.Update(addr[:], stateObject.RlpEncode())
|
||||||
}
|
}
|
||||||
@ -215,6 +212,7 @@ func (self *StateDB) DeleteStateObject(stateObject *StateObject) {
|
|||||||
|
|
||||||
addr := stateObject.Address()
|
addr := stateObject.Address()
|
||||||
self.trie.Delete(addr[:])
|
self.trie.Delete(addr[:])
|
||||||
|
//delete(self.stateObjects, addr.Str())
|
||||||
}
|
}
|
||||||
|
|
||||||
// Retrieve a state object given my the address. Nil if not found
|
// Retrieve a state object given my the address. Nil if not found
|
||||||
@ -311,67 +309,69 @@ func (self *StateDB) Set(state *StateDB) {
|
|||||||
self.logSize = state.logSize
|
self.logSize = state.logSize
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *StateDB) Root() common.Hash {
|
// IntermediateRoot computes the current root hash of the state trie.
|
||||||
|
// It is called in between transactions to get the root hash that
|
||||||
|
// goes into transaction receipts.
|
||||||
|
func (s *StateDB) IntermediateRoot() common.Hash {
|
||||||
|
s.refund = new(big.Int)
|
||||||
|
for _, stateObject := range s.stateObjects {
|
||||||
|
if stateObject.dirty {
|
||||||
|
if stateObject.remove {
|
||||||
|
s.DeleteStateObject(stateObject)
|
||||||
|
} else {
|
||||||
|
stateObject.Update()
|
||||||
|
s.UpdateStateObject(stateObject)
|
||||||
|
}
|
||||||
|
stateObject.dirty = false
|
||||||
|
}
|
||||||
|
}
|
||||||
return s.trie.Hash()
|
return s.trie.Hash()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Syncs the trie and all siblings
|
// Commit commits all state changes to the database.
|
||||||
func (s *StateDB) Sync() {
|
func (s *StateDB) Commit() (root common.Hash, err error) {
|
||||||
// Sync all nested states
|
return s.commit(s.db)
|
||||||
for _, stateObject := range s.stateObjects {
|
|
||||||
stateObject.trie.Commit()
|
|
||||||
}
|
|
||||||
|
|
||||||
s.trie.Commit()
|
|
||||||
|
|
||||||
s.Empty()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *StateDB) Empty() {
|
// CommitBatch commits all state changes to a write batch but does not
|
||||||
self.stateObjects = make(map[string]*StateObject)
|
// execute the batch. It is used to validate state changes against
|
||||||
self.refund = new(big.Int)
|
// the root hash stored in a block.
|
||||||
|
func (s *StateDB) CommitBatch() (root common.Hash, batch ethdb.Batch) {
|
||||||
|
batch = s.db.NewBatch()
|
||||||
|
root, _ = s.commit(batch)
|
||||||
|
return root, batch
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StateDB) commit(db trie.DatabaseWriter) (common.Hash, error) {
|
||||||
|
s.refund = new(big.Int)
|
||||||
|
|
||||||
|
for _, stateObject := range s.stateObjects {
|
||||||
|
if stateObject.remove {
|
||||||
|
// If the object has been removed, don't bother syncing it
|
||||||
|
// and just mark it for deletion in the trie.
|
||||||
|
s.DeleteStateObject(stateObject)
|
||||||
|
} else {
|
||||||
|
// Write any storage changes in the state object to its trie.
|
||||||
|
stateObject.Update()
|
||||||
|
// Commit the trie of the object to the batch.
|
||||||
|
// This updates the trie root internally, so
|
||||||
|
// getting the root hash of the storage trie
|
||||||
|
// through UpdateStateObject is fast.
|
||||||
|
if _, err := stateObject.trie.CommitTo(db); err != nil {
|
||||||
|
return common.Hash{}, err
|
||||||
|
}
|
||||||
|
// Update the object in the account trie.
|
||||||
|
s.UpdateStateObject(stateObject)
|
||||||
|
}
|
||||||
|
stateObject.dirty = false
|
||||||
|
}
|
||||||
|
return s.trie.CommitTo(db)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *StateDB) Refunds() *big.Int {
|
func (self *StateDB) Refunds() *big.Int {
|
||||||
return self.refund
|
return self.refund
|
||||||
}
|
}
|
||||||
|
|
||||||
// SyncIntermediate updates the intermediate state and all mid steps
|
|
||||||
func (self *StateDB) SyncIntermediate() {
|
|
||||||
self.refund = new(big.Int)
|
|
||||||
|
|
||||||
for _, stateObject := range self.stateObjects {
|
|
||||||
if stateObject.dirty {
|
|
||||||
if stateObject.remove {
|
|
||||||
self.DeleteStateObject(stateObject)
|
|
||||||
} else {
|
|
||||||
stateObject.Update()
|
|
||||||
|
|
||||||
self.UpdateStateObject(stateObject)
|
|
||||||
}
|
|
||||||
stateObject.dirty = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// SyncObjects syncs the changed objects to the trie
|
|
||||||
func (self *StateDB) SyncObjects() {
|
|
||||||
self.trie, _ = trie.NewSecure(self.root, self.db)
|
|
||||||
|
|
||||||
self.refund = new(big.Int)
|
|
||||||
|
|
||||||
for _, stateObject := range self.stateObjects {
|
|
||||||
if stateObject.remove {
|
|
||||||
self.DeleteStateObject(stateObject)
|
|
||||||
} else {
|
|
||||||
stateObject.Update()
|
|
||||||
|
|
||||||
self.UpdateStateObject(stateObject)
|
|
||||||
}
|
|
||||||
stateObject.dirty = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Debug stuff
|
// Debug stuff
|
||||||
func (self *StateDB) CreateOutputForDiff() {
|
func (self *StateDB) CreateOutputForDiff() {
|
||||||
for _, stateObject := range self.stateObjects {
|
for _, stateObject := range self.stateObjects {
|
||||||
|
@ -266,7 +266,6 @@ func (self *worker) wait() {
|
|||||||
block := result.Block
|
block := result.Block
|
||||||
work := result.Work
|
work := result.Work
|
||||||
|
|
||||||
work.state.Sync()
|
|
||||||
if self.fullValidation {
|
if self.fullValidation {
|
||||||
if _, err := self.chain.InsertChain(types.Blocks{block}); err != nil {
|
if _, err := self.chain.InsertChain(types.Blocks{block}); err != nil {
|
||||||
glog.V(logger.Error).Infoln("mining err", err)
|
glog.V(logger.Error).Infoln("mining err", err)
|
||||||
@ -274,6 +273,7 @@ func (self *worker) wait() {
|
|||||||
}
|
}
|
||||||
go self.mux.Post(core.NewMinedBlockEvent{block})
|
go self.mux.Post(core.NewMinedBlockEvent{block})
|
||||||
} else {
|
} else {
|
||||||
|
work.state.Commit()
|
||||||
parent := self.chain.GetBlock(block.ParentHash())
|
parent := self.chain.GetBlock(block.ParentHash())
|
||||||
if parent == nil {
|
if parent == nil {
|
||||||
glog.V(logger.Error).Infoln("Invalid block found during mining")
|
glog.V(logger.Error).Infoln("Invalid block found during mining")
|
||||||
@ -528,8 +528,7 @@ func (self *worker) commitNewWork() {
|
|||||||
if atomic.LoadInt32(&self.mining) == 1 {
|
if atomic.LoadInt32(&self.mining) == 1 {
|
||||||
// commit state root after all state transitions.
|
// commit state root after all state transitions.
|
||||||
core.AccumulateRewards(work.state, header, uncles)
|
core.AccumulateRewards(work.state, header, uncles)
|
||||||
work.state.SyncObjects()
|
header.Root = work.state.IntermediateRoot()
|
||||||
header.Root = work.state.Root()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// create the new block whose nonce will be mined.
|
// create the new block whose nonce will be mined.
|
||||||
|
@ -253,13 +253,13 @@ func (t *BlockTest) InsertPreState(ethereum *eth.Ethereum) (*state.StateDB, erro
|
|||||||
statedb.SetState(common.HexToAddress(addrString), common.HexToHash(k), common.HexToHash(v))
|
statedb.SetState(common.HexToAddress(addrString), common.HexToHash(k), common.HexToHash(v))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// sync objects to trie
|
|
||||||
statedb.SyncObjects()
|
|
||||||
// sync trie to disk
|
|
||||||
statedb.Sync()
|
|
||||||
|
|
||||||
if !bytes.Equal(t.Genesis.Root().Bytes(), statedb.Root().Bytes()) {
|
root, err := statedb.Commit()
|
||||||
return nil, fmt.Errorf("computed state root does not match genesis block %x %x", t.Genesis.Root().Bytes()[:4], statedb.Root().Bytes()[:4])
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("error writing state: %v", err)
|
||||||
|
}
|
||||||
|
if t.Genesis.Root() != root {
|
||||||
|
return nil, fmt.Errorf("computed state root does not match genesis block: genesis=%x computed=%x", t.Genesis.Root().Bytes()[:4], root.Bytes()[:4])
|
||||||
}
|
}
|
||||||
return statedb, nil
|
return statedb, nil
|
||||||
}
|
}
|
||||||
|
@ -200,9 +200,9 @@ func runStateTest(test VmTest) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
statedb.Sync()
|
root, _ := statedb.Commit()
|
||||||
if common.HexToHash(test.PostStateRoot) != statedb.Root() {
|
if common.HexToHash(test.PostStateRoot) != root {
|
||||||
return fmt.Errorf("Post state root error. Expected %s, got %x", test.PostStateRoot, statedb.Root())
|
return fmt.Errorf("Post state root error. Expected %s, got %x", test.PostStateRoot, root)
|
||||||
}
|
}
|
||||||
|
|
||||||
// check logs
|
// check logs
|
||||||
@ -246,7 +246,7 @@ func RunState(statedb *state.StateDB, env, tx map[string]string) ([]byte, state.
|
|||||||
if core.IsNonceErr(err) || core.IsInvalidTxErr(err) || state.IsGasLimitErr(err) {
|
if core.IsNonceErr(err) || core.IsInvalidTxErr(err) || state.IsGasLimitErr(err) {
|
||||||
statedb.Set(snapshot)
|
statedb.Set(snapshot)
|
||||||
}
|
}
|
||||||
statedb.SyncObjects()
|
statedb.Commit()
|
||||||
|
|
||||||
return ret, vmenv.state.Logs(), vmenv.Gas, err
|
return ret, vmenv.state.Logs(), vmenv.Gas, err
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user