Skip to content

Commit

Permalink
apply feedback
Browse files Browse the repository at this point in the history
  • Loading branch information
g11tech committed Oct 23, 2023
1 parent aba3bc5 commit acd897d
Show file tree
Hide file tree
Showing 2 changed files with 95 additions and 61 deletions.
154 changes: 94 additions & 60 deletions packages/beacon-node/src/api/impl/beacon/blocks/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import {fromHexString, toHexString} from "@chainsafe/ssz";
import {routes, ServerApi, ResponseFormat} from "@lodestar/api";
import {computeTimeAtSlot, signedBlindedBlockToFull, signedBlindedBlobSidecarsToFull} from "@lodestar/state-transition";
import {SLOTS_PER_HISTORICAL_ROOT} from "@lodestar/params";
import {sleep, toHex} from "@lodestar/utils";
import {sleep, toHex, LogDataBasic} from "@lodestar/utils";
import {allForks, deneb, isSignedBlockContents, isSignedBlindedBlockContents} from "@lodestar/types";
import {BlockSource, getBlockInput, ImportBlockOpts, BlockInput} from "../../../../chain/blocks/types.js";
import {promiseAllMaybeAsync} from "../../../../util/promises.js";
Expand All @@ -15,6 +15,11 @@ import {resolveBlockId, toBeaconHeaderResponse} from "./utils.js";

type PublishBlockOpts = ImportBlockOpts & {broadcastValidation?: routes.beacon.BroadcastValidation};

type ParsedSignedBlindedBlockOrContents = {
signedBlindedBlock: allForks.SignedBlindedBeaconBlock;
signedBlindedBlobSidecars: deneb.SignedBlindedBlobSidecars | null;
};

