@lodestar/beacon-node 1.45.0-dev.51a1c44b27 → 1.45.0-dev.6568180f96

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 (282) hide show
  1. package/lib/api/impl/beacon/blocks/index.d.ts.map +1 -1
  2. package/lib/api/impl/beacon/blocks/index.js +186 -57
  3. package/lib/api/impl/beacon/blocks/index.js.map +1 -1
  4. package/lib/api/impl/beacon/index.d.ts +1 -1
  5. package/lib/api/impl/beacon/index.d.ts.map +1 -1
  6. package/lib/api/impl/beacon/index.js.map +1 -1
  7. package/lib/api/impl/beacon/pool/index.d.ts.map +1 -1
  8. package/lib/api/impl/beacon/pool/index.js +6 -43
  9. package/lib/api/impl/beacon/pool/index.js.map +1 -1
  10. package/lib/api/impl/beacon/state/index.d.ts +1 -1
  11. package/lib/api/impl/beacon/state/index.d.ts.map +1 -1
  12. package/lib/api/impl/beacon/state/index.js +48 -5
  13. package/lib/api/impl/beacon/state/index.js.map +1 -1
  14. package/lib/api/impl/beacon/state/utils.d.ts +13 -3
  15. package/lib/api/impl/beacon/state/utils.d.ts.map +1 -1
  16. package/lib/api/impl/beacon/state/utils.js +48 -4
  17. package/lib/api/impl/beacon/state/utils.js.map +1 -1
  18. package/lib/api/impl/debug/index.d.ts +1 -1
  19. package/lib/api/impl/debug/index.d.ts.map +1 -1
  20. package/lib/api/impl/debug/index.js +2 -2
  21. package/lib/api/impl/debug/index.js.map +1 -1
  22. package/lib/api/impl/lodestar/index.js +2 -2
  23. package/lib/api/impl/lodestar/index.js.map +1 -1
  24. package/lib/api/impl/proof/index.d.ts +1 -1
  25. package/lib/api/impl/proof/index.d.ts.map +1 -1
  26. package/lib/api/impl/proof/index.js +2 -2
  27. package/lib/api/impl/proof/index.js.map +1 -1
  28. package/lib/api/impl/utils.d.ts +23 -0
  29. package/lib/api/impl/utils.d.ts.map +1 -1
  30. package/lib/api/impl/utils.js +44 -1
  31. package/lib/api/impl/utils.js.map +1 -1
  32. package/lib/api/impl/validator/index.d.ts +0 -14
  33. package/lib/api/impl/validator/index.d.ts.map +1 -1
  34. package/lib/api/impl/validator/index.js +238 -149
  35. package/lib/api/impl/validator/index.js.map +1 -1
  36. package/lib/api/rest/base.d.ts.map +1 -1
  37. package/lib/api/rest/base.js +14 -3
  38. package/lib/api/rest/base.js.map +1 -1
  39. package/lib/chain/blocks/importBlock.d.ts.map +1 -1
  40. package/lib/chain/blocks/importBlock.js +4 -22
  41. package/lib/chain/blocks/importBlock.js.map +1 -1
  42. package/lib/chain/blocks/payloadEnvelopeProcessor.d.ts +5 -2
  43. package/lib/chain/blocks/payloadEnvelopeProcessor.d.ts.map +1 -1
  44. package/lib/chain/blocks/payloadEnvelopeProcessor.js +17 -17
  45. package/lib/chain/blocks/payloadEnvelopeProcessor.js.map +1 -1
  46. package/lib/chain/blocks/utils/chainSegment.d.ts.map +1 -1
  47. package/lib/chain/blocks/utils/chainSegment.js +13 -5
  48. package/lib/chain/blocks/utils/chainSegment.js.map +1 -1
  49. package/lib/chain/blocks/verifyBlocksExecutionPayloads.js +1 -1
  50. package/lib/chain/blocks/verifyBlocksExecutionPayloads.js.map +1 -1
  51. package/lib/chain/blocks/verifyBlocksSanityChecks.js +1 -1
  52. package/lib/chain/blocks/verifyBlocksSanityChecks.js.map +1 -1
  53. package/lib/chain/blocks/verifyExecutionPayloadEnvelope.js +2 -2
  54. package/lib/chain/blocks/verifyExecutionPayloadEnvelope.js.map +1 -1
  55. package/lib/chain/builderCircuitBreaker.d.ts +31 -0
  56. package/lib/chain/builderCircuitBreaker.d.ts.map +1 -0
  57. package/lib/chain/builderCircuitBreaker.js +59 -0
  58. package/lib/chain/builderCircuitBreaker.js.map +1 -0
  59. package/lib/chain/chain.d.ts +3 -2
  60. package/lib/chain/chain.d.ts.map +1 -1
  61. package/lib/chain/chain.js +27 -5
  62. package/lib/chain/chain.js.map +1 -1
  63. package/lib/chain/errors/blockError.d.ts +48 -6
  64. package/lib/chain/errors/blockError.d.ts.map +1 -1
  65. package/lib/chain/errors/blockError.js +25 -4
  66. package/lib/chain/errors/blockError.js.map +1 -1
  67. package/lib/chain/errors/executionPayloadEnvelope.d.ts +11 -0
  68. package/lib/chain/errors/executionPayloadEnvelope.d.ts.map +1 -1
  69. package/lib/chain/errors/executionPayloadEnvelope.js +2 -0
  70. package/lib/chain/errors/executionPayloadEnvelope.js.map +1 -1
  71. package/lib/chain/errors/proposerPreferences.d.ts +14 -1
  72. package/lib/chain/errors/proposerPreferences.d.ts.map +1 -1
  73. package/lib/chain/errors/proposerPreferences.js +2 -0
  74. package/lib/chain/errors/proposerPreferences.js.map +1 -1
  75. package/lib/chain/interface.d.ts +3 -2
  76. package/lib/chain/interface.d.ts.map +1 -1
  77. package/lib/chain/interface.js.map +1 -1
  78. package/lib/chain/lightClient/index.d.ts.map +1 -1
  79. package/lib/chain/lightClient/index.js +11 -6
  80. package/lib/chain/lightClient/index.js.map +1 -1
  81. package/lib/chain/lightClient/proofs.d.ts.map +1 -1
  82. package/lib/chain/lightClient/proofs.js +28 -2
  83. package/lib/chain/lightClient/proofs.js.map +1 -1
  84. package/lib/chain/lightClient/types.d.ts +3 -1
  85. package/lib/chain/lightClient/types.d.ts.map +1 -1
  86. package/lib/chain/opPools/aggregatedAttestationPool.js +1 -1
  87. package/lib/chain/opPools/aggregatedAttestationPool.js.map +1 -1
  88. package/lib/chain/opPools/proposerPreferencesPool.d.ts +3 -2
  89. package/lib/chain/opPools/proposerPreferencesPool.d.ts.map +1 -1
  90. package/lib/chain/opPools/proposerPreferencesPool.js +4 -1
  91. package/lib/chain/opPools/proposerPreferencesPool.js.map +1 -1
  92. package/lib/chain/options.d.ts +5 -0
  93. package/lib/chain/options.d.ts.map +1 -1
  94. package/lib/chain/options.js +1 -0
  95. package/lib/chain/options.js.map +1 -1
  96. package/lib/chain/prepareNextSlot.d.ts.map +1 -1
  97. package/lib/chain/prepareNextSlot.js +12 -7
  98. package/lib/chain/prepareNextSlot.js.map +1 -1
  99. package/lib/chain/produceBlock/produceBlockBody.d.ts.map +1 -1
  100. package/lib/chain/produceBlock/produceBlockBody.js +4 -12
  101. package/lib/chain/produceBlock/produceBlockBody.js.map +1 -1
  102. package/lib/chain/seenCache/index.d.ts +0 -1
  103. package/lib/chain/seenCache/index.d.ts.map +1 -1
  104. package/lib/chain/seenCache/index.js +0 -1
  105. package/lib/chain/seenCache/index.js.map +1 -1
  106. package/lib/chain/stateCache/persistentCheckpointsCache.d.ts.map +1 -1
  107. package/lib/chain/stateCache/persistentCheckpointsCache.js +3 -2
  108. package/lib/chain/stateCache/persistentCheckpointsCache.js.map +1 -1
  109. package/lib/chain/validation/aggregateAndProof.js +10 -6
  110. package/lib/chain/validation/aggregateAndProof.js.map +1 -1
  111. package/lib/chain/validation/attestation.d.ts.map +1 -1
  112. package/lib/chain/validation/attestation.js +10 -6
  113. package/lib/chain/validation/attestation.js.map +1 -1
  114. package/lib/chain/validation/block.d.ts.map +1 -1
  115. package/lib/chain/validation/block.js +53 -4
  116. package/lib/chain/validation/block.js.map +1 -1
  117. package/lib/chain/validation/executionPayloadBid.js +13 -12
  118. package/lib/chain/validation/executionPayloadBid.js.map +1 -1
  119. package/lib/chain/validation/executionPayloadEnvelope.d.ts.map +1 -1
  120. package/lib/chain/validation/executionPayloadEnvelope.js +29 -0
  121. package/lib/chain/validation/executionPayloadEnvelope.js.map +1 -1
  122. package/lib/chain/validation/proposerPreferences.d.ts +9 -2
  123. package/lib/chain/validation/proposerPreferences.d.ts.map +1 -1
  124. package/lib/chain/validation/proposerPreferences.js +75 -14
  125. package/lib/chain/validation/proposerPreferences.js.map +1 -1
  126. package/lib/chain/validation/signatureSets/aggregateAndProof.d.ts.map +1 -1
  127. package/lib/chain/validation/signatureSets/aggregateAndProof.js +2 -3
  128. package/lib/chain/validation/signatureSets/aggregateAndProof.js.map +1 -1
  129. package/lib/db/repositories/lightclientSyncCommitteeWitness.d.ts.map +1 -1
  130. package/lib/db/repositories/lightclientSyncCommitteeWitness.js +42 -7
  131. package/lib/db/repositories/lightclientSyncCommitteeWitness.js.map +1 -1
  132. package/lib/execution/builder/http.d.ts +18 -0
  133. package/lib/execution/builder/http.d.ts.map +1 -1
  134. package/lib/execution/builder/http.js +20 -11
  135. package/lib/execution/builder/http.js.map +1 -1
  136. package/lib/metrics/metrics/beacon.d.ts +6 -0
  137. package/lib/metrics/metrics/beacon.d.ts.map +1 -1
  138. package/lib/metrics/metrics/beacon.js +18 -0
  139. package/lib/metrics/metrics/beacon.js.map +1 -1
  140. package/lib/network/core/metrics.d.ts +65 -0
  141. package/lib/network/core/metrics.d.ts.map +1 -1
  142. package/lib/network/core/metrics.js +94 -0
  143. package/lib/network/core/metrics.js.map +1 -1
  144. package/lib/network/discv5/utils.d.ts.map +1 -1
  145. package/lib/network/discv5/utils.js +12 -3
  146. package/lib/network/discv5/utils.js.map +1 -1
  147. package/lib/network/gossip/encoding.d.ts +2 -2
  148. package/lib/network/gossip/encoding.d.ts.map +1 -1
  149. package/lib/network/gossip/encoding.js +22 -9
  150. package/lib/network/gossip/encoding.js.map +1 -1
  151. package/lib/network/gossip/gossipsub.d.ts.map +1 -1
  152. package/lib/network/gossip/gossipsub.js +4 -3
  153. package/lib/network/gossip/gossipsub.js.map +1 -1
  154. package/lib/network/gossip/topic.d.ts +831 -226
  155. package/lib/network/gossip/topic.d.ts.map +1 -1
  156. package/lib/network/gossip/topic.js +47 -4
  157. package/lib/network/gossip/topic.js.map +1 -1
  158. package/lib/network/interface.d.ts +4 -1
  159. package/lib/network/interface.d.ts.map +1 -1
  160. package/lib/network/network.d.ts +4 -1
  161. package/lib/network/network.d.ts.map +1 -1
  162. package/lib/network/network.js +19 -11
  163. package/lib/network/network.js.map +1 -1
  164. package/lib/network/peers/peerManager.d.ts.map +1 -1
  165. package/lib/network/peers/peerManager.js +10 -0
  166. package/lib/network/peers/peerManager.js.map +1 -1
  167. package/lib/network/peers/score/score.d.ts +3 -1
  168. package/lib/network/peers/score/score.d.ts.map +1 -1
  169. package/lib/network/peers/score/score.js +6 -1
  170. package/lib/network/peers/score/score.js.map +1 -1
  171. package/lib/network/peers/score/store.d.ts.map +1 -1
  172. package/lib/network/peers/score/store.js +6 -1
  173. package/lib/network/peers/score/store.js.map +1 -1
  174. package/lib/network/peers/utils/prioritizePeers.d.ts.map +1 -1
  175. package/lib/network/peers/utils/prioritizePeers.js +14 -0
  176. package/lib/network/peers/utils/prioritizePeers.js.map +1 -1
  177. package/lib/network/processor/gossipHandlers.d.ts.map +1 -1
  178. package/lib/network/processor/gossipHandlers.js +18 -9
  179. package/lib/network/processor/gossipHandlers.js.map +1 -1
  180. package/lib/network/reqresp/handlers/executionPayloadEnvelopesByRange.d.ts.map +1 -1
  181. package/lib/network/reqresp/handlers/executionPayloadEnvelopesByRange.js +4 -2
  182. package/lib/network/reqresp/handlers/executionPayloadEnvelopesByRange.js.map +1 -1
  183. package/lib/network/reqresp/handlers/index.d.ts.map +1 -1
  184. package/lib/network/reqresp/handlers/index.js +20 -11
  185. package/lib/network/reqresp/handlers/index.js.map +1 -1
  186. package/lib/network/util.d.ts +1 -0
  187. package/lib/network/util.d.ts.map +1 -1
  188. package/lib/network/util.js +4 -0
  189. package/lib/network/util.js.map +1 -1
  190. package/lib/sync/range/chain.d.ts +4 -1
  191. package/lib/sync/range/chain.d.ts.map +1 -1
  192. package/lib/sync/range/chain.js +3 -3
  193. package/lib/sync/range/chain.js.map +1 -1
  194. package/lib/sync/range/range.d.ts.map +1 -1
  195. package/lib/sync/range/range.js +10 -0
  196. package/lib/sync/range/range.js.map +1 -1
  197. package/lib/sync/sync.d.ts.map +1 -1
  198. package/lib/sync/sync.js +7 -0
  199. package/lib/sync/sync.js.map +1 -1
  200. package/lib/sync/unknownBlock.d.ts.map +1 -1
  201. package/lib/sync/unknownBlock.js +7 -2
  202. package/lib/sync/unknownBlock.js.map +1 -1
  203. package/lib/sync/utils/downloadByRange.d.ts.map +1 -1
  204. package/lib/sync/utils/downloadByRange.js +6 -0
  205. package/lib/sync/utils/downloadByRange.js.map +1 -1
  206. package/lib/util/execution.js +1 -1
  207. package/lib/util/execution.js.map +1 -1
  208. package/lib/util/graffiti.d.ts +15 -0
  209. package/lib/util/graffiti.d.ts.map +1 -1
  210. package/lib/util/graffiti.js +55 -0
  211. package/lib/util/graffiti.js.map +1 -1
  212. package/package.json +23 -21
  213. package/src/api/impl/beacon/blocks/index.ts +221 -63
  214. package/src/api/impl/beacon/index.ts +1 -1
  215. package/src/api/impl/beacon/pool/index.ts +5 -53
  216. package/src/api/impl/beacon/state/index.ts +55 -3
  217. package/src/api/impl/beacon/state/utils.ts +68 -4
  218. package/src/api/impl/debug/index.ts +3 -2
  219. package/src/api/impl/lodestar/index.ts +2 -2
  220. package/src/api/impl/proof/index.ts +2 -2
  221. package/src/api/impl/utils.ts +51 -1
  222. package/src/api/impl/validator/index.ts +278 -162
  223. package/src/api/rest/base.ts +17 -3
  224. package/src/chain/blocks/importBlock.ts +2 -24
  225. package/src/chain/blocks/payloadEnvelopeProcessor.ts +17 -17
  226. package/src/chain/blocks/utils/chainSegment.ts +16 -5
  227. package/src/chain/blocks/verifyBlocksExecutionPayloads.ts +1 -1
  228. package/src/chain/blocks/verifyBlocksSanityChecks.ts +1 -1
  229. package/src/chain/blocks/verifyExecutionPayloadEnvelope.ts +2 -2
  230. package/src/chain/builderCircuitBreaker.ts +84 -0
  231. package/src/chain/chain.ts +38 -5
  232. package/src/chain/errors/blockError.ts +44 -7
  233. package/src/chain/errors/executionPayloadEnvelope.ts +9 -0
  234. package/src/chain/errors/proposerPreferences.ts +16 -1
  235. package/src/chain/interface.ts +2 -2
  236. package/src/chain/lightClient/index.ts +12 -7
  237. package/src/chain/lightClient/proofs.ts +36 -1
  238. package/src/chain/lightClient/types.ts +3 -1
  239. package/src/chain/opPools/aggregatedAttestationPool.ts +1 -1
  240. package/src/chain/opPools/proposerPreferencesPool.ts +6 -2
  241. package/src/chain/options.ts +6 -0
  242. package/src/chain/prepareNextSlot.ts +11 -7
  243. package/src/chain/produceBlock/produceBlockBody.ts +9 -17
  244. package/src/chain/seenCache/index.ts +0 -1
  245. package/src/chain/stateCache/persistentCheckpointsCache.ts +3 -2
  246. package/src/chain/validation/aggregateAndProof.ts +12 -6
  247. package/src/chain/validation/attestation.ts +12 -6
  248. package/src/chain/validation/block.ts +69 -4
  249. package/src/chain/validation/executionPayloadBid.ts +16 -11
  250. package/src/chain/validation/executionPayloadEnvelope.ts +37 -0
  251. package/src/chain/validation/proposerPreferences.ts +86 -14
  252. package/src/chain/validation/signatureSets/aggregateAndProof.ts +3 -3
  253. package/src/db/repositories/lightclientSyncCommitteeWitness.ts +57 -9
  254. package/src/execution/builder/http.ts +30 -17
  255. package/src/metrics/metrics/beacon.ts +19 -0
  256. package/src/network/core/metrics.ts +94 -0
  257. package/src/network/discv5/utils.ts +11 -3
  258. package/src/network/gossip/encoding.ts +20 -7
  259. package/src/network/gossip/gossipsub.ts +9 -3
  260. package/src/network/gossip/topic.ts +58 -4
  261. package/src/network/interface.ts +3 -1
  262. package/src/network/network.ts +24 -15
  263. package/src/network/peers/peerManager.ts +11 -0
  264. package/src/network/peers/score/score.ts +8 -1
  265. package/src/network/peers/score/store.ts +8 -1
  266. package/src/network/peers/utils/prioritizePeers.ts +15 -0
  267. package/src/network/processor/gossipHandlers.ts +18 -8
  268. package/src/network/reqresp/handlers/executionPayloadEnvelopesByRange.ts +4 -2
  269. package/src/network/reqresp/handlers/index.ts +21 -12
  270. package/src/network/util.ts +5 -0
  271. package/src/sync/range/chain.ts +4 -5
  272. package/src/sync/range/range.ts +10 -0
  273. package/src/sync/sync.ts +10 -0
  274. package/src/sync/unknownBlock.ts +8 -2
  275. package/src/sync/utils/downloadByRange.ts +6 -0
  276. package/src/util/execution.ts +1 -1
  277. package/src/util/graffiti.ts +78 -0
  278. package/lib/chain/seenCache/seenProposerPreferences.d.ts +0 -16
  279. package/lib/chain/seenCache/seenProposerPreferences.d.ts.map +0 -1
  280. package/lib/chain/seenCache/seenProposerPreferences.js +0 -26
  281. package/lib/chain/seenCache/seenProposerPreferences.js.map +0 -1
  282. package/src/chain/seenCache/seenProposerPreferences.ts +0 -32
