"use strict";
/**
 * SyncV2 a p2p module that contains all of the functionality for the new
 * Node List Sync v2.
 */
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    var desc = Object.getOwnPropertyDescriptor(m, k);
    if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
      desc = { enumerable: true, get: function() { return m[k]; } };
    }
    Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
    Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
    o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
    if (mod && mod.__esModule) return mod;
    var result = {};
    if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
    __setModuleDefault(result, mod);
    return result;
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.syncV2 = exports.init = void 0;
const neverthrow_1 = require("neverthrow");
const lib_types_1 = require("@shardeum-foundation/lib-types");
const queries_1 = require("./queries");
const verify_1 = require("./verify");
const Archivers = __importStar(require("../Archivers"));
const NodeList = __importStar(require("../NodeList"));
const CycleChain = __importStar(require("../CycleChain"));
const ServiceQueue = __importStar(require("../ServiceQueue"));
const routes_1 = require("./routes");
const Sync_1 = require("../Sync");
const v2_1 = require("../Join/v2");
const logger_1 = require("../../logger");
const CycleCreator_1 = require("../CycleCreator");
const queries_2 = require("./queries");
/** Initializes logging and endpoints for Sync V2. */
function init() {
    (0, queries_1.initLogger)();
    (0, routes_1.initRoutes)();
}
exports.init = init;
/**
 * This function synchronizes nodes, archivers and cycle records.
 *
 * @export
 * @param {P2P.SyncTypes.ActiveNode[]} activeNodes - An array of active nodes that can be queried for synchronization. The function first synchronizes the validator list, the archiver list, and then the latest cycle record respectively.
 *
 * Only active nodes are added to the NodeList, and all the archivers are added to the Archivers list.
 * The cycle record is then appended to the CycleChain.
 *
 * @returns {ResultAsync<void, Error>} - A ResultAsync object. On success, it will contain void and on
 * error, it will contain an Error object. The function is asynchronous and can be awaited.
 */
function syncV2(activeNodes, shardus) {
    return syncValidValidatorList(activeNodes).andThen(([validatorList, validatorListHash]) => syncArchiverList(activeNodes).andThen(([archiverList, archiverListHash]) => syncStandbyNodeList(activeNodes).andThen((standbyNodeList) => syncTxList(activeNodes).andThen((txList) => syncLatestCycleRecord(activeNodes).andThen((cycle) => {
        if (cycle.nodeListHash !== validatorListHash) {
            return (0, neverthrow_1.errAsync)(new Error(`validator list hash from received cycle (${cycle.nodeListHash}) does not match the hash received from robust query (${validatorListHash})`));
        }
        else if (cycle.archiverListHash !== archiverListHash) {
            return (0, neverthrow_1.errAsync)(new Error(`archiver list hash from received cycle (${cycle.archiverListHash}) does not match the hash received from robust query (${archiverListHash})`));
        }
        return neverthrow_1.ResultAsync.fromPromise(shardus.earlyConfigFetchAndPatch(cycle.counter), (error) => new Error(`Failed to fetch and patch config: ${error}`)).andThen(() => {
            NodeList.reset('syncV2');
            // log the counts of the nodes, archivers, and standby nodes
            /* prettier-ignore */ if (logger_1.logFlags.important_as_fatal)
                console.log(`syncV2: nodes: ${validatorList.length}, archivers: ${archiverList.length}, standby nodes: ${standbyNodeList.length}`);
            // add validators
            NodeList.addNodes(validatorList, 'syncV2', cycle);
            // add archivers
            for (const archiver of archiverList) {
                Archivers.archivers.set(archiver.publicKey, archiver);
            }
            // add standby nodes
            (0, v2_1.addStandbyJoinRequests)(standbyNodeList, true);
            // add txList
            ServiceQueue.setTxList(txList);
            // add latest cycle
            CycleChain.reset();
            // earlyConfigFetchAndPatch() is an async call so we have to do some
            // funky stuff to call it in this neverthow style code:
            info('syncV2: cycle.counter ', cycle.counter);
            info('syncV2: cycle.marker ', (0, CycleCreator_1.makeCycleMarker)(cycle));
            info('syncV2: nodelist hash ', cycle.nodeListHash);
            info('syncV2: archiverList hash ', cycle.archiverListHash);
            info('syncV2: standbyNodeList hash ', cycle.standbyNodeListHash);
            info('syncV2: cycle ', lib_types_1.Utils.safeStringify(cycle));
            (0, Sync_1.digestCycle)(cycle, 'syncV2');
            info('syncV2: CycleChain.newest.counter ', CycleChain.newest.counter);
            info('syncV2: CycleChain.newest.marker ', (0, CycleCreator_1.makeCycleMarker)(CycleChain.newest));
            info('syncV2: nodelist hash ', CycleChain.newest.nodeListHash);
            info('syncV2: archiverList hash ', CycleChain.newest.archiverListHash);
            info('syncV2: standbyNodeList hash ', CycleChain.newest.standbyNodeListHash);
            info('syncV2: CycleChain.newest ', lib_types_1.Utils.safeStringify(CycleChain.newest));
            return (0, neverthrow_1.okAsync)(void 0);
        });
    })))));
}
exports.syncV2 = syncV2;
/**
 * This function queries for a validator list from other active nodes.
 *
 * @param {P2P.SyncTypes.ActiveNode[]} activeNodes - An array of active nodes to be queried.
 * The function first performs a robust query for the latest node list hash.
 * Then, it requests a full list from one of the winning nodes using the hash
 * retrieved. The node receiving the request may or may not have the list whose
 * hash matches the one requested.
 *
 * @returns {ResultAsync<[P2P.NodeListTypes.Node[], hexstring], Error>} - A
 * ResultAsync object. On success, it will contain an array of Node objects and
 * the validator list hash, and on error, it will contain an Error object. The
 * function is asynchronous and can be awaited.
 */
