@lodestar/beacon-node 1.34.0-dev.d5a4e7a09c → 1.34.0-dev.d9fc7bb103

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 (264) hide show
  1. package/lib/api/impl/beacon/blocks/index.js +162 -55
  2. package/lib/api/impl/beacon/blocks/index.js.map +1 -1
  3. package/lib/api/impl/beacon/pool/index.js +3 -3
  4. package/lib/api/impl/beacon/pool/index.js.map +1 -1
  5. package/lib/api/impl/beacon/state/index.js +15 -16
  6. package/lib/api/impl/beacon/state/index.js.map +1 -1
  7. package/lib/api/impl/debug/index.d.ts +1 -1
  8. package/lib/api/impl/debug/index.js +24 -1
  9. package/lib/api/impl/debug/index.js.map +1 -1
  10. package/lib/api/impl/validator/index.js +37 -26
  11. package/lib/api/impl/validator/index.js.map +1 -1
  12. package/lib/api/impl/validator/utils.d.ts +3 -3
  13. package/lib/api/impl/validator/utils.js +2 -2
  14. package/lib/api/impl/validator/utils.js.map +1 -1
  15. package/lib/chain/archiveStore/archiveStore.d.ts +1 -1
  16. package/lib/chain/archiveStore/archiveStore.js +2 -2
  17. package/lib/chain/archiveStore/interface.d.ts +1 -1
  18. package/lib/chain/archiveStore/utils/archiveBlocks.d.ts +1 -1
  19. package/lib/chain/archiveStore/utils/archiveBlocks.js +89 -22
  20. package/lib/chain/archiveStore/utils/archiveBlocks.js.map +1 -1
  21. package/lib/chain/beaconProposerCache.d.ts +1 -0
  22. package/lib/chain/beaconProposerCache.js +3 -0
  23. package/lib/chain/beaconProposerCache.js.map +1 -1
  24. package/lib/chain/blocks/importBlock.js +3 -2
  25. package/lib/chain/blocks/importBlock.js.map +1 -1
  26. package/lib/chain/blocks/types.d.ts +66 -23
  27. package/lib/chain/blocks/types.js +39 -5
  28. package/lib/chain/blocks/types.js.map +1 -1
  29. package/lib/chain/blocks/utils/zebraBanner.d.ts +2 -0
  30. package/lib/chain/blocks/utils/zebraBanner.js +45 -0
  31. package/lib/chain/blocks/utils/zebraBanner.js.map +1 -0
  32. package/lib/chain/blocks/verifyBlock.js +18 -5
  33. package/lib/chain/blocks/verifyBlock.js.map +1 -1
  34. package/lib/chain/blocks/verifyBlocksDataAvailability.js +21 -10
  35. package/lib/chain/blocks/verifyBlocksDataAvailability.js.map +1 -1
  36. package/lib/chain/blocks/writeBlockInputToDb.js +63 -16
  37. package/lib/chain/blocks/writeBlockInputToDb.js.map +1 -1
  38. package/lib/chain/chain.d.ts +18 -101
  39. package/lib/chain/chain.js +107 -68
  40. package/lib/chain/chain.js.map +1 -1
  41. package/lib/chain/emitter.d.ts +18 -2
  42. package/lib/chain/emitter.js +13 -0
  43. package/lib/chain/emitter.js.map +1 -1
  44. package/lib/chain/errors/dataColumnSidecarError.d.ts +69 -0
  45. package/lib/chain/errors/dataColumnSidecarError.js +21 -0
  46. package/lib/chain/errors/dataColumnSidecarError.js.map +1 -0
  47. package/lib/chain/errors/index.d.ts +1 -0
  48. package/lib/chain/errors/index.js +1 -0
  49. package/lib/chain/errors/index.js.map +1 -1
  50. package/lib/chain/interface.d.ts +6 -7
  51. package/lib/chain/interface.js.map +1 -1
  52. package/lib/chain/options.d.ts +4 -1
  53. package/lib/chain/options.js +1 -0
  54. package/lib/chain/options.js.map +1 -1
  55. package/lib/chain/prepareNextSlot.js +2 -1
  56. package/lib/chain/prepareNextSlot.js.map +1 -1
  57. package/lib/chain/produceBlock/produceBlockBody.d.ts +30 -16
  58. package/lib/chain/produceBlock/produceBlockBody.js +28 -28
  59. package/lib/chain/produceBlock/produceBlockBody.js.map +1 -1
  60. package/lib/chain/produceBlock/validateBlobsAndKzgCommitments.d.ts +6 -3
  61. package/lib/chain/produceBlock/validateBlobsAndKzgCommitments.js +28 -4
  62. package/lib/chain/produceBlock/validateBlobsAndKzgCommitments.js.map +1 -1
  63. package/lib/chain/seenCache/seenGossipBlockInput.d.ts +64 -18
  64. package/lib/chain/seenCache/seenGossipBlockInput.js +321 -53
  65. package/lib/chain/seenCache/seenGossipBlockInput.js.map +1 -1
  66. package/lib/chain/validation/dataColumnSidecar.d.ts +29 -0
  67. package/lib/chain/validation/dataColumnSidecar.js +248 -0
  68. package/lib/chain/validation/dataColumnSidecar.js.map +1 -0
  69. package/lib/db/beacon.d.ts +3 -1
  70. package/lib/db/beacon.js +3 -1
  71. package/lib/db/beacon.js.map +1 -1
  72. package/lib/db/buckets.d.ts +3 -1
  73. package/lib/db/buckets.js +2 -0
  74. package/lib/db/buckets.js.map +1 -1
  75. package/lib/db/interface.d.ts +3 -1
  76. package/lib/db/repositories/dataColumnSidecar.d.ts +26 -0
  77. package/lib/db/repositories/dataColumnSidecar.js +39 -0
  78. package/lib/db/repositories/dataColumnSidecar.js.map +1 -0
  79. package/lib/db/repositories/dataColumnSidecarArchive.d.ts +24 -0
  80. package/lib/db/repositories/dataColumnSidecarArchive.js +39 -0
  81. package/lib/db/repositories/dataColumnSidecarArchive.js.map +1 -0
  82. package/lib/db/repositories/index.d.ts +2 -0
  83. package/lib/db/repositories/index.js +2 -0
  84. package/lib/db/repositories/index.js.map +1 -1
  85. package/lib/execution/builder/http.d.ts +20 -4
  86. package/lib/execution/builder/http.js +30 -11
  87. package/lib/execution/builder/http.js.map +1 -1
  88. package/lib/execution/builder/interface.d.ts +5 -4
  89. package/lib/execution/engine/http.d.ts +8 -5
  90. package/lib/execution/engine/http.js +56 -42
  91. package/lib/execution/engine/http.js.map +1 -1
  92. package/lib/execution/engine/interface.d.ts +6 -13
  93. package/lib/execution/engine/interface.js +1 -1
  94. package/lib/execution/engine/interface.js.map +1 -1
  95. package/lib/execution/engine/mock.d.ts +1 -0
  96. package/lib/execution/engine/mock.js +5 -0
  97. package/lib/execution/engine/mock.js.map +1 -1
  98. package/lib/execution/engine/types.d.ts +12 -2
  99. package/lib/execution/engine/types.js +8 -2
  100. package/lib/execution/engine/types.js.map +1 -1
  101. package/lib/metrics/metrics/beacon.d.ts +15 -5
  102. package/lib/metrics/metrics/beacon.js +61 -14
  103. package/lib/metrics/metrics/beacon.js.map +1 -1
  104. package/lib/metrics/metrics/lodestar.d.ts +29 -1
  105. package/lib/metrics/metrics/lodestar.js +59 -0
  106. package/lib/metrics/metrics/lodestar.js.map +1 -1
  107. package/lib/network/core/metrics.d.ts +10 -3
  108. package/lib/network/core/metrics.js +22 -4
  109. package/lib/network/core/metrics.js.map +1 -1
  110. package/lib/network/core/networkCore.d.ts +15 -4
  111. package/lib/network/core/networkCore.js +73 -23
  112. package/lib/network/core/networkCore.js.map +1 -1
  113. package/lib/network/core/networkCoreWorker.js +2 -0
  114. package/lib/network/core/networkCoreWorker.js.map +1 -1
  115. package/lib/network/core/networkCoreWorkerHandler.d.ts +5 -3
  116. package/lib/network/core/networkCoreWorkerHandler.js +6 -1
  117. package/lib/network/core/networkCoreWorkerHandler.js.map +1 -1
  118. package/lib/network/core/types.d.ts +7 -4
  119. package/lib/network/events.d.ts +4 -2
  120. package/lib/network/events.js.map +1 -1
  121. package/lib/network/gossip/gossipsub.d.ts +2 -2
  122. package/lib/network/gossip/gossipsub.js +8 -6
  123. package/lib/network/gossip/gossipsub.js.map +1 -1
  124. package/lib/network/gossip/interface.d.ts +8 -1
  125. package/lib/network/gossip/interface.js +1 -0
  126. package/lib/network/gossip/interface.js.map +1 -1
  127. package/lib/network/gossip/scoringParameters.d.ts +6 -2
  128. package/lib/network/gossip/scoringParameters.js.map +1 -1
  129. package/lib/network/gossip/topic.d.ts +2033 -1484
  130. package/lib/network/gossip/topic.js +29 -1
  131. package/lib/network/gossip/topic.js.map +1 -1
  132. package/lib/network/interface.d.ts +11 -3
  133. package/lib/network/metadata.d.ts +9 -5
  134. package/lib/network/metadata.js +26 -5
  135. package/lib/network/metadata.js.map +1 -1
  136. package/lib/network/network.d.ts +14 -4
  137. package/lib/network/network.js +73 -11
  138. package/lib/network/network.js.map +1 -1
  139. package/lib/network/networkConfig.d.ts +12 -0
  140. package/lib/network/networkConfig.js +2 -0
  141. package/lib/network/networkConfig.js.map +1 -0
  142. package/lib/network/options.d.ts +1 -0
  143. package/lib/network/options.js +5 -0
  144. package/lib/network/options.js.map +1 -1
  145. package/lib/network/peers/discover.d.ts +8 -3
  146. package/lib/network/peers/discover.js +99 -14
  147. package/lib/network/peers/discover.js.map +1 -1
  148. package/lib/network/peers/peerManager.d.ts +10 -4
  149. package/lib/network/peers/peerManager.js +105 -19
  150. package/lib/network/peers/peerManager.js.map +1 -1
  151. package/lib/network/peers/peersData.d.ts +17 -3
  152. package/lib/network/peers/peersData.js.map +1 -1
  153. package/lib/network/peers/score/interface.d.ts +1 -1
  154. package/lib/network/peers/score/score.d.ts +2 -2
  155. package/lib/network/peers/score/score.js +4 -1
  156. package/lib/network/peers/score/score.js.map +1 -1
  157. package/lib/network/peers/score/store.d.ts +3 -1
  158. package/lib/network/peers/score/store.js +6 -2
  159. package/lib/network/peers/score/store.js.map +1 -1
  160. package/lib/network/peers/utils/assertPeerRelevance.d.ts +7 -3
  161. package/lib/network/peers/utils/assertPeerRelevance.js +10 -1
  162. package/lib/network/peers/utils/assertPeerRelevance.js.map +1 -1
  163. package/lib/network/peers/utils/prioritizePeers.d.ts +19 -7
  164. package/lib/network/peers/utils/prioritizePeers.js +42 -6
  165. package/lib/network/peers/utils/prioritizePeers.js.map +1 -1
  166. package/lib/network/processor/extractSlotRootFns.js +8 -1
  167. package/lib/network/processor/extractSlotRootFns.js.map +1 -1
  168. package/lib/network/processor/gossipHandlers.js +165 -16
  169. package/lib/network/processor/gossipHandlers.js.map +1 -1
  170. package/lib/network/processor/gossipQueues/index.js +5 -0
  171. package/lib/network/processor/gossipQueues/index.js.map +1 -1
  172. package/lib/network/processor/index.js +1 -0
  173. package/lib/network/processor/index.js.map +1 -1
  174. package/lib/network/reqresp/ReqRespBeaconNode.d.ts +2 -2
  175. package/lib/network/reqresp/ReqRespBeaconNode.js +36 -14
  176. package/lib/network/reqresp/ReqRespBeaconNode.js.map +1 -1
  177. package/lib/network/reqresp/beaconBlocksMaybeBlobsByRange.d.ts +24 -4
  178. package/lib/network/reqresp/beaconBlocksMaybeBlobsByRange.js +259 -20
  179. package/lib/network/reqresp/beaconBlocksMaybeBlobsByRange.js.map +1 -1
  180. package/lib/network/reqresp/beaconBlocksMaybeBlobsByRoot.d.ts +37 -6
  181. package/lib/network/reqresp/beaconBlocksMaybeBlobsByRoot.js +283 -28
  182. package/lib/network/reqresp/beaconBlocksMaybeBlobsByRoot.js.map +1 -1
  183. package/lib/network/reqresp/handlers/beaconBlocksByRange.d.ts +1 -1
  184. package/lib/network/reqresp/handlers/beaconBlocksByRange.js +3 -3
  185. package/lib/network/reqresp/handlers/beaconBlocksByRange.js.map +1 -1
  186. package/lib/network/reqresp/handlers/beaconBlocksByRoot.d.ts +2 -2
  187. package/lib/network/reqresp/handlers/beaconBlocksByRoot.js.map +1 -1
  188. package/lib/network/reqresp/handlers/blobSidecarsByRange.d.ts +2 -2
  189. package/lib/network/reqresp/handlers/blobSidecarsByRange.js +2 -3
  190. package/lib/network/reqresp/handlers/blobSidecarsByRange.js.map +1 -1
  191. package/lib/network/reqresp/handlers/dataColumnSidecarsByRange.d.ts +8 -0
  192. package/lib/network/reqresp/handlers/dataColumnSidecarsByRange.js +73 -0
  193. package/lib/network/reqresp/handlers/dataColumnSidecarsByRange.js.map +1 -0
  194. package/lib/network/reqresp/handlers/dataColumnSidecarsByRoot.d.ts +6 -0
  195. package/lib/network/reqresp/handlers/dataColumnSidecarsByRoot.js +36 -0
  196. package/lib/network/reqresp/handlers/dataColumnSidecarsByRoot.js.map +1 -0
  197. package/lib/network/reqresp/handlers/index.js +13 -2
  198. package/lib/network/reqresp/handlers/index.js.map +1 -1
  199. package/lib/network/reqresp/protocols.d.ts +4 -0
  200. package/lib/network/reqresp/protocols.js +20 -0
  201. package/lib/network/reqresp/protocols.js.map +1 -1
  202. package/lib/network/reqresp/rateLimit.js +19 -3
  203. package/lib/network/reqresp/rateLimit.js.map +1 -1
  204. package/lib/network/reqresp/score.js +3 -0
  205. package/lib/network/reqresp/score.js.map +1 -1
  206. package/lib/network/reqresp/types.d.ts +13 -6
  207. package/lib/network/reqresp/types.js +14 -5
  208. package/lib/network/reqresp/types.js.map +1 -1
  209. package/lib/network/statusCache.d.ts +5 -5
  210. package/lib/network/statusCache.js.map +1 -1
  211. package/lib/network/subnets/interface.d.ts +3 -0
  212. package/lib/network/subnets/interface.js +14 -1
  213. package/lib/network/subnets/interface.js.map +1 -1
  214. package/lib/network/subnets/syncnetsService.js +4 -5
  215. package/lib/network/subnets/syncnetsService.js.map +1 -1
  216. package/lib/node/nodejs.js +1 -0
  217. package/lib/node/nodejs.js.map +1 -1
  218. package/lib/sync/constants.d.ts +18 -3
  219. package/lib/sync/constants.js +21 -3
  220. package/lib/sync/constants.js.map +1 -1
  221. package/lib/sync/interface.d.ts +2 -2
  222. package/lib/sync/interface.js +1 -1
  223. package/lib/sync/interface.js.map +1 -1
  224. package/lib/sync/range/batch.d.ts +17 -2
  225. package/lib/sync/range/batch.js +39 -7
  226. package/lib/sync/range/batch.js.map +1 -1
  227. package/lib/sync/range/chain.d.ts +15 -1
  228. package/lib/sync/range/chain.js +124 -33
  229. package/lib/sync/range/chain.js.map +1 -1
  230. package/lib/sync/range/range.d.ts +3 -2
  231. package/lib/sync/range/range.js +9 -3
  232. package/lib/sync/range/range.js.map +1 -1
  233. package/lib/sync/range/utils/chainTarget.d.ts +5 -1
  234. package/lib/sync/range/utils/chainTarget.js +26 -1
  235. package/lib/sync/range/utils/chainTarget.js.map +1 -1
  236. package/lib/sync/range/utils/peerBalancer.d.ts +19 -5
  237. package/lib/sync/range/utils/peerBalancer.js +104 -10
  238. package/lib/sync/range/utils/peerBalancer.js.map +1 -1
  239. package/lib/sync/sync.js +1 -1
  240. package/lib/sync/sync.js.map +1 -1
  241. package/lib/sync/unknownBlock.d.ts +54 -5
  242. package/lib/sync/unknownBlock.js +321 -61
  243. package/lib/sync/unknownBlock.js.map +1 -1
  244. package/lib/sync/utils/remoteSyncType.d.ts +4 -4
  245. package/lib/sync/utils/remoteSyncType.js.map +1 -1
  246. package/lib/util/blobs.d.ts +16 -4
  247. package/lib/util/blobs.js +122 -5
  248. package/lib/util/blobs.js.map +1 -1
  249. package/lib/util/dataColumns.d.ts +137 -0
  250. package/lib/util/dataColumns.js +358 -0
  251. package/lib/util/dataColumns.js.map +1 -0
  252. package/lib/util/metadata.d.ts +5 -0
  253. package/lib/util/metadata.js +10 -0
  254. package/lib/util/metadata.js.map +1 -1
  255. package/lib/util/sszBytes.d.ts +1 -0
  256. package/lib/util/sszBytes.js +17 -0
  257. package/lib/util/sszBytes.js.map +1 -1
  258. package/lib/util/types.d.ts +7 -0
  259. package/lib/util/types.js +3 -0
  260. package/lib/util/types.js.map +1 -1
  261. package/package.json +18 -18
  262. package/lib/network/reqresp/handlers/status.d.ts +0 -4
  263. package/lib/network/reqresp/handlers/status.js +0 -11
  264. package/lib/network/reqresp/handlers/status.js.map +0 -1
