Skip to content

Commit a8f829b

Browse files
committed
feat: add txpool-viz
1 parent 3f60fa8 commit a8f829b

File tree

8 files changed

+249
-7
lines changed

8 files changed

+249
-7
lines changed

README.md

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -758,6 +758,7 @@ additional_services:
758758
- forky
759759
- apache
760760
- tracoor
761+
- txpool_viz
761762

762763
# Configuration place for blockscout explorer - https://github.com/blockscout/blockscout
763764
blockscout_params:
@@ -1058,6 +1059,20 @@ spamoor_params:
10581059
# A list of optional params that will be passed to the spamoor command for modifying its behaviour
10591060
extra_args: []
10601061

1062+
# Configuration for txpool_viz. A mempool visualizer.
1063+
txpool_viz_params:
1064+
# The image to use for txpool_viz
1065+
image: punkhazardlabs/txpool-viz:latest
1066+
# Polling configuration for txpool_viz
1067+
polling:
1068+
interval: 0.1s # How often to poll for new mempool data (in seconds)
1069+
timeout: 5s # Timeout for polling requests (in seconds)
1070+
# Filters to apply to the mempool data
1071+
filters:
1072+
min_gas_price: 1gwei # Minimum gas price filter for transactions
1073+
focil_enabled: "true" # Enable or disable FOCIL (set to "true" to enable)
1074+
log_level: "info" # Logging level for txpool_viz (e.g., "info", "debug", "warn", "error")
1075+
10611076
# Ethereum genesis generator params
10621077
ethereum_genesis_generator_params:
10631078
# The image to use for ethereum genesis generator
@@ -1070,7 +1085,7 @@ port_publisher:
10701085
# Set to "auto" to automatically detect public IP from ident.me
10711086
# Defaults to KURTOSIS_IP_ADDR_PLACEHOLDER (uses per-service settings)
10721087
nat_exit_ip: KURTOSIS_IP_ADDR_PLACEHOLDER
1073-
1088+
10741089
# Execution Layer public port exposed to your local machine
10751090
# Disabled by default
10761091
# Public port start defaults to 32000
@@ -1083,7 +1098,7 @@ port_publisher:
10831098
# Set to "auto" to automatically detect public IP from ident.me
10841099
# Defaults to KURTOSIS_IP_ADDR_PLACEHOLDER (container IP)
10851100
nat_exit_ip: KURTOSIS_IP_ADDR_PLACEHOLDER
1086-
1101+
10871102
# Consensus Layer public port exposed to your local machine
10881103
# Disabled by default
10891104
# Public port start defaults to 33000
@@ -1096,7 +1111,7 @@ port_publisher:
10961111
# Set to "auto" to automatically detect public IP from ident.me
10971112
# Defaults to KURTOSIS_IP_ADDR_PLACEHOLDER (container IP)
10981113
nat_exit_ip: KURTOSIS_IP_ADDR_PLACEHOLDER
1099-
1114+
11001115
# Validator client public port exposed to your local machine
11011116
# Disabled by default
11021117
# Public port start defaults to 34000
@@ -1109,7 +1124,7 @@ port_publisher:
11091124
# Set to "auto" to automatically detect public IP from ident.me
11101125
# Defaults to KURTOSIS_IP_ADDR_PLACEHOLDER (container IP)
11111126
nat_exit_ip: KURTOSIS_IP_ADDR_PLACEHOLDER
1112-
1127+
11131128
# remote signer public port exposed to your local machine
11141129
# Disabled by default
11151130
# Public port start defaults to 35000
@@ -1122,7 +1137,7 @@ port_publisher:
11221137
# Set to "auto" to automatically detect public IP from ident.me
11231138
# Defaults to KURTOSIS_IP_ADDR_PLACEHOLDER (container IP)
11241139
nat_exit_ip: KURTOSIS_IP_ADDR_PLACEHOLDER
1125-
1140+
11261141
# Additional services public port exposed to your local machine
11271142
# Disabled by default
11281143
# Public port start defaults to 36000

main.star

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ get_prefunded_accounts = import_module(
5656
"./src/prefunded_accounts/get_prefunded_accounts.star"
5757
)
5858
spamoor = import_module("./src/spamoor/spamoor.star")
59+
txpool_viz = import_module("./src/txpool_viz/txpool_viz.star")
5960

