Skip to content

Staked Builder API REST Client - #11026

Open
StefanBratanov wants to merge 20 commits into
Consensys:masterfrom
StefanBratanov:gloas-staked-builder-api-rest-client
Open

Staked Builder API REST Client#11026
StefanBratanov wants to merge 20 commits into
Consensys:masterfrom
StefanBratanov:gloas-staked-builder-api-rest-client

Conversation

@StefanBratanov

@StefanBratanov StefanBratanov commented Jul 29, 2026

Copy link
Copy Markdown
Contributor

PR Description

Client for the builder rest api as per ethereum/builder-specs#138 . There is a repetition but I found it difficult to create a generic rest client implementation that both the VC client and builder client can use.

Fixed Issue(s)

related to #10822

Documentation

  • I thought about documentation and added the doc-change-required label to this PR if updates are required.

Changelog

  • I thought about adding a changelog entry, and added one if I deemed necessary.

Note

Medium Risk
New outbound client for execution payload bids and block submission on the proposing path; additive and isolated in this PR but correctness will matter once wired into the validator client.

Overview
Adds a new builder:rest Gradle module with an OkHttp-based Staked Builder API client aligned with builder-specs#138.

StakedBuilderClient exposes three async operations: fetch execution payload bids (optional SignedRequestAuth JSON body vs empty POST), submit builder preferences (JSON), and submit signed beacon blocks (SSZ application/octet-stream). Shared plumbing includes BuilderApiMethod path templates, AbstractBuilderRequest for JSON/empty/octet-stream POSTs, ResponseHandler for status codes (200/202/204 vs 4xx/5xx → BuilderClientException), and Eth-Consensus-Version headers from the spec milestone.

OkHttpStakedBuilderClient wraps the per-endpoint handlers on an AsyncRunner. Gloas-milestone MockWebServer integration tests cover success paths, request shape, and error handling. The parent builder module is included in settings.gradle with jar packaging disabled.

Reviewed by Cursor Bugbot for commit 26c5540. Bugbot is set up for automated code reviews on this repo. Configure here.

@StefanBratanov StefanBratanov changed the title Gloas staked builder api rest client Staked Builder API REST Client Jul 29, 2026
@StefanBratanov
StefanBratanov force-pushed the gloas-staked-builder-api-rest-client branch from 0baf796 to 71a3287 Compare July 29, 2026 15:06
Comment thread builder/rest/src/main/java/tech/pegasys/teku/builder/rest/BuilderApiMethod.java Outdated
@StefanBratanov
StefanBratanov force-pushed the gloas-staked-builder-api-rest-client branch from 5c4ae8d to 68b3483 Compare July 30, 2026 09:37

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

Reviewed by Cursor Bugbot for commit 68b3483. Configure here.

@StefanBratanov
StefanBratanov force-pushed the gloas-staked-builder-api-rest-client branch from 75a1255 to f07ae53 Compare July 31, 2026 07:57
@Consensys Consensys deleted a comment from cursor Bot Jul 31, 2026
@StefanBratanov
StefanBratanov force-pushed the gloas-staked-builder-api-rest-client branch 3 times, most recently from 921918d to 1f5f5d0 Compare August 3, 2026 08:00
@StefanBratanov
StefanBratanov force-pushed the gloas-staked-builder-api-rest-client branch 2 times, most recently from 6ed7989 to f48c9d8 Compare August 5, 2026 13:17
@tbenr

tbenr commented Aug 5, 2026

