From a1d9ef48c505ab4314ca8e3ee1fc272032da3034 Mon Sep 17 00:00:00 2001 From: Jeffrey Wilcke Date: Mon, 19 Oct 2015 16:08:17 +0200 Subject: core, eth, rpc: split out block validator and state processor This removes the burden on a single object to take care of all validation and state processing. Now instead the validation is done by the `core.BlockValidator` (`types.Validator`) that takes care of both header and uncle validation through the `ValidateBlock` method and state validation through the `ValidateState` method. The state processing is done by a new object `core.StateProcessor` (`types.Processor`) and accepts a new state as input and uses that to process the given block's transactions (and uncles for rewords) to calculate the state root for the next block (P_n + 1). --- cmd/utils/flags.go | 2 - core/bench_test.go | 1 - core/block_processor.go | 460 ------------------------------------------- core/block_processor_test.go | 89 --------- core/block_validator.go | 243 +++++++++++++++++++++++ core/block_validator_test.go | 89 +++++++++ core/blockchain.go | 134 +++++++++++-- core/blockchain_test.go | 139 +++++++------ core/chain_makers.go | 11 +- core/chain_makers_test.go | 9 +- core/gaspool.go | 46 +++++ core/manager.go | 34 ---- core/state_processor.go | 107 ++++++++++ core/types.go | 70 +++++++ core/types/common.go | 25 --- eth/backend.go | 41 ++-- eth/gasprice.go | 2 +- eth/helper_test.go | 2 - miner/worker.go | 21 +- rpc/api/debug.go | 28 ++- xeth/xeth.go | 2 +- 21 files changed, 807 insertions(+), 748 deletions(-) delete mode 100644 core/block_processor.go delete mode 100644 core/block_processor_test.go create mode 100644 core/block_validator.go create mode 100644 core/block_validator_test.go create mode 100644 core/gaspool.go delete mode 100644 core/manager.go create mode 100644 core/state_processor.go create mode 100644 core/types.go delete mode 100644 core/types/common.go diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go index 8335517df..3792dc1e0 100644 --- a/cmd/utils/flags.go +++ b/cmd/utils/flags.go @@ -557,8 +557,6 @@ func MakeChain(ctx *cli.Context) (chain *core.BlockChain, chainDb ethdb.Database Fatalf("Could not start chainmanager: %v", err) } - proc := core.NewBlockProcessor(chainDb, pow, chain, eventMux) - chain.SetProcessor(proc) return chain, chainDb } diff --git a/core/bench_test.go b/core/bench_test.go index b5eb51803..6fa7659b9 100644 --- a/core/bench_test.go +++ b/core/bench_test.go @@ -169,7 +169,6 @@ func benchInsertChain(b *testing.B, disk bool, gen func(int, *BlockGen)) { // State and blocks are stored in the same DB. evmux := new(event.TypeMux) chainman, _ := NewBlockChain(db, FakePow{}, evmux) - chainman.SetProcessor(NewBlockProcessor(db, FakePow{}, chainman, evmux)) defer chainman.Stop() b.ReportAllocs() b.ResetTimer() diff --git a/core/block_processor.go b/core/block_processor.go deleted file mode 100644 index e7b2f63e5..000000000 --- a/core/block_processor.go +++ /dev/null @@ -1,460 +0,0 @@ -// Copyright 2014 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 core - -import ( - "fmt" - "math/big" - "sync" - "time" - - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/core/state" - "github.com/ethereum/go-ethereum/core/types" - "github.com/ethereum/go-ethereum/core/vm" - "github.com/ethereum/go-ethereum/crypto" - "github.com/ethereum/go-ethereum/ethdb" - "github.com/ethereum/go-ethereum/event" - "github.com/ethereum/go-ethereum/logger" - "github.com/ethereum/go-ethereum/logger/glog" - "github.com/ethereum/go-ethereum/params" - "github.com/ethereum/go-ethereum/pow" - "gopkg.in/fatih/set.v0" -) - -const ( - // must be bumped when consensus algorithm is changed, this forces the upgradedb - // command to be run (forces the blocks to be imported again using the new algorithm) - BlockChainVersion = 3 -) - -type BlockProcessor struct { - chainDb ethdb.Database - // Mutex for locking the block processor. Blocks can only be handled one at a time - mutex sync.Mutex - // Canonical block chain - bc *BlockChain - // non-persistent key/value memory storage - mem map[string]*big.Int - // Proof of work used for validating - Pow pow.PoW - - events event.Subscription - - eventMux *event.TypeMux -} - -// GasPool tracks the amount of gas available during -// execution of the transactions in a block. -// The zero value is a pool with zero gas available. -type GasPool big.Int - -// AddGas makes gas available for execution. -func (gp *GasPool) AddGas(amount *big.Int) *GasPool { - i := (*big.Int)(gp) - i.Add(i, amount) - return gp -} - -// SubGas deducts the given amount from the pool if enough gas is -// available and returns an error otherwise. -func (gp *GasPool) SubGas(amount *big.Int) error { - i := (*big.Int)(gp) - if i.Cmp(amount) < 0 { - return &GasLimitErr{Have: new(big.Int).Set(i), Want: amount} - } - i.Sub(i, amount) - return nil -} - -func (gp *GasPool) String() string { - return (*big.Int)(gp).String() -} - -func NewBlockProcessor(db ethdb.Database, pow pow.PoW, blockchain *BlockChain, eventMux *event.TypeMux) *BlockProcessor { - sm := &BlockProcessor{ - chainDb: db, - mem: make(map[string]*big.Int), - Pow: pow, - bc: blockchain, - eventMux: eventMux, - } - return sm -} - -func (sm *BlockProcessor) TransitionState(statedb *state.StateDB, parent, block *types.Block, transientProcess bool) (receipts types.Receipts, err error) { - gp := new(GasPool).AddGas(block.GasLimit()) - if glog.V(logger.Core) { - glog.Infof("%x: gas (+ %v)", block.Coinbase(), gp) - } - - // Process the transactions on to parent state - receipts, err = sm.ApplyTransactions(gp, statedb, block, block.Transactions(), transientProcess) - if err != nil { - return nil, err - } - - return receipts, nil -} - -func (self *BlockProcessor) ApplyTransaction(gp *GasPool, statedb *state.StateDB, header *types.Header, tx *types.Transaction, usedGas *big.Int, transientProcess bool) (*types.Receipt, *big.Int, error) { - _, gas, err := ApplyMessage(NewEnv(statedb, self.bc, tx, header), tx, gp) - if err != nil { - return nil, nil, err - } - - // Update the state with pending changes - usedGas.Add(usedGas, gas) - receipt := types.NewReceipt(statedb.IntermediateRoot().Bytes(), usedGas) - receipt.TxHash = tx.Hash() - receipt.GasUsed = new(big.Int).Set(gas) - if MessageCreatesContract(tx) { - from, _ := tx.From() - receipt.ContractAddress = crypto.CreateAddress(from, tx.Nonce()) - } - - logs := statedb.GetLogs(tx.Hash()) - receipt.Logs = logs - receipt.Bloom = types.CreateBloom(types.Receipts{receipt}) - - glog.V(logger.Debug).Infoln(receipt) - - // Notify all subscribers - if !transientProcess { - go self.eventMux.Post(TxPostEvent{tx}) - go self.eventMux.Post(logs) - } - - return receipt, gas, err -} -func (self *BlockProcessor) BlockChain() *BlockChain { - return self.bc -} - -func (self *BlockProcessor) ApplyTransactions(gp *GasPool, statedb *state.StateDB, block *types.Block, txs types.Transactions, transientProcess bool) (types.Receipts, error) { - var ( - receipts types.Receipts - totalUsedGas = big.NewInt(0) - err error - cumulativeSum = new(big.Int) - header = block.Header() - ) - - for i, tx := range txs { - statedb.StartRecord(tx.Hash(), block.Hash(), i) - - receipt, txGas, err := self.ApplyTransaction(gp, statedb, header, tx, totalUsedGas, transientProcess) - if err != nil { - return nil, err - } - - if err != nil { - glog.V(logger.Core).Infoln("TX err:", err) - } - receipts = append(receipts, receipt) - - cumulativeSum.Add(cumulativeSum, new(big.Int).Mul(txGas, tx.GasPrice())) - } - - if block.GasUsed().Cmp(totalUsedGas) != 0 { - return nil, ValidationError(fmt.Sprintf("gas used error (%v / %v)", block.GasUsed(), totalUsedGas)) - } - - if transientProcess { - go self.eventMux.Post(PendingBlockEvent{block, statedb.Logs()}) - } - - return receipts, err -} - -func (sm *BlockProcessor) RetryProcess(block *types.Block) (logs vm.Logs, err error) { - // Processing a blocks may never happen simultaneously - sm.mutex.Lock() - defer sm.mutex.Unlock() - - if !sm.bc.HasBlock(block.ParentHash()) { - return nil, ParentError(block.ParentHash()) - } - parent := sm.bc.GetBlock(block.ParentHash()) - - // FIXME Change to full header validation. See #1225 - errch := make(chan bool) - go func() { errch <- sm.Pow.Verify(block) }() - - logs, _, err = sm.processWithParent(block, parent) - if !<-errch { - return nil, ValidationError("Block's nonce is invalid (= %x)", block.Nonce) - } - - return logs, err -} - -// Process block will attempt to process the given block's transactions and applies them -// on top of the block's parent state (given it exists) and will return wether it was -// successful or not. -func (sm *BlockProcessor) Process(block *types.Block) (logs vm.Logs, receipts types.Receipts, err error) { - // Processing a blocks may never happen simultaneously - sm.mutex.Lock() - defer sm.mutex.Unlock() - - if sm.bc.HasBlock(block.Hash()) { - if _, err := state.New(block.Root(), sm.chainDb); err == nil { - return nil, nil, &KnownBlockError{block.Number(), block.Hash()} - } - } - if parent := sm.bc.GetBlock(block.ParentHash()); parent != nil { - if _, err := state.New(parent.Root(), sm.chainDb); err == nil { - return sm.processWithParent(block, parent) - } - } - return nil, nil, ParentError(block.ParentHash()) -} - -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, err := state.New(parent.Root(), sm.chainDb) - if err != nil { - return nil, nil, err - } - header := block.Header() - uncles := block.Uncles() - txs := block.Transactions() - - // Block validation - if err = ValidateHeader(sm.Pow, header, parent.Header(), false, false); err != nil { - return - } - - // There can be at most two uncles - if len(uncles) > 2 { - return nil, nil, ValidationError("Block can only contain maximum 2 uncles (contained %v)", len(uncles)) - } - - receipts, err = sm.TransitionState(state, parent, block, false) - if err != nil { - return - } - - // Validate the received block's bloom with the one derived from the generated receipts. - // For valid blocks this should always validate to true. - rbloom := types.CreateBloom(receipts) - if rbloom != header.Bloom { - err = fmt.Errorf("unable to replicate block's bloom=%x", rbloom) - return - } - - // The transactions Trie's root (R = (Tr [[i, RLP(T1)], [i, RLP(T2)], ... [n, RLP(Tn)]])) - // can be used by light clients to make sure they've received the correct Txs - txSha := types.DeriveSha(txs) - if txSha != header.TxHash { - err = fmt.Errorf("invalid transaction root hash. received=%x calculated=%x", header.TxHash, txSha) - return - } - - // Tre receipt Trie's root (R = (Tr [[H1, R1], ... [Hn, R1]])) - receiptSha := types.DeriveSha(receipts) - if receiptSha != header.ReceiptHash { - err = fmt.Errorf("invalid receipt root hash. received=%x calculated=%x", header.ReceiptHash, receiptSha) - return - } - - // Verify UncleHash before running other uncle validations - unclesSha := types.CalcUncleHash(uncles) - if unclesSha != header.UncleHash { - err = fmt.Errorf("invalid uncles root hash. received=%x calculated=%x", header.UncleHash, unclesSha) - return - } - - // Verify uncles - if err = sm.VerifyUncles(state, block, parent); err != nil { - return - } - // Accumulate static rewards; block reward, uncle's and uncle inclusion. - AccumulateRewards(state, header, uncles) - - // Commit state objects/accounts to a database batch and calculate - // the state root. The database is not modified if the root - // doesn't match. - root, batch := state.CommitBatch() - if header.Root != root { - return nil, nil, fmt.Errorf("invalid merkle root: header=%x computed=%x", header.Root, root) - } - - // Execute the database writes. - batch.Write() - - return state.Logs(), receipts, nil -} - -var ( - big8 = big.NewInt(8) - big32 = big.NewInt(32) -) - -// AccumulateRewards credits the coinbase of the given block with the -// mining reward. The total reward consists of the static block reward -// and rewards for included uncles. The coinbase of each uncle block is -// also rewarded. -func AccumulateRewards(statedb *state.StateDB, header *types.Header, uncles []*types.Header) { - reward := new(big.Int).Set(BlockReward) - r := new(big.Int) - for _, uncle := range uncles { - r.Add(uncle.Number, big8) - r.Sub(r, header.Number) - r.Mul(r, BlockReward) - r.Div(r, big8) - statedb.AddBalance(uncle.Coinbase, r) - - r.Div(BlockReward, big32) - reward.Add(reward, r) - } - statedb.AddBalance(header.Coinbase, reward) -} - -func (sm *BlockProcessor) VerifyUncles(statedb *state.StateDB, block, parent *types.Block) error { - uncles := set.New() - ancestors := make(map[common.Hash]*types.Block) - for _, ancestor := range sm.bc.GetBlocksFromHash(block.ParentHash(), 7) { - ancestors[ancestor.Hash()] = ancestor - // Include ancestors uncles in the uncle set. Uncles must be unique. - for _, uncle := range ancestor.Uncles() { - uncles.Add(uncle.Hash()) - } - } - ancestors[block.Hash()] = block - uncles.Add(block.Hash()) - - for i, uncle := range block.Uncles() { - hash := uncle.Hash() - if uncles.Has(hash) { - // Error not unique - return UncleError("uncle[%d](%x) not unique", i, hash[:4]) - } - uncles.Add(hash) - - if ancestors[hash] != nil { - branch := fmt.Sprintf(" O - %x\n |\n", block.Hash()) - for h := range ancestors { - branch += fmt.Sprintf(" O - %x\n |\n", h) - } - glog.Infoln(branch) - return UncleError("uncle[%d](%x) is ancestor", i, hash[:4]) - } - - if ancestors[uncle.ParentHash] == nil || uncle.ParentHash == parent.Hash() { - return UncleError("uncle[%d](%x)'s parent is not ancestor (%x)", i, hash[:4], uncle.ParentHash[0:4]) - } - - if err := ValidateHeader(sm.Pow, uncle, ancestors[uncle.ParentHash].Header(), true, true); err != nil { - return ValidationError(fmt.Sprintf("uncle[%d](%x) header invalid: %v", i, hash[:4], err)) - } - } - - return nil -} - -// GetBlockReceipts returns the receipts beloniging to the block hash -func (sm *BlockProcessor) GetBlockReceipts(bhash common.Hash) types.Receipts { - if block := sm.BlockChain().GetBlock(bhash); block != nil { - return GetBlockReceipts(sm.chainDb, block.Hash()) - } - - return nil -} - -// GetLogs returns the logs of the given block. This method is using a two step approach -// where it tries to get it from the (updated) method which gets them from the receipts or -// the depricated way by re-processing the block. -func (sm *BlockProcessor) GetLogs(block *types.Block) (logs vm.Logs, err error) { - receipts := GetBlockReceipts(sm.chainDb, block.Hash()) - // coalesce logs - for _, receipt := range receipts { - logs = append(logs, receipt.Logs...) - } - return logs, nil -} - -// ValidateHeader verifies the validity of a header, relying on the database and -// POW behind the block processor. -func (sm *BlockProcessor) ValidateHeader(header *types.Header, checkPow, uncle bool) error { - // Short circuit if the header's already known or its parent missing - if sm.bc.HasHeader(header.Hash()) { - return nil - } - if parent := sm.bc.GetHeader(header.ParentHash); parent == nil { - return ParentError(header.ParentHash) - } else { - return ValidateHeader(sm.Pow, header, parent, checkPow, uncle) - } -} - -// ValidateHeaderWithParent verifies the validity of a header, relying on the database and -// POW behind the block processor. -func (sm *BlockProcessor) ValidateHeaderWithParent(header, parent *types.Header, checkPow, uncle bool) error { - if sm.bc.HasHeader(header.Hash()) { - return nil - } - return ValidateHeader(sm.Pow, header, parent, checkPow, uncle) -} - -// See YP section 4.3.4. "Block Header Validity" -// Validates a header. Returns an error if the header is invalid. -func ValidateHeader(pow pow.PoW, header *types.Header, parent *types.Header, checkPow, uncle bool) error { - if big.NewInt(int64(len(header.Extra))).Cmp(params.MaximumExtraDataSize) == 1 { - return fmt.Errorf("Header extra data too long (%d)", len(header.Extra)) - } - if uncle { - if header.Time.Cmp(common.MaxBig) == 1 { - return BlockTSTooBigErr - } - } else { - if header.Time.Cmp(big.NewInt(time.Now().Unix())) == 1 { - return BlockFutureErr - } - } - if header.Time.Cmp(parent.Time) != 1 { - return BlockEqualTSErr - } - - expd := CalcDifficulty(header.Time.Uint64(), parent.Time.Uint64(), parent.Number, parent.Difficulty) - if expd.Cmp(header.Difficulty) != 0 { - return fmt.Errorf("Difficulty check failed for header %v, %v", header.Difficulty, expd) - } - - a := new(big.Int).Set(parent.GasLimit) - a = a.Sub(a, header.GasLimit) - a.Abs(a) - b := new(big.Int).Set(parent.GasLimit) - b = b.Div(b, params.GasLimitBoundDivisor) - if !(a.Cmp(b) < 0) || (header.GasLimit.Cmp(params.MinGasLimit) == -1) { - return fmt.Errorf("GasLimit check failed for header %v (%v > %v)", header.GasLimit, a, b) - } - - num := new(big.Int).Set(parent.Number) - num.Sub(header.Number, num) - if num.Cmp(big.NewInt(1)) != 0 { - return BlockNumberErr - } - - if checkPow { - // Verify the nonce of the header. Return an error if it's not valid - if !pow.Verify(types.NewBlockWithHeader(header)) { - return &BlockNonceErr{Hash: header.Hash(), Number: header.Number, Nonce: header.Nonce.Uint64()} - } - } - return nil -} diff --git a/core/block_processor_test.go b/core/block_processor_test.go deleted file mode 100644 index 3050456b4..000000000 --- a/core/block_processor_test.go +++ /dev/null @@ -1,89 +0,0 @@ -// Copyright 2015 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 core - -import ( - "fmt" - "math/big" - "testing" - - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/core/state" - "github.com/ethereum/go-ethereum/core/types" - "github.com/ethereum/go-ethereum/core/vm" - "github.com/ethereum/go-ethereum/ethdb" - "github.com/ethereum/go-ethereum/event" - "github.com/ethereum/go-ethereum/pow/ezp" -) - -func proc() (*BlockProcessor, *BlockChain) { - db, _ := ethdb.NewMemDatabase() - var mux event.TypeMux - - WriteTestNetGenesisBlock(db, 0) - blockchain, err := NewBlockChain(db, thePow(), &mux) - if err != nil { - fmt.Println(err) - } - return NewBlockProcessor(db, ezp.New(), blockchain, &mux), blockchain -} - -func TestNumber(t *testing.T) { - pow := ezp.New() - _, chain := proc() - - 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) - if err != BlockNumberErr { - t.Errorf("expected block number error, got %q", err) - } - - header = makeHeader(chain.Genesis(), statedb) - err = ValidateHeader(pow, header, chain.Genesis().Header(), false, false) - if err == BlockNumberErr { - t.Errorf("didn't expect block number error") - } -} - -func TestPutReceipt(t *testing.T) { - db, _ := ethdb.NewMemDatabase() - - var addr common.Address - addr[0] = 1 - var hash common.Hash - hash[0] = 2 - - receipt := new(types.Receipt) - receipt.Logs = vm.Logs{&vm.Log{ - Address: addr, - Topics: []common.Hash{hash}, - Data: []byte("hi"), - BlockNumber: 42, - TxHash: hash, - TxIndex: 0, - BlockHash: hash, - Index: 0, - }} - - PutReceipts(db, types.Receipts{receipt}) - receipt = GetReceipt(db, common.Hash{}) - if receipt == nil { - t.Error("expected to get 1 receipt, got none.") - } -} diff --git a/core/block_validator.go b/core/block_validator.go new file mode 100644 index 000000000..62d096d02 --- /dev/null +++ b/core/block_validator.go @@ -0,0 +1,243 @@ +// Copyright 2014 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 core + +import ( + "fmt" + "math/big" + "time" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/state" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/logger/glog" + "github.com/ethereum/go-ethereum/params" + "github.com/ethereum/go-ethereum/pow" + "gopkg.in/fatih/set.v0" +) + +// BlockValidator is responsible for validating block headers, uncles and +// processed state. +// +// BlockValidator implements Validator. +type BlockValidator struct { + bc *BlockChain // Canonical block chain + Pow pow.PoW // Proof of work used for validating +} + +// NewBlockValidator returns a new block validator which is safe for re-use +func NewBlockValidator(blockchain *BlockChain, pow pow.PoW) *BlockValidator { + validator := &BlockValidator{ + Pow: pow, + bc: blockchain, + } + return validator +} + +// ValidateBlock validates the given block's header and uncles and verifies the +// the block header's transaction and uncle roots. +// +// ValidateBlock does not validate the header's pow. The pow work validated +// seperately so we can process them in paralel. +// +// ValidateBlock also validates and makes sure that any previous state (or present) +// state that might or might not be present is checked to make sure that fast +// sync has done it's job proper. This prevents the block validator form accepting +// false positives where a header is present but the state is not. +func (v *BlockValidator) ValidateBlock(block *types.Block) error { + if v.bc.HasBlock(block.Hash()) { + if _, err := state.New(block.Root(), v.bc.chainDb); err == nil { + return &KnownBlockError{block.Number(), block.Hash()} + } + } + parent := v.bc.GetBlock(block.ParentHash()) + if parent == nil { + return ParentError(block.ParentHash()) + } + if _, err := state.New(parent.Root(), v.bc.chainDb); err != nil { + return ParentError(block.ParentHash()) + } + + header := block.Header() + // validate the block header + if err := ValidateHeader(v.Pow, header, parent.Header(), false, false); err != nil { + return err + } + // verify the uncles are correctly rewarded + if err := v.VerifyUncles(block, parent); err != nil { + return err + } + + // Verify UncleHash before running other uncle validations + unclesSha := types.CalcUncleHash(block.Uncles()) + if unclesSha != header.UncleHash { + return fmt.Errorf("invalid uncles root hash. received=%x calculated=%x", header.UncleHash, unclesSha) + } + + // The transactions Trie's root (R = (Tr [[i, RLP(T1)], [i, RLP(T2)], ... [n, RLP(Tn)]])) + // can be used by light clients to make sure they've received the correct Txs + txSha := types.DeriveSha(block.Transactions()) + if txSha != header.TxHash { + return fmt.Errorf("invalid transaction root hash. received=%x calculated=%x", header.TxHash, txSha) + } + + return nil +} + +// ValidateState validates the various changes that happen after a state +// transition, such as amount of used gas, the receipt roots and the state root +// itself. ValidateState returns a database batch if the validation was a succes +// otherwise nil and an error is returned. +func (v *BlockValidator) ValidateState(block, parent *types.Block, statedb *state.StateDB, receipts types.Receipts, usedGas *big.Int) (err error) { + header := block.Header() + if block.GasUsed().Cmp(usedGas) != 0 { + return ValidationError(fmt.Sprintf("gas used error (%v / %v)", block.GasUsed(), usedGas)) + } + // Validate the received block's bloom with the one derived from the generated receipts. + // For valid blocks this should always validate to true. + rbloom := types.CreateBloom(receipts) + if rbloom != header.Bloom { + return fmt.Errorf("unable to replicate block's bloom=%x", rbloom) + } + // Tre receipt Trie's root (R = (Tr [[H1, R1], ... [Hn, R1]])) + receiptSha := types.DeriveSha(receipts) + if receiptSha != header.ReceiptHash { + return fmt.Errorf("invalid receipt root hash. received=%x calculated=%x", header.ReceiptHash, receiptSha) + } + // Validate the state root against the received state root and throw + // an error if they don't match. + if root := statedb.IntermediateRoot(); header.Root != root { + return fmt.Errorf("invalid merkle root: header=%x computed=%x", header.Root, root) + } + return nil +} + +// VerifyUncles verifies the given block's uncles and applies the Ethereum +// consensus rules to the various block headers included; it will return an +// error if any of the included uncle headers were invalid. It returns an error +// if the validation failed. +func (v *BlockValidator) VerifyUncles(block, parent *types.Block) error { + // validate that there at most 2 uncles included in this block + if len(block.Uncles()) > 2 { + return ValidationError("Block can only contain maximum 2 uncles (contained %v)", len(block.Uncles())) + } + + uncles := set.New() + ancestors := make(map[common.Hash]*types.Block) + for _, ancestor := range v.bc.GetBlocksFromHash(block.ParentHash(), 7) { + ancestors[ancestor.Hash()] = ancestor + // Include ancestors uncles in the uncle set. Uncles must be unique. + for _, uncle := range ancestor.Uncles() { + uncles.Add(uncle.Hash()) + } + } + ancestors[block.Hash()] = block + uncles.Add(block.Hash()) + + for i, uncle := range block.Uncles() { + hash := uncle.Hash() + if uncles.Has(hash) { + // Error not unique + return UncleError("uncle[%d](%x) not unique", i, hash[:4]) + } + uncles.Add(hash) + + if ancestors[hash] != nil { + branch := fmt.Sprintf(" O - %x\n |\n", block.Hash()) + for h := range ancestors { + branch += fmt.Sprintf(" O - %x\n |\n", h) + } + glog.Infoln(branch) + return UncleError("uncle[%d](%x) is ancestor", i, hash[:4]) + } + + if ancestors[uncle.ParentHash] == nil || uncle.ParentHash == parent.Hash() { + return UncleError("uncle[%d](%x)'s parent is not ancestor (%x)", i, hash[:4], uncle.ParentHash[0:4]) + } + + if err := ValidateHeader(v.Pow, uncle, ancestors[uncle.ParentHash].Header(), true, true); err != nil { + return ValidationError(fmt.Sprintf("uncle[%d](%x) header invalid: %v", i, hash[:4], err)) + } + } + + return nil +} + +// ValidateHeader validates the given header and, depending on the pow arg, +// checks the proof of work of the given header. Returns an error if the +// validation failed. +func (v *BlockValidator) ValidateHeader(header, parent *types.Header, checkPow bool) error { + // Short circuit if the parent is missing. + if parent == nil { + return ParentError(header.ParentHash) + } + // Short circuit if the header's already known or its parent missing + if v.bc.HasHeader(header.Hash()) { + return nil + } + return ValidateHeader(v.Pow, header, parent, checkPow, false) +} + +// Validates a header. Returns an error if the header is invalid. +// +// See YP section 4.3.4. "Block Header Validity" +func ValidateHeader(pow pow.PoW, header *types.Header, parent *types.Header, checkPow, uncle bool) error { + if big.NewInt(int64(len(header.Extra))).Cmp(params.MaximumExtraDataSize) == 1 { + return fmt.Errorf("Header extra data too long (%d)", len(header.Extra)) + } + + if uncle { + if header.Time.Cmp(common.MaxBig) == 1 { + return BlockTSTooBigErr + } + } else { + if header.Time.Cmp(big.NewInt(time.Now().Unix())) == 1 { + return BlockFutureErr + } + } + if header.Time.Cmp(parent.Time) != 1 { + return BlockEqualTSErr + } + + expd := CalcDifficulty(header.Time.Uint64(), parent.Time.Uint64(), parent.Number, parent.Difficulty) + if expd.Cmp(header.Difficulty) != 0 { + return fmt.Errorf("Difficulty check failed for header %v, %v", header.Difficulty, expd) + } + + a := new(big.Int).Set(parent.GasLimit) + a = a.Sub(a, header.GasLimit) + a.Abs(a) + b := new(big.Int).Set(parent.GasLimit) + b = b.Div(b, params.GasLimitBoundDivisor) + if !(a.Cmp(b) < 0) || (header.GasLimit.Cmp(params.MinGasLimit) == -1) { + return fmt.Errorf("GasLimit check failed for header %v (%v > %v)", header.GasLimit, a, b) + } + + num := new(big.Int).Set(parent.Number) + num.Sub(header.Number, num) + if num.Cmp(big.NewInt(1)) != 0 { + return BlockNumberErr + } + + if checkPow { + // Verify the nonce of the header. Return an error if it's not valid + if !pow.Verify(types.NewBlockWithHeader(header)) { + return &BlockNonceErr{header.Number, header.Hash(), header.Nonce.Uint64()} + } + } + return nil +} diff --git a/core/block_validator_test.go b/core/block_validator_test.go new file mode 100644 index 000000000..a0694f067 --- /dev/null +++ b/core/block_validator_test.go @@ -0,0 +1,89 @@ +// Copyright 2015 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 core + +import ( + "fmt" + "math/big" + "testing" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/state" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/core/vm" + "github.com/ethereum/go-ethereum/ethdb" + "github.com/ethereum/go-ethereum/event" + "github.com/ethereum/go-ethereum/pow/ezp" +) + +func proc() (Validator, *BlockChain) { + db, _ := ethdb.NewMemDatabase() + var mux event.TypeMux + + WriteTestNetGenesisBlock(db, 0) + blockchain, err := NewBlockChain(db, thePow(), &mux) + if err != nil { + fmt.Println(err) + } + return blockchain.validator, blockchain +} + +func TestNumber(t *testing.T) { + pow := ezp.New() + _, chain := proc() + + 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) + if err != BlockNumberErr { + t.Errorf("expected block number error, got %q", err) + } + + header = makeHeader(chain.Genesis(), statedb) + err = ValidateHeader(pow, header, chain.Genesis().Header(), false, false) + if err == BlockNumberErr { + t.Errorf("didn't expect block number error") + } +} + +func TestPutReceipt(t *testing.T) { + db, _ := ethdb.NewMemDatabase() + + var addr common.Address + addr[0] = 1 + var hash common.Hash + hash[0] = 2 + + receipt := new(types.Receipt) + receipt.Logs = vm.Logs{&vm.Log{ + Address: addr, + Topics: []common.Hash{hash}, + Data: []byte("hi"), + BlockNumber: 42, + TxHash: hash, + TxIndex: 0, + BlockHash: hash, + Index: 0, + }} + + PutReceipts(db, types.Receipts{receipt}) + receipt = GetReceipt(db, common.Hash{}) + if receipt == nil { + t.Error("expected to get 1 receipt, got none.") + } +} diff --git a/core/blockchain.go b/core/blockchain.go index cea346e38..b6b00ca04 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -33,6 +33,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/event" @@ -61,17 +62,34 @@ const ( blockCacheLimit = 256 maxFutureBlocks = 256 maxTimeFutureBlocks = 30 + // must be bumped when consensus algorithm is changed, this forces the upgradedb + // command to be run (forces the blocks to be imported again using the new algorithm) + BlockChainVersion = 3 ) +// BlockChain represents the canonical chain given a database with a genesis +// block. The Blockchain manages chain imports, reverts, chain reorganisations. +// +// Importing blocks in to the block chain happens according to the set of rules +// defined by the two stage Validator. Processing of blocks is done using the +// Processor which processes the included transaction. The validation of the state +// is done in the second part of the Validator. Failing results in aborting of +// the import. +// +// The BlockChain also helps in returning blocks from **any** chain included +// in the database as well as blocks that represents the canonical chain. It's +// important to note that GetBlock can return any block and does not need to be +// included in the canonical one where as GetBlockByNumber always represents the +// canonical chain. type BlockChain struct { chainDb ethdb.Database - processor types.BlockProcessor eventMux *event.TypeMux genesisBlock *types.Block // Last known total difficulty mu sync.RWMutex chainmu sync.RWMutex tsmu sync.RWMutex + procmu sync.RWMutex checkpoint int // checkpoint counts towards the new checkpoint currentHeader *types.Header // Current head of the header chain (may be above the block chain!) @@ -91,10 +109,15 @@ type BlockChain struct { procInterrupt int32 // interrupt signaler for block processing wg sync.WaitGroup - pow pow.PoW - rand *mrand.Rand + pow pow.PoW + rand *mrand.Rand + processor Processor + validator Validator } +// NewBlockChain returns a fully initialised block chain using information +// available in the database. It initialiser the default Ethereum Validator and +// Processor. func NewBlockChain(chainDb ethdb.Database, pow pow.PoW, mux *event.TypeMux) (*BlockChain, error) { headerCache, _ := lru.New(headerCacheLimit) bodyCache, _ := lru.New(bodyCacheLimit) @@ -121,6 +144,8 @@ func NewBlockChain(chainDb ethdb.Database, pow pow.PoW, mux *event.TypeMux) (*Bl return nil, err } bc.rand = mrand.New(mrand.NewSource(seed.Int64())) + bc.SetValidator(NewBlockValidator(bc, pow)) + bc.SetProcessor(NewStateProcessor(bc)) bc.genesisBlock = bc.GetBlockByNumber(0) if bc.genesisBlock == nil { @@ -292,6 +317,7 @@ func (self *BlockChain) FastSyncCommitHead(hash common.Hash) error { return nil } +// GasLimit returns the gas limit of the current HEAD block. func (self *BlockChain) GasLimit() *big.Int { self.mu.RLock() defer self.mu.RUnlock() @@ -299,6 +325,7 @@ func (self *BlockChain) GasLimit() *big.Int { return self.currentBlock.GasLimit() } +// LastBlockHash return the hash of the HEAD block. func (self *BlockChain) LastBlockHash() common.Hash { self.mu.RLock() defer self.mu.RUnlock() @@ -333,6 +360,8 @@ func (self *BlockChain) CurrentFastBlock() *types.Block { return self.currentFastBlock } +// Status returns status information about the current chain such as the HEAD Td, +// the HEAD hash and the hash of the genesis block. func (self *BlockChain) Status() (td *big.Int, currentBlock common.Hash, genesisBlock common.Hash) { self.mu.RLock() defer self.mu.RUnlock() @@ -340,10 +369,38 @@ func (self *BlockChain) Status() (td *big.Int, currentBlock common.Hash, genesis return self.GetTd(self.currentBlock.Hash()), self.currentBlock.Hash(), self.genesisBlock.Hash() } -func (self *BlockChain) SetProcessor(proc types.BlockProcessor) { - self.processor = proc +// SetProcessor sets the processor required for making state modifications. +func (self *BlockChain) SetProcessor(processor Processor) { + self.procmu.Lock() + defer self.procmu.Unlock() + self.processor = processor +} + +// SetValidator sets the validator which is used to validate incoming blocks. +func (self *BlockChain) SetValidator(validator Validator) { + self.procmu.Lock() + defer self.procmu.Unlock() + self.validator = validator } +// Validator returns the current validator. +func (self *BlockChain) Validator() Validator { + self.procmu.RLock() + defer self.procmu.RUnlock() + return self.validator +} + +// Processor returns the current processor. +func (self *BlockChain) Processor() Processor { + self.procmu.RLock() + defer self.procmu.RUnlock() + return self.processor +} + +// AuxValidator returns the auxiliary validator (Proof of work atm) +func (self *BlockChain) AuxValidator() pow.PoW { return self.pow } + +// State returns a new mutable state based on the current HEAD block. func (self *BlockChain) State() (*state.StateDB, error) { return state.New(self.CurrentBlock().Root(), self.chainDb) } @@ -606,6 +663,8 @@ func (self *BlockChain) GetUnclesInChain(block *types.Block, length int) []*type return uncles } +// Stop stops the blockchain service. If any imports are currently in progress +// it will abort them using the procInterrupt. func (bc *BlockChain) Stop() { if !atomic.CompareAndSwapInt32(&bc.running, 0, 1) { return @@ -758,9 +817,9 @@ func (self *BlockChain) InsertHeaderChain(chain []*types.Header, checkFreq int) var err error if index == 0 { - err = self.processor.ValidateHeader(header, checkPow, false) + err = self.Validator().ValidateHeader(header, self.GetHeader(header.ParentHash), checkPow) } else { - err = self.processor.ValidateHeaderWithParent(header, chain[index-1], checkPow, false) + err = self.Validator().ValidateHeader(header, chain[index-1], checkPow) } if err != nil { errs[index] = err @@ -1025,9 +1084,10 @@ func (self *BlockChain) InsertChain(chain types.Blocks) (int, error) { // faster than direct delivery and requires much less mutex // acquiring. var ( - stats struct{ queued, processed, ignored int } - events = make([]interface{}, 0, len(chain)) - tstart = time.Now() + stats struct{ queued, processed, ignored int } + events = make([]interface{}, 0, len(chain)) + coalescedLogs vm.Logs + tstart = time.Now() nonceChecked = make([]bool, len(chain)) ) @@ -1057,12 +1117,12 @@ func (self *BlockChain) InsertChain(chain types.Blocks) (int, error) { if BadHashes[block.Hash()] { err := BadHashError(block.Hash()) - blockErr(block, err) + reportBlock(block, err) return i, err } - // Call in to the block processor and check for errors. It's likely that if one block fails - // all others will fail too (unless a known block is returned). - logs, receipts, err := self.processor.Process(block) + // Stage 1 validation of the block using the chain's validator + // interface. + err := self.Validator().ValidateBlock(block) if err != nil { if IsKnownBlockErr(err) { stats.ignored++ @@ -1089,14 +1149,41 @@ func (self *BlockChain) InsertChain(chain types.Blocks) (int, error) { continue } - blockErr(block, err) + reportBlock(block, err) - go ReportBlock(block, err) + return i, err + } + // Create a new statedb using the parent block and report an + // error if it fails. + statedb, err := state.New(self.GetBlock(block.ParentHash()).Root(), self.chainDb) + if err != nil { + reportBlock(block, err) return i, err } + // Process block using the parent state as reference point. + receipts, logs, usedGas, err := self.processor.Process(block, statedb) + if err != nil { + reportBlock(block, err) + return i, err + } + // Validate the state using the default validator + err = self.Validator().ValidateState(block, self.GetBlock(block.ParentHash()), statedb, receipts, usedGas) + if err != nil { + reportBlock(block, err) + return i, err + } + // Write state changes to database + _, err = statedb.Commit() + if err != nil { + return i, err + } + + // coalesce logs for later processing + coalescedLogs = append(coalescedLogs, logs...) + if err := PutBlockReceipts(self.chainDb, block.Hash(), receipts); err != nil { - glog.V(logger.Warn).Infoln("error writing block receipts:", err) + return i, err } txcount += len(block.Transactions()) @@ -1105,6 +1192,7 @@ func (self *BlockChain) InsertChain(chain types.Blocks) (int, error) { if err != nil { return i, err } + switch status { case CanonStatTy: if glog.V(logger.Debug) { @@ -1141,7 +1229,7 @@ func (self *BlockChain) InsertChain(chain types.Blocks) (int, error) { start, end := chain[0], chain[len(chain)-1] glog.Infof("imported %d block(s) (%d queued %d ignored) including %d txs in %v. #%v [%x / %x]\n", stats.processed, stats.queued, stats.ignored, txcount, tend, end.Number(), start.Hash().Bytes()[:4], end.Hash().Bytes()[:4]) } - go self.postChainEvents(events) + go self.postChainEvents(events, coalescedLogs) return 0, nil } @@ -1239,7 +1327,9 @@ func (self *BlockChain) reorg(oldBlock, newBlock *types.Block) error { // postChainEvents iterates over the events generated by a chain insertion and // posts them into the event mux. -func (self *BlockChain) postChainEvents(events []interface{}) { +func (self *BlockChain) postChainEvents(events []interface{}, logs vm.Logs) { + // post event logs for further processing + self.eventMux.Post(logs) for _, event := range events { if event, ok := event.(ChainEvent); ok { // We need some control over the mining operation. Acquiring locks and waiting for the miner to create new block takes too long @@ -1265,9 +1355,13 @@ func (self *BlockChain) update() { } } -func blockErr(block *types.Block, err error) { +// reportBlock reports the given block and error using the canonical block +// reporting tool. Reporting the block to the service is handled in a separate +// goroutine. +func reportBlock(block *types.Block, err error) { if glog.V(logger.Error) { glog.Errorf("Bad block #%v (%s)\n", block.Number(), block.Hash().Hex()) glog.Errorf(" %v", err) } + go ReportBlock(block, err) } diff --git a/core/blockchain_test.go b/core/blockchain_test.go index 8ddc5032b..e5ed66377 100644 --- a/core/blockchain_test.go +++ b/core/blockchain_test.go @@ -28,6 +28,7 @@ import ( "github.com/ethereum/ethash" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/crypto" @@ -53,31 +54,29 @@ func theBlockChain(db ethdb.Database, t *testing.T) *BlockChain { WriteTestNetGenesisBlock(db, 0) blockchain, err := NewBlockChain(db, thePow(), &eventMux) if err != nil { - t.Error("failed creating chainmanager:", err) + t.Error("failed creating blockchain:", err) t.FailNow() return nil } - blockMan := NewBlockProcessor(db, nil, blockchain, &eventMux) - blockchain.SetProcessor(blockMan) return blockchain } // Test fork of length N starting from block i -func testFork(t *testing.T, processor *BlockProcessor, i, n int, full bool, comparator func(td1, td2 *big.Int)) { +func testFork(t *testing.T, blockchain *BlockChain, i, n int, full bool, comparator func(td1, td2 *big.Int)) { // Copy old chain up to #i into a new db - db, processor2, err := newCanonical(i, full) + db, blockchain2, err := newCanonical(i, full) if err != nil { t.Fatal("could not make new canonical in testFork", err) } // Assert the chains have the same header/block at #i var hash1, hash2 common.Hash if full { - hash1 = processor.bc.GetBlockByNumber(uint64(i)).Hash() - hash2 = processor2.bc.GetBlockByNumber(uint64(i)).Hash() + hash1 = blockchain.GetBlockByNumber(uint64(i)).Hash() + hash2 = blockchain2.GetBlockByNumber(uint64(i)).Hash() } else { - hash1 = processor.bc.GetHeaderByNumber(uint64(i)).Hash() - hash2 = processor2.bc.GetHeaderByNumber(uint64(i)).Hash() + hash1 = blockchain.GetHeaderByNumber(uint64(i)).Hash() + hash2 = blockchain2.GetHeaderByNumber(uint64(i)).Hash() } if hash1 != hash2 { t.Errorf("chain content mismatch at %d: have hash %v, want hash %v", i, hash2, hash1) @@ -88,13 +87,13 @@ func testFork(t *testing.T, processor *BlockProcessor, i, n int, full bool, comp headerChainB []*types.Header ) if full { - blockChainB = makeBlockChain(processor2.bc.CurrentBlock(), n, db, forkSeed) - if _, err := processor2.bc.InsertChain(blockChainB); err != nil { + blockChainB = makeBlockChain(blockchain2.CurrentBlock(), n, db, forkSeed) + if _, err := blockchain2.InsertChain(blockChainB); err != nil { t.Fatalf("failed to insert forking chain: %v", err) } } else { - headerChainB = makeHeaderChain(processor2.bc.CurrentHeader(), n, db, forkSeed) - if _, err := processor2.bc.InsertHeaderChain(headerChainB, 1); err != nil { + headerChainB = makeHeaderChain(blockchain2.CurrentHeader(), n, db, forkSeed) + if _, err := blockchain2.InsertHeaderChain(headerChainB, 1); err != nil { t.Fatalf("failed to insert forking chain: %v", err) } } @@ -102,17 +101,17 @@ func testFork(t *testing.T, processor *BlockProcessor, i, n int, full bool, comp var tdPre, tdPost *big.Int if full { - tdPre = processor.bc.GetTd(processor.bc.CurrentBlock().Hash()) - if err := testBlockChainImport(blockChainB, processor); err != nil { + tdPre = blockchain.GetTd(blockchain.CurrentBlock().Hash()) + if err := testBlockChainImport(blockChainB, blockchain); err != nil { t.Fatalf("failed to import forked block chain: %v", err) } - tdPost = processor.bc.GetTd(blockChainB[len(blockChainB)-1].Hash()) + tdPost = blockchain.GetTd(blockChainB[len(blockChainB)-1].Hash()) } else { - tdPre = processor.bc.GetTd(processor.bc.CurrentHeader().Hash()) - if err := testHeaderChainImport(headerChainB, processor); err != nil { + tdPre = blockchain.GetTd(blockchain.CurrentHeader().Hash()) + if err := testHeaderChainImport(headerChainB, blockchain); err != nil { t.Fatalf("failed to import forked header chain: %v", err) } - tdPost = processor.bc.GetTd(headerChainB[len(headerChainB)-1].Hash()) + tdPost = blockchain.GetTd(headerChainB[len(headerChainB)-1].Hash()) } // Compare the total difficulties of the chains comparator(tdPre, tdPost) @@ -127,37 +126,52 @@ func printChain(bc *BlockChain) { // testBlockChainImport tries to process a chain of blocks, writing them into // the database if successful. -func testBlockChainImport(chain []*types.Block, processor *BlockProcessor) error { +func testBlockChainImport(chain types.Blocks, blockchain *BlockChain) error { for _, block := range chain { // Try and process the block - if _, _, err := processor.Process(block); err != nil { + err := blockchain.Validator().ValidateBlock(block) + if err != nil { if IsKnownBlockErr(err) { continue } return err } - // Manually insert the block into the database, but don't reorganize (allows subsequent testing) - processor.bc.mu.Lock() - WriteTd(processor.chainDb, block.Hash(), new(big.Int).Add(block.Difficulty(), processor.bc.GetTd(block.ParentHash()))) - WriteBlock(processor.chainDb, block) - processor.bc.mu.Unlock() + statedb, err := state.New(blockchain.GetBlock(block.ParentHash()).Root(), blockchain.chainDb) + if err != nil { + return err + } + receipts, _, usedGas, err := blockchain.Processor().Process(block, statedb) + if err != nil { + reportBlock(block, err) + return err + } + err = blockchain.Validator().ValidateState(block, blockchain.GetBlock(block.ParentHash()), statedb, receipts, usedGas) + if err != nil { + reportBlock(block, err) + return err + } + blockchain.mu.Lock() + WriteTd(blockchain.chainDb, block.Hash(), new(big.Int).Add(block.Difficulty(), blockchain.GetTd(block.ParentHash()))) + WriteBlock(blockchain.chainDb, block) + statedb.Commit() + blockchain.mu.Unlock() } return nil } // testHeaderChainImport tries to process a chain of header, writing them into // the database if successful. -func testHeaderChainImport(chain []*types.Header, processor *BlockProcessor) error { +func testHeaderChainImport(chain []*types.Header, blockchain *BlockChain) error { for _, header := range chain { // Try and validate the header - if err := processor.ValidateHeader(header, false, false); err != nil { + if err := blockchain.Validator().ValidateHeader(header, blockchain.GetHeader(header.ParentHash), false); err != nil { return err } // Manually insert the header into the database, but don't reorganize (allows subsequent testing) - processor.bc.mu.Lock() - WriteTd(processor.chainDb, header.Hash(), new(big.Int).Add(header.Difficulty, processor.bc.GetTd(header.ParentHash))) - WriteHeader(processor.chainDb, header) - processor.bc.mu.Unlock() + blockchain.mu.Lock() + WriteTd(blockchain.chainDb, header.Hash(), new(big.Int).Add(header.Difficulty, blockchain.GetTd(header.ParentHash))) + WriteHeader(blockchain.chainDb, header) + blockchain.mu.Unlock() } return nil } @@ -313,19 +327,19 @@ func TestBrokenBlockChain(t *testing.T) { testBrokenChain(t, true) } func testBrokenChain(t *testing.T, full bool) { // Make chain starting from genesis - db, processor, err := newCanonical(10, full) + db, blockchain, err := newCanonical(10, full) if err != nil { t.Fatalf("failed to make new canonical chain: %v", err) } // Create a forked chain, and try to insert with a missing link if full { - chain := makeBlockChain(processor.bc.CurrentBlock(), 5, db, forkSeed)[1:] - if err := testBlockChainImport(chain, processor); err == nil { + chain := makeBlockChain(blockchain.CurrentBlock(), 5, db, forkSeed)[1:] + if err := testBlockChainImport(chain, blockchain); err == nil { t.Errorf("broken block chain not reported") } } else { - chain := makeHeaderChain(processor.bc.CurrentHeader(), 5, db, forkSeed)[1:] - if err := testHeaderChainImport(chain, processor); err == nil { + chain := makeHeaderChain(blockchain.CurrentHeader(), 5, db, forkSeed)[1:] + if err := testHeaderChainImport(chain, blockchain); err == nil { t.Errorf("broken header chain not reported") } } @@ -415,9 +429,14 @@ func TestChainMultipleInsertions(t *testing.T) { type bproc struct{} -func (bproc) Process(*types.Block) (vm.Logs, types.Receipts, error) { return nil, nil, nil } -func (bproc) ValidateHeader(*types.Header, bool, bool) error { return nil } -func (bproc) ValidateHeaderWithParent(*types.Header, *types.Header, bool, bool) error { return nil } +func (bproc) ValidateBlock(*types.Block) error { return nil } +func (bproc) ValidateHeader(*types.Header, *types.Header, bool) error { return nil } +func (bproc) ValidateState(block, parent *types.Block, state *state.StateDB, receipts types.Receipts, usedGas *big.Int) error { + return nil +} +func (bproc) Process(block *types.Block, statedb *state.StateDB) (types.Receipts, vm.Logs, *big.Int, error) { + return nil, nil, nil, nil +} func makeHeaderChainWithDiff(genesis *types.Block, d []int, seed byte) []*types.Header { blocks := makeBlockChainWithDiff(genesis, d, seed) @@ -459,7 +478,8 @@ func chm(genesis *types.Block, db ethdb.Database) *BlockChain { bc.tdCache, _ = lru.New(100) bc.blockCache, _ = lru.New(100) bc.futureBlocks, _ = lru.New(100) - bc.processor = bproc{} + bc.SetValidator(bproc{}) + bc.SetProcessor(bproc{}) bc.ResetWithGenesisBlock(genesis) return bc @@ -612,12 +632,10 @@ func TestBlocksInsertNonceError(t *testing.T) { testInsertNonceError(t, true) } func testInsertNonceError(t *testing.T, full bool) { for i := 1; i < 25 && !t.Failed(); i++ { // Create a pristine chain and database - db, processor, err := newCanonical(0, full) + db, blockchain, err := newCanonical(0, full) if err != nil { t.Fatalf("failed to create pristine chain: %v", err) } - bc := processor.bc - // Create and insert a chain with a failing nonce var ( failAt int @@ -626,34 +644,33 @@ func testInsertNonceError(t *testing.T, full bool) { failHash common.Hash ) if full { - blocks := makeBlockChain(processor.bc.CurrentBlock(), i, db, 0) + blocks := makeBlockChain(blockchain.CurrentBlock(), i, db, 0) failAt = rand.Int() % len(blocks) failNum = blocks[failAt].NumberU64() failHash = blocks[failAt].Hash() - processor.bc.pow = failPow{failNum} - processor.Pow = failPow{failNum} + blockchain.pow = failPow{failNum} - failRes, err = processor.bc.InsertChain(blocks) + failRes, err = blockchain.InsertChain(blocks) } else { - headers := makeHeaderChain(processor.bc.CurrentHeader(), i, db, 0) + headers := makeHeaderChain(blockchain.CurrentHeader(), i, db, 0) failAt = rand.Int() % len(headers) failNum = headers[failAt].Number.Uint64() failHash = headers[failAt].Hash() - processor.bc.pow = failPow{failNum} - processor.Pow = failPow{failNum} + blockchain.pow = failPow{failNum} + blockchain.validator = NewBlockValidator(blockchain, failPow{failNum}) - failRes, err = processor.bc.InsertHeaderChain(headers, 1) + failRes, err = blockchain.InsertHeaderChain(headers, 1) } // Check that the returned error indicates the nonce failure. if failRes != failAt { t.Errorf("test %d: failure index mismatch: have %d, want %d", i, failRes, failAt) } if !IsBlockNonceErr(err) { - t.Fatalf("test %d: error mismatch: have %v, want nonce error", i, err) + t.Fatalf("test %d: error mismatch: have %v, want nonce error %T", i, err, err) } nerr := err.(*BlockNonceErr) if nerr.Number.Uint64() != failNum { @@ -665,11 +682,11 @@ func testInsertNonceError(t *testing.T, full bool) { // Check that all no blocks after the failing block have been inserted. for j := 0; j < i-failAt; j++ { if full { - if block := bc.GetBlockByNumber(failNum + uint64(j)); block != nil { + if block := blockchain.GetBlockByNumber(failNum + uint64(j)); block != nil { t.Errorf("test %d: invalid block in chain: %v", i, block) } } else { - if header := bc.GetHeaderByNumber(failNum + uint64(j)); header != nil { + if header := blockchain.GetHeaderByNumber(failNum + uint64(j)); header != nil { t.Errorf("test %d: invalid header in chain: %v", i, header) } } @@ -711,7 +728,6 @@ func TestFastVsFullChains(t *testing.T) { WriteGenesisBlockForTesting(archiveDb, GenesisAccount{address, funds}) archive, _ := NewBlockChain(archiveDb, FakePow{}, new(event.TypeMux)) - archive.SetProcessor(NewBlockProcessor(archiveDb, FakePow{}, archive, new(event.TypeMux))) if n, err := archive.InsertChain(blocks); err != nil { t.Fatalf("failed to process block %d: %v", n, err) @@ -720,7 +736,6 @@ func TestFastVsFullChains(t *testing.T) { fastDb, _ := ethdb.NewMemDatabase() WriteGenesisBlockForTesting(fastDb, GenesisAccount{address, funds}) fast, _ := NewBlockChain(fastDb, FakePow{}, new(event.TypeMux)) - fast.SetProcessor(NewBlockProcessor(fastDb, FakePow{}, fast, new(event.TypeMux))) headers := make([]*types.Header, len(blocks)) for i, block := range blocks { @@ -797,7 +812,6 @@ func TestLightVsFastVsFullChainHeads(t *testing.T) { WriteGenesisBlockForTesting(archiveDb, GenesisAccount{address, funds}) archive, _ := NewBlockChain(archiveDb, FakePow{}, new(event.TypeMux)) - archive.SetProcessor(NewBlockProcessor(archiveDb, FakePow{}, archive, new(event.TypeMux))) if n, err := archive.InsertChain(blocks); err != nil { t.Fatalf("failed to process block %d: %v", n, err) @@ -810,7 +824,6 @@ func TestLightVsFastVsFullChainHeads(t *testing.T) { fastDb, _ := ethdb.NewMemDatabase() WriteGenesisBlockForTesting(fastDb, GenesisAccount{address, funds}) fast, _ := NewBlockChain(fastDb, FakePow{}, new(event.TypeMux)) - fast.SetProcessor(NewBlockProcessor(fastDb, FakePow{}, fast, new(event.TypeMux))) headers := make([]*types.Header, len(blocks)) for i, block := range blocks { @@ -830,7 +843,6 @@ func TestLightVsFastVsFullChainHeads(t *testing.T) { lightDb, _ := ethdb.NewMemDatabase() WriteGenesisBlockForTesting(lightDb, GenesisAccount{address, funds}) light, _ := NewBlockChain(lightDb, FakePow{}, new(event.TypeMux)) - light.SetProcessor(NewBlockProcessor(lightDb, FakePow{}, light, new(event.TypeMux))) if n, err := light.InsertHeaderChain(headers, 1); err != nil { t.Fatalf("failed to insert header %d: %v", n, err) @@ -895,9 +907,8 @@ func TestChainTxReorgs(t *testing.T) { }) // Import the chain. This runs all block validation rules. evmux := &event.TypeMux{} - chainman, _ := NewBlockChain(db, FakePow{}, evmux) - chainman.SetProcessor(NewBlockProcessor(db, FakePow{}, chainman, evmux)) - if i, err := chainman.InsertChain(chain); err != nil { + blockchain, _ := NewBlockChain(db, FakePow{}, evmux) + if i, err := blockchain.InsertChain(chain); err != nil { t.Fatalf("failed to insert original chain[%d]: %v", i, err) } @@ -920,7 +931,7 @@ func TestChainTxReorgs(t *testing.T) { gen.AddTx(futureAdd) // This transaction will be added after a full reorg } }) - if _, err := chainman.InsertChain(chain); err != nil { + if _, err := blockchain.InsertChain(chain); err != nil { t.Fatalf("failed to insert forked chain: %v", err) } diff --git a/core/chain_makers.go b/core/chain_makers.go index 56e37a0fc..f1ada487f 100644 --- a/core/chain_makers.go +++ b/core/chain_makers.go @@ -214,7 +214,7 @@ func makeHeader(parent *types.Block, state *state.StateDB) *types.Header { // newCanonical creates a chain database, and injects a deterministic canonical // chain. Depending on the full flag, if creates either a full block chain or a // header only chain. -func newCanonical(n int, full bool) (ethdb.Database, *BlockProcessor, error) { +func newCanonical(n int, full bool) (ethdb.Database, *BlockChain, error) { // Create te new chain database db, _ := ethdb.NewMemDatabase() evmux := &event.TypeMux{} @@ -223,23 +223,20 @@ func newCanonical(n int, full bool) (ethdb.Database, *BlockProcessor, error) { genesis, _ := WriteTestNetGenesisBlock(db, 0) blockchain, _ := NewBlockChain(db, FakePow{}, evmux) - processor := NewBlockProcessor(db, FakePow{}, blockchain, evmux) - processor.bc.SetProcessor(processor) - // Create and inject the requested chain if n == 0 { - return db, processor, nil + return db, blockchain, nil } if full { // Full block-chain requested blocks := makeBlockChain(genesis, n, db, canonicalSeed) _, err := blockchain.InsertChain(blocks) - return db, processor, err + return db, blockchain, err } // Header-only chain requested headers := makeHeaderChain(genesis.Header(), n, db, canonicalSeed) _, err := blockchain.InsertHeaderChain(headers, 1) - return db, processor, err + return db, blockchain, err } // makeHeaderChain creates a deterministic chain of headers rooted at parent. diff --git a/core/chain_makers_test.go b/core/chain_makers_test.go index 7f47cf288..b9c1d89b7 100644 --- a/core/chain_makers_test.go +++ b/core/chain_makers_test.go @@ -77,15 +77,14 @@ func ExampleGenerateChain() { // Import the chain. This runs all block validation rules. evmux := &event.TypeMux{} - chainman, _ := NewBlockChain(db, FakePow{}, evmux) - chainman.SetProcessor(NewBlockProcessor(db, FakePow{}, chainman, evmux)) - if i, err := chainman.InsertChain(chain); err != nil { + blockchain, _ := NewBlockChain(db, FakePow{}, evmux) + if i, err := blockchain.InsertChain(chain); err != nil { fmt.Printf("insert error (block %d): %v\n", i, err) return } - state, _ := chainman.State() - fmt.Printf("last block: #%d\n", chainman.CurrentBlock().Number()) + state, _ := blockchain.State() + fmt.Printf("last block: #%d\n", blockchain.CurrentBlock().Number()) fmt.Println("balance of addr1:", state.GetBalance(addr1)) fmt.Println("balance of addr2:", state.GetBalance(addr2)) fmt.Println("balance of addr3:", state.GetBalance(addr3)) diff --git a/core/gaspool.go b/core/gaspool.go new file mode 100644 index 000000000..2ef07c754 --- /dev/null +++ b/core/gaspool.go @@ -0,0 +1,46 @@ +// Copyright 2014 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 core + +import "math/big" + +// GasPool tracks the amount of gas available during +// execution of the transactions in a block. +// The zero value is a pool with zero gas available. +type GasPool big.Int + +// AddGas makes gas available for execution. +func (gp *GasPool) AddGas(amount *big.Int) *GasPool { + i := (*big.Int)(gp) + i.Add(i, amount) + return gp +} + +// SubGas deducts the given amount from the pool if enough gas is +// available and returns an error otherwise. +func (gp *GasPool) SubGas(amount *big.Int) error { + i := (*big.Int)(gp) + if i.Cmp(amount) < 0 { + return &GasLimitErr{Have: new(big.Int).Set(i), Want: amount} + } + i.Sub(i, amount) + return nil +} + +func (gp *GasPool) String() string { + return (*big.Int)(gp).String() +} diff --git a/core/manager.go b/core/manager.go deleted file mode 100644 index 289c87c11..000000000 --- a/core/manager.go +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright 2015 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 core - -import ( - "github.com/ethereum/go-ethereum/accounts" - "github.com/ethereum/go-ethereum/ethdb" - "github.com/ethereum/go-ethereum/event" -) - -// TODO move this to types? -type Backend interface { - AccountManager() *accounts.Manager - BlockProcessor() *BlockProcessor - BlockChain() *BlockChain - TxPool() *TxPool - ChainDb() ethdb.Database - DappDb() ethdb.Database - EventMux() *event.TypeMux -} diff --git a/core/state_processor.go b/core/state_processor.go new file mode 100644 index 000000000..d9c24935d --- /dev/null +++ b/core/state_processor.go @@ -0,0 +1,107 @@ +package core + +import ( + "math/big" + + "github.com/ethereum/go-ethereum/core/state" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/core/vm" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/logger" + "github.com/ethereum/go-ethereum/logger/glog" +) + +var ( + big8 = big.NewInt(8) + big32 = big.NewInt(32) +) + +type StateProcessor struct { + bc *BlockChain +} + +func NewStateProcessor(bc *BlockChain) *StateProcessor { + return &StateProcessor{bc} +} + +// Process processes the state changes according to the Ethereum rules by running +// the transaction messages using the statedb and applying any rewards to both +// the processor (coinbase) and any included uncles. +// +// Process returns the receipts and logs accumulated during the process and +// returns the amount of gas that was used in the process. If any of the +// transactions failed to execute due to insufficient gas it will return an error. +func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB) (types.Receipts, vm.Logs, *big.Int, error) { + var ( + receipts types.Receipts + totalUsedGas = big.NewInt(0) + err error + header = block.Header() + allLogs vm.Logs + gp = new(GasPool).AddGas(block.GasLimit()) + ) + + for i, tx := range block.Transactions() { + statedb.StartRecord(tx.Hash(), block.Hash(), i) + + receipt, logs, _, err := ApplyTransaction(p.bc, gp, statedb, header, tx, totalUsedGas) + if err != nil { + return nil, nil, totalUsedGas, err + } + receipts = append(receipts, receipt) + allLogs = append(allLogs, logs...) + } + AccumulateRewards(statedb, header, block.Uncles()) + + return receipts, allLogs, totalUsedGas, err +} + +// ApplyTransaction attemps to apply a transaction to the given state database +// and uses the input parameters for its environment. +// +// ApplyTransactions returns the generated receipts and vm logs during the +// execution of the state transition phase. +func ApplyTransaction(bc *BlockChain, gp *GasPool, statedb *state.StateDB, header *types.Header, tx *types.Transaction, usedGas *big.Int) (*types.Receipt, vm.Logs, *big.Int, error) { + _, gas, err := ApplyMessage(NewEnv(statedb, bc, tx, header), tx, gp) + if err != nil { + return nil, nil, nil, err + } + + // Update the state with pending changes + usedGas.Add(usedGas, gas) + receipt := types.NewReceipt(statedb.IntermediateRoot().Bytes(), usedGas) + receipt.TxHash = tx.Hash() + receipt.GasUsed = new(big.Int).Set(gas) + if MessageCreatesContract(tx) { + from, _ := tx.From() + receipt.ContractAddress = crypto.CreateAddress(from, tx.Nonce()) + } + + logs := statedb.GetLogs(tx.Hash()) + receipt.Logs = logs + receipt.Bloom = types.CreateBloom(types.Receipts{receipt}) + + glog.V(logger.Debug).Infoln(receipt) + + return receipt, logs, gas, err +} + +// AccumulateRewards credits the coinbase of the given block with the +// mining reward. The total reward consists of the static block reward +// and rewards for included uncles. The coinbase of each uncle block is +// also rewarded. +func AccumulateRewards(statedb *state.StateDB, header *types.Header, uncles []*types.Header) { + reward := new(big.Int).Set(BlockReward) + r := new(big.Int) + for _, uncle := range uncles { + r.Add(uncle.Number, big8) + r.Sub(r, header.Number) + r.Mul(r, BlockReward) + r.Div(r, big8) + statedb.AddBalance(uncle.Coinbase, r) + + r.Div(BlockReward, big32) + reward.Add(reward, r) + } + statedb.AddBalance(header.Coinbase, reward) +} diff --git a/core/types.go b/core/types.go new file mode 100644 index 000000000..027f628b1 --- /dev/null +++ b/core/types.go @@ -0,0 +1,70 @@ +// Copyright 2014 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 core + +import ( + "math/big" + + "github.com/ethereum/go-ethereum/accounts" + "github.com/ethereum/go-ethereum/core/state" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/core/vm" + "github.com/ethereum/go-ethereum/ethdb" + "github.com/ethereum/go-ethereum/event" +) + +// Validator is an interface which defines the standard for block validation. +// +// The validator is responsible for validating incoming block or, if desired, +// validates headers for fast validation. +// +// ValidateBlock validates the given block and should return an error if it +// failed to do so and should be used for "full" validation. +// +// ValidateHeader validates the given header and parent and returns an error +// if it failed to do so. +// +// ValidateStack validates the given statedb and optionally the receipts and +// gas used. The implementor should decide what to do with the given input. +type Validator interface { + ValidateBlock(block *types.Block) error + ValidateHeader(header, parent *types.Header, checkPow bool) error + ValidateState(block, parent *types.Block, state *state.StateDB, receipts types.Receipts, usedGas *big.Int) error +} + +// Processor is an interface for processing blocks using a given initial state. +// +// Process takes the block to be processed and the statedb upon which the +// initial state is based. It should return the receipts generated, amount +// of gas used in the process and return an error if any of the internal rules +// failed. +type Processor interface { + Process(block *types.Block, statedb *state.StateDB) (types.Receipts, vm.Logs, *big.Int, error) +} + +// Backend is an interface defining the basic functionality for an operable node +// with all the functionality to be a functional, valid Ethereum operator. +// +// TODO Remove this +type Backend interface { + AccountManager() *accounts.Manager + BlockChain() *BlockChain + TxPool() *TxPool + ChainDb() ethdb.Database + DappDb() ethdb.Database + EventMux() *event.TypeMux +} diff --git a/core/types/common.go b/core/types/common.go deleted file mode 100644 index fe682f98a..000000000 --- a/core/types/common.go +++ /dev/null @@ -1,25 +0,0 @@ -// Copyright 2014 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 types - -import "github.com/ethereum/go-ethereum/core/vm" - -type BlockProcessor interface { - Process(*Block) (vm.Logs, Receipts, error) - ValidateHeader(*Header, bool, bool) error - ValidateHeaderWithParent(*Header, *Header, bool, bool) error -} diff --git a/eth/backend.go b/eth/backend.go index 761a17a8f..5bd6ac55d 100644 --- a/eth/backend.go +++ b/eth/backend.go @@ -231,9 +231,7 @@ type Ethereum struct { chainDb ethdb.Database // Block chain database dappDb ethdb.Database // Dapp database - //*** SERVICES *** - // State manager for processing new blocks and managing the over all states - blockProcessor *core.BlockProcessor + // Handlers txPool *core.TxPool blockchain *core.BlockChain accountManager *accounts.Manager @@ -407,8 +405,6 @@ func New(config *Config) (*Ethereum, error) { newPool := core.NewTxPool(eth.EventMux(), eth.blockchain.State, eth.blockchain.GasLimit) eth.txPool = newPool - eth.blockProcessor = core.NewBlockProcessor(chainDb, eth.pow, eth.blockchain, eth.EventMux()) - eth.blockchain.SetProcessor(eth.blockProcessor) if eth.protocolManager, err = NewProtocolManager(config.FastSync, config.NetworkId, eth.eventMux, eth.txPool, eth.pow, eth.blockchain, chainDb); err != nil { return nil, err } @@ -485,24 +481,23 @@ func (s *Ethereum) IsMining() bool { return s.miner.Mining() } func (s *Ethereum) Miner() *miner.Miner { return s.miner } // func (s *Ethereum) Logger() logger.LogSystem { return s.logger } -func (s *Ethereum) Name() string { return s.net.Name } -func (s *Ethereum) AccountManager() *accounts.Manager { return s.accountManager } -func (s *Ethereum) BlockChain() *core.BlockChain { return s.blockchain } -func (s *Ethereum) BlockProcessor() *core.BlockProcessor { return s.blockProcessor } -func (s *Ethereum) TxPool() *core.TxPool { return s.txPool } -func (s *Ethereum) Whisper() *whisper.Whisper { return s.whisper } -func (s *Ethereum) EventMux() *event.TypeMux { return s.eventMux } -func (s *Ethereum) ChainDb() ethdb.Database { return s.chainDb } -func (s *Ethereum) DappDb() ethdb.Database { return s.dappDb } -func (s *Ethereum) IsListening() bool { return true } // Always listening -func (s *Ethereum) PeerCount() int { return s.net.PeerCount() } -func (s *Ethereum) Peers() []*p2p.Peer { return s.net.Peers() } -func (s *Ethereum) MaxPeers() int { return s.net.MaxPeers } -func (s *Ethereum) ClientVersion() string { return s.clientVersion } -func (s *Ethereum) EthVersion() int { return int(s.protocolManager.SubProtocols[0].Version) } -func (s *Ethereum) NetVersion() int { return s.netVersionId } -func (s *Ethereum) ShhVersion() int { return s.shhVersionId } -func (s *Ethereum) Downloader() *downloader.Downloader { return s.protocolManager.downloader } +func (s *Ethereum) Name() string { return s.net.Name } +func (s *Ethereum) AccountManager() *accounts.Manager { return s.accountManager } +func (s *Ethereum) BlockChain() *core.BlockChain { return s.blockchain } +func (s *Ethereum) TxPool() *core.TxPool { return s.txPool } +func (s *Ethereum) Whisper() *whisper.Whisper { return s.whisper } +func (s *Ethereum) EventMux() *event.TypeMux { return s.eventMux } +func (s *Ethereum) ChainDb() ethdb.Database { return s.chainDb } +func (s *Ethereum) DappDb() ethdb.Database { return s.dappDb } +func (s *Ethereum) IsListening() bool { return true } // Always listening +func (s *Ethereum) PeerCount() int { return s.net.PeerCount() } +func (s *Ethereum) Peers() []*p2p.Peer { return s.net.Peers() } +func (s *Ethereum) MaxPeers() int { return s.net.MaxPeers } +func (s *Ethereum) ClientVersion() string { return s.clientVersion } +func (s *Ethereum) EthVersion() int { return int(s.protocolManager.SubProtocols[0].Version) } +func (s *Ethereum) NetVersion() int { return s.netVersionId } +func (s *Ethereum) ShhVersion() int { return s.shhVersionId } +func (s *Ethereum) Downloader() *downloader.Downloader { return s.protocolManager.downloader } // Start the ethereum func (s *Ethereum) Start() error { diff --git a/eth/gasprice.go b/eth/gasprice.go index b752c22dd..e0de89e62 100644 --- a/eth/gasprice.go +++ b/eth/gasprice.go @@ -166,7 +166,7 @@ func (self *GasPriceOracle) processBlock(block *types.Block) { func (self *GasPriceOracle) lowestPrice(block *types.Block) *big.Int { gasUsed := big.NewInt(0) - receipts := self.eth.BlockProcessor().GetBlockReceipts(block.Hash()) + receipts := core.GetBlockReceipts(self.eth.ChainDb(), block.Hash()) if len(receipts) > 0 { if cgu := receipts[len(receipts)-1].CumulativeGasUsed; cgu != nil { gasUsed = receipts[len(receipts)-1].CumulativeGasUsed diff --git a/eth/helper_test.go b/eth/helper_test.go index 65fccf7b4..bbd1fb818 100644 --- a/eth/helper_test.go +++ b/eth/helper_test.go @@ -35,9 +35,7 @@ func newTestProtocolManager(fastSync bool, blocks int, generator func(int, *core db, _ = ethdb.NewMemDatabase() genesis = core.WriteGenesisBlockForTesting(db, core.GenesisAccount{testBankAddress, testBankFunds}) blockchain, _ = core.NewBlockChain(db, pow, evmux) - blockproc = core.NewBlockProcessor(db, pow, blockchain, evmux) ) - blockchain.SetProcessor(blockproc) chain, _ := core.GenerateChain(genesis, db, blocks, generator) if _, err := blockchain.InsertChain(chain); err != nil { panic(err) diff --git a/miner/worker.go b/miner/worker.go index 2d072ef60..238f1a4bf 100644 --- a/miner/worker.go +++ b/miner/worker.go @@ -100,7 +100,7 @@ type worker struct { eth core.Backend chain *core.BlockChain - proc *core.BlockProcessor + proc core.Validator chainDb ethdb.Database coinbase common.Address @@ -131,7 +131,7 @@ func newWorker(coinbase common.Address, eth core.Backend) *worker { recv: make(chan *Result, resultQueueSize), gasPrice: new(big.Int), chain: eth.BlockChain(), - proc: eth.BlockProcessor(), + proc: eth.BlockChain().Validator(), possibleUncles: make(map[common.Hash]*types.Block), coinbase: coinbase, txQueue: make(map[common.Hash]*types.Transaction), @@ -244,7 +244,7 @@ func (self *worker) update() { // Apply transaction to the pending state if we're not mining if atomic.LoadInt32(&self.mining) == 0 { self.currentMu.Lock() - self.current.commitTransactions(types.Transactions{ev.Tx}, self.gasPrice, self.proc) + self.current.commitTransactions(types.Transactions{ev.Tx}, self.gasPrice, self.chain) self.currentMu.Unlock() } } @@ -290,7 +290,9 @@ func (self *worker) wait() { glog.V(logger.Error).Infoln("Invalid block found during mining") continue } - if err := core.ValidateHeader(self.eth.BlockProcessor().Pow, block.Header(), parent.Header(), true, false); err != nil && err != core.BlockFutureErr { + + auxValidator := self.eth.BlockChain().AuxValidator() + if err := core.ValidateHeader(auxValidator, block.Header(), parent.Header(), true, false); err != nil && err != core.BlockFutureErr { glog.V(logger.Error).Infoln("Invalid header on mined block:", err) continue } @@ -516,7 +518,7 @@ func (self *worker) commitNewWork() { transactions := append(singleTxOwner, multiTxOwner...) */ - work.commitTransactions(transactions, self.gasPrice, self.proc) + work.commitTransactions(transactions, self.gasPrice, self.chain) self.eth.TxPool().RemoveTransactions(work.lowGasTxs) // compute uncles for the new block. @@ -575,9 +577,8 @@ func (self *worker) commitUncle(work *Work, uncle *types.Header) error { return nil } -func (env *Work) commitTransactions(transactions types.Transactions, gasPrice *big.Int, proc *core.BlockProcessor) { +func (env *Work) commitTransactions(transactions types.Transactions, gasPrice *big.Int, bc *core.BlockChain) { gp := new(core.GasPool).AddGas(env.header.GasLimit) - for _, tx := range transactions { // We can skip err. It has already been validated in the tx pool from, _ := tx.From() @@ -615,7 +616,7 @@ func (env *Work) commitTransactions(transactions types.Transactions, gasPrice *b env.state.StartRecord(tx.Hash(), common.Hash{}, 0) - err := env.commitTransaction(tx, proc, gp) + err := env.commitTransaction(tx, bc, gp) switch { case core.IsGasLimitErr(err): // ignore the transactor so no nonce errors will be thrown for this account @@ -635,9 +636,9 @@ func (env *Work) commitTransactions(transactions types.Transactions, gasPrice *b } } -func (env *Work) commitTransaction(tx *types.Transaction, proc *core.BlockProcessor, gp *core.GasPool) error { +func (env *Work) commitTransaction(tx *types.Transaction, bc *core.BlockChain, gp *core.GasPool) error { snap := env.state.Copy() - receipt, _, err := proc.ApplyTransaction(gp, env.state, env.header, tx, env.header.GasUsed, true) + receipt, _, _, err := core.ApplyTransaction(bc, gp, env.state, env.header, tx, env.header.GasUsed) if err != nil { env.state.Set(snap) return err diff --git a/rpc/api/debug.go b/rpc/api/debug.go index d2cbc7f19..a6faa335e 100644 --- a/rpc/api/debug.go +++ b/rpc/api/debug.go @@ -22,6 +22,7 @@ import ( "time" "github.com/ethereum/ethash" + "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/eth" @@ -166,11 +167,30 @@ func (self *debugApi) ProcessBlock(req *shared.Request) (interface{}, error) { defer func() { vm.Debug = old }() vm.Debug = true - _, err := self.ethereum.BlockProcessor().RetryProcess(block) - if err == nil { - return true, nil + var ( + blockchain = self.ethereum.BlockChain() + validator = blockchain.Validator() + processor = blockchain.Processor() + ) + + err := core.ValidateHeader(blockchain.AuxValidator(), block.Header(), blockchain.GetHeader(block.ParentHash()), true, false) + if err != nil { + return false, err } - return false, err + statedb, err := state.New(blockchain.GetBlock(block.ParentHash()).Root(), self.ethereum.ChainDb()) + if err != nil { + return false, err + } + receipts, _, usedGas, err := processor.Process(block, statedb) + if err != nil { + return false, err + } + err = validator.ValidateState(block, blockchain.GetBlock(block.ParentHash()), statedb, receipts, usedGas) + if err != nil { + return false, err + } + + return true, nil } func (self *debugApi) SeedHash(req *shared.Request) (interface{}, error) { diff --git a/xeth/xeth.go b/xeth/xeth.go index 35e6dd52d..243bef0b8 100644 --- a/xeth/xeth.go +++ b/xeth/xeth.go @@ -379,7 +379,7 @@ func (self *XEth) CurrentBlock() *types.Block { } func (self *XEth) GetBlockReceipts(bhash common.Hash) types.Receipts { - return self.backend.BlockProcessor().GetBlockReceipts(bhash) + return core.GetBlockReceipts(self.backend.ChainDb(), bhash) } func (self *XEth) GetTxReceipt(txhash common.Hash) *types.Receipt { -- cgit v1.2.3