import { peerIdFromPrivateKey } from "@libp2p/peer-id";
import { routes } from "@lodestar/api";
import { ForkSeq } from "@lodestar/params";
import { computeEpochAtSlot } from "@lodestar/state-transition";
import { isGloasDataColumnSidecar, } from "@lodestar/types";
import { prettyPrintIndices, sleep } from "@lodestar/utils";
import { ChainEvent } from "../chain/index.js";
import { computeSubnetForDataColumnSidecar } from "../chain/validation/dataColumnSidecar.js";
import { RegistryMetricCreator } from "../metrics/index.js";
import { peerIdToString } from "../util/peerId.js";
import { promiseAllMaybeAsync } from "../util/promises.js";
import { NetworkCore, WorkerNetworkCore } from "./core/index.js";
import { NetworkEvent, NetworkEventBus } from "./events.js";
import { getActiveForkBoundaries } from "./forks.js";
import { GossipType } from "./gossip/index.js";
import { getGossipSSZType, gossipTopicIgnoreDuplicatePublishError, stringifyGossipTopic } from "./gossip/topic.js";
import { AggregatorTracker } from "./processor/aggregatorTracker.js";
import { NetworkProcessor } from "./processor/index.js";
import { ReqRespMethod } from "./reqresp/index.js";
import { Version, requestSszTypeByMethod, responseSszTypeByMethod } from "./reqresp/types.js";
import { collectExactOneTyped, collectMaxResponseTyped, collectMaxResponseTypedWithBytes, } from "./reqresp/utils/collect.js";
import { collectSequentialBlocksInRange } from "./reqresp/utils/collectSequentialBlocksInRange.js";
import { isPublishToZeroPeersError, prettyPrintPeerIdStr } from "./util.js";
/**
 * Must support running both on worker and on main thread.
 *
 * Exists a front class that's what consumers interact with.
 * This class will multiplex between:
 * - libp2p in worker
 * - libp2p in main thread
 */
