@lodestar/beacon-node 1.51.0-dev.bdcac30ee0 → 1.51.0-dev.be91fc5558

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 (313) hide show
  1. package/lib/api/impl/api.js +1 -1
  2. package/lib/api/impl/api.js.map +1 -1
  3. package/lib/api/impl/beacon/blocks/index.d.ts +2 -1
  4. package/lib/api/impl/beacon/blocks/index.d.ts.map +1 -1
  5. package/lib/api/impl/beacon/blocks/index.js +29 -34
  6. package/lib/api/impl/beacon/blocks/index.js.map +1 -1
  7. package/lib/api/impl/beacon/index.d.ts +2 -1
  8. package/lib/api/impl/beacon/index.d.ts.map +1 -1
  9. package/lib/api/impl/beacon/index.js +2 -2
  10. package/lib/api/impl/beacon/index.js.map +1 -1
  11. package/lib/api/impl/beacon/pool/index.d.ts.map +1 -1
  12. package/lib/api/impl/beacon/pool/index.js +5 -2
  13. package/lib/api/impl/beacon/pool/index.js.map +1 -1
  14. package/lib/api/impl/beacon/state/utils.d.ts +1 -1
  15. package/lib/api/impl/beacon/state/utils.d.ts.map +1 -1
  16. package/lib/api/impl/validator/index.d.ts +6 -0
  17. package/lib/api/impl/validator/index.d.ts.map +1 -1
  18. package/lib/api/impl/validator/index.js +42 -26
  19. package/lib/api/impl/validator/index.js.map +1 -1
  20. package/lib/api/options.d.ts +2 -0
  21. package/lib/api/options.d.ts.map +1 -1
  22. package/lib/api/options.js +1 -0
  23. package/lib/api/options.js.map +1 -1
  24. package/lib/chain/GetBlobsTracker.d.ts +11 -0
  25. package/lib/chain/GetBlobsTracker.d.ts.map +1 -1
  26. package/lib/chain/GetBlobsTracker.js +85 -27
  27. package/lib/chain/GetBlobsTracker.js.map +1 -1
  28. package/lib/chain/archiveStore/archiveStore.d.ts.map +1 -1
  29. package/lib/chain/archiveStore/archiveStore.js +1 -0
  30. package/lib/chain/archiveStore/archiveStore.js.map +1 -1
  31. package/lib/chain/archiveStore/utils/archiveBlocks.d.ts +4 -0
  32. package/lib/chain/archiveStore/utils/archiveBlocks.d.ts.map +1 -1
  33. package/lib/chain/archiveStore/utils/archiveBlocks.js +61 -97
  34. package/lib/chain/archiveStore/utils/archiveBlocks.js.map +1 -1
  35. package/lib/chain/blocks/importBlock.d.ts.map +1 -1
  36. package/lib/chain/blocks/importBlock.js +6 -5
  37. package/lib/chain/blocks/importBlock.js.map +1 -1
  38. package/lib/chain/blocks/importExecutionPayload.d.ts +6 -11
  39. package/lib/chain/blocks/importExecutionPayload.d.ts.map +1 -1
  40. package/lib/chain/blocks/importExecutionPayload.js +8 -15
  41. package/lib/chain/blocks/importExecutionPayload.js.map +1 -1
  42. package/lib/chain/blocks/index.js +2 -2
  43. package/lib/chain/blocks/index.js.map +1 -1
  44. package/lib/chain/blocks/payloadEnvelopeProcessor.d.ts +12 -9
  45. package/lib/chain/blocks/payloadEnvelopeProcessor.d.ts.map +1 -1
  46. package/lib/chain/blocks/payloadEnvelopeProcessor.js +24 -19
  47. package/lib/chain/blocks/payloadEnvelopeProcessor.js.map +1 -1
  48. package/lib/chain/blocks/types.d.ts +4 -1
  49. package/lib/chain/blocks/types.d.ts.map +1 -1
  50. package/lib/chain/blocks/verifyBlock.d.ts.map +1 -1
  51. package/lib/chain/blocks/verifyBlock.js +18 -7
  52. package/lib/chain/blocks/verifyBlock.js.map +1 -1
  53. package/lib/chain/blocks/verifyBlocksDataAvailability.d.ts.map +1 -1
  54. package/lib/chain/blocks/verifyBlocksDataAvailability.js +8 -1
  55. package/lib/chain/blocks/verifyBlocksDataAvailability.js.map +1 -1
  56. package/lib/chain/blocks/verifyBlocksSignatures.d.ts.map +1 -1
  57. package/lib/chain/blocks/verifyBlocksSignatures.js +7 -5
  58. package/lib/chain/blocks/verifyBlocksSignatures.js.map +1 -1
  59. package/lib/chain/blocks/verifyPayloadsDataAvailability.d.ts +3 -3
  60. package/lib/chain/blocks/verifyPayloadsDataAvailability.d.ts.map +1 -1
  61. package/lib/chain/blocks/verifyPayloadsDataAvailability.js +11 -6
  62. package/lib/chain/blocks/verifyPayloadsDataAvailability.js.map +1 -1
  63. package/lib/chain/bls/interface.d.ts +2 -0
  64. package/lib/chain/bls/interface.d.ts.map +1 -1
  65. package/lib/chain/bls/multithread/index.d.ts.map +1 -1
  66. package/lib/chain/bls/multithread/index.js +13 -11
  67. package/lib/chain/bls/multithread/index.js.map +1 -1
  68. package/lib/chain/bls/multithread/types.d.ts +1 -1
  69. package/lib/chain/bls/multithread/types.d.ts.map +1 -1
  70. package/lib/chain/bls/multithread/worker.js +1 -1
  71. package/lib/chain/bls/multithread/worker.js.map +1 -1
  72. package/lib/chain/bls/singleThread.js +1 -1
  73. package/lib/chain/bls/singleThread.js.map +1 -1
  74. package/lib/chain/bls/utils.js +1 -1
  75. package/lib/chain/bls/utils.js.map +1 -1
  76. package/lib/chain/chain.d.ts +1 -1
  77. package/lib/chain/chain.d.ts.map +1 -1
  78. package/lib/chain/chain.js +4 -4
  79. package/lib/chain/chain.js.map +1 -1
  80. package/lib/chain/errors/blockError.d.ts +1 -1
  81. package/lib/chain/errors/blockError.d.ts.map +1 -1
  82. package/lib/chain/errors/blockError.js +1 -1
  83. package/lib/chain/errors/blockError.js.map +1 -1
  84. package/lib/chain/errors/executionPayloadEnvelope.d.ts +7 -0
  85. package/lib/chain/errors/executionPayloadEnvelope.d.ts.map +1 -1
  86. package/lib/chain/errors/executionPayloadEnvelope.js +1 -0
  87. package/lib/chain/errors/executionPayloadEnvelope.js.map +1 -1
  88. package/lib/chain/interface.d.ts +2 -3
  89. package/lib/chain/interface.d.ts.map +1 -1
  90. package/lib/chain/interface.js.map +1 -1
  91. package/lib/chain/opPools/aggregatedAttestationPool.d.ts.map +1 -1
  92. package/lib/chain/opPools/aggregatedAttestationPool.js +23 -10
  93. package/lib/chain/opPools/aggregatedAttestationPool.js.map +1 -1
  94. package/lib/chain/opPools/attestationPool.d.ts +1 -1
  95. package/lib/chain/opPools/attestationPool.d.ts.map +1 -1
  96. package/lib/chain/opPools/attestationPool.js +15 -4
  97. package/lib/chain/opPools/attestationPool.js.map +1 -1
  98. package/lib/chain/opPools/payloadAttestationPool.d.ts.map +1 -1
  99. package/lib/chain/opPools/payloadAttestationPool.js +17 -7
  100. package/lib/chain/opPools/payloadAttestationPool.js.map +1 -1
  101. package/lib/chain/opPools/syncCommitteeMessagePool.d.ts.map +1 -1
  102. package/lib/chain/opPools/syncCommitteeMessagePool.js +9 -3
  103. package/lib/chain/opPools/syncCommitteeMessagePool.js.map +1 -1
  104. package/lib/chain/opPools/syncContributionAndProofPool.d.ts.map +1 -1
  105. package/lib/chain/opPools/syncContributionAndProofPool.js +11 -3
  106. package/lib/chain/opPools/syncContributionAndProofPool.js.map +1 -1
  107. package/lib/chain/opPools/utils.d.ts +1 -1
  108. package/lib/chain/opPools/utils.d.ts.map +1 -1
  109. package/lib/chain/opPools/utils.js +1 -1
  110. package/lib/chain/opPools/utils.js.map +1 -1
  111. package/lib/chain/options.d.ts +0 -1
  112. package/lib/chain/options.d.ts.map +1 -1
  113. package/lib/chain/options.js +0 -1
  114. package/lib/chain/options.js.map +1 -1
  115. package/lib/chain/regen/interface.d.ts +0 -1
  116. package/lib/chain/regen/interface.d.ts.map +1 -1
  117. package/lib/chain/regen/interface.js +0 -1
  118. package/lib/chain/regen/interface.js.map +1 -1
  119. package/lib/chain/validation/executionPayloadBid.d.ts +0 -15
  120. package/lib/chain/validation/executionPayloadBid.d.ts.map +1 -1
  121. package/lib/chain/validation/executionPayloadBid.js +13 -166
  122. package/lib/chain/validation/executionPayloadBid.js.map +1 -1
  123. package/lib/chain/validation/executionPayloadEnvelope.d.ts +6 -0
  124. package/lib/chain/validation/executionPayloadEnvelope.d.ts.map +1 -1
  125. package/lib/chain/validation/executionPayloadEnvelope.js +53 -0
  126. package/lib/chain/validation/executionPayloadEnvelope.js.map +1 -1
  127. package/lib/db/beacon.d.ts +7 -0
  128. package/lib/db/beacon.d.ts.map +1 -1
  129. package/lib/db/beacon.js +34 -0
  130. package/lib/db/beacon.js.map +1 -1
  131. package/lib/db/dataColumnStore.d.ts +1 -1
  132. package/lib/db/dataColumnStore.d.ts.map +1 -1
  133. package/lib/db/dataColumnStore.js +1 -4
  134. package/lib/db/dataColumnStore.js.map +1 -1
  135. package/lib/db/flatFileStore/flatFileStore.js +5 -5
  136. package/lib/db/flatFileStore/flatFileStore.js.map +1 -1
  137. package/lib/db/flatFileStore/slotIndex.d.ts.map +1 -1
  138. package/lib/db/flatFileStore/slotIndex.js +2 -2
  139. package/lib/db/flatFileStore/slotIndex.js.map +1 -1
  140. package/lib/db/interface.d.ts +1 -0
  141. package/lib/db/interface.d.ts.map +1 -1
  142. package/lib/db/repositories/dataColumnSidecarArchive.d.ts +6 -0
  143. package/lib/db/repositories/dataColumnSidecarArchive.d.ts.map +1 -1
  144. package/lib/db/repositories/dataColumnSidecarArchive.js +19 -0
  145. package/lib/db/repositories/dataColumnSidecarArchive.js.map +1 -1
  146. package/lib/execution/builder/apiClient.d.ts +14 -8
  147. package/lib/execution/builder/apiClient.d.ts.map +1 -1
  148. package/lib/execution/builder/apiClient.js +12 -10
  149. package/lib/execution/builder/apiClient.js.map +1 -1
  150. package/lib/execution/builder/http.d.ts +2 -0
  151. package/lib/execution/builder/http.d.ts.map +1 -1
  152. package/lib/execution/builder/http.js +2 -0
  153. package/lib/execution/builder/http.js.map +1 -1
  154. package/lib/execution/engine/index.d.ts +1 -1
  155. package/lib/execution/engine/index.d.ts.map +1 -1
  156. package/lib/execution/engine/index.js +1 -1
  157. package/lib/execution/engine/index.js.map +1 -1
  158. package/lib/execution/engine/interface.d.ts +1 -1
  159. package/lib/execution/engine/interface.d.ts.map +1 -1
  160. package/lib/execution/engine/interface.js.map +1 -1
  161. package/lib/execution/engine/types.d.ts.map +1 -1
  162. package/lib/execution/engine/types.js +2 -2
  163. package/lib/execution/engine/types.js.map +1 -1
  164. package/lib/execution/engine/utils.d.ts.map +1 -1
  165. package/lib/execution/engine/utils.js +3 -2
  166. package/lib/execution/engine/utils.js.map +1 -1
  167. package/lib/index.d.ts +1 -0
  168. package/lib/index.d.ts.map +1 -1
  169. package/lib/index.js +2 -0
  170. package/lib/index.js.map +1 -1
  171. package/lib/metrics/metrics/lodestar.d.ts +7 -0
  172. package/lib/metrics/metrics/lodestar.d.ts.map +1 -1
  173. package/lib/metrics/metrics/lodestar.js +15 -0
  174. package/lib/metrics/metrics/lodestar.js.map +1 -1
  175. package/lib/metrics/server/http.js +5 -5
  176. package/lib/metrics/server/http.js.map +1 -1
  177. package/lib/monitoring/service.js +1 -1
  178. package/lib/monitoring/service.js.map +1 -1
  179. package/lib/network/core/networkCore.d.ts +2 -0
  180. package/lib/network/core/networkCore.d.ts.map +1 -1
  181. package/lib/network/core/networkCore.js +3 -0
  182. package/lib/network/core/networkCore.js.map +1 -1
  183. package/lib/network/core/networkCoreWorker.js +1 -0
  184. package/lib/network/core/networkCoreWorker.js.map +1 -1
  185. package/lib/network/core/networkCoreWorkerHandler.d.ts +2 -0
  186. package/lib/network/core/networkCoreWorkerHandler.d.ts.map +1 -1
  187. package/lib/network/core/networkCoreWorkerHandler.js +3 -0
  188. package/lib/network/core/networkCoreWorkerHandler.js.map +1 -1
  189. package/lib/network/core/types.d.ts +2 -0
  190. package/lib/network/core/types.d.ts.map +1 -1
  191. package/lib/network/interface.d.ts +2 -0
  192. package/lib/network/interface.d.ts.map +1 -1
  193. package/lib/network/libp2p/index.d.ts.map +1 -1
  194. package/lib/network/libp2p/index.js +5 -1
  195. package/lib/network/libp2p/index.js.map +1 -1
  196. package/lib/network/network.d.ts +2 -0
  197. package/lib/network/network.d.ts.map +1 -1
  198. package/lib/network/network.js +6 -1
  199. package/lib/network/network.js.map +1 -1
  200. package/lib/network/options.d.ts +2 -0
  201. package/lib/network/options.d.ts.map +1 -1
  202. package/lib/network/options.js.map +1 -1
  203. package/lib/network/peers/peerManager.d.ts +2 -1
  204. package/lib/network/peers/peerManager.d.ts.map +1 -1
  205. package/lib/network/peers/peerManager.js.map +1 -1
  206. package/lib/network/peers/utils/assertPeerRelevance.d.ts +9 -1
  207. package/lib/network/peers/utils/assertPeerRelevance.d.ts.map +1 -1
  208. package/lib/network/peers/utils/assertPeerRelevance.js +32 -21
  209. package/lib/network/peers/utils/assertPeerRelevance.js.map +1 -1
  210. package/lib/network/processor/gossipHandlers.d.ts.map +1 -1
  211. package/lib/network/processor/gossipHandlers.js +57 -37
  212. package/lib/network/processor/gossipHandlers.js.map +1 -1
  213. package/lib/node/nodejs.d.ts +3 -1
  214. package/lib/node/nodejs.d.ts.map +1 -1
  215. package/lib/node/nodejs.js +47 -21
  216. package/lib/node/nodejs.js.map +1 -1
  217. package/lib/node/options.d.ts +3 -1
  218. package/lib/node/options.d.ts.map +1 -1
  219. package/lib/node/options.js +4 -2
  220. package/lib/node/options.js.map +1 -1
  221. package/lib/sync/interface.d.ts.map +1 -1
  222. package/lib/sync/interface.js.map +1 -1
  223. package/lib/sync/sync.d.ts +7 -0
  224. package/lib/sync/sync.d.ts.map +1 -1
  225. package/lib/sync/sync.js +61 -3
  226. package/lib/sync/sync.js.map +1 -1
  227. package/lib/sync/unknownBlock.d.ts.map +1 -1
  228. package/lib/sync/unknownBlock.js +42 -18
  229. package/lib/sync/unknownBlock.js.map +1 -1
  230. package/lib/util/file.d.ts.map +1 -1
  231. package/lib/util/file.js +8 -9
  232. package/lib/util/file.js.map +1 -1
  233. package/lib/util/queue/itemQueue.js +1 -1
  234. package/lib/util/queue/itemQueue.js.map +1 -1
  235. package/lib/util/sszBytes.d.ts +5 -0
  236. package/lib/util/sszBytes.d.ts.map +1 -1
  237. package/lib/util/sszBytes.js +35 -2
  238. package/lib/util/sszBytes.js.map +1 -1
  239. package/package.json +16 -17
  240. package/src/api/impl/api.ts +1 -1
  241. package/src/api/impl/beacon/blocks/index.ts +57 -62
  242. package/src/api/impl/beacon/index.ts +3 -1
  243. package/src/api/impl/beacon/pool/index.ts +5 -1
  244. package/src/api/impl/beacon/state/utils.ts +1 -1
  245. package/src/api/impl/validator/index.ts +47 -30
  246. package/src/api/options.ts +3 -0
  247. package/src/chain/GetBlobsTracker.ts +97 -38
  248. package/src/chain/archiveStore/archiveStore.ts +1 -0
  249. package/src/chain/archiveStore/utils/archiveBlocks.ts +76 -130
  250. package/src/chain/blocks/importBlock.ts +8 -7
  251. package/src/chain/blocks/importExecutionPayload.ts +13 -21
  252. package/src/chain/blocks/index.ts +6 -2
  253. package/src/chain/blocks/payloadEnvelopeProcessor.ts +35 -24
  254. package/src/chain/blocks/types.ts +4 -1
  255. package/src/chain/blocks/verifyBlock.ts +31 -16
  256. package/src/chain/blocks/verifyBlocksDataAvailability.ts +10 -1
  257. package/src/chain/blocks/verifyBlocksSignatures.ts +7 -5
  258. package/src/chain/blocks/verifyPayloadsDataAvailability.ts +14 -6
  259. package/src/chain/bls/interface.ts +2 -0
  260. package/src/chain/bls/multithread/index.ts +14 -10
  261. package/src/chain/bls/multithread/types.ts +1 -1
  262. package/src/chain/bls/multithread/worker.ts +1 -5
  263. package/src/chain/bls/singleThread.ts +1 -1
  264. package/src/chain/bls/utils.ts +1 -1
  265. package/src/chain/chain.ts +5 -5
  266. package/src/chain/errors/blockError.ts +1 -1
  267. package/src/chain/errors/executionPayloadEnvelope.ts +8 -0
  268. package/src/chain/interface.ts +2 -3
  269. package/src/chain/opPools/aggregatedAttestationPool.ts +25 -9
  270. package/src/chain/opPools/attestationPool.ts +16 -4
  271. package/src/chain/opPools/payloadAttestationPool.ts +18 -7
  272. package/src/chain/opPools/syncCommitteeMessagePool.ts +10 -3
  273. package/src/chain/opPools/syncContributionAndProofPool.ts +13 -3
  274. package/src/chain/opPools/utils.ts +1 -1
  275. package/src/chain/options.ts +0 -2
  276. package/src/chain/regen/interface.ts +0 -1
  277. package/src/chain/validation/executionPayloadBid.ts +14 -193
  278. package/src/chain/validation/executionPayloadEnvelope.ts +68 -1
  279. package/src/db/beacon.ts +44 -0
  280. package/src/db/dataColumnStore.ts +2 -4
  281. package/src/db/flatFileStore/flatFileStore.ts +7 -7
  282. package/src/db/flatFileStore/slotIndex.ts +4 -3
  283. package/src/db/interface.ts +2 -0
  284. package/src/db/repositories/dataColumnSidecarArchive.ts +20 -0
  285. package/src/execution/builder/apiClient.ts +16 -13
  286. package/src/execution/builder/http.ts +4 -0
  287. package/src/execution/engine/index.ts +1 -1
  288. package/src/execution/engine/interface.ts +1 -1
  289. package/src/execution/engine/types.ts +3 -1
  290. package/src/execution/engine/utils.ts +4 -2
  291. package/src/index.ts +2 -0
  292. package/src/metrics/metrics/lodestar.ts +15 -0
  293. package/src/metrics/server/http.ts +5 -5
  294. package/src/monitoring/service.ts +1 -1
  295. package/src/network/core/networkCore.ts +5 -0
  296. package/src/network/core/networkCoreWorker.ts +1 -0
  297. package/src/network/core/networkCoreWorkerHandler.ts +4 -0
  298. package/src/network/core/types.ts +2 -0
  299. package/src/network/interface.ts +2 -0
  300. package/src/network/libp2p/index.ts +5 -1
  301. package/src/network/network.ts +8 -1
  302. package/src/network/options.ts +2 -0
  303. package/src/network/peers/peerManager.ts +1 -1
  304. package/src/network/peers/utils/assertPeerRelevance.ts +39 -23
  305. package/src/network/processor/gossipHandlers.ts +69 -40
  306. package/src/node/nodejs.ts +58 -22
  307. package/src/node/options.ts +9 -6
  308. package/src/sync/interface.ts +1 -0
  309. package/src/sync/sync.ts +65 -4
  310. package/src/sync/unknownBlock.ts +47 -28
  311. package/src/util/file.ts +8 -9
  312. package/src/util/queue/itemQueue.ts +1 -1
  313. package/src/util/sszBytes.ts +42 -2