@@ -1,12 +1,18 @@
1
+ import {type CompositeTypeAny} from "@chainsafe/ssz";
1
2
  import {ForkDigestContext} from "@lodestar/config";
2
3
  import {
3
4
  ATTESTATION_SUBNET_COUNT,
4
5
  ForkName,
5
6
  ForkSeq,
7
+ MAX_ATTESTER_SLASHING_SIZE,
8
+ MAX_DATA_COLUMN_SIDECAR_SIZE,
9
+ MAX_SIGNED_AGGREGATE_AND_PROOF_SIZE,
10
+ MAX_SIGNED_EXECUTION_PAYLOAD_BID_SIZE,
6
11
  SYNC_COMMITTEE_SUBNET_COUNT,
7
12
  isForkPostAltair,
8
13
  isForkPostElectra,
9
14
  isForkPostFulu,
15
+ isForkPostGloas,
10
16
  } from "@lodestar/params";
11
17
  import {Attestation, SingleAttestation, ssz, sszTypesFor} from "@lodestar/types";
12
18
  import {GossipAction, GossipActionError, GossipErrorCode} from "../../chain/errors/gossipValidation.js";
@@ -130,6 +136,30 @@ export function getGossipSSZType(topic: GossipTopic) {
130
136
  }
131
137
  }
