@lodestar/beacon-node 1.41.0-dev.e27085846c → 1.41.0-dev.ef310100c0

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 (243) hide show
  1. package/lib/api/impl/beacon/blocks/index.d.ts.map +1 -1
  2. package/lib/api/impl/beacon/blocks/index.js +121 -3
  3. package/lib/api/impl/beacon/blocks/index.js.map +1 -1
  4. package/lib/api/impl/beacon/state/index.js +8 -8
  5. package/lib/api/impl/beacon/state/index.js.map +1 -1
  6. package/lib/api/impl/beacon/state/utils.d.ts +3 -4
  7. package/lib/api/impl/beacon/state/utils.d.ts.map +1 -1
  8. package/lib/api/impl/beacon/state/utils.js +5 -24
  9. package/lib/api/impl/beacon/state/utils.js.map +1 -1
  10. package/lib/api/impl/debug/index.d.ts.map +1 -1
  11. package/lib/api/impl/debug/index.js +5 -2
  12. package/lib/api/impl/debug/index.js.map +1 -1
  13. package/lib/api/impl/lightclient/index.d.ts.map +1 -1
  14. package/lib/api/impl/lightclient/index.js +19 -2
  15. package/lib/api/impl/lightclient/index.js.map +1 -1
  16. package/lib/api/impl/validator/index.d.ts.map +1 -1
  17. package/lib/api/impl/validator/index.js +104 -6
  18. package/lib/api/impl/validator/index.js.map +1 -1
  19. package/lib/chain/archiveStore/archiveStore.d.ts +1 -0
  20. package/lib/chain/archiveStore/archiveStore.d.ts.map +1 -1
  21. package/lib/chain/archiveStore/archiveStore.js +9 -0
  22. package/lib/chain/archiveStore/archiveStore.js.map +1 -1
  23. package/lib/chain/archiveStore/historicalState/getHistoricalState.d.ts +5 -6
  24. package/lib/chain/archiveStore/historicalState/getHistoricalState.d.ts.map +1 -1
  25. package/lib/chain/archiveStore/historicalState/getHistoricalState.js +9 -10
  26. package/lib/chain/archiveStore/historicalState/getHistoricalState.js.map +1 -1
  27. package/lib/chain/archiveStore/historicalState/worker.js +3 -3
  28. package/lib/chain/archiveStore/historicalState/worker.js.map +1 -1
  29. package/lib/chain/archiveStore/utils/archivePayloads.d.ts +7 -0
  30. package/lib/chain/archiveStore/utils/archivePayloads.d.ts.map +1 -0
  31. package/lib/chain/archiveStore/utils/archivePayloads.js +10 -0
  32. package/lib/chain/archiveStore/utils/archivePayloads.js.map +1 -0
  33. package/lib/chain/blocks/importBlock.d.ts.map +1 -1
  34. package/lib/chain/blocks/importBlock.js +0 -2
  35. package/lib/chain/blocks/importBlock.js.map +1 -1
  36. package/lib/chain/blocks/index.d.ts.map +1 -1
  37. package/lib/chain/blocks/index.js +2 -1
  38. package/lib/chain/blocks/index.js.map +1 -1
  39. package/lib/chain/blocks/writeBlockInputToDb.d.ts.map +1 -1
  40. package/lib/chain/blocks/writeBlockInputToDb.js +3 -0
  41. package/lib/chain/blocks/writeBlockInputToDb.js.map +1 -1
  42. package/lib/chain/bls/multithread/index.d.ts +3 -3
  43. package/lib/chain/bls/multithread/index.d.ts.map +1 -1
  44. package/lib/chain/bls/multithread/index.js +5 -5
  45. package/lib/chain/bls/multithread/index.js.map +1 -1
  46. package/lib/chain/bls/multithread/jobItem.d.ts +2 -2
  47. package/lib/chain/bls/multithread/jobItem.d.ts.map +1 -1
  48. package/lib/chain/bls/multithread/jobItem.js +2 -2
  49. package/lib/chain/bls/multithread/jobItem.js.map +1 -1
  50. package/lib/chain/bls/singleThread.d.ts +4 -4
  51. package/lib/chain/bls/singleThread.d.ts.map +1 -1
  52. package/lib/chain/bls/singleThread.js +4 -4
  53. package/lib/chain/bls/singleThread.js.map +1 -1
  54. package/lib/chain/bls/utils.d.ts +2 -2
  55. package/lib/chain/bls/utils.d.ts.map +1 -1
  56. package/lib/chain/bls/utils.js +7 -4
  57. package/lib/chain/bls/utils.js.map +1 -1
  58. package/lib/chain/chain.d.ts +6 -9
  59. package/lib/chain/chain.d.ts.map +1 -1
  60. package/lib/chain/chain.js +32 -16
  61. package/lib/chain/chain.js.map +1 -1
  62. package/lib/chain/emitter.d.ts +2 -2
  63. package/lib/chain/emitter.d.ts.map +1 -1
  64. package/lib/chain/interface.d.ts +4 -6
  65. package/lib/chain/interface.d.ts.map +1 -1
  66. package/lib/chain/interface.js.map +1 -1
  67. package/lib/chain/lightClient/index.d.ts.map +1 -1
  68. package/lib/chain/lightClient/index.js +1 -1
  69. package/lib/chain/lightClient/index.js.map +1 -1
  70. package/lib/chain/options.d.ts.map +1 -1
  71. package/lib/chain/options.js.map +1 -1
  72. package/lib/chain/prepareNextSlot.js +3 -3
  73. package/lib/chain/prepareNextSlot.js.map +1 -1
  74. package/lib/chain/produceBlock/computeNewStateRoot.d.ts +10 -2
  75. package/lib/chain/produceBlock/computeNewStateRoot.d.ts.map +1 -1
  76. package/lib/chain/produceBlock/computeNewStateRoot.js +24 -2
  77. package/lib/chain/produceBlock/computeNewStateRoot.js.map +1 -1
  78. package/lib/chain/produceBlock/produceBlockBody.d.ts +22 -7
  79. package/lib/chain/produceBlock/produceBlockBody.d.ts.map +1 -1
  80. package/lib/chain/produceBlock/produceBlockBody.js +110 -10
  81. package/lib/chain/produceBlock/produceBlockBody.js.map +1 -1
  82. package/lib/chain/validation/attestation.d.ts.map +1 -1
  83. package/lib/chain/validation/attestation.js +4 -1
  84. package/lib/chain/validation/attestation.js.map +1 -1
  85. package/lib/chain/validation/attesterSlashing.js +1 -1
  86. package/lib/chain/validation/attesterSlashing.js.map +1 -1
  87. package/lib/chain/validation/dataColumnSidecar.d.ts +2 -2
  88. package/lib/chain/validation/dataColumnSidecar.d.ts.map +1 -1
  89. package/lib/chain/validation/dataColumnSidecar.js.map +1 -1
  90. package/lib/chain/validation/payloadAttestationMessage.js +8 -1
  91. package/lib/chain/validation/payloadAttestationMessage.js.map +1 -1
  92. package/lib/chain/validation/proposerSlashing.js +1 -1
  93. package/lib/chain/validation/proposerSlashing.js.map +1 -1
  94. package/lib/chain/validation/syncCommitteeContributionAndProof.js +1 -1
  95. package/lib/db/beacon.d.ts +3 -1
  96. package/lib/db/beacon.d.ts.map +1 -1
  97. package/lib/db/beacon.js +5 -1
  98. package/lib/db/beacon.js.map +1 -1
  99. package/lib/db/buckets.d.ts +3 -1
  100. package/lib/db/buckets.d.ts.map +1 -1
  101. package/lib/db/buckets.js +2 -0
  102. package/lib/db/buckets.js.map +1 -1
  103. package/lib/db/interface.d.ts +3 -1
  104. package/lib/db/interface.d.ts.map +1 -1
  105. package/lib/db/repositories/blockArchiveIndex.d.ts +2 -2
  106. package/lib/db/repositories/blockArchiveIndex.d.ts.map +1 -1
  107. package/lib/db/repositories/dataColumnSidecar.d.ts +5 -3
  108. package/lib/db/repositories/dataColumnSidecar.d.ts.map +1 -1
  109. package/lib/db/repositories/dataColumnSidecar.js +14 -1
  110. package/lib/db/repositories/dataColumnSidecar.js.map +1 -1
  111. package/lib/db/repositories/dataColumnSidecarArchive.d.ts +5 -3
  112. package/lib/db/repositories/dataColumnSidecarArchive.d.ts.map +1 -1
  113. package/lib/db/repositories/dataColumnSidecarArchive.js +14 -1
  114. package/lib/db/repositories/dataColumnSidecarArchive.js.map +1 -1
  115. package/lib/db/repositories/executionPayloadEnvelope.d.ts +19 -0
  116. package/lib/db/repositories/executionPayloadEnvelope.d.ts.map +1 -0
  117. package/lib/db/repositories/executionPayloadEnvelope.js +22 -0
  118. package/lib/db/repositories/executionPayloadEnvelope.js.map +1 -0
  119. package/lib/db/repositories/executionPayloadEnvelopeArchive.d.ts +18 -0
  120. package/lib/db/repositories/executionPayloadEnvelopeArchive.d.ts.map +1 -0
  121. package/lib/db/repositories/executionPayloadEnvelopeArchive.js +28 -0
  122. package/lib/db/repositories/executionPayloadEnvelopeArchive.js.map +1 -0
  123. package/lib/db/repositories/index.d.ts +2 -0
  124. package/lib/db/repositories/index.d.ts.map +1 -1
  125. package/lib/db/repositories/index.js +2 -0
  126. package/lib/db/repositories/index.js.map +1 -1
  127. package/lib/execution/engine/http.d.ts +1 -0
  128. package/lib/execution/engine/http.d.ts.map +1 -1
  129. package/lib/execution/engine/http.js +3 -0
  130. package/lib/execution/engine/http.js.map +1 -1
  131. package/lib/metrics/metrics/beacon.d.ts +1 -0
  132. package/lib/metrics/metrics/beacon.d.ts.map +1 -1
  133. package/lib/metrics/metrics/beacon.js +5 -0
  134. package/lib/metrics/metrics/beacon.js.map +1 -1
  135. package/lib/metrics/metrics/lodestar.d.ts +8 -0
  136. package/lib/metrics/metrics/lodestar.d.ts.map +1 -1
  137. package/lib/metrics/metrics/lodestar.js +14 -0
  138. package/lib/metrics/metrics/lodestar.js.map +1 -1
  139. package/lib/metrics/metrics.d.ts.map +1 -1
  140. package/lib/metrics/metrics.js +8 -3
  141. package/lib/metrics/metrics.js.map +1 -1
  142. package/lib/monitoring/service.d.ts +2 -2
  143. package/lib/monitoring/service.d.ts.map +1 -1
  144. package/lib/monitoring/service.js +3 -2
  145. package/lib/monitoring/service.js.map +1 -1
  146. package/lib/network/gossip/interface.d.ts +3 -3
  147. package/lib/network/gossip/interface.d.ts.map +1 -1
  148. package/lib/network/gossip/topic.d.ts +113 -63
  149. package/lib/network/gossip/topic.d.ts.map +1 -1
  150. package/lib/network/gossip/topic.js +2 -2
  151. package/lib/network/gossip/topic.js.map +1 -1
  152. package/lib/network/interface.d.ts +3 -2
  153. package/lib/network/interface.d.ts.map +1 -1
  154. package/lib/network/network.d.ts +3 -2
  155. package/lib/network/network.d.ts.map +1 -1
  156. package/lib/network/network.js +10 -1
  157. package/lib/network/network.js.map +1 -1
  158. package/lib/network/peers/peerManager.d.ts +2 -0
  159. package/lib/network/peers/peerManager.d.ts.map +1 -1
  160. package/lib/network/peers/peerManager.js +54 -26
  161. package/lib/network/peers/peerManager.js.map +1 -1
  162. package/lib/network/processor/gossipHandlers.d.ts.map +1 -1
  163. package/lib/network/processor/gossipHandlers.js +5 -1
  164. package/lib/network/processor/gossipHandlers.js.map +1 -1
  165. package/lib/network/reqresp/handlers/lightClientUpdatesByRange.d.ts.map +1 -1
  166. package/lib/network/reqresp/handlers/lightClientUpdatesByRange.js +7 -1
  167. package/lib/network/reqresp/handlers/lightClientUpdatesByRange.js.map +1 -1
  168. package/lib/node/nodejs.d.ts +3 -5
  169. package/lib/node/nodejs.d.ts.map +1 -1
  170. package/lib/node/nodejs.js +6 -4
  171. package/lib/node/nodejs.js.map +1 -1
  172. package/lib/util/blobs.d.ts +2 -2
  173. package/lib/util/blobs.d.ts.map +1 -1
  174. package/lib/util/blobs.js.map +1 -1
  175. package/lib/util/dataColumns.d.ts +11 -3
  176. package/lib/util/dataColumns.d.ts.map +1 -1
  177. package/lib/util/dataColumns.js +27 -0
  178. package/lib/util/dataColumns.js.map +1 -1
  179. package/lib/util/multifork.d.ts +8 -0
  180. package/lib/util/multifork.d.ts.map +1 -1
  181. package/lib/util/multifork.js +37 -0
  182. package/lib/util/multifork.js.map +1 -1
  183. package/lib/util/serializedCache.d.ts +5 -0
  184. package/lib/util/serializedCache.d.ts.map +1 -1
  185. package/lib/util/serializedCache.js +5 -0
  186. package/lib/util/serializedCache.js.map +1 -1
  187. package/package.json +15 -15
  188. package/src/api/impl/beacon/blocks/index.ts +145 -2
  189. package/src/api/impl/beacon/state/index.ts +8 -8
  190. package/src/api/impl/beacon/state/utils.ts +15 -29
  191. package/src/api/impl/debug/index.ts +8 -5
  192. package/src/api/impl/lightclient/index.ts +19 -2
  193. package/src/api/impl/validator/index.ts +127 -5
  194. package/src/chain/archiveStore/archiveStore.ts +10 -0
  195. package/src/chain/archiveStore/historicalState/getHistoricalState.ts +10 -11
  196. package/src/chain/archiveStore/historicalState/worker.ts +3 -3
  197. package/src/chain/archiveStore/utils/archivePayloads.ts +15 -0
  198. package/src/chain/blocks/importBlock.ts +0 -3
  199. package/src/chain/blocks/index.ts +2 -1
  200. package/src/chain/blocks/writeBlockInputToDb.ts +3 -0
  201. package/src/chain/bls/multithread/index.ts +7 -7
  202. package/src/chain/bls/multithread/jobItem.ts +3 -3
  203. package/src/chain/bls/singleThread.ts +5 -5
  204. package/src/chain/bls/utils.ts +8 -5
  205. package/src/chain/chain.ts +51 -26
  206. package/src/chain/emitter.ts +2 -2
  207. package/src/chain/interface.ts +4 -11
  208. package/src/chain/lightClient/index.ts +4 -1
  209. package/src/chain/options.ts +1 -0
  210. package/src/chain/prepareNextSlot.ts +5 -5
  211. package/src/chain/produceBlock/computeNewStateRoot.ts +35 -3
  212. package/src/chain/produceBlock/produceBlockBody.ts +163 -13
  213. package/src/chain/validation/attestation.ts +4 -1
  214. package/src/chain/validation/attesterSlashing.ts +1 -1
  215. package/src/chain/validation/dataColumnSidecar.ts +2 -5
  216. package/src/chain/validation/payloadAttestationMessage.ts +9 -1
  217. package/src/chain/validation/proposerSlashing.ts +1 -1
  218. package/src/chain/validation/syncCommitteeContributionAndProof.ts +1 -1
  219. package/src/db/beacon.ts +8 -0
  220. package/src/db/buckets.ts +3 -0
  221. package/src/db/interface.ts +5 -0
  222. package/src/db/repositories/dataColumnSidecar.ts +18 -3
  223. package/src/db/repositories/dataColumnSidecarArchive.ts +18 -3
  224. package/src/db/repositories/executionPayloadEnvelope.ts +26 -0
  225. package/src/db/repositories/executionPayloadEnvelopeArchive.ts +32 -0
  226. package/src/db/repositories/index.ts +2 -0
  227. package/src/execution/engine/http.ts +3 -0
  228. package/src/metrics/metrics/beacon.ts +5 -0
  229. package/src/metrics/metrics/lodestar.ts +14 -0
  230. package/src/metrics/metrics.ts +8 -3
  231. package/src/monitoring/service.ts +3 -2
  232. package/src/network/gossip/interface.ts +3 -3
  233. package/src/network/gossip/topic.ts +2 -1
  234. package/src/network/interface.ts +4 -1
  235. package/src/network/network.ts +21 -3
  236. package/src/network/peers/peerManager.ts +65 -27
  237. package/src/network/processor/gossipHandlers.ts +7 -1
  238. package/src/network/reqresp/handlers/lightClientUpdatesByRange.ts +6 -1
  239. package/src/node/nodejs.ts +8 -9
  240. package/src/util/blobs.ts +3 -3
  241. package/src/util/dataColumns.ts +37 -1
  242. package/src/util/multifork.ts +45 -0
  243. package/src/util/serializedCache.ts +5 -0
