@lodestar/beacon-node 1.44.0-dev.6ef8199cfa → 1.44.0-dev.762dd5f186

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 (238) hide show
  1. package/lib/api/impl/beacon/blocks/index.d.ts.map +1 -1
  2. package/lib/api/impl/beacon/blocks/index.js +66 -33
  3. package/lib/api/impl/beacon/blocks/index.js.map +1 -1
  4. package/lib/api/impl/beacon/pool/index.d.ts.map +1 -1
  5. package/lib/api/impl/beacon/pool/index.js +1 -26
  6. package/lib/api/impl/beacon/pool/index.js.map +1 -1
  7. package/lib/api/impl/config/constants.d.ts +5 -0
  8. package/lib/api/impl/config/constants.d.ts.map +1 -1
  9. package/lib/api/impl/config/constants.js +6 -1
  10. package/lib/api/impl/config/constants.js.map +1 -1
  11. package/lib/api/impl/debug/index.d.ts.map +1 -1
  12. package/lib/api/impl/debug/index.js +69 -12
  13. package/lib/api/impl/debug/index.js.map +1 -1
  14. package/lib/api/impl/lodestar/index.d.ts.map +1 -1
  15. package/lib/api/impl/lodestar/index.js +28 -0
  16. package/lib/api/impl/lodestar/index.js.map +1 -1
  17. package/lib/api/impl/validator/index.d.ts.map +1 -1
  18. package/lib/api/impl/validator/index.js +117 -72
  19. package/lib/api/impl/validator/index.js.map +1 -1
  20. package/lib/chain/archiveStore/archiveStore.d.ts +0 -1
  21. package/lib/chain/archiveStore/archiveStore.d.ts.map +1 -1
  22. package/lib/chain/archiveStore/archiveStore.js +0 -4
  23. package/lib/chain/archiveStore/archiveStore.js.map +1 -1
  24. package/lib/chain/blocks/importBlock.d.ts.map +1 -1
  25. package/lib/chain/blocks/importBlock.js +19 -4
  26. package/lib/chain/blocks/importBlock.js.map +1 -1
  27. package/lib/chain/blocks/importExecutionPayload.d.ts.map +1 -1
  28. package/lib/chain/blocks/importExecutionPayload.js +5 -3
  29. package/lib/chain/blocks/importExecutionPayload.js.map +1 -1
  30. package/lib/chain/blocks/verifyExecutionPayloadEnvelope.d.ts.map +1 -1
  31. package/lib/chain/blocks/verifyExecutionPayloadEnvelope.js +3 -3
  32. package/lib/chain/blocks/verifyExecutionPayloadEnvelope.js.map +1 -1
  33. package/lib/chain/chain.d.ts +3 -3
  34. package/lib/chain/chain.d.ts.map +1 -1
  35. package/lib/chain/chain.js +11 -3
  36. package/lib/chain/chain.js.map +1 -1
  37. package/lib/chain/emitter.d.ts +2 -1
  38. package/lib/chain/emitter.d.ts.map +1 -1
  39. package/lib/chain/emitter.js.map +1 -1
  40. package/lib/chain/errors/blockError.d.ts +0 -7
  41. package/lib/chain/errors/blockError.d.ts.map +1 -1
  42. package/lib/chain/errors/blockError.js +0 -3
  43. package/lib/chain/errors/blockError.js.map +1 -1
  44. package/lib/chain/errors/executionPayloadBid.d.ts +23 -1
  45. package/lib/chain/errors/executionPayloadBid.d.ts.map +1 -1
  46. package/lib/chain/errors/executionPayloadBid.js +4 -0
  47. package/lib/chain/errors/executionPayloadBid.js.map +1 -1
  48. package/lib/chain/errors/payloadAttestation.d.ts +6 -0
  49. package/lib/chain/errors/payloadAttestation.d.ts.map +1 -1
  50. package/lib/chain/errors/payloadAttestation.js +1 -0
  51. package/lib/chain/errors/payloadAttestation.js.map +1 -1
  52. package/lib/chain/forkChoice/index.d.ts +4 -4
  53. package/lib/chain/forkChoice/index.d.ts.map +1 -1
  54. package/lib/chain/forkChoice/index.js +24 -11
  55. package/lib/chain/forkChoice/index.js.map +1 -1
  56. package/lib/chain/interface.d.ts +2 -2
  57. package/lib/chain/interface.d.ts.map +1 -1
  58. package/lib/chain/interface.js.map +1 -1
  59. package/lib/chain/opPools/executionPayloadBidPool.d.ts +4 -4
  60. package/lib/chain/opPools/executionPayloadBidPool.d.ts.map +1 -1
  61. package/lib/chain/opPools/executionPayloadBidPool.js +6 -4
  62. package/lib/chain/opPools/executionPayloadBidPool.js.map +1 -1
  63. package/lib/chain/options.d.ts.map +1 -1
  64. package/lib/chain/options.js +1 -0
  65. package/lib/chain/options.js.map +1 -1
  66. package/lib/chain/prepareNextSlot.d.ts.map +1 -1
  67. package/lib/chain/prepareNextSlot.js +2 -10
  68. package/lib/chain/prepareNextSlot.js.map +1 -1
  69. package/lib/chain/produceBlock/produceBlockBody.d.ts +4 -2
  70. package/lib/chain/produceBlock/produceBlockBody.d.ts.map +1 -1
  71. package/lib/chain/produceBlock/produceBlockBody.js +81 -27
  72. package/lib/chain/produceBlock/produceBlockBody.js.map +1 -1
  73. package/lib/chain/regen/interface.d.ts +2 -1
  74. package/lib/chain/regen/interface.d.ts.map +1 -1
  75. package/lib/chain/regen/interface.js +2 -0
  76. package/lib/chain/regen/interface.js.map +1 -1
  77. package/lib/chain/regen/queued.d.ts +0 -1
  78. package/lib/chain/regen/queued.d.ts.map +1 -1
  79. package/lib/chain/regen/queued.js +0 -4
  80. package/lib/chain/regen/queued.js.map +1 -1
  81. package/lib/chain/seenCache/seenPayloadEnvelopeInput.d.ts.map +1 -1
  82. package/lib/chain/seenCache/seenPayloadEnvelopeInput.js +8 -2
  83. package/lib/chain/seenCache/seenPayloadEnvelopeInput.js.map +1 -1
  84. package/lib/chain/stateCache/fifoBlockStateCache.d.ts +0 -5
  85. package/lib/chain/stateCache/fifoBlockStateCache.d.ts.map +1 -1
  86. package/lib/chain/stateCache/fifoBlockStateCache.js +0 -5
  87. package/lib/chain/stateCache/fifoBlockStateCache.js.map +1 -1
  88. package/lib/chain/stateCache/persistentCheckpointsCache.d.ts +1 -4
  89. package/lib/chain/stateCache/persistentCheckpointsCache.d.ts.map +1 -1
  90. package/lib/chain/stateCache/persistentCheckpointsCache.js +5 -2
  91. package/lib/chain/stateCache/persistentCheckpointsCache.js.map +1 -1
  92. package/lib/chain/stateCache/types.d.ts +0 -2
  93. package/lib/chain/stateCache/types.d.ts.map +1 -1
  94. package/lib/chain/stateCache/types.js.map +1 -1
  95. package/lib/chain/validation/block.d.ts +5 -1
  96. package/lib/chain/validation/block.d.ts.map +1 -1
  97. package/lib/chain/validation/block.js +4 -14
  98. package/lib/chain/validation/block.js.map +1 -1
  99. package/lib/chain/validation/executionPayloadBid.d.ts +7 -3
  100. package/lib/chain/validation/executionPayloadBid.d.ts.map +1 -1
  101. package/lib/chain/validation/executionPayloadBid.js +81 -16
  102. package/lib/chain/validation/executionPayloadBid.js.map +1 -1
  103. package/lib/chain/validation/executionPayloadEnvelope.js +1 -3
  104. package/lib/chain/validation/executionPayloadEnvelope.js.map +1 -1
  105. package/lib/chain/validation/payloadAttestationMessage.d.ts.map +1 -1
  106. package/lib/chain/validation/payloadAttestationMessage.js +24 -4
  107. package/lib/chain/validation/payloadAttestationMessage.js.map +1 -1
  108. package/lib/chain/validatorMonitor.d.ts +1 -0
  109. package/lib/chain/validatorMonitor.d.ts.map +1 -1
  110. package/lib/chain/validatorMonitor.js +16 -0
  111. package/lib/chain/validatorMonitor.js.map +1 -1
  112. package/lib/execution/builder/index.d.ts +1 -2
  113. package/lib/execution/builder/index.d.ts.map +1 -1
  114. package/lib/execution/builder/index.js +0 -1
  115. package/lib/execution/builder/index.js.map +1 -1
  116. package/lib/execution/engine/http.js +1 -1
  117. package/lib/execution/engine/http.js.map +1 -1
  118. package/lib/execution/engine/interface.d.ts +2 -2
  119. package/lib/execution/engine/interface.d.ts.map +1 -1
  120. package/lib/execution/engine/interface.js +6 -2
  121. package/lib/execution/engine/interface.js.map +1 -1
  122. package/lib/execution/engine/mock.d.ts.map +1 -1
  123. package/lib/execution/engine/mock.js +3 -5
  124. package/lib/execution/engine/mock.js.map +1 -1
  125. package/lib/execution/engine/types.d.ts +15 -7
  126. package/lib/execution/engine/types.d.ts.map +1 -1
  127. package/lib/execution/engine/types.js +47 -9
  128. package/lib/execution/engine/types.js.map +1 -1
  129. package/lib/metrics/metrics/lodestar.d.ts +5 -1
  130. package/lib/metrics/metrics/lodestar.d.ts.map +1 -1
  131. package/lib/metrics/metrics/lodestar.js +14 -3
  132. package/lib/metrics/metrics/lodestar.js.map +1 -1
  133. package/lib/network/gossip/topic.d.ts +770 -3
  134. package/lib/network/gossip/topic.d.ts.map +1 -1
  135. package/lib/network/interface.d.ts +1 -0
  136. package/lib/network/interface.d.ts.map +1 -1
  137. package/lib/network/network.d.ts +1 -0
  138. package/lib/network/network.d.ts.map +1 -1
  139. package/lib/network/network.js +5 -0
  140. package/lib/network/network.js.map +1 -1
  141. package/lib/network/processor/gossipHandlers.d.ts.map +1 -1
  142. package/lib/network/processor/gossipHandlers.js +21 -6
  143. package/lib/network/processor/gossipHandlers.js.map +1 -1
  144. package/lib/network/processor/index.d.ts +2 -2
  145. package/lib/network/processor/index.d.ts.map +1 -1
  146. package/lib/network/processor/index.js +25 -23
  147. package/lib/network/processor/index.js.map +1 -1
  148. package/lib/network/reqresp/handlers/beaconBlocksByRange.d.ts.map +1 -1
  149. package/lib/network/reqresp/handlers/beaconBlocksByRange.js +9 -5
  150. package/lib/network/reqresp/handlers/beaconBlocksByRange.js.map +1 -1
  151. package/lib/network/reqresp/handlers/dataColumnSidecarsByRange.d.ts.map +1 -1
  152. package/lib/network/reqresp/handlers/dataColumnSidecarsByRange.js +13 -3
  153. package/lib/network/reqresp/handlers/dataColumnSidecarsByRange.js.map +1 -1
  154. package/lib/network/reqresp/handlers/dataColumnSidecarsByRoot.js +1 -1
  155. package/lib/network/reqresp/handlers/dataColumnSidecarsByRoot.js.map +1 -1
  156. package/lib/network/reqresp/handlers/executionPayloadEnvelopesByRange.d.ts +2 -1
  157. package/lib/network/reqresp/handlers/executionPayloadEnvelopesByRange.d.ts.map +1 -1
  158. package/lib/network/reqresp/handlers/executionPayloadEnvelopesByRange.js +16 -6
  159. package/lib/network/reqresp/handlers/executionPayloadEnvelopesByRange.js.map +1 -1
  160. package/lib/network/reqresp/handlers/executionPayloadEnvelopesByRoot.d.ts +2 -1
  161. package/lib/network/reqresp/handlers/executionPayloadEnvelopesByRoot.d.ts.map +1 -1
  162. package/lib/network/reqresp/handlers/executionPayloadEnvelopesByRoot.js +15 -1
  163. package/lib/network/reqresp/handlers/executionPayloadEnvelopesByRoot.js.map +1 -1
  164. package/lib/network/reqresp/handlers/index.js +4 -4
  165. package/lib/network/reqresp/handlers/index.js.map +1 -1
  166. package/lib/network/reqresp/utils/dataColumnResponseValidation.d.ts.map +1 -1
  167. package/lib/network/reqresp/utils/dataColumnResponseValidation.js +23 -5
  168. package/lib/network/reqresp/utils/dataColumnResponseValidation.js.map +1 -1
  169. package/lib/sync/types.d.ts +9 -1
  170. package/lib/sync/types.d.ts.map +1 -1
  171. package/lib/sync/types.js +9 -2
  172. package/lib/sync/types.js.map +1 -1
  173. package/lib/sync/unknownBlock.d.ts.map +1 -1
  174. package/lib/sync/unknownBlock.js +64 -30
  175. package/lib/sync/unknownBlock.js.map +1 -1
  176. package/lib/util/dataColumns.d.ts.map +1 -1
  177. package/lib/util/dataColumns.js +16 -11
  178. package/lib/util/dataColumns.js.map +1 -1
  179. package/lib/util/sszBytes.js +1 -1
  180. package/package.json +15 -17
  181. package/src/api/impl/beacon/blocks/index.ts +76 -42
  182. package/src/api/impl/beacon/pool/index.ts +4 -42
  183. package/src/api/impl/config/constants.ts +10 -0
  184. package/src/api/impl/debug/index.ts +73 -12
  185. package/src/api/impl/lodestar/index.ts +30 -0
  186. package/src/api/impl/validator/index.ts +137 -86
  187. package/src/chain/archiveStore/archiveStore.ts +0 -5
  188. package/src/chain/blocks/importBlock.ts +29 -7
  189. package/src/chain/blocks/importExecutionPayload.ts +8 -2
  190. package/src/chain/blocks/verifyExecutionPayloadEnvelope.ts +3 -5
  191. package/src/chain/chain.ts +14 -4
  192. package/src/chain/emitter.ts +3 -2
  193. package/src/chain/errors/blockError.ts +0 -4
  194. package/src/chain/errors/executionPayloadBid.ts +20 -1
  195. package/src/chain/errors/payloadAttestation.ts +2 -0
  196. package/src/chain/forkChoice/index.ts +27 -4
  197. package/src/chain/interface.ts +1 -2
  198. package/src/chain/opPools/executionPayloadBidPool.ts +10 -9
  199. package/src/chain/options.ts +1 -0
  200. package/src/chain/prepareNextSlot.ts +2 -11
  201. package/src/chain/produceBlock/produceBlockBody.ts +114 -40
  202. package/src/chain/regen/interface.ts +2 -1
  203. package/src/chain/regen/queued.ts +0 -5
  204. package/src/chain/seenCache/seenPayloadEnvelopeInput.ts +8 -3
  205. package/src/chain/stateCache/fifoBlockStateCache.ts +0 -6
  206. package/src/chain/stateCache/persistentCheckpointsCache.ts +6 -2
  207. package/src/chain/stateCache/types.ts +0 -2
  208. package/src/chain/validation/block.ts +12 -16
  209. package/src/chain/validation/executionPayloadBid.ts +90 -19
  210. package/src/chain/validation/executionPayloadEnvelope.ts +1 -3
  211. package/src/chain/validation/payloadAttestationMessage.ts +26 -4
  212. package/src/chain/validatorMonitor.ts +18 -0
  213. package/src/execution/builder/index.ts +1 -4
  214. package/src/execution/engine/http.ts +1 -1
  215. package/src/execution/engine/interface.ts +12 -2
  216. package/src/execution/engine/mock.ts +6 -5
  217. package/src/execution/engine/types.ts +75 -14
  218. package/src/metrics/metrics/lodestar.ts +15 -3
  219. package/src/network/interface.ts +1 -0
  220. package/src/network/network.ts +11 -0
  221. package/src/network/processor/gossipHandlers.ts +26 -6
  222. package/src/network/processor/index.ts +27 -27
  223. package/src/network/reqresp/handlers/beaconBlocksByRange.ts +12 -5
  224. package/src/network/reqresp/handlers/dataColumnSidecarsByRange.ts +17 -3
  225. package/src/network/reqresp/handlers/dataColumnSidecarsByRoot.ts +1 -1
  226. package/src/network/reqresp/handlers/executionPayloadEnvelopesByRange.ts +22 -6
  227. package/src/network/reqresp/handlers/executionPayloadEnvelopesByRoot.ts +20 -1
  228. package/src/network/reqresp/handlers/index.ts +4 -4
  229. package/src/network/reqresp/utils/dataColumnResponseValidation.ts +22 -5
  230. package/src/sync/types.ts +11 -2
  231. package/src/sync/unknownBlock.ts +70 -31
  232. package/src/util/dataColumns.ts +17 -12
  233. package/src/util/sszBytes.ts +1 -1
  234. package/lib/execution/builder/utils.d.ts +0 -5
  235. package/lib/execution/builder/utils.d.ts.map +0 -1
  236. package/lib/execution/builder/utils.js +0 -17
  237. package/lib/execution/builder/utils.js.map +0 -1
  238. package/src/execution/builder/utils.ts +0 -19
