@lodestar/beacon-node 1.50.0-dev.335476095e → 1.50.0-dev.3430a7cf04

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 (289) hide show
  1. package/lib/api/impl/beacon/blocks/index.d.ts.map +1 -1
  2. package/lib/api/impl/beacon/blocks/index.js +7 -2
  3. package/lib/api/impl/beacon/blocks/index.js.map +1 -1
  4. package/lib/api/impl/beacon/state/index.d.ts.map +1 -1
  5. package/lib/api/impl/beacon/state/index.js +28 -6
  6. package/lib/api/impl/beacon/state/index.js.map +1 -1
  7. package/lib/api/impl/debug/index.d.ts.map +1 -1
  8. package/lib/api/impl/debug/index.js +26 -18
  9. package/lib/api/impl/debug/index.js.map +1 -1
  10. package/lib/api/impl/validator/index.d.ts.map +1 -1
  11. package/lib/api/impl/validator/index.js +63 -17
  12. package/lib/api/impl/validator/index.js.map +1 -1
  13. package/lib/chain/ColumnReconstructionTracker.d.ts +0 -2
  14. package/lib/chain/ColumnReconstructionTracker.d.ts.map +1 -1
  15. package/lib/chain/ColumnReconstructionTracker.js +4 -5
  16. package/lib/chain/ColumnReconstructionTracker.js.map +1 -1
  17. package/lib/chain/archiveStore/archiveStore.d.ts +5 -0
  18. package/lib/chain/archiveStore/archiveStore.d.ts.map +1 -1
  19. package/lib/chain/archiveStore/archiveStore.js +13 -23
  20. package/lib/chain/archiveStore/archiveStore.js.map +1 -1
  21. package/lib/chain/archiveStore/utils/pruneHistory.d.ts +1 -1
  22. package/lib/chain/archiveStore/utils/pruneHistory.d.ts.map +1 -1
  23. package/lib/chain/archiveStore/utils/pruneHistory.js +2 -1
  24. package/lib/chain/archiveStore/utils/pruneHistory.js.map +1 -1
  25. package/lib/chain/beaconProposerCache.d.ts +6 -0
  26. package/lib/chain/beaconProposerCache.d.ts.map +1 -1
  27. package/lib/chain/beaconProposerCache.js +16 -0
  28. package/lib/chain/beaconProposerCache.js.map +1 -1
  29. package/lib/chain/blocks/blockInput/blockInput.d.ts +1 -1
  30. package/lib/chain/blocks/blockInput/blockInput.d.ts.map +1 -1
  31. package/lib/chain/blocks/blockInput/blockInput.js.map +1 -1
  32. package/lib/chain/blocks/importBlock.d.ts.map +1 -1
  33. package/lib/chain/blocks/importBlock.js +21 -12
  34. package/lib/chain/blocks/importBlock.js.map +1 -1
  35. package/lib/chain/blocks/verifyBlock.d.ts.map +1 -1
  36. package/lib/chain/blocks/verifyBlock.js +6 -6
  37. package/lib/chain/blocks/verifyBlock.js.map +1 -1
  38. package/lib/chain/chain.d.ts +4 -1
  39. package/lib/chain/chain.d.ts.map +1 -1
  40. package/lib/chain/chain.js +65 -22
  41. package/lib/chain/chain.js.map +1 -1
  42. package/lib/chain/interface.d.ts +4 -1
  43. package/lib/chain/interface.d.ts.map +1 -1
  44. package/lib/chain/opPools/aggregatedAttestationPool.d.ts +4 -2
  45. package/lib/chain/opPools/aggregatedAttestationPool.d.ts.map +1 -1
  46. package/lib/chain/opPools/aggregatedAttestationPool.js +30 -8
  47. package/lib/chain/opPools/aggregatedAttestationPool.js.map +1 -1
  48. package/lib/chain/opPools/opPool.d.ts.map +1 -1
  49. package/lib/chain/opPools/opPool.js +1 -23
  50. package/lib/chain/opPools/opPool.js.map +1 -1
  51. package/lib/chain/opPools/proposerPreferencesPool.d.ts +4 -0
  52. package/lib/chain/opPools/proposerPreferencesPool.d.ts.map +1 -1
  53. package/lib/chain/opPools/proposerPreferencesPool.js +13 -1
  54. package/lib/chain/opPools/proposerPreferencesPool.js.map +1 -1
  55. package/lib/chain/opPools/utils.d.ts +11 -0
  56. package/lib/chain/opPools/utils.d.ts.map +1 -1
  57. package/lib/chain/opPools/utils.js +22 -0
  58. package/lib/chain/opPools/utils.js.map +1 -1
  59. package/lib/chain/options.d.ts +1 -0
  60. package/lib/chain/options.d.ts.map +1 -1
  61. package/lib/chain/prepareNextSlot.d.ts.map +1 -1
  62. package/lib/chain/prepareNextSlot.js +15 -6
  63. package/lib/chain/prepareNextSlot.js.map +1 -1
  64. package/lib/chain/produceBlock/computeNewStateRoot.d.ts.map +1 -1
  65. package/lib/chain/produceBlock/computeNewStateRoot.js.map +1 -1
  66. package/lib/chain/produceBlock/produceBlockBody.d.ts +8 -0
  67. package/lib/chain/produceBlock/produceBlockBody.d.ts.map +1 -1
  68. package/lib/chain/produceBlock/produceBlockBody.js +48 -34
  69. package/lib/chain/produceBlock/produceBlockBody.js.map +1 -1
  70. package/lib/chain/reprocess.d.ts +2 -1
  71. package/lib/chain/reprocess.d.ts.map +1 -1
  72. package/lib/chain/reprocess.js +2 -1
  73. package/lib/chain/reprocess.js.map +1 -1
  74. package/lib/chain/seenCache/seenGossipBlockInput.d.ts +7 -0
  75. package/lib/chain/seenCache/seenGossipBlockInput.d.ts.map +1 -1
  76. package/lib/chain/seenCache/seenGossipBlockInput.js +22 -1
  77. package/lib/chain/seenCache/seenGossipBlockInput.js.map +1 -1
  78. package/lib/chain/shufflingCache.d.ts +18 -8
  79. package/lib/chain/shufflingCache.d.ts.map +1 -1
  80. package/lib/chain/shufflingCache.js +72 -18
  81. package/lib/chain/shufflingCache.js.map +1 -1
  82. package/lib/chain/stateCache/persistentCheckpointsCache.d.ts.map +1 -1
  83. package/lib/chain/stateCache/persistentCheckpointsCache.js +1 -1
  84. package/lib/chain/stateCache/persistentCheckpointsCache.js.map +1 -1
  85. package/lib/chain/validatorMonitor.d.ts.map +1 -1
  86. package/lib/chain/validatorMonitor.js +6 -4
  87. package/lib/chain/validatorMonitor.js.map +1 -1
  88. package/lib/constants/constants.d.ts +1 -0
  89. package/lib/constants/constants.d.ts.map +1 -1
  90. package/lib/constants/constants.js +1 -0
  91. package/lib/constants/constants.js.map +1 -1
  92. package/lib/db/beacon.d.ts +4 -1
  93. package/lib/db/beacon.d.ts.map +1 -1
  94. package/lib/db/beacon.js +6 -1
  95. package/lib/db/beacon.js.map +1 -1
  96. package/lib/db/buckets.d.ts +3 -1
  97. package/lib/db/buckets.d.ts.map +1 -1
  98. package/lib/db/buckets.js +2 -0
  99. package/lib/db/buckets.js.map +1 -1
  100. package/lib/db/interface.d.ts +4 -1
  101. package/lib/db/interface.d.ts.map +1 -1
  102. package/lib/db/repositories/blockArchive.js +2 -2
  103. package/lib/db/repositories/blockArchive.js.map +1 -1
  104. package/lib/db/repositories/blockArchiveIndex.d.ts +3 -3
  105. package/lib/db/repositories/blockArchiveIndex.d.ts.map +1 -1
  106. package/lib/db/repositories/blockArchiveIndex.js +1 -2
  107. package/lib/db/repositories/blockArchiveIndex.js.map +1 -1
  108. package/lib/db/repositories/earliestAvailableSlot.d.ts +11 -0
  109. package/lib/db/repositories/earliestAvailableSlot.d.ts.map +1 -0
  110. package/lib/db/repositories/earliestAvailableSlot.js +19 -0
  111. package/lib/db/repositories/earliestAvailableSlot.js.map +1 -0
  112. package/lib/db/repositories/index.d.ts +1 -0
  113. package/lib/db/repositories/index.d.ts.map +1 -1
  114. package/lib/db/repositories/index.js +1 -0
  115. package/lib/db/repositories/index.js.map +1 -1
  116. package/lib/db/repositories/proposerPreferences.d.ts +14 -0
  117. package/lib/db/repositories/proposerPreferences.d.ts.map +1 -0
  118. package/lib/db/repositories/proposerPreferences.js +21 -0
  119. package/lib/db/repositories/proposerPreferences.js.map +1 -0
  120. package/lib/execution/engine/blobVersionedHashes.d.ts +9 -0
  121. package/lib/execution/engine/blobVersionedHashes.d.ts.map +1 -0
  122. package/lib/execution/engine/blobVersionedHashes.js +74 -0
  123. package/lib/execution/engine/blobVersionedHashes.js.map +1 -0
  124. package/lib/execution/engine/http.d.ts +45 -13
  125. package/lib/execution/engine/http.d.ts.map +1 -1
  126. package/lib/execution/engine/http.js +236 -198
  127. package/lib/execution/engine/http.js.map +1 -1
  128. package/lib/execution/engine/index.d.ts +3 -2
  129. package/lib/execution/engine/index.d.ts.map +1 -1
  130. package/lib/execution/engine/index.js +33 -8
  131. package/lib/execution/engine/index.js.map +1 -1
  132. package/lib/execution/engine/interface.d.ts +1 -1
  133. package/lib/execution/engine/interface.d.ts.map +1 -1
  134. package/lib/execution/engine/jsonRpcTransport.d.ts +25 -0
  135. package/lib/execution/engine/jsonRpcTransport.d.ts.map +1 -0
  136. package/lib/execution/engine/jsonRpcTransport.js +158 -0
  137. package/lib/execution/engine/jsonRpcTransport.js.map +1 -0
  138. package/lib/execution/engine/restHttpClient.d.ts +71 -0
  139. package/lib/execution/engine/restHttpClient.d.ts.map +1 -0
  140. package/lib/execution/engine/restHttpClient.js +201 -0
  141. package/lib/execution/engine/restHttpClient.js.map +1 -0
  142. package/lib/execution/engine/restTransport.d.ts +46 -0
  143. package/lib/execution/engine/restTransport.d.ts.map +1 -0
  144. package/lib/execution/engine/restTransport.js +399 -0
  145. package/lib/execution/engine/restTransport.js.map +1 -0
  146. package/lib/execution/engine/sszTypes.d.ts +1261 -0
  147. package/lib/execution/engine/sszTypes.d.ts.map +1 -0
  148. package/lib/execution/engine/sszTypes.js +216 -0
  149. package/lib/execution/engine/sszTypes.js.map +1 -0
  150. package/lib/execution/engine/transport.d.ts +39 -0
  151. package/lib/execution/engine/transport.d.ts.map +1 -0
  152. package/lib/execution/engine/transport.js +2 -0
  153. package/lib/execution/engine/transport.js.map +1 -0
  154. package/lib/execution/engine/types.d.ts +2 -0
  155. package/lib/execution/engine/types.d.ts.map +1 -1
  156. package/lib/execution/engine/types.js +31 -63
  157. package/lib/execution/engine/types.js.map +1 -1
  158. package/lib/execution/engine/utils.d.ts.map +1 -1
  159. package/lib/execution/engine/utils.js +9 -2
  160. package/lib/execution/engine/utils.js.map +1 -1
  161. package/lib/metrics/metrics/lodestar.d.ts +10 -0
  162. package/lib/metrics/metrics/lodestar.d.ts.map +1 -1
  163. package/lib/metrics/metrics/lodestar.js +22 -0
  164. package/lib/metrics/metrics/lodestar.js.map +1 -1
  165. package/lib/network/core/events.d.ts +5 -1
  166. package/lib/network/core/events.d.ts.map +1 -1
  167. package/lib/network/core/events.js +8 -0
  168. package/lib/network/core/events.js.map +1 -1
  169. package/lib/network/gossip/topic.d.ts +0 -14
  170. package/lib/network/gossip/topic.d.ts.map +1 -1
  171. package/lib/network/processor/gossipHandlers.d.ts.map +1 -1
  172. package/lib/network/processor/gossipHandlers.js +60 -64
  173. package/lib/network/processor/gossipHandlers.js.map +1 -1
  174. package/lib/network/processor/index.js +3 -3
  175. package/lib/network/processor/index.js.map +1 -1
  176. package/lib/network/reqresp/rateLimit.js +2 -2
  177. package/lib/network/reqresp/rateLimit.js.map +1 -1
  178. package/lib/network/subnets/attnetsService.d.ts.map +1 -1
  179. package/lib/network/subnets/attnetsService.js +3 -1
  180. package/lib/network/subnets/attnetsService.js.map +1 -1
  181. package/lib/node/nodejs.d.ts +3 -1
  182. package/lib/node/nodejs.d.ts.map +1 -1
  183. package/lib/node/nodejs.js +2 -1
  184. package/lib/node/nodejs.js.map +1 -1
  185. package/lib/node/notifier.d.ts.map +1 -1
  186. package/lib/node/notifier.js +12 -13
  187. package/lib/node/notifier.js.map +1 -1
  188. package/lib/node/options.d.ts +3 -2
  189. package/lib/node/options.d.ts.map +1 -1
  190. package/lib/node/options.js +2 -2
  191. package/lib/node/options.js.map +1 -1
  192. package/lib/sync/range/batch.d.ts +5 -5
  193. package/lib/sync/range/batch.d.ts.map +1 -1
  194. package/lib/sync/range/batch.js +11 -17
  195. package/lib/sync/range/batch.js.map +1 -1
  196. package/lib/sync/range/chain.d.ts +4 -4
  197. package/lib/sync/range/chain.d.ts.map +1 -1
  198. package/lib/sync/range/chain.js +7 -5
  199. package/lib/sync/range/chain.js.map +1 -1
  200. package/lib/sync/range/range.d.ts.map +1 -1
  201. package/lib/sync/range/range.js +25 -11
  202. package/lib/sync/range/range.js.map +1 -1
  203. package/lib/sync/unknownBlock.d.ts +5 -2
  204. package/lib/sync/unknownBlock.d.ts.map +1 -1
  205. package/lib/sync/unknownBlock.js +47 -22
  206. package/lib/sync/unknownBlock.js.map +1 -1
  207. package/lib/sync/utils/downloadByRange.d.ts +38 -14
  208. package/lib/sync/utils/downloadByRange.d.ts.map +1 -1
  209. package/lib/sync/utils/downloadByRange.js +84 -39
  210. package/lib/sync/utils/downloadByRange.js.map +1 -1
  211. package/lib/util/asyncIterableToEvents.d.ts +10 -2
  212. package/lib/util/asyncIterableToEvents.d.ts.map +1 -1
  213. package/lib/util/asyncIterableToEvents.js +29 -3
  214. package/lib/util/asyncIterableToEvents.js.map +1 -1
  215. package/lib/util/clock.d.ts.map +1 -1
  216. package/lib/util/clock.js +3 -4
  217. package/lib/util/clock.js.map +1 -1
  218. package/lib/util/metadata.d.ts.map +1 -1
  219. package/lib/util/metadata.js +2 -1
  220. package/lib/util/metadata.js.map +1 -1
  221. package/lib/util/sszBytes.d.ts +2 -2
  222. package/lib/util/sszBytes.d.ts.map +1 -1
  223. package/lib/util/sszBytes.js +22 -14
  224. package/lib/util/sszBytes.js.map +1 -1
  225. package/package.json +17 -16
  226. package/src/api/impl/beacon/blocks/index.ts +10 -3
  227. package/src/api/impl/beacon/state/index.ts +43 -6
  228. package/src/api/impl/debug/index.ts +28 -19
  229. package/src/api/impl/validator/index.ts +86 -19
  230. package/src/chain/ColumnReconstructionTracker.ts +4 -6
  231. package/src/chain/archiveStore/archiveStore.ts +18 -24
  232. package/src/chain/archiveStore/utils/pruneHistory.ts +4 -1
  233. package/src/chain/beaconProposerCache.ts +19 -1
  234. package/src/chain/blocks/blockInput/blockInput.ts +1 -1
  235. package/src/chain/blocks/importBlock.ts +25 -11
  236. package/src/chain/blocks/verifyBlock.ts +7 -6
  237. package/src/chain/chain.ts +88 -21
  238. package/src/chain/interface.ts +5 -1
  239. package/src/chain/opPools/aggregatedAttestationPool.ts +34 -10
  240. package/src/chain/opPools/opPool.ts +1 -31
  241. package/src/chain/opPools/proposerPreferencesPool.ts +20 -1
  242. package/src/chain/opPools/utils.ts +31 -0
  243. package/src/chain/options.ts +1 -1
  244. package/src/chain/prepareNextSlot.ts +22 -6
  245. package/src/chain/produceBlock/computeNewStateRoot.ts +9 -2
  246. package/src/chain/produceBlock/produceBlockBody.ts +78 -39
  247. package/src/chain/reprocess.ts +2 -1
  248. package/src/chain/seenCache/seenGossipBlockInput.ts +25 -1
  249. package/src/chain/shufflingCache.ts +84 -22
  250. package/src/chain/stateCache/persistentCheckpointsCache.ts +4 -1
  251. package/src/chain/validatorMonitor.ts +7 -4
  252. package/src/constants/constants.ts +1 -0
  253. package/src/db/beacon.ts +6 -0
  254. package/src/db/buckets.ts +2 -0
  255. package/src/db/interface.ts +4 -0
  256. package/src/db/repositories/blockArchive.ts +2 -2
  257. package/src/db/repositories/blockArchiveIndex.ts +2 -3
  258. package/src/db/repositories/earliestAvailableSlot.ts +19 -0
  259. package/src/db/repositories/index.ts +1 -0
  260. package/src/db/repositories/proposerPreferences.ts +23 -0
  261. package/src/execution/engine/blobVersionedHashes.ts +72 -0
  262. package/src/execution/engine/http.ts +304 -271
  263. package/src/execution/engine/index.ts +41 -7
  264. package/src/execution/engine/interface.ts +1 -1
  265. package/src/execution/engine/jsonRpcTransport.ts +247 -0
  266. package/src/execution/engine/restHttpClient.ts +269 -0
  267. package/src/execution/engine/restTransport.ts +544 -0
  268. package/src/execution/engine/sszTypes.ts +354 -0
  269. package/src/execution/engine/transport.ts +61 -0
  270. package/src/execution/engine/types.ts +42 -80
  271. package/src/execution/engine/utils.ts +12 -2
  272. package/src/metrics/metrics/lodestar.ts +23 -0
  273. package/src/network/core/events.ts +16 -1
  274. package/src/network/processor/gossipHandlers.ts +91 -77
  275. package/src/network/processor/index.ts +3 -3
  276. package/src/network/reqresp/rateLimit.ts +2 -2
  277. package/src/network/subnets/attnetsService.ts +8 -3
  278. package/src/node/nodejs.ts +4 -0
  279. package/src/node/notifier.ts +14 -13
  280. package/src/node/options.ts +10 -1
  281. package/src/sync/range/batch.ts +14 -19
  282. package/src/sync/range/chain.ts +9 -7
  283. package/src/sync/range/range.ts +26 -12
  284. package/src/sync/unknownBlock.ts +60 -23
  285. package/src/sync/utils/downloadByRange.ts +123 -58
  286. package/src/util/asyncIterableToEvents.ts +44 -5
  287. package/src/util/clock.ts +3 -4
  288. package/src/util/metadata.ts +2 -1
  289. package/src/util/sszBytes.ts +25 -12
