Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
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
9 changes: 8 additions & 1 deletion packages/beacon-node/src/chain/blocks/verifyBlock.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,14 @@ export async function verifyBlocksInEpoch(
// Retrieve preState from cache (regen)
const preState0 = await this.regen
// transfer cache to process faster, postState will be in block state cache
.getPreState(block0.message, {dontTransferCache: false}, RegenCaller.processBlocksInEpoch)
.getPreState(
block0.message,
{
dontTransferCache: false,
dangerouslyAssumeValidDepositSignatures: opts.dangerouslyAssumeValidDepositSignatures,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Propagate the deposit option through block replay

When the parent is in the same epoch and its state has been evicted, StateRegenerator.getPreState() calls getState() without these options; getState() may then replay the epoch-boundary block using stateTransition() without dangerouslyAssumeValidDepositSignatures. For bls_setting=2 vectors whose pending deposit is processed at that boundary, replay rejects the placeholder signature and fails its state-root check, so this option only works while the relevant parent or checkpoint state remains cached. Carry the option into the replay path as well.

Useful? React with 馃憤聽/ 馃憥.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed

},
RegenCaller.processBlocksInEpoch
)
.catch((e) => {
throw new BlockError(block0, {code: BlockErrorCode.PRESTATE_MISSING, error: e as Error});
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ export async function verifyBlocksStateTransitionOnly(
// if block is trusted don't verify proposer or op signature
verifyProposer: !useBlsBatchVerify && !validSignatures && !validProposerSignature,
verifySignatures: !useBlsBatchVerify && !validSignatures,
dangerouslyAssumeValidDepositSignatures: opts.dangerouslyAssumeValidDepositSignatures,
dontTransferCache: false,
},
{metrics, validatorMonitor}
Expand Down
7 changes: 7 additions & 0 deletions packages/beacon-node/src/chain/options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,12 @@ export type BlockProcessOpts = {
* Assert progressive balances the same to EpochTransitionCache
*/
assertCorrectProgressiveBalances?: boolean;
/**
* Skip pending deposit signature verification during epoch processing, treating every signature as
* valid. Used for fast_confirmation spec tests, whose `bls_setting: 2` vectors carry placeholder
* deposit signatures; pyspec stubs `bls.Verify` to true for those. MUST stay false on a real node.
*/
dangerouslyAssumeValidDepositSignatures?: boolean;
/** Used for fork_choice spec tests */
disableOnBlockError?: boolean;
/** Used for fork_choice spec tests */
Expand Down Expand Up @@ -115,6 +121,7 @@ export const defaultChainOptions: IChainOptions = {
graffitiAppend: true,
serveHistoricalState: false,
assertCorrectProgressiveBalances: false,
dangerouslyAssumeValidDepositSignatures: false,
archiveStateEpochFrequency: 1024,
archiveMode: DEFAULT_ARCHIVE_MODE,
pruneHistory: false,
Expand Down
4 changes: 2 additions & 2 deletions packages/beacon-node/src/chain/regen/interface.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import {routes} from "@lodestar/api";
import {ProtoBlock} from "@lodestar/fork-choice";
import {IBeaconStateView} from "@lodestar/state-transition";
import {EpochTransitionCacheOpts, IBeaconStateView} from "@lodestar/state-transition";
import {BeaconBlock, Epoch, RootHex, Slot, phase0} from "@lodestar/types";
import {CheckpointHex} from "../stateCache/types.js";

Expand Down Expand Up @@ -36,7 +36,7 @@ export enum RegenFnName {
getPreState = "getPreState",
}

export type StateRegenerationOpts = {
export type StateRegenerationOpts = EpochTransitionCacheOpts & {
dontTransferCache: boolean;
};

Expand Down
11 changes: 8 additions & 3 deletions packages/beacon-node/src/chain/regen/regen.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import {IForkChoice, ProtoBlock} from "@lodestar/fork-choice";
import {SLOTS_PER_EPOCH} from "@lodestar/params";
import {
DataAvailabilityStatus,
EpochTransitionCacheOpts,
ExecutionPayloadStatus,
IBeaconStateView,
StateHashTreeRootSource,
Expand Down Expand Up @@ -79,7 +80,7 @@ export class StateRegenerator implements IStateRegeneratorInternal {
}

// Otherwise, get the state normally.
return this.getState(parentBlock.stateRoot, regenCaller, allowDiskReload);
return this.getState(parentBlock.stateRoot, regenCaller, allowDiskReload, opts);
}

/**
Expand Down Expand Up @@ -118,7 +119,7 @@ export class StateRegenerator implements IStateRegeneratorInternal {
// Otherwise, use the fork choice to get the stateRoot from block at the checkpoint root
// regenerate that state,
// then process empty slots until the requested epoch
const blockStateCtx = await this.getState(block.stateRoot, regenCaller, allowDiskReload);
const blockStateCtx = await this.getState(block.stateRoot, regenCaller, allowDiskReload, opts);
return processSlotsByCheckpoint(this.modules, blockStateCtx, slot, regenCaller, opts);
}

Expand All @@ -131,7 +132,9 @@ export class StateRegenerator implements IStateRegeneratorInternal {
stateRoot: RootHex,
caller: RegenCaller,
// internal option, don't want to expose to external caller
allowDiskReload = false
allowDiskReload = false,
// internal option, don't want to expose to external caller
opts?: EpochTransitionCacheOpts
): Promise<IBeaconStateView> {
// Trivial case, state at stateRoot is already cached
const cachedStateCtx = this.modules.blockStateCache.get(stateRoot);
Expand Down Expand Up @@ -243,6 +246,8 @@ export class StateRegenerator implements IStateRegeneratorInternal {
verifyStateRoot: false,
verifyProposer: false,
verifySignatures: false,
// replaying already imported blocks, keep the same deposit handling they were imported with
dangerouslyAssumeValidDepositSignatures: opts?.dangerouslyAssumeValidDepositSignatures,
dontTransferCache: false,
},
this.modules
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,9 @@ const fastConfirmationTest =
// we don't use these in fork choice spec tests
disablePrepareNextSlot: true,
assertCorrectProgressiveBalances,
// Deposits are applied in epoch processing, past the point where `validSignatures` below
// can mark a block trusted, so bls_setting=2 has to be honored separately here
dangerouslyAssumeValidDepositSignatures: testcase.meta?.bls_setting !== BigInt(1),
proposerBoost: true,
proposerBoostReorg: true,
fastConfirmation: true,
Expand Down Expand Up @@ -713,13 +716,7 @@ const fastConfirmationTest =
// and these tests are failing until we update our implementation.
name.includes("voting_source_beyond_two_epoch") ||
name.includes("justified_update_always_if_better") ||
name.includes("justified_update_not_realized_finality") ||
// These vectors carry stub deposit signatures (bls_setting=2) and expect the deposit to
// be applied. Passing them requires skipping deposit signature verification inside epoch
// processing, which Lodestar does not support. Unskip if upstream signs deposits for
// real, or if full bls_setting=2 support is ever added.
name.includes("is_one_confirmed_fails_recently_activated_validator_voting_in_empty_slot") ||
name.includes("is_one_confirmed_passes_with_new_validator_activated_in_head_state"),
name.includes("justified_update_not_realized_finality"),
},
};
};
Expand Down
12 changes: 12 additions & 0 deletions packages/state-transition/src/cache/epochTransitionCache.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,14 @@ export type EpochTransitionCacheOpts = {
* Assert progressive balances the same to EpochTransitionCache
*/
assertCorrectProgressiveBalances?: boolean;
/**
* Treat every pending deposit signature as valid instead of verifying it.
*
* SPEC TESTS ONLY. Mirrors pyspec's `bls_setting: 2`, where `bls.Verify` is stubbed to return true
* so vectors can carry placeholder deposit signatures. Enabling this on a real node applies
* deposits with invalid proofs of possession, which is a consensus fault.
*/
dangerouslyAssumeValidDepositSignatures?: boolean;
};

/**
Expand Down Expand Up @@ -194,6 +202,9 @@ export interface EpochTransitionCache {
* Used in `processEffectiveBalanceUpdates` to save one loop over validators after epoch process.
*/
isActiveNextEpoch: boolean[];

/** {@see} {@link EpochTransitionCacheOpts.dangerouslyAssumeValidDepositSignatures} */
dangerouslyAssumeValidDepositSignatures: boolean;
}

// reuse arrays to avoid memory reallocation and gc
Expand Down Expand Up @@ -518,6 +529,7 @@ export function beforeProcessEpoch(
inclusionDelays,
flags,
isCompoundingValidatorArr,
dangerouslyAssumeValidDepositSignatures: opts?.dangerouslyAssumeValidDepositSignatures ?? false,
// Will be assigned in processRewardsAndPenalties()
balances: undefined,
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,10 @@ function applyPendingDeposit(

if (!isValidatorKnown(state, validatorIndex)) {
// Verify the deposit signature (proof of possession) which is not checked by the deposit contract
if (isValidDepositSignature(state.config, pubkey, withdrawalCredentials, amount, signature)) {
if (
cache.dangerouslyAssumeValidDepositSignatures ||
isValidDepositSignature(state.config, pubkey, withdrawalCredentials, amount, signature)
) {
addValidatorToRegistry(ForkSeq.electra, state, pubkey, withdrawalCredentials, amount);
const newValidatorIndex = state.validators.length - 1;
cache.isCompoundingValidatorArr[newValidatorIndex] = hasCompoundingWithdrawalCredential(withdrawalCredentials);
Expand Down
6 changes: 5 additions & 1 deletion packages/state-transition/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,11 @@ export {
type EpochCacheImmutableData,
createEmptyEpochCacheImmutableData,
} from "./cache/epochCache.js";
export {type EpochTransitionCache, beforeProcessEpoch} from "./cache/epochTransitionCache.js";
export {
type EpochTransitionCache,
type EpochTransitionCacheOpts,
beforeProcessEpoch,
} from "./cache/epochTransitionCache.js";
// Main state caches
export {
type BeaconStateCache,
Expand Down
Loading