132
138
 
139
+ /**
140
+ * Return the maximum uncompressed SSZ byte length accepted for a gossip object.
141
+ */
142
+ export function getGossipSSZMaxSize(topic: GossipTopic, maxPayloadSize: number, sszType?: CompositeTypeAny): number {
143
+ const {fork} = topic.boundary;
144
+ // Gloas progressive containers have broad theoretical SSZ max sizes; use the preset p2p bounds instead.
145
+ switch (topic.type) {
146
+ case GossipType.beacon_block:
147
+ return maxPayloadSize;
148
+ case GossipType.beacon_aggregate_and_proof:
149
+ return isForkPostGloas(fork) ? MAX_SIGNED_AGGREGATE_AND_PROOF_SIZE : (sszType ?? getGossipSSZType(topic)).maxSize;
150
+ case GossipType.attester_slashing:
151
+ return isForkPostGloas(fork) ? MAX_ATTESTER_SLASHING_SIZE : (sszType ?? getGossipSSZType(topic)).maxSize;
152
+ case GossipType.data_column_sidecar:
153
+ return isForkPostGloas(fork) ? MAX_DATA_COLUMN_SIDECAR_SIZE : (sszType ?? getGossipSSZType(topic)).maxSize;
154
+ case GossipType.execution_payload:
155
+ return maxPayloadSize;
156
+ case GossipType.execution_payload_bid:
157
+ return MAX_SIGNED_EXECUTION_PAYLOAD_BID_SIZE;
158
+ default:
159
+ return (sszType ?? getGossipSSZType(topic)).maxSize;
160
+ }
161
+ }
162
+
133
163
  /**
134
164
  * Deserialize a gossip serialized data into an ssz object.
135
165
  */
