From 5e01efbb2f76dec03ae4a605f829565d066b4ebb Mon Sep 17 00:00:00 2001 From: rjl493456442 Date: Tue, 31 Dec 2019 17:09:48 +0800 Subject: [PATCH] core: don't mark bad for block with inconsistent body --- core/block_validator.go | 4 ++-- core/blockchain.go | 23 +++++++++++++------ core/blockchain_test.go | 51 +++++++++++++++++++++++++++++++++++++++-- core/error.go | 4 ++++ go.sum | 1 + 5 files changed, 72 insertions(+), 11 deletions(-) diff --git a/core/block_validator.go b/core/block_validator.go index b36ca56d7f..fce46c2257 100644 --- a/core/block_validator.go +++ b/core/block_validator.go @@ -59,10 +59,10 @@ func (v *BlockValidator) ValidateBody(block *types.Block) error { return err } if hash := types.CalcUncleHash(block.Uncles()); hash != header.UncleHash { - return fmt.Errorf("uncle root hash mismatch: have %x, want %x", hash, header.UncleHash) + return ErrInconsistentBody } if hash := types.DeriveSha(block.Transactions()); hash != header.TxHash { - return fmt.Errorf("transaction root hash mismatch: have %x, want %x", hash, header.TxHash) + return ErrInconsistentBody } if !v.bc.HasBlockAndState(block.ParentHash(), block.NumberU64()-1) { if !v.bc.HasBlock(block.ParentHash(), block.NumberU64()-1) { diff --git a/core/blockchain.go b/core/blockchain.go index ebab7a3ab5..a5c41c1de7 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -1572,11 +1572,19 @@ func (bc *BlockChain) insertChain(chain types.Blocks, verifySeals bool) (int, er // If there are any still remaining, mark as ignored return it.index, err + case err == ErrInconsistentBody: + // If the body is inconsistent with header, don't mark the + // whole block hash as invalid. We may receive a block with + // invalid body but valid header. + stats.ignored += len(it.chain) + bc.reportBlock(block, nil, err, false) + return it.index, err + // Some other error occurred, abort case err != nil: bc.futureBlocks.Remove(block.Hash()) stats.ignored += len(it.chain) - bc.reportBlock(block, nil, err) + bc.reportBlock(block, nil, err, true) return it.index, err } // No validation errors for the first block (or chain prefix skipped) @@ -1588,7 +1596,7 @@ func (bc *BlockChain) insertChain(chain types.Blocks, verifySeals bool) (int, er } // If the header is a banned one, straight out abort if BadHashes[block.Hash()] { - bc.reportBlock(block, nil, ErrBlacklistedHash) + bc.reportBlock(block, nil, ErrBlacklistedHash, true) return it.index, ErrBlacklistedHash } // If the block is known (in the middle of the chain), it's a special case for @@ -1647,7 +1655,7 @@ func (bc *BlockChain) insertChain(chain types.Blocks, verifySeals bool) (int, er substart := time.Now() receipts, logs, usedGas, err := bc.processor.Process(block, statedb, bc.vmConfig) if err != nil { - bc.reportBlock(block, receipts, err) + bc.reportBlock(block, receipts, err, true) atomic.StoreUint32(&followupInterrupt, 1) return it.index, err } @@ -1666,7 +1674,7 @@ func (bc *BlockChain) insertChain(chain types.Blocks, verifySeals bool) (int, er // Validate the state using the default validator substart = time.Now() if err := bc.validator.ValidateState(block, statedb, receipts, usedGas); err != nil { - bc.reportBlock(block, receipts, err) + bc.reportBlock(block, receipts, err, true) atomic.StoreUint32(&followupInterrupt, 1) return it.index, err } @@ -2065,9 +2073,10 @@ func (bc *BlockChain) addBadBlock(block *types.Block) { } // reportBlock logs a bad block error. -func (bc *BlockChain) reportBlock(block *types.Block, receipts types.Receipts, err error) { - bc.addBadBlock(block) - +func (bc *BlockChain) reportBlock(block *types.Block, receipts types.Receipts, err error, markBad bool) { + if markBad { + bc.addBadBlock(block) + } var receiptString string for i, receipt := range receipts { receiptString += fmt.Sprintf("\t %d: cumulative: %v gas: %v contract: %v status: %v tx: %v logs: %v bloom: %x state: %x\n", diff --git a/core/blockchain_test.go b/core/blockchain_test.go index de23ead210..8000d2ecab 100644 --- a/core/blockchain_test.go +++ b/core/blockchain_test.go @@ -150,12 +150,12 @@ func testBlockChainImport(chain types.Blocks, blockchain *BlockChain) error { } receipts, _, usedGas, err := blockchain.processor.Process(block, statedb, vm.Config{}) if err != nil { - blockchain.reportBlock(block, receipts, err) + blockchain.reportBlock(block, receipts, err, true) return err } err = blockchain.validator.ValidateState(block, statedb, receipts, usedGas) if err != nil { - blockchain.reportBlock(block, receipts, err) + blockchain.reportBlock(block, receipts, err, true) return err } blockchain.chainmu.Lock() @@ -2362,3 +2362,50 @@ func TestDeleteCreateRevert(t *testing.T) { t.Fatalf("block %d: failed to insert into chain: %v", n, err) } } + +func TestInconsistentBlockImport(t *testing.T) { + var ( + key1, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") + addr1 = crypto.PubkeyToAddress(key1.PublicKey) + gspec = &Genesis{ + Config: params.TestChainConfig, + Alloc: GenesisAlloc{addr1: {Balance: big.NewInt(10000000000000)}}, + } + signer = types.NewEIP155Signer(gspec.Config.ChainID) + ) + engine := ethash.NewFaker() + db := rawdb.NewMemoryDatabase() + genesis, _ := gspec.Commit(db) + + // Generate inconsistent blocks + blocks, _ := GenerateChain(params.TestChainConfig, genesis, engine, db, 32, func(i int, b *BlockGen) { + b.SetCoinbase(common.Address{1}) + tx, err := types.SignTx(types.NewContractCreation(b.TxNonce(addr1), new(big.Int), 1000000, new(big.Int), nil), signer, key1) + if err != nil { + t.Fatalf("failed to create tx: %v", err) + } + b.AddTx(tx) + }) + badBlocks := make([]*types.Block, len(blocks)) + copy(badBlocks, blocks) + badBlocks[0] = types.NewBlockWithHeader(badBlocks[0].Header()) // Modify the block, wipe body. + + // Import the canonical chain + diskdb := rawdb.NewMemoryDatabase() + gspec.Commit(diskdb) + chain, err := NewBlockChain(diskdb, nil, params.TestChainConfig, engine, vm.Config{}, nil) + if err != nil { + t.Fatalf("failed to create tester chain: %v", err) + } + n, err := chain.InsertChain(badBlocks) + if err == nil || n != 0 { + t.Fatal("expect error") + } + // Ensure the hash is not recorded as BAD + if chain.badBlocks.Contains(badBlocks[0].Hash()) { + t.Fatal("expect no recording") + } + if _, err := chain.InsertChain(blocks); err != nil { + t.Fatal("expect successfully insertion") + } +} diff --git a/core/error.go b/core/error.go index cd4be3d705..cd35dd80e3 100644 --- a/core/error.go +++ b/core/error.go @@ -35,4 +35,8 @@ var ( // ErrNoGenesis is returned when there is no Genesis Block. ErrNoGenesis = errors.New("genesis not found in chain") + + // ErrInconsistentBody is returned if the block body is inconsistent with its + // corresponding header. + ErrInconsistentBody = errors.New("body is inconsistent with header") ) diff --git a/go.sum b/go.sum index edbb5ea2e0..4d18c8c20e 100644 --- a/go.sum +++ b/go.sum @@ -101,6 +101,7 @@ github.com/influxdata/influxdb v1.2.3-0.20180221223340-01288bdb0883 h1:FSeK4fZCo github.com/influxdata/influxdb v1.2.3-0.20180221223340-01288bdb0883/go.mod h1:qZna6X/4elxqT3yI9iZYdZrWWdeFOOprn86kgg4+IzY= github.com/jackpal/go-nat-pmp v1.0.2-0.20160603034137-1fa385a6f458 h1:6OvNmYgJyexcZ3pYbTI9jWx5tHo1Dee/tWbLMfPe2TA= github.com/jackpal/go-nat-pmp v1.0.2-0.20160603034137-1fa385a6f458/go.mod h1:QPH045xvCAeXUZOxsnwmrtiCoxIr9eob+4orBN1SBKc= +github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af h1:pmfjZENx5imkbgOkpRUYLnmbU7UEFbjtDA2hxJ1ichM= github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= github.com/julienschmidt/httprouter v1.1.1-0.20170430222011-975b5c4c7c21 h1:F/iKcka0K2LgnKy/fgSBf235AETtm1n1TvBzqu40LE0= github.com/julienschmidt/httprouter v1.1.1-0.20170430222011-975b5c4c7c21/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=