@lodestar/beacon-node 1.45.0-dev.f535421f29 → 1.45.0-dev.f6c521a123

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 (234) hide show
  1. package/lib/api/impl/beacon/blocks/index.d.ts.map +1 -1
  2. package/lib/api/impl/beacon/blocks/index.js +3 -2
  3. package/lib/api/impl/beacon/blocks/index.js.map +1 -1
  4. package/lib/api/impl/beacon/index.d.ts +1 -1
  5. package/lib/api/impl/beacon/index.d.ts.map +1 -1
  6. package/lib/api/impl/beacon/index.js.map +1 -1
  7. package/lib/api/impl/beacon/pool/index.d.ts.map +1 -1
  8. package/lib/api/impl/beacon/pool/index.js +6 -43
  9. package/lib/api/impl/beacon/pool/index.js.map +1 -1
  10. package/lib/api/impl/beacon/state/index.d.ts +1 -1
  11. package/lib/api/impl/beacon/state/index.d.ts.map +1 -1
  12. package/lib/api/impl/beacon/state/index.js +48 -5
  13. package/lib/api/impl/beacon/state/index.js.map +1 -1
  14. package/lib/api/impl/beacon/state/utils.d.ts +13 -3
  15. package/lib/api/impl/beacon/state/utils.d.ts.map +1 -1
  16. package/lib/api/impl/beacon/state/utils.js +48 -4
  17. package/lib/api/impl/beacon/state/utils.js.map +1 -1
  18. package/lib/api/impl/debug/index.d.ts +1 -1
  19. package/lib/api/impl/debug/index.d.ts.map +1 -1
  20. package/lib/api/impl/debug/index.js +2 -2
  21. package/lib/api/impl/debug/index.js.map +1 -1
  22. package/lib/api/impl/lodestar/index.js +2 -2
  23. package/lib/api/impl/lodestar/index.js.map +1 -1
  24. package/lib/api/impl/proof/index.d.ts +1 -1
  25. package/lib/api/impl/proof/index.d.ts.map +1 -1
  26. package/lib/api/impl/proof/index.js +2 -2
  27. package/lib/api/impl/proof/index.js.map +1 -1
  28. package/lib/api/impl/utils.d.ts +23 -0
  29. package/lib/api/impl/utils.d.ts.map +1 -1
  30. package/lib/api/impl/utils.js +44 -1
  31. package/lib/api/impl/utils.js.map +1 -1
  32. package/lib/api/impl/validator/index.d.ts +0 -14
  33. package/lib/api/impl/validator/index.d.ts.map +1 -1
  34. package/lib/api/impl/validator/index.js +67 -62
  35. package/lib/api/impl/validator/index.js.map +1 -1
  36. package/lib/api/rest/base.d.ts.map +1 -1
  37. package/lib/api/rest/base.js +14 -3
  38. package/lib/api/rest/base.js.map +1 -1
  39. package/lib/chain/blocks/importBlock.d.ts.map +1 -1
  40. package/lib/chain/blocks/importBlock.js +4 -22
  41. package/lib/chain/blocks/importBlock.js.map +1 -1
  42. package/lib/chain/blocks/utils/chainSegment.d.ts.map +1 -1
  43. package/lib/chain/blocks/utils/chainSegment.js +13 -5
  44. package/lib/chain/blocks/utils/chainSegment.js.map +1 -1
  45. package/lib/chain/blocks/verifyBlocksExecutionPayloads.js +1 -1
  46. package/lib/chain/blocks/verifyBlocksExecutionPayloads.js.map +1 -1
  47. package/lib/chain/blocks/verifyBlocksSanityChecks.js +1 -1
  48. package/lib/chain/blocks/verifyBlocksSanityChecks.js.map +1 -1
  49. package/lib/chain/blocks/verifyExecutionPayloadEnvelope.js +2 -2
  50. package/lib/chain/blocks/verifyExecutionPayloadEnvelope.js.map +1 -1
  51. package/lib/chain/builderCircuitBreaker.d.ts +31 -0
  52. package/lib/chain/builderCircuitBreaker.d.ts.map +1 -0
  53. package/lib/chain/builderCircuitBreaker.js +59 -0
  54. package/lib/chain/builderCircuitBreaker.js.map +1 -0
  55. package/lib/chain/chain.d.ts +3 -2
  56. package/lib/chain/chain.d.ts.map +1 -1
  57. package/lib/chain/chain.js +27 -5
  58. package/lib/chain/chain.js.map +1 -1
  59. package/lib/chain/errors/blockError.d.ts +48 -6
  60. package/lib/chain/errors/blockError.d.ts.map +1 -1
  61. package/lib/chain/errors/blockError.js +25 -4
  62. package/lib/chain/errors/blockError.js.map +1 -1
  63. package/lib/chain/errors/executionPayloadEnvelope.d.ts +11 -0
  64. package/lib/chain/errors/executionPayloadEnvelope.d.ts.map +1 -1
  65. package/lib/chain/errors/executionPayloadEnvelope.js +2 -0
  66. package/lib/chain/errors/executionPayloadEnvelope.js.map +1 -1
  67. package/lib/chain/interface.d.ts +3 -2
  68. package/lib/chain/interface.d.ts.map +1 -1
  69. package/lib/chain/interface.js.map +1 -1
  70. package/lib/chain/lightClient/index.d.ts.map +1 -1
  71. package/lib/chain/lightClient/index.js +11 -6
  72. package/lib/chain/lightClient/index.js.map +1 -1
  73. package/lib/chain/lightClient/proofs.d.ts.map +1 -1
  74. package/lib/chain/lightClient/proofs.js +28 -2
  75. package/lib/chain/lightClient/proofs.js.map +1 -1
  76. package/lib/chain/lightClient/types.d.ts +3 -1
  77. package/lib/chain/lightClient/types.d.ts.map +1 -1
  78. package/lib/chain/opPools/aggregatedAttestationPool.js +1 -1
  79. package/lib/chain/opPools/aggregatedAttestationPool.js.map +1 -1
  80. package/lib/chain/opPools/proposerPreferencesPool.d.ts +3 -2
  81. package/lib/chain/opPools/proposerPreferencesPool.d.ts.map +1 -1
  82. package/lib/chain/opPools/proposerPreferencesPool.js +4 -1
  83. package/lib/chain/opPools/proposerPreferencesPool.js.map +1 -1
  84. package/lib/chain/options.d.ts +5 -0
  85. package/lib/chain/options.d.ts.map +1 -1
  86. package/lib/chain/options.js +1 -0
  87. package/lib/chain/options.js.map +1 -1
  88. package/lib/chain/prepareNextSlot.d.ts.map +1 -1
  89. package/lib/chain/prepareNextSlot.js +12 -7
  90. package/lib/chain/prepareNextSlot.js.map +1 -1
  91. package/lib/chain/produceBlock/produceBlockBody.d.ts.map +1 -1
  92. package/lib/chain/produceBlock/produceBlockBody.js +4 -12
  93. package/lib/chain/produceBlock/produceBlockBody.js.map +1 -1
  94. package/lib/chain/seenCache/index.d.ts +0 -1
  95. package/lib/chain/seenCache/index.d.ts.map +1 -1
  96. package/lib/chain/seenCache/index.js +0 -1
  97. package/lib/chain/seenCache/index.js.map +1 -1
  98. package/lib/chain/validation/aggregateAndProof.js +10 -6
  99. package/lib/chain/validation/aggregateAndProof.js.map +1 -1
  100. package/lib/chain/validation/attestation.d.ts.map +1 -1
  101. package/lib/chain/validation/attestation.js +10 -6
  102. package/lib/chain/validation/attestation.js.map +1 -1
  103. package/lib/chain/validation/block.d.ts.map +1 -1
  104. package/lib/chain/validation/block.js +53 -4
  105. package/lib/chain/validation/block.js.map +1 -1
  106. package/lib/chain/validation/executionPayloadBid.js +13 -12
  107. package/lib/chain/validation/executionPayloadBid.js.map +1 -1
  108. package/lib/chain/validation/executionPayloadEnvelope.d.ts.map +1 -1
  109. package/lib/chain/validation/executionPayloadEnvelope.js +29 -0
  110. package/lib/chain/validation/executionPayloadEnvelope.js.map +1 -1
  111. package/lib/chain/validation/proposerPreferences.d.ts.map +1 -1
  112. package/lib/chain/validation/proposerPreferences.js +14 -5
  113. package/lib/chain/validation/proposerPreferences.js.map +1 -1
  114. package/lib/chain/validation/signatureSets/aggregateAndProof.d.ts.map +1 -1
  115. package/lib/chain/validation/signatureSets/aggregateAndProof.js +2 -3
  116. package/lib/chain/validation/signatureSets/aggregateAndProof.js.map +1 -1
  117. package/lib/db/repositories/lightclientSyncCommitteeWitness.d.ts.map +1 -1
  118. package/lib/db/repositories/lightclientSyncCommitteeWitness.js +42 -7
  119. package/lib/db/repositories/lightclientSyncCommitteeWitness.js.map +1 -1
  120. package/lib/execution/builder/http.d.ts +18 -0
  121. package/lib/execution/builder/http.d.ts.map +1 -1
  122. package/lib/execution/builder/http.js +20 -11
  123. package/lib/execution/builder/http.js.map +1 -1
  124. package/lib/metrics/metrics/beacon.d.ts +6 -0
  125. package/lib/metrics/metrics/beacon.d.ts.map +1 -1
  126. package/lib/metrics/metrics/beacon.js +18 -0
  127. package/lib/metrics/metrics/beacon.js.map +1 -1
  128. package/lib/network/core/metrics.d.ts +65 -0
  129. package/lib/network/core/metrics.d.ts.map +1 -1
  130. package/lib/network/core/metrics.js +94 -0
  131. package/lib/network/core/metrics.js.map +1 -1
  132. package/lib/network/discv5/utils.d.ts.map +1 -1
  133. package/lib/network/discv5/utils.js +12 -3
  134. package/lib/network/discv5/utils.js.map +1 -1
  135. package/lib/network/gossip/encoding.d.ts +2 -2
  136. package/lib/network/gossip/encoding.d.ts.map +1 -1
  137. package/lib/network/gossip/encoding.js +22 -9
  138. package/lib/network/gossip/encoding.js.map +1 -1
  139. package/lib/network/gossip/gossipsub.d.ts.map +1 -1
  140. package/lib/network/gossip/gossipsub.js +4 -3
  141. package/lib/network/gossip/gossipsub.js.map +1 -1
  142. package/lib/network/gossip/topic.d.ts +831 -226
  143. package/lib/network/gossip/topic.d.ts.map +1 -1
  144. package/lib/network/gossip/topic.js +47 -4
  145. package/lib/network/gossip/topic.js.map +1 -1
  146. package/lib/network/peers/peerManager.d.ts.map +1 -1
  147. package/lib/network/peers/peerManager.js +10 -0
  148. package/lib/network/peers/peerManager.js.map +1 -1
  149. package/lib/network/peers/score/score.d.ts +3 -1
  150. package/lib/network/peers/score/score.d.ts.map +1 -1
  151. package/lib/network/peers/score/score.js +6 -1
  152. package/lib/network/peers/score/score.js.map +1 -1
  153. package/lib/network/peers/score/store.d.ts.map +1 -1
  154. package/lib/network/peers/score/store.js +6 -1
  155. package/lib/network/peers/score/store.js.map +1 -1
  156. package/lib/network/peers/utils/prioritizePeers.d.ts.map +1 -1
  157. package/lib/network/peers/utils/prioritizePeers.js +14 -0
  158. package/lib/network/peers/utils/prioritizePeers.js.map +1 -1
  159. package/lib/network/processor/gossipHandlers.d.ts.map +1 -1
  160. package/lib/network/processor/gossipHandlers.js +18 -9
  161. package/lib/network/processor/gossipHandlers.js.map +1 -1
  162. package/lib/network/reqresp/handlers/executionPayloadEnvelopesByRange.d.ts.map +1 -1
  163. package/lib/network/reqresp/handlers/executionPayloadEnvelopesByRange.js +4 -2
  164. package/lib/network/reqresp/handlers/executionPayloadEnvelopesByRange.js.map +1 -1
  165. package/lib/sync/sync.d.ts.map +1 -1
  166. package/lib/sync/sync.js +7 -0
  167. package/lib/sync/sync.js.map +1 -1
  168. package/lib/sync/unknownBlock.d.ts.map +1 -1
  169. package/lib/sync/unknownBlock.js +7 -2
  170. package/lib/sync/unknownBlock.js.map +1 -1
  171. package/lib/util/graffiti.d.ts +15 -0
  172. package/lib/util/graffiti.d.ts.map +1 -1
  173. package/lib/util/graffiti.js +55 -0
  174. package/lib/util/graffiti.js.map +1 -1
  175. package/package.json +23 -21
  176. package/src/api/impl/beacon/blocks/index.ts +3 -2
  177. package/src/api/impl/beacon/index.ts +1 -1
  178. package/src/api/impl/beacon/pool/index.ts +5 -53
  179. package/src/api/impl/beacon/state/index.ts +55 -3
  180. package/src/api/impl/beacon/state/utils.ts +68 -4
  181. package/src/api/impl/debug/index.ts +3 -2
  182. package/src/api/impl/lodestar/index.ts +2 -2
  183. package/src/api/impl/proof/index.ts +2 -2
  184. package/src/api/impl/utils.ts +51 -1
  185. package/src/api/impl/validator/index.ts +75 -67
  186. package/src/api/rest/base.ts +17 -3
  187. package/src/chain/blocks/importBlock.ts +2 -24
  188. package/src/chain/blocks/utils/chainSegment.ts +16 -5
  189. package/src/chain/blocks/verifyBlocksExecutionPayloads.ts +1 -1
  190. package/src/chain/blocks/verifyBlocksSanityChecks.ts +1 -1
  191. package/src/chain/blocks/verifyExecutionPayloadEnvelope.ts +2 -2
  192. package/src/chain/builderCircuitBreaker.ts +84 -0
  193. package/src/chain/chain.ts +38 -5
  194. package/src/chain/errors/blockError.ts +44 -7
  195. package/src/chain/errors/executionPayloadEnvelope.ts +9 -0
  196. package/src/chain/interface.ts +2 -2
  197. package/src/chain/lightClient/index.ts +12 -7
  198. package/src/chain/lightClient/proofs.ts +36 -1
  199. package/src/chain/lightClient/types.ts +3 -1
  200. package/src/chain/opPools/aggregatedAttestationPool.ts +1 -1
  201. package/src/chain/opPools/proposerPreferencesPool.ts +6 -2
  202. package/src/chain/options.ts +6 -0
  203. package/src/chain/prepareNextSlot.ts +11 -7
  204. package/src/chain/produceBlock/produceBlockBody.ts +9 -17
  205. package/src/chain/seenCache/index.ts +0 -1
  206. package/src/chain/validation/aggregateAndProof.ts +12 -6
  207. package/src/chain/validation/attestation.ts +12 -6
  208. package/src/chain/validation/block.ts +69 -4
  209. package/src/chain/validation/executionPayloadBid.ts +16 -11
  210. package/src/chain/validation/executionPayloadEnvelope.ts +37 -0
  211. package/src/chain/validation/proposerPreferences.ts +15 -5
  212. package/src/chain/validation/signatureSets/aggregateAndProof.ts +3 -3
  213. package/src/db/repositories/lightclientSyncCommitteeWitness.ts +57 -9
  214. package/src/execution/builder/http.ts +30 -17
  215. package/src/metrics/metrics/beacon.ts +19 -0
  216. package/src/network/core/metrics.ts +94 -0
  217. package/src/network/discv5/utils.ts +11 -3
  218. package/src/network/gossip/encoding.ts +20 -7
  219. package/src/network/gossip/gossipsub.ts +9 -3
  220. package/src/network/gossip/topic.ts +58 -4
  221. package/src/network/peers/peerManager.ts +11 -0
  222. package/src/network/peers/score/score.ts +8 -1
  223. package/src/network/peers/score/store.ts +8 -1
  224. package/src/network/peers/utils/prioritizePeers.ts +15 -0
  225. package/src/network/processor/gossipHandlers.ts +18 -8
  226. package/src/network/reqresp/handlers/executionPayloadEnvelopesByRange.ts +4 -2
  227. package/src/sync/sync.ts +10 -0
  228. package/src/sync/unknownBlock.ts +8 -2
  229. package/src/util/graffiti.ts +78 -0
  230. package/lib/chain/seenCache/seenProposerPreferences.d.ts +0 -16
  231. package/lib/chain/seenCache/seenProposerPreferences.d.ts.map +0 -1
  232. package/lib/chain/seenCache/seenProposerPreferences.js +0 -26
  233. package/lib/chain/seenCache/seenProposerPreferences.js.map +0 -1
  234. package/src/chain/seenCache/seenProposerPreferences.ts +0 -32