@@ -15,12 +15,17 @@ import {BlockErrorCode, BlockGossipError, GossipAction} from "../errors/index.js
15
15
  import {IBeaconChain} from "../interface.js";
16
16
  import {RegenCaller} from "../regen/index.js";
17
17
 
18
+ export type GossipBlockValidationResult = {
19
+ /** Number of skipped slots between the block and its parent (blockSlot - parentSlot - 1) */
20
+ skippedSlots: number;
21
+ };
22
+
18
23
  export async function validateGossipBlock(
19
24
  config: ChainForkConfig,
20
25
  chain: IBeaconChain,
21
26
  signedBlock: SignedBeaconBlock,
22
27
  fork: ForkName
23
- ): Promise<void> {
28
+ ): Promise<GossipBlockValidationResult> {
24
29
  const block = signedBlock.message;
25
30
  const blockSlot = block.slot;
26
31
  const blockEpoch = computeEpochAtSlot(blockSlot);
@@ -109,21 +114,6 @@ export async function validateGossipBlock(
109
114
  }
110
115
  }
111
116
 
112
- // [IGNORE] The attestation head block is too far behind the attestation slot, causing many skip slots.
113
- // This is deemed a DoS risk because we need to get the proposerShuffling. To get the shuffling we have
114
- // to do a bunch of epoch transitions, the longer the distance between the parent and block,
115
- // the more we have to do. epochTransitions are expensive ~750ms, so we must limit how many a
116
- // single bad block can trigger
117
- // Note: Ensure this check is done before calling chain.regen.getBlockSlotStat as this is the function that does various epoch transitions.
118
- // Note: This validation check is not part of the spec.
119
- if (chain.opts.maxSkipSlots != null && parentBlock.slot + chain.opts.maxSkipSlots < blockSlot) {
120
- throw new BlockGossipError(GossipAction.IGNORE, {
121
- code: BlockErrorCode.TOO_MANY_SKIPPED_SLOTS,
122
- parentSlot: parentBlock.slot,
123
- blockSlot,
124
- });
125
- }
126
-
127
117
  // [REJECT] The block is from a higher slot than its parent.