@@ -1,13 +1,15 @@
1
- import { INTERVALS_PER_SLOT } from "@lodestar/params";
1
+ import { ForkName, ForkSeq, INTERVALS_PER_SLOT } from "@lodestar/params";
2
2
  import { fromHex, pruneSetToMax, toRootHex } from "@lodestar/utils";
3
3
  import { sleep } from "@lodestar/utils";
4
4
  import { BlockInputType } from "../chain/blocks/types.js";
5
5
  import { BlockError, BlockErrorCode } from "../chain/errors/index.js";
6
- import { NetworkEvent, PeerAction } from "../network/index.js";
6
+ import { NetworkEvent } from "../network/index.js";
7
7
  import { beaconBlocksMaybeBlobsByRoot, unavailableBeaconBlobsByRoot, } from "../network/reqresp/beaconBlocksMaybeBlobsByRoot.js";
8
8
  import { byteArrayEquals } from "../util/bytes.js";
9
9
  import { shuffle } from "../util/shuffle.js";
10
+ import { sortBy } from "../util/sortBy.js";
10
11
  import { wrapError } from "../util/wrapError.js";
12
+ import { MAX_CONCURRENT_REQUESTS } from "./constants.js";
11
13
  import { PendingBlockStatus, PendingBlockType } from "./interface.js";
