@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.
Files changed (69) hide show
  1. package/lib/api/impl/beacon/state/index.js +5 -1
  2. package/lib/api/impl/beacon/state/index.js.map +1 -1
  3. package/lib/api/impl/config/constants.d.ts +0 -1
  4. package/lib/api/impl/config/constants.js +1 -2
  5. package/lib/api/impl/config/constants.js.map +1 -1
  6. package/lib/api/impl/validator/index.js +8 -3
  7. package/lib/api/impl/validator/index.js.map +1 -1
  8. package/lib/chain/archiver/archiveStates.d.ts +3 -2
  9. package/lib/chain/archiver/archiveStates.js +8 -4
  10. package/lib/chain/archiver/archiveStates.js.map +1 -1
  11. package/lib/chain/archiver/index.d.ts +3 -1
  12. package/lib/chain/archiver/index.js +3 -2
  13. package/lib/chain/archiver/index.js.map +1 -1
  14. package/lib/chain/blocks/importBlock.js +0 -6
  15. package/lib/chain/blocks/importBlock.js.map +1 -1
  16. package/lib/chain/blocks/verifyBlocksExecutionPayloads.js +2 -1
  17. package/lib/chain/blocks/verifyBlocksExecutionPayloads.js.map +1 -1
  18. package/lib/chain/chain.d.ts +0 -40
  19. package/lib/chain/chain.js +18 -9
  20. package/lib/chain/chain.js.map +1 -1
  21. package/lib/chain/forkChoice/index.js +1 -2
  22. package/lib/chain/forkChoice/index.js.map +1 -1
  23. package/lib/chain/initState.d.ts +1 -5
  24. package/lib/chain/initState.js +2 -30
  25. package/lib/chain/initState.js.map +1 -1
  26. package/lib/chain/lightClient/proofs.d.ts +2 -2
  27. package/lib/chain/lightClient/proofs.js +3 -2
  28. package/lib/chain/lightClient/proofs.js.map +1 -1
  29. package/lib/chain/produceBlock/produceBlockBody.js +7 -1
  30. package/lib/chain/produceBlock/produceBlockBody.js.map +1 -1
  31. package/lib/chain/serializeState.js +63 -10
  32. package/lib/chain/serializeState.js.map +1 -1
  33. package/lib/chain/shufflingCache.d.ts +37 -13
  34. package/lib/chain/shufflingCache.js +85 -76
  35. package/lib/chain/shufflingCache.js.map +1 -1
  36. package/lib/chain/stateCache/persistentCheckpointsCache.js +22 -26
  37. package/lib/chain/stateCache/persistentCheckpointsCache.js.map +1 -1
  38. package/lib/db/repositories/lightclientCheckpointHeader.d.ts +1 -0
  39. package/lib/db/repositories/lightclientCheckpointHeader.js +3 -0
  40. package/lib/db/repositories/lightclientCheckpointHeader.js.map +1 -1
  41. package/lib/execution/engine/http.d.ts +3 -2
  42. package/lib/execution/engine/http.js +27 -10
  43. package/lib/execution/engine/http.js.map +1 -1
  44. package/lib/execution/engine/interface.d.ts +5 -4
  45. package/lib/execution/engine/interface.js.map +1 -1
  46. package/lib/execution/engine/mock.js +0 -2
  47. package/lib/execution/engine/mock.js.map +1 -1
  48. package/lib/execution/engine/payloadIdCache.d.ts +0 -17
  49. package/lib/execution/engine/payloadIdCache.js.map +1 -1
  50. package/lib/execution/engine/types.d.ts +29 -25
  51. package/lib/execution/engine/types.js +28 -42
  52. package/lib/execution/engine/types.js.map +1 -1
  53. package/lib/metrics/metrics/beacon.js +1 -1
  54. package/lib/metrics/metrics/beacon.js.map +1 -1
  55. package/lib/metrics/metrics/lodestar.d.ts +13 -4
  56. package/lib/metrics/metrics/lodestar.js +43 -19
  57. package/lib/metrics/metrics/lodestar.js.map +1 -1
  58. package/lib/network/gossip/topic.d.ts +0 -27
  59. package/lib/network/reqresp/types.d.ts +5 -5
  60. package/lib/network/reqresp/types.js +1 -1
  61. package/lib/network/reqresp/types.js.map +1 -1
  62. package/lib/node/utils/interop/state.js +1 -0
  63. package/lib/node/utils/interop/state.js.map +1 -1
  64. package/lib/node/utils/state.d.ts +3 -0
  65. package/lib/node/utils/state.js +3 -0
  66. package/lib/node/utils/state.js.map +1 -1
  67. package/lib/sync/backfill/backfill.js +1 -2
  68. package/lib/sync/backfill/backfill.js.map +1 -1
  69. 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 bufferWithKey = bufferPool.alloc(size, source);
