@lodestar/beacon-node 1.39.0-dev.268dcb02bf → 1.39.0-dev.3932675174
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/chain/blocks/importBlock.d.ts.map +1 -1
- package/lib/chain/blocks/importBlock.js +6 -0
- package/lib/chain/blocks/importBlock.js.map +1 -1
- package/lib/chain/chain.d.ts +1 -0
- package/lib/chain/chain.d.ts.map +1 -1
- package/lib/chain/chain.js +11 -2
- package/lib/chain/chain.js.map +1 -1
- package/lib/chain/opPools/aggregatedAttestationPool.d.ts +1 -1
- package/lib/chain/opPools/aggregatedAttestationPool.d.ts.map +1 -1
- package/lib/chain/opPools/aggregatedAttestationPool.js +1 -1
- package/lib/chain/opPools/aggregatedAttestationPool.js.map +1 -1
- package/lib/chain/prepareNextSlot.d.ts.map +1 -1
- package/lib/chain/prepareNextSlot.js +1 -6
- package/lib/chain/prepareNextSlot.js.map +1 -1
- package/lib/chain/regen/interface.d.ts +0 -4
- package/lib/chain/regen/interface.d.ts.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/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/package.json +15 -15
- package/src/chain/blocks/importBlock.ts +7 -0
- package/src/chain/chain.ts +13 -3
- package/src/chain/opPools/aggregatedAttestationPool.ts +3 -3
- package/src/chain/prepareNextSlot.ts +1 -6
- package/src/chain/regen/interface.ts +0 -4
- package/src/chain/shufflingCache.ts +15 -61
- package/src/metrics/metrics/lodestar.ts +3 -17
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> {
|
|
@@ -980,8 +983,8 @@ export class BeaconChain implements IBeaconChain {
|
|
|
980
983
|
this.metrics?.gossipAttestation.useHeadBlockState.inc({caller: regenCaller});
|
|
981
984
|
state = await this.regen.getState(attHeadBlock.stateRoot, regenCaller);
|
|
982
985
|
}
|
|
983
|
-
|
|
984
|
-
|
|
986
|
+
// resolve the promise to unblock other calls of the same epoch and dependent root
|
|
987
|
+
this.shufflingCache.processState(state);
|
|
985
988
|
return state.epochCtx.getShufflingAtEpoch(attEpoch);
|
|
986
989
|
}
|
|
987
990
|
|
|
@@ -1165,6 +1168,13 @@ export class BeaconChain implements IBeaconChain {
|
|
|
1165
1168
|
this.logger.verbose("Fork choice justified", {epoch: cp.epoch, root: cp.rootHex});
|
|
1166
1169
|
}
|
|
1167
1170
|
|
|
1171
|
+
private onCheckpoint(this: BeaconChain, _checkpoint: phase0.Checkpoint, state: CachedBeaconStateAllForks): void {
|
|
1172
|
+
// Defer to not block other checkpoint event handlers, which can cause lightclient update delays
|
|
1173
|
+
callInNextEventLoop(() => {
|
|
1174
|
+
this.shufflingCache.processState(state);
|
|
1175
|
+
});
|
|
1176
|
+
}
|
|
1177
|
+
|
|
1168
1178
|
private async onForkChoiceFinalized(this: BeaconChain, cp: CheckpointWithHex): Promise<void> {
|
|
1169
1179
|
this.logger.verbose("Fork choice finalized", {epoch: cp.epoch, root: cp.rootHex});
|
|
1170
1180
|
this.seenBlockProposers.prune(computeStartSlotAtEpoch(cp.epoch));
|
|
@@ -218,9 +218,9 @@ export class AggregatedAttestationPool {
|
|
|
218
218
|
* Get attestations to be included in a block pre-electra. Returns up to $MAX_ATTESTATIONS items
|
|
219
219
|
*/
|
|
220
220
|
getAttestationsForBlockPreElectra(
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
221
|
+
_fork: ForkName,
|
|
222
|
+
_forkChoice: IForkChoice,
|
|
223
|
+
_state: CachedBeaconStateAllForks
|
|
224
224
|
): phase0.Attestation[] {
|
|
225
225
|
throw new Error("Does not support producing blocks for pre-electra forks anymore");
|
|
226
226
|
}
|
|
@@ -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
|
|
|
@@ -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 {
|
|
@@ -1,11 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
BeaconStateAllForks,
|
|
3
|
-
EpochShuffling,
|
|
4
|
-
IShufflingCache,
|
|
5
|
-
ShufflingBuildProps,
|
|
6
|
-
computeEpochShuffling,
|
|
7
|
-
computeEpochShufflingAsync,
|
|
8
|
-
} from "@lodestar/state-transition";
|
|
1
|
+
import {CachedBeaconStateAllForks, EpochShuffling} from "@lodestar/state-transition";
|
|
9
2
|
import {Epoch, RootHex} from "@lodestar/types";
|
|
10
3
|
import {LodestarError, Logger, MapDef, pruneSetToMax} from "@lodestar/utils";
|
|
11
4
|
import {Metrics} from "../metrics/metrics.js";
|
|
@@ -53,7 +46,7 @@ export type ShufflingCacheOpts = {
|
|
|
53
46
|
* - if a shuffling is not available (which does not happen with default chain option of maxSkipSlots = 32), track a promise to make sure we don't compute the same shuffling twice
|
|
54
47
|
* - skip computing shuffling when loading state bytes from disk
|
|
55
48
|
*/
|
|
56
|
-
export class ShufflingCache
|
|
49
|
+
export class ShufflingCache {
|
|
57
50
|
/** LRU cache implemented as a map, pruned every time we add an item */
|
|
58
51
|
private readonly itemsByDecisionRootByEpoch: MapDef<Epoch, Map<RootHex, CacheItem>> = new MapDef(
|
|
59
52
|
() => new Map<RootHex, CacheItem>()
|
|
@@ -136,60 +129,20 @@ export class ShufflingCache implements IShufflingCache {
|
|
|
136
129
|
}
|
|
137
130
|
|
|
138
131
|
/**
|
|
139
|
-
*
|
|
140
|
-
*
|
|
141
|
-
*
|
|
142
|
-
* NOTE: If a shuffling is already queued and not calculated it will build and resolve
|
|
143
|
-
* the promise but the already queued build will happen at some later time
|
|
132
|
+
* Process a state to extract and cache all shufflings (previous, current, next).
|
|
133
|
+
* Uses the stored decision roots from epochCtx.
|
|
144
134
|
*/
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
decisionRoot: RootHex,
|
|
148
|
-
buildProps?: T
|
|
149
|
-
): T extends ShufflingBuildProps ? EpochShuffling : EpochShuffling | null {
|
|
150
|
-
const cacheItem = this.itemsByDecisionRootByEpoch.getOrDefault(epoch).get(decisionRoot);
|
|
151
|
-
if (!cacheItem) {
|
|
152
|
-
this.metrics?.shufflingCache.miss.inc();
|
|
153
|
-
} else if (isShufflingCacheItem(cacheItem)) {
|
|
154
|
-
this.metrics?.shufflingCache.hit.inc();
|
|
155
|
-
return cacheItem.shuffling;
|
|
156
|
-
} else if (buildProps) {
|
|
157
|
-
// TODO: (@matthewkeil) This should possible log a warning??
|
|
158
|
-
this.metrics?.shufflingCache.shufflingPromiseNotResolvedAndThrownAway.inc();
|
|
159
|
-
} else {
|
|
160
|
-
this.metrics?.shufflingCache.shufflingPromiseNotResolved.inc();
|
|
161
|
-
}
|
|
135
|
+
processState(state: CachedBeaconStateAllForks): void {
|
|
136
|
+
const {epochCtx} = state;
|
|
162
137
|
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
const timer = this.metrics?.shufflingCache.shufflingCalculationTime.startTimer({source: "getSync"});
|
|
166
|
-
shuffling = computeEpochShuffling(buildProps.state, buildProps.activeIndices, epoch);
|
|
167
|
-
timer?.();
|
|
168
|
-
this.set(shuffling, decisionRoot);
|
|
169
|
-
}
|
|
170
|
-
return shuffling as T extends ShufflingBuildProps ? EpochShuffling : EpochShuffling | null;
|
|
171
|
-
}
|
|
138
|
+
// Cache previous shuffling
|
|
139
|
+
this.set(epochCtx.previousShuffling, epochCtx.previousDecisionRoot);
|
|
172
140
|
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
this.
|
|
178
|
-
/**
|
|
179
|
-
* TODO: (@matthewkeil) This will get replaced by a proper build queue and a worker to do calculations
|
|
180
|
-
* on a NICE thread
|
|
181
|
-
*/
|
|
182
|
-
const timer = this.metrics?.shufflingCache.shufflingCalculationTime.startTimer({source: "build"});
|
|
183
|
-
computeEpochShufflingAsync(state, activeIndices, epoch)
|
|
184
|
-
.then((shuffling) => {
|
|
185
|
-
this.set(shuffling, decisionRoot);
|
|
186
|
-
})
|
|
187
|
-
.catch((err) =>
|
|
188
|
-
this.logger?.error(`error building shuffling for epoch ${epoch} at decisionRoot ${decisionRoot}`, {}, err)
|
|
189
|
-
)
|
|
190
|
-
.finally(() => {
|
|
191
|
-
timer?.();
|
|
192
|
-
});
|
|
141
|
+
// Cache current shuffling
|
|
142
|
+
this.set(epochCtx.currentShuffling, epochCtx.currentDecisionRoot);
|
|
143
|
+
|
|
144
|
+
// Cache next shuffling
|
|
145
|
+
this.set(epochCtx.nextShuffling, epochCtx.nextDecisionRoot);
|
|
193
146
|
}
|
|
194
147
|
|
|
195
148
|
/**
|
|
@@ -207,7 +160,8 @@ export class ShufflingCache implements IShufflingCache {
|
|
|
207
160
|
(Date.now() - cacheItem.timeInsertedMs) / 1000
|
|
208
161
|
);
|
|
209
162
|
} else {
|
|
210
|
-
this.metrics?.shufflingCache.
|
|
163
|
+
this.metrics?.shufflingCache.shufflingSetMultipleTimes.inc();
|
|
164
|
+
return;
|
|
211
165
|
}
|
|
212
166
|
}
|
|
213
167
|
// set the shuffling
|
|
@@ -1308,33 +1308,19 @@ export function createLodestarMetrics(
|
|
|
1308
1308
|
name: "lodestar_shuffling_cache_miss_count",
|
|
1309
1309
|
help: "Count of shuffling cache miss",
|
|
1310
1310
|
}),
|
|
1311
|
-
|
|
1312
|
-
name: "
|
|
1313
|
-
help: "Count of shuffling that were
|
|
1314
|
-
}),
|
|
1315
|
-
shufflingPromiseNotResolvedAndThrownAway: register.gauge({
|
|
1316
|
-
name: "lodestar_shuffling_cache_promise_not_resolved_and_thrown_away_count",
|
|
1317
|
-
help: "Count of shuffling cache promises that were discarded and the shuffling was built synchronously",
|
|
1311
|
+
shufflingSetMultipleTimes: register.gauge({
|
|
1312
|
+
name: "lodestar_shuffling_cache_set_multiple_times_count",
|
|
1313
|
+
help: "Count of shuffling that were set multiple times",
|
|
1318
1314
|
}),
|
|
1319
1315
|
shufflingPromiseNotResolved: register.gauge({
|
|
1320
1316
|
name: "lodestar_shuffling_cache_promise_not_resolved_count",
|
|
1321
1317
|
help: "Count of shuffling cache promises that were requested before the promise was resolved",
|
|
1322
1318
|
}),
|
|
1323
|
-
nextShufflingNotOnEpochCache: register.gauge({
|
|
1324
|
-
name: "lodestar_shuffling_cache_next_shuffling_not_on_epoch_cache",
|
|
1325
|
-
help: "The next shuffling was not on the epoch cache before the epoch transition",
|
|
1326
|
-
}),
|
|
1327
1319
|
shufflingPromiseResolutionTime: register.histogram({
|
|
1328
1320
|
name: "lodestar_shuffling_cache_promise_resolution_time_seconds",
|
|
1329
1321
|
help: "Time from promise insertion until promise resolution when shuffling was ready in seconds",
|
|
1330
1322
|
buckets: [0.5, 1, 1.5, 2],
|
|
1331
1323
|
}),
|
|
1332
|
-
shufflingCalculationTime: register.histogram<{source: "build" | "getSync"}>({
|
|
1333
|
-
name: "lodestar_shuffling_cache_shuffling_calculation_time_seconds",
|
|
1334
|
-
help: "Run time of shuffling calculation",
|
|
1335
|
-
buckets: [0.5, 0.75, 1, 1.25, 1.5],
|
|
1336
|
-
labelNames: ["source"],
|
|
1337
|
-
}),
|
|
1338
1324
|
},
|
|
1339
1325
|
|
|
1340
1326
|
seenCache: {
|