@lodestar/beacon-node 1.45.0 → 1.46.0-dev.18a1510f34

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 (268) hide show
  1. package/lib/api/impl/beacon/blocks/index.d.ts.map +1 -1
  2. package/lib/api/impl/beacon/blocks/index.js +186 -57
  3. package/lib/api/impl/beacon/blocks/index.js.map +1 -1
  4. package/lib/api/impl/config/constants.d.ts +1 -0
  5. package/lib/api/impl/config/constants.d.ts.map +1 -1
  6. package/lib/api/impl/config/constants.js +2 -1
  7. package/lib/api/impl/config/constants.js.map +1 -1
  8. package/lib/api/impl/lodestar/index.d.ts.map +1 -1
  9. package/lib/api/impl/lodestar/index.js +2 -3
  10. package/lib/api/impl/lodestar/index.js.map +1 -1
  11. package/lib/api/impl/validator/index.d.ts.map +1 -1
  12. package/lib/api/impl/validator/index.js +173 -89
  13. package/lib/api/impl/validator/index.js.map +1 -1
  14. package/lib/api/rest/base.d.ts.map +1 -1
  15. package/lib/api/rest/base.js +6 -3
  16. package/lib/api/rest/base.js.map +1 -1
  17. package/lib/chain/archiveStore/archiveStore.d.ts.map +1 -1
  18. package/lib/chain/archiveStore/archiveStore.js +17 -3
  19. package/lib/chain/archiveStore/archiveStore.js.map +1 -1
  20. package/lib/chain/archiveStore/historicalState/historicalStateRegen.d.ts.map +1 -1
  21. package/lib/chain/archiveStore/historicalState/historicalStateRegen.js +0 -1
  22. package/lib/chain/archiveStore/historicalState/historicalStateRegen.js.map +1 -1
  23. package/lib/chain/blocks/importBlock.d.ts.map +1 -1
  24. package/lib/chain/blocks/importBlock.js +2 -17
  25. package/lib/chain/blocks/importBlock.js.map +1 -1
  26. package/lib/chain/blocks/utils/chainSegment.d.ts.map +1 -1
  27. package/lib/chain/blocks/utils/chainSegment.js +13 -5
  28. package/lib/chain/blocks/utils/chainSegment.js.map +1 -1
  29. package/lib/chain/blocks/verifyBlocksExecutionPayloads.js +1 -1
  30. package/lib/chain/blocks/verifyBlocksExecutionPayloads.js.map +1 -1
  31. package/lib/chain/blocks/verifyBlocksSanityChecks.js +1 -1
  32. package/lib/chain/blocks/verifyBlocksSanityChecks.js.map +1 -1
  33. package/lib/chain/blocks/verifyExecutionPayloadEnvelope.js +3 -3
  34. package/lib/chain/blocks/verifyExecutionPayloadEnvelope.js.map +1 -1
  35. package/lib/chain/bls/multithread/index.d.ts.map +1 -1
  36. package/lib/chain/bls/multithread/index.js +0 -1
  37. package/lib/chain/bls/multithread/index.js.map +1 -1
  38. package/lib/chain/chain.d.ts.map +1 -1
  39. package/lib/chain/chain.js +44 -2
  40. package/lib/chain/chain.js.map +1 -1
  41. package/lib/chain/errors/blockError.d.ts +48 -6
  42. package/lib/chain/errors/blockError.d.ts.map +1 -1
  43. package/lib/chain/errors/blockError.js +25 -4
  44. package/lib/chain/errors/blockError.js.map +1 -1
  45. package/lib/chain/errors/executionPayloadBid.d.ts +11 -4
  46. package/lib/chain/errors/executionPayloadBid.d.ts.map +1 -1
  47. package/lib/chain/errors/executionPayloadBid.js +1 -0
  48. package/lib/chain/errors/executionPayloadBid.js.map +1 -1
  49. package/lib/chain/errors/executionPayloadEnvelope.d.ts +11 -0
  50. package/lib/chain/errors/executionPayloadEnvelope.d.ts.map +1 -1
  51. package/lib/chain/errors/executionPayloadEnvelope.js +2 -0
  52. package/lib/chain/errors/executionPayloadEnvelope.js.map +1 -1
  53. package/lib/chain/errors/proposerPreferences.d.ts +14 -1
  54. package/lib/chain/errors/proposerPreferences.d.ts.map +1 -1
  55. package/lib/chain/errors/proposerPreferences.js +2 -0
  56. package/lib/chain/errors/proposerPreferences.js.map +1 -1
  57. package/lib/chain/lightClient/index.d.ts.map +1 -1
  58. package/lib/chain/lightClient/index.js +11 -6
  59. package/lib/chain/lightClient/index.js.map +1 -1
  60. package/lib/chain/lightClient/proofs.d.ts.map +1 -1
  61. package/lib/chain/lightClient/proofs.js +28 -2
  62. package/lib/chain/lightClient/proofs.js.map +1 -1
  63. package/lib/chain/lightClient/types.d.ts +3 -1
  64. package/lib/chain/lightClient/types.d.ts.map +1 -1
  65. package/lib/chain/opPools/aggregatedAttestationPool.d.ts.map +1 -1
  66. package/lib/chain/opPools/aggregatedAttestationPool.js +2 -1
  67. package/lib/chain/opPools/aggregatedAttestationPool.js.map +1 -1
  68. package/lib/chain/opPools/proposerPreferencesPool.d.ts +1 -1
  69. package/lib/chain/opPools/proposerPreferencesPool.d.ts.map +1 -1
  70. package/lib/chain/opPools/proposerPreferencesPool.js +1 -1
  71. package/lib/chain/opPools/proposerPreferencesPool.js.map +1 -1
  72. package/lib/chain/prepareNextSlot.js +1 -1
  73. package/lib/chain/prepareNextSlot.js.map +1 -1
  74. package/lib/chain/produceBlock/produceBlockBody.js +4 -4
  75. package/lib/chain/produceBlock/produceBlockBody.js.map +1 -1
  76. package/lib/chain/seenCache/seenExecutionPayloadBids.d.ts +6 -5
  77. package/lib/chain/seenCache/seenExecutionPayloadBids.d.ts.map +1 -1
  78. package/lib/chain/seenCache/seenExecutionPayloadBids.js +16 -8
  79. package/lib/chain/seenCache/seenExecutionPayloadBids.js.map +1 -1
  80. package/lib/chain/validation/aggregateAndProof.js +10 -6
  81. package/lib/chain/validation/aggregateAndProof.js.map +1 -1
  82. package/lib/chain/validation/attestation.d.ts.map +1 -1
  83. package/lib/chain/validation/attestation.js +10 -6
  84. package/lib/chain/validation/attestation.js.map +1 -1
  85. package/lib/chain/validation/blobSidecar.js +1 -1
  86. package/lib/chain/validation/block.d.ts.map +1 -1
  87. package/lib/chain/validation/block.js +53 -4
  88. package/lib/chain/validation/block.js.map +1 -1
  89. package/lib/chain/validation/executionPayloadBid.d.ts.map +1 -1
  90. package/lib/chain/validation/executionPayloadBid.js +100 -26
  91. package/lib/chain/validation/executionPayloadBid.js.map +1 -1
  92. package/lib/chain/validation/executionPayloadEnvelope.d.ts.map +1 -1
  93. package/lib/chain/validation/executionPayloadEnvelope.js +29 -0
  94. package/lib/chain/validation/executionPayloadEnvelope.js.map +1 -1
  95. package/lib/chain/validation/proposerPreferences.d.ts +9 -2
  96. package/lib/chain/validation/proposerPreferences.d.ts.map +1 -1
  97. package/lib/chain/validation/proposerPreferences.js +65 -13
  98. package/lib/chain/validation/proposerPreferences.js.map +1 -1
  99. package/lib/chain/validation/signatureSets/aggregateAndProof.d.ts.map +1 -1
  100. package/lib/chain/validation/signatureSets/aggregateAndProof.js +2 -3
  101. package/lib/chain/validation/signatureSets/aggregateAndProof.js.map +1 -1
  102. package/lib/chain/validatorMonitor.js +1 -1
  103. package/lib/chain/validatorMonitor.js.map +1 -1
  104. package/lib/db/repositories/lightclientSyncCommitteeWitness.d.ts.map +1 -1
  105. package/lib/db/repositories/lightclientSyncCommitteeWitness.js +42 -7
  106. package/lib/db/repositories/lightclientSyncCommitteeWitness.js.map +1 -1
  107. package/lib/execution/engine/interface.d.ts +1 -1
  108. package/lib/execution/engine/types.js +1 -1
  109. package/lib/execution/engine/types.js.map +1 -1
  110. package/lib/metrics/nodeJsMetrics.js +1 -1
  111. package/lib/metrics/nodeJsMetrics.js.map +1 -1
  112. package/lib/network/core/networkCoreWorkerHandler.d.ts.map +1 -1
  113. package/lib/network/core/networkCoreWorkerHandler.js +2 -7
  114. package/lib/network/core/networkCoreWorkerHandler.js.map +1 -1
  115. package/lib/network/discv5/index.d.ts.map +1 -1
  116. package/lib/network/discv5/index.js +1 -4
  117. package/lib/network/discv5/index.js.map +1 -1
  118. package/lib/network/gossip/encoding.d.ts +2 -2
  119. package/lib/network/gossip/encoding.d.ts.map +1 -1
  120. package/lib/network/gossip/encoding.js +15 -9
  121. package/lib/network/gossip/encoding.js.map +1 -1
  122. package/lib/network/gossip/gossipsub.d.ts.map +1 -1
  123. package/lib/network/gossip/gossipsub.js +2 -2
  124. package/lib/network/gossip/gossipsub.js.map +1 -1
  125. package/lib/network/gossip/topic.d.ts +79 -236
  126. package/lib/network/gossip/topic.d.ts.map +1 -1
  127. package/lib/network/gossip/topic.js +24 -1
  128. package/lib/network/gossip/topic.js.map +1 -1
  129. package/lib/network/interface.d.ts +4 -1
  130. package/lib/network/interface.d.ts.map +1 -1
  131. package/lib/network/libp2p/index.d.ts.map +1 -1
  132. package/lib/network/libp2p/index.js +2 -9
  133. package/lib/network/libp2p/index.js.map +1 -1
  134. package/lib/network/network.d.ts +4 -1
  135. package/lib/network/network.d.ts.map +1 -1
  136. package/lib/network/network.js +19 -11
  137. package/lib/network/network.js.map +1 -1
  138. package/lib/network/peers/datastore.d.ts +1 -1
  139. package/lib/network/peers/datastore.d.ts.map +1 -1
  140. package/lib/network/peers/datastore.js +1 -1
  141. package/lib/network/peers/datastore.js.map +1 -1
  142. package/lib/network/processor/gossipHandlers.d.ts.map +1 -1
  143. package/lib/network/processor/gossipHandlers.js +12 -6
  144. package/lib/network/processor/gossipHandlers.js.map +1 -1
  145. package/lib/network/processor/index.d.ts.map +1 -1
  146. package/lib/network/processor/index.js +2 -1
  147. package/lib/network/processor/index.js.map +1 -1
  148. package/lib/network/reqresp/handlers/executionPayloadEnvelopesByRange.d.ts.map +1 -1
  149. package/lib/network/reqresp/handlers/executionPayloadEnvelopesByRange.js +4 -2
  150. package/lib/network/reqresp/handlers/executionPayloadEnvelopesByRange.js.map +1 -1
  151. package/lib/network/reqresp/handlers/index.d.ts.map +1 -1
  152. package/lib/network/reqresp/handlers/index.js +20 -11
  153. package/lib/network/reqresp/handlers/index.js.map +1 -1
  154. package/lib/network/reqresp/handlers/lightClientFinalityUpdate.js +1 -1
  155. package/lib/network/reqresp/handlers/lightClientFinalityUpdate.js.map +1 -1
  156. package/lib/network/reqresp/handlers/lightClientOptimisticUpdate.js +1 -1
  157. package/lib/network/reqresp/handlers/lightClientOptimisticUpdate.js.map +1 -1
  158. package/lib/network/reqresp/handlers/lightClientUpdatesByRange.d.ts.map +1 -1
  159. package/lib/network/reqresp/handlers/lightClientUpdatesByRange.js +6 -2
  160. package/lib/network/reqresp/handlers/lightClientUpdatesByRange.js.map +1 -1
  161. package/lib/network/util.d.ts +1 -0
  162. package/lib/network/util.d.ts.map +1 -1
  163. package/lib/network/util.js +4 -0
  164. package/lib/network/util.js.map +1 -1
  165. package/lib/sync/constants.d.ts +6 -5
  166. package/lib/sync/constants.d.ts.map +1 -1
  167. package/lib/sync/constants.js +6 -5
  168. package/lib/sync/constants.js.map +1 -1
  169. package/lib/sync/range/batch.d.ts +10 -1
  170. package/lib/sync/range/batch.d.ts.map +1 -1
  171. package/lib/sync/range/batch.js +28 -10
  172. package/lib/sync/range/batch.js.map +1 -1
  173. package/lib/sync/range/chain.d.ts.map +1 -1
  174. package/lib/sync/range/chain.js +30 -15
  175. package/lib/sync/range/chain.js.map +1 -1
  176. package/lib/sync/range/utils/batches.d.ts +18 -0
  177. package/lib/sync/range/utils/batches.d.ts.map +1 -1
  178. package/lib/sync/range/utils/batches.js +39 -0
  179. package/lib/sync/range/utils/batches.js.map +1 -1
  180. package/lib/sync/sync.d.ts.map +1 -1
  181. package/lib/sync/sync.js +7 -0
  182. package/lib/sync/sync.js.map +1 -1
  183. package/lib/sync/unknownBlock.d.ts.map +1 -1
  184. package/lib/sync/unknownBlock.js +7 -2
  185. package/lib/sync/unknownBlock.js.map +1 -1
  186. package/lib/util/execution.js +1 -1
  187. package/lib/util/execution.js.map +1 -1
  188. package/lib/util/profile.d.ts +3 -0
  189. package/lib/util/profile.d.ts.map +1 -1
  190. package/lib/util/profile.js +1 -27
  191. package/lib/util/profile.js.map +1 -1
  192. package/lib/util/sszBytes.js +1 -1
  193. package/package.json +29 -38
  194. package/src/api/impl/beacon/blocks/index.ts +221 -63
  195. package/src/api/impl/config/constants.ts +2 -0
  196. package/src/api/impl/lodestar/index.ts +2 -3
  197. package/src/api/impl/validator/index.ts +207 -99
  198. package/src/api/rest/base.ts +8 -3
  199. package/src/chain/archiveStore/archiveStore.ts +20 -3
  200. package/src/chain/archiveStore/historicalState/historicalStateRegen.ts +0 -1
  201. package/src/chain/blocks/importBlock.ts +0 -17
  202. package/src/chain/blocks/utils/chainSegment.ts +16 -5
  203. package/src/chain/blocks/verifyBlocksExecutionPayloads.ts +1 -1
  204. package/src/chain/blocks/verifyBlocksSanityChecks.ts +1 -1
  205. package/src/chain/blocks/verifyExecutionPayloadEnvelope.ts +3 -3
  206. package/src/chain/bls/multithread/index.ts +0 -1
  207. package/src/chain/chain.ts +61 -2
  208. package/src/chain/errors/blockError.ts +44 -7
  209. package/src/chain/errors/executionPayloadBid.ts +12 -4
  210. package/src/chain/errors/executionPayloadEnvelope.ts +9 -0
  211. package/src/chain/errors/proposerPreferences.ts +16 -1
  212. package/src/chain/lightClient/index.ts +12 -7
  213. package/src/chain/lightClient/proofs.ts +36 -1
  214. package/src/chain/lightClient/types.ts +3 -1
  215. package/src/chain/opPools/aggregatedAttestationPool.ts +4 -2
  216. package/src/chain/opPools/proposerPreferencesPool.ts +1 -1
  217. package/src/chain/prepareNextSlot.ts +1 -1
  218. package/src/chain/produceBlock/produceBlockBody.ts +5 -5
  219. package/src/chain/seenCache/seenExecutionPayloadBids.ts +22 -9
  220. package/src/chain/validation/aggregateAndProof.ts +12 -6
  221. package/src/chain/validation/attestation.ts +12 -6
  222. package/src/chain/validation/blobSidecar.ts +1 -1
  223. package/src/chain/validation/block.ts +69 -4
  224. package/src/chain/validation/executionPayloadBid.ts +119 -28
  225. package/src/chain/validation/executionPayloadEnvelope.ts +37 -0
  226. package/src/chain/validation/proposerPreferences.ts +75 -13
  227. package/src/chain/validation/signatureSets/aggregateAndProof.ts +3 -3
  228. package/src/chain/validatorMonitor.ts +1 -1
  229. package/src/db/repositories/lightclientSyncCommitteeWitness.ts +57 -9
  230. package/src/execution/engine/interface.ts +1 -1
  231. package/src/execution/engine/types.ts +1 -1
  232. package/src/metrics/nodeJsMetrics.ts +1 -1
  233. package/src/network/core/networkCoreWorkerHandler.ts +2 -6
  234. package/src/network/discv5/index.ts +1 -4
  235. package/src/network/gossip/encoding.ts +13 -7
  236. package/src/network/gossip/gossipsub.ts +7 -2
  237. package/src/network/gossip/topic.ts +30 -0
  238. package/src/network/interface.ts +3 -1
  239. package/src/network/libp2p/index.ts +2 -10
  240. package/src/network/network.ts +24 -15
  241. package/src/network/peers/datastore.ts +1 -1
  242. package/src/network/processor/gossipHandlers.ts +12 -6
  243. package/src/network/processor/index.ts +2 -1
  244. package/src/network/reqresp/handlers/executionPayloadEnvelopesByRange.ts +4 -2
  245. package/src/network/reqresp/handlers/index.ts +21 -12
  246. package/src/network/reqresp/handlers/lightClientFinalityUpdate.ts +1 -1
  247. package/src/network/reqresp/handlers/lightClientOptimisticUpdate.ts +1 -1
  248. package/src/network/reqresp/handlers/lightClientUpdatesByRange.ts +6 -2
  249. package/src/network/util.ts +5 -0
  250. package/src/sync/constants.ts +6 -5
  251. package/src/sync/range/batch.ts +31 -10
  252. package/src/sync/range/chain.ts +36 -16
  253. package/src/sync/range/utils/batches.ts +44 -0
  254. package/src/sync/sync.ts +10 -0
  255. package/src/sync/unknownBlock.ts +8 -2
  256. package/src/util/execution.ts +1 -1
  257. package/src/util/profile.ts +1 -31
  258. package/src/util/sszBytes.ts +1 -1
  259. package/lib/bun-wrappers/prometheus-gc-stats.d.ts +0 -2
  260. package/lib/bun-wrappers/prometheus-gc-stats.d.ts.map +0 -1
  261. package/lib/bun-wrappers/prometheus-gc-stats.js +0 -8
  262. package/lib/bun-wrappers/prometheus-gc-stats.js.map +0 -1
  263. package/lib/network/peers/datastore_bun.d.ts +0 -3
  264. package/lib/network/peers/datastore_bun.d.ts.map +0 -1
  265. package/lib/network/peers/datastore_bun.js +0 -3
  266. package/lib/network/peers/datastore_bun.js.map +0 -1
  267. package/src/bun-wrappers/prometheus-gc-stats.ts +0 -7
  268. package/src/network/peers/datastore_bun.ts +0 -3