9
- if (bufferWithKey) {
10
- stateBytes = bufferWithKey.buffer;
11
- const dataView = new DataView(stateBytes.buffer, stateBytes.byteOffset, stateBytes.byteLength);
12
- state.serializeToBytes({ uint8Array: stateBytes, dataView }, 0);
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
- if (!stateBytes) {
16
- // we already have metrics in BufferPool so no need to do it here
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;QACf,MAAM,aAAa,GAAG,UAAU,CAAC,KAAK,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;QACrD,IAAI,aAAa,EAAE,CAAC;YAClB,UAAU,GAAG,aAAa,CAAC,MAAM,CAAC;YAClC,MAAM,QAAQ,GAAG,IAAI,QAAQ,CAAC,UAAU,CAAC,MAAM,EAAE,UAAU,CAAC,UAAU,EAAE,UAAU,CAAC,UAAU,CAAC,CAAC;YAC/F,KAAK,CAAC,gBAAgB,CAAC,EAAC,UAAU,EAAE,UAAU,EAAE,QAAQ,EAAC,EAAE,CAAC,CAAC,CAAC;QAChE,CAAC;IACH,CAAC;IAED,IAAI,CAAC,UAAU,EAAE,CAAC;QAChB,iEAAiE;QACjE,UAAU,GAAG,KAAK,CAAC,SAAS,EAAE,CAAC;IACjC,CAAC;IAED,OAAO,SAAS,CAAC,UAAU,CAAC,CAAC;IAC7B,oDAAoD;AACtD,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 { CachedBeaconStateAllForks, EpochShuffling } from "@lodestar/state-transition";
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
- private readonly metrics;
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
- * Extract shuffling from state and add to cache
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(shufflingEpoch: Epoch, decisionRootHex: RootHex): void;
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(shufflingEpoch: Epoch, decisionRootHex: RootHex): Promise<EpochShuffling | null>;
34
+ get(epoch: Epoch, decisionRoot: RootHex): Promise<EpochShuffling | null>;
34
35
  /**
35
- * Same to get() function but synchronous.
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(shufflingEpoch: Epoch, decisionRootHex: RootHex): EpochShuffling | null;
38
- private add;
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 { getShufflingDecisionBlock } from "@lodestar/state-transition";
2
- import { ssz } from "@lodestar/types";
3
- import { MapDef, pruneSetToMax, toRootHex } from "@lodestar/utils";
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
- * Extract shuffling from state and add to cache
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(shufflingEpoch, decisionRootHex) {
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: ${shufflingEpoch}, decisionRootHex: ${decisionRootHex}`);
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.add(shufflingEpoch, decisionRootHex, cacheItem);
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(shufflingEpoch, decisionRootHex) {
117
- const cacheItem = this.itemsByDecisionRootByEpoch.getOrDefault(shufflingEpoch).get(decisionRootHex);
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
- // promise
86
+ this.metrics?.shufflingCache.shufflingPromiseNotResolved.inc();
126
87
  return cacheItem.promise;
127
88
  }
128
89
  }
129
90
  /**
130
- * Same to get() function but synchronous.
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(shufflingEpoch, decisionRootHex) {
133
- const cacheItem = this.itemsByDecisionRootByEpoch.getOrDefault(shufflingEpoch).get(decisionRootHex);
134
- if (cacheItem === undefined) {
135
- return null;
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
- // ignore promise
141
- return null;
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
- add(shufflingEpoch, decisionBlock, cacheItem) {
144
- this.itemsByDecisionRootByEpoch.getOrDefault(shufflingEpoch).set(decisionBlock, cacheItem);
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
- * Get the shuffling decision block root for the given epoch of given state
156
- * - Special case close to genesis block, return the genesis block root
157
- * - This is similar to forkchoice.getDependentRoot() function, otherwise we cannot get cached shuffing in attestation verification when syncing from genesis.
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":"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"}
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.cpStateCache;
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
- persistedKey = await serializeState(state, AllocSource.PERSISTENT_CHECKPOINTS_CACHE_STATE, (stateBytes) => this.datastore.write(cpPersist, stateBytes), this.bufferPool);
635
- timer?.();
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);