@lodestar/beacon-node 1.47.0-dev.92bd4efe9b → 1.47.0-dev.931ecbec99

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 (176) 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 -6
  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 +4 -0
  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/chain/archiveStore/historicalState/metrics.d.ts.map +1 -1
  15. package/lib/chain/archiveStore/historicalState/metrics.js +21 -0
  16. package/lib/chain/archiveStore/historicalState/metrics.js.map +1 -1
  17. package/lib/chain/blocks/importBlock.d.ts.map +1 -1
  18. package/lib/chain/blocks/importBlock.js +2 -7
  19. package/lib/chain/blocks/importBlock.js.map +1 -1
  20. package/lib/chain/blocks/importExecutionPayload.d.ts.map +1 -1
  21. package/lib/chain/blocks/importExecutionPayload.js +1 -1
  22. package/lib/chain/blocks/importExecutionPayload.js.map +1 -1
  23. package/lib/chain/blocks/verifyExecutionPayloadEnvelope.d.ts +2 -2
  24. package/lib/chain/blocks/verifyExecutionPayloadEnvelope.d.ts.map +1 -1
  25. package/lib/chain/blocks/verifyExecutionPayloadEnvelope.js +2 -2
  26. package/lib/chain/blocks/verifyExecutionPayloadEnvelope.js.map +1 -1
  27. package/lib/chain/bls/index.d.ts +1 -1
  28. package/lib/chain/bls/index.d.ts.map +1 -1
  29. package/lib/chain/bls/interface.d.ts +5 -5
  30. package/lib/chain/bls/interface.d.ts.map +1 -1
  31. package/lib/chain/bls/multithread/index.d.ts +5 -11
  32. package/lib/chain/bls/multithread/index.d.ts.map +1 -1
  33. package/lib/chain/bls/multithread/index.js +116 -101
  34. package/lib/chain/bls/multithread/index.js.map +1 -1
  35. package/lib/chain/bls/multithread/jobItem.d.ts +5 -19
  36. package/lib/chain/bls/multithread/jobItem.d.ts.map +1 -1
  37. package/lib/chain/bls/multithread/jobItem.js +10 -65
  38. package/lib/chain/bls/multithread/jobItem.js.map +1 -1
  39. package/lib/chain/bls/multithread/types.d.ts +29 -8
  40. package/lib/chain/bls/multithread/types.d.ts.map +1 -1
  41. package/lib/chain/bls/multithread/types.js +12 -0
  42. package/lib/chain/bls/multithread/types.js.map +1 -1
  43. package/lib/chain/bls/multithread/utils.d.ts +4 -0
  44. package/lib/chain/bls/multithread/utils.d.ts.map +1 -1
  45. package/lib/chain/bls/multithread/utils.js +10 -0
  46. package/lib/chain/bls/multithread/utils.js.map +1 -1
  47. package/lib/chain/bls/multithread/worker.js +49 -23
  48. package/lib/chain/bls/multithread/worker.js.map +1 -1
  49. package/lib/chain/bls/singleThread.d.ts +4 -10
  50. package/lib/chain/bls/singleThread.d.ts.map +1 -1
  51. package/lib/chain/bls/singleThread.js +8 -45
  52. package/lib/chain/bls/singleThread.js.map +1 -1
  53. package/lib/chain/bls/utils.d.ts +4 -4
  54. package/lib/chain/bls/utils.d.ts.map +1 -1
  55. package/lib/chain/bls/utils.js +18 -19
  56. package/lib/chain/bls/utils.js.map +1 -1
  57. package/lib/chain/chain.d.ts +2 -1
  58. package/lib/chain/chain.d.ts.map +1 -1
  59. package/lib/chain/chain.js +6 -4
  60. package/lib/chain/chain.js.map +1 -1
  61. package/lib/chain/errors/proposerPreferences.d.ts +6 -0
  62. package/lib/chain/errors/proposerPreferences.d.ts.map +1 -1
  63. package/lib/chain/errors/proposerPreferences.js +1 -0
  64. package/lib/chain/errors/proposerPreferences.js.map +1 -1
  65. package/lib/chain/interface.d.ts +2 -1
  66. package/lib/chain/interface.d.ts.map +1 -1
  67. package/lib/chain/interface.js.map +1 -1
  68. package/lib/chain/opPools/aggregatedAttestationPool.js +1 -1
  69. package/lib/chain/opPools/aggregatedAttestationPool.js.map +1 -1
  70. package/lib/chain/opPools/attestationPool.d.ts +1 -1
  71. package/lib/chain/opPools/attestationPool.d.ts.map +1 -1
  72. package/lib/chain/opPools/attestationPool.js +1 -1
  73. package/lib/chain/opPools/attestationPool.js.map +1 -1
  74. package/lib/chain/opPools/payloadAttestationPool.js +3 -3
  75. package/lib/chain/opPools/payloadAttestationPool.js.map +1 -1
  76. package/lib/chain/opPools/syncCommitteeMessagePool.js +1 -1
  77. package/lib/chain/opPools/syncCommitteeMessagePool.js.map +1 -1
  78. package/lib/chain/opPools/syncContributionAndProofPool.js +1 -1
  79. package/lib/chain/opPools/syncContributionAndProofPool.js.map +1 -1
  80. package/lib/chain/opPools/utils.d.ts +1 -1
  81. package/lib/chain/opPools/utils.d.ts.map +1 -1
  82. package/lib/chain/opPools/utils.js +1 -1
  83. package/lib/chain/opPools/utils.js.map +1 -1
  84. package/lib/chain/seenCache/seenAttesters.d.ts +9 -5
  85. package/lib/chain/seenCache/seenAttesters.d.ts.map +1 -1
  86. package/lib/chain/seenCache/seenAttesters.js +25 -4
  87. package/lib/chain/seenCache/seenAttesters.js.map +1 -1
  88. package/lib/chain/validation/attestation.d.ts.map +1 -1
  89. package/lib/chain/validation/attestation.js +1 -4
  90. package/lib/chain/validation/attestation.js.map +1 -1
  91. package/lib/chain/validation/attesterSlashing.d.ts.map +1 -1
  92. package/lib/chain/validation/attesterSlashing.js +1 -1
  93. package/lib/chain/validation/attesterSlashing.js.map +1 -1
  94. package/lib/chain/validation/executionPayloadBid.d.ts.map +1 -1
  95. package/lib/chain/validation/executionPayloadBid.js +1 -2
  96. package/lib/chain/validation/executionPayloadBid.js.map +1 -1
  97. package/lib/chain/validation/executionPayloadEnvelope.js +1 -1
  98. package/lib/chain/validation/executionPayloadEnvelope.js.map +1 -1
  99. package/lib/chain/validation/payloadAttestationMessage.d.ts.map +1 -1
  100. package/lib/chain/validation/payloadAttestationMessage.js +5 -14
  101. package/lib/chain/validation/payloadAttestationMessage.js.map +1 -1
  102. package/lib/chain/validation/proposerPreferences.d.ts +1 -1
  103. package/lib/chain/validation/proposerPreferences.d.ts.map +1 -1
  104. package/lib/chain/validation/proposerPreferences.js +13 -4
  105. package/lib/chain/validation/proposerPreferences.js.map +1 -1
  106. package/lib/chain/validation/proposerSlashing.js +1 -1
  107. package/lib/chain/validation/proposerSlashing.js.map +1 -1
  108. package/lib/metrics/metrics/lodestar.d.ts +48 -18
  109. package/lib/metrics/metrics/lodestar.d.ts.map +1 -1
  110. package/lib/metrics/metrics/lodestar.js +96 -50
  111. package/lib/metrics/metrics/lodestar.js.map +1 -1
  112. package/lib/network/libp2p/index.d.ts.map +1 -1
  113. package/lib/network/libp2p/index.js +10 -2
  114. package/lib/network/libp2p/index.js.map +1 -1
  115. package/lib/network/processor/gossipHandlers.d.ts.map +1 -1
  116. package/lib/network/processor/gossipHandlers.js +7 -2
  117. package/lib/network/processor/gossipHandlers.js.map +1 -1
  118. package/lib/network/reqresp/score.d.ts.map +1 -1
  119. package/lib/network/reqresp/score.js +5 -11
  120. package/lib/network/reqresp/score.js.map +1 -1
  121. package/lib/node/nodejs.d.ts +2 -1
  122. package/lib/node/nodejs.d.ts.map +1 -1
  123. package/lib/node/nodejs.js.map +1 -1
  124. package/lib/sync/types.d.ts.map +1 -1
  125. package/lib/sync/types.js.map +1 -1
  126. package/lib/sync/unknownBlock.d.ts +9 -0
  127. package/lib/sync/unknownBlock.d.ts.map +1 -1
  128. package/lib/sync/unknownBlock.js +93 -9
  129. package/lib/sync/unknownBlock.js.map +1 -1
  130. package/package.json +15 -15
  131. package/src/api/impl/beacon/blocks/index.ts +4 -6
  132. package/src/api/impl/beacon/pool/index.ts +5 -0
  133. package/src/api/impl/beacon/state/utils.ts +2 -1
  134. package/src/api/impl/events/index.ts +10 -4
  135. package/src/chain/archiveStore/historicalState/metrics.ts +22 -0
  136. package/src/chain/blocks/importBlock.ts +1 -6
  137. package/src/chain/blocks/importExecutionPayload.ts +0 -1
  138. package/src/chain/blocks/verifyExecutionPayloadEnvelope.ts +1 -9
  139. package/src/chain/bls/index.ts +1 -1
  140. package/src/chain/bls/interface.ts +7 -3
  141. package/src/chain/bls/multithread/index.ts +148 -120
  142. package/src/chain/bls/multithread/jobItem.ts +12 -82
  143. package/src/chain/bls/multithread/types.ts +33 -8
  144. package/src/chain/bls/multithread/utils.ts +11 -0
  145. package/src/chain/bls/multithread/worker.ts +85 -27
  146. package/src/chain/bls/singleThread.ts +11 -55
  147. package/src/chain/bls/utils.ts +23 -24
  148. package/src/chain/chain.ts +7 -5
  149. package/src/chain/errors/proposerPreferences.ts +7 -0
  150. package/src/chain/interface.ts +2 -1
  151. package/src/chain/opPools/aggregatedAttestationPool.ts +1 -1
  152. package/src/chain/opPools/attestationPool.ts +1 -1
  153. package/src/chain/opPools/payloadAttestationPool.ts +3 -3
  154. package/src/chain/opPools/syncCommitteeMessagePool.ts +1 -1
  155. package/src/chain/opPools/syncContributionAndProofPool.ts +1 -1
  156. package/src/chain/opPools/utils.ts +1 -1
  157. package/src/chain/seenCache/seenAttesters.ts +31 -5
  158. package/src/chain/validation/attestation.ts +1 -7
  159. package/src/chain/validation/attesterSlashing.ts +1 -8
  160. package/src/chain/validation/executionPayloadBid.ts +1 -2
  161. package/src/chain/validation/executionPayloadEnvelope.ts +0 -1
  162. package/src/chain/validation/payloadAttestationMessage.ts +6 -17
  163. package/src/chain/validation/proposerPreferences.ts +15 -5
  164. package/src/chain/validation/proposerSlashing.ts +1 -1
  165. package/src/metrics/metrics/lodestar.ts +101 -51
  166. package/src/network/libp2p/index.ts +11 -2
  167. package/src/network/processor/gossipHandlers.ts +9 -0
  168. package/src/network/reqresp/score.ts +5 -11
  169. package/src/node/nodejs.ts +2 -1
  170. package/src/sync/types.ts +3 -1
  171. package/src/sync/unknownBlock.ts +108 -12
  172. package/lib/chain/bls/maybeBatch.d.ts +0 -12
  173. package/lib/chain/bls/maybeBatch.d.ts.map +0 -1
  174. package/lib/chain/bls/maybeBatch.js +0 -35
  175. package/lib/chain/bls/maybeBatch.js.map +0 -1
  176. package/src/chain/bls/maybeBatch.ts +0 -45
