@lodestar/beacon-node 1.50.0-dev.5866a397b6 → 1.50.0-dev.58d40975f9

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 (300) hide show
  1. package/lib/api/impl/beacon/blocks/index.d.ts.map +1 -1
  2. package/lib/api/impl/beacon/blocks/index.js +6 -1
  3. package/lib/api/impl/beacon/blocks/index.js.map +1 -1
  4. package/lib/api/impl/beacon/index.d.ts.map +1 -1
  5. package/lib/api/impl/beacon/index.js +13 -0
  6. package/lib/api/impl/beacon/index.js.map +1 -1
  7. package/lib/api/impl/beacon/pool/index.js +4 -4
  8. package/lib/api/impl/beacon/pool/index.js.map +1 -1
  9. package/lib/api/impl/beacon/state/index.d.ts.map +1 -1
  10. package/lib/api/impl/beacon/state/index.js +23 -3
  11. package/lib/api/impl/beacon/state/index.js.map +1 -1
  12. package/lib/api/impl/debug/index.d.ts.map +1 -1
  13. package/lib/api/impl/debug/index.js +26 -18
  14. package/lib/api/impl/debug/index.js.map +1 -1
  15. package/lib/api/impl/validator/index.d.ts +1 -0
  16. package/lib/api/impl/validator/index.d.ts.map +1 -1
  17. package/lib/api/impl/validator/index.js +23 -1
  18. package/lib/api/impl/validator/index.js.map +1 -1
  19. package/lib/chain/archiveStore/archiveStore.d.ts +6 -0
  20. package/lib/chain/archiveStore/archiveStore.d.ts.map +1 -1
  21. package/lib/chain/archiveStore/archiveStore.js +18 -24
  22. package/lib/chain/archiveStore/archiveStore.js.map +1 -1
  23. package/lib/chain/archiveStore/historicalState/getHistoricalState.d.ts +2 -2
  24. package/lib/chain/archiveStore/historicalState/getHistoricalState.d.ts.map +1 -1
  25. package/lib/chain/archiveStore/historicalState/getHistoricalState.js +5 -7
  26. package/lib/chain/archiveStore/historicalState/getHistoricalState.js.map +1 -1
  27. package/lib/chain/archiveStore/historicalState/historicalStateRegen.js +1 -1
  28. package/lib/chain/archiveStore/historicalState/historicalStateRegen.js.map +1 -1
  29. package/lib/chain/archiveStore/historicalState/types.d.ts +2 -2
  30. package/lib/chain/archiveStore/historicalState/types.d.ts.map +1 -1
  31. package/lib/chain/archiveStore/historicalState/worker.js +1 -1
  32. package/lib/chain/archiveStore/historicalState/worker.js.map +1 -1
  33. package/lib/chain/archiveStore/interface.d.ts +1 -1
  34. package/lib/chain/archiveStore/interface.d.ts.map +1 -1
  35. package/lib/chain/archiveStore/utils/pruneHistory.d.ts +4 -1
  36. package/lib/chain/archiveStore/utils/pruneHistory.d.ts.map +1 -1
  37. package/lib/chain/archiveStore/utils/pruneHistory.js +5 -3
  38. package/lib/chain/archiveStore/utils/pruneHistory.js.map +1 -1
  39. package/lib/chain/blocks/importBlock.d.ts.map +1 -1
  40. package/lib/chain/blocks/importBlock.js +17 -6
  41. package/lib/chain/blocks/importBlock.js.map +1 -1
  42. package/lib/chain/blocks/payloadEnvelopeInput/types.d.ts +2 -1
  43. package/lib/chain/blocks/payloadEnvelopeInput/types.d.ts.map +1 -1
  44. package/lib/chain/blocks/verifyBlock.d.ts.map +1 -1
  45. package/lib/chain/blocks/verifyBlock.js +7 -7
  46. package/lib/chain/blocks/verifyBlock.js.map +1 -1
  47. package/lib/chain/blocks/verifyBlocksStateTransitionOnly.d.ts +2 -1
  48. package/lib/chain/blocks/verifyBlocksStateTransitionOnly.d.ts.map +1 -1
  49. package/lib/chain/blocks/verifyBlocksStateTransitionOnly.js +5 -4
  50. package/lib/chain/blocks/verifyBlocksStateTransitionOnly.js.map +1 -1
  51. package/lib/chain/chain.d.ts +9 -5
  52. package/lib/chain/chain.d.ts.map +1 -1
  53. package/lib/chain/chain.js +90 -36
  54. package/lib/chain/chain.js.map +1 -1
  55. package/lib/chain/emitter.d.ts +7 -1
  56. package/lib/chain/emitter.d.ts.map +1 -1
  57. package/lib/chain/emitter.js +5 -0
  58. package/lib/chain/emitter.js.map +1 -1
  59. package/lib/chain/errors/envelopeReconstructionError.d.ts +5 -0
  60. package/lib/chain/errors/envelopeReconstructionError.d.ts.map +1 -1
  61. package/lib/chain/errors/envelopeReconstructionError.js +2 -0
  62. package/lib/chain/errors/envelopeReconstructionError.js.map +1 -1
  63. package/lib/chain/errors/gossipValidation.d.ts +3 -1
  64. package/lib/chain/errors/gossipValidation.d.ts.map +1 -1
  65. package/lib/chain/errors/gossipValidation.js +2 -0
  66. package/lib/chain/errors/gossipValidation.js.map +1 -1
  67. package/lib/chain/forkChoice/index.d.ts.map +1 -1
  68. package/lib/chain/forkChoice/index.js +2 -0
  69. package/lib/chain/forkChoice/index.js.map +1 -1
  70. package/lib/chain/initState.d.ts +2 -9
  71. package/lib/chain/initState.d.ts.map +1 -1
  72. package/lib/chain/initState.js +5 -38
  73. package/lib/chain/initState.js.map +1 -1
  74. package/lib/chain/interface.d.ts +3 -2
  75. package/lib/chain/interface.d.ts.map +1 -1
  76. package/lib/chain/opPools/aggregatedAttestationPool.d.ts +4 -2
  77. package/lib/chain/opPools/aggregatedAttestationPool.d.ts.map +1 -1
  78. package/lib/chain/opPools/aggregatedAttestationPool.js +30 -8
  79. package/lib/chain/opPools/aggregatedAttestationPool.js.map +1 -1
  80. package/lib/chain/opPools/opPool.d.ts +5 -4
  81. package/lib/chain/opPools/opPool.d.ts.map +1 -1
  82. package/lib/chain/opPools/opPool.js +62 -18
  83. package/lib/chain/opPools/opPool.js.map +1 -1
  84. package/lib/chain/opPools/proposerPreferencesPool.d.ts +4 -4
  85. package/lib/chain/opPools/proposerPreferencesPool.d.ts.map +1 -1
  86. package/lib/chain/opPools/proposerPreferencesPool.js +14 -11
  87. package/lib/chain/opPools/proposerPreferencesPool.js.map +1 -1
  88. package/lib/chain/opPools/utils.d.ts +4 -1
  89. package/lib/chain/opPools/utils.d.ts.map +1 -1
  90. package/lib/chain/opPools/utils.js +11 -1
  91. package/lib/chain/opPools/utils.js.map +1 -1
  92. package/lib/chain/options.d.ts +2 -1
  93. package/lib/chain/options.d.ts.map +1 -1
  94. package/lib/chain/options.js +1 -1
  95. package/lib/chain/options.js.map +1 -1
  96. package/lib/chain/prepareNextSlot.d.ts.map +1 -1
  97. package/lib/chain/prepareNextSlot.js +5 -2
  98. package/lib/chain/prepareNextSlot.js.map +1 -1
  99. package/lib/chain/produceBlock/computeNewStateRoot.js +1 -1
  100. package/lib/chain/produceBlock/computeNewStateRoot.js.map +1 -1
  101. package/lib/chain/produceBlock/produceBlockBody.d.ts +3 -1
  102. package/lib/chain/produceBlock/produceBlockBody.d.ts.map +1 -1
  103. package/lib/chain/produceBlock/produceBlockBody.js +6 -2
  104. package/lib/chain/produceBlock/produceBlockBody.js.map +1 -1
  105. package/lib/chain/regen/regen.d.ts +2 -0
  106. package/lib/chain/regen/regen.d.ts.map +1 -1
  107. package/lib/chain/regen/regen.js +10 -4
  108. package/lib/chain/regen/regen.js.map +1 -1
  109. package/lib/chain/seenCache/seenGossipBlockInput.d.ts +7 -0
  110. package/lib/chain/seenCache/seenGossipBlockInput.d.ts.map +1 -1
  111. package/lib/chain/seenCache/seenGossipBlockInput.js +21 -0
  112. package/lib/chain/seenCache/seenGossipBlockInput.js.map +1 -1
  113. package/lib/chain/seenCache/seenPayloadEnvelopeInput.d.ts +9 -2
  114. package/lib/chain/seenCache/seenPayloadEnvelopeInput.d.ts.map +1 -1
  115. package/lib/chain/seenCache/seenPayloadEnvelopeInput.js +23 -13
  116. package/lib/chain/seenCache/seenPayloadEnvelopeInput.js.map +1 -1
  117. package/lib/chain/shufflingCache.d.ts +18 -8
  118. package/lib/chain/shufflingCache.d.ts.map +1 -1
  119. package/lib/chain/shufflingCache.js +72 -18
  120. package/lib/chain/shufflingCache.js.map +1 -1
  121. package/lib/chain/validation/attesterSlashing.d.ts +4 -4
  122. package/lib/chain/validation/attesterSlashing.d.ts.map +1 -1
  123. package/lib/chain/validation/attesterSlashing.js +3 -0
  124. package/lib/chain/validation/attesterSlashing.js.map +1 -1
  125. package/lib/chain/validation/proposerSlashing.d.ts +3 -3
  126. package/lib/chain/validation/proposerSlashing.d.ts.map +1 -1
  127. package/lib/chain/validation/proposerSlashing.js +3 -0
  128. package/lib/chain/validation/proposerSlashing.js.map +1 -1
  129. package/lib/chain/validatorMonitor.d.ts +5 -1
  130. package/lib/chain/validatorMonitor.d.ts.map +1 -1
  131. package/lib/chain/validatorMonitor.js +64 -54
  132. package/lib/chain/validatorMonitor.js.map +1 -1
  133. package/lib/db/beacon.d.ts +2 -0
  134. package/lib/db/beacon.d.ts.map +1 -1
  135. package/lib/db/beacon.js +5 -0
  136. package/lib/db/beacon.js.map +1 -1
  137. package/lib/db/buckets.d.ts +2 -1
  138. package/lib/db/buckets.d.ts.map +1 -1
  139. package/lib/db/buckets.js +1 -0
  140. package/lib/db/buckets.js.map +1 -1
  141. package/lib/db/interface.d.ts +2 -0
  142. package/lib/db/interface.d.ts.map +1 -1
  143. package/lib/db/repositories/blockArchiveIndex.d.ts +2 -2
  144. package/lib/db/repositories/blockArchiveIndex.d.ts.map +1 -1
  145. package/lib/db/repositories/earliestAvailableSlot.d.ts +11 -0
  146. package/lib/db/repositories/earliestAvailableSlot.d.ts.map +1 -0
  147. package/lib/db/repositories/earliestAvailableSlot.js +19 -0
  148. package/lib/db/repositories/earliestAvailableSlot.js.map +1 -0
  149. package/lib/db/repositories/proposerSlashing.d.ts +1 -0
  150. package/lib/db/repositories/proposerSlashing.d.ts.map +1 -1
  151. package/lib/db/repositories/proposerSlashing.js +5 -1
  152. package/lib/db/repositories/proposerSlashing.js.map +1 -1
  153. package/lib/execution/engine/disabled.d.ts +0 -2
  154. package/lib/execution/engine/disabled.d.ts.map +1 -1
  155. package/lib/execution/engine/disabled.js +0 -6
  156. package/lib/execution/engine/disabled.js.map +1 -1
  157. package/lib/execution/engine/http.d.ts +1 -3
  158. package/lib/execution/engine/http.d.ts.map +1 -1
  159. package/lib/execution/engine/http.js +2 -17
  160. package/lib/execution/engine/http.js.map +1 -1
  161. package/lib/execution/engine/interface.d.ts +2 -4
  162. package/lib/execution/engine/interface.d.ts.map +1 -1
  163. package/lib/execution/engine/mock.d.ts +0 -2
  164. package/lib/execution/engine/mock.d.ts.map +1 -1
  165. package/lib/execution/engine/mock.js +0 -8
  166. package/lib/execution/engine/mock.js.map +1 -1
  167. package/lib/execution/engine/types.d.ts +0 -8
  168. package/lib/execution/engine/types.d.ts.map +1 -1
  169. package/lib/execution/engine/types.js.map +1 -1
  170. package/lib/index.d.ts +1 -1
  171. package/lib/index.d.ts.map +1 -1
  172. package/lib/index.js +1 -1
  173. package/lib/index.js.map +1 -1
  174. package/lib/metrics/metrics/beacon.d.ts +1 -1
  175. package/lib/metrics/metrics/beacon.d.ts.map +1 -1
  176. package/lib/metrics/metrics/beacon.js.map +1 -1
  177. package/lib/metrics/metrics/lodestar.d.ts +7 -0
  178. package/lib/metrics/metrics/lodestar.d.ts.map +1 -1
  179. package/lib/metrics/metrics/lodestar.js +17 -0
  180. package/lib/metrics/metrics/lodestar.js.map +1 -1
  181. package/lib/metrics/metrics.d.ts +7 -3
  182. package/lib/metrics/metrics.d.ts.map +1 -1
  183. package/lib/metrics/metrics.js +4 -4
  184. package/lib/metrics/metrics.js.map +1 -1
  185. package/lib/network/metadata.d.ts +1 -1
  186. package/lib/network/metadata.d.ts.map +1 -1
  187. package/lib/network/metadata.js +17 -17
  188. package/lib/network/metadata.js.map +1 -1
  189. package/lib/network/peers/datastore.d.ts +9 -9
  190. package/lib/network/peers/datastore.d.ts.map +1 -1
  191. package/lib/network/peers/datastore.js +36 -36
  192. package/lib/network/peers/datastore.js.map +1 -1
  193. package/lib/network/processor/gossipHandlers.d.ts.map +1 -1
  194. package/lib/network/processor/gossipHandlers.js +66 -67
  195. package/lib/network/processor/gossipHandlers.js.map +1 -1
  196. package/lib/network/processor/gossipValidatorFn.d.ts +7 -3
  197. package/lib/network/processor/gossipValidatorFn.d.ts.map +1 -1
  198. package/lib/network/processor/gossipValidatorFn.js +7 -3
  199. package/lib/network/processor/gossipValidatorFn.js.map +1 -1
  200. package/lib/network/processor/index.d.ts +9 -0
  201. package/lib/network/processor/index.d.ts.map +1 -1
  202. package/lib/network/processor/index.js +77 -10
  203. package/lib/network/processor/index.js.map +1 -1
  204. package/lib/network/reqresp/handlers/executionPayloadEnvelopesByRoot.d.ts.map +1 -1
  205. package/lib/network/reqresp/handlers/executionPayloadEnvelopesByRoot.js +41 -26
  206. package/lib/network/reqresp/handlers/executionPayloadEnvelopesByRoot.js.map +1 -1
  207. package/lib/node/nodejs.d.ts +3 -1
  208. package/lib/node/nodejs.d.ts.map +1 -1
  209. package/lib/node/nodejs.js +27 -4
  210. package/lib/node/nodejs.js.map +1 -1
  211. package/lib/sync/range/batch.d.ts +11 -5
  212. package/lib/sync/range/batch.d.ts.map +1 -1
  213. package/lib/sync/range/batch.js +21 -17
  214. package/lib/sync/range/batch.js.map +1 -1
  215. package/lib/sync/range/chain.d.ts +4 -4
  216. package/lib/sync/range/chain.d.ts.map +1 -1
  217. package/lib/sync/range/chain.js +12 -6
  218. package/lib/sync/range/chain.js.map +1 -1
  219. package/lib/sync/range/range.d.ts.map +1 -1
  220. package/lib/sync/range/range.js +75 -29
  221. package/lib/sync/range/range.js.map +1 -1
  222. package/lib/sync/unknownBlock.d.ts +14 -7
  223. package/lib/sync/unknownBlock.d.ts.map +1 -1
  224. package/lib/sync/unknownBlock.js +132 -66
  225. package/lib/sync/unknownBlock.js.map +1 -1
  226. package/lib/sync/utils/downloadByRange.d.ts +38 -14
  227. package/lib/sync/utils/downloadByRange.d.ts.map +1 -1
  228. package/lib/sync/utils/downloadByRange.js +84 -39
  229. package/lib/sync/utils/downloadByRange.js.map +1 -1
  230. package/lib/util/execution.d.ts +11 -11
  231. package/lib/util/execution.d.ts.map +1 -1
  232. package/lib/util/execution.js +11 -7
  233. package/lib/util/execution.js.map +1 -1
  234. package/package.json +17 -17
  235. package/src/api/impl/beacon/blocks/index.ts +7 -4
  236. package/src/api/impl/beacon/index.ts +15 -0
  237. package/src/api/impl/beacon/pool/index.ts +4 -4
  238. package/src/api/impl/beacon/state/index.ts +31 -3
  239. package/src/api/impl/debug/index.ts +28 -19
  240. package/src/api/impl/validator/index.ts +24 -2
  241. package/src/chain/archiveStore/archiveStore.ts +26 -26
  242. package/src/chain/archiveStore/historicalState/getHistoricalState.ts +5 -7
  243. package/src/chain/archiveStore/historicalState/historicalStateRegen.ts +1 -1
  244. package/src/chain/archiveStore/historicalState/types.ts +2 -2
  245. package/src/chain/archiveStore/historicalState/worker.ts +1 -2
  246. package/src/chain/archiveStore/interface.ts +1 -1
  247. package/src/chain/archiveStore/utils/pruneHistory.ts +9 -4
  248. package/src/chain/blocks/importBlock.ts +20 -5
  249. package/src/chain/blocks/payloadEnvelopeInput/types.ts +2 -1
  250. package/src/chain/blocks/verifyBlock.ts +8 -6
  251. package/src/chain/blocks/verifyBlocksStateTransitionOnly.ts +6 -3
  252. package/src/chain/chain.ts +118 -38
  253. package/src/chain/emitter.ts +7 -1
  254. package/src/chain/errors/envelopeReconstructionError.ts +3 -0
  255. package/src/chain/errors/gossipValidation.ts +2 -0
  256. package/src/chain/forkChoice/index.ts +2 -0
  257. package/src/chain/initState.ts +8 -60
  258. package/src/chain/interface.ts +3 -2
  259. package/src/chain/opPools/aggregatedAttestationPool.ts +34 -10
  260. package/src/chain/opPools/opPool.ts +91 -18
  261. package/src/chain/opPools/proposerPreferencesPool.ts +14 -10
  262. package/src/chain/opPools/utils.ts +31 -3
  263. package/src/chain/options.ts +3 -3
  264. package/src/chain/prepareNextSlot.ts +6 -3
  265. package/src/chain/produceBlock/computeNewStateRoot.ts +1 -1
  266. package/src/chain/produceBlock/produceBlockBody.ts +8 -1
  267. package/src/chain/regen/regen.ts +12 -4
  268. package/src/chain/seenCache/seenGossipBlockInput.ts +23 -0
  269. package/src/chain/seenCache/seenPayloadEnvelopeInput.ts +31 -13
  270. package/src/chain/shufflingCache.ts +84 -22
  271. package/src/chain/validation/attesterSlashing.ts +7 -4
  272. package/src/chain/validation/proposerSlashing.ts +7 -4
  273. package/src/chain/validatorMonitor.ts +75 -57
  274. package/src/db/beacon.ts +5 -0
  275. package/src/db/buckets.ts +1 -0
  276. package/src/db/interface.ts +2 -0
  277. package/src/db/repositories/earliestAvailableSlot.ts +19 -0
  278. package/src/db/repositories/proposerSlashing.ts +6 -1
  279. package/src/execution/engine/disabled.ts +0 -8
  280. package/src/execution/engine/http.ts +1 -30
  281. package/src/execution/engine/interface.ts +2 -6
  282. package/src/execution/engine/mock.ts +1 -19
  283. package/src/execution/engine/types.ts +0 -10
  284. package/src/index.ts +1 -1
  285. package/src/metrics/metrics/beacon.ts +1 -1
  286. package/src/metrics/metrics/lodestar.ts +18 -0
  287. package/src/metrics/metrics.ts +21 -6
  288. package/src/network/metadata.ts +17 -17
  289. package/src/network/peers/datastore.ts +36 -36
  290. package/src/network/processor/gossipHandlers.ts +97 -80
  291. package/src/network/processor/gossipValidatorFn.ts +13 -3
  292. package/src/network/processor/index.ts +89 -10
  293. package/src/network/reqresp/handlers/executionPayloadEnvelopesByRoot.ts +42 -28
  294. package/src/node/nodejs.ts +35 -3
  295. package/src/sync/range/batch.ts +25 -19
  296. package/src/sync/range/chain.ts +15 -8
  297. package/src/sync/range/range.ts +76 -30
  298. package/src/sync/unknownBlock.ts +156 -71
  299. package/src/sync/utils/downloadByRange.ts +123 -58
  300. package/src/util/execution.ts +26 -14
