@lodestar/beacon-node 1.42.0-dev.efc1ab8e31 → 1.42.0-dev.f403d99def

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 (294) hide show
  1. package/lib/api/impl/beacon/blocks/index.d.ts.map +1 -1
  2. package/lib/api/impl/beacon/blocks/index.js +31 -11
  3. package/lib/api/impl/beacon/blocks/index.js.map +1 -1
  4. package/lib/api/impl/beacon/pool/index.d.ts.map +1 -1
  5. package/lib/api/impl/beacon/pool/index.js +4 -0
  6. package/lib/api/impl/beacon/pool/index.js.map +1 -1
  7. package/lib/api/impl/beacon/state/index.d.ts.map +1 -1
  8. package/lib/api/impl/beacon/state/index.js +13 -10
  9. package/lib/api/impl/beacon/state/index.js.map +1 -1
  10. package/lib/api/impl/debug/index.js.map +1 -1
  11. package/lib/api/impl/lodestar/index.d.ts.map +1 -1
  12. package/lib/api/impl/lodestar/index.js +4 -0
  13. package/lib/api/impl/lodestar/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 +9 -3
  16. package/lib/api/impl/validator/index.js.map +1 -1
  17. package/lib/chain/ColumnReconstructionTracker.d.ts +2 -1
  18. package/lib/chain/ColumnReconstructionTracker.d.ts.map +1 -1
  19. package/lib/chain/ColumnReconstructionTracker.js +5 -5
  20. package/lib/chain/ColumnReconstructionTracker.js.map +1 -1
  21. package/lib/chain/GetBlobsTracker.d.ts +2 -1
  22. package/lib/chain/GetBlobsTracker.d.ts.map +1 -1
  23. package/lib/chain/GetBlobsTracker.js +14 -12
  24. package/lib/chain/GetBlobsTracker.js.map +1 -1
  25. package/lib/chain/blocks/blockInput/blockInput.d.ts +5 -5
  26. package/lib/chain/blocks/blockInput/blockInput.d.ts.map +1 -1
  27. package/lib/chain/blocks/blockInput/blockInput.js.map +1 -1
  28. package/lib/chain/blocks/blockInput/types.d.ts +4 -4
  29. package/lib/chain/blocks/blockInput/types.d.ts.map +1 -1
  30. package/lib/chain/blocks/importBlock.d.ts.map +1 -1
  31. package/lib/chain/blocks/importBlock.js +19 -5
  32. package/lib/chain/blocks/importBlock.js.map +1 -1
  33. package/lib/chain/blocks/importExecutionPayload.d.ts.map +1 -1
  34. package/lib/chain/blocks/importExecutionPayload.js +14 -5
  35. package/lib/chain/blocks/importExecutionPayload.js.map +1 -1
  36. package/lib/chain/blocks/index.js +1 -1
  37. package/lib/chain/blocks/index.js.map +1 -1
  38. package/lib/chain/blocks/payloadEnvelopeInput/payloadEnvelopeInput.d.ts +14 -6
  39. package/lib/chain/blocks/payloadEnvelopeInput/payloadEnvelopeInput.d.ts.map +1 -1
  40. package/lib/chain/blocks/payloadEnvelopeInput/payloadEnvelopeInput.js +33 -2
  41. package/lib/chain/blocks/payloadEnvelopeInput/payloadEnvelopeInput.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/types.d.ts +3 -3
  45. package/lib/chain/blocks/types.d.ts.map +1 -1
  46. package/lib/chain/blocks/verifyBlocksExecutionPayloads.d.ts.map +1 -1
  47. package/lib/chain/blocks/verifyBlocksExecutionPayloads.js +3 -2
  48. package/lib/chain/blocks/verifyBlocksExecutionPayloads.js.map +1 -1
  49. package/lib/chain/blocks/verifyBlocksSignatures.d.ts.map +1 -1
  50. package/lib/chain/blocks/verifyBlocksSignatures.js +4 -2
  51. package/lib/chain/blocks/verifyBlocksSignatures.js.map +1 -1
  52. package/lib/chain/chain.d.ts +4 -2
  53. package/lib/chain/chain.d.ts.map +1 -1
  54. package/lib/chain/chain.js +77 -28
  55. package/lib/chain/chain.js.map +1 -1
  56. package/lib/chain/emitter.d.ts +29 -7
  57. package/lib/chain/emitter.d.ts.map +1 -1
  58. package/lib/chain/emitter.js +12 -3
  59. package/lib/chain/emitter.js.map +1 -1
  60. package/lib/chain/errors/blockError.d.ts +11 -1
  61. package/lib/chain/errors/blockError.d.ts.map +1 -1
  62. package/lib/chain/errors/blockError.js +4 -0
  63. package/lib/chain/errors/blockError.js.map +1 -1
  64. package/lib/chain/errors/dataColumnSidecarError.d.ts +31 -1
  65. package/lib/chain/errors/dataColumnSidecarError.d.ts.map +1 -1
  66. package/lib/chain/errors/dataColumnSidecarError.js +7 -0
  67. package/lib/chain/errors/dataColumnSidecarError.js.map +1 -1
  68. package/lib/chain/forkChoice/index.d.ts.map +1 -1
  69. package/lib/chain/forkChoice/index.js +10 -8
  70. package/lib/chain/forkChoice/index.js.map +1 -1
  71. package/lib/chain/interface.d.ts +5 -2
  72. package/lib/chain/interface.d.ts.map +1 -1
  73. package/lib/chain/interface.js.map +1 -1
  74. package/lib/chain/lightClient/index.d.ts +2 -2
  75. package/lib/chain/lightClient/index.d.ts.map +1 -1
  76. package/lib/chain/lightClient/index.js +7 -0
  77. package/lib/chain/lightClient/index.js.map +1 -1
  78. package/lib/chain/opPools/aggregatedAttestationPool.d.ts.map +1 -1
  79. package/lib/chain/opPools/aggregatedAttestationPool.js +5 -2
  80. package/lib/chain/opPools/aggregatedAttestationPool.js.map +1 -1
  81. package/lib/chain/opPools/executionPayloadBidPool.d.ts +2 -2
  82. package/lib/chain/opPools/executionPayloadBidPool.d.ts.map +1 -1
  83. package/lib/chain/opPools/executionPayloadBidPool.js +2 -2
  84. package/lib/chain/opPools/executionPayloadBidPool.js.map +1 -1
  85. package/lib/chain/prepareNextSlot.d.ts.map +1 -1
  86. package/lib/chain/prepareNextSlot.js +7 -1
  87. package/lib/chain/prepareNextSlot.js.map +1 -1
  88. package/lib/chain/produceBlock/computeNewStateRoot.d.ts +3 -3
  89. package/lib/chain/produceBlock/computeNewStateRoot.d.ts.map +1 -1
  90. package/lib/chain/produceBlock/computeNewStateRoot.js +8 -8
  91. package/lib/chain/produceBlock/computeNewStateRoot.js.map +1 -1
  92. package/lib/chain/produceBlock/produceBlockBody.d.ts +5 -5
  93. package/lib/chain/produceBlock/produceBlockBody.d.ts.map +1 -1
  94. package/lib/chain/produceBlock/produceBlockBody.js +23 -4
  95. package/lib/chain/produceBlock/produceBlockBody.js.map +1 -1
  96. package/lib/chain/seenCache/seenGossipBlockInput.d.ts +1 -1
  97. package/lib/chain/seenCache/seenGossipBlockInput.d.ts.map +1 -1
  98. package/lib/chain/seenCache/seenGossipBlockInput.js +2 -2
  99. package/lib/chain/seenCache/seenGossipBlockInput.js.map +1 -1
  100. package/lib/chain/seenCache/seenPayloadEnvelopeInput.d.ts +1 -1
  101. package/lib/chain/seenCache/seenPayloadEnvelopeInput.d.ts.map +1 -1
  102. package/lib/chain/seenCache/seenPayloadEnvelopeInput.js +2 -2
  103. package/lib/chain/seenCache/seenPayloadEnvelopeInput.js.map +1 -1
  104. package/lib/chain/validation/block.d.ts.map +1 -1
  105. package/lib/chain/validation/block.js +27 -5
  106. package/lib/chain/validation/block.js.map +1 -1
  107. package/lib/chain/validation/dataColumnSidecar.d.ts +11 -4
  108. package/lib/chain/validation/dataColumnSidecar.d.ts.map +1 -1
  109. package/lib/chain/validation/dataColumnSidecar.js +184 -5
  110. package/lib/chain/validation/dataColumnSidecar.js.map +1 -1
  111. package/lib/chain/validation/executionPayloadBid.d.ts.map +1 -1
  112. package/lib/chain/validation/executionPayloadBid.js +7 -4
  113. package/lib/chain/validation/executionPayloadBid.js.map +1 -1
  114. package/lib/chain/validation/executionPayloadEnvelope.d.ts.map +1 -1
  115. package/lib/chain/validation/executionPayloadEnvelope.js +6 -1
  116. package/lib/chain/validation/executionPayloadEnvelope.js.map +1 -1
  117. package/lib/chain/validation/payloadAttestationMessage.d.ts.map +1 -1
  118. package/lib/chain/validation/payloadAttestationMessage.js +4 -1
  119. package/lib/chain/validation/payloadAttestationMessage.js.map +1 -1
  120. package/lib/chain/validation/syncCommittee.d.ts.map +1 -1
  121. package/lib/chain/validation/syncCommittee.js +4 -0
  122. package/lib/chain/validation/syncCommittee.js.map +1 -1
  123. package/lib/chain/validation/syncCommitteeContributionAndProof.js +4 -1
  124. package/lib/chain/validation/syncCommitteeContributionAndProof.js.map +1 -1
  125. package/lib/chain/validatorMonitor.d.ts.map +1 -1
  126. package/lib/chain/validatorMonitor.js +3 -3
  127. package/lib/chain/validatorMonitor.js.map +1 -1
  128. package/lib/db/buckets.d.ts +2 -2
  129. package/lib/db/buckets.d.ts.map +1 -1
  130. package/lib/db/buckets.js +2 -2
  131. package/lib/db/buckets.js.map +1 -1
  132. package/lib/db/repositories/blockArchiveIndex.d.ts +2 -2
  133. package/lib/db/repositories/blockArchiveIndex.d.ts.map +1 -1
  134. package/lib/db/repositories/dataColumnSidecar.d.ts.map +1 -1
  135. package/lib/db/repositories/dataColumnSidecar.js +4 -2
  136. package/lib/db/repositories/dataColumnSidecar.js.map +1 -1
  137. package/lib/db/repositories/dataColumnSidecarArchive.d.ts.map +1 -1
  138. package/lib/db/repositories/dataColumnSidecarArchive.js +4 -2
  139. package/lib/db/repositories/dataColumnSidecarArchive.js.map +1 -1
  140. package/lib/metrics/metrics/lodestar.d.ts +20 -0
  141. package/lib/metrics/metrics/lodestar.d.ts.map +1 -1
  142. package/lib/metrics/metrics/lodestar.js +33 -0
  143. package/lib/metrics/metrics/lodestar.js.map +1 -1
  144. package/lib/network/interface.d.ts +3 -2
  145. package/lib/network/interface.d.ts.map +1 -1
  146. package/lib/network/libp2p/index.d.ts.map +1 -1
  147. package/lib/network/libp2p/index.js +19 -13
  148. package/lib/network/libp2p/index.js.map +1 -1
  149. package/lib/network/network.d.ts +3 -2
  150. package/lib/network/network.d.ts.map +1 -1
  151. package/lib/network/network.js +3 -0
  152. package/lib/network/network.js.map +1 -1
  153. package/lib/network/options.d.ts.map +1 -1
  154. package/lib/network/options.js +7 -2
  155. package/lib/network/options.js.map +1 -1
  156. package/lib/network/processor/extractSlotRootFns.d.ts +1 -1
  157. package/lib/network/processor/extractSlotRootFns.d.ts.map +1 -1
  158. package/lib/network/processor/extractSlotRootFns.js +25 -5
  159. package/lib/network/processor/extractSlotRootFns.js.map +1 -1
  160. package/lib/network/processor/gossipHandlers.d.ts.map +1 -1
  161. package/lib/network/processor/gossipHandlers.js +246 -66
  162. package/lib/network/processor/gossipHandlers.js.map +1 -1
  163. package/lib/network/processor/index.d.ts +11 -1
  164. package/lib/network/processor/index.d.ts.map +1 -1
  165. package/lib/network/processor/index.js +234 -22
  166. package/lib/network/processor/index.js.map +1 -1
  167. package/lib/network/reqresp/handlers/beaconBlocksByRange.d.ts.map +1 -1
  168. package/lib/network/reqresp/handlers/beaconBlocksByRange.js +2 -1
  169. package/lib/network/reqresp/handlers/beaconBlocksByRange.js.map +1 -1
  170. package/lib/network/reqresp/handlers/beaconBlocksByRoot.d.ts.map +1 -1
  171. package/lib/network/reqresp/handlers/beaconBlocksByRoot.js +2 -0
  172. package/lib/network/reqresp/handlers/beaconBlocksByRoot.js.map +1 -1
  173. package/lib/network/reqresp/handlers/blobSidecarsByRange.d.ts +2 -2
  174. package/lib/network/reqresp/handlers/blobSidecarsByRange.d.ts.map +1 -1
  175. package/lib/network/reqresp/handlers/blobSidecarsByRange.js +7 -3
  176. package/lib/network/reqresp/handlers/blobSidecarsByRange.js.map +1 -1
  177. package/lib/network/reqresp/handlers/blobSidecarsByRoot.d.ts.map +1 -1
  178. package/lib/network/reqresp/handlers/blobSidecarsByRoot.js +6 -0
  179. package/lib/network/reqresp/handlers/blobSidecarsByRoot.js.map +1 -1
  180. package/lib/network/reqresp/handlers/dataColumnSidecarsByRange.d.ts +2 -2
  181. package/lib/network/reqresp/handlers/dataColumnSidecarsByRange.d.ts.map +1 -1
  182. package/lib/network/reqresp/handlers/dataColumnSidecarsByRange.js +7 -3
  183. package/lib/network/reqresp/handlers/dataColumnSidecarsByRange.js.map +1 -1
  184. package/lib/network/reqresp/handlers/executionPayloadEnvelopesByRange.d.ts.map +1 -1
  185. package/lib/network/reqresp/handlers/executionPayloadEnvelopesByRange.js +2 -1
  186. package/lib/network/reqresp/handlers/executionPayloadEnvelopesByRange.js.map +1 -1
  187. package/lib/network/reqresp/handlers/executionPayloadEnvelopesByRoot.d.ts.map +1 -1
  188. package/lib/network/reqresp/handlers/executionPayloadEnvelopesByRoot.js +3 -8
  189. package/lib/network/reqresp/handlers/executionPayloadEnvelopesByRoot.js.map +1 -1
  190. package/lib/network/reqresp/types.d.ts +3 -3
  191. package/lib/network/reqresp/types.d.ts.map +1 -1
  192. package/lib/network/reqresp/types.js +9 -3
  193. package/lib/network/reqresp/types.js.map +1 -1
  194. package/lib/node/nodejs.d.ts.map +1 -1
  195. package/lib/node/nodejs.js +4 -1
  196. package/lib/node/nodejs.js.map +1 -1
  197. package/lib/node/notifier.d.ts.map +1 -1
  198. package/lib/node/notifier.js +2 -2
  199. package/lib/node/notifier.js.map +1 -1
  200. package/lib/sync/unknownBlock.js +2 -2
  201. package/lib/sync/unknownBlock.js.map +1 -1
  202. package/lib/sync/utils/downloadByRange.d.ts +3 -3
  203. package/lib/sync/utils/downloadByRange.d.ts.map +1 -1
  204. package/lib/sync/utils/downloadByRange.js +4 -2
  205. package/lib/sync/utils/downloadByRange.js.map +1 -1
  206. package/lib/sync/utils/downloadByRoot.d.ts +3 -3
  207. package/lib/sync/utils/downloadByRoot.d.ts.map +1 -1
  208. package/lib/sync/utils/downloadByRoot.js +10 -5
  209. package/lib/sync/utils/downloadByRoot.js.map +1 -1
  210. package/lib/util/blobs.d.ts +3 -3
  211. package/lib/util/blobs.d.ts.map +1 -1
  212. package/lib/util/blobs.js +21 -10
  213. package/lib/util/blobs.js.map +1 -1
  214. package/lib/util/dataColumns.d.ts +18 -11
  215. package/lib/util/dataColumns.d.ts.map +1 -1
  216. package/lib/util/dataColumns.js +51 -17
  217. package/lib/util/dataColumns.js.map +1 -1
  218. package/lib/util/execution.d.ts +6 -2
  219. package/lib/util/execution.d.ts.map +1 -1
  220. package/lib/util/execution.js +49 -25
  221. package/lib/util/execution.js.map +1 -1
  222. package/lib/util/sszBytes.d.ts +25 -1
  223. package/lib/util/sszBytes.d.ts.map +1 -1
  224. package/lib/util/sszBytes.js +189 -2
  225. package/lib/util/sszBytes.js.map +1 -1
  226. package/package.json +15 -15
  227. package/src/api/impl/beacon/blocks/index.ts +46 -14
  228. package/src/api/impl/beacon/pool/index.ts +4 -0
  229. package/src/api/impl/beacon/state/index.ts +15 -15
  230. package/src/api/impl/debug/index.ts +2 -2
  231. package/src/api/impl/lodestar/index.ts +4 -0
  232. package/src/api/impl/validator/index.ts +9 -2
  233. package/src/chain/ColumnReconstructionTracker.ts +6 -5
  234. package/src/chain/GetBlobsTracker.ts +14 -12
  235. package/src/chain/blocks/blockInput/blockInput.ts +8 -8
  236. package/src/chain/blocks/blockInput/types.ts +4 -4
  237. package/src/chain/blocks/importBlock.ts +29 -8
  238. package/src/chain/blocks/importExecutionPayload.ts +15 -5
  239. package/src/chain/blocks/index.ts +1 -1
  240. package/src/chain/blocks/payloadEnvelopeInput/payloadEnvelopeInput.ts +53 -12
  241. package/src/chain/blocks/payloadEnvelopeInput/types.ts +2 -1
  242. package/src/chain/blocks/types.ts +3 -3
  243. package/src/chain/blocks/verifyBlocksExecutionPayloads.ts +2 -1
  244. package/src/chain/blocks/verifyBlocksSignatures.ts +9 -2
  245. package/src/chain/chain.ts +93 -32
  246. package/src/chain/emitter.ts +25 -7
  247. package/src/chain/errors/blockError.ts +7 -1
  248. package/src/chain/errors/dataColumnSidecarError.ts +32 -1
  249. package/src/chain/forkChoice/index.ts +11 -8
  250. package/src/chain/interface.ts +9 -2
  251. package/src/chain/lightClient/index.ts +15 -3
  252. package/src/chain/opPools/aggregatedAttestationPool.ts +6 -1
  253. package/src/chain/opPools/executionPayloadBidPool.ts +3 -3
  254. package/src/chain/prepareNextSlot.ts +8 -0
  255. package/src/chain/produceBlock/computeNewStateRoot.ts +11 -10
  256. package/src/chain/produceBlock/produceBlockBody.ts +40 -10
  257. package/src/chain/seenCache/seenGossipBlockInput.ts +2 -2
  258. package/src/chain/seenCache/seenPayloadEnvelopeInput.ts +2 -2
  259. package/src/chain/validation/block.ts +30 -7
  260. package/src/chain/validation/dataColumnSidecar.ts +230 -7
  261. package/src/chain/validation/executionPayloadBid.ts +7 -3
  262. package/src/chain/validation/executionPayloadEnvelope.ts +10 -1
  263. package/src/chain/validation/payloadAttestationMessage.ts +4 -0
  264. package/src/chain/validation/syncCommittee.ts +5 -1
  265. package/src/chain/validation/syncCommitteeContributionAndProof.ts +5 -1
  266. package/src/chain/validatorMonitor.ts +3 -2
  267. package/src/db/buckets.ts +2 -2
  268. package/src/db/repositories/dataColumnSidecar.ts +4 -2
  269. package/src/db/repositories/dataColumnSidecarArchive.ts +4 -2
  270. package/src/metrics/metrics/lodestar.ts +34 -0
  271. package/src/network/interface.ts +3 -2
  272. package/src/network/libp2p/index.ts +21 -15
  273. package/src/network/network.ts +7 -4
  274. package/src/network/options.ts +7 -2
  275. package/src/network/processor/extractSlotRootFns.ts +32 -6
  276. package/src/network/processor/gossipHandlers.ts +310 -79
  277. package/src/network/processor/index.ts +304 -22
  278. package/src/network/reqresp/handlers/beaconBlocksByRange.ts +3 -1
  279. package/src/network/reqresp/handlers/beaconBlocksByRoot.ts +3 -0
  280. package/src/network/reqresp/handlers/blobSidecarsByRange.ts +15 -3
  281. package/src/network/reqresp/handlers/blobSidecarsByRoot.ts +11 -0
  282. package/src/network/reqresp/handlers/dataColumnSidecarsByRange.ts +19 -3
  283. package/src/network/reqresp/handlers/executionPayloadEnvelopesByRange.ts +3 -1
  284. package/src/network/reqresp/handlers/executionPayloadEnvelopesByRoot.ts +3 -12
  285. package/src/network/reqresp/types.ts +13 -5
  286. package/src/node/nodejs.ts +5 -2
  287. package/src/node/notifier.ts +7 -2
  288. package/src/sync/unknownBlock.ts +3 -3
  289. package/src/sync/utils/downloadByRange.ts +9 -7
  290. package/src/sync/utils/downloadByRoot.ts +16 -12
  291. package/src/util/blobs.ts +35 -15
  292. package/src/util/dataColumns.ts +69 -25
  293. package/src/util/execution.ts +49 -30
  294. package/src/util/sszBytes.ts +245 -3