6061
GRAFANA_USER = "admin"
6162
GRAFANA_PASSWORD = "admin"
@@ -467,6 +468,18 @@ def run(plan, args={}):
467468
global_node_selectors,
468469
)
469470
plan.print("Successfully launched tx-fuzz")
471+
elif additional_service == "txpool_viz":
472+
plan.print("Launching txpool-viz")
473+
txpool_viz_config_template = read_file(
474+
static_files.TXPOOL_VIZ_CONFIG_TEMPLATE_FILEPATH
475+
)
476+
txpool_viz.launch_txpool_viz(
477+
plan,
478+
txpool_viz_config_template,
479+
all_participants,
480+
args_with_right_defaults.txpool_viz_params,
481+
global_node_selectors
482+
)
470483
elif additional_service == "forkmon":
471484
plan.print("Launching el forkmon")
472485
forkmon_config_template = read_file(

src/package_io/constants.star

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,7 @@ MEV_RS_MEV_TYPE = "mev-rs"
9393
COMMIT_BOOST_MEV_TYPE = "commit-boost"
9494
DEFAULT_DORA_IMAGE = "ethpandaops/dora:latest"
9595
DEFAULT_SPAMOOR_IMAGE = "ethpandaops/spamoor:latest"
96+
DEFAULT_TXPOOL_VIZ_IMAGE = "punkhazardlabs/txpool-viz:latest"
9697
DEFAULT_ASSERTOOR_IMAGE = "ethpandaops/assertoor:latest"
9798
DEFAULT_SNOOPER_IMAGE = "ethpandaops/rpc-snooper:latest"
9899
DEFAULT_ETHEREUM_GENESIS_GENERATOR_IMAGE = (

src/package_io/input_parser.star

Lines changed: 53 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ ATTR_TO_BE_SKIPPED_AT_ROOT = (
7474
"dora_params",
7575
"docker_cache_params",
7676
"assertoor_params",
77+
"txpool_viz_params",
7778
"prometheus_params",
7879
"grafana_params",
7980
"tx_fuzz_params",
@@ -179,6 +180,8 @@ def input_parser(plan, input_args):
179180
for sub_attr in input_args["spamoor_params"]:
180181
sub_value = input_args["spamoor_params"][sub_attr]
181182
result["spamoor_params"][sub_attr] = sub_value
183+
elif attr == "txpool_viz_params":
184+
result["txpool_viz_params"] = get_txpool_viz_params(input_args)
182185
elif attr == "ethereum_genesis_generator_params":
183186
for sub_attr in input_args["ethereum_genesis_generator_params"]:
184187
sub_value = input_args["ethereum_genesis_generator_params"][sub_attr]
@@ -572,6 +575,19 @@ def input_parser(plan, input_args):
572575
spammers=result["spamoor_params"]["spammers"],
573576
extra_args=result["spamoor_params"]["extra_args"],
574577
),
578+
txpool_viz_params=struct(
579+
image=result["txpool_viz_params"]["image"],
580+
min_cpu=result["txpool_viz_params"]["min_cpu"],
581+
max_cpu=result["txpool_viz_params"]["max_cpu"],
582+
min_mem=result["txpool_viz_params"]["min_mem"],
583+
max_mem=result["txpool_viz_params"]["max_mem"],
584+
extra_args=result["txpool_viz_params"]["extra_args"],
585+
polling=result["txpool_viz_params"]["polling"],
586+
filters=result["txpool_viz_params"]["filters"],
587+
focil_enabled=result["txpool_viz_params"]["focil_enabled"],
588+
log_level=result["txpool_viz_params"]["log_level"],
589+
env=result["txpool_viz_params"]["env"],
590+
),
575591
additional_services=result["additional_services"],
576592
wait_for_finalization=result["wait_for_finalization"],
577593
global_log_level=result["global_log_level"],
@@ -1698,6 +1714,7 @@ def docker_cache_image_override(plan, result):
16981714
"prometheus_params.image",
16991715
"grafana_params.image",
17001716
"spamoor_params.image",
1717+
"txpool_viz_params.image",
17011718
"ethereum_genesis_generator_params.image",
17021719
]
17031720

@@ -1792,7 +1809,6 @@ def get_default_ethereum_genesis_generator_params():
17921809
"image": constants.DEFAULT_ETHEREUM_GENESIS_GENERATOR_IMAGE,
17931810
}
17941811

1795-
17961812
def get_devnet_image_tag(network_name, original_image):
17971813
if "devnet" not in network_name:
17981814
return original_image
@@ -1825,7 +1841,6 @@ def get_devnet_image_tag(network_name, original_image):
18251841
image_name = original_image
18261842
return "ethpandaops/{0}:{1}".format(image_name, network_name)
18271843

1828-
18291844
def get_devnet_modified_images(network_name, default_images):
18301845
if "devnet" not in network_name:
18311846
return default_images
@@ -1835,3 +1850,39 @@ def get_devnet_modified_images(network_name, default_images):
18351850
modified_images[client_type] = get_devnet_image_tag(network_name, image)
18361851

