import worker from "node:worker_threads";
import { privateKeyFromProtobuf } from "@libp2p/crypto/keys";
import { peerIdFromPrivateKey } from "@libp2p/peer-id";
import { multiaddr } from "@multiformats/multiaddr";
import { Discv5 } from "@chainsafe/discv5";
import { SignableENR } from "@chainsafe/enr";
import { Observable, Subject } from "@chainsafe/threads/observable";
import { expose } from "@chainsafe/threads/worker";
import { createBeaconConfig } from "@lodestar/config";
import { getNodeLogger } from "@lodestar/logger/node";
import { RegistryMetricCreator } from "../../metrics/index.js";
import { collectNodeJSMetrics } from "../../metrics/nodeJsMetrics.js";
import { Clock } from "../../util/clock.js";
import { ProfileThread, profileThread, writeHeapSnapshot } from "../../util/profile.js";
import { ENRRelevance, enrRelevance } from "./utils.js";
// This discv5 worker will start discv5 on initialization (there is no `start` function to call)
// A consumer _should_ call `close` before terminating the worker to cleanly exit discv5 before destroying the thread
// A `setEnrValue` function is also provided to update the host ENR key-values shared in the discv5 network.
// Cloned data from instatiation
const workerData = worker.workerData;
if (!workerData)
    throw Error("workerData must be defined");
const logger = getNodeLogger(workerData.loggerOpts);
// Set up metrics, nodejs and discv5-specific
let metricsRegistry;
let enrRelevanceMetric;
let closeMetrics;
if (workerData.metrics) {
    metricsRegistry = new RegistryMetricCreator();
    closeMetrics = collectNodeJSMetrics(metricsRegistry, "discv5_worker_");
    // add enr relevance metric
    enrRelevanceMetric = metricsRegistry.gauge({
        name: "lodestar_discv5_discovered_status_total_count",
        help: "Total count of status results of enrRelevance() function",
        labelNames: ["status"],
    });
}
const privateKey = privateKeyFromProtobuf(workerData.privateKeyProto);
const peerId = peerIdFromPrivateKey(privateKey);
const config = createBeaconConfig(workerData.chainConfig, workerData.genesisValidatorsRoot);
// Initialize discv5
const discv5 = Discv5.create({
    enr: SignableENR.decodeTxt(workerData.enr, privateKey.raw),
    privateKey,
    bindAddrs: {
        ip4: (workerData.bindAddrs.ip4 ? multiaddr(workerData.bindAddrs.ip4) : undefined),
        ip6: workerData.bindAddrs.ip6 ? multiaddr(workerData.bindAddrs.ip6) : undefined,
    },
    config: workerData.config,
    metricsRegistry,
});
// Load boot enrs
for (const bootEnr of workerData.bootEnrs) {
    discv5.addEnr(bootEnr);
}
/** Used to push discovered ENRs */
const subject = new Subject();
/** Define a new clock */
const abortController = new AbortController();
const clock = new Clock({ config, genesisTime: workerData.genesisTime, signal: abortController.signal });
const onDiscovered = (enr) => {
    const status = enrRelevance(enr, config, clock);
    enrRelevanceMetric?.inc({ status });
    if (status === ENRRelevance.relevant) {
        subject.next(enr.toObject());
    }
};
discv5.addListener("discovered", onDiscovered);
// Discv5 will now begin accepting request/responses
await discv5.start();
const module = {
    async enr() {
        return discv5.enr.toObject();
    },
    async setEnrValue(key, value) {
        discv5.enr.set(key, value);
    },
    async kadValues() {
        return discv5.kadValues().map((enr) => enr.toObject());
    },
    async discoverKadValues() {
        discv5.kadValues().map(onDiscovered);
    },
    async findRandomNode() {
        return (await discv5.findRandomNode()).map((enr) => enr.toObject());
    },
    discovered() {
        return Observable.from(subject);
    },
    async scrapeMetrics() {
        return (await metricsRegistry?.metrics()) ?? "";
    },
    writeProfile: async (durationMs, dirpath) => {
        return profileThread(ProfileThread.DISC5, durationMs, dirpath);
    },
    writeHeapSnapshot: async (prefix, dirpath) => {
        return writeHeapSnapshot(prefix, dirpath);
    },
    async close() {
        closeMetrics?.();
        discv5.removeListener("discovered", onDiscovered);
        subject.complete();
        await discv5.stop();
    },
};
expose(module);
const logData = {
    peerId: peerId.toString(),
    initialENR: workerData.enr,
};
if (workerData.bindAddrs.ip4)
    logData.bindAddr4 = workerData.bindAddrs.ip4;
if (workerData.bindAddrs.ip6)
    logData.bindAddr6 = workerData.bindAddrs.ip6;
logger.info("discv5 worker started", logData);
//# sourceMappingURL=worker.js.map