diff --git a/core/chain_manager.go b/core/chain_manager.go index 86d1c1454..291e411ae 100644 --- a/core/chain_manager.go +++ b/core/chain_manager.go @@ -551,12 +551,12 @@ func (self *ChainManager) InsertChain(chain types.Blocks) (int, error) { bstart := time.Now() // Wait for block i's nonce to be verified before processing // its state transition. - for nonceChecked[i] { + for !nonceChecked[i] { r := <-nonceDone nonceChecked[r.i] = true if !r.valid { - block := chain[i] - return i, ValidationError("Block (#%v / %x) nonce is invalid (= %x)", block.Number(), block.Hash(), block.Nonce) + block := chain[r.i] + return r.i, &BlockNonceErr{Hash: block.Hash(), Number: block.Number(), Nonce: block.Nonce()} } } diff --git a/core/chain_manager_test.go b/core/chain_manager_test.go index 560e85f77..45bec7140 100644 --- a/core/chain_manager_test.go +++ b/core/chain_manager_test.go @@ -3,6 +3,7 @@ package core import ( "fmt" "math/big" + "math/rand" "os" "path/filepath" "runtime" @@ -426,3 +427,55 @@ func TestReorgShortest(t *testing.T) { } } } + +func TestInsertNonceError(t *testing.T) { + for i := 1; i < 25 && !t.Failed(); i++ { + db, _ := ethdb.NewMemDatabase() + genesis := GenesisBlock(db) + bc := chm(genesis, db) + bc.processor = NewBlockProcessor(db, db, bc.pow, bc, bc.eventMux) + blocks := makeChain(bc.processor.(*BlockProcessor), bc.currentBlock, i, db, 0) + + fail := rand.Int() % len(blocks) + failblock := blocks[fail] + bc.pow = failpow{failblock.NumberU64()} + n, err := bc.InsertChain(blocks) + + // Check that the returned error indicates the nonce failure. + if n != fail { + t.Errorf("(i=%d) wrong failed block index: got %d, want %d", i, n, fail) + } + if !IsBlockNonceErr(err) { + t.Fatalf("(i=%d) got %q, want a nonce error", i, err) + } + nerr := err.(*BlockNonceErr) + if nerr.Number.Cmp(failblock.Number()) != 0 { + t.Errorf("(i=%d) wrong block number in error, got %v, want %v", i, nerr.Number, failblock.Number()) + } + if nerr.Hash != failblock.Hash() { + t.Errorf("(i=%d) wrong block hash in error, got %v, want %v", i, nerr.Hash, failblock.Hash()) + } + + // Check that all no blocks after the failing block have been inserted. + for _, block := range blocks[fail:] { + if bc.HasBlock(block.Hash()) { + t.Errorf("(i=%d) invalid block %d present in chain", i, block.NumberU64()) + } + } + } +} + +// failpow returns false from Verify for a certain block number. +type failpow struct{ num uint64 } + +func (pow failpow) Search(pow.Block, <-chan struct{}) (nonce uint64, mixHash []byte) { + return 0, nil +} +func (pow failpow) Verify(b pow.Block) bool { + return b.NumberU64() != pow.num +} +func (pow failpow) GetHashrate() int64 { + return 0 +} +func (pow failpow) Turbo(bool) { +} diff --git a/core/error.go b/core/error.go index 2bdad364f..3f3c350df 100644 --- a/core/error.go +++ b/core/error.go @@ -90,6 +90,23 @@ func IsNonceErr(err error) bool { return ok } +// BlockNonceErr indicates that a block's nonce is invalid. +type BlockNonceErr struct { + Number *big.Int + Hash common.Hash + Nonce uint64 +} + +func (err *BlockNonceErr) Error() string { + return fmt.Sprintf("block %d (%v) nonce is invalid (got %d)", err.Number, err.Hash, err.Nonce) +} + +// IsBlockNonceErr returns true for invalid block nonce errors. +func IsBlockNonceErr(err error) bool { + _, ok := err.(*BlockNonceErr) + return ok +} + type InvalidTxErr struct { Message string }