@lodestar/beacon-node 1.47.0-dev.d9c16ea16f → 1.47.0-dev.da8dfeabda

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 (258) hide show
  1. package/lib/api/impl/beacon/blocks/index.d.ts.map +1 -1
  2. package/lib/api/impl/beacon/blocks/index.js +4 -7
  3. package/lib/api/impl/beacon/blocks/index.js.map +1 -1
  4. package/lib/api/impl/beacon/pool/index.d.ts.map +1 -1
  5. package/lib/api/impl/beacon/pool/index.js +12 -1
  6. package/lib/api/impl/beacon/pool/index.js.map +1 -1
  7. package/lib/api/impl/beacon/state/utils.d.ts +2 -1
  8. package/lib/api/impl/beacon/state/utils.d.ts.map +1 -1
  9. package/lib/api/impl/beacon/state/utils.js.map +1 -1
  10. package/lib/api/impl/events/index.d.ts +1 -1
  11. package/lib/api/impl/events/index.d.ts.map +1 -1
  12. package/lib/api/impl/events/index.js +10 -3
  13. package/lib/api/impl/events/index.js.map +1 -1
  14. package/lib/api/impl/validator/index.d.ts.map +1 -1
  15. package/lib/api/impl/validator/index.js +12 -22
  16. package/lib/api/impl/validator/index.js.map +1 -1
  17. package/lib/api/impl/validator/utils.d.ts +5 -0
  18. package/lib/api/impl/validator/utils.d.ts.map +1 -1
  19. package/lib/api/impl/validator/utils.js +18 -12
  20. package/lib/api/impl/validator/utils.js.map +1 -1
  21. package/lib/chain/archiveStore/historicalState/metrics.d.ts.map +1 -1
  22. package/lib/chain/archiveStore/historicalState/metrics.js +21 -0
  23. package/lib/chain/archiveStore/historicalState/metrics.js.map +1 -1
  24. package/lib/chain/blocks/importBlock.d.ts.map +1 -1
  25. package/lib/chain/blocks/importBlock.js +2 -7
  26. package/lib/chain/blocks/importBlock.js.map +1 -1
  27. package/lib/chain/blocks/importExecutionPayload.d.ts +3 -2
  28. package/lib/chain/blocks/importExecutionPayload.d.ts.map +1 -1
  29. package/lib/chain/blocks/importExecutionPayload.js +11 -9
  30. package/lib/chain/blocks/importExecutionPayload.js.map +1 -1
  31. package/lib/chain/blocks/index.d.ts.map +1 -1
  32. package/lib/chain/blocks/index.js +15 -7
  33. package/lib/chain/blocks/index.js.map +1 -1
  34. package/lib/chain/blocks/verifyBlocksStateTransitionOnly.d.ts.map +1 -1
  35. package/lib/chain/blocks/verifyBlocksStateTransitionOnly.js +22 -12
  36. package/lib/chain/blocks/verifyBlocksStateTransitionOnly.js.map +1 -1
  37. package/lib/chain/blocks/verifyExecutionPayloadEnvelope.d.ts +2 -2
  38. package/lib/chain/blocks/verifyExecutionPayloadEnvelope.d.ts.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/bls/index.d.ts +1 -1
  42. package/lib/chain/bls/index.d.ts.map +1 -1
  43. package/lib/chain/bls/interface.d.ts +5 -5
  44. package/lib/chain/bls/interface.d.ts.map +1 -1
  45. package/lib/chain/bls/multithread/index.d.ts +5 -11
  46. package/lib/chain/bls/multithread/index.d.ts.map +1 -1
  47. package/lib/chain/bls/multithread/index.js +116 -101
  48. package/lib/chain/bls/multithread/index.js.map +1 -1
  49. package/lib/chain/bls/multithread/jobItem.d.ts +5 -19
  50. package/lib/chain/bls/multithread/jobItem.d.ts.map +1 -1
  51. package/lib/chain/bls/multithread/jobItem.js +10 -65
  52. package/lib/chain/bls/multithread/jobItem.js.map +1 -1
  53. package/lib/chain/bls/multithread/types.d.ts +29 -8
  54. package/lib/chain/bls/multithread/types.d.ts.map +1 -1
  55. package/lib/chain/bls/multithread/types.js +12 -0
  56. package/lib/chain/bls/multithread/types.js.map +1 -1
  57. package/lib/chain/bls/multithread/utils.d.ts +4 -0
  58. package/lib/chain/bls/multithread/utils.d.ts.map +1 -1
  59. package/lib/chain/bls/multithread/utils.js +10 -0
  60. package/lib/chain/bls/multithread/utils.js.map +1 -1
  61. package/lib/chain/bls/multithread/worker.js +49 -23
  62. package/lib/chain/bls/multithread/worker.js.map +1 -1
  63. package/lib/chain/bls/singleThread.d.ts +4 -10
  64. package/lib/chain/bls/singleThread.d.ts.map +1 -1
  65. package/lib/chain/bls/singleThread.js +8 -45
  66. package/lib/chain/bls/singleThread.js.map +1 -1
  67. package/lib/chain/bls/utils.d.ts +4 -4
  68. package/lib/chain/bls/utils.d.ts.map +1 -1
  69. package/lib/chain/bls/utils.js +18 -19
  70. package/lib/chain/bls/utils.js.map +1 -1
  71. package/lib/chain/builderCircuitBreaker.d.ts +6 -5
  72. package/lib/chain/builderCircuitBreaker.d.ts.map +1 -1
  73. package/lib/chain/builderCircuitBreaker.js +28 -15
  74. package/lib/chain/builderCircuitBreaker.js.map +1 -1
  75. package/lib/chain/chain.d.ts +3 -2
  76. package/lib/chain/chain.d.ts.map +1 -1
  77. package/lib/chain/chain.js +8 -5
  78. package/lib/chain/chain.js.map +1 -1
  79. package/lib/chain/errors/blockError.d.ts +2 -0
  80. package/lib/chain/errors/blockError.d.ts.map +1 -1
  81. package/lib/chain/errors/blockError.js +6 -1
  82. package/lib/chain/errors/blockError.js.map +1 -1
  83. package/lib/chain/errors/proposerPreferences.d.ts +6 -0
  84. package/lib/chain/errors/proposerPreferences.d.ts.map +1 -1
  85. package/lib/chain/errors/proposerPreferences.js +1 -0
  86. package/lib/chain/errors/proposerPreferences.js.map +1 -1
  87. package/lib/chain/interface.d.ts +2 -1
  88. package/lib/chain/interface.d.ts.map +1 -1
  89. package/lib/chain/interface.js.map +1 -1
  90. package/lib/chain/opPools/aggregatedAttestationPool.js +1 -1
  91. package/lib/chain/opPools/aggregatedAttestationPool.js.map +1 -1
  92. package/lib/chain/opPools/attestationPool.d.ts +1 -1
  93. package/lib/chain/opPools/attestationPool.d.ts.map +1 -1
  94. package/lib/chain/opPools/attestationPool.js +1 -1
  95. package/lib/chain/opPools/attestationPool.js.map +1 -1
  96. package/lib/chain/opPools/payloadAttestationPool.js +3 -3
  97. package/lib/chain/opPools/payloadAttestationPool.js.map +1 -1
  98. package/lib/chain/opPools/syncCommitteeMessagePool.js +1 -1
  99. package/lib/chain/opPools/syncCommitteeMessagePool.js.map +1 -1
  100. package/lib/chain/opPools/syncContributionAndProofPool.js +1 -1
  101. package/lib/chain/opPools/syncContributionAndProofPool.js.map +1 -1
  102. package/lib/chain/opPools/utils.d.ts +1 -1
  103. package/lib/chain/opPools/utils.d.ts.map +1 -1
  104. package/lib/chain/opPools/utils.js +1 -1
  105. package/lib/chain/opPools/utils.js.map +1 -1
  106. package/lib/chain/options.d.ts +1 -1
  107. package/lib/chain/options.d.ts.map +1 -1
  108. package/lib/chain/prepareNextSlot.js +1 -1
  109. package/lib/chain/prepareNextSlot.js.map +1 -1
  110. package/lib/chain/produceBlock/produceBlockBody.d.ts +2 -0
  111. package/lib/chain/produceBlock/produceBlockBody.d.ts.map +1 -1
  112. package/lib/chain/produceBlock/produceBlockBody.js +6 -1
  113. package/lib/chain/produceBlock/produceBlockBody.js.map +1 -1
  114. package/lib/chain/seenCache/seenAttesters.d.ts +9 -5
  115. package/lib/chain/seenCache/seenAttesters.d.ts.map +1 -1
  116. package/lib/chain/seenCache/seenAttesters.js +25 -4
  117. package/lib/chain/seenCache/seenAttesters.js.map +1 -1
  118. package/lib/chain/seenCache/seenBlockProposers.d.ts +5 -1
  119. package/lib/chain/seenCache/seenBlockProposers.d.ts.map +1 -1
  120. package/lib/chain/seenCache/seenBlockProposers.js +10 -3
  121. package/lib/chain/seenCache/seenBlockProposers.js.map +1 -1
  122. package/lib/chain/validation/attestation.d.ts.map +1 -1
  123. package/lib/chain/validation/attestation.js +1 -4
  124. package/lib/chain/validation/attestation.js.map +1 -1
  125. package/lib/chain/validation/attesterSlashing.d.ts.map +1 -1
  126. package/lib/chain/validation/attesterSlashing.js +1 -1
  127. package/lib/chain/validation/attesterSlashing.js.map +1 -1
  128. package/lib/chain/validation/block.d.ts.map +1 -1
  129. package/lib/chain/validation/block.js +21 -7
  130. package/lib/chain/validation/block.js.map +1 -1
  131. package/lib/chain/validation/executionPayloadBid.d.ts.map +1 -1
  132. package/lib/chain/validation/executionPayloadBid.js +1 -2
  133. package/lib/chain/validation/executionPayloadBid.js.map +1 -1
  134. package/lib/chain/validation/executionPayloadEnvelope.js +1 -1
  135. package/lib/chain/validation/executionPayloadEnvelope.js.map +1 -1
  136. package/lib/chain/validation/payloadAttestationMessage.d.ts.map +1 -1
  137. package/lib/chain/validation/payloadAttestationMessage.js +5 -14
  138. package/lib/chain/validation/payloadAttestationMessage.js.map +1 -1
  139. package/lib/chain/validation/proposerPreferences.d.ts +1 -1
  140. package/lib/chain/validation/proposerPreferences.d.ts.map +1 -1
  141. package/lib/chain/validation/proposerPreferences.js +13 -4
  142. package/lib/chain/validation/proposerPreferences.js.map +1 -1
  143. package/lib/chain/validation/proposerSlashing.js +1 -1
  144. package/lib/chain/validation/proposerSlashing.js.map +1 -1
  145. package/lib/execution/builder/http.d.ts +10 -7
  146. package/lib/execution/builder/http.d.ts.map +1 -1
  147. package/lib/execution/builder/http.js +11 -8
  148. package/lib/execution/builder/http.js.map +1 -1
  149. package/lib/metrics/metrics/beacon.d.ts +3 -3
  150. package/lib/metrics/metrics/beacon.d.ts.map +1 -1
  151. package/lib/metrics/metrics/beacon.js +9 -9
  152. package/lib/metrics/metrics/beacon.js.map +1 -1
  153. package/lib/metrics/metrics/lodestar.d.ts +48 -18
  154. package/lib/metrics/metrics/lodestar.d.ts.map +1 -1
  155. package/lib/metrics/metrics/lodestar.js +96 -50
  156. package/lib/metrics/metrics/lodestar.js.map +1 -1
  157. package/lib/network/gossip/topic.d.ts +13 -0
  158. package/lib/network/gossip/topic.d.ts.map +1 -1
  159. package/lib/network/gossip/topic.js +37 -0
  160. package/lib/network/gossip/topic.js.map +1 -1
  161. package/lib/network/libp2p/index.d.ts.map +1 -1
  162. package/lib/network/libp2p/index.js +10 -2
  163. package/lib/network/libp2p/index.js.map +1 -1
  164. package/lib/network/network.d.ts.map +1 -1
  165. package/lib/network/network.js +22 -43
  166. package/lib/network/network.js.map +1 -1
  167. package/lib/network/processor/gossipHandlers.d.ts.map +1 -1
  168. package/lib/network/processor/gossipHandlers.js +32 -5
  169. package/lib/network/processor/gossipHandlers.js.map +1 -1
  170. package/lib/network/reqresp/score.d.ts.map +1 -1
  171. package/lib/network/reqresp/score.js +5 -11
  172. package/lib/network/reqresp/score.js.map +1 -1
  173. package/lib/network/util.d.ts +0 -1
  174. package/lib/network/util.d.ts.map +1 -1
  175. package/lib/network/util.js +0 -4
  176. package/lib/network/util.js.map +1 -1
  177. package/lib/node/nodejs.d.ts +2 -1
  178. package/lib/node/nodejs.d.ts.map +1 -1
  179. package/lib/node/nodejs.js.map +1 -1
  180. package/lib/sync/range/batch.d.ts +23 -5
  181. package/lib/sync/range/batch.d.ts.map +1 -1
  182. package/lib/sync/range/batch.js +61 -22
  183. package/lib/sync/range/batch.js.map +1 -1
  184. package/lib/sync/range/chain.d.ts +2 -1
  185. package/lib/sync/range/chain.d.ts.map +1 -1
  186. package/lib/sync/range/chain.js +23 -10
  187. package/lib/sync/range/chain.js.map +1 -1
  188. package/lib/sync/types.d.ts.map +1 -1
  189. package/lib/sync/types.js.map +1 -1
  190. package/lib/sync/unknownBlock.d.ts +9 -0
  191. package/lib/sync/unknownBlock.d.ts.map +1 -1
  192. package/lib/sync/unknownBlock.js +110 -14
  193. package/lib/sync/unknownBlock.js.map +1 -1
  194. package/package.json +18 -18
  195. package/src/api/impl/beacon/blocks/index.ts +5 -7
  196. package/src/api/impl/beacon/pool/index.ts +12 -1
  197. package/src/api/impl/beacon/state/utils.ts +2 -1
  198. package/src/api/impl/events/index.ts +10 -4
  199. package/src/api/impl/validator/index.ts +22 -27
  200. package/src/api/impl/validator/utils.ts +30 -15
  201. package/src/chain/archiveStore/historicalState/metrics.ts +22 -0
  202. package/src/chain/blocks/importBlock.ts +1 -6
  203. package/src/chain/blocks/importExecutionPayload.ts +12 -11
  204. package/src/chain/blocks/index.ts +19 -7
  205. package/src/chain/blocks/verifyBlocksStateTransitionOnly.ts +25 -16
  206. package/src/chain/blocks/verifyExecutionPayloadEnvelope.ts +1 -9
  207. package/src/chain/bls/index.ts +1 -1
  208. package/src/chain/bls/interface.ts +7 -3
  209. package/src/chain/bls/multithread/index.ts +148 -120
  210. package/src/chain/bls/multithread/jobItem.ts +12 -82
  211. package/src/chain/bls/multithread/types.ts +33 -8
  212. package/src/chain/bls/multithread/utils.ts +11 -0
  213. package/src/chain/bls/multithread/worker.ts +85 -27
  214. package/src/chain/bls/singleThread.ts +11 -55
  215. package/src/chain/bls/utils.ts +23 -24
  216. package/src/chain/builderCircuitBreaker.ts +31 -17
  217. package/src/chain/chain.ts +9 -5
  218. package/src/chain/errors/blockError.ts +9 -3
  219. package/src/chain/errors/proposerPreferences.ts +7 -0
  220. package/src/chain/interface.ts +2 -1
  221. package/src/chain/opPools/aggregatedAttestationPool.ts +1 -1
  222. package/src/chain/opPools/attestationPool.ts +1 -1
  223. package/src/chain/opPools/payloadAttestationPool.ts +3 -3
  224. package/src/chain/opPools/syncCommitteeMessagePool.ts +1 -1
  225. package/src/chain/opPools/syncContributionAndProofPool.ts +1 -1
  226. package/src/chain/opPools/utils.ts +1 -1
  227. package/src/chain/options.ts +1 -1
  228. package/src/chain/prepareNextSlot.ts +1 -1
  229. package/src/chain/produceBlock/produceBlockBody.ts +13 -0
  230. package/src/chain/seenCache/seenAttesters.ts +31 -5
  231. package/src/chain/seenCache/seenBlockProposers.ts +13 -3
  232. package/src/chain/validation/attestation.ts +1 -7
  233. package/src/chain/validation/attesterSlashing.ts +1 -8
  234. package/src/chain/validation/block.ts +21 -7
  235. package/src/chain/validation/executionPayloadBid.ts +1 -2
  236. package/src/chain/validation/executionPayloadEnvelope.ts +0 -1
  237. package/src/chain/validation/payloadAttestationMessage.ts +6 -17
  238. package/src/chain/validation/proposerPreferences.ts +15 -5
  239. package/src/chain/validation/proposerSlashing.ts +1 -1
  240. package/src/execution/builder/http.ts +11 -11
  241. package/src/metrics/metrics/beacon.ts +9 -9
  242. package/src/metrics/metrics/lodestar.ts +101 -51
  243. package/src/network/gossip/topic.ts +38 -0
  244. package/src/network/libp2p/index.ts +11 -2
  245. package/src/network/network.ts +27 -55
  246. package/src/network/processor/gossipHandlers.ts +35 -3
  247. package/src/network/reqresp/score.ts +5 -11
  248. package/src/network/util.ts +0 -5
  249. package/src/node/nodejs.ts +2 -1
  250. package/src/sync/range/batch.ts +83 -28
  251. package/src/sync/range/chain.ts +26 -11
  252. package/src/sync/types.ts +3 -1
  253. package/src/sync/unknownBlock.ts +126 -17
  254. package/lib/chain/bls/maybeBatch.d.ts +0 -12
  255. package/lib/chain/bls/maybeBatch.d.ts.map +0 -1
  256. package/lib/chain/bls/maybeBatch.js +0 -35
  257. package/lib/chain/bls/maybeBatch.js.map +0 -1
  258. package/src/chain/bls/maybeBatch.ts +0 -45
