@lodestar/beacon-node 1.36.0-dev.793f92c091 → 1.36.0-dev.88fbac9fcf

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 (214) hide show
  1. package/lib/api/impl/config/constants.d.ts +4 -2
  2. package/lib/api/impl/config/constants.d.ts.map +1 -1
  3. package/lib/api/impl/config/constants.js +6 -3
  4. package/lib/api/impl/config/constants.js.map +1 -1
  5. package/lib/api/impl/lodestar/index.d.ts +5 -0
  6. package/lib/api/impl/lodestar/index.d.ts.map +1 -1
  7. package/lib/api/impl/lodestar/index.js +35 -10
  8. package/lib/api/impl/lodestar/index.js.map +1 -1
  9. package/lib/api/impl/node/utils.js +1 -1
  10. package/lib/api/impl/node/utils.js.map +1 -1
  11. package/lib/api/impl/validator/index.d.ts.map +1 -1
  12. package/lib/api/impl/validator/index.js +2 -2
  13. package/lib/api/impl/validator/index.js.map +1 -1
  14. package/lib/chain/archiveStore/archiveStore.d.ts +9 -0
  15. package/lib/chain/archiveStore/archiveStore.d.ts.map +1 -1
  16. package/lib/chain/archiveStore/archiveStore.js +24 -0
  17. package/lib/chain/archiveStore/archiveStore.js.map +1 -1
  18. package/lib/chain/archiveStore/strategies/frequencyStateArchiveStrategy.d.ts +7 -0
  19. package/lib/chain/archiveStore/strategies/frequencyStateArchiveStrategy.d.ts.map +1 -1
  20. package/lib/chain/archiveStore/strategies/frequencyStateArchiveStrategy.js +31 -5
  21. package/lib/chain/archiveStore/strategies/frequencyStateArchiveStrategy.js.map +1 -1
  22. package/lib/chain/beaconProposerCache.d.ts +3 -0
  23. package/lib/chain/beaconProposerCache.d.ts.map +1 -1
  24. package/lib/chain/beaconProposerCache.js +4 -6
  25. package/lib/chain/beaconProposerCache.js.map +1 -1
  26. package/lib/chain/chain.d.ts +5 -2
  27. package/lib/chain/chain.d.ts.map +1 -1
  28. package/lib/chain/chain.js +34 -18
  29. package/lib/chain/chain.js.map +1 -1
  30. package/lib/chain/errors/dataColumnSidecarError.d.ts +7 -0
  31. package/lib/chain/errors/dataColumnSidecarError.d.ts.map +1 -1
  32. package/lib/chain/errors/dataColumnSidecarError.js +1 -0
  33. package/lib/chain/errors/dataColumnSidecarError.js.map +1 -1
  34. package/lib/chain/errors/voluntaryExitError.d.ts +16 -2
  35. package/lib/chain/errors/voluntaryExitError.d.ts.map +1 -1
  36. package/lib/chain/errors/voluntaryExitError.js +22 -1
  37. package/lib/chain/errors/voluntaryExitError.js.map +1 -1
  38. package/lib/chain/forkChoice/index.d.ts +9 -1
  39. package/lib/chain/forkChoice/index.d.ts.map +1 -1
  40. package/lib/chain/forkChoice/index.js +109 -4
  41. package/lib/chain/forkChoice/index.js.map +1 -1
  42. package/lib/chain/interface.d.ts +2 -0
  43. package/lib/chain/interface.d.ts.map +1 -1
  44. package/lib/chain/lightClient/index.d.ts +3 -0
  45. package/lib/chain/lightClient/index.d.ts.map +1 -1
  46. package/lib/chain/lightClient/index.js +24 -13
  47. package/lib/chain/lightClient/index.js.map +1 -1
  48. package/lib/chain/opPools/syncContributionAndProofPool.d.ts +3 -1
  49. package/lib/chain/opPools/syncContributionAndProofPool.d.ts.map +1 -1
  50. package/lib/chain/opPools/syncContributionAndProofPool.js +4 -3
  51. package/lib/chain/opPools/syncContributionAndProofPool.js.map +1 -1
  52. package/lib/chain/options.d.ts +0 -2
  53. package/lib/chain/options.d.ts.map +1 -1
  54. package/lib/chain/options.js +0 -1
  55. package/lib/chain/options.js.map +1 -1
  56. package/lib/chain/stateCache/datastore/db.d.ts +12 -0
  57. package/lib/chain/stateCache/datastore/db.d.ts.map +1 -1
  58. package/lib/chain/stateCache/datastore/db.js +70 -0
  59. package/lib/chain/stateCache/datastore/db.js.map +1 -1
  60. package/lib/chain/stateCache/datastore/file.d.ts +1 -0
  61. package/lib/chain/stateCache/datastore/file.d.ts.map +1 -1
  62. package/lib/chain/stateCache/datastore/file.js +7 -0
  63. package/lib/chain/stateCache/datastore/file.js.map +1 -1
  64. package/lib/chain/stateCache/datastore/types.d.ts +1 -0
  65. package/lib/chain/stateCache/datastore/types.d.ts.map +1 -1
  66. package/lib/chain/validation/attestation.d.ts.map +1 -1
  67. package/lib/chain/validation/attestation.js +6 -7
  68. package/lib/chain/validation/attestation.js.map +1 -1
  69. package/lib/chain/validation/block.d.ts.map +1 -1
  70. package/lib/chain/validation/block.js +1 -2
  71. package/lib/chain/validation/block.js.map +1 -1
  72. package/lib/chain/validation/dataColumnSidecar.d.ts.map +1 -1
  73. package/lib/chain/validation/dataColumnSidecar.js +14 -3
  74. package/lib/chain/validation/dataColumnSidecar.js.map +1 -1
  75. package/lib/chain/validation/lightClientOptimisticUpdate.d.ts.map +1 -1
  76. package/lib/chain/validation/lightClientOptimisticUpdate.js +1 -2
  77. package/lib/chain/validation/lightClientOptimisticUpdate.js.map +1 -1
  78. package/lib/chain/validation/voluntaryExit.d.ts.map +1 -1
  79. package/lib/chain/validation/voluntaryExit.js +5 -4
  80. package/lib/chain/validation/voluntaryExit.js.map +1 -1
  81. package/lib/constants/constants.d.ts +0 -5
  82. package/lib/constants/constants.d.ts.map +1 -1
  83. package/lib/constants/constants.js +0 -5
  84. package/lib/constants/constants.js.map +1 -1
  85. package/lib/constants/network.d.ts +0 -14
  86. package/lib/constants/network.d.ts.map +1 -1
  87. package/lib/constants/network.js +0 -15
  88. package/lib/constants/network.js.map +1 -1
  89. package/lib/index.d.ts +2 -0
  90. package/lib/index.d.ts.map +1 -1
  91. package/lib/index.js +2 -0
  92. package/lib/index.js.map +1 -1
  93. package/lib/metrics/metrics/lodestar.d.ts +11 -0
  94. package/lib/metrics/metrics/lodestar.d.ts.map +1 -1
  95. package/lib/metrics/metrics/lodestar.js +19 -1
  96. package/lib/metrics/metrics/lodestar.js.map +1 -1
  97. package/lib/network/core/networkCore.d.ts.map +1 -1
  98. package/lib/network/core/networkCore.js +5 -1
  99. package/lib/network/core/networkCore.js.map +1 -1
  100. package/lib/network/core/networkCoreWorker.js +2 -7
  101. package/lib/network/core/networkCoreWorker.js.map +1 -1
  102. package/lib/network/discv5/utils.d.ts.map +1 -1
  103. package/lib/network/discv5/utils.js +1 -2
  104. package/lib/network/discv5/utils.js.map +1 -1
  105. package/lib/network/discv5/worker.js +2 -7
  106. package/lib/network/discv5/worker.js.map +1 -1
  107. package/lib/network/gossip/encoding.js +1 -1
  108. package/lib/network/gossip/encoding.js.map +1 -1
  109. package/lib/network/gossip/gossipsub.d.ts.map +1 -1
  110. package/lib/network/gossip/gossipsub.js +1 -7
  111. package/lib/network/gossip/gossipsub.js.map +1 -1
  112. package/lib/network/gossip/snappy_bun.d.ts +3 -0
  113. package/lib/network/gossip/snappy_bun.d.ts.map +1 -0
  114. package/lib/network/gossip/snappy_bun.js +3 -0
  115. package/lib/network/gossip/snappy_bun.js.map +1 -0
  116. package/lib/network/metadata.d.ts +1 -1
  117. package/lib/network/metadata.d.ts.map +1 -1
  118. package/lib/network/metadata.js +1 -0
  119. package/lib/network/metadata.js.map +1 -1
  120. package/lib/network/network.d.ts.map +1 -1
  121. package/lib/network/network.js +1 -0
  122. package/lib/network/network.js.map +1 -1
  123. package/lib/network/options.d.ts +0 -1
  124. package/lib/network/options.d.ts.map +1 -1
  125. package/lib/network/options.js.map +1 -1
  126. package/lib/network/processor/gossipHandlers.d.ts.map +1 -1
  127. package/lib/network/processor/gossipHandlers.js +14 -8
  128. package/lib/network/processor/gossipHandlers.js.map +1 -1
  129. package/lib/network/reqresp/handlers/beaconBlocksByRange.d.ts.map +1 -1
  130. package/lib/network/reqresp/handlers/beaconBlocksByRange.js +3 -1
  131. package/lib/network/reqresp/handlers/beaconBlocksByRange.js.map +1 -1
  132. package/lib/network/reqresp/handlers/blobSidecarsByRange.js +5 -5
  133. package/lib/network/reqresp/handlers/blobSidecarsByRange.js.map +1 -1
  134. package/lib/network/reqresp/handlers/blobSidecarsByRoot.js +4 -4
  135. package/lib/network/reqresp/handlers/blobSidecarsByRoot.js.map +1 -1
  136. package/lib/network/subnets/attnetsService.d.ts +1 -1
  137. package/lib/network/subnets/attnetsService.js +6 -6
  138. package/lib/network/subnets/attnetsService.js.map +1 -1
  139. package/lib/network/subnets/util.d.ts +3 -2
  140. package/lib/network/subnets/util.d.ts.map +1 -1
  141. package/lib/network/subnets/util.js +6 -6
  142. package/lib/network/subnets/util.js.map +1 -1
  143. package/lib/node/nodejs.d.ts +2 -1
  144. package/lib/node/nodejs.d.ts.map +1 -1
  145. package/lib/node/nodejs.js +2 -1
  146. package/lib/node/nodejs.js.map +1 -1
  147. package/lib/sync/range/range.d.ts.map +1 -1
  148. package/lib/sync/range/range.js +2 -1
  149. package/lib/sync/range/range.js.map +1 -1
  150. package/lib/sync/utils/remoteSyncType.d.ts +2 -1
  151. package/lib/sync/utils/remoteSyncType.d.ts.map +1 -1
  152. package/lib/sync/utils/remoteSyncType.js +19 -4
  153. package/lib/sync/utils/remoteSyncType.js.map +1 -1
  154. package/lib/util/clock.d.ts.map +1 -1
  155. package/lib/util/clock.js +3 -4
  156. package/lib/util/clock.js.map +1 -1
  157. package/lib/util/profile.d.ts +6 -4
  158. package/lib/util/profile.d.ts.map +1 -1
  159. package/lib/util/profile.js +40 -3
  160. package/lib/util/profile.js.map +1 -1
  161. package/lib/util/sszBytes.d.ts +2 -0
  162. package/lib/util/sszBytes.d.ts.map +1 -1
  163. package/lib/util/sszBytes.js +25 -0
  164. package/lib/util/sszBytes.js.map +1 -1
  165. package/package.json +31 -24
  166. package/src/api/impl/config/constants.ts +10 -4
  167. package/src/api/impl/lodestar/index.ts +42 -10
  168. package/src/api/impl/node/utils.ts +1 -1
  169. package/src/api/impl/validator/index.ts +5 -2
  170. package/src/chain/archiveStore/archiveStore.ts +27 -0
  171. package/src/chain/archiveStore/strategies/frequencyStateArchiveStrategy.ts +32 -5
  172. package/src/chain/beaconProposerCache.ts +4 -8
  173. package/src/chain/chain.ts +50 -25
  174. package/src/chain/errors/dataColumnSidecarError.ts +8 -0
  175. package/src/chain/errors/voluntaryExitError.ts +30 -2
  176. package/src/chain/forkChoice/index.ts +178 -2
  177. package/src/chain/interface.ts +2 -0
  178. package/src/chain/lightClient/index.ts +26 -12
  179. package/src/chain/opPools/syncContributionAndProofPool.ts +3 -2
  180. package/src/chain/options.ts +0 -3
  181. package/src/chain/stateCache/datastore/db.ts +89 -1
  182. package/src/chain/stateCache/datastore/file.ts +8 -0
  183. package/src/chain/stateCache/datastore/types.ts +1 -0
  184. package/src/chain/validation/attestation.ts +6 -7
  185. package/src/chain/validation/block.ts +1 -2
  186. package/src/chain/validation/dataColumnSidecar.ts +21 -4
  187. package/src/chain/validation/lightClientOptimisticUpdate.ts +3 -2
  188. package/src/chain/validation/voluntaryExit.ts +14 -4
  189. package/src/constants/constants.ts +0 -6
  190. package/src/constants/network.ts +0 -19
  191. package/src/index.ts +2 -0
  192. package/src/metrics/metrics/lodestar.ts +22 -1
  193. package/src/network/core/networkCore.ts +5 -1
  194. package/src/network/core/networkCoreWorker.ts +2 -7
  195. package/src/network/discv5/utils.ts +1 -2
  196. package/src/network/discv5/worker.ts +2 -7
  197. package/src/network/gossip/encoding.ts +1 -1
  198. package/src/network/gossip/gossipsub.ts +1 -10
  199. package/src/network/gossip/snappy_bun.ts +2 -0
  200. package/src/network/metadata.ts +3 -1
  201. package/src/network/network.ts +1 -0
  202. package/src/network/options.ts +0 -1
  203. package/src/network/processor/gossipHandlers.ts +16 -7
  204. package/src/network/reqresp/handlers/beaconBlocksByRange.ts +3 -1
  205. package/src/network/reqresp/handlers/blobSidecarsByRange.ts +6 -6
  206. package/src/network/reqresp/handlers/blobSidecarsByRoot.ts +5 -5
  207. package/src/network/subnets/attnetsService.ts +6 -6
  208. package/src/network/subnets/util.ts +12 -12
  209. package/src/node/nodejs.ts +3 -0
  210. package/src/sync/range/range.ts +2 -1
  211. package/src/sync/utils/remoteSyncType.ts +23 -4
  212. package/src/util/clock.ts +3 -4
  213. package/src/util/profile.ts +45 -3
  214. package/src/util/sszBytes.ts +30 -0