export class Network {
    peerId;
    custodyConfig;
    // TODO: Make private
    events;
    logger;
    config;
    clock;
    chain;
    // Used only for sleep() statements
    controller;
    // TODO: Review
    networkProcessor;
    core;
    aggregatorTracker;
    subscribedToCoreTopics = false;
    connectedPeersSyncMeta = new Map();
    constructor(modules) {
        this.peerId = peerIdFromPrivateKey(modules.privateKey);
        this.config = modules.config;
        this.custodyConfig = modules.chain.custodyConfig;
        this.logger = modules.logger;
        this.chain = modules.chain;
        this.clock = modules.chain.clock;
        this.controller = new AbortController();
        this.events = modules.networkEventBus;
        this.networkProcessor = modules.networkProcessor;
        this.core = modules.core;
        this.aggregatorTracker = modules.aggregatorTracker;
        this.events.on(NetworkEvent.peerConnected, this.onPeerConnected);
        this.events.on(NetworkEvent.peerDisconnected, this.onPeerDisconnected);
        this.chain.emitter.on(routes.events.EventType.head, this.onHead);
        this.chain.emitter.on(routes.events.EventType.lightClientFinalityUpdate, ({ data }) => this.onLightClientFinalityUpdate(data));
        this.chain.emitter.on(routes.events.EventType.lightClientOptimisticUpdate, ({ data }) => this.onLightClientOptimisticUpdate(data));
        this.chain.emitter.on(ChainEvent.updateTargetCustodyGroupCount, this.onTargetGroupCountUpdated);
        this.chain.emitter.on(ChainEvent.publishDataColumns, this.onPublishDataColumns);
        this.chain.emitter.on(ChainEvent.publishBlobSidecars, this.onPublishBlobSidecars);
        this.chain.emitter.on(ChainEvent.updateStatus, this.onUpdateStatus);
    }
    static async init({ opts, config, logger, metrics, chain, db, gossipHandlers, privateKey, peerStoreDir, getReqRespHandler, }) {
        const events = new NetworkEventBus();
        const aggregatorTracker = new AggregatorTracker();
        const activeValidatorCount = chain.getHeadState().epochCtx.currentShuffling.activeIndices.length;
        const initialStatus = chain.getStatus();
        const initialCustodyGroupCount = chain.custodyConfig.targetCustodyGroupCount;
        if (opts.useWorker) {
            logger.info("running libp2p instance in worker thread");
        }
        const core = opts.useWorker
            ? await WorkerNetworkCore.init({
                opts: {
                    ...opts,
                    peerStoreDir,
                    metricsEnabled: Boolean(metrics),
                    activeValidatorCount,
                    genesisTime: chain.genesisTime,
                    initialStatus,
                    initialCustodyGroupCount,
                },
                config,
                privateKey,
                logger,
                events,
                metrics,
                getReqRespHandler,
            })
            : await NetworkCore.init({
                opts,
                config,
                privateKey,
                peerStoreDir,
                logger,
                clock: chain.clock,
                events,
                getReqRespHandler,
                metricsRegistry: metrics ? new RegistryMetricCreator() : null,
                initialStatus,
                initialCustodyGroupCount,
                activeValidatorCount,
            });
        const networkProcessor = new NetworkProcessor({ chain, db, config, logger, metrics, events, gossipHandlers, core, aggregatorTracker }, opts);
        const multiaddresses = opts.localMultiaddrs?.join(",");
        const peerId = peerIdFromPrivateKey(privateKey);
        logger.info(`PeerId ${peerIdToString(peerId)}, Multiaddrs ${multiaddresses}`);
        return new Network({
            opts,
            privateKey,
            config,
            logger,
            chain,
            networkEventBus: events,
            aggregatorTracker,
            networkProcessor,
            core,
        });
    }
    get closed() {
        return this.controller.signal.aborted;
    }
    /** Destroy this instance. Can only be called once. */
    async close() {
        if (this.closed)
            return;
        this.events.off(NetworkEvent.peerConnected, this.onPeerConnected);
        this.events.off(NetworkEvent.peerDisconnected, this.onPeerDisconnected);
        this.chain.emitter.off(routes.events.EventType.head, this.onHead);
        this.chain.emitter.off(routes.events.EventType.lightClientFinalityUpdate, this.onLightClientFinalityUpdate);
        this.chain.emitter.off(routes.events.EventType.lightClientOptimisticUpdate, this.onLightClientOptimisticUpdate);
        this.chain.emitter.off(ChainEvent.updateTargetCustodyGroupCount, this.onTargetGroupCountUpdated);
        this.chain.emitter.off(ChainEvent.publishDataColumns, this.onPublishDataColumns);
        this.chain.emitter.off(ChainEvent.publishBlobSidecars, this.onPublishBlobSidecars);
        this.chain.emitter.off(ChainEvent.updateStatus, this.onUpdateStatus);
        await this.core.close();
        // Used only for sleep() statements
        this.controller.abort();
        this.logger.debug("network core closed");
    }
    async scrapeMetrics() {
        return this.core.scrapeMetrics();
    }
    /**
     * Request att subnets up `toSlot`. Network will ensure to mantain some peers for each
     */
    async prepareBeaconCommitteeSubnets(subscriptions) {
        for (const subscription of subscriptions) {
            if (subscription.isAggregator) {
                this.aggregatorTracker.addAggregator(subscription.subnet, subscription.slot);
            }
        }
        this.aggregatorTracker.prune();
        return this.core.prepareBeaconCommitteeSubnets(subscriptions);
    }
    async prepareSyncCommitteeSubnets(subscriptions) {
        return this.core.prepareSyncCommitteeSubnets(subscriptions);
    }
    /**
     * The app layer needs to refresh the status of some peers. The sync have reached a target
     */
    async reStatusPeers(peers) {
        return this.core.reStatusPeers(peers);
    }
    searchUnknownSlotRoot(slotRoot, source, peer) {
        this.networkProcessor.searchUnknownSlotRoot(slotRoot, source, peer);
    }
    async reportPeer(peer, action, actionName) {
        return this.core.reportPeer(peer, action, actionName);
    }
    // REST API queries
    getConnectedPeers() {
        return Array.from(this.connectedPeersSyncMeta.keys());
    }
    getConnectedPeerSyncMeta(peerId) {
        const syncMeta = this.connectedPeersSyncMeta.get(peerId);
        if (!syncMeta) {
            throw new Error(`peerId=${prettyPrintPeerIdStr(peerId)} not in connectedPeerSyncMeta`);
        }
        return { peerId, ...syncMeta };
    }
    getConnectedPeerCount() {
        return this.connectedPeersSyncMeta.size;
    }
    async getNetworkIdentity() {
        return this.core.getNetworkIdentity();
    }
    /**
     * Subscribe to all gossip events. Safe to call multiple times
     */
    async subscribeGossipCoreTopics() {
        if (!this.subscribedToCoreTopics) {
            await this.core.subscribeGossipCoreTopics();
            // Only mark subscribedToCoreTopics if worker resolved this call
            this.subscribedToCoreTopics = true;
        }
    }
    /**
     * Unsubscribe from all gossip events. Safe to call multiple times
     */
    async unsubscribeGossipCoreTopics() {
        // Drop all the gossip validation queues
        this.networkProcessor.dropAllJobs();
        await this.core.unsubscribeGossipCoreTopics();
        this.subscribedToCoreTopics = false;
    }
    isSubscribedToGossipCoreTopics() {
        return this.subscribedToCoreTopics;
    }
    shouldAggregate(subnet, slot) {
        return this.aggregatorTracker.shouldAggregate(subnet, slot);
    }
    // Gossip
    async publishBeaconBlock(signedBlock) {
        const epoch = computeEpochAtSlot(signedBlock.message.slot);
        const boundary = this.config.getForkBoundaryAtEpoch(epoch);
        return this.publishGossip({ type: GossipType.beacon_block, boundary }, signedBlock, {
            ignoreDuplicatePublishError: true,
        });
    }
    async publishBlobSidecar(blobSidecar) {
        const epoch = computeEpochAtSlot(blobSidecar.signedBlockHeader.message.slot);
        const boundary = this.config.getForkBoundaryAtEpoch(epoch);
        const subnet = blobSidecar.index;
        return this.publishGossip({ type: GossipType.blob_sidecar, boundary, subnet }, blobSidecar, {
            ignoreDuplicatePublishError: true,
        });
    }
    async publishDataColumnSidecar(dataColumnSidecar) {
        const slot = isGloasDataColumnSidecar(dataColumnSidecar)
            ? dataColumnSidecar.slot
            : dataColumnSidecar.signedBlockHeader.message.slot;
        const epoch = computeEpochAtSlot(slot);
        const boundary = this.config.getForkBoundaryAtEpoch(epoch);
        const subnet = computeSubnetForDataColumnSidecar(this.config, dataColumnSidecar);
        return this.publishGossip({ type: GossipType.data_column_sidecar, boundary, subnet }, dataColumnSidecar, {
            ignoreDuplicatePublishError: true,
            // we ensure having all topic peers via prioritizePeers() function
            // in the worse case, if there is 0 peer on the topic, the overall publish operation could be still a success
            // because supernode will rebuild and publish missing data column sidecars for us
            // hence we want to track sent peers as 0 instead of an error
            allowPublishToZeroTopicPeers: true,
        });
    }
    async publishBeaconAggregateAndProof(aggregateAndProof) {
        const epoch = computeEpochAtSlot(aggregateAndProof.message.aggregate.data.slot);
        const boundary = this.config.getForkBoundaryAtEpoch(epoch);
        return this.publishGossip({ type: GossipType.beacon_aggregate_and_proof, boundary }, aggregateAndProof, { ignoreDuplicatePublishError: true });
    }
    async publishBeaconAttestation(attestation, subnet) {
        const epoch = computeEpochAtSlot(attestation.data.slot);
        const boundary = this.config.getForkBoundaryAtEpoch(epoch);
        return this.publishGossip({ type: GossipType.beacon_attestation, boundary, subnet }, attestation, { ignoreDuplicatePublishError: true });
    }
    async publishVoluntaryExit(voluntaryExit) {
        const epoch = voluntaryExit.message.epoch;
        const boundary = this.config.getForkBoundaryAtEpoch(epoch);
        return this.publishGossip({ type: GossipType.voluntary_exit, boundary }, voluntaryExit, {
            ignoreDuplicatePublishError: true,
        });
    }
    async publishBlsToExecutionChange(blsToExecutionChange) {
        const publishChanges = [];
        for (const boundary of getActiveForkBoundaries(this.config, this.clock.currentEpoch)) {
            const fork = ForkSeq[boundary.fork];
            if (fork >= ForkSeq.capella) {
                const publishPromise = this.publishGossip({ type: GossipType.bls_to_execution_change, boundary }, blsToExecutionChange, { ignoreDuplicatePublishError: true });
                publishChanges.push(publishPromise);
            }
        }
        if (publishChanges.length === 0) {
            throw Error("No capella+ fork active yet to publish blsToExecutionChange");
        }
        return Promise.any(publishChanges);
    }
    async publishProposerSlashing(proposerSlashing) {
        const epoch = computeEpochAtSlot(Number(proposerSlashing.signedHeader1.message.slot));
        const boundary = this.config.getForkBoundaryAtEpoch(epoch);
        return this.publishGossip({ type: GossipType.proposer_slashing, boundary }, proposerSlashing);
    }
    async publishAttesterSlashing(attesterSlashing) {
        const epoch = computeEpochAtSlot(Number(attesterSlashing.attestation1.data.slot));
        const boundary = this.config.getForkBoundaryAtEpoch(epoch);
        return this.publishGossip({ type: GossipType.attester_slashing, boundary }, attesterSlashing);
    }
    async publishSyncCommitteeSignature(signature, subnet) {
        const epoch = computeEpochAtSlot(signature.slot);
        const boundary = this.config.getForkBoundaryAtEpoch(epoch);
        return this.publishGossip({ type: GossipType.sync_committee, boundary, subnet }, signature, {
            ignoreDuplicatePublishError: true,
        });
    }
    async publishContributionAndProof(contributionAndProof) {
        const epoch = computeEpochAtSlot(contributionAndProof.message.contribution.slot);
        const boundary = this.config.getForkBoundaryAtEpoch(epoch);
        return this.publishGossip({ type: GossipType.sync_committee_contribution_and_proof, boundary }, contributionAndProof, { ignoreDuplicatePublishError: true });
    }
    async publishLightClientFinalityUpdate(update) {
        const epoch = computeEpochAtSlot(update.signatureSlot);
        const boundary = this.config.getForkBoundaryAtEpoch(epoch);
        return this.publishGossip({ type: GossipType.light_client_finality_update, boundary }, update);
    }
    async publishLightClientOptimisticUpdate(update) {
        const epoch = computeEpochAtSlot(update.signatureSlot);
        const boundary = this.config.getForkBoundaryAtEpoch(epoch);
        return this.publishGossip({ type: GossipType.light_client_optimistic_update, boundary }, update);
    }
    async publishSignedExecutionPayloadEnvelope(signedEnvelope) {
        const epoch = computeEpochAtSlot(signedEnvelope.message.slot);
        const boundary = this.config.getForkBoundaryAtEpoch(epoch);
        return this.publishGossip({ type: GossipType.execution_payload, boundary }, signedEnvelope, { ignoreDuplicatePublishError: true });
    }
    async publishGossip(topic, object, opts) {
        const topicStr = stringifyGossipTopic(this.config, topic);
        const sszType = getGossipSSZType(topic);
        const messageData = sszType.serialize(object);
        opts = {
            ...opts,
            ignoreDuplicatePublishError: gossipTopicIgnoreDuplicatePublishError[topic.type],
        };
        const sentPeers = await this.core.publishGossip(topicStr, messageData, opts);
        this.logger.verbose("Publish to topic", { topic: topicStr, sentPeers, currentSlot: this.clock.currentSlot });
        return sentPeers;
    }
    // ReqResp
    async sendBeaconBlocksByRange(peerId, request) {
        return collectSequentialBlocksInRange(this.sendReqRespRequest(peerId, ReqRespMethod.BeaconBlocksByRange, 
        // Before altair, prioritize V2. After altair only request V2
        this.config.getForkSeq(this.clock.currentSlot) >= ForkSeq.altair ? [Version.V2] : [Version.V2, Version.V1], request), request, this.chain.serializedCache);
    }
    async sendBeaconBlocksByRoot(peerId, request) {
        return collectMaxResponseTypedWithBytes(this.sendReqRespRequest(peerId, ReqRespMethod.BeaconBlocksByRoot, 
        // Before altair, prioritize V2. After altair only request V2
        this.config.getForkSeq(this.clock.currentSlot) >= ForkSeq.altair ? [Version.V2] : [Version.V2, Version.V1], request), request.length, responseSszTypeByMethod[ReqRespMethod.BeaconBlocksByRoot], this.chain.serializedCache);
    }
    async sendLightClientBootstrap(peerId, request) {
        return collectExactOneTyped(this.sendReqRespRequest(peerId, ReqRespMethod.LightClientBootstrap, [Version.V1], request), responseSszTypeByMethod[ReqRespMethod.LightClientBootstrap]);
    }
    async sendLightClientOptimisticUpdate(peerId) {
        return collectExactOneTyped(this.sendReqRespRequest(peerId, ReqRespMethod.LightClientOptimisticUpdate, [Version.V1], null), responseSszTypeByMethod[ReqRespMethod.LightClientOptimisticUpdate]);
    }
    async sendLightClientFinalityUpdate(peerId) {
        return collectExactOneTyped(this.sendReqRespRequest(peerId, ReqRespMethod.LightClientFinalityUpdate, [Version.V1], null), responseSszTypeByMethod[ReqRespMethod.LightClientFinalityUpdate]);
    }
    async sendLightClientUpdatesByRange(peerId, request) {
        return collectMaxResponseTyped(this.sendReqRespRequest(peerId, ReqRespMethod.LightClientUpdatesByRange, [Version.V1], request), request.count, responseSszTypeByMethod[ReqRespMethod.LightClientUpdatesByRange]);
    }
    async sendBlobSidecarsByRange(peerId, request) {
        const epoch = computeEpochAtSlot(request.startSlot);
        return collectMaxResponseTyped(this.sendReqRespRequest(peerId, ReqRespMethod.BlobSidecarsByRange, [Version.V1], request), 
        // request's count represent the slots, so the actual max count received could be slots * blobs per slot
        request.count * this.config.getMaxBlobsPerBlock(epoch), responseSszTypeByMethod[ReqRespMethod.BlobSidecarsByRange]);
    }
    async sendBlobSidecarsByRoot(peerId, request) {
        return collectMaxResponseTyped(this.sendReqRespRequest(peerId, ReqRespMethod.BlobSidecarsByRoot, [Version.V1], request), request.length, responseSszTypeByMethod[ReqRespMethod.BlobSidecarsByRoot], this.chain.serializedCache);
    }
    async sendDataColumnSidecarsByRange(peerId, request) {
        return collectMaxResponseTyped(this.sendReqRespRequest(peerId, ReqRespMethod.DataColumnSidecarsByRange, [Version.V1], request), request.count * request.columns.length, responseSszTypeByMethod[ReqRespMethod.DataColumnSidecarsByRange]);
    }
    async sendDataColumnSidecarsByRoot(peerId, request) {
        return collectMaxResponseTyped(this.sendReqRespRequest(peerId, ReqRespMethod.DataColumnSidecarsByRoot, [Version.V1], request), request.reduce((total, { columns }) => total + columns.length, 0), responseSszTypeByMethod[ReqRespMethod.DataColumnSidecarsByRoot], this.chain.serializedCache);
    }
    sendReqRespRequest(peerId, method, versions, request) {
        const fork = this.config.getForkName(this.clock.currentSlot);
        const requestType = requestSszTypeByMethod(fork, this.config)[method];
        const requestData = requestType ? requestType.serialize(request) : new Uint8Array();
        // ReqResp outgoing request, emit from main thread to worker
        return this.core.sendReqRespRequest({ peerId, method, versions, requestData });
    }
    // Debug
    connectToPeer(peer, multiaddr) {
        return this.core.connectToPeer(peer, multiaddr);
    }
    disconnectPeer(peer) {
        return this.core.disconnectPeer(peer);
    }
    addDirectPeer(peer) {
        return this.core.addDirectPeer(peer);
    }
    removeDirectPeer(peerId) {
        return this.core.removeDirectPeer(peerId);
    }
    getDirectPeers() {
        return this.core.getDirectPeers();
    }
    dumpPeer(peerIdStr) {
        return this.core.dumpPeer(peerIdStr);
    }
    dumpPeers() {
        return this.core.dumpPeers();
    }
    dumpPeerScoreStats() {
        return this.core.dumpPeerScoreStats();
    }
    dumpGossipPeerScoreStats() {
        return this.core.dumpGossipPeerScoreStats();
    }
    dumpDiscv5KadValues() {
        return this.core.dumpDiscv5KadValues();
    }
    dumpMeshPeers() {
        return this.core.dumpMeshPeers();
    }
    async dumpGossipQueue(gossipType) {
        return this.networkProcessor.dumpGossipQueue(gossipType);
    }
    async writeNetworkThreadProfile(durationMs, dirpath) {
        return this.core.writeNetworkThreadProfile(durationMs, dirpath);
    }
    async writeDiscv5Profile(durationMs, dirpath) {
        return this.core.writeDiscv5Profile(durationMs, dirpath);
    }
    async writeNetworkHeapSnapshot(prefix, dirpath) {
        return this.core.writeNetworkHeapSnapshot(prefix, dirpath);
    }
    async writeDiscv5HeapSnapshot(prefix, dirpath) {
        return this.core.writeDiscv5HeapSnapshot(prefix, dirpath);
    }
    onLightClientFinalityUpdate = async (finalityUpdate) => {
        // TODO: Review is OK to remove if (this.hasAttachedSyncCommitteeMember())
        try {
            // messages SHOULD be broadcast after SYNC_MESSAGE_DUE_BPS of slot has transpired
            // https://github.com/ethereum/consensus-specs/blob/dev/specs/altair/light-client/p2p-interface.md#sync-committee
            await this.waitForSyncMessageCutoff(finalityUpdate.signatureSlot);
            await this.publishLightClientFinalityUpdate(finalityUpdate);
        }
        catch (e) {
            // Non-mandatory route on most of network as of Oct 2022. May not have found any peers on topic yet
            // Remove once https://github.com/ChainSafe/js-libp2p-gossipsub/issues/367
            if (!isPublishToZeroPeersError(e)) {
                this.logger.debug("Error on BeaconGossipHandler.onLightclientFinalityUpdate", {}, e);
            }
        }
    };
    onLightClientOptimisticUpdate = async (optimisticUpdate) => {
        // TODO: Review is OK to remove if (this.hasAttachedSyncCommitteeMember())
        try {
            // messages SHOULD be broadcast after SYNC_MESSAGE_DUE_BPS of slot has transpired
            // https://github.com/ethereum/consensus-specs/blob/dev/specs/altair/light-client/p2p-interface.md#sync-committee
            await this.waitForSyncMessageCutoff(optimisticUpdate.signatureSlot);
            await this.publishLightClientOptimisticUpdate(optimisticUpdate);
        }
        catch (e) {
            // Non-mandatory route on most of network as of Oct 2022. May not have found any peers on topic yet
            // Remove once https://github.com/ChainSafe/js-libp2p-gossipsub/issues/367
            if (!isPublishToZeroPeersError(e)) {
                this.logger.debug("Error on BeaconGossipHandler.onLightclientOptimisticUpdate", {}, e);
            }
        }
    };
    waitForSyncMessageCutoff = async (slot) => {
        const fork = this.config.getForkName(slot);
        const msToCutoffTime = this.config.getSyncMessageDueMs(fork) - this.chain.clock.msFromSlot(slot);
        await sleep(msToCutoffTime, this.controller.signal);
    };
    onHead = async () => {
        await this.onUpdateStatus();
    };
    onPeerConnected = (data) => {
        const { peer, clientAgent, custodyColumns, status } = data;
        const earliestAvailableSlot = status.earliestAvailableSlot;
        this.logger.verbose("onPeerConnected", {
            peer,
            clientAgent,
            custodyColumns: prettyPrintIndices(custodyColumns),
            earliestAvailableSlot: earliestAvailableSlot ?? "pre-fulu",
        });
        this.connectedPeersSyncMeta.set(peer, {
            client: clientAgent,
            custodyColumns,
            earliestAvailableSlot, // can be undefined pre-fulu
        });
    };
    onPeerDisconnected = (data) => {
        this.connectedPeersSyncMeta.delete(data.peer);
    };
    onTargetGroupCountUpdated = (count) => {
        this.core.setTargetGroupCount(count);
    };
    onPublishDataColumns = (sidecars) => {
        return promiseAllMaybeAsync(sidecars.map((sidecar) => () => this.publishDataColumnSidecar(sidecar)));
    };
    onPublishBlobSidecars = (sidecars) => {
        return promiseAllMaybeAsync(sidecars.map((sidecar) => () => this.publishBlobSidecar(sidecar)));
    };
    onUpdateStatus = async () => {
        await this.core.updateStatus(this.chain.getStatus());
    };
}
//# sourceMappingURL=network.js.map