@lodestar/beacon-node 1.40.0-dev.2ae7375100 → 1.40.0-dev.2c42f62eed

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 (63) hide show
  1. package/lib/api/impl/validator/index.d.ts.map +1 -1
  2. package/lib/api/impl/validator/index.js.map +1 -1
  3. package/lib/chain/blocks/blockInput/blockInput.d.ts +28 -0
  4. package/lib/chain/blocks/blockInput/blockInput.d.ts.map +1 -1
  5. package/lib/chain/blocks/blockInput/blockInput.js +36 -1
  6. package/lib/chain/blocks/blockInput/blockInput.js.map +1 -1
  7. package/lib/chain/blocks/importBlock.js +1 -1
  8. package/lib/chain/blocks/importBlock.js.map +1 -1
  9. package/lib/chain/blocks/writeBlockInputToDb.d.ts.map +1 -1
  10. package/lib/chain/blocks/writeBlockInputToDb.js +8 -0
  11. package/lib/chain/blocks/writeBlockInputToDb.js.map +1 -1
  12. package/lib/chain/chain.d.ts +1 -1
  13. package/lib/chain/chain.d.ts.map +1 -1
  14. package/lib/chain/chain.js +12 -24
  15. package/lib/chain/chain.js.map +1 -1
  16. package/lib/chain/options.d.ts +0 -1
  17. package/lib/chain/options.d.ts.map +1 -1
  18. package/lib/chain/options.js +0 -1
  19. package/lib/chain/options.js.map +1 -1
  20. package/lib/chain/regen/interface.d.ts +1 -1
  21. package/lib/chain/regen/queued.d.ts +1 -1
  22. package/lib/chain/regen/queued.d.ts.map +1 -1
  23. package/lib/chain/regen/queued.js.map +1 -1
  24. package/lib/chain/stateCache/index.d.ts +0 -2
  25. package/lib/chain/stateCache/index.d.ts.map +1 -1
  26. package/lib/chain/stateCache/index.js +0 -2
  27. package/lib/chain/stateCache/index.js.map +1 -1
  28. package/lib/chain/stateCache/persistentCheckpointsCache.d.ts +2 -1
  29. package/lib/chain/stateCache/persistentCheckpointsCache.d.ts.map +1 -1
  30. package/lib/chain/stateCache/persistentCheckpointsCache.js +3 -0
  31. package/lib/chain/stateCache/persistentCheckpointsCache.js.map +1 -1
  32. package/lib/chain/validation/block.d.ts.map +1 -1
  33. package/lib/chain/validation/block.js +1 -2
  34. package/lib/chain/validation/block.js.map +1 -1
  35. package/lib/network/peers/peerManager.d.ts.map +1 -1
  36. package/lib/network/peers/peerManager.js +9 -0
  37. package/lib/network/peers/peerManager.js.map +1 -1
  38. package/lib/network/processor/gossipHandlers.js +1 -1
  39. package/lib/network/processor/gossipHandlers.js.map +1 -1
  40. package/package.json +15 -15
  41. package/src/api/impl/validator/index.ts +2 -1
  42. package/src/chain/blocks/blockInput/blockInput.ts +45 -2
  43. package/src/chain/blocks/importBlock.ts +1 -1
  44. package/src/chain/blocks/writeBlockInputToDb.ts +9 -0
  45. package/src/chain/chain.ts +16 -27
  46. package/src/chain/options.ts +0 -2
  47. package/src/chain/regen/interface.ts +1 -1
  48. package/src/chain/regen/queued.ts +1 -2
  49. package/src/chain/stateCache/index.ts +0 -2
  50. package/src/chain/stateCache/persistentCheckpointsCache.ts +6 -2
  51. package/src/chain/validation/block.ts +1 -2
  52. package/src/network/peers/peerManager.ts +11 -0
  53. package/src/network/processor/gossipHandlers.ts +1 -1
  54. package/lib/chain/stateCache/blockStateCacheImpl.d.ts +0 -54
  55. package/lib/chain/stateCache/blockStateCacheImpl.d.ts.map +0 -1
  56. package/lib/chain/stateCache/blockStateCacheImpl.js +0 -130
  57. package/lib/chain/stateCache/blockStateCacheImpl.js.map +0 -1
  58. package/lib/chain/stateCache/inMemoryCheckpointsCache.d.ts +0 -60
  59. package/lib/chain/stateCache/inMemoryCheckpointsCache.d.ts.map +0 -1
  60. package/lib/chain/stateCache/inMemoryCheckpointsCache.js +0 -156
  61. package/lib/chain/stateCache/inMemoryCheckpointsCache.js.map +0 -1
  62. package/src/chain/stateCache/blockStateCacheImpl.ts +0 -149
  63. package/src/chain/stateCache/inMemoryCheckpointsCache.ts +0 -192
@@ -1,4 +1,4 @@
1
- import {ForkName, ForkPostFulu, ForkPreDeneb, ForkPreGloas} from "@lodestar/params";
1
+ import {ForkName, ForkPostFulu, ForkPreDeneb, ForkPreGloas, NUMBER_OF_COLUMNS} from "@lodestar/params";
2
2
  import {BeaconBlockBody, BlobIndex, ColumnIndex, SignedBeaconBlock, Slot, deneb, fulu} from "@lodestar/types";
