diff --git a/core/headerchain.go b/core/headerchain.go index 789db20816..d9686e5028 100644 --- a/core/headerchain.go +++ b/core/headerchain.go @@ -225,6 +225,9 @@ type WhCallback func(*types.Header) error func (hc *HeaderChain) ValidateHeaderChain(chain []*types.Header, checkFreq int) (int, error) { // Do a sanity check that the provided chain is actually ordered and linked for i := 1; i < len(chain); i++ { + if chain[i] == nil { + return 0, fmt.Errorf("header was nil") + } if chain[i].Number.Uint64() != chain[i-1].Number.Uint64()+1 || chain[i].ParentHash != chain[i-1].Hash() { // Chain broke ancestry, log a message (programming error) and skip insertion log.Error("Non contiguous header insert", "number", chain[i].Number, "hash", chain[i].Hash(), @@ -291,6 +294,9 @@ func (hc *HeaderChain) InsertHeaderChain(chain []*types.Header, writeHeader WhCa log.Debug("Premature abort during headers import") return i, errors.New("aborted") } + if header == nil { + return i, fmt.Errorf("nil header") + } // If the header's already known, skip it, otherwise store hash := header.Hash() if hc.HasHeader(hash, header.Number.Uint64()) { diff --git a/light/txpool.go b/light/txpool.go index d96cf12e18..c8384e5c5c 100644 --- a/light/txpool.go +++ b/light/txpool.go @@ -223,6 +223,15 @@ func (pool *TxPool) rollbackTxs(hash common.Hash, txc txStateChanges) { func (pool *TxPool) reorgOnNewHead(ctx context.Context, newHeader *types.Header) (txStateChanges, error) { txc := make(txStateChanges) oldh := pool.chain.GetHeaderByHash(pool.head) + if oldh == nil { + current := pool.chain.CurrentHeader() + if current.Number.Uint64() > 0 { + oldh = pool.chain.GetHeaderByHash(current.ParentHash) + } else { + oldh = current + } + pool.head = oldh.Hash() + } newh := newHeader // find common ancestor, create list of rolled back and new block hashes var oldHashes, newHashes []common.Hash @@ -308,7 +317,15 @@ func (pool *TxPool) setNewHead(head *types.Header) { ctx, cancel := context.WithTimeout(context.Background(), blockCheckTimeout) defer cancel() - txc, _ := pool.reorgOnNewHead(ctx, head) + if head == nil { + return + } + + txc, err := pool.reorgOnNewHead(ctx, head) + if err != nil { + log.Info("light.txpool reorg failed", "error", err) + return + } m, r := txc.getLists() pool.relay.NewHead(pool.head, m, r) pool.eip2f = pool.config.IsEnabled(pool.config.GetEthashEIP2Transition, head.Number) diff --git a/light/txpool_test.go b/light/txpool_test.go index 963d82653e..7ac6ec3331 100644 --- a/light/txpool_test.go +++ b/light/txpool_test.go @@ -53,33 +53,33 @@ func (self *testTxRelay) Discard(hashes []common.Hash) { self.discard <- len(hashes) } -const poolTestTxs = 1000 -const poolTestBlocks = 100 +const poolTestTxsN = 1000 +const poolTestBlocksN = 100 // test tx 0..n-1 -var testTx [poolTestTxs]*types.Transaction +var testTxSet [poolTestTxsN]*types.Transaction // txs sent before block i func sentTx(i int) int { - return int(math.Pow(float64(i)/float64(poolTestBlocks), 0.9) * poolTestTxs) + return int(math.Pow(float64(i)/float64(poolTestBlocksN), 0.9) * poolTestTxsN) } // txs included in block i or before that (minedTx(i) <= sentTx(i)) func minedTx(i int) int { - return int(math.Pow(float64(i)/float64(poolTestBlocks), 1.1) * poolTestTxs) + return int(math.Pow(float64(i)/float64(poolTestBlocksN), 1.1) * poolTestTxsN) } func txPoolTestChainGen(i int, block *core.BlockGen) { s := minedTx(i) e := minedTx(i + 1) for i := s; i < e; i++ { - block.AddTx(testTx[i]) + block.AddTx(testTxSet[i]) } } func TestTxPool(t *testing.T) { - for i := range testTx { - testTx[i], _ = types.SignTx(types.NewTransaction(uint64(i), acc1Addr, big.NewInt(10000), vars.TxGas, nil, nil), types.HomesteadSigner{}, testBankKey) + for i := range testTxSet { + testTxSet[i], _ = types.SignTx(types.NewTransaction(uint64(i), acc1Addr, big.NewInt(10000), vars.TxGas, nil, nil), types.HomesteadSigner{}, testBankKey) } var ( @@ -91,7 +91,7 @@ func TestTxPool(t *testing.T) { core.MustCommitGenesis(ldb, &gspec) // Assemble the test environment blockchain, _ := core.NewBlockChain(sdb, nil, params.TestChainConfig, ethash.NewFullFaker(), vm.Config{}, nil) - gchain, _ := core.GenerateChain(params.TestChainConfig, genesis, ethash.NewFaker(), sdb, poolTestBlocks, txPoolTestChainGen) + gchain, _ := core.GenerateChain(params.TestChainConfig, genesis, ethash.NewFaker(), sdb, poolTestBlocksN, txPoolTestChainGen) if _, err := blockchain.InsertChain(gchain); err != nil { panic(err) } @@ -113,16 +113,35 @@ func TestTxPool(t *testing.T) { s := sentTx(i - 1) e := sentTx(i) for i := s; i < e; i++ { - pool.Add(ctx, testTx[i]) + pool.Add(ctx, testTxSet[i]) got := <-relay.send exp := 1 if got != exp { t.Errorf("relay.Send expected len = %d, got %d", exp, got) } } + if ii == len(gchain)/4 { + // Fuck up pool head + // This is an edge case that I'm not sure could really happen (hopefully not), + // but checking anyways. Call it sanity. + t.Log("Setting pool head to empty hash") + pool.head = common.Hash{} + } + if ii == len(gchain)/2 { + // Attempt to insert a nil header into the headerchain + t.Log("Inserting nil header into header chain") + if _, err := lightchain.InsertHeaderChain([]*types.Header{nil}, 1); err == nil { + t.Fatal("insert nil header error should not be errorless") + } + } + if ii == len(gchain)/4*3 { + var h *types.Header + t.Log("Setting pool head to a nil header", h.Hash().Hex()) + pool.setNewHead(h) + } if _, err := lightchain.InsertHeaderChain([]*types.Header{block.Header()}, 1); err != nil { - panic(err) + t.Fatal(err) } got := <-relay.mined