@@ -100,6 +100,8 @@ export type BlockAttributes = {
100
100
  slot: Slot;
101
101
  parentBlock: ProtoBlock;
102
102
  feeRecipient?: string;
103
+ /** Verify that a locally produced execution payload uses `feeRecipient`. */
104
+ strictFeeRecipientCheck?: boolean;
103
105
  /** When provided, build block with this builder bid instead of a self-build bid */
104
106
  builderBid?: gloas.SignedExecutionPayloadBid;
105
107
  };
@@ -201,6 +203,7 @@ export async function produceBlockBody<T extends BlockType>(
201
203
  const {
202
204
  slot: blockSlot,
203
205
  feeRecipient: requestedFeeRecipient,
206
+ strictFeeRecipientCheck,
204
207
  parentBlock,
205
208
  proposerIndex,
206
209
  proposerPubKey,
@@ -334,6 +337,16 @@ export async function produceBlockBody<T extends BlockType>(
334
337
  executionPayloadValue = payloadRes.executionPayloadValue;
335
338
  shouldOverrideBuilder = payloadRes.shouldOverrideBuilder;
336
339
 
340
+ if (
341
+ strictFeeRecipientCheck &&
342
+ requestedFeeRecipient &&
343
+ !byteArrayEquals(executionPayload.feeRecipient, fromHex(requestedFeeRecipient))
344
+ ) {
345
+ throw Error(
346
+ `Invalid feeRecipient set in engine payload expected=${requestedFeeRecipient} actual=${toHex(executionPayload.feeRecipient)}`
347
+ );
348
+ }
349
+
337
350
  if (blobsBundle === undefined) {
338
351
  throw Error(`Missing blobsBundle response from getPayload at fork=${fork}`);
339
352
  }
@@ -1,4 +1,5 @@
1
- import {Epoch, ValidatorIndex} from "@lodestar/types";
1
+ import {computeEpochAtSlot} from "@lodestar/state-transition";
2
+ import {Epoch, Slot, ValidatorIndex} from "@lodestar/types";
2
3
  import {MapDef} from "@lodestar/utils";
3
4
 
4
5
  // How many *non future* epochs we intend to keep for SeenAttesters.
@@ -57,7 +58,32 @@ export class SeenAttesters {
57
58
  */
58
59
  export class SeenAggregators extends SeenAttesters {}
59
60
 
60
- /**
61
- * Keeps a cache to filter payload attestations from the same attesters in the same epoch
62
- */
63
- export class SeenPayloadAttesters extends SeenAttesters {}
61
+ /** Keeps a cache to filter payload attestations from the same validator in the same slot. */
62
+ export class SeenPayloadAttesters {
63
+ private readonly validatorIndexesBySlot = new MapDef<Slot, Set<ValidatorIndex>>(() => new Set<ValidatorIndex>());
64
+ private readonly validatorIndexesByEpoch = new SeenAttesters();
65
+
66
+ isKnown(slot: Slot, validatorIndex: ValidatorIndex): boolean {
67
+ return this.validatorIndexesBySlot.get(slot)?.has(validatorIndex) === true;
68
+ }
69
+
70
+ add(slot: Slot, validatorIndex: ValidatorIndex): void {
71
+ const epoch = computeEpochAtSlot(slot);
72
+ this.validatorIndexesByEpoch.add(epoch, validatorIndex);
73
+ this.validatorIndexesBySlot.getOrDefault(slot).add(validatorIndex);
74
+ }
75
+
76
+ seenAtEpoch(epoch: Epoch, validatorIndex: ValidatorIndex): boolean {
77
+ return this.validatorIndexesByEpoch.isKnown(epoch, validatorIndex);
78
+ }
79
+
80
+ prune(currentEpoch: Epoch): void {
81
+ const lowestPermissibleEpoch = Math.max(currentEpoch - EPOCH_LOOKBACK_LIMIT, 0);
82
+ for (const slot of this.validatorIndexesBySlot.keys()) {
83
+ if (computeEpochAtSlot(slot) < lowestPermissibleEpoch) {
84
+ this.validatorIndexesBySlot.delete(slot);
85
+ }
86
+ }
87
+ this.validatorIndexesByEpoch.prune(currentEpoch);
88
+ }
89
+ }
@@ -19,7 +19,9 @@ const MIN_EQUIVOCATION_BLOCK_ROOTS_PER_PROPOSAL = 2;
19
19
  * The cache is pruned on finalization and bounds the number of roots stored per proposer and slot
20
20
  */
21
21
  export class SeenBlockProposers {
22
- private readonly proposerIndexesBySlot = new MapDef<Slot, Set<ValidatorIndex>>(() => new Set<ValidatorIndex>());
22
+ private readonly proposerIndexesBySlot = new MapDef<Slot, Map<ValidatorIndex, RootHex>>(
23
+ () => new Map<ValidatorIndex, RootHex>()
24
+ );
23
25
  private readonly signedBlockHeadersBySlot = new MapDef<
24
26
  Slot,
25
27
  MapDef<ValidatorIndex, Map<RootHex, phase0.SignedBeaconBlockHeader>>
@@ -30,6 +32,14 @@ export class SeenBlockProposers {
30
32
  return this.proposerIndexesBySlot.get(blockSlot)?.has(proposerIndex) === true;
31
33
  }
32
34
 
35
+ /**
36
+ * The block proposer is known at slot with a different root.
37
+ */
38
+ isRepeatProposal(blockSlot: Slot, proposerIndex: ValidatorIndex, blockRoot: RootHex): boolean {
39
+ const knownRoot = this.proposerIndexesBySlot.get(blockSlot)?.get(proposerIndex);
40
+ return knownRoot !== undefined && knownRoot !== blockRoot;
41
+ }
42
+
33
43
  hasBlockRoot(blockSlot: Slot, proposerIndex: ValidatorIndex, blockRoot: RootHex): boolean {
34
44
  return this.signedBlockHeadersBySlot.get(blockSlot)?.get(proposerIndex)?.has(blockRoot) === true;
35
45
  }
@@ -85,12 +95,12 @@ export class SeenBlockProposers {
85
95
  }
86
96
 
87
97
  /** Mark a block as known from gossip or another block import path */
88
- add(blockSlot: Slot, proposerIndex: ValidatorIndex): void {
98
+ add(blockSlot: Slot, proposerIndex: ValidatorIndex, blockRoot: RootHex): void {
89
99
  if (blockSlot < this.finalizedSlot) {
90
100
  throw Error(`blockSlot ${blockSlot} < finalizedSlot ${this.finalizedSlot}`);
91
101
  }
92
102
 
93
- this.proposerIndexesBySlot.getOrDefault(blockSlot).add(proposerIndex);
103
+ this.proposerIndexesBySlot.getOrDefault(blockSlot).set(proposerIndex, blockRoot);
94
104
  }
95
105
 
96
106
  prune(finalizedSlot: Slot): void {
@@ -142,13 +142,7 @@ export async function validateGossipAttestationsSameAttData(
142
142
  const batchableBls = signatureSets.length >= chain.opts.minSameMessageSignatureSetsToBatch;
143
143
  if (batchableBls) {
144
144
  // all signature sets should have same signing root since we filtered in network processor
145
- signatureValids = await chain.bls.verifySignatureSetsSameMessage(
146
- signatureSets.map((set) => {
147
- const publicKey = chain.pubkeyCache.getOrThrow(set.index);
148
- return {publicKey, signature: set.signature};
149
- }),
150
- signatureSets[0].signingRoot
151
- );
145
+ signatureValids = await chain.bls.verifySignatureSetsSameMessage(signatureSets, signatureSets[0].signingRoot);
152
146
  } else {
153
147
  // don't want to block the main thread if there are too few signatures
154
148
  signatureValids = await Promise.all(
@@ -44,14 +44,7 @@ export async function validateAttesterSlashing(
44
44
  // [REJECT] All of the conditions within process_attester_slashing pass validation.
45
45
  try {
46
46
  // verifySignature = false, verified in batch below
47
- assertValidAttesterSlashing(
48
- chain.config,
49
- chain.pubkeyCache,
50
- state.slot,
51
- state.validatorCount,
52
- attesterSlashing,
53
- false
54
- );
47
+ assertValidAttesterSlashing(chain.config, state.slot, state.validatorCount, attesterSlashing, false);
55
48
  } catch (e) {
56
49
  throw new AttesterSlashingError(GossipAction.REJECT, {
57
50
  code: AttesterSlashingErrorCode.INVALID,
@@ -89,13 +89,20 @@ export async function validateGossipBlock(
89
89
 
90
90
  // [IGNORE] The block is the first block with valid signature received for the proposer for the slot, signed_beacon_block.message.slot.
91
91
  const proposerIndex = block.proposerIndex;
92
- const hasBlockRoot = chain.seenBlockProposers.hasBlockRoot(blockSlot, proposerIndex, blockRoot);
93
92
  if (chain.seenBlockProposers.isKnown(blockSlot, proposerIndex)) {
94
- if (!hasBlockRoot && !chain.seenBlockProposers.isEquivocating(blockSlot, proposerIndex)) {
95
- await verifyBlockProposerSignature(chain, signedBlock, blockRoot, {verifyOnMainThread: false});
96
- chain.seenBlockProposers.observeBlockRoot(blockSlot, proposerIndex, blockRoot, signedBlockHeader);
93
+ if (chain.seenBlockProposers.isRepeatProposal(blockSlot, proposerIndex, blockRoot)) {
94
+ const hasBlockRoot = chain.seenBlockProposers.hasBlockRoot(blockSlot, proposerIndex, blockRoot);
95
+ if (!hasBlockRoot && !chain.seenBlockProposers.isEquivocating(blockSlot, proposerIndex)) {
96
+ await verifyBlockProposerSignature(chain, signedBlock, blockRoot, {verifyOnMainThread: false});
97
+ chain.seenBlockProposers.observeBlockRoot(blockSlot, proposerIndex, blockRoot, signedBlockHeader);
98
+ }
99
+ throw new BlockGossipError(GossipAction.IGNORE, {
100
+ code: BlockErrorCode.REPEAT_PROPOSAL,
101
+ proposerIndex,
102
+ root: blockRoot,
103
+ });
97
104
  }
98
- throw new BlockGossipError(GossipAction.IGNORE, {code: BlockErrorCode.REPEAT_PROPOSAL, proposerIndex});
105
+ throw new BlockGossipError(GossipAction.IGNORE, {code: BlockErrorCode.ALREADY_KNOWN, root: blockRoot});
99
106
  }
100
107
 
101
108
  // [REJECT] The current finalized_checkpoint is an ancestor of block -- i.e.
@@ -301,10 +308,17 @@ export async function validateGossipBlock(
301
308
 
302
309
  // Check again after all async validation and the early-block delay so concurrent proposals cannot both pass
303
310
  if (chain.seenBlockProposers.isKnown(blockSlot, proposerIndex)) {
304
- throw new BlockGossipError(GossipAction.IGNORE, {code: BlockErrorCode.REPEAT_PROPOSAL, proposerIndex});
311
+ if (chain.seenBlockProposers.isRepeatProposal(blockSlot, proposerIndex, blockRoot)) {
312
+ throw new BlockGossipError(GossipAction.IGNORE, {
313
+ code: BlockErrorCode.REPEAT_PROPOSAL,
314
+ proposerIndex,
315
+ root: blockRoot,
316
+ });
317
+ }
318
+ throw new BlockGossipError(GossipAction.IGNORE, {code: BlockErrorCode.ALREADY_KNOWN, root: blockRoot});
305
319
  }
306
320
 
307
- chain.seenBlockProposers.add(blockSlot, proposerIndex);
321
+ chain.seenBlockProposers.add(blockSlot, proposerIndex, blockRoot);
308
322
 
309
323
  return {skippedSlots};
310
324
  }
@@ -1,4 +1,3 @@
1
- import {PublicKey} from "@chainsafe/blst";
2
1
  import {IForkChoice, ProtoBlock} from "@lodestar/fork-choice";
3
2
  import {PAYLOAD_BUILDER_VERSION} from "@lodestar/params";
4
3
  import {
@@ -345,7 +344,7 @@ async function validateExecutionPayloadBid(
345
344
 
346
345
  // [REJECT] `signed_execution_payload_bid.signature` is valid with respect to the `bid.builder_index`.
347
346
  const signatureSet = createSingleSignatureSetFromComponents(
348
- PublicKey.fromBytes(builder.pubkey),
347
+ builder.pubkey,
349
348
  getExecutionPayloadBidSigningRoot(chain.config, bid),
350
349
  signedExecutionPayloadBid.signature
351
350
  );
@@ -170,7 +170,6 @@ async function validateExecutionPayloadEnvelope(
170
170
  // by `verify_execution_payload_envelope_signature`.
171
171
  const signatureSet = getExecutionPayloadEnvelopeSignatureSet(
172
172
  chain.config,
173
- chain.pubkeyCache,
174
173
  blockState,
175
174
  executionPayloadEnvelope,
176
175
  payloadInput.proposerIndex
@@ -1,6 +1,5 @@
1
1
  import {
2
- computeEpochAtSlot,
3
- createSingleSignatureSetFromComponents,
2
+ createIndexedSignatureSetFromComponents,
4
3
  getPayloadAttestationDataSigningRoot,
5
4
  isStatePostGloas,
6
5
  } from "@lodestar/state-transition";
@@ -36,7 +35,6 @@ async function validatePayloadAttestationMessage(
36
35
  prioritizeBls = false
37
36
  ): Promise<PayloadAttestationValidationResult> {
38
37
  const {data, validatorIndex} = payloadAttestationMessage;
39
- const epoch = computeEpochAtSlot(data.slot);
40
38
 
41
39
  // [IGNORE] The message's slot is for the current slot (with a `MAXIMUM_GOSSIP_CLOCK_DISPARITY` allowance), i.e. `data.slot == current_slot`.
42
40
  if (!chain.clock.isCurrentSlotGivenGossipDisparity(data.slot)) {
@@ -48,9 +46,8 @@ async function validatePayloadAttestationMessage(
48
46
  }
49
47
 
50
48
  // [IGNORE] The `payload_attestation_message` is the first valid message received
51
- // from the validator with index `payload_attestation_message.validator_index`.
52
- // A single validator can participate PTC at most once per epoch
53
- if (chain.seenPayloadAttesters.isKnown(epoch, validatorIndex)) {
49
+ // for the slot from the validator with index `payload_attestation_message.validator_index`.
50
+ if (chain.seenPayloadAttesters.isKnown(data.slot, validatorIndex)) {
54
51
  throw new PayloadAttestationError(GossipAction.IGNORE, {
55
52
  code: PayloadAttestationErrorCode.PAYLOAD_ATTESTATION_ALREADY_KNOWN,
56
53
  validatorIndex,
@@ -114,16 +111,8 @@ async function validatePayloadAttestationMessage(
114
111
  }
115
112
 
116
113
  // [REJECT] `payload_attestation_message.signature` is valid with respect to the validator's public key.
117
- const validatorPubkey = chain.pubkeyCache.get(validatorIndex);
118
- if (!validatorPubkey) {
119
- throw new PayloadAttestationError(GossipAction.REJECT, {
120
- code: PayloadAttestationErrorCode.INVALID_ATTESTER,
121
- attesterIndex: validatorIndex,
122
- });
123
- }
124
-
125
- const signatureSet = createSingleSignatureSetFromComponents(
126
- validatorPubkey,
114
+ const signatureSet = createIndexedSignatureSetFromComponents(
115
+ validatorIndex,
127
116
  getPayloadAttestationDataSigningRoot(chain.config, data),
128
117
  payloadAttestationMessage.signature
129
118
  );
@@ -135,7 +124,7 @@ async function validatePayloadAttestationMessage(
135
124
  }
136
125
 
137
126
  // Valid
138
- chain.seenPayloadAttesters.add(epoch, validatorIndex);
127
+ chain.seenPayloadAttesters.add(data.slot, validatorIndex);
139
128
 
140
129
  return {
141
130
  attDataRootHex: toRootHex(ssz.gloas.PayloadAttestationData.hashTreeRoot(data)),
@@ -1,9 +1,9 @@
1
1
  import {IForkChoice, ProtoBlock} from "@lodestar/fork-choice";
2
- import {GENESIS_EPOCH, GENESIS_SLOT, MIN_SEED_LOOKAHEAD, SLOTS_PER_EPOCH} from "@lodestar/params";
2
+ import {GENESIS_EPOCH, GENESIS_SLOT, MIN_SEED_LOOKAHEAD, SLOTS_PER_EPOCH, isForkPostGloas} from "@lodestar/params";
3
3
  import {
4
4
  computeEpochAtSlot,
5
5
  computeStartSlotAtEpoch,
6
- createSingleSignatureSetFromComponents,
6
+ createIndexedSignatureSetFromComponents,
7
7
  getProposerPreferencesSigningRoot,
8
8
  } from "@lodestar/state-transition";
9
9
  import {Epoch, Slot, ValidatorIndex, gloas} from "@lodestar/types";
@@ -13,7 +13,7 @@ import {IBeaconChain} from "../index.js";
13
13
 
14
14
  /**
15
15
  * Validates a gossiped `SignedProposerPreferences` per
16
- * https://github.com/ethereum/consensus-specs/blob/v1.7.0-alpha.13/specs/gloas/p2p-interface.md#new-proposer_preferences
16
+ * https://github.com/ethereum/consensus-specs/blob/v1.7.0-alpha.14/specs/gloas/p2p-interface.md#new-proposer_preferences
17
17
  */
18
18
  export async function validateGossipProposerPreferences(
19
19
  chain: IBeaconChain,
@@ -24,6 +24,16 @@ export async function validateGossipProposerPreferences(
24
24
  const dependentRootHex = toRootHex(dependentRoot);
25
25
  const proposalEpoch = computeEpochAtSlot(proposalSlot);
26
26
 
27
+ // [IGNORE] The proposal epoch is after the Gloas upgrade
28
+ const proposalFork = chain.config.getForkName(proposalSlot);
29
+ if (!isForkPostGloas(proposalFork)) {
30
+ throw new ProposerPreferencesError(GossipAction.IGNORE, {
31
+ code: ProposerPreferencesErrorCode.PRE_GLOAS_PROPOSAL_SLOT,
32
+ proposalSlot,
33
+ proposalFork,
34
+ });
35
+ }
36
+
27
37
  // [IGNORE] `preferences.proposal_slot` is within the proposer lookahead,
28
38
  // allowing for `MAXIMUM_GOSSIP_CLOCK_DISPARITY`.
29
39
  const currentEpoch = computeEpochAtSlot(chain.clock.currentSlotWithGossipDisparity);
@@ -129,8 +139,8 @@ export async function validateGossipProposerPreferences(
129
139
  }
130
140
 
131
141
  // [REJECT] `signed_proposer_preferences.signature` is valid with respect to the validator's public key.
132
- const signatureSet = createSingleSignatureSetFromComponents(
133
- chain.pubkeyCache.getOrThrow(validatorIndex),
142
+ const signatureSet = createIndexedSignatureSetFromComponents(
143
+ validatorIndex,
134
144
  getProposerPreferencesSigningRoot(chain.config, preferences),
135
145
  signedProposerPreferences.signature
136
146
  );
@@ -37,7 +37,7 @@ async function validateProposerSlashing(
37
37
  try {
38
38
  const proposer = state.getValidator(proposerSlashing.signedHeader1.message.proposerIndex);
39
39
  // verifySignature = false, verified in batch below
40
- assertValidProposerSlashing(chain.config, chain.pubkeyCache, state.slot, proposerSlashing, proposer, false);
40
+ assertValidProposerSlashing(chain.config, state.slot, proposerSlashing, proposer, false);
41
41
  } catch (e) {
42
42
  throw new ProposerSlashingError(GossipAction.REJECT, {
43
43
  code: ProposerSlashingErrorCode.INVALID,
@@ -71,15 +71,18 @@ export class NoBidReceived extends Error {
71
71
  }
72
72
 
73
73
  /**
74
- * Beacon clients select randomized values from the following ranges when initializing
75
- * the circuit breaker (so at boot time and once for each unique boot).
74
+ * Default circuit breaker parameters:
76
75
  *
77
- * ALLOWED_FAULTS: between 1 and SLOTS_PER_EPOCH // 4
78
- * FAULT_INSPECTION_WINDOW: between SLOTS_PER_EPOCH and 2 * SLOTS_PER_EPOCH
76
+ * SLOTS_PER_EPOCH <= FAULT_INSPECTION_WINDOW < 2 * SLOTS_PER_EPOCH (randomized at initialization)
77
+ * ALLOWED_FAULTS: FAULT_INSPECTION_WINDOW // 4
79
78
  *
80
- * The values are randomized per node so builders cannot predict when a given proposer will
81
- * fall back to local blocks. With fixed thresholds a builder could withhold payloads right up
82
- * to the limit without ever tripping the breaker.
79
+ * e.g. on mainnet SLOTS_PER_EPOCH is 32, so FAULT_INSPECTION_WINDOW is between 32 and 63,
80
+ * and a window of 40 results in ALLOWED_FAULTS = 10.
81
+ *
82
+ * The randomized default window keeps the exact slots under inspection unpredictable per node,
83
+ * so a builder cannot tell when past faults age out of a given proposer's window and time
84
+ * withholding around trip or recovery points. Explicitly configured windows are clamped to at
85
+ * least SLOTS_PER_EPOCH, and configured allowedFaults is capped at the default ~25% budget.
83
86
  */
84
87
  export function getFaultInspectionParams(opts: {faultInspectionWindow?: number; allowedFaults?: number}): {
85
88
  faultInspectionWindow: number;
@@ -90,10 +93,7 @@ export function getFaultInspectionParams(opts: {faultInspectionWindow?: number;
90
93
  SLOTS_PER_EPOCH
91
94
  );
92
95
  // allowedFaults should be < faultInspectionWindow, limiting them to faultInspectionWindow/4
93
- const allowedFaults = Math.min(
94
- opts.allowedFaults ?? Math.floor(faultInspectionWindow / 4),
95
- Math.floor(faultInspectionWindow / 4)
96
- );
96
+ const allowedFaults = Math.min(opts.allowedFaults ?? Infinity, Math.floor(faultInspectionWindow / 4));
97
97
  return {faultInspectionWindow, allowedFaults};
98
98
  }
99
99
 
@@ -143,17 +143,17 @@ export function createBeaconMetrics(register: RegistryMetricCreator) {
143
143
  name: "beacon_builder_circuit_breaker_active",
144
144
  help: "Whether the builder circuit breaker is active (1) causing builder bids to be ignored",
145
145
  }),
146
- faults: register.gauge({
147
- name: "beacon_builder_circuit_breaker_faults",
148
- help: "Count of blocks with unrevealed payloads in the fault inspection window",
146
+ canonicalBlocks: register.gauge({
147
+ name: "beacon_builder_circuit_breaker_canonical_blocks",
148
+ help: "Count of canonical FULL and EMPTY blocks in the fault inspection window",
149
149
  }),
150
- blocksPresent: register.gauge({
151
- name: "beacon_builder_circuit_breaker_blocks_present",
152
- help: "Count of blocks present in the fault inspection window",
150
+ fullBlocks: register.gauge({
151
+ name: "beacon_builder_circuit_breaker_full_blocks",
152
+ help: "Count of canonical FULL blocks in the fault inspection window",
153
153
  }),
154
- payloadsRevealed: register.gauge({
155
- name: "beacon_builder_circuit_breaker_payloads_revealed",
156
- help: "Count of blocks with revealed payloads in the fault inspection window",
154
+ emptyBlocks: register.gauge({
155
+ name: "beacon_builder_circuit_breaker_empty_blocks",
156
+ help: "Count of canonical EMPTY blocks in the fault inspection window",
157
157
  }),
158
158
  },
159
159
 
@@ -31,6 +31,10 @@ import {RegistryMetricCreator} from "../utils/registryMetricCreator.js";
31
31
 
32
32
  export type LodestarMetrics = ReturnType<typeof createLodestarMetrics>;
33
33
 
34
+ type BlsJobOutcome = "valid" | "invalid" | "prepError" | "verifyError" | "workerError";
35
+ type BlsBufferFlushReason = "size" | "timeout";
36
+ type BlsVerificationCallOperation = "batch" | "single" | "fallback" | "same_message";
37
+
34
38
  /**
35
39
  * Extra Lodestar custom metrics
36
40
  */
@@ -396,86 +400,108 @@ export function createLodestarMetrics(
396
400
  // BLS verifier thread pool and queue
397
401
 
398
402
  bls: {
399
- aggregatedPubkeys: register.gauge({
403
+ aggregatedPubkeys: register.counter({
400
404
  name: "lodestar_bls_aggregated_pubkeys_total",
401
- help: "Total aggregated pubkeys for BLS validation",
405
+ help: "Total validator public keys used as inputs to aggregate signature sets",
406
+ }),
407
+ pubkeyCacheSize: register.gauge({
408
+ name: "lodestar_bls_pubkey_cache_size",
409
+ help: "Current number of validator public keys in the native cache",
410
+ }),
411
+ pubkeyCacheCapacity: register.gauge({
412
+ name: "lodestar_bls_pubkey_cache_capacity",
413
+ help: "Number of validator public keys the native cache can hold without growing",
402
414
  }),
403
415
  },
404
416
 
405
417
  blsThreadPool: {
406
- jobsWorkerTime: register.gauge<{workerId: number}>({
418
+ jobsWorkerTime: register.counter<{workerId: number}>({
407
419
  name: "lodestar_bls_thread_pool_time_seconds_sum",
408
- help: "Total time spent verifying signature sets measured on the worker",
420
+ help: "Cumulative wall time spent processing BLS dispatch groups by worker",
409
421
  labelNames: ["workerId"],
410
422
  }),
411
- successJobsSignatureSetsCount: register.gauge({
423
+ successJobsSignatureSetsCount: register.counter({
412
424
  name: "lodestar_bls_thread_pool_success_jobs_signature_sets_count",
413
- help: "Count of total verified signature sets",
425
+ help: "Count of signature sets completed without an operational error",
414
426
  }),
415
- errorAggregateSignatureSetsCount: register.gauge<{type: JobQueueItemType}>({
416
- name: "lodestar_bls_thread_pool_error_aggregate_signature_sets_count",
417
- help: "Count of error when aggregating pubkeys or signatures",
418
- labelNames: ["type"],
419
- }),
420
- errorJobsSignatureSetsCount: register.gauge({
427
+ errorJobsSignatureSetsCount: register.counter({
421
428
  name: "lodestar_bls_thread_pool_error_jobs_signature_sets_count",
422
- help: "Count of total error-ed signature sets",
429
+ help: "Count of signature sets that ended in an input, binding, or worker error",
423
430
  }),
424
- jobWaitTime: register.histogram({
431
+ jobWaitTime: register.histogram<{type: JobQueueItemType}>({
425
432
  name: "lodestar_bls_thread_pool_queue_job_wait_time_seconds",
426
- help: "Time from job added to the queue to starting the job in seconds",
427
- buckets: [0.01, 0.02, 0.1, 0.3, 0.5, 1],
433
+ help: "Time from adding a BLS job until it is selected for a worker dispatch group",
434
+ labelNames: ["type"],
435
+ buckets: [0.005, 0.01, 0.025, 0.05, 0.1, 0.2, 0.5, 1, 2],
436
+ }),
437
+ jobDuration: register.histogram<{type: JobQueueItemType}>({
438
+ name: "lodestar_bls_thread_pool_job_duration_seconds",
439
+ help: "End-to-end duration of a BLS job from submission through completion",
440
+ labelNames: ["type"],
441
+ buckets: [0.005, 0.01, 0.025, 0.05, 0.1, 0.2, 0.5, 1, 2],
442
+ }),
443
+ jobResults: register.counter<{type: JobQueueItemType; outcome: BlsJobOutcome}>({
444
+ name: "lodestar_bls_thread_pool_job_results_total",
445
+ help: "Count of completed BLS jobs by cryptographic or operational outcome",
446
+ labelNames: ["type", "outcome"],
428
447
  }),
429
448
  queueLength: register.gauge({
430
449
  name: "lodestar_bls_thread_pool_queue_length",
431
- help: "Count of total block processor queue length",
450
+ help: "Count of ready BLS jobs waiting for a worker",
451
+ }),
452
+ bufferedQueueLength: register.gauge({
453
+ name: "lodestar_bls_thread_pool_buffered_queue_length",
454
+ help: "Count of BLS jobs waiting in the intentional batching buffer",
432
455
  }),
433
456
  workersBusy: register.gauge({
434
457
  name: "lodestar_bls_thread_pool_workers_busy",
435
458
  help: "Count of current busy workers",
436
459
  }),
437
- totalJobsGroupsStarted: register.gauge({
460
+ totalJobsGroupsStarted: register.counter({
438
461
  name: "lodestar_bls_thread_pool_job_groups_started_total",
439
- help: "Count of total jobs groups started in bls thread pool, job groups include +1 jobs",
462
+ help: "Count of worker dispatch groups started by the BLS thread pool",
440
463
  }),
441
- totalJobsStarted: register.gauge<{type: JobQueueItemType}>({
464
+ totalJobsStarted: register.counter<{type: JobQueueItemType}>({
442
465
  name: "lodestar_bls_thread_pool_jobs_started_total",
443
- help: "Count of total jobs started in bls thread pool, jobs include +1 signature sets",
466
+ help: "Count of jobs started by the BLS thread pool",
444
467
  labelNames: ["type"],
445
468
  }),
446
- totalSigSetsStarted: register.gauge<{type: JobQueueItemType}>({
469
+ totalSigSetsStarted: register.counter<{type: JobQueueItemType}>({
447
470
  name: "lodestar_bls_thread_pool_sig_sets_started_total",
448
- help: "Count of total signature sets started in bls thread pool, sig sets include 1 pk, msg, sig",
471
+ help: "Count of signature sets started by the BLS thread pool",
449
472
  labelNames: ["type"],
450
473
  }),
451
474
  // Re-verifying a batch means doing double work. This number must be very low or it can be a waste of CPU resources
452
- batchRetries: register.gauge({
475
+ batchRetries: register.counter({
453
476
  name: "lodestar_bls_thread_pool_batch_retries_total",
454
- help: "Count of total batches that failed and had to be verified again.",
477
+ help: "Count of combined batches that failed and required per-job fallback",
455
478
  }),
456
479
  // To count how many sigs are being validated with the optimization of batching them
457
- batchSigsSuccess: register.gauge({
480
+ batchSigsSuccess: register.counter({
458
481
  name: "lodestar_bls_thread_pool_batch_sigs_success_total",
459
- help: "Count of total batches that failed and had to be verified again.",
482
+ help: "Count of signature sets accepted by combined batch verification without fallback",
460
483
  }),
461
- sameMessageRetryJobs: register.gauge({
462
- name: "lodestar_bls_thread_pool_same_message_jobs_retries_total",
463
- help: "Count of total same message jobs that failed and had to be verified again.",
484
+ bufferFlushes: register.counter<{reason: BlsBufferFlushReason}>({
485
+ name: "lodestar_bls_thread_pool_buffer_flushes_total",
486
+ help: "Count of batching-buffer flushes by trigger",
487
+ labelNames: ["reason"],
464
488
  }),
465
- sameMessageRetrySets: register.gauge({
466
- name: "lodestar_bls_thread_pool_same_message_sets_retries_total",
467
- help: "Count of total same message sets that failed and had to be verified again.",
489
+ bufferedSignatureSets: register.histogram({
490
+ name: "lodestar_bls_thread_pool_buffered_signature_sets",
491
+ help: "Count of signature sets included in each batching-buffer flush",
492
+ buckets: [1, 8, 16, 24, 28, 32, 36, 40, 44, 48, 64, 128],
468
493
  }),
469
494
  // To measure the time cost of main thread <-> worker message passing
470
495
  latencyToWorker: register.histogram({
471
496
  name: "lodestar_bls_thread_pool_latency_to_worker",
472
497
  help: "Time from sending the job to the worker and the worker receiving it",
473
- buckets: [0.001, 0.003, 0.01, 0.03, 0.1],
498
+ // The dashboard treats 20ms as slow; values above 100ms only need severe-stall classification.
499
+ buckets: [0.00025, 0.001, 0.005, 0.02, 0.1],
474
500
  }),
475
501
  latencyFromWorker: register.histogram({
476
502
  name: "lodestar_bls_thread_pool_latency_from_worker",
477
503
  help: "Time from the worker sending the result and the main thread receiving it",
478
- buckets: [0.001, 0.003, 0.01, 0.03, 0.1],
504
+ buckets: [0.00025, 0.001, 0.005, 0.02, 0.1],
479
505
  }),
480
506
  mainThreadDurationInThreadPool: register.histogram({
481
507
  name: "lodestar_bls_thread_pool_main_thread_time_seconds",
@@ -485,32 +511,40 @@ export function createLodestarMetrics(
485
511
  }),
486
512
  timePerSigSet: register.histogram({
487
513
  name: "lodestar_bls_worker_thread_time_per_sigset_seconds",
488
- help: "Time to verify each sigset with worker thread mode",
514
+ help: "Average BLS worker dispatch wall time per original signature set",
489
515
  // Time per sig ~0.9ms on good machines
490
- buckets: [0.5e-3, 0.75e-3, 1e-3, 1.5e-3, 2e-3, 5e-3],
491
- }),
492
- totalSigSets: register.gauge({
516
+ buckets: [0.5e-3, 0.75e-3, 1e-3, 1.5e-3, 2e-3, 5e-3, 10e-3, 20e-3, 50e-3, 100e-3],
517
+ }),
518
+ workRequestPreparationDuration: register.histogram({
519
+ name: "lodestar_bls_thread_pool_work_request_preparation_duration_seconds",
520
+ help: "Main-thread time spent converting one worker dispatch group to binding inputs",
521
+ // Normal requests take microseconds; the dashboard treats 1ms as a main-thread stall.
522
+ buckets: [0.000005, 0.000025, 0.0001, 0.0005, 0.001, 0.005, 0.025],
523
+ }),
524
+ verificationCallDuration: register.histogram<{operation: BlsVerificationCallOperation}>({
525
+ name: "lodestar_bls_thread_pool_verification_call_duration_seconds",
526
+ help: "Duration of one worker-side lodestar-z verification binding call, including input conversion",
527
+ labelNames: ["operation"],
528
+ // Verification ranges from sub-millisecond calls to large batches; above 500ms is uniformly severe.
529
+ buckets: [0.00025, 0.001, 0.005, 0.02, 0.1, 0.5],
530
+ }),
531
+ verificationCallSignatureSets: register.counter<{operation: BlsVerificationCallOperation}>({
532
+ name: "lodestar_bls_thread_pool_verification_call_signature_sets_total",
533
+ help: "Count of signature sets passed to worker-side lodestar-z verification binding calls",
534
+ labelNames: ["operation"],
535
+ }),
536
+ totalSigSets: register.counter({
493
537
  name: "lodestar_bls_thread_pool_sig_sets_total",
494
538
  help: "Count of total signature sets",
495
539
  }),
496
- prioritizedSigSets: register.gauge({
540
+ prioritizedSigSets: register.counter({
497
541
  name: "lodestar_bls_thread_pool_prioritized_sig_sets_total",
498
542
  help: "Count of total prioritized signature sets",
499
543
  }),
500
- batchableSigSets: register.gauge({
544
+ batchableSigSets: register.counter({
501
545
  name: "lodestar_bls_thread_pool_batchable_sig_sets_total",
502
546
  help: "Count of total batchable signature sets",
503
547
  }),
504
- aggregateWithRandomnessAsyncDuration: register.histogram({
505
- name: "lodestar_bls_thread_pool_aggregate_with_randomness_async_time_seconds",
506
- help: "Total time performing aggregateWithRandomness async",
507
- buckets: [0.001, 0.005, 0.01, 0.1, 0.3],
508
- }),
509
- pubkeysAggregationMainThreadDuration: register.histogram({
510
- name: "lodestar_bls_thread_pool_pubkeys_aggregation_main_thread_time_seconds",
511
- help: "Total time spent aggregating pubkeys on main thread",
512
- buckets: [0.001, 0.005, 0.01, 0.1],
513
- }),
514
548
  },
515
549
 
516
550
  // BLS time on single thread mode
@@ -908,6 +942,22 @@ export function createLodestarMetrics(
908
942
  labelNames: ["error"],
909
943
  }),
910
944
  },
945
+ gossipExecutionPayloadBid: {
946
+ elapsedTimeTillReceived: register.histogram<{source: OpSource}>({
947
+ name: "lodestar_gossip_execution_payload_bid_elapsed_time_till_received_seconds",
948
+ help: "Time elapsed between the bid's slot start and the time the execution payload bid was received (negative = received before its slot, broadcast in the previous slot; positive = received during its own slot, late)",
949
+ labelNames: ["source"],
950
+ buckets: [-6, -3, -1, 0, 3],
951
+ }),
952
+ },
953
+ gossipPayloadAttestationMessage: {
954
+ elapsedTimeTillReceived: register.histogram<{source: OpSource}>({
955
+ name: "lodestar_gossip_payload_attestation_message_elapsed_time_till_received_seconds",
956
+ help: "Time elapsed between slot time and the time the payload attestation message was received",
957
+ labelNames: ["source"],
958
+ buckets: [3, 6, 9, 12],
959
+ }),
960
+ },
911
961
  // recovery in the case of specific blob rows required
912
962
  recoverBlobSidecars: {
913
963
  blobsReconstructed: register.counter({