12
14
  import { getAllDescendantBlocks, getDescendantBlocks, getUnknownAndAncestorBlocks } from "./utils/pendingBlocksTree.js";
13
15
  const MAX_ATTEMPTS_PER_BLOCK = 5;
@@ -68,6 +70,21 @@ export class UnknownBlockSync {
68
70
  this.logger.debug("Error handling unknownBlockParent event", {}, e);
69
71
  }
70
72
  };
73
+ this.onPeerConnected = (data) => {
74
+ try {
75
+ const peerId = data.peer;
76
+ const peerSyncMeta = this.network.getConnectedPeerSyncMeta(peerId);
77
+ this.peerBalancer.onPeerConnected(data.peer, peerSyncMeta);
78
+ this.triggerUnknownBlockSearch();
79
+ }
80
+ catch (e) {
81
+ this.logger.debug("Error handling peerConnected event", {}, e);
82
+ }
83
+ };
84
+ this.onPeerDisconnected = (data) => {
85
+ const peerId = data.peer;
86
+ this.peerBalancer.onPeerDisconnected(peerId);
87
+ };
71
88
  /**
72
89
  * Gather tip parent blocks with unknown parent and do a search for all of them
73
90
  */
@@ -105,16 +122,22 @@ export class UnknownBlockSync {
105
122
  }