@@ -24,6 +24,7 @@ import {
24
24
  deneb,
25
25
  fulu,
26
26
  gloas,
27
+ isGloasBeaconBlock,
27
28
  isGloasDataColumnSidecar,
28
29
  ssz,
29
30
  sszTypesFor,
@@ -65,7 +66,10 @@ import {
65
66
  validateGossipGloasDataColumnSidecar,
66
67
  } from "../../chain/validation/dataColumnSidecar.js";
67
68
  import {validateGossipExecutionPayloadBid} from "../../chain/validation/executionPayloadBid.js";
68
- import {validateGossipExecutionPayloadEnvelope} from "../../chain/validation/executionPayloadEnvelope.js";
69
+ import {
70
+ validateGossipExecutionPayloadEnvelope,
71
+ validateGossipExecutionPayloadEnvelopeSerialized,
72
+ } from "../../chain/validation/executionPayloadEnvelope.js";
69
73
  import {InclusionListValidationResult, validateGossipInclusionList} from "../../chain/validation/inclusionList.js";
70
74
  import {
71
75
  AggregateAndProofValidationResult,
@@ -150,6 +154,31 @@ export function getGossipHandlers(modules: ValidatorFnsModules, options: GossipH
150
154
  */
151
155
  function getSequentialHandlers(modules: ValidatorFnsModules, options: GossipHandlerOpts): SequentialGossipHandlers {
152
156
  const {chain, config, metrics, logger, core} = modules;
157
+ const incompletePayloadWaits = new WeakSet<PayloadEnvelopeInput>();
158
+
159
+ function scheduleIncompletePayloadEnvelope(payloadInput: PayloadEnvelopeInput, peer: string): void {
160
+ if (payloadInput.isComplete() || incompletePayloadWaits.has(payloadInput)) {
161
+ return;
162
+ }
163
+ incompletePayloadWaits.add(payloadInput);
164
+
165
+ const fork = config.getForkName(payloadInput.slot);
166
+ const cutoffTimeMs = getCutoffTimeMs(chain, payloadInput.slot, config.getPayloadDueMs(fork));
167
+ payloadInput.waitForEnvelopeAndAllData(cutoffTimeMs).catch(() => {
168
+ if (
169
+ payloadInput.isComplete() ||
170
+ chain.seenPayloadEnvelopeInputCache.get(payloadInput.blockRootHex) !== payloadInput
171
+ ) {
172
+ return;
173
+ }
174
+ chain.logger.debug("Payload envelope incomplete at payload deadline", payloadInput.getLogMeta());
175
+ chain.emitter.emit(ChainEvent.incompletePayloadEnvelope, {
176
+ payloadInput,
177
+ peer,
178
+ source: BlockInputSource.gossip,
179
+ });
180
+ });
181
+ }
153
182
 
154
183
  /**
155
184
  * Add a gossip block to the seen caches. The proposer signature of the block MUST be verified.
@@ -679,10 +708,8 @@ function getSequentialHandlers(modules: ValidatorFnsModules, options: GossipHand
679
708
 
680
709
  // Immediately attempt fetch of data columns from execution engine as the bid contains kzg commitments
681
710
  // which is all the information we need so there is no reason to delay until execution payload arrives
682
- // TODO GLOAS: If we want EL retries after this initial attempt, add an explicit retry policy here
683
- // (for example later in the slot). Do not couple retries to incoming gossip columns.
684
- // Columns fetched here feed payloadInput.addColumn, which resolves waitForAllData for any
685
- // in-flight importExecutionPayload. No processExecutionPayload trigger needed from this path.
711
+ // Columns fetched here feed payloadInput.addColumn, which resolves the waitForAllData of a
712
+ // pending payload import. No processExecutionPayload trigger needed from this path.
686
713
  chain.getBlobsTracker.triggerGetBlobs(payloadInput);
687
714
  }
688
715
  })
@@ -692,11 +719,10 @@ function getSequentialHandlers(modules: ValidatorFnsModules, options: GossipHand
692
719
 
693
720
  if (e instanceof BlockError) {
694
721
  switch (e.type.code) {
695
- case BlockErrorCode.DATA_UNAVAILABLE: {
696
- // Error is quite frequent and not critical
722
+ case BlockErrorCode.DATA_UNAVAILABLE:
723
+ // Data did not arrive in time, BlockInputSync keeps pulling it and retries the import
697
724
  logLevel = LogLevel.debug;
698
725
  break;
699
- }
700
726
  // ALREADY_KNOWN should not happen with ignoreIfKnown=true above
701
727
  // PARENT_BLOCK_UNKNOWN should not happen, we handled this in validateBeaconBlock() function above
702
728
  case BlockErrorCode.ALREADY_KNOWN:
@@ -764,7 +790,12 @@ function getSequentialHandlers(modules: ValidatorFnsModules, options: GossipHand
764
790
  // roots per proposer and slot, which bounds full imports over gossip to one alternate
765
791
  chain.seenBlockProposers.hasBlockRoot(signedBlock.message.slot, e.type.proposerIndex, e.type.root) &&
766
792
  // A sibling with an unknown parent cannot be imported, sync fetches it if its branch becomes relevant
767
- chain.forkChoice.getBlockHexDefaultStatus(toRootHex(signedBlock.message.parentRoot)) !== null
793
+ chain.forkChoice.getBlockHexDefaultStatus(toRootHex(signedBlock.message.parentRoot)) !== null &&
794
+ (!isGloasBeaconBlock(signedBlock.message) ||
795
+ chain.forkChoice.getBlockHexAndBlockHash(
796
+ toRootHex(signedBlock.message.parentRoot),
797
+ toRootHex(signedBlock.message.body.signedExecutionPayloadBid.message.parentBlockHash)
798
+ ) !== null)
768
799
  ) {
769
800
  const blockInput = cacheSignatureVerifiedBlock(
770
801
  signedBlock,
@@ -899,28 +930,10 @@ function getSequentialHandlers(modules: ValidatorFnsModules, options: GossipHand
899
930
  }
900
931
 
901
932
  // NOTE: we do NOT call chain.processExecutionPayload here. That is triggered only by
902
- // envelope arrival (gossip or API). An in-flight importExecutionPayload is awaiting
933
+ // envelope arrival (gossip or API). A pending payload import is awaiting
903
934
  // payloadInput.waitForAllData(); addColumn above will resolve it once hasAllData flips.
904
935
 
905
- if (!payloadInput.isComplete()) {
906
- const cutoffTimeMs = getCutoffTimeMs(chain, dataColumnSlot, BLOCK_AVAILABILITY_CUTOFF_MS);
907
- // do not await here to not delay gossip validation
908
- payloadInput.waitForEnvelopeAndAllData(cutoffTimeMs).catch((_e) => {
909
- chain.logger.debug(
910
- "Waited for envelope and data after receiving gossip column. Cut-off reached so emitting incompletePayloadEnvelope",
911
- {
912
- dataColumnIndex: index,
913
- ...payloadInputMeta,
914
- }
915
- );
916
- // TODO GLOAS: UnknownBlockSync to handle this event
917
- chain.emitter.emit(ChainEvent.incompletePayloadEnvelope, {
918
- payloadInput,
919
- peer: peerIdStr,
920
- source: BlockInputSource.gossip,
921
- });
922
- });
923
- }
936
+ scheduleIncompletePayloadEnvelope(payloadInput, peerIdStr);
924
937
  } catch (e) {
925
938
  logger.debug(
926
939
  "Error handling gossip data column",
@@ -1266,6 +1279,7 @@ function getSequentialHandlers(modules: ValidatorFnsModules, options: GossipHand
1266
1279
  seenTimestampSec,
1267
1280
  }: GossipHandlerParamGeneric<GossipType.execution_payload>) => {
1268
1281
  const {serializedData} = gossipData;
1282
+ validateGossipExecutionPayloadEnvelopeSerialized(chain, serializedData);
1269
1283
  const signedEnvelope = sszDeserialize(topic, serializedData);
1270
1284
  const envelope = signedEnvelope.message;
1271
1285
 
@@ -1318,17 +1332,23 @@ function getSequentialHandlers(modules: ValidatorFnsModules, options: GossipHand
1318
1332
  });
1319
1333
  }
1320
1334
 
1335
+ // Add the envelope in the same tick as validation, otherwise a by-root download that completes
1336
+ // before the next event loop sees no envelope, validates it again and adds it first
1337
+ if (!payloadInput.hasPayloadEnvelope()) {
1338
+ chain.serializedCache.set(signedEnvelope, serializedData);
1339
+
1340
+ payloadInput.addPayloadEnvelope({
1341
+ envelope: signedEnvelope,
1342
+ source: PayloadEnvelopeInputSource.gossip,
1343
+ seenTimestampSec,
1344
+ peerIdStr,
1345
+ });
1346
+ }
1347
+
1321
1348
  // Handler - deferred to next event loop so the validation result propagates first
1322
1349
  callInNextEventLoop(() => {
1323
1350
  try {
1324
- chain.serializedCache.set(signedEnvelope, serializedData);
1325
-
1326
- payloadInput.addPayloadEnvelope({
1327
- envelope: signedEnvelope,
1328
- source: PayloadEnvelopeInputSource.gossip,
1329
- seenTimestampSec,
1330
- peerIdStr,
1331
- });
1351
+ scheduleIncompletePayloadEnvelope(payloadInput, peerIdStr);
1332
1352
 
1333
1353
  chain.emitter.emit(routes.events.EventType.executionPayloadGossip, {
1334
1354
  slot,
@@ -1337,12 +1357,18 @@ function getSequentialHandlers(modules: ValidatorFnsModules, options: GossipHand
1337
1357
  blockRoot: blockRootHex,
1338
1358
  });
1339
1359
 
1340
- chain.processExecutionPayload(payloadInput, {validSignature: true}).catch((e) => {
1360
+ // Signature is verified above only for this envelope, one added by another source is verified at import
1361
+ const validSignature = payloadInput.getPayloadEnvelope() === signedEnvelope;
1362
+ chain.processExecutionPayload(payloadInput, {validSignature}).catch((e) => {
1341
1363
  // Adjust verbosity based on error type
1342
1364
  let logLevel: LogLevel;
1343
1365
 
1344
1366
  if (e instanceof PayloadError) {
1345
1367
  switch (e.type.code) {
1368
+ case PayloadErrorCode.DATA_UNAVAILABLE:
1369
+ // Columns did not arrive in time, BlockInputSync keeps pulling them and retries the import
1370
+ logLevel = LogLevel.debug;
1371
+ break;
1346
1372
  // BLOCK_NOT_IN_FORK_CHOICE should not happen, validateGossipExecutionPayloadEnvelope above
1347
1373
  // already verified the block is in fork choice
1348
1374
  case PayloadErrorCode.BLOCK_NOT_IN_FORK_CHOICE:
@@ -1351,11 +1377,14 @@ function getSequentialHandlers(modules: ValidatorFnsModules, options: GossipHand
1351
1377
  // Errors might indicate an issue with our node or the connected EL client
1352
1378
  logLevel = LogLevel.error;
1353
1379
  break;
1354
- // INVALID_SIGNATURE should not happen, signature is verified during gossip validation
1380
+ // INVALID_SIGNATURE only happens for an envelope added by another source
1355
1381
  case PayloadErrorCode.INVALID_SIGNATURE:
1356
1382
  case PayloadErrorCode.ENVELOPE_VERIFICATION_ERROR:
1357
1383
  case PayloadErrorCode.EXECUTION_ENGINE_INVALID:
1358
- core.reportPeer(peerIdStr, PeerAction.LowToleranceError, "BadGossipPayload");
1384
+ // Penalize only when the stored envelope is the one this peer sent, not one added by another source
1385
+ if (validSignature) {
1386
+ core.reportPeer(peerIdStr, PeerAction.LowToleranceError, "BadGossipPayload");
1387
+ }
1359
1388
  // The builder may have signed another envelope that is valid, keeping this one would make
1360
1389
  // by-root and range sync reuse it and never import the payload
1361
1390
  chain.seenPayloadEnvelopeInputCache.removeInvalid(payloadInput);
@@ -1,23 +1,29 @@
1
1
  import {setMaxListeners} from "node:events";
2
2
  import {PrivateKey} from "@libp2p/interface";
3
3
  import {Registry} from "prom-client";
4
+ import {hasher} from "@chainsafe/persistent-merkle-tree";
5
+ import {BeaconApiMethods} from "@lodestar/api/beacon/server";
6
+ import {BeaconConfig} from "@lodestar/config";
7
+ import type {LoggerNode} from "@lodestar/logger/node";
4
8
  import {
5
9
  init as initNativeMetrics,
6
10
  registerLocalValidator as registerNativeLocalValidator,
7
11
  scrapeMetrics as scrapeNativeMetrics,
8
12
  unregisterLocalValidator as unregisterNativeLocalValidator,
9
- } from "@chainsafe/lodestar-z/metrics";
10
- import {type PubkeyCache} from "@chainsafe/lodestar-z/pubkeys";
11
- import {hasher} from "@chainsafe/persistent-merkle-tree";
12
- import {BeaconApiMethods} from "@lodestar/api/beacon/server";
13
- import {BeaconConfig} from "@lodestar/config";
14
- import type {LoggerNode} from "@lodestar/logger/node";
13
+ } from "@lodestar/native/metrics";
14
+ import {type PubkeyCache} from "@lodestar/native/pubkeys";
15
15
  import {ZERO_HASH_HEX} from "@lodestar/params";
16
- import {IBeaconStateView, isStatePostBellatrix, isStatePostGloas} from "@lodestar/state-transition";
16
+ import {
17
+ IBeaconStateView,
18
+ computeStartSlotAtEpoch,
19
+ isStatePostBellatrix,
20
+ isStatePostGloas,
21
+ } from "@lodestar/state-transition";
17
22
  import {Slot} from "@lodestar/types";
18
- import {sleep, toRootHex} from "@lodestar/utils";
23
+ import {defer, sleep, toRootHex} from "@lodestar/utils";
19
24
  import {ProcessShutdownCallback} from "@lodestar/validator";
20
25
  import {BeaconRestApiServer, getApi} from "../api/index.js";
26
+ import {getDataColumnSidecarsMinEpoch} from "../chain/archiveStore/utils/archiveBlocks.js";
21
27
  import {BeaconChain, IBeaconChain, initBeaconMetrics} from "../chain/index.js";
22
28
  import {ValidatorMonitor, createValidatorMonitor} from "../chain/validatorMonitor.js";
23
29
  import {IBeaconDb} from "../db/index.js";
@@ -111,6 +117,7 @@ export class BeaconNode {
111
117
 
112
118
  status: BeaconNodeStatus;
113
119
  private controller?: AbortController;
120
+ private closePromise: Promise<void> | undefined;
114
121
 
115
122
  constructor({
116
123
  opts,
@@ -212,6 +219,17 @@ export class BeaconNode {
212
219
 
213
220
  const clock = new Clock({config, genesisTime: anchorState.genesisTime, signal});
214
221
 
222
+ // Before any lookup into the legacy column buckets, which can be slow until compacted
223
+ const columnsMinEpoch =
224
+ opts.chain.archiveDataEpochs === Infinity
225
+ ? null
226
+ : getDataColumnSidecarsMinEpoch(config, clock.currentEpoch, opts.chain.archiveDataEpochs);
227
+ await db.compactLegacyDataColumns(
228
+ columnsMinEpoch !== null && columnsMinEpoch >= config.FULU_FORK_EPOCH
229
+ ? computeStartSlotAtEpoch(columnsMinEpoch)
230
+ : 0
231
+ );
232
+
215
233
  await db.init();
216
234
 
217
235
  // Prune hot db repos
@@ -280,6 +298,7 @@ export class BeaconNode {
280
298
  : undefined,
281
299
  builderApiClientOpts: {
282
300
  timeout: opts.executionBuilder.timeout,
301
+ bidDeadline: opts.executionBuilder.bidDeadline,
283
302
  // Sent with all builder api requests, unless the node runs in private mode
284
303
  userAgent: opts.executionBuilder.userAgent,
285
304
  },
@@ -375,23 +394,40 @@ export class BeaconNode {
375
394
  /**
376
395
  * Stop beacon node and its sub-components.
377
396
  */
378
- async close(): Promise<void> {
379
- if (this.status === BeaconNodeStatus.started) {
380
- this.status = BeaconNodeStatus.closing;
381
- this.sync.close();
382
- if (this.restApi) await this.restApi.close();
383
- await this.network.close();
384
- if (this.metricsServer) await this.metricsServer.close();
385
- if (this.monitoring) await this.monitoring.close();
386
- await this.chain.persistToDisk();
387
- await this.chain.close();
397
+ close(): Promise<void> {
398
+ if (this.closePromise !== undefined) return this.closePromise;
399
+ const completion = defer<void>();
400
+ this.closePromise = completion.promise;
401
+ this.status = BeaconNodeStatus.closing;
402
+ void this.closeInternal().then(completion.resolve, completion.reject);
403
+ return this.closePromise;
404
+ }
405
+
406
+ private async closeInternal(): Promise<void> {
407
+ const errors: unknown[] = [];
408
+ for (const cleanup of [
409
+ () => this.sync.close(),
410
+ () => this.restApi?.close(),
411
+ () => this.network.close(),
412
+ () => this.metricsServer?.close(),
413
+ () => this.monitoring?.close(),
414
+ () => this.chain.persistToDisk(),
415
+ () => this.chain.close(),
388
416
  // Abort signal last: close() calls above clear intervals/timeouts so no new
389
417
  // operations get scheduled. If we aborted first, a still-pending interval could
390
418
  // fire and schedule a new operation after abort, leaving it stuck and delaying shutdown.
391
- if (this.controller) this.controller.abort();
392
- await sleep(DELAY_BEFORE_CLOSING_DB_MS);
393
- await this.db.close();
394
- this.status = BeaconNodeStatus.closed;
419
+ () => this.controller?.abort(),
420
+ () => sleep(DELAY_BEFORE_CLOSING_DB_MS),
421
+ () => this.db.close(),
422
+ ]) {
423
+ try {
424
+ await cleanup();
425
+ } catch (error) {
426
+ errors.push(error);
427
+ }
395
428
  }
429
+ this.status = BeaconNodeStatus.closed;
430
+ if (errors.length === 1) throw errors[0];
431
+ if (errors.length > 1) throw new AggregateError(errors, "Beacon node cleanup failed");
396
432
  }
397
433
  }
@@ -1,7 +1,9 @@
1
+ import {BLOCK_PRODUCTION_RACE_CUTOFF_GLOAS_BPS} from "../api/impl/validator/index.js";
1
2
  import {ApiOptions, defaultApiOptions} from "../api/options.js";
2
3
  import {ArchiveMode, DEFAULT_ARCHIVE_MODE, IChainOptions, defaultChainOptions} from "../chain/options.js";
3
4
  import {ValidatorMonitorOpts, defaultValidatorMonitorOpts} from "../chain/validatorMonitor.js";
4
5
  import {DatabaseOptions, defaultDbOptions} from "../db/options.js";
6
+ import {BUILDER_BID_LAG_BUFFER_MS} from "../execution/builder/apiClient.js";
5
7
  import {
6
8
  EngineApiMode,
7
9
  ExecutionBuilderOpts,
@@ -19,16 +21,17 @@ import {SyncOptions, defaultSyncOptions} from "../sync/options.js";
19
21
 
20
22
  // Re-export so the CLI doesn't need to depend on lodestar-api
21
23
  export {allNamespaces} from "../api/rest/index.js";
22
-
23
- // Re-export to use as default values in CLI args
24
+ export type {EngineApiMode};
25
+ // Re-export to use as default values and for validation in CLI args
24
26
  export {
25
- defaultExecutionEngineHttpOpts,
26
- defaultExecutionBuilderHttpOpts,
27
- engineApiModes,
28
27
  ArchiveMode,
28
+ BLOCK_PRODUCTION_RACE_CUTOFF_GLOAS_BPS,
29
+ BUILDER_BID_LAG_BUFFER_MS,
29
30
  DEFAULT_ARCHIVE_MODE,
31
+ defaultExecutionBuilderHttpOpts,
32
+ defaultExecutionEngineHttpOpts,
33
+ engineApiModes,
30
34
  };
31
- export type {EngineApiMode};
32
35
 
33
36
  export interface IBeaconNodeOptions {
34
37
  api: ApiOptions;
@@ -7,6 +7,7 @@ import {IBeaconDb} from "../db/index.js";
7
7
  import {Metrics} from "../metrics/index.js";
8
8
  import {INetwork} from "../network/index.js";
9
9
  import {SyncChainDebugState} from "./range/chain.js";
10
+
10
11
  export type {SyncChainDebugState};
11
12
 
12
13
  export type SyncingStatus = routes.node.SyncingStatus;
package/src/sync/sync.ts CHANGED
@@ -1,13 +1,17 @@
1
1
  import {SLOTS_PER_EPOCH} from "@lodestar/params";
2
- import {Slot} from "@lodestar/types";
2
+ import {computeStartSlotAtEpoch} from "@lodestar/state-transition";
3
+ import {Slot, Status} from "@lodestar/types";
3
4
  import {Logger, toRootHex} from "@lodestar/utils";
4
5
  import {IBeaconChain} from "../chain/index.js";
5
- import {GENESIS_SLOT} from "../constants/constants.js";
6
+ import {GENESIS_EPOCH, GENESIS_SLOT} from "../constants/constants.js";
7
+ import {GoodByeReasonCode} from "../constants/index.js";
8
+ import {IBeaconDb} from "../db/index.js";
6
9
  import {ExecutionEngineState} from "../execution/index.js";
7
10
  import {Metrics} from "../metrics/index.js";
8
- import {INetwork, NetworkEvent, NetworkEventData} from "../network/index.js";
11
+ import {INetwork, NetworkEvent, NetworkEventData, PeerAction} from "../network/index.js";
9
12
  import {ClockEvent} from "../util/clock.js";
10
13
  import {isOptimisticBlock} from "../util/forkChoice.js";
14
+ import {PeerIdStr} from "../util/peerId.js";
11
15
  import {MIN_EPOCH_TO_START_GOSSIP} from "./constants.js";
12
16
  import {IBeaconSync, SyncChainDebugState, SyncModules, SyncState, SyncingStatus, syncStateMetric} from "./interface.js";
13
17
  import {SyncOptions} from "./options.js";
@@ -19,6 +23,7 @@ export class BeaconSync implements IBeaconSync {
19
23
  private readonly logger: Logger;
20
24
  private readonly network: INetwork;
21
25
  private readonly chain: IBeaconChain;
26
+ private readonly db: IBeaconDb;
22
27
  private readonly metrics: Metrics | null;
23
28
  private readonly opts: SyncOptions;
24
29
 
@@ -30,10 +35,11 @@ export class BeaconSync implements IBeaconSync {
30
35
  private readonly slotImportTolerance: Slot;
31
36
 
32
37
  constructor(opts: SyncOptions, modules: SyncModules) {
33
- const {config, chain, metrics, network, logger} = modules;
38
+ const {config, chain, db, metrics, network, logger} = modules;
34
39
  this.opts = opts;
35
40
  this.network = network;
36
41
  this.chain = chain;
42
+ this.db = db;
37
43
  this.metrics = metrics;
38
44
  this.logger = logger;
39
45
  this.rangeSync = new RangeSync(modules, opts);
@@ -192,6 +198,11 @@ export class BeaconSync implements IBeaconSync {
192
198
  */
193
199
  private addPeer = (data: NetworkEventData[NetworkEvent.peerConnected]): void => {
194
200
  const localStatus = this.chain.getStatus();
201
+ // Genesis and equal finalized epochs are already checked by assertPeerRelevance in the network worker
202
+ if (data.status.finalizedEpoch > GENESIS_EPOCH && data.status.finalizedEpoch < localStatus.finalizedEpoch) {
203
+ void this.verifyPastFinalizedCheckpoint(data.peer, data.status);
204
+ }
205
+
195
206
  const syncType = getPeerSyncType(localStatus, data.status, this.chain.forkChoice, this.slotImportTolerance);
196
207
  this.logger.verbose("Peer sync type classified", {
197
208
  peer: data.peer,
@@ -216,6 +227,56 @@ export class BeaconSync implements IBeaconSync {
216
227
  this.updateSyncState();
217
228
  };
218
229
 
230
+ /**
231
+ * A behind peer's finalized checkpoint must be the canonical block in effect at the start slot of its epoch
232
+ */
233
+ async verifyPastFinalizedCheckpoint(peer: PeerIdStr, remote: Status): Promise<void> {
234
+ const startSlot = computeStartSlotAtEpoch(remote.finalizedEpoch);
235
+ const root = toRootHex(remote.finalizedRoot);
236
+ try {
237
+ let reason: string | null = null;
238
+ // After a finality jump, finalized blocks stay in fork choice until the archiver migrates them.
239
+ // Fork choice is pruned only after archiving, so a miss here means the db already has this range.
240
+ const canonicalBlock = this.chain.forkChoice.getCanonicalBlockClosestLteSlot(startSlot);
241
+ if (canonicalBlock !== null) {
242
+ if (canonicalBlock.blockRoot === root) return;
243
+ reason = "finalized_root_not_canonical";
244
+ } else {
245
+ const slot = await this.db.blockArchive.getSlotByRoot(remote.finalizedRoot);
246
+ if (slot !== null && slot > startSlot) {
247
+ reason = "finalized_root_after_epoch_start";
248
+ } else if (slot !== startSlot) {
249
+ const childSlot = await this.db.blockArchive.getSlotByParentRoot(remote.finalizedRoot);
250
+ if (childSlot !== null) {
251
+ if (childSlot <= startSlot) reason = "finalized_root_not_in_effect_at_epoch_start";
252
+ } else if (slot === null && startSlot > this.chain.earliestAvailableSlot) {
253
+ // childSlot == null too, neither the root nor a child is canonical
254
+ // or they are canonical and both pruned but peer's finalized epoch starts after earliestAvailableSlot
255
+ reason = "unknown_finalized_root";
256
+ }
257
+ }
258
+ }
259
+
260
+ if (reason !== null) {
261
+ this.logger.debug("Peer finalized checkpoint not in our chain", {
262
+ peer,
263
+ finalizedEpoch: remote.finalizedEpoch,
264
+ root,
265
+ reason,
266
+ });
267
+ this.metrics?.syncPeerFinalizedCheckpointMismatch.inc({reason});
268
+ this.network.reportPeer(peer, PeerAction.Fatal, "PeerFinalizedCheckpointMismatch");
269
+ await this.network.goodbyeAndDisconnect(peer, GoodByeReasonCode.IRRELEVANT_NETWORK);
270
+ }
271
+ } catch (e) {
272
+ this.logger.debug(
273
+ "Error verifying peer finalized checkpoint",
274
+ {peer, finalizedEpoch: remote.finalizedEpoch, root},
275
+ e as Error
276
+ );
277
+ }
278
+ }
279
+
219
280
  /**
220
281
  * Must be called by libp2p when a peer is removed from the peer manager
221
282
  */
@@ -300,10 +300,19 @@ export class BlockInputSync {
300
300
 
301
301
  private onIncompletePayloadEnvelope = (data: ChainEventData[ChainEvent.incompletePayloadEnvelope]): void => {
302
302
  try {
303
- this.addByPayloadInput(data.payloadInput, data.peer);
304
- this.triggerUnknownBlockSearch();
305
- this.metrics?.blockInputSync.requests.inc({type: PendingBlockType.INCOMPLETE_PAYLOAD_ENVELOPE});
306
- this.metrics?.blockInputSync.payloadSource.inc({source: data.source});
303
+ const isNewRoot = this.addByPayloadInput(data.payloadInput, data.peer);
304
+ const pendingPayload = this.pendingPayloads.get(data.payloadInput.blockRootHex);
305
+ if (
306
+ pendingPayload &&
307
+ pendingPayload.status !== PendingPayloadInputStatus.fetching &&
308
+ pendingPayload.status !== PendingPayloadInputStatus.processing
309
+ ) {
310
+ this.triggerUnknownBlockSearch();
311
+ }
312
+ if (isNewRoot) {
313
+ this.metrics?.blockInputSync.requests.inc({type: PendingBlockType.INCOMPLETE_PAYLOAD_ENVELOPE});
314
+ this.metrics?.blockInputSync.payloadSource.inc({source: data.source});
315
+ }
307
316
  } catch (e) {
308
317
  this.logger.debug("Error handling incompletePayloadEnvelope event", {}, e as Error);
309
318
  }
@@ -599,43 +608,44 @@ export class BlockInputSync {
599
608
  return this.chain.seenPayloadEnvelopeInputCache.get(rootHex)?.slot;
600
609
  }
601
610
 
602
- private addByPayloadInput = (
603
- payloadInput: PayloadEnvelopeInput,
604
- peerIdStr?: PeerIdStr,
605
- envelope?: gloas.SignedExecutionPayloadEnvelope
606
- ): void => {
611
+ private addByPayloadInput = (payloadInput: PayloadEnvelopeInput, peerIdStr?: PeerIdStr): boolean => {
607
612
  if (this.knownBadBlocks.has(payloadInput.blockRootHex)) {
608
613
  this.logger.debug("Ignoring payload input for known bad block root", {
609
614
  slot: payloadInput.slot,
610
615
  root: payloadInput.blockRootHex,
611
616
  peerIdStr,
612
617
  });
613
- return;
618
+ return false;
614
619
  }
615
620
 
616
- const pendingPayload = this.toPendingPayloadInput(
617
- payloadInput,
618
- this.pendingPayloads.get(payloadInput.blockRootHex),
619
- envelope
620
- );
621
+ let pendingPayload = this.pendingPayloads.get(payloadInput.blockRootHex);
622
+ const isNewRoot = pendingPayload === undefined;
623
+ // Envelope-only entries must go through reconciliation to validate their speculative envelope.
624
+ if (
625
+ !pendingPayload ||
626
+ pendingPayload.status === PendingPayloadInputStatus.pending ||
627
+ pendingPayload.status === PendingPayloadInputStatus.downloaded
628
+ ) {
629
+ pendingPayload = this.toPendingPayloadInput(payloadInput, pendingPayload);
630
+ this.pendingPayloads.set(payloadInput.blockRootHex, pendingPayload);
631
+
632
+ this.logger.verbose("Added payloadInput to BlockInputSync.pendingPayloads", {
633
+ slot: payloadInput.slot,
634
+ root: payloadInput.blockRootHex,
635
+ delaySec: this.chain.clock.secFromSlot(payloadInput.slot),
636
+ });
637
+ }
621
638
 
622
639
  if (peerIdStr && pendingPayload.peerIdStrings.size < MAX_PEERS_PER_ROOT) {
623
640
  pendingPayload.peerIdStrings.add(peerIdStr);
624
641
  }
625
642
 
626
- this.pendingPayloads.set(payloadInput.blockRootHex, pendingPayload);
627
-
628
- this.logger.verbose("Added payloadInput to BlockInputSync.pendingPayloads", {
629
- slot: payloadInput.slot,
630
- root: payloadInput.blockRootHex,
631
- delaySec: this.chain.clock.secFromSlot(payloadInput.slot),
632
- });
633
-
634
643
  const prunedItemCount = pruneSetToMax(this.pendingPayloads, this.maxPendingBlocks);
635
644
  if (prunedItemCount > 0) {
636
645
  this.metrics?.blockInputSync.removedPayloads.inc({reason: DroppedItemReason.capacity}, prunedItemCount);
637
646
  this.logger.verbose(`Pruned ${prunedItemCount} items from BlockInputSync.pendingPayloads`);
638
647
  }
648
+ return isNewRoot;
639
649
  };
640
650
 
641
651
  private onPeerConnected = (data: NetworkEventData[NetworkEvent.peerConnected]): void => {
@@ -1267,6 +1277,9 @@ export class BlockInputSync {
1267
1277
  }
1268
1278
 
1269
1279
  const payloadInput = await this.chain.seenPayloadEnvelopeInputCache.getOrReload(rootHex);
1280
+ if (this.pendingPayloads.get(rootHex) !== pendingPayload) {
1281
+ return;
1282
+ }
1270
1283
  if (!payloadInput) {
1271
1284
  this.logger.debug("PayloadEnvelopeInput not yet reloadable for imported block, will retry", {
1272
1285
  root: rootHex,
@@ -1278,6 +1291,9 @@ export class BlockInputSync {
1278
1291
  const validationResult = await wrapError(
1279
1292
  validateGossipExecutionPayloadEnvelope(this.chain, pendingPayload.envelope)
1280
1293
  );
1294
+ if (this.pendingPayloads.get(rootHex) !== pendingPayload) {
1295
+ return;
1296
+ }
1281
1297
  if (validationResult.err) {
1282
1298
  this.logger.debug(
1283
1299
  "Pending payload envelope failed validation after block import, refetching by root",
@@ -1345,14 +1361,13 @@ export class BlockInputSync {
1345
1361
  payload.status = PendingPayloadInputStatus.fetching;
1346
1362
 
1347
1363
  const res = await wrapError(this.fetchPayloadInput(payload));
1364
+ if (this.pendingPayloads.get(rootHex) !== payload) {
1365
+ this.logger.verbose("Dropping downloaded payload, entry changed during fetch", logCtx);
1366
+ return;
1367
+ }
1348
1368
  if (!res.err) {
1349
1369
  const pendingPayload = res.result;
1350
1370
  const resultRootHex = getPayloadSyncCacheItemRootHex(pendingPayload);
1351
- // finalization may have deleted this entry while we were awaiting the network, do not resurrect it via the set below
1352
- if (!this.pendingPayloads.has(resultRootHex)) {
1353
- this.logger.verbose("Dropping downloaded payload, entry pruned during fetch", logCtx);
1354
- return;
1355
- }
1356
1371
  this.pendingPayloads.set(resultRootHex, pendingPayload);
1357
1372
  const result = isPendingPayloadEnvelope(pendingPayload)
1358
1373
  ? DownloadResult.WaitingForBlock
@@ -1423,12 +1438,16 @@ export class BlockInputSync {
1423
1438
  this.logger.debug("Processing downloaded payload", logCtx);
1424
1439
 
1425
1440
  const res = await wrapError(this.chain.processExecutionPayload(pendingPayload.payloadInput));
1441
+ // The executionPayload event emitted during import usually removes the entry before this point
1426
1442
  if (!res.err) {
1427
1443
  this.logger.debug("Processed payload from unknown sync", logCtx);
1428
1444
  this.pendingPayloads.delete(rootHex);
1429
1445
  this.triggerUnknownBlockSearch();
1430
1446
  return;
1431
1447
  }
1448
+ if (this.pendingPayloads.get(rootHex) !== pendingPayload) {
1449
+ return;
1450
+ }
1432
1451
 
1433
1452
  if (res.err instanceof PayloadError) {
1434
1453
  switch (res.err.type.code) {