128
118
  if (parentBlock.slot >= blockSlot) {
129
119
  throw new BlockGossipError(GossipAction.REJECT, {
@@ -133,6 +123,10 @@ export async function validateGossipBlock(
133
123
  });
134
124
  }
135
125
 
126
+ // Number of skipped slots between block and parent (non-spec). Previously this gated blocks via
127
+ // maxSkipSlots; now the caller only observes it so legitimate post-skip blocks are no longer ignored.
128
+ const skippedSlots = blockSlot - parentBlock.slot - 1;
129
+
136
130
  // [REJECT] The length of KZG commitments is less than or equal to the limitation defined in Consensus Layer -- i.e. validate that len(body.signed_beacon_block.message.blob_kzg_commitments) <= MAX_BLOBS_PER_BLOCK
137
131
  if (isForkPostDeneb(fork) && !isForkPostGloas(fork)) {
138
132
  const blobKzgCommitmentsLen = (block as deneb.BeaconBlock).body.blobKzgCommitments.length;
@@ -247,4 +241,6 @@ export async function validateGossipBlock(
247
241
  }
248
242
 
249
243
  chain.seenBlockProposers.add(blockSlot, proposerIndex);
244
+
245
+ return {skippedSlots};
250
246
  }
@@ -1,12 +1,14 @@
1
1
  import {PublicKey} from "@chainsafe/blst";
2
+ import {PAYLOAD_BUILDER_VERSION} from "@lodestar/params";
2
3
  import {
3
4
  computeEpochAtSlot,
4
5
  createSingleSignatureSetFromComponents,
5
6
  getExecutionPayloadBidSigningRoot,
6
7
  isActiveBuilder,
8
+ isGasLimitTargetCompatible,
7
9
  isStatePostGloas,
8
10
  } from "@lodestar/state-transition";
9
- import {gloas} from "@lodestar/types";
11
+ import {ValidatorIndex, gloas} from "@lodestar/types";
10
12
  import {byteArrayEquals, toHex, toRootHex} from "@lodestar/utils";
11
13
  import {getShufflingDependentRoot} from "../../util/dependentRoot.js";
12
14
  import {ExecutionPayloadBidError, ExecutionPayloadBidErrorCode, GossipAction} from "../errors/index.js";
@@ -16,28 +18,24 @@ import {RegenCaller} from "../regen/index.js";
16
18
  export async function validateApiExecutionPayloadBid(
17
19
  chain: IBeaconChain,
18
20
  signedExecutionPayloadBid: gloas.SignedExecutionPayloadBid
19
- ): Promise<void> {
21
+ ): Promise<{proposerIndex: ValidatorIndex}> {
20
22
  return validateExecutionPayloadBid(chain, signedExecutionPayloadBid);
21
23
  }
22
24
 
23
25
  export async function validateGossipExecutionPayloadBid(
24
26
  chain: IBeaconChain,
25
27
  signedExecutionPayloadBid: gloas.SignedExecutionPayloadBid
26
- ): Promise<void> {
28
+ ): Promise<{proposerIndex: ValidatorIndex}> {
27
29
  return validateExecutionPayloadBid(chain, signedExecutionPayloadBid);
28
30
  }
29
31
 
30
32
  async function validateExecutionPayloadBid(
31
33
  chain: IBeaconChain,
32
34
  signedExecutionPayloadBid: gloas.SignedExecutionPayloadBid
33
- ): Promise<void> {
35
+ ): Promise<{proposerIndex: ValidatorIndex}> {
34
36
  const bid = signedExecutionPayloadBid.message;
35
37
  const parentBlockRootHex = toRootHex(bid.parentBlockRoot);
36
38
  const parentBlockHashHex = toRootHex(bid.parentBlockHash);
37
- const state = await chain.getHeadStateAtCurrentEpoch(RegenCaller.validateGossipExecutionPayloadBid);
38
- if (!isStatePostGloas(state)) {
39
- throw new Error(`Expected gloas+ state for execution payload bid validation, got fork=${state.forkName}`);
40
- }
41
39
 
42
40
  // [IGNORE] `bid.slot` is the current slot or the next slot.
43
41
  const currentSlot = chain.clock.currentSlot;
@@ -60,6 +58,17 @@ async function validateExecutionPayloadBid(
60
58
  });
61
59
  }
