Skip to content

Commit

Permalink
Update forkChoice with valid/invalid updates from execution
Browse files Browse the repository at this point in the history
 * extend forkchoice execution type

 * handle the invalid status in protoarray

 * add the pre execution status validation testcase

 * relocate validateLatestHash to protoarray for better testing

 * add case for invalidating a branch

 * add invalidations and validations

 * add a bit more desc to test case title

 * fix the invalidate index bug

 * refac the reverse lookup and throw if forkchoice becomes invalidated

 * relax the LVH conditions in invalid responses

 * only invalidate post merge
  • Loading branch information
g11tech committed Jul 6, 2022
1 parent e5dabac commit adedd33
Show file tree
Hide file tree
Showing 10 changed files with 492 additions and 31 deletions.
4 changes: 2 additions & 2 deletions packages/beacon-node/src/chain/blocks/types.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import {CachedBeaconStateAllForks} from "@lodestar/state-transition";
import {ProtoBlock, ExecutionStatus} from "@lodestar/fork-choice";
import {ProtoBlock, MaybeValidExecutionStatus} from "@lodestar/fork-choice";
import {allForks} from "@lodestar/types";

export type FullyVerifiedBlockFlags = {
Expand All @@ -22,7 +22,7 @@ export type FullyVerifiedBlockFlags = {
/**
* If the execution payload couldnt be verified because of EL syncing status, used in optimistic sync or for merge block
*/
executionStatus?: ExecutionStatus;
executionStatus?: MaybeValidExecutionStatus;
};

export type PartiallyVerifiedBlockFlags = FullyVerifiedBlockFlags & {
Expand Down
29 changes: 19 additions & 10 deletions packages/beacon-node/src/chain/blocks/verifyBlock.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,13 @@ import {
} from "@lodestar/state-transition";
import {bellatrix} from "@lodestar/types";
import {toHexString} from "@chainsafe/ssz";
import {IForkChoice, ProtoBlock, ExecutionStatus, assertValidTerminalPowBlock} from "@lodestar/fork-choice";
import {
IForkChoice,
ProtoBlock,
ExecutionStatus,
MaybeValidExecutionStatus,
assertValidTerminalPowBlock,
} from "@lodestar/fork-choice";
import {IChainForkConfig} from "@lodestar/config";
import {ILogger} from "@lodestar/utils";
import {IMetrics} from "../../metrics/index.js";
Expand Down Expand Up @@ -128,7 +134,7 @@ export async function verifyBlockStateTransition(
chain: VerifyBlockModules,
partiallyVerifiedBlock: PartiallyVerifiedBlock,
opts: BlockProcessOpts
): Promise<{postState: CachedBeaconStateAllForks; executionStatus: ExecutionStatus}> {
): Promise<{postState: CachedBeaconStateAllForks; executionStatus: MaybeValidExecutionStatus}> {
const {block, validProposerSignature, validSignatures} = partiallyVerifiedBlock;

// TODO: Skip in process chain segment
Expand Down Expand Up @@ -185,6 +191,13 @@ export async function verifyBlockStateTransition(
}
}

// Parent is known to the fork-choice
const parentRoot = toHexString(block.message.parentRoot);
const parentBlock = chain.forkChoice.getBlockHex(parentRoot);
if (!parentBlock) {
throw new BlockError(block, {code: BlockErrorCode.PARENT_UNKNOWN, parentRoot});
}

let executionStatus: ExecutionStatus;
if (executionPayloadEnabled) {
// TODO: Handle better notifyNewPayload() returning error is syncing
Expand All @@ -199,10 +212,9 @@ export async function verifyBlockStateTransition(
case ExecutePayloadStatus.INVALID: {
// If the parentRoot is not same as latestValidHash, then the branch from latestValidHash
// to parentRoot needs to be invalidated
const parentHashHex = toHexString(block.message.parentRoot);
chain.forkChoice.validateLatestHash(
execResult.latestValidHash,
parentHashHex !== execResult.latestValidHash ? parentHashHex : null
parentBlock.executionPayloadBlockHash !== execResult.latestValidHash ? parentRoot : null
);
throw new BlockError(block, {
code: BlockErrorCode.EXECUTION_ENGINE_ERROR,
Expand Down Expand Up @@ -236,16 +248,13 @@ export async function verifyBlockStateTransition(
// SAFE_SLOTS_TO_IMPORT_OPTIMISTICALLY ahead of the slot of the block being
// imported.

const parentRoot = toHexString(block.message.parentRoot);
const parentBlock = chain.forkChoice.getBlockHex(parentRoot);
const justifiedBlock = chain.forkChoice.getJustifiedBlock();

if (
!parentBlock ||
// Following condition is the !(Not) of the safe import condition
(parentBlock.executionStatus === ExecutionStatus.PreMerge &&
justifiedBlock.executionStatus === ExecutionStatus.PreMerge &&
block.message.slot + opts.safeSlotsToImportOptimistically > chain.clock.currentSlot)
parentBlock.executionStatus === ExecutionStatus.PreMerge &&
justifiedBlock.executionStatus === ExecutionStatus.PreMerge &&
block.message.slot + opts.safeSlotsToImportOptimistically > chain.clock.currentSlot
) {
throw new BlockError(block, {
code: BlockErrorCode.EXECUTION_ENGINE_ERROR,
Expand Down
4 changes: 2 additions & 2 deletions packages/beacon-node/src/execution/engine/http.ts
Original file line number Diff line number Diff line change
Expand Up @@ -202,8 +202,8 @@ export class ExecutionEngineHttp implements IExecutionEngine {
}
: undefined;

// TODO: propogate latestValidHash to the forkchoice, for now ignore it as we
// currently do not propogate the validation status up the forkchoice
// TODO: propagate latestValidHash to the forkchoice, for now ignore it as we
// currently do not propagate the validation status up the forkchoice
const {
payloadStatus: {status, latestValidHash: _latestValidHash, validationError},
payloadId,
Expand Down
20 changes: 13 additions & 7 deletions packages/fork-choice/src/forkChoice/forkChoice.ts
Original file line number Diff line number Diff line change
Expand Up @@ -684,14 +684,20 @@ export class ForkChoice implements IForkChoice {
}

/**
* Optimistic sync validate till validated latest hash, invalidate any decendant branch if invalidate till hash provided
* TODO: implementation:
* 1. verify is_merge_block if the mergeblock has not yet been validated
* 2. Throw critical error and exit if a block in finalized chain gets invalidated
* Optimistic sync validate till validated latest hash, invalidate any decendant
* branch if invalidate till hash provided
*
* Proxies to protoArray's validateLatestHash and could run extra validations for the
* justified's status as well as validate the terminal conditions if terminal block
* becomes valid
*/
validateLatestHash(_latestValidHash: RootHex, _invalidateTillHash: RootHex | null): void {
// Silently ignore for now if all calls were valid
return;
validateLatestHash(latestValidExecHash: RootHex, invalidateTillBlockHash: RootHex | null): void {
this.protoArray.validateLatestHash(latestValidExecHash, invalidateTillBlockHash);

// Call findHead to validate that the forkChoice consensus has not broken down
// as it is possible for invalidation to invalidate the entire forkChoice if
// the consensus breaks down, which will cause findHead to throw
this.protoArray.findHead(this.fcStore.justifiedCheckpoint.rootHex);
}

private getPreMergeExecStatus(preCachedData?: OnBlockPrecachedData): ExecutionStatus.PreMerge {
Expand Down
4 changes: 2 additions & 2 deletions packages/fork-choice/src/forkChoice/interface.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import {EffectiveBalanceIncrements} from "@lodestar/state-transition";
import {BeaconStateAllForks} from "@lodestar/state-transition";
import {Epoch, Slot, ValidatorIndex, phase0, allForks, Root, RootHex} from "@lodestar/types";
import {ProtoBlock, ExecutionStatus} from "../protoArray/interface.js";
import {ProtoBlock, MaybeValidExecutionStatus} from "../protoArray/interface.js";
import {CheckpointWithHex} from "./store.js";

export type CheckpointHex = {
Expand Down Expand Up @@ -152,7 +152,7 @@ export type OnBlockPrecachedData = {
justifiedBalances?: EffectiveBalanceIncrements;
/** Time in seconds when the block was received */
blockDelaySec: number;
executionStatus?: ExecutionStatus;
executionStatus?: MaybeValidExecutionStatus;
};

export type LatestMessage = {
Expand Down
8 changes: 7 additions & 1 deletion packages/fork-choice/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
export {ProtoArray} from "./protoArray/protoArray.js";
export {ProtoBlock, ProtoNode, ExecutionStatus} from "./protoArray/interface.js";
export {
ProtoBlock,
ProtoNode,
ExecutionStatus,
MaybeValidExecutionStatus,
BlockExecution,
} from "./protoArray/interface.js";

export {ForkChoice, assertValidTerminalPowBlock} from "./forkChoice/forkChoice.js";
export {IForkChoice, OnBlockPrecachedData, PowBlockHex} from "./forkChoice/interface.js";
Expand Down
8 changes: 7 additions & 1 deletion packages/fork-choice/src/protoArray/errors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@ export enum ProtoArrayErrorCode {
INVALID_DELTA_LEN = "PROTO_ARRAY_ERROR_INVALID_DELTA_LEN",
REVERTED_FINALIZED_EPOCH = "PROTO_ARRAY_ERROR_REVERTED_FINALIZED_EPOCH",
INVALID_BEST_NODE = "PROTO_ARRAY_ERROR_INVALID_BEST_NODE",
INVALID_BLOCK_EXECUTION_STATUS = "PROTO_ARRAY_INVALID_BLOCK_EXECUTION_STATUS",
INVALID_PARENT_EXECUTION_STATUS = "PROTO_ARRAY_INVALID_PARENT_EXECUTION_STATUS",
INVALID_JUSTIFIED_EXECUTION_STATUS = "PROTO_ARRAY_INVALID_JUSTIFIED_EXECUTION_STATUS",
}

export type ProtoArrayErrorType =
Expand All @@ -40,7 +43,10 @@ export type ProtoArrayErrorType =
headRoot: RootHex;
headJustifiedEpoch: Epoch;
headFinalizedEpoch: Epoch;
};
}
| {code: ProtoArrayErrorCode.INVALID_BLOCK_EXECUTION_STATUS; root: RootHex}
| {code: ProtoArrayErrorCode.INVALID_PARENT_EXECUTION_STATUS; root: RootHex; parent: RootHex}
| {code: ProtoArrayErrorCode.INVALID_JUSTIFIED_EXECUTION_STATUS; root: RootHex};

export class ProtoArrayError extends LodestarError<ProtoArrayErrorType> {
constructor(type: ProtoArrayErrorType) {
Expand Down
7 changes: 5 additions & 2 deletions packages/fork-choice/src/protoArray/interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,13 @@ export enum ExecutionStatus {
Valid = "Valid",
Syncing = "Syncing",
PreMerge = "PreMerge",
Invalid = "Invalid",
}

type BlockExecution =
| {executionPayloadBlockHash: RootHex; executionStatus: ExecutionStatus.Valid | ExecutionStatus.Syncing}
export type MaybeValidExecutionStatus = Exclude<ExecutionStatus, ExecutionStatus.Invalid>;

export type BlockExecution =
| {executionPayloadBlockHash: RootHex; executionStatus: Exclude<ExecutionStatus, ExecutionStatus.PreMerge>}
| {executionPayloadBlockHash: null; executionStatus: ExecutionStatus.PreMerge};
/**
* A block that is to be applied to the fork choice
Expand Down
Loading

0 comments on commit adedd33

Please sign in to comment.