Copy link
Copy Markdown
Contributor
  • Missing Eth-Consensus-Version request header on two endpoints. The spec marks this header required: true on both getExecutionPayloadBid and submitBuilderPreferences (the description qualifies it as "Required
    if the request body is SSZ encoded", but the OpenAPI declares it required unconditionally). The client sends it only for submitSignedBeaconBlock. A builder validating strictly against the OpenAPI schema would
    reject the other two requests. Since the milestone is trivially available, I'd send it on all three (it also future-proofs the JSON body across forks).

  • ResponseHandler.VOID is a shared mutable singleton. withHandler() is public and mutates the internal Int2ObjectOpenHashMap, which is not thread-safe. Any future caller doing
    ResponseHandler.VOID.withHandler(...) would mutate global state visible to all concurrent requests. Either make withHandler copy-on-write, drop it from the shared instance, or replace VOID with a factory method
    (ResponseHandler.voidHandler()).

  • All three central names already exist in the codebase for the legacy MEV-Boost Builder API. When this gets wired into ExecutionLayerManager (which already imports the old BuilderClient), both interfaces will coexist in the same call sites and imports will be genuinely confusing. Suggest
    StakedBuilderClient / StakedBuilderApiMethod or similar. Also note the old enum uses :param placeholders while the new one uses {param} — if both survive, converging on one convention would help.

  • AbstractBuilderRequestTestBase is named Abstract... but declared as a plain public class — declare it abstract.

  • getErrorMessage reads the full error body unbounded; a broken/hostile builder could return a huge payload that ends up in an exception message and log. Consider truncating (e.g. body.source().readUtf8(MAX)).

    Test coverage

    • shouldIncludeSignedRequestAuthInBodyWhenPresent only asserts body.size() > 0 — it should assert Content-Type: application/json and ideally round-trip the body back to the SignedRequestAuth to prove the schema
      used is the right one.
    • No test for the unknownResponseCodeHandler path (e.g. a 418) or for 503, and no test for a malformed 200 JSON body on the bid endpoint.

@rolfyone

rolfyone commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

would be good to add the references like OpenApiIntegrationTest does so that we can easily see the api definitions...

@StefanBratanov
StefanBratanov force-pushed the gloas-staked-builder-api-rest-client branch from f48c9d8 to 01d69a4 Compare August 6, 2026 13:55

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

Reviewed by Cursor Bugbot for commit b1cde23. Configure here.

Comment thread builder/rest/src/main/java/tech/pegasys/teku/builder/rest/ResponseHandler.java Outdated
@StefanBratanov

Copy link
Copy Markdown
Contributor Author

@tbenr fixed the 5 points, will tackle testing one separately

@StefanBratanov

Copy link
Copy Markdown
Contributor Author

would be good to add the references like OpenApiIntegrationTest does so that we can easily see the api definitions...

They will show eventually anyways when we pipe them through the Beacon API.

@tbenr

tbenr commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

just one minor thing: Eth-Consensus-Version presence is not tested in all methods

…oas/ePBS

Adds a new builder:rest Gradle submodule with an OkHttp-based REST client
for the Staked Builder API defined in builder-specs PR Consensys#138. The module is
self-contained with no wiring into existing Teku infrastructure yet.

Files added:
- BuilderClient interface (four Gloas builder endpoints)
- BuilderApiMethod enum with {param} URL template substitution
- BuilderIdentity record with JSON type definition and data wrapper
- BuilderClientException for typed error handling (with HTTP status code)
- ResponseHandler with per-status-code deserialization (200/202/204/400/401/500/503)
- AbstractBuilderRequest base class (GET, postJson, postEmpty, postOctetStream helpers)
- Four handler classes: GetBuilderIdentityRequest, GetExecutionPayloadBidRequest,
  SendBuilderPreferencesRequest, SendSignedBeaconBlockRequest
- OkHttpBuilderClient wrapping handlers with AsyncRunner for SafeFuture responses
- Integration tests using MockWebServer for all four handlers
…nt-Type

- SendSignedBeaconBlockRequest now takes Spec and sends Eth-Consensus-Version
  header derived from the block's slot milestone; postOctetStream gains a
  headers overload to support this
- postEmpty no longer sets Content-Type: application/json on a zero-length
  body; uses null MediaType so no Content-Type is sent (empty body is not
  valid JSON and would cause builders to return 400)
@StefanBratanov
StefanBratanov force-pushed the gloas-staked-builder-api-rest-client branch from bdcdaec to fa389c9 Compare August 10, 2026 17:33
@StefanBratanov

Copy link
Copy Markdown
Contributor Author

just one minor thing: Eth-Consensus-Version presence is not tested in all methods

Done

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants