Repository navigation
epbs builder api endpoints - #505
JasonVranek wants to merge 6 commits into
Conversation
Pin the lighthouse crates to sigp/lighthouse unstable at 31d8cfd, whose gloas containers hash as EIP-7495 progressive containers, matching the consensus specs. Mirror lighthouse's own [patch.crates-io] entries for the progressive ssz stack (a consumer does not inherit a dependency's patches) and pin the blstrs_plus patch to an explicit rev. lighthouse dropped `TestRandom` for an `arbitrary` generator, so `TestRandomSeed` now draws from `arbitrary::Arbitrary`. Validated BLS points do not randomize under `arbitrary`, so tests that need a real key use `BlsSecretKey::random()`. Also adapt to `ForkName::Heze` and to `ExecutionRequests` no longer implementing `ssz::Decode`.
Tests discovered a free port, dropped the listener and let the server rebind it, leaving a window where another process could take the port. PbsService and SigningService gain `run_with_listener`, the mock SSV servers take a bound listener, and the legacy suites hand their listeners straight to the server. Also add `wait_for_ready`, which polls /status instead of sleeping a fixed 100 ms.
From the Gloas fork the beacon node calls three builder-API endpoints on
Commit-Boost instead of get_header and get_payload:
- POST /eth/v1/builder/execution_payload_bid/{slot}/{parent_hash}/{parent_root}/{proposer_pubkey}
sends the request to the one relay its auth data names and returns that
relay's bid (200), its 400 or 401, or 204 when it has no bid or fails.
Commit-Boost does not validate or rank the bid: the beacon node checks it,
and each request names exactly one builder.
- POST /eth/v1/builder/builder_preferences/{proposer_pubkey} forwards to the
relay the auth data names and returns that builder's answer.
- POST /eth/v1/builder/beacon_blocks forwards the SSZ block bytes unparsed to
every configured relay and answers 202 when one accepts.
Requests must carry Eth-Consensus-Version: gloas. A body without
Content-Type is JSON and a request without Accept gets JSON, as
builder-specs requires. Bid requests need Date-Milliseconds and
X-Timeout-Ms; Commit-Boost clamps the deadline to one slot, keeps
proposer_deadline_buffer_ms back for the return trip and sends the rest to
the builder as its own X-Timeout-Ms, or returns 204 when nothing is left.
Routing follows builder-specs #168: auth data matches a relay when it equals
the lowercase hostname of the relay URL. The first match in config order wins
and unmatched auth data is a 400. Commit-Boost never verifies auth data; the
builder does. Relay-supplied amounts are saturated.
The new operator page (docs/get_started/epbs.md) covers writing each
validator key's builder config through the keymanager API, with a
copy-paste call, plus routing, timing, metrics and troubleshooting.
Unreleased, shipping from v0.12.0-rc1.
| pub(crate) async fn send_to_relay( | ||
| req: reqwest::RequestBuilder, | ||
| relay: &RelayClient, | ||
| tag: &str, | ||
| ) -> Result<(reqwest::Response, Duration), PbsError> { | ||
| let start_request = Instant::now(); | ||
| let res = match req.send().await { | ||
| Ok(res) => res, | ||
| Err(err) => { | ||
| RELAY_STATUS_CODE.with_label_values(&[TIMEOUT_ERROR_CODE_STR, tag, &relay.id]).inc(); | ||
| return Err(err.into()); | ||
| } | ||
| }; | ||
|
|
||
| let request_latency = start_request.elapsed(); | ||
| RELAY_LATENCY.with_label_values(&[tag, &relay.id]).observe(request_latency.as_secs_f64()); | ||
|
|
||
| let code = res.status(); | ||
| RELAY_STATUS_CODE.with_label_values(&[code.as_str(), tag, &relay.id]).inc(); | ||
|
|
||
| Ok((res, request_latency)) | ||
| } |
There was a problem hiding this comment.
get_header and get_payload routes also do similar work, but inline
can be reused to remove duplicate code
There was a problem hiding this comment.
agreed but would rather leave PBS untouched so nothing can break and then deprecate PBS routes post fork / adopt this for /status
| /// `/eth/v1/builder/execution_payload_bid/{slot}/{parent_hash}/{parent_root}/ | ||
| /// {proposer_pubkey}` | ||
| #[derive(Debug, Serialize, Deserialize, Clone)] | ||
| pub struct GetExecutionPayloadBidParams { |
There was a problem hiding this comment.
the naming of this is super confusing, its a POST called Get.
There was a problem hiding this comment.
early on in the builder spec timeline, the BuilderRequestAuth was just a header, then some advocated for it to be part of the body
| #[derive(Debug, Serialize, Deserialize, Clone)] | ||
| pub struct SubmitBuilderPreferencesParams { | ||
| /// The public key of the proposer expressing these preferences | ||
| pub proposer_pubkey: BlsPublicKey, |
There was a problem hiding this comment.
out of interest, why is the proposer pubkey specified as a path parameter and not a field on the request?
There was a problem hiding this comment.
also part of builder spec, i believe the reasoning was it was to remain as similar to the get_header path params
| Err(PbsClientError::AuthDataMismatch) | ||
| } | ||
| } | ||
| } |
There was a problem hiding this comment.
this seems a bit fragile? if I understand correctly, the beacon node must encode into the auth data the URL of a relay that is configured for the proposer pubkey?
why do we have the requirement that the relay is configured in commit boost?
There was a problem hiding this comment.
the BN doesn't build the auth data, it just sends whatever the VC's config held and signed. The spec says if auth data isn't specified, VCs should default to using the url's lowercase hostname. So if any node operator mistakenly set's only url = cb.example.com but omits auth_data, then the VC default will set auth_data = cb.example.com and CB can't dial the request properly and must 400.
so there's a couple cases:
url = cb.example.comandauth_data = builder.example.com-> CB dials correctlyurl = auth_data = cb.example.com-> CB 400surl = cb.example.comandauth_data = junk-> CB 400s
In a follow up PR we'll have the CB CLI tool to programmatically set auth_data correctly via KeyManager API to be in case 1. Since the CLI tool sources from CB's config resolve_addressed_relay would find it. That being said, I agree we don't need to be this restrictive. We may end up in case 1 if another tool is used to call KeyManager API or the user manually enters or something. Also in a followup PR we relaxed this to try to dial to what auth_data holds even if it's not known to CB's config with a guard for SSRF.
|
|
||
| // Only the winner accepts, so one 202 across the broadcast is success | ||
| if accepted == 0 { | ||
| return Err(PbsClientError::NoBuilderResponse); |
There was a problem hiding this comment.
I am not entirely convinced relays will do anything with these requests - they have no particular interest in trusting what the proposer sends vs. observing what is gossiped on p2p - so we should be careful not to treat an apparent failure as an actual problem.
There was a problem hiding this comment.
the beacon blocks are signed, but if you're referring to relays/builders waiting long enough to know the block won't get reorged out then agree / agree with your posts on discord that it isn't particularly useful info to get back a success when your BN gossips it anyways
condition and lint test
This PR adds the core functionality for the three new builder-API endpoints and later PRs will extend it.
What it does
POST /eth/v1/builder/execution_payload_bid/{slot}/{parent_hash}/{parent_root}/{proposer_pubkey}: sends the request to the relay its auth data names and returns that relay's bid as200, the builder's own400or401, or204when it has no bid or fails (timeout, 5xx, unreachable, undecodable). Commit-Boost does not validate or rank the bid: the beacon node checks it against the builder registry and the key's builder config, and each request names exactly one builder.POST /eth/v1/builder/builder_preferences/{proposer_pubkey}: forwards to the relay its auth data names and returns that builder's answer (202, or its400/401).POST /eth/v1/builder/beacon_blocks: forwards the SSZ block bytes unparsed to every configured relay and answers202when one accepts (only the winning builder does). A non-SSZ body is a415.Decisions
Date-MillisecondsandX-Timeout-Msheaders, and reservesproposer_deadline_buffer_ms(default 50) back for the return trip.Left to later PRs
cb-km, a tool that writes validators' builder configs from the Commit-Boost config (includingmax_execution_paymentwith an explicit unclamped setting), and the config fields only it reads.Configuring it by hand
Until
cb-kmlands, each validator key's builder config is written through the validator client's keymanager API (keymanager-APIs PR88). For every builder, add an entry whoseurlis Commit-Boost's own URL and whoseauth_datais the hex of the builder's hostname bytes (echo 0x$(printf builder-a.example.com | xxd -p | tr -d '\n')). A key without builder config sends Commit-Boost's own hostname, which matches no relay, so its bid requests get400. The newdocs/get_started/epbs.mdpage walks through it with a worked example.Testing