@lodestar/beacon-node 1.48.0-dev.e9c9b8835d → 1.48.0-dev.fd70e5fc15

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 (200) hide show
  1. package/lib/api/impl/beacon/blocks/index.d.ts.map +1 -1
  2. package/lib/api/impl/beacon/blocks/index.js +4 -4
  3. package/lib/api/impl/beacon/blocks/index.js.map +1 -1
  4. package/lib/chain/archiveStore/archiveStore.d.ts +0 -1
  5. package/lib/chain/archiveStore/archiveStore.d.ts.map +1 -1
  6. package/lib/chain/archiveStore/archiveStore.js +0 -1
  7. package/lib/chain/archiveStore/archiveStore.js.map +1 -1
  8. package/lib/chain/archiveStore/historicalState/historicalStateRegen.d.ts.map +1 -1
  9. package/lib/chain/archiveStore/historicalState/historicalStateRegen.js +0 -1
  10. package/lib/chain/archiveStore/historicalState/historicalStateRegen.js.map +1 -1
  11. package/lib/chain/archiveStore/historicalState/metrics.d.ts.map +1 -1
  12. package/lib/chain/archiveStore/historicalState/metrics.js +1 -12
  13. package/lib/chain/archiveStore/historicalState/metrics.js.map +1 -1
  14. package/lib/chain/archiveStore/historicalState/types.d.ts +0 -2
  15. package/lib/chain/archiveStore/historicalState/types.d.ts.map +1 -1
  16. package/lib/chain/archiveStore/historicalState/types.js.map +1 -1
  17. package/lib/chain/archiveStore/historicalState/worker.js +1 -4
  18. package/lib/chain/archiveStore/historicalState/worker.js.map +1 -1
  19. package/lib/chain/archiveStore/utils/archiveBlocks.d.ts.map +1 -1
  20. package/lib/chain/archiveStore/utils/archiveBlocks.js +34 -31
  21. package/lib/chain/archiveStore/utils/archiveBlocks.js.map +1 -1
  22. package/lib/chain/blocks/index.d.ts.map +1 -1
  23. package/lib/chain/blocks/index.js +2 -3
  24. package/lib/chain/blocks/index.js.map +1 -1
  25. package/lib/chain/blocks/writeBlockInputToDb.d.ts.map +1 -1
  26. package/lib/chain/blocks/writeBlockInputToDb.js +16 -9
  27. package/lib/chain/blocks/writeBlockInputToDb.js.map +1 -1
  28. package/lib/chain/chain.d.ts +3 -4
  29. package/lib/chain/chain.d.ts.map +1 -1
  30. package/lib/chain/chain.js +35 -60
  31. package/lib/chain/chain.js.map +1 -1
  32. package/lib/chain/errors/blockError.d.ts +2 -16
  33. package/lib/chain/errors/blockError.d.ts.map +1 -1
  34. package/lib/chain/errors/blockError.js.map +1 -1
  35. package/lib/chain/errors/dataColumnSidecarError.d.ts +2 -15
  36. package/lib/chain/errors/dataColumnSidecarError.d.ts.map +1 -1
  37. package/lib/chain/errors/dataColumnSidecarError.js.map +1 -1
  38. package/lib/chain/errors/executionPayloadEnvelope.d.ts +0 -10
  39. package/lib/chain/errors/executionPayloadEnvelope.d.ts.map +1 -1
  40. package/lib/chain/errors/executionPayloadEnvelope.js.map +1 -1
  41. package/lib/chain/interface.d.ts +2 -2
  42. package/lib/chain/interface.d.ts.map +1 -1
  43. package/lib/chain/prepareNextSlot.js +13 -13
  44. package/lib/chain/prepareNextSlot.js.map +1 -1
  45. package/lib/chain/validation/block.d.ts.map +1 -1
  46. package/lib/chain/validation/block.js +4 -26
  47. package/lib/chain/validation/block.js.map +1 -1
  48. package/lib/chain/validation/dataColumnSidecar.d.ts.map +1 -1
  49. package/lib/chain/validation/dataColumnSidecar.js +13 -38
  50. package/lib/chain/validation/dataColumnSidecar.js.map +1 -1
  51. package/lib/chain/validation/executionPayloadEnvelope.js +0 -10
  52. package/lib/chain/validation/executionPayloadEnvelope.js.map +1 -1
  53. package/lib/db/beacon.d.ts +5 -14
  54. package/lib/db/beacon.d.ts.map +1 -1
  55. package/lib/db/beacon.js +3 -17
  56. package/lib/db/beacon.js.map +1 -1
  57. package/lib/db/index.d.ts +0 -1
  58. package/lib/db/index.d.ts.map +1 -1
  59. package/lib/db/interface.d.ts +1 -8
  60. package/lib/db/interface.d.ts.map +1 -1
  61. package/lib/db/repositories/blockArchive.d.ts +1 -8
  62. package/lib/db/repositories/blockArchive.d.ts.map +1 -1
  63. package/lib/db/repositories/blockArchive.js +28 -65
  64. package/lib/db/repositories/blockArchive.js.map +1 -1
  65. package/lib/db/repositories/blockArchiveIndex.d.ts +2 -1
  66. package/lib/db/repositories/blockArchiveIndex.d.ts.map +1 -1
  67. package/lib/db/repositories/blockArchiveIndex.js +7 -3
  68. package/lib/db/repositories/blockArchiveIndex.js.map +1 -1
  69. package/lib/metrics/metrics/lodestar.d.ts +0 -18
  70. package/lib/metrics/metrics/lodestar.d.ts.map +1 -1
  71. package/lib/metrics/metrics/lodestar.js +1 -55
  72. package/lib/metrics/metrics/lodestar.js.map +1 -1
  73. package/lib/monitoring/clientStats.js +1 -1
  74. package/lib/monitoring/clientStats.js.map +1 -1
  75. package/lib/network/gossip/encoding.d.ts.map +1 -1
  76. package/lib/network/gossip/encoding.js +6 -0
  77. package/lib/network/gossip/encoding.js.map +1 -1
  78. package/lib/network/gossip/topic.d.ts +1 -2
  79. package/lib/network/gossip/topic.d.ts.map +1 -1
  80. package/lib/network/gossip/topic.js +20 -4
  81. package/lib/network/gossip/topic.js.map +1 -1
  82. package/lib/network/processor/gossipHandlers.d.ts.map +1 -1
  83. package/lib/network/processor/gossipHandlers.js +7 -6
  84. package/lib/network/processor/gossipHandlers.js.map +1 -1
  85. package/lib/network/processor/index.d.ts +3 -49
  86. package/lib/network/processor/index.d.ts.map +1 -1
  87. package/lib/network/processor/index.js +98 -232
  88. package/lib/network/processor/index.js.map +1 -1
  89. package/lib/network/reqresp/handlers/dataColumnSidecarsByRange.d.ts.map +1 -1
  90. package/lib/network/reqresp/handlers/dataColumnSidecarsByRange.js +91 -112
  91. package/lib/network/reqresp/handlers/dataColumnSidecarsByRange.js.map +1 -1
  92. package/lib/network/reqresp/handlers/dataColumnSidecarsByRoot.d.ts.map +1 -1
  93. package/lib/network/reqresp/handlers/dataColumnSidecarsByRoot.js +0 -2
  94. package/lib/network/reqresp/handlers/dataColumnSidecarsByRoot.js.map +1 -1
  95. package/lib/network/reqresp/protocols.d.ts.map +1 -1
  96. package/lib/network/reqresp/protocols.js +22 -6
  97. package/lib/network/reqresp/protocols.js.map +1 -1
  98. package/lib/network/reqresp/utils/dataColumnResponseValidation.d.ts +1 -2
  99. package/lib/network/reqresp/utils/dataColumnResponseValidation.d.ts.map +1 -1
  100. package/lib/network/reqresp/utils/dataColumnResponseValidation.js +9 -14
  101. package/lib/network/reqresp/utils/dataColumnResponseValidation.js.map +1 -1
  102. package/lib/node/nodejs.d.ts +1 -2
  103. package/lib/node/nodejs.d.ts.map +1 -1
  104. package/lib/node/nodejs.js +2 -4
  105. package/lib/node/nodejs.js.map +1 -1
  106. package/lib/util/sszBytes.d.ts +0 -6
  107. package/lib/util/sszBytes.d.ts.map +1 -1
  108. package/lib/util/sszBytes.js +1 -1
  109. package/lib/util/sszBytes.js.map +1 -1
  110. package/package.json +15 -15
  111. package/src/api/impl/beacon/blocks/index.ts +4 -8
  112. package/src/chain/archiveStore/archiveStore.ts +1 -6
  113. package/src/chain/archiveStore/historicalState/historicalStateRegen.ts +0 -1
  114. package/src/chain/archiveStore/historicalState/metrics.ts +1 -14
  115. package/src/chain/archiveStore/historicalState/types.ts +0 -2
  116. package/src/chain/archiveStore/historicalState/worker.ts +1 -4
  117. package/src/chain/archiveStore/utils/archiveBlocks.ts +39 -34
  118. package/src/chain/blocks/index.ts +2 -13
  119. package/src/chain/blocks/writeBlockInputToDb.ts +17 -10
  120. package/src/chain/chain.ts +34 -75
  121. package/src/chain/errors/blockError.ts +8 -20
  122. package/src/chain/errors/dataColumnSidecarError.ts +8 -24
  123. package/src/chain/errors/executionPayloadEnvelope.ts +2 -14
  124. package/src/chain/interface.ts +2 -2
  125. package/src/chain/prepareNextSlot.ts +16 -16
  126. package/src/chain/validation/block.ts +4 -26
  127. package/src/chain/validation/dataColumnSidecar.ts +14 -47
  128. package/src/chain/validation/executionPayloadEnvelope.ts +0 -10
  129. package/src/db/beacon.ts +6 -33
  130. package/src/db/index.ts +0 -1
  131. package/src/db/interface.ts +1 -9
  132. package/src/db/repositories/blockArchive.ts +30 -89
  133. package/src/db/repositories/blockArchiveIndex.ts +9 -4
  134. package/src/metrics/metrics/lodestar.ts +1 -57
  135. package/src/monitoring/clientStats.ts +1 -1
  136. package/src/network/gossip/encoding.ts +6 -0
  137. package/src/network/gossip/topic.ts +25 -3
  138. package/src/network/processor/gossipHandlers.ts +11 -11
  139. package/src/network/processor/index.ts +148 -263
  140. package/src/network/reqresp/handlers/dataColumnSidecarsByRange.ts +105 -139
  141. package/src/network/reqresp/handlers/dataColumnSidecarsByRoot.ts +0 -2
  142. package/src/network/reqresp/protocols.ts +24 -7
  143. package/src/network/reqresp/utils/dataColumnResponseValidation.ts +7 -15
  144. package/src/node/nodejs.ts +1 -6
  145. package/src/util/sszBytes.ts +1 -1
  146. package/lib/db/dataColumnStore.d.ts +0 -39
  147. package/lib/db/dataColumnStore.d.ts.map +0 -1
  148. package/lib/db/dataColumnStore.js +0 -81
  149. package/lib/db/dataColumnStore.js.map +0 -1
  150. package/lib/db/flatFileStore/atomicWrite.d.ts +0 -8
  151. package/lib/db/flatFileStore/atomicWrite.d.ts.map +0 -1
  152. package/lib/db/flatFileStore/atomicWrite.js +0 -61
  153. package/lib/db/flatFileStore/atomicWrite.js.map +0 -1
  154. package/lib/db/flatFileStore/dcolFormat.d.ts +0 -90
  155. package/lib/db/flatFileStore/dcolFormat.d.ts.map +0 -1
  156. package/lib/db/flatFileStore/dcolFormat.js +0 -286
  157. package/lib/db/flatFileStore/dcolFormat.js.map +0 -1
  158. package/lib/db/flatFileStore/errors.d.ts +0 -82
  159. package/lib/db/flatFileStore/errors.d.ts.map +0 -1
  160. package/lib/db/flatFileStore/errors.js +0 -32
  161. package/lib/db/flatFileStore/errors.js.map +0 -1
  162. package/lib/db/flatFileStore/flatFileStore.d.ts +0 -41
  163. package/lib/db/flatFileStore/flatFileStore.d.ts.map +0 -1
  164. package/lib/db/flatFileStore/flatFileStore.js +0 -251
  165. package/lib/db/flatFileStore/flatFileStore.js.map +0 -1
  166. package/lib/db/flatFileStore/index.d.ts +0 -4
  167. package/lib/db/flatFileStore/index.d.ts.map +0 -1
  168. package/lib/db/flatFileStore/index.js +0 -2
  169. package/lib/db/flatFileStore/index.js.map +0 -1
  170. package/lib/db/flatFileStore/interface.d.ts +0 -25
  171. package/lib/db/flatFileStore/interface.d.ts.map +0 -1
  172. package/lib/db/flatFileStore/interface.js +0 -2
  173. package/lib/db/flatFileStore/interface.js.map +0 -1
  174. package/lib/db/flatFileStore/metrics.d.ts +0 -24
  175. package/lib/db/flatFileStore/metrics.d.ts.map +0 -1
  176. package/lib/db/flatFileStore/metrics.js +0 -25
  177. package/lib/db/flatFileStore/metrics.js.map +0 -1
  178. package/lib/db/flatFileStore/path.d.ts +0 -9
  179. package/lib/db/flatFileStore/path.d.ts.map +0 -1
  180. package/lib/db/flatFileStore/path.js +0 -21
  181. package/lib/db/flatFileStore/path.js.map +0 -1
  182. package/lib/db/flatFileStore/slotIndex.d.ts +0 -14
  183. package/lib/db/flatFileStore/slotIndex.d.ts.map +0 -1
  184. package/lib/db/flatFileStore/slotIndex.js +0 -46
  185. package/lib/db/flatFileStore/slotIndex.js.map +0 -1
  186. package/lib/db/flatFileStore/snappy.d.ts +0 -3
  187. package/lib/db/flatFileStore/snappy.d.ts.map +0 -1
  188. package/lib/db/flatFileStore/snappy.js +0 -12
  189. package/lib/db/flatFileStore/snappy.js.map +0 -1
  190. package/src/db/dataColumnStore.ts +0 -113
  191. package/src/db/flatFileStore/atomicWrite.ts +0 -65
  192. package/src/db/flatFileStore/dcolFormat.ts +0 -389
  193. package/src/db/flatFileStore/errors.ts +0 -53
  194. package/src/db/flatFileStore/flatFileStore.ts +0 -310
  195. package/src/db/flatFileStore/index.ts +0 -3
  196. package/src/db/flatFileStore/interface.ts +0 -21
  197. package/src/db/flatFileStore/metrics.ts +0 -48
  198. package/src/db/flatFileStore/path.ts +0 -32
  199. package/src/db/flatFileStore/slotIndex.ts +0 -56
  200. package/src/db/flatFileStore/snappy.ts +0 -14