106
123
  // most of the time there is exactly 1 unknown block
107
124
  for (const block of unknowns) {
108
- this.downloadBlock(block, connectedPeers).catch((e) => {
125
+ this.downloadBlock(block).catch((e) => {
109
126
  this.logger.debug("Unexpected error - downloadBlock", { root: block.blockRootHex }, e);
110
127
  });
111
128
  }
112
129
  };
113
130
  this.maxPendingBlocks = opts?.maxPendingBlocks ?? MAX_PENDING_BLOCKS;
114
131
  this.proposerBoostSecWindow = this.config.SECONDS_PER_SLOT / INTERVALS_PER_SLOT;
132
+ this.peerBalancer = new UnknownBlockPeerBalancer(this.network.custodyConfig);
115
133
  if (metrics) {
116
- metrics.syncUnknownBlock.pendingBlocks.addCollect(() => metrics.syncUnknownBlock.pendingBlocks.set(this.pendingBlocks.size));
117
- metrics.syncUnknownBlock.knownBadBlocks.addCollect(() => metrics.syncUnknownBlock.knownBadBlocks.set(this.knownBadBlocks.size));
134
+ metrics.syncUnknownBlock.pendingBlocks.addCollect(() => {
135
+ metrics.syncUnknownBlock.pendingBlocks.set(this.pendingBlocks.size);
136
+ metrics.syncUnknownBlock.knownBadBlocks.set(this.knownBadBlocks.size);
137
+ metrics.syncUnknownBlock.peerBalancer.peersMetaCount.set(this.peerBalancer.peersMeta.size);
138
+ metrics.syncUnknownBlock.peerBalancer.peersActiveRequestCount.set(this.peerBalancer.activeRequests.size);
139
+ metrics.syncUnknownBlock.peerBalancer.totalActiveRequests.set(this.peerBalancer.getTotalActiveRequests());
140
+ });
118
141
  }
119
142
  }
120
143
  subscribeToNetwork() {
@@ -125,7 +148,8 @@ export class UnknownBlockSync {
125
148
  this.network.events.on(NetworkEvent.unknownBlock, this.onUnknownBlock);
126
149
  this.network.events.on(NetworkEvent.unknownBlockInput, this.onUnknownBlockInput);
127
150
  this.network.events.on(NetworkEvent.unknownBlockParent, this.onUnknownParent);
128
- this.network.events.on(NetworkEvent.peerConnected, this.triggerUnknownBlockSearch);
151
+ this.network.events.on(NetworkEvent.peerConnected, this.onPeerConnected);
152
+ this.network.events.on(NetworkEvent.peerDisconnected, this.onPeerDisconnected);
129
153
  this.subscribedToNetworkEvents = true;
130
154
  }
131
155
  }
@@ -138,7 +162,8 @@ export class UnknownBlockSync {
138
162
  this.network.events.off(NetworkEvent.unknownBlock, this.onUnknownBlock);
139
163
  this.network.events.off(NetworkEvent.unknownBlockInput, this.onUnknownBlockInput);
140
164
  this.network.events.off(NetworkEvent.unknownBlockParent, this.onUnknownParent);
141
- this.network.events.off(NetworkEvent.peerConnected, this.triggerUnknownBlockSearch);
165
+ this.network.events.off(NetworkEvent.peerConnected, this.onPeerConnected);
166
+ this.network.events.off(NetworkEvent.peerDisconnected, this.onPeerDisconnected);
142
167
  this.subscribedToNetworkEvents = false;
143
168
  }
