Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: forkchoice filter change #6288

Merged
merged 6 commits into from
Jan 19, 2024
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
Original file line number Diff line number Diff line change
Expand Up @@ -103,11 +103,17 @@ describe("getAttestationsForBlock", () => {
);
}

let totalBalance = 0;
for (let i = 0; i < originalState.epochCtx.effectiveBalanceIncrements.length; i++) {
totalBalance += originalState.epochCtx.effectiveBalanceIncrements[i];
}

const fcStore: IForkChoiceStore = {
currentSlot: originalState.slot,
justified: {
checkpoint: {...justifiedCheckpoint, rootHex: toHexString(justifiedCheckpoint.root)},
balances: originalState.epochCtx.effectiveBalanceIncrements,
totalBalance,
},
unrealizedJustified: {
checkpoint: {...justifiedCheckpoint, rootHex: toHexString(justifiedCheckpoint.root)},
Expand Down
9 changes: 7 additions & 2 deletions packages/beacon-node/test/spec/presets/ssz_static.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,12 @@ type Types = Record<string, Type<any>>;
//

const sszStatic =
(skippedTypes?: string[]) =>
(skippedFork: string, skippedTypes?: string[]) =>
(fork: ForkName, typeName: string, testSuite: string, testSuiteDirpath: string): void => {
if (fork === skippedFork) {
return;
}

// Do not manually skip tests here, do it in packages/beacon-node/test/spec/presets/index.test.ts
if (skippedTypes?.includes(typeName)) {
return;
Expand Down Expand Up @@ -71,6 +75,7 @@ specTestIterator(path.join(ethereumConsensusSpecsTests.outputDir, "tests", ACTIV
// eslint-disable-next-line @typescript-eslint/naming-convention
ssz_static: {
type: RunnerType.custom,
fn: sszStatic(),
// starting from v1.4.0-beta.6, there is "whisk" fork in ssz_static tests but we ignore them
fn: sszStatic("whisk"),
},
});
2 changes: 1 addition & 1 deletion packages/beacon-node/test/spec/specTestVersioning.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import {DownloadTestsOptions} from "@lodestar/spec-test-util";
const __dirname = path.dirname(fileURLToPath(import.meta.url));

export const ethereumConsensusSpecsTests: DownloadTestsOptions = {
specVersion: "v1.4.0-beta.5",
specVersion: "v1.4.0-beta.6",
// Target directory is the host package root: 'packages/*/spec-tests'
outputDir: path.join(__dirname, "../../spec-tests"),
specTestsRepoUrl: "https://github.com/ethereum/consensus-spec-tests",
Expand Down
32 changes: 5 additions & 27 deletions packages/fork-choice/src/forkChoice/forkChoice.ts
Original file line number Diff line number Diff line change
Expand Up @@ -201,7 +201,7 @@ export class ForkChoice implements IForkChoice {
if (this.opts?.proposerBoostEnabled && this.proposerBoostRoot) {
const proposerBoostScore =
this.justifiedProposerBoostScore ??
computeProposerBoostScoreFromBalances(this.fcStore.justified.balances, {
getProposerScore(this.fcStore.justified.totalBalance, {
slotsPerEpoch: SLOTS_PER_EPOCH,
proposerScoreBoost: this.config.PROPOSER_SCORE_BOOST,
});
Expand Down Expand Up @@ -1260,32 +1260,10 @@ export function assertValidTerminalPowBlock(
}
}

function computeProposerBoostScore(
{
justifiedTotalActiveBalanceByIncrement,
justifiedActiveValidators,
}: {justifiedTotalActiveBalanceByIncrement: number; justifiedActiveValidators: number},
export function getProposerScore(
justifiedTotalActiveBalanceByIncrement: number,
config: {slotsPerEpoch: number; proposerScoreBoost: number}
): number {
const avgBalanceByIncrement = Math.floor(justifiedTotalActiveBalanceByIncrement / justifiedActiveValidators);
const committeeSize = Math.floor(justifiedActiveValidators / config.slotsPerEpoch);
const committeeWeight = committeeSize * avgBalanceByIncrement;
const proposerScore = Math.floor((committeeWeight * config.proposerScoreBoost) / 100);
return proposerScore;
}

export function computeProposerBoostScoreFromBalances(
justifiedBalances: EffectiveBalanceIncrements,
config: {slotsPerEpoch: number; proposerScoreBoost: number}
): number {
let justifiedTotalActiveBalanceByIncrement = 0,
justifiedActiveValidators = 0;
for (let i = 0; i < justifiedBalances.length; i++) {
if (justifiedBalances[i] > 0) {
justifiedActiveValidators += 1;
// justified balances here are by increment
justifiedTotalActiveBalanceByIncrement += justifiedBalances[i];
}
}
return computeProposerBoostScore({justifiedTotalActiveBalanceByIncrement, justifiedActiveValidators}, config);
const committeeWeight = Math.floor(justifiedTotalActiveBalanceByIncrement / config.slotsPerEpoch);
return Math.floor((committeeWeight * config.proposerScoreBoost) / 100);
}
4 changes: 4 additions & 0 deletions packages/fork-choice/src/forkChoice/interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,10 @@ export type CheckpointHexWithBalance = {
balances: EffectiveBalanceIncrements;
};

export type CheckpointHexWithTotalBalance = CheckpointHexWithBalance & {
totalBalance: number;
};

export enum EpochDifference {
current = 0,
previous = 1,
Expand Down
22 changes: 16 additions & 6 deletions packages/fork-choice/src/forkChoice/store.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import {toHexString} from "@chainsafe/ssz";
import {EffectiveBalanceIncrements, CachedBeaconStateAllForks} from "@lodestar/state-transition";
import {phase0, Slot, RootHex, ValidatorIndex} from "@lodestar/types";
import {CheckpointHexWithBalance} from "./interface.js";
import {CheckpointHexWithTotalBalance, CheckpointHexWithBalance} from "./interface.js";

/**
* Stores checkpoints in a hybrid format:
Expand Down Expand Up @@ -37,7 +37,8 @@ export type JustifiedBalancesGetter = (
*/
export interface IForkChoiceStore {
currentSlot: Slot;
justified: CheckpointHexWithBalance;
get justified(): CheckpointHexWithTotalBalance;
set justified(justified: CheckpointHexWithBalance);
unrealizedJustified: CheckpointHexWithBalance;
finalizedCheckpoint: CheckpointWithHex;
unrealizedFinalizedCheckpoint: CheckpointWithHex;
Expand All @@ -49,7 +50,7 @@ export interface IForkChoiceStore {
* IForkChoiceStore implementer which emits forkChoice events on updated justified and finalized checkpoints.
*/
export class ForkChoiceStore implements IForkChoiceStore {
private _justified: CheckpointHexWithBalance;
private _justified: CheckpointHexWithTotalBalance;
unrealizedJustified: CheckpointHexWithBalance;
private _finalizedCheckpoint: CheckpointWithHex;
unrealizedFinalizedCheckpoint: CheckpointWithHex;
Expand All @@ -66,21 +67,22 @@ export class ForkChoiceStore implements IForkChoiceStore {
onFinalized: (cp: CheckpointWithHex) => void;
}
) {
const justified: CheckpointHexWithBalance = {
const justified = {
checkpoint: toCheckpointWithHex(justifiedCheckpoint),
balances: justifiedBalances,
totalBalance: computeTotalBalance(justifiedBalances),
};
this._justified = justified;
this.unrealizedJustified = justified;
this._finalizedCheckpoint = toCheckpointWithHex(finalizedCheckpoint);
this.unrealizedFinalizedCheckpoint = this._finalizedCheckpoint;
}

get justified(): CheckpointHexWithBalance {
get justified(): CheckpointHexWithTotalBalance {
return this._justified;
}
set justified(justified: CheckpointHexWithBalance) {
this._justified = justified;
this._justified = {...justified, totalBalance: computeTotalBalance(justified.balances)};
this.events?.onJustified(justified.checkpoint);
}

Expand Down Expand Up @@ -108,3 +110,11 @@ export function toCheckpointWithHex(checkpoint: phase0.Checkpoint): CheckpointWi
export function equalCheckpointWithHex(a: CheckpointWithHex, b: CheckpointWithHex): boolean {
return a.epoch === b.epoch && a.rootHex === b.rootHex;
}

export function computeTotalBalance(balances: EffectiveBalanceIncrements): number {
let totalBalance = 0;
for (let i = 0; i < balances.length; i++) {
totalBalance += balances[i];
}
return totalBalance;
}
16 changes: 6 additions & 10 deletions packages/fork-choice/src/protoArray/protoArray.ts
Original file line number Diff line number Diff line change
Expand Up @@ -723,22 +723,18 @@ export class ProtoArray {
return false;
}
const currentEpoch = computeEpochAtSlot(currentSlot);
const previousEpoch = currentEpoch - 1;

// If block is from a previous epoch, filter using unrealized justification & finalization information
// If block is from the current epoch, filter using the head state's justification & finalization information
const isFromPrevEpoch = computeEpochAtSlot(node.slot) < currentEpoch;
const votingSourceEpoch = isFromPrevEpoch ? node.unrealizedJustifiedEpoch : node.justifiedEpoch;

// The voting source should be at the same height as the store's justified checkpoint
let correctJustified = votingSourceEpoch === this.justifiedEpoch || this.justifiedEpoch === 0;

// If this is a pulled-up block from the current epoch, also check that
// the unrealized justification is higher than the store's justified checkpoint, and
// the voting source is not more than two epochs ago.
if (!correctJustified && currentEpoch > GENESIS_EPOCH && this.justifiedEpoch === previousEpoch) {
correctJustified = node.unrealizedJustifiedEpoch >= previousEpoch && votingSourceEpoch + 2 >= currentEpoch;
}
// The voting source should be at the same height as the store's justified checkpoint or
// not more than two epochs ago
const correctJustified =
this.justifiedEpoch === GENESIS_EPOCH ||
votingSourceEpoch === this.justifiedEpoch ||
votingSourceEpoch + 2 >= currentEpoch;

const correctFinalized = this.finalizedEpoch === 0 || this.isFinalizedRootOrDescendant(node);
return correctJustified && correctFinalized;
Expand Down
2 changes: 2 additions & 0 deletions packages/fork-choice/test/perf/forkChoice/util.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import {fromHexString} from "@chainsafe/ssz";
import {config} from "@lodestar/config/default";
import {ExecutionStatus, ForkChoice, IForkChoiceStore, ProtoBlock, ProtoArray} from "../../../src/index.js";
import {computeTotalBalance} from "../../../src/forkChoice/store.js";

const genesisSlot = 0;
const genesisEpoch = 0;
Expand Down Expand Up @@ -39,6 +40,7 @@ export function initializeForkChoice(opts: Opts): ForkChoice {
justified: {
checkpoint: {epoch: genesisEpoch, root: fromHexString(genesisRoot), rootHex: genesisRoot},
balances,
totalBalance: computeTotalBalance(balances),
},
unrealizedJustified: {
checkpoint: {epoch: genesisEpoch, root: fromHexString(genesisRoot), rootHex: genesisRoot},
Expand Down
10 changes: 0 additions & 10 deletions packages/fork-choice/test/perf/protoArray/computeDeltas.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import {itBench, setBenchOpts} from "@dapplion/benchmark";
import {EffectiveBalanceIncrements, getEffectiveBalanceIncrementsZeroed} from "@lodestar/state-transition";
import {VoteTracker} from "../../../src/protoArray/interface.js";
import {computeDeltas} from "../../../src/protoArray/computeDeltas.js";
import {computeProposerBoostScoreFromBalances} from "../../../src/forkChoice/forkChoice.js";

describe("computeDeltas", () => {
let oldBalances: EffectiveBalanceIncrements;
Expand Down Expand Up @@ -51,13 +50,4 @@ describe("computeDeltas", () => {
});
}
}

for (const numValidator of numValidators) {
itBench({
id: `computeProposerBoostScoreFromBalances ${numValidator} validators`,
fn: () => {
computeProposerBoostScoreFromBalances(newBalances, {slotsPerEpoch: 32, proposerScoreBoost: 70});
},
});
}
});
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ describe("Forkchoice", function () {
justified: {
checkpoint: {epoch: genesisEpoch, root: fromHexString(finalizedRoot), rootHex: finalizedRoot},
balances: new Uint8Array([32]),
totalBalance: 32,
},
unrealizedJustified: {
checkpoint: {epoch: genesisEpoch, root: fromHexString(finalizedRoot), rootHex: finalizedRoot},
Expand Down
Loading