Compare commits

...

17 Commits

Author SHA1 Message Date
nisdas
ed26181236 Add Readme 2024-06-24 16:18:47 +08:00
nisdas
96c0e15c13 Update Dashboard 2024-06-24 16:09:34 +08:00
nisdas
287d247322 Lint 2024-06-24 12:16:27 +08:00
nisdas
47ab93d594 Add new builder metric 2024-06-24 09:55:25 +08:00
nisdas
8a0aa474a6 Add new builder metric 2024-06-24 08:37:24 +08:00
nisdas
048c8de4a7 Terence's Request 2024-06-23 22:57:08 +08:00
nisdas
5877eaa8d0 Add ZkSync 2024-06-23 22:25:58 +08:00
nisdas
d5c4e91147 Add dashboard 2024-06-23 15:15:58 +08:00
nisdas
152d72af4b Add new blobs gauge 2024-06-23 12:48:22 +08:00
nisdas
cffa012852 Change to Counter 2024-06-23 12:44:47 +08:00
nisdas
4f8c595b52 Include Block Metric 2024-06-23 12:42:40 +08:00
nisdas
03555a15cb Add Error 2024-06-22 19:57:13 +08:00
nisdas
f7dc13d10a Formatting 2024-06-22 19:55:22 +08:00
nisdas
bb12cd169b Add Linea 2024-06-22 19:47:02 +08:00
nisdas
2401b8a13b Reduce Cardinality 2024-06-22 19:34:03 +08:00
nisdas
4f172eae68 Add Metrics Server 2024-06-22 19:23:49 +08:00
nisdas
ce6c9907c4 Add Blobwatcher 2024-06-22 18:12:09 +08:00
5 changed files with 1078 additions and 0 deletions

View File

@@ -0,0 +1,32 @@
load("@io_bazel_rules_go//go:def.bzl", "go_binary")
load("@prysm//tools/go:def.bzl", "go_library")
go_library(
name = "go_default_library",
srcs = [
"main.go",
"metrics.go",
],
importpath = "github.com/prysmaticlabs/prysm/v5/tools/blob-watcher",
visibility = ["//visibility:private"],
deps = [
"@com_github_ethereum_go_ethereum//common:go_default_library",
"@com_github_ethereum_go_ethereum//common/hexutil:go_default_library",
"@com_github_ethereum_go_ethereum//consensus/misc/eip4844:go_default_library",
"@com_github_ethereum_go_ethereum//core/types:go_default_library",
"@com_github_ethereum_go_ethereum//ethclient:go_default_library",
"@com_github_ethereum_go_ethereum//ethclient/gethclient:go_default_library",
"@com_github_ethereum_go_ethereum//params:go_default_library",
"@com_github_ethereum_go_ethereum//rpc:go_default_library",
"@com_github_prometheus_client_golang//prometheus:go_default_library",
"@com_github_prometheus_client_golang//prometheus/promauto:go_default_library",
"@com_github_prometheus_client_golang//prometheus/promhttp:go_default_library",
"@com_github_sirupsen_logrus//:go_default_library",
],
)
go_binary(
name = "blob-watcher",
embed = [":go_default_library"],
visibility = ["//visibility:public"],
)

View File

@@ -0,0 +1,26 @@
# BlobWatcher
Blobwatcher is a tool to monitor your execution client's mempool for blob transactions
and determine how long it takes for them to get included. Data tracked by the tool:
- BaseFee monitoring for both blobs and the network
- Users propagating blob transactions along with the appropriate labelling for popular rollups.
- Builder Monitoring by blob transactions included
- Transaction Pool Monitoring For Blobs
This tool currently only works using a websocket endpoint.
```
bazel run //tools/blob-watcher:blob-watcher -- --execution-endpoint ws://localhost:8546 --metrics-endpoint localhost:8080
```
Flags:
```
-execution-endpoint string
Path to webscocket endpoint for execution client. (default "ws://localhost:8546")
-metrics-endpoint string
Path for our metrics server. (default "localhost:8080")
-origin-secret string
Origin string for websocket connection
```

View File