@@ -1,6 +1,6 @@
1
1
  import { TopicValidatorResult } from "@libp2p/gossipsub";
2
2
  import { routes } from "@lodestar/api";
3
- import { ForkSeq, NUMBER_OF_COLUMNS } from "@lodestar/params";
3
+ import { ForkSeq } from "@lodestar/params";
4
4
  import { computeStartSlotAtEpoch } from "@lodestar/state-transition";
5
5
  import { MapDef, mapValues, sleep } from "@lodestar/utils";
6
6
  import { BlockInputSource } from "../../chain/blocks/blockInput/types.js";
@@ -20,28 +20,6 @@ export * from "./types.js";
20
20
  * Keep up to 3 slot of unknown roots, so we don't always emit to UnknownBlock sync.
21
21
  */
22
22
  const MAX_UNKNOWN_ROOTS_SLOT_CACHE_SIZE = 3;
23
- /**
24
- * Max distinct roots we BUFFER messages for, per slot. Kept tight to avoid the OOM
25
- * risk. We don't support super forky condition where there are more than this many roots per slot via gossip.
26
- */
27
- export const MAX_BUFFERED_ROOTS_PER_SLOT = 5;
28
- /**
29
- * Max distinct roots we emit an unknown-root search for, per slot. Higher than
30
- * the buffer budget because:
31
- * - in the attack scenario, the genuine root may comes after the first MAX_BUFFERED_ROOTS_PER_SLOT roots
32
- * - if we cannot search, we'll penalize peers in UnknownBlockInput, not in NetworkProcessor
33
- */
34
- export const MAX_SEARCHED_ROOTS_PER_SLOT = 32;
35
- /**
36
- * Given the same root and topic, we'll ignore messages after the below cap. Each cap is the number of
37
- * legitimate messages we expect for one block, plus headroom for some malformed messages that may arrives first
38
- * they will be penalized by the gossip handler
39
- */
40
- export const MAX_AWAITING_MESSAGES_PER_ROOT = {
41
- [GossipType.data_column_sidecar]: 2 * NUMBER_OF_COLUMNS,
42
- [GossipType.execution_payload]: 2,
43
- [GossipType.execution_payload_bid]: 8,
44
- };
45
23
  /**
46
24
  * This is respective to gossipsub seenTTL (which is 550 * 0.7 = 385s), also it's respective
47
25
  * to beacon_attestation ATTESTATION_PROPAGATION_SLOT_RANGE (32 slots).
@@ -94,14 +72,6 @@ export var ReprocessRejectReason;
94
72
  * There are too many gossip messages that have unknown block root.
95
73
  */
