@lodestar/beacon-node 1.50.0-dev.c3aaeedc3b → 1.50.0-dev.c785f2463a

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 (255) 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 +6 -4
  6. package/lib/api/impl/beacon/state/index.js.map +1 -1
  7. package/lib/api/impl/validator/index.d.ts.map +1 -1
  8. package/lib/api/impl/validator/index.js +57 -17
  9. package/lib/api/impl/validator/index.js.map +1 -1
  10. package/lib/chain/ColumnReconstructionTracker.d.ts +0 -2
  11. package/lib/chain/ColumnReconstructionTracker.d.ts.map +1 -1
  12. package/lib/chain/ColumnReconstructionTracker.js +4 -5
  13. package/lib/chain/ColumnReconstructionTracker.js.map +1 -1
  14. package/lib/chain/beaconProposerCache.d.ts +6 -0
  15. package/lib/chain/beaconProposerCache.d.ts.map +1 -1
  16. package/lib/chain/beaconProposerCache.js +16 -0
  17. package/lib/chain/beaconProposerCache.js.map +1 -1
  18. package/lib/chain/blocks/blockInput/blockInput.d.ts +1 -1
  19. package/lib/chain/blocks/blockInput/blockInput.d.ts.map +1 -1
  20. package/lib/chain/blocks/blockInput/blockInput.js.map +1 -1
  21. package/lib/chain/blocks/importBlock.d.ts.map +1 -1
  22. package/lib/chain/blocks/importBlock.js +20 -11
  23. package/lib/chain/blocks/importBlock.js.map +1 -1
  24. package/lib/chain/blocks/verifyBlock.d.ts.map +1 -1
  25. package/lib/chain/blocks/verifyBlock.js +6 -6
  26. package/lib/chain/blocks/verifyBlock.js.map +1 -1
  27. package/lib/chain/chain.d.ts +1 -0
  28. package/lib/chain/chain.d.ts.map +1 -1
  29. package/lib/chain/chain.js +52 -17
  30. package/lib/chain/chain.js.map +1 -1
  31. package/lib/chain/interface.d.ts +3 -1
  32. package/lib/chain/interface.d.ts.map +1 -1
  33. package/lib/chain/opPools/opPool.d.ts.map +1 -1
  34. package/lib/chain/opPools/opPool.js +1 -23
  35. package/lib/chain/opPools/opPool.js.map +1 -1
  36. package/lib/chain/opPools/proposerPreferencesPool.d.ts +4 -0
  37. package/lib/chain/opPools/proposerPreferencesPool.d.ts.map +1 -1
  38. package/lib/chain/opPools/proposerPreferencesPool.js +13 -1
  39. package/lib/chain/opPools/proposerPreferencesPool.js.map +1 -1
  40. package/lib/chain/opPools/utils.d.ts +11 -0
  41. package/lib/chain/opPools/utils.d.ts.map +1 -1
  42. package/lib/chain/opPools/utils.js +22 -0
  43. package/lib/chain/opPools/utils.js.map +1 -1
  44. package/lib/chain/prepareNextSlot.d.ts.map +1 -1
  45. package/lib/chain/prepareNextSlot.js +15 -6
  46. package/lib/chain/prepareNextSlot.js.map +1 -1
  47. package/lib/chain/produceBlock/computeNewStateRoot.d.ts.map +1 -1
  48. package/lib/chain/produceBlock/computeNewStateRoot.js.map +1 -1
  49. package/lib/chain/produceBlock/produceBlockBody.d.ts +8 -0
  50. package/lib/chain/produceBlock/produceBlockBody.d.ts.map +1 -1
  51. package/lib/chain/produceBlock/produceBlockBody.js +48 -34
  52. package/lib/chain/produceBlock/produceBlockBody.js.map +1 -1
  53. package/lib/chain/reprocess.d.ts +2 -1
  54. package/lib/chain/reprocess.d.ts.map +1 -1
  55. package/lib/chain/reprocess.js +2 -1
  56. package/lib/chain/reprocess.js.map +1 -1
  57. package/lib/chain/seenCache/seenGossipBlockInput.d.ts.map +1 -1
  58. package/lib/chain/seenCache/seenGossipBlockInput.js +1 -1
  59. package/lib/chain/seenCache/seenGossipBlockInput.js.map +1 -1
  60. package/lib/chain/shufflingCache.d.ts +17 -8
  61. package/lib/chain/shufflingCache.d.ts.map +1 -1
  62. package/lib/chain/shufflingCache.js +68 -18
  63. package/lib/chain/shufflingCache.js.map +1 -1
  64. package/lib/chain/stateCache/persistentCheckpointsCache.d.ts.map +1 -1
  65. package/lib/chain/stateCache/persistentCheckpointsCache.js +1 -1
  66. package/lib/chain/stateCache/persistentCheckpointsCache.js.map +1 -1
  67. package/lib/chain/validatorMonitor.d.ts.map +1 -1
  68. package/lib/chain/validatorMonitor.js +6 -4
  69. package/lib/chain/validatorMonitor.js.map +1 -1
  70. package/lib/constants/constants.d.ts +1 -0
  71. package/lib/constants/constants.d.ts.map +1 -1
  72. package/lib/constants/constants.js +1 -0
  73. package/lib/constants/constants.js.map +1 -1
  74. package/lib/db/beacon.d.ts +2 -1
  75. package/lib/db/beacon.d.ts.map +1 -1
  76. package/lib/db/beacon.js +3 -1
  77. package/lib/db/beacon.js.map +1 -1
  78. package/lib/db/buckets.d.ts +2 -1
  79. package/lib/db/buckets.d.ts.map +1 -1
  80. package/lib/db/buckets.js +1 -0
  81. package/lib/db/buckets.js.map +1 -1
  82. package/lib/db/interface.d.ts +2 -1
  83. package/lib/db/interface.d.ts.map +1 -1
  84. package/lib/db/repositories/blockArchive.js +2 -2
  85. package/lib/db/repositories/blockArchive.js.map +1 -1
  86. package/lib/db/repositories/blockArchiveIndex.d.ts +3 -3
  87. package/lib/db/repositories/blockArchiveIndex.d.ts.map +1 -1
  88. package/lib/db/repositories/blockArchiveIndex.js +1 -2
  89. package/lib/db/repositories/blockArchiveIndex.js.map +1 -1
  90. package/lib/db/repositories/index.d.ts +1 -0
  91. package/lib/db/repositories/index.d.ts.map +1 -1
  92. package/lib/db/repositories/index.js +1 -0
  93. package/lib/db/repositories/index.js.map +1 -1
  94. package/lib/db/repositories/proposerPreferences.d.ts +14 -0
  95. package/lib/db/repositories/proposerPreferences.d.ts.map +1 -0
  96. package/lib/db/repositories/proposerPreferences.js +21 -0
  97. package/lib/db/repositories/proposerPreferences.js.map +1 -0
  98. package/lib/execution/engine/blobVersionedHashes.d.ts +9 -0
  99. package/lib/execution/engine/blobVersionedHashes.d.ts.map +1 -0
  100. package/lib/execution/engine/blobVersionedHashes.js +74 -0
  101. package/lib/execution/engine/blobVersionedHashes.js.map +1 -0
  102. package/lib/execution/engine/http.d.ts +45 -13
  103. package/lib/execution/engine/http.d.ts.map +1 -1
  104. package/lib/execution/engine/http.js +236 -198
  105. package/lib/execution/engine/http.js.map +1 -1
  106. package/lib/execution/engine/index.d.ts +3 -2
  107. package/lib/execution/engine/index.d.ts.map +1 -1
  108. package/lib/execution/engine/index.js +33 -8
  109. package/lib/execution/engine/index.js.map +1 -1
  110. package/lib/execution/engine/interface.d.ts +1 -1
  111. package/lib/execution/engine/interface.d.ts.map +1 -1
  112. package/lib/execution/engine/jsonRpcTransport.d.ts +25 -0
  113. package/lib/execution/engine/jsonRpcTransport.d.ts.map +1 -0
  114. package/lib/execution/engine/jsonRpcTransport.js +158 -0
  115. package/lib/execution/engine/jsonRpcTransport.js.map +1 -0
  116. package/lib/execution/engine/restHttpClient.d.ts +71 -0
  117. package/lib/execution/engine/restHttpClient.d.ts.map +1 -0
  118. package/lib/execution/engine/restHttpClient.js +201 -0
  119. package/lib/execution/engine/restHttpClient.js.map +1 -0
  120. package/lib/execution/engine/restTransport.d.ts +46 -0
  121. package/lib/execution/engine/restTransport.d.ts.map +1 -0
  122. package/lib/execution/engine/restTransport.js +399 -0
  123. package/lib/execution/engine/restTransport.js.map +1 -0
  124. package/lib/execution/engine/sszTypes.d.ts +1261 -0
  125. package/lib/execution/engine/sszTypes.d.ts.map +1 -0
  126. package/lib/execution/engine/sszTypes.js +216 -0
  127. package/lib/execution/engine/sszTypes.js.map +1 -0
  128. package/lib/execution/engine/transport.d.ts +39 -0
  129. package/lib/execution/engine/transport.d.ts.map +1 -0
  130. package/lib/execution/engine/transport.js +2 -0
  131. package/lib/execution/engine/transport.js.map +1 -0
  132. package/lib/execution/engine/types.d.ts +2 -0
  133. package/lib/execution/engine/types.d.ts.map +1 -1
  134. package/lib/execution/engine/types.js +31 -63
  135. package/lib/execution/engine/types.js.map +1 -1
  136. package/lib/execution/engine/utils.d.ts.map +1 -1
  137. package/lib/execution/engine/utils.js +9 -2
  138. package/lib/execution/engine/utils.js.map +1 -1
  139. package/lib/metrics/metrics/lodestar.d.ts +9 -0
  140. package/lib/metrics/metrics/lodestar.d.ts.map +1 -1
  141. package/lib/metrics/metrics/lodestar.js +18 -0
  142. package/lib/metrics/metrics/lodestar.js.map +1 -1
  143. package/lib/network/core/events.d.ts +5 -1
  144. package/lib/network/core/events.d.ts.map +1 -1
  145. package/lib/network/core/events.js +8 -0
  146. package/lib/network/core/events.js.map +1 -1
  147. package/lib/network/gossip/topic.d.ts +0 -14
  148. package/lib/network/gossip/topic.d.ts.map +1 -1
  149. package/lib/network/processor/gossipHandlers.js +1 -1
  150. package/lib/network/processor/gossipHandlers.js.map +1 -1
  151. package/lib/network/processor/index.js +3 -3
  152. package/lib/network/processor/index.js.map +1 -1
  153. package/lib/network/reqresp/rateLimit.js +2 -2
  154. package/lib/network/reqresp/rateLimit.js.map +1 -1
  155. package/lib/network/subnets/attnetsService.d.ts.map +1 -1
  156. package/lib/network/subnets/attnetsService.js +3 -1
  157. package/lib/network/subnets/attnetsService.js.map +1 -1
  158. package/lib/node/notifier.d.ts.map +1 -1
  159. package/lib/node/notifier.js +12 -13
  160. package/lib/node/notifier.js.map +1 -1
  161. package/lib/node/options.d.ts +3 -2
  162. package/lib/node/options.d.ts.map +1 -1
  163. package/lib/node/options.js +2 -2
  164. package/lib/node/options.js.map +1 -1
  165. package/lib/sync/range/batch.d.ts +5 -5
  166. package/lib/sync/range/batch.d.ts.map +1 -1
  167. package/lib/sync/range/batch.js +11 -17
  168. package/lib/sync/range/batch.js.map +1 -1
  169. package/lib/sync/range/chain.d.ts +4 -4
  170. package/lib/sync/range/chain.d.ts.map +1 -1
  171. package/lib/sync/range/chain.js +7 -5
  172. package/lib/sync/range/chain.js.map +1 -1
  173. package/lib/sync/range/range.d.ts.map +1 -1
  174. package/lib/sync/range/range.js +25 -11
  175. package/lib/sync/range/range.js.map +1 -1
  176. package/lib/sync/unknownBlock.d.ts +5 -2
  177. package/lib/sync/unknownBlock.d.ts.map +1 -1
  178. package/lib/sync/unknownBlock.js +47 -22
  179. package/lib/sync/unknownBlock.js.map +1 -1
  180. package/lib/sync/utils/downloadByRange.d.ts +38 -14
  181. package/lib/sync/utils/downloadByRange.d.ts.map +1 -1
  182. package/lib/sync/utils/downloadByRange.js +84 -39
  183. package/lib/sync/utils/downloadByRange.js.map +1 -1
  184. package/lib/util/asyncIterableToEvents.d.ts +10 -2
  185. package/lib/util/asyncIterableToEvents.d.ts.map +1 -1
  186. package/lib/util/asyncIterableToEvents.js +29 -3
  187. package/lib/util/asyncIterableToEvents.js.map +1 -1
  188. package/lib/util/clock.d.ts.map +1 -1
  189. package/lib/util/clock.js +3 -4
  190. package/lib/util/clock.js.map +1 -1
  191. package/lib/util/metadata.d.ts.map +1 -1
  192. package/lib/util/metadata.js +2 -1
  193. package/lib/util/metadata.js.map +1 -1
  194. package/lib/util/sszBytes.d.ts +2 -2
  195. package/lib/util/sszBytes.d.ts.map +1 -1
  196. package/lib/util/sszBytes.js +22 -14
  197. package/lib/util/sszBytes.js.map +1 -1
  198. package/package.json +16 -15
  199. package/src/api/impl/beacon/blocks/index.ts +10 -3
  200. package/src/api/impl/beacon/state/index.ts +13 -4
  201. package/src/api/impl/validator/index.ts +78 -19
  202. package/src/chain/ColumnReconstructionTracker.ts +4 -6
  203. package/src/chain/beaconProposerCache.ts +19 -1
  204. package/src/chain/blocks/blockInput/blockInput.ts +1 -1
  205. package/src/chain/blocks/importBlock.ts +24 -10
  206. package/src/chain/blocks/verifyBlock.ts +7 -6
  207. package/src/chain/chain.ts +62 -17
  208. package/src/chain/interface.ts +4 -1
  209. package/src/chain/opPools/opPool.ts +1 -31
  210. package/src/chain/opPools/proposerPreferencesPool.ts +20 -1
  211. package/src/chain/opPools/utils.ts +31 -0
  212. package/src/chain/prepareNextSlot.ts +22 -6
  213. package/src/chain/produceBlock/computeNewStateRoot.ts +9 -2
  214. package/src/chain/produceBlock/produceBlockBody.ts +78 -39
  215. package/src/chain/reprocess.ts +2 -1
  216. package/src/chain/seenCache/seenGossipBlockInput.ts +2 -1
  217. package/src/chain/shufflingCache.ts +79 -22
  218. package/src/chain/stateCache/persistentCheckpointsCache.ts +4 -1
  219. package/src/chain/validatorMonitor.ts +7 -4
  220. package/src/constants/constants.ts +1 -0
  221. package/src/db/beacon.ts +3 -0
  222. package/src/db/buckets.ts +1 -0
  223. package/src/db/interface.ts +2 -0
  224. package/src/db/repositories/blockArchive.ts +2 -2
  225. package/src/db/repositories/blockArchiveIndex.ts +2 -3
  226. package/src/db/repositories/index.ts +1 -0
  227. package/src/db/repositories/proposerPreferences.ts +23 -0
  228. package/src/execution/engine/blobVersionedHashes.ts +72 -0
  229. package/src/execution/engine/http.ts +304 -271
  230. package/src/execution/engine/index.ts +41 -7
  231. package/src/execution/engine/interface.ts +1 -1
  232. package/src/execution/engine/jsonRpcTransport.ts +247 -0
  233. package/src/execution/engine/restHttpClient.ts +269 -0
  234. package/src/execution/engine/restTransport.ts +544 -0
  235. package/src/execution/engine/sszTypes.ts +354 -0
  236. package/src/execution/engine/transport.ts +61 -0
  237. package/src/execution/engine/types.ts +42 -80
  238. package/src/execution/engine/utils.ts +12 -2
  239. package/src/metrics/metrics/lodestar.ts +19 -0
  240. package/src/network/core/events.ts +16 -1
  241. package/src/network/processor/gossipHandlers.ts +1 -1
  242. package/src/network/processor/index.ts +3 -3
  243. package/src/network/reqresp/rateLimit.ts +2 -2
  244. package/src/network/subnets/attnetsService.ts +8 -3
  245. package/src/node/notifier.ts +14 -13
  246. package/src/node/options.ts +10 -1
  247. package/src/sync/range/batch.ts +14 -19
  248. package/src/sync/range/chain.ts +9 -7
  249. package/src/sync/range/range.ts +26 -12
  250. package/src/sync/unknownBlock.ts +60 -23
  251. package/src/sync/utils/downloadByRange.ts +123 -58
  252. package/src/util/asyncIterableToEvents.ts +44 -5
  253. package/src/util/clock.ts +3 -4
  254. package/src/util/metadata.ts +2 -1
  255. package/src/util/sszBytes.ts +25 -12
