@lodestar/beacon-node 1.23.0-dev.86a8df7fb2 → 1.23.0-dev.94b6c5b0db
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/index.js +5 -1
- package/lib/api/impl/beacon/state/index.js.map +1 -1
- package/lib/api/impl/config/constants.d.ts +0 -1
- package/lib/api/impl/config/constants.js +1 -2
- package/lib/api/impl/config/constants.js.map +1 -1
- package/lib/api/impl/validator/index.js +8 -3
- package/lib/api/impl/validator/index.js.map +1 -1
- package/lib/chain/archiver/archiveStates.d.ts +3 -2
- package/lib/chain/archiver/archiveStates.js +8 -4
- package/lib/chain/archiver/archiveStates.js.map +1 -1
- package/lib/chain/archiver/index.d.ts +3 -1
- package/lib/chain/archiver/index.js +3 -2
- package/lib/chain/archiver/index.js.map +1 -1
- package/lib/chain/blocks/importBlock.js +0 -6
- package/lib/chain/blocks/importBlock.js.map +1 -1
- package/lib/chain/blocks/verifyBlocksExecutionPayloads.js +2 -1
- package/lib/chain/blocks/verifyBlocksExecutionPayloads.js.map +1 -1
- package/lib/chain/chain.d.ts +0 -40
- package/lib/chain/chain.js +18 -9
- package/lib/chain/chain.js.map +1 -1
- package/lib/chain/forkChoice/index.js +1 -2
- package/lib/chain/forkChoice/index.js.map +1 -1
- package/lib/chain/initState.d.ts +1 -5
- package/lib/chain/initState.js +2 -30
- package/lib/chain/initState.js.map +1 -1
- package/lib/chain/lightClient/proofs.d.ts +2 -2
- package/lib/chain/lightClient/proofs.js +3 -2
- package/lib/chain/lightClient/proofs.js.map +1 -1
- package/lib/chain/produceBlock/produceBlockBody.js +7 -1
- package/lib/chain/produceBlock/produceBlockBody.js.map +1 -1
- package/lib/chain/serializeState.js +63 -10
- package/lib/chain/serializeState.js.map +1 -1
- package/lib/chain/shufflingCache.d.ts +37 -13
- package/lib/chain/shufflingCache.js +85 -76
- package/lib/chain/shufflingCache.js.map +1 -1
- package/lib/chain/stateCache/persistentCheckpointsCache.js +22 -26
- package/lib/chain/stateCache/persistentCheckpointsCache.js.map +1 -1
- package/lib/db/repositories/lightclientCheckpointHeader.d.ts +1 -0
- package/lib/db/repositories/lightclientCheckpointHeader.js +3 -0
- package/lib/db/repositories/lightclientCheckpointHeader.js.map +1 -1
- package/lib/execution/engine/http.d.ts +3 -2
- package/lib/execution/engine/http.js +27 -10
- package/lib/execution/engine/http.js.map +1 -1
- package/lib/execution/engine/interface.d.ts +5 -4
- package/lib/execution/engine/interface.js.map +1 -1
- package/lib/execution/engine/mock.js +0 -2
- package/lib/execution/engine/mock.js.map +1 -1
- package/lib/execution/engine/payloadIdCache.d.ts +0 -17
- package/lib/execution/engine/payloadIdCache.js.map +1 -1
- package/lib/execution/engine/types.d.ts +29 -25
- package/lib/execution/engine/types.js +28 -42
- package/lib/execution/engine/types.js.map +1 -1
- package/lib/metrics/metrics/beacon.js +1 -1
- package/lib/metrics/metrics/beacon.js.map +1 -1
- package/lib/metrics/metrics/lodestar.d.ts +13 -4
- package/lib/metrics/metrics/lodestar.js +43 -19
- package/lib/metrics/metrics/lodestar.js.map +1 -1
- package/lib/network/gossip/topic.d.ts +0 -27
- package/lib/network/reqresp/types.d.ts +5 -5
- package/lib/network/reqresp/types.js +1 -1
- package/lib/network/reqresp/types.js.map +1 -1
- package/lib/node/utils/interop/state.js +1 -0
- package/lib/node/utils/interop/state.js.map +1 -1
- package/lib/node/utils/state.d.ts +3 -0
- package/lib/node/utils/state.js +3 -0
- package/lib/node/utils/state.js.map +1 -1
- package/lib/sync/backfill/backfill.js +1 -2
- package/lib/sync/backfill/backfill.js.map +1 -1
- package/package.json +19 -19
|
@@ -1,3 +1,48 @@
|
|
|
1
|
+
var __addDisposableResource = (this && this.__addDisposableResource) || function (env, value, async) {
|
|
2
|
+
if (value !== null && value !== void 0) {
|
|
3
|
+
if (typeof value !== "object" && typeof value !== "function") throw new TypeError("Object expected.");
|
|
4
|
+
var dispose;
|
|
5
|
+
if (async) {
|
|
6
|
+
if (!Symbol.asyncDispose) throw new TypeError("Symbol.asyncDispose is not defined.");
|
|
7
|
+
dispose = value[Symbol.asyncDispose];
|
|
8
|
+
}
|
|
9
|
+
if (dispose === void 0) {
|
|
10
|
+
if (!Symbol.dispose) throw new TypeError("Symbol.dispose is not defined.");
|
|
11
|
+
dispose = value[Symbol.dispose];
|
|
12
|
+
}
|
|
13
|
+
if (typeof dispose !== "function") throw new TypeError("Object not disposable.");
|
|
14
|
+
env.stack.push({ value: value, dispose: dispose, async: async });
|
|
15
|
+
}
|
|
16
|
+
else if (async) {
|
|
17
|
+
env.stack.push({ async: true });
|
|
18
|
+
}
|
|
19
|
+
return value;
|
|
20
|
+
};
|
|
21
|
+
var __disposeResources = (this && this.__disposeResources) || (function (SuppressedError) {
|
|
22
|
+
return function (env) {
|
|
23
|
+
function fail(e) {
|
|
24
|
+
env.error = env.hasError ? new SuppressedError(e, env.error, "An error was suppressed during disposal.") : e;
|
|
25
|
+
env.hasError = true;
|
|
26
|
+
}
|
|
27
|
+
function next() {
|
|
28
|
+
while (env.stack.length) {
|
|
29
|
+
var rec = env.stack.pop();
|
|
30
|
+
try {
|
|
31
|
+
var result = rec.dispose && rec.dispose.call(rec.value);
|
|
32
|
+
if (rec.async) return Promise.resolve(result).then(next, function(e) { fail(e); return next(); });
|
|
33
|
+
}
|
|
34
|
+
catch (e) {
|
|
35
|
+
fail(e);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
if (env.hasError) throw env.error;
|
|
39
|
+
}
|
|
40
|
+
return next();
|
|
41
|
+
};
|
|
42
|
+
})(typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) {
|
|
43
|
+
var e = new Error(message);
|
|
44
|
+
return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
|
|
45
|
+
});
|
|
1
46
|
/*
|
|
2
47
|
* Serialize state using the BufferPool if provided.
|
|
3
48
|
*/
|
|
@@ -5,18 +50,26 @@ export async function serializeState(state, source, processFn, bufferPool) {
|
|
|
5
50
|
const size = state.type.tree_serializedSize(state.node);
|
|
6
51
|
let stateBytes = null;
|
|
7
52
|
if (bufferPool) {
|
|
8
|
-
const
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
53
|
+
const env_1 = { stack: [], error: void 0, hasError: false };
|
|
54
|
+
try {
|
|
55
|
+
const bufferWithKey = __addDisposableResource(env_1, bufferPool.alloc(size, source), false);
|
|
56
|
+
if (bufferWithKey) {
|
|
57
|
+
stateBytes = bufferWithKey.buffer;
|
|
58
|
+
const dataView = new DataView(stateBytes.buffer, stateBytes.byteOffset, stateBytes.byteLength);
|
|
59
|
+
state.serializeToBytes({ uint8Array: stateBytes, dataView }, 0);
|
|
60
|
+
return processFn(stateBytes);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
catch (e_1) {
|
|
64
|
+
env_1.error = e_1;
|
|
65
|
+
env_1.hasError = true;
|
|
66
|
+
}
|
|
67
|
+
finally {
|
|
68
|
+
__disposeResources(env_1);
|
|
13
69
|
}
|
|
14
70
|
}
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
stateBytes = state.serialize();
|
|
18
|
-
}
|
|
71
|
+
// we already have metrics in BufferPool so no need to do it here
|
|
72
|
+
stateBytes = state.serialize();
|
|
19
73
|
return processFn(stateBytes);
|
|
20
|
-
// release the buffer back to the pool automatically
|
|
21
74
|
}
|
|
22
75
|
//# sourceMappingURL=serializeState.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"serializeState.js","sourceRoot":"","sources":["../../src/chain/serializeState.ts"],"names":[],"mappings":"AAKA;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,cAAc,CAClC,KAAgC,EAChC,MAAmB,EACnB,SAAiC,EACjC,UAA8B;IAE9B,MAAM,IAAI,GAAG,KAAK,CAAC,IAAI,CAAC,mBAAmB,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IACxD,IAAI,UAAU,GAAsB,IAAI,CAAC;IACzC,IAAI,UAAU,EAAE,CAAC
|
|
1
|
+
{"version":3,"file":"serializeState.js","sourceRoot":"","sources":["../../src/chain/serializeState.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAKA;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,cAAc,CAClC,KAAgC,EAChC,MAAmB,EACnB,SAAiC,EACjC,UAA8B;IAE9B,MAAM,IAAI,GAAG,KAAK,CAAC,IAAI,CAAC,mBAAmB,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IACxD,IAAI,UAAU,GAAsB,IAAI,CAAC;IACzC,IAAI,UAAU,EAAE,CAAC;;;YACf,MAAM,aAAa,kCAAG,UAAU,CAAC,KAAK,CAAC,IAAI,EAAE,MAAM,CAAC,QAAA,CAAC;YACrD,IAAI,aAAa,EAAE,CAAC;gBAClB,UAAU,GAAG,aAAa,CAAC,MAAM,CAAC;gBAClC,MAAM,QAAQ,GAAG,IAAI,QAAQ,CAAC,UAAU,CAAC,MAAM,EAAE,UAAU,CAAC,UAAU,EAAE,UAAU,CAAC,UAAU,CAAC,CAAC;gBAC/F,KAAK,CAAC,gBAAgB,CAAC,EAAC,UAAU,EAAE,UAAU,EAAE,QAAQ,EAAC,EAAE,CAAC,CAAC,CAAC;gBAC9D,OAAO,SAAS,CAAC,UAAU,CAAC,CAAC;YAC/B,CAAC;;;;;;;;;KAEF;IAED,iEAAiE;IACjE,UAAU,GAAG,KAAK,CAAC,SAAS,EAAE,CAAC;IAE/B,OAAO,SAAS,CAAC,UAAU,CAAC,CAAC;AAC/B,CAAC"}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { BeaconStateAllForks, EpochShuffling, IShufflingCache, ShufflingBuildProps } from "@lodestar/state-transition";
|
|
2
2
|
import { Epoch, RootHex } from "@lodestar/types";
|
|
3
|
+
import { LodestarError, Logger } from "@lodestar/utils";
|
|
3
4
|
import { Metrics } from "../metrics/metrics.js";
|
|
4
5
|
export type ShufflingCacheOpts = {
|
|
5
6
|
maxShufflingCacheEpochs?: number;
|
|
@@ -10,31 +11,54 @@ export type ShufflingCacheOpts = {
|
|
|
10
11
|
* - 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
|
|
11
12
|
* - skip computing shuffling when loading state bytes from disk
|
|
12
13
|
*/
|
|
13
|
-
export declare class ShufflingCache {
|
|
14
|
-
|
|
14
|
+
export declare class ShufflingCache implements IShufflingCache {
|
|
15
|
+
readonly metrics: Metrics | null;
|
|
16
|
+
readonly logger: Logger | null;
|
|
15
17
|
/** LRU cache implemented as a map, pruned every time we add an item */
|
|
16
18
|
private readonly itemsByDecisionRootByEpoch;
|
|
17
19
|
private readonly maxEpochs;
|
|
18
|
-
constructor(metrics?: Metrics | null, opts?: ShufflingCacheOpts
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
processState(state: CachedBeaconStateAllForks, shufflingEpoch: Epoch): EpochShuffling;
|
|
20
|
+
constructor(metrics?: Metrics | null, logger?: Logger | null, opts?: ShufflingCacheOpts, precalculatedShufflings?: {
|
|
21
|
+
shuffling: EpochShuffling | null;
|
|
22
|
+
decisionRoot: RootHex;
|
|
23
|
+
}[]);
|
|
23
24
|
/**
|
|
24
25
|
* Insert a promise to make sure we don't regen state for the same shuffling.
|
|
25
26
|
* Bound by MAX_SHUFFLING_PROMISE to make sure our node does not blow up.
|
|
26
27
|
*/
|
|
27
|
-
insertPromise(
|
|
28
|
+
insertPromise(epoch: Epoch, decisionRoot: RootHex): void;
|
|
28
29
|
/**
|
|
29
30
|
* Most of the time, this should return a shuffling immediately.
|
|
30
31
|
* If there's a promise, it means we are computing the same shuffling, so we wait for the promise to resolve.
|
|
31
32
|
* Return null if we don't have a shuffling for this epoch and dependentRootHex.
|
|
32
33
|
*/
|
|
33
|
-
get(
|
|
34
|
+
get(epoch: Epoch, decisionRoot: RootHex): Promise<EpochShuffling | null>;
|
|
34
35
|
/**
|
|
35
|
-
*
|
|
36
|
+
* Gets a cached shuffling via the epoch and decision root. If the shuffling is not
|
|
37
|
+
* available it will build it synchronously and return the shuffling.
|
|
38
|
+
*
|
|
39
|
+
* NOTE: If a shuffling is already queued and not calculated it will build and resolve
|
|
40
|
+
* the promise but the already queued build will happen at some later time
|
|
36
41
|
*/
|
|
37
|
-
getSync(
|
|
38
|
-
|
|
42
|
+
getSync<T extends ShufflingBuildProps | undefined>(epoch: Epoch, decisionRoot: RootHex, buildProps?: T): T extends ShufflingBuildProps ? EpochShuffling : EpochShuffling | null;
|
|
43
|
+
/**
|
|
44
|
+
* Queue asynchronous build for an EpochShuffling, triggered from state-transition
|
|
45
|
+
*/
|
|
46
|
+
build(epoch: number, decisionRoot: string, state: BeaconStateAllForks, activeIndices: Uint32Array): void;
|
|
47
|
+
/**
|
|
48
|
+
* Add an EpochShuffling to the ShufflingCache. If a promise for the shuffling is present it will
|
|
49
|
+
* resolve the promise with the built shuffling
|
|
50
|
+
*/
|
|
51
|
+
private set;
|
|
52
|
+
}
|
|
53
|
+
export declare enum ShufflingCacheErrorCode {
|
|
54
|
+
NO_SHUFFLING_FOUND = "SHUFFLING_CACHE_ERROR_NO_SHUFFLING_FOUND"
|
|
55
|
+
}
|
|
56
|
+
type ShufflingCacheErrorType = {
|
|
57
|
+
code: ShufflingCacheErrorCode.NO_SHUFFLING_FOUND;
|
|
58
|
+
epoch: Epoch;
|
|
59
|
+
decisionRoot: RootHex;
|
|
60
|
+
};
|
|
61
|
+
export declare class ShufflingCacheError extends LodestarError<ShufflingCacheErrorType> {
|
|
39
62
|
}
|
|
63
|
+
export {};
|
|
40
64
|
//# sourceMappingURL=shufflingCache.d.ts.map
|
|
@@ -1,8 +1,6 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import { GENESIS_SLOT } from "@lodestar/params";
|
|
5
|
-
import { computeAnchorCheckpoint } from "./initState.js";
|
|
1
|
+
import { computeEpochShuffling, } from "@lodestar/state-transition";
|
|
2
|
+
import { LodestarError, MapDef, pruneSetToMax } from "@lodestar/utils";
|
|
3
|
+
import { callInNextEventLoop } from "../util/eventLoop.js";
|
|
6
4
|
/**
|
|
7
5
|
* Same value to CheckpointBalancesCache, with the assumption that we don't have to use it for old epochs. In the worse case:
|
|
8
6
|
* - when loading state bytes from disk, we need to compute shuffling for all epochs (~1s as of Sep 2023)
|
|
@@ -27,71 +25,31 @@ var CacheItemType;
|
|
|
27
25
|
* - skip computing shuffling when loading state bytes from disk
|
|
28
26
|
*/
|
|
29
27
|
export class ShufflingCache {
|
|
30
|
-
constructor(metrics = null, opts = {}) {
|
|
28
|
+
constructor(metrics = null, logger = null, opts = {}, precalculatedShufflings) {
|
|
31
29
|
this.metrics = metrics;
|
|
30
|
+
this.logger = logger;
|
|
32
31
|
/** LRU cache implemented as a map, pruned every time we add an item */
|
|
33
32
|
this.itemsByDecisionRootByEpoch = new MapDef(() => new Map());
|
|
34
33
|
if (metrics) {
|
|
35
34
|
metrics.shufflingCache.size.addCollect(() => metrics.shufflingCache.size.set(Array.from(this.itemsByDecisionRootByEpoch.values()).reduce((total, innerMap) => total + innerMap.size, 0)));
|
|
36
35
|
}
|
|
37
36
|
this.maxEpochs = opts.maxShufflingCacheEpochs ?? MAX_EPOCHS;
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
*/
|
|
42
|
-
processState(state, shufflingEpoch) {
|
|
43
|
-
const decisionBlockHex = getDecisionBlock(state, shufflingEpoch);
|
|
44
|
-
let shuffling;
|
|
45
|
-
switch (shufflingEpoch) {
|
|
46
|
-
case state.epochCtx.nextShuffling.epoch:
|
|
47
|
-
shuffling = state.epochCtx.nextShuffling;
|
|
48
|
-
break;
|
|
49
|
-
case state.epochCtx.currentShuffling.epoch:
|
|
50
|
-
shuffling = state.epochCtx.currentShuffling;
|
|
51
|
-
break;
|
|
52
|
-
case state.epochCtx.previousShuffling.epoch:
|
|
53
|
-
shuffling = state.epochCtx.previousShuffling;
|
|
54
|
-
break;
|
|
55
|
-
default:
|
|
56
|
-
throw new Error(`Shuffling not found from state ${state.slot} for epoch ${shufflingEpoch}`);
|
|
57
|
-
}
|
|
58
|
-
let cacheItem = this.itemsByDecisionRootByEpoch.getOrDefault(shufflingEpoch).get(decisionBlockHex);
|
|
59
|
-
if (cacheItem !== undefined) {
|
|
60
|
-
// update existing promise
|
|
61
|
-
if (isPromiseCacheItem(cacheItem)) {
|
|
62
|
-
// unblock consumers of this promise
|
|
63
|
-
cacheItem.resolveFn(shuffling);
|
|
64
|
-
// then update item type to shuffling
|
|
65
|
-
cacheItem = {
|
|
66
|
-
type: CacheItemType.shuffling,
|
|
67
|
-
shuffling,
|
|
68
|
-
};
|
|
69
|
-
this.add(shufflingEpoch, decisionBlockHex, cacheItem);
|
|
70
|
-
// we updated type to CacheItemType.shuffling so the above fields are not used anyway
|
|
71
|
-
this.metrics?.shufflingCache.processStateUpdatePromise.inc();
|
|
72
|
-
}
|
|
73
|
-
else {
|
|
74
|
-
// ShufflingCacheItem, do nothing
|
|
75
|
-
this.metrics?.shufflingCache.processStateNoOp.inc();
|
|
37
|
+
precalculatedShufflings?.map(({ shuffling, decisionRoot }) => {
|
|
38
|
+
if (shuffling !== null) {
|
|
39
|
+
this.set(shuffling, decisionRoot);
|
|
76
40
|
}
|
|
77
|
-
}
|
|
78
|
-
else {
|
|
79
|
-
// not found, new shuffling
|
|
80
|
-
this.add(shufflingEpoch, decisionBlockHex, { type: CacheItemType.shuffling, shuffling });
|
|
81
|
-
this.metrics?.shufflingCache.processStateInsertNew.inc();
|
|
82
|
-
}
|
|
83
|
-
return shuffling;
|
|
41
|
+
});
|
|
84
42
|
}
|
|
85
43
|
/**
|
|
86
44
|
* Insert a promise to make sure we don't regen state for the same shuffling.
|
|
87
45
|
* Bound by MAX_SHUFFLING_PROMISE to make sure our node does not blow up.
|
|
88
46
|
*/
|
|
89
|
-
insertPromise(
|
|
47
|
+
insertPromise(epoch, decisionRoot) {
|
|
90
48
|
const promiseCount = Array.from(this.itemsByDecisionRootByEpoch.values())
|
|
91
49
|
.flatMap((innerMap) => Array.from(innerMap.values()))
|
|
92
50
|
.filter((item) => isPromiseCacheItem(item)).length;
|
|
93
51
|
if (promiseCount >= MAX_PROMISES) {
|
|
94
|
-
throw new Error(`Too many shuffling promises: ${promiseCount}, shufflingEpoch: ${
|
|
52
|
+
throw new Error(`Too many shuffling promises: ${promiseCount}, shufflingEpoch: ${epoch}, decisionRootHex: ${decisionRoot}`);
|
|
95
53
|
}
|
|
96
54
|
let resolveFn = null;
|
|
97
55
|
const promise = new Promise((resolve) => {
|
|
@@ -102,10 +60,11 @@ export class ShufflingCache {
|
|
|
102
60
|
}
|
|
103
61
|
const cacheItem = {
|
|
104
62
|
type: CacheItemType.promise,
|
|
63
|
+
timeInsertedMs: Date.now(),
|
|
105
64
|
promise,
|
|
106
65
|
resolveFn,
|
|
107
66
|
};
|
|
108
|
-
this.
|
|
67
|
+
this.itemsByDecisionRootByEpoch.getOrDefault(epoch).set(decisionRoot, cacheItem);
|
|
109
68
|
this.metrics?.shufflingCache.insertPromiseCount.inc();
|
|
110
69
|
}
|
|
111
70
|
/**
|
|
@@ -113,35 +72,89 @@ export class ShufflingCache {
|
|
|
113
72
|
* If there's a promise, it means we are computing the same shuffling, so we wait for the promise to resolve.
|
|
114
73
|
* Return null if we don't have a shuffling for this epoch and dependentRootHex.
|
|
115
74
|
*/
|
|
116
|
-
async get(
|
|
117
|
-
const cacheItem = this.itemsByDecisionRootByEpoch.getOrDefault(
|
|
75
|
+
async get(epoch, decisionRoot) {
|
|
76
|
+
const cacheItem = this.itemsByDecisionRootByEpoch.getOrDefault(epoch).get(decisionRoot);
|
|
118
77
|
if (cacheItem === undefined) {
|
|
78
|
+
this.metrics?.shufflingCache.miss.inc();
|
|
119
79
|
return null;
|
|
120
80
|
}
|
|
121
81
|
if (isShufflingCacheItem(cacheItem)) {
|
|
82
|
+
this.metrics?.shufflingCache.hit.inc();
|
|
122
83
|
return cacheItem.shuffling;
|
|
123
84
|
}
|
|
124
85
|
else {
|
|
125
|
-
|
|
86
|
+
this.metrics?.shufflingCache.shufflingPromiseNotResolved.inc();
|
|
126
87
|
return cacheItem.promise;
|
|
127
88
|
}
|
|
128
89
|
}
|
|
129
90
|
/**
|
|
130
|
-
*
|
|
91
|
+
* Gets a cached shuffling via the epoch and decision root. If the shuffling is not
|
|
92
|
+
* available it will build it synchronously and return the shuffling.
|
|
93
|
+
*
|
|
94
|
+
* NOTE: If a shuffling is already queued and not calculated it will build and resolve
|
|
95
|
+
* the promise but the already queued build will happen at some later time
|
|
131
96
|
*/
|
|
132
|
-
getSync(
|
|
133
|
-
const cacheItem = this.itemsByDecisionRootByEpoch.getOrDefault(
|
|
134
|
-
if (cacheItem
|
|
135
|
-
|
|
97
|
+
getSync(epoch, decisionRoot, buildProps) {
|
|
98
|
+
const cacheItem = this.itemsByDecisionRootByEpoch.getOrDefault(epoch).get(decisionRoot);
|
|
99
|
+
if (!cacheItem) {
|
|
100
|
+
this.metrics?.shufflingCache.miss.inc();
|
|
136
101
|
}
|
|
137
|
-
if (isShufflingCacheItem(cacheItem)) {
|
|
102
|
+
else if (isShufflingCacheItem(cacheItem)) {
|
|
103
|
+
this.metrics?.shufflingCache.hit.inc();
|
|
138
104
|
return cacheItem.shuffling;
|
|
139
105
|
}
|
|
140
|
-
|
|
141
|
-
|
|
106
|
+
else if (buildProps) {
|
|
107
|
+
// TODO: (@matthewkeil) This should possible log a warning??
|
|
108
|
+
this.metrics?.shufflingCache.shufflingPromiseNotResolvedAndThrownAway.inc();
|
|
109
|
+
}
|
|
110
|
+
else {
|
|
111
|
+
this.metrics?.shufflingCache.shufflingPromiseNotResolved.inc();
|
|
112
|
+
}
|
|
113
|
+
let shuffling = null;
|
|
114
|
+
if (buildProps) {
|
|
115
|
+
const timer = this.metrics?.shufflingCache.shufflingCalculationTime.startTimer({ source: "getSync" });
|
|
116
|
+
shuffling = computeEpochShuffling(buildProps.state, buildProps.activeIndices, epoch);
|
|
117
|
+
timer?.();
|
|
118
|
+
this.set(shuffling, decisionRoot);
|
|
119
|
+
}
|
|
120
|
+
return shuffling;
|
|
142
121
|
}
|
|
143
|
-
|
|
144
|
-
|
|
122
|
+
/**
|
|
123
|
+
* Queue asynchronous build for an EpochShuffling, triggered from state-transition
|
|
124
|
+
*/
|
|
125
|
+
build(epoch, decisionRoot, state, activeIndices) {
|
|
126
|
+
this.insertPromise(epoch, decisionRoot);
|
|
127
|
+
/**
|
|
128
|
+
* TODO: (@matthewkeil) This will get replaced by a proper build queue and a worker to do calculations
|
|
129
|
+
* on a NICE thread with a rust implementation
|
|
130
|
+
*/
|
|
131
|
+
callInNextEventLoop(() => {
|
|
132
|
+
const timer = this.metrics?.shufflingCache.shufflingCalculationTime.startTimer({ source: "build" });
|
|
133
|
+
const shuffling = computeEpochShuffling(state, activeIndices, epoch);
|
|
134
|
+
timer?.();
|
|
135
|
+
this.set(shuffling, decisionRoot);
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* Add an EpochShuffling to the ShufflingCache. If a promise for the shuffling is present it will
|
|
140
|
+
* resolve the promise with the built shuffling
|
|
141
|
+
*/
|
|
142
|
+
set(shuffling, decisionRoot) {
|
|
143
|
+
const shufflingAtEpoch = this.itemsByDecisionRootByEpoch.getOrDefault(shuffling.epoch);
|
|
144
|
+
// if a pending shuffling promise exists, resolve it
|
|
145
|
+
const cacheItem = shufflingAtEpoch.get(decisionRoot);
|
|
146
|
+
if (cacheItem) {
|
|
147
|
+
if (isPromiseCacheItem(cacheItem)) {
|
|
148
|
+
cacheItem.resolveFn(shuffling);
|
|
149
|
+
this.metrics?.shufflingCache.shufflingPromiseResolutionTime.observe((Date.now() - cacheItem.timeInsertedMs) / 1000);
|
|
150
|
+
}
|
|
151
|
+
else {
|
|
152
|
+
this.metrics?.shufflingCache.shufflingBuiltMultipleTimes.inc();
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
// set the shuffling
|
|
156
|
+
shufflingAtEpoch.set(decisionRoot, { type: CacheItemType.shuffling, shuffling });
|
|
157
|
+
// prune the cache
|
|
145
158
|
pruneSetToMax(this.itemsByDecisionRootByEpoch, this.maxEpochs);
|
|
146
159
|
}
|
|
147
160
|
}
|
|
@@ -151,14 +164,10 @@ function isShufflingCacheItem(item) {
|
|
|
151
164
|
function isPromiseCacheItem(item) {
|
|
152
165
|
return item.type === CacheItemType.promise;
|
|
153
166
|
}
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
function getDecisionBlock(state, epoch) {
|
|
160
|
-
return state.slot > GENESIS_SLOT
|
|
161
|
-
? getShufflingDecisionBlock(state, epoch)
|
|
162
|
-
: toRootHex(ssz.phase0.BeaconBlockHeader.hashTreeRoot(computeAnchorCheckpoint(state.config, state).blockHeader));
|
|
167
|
+
export var ShufflingCacheErrorCode;
|
|
168
|
+
(function (ShufflingCacheErrorCode) {
|
|
169
|
+
ShufflingCacheErrorCode["NO_SHUFFLING_FOUND"] = "SHUFFLING_CACHE_ERROR_NO_SHUFFLING_FOUND";
|
|
170
|
+
})(ShufflingCacheErrorCode || (ShufflingCacheErrorCode = {}));
|
|
171
|
+
export class ShufflingCacheError extends LodestarError {
|
|
163
172
|
}
|
|
164
173
|
//# sourceMappingURL=shufflingCache.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"shufflingCache.js","sourceRoot":"","sources":["../../src/chain/shufflingCache.ts"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"shufflingCache.js","sourceRoot":"","sources":["../../src/chain/shufflingCache.ts"],"names":[],"mappings":"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"}
|
|
@@ -97,7 +97,7 @@ export class PersistentCheckpointStateCache {
|
|
|
97
97
|
this.preComputedCheckpointHits = null;
|
|
98
98
|
this.cache = new MapTracker(metrics?.cpStateCache);
|
|
99
99
|
if (metrics) {
|
|
100
|
-
this.metrics = metrics
|
|
100
|
+
this.metrics = metrics;
|
|
101
101
|
metrics.cpStateCache.size.addCollect(() => {
|
|
102
102
|
let persistCount = 0;
|
|
103
103
|
let inMemoryCount = 0;
|
|
@@ -167,34 +167,26 @@ export class PersistentCheckpointStateCache {
|
|
|
167
167
|
const { persistedKey, stateBytes } = stateOrStateBytesData;
|
|
168
168
|
const logMeta = { persistedKey: toHex(persistedKey) };
|
|
169
169
|
this.logger.debug("Reload: read state successful", logMeta);
|
|
170
|
-
this.metrics?.stateReloadSecFromSlot.observe(this.clock?.secFromSlot(this.clock?.currentSlot ?? 0) ?? 0);
|
|
170
|
+
this.metrics?.cpStateCache.stateReloadSecFromSlot.observe(this.clock?.secFromSlot(this.clock?.currentSlot ?? 0) ?? 0);
|
|
171
171
|
const seedState = this.findSeedStateToReload(cp);
|
|
172
|
-
this.metrics?.stateReloadEpochDiff.observe(Math.abs(seedState.epochCtx.epoch - cp.epoch));
|
|
172
|
+
this.metrics?.cpStateCache.stateReloadEpochDiff.observe(Math.abs(seedState.epochCtx.epoch - cp.epoch));
|
|
173
173
|
this.logger.debug("Reload: found seed state", { ...logMeta, seedSlot: seedState.slot });
|
|
174
174
|
try {
|
|
175
175
|
const env_1 = { stack: [], error: void 0, hasError: false };
|
|
176
176
|
try {
|
|
177
177
|
// 80% of validators serialization time comes from memory allocation, this is to avoid it
|
|
178
|
-
const sszTimer = this.metrics?.stateReloadValidatorsSerializeDuration.startTimer();
|
|
178
|
+
const sszTimer = this.metrics?.cpStateCache.stateReloadValidatorsSerializeDuration.startTimer();
|
|
179
179
|
// automatically free the buffer pool after this scope
|
|
180
180
|
const validatorsBytesWithKey = __addDisposableResource(env_1, this.serializeStateValidators(seedState), false);
|
|
181
181
|
let validatorsBytes = validatorsBytesWithKey?.buffer;
|
|
182
182
|
if (validatorsBytes == null) {
|
|
183
183
|
// fallback logic in case we can't use the buffer pool
|
|
184
|
-
this.metrics?.stateReloadValidatorsSerializeAllocCount.inc();
|
|
184
|
+
this.metrics?.cpStateCache.stateReloadValidatorsSerializeAllocCount.inc();
|
|
185
185
|
validatorsBytes = seedState.validators.serialize();
|
|
186
186
|
}
|
|
187
187
|
sszTimer?.();
|
|
188
|
-
const timer = this.metrics?.stateReloadDuration.startTimer();
|
|
189
|
-
const newCachedState = loadCachedBeaconState(seedState, stateBytes, {
|
|
190
|
-
shufflingGetter: (shufflingEpoch, decisionRootHex) => {
|
|
191
|
-
const shuffling = this.shufflingCache.getSync(shufflingEpoch, decisionRootHex);
|
|
192
|
-
if (shuffling == null) {
|
|
193
|
-
this.metrics?.stateReloadShufflingCacheMiss.inc();
|
|
194
|
-
}
|
|
195
|
-
return shuffling;
|
|
196
|
-
},
|
|
197
|
-
}, validatorsBytes);
|
|
188
|
+
const timer = this.metrics?.cpStateCache.stateReloadDuration.startTimer();
|
|
189
|
+
const newCachedState = loadCachedBeaconState(seedState, stateBytes, {}, validatorsBytes);
|
|
198
190
|
newCachedState.commit();
|
|
199
191
|
const stateRoot = toRootHex(newCachedState.hashTreeRoot());
|
|
200
192
|
timer?.();
|
|
@@ -256,7 +248,7 @@ export class PersistentCheckpointStateCache {
|
|
|
256
248
|
throw new Error("Expected persistent key");
|
|
257
249
|
}
|
|
258
250
|
const persistedKey = cacheItem.value;
|
|
259
|
-
const dbReadTimer = this.metrics?.stateReloadDbReadTime.startTimer();
|
|
251
|
+
const dbReadTimer = this.metrics?.cpStateCache.stateReloadDbReadTime.startTimer();
|
|
260
252
|
const stateBytes = await this.datastore.read(persistedKey);
|
|
261
253
|
dbReadTimer?.();
|
|
262
254
|
if (stateBytes === null) {
|
|
@@ -268,19 +260,19 @@ export class PersistentCheckpointStateCache {
|
|
|
268
260
|
* Similar to get() api without reloading from disk
|
|
269
261
|
*/
|
|
270
262
|
get(cpOrKey, opts) {
|
|
271
|
-
this.metrics?.lookups.inc();
|
|
263
|
+
this.metrics?.cpStateCache.lookups.inc();
|
|
272
264
|
const cpKey = typeof cpOrKey === "string" ? cpOrKey : toCacheKey(cpOrKey);
|
|
273
265
|
const cacheItem = this.cache.get(cpKey);
|
|
274
266
|
if (cacheItem === undefined) {
|
|
275
267
|
return null;
|
|
276
268
|
}
|
|
277
|
-
this.metrics?.hits.inc();
|
|
269
|
+
this.metrics?.cpStateCache.hits.inc();
|
|
278
270
|
if (cpKey === this.preComputedCheckpoint) {
|
|
279
271
|
this.preComputedCheckpointHits = (this.preComputedCheckpointHits ?? 0) + 1;
|
|
280
272
|
}
|
|
281
273
|
if (isInMemoryCacheItem(cacheItem)) {
|
|
282
274
|
const { state } = cacheItem;
|
|
283
|
-
this.metrics?.stateClonedCount.observe(state.clonedCount);
|
|
275
|
+
this.metrics?.cpStateCache.stateClonedCount.observe(state.clonedCount);
|
|
284
276
|
return state.clone(opts?.dontTransferCache);
|
|
285
277
|
}
|
|
286
278
|
return null;
|
|
@@ -292,7 +284,7 @@ export class PersistentCheckpointStateCache {
|
|
|
292
284
|
const cpHex = toCheckpointHex(cp);
|
|
293
285
|
const key = toCacheKey(cpHex);
|
|
294
286
|
const cacheItem = this.cache.get(key);
|
|
295
|
-
this.metrics?.adds.inc();
|
|
287
|
+
this.metrics?.cpStateCache.adds.inc();
|
|
296
288
|
if (cacheItem !== undefined && isPersistedCacheItem(cacheItem)) {
|
|
297
289
|
const persistedKey = cacheItem.value;
|
|
298
290
|
// was persisted to disk, set back to memory
|
|
@@ -622,7 +614,7 @@ export class PersistentCheckpointStateCache {
|
|
|
622
614
|
}
|
|
623
615
|
else {
|
|
624
616
|
// persist and do not update epochIndex
|
|
625
|
-
this.metrics?.statePersistSecFromSlot.observe(this.clock?.secFromSlot(this.clock?.currentSlot ?? 0) ?? 0);
|
|
617
|
+
this.metrics?.cpStateCache.statePersistSecFromSlot.observe(this.clock?.secFromSlot(this.clock?.currentSlot ?? 0) ?? 0);
|
|
626
618
|
const cpPersist = { epoch: epoch, root: fromHex(rootHex) };
|
|
627
619
|
// It's not sustainable to allocate ~240MB for each state every epoch, so we use buffer pool to reuse the memory.
|
|
628
620
|
// As monitored on holesky as of Jan 2024:
|
|
@@ -630,9 +622,13 @@ export class PersistentCheckpointStateCache {
|
|
|
630
622
|
// - It helps stabilize persist time and save ~300ms in average (1.5s vs 1.2s)
|
|
631
623
|
// - It also helps the state reload to save ~500ms in average (4.3s vs 3.8s)
|
|
632
624
|
// - Also `serializeState.test.ts` perf test shows a lot of differences allocating ~240MB once vs per state serialization
|
|
633
|
-
const timer = this.metrics?.stateSerializeDuration.startTimer(
|
|
634
|
-
|
|
635
|
-
|
|
625
|
+
const timer = this.metrics?.stateSerializeDuration.startTimer({
|
|
626
|
+
source: AllocSource.PERSISTENT_CHECKPOINTS_CACHE_STATE,
|
|
627
|
+
});
|
|
628
|
+
persistedKey = await serializeState(state, AllocSource.PERSISTENT_CHECKPOINTS_CACHE_STATE, (stateBytes) => {
|
|
629
|
+
timer?.();
|
|
630
|
+
return this.datastore.write(cpPersist, stateBytes);
|
|
631
|
+
}, this.bufferPool);
|
|
636
632
|
persistCount++;
|
|
637
633
|
this.logger.verbose("Pruned checkpoint state from memory and persisted to disk", {
|
|
638
634
|
...logMeta,
|
|
@@ -654,7 +650,7 @@ export class PersistentCheckpointStateCache {
|
|
|
654
650
|
this.cache.delete(cpKey);
|
|
655
651
|
this.epochIndex.get(epoch)?.delete(rootHex);
|
|
656
652
|
}
|
|
657
|
-
this.metrics?.statePruneFromMemoryCount.inc();
|
|
653
|
+
this.metrics?.cpStateCache.statePruneFromMemoryCount.inc();
|
|
658
654
|
this.logger.verbose("Pruned checkpoint state from memory", logMeta);
|
|
659
655
|
}
|
|
660
656
|
}
|
|
@@ -675,7 +671,7 @@ export class PersistentCheckpointStateCache {
|
|
|
675
671
|
if (persistedKey) {
|
|
676
672
|
await this.datastore.remove(persistedKey);
|
|
677
673
|
persistCount++;
|
|
678
|
-
this.metrics?.persistedStateRemoveCount.inc();
|
|
674
|
+
this.metrics?.cpStateCache.persistedStateRemoveCount.inc();
|
|
679
675
|
}
|
|
680
676
|
}
|
|
681
677
|
this.cache.delete(key);
|