@lodestar/beacon-node 1.50.0-dev.08c4bed201 → 1.50.0-dev.09bef4706f

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 (178) 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/validator/index.d.ts.map +1 -1
  5. package/lib/api/impl/validator/index.js +57 -17
  6. package/lib/api/impl/validator/index.js.map +1 -1
  7. package/lib/chain/ColumnReconstructionTracker.d.ts +0 -2
  8. package/lib/chain/ColumnReconstructionTracker.d.ts.map +1 -1
  9. package/lib/chain/ColumnReconstructionTracker.js +4 -5
  10. package/lib/chain/ColumnReconstructionTracker.js.map +1 -1
  11. package/lib/chain/beaconProposerCache.d.ts +6 -0
  12. package/lib/chain/beaconProposerCache.d.ts.map +1 -1
  13. package/lib/chain/beaconProposerCache.js +16 -0
  14. package/lib/chain/beaconProposerCache.js.map +1 -1
  15. package/lib/chain/blocks/blockInput/blockInput.d.ts +1 -1
  16. package/lib/chain/blocks/blockInput/blockInput.d.ts.map +1 -1
  17. package/lib/chain/blocks/blockInput/blockInput.js.map +1 -1
  18. package/lib/chain/blocks/importBlock.d.ts.map +1 -1
  19. package/lib/chain/blocks/importBlock.js +8 -6
  20. package/lib/chain/blocks/importBlock.js.map +1 -1
  21. package/lib/chain/chain.d.ts +1 -0
  22. package/lib/chain/chain.d.ts.map +1 -1
  23. package/lib/chain/chain.js +16 -2
  24. package/lib/chain/chain.js.map +1 -1
  25. package/lib/chain/interface.d.ts +3 -1
  26. package/lib/chain/interface.d.ts.map +1 -1
  27. package/lib/chain/prepareNextSlot.d.ts.map +1 -1
  28. package/lib/chain/prepareNextSlot.js +15 -6
  29. package/lib/chain/prepareNextSlot.js.map +1 -1
  30. package/lib/chain/produceBlock/computeNewStateRoot.d.ts.map +1 -1
  31. package/lib/chain/produceBlock/computeNewStateRoot.js.map +1 -1
  32. package/lib/chain/produceBlock/produceBlockBody.d.ts +8 -0
  33. package/lib/chain/produceBlock/produceBlockBody.d.ts.map +1 -1
  34. package/lib/chain/produceBlock/produceBlockBody.js +48 -34
  35. package/lib/chain/produceBlock/produceBlockBody.js.map +1 -1
  36. package/lib/chain/reprocess.d.ts +2 -1
  37. package/lib/chain/reprocess.d.ts.map +1 -1
  38. package/lib/chain/reprocess.js +2 -1
  39. package/lib/chain/reprocess.js.map +1 -1
  40. package/lib/chain/seenCache/seenGossipBlockInput.d.ts.map +1 -1
  41. package/lib/chain/seenCache/seenGossipBlockInput.js +1 -1
  42. package/lib/chain/seenCache/seenGossipBlockInput.js.map +1 -1
  43. package/lib/chain/stateCache/persistentCheckpointsCache.d.ts.map +1 -1
  44. package/lib/chain/stateCache/persistentCheckpointsCache.js +1 -1
  45. package/lib/chain/stateCache/persistentCheckpointsCache.js.map +1 -1
  46. package/lib/chain/validatorMonitor.d.ts.map +1 -1
  47. package/lib/chain/validatorMonitor.js +6 -4
  48. package/lib/chain/validatorMonitor.js.map +1 -1
  49. package/lib/constants/constants.d.ts +1 -0
  50. package/lib/constants/constants.d.ts.map +1 -1
  51. package/lib/constants/constants.js +1 -0
  52. package/lib/constants/constants.js.map +1 -1
  53. package/lib/db/repositories/blockArchive.js +2 -2
  54. package/lib/db/repositories/blockArchive.js.map +1 -1
  55. package/lib/db/repositories/blockArchiveIndex.d.ts +1 -1
  56. package/lib/db/repositories/blockArchiveIndex.d.ts.map +1 -1
  57. package/lib/db/repositories/blockArchiveIndex.js +1 -2
  58. package/lib/db/repositories/blockArchiveIndex.js.map +1 -1
  59. package/lib/execution/engine/blobVersionedHashes.d.ts +9 -0
  60. package/lib/execution/engine/blobVersionedHashes.d.ts.map +1 -0
  61. package/lib/execution/engine/blobVersionedHashes.js +74 -0
  62. package/lib/execution/engine/blobVersionedHashes.js.map +1 -0
  63. package/lib/execution/engine/http.d.ts +45 -13
  64. package/lib/execution/engine/http.d.ts.map +1 -1
  65. package/lib/execution/engine/http.js +236 -198
  66. package/lib/execution/engine/http.js.map +1 -1
  67. package/lib/execution/engine/index.d.ts +3 -2
  68. package/lib/execution/engine/index.d.ts.map +1 -1
  69. package/lib/execution/engine/index.js +33 -8
  70. package/lib/execution/engine/index.js.map +1 -1
  71. package/lib/execution/engine/interface.d.ts +1 -1
  72. package/lib/execution/engine/interface.d.ts.map +1 -1
  73. package/lib/execution/engine/jsonRpcHttpClient.d.ts.map +1 -1
  74. package/lib/execution/engine/jsonRpcHttpClient.js +14 -4
  75. package/lib/execution/engine/jsonRpcHttpClient.js.map +1 -1
  76. package/lib/execution/engine/jsonRpcTransport.d.ts +25 -0
  77. package/lib/execution/engine/jsonRpcTransport.d.ts.map +1 -0
  78. package/lib/execution/engine/jsonRpcTransport.js +158 -0
  79. package/lib/execution/engine/jsonRpcTransport.js.map +1 -0
  80. package/lib/execution/engine/restHttpClient.d.ts +71 -0
  81. package/lib/execution/engine/restHttpClient.d.ts.map +1 -0
  82. package/lib/execution/engine/restHttpClient.js +201 -0
  83. package/lib/execution/engine/restHttpClient.js.map +1 -0
  84. package/lib/execution/engine/restTransport.d.ts +48 -0
  85. package/lib/execution/engine/restTransport.d.ts.map +1 -0
  86. package/lib/execution/engine/restTransport.js +411 -0
  87. package/lib/execution/engine/restTransport.js.map +1 -0
  88. package/lib/execution/engine/sszTypes.d.ts +1261 -0
  89. package/lib/execution/engine/sszTypes.d.ts.map +1 -0
  90. package/lib/execution/engine/sszTypes.js +216 -0
  91. package/lib/execution/engine/sszTypes.js.map +1 -0
  92. package/lib/execution/engine/transport.d.ts +39 -0
  93. package/lib/execution/engine/transport.d.ts.map +1 -0
  94. package/lib/execution/engine/transport.js +2 -0
  95. package/lib/execution/engine/transport.js.map +1 -0
  96. package/lib/execution/engine/transportMetrics.d.ts +5 -1
  97. package/lib/execution/engine/transportMetrics.d.ts.map +1 -1
  98. package/lib/execution/engine/types.d.ts +2 -0
  99. package/lib/execution/engine/types.d.ts.map +1 -1
  100. package/lib/execution/engine/types.js +31 -63
  101. package/lib/execution/engine/types.js.map +1 -1
  102. package/lib/execution/engine/utils.d.ts.map +1 -1
  103. package/lib/execution/engine/utils.js +9 -2
  104. package/lib/execution/engine/utils.js.map +1 -1
  105. package/lib/metrics/metrics/lodestar.d.ts +7 -0
  106. package/lib/metrics/metrics/lodestar.d.ts.map +1 -1
  107. package/lib/metrics/metrics/lodestar.js +12 -1
  108. package/lib/metrics/metrics/lodestar.js.map +1 -1
  109. package/lib/network/gossip/topic.d.ts +0 -14
  110. package/lib/network/gossip/topic.d.ts.map +1 -1
  111. package/lib/network/processor/gossipHandlers.js +1 -1
  112. package/lib/network/processor/gossipHandlers.js.map +1 -1
  113. package/lib/network/processor/index.js +3 -3
  114. package/lib/network/processor/index.js.map +1 -1
  115. package/lib/network/reqresp/rateLimit.js +2 -2
  116. package/lib/network/reqresp/rateLimit.js.map +1 -1
  117. package/lib/network/subnets/attnetsService.d.ts.map +1 -1
  118. package/lib/network/subnets/attnetsService.js +3 -1
  119. package/lib/network/subnets/attnetsService.js.map +1 -1
  120. package/lib/node/notifier.d.ts.map +1 -1
  121. package/lib/node/notifier.js +12 -13
  122. package/lib/node/notifier.js.map +1 -1
  123. package/lib/node/options.d.ts +3 -2
  124. package/lib/node/options.d.ts.map +1 -1
  125. package/lib/node/options.js +2 -2
  126. package/lib/node/options.js.map +1 -1
  127. package/lib/sync/unknownBlock.d.ts.map +1 -1
  128. package/lib/sync/unknownBlock.js +3 -2
  129. package/lib/sync/unknownBlock.js.map +1 -1
  130. package/lib/util/clock.d.ts.map +1 -1
  131. package/lib/util/clock.js +3 -4
  132. package/lib/util/clock.js.map +1 -1
  133. package/lib/util/sszBytes.d.ts +2 -2
  134. package/lib/util/sszBytes.d.ts.map +1 -1
  135. package/lib/util/sszBytes.js +22 -14
  136. package/lib/util/sszBytes.js.map +1 -1
  137. package/package.json +16 -15
  138. package/src/api/impl/beacon/blocks/index.ts +10 -3
  139. package/src/api/impl/validator/index.ts +78 -19
  140. package/src/chain/ColumnReconstructionTracker.ts +4 -6
  141. package/src/chain/beaconProposerCache.ts +19 -1
  142. package/src/chain/blocks/blockInput/blockInput.ts +1 -1
  143. package/src/chain/blocks/importBlock.ts +9 -6
  144. package/src/chain/chain.ts +18 -2
  145. package/src/chain/interface.ts +4 -1
  146. package/src/chain/prepareNextSlot.ts +22 -6
  147. package/src/chain/produceBlock/computeNewStateRoot.ts +9 -2
  148. package/src/chain/produceBlock/produceBlockBody.ts +78 -39
  149. package/src/chain/reprocess.ts +2 -1
  150. package/src/chain/seenCache/seenGossipBlockInput.ts +2 -1
  151. package/src/chain/stateCache/persistentCheckpointsCache.ts +4 -1
  152. package/src/chain/validatorMonitor.ts +7 -4
  153. package/src/constants/constants.ts +1 -0
  154. package/src/db/repositories/blockArchive.ts +2 -2
  155. package/src/db/repositories/blockArchiveIndex.ts +2 -3
  156. package/src/execution/engine/blobVersionedHashes.ts +72 -0
  157. package/src/execution/engine/http.ts +304 -271
  158. package/src/execution/engine/index.ts +42 -7
  159. package/src/execution/engine/interface.ts +1 -1
  160. package/src/execution/engine/jsonRpcHttpClient.ts +12 -5
  161. package/src/execution/engine/jsonRpcTransport.ts +247 -0
  162. package/src/execution/engine/restHttpClient.ts +269 -0
  163. package/src/execution/engine/restTransport.ts +575 -0
  164. package/src/execution/engine/sszTypes.ts +354 -0
  165. package/src/execution/engine/transport.ts +61 -0
  166. package/src/execution/engine/transportMetrics.ts +2 -1
  167. package/src/execution/engine/types.ts +42 -80
  168. package/src/execution/engine/utils.ts +12 -2
  169. package/src/metrics/metrics/lodestar.ts +12 -1
  170. package/src/network/processor/gossipHandlers.ts +1 -1
  171. package/src/network/processor/index.ts +3 -3
  172. package/src/network/reqresp/rateLimit.ts +2 -2
  173. package/src/network/subnets/attnetsService.ts +8 -3
  174. package/src/node/notifier.ts +14 -13
  175. package/src/node/options.ts +10 -1
  176. package/src/sync/unknownBlock.ts +3 -2
  177. package/src/util/clock.ts +3 -4
  178. package/src/util/sszBytes.ts +25 -12
