From d154cb7382c605d4eff3ae4032c6f8b166a9da60 Mon Sep 17 00:00:00 2001 From: Sam Calder-Mason Date: Tue, 17 May 2022 20:23:52 +1000 Subject: [PATCH] feat(execution): Fetch total difficulty from the Head block rather than using admin_nodeInfo --- go.mod | 1 + go.sum | 2 + pkg/exporter/execution/execution.go | 30 ++++++----- pkg/exporter/execution/jobs/admin.go | 11 ++-- pkg/exporter/execution/jobs/block.go | 64 ++++++++++++++++------- pkg/exporter/execution/jobs/general.go | 23 ++++---- pkg/exporter/execution/jobs/syncstatus.go | 11 ++-- pkg/exporter/execution/jobs/txpool.go | 11 ++-- pkg/exporter/execution/metrics.go | 15 +++--- 9 files changed, 108 insertions(+), 60 deletions(-) diff --git a/go.mod b/go.mod index 4c1a64a..b884efd 100644 --- a/go.mod +++ b/go.mod @@ -38,6 +38,7 @@ require ( github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect github.com/minio/sha256-simd v1.0.0 // indirect github.com/mitchellh/mapstructure v1.4.3 // indirect + github.com/onrik/ethrpc v1.0.0 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/prometheus/client_golang v1.12.1 // indirect github.com/prometheus/client_model v0.2.0 // indirect diff --git a/go.sum b/go.sum index 504144c..9981509 100644 --- a/go.sum +++ b/go.sum @@ -351,6 +351,8 @@ github.com/naoina/toml v0.1.2-0.20170918210437-9fafd6967416/go.mod h1:NBIhNtsFMo github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= +github.com/onrik/ethrpc v1.0.0 h1:caG0U/Y1op32mntqmDnwvzue4Tg37cSA2EU8PMIMSjM= +github.com/onrik/ethrpc v1.0.0/go.mod h1:RoqOlDiBBs1qYamkcYhxMgkPijxu5R8t55mgUiy4le8= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY= diff --git a/pkg/exporter/execution/execution.go b/pkg/exporter/execution/execution.go index 8c2244b..44ec74a 100644 --- a/pkg/exporter/execution/execution.go +++ b/pkg/exporter/execution/execution.go @@ -5,6 +5,7 @@ import ( "time" "github.com/ethereum/go-ethereum/ethclient" + "github.com/onrik/ethrpc" "github.com/samcm/ethereum-metrics-exporter/pkg/exporter/execution/api" "github.com/sirupsen/logrus" ) @@ -24,27 +25,30 @@ type Node interface { } type node struct { - name string - url string - client *ethclient.Client - internalApi api.ExecutionClient - log logrus.FieldLogger - metrics Metrics + name string + url string + client *ethclient.Client + internalApi api.ExecutionClient + ethrpcClient *ethrpc.EthRPC + log logrus.FieldLogger + metrics Metrics } // NewExecutionNode returns a new execution node. func NewExecutionNode(ctx context.Context, log logrus.FieldLogger, namespace string, nodeName string, url string, enabledModules []string) (Node, error) { internalApi := api.NewExecutionClient(ctx, log, url) client, _ := ethclient.Dial(url) - metrics := NewMetrics(client, internalApi, log, nodeName, namespace, enabledModules) + ethrpcClient := ethrpc.New(url) + metrics := NewMetrics(client, internalApi, ethrpcClient, log, nodeName, namespace, enabledModules) node := &node{ - name: nodeName, - url: url, - log: log, - internalApi: internalApi, - client: client, - metrics: metrics, + name: nodeName, + url: url, + log: log, + ethrpcClient: ethrpcClient, + internalApi: internalApi, + client: client, + metrics: metrics, } return node, nil diff --git a/pkg/exporter/execution/jobs/admin.go b/pkg/exporter/execution/jobs/admin.go index 2c7ad26..7de6ebd 100644 --- a/pkg/exporter/execution/jobs/admin.go +++ b/pkg/exporter/execution/jobs/admin.go @@ -7,6 +7,7 @@ import ( "time" "github.com/ethereum/go-ethereum/ethclient" + "github.com/onrik/ethrpc" "github.com/prometheus/client_golang/prometheus" "github.com/samcm/ethereum-metrics-exporter/pkg/exporter/execution/api" "github.com/samcm/ethereum-metrics-exporter/pkg/exporter/execution/api/types" @@ -18,6 +19,7 @@ type Admin struct { MetricExporter client *ethclient.Client api api.ExecutionClient + ethrpcClient *ethrpc.EthRPC log logrus.FieldLogger NodeInfo prometheus.GaugeVec Port prometheus.GaugeVec @@ -39,14 +41,15 @@ func (t *Admin) RequiredModules() []string { } // NewAdmin returns a new Admin instance. -func NewAdmin(client *ethclient.Client, internalApi api.ExecutionClient, log logrus.FieldLogger, namespace string, constLabels map[string]string) Admin { +func NewAdmin(client *ethclient.Client, internalApi api.ExecutionClient, ethRpcClient *ethrpc.EthRPC, log logrus.FieldLogger, namespace string, constLabels map[string]string) Admin { namespace = namespace + "_admin" constLabels["module"] = NameAdmin return Admin{ - client: client, - api: internalApi, - log: log.WithField("module", NameAdmin), + client: client, + api: internalApi, + ethrpcClient: ethRpcClient, + log: log.WithField("module", NameAdmin), NodeInfo: *prometheus.NewGaugeVec( prometheus.GaugeOpts{ Namespace: namespace, diff --git a/pkg/exporter/execution/jobs/block.go b/pkg/exporter/execution/jobs/block.go index 0ba87b8..20a1558 100644 --- a/pkg/exporter/execution/jobs/block.go +++ b/pkg/exporter/execution/jobs/block.go @@ -8,6 +8,7 @@ import ( "time" "github.com/ethereum/go-ethereum/ethclient" + "github.com/onrik/ethrpc" "github.com/prometheus/client_golang/prometheus" "github.com/samcm/ethereum-metrics-exporter/pkg/exporter/execution/api" "github.com/sirupsen/logrus" @@ -16,17 +17,20 @@ import ( // BlockMetrics exposes metrics on the head/safest block. type BlockMetrics struct { MetricExporter - client *ethclient.Client - api api.ExecutionClient - log logrus.FieldLogger + client *ethclient.Client + api api.ExecutionClient + ethRpcClient *ethrpc.EthRPC + log logrus.FieldLogger MostRecentBlockNumber prometheus.GaugeVec - HeadGasUsed prometheus.Gauge - HeadGasLimit prometheus.Gauge - HeadBaseFeePerGas prometheus.Gauge - HeadBlockSize prometheus.Gauge - HeadTransactionCount prometheus.Gauge + HeadGasUsed prometheus.Gauge + HeadGasLimit prometheus.Gauge + HeadBaseFeePerGas prometheus.Gauge + HeadBlockSize prometheus.Gauge + HeadTransactionCount prometheus.Gauge + HeadTotalDifficulty prometheus.Gauge + HeadTotalDifficultyTrillions prometheus.Gauge SafeGasUsed prometheus.Counter SafeGasLimit prometheus.Counter @@ -52,13 +56,14 @@ func (b *BlockMetrics) RequiredModules() []string { } // NewBlockMetrics returns a new Block metrics instance. -func NewBlockMetrics(client *ethclient.Client, internalApi api.ExecutionClient, log logrus.FieldLogger, namespace string, constLabels map[string]string) BlockMetrics { +func NewBlockMetrics(client *ethclient.Client, internalApi api.ExecutionClient, ethRpcClient *ethrpc.EthRPC, log logrus.FieldLogger, namespace string, constLabels map[string]string) BlockMetrics { constLabels["module"] = NameBlock namespace = namespace + "_" + NameBlock return BlockMetrics{ - client: client, - api: internalApi, - log: log.WithField("module", NameBlock), + client: client, + api: internalApi, + ethRpcClient: ethRpcClient, + log: log.WithField("module", NameBlock), MostRecentBlockNumber: *prometheus.NewGaugeVec( prometheus.GaugeOpts{ @@ -112,6 +117,22 @@ func NewBlockMetrics(client *ethclient.Client, internalApi api.ExecutionClient, ConstLabels: constLabels, }, ), + HeadTotalDifficulty: prometheus.NewGauge( + prometheus.GaugeOpts{ + Namespace: namespace, + Name: "head_total_difficulty", + Help: "The total difficulty of the head block.", + ConstLabels: constLabels, + }, + ), + HeadTotalDifficultyTrillions: prometheus.NewGauge( + prometheus.GaugeOpts{ + Namespace: namespace, + Name: "head_total_difficulty_trillions", + Help: "The total difficulty of the head block (in trillions).", + ConstLabels: constLabels, + }, + ), SafeGasUsed: prometheus.NewCounter( prometheus.CounterOpts{ @@ -197,17 +218,22 @@ func (b *BlockMetrics) getHeadBlockStats(ctx context.Context) error { b.currentHeadBlockNumber = mostRecentBlockNumber b.MostRecentBlockNumber.WithLabelValues("head").Set(float64(mostRecentBlockNumber)) - block, err := b.client.BlockByNumber(ctx, big.NewInt(int64(mostRecentBlockNumber))) + block, err := b.ethRpcClient.EthGetBlockByNumber(int(mostRecentBlockNumber), true) if err != nil { return err } - b.HeadGasUsed.Set(float64(block.GasUsed())) - b.HeadGasLimit.Set(float64(block.GasLimit())) - b.HeadBaseFeePerGas.Set(float64(block.BaseFee().Int64())) - b.HeadBlockSize.Set(float64(block.Size())) - b.HeadTransactionCount.Set(float64(len(block.Transactions()))) - + b.HeadGasUsed.Set(float64(block.GasUsed)) + b.HeadGasLimit.Set(float64(block.GasLimit)) + b.HeadBlockSize.Set(float64(block.Size)) + b.HeadTransactionCount.Set(float64(len(block.Transactions))) + // b.HeadBaseFeePerGas.Set(float64(block.BaseFee().Int64())) TODO(sam.calder-mason): Fix me + + b.HeadTotalDifficulty.Set(float64(block.TotalDifficulty.Uint64())) + // Since we can't represent a big.Int as a float64, and the TD on mainnet is beyond float64, we'll divide the number by a trillion + trillion := big.NewInt(1e12) + divided := new(big.Int).Quo(&block.TotalDifficulty, trillion) + b.HeadTotalDifficultyTrillions.Set(float64(divided.Uint64())) return nil } diff --git a/pkg/exporter/execution/jobs/general.go b/pkg/exporter/execution/jobs/general.go index 2012676..5c54ded 100644 --- a/pkg/exporter/execution/jobs/general.go +++ b/pkg/exporter/execution/jobs/general.go @@ -5,6 +5,7 @@ import ( "time" "github.com/ethereum/go-ethereum/ethclient" + "github.com/onrik/ethrpc" "github.com/prometheus/client_golang/prometheus" "github.com/samcm/ethereum-metrics-exporter/pkg/exporter/execution/api" "github.com/sirupsen/logrus" @@ -13,12 +14,13 @@ import ( // GeneralMetrics exposes metrics that otherwise don't fit in to a specific module. type GeneralMetrics struct { MetricExporter - client *ethclient.Client - api api.ExecutionClient - log logrus.FieldLogger - GasPrice prometheus.Gauge - NetworkID prometheus.Gauge - ChainID prometheus.Gauge + client *ethclient.Client + api api.ExecutionClient + ethRpcClient *ethrpc.EthRPC + log logrus.FieldLogger + GasPrice prometheus.Gauge + NetworkID prometheus.Gauge + ChainID prometheus.Gauge } const ( @@ -34,12 +36,13 @@ func (g *GeneralMetrics) RequiredModules() []string { } // NewGeneralMetrics returns a new General metrics instance. -func NewGeneralMetrics(client *ethclient.Client, internalApi api.ExecutionClient, log logrus.FieldLogger, namespace string, constLabels map[string]string) GeneralMetrics { +func NewGeneralMetrics(client *ethclient.Client, internalApi api.ExecutionClient, ethRpcClient *ethrpc.EthRPC, log logrus.FieldLogger, namespace string, constLabels map[string]string) GeneralMetrics { constLabels["module"] = NameGeneral return GeneralMetrics{ - client: client, - api: internalApi, - log: log.WithField("module", NameGeneral), + client: client, + api: internalApi, + ethRpcClient: ethRpcClient, + log: log.WithField("module", NameGeneral), GasPrice: prometheus.NewGauge( prometheus.GaugeOpts{ Namespace: namespace, diff --git a/pkg/exporter/execution/jobs/syncstatus.go b/pkg/exporter/execution/jobs/syncstatus.go index b0303ff..3b4961a 100644 --- a/pkg/exporter/execution/jobs/syncstatus.go +++ b/pkg/exporter/execution/jobs/syncstatus.go @@ -5,6 +5,7 @@ import ( "time" "github.com/ethereum/go-ethereum/ethclient" + "github.com/onrik/ethrpc" "github.com/prometheus/client_golang/prometheus" "github.com/samcm/ethereum-metrics-exporter/pkg/exporter/execution/api" "github.com/sirupsen/logrus" @@ -15,6 +16,7 @@ type SyncStatus struct { MetricExporter client *ethclient.Client api api.ExecutionClient + ethRpcClient *ethrpc.EthRPC log logrus.FieldLogger Percentage prometheus.Gauge CurrentBlock prometheus.Gauge @@ -51,13 +53,14 @@ func (s *syncingStatus) Percent() float64 { } // NewSyncStatus returns a new SyncStatus instance. -func NewSyncStatus(client *ethclient.Client, internalApi api.ExecutionClient, log logrus.FieldLogger, namespace string, constLabels map[string]string) SyncStatus { +func NewSyncStatus(client *ethclient.Client, internalApi api.ExecutionClient, ethRpcClient *ethrpc.EthRPC, log logrus.FieldLogger, namespace string, constLabels map[string]string) SyncStatus { constLabels["module"] = NameSyncStatus namespace = namespace + "_sync" return SyncStatus{ - client: client, - api: internalApi, - log: log.WithField("module", NameSyncStatus), + client: client, + api: internalApi, + ethRpcClient: ethRpcClient, + log: log.WithField("module", NameSyncStatus), Percentage: prometheus.NewGauge( prometheus.GaugeOpts{ Namespace: namespace, diff --git a/pkg/exporter/execution/jobs/txpool.go b/pkg/exporter/execution/jobs/txpool.go index 3145af8..986f4d9 100644 --- a/pkg/exporter/execution/jobs/txpool.go +++ b/pkg/exporter/execution/jobs/txpool.go @@ -5,6 +5,7 @@ import ( "time" "github.com/ethereum/go-ethereum/ethclient" + "github.com/onrik/ethrpc" "github.com/prometheus/client_golang/prometheus" "github.com/samcm/ethereum-metrics-exporter/pkg/exporter/execution/api" "github.com/sirupsen/logrus" @@ -15,6 +16,7 @@ type TXPool struct { MetricExporter client *ethclient.Client api api.ExecutionClient + ethRpcClient *ethrpc.EthRPC log logrus.FieldLogger Transactions prometheus.GaugeVec } @@ -32,13 +34,14 @@ func (t *TXPool) RequiredModules() []string { } // NewTXPool creates a new TXPool instance. -func NewTXPool(client *ethclient.Client, internalApi api.ExecutionClient, log logrus.FieldLogger, namespace string, constLabels map[string]string) TXPool { +func NewTXPool(client *ethclient.Client, internalApi api.ExecutionClient, ethRpcClient *ethrpc.EthRPC, log logrus.FieldLogger, namespace string, constLabels map[string]string) TXPool { constLabels["module"] = NameTxPool namespace = namespace + "_txpool" return TXPool{ - client: client, - api: internalApi, - log: log.WithField("module", NameGeneral), + client: client, + api: internalApi, + ethRpcClient: ethRpcClient, + log: log.WithField("module", NameGeneral), Transactions: *prometheus.NewGaugeVec( prometheus.GaugeOpts{ Namespace: namespace, diff --git a/pkg/exporter/execution/metrics.go b/pkg/exporter/execution/metrics.go index 747cd85..fa2729c 100644 --- a/pkg/exporter/execution/metrics.go +++ b/pkg/exporter/execution/metrics.go @@ -4,6 +4,7 @@ import ( "context" "github.com/ethereum/go-ethereum/ethclient" + "github.com/onrik/ethrpc" "github.com/prometheus/client_golang/prometheus" "github.com/samcm/ethereum-metrics-exporter/pkg/exporter/execution/api" "github.com/samcm/ethereum-metrics-exporter/pkg/exporter/execution/jobs" @@ -28,18 +29,18 @@ type metrics struct { } // NewMetrics creates a new execution Metrics instance -func NewMetrics(client *ethclient.Client, internalApi api.ExecutionClient, log logrus.FieldLogger, nodeName, namespace string, enabledModules []string) Metrics { +func NewMetrics(client *ethclient.Client, internalApi api.ExecutionClient, ethRpcClient *ethrpc.EthRPC, log logrus.FieldLogger, nodeName, namespace string, enabledModules []string) Metrics { constLabels := make(prometheus.Labels) constLabels["ethereum_role"] = "execution" constLabels["node_name"] = nodeName m := &metrics{ log: log, - generalMetrics: jobs.NewGeneralMetrics(client, internalApi, log, namespace, constLabels), - syncMetrics: jobs.NewSyncStatus(client, internalApi, log, namespace, constLabels), - txpoolMetrics: jobs.NewTXPool(client, internalApi, log, namespace, constLabels), - adminMetrics: jobs.NewAdmin(client, internalApi, log, namespace, constLabels), - blockMetrics: jobs.NewBlockMetrics(client, internalApi, log, namespace, constLabels), + generalMetrics: jobs.NewGeneralMetrics(client, internalApi, ethRpcClient, log, namespace, constLabels), + syncMetrics: jobs.NewSyncStatus(client, internalApi, ethRpcClient, log, namespace, constLabels), + txpoolMetrics: jobs.NewTXPool(client, internalApi, ethRpcClient, log, namespace, constLabels), + adminMetrics: jobs.NewAdmin(client, internalApi, ethRpcClient, log, namespace, constLabels), + blockMetrics: jobs.NewBlockMetrics(client, internalApi, ethRpcClient, log, namespace, constLabels), enabledJobs: make(map[string]bool), } @@ -75,6 +76,8 @@ func NewMetrics(client *ethclient.Client, internalApi api.ExecutionClient, log l prometheus.MustRegister(m.blockMetrics.HeadGasUsed) prometheus.MustRegister(m.blockMetrics.HeadTransactionCount) prometheus.MustRegister(m.blockMetrics.HeadBaseFeePerGas) + prometheus.MustRegister(m.blockMetrics.HeadTotalDifficulty) + prometheus.MustRegister(m.blockMetrics.HeadTotalDifficultyTrillions) prometheus.MustRegister(m.blockMetrics.SafeBaseFeePerGas) prometheus.MustRegister(m.blockMetrics.SafeBlockSize)