@lodestar/beacon-node 1.45.0-dev.9d930bdc5b → 1.45.0-dev.aadf7a7167

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 (214) hide show
  1. package/lib/api/impl/beacon/index.d.ts +1 -1
  2. package/lib/api/impl/beacon/index.d.ts.map +1 -1
  3. package/lib/api/impl/beacon/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 +6 -43
  6. package/lib/api/impl/beacon/pool/index.js.map +1 -1
  7. package/lib/api/impl/beacon/state/index.d.ts +1 -1
  8. package/lib/api/impl/beacon/state/index.d.ts.map +1 -1
  9. package/lib/api/impl/beacon/state/index.js +48 -5
  10. package/lib/api/impl/beacon/state/index.js.map +1 -1
  11. package/lib/api/impl/beacon/state/utils.d.ts +13 -3
  12. package/lib/api/impl/beacon/state/utils.d.ts.map +1 -1
  13. package/lib/api/impl/beacon/state/utils.js +48 -4
  14. package/lib/api/impl/beacon/state/utils.js.map +1 -1
  15. package/lib/api/impl/debug/index.d.ts +1 -1
  16. package/lib/api/impl/debug/index.d.ts.map +1 -1
  17. package/lib/api/impl/debug/index.js +2 -2
  18. package/lib/api/impl/debug/index.js.map +1 -1
  19. package/lib/api/impl/lodestar/index.js +2 -2
  20. package/lib/api/impl/lodestar/index.js.map +1 -1
  21. package/lib/api/impl/proof/index.d.ts +1 -1
  22. package/lib/api/impl/proof/index.d.ts.map +1 -1
  23. package/lib/api/impl/proof/index.js +2 -2
  24. package/lib/api/impl/proof/index.js.map +1 -1
  25. package/lib/api/impl/utils.d.ts +23 -0
  26. package/lib/api/impl/utils.d.ts.map +1 -1
  27. package/lib/api/impl/utils.js +44 -1
  28. package/lib/api/impl/utils.js.map +1 -1
  29. package/lib/api/impl/validator/index.d.ts +0 -14
  30. package/lib/api/impl/validator/index.d.ts.map +1 -1
  31. package/lib/api/impl/validator/index.js +67 -62
  32. package/lib/api/impl/validator/index.js.map +1 -1
  33. package/lib/api/rest/base.d.ts.map +1 -1
  34. package/lib/api/rest/base.js +8 -0
  35. package/lib/api/rest/base.js.map +1 -1
  36. package/lib/chain/blocks/importBlock.d.ts.map +1 -1
  37. package/lib/chain/blocks/importBlock.js +2 -5
  38. package/lib/chain/blocks/importBlock.js.map +1 -1
  39. package/lib/chain/blocks/verifyExecutionPayloadEnvelope.js +2 -2
  40. package/lib/chain/blocks/verifyExecutionPayloadEnvelope.js.map +1 -1
  41. package/lib/chain/builderCircuitBreaker.d.ts +31 -0
  42. package/lib/chain/builderCircuitBreaker.d.ts.map +1 -0
  43. package/lib/chain/builderCircuitBreaker.js +59 -0
  44. package/lib/chain/builderCircuitBreaker.js.map +1 -0
  45. package/lib/chain/chain.d.ts +3 -2
  46. package/lib/chain/chain.d.ts.map +1 -1
  47. package/lib/chain/chain.js +4 -3
  48. package/lib/chain/chain.js.map +1 -1
  49. package/lib/chain/errors/blockError.d.ts +7 -0
  50. package/lib/chain/errors/blockError.d.ts.map +1 -1
  51. package/lib/chain/errors/blockError.js +2 -0
  52. package/lib/chain/errors/blockError.js.map +1 -1
  53. package/lib/chain/errors/executionPayloadEnvelope.d.ts +11 -0
  54. package/lib/chain/errors/executionPayloadEnvelope.d.ts.map +1 -1
  55. package/lib/chain/errors/executionPayloadEnvelope.js +2 -0
  56. package/lib/chain/errors/executionPayloadEnvelope.js.map +1 -1
  57. package/lib/chain/interface.d.ts +3 -2
  58. package/lib/chain/interface.d.ts.map +1 -1
  59. package/lib/chain/interface.js.map +1 -1
  60. package/lib/chain/lightClient/index.d.ts.map +1 -1
  61. package/lib/chain/lightClient/index.js +11 -6
  62. package/lib/chain/lightClient/index.js.map +1 -1
  63. package/lib/chain/lightClient/proofs.d.ts.map +1 -1
  64. package/lib/chain/lightClient/proofs.js +28 -2
  65. package/lib/chain/lightClient/proofs.js.map +1 -1
  66. package/lib/chain/lightClient/types.d.ts +3 -1
  67. package/lib/chain/lightClient/types.d.ts.map +1 -1
  68. package/lib/chain/opPools/aggregatedAttestationPool.js +1 -1
  69. package/lib/chain/opPools/aggregatedAttestationPool.js.map +1 -1
  70. package/lib/chain/opPools/proposerPreferencesPool.d.ts +2 -1
  71. package/lib/chain/opPools/proposerPreferencesPool.d.ts.map +1 -1
  72. package/lib/chain/opPools/proposerPreferencesPool.js +3 -0
  73. package/lib/chain/opPools/proposerPreferencesPool.js.map +1 -1
  74. package/lib/chain/options.d.ts +5 -0
  75. package/lib/chain/options.d.ts.map +1 -1
  76. package/lib/chain/options.js +1 -0
  77. package/lib/chain/options.js.map +1 -1
  78. package/lib/chain/prepareNextSlot.d.ts.map +1 -1
  79. package/lib/chain/prepareNextSlot.js +12 -7
  80. package/lib/chain/prepareNextSlot.js.map +1 -1
  81. package/lib/chain/produceBlock/produceBlockBody.d.ts.map +1 -1
  82. package/lib/chain/produceBlock/produceBlockBody.js +4 -12
  83. package/lib/chain/produceBlock/produceBlockBody.js.map +1 -1
  84. package/lib/chain/seenCache/index.d.ts +0 -1
  85. package/lib/chain/seenCache/index.d.ts.map +1 -1
  86. package/lib/chain/seenCache/index.js +0 -1
  87. package/lib/chain/seenCache/index.js.map +1 -1
  88. package/lib/chain/stateCache/persistentCheckpointsCache.d.ts.map +1 -1
  89. package/lib/chain/stateCache/persistentCheckpointsCache.js +3 -2
  90. package/lib/chain/stateCache/persistentCheckpointsCache.js.map +1 -1
  91. package/lib/chain/validation/block.d.ts.map +1 -1
  92. package/lib/chain/validation/block.js +52 -1
  93. package/lib/chain/validation/block.js.map +1 -1
  94. package/lib/chain/validation/executionPayloadBid.js +11 -10
  95. package/lib/chain/validation/executionPayloadBid.js.map +1 -1
  96. package/lib/chain/validation/executionPayloadEnvelope.d.ts.map +1 -1
  97. package/lib/chain/validation/executionPayloadEnvelope.js +30 -0
  98. package/lib/chain/validation/executionPayloadEnvelope.js.map +1 -1
  99. package/lib/chain/validation/proposerPreferences.d.ts.map +1 -1
  100. package/lib/chain/validation/proposerPreferences.js +14 -5
  101. package/lib/chain/validation/proposerPreferences.js.map +1 -1
  102. package/lib/chain/validation/signatureSets/aggregateAndProof.d.ts.map +1 -1
  103. package/lib/chain/validation/signatureSets/aggregateAndProof.js +2 -3
  104. package/lib/chain/validation/signatureSets/aggregateAndProof.js.map +1 -1
  105. package/lib/db/repositories/lightclientSyncCommitteeWitness.d.ts.map +1 -1
  106. package/lib/db/repositories/lightclientSyncCommitteeWitness.js +42 -7
  107. package/lib/db/repositories/lightclientSyncCommitteeWitness.js.map +1 -1
  108. package/lib/execution/builder/http.d.ts +18 -0
  109. package/lib/execution/builder/http.d.ts.map +1 -1
  110. package/lib/execution/builder/http.js +20 -11
  111. package/lib/execution/builder/http.js.map +1 -1
  112. package/lib/metrics/metrics/beacon.d.ts +6 -0
  113. package/lib/metrics/metrics/beacon.d.ts.map +1 -1
  114. package/lib/metrics/metrics/beacon.js +18 -0
  115. package/lib/metrics/metrics/beacon.js.map +1 -1
  116. package/lib/network/core/metrics.d.ts +65 -0
  117. package/lib/network/core/metrics.d.ts.map +1 -1
  118. package/lib/network/core/metrics.js +94 -0
  119. package/lib/network/core/metrics.js.map +1 -1
  120. package/lib/network/discv5/utils.d.ts.map +1 -1
  121. package/lib/network/discv5/utils.js +12 -3
  122. package/lib/network/discv5/utils.js.map +1 -1
  123. package/lib/network/gossip/encoding.d.ts +2 -2
  124. package/lib/network/gossip/encoding.d.ts.map +1 -1
  125. package/lib/network/gossip/encoding.js +22 -9
  126. package/lib/network/gossip/encoding.js.map +1 -1
  127. package/lib/network/gossip/gossipsub.d.ts.map +1 -1
  128. package/lib/network/gossip/gossipsub.js +4 -3
  129. package/lib/network/gossip/gossipsub.js.map +1 -1
  130. package/lib/network/gossip/topic.d.ts +831 -226
  131. package/lib/network/gossip/topic.d.ts.map +1 -1
  132. package/lib/network/gossip/topic.js +47 -4
  133. package/lib/network/gossip/topic.js.map +1 -1
  134. package/lib/network/peers/peerManager.d.ts.map +1 -1
  135. package/lib/network/peers/peerManager.js +10 -0
  136. package/lib/network/peers/peerManager.js.map +1 -1
  137. package/lib/network/peers/score/score.d.ts +3 -1
  138. package/lib/network/peers/score/score.d.ts.map +1 -1
  139. package/lib/network/peers/score/score.js +6 -1
  140. package/lib/network/peers/score/score.js.map +1 -1
  141. package/lib/network/peers/score/store.d.ts.map +1 -1
  142. package/lib/network/peers/score/store.js +6 -1
  143. package/lib/network/peers/score/store.js.map +1 -1
  144. package/lib/network/peers/utils/prioritizePeers.d.ts.map +1 -1
  145. package/lib/network/peers/utils/prioritizePeers.js +14 -0
  146. package/lib/network/peers/utils/prioritizePeers.js.map +1 -1
  147. package/lib/network/processor/gossipHandlers.d.ts.map +1 -1
  148. package/lib/network/processor/gossipHandlers.js +6 -3
  149. package/lib/network/processor/gossipHandlers.js.map +1 -1
  150. package/lib/sync/range/chain.d.ts +4 -1
  151. package/lib/sync/range/chain.d.ts.map +1 -1
  152. package/lib/sync/range/chain.js +3 -3
  153. package/lib/sync/range/chain.js.map +1 -1
  154. package/lib/sync/range/range.d.ts.map +1 -1
  155. package/lib/sync/range/range.js +10 -0
  156. package/lib/sync/range/range.js.map +1 -1
  157. package/lib/util/graffiti.d.ts +15 -0
  158. package/lib/util/graffiti.d.ts.map +1 -1
  159. package/lib/util/graffiti.js +55 -0
  160. package/lib/util/graffiti.js.map +1 -1
  161. package/package.json +19 -19
  162. package/src/api/impl/beacon/index.ts +1 -1
  163. package/src/api/impl/beacon/pool/index.ts +5 -53
  164. package/src/api/impl/beacon/state/index.ts +55 -3
  165. package/src/api/impl/beacon/state/utils.ts +68 -4
  166. package/src/api/impl/debug/index.ts +3 -2
  167. package/src/api/impl/lodestar/index.ts +2 -2
  168. package/src/api/impl/proof/index.ts +2 -2
  169. package/src/api/impl/utils.ts +51 -1
  170. package/src/api/impl/validator/index.ts +75 -67
  171. package/src/api/rest/base.ts +9 -0
  172. package/src/chain/blocks/importBlock.ts +2 -7
  173. package/src/chain/blocks/verifyExecutionPayloadEnvelope.ts +2 -2
  174. package/src/chain/builderCircuitBreaker.ts +84 -0
  175. package/src/chain/chain.ts +7 -3
  176. package/src/chain/errors/blockError.ts +3 -0
  177. package/src/chain/errors/executionPayloadEnvelope.ts +9 -0
  178. package/src/chain/interface.ts +2 -2
  179. package/src/chain/lightClient/index.ts +12 -7
  180. package/src/chain/lightClient/proofs.ts +36 -1
  181. package/src/chain/lightClient/types.ts +3 -1
  182. package/src/chain/opPools/aggregatedAttestationPool.ts +1 -1
  183. package/src/chain/opPools/proposerPreferencesPool.ts +5 -1
  184. package/src/chain/options.ts +6 -0
  185. package/src/chain/prepareNextSlot.ts +11 -7
  186. package/src/chain/produceBlock/produceBlockBody.ts +9 -17
  187. package/src/chain/seenCache/index.ts +0 -1
  188. package/src/chain/stateCache/persistentCheckpointsCache.ts +3 -2
  189. package/src/chain/validation/block.ts +69 -1
  190. package/src/chain/validation/executionPayloadBid.ts +14 -9
  191. package/src/chain/validation/executionPayloadEnvelope.ts +39 -0
  192. package/src/chain/validation/proposerPreferences.ts +15 -5
  193. package/src/chain/validation/signatureSets/aggregateAndProof.ts +3 -3
  194. package/src/db/repositories/lightclientSyncCommitteeWitness.ts +57 -9
  195. package/src/execution/builder/http.ts +30 -17
  196. package/src/metrics/metrics/beacon.ts +19 -0
  197. package/src/network/core/metrics.ts +94 -0
  198. package/src/network/discv5/utils.ts +11 -3
  199. package/src/network/gossip/encoding.ts +20 -7
  200. package/src/network/gossip/gossipsub.ts +9 -3
  201. package/src/network/gossip/topic.ts +59 -4
  202. package/src/network/peers/peerManager.ts +11 -0
  203. package/src/network/peers/score/score.ts +8 -1
  204. package/src/network/peers/score/store.ts +8 -1
  205. package/src/network/peers/utils/prioritizePeers.ts +15 -0
  206. package/src/network/processor/gossipHandlers.ts +6 -2
  207. package/src/sync/range/chain.ts +4 -5
  208. package/src/sync/range/range.ts +10 -0
  209. package/src/util/graffiti.ts +78 -0
  210. package/lib/chain/seenCache/seenProposerPreferences.d.ts +0 -16
  211. package/lib/chain/seenCache/seenProposerPreferences.d.ts.map +0 -1
  212. package/lib/chain/seenCache/seenProposerPreferences.js +0 -26
  213. package/lib/chain/seenCache/seenProposerPreferences.js.map +0 -1
  214. package/src/chain/seenCache/seenProposerPreferences.ts +0 -32
