Repository navigation
Expand file tree
/
Copy pathdocker-compose.yml
More file actions
647 lines (625 loc) · 26.4 KB
/
Copy pathdocker-compose.yml
File metadata and controls
647 lines (625 loc) · 26.4 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
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
# =============================================================================
# Self-contained local Ethereum PoS devnet for testing Flashbots rbuilder.
# =============================================================================
# Nothing in this file talks to mainnet, Sepolia, Hoodi, Holesky, a public
# MEV-Boost relay, or a public bundle endpoint. All host port bindings are
# pinned to 127.0.0.1. All keys are published test-only keys (see .env.example).
#
# Boot order is enforced with healthchecks + service_completed_successfully,
# never with `sleep`.
# =============================================================================
name: boostplus-devnet
networks:
devnet:
name: boostplus-devnet
driver: bridge
ipam:
config:
- subnet: 172.31.77.0/24
volumes:
# Genesis artifacts, JWT, validator keystores, reth IPC socket.
devnet-shared:
# Reth MDBX database + static files. Shared read/write with rbuilder.
reth-data:
# rbuilder order-flow traces, backtest sqlite, generated runtime config.
rbuilder-data:
# Machine-readable experiment output (JSON Lines).
experiments:
# Plain-text logs of every service that writes them.
devnet-logs:
lighthouse-bn-data:
lighthouse-vc-data:
relay-postgres-data:
relay-redis-data:
prometheus-data:
# --- reusable fragments -------------------------------------------------------
x-logging: &default-logging
driver: json-file
options:
max-size: "50m"
max-file: "5"
x-relay-network-env: &relay-network-env
NETWORK: custom
LOG_LEVEL: ${RELAY_LOG_LEVEL:-info}
services:
# ===========================================================================
# 1. One-shot devnet initialisation: genesis, JWT, validator keystores.
# Idempotent: re-running never regenerates an existing chain.
# ===========================================================================
devnet-init:
image: ${GENESIS_GENERATOR_IMAGE:-ethpandaops/ethereum-genesis-generator:6.1.5}
container_name: devnet-init
networks: [devnet]
entrypoint: ["/bin/bash", "/scripts/init-devnet.sh"]
environment:
DEVNET_CHAIN_ID: ${DEVNET_CHAIN_ID:-7788}
DEVNET_SLOT_DURATION_MS: ${DEVNET_SLOT_DURATION_MS:-3000}
DEVNET_GENESIS_DELAY: ${DEVNET_GENESIS_DELAY:-30}
DEVNET_VALIDATOR_COUNT: ${DEVNET_VALIDATOR_COUNT:-64}
DEVNET_GENESIS_GASLIMIT: ${DEVNET_GENESIS_GASLIMIT:-36000000}
DEVNET_FULU_FORK_EPOCH: ${DEVNET_FULU_FORK_EPOCH:-18446744073709551615}
DEVNET_JWT_SECRET: ${DEVNET_JWT_SECRET:-0x2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a}
DEVNET_VALIDATOR_MNEMONIC: ${DEVNET_VALIDATOR_MNEMONIC:-sleep moment list remain like wall lake industry canvas wonder ecology elite duck salad naive syrup frame brass utility club odor country obey pudding}
VALIDATOR_FEE_RECIPIENT: ${VALIDATOR_FEE_RECIPIENT:-0x000000000000000000000000000000000000F33D}
RBUILDER_COINBASE_ADDRESS: ${RBUILDER_COINBASE_ADDRESS:-0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266}
volumes:
- ./configs/genesis:/config:ro
- ./scripts:/scripts:ro
- devnet-shared:/data
- devnet-logs:/logs
logging: *default-logging
# ===========================================================================
# 2. Execution layer: Reth
# ===========================================================================
reth:
image: ${RETH_IMAGE:-ghcr.io/paradigmxyz/reth:v2.2.0}
container_name: reth
networks: [devnet]
depends_on:
devnet-init:
condition: service_completed_successfully
# --engine.persistence-threshold 0 / --engine.memory-block-buffer-target 0
# -> every canonical block is flushed to the MDBX database immediately,
# which is what rbuilder needs because it reads the database directly.
# --engine.always-process-payload-attributes-on-canonical-head
# -> pairs with Lighthouse's --always-prepare-payload.
# No pruning flags are set, so full history/state is retained for replay.
command:
- node
- --chain=/shared/genesis.json
- --datadir=/shared/reth
- --http
- --http.addr=0.0.0.0
- --http.port=8545
- --http.api=eth,net,web3,txpool,debug,trace,rpc,reth,admin,flashbots
- --http.corsdomain=*
- --ws
- --ws.addr=0.0.0.0
- --ws.port=8546
- --ws.api=eth,net,web3,txpool,debug,trace
- --ws.origins=*
- --authrpc.addr=0.0.0.0
- --authrpc.port=8551
- --authrpc.jwtsecret=/shared/jwt.hex
- --ipcpath=/shared/reth.ipc
- --ipc.permissions=777
- --engine.persistence-threshold=0
- --engine.memory-block-buffer-target=0
- --engine.always-process-payload-attributes-on-canonical-head
- --disable-discovery
- --no-persist-peers
- --port=30303
- --metrics=0.0.0.0:9001
- --txpool.pending-max-count=50000
- --txpool.queued-max-count=50000
- --txpool.nolocals
- --builder.extradata=reth-local-devnet
- --builder.gaslimit=${DEVNET_GENESIS_GASLIMIT:-36000000}
- --rpc.max-connections=8192
- -vvv
volumes:
- devnet-shared:/shared
- reth-data:/shared/reth
- ./scripts:/scripts:ro
ports:
- "127.0.0.1:${PORT_RETH_HTTP:-8545}:8545"
- "127.0.0.1:${PORT_RETH_WS:-8546}:8546"
- "127.0.0.1:${PORT_RETH_METRICS:-9001}:9001"
healthcheck:
test: ["CMD", "bash", "/scripts/hc/reth.sh"]
interval: 5s
timeout: 5s
retries: 40
start_period: 20s
restart: unless-stopped
logging: *default-logging
# ===========================================================================
# 3. Consensus layer: Lighthouse beacon node
# ===========================================================================
lighthouse-bn:
image: ${LIGHTHOUSE_IMAGE:-sigp/lighthouse:v8.2.1}
container_name: lighthouse-bn
networks: [devnet]
depends_on:
devnet-init:
condition: service_completed_successfully
reth:
condition: service_healthy
command:
- lighthouse
- beacon_node
- --testnet-dir=/shared/metadata
- --datadir=/bndata
- --execution-endpoint=http://reth:8551
- --execution-jwt=/shared/jwt.hex
- --http
- --http-address=0.0.0.0
- --http-port=5052
- --http-allow-origin=*
- --metrics
- --metrics-address=0.0.0.0
- --metrics-port=5054
# --- builder / MEV-Boost path ---
- --builder=http://mev-boost:18550
- --builder-fallback-disable-checks
- --always-prepare-payload
- --prepare-payload-lookahead=${DEVNET_PREPARE_PAYLOAD_LOOKAHEAD_MS:-2000}
- --suggested-fee-recipient=${VALIDATOR_FEE_RECIPIENT:-0x000000000000000000000000000000000000F33D}
# --- single-node devnet networking ---
- --disable-packet-filter
- --disable-upnp
- --disable-quic
- --disable-enr-auto-update
- --enr-address=127.0.0.1
- --target-peers=0
- --allow-insecure-genesis-sync
# A devnet that is stopped and resumed later always trips the weak
# subjectivity check: wall-clock keeps advancing while the chain does not,
# so on restart the head state is far outside the WS period and the beacon
# node refuses to start with
# "The current head state is outside the weak subjectivity period"
# WS protects against long-range attacks on a public chain. This chain has
# one node, 64 throwaway validators and no value, so the check has nothing
# to protect. Without this flag `make down` + `make up` the next day is a
# restart loop.
- --ignore-ws-check
- --subscribe-all-subnets
- --validator-monitor-auto
- --debug-level=info
volumes:
- devnet-shared:/shared:ro
- lighthouse-bn-data:/bndata
- ./scripts:/scripts:ro
ports:
- "127.0.0.1:${PORT_LIGHTHOUSE_BN_HTTP:-5052}:5052"
- "127.0.0.1:${PORT_LIGHTHOUSE_BN_METRICS:-5054}:5054"
healthcheck:
test: ["CMD", "bash", "/scripts/hc/lighthouse-bn.sh"]
interval: 5s
timeout: 5s
retries: 60
start_period: 20s
restart: unless-stopped
logging: *default-logging
# ===========================================================================
# 4. Consensus layer: Lighthouse validator client
# ===========================================================================
lighthouse-vc:
image: ${LIGHTHOUSE_IMAGE:-sigp/lighthouse:v8.2.1}
container_name: lighthouse-vc
networks: [devnet]
depends_on:
lighthouse-bn:
condition: service_healthy
# --prefer-builder-proposals: always take the relay's block, regardless of
# the local payload's value. On a devnet the local payload is frequently
# worth more than a tiny builder bid; without this flag the proposer would
# silently fall back to the local EL block and rbuilder blocks would never
# land. Doppelganger protection is intentionally NOT enabled (it would
# stall proposals for two epochs after every restart).
# scripts/entrypoint-lighthouse-vc.sh copies the generated keystores into
# <datadir>/validators first: Lighthouse v8 rejects --datadir together with
# --validators-dir, and the VC needs to write validator_definitions.yml and
# slashing_protection.sqlite, which it cannot do on the read-only /shared.
entrypoint: ["/bin/bash", "/scripts/entrypoint-lighthouse-vc.sh"]
command:
- --testnet-dir=/shared/metadata
- --datadir=/vcdata
- --secrets-dir=/shared/validator-keys/secrets
- --beacon-nodes=http://lighthouse-bn:5052
- --init-slashing-protection
- --builder-proposals
- --prefer-builder-proposals
- --suggested-fee-recipient=${VALIDATOR_FEE_RECIPIENT:-0x000000000000000000000000000000000000F33D}
- --graffiti=boostplus-devnet
- --metrics
- --metrics-address=0.0.0.0
- --metrics-port=5064
- --debug-level=info
volumes:
- devnet-shared:/shared:ro
- lighthouse-vc-data:/vcdata
- ./scripts:/scripts:ro
ports:
- "127.0.0.1:${PORT_LIGHTHOUSE_VC_METRICS:-5064}:5064"
healthcheck:
test: ["CMD", "bash", "/scripts/hc/lighthouse-vc.sh"]
interval: 10s
timeout: 5s
retries: 30
start_period: 30s
restart: unless-stopped
logging: *default-logging
# ===========================================================================
# 5. Local relay: PostgreSQL + Redis + mev-boost-relay housekeeper/api
# ===========================================================================
relay-postgres:
image: ${POSTGRES_IMAGE:-postgres:16-alpine}
container_name: relay-postgres
networks: [devnet]
environment:
POSTGRES_USER: ${POSTGRES_USER:-relay}
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-local-test-only}
POSTGRES_DB: ${POSTGRES_DB:-relay}
volumes:
- relay-postgres-data:/var/lib/postgresql/data
ports:
- "127.0.0.1:${PORT_POSTGRES:-5432}:5432"
healthcheck:
test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER:-relay} -d ${POSTGRES_DB:-relay}"]
interval: 5s
timeout: 5s
retries: 20
restart: unless-stopped
logging: *default-logging
relay-redis:
image: ${REDIS_IMAGE:-redis:7.4-alpine}
container_name: relay-redis
networks: [devnet]
command: ["redis-server", "--appendonly", "yes", "--save", ""]
volumes:
- relay-redis-data:/data
ports:
- "127.0.0.1:${PORT_REDIS:-6379}:6379"
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 5s
timeout: 3s
retries: 20
restart: unless-stopped
logging: *default-logging
# The housekeeper populates proposer duties + validator registrations in
# Redis. Without it the builder API has no known validators and rejects
# every submission.
relay-housekeeper:
image: ${MEV_BOOST_RELAY_IMAGE:-flashbots/mev-boost-relay:0.34.5}
container_name: relay-housekeeper
networks: [devnet]
depends_on:
relay-postgres: {condition: service_healthy}
relay-redis: {condition: service_healthy}
lighthouse-bn: {condition: service_healthy}
# Both roles apply the relay's SQL schema on startup. Starting them
# together races on DDL and one dies with
# Failed to connect to Postgres ... pq: duplicate key value violates
# unique constraint "pg_type_typname_nsp_index"
# The restart policy recovers it, but waiting for relay-api to be healthy
# means the schema already exists and no fatal error is logged at all.
relay-api: {condition: service_healthy}
entrypoint: ["/bin/sh", "/scripts/entrypoint-relay.sh", "housekeeper"]
environment:
<<: *relay-network-env
BEACON_URIS: http://lighthouse-bn:5052
REDIS_URI: relay-redis:6379
POSTGRES_DSN: postgres://${POSTGRES_USER:-relay}:${POSTGRES_PASSWORD:-local-test-only}@relay-postgres:5432/${POSTGRES_DB:-relay}?sslmode=disable
LISTEN_ADDR: 0.0.0.0:9064
volumes:
- devnet-shared:/shared:ro
- ./scripts:/scripts:ro
- devnet-logs:/logs
ports:
- "127.0.0.1:${PORT_RELAY_HOUSEKEEPER:-9064}:9064"
restart: unless-stopped
logging: *default-logging
relay-api:
image: ${MEV_BOOST_RELAY_IMAGE:-flashbots/mev-boost-relay:0.34.5}
container_name: relay-api
networks: [devnet]
depends_on:
relay-postgres: {condition: service_healthy}
relay-redis: {condition: service_healthy}
lighthouse-bn: {condition: service_healthy}
reth: {condition: service_healthy}
entrypoint: ["/bin/sh", "/scripts/entrypoint-relay.sh", "api"]
environment:
<<: *relay-network-env
BEACON_URIS: http://lighthouse-bn:5052
REDIS_URI: relay-redis:6379
POSTGRES_DSN: postgres://${POSTGRES_USER:-relay}:${POSTGRES_PASSWORD:-local-test-only}@relay-postgres:5432/${POSTGRES_DB:-relay}?sslmode=disable
LISTEN_ADDR: 0.0.0.0:9062
SECRET_KEY: ${RELAY_SECRET_KEY:-0x607a11b45a7219cc61a3d9c5fd08c7eebd602a6a19a977f8d3771d5711a550f2}
# Builder submissions are re-executed against the local reth node via
# flashbots_validateBuilderSubmissionV4. No external simulator is used.
BLOCKSIM_URI: http://reth:8545
ENABLE_INTERNAL_API: "1"
# rbuilder always submits with ?cancellations=1 - the flag is hardcoded
# in MevBoostRelay::new (crates/rbuilder/src/mev_boost/mod.rs) and is not
# exposed in RelayConfig. Without this feature flag the relay answers
# every submission with
# (code: 400, message: cancellations are disabled)
# so no bid ever reaches the proposer and every block comes from reth's
# local payload builder instead. Env-var only; there is no CLI flag.
ENABLE_BUILDER_CANCELLATIONS: "1"
volumes:
- devnet-shared:/shared:ro
- ./scripts:/scripts:ro
- devnet-logs:/logs
ports:
- "127.0.0.1:${PORT_RELAY_API:-9062}:9062"
healthcheck:
test: ["CMD", "wget", "-q", "-O-", "http://127.0.0.1:9062/eth/v1/builder/status"]
interval: 5s
timeout: 5s
retries: 40
start_period: 15s
restart: unless-stopped
logging: *default-logging
# Optional relay web UI (profile: observability)
relay-website:
image: ${MEV_BOOST_RELAY_IMAGE:-flashbots/mev-boost-relay:0.34.5}
container_name: relay-website
profiles: ["observability"]
networks: [devnet]
depends_on:
relay-api: {condition: service_healthy}
entrypoint: ["/bin/sh", "/scripts/entrypoint-relay.sh", "website"]
environment:
<<: *relay-network-env
REDIS_URI: relay-redis:6379
POSTGRES_DSN: postgres://${POSTGRES_USER:-relay}:${POSTGRES_PASSWORD:-local-test-only}@relay-postgres:5432/${POSTGRES_DB:-relay}?sslmode=disable
LISTEN_ADDR: 0.0.0.0:9060
RELAY_URL: http://${RELAY_BLS_PUBKEY:-0xa55c1285d84ba83a5ad26420cd5ad3091e49c55a813eee651cd467db38a8c8e63192f47955e9376f6b42f6d190571cb5}@127.0.0.1:${PORT_RELAY_API:-9062}
LINK_DATA_API: http://127.0.0.1:${PORT_RELAY_API:-9062}
PUBKEY_OVERRIDE: ${RELAY_BLS_PUBKEY:-0xa55c1285d84ba83a5ad26420cd5ad3091e49c55a813eee651cd467db38a8c8e63192f47955e9376f6b42f6d190571cb5}
volumes:
- devnet-shared:/shared:ro
- ./scripts:/scripts:ro
ports:
- "127.0.0.1:${PORT_RELAY_WEBSITE:-9060}:9060"
restart: unless-stopped
logging: *default-logging
# ===========================================================================
# 6. mev-boost (proposer-side multiplexer). Only ever points at the local relay.
# ===========================================================================
mev-boost:
image: ${MEV_BOOST_IMAGE:-flashbots/mev-boost:1.12}
container_name: mev-boost
networks: [devnet]
depends_on:
relay-api: {condition: service_healthy}
entrypoint: ["/bin/sh", "/scripts/entrypoint-mevboost.sh"]
environment:
BOOST_LISTEN_ADDR: 0.0.0.0:18550
RELAYS: http://${RELAY_BLS_PUBKEY:-0xa55c1285d84ba83a5ad26420cd5ad3091e49c55a813eee651cd467db38a8c8e63192f47955e9376f6b42f6d190571cb5}@relay-api:9062
MIN_BID_ETH: "0"
RELAY_STARTUP_CHECK: "true"
LOG_LEVEL: ${MEV_BOOST_LOG_LEVEL:-info}
RELAY_TIMEOUT_MS_GETHEADER: "1500"
RELAY_TIMEOUT_MS_GETPAYLOAD: "${RELAY_TIMEOUT_MS_GETPAYLOAD:-2500}"
METRICS_ENABLED: "true"
METRICS_ADDR: 0.0.0.0:18551
volumes:
- devnet-shared:/shared:ro
- ./scripts:/scripts:ro
- devnet-logs:/logs
ports:
- "127.0.0.1:${PORT_MEV_BOOST:-18550}:18550"
healthcheck:
test: ["CMD", "wget", "-q", "-O-", "http://127.0.0.1:18550/eth/v1/builder/status"]
interval: 5s
timeout: 5s
retries: 30
start_period: 10s
restart: unless-stopped
logging: *default-logging
# ===========================================================================
# 7. Flashbots rbuilder (external block builder), built from source.
# ===========================================================================
rbuilder:
build:
context: .
dockerfile: Dockerfile.rbuilder
args:
RBUILDER_REPO: ${RBUILDER_REPO:-https://github.com/hackingdecentralized/rbuilder.git}
RBUILDER_COMMIT: ${RBUILDER_COMMIT:-db8e5b57f7d3d086131eb9f48502fde85763fae6}
image: boostplus-devnet/rbuilder:${RBUILDER_COMMIT:-db8e5b57f7d3d086131eb9f48502fde85763fae6}
container_name: rbuilder
networks: [devnet]
# rbuilder opens reth's MDBX database directly (read-only). libmdbx's
# lock protocol is PID-based, so a reader in a *different PID namespace*
# than the writer fails at open with
# failed to open the database: Resource temporarily unavailable (11)
# Upstream this never shows up because rbuilder and reth run as two
# processes on one host. Verified with reth's own binary: a sibling
# container fails, the same command with --pid=container:reth succeeds.
# Sharing reth's PID namespace is what makes direct DB access work here.
pid: "service:reth"
depends_on:
reth: {condition: service_healthy}
lighthouse-bn: {condition: service_healthy}
relay-api: {condition: service_healthy}
entrypoint: ["/bin/bash", "/scripts/entrypoint-rbuilder.sh"]
environment:
RBUILDER_COINBASE_SECRET_KEY: ${RBUILDER_COINBASE_SECRET_KEY:-0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80}
RBUILDER_RELAY_SECRET_KEY: ${RBUILDER_RELAY_SECRET_KEY:-5eae315483f028b5cdd5d1090ff0c7618b18737ea9bf3c35047189db22835c48}
RBUILDER_CONFIG_TEMPLATE: ${RBUILDER_CONFIG_TEMPLATE:-rbuilder.toml.tmpl}
RBUILDER_BACKTEST_TEMPLATE: ${RBUILDER_BACKTEST_TEMPLATE:-backtest.toml.tmpl}
RBUILDER_LIVE_BUILDERS: '${RBUILDER_LIVE_BUILDERS:-["mgp-ordering", "mp-ordering"]}'
RBUILDER_LOG_LEVEL: ${RBUILDER_LOG_LEVEL:-info,rbuilder=debug}
RBUILDER_EXTRA_DATA: ${RBUILDER_EXTRA_DATA:-boostplus-devnet}
RBUILDER_TRACE_MAX_BLOCKS: ${RBUILDER_TRACE_MAX_BLOCKS:-2000}
RBUILDER_MAX_BID_ETH: ${RBUILDER_MAX_BID_ETH:-}
RBUILDER_RELAY_URL: http://relay-api:9062
RBUILDER_CL_NODE_URL: http://lighthouse-bn:5052
RUST_BACKTRACE: "1"
volumes:
- devnet-shared:/shared
- reth-data:/shared/reth
- rbuilder-data:/shared/rbuilder
- experiments:/experiments
- devnet-logs:/logs
- ./configs/rbuilder:/config:ro
- ./scripts:/scripts:ro
ports:
- "127.0.0.1:${PORT_RBUILDER_RPC:-8645}:8645"
- "127.0.0.1:${PORT_RBUILDER_TELEMETRY:-6060}:6060"
- "127.0.0.1:${PORT_RBUILDER_TELEMETRY_REDACTED:-6070}:6070"
healthcheck:
test: ["CMD", "bash", "/scripts/hc/rbuilder.sh"]
interval: 5s
timeout: 5s
retries: 40
start_period: 30s
restart: unless-stopped
logging: *default-logging
# ===========================================================================
# 8. Order-flow adapter: exposes mev_sendBundle and forwards everything to
# rbuilder. See docs/ORDERFLOW.md for exactly what this translates.
# ===========================================================================
orderflow-shim:
build:
context: ./workload
dockerfile: Dockerfile
args:
PYTHON_IMAGE: ${PYTHON_IMAGE:-python:3.12-slim-bookworm}
image: boostplus-devnet/workload:1
container_name: orderflow-shim
networks: [devnet]
depends_on:
rbuilder: {condition: service_healthy}
command: ["python", "-u", "/app/mev_shim.py"]
environment:
SHIM_LISTEN_HOST: 0.0.0.0
SHIM_LISTEN_PORT: "8646"
RBUILDER_RPC_URL: http://rbuilder:8645
SHIM_LOG_PATH: /experiments/orderflow-shim.jsonl
volumes:
- experiments:/experiments
- devnet-logs:/logs
ports:
- "127.0.0.1:${PORT_ORDERFLOW_SHIM:-8646}:8646"
restart: unless-stopped
logging: *default-logging
# ===========================================================================
# 9. Deterministic workload generator.
# ===========================================================================
txgen:
# No `build:` here on purpose. txgen, orderflow-shim and experiment-recorder
# are the same image with different commands. When more than one of them
# declares an identical build producing the same tag, `docker compose up
# --build` runs those builds concurrently and they race to export the tag:
# failed to solve: image "docker.io/boostplus-devnet/workload:1":
# already exists
# (intermittent - it only fires when two exports overlap). orderflow-shim is
# the single builder of this image, which is also what `make build` targets.
image: boostplus-devnet/workload:1
container_name: txgen
networks: [devnet]
depends_on:
rbuilder: {condition: service_healthy}
orderflow-shim: {condition: service_started}
command: ["python", "-u", "/app/txgen.py"]
environment:
TXGEN_ENABLED: ${TXGEN_ENABLED:-true}
TXGEN_TPS: ${TXGEN_TPS:-6}
TXGEN_SEED: ${TXGEN_SEED:-42}
TXGEN_BUNDLE_RATIO: ${TXGEN_BUNDLE_RATIO:-0.35}
TXGEN_USE_PUBLIC_MEMPOOL: ${TXGEN_USE_PUBLIC_MEMPOOL:-true}
TXGEN_OUTPUT_DIR: /experiments/workload
ETH_RPC_URL: http://reth:8545
RBUILDER_RPC_URL: http://rbuilder:8645
SHIM_RPC_URL: http://orderflow-shim:8646
CHAIN_ID: ${DEVNET_CHAIN_ID:-7788}
BUILDER_COINBASE_ADDRESS: ${RBUILDER_COINBASE_ADDRESS:-0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266}
volumes:
- experiments:/experiments
- devnet-logs:/logs
# Writable so `mempool_parquet.py` can drop mempool-dumpster-format
# parquet files where rbuilder's backtest_fetch_mempool_data_dir points.
- rbuilder-data:/shared/rbuilder
restart: unless-stopped
logging: *default-logging
# ===========================================================================
# 10. Per-slot experiment recorder (JSON Lines).
# ===========================================================================
experiment-recorder:
# Built by orderflow-shim - see the note on txgen above.
image: boostplus-devnet/workload:1
container_name: experiment-recorder
networks: [devnet]
depends_on:
lighthouse-bn: {condition: service_healthy}
relay-api: {condition: service_healthy}
rbuilder: {condition: service_healthy}
command: ["python", "-u", "/app/recorder.py"]
environment:
BEACON_URL: http://lighthouse-bn:5052
ETH_RPC_URL: http://reth:8545
RELAY_URL: http://relay-api:9062
RBUILDER_TELEMETRY_URL: http://rbuilder:6060
RECORDER_OUTPUT_DIR: /experiments/slots
RBUILDER_TRACE_DIR: /shared/rbuilder/traces
RBUILDER_DECODED_TRACE_DIR: /experiments/traces
WORKLOAD_DIR: /experiments/workload
volumes:
- experiments:/experiments
- rbuilder-data:/shared/rbuilder:ro
- devnet-logs:/logs
restart: unless-stopped
logging: *default-logging
# ===========================================================================
# 11. Order-flow trace decoder: gz+bincode -> plain text, continuously.
# ===========================================================================
trace-decoder:
# Built by the rbuilder service - same reason as txgen above: two services
# declaring an identical build for one tag race to export it under
# `docker compose up --build`. rbuilder is the single builder of this image
# and is what `make build` targets. This service only needs the
# orderflow-report-decoder binary that image already contains.
image: boostplus-devnet/rbuilder:${RBUILDER_COMMIT:-db8e5b57f7d3d086131eb9f48502fde85763fae6}
container_name: trace-decoder
networks: [devnet]
depends_on:
rbuilder: {condition: service_healthy}
entrypoint: ["/bin/bash", "/scripts/trace-decoder.sh"]
environment:
TRACE_DIR: /shared/rbuilder/traces
DECODED_DIR: /experiments/traces
POLL_SECONDS: "5"
# Keep the decoded .txt count in step with rbuilder's own .bin cap.
MAX_DECODED: ${RBUILDER_TRACE_MAX_BLOCKS:-2000}
volumes:
- rbuilder-data:/shared/rbuilder:ro
- experiments:/experiments
- ./scripts:/scripts:ro
restart: unless-stopped
logging: *default-logging
# ===========================================================================
# 12. Prometheus (profile: observability)
# ===========================================================================
prometheus:
image: ${PROMETHEUS_IMAGE:-prom/prometheus:v3.1.0}
container_name: prometheus
profiles: ["observability"]
networks: [devnet]
volumes:
- ./configs/prometheus:/etc/prometheus:ro
- prometheus-data:/prometheus
command:
- --config.file=/etc/prometheus/prometheus.yml
- --storage.tsdb.path=/prometheus
- --storage.tsdb.retention.time=7d
ports:
- "127.0.0.1:${PORT_PROMETHEUS:-9090}:9090"
restart: unless-stopped
logging: *default-logging