@lodestar/beacon-node 1.51.0-dev.950d6b9392 → 1.51.0-dev.9998f24b6e

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 (266) hide show
  1. package/lib/api/impl/beacon/blocks/index.d.ts.map +1 -1
  2. package/lib/api/impl/beacon/blocks/index.js +17 -27
  3. package/lib/api/impl/beacon/blocks/index.js.map +1 -1
  4. package/lib/api/impl/beacon/pool/index.d.ts.map +1 -1
  5. package/lib/api/impl/beacon/pool/index.js +5 -2
  6. package/lib/api/impl/beacon/pool/index.js.map +1 -1
  7. package/lib/api/impl/beacon/state/utils.d.ts +1 -1
  8. package/lib/api/impl/beacon/state/utils.d.ts.map +1 -1
  9. package/lib/api/impl/validator/index.d.ts +6 -0
  10. package/lib/api/impl/validator/index.d.ts.map +1 -1
  11. package/lib/api/impl/validator/index.js +42 -26
  12. package/lib/api/impl/validator/index.js.map +1 -1
  13. package/lib/chain/GetBlobsTracker.d.ts +11 -0
  14. package/lib/chain/GetBlobsTracker.d.ts.map +1 -1
  15. package/lib/chain/GetBlobsTracker.js +85 -27
  16. package/lib/chain/GetBlobsTracker.js.map +1 -1
  17. package/lib/chain/archiveStore/archiveStore.d.ts.map +1 -1
  18. package/lib/chain/archiveStore/archiveStore.js +1 -0
  19. package/lib/chain/archiveStore/archiveStore.js.map +1 -1
  20. package/lib/chain/archiveStore/utils/archiveBlocks.d.ts +4 -0
  21. package/lib/chain/archiveStore/utils/archiveBlocks.d.ts.map +1 -1
  22. package/lib/chain/archiveStore/utils/archiveBlocks.js +7 -1
  23. package/lib/chain/archiveStore/utils/archiveBlocks.js.map +1 -1
  24. package/lib/chain/blocks/importExecutionPayload.d.ts +6 -11
  25. package/lib/chain/blocks/importExecutionPayload.d.ts.map +1 -1
  26. package/lib/chain/blocks/importExecutionPayload.js +8 -15
  27. package/lib/chain/blocks/importExecutionPayload.js.map +1 -1
  28. package/lib/chain/blocks/index.js +2 -2
  29. package/lib/chain/blocks/index.js.map +1 -1
  30. package/lib/chain/blocks/payloadEnvelopeProcessor.d.ts +12 -9
  31. package/lib/chain/blocks/payloadEnvelopeProcessor.d.ts.map +1 -1
  32. package/lib/chain/blocks/payloadEnvelopeProcessor.js +24 -19
  33. package/lib/chain/blocks/payloadEnvelopeProcessor.js.map +1 -1
  34. package/lib/chain/blocks/types.d.ts +4 -1
  35. package/lib/chain/blocks/types.d.ts.map +1 -1
  36. package/lib/chain/blocks/verifyBlock.d.ts.map +1 -1
  37. package/lib/chain/blocks/verifyBlock.js +18 -7
  38. package/lib/chain/blocks/verifyBlock.js.map +1 -1
  39. package/lib/chain/blocks/verifyBlocksDataAvailability.d.ts.map +1 -1
  40. package/lib/chain/blocks/verifyBlocksDataAvailability.js +8 -1
  41. package/lib/chain/blocks/verifyBlocksDataAvailability.js.map +1 -1
  42. package/lib/chain/blocks/verifyBlocksSignatures.d.ts.map +1 -1
  43. package/lib/chain/blocks/verifyBlocksSignatures.js +7 -5
  44. package/lib/chain/blocks/verifyBlocksSignatures.js.map +1 -1
  45. package/lib/chain/blocks/verifyPayloadsDataAvailability.d.ts +3 -3
  46. package/lib/chain/blocks/verifyPayloadsDataAvailability.d.ts.map +1 -1
  47. package/lib/chain/blocks/verifyPayloadsDataAvailability.js +11 -6
  48. package/lib/chain/blocks/verifyPayloadsDataAvailability.js.map +1 -1
  49. package/lib/chain/bls/interface.d.ts +2 -0
  50. package/lib/chain/bls/interface.d.ts.map +1 -1
  51. package/lib/chain/bls/multithread/index.d.ts.map +1 -1
  52. package/lib/chain/bls/multithread/index.js +13 -11
  53. package/lib/chain/bls/multithread/index.js.map +1 -1
  54. package/lib/chain/bls/multithread/types.d.ts +1 -1
  55. package/lib/chain/bls/multithread/types.d.ts.map +1 -1
  56. package/lib/chain/bls/multithread/worker.js +1 -1
  57. package/lib/chain/bls/multithread/worker.js.map +1 -1
  58. package/lib/chain/bls/singleThread.js +1 -1
  59. package/lib/chain/bls/singleThread.js.map +1 -1
  60. package/lib/chain/bls/utils.js +1 -1
  61. package/lib/chain/bls/utils.js.map +1 -1
  62. package/lib/chain/chain.d.ts +1 -1
  63. package/lib/chain/chain.d.ts.map +1 -1
  64. package/lib/chain/chain.js.map +1 -1
  65. package/lib/chain/errors/blockError.d.ts +1 -1
  66. package/lib/chain/errors/blockError.d.ts.map +1 -1
  67. package/lib/chain/errors/blockError.js +1 -1
  68. package/lib/chain/errors/blockError.js.map +1 -1
  69. package/lib/chain/interface.d.ts +2 -3
  70. package/lib/chain/interface.d.ts.map +1 -1
  71. package/lib/chain/interface.js.map +1 -1
  72. package/lib/chain/opPools/aggregatedAttestationPool.d.ts.map +1 -1
  73. package/lib/chain/opPools/aggregatedAttestationPool.js +23 -10
  74. package/lib/chain/opPools/aggregatedAttestationPool.js.map +1 -1
  75. package/lib/chain/opPools/attestationPool.d.ts +1 -1
  76. package/lib/chain/opPools/attestationPool.d.ts.map +1 -1
  77. package/lib/chain/opPools/attestationPool.js +15 -4
  78. package/lib/chain/opPools/attestationPool.js.map +1 -1
  79. package/lib/chain/opPools/payloadAttestationPool.d.ts.map +1 -1
  80. package/lib/chain/opPools/payloadAttestationPool.js +17 -7
  81. package/lib/chain/opPools/payloadAttestationPool.js.map +1 -1
  82. package/lib/chain/opPools/syncCommitteeMessagePool.d.ts.map +1 -1
  83. package/lib/chain/opPools/syncCommitteeMessagePool.js +9 -3
  84. package/lib/chain/opPools/syncCommitteeMessagePool.js.map +1 -1
  85. package/lib/chain/opPools/syncContributionAndProofPool.d.ts.map +1 -1
  86. package/lib/chain/opPools/syncContributionAndProofPool.js +11 -3
  87. package/lib/chain/opPools/syncContributionAndProofPool.js.map +1 -1
  88. package/lib/chain/opPools/utils.d.ts +1 -1
  89. package/lib/chain/opPools/utils.d.ts.map +1 -1
  90. package/lib/chain/opPools/utils.js +1 -1
  91. package/lib/chain/opPools/utils.js.map +1 -1
  92. package/lib/chain/options.d.ts +0 -1
  93. package/lib/chain/options.d.ts.map +1 -1
  94. package/lib/chain/options.js +0 -1
  95. package/lib/chain/options.js.map +1 -1
  96. package/lib/chain/validation/executionPayloadBid.js +13 -13
  97. package/lib/chain/validation/executionPayloadBid.js.map +1 -1
  98. package/lib/db/beacon.d.ts +7 -0
  99. package/lib/db/beacon.d.ts.map +1 -1
  100. package/lib/db/beacon.js +32 -0
  101. package/lib/db/beacon.js.map +1 -1
  102. package/lib/db/dataColumnStore.d.ts +1 -1
  103. package/lib/db/dataColumnStore.d.ts.map +1 -1
  104. package/lib/db/dataColumnStore.js +1 -4
  105. package/lib/db/dataColumnStore.js.map +1 -1
  106. package/lib/db/flatFileStore/flatFileStore.js +5 -5
  107. package/lib/db/flatFileStore/flatFileStore.js.map +1 -1
  108. package/lib/db/flatFileStore/slotIndex.d.ts.map +1 -1
  109. package/lib/db/flatFileStore/slotIndex.js +2 -2
  110. package/lib/db/flatFileStore/slotIndex.js.map +1 -1
  111. package/lib/db/interface.d.ts +1 -0
  112. package/lib/db/interface.d.ts.map +1 -1
  113. package/lib/db/repositories/dataColumnSidecarArchive.d.ts +6 -0
  114. package/lib/db/repositories/dataColumnSidecarArchive.d.ts.map +1 -1
  115. package/lib/db/repositories/dataColumnSidecarArchive.js +19 -0
  116. package/lib/db/repositories/dataColumnSidecarArchive.js.map +1 -1
  117. package/lib/execution/builder/apiClient.d.ts +14 -8
  118. package/lib/execution/builder/apiClient.d.ts.map +1 -1
  119. package/lib/execution/builder/apiClient.js +12 -10
  120. package/lib/execution/builder/apiClient.js.map +1 -1
  121. package/lib/execution/builder/http.d.ts +2 -0
  122. package/lib/execution/builder/http.d.ts.map +1 -1
  123. package/lib/execution/builder/http.js +2 -0
  124. package/lib/execution/builder/http.js.map +1 -1
  125. package/lib/execution/engine/index.d.ts +1 -1
  126. package/lib/execution/engine/index.d.ts.map +1 -1
  127. package/lib/execution/engine/index.js +1 -1
  128. package/lib/execution/engine/index.js.map +1 -1
  129. package/lib/execution/engine/interface.d.ts +1 -1
  130. package/lib/execution/engine/interface.d.ts.map +1 -1
  131. package/lib/execution/engine/interface.js.map +1 -1
  132. package/lib/execution/engine/types.d.ts.map +1 -1
  133. package/lib/execution/engine/types.js +2 -2
  134. package/lib/execution/engine/types.js.map +1 -1
  135. package/lib/execution/engine/utils.d.ts.map +1 -1
  136. package/lib/execution/engine/utils.js +3 -2
  137. package/lib/execution/engine/utils.js.map +1 -1
  138. package/lib/metrics/metrics/lodestar.d.ts +4 -0
  139. package/lib/metrics/metrics/lodestar.d.ts.map +1 -1
  140. package/lib/metrics/metrics/lodestar.js +10 -0
  141. package/lib/metrics/metrics/lodestar.js.map +1 -1
  142. package/lib/metrics/server/http.js +5 -5
  143. package/lib/metrics/server/http.js.map +1 -1
  144. package/lib/monitoring/service.js +1 -1
  145. package/lib/monitoring/service.js.map +1 -1
  146. package/lib/network/core/networkCore.d.ts +2 -0
  147. package/lib/network/core/networkCore.d.ts.map +1 -1
  148. package/lib/network/core/networkCore.js +3 -0
  149. package/lib/network/core/networkCore.js.map +1 -1
  150. package/lib/network/core/networkCoreWorker.js +1 -0
  151. package/lib/network/core/networkCoreWorker.js.map +1 -1
  152. package/lib/network/core/networkCoreWorkerHandler.d.ts +2 -0
  153. package/lib/network/core/networkCoreWorkerHandler.d.ts.map +1 -1
  154. package/lib/network/core/networkCoreWorkerHandler.js +3 -0
  155. package/lib/network/core/networkCoreWorkerHandler.js.map +1 -1
  156. package/lib/network/core/types.d.ts +2 -0
  157. package/lib/network/core/types.d.ts.map +1 -1
  158. package/lib/network/interface.d.ts +2 -0
  159. package/lib/network/interface.d.ts.map +1 -1
  160. package/lib/network/libp2p/index.d.ts.map +1 -1
  161. package/lib/network/libp2p/index.js +5 -1
  162. package/lib/network/libp2p/index.js.map +1 -1
  163. package/lib/network/network.d.ts +2 -0
  164. package/lib/network/network.d.ts.map +1 -1
  165. package/lib/network/network.js +6 -1
  166. package/lib/network/network.js.map +1 -1
  167. package/lib/network/options.d.ts +2 -0
  168. package/lib/network/options.d.ts.map +1 -1
  169. package/lib/network/options.js.map +1 -1
  170. package/lib/network/peers/peerManager.d.ts +2 -1
  171. package/lib/network/peers/peerManager.d.ts.map +1 -1
  172. package/lib/network/peers/peerManager.js.map +1 -1
  173. package/lib/network/peers/utils/assertPeerRelevance.d.ts +9 -1
  174. package/lib/network/peers/utils/assertPeerRelevance.d.ts.map +1 -1
  175. package/lib/network/peers/utils/assertPeerRelevance.js +32 -21
  176. package/lib/network/peers/utils/assertPeerRelevance.js.map +1 -1
  177. package/lib/network/processor/gossipHandlers.d.ts.map +1 -1
  178. package/lib/network/processor/gossipHandlers.js +28 -18
  179. package/lib/network/processor/gossipHandlers.js.map +1 -1
  180. package/lib/node/nodejs.d.ts +3 -1
  181. package/lib/node/nodejs.d.ts.map +1 -1
  182. package/lib/node/nodejs.js +47 -21
  183. package/lib/node/nodejs.js.map +1 -1
  184. package/lib/node/options.d.ts +3 -1
  185. package/lib/node/options.d.ts.map +1 -1
  186. package/lib/node/options.js +4 -2
  187. package/lib/node/options.js.map +1 -1
  188. package/lib/sync/interface.d.ts.map +1 -1
  189. package/lib/sync/interface.js.map +1 -1
  190. package/lib/sync/sync.d.ts +7 -0
  191. package/lib/sync/sync.d.ts.map +1 -1
  192. package/lib/sync/sync.js +61 -3
  193. package/lib/sync/sync.js.map +1 -1
  194. package/lib/util/file.d.ts.map +1 -1
  195. package/lib/util/file.js +8 -9
  196. package/lib/util/file.js.map +1 -1
  197. package/lib/util/queue/itemQueue.js +1 -1
  198. package/lib/util/queue/itemQueue.js.map +1 -1
  199. package/lib/util/sszBytes.js +2 -2
  200. package/lib/util/sszBytes.js.map +1 -1
  201. package/package.json +16 -17
  202. package/src/api/impl/beacon/blocks/index.ts +35 -45
  203. package/src/api/impl/beacon/pool/index.ts +5 -1
  204. package/src/api/impl/beacon/state/utils.ts +1 -1
  205. package/src/api/impl/validator/index.ts +47 -30
  206. package/src/chain/GetBlobsTracker.ts +97 -38
  207. package/src/chain/archiveStore/archiveStore.ts +1 -0
  208. package/src/chain/archiveStore/utils/archiveBlocks.ts +12 -4
  209. package/src/chain/blocks/importExecutionPayload.ts +13 -21
  210. package/src/chain/blocks/index.ts +6 -2
  211. package/src/chain/blocks/payloadEnvelopeProcessor.ts +35 -24
  212. package/src/chain/blocks/types.ts +4 -1
  213. package/src/chain/blocks/verifyBlock.ts +31 -16
  214. package/src/chain/blocks/verifyBlocksDataAvailability.ts +10 -1
  215. package/src/chain/blocks/verifyBlocksSignatures.ts +7 -5
  216. package/src/chain/blocks/verifyPayloadsDataAvailability.ts +14 -6
  217. package/src/chain/bls/interface.ts +2 -0
  218. package/src/chain/bls/multithread/index.ts +14 -10
  219. package/src/chain/bls/multithread/types.ts +1 -1
  220. package/src/chain/bls/multithread/worker.ts +1 -5
  221. package/src/chain/bls/singleThread.ts +1 -1
  222. package/src/chain/bls/utils.ts +1 -1
  223. package/src/chain/chain.ts +1 -1
  224. package/src/chain/errors/blockError.ts +1 -1
  225. package/src/chain/interface.ts +2 -3
  226. package/src/chain/opPools/aggregatedAttestationPool.ts +25 -9
  227. package/src/chain/opPools/attestationPool.ts +16 -4
  228. package/src/chain/opPools/payloadAttestationPool.ts +18 -7
  229. package/src/chain/opPools/syncCommitteeMessagePool.ts +10 -3
  230. package/src/chain/opPools/syncContributionAndProofPool.ts +13 -3
  231. package/src/chain/opPools/utils.ts +1 -1
  232. package/src/chain/options.ts +0 -2
  233. package/src/chain/validation/executionPayloadBid.ts +14 -13
  234. package/src/db/beacon.ts +39 -0
  235. package/src/db/dataColumnStore.ts +2 -4
  236. package/src/db/flatFileStore/flatFileStore.ts +7 -7
  237. package/src/db/flatFileStore/slotIndex.ts +4 -3
  238. package/src/db/interface.ts +2 -0
  239. package/src/db/repositories/dataColumnSidecarArchive.ts +20 -0
  240. package/src/execution/builder/apiClient.ts +16 -13
  241. package/src/execution/builder/http.ts +4 -0
  242. package/src/execution/engine/index.ts +1 -1
  243. package/src/execution/engine/interface.ts +1 -1
  244. package/src/execution/engine/types.ts +3 -1
  245. package/src/execution/engine/utils.ts +4 -2
  246. package/src/metrics/metrics/lodestar.ts +10 -0
  247. package/src/metrics/server/http.ts +5 -5
  248. package/src/monitoring/service.ts +1 -1
  249. package/src/network/core/networkCore.ts +5 -0
  250. package/src/network/core/networkCoreWorker.ts +1 -0
  251. package/src/network/core/networkCoreWorkerHandler.ts +4 -0
  252. package/src/network/core/types.ts +2 -0
  253. package/src/network/interface.ts +2 -0
  254. package/src/network/libp2p/index.ts +5 -1
  255. package/src/network/network.ts +8 -1
  256. package/src/network/options.ts +2 -0
  257. package/src/network/peers/peerManager.ts +1 -1
  258. package/src/network/peers/utils/assertPeerRelevance.ts +39 -23
  259. package/src/network/processor/gossipHandlers.ts +30 -20
  260. package/src/node/nodejs.ts +58 -22
  261. package/src/node/options.ts +9 -6
  262. package/src/sync/interface.ts +1 -0
  263. package/src/sync/sync.ts +65 -4
  264. package/src/util/file.ts +8 -9
  265. package/src/util/queue/itemQueue.ts +1 -1
  266. package/src/util/sszBytes.ts +2 -2