@@ -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}
@@ -20,6 +20,7 @@ import {
20
20
  isForkPostAltair,
21
21
  isForkPostBellatrix,
22
22
  isForkPostGloas,
23
+ isForkPostHeze,
23
24
  } from "@lodestar/params";
24
25
  import {
25
26
  G2_POINT_AT_INFINITY,
@@ -77,6 +78,7 @@ import {IExecutionBuilder, IExecutionEngine, PayloadAttributes, PayloadId} from
77
78
  import {getShufflingDependentRoot} from "../../util/dependentRoot.js";
78
79
  import {fromGraffitiBytes} from "../../util/graffiti.js";
79
80
  import {kzg} from "../../util/kzg.js";
81
+ import {BeaconProposerCache} from "../beaconProposerCache.js";
80
82
  import type {BeaconChain} from "../chain.js";
81
83
  import {CommonBlockBody} from "../interface.js";
82
84
  import {ProposerPreferencesPool} from "../opPools/index.js";
@@ -286,10 +288,13 @@ export async function produceBlockBody<T extends BlockType>(
286
288
  // this into a completely separate function and have pre/post gloas more separated
287
289
  const safeBlockHash = getSafeExecutionBlockHash(this.forkChoice, this.logger);
288
290
  const finalizedBlockHash = getFinalizedExecutionBlockHash(this.forkChoice);
289
- // TODO GLOAS: post-Gloas, proposer feeRecipient is also carried (signed) in
290
- // ProposerPreferencesPool. Consider using this unified cache instead
291
- // see https://github.com/ChainSafe/lodestar/issues/9379
292
- const feeRecipient = requestedFeeRecipient ?? this.beaconProposerCache.getOrDefault(proposerIndex);
291
+ const {feeRecipient, feeRecipientType} = getProposerFeeRecipient(
292
+ requestedFeeRecipient,
293
+ getPooledProposerPreferences(this, blockSlot, parentBlock.blockRoot),
294
+ this.beaconProposerCache,
295
+ proposerIndex
296
+ );
297
+ Object.assign(logMeta, {feeRecipientType, feeRecipient});
293
298
 
294
299
  const endExecutionPayload = this.metrics?.executionBlockProductionTimeSteps.startTimer();
295
300
 
@@ -442,13 +447,12 @@ export async function produceBlockBody<T extends BlockType>(
442
447
 
443
448
  const safeBlockHash = getSafeExecutionBlockHash(this.forkChoice, this.logger);
444
449
  const finalizedBlockHash = getFinalizedExecutionBlockHash(this.forkChoice);
445
- const feeRecipient = requestedFeeRecipient ?? this.beaconProposerCache.getOrDefault(proposerIndex);
446
- const feeRecipientType = requestedFeeRecipient
447
- ? "requested"
448
- : this.beaconProposerCache.get(proposerIndex)
449
- ? "cached"
450
- : "default";
451
-
450
+ const {feeRecipient, feeRecipientType} = getProposerFeeRecipient(
451
+ requestedFeeRecipient,
452
+ null,
453
+ this.beaconProposerCache,
454
+ proposerIndex
455
+ );
452
456
  Object.assign(logMeta, {feeRecipientType, feeRecipient});
453
457
 
454
458
  if (blockType === BlockType.Blinded) {
@@ -681,12 +685,11 @@ export async function produceBlockBody<T extends BlockType>(
681
685
  executionPayloadValue = BigInt(0);
682
686
  }
683
687
 
684
- const {graffiti, attestations, deposits, voluntaryExits, attesterSlashings, proposerSlashings} = blockBody;
688
+ const {graffiti, attestations, voluntaryExits, attesterSlashings, proposerSlashings} = blockBody;
685
689
 
686
690
  Object.assign(logMeta, {
687
691
  graffiti: fromGraffitiBytes(graffiti),
688
692
  attestations: attestations.length,
689
- deposits: deposits.length,
690
693
  voluntaryExits: voluntaryExits.length,
691
694
  attesterSlashings: attesterSlashings.length,
692
695
  proposerSlashings: proposerSlashings.length,
@@ -974,9 +977,8 @@ function preparePayloadAttributes(
974
977
  * Resolve the proposer's preferred (target) gas limit for the Gloas `PayloadAttributesV4`
975
978
  * `targetGasLimit` field (consensus-specs#5235, execution-apis#796).
976
979
  *
977
- * Sourced from the `SignedProposerPreferences` the proposer's VC submitted to the pool
978
- * (same `(slot, dependent_root)` lookup as gossip bid validation). When no matching
979
- * preferences are pooled, target the parent payload's gas limit so the gas limit stays
980
+ * Sourced from the `SignedProposerPreferences` the proposer's VC submitted to the pool. When no
981
+ * matching preferences are pooled, target the parent payload's gas limit so the gas limit stays
980
982
  * unchanged (`is_gas_limit_target_compatible` then requires `gas_limit == parent_gas_limit`).
981
983
  *
982
984
  * The parent payload's gas_limit is read from fork choice — the variant matching
@@ -991,26 +993,9 @@ function getProposerTargetGasLimit(
991
993
  parentBlockHash: Bytes32
992
994
  ): bigint {
993
995
  const parentBlockRootHex = toRootHex(parentBlockRoot);
994
- const parentBlock = chain.forkChoice.getBlockHexDefaultStatus(parentBlockRootHex);
995
- const dependentRootHex = (() => {
996
- if (parentBlock === null) {
997
- return null;
998
- }
999
- try {
1000
- return getShufflingDependentRoot(
1001
- chain.forkChoice,
1002
- computeEpochAtSlot(prepareSlot),
1003
- computeEpochAtSlot(parentBlock.slot),
1004
- parentBlock
1005
- );
1006
- } catch {
1007
- return null;
1008
- }
1009
- })();
1010
-
1011
- const pref = dependentRootHex !== null ? chain.proposerPreferencesPool.get(prepareSlot, dependentRootHex) : null;
1012
- if (pref !== null) {
1013
- return pref.message.targetGasLimit;
996
+ const preferences = getPooledProposerPreferences(chain, prepareSlot, parentBlockRootHex);
997
+ if (preferences !== null) {
998
+ return preferences.targetGasLimit;
1014
999
  }
1015
1000
 
1016
1001
  const parentPayloadVariant = chain.forkChoice.getBlockHexAndBlockHash(parentBlockRootHex, toRootHex(parentBlockHash));
@@ -1022,6 +1007,55 @@ function getProposerTargetGasLimit(
1022
1007
  return BigInt(parentPayloadVariant.executionPayloadGasLimit);
1023
1008
  }
1024
1009
 
1010
+ /** The proposer's request wins over its signed preferences, which win over its registered proposer data */
1011
+ function getProposerFeeRecipient(
1012
+ requestedFeeRecipient: string | undefined,
1013
+ pooledPreferences: gloas.ProposerPreferences | null,
1014
+ beaconProposerCache: BeaconProposerCache,
1015
+ proposerIndex: ValidatorIndex
1016
+ ): {feeRecipient: string; feeRecipientType: "requested" | "preferences" | "cached" | "default"} {
1017
+ if (requestedFeeRecipient !== undefined) {
1018
+ return {feeRecipient: requestedFeeRecipient, feeRecipientType: "requested"};
1019
+ }
1020
+ if (pooledPreferences !== null) {
1021
+ return {feeRecipient: toHex(pooledPreferences.feeRecipient), feeRecipientType: "preferences"};
1022
+ }
1023
+ const cachedFeeRecipient = beaconProposerCache.get(proposerIndex);
1024
+ if (cachedFeeRecipient !== undefined) {
1025
+ return {feeRecipient: cachedFeeRecipient, feeRecipientType: "cached"};
1026
+ }
1027
+ return {feeRecipient: beaconProposerCache.getOrDefault(proposerIndex), feeRecipientType: "default"};
1028
+ }
1029
+
1030
+ /**
1031
+ * The preferences the proposer of `slot` signed for the branch of `parentBlockRoot`, the same
1032
+ * `(slot, dependent_root)` lookup as gossip bid validation. Null if none are pooled.
1033
+ */
1034
+ export function getPooledProposerPreferences(
1035
+ chain: {forkChoice: IForkChoice; proposerPreferencesPool: ProposerPreferencesPool},
1036
+ slot: Slot,
1037
+ parentBlockRootHex: RootHex
1038
+ ): gloas.ProposerPreferences | null {
1039
+ const parentBlock = chain.forkChoice.getBlockHexDefaultStatus(parentBlockRootHex);
1040
+ if (parentBlock === null) {
1041
+ return null;
1042
+ }
1043
+
1044
+ let dependentRootHex: RootHex;
1045
+ try {
1046
+ dependentRootHex = getShufflingDependentRoot(
1047
+ chain.forkChoice,
1048
+ computeEpochAtSlot(slot),
1049
+ computeEpochAtSlot(parentBlock.slot),
1050
+ parentBlock
1051
+ );
1052
+ } catch {
1053
+ return null;
1054
+ }
1055
+
1056
+ return chain.proposerPreferencesPool.get(slot, dependentRootHex)?.message ?? null;
1057
+ }
1058
+
1025
1059
  export async function produceCommonBlockBody<T extends BlockType>(
1026
1060
  this: BeaconChain,
1027
1061
  blockType: T,
@@ -1072,16 +1106,21 @@ export async function produceCommonBlockBody<T extends BlockType>(
1072
1106
  });
1073
1107
 
1074
1108
  // Live proposal production is supported from Fulu onward.
1075
- return {
1109
+ const commonBlockBody: CommonBlockBody = {
1076
1110
  randaoReveal,
1077
1111
  graffiti,
1078
- eth1Data: currentState.eth1Data,
1079
1112
  proposerSlashings: this.opts.disableProposerSlashings === true ? [] : proposerSlashings,
1080
1113
  attesterSlashings,
1081
1114
  attestations,
1082
- deposits: [],
1083
1115
  voluntaryExits,
1084
1116
  blsToExecutionChanges,
1085
1117
  syncAggregate,
1086
1118
  };
1119
+
1120
+ if (!isForkPostHeze(fork)) {
1121
+ commonBlockBody.eth1Data = currentState.eth1Data;
1122
+ commonBlockBody.deposits = [];
1123
+ }
1124
+
1125
+ return commonBlockBody;
1087
1126
  }
@@ -5,8 +5,9 @@ import {Metrics} from "../metrics/index.js";
5
5
  /**
6
6
  * To prevent our node from having to reprocess while struggling to sync,
7
7
  * we only want to reprocess attestations if block reaches our node before this time.
8
+ * Expressed in basis points of slot duration (1667 BPS = 2s for 12s slots).
8
9
  */
9
- export const REPROCESS_MIN_TIME_TO_NEXT_SLOT_SEC = 2;
10
+ export const REPROCESS_MIN_TIME_TO_NEXT_SLOT_BPS = 1667;
10
11
 
11
12
  /**
12
13
  * Reprocess status for metrics
@@ -4,6 +4,7 @@ import {
4
4
  ForkName,
5
5
  ForkPostFulu,
6
6
  ForkPostGloas,
7
+ ForkPreDeneb,
7
8
  ForkPreGloas,
8
9
  SLOTS_PER_EPOCH,
9
10
  isForkPostDeneb,
@@ -240,7 +241,7 @@ export class SeenBlockInput {
240
241
  } else if (!isForkPostDeneb(forkName)) {
241
242
  // Pre-deneb
242
243
  blockInput = BlockInputPreData.createFromBlock({
243
- block,
244
+ block: block as SignedBeaconBlock<ForkPreDeneb>,
244
245
  blockRootHex,
245
246
  daOutOfRange,
246
247
  forkName,
@@ -592,7 +592,10 @@ export class PersistentCheckpointStateCache implements CheckpointStateCache {
592
592
  }
593
593
 
594
594
  const blockSlot = state.slot;
595
- const processCPStatesTimeMs = this.config.getSlotComponentDurationMs(PROCESS_CHECKPOINT_STATES_BPS);
595
+ const processCPStatesTimeMs = this.config.getSlotComponentDurationMs(
596
+ this.config.getForkName(blockSlot),
597
+ PROCESS_CHECKPOINT_STATES_BPS
598
+ );
596
599
  // we always have clock in production, fallback value is only for test
597
600
  const msFromSlot = this.clock?.msFromSlot(blockSlot) ?? processCPStatesTimeMs;
598
601
  const msToProcessCPStates = processCPStatesTimeMs - msFromSlot;
@@ -1,5 +1,5 @@
1
1
  import {ChainForkConfig} from "@lodestar/config";
2
- import {MIN_ATTESTATION_INCLUSION_DELAY, SLOTS_PER_EPOCH, SLOTS_PER_HISTORICAL_ROOT} from "@lodestar/params";
2
+ import {ForkName, MIN_ATTESTATION_INCLUSION_DELAY, SLOTS_PER_EPOCH, SLOTS_PER_HISTORICAL_ROOT} from "@lodestar/params";
3
3
  import {
4
4
  IBeaconStateView,
5
5
  ParticipationFlags,
@@ -776,13 +776,14 @@ export function createValidatorMonitor(
776
776
 
777
777
  if (isStatePostAltair(headState)) {
778
778
  const prevEpochStartSlot = computeStartSlotAtEpoch(prevEpoch);
779
+ const prevEpochFork = config.getForkName(prevEpochStartSlot);
779
780
  const prevEpochTargetRoot = toRootHex(headState.getBlockRootAtSlot(prevEpochStartSlot));
780
781
 
781
782
  // Check attestation performance
782
783
  for (const [index, validator] of validators.entries()) {
783
784
  const flags = parseParticipationFlags(headState.getPreviousEpochParticipation(index));
784
785
  const attestationSummary = validator.attestations.get(prevEpoch)?.get(prevEpochTargetRoot);
785
- const summary = renderAttestationSummary(config, rootCache, attestationSummary, flags);
786
+ const summary = renderAttestationSummary(config, prevEpochFork, rootCache, attestationSummary, flags);
786
787
  validatorMonitorMetrics?.prevEpochAttestationSummary.inc({summary});
787
788
  log("Previous epoch attestation", {
788
789
  validator: index,
@@ -941,6 +942,7 @@ export function createValidatorMonitor(
941
942
  */
942
943
  function renderAttestationSummary(
943
944
  config: ChainForkConfig,
945
+ fork: ForkName,
944
946
  rootCache: RootHexCache,
945
947
  summary: AttestationSummary | undefined,
946
948
  flags: ParticipationFlags
@@ -1043,7 +1045,7 @@ function renderAttestationSummary(
1043
1045
  }
1044
1046
 
1045
1047
  const submittedLate =
1046
- summary.poolSubmitDelayMinSec > config.getSlotComponentDurationMs(LATE_ATTESTATION_SUBMISSION_BPS) / 1000;
1048
+ summary.poolSubmitDelayMinSec > config.getSlotComponentDurationMs(fork, LATE_ATTESTATION_SUBMISSION_BPS) / 1000;
1047
1049
 
1048
1050
  const aggregateInclusion = summary.aggregateInclusionDelaysSec.length > 0;
1049
1051
 
@@ -1178,7 +1180,8 @@ function renderBlockProposalSummary(
1178
1180
 
1179
1181
  if (
1180
1182
  proposal.poolSubmitDelaySec !== null &&
1181
- proposal.poolSubmitDelaySec > config.getSlotComponentDurationMs(LATE_BLOCK_SUBMISSION_BPS) / 1000
1183
+ proposal.poolSubmitDelaySec >
1184
+ config.getSlotComponentDurationMs(config.getForkName(proposalSlot), LATE_BLOCK_SUBMISSION_BPS) / 1000
1182
1185
  ) {
1183
1186
  out += "_late";
1184
1187
  }
@@ -7,3 +7,4 @@ export const ZERO_HASH = Buffer.alloc(32, 0);
7
7
  export const ZERO_HASH_HEX = "0x" + "00".repeat(32);
8
8
  export const EMPTY_SIGNATURE = Buffer.alloc(96, 0);
9
9
  export const GRAFFITI_SIZE = 32;
10
+ export const HALF_SLOT_BPS = 5000;
@@ -184,7 +184,7 @@ export class BlockArchiveRepository extends Repository<Slot, SignedBeaconBlock>
184
184
  async remove(value: SignedBeaconBlock): Promise<void> {
185
185
  await Promise.all([
186
186
  super.remove(value),
187
- deleteRootIndex(this.db, this.config.getForkTypes(value.message.slot).SignedBeaconBlock, value),
187
+ deleteRootIndex(this.db, this.config.getForkTypes(value.message.slot).BeaconBlock, value),
188
188
  deleteParentRootIndex(this.db, value),
189
189
  ]);
190
190
  }
@@ -193,7 +193,7 @@ export class BlockArchiveRepository extends Repository<Slot, SignedBeaconBlock>
193
193
  await Promise.all([
194
194
  super.batchRemove(values),
195
195
  ...values.map((value) =>
196
- deleteRootIndex(this.db, this.config.getForkTypes(value.message.slot).SignedBeaconBlock, value)
196
+ deleteRootIndex(this.db, this.config.getForkTypes(value.message.slot).BeaconBlock, value)
197
197
  ),
198
198
  ...values.map((value) => deleteParentRootIndex(this.db, value)),
199
199
  ]);
@@ -1,6 +1,6 @@
1
1
  import {Db, encodeKey} from "@lodestar/db";
2
2
  import {ForkAll} from "@lodestar/params";
3
- import {Root, SSZTypesFor, SignedBeaconBlock, Slot, ssz} from "@lodestar/types";
3
+ import {Root, SSZTypesFor, SignedBeaconBlock, Slot} from "@lodestar/types";
4
4
  import {Bucket, getBucketNameByValue} from "../buckets.js";
5
5
 
6
6
  export const rootIndexBucketId = getBucketNameByValue(Bucket.index_blockArchiveRootIndex);
@@ -16,10 +16,9 @@ export async function getParentRootIndex(db: Db, parentRoot: Root): Promise<Uint
16
16
 
17
17
  export async function deleteRootIndex(
18
18
  db: Db,
19
- signedBeaconBlockType: SSZTypesFor<ForkAll, "SignedBeaconBlock">,
19
+ beaconBlockType: SSZTypesFor<ForkAll, "BeaconBlock">,
20
20
  block: SignedBeaconBlock
21
21
  ): Promise<void> {
22
- const beaconBlockType = (signedBeaconBlockType as typeof ssz.phase0.SignedBeaconBlock).fields.message;
23
22
  return db.delete(getRootIndexKey(beaconBlockType.hashTreeRoot(block.message)), {bucketId: rootIndexBucketId});
24
23
  }
25
24
 
@@ -0,0 +1,72 @@
1
+ import {byteArrayEquals} from "@lodestar/utils";
2
+ import {VersionedHashes} from "./interface.js";
3
+
4
+ /**
5
+ * Preserve Deneb's is_valid_versioned_hashes check when the REST envelope omits expected hashes.
6
+ * EIP-4844 encodes blob_versioned_hashes at index 10 of the signed type-3 transaction's RLP list.
7
+ * Skip unrelated fields without decoding or copying them, including untrusted nested access lists.
8
+ * The EL remains responsible for the other transaction validity checks.
9
+ */
10
+ export function isValidBlobVersionedHashes(transactions: Uint8Array[], expected: VersionedHashes): boolean {
11
+ let index = 0;
12
+ for (const transaction of transactions) {
13
+ if (transaction.length === 0) return false;
14
+ if (transaction[0] !== 0x03) continue;
15
+
16
+ const outer = readRlpItem(transaction, 1, transaction.length);
17
+ if (!outer?.isList || outer.end !== transaction.length) return false;
18
+
19
+ let offset = outer.start;
20
+ for (let fieldIndex = 0; fieldIndex < 14; fieldIndex++) {
21
+ const field = readRlpItem(transaction, offset, outer.end);
22
+ if (field === null) return false;
23
+ if (fieldIndex === 10) {
24
+ if (!field.isList) return false;
25
+ // A canonical RLP-encoded Bytes32 is exactly the 0xa0 prefix followed by 32 bytes.
26
+ const count = (field.end - field.start) / 33;
27
+ if (!Number.isInteger(count) || index + count > expected.length) return false;
28
+ for (let hashOffset = field.start; hashOffset < field.end; hashOffset += 33) {
29
+ if (
30
+ transaction[hashOffset] !== 0xa0 ||
31
+ !byteArrayEquals(transaction.subarray(hashOffset + 1, hashOffset + 33), expected[index])
32
+ ) {
33
+ return false;
34
+ }
35
+ index++;
36
+ }
37
+ }
38
+ offset = field.end;
39
+ }
40
+ if (offset !== outer.end) return false;
41
+ }
42
+ return index === expected.length;
43
+ }
44
+
45
+ /** Read one RLP item's boundaries without descending into lists. */
46
+ function readRlpItem(
47
+ bytes: Uint8Array,
48
+ offset: number,
49
+ limit: number
50
+ ): {start: number; end: number; isList: boolean} | null {
51
+ if (offset >= limit) return null;
52
+ const prefix = bytes[offset];
53
+ if (prefix < 0x80) return {start: offset, end: offset + 1, isList: false};
54
+
55
+ const isList = prefix >= 0xc0;
56
+ const lengthCode = prefix - (isList ? 0xc0 : 0x80);
57
+ let start = offset + 1;
58
+ let length = lengthCode;
59
+ if (lengthCode > 55) {
60
+ const lengthBytes = lengthCode - 55;
61
+ start += lengthBytes;
62
+ if (start > limit || bytes[offset + 1] === 0) return null;
63
+ length = 0;
64
+ for (let i = offset + 1; i < start; i++) {
65
+ length = length * 256 + bytes[i];
66
+ }
67
+ if (length <= 55) return null;
68
+ }
69
+ if (length > limit - start) return null;
70
+ if (!isList && length === 1 && bytes[start] < 0x80) return null;
71
+ return {start, end: start + length, isList};
72
+ }