18371852
return modified_images
1853+
1854+
def get_txpool_viz_params(input_args):
1855+
image = input_args.get("txpool_viz_params", {}).get("image", constants.DEFAULT_TXPOOL_VIZ_IMAGE)
1856+
min_cpu = input_args.get("txpool_viz_params", {}).get("min_cpu", False)
1857+
max_cpu = input_args.get("txpool_viz_params", {}).get("max_cpu", False)
1858+
min_mem = input_args.get("txpool_viz_params", {}).get("min_mem", False)
1859+
max_mem = input_args.get("txpool_viz_params", {}).get("max_mem", False)
1860+
extra_args = input_args.get("txpool_viz_params", {}).get("extra_args", [])
1861+
polling_args = input_args.get("txpool_viz_params", {}).get("polling", {})
1862+
filters_args = input_args.get("txpool_viz_params", {}).get("filters", {})
1863+
focil_enabled = input_args.get("txpool_viz_params", {}).get("focil_enabled", "false")
1864+
log_level = input_args.get("txpool_viz_params", {}).get("log_level", "info")
1865+
env = input_args.get("txpool_viz_params", {}).get("env", {})
1866+
1867+
polling_config = {
1868+
"interval": polling_args.get("interval", "0.5s"),
1869+
"timeout": polling_args.get("timeout", "3s"),
1870+
}
1871+
1872+
filters_config = {
1873+
"min_gas_price": filters_args.get("min_gas_price", "1gwei"),
1874+
}
1875+
1876+
return {
1877+
"image": image,
1878+
"min_cpu": min_cpu,
1879+
"max_cpu": max_cpu,
1880+
"min_mem": min_mem,
1881+
"max_mem": max_mem,
1882+
"extra_args": extra_args,
1883+
"polling": polling_config,
1884+
"filters": filters_config,
1885+
"focil_enabled": focil_enabled,
1886+
"log_level": log_level,
1887+
"env": env,
1888+
}

src/package_io/sanity_check.star

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -299,6 +299,19 @@ SUBCATEGORY_PARAMS = {
299299
"mev",
300300
"other",
301301
],
302+
"txpool_viz_params": [
303+
"image",
304+
"min_cpu",
305+
"max_cpu",
306+
"min_mem",
307+
"max_mem",
308+
"extra_args",
309+
"polling",
310+
"filters",
311+
"focil_enabled",
312+
"log_level",
313+
"env"
314+
]
302315
}
303316

