Skip to content
Merged
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
8 changes: 7 additions & 1 deletion packages/beacon-node/src/chain/blocks/importBlock.ts
Original file line number Diff line number Diff line change
Expand Up @@ -116,13 +116,19 @@ export async function importBlock(
}
executionStatus = parentBlock.executionStatus;
}

// getBeaconProposerOrNull will return null if head state is more than one epoch away
// from block slot. We skip proposer boost canonical check as we cannot determine the canonical proposer
const expectedProposerIndex: number | null = this.getHeadState().getBeaconProposerOrNull(blockSlot);

const blockSummary = this.forkChoice.onBlock(
block.message,
postState,
blockDelaySec,
currentSlot,
executionStatus,
dataAvailabilityStatus
dataAvailabilityStatus,
expectedProposerIndex
);

// This adds the state necessary to process the next block
Expand Down
5 changes: 0 additions & 5 deletions packages/beacon-node/test/spec/presets/fork_choice.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -624,11 +624,6 @@ const forkChoiceTest =
// integrated
shouldSkip: (_testcase, name, _index) =>
name.includes("invalid_incorrect_proof") ||
// TODO GLOAS: Proposer boost specs have been changed retroactively in v1.7.0-alpha.1,
// 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") ||
// TODO GLOAS: These tests will be unskipped by https://github.com/ChainSafe/lodestar/pull/9233
(name.includes("gloas") &&
(name.includes("simple_attempted_reorg_without_enough_ffg_votes") ||
Expand Down
7 changes: 5 additions & 2 deletions packages/beacon-node/test/spec/utils/gossipValidation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -475,6 +475,7 @@ export async function runGossipValidationTest(
}

const postState = computePostState(parentState, signedBlock, fork);
const expectedProposerIndex: number | null = chain.getHeadState().getBeaconProposerOrNull(slot);

if (blockEntry.failed) {
// payload_status === "VALID" (filtered above)
Expand All @@ -486,7 +487,8 @@ export async function runGossipValidationTest(
0,
slot,
ExecutionStatus.Valid,
getDataAvailabilityStatusForFork(fork)
getDataAvailabilityStatusForFork(fork),
expectedProposerIndex
);
blockStatesByRoot.set(blockRootHex, postState);
continue;
Expand All @@ -501,7 +503,8 @@ export async function runGossipValidationTest(
0,
slot,
ExecutionStatus.Syncing,
getDataAvailabilityStatusForFork(fork)
getDataAvailabilityStatusForFork(fork),
expectedProposerIndex
);
blockStatesByRoot.set(blockRootHex, postState);
invalidateImportedBlock(chain, blockRootHex, parentRootHex);
Expand Down
106 changes: 87 additions & 19 deletions packages/beacon-node/test/unit/chain/forkChoice/forkChoice.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -106,15 +106,17 @@ describe("LodestarForkChoice", () => {
blockDelaySec,
currentSlot,
executionStatus,
dataAvailabilityStatus
dataAvailabilityStatus,
null
);
forkChoice.onBlock(
orphanedBlock.message,
orphanedState,
blockDelaySec,
currentSlot,
executionStatus,
dataAvailabilityStatus
dataAvailabilityStatus,
null
);
let head = forkChoice.getHead();
expect(head.slot).toBe(orphanedBlock.message.slot);
Expand All @@ -124,7 +126,8 @@ describe("LodestarForkChoice", () => {
blockDelaySec,
currentSlot,
executionStatus,
dataAvailabilityStatus
dataAvailabilityStatus,
null
);
// tie break condition causes head to be orphaned block (based on hex root comparison)
head = forkChoice.getHead();
Expand All @@ -135,7 +138,8 @@ describe("LodestarForkChoice", () => {
blockDelaySec,
currentSlot,
executionStatus,
dataAvailabilityStatus
dataAvailabilityStatus,
null
);
head = forkChoice.getHead();
// without vote, head gets stuck at orphaned block
Expand Down Expand Up @@ -195,19 +199,75 @@ describe("LodestarForkChoice", () => {
const currentSlot = 128;
forkChoice.updateTime(currentSlot);

forkChoice.onBlock(block08.message, state08, blockDelaySec, currentSlot, executionStatus, dataAvailabilityStatus);
forkChoice.onBlock(block12.message, state12, blockDelaySec, currentSlot, executionStatus, dataAvailabilityStatus);
forkChoice.onBlock(block16.message, state16, blockDelaySec, currentSlot, executionStatus, dataAvailabilityStatus);
forkChoice.onBlock(block20.message, state20, blockDelaySec, currentSlot, executionStatus, dataAvailabilityStatus);
forkChoice.onBlock(block24.message, state24, blockDelaySec, currentSlot, executionStatus, dataAvailabilityStatus);
forkChoice.onBlock(block28.message, state28, blockDelaySec, currentSlot, executionStatus, dataAvailabilityStatus);
forkChoice.onBlock(
block08.message,
state08,
blockDelaySec,
currentSlot,
executionStatus,
dataAvailabilityStatus,
null
);
forkChoice.onBlock(
block12.message,
state12,
blockDelaySec,
currentSlot,
executionStatus,
dataAvailabilityStatus,
null
);
forkChoice.onBlock(
block16.message,
state16,
blockDelaySec,
currentSlot,
executionStatus,
dataAvailabilityStatus,
null
);
forkChoice.onBlock(
block20.message,
state20,
blockDelaySec,
currentSlot,
executionStatus,
dataAvailabilityStatus,
null
);
forkChoice.onBlock(
block24.message,
state24,
blockDelaySec,
currentSlot,
executionStatus,
dataAvailabilityStatus,
null
);
forkChoice.onBlock(
block28.message,
state28,
blockDelaySec,
currentSlot,
executionStatus,
dataAvailabilityStatus,
null
);
expect(forkChoice.getAllAncestorBlocks(hashBlock(block16.message), PayloadStatus.FULL)).toHaveLength(4);
expect(forkChoice.getAllAncestorBlocks(hashBlock(block24.message), PayloadStatus.FULL)).toHaveLength(6);
expect(forkChoice.getBlockHexDefaultStatus(hashBlock(block08.message))).not.toBeNull();
expect(forkChoice.getBlockHexDefaultStatus(hashBlock(block12.message))).not.toBeNull();
expect(forkChoice.hasBlockHex(hashBlock(block08.message))).toBe(true);
expect(forkChoice.hasBlockHex(hashBlock(block12.message))).toBe(true);
forkChoice.onBlock(block32.message, state32, blockDelaySec, currentSlot, executionStatus, dataAvailabilityStatus);
forkChoice.onBlock(
block32.message,
state32,
blockDelaySec,
currentSlot,
executionStatus,
dataAvailabilityStatus,
null
);
forkChoice.prune(hashBlock(block16.message));
expect(forkChoice.getAllAncestorBlocks(hashBlock(block16.message), PayloadStatus.FULL).length).toBeWithMessage(
1,
Expand Down Expand Up @@ -243,31 +303,35 @@ describe("LodestarForkChoice", () => {
blockDelaySec,
currentSlot,
executionStatus,
dataAvailabilityStatus
dataAvailabilityStatus,
null
);
forkChoice.onBlock(
orphanedBlock.message,
orphanedState,
blockDelaySec,
currentSlot,
executionStatus,
dataAvailabilityStatus
dataAvailabilityStatus,
null
);
forkChoice.onBlock(
parentBlock.message,
parentState,
blockDelaySec,
currentSlot,
executionStatus,
dataAvailabilityStatus
dataAvailabilityStatus,
null
);
forkChoice.onBlock(
childBlock.message,
childState,
blockDelaySec,
currentSlot,
executionStatus,
dataAvailabilityStatus
dataAvailabilityStatus,
null
);
const childBlockRoot = toHexString(ssz.phase0.BeaconBlock.hashTreeRoot(childBlock.message));
// the old way to get non canonical blocks
Expand Down Expand Up @@ -314,7 +378,8 @@ describe("LodestarForkChoice", () => {
blockDelaySec,
blockW.message.slot,
executionStatus,
dataAvailabilityStatus
dataAvailabilityStatus,
null
);

// X
Expand All @@ -326,7 +391,8 @@ describe("LodestarForkChoice", () => {
blockDelaySec,
blockX.message.slot,
executionStatus,
dataAvailabilityStatus
dataAvailabilityStatus,
null
);

// Y, same epoch to X
Expand All @@ -338,7 +404,8 @@ describe("LodestarForkChoice", () => {
blockDelaySec,
blockY.message.slot,
executionStatus,
dataAvailabilityStatus
dataAvailabilityStatus,
null
);

// Y and Z are candidates for new head, make more attestations on Y
Expand Down Expand Up @@ -379,7 +446,8 @@ describe("LodestarForkChoice", () => {
blockDelaySec,
blockZ.message.slot,
executionStatus,
dataAvailabilityStatus
dataAvailabilityStatus,
null
);

const head = forkChoice.updateHead();
Expand Down
10 changes: 8 additions & 2 deletions packages/fork-choice/src/forkChoice/forkChoice.ts
Original file line number Diff line number Diff line change
Expand Up @@ -601,7 +601,11 @@ export class ForkChoice implements IForkChoice {
blockDelaySec: number,
currentSlot: Slot,
executionStatus: BlockExecutionStatus,
dataAvailabilityStatus: DataAvailabilityStatus
dataAvailabilityStatus: DataAvailabilityStatus,
// The expected proposer index on the canonical chain we are following.
// Calculated by our head state. We use it as part of the proposer
// boost decision making. No boost will be set if this is null.
expectedProposerIndex: ValidatorIndex | null
): ProtoBlock {
const {parentRoot, slot} = block;
const parentRootHex = toRootHex(parentRoot);
Expand Down Expand Up @@ -674,7 +678,9 @@ export class ForkChoice implements IForkChoice {
this.opts?.proposerBoost &&
isTimely &&
// only boost the first block we see
this.proposerBoostRoot === null
this.proposerBoostRoot === null &&
expectedProposerIndex !== null &&
block.proposerIndex === expectedProposerIndex

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

this will be changed in ethereum/consensus-specs#5306 to use dependent root instead

) {
this.proposerBoostRoot = blockRootHex;
}
Expand Down
14 changes: 12 additions & 2 deletions packages/fork-choice/src/forkChoice/interface.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,14 @@
import {DataAvailabilityStatus, EffectiveBalanceIncrements, IBeaconStateView} from "@lodestar/state-transition";
import {AttesterSlashing, BeaconBlock, Epoch, IndexedAttestation, Root, RootHex, Slot} from "@lodestar/types";
import {
AttesterSlashing,
BeaconBlock,
Epoch,
IndexedAttestation,
Root,
RootHex,
Slot,
ValidatorIndex,
} from "@lodestar/types";
import {
BlockExecutionStatus,
LVHExecResponse,
Expand Down Expand Up @@ -145,7 +154,8 @@ export interface IForkChoice {
blockDelaySec: number,
currentSlot: Slot,
executionStatus: BlockExecutionStatus,
dataAvailabilityStatus: DataAvailabilityStatus
dataAvailabilityStatus: DataAvailabilityStatus,
expectedProposerIndex: ValidatorIndex | null
): ProtoBlock;
/**
* Register `attestation` with the fork choice DAG so that it may influence future calls to `getHead`.
Expand Down
17 changes: 10 additions & 7 deletions packages/state-transition/src/cache/epochCache.ts
Original file line number Diff line number Diff line change
Expand Up @@ -782,14 +782,17 @@ export class EpochCache {
*/
getBeaconProposer(slot: Slot): ValidatorIndex {
const epoch = computeEpochAtSlot(slot);
if (epoch !== this.currentShuffling.epoch) {
throw new EpochCacheError({
code: EpochCacheErrorCode.PROPOSER_EPOCH_MISMATCH,
currentEpoch: this.currentShuffling.epoch,
requestedEpoch: epoch,
});
if (epoch === this.currentShuffling.epoch) {
return this.proposers[slot % SLOTS_PER_EPOCH];
}
if (epoch === this.nextEpoch) {
return this.getBeaconProposersNextEpoch()[slot % SLOTS_PER_EPOCH];
}
return this.proposers[slot % SLOTS_PER_EPOCH];
throw new EpochCacheError({
code: EpochCacheErrorCode.PROPOSER_EPOCH_MISMATCH,
currentEpoch: this.currentShuffling.epoch,
requestedEpoch: epoch,
});
}

getBeaconProposers(): ValidatorIndex[] {
Expand Down
8 changes: 8 additions & 0 deletions packages/state-transition/src/stateView/beaconStateView.ts
Original file line number Diff line number Diff line change
Expand Up @@ -498,6 +498,14 @@ export class BeaconStateView implements IBeaconStateViewLatestFork {
return this.cachedState.epochCtx.getBeaconProposer(slot);
}

getBeaconProposerOrNull(slot: Slot): ValidatorIndex | null {
try {
return this.cachedState.epochCtx.getBeaconProposer(slot);
} catch {
return null;
}
}

computeAnchorCheckpoint(): {checkpoint: phase0.Checkpoint; blockHeader: phase0.BeaconBlockHeader} {
return computeAnchorCheckpoint(this.config, this.cachedState);
}
Expand Down
1 change: 1 addition & 0 deletions packages/state-transition/src/stateView/interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ export interface IBeaconStateView {
currentProposers: ValidatorIndex[];
nextProposers: ValidatorIndex[];
getBeaconProposer(slot: Slot): ValidatorIndex;
getBeaconProposerOrNull(slot: Slot): ValidatorIndex | null;

// Validators and balances
effectiveBalanceIncrements: EffectiveBalanceIncrements;
Expand Down
Loading