function syncValidValidatorList(activeNodes) {
    // run a robust query for the lastest node list hash
    return (0, queries_1.robustQueryForValidatorListHash)(activeNodes).andThen(({ value, winningNodes }) => 
    // get full node list from one of the winning nodes
    (0, queries_1.getValidatorListFromNode)(winningNodes[0], value.nodeListHash).andThen((nodeList) => 
    // verify a hash of the retrieved node list matches the hash from before.
    // if it does, return the node list
    (0, verify_1.verifyValidatorList)(nodeList, value.nodeListHash).map(() => [nodeList, value.nodeListHash])));
}
/**
 * This function queries for an archiver list from other active nodes.
 *
 * @param {P2P.SyncTypes.ActiveNode[]} activeNodes - An array of active nodes to be queried.
 * The function first performs a robust query for the latest archiver list hash.
 * Then, it requests a full list from one of the winning nodes using the hash
 * retrieved. The node receiving the request may or may not have the list whose
 * hash matches the one requested.
 *
 * @returns {ResultAsync<[P2P.ArchiversTypes.JoinedArchiver[], hexstring], Error>} - A ResultAsync object. On success, it will contain an array of
 * JoinedArchiver objects and the archiver list hash, and on error, it will contain an Error object. The function is asynchronous and can be awaited.
 */
function syncArchiverList(activeNodes) {
    // run a robust query for the lastest archiver list hash
    return (0, queries_1.robustQueryForArchiverListHash)(activeNodes).andThen(({ value, winningNodes }) => 
    // get full archiver list from one of the winning nodes
    (0, queries_1.getArchiverListFromNode)(winningNodes[0], value.archiverListHash).andThen((archiverList) => 
    // verify a hash of the retrieved archiver list matches the hash from before.
    // if it does, return the archiver list
    (0, verify_1.verifyArchiverList)(archiverList, value.archiverListHash).map(() => [archiverList, value.archiverListHash])));
}
/**
 * This function queries for a valid standby node list.
 *
 * @param {P2P.SyncTypes.ActiveNode[]} activeNodes - An array of active nodes to be queried.
 * The function first performs a robust query for the latest standby node list hash.
 * Then, it requests a full list from one of the winning nodes using the hash
 * retrieved. The node receiving the request may or may not have the list whose
 * hash matches the one requested.
 *
 * @returns {ResultAsync<P2P.ArchiversTypes.JoinedArchiver[], Error>} - A ResultAsync object. On success, it will contain
 * an array of JoinRequest objects, and on error, it will contain an Error object. The function is asynchronous
 * and can be awaited.
 */
function syncStandbyNodeList(activeNodes) {
    // run a robust query for the lastest archiver list hash
    return (0, queries_1.robustQueryForStandbyNodeListHash)(activeNodes).andThen(({ value, winningNodes }) => {
        // get full archiver list from one of the winning nodes
        return (0, queries_1.getStandbyNodeListFromNode)(winningNodes[0], value.standbyNodeListHash);
    });
}
function syncTxList(activeNodes) {
    return (0, queries_1.robustQueryForTxListHash)(activeNodes).andThen(({ value, winningNodes }) => (0, queries_1.getTxListFromNode)(winningNodes[0], value.txListHash).andThen((txList) => (0, verify_1.verifyTxList)(txList, value.txListHash).map(() => txList)));
}
/**
 * Synchronizes the latest cycle record from a list of active nodes.
 *
 * @param {P2P.SyncTypes.ActiveNode[]} activeNodes - An array of active nodes to be queried.
 * The function first performs a robust query for the latest cycle record hash.
 * After obtaining the hash, it retrieves the current cycle data from one of the winning nodes.
 * It then verifies whether the cycle record marker matches the previously obtained hash.
 * If it matches, the cycle record is returned.
 *
 * @returns {ResultAsync<P2P.CycleCreatorTypes.CycleRecord, Error>} - A ResultAsync object.
 * On success, it will contain a CycleRecord object, and on error, it will contain an Error object.
 * The function is asynchronous and can be awaited.
 */
function syncLatestCycleRecord(activeNodes) {
    // run a robust query for the latest cycle record hash
    return (0, queries_1.robustQueryForCycleRecordHash)(activeNodes).andThen(({ value, winningNodes }) => 
    // get current cycle record from node
    (0, queries_1.getCycleDataFromNode)(winningNodes[0], value.currentCycleHash).andThen((cycle) => 
    // verify the cycle record marker matches the hash from before. if it
    // does, return the cycle
    (0, verify_1.verifyCycleRecord)(cycle, value.currentCycleHash).map(() => cycle)));
}
function info(...msg) {
    const entry = `SyncV2: ${msg.join(' ')}`;
    queries_2.p2pLogger.info(entry);
}
//# sourceMappingURL=index.js.map