@@ -1,8 +1,17 @@
1
1
  import path from "node:path";
2
2
  import {PrivateKey} from "@libp2p/interface";
3
3
  import {Type} from "@chainsafe/ssz";
4
+ import {routes} from "@lodestar/api";
4
5
  import {BeaconConfig} from "@lodestar/config";
5
- import {CheckpointWithHex, ForkChoiceStateGetter, IForkChoice, ProtoBlock, UpdateHeadOpt} from "@lodestar/fork-choice";
6
+ import {
7
+ CheckpointWithHex,
8
+ EpochDifference,
9
+ ForkChoiceStateGetter,
10
+ IForkChoice,
11
+ PayloadStatus,
12
+ ProtoBlock,
13
+ UpdateHeadOpt,
14
+ } from "@lodestar/fork-choice";
6
15
  import {LoggerNode} from "@lodestar/logger/node";
7
16
  import {
8
17
  EFFECTIVE_BALANCE_INCREMENT,
@@ -1179,7 +1188,56 @@ export class BeaconChain implements IBeaconChain {
1179
1188
  const timer = this.metrics?.forkChoice.findHead.startTimer({caller});
1180
1189
 
1181
1190
  try {
1182
- return this.forkChoice.updateAndGetHead({mode: UpdateHeadOpt.GetCanonicalHead}).head;
1191
+ const prevHead = this.forkChoice.getHead();
1192
+ const head = this.forkChoice.updateAndGetHead({mode: UpdateHeadOpt.GetCanonicalHead}).head;
1193
+
1194
+ const headRootChanged = head.blockRoot !== prevHead.blockRoot;
1195
+
1196
+ if (!headRootChanged && prevHead.payloadStatus === head.payloadStatus) {
1197
+ return head;
1198
+ }
1199
+
1200
+ try {
1201
+ const previousDutyDependentRoot = this.forkChoice.getDependentRoot(head, EpochDifference.previous);
1202
+ const currentDutyDependentRoot = this.forkChoice.getDependentRoot(head, EpochDifference.current);
1203
+ const epochTransition = computeStartSlotAtEpoch(computeEpochAtSlot(head.slot)) === head.slot;
1204
+ const executionOptimistic = isOptimisticBlock(head);
1205
+
1206
+ if (headRootChanged) {
1207
+ this.emitter.emit(routes.events.EventType.head, {
1208
+ block: head.blockRoot,
1209
+ epochTransition,
1210
+ slot: head.slot,
1211
+ state: head.stateRoot,
1212
+ previousDutyDependentRoot,
1213
+ currentDutyDependentRoot,
1214
+ executionOptimistic,
1215
+ });
1216
+ }
1217
+
1218
+ this.emitter.emit(routes.events.EventType.headV2, {
1219
+ version: this.config.getForkName(head.slot),
1220
+ data: {
1221
+ slot: head.slot,
1222
+ block: head.blockRoot,
1223
+ state: head.stateRoot,
1224
+ payloadStatus: head.payloadStatus === PayloadStatus.FULL ? "full" : "empty",
1225
+ epochTransition,
1226
+ currentEpochDependentRoot: previousDutyDependentRoot,
1227
+ nextEpochDependentRoot: currentDutyDependentRoot,
1228
+ executionOptimistic,
1229
+ },
1230
+ });
1231
+ } catch (e) {
1232
+ // getDependentRoot() may fail with error: "No block for root" as we can see in holesky non-finality issue
1233
+ this.logger.debug(
1234
+ "Error emitting head/head_v2 event",
1235
+ {slot: head.slot, root: head.blockRoot, headRootChanged},
1236
+ e as Error
1237
+ );
1238
+ }
1239
+
1240
+ return head;
1183
1241
  } catch (e) {
1184
1242
  this.metrics?.forkChoice.errors.inc({entrypoint: UpdateHeadOpt.GetCanonicalHead});
1185
1243
  throw e;
@@ -1209,6 +1267,7 @@ export class BeaconChain implements IBeaconChain {
1209
1267
  const secFromSlot = this.clock.secFromSlot(slot);
1210
1268
 
1211
1269
  try {
1270
+ // Do not emit head event here, when proposing we rely on the one emitted when importing our own block
1212
1271
  const {head, isHeadTimely, notReorgedReason} = this.forkChoice.updateAndGetHead({
1213
1272
  mode: UpdateHeadOpt.GetProposerHead,
1214
1273
  secFromSlot,
@@ -8,8 +8,10 @@ import {GossipActionError} from "./gossipValidation.js";
8
8
  export enum BlockErrorCode {
9
9
  /** The prestate cannot be fetched */
10
10
  PRESTATE_MISSING = "BLOCK_ERROR_PRESTATE_MISSING",
11
- /** The parent block was unknown. */
12
- PARENT_UNKNOWN = "BLOCK_ERROR_PARENT_UNKNOWN",
11
+ /**
12
+ * The first block of a chain segment references a parentRoot unknown to fork-choice.
13
+ */
14
+ PARENT_BLOCK_UNKNOWN = "BLOCK_ERROR_PARENT_BLOCK_UNKNOWN",
13
15
  /** The block slot is greater than the present slot. */
14
16
  FUTURE_SLOT = "BLOCK_ERROR_FUTURE_SLOT",
15
17
  /** The block state_root does not match the generated state. */
@@ -38,7 +40,10 @@ export enum BlockErrorCode {
38
40
  NOT_FINALIZED_DESCENDANT = "BLOCK_ERROR_NOT_FINALIZED_DESCENDANT",
39
41
  /** The provided block is from an later slot than its parent. */
40
42
  NOT_LATER_THAN_PARENT = "BLOCK_ERROR_NOT_LATER_THAN_PARENT",
41
- /** At least one block in the chain segment did not have it's parent root set to the root of the prior block. */
43
+ /**
44
+ * A block in the middle of a chain segment does not set its parentRoot to the previous block's root
45
+ * (broken parent-root link inside the segment).
46
+ */
42
47
  NON_LINEAR_PARENT_ROOTS = "BLOCK_ERROR_NON_LINEAR_PARENT_ROOTS",
43
48
  /** The slots of the blocks in the chain segment were not strictly increasing. */
44
49
  NON_LINEAR_SLOTS = "BLOCK_ERROR_NON_LINEAR_SLOTS",
@@ -61,16 +66,32 @@ export enum BlockErrorCode {
61
66
  TRANSACTIONS_TOO_BIG = "BLOCK_ERROR_TRANSACTIONS_TOO_BIG",
62
67
  /** Execution engine is unavailable, syncing, or api call errored. Peers must not be downscored on this code */
63
68
  EXECUTION_ENGINE_ERROR = "BLOCK_ERROR_EXECUTION_ERROR",
69
+ /**
70
+ * Execution engine returned a definitive INVALID verdict on the payload. Unlike
71
+ * EXECUTION_ENGINE_ERROR this is evidence about the data itself, not a local malfunction, so
72
+ * whoever served the block MAY be downscored. Mirrors PayloadErrorCode.EXECUTION_ENGINE_INVALID.
73
+ */
74
+ EXECUTION_ENGINE_INVALID = "BLOCK_ERROR_EXECUTION_INVALID",
64
75
  /** The blobs are unavailable */
65
76
  DATA_UNAVAILABLE = "BLOCK_ERROR_DATA_UNAVAILABLE",
66
77
  /** Block contains too many kzg commitments */
67
78
  TOO_MANY_KZG_COMMITMENTS = "BLOCK_ERROR_TOO_MANY_KZG_COMMITMENTS",
68
79
  /** Bid parent block root does not match block parent root */
69
80
  BID_PARENT_ROOT_MISMATCH = "BLOCK_ERROR_BID_PARENT_ROOT_MISMATCH",
81
+ /** A block body operation or parent execution request list exceeds its per-block limit */
82
+ TOO_MANY_BLOCK_OPERATIONS = "BLOCK_ERROR_TOO_MANY_BLOCK_OPERATIONS",
70
83
  /** The parent block's execution payload has been verified as invalid */
71
84
  PARENT_EXECUTION_INVALID = "BLOCK_ERROR_PARENT_EXECUTION_INVALID",
72
- /** The block's parent execution payload (defined by bid.parent_block_hash) has not been seen */
85
+ /**
86
+ * [gloas] The first block of a chain segment references a parent execution payload
87
+ * (bid.parent_block_hash) unknown to fork-choice (segment boundary).
88
+ */
73
89
  PARENT_PAYLOAD_UNKNOWN = "BLOCK_ERROR_PARENT_PAYLOAD_UNKNOWN",
90
+ /**
91
+ * [gloas] A block in the middle of a chain segment has a bid.parent_block_hash that does not chain onto the
92
+ * previous block's execution payload (broken payload link inside the segment).
93
+ */
94
+ NON_LINEAR_PAYLOAD_ROOTS = "BLOCK_ERROR_NON_LINEAR_PAYLOAD_ROOTS",
74
95
  /** An execution payload envelope in the chain segment references a block root that does not match its slot's block */
75
96
  ENVELOPE_BLOCK_ROOT_MISMATCH = "BLOCK_ERROR_ENVELOPE_BLOCK_ROOT_MISMATCH",
76
97
  }
@@ -80,9 +101,18 @@ type ExecutionErrorStatus = Exclude<
80
101
  ExecutionPayloadStatus.VALID | ExecutionPayloadStatus.ACCEPTED | ExecutionPayloadStatus.SYNCING
81
102
  >;
82
103
 
104
+ /**
105
+ * Statuses where the execution engine could NOT render a verdict on the payload: it is unreachable
106
+ * (UNAVAILABLE), errored (ELERROR), or returned a malformed/client-specific response
107
+ * (INVALID_BLOCK_HASH, dropped from engine_newPayloadV2+). None of these are evidence about the
108
+ * data, so peers must not be downscored on them. A definitive INVALID carries
109
+ * BlockErrorCode.EXECUTION_ENGINE_INVALID instead.
110
+ */
111
+ type ExecutionEngineErrorStatus = Exclude<ExecutionErrorStatus, ExecutionPayloadStatus.INVALID>;
112
+
83
113
  export type BlockErrorType =
84
114
  | {code: BlockErrorCode.PRESTATE_MISSING; error: Error}
85
- | {code: BlockErrorCode.PARENT_UNKNOWN; parentRoot: RootHex}
115
+ | {code: BlockErrorCode.PARENT_BLOCK_UNKNOWN; parentRoot: RootHex}
86
116
  | {code: BlockErrorCode.FUTURE_SLOT; blockSlot: Slot; currentSlot: Slot}
87
117
  | {code: BlockErrorCode.STATE_ROOT_MISMATCH}
88
118
  | {code: BlockErrorCode.GENESIS_BLOCK}
@@ -114,12 +144,19 @@ export type BlockErrorType =
114
144
  | {code: BlockErrorCode.TOO_MUCH_GAS_USED; gasUsed: number; gasLimit: number}
115
145
  | {code: BlockErrorCode.SAME_PARENT_HASH; blockHash: RootHex}
116
146
  | {code: BlockErrorCode.TRANSACTIONS_TOO_BIG; size: number; max: number}
117
- | {code: BlockErrorCode.EXECUTION_ENGINE_ERROR; execStatus: ExecutionErrorStatus; errorMessage: string}
147
+ | {code: BlockErrorCode.EXECUTION_ENGINE_ERROR; execStatus: ExecutionEngineErrorStatus; errorMessage: string}
148
+ | {
149
+ code: BlockErrorCode.EXECUTION_ENGINE_INVALID;
150
+ execStatus: ExecutionPayloadStatus.INVALID;
151
+ errorMessage: string;
152
+ }
118
153
  | {code: BlockErrorCode.DATA_UNAVAILABLE}
119
154
  | {code: BlockErrorCode.TOO_MANY_KZG_COMMITMENTS; blobKzgCommitmentsLen: number; commitmentLimit: number}
120
155
  | {code: BlockErrorCode.BID_PARENT_ROOT_MISMATCH; bidParentRoot: RootHex; blockParentRoot: RootHex}
156
+ | {code: BlockErrorCode.TOO_MANY_BLOCK_OPERATIONS; name: string; count: number; limit: number}
121
157
  | {code: BlockErrorCode.PARENT_EXECUTION_INVALID; parentRoot: RootHex}
122
- | {code: BlockErrorCode.PARENT_PAYLOAD_UNKNOWN; parentRoot: RootHex; parentBlockHash: RootHex};
158
+ | {code: BlockErrorCode.PARENT_PAYLOAD_UNKNOWN; parentRoot: RootHex; parentBlockHash: RootHex}
159
+ | {code: BlockErrorCode.NON_LINEAR_PAYLOAD_ROOTS; parentBlockHash: RootHex; expectedBlockHash: RootHex};
123
160
 
124
161
  export class BlockGossipError extends GossipActionError<BlockErrorType> {}
125
162
 
@@ -5,6 +5,7 @@ export enum ExecutionPayloadBidErrorCode {
5
5
  BUILDER_NOT_ELIGIBLE = "EXECUTION_PAYLOAD_BID_ERROR_BUILDER_NOT_ELIGIBLE",
6
6
  INVALID_BUILDER_VERSION = "EXECUTION_PAYLOAD_BID_ERROR_INVALID_BUILDER_VERSION",
7
7
  NON_ZERO_EXECUTION_PAYMENT = "EXECUTION_PAYLOAD_BID_ERROR_NON_ZERO_EXECUTION_PAYMENT",
8
+ INCOMPATIBLE_WITH_HEAD = "EXECUTION_PAYLOAD_BID_ERROR_INCOMPATIBLE_WITH_HEAD",
8
9
  BID_ALREADY_KNOWN = "EXECUTION_PAYLOAD_BID_ERROR_BID_ALREADY_KNOWN",
9
10
  BID_TOO_LOW = "EXECUTION_PAYLOAD_BID_ERROR_BID_TOO_LOW",
10
11
  BID_TOO_HIGH = "EXECUTION_PAYLOAD_BID_ERROR_BID_TOO_HIGH",
@@ -31,7 +32,14 @@ export type ExecutionPayloadBidErrorType =
31
32
  | {
32
33
  code: ExecutionPayloadBidErrorCode.NON_ZERO_EXECUTION_PAYMENT;
33
34
  builderIndex: BuilderIndex;
34
- executionPayment: number;
35
+ executionPayment: bigint;
36
+ }
37
+ | {
38
+ code: ExecutionPayloadBidErrorCode.INCOMPATIBLE_WITH_HEAD;
39
+ slot: Slot;
40
+ parentBlockRoot: RootHex;
41
+ parentBlockHash: RootHex;
42
+ headBlockRoot: RootHex;
35
43
  }
36
44
  | {
37
45
  code: ExecutionPayloadBidErrorCode.BID_ALREADY_KNOWN;
@@ -73,9 +81,9 @@ export type ExecutionPayloadBidErrorType =
73
81
  | {
74
82
  code: ExecutionPayloadBidErrorCode.PROPOSER_PREFERENCES_GAS_LIMIT_MISMATCH;
75
83
  builderIndex: BuilderIndex;
76
- bidGasLimit: number;
77
- parentGasLimit: number;
78
- targetGasLimit: number;
84
+ bidGasLimit: bigint;
85
+ parentGasLimit: bigint;
86
+ targetGasLimit: bigint;
79
87
  };
80
88
 
81
89
  export class ExecutionPayloadBidError extends GossipActionError<ExecutionPayloadBidErrorType> {}
@@ -12,6 +12,8 @@ export enum ExecutionPayloadEnvelopeErrorCode {
12
12
  BUILDER_INDEX_MISMATCH = "EXECUTION_PAYLOAD_ENVELOPE_ERROR_BUILDER_INDEX_MISMATCH",
13
13
  BLOCK_HASH_MISMATCH = "EXECUTION_PAYLOAD_ENVELOPE_ERROR_BLOCK_HASH_MISMATCH",
14
14
  EXECUTION_REQUESTS_ROOT_MISMATCH = "EXECUTION_PAYLOAD_ENVELOPE_ERROR_EXECUTION_REQUESTS_ROOT_MISMATCH",
15
+ EXECUTION_REQUESTS_COUNT_EXCEEDED = "EXECUTION_PAYLOAD_ENVELOPE_ERROR_EXECUTION_REQUESTS_COUNT_EXCEEDED",
16
+ WITHDRAWALS_COUNT_EXCEEDED = "EXECUTION_PAYLOAD_ENVELOPE_ERROR_WITHDRAWALS_COUNT_EXCEEDED",
15
17
  INVALID_SIGNATURE = "EXECUTION_PAYLOAD_ENVELOPE_ERROR_INVALID_SIGNATURE",
16
18
  PAYLOAD_ENVELOPE_INPUT_MISSING = "EXECUTION_PAYLOAD_ENVELOPE_ERROR_PAYLOAD_ENVELOPE_INPUT_MISSING",
17
19
  }
@@ -42,6 +44,13 @@ export type ExecutionPayloadEnvelopeErrorType =
42
44
  envelopeRequestsRoot: RootHex;
43
45
  bidRequestsRoot: RootHex;
44
46
  }
47
+ | {
48
+ code: ExecutionPayloadEnvelopeErrorCode.EXECUTION_REQUESTS_COUNT_EXCEEDED;
49
+ name: string;
50
+ count: number;
51
+ limit: number;
52
+ }
53
+ | {code: ExecutionPayloadEnvelopeErrorCode.WITHDRAWALS_COUNT_EXCEEDED; count: number; limit: number}
45
54
  | {code: ExecutionPayloadEnvelopeErrorCode.INVALID_SIGNATURE}
46
55
  | {code: ExecutionPayloadEnvelopeErrorCode.PAYLOAD_ENVELOPE_INPUT_MISSING; blockRoot: RootHex};
47
56
 
@@ -1,10 +1,12 @@
1
- import {RootHex, Slot, ValidatorIndex} from "@lodestar/types";
1
+ import {Epoch, RootHex, Slot, ValidatorIndex} from "@lodestar/types";
2
2
  import {GossipActionError} from "./gossipValidation.js";
3
3
 
4
4
  export enum ProposerPreferencesErrorCode {
5
5
  INVALID_EPOCH = "PROPOSER_PREFERENCES_ERROR_INVALID_EPOCH",
6
6
  PROPOSAL_SLOT_PASSED = "PROPOSER_PREFERENCES_ERROR_PROPOSAL_SLOT_PASSED",
7
7
  UNKNOWN_DEPENDENT_ROOT = "PROPOSER_PREFERENCES_ERROR_UNKNOWN_DEPENDENT_ROOT",
8
+ INVALID_DEPENDENT_ROOT_SLOT = "PROPOSER_PREFERENCES_ERROR_INVALID_DEPENDENT_ROOT_SLOT",
9
+ INVALID_DEPENDENT_ROOT = "PROPOSER_PREFERENCES_ERROR_INVALID_DEPENDENT_ROOT",
8
10
  INVALID_PROPOSER = "PROPOSER_PREFERENCES_ERROR_INVALID_PROPOSER",
9
11
  ALREADY_KNOWN = "PROPOSER_PREFERENCES_ERROR_ALREADY_KNOWN",
10
12
  INVALID_SIGNATURE = "PROPOSER_PREFERENCES_ERROR_INVALID_SIGNATURE",
@@ -26,6 +28,19 @@ export type ProposerPreferencesErrorType =
26
28
  proposalSlot: Slot;
27
29
  dependentRoot: RootHex;
28
30
  }
31
+ | {
32
+ code: ProposerPreferencesErrorCode.INVALID_DEPENDENT_ROOT_SLOT;
33
+ dependentRoot: RootHex;
34
+ dependentBlockSlot: Slot;
35
+ dependentRootSlot: Slot;
36
+ dependentEpoch: Epoch;
37
+ }
38
+ | {
39
+ code: ProposerPreferencesErrorCode.INVALID_DEPENDENT_ROOT;
40
+ dependentRoot: RootHex;
41
+ dependentBlockSlot: Slot;
42
+ dependentEpoch: Epoch;
43
+ }
29
44
  | {
30
45
  code: ProposerPreferencesErrorCode.INVALID_PROPOSER;
31
46
  proposalSlot: Slot;
@@ -10,9 +10,8 @@ import {
10
10
  MIN_SYNC_COMMITTEE_PARTICIPANTS,
11
11
  SLOTS_PER_EPOCH,
12
12
  SYNC_COMMITTEE_SIZE,
13
- forkPostAltair,
14
- highestFork,
15
13
  isForkPostElectra,
14
+ isForkPostGloas,
16
15
  } from "@lodestar/params";
17
16
  import {
18
17
  type IBeaconStateViewAltair,
@@ -44,7 +43,6 @@ import {
44
43
  electra,
45
44
  phase0,
46
45
  ssz,
47
- sszTypesFor,
48
46
  } from "@lodestar/types";
49
47
  import {Logger, MapDef, byteArrayEquals, pruneSetToMax, toRootHex} from "@lodestar/utils";
50
48
  import {ZERO_HASH} from "../../constants/index.js";
@@ -230,8 +228,8 @@ export class LightClientServer {
230
228
  this.signal = signal;
231
229
 
232
230
  this.zero = {
233
- // Assign the hightest fork's default value because it can always be typecasted down to correct fork
234
- finalizedHeader: sszTypesFor(highestFork(forkPostAltair)).LightClientHeader.defaultValue(),
231
+ // Assign the highest pre-Gloas light-client header because post-Gloas light-client updates are skipped for now.
232
+ finalizedHeader: ssz.electra.LightClientHeader.defaultValue(),
235
233
  // Electra finalityBranch has fixed length of 5 whereas altair has 4. The fifth element will be ignored
236
234
  // when serializing as altair LightClientUpdate
237
235
  finalityBranch: ssz.electra.LightClientUpdate.fields.finalityBranch.defaultValue(),
@@ -658,10 +656,17 @@ export class LightClientServer {
658
656
 
659
657
  const attestedFork = this.config.getForkName(attestedHeader.beacon.slot);
660
658
  const numWitness = syncCommitteeWitness.witness.length;
661
- if (isForkPostElectra(attestedFork) && numWitness !== NUM_WITNESS_ELECTRA) {
659
+ if (
660
+ isForkPostGloas(attestedFork) &&
661
+ (syncCommitteeWitness.currentSyncCommitteeBranch === undefined ||
662
+ syncCommitteeWitness.nextSyncCommitteeBranch === undefined)
663
+ ) {
664
+ throw Error("Expected post-Gloas sync committee branches");
665
+ }
666
+ if (!isForkPostGloas(attestedFork) && isForkPostElectra(attestedFork) && numWitness !== NUM_WITNESS_ELECTRA) {
662
667
  throw Error(`Expected ${NUM_WITNESS_ELECTRA} witnesses in post-Electra numWitness=${numWitness}`);
663
668
  }
664
- if (!isForkPostElectra(attestedFork) && numWitness !== NUM_WITNESS) {
669
+ if (!isForkPostGloas(attestedFork) && !isForkPostElectra(attestedFork) && numWitness !== NUM_WITNESS) {
665
670
  throw Error(`Expected ${NUM_WITNESS} witnesses in pre-Electra numWitness=${numWitness}`);
666
671
  }
667
672
 
@@ -1,11 +1,15 @@
1
1
  import {Tree} from "@chainsafe/persistent-merkle-tree";
2
2
  import {
3
3
  BLOCK_BODY_EXECUTION_PAYLOAD_GINDEX,
4
+ CURRENT_SYNC_COMMITTEE_GINDEX_GLOAS,
4
5
  FINALIZED_ROOT_GINDEX,
5
6
  FINALIZED_ROOT_GINDEX_ELECTRA,
7
+ FINALIZED_ROOT_GINDEX_GLOAS,
6
8
  ForkName,
7
9
  ForkPostBellatrix,
10
+ NEXT_SYNC_COMMITTEE_GINDEX_GLOAS,
8
11
  isForkPostElectra,
12
+ isForkPostGloas,
9
13
  } from "@lodestar/params";
10
14
  import {BeaconStateAllForks, CachedBeaconStateAllForks} from "@lodestar/state-transition";
11
15
  import {BeaconBlockBody, SSZTypesFor, ssz} from "@lodestar/types";
@@ -17,6 +21,24 @@ export function getSyncCommitteesWitness(fork: ForkName, state: BeaconStateAllFo
17
21
  let currentSyncCommitteeRoot: Uint8Array;
18
22
  let nextSyncCommitteeRoot: Uint8Array;
19
23
 
24
+ if (isForkPostGloas(fork)) {
25
+ const tree = new Tree(state.node);
26
+ const currentSyncCommitteeGindex = BigInt(CURRENT_SYNC_COMMITTEE_GINDEX_GLOAS);
27
+ const nextSyncCommitteeGindex = BigInt(NEXT_SYNC_COMMITTEE_GINDEX_GLOAS);
28
+
29
+ currentSyncCommitteeRoot = tree.getRoot(currentSyncCommitteeGindex);
30
+ nextSyncCommitteeRoot = tree.getRoot(nextSyncCommitteeGindex);
31
+ witness = [];
32
+
33
+ return {
34
+ witness,
35
+ currentSyncCommitteeRoot,
36
+ nextSyncCommitteeRoot,
37
+ currentSyncCommitteeBranch: tree.getSingleProof(currentSyncCommitteeGindex),
38
+ nextSyncCommitteeBranch: tree.getSingleProof(nextSyncCommitteeGindex),
39
+ };
40
+ }
41
+
20
42
  if (isForkPostElectra(fork)) {
21
43
  const n2 = n1.left;
22
44
  const n5 = n2.right;
@@ -60,17 +82,30 @@ export function getSyncCommitteesWitness(fork: ForkName, state: BeaconStateAllFo
60
82
  }
61
83
 
62
84
  export function getNextSyncCommitteeBranch(syncCommitteesWitness: SyncCommitteeWitness): Uint8Array[] {
85
+ if (syncCommitteesWitness.nextSyncCommitteeBranch) {
86
+ return syncCommitteesWitness.nextSyncCommitteeBranch;
87
+ }
88
+
63
89
  // Witness branch is sorted by descending gindex
64
90
  return [syncCommitteesWitness.currentSyncCommitteeRoot, ...syncCommitteesWitness.witness];
65
91
  }
66
92
 
67
93
  export function getCurrentSyncCommitteeBranch(syncCommitteesWitness: SyncCommitteeWitness): Uint8Array[] {
94
+ if (syncCommitteesWitness.currentSyncCommitteeBranch) {
95
+ return syncCommitteesWitness.currentSyncCommitteeBranch;
96
+ }
97
+
68
98
  // Witness branch is sorted by descending gindex
69
99
  return [syncCommitteesWitness.nextSyncCommitteeRoot, ...syncCommitteesWitness.witness];
70
100
  }
71
101
 
72
102
  export function getFinalizedRootProof(state: CachedBeaconStateAllForks): Uint8Array[] {
73
- const finalizedRootGindex = state.epochCtx.isPostElectra() ? FINALIZED_ROOT_GINDEX_ELECTRA : FINALIZED_ROOT_GINDEX;
103
+ const fork = state.config.getForkName(state.slot);
104
+ const finalizedRootGindex = isForkPostGloas(fork)
105
+ ? FINALIZED_ROOT_GINDEX_GLOAS
106
+ : state.epochCtx.isPostElectra()
107
+ ? FINALIZED_ROOT_GINDEX_ELECTRA
108
+ : FINALIZED_ROOT_GINDEX;
74
109
  return new Tree(state.node).getSingleProof(BigInt(finalizedRootGindex));
75
110
  }
76
111
 
@@ -26,8 +26,10 @@
26
26
  * ```
27
27
  */
28
28
  export type SyncCommitteeWitness = {
29
- /** Vector[Bytes32, 4] or Vector[Bytes32, 5] depending on the fork */
29
+ /** Shared witness for pre-Gloas forks where current and next sync committees are siblings. */
30
30
  witness: Uint8Array[];
31
31
  currentSyncCommitteeRoot: Uint8Array;
32
32
  nextSyncCommitteeRoot: Uint8Array;
33
+ currentSyncCommitteeBranch?: Uint8Array[];
34
+ nextSyncCommitteeBranch?: Uint8Array[];
33
35
  };
@@ -234,6 +234,7 @@ export class AggregatedAttestationPool {
234
234
  const stateEpoch = state.epoch;
235
235
  const statePrevEpoch = stateEpoch - 1;
236
236
  const rootCache = new RootCache(state);
237
+ const gloasState = isStatePostGloas(state) ? state : null;
237
238
 
238
239
  const notSeenValidatorsFn = getNotSeenValidatorsFn(this.config, shufflingCache, state);
239
240
  const validateAttestationDataFn = getValidateAttestationDataFn(forkChoice, state);
@@ -355,13 +356,14 @@ export class AggregatedAttestationPool {
355
356
  // after all committees are processed, we have a list of sameAttDataCons
356
357
  for (const consolidation of sameAttDataCons) {
357
358
  // Score attestations by profitability to maximize proposer reward
358
- const flags = getAttestationParticipationStatus(
359
+ const {flags} = getAttestationParticipationStatus(
359
360
  ForkSeq[fork],
360
361
  consolidation.attData,
361
362
  inclusionDistance,
362
363
  stateEpoch,
363
364
  rootCache,
364
- isStatePostGloas(state) ? state.executionPayloadAvailability : null
365
+ gloasState?.executionPayloadAvailability ?? null,
366
+ gloasState?.latestExecutionPayloadBid.slot ?? null
365
367
  );
366
368
 
367
369
  const weight =
@@ -17,7 +17,7 @@ import {toRootHex} from "@lodestar/utils";
17
17
  export class ProposerPreferencesPool {
18
18
  private readonly bySlot = new Map<Slot, Map<RootHex, gloas.SignedProposerPreferences>>();
19
19
 
20
- /** Lookup for bid validation: matches `(bid.slot, get_proposer_dependent_root(parent_state, ...))`. */
20
+ /** Lookup for bid validation: matches `(bid.slot, get_shuffling_dependent_root(store, bid.parent_block_root, epoch))`. */
21
21
  get(slot: Slot, dependentRootHex: RootHex): gloas.SignedProposerPreferences | null {
22
22
  return this.bySlot.get(slot)?.get(dependentRootHex) ?? null;
23
23
  }
@@ -173,7 +173,7 @@ export class PrepareNextSlotScheduler {
173
173
  // Apply parent payload once here as it's reused by EL prep and SSE emit below
174
174
  let stateAfterParentPayload: IBeaconStateViewBellatrix = updatedPrepareState;
175
175
  if (isStatePostGloas(updatedPrepareState)) {
176
- // Spec: should_build_on_full(store, head) — see produceBlockBody.ts for context.
176
+ // Spec: should_build_on_full(store, head, slot) - see produceBlockBody.ts for context.
177
177
  if (this.chain.forkChoice.shouldBuildOnFull(updatedHead, prepareSlot)) {
178
178
  parentBlockHash = updatedPrepareState.latestExecutionPayloadBid.blockHash;
179
179
  // Skip applying parent payload unless we're proposing the next slot or have to emit payload_attributes events
@@ -276,7 +276,7 @@ export async function produceBlockBody<T extends BlockType>(
276
276
  let parentExecutionRequests: gloas.ExecutionRequests;
277
277
  // Apply parent payload once here as it's reused by EL prep and voluntary exit filtering below
278
278
  let stateAfterParentPayload: IBeaconStateViewBellatrix = currentState;
279
- // Spec: should_build_on_full(store, head). `parentBlock` is the proposer's head
279
+ // Spec: should_build_on_full(store, head, slot). `parentBlock` is the proposer's head
280
280
  // (set by chain.getProposerHead(slot)). Returns false when the PTC majority signalled
281
281
  // the blob data is not available or the payload was not timely, forcing a build on EMPTY (reorg).
282
282
  const isBuildingOnFull = this.forkChoice.shouldBuildOnFull(parentBlock, blockSlot);
@@ -345,11 +345,11 @@ export async function produceBlockBody<T extends BlockType>(
345
345
  blockHash: executionPayload.blockHash,
346
346
  prevRandao: currentState.getRandaoMix(currentState.epoch),
347
347
  feeRecipient: executionPayload.feeRecipient,
348
- gasLimit: executionPayload.gasLimit,
348
+ gasLimit: BigInt(executionPayload.gasLimit),
349
349
  builderIndex: BUILDER_INDEX_SELF_BUILD,
350
350
  slot: blockSlot,
351
351
  value: 0,
352
- executionPayment: 0,
352
+ executionPayment: 0n,
353
353
  blobKzgCommitments: blobsBundle.commitments,
354
354
  executionRequestsRoot: ssz.gloas.ExecutionRequests.hashTreeRoot(executionRequests as gloas.ExecutionRequests),
355
355
  };
@@ -949,7 +949,7 @@ function getProposerTargetGasLimit(
949
949
  prepareSlot: Slot,
950
950
  parentBlockRoot: Root,
951
951
  parentBlockHash: Bytes32
952
- ): number {
952
+ ): bigint {
953
953
  const parentBlockRootHex = toRootHex(parentBlockRoot);
954
954
  const parentBlock = chain.forkChoice.getBlockHexDefaultStatus(parentBlockRootHex);
955
955
  const dependentRootHex = (() => {
@@ -979,7 +979,7 @@ function getProposerTargetGasLimit(
979
979
  `Cannot resolve parent payload gas_limit for proposer targetGasLimit fallback parentBlockRoot=${parentBlockRootHex} parentBlockHash=${toRootHex(parentBlockHash)}`
980
980
  );
981
981
  }
982
- return parentPayloadVariant.executionPayloadGasLimit;
982
+ return BigInt(parentPayloadVariant.executionPayloadGasLimit);
983
983
  }
984
984
 
985
985
  export async function produceCommonBlockBody<T extends BlockType>(
@@ -1,4 +1,4 @@
1
- import {BuilderIndex, Slot} from "@lodestar/types";
1
+ import {BuilderIndex, RootHex, Slot} from "@lodestar/types";
2
2
  import {MapDef} from "@lodestar/utils";
3
3
 
4
4
  /**
@@ -7,29 +7,42 @@ import {MapDef} from "@lodestar/utils";
7
7
  const SLOTS_RETAINED = 2;
8
8
 
9
9
  /**
10
- * Tracks execution payload bids we've already seen per (slot, builder).
10
+ * Tracks execution payload bids we've already seen per
11
+ * (slot, builder, parent block hash, parent block root).
11
12
  */
12
13
  export class SeenExecutionPayloadBids {
13
- private readonly builderIndexesBySlot = new MapDef<Slot, Set<BuilderIndex>>(() => new Set<BuilderIndex>());
14
+ private readonly branchesByBuilderBySlot = new MapDef<Slot, MapDef<BuilderIndex, Set<string>>>(
15
+ () => new MapDef<BuilderIndex, Set<string>>(() => new Set<string>())
16
+ );
14
17
  private lowestPermissibleSlot: Slot = 0;
15
18
 
16
- isKnown(slot: Slot, builderIndex: BuilderIndex): boolean {
17
- return this.builderIndexesBySlot.get(slot)?.has(builderIndex) === true;
19
+ isKnown(slot: Slot, builderIndex: BuilderIndex, parentBlockHash: RootHex, parentBlockRoot: RootHex): boolean {
20
+ return (
21
+ this.branchesByBuilderBySlot.get(slot)?.get(builderIndex)?.has(branchKey(parentBlockHash, parentBlockRoot)) ===
22
+ true
23
+ );
18
24
  }
19
25
 
20
- add(slot: Slot, builderIndex: BuilderIndex): void {
26
+ add(slot: Slot, builderIndex: BuilderIndex, parentBlockHash: RootHex, parentBlockRoot: RootHex): void {
21
27
  if (slot < this.lowestPermissibleSlot) {
22
28
  throw Error(`slot ${slot} < lowestPermissibleSlot ${this.lowestPermissibleSlot}`);
23
29
  }
24
- this.builderIndexesBySlot.getOrDefault(slot).add(builderIndex);
30
+ this.branchesByBuilderBySlot
31
+ .getOrDefault(slot)
32
+ .getOrDefault(builderIndex)
33
+ .add(branchKey(parentBlockHash, parentBlockRoot));
25
34
  }
26
35
 
27
36
  prune(currentSlot: Slot): void {
28
37
  this.lowestPermissibleSlot = Math.max(currentSlot - SLOTS_RETAINED, 0);
29
- for (const slot of this.builderIndexesBySlot.keys()) {
38
+ for (const slot of this.branchesByBuilderBySlot.keys()) {
30
39
  if (slot < this.lowestPermissibleSlot) {
31
- this.builderIndexesBySlot.delete(slot);
40
+ this.branchesByBuilderBySlot.delete(slot);
32
41
  }
33
42
  }
34
43
  }
35
44
  }
45
+
46
+ function branchKey(parentBlockHash: RootHex, parentBlockRoot: RootHex): string {
47
+ return `${parentBlockHash}:${parentBlockRoot}`;
48
+ }
@@ -90,13 +90,19 @@ async function validateAggregateAndProof(
90
90
  });
91
91
  }
92
92
 
93
- // [REJECT] If `aggregate.data.index == 1` (payload present for a past
94
- // block), the execution payload for `block` passes validation.
93
+ // [REJECT] If `aggregate.data.index == 1` (payload present for a past block)
94
+ // the corresponding execution payload for `block` passes validation.
95
95
  // [IGNORE] When `aggregate.data.index == 1` (payload present for a past block),
96
- // the corresponding execution payload for `block` has been seen (a client MAY queue
97
- // attestations for processing once the payload is retrieved and SHOULD request the
98
- // payload envelope via `ExecutionPayloadEnvelopesByRoot`).
99
- if (block !== null && attData.index === 1 && !chain.seenPayloadEnvelope(toRootHex(attData.beaconBlockRoot))) {
96
+ // the corresponding execution payload for `block` has been fully imported, including its
97
+ // data -- i.e. `is_payload_verified(store, aggregate.data.beacon_block_root)` returns `True`
98
+ // (a client MAY queue attestations for processing until the payload is imported and SHOULD
99
+ // request the payload envelope via `ExecutionPayloadEnvelopesByRoot` using
100
+ // `aggregate.data.beacon_block_root`).
101
+ if (
102
+ block !== null &&
103
+ attData.index === 1 &&
104
+ !chain.forkChoice.hasPayloadHexUnsafe(toRootHex(attData.beaconBlockRoot))
105
+ ) {
100
106
  throw new AttestationError(GossipAction.IGNORE, {
101
107
  code: AttestationErrorCode.EXECUTION_PAYLOAD_NOT_SEEN,
102
108
  beaconBlockRoot: toRootHex(attData.beaconBlockRoot),
@@ -316,13 +316,19 @@ async function validateAttestationNoSignatureCheck(
316
316
  });
317
317
  }
318
318
 
319
- // [REJECT] If `attestation.data.index == 1` (payload present for a past
320
- // block), the execution payload for `block` passes validation.
319
+ // [REJECT] If `attestation.data.index == 1` (payload present for a past block),
320
+ // the execution payload for `block` passes validation.
321
321
  // [IGNORE] When `attestation.data.index == 1` (payload present for a past block),
322
- // the corresponding execution payload for `block` has been seen (a client MAY queue
323
- // attestations for processing once the payload is retrieved and SHOULD request the
324
- // payload envelope via `ExecutionPayloadEnvelopesByRoot`).
325
- if (block !== null && attData.index === 1 && !chain.seenPayloadEnvelope(toRootHex(attData.beaconBlockRoot))) {
322
+ // the execution payload for `block` has been fully imported, including its data -- i.e.
323
+ // `is_payload_verified(store, attestation.data.beacon_block_root)` returns `True`
324
+ // (a client MAY queue attestations for processing until the payload is imported and
325
+ // SHOULD request the payload envelope via `ExecutionPayloadEnvelopesByRoot` using
326
+ // `attestation.data.beacon_block_root`).
327
+ if (
328
+ block !== null &&
329
+ attData.index === 1 &&
330
+ !chain.forkChoice.hasPayloadHexUnsafe(toRootHex(attData.beaconBlockRoot))
331
+ ) {
326
332
  throw new AttestationError(GossipAction.IGNORE, {
327
333
  code: AttestationErrorCode.EXECUTION_PAYLOAD_NOT_SEEN,
328
334
  beaconBlockRoot: toRootHex(attData.beaconBlockRoot),
@@ -111,7 +111,7 @@ export async function validateGossipBlobSidecar(
111
111
 
112
112
  // [REJECT] The blob is from a higher slot than its parent.
113
113
  if (parentBlock.slot >= blobSlot) {
114
- throw new BlobSidecarGossipError(GossipAction.IGNORE, {
114
+ throw new BlobSidecarGossipError(GossipAction.REJECT, {
115
115
  code: BlobSidecarErrorCode.NOT_LATER_THAN_PARENT,
116
116
  parentSlot: parentBlock.slot,
117
117
  slot: blobSlot,