-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathMakefile
More file actions
363 lines (316 loc) · 16.3 KB
/
Copy pathMakefile
File metadata and controls
363 lines (316 loc) · 16.3 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
# =============================================================================
# Boost+ artifact workflow
# =============================================================================
# Quick start:
# make up build images, generate genesis, start everything
# make status one-screen health summary
# make smoke-test end-to-end validation (non-zero exit on failure)
# make down stop, keep the chain
# make clean stop AND DELETE the chain and all experiment data
# =============================================================================
SHELL := /bin/bash
.DEFAULT_GOAL := help
.PHONY: help up down restart resume freeze-at-block freeze-after-blocks clean build rebuild logs logs-% status ps \
smoke-test send-test-tx send-test-bundle send-test-mev-bundle \
cancel-test-bundle relay-blocks relay-received backtest \
mempool-data fetch candidates preflight validate-configs \
experiments traces shell-rbuilder shell-reth reth-rpc beacon-api \
metrics validators check-tools print-config status-detail
COMPOSE ?= docker compose
# Host ports (mirrors .env; used by the convenience targets below).
ifneq ($(wildcard .env),)
include .env
endif
export
PORT_RETH_HTTP ?= 8545
PORT_LIGHTHOUSE_BN_HTTP ?= 5052
PORT_RELAY_API ?= 9062
PORT_RBUILDER_RPC ?= 8645
PORT_RBUILDER_TELEMETRY ?= 6060
PYTHON_IMAGE ?= python:3.12-slim-bookworm
BOLD := $(shell tput bold 2>/dev/null)
RESET := $(shell tput sgr0 2>/dev/null)
RED := $(shell tput setaf 1 2>/dev/null)
GREEN := $(shell tput setaf 2 2>/dev/null)
YELLOW := $(shell tput setaf 3 2>/dev/null)
# -----------------------------------------------------------------------------
help: ## Show this help
@echo "$(BOLD)Boost+ artifact$(RESET)"
@echo
@grep -hE '^[a-zA-Z_%-]+:.*?## .*$$' $(MAKEFILE_LIST) \
| awk 'BEGIN {FS = ":.*?## "}; {printf " $(BOLD)%-22s$(RESET) %s\n", $$1, $$2}'
@echo
@echo " Examples:"
@echo " make up STOP_AT_BLOCK=100 # freeze at block 100, keep replay services online"
@echo " make up # unbounded live run"
@echo " make up STOP_AFTER_BLOCKS=100 # freeze 100 blocks after the current head"
@echo " make candidates # choose a DELIVERED=yes block"
@echo " make backtest BLOCK=81 # example; use your candidate height"
@echo " make backtest BLOCK=81 CUTOFF_MS=2000 BUILDERS=\"mgp-ordering mp-ordering parallel default\""
@echo " make mempool-data && make fetch BLOCKS=\"200 260\" RANGE=1"
@echo " make logs-rbuilder"
check-tools:
@command -v docker >/dev/null || { echo "$(RED)docker is required$(RESET)"; exit 1; }
@$(COMPOSE) version >/dev/null 2>&1 || { echo "$(RED)docker compose v2 is required$(RESET)"; exit 1; }
@command -v jq >/dev/null || echo "$(YELLOW)warning: jq not found; some targets print raw JSON$(RESET)"
.env:
@cp .env.example .env
@echo "$(YELLOW)created .env from .env.example$(RESET)"
# -----------------------------------------------------------------------------
preflight: ## Check Docker version, RAM, disk and ports BEFORE building
@./scripts/preflight.sh
STOP_AT_BLOCK ?=
STOP_AFTER_BLOCKS ?=
STOP_WAIT_TIMEOUT ?= 7200
STOP_POLL_SECONDS ?= 0.25
STOP_SERVICE_TIMEOUT ?= 3
up: check-tools .env ## Build/start; optionally freeze at an absolute/relative height
@if [ -n "$(STOP_AT_BLOCK)" ] && [ -n "$(STOP_AFTER_BLOCKS)" ]; then \
echo "$(RED)use either STOP_AT_BLOCK or STOP_AFTER_BLOCKS, not both$(RESET)"; exit 2; \
fi
@if [ -n "$(STOP_AT_BLOCK)" ]; then \
case "$(STOP_AT_BLOCK)" in *[!0-9]*) \
echo "$(RED)STOP_AT_BLOCK must be a positive integer$(RESET)"; exit 2 ;; \
esac; \
if [ "$(STOP_AT_BLOCK)" -eq 0 ]; then \
echo "$(RED)STOP_AT_BLOCK must be greater than zero$(RESET)"; exit 2; \
fi; \
fi
@if [ -n "$(STOP_AFTER_BLOCKS)" ]; then \
case "$(STOP_AFTER_BLOCKS)" in *[!0-9]*) \
echo "$(RED)STOP_AFTER_BLOCKS must be a positive integer$(RESET)"; exit 2 ;; \
esac; \
if [ "$(STOP_AFTER_BLOCKS)" -eq 0 ]; then \
echo "$(RED)STOP_AFTER_BLOCKS must be greater than zero$(RESET)"; exit 2; \
fi; \
fi
@echo "$(BOLD)starting the artifact environment ...$(RESET)"
@echo " first run compiles rbuilder from source; expect 10-30 minutes"
$(COMPOSE) up --build -d
@echo
@$(MAKE) --no-print-directory status
@if [ -n "$(STOP_AT_BLOCK)$(STOP_AFTER_BLOCKS)" ]; then \
echo; \
STOP_WAIT_TIMEOUT="$(STOP_WAIT_TIMEOUT)" \
STOP_POLL_SECONDS="$(STOP_POLL_SECONDS)" \
STOP_SERVICE_TIMEOUT="$(STOP_SERVICE_TIMEOUT)" \
./scripts/freeze-at-block.sh "$(if $(STOP_AFTER_BLOCKS),+$(STOP_AFTER_BLOCKS),$(STOP_AT_BLOCK))"; \
fi
build: check-tools .env ## Build/rebuild the rbuilder and workload images only
$(COMPOSE) build rbuilder orderflow-shim
rebuild: check-tools .env ## Force a clean rebuild of the rbuilder image
$(COMPOSE) build --no-cache rbuilder
down: ## Stop all services (chain state and experiments are kept)
$(COMPOSE) down --remove-orphans
@echo "$(GREEN)stopped. Volumes kept - 'make up' resumes the same chain.$(RESET)"
restart: ## Restart every service without touching data
$(COMPOSE) restart
resume: ## Resume block production after a bounded run was frozen
@for service in reth rbuilder relay-api; do \
if [ -z "$$($(COMPOSE) ps -q "$$service" 2>/dev/null)" ]; then \
echo "$(RED)$$service is not running; use 'make up' instead$(RESET)"; exit 1; \
fi; \
done
$(COMPOSE) start lighthouse-vc
$(COMPOSE) start txgen
@echo "$(GREEN)block production and deterministic order flow resumed.$(RESET)"
freeze-at-block: ## Freeze a running devnet: make freeze-at-block BLOCK=<n>
@if [ -z "$(BLOCK)" ]; then \
echo "$(RED)usage: make freeze-at-block BLOCK=<number>$(RESET)"; exit 2; \
fi
@STOP_WAIT_TIMEOUT="$(STOP_WAIT_TIMEOUT)" \
STOP_POLL_SECONDS="$(STOP_POLL_SECONDS)" \
STOP_SERVICE_TIMEOUT="$(STOP_SERVICE_TIMEOUT)" \
./scripts/freeze-at-block.sh "$(BLOCK)"
freeze-after-blocks: ## Freeze after N more blocks: make freeze-after-blocks BLOCKS=<n>
@if [ -z "$(BLOCKS)" ]; then \
echo "$(RED)usage: make freeze-after-blocks BLOCKS=<number>$(RESET)"; exit 2; \
fi
@STOP_WAIT_TIMEOUT="$(STOP_WAIT_TIMEOUT)" \
STOP_POLL_SECONDS="$(STOP_POLL_SECONDS)" \
STOP_SERVICE_TIMEOUT="$(STOP_SERVICE_TIMEOUT)" \
./scripts/freeze-at-block.sh "+$(BLOCKS)"
clean: ## DESTRUCTIVE: stop and delete the chain, logs and experiment data
@echo "$(RED)$(BOLD)"
@echo " ##################################################################"
@echo " # #"
@echo " # WARNING: this DELETES ALL LOCAL ARTIFACT RUN DATA #"
@echo " # #"
@echo " # - the entire chain (reth database and static files) #"
@echo " # - the beacon chain and validator slashing-protection DB #"
@echo " # - the generated genesis and validator keystores #"
@echo " # - the relay's PostgreSQL and Redis contents #"
@echo " # - every rbuilder order-flow trace #"
@echo " # - every experiment record under /experiments #"
@echo " # - every log file under /logs #"
@echo " # #"
@echo " # This cannot be undone. A NEW genesis is created next time, #"
@echo " # so block numbers and hashes will NOT match earlier runs and #"
@echo " # previously recorded experiments become unreplayable. #"
@echo " # #"
@echo " ##################################################################"
@echo "$(RESET)"
@if [ "$(FORCE)" != "1" ]; then \
read -r -p " Type 'delete' to confirm: " answer; \
if [ "$$answer" != "delete" ]; then echo " aborted."; exit 1; fi; \
fi
$(COMPOSE) down -v --remove-orphans
@echo "$(GREEN)all devnet data deleted.$(RESET)"
# -----------------------------------------------------------------------------
logs: ## Follow logs from every service
$(COMPOSE) logs -f --tail=100
logs-%: ## Follow logs from one service, e.g. make logs-rbuilder
$(COMPOSE) logs -f --tail=200 $*
ps: ## Raw container list
$(COMPOSE) ps
status: ## One-screen health summary
@if [ -z "$$($(COMPOSE) ps -q 2>/dev/null)" ]; then \
echo "$(BOLD)the devnet is not running$(RESET)"; \
echo; \
echo " Start it with:"; \
echo " make preflight # 30s: check Docker, RAM, disk, ports"; \
echo " make up # first build 10-30 min, then starts"; \
echo; \
echo " Then:"; \
echo " make status # this screen, populated"; \
echo " make smoke-test # 10 end-to-end checks"; \
echo " make candidates # blocks you can replay"; \
else \
$(MAKE) --no-print-directory status-detail; \
fi
status-detail:
@echo "$(BOLD)containers$(RESET)"
@$(COMPOSE) ps --all --format 'table {{.Service}}\t{{.Status}}' 2>/dev/null || $(COMPOSE) ps --all
@printf ' mode: '; \
vc_all="$$($(COMPOSE) ps --all -q lighthouse-vc 2>/dev/null)"; \
vc_up="$$($(COMPOSE) ps -q lighthouse-vc 2>/dev/null)"; \
tx_all="$$($(COMPOSE) ps --all -q txgen 2>/dev/null)"; \
tx_up="$$($(COMPOSE) ps -q txgen 2>/dev/null)"; \
if [ -n "$$vc_all" ] && [ -n "$$tx_all" ] && \
[ -z "$$vc_up" ] && [ -z "$$tx_up" ]; then \
echo "frozen (lighthouse-vc stopped; use 'make resume')"; \
elif [ -n "$$vc_up" ] && [ -n "$$tx_up" ]; then \
echo "running"; \
else \
echo "degraded (lighthouse-vc/txgen state mismatch)"; \
fi
@echo
@echo "$(BOLD)execution layer$(RESET)"
@head_hex="$$(curl -fsS --max-time 5 -X POST -H 'content-type: application/json' \
--data '{"jsonrpc":"2.0","id":1,"method":"eth_blockNumber","params":[]}' \
http://127.0.0.1:$(PORT_RETH_HTTP) 2>/dev/null \
| jq -r '.result // empty' 2>/dev/null)"; \
if [[ "$$head_hex" =~ ^0x[0-9a-fA-F]+$$ ]]; then \
printf ' head block : %s (%d)\n' "$$head_hex" "$$((head_hex))"; \
else \
echo " $(RED)reth unreachable on 127.0.0.1:$(PORT_RETH_HTTP)$(RESET)"; \
fi
@echo
@echo "$(BOLD)consensus layer$(RESET)"
@sync_line="$$(curl -fsS --max-time 5 \
http://127.0.0.1:$(PORT_LIGHTHOUSE_BN_HTTP)/eth/v1/node/syncing 2>/dev/null \
| jq -r 'if .data.head_slot then " head slot : \(.data.head_slot) syncing: \(.data.is_syncing) el_offline: \(.data.el_offline)" else empty end' \
2>/dev/null)"; \
if [ -n "$$sync_line" ]; then echo "$$sync_line"; \
else echo " $(YELLOW)starting or unavailable on 127.0.0.1:$(PORT_LIGHTHOUSE_BN_HTTP)$(RESET)"; fi
@echo
@echo "$(BOLD)relay$(RESET)"
@delivered_json="$$(curl -fsS --max-time 5 \
"http://127.0.0.1:$(PORT_RELAY_API)/relay/v1/data/bidtraces/proposer_payload_delivered?limit=50" 2>/dev/null)"; \
delivered="$$(printf '%s' "$$delivered_json" \
| jq -r 'if type == "array" then length else empty end' 2>/dev/null)"; \
slot="$$(printf '%s' "$$delivered_json" \
| jq -r 'if type == "array" and length > 0 then .[0].slot else empty end' 2>/dev/null)"; \
if [ -z "$$slot" ]; then \
slot="$$(curl -fsS --max-time 5 \
"http://127.0.0.1:$(PORT_LIGHTHOUSE_BN_HTTP)/eth/v1/node/syncing" 2>/dev/null \
| jq -r '.data.head_slot // empty' 2>/dev/null)"; \
fi; \
submissions=""; \
if [ -n "$$slot" ]; then \
submissions="$$(curl -fsS --max-time 5 \
"http://127.0.0.1:$(PORT_RELAY_API)/relay/v1/data/bidtraces/builder_blocks_received?slot=$$slot" 2>/dev/null \
| jq -r 'if type == "array" then length else empty end' 2>/dev/null)"; \
fi; \
if [ -n "$$submissions" ]; then \
printf ' submissions: %s (slot %s)\n' "$$submissions" "$$slot"; \
else \
printf ' submissions: starting/unavailable\n'; \
fi; \
printf ' delivered : %s\n' "$${delivered:-starting/unavailable}"
@echo
@echo "$(BOLD)rbuilder$(RESET)"
@metrics="$$(curl -fsS --max-time 5 http://127.0.0.1:$(PORT_RBUILDER_TELEMETRY)/debug/metrics/prometheus 2>/dev/null \
| grep -E '^rbuilder_(block_built_count|relay_submit)' | head -5 \
| sed 's/^/ /')"; \
if [ -n "$$metrics" ]; then echo "$$metrics"; \
else echo " $(YELLOW)telemetry starting or no matching metrics yet$(RESET)"; fi
@printf ' live_builders: '; $(COMPOSE) exec -T rbuilder \
grep -m1 '^live_builders' /shared/rbuilder/rbuilder.runtime.toml 2>/dev/null || echo "?"
@echo
@echo "$(BOLD)experiment output$(RESET)"
@$(COMPOSE) exec -T rbuilder sh -c '\
printf " order-flow traces : %s\n" "$$(ls -1 /shared/rbuilder/traces/*.bin 2>/dev/null | wc -l | tr -d " ")"; \
printf " decoded traces : %s\n" "$$(ls -1 /experiments/traces/*.txt 2>/dev/null | wc -l | tr -d " ")"; \
printf " slot records : %s\n" "$$(cat /experiments/slots/*.jsonl 2>/dev/null | wc -l | tr -d " ")"; \
printf " workload records : %s\n" "$$(cat /experiments/workload/*.jsonl 2>/dev/null | wc -l | tr -d " ")"' \
2>/dev/null || echo " (rbuilder container not running)"
validate-configs: ## Render every rbuilder TOML template and check key placement
@docker run --rm \
-v "$(CURDIR):/work:ro" -w /work \
$(PYTHON_IMAGE) python scripts/validate-templates.py
print-config: ## Show the rendered rbuilder configuration
$(COMPOSE) exec -T rbuilder cat /shared/rbuilder/rbuilder.runtime.toml
# -----------------------------------------------------------------------------
smoke-test: ## Run the end-to-end smoke test (non-zero exit on failure)
@./scripts/smoke-test.sh
send-test-tx: ## Submit one test transaction (ARGS="--priority-gwei 50")
@./scripts/send-test-tx.sh $(ARGS)
send-test-bundle: ## Submit one test bundle (ARGS="--txs 3 --reverting")
@./scripts/send-test-bundle.sh $(ARGS)
send-test-mev-bundle: ## Submit one bundle via mev_sendBundle (through the adapter)
@MEV=1 ./scripts/send-test-bundle.sh $(ARGS)
cancel-test-bundle: ## Cancel a replaceable bundle: make cancel-test-bundle ARGS="--uuid X --nonce 2"
@./scripts/cancel-test-bundle.sh $(ARGS)
candidates: ## List blocks that can actually be replayed (orders + relay record)
@./scripts/backtest-candidates.sh $(ARGS)
relay-blocks: ## Show payloads the local relay delivered to the proposer
@./scripts/relay-blocks.sh $(ARGS)
relay-received: ## Show builder submissions the local relay received
@./scripts/relay-blocks.sh --received $(ARGS)
# -----------------------------------------------------------------------------
BLOCK ?=
CUTOFF_MS ?= 0
BUILDERS ?= mgp-ordering mp-ordering parallel default
backtest: ## Replay a local block: make backtest BLOCK=<n> [CUTOFF_MS=1000] [SKIP_INGEST=1]
@if [ -z "$(BLOCK)" ]; then \
echo "$(RED)usage: make backtest BLOCK=<number> [CUTOFF_MS=1000] [BUILDERS=\"a b\"] [SKIP_INGEST=1]$(RESET)"; \
exit 2; fi
@./scripts/replay.sh $(BLOCK) --cutoff-ms $(CUTOFF_MS) --builders "$(BUILDERS)" \
$(if $(SKIP_INGEST),--skip-ingest,)
BLOCKS ?=
mempool-data: ## Build mempool-dumpster parquet files from recorded orders (ARGS="--include-bundles")
@./scripts/gen-mempool-data.sh $(ARGS)
fetch: ## Offline backtest-fetch: make fetch BLOCKS="200 260" [RANGE=1]
@if [ -z "$(BLOCKS)" ]; then \
echo "$(RED)usage: make fetch BLOCKS=\"200 260\" [RANGE=1]$(RESET)"; exit 2; fi
@./scripts/fetch-blocks.sh $(BLOCKS) $(if $(RANGE),--range,) $(ARGS)
experiments: ## Tail the per-slot experiment records
$(COMPOSE) exec -T experiment-recorder sh -c 'tail -f /experiments/slots/*.jsonl'
traces: ## List decoded order-flow traces
$(COMPOSE) exec -T rbuilder sh -c 'ls -lt /experiments/traces | head -30'
validators: ## Show active validators
@curl -fsS "http://127.0.0.1:$(PORT_LIGHTHOUSE_BN_HTTP)/eth/v1/beacon/states/head/validators?status=active_ongoing" \
| jq -r '"active validators: \(.data|length)"'
metrics: ## Print rbuilder Prometheus metrics
@curl -fsS http://127.0.0.1:$(PORT_RBUILDER_TELEMETRY)/debug/metrics/prometheus
reth-rpc: ## Interactive-ish reth RPC: make reth-rpc M=eth_blockNumber P='[]'
@curl -fsS -X POST -H 'content-type: application/json' \
--data '{"jsonrpc":"2.0","id":1,"method":"$(or $(M),eth_blockNumber)","params":$(or $(P),[])}' \
http://127.0.0.1:$(PORT_RETH_HTTP) | jq .
beacon-api: ## Query the beacon API: make beacon-api PATH_=/eth/v1/node/syncing
@curl -fsS "http://127.0.0.1:$(PORT_LIGHTHOUSE_BN_HTTP)$(or $(PATH_),/eth/v1/node/syncing)" | jq .
shell-rbuilder: ## Shell inside the rbuilder container
$(COMPOSE) exec rbuilder bash
shell-reth: ## Shell inside the reth container
$(COMPOSE) exec reth bash