Skip to content

Commit

Permalink
feat: add ssz support to builder api (#7180)
Browse files Browse the repository at this point in the history
* feat: add ssz support to builder api

* Rephrase comment
  • Loading branch information
nflaig authored Oct 21, 2024
1 parent e01142b commit 0e4ea98
Show file tree
Hide file tree
Showing 2 changed files with 37 additions and 10 deletions.
22 changes: 17 additions & 5 deletions packages/api/src/builder/routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,9 +78,6 @@ export type Endpoints = {
>;
};

// NOTE: Builder API does not support SSZ as per spec, need to keep routes as JSON-only for now
// See https://github.com/ethereum/builder-specs/issues/53 for more details

export function getDefinitions(config: ChainForkConfig): RouteDefinitions<Endpoints> {
return {
status: {
Expand Down Expand Up @@ -125,7 +122,7 @@ export function getDefinitions(config: ChainForkConfig): RouteDefinitions<Endpoi
submitBlindedBlock: {
url: "/eth/v1/builder/blinded_blocks",
method: "POST",
req: JsonOnlyReq({
req: {
writeReqJson: ({signedBlindedBlock}) => {
const fork = config.getForkName(signedBlindedBlock.message.slot);
return {
Expand All @@ -141,11 +138,26 @@ export function getDefinitions(config: ChainForkConfig): RouteDefinitions<Endpoi
signedBlindedBlock: getExecutionForkTypes(fork).SignedBlindedBeaconBlock.fromJson(body),
};
},
writeReqSsz: ({signedBlindedBlock}) => {
const fork = config.getForkName(signedBlindedBlock.message.slot);
return {
body: getExecutionForkTypes(fork).SignedBlindedBeaconBlock.serialize(signedBlindedBlock),
headers: {
[MetaHeader.Version]: fork,
},
};
},
parseReqSsz: ({body, headers}) => {
const fork = toForkName(fromHeaders(headers, MetaHeader.Version));
return {
signedBlindedBlock: getExecutionForkTypes(fork).SignedBlindedBeaconBlock.deserialize(body),
};
},
schema: {
body: Schema.Object,
headers: {[MetaHeader.Version]: Schema.String},
},
}),
},
resp: {
data: WithVersion<ExecutionPayload | ExecutionPayloadAndBlobsBundle, VersionMeta>((fork: ForkName) => {
return isForkBlobs(fork)
Expand Down
25 changes: 20 additions & 5 deletions packages/beacon-node/src/execution/builder/http.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import {SLOTS_PER_EPOCH, ForkExecution} from "@lodestar/params";
import {toPrintableUrl} from "@lodestar/utils";
import {Metrics} from "../../metrics/metrics.js";
import {IExecutionBuilder} from "./interface.js";
import {WireFormat} from "@lodestar/api";

export type ExecutionBuilderHttpOpts = {
enabled: boolean;
Expand Down Expand Up @@ -53,6 +54,13 @@ export class ExecutionBuilderHttp implements IExecutionBuilder {
faultInspectionWindow: number;
allowedFaults: number;

/**
* Determine if SSZ is supported by requesting an SSZ encoded response in the `getHeader` request.
* The builder responding with a SSZ serialized `SignedBuilderBid` indicates support to handle the
* `SignedBlindedBeaconBlock` as SSZ serialized bytes instead of JSON when calling `submitBlindedBlock`.
*/
private sszSupported = false;

constructor(
opts: ExecutionBuilderHttpOpts,
config: ChainForkConfig,
Expand Down Expand Up @@ -123,24 +131,31 @@ export class ExecutionBuilderHttp implements IExecutionBuilder {
blobKzgCommitments?: deneb.BlobKzgCommitments;
executionRequests?: electra.ExecutionRequests;
}> {
const signedBuilderBid = (
await this.api.getHeader({slot, parentHash, proposerPubkey}, {timeoutMs: BUILDER_PROPOSAL_DELAY_TOLERANCE})
).value();
const res = await this.api.getHeader(
{slot, parentHash, proposerPubkey},
{timeoutMs: BUILDER_PROPOSAL_DELAY_TOLERANCE}
);
const signedBuilderBid = res.value();

if (!signedBuilderBid) {
throw Error("No bid received");
}

this.sszSupported = res.wireFormat() === WireFormat.ssz;

const {header, value: executionPayloadValue} = signedBuilderBid.message;
const {blobKzgCommitments} = signedBuilderBid.message as deneb.BuilderBid;
const {executionRequests} = signedBuilderBid.message as electra.BuilderBid;
return {header, executionPayloadValue, blobKzgCommitments, executionRequests};
}

async submitBlindedBlock(signedBlindedBlock: SignedBlindedBeaconBlock): Promise<SignedBeaconBlockOrContents> {
const data = (await this.api.submitBlindedBlock({signedBlindedBlock}, {retries: 2})).value();
const res = await this.api.submitBlindedBlock(
{signedBlindedBlock},
{retries: 2, requestWireFormat: this.sszSupported ? WireFormat.ssz : WireFormat.json}
);

const {executionPayload, blobsBundle} = parseExecutionPayloadAndBlobsBundle(data);
const {executionPayload, blobsBundle} = parseExecutionPayloadAndBlobsBundle(res.value());

// for the sake of timely proposals we can skip matching the payload with payloadHeader
// if the roots (transactions, withdrawals) don't match, this will likely lead to a block with
Expand Down

0 comments on commit 0e4ea98

Please sign in to comment.