Skip to content

Commit

Permalink
feat: produce phase0 beacon block body once (ChainSafe#6270)
Browse files Browse the repository at this point in the history
* Update the beacon block production for phase0

* Update the workflow with common body

* Fix lint errors

* Fix the types

* Fix the log message

* Fix mutation of the common block body

* Fix lint formatting

* Fix lint formatting
  • Loading branch information
nazarhussain authored Jan 11, 2024
1 parent 0b2d35a commit aa87c54
Show file tree
Hide file tree
Showing 6 changed files with 174 additions and 78 deletions.
29 changes: 25 additions & 4 deletions packages/beacon-node/src/api/impl/validator/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ import {RegenCaller} from "../../../chain/regen/index.js";
import {getValidatorStatus} from "../beacon/state/utils.js";
import {validateGossipFnRetryUnknownRoot} from "../../../network/processor/gossipHandlers.js";
import {SCHEDULER_LOOKAHEAD_FACTOR} from "../../../chain/prepareNextSlot.js";
import {ChainEvent, CheckpointHex} from "../../../chain/index.js";
import {ChainEvent, CheckpointHex, CommonBlockBody} from "../../../chain/index.js";
import {computeSubnetForCommitteesAtSlot, getPubkeysForIndices} from "./utils.js";

/**
Expand Down Expand Up @@ -287,7 +287,11 @@ export function getValidatorApi({
// as of now fee recipient checks can not be performed because builder does not return bid recipient
{
skipHeadChecksAndUpdate,
}: Omit<routes.validator.ExtraProduceBlockOps, "builderSelection"> & {skipHeadChecksAndUpdate?: boolean} = {}
commonBlockBody,
}: Omit<routes.validator.ExtraProduceBlockOps, "builderSelection"> & {
skipHeadChecksAndUpdate?: boolean;
commonBlockBody?: CommonBlockBody;
} = {}
): Promise<routes.validator.ProduceBlindedBlockRes> {
const version = config.getForkName(slot);
if (!isForkExecution(version)) {
Expand Down Expand Up @@ -323,6 +327,7 @@ export function getValidatorApi({
slot,
randaoReveal,
graffiti: toGraffitiBuffer(graffiti || ""),
commonBlockBody,
});

metrics?.blockProductionSuccess.inc({source});
Expand Down Expand Up @@ -352,7 +357,11 @@ export function getValidatorApi({
feeRecipient,
strictFeeRecipientCheck,
skipHeadChecksAndUpdate,
}: Omit<routes.validator.ExtraProduceBlockOps, "builderSelection"> & {skipHeadChecksAndUpdate?: boolean} = {}
commonBlockBody,
}: Omit<routes.validator.ExtraProduceBlockOps, "builderSelection"> & {
skipHeadChecksAndUpdate?: boolean;
commonBlockBody?: CommonBlockBody;
} = {}
): Promise<routes.validator.ProduceBlockOrContentsRes & {shouldOverrideBuilder?: boolean}> {
const source = ProducedBlockSource.engine;
metrics?.blockProductionRequests.inc({source});
Expand All @@ -376,6 +385,7 @@ export function getValidatorApi({
randaoReveal,
graffiti: toGraffitiBuffer(graffiti || ""),
feeRecipient,
commonBlockBody,
});
const version = config.getForkName(block.slot);
if (strictFeeRecipientCheck && feeRecipient && isForkExecution(version)) {
Expand Down Expand Up @@ -456,22 +466,32 @@ export function getValidatorApi({
chain.executionBuilder !== undefined &&
builderSelection !== routes.validator.BuilderSelection.ExecutionOnly;

logger.verbose("Assembling block with produceEngineOrBuilderBlock ", {
const loggerContext = {
fork,
builderSelection,
slot,
isBuilderEnabled,
strictFeeRecipientCheck,
// winston logger doesn't like bigint
builderBoostFactor: `${builderBoostFactor}`,
};

logger.verbose("Assembling block with produceEngineOrBuilderBlock", loggerContext);
const commonBlockBody = await chain.produceCommonBlockBody({
slot,
randaoReveal,
graffiti: toGraffitiBuffer(graffiti || ""),
});
logger.debug("Produced common block body", loggerContext);

// Start calls for building execution and builder blocks
const blindedBlockPromise = isBuilderEnabled
? // can't do fee recipient checks as builder bid doesn't return feeRecipient as of now
produceBuilderBlindedBlock(slot, randaoReveal, graffiti, {
feeRecipient,
// skip checking and recomputing head in these individual produce calls
skipHeadChecksAndUpdate: true,
commonBlockBody,
}).catch((e) => {
logger.error("produceBuilderBlindedBlock failed to produce block", {slot}, e);
return null;
Expand All @@ -494,6 +514,7 @@ export function getValidatorApi({
strictFeeRecipientCheck,
// skip checking and recomputing head in these individual produce calls
skipHeadChecksAndUpdate: true,
commonBlockBody,
}).catch((e) => {
logger.error("produceEngineFullBlockOrContents failed to produce block", {slot}, e);
return null;
Expand Down
34 changes: 28 additions & 6 deletions packages/beacon-node/src/chain/chain.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ import {ensureDir, writeIfNotExist} from "../util/file.js";
import {isOptimisticBlock} from "../util/forkChoice.js";
import {BlockProcessor, ImportBlockOpts} from "./blocks/index.js";
import {ChainEventEmitter, ChainEvent} from "./emitter.js";
import {IBeaconChain, ProposerPreparationData, BlockHash, StateGetOpts} from "./interface.js";
import {IBeaconChain, ProposerPreparationData, BlockHash, StateGetOpts, CommonBlockBody} from "./interface.js";
import {IChainOptions} from "./options.js";
import {QueuedStateRegenerator, RegenCaller} from "./regen/index.js";
import {initializeForkChoice} from "./forkChoice/index.js";
Expand Down Expand Up @@ -73,7 +73,7 @@ import {SeenBlockAttesters} from "./seenCache/seenBlockAttesters.js";
import {BeaconProposerCache} from "./beaconProposerCache.js";
import {CheckpointBalancesCache} from "./balancesCache.js";
import {AssembledBlockType, BlobsResultType, BlockType} from "./produceBlock/index.js";
import {BlockAttributes, produceBlockBody} from "./produceBlock/produceBlockBody.js";
import {BlockAttributes, produceBlockBody, produceCommonBlockBody} from "./produceBlock/produceBlockBody.js";
import {computeNewStateRoot} from "./produceBlock/computeNewStateRoot.js";
import {BlockInput} from "./blocks/types.js";
import {SeenAttestationDatas} from "./seenCache/seenAttestationData.js";
Expand Down Expand Up @@ -463,14 +463,35 @@ export class BeaconChain implements IBeaconChain {
return {block: data, executionOptimistic: isOptimisticBlock(block)};
}
// If block is not found in hot db, try cold db since there could be an archive cycle happening
// TODO: Add a lock to the archiver to have determinstic behaviour on where are blocks
// TODO: Add a lock to the archiver to have deterministic behavior on where are blocks
}

const data = await this.db.blockArchive.getByRoot(fromHexString(root));
return data && {block: data, executionOptimistic: false};
}

produceBlock(blockAttributes: BlockAttributes): Promise<{
async produceCommonBlockBody(blockAttributes: BlockAttributes): Promise<CommonBlockBody> {
const {slot} = blockAttributes;
const head = this.forkChoice.getHead();
const state = await this.regen.getBlockSlotState(
head.blockRoot,
slot,
{dontTransferCache: true},
RegenCaller.produceBlock
);
const parentBlockRoot = fromHexString(head.blockRoot);

// TODO: To avoid breaking changes for metric define this attribute
const blockType = BlockType.Full;

return produceCommonBlockBody.call(this, blockType, state, {
...blockAttributes,
parentBlockRoot,
parentSlot: slot - 1,
});
}

produceBlock(blockAttributes: BlockAttributes & {commonBlockBody?: CommonBlockBody}): Promise<{
block: allForks.BeaconBlock;
executionPayloadValue: Wei;
consensusBlockValue: Gwei;
Expand All @@ -479,7 +500,7 @@ export class BeaconChain implements IBeaconChain {
return this.produceBlockWrapper<BlockType.Full>(BlockType.Full, blockAttributes);
}

produceBlindedBlock(blockAttributes: BlockAttributes): Promise<{
produceBlindedBlock(blockAttributes: BlockAttributes & {commonBlockBody?: CommonBlockBody}): Promise<{
block: allForks.BlindedBeaconBlock;
executionPayloadValue: Wei;
consensusBlockValue: Gwei;
Expand All @@ -489,7 +510,7 @@ export class BeaconChain implements IBeaconChain {

async produceBlockWrapper<T extends BlockType>(
blockType: T,
{randaoReveal, graffiti, slot, feeRecipient}: BlockAttributes
{randaoReveal, graffiti, slot, feeRecipient, commonBlockBody}: BlockAttributes & {commonBlockBody?: CommonBlockBody}
): Promise<{
block: AssembledBlockType<T>;
executionPayloadValue: Wei;
Expand Down Expand Up @@ -520,6 +541,7 @@ export class BeaconChain implements IBeaconChain {
parentBlockRoot,
proposerIndex,
proposerPubKey,
commonBlockBody,
}
);

Expand Down
11 changes: 9 additions & 2 deletions packages/beacon-node/src/chain/interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ import {
deneb,
Wei,
Gwei,
capella,
altair,
} from "@lodestar/types";
import {
BeaconStateAllForks,
Expand Down Expand Up @@ -154,13 +156,14 @@ export interface IBeaconChain {

getContents(beaconBlock: deneb.BeaconBlock): deneb.Contents;

produceBlock(blockAttributes: BlockAttributes): Promise<{
produceCommonBlockBody(blockAttributes: BlockAttributes): Promise<CommonBlockBody>;
produceBlock(blockAttributes: BlockAttributes & {commonBlockBody?: CommonBlockBody}): Promise<{
block: allForks.BeaconBlock;
executionPayloadValue: Wei;
consensusBlockValue: Gwei;
shouldOverrideBuilder?: boolean;
}>;
produceBlindedBlock(blockAttributes: BlockAttributes): Promise<{
produceBlindedBlock(blockAttributes: BlockAttributes & {commonBlockBody?: CommonBlockBody}): Promise<{
block: allForks.BlindedBeaconBlock;
executionPayloadValue: Wei;
consensusBlockValue: Gwei;
Expand Down Expand Up @@ -204,3 +207,7 @@ export type SSZObjectType =
| "signedAggregatedAndProof"
| "syncCommittee"
| "contributionAndProof";

export type CommonBlockBody = phase0.BeaconBlockBody &
Pick<capella.BeaconBlockBody, "blsToExecutionChanges"> &
Pick<altair.BeaconBlockBody, "syncAggregate">;
Loading

0 comments on commit aa87c54

Please sign in to comment.