62
60
 
61
+ // [REJECT] The bid is for a higher slot than its parent block -- i.e.
62
+ // validate that `bid.slot` is greater than the slot of the block with root
63
+ // `bid.parent_block_root`.
64
+ if (bid.slot <= parentBlock.slot) {
65
+ throw new ExecutionPayloadBidError(GossipAction.REJECT, {
66
+ code: ExecutionPayloadBidErrorCode.NOT_LATER_THAN_PARENT,
67
+ parentSlot: parentBlock.slot,
68
+ slot: bid.slot,
69
+ });
70
+ }
71
+
63
72
  // [IGNORE] A `SignedProposerPreferences` matching `bid.slot` and the bid's branch has been
64
73
  // seen — i.e. `proposal_slot == bid.slot` AND `dependent_root ==
65
74
  // get_proposer_dependent_root(parent_state, compute_epoch_at_slot(bid.slot))`.
@@ -99,9 +108,31 @@ async function validateExecutionPayloadBid(
99
108
  });
100
109
  }
101
110
 
111
+ // Use the bid's parent branch state for builder checks
112
+ const state = await chain.regen
113
+ .getBlockSlotState(parentBlock, bid.slot, {dontTransferCache: true}, RegenCaller.validateGossipExecutionPayloadBid)
114
+ .catch(() => {
115
+ throw new ExecutionPayloadBidError(GossipAction.IGNORE, {
116
+ code: ExecutionPayloadBidErrorCode.UNKNOWN_BLOCK_ROOT,
117
+ parentBlockRoot: parentBlockRootHex,
118
+ });
119
+ });
120
+
121
+ if (!isStatePostGloas(state)) {
122
+ throw new Error(`Expected gloas+ state for execution payload bid validation, got fork=${state.forkName}`);
123
+ }
124
+
102
125
  // [REJECT] `bid.builder_index` is a valid/active builder index -- i.e.
103
126
  // `is_active_builder(state, bid.builder_index)` returns `True`.
