mirror of
https://github.com/OffchainLabs/prysm.git
synced 2026-01-09 21:38:05 -05:00
Compare commits
17 Commits
v6.1.1
...
blobWatche
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ed26181236 | ||
|
|
96c0e15c13 | ||
|
|
287d247322 | ||
|
|
47ab93d594 | ||
|
|
8a0aa474a6 | ||
|
|
048c8de4a7 | ||
|
|
5877eaa8d0 | ||
|
|
d5c4e91147 | ||
|
|
152d72af4b | ||
|
|
cffa012852 | ||
|
|
4f8c595b52 | ||
|
|
03555a15cb | ||
|
|
f7dc13d10a | ||
|
|
bb12cd169b | ||
|
|
2401b8a13b | ||
|
|
4f172eae68 | ||
|
|
ce6c9907c4 |
32
tools/blob-watcher/BUILD.bazel
Normal file
32
tools/blob-watcher/BUILD.bazel
Normal 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"],
|
||||
)
|
||||
26
tools/blob-watcher/README.md
Normal file
26
tools/blob-watcher/README.md
Normal 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
|
||||
|
||||
```
|
||||
704
tools/blob-watcher/dashboard/BlobWatcher Dashboard.json
Normal file
704
tools/blob-watcher/dashboard/BlobWatcher Dashboard.json
Normal 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
236
tools/blob-watcher/main.go
Normal 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())
|
||||
}
|
||||
80
tools/blob-watcher/metrics.go
Normal file
80
tools/blob-watcher/metrics.go
Normal 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
|
||||
}
|
||||
Reference in New Issue
Block a user