@lodestar/beacon-node 1.39.0-dev.ad23ef56aa → 1.39.0-dev.addea91de2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/api/impl/beacon/state/utils.d.ts +2 -7
- package/lib/api/impl/beacon/state/utils.d.ts.map +1 -1
- package/lib/api/impl/beacon/state/utils.js +0 -12
- package/lib/api/impl/beacon/state/utils.js.map +1 -1
- package/lib/api/impl/lodestar/index.js +1 -1
- package/lib/api/impl/lodestar/index.js.map +1 -1
- package/lib/api/impl/proof/index.d.ts.map +1 -1
- package/lib/api/impl/proof/index.js +1 -2
- package/lib/api/impl/proof/index.js.map +1 -1
- package/lib/api/impl/validator/index.d.ts.map +1 -1
- package/lib/api/impl/validator/index.js +1 -3
- package/lib/api/impl/validator/index.js.map +1 -1
- package/lib/chain/blocks/importBlock.d.ts.map +1 -1
- package/lib/chain/blocks/importBlock.js +8 -2
- package/lib/chain/blocks/importBlock.js.map +1 -1
- package/lib/chain/blocks/verifyBlocksStateTransitionOnly.d.ts.map +1 -1
- package/lib/chain/blocks/verifyBlocksStateTransitionOnly.js +1 -0
- package/lib/chain/blocks/verifyBlocksStateTransitionOnly.js.map +1 -1
- package/lib/chain/chain.d.ts +3 -2
- package/lib/chain/chain.d.ts.map +1 -1
- package/lib/chain/chain.js +16 -6
- package/lib/chain/chain.js.map +1 -1
- package/lib/chain/interface.d.ts +2 -2
- package/lib/chain/interface.d.ts.map +1 -1
- package/lib/chain/lightClient/proofs.d.ts.map +1 -1
- package/lib/chain/lightClient/proofs.js +0 -2
- package/lib/chain/lightClient/proofs.js.map +1 -1
- package/lib/chain/opPools/aggregatedAttestationPool.d.ts +2 -3
- package/lib/chain/opPools/aggregatedAttestationPool.d.ts.map +1 -1
- package/lib/chain/opPools/aggregatedAttestationPool.js +5 -130
- package/lib/chain/opPools/aggregatedAttestationPool.js.map +1 -1
- package/lib/chain/opPools/opPool.js +5 -8
- package/lib/chain/opPools/opPool.js.map +1 -1
- package/lib/chain/prepareNextSlot.d.ts.map +1 -1
- package/lib/chain/prepareNextSlot.js +4 -7
- package/lib/chain/prepareNextSlot.js.map +1 -1
- package/lib/chain/regen/interface.d.ts +1 -5
- package/lib/chain/regen/interface.d.ts.map +1 -1
- package/lib/chain/regen/queued.d.ts +4 -7
- package/lib/chain/regen/queued.d.ts.map +1 -1
- package/lib/chain/regen/queued.js +15 -25
- package/lib/chain/regen/queued.js.map +1 -1
- package/lib/chain/regen/regen.d.ts +1 -1
- package/lib/chain/regen/regen.d.ts.map +1 -1
- package/lib/chain/regen/regen.js +13 -17
- package/lib/chain/regen/regen.js.map +1 -1
- package/lib/chain/shufflingCache.d.ts +5 -12
- package/lib/chain/shufflingCache.d.ts.map +1 -1
- package/lib/chain/shufflingCache.js +12 -50
- package/lib/chain/shufflingCache.js.map +1 -1
- package/lib/chain/stateCache/blockStateCacheImpl.d.ts +1 -2
- package/lib/chain/stateCache/blockStateCacheImpl.d.ts.map +1 -1
- package/lib/chain/stateCache/blockStateCacheImpl.js +2 -2
- package/lib/chain/stateCache/blockStateCacheImpl.js.map +1 -1
- package/lib/chain/stateCache/fifoBlockStateCache.d.ts +1 -2
- package/lib/chain/stateCache/fifoBlockStateCache.d.ts.map +1 -1
- package/lib/chain/stateCache/fifoBlockStateCache.js +4 -4
- package/lib/chain/stateCache/fifoBlockStateCache.js.map +1 -1
- package/lib/chain/stateCache/inMemoryCheckpointsCache.d.ts +4 -5
- package/lib/chain/stateCache/inMemoryCheckpointsCache.d.ts.map +1 -1
- package/lib/chain/stateCache/inMemoryCheckpointsCache.js +9 -10
- package/lib/chain/stateCache/inMemoryCheckpointsCache.js.map +1 -1
- package/lib/chain/stateCache/persistentCheckpointsCache.d.ts +5 -6
- package/lib/chain/stateCache/persistentCheckpointsCache.d.ts.map +1 -1
- package/lib/chain/stateCache/persistentCheckpointsCache.js +17 -17
- package/lib/chain/stateCache/persistentCheckpointsCache.js.map +1 -1
- package/lib/chain/stateCache/types.d.ts +5 -6
- package/lib/chain/stateCache/types.d.ts.map +1 -1
- package/lib/chain/stateCache/types.js.map +1 -1
- package/lib/db/repositories/stateArchive.d.ts +1 -1
- package/lib/db/repositories/stateArchive.d.ts.map +1 -1
- package/lib/db/repositories/stateArchive.js +2 -2
- package/lib/db/repositories/stateArchive.js.map +1 -1
- package/lib/execution/engine/mock.d.ts +9 -6
- package/lib/execution/engine/mock.d.ts.map +1 -1
- package/lib/execution/engine/mock.js +34 -7
- package/lib/execution/engine/mock.js.map +1 -1
- package/lib/metrics/metrics/lodestar.d.ts +1 -6
- package/lib/metrics/metrics/lodestar.d.ts.map +1 -1
- package/lib/metrics/metrics/lodestar.js +3 -17
- package/lib/metrics/metrics/lodestar.js.map +1 -1
- package/lib/network/reqresp/utils/dataColumnResponseValidation.js +1 -1
- package/lib/network/reqresp/utils/dataColumnResponseValidation.js.map +1 -1
- package/lib/node/nodejs.d.ts.map +1 -1
- package/lib/node/nodejs.js +17 -2
- package/lib/node/nodejs.js.map +1 -1
- package/lib/util/sszBytes.js +1 -1
- package/lib/util/sszBytes.js.map +1 -1
- package/package.json +15 -15
- package/src/api/impl/beacon/state/utils.ts +2 -22
- package/src/api/impl/lodestar/index.ts +1 -1
- package/src/api/impl/proof/index.ts +1 -2
- package/src/api/impl/validator/index.ts +1 -3
- package/src/chain/blocks/importBlock.ts +9 -2
- package/src/chain/blocks/verifyBlocksStateTransitionOnly.ts +1 -0
- package/src/chain/chain.ts +20 -9
- package/src/chain/interface.ts +2 -2
- package/src/chain/lightClient/proofs.ts +0 -2
- package/src/chain/opPools/aggregatedAttestationPool.ts +6 -181
- package/src/chain/opPools/opPool.ts +5 -7
- package/src/chain/prepareNextSlot.ts +2 -6
- package/src/chain/regen/interface.ts +1 -5
- package/src/chain/regen/queued.ts +15 -34
- package/src/chain/regen/regen.ts +12 -18
- package/src/chain/shufflingCache.ts +15 -61
- package/src/chain/stateCache/blockStateCacheImpl.ts +2 -3
- package/src/chain/stateCache/fifoBlockStateCache.ts +4 -5
- package/src/chain/stateCache/inMemoryCheckpointsCache.ts +9 -15
- package/src/chain/stateCache/persistentCheckpointsCache.ts +17 -25
- package/src/chain/stateCache/types.ts +5 -10
- package/src/db/repositories/stateArchive.ts +2 -2
- package/src/execution/engine/mock.ts +40 -13
- package/src/metrics/metrics/lodestar.ts +3 -17
- package/src/network/reqresp/utils/dataColumnResponseValidation.ts +1 -1
- package/src/node/nodejs.ts +18 -3
- package/src/util/sszBytes.ts +1 -1
package/src/chain/chain.ts
CHANGED
|
@@ -52,6 +52,7 @@ import {computeNodeIdFromPrivateKey} from "../network/subnets/interface.js";
|
|
|
52
52
|
import {BufferPool} from "../util/bufferPool.js";
|
|
53
53
|
import {Clock, ClockEvent, IClock} from "../util/clock.js";
|
|
54
54
|
import {CustodyConfig, getValidatorsCustodyRequirement} from "../util/dataColumns.js";
|
|
55
|
+
import {callInNextEventLoop} from "../util/eventLoop.js";
|
|
55
56
|
import {ensureDir, writeIfNotExist} from "../util/file.js";
|
|
56
57
|
import {isOptimisticBlock} from "../util/forkChoice.js";
|
|
57
58
|
import {SerializedCache} from "../util/serializedCache.js";
|
|
@@ -291,7 +292,8 @@ export class BeaconChain implements IBeaconChain {
|
|
|
291
292
|
});
|
|
292
293
|
|
|
293
294
|
this._earliestAvailableSlot = anchorState.slot;
|
|
294
|
-
|
|
295
|
+
|
|
296
|
+
this.shufflingCache = new ShufflingCache(metrics, logger, this.opts, [
|
|
295
297
|
{
|
|
296
298
|
shuffling: anchorState.epochCtx.previousShuffling,
|
|
297
299
|
decisionRoot: anchorState.epochCtx.previousDecisionRoot,
|
|
@@ -417,6 +419,7 @@ export class BeaconChain implements IBeaconChain {
|
|
|
417
419
|
clock.addListener(ClockEvent.epoch, this.onClockEpoch.bind(this));
|
|
418
420
|
emitter.addListener(ChainEvent.forkChoiceFinalized, this.onForkChoiceFinalized.bind(this));
|
|
419
421
|
emitter.addListener(ChainEvent.forkChoiceJustified, this.onForkChoiceJustified.bind(this));
|
|
422
|
+
emitter.addListener(ChainEvent.checkpoint, this.onCheckpoint.bind(this));
|
|
420
423
|
}
|
|
421
424
|
|
|
422
425
|
async init(): Promise<void> {
|
|
@@ -504,7 +507,7 @@ export class BeaconChain implements IBeaconChain {
|
|
|
504
507
|
async getStateBySlot(
|
|
505
508
|
slot: Slot,
|
|
506
509
|
opts?: StateGetOpts
|
|
507
|
-
): Promise<{state:
|
|
510
|
+
): Promise<{state: CachedBeaconStateAllForks; executionOptimistic: boolean; finalized: boolean} | null> {
|
|
508
511
|
const finalizedBlock = this.forkChoice.getFinalizedBlock();
|
|
509
512
|
|
|
510
513
|
if (slot < finalizedBlock.slot) {
|
|
@@ -559,7 +562,7 @@ export class BeaconChain implements IBeaconChain {
|
|
|
559
562
|
async getStateByStateRoot(
|
|
560
563
|
stateRoot: RootHex,
|
|
561
564
|
opts?: StateGetOpts
|
|
562
|
-
): Promise<{state:
|
|
565
|
+
): Promise<{state: CachedBeaconStateAllForks | Uint8Array; executionOptimistic: boolean; finalized: boolean} | null> {
|
|
563
566
|
if (opts?.allowRegen) {
|
|
564
567
|
const state = await this.regen.getState(stateRoot, RegenCaller.restApi);
|
|
565
568
|
const block = this.forkChoice.getBlock(state.latestBlockHeader.hashTreeRoot());
|
|
@@ -587,7 +590,8 @@ export class BeaconChain implements IBeaconChain {
|
|
|
587
590
|
};
|
|
588
591
|
}
|
|
589
592
|
|
|
590
|
-
|
|
593
|
+
// this is mostly useful for a node with `--chain.archiveStateEpochFrequency 1`
|
|
594
|
+
const data = await this.db.stateArchive.getBinaryByRoot(fromHex(stateRoot));
|
|
591
595
|
return data && {state: data, executionOptimistic: false, finalized: true};
|
|
592
596
|
}
|
|
593
597
|
|
|
@@ -980,8 +984,8 @@ export class BeaconChain implements IBeaconChain {
|
|
|
980
984
|
this.metrics?.gossipAttestation.useHeadBlockState.inc({caller: regenCaller});
|
|
981
985
|
state = await this.regen.getState(attHeadBlock.stateRoot, regenCaller);
|
|
982
986
|
}
|
|
983
|
-
|
|
984
|
-
|
|
987
|
+
// resolve the promise to unblock other calls of the same epoch and dependent root
|
|
988
|
+
this.shufflingCache.processState(state);
|
|
985
989
|
return state.epochCtx.getShufflingAtEpoch(attEpoch);
|
|
986
990
|
}
|
|
987
991
|
|
|
@@ -1165,6 +1169,13 @@ export class BeaconChain implements IBeaconChain {
|
|
|
1165
1169
|
this.logger.verbose("Fork choice justified", {epoch: cp.epoch, root: cp.rootHex});
|
|
1166
1170
|
}
|
|
1167
1171
|
|
|
1172
|
+
private onCheckpoint(this: BeaconChain, _checkpoint: phase0.Checkpoint, state: CachedBeaconStateAllForks): void {
|
|
1173
|
+
// Defer to not block other checkpoint event handlers, which can cause lightclient update delays
|
|
1174
|
+
callInNextEventLoop(() => {
|
|
1175
|
+
this.shufflingCache.processState(state);
|
|
1176
|
+
});
|
|
1177
|
+
}
|
|
1178
|
+
|
|
1168
1179
|
private async onForkChoiceFinalized(this: BeaconChain, cp: CheckpointWithHex): Promise<void> {
|
|
1169
1180
|
this.logger.verbose("Fork choice finalized", {epoch: cp.epoch, root: cp.rootHex});
|
|
1170
1181
|
this.seenBlockProposers.prune(computeStartSlotAtEpoch(cp.epoch));
|
|
@@ -1295,9 +1306,9 @@ export class BeaconChain implements IBeaconChain {
|
|
|
1295
1306
|
|
|
1296
1307
|
preState = processSlots(preState, block.slot); // Dial preState's slot to block.slot
|
|
1297
1308
|
|
|
1298
|
-
const
|
|
1309
|
+
const proposerRewards = this.regen.getStateSync(toRootHex(block.stateRoot))?.proposerRewards ?? undefined;
|
|
1299
1310
|
|
|
1300
|
-
return computeBlockRewards(this.config, block, preState
|
|
1311
|
+
return computeBlockRewards(this.config, block, preState, proposerRewards);
|
|
1301
1312
|
}
|
|
1302
1313
|
|
|
1303
1314
|
async getAttestationsRewards(
|
|
@@ -1338,6 +1349,6 @@ export class BeaconChain implements IBeaconChain {
|
|
|
1338
1349
|
|
|
1339
1350
|
preState = processSlots(preState, block.slot); // Dial preState's slot to block.slot
|
|
1340
1351
|
|
|
1341
|
-
return computeSyncCommitteeRewards(this.config, this.index2pubkey, block, preState
|
|
1352
|
+
return computeSyncCommitteeRewards(this.config, this.index2pubkey, block, preState, validatorIds);
|
|
1342
1353
|
}
|
|
1343
1354
|
}
|
package/src/chain/interface.ts
CHANGED
|
@@ -168,12 +168,12 @@ export interface IBeaconChain {
|
|
|
168
168
|
getStateBySlot(
|
|
169
169
|
slot: Slot,
|
|
170
170
|
opts?: StateGetOpts
|
|
171
|
-
): Promise<{state:
|
|
171
|
+
): Promise<{state: CachedBeaconStateAllForks; executionOptimistic: boolean; finalized: boolean} | null>;
|
|
172
172
|
/** Returns a local state by state root */
|
|
173
173
|
getStateByStateRoot(
|
|
174
174
|
stateRoot: RootHex,
|
|
175
175
|
opts?: StateGetOpts
|
|
176
|
-
): Promise<{state:
|
|
176
|
+
): Promise<{state: CachedBeaconStateAllForks | Uint8Array; executionOptimistic: boolean; finalized: boolean} | null>;
|
|
177
177
|
/** Return serialized bytes of a persisted checkpoint state */
|
|
178
178
|
getPersistedCheckpointState(checkpoint?: phase0.Checkpoint): Promise<Uint8Array | null>;
|
|
179
179
|
/** Returns a cached state by checkpoint */
|
|
@@ -12,7 +12,6 @@ import {BeaconBlockBody, SSZTypesFor, ssz} from "@lodestar/types";
|
|
|
12
12
|
import {SyncCommitteeWitness} from "./types.js";
|
|
13
13
|
|
|
14
14
|
export function getSyncCommitteesWitness(fork: ForkName, state: BeaconStateAllForks): SyncCommitteeWitness {
|
|
15
|
-
state.commit();
|
|
16
15
|
const n1 = state.node;
|
|
17
16
|
let witness: Uint8Array[];
|
|
18
17
|
let currentSyncCommitteeRoot: Uint8Array;
|
|
@@ -71,7 +70,6 @@ export function getCurrentSyncCommitteeBranch(syncCommitteesWitness: SyncCommitt
|
|
|
71
70
|
}
|
|
72
71
|
|
|
73
72
|
export function getFinalizedRootProof(state: CachedBeaconStateAllForks): Uint8Array[] {
|
|
74
|
-
state.commit();
|
|
75
73
|
const finalizedRootGindex = state.epochCtx.isPostElectra() ? FINALIZED_ROOT_GINDEX_ELECTRA : FINALIZED_ROOT_GINDEX;
|
|
76
74
|
return new Tree(state.node).getSingleProof(BigInt(finalizedRootGindex));
|
|
77
75
|
}
|
|
@@ -5,7 +5,6 @@ import {IForkChoice} from "@lodestar/fork-choice";
|
|
|
5
5
|
import {
|
|
6
6
|
ForkName,
|
|
7
7
|
ForkSeq,
|
|
8
|
-
MAX_ATTESTATIONS,
|
|
9
8
|
MAX_ATTESTATIONS_ELECTRA,
|
|
10
9
|
MAX_COMMITTEES_PER_SLOT,
|
|
11
10
|
MIN_ATTESTATION_INCLUSION_DELAY,
|
|
@@ -23,7 +22,6 @@ import {
|
|
|
23
22
|
CachedBeaconStateAllForks,
|
|
24
23
|
CachedBeaconStateAltair,
|
|
25
24
|
CachedBeaconStateGloas,
|
|
26
|
-
CachedBeaconStatePhase0,
|
|
27
25
|
EffectiveBalanceIncrements,
|
|
28
26
|
RootCache,
|
|
29
27
|
computeEpochAtSlot,
|
|
@@ -32,17 +30,7 @@ import {
|
|
|
32
30
|
getAttestationParticipationStatus,
|
|
33
31
|
getBlockRootAtSlot,
|
|
34
32
|
} from "@lodestar/state-transition";
|
|
35
|
-
import {
|
|
36
|
-
Attestation,
|
|
37
|
-
Epoch,
|
|
38
|
-
RootHex,
|
|
39
|
-
Slot,
|
|
40
|
-
ValidatorIndex,
|
|
41
|
-
electra,
|
|
42
|
-
isElectraAttestation,
|
|
43
|
-
phase0,
|
|
44
|
-
ssz,
|
|
45
|
-
} from "@lodestar/types";
|
|
33
|
+
import {Attestation, Epoch, RootHex, Slot, electra, isElectraAttestation, phase0, ssz} from "@lodestar/types";
|
|
46
34
|
import {MapDef, assert, toRootHex} from "@lodestar/utils";
|
|
47
35
|
import {Metrics} from "../../metrics/metrics.js";
|
|
48
36
|
import {IntersectResult, intersectUint8Arrays} from "../../util/bitArray.js";
|
|
@@ -54,8 +42,6 @@ type DataRootHex = string;
|
|
|
54
42
|
|
|
55
43
|
type CommitteeIndex = number;
|
|
56
44
|
|
|
57
|
-
// for pre-electra
|
|
58
|
-
type AttestationWithScore = {attestation: Attestation; score: number};
|
|
59
45
|
/**
|
|
60
46
|
* for electra, this is to consolidate aggregated attestations of the same attestation data into a single attestation to be included in block
|
|
61
47
|
* note that this is local definition in this file and it's NOT validator consolidation
|
|
@@ -110,15 +96,6 @@ const MAX_RETAINED_ATTESTATIONS_PER_GROUP = 4;
|
|
|
110
96
|
*/
|
|
111
97
|
const MAX_RETAINED_ATTESTATIONS_PER_GROUP_ELECTRA = 8;
|
|
112
98
|
|
|
113
|
-
/**
|
|
114
|
-
* Pre-electra, each slot has 64 committees, and each block has 128 attestations max so in average
|
|
115
|
-
* we get 2 attestation per groups.
|
|
116
|
-
* Starting from Jan 2024, we have a performance issue getting attestations for a block. Based on the
|
|
117
|
-
* fact that lot of groups will have only 1 full participation attestation, increase this number
|
|
118
|
-
* a bit higher than average. This also help decrease number of slots to search for attestations.
|
|
119
|
-
*/
|
|
120
|
-
const MAX_ATTESTATIONS_PER_GROUP = 3;
|
|
121
|
-
|
|
122
99
|
/**
|
|
123
100
|
* For electra, there is on chain aggregation of attestations across committees, so we can just pick up to 8
|
|
124
101
|
* attestations per group, sort by scores to get first 8.
|
|
@@ -241,112 +218,11 @@ export class AggregatedAttestationPool {
|
|
|
241
218
|
* Get attestations to be included in a block pre-electra. Returns up to $MAX_ATTESTATIONS items
|
|
242
219
|
*/
|
|
243
220
|
getAttestationsForBlockPreElectra(
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
221
|
+
_fork: ForkName,
|
|
222
|
+
_forkChoice: IForkChoice,
|
|
223
|
+
_state: CachedBeaconStateAllForks
|
|
247
224
|
): phase0.Attestation[] {
|
|
248
|
-
|
|
249
|
-
const stateEpoch = state.epochCtx.epoch;
|
|
250
|
-
const statePrevEpoch = stateEpoch - 1;
|
|
251
|
-
|
|
252
|
-
const notSeenValidatorsFn = getNotSeenValidatorsFn(this.config, state);
|
|
253
|
-
const validateAttestationDataFn = getValidateAttestationDataFn(forkChoice, state);
|
|
254
|
-
|
|
255
|
-
const attestationsByScore: AttestationWithScore[] = [];
|
|
256
|
-
|
|
257
|
-
const slots = Array.from(this.attestationGroupByIndexByDataHexBySlot.keys()).sort((a, b) => b - a);
|
|
258
|
-
let minScore = Number.MAX_SAFE_INTEGER;
|
|
259
|
-
let slotCount = 0;
|
|
260
|
-
slot: for (const slot of slots) {
|
|
261
|
-
slotCount++;
|
|
262
|
-
const attestationGroupByIndexByDataHash = this.attestationGroupByIndexByDataHexBySlot.get(slot);
|
|
263
|
-
// should not happen
|
|
264
|
-
if (!attestationGroupByIndexByDataHash) {
|
|
265
|
-
throw Error(`No aggregated attestation pool for slot=${slot}`);
|
|
266
|
-
}
|
|
267
|
-
|
|
268
|
-
const epoch = computeEpochAtSlot(slot);
|
|
269
|
-
// validateAttestation condition: Attestation target epoch not in previous or current epoch
|
|
270
|
-
if (!(epoch === stateEpoch || epoch === statePrevEpoch)) {
|
|
271
|
-
continue; // Invalid attestations
|
|
272
|
-
}
|
|
273
|
-
// validateAttestation condition: Attestation slot not within inclusion window
|
|
274
|
-
if (
|
|
275
|
-
!(
|
|
276
|
-
slot + MIN_ATTESTATION_INCLUSION_DELAY <= stateSlot &&
|
|
277
|
-
// Post deneb, attestations are valid for current and previous epoch
|
|
278
|
-
(ForkSeq[fork] >= ForkSeq.deneb || stateSlot <= slot + SLOTS_PER_EPOCH)
|
|
279
|
-
)
|
|
280
|
-
) {
|
|
281
|
-
continue; // Invalid attestations
|
|
282
|
-
}
|
|
283
|
-
|
|
284
|
-
const inclusionDistance = stateSlot - slot;
|
|
285
|
-
for (const attestationGroupByIndex of attestationGroupByIndexByDataHash.values()) {
|
|
286
|
-
for (const [committeeIndex, attestationGroup] of attestationGroupByIndex.entries()) {
|
|
287
|
-
const notSeenCommitteeMembers = notSeenValidatorsFn(epoch, slot, committeeIndex);
|
|
288
|
-
if (notSeenCommitteeMembers === null || notSeenCommitteeMembers.size === 0) {
|
|
289
|
-
continue;
|
|
290
|
-
}
|
|
291
|
-
|
|
292
|
-
if (
|
|
293
|
-
slotCount > 2 &&
|
|
294
|
-
attestationsByScore.length >= MAX_ATTESTATIONS &&
|
|
295
|
-
notSeenCommitteeMembers.size / inclusionDistance < minScore
|
|
296
|
-
) {
|
|
297
|
-
// after 2 slots, there are a good chance that we have 2 * MAX_ATTESTATIONS attestations and break the for loop early
|
|
298
|
-
// if not, we may have to scan all slots in the pool
|
|
299
|
-
// if we have enough attestations and the max possible score is lower than scores of `attestationsByScore`, we should skip
|
|
300
|
-
// otherwise it takes time to check attestation, add it and remove it later after the sort by score
|
|
301
|
-
continue;
|
|
302
|
-
}
|
|
303
|
-
|
|
304
|
-
if (validateAttestationDataFn(attestationGroup.data) !== null) {
|
|
305
|
-
continue;
|
|
306
|
-
}
|
|
307
|
-
|
|
308
|
-
// TODO: Is it necessary to validateAttestation for:
|
|
309
|
-
// - Attestation committee index not within current committee count
|
|
310
|
-
// - Attestation aggregation bits length does not match committee length
|
|
311
|
-
//
|
|
312
|
-
// These properties should not change after being validate in gossip
|
|
313
|
-
// IF they have to be validated, do it only with one attestation per group since same data
|
|
314
|
-
// The committeeCountPerSlot can be precomputed once per slot
|
|
315
|
-
const getAttestationsResult = attestationGroup.getAttestationsForBlock(
|
|
316
|
-
fork,
|
|
317
|
-
state.epochCtx.effectiveBalanceIncrements,
|
|
318
|
-
notSeenCommitteeMembers,
|
|
319
|
-
MAX_ATTESTATIONS_PER_GROUP
|
|
320
|
-
);
|
|
321
|
-
for (const {attestation, newSeenEffectiveBalance} of getAttestationsResult.result) {
|
|
322
|
-
const score = newSeenEffectiveBalance / inclusionDistance;
|
|
323
|
-
if (score < minScore) {
|
|
324
|
-
minScore = score;
|
|
325
|
-
}
|
|
326
|
-
attestationsByScore.push({
|
|
327
|
-
attestation,
|
|
328
|
-
score,
|
|
329
|
-
});
|
|
330
|
-
}
|
|
331
|
-
|
|
332
|
-
// Stop accumulating attestations there are enough that may have good scoring
|
|
333
|
-
if (attestationsByScore.length >= MAX_ATTESTATIONS * 2) {
|
|
334
|
-
break slot;
|
|
335
|
-
}
|
|
336
|
-
}
|
|
337
|
-
}
|
|
338
|
-
}
|
|
339
|
-
|
|
340
|
-
const sortedAttestationsByScore = attestationsByScore.sort((a, b) => b.score - a.score);
|
|
341
|
-
const attestationsForBlock: phase0.Attestation[] = [];
|
|
342
|
-
for (const [i, attestationWithScore] of sortedAttestationsByScore.entries()) {
|
|
343
|
-
if (i >= MAX_ATTESTATIONS) {
|
|
344
|
-
break;
|
|
345
|
-
}
|
|
346
|
-
// attestations could be modified in this op pool, so we need to clone for block
|
|
347
|
-
attestationsForBlock.push(ssz.phase0.Attestation.clone(attestationWithScore.attestation));
|
|
348
|
-
}
|
|
349
|
-
return attestationsForBlock;
|
|
225
|
+
throw new Error("Does not support producing blocks for pre-electra forks anymore");
|
|
350
226
|
}
|
|
351
227
|
|
|
352
228
|
/**
|
|
@@ -867,38 +743,7 @@ export function aggregateConsolidation({byCommittee, attData}: AttestationsConso
|
|
|
867
743
|
export function getNotSeenValidatorsFn(config: BeaconConfig, state: CachedBeaconStateAllForks): GetNotSeenValidatorsFn {
|
|
868
744
|
const stateSlot = state.slot;
|
|
869
745
|
if (config.getForkName(stateSlot) === ForkName.phase0) {
|
|
870
|
-
|
|
871
|
-
// As we are close to altair, this is not really important, it's mainly for e2e.
|
|
872
|
-
// The performance is not great due to the different BeaconState data structure to altair.
|
|
873
|
-
// check for phase0 block already
|
|
874
|
-
const phase0State = state as CachedBeaconStatePhase0;
|
|
875
|
-
const stateEpoch = computeEpochAtSlot(stateSlot);
|
|
876
|
-
|
|
877
|
-
const previousEpochParticipants = extractParticipationPhase0(
|
|
878
|
-
phase0State.previousEpochAttestations.getAllReadonly(),
|
|
879
|
-
state
|
|
880
|
-
);
|
|
881
|
-
const currentEpochParticipants = extractParticipationPhase0(
|
|
882
|
-
phase0State.currentEpochAttestations.getAllReadonly(),
|
|
883
|
-
state
|
|
884
|
-
);
|
|
885
|
-
|
|
886
|
-
return (epoch: Epoch, slot: Slot, committeeIndex: number) => {
|
|
887
|
-
const participants =
|
|
888
|
-
epoch === stateEpoch ? currentEpochParticipants : epoch === stateEpoch - 1 ? previousEpochParticipants : null;
|
|
889
|
-
if (participants === null) {
|
|
890
|
-
return null;
|
|
891
|
-
}
|
|
892
|
-
const committee = state.epochCtx.getBeaconCommittee(slot, committeeIndex);
|
|
893
|
-
|
|
894
|
-
const notSeenCommitteeMembers = new Set<number>();
|
|
895
|
-
for (const [i, validatorIndex] of committee.entries()) {
|
|
896
|
-
if (!participants.has(validatorIndex)) {
|
|
897
|
-
notSeenCommitteeMembers.add(i);
|
|
898
|
-
}
|
|
899
|
-
}
|
|
900
|
-
return notSeenCommitteeMembers.size === 0 ? null : notSeenCommitteeMembers;
|
|
901
|
-
};
|
|
746
|
+
throw new Error("getNotSeenValidatorsFn is not supported phase0 state");
|
|
902
747
|
}
|
|
903
748
|
|
|
904
749
|
// altair and future forks
|
|
@@ -942,26 +787,6 @@ export function getNotSeenValidatorsFn(config: BeaconConfig, state: CachedBeacon
|
|
|
942
787
|
};
|
|
943
788
|
}
|
|
944
789
|
|
|
945
|
-
export function extractParticipationPhase0(
|
|
946
|
-
attestations: phase0.PendingAttestation[],
|
|
947
|
-
state: CachedBeaconStateAllForks
|
|
948
|
-
): Set<ValidatorIndex> {
|
|
949
|
-
const {epochCtx} = state;
|
|
950
|
-
const allParticipants = new Set<ValidatorIndex>();
|
|
951
|
-
for (const att of attestations) {
|
|
952
|
-
const aggregationBits = att.aggregationBits;
|
|
953
|
-
const attData = att.data;
|
|
954
|
-
const attSlot = attData.slot;
|
|
955
|
-
const committeeIndex = attData.index;
|
|
956
|
-
const committee = epochCtx.getBeaconCommittee(attSlot, committeeIndex);
|
|
957
|
-
const participants = aggregationBits.intersectValues(committee);
|
|
958
|
-
for (const participant of participants) {
|
|
959
|
-
allParticipants.add(participant);
|
|
960
|
-
}
|
|
961
|
-
}
|
|
962
|
-
return allParticipants;
|
|
963
|
-
}
|
|
964
|
-
|
|
965
790
|
/**
|
|
966
791
|
* This returns a function to validate if an attestation data is compatible to a state.
|
|
967
792
|
*
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import {BeaconConfig} from "@lodestar/config";
|
|
2
|
-
import {Id, Repository} from "@lodestar/db";
|
|
2
|
+
import {DbBatch, Id, Repository} from "@lodestar/db";
|
|
3
3
|
import {
|
|
4
4
|
BLS_WITHDRAWAL_PREFIX,
|
|
5
5
|
ForkName,
|
|
@@ -440,23 +440,21 @@ async function persistDiff<K extends Id, V>(
|
|
|
440
440
|
serializeKey: (key: K) => number | string
|
|
441
441
|
): Promise<void> {
|
|
442
442
|
const persistedKeys = await dbRepo.keys();
|
|
443
|
-
const
|
|
444
|
-
const keysToDelete: K[] = [];
|
|
443
|
+
const batch: DbBatch<K, V> = [];
|
|
445
444
|
|
|
446
445
|
const persistedKeysSerialized = new Set(persistedKeys.map(serializeKey));
|
|
447
446
|
for (const item of items) {
|
|
448
447
|
if (!persistedKeysSerialized.has(serializeKey(item.key))) {
|
|
449
|
-
|
|
448
|
+
batch.push({type: "put", key: item.key, value: item.value});
|
|
450
449
|
}
|
|
451
450
|
}
|
|
452
451
|
|
|
453
452
|
const targetKeysSerialized = new Set(items.map((item) => serializeKey(item.key)));
|
|
454
453
|
for (const persistedKey of persistedKeys) {
|
|
455
454
|
if (!targetKeysSerialized.has(serializeKey(persistedKey))) {
|
|
456
|
-
|
|
455
|
+
batch.push({type: "del", key: persistedKey});
|
|
457
456
|
}
|
|
458
457
|
}
|
|
459
458
|
|
|
460
|
-
if (
|
|
461
|
-
if (keysToDelete.length > 0) await dbRepo.batchDelete(keysToDelete);
|
|
459
|
+
if (batch.length > 0) await dbRepo.batch(batch);
|
|
462
460
|
}
|
|
@@ -117,12 +117,7 @@ export class PrepareNextSlotScheduler {
|
|
|
117
117
|
// the slot 0 of next epoch will likely use this Previous Root Checkpoint state for state transition so we transfer cache here
|
|
118
118
|
// the resulting state with cache will be cached in Checkpoint State Cache which is used for the upcoming block processing
|
|
119
119
|
// for other slots dontTransferCached=true because we don't run state transition on this state
|
|
120
|
-
|
|
121
|
-
// Shuffling calculation will be done asynchronously when passing asyncShufflingCalculation=true. Shuffling will be queued in
|
|
122
|
-
// beforeProcessEpoch and should theoretically be ready immediately after the synchronous epoch transition finished and the
|
|
123
|
-
// event loop is free. In long periods of non-finality too many forks will cause the shufflingCache to throw an error for
|
|
124
|
-
// too many queued shufflings so only run async during normal epoch transition. See issue ChainSafe/lodestar#7244
|
|
125
|
-
{dontTransferCache: !isEpochTransition, asyncShufflingCalculation: true},
|
|
120
|
+
{dontTransferCache: !isEpochTransition},
|
|
126
121
|
RegenCaller.precomputeEpoch
|
|
127
122
|
);
|
|
128
123
|
|
|
@@ -148,6 +143,7 @@ export class PrepareNextSlotScheduler {
|
|
|
148
143
|
updatedPrepareState = (await this.chain.regen.getBlockSlotState(
|
|
149
144
|
proposerHeadRoot,
|
|
150
145
|
prepareSlot,
|
|
146
|
+
// only transfer cache if epoch transition because that's the state we will use to stateTransition() the 1st block of epoch
|
|
151
147
|
{dontTransferCache: !isEpochTransition},
|
|
152
148
|
RegenCaller.predictProposerHead
|
|
153
149
|
)) as CachedBeaconStateExecutions;
|
|
@@ -31,10 +31,6 @@ export enum RegenFnName {
|
|
|
31
31
|
|
|
32
32
|
export type StateRegenerationOpts = {
|
|
33
33
|
dontTransferCache: boolean;
|
|
34
|
-
/**
|
|
35
|
-
* Do not queue shuffling calculation async. Forces sync JIT calculation in afterProcessEpoch if not passed as `true`
|
|
36
|
-
*/
|
|
37
|
-
asyncShufflingCalculation?: boolean;
|
|
38
34
|
};
|
|
39
35
|
|
|
40
36
|
export interface IStateRegenerator extends IStateRegeneratorInternal {
|
|
@@ -90,5 +86,5 @@ export interface IStateRegeneratorInternal {
|
|
|
90
86
|
/**
|
|
91
87
|
* Return the exact state with `stateRoot`
|
|
92
88
|
*/
|
|
93
|
-
getState(stateRoot: RootHex, rCaller: RegenCaller
|
|
89
|
+
getState(stateRoot: RootHex, rCaller: RegenCaller): Promise<CachedBeaconStateAllForks>;
|
|
94
90
|
}
|
|
@@ -79,21 +79,15 @@ export class QueuedStateRegenerator implements IStateRegenerator {
|
|
|
79
79
|
|
|
80
80
|
/**
|
|
81
81
|
* Get a state from block state cache.
|
|
82
|
-
* This is not for block processing so don't transfer cache
|
|
83
82
|
*/
|
|
84
83
|
getStateSync(stateRoot: RootHex): CachedBeaconStateAllForks | null {
|
|
85
|
-
return this.blockStateCache.get(stateRoot
|
|
84
|
+
return this.blockStateCache.get(stateRoot);
|
|
86
85
|
}
|
|
87
86
|
|
|
88
87
|
/**
|
|
89
88
|
* Get state for block processing.
|
|
90
|
-
* By default, do not transfer cache except for the block at clock slot
|
|
91
|
-
* which is usually the gossip block.
|
|
92
89
|
*/
|
|
93
|
-
getPreStateSync(
|
|
94
|
-
block: BeaconBlock,
|
|
95
|
-
opts: StateRegenerationOpts = {dontTransferCache: true}
|
|
96
|
-
): CachedBeaconStateAllForks | null {
|
|
90
|
+
getPreStateSync(block: BeaconBlock): CachedBeaconStateAllForks | null {
|
|
97
91
|
const parentRoot = toRootHex(block.parentRoot);
|
|
98
92
|
const parentBlock = this.forkChoice.getBlockHex(parentRoot);
|
|
99
93
|
if (!parentBlock) {
|
|
@@ -108,7 +102,7 @@ export class QueuedStateRegenerator implements IStateRegenerator {
|
|
|
108
102
|
|
|
109
103
|
// Check the checkpoint cache (if the pre-state is a checkpoint state)
|
|
110
104
|
if (parentEpoch < blockEpoch) {
|
|
111
|
-
const checkpointState = this.checkpointStateCache.getLatest(parentRoot, blockEpoch
|
|
105
|
+
const checkpointState = this.checkpointStateCache.getLatest(parentRoot, blockEpoch);
|
|
112
106
|
if (checkpointState && computeEpochAtSlot(checkpointState.slot) === blockEpoch) {
|
|
113
107
|
return checkpointState;
|
|
114
108
|
}
|
|
@@ -118,7 +112,7 @@ export class QueuedStateRegenerator implements IStateRegenerator {
|
|
|
118
112
|
// Otherwise the state transition may not be cached and wasted. Queue for regen since the
|
|
119
113
|
// work required will still be significant.
|
|
120
114
|
if (parentEpoch === blockEpoch) {
|
|
121
|
-
const state = this.blockStateCache.get(parentBlock.stateRoot
|
|
115
|
+
const state = this.blockStateCache.get(parentBlock.stateRoot);
|
|
122
116
|
if (state) {
|
|
123
117
|
return state;
|
|
124
118
|
}
|
|
@@ -132,21 +126,17 @@ export class QueuedStateRegenerator implements IStateRegenerator {
|
|
|
132
126
|
}
|
|
133
127
|
|
|
134
128
|
/**
|
|
135
|
-
* Get checkpoint state from cache
|
|
129
|
+
* Get checkpoint state from cache
|
|
136
130
|
*/
|
|
137
131
|
getCheckpointStateSync(cp: CheckpointHex): CachedBeaconStateAllForks | null {
|
|
138
|
-
return this.checkpointStateCache.get(cp
|
|
132
|
+
return this.checkpointStateCache.get(cp);
|
|
139
133
|
}
|
|
140
134
|
|
|
141
135
|
/**
|
|
142
|
-
* Get state closest to head
|
|
136
|
+
* Get state closest to head
|
|
143
137
|
*/
|
|
144
138
|
getClosestHeadState(head: ProtoBlock): CachedBeaconStateAllForks | null {
|
|
145
|
-
|
|
146
|
-
return (
|
|
147
|
-
this.checkpointStateCache.getLatest(head.blockRoot, Infinity, opts) ||
|
|
148
|
-
this.blockStateCache.get(head.stateRoot, opts)
|
|
149
|
-
);
|
|
139
|
+
return this.checkpointStateCache.getLatest(head.blockRoot, Infinity) || this.blockStateCache.get(head.stateRoot);
|
|
150
140
|
}
|
|
151
141
|
|
|
152
142
|
pruneOnCheckpoint(finalizedEpoch: Epoch, justifiedEpoch: Epoch, headStateRoot: RootHex): void {
|
|
@@ -181,10 +171,7 @@ export class QueuedStateRegenerator implements IStateRegenerator {
|
|
|
181
171
|
maybeHeadStateRoot,
|
|
182
172
|
};
|
|
183
173
|
const headState =
|
|
184
|
-
newHeadStateRoot === maybeHeadStateRoot
|
|
185
|
-
? maybeHeadState
|
|
186
|
-
: // maybeHeadState was already in block state cache so we don't transfer the cache
|
|
187
|
-
this.blockStateCache.get(newHeadStateRoot, {dontTransferCache: true});
|
|
174
|
+
newHeadStateRoot === maybeHeadStateRoot ? maybeHeadState : this.blockStateCache.get(newHeadStateRoot);
|
|
188
175
|
|
|
189
176
|
if (headState) {
|
|
190
177
|
this.blockStateCache.setHeadState(headState);
|
|
@@ -199,9 +186,7 @@ export class QueuedStateRegenerator implements IStateRegenerator {
|
|
|
199
186
|
|
|
200
187
|
// for the new FIFOBlockStateCache, it's important to reload state to regen head state here if needed
|
|
201
188
|
const allowDiskReload = true;
|
|
202
|
-
|
|
203
|
-
const cloneOpts = {dontTransferCache: false};
|
|
204
|
-
this.regen.getState(newHeadStateRoot, RegenCaller.processBlock, cloneOpts, allowDiskReload).then(
|
|
189
|
+
this.regen.getState(newHeadStateRoot, RegenCaller.processBlock, allowDiskReload).then(
|
|
205
190
|
(headStateRegen) => this.blockStateCache.setHeadState(headStateRegen),
|
|
206
191
|
(e) => this.logger.error("Error on head state regen", logCtx, e)
|
|
207
192
|
);
|
|
@@ -224,7 +209,7 @@ export class QueuedStateRegenerator implements IStateRegenerator {
|
|
|
224
209
|
this.metrics?.regenFnCallTotal.inc({caller: rCaller, entrypoint: RegenFnName.getPreState});
|
|
225
210
|
|
|
226
211
|
// First attempt to fetch the state from caches before queueing
|
|
227
|
-
const cachedState = this.getPreStateSync(block
|
|
212
|
+
const cachedState = this.getPreStateSync(block);
|
|
228
213
|
|
|
229
214
|
if (cachedState !== null) {
|
|
230
215
|
return cachedState;
|
|
@@ -243,7 +228,7 @@ export class QueuedStateRegenerator implements IStateRegenerator {
|
|
|
243
228
|
this.metrics?.regenFnCallTotal.inc({caller: rCaller, entrypoint: RegenFnName.getCheckpointState});
|
|
244
229
|
|
|
245
230
|
// First attempt to fetch the state from cache before queueing
|
|
246
|
-
const checkpointState = this.checkpointStateCache.get(toCheckpointHex(cp)
|
|
231
|
+
const checkpointState = this.checkpointStateCache.get(toCheckpointHex(cp));
|
|
247
232
|
if (checkpointState) {
|
|
248
233
|
return checkpointState;
|
|
249
234
|
}
|
|
@@ -271,22 +256,18 @@ export class QueuedStateRegenerator implements IStateRegenerator {
|
|
|
271
256
|
return this.jobQueue.push({key: "getBlockSlotState", args: [blockRoot, slot, opts, rCaller]});
|
|
272
257
|
}
|
|
273
258
|
|
|
274
|
-
async getState(
|
|
275
|
-
stateRoot: RootHex,
|
|
276
|
-
rCaller: RegenCaller,
|
|
277
|
-
opts: StateRegenerationOpts = {dontTransferCache: true}
|
|
278
|
-
): Promise<CachedBeaconStateAllForks> {
|
|
259
|
+
async getState(stateRoot: RootHex, rCaller: RegenCaller): Promise<CachedBeaconStateAllForks> {
|
|
279
260
|
this.metrics?.regenFnCallTotal.inc({caller: rCaller, entrypoint: RegenFnName.getState});
|
|
280
261
|
|
|
281
262
|
// First attempt to fetch the state from cache before queueing
|
|
282
|
-
const state = this.blockStateCache.get(stateRoot
|
|
263
|
+
const state = this.blockStateCache.get(stateRoot);
|
|
283
264
|
if (state) {
|
|
284
265
|
return state;
|
|
285
266
|
}
|
|
286
267
|
|
|
287
268
|
// The state is not immediately available in the cache, enqueue the job
|
|
288
269
|
this.metrics?.regenFnQueuedTotal.inc({caller: rCaller, entrypoint: RegenFnName.getState});
|
|
289
|
-
return this.jobQueue.push({key: "getState", args: [stateRoot, rCaller
|
|
270
|
+
return this.jobQueue.push({key: "getState", args: [stateRoot, rCaller]});
|
|
290
271
|
}
|
|
291
272
|
|
|
292
273
|
private jobQueueProcessor = async (regenRequest: RegenRequest): Promise<CachedBeaconStateAllForks> => {
|