104
- const builder = state.getBuilder(bid.builderIndex);
127
+ let builder: gloas.Builder;
128
+ try {
129
+ builder = state.getBuilder(bid.builderIndex);
130
+ } catch {
131
+ throw new ExecutionPayloadBidError(GossipAction.REJECT, {
132
+ code: ExecutionPayloadBidErrorCode.BUILDER_NOT_ELIGIBLE,
133
+ builderIndex: bid.builderIndex,
134
+ });
135
+ }
105
136
  if (!isActiveBuilder(builder, state.finalizedCheckpoint.epoch)) {
106
137
  throw new ExecutionPayloadBidError(GossipAction.REJECT, {
107
138
  code: ExecutionPayloadBidErrorCode.BUILDER_NOT_ELIGIBLE,
@@ -109,6 +140,17 @@ async function validateExecutionPayloadBid(
109
140
  });
110
141
  }
111
142
 
143
+ // [REJECT] The builder version is `PAYLOAD_BUILDER_VERSION` -- i.e.
144
+ // `state.builders[bid.builder_index].version == PAYLOAD_BUILDER_VERSION`.
145
+ if (builder.version !== PAYLOAD_BUILDER_VERSION) {
146
+ throw new ExecutionPayloadBidError(GossipAction.REJECT, {
147
+ code: ExecutionPayloadBidErrorCode.INVALID_BUILDER_VERSION,
148
+ builderIndex: bid.builderIndex,
149
+ version: builder.version,
150
+ expectedVersion: PAYLOAD_BUILDER_VERSION,
151
+ });
152
+ }
153
+
112
154
  // [REJECT] `bid.execution_payment` is zero.
113
155
  if (bid.executionPayment !== 0) {
114
156
  throw new ExecutionPayloadBidError(GossipAction.REJECT, {
@@ -128,14 +170,33 @@ async function validateExecutionPayloadBid(
128
170
  });
129
171
  }
130
172
 
131
- // [REJECT] `bid.gas_limit == proposer_preferences.target_gas_limit`.
132
- const bidGasLimit = Number(bid.gasLimit);
133
- if (bidGasLimit !== proposerPreferences.message.targetGasLimit) {
134
- throw new ExecutionPayloadBidError(GossipAction.REJECT, {
173
+ // [IGNORE] `bid.parent_block_hash` is the block hash of a known execution payload in fork
174
+ // choice. Looks up the variant of `bid.parent_block_root` whose payload hash matches
175
+ // `bid.parent_block_hash` — works for both FULL parents (FULL variant carries the delivered
176
+ // payload's hash) and EMPTY parents (EMPTY/PENDING variants carry the inherited parent
177
+ // payload's hash, since the new block doesn't have its own payload). Variant carries the
178
+ // executed payload's gas_limit, which we use as `parent_gas_limit` below.
179
+ const parentPayloadVariant = chain.forkChoice.getBlockHexAndBlockHash(parentBlockRootHex, parentBlockHashHex);
180
+ if (parentPayloadVariant === null || parentPayloadVariant.executionPayloadBlockHash === null) {
181
+ throw new ExecutionPayloadBidError(GossipAction.IGNORE, {
182
+ code: ExecutionPayloadBidErrorCode.UNKNOWN_PARENT_BLOCK_HASH,
183
+ parentBlockHash: parentBlockHashHex,
184
+ });
185
+ }
186
+
187
+ // [IGNORE] `is_gas_limit_target_compatible(parent_gas_limit, bid.gas_limit, target_gas_limit)`,
188
+ // where `parent_gas_limit` is the `gas_limit` of the parent execution payload and
189
+ // `target_gas_limit` is `proposer_preferences.target_gas_limit`.
190
+ const bidGasLimit = bid.gasLimit;
191
+ const parentGasLimit = parentPayloadVariant.executionPayloadGasLimit;
192
+ const targetGasLimit = proposerPreferences.message.targetGasLimit;
193
+ if (!isGasLimitTargetCompatible(parentGasLimit, bidGasLimit, targetGasLimit)) {
194
+ throw new ExecutionPayloadBidError(GossipAction.IGNORE, {
135
195
  code: ExecutionPayloadBidErrorCode.PROPOSER_PREFERENCES_GAS_LIMIT_MISMATCH,
136
196
  builderIndex: bid.builderIndex,
137
197
  bidGasLimit,
138
- expectedGasLimit: proposerPreferences.message.targetGasLimit,
198
+ parentGasLimit,
199
+ targetGasLimit,
139
200
  });
140
201
  }
141
202
 
@@ -166,11 +227,11 @@ async function validateExecutionPayloadBid(
166
227
  // [IGNORE] this bid is the highest value bid seen for the tuple
167
228
  // `(bid.slot, bid.parent_block_hash, bid.parent_block_root)`.
168
229
  const bestBid = chain.executionPayloadBidPool.getBestBid(bid.slot, parentBlockHashHex, parentBlockRootHex);
169
- if (bestBid !== null && bestBid.value >= bid.value) {
230
+ if (bestBid !== null && bestBid.message.value >= bid.value) {
170
231
  throw new ExecutionPayloadBidError(GossipAction.IGNORE, {
171
232
  code: ExecutionPayloadBidErrorCode.BID_TOO_LOW,
172
233
  bidValue: bid.value,
173
- currentHighestBid: bestBid.value,
234
+ currentHighestBid: bestBid.message.value,
174
235
  });
175
236
  }
176
237
  // [IGNORE] `bid.value` is less or equal than the builder's excess balance --
@@ -183,9 +244,17 @@ async function validateExecutionPayloadBid(
183
244
  });
184
245
  }
185
246
 
186
- // [IGNORE] `bid.parent_block_hash` is the block hash of a known execution
187
- // payload in fork choice.
188
- // TODO GLOAS: implement this
247
+ // [REJECT] `bid.prev_randao` is the correct RANDAO mix -- i.e. validate that
248
+ // `bid.prev_randao == get_randao_mix(parent_state, get_current_epoch(parent_state))`.
249
+ const randaoMix = state.getRandaoMix(computeEpochAtSlot(state.slot));
250
+ if (!byteArrayEquals(bid.prevRandao, randaoMix)) {
251
+ throw new ExecutionPayloadBidError(GossipAction.REJECT, {
252
+ code: ExecutionPayloadBidErrorCode.INVALID_PREV_RANDAO,
253
+ builderIndex: bid.builderIndex,
254
+ bidPrevRandao: toHex(bid.prevRandao),
255
+ expectedPrevRandao: toHex(randaoMix),
256
+ });
257
+ }
189
258
 
190
259
  // [REJECT] `signed_execution_payload_bid.signature` is valid with respect to the `bid.builder_index`.
191
260
  const signatureSet = createSingleSignatureSetFromComponents(
@@ -204,4 +273,6 @@ async function validateExecutionPayloadBid(
204
273
 
205
274
  // Valid
206
275
  chain.seenExecutionPayloadBids.add(bid.slot, bid.builderIndex);
276
+
277
+ return {proposerIndex: proposerPreferences.message.validatorIndex};
207
278
  }
@@ -35,8 +35,6 @@ async function validateExecutionPayloadEnvelope(
35
35
  // [IGNORE] The envelope's block root `envelope.beacon_block_root` has been seen (via
36
36
  // gossip or non-gossip sources) (a client MAY queue payload for processing once
37
37
  // the block is retrieved).
38
- // TODO GLOAS: Need to review this, we should queue the envelope for later
39
- // processing if the block is not yet known, otherwise we would ignore it here
40
38
  const block = chain.forkChoice.getBlockDefaultStatus(envelope.beaconBlockRoot);
41
39
  if (block === null) {
42
40
  throw new ExecutionPayloadEnvelopeError(GossipAction.IGNORE, {
@@ -108,7 +106,7 @@ async function validateExecutionPayloadEnvelope(
108
106
  }
109
107
 
110
108
  // [REJECT] `hash_tree_root(envelope.execution_requests) == bid.execution_requests_root`
111
- const requestsRoot = ssz.electra.ExecutionRequests.hashTreeRoot(envelope.executionRequests);
109
+ const requestsRoot = ssz.gloas.ExecutionRequests.hashTreeRoot(envelope.executionRequests);
112
110
  if (!byteArrayEquals(requestsRoot, payloadInput.getBid().executionRequestsRoot)) {
113
111
  throw new ExecutionPayloadEnvelopeError(GossipAction.REJECT, {
114
112
  code: ExecutionPayloadEnvelopeErrorCode.EXECUTION_REQUESTS_ROOT_MISMATCH,
@@ -8,6 +8,7 @@ import {RootHex, gloas, ssz} from "@lodestar/types";
8
8
  import {toRootHex} from "@lodestar/utils";
9
9
  import {GossipAction, PayloadAttestationError, PayloadAttestationErrorCode} from "../errors/index.js";
10
10
  import {IBeaconChain} from "../index.js";
11
+ import {RegenCaller} from "../regen/index.js";
11
12
 
12
13
  export type PayloadAttestationValidationResult = {
13
14
  attDataRootHex: RootHex;
@@ -61,22 +62,43 @@ async function validatePayloadAttestationMessage(
61
62
  // [IGNORE] The message's block `data.beacon_block_root` has been seen (via
62
63
  // gossip or non-gossip sources) (a client MAY queue attestation for processing
63
64
  // once the block is retrieved. Note a client might want to request payload after).
64
- if (!chain.forkChoice.hasBlock(data.beaconBlockRoot)) {
65
+ const block = chain.forkChoice.getBlockDefaultStatus(data.beaconBlockRoot);
66
+ if (!block) {
65
67
  throw new PayloadAttestationError(GossipAction.IGNORE, {
66
68
  code: PayloadAttestationErrorCode.UNKNOWN_BLOCK_ROOT,
67
69
  blockRoot: toRootHex(data.beaconBlockRoot),
68
70
  });
69
71
  }
70
72
 
71
- const state = chain.getHeadState();
72
- if (!isStatePostGloas(state)) {
73
- throw new Error(`Expected gloas+ state for payload attestation validation, got fork=${state.forkName}`);
73
+ // [IGNORE] The block referenced by `data.beacon_block_root` is at slot `data.slot`,
74
+ // i.e. the block has `block.slot == data.slot`.
75
+ if (block.slot !== data.slot) {
76
+ throw new PayloadAttestationError(GossipAction.IGNORE, {
77
+ code: PayloadAttestationErrorCode.INVALID_BLOCK_SLOT,
78
+ blockRoot: toRootHex(data.beaconBlockRoot),
79
+ blockSlot: block.slot,
80
+ slot: data.slot,
81
+ });
74
82
  }
75
83
 
76
84
  // [REJECT] The message's block `data.beacon_block_root` passes validation.
77
85
  // TODO GLOAS: implement this. Technically if we cannot get proto block from fork choice,
78
86
  // it is possible that the block didn't pass the validation
79
87
 
88
+ // Use the referenced block's branch state for the PTC committee check
89
+ const state = await chain.regen
90
+ .getBlockSlotState(block, data.slot, {dontTransferCache: true}, RegenCaller.validateGossipPayloadAttestationMessage)
91
+ .catch(() => {
92
+ throw new PayloadAttestationError(GossipAction.IGNORE, {
93
+ code: PayloadAttestationErrorCode.UNKNOWN_BLOCK_ROOT,
94
+ blockRoot: toRootHex(data.beaconBlockRoot),
95
+ });
96
+ });
97
+
98
+ if (!isStatePostGloas(state)) {
99
+ throw new Error(`Expected gloas+ state for payload attestation validation, got fork=${state.forkName}`);
100
+ }
101
+
80
102
  // [REJECT] The message's validator index is within the payload committee in
81
103
  // `get_ptc(state, data.slot)`. The `state` is the head state corresponding to
82
104
  // processing the block up to the current slot as determined by the fork choice.
@@ -66,6 +66,7 @@ export type ValidatorMonitor = {
66
66
  delaySec: Seconds,
67
67
  envelope: gloas.SignedExecutionPayloadEnvelope
68
68
  ): void;
69
+ registerExecutionPayloadBid(src: OpSource, proposerIndex: ValidatorIndex, bid: gloas.ExecutionPayloadBid): void;
69
70
  registerImportedBlock(block: BeaconBlock, data: {proposerBalanceDelta: number}): void;
70
71
  onPoolSubmitUnaggregatedAttestation(
71
72
  seenTimestampSec: number,
@@ -459,6 +460,23 @@ export function createValidatorMonitor(
459
460
  // TODO GLOAS: implement execution payload envelope monitoring
460
461
  },
461
462
 
463
+ registerExecutionPayloadBid(src, proposerIndex, bid) {
464
+ if (!validators.has(proposerIndex)) {
465
+ return;
466
+ }
467
+ log("Received an execution payload bid for monitored proposer", {
468
+ slot: bid.slot,
469
+ proposerIndex,
470
+ src,
471
+ builderIndex: bid.builderIndex,
472
+ gasLimit: bid.gasLimit,
473
+ value: bid.value.toString(),
474
+ parentBlockRoot: toRootHex(bid.parentBlockRoot),
475
+ parentBlockHash: toRootHex(bid.parentBlockHash),
476
+ blockHash: toRootHex(bid.blockHash),
477
+ });
478
+ },
479
+
462
480
  registerImportedBlock(block, {proposerBalanceDelta}) {
463
481
  const validator = validators.get(block.proposerIndex);
464
482
  if (validator) {
@@ -1,11 +1,8 @@
1
1
  import {ChainForkConfig} from "@lodestar/config";
2
2
  import {Logger} from "@lodestar/logger";
3
3
  import {Metrics} from "../../metrics/metrics.js";
4
- import {IExecutionBuilder} from "./interface.js";
5
-
6
- export {getExpectedGasLimit} from "./utils.js";
7
-
8
4
  import {ExecutionBuilderHttp, ExecutionBuilderHttpOpts, defaultExecutionBuilderHttpOpts} from "./http.js";
5
+ import {IExecutionBuilder} from "./interface.js";
9
6
 
10
7
  export {ExecutionBuilderHttp, defaultExecutionBuilderHttpOpts};
11
8
 
@@ -244,7 +244,7 @@ export class ExecutionEngineHttp implements IExecutionEngine {
244
244
  if (executionRequests === undefined) {
245
245
  throw Error(`executionRequests required in notifyNewPayload for fork=${fork}`);
246
246
  }
247
- const serializedExecutionRequests = serializeExecutionRequests(executionRequests);
247
+ const serializedExecutionRequests = serializeExecutionRequests(fork, executionRequests);
248
248
  engineRequest = {
249
249
  method: ForkSeq[fork] >= ForkSeq.gloas ? "engine_newPayloadV5" : "engine_newPayloadV4",
250
250
  params: [
@@ -1,5 +1,7 @@
1
1
  import {routes} from "@lodestar/api";
2
2
  import {
3
+ BUILDER_DEPOSIT_REQUEST_TYPE,
4
+ BUILDER_EXIT_REQUEST_TYPE,
3
5
  CONSOLIDATION_REQUEST_TYPE,
4
6
  DEPOSIT_REQUEST_TYPE,
5
7
  ForkName,
@@ -51,10 +53,18 @@ export enum ExecutionEngineState {
51
53
  export type ExecutionRequestType =
52
54
  | typeof DEPOSIT_REQUEST_TYPE
53
55
  | typeof WITHDRAWAL_REQUEST_TYPE
54
- | typeof CONSOLIDATION_REQUEST_TYPE;
56
+ | typeof CONSOLIDATION_REQUEST_TYPE
57
+ | typeof BUILDER_DEPOSIT_REQUEST_TYPE
58
+ | typeof BUILDER_EXIT_REQUEST_TYPE;
55
59
 
56
60
  export function isExecutionRequestType(type: number): type is ExecutionRequestType {
57
- return type === DEPOSIT_REQUEST_TYPE || type === WITHDRAWAL_REQUEST_TYPE || type === CONSOLIDATION_REQUEST_TYPE;
61
+ return (
62
+ type === DEPOSIT_REQUEST_TYPE ||
63
+ type === WITHDRAWAL_REQUEST_TYPE ||
64
+ type === CONSOLIDATION_REQUEST_TYPE ||
65
+ type === BUILDER_DEPOSIT_REQUEST_TYPE ||
66
+ type === BUILDER_EXIT_REQUEST_TYPE
67
+ );
58
68
  }
59
69
 
60
70
  export type ExecutePayloadResponse =
@@ -393,11 +393,12 @@ export class ExecutionEngineMockBackend implements JsonRpcBackend {
393
393
  blobs,
394
394
  proofs,
395
395
  }),
396
- executionRequests: serializeExecutionRequests({
397
- deposits: ssz.electra.DepositRequests.defaultValue(),
398
- withdrawals: ssz.electra.WithdrawalRequests.defaultValue(),
399
- consolidations: ssz.electra.ConsolidationRequests.defaultValue(),
400
- }),
396
+ executionRequests: serializeExecutionRequests(
397
+ fork,
398
+ ForkSeq[fork] >= ForkSeq.gloas
399
+ ? ssz.gloas.ExecutionRequests.defaultValue()
400
+ : ssz.electra.ExecutionRequests.defaultValue()
401
+ ),
401
402
  });
402
403
 
403
404
  // IF the payload is deemed VALID and the build process has begun
@@ -1,4 +1,6 @@
1
1
  import {
2
+ BUILDER_DEPOSIT_REQUEST_TYPE,
3
+ BUILDER_EXIT_REQUEST_TYPE,
2
4
  BYTES_PER_FIELD_ELEMENT,
3
5
  BYTES_PER_LOGS_BLOOM,
4
6
  CELLS_PER_EXT_BLOB,
@@ -205,16 +207,27 @@ export type WithdrawalRpc = {
205
207
  };
206
208
 
207
209
  /**
208
- * ExecutionRequestsRpc only holds at most 3 elements and no repeated type:
209
- * - ssz'ed DepositRequests
210
- * - ssz'ed WithdrawalRequests
211
- * - ssz'ed ConsolidationRequests
210
+ * ExecutionRequestsRpc holds at most 5 elements and no repeated type, ordered by
211
+ * the single-byte request type prefix:
212
+ * - ssz'ed DepositRequests (0x00)
213
+ * - ssz'ed WithdrawalRequests (0x01)
214
+ * - ssz'ed ConsolidationRequests (0x02)
215
+ * - ssz'ed BuilderDepositRequests (0x03)
216
+ * - ssz'ed BuilderExitRequests (0x04)
212
217
  */
213
- export type ExecutionRequestsRpc = (DepositRequestsRpc | WithdrawalRequestsRpc | ConsolidationRequestsRpc)[];
218
+ export type ExecutionRequestsRpc = (
219
+ | DepositRequestsRpc
220
+ | WithdrawalRequestsRpc
221
+ | ConsolidationRequestsRpc
222
+ | BuilderDepositRequestsRpc
223
+ | BuilderExitRequestsRpc
224
+ )[];
214
225
 
215
226
  export type DepositRequestsRpc = DATA;
216
227
  export type WithdrawalRequestsRpc = DATA;
217
228
  export type ConsolidationRequestsRpc = DATA;
229
+ export type BuilderDepositRequestsRpc = DATA;
230
+ export type BuilderExitRequestsRpc = DATA;
218
231
 
219
232
  export type BlobAndProofRpc = {
220
233
  blob: DATA;
@@ -339,7 +352,7 @@ export function parseExecutionPayload(
339
352
  data = response.executionPayload;
340
353
  blobsBundle = response.blobsBundle ? parseBlobsBundle(response.blobsBundle) : undefined;
341
354
  executionRequests = response.executionRequests
342
- ? deserializeExecutionRequests(response.executionRequests)
355
+ ? deserializeExecutionRequests(fork, response.executionRequests)
343
356
  : undefined;
344
357
  shouldOverrideBuilder = response.shouldOverrideBuilder ?? false;
345
358
  } else {
@@ -526,13 +539,36 @@ function deserializeConsolidationRequests(serialized: ConsolidationRequestsRpc):
526
539
  return ssz.electra.ConsolidationRequests.deserialize(dataToBytes(serialized, null));
527
540
  }
528
541
 
542
+ function serializeBuilderDepositRequests(
543
+ builderDepositRequests: gloas.BuilderDepositRequests
544
+ ): BuilderDepositRequestsRpc {
545
+ const requestsBytes = ssz.gloas.BuilderDepositRequests.serialize(builderDepositRequests);
546
+ return bytesToData(prefixRequests(requestsBytes, BUILDER_DEPOSIT_REQUEST_TYPE));
547
+ }
548
+
549
+ function deserializeBuilderDepositRequests(serialized: BuilderDepositRequestsRpc): gloas.BuilderDepositRequests {
550
+ return ssz.gloas.BuilderDepositRequests.deserialize(dataToBytes(serialized, null));
551
+ }
552
+
553
+ function serializeBuilderExitRequests(builderExitRequests: gloas.BuilderExitRequests): BuilderExitRequestsRpc {
554
+ const requestsBytes = ssz.gloas.BuilderExitRequests.serialize(builderExitRequests);
555
+ return bytesToData(prefixRequests(requestsBytes, BUILDER_EXIT_REQUEST_TYPE));
556
+ }
557
+
558
+ function deserializeBuilderExitRequests(serialized: BuilderExitRequestsRpc): gloas.BuilderExitRequests {
559
+ return ssz.gloas.BuilderExitRequests.deserialize(dataToBytes(serialized, null));
560
+ }
561
+
529
562
  /**
530
563
  * This is identical to get_execution_requests_list in
531
564
  * https://github.com/ethereum/consensus-specs/blob/v1.5.0-alpha.8/specs/electra/beacon-chain.md#new-get_execution_requests_list
565
+ *
566
+ * Gloas extends the list with builder deposits (0x03) and builder exits (0x04) per
567
+ * https://github.com/ethereum/consensus-specs/blob/v1.7.0-alpha.11/specs/gloas/beacon-chain.md#modified-get_execution_requests_list
532
568
  */
533
- export function serializeExecutionRequests(executionRequests: ExecutionRequests): ExecutionRequestsRpc {
569
+ export function serializeExecutionRequests(fork: ForkName, executionRequests: ExecutionRequests): ExecutionRequestsRpc {
534
570
  const {deposits, withdrawals, consolidations} = executionRequests;
535
- const result = [];
571
+ const result: ExecutionRequestsRpc = [];
536
572
 
537
573
  if (deposits.length !== 0) {
538
574
  result.push(serializeDepositRequests(deposits));
@@ -546,15 +582,26 @@ export function serializeExecutionRequests(executionRequests: ExecutionRequests)
546
582
  result.push(serializeConsolidationRequests(consolidations));
547
583
  }
548
584
 
585
+ if (ForkSeq[fork] >= ForkSeq.gloas) {
586
+ const {builderDeposits, builderExits} = executionRequests as gloas.ExecutionRequests;
587
+
588
+ if (builderDeposits.length !== 0) {
589
+ result.push(serializeBuilderDepositRequests(builderDeposits));
590
+ }
591
+
592
+ if (builderExits.length !== 0) {
593
+ result.push(serializeBuilderExitRequests(builderExits));
594
+ }
595
+ }
596
+
549
597
  return result;
550
598
  }
551
599
 
552
- export function deserializeExecutionRequests(serialized: ExecutionRequestsRpc): ExecutionRequests {
553
- const result: ExecutionRequests = {
554
- deposits: [],
555
- withdrawals: [],
556
- consolidations: [],
557
- };
600
+ export function deserializeExecutionRequests(fork: ForkName, serialized: ExecutionRequestsRpc): ExecutionRequests {
601
+ const result: ExecutionRequests =
602
+ ForkSeq[fork] >= ForkSeq.gloas
603
+ ? {deposits: [], withdrawals: [], consolidations: [], builderDeposits: [], builderExits: []}
604
+ : {deposits: [], withdrawals: [], consolidations: []};
558
605
 
559
606
  if (serialized.length === 0) {
560
607
  return result;
@@ -601,6 +648,20 @@ export function deserializeExecutionRequests(serialized: ExecutionRequestsRpc):
601
648
  result.consolidations = deserializeConsolidationRequests(requests);
602
649
  break;
603
650
  }
651
+ case BUILDER_DEPOSIT_REQUEST_TYPE: {
652
+ if (ForkSeq[fork] < ForkSeq.gloas) {
653
+ throw Error(`Builder deposit request is not supported pre-gloas fork=${fork}`);
654
+ }
655
+ (result as gloas.ExecutionRequests).builderDeposits = deserializeBuilderDepositRequests(requests);
656
+ break;
657
+ }
658
+ case BUILDER_EXIT_REQUEST_TYPE: {
659
+ if (ForkSeq[fork] < ForkSeq.gloas) {
660
+ throw Error(`Builder exit request is not supported pre-gloas fork=${fork}`);
661
+ }
662
+ (result as gloas.ExecutionRequests).builderExits = deserializeBuilderExitRequests(requests);
663
+ break;
664
+ }
604
665
  }
605
666
  prevRequestType = currentRequestType;
606
667
  }
@@ -610,6 +610,11 @@ export function createLodestarMetrics(
610
610
  help: "The origination source of one of the BlockInputSync triggers",
611
611
  labelNames: ["source"],
612
612
  }),
613
+ payloadSource: register.counter<{source: BlockInputSource}>({
614
+ name: "lodestar_payload_input_sync_source_total",
615
+ help: "Count of payload (execution payload envelope) sync triggers, labeled by their source",
616
+ labelNames: ["source"],
617
+ }),
613
618
  pendingBlocks: register.gauge({
614
619
  name: "lodestar_sync_unknown_block_pending_blocks_size",
615
620
  help: "Current size of UnknownBlockSync pending blocks cache",
@@ -861,6 +866,12 @@ export function createLodestarMetrics(
861
866
  labelNames: ["numBlobs"],
862
867
  }),
863
868
 
869
+ skippedSlots: register.histogram({
870
+ name: "lodestar_gossip_block_skipped_slots",
871
+ help: "Number of skipped slots between a gossip block and its parent (blockSlot - parentSlot - 1)",
872
+ buckets: [0, 1, 2, 4, 8, 16, 32],
873
+ }),
874
+
864
875
  processBlockErrors: register.gauge<{error: BlockErrorCode | "NOT_BLOCK_ERROR"}>({
865
876
  name: "lodestar_gossip_block_process_block_errors",
866
877
  help: "Count of errors, by error type, while processing blocks",
@@ -987,10 +998,11 @@ export function createLodestarMetrics(
987
998
  }),
988
999
  },
989
1000
  importPayload: {
990
- bySource: register.gauge<{source: PayloadEnvelopeInputSource}>({
991
- name: "lodestar_import_payload_by_source_total",
992
- help: "Total number of imported execution payload envelopes by source",
1001
+ elapsedTimeTillImported: register.histogram<{source: PayloadEnvelopeInputSource}>({
1002
+ name: "lodestar_import_payload_elapsed_time_till_imported_seconds",
1003
+ help: "Time elapsed between slot time and the time execution payload envelope is imported (added to fork choice)",
993
1004
  labelNames: ["source"],
1005
+ buckets: [1, 2, 3, 6, 9, 12],
994
1006
  }),
995
1007
  columnsBySource: register.gauge<{source: PayloadEnvelopeInputSource}>({
996
1008
  name: "lodestar_import_payload_columns_by_source_total",
@@ -113,6 +113,7 @@ export interface INetwork extends INetworkCorePublic {
113
113
  publishLightClientFinalityUpdate(update: LightClientFinalityUpdate): Promise<number>;
114
114
  publishLightClientOptimisticUpdate(update: LightClientOptimisticUpdate): Promise<number>;
115
115
  publishSignedExecutionPayloadEnvelope(signedEnvelope: gloas.SignedExecutionPayloadEnvelope): Promise<number>;
116
+ publishSignedExecutionPayloadBid(signedBid: gloas.SignedExecutionPayloadBid): Promise<number>;
116
117
  publishPayloadAttestationMessage(payloadAttestationMessage: gloas.PayloadAttestationMessage): Promise<number>;
117
118
  publishProposerPreferences(signedProposerPreferences: gloas.SignedProposerPreferences): Promise<number>;
118
119
 
@@ -515,6 +515,17 @@ export class Network implements INetwork {
515
515
  );
516
516
  }
517
517
 
518
+ async publishSignedExecutionPayloadBid(signedBid: gloas.SignedExecutionPayloadBid): Promise<number> {
519
+ const epoch = computeEpochAtSlot(signedBid.message.slot);
520
+ const boundary = this.config.getForkBoundaryAtEpoch(epoch);
521
+
522
+ return this.publishGossip<GossipType.execution_payload_bid>(
523
+ {type: GossipType.execution_payload_bid, boundary},
524
+ signedBid,
525
+ {ignoreDuplicatePublishError: true}
526
+ );
527
+ }
528
+
518
529
  async publishPayloadAttestationMessage(payloadAttestationMessage: gloas.PayloadAttestationMessage): Promise<number> {
519
530
  const epoch = computeEpochAtSlot(payloadAttestationMessage.data.slot);
520
531
  const boundary = this.config.getForkBoundaryAtEpoch(epoch);