144
169
  close() {
@@ -150,7 +175,7 @@ export class UnknownBlockSync {
150
175
  }
151
176
  /**
152
177
  * When a blockInput comes with an unknown parent:
153
- * - add the block to pendingBlocks with status downloaded, blockRootHex as key. This is similar to
178
+ * - add the block to pendingBlocks with status downloaded or pending blockRootHex as key. This is similar to
154
179
  * an `onUnknownBlock` event, but the blocks is downloaded.
155
180
  * - add the parent root to pendingBlocks with status pending, parentBlockRootHex as key. This is
156
181
  * the same to an `onUnknownBlock` event with parentBlockRootHex as root.
@@ -163,14 +188,26 @@ export class UnknownBlockSync {
163
188
  // add 1 pending block with status downloaded
164
189
  let pendingBlock = this.pendingBlocks.get(blockRootHex);
165
190
  if (!pendingBlock) {
166
- pendingBlock = {
167
- blockRootHex,
168
- parentBlockRootHex,
169
- blockInput,
170
- peerIdStrs: new Set(),
171
- status: PendingBlockStatus.downloaded,
172
- downloadAttempts: 0,
173
- };
191
+ pendingBlock =
192
+ blockInput.type === BlockInputType.dataPromise
193
+ ? {
194
+ unknownBlockType: PendingBlockType.UNKNOWN_DATA,
195
+ blockRootHex,
196
+ // this will be set after we download block
197
+ parentBlockRootHex: null,
198
+ blockInput,
199
+ peerIdStrs: new Set(),
200
+ status: PendingBlockStatus.pending,
201
+ downloadAttempts: 0,
202
+ }
203
+ : {
204
+ blockRootHex,
205
+ parentBlockRootHex,
206
+ blockInput,
207
+ peerIdStrs: new Set(),
208
+ status: PendingBlockStatus.downloaded,
209
+ downloadAttempts: 0,
210
+ };
174
211
  this.pendingBlocks.set(blockRootHex, pendingBlock);
175
212
  this.logger.verbose("Added unknown block parent to pendingBlocks", {
176
213
  root: blockRootHex,
@@ -194,7 +231,7 @@ export class UnknownBlockSync {
194
231
  if (blockInputOrRootHex.block !== null) {
195
232
  const { block } = blockInputOrRootHex;
196
233
  blockRootHex = toRootHex(this.config.getForkTypes(block.message.slot).BeaconBlock.hashTreeRoot(block.message));
197
- unknownBlockType = PendingBlockType.UNKNOWN_BLOBS;
234
+ unknownBlockType = PendingBlockType.UNKNOWN_DATA;
198
235
  }
199
236
  else {
200
237
  unknownBlockType = PendingBlockType.UNKNOWN_BLOCKINPUT;
@@ -207,6 +244,7 @@ export class UnknownBlockSync {
207
244
  pendingBlock = {
208
245
  unknownBlockType,
209
246
  blockRootHex,
247
+ // this will be set after we download block
210
248
  parentBlockRootHex: null,
211
249
  blockInput,
212
250
  peerIdStrs: new Set(),
@@ -230,24 +268,26 @@ export class UnknownBlockSync {
230
268
  }
231
269
  return unknownBlockType;
232
270
  }
233
- async downloadBlock(block, connectedPeers) {
271
+ async downloadBlock(block) {
234
272
  if (block.status !== PendingBlockStatus.pending) {
235
273
  return;
236
274
  }
237
275
  const unknownBlockType = block.unknownBlockType;
238
- this.logger.verbose("Downloading unknown block", {
276
+ const logCtx = {
239
277
  root: block.blockRootHex,
240
278
  pendingBlocks: this.pendingBlocks.size,
241
279
  slot: block.blockInput?.block?.message.slot ?? "unknown",
242
280
  unknownBlockType,
243
- });
281
+ };
282
+ this.logger.verbose("Downloading unknown block", logCtx);
244
283
  block.status = PendingBlockStatus.fetching;
245
284
  let res;
246
285
  if (block.blockInput === null) {
247
- res = await wrapError(this.fetchUnknownBlockRoot(fromHex(block.blockRootHex), connectedPeers));
286
+ // we only have block root, and nothing else
287
+ res = await wrapError(this.fetchUnknownBlockRoot(fromHex(block.blockRootHex)));
248
288
  }
249
289
  else {
250
- res = await wrapError(this.fetchUnavailableBlockInput(block.blockInput, connectedPeers));
290
+ res = await wrapError(this.fetchUnavailableBlockInput(block.blockInput));
251
291
  }
252
292
  if (res.err)
253
293
  this.metrics?.syncUnknownBlock.downloadedBlocksError.inc();
@@ -255,6 +295,11 @@ export class UnknownBlockSync {
255
295
  this.metrics?.syncUnknownBlock.downloadedBlocksSuccess.inc();
256
296
  if (!res.err) {
257
297
  const { blockInput, peerIdStr } = res.result;
298
+ // fetchUnknownBlockRoot and fetchUnavailableBlockInput should return available data BlockInput, throw error if not
299
+ if (blockInput.type === BlockInputType.dataPromise) {
300
+ // if there were any peers who would have had the missing datacolumns, it would have resulted in err
301
+ throw Error(`Expected BlockInput to be available, got dataPromise for ${block.blockRootHex}`);
302
+ }
258
303
  block = {
259
304
  ...block,
260
305
  status: PendingBlockStatus.downloaded,
@@ -300,7 +345,7 @@ export class UnknownBlockSync {
300
345
  }
301
346
  }
302
347
  else {
303
- // this allows to retry the download of the block
348
+ // block download has error, this allows to retry the download of the block
304
349
  block.status = PendingBlockStatus.pending;
305
350
  // parentSlot > finalizedSlot, continue downloading parent of parent
306
351
  block.downloadAttempts++;
@@ -319,9 +364,21 @@ export class UnknownBlockSync {
319
364
  /**
320
365
  * Send block to the processor awaiting completition. If processed successfully, send all children to the processor.
321
366
  * On error, remove and downscore all descendants.
367
+ * This function could run recursively for all descendant blocks
322
368
  */
323
369
  async processBlock(pendingBlock) {
370
+ // pending block status is `downloaded` right after `downloadBlock`
371
+ // but could be `pending` if added by `onUnknownBlockParent` event and this function is called recursively
324
372
  if (pendingBlock.status !== PendingBlockStatus.downloaded) {
373
+ if (pendingBlock.status === PendingBlockStatus.pending) {
374
+ const connectedPeers = this.network.getConnectedPeers();
375
+ if (connectedPeers.length === 0) {
376
+ this.logger.debug("No connected peers, skipping download block", { blockRoot: pendingBlock.blockRootHex });
377
+ return;
378
+ }
379
+ // if the download is a success we'll call `processBlock()` for this block
380
+ await this.downloadBlock(pendingBlock);
381
+ }
325
382
  return;
326
383
  }
327
384
  pendingBlock.status = PendingBlockStatus.processing;
@@ -403,83 +460,126 @@ export class UnknownBlockSync {
403
460
  }
404
461
  }
405
462
  /**
406
- * Fetches the parent of a block by root from a set of shuffled peers.
407
- * Will attempt a max of `MAX_ATTEMPTS_PER_BLOCK` on different peers if connectPeers.length > MAX_ATTEMPTS_PER_BLOCK.
463
+ * From a set of shuffled peers:
464
+ * - fetch the block
465
+ * - from deneb, fetch all missing blobs
466
+ * - from peerDAS, fetch sampled colmns
467
+ * TODO: this means we only have block root, and nothing else. Consider to reflect this in the function name
468
+ * prefulu, will attempt a max of `MAX_ATTEMPTS_PER_BLOCK` on different peers, postfulu we may attempt more as defined in `getMaxDownloadAttempts()` function
408
469
  * Also verifies the received block root + returns the peer that provided the block for future downscoring.
409
470
  */
410
- async fetchUnknownBlockRoot(blockRoot, connectedPeers) {
411
- const shuffledPeers = shuffle(connectedPeers);
471
+ async fetchUnknownBlockRoot(blockRoot) {
412
472
  const blockRootHex = toRootHex(blockRoot);
473
+ const excludedPeers = new Set();
474
+ let partialDownload = null;
475
+ const defaultPendingColumns = this.config.getForkSeq(this.chain.clock.currentSlot) >= ForkSeq.fulu
476
+ ? new Set(this.network.custodyConfig.sampleGroups)
477
+ : null;
413
478
  let lastError = null;
414
- for (let i = 0; i < MAX_ATTEMPTS_PER_BLOCK; i++) {
415
- const peer = shuffledPeers[i % shuffledPeers.length];
479
+ let i = 0;
480
+ while (i++ < this.getMaxDownloadAttempts()) {
481
+ // pendingDataColumns is null prefulu
482
+ const peer = this.peerBalancer.bestPeerForPendingColumns(partialDownload ? new Set(partialDownload.pendingDataColumns) : defaultPendingColumns, excludedPeers);
483
+ if (peer === null) {
484
+ // no more peer with needed columns to try, throw error
485
+ throw Error(`Error fetching UnknownBlockRoot after ${i}: cannot find peer with needed columns ${partialDownload?.pendingDataColumns.join(", ")}`);
486
+ }
487
+ const { peerId, client: peerClient } = peer;
488
+ excludedPeers.add(peerId);
416
489
  try {
417
- const [blockInput] = await beaconBlocksMaybeBlobsByRoot(this.config, this.network, peer, [blockRoot]);
490
+ const { blocks: [blockInput], pendingDataColumns, } = await beaconBlocksMaybeBlobsByRoot(this.config, this.network, peerId, [blockRoot], partialDownload, peerClient, this.metrics, this.logger);
418
491
  // Peer does not have the block, try with next peer
419
492
  if (blockInput === undefined) {
420
493
  continue;
421
494
  }
422
- // Verify block root is correct
495
+ if (pendingDataColumns !== null) {
496
+ partialDownload = { blocks: [blockInput], pendingDataColumns };
497
+ continue;
498
+ }
499
+ // data is available, verify block root is correct
423
500
  const block = blockInput.block.message;
424
501
  const receivedBlockRoot = this.config.getForkTypes(block.slot).BeaconBlock.hashTreeRoot(block);
425
502
  if (!byteArrayEquals(receivedBlockRoot, blockRoot)) {
426
503
  throw Error(`Wrong block received by peer, got ${toRootHex(receivedBlockRoot)} expected ${blockRootHex}`);
427
504
  }
428
- return { blockInput, peerIdStr: peer };
505
+ return { blockInput, peerIdStr: peerId };
429
506
  }
430
507
  catch (e) {
431
- this.logger.debug("Error fetching UnknownBlockRoot", { attempt: i, blockRootHex, peer }, e);
508
+ this.logger.debug("Error fetching UnknownBlockRoot", { attempt: i, blockRootHex, peer: peerId }, e);
432
509
  lastError = e;
433
510
  }
511
+ finally {
512
+ this.peerBalancer.onRequestCompleted(peerId);
513
+ }
434
514
  }
435
515
  if (lastError) {
436
- lastError.message = `Error fetching UnknownBlockRoot after ${MAX_ATTEMPTS_PER_BLOCK} attempts: ${lastError.message}`;
516
+ lastError.message = `Error fetching UnknownBlockRoot after ${i} attempts: ${lastError.message}`;
437
517
  throw lastError;
438
518
  }
439
- throw Error(`Error fetching UnknownBlockRoot after ${MAX_ATTEMPTS_PER_BLOCK}: unknown error`);
519
+ throw Error(`Error fetching UnknownBlockRoot after ${i}: cannot download all blobs or data columns for block ${blockRootHex}`);
440
520
  }
441
521
  /**
442
- * Fetches missing blobs for the blockinput, in future can also pull block is thats also missing
443
- * along with the blobs (i.e. only some blobs are available)
522
+ * We have partial block input:
523
+ * - we have block but not have all blobs (deneb) or needed columns (fulu)
524
+ * - we don't have block and have some blobs (deneb) or some columns (fulu)
525
+ * Fetches missing block/data columns/block for the blockinput. This function returns either preData or availableData BlockInput.
444
526
  */
445
- async fetchUnavailableBlockInput(unavailableBlockInput, connectedPeers) {
527
+ async fetchUnavailableBlockInput(unavailableBlockInput) {
446
528
  if (unavailableBlockInput.block !== null && unavailableBlockInput.type !== BlockInputType.dataPromise) {
447
529
  return { blockInput: unavailableBlockInput, peerIdStr: "" };
448
530
  }
449
- const shuffledPeers = shuffle(connectedPeers);
450
531
  let blockRootHex;
451
- let pendingBlobs;
452
532
  let blobKzgCommitmentsLen;
453
533
  let blockRoot;
534
+ const dataMeta = {};
535
+ let sampledColumns = [];
454
536
  if (unavailableBlockInput.block === null) {
455
537
  blockRootHex = unavailableBlockInput.blockRootHex;
456
538
  blockRoot = fromHex(blockRootHex);
457
539
  }
458
540
  else {
459
- const unavailableBlock = unavailableBlockInput.block;
541
+ const { cachedData, block: unavailableBlock } = unavailableBlockInput;
460
542
  blockRoot = this.config
461
543
  .getForkTypes(unavailableBlock.message.slot)
462
544
  .BeaconBlock.hashTreeRoot(unavailableBlock.message);
463
545
  blockRootHex = toRootHex(blockRoot);
464
546
  blobKzgCommitmentsLen = unavailableBlock.message.body.blobKzgCommitments.length;
465
- pendingBlobs = blobKzgCommitmentsLen - unavailableBlockInput.cachedData.blobsCache.size;
547
+ if (cachedData.fork === ForkName.deneb || cachedData.fork === ForkName.electra) {
548
+ const pendingBlobs = blobKzgCommitmentsLen - cachedData.blobsCache.size;
549
+ Object.assign(dataMeta, { pendingBlobs });
550
+ }
551
+ else if (cachedData.fork === ForkName.fulu) {
552
+ sampledColumns = this.network.custodyConfig.sampledColumns;
553
+ const pendingColumns = sampledColumns.length - cachedData.dataColumnsCache.size;
554
+ Object.assign(dataMeta, { pendingColumns });
555
+ }
466
556
  }
467
557
  let lastError = null;
468
- for (let i = 0; i < MAX_ATTEMPTS_PER_BLOCK; i++) {
469
- const peer = shuffledPeers[i % shuffledPeers.length];
558
+ let i = 0;
559
+ const excludedPeers = new Set();
560
+ while (i++ < this.getMaxDownloadAttempts()) {
561
+ const bestPeer = this.peerBalancer.bestPeerForBlockInput(unavailableBlockInput, excludedPeers);
562
+ if (bestPeer === null) {
563
+ // no more peer to try, throw error
564
+ throw Error(`Error fetching UnavailableBlockInput after ${i}: cannot find peer with needed columns ${sampledColumns.join(", ")}`);
565
+ }
566
+ const { peerId, client: peerClient } = bestPeer;
567
+ excludedPeers.add(peerId);
470
568
  try {
471
- const blockInput = await unavailableBeaconBlobsByRoot(this.config, this.network, peer, unavailableBlockInput, {
569
+ const blockInput = await unavailableBeaconBlobsByRoot(this.config, this.network, peerId, peerClient, unavailableBlockInput, {
472
570
  metrics: this.metrics,
473
- emitter: this.chain.emitter,
571
+ logger: this.logger,
474
572
  executionEngine: this.chain.executionEngine,
475
- engineGetBlobsCache: this.engineGetBlobsCache,
573
+ emitter: this.chain.emitter,
476
574
  blockInputsRetryTrackerCache: this.blockInputsRetryTrackerCache,
575
+ engineGetBlobsCache: this.engineGetBlobsCache,
477
576
  });
478
- // Peer does not have the block, try with next peer
479
- if (blockInput === undefined) {
577
+ if (unavailableBlockInput.block !== null && blockInput.type === BlockInputType.dataPromise) {
578
+ // all datacolumns were not downloaded we can continue with other peers
579
+ // as unavailableBlockInput.block's dataColumnsCache would be updated
480
580
  continue;
481
581
  }
482
- // Verify block root is correct
582
+ // data is available, verify block root is correct
483
583
  const block = blockInput.block.message;
484
584
  const receivedBlockRoot = this.config.getForkTypes(block.slot).BeaconBlock.hashTreeRoot(block);
485
585
  if (!byteArrayEquals(receivedBlockRoot, blockRoot)) {
@@ -489,20 +589,23 @@ export class UnknownBlockSync {
489
589
  this.logger.debug("Fetched NullBlockInput", { attempts: i, blockRootHex });
490
590
  }
491
591
  else {
492
- this.logger.debug("Fetched UnavailableBlockInput", { attempts: i, pendingBlobs, blobKzgCommitmentsLen });
592
+ this.logger.debug("Fetched UnavailableBlockInput", { attempts: i, ...dataMeta, blobKzgCommitmentsLen });
493
593
  }
494
- return { blockInput, peerIdStr: peer };
594
+ return { blockInput, peerIdStr: peerId };
495
595
  }
496
596
  catch (e) {
497
- this.logger.debug("Error fetching UnavailableBlockInput", { attempt: i, blockRootHex, peer }, e);
597
+ this.logger.debug("Error fetching UnavailableBlockInput", { attempt: i, blockRootHex, peer: peerId }, e);
498
598
  lastError = e;
499
599
  }
600
+ finally {
601
+ this.peerBalancer.onRequestCompleted(peerId);
602
+ }
500
603
  }
501
604
  if (lastError) {
502
- lastError.message = `Error fetching UnavailableBlockInput after ${MAX_ATTEMPTS_PER_BLOCK} attempts: ${lastError.message}`;
605
+ lastError.message = `Error fetching UnavailableBlockInput after ${i} attempts: ${lastError.message}`;
503
606
  throw lastError;
504
607
  }
505
- throw Error(`Error fetching UnavailableBlockInput after ${MAX_ATTEMPTS_PER_BLOCK}: unknown error`);
608
+ throw Error(`Error fetching UnavailableBlockInput after ${i}: unknown error`);
506
609
  }
507
610
  /**
508
611
  * Gets all descendant blocks of `block` recursively from `pendingBlocks`.
@@ -513,13 +616,15 @@ export class UnknownBlockSync {
513
616
  removeAndDownscoreAllDescendants(block) {
514
617
  // Get all blocks that are a descendant of this one
515
618
  const badPendingBlocks = this.removeAllDescendants(block);
619
+ // just console log and do not penalize on pending/bad blocks for debugging
620
+ // console.log("removeAndDownscoreAllDescendants", {block});
516
621
  for (const block of badPendingBlocks) {
517
- this.knownBadBlocks.add(block.blockRootHex);
518
- for (const peerIdStr of block.peerIdStrs) {
519
- // TODO: Refactor peerRpcScores to work with peerIdStr only
520
- this.network.reportPeer(peerIdStr, PeerAction.LowToleranceError, "BadBlockByRoot");
521
- }
522
- this.logger.debug("Banning unknown block", {
622
+ // this.knownBadBlocks.add(block.blockRootHex);
623
+ // for (const peerIdStr of block.peerIdStrs) {
624
+ // // TODO: Refactor peerRpcScores to work with peerIdStr only
625
+ // this.network.reportPeer(peerIdStr, PeerAction.LowToleranceError, "BadBlockByRoot");
626
+ // }
627
+ this.logger.debug("ignored Banning unknown block", {
523
628
  root: block.blockRootHex,
524
629
  peerIdStrs: Array.from(block.peerIdStrs).join(","),
525
630
  });
@@ -539,5 +644,160 @@ export class UnknownBlockSync {
539
644
  }
540
645
  return badPendingBlocks;
541
646
  }
647
+ getMaxDownloadAttempts() {
648
+ if (this.config.getForkSeq(this.chain.clock.currentSlot) < ForkSeq.fulu) {
649
+ return MAX_ATTEMPTS_PER_BLOCK;
650
+ }
651
+ // TODO: I consider max 20 downloads per block for a supernode is enough for devnets
652
+ // review this computation for public testnets or mainnet
653
+ return Math.min(20, (MAX_ATTEMPTS_PER_BLOCK * this.network.custodyConfig.sampleGroups.length) / this.config.SAMPLES_PER_SLOT);
654
+ }
655
+ }
656
+ /**
657
+ * Class to track active byRoots requests and balance them across eligible peers.
658
+ */
659
+ export class UnknownBlockPeerBalancer {
660
+ constructor(custodyConfig) {
661
+ this.peersMeta = new Map();
662
+ this.activeRequests = new Map();
663
+ this.custodyConfig = custodyConfig;
664
+ }
665
+ /** Trigger on each peer re-status */
666
+ onPeerConnected(peerId, syncMeta) {
667
+ this.peersMeta.set(peerId, syncMeta);
668
+ if (!this.activeRequests.has(peerId)) {
669
+ this.activeRequests.set(peerId, 0);
670
+ }
671
+ }
672
+ onPeerDisconnected(peerId) {
673
+ this.peersMeta.delete(peerId);
674
+ this.activeRequests.delete(peerId);
675
+ }
676
+ /**
677
+ * called from fetchUnknownBlockRoot() where we only have block root and nothing else
678
+ * excludedPeers are the peers that we requested already so we don't want to try again
679
+ * pendingColumns is empty for prefulu, or the 1st time we we download a block by root
680
+ */
681
+ bestPeerForPendingColumns(pendingColumns, excludedPeers) {
682
+ const eligiblePeers = this.filterPeers(pendingColumns, excludedPeers);
683
+ if (eligiblePeers.length === 0) {
684
+ return null;
685
+ }
686
+ const sortedEligiblePeers = sortBy(shuffle(eligiblePeers),
687
+ // prefer peers with least active req
688
+ (peerId) => this.activeRequests.get(peerId) ?? 0);
689
+ const bestPeerId = sortedEligiblePeers[0];
690
+ this.onRequest(bestPeerId);
691
+ return this.peersMeta.get(bestPeerId) ?? null;
692
+ }
693
+ /**
694
+ * called from fetchUnavailableBlockInput() where we have either BlockInput or NullBlockInput
695
+ * excludedPeers are the peers that we requested already so we don't want to try again
696
+ */
697
+ bestPeerForBlockInput(unavailableBlockInput, excludedPeers) {
698
+ let cachedData = undefined;
699
+ if (unavailableBlockInput.block === null) {
700
+ // NullBlockInput
701
+ cachedData = unavailableBlockInput.cachedData;
702
+ }
703
+ else {
704
+ // BlockInput
705
+ if (unavailableBlockInput.type !== BlockInputType.dataPromise) {
706
+ throw Error(`bestPeerForBlockInput called with BlockInput type ${unavailableBlockInput.type}, expected dataPromise`);
707
+ }
708
+ cachedData = unavailableBlockInput.cachedData;
709
+ }
710
+ const eligiblePeers = [];
711
+ if (cachedData.fork === ForkName.fulu) {
712
+ // cached data is CachedDataColumns
713
+ const { dataColumnsCache } = cachedData;
714
+ const pendingDataColumns = new Set();
715
+ for (const column of this.custodyConfig.sampledColumns) {
716
+ if (!dataColumnsCache.has(column)) {
717
+ pendingDataColumns.add(column);
718
+ }
719
+ }
720
+ // there could be no pending column in case of NullBlockInput
721
+ eligiblePeers.push(...this.filterPeers(pendingDataColumns, excludedPeers));
722
+ }
723
+ else {
724
+ // prefulu
725
+ const pendingDataColumns = null;
726
+ eligiblePeers.push(...this.filterPeers(pendingDataColumns, excludedPeers));
727
+ }
728
+ if (eligiblePeers.length === 0) {
729
+ return null;
730
+ }
731
+ const sortedEligiblePeers = sortBy(shuffle(eligiblePeers),
732
+ // prefer peers with least active req
733
+ (peerId) => this.activeRequests.get(peerId) ?? 0);
734
+ const bestPeerId = sortedEligiblePeers[0];
735
+ this.onRequest(bestPeerId);
736
+ return this.peersMeta.get(bestPeerId) ?? null;
737
+ }
738
+ /**
739
+ * Consumers don't need to call this method directly, it is called internally by bestPeer*() methods
740
+ * make this public for testing
741
+ */
742
+ onRequest(peerId) {
743
+ this.activeRequests.set(peerId, (this.activeRequests.get(peerId) ?? 0) + 1);
744
+ }
745
+ /**
746
+ * Consumers should call this method when a request is completed for a peer.
747
+ */
748
+ onRequestCompleted(peerId) {
749
+ this.activeRequests.set(peerId, Math.max(0, (this.activeRequests.get(peerId) ?? 1) - 1));
750
+ }
751
+ getTotalActiveRequests() {
752
+ let totalActiveRequests = 0;
753
+ for (const count of this.activeRequests.values()) {
754
+ totalActiveRequests += count;
755
+ }
756
+ return totalActiveRequests;
757
+ }
758
+ // pendingDataColumns could be null for prefulu
759
+ filterPeers(pendingDataColumns, excludedPeers) {
760
+ let maxColumnCount = 0;
761
+ const considerPeers = [];
762
+ for (const [peerId, syncMeta] of this.peersMeta.entries()) {
763
+ if (excludedPeers.has(peerId)) {
764
+ // made request to this peer already
765
+ continue;
766
+ }
767
+ const activeRequests = this.activeRequests.get(peerId) ?? 0;
768
+ if (activeRequests >= MAX_CONCURRENT_REQUESTS) {
769
+ // should return peer with no more than MAX_CONCURRENT_REQUESTS active requests
770
+ continue;
771
+ }
772
+ if (pendingDataColumns === null || pendingDataColumns.size === 0) {
773
+ // prefulu, no pending columns
774
+ considerPeers.push({ peerId, columnCount: 0 });
775
+ continue;
776
+ }
777
+ // postfulu, find peers that have custody columns that we need
778
+ const { custodyGroups: peerColumns } = syncMeta;
779
+ // check if the peer has all needed columns
780
+ // get match
781
+ const columns = peerColumns.reduce((acc, elem) => {
782
+ if (pendingDataColumns.has(elem)) {
783
+ acc.push(elem);
784
+ }
785
+ return acc;
786
+ }, []);
787
+ if (columns.length > 0) {
788
+ if (columns.length > maxColumnCount) {
789
+ maxColumnCount = columns.length;
790
+ }
791
+ considerPeers.push({ peerId, columnCount: columns.length });
792
+ }
793
+ } // end for
794
+ const eligiblePeers = [];
795
+ for (const { peerId, columnCount } of considerPeers) {
796
+ if (columnCount === maxColumnCount) {
797
+ eligiblePeers.push(peerId);
798
+ }
799
+ }
800
+ return eligiblePeers;
801
+ }
542
802
  }
543
803
  //# sourceMappingURL=unknownBlock.js.map