op-conductor,op-node: SSZ-binary commit-unsafe-payload endpoint#36
Draft
BrianBland wants to merge 1 commit into
Draft
op-conductor,op-node: SSZ-binary commit-unsafe-payload endpoint#36BrianBland wants to merge 1 commit into
BrianBland wants to merge 1 commit into
Conversation
Adds a non-JSON-RPC HTTP endpoint to op-conductor for committing unsafe
payloads, and an opt-in op-node client switch to use it.
## Wire protocol
POST /commit-unsafe-payload
Content-Type: application/octet-stream
Body: SSZ-encoded ExecutionPayloadEnvelope (raw bytes, no length prefix)
Response is 200 OK on success, 4xx/5xx with plain-text body otherwise. The
body cap is 16 MiB by default, comfortably fitting future 10 MB SSZ blocks.
## Why
The existing JSON-RPC commit-unsafe-payload encodes the entire payload as
JSON, which is dominated by encoding/json's per-byte tokenizer. On the
sequencer the leader-side RPC handler currently spends ~10ms (mainnet
typical) to ~27ms (peak) on JSON decode alone, before raft replication
even starts. Calibration against the op-conductor commit-unsafe-payload
latency notebook (DD #13980177) confirms ~109 MB/s JSON decode throughput
matches loopback bench predictions.
## Speedup
Loopback bench (M4 Max, 2 MB SSZ payload):
JSON-RPC end-to-end: 38.4 ms (109 MB/s)
Binary SSZ end-to-end: 1.4 ms (1.5 GB/s) ~27x faster
Production extrapolation (linear scaling):
Mainnet typical (~500 KB SSZ): 10.1 ms -> ~1.7 ms (~6x)
Mainnet peak (~1.3 MB SSZ): 27.1 ms -> ~2.5 ms (~11x)
Future 10 MB SSZ blocks: blocked (5 MB JSON-RPC body cap) -> ~7 ms
The remaining ~1.4 ms residual on the binary path is the HTTP machinery
floor (verified by a discard-only handler at 1.37 ms).
## Op-node integration
Off by default. Opt in with --conductor.binary-commit (env
OP_NODE_CONDUCTOR_BINARY_COMMIT=true). The op-node side keeps the
existing JSON-RPC client and adds a parallel BinaryCommitClient that
points at the same conductor URL (the binary endpoint shares the HTTP
server with the JSON-RPC route). When the flag is on, CommitUnsafePayload
uses the binary client; everything else (Leader, OverrideLeader, etc.)
stays on JSON-RPC.
External CL clients (e.g. base's Rust replacement for op-node) can
target the same endpoint with any HTTP client + SSZ encoder.
## Test plan
- [ ] Devnet: run a 3-node conductor cluster with op-node using the new flag
- [ ] Compare commit_unsafe_payload latency before/after on devnet
- [ ] Roll the conductor side first (binary endpoint is additive; JSON-RPC
still served), then op-node opt-in per-environment
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
3 tasks
BrianBland
added a commit
to BrianBland/base
that referenced
this pull request
May 13, 2026
Adds support in base-consensus for the new conductor SSZ-binary commit-unsafe-payload endpoint (base/optimism#36). When --conductor.binary-commit (BASE_NODE_CONDUCTOR_BINARY_COMMIT=true) is set, ConductorClient.commit_unsafe_payload SSZ-encodes the payload via ssz::Encode and POSTs it to <conductor>/commit-unsafe-payload as raw application/octet-stream. The other conductor RPCs (leader, active, override_leader) continue to use jsonrpsee. ## Why Calibration against the op-conductor commit-unsafe-payload latency notebook (Datadog #13980177) and a loopback bench shows JSON-RPC commit-unsafe-payload spends ~10ms (mainnet typical) to ~27ms (peak) on encoding/json's per-byte tokenizer alone, before raft replication starts. The binary endpoint drops this to ~1.4ms regardless of payload size, ~6-11x speedup on production payloads, and removes the 5 MiB JSON-RPC body cap that currently blocks 10 MB blocks. ## Wire format (matches op-conductor BinaryCommitHandler) POST /commit-unsafe-payload Content-Type: application/octet-stream Body: SSZ-encoded BaseExecutionPayloadEnvelope (raw bytes, no length prefix). For V3+ payloads the parent_beacon_block_root is the first 32 bytes per <BaseExecutionPayloadEnvelope as ssz::Encode>, which matches Go's eth.ExecutionPayloadEnvelope.MarshalSSZ. 200 OK on success; 4xx/5xx with plain-text body otherwise. ## Rollout Default off. Conductor side is additive (binary endpoint coexists with JSON-RPC). Roll the conductor first, then set BASE_NODE_CONDUCTOR_BINARY_COMMIT per environment. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
POST /commit-unsafe-payloadaccepts a raw SSZ-encodedExecutionPayloadEnvelopebody. Bypasses JSON-RPC's per-byte tokenizer cost.--conductor.binary-commit(envOP_NODE_CONDUCTOR_BINARY_COMMIT). Uses the same conductor URL — binary endpoint shares the HTTP server with the JSON-RPC route.Wire protocol
200 OK on success; 4xx/5xx with plain-text body otherwise. Body cap 16 MiB (fits 10 MB SSZ blocks comfortably).
Speedup
Loopback bench (M4 Max, 2 MB SSZ):
Production extrapolation (per DD #13980177 calibration — loopback throughput matches mainnet on a per-byte basis):
Floor is the HTTP machinery itself (~1.37 ms for 2 MB; verified by a discard-only handler).
Why not just hex-encode SSZ in JSON-RPC?
I tried that variant — same speed as structured JSON-RPC. The bottleneck is
encoding/jsonlexing through every byte of the body looking for backslash escapes, regardless of whether it's one big string token or thousands of small tokens. Only escaping JSON entirely helps.Why not gRPC / Unix socket / custom framed TCP?
Plain HTTP is already at the floor and stays Rust-friendly.
Op-node integration
Off by default. Existing JSON-RPC method untouched. Rollout is conductor-side first (additive — both routes served), then op-node opt-in per environment.
Stacked on
PR #35 (raft-mdb backend + commit-path metrics) — base branch is
brianbland/conductor-raft-mdb. The metrics from #35 are what enable verifying the speedup in production.Test plan
commit_unsafe_payloadlatency before/after on devnet (the new metrics from [NOMERGE] op-conductor: add configurable raft-mdb backend #35 give the breakdown)🤖 Generated with Claude Code