UNPKG

@lodestar/beacon-node

Version:

A Typescript implementation of the beacon chain

134 lines 6.98 kB
import fs from "node:fs"; import path from "node:path"; import worker from "node:worker_threads"; import { expose } from "@chainsafe/threads/worker"; import { privateKeyFromProtobuf } from "@libp2p/crypto/keys"; import { peerIdFromPrivateKey } from "@libp2p/peer-id"; import { chainConfigFromJson, createBeaconConfig } from "@lodestar/config"; import { getNodeLogger } from "@lodestar/logger/node"; import { RegistryMetricCreator, collectNodeJSMetrics } from "../../metrics/index.js"; import { AsyncIterableBridgeCaller, AsyncIterableBridgeHandler } from "../../util/asyncIterableToEvents.js"; import { Clock } from "../../util/clock.js"; import { peerIdToString } from "../../util/peerId.js"; import { profileNodeJS, writeHeapSnapshot } from "../../util/profile.js"; import { wireEventsOnWorkerThread } from "../../util/workerEvents.js"; import { NetworkEventBus, networkEventDirection } from "../events.js"; import { NetworkWorkerThreadEventType, ReqRespBridgeEventBus, getReqRespBridgeReqEvents, getReqRespBridgeRespEvents, reqRespBridgeEventDirection, } from "./events.js"; import { getNetworkCoreWorkerMetrics } from "./metrics.js"; import { NetworkCore } from "./networkCore.js"; // Cloned data from instantiation const workerData = worker.workerData; const parentPort = worker.parentPort; if (!workerData) throw Error("workerData must be defined"); if (!parentPort) throw Error("parentPort must be defined"); const config = createBeaconConfig(chainConfigFromJson(workerData.chainConfigJson), workerData.genesisValidatorsRoot); const privateKey = privateKeyFromProtobuf(workerData.privateKeyProto); const peerId = peerIdFromPrivateKey(privateKey); // TODO: Pass options from main thread for logging // TODO: Logging won't be visible in file loggers const logger = getNodeLogger(workerData.loggerOpts); // Alive and consistency check logger.info("libp2p worker started", { peer: peerIdToString(peerId) }); const abortController = new AbortController(); // Set up metrics, nodejs and discv5-specific const metricsRegister = workerData.metricsEnabled ? new RegistryMetricCreator() : null; if (metricsRegister) { const closeMetrics = collectNodeJSMetrics(metricsRegister, "network_worker_"); abortController.signal.addEventListener("abort", closeMetrics, { once: true }); } // Main event bus shared across the stack const events = new NetworkEventBus(); const reqRespBridgeEventBus = new ReqRespBridgeEventBus(); const clock = new Clock({ config, genesisTime: workerData.genesisTime, signal: abortController.signal }); // ReqResp event bridge // // The ReqRespHandler module handles app-level requests / responses from other peers, // fetching state from the chain and database as needed. // // On the worker's side the ReqResp module will call the async generator when the stream // is ready to send responses to the multiplexed libp2p stream. // // - libp2p inbound stream opened for reqresp method // - request data fully streamed // - ResResp handler called with request data and started // - stream calls next() on handler // - handler fetches block from DB, yields value // - stream calls next() again // - handler fetches block from DB, yields value // (case a) // - remote peer disconnects, stream aborted, calls return() on handler // (case b) // - handler has yielded all blocks, returns // (case c) // - handler encounters error, throws new AsyncIterableBridgeHandler(getReqRespBridgeReqEvents(reqRespBridgeEventBus), (data) => core.sendReqRespRequest(data)); const reqRespBridgeRespCaller = new AsyncIterableBridgeCaller(getReqRespBridgeRespEvents(reqRespBridgeEventBus)); const networkCoreWorkerMetrics = metricsRegister ? getNetworkCoreWorkerMetrics(metricsRegister) : null; // respBridgeCaller metrics if (networkCoreWorkerMetrics) { networkCoreWorkerMetrics.reqRespBridgeRespCallerPending.addCollect(() => { networkCoreWorkerMetrics.reqRespBridgeRespCallerPending.set(reqRespBridgeRespCaller.pendingCount); }); } const core = await NetworkCore.init({ opts: workerData.opts, config, privateKey, peerStoreDir: workerData.peerStoreDir, logger, metricsRegistry: metricsRegister, events, clock, getReqRespHandler: (method) => (req, peerId) => reqRespBridgeRespCaller.getAsyncIterable({ method, req, peerId: peerIdToString(peerId) }), activeValidatorCount: workerData.activeValidatorCount, initialStatus: workerData.initialStatus, }); wireEventsOnWorkerThread(NetworkWorkerThreadEventType.networkEvent, events, parentPort, networkCoreWorkerMetrics, networkEventDirection); wireEventsOnWorkerThread(NetworkWorkerThreadEventType.reqRespBridgeEvents, reqRespBridgeEventBus, parentPort, networkCoreWorkerMetrics, reqRespBridgeEventDirection); const libp2pWorkerApi = { close: async () => { abortController.abort(); await core.close(); }, scrapeMetrics: () => core.scrapeMetrics(), updateStatus: (status) => core.updateStatus(status), prepareBeaconCommitteeSubnets: (subscriptions) => core.prepareBeaconCommitteeSubnets(subscriptions), prepareSyncCommitteeSubnets: (subscriptions) => core.prepareSyncCommitteeSubnets(subscriptions), reportPeer: (peer, action, actionName) => core.reportPeer(peer, action, actionName), reStatusPeers: (peers) => core.reStatusPeers(peers), subscribeGossipCoreTopics: () => core.subscribeGossipCoreTopics(), unsubscribeGossipCoreTopics: () => core.unsubscribeGossipCoreTopics(), // sendReqRespRequest - handled via events with AsyncIterableBridgeHandler publishGossip: (topic, data, opts) => core.publishGossip(topic, data, opts), // Debug getNetworkIdentity: () => core.getNetworkIdentity(), getConnectedPeers: () => core.getConnectedPeers(), getConnectedPeerCount: () => core.getConnectedPeerCount(), connectToPeer: (peer, multiaddr) => core.connectToPeer(peer, multiaddr), disconnectPeer: (peer) => core.disconnectPeer(peer), dumpPeers: () => core.dumpPeers(), dumpPeer: (peerIdStr) => core.dumpPeer(peerIdStr), dumpPeerScoreStats: () => core.dumpPeerScoreStats(), dumpGossipPeerScoreStats: () => core.dumpGossipPeerScoreStats(), dumpDiscv5KadValues: () => core.dumpDiscv5KadValues(), dumpMeshPeers: () => core.dumpMeshPeers(), writeProfile: async (durationMs, dirpath) => { const profile = await profileNodeJS(durationMs); const filePath = path.join(dirpath, `network_thread_${new Date().toISOString()}.cpuprofile`); fs.writeFileSync(filePath, profile); return filePath; }, writeDiscv5Profile: async (durationMs, dirpath) => { return core.writeDiscv5Profile(durationMs, dirpath); }, writeHeapSnapshot: async (prefix, dirpath) => { return writeHeapSnapshot(prefix, dirpath); }, writeDiscv5HeapSnapshot: async (prefix, dirpath) => { return core.writeDiscv5HeapSnapshot(prefix, dirpath); }, }; expose(libp2pWorkerApi); //# sourceMappingURL=networkCoreWorker.js.map