@@ -246,7 +276,7 @@ export function parseGossipTopic(forkDigestContext: ForkDigestContext, topicStr:
246
276
  export function getCoreTopicsAtFork(
247
277
  networkConfig: NetworkConfig,
248
278
  fork: ForkName,
249
- opts: {subscribeAllSubnets?: boolean; disableLightClientServer?: boolean}
279
+ opts: {subscribeAllSubnets?: boolean; subscribeAllColumnSubnets?: boolean; disableLightClientServer?: boolean}
250
280
  ): GossipTopicTypeMap[keyof GossipTopicTypeMap][] {
251
281
  // Common topics for all forks
252
282
  const topics: GossipTopicTypeMap[keyof GossipTopicTypeMap][] = [
@@ -266,7 +296,7 @@ export function getCoreTopicsAtFork(
266
296
 
267
297
  // After fulu also track data_column_sidecar_{index}
268
298
  if (ForkSeq[fork] >= ForkSeq.fulu) {
269
- topics.push(...getDataColumnSidecarTopics(networkConfig));
299
+ topics.push(...getDataColumnSidecarTopics(networkConfig, opts.subscribeAllColumnSubnets));
270
300
  }
271
301
 
272
302
  // After Deneb and before Fulu also track blob_sidecar_{subnet_id}
@@ -309,15 +339,39 @@ export function getCoreTopicsAtFork(
309
339
  return topics;
310
340
  }
311
341
 
342
+ /**
343
+ * Build the complete set of valid gossip topic strings across every fork boundary.
344
+ */
345
+ export function getAllowedTopics(networkConfig: NetworkConfig): Set<string> {
346
+ const {config} = networkConfig;
347
+ const allowedTopics = new Set<string>();
348
+
349
+ for (const boundary of config.forkBoundariesAscendingEpochOrder) {
350
+ const topics = getCoreTopicsAtFork(networkConfig, boundary.fork, {
351
+ subscribeAllSubnets: true,
352
+ subscribeAllColumnSubnets: true,
353
+ disableLightClientServer: false,
354
+ });
355
+ for (const topic of topics) {
356
+ allowedTopics.add(stringifyGossipTopic(config, {...topic, boundary}));
357
+ }
358
+ }
359
+
360
+ return allowedTopics;
361
+ }
362
+
312
363
  /**
313
364
  * Pick data column subnets to subscribe to post-fulu.
314
365
  */
315
366
  export function getDataColumnSidecarTopics(
316
- networkConfig: NetworkConfig
367
+ networkConfig: NetworkConfig,
368
+ subscribeAllColumnSubnets = false
317
369
  ): GossipTopicTypeMap[keyof GossipTopicTypeMap][] {
318
370
  const topics: GossipTopicTypeMap[keyof GossipTopicTypeMap][] = [];
319
371
 
320
- const subnets = networkConfig.custodyConfig.sampledSubnets;
372
+ const subnets = subscribeAllColumnSubnets
373
+ ? Array.from({length: networkConfig.config.DATA_COLUMN_SIDECAR_SUBNET_COUNT}, (_, i) => i)
374
+ : networkConfig.custodyConfig.sampledSubnets;
321
375
  for (const subnet of subnets) {
322
376
  topics.push({type: GossipType.data_column_sidecar, subnet});
323
377
  }
@@ -103,7 +103,9 @@ export interface INetwork extends INetworkCorePublic {
103
103
  publishBlobSidecar(blobSidecar: deneb.BlobSidecar): Promise<number>;
104
104
  publishBeaconAggregateAndProof(aggregateAndProof: SignedAggregateAndProof): Promise<number>;
105
105
  publishBeaconAttestation(attestation: SingleAttestation, subnet: SubnetID): Promise<number>;
106
- publishDataColumnSidecar(dataColumnSideCar: DataColumnSidecar): Promise<number>;
106
+ publishDataColumnSidecar(
107
+ dataColumnSideCar: DataColumnSidecar
108
+ ): Promise<{sentPeers: number; alreadyPublished: boolean}>;
107
109
  publishVoluntaryExit(voluntaryExit: phase0.SignedVoluntaryExit): Promise<number>;
108
110
  publishBlsToExecutionChange(blsToExecutionChange: capella.SignedBLSToExecutionChange): Promise<number>;
109
111
  publishProposerSlashing(proposerSlashing: phase0.ProposerSlashing): Promise<number>;
@@ -65,7 +65,7 @@ import {
65
65
  } from "./reqresp/utils/collect.js";
66
66
  import {collectSequentialBlocksInRange} from "./reqresp/utils/collectSequentialBlocksInRange.js";
67
67
  import {CommitteeSubscription} from "./subnets/index.js";
68
- import {isPublishToZeroPeersError, prettyPrintPeerIdStr} from "./util.js";
68
+ import {isPublishDuplicateError, isPublishToZeroPeersError, prettyPrintPeerIdStr} from "./util.js";
69
69
 
70
70
  type NetworkModules = {
71
71
  opts: NetworkOptions;
@@ -366,7 +366,9 @@ export class Network implements INetwork {
366
366
  });
367
367
  }
368
368
 
369
- async publishDataColumnSidecar(dataColumnSidecar: DataColumnSidecar): Promise<number> {
369
+ async publishDataColumnSidecar(
370
+ dataColumnSidecar: DataColumnSidecar
371
+ ): Promise<{sentPeers: number; alreadyPublished: boolean}> {
370
372
  const slot = isGloasDataColumnSidecar(dataColumnSidecar)
371
373
  ? dataColumnSidecar.slot
372
374
  : dataColumnSidecar.signedBlockHeader.message.slot;
@@ -374,18 +376,25 @@ export class Network implements INetwork {
374
376
  const boundary = this.config.getForkBoundaryAtEpoch(epoch);
375
377
 
376
378
  const subnet = computeSubnetForDataColumnSidecar(this.config, dataColumnSidecar);
377
- return this.publishGossip<GossipType.data_column_sidecar>(
378
- {type: GossipType.data_column_sidecar, boundary, subnet},
379
- dataColumnSidecar,
380
- {
381
- ignoreDuplicatePublishError: true,
382
- // we ensure having all topic peers via prioritizePeers() function
383
- // in the worse case, if there is 0 peer on the topic, the overall publish operation could be still a success
384
- // because supernode will rebuild and publish missing data column sidecars for us
385
- // hence we want to track sent peers as 0 instead of an error
386
- allowPublishToZeroTopicPeers: true,
379
+ try {
380
+ const sentPeers = await this.publishGossip<GossipType.data_column_sidecar>(
381
+ {type: GossipType.data_column_sidecar, boundary, subnet},
382
+ dataColumnSidecar,
383
+ {
384
+ // we ensure having all topic peers via prioritizePeers() function
385
+ // in the worse case, if there is 0 peer on the topic, the overall publish operation could be still a success
386
+ // because supernode will rebuild and publish missing data column sidecars for us
387
+ // hence we want to track sent peers as 0 instead of an error
388
+ allowPublishToZeroTopicPeers: true,
389
+ }
390
+ );
391
+ return {sentPeers, alreadyPublished: false};
392
+ } catch (e) {
393
+ if (isPublishDuplicateError(e as Error)) {
394
+ return {sentPeers: 0, alreadyPublished: true};
387
395
  }
388
- );
396
+ throw e;
397
+ }
389
398
  }
390
399
 
391
400
  async publishBeaconAggregateAndProof(aggregateAndProof: SignedAggregateAndProof): Promise<number> {
@@ -860,8 +869,8 @@ export class Network implements INetwork {
860
869
  this.core.setTargetGroupCount(count);
861
870
  };
862
871
 
863
- private onPublishDataColumns = (sidecars: DataColumnSidecar[]): Promise<number[]> => {
864
- return promiseAllMaybeAsync(sidecars.map((sidecar) => () => this.publishDataColumnSidecar(sidecar)));
872
+ private onPublishDataColumns = async (sidecars: DataColumnSidecar[]): Promise<void> => {
873
+ await promiseAllMaybeAsync(sidecars.map((sidecar) => () => this.publishDataColumnSidecar(sidecar)));
865
874
  };
866
875
 
867
876
  private onPublishBlobSidecars = (sidecars: deneb.BlobSidecar[]): Promise<number[]> => {
@@ -415,8 +415,10 @@ export class PeerManager {
415
415
  );
416
416
  if (irrelevantReasonType === null) {
417
417
  isIrrelevant = false;
418
+ this.metrics?.peerManager.relevanceCheck.inc({result: "relevant"});
418
419
  } else {
419
420
  isIrrelevant = true;
421
+ this.metrics?.peerManager.relevanceCheck.inc({result: irrelevantReasonType.code});
420
422
  this.logger.debug("Irrelevant peer", {
421
423
  peer: prettyPrintPeerId(peer),
422
424
  reason: renderIrrelevantPeerType(irrelevantReasonType),
@@ -424,6 +426,7 @@ export class PeerManager {
424
426
  }
425
427
  } catch (e) {
426
428
  this.logger.error("Irrelevant peer - unexpected error", {peer: prettyPrintPeerId(peer)}, e as Error);
429
+ this.metrics?.peerManager.relevanceCheck.inc({result: "error"});
427
430
  isIrrelevant = true;
428
431
  }
429
432
 
@@ -552,15 +555,18 @@ export class PeerManager {
552
555
  for (const peer of connectedPeers) {
553
556
  switch (this.peerRpcScores.getScoreState(peer)) {
554
557
  case ScoreState.Banned:
558
+ this.metrics?.peerManager.peersPruned.inc({reason: "banned"});
555
559
  void this.goodbyeAndDisconnect(peer, GoodByeReasonCode.BANNED);
556
560
  break;
557
561
  case ScoreState.Disconnected:
562
+ this.metrics?.peerManager.peersPruned.inc({reason: "score_too_low"});
558
563
  void this.goodbyeAndDisconnect(peer, GoodByeReasonCode.SCORE_TOO_LOW);
559
564
  break;
560
565
  case ScoreState.Healthy:
561
566
  connectedHealthyPeers.push(peer);
562
567
  }
563
568
  }
569
+ this.metrics?.peerManager.peersEvaluated.observe(connectedHealthyPeers.length);
564
570
 
565
571
  const status = this.statusCache.get();
566
572
  const starved =
@@ -572,6 +578,7 @@ export class PeerManager {
572
578
  this.metrics?.peerManager.starved.set(starved ? 1 : 0);
573
579
  const forkSeq = this.config.getForkSeq(this.clock.currentSlot);
574
580
 
581
+ const prioritizeTimer = this.metrics?.peerManager.prioritizePeersDuration.startTimer();
575
582
  const {peersToDisconnect, peersToConnect, attnetQueries, syncnetQueries, custodyGroupQueries} = prioritizePeers(
576
583
  connectedHealthyPeers.map((peer) => {
577
584
  const peerData = this.connectedPeers.get(peer.toString());
@@ -601,6 +608,7 @@ export class PeerManager {
601
608
  this.config,
602
609
  this.metrics
603
610
  );
611
+ prioritizeTimer?.();
604
612
 
605
613
  const queriesMerged: SubnetDiscvQueryMs[] = [];
606
614
  for (const {type, queries} of [
@@ -632,6 +640,7 @@ export class PeerManager {
632
640
  // disconnect first to have more slots before we dial new peers
633
641
  for (const [reason, peers] of peersToDisconnect) {
634
642
  this.metrics?.peersRequestedToDisconnect.inc({reason}, peers.length);
643
+ this.metrics?.peerManager.peersPruned.inc({reason}, peers.length);
635
644
  for (const peer of peers) {
636
645
  void this.goodbyeAndDisconnect(peer, GoodByeReasonCode.TOO_MANY_PEERS);
637
646
  }
@@ -659,6 +668,8 @@ export class PeerManager {
659
668
  }
660
669
  }
661
670
 
671
+ this.metrics?.peerManager.connectedPeersMapSize.set(this.connectedPeers.size);
672
+
662
673
  timer?.();
663
674
 
664
675
  this.logger.debug("peerManager heartbeat result", {
@@ -1,4 +1,5 @@
1
1
  import {GoodByeReasonCode} from "../../../constants/network.js";
2
+ import {NetworkCoreMetrics} from "../../core/metrics.js";
2
3
  import {
3
4
  COOL_DOWN_BEFORE_DECAY_MS,
4
5
  DEFAULT_SCORE,
@@ -23,13 +24,15 @@ export class RealScore implements IPeerScore {
23
24
  /** The final score, computed from the above */
24
25
  private score: number;
25
26
  private lastUpdate: number;
27
+ private readonly metrics: NetworkCoreMetrics | null;
26
28
 
27
- constructor() {
29
+ constructor(metrics: NetworkCoreMetrics | null = null) {
28
30
  this.lodestarScore = DEFAULT_SCORE;
29
31
  this.gossipScore = DEFAULT_SCORE;
30
32
  this.score = DEFAULT_SCORE;
31
33
  this.ignoreNegativeGossipScore = false;
32
34
  this.lastUpdate = Date.now();
35
+ this.metrics = metrics;
33
36
  }
34
37
 
35
38
  isCoolingDown(): boolean {
@@ -136,6 +139,10 @@ export class RealScore implements IPeerScore {
136
139
  this.recomputeScore();
137
140
  const newState = scoreToState(this.score);
138
141
 
142
+ if (prevState !== newState) {
143
+ this.metrics?.peerManager.scoreStateTransitions.inc({from: prevState, to: newState});
144
+ }
145
+
139
146
  if (prevState !== ScoreState.Banned && newState === ScoreState.Banned) {
140
147
  // ban this peer for at least BANNED_BEFORE_DECAY_MS seconds
141
148
  this.lastUpdate = Date.now() + COOL_DOWN_BEFORE_DECAY_MS;
@@ -32,7 +32,9 @@ export class PeerRpcScoreStore implements IPeerRpcScoreStore {
32
32
  constructor(opts: PeerRpcScoreOpts = {}, metrics: NetworkCoreMetrics | null = null, logger: Logger | null = null) {
33
33
  this.metrics = metrics;
34
34
  this.logger = logger;
35
- this.scores = opts.disablePeerScoring ? new MapDef(() => new MaxScore()) : new MapDef(() => new RealScore());
35
+ this.scores = opts.disablePeerScoring
36
+ ? new MapDef(() => new MaxScore())
37
+ : new MapDef(() => new RealScore(this.metrics));
36
38
  }
37
39
 
38
40
  getScore(peer: PeerId): number {
@@ -75,6 +77,8 @@ export class PeerRpcScoreStore implements IPeerRpcScoreStore {
75
77
  }
76
78
 
77
79
  update(): void {
80
+ const timer = this.metrics?.peerManager.scoreUpdateDuration.startTimer();
81
+
78
82
  // Bound size of data structures
79
83
  pruneSetToMax(this.scores, MAX_ENTRIES);
80
84
 
@@ -86,6 +90,9 @@ export class PeerRpcScoreStore implements IPeerRpcScoreStore {
86
90
  this.scores.delete(peerIdStr);
87
91
  }
88
92
  }
93
+
94
+ this.metrics?.peerManager.scoreMapSize.set(this.scores.size);
95
+ timer?.();
89
96
  }
90
97
 
91
98
  updateGossipsubScore(peerId: PeerIdStr, newScore: number, ignore: boolean): void {
@@ -7,6 +7,7 @@ import {MapDef} from "@lodestar/utils";
7
7
  import {shuffle} from "../../../util/shuffle.js";
8
8
  import {sortBy} from "../../../util/sortBy.js";
9
9
  import {NetworkCoreMetrics} from "../../core/metrics.js";
10
+ import {SubnetType} from "../../metadata.js";
10
11
  import {RequestedSubnet} from "./subnetMap.js";
11
12
 
12
13
  /** Target number of peers we'd like to have connected to a given long-lived subnet */
@@ -189,6 +190,18 @@ export function prioritizePeers(
189
190
 
190
191
  const connectedPeerCount = connectedPeers.length;
191
192
 
193
+ // Track the outbound peers invariant (we keep at least OUTBOUND_PEERS_RATIO outbound peers).
194
+ // Report 0 when there are no peers so the gauge doesn't go stale at its last value.
195
+ if (metrics) {
196
+ let outboundPeerCount = 0;
197
+ for (const peer of connectedPeers) {
198
+ if (peer.direction === "outbound") {
199
+ outboundPeerCount++;
200
+ }
201
+ }
202
+ metrics.peerManager.outboundPeersRatio.set(connectedPeerCount > 0 ? outboundPeerCount / connectedPeerCount : 0);
203
+ }
204
+
192
205
  if (connectedPeerCount < targetPeers) {
193
206
  // Need more peers.
194
207
  // Instead of attempting to connect the exact amount necessary this will overshoot a little since the success
@@ -256,6 +269,7 @@ function requestSubnetPeers(
256
269
 
257
270
  for (const {subnet, toSlot} of activeAttnets) {
258
271
  const peersInSubnet = peersPerSubnet.get(subnet) ?? 0;
272
+ metrics?.peerManager.peersPerActiveSubnet.observe({type: SubnetType.attnets}, peersInSubnet);
259
273
  if (peersInSubnet < targetSubnetPeers) {
260
274
  // We need more peers
261
275
  attnetQueries.push({subnet, toSlot, maxPeersToDiscover: targetSubnetPeers - peersInSubnet});
@@ -282,6 +296,7 @@ function requestSubnetPeers(
282
296
 
283
297
  for (const {subnet, toSlot} of activeSyncnets) {
284
298
  const peersInSubnet = peersPerSubnet.get(subnet) ?? 0;
299
+ metrics?.peerManager.peersPerActiveSubnet.observe({type: SubnetType.syncnets}, peersInSubnet);
285
300
  if (peersInSubnet < targetSubnetPeers) {
286
301
  // We need more peers
287
302
  syncnetQueries.push({subnet, toSlot, maxPeersToDiscover: targetSubnetPeers - peersInSubnet});
@@ -176,7 +176,7 @@ function getSequentialHandlers(modules: ValidatorFnsModules, options: GossipHand
176
176
 
177
177
  // optimistically add gossip block to the seen cache
178
178
  // if validation fails, we will NOT forward this gossip block to peers
179
- // - if PARENT_UNKNOWN error, blockInput will then be queued inside BlockInputSync. If the gossip block is really invalid, it will be pruned there
179
+ // - if PARENT_BLOCK_UNKNOWN error, blockInput will then be queued inside BlockInputSync. If the gossip block is really invalid, it will be pruned there
180
180
  // - if other validator errors, blockInput will stay in the seen cache and will be pruned on finalization
181
181
  const blockInput = chain.seenBlockInputCache.getByBlock({
182
182
  block: signedBlock,
@@ -187,7 +187,7 @@ function getSequentialHandlers(modules: ValidatorFnsModules, options: GossipHand
187
187
  });
188
188
 
189
189
  // Optimistically seed the payload-envelope cache too, mirroring seenBlockInputCache above.
190
- // This ensures we have PayloadEnvelopeInput, even through "PARENT_UNKNOWN" error
190
+ // This ensures we have PayloadEnvelopeInput, even through "PARENT_BLOCK_UNKNOWN" error
191
191
  // see https://github.com/ChainSafe/lodestar/issues/9475
192
192
  if (isForkPostGloas(fork)) {
193
193
  chain.seenPayloadEnvelopeInputCache.add({
@@ -227,7 +227,8 @@ function getSequentialHandlers(modules: ValidatorFnsModules, options: GossipHand
227
227
  if (e instanceof BlockGossipError) {
228
228
  logger.debug("Gossip block has error", {slot, root: blockShortHex, code: e.type.code});
229
229
  if (
230
- (e.type.code === BlockErrorCode.PARENT_UNKNOWN || e.type.code === BlockErrorCode.PARENT_PAYLOAD_UNKNOWN) &&
230
+ (e.type.code === BlockErrorCode.PARENT_BLOCK_UNKNOWN ||
231
+ e.type.code === BlockErrorCode.PARENT_PAYLOAD_UNKNOWN) &&
231
232
  blockInput
232
233
  ) {
233
234
  chain.emitter.emit(ChainEvent.blockUnknownParent, {
@@ -661,14 +662,19 @@ function getSequentialHandlers(modules: ValidatorFnsModules, options: GossipHand
661
662
  break;
662
663
  }
663
664
  // ALREADY_KNOWN should not happen with ignoreIfKnown=true above
664
- // PARENT_UNKNOWN should not happen, we handled this in validateBeaconBlock() function above
665
+ // PARENT_BLOCK_UNKNOWN should not happen, we handled this in validateBeaconBlock() function above
665
666
  case BlockErrorCode.ALREADY_KNOWN:
666
- case BlockErrorCode.PARENT_UNKNOWN:
667
+ case BlockErrorCode.PARENT_BLOCK_UNKNOWN:
667
668
  case BlockErrorCode.PRESTATE_MISSING:
668
669
  case BlockErrorCode.EXECUTION_ENGINE_ERROR:
669
- // Errors might indicate an issue with our node or the connected EL client
670
+ // Errors might indicate an issue with our node or the connected EL client.
670
671
  logLevel = LogLevel.error;
671
672
  break;
673
+ case BlockErrorCode.EXECUTION_ENGINE_INVALID:
674
+ // the peer served a bad block
675
+ core.reportPeer(peerIdStr, PeerAction.LowToleranceError, "ExecutionEngineInvalid");
676
+ logLevel = LogLevel.warn;
677
+ break;
672
678
  default:
673
679
  // TODO: Should it use PeerId or string?
674
680
  core.reportPeer(peerIdStr, PeerAction.LowToleranceError, "BadGossipBlock");
@@ -1241,6 +1247,11 @@ function getSequentialHandlers(modules: ValidatorFnsModules, options: GossipHand
1241
1247
  payloadAttestationMessage.data.payloadPresent,
1242
1248
  payloadAttestationMessage.data.blobDataAvailable
1243
1249
  );
1250
+
1251
+ chain.emitter.emit(routes.events.EventType.payloadAttestationMessage, {
1252
+ version: config.getForkName(payloadAttestationMessage.data.slot),
1253
+ data: payloadAttestationMessage,
1254
+ });
1244
1255
  },
1245
1256
  [GossipType.execution_payload_bid]: async ({
1246
1257
  gossipData,
@@ -1273,9 +1284,8 @@ function getSequentialHandlers(modules: ValidatorFnsModules, options: GossipHand
1273
1284
  const signedProposerPreferences = sszDeserialize(topic, serializedData);
1274
1285
  await validateGossipProposerPreferences(chain, signedProposerPreferences);
1275
1286
 
1276
- chain.proposerPreferencesPool.add(signedProposerPreferences);
1277
1287
  chain.emitter.emit(routes.events.EventType.proposerPreferences, {
1278
- version: ForkName.gloas,
1288
+ version: config.getForkName(signedProposerPreferences.message.proposalSlot),
1279
1289
  data: signedProposerPreferences,
1280
1290
  });
1281
1291
  },
@@ -107,8 +107,10 @@ export function validateExecutionPayloadEnvelopesByRangeRequest(
107
107
  // The gloas req/resp spec uses MIN_EPOCHS_FOR_BLOCK_REQUESTS to define the minimum range peers MUST serve.
108
108
  // Archival nodes may still serve older retained payloads to allow genesis sync.
109
109
 
110
- if (count > config.MAX_REQUEST_BLOCKS_DENEB) {
111
- count = config.MAX_REQUEST_BLOCKS_DENEB;
110
+ // Spec: EnvelopesByRange response is bounded by MAX_REQUEST_PAYLOADS (consensus-specs #5383),
111
+ // distinct from the MAX_REQUEST_BLOCKS_DENEB cap used for block-by-range.
112
+ if (count > config.MAX_REQUEST_PAYLOADS) {
113
+ count = config.MAX_REQUEST_PAYLOADS;
112
114
  }
113
115
 
114
116
  return {startSlot, count};
@@ -1,4 +1,5 @@
1
- import {ProtocolHandler} from "@lodestar/reqresp";
1
+ import type {Type} from "@chainsafe/ssz";
2
+ import {ProtocolHandler, RespStatus, ResponseError} from "@lodestar/reqresp";
2
3
  import {ssz} from "@lodestar/types";
3
4
  import {IBeaconChain} from "../../../chain/index.js";
4
5
  import {IBeaconDb} from "../../../db/index.js";
@@ -29,6 +30,14 @@ function notImplemented(method: ReqRespMethod): ProtocolHandler {
29
30
  };
30
31
  }
31
32
 
33
+ function deserializeRequestBody<T>(type: Type<T>, data: Uint8Array): T {
34
+ try {
35
+ return type.deserialize(data);
36
+ } catch (e) {
37
+ throw new ResponseError(RespStatus.INVALID_REQUEST, e instanceof Error ? e.message : String(e));
38
+ }
39
+ }
40
+
32
41
  /**
33
42
  * The ReqRespHandler module handles app-level requests / responses from other peers,
34
43
  * fetching state from the chain and database as needed.
@@ -40,51 +49,51 @@ export function getReqRespHandlers({db, chain}: {db: IBeaconDb; chain: IBeaconCh
40
49
  [ReqRespMethod.Ping]: notImplemented(ReqRespMethod.Ping),
41
50
  [ReqRespMethod.Metadata]: notImplemented(ReqRespMethod.Metadata),
42
51
  [ReqRespMethod.BeaconBlocksByRange]: (req, peerId, peerClient) => {
43
- const body = ssz.phase0.BeaconBlocksByRangeRequest.deserialize(req.data);
52
+ const body = deserializeRequestBody(ssz.phase0.BeaconBlocksByRangeRequest, req.data);
44
53
  return onBeaconBlocksByRange(body, chain, db, peerId, peerClient);
45
54
  },
46
55
  [ReqRespMethod.BeaconBlocksByRoot]: (req) => {
47
56
  const fork = chain.config.getForkName(chain.clock.currentSlot);
48
- const body = BeaconBlocksByRootRequestType(fork, chain.config).deserialize(req.data);
57
+ const body = deserializeRequestBody(BeaconBlocksByRootRequestType(fork, chain.config), req.data);
49
58
  return onBeaconBlocksByRoot(body, chain);
50
59
  },
51
60
  [ReqRespMethod.BeaconBlocksByHead]: (req, peerId, peerClient) => {
52
- const body = ssz.fulu.BeaconBlocksByHeadRequest.deserialize(req.data);
61
+ const body = deserializeRequestBody(ssz.fulu.BeaconBlocksByHeadRequest, req.data);
53
62
  return onBeaconBlocksByHead(body, chain, peerId, peerClient);
54
63
  },
55
64
  [ReqRespMethod.BlobSidecarsByRoot]: (req) => {
56
65
  const fork = chain.config.getForkName(chain.clock.currentSlot);
57
- const body = BlobSidecarsByRootRequestType(fork, chain.config).deserialize(req.data);
66
+ const body = deserializeRequestBody(BlobSidecarsByRootRequestType(fork, chain.config), req.data);
58
67
  return onBlobSidecarsByRoot(body, chain);
59
68
  },
60
69
  [ReqRespMethod.BlobSidecarsByRange]: (req) => {
61
- const body = ssz.deneb.BlobSidecarsByRangeRequest.deserialize(req.data);
70
+ const body = deserializeRequestBody(ssz.deneb.BlobSidecarsByRangeRequest, req.data);
62
71
  return onBlobSidecarsByRange(body, chain, db);
63
72
  },
64
73
  [ReqRespMethod.DataColumnSidecarsByRange]: (req, peerId, peerClient) => {
65
- const body = ssz.fulu.DataColumnSidecarsByRangeRequest.deserialize(req.data);
74
+ const body = deserializeRequestBody(ssz.fulu.DataColumnSidecarsByRangeRequest, req.data);
66
75
  return onDataColumnSidecarsByRange(body, chain, db, peerId, peerClient);
67
76
  },
68
77
  [ReqRespMethod.DataColumnSidecarsByRoot]: (req, peerId, peerClient) => {
69
- const body = DataColumnSidecarsByRootRequestType(chain.config).deserialize(req.data);
78
+ const body = deserializeRequestBody(DataColumnSidecarsByRootRequestType(chain.config), req.data);
70
79
  return onDataColumnSidecarsByRoot(body, chain, db, peerId, peerClient);
71
80
  },
72
81
 
73
82
  [ReqRespMethod.ExecutionPayloadEnvelopesByRoot]: (req, peerId, peerClient) => {
74
- const body = ExecutionPayloadEnvelopesByRootRequestType(chain.config).deserialize(req.data);
83
+ const body = deserializeRequestBody(ExecutionPayloadEnvelopesByRootRequestType(chain.config), req.data);
75
84
  return onExecutionPayloadEnvelopesByRoot(body, chain, db, peerId, peerClient);
76
85
  },
77
86
  [ReqRespMethod.ExecutionPayloadEnvelopesByRange]: (req, peerId, peerClient) => {
78
- const body = ssz.gloas.ExecutionPayloadEnvelopesByRangeRequest.deserialize(req.data);
87
+ const body = deserializeRequestBody(ssz.gloas.ExecutionPayloadEnvelopesByRangeRequest, req.data);
79
88
  return onExecutionPayloadEnvelopesByRange(body, chain, db, peerId, peerClient);
80
89
  },
81
90
 
82
91
  [ReqRespMethod.LightClientBootstrap]: (req) => {
83
- const body = ssz.Root.deserialize(req.data);
92
+ const body = deserializeRequestBody(ssz.Root, req.data);
84
93
  return onLightClientBootstrap(body, chain);
85
94
  },
86
95
  [ReqRespMethod.LightClientUpdatesByRange]: (req) => {
87
- const body = ssz.altair.LightClientUpdatesByRange.deserialize(req.data);
96
+ const body = deserializeRequestBody(ssz.altair.LightClientUpdatesByRange, req.data);
88
97
  return onLightClientUpdatesByRange(body, chain);
89
98
  },
90
99
  [ReqRespMethod.LightClientFinalityUpdate]: () => onLightClientFinalityUpdate(chain),
@@ -27,3 +27,8 @@ export function getConnection(libp2p: Libp2p, peerIdStr: string): Connection | u
27
27
  export function isPublishToZeroPeersError(e: Error): boolean {
28
28
  return e.message.includes("PublishError.NoPeersSubscribedToTopic");
29
29
  }
30
+
31
+ // https://github.com/libp2p/js-libp2p/blob/f87cba928991736d9646b3e054c367f55cab315c/packages/gossipsub/src/gossipsub.ts#L2076
32
+ export function isPublishDuplicateError(e: Error): boolean {
33
+ return e.message.includes("PublishError.Duplicate");
34
+ }
@@ -186,10 +186,6 @@ export class SyncChain {
186
186
  this.logger = logger;
187
187
  this.logId = `${syncType}-${nextChainId++}`;
188
188
 
189
- if (metrics) {
190
- metrics.syncRange.headSyncPeers.addCollect(() => this.scrapeMetrics(metrics));
191
- }
192
-
193
189
  // Trigger event on parent class
194
190
  this.sync().then(
195
191
  () => fns.onEnd(null, this.target),
@@ -775,7 +771,10 @@ export class SyncChain {
775
771
  });
776
772
  }
777
773
 
778
- private scrapeMetrics(metrics: Metrics): void {
774
+ /**
775
+ * Called by `RangeSync`'s to avoid collecting metrics of removed chains.
776
+ */
777
+ scrapeMetrics(metrics: Metrics): void {
779
778
  const syncPeersMetric =
780
779
  this.syncType === RangeSyncType.Finalized
781
780
  ? metrics.syncRange.finalizedSyncPeers
@@ -105,6 +105,16 @@ export class RangeSync extends (EventEmitter as {new (): RangeSyncEmitter}) {
105
105
 
106
106
  if (metrics) {
107
107
  metrics.syncStatus.addCollect(() => this.scrapeMetrics(metrics));
108
+ metrics.syncRange.headSyncPeers.addCollect(() => {
109
+ // Gauges retain their last set value, so a removed chain would keep reporting stale
110
+ // per-column peer counts (finalizedSyncPeers especially, as it is rarely recreated once
111
+ // synced)
112
+ metrics.syncRange.headSyncPeers.reset();
113
+ metrics.syncRange.finalizedSyncPeers.reset();
114
+ for (const syncChain of this.chains.values()) {
115
+ syncChain.scrapeMetrics(metrics);
116
+ }
117
+ });
108
118
  }
109
119
  }
110
120
 
package/src/sync/sync.ts CHANGED
@@ -75,6 +75,11 @@ export class BeaconSync implements IBeaconSync {
75
75
  if (metrics) {
76
76
  metrics.syncStatus.addCollect(() => this.scrapeMetrics(metrics));
77
77
  }
78
+
79
+ // A from-scratch or large-gap start boots not synced; updateSyncState resumes once synced
80
+ if (this.state !== SyncState.Synced) {
81
+ this.chain.forkChoice.pauseFastConfirmation();
82
+ }
78
83
  }
79
84
 
80
85
  close(): void {
@@ -228,6 +233,8 @@ export class BeaconSync implements IBeaconSync {
228
233
 
229
234
  // We have become synced, subscribe to all the gossip core topics
230
235
  if (state === SyncState.Synced && this.chain.clock.currentEpoch >= MIN_EPOCH_TO_START_GOSSIP) {
236
+ this.chain.forkChoice.resumeFastConfirmation();
237
+
231
238
  if (!this.network.isSubscribedToGossipCoreTopics()) {
232
239
  this.network
233
240
  .subscribeGossipCoreTopics()
@@ -251,6 +258,9 @@ export class BeaconSync implements IBeaconSync {
251
258
  else if (state !== SyncState.Synced) {
252
259
  const syncDiff = this.chain.clock.currentSlot - this.chain.forkChoice.getHead().slot;
253
260
  if (syncDiff > this.slotImportTolerance * 2) {
261
+ // Same debounce as gossip: transient blips keep the rule running, only a real gap pauses it
262
+ this.chain.forkChoice.pauseFastConfirmation();
263
+
254
264
  if (this.network.isSubscribedToGossipCoreTopics()) {
255
265
  this.logger.warn(`Node sync has fallen behind by ${syncDiff} slots`);
256
266
  this.network
@@ -819,7 +819,7 @@ export class BlockInputSync {
819
819
  // case BlockErrorCode.ALREADY_KNOWN:
820
820
  // case BlockErrorCode.GENESIS_BLOCK:
821
821
 
822
- case BlockErrorCode.PARENT_UNKNOWN:
822
+ case BlockErrorCode.PARENT_BLOCK_UNKNOWN:
823
823
  case BlockErrorCode.PRESTATE_MISSING:
824
824
  // Should not happen, mark as downloaded to try again latter
825
825
  this.logger.debug("Attempted to process block but its parent was still unknown", errorData, res.err);
@@ -841,10 +841,16 @@ export class BlockInputSync {
841
841
 
842
842
  case BlockErrorCode.EXECUTION_ENGINE_ERROR:
843
843
  // Removing the block(s) without penalizing the peers, hoping for EL to
844
- // recover on a latter download + verify attempt
844
+ // recover on a latter download + verify attempt.
845
845
  this.removeAllDescendants(pendingBlock);
846
846
  break;
847
847
 
848
+ case BlockErrorCode.EXECUTION_ENGINE_INVALID:
849
+ // the peer served a bad block
850
+ this.logger.debug("Execution engine rejected block from unknown parent sync", errorData, res.err);
851
+ this.removeAndDownScoreAllDescendants(pendingBlock);
852
+ break;
853
+
848
854
  default:
849
855
  // Block is not correct with respect to our chain. Log error loudly
850
856
  this.logger.debug("Error processing block from unknown parent sync", errorData, res.err);
@@ -981,6 +981,12 @@ export async function validateColumnsByRangeResponse(
981
981
  missingIndices: prettyPrintIndices(request.columns),
982
982
  })
983
983
  );
984
+ // Gloas: the bid commits to blobs even for payloads that get orphaned, whose columns are
985
+ // pruned and unservable. Skip instead of truncating + re-requesting forever; DA is enforced
986
+ // on envelope processing. Pre-Gloas commitments are canonical, so keep strict.
987
+ if (isForkPostGloas(forkName)) {
988
+ continue;
989
+ }
984
990
  break;
985
991
  }
986
992