@@ -0,0 +1,704 @@
{
"annotations": {
"list": [
{
"builtIn": 1,
"datasource": {
"type": "grafana",
"uid": "-- Grafana --"
},
"enable": true,
"hide": true,
"iconColor": "rgba(0, 211, 255, 1)",
"name": "Annotations & Alerts",
"type": "dashboard"
}
]
},
"editable": true,
"fiscalYearStartMonth": 0,
"graphTooltip": 0,
"id": 1,
"links": [],
"panels": [
{
"datasource": {
"type": "prometheus",
"uid": "cdpm0r0qz6328a"
},
"fieldConfig": {
"defaults": {
"color": {
"mode": "palette-classic"
},
"custom": {
"axisBorderShow": false,
"axisCenteredZero": false,
"axisColorMode": "text",
"axisLabel": "",
"axisPlacement": "auto",
"barAlignment": 0,
"drawStyle": "points",
"fillOpacity": 0,
"gradientMode": "none",
"hideFrom": {
"legend": false,
"tooltip": false,
"viz": false
},
"insertNulls": false,
"lineInterpolation": "linear",
"lineWidth": 1,
"pointSize": 5,
"scaleDistribution": {
"type": "linear"
},
"showPoints": "auto",
"spanNulls": false,
"stacking": {
"group": "A",
"mode": "none"
},
"thresholdsStyle": {
"mode": "off"
}
},
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
},
{
"color": "red",
"value": 80
}
]
}
},
"overrides": []
},
"gridPos": {
"h": 8,
"w": 12,
"x": 0,
"y": 0
},
"id": 5,
"options": {
"legend": {
"calcs": [],
"displayMode": "list",
"placement": "bottom",
"showLegend": true
},
"tooltip": {
"maxHeight": 600,
"mode": "single",
"sort": "none"
}
},
"targets": [
{
"datasource": {
"type": "prometheus",
"uid": "cdpm0r0qz6328a"
},
"editorMode": "code",
"expr": "increase(blob_inclusion_by_builder[30s]) / increase(builder_blocks[30s])",
"instant": false,
"interval": "",
"legendFormat": "{{builder}}",
"range": true,
"refId": "A"
}
],
"title": "Builder Blob Inclusion",
"type": "timeseries"
},
{
"datasource": {
"type": "prometheus",
"uid": "cdpm0r0qz6328a"
},
"fieldConfig": {
"defaults": {
"color": {
"mode": "palette-classic"
},
"custom": {
"axisBorderShow": false,
"axisCenteredZero": false,
"axisColorMode": "text",
"axisLabel": "",
"axisPlacement": "auto",
"barAlignment": 0,
"drawStyle": "points",
"fillOpacity": 0,
"gradientMode": "none",
"hideFrom": {
"legend": false,
"tooltip": false,
"viz": false
},
"insertNulls": false,
"lineInterpolation": "linear",
"lineWidth": 1,
"pointSize": 2,
"scaleDistribution": {
"type": "linear"
},
"showPoints": "auto",
"spanNulls": false,
"stacking": {
"group": "A",
"mode": "none"
},
"thresholdsStyle": {
"mode": "off"
}
},
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
},
{
"color": "red",
"value": 80
}
]
}
},
"overrides": []
},
"gridPos": {
"h": 8,
"w": 12,
"x": 12,
"y": 0
},
"id": 6,
"options": {
"legend": {
"calcs": [],
"displayMode": "list",
"placement": "bottom",
"showLegend": true
},
"tooltip": {
"maxHeight": 600,
"mode": "single",
"sort": "none"
}
},
"targets": [
{
"datasource": {
"type": "prometheus",
"uid": "cdpm0r0qz6328a"
},
"editorMode": "code",
"expr": "avg by (builder) (blob_inclusion_by_builder / builder_blocks)",
"instant": false,
"interval": "",
"legendFormat": "Average-{{builder}}",
"range": true,
"refId": "A"
}
],
"title": "Average Builder Blob Inclusion",
"type": "timeseries"
},
{
"datasource": {
"type": "prometheus",
"uid": "cdpm0r0qz6328a"
},
"description": "",
"fieldConfig": {
"defaults": {
"color": {
"mode": "palette-classic"
},
"custom": {
"axisBorderShow": false,
"axisCenteredZero": false,
"axisColorMode": "text",
"axisLabel": "",
"axisPlacement": "auto",
"barAlignment": 0,
"drawStyle": "line",
"fillOpacity": 0,
"gradientMode": "none",
"hideFrom": {
"legend": false,
"tooltip": false,
"viz": false
},
"insertNulls": false,
"lineInterpolation": "linear",
"lineWidth": 1,
"pointSize": 5,
"scaleDistribution": {
"type": "linear"
},
"showPoints": "auto",
"spanNulls": false,
"stacking": {
"group": "A",
"mode": "none"
},
"thresholdsStyle": {
"mode": "off"
}
},
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
},
{
"color": "red",
"value": 80
}
]
}
},
"overrides": []
},
"gridPos": {
"h": 8,
"w": 12,
"x": 0,
"y": 8
},
"id": 4,
"options": {
"legend": {
"calcs": [],
"displayMode": "list",
"placement": "bottom",
"showLegend": true
},
"tooltip": {
"maxHeight": 600,
"mode": "single",
"sort": "none"
}
},
"targets": [
{
"datasource": {
"type": "prometheus",
"uid": "cdpm0r0qz6328a"
},
"editorMode": "code",
"expr": "viable_transaction",
"instant": false,
"legendFormat": "Viable Transactions",
"range": true,
"refId": "A"
},
{
"datasource": {
"type": "prometheus",
"uid": "cdpm0r0qz6328a"
},
"editorMode": "code",
"expr": "viable_blobs",
"hide": false,
"instant": false,
"interval": "",
"legendFormat": "Viable Blobs",
"range": true,
"refId": "D"
},
{
"datasource": {
"type": "prometheus",
"uid": "cdpm0r0qz6328a"
},
"editorMode": "code",
"expr": "increase(transaction_inclusion[30s])/increase(block_number[30s])",
"hide": false,
"instant": false,
"interval": "",
"legendFormat": "Transactions Included In Block",
"range": true,
"refId": "B"
},
{
"datasource": {
"type": "prometheus",
"uid": "cdpm0r0qz6328a"
},
"editorMode": "code",
"expr": "increase(blob_inclusion[30s])/increase(block_number[30s])",
"hide": false,
"instant": false,
"interval": "",
"legendFormat": "Blobs Included In Block",
"range": true,
"refId": "C"
}
],
"title": "Blob Mempool",
"type": "timeseries"
},
{
"datasource": {
"type": "prometheus",
"uid": "cdpm0r0qz6328a"
},
"description": "",
"fieldConfig": {
"defaults": {
"color": {
"mode": "palette-classic"
},
"custom": {
"axisBorderShow": false,
"axisCenteredZero": false,
"axisColorMode": "text",
"axisLabel": "",
"axisPlacement": "auto",
"barAlignment": 0,
"drawStyle": "bars",
"fillOpacity": 0,
"gradientMode": "none",
"hideFrom": {
"legend": false,
"tooltip": false,
"viz": false
},
"insertNulls": false,
"lineInterpolation": "linear",
"lineWidth": 1,
"pointSize": 5,
"scaleDistribution": {
"type": "linear"
},
"showPoints": "auto",
"spanNulls": false,
"stacking": {
"group": "A",
"mode": "none"
},
"thresholdsStyle": {
"mode": "off"
}
},
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
},
{
"color": "red",
"value": 80
}
]
}
},
"overrides": []
},
"gridPos": {
"h": 8,
"w": 12,
"x": 12,
"y": 8
},
"id": 1,
"options": {
"legend": {
"calcs": [],
"displayMode": "list",
"placement": "bottom",
"showLegend": true
},
"tooltip": {
"maxHeight": 600,
"mode": "single",
"sort": "none"
}
},
"pluginVersion": "11.0.0",
"targets": [
{
"datasource": {
"type": "prometheus",
"uid": "cdpm0r0qz6328a"
},
"editorMode": "code",
"exemplar": false,
"expr": "increase(transaction_inclusion_delay_sum{account=\"$Account\"}[30s])/increase(transaction_inclusion_delay_count{account=\"$Account\"}[30s])",
"format": "time_series",
"instant": false,
"interval": "",
"legendFormat": "$Account",
"range": true,
"refId": "A"
}
],
"title": "Blob Transaction Inclusion Delay",
"type": "timeseries"
},
{
"datasource": {
"type": "prometheus",
"uid": "cdpm0r0qz6328a"
},
"description": "",
"fieldConfig": {
"defaults": {
"color": {
"mode": "palette-classic"
},
"custom": {
"axisBorderShow": false,
"axisCenteredZero": false,
"axisColorMode": "text",
"axisLabel": "",
"axisPlacement": "auto",
"barAlignment": 0,
"drawStyle": "line",
"fillOpacity": 0,
"gradientMode": "none",
"hideFrom": {
"legend": false,
"tooltip": false,
"viz": false
},
"insertNulls": false,
"lineInterpolation": "linear",
"lineWidth": 1,
"pointSize": 5,
"scaleDistribution": {
"type": "linear"
},
"showPoints": "auto",
"spanNulls": false,
"stacking": {
"group": "A",
"mode": "none"
},
"thresholdsStyle": {
"mode": "off"
}
},
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
},
{
"color": "red",
"value": 80
}
]
}
},
"overrides": []
},
"gridPos": {
"h": 8,
"w": 12,
"x": 0,
"y": 16
},
"id": 3,
"options": {
"legend": {
"calcs": [],
"displayMode": "list",
"placement": "bottom",
"showLegend": true
},
"tooltip": {
"maxHeight": 600,
"mode": "single",
"sort": "none"
}
},
"targets": [
{
"datasource": {
"type": "prometheus",
"uid": "cdpm0r0qz6328a"
},
"editorMode": "code",
"expr": "blob_base_fee",
"instant": false,
"legendFormat": "Blob Base Fee",
"range": true,
"refId": "A"
}
],
"title": "Blob Base Fee",
"type": "timeseries"
},
{
"datasource": {
"type": "prometheus",
"uid": "cdpm0r0qz6328a"
},
"description": "",
"fieldConfig": {
"defaults": {
"color": {
"mode": "palette-classic"
},
"custom": {
"axisBorderShow": false,
"axisCenteredZero": false,
"axisColorMode": "text",
"axisLabel": "",
"axisPlacement": "auto",
"barAlignment": 0,
"drawStyle": "bars",
"fillOpacity": 0,
"gradientMode": "none",
"hideFrom": {
"legend": false,
"tooltip": false,
"viz": false
},
"insertNulls": false,
"lineInterpolation": "linear",
"lineWidth": 1,
"pointSize": 2,
"scaleDistribution": {
"type": "linear"
},
"showPoints": "auto",
"spanNulls": false,
"stacking": {
"group": "A",
"mode": "none"
},
"thresholdsStyle": {
"mode": "off"
}
},
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
},
{
"color": "red",
"value": 80
}
]
}
},
"overrides": []
},
"gridPos": {
"h": 8,
"w": 12,
"x": 12,
"y": 16
},
"id": 2,
"options": {
"legend": {
"calcs": [],
"displayMode": "list",
"placement": "bottom",
"showLegend": true
},
"tooltip": {
"maxHeight": 600,
"mode": "single",
"sort": "none"
}
},
"targets": [
{
"datasource": {
"type": "prometheus",
"uid": "cdpm0r0qz6328a"
},
"editorMode": "code",
"expr": "increase(transactions_observed{account=\"$Account\"}[30s])",
"instant": false,
"interval": "",
"legendFormat": "$Account-blobFee - {{maxBlobBaseFee}}",
"range": true,
"refId": "A"
}
],
"title": "Transaction Blob Fee",
"type": "timeseries"
}
],
"refresh": "",
"schemaVersion": 39,
"tags": [],
"templating": {
"list": [
{
"current": {
"selected": true,
"text": [
"Arbitrum"
],
"value": [
"Arbitrum"
]
},
"datasource": {
"type": "prometheus",
"uid": "cdpm0r0qz6328a"
},
"definition": "label_values(account)",
"hide": 0,
"includeAll": false,
"label": "",
"multi": true,
"name": "Account",
"options": [],
"query": {
"qryType": 1,
"query": "label_values(account)",
"refId": "PrometheusVariableQueryEditor-VariableQuery"
},
"refresh": 1,
"regex": "",
"skipUrlSync": false,
"sort": 0,
"type": "query"
}
]
},
"time": {
"from": "now-1h",
"to": "now"
},
"timeRangeUpdatedDuringEditOrView": false,
"timepicker": {},
"timezone": "browser",
"title": "BlobWatcher Dashboard",
"uid": "bdpm1e8v5m48we",
"version": 18,
"weekStart": ""
}

