Skip to content

Commit e65169f

Browse files
committed
Merge branch 'main' into caner/rbuilder-reth2-testing
2 parents 3ed89d6 + 59ac9a3 commit e65169f

7 files changed

Lines changed: 532 additions & 273 deletions

File tree

containers/helix.ts

Lines changed: 103 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,18 @@
11
import type { ContainerDef, ContainerResult, Ctx, Ports } from "../utils/types.ts";
2+
import { RELAY_BUILDER_PUBKEY } from "./rbuilder.ts";
3+
4+
// Optimistic relaying is per-builder: the builder's pubkey must be present in
5+
// `builders:` with is_optimistic + collateral ≥ bid value. 1e24 wei (1M ETH)
6+
// clears any devnet bid. accept_optimistic itself already defaults true in helix.
7+
const DEFAULT_COLLATERAL = "1000000000000000000000000";
28

39
const DEFAULT_API_PORT = 4040;
410
const DEFAULT_TCP_PORT = 4041;
511
const DEFAULT_WEBSITE_PORT = 9060;
12+
// helix serves prometheus metrics from start_metrics_server on $METRICS_PORT
13+
// (default 9500). Exposed so the bench's prometheus can scrape helix-native
14+
// metrics (helix_request_latency_secs, helix_submission_trace_latency_us, …).
15+
const DEFAULT_METRICS_PORT = 9500;
616
const DEFAULT_RELAY_KEY = "0x64496d4e301e541a6e1237d6ef13a8f8b8b6cb82be9d8ac90073a833dfc2af11";
717
const DEFAULT_ADMIN_TOKEN = "decker";
818
const DEFAULT_POSTGRES_PASSWORD = "helix";
@@ -11,9 +21,72 @@ export const ports: Ports = {
1121
http: DEFAULT_API_PORT,
1222
tcp: DEFAULT_TCP_PORT,
1323
website: DEFAULT_WEBSITE_PORT,
24+
metrics: DEFAULT_METRICS_PORT,
25+
};
26+
27+
type HelixYamlOpts = {
28+
beaconUrl: string;
29+
simUrl: string;
30+
apiPort: number;
31+
tcpPort: number;
32+
headerDelay: boolean;
33+
optimistic: boolean;
34+
builderPubkey: string;
35+
collateral: string;
36+
coreCount: number;
1437
};
1538

