Skip to content

Commit

Permalink
Use enum for BlockImport
Browse files Browse the repository at this point in the history
  • Loading branch information
dapplion committed Nov 29, 2022
1 parent 4bf587d commit b30aa71
Show file tree
Hide file tree
Showing 14 changed files with 197 additions and 135 deletions.
13 changes: 10 additions & 3 deletions packages/beacon-node/src/api/impl/beacon/blocks/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import {ForkSeq, SLOTS_PER_HISTORICAL_ROOT} from "@lodestar/params";
import {sleep} from "@lodestar/utils";
import {eip4844} from "@lodestar/types";
import {fromHexString, toHexString} from "@chainsafe/ssz";
import {BlockImport} from "../../../../chain/blocks/types.js";
import {BlockImport, BlockImportType} from "../../../../chain/blocks/types.js";
import {promiseAllMaybeAsync} from "../../../../util/promises.js";
import {BlockError, BlockErrorCode} from "../../../../chain/errors/index.js";
import {OpSource} from "../../../../metrics/validatorMonitor.js";
Expand Down Expand Up @@ -192,8 +192,15 @@ export function getBeaconBlockApi({
// TODO EIP-4844: Open question if broadcast to both block topic + block_and_blobs topic
const blockForImport: BlockImport =
config.getForkSeq(signedBlock.message.slot) >= ForkSeq.eip4844
? {block: signedBlock, blobs: chain.getBlobsSidecar(signedBlock.message as eip4844.BeaconBlock)}
: {block: signedBlock, blobs: null};
? {
type: BlockImportType.postEIP4844,
block: signedBlock,
blobs: chain.getBlobsSidecar(signedBlock.message as eip4844.BeaconBlock),
}
: {
type: BlockImportType.preEIP4844,
block: signedBlock,
};

await promiseAllMaybeAsync([
// Send the block, regardless of whether or not it is valid. The API
Expand Down
16 changes: 7 additions & 9 deletions packages/beacon-node/src/chain/blocks/importBlock.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import {altair, ssz} from "@lodestar/types";
import {ForkSeq, MAX_SEED_LOOKAHEAD, SLOTS_PER_EPOCH} from "@lodestar/params";
import {MAX_SEED_LOOKAHEAD, SLOTS_PER_EPOCH} from "@lodestar/params";
import {toHexString} from "@chainsafe/ssz";
import {toHex} from "@lodestar/utils";
import {
Expand All @@ -16,7 +16,7 @@ import {ChainEvent} from "../emitter.js";
import {REPROCESS_MIN_TIME_TO_NEXT_SLOT_SEC} from "../reprocess.js";
import {RegenCaller} from "../regen/interface.js";
import type {BeaconChain} from "../chain.js";
import {FullyVerifiedBlock, ImportBlockOpts} from "./types.js";
import {BlockImportType, FullyVerifiedBlock, ImportBlockOpts} from "./types.js";
import {PendingEvents} from "./utils/pendingEvents.js";
import {getCheckpointFromState} from "./utils/checkpoint.js";

Expand Down Expand Up @@ -49,7 +49,8 @@ export async function importBlock(
fullyVerifiedBlock: FullyVerifiedBlock,
opts: ImportBlockOpts
): Promise<void> {
const {block, blobs, postState, parentBlockSlot, executionStatus} = fullyVerifiedBlock;
const {blockImport, postState, parentBlockSlot, executionStatus} = fullyVerifiedBlock;
const {block} = blockImport;
const pendingEvents = new PendingEvents(this.emitter);

// - Observe attestations
Expand Down Expand Up @@ -320,18 +321,15 @@ export async function importBlock(
root: blockRoot,
});

if (this.config.getForkSeq(block.message.slot) >= ForkSeq.eip4844) {
if (!blobs) {
throw Error("blobsSidecar not provided for block post eip4844");
}
if (blockImport.type === BlockImportType.postEIP4844) {
const {blobs} = blockImport;
// NOTE: Old blobs are pruned on archive
await this.db.blobsSidecar.add(blobs);
this.logger.debug("Persisted blobsSidecar to hot DB", {
blobsLen: blobs.blobs.length,
slot: blobs.beaconBlockSlot,
root: toHex(blobs.beaconBlockRoot),
});

// TODO EIP-4844: Prune old blobs
}

// - head_tracker.register_block(block_root, parent_root, slot)
Expand Down
5 changes: 2 additions & 3 deletions packages/beacon-node/src/chain/blocks/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -86,9 +86,8 @@ export async function processBlocks(

const {executionStatuses} = segmentExecStatus;
const fullyVerifiedBlocks = relevantBlocks.map(
({block, blobs}, i): FullyVerifiedBlock => ({
block,
blobs,
(block, i): FullyVerifiedBlock => ({
blockImport: block,
postState: postStates[i],
parentBlockSlot: parentSlots[i],
executionStatus: executionStatuses[i],
Expand Down
60 changes: 54 additions & 6 deletions packages/beacon-node/src/chain/blocks/types.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,59 @@
import {CachedBeaconStateAllForks} from "@lodestar/state-transition";
import {CachedBeaconStateAllForks, computeEpochAtSlot} from "@lodestar/state-transition";
import {MaybeValidExecutionStatus} from "@lodestar/fork-choice";
import {allForks, eip4844, Slot} from "@lodestar/types";
import {ForkSeq} from "@lodestar/params";
import {IChainForkConfig} from "@lodestar/config";

export type BlockImport = {
block: allForks.SignedBeaconBlock;
blobs: eip4844.BlobsSidecar | null;
export enum BlockImportType {
preEIP4844 = "preEIP4844",
postEIP4844 = "postEIP4844",
postEIP4844OldBlobs = "postEIP4844OldBlobs",
}

export type BlockImport =
| {type: BlockImportType.preEIP4844; block: allForks.SignedBeaconBlock}
| {type: BlockImportType.postEIP4844; block: allForks.SignedBeaconBlock; blobs: eip4844.BlobsSidecar}
| {type: BlockImportType.postEIP4844OldBlobs; block: allForks.SignedBeaconBlock};

export function blockRequiresBlobs(config: IChainForkConfig, blockSlot: Slot, clockSlot: Slot): boolean {
return (
config.getForkSeq(blockSlot) >= ForkSeq.eip4844 &&
// Only request blobs if they are recent enough
computeEpochAtSlot(blockSlot) >= computeEpochAtSlot(clockSlot) - config.MIN_EPOCHS_FOR_BLOBS_SIDECARS_REQUESTS
);
}

export const getBlockImport = {
preEIP4844(config: IChainForkConfig, block: allForks.SignedBeaconBlock): BlockImport {
if (config.getForkSeq(block.message.slot) >= ForkSeq.eip4844) {
throw Error(`Post EIP4844 block slot ${block.message.slot}`);
}
return {
type: BlockImportType.preEIP4844,
block,
};
},

postEIP4844(config: IChainForkConfig, block: allForks.SignedBeaconBlock, blobs: eip4844.BlobsSidecar): BlockImport {
if (config.getForkSeq(block.message.slot) < ForkSeq.eip4844) {
throw Error(`Pre EIP4844 block slot ${block.message.slot}`);
}
return {
type: BlockImportType.postEIP4844,
block,
blobs,
};
},

postEIP4844OldBlobs(config: IChainForkConfig, block: allForks.SignedBeaconBlock): BlockImport {
if (config.getForkSeq(block.message.slot) < ForkSeq.eip4844) {
throw Error(`Pre EIP4844 block slot ${block.message.slot}`);
}
return {
type: BlockImportType.postEIP4844OldBlobs,
block,
};
},
};

export type ImportBlockOpts = {
Expand Down Expand Up @@ -48,8 +97,7 @@ export type ImportBlockOpts = {
* A wrapper around a `SignedBeaconBlock` that indicates that this block is fully verified and ready to import
*/
export type FullyVerifiedBlock = {
block: allForks.SignedBeaconBlock;
blobs: eip4844.BlobsSidecar | null;
blockImport: BlockImport;
postState: CachedBeaconStateAllForks;
parentBlockSlot: Slot;
proposerBalanceDelta: number;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,13 @@ import {
} from "@lodestar/state-transition";
import {eip4844} from "@lodestar/types";
import {ErrorAborted, sleep} from "@lodestar/utils";
import {ForkSeq} from "@lodestar/params";
import {IChainForkConfig} from "@lodestar/config";
import {IMetrics} from "../../metrics/index.js";
import {BlockError, BlockErrorCode} from "../errors/index.js";
import {BlockProcessOpts} from "../options.js";
import {byteArrayEquals} from "../../util/bytes.js";
import {validateBlobsSidecar} from "../validation/blobsSidecar.js";
import {BlockImport, ImportBlockOpts} from "./types.js";
import {BlockImport, BlockImportType, ImportBlockOpts} from "./types.js";

/**
* Verifies 1 or more blocks are fully valid running the full state transition; from a linear sequence of blocks.
Expand All @@ -35,24 +35,13 @@ export async function verifyBlocksStateTransitionOnly(

for (let i = 0; i < blocks.length; i++) {
const {validProposerSignature, validSignatures} = opts;
const {block, blobs} = blocks[i];
const blockSlot = block.message.slot;
const {block} = blocks[i];
const preState = i === 0 ? preState0 : postStates[i - 1];

// TODO EIP-4844: Is the best place here to call validateBlobsSidecar()?
// TODO EIP-4844: Gossip may already call validateBlobsSidecar, add some flag to de-dup from here
// TODO EIP-4844: For sync if this function is expensive, consider adding sleep(0) if metrics show it
const postEIP4844 = config.getForkSeq(blockSlot) >= ForkSeq.eip4844;
if (postEIP4844) {
if (blobs === null) {
// TODO EIP-4844: Throw typed errors
throw Error(`Post eip-4844 block slot ${blockSlot} blobs = null`);
}
const {blobKzgCommitments} = (block as eip4844.SignedBeaconBlock).message.body;
const beaconBlockRoot = config.getForkTypes(blockSlot).BeaconBlock.hashTreeRoot(block.message);
// TODO EIP-4844: This function throws un-typed errors
validateBlobsSidecar(blockSlot, beaconBlockRoot, blobKzgCommitments, blobs);
}
const dataAvailableStatus = maybeValidateBlobs(config, blocks[i]);

// STFN - per_slot_processing() + per_block_processing()
// NOTE: `regen.getPreState()` should have dialed forward the state already caching checkpoint states
Expand All @@ -65,7 +54,7 @@ export async function verifyBlocksStateTransitionOnly(
// Latter verifyBlocksInEpoch() will make sure that payload is indeed valid
executionPayloadStatus: ExecutionPayloadStatus.valid,
// TODO EIP-4844: Data is validated above for
dataAvailableStatus: postEIP4844 ? DataAvailableStatus.available : DataAvailableStatus.preEIP4844,
dataAvailableStatus,
// false because it's verified below with better error typing
verifyStateRoot: false,
// if block is trusted don't verify proposer or op signature
Expand Down Expand Up @@ -106,3 +95,26 @@ export async function verifyBlocksStateTransitionOnly(

return {postStates, proposerBalanceDeltas};
}

function maybeValidateBlobs(config: IChainForkConfig, blockImport: BlockImport): DataAvailableStatus {
// TODO EIP4844: Make switch verify it's exhaustive
switch (blockImport.type) {
case BlockImportType.postEIP4844: {
const {block, blobs} = blockImport;
const blockSlot = block.message.slot;
const {blobKzgCommitments} = (block as eip4844.SignedBeaconBlock).message.body;
const beaconBlockRoot = config.getForkTypes(blockSlot).BeaconBlock.hashTreeRoot(block.message);
// TODO EIP-4844: This function throws un-typed errors
validateBlobsSidecar(blockSlot, beaconBlockRoot, blobKzgCommitments, blobs);

return DataAvailableStatus.available;
}

case BlockImportType.preEIP4844:
return DataAvailableStatus.preEIP4844;

// TODO: Ok to assume old data available?
case BlockImportType.postEIP4844OldBlobs:
return DataAvailableStatus.available;
}
}
10 changes: 4 additions & 6 deletions packages/beacon-node/src/network/gossip/handlers/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ import {PeerAction} from "../../peers/index.js";
import {validateLightClientFinalityUpdate} from "../../../chain/validation/lightClientFinalityUpdate.js";
import {validateLightClientOptimisticUpdate} from "../../../chain/validation/lightClientOptimisticUpdate.js";
import {validateGossipBlobsSidecar} from "../../../chain/validation/blobsSidecar.js";
import {BlockImport} from "../../../chain/blocks/types.js";
import {BlockImport, getBlockImport} from "../../../chain/blocks/types.js";

/**
* Gossip handler options as part of network options
Expand Down Expand Up @@ -152,27 +152,25 @@ export function getGossipHandlers(modules: ValidatorFnsModules, options: GossipH

return {
[GossipType.beacon_block]: async (signedBlock, topic, peerIdStr, seenTimestampSec) => {
const blockImport: BlockImport = {block: signedBlock, blobs: null};
await validateBeaconBlock(blockImport, topic.fork, peerIdStr);

// TODO EIP-4844: Can blocks be received by this topic?
if (config.getForkSeq(signedBlock.message.slot) >= ForkSeq.eip4844) {
throw new GossipActionError(GossipAction.REJECT, {code: "POST_EIP4844_BLOCK"});
}

const blockImport = getBlockImport.preEIP4844(config, signedBlock);
await validateBeaconBlock(blockImport, topic.fork, peerIdStr);
handleValidBeaconBlock(blockImport, peerIdStr, seenTimestampSec);
},

[GossipType.beacon_block_and_blobs_sidecar]: async (blockAndBlocks, topic, peerIdStr, seenTimestampSec) => {
const {beaconBlock, blobsSidecar} = blockAndBlocks;
const blockImport: BlockImport = {block: beaconBlock, blobs: blobsSidecar};

// TODO EIP-4844: Should throw for pre fork blocks?
if (config.getForkSeq(beaconBlock.message.slot) < ForkSeq.eip4844) {
throw new GossipActionError(GossipAction.REJECT, {code: "PRE_EIP4844_BLOCK"});
}

// Validate block + blob. Then forward, then handle both
const blockImport = getBlockImport.postEIP4844(config, beaconBlock, blobsSidecar);
await validateBeaconBlock(blockImport, topic.fork, peerIdStr);
await validateGossipBlobsSidecar(beaconBlock, blobsSidecar);
handleValidBeaconBlock(blockImport, peerIdStr, seenTimestampSec);
Expand Down
Loading

0 comments on commit b30aa71

Please sign in to comment.