diff --git a/packages/fork-choice/test/perf/forkChoice/fastConfirmation.test.ts b/packages/fork-choice/test/perf/forkChoice/fastConfirmation.test.ts index 4b89131f4718..fe6405bd9eff 100644 --- a/packages/fork-choice/test/perf/forkChoice/fastConfirmation.test.ts +++ b/packages/fork-choice/test/perf/forkChoice/fastConfirmation.test.ts @@ -1,6 +1,5 @@ import {bench, describe} from "@chainsafe/benchmark"; import {SLOTS_PER_EPOCH} from "@lodestar/params"; -import {computeEpochAtSlot} from "@lodestar/state-transition"; import {Slot} from "@lodestar/types"; import { buildFastConfirmationSnapshot, @@ -41,10 +40,8 @@ function runFCRRulesBenchmark(opts: Opts): void { const forkChoice = initializeForkChoice({...opts, fastConfirmation: true}); const head = forkChoice.updateHead(); - const prevBlock = forkChoice.getBlockHexDefaultStatus(head.parentRoot); - if (!prevBlock) throw Error("no prevBlock"); - // Vote everyone for head + // Vote everyone for head so the FCR vote-map paths see a populated voteNextIndices. everyoneVotes(head, forkChoice); // Advance to second slot of epoch 2 (not epoch boundary) @@ -60,25 +57,17 @@ function runFCRRulesBenchmark(opts: Opts): void { const store = forkChoice["fcStore"] as unknown as IFastConfirmationStore; if (!ctx) throw Error("fastConfirmationContext not initialized"); - return {forkChoice, ctx, store, head, prevBlock, currentVoteIs1: true, currentSlot}; - }, - beforeEach: (data) => { - // Flip votes to force fresh vote map computation each iteration - const target = data.currentVoteIs1 ? data.prevBlock : data.head; - everyoneVotes(target, data.forkChoice); - data.currentVoteIs1 = !data.currentVoteIs1; - data.currentSlot = (data.currentSlot + 1) as Slot; - - // Advance the slot (this calls updateHead + processAttestationQueue but NOT the FCR) - // We need to advance the slot so getCurrentSlot() returns the right value - data.forkChoice["fcStore"].currentSlot = data.currentSlot; - // Apply votes to proto array so getHead/isDescendant work correctly - data.forkChoice.updateHead(); - - return data; + return {ctx, store}; }, + // Pass-through: the bench harness invokes fn(beforeEach(inputAll)), so without a + // beforeEach fn would receive undefined. store is not mutated by the rule runner so + // no per-iteration reset is required. + beforeEach: (data) => data, fn: ({ctx, store}) => { - // Measure ONLY the FCR rules — build cache + snapshot + run rules + // Measure ONLY the FCR rules — build cache + snapshot + run rules. + // store is not mutated by runFastConfirmationRules (only the rule runner's caller + // would assign store.confirmedRoot), so each iteration replays the same work and + // no beforeEach reset is needed. const cache = createFastConfirmationCache(); const snapshot = buildFastConfirmationSnapshot(ctx, store, cache); runFastConfirmationRules(snapshot, ctx, store, cache); @@ -87,9 +76,11 @@ function runFCRRulesBenchmark(opts: Opts): void { } function everyoneVotes(vote: ProtoBlock, forkChoice: ForkChoice): void { - const nextEpoch = computeEpochAtSlot(vote.slot); const nextRoot = vote.blockRoot; for (let i = 0; i < forkChoice["balances"].length; i++) { - forkChoice["addLatestMessage"](i, nextEpoch, nextRoot, PayloadStatus.FULL); + // addLatestMessage's second arg is `nextSlot: Slot`, not an epoch — pass vote.slot directly so + // the internal computeEpochAtSlot(nextSlot) lands on the correct vote epoch instead of + // computeEpochAtSlot(epoch)=0 making every vote look 3 epochs stale. + forkChoice["addLatestMessage"](i, vote.slot, nextRoot, PayloadStatus.FULL); } }