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: de-duplicate payloads from persisted beacon blocks #7034

Draft
wants to merge 18 commits into
base: unstable
Choose a base branch
from
Draft
Changes from 1 commit
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
Prev Previous commit
Next Next commit
feat: update chain to work with full or blinded blocks
matthewkeil committed Sep 16, 2024
commit a739828431ea5b8e5e1bf588959107701d36f795
37 changes: 30 additions & 7 deletions packages/beacon-node/src/chain/chain.ts
Original file line number Diff line number Diff line change
@@ -13,6 +13,7 @@ import {
PubkeyIndexMap,
EpochShuffling,
computeEndSlotAtEpoch,
blindedOrFullBlockHashTreeRoot,
} from "@lodestar/state-transition";
import {BeaconConfig} from "@lodestar/config";
import {
@@ -32,10 +33,11 @@ import {
ExecutionPayload,
BlindedBeaconBlock,
BlindedBeaconBlockBody,
SignedBlindedBeaconBlock,
} from "@lodestar/types";
import {CheckpointWithHex, ExecutionStatus, IForkChoice, ProtoBlock, UpdateHeadOpt} from "@lodestar/fork-choice";
import {ProcessShutdownCallback} from "@lodestar/validator";
import {Logger, fromHex, gweiToWei, isErrorAborted, pruneSetToMax, sleep, toRootHex} from "@lodestar/utils";
import {Logger, fromHex, toHex, gweiToWei, isErrorAborted, pruneSetToMax, sleep, toRootHex} from "@lodestar/utils";
import {ForkSeq, GENESIS_SLOT, SLOTS_PER_EPOCH} from "@lodestar/params";

import {GENESIS_EPOCH, ZERO_HASH} from "../constants/index.js";
@@ -47,6 +49,8 @@ import {Clock, ClockEvent, IClock} from "../util/clock.js";
import {ensureDir, writeIfNotExist} from "../util/file.js";
import {isOptimisticBlock} from "../util/forkChoice.js";
import {BufferPool} from "../util/bufferPool.js";
import {Eth1Error, Eth1ErrorCode} from "../eth1/errors.js";
import {blindedOrFullBlockToFull} from "../util/fullOrBlindedBlock.js";
import {BlockProcessor, ImportBlockOpts} from "./blocks/index.js";
import {ChainEventEmitter, ChainEvent} from "./emitter.js";
import {
@@ -560,9 +564,11 @@ export class BeaconChain implements IBeaconChain {
return null;
}

async getCanonicalBlockAtSlot(
slot: Slot
): Promise<{block: SignedBeaconBlock; executionOptimistic: boolean; finalized: boolean} | null> {
async getCanonicalBlockAtSlot(slot: Slot): Promise<{
block: SignedBeaconBlock | SignedBlindedBeaconBlock;
executionOptimistic: boolean;
finalized: boolean;
} | null> {
const finalizedBlock = this.forkChoice.getFinalizedBlock();
if (slot > finalizedBlock.slot) {
// Unfinalized slot, attempt to find in fork-choice
@@ -582,9 +588,11 @@ export class BeaconChain implements IBeaconChain {
return data && {block: data, executionOptimistic: false, finalized: true};
}

async getBlockByRoot(
root: string
): Promise<{block: SignedBeaconBlock; executionOptimistic: boolean; finalized: boolean} | null> {
async getBlockByRoot(root: string): Promise<{
block: SignedBeaconBlock | SignedBlindedBeaconBlock;
executionOptimistic: boolean;
finalized: boolean;
} | null> {
const block = this.forkChoice.getBlockHex(root);
if (block) {
const data = await this.db.block.get(fromHex(root));
@@ -995,6 +1003,21 @@ export class BeaconChain implements IBeaconChain {
return {state: blockState, stateId: "block_state_any_epoch", shouldWarn: true};
}

async fullOrBlindedSignedBeaconBlockToFull(
block: SignedBeaconBlock | SignedBlindedBeaconBlock
): Promise<SignedBeaconBlock> {
if (!isBlindedBeaconBlock(block)) return block;
const blockHash = toHex(blindedOrFullBlockHashTreeRoot(this.config, block.message));
const [payload] = await this.executionEngine.getPayloadBodiesByHash([blockHash]);
if (!payload) {
throw new Eth1Error(
{code: Eth1ErrorCode.INVALID_PAYLOAD_BODY, blockHash},
`Execution PayloadBody not found by eth1 engine for ${blockHash}`
);
}
return blindedOrFullBlockToFull(this.config, block, payload);
}

private async persistSszObject(
typeName: string,
bytes: Uint8Array,
19 changes: 13 additions & 6 deletions packages/beacon-node/src/chain/interface.ts
Original file line number Diff line number Diff line change
@@ -15,6 +15,7 @@ import {
ExecutionPayload,
SignedBeaconBlock,
BlindedBeaconBlock,
SignedBlindedBeaconBlock,
} from "@lodestar/types";
import {
BeaconStateAllForks,
@@ -172,15 +173,19 @@ export interface IBeaconChain {
* this methods returns blocks in current chain head according to
* forkchoice. Works for finalized slots as well
*/
getCanonicalBlockAtSlot(
slot: Slot
): Promise<{block: SignedBeaconBlock; executionOptimistic: boolean; finalized: boolean} | null>;
getCanonicalBlockAtSlot(slot: Slot): Promise<{
block: SignedBeaconBlock | SignedBlindedBeaconBlock;
executionOptimistic: boolean;
finalized: boolean;
} | null>;
/**
* Get local block by root, does not fetch from the network
*/
getBlockByRoot(
root: RootHex
): Promise<{block: SignedBeaconBlock; executionOptimistic: boolean; finalized: boolean} | null>;
getBlockByRoot(root: RootHex): Promise<{
block: SignedBeaconBlock | SignedBlindedBeaconBlock;
executionOptimistic: boolean;
finalized: boolean;
} | null>;

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

@@ -197,6 +202,8 @@ export interface IBeaconChain {
consensusBlockValue: Wei;
}>;

fullOrBlindedSignedBeaconBlockToFull(block: SignedBeaconBlock | SignedBlindedBeaconBlock): Promise<SignedBeaconBlock>;

/** Process a block until complete */
processBlock(block: BlockInput, opts?: ImportBlockOpts): Promise<void>;
/** Process a chain of blocks until complete */
5 changes: 4 additions & 1 deletion packages/beacon-node/src/eth1/errors.ts
Original file line number Diff line number Diff line change
@@ -23,6 +23,8 @@ export enum Eth1ErrorCode {
NON_CONSECUTIVE_LOGS = "ETH1_ERROR_NON_CONSECUTIVE_LOGS",
/** Expected a deposit log in the db for the index, missing log implies a corrupted db */
MISSING_DEPOSIT_LOG = "ETH1_ERROR_MISSING_DEPOSIT_LOG",
/** Expected transactions or withdrawals for un-blinding block from db before serving */
INVALID_PAYLOAD_BODY = "ETH1_ERROR_INVALID_PAYLOAD_BODY",
}

export type Eth1ErrorType =
@@ -35,6 +37,7 @@ export type Eth1ErrorType =
| {code: Eth1ErrorCode.NOT_ENOUGH_DEPOSIT_ROOTS; index: number; treeLength: number}
| {code: Eth1ErrorCode.DUPLICATE_DISTINCT_LOG; newIndex: number; lastLogIndex: number}
| {code: Eth1ErrorCode.NON_CONSECUTIVE_LOGS; newIndex: number; lastLogIndex: number}
| {code: Eth1ErrorCode.MISSING_DEPOSIT_LOG; newIndex: number; lastLogIndex: number};
| {code: Eth1ErrorCode.MISSING_DEPOSIT_LOG; newIndex: number; lastLogIndex: number}
| {code: Eth1ErrorCode.INVALID_PAYLOAD_BODY; blockHash: string};

export class Eth1Error extends LodestarError<Eth1ErrorType> {}