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
6 changes: 3 additions & 3 deletions packages/beacon-node/src/chain/emitter.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import {EventEmitter} from "node:events";
import {StrictEventEmitter} from "strict-event-emitter-types";
import {routes} from "@lodestar/api";
import {CheckpointWithHex} from "@lodestar/fork-choice";
import {CheckpointWithPayload} from "@lodestar/fork-choice";
import {CachedBeaconStateAllForks} from "@lodestar/state-transition";
import {RootHex, deneb, fulu, phase0} from "@lodestar/types";
import {PeerIdStr} from "../util/peerId.js";
Expand Down Expand Up @@ -83,8 +83,8 @@ export type ChainEventData = {
export type IChainEvents = ApiEvents & {
[ChainEvent.checkpoint]: (checkpoint: phase0.Checkpoint, state: CachedBeaconStateAllForks) => void;

[ChainEvent.forkChoiceJustified]: (checkpoint: CheckpointWithHex) => void;
[ChainEvent.forkChoiceFinalized]: (checkpoint: CheckpointWithHex) => void;
[ChainEvent.forkChoiceJustified]: (checkpoint: CheckpointWithPayload) => void;
[ChainEvent.forkChoiceFinalized]: (checkpoint: CheckpointWithPayload) => void;

[ChainEvent.updateTargetCustodyGroupCount]: (targetGroupCount: number) => void;

Expand Down
21 changes: 19 additions & 2 deletions packages/beacon-node/src/chain/forkChoice/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
ProtoArray,
ProtoBlock,
ForkChoiceOpts as RawForkChoiceOpts,
getCheckpointPayloadStatus,
} from "@lodestar/fork-choice";
import {ZERO_HASH_HEX} from "@lodestar/params";
import {
Expand Down Expand Up @@ -107,6 +108,12 @@ export function initializeForkChoiceFromFinalizedState(

const isForkPostGloas = (state as CachedBeaconStateGloas).latestBlockHash !== undefined;

// Determine justified checkpoint payload status
const justifiedPayloadStatus = getCheckpointPayloadStatus(state, justifiedCheckpoint.epoch);

// Determine finalized checkpoint payload status
const finalizedPayloadStatus = getCheckpointPayloadStatus(state, finalizedCheckpoint.epoch);

return new forkchoiceConstructor(
config,

Expand All @@ -116,6 +123,8 @@ export function initializeForkChoiceFromFinalizedState(
finalizedCheckpoint,
justifiedBalances,
justifiedBalancesGetter,
justifiedPayloadStatus,
finalizedPayloadStatus,
{
onJustified: (cp) => emitter.emit(ChainEvent.forkChoiceJustified, cp),
onFinalized: (cp) => emitter.emit(ChainEvent.forkChoiceFinalized, cp),
Expand Down Expand Up @@ -196,20 +205,28 @@ export function initializeForkChoiceFromUnfinalizedState(

// this is not the justified state, but there is no other ways to get justified balances
const justifiedBalances = getEffectiveBalanceIncrementsZeroInactive(unfinalizedState);

const isForkPostGloas = (unfinalizedState as CachedBeaconStateGloas).latestBlockHash !== undefined;

// For unfinalized state, use getCheckpointPayloadStatus to determine the correct status.
// It checks state.execution_payload_availability to determine EMPTY vs FULL.
const justifiedPayloadStatus = getCheckpointPayloadStatus(unfinalizedState, justifiedCheckpoint.epoch);
const finalizedPayloadStatus = getCheckpointPayloadStatus(unfinalizedState, finalizedCheckpoint.epoch);

const store = new ForkChoiceStore(
currentSlot,
justifiedCheckpoint,
finalizedCheckpoint,
justifiedBalances,
justifiedBalancesGetter,
justifiedPayloadStatus,
finalizedPayloadStatus,
{
onJustified: (cp) => emitter.emit(ChainEvent.forkChoiceJustified, cp),
onFinalized: (cp) => emitter.emit(ChainEvent.forkChoiceFinalized, cp),
}
);

const isForkPostGloas = (unfinalizedState as CachedBeaconStateGloas).latestBlockHash !== undefined;

// this is the same to the finalized state
const headBlock: ProtoBlock = {
slot: blockHeader.slot,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import {beforeAll, bench, describe} from "@chainsafe/benchmark";
import {BitArray, toHexString} from "@chainsafe/ssz";
import {createBeaconConfig, defaultChainConfig} from "@lodestar/config";
import {ExecutionStatus, ForkChoice, IForkChoiceStore, ProtoArray} from "@lodestar/fork-choice";
import {ExecutionStatus, ForkChoice, IForkChoiceStore, PayloadStatus, ProtoArray} from "@lodestar/fork-choice";
import {HISTORICAL_ROOTS_LIMIT, SLOTS_PER_EPOCH} from "@lodestar/params";
import {
CachedBeaconStateAltair,
Expand Down Expand Up @@ -116,16 +116,32 @@ describe(`getAttestationsForBlock vc=${vc}`, () => {
const fcStore: IForkChoiceStore = {
currentSlot: originalState.slot,
justified: {
checkpoint: {...justifiedCheckpoint, rootHex: toHexString(justifiedCheckpoint.root)},
checkpoint: {
...justifiedCheckpoint,
rootHex: toHexString(justifiedCheckpoint.root),
payloadStatus: PayloadStatus.FULL,
},
balances: originalState.epochCtx.effectiveBalanceIncrements,
totalBalance,
},
unrealizedJustified: {
checkpoint: {...justifiedCheckpoint, rootHex: toHexString(justifiedCheckpoint.root)},
checkpoint: {
...justifiedCheckpoint,
rootHex: toHexString(justifiedCheckpoint.root),
payloadStatus: PayloadStatus.FULL,
},
balances: originalState.epochCtx.effectiveBalanceIncrements,
},
finalizedCheckpoint: {...finalizedCheckpoint, rootHex: toHexString(finalizedCheckpoint.root)},
unrealizedFinalizedCheckpoint: {...finalizedCheckpoint, rootHex: toHexString(finalizedCheckpoint.root)},
finalizedCheckpoint: {
...finalizedCheckpoint,
rootHex: toHexString(finalizedCheckpoint.root),
payloadStatus: PayloadStatus.FULL,
},
unrealizedFinalizedCheckpoint: {
...finalizedCheckpoint,
rootHex: toHexString(finalizedCheckpoint.root),
payloadStatus: PayloadStatus.FULL,
},
justifiedBalancesGetter: () => originalState.epochCtx.effectiveBalanceIncrements,
equivocatingIndices: new Set(),
};
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import {beforeEach, describe, expect, it} from "vitest";
import {config} from "@lodestar/config/default";
import {IForkChoice, ProtoBlock} from "@lodestar/fork-choice";
import {IForkChoice, PayloadStatus, ProtoBlock} from "@lodestar/fork-choice";
import {computeStartSlotAtEpoch} from "@lodestar/state-transition";
import {SignedBeaconBlock, Slot, ssz} from "@lodestar/types";
import {toHex, toRootHex} from "@lodestar/utils";
Expand All @@ -25,7 +25,12 @@ describe("chain / blocks / verifyBlocksSanityChecks", () => {
block.message.slot = currentSlot;

forkChoice = getMockedBeaconChain().forkChoice;
forkChoice.getFinalizedCheckpoint.mockReturnValue({epoch: 0, root: Buffer.alloc(32), rootHex: ""});
forkChoice.getFinalizedCheckpoint.mockReturnValue({
epoch: 0,
root: Buffer.alloc(32),
rootHex: "",
payloadStatus: PayloadStatus.FULL,
});
clock = new ClockStopped(currentSlot);
modules = {config, forkChoice, clock, opts: {} as IChainOptions, blacklistedBlocks: new Map()};
// On first call, parentRoot is known
Expand All @@ -48,7 +53,12 @@ describe("chain / blocks / verifyBlocksSanityChecks", () => {
});

it("WOULD_REVERT_FINALIZED_SLOT", () => {
forkChoice.getFinalizedCheckpoint.mockReturnValue({epoch: 5, root: Buffer.alloc(32), rootHex: ""});
forkChoice.getFinalizedCheckpoint.mockReturnValue({
epoch: 5,
root: Buffer.alloc(32),
rootHex: "",
payloadStatus: PayloadStatus.FULL,
});
expectThrowsLodestarError(
() => verifyBlocksSanityChecks(modules, [block], {}),
BlockErrorCode.WOULD_REVERT_FINALIZED_SLOT
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import {generateKeyPair} from "@libp2p/crypto/keys";
import {beforeEach, describe, expect, it} from "vitest";
import {PayloadStatus} from "@lodestar/fork-choice";
import {ForkName, ForkPostFulu, ForkPreGloas} from "@lodestar/params";
import {signedBlockToSignedHeader} from "@lodestar/state-transition";
import {SignedBeaconBlock} from "@lodestar/types";
Expand Down Expand Up @@ -218,6 +219,7 @@ describe("SeenBlockInputCache", async () => {
epoch: config.DENEB_FORK_EPOCH,
root,
rootHex,
payloadStatus: PayloadStatus.FULL,
});
expect(cache.get(childRootHex)).toBeUndefined();
expect(cache.get(parentRootHex)).toBeUndefined();
Expand All @@ -228,6 +230,7 @@ describe("SeenBlockInputCache", async () => {
epoch: config.CAPELLA_FORK_EPOCH,
root,
rootHex,
payloadStatus: PayloadStatus.FULL,
});
expect(cache.get(childRootHex)).toBe(childBlockInput);
expect(cache.get(parentRootHex)).toBe(parentBlockInput);
Expand Down
16 changes: 13 additions & 3 deletions packages/beacon-node/test/unit/chain/validation/block.test.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import {Mock, Mocked, beforeEach, describe, it, vi} from "vitest";
import {createBeaconConfig, createChainForkConfig} from "@lodestar/config";
import {config as configDef} from "@lodestar/config/default";
import {ProtoBlock} from "@lodestar/fork-choice";
import {PayloadStatus, ProtoBlock} from "@lodestar/fork-choice";
import {ForkName, ForkPostDeneb, ForkPreFulu} from "@lodestar/params";
import {SignedBeaconBlock, ssz} from "@lodestar/types";
import {BlockErrorCode} from "../../../../src/chain/errors/index.js";
Expand Down Expand Up @@ -46,7 +46,12 @@ describe("gossip block validation", () => {

verifySignature = chain.bls.verifySignatureSets;
verifySignature.mockResolvedValue(true);
forkChoice.getFinalizedCheckpoint.mockReturnValue({epoch: 0, root: ZERO_HASH, rootHex: ""});
forkChoice.getFinalizedCheckpoint.mockReturnValue({
epoch: 0,
root: ZERO_HASH,
rootHex: "",
payloadStatus: PayloadStatus.FULL,
});

// Reset seen cache
(
Expand All @@ -70,7 +75,12 @@ describe("gossip block validation", () => {

it("WOULD_REVERT_FINALIZED_SLOT", async () => {
// Set finalized epoch to be greater than block's epoch
forkChoice.getFinalizedCheckpoint.mockReturnValue({epoch: Infinity, root: ZERO_HASH, rootHex: ""});
forkChoice.getFinalizedCheckpoint.mockReturnValue({
epoch: Infinity,
root: ZERO_HASH,
rootHex: "",
payloadStatus: PayloadStatus.FULL,
});

await expectRejectedWithLodestarError(
validateGossipBlock(config, chain, job, ForkName.phase0),
Expand Down
Loading
Loading