@@ -9,6 +9,7 @@ import {prettyPrintIndices, toHex, withTimeout} from "@lodestar/utils";
9
9
  import {GOODBYE_KNOWN_CODES, GoodByeReasonCode, Libp2pEvent} from "../../constants/index.js";
10
10
  import {IClock} from "../../util/clock.js";
11
11
  import {computeColumnsForCustodyGroup, getCustodyGroups} from "../../util/dataColumns.js";
12
+ import {callInNextEventLoop} from "../../util/eventLoop.js";
12
13
  import {NetworkCoreMetrics} from "../core/metrics.js";
13
14
  import {LodestarDiscv5Opts} from "../discv5/types.js";
14
15
  import {INetworkEventBus, NetworkEvent, NetworkEventData} from "../events.js";
@@ -196,6 +197,13 @@ export class PeerManager {
196
197
 
197
198
  this.lastStatus = this.statusCache.get();
198
199
 
200
+ // A connection may already be open before listeners are attached.
201
+ // Seed those peers so they are tracked in connectedPeers immediately.
202
+ this.bootstrapAlreadyOpenConnections();
203
+ // Defer status/ping to the next event loop tick so the heartbeat interval and
204
+ // event listeners are fully registered before we begin handshakes.
205
+ callInNextEventLoop(() => this.pingAndStatusTimeouts());
206
+
199
207
  // On start-up will connected to existing peers in libp2p.peerStore, same as autoDial behaviour
200
208
  this.heartbeat();
201
209
  this.intervals = [
@@ -692,36 +700,39 @@ export class PeerManager {
692
700
  }
693
701
  }
694
702
 
695
- /**
696
- * The libp2p Upgrader has successfully upgraded a peer connection on a particular multiaddress
697
- * This event is routed through the connectionManager
698
- *
699
- * Registers a peer as connected. The `direction` parameter determines if the peer is being
700
- * dialed or connecting to us.
701
- */
702
- private onLibp2pPeerConnect = async (evt: CustomEvent<Connection>): Promise<void> => {
703
- const {direction, status, remotePeer} = evt.detail;
703
+ private bootstrapAlreadyOpenConnections(): void {
704
+ let bootstrapped = 0;
705
+
706
+ for (const {value: connections} of getConnectionsMap(this.libp2p).values()) {
707
+ for (const connection of connections) {
708
+ // trackLibp2pConnection handles deduplication via overwriteExisting: false
709
+ if (this.trackLibp2pConnection(connection, {overwriteExisting: false, triggerHandshakeNow: false})) {
710
+ bootstrapped++;
711
+ }
712
+ }
713
+ }
714
+
715
+ if (bootstrapped > 0) {
716
+ this.logger.verbose("Bootstrapped already-open libp2p peers", {bootstrapped});
717
+ }
718
+ }
719
+
720
+ private trackLibp2pConnection(
721
+ connection: Connection,
722
+ opts: {overwriteExisting: boolean; triggerHandshakeNow: boolean}
723
+ ): boolean {
724
+ const {direction, status, remotePeer} = connection;
704
725
  const remotePeerStr = remotePeer.toString();
705
726
  const remotePeerPrettyStr = prettyPrintPeerId(remotePeer);
706
- this.logger.verbose("peer connected", {peer: remotePeerPrettyStr, direction, status});
707
- // NOTE: The peerConnect event is not emitted here here, but after asserting peer relevance
708
- this.metrics?.peerConnectedEvent.inc({direction, status});
709
727
 
710
- if (evt.detail.status !== "open") {
728
+ if (status !== "open") {
711
729
  this.logger.debug("Peer disconnected before identify protocol initiated", {
712
730
  peerId: remotePeerPrettyStr,
713
- status: evt.detail.status,
731
+ status,
714
732
  });
715
- return;
733
+ return false;
716
734
  }
717
735
 
718
- // On connection:
719
- // - Outbound connections: send a STATUS and PING request
720
- // - Inbound connections: expect to be STATUS'd, schedule STATUS and PING for latter
721
- // NOTE: libp2p may emit two "peer:connect" events: One for inbound, one for outbound
722
- // If that happens, it's okay. Only the "outbound" connection triggers immediate action
723
- const now = Date.now();
724
-
725
736
  // Ethereum uses secp256k1 for node IDs, reject peers with other key types
726
737
  if (remotePeer.type !== "secp256k1") {
727
738
  this.logger.debug("Peer does not have secp256k1 key, disconnecting", {
@@ -729,9 +740,19 @@ export class PeerManager {
729
740
  type: remotePeer.type,
730
741
  });
731
742
  void this.goodbyeAndDisconnect(remotePeer, GoodByeReasonCode.IRRELEVANT_NETWORK);
732
- return;
743
+ return false;
733
744
  }
734
745
 
746
+ if (!opts.overwriteExisting && this.connectedPeers.has(remotePeerStr)) {
747
+ return false;
748
+ }
749
+
750
+ // On connection:
751
+ // - Outbound connections: send a STATUS and PING request
752
+ // - Inbound connections: expect to be STATUS'd, schedule STATUS and PING for later
753
+ // NOTE: libp2p may emit two "peer:connect" events: One for inbound, one for outbound
754
+ // If that happens, it's okay. Only the "outbound" connection triggers immediate action
755
+ const now = Date.now();
735
756
  const nodeId = computeNodeId(remotePeer);
736
757
  const peerData: PeerData = {
737
758
  lastReceivedMsgUnixTsMs: direction === "outbound" ? 0 : now,
@@ -750,14 +771,13 @@ export class PeerManager {
750
771
  };
751
772
  this.connectedPeers.set(remotePeerStr, peerData);
752
773
 
753
- if (direction === "outbound") {
754
- // this.pingAndStatusTimeouts();
774
+ if (direction === "outbound" && opts.triggerHandshakeNow) {
755
775
  void this.requestPing(remotePeer);
756
776
  void this.requestStatus(remotePeer, this.statusCache.get());
757
777
  }
758
778
 
759
779
  this.libp2p.services.identify
760
- .identify(evt.detail)
780
+ .identify(connection)
761
781
  .then((result) => {
762
782
  const agentVersion = result.agentVersion;
763
783
  if (agentVersion) {
@@ -766,7 +786,7 @@ export class PeerManager {
766
786
  }
767
787
  })
768
788
  .catch((err) => {
769
- if (evt.detail.status !== "open") {
789
+ if (connection.status !== "open") {
770
790
  this.logger.debug("Peer disconnected during identify protocol", {
771
791
  peerId: remotePeerPrettyStr,
772
792
  error: (err as Error).message,
@@ -775,6 +795,24 @@ export class PeerManager {
775
795
  this.logger.debug("Error setting agentVersion for the peer", {peerId: remotePeerPrettyStr}, err);
776
796
  }
777
797
  });
798
+
799
+ return true;
800
+ }
801
+
802
+ /**
803
+ * The libp2p Upgrader has successfully upgraded a peer connection on a particular multiaddress
804
+ * This event is routed through the connectionManager
805
+ *
806
+ * Registers a peer as connected. The `direction` parameter determines if the peer is being
807
+ * dialed or connecting to us.
808
+ */
809
+ private onLibp2pPeerConnect = (evt: CustomEvent<Connection>): void => {
810
+ const {direction, status, remotePeer} = evt.detail;
811
+ this.logger.verbose("peer connected", {peer: prettyPrintPeerId(remotePeer), direction, status});
812
+ // NOTE: The peerConnect event is not emitted here here, but after asserting peer relevance
813
+ this.metrics?.peerConnectedEvent.inc({direction, status});
814
+
815
+ this.trackLibp2pConnection(evt.detail, {overwriteExisting: true, triggerHandshakeNow: true});
778
816
  };
779
817
 
780
818
  /**
@@ -548,7 +548,8 @@ function getSequentialHandlers(modules: ValidatorFnsModules, options: GossipHand
548
548
  seenTimestampSec,
549
549
  }: GossipHandlerParamGeneric<GossipType.data_column_sidecar>) => {
550
550
  const {serializedData} = gossipData;
551
- const dataColumnSidecar = sszDeserialize(topic, serializedData);
551
+ // TODO GLOAS: handle gloas.DataColumnSidecar
552
+ const dataColumnSidecar = sszDeserialize(topic, serializedData) as fulu.DataColumnSidecar;
552
553
  const dataColumnSlot = dataColumnSidecar.signedBlockHeader.message.slot;
553
554
  const index = dataColumnSidecar.index;
554
555
 
@@ -821,11 +822,16 @@ function getSequentialHandlers(modules: ValidatorFnsModules, options: GossipHand
821
822
  [GossipType.execution_payload]: async ({
822
823
  gossipData,
823
824
  topic,
825
+ seenTimestampSec,
824
826
  }: GossipHandlerParamGeneric<GossipType.execution_payload>) => {
825
827
  const {serializedData} = gossipData;
826
828
  const executionPayloadEnvelope = sszDeserialize(topic, serializedData);
827
829
  await validateGossipExecutionPayloadEnvelope(chain, executionPayloadEnvelope);
828
830
 
831
+ const slot = executionPayloadEnvelope.message.slot;
832
+ const delaySec = seenTimestampSec - computeTimeAtSlot(config, slot, chain.genesisTime);
833
+ metrics?.gossipExecutionPayloadEnvelope.elapsedTimeTillReceived.observe({source: OpSource.gossip}, delaySec);
834
+
829
835
  // TODO GLOAS: Handle valid envelope. Need an import flow that calls `processExecutionPayloadEnvelope` and fork choice
830
836
  },
831
837
  [GossipType.payload_attestation_message]: async ({
@@ -19,6 +19,7 @@ export async function* onLightClientUpdatesByRange(
19
19
  assertLightClientServer(chain.lightClientServer);
20
20
 
21
21
  const count = Math.min(MAX_REQUEST_LIGHT_CLIENT_UPDATES, requestBody.count);
22
+ let started = false;
22
23
  for (let period = requestBody.startPeriod; period < requestBody.startPeriod + count; period++) {
23
24
  try {
24
25
  const update = await chain.lightClientServer.getUpdate(period);
@@ -29,9 +30,13 @@ export async function* onLightClientUpdatesByRange(
29
30
  data: type.serialize(update),
30
31
  boundary,
31
32
  };
33
+ started = true;
32
34
  } catch (e) {
33
35
  if ((e as LightClientServerError).type?.code === LightClientServerErrorCode.RESOURCE_UNAVAILABLE) {
34
- throw new ResponseError(RespStatus.RESOURCE_UNAVAILABLE, (e as Error).message);
36
+ // Period not available, if we already started yielding, stop to
37
+ // preserve consecutive order. Otherwise skip leading gaps.
38
+ if (started) return;
39
+ continue;
35
40
  }
36
41
  throw new ResponseError(RespStatus.SERVER_ERROR, (e as Error).message);
37
42
  }
@@ -2,12 +2,11 @@ import {setMaxListeners} from "node:events";
2
2
  import {PrivateKey} from "@libp2p/interface";
3
3
  import {Registry} from "prom-client";
4
4
  import {hasher} from "@chainsafe/persistent-merkle-tree";
5
- import {PubkeyIndexMap} from "@chainsafe/pubkey-index-map";
6
5
  import {BeaconApiMethods} from "@lodestar/api/beacon/server";
7
6
  import {BeaconConfig} from "@lodestar/config";
8
7
  import type {LoggerNode} from "@lodestar/logger/node";
9
8
  import {ZERO_HASH_HEX} from "@lodestar/params";
10
- import {CachedBeaconStateAllForks, Index2PubkeyCache, isExecutionCachedStateType} from "@lodestar/state-transition";
9
+ import {CachedBeaconStateAllForks, PubkeyCache, isExecutionCachedStateType} from "@lodestar/state-transition";
11
10
  import {phase0} from "@lodestar/types";
12
11
  import {sleep, toRootHex} from "@lodestar/utils";
13
12
  import {ProcessShutdownCallback} from "@lodestar/validator";
@@ -47,8 +46,7 @@ export type BeaconNodeModules = {
47
46
  export type BeaconNodeInitModules = {
48
47
  opts: IBeaconNodeOptions;
49
48
  config: BeaconConfig;
50
- pubkey2index: PubkeyIndexMap;
51
- index2pubkey: Index2PubkeyCache;
49
+ pubkeyCache: PubkeyCache;
52
50
  db: IBeaconDb;
53
51
  logger: LoggerNode;
54
52
  processShutdownCallback: ProcessShutdownCallback;
@@ -150,8 +148,7 @@ export class BeaconNode {
150
148
  static async init<T extends BeaconNode = BeaconNode>({
151
149
  opts,
152
150
  config,
153
- pubkey2index,
154
- index2pubkey,
151
+ pubkeyCache,
155
152
  db,
156
153
  logger,
157
154
  processShutdownCallback,
@@ -240,8 +237,7 @@ export class BeaconNode {
240
237
  privateKey,
241
238
  config,
242
239
  clock,
243
- pubkey2index,
244
- index2pubkey,
240
+ pubkeyCache,
245
241
  dataDir,
246
242
  db,
247
243
  dbName: opts.db.name,
@@ -364,9 +360,12 @@ export class BeaconNode {
364
360
  if (this.restApi) await this.restApi.close();
365
361
  await this.network.close();
366
362
  if (this.metricsServer) await this.metricsServer.close();
367
- if (this.monitoring) this.monitoring.close();
363
+ if (this.monitoring) await this.monitoring.close();
368
364
  await this.chain.persistToDisk();
369
365
  await this.chain.close();
366
+ // Abort signal last: close() calls above clear intervals/timeouts so no new
367
+ // operations get scheduled. If we aborted first, a still-pending interval could
368
+ // fire and schedule a new operation after abort, leaving it stuck and delaying shutdown.
370
369
  if (this.controller) this.controller.abort();
371
370
  await sleep(DELAY_BEFORE_CLOSING_DB_MS);
372
371
  await this.db.close();
package/src/util/blobs.ts CHANGED
@@ -13,7 +13,7 @@ import {
13
13
  VERSIONED_HASH_VERSION_KZG,
14
14
  } from "@lodestar/params";
15
15
  import {signedBlockToSignedHeader} from "@lodestar/state-transition";
16
- import {BeaconBlockBody, SSZTypesFor, SignedBeaconBlock, deneb, fulu, ssz} from "@lodestar/types";
16
+ import {BeaconBlockBody, DataColumnSidecars, SSZTypesFor, SignedBeaconBlock, deneb, fulu, ssz} from "@lodestar/types";
17
17
  import {kzg} from "./kzg.js";
18
18
 
19
19
  type VersionHash = Uint8Array;
@@ -149,7 +149,7 @@ export async function dataColumnMatrixRecovery(
149
149
  * Reconstruct blobs from a set of data columns, at least 50%+ of all the columns
150
150
  * must be provided to allow to reconstruct the full data matrix
151
151
  */
152
- export async function reconstructBlobs(sidecars: fulu.DataColumnSidecars, indices?: number[]): Promise<deneb.Blobs> {
152
+ export async function reconstructBlobs(sidecars: DataColumnSidecars, indices?: number[]): Promise<deneb.Blobs> {
153
153
  if (sidecars.length < NUMBER_OF_COLUMNS / 2) {
154
154
  throw Error(
155
155
  `Expected at least ${NUMBER_OF_COLUMNS / 2} data columns to reconstruct blobs, received ${sidecars.length}`
@@ -188,7 +188,7 @@ export async function reconstructBlobs(sidecars: fulu.DataColumnSidecars, indice
188
188
  * Recover cells for specific blob indices from a set of data columns
189
189
  */
190
190
  async function recoverBlobCells(
191
- partialSidecars: fulu.DataColumnSidecar[],
191
+ partialSidecars: DataColumnSidecars,
192
192
  blobIndices: number[]
193
193
  ): Promise<Map<number, fulu.Cell[]> | null> {
194
194
  const columnCount = partialSidecars.length;
@@ -4,6 +4,7 @@ import {ChainForkConfig} from "@lodestar/config";
4
4
  import {
5
5
  ForkAll,
6
6
  ForkName,
7
+ ForkPostDeneb,
7
8
  ForkPostFulu,
8
9
  ForkPreGloas,
9
10
  KZG_COMMITMENTS_GINDEX,
@@ -15,9 +16,11 @@ import {
15
16
  BeaconBlockBody,
16
17
  ColumnIndex,
17
18
  CustodyIndex,
19
+ Root,
18
20
  SSZTypesFor,
19
21
  SignedBeaconBlock,
20
22
  SignedBeaconBlockHeader,
23
+ Slot,
21
24
  deneb,
22
25
  fulu,
23
26
  gloas,
@@ -269,7 +272,7 @@ export async function getCellsAndProofs(
269
272
  */
270
273
  export function getBlobKzgCommitments(
271
274
  fork: ForkName,
272
- signedBlock: SignedBeaconBlock<ForkPostFulu>
275
+ signedBlock: SignedBeaconBlock<ForkPostDeneb>
273
276
  ): deneb.KZGCommitment[] {
274
277
  if (isForkPostGloas(fork)) {
275
278
  return (signedBlock as gloas.SignedBeaconBlock).message.body.signedExecutionPayloadBid.message.blobKzgCommitments;
@@ -359,6 +362,39 @@ export function getDataColumnSidecarsFromColumnSidecar(
359
362
  );
360
363
  }
361
364
 
365
+ /**
366
+ * In Gloas, data column sidecars have a simplified structure with `slot` and `beaconBlockRoot`
367
+ * instead of `signedBlockHeader`, `kzgCommitments`, and `kzgCommitmentsInclusionProof`.
368
+ */
369
+ export function getDataColumnSidecarsForGloas(
370
+ slot: Slot,
371
+ beaconBlockRoot: Root,
372
+ cellsAndKzgProofs: {cells: Uint8Array[]; proofs: Uint8Array[]}[]
373
+ ): gloas.DataColumnSidecars {
374
+ // No need to create data column sidecars if there are no blobs
375
+ if (cellsAndKzgProofs.length === 0) {
376
+ return [];
377
+ }
378
+
379
+ const sidecars: gloas.DataColumnSidecars = [];
380
+ for (let columnIndex = 0; columnIndex < NUMBER_OF_COLUMNS; columnIndex++) {
381
+ const column: Uint8Array[] = [];
382
+ const kzgProofs: Uint8Array[] = [];
383
+ for (const {cells, proofs} of cellsAndKzgProofs) {
384
+ column.push(cells[columnIndex]);
385
+ kzgProofs.push(proofs[columnIndex]);
386
+ }
387
+ sidecars.push({
388
+ index: columnIndex,
389
+ column,
390
+ kzgProofs,
391
+ slot,
392
+ beaconBlockRoot,
393
+ });
394
+ }
395
+ return sidecars;
396
+ }
397
+
362
398
  /**
363
399
  * If we receive more than half of NUMBER_OF_COLUMNS (64) we should recover all remaining columns
364
400
  */
@@ -9,6 +9,11 @@ import {getSlotFromSignedBeaconBlockSerialized} from "./sszBytes.js";
9
9
  */
10
10
  const SLOT_BYTE_COUNT = 8;
11
11
 
12
+ /**
13
+ * SSZ offset uint32
14
+ */
15
+ const OFFSET_BYTE_COUNT = 4;
16
+
12
17
  /**
13
18
  * 8 + 32 = 40
14
19
  * ```
@@ -67,3 +72,43 @@ export function getLightClientHeaderTypeFromBytes(
67
72
  );
68
73
  return config.getPostAltairForkTypes(slot).LightClientHeader;
69
74
  }
75
+
76
+ /**
77
+ * Position of first offset in DataColumnSidecar (after index field)
78
+ *
79
+ * Fulu DataColumnSidecar (6 fields):
80
+ * index: uint64 [fixed - 8 bytes]
81
+ * column: List [variable - 4-byte offset]
82
+ * kzgCommitments: List [variable - 4-byte offset]
83
+ * kzgProofs: List [variable - 4-byte offset]
84
+ * signedBlockHeader: Container [fixed - 208 bytes]
85
+ * kzgCommitmentsInclusionProof: Vector[Bytes32, 4] [fixed - 128 bytes]
86
+ * => First offset value = 8 + 4 + 4 + 4 + 208 + 128 = 356
87
+ *
88
+ * Gloas DataColumnSidecar (5 fields):
89
+ * index: uint64 [fixed - 8 bytes]
90
+ * column: List [variable - 4-byte offset]
91
+ * kzgProofs: List [variable - 4-byte offset]
92
+ * slot: uint64 [fixed - 8 bytes]
93
+ * beaconBlockRoot: Bytes32 [fixed - 32 bytes]
94
+ * => First offset value = 8 + 4 + 4 + 8 + 32 = 56
95
+ */
96
+ const FIRST_OFFSET_POSITION_IN_DATA_COLUMN_SIDECAR = 8;
97
+ const GLOAS_DATA_COLUMN_SIDECAR_FIRST_OFFSET = 56;
98
+
99
+ /**
100
+ * Determines if DataColumnSidecar bytes are from Gloas fork by checking the SSZ offset structure.
101
+ *
102
+ * The first offset (bytes 8-12) indicates where variable-size data begins:
103
+ * - Gloas: 56 (small fixed section)
104
+ * - Fulu: 356
105
+ */
106
+ export function isGloasDataColumnSidecarBytes(bytes: Uint8Array): boolean {
107
+ const firstOffset = bytesToInt(
108
+ bytes.subarray(
109
+ FIRST_OFFSET_POSITION_IN_DATA_COLUMN_SIDECAR,
110
+ FIRST_OFFSET_POSITION_IN_DATA_COLUMN_SIDECAR + OFFSET_BYTE_COUNT
111
+ )
112
+ );
113
+ return firstOffset === GLOAS_DATA_COLUMN_SIDECAR_FIRST_OFFSET;
114
+ }
@@ -14,6 +14,11 @@ export class SerializedCache {
14
14
  this.map.set(obj, serialized);
15
15
  }
16
16
 
17
+ /**
18
+ * Replace the internal WeakMap to force GC of all cached entries.
19
+ * Must only be called after all DB writes that may read from this cache have completed,
20
+ * otherwise cached serialized bytes will be unavailable and data will be re-serialized unnecessarily.
21
+ */
17
22
  clear(): void {
18
23
  this.map = new WeakMap();
19
24
  }