Skip to content

Commit b78cb92

Browse files
authored
feat: remove unfinalized pubkey cache (#7230)
* Remove unfinalized pubkey cache * lint * Fix unit test
1 parent 25c2ee5 commit b78cb92

File tree

15 files changed

+15
-514
lines changed

15 files changed

+15
-514
lines changed

packages/beacon-node/src/chain/chain.ts

-22
Original file line numberDiff line numberDiff line change
@@ -1047,9 +1047,6 @@ export class BeaconChain implements IBeaconChain {
10471047
metrics.forkChoice.balancesLength.set(forkChoiceMetrics.balancesLength);
10481048
metrics.forkChoice.nodes.set(forkChoiceMetrics.nodes);
10491049
metrics.forkChoice.indices.set(forkChoiceMetrics.indices);
1050-
1051-
const headState = this.getHeadState();
1052-
metrics.headState.unfinalizedPubkeyCacheSize.set(headState.epochCtx.unfinalizedPubkey2index.size);
10531050
}
10541051

10551052
private onClockSlot(slot: Slot): void {
@@ -1139,27 +1136,8 @@ export class BeaconChain implements IBeaconChain {
11391136
this.opPool.pruneAll(headBlock, headState);
11401137
}
11411138

1142-
const cpEpoch = cp.epoch;
1143-
11441139
if (headState === null) {
11451140
this.logger.verbose("Head state is null");
1146-
} else if (cpEpoch >= this.config.ELECTRA_FORK_EPOCH) {
1147-
// Get the validator.length from the state at cpEpoch
1148-
// We are confident the last element in the list is from headEpoch
1149-
// Thus we query from the end of the list. (cpEpoch - headEpoch - 1) is negative number
1150-
const pivotValidatorIndex = headState.epochCtx.getValidatorCountAtEpoch(cpEpoch);
1151-
1152-
if (pivotValidatorIndex !== undefined) {
1153-
// Note EIP-6914 will break this logic
1154-
const newFinalizedValidators = headState.epochCtx.unfinalizedPubkey2index.filter(
1155-
(index, _pubkey) => index < pivotValidatorIndex
1156-
);
1157-
1158-
// Populate finalized pubkey cache and remove unfinalized pubkey cache
1159-
if (!newFinalizedValidators.isEmpty()) {
1160-
this.regen.updateUnfinalizedPubkeys(newFinalizedValidators);
1161-
}
1162-
}
11631141
}
11641142

11651143
// TODO-Electra: Deprecating eth1Data poll requires a check on a finalized checkpoint state.

packages/beacon-node/src/chain/regen/queued.ts

+1-49
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import {routes} from "@lodestar/api";
22
import {IForkChoice, ProtoBlock} from "@lodestar/fork-choice";
3-
import {CachedBeaconStateAllForks, UnfinalizedPubkeyIndexMap, computeEpochAtSlot} from "@lodestar/state-transition";
3+
import {CachedBeaconStateAllForks, computeEpochAtSlot} from "@lodestar/state-transition";
44
import {BeaconBlock, Epoch, RootHex, Slot, phase0} from "@lodestar/types";
55
import {Logger, toRootHex} from "@lodestar/utils";
66
import {Metrics} from "../../metrics/index.js";
@@ -206,54 +206,6 @@ export class QueuedStateRegenerator implements IStateRegenerator {
206206
return this.checkpointStateCache.updatePreComputedCheckpoint(rootHex, epoch);
207207
}
208208

209-
/**
210-
* Remove `validators` from all unfinalized cache's epochCtx.UnfinalizedPubkey2Index,
211-
* and add them to epochCtx.pubkey2index and epochCtx.index2pubkey
212-
*/
213-
updateUnfinalizedPubkeys(validators: UnfinalizedPubkeyIndexMap): void {
214-
let numStatesUpdated = 0;
215-
const states = this.blockStateCache.getStates();
216-
const cpStates = this.checkpointStateCache.getStates();
217-
218-
// Add finalized pubkeys to all states.
219-
const addTimer = this.metrics?.regenFnAddPubkeyTime.startTimer();
220-
221-
// We only need to add pubkeys to any one of the states since the finalized caches is shared globally across all states
222-
const firstState = (states.next().value ?? cpStates.next().value) as CachedBeaconStateAllForks | undefined;
223-
224-
if (firstState !== undefined) {
225-
firstState.epochCtx.addFinalizedPubkeys(validators, this.metrics?.epochCache ?? undefined);
226-
} else {
227-
this.logger.warn("Attempt to delete finalized pubkey from unfinalized pubkey cache. But no state is available");
228-
}
229-
230-
addTimer?.();
231-
232-
// Delete finalized pubkeys from unfinalized pubkey cache for all states
233-
const deleteTimer = this.metrics?.regenFnDeletePubkeyTime.startTimer();
234-
const pubkeysToDelete = Array.from(validators.keys());
235-
236-
for (const s of states) {
237-
s.epochCtx.deleteUnfinalizedPubkeys(pubkeysToDelete);
238-
numStatesUpdated++;
239-
}
240-
241-
for (const s of cpStates) {
242-
s.epochCtx.deleteUnfinalizedPubkeys(pubkeysToDelete);
243-
numStatesUpdated++;
244-
}
245-
246-
// Since first state is consumed from the iterator. Will need to perform delete explicitly
247-
if (firstState !== undefined) {
248-
firstState?.epochCtx.deleteUnfinalizedPubkeys(pubkeysToDelete);
249-
numStatesUpdated++;
250-
}
251-
252-
deleteTimer?.();
253-
254-
this.metrics?.regenFnNumStatesUpdated.observe(numStatesUpdated);
255-
}
256-
257209
/**
258210
* Get the state to run with `block`.
259211
* - State after `block.parentRoot` dialed forward to block.slot

packages/beacon-node/src/chain/stateCache/blockStateCacheImpl.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ export class BlockStateCacheImpl implements BlockStateCache {
3434
this.maxStates = maxStates;
3535
this.cache = new MapTracker(metrics?.stateCache);
3636
if (metrics) {
37-
this.metrics = {...metrics.stateCache, ...metrics.epochCache};
37+
this.metrics = metrics.stateCache;
3838
metrics.stateCache.size.addCollect(() => metrics.stateCache.size.set(this.cache.size));
3939
}
4040
}

packages/beacon-node/src/metrics/metrics/beacon.ts

-7
Original file line numberDiff line numberDiff line change
@@ -124,13 +124,6 @@ export function createBeaconMetrics(register: RegistryMetricCreator) {
124124
}),
125125
},
126126

127-
headState: {
128-
unfinalizedPubkeyCacheSize: register.gauge({
129-
name: "beacon_head_state_unfinalized_pubkey_cache_size",
130-
help: "Current size of the unfinalizedPubkey2Index cache in the head state",
131-
}),
132-
},
133-
134127
parentBlockDistance: register.histogram({
135128
name: "beacon_imported_block_parent_distance",
136129
help: "Histogram of distance to parent block of valid imported blocks",

packages/beacon-node/src/metrics/metrics/lodestar.ts

-11
Original file line numberDiff line numberDiff line change
@@ -378,17 +378,6 @@ export function createLodestarMetrics(
378378
help: "Total count state.validators nodesPopulated is false on stfn for post state",
379379
}),
380380

381-
epochCache: {
382-
finalizedPubkeyDuplicateInsert: register.gauge({
383-
name: "lodestar_epoch_cache_finalized_pubkey_duplicate_insert_total",
384-
help: "Total count of duplicate insert of finalized pubkeys",
385-
}),
386-
newUnFinalizedPubkey: register.gauge({
387-
name: "lodestar_epoch_cache_new_unfinalized_pubkey_total",
388-
help: "Total count of unfinalized pubkeys added",
389-
}),
390-
},
391-
392381
// BLS verifier thread pool and queue
393382

394383
bls: {

packages/beacon-node/test/memory/unfinalizedPubkey2Index.ts

-54
This file was deleted.

packages/beacon-node/test/perf/chain/stateCache/updateUnfinalizedPubkeys.test.ts

-114
This file was deleted.

packages/beacon-node/test/sim/electra-interop.test.ts

+3-28
Original file line numberDiff line numberDiff line change
@@ -375,17 +375,8 @@ describe("executionEngine / ExecutionEngineHttp", () => {
375375
if (headState.validators.length !== 33 || headState.balances.length !== 33) {
376376
throw Error("New validator is not reflected in the beacon state at slot 5");
377377
}
378-
if (epochCtx.index2pubkey.length !== 32 || epochCtx.pubkey2index.size !== 32) {
379-
throw Error("Finalized cache is modified.");
380-
}
381-
if (epochCtx.unfinalizedPubkey2index.size !== 1) {
382-
throw Error(
383-
`Unfinalized cache is missing the expected validator. Size: ${epochCtx.unfinalizedPubkey2index.size}`
384-
);
385-
}
386-
// validator count at epoch 1 should be empty at this point since no epoch transition has happened.
387-
if (epochCtx.getValidatorCountAtEpoch(1) !== undefined) {
388-
throw Error("Historical validator lengths is modified");
378+
if (epochCtx.index2pubkey.length !== 33 || epochCtx.pubkey2index.size !== 33) {
379+
throw Error("Pubkey cache is not updated");
389380
}
390381

391382
await new Promise<void>((resolve, _reject) => {
@@ -412,23 +403,7 @@ describe("executionEngine / ExecutionEngineHttp", () => {
412403
throw Error("New validator is not reflected in the beacon state.");
413404
}
414405
if (epochCtx.index2pubkey.length !== 33 || epochCtx.pubkey2index.size !== 33) {
415-
throw Error("New validator is not in finalized cache");
416-
}
417-
if (!epochCtx.unfinalizedPubkey2index.isEmpty()) {
418-
throw Error("Unfinalized cache still contains new validator");
419-
}
420-
// After 4 epochs, headState's finalized cp epoch should be 2
421-
// epochCtx should only have validator count for epoch 3 and 4.
422-
if (epochCtx.getValidatorCountAtEpoch(4) === undefined || epochCtx.getValidatorCountAtEpoch(3) === undefined) {
423-
throw Error("Missing historical validator length for epoch 3 or 4");
424-
}
425-
426-
if (epochCtx.getValidatorCountAtEpoch(4) !== 33 || epochCtx.getValidatorCountAtEpoch(3) !== 33) {
427-
throw Error("Incorrect historical validator length for epoch 3 or 4");
428-
}
429-
430-
if (epochCtx.getValidatorCountAtEpoch(2) !== undefined || epochCtx.getValidatorCountAtEpoch(1) !== undefined) {
431-
throw Error("Historical validator length for epoch 1 or 2 is not dropped properly");
406+
throw Error("New validator is not in pubkey cache");
432407
}
433408

434409
if (headState.depositRequestsStartIndex === UNSET_DEPOSIT_REQUESTS_START_INDEX) {

packages/state-transition/package.json

+1-2
Original file line numberDiff line numberDiff line change
@@ -69,8 +69,7 @@
6969
"@lodestar/params": "^1.23.0",
7070
"@lodestar/types": "^1.23.0",
7171
"@lodestar/utils": "^1.23.0",
72-
"bigint-buffer": "^1.1.5",
73-
"immutable": "^4.3.2"
72+
"bigint-buffer": "^1.1.5"
7473
},
7574
"keywords": [
7675
"ethereum",

0 commit comments

Comments
 (0)