@@ -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
@@ -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
 
@@ -58,7 +58,7 @@ import {
58
58
  } from "@lodestar/types";
59
59
  import {Logger, fromHex, gweiToWei, isErrorAborted, pruneSetToMax, sleep, toRootHex} from "@lodestar/utils";
60
60
  import {ProcessShutdownCallback} from "@lodestar/validator";
61
- import {GENESIS_EPOCH, ZERO_HASH} from "../constants/index.js";
61
+ import {GENESIS_EPOCH, HALF_SLOT_BPS, ZERO_HASH} from "../constants/index.js";
62
62
  import {IBeaconDb} from "../db/index.js";
63
63
  import {BLOB_SIDECARS_IN_WRAPPER_INDEX} from "../db/repositories/blobSidecars.js";
64
64
  import {decodeArchivedEnvelope} from "../db/repositories/index.js";
@@ -128,7 +128,7 @@ import {SeenAggregatedAttestations} from "./seenCache/seenAggregateAndProof.js";
128
128
  import {SeenAttestationDatas} from "./seenCache/seenAttestationData.js";
129
129
  import {SeenBlockAttesters} from "./seenCache/seenBlockAttesters.js";
130
130
  import {SeenBlockInput} from "./seenCache/seenGossipBlockInput.js";
131
- import {ShufflingCache} from "./shufflingCache.js";
131
+ import {ShufflingCache, ShufflingPromiseCancelReason} from "./shufflingCache.js";
132
132
  import {DbCPStateDatastore, checkpointToDatastoreKey} from "./stateCache/datastore/db.js";