@@ -15,7 +15,7 @@ import {ChainEvent, ChainEventData, IBeaconChain} from "../chain/index.js";
15
15
  import {validateGloasBlockDataColumnSidecars} from "../chain/validation/dataColumnSidecar.js";
16
16
  import {validateGossipExecutionPayloadEnvelope} from "../chain/validation/executionPayloadEnvelope.js";
17
17
  import {Metrics} from "../metrics/index.js";
18
- import {INetwork, NetworkEvent, NetworkEventData, prettyPrintPeerIdStr} from "../network/index.js";
18
+ import {INetwork, NetworkEvent, NetworkEventData, PeerAction, prettyPrintPeerIdStr} from "../network/index.js";
19
19
  import {PeerSyncMeta} from "../network/peers/peersData.js";
20
20
  import {MAX_PEERS_PER_ROOT} from "../network/processor/constants.js";
21
21
  import {ClockEvent} from "../util/clock.js";
@@ -73,7 +73,11 @@ type AdvancePendingBlockResult =
73
73
  | "removed";
74
74
 
75
75
  class UnknownBlockRateLimitedError extends Error {
76
- constructor(message: string) {
76
+ constructor(
77
+ message: string,
78
+ /** Earliest backoff expiry seen when the download was deferred, it is retried once that has passed */
79
+ readonly retryAt: number
80
+ ) {
77
81
  super(message);
78
82
  this.name = "UnknownBlockRateLimitedError";
79
83
  }
@@ -442,6 +446,11 @@ export class BlockInputSync {
442
446
  };
443
447
 
444
448
  private addByRootHex = (rootHex: RootHex, peerIdStr?: PeerIdStr): boolean => {
449
+ if (this.knownBadBlocks.has(rootHex)) {
450
+ this.logger.debug("Ignoring known bad block root", {root: rootHex, peerIdStr: peerIdStr ?? "unknown peer"});
451
+ return false;
452
+ }
453
+
445
454
  let pendingBlock = this.pendingBlocks.get(rootHex);
446
455
  let added = false;
447
456
  if (!pendingBlock) {
@@ -477,6 +486,11 @@ export class BlockInputSync {
477
486
  };
478
487
 
479
488
  private addByBlockInput = (blockInput: IBlockInput, peerIdStr?: string): void => {
489
+ if (this.knownBadBlocks.has(blockInput.blockRootHex)) {
490
+ this.logger.debug("Ignoring known bad block", {slot: blockInput.slot, root: blockInput.blockRootHex, peerIdStr});
491
+ return;
492
+ }
493
+
480
494
  let pendingBlock = this.pendingBlocks.get(blockInput.blockRootHex);
481
495
  // if entry is missing or was added via rootHex and now we have more complete information overwrite
482
496
  // the existing information with the more complete cache entry
@@ -513,6 +527,15 @@ export class BlockInputSync {
513
527
  };
514
528
 
515
529
  private addByPayloadRootHex = (rootHex: RootHex, peerIdStr?: PeerIdStr, slot?: Slot): boolean => {
530
+ if (this.knownBadBlocks.has(rootHex)) {
531
+ this.logger.debug("Ignoring payload for known bad block root", {
532
+ slot: slot ?? "unknown",
533
+ root: rootHex,
534
+ peerIdStr,
535
+ });
536
+ return false;
537
+ }
538
+
516
539
  let pendingPayload = this.pendingPayloads.get(rootHex);
517
540
  let added = false;
518
541
  if (!pendingPayload) {
@@ -581,6 +604,15 @@ export class BlockInputSync {
581
604
  peerIdStr?: PeerIdStr,
582
605
  envelope?: gloas.SignedExecutionPayloadEnvelope
583
606
  ): void => {
607
+ if (this.knownBadBlocks.has(payloadInput.blockRootHex)) {
608
+ this.logger.debug("Ignoring payload input for known bad block root", {
609
+ slot: payloadInput.slot,
610
+ root: payloadInput.blockRootHex,
611
+ peerIdStr,
612
+ });
613
+ return;
614
+ }
615
+
584
616
  const pendingPayload = this.toPendingPayloadInput(
585
617
  payloadInput,
586
618
  this.pendingPayloads.get(payloadInput.blockRootHex),
@@ -654,6 +686,7 @@ export class BlockInputSync {
654
686
  return {kind: "ready"};
655
687
  }
656
688
 
689
+ // The parent's payload is known but does not match `parentBlockHashHex`
657
690
  if (this.chain.forkChoice.hasPayloadHexUnsafe(parentRootHex)) {
658
691
  return {kind: "invalidParentPayload", parentRootHex, parentBlockHashHex};
659
692
  }
@@ -820,17 +853,20 @@ export class BlockInputSync {
820
853
  }
821
854
  };
822
855
 
823
- private scheduleRateLimitBackoffRetry(): void {
856
+ /**
857
+ * Returns false if there is no backoff left to wait for, in which case no retry is scheduled.
858
+ */
859
+ private scheduleRateLimitBackoffRetry(): boolean {
824
860
  this.clearRateLimitBackoffTimer();
825
861
 
826
862
  if (!this.subscribedToNetworkEvents || (this.pendingBlocks.size === 0 && this.pendingPayloads.size === 0)) {
827
- return;
863
+ return false;
828
864
  }
829
865
 
830
866
  const now = Date.now();
831
867
  const retryAt = this.peerBalancer.getNextRateLimitRetryAt();
832
868
  if (retryAt === null) {
833
- return;
869
+ return false;
834
870
  }
835
871
 
836
872
  this.rateLimitBackoffTimeout = setTimeout(
@@ -841,6 +877,7 @@ export class BlockInputSync {
841
877
  },
842
878
  Math.max(0, retryAt - now)
843
879
  );
880
+ return true;
844
881
  }
845
882
 
846
883
  private clearRateLimitBackoffTimer(): void {
@@ -1032,7 +1069,8 @@ export class BlockInputSync {
1032
1069
  }
1033
1070
  } else if (blockSlot <= finalizedSlot) {
1034
1071
  // the common ancestor of the downloading chain and canonical chain should be at least the finalized slot and
1035
- // we should found it through forkchoice. If not, we should penalize all peers sending us this block chain
1072
+ // we should found it through forkchoice. If not, drop the chain without penalizing peers because pending entries are
1073
+ // block retained on failed download, so finalization can pass the block while it is still pending
1036
1074
  // 0 - 1 - ... - n - finalizedSlot
1037
1075
  // \
1038
1076
  // parent 1 - parent 2 - ... - unknownParent block
@@ -1040,7 +1078,7 @@ export class BlockInputSync {
1040
1078
  ...logCtx2,
1041
1079
  finalizedSlot,
1042
1080
  });
1043
- this.removeAndDownScoreAllDescendants(block, DroppedItemReason.belowFinalized);
1081
+ this.removeAllDescendants(block, DroppedItemReason.belowFinalized);
1044
1082
  } else {
1045
1083
  this.onUnknownBlockRoot({rootHex: pending.blockInput.parentRootHex, source: BlockInputSource.byRoot});
1046
1084
  }
@@ -1051,7 +1089,11 @@ export class BlockInputSync {
1051
1089
  pendingBlock.status = PendingBlockInputStatus.pending;
1052
1090
  }
1053
1091
  this.logger.debug("Deferring unknown block download due to peer rate limit", logCtx, res.err);
1054
- this.scheduleRateLimitBackoffRetry();
1092
+ if (!this.scheduleRateLimitBackoffRetry() || res.err.retryAt <= Date.now()) {
1093
+ // The backoff this download waited for expired in the meantime, a timer is only armed if another
1094
+ // peer is still in backoff and would retry it later than needed
1095
+ this.triggerUnknownBlockSearch();
1096
+ }
1055
1097
  return;
1056
1098
  }
1057
1099
 
@@ -1172,15 +1214,18 @@ export class BlockInputSync {
1172
1214
  break;
1173
1215
 
1174
1216
  case BlockErrorCode.EXECUTION_ENGINE_INVALID:
1175
- // the peer served a bad block
1217
+ // Ban but do not downscore: optimistic-sync peers may forward and serve blocks whose payload is later
1218
+ // rejected by the execution engine. Per the spec, an INVALID execution payload SHOULD NOT cause a peer
1219
+ // to be down-scored or disconnected, only consensus-layer invalidity MAY.
1220
+ // https://github.com/ethereum/consensus-specs/blob/master/specs/bellatrix/p2p-interface.md#the-reqresp-domain
1176
1221
  this.logger.debug("Execution engine rejected block from unknown parent sync", errorData, res.err);
1177
- this.removeAndDownScoreAllDescendants(pendingBlock, DroppedItemReason.elInvalid);
1222
+ this.removeAndBanAllDescendants(pendingBlock, DroppedItemReason.elInvalid);
1178
1223
  break;
1179
1224
 
1180
1225
  default:
1181
1226
  // Block is not correct with respect to our chain. Log error loudly
1182
1227
  this.logger.debug("Error processing block from unknown parent sync", errorData, res.err);
1183
- this.removeAndDownScoreAllDescendants(pendingBlock, DroppedItemReason.invalidBlock);
1228
+ this.removeAllDescendants(pendingBlock, DroppedItemReason.invalidBlock);
1184
1229
  }
1185
1230
  }
1186
1231
 
@@ -1331,6 +1376,14 @@ export class BlockInputSync {
1331
1376
  if (!isPendingPayloadEnvelope(payload)) {
1332
1377
  payload.status = PendingPayloadInputStatus.pending;
1333
1378
  }
1379
+ if (
1380
+ res.err instanceof UnknownBlockRateLimitedError &&
1381
+ (!this.scheduleRateLimitBackoffRetry() || res.err.retryAt <= Date.now())
1382
+ ) {
1383
+ // The backoff this download waited for expired in the meantime, a timer is only armed if another
1384
+ // peer is still in backoff and would retry it later than needed
1385
+ this.triggerUnknownBlockSearch();
1386
+ }
1334
1387
  }
1335
1388
 
1336
1389
  private async processPayload(pendingPayload: PendingPayloadInput): Promise<void> {
@@ -1393,20 +1446,23 @@ export class BlockInputSync {
1393
1446
  pendingPayload.status = PendingPayloadInputStatus.downloaded;
1394
1447
  break;
1395
1448
 
1449
+ // The invalid envelope is evicted from the seen cache in the cases below, otherwise the next
1450
+ // attempt would reuse it instead of fetching another envelope from peers
1396
1451
  case PayloadErrorCode.EXECUTION_ENGINE_INVALID:
1397
1452
  this.logger.debug("Error processing payload from unknown sync", logCtx, res.err);
1453
+ this.chain.seenPayloadEnvelopeInputCache.removeInvalid(pendingPayload.payloadInput);
1398
1454
  this.removePendingPayloadAndDescendants(rootHex, DroppedItemReason.elInvalid);
1399
1455
  break;
1400
1456
 
1401
1457
  case PayloadErrorCode.ENVELOPE_VERIFICATION_ERROR:
1402
1458
  this.logger.debug("Error processing payload from unknown sync", logCtx, res.err);
1459
+ this.chain.seenPayloadEnvelopeInputCache.removeInvalid(pendingPayload.payloadInput);
1403
1460
  this.removePendingPayloadAndDescendants(rootHex, DroppedItemReason.invalidEnvelope);
1404
1461
  break;
1405
1462
 
1406
1463
  case PayloadErrorCode.INVALID_SIGNATURE:
1407
- // TODO GLOAS: Decide how invalid payload inputs should eventually leave memory without
1408
- // reintroducing envelope replacement / recreation flows.
1409
1464
  this.logger.debug("Error processing payload from unknown sync", logCtx, res.err);
1465
+ this.chain.seenPayloadEnvelopeInputCache.removeInvalid(pendingPayload.payloadInput);
1410
1466
  this.removePendingPayloadAndDescendants(rootHex, DroppedItemReason.invalidSignature);
1411
1467
  break;
1412
1468
 
@@ -1452,16 +1508,21 @@ export class BlockInputSync {
1452
1508
  const pendingColumns = payloadInput?.hasAllData()
1453
1509
  ? new Set<number>()
1454
1510
  : new Set(payloadInput?.getMissingSampledColumnMeta().missing ?? []);
1511
+ // Same `now` for both calls, a backoff expiring in between would be reported as no peer available
1512
+ const now = Date.now();
1455
1513
  // prefer peers that gossiped this payload root to us (#9923)
1456
1514
  const peerMeta = this.peerBalancer.bestPeerForPendingColumns(
1457
1515
  pendingColumns,
1458
1516
  excludedPeers,
1459
- cacheItem.peerIdStrings
1517
+ cacheItem.peerIdStrings,
1518
+ now
1460
1519
  );
1461
1520
  if (peerMeta === null) {
1462
- if (this.peerBalancer.getNextRateLimitRetryAt(pendingColumns, excludedPeers) !== null) {
1521
+ const retryAt = this.peerBalancer.getNextRateLimitRetryAt(pendingColumns, excludedPeers, now);
1522
+ if (retryAt !== null) {
1463
1523
  throw new UnknownBlockRateLimitedError(
1464
- `Error fetching payload by root slot=${slot} root=${rootHex} after ${i}: peers with needed columns are rate-limited`
1524
+ `Error fetching payload by root slot=${slot} root=${rootHex} after ${i}: peers with needed columns are rate-limited`,
1525
+ retryAt
1465
1526
  );
1466
1527
  }
1467
1528
 
@@ -1478,11 +1539,11 @@ export class BlockInputSync {
1478
1539
  }
1479
1540
 
1480
1541
  const {peerId, client: peerClient} = peerMeta;
1481
- cacheItem.peerIdStrings.add(peerId);
1482
1542
 
1483
1543
  try {
1484
1544
  if (!envelope) {
1485
1545
  envelope = await this.fetchExecutionPayloadEnvelope(peerId, blockRoot, rootHex);
1546
+ cacheItem.peerIdStrings.add(peerId);
1486
1547
  const slotWasUnknown = typeof slot !== "number";
1487
1548
  slot = envelope.message.payload.slotNumber;
1488
1549
  if (slotWasUnknown) {
@@ -1524,6 +1585,8 @@ export class BlockInputSync {
1524
1585
  const missing = pendingPayload.payloadInput.getMissingSampledColumnMeta().missing;
1525
1586
  if (missing.length > 0) {
1526
1587
  const columnSidecars = await this.fetchPayloadColumns(peerMeta, pendingPayload.payloadInput, missing);
1588
+ // toPendingPayloadInput copied cacheItem's set, so add to the object that is carried forward
1589
+ pendingPayload.peerIdStrings.add(peerId);
1527
1590
  const seenTimestampSec = Date.now() / 1000;
1528
1591
  for (const columnSidecar of columnSidecars) {
1529
1592
  if (pendingPayload.payloadInput.hasColumn(columnSidecar.index)) {
@@ -1598,9 +1661,11 @@ export class BlockInputSync {
1598
1661
  );
1599
1662
  this.metrics?.blockInputSync.payloadFetchPeers.set({result: FetchResult.FailureMaxAttempts}, i - 1);
1600
1663
 
1601
- if (deferredByRateLimit && this.peerBalancer.getNextRateLimitRetryAt() !== null) {
1664
+ const retryAt = deferredByRateLimit ? this.peerBalancer.getNextRateLimitRetryAt() : null;
1665
+ if (retryAt !== null) {
1602
1666
  throw new UnknownBlockRateLimitedError(
1603
- `Error fetching payload with slot=${slot} root=${rootHex} after ${i - 1} attempts: peers are rate-limited`
1667
+ `Error fetching payload with slot=${slot} root=${rootHex} after ${i - 1} attempts: peers are rate-limited`,
1668
+ retryAt
1604
1669
  );
1605
1670
  }
1606
1671
  throw Error(`Error fetching payload with slot=${slot} root=${rootHex} after ${i - 1} attempts.`);
@@ -1692,16 +1757,21 @@ export class BlockInputSync {
1692
1757
  isPendingBlockInput(cacheItem) && isBlockInputColumns(cacheItem.blockInput)
1693
1758
  ? new Set(cacheItem.blockInput.getMissingSampledColumnMeta().missing)
1694
1759
  : defaultPendingColumns;
1760
+ // Same `now` for both calls, a backoff expiring in between would be reported as no peer available
1761
+ const now = Date.now();
1695
1762
  // prefer peers that gossiped this block root to us (#9923)
1696
1763
  const peerMeta = this.peerBalancer.bestPeerForPendingColumns(
1697
1764
  pendingColumns,
1698
1765
  excludedPeers,
1699
- cacheItem.peerIdStrings
1766
+ cacheItem.peerIdStrings,
1767
+ now
1700
1768
  );
1701
1769
  if (peerMeta === null) {
1702
- if (this.peerBalancer.getNextRateLimitRetryAt(pendingColumns, excludedPeers) !== null) {
1770
+ const retryAt = this.peerBalancer.getNextRateLimitRetryAt(pendingColumns, excludedPeers, now);
1771
+ if (retryAt !== null) {
1703
1772
  throw new UnknownBlockRateLimitedError(
1704
- `Error fetching UnknownBlockRoot slot=${slot} root=${rootHex} after ${i}: peers with needed columns are rate-limited`
1773
+ `Error fetching UnknownBlockRoot slot=${slot} root=${rootHex} after ${i}: peers with needed columns are rate-limited`,
1774
+ retryAt
1705
1775
  );
1706
1776
  }
1707
1777
 
@@ -1716,8 +1786,6 @@ export class BlockInputSync {
1716
1786
  }
1717
1787
  const {peerId, client: peerClient} = peerMeta;
1718
1788
 
1719
- cacheItem.peerIdStrings.add(peerId);
1720
-
1721
1789
  try {
1722
1790
  const downloadResult = await downloadByRoot({
1723
1791
  config: this.config,
@@ -1728,6 +1796,7 @@ export class BlockInputSync {
1728
1796
  cacheItem,
1729
1797
  });
1730
1798
  cacheItem = downloadResult.result;
1799
+ cacheItem.peerIdStrings.add(peerId);
1731
1800
  if (slot === undefined) {
1732
1801
  slot = cacheItem.blockInput.slot;
1733
1802
  // we were not able to observe the time into slot when starting the fetch, do it now
@@ -1808,8 +1877,9 @@ export class BlockInputSync {
1808
1877
 
1809
1878
  const message = `Error fetching BlockInput with slot=${slot} root=${rootHex} after ${i - 1} attempts.`;
1810
1879
 
1811
- if (deferredByRateLimit && this.peerBalancer.getNextRateLimitRetryAt() !== null) {
1812
- throw new UnknownBlockRateLimitedError(`${message} Peers are rate-limited.`);
1880
+ const retryAt = deferredByRateLimit ? this.peerBalancer.getNextRateLimitRetryAt() : null;
1881
+ if (retryAt !== null) {
1882
+ throw new UnknownBlockRateLimitedError(`${message} Peers are rate-limited.`, retryAt);
1813
1883
  }
1814
1884
 
1815
1885
  if (!isPendingBlockInput(cacheItem)) {
@@ -1843,44 +1913,8 @@ export class BlockInputSync {
1843
1913
  throw Error(message);
1844
1914
  }
1845
1915
 
1846
- /**
1847
- * Gets all descendant blocks of `block` recursively from `pendingBlocks`.
1848
- * Assumes that if a parent block does not exist or is not processable, all descendant blocks are bad too.
1849
- * Downscore all peers that have referenced any of this bad blocks. May report peers multiple times if they have
1850
- * referenced more than one bad block.
1851
- */
1852
- private removeAndDownScoreAllDescendants(block: BlockInputSyncCacheItem, headReason: DroppedItemReason): void {
1853
- // Get all blocks that are a descendant of this one
1854
- const badPendingBlocks = this.removeAllDescendants(block, headReason, DroppedItemReason.invalidParent);
1855
- // just console log and do not penalize on pending/bad blocks for debugging
1856
- // console.log("removeAndDownscoreAllDescendants", {block});
1857
-
1858
- for (const block of badPendingBlocks) {
1859
- //
1860
- // TODO(fulu): why is this commented out here?
1861
- //
1862
- // this.knownBadBlocks.add(block.blockRootHex);
1863
- // for (const peerIdStr of block.peerIdStrings) {
1864
- // // TODO: Refactor peerRpcScores to work with peerIdStr only
1865
- // this.network.reportPeer(peerIdStr, PeerAction.LowToleranceError, "BadBlockByRoot");
1866
- // }
1867
- this.logger.debug("ignored Banning unknown block", {
1868
- slot: getBlockInputSyncCacheItemSlot(block),
1869
- root: getBlockInputSyncCacheItemRootHex(block),
1870
- peerIdStrings: Array.from(block.peerIdStrings)
1871
- .map((id) => prettyPrintPeerIdStr(id))
1872
- .join(","),
1873
- });
1874
- }
1875
-
1876
- // Prune knownBadBlocks
1877
- pruneSetToMax(this.knownBadBlocks, MAX_KNOWN_BAD_BLOCKS);
1878
- }
1879
-
1880
1916
  // Once a parent payload is invalid, every descendant waiting on that payload lineage becomes unrecoverable too.
1881
1917
  private removePendingPayloadAndDescendants(rootHex: RootHex, headReason: DroppedItemReason): void {
1882
- // Keep PayloadEnvelopeInput resident in the seen cache. importBlock() owns that object and
1883
- // later validation/finalization logic decides when it can leave memory.
1884
1918
  if (this.pendingPayloads.delete(rootHex)) {
1885
1919
  this.metrics?.blockInputSync.removedPayloads.inc({reason: headReason}, 1);
1886
1920
  }
@@ -1903,13 +1937,61 @@ export class BlockInputSync {
1903
1937
  }
1904
1938
  }
1905
1939
 
1940
+ /**
1941
+ * Same as `removeAndBanAllDescendants`, and downscores every peer that referenced or served a banned item, once per
1942
+ * peer. Only call for consensus-layer invalidity, never for an INVALID execution payload:
1943
+ * https://github.com/ethereum/consensus-specs/blob/master/specs/bellatrix/p2p-interface.md#the-reqresp-domain
1944
+ */
1945
+ private removeAndDownScoreAllDescendants(block: BlockInputSyncCacheItem, headReason: DroppedItemReason): void {
1946
+ const peersToReport = new Set<PeerIdStr>();
1947
+ for (const bannedBlock of this.removeAndBanAllDescendants(block, headReason)) {
1948
+ for (const peerIdStr of bannedBlock.peerIdStrings) {
1949
+ peersToReport.add(peerIdStr);
1950
+ }
1951
+ }
1952
+
1953
+ for (const peerIdStr of peersToReport) {
1954
+ this.network.reportPeer(peerIdStr, PeerAction.LowToleranceError, headReason);
1955
+ }
1956
+ }
1957
+
1958
+ /**
1959
+ * Removes `block` and all its pending descendants and bans their roots in `knownBadBlocks`. Only call when the chain
1960
+ * is provably bad: a banned root is never searched for again. Returns the banned items.
1961
+ */
1962
+ private removeAndBanAllDescendants(
1963
+ block: BlockInputSyncCacheItem,
1964
+ headReason: DroppedItemReason
1965
+ ): BlockInputSyncCacheItem[] {
1966
+ const bannedBlocks: BlockInputSyncCacheItem[] = [];
1967
+ for (const badBlock of this.removeAllDescendants(block, headReason, DroppedItemReason.invalidParent)) {
1968
+ const rootHex = getBlockInputSyncCacheItemRootHex(badBlock);
1969
+ // a gossip import may race a failing sync path, never ban a root fork choice has imported
1970
+ if (this.chain.forkChoice.hasBlockHex(rootHex)) {
1971
+ continue;
1972
+ }
1973
+ this.knownBadBlocks.add(rootHex);
1974
+ bannedBlocks.push(badBlock);
1975
+ this.logger.debug("Banning unknown block", {
1976
+ slot: getBlockInputSyncCacheItemSlot(badBlock),
1977
+ root: rootHex,
1978
+ reason: headReason,
1979
+ peerIdStrings: Array.from(badBlock.peerIdStrings)
1980
+ .map((id) => prettyPrintPeerIdStr(id))
1981
+ .join(","),
1982
+ });
1983
+ }
1984
+
1985
+ pruneSetToMax(this.knownBadBlocks, MAX_KNOWN_BAD_BLOCKS);
1986
+ return bannedBlocks;
1987
+ }
1988
+
1906
1989
  private removeAllDescendants(
1907
1990
  block: BlockInputSyncCacheItem,
1908
1991
  headReason: DroppedItemReason,
1909
1992
  descendantReason: DroppedItemReason = DroppedItemReason.invalidParent
1910
1993
  ): BlockInputSyncCacheItem[] {
1911
1994
  const rootHex = getBlockInputSyncCacheItemRootHex(block);
1912
- const slot = getBlockInputSyncCacheItemSlot(block);
1913
1995
  // Get all blocks that are a descendant of this one (index 0 is the head item itself)
1914
1996
  const badPendingBlocks = [block, ...getAllDescendantBlocks(rootHex, this.pendingBlocks)];
1915
1997
 
@@ -1926,8 +2008,8 @@ export class BlockInputSync {
1926
2008
  // Keep PayloadEnvelopeInput resident in the seen cache for consistency with the
1927
2009
  // importBlock()-owned lifecycle.
1928
2010
  this.logger.debug("Removing bad/unknown/incomplete BlockInputSyncCacheItem", {
1929
- slot,
1930
- blockRoot: rootHex,
2011
+ slot: getBlockInputSyncCacheItemSlot(block),
2012
+ root: rootHex,
1931
2013
  });
1932
2014
  }
1933
2015
 
@@ -1988,8 +2070,11 @@ export class UnknownBlockPeerBalancer {
1988
2070
  this.rateLimitedUntilByPeer.set(peerId, rateLimitedUntilMs);
1989
2071
  }
1990
2072
 
1991
- getNextRateLimitRetryAt(pendingColumns?: Set<number>, excludedPeers?: Set<PeerIdStr>): number | null {
1992
- const now = Date.now();
2073
+ getNextRateLimitRetryAt(
2074
+ pendingColumns?: Set<number>,
2075
+ excludedPeers?: Set<PeerIdStr>,
2076
+ now = Date.now()
2077
+ ): number | null {
1993
2078
  let retryAt: number | null = null;
1994
2079
 
1995
2080
  for (const [peerId, rateLimitedUntil] of this.rateLimitedUntilByPeer.entries()) {
@@ -2028,9 +2113,10 @@ export class UnknownBlockPeerBalancer {
2028
2113
  bestPeerForPendingColumns(
2029
2114
  pendingColumns: Set<number>,
2030
2115
  excludedPeers: Set<PeerIdStr>,
2031
- preferredPeers?: Set<PeerIdStr>
2116
+ preferredPeers?: Set<PeerIdStr>,
2117
+ now = Date.now()
2032
2118
  ): PeerSyncMeta | null {
2033
- const eligiblePeers = this.filterPeers(pendingColumns, excludedPeers);
2119
+ const eligiblePeers = this.filterPeers(pendingColumns, excludedPeers, now);
2034
2120
  if (eligiblePeers.length === 0) {
2035
2121
  return null;
2036
2122
  }
@@ -2071,8 +2157,7 @@ export class UnknownBlockPeerBalancer {
2071
2157
  return totalActiveRequests;
2072
2158
  }
2073
2159
 
2074
- private filterPeers(pendingDataColumns: Set<number>, excludedPeers: Set<PeerIdStr>): PeerIdStr[] {
2075
- const now = Date.now();
2160
+ private filterPeers(pendingDataColumns: Set<number>, excludedPeers: Set<PeerIdStr>, now: number): PeerIdStr[] {
2076
2161
  let maxColumnCount = 0;
2077
2162
  const considerPeers: {peerId: PeerIdStr; columnCount: number}[] = [];
2078
2163
  for (const [peerId, syncMeta] of this.peersMeta.entries()) {