236
tools/blob-watcher/main.go Normal file
View File

@@ -0,0 +1,236 @@
package main
import (
"context"
"flag"
"fmt"
"math/big"
"strings"
"time"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/consensus/misc/eip4844"
gethtypes "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/ethclient"
"github.com/ethereum/go-ethereum/ethclient/gethclient"
"github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/rpc"
log "github.com/sirupsen/logrus"
)
var (
// Required fields
executionEndpoint = flag.String("execution-endpoint", "ws://localhost:8546", "Path to webscocket endpoint for execution client.")
wsOrigin = flag.String("origin-secret", "", "Origin string for websocket connection")
metricsEndpoint = flag.String("metrics-endpoint", "localhost:8080", "Path for our metrics server.")
)
func main() {
flag.Parse()
log.Info("Starting blob watcher service")
log.Infof("Using websocket endpoint of %s", *executionEndpoint)
srv := StartMetricsServer(*metricsEndpoint)
defer func() {
if err := srv.Close(); err != nil {
log.Error(err)
}
}()
client, err := rpc.DialWebsocket(context.Background(), *executionEndpoint, *wsOrigin)
if err != nil {
log.Fatal(err)
}
ec := ethclient.NewClient(client)
gc := gethclient.New(client)
txChan := make(chan *gethtypes.Transaction, 100)
pSub, err := gc.SubscribeFullPendingTransactions(context.Background(), txChan)
if err != nil {
log.Fatal(err)
}
hdrChan := make(chan *gethtypes.Header, 100)
hSub, err := ec.SubscribeNewHead(context.Background(), hdrChan)
if err != nil {
log.Fatal(err)
}
chainID, err := ec.ChainID(context.Background())
if err != nil {
log.Fatal(err)
}
currBaseFee := new(big.Int)
pendingTxs := make(map[common.Hash]*gethtypes.Transaction)
txTime := make(map[common.Hash]time.Time)
for {
select {
case err := <-pSub.Err():
log.WithError(err).Error("Pending transaction subscription error")
ec.Close()
client.Close()
close(txChan)
close(hdrChan)
hSub.Unsubscribe()
return
case <-hSub.Err():
log.WithError(err).Error("New head subscription error")
ec.Close()
client.Close()
close(txChan)
close(hdrChan)
pSub.Unsubscribe()
return
case tx := <-txChan:
if tx.Type() == gethtypes.BlobTxType {
tHash := tx.Hash()
log.WithFields(txData(tx, chainID)).Infof("Received new Transaction from Gossip")
recordTxMetrics(tx, chainID)
pendingTxs[tHash] = tx
txTime[tHash] = time.Now()
}
case h := <-hdrChan:
if h.ExcessBlobGas != nil {
currBaseFee = eip4844.CalcBlobFee(*h.ExcessBlobGas)
}
log.Infof("*/-------------------------------------------------------------------------------------------------------------------------------------------------------------------*/")
log.WithFields(log.Fields{
"blockHash": h.Hash(),
"blockNumber": h.Number.Uint64(),
"blockTime": h.Time,
"blobBaseFee(wei)": currBaseFee.Uint64(),
"baseFee(Gwei)": float64(h.BaseFee.Uint64()) / params.GWei,
"builder": strings.ToValidUTF8(string(h.Extra), ""),
}).Infof("Received new block")
blockNumberGauge.Set(float64(h.Number.Uint64()))
blobBaseFeeGauge.Set(float64(currBaseFee.Uint64()))
currentPendingTxs := len(pendingTxs)
blobsIncluded := 0
viabletxs := 0
viableBlobs := 0
for hash, tx := range pendingTxs {
r, err := ec.TransactionReceipt(context.Background(), hash)
if err == nil && r.BlockHash == h.Hash() {
log.WithFields(txData(tx, chainID)).Infof("Transaction was included in block %d in %s", r.BlockNumber.Uint64(), time.Since(txTime[hash]))
recordTxInclusion(tx, chainID, time.Since(txTime[hash]))
blobsIncluded += len(tx.BlobHashes())
delete(pendingTxs, hash)
delete(txTime, hash)
continue
}
acc, err := gethtypes.Sender(gethtypes.NewCancunSigner(chainID), tx)
if err != nil {
log.WithError(err).Error("Could not get sender's account address")
continue
}
currNonce, err := ec.NonceAtHash(context.Background(), acc, h.Hash())
if err != nil {
log.WithError(err).Error("Could not get sender's account nonce")
continue
}
if tx.Nonce() < currNonce {
log.WithFields(txData(tx, chainID)).Infof("Transaction has been successfully replaced and included on chain in %s", time.Since(txTime[hash]))
delete(pendingTxs, hash)
delete(txTime, hash)
continue
}
if tx.Nonce() != currNonce {
// This is not an immediate transaction that can be included.
continue
}
if tx.BlobGasFeeCap().Cmp(currBaseFee) >= 0 {
viabletxs++
viableBlobs += len(tx.BlobHashes())
log.WithFields(txData(tx, chainID)).Infof("Transaction was still not included after %s", time.Since(txTime[hash]))
}
}
pendingTransactionGauge.Set(float64(len(pendingTxs)))
viableTransactionGauge.Set(float64(viabletxs))
viableBlobsGauge.Set(float64(viableBlobs))
transactionInclusionCounter.Add(float64(currentPendingTxs - len(pendingTxs)))
blobInclusionCounter.Add(float64(blobsIncluded))
blobInclusionBuilderCounter.WithLabelValues(strings.ToValidUTF8(string(h.Extra), "")).Add(float64(blobsIncluded))
builderCounter.WithLabelValues(strings.ToValidUTF8(string(h.Extra), "")).Add(1)
log.WithFields(log.Fields{
"previousPendingTxs": currentPendingTxs,
"currentPendingTxs": len(pendingTxs),
"viableTxs": viabletxs,
}).Infof("Post block Summary for blob transactions")
log.Infof("*/-------------------------------------------------------------------------------------------------------------------------------------------------------------------*/")
}
}
}
func txData(tx *gethtypes.Transaction, chainID *big.Int) log.Fields {
acc, err := gethtypes.Sender(gethtypes.NewCancunSigner(chainID), tx)
if err != nil {
log.WithError(err).Error("Could not get sender's account address")
return nil
}
accName := acc.String()
if name, ok := accountLabels[[20]byte(acc.Bytes())]; ok {
accName = name
}
return log.Fields{
"TxHash": tx.Hash(),
"BlobGasFeeCap(Gwei)": float64(tx.BlobGasFeeCap().Uint64()) / params.GWei,
"BlobGas": tx.BlobGas(),
"BlobCount": len(tx.BlobHashes()),
"GasFeeCap(Gwei)": float64(tx.GasFeeCap().Uint64()) / params.GWei,
"GasTipCap(Gwei)": float64(tx.GasTipCap().Uint64()) / params.GWei,
"Gas": tx.Gas(),
"Account": accName,
}
}
var accountLabels = map[[20]byte]string{
mustDecode("0xc1b634853cb333d3ad8663715b08f41a3aec47cc"): "Arbitrum",
mustDecode("0x6887246668a3b87f54deb3b94ba47a6f63f32985"): "Optimism",
mustDecode("0x5050f69a9786f081509234f1a7f4684b5e5b76c9"): "Base",
mustDecode("0x000000633b68f5d8d3a86593ebb815b4663bcbe0"): "Taiko",
mustDecode("0x2c169dfe5fbba12957bdd0ba47d9cedbfe260ca7"): "Starknet",
mustDecode("0x0D3250c3D5FAcb74Ac15834096397a3Ef790ec99"): "ZkSync",
mustDecode("0xcf2898225ed05be911d3709d9417e86e0b4cfc8f"): "Scroll",
mustDecode("0x415c8893d514f9bc5211d36eeda4183226b84aa7"): "Blast",
mustDecode("0xa9268341831efa4937537bc3e9eb36dbece83c7e"): "Linea",
}
func mustDecode(address string) [20]byte {
byteAddr := hexutil.MustDecode(address)
return [20]byte(byteAddr)
}
func recordTxMetrics(tx *gethtypes.Transaction, chainID *big.Int) {
acc, err := gethtypes.Sender(gethtypes.NewCancunSigner(chainID), tx)
if err != nil {
log.WithError(err).Error("Could not get sender's account address")
return
}
accName := acc.String()
if name, ok := accountLabels[[20]byte(acc.Bytes())]; ok {
accName = name
}
transactionsObservedGauge.WithLabelValues(accName, fmt.Sprintf("%d", len(tx.BlobHashes())), fmt.Sprintf("%d", tx.BlobGasFeeCap().Uint64())).Inc()
}
func recordTxInclusion(tx *gethtypes.Transaction, chainID *big.Int, inclusionDelay time.Duration) {
acc, err := gethtypes.Sender(gethtypes.NewCancunSigner(chainID), tx)
if err != nil {
log.WithError(err).Error("Could not get sender's account address")
return
}
accName := acc.String()
if name, ok := accountLabels[[20]byte(acc.Bytes())]; ok {
accName = name
}
transactionInclusionDelay.WithLabelValues(accName, fmt.Sprintf("%d", len(tx.BlobHashes()))).Observe(inclusionDelay.Seconds())
}