@@ -65,6 +65,8 @@ import {
65
65
  AttestationError,
66
66
  AttestationErrorCode,
67
67
  GossipAction,
68
+ ProposerPreferencesError,
69
+ ProposerPreferencesErrorCode,
68
70
  SyncCommitteeError,
69
71
  SyncCommitteeErrorCode,
70
72
  } from "../../../chain/errors/index.js";
@@ -74,37 +76,23 @@ import {BlockType, ProduceFullDeneb, ProduceFullGloas} from "../../../chain/prod
74
76
  import {RegenCaller} from "../../../chain/regen/index.js";
75
77
  import {CheckpointHex} from "../../../chain/stateCache/types.js";
76
78
  import {validateApiAggregateAndProof} from "../../../chain/validation/index.js";
79
+ import {validateGossipProposerPreferences} from "../../../chain/validation/proposerPreferences.js";
77
80
  import {validateSyncCommitteeGossipContributionAndProof} from "../../../chain/validation/syncCommitteeContributionAndProof.js";
78
81
  import {ZERO_HASH} from "../../../constants/index.js";
79
82
  import {BuilderStatus, NoBidReceived} from "../../../execution/builder/http.js";
80
83
  import {validateGossipFnRetryUnknownRoot} from "../../../network/processor/gossipHandlers.js";
