From 1b1f293082044c43d8d1c5df9ac40aab8fdb2ae8 Mon Sep 17 00:00:00 2001 From: Gustav Simonsson Date: Tue, 6 Oct 2015 16:35:55 +0200 Subject: core/state, core, miner: handle missing root error from state.New --- core/block_processor.go | 5 +++- core/block_processor_test.go | 2 +- core/blockchain.go | 2 +- core/chain_makers.go | 5 +++- core/chain_makers_test.go | 2 +- core/genesis.go | 7 +++--- core/state/managed_state_test.go | 2 +- core/state/state_test.go | 6 ++--- core/state/statedb.go | 10 ++++---- core/transaction_pool.go | 51 ++++++++++++++++++++++++++++++-------- core/transaction_pool_test.go | 53 ++++++++++++++++++++++++---------------- 11 files changed, 97 insertions(+), 48 deletions(-) (limited to 'core') diff --git a/core/block_processor.go b/core/block_processor.go index 783e15687..a07d79bcf 100644 --- a/core/block_processor.go +++ b/core/block_processor.go @@ -207,7 +207,10 @@ func (sm *BlockProcessor) Process(block *types.Block) (logs vm.Logs, receipts ty func (sm *BlockProcessor) processWithParent(block, parent *types.Block) (logs vm.Logs, receipts types.Receipts, err error) { // Create a new state based on the parent's root (e.g., create copy) - state := state.New(parent.Root(), sm.chainDb) + state, err := state.New(parent.Root(), sm.chainDb) + if err != nil { + return nil, nil, err + } header := block.Header() uncles := block.Uncles() txs := block.Transactions() diff --git a/core/block_processor_test.go b/core/block_processor_test.go index ba8bd7bcd..e0e5607b9 100644 --- a/core/block_processor_test.go +++ b/core/block_processor_test.go @@ -46,7 +46,7 @@ func TestNumber(t *testing.T) { pow := ezp.New() _, chain := proc() - statedb := state.New(chain.Genesis().Root(), chain.chainDb) + statedb, _ := state.New(chain.Genesis().Root(), chain.chainDb) header := makeHeader(chain.Genesis(), statedb) header.Number = big.NewInt(3) err := ValidateHeader(pow, header, chain.Genesis().Header(), false, false) diff --git a/core/blockchain.go b/core/blockchain.go index 6c555e9ee..62a306265 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -199,7 +199,7 @@ func (self *BlockChain) SetProcessor(proc types.BlockProcessor) { self.processor = proc } -func (self *BlockChain) State() *state.StateDB { +func (self *BlockChain) State() (*state.StateDB, error) { return state.New(self.CurrentBlock().Root(), self.chainDb) } diff --git a/core/chain_makers.go b/core/chain_makers.go index ba09b3029..4347d9173 100644 --- a/core/chain_makers.go +++ b/core/chain_makers.go @@ -156,7 +156,10 @@ func (b *BlockGen) OffsetTime(seconds int64) { // values. Inserting them into BlockChain requires use of FakePow or // a similar non-validating proof of work implementation. func GenerateChain(parent *types.Block, db ethdb.Database, n int, gen func(int, *BlockGen)) []*types.Block { - statedb := state.New(parent.Root(), db) + statedb, err := state.New(parent.Root(), db) + if err != nil { + panic(err) + } blocks := make(types.Blocks, n) genblock := func(i int, h *types.Header) *types.Block { b := &BlockGen{parent: parent, i: i, chain: blocks, header: h, statedb: statedb} diff --git a/core/chain_makers_test.go b/core/chain_makers_test.go index b33af8d87..63825c261 100644 --- a/core/chain_makers_test.go +++ b/core/chain_makers_test.go @@ -84,7 +84,7 @@ func ExampleGenerateChain() { return } - state := chainman.State() + state, _ := chainman.State() fmt.Printf("last block: #%d\n", chainman.CurrentBlock().Number()) fmt.Println("balance of addr1:", state.GetBalance(addr1)) fmt.Println("balance of addr2:", state.GetBalance(addr2)) diff --git a/core/genesis.go b/core/genesis.go index 4c5c17f60..16c1598c2 100644 --- a/core/genesis.go +++ b/core/genesis.go @@ -60,7 +60,8 @@ func WriteGenesisBlock(chainDb ethdb.Database, reader io.Reader) (*types.Block, return nil, err } - statedb := state.New(common.Hash{}, chainDb) + // creating with empty hash always works + statedb, _ := state.New(common.Hash{}, chainDb) for addr, account := range genesis.Alloc { address := common.HexToAddress(addr) statedb.AddBalance(address, common.String2Big(account.Balance)) @@ -115,9 +116,9 @@ func WriteGenesisBlock(chainDb ethdb.Database, reader io.Reader) (*types.Block, } // GenesisBlockForTesting creates a block in which addr has the given wei balance. -// The state trie of the block is written to db. +// The state trie of the block is written to db. the passed db needs to contain a state root func GenesisBlockForTesting(db ethdb.Database, addr common.Address, balance *big.Int) *types.Block { - statedb := state.New(common.Hash{}, db) + statedb, _ := state.New(common.Hash{}, db) obj := statedb.GetOrNewStateObject(addr) obj.SetBalance(balance) root, err := statedb.Commit() diff --git a/core/state/managed_state_test.go b/core/state/managed_state_test.go index 58e77d842..0b53a42c5 100644 --- a/core/state/managed_state_test.go +++ b/core/state/managed_state_test.go @@ -27,7 +27,7 @@ var addr = common.BytesToAddress([]byte("test")) func create() (*ManagedState, *account) { db, _ := ethdb.NewMemDatabase() - statedb := New(common.Hash{}, db) + statedb, _ := New(common.Hash{}, db) ms := ManageState(statedb) so := &StateObject{address: addr, nonce: 100} ms.StateDB.stateObjects[addr.Str()] = so diff --git a/core/state/state_test.go b/core/state/state_test.go index b5a7f4081..08fbc47fa 100644 --- a/core/state/state_test.go +++ b/core/state/state_test.go @@ -77,12 +77,12 @@ func (s *StateSuite) TestDump(c *checker.C) { func (s *StateSuite) SetUpTest(c *checker.C) { db, _ := ethdb.NewMemDatabase() - s.state = New(common.Hash{}, db) + s.state, _ = New(common.Hash{}, db) } func TestNull(t *testing.T) { db, _ := ethdb.NewMemDatabase() - state := New(common.Hash{}, db) + state, _ := New(common.Hash{}, db) address := common.HexToAddress("0x823140710bf13990e4500136726d8b55") state.CreateAccount(address) @@ -122,7 +122,7 @@ func (s *StateSuite) TestSnapshot(c *checker.C) { // printing/logging in tests (-check.vv does not work) func TestSnapshot2(t *testing.T) { db, _ := ethdb.NewMemDatabase() - state := New(common.Hash{}, db) + state, _ := New(common.Hash{}, db) stateobjaddr0 := toAddr([]byte("so0")) stateobjaddr1 := toAddr([]byte("so1")) diff --git a/core/state/statedb.go b/core/state/statedb.go index ad673aecb..a9de71409 100644 --- a/core/state/statedb.go +++ b/core/state/statedb.go @@ -52,12 +52,11 @@ type StateDB struct { } // Create a new state from a given trie -func New(root common.Hash, db ethdb.Database) *StateDB { +func New(root common.Hash, db ethdb.Database) (*StateDB, error) { tr, err := trie.NewSecure(root, db) if err != nil { - // TODO: bubble this up - tr, _ = trie.NewSecure(common.Hash{}, db) glog.Errorf("can't create state trie with root %x: %v", root[:], err) + return nil, err } return &StateDB{ db: db, @@ -65,7 +64,7 @@ func New(root common.Hash, db ethdb.Database) *StateDB { stateObjects: make(map[string]*StateObject), refund: new(big.Int), logs: make(map[common.Hash]vm.Logs), - } + }, nil } func (self *StateDB) StartRecord(thash, bhash common.Hash, ti int) { @@ -297,7 +296,8 @@ func (self *StateDB) CreateAccount(addr common.Address) vm.Account { // func (self *StateDB) Copy() *StateDB { - state := New(common.Hash{}, self.db) + // ignore error - we assume state-to-be-copied always exists + state, _ := New(common.Hash{}, self.db) state.trie = self.trie for k, stateObject := range self.stateObjects { state.stateObjects[k] = stateObject.Copy() diff --git a/core/transaction_pool.go b/core/transaction_pool.go index a4e6ce3e2..16f66efdc 100644 --- a/core/transaction_pool.go +++ b/core/transaction_pool.go @@ -48,7 +48,7 @@ const ( maxQueued = 64 // max limit of queued txs per address ) -type stateFn func() *state.StateDB +type stateFn func() (*state.StateDB, error) // TxPool contains all currently known transactions. Transactions // enter the pool when they are received from the network or submitted @@ -80,7 +80,7 @@ func NewTxPool(eventMux *event.TypeMux, currentStateFn stateFn, gasLimitFn func( currentState: currentStateFn, gasLimit: gasLimitFn, minGasPrice: new(big.Int), - pendingState: state.ManageState(currentStateFn()), + pendingState: nil, events: eventMux.Subscribe(ChainHeadEvent{}, GasPriceChanged{}, RemovedTransactionEvent{}), } go pool.eventLoop() @@ -109,7 +109,17 @@ func (pool *TxPool) eventLoop() { } func (pool *TxPool) resetState() { - pool.pendingState = state.ManageState(pool.currentState()) + currentState, err := pool.currentState() + if err != nil { + glog.V(logger.Info).Infoln("failed to get current state: %v", err) + return + } + managedState := state.ManageState(currentState) + if err != nil { + glog.V(logger.Info).Infoln("failed to get managed state: %v", err) + return + } + pool.pendingState = managedState // validate the pool of pending transactions, this will remove // any transactions that have been included in the block or @@ -180,12 +190,16 @@ func (pool *TxPool) validateTx(tx *types.Transaction) error { // Make sure the account exist. Non existent accounts // haven't got funds and well therefor never pass. - if !pool.currentState().HasAccount(from) { + currentState, err := pool.currentState() + if err != nil { + return err + } + if !currentState.HasAccount(from) { return ErrNonExistentAccount } // Last but not least check for nonce errors - if pool.currentState().GetNonce(from) > tx.Nonce() { + if currentState.GetNonce(from) > tx.Nonce() { return ErrNonce } @@ -204,7 +218,7 @@ func (pool *TxPool) validateTx(tx *types.Transaction) error { // Transactor should have enough funds to cover the costs // cost == V + GP * GL - if pool.currentState().GetBalance(from).Cmp(tx.Cost()) < 0 { + if currentState.GetBalance(from).Cmp(tx.Cost()) < 0 { return ErrInsufficientFunds } @@ -257,6 +271,11 @@ func (self *TxPool) queueTx(hash common.Hash, tx *types.Transaction) { // addTx will add a transaction to the pending (processable queue) list of transactions func (pool *TxPool) addTx(hash common.Hash, addr common.Address, tx *types.Transaction) { + // init delayed since tx pool could have been started before any state sync + if pool.pendingState == nil { + pool.resetState() + } + if _, ok := pool.pending[hash]; !ok { pool.pending[hash] = tx @@ -382,14 +401,22 @@ func (pool *TxPool) RemoveTx(hash common.Hash) { // checkQueue moves transactions that have become processable to main pool. func (pool *TxPool) checkQueue() { - state := pool.pendingState + // init delayed since tx pool could have been started before any state sync + if pool.pendingState == nil { + pool.resetState() + } var addq txQueue for address, txs := range pool.queue { // guessed nonce is the nonce currently kept by the tx pool (pending state) - guessedNonce := state.GetNonce(address) + guessedNonce := pool.pendingState.GetNonce(address) // true nonce is the nonce known by the last state - trueNonce := pool.currentState().GetNonce(address) + currentState, err := pool.currentState() + if err != nil { + glog.Errorf("could not get current state: %v", err) + return + } + trueNonce := currentState.GetNonce(address) addq := addq[:0] for hash, tx := range txs { if tx.Nonce() < trueNonce { @@ -434,7 +461,11 @@ func (pool *TxPool) checkQueue() { // validatePool removes invalid and processed transactions from the main pool. func (pool *TxPool) validatePool() { - state := pool.currentState() + state, err := pool.currentState() + if err != nil { + glog.V(logger.Info).Infoln("failed to get current state: %v", err) + return + } for hash, tx := range pool.pending { from, _ := tx.From() // err already checked // perform light nonce validation diff --git a/core/transaction_pool_test.go b/core/transaction_pool_test.go index 37cd20c96..229dcacf3 100644 --- a/core/transaction_pool_test.go +++ b/core/transaction_pool_test.go @@ -36,11 +36,13 @@ func transaction(nonce uint64, gaslimit *big.Int, key *ecdsa.PrivateKey) *types. func setupTxPool() (*TxPool, *ecdsa.PrivateKey) { db, _ := ethdb.NewMemDatabase() - statedb := state.New(common.Hash{}, db) + statedb, _ := state.New(common.Hash{}, db) var m event.TypeMux key, _ := crypto.GenerateKey() - return NewTxPool(&m, func() *state.StateDB { return statedb }, func() *big.Int { return big.NewInt(1000000) }), key + newPool := NewTxPool(&m, func() (*state.StateDB, error) { return statedb, nil }, func() *big.Int { return big.NewInt(1000000) }) + newPool.resetState() + return newPool, key } func TestInvalidTransactions(t *testing.T) { @@ -52,19 +54,20 @@ func TestInvalidTransactions(t *testing.T) { } from, _ := tx.From() - pool.currentState().AddBalance(from, big.NewInt(1)) + currentState, _ := pool.currentState() + currentState.AddBalance(from, big.NewInt(1)) if err := pool.Add(tx); err != ErrInsufficientFunds { t.Error("expected", ErrInsufficientFunds) } balance := new(big.Int).Add(tx.Value(), new(big.Int).Mul(tx.Gas(), tx.GasPrice())) - pool.currentState().AddBalance(from, balance) + currentState.AddBalance(from, balance) if err := pool.Add(tx); err != ErrIntrinsicGas { t.Error("expected", ErrIntrinsicGas, "got", err) } - pool.currentState().SetNonce(from, 1) - pool.currentState().AddBalance(from, big.NewInt(0xffffffffffffff)) + currentState.SetNonce(from, 1) + currentState.AddBalance(from, big.NewInt(0xffffffffffffff)) tx = transaction(0, big.NewInt(100000), key) if err := pool.Add(tx); err != ErrNonce { t.Error("expected", ErrNonce) @@ -75,7 +78,8 @@ func TestTransactionQueue(t *testing.T) { pool, key := setupTxPool() tx := transaction(0, big.NewInt(100), key) from, _ := tx.From() - pool.currentState().AddBalance(from, big.NewInt(1)) + currentState, _ := pool.currentState() + currentState.AddBalance(from, big.NewInt(1)) pool.queueTx(tx.Hash(), tx) pool.checkQueue() @@ -85,7 +89,7 @@ func TestTransactionQueue(t *testing.T) { tx = transaction(1, big.NewInt(100), key) from, _ = tx.From() - pool.currentState().SetNonce(from, 2) + currentState.SetNonce(from, 2) pool.queueTx(tx.Hash(), tx) pool.checkQueue() if _, ok := pool.pending[tx.Hash()]; ok { @@ -119,7 +123,8 @@ func TestRemoveTx(t *testing.T) { pool, key := setupTxPool() tx := transaction(0, big.NewInt(100), key) from, _ := tx.From() - pool.currentState().AddBalance(from, big.NewInt(1)) + currentState, _ := pool.currentState() + currentState.AddBalance(from, big.NewInt(1)) pool.queueTx(tx.Hash(), tx) pool.addTx(tx.Hash(), from, tx) if len(pool.queue) != 1 { @@ -146,7 +151,8 @@ func TestNegativeValue(t *testing.T) { tx, _ := types.NewTransaction(0, common.Address{}, big.NewInt(-1), big.NewInt(100), big.NewInt(1), nil).SignECDSA(key) from, _ := tx.From() - pool.currentState().AddBalance(from, big.NewInt(1)) + currentState, _ := pool.currentState() + currentState.AddBalance(from, big.NewInt(1)) if err := pool.Add(tx); err != ErrNegativeValue { t.Error("expected", ErrNegativeValue, "got", err) } @@ -157,9 +163,10 @@ func TestTransactionChainFork(t *testing.T) { addr := crypto.PubkeyToAddress(key.PublicKey) resetState := func() { db, _ := ethdb.NewMemDatabase() - statedb := state.New(common.Hash{}, db) - pool.currentState = func() *state.StateDB { return statedb } - pool.currentState().AddBalance(addr, big.NewInt(100000000000000)) + statedb, _ := state.New(common.Hash{}, db) + pool.currentState = func() (*state.StateDB, error) { return statedb, nil } + currentState, _ := pool.currentState() + currentState.AddBalance(addr, big.NewInt(100000000000000)) pool.resetState() } resetState() @@ -182,9 +189,10 @@ func TestTransactionDoubleNonce(t *testing.T) { addr := crypto.PubkeyToAddress(key.PublicKey) resetState := func() { db, _ := ethdb.NewMemDatabase() - statedb := state.New(common.Hash{}, db) - pool.currentState = func() *state.StateDB { return statedb } - pool.currentState().AddBalance(addr, big.NewInt(100000000000000)) + statedb, _ := state.New(common.Hash{}, db) + pool.currentState = func() (*state.StateDB, error) { return statedb, nil } + currentState, _ := pool.currentState() + currentState.AddBalance(addr, big.NewInt(100000000000000)) pool.resetState() } resetState() @@ -207,7 +215,8 @@ func TestTransactionDoubleNonce(t *testing.T) { func TestMissingNonce(t *testing.T) { pool, key := setupTxPool() addr := crypto.PubkeyToAddress(key.PublicKey) - pool.currentState().AddBalance(addr, big.NewInt(100000000000000)) + currentState, _ := pool.currentState() + currentState.AddBalance(addr, big.NewInt(100000000000000)) tx := transaction(1, big.NewInt(100000), key) if err := pool.add(tx); err != nil { t.Error("didn't expect error", err) @@ -224,15 +233,16 @@ func TestNonceRecovery(t *testing.T) { const n = 10 pool, key := setupTxPool() addr := crypto.PubkeyToAddress(key.PublicKey) - pool.currentState().SetNonce(addr, n) - pool.currentState().AddBalance(addr, big.NewInt(100000000000000)) + currentState, _ := pool.currentState() + currentState.SetNonce(addr, n) + currentState.AddBalance(addr, big.NewInt(100000000000000)) pool.resetState() tx := transaction(n, big.NewInt(100000), key) if err := pool.Add(tx); err != nil { t.Error(err) } // simulate some weird re-order of transactions and missing nonce(s) - pool.currentState().SetNonce(addr, n-1) + currentState.SetNonce(addr, n-1) pool.resetState() if fn := pool.pendingState.GetNonce(addr); fn != n+1 { t.Errorf("expected nonce to be %d, got %d", n+1, fn) @@ -243,7 +253,8 @@ func TestRemovedTxEvent(t *testing.T) { pool, key := setupTxPool() tx := transaction(0, big.NewInt(1000000), key) from, _ := tx.From() - pool.currentState().AddBalance(from, big.NewInt(1000000000000)) + currentState, _ := pool.currentState() + currentState.AddBalance(from, big.NewInt(1000000000000)) pool.eventMux.Post(RemovedTransactionEvent{types.Transactions{tx}}) pool.eventMux.Post(ChainHeadEvent{nil}) if len(pool.pending) != 1 { -- cgit