From 72dcd3c58bec0a281280d5d42ed53b6e429ce4af Mon Sep 17 00:00:00 2001
From: Martin Holst Swende <martin@swende.se>
Date: Mon, 13 Feb 2017 21:44:06 +0100
Subject: [PATCH] core, eth, internal: Added `debug_getBadBlocks()` method
 (#3654)

* core,eth,internal: Added `debug_getBadBlocks()` method

When bad blocks are discovered, these are stored within geth.
An RPC-endpoint makes them availablewithin the `debug`
namespace. This feature makes it easier to discover network forks.

```

* core, api: go format + docs

* core/blockchain: Documentation, fix minor nitpick

* core: fix failing blockchain test
---
 core/blockchain.go          | 30 +++++++++++++++++++++++++++++-
 core/blockchain_test.go     |  1 +
 eth/api.go                  |  6 ++++++
 internal/web3ext/web3ext.go |  7 ++++++-
 4 files changed, 42 insertions(+), 2 deletions(-)

diff --git a/core/blockchain.go b/core/blockchain.go
index 281f28f36..ae4fbbcd7 100644
--- a/core/blockchain.go
+++ b/core/blockchain.go
@@ -60,6 +60,7 @@ 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
+	badBlockLimit     = 10
 )
 
 // BlockChain represents the canonical chain given a database with a genesis
@@ -108,6 +109,8 @@ type BlockChain struct {
 	processor Processor // block processor interface
 	validator Validator // block and state validator interface
 	vmConfig  vm.Config
+
+	badBlocks *lru.Cache // Bad block cache
 }
 
 // NewBlockChain returns a fully initialised block chain using information
@@ -118,6 +121,7 @@ func NewBlockChain(chainDb ethdb.Database, config *params.ChainConfig, pow pow.P
 	bodyRLPCache, _ := lru.New(bodyCacheLimit)
 	blockCache, _ := lru.New(blockCacheLimit)
 	futureBlocks, _ := lru.New(maxFutureBlocks)
+	badBlocks, _ := lru.New(badBlockLimit)
 
 	bc := &BlockChain{
 		config:       config,
@@ -130,6 +134,7 @@ func NewBlockChain(chainDb ethdb.Database, config *params.ChainConfig, pow pow.P
 		futureBlocks: futureBlocks,
 		pow:          pow,
 		vmConfig:     vmConfig,
+		badBlocks:    badBlocks,
 	}
 	bc.SetValidator(NewBlockValidator(config, bc, pow))
 	bc.SetProcessor(NewStateProcessor(config, bc))
@@ -893,7 +898,6 @@ func (self *BlockChain) InsertChain(chain types.Blocks) (int, error) {
 			glog.V(logger.Debug).Infoln("Premature abort during block chain processing")
 			break
 		}
-
 		bstart := time.Now()
 		// Wait for block i's nonce to be verified before processing
 		// its state transition.
@@ -1242,8 +1246,32 @@ func (self *BlockChain) update() {
 	}
 }
 
+// BadBlockArgs represents the entries in the list returned when bad blocks are queried.
+type BadBlockArgs struct {
+	Hash   common.Hash   `json:"hash"`
+	Header *types.Header `json:"header"`
+}
+
+// BadBlocks returns a list of the last 'bad blocks' that the client has seen on the network
+func (bc *BlockChain) BadBlocks() ([]BadBlockArgs, error) {
+	headers := make([]BadBlockArgs, 0, bc.badBlocks.Len())
+	for _, hash := range bc.badBlocks.Keys() {
+		if hdr, exist := bc.badBlocks.Peek(hash); exist {
+			header := hdr.(*types.Header)
+			headers = append(headers, BadBlockArgs{header.Hash(), header})
+		}
+	}
+	return headers, nil
+}
+
+// addBadBlock adds a bad block to the bad-block LRU cache
+func (bc *BlockChain) addBadBlock(block *types.Block) {
+	bc.badBlocks.Add(block.Header().Hash(), block.Header())
+}
+
 // reportBlock logs a bad block error.
 func (bc *BlockChain) reportBlock(block *types.Block, receipts types.Receipts, err error) {
+	bc.addBadBlock(block)
 	if glog.V(logger.Error) {
 		var receiptString string
 		for _, receipt := range receipts {
diff --git a/core/blockchain_test.go b/core/blockchain_test.go
index 8f1383acd..16bea9d7c 100644
--- a/core/blockchain_test.go
+++ b/core/blockchain_test.go
@@ -485,6 +485,7 @@ func chm(genesis *types.Block, db ethdb.Database) *BlockChain {
 	bc.bodyRLPCache, _ = lru.New(100)
 	bc.blockCache, _ = lru.New(100)
 	bc.futureBlocks, _ = lru.New(100)
+	bc.badBlocks, _ = lru.New(10)
 	bc.SetValidator(bproc{})
 	bc.SetProcessor(bproc{})
 	bc.ResetWithGenesisBlock(genesis)
diff --git a/eth/api.go b/eth/api.go
index 07df0b79e..f38c0a6b6 100644
--- a/eth/api.go
+++ b/eth/api.go
@@ -566,3 +566,9 @@ func (api *PrivateDebugAPI) Preimage(ctx context.Context, hash common.Hash) (hex
 	db := core.PreimageTable(api.eth.ChainDb())
 	return db.Get(hash.Bytes())
 }
+
+// GetBadBLocks returns a list of the last 'bad blocks' that the client has seen on the network
+// and returns them as a JSON list of block-hashes
+func (api *PrivateDebugAPI) GetBadBlocks(ctx context.Context) ([]core.BadBlockArgs, error) {
+	return api.eth.BlockChain().BadBlocks()
+}
diff --git a/internal/web3ext/web3ext.go b/internal/web3ext/web3ext.go
index 2012c2517..e6bd18116 100644
--- a/internal/web3ext/web3ext.go
+++ b/internal/web3ext/web3ext.go
@@ -294,7 +294,12 @@ web3._extend({
 			call: 'debug_preimage',
 			params: 1,
 			inputFormatter: [null]
-		})
+		}),
+		new web3._extend.Method({
+			name: 'getBadBlocks',
+			call: 'debug_getBadBlocks',
+			params: 0,
+		}),
 	],
 	properties: []
 });
-- 
GitLab