"use strict";
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 () {
    var ownKeys = function(o) {
        ownKeys = Object.getOwnPropertyNames || function (o) {
            var ar = [];
            for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
            return ar;
        };
        return ownKeys(o);
    };
    return function (mod) {
        if (mod && mod.__esModule) return mod;
        var result = {};
        if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
        __setModuleDefault(result, mod);
        return result;
    };
})();
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.targetCount = exports.desiredCount = exports.scalingRequestsCollector = exports.lastScalingType = exports.approvedScalingType = exports.requestedScalingType = exports.scalingRequested = void 0;
exports.init = init;
exports.reset = reset;
exports.getDesiredCount = getDesiredCount;
exports.requestNetworkUpsize = requestNetworkUpsize;
exports.requestNetworkDownsize = requestNetworkDownsize;
exports.configUpdated = configUpdated;
exports.queueRequest = queueRequest;
exports.sendRequests = sendRequests;
exports.getTxs = getTxs;
exports.validateRecordTypes = validateRecordTypes;
exports.updateRecord = updateRecord;
exports.parseRecord = parseRecord;
const deepmerge_1 = __importDefault(require("deepmerge"));
const logger_1 = require("../logger");
const lib_types_1 = require("@shardeum-foundation/lib-types");
const utils_1 = require("../utils");
const Comms = __importStar(require("./Comms"));
const Context_1 = require("./Context");
const CycleChain = __importStar(require("./CycleChain"));
const CycleCreator = __importStar(require("./CycleCreator"));
const NodeList = __importStar(require("./NodeList"));
const Self = __importStar(require("./Self"));
const profiler_1 = require("../utils/profiler");
const nestedCounters_1 = require("../utils/nestedCounters");
const Modes_1 = require("./Modes");
const Utils_1 = require("./Utils");
const network_1 = require("../network");
const lib_types_2 = require("@shardeum-foundation/lib-types");
const promises_1 = require("../utils/functions/promises");
/** STATE */
let p2pLogger;
reset();
const gossipScaleRoute = async (payload, sender, tracker) => {
    profiler_1.profilerInstance.scopedProfileSectionStart('gossip-scaling');
    try {
        if (logger_1.logFlags.p2pNonFatal)
            info(`Got scale request: ${lib_types_2.Utils.safeStringify(payload)}`);
        if (!payload) {
            warn('No payload provided for the `scaling` request.');
            return;
        }
        const id = payload.nodeId;
        if (_canThisNodeSendScaleRequests(id) !== true) {
            return;
        }
        const added = addExtScalingRequest(payload);
        if (!added)
            return;
        (0, promises_1.fireAndForget)(() => Comms.sendGossip('scaling', payload, tracker, sender, NodeList.byIdOrder, false, 2));
    }
    finally {
        profiler_1.profilerInstance.scopedProfileSectionEnd('gossip-scaling');
    }
};
const routes = {
    internal: {},
    gossip: {
        scaling: gossipScaleRoute,
    },
};
/** FUNCTIONS */
function init() {
    p2pLogger = Context_1.logger.getLogger('p2p');
    exports.desiredCount = Context_1.config.p2p.minNodes;
    for (const [name, handler] of Object.entries(routes.gossip)) {
        Comms.registerGossipHandler(name, handler);
    }
}
function reset() {
    /* prettier-ignore */ if (logger_1.logFlags?.verbose)
        Context_1.logger.mainLog_debug('RESET_1', Context_1.logger.combine('Resetting auto-scale module', `Cycle ${CycleCreator.currentCycle}, Quarter: ${CycleCreator.currentQuarter}`));
    exports.scalingRequested = false;
    exports.scalingRequestsCollector = new Map();
    exports.requestedScalingType = null;
    exports.approvedScalingType = null;
}
function getDesiredCount() {
    // having trouble finding a better way to update this!
    // Check the feature flag and set the lower limit accordingly
    if (exports.desiredCount < Context_1.config.p2p.minNodes) {
        return (exports.desiredCount = Context_1.config.p2p.minNodes);
    }
    else {
        return exports.desiredCount;
    }
}
function createScaleRequest(scaleType) {
    const request = {
        nodeId: Self.id,
        timestamp: (0, network_1.shardusGetTime)(),
        counter: CycleCreator.currentCycle,
        scale: undefined,
    };
    switch (scaleType) {
        case lib_types_1.P2P.CycleAutoScaleTypes.ScaleType.UP:
            request.scale = lib_types_1.P2P.CycleAutoScaleTypes.ScaleType.UP;
            break;
        case lib_types_1.P2P.CycleAutoScaleTypes.ScaleType.DOWN:
            request.scale = lib_types_1.P2P.CycleAutoScaleTypes.ScaleType.DOWN;
            break;
        default:
            const err = new Error(`Invalid scaling request type: ${scaleType}`);
            error(err);
            throw err;
    }
    const signedReq = Context_1.crypto.sign(request);
    return signedReq;
}
function _requestNetworkScaling(upOrDown) {
    //scalingRequested makes sure we only request scaling on our own once per cycle
    if (!Self.isActive || exports.scalingRequested)
        return;
    const signedRequest = createScaleRequest(upOrDown);
    // await _waitUntilEndOfCycle()
    const isRequestAdded = addExtScalingRequest(signedRequest);
    if (isRequestAdded) {
        info(`Our scale request is added. Gossiping our scale request to other nodes. ${lib_types_2.Utils.safeStringify(signedRequest)}`);
        (0, promises_1.fireAndForget)(() => Comms.sendGossip('scaling', signedRequest, '', null, NodeList.byIdOrder, true, 2));
        exports.scalingRequested = true;
        exports.requestedScalingType = signedRequest.scale; //only set this when our node requests scaling
        nestedCounters_1.nestedCountersInstance.countEvent('p2p', 'initiate gossip: scaling: ' + (upOrDown ? 'up' : 'down'));
    }
}
function requestNetworkUpsize() {
    if (getDesiredCount() >= Context_1.config.p2p.maxNodes) {
        return;
    }
    if (_canThisNodeSendScaleRequests(Self.id) === false && Context_1.config.debug.ignoreScaleGossipSelfCheck === false) {
        return;
    }
    /* prettier-ignore */ if (logger_1.logFlags?.verbose)
        Context_1.logger.mainLog_debug('REQUESTNETWORKUPSIZE_1', 'CycleAutoScale: UPSIZE!');
    _requestNetworkScaling(lib_types_1.P2P.CycleAutoScaleTypes.ScaleType.UP);
}
function requestNetworkDownsize() {
    // minNodes is used in absense of load, not baselineNodes. Main idea here is to grow to desired which is essentially minNodes
    if (getDesiredCount() <= Context_1.config.p2p.minNodes) {
        return;
    }
    if (_canThisNodeSendScaleRequests(Self.id) === false && Context_1.config.debug.ignoreScaleGossipSelfCheck === false) {
        return;
    }
    /* prettier-ignore */ if (logger_1.logFlags?.verbose)
        Context_1.logger.mainLog_debug('REQUESTNETWORKDOWNSIZE_1', 'CycleAutoScale: DOWNSIZE!');
    _requestNetworkScaling(lib_types_1.P2P.CycleAutoScaleTypes.ScaleType.DOWN);
}
function addExtScalingRequest(scalingRequest) {
    const added = _addScalingRequest(scalingRequest);
    return added;
}
function validateScalingRequest(scalingRequest) {
    // Check existence of fields
    if (!scalingRequest.nodeId ||
        !scalingRequest.timestamp ||
        !scalingRequest.counter ||
        !scalingRequest.scale ||
        !scalingRequest.sign) {
        warn(`Invalid scaling request, missing fields. Request: ${lib_types_2.Utils.safeStringify(scalingRequest)}`);
        return false;
    }
    // Check if cycle counter matches
    if (scalingRequest.counter !== CycleCreator.currentCycle) {
        warn(`Invalid scaling request, not for this cycle. Current cycle:${CycleCreator.currentCycle}, cycleInScaleRequest: ${scalingRequest.counter} Request: ${lib_types_2.Utils.safeStringify(scalingRequest)}`);
        return false;
    }
    // Check if we are trying to scale either up or down
    if (scalingRequest.scale !== lib_types_1.P2P.CycleAutoScaleTypes.ScaleType.UP &&
        scalingRequest.scale !== lib_types_1.P2P.CycleAutoScaleTypes.ScaleType.DOWN) {
        warn(`Invalid scaling request, not a valid scaling type. Request: ${lib_types_2.Utils.safeStringify(scalingRequest)}`);
        return false;
    }
    // Try to get the node who supposedly signed this request
    let node;
    try {
        node = NodeList.nodes.get(scalingRequest.nodeId);
    }
    catch (e) {
        warn(e);
        warn(`Invalid scaling request, not a known node. Request: ${lib_types_2.Utils.safeStringify(scalingRequest)}`);
        return false;
    }
    if (node == null) {
        warn(`Invalid scaling request, not a known node. Request: ${lib_types_2.Utils.safeStringify(scalingRequest)}`);
        return false;
    }
    // TODO need to verify if the node was a valid sender in the sub commitee
    // Return false if fails validation for signature
    if (!Context_1.crypto.verify(scalingRequest, node.publicKey)) {
        warn(`Invalid scaling request, signature is not valid. Request: ${lib_types_2.Utils.safeStringify(scalingRequest)}`);
        return false;
    }
    return true;
}
//Protocl security node:
//note we will need the ablity to check if other nodes also meet this requirement
//so that we can reject input from nodes that are not permitted to send a vote
//to do that well we need to make it easy to get the index of a node by ID.
//If the currentCycleShardData is null then we can't do this check and return false
function _canThisNodeSendScaleRequests(_nodeId) {
    let scaleVoteLimits = Context_1.config.p2p.scaleGroupLimit > 0;
    if (scaleVoteLimits === false) {
        return true;
    }
    if (Context_1.stateManager.currentCycleShardData == null) {
        return false;
    }
    let numActiveNodes = NodeList.activeByIdOrder.length;
    if (numActiveNodes < Context_1.config.p2p.scaleGroupLimit) {
        return true;
    }
    let canSendGossip = false;
    let offset = CycleChain.newest.counter; //todo something more random
    const ourIndex = (0, Utils_1.getOurNodeIndex)();
    if (ourIndex == null)
        return false;
    canSendGossip = (0, utils_1.fastIsPicked)(ourIndex, numActiveNodes, Context_1.config.p2p.scaleGroupLimit, offset);
    return canSendGossip;
}
function _checkScaling() {
    // Keep a flag if we have changed our metadata.scaling at all
    let changed = false;
    exports.lastScalingType = exports.approvedScalingType;
    if (exports.approvedScalingType === lib_types_1.P2P.CycleAutoScaleTypes.ScaleType.UP) {
        warn('Already set to scale up this cycle. No need to scale up anymore.');
        return;
    }
    if (exports.approvedScalingType === lib_types_1.P2P.CycleAutoScaleTypes.ScaleType.DOWN) {
        warn('Already set to scale down this cycle. No need to scale down anymore.');
        return;
    }
    // lazy init of desiredCount
    // if we have a good value in our cycle chane for desired nodes update our desired count.
    if (CycleChain.newest != null && CycleChain.newest.desired != null) {
        exports.desiredCount = CycleChain.newest.desired;
    }
    let numActiveNodes = NodeList.activeByIdOrder.length;
    let requiredVotes = Math.max(Context_1.config.p2p.minScaleReqsNeeded, Context_1.config.p2p.scaleConsensusRequired * numActiveNodes);
    let scaleUpRequests = getScaleUpRequests();
    let scaleDownRequests = getScaleDownRequests();
    let scaleVoteLimits = Context_1.config.p2p.scaleGroupLimit > 0 && numActiveNodes > Context_1.config.p2p.scaleGroupLimit;
    if (scaleVoteLimits) {
        //This will reduce the amount of votde required, because scaleGroupLimit will limit the number of nodes actually voting
        requiredVotes = Math.min(requiredVotes, Context_1.config.p2p.scaleGroupLimit * Context_1.config.p2p.scaleConsensusRequired);
    }
    // Check up first, but must have more votes than down votes.
    if (scaleUpRequests.length >= requiredVotes && scaleUpRequests.length >= scaleDownRequests.length) {
        exports.approvedScalingType = lib_types_1.P2P.CycleAutoScaleTypes.ScaleType.UP;
        changed = true;
    }
    // If we haven't approved an scale type, check if we should scale down
    if (!changed) {
        /* prettier-ignore */ if (logger_1.logFlags?.verbose)
            Context_1.logger.mainLog_debug('CHECKSCALING_1', 'CycleAutoScale: scale up not approved');
        if (scaleDownRequests.length >= requiredVotes) {
            exports.approvedScalingType = lib_types_1.P2P.CycleAutoScaleTypes.ScaleType.DOWN;
            changed = true;
        }
        else {
            /* prettier-ignore */ if (logger_1.logFlags && logger_1.logFlags.verbose)
                console.log("CycleAutoScale: changed is ", changed);
            /* prettier-ignore */ if (logger_1.logFlags && logger_1.logFlags.verbose)
                console.log("CycleAutoScale: won't change desired");
            // Return if we don't change anything
            return;
        }
    }
    exports.lastScalingType = exports.approvedScalingType;
    // At this point, we have changed our scaling type flag (approvedScalingType)
    let newDesired;
    switch (exports.approvedScalingType) {
        case lib_types_1.P2P.CycleAutoScaleTypes.ScaleType.UP:
            newDesired = CycleChain.newest.desired + Context_1.config.p2p.amountToGrow;
            // If newDesired more than maxNodes, set newDesired to maxNodes
            if (newDesired > Context_1.config.p2p.maxNodes)
                newDesired = Context_1.config.p2p.maxNodes;
            //limit growth to no more than 20% more than active
            let moreThanActiveMax = Math.floor(numActiveNodes * Context_1.config.p2p.maxDesiredMultiplier);
            if (newDesired > moreThanActiveMax)
                newDesired = moreThanActiveMax;
            setDesiredCount(newDesired);
            break;
        case lib_types_1.P2P.CycleAutoScaleTypes.ScaleType.DOWN:
            newDesired = CycleChain.newest.desired - Context_1.config.p2p.amountToGrow;
            // If newDesired less than minNodes, set newDesired to minNodes
            // minNodes is used in absense of load, not baselineNodes. Main idea here is to grow to desired which is essentially minNodes
            if (newDesired < Context_1.config.p2p.minNodes)
                newDesired = Context_1.config.p2p.minNodes;
            setDesiredCount(newDesired);
            break;
        default:
            error(new Error(`Invalid scaling flag after changing flag. Flag: ${exports.approvedScalingType}`));
            return;
    }
    /* prettier-ignore */ if (logger_1.logFlags?.verbose)
        Context_1.logger.mainLog_debug('CHECKSCALING_2', Context_1.logger.combine('newDesired', newDesired));
}
function setDesiredCount(count) {
    // Always use minNodes as the lower limit to prevent scaling down below the minimum required nodes for the network.
    // This is to ensure that the network will not scale down when node count is below minNodes.
    const lowerLimit = Context_1.config.p2p.minNodes;
    if (count >= lowerLimit && count <= Context_1.config.p2p.maxNodes) {
        console.log('Setting desired count to', count);
        exports.desiredCount = count;
    }
}
function setAndGetTargetCount(prevRecord) {
    const active = NodeList.activeByIdOrder.length;
    const syncing = NodeList.syncingByIdOrder.length;
    if (prevRecord && prevRecord.mode !== undefined) {
        // For now, we are using the desired value from the previous cycle. In the future, we should look at using the next desired value
        const desired = prevRecord.desired;
        if (prevRecord.mode === 'forming') {
            /* prettier-ignore */ if (logger_1.logFlags && logger_1.logFlags.verbose)
                console.log("CycleAutoScale: in forming");
            if (active != desired) {
                if (active < desired) {
                    /* prettier-ignore */ if (logger_1.logFlags && logger_1.logFlags.verbose)
                        console.log("CycleAutoScale: entered active < desired");
                    let add = ~~(0.5 * active);
                    if (add < Context_1.config.p2p.formingNodesPerCycle) {
                        add = Context_1.config.p2p.formingNodesPerCycle;
                    }
                    exports.targetCount = active + add;
                    if (exports.targetCount > desired) {
                        exports.targetCount = desired;
                    }
                }
                if (active > desired) {
                    /* prettier-ignore */ if (logger_1.logFlags && logger_1.logFlags.verbose)
                        console.log("CycleAutoScale: entered active > desired");
                    let sub = ~~(0.3 * active);
                    if (sub < 1) {
                        sub = 1;
                    }
                    exports.targetCount = active - sub;
                    if (exports.targetCount < desired) {
                        exports.targetCount = desired;
                    }
                }
            }
        }
        else if (prevRecord.mode === 'processing') {
            /* prettier-ignore */ if (logger_1.logFlags?.verbose)
                Context_1.logger.mainLog_debug('SETANDGETTARGETCOUNT_PROCESSING_1', "CycleAutoScale: in processing");
            if ((0, Modes_1.enterSafety)(active) === false && (0, Modes_1.enterRecovery)(active) === false) {
                /* prettier-ignore */ if (logger_1.logFlags?.verbose)
                    Context_1.logger.mainLog_debug('SETANDGETTARGETCOUNT_PROCESSING_2', "CycleAutoScale: not in safety");
                let addRem = (desired - prevRecord.target) * 0.1;
                /* prettier-ignore */ if (logger_1.logFlags?.verbose)
                    Context_1.logger.mainLog_debug('SETANDGETTARGETCOUNT_PROCESSING_3', `addRem: ${addRem}, desired: ${desired}, prevTarget: ${prevRecord.target}`);
                if (addRem > active * 0.01) {
                    addRem = active * 0.01;
                }
                if (addRem < 0 - active * 0.005) {
                    addRem = 0 - active * 0.005;
                }
                /* prettier-ignore */ if (logger_1.logFlags?.verbose)
                    Context_1.logger.mainLog_debug('SETANDGETTARGETCOUNT_PROCESSING_4', `CycleAutoScale: prev target is ${prevRecord.target} and addRem is ${addRem}`);
                exports.targetCount = prevRecord.target + addRem;
                // may want to swap config values to values from cycle record
                if (exports.targetCount < Context_1.config.p2p.minNodes) {
                    exports.targetCount = Context_1.config.p2p.minNodes;
                }
                else if (exports.targetCount > Context_1.config.p2p.maxNodes) {
                    exports.targetCount = Context_1.config.p2p.maxNodes;
                }
            }
        }
        else if (
        // In these modes, the target count should be set to the desired/min count
        prevRecord.mode === 'safety' ||
            prevRecord.mode === 'restore') {
            // For the number of nodes to be added in each cycle during these modes is defined in the calculateToAcceptV2 function
            // using minNodes here since target is what will be used to get into processing mode
            exports.targetCount = Context_1.config.p2p.minNodes;
        }
        else if (prevRecord.mode === 'recovery') {
            exports.targetCount = Context_1.config.p2p.minNodes + Context_1.config.p2p.extraNodesToAddInRestart;
        }
        else if (prevRecord.mode === 'restart') {
            // In restart mode, all the nodes remain in 'selected?' mode until the desired number of nodes are reached
            //Instruction:  I have a bunch of logs like this but I want to upgrade them to call logger.mainLog_debug.
            /* prettier-ignore */ //if (logFlags && logFlags.verbose) console.log("CycleAutoScale: in restart")
            //Instruction: here is what I want the log to look like.  Note the first argument is a unique key.  The funciton name in all capps followed by some context and then an int that incrments so we dont have dupes will work
            /* prettier-ignore */ if (logger_1.logFlags?.verbose)
                Context_1.logger.mainLog_debug('SETANDGETTARGETCOUNT_RESTART_1', "CycleAutoScale: in restart");
            if (syncing < desired + Context_1.config.p2p.extraNodesToAddInRestart) {
                /* prettier-ignore */ if (logger_1.logFlags && logger_1.logFlags.verbose)
                    console.log("CycleAutoScale: entered syncing < desired");
                let add = ~~(0.5 * syncing); // Add 50% more nodes on each cycle
                if (add < Context_1.config.p2p.formingNodesPerCycle) {
                    add = Context_1.config.p2p.formingNodesPerCycle;
                }
                exports.targetCount = syncing + add;
                if (exports.targetCount > desired + Context_1.config.p2p.extraNodesToAddInRestart) {
                    exports.targetCount = desired + Context_1.config.p2p.extraNodesToAddInRestart;
                }
            }
        }
        else if (prevRecord.mode === 'shutdown')
            exports.targetCount = Context_1.config.p2p.formingNodesPerCycle;
    }
    else if (Self.isFirst && active < 1) {
        /* prettier-ignore */ if (logger_1.logFlags && logger_1.logFlags.verbose)
            console.log("CycleAutoScale: in Self.isFirst condition");
        exports.targetCount = Context_1.config.p2p.formingNodesPerCycle;
    }
    /* prettier-ignore */ if (logger_1.logFlags?.verbose)
        Context_1.logger.mainLog_debug('SETANDGETTARGETCOUNT_1', Context_1.logger.combine('CycleAutoScale: target count is', exports.targetCount));
    return exports.targetCount;
}
function configUpdated() {
    if (exports.desiredCount < Context_1.config.p2p.minNodes) {
        exports.desiredCount = Context_1.config.p2p.minNodes;
        //requestNetworkUpsize updates desiredCount internally
        //we may still want this, but need some special testing to be sure
        //requestNetworkUpsize()
    }
    // if (desiredCount > config.p2p.maxNodes) {
    //   desiredCount = config.p2p.maxNodes
    //   //we may still want this, but need some special testing to be sure
    //   //requestNetworkDownsize()
    // }
}
function queueRequest(request) { }
function sendRequests() { }
//TODO please review this.  It seems we get consensus on scale up/down, but then
//are not using the autoscaling list for the subsequent operations?
//maybe that is ok?
function getTxs() {
    // [IMPORTANT] Must return a copy to avoid mutation
    const requestsCopy = (0, deepmerge_1.default)({}, [...Object.values(exports.scalingRequestsCollector)]);
    return {
        autoscaling: requestsCopy,
    };
}
function validateRecordTypes(rec) {
    let err = (0, utils_1.validateTypes)(rec, { desired: 'n' });
    if (err)
        return err;
    return '';
}
function updateRecord(txs, record, prevRecord) {
    //just in time scaling vote count.
    _checkScaling();
    record.desired = getDesiredCount();
    record.target = setAndGetTargetCount(prevRecord);
    reset();
}
function parseRecord(record) {
    // Since we don't touch the NodeList, return an empty Change
    return {
        added: [],
        removed: [],
        updated: [],
    };
}
function getScaleUpRequests() {
    let requests = [];
    for (let [nodeId, request] of exports.scalingRequestsCollector) {
        if (request.scale === lib_types_1.P2P.CycleAutoScaleTypes.ScaleType.UP)
            requests.push(request);
    }
    return requests;
}
function getScaleDownRequests() {
    let requests = [];
    for (let [nodeId, request] of exports.scalingRequestsCollector) {
        if (request.scale === lib_types_1.P2P.CycleAutoScaleTypes.ScaleType.DOWN)
            requests.push(request);
    }
    return requests;
}
function _addToScalingRequests(scalingRequest) {
    switch (scalingRequest.scale) {
        case lib_types_1.P2P.CycleAutoScaleTypes.ScaleType.UP:
            // This was blocking other votes from comming in.. need to check this in _requestNetworkScaling
            // if (requestedScalingType === P2P.CycleAutoScaleTypes.ScaleType.DOWN) {
            //   warn('Already scaling down this cycle. Cannot add scaling up request.')
            //   return false
            // }
            // Check if we have exceeded the limit of scaling requests
            if (exports.scalingRequestsCollector.size >= Context_1.config.p2p.maxScaleReqs) {
                warn('Max scale up requests already exceeded. Cannot add request.');
                return true; // return true because this should not short circuit gossip
            }
            exports.scalingRequestsCollector.set(scalingRequest.nodeId, scalingRequest);
            // requestedScalingType = P2P.CycleAutoScaleTypes.ScaleType.UP //this was locking out votes for scale down
            // console.log(`Added scale request in cycle ${CycleCreator.currentCycle}, quarter ${CycleCreator.currentQuarter}`, requestedScalingType, scalingRequest)
            //_checkScaling() //Wait till later to calculate this.  Not for perf reasons, but to get the max possible votes considered
            return true;
        case lib_types_1.P2P.CycleAutoScaleTypes.ScaleType.DOWN:
            // This was blocking other votes from comming in.. need to check this in _requestNetworkScaling
            // Check if we are already voting scale up, don't add in that case
            // if (requestedScalingType === P2P.CycleAutoScaleTypes.ScaleType.UP) {
            //   warn('Already scaling up this cycle. Cannot add scaling down request.')
            //   return false
            // }
            // Check if we have exceeded the limit of scaling requests
            if (exports.scalingRequestsCollector.size >= Context_1.config.p2p.maxScaleReqs) {
                warn('Max scale down requests already exceeded. Cannot add request.');
                return true; // return true because this should not short circuit gossip
            }
            exports.scalingRequestsCollector.set(scalingRequest.nodeId, scalingRequest);
            // requestedScalingType = P2P.CycleAutoScaleTypes.ScaleType.DOWN //this was locking out votes for scale up
            //_checkScaling() //Wait till later to calculate this.  Not for perf reasons, but to get the max possible votes considered
            return true;
        default:
            warn(`Invalid scaling type in _addToScalingRequests(). Request: ${lib_types_2.Utils.safeStringify(scalingRequest)}`);
            return false;
    }
}
function _addScalingRequest(scalingRequest) {
    // Check existence of node
    if (!scalingRequest.nodeId)
        return false;
    // Check scaling seen for this node
    if (exports.scalingRequestsCollector.has(scalingRequest.nodeId))
        return false;
    const valid = validateScalingRequest(scalingRequest);
    if (!valid)
        return false;
    // If we pass validation, add to current cycle
    const added = _addToScalingRequests(scalingRequest);
    return added;
}
//TODO: we need to consider using _waitUntilEndOfCycle to schedule voting times
async function _waitUntilEndOfCycle() {
    const currentTime = (0, network_1.shardusGetTime)();
    const nextQ1Start = CycleCreator.nextQ1Start;
    if (logger_1.logFlags.p2pNonFatal)
        info(`Current time is: ${currentTime}`);
    if (logger_1.logFlags.p2pNonFatal)
        info(`Next cycle will start at: ${nextQ1Start}`);
    let timeToWait;
    if (currentTime < nextQ1Start) {
        timeToWait = nextQ1Start - currentTime + Context_1.config.p2p.queryDelay * 1000;
    }
    else {
        timeToWait = 0;
    }
    if (logger_1.logFlags.p2pNonFatal)
        info(`Waiting for ${timeToWait} ms before next cycle marker creation...`);
    await (0, utils_1.sleep)(timeToWait);
}
function info(...msg) {
    const entry = `CycleAutoScale: ${msg.join(' ')}`;
    p2pLogger.info(entry);
}
function warn(...msg) {
    const entry = `CycleAutoScale: ${msg.join(' ')}`;
    p2pLogger.warn(entry);
}
function error(...msg) {
    const entry = `CycleAutoScale: ${msg.join(' ')}`;
    p2pLogger.error(entry);
}
//# sourceMappingURL=CycleAutoScale.js.map