304317
ADDITIONAL_SERVICES_PARAMS = [
@@ -321,6 +334,7 @@ ADDITIONAL_SERVICES_PARAMS = [
321334
"nginx",
322335
"tracoor",
323336
"spamoor",
337+
"txpool_viz"
324338
]
325339

326340
ADDITIONAL_CATEGORY_PARAMS = {

src/static_files/static_files.star

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,3 +123,10 @@ FLASHBOTS_RBUILDER_CONFIG_FILEPATH = (
123123
COMMIT_BOOST_CONFIG_FILEPATH = (
124124
STATIC_FILES_DIRPATH + "/mev/commit-boost/cb-config.toml.tmpl"
125125
)
126+
127+
# txpool-viz config
128+
TXPOOL_VIZ_CONFIG_DIRPATH = "/txpool-viz-config"
129+
TXPOOL_VIZ_CONFIG_TEMPLATE_FILEPATH = (
130+
STATIC_FILES_DIRPATH + TXPOOL_VIZ_CONFIG_DIRPATH + "/config.yaml.tmpl"
131+
)
132+

src/txpool_viz/txpool_viz.star

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
redis_module = import_module("github.com/kurtosis-tech/redis-package/main.star")
2+
postgres_module = import_module("github.com/kurtosis-tech/postgres-package/main.star")
3+
shared_utils = import_module("../shared_utils/shared_utils.star")
4+
5+
HTTP_PORT_NUMBER= 8080
6+
TXPOOL_VIZ_SERVICE_NAME="txpool-viz"
7+
8+
TXPOOL_VIZ_CONFIG_FILENAME = "config.yaml"
9+
TXPOOL_VIZ_CONFIG_PATH="/cfg/"
10+
11+
# The min/max CPU/memory that txpool-viz can use.
12+
MIN_CPU = 100
13+
MAX_CPU = 1000
14+
MIN_MEM = 128
15+
MAX_MEM = 1024
16+
17+
def launch_txpool_viz(
18+
plan,
19+
config_template,
20+
network_participants,
21+
txpool_viz_params,
22+
global_node_selectors
23+
):
24+
endpoints_list = []
25+
for participant in network_participants:
26+
el_metrics_info = participant.el_context.el_metrics_info
27+
endpoints_list.append({
28+
"Name": el_metrics_info[0]["name"] if el_metrics_info else "unknown",
29+
"RPCUrl": participant.el_context.rpc_http_url,
30+
"Socket": participant.el_context.ws_url
31+
})
32+
33+
beacon_endpoints = []
34+
for participant in network_participants:
35+
beacon_endpoints.append({
36+
"Name": participant.cl_context.beacon_service_name,
37+
"BeaconUrl": participant.cl_context.beacon_http_url,
38+
})
39+
40+
# config data & template
41+
template_data = txpool_viz_config_template_data(txpool_viz_params, endpoints_list, beacon_endpoints)
42+
43+
template_and_data = shared_utils.new_template_and_data(
44+
config_template, template_data
45+
)
46+
47+
file_config = {}
48+
file_config[TXPOOL_VIZ_CONFIG_FILENAME] = template_and_data
49+
50+
config_files_artifact_name = plan.render_templates(
51+
config=file_config,
52+
name="txpool-viz-config",
53+
)
54+
55+
postgres = postgres_module.run(
56+
plan,
57+
service_name="txpool-viz-postgres",
58+
)
59+
60+
# attaching redis server
61+
redis = redis_module.run(
62+
plan,
63+
service_name="txpool-viz-redis",
64+
)
65+
66+
redis_url = "redis://" + redis.hostname + ":" + str(redis.port_number) + "/0"
67+
68+
environment_variables = dict(txpool_viz_params.env)
69+
environment_variables.update({
70+
"POSTGRES_URL": postgres.url,
71+
"REDIS_URL": redis_url,
72+
"PORT": str(HTTP_PORT_NUMBER),
73+
"ENV": "prod",
74+
})
75+
76+
service_config = get_service_config(environment_variables, txpool_viz_params, global_node_selectors, config_files_artifact_name)
77+
78+
txpoolviz = plan.add_service(TXPOOL_VIZ_SERVICE_NAME, config=service_config)
79+
80+
def get_service_config(environment_variables, txpool_viz_params, node_selectors, files_artifact):
81+
return ServiceConfig(
82+
image=txpool_viz_params.image,
83+
ports= {
84+
shared_utils.HTTP_APPLICATION_PROTOCOL: PortSpec(
85+
number=HTTP_PORT_NUMBER,
86+
application_protocol=shared_utils.HTTP_APPLICATION_PROTOCOL,
87+
transport_protocol=shared_utils.TCP_PROTOCOL,
88+
),
89+
},
90+
env_vars=environment_variables,
91+
min_cpu=txpool_viz_params.min_cpu or MIN_CPU,
92+
max_cpu=txpool_viz_params.max_cpu or MAX_CPU,
93+
min_memory=txpool_viz_params.min_mem or MIN_MEM,
94+
max_memory=txpool_viz_params.max_mem or MAX_MEM,
95+
files = {
96+
TXPOOL_VIZ_CONFIG_PATH : files_artifact
97+
},
98+
node_selectors=node_selectors,
99+
)
100+
101+
def txpool_viz_config_template_data(config, endpoints_list, beacon_endpoints):
102+
cfg = dict(config.extra_args)
103+
cfg.update({
104+
"Endpoints": endpoints_list,
105+
"Polling": config.polling,
106+
"Filters": config.filters,
107+
"LogLevel": config.log_level,
108+
})
109+
110+
if config.focil_enabled == "true":
111+
cfg["FocilEnabled"] = config.focil_enabled
112+
cfg["BeaconEndpoints"] = beacon_endpoints
113+
114+
return cfg
115+
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
endpoints:
2+
{{- range .Endpoints }}
3+
- name: {{ .Name }}
4+
rpc_url: "{{ .RPCUrl }}"
5+
socket: "{{ .Socket }}"
6+
{{- end }}
7+
8+
{{- if eq .FocilEnabled "true" }}
9+
focil_enabled: true
10+
beacon_urls:
11+
{{- range .BeaconEndpoints }}
12+
- name: {{ .Name }}
13+
beacon_url: "{{ .BeaconUrl }}"
14+
{{- end }}
15+
{{- end }}
16+
17+
polling:
18+
interval: {{ .Polling.interval }}
19+
timeout: {{ .Polling.timeout }}
20+
21+
filters:
22+
min_gas_price: {{ .Filters.MinGasPrice }}
23+
24+
log_level: "{{ .LogLevel }}"
25+
26+
extra_args: {{ .ExtraArgs }}

0 commit comments

Comments
 (0)