16-
function helixYaml(beaconUrl: string, simUrl: string, apiPort: number, tcpPort: number): string {
39+
// helix hard-pins each tile thread to a CPU id via core_affinity, and `cores` is
40+
// a required config block — so the layout determines whether helix's pipeline runs
41+
// in parallel or all on one core. Spread the tiles across the host's cores: the
42+
// auctioneer (the single-threaded serial stage) gets its own core, and the async
43+
// HTTP runtime (serves submitBlock + getHeader) and the decoder get the largest
44+
// shares; background tiles share core 0. On a small host pinning to high core ids
45+
// would fail, so fall back to the original single-core layout. coreCount is the
46+
// host's logical CPUs (the devnet sets no cpuset, so the container sees them all).
47+
function helixCoresYaml(coreCount: number): string {
48+
if (coreCount < 8) {
49+
return `cores:
50+
auctioneer: 0
51+
tokio: [0]
52+
reg_workers: [0]
53+
tcp_bid_submissions_tile: 0
54+
decoder: [0]
55+
simulator: 0
56+
top_bid: 0`;
57+
}
58+
const pool = Array.from({ length: coreCount - 2 }, (_, i) => i + 2); // cores 2 … n-1
59+
const tcp = pool.pop()!;
60+
const sim = pool.pop()!;
61+
const reg = [pool.pop()!, pool.pop()!];
62+
const half = Math.ceil(pool.length / 2);
63+
const tokio = pool.slice(0, half); // bigger share → HTTP runtime
64+
const decoder = pool.slice(half); // remainder → decode/deserialize
65+
return `cores:
66+
auctioneer: 1
67+
tokio: [${tokio.join(", ")}]
68+
reg_workers: [${reg.join(", ")}]
69+
tcp_bid_submissions_tile: ${tcp}
70+
decoder: [${decoder.join(", ")}]
71+
simulator: ${sim}
72+
top_bid: 0
73+
data_gatherer: 0`;
74+
}
75+
76+
// Register the builder in both modes so the comparison is a known builder either
77+
// way; is_optimistic toggles the path: false = pessimistic (wait on sim), true =
78+
// fast path (accept before sim). An unregistered builder is a different scenario,
79+
// not the sync baseline we want.
80+
function buildersBlock(o: HelixYamlOpts): string {
81+
return `builders:
82+
- pub_key: "${o.builderPubkey}"
83+
builder_info:
84+
collateral: "${o.collateral}"
85+
is_optimistic: ${o.optimistic}
86+
is_optimistic_for_regional_filtering: false`;
87+
}
88+
89+
function helixYaml(o: HelixYamlOpts): string {
1790
return `\
1891
postgres:
1992
hostname: 127.0.0.1
@@ -23,15 +96,15 @@ postgres:
2396
region: 0
2497
region_name: "LOCAL"
2598
simulators:
26-
- url: ${simUrl}
99+
- url: ${o.simUrl}
27100
beacon_clients:
28-
- url: ${beaconUrl}
101+
- url: ${o.beaconUrl}
29102
relays: []
30-
builders: []
103+
${buildersBlock(o)}
31104
validator_preferences:
32105
filtering: global
33106
trusted_builders: null
34-
header_delay: true
107+
header_delay: ${o.headerDelay}
35108
router_config:
36109
enabled_routes:
37110
- route: All
@@ -44,16 +117,9 @@ is_submission_instance: true
44117
is_registration_instance: true
45118
website:
46119
enabled: false
47-
cores:
48-
auctioneer: 0
49-
tokio: [0]
50-
reg_workers: [0]
51-
tcp_bid_submissions_tile: 0
52-
decoder: [0]
53-
simulator: 0
54-
top_bid: 0
55-
api_port: ${apiPort}
56-
tcp_port: ${tcpPort}
120+
${helixCoresYaml(o.coreCount)}
121+
api_port: ${o.apiPort}
122+
tcp_port: ${o.tcpPort}
57123
`;
58124
}
59125

@@ -66,9 +132,18 @@ export function buildContainer(def: ContainerDef, ctx: Ctx): ContainerResult {
66132
const apiPort = (def.config?.apiPort as number | undefined) ?? DEFAULT_API_PORT;
67133
const tcpPort = (def.config?.tcpPort as number | undefined) ?? DEFAULT_TCP_PORT;
68134
const websitePort = (def.config?.websitePort as number | undefined) ?? DEFAULT_WEBSITE_PORT;
135+
const metricsPort = (def.config?.metricsPort as number | undefined) ?? DEFAULT_METRICS_PORT;
69136
const relayKey = (def.config?.relayKey as string | undefined) ?? DEFAULT_RELAY_KEY;
70137
const adminToken = (def.config?.adminToken as string | undefined) ?? DEFAULT_ADMIN_TOKEN;
71138
const postgresPassword = (def.config?.postgresPassword as string | undefined) ?? DEFAULT_POSTGRES_PASSWORD;
139+
// Bench knobs: optimistic enables the fast path for the builder; headerDelay
140+
// off measures raw getHeader serve latency (defaults preserve standalone behaviour).
141+
const optimistic = (def.config?.optimistic as boolean | undefined) ?? false;
142+
const headerDelay = (def.config?.headerDelay as boolean | undefined) ?? true;
143+
const builderPubkey = (def.config?.builderPubkey as string | undefined) ?? RELAY_BUILDER_PUBKEY;
144+
const collateral = (def.config?.collateral as string | undefined) ?? DEFAULT_COLLATERAL;
145+
// Distribute helix's tiles across the host's CPUs (override with config.coreCount).
146+
const coreCount = (def.config?.coreCount as number | undefined) ?? navigator.hardwareConcurrency;
72147

73148
return {
74149
container: {
@@ -78,8 +153,9 @@ export function buildContainer(def: ContainerDef, ctx: Ctx): ContainerResult {
78153
RELAY_KEY: relayKey,
79154
ADMIN_TOKEN: adminToken,
80155
POSTGRES_PASSWORD: postgresPassword,
156+
METRICS_PORT: String(metricsPort),
81157
},
82-
ports: { http: apiPort, tcp: tcpPort, website: websitePort },
158+
ports: { http: apiPort, tcp: tcpPort, website: websitePort, metrics: metricsPort },
83159
volumeMounts: [
84160
{ name: "relay-logs", mountPath: "/app/logs" },
85161
],
@@ -90,7 +166,17 @@ export function buildContainer(def: ContainerDef, ctx: Ctx): ContainerResult {
90166
configs: [
91167
{
92168
filename: "helix.yml",
93-
content: helixYaml(ctx.url(beacon, "http"), ctx.url(sim, "rpc"), apiPort, tcpPort),
169+
content: helixYaml({
170+
beaconUrl: ctx.url(beacon, "http"),
171+
simUrl: ctx.url(sim, "rpc"),
172+
apiPort,
173+
tcpPort,
174+
headerDelay,
175+
optimistic,
176+
builderPubkey,
177+
collateral,
178+
coreCount,
179+
}),
94180
mountPath: "/config/helix.yml",
95181
},
96182
],

containers/rbuilder.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,13 @@ import type { ContainerDef, ContainerResult, Ctx, HostCtx, ProcessDef, ProcessRe
22

33
const DEFAULT_HTTP_PORT = 8745;
44

5+
// BLS pubkey (G1, 48 bytes) of relay_secret_key below — the builder identity the
6+
// relay sees on submitBlock. Relays mark THIS pubkey optimistic to enable the
7+
// fast path. (Derived from the secret key; the devnet reuses validator-0's BLS
8+
// key, so it coincides with the sole proposer's pubkey.)
9+
export const RELAY_BUILDER_PUBKEY =
10+
"0xa99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c";
11+
512
export const ports = {
613
http: { port: DEFAULT_HTTP_PORT, protocol: "TCP" as const, service: false },
714
// Telemetry servers (see rbuilderConfigFor): redacted = http+1, full = http+2.

recipes/contender-bench.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import type { Recipe } from "../utils/types.ts";
1111
// No `artifacts`: contender is a pure client and consumes none, so up() skips
1212
// generation entirely.
1313
export function contenderBench(
14-
opts: { rpcUrl: string; txsUrl?: string; duration?: number; privKey?: string },
14+
opts: { rpcUrl: string; txsUrl?: string; duration?: number; privKey?: string; tps?: number; scenario?: string },
1515
): Recipe {
1616
return {
1717
pods: [
@@ -24,6 +24,8 @@ export function contenderBench(
2424
config: {
2525
rpcUrl: opts.rpcUrl,
2626
duration: opts.duration ?? 30,
27+
...(opts.tps ? { tps: opts.tps } : {}),
28+
...(opts.scenario ? { scenario: opts.scenario } : {}),
2729
...(opts.txsUrl ? { txsUrl: opts.txsUrl } : {}),
2830
...(opts.privKey ? { privKey: opts.privKey } : {}),
2931
},

recipes/relay-bench.ts

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,11 @@
11
import type { Recipe } from "../utils/types.ts";
2-
import { benchmarkRelays } from "../scripts/relay-bench.ts";
3-
import { recipe as helix } from "./relay/helix.ts";
4-
import { recipe as mevBoostRelay } from "./relay/mev-boost-relay.ts";
2+
import { benchmarkRelays, defaultTargets } from "../scripts/relay-bench.ts";
53

6-
// Parent: no devnet of its own. Its script brings up each base single-relay
7-
// recipe in turn, loads it, measures, tears it down, and prints the comparison.
8-
// `decker up relay-bench` runs the whole thing.
4+
// Parent: no devnet of its own. Its script benchmarks each relay in both sync and
5+
// optimistic mode — bringing up a fresh single-relay devnet per (relay, mode),
6+
// loading the one builder, measuring from the relay's native metrics, tearing it
7+
// down — then prints the comparison. `decker up relay-bench` runs the whole thing.
98
export const recipe: Recipe = {
109
pods: [],
11-
scripts: [benchmarkRelays([
12-
{ name: "helix", label: "helix-1", recipe: helix, relayPort: 4040 },
13-
{ name: "mev-boost-relay", label: "mev-boost-relay-1", recipe: mevBoostRelay, relayPort: 9062 },
14-
])],
10+
scripts: [benchmarkRelays(await defaultTargets())],
1511
};

0 commit comments

Comments
 (0)