@@ -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,
@@ -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({
@@ -1,3 +1,4 @@
1
+ import {getCiphers} from "node:crypto";
1
2
  import {bootstrap} from "@libp2p/bootstrap";
2
3
  import {identify} from "@libp2p/identify";
3
4
  import type {PrivateKey} from "@libp2p/interface";
@@ -9,12 +10,20 @@ import {Libp2pInit, createLibp2p} from "libp2p";
9
10
  import {Registry} from "prom-client";
10
11
  import {ENR} from "@chainsafe/enr";
11
12
  import {noise} from "@chainsafe/libp2p-noise";
12
- import {defaultCrypto} from "@chainsafe/libp2p-noise/crypto";
13
+ import {asCrypto, defaultCrypto} from "@chainsafe/libp2p-noise/crypto";
13
14
  import {quic} from "@chainsafe/libp2p-quic";
14
15
  import {Libp2p, LodestarComponents} from "../interface.js";
15
16
  import {NetworkOptions, defaultNetworkOptions} from "../options.js";
16
17
  import {Eth2PeerDataStore} from "../peers/datastore.js";
17
18
 
19
+ const noiseCrypto = getCiphers().includes("chacha20-poly1305")
20
+ ? defaultCrypto
21
+ : {
22
+ ...defaultCrypto,
23
+ chaCha20Poly1305Encrypt: asCrypto.chaCha20Poly1305Encrypt,
24
+ chaCha20Poly1305Decrypt: asCrypto.chaCha20Poly1305Decrypt,
25
+ };
26
+
18
27
  export type NodeJsLibp2pOpts = {
19
28
  peerStoreDir?: string;
20
29
  disablePeerDiscovery?: boolean;
@@ -122,7 +131,7 @@ export async function createNodeJsLibp2p(
122
131
  listen: localMultiaddrs,
123
132
  announce: [],
124
133
  },
125
- connectionEncrypters: [noise({crypto: defaultCrypto})],
134
+ connectionEncrypters: [noise({crypto: noiseCrypto})],
126
135
  transports,
127
136
  streamMuxers: [mplex({disconnectThreshold})],
128
137
  peerDiscovery,
@@ -1255,11 +1255,15 @@ function getSequentialHandlers(modules: ValidatorFnsModules, options: GossipHand
1255
1255
  [GossipType.payload_attestation_message]: async ({
1256
1256
  gossipData,
1257
1257
  topic,
1258
+ seenTimestampSec,
1258
1259
  }: GossipHandlerParamGeneric<GossipType.payload_attestation_message>) => {
1259
1260
  const {serializedData} = gossipData;
1260
1261
  const payloadAttestationMessage = sszDeserialize(topic, serializedData);
1261
1262
  const validationResult = await validateGossipPayloadAttestationMessage(chain, payloadAttestationMessage);
1262
1263
 
1264
+ const delaySec = chain.clock.secFromSlot(payloadAttestationMessage.data.slot, seenTimestampSec);
1265
+ metrics?.gossipPayloadAttestationMessage.elapsedTimeTillReceived.observe({source: OpSource.gossip}, delaySec);
1266
+
1263
1267
  try {
1264
1268
  const insertOutcome = chain.payloadAttestationPool.add(
1265
1269
  payloadAttestationMessage,
@@ -1286,11 +1290,16 @@ function getSequentialHandlers(modules: ValidatorFnsModules, options: GossipHand
1286
1290
  [GossipType.execution_payload_bid]: async ({
1287
1291
  gossipData,
1288
1292
  topic,
1293
+ seenTimestampSec,
1289
1294
  }: GossipHandlerParamGeneric<GossipType.execution_payload_bid>) => {
1290
1295
  const {serializedData} = gossipData;
1291
1296
  const executionPayloadBid = sszDeserialize(topic, serializedData);
1292
1297
  const {proposerIndex} = await validateGossipExecutionPayloadBid(chain, executionPayloadBid);
1293
1298
 
1299
+ // this could be negative, because it's most likely the bid of next slot comes at this clock slot
1300
+ const elapsedSec = chain.clock.secFromSlot(executionPayloadBid.message.slot, seenTimestampSec);
1301
+ metrics?.gossipExecutionPayloadBid.elapsedTimeTillReceived.observe({source: OpSource.gossip}, elapsedSec);
1302
+
1294
1303
  // Handle valid payload bid by storing in a bid pool
1295
1304
  try {
1296
1305
  const insertOutcome = chain.executionPayloadBidPool.add(executionPayloadBid);
@@ -44,17 +44,11 @@ export function onOutgoingReqRespError(e: RequestError, method: ReqRespMethod):
44
44
  // failure, so keep the stronger penalty (Fatal for Ping, as before).
45
45
  return method === ReqRespMethod.Ping ? PeerAction.Fatal : PeerAction.LowToleranceError;
46
46
  }
47
- switch (method) {
48
- // Ping and Status are liveness probes; their dial timeouts are dominated by transient
49
- // network congestion rather than peer misbehavior, so penalize leniently to avoid
50
- // self-inflicted peer starvation (https://github.com/ChainSafe/lodestar/issues/9562),
51
- // while still applying some penalty so genuinely dead peers eventually free the slot.
52
- case ReqRespMethod.Ping:
53
- case ReqRespMethod.Status:
54
- return PeerAction.HighToleranceError;
55
- default:
56
- return PeerAction.LowToleranceError;
57
- }
47
+ // A dial failure means we could not open a stream. That is evidence about the transport, not
48
+ // about the peer's behavior, and it is dominated by transient congestion on our side, so
49
+ // penalize leniently for every method to avoid self-inflicted peer starvation (#9562). A
50
+ // genuinely dead peer still accumulates penalty and is eventually evicted, freeing the slot.
51
+ return PeerAction.HighToleranceError;
58
52
  // TODO: Detect SSZDecodeError and return PeerAction.Fatal
59
53
 
60
54
  case RequestErrorCode.RESP_TIMEOUT:
@@ -1,12 +1,13 @@
1
1
  import {setMaxListeners} from "node:events";
2
2
  import {PrivateKey} from "@libp2p/interface";
3
3
  import {Registry} from "prom-client";
4
+ import {type PubkeyCache} from "@chainsafe/lodestar-z/pubkeys";
4
5
  import {hasher} from "@chainsafe/persistent-merkle-tree";
5
6
  import {BeaconApiMethods} from "@lodestar/api/beacon/server";
6
7
  import {BeaconConfig} from "@lodestar/config";
7
8
  import type {LoggerNode} from "@lodestar/logger/node";
8
9
  import {ZERO_HASH_HEX} from "@lodestar/params";
9
- import {IBeaconStateView, PubkeyCache, isStatePostBellatrix, isStatePostGloas} from "@lodestar/state-transition";
10
+ import {IBeaconStateView, isStatePostBellatrix, isStatePostGloas} from "@lodestar/state-transition";
10
11
  import {phase0} from "@lodestar/types";
11
12
  import {sleep, toRootHex} from "@lodestar/utils";
12
13
  import {ProcessShutdownCallback} from "@lodestar/validator";
package/src/sync/types.ts CHANGED
@@ -77,7 +77,9 @@ export type PendingPayloadInput = {
77
77
  export type PendingPayloadRootHex = {
78
78
  status: PendingPayloadInputStatus.pending | PendingPayloadInputStatus.fetching;
79
79
  rootHex: RootHex;
80
- // message slot hint, may be missing when resolving a parent payload
80
+ // Trusted slot only (fork choice / validated data), may be missing until resolved. NOT the gossip
81
+ // message slot from ChainEvent.unknownEnvelopeBlockRoot, which is untrusted and not necessarily the
82
+ // payload/block slot. See BlockInputSync.resolvePayloadSlot.
81
83
  slot?: Slot;
82
84
  timeAddedSec: number;
83
85
  timeSyncedSec?: number;
@@ -1,5 +1,6 @@
1
1
  import {routes} from "@lodestar/api";
2
2
  import {ChainForkConfig} from "@lodestar/config";
3
+ import {CheckpointWithHex} from "@lodestar/fork-choice";
3
4
  import {ForkSeq, isForkPostGloas} from "@lodestar/params";
4
5
  import {RequestError, RequestErrorCode} from "@lodestar/reqresp";
5
6
  import {computeTimeAtSlot} from "@lodestar/state-transition";
@@ -154,6 +155,7 @@ export class BlockInputSync {
154
155
  this.chain.emitter.on(ChainEvent.incompleteBlockInput, this.onIncompleteBlockInput);
155
156
  this.chain.emitter.on(ChainEvent.incompletePayloadEnvelope, this.onIncompletePayloadEnvelope);
156
157
  this.chain.emitter.on(ChainEvent.blockUnknownParent, this.onUnknownParent);
158
+ this.chain.emitter.on(ChainEvent.forkChoiceFinalized, this.pruneFinalized);
157
159
  this.chain.emitter.on(routes.events.EventType.block, this.onBlockImported);
158
160
  this.chain.emitter.on(routes.events.EventType.executionPayload, this.onPayloadImported);
159
161
  this.network.events.on(NetworkEvent.peerConnected, this.onPeerConnected);
@@ -170,6 +172,7 @@ export class BlockInputSync {
170
172
  this.chain.emitter.off(ChainEvent.incompleteBlockInput, this.onIncompleteBlockInput);
171
173
  this.chain.emitter.off(ChainEvent.incompletePayloadEnvelope, this.onIncompletePayloadEnvelope);
172
174
  this.chain.emitter.off(ChainEvent.blockUnknownParent, this.onUnknownParent);
175
+ this.chain.emitter.off(ChainEvent.forkChoiceFinalized, this.pruneFinalized);
173
176
  this.chain.emitter.off(routes.events.EventType.block, this.onBlockImported);
174
177
  this.chain.emitter.off(routes.events.EventType.executionPayload, this.onPayloadImported);
175
178
  this.network.events.off(NetworkEvent.peerConnected, this.onPeerConnected);
@@ -215,7 +218,7 @@ export class BlockInputSync {
215
218
 
216
219
  private onUnknownEnvelopeBlockRoot = (data: ChainEventData[ChainEvent.unknownEnvelopeBlockRoot]): void => {
217
220
  try {
218
- this.addByPayloadRootHex(data.rootHex, data.peer, data.slot);
221
+ this.addByPayloadRootHex(data.rootHex, data.peer, this.resolvePayloadSlot(data.rootHex));
219
222
  this.triggerUnknownBlockSearch();
220
223
  this.metrics?.blockInputSync.requests.inc({type: PendingBlockType.UNKNOWN_PAYLOAD_BLOCK_ROOT});
221
224
  this.metrics?.blockInputSync.payloadSource.inc({source: data.source});
@@ -258,7 +261,7 @@ export class BlockInputSync {
258
261
  }
259
262
 
260
263
  if (missingDependency.kind === "parentPayload") {
261
- this.addByPayloadRootHex(missingDependency.rootHex, data.peer);
264
+ this.addByPayloadRootHex(missingDependency.rootHex, data.peer, missingDependency.slot);
262
265
  } else if (missingDependency.kind === "parentBlock") {
263
266
  this.addByRootHex(missingDependency.rootHex, data.peer);
264
267
  }
@@ -284,6 +287,61 @@ export class BlockInputSync {
284
287
  this.triggerUnknownBlockSearch();
285
288
  };
286
289
 
290
+ /**
291
+ * Drop pending payloads and blocks that fell below the finalized slot.
292
+ * See https://github.com/ChainSafe/lodestar/issues/9800
293
+ */
294
+ private pruneFinalized = (checkpoint: CheckpointWithHex): void => {
295
+ // Use the actual finalized block slot, not computeStartSlotAtEpoch(checkpoint.epoch)
296
+ // because the payload at the finalized block is not finalized
297
+ const finalizedSlot = this.chain.forkChoice.getFinalizedBlock().slot;
298
+
299
+ let deletedPayloads = 0;
300
+ let unresolvedSlotPayloads = 0;
301
+ for (const [rootHex, payload] of this.pendingPayloads) {
302
+ // Last-effort resolve for a slot-less PendingPayloadRootHex, and store it back for later passes.
303
+ if (!isPendingPayloadInput(payload) && !isPendingPayloadEnvelope(payload) && payload.slot === undefined) {
304
+ payload.slot = this.resolvePayloadSlot(rootHex);
305
+ }
306
+
307
+ const slot = getPayloadSyncCacheItemSlot(payload);
308
+ if (typeof slot !== "number") {
309
+ unresolvedSlotPayloads++;
310
+ this.logger.debug("BlockInputSync.pruneFinalized: pending payload with unresolved slot", {
311
+ root: rootHex,
312
+ finalizedSlot,
313
+ });
314
+ continue;
315
+ }
316
+
317
+ if (slot < finalizedSlot) {
318
+ this.pendingPayloads.delete(rootHex);
319
+ deletedPayloads++;
320
+ }
321
+ }
322
+
323
+ let deletedBlocks = 0;
324
+ for (const [rootHex, block] of this.pendingBlocks) {
325
+ const slot = getBlockInputSyncCacheItemSlot(block);
326
+ if (typeof slot === "number" && slot < finalizedSlot) {
327
+ this.pendingBlocks.delete(rootHex);
328
+ deletedBlocks++;
329
+ }
330
+ }
331
+
332
+ if (deletedPayloads > 0 || deletedBlocks > 0) {
333
+ this.metrics?.blockInputSync.removedBlocks.inc(deletedPayloads + deletedBlocks);
334
+ }
335
+
336
+ this.logger.debug("BlockInputSync.pruneFinalized dropped pre-finalized pending items", {
337
+ finalizedSlot,
338
+ finalizedRoot: checkpoint.rootHex,
339
+ deletedPayloads,
340
+ deletedBlocks,
341
+ unresolvedSlotPayloads,
342
+ });
343
+ };
344
+
287
345
  private addByRootHex = (rootHex: RootHex, peerIdStr?: PeerIdStr): boolean => {
288
346
  let pendingBlock = this.pendingBlocks.get(rootHex);
289
347
  let added = false;
@@ -382,6 +440,29 @@ export class BlockInputSync {
382
440
  return added;
383
441
  };
384
442
 
443
+ /**
444
+ * Resolve the block slot for a payload root.
445
+ */
446
+ private resolvePayloadSlot(rootHex: RootHex): Slot | undefined {
447
+ // a pending block of the same root
448
+ const pendingBlock = this.pendingBlocks.get(rootHex);
449
+ if (pendingBlock) {
450
+ const pendingBlockSlot = getBlockInputSyncCacheItemSlot(pendingBlock);
451
+ if (typeof pendingBlockSlot === "number") {
452
+ return pendingBlockSlot;
453
+ }
454
+ }
455
+
456
+ // the block is in fork choice
457
+ const forkChoiceSlot = this.chain.forkChoice.getBlockHexDefaultStatus(rootHex)?.slot;
458
+ if (forkChoiceSlot !== undefined) {
459
+ return forkChoiceSlot;
460
+ }
461
+
462
+ // a seen payload envelope input
463
+ return this.chain.seenPayloadEnvelopeInputCache.get(rootHex)?.slot;
464
+ }
465
+
385
466
  private addByPayloadInput = (
386
467
  payloadInput: PayloadEnvelopeInput,
387
468
  peerIdStr?: PeerIdStr,
@@ -436,10 +517,12 @@ export class BlockInputSync {
436
517
  blockInput: IBlockInput
437
518
  ):
438
519
  | {kind: "ready"}
439
- | {kind: "block" | "parentBlock" | "parentPayload"; rootHex: RootHex}
520
+ | {kind: "block" | "parentBlock"; rootHex: RootHex}
521
+ | {kind: "parentPayload"; rootHex: RootHex; slot: Slot}
440
522
  | {kind: "invalidParentPayload"; parentRootHex: RootHex; parentBlockHashHex: RootHex} {
441
523
  const parentRootHex = blockInput.parentRootHex;
442
- if (!this.chain.forkChoice.hasBlockHex(parentRootHex)) {
524
+ const parentBlock = this.chain.forkChoice.getBlockHexDefaultStatus(parentRootHex);
525
+ if (!parentBlock) {
443
526
  return {kind: "parentBlock", rootHex: parentRootHex};
444
527
  }
445
528
 
@@ -464,13 +547,13 @@ export class BlockInputSync {
464
547
  const parentPayloadInput = this.chain.seenPayloadEnvelopeInputCache.get(parentRootHex);
465
548
  if (parentPayloadInput) {
466
549
  if (parentPayloadInput.getBlockHashHex() === parentBlockHashHex) {
467
- return {kind: "parentPayload", rootHex: parentRootHex};
550
+ return {kind: "parentPayload", rootHex: parentRootHex, slot: parentBlock.slot};
468
551
  }
469
552
 
470
553
  return {kind: "invalidParentPayload", parentRootHex, parentBlockHashHex};
471
554
  }
472
555
 
473
- return {kind: "parentPayload", rootHex: parentRootHex};
556
+ return {kind: "parentPayload", rootHex: parentRootHex, slot: parentBlock.slot};
474
557
  }
475
558
 
476
559
  private advancePendingBlock(pendingBlock: PendingBlockInput): AdvancePendingBlockResult {
@@ -493,9 +576,9 @@ export class BlockInputSync {
493
576
  }
494
577
 
495
578
  case "parentPayload": {
496
- let added = this.addByPayloadRootHex(missingDependency.rootHex);
579
+ let added = this.addByPayloadRootHex(missingDependency.rootHex, undefined, missingDependency.slot);
497
580
  for (const peerIdStr of pendingBlock.peerIdStrings) {
498
- added = this.addByPayloadRootHex(missingDependency.rootHex, peerIdStr) || added;
581
+ added = this.addByPayloadRootHex(missingDependency.rootHex, peerIdStr, missingDependency.slot) || added;
499
582
  }
500
583
  return added ? "queued_parent_payload" : "blocked";
501
584
  }
@@ -928,6 +1011,7 @@ export class BlockInputSync {
928
1011
  const pendingPayloadByRoot: PendingPayloadRootHex = {
929
1012
  status: PendingPayloadInputStatus.pending,
930
1013
  rootHex,
1014
+ slot: pendingPayload.envelope.message.payload.slotNumber,
931
1015
  timeAddedSec: pendingPayload.timeAddedSec,
932
1016
  peerIdStrings: new Set(pendingPayload.peerIdStrings),
933
1017
  };
@@ -967,6 +1051,10 @@ export class BlockInputSync {
967
1051
  return;
968
1052
  }
969
1053
 
1054
+ if (!isPendingPayloadInput(payload) && payload.slot === undefined) {
1055
+ payload.slot = this.resolvePayloadSlot(rootHex);
1056
+ }
1057
+
970
1058
  const payloadSlot = getPayloadSyncCacheItemSlot(payload);
971
1059
  const logCtx = {
972
1060
  slot: payloadSlot,
@@ -982,7 +1070,13 @@ export class BlockInputSync {
982
1070
  const res = await wrapError(this.fetchPayloadInput(payload));
983
1071
  if (!res.err) {
984
1072
  const pendingPayload = res.result;
985
- this.pendingPayloads.set(getPayloadSyncCacheItemRootHex(pendingPayload), pendingPayload);
1073
+ const resultRootHex = getPayloadSyncCacheItemRootHex(pendingPayload);
1074
+ // finalization may have deleted this entry while we were awaiting the network, do not resurrect it via the set below
1075
+ if (!this.pendingPayloads.has(resultRootHex)) {
1076
+ this.logger.verbose("Dropping downloaded payload, entry pruned during fetch", logCtx);
1077
+ return;
1078
+ }
1079
+ this.pendingPayloads.set(resultRootHex, pendingPayload);
986
1080
 
987
1081
  if (isPendingPayloadEnvelope(pendingPayload)) {
988
1082
  await this.reconcilePayloadEnvelope(pendingPayload);
@@ -1110,9 +1204,11 @@ export class BlockInputSync {
1110
1204
  );
1111
1205
  }
1112
1206
 
1113
- throw Error(
1114
- `Error fetching payload by root slot=${slot} root=${rootHex} after ${i}: cannot find peer with needed columns=${prettyPrintIndices(Array.from(pendingColumns))}`
1115
- );
1207
+ const reason =
1208
+ pendingColumns.size > 0
1209
+ ? `cannot find peer with needed columns=${prettyPrintIndices(Array.from(pendingColumns))}`
1210
+ : "no peer available to download pending payload";
1211
+ throw Error(`Error fetching payload by root slot=${slot} root=${rootHex} after ${i}: ${reason}`);
1116
1212
  }
1117
1213
 
1118
1214
  const {peerId, client: peerClient} = peerMeta;
@@ -1,12 +0,0 @@
1
- import { PublicKey } from "@chainsafe/blst";
2
- export type SignatureSetDeserialized = {
3
- publicKey: PublicKey;
4
- message: Uint8Array;
5
- signature: Uint8Array;
6
- };
7
- /**
8
- * Verify signatures sets with batch verification or regular core verify depending on the set count.
9
- * Abstracted in a separate file to be consumed by the threaded pool and the main thread implementation.
10
- */
11
- export declare function verifySignatureSetsMaybeBatch(sets: SignatureSetDeserialized[]): boolean;
12
- //# sourceMappingURL=maybeBatch.d.ts.map