From 3f4a875bf6a74defd6e5e5a9cf121a5adc6e6c86 Mon Sep 17 00:00:00 2001
From: Martin Holst Swende <martin@swende.se>
Date: Mon, 18 Nov 2019 17:03:57 +0100
Subject: [PATCH] consensus/clique: add clique_status API method (#20103)

This PR introduces clique_status which gives info about the health of
the clique network.

It's currently a bit PITA to find out how a clique network is
performing, and it can easily happen that sealers drop off -- and
everything is 'fine' until one more signer drops off, and the network
suddenly halts.

The new method provides the following stats:

- Which signers are currently active, and have signed blocks in the last
  N (set to 64) blocks?
- How many blocks has each signer signed?
- What is the difficulty in the last N blocks, compared to the
  theoretical maximum?
---
 consensus/clique/api.go | 58 +++++++++++++++++++++++++++++++++++++++++
 1 file changed, 58 insertions(+)

diff --git a/consensus/clique/api.go b/consensus/clique/api.go
index 6bcf987af..fdb7b7a07 100644
--- a/consensus/clique/api.go
+++ b/consensus/clique/api.go
@@ -17,6 +17,8 @@
 package clique
 
 import (
+	"fmt"
+
 	"github.com/ethereum/go-ethereum/common"
 	"github.com/ethereum/go-ethereum/consensus"
 	"github.com/ethereum/go-ethereum/core/types"
@@ -117,3 +119,59 @@ func (api *API) Discard(address common.Address) {
 
 	delete(api.clique.proposals, address)
 }
+
+type Status struct {
+	InturnPercent float64                `json:"inturnPercent"`
+	SigningStatus map[common.Address]int `json:"sealerActivity""`
+	NumBlocks     uint64                 `json:"numBlocks"`
+}
+
+// Status returns the status of the last N blocks,
+// - the number of active signers,
+// - the number of signers,
+// - the percentage of in-turn blocks
+func (api *API) Status() (*Status, error) {
+	var (
+		numBlocks = uint64(64)
+		header    = api.chain.CurrentHeader()
+		diff      = uint64(0)
+		optimals  = 0
+	)
+	snap, err := api.clique.snapshot(api.chain, header.Number.Uint64(), header.Hash(), nil)
+	if err != nil {
+		return nil, err
+	}
+	var (
+		signers = snap.signers()
+		end     = header.Number.Uint64()
+		start   = end - numBlocks
+	)
+	if numBlocks > end {
+		start = 1
+		numBlocks = end - start
+	}
+	signStatus := make(map[common.Address]int)
+	for _, s := range signers {
+		signStatus[s] = 0
+	}
+	for n := start; n < end; n++ {
+		h := api.chain.GetHeaderByNumber(n)
+		if h == nil {
+			return nil, fmt.Errorf("missing block %d", n)
+		}
+		if h.Difficulty.Cmp(diffInTurn) == 0 {
+			optimals++
+		}
+		diff += h.Difficulty.Uint64()
+		sealer, err := api.clique.Author(h)
+		if err != nil {
+			return nil, err
+		}
+		signStatus[sealer]++
+	}
+	return &Status{
+		InturnPercent: float64((100 * optimals)) / float64(numBlocks),
+		SigningStatus: signStatus,
+		NumBlocks:     numBlocks,
+	}, nil
+}
-- 
GitLab