@@ -12,6 +12,16 @@ import {Metrics} from "../../metrics/metrics.js";
12
12
  import {ClockEvent} from "../../util/clock.js";
13
13
  import {callInNextEventLoop} from "../../util/eventLoop.js";
14
14
  import {PeerIdStr} from "../../util/peerId.js";
15
+ import {
16
+ getBeaconBlockRootFromExecutionPayloadEnvelopeSerialized,
17
+ getDataIndexFromSignedAggregateAndProofSerialized,
18
+ getDataIndexFromSingleAttestationSerialized,
19
+ getParentBlockHashFromGloasSignedBeaconBlockSerialized,
20
+ getParentBlockHashFromSignedExecutionPayloadBidSerialized,
21
+ getParentBlockRootFromSignedExecutionPayloadBidSerialized,
22
+ getParentRootFromSignedBeaconBlockSerialized,
23
+ getPayloadPresentFromPayloadAttestationMessageSerialized,
24
+ } from "../../util/sszBytes.js";
15
25
  import {NetworkEvent, NetworkEventBus} from "../events.js";
16
26
  import {
17
27
  GossipHandlers,
@@ -89,6 +99,8 @@ const MAX_JOBS_SUBMITTED_PER_TICK = 128;
89
99
 
90
100
  // How many gossip messages we keep before new ones get dropped.
91
101
  const MAX_QUEUED_UNKNOWN_BLOCK_GOSSIP_OBJECTS = 16_384;
102
+ // TODO gloas: arbitrary constant, check metrics.
103
+ const MAX_QUEUED_UNKNOWN_PAYLOAD_GOSSIP_OBJECTS = 1024;
92
104
 
93
105
  // We don't want to process too many gossip messages in a single tick
94
106
  // As seen on mainnet, gossip messages concurrency metric ranges from 1000 to 2000
@@ -126,6 +138,20 @@ export enum CannotAcceptWorkReason {
126
138
  regen = "regen_busy",
127
139
  }
128
140
 
141
+ /**
142
+ * No metrics needed here; using a number to keep it lightweight
143
+ */
144
+ enum PreprocessAction {
145
+ AwaitBlock,
146
+ AwaitEnvelope,
147
+ PushToQueue,
148
+ }
149
+
150
+ type PreprocessResult =
151
+ | {action: PreprocessAction.PushToQueue}
152
+ | {action: PreprocessAction.AwaitBlock; root: RootHex}
153
+ | {action: PreprocessAction.AwaitEnvelope; root: RootHex};
154
+
129
155
  /**
130
156
  * Network processor handles the gossip queues and throtles processing to not overload the main thread
131
157
  * - Decides when to process work and what to process
@@ -159,7 +185,11 @@ export class NetworkProcessor {
159
185
  // we may not receive the block for messages like Attestation and SignedAggregateAndProof messages, in that case PendingGossipsubMessage needs
160
186
  // to be stored in this Map and reprocessed once the block comes
161
187
  private readonly awaitingMessagesByBlockRoot: MapDef<RootHex, Set<PendingGossipsubMessage>>;
188
+ // we may not receive the payload for messages that require the FULL payload variant to be processed,
189
+ // in that case PendingGossipsubMessage needs to be stored in this Map and reprocessed once the payload comes
190
+ private readonly awaitingMessagesByPayloadBlockRoot: MapDef<RootHex, Set<PendingGossipsubMessage>>;
162
191
  private unknownBlocksBySlot = new MapDef<Slot, Set<RootHex>>(() => new Set());
192
+ private unknownEnvelopesBySlot = new MapDef<Slot, Set<RootHex>>(() => new Set());
163
193
 
164
194
  constructor(
165
195
  modules: NetworkProcessorModules,
@@ -179,11 +209,13 @@ export class NetworkProcessor {
179
209
  modules
180
210
  );
181
211
 
182
- events.on(NetworkEvent.pendingGossipsubMessage, this.onPendingGossipsubMessage.bind(this));
183
- this.chain.emitter.on(routes.events.EventType.block, this.onBlockProcessed.bind(this));
184
- this.chain.clock.on(ClockEvent.slot, this.onClockSlot.bind(this));
212
+ events.on(NetworkEvent.pendingGossipsubMessage, this.onPendingGossipsubMessage);
213
+ this.chain.emitter.on(routes.events.EventType.block, this.onBlockProcessed);
214
+ this.chain.emitter.on(routes.events.EventType.executionPayload, this.onPayloadEnvelopeProcessed);
215
+ this.chain.clock.on(ClockEvent.slot, this.onClockSlot);
185
216
 
186
217
  this.awaitingMessagesByBlockRoot = new MapDef<RootHex, Set<PendingGossipsubMessage>>(() => new Set());
218
+ this.awaitingMessagesByPayloadBlockRoot = new MapDef<RootHex, Set<PendingGossipsubMessage>>(() => new Set());
187
219
 
188
220
  // TODO: Implement queues and priorization for ReqResp incoming requests
189
221
  // Listens to NetworkEvent.reqRespIncomingRequest event
@@ -196,6 +228,7 @@ export class NetworkProcessor {
196
228
  metrics.gossipValidationQueue.concurrency.set({topic}, this.gossipTopicConcurrency[topic]);
197
229
  }
198
230
  metrics.awaitingBlockGossipMessages.countPerSlot.set(this.unknownBlockGossipsubMessagesCount);
231
+ metrics.awaitingPayloadGossipMessages.countPerSlot.set(this.unknownPayloadGossipsubMessagesCount);
199
232
  // specific metric for beacon_attestation topic
200
233
  metrics.gossipValidationQueue.keyAge.reset();
201
234
  for (const ageMs of this.gossipQueues.beacon_attestation.getDataAgeMs()) {
@@ -212,6 +245,7 @@ export class NetworkProcessor {
212
245
  async stop(): Promise<void> {
213
246
  this.events.off(NetworkEvent.pendingGossipsubMessage, this.onPendingGossipsubMessage);
214
247
  this.chain.emitter.off(routes.events.EventType.block, this.onBlockProcessed);
248
+ this.chain.emitter.off(routes.events.EventType.executionPayload, this.onPayloadEnvelopeProcessed);
215
249
  this.chain.emitter.off(ClockEvent.slot, this.onClockSlot);
216
250
  }
217
251
 
@@ -232,7 +266,7 @@ export class NetworkProcessor {
232
266
 
233
267
  /**
234
268
  * Search block via `ChainEvent.unknownBlockRoot` event
235
- * Note that slot is not necessarily the same to the block's slot but it can be used for a good prune strategy.
269
+ * Slot is the message slot, which is not necessarily the same as the block's slot, but it can be used for a good prune strategy.
236
270
  * In the rare case, if 2 messages on 2 slots search for the same root (for example beacon_attestation) we may emit the same root twice but BlockInputSync should handle it well.
237
271
  */
238
272
  searchUnknownBlock({slot, root}: SlotRootHex, source: BlockInputSource, peer?: PeerIdStr): void {
@@ -248,9 +282,29 @@ export class NetworkProcessor {
248
282
  this.chain.emitter.emit(ChainEvent.unknownBlockRoot, {rootHex: root, peer, source});
249
283
  }
250
284
 
251
- private onPendingGossipsubMessage(message: PendingGossipsubMessage): void {
285
+ /**
286
+ * Search envelope via `ChainEvent.unknownEnvelopeBlockRoot` event
287
+ * Slot is the message slot, which is not necessarily the same as the envelope's slot, but it can be used for a good prune strategy.
288
+ * In the rare case, if 2 messages on 2 slots search for the same root (for example beacon_attestation) we may emit the same root twice but BlockInputSync should handle it well.
289
+ */
290
+ searchUnknownEnvelope({slot, root}: SlotRootHex, source: BlockInputSource, peer?: PeerIdStr): void {
291
+ if (
292
+ this.chain.seenPayloadEnvelope(root) ||
293
+ this.awaitingMessagesByPayloadBlockRoot.has(root) ||
294
+ this.unknownEnvelopesBySlot.getOrDefault(slot).has(root)
295
+ ) {
296
+ return;
297
+ }
298
+ this.unknownEnvelopesBySlot.getOrDefault(slot).add(root);
299
+ this.chain.emitter.emit(ChainEvent.unknownEnvelopeBlockRoot, {rootHex: root, peer, source});
300
+ }
301
+
302
+ private onPendingGossipsubMessage = (message: PendingGossipsubMessage): void => {
252
303
  const topicType = message.topic.type;
253
304
  const extractBlockSlotRootFn = this.extractBlockSlotRootFns[topicType];
305
+
306
+ // 1st extract round: make sure slot is in range and if block root is not available
307
+ // proactively search for it + queue the message
254
308
  const slotRoot = extractBlockSlotRootFn
255
309
  ? extractBlockSlotRootFn(message.msg.data, message.topic.boundary.fork)
256
310
  : null;
@@ -282,27 +336,198 @@ export class NetworkProcessor {
282
336
 
283
337
  message.msgSlot = slot;
284
338
 
339
+ // this determines whether this message needs to wait for a Block or Envelope
340
+ // a message should only wait for what it voted for, hence we don't want to put it on both queues
341
+ let preprocessResult: PreprocessResult = {action: PreprocessAction.PushToQueue};
285
342
  // no need to check if root is a descendant of the current finalized block, it will be checked once we validate the message if needed
286
343
  if (root && !this.chain.forkChoice.hasBlockHexUnsafe(root)) {
344
+ // starting from GLOAS, unknown root from data_column_sidecar also falls into this case
287
345
  this.searchUnknownBlock({slot, root}, BlockInputSource.network_processor, message.propagationSource.toString());
346
+ // for beacon_attestation and beacon_aggregate_and_proof messages, this is only temporary.
347
+ // if "index = 1" we need to await for the Envelope instead
348
+ preprocessResult = {action: PreprocessAction.AwaitBlock, root};
349
+ }
288
350
 
289
- if (this.unknownBlockGossipsubMessagesCount > MAX_QUEUED_UNKNOWN_BLOCK_GOSSIP_OBJECTS) {
290
- // No need to report the dropped job to gossip. It will be eventually pruned from the mcache
291
- this.metrics?.awaitingBlockGossipMessages.reject.inc({
292
- reason: ReprocessRejectReason.reached_limit,
293
- topic: topicType,
294
- });
295
- return;
351
+ // 2nd extract round for some specific topics
352
+ // we separate the search action from the await action
353
+
354
+ // beacon_block: proactively search for parent block/envelope across all forks, but never queue.
355
+ // BlockInputSync handles cascading recovery if the gossip handler throws.
356
+ if (topicType === GossipType.beacon_block) {
357
+ const parentRoot = getParentRootFromSignedBeaconBlockSerialized(message.msg.data);
358
+ if (parentRoot) {
359
+ if (ForkSeq[fork] >= ForkSeq.gloas) {
360
+ // GLOAS: also check parent envelope, same logic as execution_payload_bid
361
+ const parentBlockHash = getParentBlockHashFromGloasSignedBeaconBlockSerialized(message.msg.data);
362
+ if (parentBlockHash && !this.chain.forkChoice.getBlockHexAndBlockHash(parentRoot, parentBlockHash)) {
363
+ const protoBlock = this.chain.forkChoice.getBlockHexDefaultStatus(parentRoot);
364
+ if (protoBlock === null) {
365
+ this.searchUnknownBlock(
366
+ {slot, root: parentRoot},
367
+ BlockInputSource.network_processor,
368
+ message.propagationSource.toString()
369
+ );
370
+ } else if (
371
+ protoBlock.executionPayloadBlockHash &&
372
+ protoBlock.executionPayloadBlockHash !== parentBlockHash
373
+ ) {
374
+ // only search for the envelope by block root if we're sure there is one. Otherwise UnknownBlockSync will penalize the peer.
375
+ this.searchUnknownEnvelope(
376
+ {slot, root: parentRoot},
377
+ BlockInputSource.network_processor,
378
+ message.propagationSource.toString()
379
+ );
380
+ }
381
+ }
382
+ } else if (!this.chain.forkChoice.hasBlockHexUnsafe(parentRoot)) {
383
+ this.searchUnknownBlock(
384
+ {slot, root: parentRoot},
385
+ BlockInputSource.network_processor,
386
+ message.propagationSource.toString()
387
+ );
388
+ }
296
389
  }
390
+ preprocessResult = {action: PreprocessAction.PushToQueue};
391
+ }
297
392
 
298
- this.metrics?.awaitingBlockGossipMessages.queue.inc({topic: topicType});
299
- const awaitingGossipsubMessages = this.awaitingMessagesByBlockRoot.getOrDefault(root);
300
- awaitingGossipsubMessages.add(message);
301
- return;
393
+ if (ForkSeq[fork] >= ForkSeq.gloas) {
394
+ // specific check for each topic
395
+ // note that it's supposed to NOT queue beacon_block (handled above) and execution_payload because it's not a one-off;
396
+ // for those topics, gossip handlers will throw and BlockInputSync will handle a tree of them instead
397
+ switch (topicType) {
398
+ case GossipType.beacon_attestation:
399
+ case GossipType.beacon_aggregate_and_proof: {
400
+ if (root == null) break;
401
+ const attIndex =
402
+ topicType === GossipType.beacon_attestation
403
+ ? getDataIndexFromSingleAttestationSerialized(fork, message.msg.data)
404
+ : getDataIndexFromSignedAggregateAndProofSerialized(message.msg.data);
405
+ if (attIndex === 1 && !this.chain.forkChoice.hasPayloadHexUnsafe(root)) {
406
+ // attestation votes that the payload is available but it is not yet known
407
+ this.searchUnknownEnvelope(
408
+ {slot, root},
409
+ BlockInputSource.network_processor,
410
+ message.propagationSource.toString()
411
+ );
412
+ preprocessResult = {action: PreprocessAction.AwaitEnvelope, root};
413
+ }
414
+ break;
415
+ }
416
+ case GossipType.payload_attestation_message: {
417
+ if (root == null) break;
418
+ const payloadPresent = getPayloadPresentFromPayloadAttestationMessageSerialized(message.msg.data);
419
+ if (payloadPresent && !this.chain.forkChoice.hasPayloadHexUnsafe(root)) {
420
+ // payload attestation votes that the payload is available but it is not yet known
421
+ this.searchUnknownEnvelope(
422
+ {slot, root},
423
+ BlockInputSource.network_processor,
424
+ message.propagationSource.toString()
425
+ );
426
+ // do not await the envelope, payload attestation processing only requires that the block is known
427
+ // also do not reset preprocessResult, we may already await for the block
428
+ }
429
+ break;
430
+ }
431
+ case GossipType.data_column_sidecar: {
432
+ if (root == null) break;
433
+ if (!this.chain.forkChoice.hasPayloadHexUnsafe(root)) {
434
+ this.searchUnknownEnvelope(
435
+ {slot, root},
436
+ BlockInputSource.network_processor,
437
+ message.propagationSource.toString()
438
+ );
439
+ // do not await the envelope, we can do gossip validation
440
+ // also do not reset preprocessResult, we may already await for the block
441
+ }
442
+ break;
443
+ }
444
+ case GossipType.execution_payload: {
445
+ // extractBlockSlotRootFn does not return a root for this topic.
446
+ // Extract beacon_block_root directly and proactively trigger block sync if missing.
447
+ // Do NOT await the block — the handler runs immediately; BlockInputSync handles recovery.
448
+ const blockRoot = getBeaconBlockRootFromExecutionPayloadEnvelopeSerialized(message.msg.data);
449
+ if (blockRoot && !this.chain.forkChoice.hasBlockHexUnsafe(blockRoot)) {
450
+ this.searchUnknownBlock(
451
+ {slot, root: blockRoot},
452
+ BlockInputSource.network_processor,
453
+ message.propagationSource.toString()
454
+ );
455
+ }
456
+ // do not await the block, we want UnknownBlockSync to handle it.
457
+ preprocessResult = {action: PreprocessAction.PushToQueue};
458
+ break;
459
+ }
460
+ case GossipType.execution_payload_bid: {
461
+ // instead of searching for the message root, this searches for the parent root
462
+ const parentBlockRoot = getParentBlockRootFromSignedExecutionPayloadBidSerialized(message.msg.data);
463
+ const parentBlockHash = getParentBlockHashFromSignedExecutionPayloadBidSerialized(message.msg.data);
464
+ if (
465
+ parentBlockRoot &&
466
+ parentBlockHash &&
467
+ !this.chain.forkChoice.getBlockHexAndBlockHash(parentBlockRoot, parentBlockHash)
468
+ ) {
469
+ const protoBlock = this.chain.forkChoice.getBlockHexDefaultStatus(parentBlockRoot);
470
+ if (protoBlock === null) {
471
+ this.searchUnknownBlock(
472
+ {slot, root: parentBlockRoot},
473
+ BlockInputSource.network_processor,
474
+ message.propagationSource.toString()
475
+ );
476
+ preprocessResult = {action: PreprocessAction.AwaitBlock, root: parentBlockRoot};
477
+ } else if (
478
+ protoBlock.executionPayloadBlockHash &&
479
+ protoBlock.executionPayloadBlockHash !== parentBlockHash
480
+ ) {
481
+ this.searchUnknownEnvelope(
482
+ {slot, root: parentBlockRoot},
483
+ BlockInputSource.network_processor,
484
+ message.propagationSource.toString()
485
+ );
486
+ preprocessResult = {action: PreprocessAction.AwaitEnvelope, root: parentBlockRoot};
487
+ }
488
+ }
489
+ break;
490
+ }
491
+ }
302
492
  }
303
493
 
304
- this.pushPendingGossipsubMessageToQueue(message);
305
- }
494
+ switch (preprocessResult.action) {
495
+ case PreprocessAction.PushToQueue:
496
+ this.pushPendingGossipsubMessageToQueue(message);
497
+ break;
498
+ case PreprocessAction.AwaitBlock: {
499
+ if (this.unknownBlockGossipsubMessagesCount > MAX_QUEUED_UNKNOWN_BLOCK_GOSSIP_OBJECTS) {
500
+ // No need to report the dropped job to gossip. It will be eventually pruned from the mcache
501
+ this.metrics?.awaitingBlockGossipMessages.reject.inc({
502
+ reason: ReprocessRejectReason.reached_limit,
503
+ topic: topicType,
504
+ });
505
+ return;
506
+ }
507
+
508
+ this.metrics?.awaitingBlockGossipMessages.queue.inc({topic: topicType});
509
+ const awaitingGossipsubMessages = this.awaitingMessagesByBlockRoot.getOrDefault(preprocessResult.root);
510
+ awaitingGossipsubMessages.add(message);
511
+ break;
512
+ }
513
+ case PreprocessAction.AwaitEnvelope: {
514
+ if (this.unknownPayloadGossipsubMessagesCount > MAX_QUEUED_UNKNOWN_PAYLOAD_GOSSIP_OBJECTS) {
515
+ this.metrics?.awaitingPayloadGossipMessages.reject.inc({
516
+ reason: ReprocessRejectReason.reached_limit,
517
+ topic: topicType,
518
+ });
519
+ return;
520
+ }
521
+
522
+ this.metrics?.awaitingPayloadGossipMessages.queue.inc({topic: topicType});
523
+ const awaitingPayloadGossipsubMessages = this.awaitingMessagesByPayloadBlockRoot.getOrDefault(
524
+ preprocessResult.root
525
+ );
526
+ awaitingPayloadGossipsubMessages.add(message);
527
+ break;
528
+ }
529
+ }
530
+ };
306
531
 
307
532
  private pushPendingGossipsubMessageToQueue(message: PendingGossipsubMessage): void {
308
533
  const topicType = message.topic.type;
@@ -316,7 +541,7 @@ export class NetworkProcessor {
316
541
  this.executeWork();
317
542
  }
318
543
 
319
- private async onBlockProcessed({block: rootHex}: {block: string; executionOptimistic: boolean}): Promise<void> {
544
+ private onBlockProcessed = async ({block: rootHex}: {block: string; executionOptimistic: boolean}): Promise<void> => {
320
545
  const waitingGossipsubMessages = this.awaitingMessagesByBlockRoot.get(rootHex);
321
546
  if (!waitingGossipsubMessages || waitingGossipsubMessages.size === 0) {
322
547
  return;
@@ -343,9 +568,35 @@ export class NetworkProcessor {
343
568
  }
344
569
 
345
570
  this.awaitingMessagesByBlockRoot.delete(rootHex);
346
- }
571
+ };
572
+
573
+ private onPayloadEnvelopeProcessed = async ({blockRoot: rootHex}: {blockRoot: RootHex}): Promise<void> => {
574
+ const waitingGossipsubMessages = this.awaitingMessagesByPayloadBlockRoot.get(rootHex);
575
+ if (!waitingGossipsubMessages || waitingGossipsubMessages.size === 0) {
576
+ return;
577
+ }
578
+
579
+ const nowSec = Date.now() / 1000;
580
+ let count = 0;
581
+ for (const message of waitingGossipsubMessages) {
582
+ const topicType = message.topic.type;
583
+ this.metrics?.awaitingPayloadGossipMessages.waitSecBeforeResolve.set(
584
+ {topic: topicType},
585
+ nowSec - message.seenTimestampSec
586
+ );
587
+ this.metrics?.awaitingPayloadGossipMessages.resolve.inc({topic: topicType});
588
+ this.pushPendingGossipsubMessageToQueue(message);
589
+ count++;
590
+ if (count === MAX_AWAITING_GOSSIP_OBJECTS_PER_TICK) {
591
+ count = 0;
592
+ await sleep(AWAITING_GOSSIP_OBJECTS_YIELD_EVERY_MS);
593
+ }
594
+ }
347
595
 
348
- private onClockSlot(clockSlot: Slot): void {
596
+ this.awaitingMessagesByPayloadBlockRoot.delete(rootHex);
597
+ };
598
+
599
+ private onClockSlot = (clockSlot: Slot): void => {
349
600
  const nowSec = Date.now() / 1000;
350
601
  const minSlot = clockSlot - MAX_UNKNOWN_ROOTS_SLOT_CACHE_SIZE;
351
602
 
@@ -371,7 +622,30 @@ export class NetworkProcessor {
371
622
  }
372
623
  this.unknownBlocksBySlot.delete(slot);
373
624
  }
374
- }
625
+
626
+ for (const [slot, roots] of this.unknownEnvelopesBySlot) {
627
+ if (slot > minSlot) continue;
628
+ for (const rootHex of roots) {
629
+ const gossipMessages = this.awaitingMessagesByPayloadBlockRoot.get(rootHex);
630
+ if (gossipMessages !== undefined) {
631
+ for (const message of gossipMessages) {
632
+ const topicType = message.topic.type;
633
+ this.metrics?.awaitingPayloadGossipMessages.reject.inc({
634
+ topic: topicType,
635
+ reason: ReprocessRejectReason.expired,
636
+ });
637
+ this.metrics?.awaitingPayloadGossipMessages.waitSecBeforeReject.set(
638
+ {topic: topicType, reason: ReprocessRejectReason.expired},
639
+ nowSec - message.seenTimestampSec
640
+ );
641
+ // No need to report the dropped job to gossip. It will be eventually pruned from the mcache
642
+ }
643
+ this.awaitingMessagesByPayloadBlockRoot.delete(rootHex);
644
+ }
645
+ }
646
+ this.unknownEnvelopesBySlot.delete(slot);
647
+ }
648
+ };
375
649
 
376
650
  private executeWork(): void {
377
651
  // TODO: Maybe de-bounce by timing the last time executeWork was run
@@ -517,4 +791,12 @@ export class NetworkProcessor {
517
791
  }
518
792
  return count;
519
793
  }
794
+
795
+ private get unknownPayloadGossipsubMessagesCount(): number {
796
+ let count = 0;
797
+ for (const messages of this.awaitingMessagesByPayloadBlockRoot.values()) {
798
+ count += messages.size;
799
+ }
800
+ return count;
801
+ }
520
802
  }
@@ -96,11 +96,13 @@ export function validateBeaconBlocksByRangeRequest(
96
96
  if (count < 1) {
97
97
  throw new ResponseError(RespStatus.INVALID_REQUEST, "count < 1");
98
98
  }
99
- // TODO: validate against MIN_EPOCHS_FOR_BLOCK_REQUESTS
100
99
  if (startSlot < GENESIS_SLOT) {
101
100
  throw new ResponseError(RespStatus.INVALID_REQUEST, "startSlot < genesis");
102
101
  }
103
102
 
103
+ // The phase0 req/resp spec uses MIN_EPOCHS_FOR_BLOCK_REQUESTS to define the minimum range peers MUST serve.
104
+ // Archival nodes may still serve older retained blocks to allow genesis sync.
105
+
104
106
  // step > 1 is deprecated, see https://github.com/ethereum/consensus-specs/pull/2856
105
107
 
106
108
  const maxRequestBlocks = isForkPostDeneb(config.getForkName(startSlot))
@@ -8,6 +8,9 @@ export async function* onBeaconBlocksByRoot(
8
8
  requestBody: BeaconBlocksByRootRequest,
9
9
  chain: IBeaconChain
10
10
  ): AsyncIterable<ResponseOutgoing> {
11
+ // The phase0 req/resp spec uses MIN_EPOCHS_FOR_BLOCK_REQUESTS to define the minimum range peers MUST serve.
12
+ // Archival nodes may still serve older retained blocks to allow genesis sync.
13
+
11
14
  for (const blockRoot of requestBody) {
12
15
  const root = blockRoot;
13
16
  const block = await chain.getSerializedBlockByRoot(toRootHex(root));
@@ -2,7 +2,7 @@ import {ChainConfig} from "@lodestar/config";
2
2
  import {BLOB_SIDECAR_FIXED_SIZE, GENESIS_SLOT} from "@lodestar/params";
3
3
  import {RespStatus, ResponseError, ResponseOutgoing} from "@lodestar/reqresp";
4
4
  import {computeEpochAtSlot} from "@lodestar/state-transition";
5
- import {Slot, deneb} from "@lodestar/types";
5
+ import {Epoch, Slot, deneb} from "@lodestar/types";
6
6
  import {fromHex} from "@lodestar/utils";
7
7
  import {IBeaconChain} from "../../../chain/index.js";
8
8
  import {IBeaconDb} from "../../../db/index.js";
@@ -14,7 +14,7 @@ export async function* onBlobSidecarsByRange(
14
14
  db: IBeaconDb
15
15
  ): AsyncIterable<ResponseOutgoing> {
16
16
  // Non-finalized range of blobs
17
- const {startSlot, count} = validateBlobSidecarsByRangeRequest(chain.config, request);
17
+ const {startSlot, count} = validateBlobSidecarsByRangeRequest(chain.config, chain.clock.currentEpoch, request);
18
18
  const endSlot = startSlot + count;
19
19
 
20
20
  const finalized = db.blobSidecarsArchive;
@@ -94,6 +94,7 @@ export function* iterateBlobBytesFromWrapper(
94
94
 
95
95
  export function validateBlobSidecarsByRangeRequest(
96
96
  config: ChainConfig,
97
+ currentEpoch: Epoch,
97
98
  request: deneb.BlobSidecarsByRangeRequest
98
99
  ): deneb.BlobSidecarsByRangeRequest {
99
100
  const {startSlot} = request;
@@ -102,11 +103,22 @@ export function validateBlobSidecarsByRangeRequest(
102
103
  if (count < 1) {
103
104
  throw new ResponseError(RespStatus.INVALID_REQUEST, "count < 1");
104
105
  }
105
- // TODO: validate against MIN_EPOCHS_FOR_BLOCK_REQUESTS
106
106
  if (startSlot < GENESIS_SLOT) {
107
107
  throw new ResponseError(RespStatus.INVALID_REQUEST, "startSlot < genesis");
108
108
  }
109
109
 
110
+ // Spec: [max(current_epoch - MIN_EPOCHS_FOR_BLOB_SIDECARS_REQUESTS, DENEB_FORK_EPOCH), current_epoch]
111
+ const minimumRequestEpoch = Math.max(
112
+ currentEpoch - config.MIN_EPOCHS_FOR_BLOB_SIDECARS_REQUESTS,
113
+ config.DENEB_FORK_EPOCH
114
+ );
115
+ if (computeEpochAtSlot(startSlot) < minimumRequestEpoch) {
116
+ throw new ResponseError(
117
+ RespStatus.RESOURCE_UNAVAILABLE,
118
+ "startSlot is before MIN_EPOCHS_FOR_BLOB_SIDECARS_REQUESTS"
119
+ );
120
+ }
121
+
110
122
  if (count > config.MAX_REQUEST_BLOCKS_DENEB) {
111
123
  count = config.MAX_REQUEST_BLOCKS_DENEB;
112
124
  }
@@ -12,6 +12,13 @@ export async function* onBlobSidecarsByRoot(
12
12
  ): AsyncIterable<ResponseOutgoing> {
13
13
  const finalizedSlot = chain.forkChoice.getFinalizedBlock().slot;
14
14
 
15
+ // Spec: [max(current_epoch - MIN_EPOCHS_FOR_BLOB_SIDECARS_REQUESTS, DENEB_FORK_EPOCH), current_epoch]
16
+ const currentEpoch = chain.clock.currentEpoch;
17
+ const minimumRequestEpoch = Math.max(
18
+ currentEpoch - chain.config.MIN_EPOCHS_FOR_BLOB_SIDECARS_REQUESTS,
19
+ chain.config.DENEB_FORK_EPOCH
20
+ );
21
+
15
22
  // In sidecars by root request, it can be expected that sidecar requests will be come
16
23
  // clustured by blockroots, and this helps us save db lookups once we load sidecars
17
24
  // for a root
@@ -29,6 +36,10 @@ export async function* onBlobSidecarsByRoot(
29
36
  continue;
30
37
  }
31
38
 
39
+ if (computeEpochAtSlot(block.slot) < minimumRequestEpoch) {
40
+ continue;
41
+ }
42
+
32
43
  // Check if we need to load sidecars for a new block root
33
44
  if (lastFetchedSideCars === null || lastFetchedSideCars.blockRoot !== blockRootHex) {
34
45
  const blobSidecarsBytes = await chain.getSerializedBlobSidecars(block.slot, blockRootHex);
@@ -3,7 +3,7 @@ import {ChainConfig} from "@lodestar/config";
3
3
  import {GENESIS_SLOT} from "@lodestar/params";
4
4
  import {RespStatus, ResponseError, ResponseOutgoing} from "@lodestar/reqresp";
5
5
  import {computeEpochAtSlot} from "@lodestar/state-transition";
6
- import {ColumnIndex, fulu} from "@lodestar/types";
6
+ import {ColumnIndex, Epoch, fulu} from "@lodestar/types";
7
7
  import {fromHex} from "@lodestar/utils";
8
8
  import {IBeaconChain} from "../../../chain/index.js";
9
9
  import {IBeaconDb} from "../../../db/index.js";
@@ -21,7 +21,11 @@ export async function* onDataColumnSidecarsByRange(
21
21
  peerClient: string
22
22
  ): AsyncIterable<ResponseOutgoing> {
23
23
  // Non-finalized range of columns
24
- const {startSlot, count, columns: requestedColumns} = validateDataColumnSidecarsByRangeRequest(chain.config, request);
24
+ const {
25
+ startSlot,
26
+ count,
27
+ columns: requestedColumns,
28
+ } = validateDataColumnSidecarsByRangeRequest(chain.config, chain.clock.currentEpoch, request);
25
29
  const availableColumns = validateRequestedDataColumns(chain, requestedColumns);
26
30
  const endSlot = startSlot + count;
27
31
 
@@ -139,6 +143,7 @@ export async function* onDataColumnSidecarsByRange(
139
143
 
140
144
  export function validateDataColumnSidecarsByRangeRequest(
141
145
  config: ChainConfig,
146
+ currentEpoch: Epoch,
142
147
  request: fulu.DataColumnSidecarsByRangeRequest
143
148
  ): fulu.DataColumnSidecarsByRangeRequest {
144
149
  const {startSlot, columns} = request;
@@ -147,11 +152,22 @@ export function validateDataColumnSidecarsByRangeRequest(
147
152
  if (count < 1) {
148
153
  throw new ResponseError(RespStatus.INVALID_REQUEST, "count < 1");
149
154
  }
150
- // TODO: validate against MIN_EPOCHS_FOR_BLOCK_REQUESTS
151
155
  if (startSlot < GENESIS_SLOT) {
152
156
  throw new ResponseError(RespStatus.INVALID_REQUEST, "startSlot < genesis");
153
157
  }
154
158
 
159
+ // Spec: [max(current_epoch - MIN_EPOCHS_FOR_DATA_COLUMN_SIDECARS_REQUESTS, FULU_FORK_EPOCH), current_epoch]
160
+ const minimumRequestEpoch = Math.max(
161
+ currentEpoch - config.MIN_EPOCHS_FOR_DATA_COLUMN_SIDECARS_REQUESTS,
162
+ config.FULU_FORK_EPOCH
163
+ );
164
+ if (computeEpochAtSlot(startSlot) < minimumRequestEpoch) {
165
+ throw new ResponseError(
166
+ RespStatus.RESOURCE_UNAVAILABLE,
167
+ "startSlot is before MIN_EPOCHS_FOR_DATA_COLUMN_SIDECARS_REQUESTS"
168
+ );
169
+ }
170
+
155
171
  if (count > config.MAX_REQUEST_BLOCKS_DENEB) {
156
172
  count = config.MAX_REQUEST_BLOCKS_DENEB;
157
173
  }
@@ -81,11 +81,13 @@ export function validateExecutionPayloadEnvelopesByRangeRequest(
81
81
  if (count < 1) {
82
82
  throw new ResponseError(RespStatus.INVALID_REQUEST, "count < 1");
83
83
  }
84
- // TODO: validate against MIN_EPOCHS_FOR_BLOCK_REQUESTS
85
84
  if (startSlot < GENESIS_SLOT) {
86
85
  throw new ResponseError(RespStatus.INVALID_REQUEST, "startSlot < genesis");
87
86
  }
88
87
 
88
+ // The gloas req/resp spec uses MIN_EPOCHS_FOR_BLOCK_REQUESTS to define the minimum range peers MUST serve.
89
+ // Archival nodes may still serve older retained payloads to allow genesis sync.
90
+
89
91
  if (count > config.MAX_REQUEST_BLOCKS_DENEB) {
90
92
  count = config.MAX_REQUEST_BLOCKS_DENEB;
91
93
  }
@@ -10,12 +10,8 @@ export async function* onExecutionPayloadEnvelopesByRoot(
10
10
  chain: IBeaconChain,
11
11
  db: IBeaconDb
12
12
  ): AsyncIterable<ResponseOutgoing> {
13
- // Spec: [max(GLOAS_FORK_EPOCH, current_epoch - MIN_EPOCHS_FOR_BLOCK_REQUESTS), current_epoch]
14
- const currentEpoch = chain.clock.currentEpoch;
15
- const minimumRequestEpoch = Math.max(
16
- currentEpoch - chain.config.MIN_EPOCHS_FOR_BLOCK_REQUESTS,
17
- chain.config.GLOAS_FORK_EPOCH
18
- );
13
+ // The gloas req/resp spec uses MIN_EPOCHS_FOR_BLOCK_REQUESTS to define the minimum range peers MUST serve.
14
+ // Archival nodes may still serve older retained payloads to allow genesis sync.
19
15
 
20
16
  for (const root of requestBody) {
21
17
  const rootHex = toRootHex(root);
@@ -27,16 +23,11 @@ export async function* onExecutionPayloadEnvelopesByRoot(
27
23
  continue;
28
24
  }
29
25
 
30
- const requestedEpoch = computeEpochAtSlot(slot);
31
- if (requestedEpoch < minimumRequestEpoch) {
32
- continue;
33
- }
34
-
35
26
  const envelopeBytes = await chain.getSerializedExecutionPayloadEnvelope(slot, rootHex);
36
27
  if (envelopeBytes) {
37
28
  yield {
38
29
  data: envelopeBytes,
39
- boundary: chain.config.getForkBoundaryAtEpoch(requestedEpoch),
30
+ boundary: chain.config.getForkBoundaryAtEpoch(computeEpochAtSlot(slot)),
40
31
  };
41
32
  }
42
33
  }