@@ -10,6 +10,7 @@ import {isForkPostFulu} from "@lodestar/params";
10
10
  import {ResponseIncoming} from "@lodestar/reqresp";
11
11
  import {Epoch, Status, fulu, sszTypesFor} from "@lodestar/types";
12
12
  import {formatNodePeer} from "../../api/impl/node/utils.js";
13
+ import {GoodByeReasonCode} from "../../constants/index.js";
13
14
  import {RegistryMetricCreator} from "../../metrics/index.js";
14
15
  import {ClockEvent, IClock} from "../../util/clock.js";
15
16
  import {CustodyConfig} from "../../util/dataColumns.js";
@@ -332,6 +333,10 @@ export class NetworkCore implements INetworkCore {
332
333
  this.peerManager.reStatusPeers(peers);
333
334
  }
334
335
 
336
+ async goodbyeAndDisconnect(peer: PeerIdStr, reason: GoodByeReasonCode): Promise<void> {
337
+ await this.peerManager.goodbyeAndDisconnect(peerIdFromString(peer), reason);
338
+ }
339
+
335
340
  /**
336
341
  * Request att subnets up `toSlot`. Network will ensure to mantain some peers for each
337
342
  */
@@ -138,6 +138,7 @@ const libp2pWorkerApi: NetworkWorkerApi = {
138
138
  prepareSyncCommitteeSubnets: (subscriptions) => core.prepareSyncCommitteeSubnets(subscriptions),
139
139
  reportPeer: (peer, action, actionName) => core.reportPeer(peer, action, actionName),
140
140
  reStatusPeers: (peers) => core.reStatusPeers(peers),
141
+ goodbyeAndDisconnect: (peer, reason) => core.goodbyeAndDisconnect(peer, reason),
141
142
  subscribeGossipCoreTopics: () => core.subscribeGossipCoreTopics(),
142
143
  unsubscribeGossipCoreTopics: () => core.unsubscribeGossipCoreTopics(),
143
144
 
@@ -11,6 +11,7 @@ import type {LoggerNode} from "@lodestar/logger/node";
11
11
  import {ResponseIncoming, ResponseOutgoing} from "@lodestar/reqresp";
12
12
  import {Status} from "@lodestar/types";
13
13
  import {withTimeout} from "@lodestar/utils";
14
+ import {GoodByeReasonCode} from "../../constants/index.js";
14
15
  import {Metrics} from "../../metrics/index.js";
15
16
  import {AsyncIterableBridgeCaller, AsyncIterableBridgeHandler} from "../../util/asyncIterableToEvents.js";
16
17
  import {PeerIdStr, peerIdFromString} from "../../util/peerId.js";
@@ -205,6 +206,9 @@ export class WorkerNetworkCore implements INetworkCore {
205
206
  reStatusPeers(peers: PeerIdStr[]): Promise<void> {
206
207
  return this.getApi().reStatusPeers(peers);
207
208
  }
209
+ goodbyeAndDisconnect(peer: PeerIdStr, reason: GoodByeReasonCode): Promise<void> {
210
+ return this.getApi().goodbyeAndDisconnect(peer, reason);
211
+ }
208
212
  reportPeer(peer: PeerIdStr, action: PeerAction, actionName: string): Promise<void> {
209
213
  return this.getApi().reportPeer(peer, action, actionName);
210
214
  }
@@ -5,6 +5,7 @@ import {SpecJson} from "@lodestar/config";
5
5
  import {LoggerNodeOpts} from "@lodestar/logger/node";
6
6
  import {ResponseIncoming} from "@lodestar/reqresp";
7
7
  import {Status} from "@lodestar/types";
8
+ import {GoodByeReasonCode} from "../../constants/index.js";
8
9
  import {PeerIdStr} from "../../util/peerId.js";
9
10
  import {NetworkOptions} from "../options.js";
10
11
  import {PeerAction, PeerScoreStats} from "../peers/index.js";
@@ -53,6 +54,7 @@ export interface INetworkCore extends INetworkCorePublic {
53
54
  // Sync method
54
55
  reportPeer(peer: PeerIdStr, action: PeerAction, actionName: string): void;
55
56
  reStatusPeers(peers: PeerIdStr[]): Promise<void>;
57
+ goodbyeAndDisconnect(peer: PeerIdStr, reason: GoodByeReasonCode): Promise<void>;
56
58
 
57
59
  // TODO: Duplicated methods with INetwork interface
58
60
  getConnectedPeers(): Promise<PeerIdStr[]>;
@@ -37,6 +37,7 @@ import {
37
37
  phase0,
38
38
  } from "@lodestar/types";
39
39
  import {BlockInputSource} from "../chain/blocks/blockInput/types.js";
40
+ import {GoodByeReasonCode} from "../constants/index.js";
40
41
  import {CustodyConfig} from "../util/dataColumns.js";
41
42
  import {PeerIdStr} from "../util/peerId.js";
42
43
  import {
@@ -74,6 +75,7 @@ export interface INetwork extends INetworkCorePublic {
74
75
  reportPeer(peer: PeerIdStr, action: PeerAction, actionName: string): void;
75
76
  shouldAggregate(subnet: SubnetID, slot: Slot): boolean;
76
77
  reStatusPeers(peers: PeerIdStr[]): Promise<void>;
78
+ goodbyeAndDisconnect(peer: PeerIdStr, reason: GoodByeReasonCode): Promise<void>;
77
79
  searchUnknownBlock(slotRoot: SlotRootHex, source: BlockInputSource, peer?: PeerIdStr): void;
78
80
  searchUnknownEnvelope(slotRoot: SlotRootHex, source: BlockInputSource, peer?: PeerIdStr): void;
79
81
  // ReqResp
@@ -150,7 +150,11 @@ export async function createNodeJsLibp2p(
150
150
  nodeInfo: {
151
151
  name: "lodestar",
152
152
  version: networkOpts.version ?? "unknown",
153
- userAgent: networkOpts.private ? "" : networkOpts.version ? `lodestar/${networkOpts.version}` : "lodestar",
153
+ userAgent: networkOpts.private
154
+ ? ""
155
+ : networkOpts.advertiseVersion && networkOpts.version
156
+ ? `lodestar/${networkOpts.version}`
157
+ : "lodestar",
154
158
  },
155
159
  addresses: {
156
160
  listen: localMultiaddrs,
@@ -34,6 +34,7 @@ import {prettyPrintIndices, sleep} from "@lodestar/utils";
34
34
  import {BlockInputSource} from "../chain/blocks/blockInput/types.js";
35
35
  import {ChainEvent, IBeaconChain} from "../chain/index.js";
36
36
  import {computeSubnetForDataColumnSidecar} from "../chain/validation/dataColumnSidecar.js";
37
+ import {GoodByeReasonCode} from "../constants/index.js";
37
38
  import {IBeaconDb} from "../db/interface.js";
38
39
  import {Metrics, RegistryMetricCreator} from "../metrics/index.js";
39
40
  import {IClock} from "../util/clock.js";
@@ -289,6 +290,10 @@ export class Network implements INetwork {
289
290
  return this.core.reStatusPeers(peers);
290
291
  }
291
292
 
293
+ async goodbyeAndDisconnect(peer: PeerIdStr, reason: GoodByeReasonCode): Promise<void> {
294
+ return this.core.goodbyeAndDisconnect(peer, reason);
295
+ }
296
+
292
297
  searchUnknownBlock(slotRoot: SlotRootHex, source: BlockInputSource, peer?: PeerIdStr): void {
293
298
  this.networkProcessor.searchUnknownBlock(slotRoot, source, peer);
294
299
  }
@@ -855,7 +860,9 @@ export class Network implements INetwork {
855
860
  };
856
861
 
857
862
  private onTargetGroupCountUpdated = (count: number): void => {
858
- this.core.setTargetGroupCount(count);
863
+ this.core.setTargetGroupCount(count).catch((e) => {
864
+ this.logger.debug("Error setting target custody group count", {count}, e as Error);
865
+ });
859
866
  };
860
867
 
861
868
  private onPublishDataColumns = async (sidecars: DataColumnSidecar[]): Promise<void> => {
@@ -25,6 +25,8 @@ export interface NetworkOptions
25
25
  mdns?: boolean;
26
26
  connectToDiscv5Bootnodes?: boolean;
27
27
  version?: string;
28
+ /** Include the version in libp2p Identify. Private mode takes precedence. */
29
+ advertiseVersion?: boolean;
28
30
  private?: boolean;
29
31
  useWorker?: boolean;
30
32
  maxYoungGenerationSizeMb?: number;
@@ -893,7 +893,7 @@ export class PeerManager {
893
893
  }
894
894
  }
895
895
 
896
- private async goodbyeAndDisconnect(peer: PeerId, goodbye: GoodByeReasonCode): Promise<void> {
896
+ async goodbyeAndDisconnect(peer: PeerId, goodbye: GoodByeReasonCode): Promise<void> {
897
897
  const reason = GOODBYE_KNOWN_CODES[goodbye.toString()] || "";
898
898
  const peerIdStr = peer.toString();
899
899
  try {
@@ -1,5 +1,6 @@
1
- import {ForkName, isForkPostFulu} from "@lodestar/params";
2
- import {ForkDigest, Root, Slot, Status, fulu, ssz} from "@lodestar/types";
1
+ import {ForkName, GENESIS_EPOCH, isForkPostFulu} from "@lodestar/params";
2
+ import {computeEpochAtSlot} from "@lodestar/state-transition";
3
+ import {Epoch, ForkDigest, Root, Slot, Status, fulu, ssz} from "@lodestar/types";
3
4
  import {toHex, toRootHex} from "@lodestar/utils";
4
5
 
5
6
  // TODO: Why this value? (From Lighthouse)
@@ -9,6 +10,8 @@ export enum IrrelevantPeerCode {
9
10
  INCOMPATIBLE_FORKS = "IRRELEVANT_PEER_INCOMPATIBLE_FORKS",
10
11
  DIFFERENT_CLOCKS = "IRRELEVANT_PEER_DIFFERENT_CLOCKS",
11
12
  DIFFERENT_FINALIZED = "IRRELEVANT_PEER_DIFFERENT_FINALIZED",
13
+ FINALIZED_EPOCH_TOO_HIGH = "IRRELEVANT_PEER_FINALIZED_EPOCH_TOO_HIGH",
14
+ INVALID_FINALIZED_ROOT = "IRRELEVANT_PEER_INVALID_FINALIZED_ROOT",
12
15
  NO_EARLIEST_AVAILABLE_SLOT = "NO_EARLIEST_AVAILABLE_SLOT",
13
16
  }
14
17
 
@@ -16,6 +19,8 @@ type IrrelevantPeerType =
16
19
  | {code: IrrelevantPeerCode.INCOMPATIBLE_FORKS; ours: ForkDigest; theirs: ForkDigest}
17
20
  | {code: IrrelevantPeerCode.DIFFERENT_CLOCKS; slotDiff: number}
18
21
  | {code: IrrelevantPeerCode.NO_EARLIEST_AVAILABLE_SLOT}
22
+ | {code: IrrelevantPeerCode.FINALIZED_EPOCH_TOO_HIGH; finalizedEpoch: Epoch; maxFinalizedEpoch: Epoch}
23
+ | {code: IrrelevantPeerCode.INVALID_FINALIZED_ROOT}
19
24
  | {code: IrrelevantPeerCode.DIFFERENT_FINALIZED; expectedRoot: Root; remoteRoot: Root};
20
25
 
21
26
  /**
@@ -45,29 +50,35 @@ export function assertPeerRelevance(
45
50
  return {code: IrrelevantPeerCode.DIFFERENT_CLOCKS, slotDiff};
46
51
  }
47
52
 
48
- // The remote's finalized epoch is less than or equal to ours, but the block root is
49
- // different to the one in our chain. Therefore, the node is on a different chain and we
50
- // should not communicate with them.
53
+ // A checkpoint can be finalized at the earliest at the start of epoch + 2
54
+ const maxFinalizedEpoch = Math.max(
55
+ GENESIS_EPOCH,
56
+ computeEpochAtSlot(Math.max(currentSlot, 0) + FUTURE_SLOT_TOLERANCE) - 2
57
+ );
58
+ if (remote.finalizedEpoch > maxFinalizedEpoch) {
59
+ return {
60
+ code: IrrelevantPeerCode.FINALIZED_EPOCH_TOO_HIGH,
61
+ finalizedEpoch: remote.finalizedEpoch,
62
+ maxFinalizedEpoch,
63
+ };
64
+ }
65
+
66
+ if (remote.finalizedEpoch > GENESIS_EPOCH && isZeroRoot(remote.finalizedRoot)) {
67
+ return {code: IrrelevantPeerCode.INVALID_FINALIZED_ROOT};
68
+ }
51
69
 
70
+ // The remote's finalized epoch is equal to ours, but the block root is different to the one in our chain.
71
+ // Therefore, the node is on a different chain and we should not communicate with them.
52
72
  if (
53
- remote.finalizedEpoch <= local.finalizedEpoch &&
54
- !isZeroRoot(remote.finalizedRoot) &&
55
- !isZeroRoot(local.finalizedRoot)
73
+ remote.finalizedEpoch === local.finalizedEpoch &&
74
+ !isZeroRoot(local.finalizedRoot) &&
75
+ !ssz.Root.equals(remote.finalizedRoot, local.finalizedRoot)
56
76
  ) {
57
- // NOTE: due to preferring to not access chain state here, we can't check the finalized root against our history.
58
- // The impact of not doing check is low: peers that are behind us we can't confirm they are in the same chain as us.
59
- // In the worst case they will attempt to sync from us, fail and disconnect. The ENR fork check should be sufficient
60
- // to differentiate most peers in normal network conditions.
61
- const remoteRoot = remote.finalizedRoot;
62
- const expectedRoot = remote.finalizedEpoch === local.finalizedEpoch ? local.finalizedRoot : null;
63
-
64
- if (expectedRoot !== null && !ssz.Root.equals(remoteRoot, expectedRoot)) {
65
- return {
66
- code: IrrelevantPeerCode.DIFFERENT_FINALIZED,
67
- expectedRoot: expectedRoot, // forkChoice returns Tree BranchNode which the logger prints as {}
68
- remoteRoot: remoteRoot,
69
- };
70
- }
77
+ return {
78
+ code: IrrelevantPeerCode.DIFFERENT_FINALIZED,
79
+ expectedRoot: local.finalizedRoot,
80
+ remoteRoot: remote.finalizedRoot,
81
+ };
71
82
  }
72
83
 
73
84
  if (isForkPostFulu(forkName) && (remote as fulu.Status).earliestAvailableSlot === undefined) {
@@ -76,7 +87,8 @@ export function assertPeerRelevance(
76
87
  };
77
88
  }
78
89
 
79
- // Note: Accept request status finalized checkpoint in the future, we do not know if it is a true finalized root
90
+ // Note: for a behind finalized checkpoint, we validate it more on the main thread against our history,
91
+ // see BeaconSync.verifyPastFinalizedCheckpoint
80
92
  return null;
81
93
  }
82
94
 
@@ -93,6 +105,10 @@ export function renderIrrelevantPeerType(type: IrrelevantPeerType): string {
93
105
  return `DIFFERENT_CLOCKS slotDiff: ${type.slotDiff}`;
94
106
  case IrrelevantPeerCode.DIFFERENT_FINALIZED:
95
107
  return `DIFFERENT_FINALIZED root: ${toRootHex(type.remoteRoot)} expected: ${toRootHex(type.expectedRoot)}`;
108
+ case IrrelevantPeerCode.FINALIZED_EPOCH_TOO_HIGH:
109
+ return `FINALIZED_EPOCH_TOO_HIGH finalizedEpoch: ${type.finalizedEpoch} max: ${type.maxFinalizedEpoch}`;
110
+ case IrrelevantPeerCode.INVALID_FINALIZED_ROOT:
111
+ return "INVALID_FINALIZED_ROOT zero root for a non-genesis finalized epoch";
96
112
  case IrrelevantPeerCode.NO_EARLIEST_AVAILABLE_SLOT:
97
113
  return "No earliestAvailableSlot announced via peer Status";
98
114
  }
@@ -705,10 +705,8 @@ function getSequentialHandlers(modules: ValidatorFnsModules, options: GossipHand
705
705
 
706
706
  // Immediately attempt fetch of data columns from execution engine as the bid contains kzg commitments
707
707
  // which is all the information we need so there is no reason to delay until execution payload arrives
708
- // TODO GLOAS: If we want EL retries after this initial attempt, add an explicit retry policy here
709
- // (for example later in the slot). Do not couple retries to incoming gossip columns.
710
- // Columns fetched here feed payloadInput.addColumn, which resolves waitForAllData for any
711
- // in-flight importExecutionPayload. No processExecutionPayload trigger needed from this path.
708
+ // Columns fetched here feed payloadInput.addColumn, which resolves the waitForAllData of a
709
+ // pending payload import. No processExecutionPayload trigger needed from this path.
712
710
  chain.getBlobsTracker.triggerGetBlobs(payloadInput);
713
711
  }
714
712
  })
@@ -718,11 +716,10 @@ function getSequentialHandlers(modules: ValidatorFnsModules, options: GossipHand
718
716
 
719
717
  if (e instanceof BlockError) {
720
718
  switch (e.type.code) {
721
- case BlockErrorCode.DATA_UNAVAILABLE: {
722
- // Error is quite frequent and not critical
719
+ case BlockErrorCode.DATA_UNAVAILABLE:
720
+ // Data did not arrive in time, BlockInputSync keeps pulling it and retries the import
723
721
  logLevel = LogLevel.debug;
724
722
  break;
725
- }
726
723
  // ALREADY_KNOWN should not happen with ignoreIfKnown=true above
727
724
  // PARENT_BLOCK_UNKNOWN should not happen, we handled this in validateBeaconBlock() function above
728
725
  case BlockErrorCode.ALREADY_KNOWN:
@@ -930,7 +927,7 @@ function getSequentialHandlers(modules: ValidatorFnsModules, options: GossipHand
930
927
  }
931
928
 
932
929
  // NOTE: we do NOT call chain.processExecutionPayload here. That is triggered only by
933
- // envelope arrival (gossip or API). An in-flight importExecutionPayload is awaiting
930
+ // envelope arrival (gossip or API). A pending payload import is awaiting
934
931
  // payloadInput.waitForAllData(); addColumn above will resolve it once hasAllData flips.
935
932
 
936
933
  scheduleIncompletePayloadEnvelope(payloadInput, peerIdStr);
@@ -1331,18 +1328,22 @@ function getSequentialHandlers(modules: ValidatorFnsModules, options: GossipHand
1331
1328
  });
1332
1329
  }
1333
1330
 
1331
+ // Add the envelope in the same tick as validation, otherwise a by-root download that completes
1332
+ // before the next event loop sees no envelope, validates it again and adds it first
1333
+ if (!payloadInput.hasPayloadEnvelope()) {
1334
+ chain.serializedCache.set(signedEnvelope, serializedData);
1335
+
1336
+ payloadInput.addPayloadEnvelope({
1337
+ envelope: signedEnvelope,
1338
+ source: PayloadEnvelopeInputSource.gossip,
1339
+ seenTimestampSec,
1340
+ peerIdStr,
1341
+ });
1342
+ }
1343
+
1334
1344
  // Handler - deferred to next event loop so the validation result propagates first
1335
1345
  callInNextEventLoop(() => {
1336
1346
  try {
1337
- chain.serializedCache.set(signedEnvelope, serializedData);
1338
-
1339
- payloadInput.addPayloadEnvelope({
1340
- envelope: signedEnvelope,
1341
- source: PayloadEnvelopeInputSource.gossip,
1342
- seenTimestampSec,
1343
- peerIdStr,
1344
- });
1345
-
1346
1347
  scheduleIncompletePayloadEnvelope(payloadInput, peerIdStr);
1347
1348
 
1348
1349
  chain.emitter.emit(routes.events.EventType.executionPayloadGossip, {
@@ -1352,12 +1353,18 @@ function getSequentialHandlers(modules: ValidatorFnsModules, options: GossipHand
1352
1353
  blockRoot: blockRootHex,
1353
1354
  });
1354
1355
 
1355
- chain.processExecutionPayload(payloadInput, {validSignature: true}).catch((e) => {
1356
+ // Signature is verified above only for this envelope, one added by another source is verified at import
1357
+ const validSignature = payloadInput.getPayloadEnvelope() === signedEnvelope;
1358
+ chain.processExecutionPayload(payloadInput, {validSignature}).catch((e) => {
1356
1359
  // Adjust verbosity based on error type
1357
1360
  let logLevel: LogLevel;
1358
1361
 
1359
1362
  if (e instanceof PayloadError) {
1360
1363
  switch (e.type.code) {
1364
+ case PayloadErrorCode.DATA_UNAVAILABLE:
1365
+ // Columns did not arrive in time, BlockInputSync keeps pulling them and retries the import
1366
+ logLevel = LogLevel.debug;
1367
+ break;
1361
1368
  // BLOCK_NOT_IN_FORK_CHOICE should not happen, validateGossipExecutionPayloadEnvelope above
1362
1369
  // already verified the block is in fork choice
1363
1370
  case PayloadErrorCode.BLOCK_NOT_IN_FORK_CHOICE:
@@ -1366,11 +1373,14 @@ function getSequentialHandlers(modules: ValidatorFnsModules, options: GossipHand
1366
1373
  // Errors might indicate an issue with our node or the connected EL client
1367
1374
  logLevel = LogLevel.error;
1368
1375
  break;
1369
- // INVALID_SIGNATURE should not happen, signature is verified during gossip validation
1376
+ // INVALID_SIGNATURE only happens for an envelope added by another source
1370
1377
  case PayloadErrorCode.INVALID_SIGNATURE:
1371
1378
  case PayloadErrorCode.ENVELOPE_VERIFICATION_ERROR:
1372
1379
  case PayloadErrorCode.EXECUTION_ENGINE_INVALID:
1373
- core.reportPeer(peerIdStr, PeerAction.LowToleranceError, "BadGossipPayload");
1380
+ // Penalize only when the stored envelope is the one this peer sent, not one added by another source
1381
+ if (validSignature) {
1382
+ core.reportPeer(peerIdStr, PeerAction.LowToleranceError, "BadGossipPayload");
1383
+ }
1374
1384
  // The builder may have signed another envelope that is valid, keeping this one would make
1375
1385
  // by-root and range sync reuse it and never import the payload
1376
1386
  chain.seenPayloadEnvelopeInputCache.removeInvalid(payloadInput);
@@ -1,23 +1,29 @@
1
1
  import {setMaxListeners} from "node:events";
2
2
  import {PrivateKey} from "@libp2p/interface";
3
3
  import {Registry} from "prom-client";
4
+ import {hasher} from "@chainsafe/persistent-merkle-tree";
5
+ import {BeaconApiMethods} from "@lodestar/api/beacon/server";
6
+ import {BeaconConfig} from "@lodestar/config";
7
+ import type {LoggerNode} from "@lodestar/logger/node";
4
8
  import {
5
9
  init as initNativeMetrics,
6
10
  registerLocalValidator as registerNativeLocalValidator,
7
11
  scrapeMetrics as scrapeNativeMetrics,
8
12
  unregisterLocalValidator as unregisterNativeLocalValidator,
9
- } from "@chainsafe/lodestar-z/metrics";
10
- import {type PubkeyCache} from "@chainsafe/lodestar-z/pubkeys";
11
- import {hasher} from "@chainsafe/persistent-merkle-tree";
12
- import {BeaconApiMethods} from "@lodestar/api/beacon/server";
13
- import {BeaconConfig} from "@lodestar/config";
14
- import type {LoggerNode} from "@lodestar/logger/node";
13
+ } from "@lodestar/native/metrics";
14
+ import {type PubkeyCache} from "@lodestar/native/pubkeys";
15
15
  import {ZERO_HASH_HEX} from "@lodestar/params";
16
- import {IBeaconStateView, isStatePostBellatrix, isStatePostGloas} from "@lodestar/state-transition";
16
+ import {
17
+ IBeaconStateView,
18
+ computeStartSlotAtEpoch,
19
+ isStatePostBellatrix,
20
+ isStatePostGloas,
21
+ } from "@lodestar/state-transition";
17
22
  import {Slot} from "@lodestar/types";
18
- import {sleep, toRootHex} from "@lodestar/utils";
23
+ import {defer, sleep, toRootHex} from "@lodestar/utils";
19
24
  import {ProcessShutdownCallback} from "@lodestar/validator";
20
25
  import {BeaconRestApiServer, getApi} from "../api/index.js";
26
+ import {getDataColumnSidecarsMinEpoch} from "../chain/archiveStore/utils/archiveBlocks.js";
21
27
  import {BeaconChain, IBeaconChain, initBeaconMetrics} from "../chain/index.js";
22
28
  import {ValidatorMonitor, createValidatorMonitor} from "../chain/validatorMonitor.js";
23
29
  import {IBeaconDb} from "../db/index.js";
@@ -111,6 +117,7 @@ export class BeaconNode {
111
117
 
112
118
  status: BeaconNodeStatus;
113
119
  private controller?: AbortController;
120
+ private closePromise: Promise<void> | undefined;
114
121
 
115
122
  constructor({
116
123
  opts,
@@ -212,6 +219,17 @@ export class BeaconNode {
212
219
 
213
220
  const clock = new Clock({config, genesisTime: anchorState.genesisTime, signal});
214
221
 
222
+ // Before any lookup into the legacy column buckets, which can be slow until compacted
223
+ const columnsMinEpoch =
224
+ opts.chain.archiveDataEpochs === Infinity
225
+ ? null
226
+ : getDataColumnSidecarsMinEpoch(config, clock.currentEpoch, opts.chain.archiveDataEpochs);
227
+ await db.compactLegacyDataColumns(
228
+ columnsMinEpoch !== null && columnsMinEpoch >= config.FULU_FORK_EPOCH
229
+ ? computeStartSlotAtEpoch(columnsMinEpoch)
230
+ : 0
231
+ );
232
+
215
233
  await db.init();
216
234
 
217
235
  // Prune hot db repos
@@ -280,6 +298,7 @@ export class BeaconNode {
280
298
  : undefined,
281
299
  builderApiClientOpts: {
282
300
  timeout: opts.executionBuilder.timeout,
301
+ bidDeadline: opts.executionBuilder.bidDeadline,
283
302
  // Sent with all builder api requests, unless the node runs in private mode
284
303
  userAgent: opts.executionBuilder.userAgent,
285
304
  },
@@ -375,23 +394,40 @@ export class BeaconNode {
375
394
  /**
376
395
  * Stop beacon node and its sub-components.
377
396
  */
378
- async close(): Promise<void> {
379
- if (this.status === BeaconNodeStatus.started) {
380
- this.status = BeaconNodeStatus.closing;
381
- this.sync.close();
382
- if (this.restApi) await this.restApi.close();
383
- await this.network.close();
384
- if (this.metricsServer) await this.metricsServer.close();
385
- if (this.monitoring) await this.monitoring.close();
386
- await this.chain.persistToDisk();
387
- await this.chain.close();
397
+ close(): Promise<void> {
398
+ if (this.closePromise !== undefined) return this.closePromise;
399
+ const completion = defer<void>();
400
+ this.closePromise = completion.promise;
401
+ this.status = BeaconNodeStatus.closing;
402
+ void this.closeInternal().then(completion.resolve, completion.reject);
403
+ return this.closePromise;
404
+ }
405
+
406
+ private async closeInternal(): Promise<void> {
407
+ const errors: unknown[] = [];
408
+ for (const cleanup of [
409
+ () => this.sync.close(),
410
+ () => this.restApi?.close(),
411
+ () => this.network.close(),
412
+ () => this.metricsServer?.close(),
413
+ () => this.monitoring?.close(),
414
+ () => this.chain.persistToDisk(),
415
+ () => this.chain.close(),
388
416
  // Abort signal last: close() calls above clear intervals/timeouts so no new
389
417
  // operations get scheduled. If we aborted first, a still-pending interval could
390
418
  // fire and schedule a new operation after abort, leaving it stuck and delaying shutdown.
391
- if (this.controller) this.controller.abort();
392
- await sleep(DELAY_BEFORE_CLOSING_DB_MS);
393
- await this.db.close();
394
- this.status = BeaconNodeStatus.closed;
419
+ () => this.controller?.abort(),
420
+ () => sleep(DELAY_BEFORE_CLOSING_DB_MS),
421
+ () => this.db.close(),
422
+ ]) {
423
+ try {
424
+ await cleanup();
425
+ } catch (error) {
426
+ errors.push(error);
427
+ }
395
428
  }
429
+ this.status = BeaconNodeStatus.closed;
430
+ if (errors.length === 1) throw errors[0];
431
+ if (errors.length > 1) throw new AggregateError(errors, "Beacon node cleanup failed");
396
432
  }
397
433
  }
@@ -1,7 +1,9 @@
1
+ import {BLOCK_PRODUCTION_RACE_CUTOFF_GLOAS_BPS} from "../api/impl/validator/index.js";
1
2
  import {ApiOptions, defaultApiOptions} from "../api/options.js";
2
3
  import {ArchiveMode, DEFAULT_ARCHIVE_MODE, IChainOptions, defaultChainOptions} from "../chain/options.js";
3
4
  import {ValidatorMonitorOpts, defaultValidatorMonitorOpts} from "../chain/validatorMonitor.js";
4
5
  import {DatabaseOptions, defaultDbOptions} from "../db/options.js";
6
+ import {BUILDER_BID_LAG_BUFFER_MS} from "../execution/builder/apiClient.js";
5
7
  import {
6
8
  EngineApiMode,
7
9
  ExecutionBuilderOpts,
@@ -19,16 +21,17 @@ import {SyncOptions, defaultSyncOptions} from "../sync/options.js";
19
21
 
20
22
  // Re-export so the CLI doesn't need to depend on lodestar-api
21
23
  export {allNamespaces} from "../api/rest/index.js";
22
-
23
- // Re-export to use as default values in CLI args
24
+ export type {EngineApiMode};
25
+ // Re-export to use as default values and for validation in CLI args
24
26
  export {
25
- defaultExecutionEngineHttpOpts,
26
- defaultExecutionBuilderHttpOpts,
27
- engineApiModes,
28
27
  ArchiveMode,
28
+ BLOCK_PRODUCTION_RACE_CUTOFF_GLOAS_BPS,
29
+ BUILDER_BID_LAG_BUFFER_MS,
29
30
  DEFAULT_ARCHIVE_MODE,
31
+ defaultExecutionBuilderHttpOpts,
32
+ defaultExecutionEngineHttpOpts,
33
+ engineApiModes,
30
34
  };
31
- export type {EngineApiMode};
32
35
 
33
36
  export interface IBeaconNodeOptions {
34
37
  api: ApiOptions;
@@ -7,6 +7,7 @@ import {IBeaconDb} from "../db/index.js";
7
7
  import {Metrics} from "../metrics/index.js";
8
8
  import {INetwork} from "../network/index.js";
9
9
  import {SyncChainDebugState} from "./range/chain.js";
10
+
10
11
  export type {SyncChainDebugState};
11
12
 
12
13
  export type SyncingStatus = routes.node.SyncingStatus;
package/src/sync/sync.ts CHANGED
@@ -1,13 +1,17 @@
1
1
  import {SLOTS_PER_EPOCH} from "@lodestar/params";
2
- import {Slot} from "@lodestar/types";
2
+ import {computeStartSlotAtEpoch} from "@lodestar/state-transition";
3
+ import {Slot, Status} from "@lodestar/types";
3
4
  import {Logger, toRootHex} from "@lodestar/utils";
4
5
  import {IBeaconChain} from "../chain/index.js";
5
- import {GENESIS_SLOT} from "../constants/constants.js";
6
+ import {GENESIS_EPOCH, GENESIS_SLOT} from "../constants/constants.js";
7
+ import {GoodByeReasonCode} from "../constants/index.js";
8
+ import {IBeaconDb} from "../db/index.js";
6
9
  import {ExecutionEngineState} from "../execution/index.js";
7
10
  import {Metrics} from "../metrics/index.js";
8
- import {INetwork, NetworkEvent, NetworkEventData} from "../network/index.js";
11
+ import {INetwork, NetworkEvent, NetworkEventData, PeerAction} from "../network/index.js";
9
12
  import {ClockEvent} from "../util/clock.js";
10
13
  import {isOptimisticBlock} from "../util/forkChoice.js";
14
+ import {PeerIdStr} from "../util/peerId.js";
11
15
  import {MIN_EPOCH_TO_START_GOSSIP} from "./constants.js";
12
16
  import {IBeaconSync, SyncChainDebugState, SyncModules, SyncState, SyncingStatus, syncStateMetric} from "./interface.js";
13
17
  import {SyncOptions} from "./options.js";
@@ -19,6 +23,7 @@ export class BeaconSync implements IBeaconSync {
19
23
  private readonly logger: Logger;
20
24
  private readonly network: INetwork;
21
25
  private readonly chain: IBeaconChain;
26
+ private readonly db: IBeaconDb;
22
27
  private readonly metrics: Metrics | null;
23
28
  private readonly opts: SyncOptions;
24
29
 
@@ -30,10 +35,11 @@ export class BeaconSync implements IBeaconSync {
30
35
  private readonly slotImportTolerance: Slot;
31
36
 
32
37
  constructor(opts: SyncOptions, modules: SyncModules) {
33
- const {config, chain, metrics, network, logger} = modules;
38
+ const {config, chain, db, metrics, network, logger} = modules;
34
39
  this.opts = opts;
35
40
  this.network = network;
36
41
  this.chain = chain;
42
+ this.db = db;
37
43
  this.metrics = metrics;
38
44
  this.logger = logger;
39
45
  this.rangeSync = new RangeSync(modules, opts);
@@ -192,6 +198,11 @@ export class BeaconSync implements IBeaconSync {
192
198
  */
193
199
  private addPeer = (data: NetworkEventData[NetworkEvent.peerConnected]): void => {
194
200
  const localStatus = this.chain.getStatus();
201
+ // Genesis and equal finalized epochs are already checked by assertPeerRelevance in the network worker
202
+ if (data.status.finalizedEpoch > GENESIS_EPOCH && data.status.finalizedEpoch < localStatus.finalizedEpoch) {
203
+ void this.verifyPastFinalizedCheckpoint(data.peer, data.status);
204
+ }
205
+
195
206
  const syncType = getPeerSyncType(localStatus, data.status, this.chain.forkChoice, this.slotImportTolerance);
196
207
  this.logger.verbose("Peer sync type classified", {
197
208
  peer: data.peer,
@@ -216,6 +227,56 @@ export class BeaconSync implements IBeaconSync {
216
227
  this.updateSyncState();
217
228
  };
218
229
 
230
+ /**
231
+ * A behind peer's finalized checkpoint must be the canonical block in effect at the start slot of its epoch
232
+ */
233
+ async verifyPastFinalizedCheckpoint(peer: PeerIdStr, remote: Status): Promise<void> {
234
+ const startSlot = computeStartSlotAtEpoch(remote.finalizedEpoch);
235
+ const root = toRootHex(remote.finalizedRoot);
236
+ try {
237
+ let reason: string | null = null;
238
+ // After a finality jump, finalized blocks stay in fork choice until the archiver migrates them.
239
+ // Fork choice is pruned only after archiving, so a miss here means the db already has this range.
240
+ const canonicalBlock = this.chain.forkChoice.getCanonicalBlockClosestLteSlot(startSlot);
241
+ if (canonicalBlock !== null) {
242
+ if (canonicalBlock.blockRoot === root) return;
243
+ reason = "finalized_root_not_canonical";
244
+ } else {
245
+ const slot = await this.db.blockArchive.getSlotByRoot(remote.finalizedRoot);
246
+ if (slot !== null && slot > startSlot) {
247
+ reason = "finalized_root_after_epoch_start";
248
+ } else if (slot !== startSlot) {
249
+ const childSlot = await this.db.blockArchive.getSlotByParentRoot(remote.finalizedRoot);
250
+ if (childSlot !== null) {
251
+ if (childSlot <= startSlot) reason = "finalized_root_not_in_effect_at_epoch_start";
252
+ } else if (slot === null && startSlot > this.chain.earliestAvailableSlot) {
253
+ // childSlot == null too, neither the root nor a child is canonical
254
+ // or they are canonical and both pruned but peer's finalized epoch starts after earliestAvailableSlot
255
+ reason = "unknown_finalized_root";
256
+ }
257
+ }
258
+ }
259
+
260
+ if (reason !== null) {
261
+ this.logger.debug("Peer finalized checkpoint not in our chain", {
262
+ peer,
263
+ finalizedEpoch: remote.finalizedEpoch,
264
+ root,
265
+ reason,
266
+ });
267
+ this.metrics?.syncPeerFinalizedCheckpointMismatch.inc({reason});
268
+ this.network.reportPeer(peer, PeerAction.Fatal, "PeerFinalizedCheckpointMismatch");
269
+ await this.network.goodbyeAndDisconnect(peer, GoodByeReasonCode.IRRELEVANT_NETWORK);
270
+ }
271
+ } catch (e) {
272
+ this.logger.debug(
273
+ "Error verifying peer finalized checkpoint",
274
+ {peer, finalizedEpoch: remote.finalizedEpoch, root},
275
+ e as Error
276
+ );
277
+ }
278
+ }
279
+
219
280
  /**
220
281
  * Must be called by libp2p when a peer is removed from the peer manager
221
282
  */