/**
* Validator clock may be advanced from beacon's clock. If the validator requests a resource in a
* future slot, wait some time instead of rejecting the request because it's in the future
Expand Down Expand Up @@ -138,78 +143,36 @@ export function getBeaconBlockApi({
signedBlindedBlockOrContents,
opts: PublishBlockOpts = {}
) => {
let signedBlindedBlock: allForks.SignedBlindedBeaconBlock;
let signedBlindedBlobSidecars: deneb.SignedBlindedBlobSidecars | null;
const {signedBlindedBlock, signedBlindedBlobSidecars} =
parseSignedBlindedBlockOrContents(signedBlindedBlockOrContents);

if (isSignedBlindedBlockContents(signedBlindedBlockOrContents)) {
signedBlindedBlock = signedBlindedBlockOrContents.signedBlindedBlock;
signedBlindedBlobSidecars = signedBlindedBlockOrContents.signedBlindedBlobSidecars;
} else {
signedBlindedBlock = signedBlindedBlockOrContents;
signedBlindedBlobSidecars = null;
}
const slot = signedBlindedBlock.message.slot;
const blockRoot = toHex(
chain.config
.getBlindedForkTypes(signedBlindedBlock.message.slot)
.BeaconBlock.hashTreeRoot(signedBlindedBlock.message)
);
const executionPayload = chain.producedBlockRoot.get(blockRoot);
let signedBlockOrContents: allForks.SignedBeaconBlockOrContents;

const logCtx = {blockRoot, slot};
if (executionPayload !== null && executionPayload !== undefined) {
Object.assign(logCtx, {transactions: executionPayload.transactions.length});
}

// Either the payload/blobs are cached from i) engine locally or ii) they are from the builder
//
// executionPayload can be null or a real payload in locally produced, its only undefined when
// the block came from the builder

if (executionPayload !== undefined) {
const signedBlock = signedBlindedBlockToFull(signedBlindedBlock, executionPayload);

if (signedBlindedBlobSidecars !== null) {
if (executionPayload === null) {
throw Error("Missing locally produced executionPayload for deneb+ publishBlindedBlock");
}

const blockHash = toHex(executionPayload.blockHash);
const blobSidecars = chain.producedBlobSidecarsCache.get(blockHash);
if (blobSidecars === undefined) {
throw Error("Missing blobSidecars from the local execution cache");
}
if (blobSidecars.length !== signedBlindedBlobSidecars.length) {
throw Error(
`Length mismatch signedBlindedBlobSidecars=${signedBlindedBlobSidecars.length} blobSidecars=${blobSidecars.length}`
);
}
const signedBlobSidecars = signedBlindedBlobSidecarsToFull(
signedBlindedBlobSidecars,
blobSidecars.map((blobSidecar) => blobSidecar.blob)
);

signedBlockOrContents = {signedBlock, signedBlobSidecars} as allForks.SignedBeaconBlockOrContents;
Object.assign(logCtx, {blobs: signedBlindedBlobSidecars.length});
} else {
signedBlockOrContents = signedBlock as allForks.SignedBeaconBlockOrContents;
}

chain.logger.verbose("Publishing block assembled from locally cached payload", logCtx);
} else {
// Mechanism for blobs & blocks on builder is implemenented separately in a followup deneb-builder PR
if (isSignedBlindedBlockContents(signedBlindedBlockOrContents)) {
throw Error("exeutionBuilder not yet implemented for deneb+ forks");
}
const executionBuilder = chain.executionBuilder;
if (!executionBuilder) throw Error("exeutionBuilder required to publish SignedBlindedBeaconBlock");
signedBlockOrContents = await executionBuilder.submitBlindedBlock(signedBlindedBlockOrContents);
chain.logger.verbose("Publishing block assembled from the builder", logCtx);
}

// the full block is published by relay and it's possible that the block is already known to us by gossip
// see https://github.com/ChainSafe/lodestar/issues/5404
const executionPayload = chain.producedBlockRoot.get(blockRoot);
const signedBlockOrContents =
executionPayload !== undefined
? reconstructLocalBlockOrContents(
chain,
{signedBlindedBlock, signedBlindedBlobSidecars},
executionPayload,
logCtx
)
: await reconstructBuilderBlockOrContents(chain, signedBlindedBlockOrContents, logCtx);

// the full block is published by relay and it's possible that the block is already known to us
// by gossip
//
// see: https://github.com/ChainSafe/lodestar/issues/5404
return publishBlock(signedBlockOrContents, {...opts, ignoreIfKnown: true});
};

Expand Down Expand Up @@ -401,3 +364,74 @@ export function getBeaconBlockApi({
},
};
}

function parseSignedBlindedBlockOrContents(
signedBlindedBlockOrContents: allForks.SignedBlindedBeaconBlockOrContents
): ParsedSignedBlindedBlockOrContents {
if (isSignedBlindedBlockContents(signedBlindedBlockOrContents)) {
const signedBlindedBlock = signedBlindedBlockOrContents.signedBlindedBlock;
const signedBlindedBlobSidecars = signedBlindedBlockOrContents.signedBlindedBlobSidecars;
return {signedBlindedBlock, signedBlindedBlobSidecars};
} else {
return {signedBlindedBlock: signedBlindedBlockOrContents, signedBlindedBlobSidecars: null};
}
}

function reconstructLocalBlockOrContents(
chain: ApiModules["chain"],
{signedBlindedBlock, signedBlindedBlobSidecars}: ParsedSignedBlindedBlockOrContents,
executionPayload: allForks.ExecutionPayload | null,
logCtx: Record<string, LogDataBasic>
): allForks.SignedBeaconBlockOrContents {
const signedBlock = signedBlindedBlockToFull(signedBlindedBlock, executionPayload);
if (executionPayload !== null) {
Object.assign(logCtx, {transactions: executionPayload.transactions.length});
}

if (signedBlindedBlobSidecars !== null) {
if (executionPayload === null) {
throw Error("Missing locally produced executionPayload for deneb+ publishBlindedBlock");
}

const blockHash = toHex(executionPayload.blockHash);
const blobSidecars = chain.producedBlobSidecarsCache.get(blockHash);
if (blobSidecars === undefined) {
throw Error("Missing blobSidecars from the local execution cache");
}
if (blobSidecars.length !== signedBlindedBlobSidecars.length) {
throw Error(
`Length mismatch signedBlindedBlobSidecars=${signedBlindedBlobSidecars.length} blobSidecars=${blobSidecars.length}`
);
}
const signedBlobSidecars = signedBlindedBlobSidecarsToFull(
signedBlindedBlobSidecars,
blobSidecars.map((blobSidecar) => blobSidecar.blob)
);

Object.assign(logCtx, {blobs: signedBlindedBlobSidecars.length});
chain.logger.verbose("Block & blobs assembled from locally cached payload", logCtx);
return {signedBlock, signedBlobSidecars} as allForks.SignedBeaconBlockOrContents;
} else {
chain.logger.verbose("Block assembled from locally cached payload", logCtx);
return signedBlock as allForks.SignedBeaconBlockOrContents;
}
}

async function reconstructBuilderBlockOrContents(
chain: ApiModules["chain"],
signedBlindedBlockOrContents: allForks.SignedBlindedBeaconBlockOrContents,
logCtx: Record<string, LogDataBasic>
): Promise<allForks.SignedBeaconBlockOrContents> {
// Mechanism for blobs & blocks on builder is implemenented separately in a followup deneb-builder PR
if (isSignedBlindedBlockContents(signedBlindedBlockOrContents)) {
throw Error("exeutionBuilder not yet implemented for deneb+ forks");
}
const executionBuilder = chain.executionBuilder;
if (!executionBuilder) {
throw Error("exeutionBuilder required to publish SignedBlindedBeaconBlock");
}

const signedBlockOrContents = await executionBuilder.submitBlindedBlock(signedBlindedBlockOrContents);
chain.logger.verbose("Publishing block assembled from the builder", logCtx);
return signedBlockOrContents;
}
2 changes: 1 addition & 1 deletion packages/utils/src/logger.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,5 +17,5 @@ export const LogLevels = Object.values(LogLevel);

export type LogHandler = (message: string, context?: LogData, error?: Error) => void;

type LogDataBasic = string | number | bigint | boolean | null | undefined;
export type LogDataBasic = string | number | bigint | boolean | null | undefined;
export type LogData = LogDataBasic | Record<string, LogDataBasic> | LogDataBasic[] | Record<string, LogDataBasic>[];

0 comments on commit acd897d

Please sign in to comment.