Skip to content

Commit

Permalink
feat: more insight when publishing blocks (#6166)
Browse files Browse the repository at this point in the history
* fix: persist invalid blocks when publish

* feat: add option to persist produced blocks

* chore: log body root when publishing block
  • Loading branch information
twoeths authored and philknows committed Dec 8, 2023
1 parent e12a21f commit b94c06e
Show file tree
Hide file tree
Showing 7 changed files with 53 additions and 9 deletions.
19 changes: 18 additions & 1 deletion packages/beacon-node/src/api/impl/beacon/blocks/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,9 +74,11 @@ export function getBeaconBlockApi({
const slot = signedBlock.message.slot;
const fork = config.getForkName(slot);
const blockRoot = toHex(chain.config.getForkTypes(slot).BeaconBlock.hashTreeRoot(signedBlock.message));
// bodyRoot should be the same to produced block
const bodyRoot = toHex(chain.config.getForkTypes(slot).BeaconBlockBody.hashTreeRoot(signedBlock.message.body));
const blockLocallyProduced =
chain.producedBlockRoot.has(blockRoot) || chain.producedBlindedBlockRoot.has(blockRoot);
const valLogMeta = {broadcastValidation, blockRoot, blockLocallyProduced, slot};
const valLogMeta = {broadcastValidation, blockRoot, bodyRoot, blockLocallyProduced, slot};

switch (broadcastValidation) {
case routes.beacon.BroadcastValidation.gossip: {
Expand All @@ -85,6 +87,11 @@ export function getBeaconBlockApi({
await validateGossipBlock(config, chain, signedBlock, fork);
} catch (error) {
chain.logger.error("Gossip validations failed while publishing the block", valLogMeta, error as Error);
chain.persistInvalidSszValue(
chain.config.getForkTypes(slot).SignedBeaconBlock,
signedBlock,
"api_reject_gossip_failure"
);
throw error;
}
}
Expand All @@ -102,6 +109,11 @@ export function getBeaconBlockApi({
blockInput: blockForImport,
peer: IDENTITY_PEER_ID,
});
chain.persistInvalidSszValue(
chain.config.getForkTypes(slot).SignedBeaconBlock,
signedBlock,
"api_reject_parent_unknown"
);
throw new BlockError(signedBlock, {
code: BlockErrorCode.PARENT_UNKNOWN,
parentRoot: toHexString(signedBlock.message.parentRoot),
Expand All @@ -123,6 +135,11 @@ export function getBeaconBlockApi({
);
} catch (error) {
chain.logger.error("Consensus checks failed while publishing the block", valLogMeta, error as Error);
chain.persistInvalidSszValue(
chain.config.getForkTypes(slot).SignedBeaconBlock,
signedBlock,
"api_reject_consensus_failure"
);
throw error;
}
}
Expand Down
6 changes: 6 additions & 0 deletions packages/beacon-node/src/api/impl/validator/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -309,6 +309,9 @@ export function getValidatorApi({
});

const version = config.getForkName(block.slot);
if (chain.opts.persistProducedBlocks) {
void chain.persistBlock(block, "produced_builder_block");
}
if (isForkBlobs(version)) {
const blockHash = toHex((block as bellatrix.BlindedBeaconBlock).body.executionPayloadHeader.blockHash);
const blindedBlobSidecars = chain.producedBlindedBlobSidecarsCache.get(blockHash);
Expand Down Expand Up @@ -377,6 +380,9 @@ export function getValidatorApi({
executionPayloadValue,
root: toHexString(config.getForkTypes(slot).BeaconBlock.hashTreeRoot(block)),
});
if (chain.opts.persistProducedBlocks) {
void chain.persistBlock(block, "produced_engine_block");
}
if (isForkBlobs(version)) {
const blockHash = toHex((block as bellatrix.BeaconBlock).body.executionPayload.blockHash);
const blobSidecars = chain.producedBlobSidecarsCache.get(blockHash);
Expand Down
24 changes: 16 additions & 8 deletions packages/beacon-node/src/chain/chain.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import {
deneb,
Wei,
bellatrix,
isBlindedBeaconBlock,
} from "@lodestar/types";
import {CheckpointWithHex, ExecutionStatus, IForkChoice, ProtoBlock} from "@lodestar/fork-choice";
import {ProcessShutdownCallback} from "@lodestar/validator";
Expand Down Expand Up @@ -637,21 +638,32 @@ export class BeaconChain implements IBeaconChain {
return this.reprocessController.waitForBlockOfAttestation(slot, root);
}

persistBlock(data: allForks.BeaconBlock | allForks.BlindedBeaconBlock, suffix?: string): void {
const slot = data.slot;
if (isBlindedBeaconBlock(data)) {
const sszType = this.config.getBlindedForkTypes(slot).BeaconBlock;
void this.persistSszObject("BlindedBeaconBlock", sszType.serialize(data), sszType.hashTreeRoot(data), suffix);
} else {
const sszType = this.config.getForkTypes(slot).BeaconBlock;
void this.persistSszObject("BeaconBlock", sszType.serialize(data), sszType.hashTreeRoot(data), suffix);
}
}

persistInvalidSszValue<T>(type: Type<T>, sszObject: T, suffix?: string): void {
if (this.opts.persistInvalidSszObjects) {
void this.persistInvalidSszObject(type.typeName, type.serialize(sszObject), type.hashTreeRoot(sszObject), suffix);
void this.persistSszObject(type.typeName, type.serialize(sszObject), type.hashTreeRoot(sszObject), suffix);
}
}

persistInvalidSszBytes(typeName: string, sszBytes: Uint8Array, suffix?: string): void {
if (this.opts.persistInvalidSszObjects) {
void this.persistInvalidSszObject(typeName, sszBytes, sszBytes, suffix);
void this.persistSszObject(typeName, sszBytes, sszBytes, suffix);
}
}

persistInvalidSszView(view: TreeView<CompositeTypeAny>, suffix?: string): void {
if (this.opts.persistInvalidSszObjects) {
void this.persistInvalidSszObject(view.type.typeName, view.serialize(), view.hashTreeRoot(), suffix);
void this.persistSszObject(view.type.typeName, view.serialize(), view.hashTreeRoot(), suffix);
}
}

Expand Down Expand Up @@ -744,16 +756,12 @@ export class BeaconChain implements IBeaconChain {
return {state: blockState, stateId: "block_state_any_epoch", shouldWarn: true};
}

private async persistInvalidSszObject(
private async persistSszObject(
typeName: string,
bytes: Uint8Array,
root: Uint8Array,
suffix?: string
): Promise<void> {
if (!this.opts.persistInvalidSszObjects) {
return;
}

const now = new Date();
// yyyy-MM-dd
const dateStr = now.toISOString().split("T")[0];
Expand Down
1 change: 1 addition & 0 deletions packages/beacon-node/src/chain/interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,7 @@ export interface IBeaconChain {

updateBeaconProposerData(epoch: Epoch, proposers: ProposerPreparationData[]): Promise<void>;

persistBlock(data: allForks.BeaconBlock | allForks.BlindedBeaconBlock, suffix?: string): void;
persistInvalidSszValue<T>(type: Type<T>, sszObject: T | Uint8Array, suffix?: string): void;
persistInvalidSszBytes(type: string, sszBytes: Uint8Array, suffix?: string): void;
/** Persist bad items to persistInvalidSszObjectsDir dir, for example invalid state, attestations etc. */
Expand Down
1 change: 1 addition & 0 deletions packages/beacon-node/src/chain/options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ export type IChainOptions = BlockProcessOpts &
LightClientServerOpts & {
blsVerifyAllMainThread?: boolean;
blsVerifyAllMultiThread?: boolean;
persistProducedBlocks?: boolean;
persistInvalidSszObjects?: boolean;
persistInvalidSszObjectsDir?: string;
skipCreateStateCacheIfAvailable?: boolean;
Expand Down
9 changes: 9 additions & 0 deletions packages/cli/src/options/beaconNodeOptions/chain.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ export type ChainArgs = {
"chain.blsVerifyAllMultiThread"?: boolean;
"chain.blsVerifyAllMainThread"?: boolean;
"chain.disableBlsBatchVerify"?: boolean;
"chain.persistProducedBlocks"?: boolean;
"chain.persistInvalidSszObjects"?: boolean;
// No need to define chain.persistInvalidSszObjects as part of ChainArgs
// as this is defined as part of BeaconPaths
Expand All @@ -32,6 +33,7 @@ export function parseArgs(args: ChainArgs): IBeaconNodeOptions["chain"] {
blsVerifyAllMultiThread: args["chain.blsVerifyAllMultiThread"],
blsVerifyAllMainThread: args["chain.blsVerifyAllMainThread"],
disableBlsBatchVerify: args["chain.disableBlsBatchVerify"],
persistProducedBlocks: args["chain.persistProducedBlocks"],
persistInvalidSszObjects: args["chain.persistInvalidSszObjects"],
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-explicit-any
persistInvalidSszObjectsDir: undefined as any,
Expand Down Expand Up @@ -94,6 +96,13 @@ Will double processing times. Use only for debugging purposes.",
group: "chain",
},

"chain.persistProducedBlocks": {
hidden: true,
type: "boolean",
description: "Persist produced blocks or not for debugging purpose",
group: "chain",
},

"chain.persistInvalidSszObjects": {
hidden: true,
type: "boolean",
Expand Down
2 changes: 2 additions & 0 deletions packages/cli/test/unit/options/beaconNodeOptions.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ describe("options / beaconNodeOptions", () => {
"chain.blsVerifyAllMultiThread": true,
"chain.blsVerifyAllMainThread": true,
"chain.disableBlsBatchVerify": true,
"chain.persistProducedBlocks": true,
"chain.persistInvalidSszObjects": true,
"chain.proposerBoostEnabled": false,
"chain.disableImportExecutionFcU": false,
Expand Down Expand Up @@ -121,6 +122,7 @@ describe("options / beaconNodeOptions", () => {
blsVerifyAllMultiThread: true,
blsVerifyAllMainThread: true,
disableBlsBatchVerify: true,
persistProducedBlocks: true,
persistInvalidSszObjects: true,
proposerBoostEnabled: false,
disableImportExecutionFcU: false,
Expand Down

0 comments on commit b94c06e

Please sign in to comment.