96
74
  ReprocessRejectReason["reached_limit"] = "reached_limit";
97
- /**
98
- * Too many distinct unknown roots are already tracked for the message's slot (MAX_UNKNOWN_ROOTS_PER_SLOT).
99
- */
100
- ReprocessRejectReason["reached_root_limit"] = "reached_root_limit";
101
- /**
102
- * Too many gossip messages are already buffered for this (root, topic) (MAX_AWAITING_MESSAGES_PER_ROOT).
103
- */
104
- ReprocessRejectReason["reached_topic_limit"] = "reached_topic_limit";
105
75
  /**
106
76
  * The awaiting gossip message is pruned per clock slot.
107
77
  */
@@ -162,16 +132,15 @@ export class NetworkProcessor {
162
132
  gossipTopicConcurrency;
163
133
  extractBlockSlotRootFns = createExtractBlockSlotRootFns();
164
134
  // we may not receive the block for messages like Attestation and SignedAggregateAndProof messages, in that case PendingGossipsubMessage needs
165
- // to be stored in this Map and reprocessed once the block comes. Keyed by topic per root so we can cap
166
- // the number of buffered messages per (root, topic) - see MAX_AWAITING_MESSAGES_PER_ROOT.
135
+ // to be stored in this Map and reprocessed once the block comes
167
136
  awaitingMessagesByBlockRoot;
168
137
  awaitingBlockMessageCount = 0;
169
138
  // we may not receive the payload for messages that require the FULL payload variant to be processed,
170
139
  // in that case PendingGossipsubMessage needs to be stored in this Map and reprocessed once the payload comes
171
140
  awaitingMessagesByPayloadBlockRoot;
172
141
  awaitingPayloadMessageCount = 0;
173
- searchedRootsBySlot = new MapDef(() => new MapDef(() => ({})));
174
- bufferedRootsBySlot = new MapDef(() => new Set());
142
+ unknownBlocksBySlot = new MapDef(() => new MapDef(() => new Set()));
143
+ unknownEnvelopesBySlot = new MapDef(() => new MapDef(() => new Set()));
175
144
  constructor(modules, opts) {
176
145
  this.opts = opts;
177
146
  const { chain, events, logger, metrics } = modules;
@@ -188,8 +157,8 @@ export class NetworkProcessor {
188
157
  this.chain.emitter.on(routes.events.EventType.block, this.onBlockProcessed);
189
158
  this.chain.emitter.on(routes.events.EventType.executionPayload, this.onPayloadEnvelopeProcessed);
190
159
  this.chain.clock.on(ClockEvent.slot, this.onClockSlot);
191
- this.awaitingMessagesByBlockRoot = new MapDef(() => new MapDef(() => new Set()));
192
- this.awaitingMessagesByPayloadBlockRoot = new MapDef(() => new MapDef(() => new Set()));
160
+ this.awaitingMessagesByBlockRoot = new MapDef(() => new Set());
161
+ this.awaitingMessagesByPayloadBlockRoot = new MapDef(() => new Set());
193
162
  // TODO: Implement queues and priorization for ReqResp incoming requests
194
163
  // Listens to NetworkEvent.reqRespIncomingRequest event
195
164
  if (metrics) {
@@ -241,12 +210,9 @@ export class NetworkProcessor {
241
210
  if (this.chain.seenBlock(root)) {
242
211
  return;
243
212
  }
244
- const entry = this.searchedRootsBySlot.getOrDefault(slot).getOrDefault(root);
245
- const alreadySearching = entry.blockPeerIds !== undefined;
246
- if (entry.blockPeerIds === undefined) {
247
- entry.blockPeerIds = new Set();
248
- }
249
- const forwardedPeers = entry.blockPeerIds;
213
+ const peersForRoot = this.unknownBlocksBySlot.getOrDefault(slot);
214
+ const alreadySearching = peersForRoot.has(root) || this.awaitingMessagesByBlockRoot.has(root);
215
+ const forwardedPeers = peersForRoot.getOrDefault(root);
250
216
  // brand-new search always emits (peer may be undefined)
251
217
  let shouldEmit = !alreadySearching;
252
218
  if (peer !== undefined && !forwardedPeers.has(peer) && forwardedPeers.size < MAX_PEERS_PER_ROOT) {
@@ -268,12 +234,10 @@ export class NetworkProcessor {
268
234
  if (this.chain.seenPayloadEnvelope(root)) {
269
235
  return;
270
236
  }
271
- const entry = this.searchedRootsBySlot.getOrDefault(slot).getOrDefault(root);
272
- const alreadySearching = entry.envelopePeerIds !== undefined;
273
- if (entry.envelopePeerIds === undefined) {
274
- entry.envelopePeerIds = new Set();
275
- }
276
- const forwardedPeers = entry.envelopePeerIds;
237
+ const peersForRoot = this.unknownEnvelopesBySlot.getOrDefault(slot);
238
+ // capture "already searching" BEFORE getOrDefault(root) creates the entry
239
+ const alreadySearching = peersForRoot.has(root) || this.awaitingMessagesByPayloadBlockRoot.has(root);
240
+ const forwardedPeers = peersForRoot.getOrDefault(root);
277
241
  let shouldEmit = !alreadySearching; // brand-new search always emits (peer may be undefined)
278
242
  if (peer !== undefined && !forwardedPeers.has(peer) && forwardedPeers.size < MAX_PEERS_PER_ROOT) {
279
243
  // First time we forward THIS peer for THIS root: emit so BlockInputSync accumulates it.
@@ -328,14 +292,13 @@ export class NetworkProcessor {
328
292
  return;
329
293
  }
330
294
  message.msgSlot = slot;
331
- const peerId = message.propagationSource.toString();
332
295
  // this determines whether this message needs to wait for a Block or Envelope
333
- // a message should only wait for what it voted for, hence we don't want to put it on both queues.
296
+ // a message should only wait for what it voted for, hence we don't want to put it on both queues
334
297
  let preprocessResult = { action: PreprocessAction.PushToQueue };
335
298
  // 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
336
299
  if (root && !this.chain.forkChoice.hasBlockHexUnsafe(root)) {
337
300
  // starting from GLOAS, unknown root from data_column_sidecar also falls into this case
338
- this.searchUnknownRoot({ slot, root }, true, false, peerId);
301
+ this.searchUnknownBlock({ slot, root }, BlockInputSource.network_processor, message.propagationSource.toString());
339
302
  // for beacon_attestation and beacon_aggregate_and_proof messages, this is only temporary.
340
303
  // if "index = 1" we need to await for the Envelope instead
341
304
  preprocessResult = { action: PreprocessAction.AwaitBlock, root };
@@ -343,32 +306,28 @@ export class NetworkProcessor {
343
306
  // 2nd extract round for some specific topics
344
307
  // we separate the search action from the await action
345
308
  // beacon_block: proactively search for parent block/envelope across all forks, but never queue.
346
- // BlockInputSync handles cascading recovery if the gossip handler throws. The parent root differs from
347
- // the message root and this message never awaits, so its search flushes inline here (gated by the budget).
309
+ // BlockInputSync handles cascading recovery if the gossip handler throws.
348
310
  if (topicType === GossipType.beacon_block) {
349
311
  const parentRoot = getParentRootFromSignedBeaconBlockSerialized(message.msg.data);
350
312
  if (parentRoot) {
351
- let searchParentBlock = false;
352
- let searchParentEnvelope = false;
353
313
  if (ForkSeq[fork] >= ForkSeq.gloas) {
354
314
  // GLOAS: also check parent envelope, same logic as execution_payload_bid
355
315
  const parentBlockHash = getParentBlockHashFromGloasSignedBeaconBlockSerialized(message.msg.data);
356
316
  if (parentBlockHash && !this.chain.forkChoice.getBlockHexAndBlockHash(parentRoot, parentBlockHash)) {
357
317
  const protoBlock = this.chain.forkChoice.getBlockHexDefaultStatus(parentRoot);
358
318
  if (protoBlock === null) {
359
- searchParentBlock = true;
319
+ this.searchUnknownBlock({ slot, root: parentRoot }, BlockInputSource.network_processor, message.propagationSource.toString());
360
320
  }
361
321
  else if (protoBlock.executionPayloadBlockHash &&
362
322
  protoBlock.executionPayloadBlockHash !== parentBlockHash) {
363
323
  // only search for the envelope by block root if we're sure there is one. Otherwise UnknownBlockSync will penalize the peer.
364
- searchParentEnvelope = true;
324
+ this.searchUnknownEnvelope({ slot, root: parentRoot }, BlockInputSource.network_processor, message.propagationSource.toString());
365
325
  }
366
326
  }
367
327
  }
368
328
  else if (!this.chain.forkChoice.hasBlockHexUnsafe(parentRoot)) {
369
- searchParentBlock = true;
329
+ this.searchUnknownBlock({ slot, root: parentRoot }, BlockInputSource.network_processor, message.propagationSource.toString());
370
330
  }
371
- this.searchUnknownRoot({ slot, root: parentRoot }, searchParentBlock, searchParentEnvelope, peerId);
372
331
  }
373
332
  preprocessResult = { action: PreprocessAction.PushToQueue };
374
333
  }
@@ -386,7 +345,7 @@ export class NetworkProcessor {
386
345
  : getDataIndexFromSignedAggregateAndProofSerialized(message.msg.data);
387
346
  if (attIndex === 1 && !this.chain.forkChoice.hasPayloadHexUnsafe(root)) {
388
347
  // attestation votes that the payload is available but it is not yet known
389
- this.searchUnknownRoot({ slot, root }, false, true, peerId);
348
+ this.searchUnknownEnvelope({ slot, root }, BlockInputSource.network_processor, message.propagationSource.toString());
390
349
  preprocessResult = { action: PreprocessAction.AwaitEnvelope, root };
391
350
  }
392
351
  break;
@@ -397,7 +356,7 @@ export class NetworkProcessor {
397
356
  const payloadPresent = getPayloadPresentFromPayloadAttestationMessageSerialized(message.msg.data);
398
357
  if (payloadPresent && !this.chain.forkChoice.hasPayloadHexUnsafe(root)) {
399
358
  // payload attestation votes that the payload is available but it is not yet known
400
- this.searchUnknownRoot({ slot, root }, false, true, peerId);
359
+ this.searchUnknownEnvelope({ slot, root }, BlockInputSource.network_processor, message.propagationSource.toString());
401
360
  // do not await the envelope, payload attestation processing only requires that the block is known
402
361
  // also do not reset preprocessResult, we may already await for the block
403
362
  }
@@ -407,7 +366,7 @@ export class NetworkProcessor {
407
366
  if (root == null)
408
367
  break;
409
368
  if (!this.chain.forkChoice.hasPayloadHexUnsafe(root)) {
410
- this.searchUnknownRoot({ slot, root }, false, true, peerId);
369
+ this.searchUnknownEnvelope({ slot, root }, BlockInputSource.network_processor, message.propagationSource.toString());
411
370
  // do not await the envelope, we can do gossip validation
412
371
  // also do not reset preprocessResult, we may already await for the block
413
372
  }
@@ -418,7 +377,7 @@ export class NetworkProcessor {
418
377
  // Extract beacon_block_root directly
419
378
  const blockRoot = getBeaconBlockRootFromExecutionPayloadEnvelopeSerialized(message.msg.data);
420
379
  if (blockRoot && !this.chain.forkChoice.hasBlockHexUnsafe(blockRoot)) {
421
- this.searchUnknownRoot({ slot, root: blockRoot }, true, false, peerId);
380
+ this.searchUnknownBlock({ slot, root: blockRoot }, BlockInputSource.network_processor, message.propagationSource.toString());
422
381
  // We always want to await the block
423
382
  // This allows us to properly forward the payload envelope
424
383
  preprocessResult = { action: PreprocessAction.AwaitBlock, root: blockRoot };
@@ -435,12 +394,12 @@ export class NetworkProcessor {
435
394
  !this.chain.forkChoice.getBlockHexAndBlockHash(parentBlockRoot, parentBlockHash)) {
436
395
  const protoBlock = this.chain.forkChoice.getBlockHexDefaultStatus(parentBlockRoot);
437
396
  if (protoBlock === null) {
438
- this.searchUnknownRoot({ slot, root: parentBlockRoot }, true, false, peerId);
397
+ this.searchUnknownBlock({ slot, root: parentBlockRoot }, BlockInputSource.network_processor, message.propagationSource.toString());
439
398
  preprocessResult = { action: PreprocessAction.AwaitBlock, root: parentBlockRoot };
440
399
  }
441
400
  else if (protoBlock.executionPayloadBlockHash &&
442
401
  protoBlock.executionPayloadBlockHash !== parentBlockHash) {
443
- this.searchUnknownRoot({ slot, root: parentBlockRoot }, false, true, peerId);
402
+ this.searchUnknownEnvelope({ slot, root: parentBlockRoot }, BlockInputSource.network_processor, message.propagationSource.toString());
444
403
  preprocessResult = { action: PreprocessAction.AwaitEnvelope, root: parentBlockRoot };
445
404
  }
446
405
  }
@@ -452,96 +411,37 @@ export class NetworkProcessor {
452
411
  case PreprocessAction.PushToQueue:
453
412
  this.pushPendingGossipsubMessageToQueue(message);
454
413
  break;
455
- case PreprocessAction.AwaitBlock:
456
- this.maybeAwaitBlock(preprocessResult.root, topicType, slot, message);
414
+ case PreprocessAction.AwaitBlock: {
415
+ if (this.awaitingBlockMessageCount > MAX_QUEUED_UNKNOWN_BLOCK_GOSSIP_OBJECTS) {
416
+ // No need to report the dropped job to gossip. It will be eventually pruned from the mcache
417
+ this.metrics?.awaitingBlockGossipMessages.reject.inc({
418
+ reason: ReprocessRejectReason.reached_limit,
419
+ topic: topicType,
420
+ });
421
+ return;
422
+ }
423
+ this.metrics?.awaitingBlockGossipMessages.queue.inc({ topic: topicType });
424
+ const awaitingGossipsubMessages = this.awaitingMessagesByBlockRoot.getOrDefault(preprocessResult.root);
425
+ awaitingGossipsubMessages.add(message);
426
+ this.awaitingBlockMessageCount++;
457
427
  break;
458
- case PreprocessAction.AwaitEnvelope:
459
- this.maybeAwaitPayload(preprocessResult.root, topicType, slot, message);
428
+ }
429
+ case PreprocessAction.AwaitEnvelope: {
430
+ if (this.awaitingPayloadMessageCount > MAX_QUEUED_UNKNOWN_PAYLOAD_GOSSIP_OBJECTS) {
431
+ this.metrics?.awaitingPayloadGossipMessages.reject.inc({
432
+ reason: ReprocessRejectReason.reached_limit,
433
+ topic: topicType,
434
+ });
435
+ return;
436
+ }
437
+ this.metrics?.awaitingPayloadGossipMessages.queue.inc({ topic: topicType });
438
+ const awaitingPayloadGossipsubMessages = this.awaitingMessagesByPayloadBlockRoot.getOrDefault(preprocessResult.root);
439
+ awaitingPayloadGossipsubMessages.add(message);
440
+ this.awaitingPayloadMessageCount++;
460
441
  break;
442
+ }
461
443
  }
462
444
  };
463
- /**
464
- * Buffer a gossip message that must wait for an unknown block before it can be validated.
465
- *
466
- * For DOS protection, the message is ignored (reject metric incremented) when any cap is hit:
467
- * - MAX_QUEUED_UNKNOWN_BLOCK_GOSSIP_OBJECTS: global awaiting-block message count
468
- * - MAX_BUFFERED_ROOTS_PER_SLOT: distinct roots we buffer messages for, per slot
469
- * - MAX_AWAITING_MESSAGES_PER_ROOT[topic]: messages buffered per (root, topic)
470
- */
471
- maybeAwaitBlock(root, topicType, slot, message) {
472
- const metric = this.metrics?.awaitingBlockGossipMessages;
473
- // global message-count cap - cheapest check first
474
- if (this.awaitingBlockMessageCount > MAX_QUEUED_UNKNOWN_BLOCK_GOSSIP_OBJECTS) {
475
- metric?.reject.inc({ reason: ReprocessRejectReason.reached_limit, topic: topicType });
476
- return;
477
- }
478
- // per-(root, topic) message cap: e.g. <= NUMBER_OF_COLUMNS columns per root
479
- const perRootCap = MAX_AWAITING_MESSAGES_PER_ROOT[topicType];
480
- if (perRootCap !== undefined &&
481
- (this.awaitingMessagesByBlockRoot.get(root)?.get(topicType)?.size ?? 0) >= perRootCap) {
482
- metric?.reject.inc({ reason: ReprocessRejectReason.reached_topic_limit, topic: topicType });
483
- return;
484
- }
485
- if (!this.tryReserveBufferedRoot(slot, root)) {
486
- metric?.reject.inc({ reason: ReprocessRejectReason.reached_root_limit, topic: topicType });
487
- return;
488
- }
489
- metric?.queue.inc({ topic: topicType });
490
- this.awaitingMessagesByBlockRoot.getOrDefault(root).getOrDefault(topicType).add(message);
491
- this.awaitingBlockMessageCount++;
492
- }
493
- /** Payload counterpart of maybeAwaitBlock */
494
- maybeAwaitPayload(root, topicType, slot, message) {
495
- const metric = this.metrics?.awaitingPayloadGossipMessages;
496
- if (this.awaitingPayloadMessageCount > MAX_QUEUED_UNKNOWN_PAYLOAD_GOSSIP_OBJECTS) {
497
- metric?.reject.inc({ reason: ReprocessRejectReason.reached_limit, topic: topicType });
498
- return;
499
- }
500
- const perRootCap = MAX_AWAITING_MESSAGES_PER_ROOT[topicType];
501
- if (perRootCap !== undefined &&
502
- (this.awaitingMessagesByPayloadBlockRoot.get(root)?.get(topicType)?.size ?? 0) >= perRootCap) {
503
- metric?.reject.inc({ reason: ReprocessRejectReason.reached_topic_limit, topic: topicType });
504
- return;
505
- }
506
- if (!this.tryReserveBufferedRoot(slot, root)) {
507
- metric?.reject.inc({ reason: ReprocessRejectReason.reached_root_limit, topic: topicType });
508
- return;
509
- }
510
- metric?.queue.inc({ topic: topicType });
511
- this.awaitingMessagesByPayloadBlockRoot.getOrDefault(root).getOrDefault(topicType).add(message);
512
- this.awaitingPayloadMessageCount++;
513
- }
514
- /**
515
- * Cap distinct roots we emit an unknown-root search for, an already-tracked root (dedup) never counts as "too many".
516
- */
517
- tooManySearchedRoots(slot, root) {
518
- const roots = this.searchedRootsBySlot.get(slot);
519
- if (roots === undefined)
520
- return false;
521
- return !roots.has(root) && roots.size >= MAX_SEARCHED_ROOTS_PER_SLOT;
522
- }
523
- /**
524
- * Cap distinct roots we buffer per slot.
525
- */
526
- tryReserveBufferedRoot(slot, root) {
527
- const roots = this.bufferedRootsBySlot.get(slot);
528
- if (roots?.has(root))
529
- return true;
530
- if ((roots?.size ?? 0) >= MAX_BUFFERED_ROOTS_PER_SLOT)
531
- return false;
532
- this.bufferedRootsBySlot.getOrDefault(slot).add(root);
533
- return true;
534
- }
535
- searchUnknownRoot(slotRoot, searchBlock, searchEnvelope, peerId) {
536
- if (!searchBlock && !searchEnvelope)
537
- return;
538
- if (this.tooManySearchedRoots(slotRoot.slot, slotRoot.root))
539
- return;
540
- if (searchBlock)
541
- this.searchUnknownBlock(slotRoot, BlockInputSource.network_processor, peerId);
542
- if (searchEnvelope)
543
- this.searchUnknownEnvelope(slotRoot, BlockInputSource.network_processor, peerId);
544
- }
545
445
  pushPendingGossipsubMessageToQueue(message) {
546
446
  const topicType = message.topic.type;
547
447
  message.queueAddedMs = Date.now();
@@ -554,92 +454,66 @@ export class NetworkProcessor {
554
454
  this.executeWork();
555
455
  }
556
456
  onBlockProcessed = async ({ block: rootHex }) => {
557
- const messagesByTopic = this.awaitingMessagesByBlockRoot.get(rootHex);
558
- if (messagesByTopic === undefined) {
559
- return;
560
- }
561
- let total = 0;
562
- for (const messages of messagesByTopic.values()) {
563
- total += messages.size;
564
- }
565
- if (total === 0) {
457
+ const waitingGossipsubMessages = this.awaitingMessagesByBlockRoot.get(rootHex);
458
+ if (!waitingGossipsubMessages || waitingGossipsubMessages.size === 0) {
566
459
  return;
567
460
  }
568
461
  // Atomically remove from map and update counter before async iteration to
569
462
  // prevent double-decrement race with onClockSlot during yield points below
570
463
  if (this.awaitingMessagesByBlockRoot.delete(rootHex)) {
571
- this.awaitingBlockMessageCount -= total;
464
+ this.awaitingBlockMessageCount -= waitingGossipsubMessages.size;
572
465
  }
573
466
  const nowSec = Date.now() / 1000;
574
467
  let count = 0;
575
468
  // TODO: we can group attestations to process in batches but since we have the SeenAttestationDatas
576
469
  // cache, it may not be necessary at this time
577
- for (const messages of messagesByTopic.values()) {
578
- for (const message of messages) {
579
- const topicType = message.topic.type;
580
- this.metrics?.awaitingBlockGossipMessages.waitSecBeforeResolve.set({ topic: topicType }, nowSec - message.seenTimestampSec);
581
- this.metrics?.awaitingBlockGossipMessages.resolve.inc({ topic: topicType });
582
- this.pushPendingGossipsubMessageToQueue(message);
583
- count++;
584
- // don't want to block the event loop, worse case it'd wait for 16_084 / 1024 * 50ms = 800ms which is not a big deal
585
- if (count === MAX_AWAITING_GOSSIP_OBJECTS_PER_TICK) {
586
- count = 0;
587
- await sleep(AWAITING_GOSSIP_OBJECTS_YIELD_EVERY_MS);
588
- }
470
+ for (const message of waitingGossipsubMessages) {
471
+ const topicType = message.topic.type;
472
+ this.metrics?.awaitingBlockGossipMessages.waitSecBeforeResolve.set({ topic: topicType }, nowSec - message.seenTimestampSec);
473
+ this.metrics?.awaitingBlockGossipMessages.resolve.inc({ topic: topicType });
474
+ this.pushPendingGossipsubMessageToQueue(message);
475
+ count++;
476
+ // don't want to block the event loop, worse case it'd wait for 16_084 / 1024 * 50ms = 800ms which is not a big deal
477
+ if (count === MAX_AWAITING_GOSSIP_OBJECTS_PER_TICK) {
478
+ count = 0;
479
+ await sleep(AWAITING_GOSSIP_OBJECTS_YIELD_EVERY_MS);
589
480
  }
590
481
  }
591
482
  };
592
483
  onPayloadEnvelopeProcessed = async ({ blockRoot: rootHex }) => {
593
- const messagesByTopic = this.awaitingMessagesByPayloadBlockRoot.get(rootHex);
594
- if (messagesByTopic === undefined) {
595
- return;
596
- }
597
- let total = 0;
598
- for (const messages of messagesByTopic.values()) {
599
- total += messages.size;
600
- }
601
- if (total === 0) {
484
+ const waitingGossipsubMessages = this.awaitingMessagesByPayloadBlockRoot.get(rootHex);
485
+ if (!waitingGossipsubMessages || waitingGossipsubMessages.size === 0) {
602
486
  return;
603
487
  }
604
488
  // Atomically remove from map and update counter before async iteration to
605
489
  // prevent double-decrement race with onClockSlot during yield points below
606
490
  if (this.awaitingMessagesByPayloadBlockRoot.delete(rootHex)) {
607
- this.awaitingPayloadMessageCount -= total;
491
+ this.awaitingPayloadMessageCount -= waitingGossipsubMessages.size;
608
492
  }
609
493
  const nowSec = Date.now() / 1000;
610
494
  let count = 0;
611
- for (const messages of messagesByTopic.values()) {
612
- for (const message of messages) {
613
- const topicType = message.topic.type;
614
- this.metrics?.awaitingPayloadGossipMessages.waitSecBeforeResolve.set({ topic: topicType }, nowSec - message.seenTimestampSec);
615
- this.metrics?.awaitingPayloadGossipMessages.resolve.inc({ topic: topicType });
616
- this.pushPendingGossipsubMessageToQueue(message);
617
- count++;
618
- if (count === MAX_AWAITING_GOSSIP_OBJECTS_PER_TICK) {
619
- count = 0;
620
- await sleep(AWAITING_GOSSIP_OBJECTS_YIELD_EVERY_MS);
621
- }
495
+ for (const message of waitingGossipsubMessages) {
496
+ const topicType = message.topic.type;
497
+ this.metrics?.awaitingPayloadGossipMessages.waitSecBeforeResolve.set({ topic: topicType }, nowSec - message.seenTimestampSec);
498
+ this.metrics?.awaitingPayloadGossipMessages.resolve.inc({ topic: topicType });
499
+ this.pushPendingGossipsubMessageToQueue(message);
500
+ count++;
501
+ if (count === MAX_AWAITING_GOSSIP_OBJECTS_PER_TICK) {
502
+ count = 0;
503
+ await sleep(AWAITING_GOSSIP_OBJECTS_YIELD_EVERY_MS);
622
504
  }
623
505
  }
624
506
  };
625
507
  onClockSlot = (clockSlot) => {
626
508
  const nowSec = Date.now() / 1000;
627
509
  const minSlot = clockSlot - MAX_UNKNOWN_ROOTS_SLOT_CACHE_SIZE;
628
- for (const [slot, searchedRoots] of this.searchedRootsBySlot) {
510
+ for (const [slot, roots] of this.unknownBlocksBySlot) {
629
511
  if (slot > minSlot)
630
512
  continue;
631
- // expire messages awaiting an unknown block for these roots. Only roots this slot did a BLOCK search
632
- // for (blockPeerIds set) have block waits keyed here; skipping the rest avoids deleting a payload wait
633
- // that a newer slot created for the same root (block/envelope searches share searchedRootsBySlot).
634
- for (const [rootHex, entry] of searchedRoots) {
635
- if (entry.blockPeerIds === undefined)
636
- continue;
637
- const messagesByTopic = this.awaitingMessagesByBlockRoot.get(rootHex);
638
- if (messagesByTopic === undefined)
639
- continue;
640
- let removed = 0;
641
- for (const messages of messagesByTopic.values()) {
642
- for (const message of messages) {
513
+ for (const rootHex of roots.keys()) {
514
+ const gossipMessages = this.awaitingMessagesByBlockRoot.get(rootHex);
515
+ if (gossipMessages !== undefined) {
516
+ for (const message of gossipMessages) {
643
517
  const topicType = message.topic.type;
644
518
  this.metrics?.awaitingBlockGossipMessages.reject.inc({
645
519
  topic: topicType,
@@ -648,23 +522,20 @@ export class NetworkProcessor {
648
522
  this.metrics?.awaitingBlockGossipMessages.waitSecBeforeReject.set({ topic: topicType, reason: ReprocessRejectReason.expired }, nowSec - message.seenTimestampSec);
649
523
  // No need to report the dropped job to gossip. It will be eventually pruned from the mcache
650
524
  }
651
- removed += messages.size;
652
- }
653
- if (this.awaitingMessagesByBlockRoot.delete(rootHex)) {
654
- this.awaitingBlockMessageCount -= removed;
525
+ if (this.awaitingMessagesByBlockRoot.delete(rootHex)) {
526
+ this.awaitingBlockMessageCount -= gossipMessages.size;
527
+ }
655
528
  }
656
529
  }
657
- // expire messages awaiting an unknown payload envelope for these roots. Symmetric to the block pass:
658
- // only roots this slot did an ENVELOPE search for (envelopePeerIds set) have payload waits keyed here.
659
- for (const [rootHex, entry] of searchedRoots) {
660
- if (entry.envelopePeerIds === undefined)
661
- continue;
662
- const messagesByTopic = this.awaitingMessagesByPayloadBlockRoot.get(rootHex);
663
- if (messagesByTopic === undefined)
664
- continue;
665
- let removed = 0;
666
- for (const messages of messagesByTopic.values()) {
667
- for (const message of messages) {
530
+ this.unknownBlocksBySlot.delete(slot);
531
+ }
532
+ for (const [slot, roots] of this.unknownEnvelopesBySlot) {
533
+ if (slot > minSlot)
534
+ continue;
535
+ for (const rootHex of roots.keys()) {
536
+ const gossipMessages = this.awaitingMessagesByPayloadBlockRoot.get(rootHex);
537
+ if (gossipMessages !== undefined) {
538
+ for (const message of gossipMessages) {
668
539
  const topicType = message.topic.type;
669
540
  this.metrics?.awaitingPayloadGossipMessages.reject.inc({
670
541
  topic: topicType,
@@ -673,17 +544,12 @@ export class NetworkProcessor {
673
544
  this.metrics?.awaitingPayloadGossipMessages.waitSecBeforeReject.set({ topic: topicType, reason: ReprocessRejectReason.expired }, nowSec - message.seenTimestampSec);
674
545
  // No need to report the dropped job to gossip. It will be eventually pruned from the mcache
675
546
  }
676
- removed += messages.size;
677
- }
678
- if (this.awaitingMessagesByPayloadBlockRoot.delete(rootHex)) {
679
- this.awaitingPayloadMessageCount -= removed;
547
+ if (this.awaitingMessagesByPayloadBlockRoot.delete(rootHex)) {
548
+ this.awaitingPayloadMessageCount -= gossipMessages.size;
549
+ }
680
550
  }
681
551
  }
682
- this.searchedRootsBySlot.delete(slot);
683
- }
684
- for (const slot of this.bufferedRootsBySlot.keys()) {
685
- if (slot <= minSlot)
686
- this.bufferedRootsBySlot.delete(slot);
552
+ this.unknownEnvelopesBySlot.delete(slot);
687
553
  }
688
554
  };
689
555
  executeWork() {