3
3
  import {fromHex, prettyBytes, toRootHex, withTimeout} from "@lodestar/utils";
4
4
  import {VersionedHashes} from "../../../execution/index.js";
@@ -561,6 +561,7 @@ type BlockInputColumnsState =
561
561
  | {
562
562
  hasBlock: true;
563
563
  hasAllData: true;
564
+ hasComputedAllData: boolean;
564
565
  versionedHashes: VersionedHashes;
565
566
  block: SignedBeaconBlock<ForkColumnsDA>;
566
567
  source: SourceMeta;
@@ -569,6 +570,7 @@ type BlockInputColumnsState =
569
570
  | {
570
571
  hasBlock: true;
571
572
  hasAllData: false;
573
+ hasComputedAllData: false;
572
574
  versionedHashes: VersionedHashes;
573
575
  block: SignedBeaconBlock<ForkColumnsDA>;
574
576
  source: SourceMeta;
@@ -576,11 +578,13 @@ type BlockInputColumnsState =
576
578
  | {
577
579
  hasBlock: false;
578
580
  hasAllData: true;
581
+ hasComputedAllData: boolean;
579
582
  versionedHashes: VersionedHashes;
580
583
  }
581
584
  | {
582
585
  hasBlock: false;
583
586
  hasAllData: false;
587
+ hasComputedAllData: false;
584
588
  versionedHashes: VersionedHashes;
585
589
  };
586
590
  /**
@@ -598,6 +602,12 @@ export class BlockInputColumns extends AbstractBlockInput<ForkColumnsDA, fulu.Da
598
602
  private columnsCache = new Map<ColumnIndex, ColumnWithSource>();
599
603
  private readonly sampledColumns: ColumnIndex[];
600
604
  private readonly custodyColumns: ColumnIndex[];
605
+ /**
606
+ * This promise resolves when all sampled columns are available
607
+ *
608
+ * This is different from `dataPromise` which resolves when all data is available or could become available (e.g. through reconstruction)
609
+ */
610
+ protected computedDataPromise = createPromise<fulu.DataColumnSidecars>();
601
611
 
602
612
  private constructor(
603
613
  init: BlockInputInit,
@@ -626,6 +636,7 @@ export class BlockInputColumns extends AbstractBlockInput<ForkColumnsDA, fulu.Da
626
636
  const state = {
627
637
  hasBlock: true,
628
638
  hasAllData,
639
+ hasComputedAllData: hasAllData,
629
640
  versionedHashes: props.block.message.body.blobKzgCommitments.map(kzgCommitmentToVersionedHash),
630
641
  block: props.block,
631
642
  source: {
@@ -649,6 +660,7 @@ export class BlockInputColumns extends AbstractBlockInput<ForkColumnsDA, fulu.Da
649
660
  blockInput.blockPromise.resolve(props.block);
650
661
  if (hasAllData) {
651
662
  blockInput.dataPromise.resolve([]);
663
+ blockInput.computedDataPromise.resolve([]);
652
664
  }
653
665
  return blockInput;
654
666
  }
@@ -661,6 +673,7 @@ export class BlockInputColumns extends AbstractBlockInput<ForkColumnsDA, fulu.Da
661
673
  const state: BlockInputColumnsState = {
662
674
  hasBlock: false,
663
675
  hasAllData,
676
+ hasComputedAllData: hasAllData as false,
664
677
  versionedHashes: props.columnSidecar.kzgCommitments.map(kzgCommitmentToVersionedHash),
665
678
  };
666
679
  const init: BlockInputInit = {
@@ -674,6 +687,7 @@ export class BlockInputColumns extends AbstractBlockInput<ForkColumnsDA, fulu.Da
674
687
  const blockInput = new BlockInputColumns(init, state, props.sampledColumns, props.custodyColumns);
675
688
  if (hasAllData) {
676
689
  blockInput.dataPromise.resolve([]);
690
+ blockInput.computedDataPromise.resolve([]);
677
691
  }
678
692
  return blockInput;
679
693
  }
@@ -722,11 +736,14 @@ export class BlockInputColumns extends AbstractBlockInput<ForkColumnsDA, fulu.Da
722
736
  const hasAllData =
723
737
  (props.block.message.body as BeaconBlockBody<ForkPostFulu & ForkPreGloas>).blobKzgCommitments.length === 0 ||
724
738
  this.state.hasAllData;
739
+ const hasComputedAllData =
740
+ props.block.message.body.blobKzgCommitments.length === 0 || this.state.hasComputedAllData;
725
741
 
726
742
  this.state = {
727
743
  ...this.state,
728
744
  hasBlock: true,
729
745
  hasAllData,
746
+ hasComputedAllData,
730
747
  block: props.block,
731
748
  source: {
732
749
  source: props.source,
@@ -774,17 +791,32 @@ export class BlockInputColumns extends AbstractBlockInput<ForkColumnsDA, fulu.Da
774
791
  this.columnsCache.set(columnSidecar.index, {columnSidecar, source, seenTimestampSec, peerIdStr});
775
792
 
776
793
  const sampledColumns = this.getSampledColumns();
777
- const hasAllData = this.state.hasAllData || sampledColumns.length === this.sampledColumns.length;
794
+ const hasAllData =
795
+ // already hasAllData
796
+ this.state.hasAllData ||
797
+ // has all sampled columns
798
+ sampledColumns.length === this.sampledColumns.length ||
799
+ // has enough columns to reconstruct the rest
800
+ this.columnsCache.size >= NUMBER_OF_COLUMNS / 2;
801
+
802
+ const hasComputedAllData =
803
+ // has all sampled columns
804
+ sampledColumns.length === this.sampledColumns.length;
778
805
 
779
806
  this.state = {
780
807
  ...this.state,
781
808
  hasAllData: hasAllData || this.state.hasAllData,
809
+ hasComputedAllData: hasComputedAllData || this.state.hasComputedAllData,
782
810
  timeCompleteSec: hasAllData ? seenTimestampSec : undefined,
783
811
  } as BlockInputColumnsState;
784
812
 
785
813
  if (hasAllData && sampledColumns !== null) {
786
814
  this.dataPromise.resolve(sampledColumns);
787
815
  }
816
+
817
+ if (hasComputedAllData && sampledColumns !== null) {
818
+ this.computedDataPromise.resolve(sampledColumns);
819
+ }
788
820
  }
789
821
 
790
822
  hasColumn(columnIndex: number): boolean {
@@ -859,4 +891,15 @@ export class BlockInputColumns extends AbstractBlockInput<ForkColumnsDA, fulu.Da
859
891
  versionedHashes: this.state.versionedHashes,
860
892
  };
861
893
  }
894
+
895
+ hasComputedAllData(): boolean {
896
+ return this.state.hasComputedAllData;
897
+ }
898
+
899
+ waitForComputedAllData(timeout: number, signal?: AbortSignal): Promise<fulu.DataColumnSidecars> {
900
+ if (!this.state.hasComputedAllData) {
901
+ return withTimeout(() => this.computedDataPromise.promise, timeout, signal);
902
+ }
903
+ return Promise.resolve(this.getSampledColumns());
904
+ }
862
905
  }
@@ -30,7 +30,7 @@ import type {BeaconChain} from "../chain.js";
30
30
  import {ChainEvent, ReorgEventData} from "../emitter.js";
31
31
  import {ForkchoiceCaller} from "../forkChoice/index.js";
32
32
  import {REPROCESS_MIN_TIME_TO_NEXT_SLOT_SEC} from "../reprocess.js";
33
- import {toCheckpointHex} from "../stateCache/index.js";
33
+ import {toCheckpointHex} from "../stateCache/persistentCheckpointsCache.js";
34
34
  import {isBlockInputBlobs, isBlockInputColumns} from "./blockInput/blockInput.js";
35
35
  import {AttestationImportOpt, FullyVerifiedBlock, ImportBlockOpts} from "./types.js";
36
36
  import {getCheckpointFromState} from "./utils/checkpoint.js";
@@ -44,6 +44,15 @@ export async function writeBlockInputToDb(this: BeaconChain, blocksInputs: IBloc
44
44
 
45
45
  // NOTE: Old data is pruned on archive
46
46
  if (isBlockInputColumns(blockInput)) {
47
+ if (!blockInput.hasComputedAllData()) {
48
+ // Supernodes may only have a subset of the data columns by the time the block begins to be imported
49
+ // because full data availability can be assumed after NUMBER_OF_COLUMNS / 2 columns are available.
50
+ // Here, however, all data columns must be fully available/reconstructed before persisting to the DB.
51
+ await blockInput.waitForComputedAllData(BLOB_AVAILABILITY_TIMEOUT).catch(() => {
52
+ this.logger.debug("Failed to wait for computed all data", {slot, blockRoot: blockRootHex});
53
+ });
54
+ }
55
+
47
56
  const {custodyColumns} = this.custodyConfig;
48
57
  const blobsLen = (block.message as fulu.BeaconBlock).body.blobKzgCommitments.length;
49
58
  let dataColumnsLen: number;
@@ -107,12 +107,10 @@ import {SeenAttestationDatas} from "./seenCache/seenAttestationData.js";
107
107
  import {SeenBlockAttesters} from "./seenCache/seenBlockAttesters.js";
108
108
  import {SeenBlockInput} from "./seenCache/seenGossipBlockInput.js";
109
109
  import {ShufflingCache} from "./shufflingCache.js";
110
- import {BlockStateCacheImpl} from "./stateCache/blockStateCacheImpl.js";
111
110
  import {DbCPStateDatastore, checkpointToDatastoreKey} from "./stateCache/datastore/db.js";
112
111
  import {FileCPStateDatastore} from "./stateCache/datastore/file.js";
113
112
  import {CPStateDatastore} from "./stateCache/datastore/types.js";
114
113
  import {FIFOBlockStateCache} from "./stateCache/fifoBlockStateCache.js";
115
- import {InMemoryCheckpointStateCache} from "./stateCache/inMemoryCheckpointsCache.js";
116
114
  import {PersistentCheckpointStateCache} from "./stateCache/persistentCheckpointsCache.js";
117
115
  import {CheckpointStateCache} from "./stateCache/types.js";
118
116
  import {ValidatorMonitor} from "./validatorMonitor.js";
@@ -142,7 +140,7 @@ export class BeaconChain implements IBeaconChain {
142
140
  readonly logger: Logger;
143
141
  readonly metrics: Metrics | null;
144
142
  readonly validatorMonitor: ValidatorMonitor | null;
145
- readonly bufferPool: BufferPool | null;
143
+ readonly bufferPool: BufferPool;
146
144
 
147
145
  readonly anchorStateLatestBlockSlot: Slot;
148
146
 
@@ -339,32 +337,23 @@ export class BeaconChain implements IBeaconChain {
339
337
  this.index2pubkey = index2pubkey;
340
338
 
341
339
  const fileDataStore = opts.nHistoricalStatesFileDataStore ?? true;
342
- const blockStateCache = this.opts.nHistoricalStates
343
- ? new FIFOBlockStateCache(this.opts, {metrics})
344
- : new BlockStateCacheImpl({metrics});
345
- this.bufferPool = this.opts.nHistoricalStates
346
- ? new BufferPool(anchorState.type.tree_serializedSize(anchorState.node), metrics)
347
- : null;
340
+ const blockStateCache = new FIFOBlockStateCache(this.opts, {metrics});
341
+ this.bufferPool = new BufferPool(anchorState.type.tree_serializedSize(anchorState.node), metrics);
348
342
 
349
343
  let checkpointStateCache: CheckpointStateCache;
350
- this.cpStateDatastore = undefined;
351
- if (this.opts.nHistoricalStates) {
352
- this.cpStateDatastore = fileDataStore ? new FileCPStateDatastore(dataDir) : new DbCPStateDatastore(this.db);
353
- checkpointStateCache = new PersistentCheckpointStateCache(
354
- {
355
- config,
356
- metrics,
357
- logger,
358
- clock,
359
- blockStateCache,
360
- bufferPool: this.bufferPool,
361
- datastore: this.cpStateDatastore,
362
- },
363
- this.opts
364
- );
365
- } else {
366
- checkpointStateCache = new InMemoryCheckpointStateCache({metrics});
367
- }
344
+ this.cpStateDatastore = fileDataStore ? new FileCPStateDatastore(dataDir) : new DbCPStateDatastore(this.db);
345
+ checkpointStateCache = new PersistentCheckpointStateCache(
346
+ {
347
+ config,
348
+ metrics,
349
+ logger,
350
+ clock,
351
+ blockStateCache,
352
+ bufferPool: this.bufferPool,
353
+ datastore: this.cpStateDatastore,
354
+ },
355
+ this.opts
356
+ );
368
357
 
369
358
  const {checkpoint} = computeAnchorCheckpoint(config, anchorState);
370
359
  blockStateCache.add(anchorState);
@@ -45,7 +45,6 @@ export type IChainOptions = BlockProcessOpts &
45
45
  broadcastValidationStrictness?: string;
46
46
  minSameMessageSignatureSetsToBatch: number;
47
47
  archiveDateEpochs?: number;
48
- nHistoricalStates?: boolean;
49
48
  nHistoricalStatesFileDataStore?: boolean;
50
49
  };
51
50
 
@@ -119,7 +118,6 @@ export const defaultChainOptions: IChainOptions = {
119
118
  // batching too much may block the I/O thread so if useWorker=false, suggest this value to be 32
120
119
  // since this batch attestation work is designed to work with useWorker=true, make this the lowest value
121
120
  minSameMessageSignatureSetsToBatch: 2,
122
- nHistoricalStates: true,
123
121
  // as of Feb 2025, this option turned out to be very useful:
124
122
  // - it allows to share a persisted checkpoint state to other nodes
125
123
  // - users can prune the persisted checkpoint state files manually to save disc space
@@ -2,7 +2,7 @@ import {routes} from "@lodestar/api";
2
2
  import {ProtoBlock} from "@lodestar/fork-choice";
3
3
  import {CachedBeaconStateAllForks} from "@lodestar/state-transition";
4
4
  import {BeaconBlock, Epoch, RootHex, Slot, phase0} from "@lodestar/types";
5
- import {CheckpointHex} from "../stateCache/index.js";
5
+ import {CheckpointHex} from "../stateCache/types.js";
6
6
 
7
7
  export enum RegenCaller {
8
8
  getDuties = "getDuties",
@@ -5,8 +5,7 @@ import {BeaconBlock, Epoch, RootHex, Slot, phase0} from "@lodestar/types";
5
5
  import {Logger, toRootHex} from "@lodestar/utils";
6
6
  import {Metrics} from "../../metrics/index.js";
7
7
  import {JobItemQueue} from "../../util/queue/index.js";
8
- import {CheckpointHex} from "../stateCache/index.js";
9
- import {BlockStateCache, CheckpointStateCache} from "../stateCache/types.js";
8
+ import {BlockStateCache, CheckpointHex, CheckpointStateCache} from "../stateCache/types.js";
10
9
  import {RegenError, RegenErrorCode} from "./errors.js";
11
10
  import {
12
11
  IStateRegenerator,
@@ -1,3 +1 @@
1
- export * from "./blockStateCacheImpl.js";
2
1
  export * from "./fifoBlockStateCache.js";
3
- export * from "./inMemoryCheckpointsCache.js";
@@ -31,7 +31,7 @@ type PersistentCheckpointStateCacheModules = {
31
31
  signal?: AbortSignal;
32
32
  datastore: CPStateDatastore;
33
33
  blockStateCache: BlockStateCache;
34
- bufferPool?: BufferPool | null;
34
+ bufferPool?: BufferPool;
35
35
  };
36
36
 
37
37
  /** checkpoint serialized as a string */
@@ -119,7 +119,7 @@ export class PersistentCheckpointStateCache implements CheckpointStateCache {
119
119
  private readonly maxEpochsOnDisk: number;
120
120
  private readonly datastore: CPStateDatastore;
121
121
  private readonly blockStateCache: BlockStateCache;
122
- private readonly bufferPool?: BufferPool | null;
122
+ private readonly bufferPool?: BufferPool;
123
123
 
124
124
  constructor(
125
125
  {
@@ -851,6 +851,10 @@ export function toCheckpointHex(checkpoint: phase0.Checkpoint): CheckpointHex {
851
851
  };
852
852
  }
853
853
 
854
+ export function toCheckpointKey(cp: CheckpointHex): string {
855
+ return `${cp.rootHex}:${cp.epoch}`;
856
+ }
857
+
854
858
  function toCacheKey(cp: CheckpointHex | phase0.Checkpoint): CacheKey {
855
859
  if (isCheckpointHex(cp)) {
856
860
  return `${cp.rootHex}_${cp.epoch}`;
@@ -138,11 +138,10 @@ export async function validateGossipBlock(
138
138
  // in forky condition, make sure to populate ShufflingCache with regened state
139
139
  chain.shufflingCache.processState(blockState);
140
140
 
141
- // Extra conditions for merge fork blocks
142
141
  // [REJECT] The block's execution payload timestamp is correct with respect to the slot
143
142
  // -- i.e. execution_payload.timestamp == compute_timestamp_at_slot(state, block.slot).
144
143
  if (isForkPostBellatrix(fork) && !isForkPostGloas(fork)) {
145
- if (!isExecutionBlockBodyType(block.body)) throw Error("Not merge block type");
144
+ if (!isExecutionBlockBodyType(block.body)) throw Error("Not execution block body type");
146
145
  const executionPayload = block.body.executionPayload;
147
146
  if (isExecutionStateType(blockState) && isExecutionEnabled(blockState, block)) {
148
147
  const expectedTimestamp = computeTimeAtSlot(config, blockSlot, chain.genesisTime);
@@ -721,6 +721,17 @@ export class PeerManager {
721
721
  // NOTE: libp2p may emit two "peer:connect" events: One for inbound, one for outbound
722
722
  // If that happens, it's okay. Only the "outbound" connection triggers immediate action
723
723
  const now = Date.now();
724
+
725
+ // Ethereum uses secp256k1 for node IDs, reject peers with other key types
726
+ if (remotePeer.type !== "secp256k1") {
727
+ this.logger.debug("Peer does not have secp256k1 key, disconnecting", {
728
+ peer: remotePeerPrettyStr,
729
+ type: remotePeer.type,
730
+ });
731
+ void this.goodbyeAndDisconnect(remotePeer, GoodByeReasonCode.IRRELEVANT_NETWORK);
732
+ return;
733
+ }
734
+
724
735
  const nodeId = computeNodeId(remotePeer);
725
736
  const peerData: PeerData = {
726
737
  lastReceivedMsgUnixTsMs: direction === "outbound" ? 0 : now,
@@ -579,7 +579,7 @@ function getSequentialHandlers(modules: ValidatorFnsModules, options: GossipHand
579
579
  break;
580
580
  }
581
581
 
582
- if (!blockInput.hasAllData()) {
582
+ if (!blockInput.hasComputedAllData()) {
583
583
  // immediately attempt fetch of data columns from execution engine
584
584
  chain.getBlobsTracker.triggerGetBlobs(blockInput);
585
585
  // if we've received at least half of the columns, trigger reconstruction of the rest
@@ -1,54 +0,0 @@
1
- import { routes } from "@lodestar/api";
2
- import { CachedBeaconStateAllForks } from "@lodestar/state-transition";
3
- import { Epoch, RootHex } from "@lodestar/types";
4
- import { Metrics } from "../../metrics/index.js";
5
- import { BlockStateCache } from "./types.js";
6
- /**
7
- * Old implementation of StateCache (used to call `StateContextCache`)
8
- * - Prune per checkpoint so number of states ranges from 96 to 128
9
- * - Keep a separate head state to make sure it is always available
10
- */
11
- export declare class BlockStateCacheImpl implements BlockStateCache {
12
- /**
13
- * Max number of states allowed in the cache
14
- */
15
- readonly maxStates: number;
16
- private readonly cache;
17
- /** Epoch -> Set<blockRoot> */
18
- private readonly epochIndex;
19
- private readonly metrics;
20
- /**
21
- * Strong reference to prevent head state from being pruned.
22
- * null if head state is being regen and not available at the moment.
23
- */
24
- private head;
25
- constructor({ maxStates, metrics }: {
26
- maxStates?: number;
27
- metrics?: Metrics | null;
28
- });
29
- get(rootHex: RootHex): CachedBeaconStateAllForks | null;
30
- add(item: CachedBeaconStateAllForks): void;
31
- setHeadState(item: CachedBeaconStateAllForks | null): void;
32
- /**
33
- * Get a seed state for state reload.
34
- * This is to conform to the api only as this cache is not used in n-historical state.
35
- * See ./fifoBlockStateCache.ts for implementation
36
- */
37
- getSeedState(): CachedBeaconStateAllForks;
38
- clear(): void;
39
- get size(): number;
40
- /**
41
- * TODO make this more robust.
42
- * Without more thought, this currently breaks our assumptions about recent state availablity
43
- */
44
- prune(headStateRootHex: RootHex): void;
45
- /**
46
- * Prune per finalized epoch.
47
- */
48
- deleteAllBeforeEpoch(finalizedEpoch: Epoch): void;
49
- /** ONLY FOR DEBUGGING PURPOSES. For lodestar debug API */
50
- dumpSummary(): routes.lodestar.StateCacheItem[];
51
- getStates(): IterableIterator<CachedBeaconStateAllForks>;
52
- private deleteAllEpochItems;
53
- }
54
- //# sourceMappingURL=blockStateCacheImpl.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"blockStateCacheImpl.d.ts","sourceRoot":"","sources":["../../../src/chain/stateCache/blockStateCacheImpl.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,MAAM,EAAC,MAAM,eAAe,CAAC;AACrC,OAAO,EAAC,yBAAyB,EAAC,MAAM,4BAA4B,CAAC;AACrE,OAAO,EAAC,KAAK,EAAE,OAAO,EAAC,MAAM,iBAAiB,CAAC;AAE/C,OAAO,EAAC,OAAO,EAAC,MAAM,wBAAwB,CAAC;AAE/C,OAAO,EAAC,eAAe,EAAC,MAAM,YAAY,CAAC;AAI3C;;;;GAIG;AACH,qBAAa,mBAAoB,YAAW,eAAe;IACzD;;OAEG;IACH,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAE3B,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAgD;IACtE,8BAA8B;IAC9B,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAiC;IAC5D,OAAO,CAAC,QAAQ,CAAC,OAAO,CAA2C;IACnE;;;OAGG;IACH,OAAO,CAAC,IAAI,CAAuE;gBAEvE,EAAC,SAAsB,EAAE,OAAO,EAAC,EAAE;QAAC,SAAS,CAAC,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,OAAO,GAAG,IAAI,CAAA;KAAC;IAS7F,GAAG,CAAC,OAAO,EAAE,OAAO,GAAG,yBAAyB,GAAG,IAAI;IAavD,GAAG,CAAC,IAAI,EAAE,yBAAyB,GAAG,IAAI;IAgB1C,YAAY,CAAC,IAAI,EAAE,yBAAyB,GAAG,IAAI,GAAG,IAAI;IAS1D;;;;OAIG;IACH,YAAY,IAAI,yBAAyB;IAIzC,KAAK,IAAI,IAAI;IAKb,IAAI,IAAI,IAAI,MAAM,CAEjB;IAED;;;OAGG;IACH,KAAK,CAAC,gBAAgB,EAAE,OAAO,GAAG,IAAI;IAgBtC;;OAEG;IACH,oBAAoB,CAAC,cAAc,EAAE,KAAK,GAAG,IAAI;IAQjD,0DAA0D;IAC1D,WAAW,IAAI,MAAM,CAAC,QAAQ,CAAC,cAAc,EAAE;IAU/C,SAAS,IAAI,gBAAgB,CAAC,yBAAyB,CAAC;IAIxD,OAAO,CAAC,mBAAmB;CAM5B"}
@@ -1,130 +0,0 @@
1
- import { toRootHex } from "@lodestar/utils";
2
- import { MapTracker } from "./mapMetrics.js";
3
- const MAX_STATES = 3 * 32;
4
- /**
5
- * Old implementation of StateCache (used to call `StateContextCache`)
6
- * - Prune per checkpoint so number of states ranges from 96 to 128
7
- * - Keep a separate head state to make sure it is always available
8
- */
9
- export class BlockStateCacheImpl {
10
- /**
11
- * Max number of states allowed in the cache
12
- */
13
- maxStates;
14
- cache;
15
- /** Epoch -> Set<blockRoot> */
16
- epochIndex = new Map();
17
- metrics;
18
- /**
19
- * Strong reference to prevent head state from being pruned.
20
- * null if head state is being regen and not available at the moment.
21
- */
22
- head = null;
23
- constructor({ maxStates = MAX_STATES, metrics }) {
24
- this.maxStates = maxStates;
25
- this.cache = new MapTracker(metrics?.stateCache);
26
- if (metrics) {
27
- this.metrics = metrics.stateCache;
28
- metrics.stateCache.size.addCollect(() => metrics.stateCache.size.set(this.cache.size));
29
- }
30
- }
31
- get(rootHex) {
32
- this.metrics?.lookups.inc();
33
- const item = this.head?.stateRoot === rootHex ? this.head.state : this.cache.get(rootHex);
34
- if (!item) {
35
- return null;
36
- }
37
- this.metrics?.hits.inc();
38
- this.metrics?.stateClonedCount.observe(item.clonedCount);
39
- return item;
40
- }
41
- add(item) {
42
- const key = toRootHex(item.hashTreeRoot());
43
- if (this.cache.get(key)) {
44
- return;
45
- }
46
- this.metrics?.adds.inc();
47
- this.cache.set(key, item);
48
- const epoch = item.epochCtx.epoch;
49
- const blockRoots = this.epochIndex.get(epoch);
50
- if (blockRoots) {
51
- blockRoots.add(key);
52
- }
53
- else {
54
- this.epochIndex.set(epoch, new Set([key]));
55
- }
56
- }
57
- setHeadState(item) {
58
- if (item) {
59
- const key = toRootHex(item.hashTreeRoot());
60
- this.head = { state: item, stateRoot: key };
61
- }
62
- else {
63
- this.head = null;
64
- }
65
- }
66
- /**
67
- * Get a seed state for state reload.
68
- * This is to conform to the api only as this cache is not used in n-historical state.
69
- * See ./fifoBlockStateCache.ts for implementation
70
- */
71
- getSeedState() {
72
- throw Error("Not implemented for BlockStateCacheImpl");
73
- }
74
- clear() {
75
- this.cache.clear();
76
- this.epochIndex.clear();
77
- }
78
- get size() {
79
- return this.cache.size;
80
- }
81
- /**
82
- * TODO make this more robust.
83
- * Without more thought, this currently breaks our assumptions about recent state availablity
84
- */
85
- prune(headStateRootHex) {
86
- const keys = Array.from(this.cache.keys());
87
- if (keys.length > this.maxStates) {
88
- // object keys are stored in insertion order, delete keys starting from the front
89
- for (const key of keys.slice(0, keys.length - this.maxStates)) {
90
- if (key !== headStateRootHex) {
91
- const item = this.cache.get(key);
92
- if (item) {
93
- this.epochIndex.get(item.epochCtx.epoch)?.delete(key);
94
- this.cache.delete(key);
95
- }
96
- }
97
- }
98
- }
99
- }
100
- /**
101
- * Prune per finalized epoch.
102
- */
103
- deleteAllBeforeEpoch(finalizedEpoch) {
104
- for (const epoch of this.epochIndex.keys()) {
105
- if (epoch < finalizedEpoch) {
106
- this.deleteAllEpochItems(epoch);
107
- }
108
- }
109
- }
110
- /** ONLY FOR DEBUGGING PURPOSES. For lodestar debug API */
111
- dumpSummary() {
112
- return Array.from(this.cache.entries()).map(([key, state]) => ({
113
- slot: state.slot,
114
- root: toRootHex(state.hashTreeRoot()),
115
- reads: this.cache.readCount.get(key) ?? 0,
116
- lastRead: this.cache.lastRead.get(key) ?? 0,
117
- checkpointState: false,
118
- }));
119
- }
120
- getStates() {
121
- return this.cache.values();
122
- }
123
- deleteAllEpochItems(epoch) {
124
- for (const rootHex of this.epochIndex.get(epoch) || []) {
125
- this.cache.delete(rootHex);
126
- }
127
- this.epochIndex.delete(epoch);
128
- }
129
- }
130
- //# sourceMappingURL=blockStateCacheImpl.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"blockStateCacheImpl.js","sourceRoot":"","sources":["../../../src/chain/stateCache/blockStateCacheImpl.ts"],"names":[],"mappings":"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"}
@@ -1,60 +0,0 @@
1
- import { routes } from "@lodestar/api";
2
- import { CachedBeaconStateAllForks } from "@lodestar/state-transition";
3
- import { Epoch, RootHex, phase0 } from "@lodestar/types";
4
- import { Metrics } from "../../metrics/index.js";
5
- import { CheckpointStateCache } from "./types.js";
6
- export type CheckpointHex = {
7
- epoch: Epoch;
8
- rootHex: RootHex;
9
- };
10
- /**
11
- * In memory cache of CachedBeaconState
12
- * belonging to checkpoint
13
- *
14
- * Similar API to Repository
15
- */
16
- export declare class InMemoryCheckpointStateCache implements CheckpointStateCache {
17
- private readonly cache;
18
- /** Epoch -> Set<blockRoot> */
19
- private readonly epochIndex;
20
- /**
21
- * Max number of epochs allowed in the cache
22
- */
23
- private readonly maxEpochs;
24
- private readonly metrics;
25
- private preComputedCheckpoint;
26
- private preComputedCheckpointHits;
27
- constructor({ metrics }: {
28
- metrics?: Metrics | null;
29
- }, { maxEpochs }?: {
30
- maxEpochs?: number;
31
- });
32
- getOrReload(cp: CheckpointHex): Promise<CachedBeaconStateAllForks | null>;
33
- getStateOrBytes(cp: CheckpointHex): Promise<Uint8Array | CachedBeaconStateAllForks | null>;
34
- getOrReloadLatest(rootHex: string, maxEpoch: number): Promise<CachedBeaconStateAllForks | null>;
35
- processState(): Promise<number>;
36
- get(cp: CheckpointHex): CachedBeaconStateAllForks | null;
37
- add(cp: phase0.Checkpoint, item: CachedBeaconStateAllForks): void;
38
- /**
39
- * Searches for the latest cached state with a `root`, starting with `epoch` and descending
40
- */
41
- getLatest(rootHex: RootHex, maxEpoch: Epoch): CachedBeaconStateAllForks | null;
42
- /**
43
- * Update the precomputed checkpoint and return the number of his for the
44
- * previous one (if any).
45
- */
46
- updatePreComputedCheckpoint(rootHex: RootHex, epoch: Epoch): number | null;
47
- pruneFinalized(finalizedEpoch: Epoch): void;
48
- prune(finalizedEpoch: Epoch, justifiedEpoch: Epoch): void;
49
- delete(cp: phase0.Checkpoint): void;
50
- deleteAllEpochItems(epoch: Epoch): void;
51
- clear(): void;
52
- /** ONLY FOR DEBUGGING PURPOSES. For lodestar debug API */
53
- dumpSummary(): routes.lodestar.StateCacheItem[];
54
- getStates(): IterableIterator<CachedBeaconStateAllForks>;
55
- /** ONLY FOR DEBUGGING PURPOSES. For spec tests on error */
56
- dumpCheckpointKeys(): string[];
57
- }
58
- export declare function toCheckpointHex(checkpoint: phase0.Checkpoint): CheckpointHex;
59
- export declare function toCheckpointKey(cp: CheckpointHex): string;
60
- //# sourceMappingURL=inMemoryCheckpointsCache.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"inMemoryCheckpointsCache.d.ts","sourceRoot":"","sources":["../../../src/chain/stateCache/inMemoryCheckpointsCache.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,MAAM,EAAC,MAAM,eAAe,CAAC;AACrC,OAAO,EAAC,yBAAyB,EAAC,MAAM,4BAA4B,CAAC;AACrE,OAAO,EAAC,KAAK,EAAE,OAAO,EAAE,MAAM,EAAC,MAAM,iBAAiB,CAAC;AAEvD,OAAO,EAAC,OAAO,EAAC,MAAM,wBAAwB,CAAC;AAE/C,OAAO,EAAgB,oBAAoB,EAAC,MAAM,YAAY,CAAC;AAE/D,MAAM,MAAM,aAAa,GAAG;IAAC,KAAK,EAAE,KAAK,CAAC;IAAC,OAAO,EAAE,OAAO,CAAA;CAAC,CAAC;AAG7D;;;;;GAKG;AACH,qBAAa,4BAA6B,YAAW,oBAAoB;IACvE,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAgD;IACtE,8BAA8B;IAC9B,OAAO,CAAC,QAAQ,CAAC,UAAU,CAA2D;IACtF;;OAEG;IACH,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;IACnC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAA6C;IACrE,OAAO,CAAC,qBAAqB,CAAuB;IACpD,OAAO,CAAC,yBAAyB,CAAuB;gBAE5C,EAAC,OAAc,EAAC,EAAE;QAAC,OAAO,CAAC,EAAE,OAAO,GAAG,IAAI,CAAA;KAAC,EAAE,EAAC,SAAsB,EAAC,GAAE;QAAC,SAAS,CAAC,EAAE,MAAM,CAAA;KAAM;IAcvG,WAAW,CAAC,EAAE,EAAE,aAAa,GAAG,OAAO,CAAC,yBAAyB,GAAG,IAAI,CAAC;IAIzE,eAAe,CAAC,EAAE,EAAE,aAAa,GAAG,OAAO,CAAC,UAAU,GAAG,yBAAyB,GAAG,IAAI,CAAC;IAI1F,iBAAiB,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,yBAAyB,GAAG,IAAI,CAAC;IAI/F,YAAY,IAAI,OAAO,CAAC,MAAM,CAAC;IAKrC,GAAG,CAAC,EAAE,EAAE,aAAa,GAAG,yBAAyB,GAAG,IAAI;IAoBxD,GAAG,CAAC,EAAE,EAAE,MAAM,CAAC,UAAU,EAAE,IAAI,EAAE,yBAAyB,GAAG,IAAI;IAWjE;;OAEG;IACH,SAAS,CAAC,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,KAAK,GAAG,yBAAyB,GAAG,IAAI;IAa9E;;;OAGG;IACH,2BAA2B,CAAC,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,GAAG,MAAM,GAAG,IAAI;IAO1E,cAAc,CAAC,cAAc,EAAE,KAAK,GAAG,IAAI;IAQ3C,KAAK,CAAC,cAAc,EAAE,KAAK,EAAE,cAAc,EAAE,KAAK,GAAG,IAAI;IAWzD,MAAM,CAAC,EAAE,EAAE,MAAM,CAAC,UAAU,GAAG,IAAI;IAYnC,mBAAmB,CAAC,KAAK,EAAE,KAAK,GAAG,IAAI;IAOvC,KAAK,IAAI,IAAI;IAKb,0DAA0D;IAC1D,WAAW,IAAI,MAAM,CAAC,QAAQ,CAAC,cAAc,EAAE;IAU/C,SAAS,IAAI,gBAAgB,CAAC,yBAAyB,CAAC;IAIxD,2DAA2D;IAC3D,kBAAkB,IAAI,MAAM,EAAE;CAG/B;AAED,wBAAgB,eAAe,CAAC,UAAU,EAAE,MAAM,CAAC,UAAU,GAAG,aAAa,CAK5E;AAED,wBAAgB,eAAe,CAAC,EAAE,EAAE,aAAa,GAAG,MAAM,CAEzD"}