133
133
  import {FileCPStateDatastore} from "./stateCache/datastore/file.js";
134
134
  import {CPStateDatastore} from "./stateCache/datastore/types.js";
@@ -620,12 +620,14 @@ export class BeaconChain implements IBeaconChain {
620
620
  async loadFromDisk(): Promise<void> {
621
621
  await this.regen.init();
622
622
  await this.opPool.fromPersisted(this.db, this.getHeadState(), this.bls, this.clock.currentSlot);
623
+ await this.proposerPreferencesPool.fromPersisted(this.db, this.clock.currentSlot);
623
624
  }
624
625
 
625
626
  /** Persist in-memory data to the DB. Call at least once before stopping the process */
626
627
  async persistToDisk(): Promise<void> {
627
628
  await this.archiveStore.persistToDisk();
628
629
  await this.opPool.toPersisted(this.db);
630
+ await this.proposerPreferencesPool.toPersisted(this.db);
629
631
  }
630
632
 
631
633
  getHeadState(): IBeaconStateView {
@@ -1629,21 +1631,48 @@ export class BeaconChain implements IBeaconChain {
1629
1631
  const blockEpoch = computeEpochAtSlot(attHeadBlock.slot);
1630
1632
 
1631
1633
  let state: IBeaconStateView;
1632
- if (blockEpoch < attEpoch - 1) {
1633
- // thanks to one epoch look ahead, we don't need to dial up to attEpoch
1634
- const targetSlot = computeStartSlotAtEpoch(attEpoch - 1);
1635
- this.metrics?.gossipAttestation.useHeadBlockStateDialedToTargetEpoch.inc({caller: regenCaller});
1636
- state = await this.regen.getBlockSlotState(attHeadBlock, targetSlot, {dontTransferCache: true}, regenCaller);
1637
- } else if (blockEpoch > attEpoch) {
1638
- // should not happen, handled inside attestation verification code
1639
- throw Error(`Block epoch ${blockEpoch} is after attestation epoch ${attEpoch}`);
1640
- } else {
1641
- // should use either current or next shuffling of head state
1642
- // it's not likely to hit this since these shufflings are cached already
1643
- // so handle just in case
1644
- this.metrics?.gossipAttestation.useHeadBlockState.inc({caller: regenCaller});
1645
- state = await this.regen.getState(attHeadBlock.stateRoot, regenCaller);
1634
+ try {
1635
+ if (blockEpoch < attEpoch - 1) {
1636
+ // thanks to one epoch look ahead, we don't need to dial up to attEpoch
1637
+ const targetSlot = computeStartSlotAtEpoch(attEpoch - 1);
1638
+ this.metrics?.gossipAttestation.useHeadBlockStateDialedToTargetEpoch.inc({caller: regenCaller});
1639
+ state = await this.regen.getBlockSlotState(attHeadBlock, targetSlot, {dontTransferCache: true}, regenCaller);
1640
+ } else if (blockEpoch > attEpoch) {
1641
+ // should not happen, handled inside attestation verification code
1642
+ throw Error(`Block epoch ${blockEpoch} is after attestation epoch ${attEpoch}`);
1643
+ } else {
1644
+ // should use either current or next shuffling of head state
1645
+ // it's not likely to hit this since these shufflings are cached already
1646
+ // so handle just in case
1647
+ this.metrics?.gossipAttestation.useHeadBlockState.inc({caller: regenCaller});
1648
+ state = await this.regen.getState(attHeadBlock.stateRoot, regenCaller);
1649
+ }
1650
+ } catch (e) {
1651
+ this.shufflingCache.cancelPromise(attEpoch, shufflingDependentRoot, ShufflingPromiseCancelReason.regenError);
1652
+ throw e;
1653
+ }
1654
+
1655
+ const stateDecisionRoot = state.getShufflingDecisionRoot(attEpoch);
1656
+ if (stateDecisionRoot !== shufflingDependentRoot) {
1657
+ // the promise would never be resolved by the state's shuffling
1658
+ this.shufflingCache.cancelPromise(
1659
+ attEpoch,
1660
+ shufflingDependentRoot,
1661
+ ShufflingPromiseCancelReason.decisionRootMismatch
1662
+ );
1663
+ this.logger.debug("Shuffling decision root mismatch on attestation regen", {
1664
+ slot: attHeadBlock.slot,
1665
+ root: attHeadBlock.blockRoot,
1666
+ epoch: attEpoch,
1667
+ shufflingDependentRoot,
1668
+ stateDecisionRoot,
1669
+ });
1670
+
1671
+ throw Error(
1672
+ `Shuffling decision root mismatch epoch=${attEpoch} slot=${attHeadBlock.slot} root=${attHeadBlock.blockRoot} expected=${shufflingDependentRoot} actual=${stateDecisionRoot}`
1673
+ );
1646
1674
  }
1675
+
1647
1676
  // resolve the promise to unblock other calls of the same epoch and dependent root
1648
1677
  this.shufflingCache.processState(state);
1649
1678
  return state.getShufflingAtEpoch(attEpoch);
@@ -1812,7 +1841,8 @@ export class BeaconChain implements IBeaconChain {
1812
1841
  const metrics = this.metrics;
1813
1842
  if (metrics && (slot + 1) % SLOTS_PER_EPOCH === 0) {
1814
1843
  // On the last slot of the epoch
1815
- sleep(this.config.SLOT_DURATION_MS / 2)
1844
+ const fork = this.config.getForkName(slot);
1845
+ sleep(this.config.getSlotComponentDurationMs(fork, HALF_SLOT_BPS))
1816
1846
  .then(() => this.validatorMonitor?.onceEveryEndOfEpoch(this.getHeadState()))
1817
1847
  .catch((e) => {
1818
1848
  if (!isErrorAborted(e)) this.logger.error("Error on validator monitor onceEveryEndOfEpoch", {slot}, e);
@@ -1907,6 +1937,21 @@ export class BeaconChain implements IBeaconChain {
1907
1937
  }
1908
1938
  }
1909
1939
 
1940
+ async updateAttachedValidators(epoch: Epoch, validatorIndices: ValidatorIndex[]): Promise<void> {
1941
+ let hasNewValidators = false;
1942
+ for (const validatorIndex of validatorIndices) {
1943
+ if (this.beaconProposerCache.track(epoch, validatorIndex)) {
1944
+ hasNewValidators = true;
1945
+ }
1946
+ }
1947
+
1948
+ // Only update validator custody if we discovered new validators
1949
+ if (hasNewValidators) {
1950
+ const finalizedCheckpoint = this.forkChoice.getFinalizedCheckpoint();
1951
+ await this.updateValidatorsCustodyRequirement(finalizedCheckpoint);
1952
+ }
1953
+ }
1954
+
1910
1955
  private async updateValidatorsCustodyRequirement(finalizedCheckpoint: CheckpointWithHex): Promise<void> {
1911
1956
  if (this.custodyConfig.targetCustodyGroupCount === this.config.NUMBER_OF_CUSTODY_GROUPS) {
1912
1957
  // Custody requirements can only be increased, we can disable dynamic custody updates
@@ -288,6 +288,8 @@ export interface IBeaconChain {
288
288
  waitForBlock(slot: Slot, root: RootHex): Promise<boolean>;
289
289
 
290
290
  updateBeaconProposerData(epoch: Epoch, proposers: ProposerPreparationData[]): Promise<void>;
291
+ /** Track validators that use this node for duties, e.g. by subscribing to attestation subnets */
292
+ updateAttachedValidators(epoch: Epoch, validatorIndices: ValidatorIndex[]): Promise<void>;
291
293
 
292
294
  persistBlock(data: BeaconBlock | BlindedBeaconBlock, suffix?: string): void;
293
295
  persistExecutionPayloadEnvelope(envelope: gloas.ExecutionPayloadEnvelope): void;
@@ -329,6 +331,7 @@ export type SSZObjectType =
329
331
  | "syncCommittee"
330
332
  | "contributionAndProof";
331
333
 
332
- export type CommonBlockBody = phase0.BeaconBlockBody &
334
+ export type CommonBlockBody = Omit<phase0.BeaconBlockBody, "eth1Data" | "deposits"> &
335
+ Partial<Pick<phase0.BeaconBlockBody, "eth1Data" | "deposits">> &
333
336
  Pick<capella.BeaconBlockBody, "blsToExecutionChanges"> &
334
337
  Pick<altair.BeaconBlockBody, "syncAggregate">;
@@ -1,5 +1,4 @@
1
1
  import {BeaconConfig} from "@lodestar/config";
2
- import {DbBatch, Id, Repository} from "@lodestar/db";
3
2
  import {
4
3
  BLS_WITHDRAWAL_PREFIX,
5
4
  ForkName,
@@ -42,6 +41,7 @@ import {
42
41
  getAttesterSlashingSignatureDomains,
43
42
  getProposerSlashingSignatureDomain,
44
43
  isValidBlsToExecutionChangeForBlockInclusion,
44
+ persistDiff,
45
45
  } from "./utils.js";
46
46
 
47
47
  type HexRoot = string;
@@ -500,33 +500,3 @@ function isVoluntaryExitSignatureIncludable(stateFork: ForkSeq, voluntaryExitFor
500
500
  function isSlashableAtEpoch(validator: phase0.Validator, epoch: Epoch): boolean {
501
501
  return !validator.slashed && validator.activationEpoch <= epoch && epoch < validator.withdrawableEpoch;
502
502
  }
503
-
504
- /**
505
- * Persist target items `items` in `dbRepo` doing minimum put and delete writes.
506
- * Reads all keys in repository to compute the diff between current persisted data and target data.
507
- */
508
- async function persistDiff<K extends Id, V>(
509
- dbRepo: Repository<K, V>,
510
- items: {key: K; value: V}[],
511
- serializeKey: (key: K) => number | string,
512
- opts: {updateExisting?: boolean} = {}
513
- ): Promise<void> {
514
- const persistedKeys = await dbRepo.keys();
515
- const batch: DbBatch<K, V> = [];
516
-
517
- const persistedKeysSerialized = new Set(persistedKeys.map(serializeKey));
518
- for (const item of items) {
519
- if (opts.updateExisting || !persistedKeysSerialized.has(serializeKey(item.key))) {
520
- batch.push({type: "put", key: item.key, value: item.value});
521
- }
522
- }
523
-
524
- const targetKeysSerialized = new Set(items.map((item) => serializeKey(item.key)));
525
- for (const persistedKey of persistedKeys) {
526
- if (!targetKeysSerialized.has(serializeKey(persistedKey))) {
527
- batch.push({type: "del", key: persistedKey});
528
- }
529
- }
530
-
531
- if (batch.length > 0) await dbRepo.batch(batch);
532
- }
@@ -1,5 +1,7 @@
1
1
  import {RootHex, Slot, ValidatorIndex, gloas} from "@lodestar/types";
2
- import {toRootHex} from "@lodestar/utils";
2
+ import {toHex, toRootHex} from "@lodestar/utils";
3
+ import {IBeaconDb} from "../../db/index.js";
4
+ import {persistDiff} from "./utils.js";
3
5
 
4
6
  /**
5
7
  * Pool of validated `SignedProposerPreferences` indexed by `(slot, dependent_root)`.
@@ -64,4 +66,21 @@ export class ProposerPreferencesPool {
64
66
  if (slot < currentSlot) this.bySlot.delete(slot);
65
67
  }
66
68
  }
69
+
70
+ /** Restore the preferences of upcoming slots, they were validated before they were persisted */
71
+ async fromPersisted(db: IBeaconDb, currentSlot: Slot): Promise<void> {
72
+ for (const signed of await db.proposerPreferences.values()) {
73
+ if (signed.message.proposalSlot >= currentSlot) {
74
+ this.add(signed);
75
+ }
76
+ }
77
+ }
78
+
79
+ async toPersisted(db: IBeaconDb): Promise<void> {
80
+ await persistDiff(
81
+ db.proposerPreferences,
82
+ this.getAll().map((signed) => ({key: db.proposerPreferences.getId(signed), value: signed})),
83
+ toHex
84
+ );
85
+ }
67
86
  }
@@ -1,5 +1,6 @@
1
1
  import {Signature} from "@chainsafe/lodestar-z/blst";
2
2
  import {BeaconConfig} from "@lodestar/config";
3
+ import {DbBatch, Id, Repository} from "@lodestar/db";
3
4
  import {BLS_WITHDRAWAL_PREFIX, DOMAIN_BEACON_ATTESTER, DOMAIN_BEACON_PROPOSER} from "@lodestar/params";
4
5
  import {IBeaconStateView, computeStartSlotAtEpoch} from "@lodestar/state-transition";
5
6
  import {AttesterSlashing, Domain, Slot, capella, phase0} from "@lodestar/types";
@@ -91,3 +92,33 @@ export function isValidBlsToExecutionChangeForBlockInclusion(
91
92
  export function isElectraAggregate(aggregate: AggregateFast): aggregate is AggregateFastElectra {
92
93
  return (aggregate as AggregateFastElectra).committeeBits !== undefined;
93
94
  }
95
+
96
+ /**
97
+ * Persist target items `items` in `dbRepo` doing minimum put and delete writes.
98
+ * Reads all keys in repository to compute the diff between current persisted data and target data.
99
+ */
100
+ export async function persistDiff<K extends Id, V>(
101
+ dbRepo: Repository<K, V>,
102
+ items: {key: K; value: V}[],
103
+ serializeKey: (key: K) => number | string,
104
+ opts: {updateExisting?: boolean} = {}
105
+ ): Promise<void> {
106
+ const persistedKeys = await dbRepo.keys();
107
+ const batch: DbBatch<K, V> = [];
108
+
109
+ const persistedKeysSerialized = new Set(persistedKeys.map(serializeKey));
110
+ for (const item of items) {
111
+ if (opts.updateExisting || !persistedKeysSerialized.has(serializeKey(item.key))) {
112
+ batch.push({type: "put", key: item.key, value: item.value});
113
+ }
114
+ }
115
+
116
+ const targetKeysSerialized = new Set(items.map((item) => serializeKey(item.key)));
117
+ for (const persistedKey of persistedKeys) {
118
+ if (!targetKeysSerialized.has(serializeKey(persistedKey))) {
119
+ batch.push({type: "del", key: persistedKey});
120
+ }
121
+ }
122
+
123
+ if (batch.length > 0) await dbRepo.batch(batch);
124
+ }
@@ -22,7 +22,7 @@ import {
22
22
  isStatePostGloas,
23
23
  } from "@lodestar/state-transition";
24
24
  import {Bytes32, Slot, ValidatorIndex} from "@lodestar/types";
25
- import {Logger, fromHex, isErrorAborted, sleep} from "@lodestar/utils";
25
+ import {Logger, fromHex, isErrorAborted, sleep, toHex} from "@lodestar/utils";
26
26
  import {GENESIS_SLOT} from "../constants/constants.js";
27
27
  import {BuilderStatus} from "../execution/builder/http.js";
28
28
  import {PayloadAttributes} from "../execution/index.js";
@@ -31,7 +31,11 @@ import {ClockEvent} from "../util/clock.js";
31
31
  import {isQueueErrorAborted} from "../util/queue/index.js";
32
32
  import {ForkchoiceCaller} from "./forkChoice/index.js";
33
33
  import {IBeaconChain} from "./interface.js";
34
- import {getPayloadAttributesForSSE, prepareExecutionPayload} from "./produceBlock/produceBlockBody.js";
34
+ import {
35
+ getPayloadAttributesForSSE,
36
+ getPooledProposerPreferences,
37
+ prepareExecutionPayload,
38
+ } from "./produceBlock/produceBlockBody.js";
35
39
  import {RegenCaller} from "./regen/index.js";
36
40
 
37
41
  // TODO GLOAS: re-evaluate this timing
@@ -92,6 +96,7 @@ export class PrepareNextSlotScheduler {
92
96
  const nextEpoch = computeEpochAtSlot(clockSlot) + 1;
93
97
  const isEpochTransition = prepareEpoch === nextEpoch;
94
98
  const fork = this.config.getForkName(prepareSlot);
99
+ const currentFork = this.config.getForkName(clockSlot);
95
100
 
96
101
  // Early return if we are pre-genesis
97
102
  // or we are pre-bellatrix and this is not an epoch transition
@@ -102,7 +107,7 @@ export class PrepareNextSlotScheduler {
102
107
  try {
103
108
  // At PREPARE_NEXT_SLOT_BPS (~67%) of the current slot we prepare payload for the next slot
104
109
  // or precompute epoch transition
105
- await sleep(this.config.getSlotComponentDurationMs(PREPARE_NEXT_SLOT_BPS), this.signal);
110
+ await sleep(this.config.getSlotComponentDurationMs(currentFork, PREPARE_NEXT_SLOT_BPS), this.signal);
106
111
 
107
112
  // calling updateHead() here before we produce a block to reduce reorg possibility
108
113
  const headBlock = this.chain.recomputeForkChoiceHead(ForkchoiceCaller.prepareNextSlot);
@@ -158,7 +163,15 @@ export class PrepareNextSlotScheduler {
158
163
  };
159
164
 
160
165
  const proposerIndex = await getProposerIndex();
161
- feeRecipient = this.chain.beaconProposerCache.get(proposerIndex);
166
+ if (this.chain.beaconProposerCache.has(proposerIndex)) {
167
+ const pooledPreferences = isForkPostGloas(fork)
168
+ ? getPooledProposerPreferences(this.chain, prepareSlot, headRoot)
169
+ : null;
170
+ feeRecipient =
171
+ pooledPreferences !== null
172
+ ? toHex(pooledPreferences.feeRecipient)
173
+ : this.chain.beaconProposerCache.getOrDefault(proposerIndex);
174
+ }
162
175
 
163
176
  // Predict the proposer head of the next slot when either:
164
177
  // - we are proposing the next slot (single-slot proposer-boost-reorg), or
@@ -357,7 +370,7 @@ export class PrepareNextSlotScheduler {
357
370
  // skip when we're the next-slot proposer — don't compete with block-production prep.
358
371
  feeRecipient === undefined
359
372
  ) {
360
- const maxDurationMs = this.config.getSlotComponentDurationMs(BUILDER_PREVERIFY_LIMIT_BPS);
373
+ const maxDurationMs = this.config.getSlotComponentDurationMs(currentFork, BUILDER_PREVERIFY_LIMIT_BPS);
361
374
  const preVerifyTimer = this.metrics?.builderDepositPreVerify.duration.startTimer();
362
375
  const result = headState.preVerifyBuilderDepositsPreGloas(MAX_BUILDER_DEPOSITS_PER_SLOT, maxDurationMs);
363
376
  preVerifyTimer?.();
@@ -411,7 +424,10 @@ export class PrepareNextSlotScheduler {
411
424
  * later in the slot, connecting at PREPARE_NEXT_SLOT_BPS would go cold before bids are requested.
412
425
  */
413
426
  private checkBuilderStatusBeforeSlot(prepareSlot: Slot): void {
414
- const msBeforeSlot = this.config.getSlotComponentDurationMs(BUILDER_STATUS_CHECK_BEFORE_SLOT_BPS);
427
+ const msBeforeSlot = this.config.getSlotComponentDurationMs(
428
+ this.config.getForkName(prepareSlot - 1),
429
+ BUILDER_STATUS_CHECK_BEFORE_SLOT_BPS
430
+ );
415
431
  const msUntilCheck = -this.chain.clock.msFromSlot(prepareSlot) - msBeforeSlot;
416
432
  sleep(Math.max(0, msUntilCheck), this.signal)
417
433
  .then(() => this.chain.builderApiClient.checkStatus())
@@ -1,5 +1,12 @@
1
1
  import {EMPTY_SIGNATURE, IBeaconStateView, StateHashTreeRootSource} from "@lodestar/state-transition";
2
- import {BeaconBlock, BlindedBeaconBlock, Gwei, Root} from "@lodestar/types";
2
+ import {
3
+ BeaconBlock,
4
+ BlindedBeaconBlock,
5
+ Gwei,
6
+ Root,
7
+ SignedBeaconBlock,
8
+ SignedBlindedBeaconBlock,
9
+ } from "@lodestar/types";
3
10
  import {Metrics} from "../../metrics/index.js";
4
11
 
5
12
  /**
@@ -13,7 +20,7 @@ export function computeNewStateRoot(
13
20
  block: BeaconBlock | BlindedBeaconBlock
14
21
  ): {newStateRoot: Root; proposerReward: Gwei; postState: IBeaconStateView} {
15
22
  // Set signature to zero to re-use stateTransition() function which requires the SignedBeaconBlock type
16
- const signedBlock = {message: block, signature: EMPTY_SIGNATURE};
23
+ const signedBlock = {message: block, signature: EMPTY_SIGNATURE} as SignedBeaconBlock | SignedBlindedBeaconBlock;
17
24
  const {newStateRoot, proposerReward, postState, hashTreeRootTime} = state.computeNewStateRoot(
18
25
  {block: signedBlock},
19
26
  {metrics: metrics?.stateTransition}