@lodestar/beacon-node
Version:
A Typescript implementation of the beacon chain
179 lines (167 loc) • 8.97 kB
text/typescript
import {IBeaconStateView} from "@lodestar/state-transition";
import {RootHex, SignedBeaconBlock, Slot, ValidatorIndex} from "@lodestar/types";
import {LodestarError, toRootHex} from "@lodestar/utils";
import {ExecutionPayloadStatus} from "../../execution/engine/interface.js";
import {QueueErrorCode} from "../../util/queue/index.js";
import {GossipActionError} from "./gossipValidation.js";
export enum BlockErrorCode {
/** The prestate cannot be fetched */
PRESTATE_MISSING = "BLOCK_ERROR_PRESTATE_MISSING",
/** The parent block was unknown. */
PARENT_UNKNOWN = "BLOCK_ERROR_PARENT_UNKNOWN",
/** The block slot is greater than the present slot. */
FUTURE_SLOT = "BLOCK_ERROR_FUTURE_SLOT",
/** The block state_root does not match the generated state. */
STATE_ROOT_MISMATCH = "BLOCK_ERROR_STATE_ROOT_MISMATCH",
/** The block was a genesis block, these blocks cannot be re-imported. */
GENESIS_BLOCK = "BLOCK_ERROR_GENESIS_BLOCK",
/** The slot is finalized, no need to import. */
WOULD_REVERT_FINALIZED_SLOT = "BLOCK_ERROR_WOULD_REVERT_FINALIZED_SLOT",
/** Block is already known, no need to re-import. */
ALREADY_KNOWN = "BLOCK_ERROR_ALREADY_KNOWN",
/** A block for this proposer and slot has already been observed. */
REPEAT_PROPOSAL = "BLOCK_ERROR_REPEAT_PROPOSAL",
/** The block slot exceeds the MAXIMUM_BLOCK_SLOT_NUMBER. */
BLOCK_SLOT_LIMIT_REACHED = "BLOCK_ERROR_BLOCK_SLOT_LIMIT_REACHED",
/** The `BeaconBlock` has a `proposer_index` that does not match the index we computed locally. */
INCORRECT_PROPOSER = "BLOCK_ERROR_INCORRECT_PROPOSER",
/** The proposal signature in invalid. */
PROPOSAL_SIGNATURE_INVALID = "BLOCK_ERROR_PROPOSAL_SIGNATURE_INVALID",
/** The `block.proposer_index` is not known. */
UNKNOWN_PROPOSER = "BLOCK_ERROR_UNKNOWN_PROPOSER",
/** A signature in the block is invalid (exactly which is unknown). */
INVALID_SIGNATURE = "BLOCK_ERROR_INVALID_SIGNATURE",
/** Block transition returns invalid state root. */
INVALID_STATE_ROOT = "BLOCK_ERROR_INVALID_STATE_ROOT",
/** Block (its parent) is not a descendant of current finalized block */
NOT_FINALIZED_DESCENDANT = "BLOCK_ERROR_NOT_FINALIZED_DESCENDANT",
/** The provided block is from an later slot than its parent. */
NOT_LATER_THAN_PARENT = "BLOCK_ERROR_NOT_LATER_THAN_PARENT",
/** At least one block in the chain segment did not have it's parent root set to the root of the prior block. */
NON_LINEAR_PARENT_ROOTS = "BLOCK_ERROR_NON_LINEAR_PARENT_ROOTS",
/** The slots of the blocks in the chain segment were not strictly increasing. */
NON_LINEAR_SLOTS = "BLOCK_ERROR_NON_LINEAR_SLOTS",
/** The block failed the specification's `per_block_processing` function, it is invalid. */
PER_BLOCK_PROCESSING_ERROR = "BLOCK_ERROR_PER_BLOCK_PROCESSING_ERROR",
/** There was an error whilst processing the block. It is not necessarily invalid. */
BEACON_CHAIN_ERROR = "BLOCK_ERROR_BEACON_CHAIN_ERROR",
/** Block did not pass validation during block processing. */
KNOWN_BAD_BLOCK = "BLOCK_ERROR_KNOWN_BAD_BLOCK",
/** Blacklisted blocks that should not pass processing */
BLACKLISTED_BLOCK = "BLOCK_ERROR_BLACKLISTED_BLOCK",
// Merge p2p
/** executionPayload.timestamp is not the expected value */
INCORRECT_TIMESTAMP = "BLOCK_ERROR_INCORRECT_TIMESTAMP",
/** executionPayload.gasUsed > executionPayload.gasLimit */
TOO_MUCH_GAS_USED = "BLOCK_ERROR_TOO_MUCH_GAS_USED",
/** executionPayload.blockHash == executionPayload.parentHash */
SAME_PARENT_HASH = "BLOCK_ERROR_SAME_PARENT_HASH",
/** Total size of executionPayload.transactions exceed a sane limit to prevent DOS attacks */
TRANSACTIONS_TOO_BIG = "BLOCK_ERROR_TRANSACTIONS_TOO_BIG",
/** Execution engine is unavailable, syncing, or api call errored. Peers must not be downscored on this code */
EXECUTION_ENGINE_ERROR = "BLOCK_ERROR_EXECUTION_ERROR",
/** The attestation head block is too far behind the attestation slot, causing many skip slots.
This is deemed a DoS risk */
TOO_MANY_SKIPPED_SLOTS = "TOO_MANY_SKIPPED_SLOTS",
/** The blobs are unavailable */
DATA_UNAVAILABLE = "BLOCK_ERROR_DATA_UNAVAILABLE",
/** Block contains too many kzg commitments */
TOO_MANY_KZG_COMMITMENTS = "BLOCK_ERROR_TOO_MANY_KZG_COMMITMENTS",
/** Bid parent block root does not match block parent root */
BID_PARENT_ROOT_MISMATCH = "BLOCK_ERROR_BID_PARENT_ROOT_MISMATCH",
/** The parent block's execution payload has been verified as invalid */
PARENT_EXECUTION_INVALID = "BLOCK_ERROR_PARENT_EXECUTION_INVALID",
/** The block's parent execution payload (defined by bid.parent_block_hash) has not been seen */
PARENT_PAYLOAD_UNKNOWN = "BLOCK_ERROR_PARENT_PAYLOAD_UNKNOWN",
/** An execution payload envelope in the chain segment references a block root that does not match its slot's block */
ENVELOPE_BLOCK_ROOT_MISMATCH = "BLOCK_ERROR_ENVELOPE_BLOCK_ROOT_MISMATCH",
}
type ExecutionErrorStatus = Exclude<
ExecutionPayloadStatus,
ExecutionPayloadStatus.VALID | ExecutionPayloadStatus.ACCEPTED | ExecutionPayloadStatus.SYNCING
>;
export type BlockErrorType =
| {code: BlockErrorCode.PRESTATE_MISSING; error: Error}
| {code: BlockErrorCode.PARENT_UNKNOWN; parentRoot: RootHex}
| {code: BlockErrorCode.FUTURE_SLOT; blockSlot: Slot; currentSlot: Slot}
| {code: BlockErrorCode.STATE_ROOT_MISMATCH}
| {code: BlockErrorCode.GENESIS_BLOCK}
| {code: BlockErrorCode.TOO_MANY_SKIPPED_SLOTS; parentSlot: Slot; blockSlot: Slot}
| {code: BlockErrorCode.WOULD_REVERT_FINALIZED_SLOT; blockSlot: Slot; finalizedSlot: Slot}
| {code: BlockErrorCode.ALREADY_KNOWN; root: RootHex}
| {code: BlockErrorCode.REPEAT_PROPOSAL; proposerIndex: ValidatorIndex}
| {code: BlockErrorCode.BLOCK_SLOT_LIMIT_REACHED}
| {code: BlockErrorCode.INCORRECT_PROPOSER; proposerIndex: ValidatorIndex}
| {code: BlockErrorCode.PROPOSAL_SIGNATURE_INVALID; blockSlot: Slot}
| {code: BlockErrorCode.UNKNOWN_PROPOSER; proposerIndex: ValidatorIndex}
| {code: BlockErrorCode.INVALID_SIGNATURE; state: IBeaconStateView}
| {
code: BlockErrorCode.INVALID_STATE_ROOT;
root: Uint8Array;
expectedRoot: Uint8Array;
preState: IBeaconStateView;
postState: IBeaconStateView;
}
| {code: BlockErrorCode.NOT_FINALIZED_DESCENDANT; parentRoot: RootHex}
| {code: BlockErrorCode.NOT_LATER_THAN_PARENT; parentSlot: Slot; slot: Slot}
| {code: BlockErrorCode.NON_LINEAR_PARENT_ROOTS}
| {code: BlockErrorCode.NON_LINEAR_SLOTS}
| {code: BlockErrorCode.ENVELOPE_BLOCK_ROOT_MISMATCH; envelopeBlockRoot: RootHex; blockRoot: RootHex}
| {code: BlockErrorCode.PER_BLOCK_PROCESSING_ERROR; error: Error}
| {code: BlockErrorCode.BEACON_CHAIN_ERROR; error: Error}
| {code: BlockErrorCode.KNOWN_BAD_BLOCK}
| {code: BlockErrorCode.BLACKLISTED_BLOCK}
| {code: BlockErrorCode.INCORRECT_TIMESTAMP; timestamp: number; expectedTimestamp: number}
| {code: BlockErrorCode.TOO_MUCH_GAS_USED; gasUsed: number; gasLimit: number}
| {code: BlockErrorCode.SAME_PARENT_HASH; blockHash: RootHex}
| {code: BlockErrorCode.TRANSACTIONS_TOO_BIG; size: number; max: number}
| {code: BlockErrorCode.EXECUTION_ENGINE_ERROR; execStatus: ExecutionErrorStatus; errorMessage: string}
| {code: BlockErrorCode.DATA_UNAVAILABLE}
| {code: BlockErrorCode.TOO_MANY_KZG_COMMITMENTS; blobKzgCommitmentsLen: number; commitmentLimit: number}
| {code: BlockErrorCode.BID_PARENT_ROOT_MISMATCH; bidParentRoot: RootHex; blockParentRoot: RootHex}
| {code: BlockErrorCode.PARENT_EXECUTION_INVALID; parentRoot: RootHex}
| {code: BlockErrorCode.PARENT_PAYLOAD_UNKNOWN; parentRoot: RootHex; parentBlockHash: RootHex};
export class BlockGossipError extends GossipActionError<BlockErrorType> {}
export class BlockError extends LodestarError<BlockErrorType> {
constructor(
readonly signedBlock: SignedBeaconBlock,
type: BlockErrorType
) {
super(type);
}
getMetadata(): Record<string, string | number | null> {
return renderBlockErrorType(this.type);
}
}
export function isBlockErrorAborted(e: unknown): e is BlockError {
return (
e instanceof BlockError &&
e.type.code === BlockErrorCode.EXECUTION_ENGINE_ERROR &&
e.type.errorMessage === QueueErrorCode.QUEUE_ABORTED
);
}
export function renderBlockErrorType(type: BlockErrorType): Record<string, string | number | null> {
switch (type.code) {
case BlockErrorCode.PRESTATE_MISSING:
case BlockErrorCode.PER_BLOCK_PROCESSING_ERROR:
case BlockErrorCode.BEACON_CHAIN_ERROR:
return {
code: type.code,
error: type.error.message,
};
case BlockErrorCode.INVALID_SIGNATURE:
return {
code: type.code,
slot: type.state.slot,
};
case BlockErrorCode.INVALID_STATE_ROOT:
return {
code: type.code,
slot: type.postState.slot,
root: toRootHex(type.root),
expectedRoot: toRootHex(type.expectedRoot),
};
default:
return type;
}
}