From c04c8f10f04a41e762589358418c65fd99891bb4 Mon Sep 17 00:00:00 2001 From: Jeffrey Wilcke Date: Wed, 23 Nov 2016 13:32:25 +0100 Subject: [PATCH] core: improved bad block error reporting (#3320) --- core/block_validator.go | 16 ++++++++-------- core/blockchain.go | 29 +++++++++++++++++++++-------- core/blockchain_test.go | 4 ++-- eth/backend.go | 2 ++ params/config.go | 14 ++++++++++++++ 5 files changed, 47 insertions(+), 18 deletions(-) diff --git a/core/block_validator.go b/core/block_validator.go index 3353683c07..65f975345c 100644 --- a/core/block_validator.go +++ b/core/block_validator.go @@ -93,14 +93,14 @@ func (v *BlockValidator) ValidateBlock(block *types.Block) error { // 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) + return fmt.Errorf("invalid uncles root hash (remote: %x local: %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 fmt.Errorf("invalid transaction root hash (remote: %x local: %x)", header.TxHash, txSha) } return nil @@ -113,23 +113,23 @@ func (v *BlockValidator) ValidateBlock(block *types.Block) error { 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)) + return ValidationError(fmt.Sprintf("invalid gas used (remote: %v local: %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 vs calculated bloom=%x", header.Bloom, rbloom) + return fmt.Errorf("invalid bloom (remote: %x local: %x)", header.Bloom, 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) + return fmt.Errorf("invalid receipt root hash (remote: %x local: %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(v.config.IsEIP158(header.Number)); header.Root != root { - return fmt.Errorf("invalid merkle root: header=%x computed=%x", header.Root, root) + return fmt.Errorf("invalid merkle root (remote: %x local: %x)", header.Root, root) } return nil } @@ -223,7 +223,7 @@ func ValidateHeader(config *params.ChainConfig, pow pow.PoW, header *types.Heade expd := CalcDifficulty(config, 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) + return fmt.Errorf("Difficulty check failed for header (remote: %v local: %v)", header.Difficulty, expd) } a := new(big.Int).Set(parent.GasLimit) @@ -232,7 +232,7 @@ func ValidateHeader(config *params.ChainConfig, pow pow.PoW, header *types.Heade 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) + return fmt.Errorf("GasLimit check failed for header (remote: %v local_max: %v)", header.GasLimit, b) } num := new(big.Int).Set(parent.Number) diff --git a/core/blockchain.go b/core/blockchain.go index bbcff3b92d..9ace6d79a0 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -878,7 +878,7 @@ func (self *BlockChain) InsertChain(chain types.Blocks) (int, error) { if BadHashes[block.Hash()] { err := BadHashError(block.Hash()) - reportBlock(block, err) + self.reportBlock(block, nil, err) return i, err } // Stage 1 validation of the block using the chain's validator @@ -910,7 +910,7 @@ func (self *BlockChain) InsertChain(chain types.Blocks) (int, error) { continue } - reportBlock(block, err) + self.reportBlock(block, nil, err) return i, err } @@ -924,19 +924,19 @@ func (self *BlockChain) InsertChain(chain types.Blocks) (int, error) { err = self.stateCache.Reset(chain[i-1].Root()) } if err != nil { - reportBlock(block, err) + self.reportBlock(block, nil, err) return i, err } // Process block using the parent state as reference point. receipts, logs, usedGas, err := self.processor.Process(block, self.stateCache, vm.Config{}) if err != nil { - reportBlock(block, err) + self.reportBlock(block, receipts, err) return i, err } // Validate the state using the default validator err = self.Validator().ValidateState(block, self.GetBlock(block.ParentHash(), block.NumberU64()-1), self.stateCache, receipts, usedGas) if err != nil { - reportBlock(block, err) + self.reportBlock(block, receipts, err) return i, err } // Write state changes to database @@ -1207,10 +1207,23 @@ func (self *BlockChain) update() { } // reportBlock logs a bad block error. -func reportBlock(block *types.Block, err error) { +func (bc *BlockChain) reportBlock(block *types.Block, receipts types.Receipts, err error) { if glog.V(logger.Error) { - glog.Errorf("Bad block #%v (%s)\n", block.Number(), block.Hash().Hex()) - glog.Errorf(" %v", err) + var receiptString string + for _, receipt := range receipts { + receiptString += fmt.Sprintf("\t%v\n", receipt) + } + glog.Errorf(` +########## BAD BLOCK ######### +Chain config: %v + +Number: %v +Hash: 0x%x +%v + +Error: %v +############################## +`, bc.config, block.Number(), block.Hash(), receiptString, err) } } diff --git a/core/blockchain_test.go b/core/blockchain_test.go index 934ae74e1a..2d4e2b6b22 100644 --- a/core/blockchain_test.go +++ b/core/blockchain_test.go @@ -143,12 +143,12 @@ func testBlockChainImport(chain types.Blocks, blockchain *BlockChain) error { } receipts, _, usedGas, err := blockchain.Processor().Process(block, statedb, vm.Config{}) if err != nil { - reportBlock(block, err) + blockchain.reportBlock(block, receipts, err) return err } err = blockchain.Validator().ValidateState(block, blockchain.GetBlockByHash(block.ParentHash()), statedb, receipts, usedGas) if err != nil { - reportBlock(block, err) + blockchain.reportBlock(block, receipts, err) return err } blockchain.mu.Lock() diff --git a/eth/backend.go b/eth/backend.go index 67598bdbb3..2351a62c8b 100644 --- a/eth/backend.go +++ b/eth/backend.go @@ -218,6 +218,8 @@ func New(ctx *node.ServiceContext, config *Config) (*Ethereum, error) { eth.chainConfig = config.ChainConfig + glog.V(logger.Info).Infoln("Chain config:", eth.chainConfig) + eth.blockchain, err = core.NewBlockChain(chainDb, eth.chainConfig, eth.pow, eth.EventMux()) if err != nil { if err == core.ErrNoGenesis { diff --git a/params/config.go b/params/config.go index d083adf46a..8c285781e9 100644 --- a/params/config.go +++ b/params/config.go @@ -17,6 +17,7 @@ package params import ( + "fmt" "math/big" "github.com/ethereum/go-ethereum/common" @@ -66,6 +67,19 @@ type ChainConfig struct { EIP158Block *big.Int `json:"eip158Block"` // EIP158 HF block } +// String implements the Stringer interface. +func (c *ChainConfig) String() string { + return fmt.Sprintf("{ChainID: %v Homestead: %v DAO: %v DAOSupport: %v EIP150: %v EIP155: %v EIP158: %v}", + c.ChainId, + c.HomesteadBlock, + c.DAOForkBlock, + c.DAOForkSupport, + c.EIP150Block, + c.EIP155Block, + c.EIP158Block, + ) +} + var ( TestChainConfig = &ChainConfig{big.NewInt(1), new(big.Int), new(big.Int), true, new(big.Int), common.Hash{}, new(big.Int), new(big.Int)} TestRules = TestChainConfig.Rules(new(big.Int))