@@ -2,7 +2,6 @@ import {BitArray} from "@chainsafe/ssz";
2
2
  import {routes} from "@lodestar/api";
3
3
  import {
4
4
  AncestorStatus,
5
- EpochDifference,
6
5
  ExecutionStatus,
7
6
  ForkChoiceError,
8
7
  ForkChoiceErrorCode,
@@ -21,7 +20,6 @@ import {
21
20
  IBeaconStateView,
22
21
  RootCache,
23
22
  computeEpochAtSlot,
24
- computeStartSlotAtEpoch,
25
23
  computeTimeAtSlot,
26
24
  isStartSlotOfEpoch,
27
25
  isStatePostAltair,
@@ -124,18 +122,13 @@ export async function importBlock(
124
122
  executionStatus = parentBlock.executionStatus;
125
123
  }
126
124
 
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
125
  const blockSummary = this.forkChoice.onBlock(
132
126
  block.message,
133
127
  postState,
134
128
  blockDelaySec,
135
129
  currentSlot,
136
130
  executionStatus,
137
- dataAvailabilityStatus,
138
- expectedProposerIndex
131
+ dataAvailabilityStatus
139
132
  );
140
133
 
141
134
  // This adds the state necessary to process the next block
@@ -306,21 +299,6 @@ export async function importBlock(
306
299
  // Set head state as strong reference
307
300
  this.regen.updateHeadState(newHead, postState);
308
301
 
309
- try {
310
- this.emitter.emit(routes.events.EventType.head, {
311
- block: newHead.blockRoot,
312
- epochTransition: computeStartSlotAtEpoch(computeEpochAtSlot(newHead.slot)) === newHead.slot,
313
- slot: newHead.slot,
314
- state: newHead.stateRoot,
315
- previousDutyDependentRoot: this.forkChoice.getDependentRoot(newHead, EpochDifference.previous),
316
- currentDutyDependentRoot: this.forkChoice.getDependentRoot(newHead, EpochDifference.current),
317
- executionOptimistic: isOptimisticBlock(newHead),
318
- });
319
- } catch (e) {
320
- // getDependentRoot() may fail with error: "No block for root" as we can see in holesky non-finality issue
321
- this.logger.debug("Error emitting head event", {slot: newHead.slot, root: newHead.blockRoot}, e as Error);
322
- }
323
-
324
302
  const delaySec = this.clock.secFromSlot(newHead.slot);
325
303
  this.logger.verbose("New chain head", {
326
304
  slot: newHead.slot,
@@ -426,7 +404,7 @@ export async function importBlock(
426
404
  }
427
405
  } catch (e) {
428
406
  if (isStartSlotOfEpoch(proposalSlot)) {
429
- notOverrideFcuReason = NotReorgedReason.NotShufflingStable;
407
+ notOverrideFcuReason = NotReorgedReason.AtEpochBoundary;
430
408
  } else {
431
409
  this.logger.warn("Unable to get beacon proposer. Do not override fcu.", {proposalSlot}, e as Error);
432
410
  }
@@ -86,11 +86,22 @@ export function assertLinearChainSegment(
86
86
  // Maybe the previous slot's FULL envelope was orphaned — try falling back.
87
87
  // If even prevExecHash doesn't match, the segment is non-linear.
88
88
  if (bidParentHash !== prevExecHash) {
89
- throw new BlockError(block, {
90
- code: BlockErrorCode.PARENT_PAYLOAD_UNKNOWN,
91
- parentRoot: toRootHex(block.message.parentRoot),
92
- parentBlockHash: bidParentHash,
93
- });
89
+ // i === 0 compares against the seeded fork-choice parent (segment boundary); i > 0
90
+ // compares against the in-segment predecessor's payload (broken link inside the segment).
91
+ throw new BlockError(
92
+ block,
93
+ i === 0
94
+ ? {
95
+ code: BlockErrorCode.PARENT_PAYLOAD_UNKNOWN,
96
+ parentRoot: toRootHex(block.message.parentRoot),
97
+ parentBlockHash: bidParentHash,
98
+ }
99
+ : {
100
+ code: BlockErrorCode.NON_LINEAR_PAYLOAD_ROOTS,
101
+ parentBlockHash: bidParentHash,
102
+ expectedBlockHash: currentExecHash,
103
+ }
104
+ );
94
105
  }
95
106
  if (lastFullSlot !== null && payloadEnvelopes !== null) {
96
107
  const orphanedInput = payloadEnvelopes.get(lastFullSlot);
@@ -201,7 +201,7 @@ export async function verifyBlockExecutionPayload(
201
201
  invalidateFromParentBlockHash: toRootHex(executionPayloadEnabled.parentHash),
202
202
  };
203
203
  const execError = new BlockError(block, {
204
- code: BlockErrorCode.EXECUTION_ENGINE_ERROR,
204
+ code: BlockErrorCode.EXECUTION_ENGINE_INVALID,
205
205
  execStatus: execResult.status,
206
206
  errorMessage: execResult.validationError ?? "",
207
207
  });
@@ -94,7 +94,7 @@ export function verifyBlocksSanityChecks(
94
94
  const parentRoot = toRootHex(block.message.parentRoot);
95
95
  const parentBlockDefaultStatus = chain.forkChoice.getBlockHexDefaultStatus(parentRoot);
96
96
  if (!parentBlockDefaultStatus) {
97
- throw new BlockError(block, {code: BlockErrorCode.PARENT_UNKNOWN, parentRoot});
97
+ throw new BlockError(block, {code: BlockErrorCode.PARENT_BLOCK_UNKNOWN, parentRoot});
98
98
  }
99
99
 
100
100
  parentBlock = parentBlockDefaultStatus;
@@ -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
+ }
@@ -1,8 +1,16 @@
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
+ ProtoBlock,
12
+ UpdateHeadOpt,
13
+ } from "@lodestar/fork-choice";
6
14
  import {LoggerNode} from "@lodestar/logger/node";
7
15
  import {
8
16
  EFFECTIVE_BALANCE_INCREMENT,
@@ -75,6 +83,7 @@ import {ImportPayloadOpts} from "./blocks/types.js";
75
83
  import {persistBlockInput} from "./blocks/writeBlockInputToDb.js";
76
84
  import {persistPayloadEnvelopeInput} from "./blocks/writePayloadEnvelopeInputToDb.js";
77
85
  import {BlsMultiThreadWorkerPool, BlsSingleThreadVerifier, IBlsVerifier} from "./bls/index.js";
86
+ import {BuilderCircuitBreaker} from "./builderCircuitBreaker.js";
78
87
  import {ColumnReconstructionTracker} from "./ColumnReconstructionTracker.js";
79
88
  import {ChainEvent, ChainEventEmitter} from "./emitter.js";
80
89
  import {ForkchoiceCaller, initializeForkChoice} from "./forkChoice/index.js";
@@ -107,7 +116,6 @@ import {
107
116
  SeenExecutionPayloadBids,
108
117
  SeenPayloadAttesters,
109
118
  SeenPayloadEnvelopeInput,
110
- SeenProposerPreferences,
111
119
  SeenSyncCommitteeMessages,
112
120
  } from "./seenCache/index.js";
113
121
  import {SeenAggregatedAttestations} from "./seenCache/seenAggregateAndProof.js";
@@ -152,6 +160,7 @@ export class BeaconChain implements IBeaconChain {
152
160
  readonly genesisValidatorsRoot: Root;
153
161
  readonly executionEngine: IExecutionEngine;
154
162
  readonly executionBuilder?: IExecutionBuilder;
163
+ readonly builderCircuitBreaker: BuilderCircuitBreaker;
155
164
  // Expose config for convenience in modularized functions
156
165
  readonly config: BeaconConfig;
157
166
  readonly custodyConfig: CustodyConfig;
@@ -189,7 +198,6 @@ export class BeaconChain implements IBeaconChain {
189
198
  readonly seenPayloadAttesters = new SeenPayloadAttesters();
190
199
  readonly seenAggregatedAttestations: SeenAggregatedAttestations;
191
200
  readonly seenExecutionPayloadBids = new SeenExecutionPayloadBids();
192
- readonly seenProposerPreferences = new SeenProposerPreferences();
193
201
  readonly seenBlockProposers = new SeenBlockProposers();
194
202
  readonly seenSyncCommitteeMessages = new SeenSyncCommitteeMessages();
195
203
  readonly seenContributionAndProof: SeenContributionAndProof;
@@ -427,6 +435,11 @@ export class BeaconChain implements IBeaconChain {
427
435
 
428
436
  this.forkChoice = forkChoice;
429
437
 
438
+ this.builderCircuitBreaker = new BuilderCircuitBreaker(
439
+ {faultInspectionWindow: opts.faultInspectionWindow, allowedFaults: opts.allowedFaults},
440
+ {forkChoice, logger, metrics}
441
+ );
442
+
430
443
  this.seenPayloadEnvelopeInputCache = new SeenPayloadEnvelopeInput({
431
444
  config,
432
445
  clock,
@@ -1174,7 +1187,27 @@ export class BeaconChain implements IBeaconChain {
1174
1187
  const timer = this.metrics?.forkChoice.findHead.startTimer({caller});
1175
1188
 
1176
1189
  try {
1177
- return this.forkChoice.updateAndGetHead({mode: UpdateHeadOpt.GetCanonicalHead}).head;
1190
+ const prevHead = this.forkChoice.getHead();
1191
+ const head = this.forkChoice.updateAndGetHead({mode: UpdateHeadOpt.GetCanonicalHead}).head;
1192
+
1193
+ if (head.blockRoot !== prevHead.blockRoot) {
1194
+ try {
1195
+ this.emitter.emit(routes.events.EventType.head, {
1196
+ block: head.blockRoot,
1197
+ epochTransition: computeStartSlotAtEpoch(computeEpochAtSlot(head.slot)) === head.slot,
1198
+ slot: head.slot,
1199
+ state: head.stateRoot,
1200
+ previousDutyDependentRoot: this.forkChoice.getDependentRoot(head, EpochDifference.previous),
1201
+ currentDutyDependentRoot: this.forkChoice.getDependentRoot(head, EpochDifference.current),
1202
+ executionOptimistic: isOptimisticBlock(head),
1203
+ });
1204
+ } catch (e) {
1205
+ // getDependentRoot() may fail with error: "No block for root" as we can see in holesky non-finality issue
1206
+ this.logger.debug("Error emitting head event", {slot: head.slot, root: head.blockRoot}, e as Error);
1207
+ }
1208
+ }
1209
+
1210
+ return head;
1178
1211
  } catch (e) {
1179
1212
  this.metrics?.forkChoice.errors.inc({entrypoint: UpdateHeadOpt.GetCanonicalHead});
1180
1213
  throw e;
@@ -1204,6 +1237,7 @@ export class BeaconChain implements IBeaconChain {
1204
1237
  const secFromSlot = this.clock.secFromSlot(slot);
1205
1238
 
1206
1239
  try {
1240
+ // Do not emit head event here, when proposing we rely on the one emitted when importing our own block
1207
1241
  const {head, isHeadTimely, notReorgedReason} = this.forkChoice.updateAndGetHead({
1208
1242
  mode: UpdateHeadOpt.GetProposerHead,
1209
1243
  secFromSlot,
@@ -1473,7 +1507,6 @@ export class BeaconChain implements IBeaconChain {
1473
1507
  this.payloadAttestationPool.prune(slot);
1474
1508
  this.executionPayloadBidPool.prune(slot);
1475
1509
  this.seenExecutionPayloadBids.prune(slot);
1476
- this.seenProposerPreferences.prune(slot);
1477
1510
  this.proposerPreferencesPool.prune(slot);
1478
1511
  this.seenAttestationDatas.onSlot(slot);
1479
1512
  this.reprocessController.onSlot(slot);
@@ -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
 
@@ -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
 
@@ -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
  };
@@ -355,7 +355,7 @@ export class AggregatedAttestationPool {
355
355
  // after all committees are processed, we have a list of sameAttDataCons
356
356
  for (const consolidation of sameAttDataCons) {
357
357
  // Score attestations by profitability to maximize proposer reward
358
- const flags = getAttestationParticipationStatus(
358
+ const {flags} = getAttestationParticipationStatus(
359
359
  ForkSeq[fork],
360
360
  consolidation.attData,
361
361
  inclusionDistance,
@@ -1,4 +1,4 @@
1
- import {RootHex, Slot, gloas} from "@lodestar/types";
1
+ import {RootHex, Slot, ValidatorIndex, gloas} from "@lodestar/types";
2
2
  import {toRootHex} from "@lodestar/utils";
3
3
 
4
4
  /**
@@ -17,11 +17,15 @@ 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
  }
24
24
 
25
+ isKnown(proposalSlot: Slot, dependentRoot: RootHex, validatorIndex: ValidatorIndex): boolean {
26
+ return this.get(proposalSlot, dependentRoot)?.message.validatorIndex === validatorIndex;
27
+ }
28
+
25
29
  add(signed: gloas.SignedProposerPreferences): void {
26
30
  const {proposalSlot, dependentRoot} = signed.message;
27
31
  const rootHex = toRootHex(dependentRoot);