@@ -126,7 +126,7 @@ export class MetadataController {
126
126
  * 2. Network MUST call this method on fork transition.
127
127
  * Current Clock implementation ensures no race conditions, epoch is correct if re-fetched
128
128
  */
129
- updateEth2Field(epoch: Epoch): void {
129
+ updateEth2Field(epoch: Epoch): phase0.ENRForkID {
130
130
  const config = this.networkConfig.config;
131
131
  const enrForkId = getENRForkID(config, epoch);
132
132
  const {forkDigest, nextForkVersion, nextForkEpoch} = enrForkId;
@@ -143,6 +143,8 @@ export class MetadataController {
143
143
  : ssz.ForkDigest.defaultValue();
144
144
  this.onSetValue(ENRKey.nfd, nextForkDigest);
145
145
  this.logger.debug("Updated nfd field in ENR", {nextForkDigest: toHex(nextForkDigest)});
146
+
147
+ return enrForkId;
146
148
  }
147
149
  }
148
150
 
@@ -236,6 +236,7 @@ export class Network implements INetwork {
236
236
  this.chain.emitter.off(routes.events.EventType.lightClientOptimisticUpdate, this.onLightClientOptimisticUpdate);
237
237
  this.chain.emitter.off(ChainEvent.updateTargetCustodyGroupCount, this.onTargetGroupCountUpdated);
238
238
  this.chain.emitter.off(ChainEvent.publishDataColumns, this.onPublishDataColumns);
239
+ this.chain.emitter.off(ChainEvent.publishBlobSidecars, this.onPublishBlobSidecars);
239
240
  this.chain.emitter.off(ChainEvent.updateStatus, this.onUpdateStatus);
240
241
  await this.core.close();
241
242
 
@@ -23,7 +23,6 @@ export interface NetworkOptions
23
23
  useWorker?: boolean;
24
24
  maxYoungGenerationSizeMb?: number;
25
25
  disableLightClientServer?: boolean;
26
- supernode?: boolean;
27
26
 
28
27
  /**
29
28
  * During E2E tests observe a lot of following `missing stream`:
@@ -39,6 +39,7 @@ import {
39
39
  BlockError,
40
40
  BlockErrorCode,
41
41
  BlockGossipError,
42
+ DataColumnSidecarErrorCode,
42
43
  DataColumnSidecarGossipError,
43
44
  GossipAction,
44
45
  GossipActionError,
@@ -304,7 +305,11 @@ function getSequentialHandlers(modules: ValidatorFnsModules, options: GossipHand
304
305
  ...blockInput.getLogMeta(),
305
306
  index: dataColumnSidecar.index,
306
307
  });
307
- return blockInput;
308
+ throw new DataColumnSidecarGossipError(GossipAction.IGNORE, {
309
+ code: DataColumnSidecarErrorCode.ALREADY_KNOWN,
310
+ columnIdx: dataColumnSidecar.index,
311
+ slot,
312
+ });
308
313
  }
309
314
  }
310
315
 
@@ -556,6 +561,16 @@ function getSequentialHandlers(modules: ValidatorFnsModules, options: GossipHand
556
561
  metrics?.dataColumns.elapsedTimeTillReceived.observe({receivedOrder: receivedColumns}, delaySec);
557
562
  break;
558
563
  }
564
+
565
+ if (!blockInput.hasAllData()) {
566
+ // immediately attempt fetch of data columns from execution engine
567
+ chain.getBlobsTracker.triggerGetBlobs(blockInput);
568
+ // if we've received at least half of the columns, trigger reconstruction of the rest
569
+ if (blockInput.columnCount >= NUMBER_OF_COLUMNS / 2) {
570
+ chain.columnReconstructionTracker.triggerColumnReconstruction(blockInput);
571
+ }
572
+ }
573
+
559
574
  if (!blockInput.hasBlockAndAllData()) {
560
575
  const cutoffTimeMs = getCutoffTimeMs(chain, dataColumnSlot, BLOCK_AVAILABILITY_CUTOFF_MS);
561
576
  chain.logger.debug("Received gossip data column, waiting for full data availability", {
@@ -578,12 +593,6 @@ function getSequentialHandlers(modules: ValidatorFnsModules, options: GossipHand
578
593
  source: BlockInputSource.gossip,
579
594
  });
580
595
  });
581
- // immediately attempt fetch of data columns from execution engine
582
- chain.getBlobsTracker.triggerGetBlobs(blockInput);
583
- // if we've received at least half of the columns, trigger reconstruction of the rest
584
- if (blockInput.columnCount >= NUMBER_OF_COLUMNS / 2) {
585
- chain.columnReconstructionTracker.triggerColumnReconstruction(blockInput);
586
- }
587
596
  }
588
597
  },
589
598
 
@@ -19,7 +19,9 @@ export async function* onBeaconBlocksByRange(
19
19
 
20
20
  const finalized = db.blockArchive;
21
21
  const unfinalized = db.block;
22
- const finalizedSlot = chain.forkChoice.getFinalizedBlock().slot;
22
+ // in the case of initializing from a non-finalized state, we don't have the finalized block so this api does not work
23
+ // chain.forkChoice.getFinalizeBlock().slot
24
+ const finalizedSlot = chain.forkChoice.getFinalizedCheckpointSlot();
23
25
 
24
26
  // Finalized range of blocks
25
27
  if (startSlot <= finalizedSlot) {
@@ -1,5 +1,5 @@
1
1
  import {ChainConfig} from "@lodestar/config";
2
- import {BLOBSIDECAR_FIXED_SIZE, GENESIS_SLOT} from "@lodestar/params";
2
+ import {BLOB_SIDECAR_FIXED_SIZE, GENESIS_SLOT} from "@lodestar/params";
3
3
  import {RespStatus, ResponseError, ResponseOutgoing} from "@lodestar/reqresp";
4
4
  import {computeEpochAtSlot} from "@lodestar/state-transition";
5
5
  import {Slot, deneb} from "@lodestar/types";
@@ -71,17 +71,17 @@ export function* iterateBlobBytesFromWrapper(
71
71
  blockSlot: Slot
72
72
  ): Iterable<ResponseOutgoing> {
73
73
  const allBlobSideCarsBytes = blobSideCarsBytesWrapped.slice(BLOB_SIDECARS_IN_WRAPPER_INDEX);
74
- const blobsLen = allBlobSideCarsBytes.length / BLOBSIDECAR_FIXED_SIZE;
74
+ const blobsLen = allBlobSideCarsBytes.length / BLOB_SIDECAR_FIXED_SIZE;
75
75
 
76
76
  for (let index = 0; index < blobsLen; index++) {
77
77
  const blobSideCarBytes = allBlobSideCarsBytes.slice(
78
- index * BLOBSIDECAR_FIXED_SIZE,
79
- (index + 1) * BLOBSIDECAR_FIXED_SIZE
78
+ index * BLOB_SIDECAR_FIXED_SIZE,
79
+ (index + 1) * BLOB_SIDECAR_FIXED_SIZE
80
80
  );
81
- if (blobSideCarBytes.length !== BLOBSIDECAR_FIXED_SIZE) {
81
+ if (blobSideCarBytes.length !== BLOB_SIDECAR_FIXED_SIZE) {
82
82
  throw new ResponseError(
83
83
  RespStatus.SERVER_ERROR,
84
- `Invalid blobSidecar index=${index} bytes length=${blobSideCarBytes.length} expected=${BLOBSIDECAR_FIXED_SIZE} for slot ${blockSlot} blobsLen=${blobsLen}`
84
+ `Invalid blobSidecar index=${index} bytes length=${blobSideCarBytes.length} expected=${BLOB_SIDECAR_FIXED_SIZE} for slot ${blockSlot} blobsLen=${blobsLen}`
85
85
  );
86
86
  }
87
87
  yield {
@@ -1,4 +1,4 @@
1
- import {BLOBSIDECAR_FIXED_SIZE} from "@lodestar/params";
1
+ import {BLOB_SIDECAR_FIXED_SIZE} from "@lodestar/params";
2
2
  import {RespStatus, ResponseError, ResponseOutgoing} from "@lodestar/reqresp";
3
3
  import {computeEpochAtSlot} from "@lodestar/state-transition";
4
4
  import {RootHex} from "@lodestar/types";
@@ -45,12 +45,12 @@ export async function* onBlobSidecarsByRoot(
45
45
  }
46
46
 
47
47
  const blobSidecarBytes = lastFetchedSideCars.bytes.slice(
48
- index * BLOBSIDECAR_FIXED_SIZE,
49
- (index + 1) * BLOBSIDECAR_FIXED_SIZE
48
+ index * BLOB_SIDECAR_FIXED_SIZE,
49
+ (index + 1) * BLOB_SIDECAR_FIXED_SIZE
50
50
  );
51
- if (blobSidecarBytes.length !== BLOBSIDECAR_FIXED_SIZE) {
51
+ if (blobSidecarBytes.length !== BLOB_SIDECAR_FIXED_SIZE) {
52
52
  throw Error(
53
- `Inconsistent state, blobSidecar blockRoot=${blockRootHex} index=${index} blobSidecarBytes=${blobSidecarBytes.length} expected=${BLOBSIDECAR_FIXED_SIZE}`
53
+ `Inconsistent state, blobSidecar blockRoot=${blockRootHex} index=${index} blobSidecarBytes=${blobSidecarBytes.length} expected=${BLOB_SIDECAR_FIXED_SIZE}`
54
54
  );
55
55
  }
56
56
 
@@ -1,5 +1,5 @@
1
1
  import {BeaconConfig, ForkBoundary} from "@lodestar/config";
2
- import {ATTESTATION_SUBNET_COUNT, EPOCHS_PER_RANDOM_SUBNET_SUBSCRIPTION, SLOTS_PER_EPOCH} from "@lodestar/params";
2
+ import {ATTESTATION_SUBNET_COUNT, SLOTS_PER_EPOCH} from "@lodestar/params";
3
3
  import {computeEpochAtSlot} from "@lodestar/state-transition";
4
4
  import {Epoch, Slot, SubnetID, ssz} from "@lodestar/types";
5
5
  import {Logger, MapDef} from "@lodestar/utils";
@@ -92,8 +92,8 @@ export class AttnetsService implements IAttnetsService {
92
92
  const shortLivedSubnets = this.committeeSubnets.getActiveTtl(this.clock.currentSlot);
93
93
 
94
94
  const longLivedSubscriptionsToSlot =
95
- (Math.floor(this.clock.currentEpoch / EPOCHS_PER_RANDOM_SUBNET_SUBSCRIPTION) + 1) *
96
- EPOCHS_PER_RANDOM_SUBNET_SUBSCRIPTION *
95
+ (Math.floor(this.clock.currentEpoch / this.config.EPOCHS_PER_SUBNET_SUBSCRIPTION) + 1) *
96
+ this.config.EPOCHS_PER_SUBNET_SUBSCRIPTION *
97
97
  SLOTS_PER_EPOCH;
98
98
  const longLivedSubnets = Array.from(this.longLivedSubscriptions).map((subnet) => ({
99
99
  subnet,
@@ -234,11 +234,11 @@ export class AttnetsService implements IAttnetsService {
234
234
 
235
235
  /**
236
236
  * Run per epoch, clean-up operations that are not urgent
237
- * Subscribe to new random subnets every EPOCHS_PER_RANDOM_SUBNET_SUBSCRIPTION epochs
237
+ * Subscribe to new random subnets every EPOCHS_PER_SUBNET_SUBSCRIPTION epochs
238
238
  */
239
239
  private onEpoch = (epoch: Epoch): void => {
240
240
  try {
241
- if (epoch % EPOCHS_PER_RANDOM_SUBNET_SUBSCRIPTION === 0) {
241
+ if (epoch % this.config.EPOCHS_PER_SUBNET_SUBSCRIPTION === 0) {
242
242
  this.recomputeLongLivedSubnets();
243
243
  }
244
244
  } catch (e) {
@@ -253,7 +253,7 @@ export class AttnetsService implements IAttnetsService {
253
253
  }
254
254
 
255
255
  const oldSubnets = this.longLivedSubscriptions;
256
- const newSubnets = computeSubscribedSubnet(this.nodeId, this.clock.currentEpoch);
256
+ const newSubnets = computeSubscribedSubnet(this.config, this.nodeId, this.clock.currentEpoch);
257
257
  this.logger.verbose("Recomputing long-lived subscriptions", {
258
258
  epoch: this.clock.currentEpoch,
259
259
  oldSubnets: Array.from(oldSubnets).join(","),
@@ -1,11 +1,6 @@
1
1
  import {digest} from "@chainsafe/as-sha256";
2
- import {
3
- ATTESTATION_SUBNET_COUNT,
4
- ATTESTATION_SUBNET_PREFIX_BITS,
5
- EPOCHS_PER_SUBNET_SUBSCRIPTION,
6
- NODE_ID_BITS,
7
- SUBNETS_PER_NODE,
8
- } from "@lodestar/params";
2
+ import {ChainConfig} from "@lodestar/config";
3
+ import {ATTESTATION_SUBNET_COUNT, ATTESTATION_SUBNET_PREFIX_BITS, NODE_ID_BITS} from "@lodestar/params";
9
4
  import {computeShuffledIndex} from "@lodestar/state-transition";
10
5
  import {Epoch, ssz} from "@lodestar/types";
11
6
  import {NodeId} from "./interface.js";
@@ -13,10 +8,10 @@ import {NodeId} from "./interface.js";
13
8
  /**
14
9
  * Spec https://github.com/ethereum/consensus-specs/blob/v1.4.0-alpha.3/specs/phase0/p2p-interface.md
15
10
  */
16
- export function computeSubscribedSubnet(nodeId: NodeId, epoch: Epoch): number[] {
11
+ export function computeSubscribedSubnet(config: ChainConfig, nodeId: NodeId, epoch: Epoch): number[] {
17
12
  const subnets: number[] = [];
18
- for (let index = 0; index < SUBNETS_PER_NODE; index++) {
19
- subnets.push(computeSubscribedSubnetByIndex(nodeId, epoch, index));
13
+ for (let index = 0; index < config.SUBNETS_PER_NODE; index++) {
14
+ subnets.push(computeSubscribedSubnetByIndex(config, nodeId, epoch, index));
20
15
  }
21
16
  return subnets;
22
17
  }
@@ -24,11 +19,16 @@ export function computeSubscribedSubnet(nodeId: NodeId, epoch: Epoch): number[]
24
19
  /**
25
20
  * Spec https://github.com/ethereum/consensus-specs/blob/v1.4.0-alpha.3/specs/phase0/p2p-interface.md
26
21
  */
27
- export function computeSubscribedSubnetByIndex(nodeId: NodeId, epoch: Epoch, index: number): number {
22
+ export function computeSubscribedSubnetByIndex(
23
+ config: ChainConfig,
24
+ nodeId: NodeId,
25
+ epoch: Epoch,
26
+ index: number
27
+ ): number {
28
28
  const nodeIdPrefix = getNodeIdPrefix(nodeId);
29
29
  const nodeOffset = getNodeOffset(nodeId);
30
30
  const permutationSeed = digest(
31
- ssz.UintNum64.serialize(Math.floor((epoch + nodeOffset) / EPOCHS_PER_SUBNET_SUBSCRIPTION))
31
+ ssz.UintNum64.serialize(Math.floor((epoch + nodeOffset) / config.EPOCHS_PER_SUBNET_SUBSCRIPTION))
32
32
  );
33
33
  const permutatedPrefix = computeShuffledIndex(nodeIdPrefix, 1 << ATTESTATION_SUBNET_PREFIX_BITS, permutationSeed);
34
34
  return (permutatedPrefix + index) % ATTESTATION_SUBNET_COUNT;
@@ -53,6 +53,7 @@ export type BeaconNodeInitModules = {
53
53
  dataDir: string;
54
54
  peerStoreDir?: string;
55
55
  anchorState: BeaconStateAllForks;
56
+ isAnchorStateFinalized: boolean;
56
57
  wsCheckpoint?: phase0.Checkpoint;
57
58
  metricsRegistries?: Registry[];
58
59
  };
@@ -154,6 +155,7 @@ export class BeaconNode {
154
155
  dataDir,
155
156
  peerStoreDir,
156
157
  anchorState,
158
+ isAnchorStateFinalized,
157
159
  wsCheckpoint,
158
160
  metricsRegistries = [],
159
161
  }: BeaconNodeInitModules): Promise<T> {
@@ -217,6 +219,7 @@ export class BeaconNode {
217
219
  metrics,
218
220
  validatorMonitor,
219
221
  anchorState,
222
+ isAnchorStateFinalized,
220
223
  eth1: initializeEth1ForBlockProduction(opts.eth1, {
221
224
  config,
222
225
  db,
@@ -114,13 +114,14 @@ export class RangeSync extends (EventEmitter as {new (): RangeSyncEmitter}) {
114
114
  */
115
115
  addPeer(peerId: PeerIdStr, localStatus: Status, peerStatus: Status): void {
116
116
  // Compute if we should do a Finalized or Head sync with this peer
117
- const {syncType, startEpoch, target} = getRangeSyncTarget(localStatus, peerStatus, this.chain.forkChoice);
117
+ const {syncType, startEpoch, target} = getRangeSyncTarget(localStatus, peerStatus, this.chain);
118
118
  this.logger.debug("Sync peer joined", {
119
119
  peer: peerId,
120
120
  syncType,
121
121
  startEpoch,
122
122
  targetSlot: target.slot,
123
123
  targetRoot: toRootHex(target.root),
124
+ localHeadSlot: localStatus.headSlot,
124
125
  earliestAvailableSlot: (peerStatus as fulu.Status).earliestAvailableSlot ?? Infinity,
125
126
  });
126
127
 
@@ -1,6 +1,7 @@
1
1
  import {IForkChoice} from "@lodestar/fork-choice";
2
2
  import {computeEpochAtSlot, computeStartSlotAtEpoch} from "@lodestar/state-transition";
3
3
  import {Slot, Status} from "@lodestar/types";
4
+ import {IBeaconChain} from "../../chain/interface.ts";
4
5
  import {ChainTarget} from "../range/utils/index.js";
5
6
 
6
7
  /** The type of peer relative to our current state */
@@ -103,8 +104,11 @@ export function getRangeSyncType(local: Status, remote: Status, forkChoice: IFor
103
104
  export function getRangeSyncTarget(
104
105
  local: Status,
105
106
  remote: Status,
106
- forkChoice: IForkChoice
107
+ chain: IBeaconChain
107
108
  ): {syncType: RangeSyncType; startEpoch: Slot; target: ChainTarget} {
109
+ const forkChoice = chain.forkChoice;
110
+
111
+ // finalized sync
108
112
  if (remote.finalizedEpoch > local.finalizedEpoch && !forkChoice.hasBlock(remote.finalizedRoot)) {
109
113
  return {
110
114
  // If RangeSyncType.Finalized, the range of blocks fetchable from startEpoch and target must allow to switch
@@ -131,11 +135,26 @@ export function getRangeSyncTarget(
131
135
  },
132
136
  };
133
137
  }
138
+
139
+ // we don't want to sync from epoch < minEpoch
140
+ // if we boot from an unfinalized checkpoint state, we don't want to sync before anchorStateLatestBlockSlot
141
+ // if we boot from a finalized checkpoint state, anchorStateLatestBlockSlot is trusted and we also don't want to sync before it
142
+ const minEpoch = Math.max(remote.finalizedEpoch, computeEpochAtSlot(chain.anchorStateLatestBlockSlot));
143
+
144
+ // head sync
134
145
  return {
135
146
  syncType: RangeSyncType.Head,
136
- // The new peer has the same finalized (earlier filters should prevent a peer with an
137
- // earlier finalized chain from reaching here).
138
- startEpoch: Math.min(computeEpochAtSlot(local.headSlot), remote.finalizedEpoch),
147
+ // The new peer has the same finalized `remote.finalizedEpoch == local.finalizedEpoch` since
148
+ // previous filters should prevent a peer with an earlier finalized chain from reaching here.
149
+ //
150
+ // By default and during stable network conditions, the head sync always starts from
151
+ // the finalized epoch (even though it's the head sync) because finalized epoch is < local head.
152
+ // This is to prevent the issue noted here https://github.com/ChainSafe/lodestar/pull/7509#discussion_r1984353063.
153
+ //
154
+ // During non-finality of the network, when starting from an unfinalized checkpoint state, we don't want
155
+ // to sync before anchorStateLatestBlockSlot as finalized epoch is too far away. Local head will also be
156
+ // the same to that value at startup, the head sync always starts from anchorStateLatestBlockSlot in this case.
157
+ startEpoch: Math.min(computeEpochAtSlot(local.headSlot), minEpoch),
139
158
  target: {
140
159
  slot: remote.headSlot,
141
160
  root: remote.headRoot,
package/src/util/clock.ts CHANGED
@@ -4,7 +4,6 @@ import {ChainForkConfig} from "@lodestar/config";
4
4
  import {computeEpochAtSlot, computeTimeAtSlot, getCurrentSlot} from "@lodestar/state-transition";
5
5
  import type {Epoch, Slot} from "@lodestar/types";
6
6
  import {ErrorAborted} from "@lodestar/utils";
7
- import {MAXIMUM_GOSSIP_CLOCK_DISPARITY} from "../constants/constants.js";
8
7
 
9
8
  export enum ClockEvent {
10
9
  /**
@@ -101,7 +100,7 @@ export class Clock extends EventEmitter implements IClock {
101
100
  get currentSlotWithGossipDisparity(): Slot {
102
101
  const currentSlot = this.currentSlot;
103
102
  const nextSlotTime = computeTimeAtSlot(this.config, currentSlot + 1, this.genesisTime) * 1000;
104
- return nextSlotTime - Date.now() < MAXIMUM_GOSSIP_CLOCK_DISPARITY ? currentSlot + 1 : currentSlot;
103
+ return nextSlotTime - Date.now() < this.config.MAXIMUM_GOSSIP_CLOCK_DISPARITY ? currentSlot + 1 : currentSlot;
105
104
  }
106
105
 
107
106
  get currentEpoch(): Epoch {
@@ -130,12 +129,12 @@ export class Clock extends EventEmitter implements IClock {
130
129
  }
131
130
  const nextSlotTime = computeTimeAtSlot(this.config, currentSlot + 1, this.genesisTime) * 1000;
132
131
  // we're too close to next slot, accept next slot
133
- if (nextSlotTime - Date.now() < MAXIMUM_GOSSIP_CLOCK_DISPARITY) {
132
+ if (nextSlotTime - Date.now() < this.config.MAXIMUM_GOSSIP_CLOCK_DISPARITY) {
134
133
  return slot === currentSlot + 1;
135
134
  }
136
135
  const currentSlotTime = computeTimeAtSlot(this.config, currentSlot, this.genesisTime) * 1000;
137
136
  // we've just passed the current slot, accept previous slot
138
- if (Date.now() - currentSlotTime < MAXIMUM_GOSSIP_CLOCK_DISPARITY) {
137
+ if (Date.now() - currentSlotTime < this.config.MAXIMUM_GOSSIP_CLOCK_DISPARITY) {
139
138
  return slot === currentSlot - 1;
140
139
  }
141
140
  return false;
@@ -1,13 +1,32 @@
1
+ import fs from "node:fs";
2
+ import path from "node:path";
1
3
  import {sleep} from "@lodestar/utils";
2
4
 
5
+ export enum ProfileThread {
6
+ MAIN = "main",
7
+ NETWORK = "network",
8
+ DISC5 = "discv5",
9
+ }
10
+
11
+ /**
12
+ * The time to take a Bun profile.
13
+ * If we increase this time it'll potentiall cause the app to crash.
14
+ * If we decrease this time, profile recorded will be fragmented and hard to analyze.
15
+ */
16
+ const BUN_PROFILE_MS = 3 * 1000;
17
+
18
+ export async function profileThread(thread: ProfileThread, durationMs: number, dirpath: string): Promise<string> {
19
+ return globalThis.Bun ? profileBun(thread, durationMs) : profileNodeJS(thread, durationMs, dirpath);
20
+ }
21
+
3
22
  /**
4
- * Take 10m profile of the current thread without promise tracking.
23
+ * Take `durationMs` profile of the current thread and return the persisted file path.
5
24
  */
6
- export async function profileNodeJS(durationMs: number): Promise<string> {
25
+ async function profileNodeJS(thread: ProfileThread, durationMs: number, dirpath: string): Promise<string> {
7
26
  const inspector = await import("node:inspector");
8
27
 
9
28
  // due to some typing issues, not able to use promisify here
10
- return new Promise<string>((resolve, reject) => {
29
+ const profile = await new Promise<string>((resolve, reject) => {
11
30
  // Start the inspector and connect to it
12
31
  const session = new inspector.Session();
13
32
  session.connect();
@@ -29,6 +48,29 @@ export async function profileNodeJS(durationMs: number): Promise<string> {
29
48
  });
30
49
  });
31
50
  });
51
+
52
+ const filePath = path.join(dirpath, `${thread}_thread_${new Date().toISOString()}.cpuprofile`);
53
+ fs.writeFileSync(filePath, profile);
54
+ return filePath;
55
+ }
56
+
57
+ /**
58
+ * Unlike NodeJS, Bun console.profile() api flush data to the inspector,
59
+ * so this api returns ms taken of this profile instead of file path.
60
+ */
61
+ async function profileBun(thread: ProfileThread, durationMs: number): Promise<string> {
62
+ const start = Date.now();
63
+ let now = Date.now();
64
+ while (now - start < durationMs) {
65
+ // biome-ignore lint/suspicious/noConsole: need to use console api to profile in Bun
66
+ console.profile(String(now));
67
+ await sleep(BUN_PROFILE_MS);
68
+ // biome-ignore lint/suspicious/noConsole: need to use console api to profile in Bun
69
+ console.profileEnd(String(now));
70
+ now = Date.now();
71
+ }
72
+
73
+ return `Successfully take Bun ${thread} thread profile in ${now - start}ms. Check your inspector to see the profile.`;
32
74
  }
33
75
 
34
76
  /**
@@ -417,6 +417,36 @@ export function getSlotFromDataColumnSidecarSerialized(data: Uint8Array): Slot |
417
417
  return getSlotFromOffset(data, SLOT_BYTES_POSITION_IN_SIGNED_DATA_COLUMN_SIDECAR);
418
418
  }
419
419
 
420
+ /**
421
+ * BeaconState of all forks (up until Electra, check with new forks)
422
+ * class BeaconState(Container):
423
+ * genesis_time: uint64 - 8 bytes
424
+ * genesis_validators_root: Root - 32 bytes
425
+ * slot: Slot - 8 bytes
426
+ * fork: Fork - 16 bytes
427
+ * latest_block_header: BeaconBlockHeader - fixed size
428
+ * slot: Slot - 8 bytes
429
+ *
430
+ */
431
+
432
+ const BLOCK_HEADER_SLOT_BYTES_POSITION_IN_BEACON_STATE = 8 + 32 + 8 + 16;
433
+ export function getLastProcessedSlotFromBeaconStateSerialized(data: Uint8Array): Slot | null {
434
+ if (data.length < BLOCK_HEADER_SLOT_BYTES_POSITION_IN_BEACON_STATE + SLOT_SIZE) {
435
+ return null;
436
+ }
437
+
438
+ return getSlotFromOffset(data, BLOCK_HEADER_SLOT_BYTES_POSITION_IN_BEACON_STATE);
439
+ }
440
+
441
+ const SLOT_BYTES_POSITION_IN_BEACON_STATE = 8 + 32;
442
+ export function getSlotFromBeaconStateSerialized(data: Uint8Array): Slot | null {
443
+ if (data.length < SLOT_BYTES_POSITION_IN_BEACON_STATE) {
444
+ return null;
445
+ }
446
+
447
+ return getSlotFromOffset(data, SLOT_BYTES_POSITION_IN_BEACON_STATE);
448
+ }
449
+
420
450
  /**
421
451
  * Read only the first 4 bytes of Slot, max value is 4,294,967,295 will be reached 1634 years after genesis
422
452
  *