81
84
  import {CommitteeSubscription} from "../../../network/subnets/index.js";
82
- import {SyncState} from "../../../sync/index.js";
83
85
  import {callInNextEventLoop} from "../../../util/eventLoop.js";
84
86
  import {isOptimisticBlock} from "../../../util/forkChoice.js";
85
- import {getDefaultGraffiti, toGraffitiBytes} from "../../../util/graffiti.js";
87
+ import {getBlockGraffiti, toGraffitiBytes} from "../../../util/graffiti.js";
86
88
  import {getLodestarClientVersion} from "../../../util/metadata.js";
87
89
  import {ApiOptions} from "../../options.js";
88
90
  import {getStateResponseWithRegen} from "../beacon/state/utils.js";
89
91
  import {ApiError, FailureList, IndexedError, NodeIsSyncing, OnlySupportedByDVT} from "../errors.js";
90
92
  import {ApiModules} from "../types.js";
93
+ import {notWhileSyncing} from "../utils.js";
91
94
  import {computeSubnetForCommitteesAtSlot, getPubkeysForIndices, selectBlockProductionSource} from "./utils.js";
92
95
 
93
- /**
94
- * If the node is within this many epochs from the head, we declare it to be synced regardless of
95
- * the network sync state.
96
- *
97
- * This helps prevent attacks where nodes can convince us that we're syncing some non-existent
98
- * finalized head.
99
- *
100
- * TODO: Lighthouse uses 8 for the attack described above. However, 8 kills Lodestar since validators
101
- * can trigger regen to fast-forward head state 8 epochs to be immediately invalidated as sync sets
102
- * a new head. Then the checkpoint state cache grows unbounded with very different states (because
103
- * they are 8 epochs apart) and causes an OOM. Research a proper solution once regen and the state
104
- * caches are better.
105
- */
106
- export const SYNC_TOLERANCE_EPOCHS = 1;
107
-
108
96
  /**
109
97
  * Cutoff time to wait from start of the slot for execution and builder block production apis to resolve.
110
98
  * Post this time, race execution and builder to pick whatever resolves first.
@@ -338,36 +326,6 @@ export function getValidatorApi(
338
326
  return null;
339
327
  }
340
328
 
341
- /**
342
- * Reject any request while the node is syncing
343
- */
344
- function notWhileSyncing(): void {
345
- // Consider node synced before or close to genesis
346
- if (chain.clock.currentSlot < SLOTS_PER_EPOCH) {
347
- return;
348
- }
349
-
350
- const syncState = sync.state;
351
- switch (syncState) {
352
- case SyncState.SyncingFinalized:
353
- case SyncState.SyncingHead: {
354
- const currentSlot = chain.clock.currentSlot;
355
- const headSlot = chain.forkChoice.getHead().slot;
356
- if (currentSlot - headSlot > SYNC_TOLERANCE_EPOCHS * SLOTS_PER_EPOCH) {
357
- throw new NodeIsSyncing(`headSlot ${headSlot} currentSlot ${currentSlot}`);
358
- }
359
-
360
- return;
361
- }
362
-
363
- case SyncState.Synced:
364
- return;
365
-
366
- case SyncState.Stalled:
367
- throw new NodeIsSyncing("waiting for peers");
368
- }
369
- }
370
-
371
329
  /**
372
330
  * Post merge, the CL and EL could be out of step in the sync, and could result in
373
331
  * Syncing status of the chain head. To be precise:
@@ -570,7 +528,7 @@ export function getValidatorApi(
570
528
  builderBoostFactor?: bigint,
571
529
  {feeRecipient, builderSelection, strictFeeRecipientCheck}: routes.validator.ExtraProduceBlockOpts = {}
572
530
  ): Promise<ProduceBlindedBlockOrBlockContentsRes> {
573
- notWhileSyncing();
531
+ notWhileSyncing(chain, sync.state);
574
532
  await waitForSlot(slot); // Must never request for a future slot > currentSlot
575
533
 
576
534
  const parentBlock = chain.getProposerHead(slot);
@@ -611,7 +569,10 @@ export function getValidatorApi(
611
569
  }
612
570
 
613
571
  const graffitiBytes = toGraffitiBytes(
614
- graffiti ?? getDefaultGraffiti(getLodestarClientVersion(opts), chain.executionEngine.clientVersion, opts)
572
+ getBlockGraffiti(graffiti, getLodestarClientVersion(opts), chain.executionEngine.clientVersion, {
573
+ private: opts.private,
574
+ graffitiAppend: chain.opts.graffitiAppend,
575
+ })
615
576
  );
616
577
 
617
578
  const loggerContext = {
@@ -911,7 +872,7 @@ export function getValidatorApi(
911
872
  throw new ApiError(400, `produceBlockV4 not supported for pre-gloas fork=${fork}`);
912
873
  }
913
874
 
914
- notWhileSyncing();
875
+ notWhileSyncing(chain, sync.state);
915
876
  await waitForSlot(slot);
916
877
 
917
878
  const parentBlock = chain.getProposerHead(slot);
@@ -921,7 +882,10 @@ export function getValidatorApi(
921
882
  metrics?.blockProductionSlotDelta.set(slot - parentSlot);
922
883
 
923
884
  const graffitiBytes = toGraffitiBytes(
924
- graffiti ?? getDefaultGraffiti(getLodestarClientVersion(opts), chain.executionEngine.clientVersion, opts)
885
+ getBlockGraffiti(graffiti, getLodestarClientVersion(opts), chain.executionEngine.clientVersion, {
886
+ private: opts.private,
887
+ graffitiAppend: chain.opts.graffitiAppend,
888
+ })
925
889
  );
926
890
 
927
891
  // TODO GLOAS: respect builderSelection (MaxProfit, BuilderAlways, ExecutionAlways, etc.) to let
@@ -929,7 +893,10 @@ export function getValidatorApi(
929
893
  // support when it is implemented.
930
894
  const isBuildingOnFull = chain.forkChoice.shouldBuildOnFull(parentBlock, slot);
931
895
  const bidParentBlockHash = isBuildingOnFull ? parentBlock.executionPayloadBlockHash : parentBlock.parentBlockHash;
932
- const builderBid = chain.executionPayloadBidPool.getBestBid(slot, bidParentBlockHash, parentBlockRootHex);
896
+ const circuitBreakerActive = chain.builderCircuitBreaker.isActive(slot);
897
+ const builderBid = circuitBreakerActive
898
+ ? null
899
+ : chain.executionPayloadBidPool.getBestBid(slot, bidParentBlockHash, parentBlockRootHex);
933
900
 
934
901
  const logCtx = {
935
902
  slot,
@@ -937,6 +904,7 @@ export function getValidatorApi(
937
904
  parentBlockRoot: parentBlockRootHex,
938
905
  parentBlockHash: parentBlock.executionPayloadBlockHash,
939
906
  fork,
907
+ circuitBreakerActive,
940
908
  ...(builderBid !== null
941
909
  ? {
942
910
  bidValue: builderBid.message.value,
@@ -1026,7 +994,7 @@ export function getValidatorApi(
1026
994
  },
1027
995
 
1028
996
  async produceAttestationData({committeeIndex, slot}) {
1029
- notWhileSyncing();
997
+ notWhileSyncing(chain, sync.state);
1030
998
 
1031
999
  await waitForSlot(slot); // Must never request for a future slot > currentSlot
1032
1000
 
@@ -1107,13 +1075,13 @@ export function getValidatorApi(
1107
1075
  throw new ApiError(400, `producePayloadAttestationData is not supported before Gloas fork=${fork}`);
1108
1076
  }
1109
1077
 
1110
- notWhileSyncing();
1078
+ notWhileSyncing(chain, sync.state);
1111
1079
  await waitForSlot(slot);
1112
1080
 
1113
1081
  const block = chain.forkChoice.getCanonicalBlockAtSlot(slot);
1114
1082
  if (!block) {
1115
- // No block is seen at slot. Return 404 so vc can skip casting payload attestation.
1116
- throw new ApiError(404, `No canonical block found at slot=${slot}`);
1083
+ // No canonical block is seen at slot. Return 204 so vc can skip casting payload attestation.
1084
+ return {data: undefined, meta: {version: fork}, status: 204};
1117
1085
  }
1118
1086
 
1119
1087
  const payloadInput = chain.seenPayloadEnvelopeInputCache.get(block.blockRoot);
@@ -1192,7 +1160,7 @@ export function getValidatorApi(
1192
1160
  },
1193
1161
 
1194
1162
  async getProposerDuties({epoch}, _context, opts?: {v2?: boolean}) {
1195
- notWhileSyncing();
1163
+ notWhileSyncing(chain, sync.state);
1196
1164
 
1197
1165
  const currentEpoch = currentEpochWithDisparity();
1198
1166
  const nextEpoch = currentEpoch + 1;
@@ -1239,7 +1207,7 @@ export function getValidatorApi(
1239
1207
  // requested epoch is within that range, we can use the head state at current epoch
1240
1208
  state = await chain.getHeadStateAtCurrentEpoch(RegenCaller.getDuties);
1241
1209
  } else {
1242
- const res = await getStateResponseWithRegen(chain, startSlot);
1210
+ const res = await getStateResponseWithRegen(chain, sync, startSlot);
1243
1211
 
1244
1212
  state = res.state instanceof Uint8Array ? chain.getHeadState().loadOtherState(res.state) : res.state;
1245
1213
 
@@ -1333,7 +1301,7 @@ export function getValidatorApi(
1333
1301
  },
1334
1302
 
1335
1303
  async getAttesterDuties({epoch, indices}) {
1336
- notWhileSyncing();
1304
+ notWhileSyncing(chain, sync.state);
1337
1305
 
1338
1306
  if (indices.length === 0) {
1339
1307
  throw new ApiError(400, "No validator to get attester duties");
@@ -1393,7 +1361,7 @@ export function getValidatorApi(
1393
1361
  },
1394
1362
 
1395
1363
  async getPtcDuties({epoch, indices}) {
1396
- notWhileSyncing();
1364
+ notWhileSyncing(chain, sync.state);
1397
1365
 
1398
1366
  if (indices.length === 0) {
1399
1367
  throw new ApiError(400, "No validator to get PTC duties");
@@ -1455,7 +1423,7 @@ export function getValidatorApi(
1455
1423
  * @param validatorIndices an array of the validator indices for which to obtain the duties.
1456
1424
  */