View File

@@ -0,0 +1,80 @@
package main
import (
"net/http"
"time"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promauto"
"github.com/prometheus/client_golang/prometheus/promhttp"
log "github.com/sirupsen/logrus"
)
var (
transactionsObservedGauge = promauto.NewGaugeVec(prometheus.GaugeOpts{
Name: "transactions_observed",
Help: "Count the number of blob transactions observed in your local mempool",
}, []string{"account", "blobCount", "maxBlobBaseFee"})
transactionInclusionDelay = promauto.NewHistogramVec(
prometheus.HistogramOpts{
Name: "transaction_inclusion_delay",
Help: "The number of seconds it takes to include a blob transaction on chain",
Buckets: []float64{1, 2, 16, 32, 64, 128, 256, 512, 1024},
},
[]string{"account", "blobCount"},
)
blockNumberGauge = promauto.NewGauge(prometheus.GaugeOpts{
Name: "block_number",
Help: "The current block number in your execution client",
})
blobBaseFeeGauge = promauto.NewGauge(prometheus.GaugeOpts{
Name: "blob_base_fee",
Help: "The blob base fee",
})
pendingTransactionGauge = promauto.NewGauge(prometheus.GaugeOpts{
Name: "pending_transactions",
Help: "The current number of pending transactions in the mempool",
})
viableTransactionGauge = promauto.NewGauge(prometheus.GaugeOpts{
Name: "viable_transaction",
Help: "The current number of viable transactions in the mempool",
})
viableBlobsGauge = promauto.NewGauge(prometheus.GaugeOpts{
Name: "viable_blobs",
Help: "The current number of viable blobs in the mempool",
})
transactionInclusionCounter = promauto.NewCounter(prometheus.CounterOpts{
Name: "transaction_inclusion",
Help: "The current number of transactions included in a block",
})
blobInclusionCounter = promauto.NewCounter(prometheus.CounterOpts{
Name: "blob_inclusion",
Help: "The number of blobs included on chain via a transaction",
})
blobInclusionBuilderCounter = promauto.NewCounterVec(prometheus.CounterOpts{
Name: "blob_inclusion_by_builder",
Help: "The number of blobs included on chain via a transaction by builder",
}, []string{"builder"})
builderCounter = promauto.NewCounterVec(prometheus.CounterOpts{
Name: "builder_blocks",
Help: "The number of blocks built by a builder",
}, []string{"builder"})
)
func StartMetricsServer(addr string) *http.Server {
mux := http.NewServeMux()
mux.Handle("/metrics", promhttp.HandlerFor(prometheus.DefaultGatherer, promhttp.HandlerOpts{
MaxRequestsInFlight: 5,
Timeout: 30 * time.Second,
}))
srv := &http.Server{Addr: addr, Handler: mux, ReadHeaderTimeout: time.Second}
log.WithField("address", srv.Addr).Debug("Starting prometheus server")
go func() {
err := srv.ListenAndServe()
if err != nil && err != http.ErrServerClosed {
log.WithError(err).Fatalf("Could not listen to host:port :%s", srv.Addr)
}
}()
return srv
}