Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: decouple the deneb blob and block production #5492

Merged
merged 4 commits into from
May 19, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
64 changes: 44 additions & 20 deletions packages/api/src/beacon/routes/beacon/block.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,14 @@ import {
} from "../../../utils/index.js";
import {HttpStatusCode} from "../../../utils/client/httpStatusCode.js";
import {ApiClientResponse} from "../../../interfaces.js";
import {
SignedBlockContents,
SignedBlindedBlockContents,
isSignedBlockContents,
isSignedBlindedBlockContents,
AllForksSignedBlockContentsReqSerializer,
AllForksSignedBlindedBlockContentsReqSerializer,
} from "../../../utils/routes.js";

// See /packages/api/src/routes/index.ts for reasoning and instructions to add new routes

Expand Down Expand Up @@ -150,7 +158,7 @@ export type Api = {
* @param requestBody The `SignedBeaconBlock` object composed of `BeaconBlock` object (produced by beacon node) and validator signature.
* @returns any The block was validated successfully and has been broadcast. It has also been integrated into the beacon node's database.
*/
publishBlock(block: allForks.SignedBeaconBlock): Promise<
publishBlock(blockOrContents: allForks.SignedBeaconBlock | SignedBlockContents): Promise<
ApiClientResponse<
{
[HttpStatusCode.OK]: void;
Expand All @@ -163,7 +171,7 @@ export type Api = {
* Publish a signed blinded block by submitting it to the mev relay and patching in the block
* transactions beacon node gets in response.
*/
publishBlindedBlock(block: allForks.SignedBlindedBeaconBlock): Promise<
publishBlindedBlock(blindedBlockOrContents: allForks.SignedBlindedBeaconBlock | SignedBlindedBlockContents): Promise<
ApiClientResponse<
{
[HttpStatusCode.OK]: void;
Expand All @@ -173,14 +181,14 @@ export type Api = {
>
>;
/**
* Get block BlobsSidecar
* Retrieves BlobsSidecar included in requested block.
* Get block BlobSidecar
* Retrieves BlobSidecar included in requested block.
* @param blockId Block identifier.
* Can be one of: "head" (canonical head in node's view), "genesis", "finalized", \<slot\>, \<hex encoded blockRoot with 0x prefix\>.
*/
getBlobsSidecar(blockId: BlockId): Promise<
getBlobSidecars(blockId: BlockId): Promise<
ApiClientResponse<{
[HttpStatusCode.OK]: {executionOptimistic: ExecutionOptimistic; data: deneb.BlobsSidecar};
[HttpStatusCode.OK]: {executionOptimistic: ExecutionOptimistic; data: deneb.BlobSidecars};
}>
>;
};
Expand All @@ -197,7 +205,7 @@ export const routesData: RoutesData<Api> = {
getBlockRoot: {url: "/eth/v1/beacon/blocks/{block_id}/root", method: "GET"},
publishBlock: {url: "/eth/v1/beacon/blocks", method: "POST"},
publishBlindedBlock: {url: "/eth/v1/beacon/blinded_blocks", method: "POST"},
getBlobsSidecar: {url: "/eth/v1/beacon/blobs_sidecars/{block_id}", method: "GET"},
getBlobSidecars: {url: "/eth/v1/beacon/blob_sidecars/{block_id}", method: "GET"},
};

/* eslint-disable @typescript-eslint/naming-convention */
Expand All @@ -213,7 +221,7 @@ export type ReqTypes = {
getBlockRoot: BlockIdOnlyReq;
publishBlock: {body: unknown};
publishBlindedBlock: {body: unknown};
getBlobsSidecar: BlockIdOnlyReq;
getBlobSidecars: BlockIdOnlyReq;
};

export function getReqSerializers(config: ChainForkConfig): ReqSerializers<Api, ReqTypes> {
Expand All @@ -227,21 +235,37 @@ export function getReqSerializers(config: ChainForkConfig): ReqSerializers<Api,
const getSignedBeaconBlockType = (data: allForks.SignedBeaconBlock): allForks.AllForksSSZTypes["SignedBeaconBlock"] =>
config.getForkTypes(data.message.slot).SignedBeaconBlock;

const AllForksSignedBeaconBlock: TypeJson<allForks.SignedBeaconBlock> = {
toJson: (data) => getSignedBeaconBlockType(data).toJson(data),
fromJson: (data) => getSignedBeaconBlockType(data as unknown as allForks.SignedBeaconBlock).fromJson(data),
const AllForksSignedBlockOrContents: TypeJson<allForks.SignedBeaconBlock | SignedBlockContents> = {
toJson: (data) =>
isSignedBlockContents(data)
? AllForksSignedBlockContentsReqSerializer(getSignedBeaconBlockType).toJson(data)
: getSignedBeaconBlockType(data as allForks.SignedBeaconBlock).toJson(data as allForks.SignedBeaconBlock),

fromJson: (data) =>
(data as {signed_block: unknown}).signed_block !== undefined
? AllForksSignedBlockContentsReqSerializer(getSignedBeaconBlockType).fromJson(data)
: getSignedBeaconBlockType(data as allForks.SignedBeaconBlock).fromJson(data),
};

const getSignedBlindedBeaconBlockType = (
data: allForks.SignedBlindedBeaconBlock
): allForks.AllForksBlindedSSZTypes["SignedBeaconBlock"] =>
config.getBlindedForkTypes(data.message.slot).SignedBeaconBlock;

const AllForksSignedBlindedBeaconBlock: TypeJson<allForks.SignedBlindedBeaconBlock> = {
toJson: (data) => getSignedBlindedBeaconBlockType(data).toJson(data),
fromJson: (data) =>
getSignedBlindedBeaconBlockType(data as unknown as allForks.SignedBlindedBeaconBlock).fromJson(data),
};
const AllForksSignedBlindedBlockOrContents: TypeJson<allForks.SignedBlindedBeaconBlock | SignedBlindedBlockContents> =
{
toJson: (data) =>
isSignedBlindedBlockContents(data)
? AllForksSignedBlindedBlockContentsReqSerializer(getSignedBlindedBeaconBlockType).toJson(data)
: getSignedBlindedBeaconBlockType(data as allForks.SignedBlindedBeaconBlock).toJson(
data as allForks.SignedBlindedBeaconBlock
),

fromJson: (data) =>
(data as {signed_blinded_block: unknown}).signed_blinded_block !== undefined
? AllForksSignedBlindedBlockContentsReqSerializer(getSignedBlindedBeaconBlockType).fromJson(data)
: getSignedBlindedBeaconBlockType(data as allForks.SignedBlindedBeaconBlock).fromJson(data),
};

return {
getBlock: blockIdOnlyReq,
Expand All @@ -254,9 +278,9 @@ export function getReqSerializers(config: ChainForkConfig): ReqSerializers<Api,
schema: {query: {slot: Schema.Uint, parent_root: Schema.String}},
},
getBlockRoot: blockIdOnlyReq,
publishBlock: reqOnlyBody(AllForksSignedBeaconBlock, Schema.Object),
publishBlindedBlock: reqOnlyBody(AllForksSignedBlindedBeaconBlock, Schema.Object),
getBlobsSidecar: blockIdOnlyReq,
publishBlock: reqOnlyBody(AllForksSignedBlockOrContents, Schema.Object),
publishBlindedBlock: reqOnlyBody(AllForksSignedBlindedBlockOrContents, Schema.Object),
getBlobSidecars: blockIdOnlyReq,
};
}

Expand All @@ -278,6 +302,6 @@ export function getReturnTypes(): ReturnTypes<Api> {
getBlockHeader: ContainerDataExecutionOptimistic(BeaconHeaderResType),
getBlockHeaders: ContainerDataExecutionOptimistic(ArrayOf(BeaconHeaderResType)),
getBlockRoot: ContainerDataExecutionOptimistic(RootContainer),
getBlobsSidecar: ContainerDataExecutionOptimistic(ssz.deneb.BlobsSidecar),
getBlobSidecars: ContainerDataExecutionOptimistic(ssz.deneb.BlobSidecars),
};
}
35 changes: 25 additions & 10 deletions packages/api/src/beacon/routes/validator.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import {ContainerType, fromHexString, toHexString, Type} from "@chainsafe/ssz";
import {ForkName} from "@lodestar/params";
import {ForkName, isForkBlobs, isForkExecution} from "@lodestar/params";
import {
allForks,
altair,
Expand Down Expand Up @@ -35,6 +35,12 @@ import {
ContainerData,
} from "../../utils/index.js";
import {fromU64Str, fromGraffitiHex, toU64Str, U64Str, toGraffitiHex} from "../../utils/serdes.js";
import {
BlockContents,
BlindedBlockContents,
AllForksBlockContentsResSerializer,
AllForksBlindedBlockContentsResSerializer,
} from "../../utils/routes.js";
import {ExecutionOptimistic} from "./beacon/block.js";

// See /packages/api/src/routes/index.ts for reasoning and instructions to add new routes
Expand Down Expand Up @@ -220,7 +226,7 @@ export type Api = {
graffiti: string
): Promise<
ApiClientResponse<
{[HttpStatusCode.OK]: {data: allForks.BeaconBlock; version: ForkName; blockValue: Wei}},
{[HttpStatusCode.OK]: {data: allForks.BeaconBlock | BlockContents; version: ForkName; blockValue: Wei}},
HttpStatusCode.BAD_REQUEST | HttpStatusCode.SERVICE_UNAVAILABLE
>
>;
Expand All @@ -231,7 +237,13 @@ export type Api = {
graffiti: string
): Promise<
ApiClientResponse<
{[HttpStatusCode.OK]: {data: allForks.BlindedBeaconBlock; version: ForkName; blockValue: Wei}},
{
[HttpStatusCode.OK]: {
data: allForks.BlindedBeaconBlock | BlindedBlockContents;
version: ForkName;
blockValue: Wei;
};
},
HttpStatusCode.BAD_REQUEST | HttpStatusCode.SERVICE_UNAVAILABLE
>
>;
Expand Down Expand Up @@ -624,14 +636,17 @@ export function getReturnTypes(): ReturnTypes<Api> {
getProposerDuties: WithDependentRootExecutionOptimistic(ArrayOf(ProposerDuty)),
getSyncCommitteeDuties: ContainerDataExecutionOptimistic(ArrayOf(SyncDuty)),
produceBlock: WithBlockValue(ContainerData(ssz.phase0.BeaconBlock)),
produceBlockV2: WithBlockValue(WithVersion((fork: ForkName) => ssz[fork].BeaconBlock)),
produceBlockV2: WithBlockValue(
WithVersion<allForks.BeaconBlock | BlockContents>((fork: ForkName) =>
isForkBlobs(fork) ? AllForksBlockContentsResSerializer(() => fork) : ssz[fork].BeaconBlock
)
),
produceBlindedBlock: WithBlockValue(
WithVersion((fork: ForkName) => {
if (fork === ForkName.phase0 || fork === ForkName.altair) {
throw Error(`No BlindedBlock for fork ${fork} previous to bellatrix`);
}
return ssz[fork].BlindedBeaconBlock;
})
WithVersion<allForks.BlindedBeaconBlock | BlindedBlockContents>((fork: ForkName) =>
isForkBlobs(fork)
? AllForksBlindedBlockContentsResSerializer(() => fork)
: ssz.allForksBlinded[isForkExecution(fork) ? fork : ForkName.bellatrix].BeaconBlock
)
),
produceAttestationData: ContainerData(ssz.phase0.AttestationData),
produceSyncCommitteeContribution: ContainerData(ssz.altair.SyncCommitteeContribution),
Expand Down
3 changes: 2 additions & 1 deletion packages/api/src/builder/routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import {
import {getReqSerializers as getBeaconReqSerializers} from "../beacon/routes/beacon/block.js";
import {HttpStatusCode} from "../utils/client/httpStatusCode.js";
import {ApiClientResponse} from "../interfaces.js";
import {SignedBlindedBlockContents} from "../utils/routes.js";

export type Api = {
status(): Promise<ApiClientResponse<{[HttpStatusCode.OK]: void}, HttpStatusCode.SERVICE_UNAVAILABLE>>;
Expand All @@ -35,7 +36,7 @@ export type Api = {
>
>;
submitBlindedBlock(
signedBlock: allForks.SignedBlindedBeaconBlock
signedBlock: allForks.SignedBlindedBeaconBlock | SignedBlindedBlockContents
): Promise<
ApiClientResponse<
{[HttpStatusCode.OK]: {data: allForks.ExecutionPayload; version: ForkName}},
Expand Down
1 change: 1 addition & 0 deletions packages/api/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,6 @@ export {
ApiError,
Metrics,
} from "./utils/client/index.js";
export * from "./utils/routes.js";

// NOTE: Don't export server here so it's not bundled to all consumers
105 changes: 105 additions & 0 deletions packages/api/src/utils/routes.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
import {allForks, deneb, ssz} from "@lodestar/types";
import {ForkBlobs} from "@lodestar/params";

import {TypeJson} from "./types.js";

export type BlockContents = {block: allForks.BeaconBlock; blobSidecars: deneb.BlobSidecars};
export type SignedBlockContents = {
signedBlock: allForks.SignedBeaconBlock;
signedBlobSidecars: deneb.SignedBlobSidecars;
};

export type BlindedBlockContents = {
blindedBlock: allForks.BlindedBeaconBlock;
blindedBlobSidecars: deneb.BlindedBlobSidecars;
};
export type SignedBlindedBlockContents = {
signedBlindedBlock: allForks.SignedBlindedBeaconBlock;
signedBlindedBlobSidecars: deneb.SignedBlindedBlobSidecars;
};

export function isBlockContents(data: allForks.BeaconBlock | BlockContents): data is BlockContents {
return (data as BlockContents).blobSidecars !== undefined;
}

export function isSignedBlockContents(
data: allForks.SignedBeaconBlock | SignedBlockContents
): data is SignedBlockContents {
return (data as SignedBlockContents).signedBlobSidecars !== undefined;
}

export function isBlindedBlockContents(
data: allForks.BlindedBeaconBlock | BlindedBlockContents
): data is BlindedBlockContents {
return (data as BlindedBlockContents).blindedBlobSidecars !== undefined;
}

export function isSignedBlindedBlockContents(
data: allForks.SignedBlindedBeaconBlock | SignedBlindedBlockContents
): data is SignedBlindedBlockContents {
return (data as SignedBlindedBlockContents).signedBlindedBlobSidecars !== undefined;
}

/* eslint-disable @typescript-eslint/naming-convention */

export function AllForksSignedBlockContentsReqSerializer(
blockSerializer: (data: allForks.SignedBeaconBlock) => TypeJson<allForks.SignedBeaconBlock>
): TypeJson<SignedBlockContents> {
return {
toJson: (data) => ({
signed_block: blockSerializer(data.signedBlock).toJson(data.signedBlock),
signed_blob_sidecars: ssz.deneb.SignedBlobSidecars.toJson(data.signedBlobSidecars),
}),

fromJson: (data: {signed_block: unknown; signed_blob_sidecars: unknown}) => ({
signedBlock: blockSerializer(data.signed_block as allForks.SignedBeaconBlock).fromJson(data.signed_block),
signedBlobSidecars: ssz.deneb.SignedBlobSidecars.fromJson(data.signed_blob_sidecars),
}),
};
}

export function AllForksBlockContentsResSerializer(getType: () => ForkBlobs): TypeJson<BlockContents> {
return {
toJson: (data) => ({
block: (ssz.allForks[getType()].BeaconBlock as allForks.AllForksSSZTypes["BeaconBlock"]).toJson(data.block),
blob_sidecars: ssz.deneb.BlobSidecars.toJson(data.blobSidecars),
}),
fromJson: (data: {block: unknown; blob_sidecars: unknown}) => ({
block: ssz.allForks[getType()].BeaconBlock.fromJson(data.block),
blobSidecars: ssz.deneb.BlobSidecars.fromJson(data.blob_sidecars),
}),
};
}

export function AllForksSignedBlindedBlockContentsReqSerializer(
blockSerializer: (data: allForks.SignedBlindedBeaconBlock) => TypeJson<allForks.SignedBlindedBeaconBlock>
): TypeJson<SignedBlindedBlockContents> {
return {
toJson: (data) => ({
signed_blinded_block: blockSerializer(data.signedBlindedBlock).toJson(data.signedBlindedBlock),
signed_blinded_blob_sidecars: ssz.deneb.SignedBlindedBlobSidecars.toJson(data.signedBlindedBlobSidecars),
}),

fromJson: (data: {signed_blinded_block: unknown; signed_blinded_blob_sidecars: unknown}) => ({
signedBlindedBlock: blockSerializer(data.signed_blinded_block as allForks.SignedBlindedBeaconBlock).fromJson(
data.signed_blinded_block
),
signedBlindedBlobSidecars: ssz.deneb.SignedBlindedBlobSidecars.fromJson(data.signed_blinded_blob_sidecars),
}),
};
}

export function AllForksBlindedBlockContentsResSerializer(getType: () => ForkBlobs): TypeJson<BlindedBlockContents> {
return {
toJson: (data) => ({
blinded_block: (
ssz.allForksBlinded[getType()].BeaconBlock as allForks.AllForksBlindedSSZTypes["BeaconBlock"]
).toJson(data.blindedBlock),
blinded_blob_sidecars: ssz.deneb.BlindedBlobSidecars.toJson(data.blindedBlobSidecars),
}),
fromJson: (data: {blinded_block: unknown; blinded_blob_sidecars: unknown}) => ({
blindedBlock: ssz.allForksBlinded[getType()].BeaconBlock.fromJson(data.blinded_block),
blindedBlobSidecars: ssz.deneb.BlindedBlobSidecars.fromJson(data.blinded_blob_sidecars),
}),
};
}
4 changes: 2 additions & 2 deletions packages/api/test/unit/beacon/testData/beacon.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,9 +56,9 @@ export const testData: GenericServerTestCases<Api> = {
args: [getDefaultBlindedBlock(64)],
res: undefined,
},
getBlobsSidecar: {
getBlobSidecars: {
args: ["head"],
res: {executionOptimistic: true, data: ssz.deneb.BlobsSidecar.defaultValue()},
res: {executionOptimistic: true, data: ssz.deneb.BlobSidecars.defaultValue()},
},

// pool
Expand Down
Loading