1457
1425
  async getSyncCommitteeDuties({epoch, indices}) {
1458
- notWhileSyncing();
1426
+ notWhileSyncing(chain, sync.state);
1459
1427
 
1460
1428
  if (indices.length === 0) {
1461
1429
  throw new ApiError(400, "No validator to get attester duties");
@@ -1502,7 +1470,7 @@ export function getValidatorApi(
1502
1470
  },
1503
1471
 
1504
1472
  async getAggregatedAttestationV2({attestationDataRoot, slot, committeeIndex}) {
1505
- notWhileSyncing();
1473
+ notWhileSyncing(chain, sync.state);
1506
1474
 
1507
1475
  await waitForSlot(slot); // Must never request for a future slot > currentSlot
1508
1476
 
@@ -1525,7 +1493,7 @@ export function getValidatorApi(
1525
1493
  },
1526
1494
 
1527
1495
  async publishAggregateAndProofsV2({signedAggregateAndProofs}) {
1528
- notWhileSyncing();
1496
+ notWhileSyncing(chain, sync.state);
1529
1497
 
1530
1498
  const seenTimestampSec = Date.now() / 1000;
1531
1499
  const failures: FailureList = [];
@@ -1586,7 +1554,7 @@ export function getValidatorApi(
1586
1554
  * https://github.com/ethereum/beacon-APIs/pull/137
1587
1555
  */
1588
1556
  async publishContributionAndProofs({contributionAndProofs}) {
1589
- notWhileSyncing();
1557
+ notWhileSyncing(chain, sync.state);
1590
1558
 
1591
1559
  const failures: FailureList = [];
1592
1560
 
@@ -1635,7 +1603,7 @@ export function getValidatorApi(
1635
1603
  },
1636
1604
 
1637
1605
  async prepareBeaconCommitteeSubnet({subscriptions}) {
1638
- notWhileSyncing();
1606
+ notWhileSyncing(chain, sync.state);
1639
1607
 
1640
1608
  await network.prepareBeaconCommitteeSubnets(
1641
1609
  subscriptions.map(({validatorIndex, slot, isAggregator, committeesAtSlot, committeeIndex}) => ({
@@ -1668,7 +1636,7 @@ export function getValidatorApi(
1668
1636
  * https://github.com/ethereum/beacon-APIs/pull/136
1669
1637
  */
1670
1638
  async prepareSyncCommitteeSubnets({subscriptions}) {
1671
- notWhileSyncing();
1639
+ notWhileSyncing(chain, sync.state);
1672
1640
 
1673
1641
  // A `validatorIndex` can be in multiple subnets, so compute the CommitteeSubscription with double for loop
1674
1642
  const subs: CommitteeSubscription[] = [];
@@ -1765,6 +1733,46 @@ export function getValidatorApi(
1765
1733
  });
1766
1734
  },
1767
1735
 
1736
+ async submitProposerPreferences({signedProposerPreferences}) {
1737
+ const failures: FailureList = [];
1738
+
1739
+ await Promise.all(
1740
+ signedProposerPreferences.map(async (signed, i) => {
1741
+ try {
1742
+ await validateGossipProposerPreferences(chain, signed);
1743
+
1744
+ chain.proposerPreferencesPool.add(signed);
1745
+ await network.publishProposerPreferences(signed);
1746
+ chain.emitter.emit(routes.events.EventType.proposerPreferences, {
1747
+ version: config.getForkName(signed.message.proposalSlot),
1748
+ data: signed,
1749
+ });
1750
+ } catch (e) {
1751
+ const logCtx = {
1752
+ slot: signed.message.proposalSlot,
1753
+ validatorIndex: signed.message.validatorIndex,
1754
+ dependentRoot: toRootHex(signed.message.dependentRoot),
1755
+ };
1756
+
1757
+ if (e instanceof ProposerPreferencesError && e.type.code === ProposerPreferencesErrorCode.ALREADY_KNOWN) {
1758
+ logger.debug("Ignoring known signed proposer preferences", logCtx);
1759
+ return;
1760
+ }
1761
+
1762
+ failures.push({index: i, message: (e as Error).message});
1763
+ logger.verbose(`Error on submitProposerPreferences [${i}]`, logCtx, e as Error);
1764
+ if (e instanceof ProposerPreferencesError && e.action === GossipAction.REJECT) {
1765
+ chain.persistInvalidSszValue(ssz.gloas.SignedProposerPreferences, signed, "api_reject");
1766
+ }
1767
+ }
1768
+ })
1769
+ );
1770
+
1771
+ if (failures.length > 0) {
1772
+ throw new IndexedError("Error processing signed proposer preferences", failures);
1773
+ }
1774
+ },
1775
+
1768
1776
  async getExecutionPayloadEnvelope({slot, beaconBlockRoot}) {
1769
1777
  const fork = config.getForkName(slot);
1770
1778
 
@@ -1772,7 +1780,7 @@ export function getValidatorApi(
1772
1780
  throw new ApiError(400, `getExecutionPayloadEnvelope not supported for pre-gloas fork=${fork}`);
1773
1781
  }
1774
1782
 
1775
- notWhileSyncing();
1783
+ notWhileSyncing(chain, sync.state);
1776
1784
  await waitForSlot(slot);
1777
1785
 
1778
1786
  const blockRootHex = toRootHex(beaconBlockRoot);
@@ -3,6 +3,7 @@ import {fastifyCors} from "@fastify/cors";
3
3
  import {FastifyError, FastifyInstance, FastifyRequest, errorCodes, fastify} from "fastify";
4
4
  import {parse as parseQueryString} from "qs";
5
5
  import {addSszContentTypeParser} from "@lodestar/api/server";
6
+ import {NUMBER_OF_COLUMNS} from "@lodestar/params";
6
7
  import {ErrorAborted, Gauge, Histogram, Logger} from "@lodestar/utils";
7
8
  import {isLocalhostIP} from "../../util/ip.js";
8
9
  import {ApiError, FailureList, IndexedError, NodeIsSyncing} from "../impl/errors.js";
@@ -55,6 +56,13 @@ const INVALID_MEDIA_TYPE_CODE = errorCodes.FST_ERR_CTP_INVALID_MEDIA_TYPE().code
55
56
  */
56
57
  const SCHEMA_VALIDATION_ERROR_CODE = errorCodes.FST_ERR_VALIDATION().code;
57
58
 
59
+ /**
60
+ * Cap for array query params, set to the largest array any beacon-API query can carry:
61
+ * a full data-column custody set (`getDebugDataColumnSidecars` `indices`). `qs` turns
62
+ * longer arrays into an object, which then fails schema validation.
63
+ */
64
+ const QUERY_STRING_ARRAY_LIMIT = NUMBER_OF_COLUMNS;
65
+
58
66
  /**
59
67
  * REST API powered by `fastify` server.
60
68
  */
@@ -82,6 +90,7 @@ export class RestApiServer {
82
90
  // be OpenAPI spec compliant and results are inconsistent, see https://github.com/ljharb/qs/issues/331.
83
91
  // The schema validation will catch this and throw an error as parsed query string results in an object.
84
92
  parseArrays: false,
93
+ arrayLimit: QUERY_STRING_ARRAY_LIMIT,
85
94
  }),
86
95
  },
87
96
  bodyLimit: opts.bodyLimit,
@@ -124,18 +124,13 @@ export async function importBlock(
124
124
  executionStatus = parentBlock.executionStatus;
125
125
  }
126
126
 
127
- // getBeaconProposerOrNull will return null if head state is more than one epoch away
128
- // from block slot. We skip proposer boost canonical check as we cannot determine the canonical proposer
129
- const expectedProposerIndex: number | null = this.getHeadState().getBeaconProposerOrNull(blockSlot);
130
-
131
127
  const blockSummary = this.forkChoice.onBlock(
132
128
  block.message,
133
129
  postState,
134
130
  blockDelaySec,
135
131
  currentSlot,
136
132
  executionStatus,
137
- dataAvailabilityStatus,
138
- expectedProposerIndex
133
+ dataAvailabilityStatus
139
134
  );
140
135
 
141
136
  // This adds the state necessary to process the next block
@@ -426,7 +421,7 @@ export async function importBlock(
426
421
  }
427
422
  } catch (e) {
428
423
  if (isStartSlotOfEpoch(proposalSlot)) {
429
- notOverrideFcuReason = NotReorgedReason.NotShufflingStable;
424
+ notOverrideFcuReason = NotReorgedReason.AtEpochBoundary;
430
425
  } else {
431
426
  this.logger.warn("Unable to get beacon proposer. Do not override fcu.", {proposalSlot}, e as Error);
432
427
  }
@@ -100,8 +100,8 @@ export function verifyExecutionPayloadEnvelope(
100
100
  }
101
101
 
102
102
  // Verify consistency with expected withdrawals
103
- const payloadWithdrawalsRoot = ssz.capella.Withdrawals.hashTreeRoot(payload.withdrawals);
104
- const expectedWithdrawalsRoot = ssz.capella.Withdrawals.hashTreeRoot(state.payloadExpectedWithdrawals);
103
+ const payloadWithdrawalsRoot = ssz.gloas.Withdrawals.hashTreeRoot(payload.withdrawals);
104
+ const expectedWithdrawalsRoot = ssz.gloas.Withdrawals.hashTreeRoot(state.payloadExpectedWithdrawals);
105
105
  if (!byteArrayEquals(payloadWithdrawalsRoot, expectedWithdrawalsRoot)) {
106
106
  throw new Error(
107
107
  `Withdrawals mismatch between payload and expected payload=${toRootHex(payloadWithdrawalsRoot)} expected=${toRootHex(expectedWithdrawalsRoot)}`
@@ -0,0 +1,84 @@
1
+ import {IForkChoice} from "@lodestar/fork-choice";
2
+ import {Slot} from "@lodestar/types";
3
+ import {Logger} from "@lodestar/utils";
4
+ import {getFaultInspectionParams} from "../execution/builder/http.js";
5
+ import {Metrics} from "../metrics/index.js";
6
+
7
+ export type BuilderCircuitBreakerOpts = {
8
+ faultInspectionWindow?: number;
9
+ allowedFaults?: number;
10
+ };
11
+
12
+ export type BuilderCircuitBreakerModules = {
13
+ forkChoice: IForkChoice;
14
+ logger: Logger;
15
+ metrics: Metrics | null;
16
+ };
17
+
18
+ /**
19
+ * Post-gloas circuit breaker for builder bids. The beacon block is produced by the proposer
20
+ * regardless of bid source, so missed blocks are not a useful builder health signal. Instead
21
+ * count blocks whose payload was never revealed and stop selecting builder bids while the
22
+ * non-reveal rate in the fault inspection window is too high.
23
+ */
24
+ export class BuilderCircuitBreaker {
25
+ readonly faultInspectionWindow: number;
26
+ readonly allowedFaults: number;
27
+
28
+ private active = false;
29
+ private lastUpdatedSlot = -1;
30
+
31
+ constructor(
32
+ opts: BuilderCircuitBreakerOpts,
33
+ private readonly modules: BuilderCircuitBreakerModules
34
+ ) {
35
+ const {faultInspectionWindow, allowedFaults} = getFaultInspectionParams(opts);
36
+ this.faultInspectionWindow = faultInspectionWindow;
37
+ this.allowedFaults = allowedFaults;
38
+ }
39
+
40
+ /** Whether builder bids must be ignored for a block produced at clockSlot */
41
+ isActive(clockSlot: Slot): boolean {
42
+ this.update(clockSlot);
43
+ return this.active;
44
+ }
45
+
46
+ update(clockSlot: Slot): void {
47
+ if (clockSlot <= this.lastUpdatedSlot) {
48
+ return;
49
+ }
50
+ this.lastUpdatedSlot = clockSlot;
51
+
52
+ // Exclude clockSlot itself, its payload reveal may still be in flight
53
+ const {blocksPresent, payloadsRevealed} = this.modules.forkChoice.getPayloadRevealCounts(
54
+ Math.max(clockSlot - this.faultInspectionWindow, 0),
55
+ clockSlot - 1
56
+ );
57
+ const faults = blocksPresent - payloadsRevealed;
58
+
59
+ const wasActive = this.active;
60
+ // Scale the fault budget by blocks present so sparse windows still trigger on high non-reveal rates
61
+ this.active = faults * this.faultInspectionWindow > this.allowedFaults * blocksPresent;
62
+
63
+ this.modules.metrics?.builderCircuitBreaker.active.set(this.active ? 1 : 0);
64
+ this.modules.metrics?.builderCircuitBreaker.faults.set(faults);
65
+ this.modules.metrics?.builderCircuitBreaker.blocksPresent.set(blocksPresent);
66
+ this.modules.metrics?.builderCircuitBreaker.payloadsRevealed.set(payloadsRevealed);
67
+
68
+ const logCtx = {
69
+ blocksPresent,
70
+ faults,
71
+ faultInspectionWindow: this.faultInspectionWindow,
72
+ allowedFaults: this.allowedFaults,
73
+ };
74
+ if (this.active !== wasActive) {
75
+ if (this.active) {
76
+ this.modules.logger.warn("Builder circuit breaker activated, ignoring builder bids", logCtx);
77
+ } else {
78
+ this.modules.logger.info("Builder circuit breaker deactivated", logCtx);
79
+ }
80
+ } else {
81
+ this.modules.logger.verbose("Builder circuit breaker status", {active: this.active, ...logCtx});
82
+ }
83
+ }
84
+ }
@@ -75,6 +75,7 @@ import {ImportPayloadOpts} from "./blocks/types.js";
75
75
  import {persistBlockInput} from "./blocks/writeBlockInputToDb.js";
76
76
  import {persistPayloadEnvelopeInput} from "./blocks/writePayloadEnvelopeInputToDb.js";
77
77
  import {BlsMultiThreadWorkerPool, BlsSingleThreadVerifier, IBlsVerifier} from "./bls/index.js";
78
+ import {BuilderCircuitBreaker} from "./builderCircuitBreaker.js";
78
79
  import {ColumnReconstructionTracker} from "./ColumnReconstructionTracker.js";
79
80
  import {ChainEvent, ChainEventEmitter} from "./emitter.js";
80
81
  import {ForkchoiceCaller, initializeForkChoice} from "./forkChoice/index.js";
@@ -107,7 +108,6 @@ import {
107
108
  SeenExecutionPayloadBids,
108
109
  SeenPayloadAttesters,
109
110
  SeenPayloadEnvelopeInput,
110
- SeenProposerPreferences,
111
111
  SeenSyncCommitteeMessages,
112
112
  } from "./seenCache/index.js";
113
113
  import {SeenAggregatedAttestations} from "./seenCache/seenAggregateAndProof.js";
@@ -152,6 +152,7 @@ export class BeaconChain implements IBeaconChain {
152
152
  readonly genesisValidatorsRoot: Root;
153
153
  readonly executionEngine: IExecutionEngine;
154
154
  readonly executionBuilder?: IExecutionBuilder;
155
+ readonly builderCircuitBreaker: BuilderCircuitBreaker;
155
156
  // Expose config for convenience in modularized functions
156
157
  readonly config: BeaconConfig;
157
158
  readonly custodyConfig: CustodyConfig;
@@ -189,7 +190,6 @@ export class BeaconChain implements IBeaconChain {
189
190
  readonly seenPayloadAttesters = new SeenPayloadAttesters();
190
191
  readonly seenAggregatedAttestations: SeenAggregatedAttestations;
191
192
  readonly seenExecutionPayloadBids = new SeenExecutionPayloadBids();
192
- readonly seenProposerPreferences = new SeenProposerPreferences();
193
193
  readonly seenBlockProposers = new SeenBlockProposers();
194
194
  readonly seenSyncCommitteeMessages = new SeenSyncCommitteeMessages();
195
195
  readonly seenContributionAndProof: SeenContributionAndProof;
@@ -427,6 +427,11 @@ export class BeaconChain implements IBeaconChain {
427
427
 
428
428
  this.forkChoice = forkChoice;
429
429
 
430
+ this.builderCircuitBreaker = new BuilderCircuitBreaker(
431
+ {faultInspectionWindow: opts.faultInspectionWindow, allowedFaults: opts.allowedFaults},
432
+ {forkChoice, logger, metrics}
433
+ );
434
+
430
435
  this.seenPayloadEnvelopeInputCache = new SeenPayloadEnvelopeInput({
431
436
  config,
432
437
  clock,
@@ -1473,7 +1478,6 @@ export class BeaconChain implements IBeaconChain {
1473
1478
  this.payloadAttestationPool.prune(slot);
1474
1479
  this.executionPayloadBidPool.prune(slot);
1475
1480
  this.seenExecutionPayloadBids.prune(slot);
1476
- this.seenProposerPreferences.prune(slot);
1477
1481
  this.proposerPreferencesPool.prune(slot);
1478
1482
  this.seenAttestationDatas.onSlot(slot);
1479
1483
  this.reprocessController.onSlot(slot);
@@ -67,6 +67,8 @@ export enum BlockErrorCode {
67
67
  TOO_MANY_KZG_COMMITMENTS = "BLOCK_ERROR_TOO_MANY_KZG_COMMITMENTS",
68
68
  /** Bid parent block root does not match block parent root */
69
69
  BID_PARENT_ROOT_MISMATCH = "BLOCK_ERROR_BID_PARENT_ROOT_MISMATCH",
70
+ /** A block body operation or parent execution request list exceeds its per-block limit */
71
+ TOO_MANY_BLOCK_OPERATIONS = "BLOCK_ERROR_TOO_MANY_BLOCK_OPERATIONS",
70
72
  /** The parent block's execution payload has been verified as invalid */
71
73
  PARENT_EXECUTION_INVALID = "BLOCK_ERROR_PARENT_EXECUTION_INVALID",
72
74
  /** The block's parent execution payload (defined by bid.parent_block_hash) has not been seen */
@@ -118,6 +120,7 @@ export type BlockErrorType =
118
120
  | {code: BlockErrorCode.DATA_UNAVAILABLE}
119
121
  | {code: BlockErrorCode.TOO_MANY_KZG_COMMITMENTS; blobKzgCommitmentsLen: number; commitmentLimit: number}
120
122
  | {code: BlockErrorCode.BID_PARENT_ROOT_MISMATCH; bidParentRoot: RootHex; blockParentRoot: RootHex}
123
+ | {code: BlockErrorCode.TOO_MANY_BLOCK_OPERATIONS; name: string; count: number; limit: number}
121
124
  | {code: BlockErrorCode.PARENT_EXECUTION_INVALID; parentRoot: RootHex}
122
125
  | {code: BlockErrorCode.PARENT_PAYLOAD_UNKNOWN; parentRoot: RootHex; parentBlockHash: RootHex};
123
126
 
@@ -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
 
@@ -35,6 +35,7 @@ import {BeaconProposerCache, ProposerPreparationData} from "./beaconProposerCach
35
35
  import {IBlockInput} from "./blocks/blockInput/index.js";
36
36
  import {ImportBlockOpts, ImportPayloadOpts} from "./blocks/types.js";
37
37
  import {IBlsVerifier} from "./bls/index.js";
38
+ import {BuilderCircuitBreaker} from "./builderCircuitBreaker.js";
38
39
  import {ColumnReconstructionTracker} from "./ColumnReconstructionTracker.js";
39
40
  import {ChainEventEmitter} from "./emitter.js";
40
41
  import {ForkchoiceCaller} from "./forkChoice/index.js";
@@ -61,7 +62,6 @@ import {
61
62
  SeenContributionAndProof,
62
63
  SeenExecutionPayloadBids,
63
64
  SeenPayloadAttesters,
64
- SeenProposerPreferences,
65
65
  SeenSyncCommitteeMessages,
66
66
  } from "./seenCache/index.js";
67
67
  import {SeenAggregatedAttestations} from "./seenCache/seenAggregateAndProof.js";
@@ -96,6 +96,7 @@ export interface IBeaconChain {
96
96
  readonly earliestAvailableSlot: Slot;
97
97
  readonly executionEngine: IExecutionEngine;
98
98
  readonly executionBuilder?: IExecutionBuilder;
99
+ readonly builderCircuitBreaker: BuilderCircuitBreaker;
99
100
  // Expose config for convenience in modularized functions
100
101
  readonly config: BeaconConfig;
101
102
  readonly custodyConfig: CustodyConfig;
@@ -133,7 +134,6 @@ export interface IBeaconChain {
133
134
  readonly seenPayloadAttesters: SeenPayloadAttesters;
134
135
  readonly seenAggregatedAttestations: SeenAggregatedAttestations;
135
136
  readonly seenExecutionPayloadBids: SeenExecutionPayloadBids;
136
- readonly seenProposerPreferences: SeenProposerPreferences;
137
137
  readonly seenBlockProposers: SeenBlockProposers;
138
138
  readonly seenSyncCommitteeMessages: SeenSyncCommitteeMessages;
139
139
  readonly seenContributionAndProof: SeenContributionAndProof;
@@ -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