@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
@@ -358,9 +358,10 @@ export class SyncChain {
358
358
  // If a batch exceeds it's retry limit, maybe downscore peers.
359
359
  // shouldDownscoreOnBatchError() functions enforces that all BatchErrorCode values are covered
360
360
  if (e instanceof BatchError) {
361
- const shouldReportPeer = shouldReportPeerOnBatchError(e.type.code);
361
+ const shouldReportPeer = shouldReportPeerOnBatchError(e.type.code, this.batches.get(e.type.startEpoch));
362
362
  if (shouldReportPeer) {
363
- for (const peer of this.peerset.keys()) {
363
+ // Only the peers actually at fault are reported, never the whole peerset.
364
+ for (const peer of shouldReportPeer.peers) {
364
365
  this.reportPeer(peer, shouldReportPeer.action, shouldReportPeer.reason);
365
366
  }
366
367
  }
@@ -768,8 +769,11 @@ export class SyncChain {
768
769
 
769
770
  // The last batch attempt is right, all others are wrong. Penalize other peers
770
771
  const attemptOk = batch.validationSuccess();
771
- for (const attempt of batch.failedProcessingAttempts) {
772
+ for (const attempt of batch.getPeerAttributableAttempts()) {
772
773
  if (attempt.hash !== attemptOk.hash) {
774
+ // attempt.peers contains block/column/envelope peers.
775
+ // And if there is one bad envelope we will downscore block and column peers to
776
+ // TODO: resolve this
773
777
  for (const badAttemptPeer of attempt.peers) {
774
778
  if (attemptOk.peers.find((goodPeer) => goodPeer === badAttemptPeer)) {
775
779
  // The same peer corrected its previous attempt
@@ -830,21 +834,32 @@ export class SyncChain {
830
834
  * If peer should not be downscored, returns null.
831
835
  */
832
836
  export function shouldReportPeerOnBatchError(
833
- code: BatchErrorCode
834
- ): {action: PeerAction.LowToleranceError; reason: string} | null {
837
+ code: BatchErrorCode,
838
+ batch: Batch | undefined
839
+ ): {action: PeerAction.LowToleranceError; reason: string; peers: PeerIdStr[]} | null {
835
840
  switch (code) {
836
- // A batch could not be processed after max retry limit. It's likely that all peers
837
- // in this chain are sending invalid batches repeatedly so are either malicious or faulty.
838
- // We drop the chain and report all peers.
839
- // There are some edge cases with forks that could cause this situation, but it's unlikely.
841
+ // A batch could not be processed after max retry limit. Report
842
+ // only the peers that served a peer-attributable failed attempt.
840
843
  case BatchErrorCode.MAX_PROCESSING_ATTEMPTS:
841
- return {action: PeerAction.LowToleranceError, reason: "SyncChainMaxProcessingAttempts"};
844
+ case BatchErrorCode.MAX_EXECUTION_ENGINE_ERROR_ATTEMPTS: {
845
+ // penalize both the processing-error and execution-error peers
846
+ const attributablePeers = [
847
+ ...new Set(batch?.getPeerAttributableAttempts().flatMap((attempt) => attempt.peers) ?? []),
848
+ ];
849
+ if (attributablePeers.length === 0) {
850
+ return null;
851
+ }
852
+ const reason =
853
+ code === BatchErrorCode.MAX_PROCESSING_ATTEMPTS
854
+ ? "SyncChainMaxProcessingAttempts"
855
+ : "SyncChainMaxExecutionEngineErrorAttempts";
856
+ return {action: PeerAction.LowToleranceError, reason, peers: attributablePeers};
857
+ }
842
858
 
843
859
  // TODO: Should peers be reported for MAX_DOWNLOAD_ATTEMPTS?
844
860
  case BatchErrorCode.MAX_DOWNLOAD_ATTEMPTS:
845
861
  case BatchErrorCode.INVALID_COUNT:
846
862
  case BatchErrorCode.WRONG_STATUS:
847
- case BatchErrorCode.MAX_EXECUTION_ENGINE_ERROR_ATTEMPTS:
848
863
  return null;
849
864
  }
850
865
  }
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,6 +1,7 @@
1
1
  import {routes} from "@lodestar/api";
2
2
  import {ChainForkConfig} from "@lodestar/config";
3
- import {ForkSeq} from "@lodestar/params";
3
+ import {CheckpointWithHex} from "@lodestar/fork-choice";
4
+ import {ForkSeq, isForkPostGloas} from "@lodestar/params";
4
5
  import {RequestError, RequestErrorCode} from "@lodestar/reqresp";
5
6
  import {computeTimeAtSlot} from "@lodestar/state-transition";
6
7
  import {RootHex, Slot, gloas} from "@lodestar/types";
@@ -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
  }
@@ -764,9 +847,10 @@ export class BlockInputSync {
764
847
  // this prevents unbundling attack
765
848
  // see https://lighthouse-blog.sigmaprime.io/mev-unbundling-rpc.html
766
849
  const {slot: blockSlot, proposerIndex} = pendingBlock.blockInput.getBlock().message;
850
+ const blockRootHex = pendingBlock.blockInput.blockRootHex;
767
851
  const logCtx = {
768
852
  slot: blockSlot,
769
- root: pendingBlock.blockInput.blockRootHex,
853
+ root: blockRootHex,
770
854
  delaySec: this.chain.clock.secFromSlot(blockSlot),
771
855
  };
772
856
  this.logger.verbose("Processing downloaded block", logCtx);
@@ -780,7 +864,7 @@ export class BlockInputSync {
780
864
  // eligible for proposer boost to prevent unbundling attack
781
865
  this.logger.verbose("Avoid proposer boost for this block of known proposer", {
782
866
  slot: blockSlot,
783
- root: pendingBlock.blockInput.blockRootHex,
867
+ root: blockRootHex,
784
868
  proposerIndex,
785
869
  });
786
870
  await sleep(proposerBoostWindowMs);
@@ -807,12 +891,24 @@ export class BlockInputSync {
807
891
  if (!res.err) {
808
892
  this.logger.verbose("Processed block from unknown sync", logCtx);
809
893
  // no need to update status to "processed", delete anyway
810
- this.pendingBlocks.delete(pendingBlock.blockInput.blockRootHex);
894
+ this.pendingBlocks.delete(blockRootHex);
895
+
896
+ if (isForkPostGloas(fork)) {
897
+ const payloadInput = this.chain.seenPayloadEnvelopeInputCache.get(blockRootHex);
898
+ if (!payloadInput) {
899
+ this.logger.warn("PayloadEnvelopeInput not seeded during unknown sync processReadyBlock()", logCtx);
900
+ } else {
901
+ // Similar to the gossip path: immediately attempt fetch of data columns from the execution engine.
902
+ // The bid already carries the kzg commitments, so there is no reason to wait for the payload to arrive.
903
+ this.chain.getBlobsTracker.triggerGetBlobs(payloadInput);
904
+ }
905
+ }
906
+
811
907
  // Re-enter the scheduler so descendants blocked on either parent blocks or parent payloads
812
908
  // are advanced through the same dependency checks as every other pending item.
813
909
  this.triggerUnknownBlockSearch();
814
910
  } else {
815
- const errorData = {slot: pendingBlock.blockInput.slot, root: pendingBlock.blockInput.blockRootHex};
911
+ const errorData = {slot: pendingBlock.blockInput.slot, root: blockRootHex};
816
912
  if (res.err instanceof BlockError) {
817
913
  switch (res.err.type.code) {
818
914
  // This cases are already handled with `{ignoreIfKnown: true}`
@@ -915,6 +1011,7 @@ export class BlockInputSync {
915
1011
  const pendingPayloadByRoot: PendingPayloadRootHex = {
916
1012
  status: PendingPayloadInputStatus.pending,
917
1013
  rootHex,
1014
+ slot: pendingPayload.envelope.message.payload.slotNumber,
918
1015
  timeAddedSec: pendingPayload.timeAddedSec,
919
1016
  peerIdStrings: new Set(pendingPayload.peerIdStrings),
920
1017
  };
@@ -954,6 +1051,10 @@ export class BlockInputSync {
954
1051
  return;
955
1052
  }
956
1053
 
1054
+ if (!isPendingPayloadInput(payload) && payload.slot === undefined) {
1055
+ payload.slot = this.resolvePayloadSlot(rootHex);
1056
+ }
1057
+
957
1058
  const payloadSlot = getPayloadSyncCacheItemSlot(payload);
958
1059
  const logCtx = {
959
1060
  slot: payloadSlot,
@@ -969,7 +1070,13 @@ export class BlockInputSync {
969
1070
  const res = await wrapError(this.fetchPayloadInput(payload));
970
1071
  if (!res.err) {
971
1072
  const pendingPayload = res.result;
972
- 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);
973
1080
 
974
1081
  if (isPendingPayloadEnvelope(pendingPayload)) {
975
1082
  await this.reconcilePayloadEnvelope(pendingPayload);
@@ -1097,9 +1204,11 @@ export class BlockInputSync {
1097
1204
  );
1098
1205
  }
1099
1206
 
1100
- throw Error(
1101
- `Error fetching payload by root slot=${slot} root=${rootHex} after ${i}: cannot find peer with needed columns=${prettyPrintIndices(Array.from(pendingColumns))}`
1102
- );
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}`);
1103
1212
  }
1104
1213
 
1105
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
@@ -1 +0,0 @@
1
- {"version":3,"file":"maybeBatch.d.ts","sourceRoot":"","sources":["../../../src/chain/bls/maybeBatch.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,SAAS,EAAuD,MAAM,iBAAiB,CAAC;AAIhG,MAAM,MAAM,wBAAwB,GAAG;IACrC,SAAS,EAAE,SAAS,CAAC;IACrB,OAAO,EAAE,UAAU,CAAC;IACpB,SAAS,EAAE,UAAU,CAAC;CACvB,CAAC;AAEF;;;GAGG;AACH,wBAAgB,6BAA6B,CAAC,IAAI,EAAE,wBAAwB,EAAE,GAAG,OAAO,CA8BvF"}
@@ -1,35 +0,0 @@
1
- import { Signature, verify, verifyMultipleAggregateSignatures } from "@chainsafe/blst";
2
- const MIN_SET_COUNT_TO_BATCH = 2;
3
- /**
4
- * Verify signatures sets with batch verification or regular core verify depending on the set count.
5
- * Abstracted in a separate file to be consumed by the threaded pool and the main thread implementation.
6
- */
7
- export function verifySignatureSetsMaybeBatch(sets) {
8
- try {
9
- if (sets.length >= MIN_SET_COUNT_TO_BATCH) {
10
- return verifyMultipleAggregateSignatures(sets.map((s) => ({
11
- pk: s.publicKey,
12
- msg: s.message,
13
- // true = validate signature
14
- sig: Signature.fromBytes(s.signature, true),
15
- })));
16
- }
17
- // .every on an empty array returns true
18
- if (sets.length === 0) {
19
- throw Error("Empty signature set");
20
- }
21
- // If too few signature sets verify them without batching
22
- return sets.every((set) => {
23
- // true = validate signature
24
- const sig = Signature.fromBytes(set.signature, true);
25
- return verify(set.message, set.publicKey, sig);
26
- });
27
- }
28
- catch (_) {
29
- // A signature could be malformed, in that case fromBytes throws error
30
- // blst-ts `verifyMultipleSignatures` is also a fallible operation if mul_n_aggregate fails
31
- // see https://github.com/ChainSafe/blst-ts/blob/b1ba6333f664b08e5c50b2b0d18c4f079203962b/src/lib.ts#L291
32
- return false;
33
- }
34
- }
35
- //# sourceMappingURL=maybeBatch.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"maybeBatch.js","sourceRoot":"","sources":["../../../src/chain/bls/maybeBatch.ts"],"names":[],"mappings":"AAAA,OAAO,EAAY,SAAS,EAAE,MAAM,EAAE,iCAAiC,EAAC,MAAM,iBAAiB,CAAC;AAEhG,MAAM,sBAAsB,GAAG,CAAC,CAAC;AAQjC;;;GAGG;AACH,MAAM,UAAU,6BAA6B,CAAC,IAAgC;IAC5E,IAAI,CAAC;QACH,IAAI,IAAI,CAAC,MAAM,IAAI,sBAAsB,EAAE,CAAC;YAC1C,OAAO,iCAAiC,CACtC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;gBACf,EAAE,EAAE,CAAC,CAAC,SAAS;gBACf,GAAG,EAAE,CAAC,CAAC,OAAO;gBACd,4BAA4B;gBAC5B,GAAG,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC,CAAC,SAAS,EAAE,IAAI,CAAC;aAC5C,CAAC,CAAC,CACJ,CAAC;QACJ,CAAC;QAED,wCAAwC;QACxC,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACtB,MAAM,KAAK,CAAC,qBAAqB,CAAC,CAAC;QACrC,CAAC;QAED,yDAAyD;QACzD,OAAO,IAAI,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE;YACxB,4BAA4B;YAC5B,MAAM,GAAG,GAAG,SAAS,CAAC,SAAS,CAAC,GAAG,CAAC,SAAS,EAAE,IAAI,CAAC,CAAC;YACrD,OAAO,MAAM,CAAC,GAAG,CAAC,OAAO,EAAE,GAAG,CAAC,SAAS,EAAE,GAAG,CAAC,CAAC;QACjD,CAAC,CAAC,CAAC;IACL,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC;QACX,sEAAsE;QACtE,2FAA2F;QAC3F,yGAAyG;QACzG,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC"}
@@ -1,45 +0,0 @@
1
- import {PublicKey, Signature, verify, verifyMultipleAggregateSignatures} from "@chainsafe/blst";
2
-
3
- const MIN_SET_COUNT_TO_BATCH = 2;
4
-
5
- export type SignatureSetDeserialized = {
6
- publicKey: PublicKey;
7
- message: Uint8Array;
8
- signature: Uint8Array;
9
- };
10
-
11
- /**
12
- * Verify signatures sets with batch verification or regular core verify depending on the set count.
13
- * Abstracted in a separate file to be consumed by the threaded pool and the main thread implementation.
14
- */
15
- export function verifySignatureSetsMaybeBatch(sets: SignatureSetDeserialized[]): boolean {
16
- try {
17
- if (sets.length >= MIN_SET_COUNT_TO_BATCH) {
18
- return verifyMultipleAggregateSignatures(
19
- sets.map((s) => ({
20
- pk: s.publicKey,
21
- msg: s.message,
22
- // true = validate signature
23
- sig: Signature.fromBytes(s.signature, true),
24
- }))
25
- );
26
- }
27
-
28
- // .every on an empty array returns true
29
- if (sets.length === 0) {
30
- throw Error("Empty signature set");
31
- }
32
-
33
- // If too few signature sets verify them without batching
34
- return sets.every((set) => {
35
- // true = validate signature
36
- const sig = Signature.fromBytes(set.signature, true);
37
- return verify(set.message, set.publicKey, sig);
38
- });
39
- } catch (_) {
40
- // A signature could be malformed, in that case fromBytes throws error
41
- // blst-ts `verifyMultipleSignatures` is also a fallible operation if mul_n_aggregate fails
42
- // see https://github.com/ChainSafe/blst-ts/blob/b1ba6333f664b08e5c50b2b0d18c4f079203962b/src/lib.ts#L291
43
- return false;
44
- }
45
- }