@@ -5,6 +5,8 @@ import {
5
5
  BUILDER_INDEX_SELF_BUILD,
6
6
  ForkName,
7
7
  ForkPostBellatrix,
8
+ ForkPostDeneb,
9
+ ForkPreDeneb,
8
10
  ForkPreGloas,
9
11
  ForkSeq,
10
12
  GENESIS_SLOT,
@@ -111,14 +113,13 @@ import {
111
113
  * Post this time, race execution and builder to pick whatever resolves first.
112
114
  *
113
115
  * Empirically the builder block resolves in ~1 second, and execution block resolves in <500 ms.
114
- * A cutoff of 2 seconds gives enough time and if there are unexpected delays it ensures we publish
115
- * in time as proposals post 4 seconds into the slot will likely be orphaned due to proposer boost reorg.
116
+ * A cutoff of 1667 BPS (2 seconds for 12s slots) gives enough time and if there are unexpected delays
117
+ * it ensures we publish in time as proposals after the attestation deadline will likely be orphaned
118
+ * due to proposer boost reorg.
116
119
  *
117
120
  * TODO GLOAS: re-evaluate cutoff timing due to attestation deadline changes in gloas
118
121
  */
119
- const BLOCK_PRODUCTION_RACE_CUTOFF_MS = 2_000;
120
- /** Overall timeout for execution and block production apis */
121
- const BLOCK_PRODUCTION_RACE_TIMEOUT_MS = 12_000;
122
+ const BLOCK_PRODUCTION_RACE_CUTOFF_BPS = 1667;
122
123
  /** Rejection message of the bid block branch when there is no viable bid to commit to */
123
124
  const NO_BID_AVAILABLE = "No builder bid available";
124
125
 
@@ -167,8 +168,8 @@ function compareBidCandidates(a: BidCandidate, b: BidCandidate): number {
167
168
  }
168
169
 
169
170
  type ProduceBlockContentsRes = {executionPayloadValue: Wei; consensusBlockValue: Wei} & {
170
- data: BlockContents;
171
- version: ForkName;
171
+ data: BlockContents<ForkPreGloas>;
172
+ version: ForkPreGloas;
172
173
  };
173
174
  type ProduceBlindedBlockRes = {executionPayloadValue: Wei; consensusBlockValue: Wei} & {
174
175
  data: BlindedBeaconBlock;
@@ -301,8 +302,8 @@ export function getValidatorApi(
301
302
  */
302
303
  function msToNextEpoch(): number {
303
304
  const nextEpoch = chain.clock.currentEpoch + 1;
304
- const secPerEpoch = (SLOTS_PER_EPOCH * config.SLOT_DURATION_MS) / 1000;
305
- const nextEpochStartSec = chain.genesisTime + nextEpoch * secPerEpoch;
305
+ const nextEpochSlot = computeStartSlotAtEpoch(nextEpoch);
306
+ const nextEpochStartSec = computeTimeAtSlot(config, nextEpochSlot, chain.genesisTime);
306
307
  return nextEpochStartSec * 1000 - Date.now();
307
308
  }
308
309
 
@@ -511,6 +512,10 @@ export function getValidatorApi(
511
512
  parentBlock: ProtoBlock;
512
513
  }
513
514
  ): Promise<ProduceBlockContentsRes & {shouldOverrideBuilder?: boolean}> {
515
+ const version = config.getForkName(slot);
516
+ if (isForkPostGloas(version)) {
517
+ throw Error(`produceEngineBlockContents called for post-gloas fork=${version}`);
518
+ }
514
519
  const source = ProducedBlockSource.engine;
515
520
  metrics?.blockProductionRequests.inc({source});
516
521
 
@@ -525,7 +530,6 @@ export function getValidatorApi(
525
530
  feeRecipient,
526
531
  commonBlockBodyPromise,
527
532
  });
528
- const version = config.getForkName(block.slot);
529
533
  if (strictFeeRecipientCheck && feeRecipient && isForkPostBellatrix(version)) {
530
534
  const blockFeeRecipient = toHex((block as bellatrix.BeaconBlock).body.executionPayload.feeRecipient);
531
535
  if (blockFeeRecipient !== feeRecipient) {
@@ -563,7 +567,7 @@ export function getValidatorApi(
563
567
 
564
568
  return {
565
569
  data: {
566
- block,
570
+ block: block as BeaconBlock<ForkPostDeneb & ForkPreGloas>,
567
571
  blobs: blobsBundle.blobs,
568
572
  kzgProofs: blobsBundle.proofs,
569
573
  },
@@ -574,7 +578,13 @@ export function getValidatorApi(
574
578
  };
575
579
  }
576
580
 
577
- return {data: {block}, version, executionPayloadValue, consensusBlockValue, shouldOverrideBuilder};
581
+ return {
582
+ data: {block: block as BeaconBlock<ForkPreDeneb>},
583
+ version,
584
+ executionPayloadValue,
585
+ consensusBlockValue,
586
+ shouldOverrideBuilder,
587
+ };
578
588
  } finally {
579
589
  if (timer) timer({source});
580
590
  }
@@ -680,17 +690,21 @@ export function getValidatorApi(
680
690
  });
681
691
 
682
692
  // Calculate cutoff time based on start of the slot
683
- const cutoffMs = Math.max(0, BLOCK_PRODUCTION_RACE_CUTOFF_MS - chain.clock.msFromSlot(slot));
693
+ const cutoffMs = Math.max(
694
+ 0,
695
+ config.getSlotComponentDurationMs(fork, BLOCK_PRODUCTION_RACE_CUTOFF_BPS) - chain.clock.msFromSlot(slot)
696
+ );
697
+ const blockProductionTimeoutMs = config.getSlotDurationMs(fork);
684
698
 
685
699
  logger.verbose("Block production race (builder vs execution) starting", {
686
700
  ...loggerContext,
687
701
  cutoffMs,
688
- timeoutMs: BLOCK_PRODUCTION_RACE_TIMEOUT_MS,
702
+ timeoutMs: blockProductionTimeoutMs,
689
703
  });
690
704
 
691
705
  const blockProductionRacePromise = resolveOrRacePromises([builderPromise, enginePromise], {
692
706
  resolveTimeoutMs: cutoffMs,
693
- raceTimeoutMs: BLOCK_PRODUCTION_RACE_TIMEOUT_MS,
707
+ raceTimeoutMs: blockProductionTimeoutMs,
694
708
  signal: controller.signal,
695
709
  });
696
710
 
@@ -878,6 +892,10 @@ export function getValidatorApi(
878
892
 
879
893
  return {
880
894
  async produceBlockV3({slot, randaoReveal, graffiti, skipRandaoVerification, builderBoostFactor, ...opts}) {
895
+ const forkName = config.getForkName(slot);
896
+ if (isForkPostGloas(forkName)) {
897
+ throw new ApiError(400, `produceBlockV3 not supported for post-gloas fork=${forkName}, use produceBlockV4`);
898
+ }
881
899
  const {data, ...meta} = await produceEngineOrBuilderBlock(
882
900
  slot,
883
901
  randaoReveal,
@@ -1095,7 +1113,10 @@ export function getValidatorApi(
1095
1113
 
1096
1114
  // Calculate cutoff time based on start of the slot, ensures a slow local payload build does
1097
1115
  // not delay the proposal when a builder bid block is available (and vice versa)
1098
- const cutoffMs = Math.max(0, BLOCK_PRODUCTION_RACE_CUTOFF_MS - chain.clock.msFromSlot(slot));
1116
+ const cutoffMs = Math.max(
1117
+ 0,
1118
+ config.getSlotComponentDurationMs(fork, BLOCK_PRODUCTION_RACE_CUTOFF_BPS) - chain.clock.msFromSlot(slot)
1119
+ );
1099
1120
 
1100
1121
  // use abort controller to stop waiting for the bid block if the engine block will be selected
1101
1122
  const controller = new AbortController();
@@ -1123,7 +1144,7 @@ export function getValidatorApi(
1123
1144
 
1124
1145
  const [engineResult, bidBlockResult] = await resolveOrRacePromises([enginePromise, bidBlockPromise], {
1125
1146
  resolveTimeoutMs: cutoffMs,
1126
- raceTimeoutMs: BLOCK_PRODUCTION_RACE_TIMEOUT_MS,
1147
+ raceTimeoutMs: config.getSlotDurationMs(fork),
1127
1148
  signal: controller.signal,
1128
1149
  });
1129
1150
 
@@ -1300,6 +1321,10 @@ export function getValidatorApi(
1300
1321
  blobs: blobsBundle.blobs,
1301
1322
  };
1302
1323
 
1324
+ if (chain.opts.persistProducedPayloadEnvelopes) {
1325
+ chain.persistExecutionPayloadEnvelope(blockContents.executionPayloadEnvelope);
1326
+ }
1327
+
1303
1328
  return {
1304
1329
  data: blockContents,
1305
1330
  meta: {version: fork, consensusBlockValue, executionPayloadValue, executionPayloadIncluded: true},
@@ -1413,7 +1438,7 @@ export function getValidatorApi(
1413
1438
  // Spec: set payload_present only if the envelope was seen before get_payload_due_ms()
1414
1439
  // into the slot. Use the envelope's own arrival time (getPayloadEnvelopeSource), not
1415
1440
  // the input's creation time.
1416
- const payloadDueSec = config.getPayloadDueMs() / 1000;
1441
+ const payloadDueSec = config.getPayloadDueMs(fork) / 1000;
1417
1442
  const payloadSeenSec =
1418
1443
  payloadInput?.hasPayloadEnvelope() === true
1419
1444
  ? chain.clock.secFromSlot(slot, payloadInput.getPayloadEnvelopeSource().seenTimestampSec)
@@ -1490,8 +1515,9 @@ export function getValidatorApi(
1490
1515
  const currentEpoch = currentEpochWithDisparity();
1491
1516
  const nextEpoch = currentEpoch + 1;
1492
1517
  const startSlot = computeStartSlotAtEpoch(epoch);
1518
+ const prepareFork = config.getForkName(computeStartSlotAtEpoch(nextEpoch) - 1);
1493
1519
  const prepareNextSlotLookAheadMs =
1494
- config.SLOT_DURATION_MS - config.getSlotComponentDurationMs(PREPARE_NEXT_SLOT_BPS);
1520
+ config.getSlotDurationMs(prepareFork) - config.getSlotComponentDurationMs(prepareFork, PREPARE_NEXT_SLOT_BPS);
1495
1521
  const toNextEpochMs = msToNextEpoch();
1496
1522
  const nearNextEpoch = toNextEpochMs < prepareNextSlotLookAheadMs;
1497
1523
  // Post-Fulu the proposer lookahead is deterministic and known a full epoch ahead, so
@@ -1928,6 +1954,17 @@ export function getValidatorApi(
1928
1954
  },
1929
1955
 
1930
1956
  async prepareBeaconCommitteeSubnet({subscriptions}) {
1957
+ // The subscribing validators use this node for duties regardless of its sync state and
1958
+ // the subscription must not fail on the tracking, e.g. if the finalized state is unavailable
1959
+ try {
1960
+ await chain.updateAttachedValidators(
1961
+ chain.clock.currentEpoch,
1962
+ Array.from(new Set(subscriptions.map(({validatorIndex}) => validatorIndex)))
1963
+ );
1964
+ } catch (e) {
1965
+ logger.warn("Error tracking attached validators", {count: subscriptions.length}, e as Error);
1966
+ }
1967
+
1931
1968
  notWhileSyncing(chain, sync.state);
1932
1969
 
1933
1970
  await network.prepareBeaconCommitteeSubnets(
@@ -1991,6 +2028,13 @@ export function getValidatorApi(
1991
2028
  },
1992
2029
 
1993
2030
  async prepareBeaconProposer({proposers}) {
2031
+ if (isForkPostGloas(config.getForkName(chain.clock.currentSlot))) {
2032
+ throw new ApiError(
2033
+ 410,
2034
+ "prepareBeaconProposer is no longer supported from gloas, submit signed proposer preferences via POST /eth/v1/validator/proposer_preferences instead"
2035
+ );
2036
+ }
2037
+
1994
2038
  await chain.updateBeaconProposerData(chain.clock.currentEpoch, proposers);
1995
2039
  },
1996
2040
 
@@ -2025,6 +2069,13 @@ export function getValidatorApi(
2025
2069
  },
2026
2070
 
2027
2071
  async registerValidator({registrations}) {
2072
+ if (isForkPostGloas(config.getForkName(chain.clock.currentSlot))) {
2073
+ throw new ApiError(
2074
+ 410,
2075
+ "registerValidator is no longer supported from gloas, submit builder preferences via POST /eth/v1/validator/builder_preferences instead"
2076
+ );
2077
+ }
2078
+
2028
2079
  if (!chain.executionBuilder) {
2029
2080
  throw Error("External builder not configured");
2030
2081
  }
@@ -2060,11 +2111,14 @@ export function getValidatorApi(
2060
2111
 
2061
2112
  async submitProposerPreferences({signedProposerPreferences}) {
2062
2113
  const failures: FailureList = [];
2114
+ // Preferences submitted here are signed by validators using this node, gossiped ones are not
2115
+ const attachedValidatorIndices: ValidatorIndex[] = [];
2063
2116
 
2064
2117
  await Promise.all(
2065
2118
  signedProposerPreferences.map(async (signed, i) => {
2066
2119
  try {
2067
2120
  await validateGossipProposerPreferences(chain, signed);
2121
+ attachedValidatorIndices.push(signed.message.validatorIndex);
2068
2122
 
2069
2123
  chain.proposerPreferencesPool.add(signed);
2070
2124
  await network.publishProposerPreferences(signed);
@@ -2080,6 +2134,7 @@ export function getValidatorApi(
2080
2134
  };
2081
2135
 
2082
2136
  if (e instanceof ProposerPreferencesError && e.type.code === ProposerPreferencesErrorCode.ALREADY_KNOWN) {
2137
+ attachedValidatorIndices.push(signed.message.validatorIndex);
2083
2138
  logger.debug("Ignoring known signed proposer preferences", logCtx);
2084
2139
  return;
2085
2140
  }
@@ -2093,6 +2148,14 @@ export function getValidatorApi(
2093
2148
  })
2094
2149
  );
2095
2150
 
2151
+ if (attachedValidatorIndices.length > 0) {
2152
+ try {
2153
+ await chain.updateAttachedValidators(chain.clock.currentEpoch, attachedValidatorIndices);
2154
+ } catch (e) {
2155
+ logger.warn("Error tracking attached validators", {count: attachedValidatorIndices.length}, e as Error);
2156
+ }
2157
+ }
2158
+
2096
2159
  if (failures.length > 0) {
2097
2160
  throw new IndexedError("Error processing signed proposer preferences", failures);
2098
2161
  }
@@ -2187,6 +2250,10 @@ export function getValidatorApi(
2187
2250
  blockHash: toRootHex(executionPayload.blockHash),
2188
2251
  });
2189
2252
 
2253
+ if (chain.opts.persistProducedPayloadEnvelopes) {
2254
+ chain.persistExecutionPayloadEnvelope(envelope);
2255
+ }
2256
+
2190
2257
  return {
2191
2258
  data: envelope,
2192
2259
  meta: {
@@ -42,16 +42,11 @@ export class ColumnReconstructionTracker {
42
42
  /** Track if a reconstruction attempt is in-flight */
43
43
  running = false;
44
44
 
45
- private readonly minDelayMs: number;
46
- private readonly maxDelayMs: number;
47
-
48
45
  constructor(init: ColumnReconstructionTrackerInit) {
49
46
  this.logger = init.logger;
50
47
  this.emitter = init.emitter;
51
48
  this.metrics = init.metrics;
52
49
  this.config = init.config;
53
- this.minDelayMs = this.config.getSlotComponentDurationMs(RECONSTRUCTION_DELAY_MIN_BPS);
54
- this.maxDelayMs = this.config.getSlotComponentDurationMs(RECONSTRUCTION_DELAY_MAX_BPS);
55
50
  }
56
51
 
57
52
  triggerColumnReconstruction(input: BlockInputColumns | PayloadEnvelopeInput): void {
@@ -67,7 +62,10 @@ export class ColumnReconstructionTracker {
67
62
  // just that it has been triggered for this block root.
68
63
  this.running = true;
69
64
  this.lastBlockRootHex = input.blockRootHex;
70
- const delay = this.minDelayMs + Math.random() * (this.maxDelayMs - this.minDelayMs);
65
+ const fork = this.config.getForkName(input.slot);
66
+ const minDelayMs = this.config.getSlotComponentDurationMs(fork, RECONSTRUCTION_DELAY_MIN_BPS);
67
+ const maxDelayMs = this.config.getSlotComponentDurationMs(fork, RECONSTRUCTION_DELAY_MAX_BPS);
68
+ const delay = minDelayMs + Math.random() * (maxDelayMs - minDelayMs);
71
69
  sleep(delay)
72
70
  .then(() => {
73
71
  const logCtx = {slot: input.slot, root: input.blockRootHex};
@@ -113,7 +113,9 @@ export class ArchiveStore {
113
113
  this.logger,
114
114
  this.metrics,
115
115
  this.opts.anchorState.finalizedCheckpoint.epoch,
116
- this.chain.clock.currentEpoch
116
+ this.chain.clock.currentEpoch,
117
+ this.statePruneFromSlot,
118
+ this.advanceEarliestAvailableSlot
117
119
  ),
118
120
  () => this.logger.info("Still pruning historical data, please wait..."),
119
121
  30_000,
@@ -122,26 +124,6 @@ export class ArchiveStore {
122
124
  this.statePruneFromSlot = stateCutoffSlot;
123
125
  }
124
126
 
125
- // Initialize earliestAvailableSlot from the earliest block actually retained (after any pruning
126
- // above), rather than the anchor state slot set in the constructor. On an in-place restart the DB
127
- // still holds finalized history below the anchor; leaving the value at the anchor makes the node
128
- // reject by_range requests (beacon_blocks, execution_payload_envelopes and data_column_sidecars
129
- // all gate on this slot) for data it still retains. An empty archive (e.g. a fresh checkpoint
130
- // sync with nothing below the anchor) leaves the constructor's anchor slot in place.
131
- const earliestArchivedBlockSlot = await this.db.blockArchive.firstKey();
132
- if (earliestArchivedBlockSlot != null) {
133
- const oldEarliestAvailableSlot = this.chain.earliestAvailableSlot;
134
- this.chain.earliestAvailableSlot = earliestArchivedBlockSlot;
135
- this.logger.verbose("Initialized earliestAvailableSlot from retained block archive", {
136
- oldEarliestAvailableSlot,
137
- newEarliestAvailableSlot: earliestArchivedBlockSlot,
138
- });
139
- } else {
140
- this.logger.verbose("Empty block archive on init, keeping anchor earliestAvailableSlot", {
141
- earliestAvailableSlot: this.chain.earliestAvailableSlot,
142
- });
143
- }
144
-
145
127
  if (this.opts.serveHistoricalState) {
146
128
  this.historicalStateRegen = await HistoricalStateRegen.init({
147
129
  opts: {
@@ -158,6 +140,18 @@ export class ArchiveStore {
158
140
  }
159
141
  }
160
142
 
143
+ /**
144
+ * The CLI owns the initial value; this only advances it during pruneHistory.
145
+ * Persisted before any deletion so a crash mid-prune never leaves the floor below deleted data.
146
+ */
147
+ private advanceEarliestAvailableSlot = async (blockCutoffSlot: Slot): Promise<void> => {
148
+ const earliestAvailableSlot = Math.max(this.chain.earliestAvailableSlot, blockCutoffSlot);
149
+ if (earliestAvailableSlot !== this.chain.earliestAvailableSlot) {
150
+ await this.db.earliestAvailableSlot.set(earliestAvailableSlot);
151
+ this.chain.earliestAvailableSlot = earliestAvailableSlot;
152
+ }
153
+ };
154
+
161
155
  async close(): Promise<void> {
162
156
  await this.historicalStateRegen?.close();
163
157
  }
@@ -259,17 +253,17 @@ export class ArchiveStore {
259
253
 
260
254
  if (this.opts.pruneHistory) {
261
255
  timer = this.metrics?.processFinalizedCheckpoint.durationByTask.startTimer();
262
- const {blockCutoffSlot, stateCutoffSlot} = await pruneHistory(
256
+ const {stateCutoffSlot} = await pruneHistory(
263
257
  this.chain.config,
264
258
  this.db,
265
259
  this.logger,
266
260
  this.metrics,
267
261
  finalizedEpoch,
268
262
  this.chain.clock.currentEpoch,
269
- this.statePruneFromSlot
263
+ this.statePruneFromSlot,
264
+ this.advanceEarliestAvailableSlot
270
265
  );
271
266
  this.statePruneFromSlot = stateCutoffSlot;
272
- this.chain.earliestAvailableSlot = Math.max(this.chain.earliestAvailableSlot, blockCutoffSlot);
273
267
  timer?.({source: ArchiveStoreTask.PruneHistory});
274
268
  }
275
269
 
@@ -12,7 +12,8 @@ export async function pruneHistory(
12
12
  metrics: Metrics | null | undefined,
13
13
  finalizedEpoch: Epoch,
14
14
  currentEpoch: Epoch,
15
- statePruneFromSlot: Slot = 0
15
+ statePruneFromSlot: Slot,
16
+ beforePrune: (blockCutoffSlot: Slot) => Promise<void>
16
17
  ): Promise<{blockCutoffSlot: Slot; stateCutoffSlot: Slot}> {
17
18
  const blockCutoffEpoch = Math.min(
18
19
  // set by config, with underflow protection
@@ -51,6 +52,8 @@ export async function pruneHistory(
51
52
  stateSlots: prettyPrintIndices(states),
52
53
  });
53
54
 
55
+ await beforePrune(blockCutoffSlot);
56
+
54
57
  const step1 = metrics?.pruneHistory.pruneKeys.startTimer();
55
58
  await Promise.all([
56
59
  // ->
@@ -6,7 +6,7 @@ const PROPOSER_PRESERVE_EPOCHS = 2;
6
6
  export type ProposerPreparationData = routes.validator.ProposerPreparationData;
7
7
 
8
8
  export class BeaconProposerCache {
9
- private readonly feeRecipientByValidatorIndex: Map<number, {epoch: Epoch; feeRecipient: string}>;
9
+ private readonly feeRecipientByValidatorIndex: Map<number, {epoch: Epoch; feeRecipient?: string}>;
10
10
  constructor(readonly opts: {suggestedFeeRecipient: string}) {
11
11
  this.feeRecipientByValidatorIndex = new Map();
12
12
  }
@@ -15,6 +15,24 @@ export class BeaconProposerCache {
15
15
  this.feeRecipientByValidatorIndex.set(validatorIndex, {epoch, feeRecipient});
16
16
  }
17
17
 
18
+ /**
19
+ * Track a validator that uses this node for duties, keeping the fee recipient it may have
20
+ * registered. Returns true if the validator was not tracked before.
21
+ */
22
+ track(epoch: Epoch, validatorIndex: number): boolean {
23
+ const entry = this.feeRecipientByValidatorIndex.get(validatorIndex);
24
+ if (entry !== undefined) {
25
+ entry.epoch = epoch;
26
+ return false;
27
+ }
28
+ this.feeRecipientByValidatorIndex.set(validatorIndex, {epoch});
29
+ return true;
30
+ }
31
+
32
+ has(validatorIndex: number): boolean {
33
+ return this.feeRecipientByValidatorIndex.has(validatorIndex);
34
+ }
35
+
18
36
  prune(epoch: Epoch): void {
19
37
  // This is not so optimized function, but could maintain a 2d array may be?
20
38
  for (const [validatorIndex, feeRecipientEntry] of this.feeRecipientByValidatorIndex.entries()) {
@@ -210,7 +210,7 @@ export class BlockInputPreData extends AbstractBlockInput<ForkPreDeneb, null> {
210
210
  this.blockPromise.resolve(state.block);
211
211
  }
212
212
 
213
- static createFromBlock(props: AddBlock & CreateBlockInputMeta): BlockInputPreData {
213
+ static createFromBlock(props: AddBlock<ForkPreDeneb> & CreateBlockInputMeta): BlockInputPreData {
214
214
  const init: BlockInputInit = {
215
215
  daOutOfRange: props.daOutOfRange,
216
216
  timeCreated: props.seenTimestampSec,
@@ -22,6 +22,7 @@ import {
22
22
  RootCache,
23
23
  computeEpochAtSlot,
24
24
  computeTimeAtSlot,
25
+ getBeaconCommittees,
25
26
  isStatePostAltair,
26
27
  isStatePostBellatrix,
27
28
  } from "@lodestar/state-transition";
@@ -44,7 +45,7 @@ import {isQueueErrorAborted} from "../../util/queue/index.js";
44
45
  import type {BeaconChain} from "../chain.js";
45
46
  import {ChainEvent, ReorgEventData} from "../emitter.js";
46
47
  import {ForkchoiceCaller} from "../forkChoice/index.js";
47
- import {REPROCESS_MIN_TIME_TO_NEXT_SLOT_SEC} from "../reprocess.js";
48
+ import {REPROCESS_MIN_TIME_TO_NEXT_SLOT_BPS} from "../reprocess.js";
48
49
  import {toCheckpointHex} from "../stateCache/persistentCheckpointsCache.js";
49
50
  import {isBlockInputBlobs, isBlockInputColumns} from "./blockInput/blockInput.js";
50
51
  import {AttestationImportOpt, FullyVerifiedBlock, ImportBlockOpts} from "./types.js";
@@ -129,11 +130,17 @@ export async function importBlock(
129
130
 
130
131
  // Should compute checkpoint balances before forkchoice.onBlock
131
132
  this.checkpointBalancesCache.processState(blockRootHex, postState);
132
- if (fork >= ForkSeq.gloas) {
133
+ if (isGloasBeaconBlock(block.message)) {
134
+ // A gloas block has no payload of its own, it inherits the status of the payload it builds on,
135
+ // i.e. the parent variant matching the bid's parent block hash. The parent's default (PENDING)
136
+ // variant does not reflect a SYNCING parent payload, and onBlock would then mark that payload VALID.
133
137
  const parentRootHex = toRootHex(block.message.parentRoot);
134
- const parentBlock = this.forkChoice.getBlockHexDefaultStatus(parentRootHex);
138
+ const parentBlockHashHex = toRootHex(block.message.body.signedExecutionPayloadBid.message.parentBlockHash);
139
+ const parentBlock = this.forkChoice.getBlockHexAndBlockHash(parentRootHex, parentBlockHashHex);
135
140
  if (parentBlock === null) {
136
- throw Error(`Parent block not found in forkChoice, parentRoot=${parentRootHex}`);
141
+ throw Error(
142
+ `Parent block not found in forkChoice, parentRoot=${parentRootHex} parentBlockHash=${parentBlockHashHex}`
143
+ );
137
144
  }
138
145
  if (parentBlock.executionStatus === ExecutionStatus.Invalid) {
139
146
  throw Error(`Parent block has invalid execution status, parentRoot=${parentRootHex}`);
@@ -328,7 +335,8 @@ export async function importBlock(
328
335
  this.metrics.headSlot.set(newHead.slot);
329
336
  // Only track "recent" blocks. Otherwise sync can distort this metrics heavily.
330
337
  // We want to track recent blocks coming from gossip, unknown block sync, and API.
331
- if (delaySec < (SLOTS_PER_EPOCH * this.config.SLOT_DURATION_MS) / 1000) {
338
+ const headFork = this.config.getForkName(newHead.slot);
339
+ if (delaySec < (SLOTS_PER_EPOCH * this.config.getSlotDurationMs(headFork)) / 1000) {
332
340
  this.metrics.importBlock.elapsedTimeTillBecomeHead.observe(delaySec);
333
341
  const cutOffSec = this.config.getAttestationDueMs(this.config.getForkName(blockSlot)) / 1000;
334
342
  if (delaySec > cutOffSec) {
@@ -400,9 +408,8 @@ export async function importBlock(
400
408
  const proposalSlot = blockSlot + 1;
401
409
  try {
402
410
  const proposerIndex = postState.getBeaconProposer(proposalSlot);
403
- const feeRecipient = this.beaconProposerCache.get(proposerIndex);
404
411
 
405
- if (feeRecipient) {
412
+ if (this.beaconProposerCache.has(proposerIndex)) {
406
413
  // We would set this to true if
407
414
  // 1) This is a gossip block
408
415
  // 2) We are proposer of next slot
@@ -580,7 +587,7 @@ export async function importBlock(
580
587
  this.validatorMonitor?.registerSyncAggregateInBlock(
581
588
  blockEpoch,
582
589
  (block as altair.SignedBeaconBlock).message.body.syncAggregate,
583
- fullyVerifiedBlock.postState.currentSyncCommitteeIndexed.validatorIndices
590
+ fullyVerifiedBlock.postState.currentSyncCommitteeValidatorIndices
584
591
  );
585
592
  }
586
593
 
@@ -594,7 +601,10 @@ export async function importBlock(
594
601
  }
595
602
  }
596
603
 
597
- const advancedSlot = this.clock.slotWithFutureTolerance(REPROCESS_MIN_TIME_TO_NEXT_SLOT_SEC);
604
+ const reprocessGuardSec =
605
+ this.config.getSlotComponentDurationMs(this.config.getForkName(blockSlot), REPROCESS_MIN_TIME_TO_NEXT_SLOT_BPS) /
606
+ 1000;
607
+ const advancedSlot = this.clock.slotWithFutureTolerance(reprocessGuardSec);
598
608
 
599
609
  // Gossip blocks need to be imported as soon as possible, waiting attestations could be processed
600
610
  // in the next event loop. See https://github.com/ChainSafe/lodestar/issues/4789
@@ -657,8 +667,12 @@ export function addAttestationPostElectra(
657
667
  } else {
658
668
  const attSlot = attestation.data.slot;
659
669
  const attEpoch = computeEpochAtSlot(attSlot);
660
- const decisionRoot = state.getShufflingDecisionRoot(attEpoch);
661
- const committees = this.shufflingCache.getBeaconCommittees(attEpoch, decisionRoot, attSlot, committeeIndices);
670
+ // calling getShufflingAtEpoch may take some time for NativeBeaconStateView
671
+ // we should have the shuffling inside ShufflingCache most of the time
672
+ const shuffling =
673
+ this.shufflingCache.getSync(attEpoch, state.getShufflingDecisionRoot(attEpoch)) ??
674
+ state.getShufflingAtEpoch(attEpoch);
675
+ const committees = getBeaconCommittees(shuffling, attSlot, committeeIndices);
662
676
  const aggregationBools = attestation.aggregationBits.toBoolArray();
663
677
  let offset = 0;
664
678
  for (let i = 0; i < committees.length; i++) {
@@ -4,6 +4,7 @@ import {
4
4
  DataAvailabilityStatus,
5
5
  IBeaconStateView,
6
6
  computeEpochAtSlot,
7
+ getIndexedAttestation,
7
8
  signedBlockToSignedHeader,
8
9
  } from "@lodestar/state-transition";
9
10
  import {IndexedAttestation, Slot, deneb} from "@lodestar/types";
@@ -81,10 +82,6 @@ export async function verifyBlocksInEpoch(
81
82
  throw new BlockError(block0, {code: BlockErrorCode.PRESTATE_MISSING, error: e as Error});
82
83
  });
83
84
 
84
- // in forky condition, make sure to populate ShufflingCache with regened state
85
- // otherwise it may fail to get indexed attestations from shuffling cache later
86
- this.shufflingCache.processState(preState0);
87
-
88
85
  if (!preState0.isStateValidatorsNodesPopulated()) {
89
86
  this.logger.verbose("verifyBlocksInEpoch preState0 SSZ cache stats", {
90
87
  slot: preState0.slot,
@@ -117,8 +114,12 @@ export async function verifyBlocksInEpoch(
117
114
  for (const [i, block] of blocks.entries()) {
118
115
  indexedAttestationsByBlock[i] = block.message.body.attestations.map((attestation) => {
119
116
  const attEpoch = computeEpochAtSlot(attestation.data.slot);
120
- const decisionRoot = preState0.getShufflingDecisionRoot(attEpoch);
121
- return this.shufflingCache.getIndexedAttestation(attEpoch, decisionRoot, fork, attestation);
117
+ // calling getShufflingAtEpoch may take some time for NativeBeaconStateView
118
+ // we should have the shuffling inside ShufflingCache most of the time
119
+ const shuffling =
120
+ this.shufflingCache.getSync(attEpoch, preState0.getShufflingDecisionRoot(attEpoch)) ??
121
+ preState0.getShufflingAtEpoch(attEpoch);
122
+ return getIndexedAttestation(shuffling, fork, attestation);
122
123
  });
123
124
  }
124
125