示例#1
0
def info(bot, update):

    logger.info("network")

    response = messages.markdown("<u><b>SmartNode Network<b><u>\n\n",
                                 bot.messenger)

    with bot.nodeList as nodeList:

        if nodeList.synced() and nodeList.enabled():

            lastBlock = nodeList.lastBlock
            created = nodeList.count()
            enabled = nodeList.enabled()
            preEnabled = nodeList.preEnabled
            expired = nodeList.expired
            newStartRequired = nodeList.newStartRequired
            qualifiedNormal = nodeList.qualifiedNormal
            qualifiedUpgrade = nodeList.qualifiedUpgrade
            upgradeModeDuration = nodeList.remainingUpgradeModeDuration
            protocolRequirement = nodeList.protocolRequirement()
            protocolOld = nodeList.count(nodeList.oldProtocol)
            protocolNew = nodeList.count(nodeList.newProtocol)
            initialWait = nodeList.minimumUptime()
            minPosition = int(enabled * 0.1)
            aberration = bot.aberration

            # Fallback if for whatever reason the top node could not filtered which
            # should actually not happen.
            top10Seconds = (int(
                (qualifiedNormal * 55) / 0.5) * (1 + bot.aberration))

            topNode = list(
                filter(lambda x: x.position == minPosition,
                       nodeList.nodes.values()))

            if len(topNode) and topNode[0].lastPaidTime:
                top10FromList = time.time() - topNode[0].lastPaidTime
                if top10FromList < 1.2 * top10Seconds:
                    top10Seconds = top10FromList

            top10Time = util.secondsToText(top10Seconds)

            if upgradeModeDuration:
                upgradeModeDuration = util.secondsToText(upgradeModeDuration)

            response += messages.networkState(
                bot.messenger, lastBlock, created, preEnabled, enabled,
                expired, newStartRequired, qualifiedNormal, qualifiedUpgrade,
                upgradeModeDuration, protocolRequirement, nodeList.oldProtocol,
                nodeList.newProtocol, protocolOld, protocolNew,
                util.secondsToText(initialWait), top10Time, aberration)

        else:
            response += messages.notSynced(bot.messenger)

    return response
示例#2
0
def detail(bot, update):

    response = messages.markdown("<u><b>Detail<b><u>\n\n", bot.messenger)

    userInfo = util.crossMessengerSplit(update)
    userId = userInfo['user'] if 'user' in userInfo else None
    userName = userInfo[
        'name'] if 'name' in userInfo else update.message.from_user.name

    logger.debug("detail - user: {}".format(userId))

    nodesFound = False

    user = bot.database.getUser(userId)
    userNodes = bot.database.getAllNodes(userId)

    if user == None or userNodes == None or len(userNodes) == 0:

        response += messages.nodesRequired(bot.messenger)

    else:

        with bot.nodeList as nodeList:

            minimumUptime = nodeList.minimumUptime()
            top10 = nodeList.enabledWithMinProtocol() * 0.1

            for userNode in userNodes:

                masternode = nodeList.getNodes([userNode['collateral']])[0]

                response += messages.markdown(
                    ("<b>" + userNode['name'] + " - " + masternode.ip + "<b>"),
                    bot.messenger)
                response += "\n  `Status` " + masternode.status
                response += "\n  `Position` " + messages.markdown(
                    masternode.positionString(minimumUptime, top10),
                    bot.messenger)
                response += "\n  `Payee` " + masternode.payee
                response += "\n  `Active since` " + util.secondsToText(
                    masternode.activeSeconds)
                response += "\n  `Last seen` " + util.secondsToText(
                    int(time.time()) - masternode.lastSeen)
                response += "\n  `Last payout (Block)` " + masternode.payoutBlockString(
                )
                response += "\n  `Last payout (Time)` " + masternode.payoutTimeString(
                )
                response += "\n  `Protocol` {}".format(masternode.protocol)
                #response += "\n  `Rank` {}".format(masternode.rank)
                response += "\n  " + messages.link(
                    bot.messenger,
                    'https://explorer3.curium.cc/address/{}'.format(
                        masternode.payee), 'Open the explorer!')
                response += "\n\n"

    return response
示例#3
0
def nodes(bot, update):

    response = messages.markdown("<u><b>Nodes<b><u>\n\n", bot.messenger)

    userInfo = util.crossMessengerSplit(update)
    userId = userInfo['user'] if 'user' in userInfo else None
    userName = userInfo['name'] if 'name' in userInfo else None

    logger.debug("nodes - user: {}".format(userId))

    nodesFound = False

    user = bot.database.getUser(userId)
    userNodes = bot.database.getAllNodes(userId)

    if user == None or userNodes == None or len(userNodes) == 0:

        response += messages.nodesRequired(bot.messenger)

    else:

        with bot.nodeList as nodeList:

            collaterals = list(map(lambda x: x['collateral'], userNodes))
            nodes = nodeList.getNodes(collaterals)
            minimumUptime = nodeList.minimumUptime()
            top10 = nodeList.enabledWithMinProtocol() * 0.1

            for masternode in sorted(nodes,
                                     key=lambda x: x.position
                                     if x.position > 0 else 100000):

                userNode = bot.database.getNodes(masternode.collateral,
                                                 user['id'])

                payoutText = util.secondsToText(masternode.lastPaidTime)
                response += messages.markdown(
                    "<b>" + userNode['name'] + "<b> - `" + masternode.status +
                    "`", bot.messenger)
                response += "\nPosition " + messages.markdown(
                    masternode.positionString(minimumUptime, top10),
                    bot.messenger)
                response += "\nLast seen " + util.secondsToText(
                    int(time.time()) - masternode.lastSeen)
                response += "\nLast payout " + masternode.payoutTimeString()
                response += "\n" + messages.link(
                    bot.messenger,
                    'https://explorer3.curium.cc/address/{}'.format(
                        masternode.payee), 'Open the explorer!')
                response += "\n\n"

    return response
示例#4
0
def payouts(bot, args):

    logger.info("payouts")

    response = messages.markdown("<u><b>Payout statistics<b><u>\n\n",
                                 bot.messenger)

    if not bot.rewardList.running:
        response += "Not initialized yet. Wait a bit..."
        return response

    hours = 12

    if len(args):
        try:
            hours = float(args[0])
        except:
            pass

    start = time.time() - (hours * 3600)

    firstReward = bot.rewardList.getNextReward(start)
    lastReward = bot.rewardList.getLastReward()

    if not firstReward:
        response += "Could not fetch the rewards in the given time range!\n"
        response += "The last available is at block {} from {} ago.".format(
            lastReward.block,
            util.secondsToText(time.time() - lastReward.txtime))
        return response

    total = bot.rewardList.getRewardCount(start=start)
    vChain = bot.rewardList.getRewardCount(start=start, source=0, meta=0)
    iChain = bot.rewardList.getRewardCount(start=start, meta=1)
    nList = bot.rewardList.getRewardCount(start=start, source=1)
    err = bot.rewardList.getRewardCount(start=start, meta=-1)

    response += "Blocks: {}\n".format(lastReward.block - firstReward.block)
    response += "RT: {}\n".format(
        util.secondsToText(lastReward.txtime - firstReward.txtime))
    response += "P: {}\n".format(total)
    response += "V: {}\n".format(vChain)
    response += "I: {}\n".format(iChain)
    response += "NL: {}\n".format(nList)
    response += "ERR: {}\n".format(err)
    response += "E: {}%".format(round((1 - (nList / total)) * 100, 1))

    return response
示例#5
0
def nodeUpdated(bot, update, user, userNode, node):

    responses = []

    nodeName = userNode['name']

    if update['status'] and user['status_n']:

        response = messages.statusNotification(bot.messenger, nodeName,
                                               node.status)
        responses.append(response)

    if update['timeout'] and user['timeout_n']:

        if node.timeout != -1:
            timeString = util.secondsToText(int(time.time()) - node.lastSeen)
            response = messages.panicNotification(bot.messenger, nodeName,
                                                  timeString)
        else:
            response = messages.relaxNotification(bot.messenger, nodeName)

        responses.append(response)

    if update['lastPaid'] and user['reward_n']:

        # Prevent zero division if for any reason lastPaid is 0
        calcBlock = node.lastPaidBlock if node.lastPaidBlock != 0 else bot.nodeList.lastBlock
        reward = 5000 * (143500 / calcBlock) * 0.1

        response = messages.rewardNotification(bot.messenger, nodeName,
                                               calcBlock, reward)
        responses.append(response)

    return responses
示例#6
0
    def remainingString(self):

        s = self.votingDeadline
        seconds = self.remainingSeconds()

        if seconds:
            s = util.secondsToText(seconds)

        return s
def position(bot):

    with bot.nodeList as nodeList:

        if not nodeList.synced() or not nodeList.enabled():
            return messages.notSynced(bot.messenger)


        nodes = nodeList.count()
        enabled = nodeList.enabled()
        qualified = nodeList.qualifiedNormal
        unqualified = nodes - qualified
        minPosition = int(enabled * 0.1)
        top10Seconds = util.secondsToText(int((qualified * 55) * (1 + bot.aberration)))
        topNode = list(filter(lambda x: x.position == minPosition, nodeList.nodes.values()))

        if len(topNode) and topNode[0].lastPaidTime:
            top10Seconds = time.time() - topNode[0].lastPaidTime

        top10Time = util.secondsToText(top10Seconds)

        return (
        "We have currenty <b>{}<b> qualified MasterNodes. All those are in a "
        "virtual payout queue. Each mined block (55seconds) one "
        "of the nodes in the top 10% of that queue gets picked perchance and"
        " receives a payout. After that it moves back to the end of the queue.\n\n"
        "The position of your node represents the position in the queue. Once "
        "your node has a position <b>less<b> than (currently) <b>{}<b>"
        " it is in the <b>random payout zone<b> (top 10%).\n\n"
        "The minimum position depends on the number of enabled nodes and will change"
        " when the number of enabled nodes changes.\n\n"
        "Right now it takes <b>{}<b> to reach the payout zone.\n\n"
        "It's normal and to be expected that your nodes moves also backwards a "
        "bit in the queue from time to time. This happens each time a node that"
        " has received its last payment longer ago as yours becomes eligible"
        " for payouts (it jumpes into the queue and receives a position)."
        " At this moment we have <b>{}<b> MasterNodes. <b>{}<b> of them are"
        " <b>not<b> qualified for payouts. Each time one of the unqualified"
        " nodes becomes eligible due to a full match of the requirements"
        " it is very likely that it will jump ahead of yours.\n\n"
        "You can send me <cb>info<ca> to see the number of qualified nodes, "
        "to check your nodes positions send me <cb>detail<ca>, <cb>nodes<ca>"
        " <cb>top<ca> or use the <cb>lookup<ca> command.\n\n"
        ).format(qualified, minPosition, top10Time, nodes, unqualified)
    def status(self, bot, update):

        response = "*Cryptopia*\n"
        response += "Last updated {}\n".format(util.secondsToText(int(time.time() - self.cryptopia['updated'])))
        response += "*Status* `{}`\n".format(self.cryptopia['status'])
        response += "*Message* `{}`\n\n".format(self.cryptopia['message'])

        response += "*HitBTC*\n"
        response += "Last updated {}\n".format(util.secondsToText(int(time.time() - self.hitbtc['updated'])))
        response += "*Deposit* `{}`\n".format(self.hitbtc['deposit'])
        response += "*Withdraw* `{}`\n\n".format(self.hitbtc['withdraw'])

        response += "*Coinexchange*\n"
        response += "Last updated {}\n".format(util.secondsToText(int(time.time() - self.coinexchange['updated'])))
        response += "*Wallet* `{}`\n".format(self.coinexchange['wallet'])
        
        self.database.addChat(update.message.chat_id)

        self.sendMessage(update.message.chat_id, response)
    def calculateUpgradeModeDuration(self):

        # Start with an accuracy of 5 nodes.
        # Will become increased if it takes too long
        accuracy = 20
        # Minimum required nodes to continue with normal mode
        requiredNodes = int(self.enabledWithMinProtocol() / 3)
        # Get the max active seconds to determine a start point
        currentCheckTime = max(list(map(lambda x: x.activeSeconds if x.protocol >= self.protocolRequirement() and x.status == 'ENABLED' else 0, self.nodes.values())))
        logger.debug("Maximum uptime {}".format(currentCheckTime))
        # Start value
        step = currentCheckTime * 0.5
        currentCheckTime -= step

        # Start time for accuracy descrease if needed
        start = int(time.time())
        rounds = 1

        calcCount = None

        while accuracy < 1000:

            step *= 0.5

            calcCount = len(list(filter(lambda x: x.protocol == self.protocolRequirement() and\
                                                  x.status == 'ENABLED' and\
                                                  (self.lastBlock - x.collateral.block) >= self.minimumConfirmations() and\
                                                  x.activeSeconds > currentCheckTime, self.nodes.values() )))

            logger.debug("Current count: {}".format(calcCount))
            logger.debug("Current time: {}".format(currentCheckTime))
            logger.debug("Current accuracy: {}".format(accuracy))
            logger.debug("Current accuracy matched: {}\n".format(abs(requiredNodes - calcCount)))

            if int(time.time()) - start >= 2 * rounds:
                rounds += 1
                accuracy += 20

            if abs(requiredNodes - calcCount) < accuracy:
                logger.info("Final accuracy {}".format(accuracy))
                logger.info("Final accuracy matched {}".format(abs(requiredNodes - calcCount)))
                logger.info("Remaining duration: {}".format( util.secondsToText((self.minimumUptime() - currentCheckTime))))
                logger.info("CalcTime: {}, Rounds: {}".format( int(time.time()) - start,rounds))
                return self.minimumUptime() - currentCheckTime
            elif calcCount > requiredNodes:
                currentCheckTime += step
            else:
                currentCheckTime -= step

        logger.warning("Could not determine duration?!")
        logger.warning("Final accuracy {}".format(accuracy))
        logger.warning("Final accuracy before step out {}".format(abs(requiredNodes - calcCount)))

        return None
示例#10
0
def info(bot, update):

    logger.info("network")

    response = messages.markdown("<u><b>SmartNode Network<b><u>\n\n",
                                 bot.messenger)

    if bot.nodeList.synced() and bot.nodeList.lastBlock:

        bot.nodeList.acquire()

        lastBlock = bot.nodeList.lastBlock
        created = bot.nodeList.count()
        enabled = bot.nodeList.enabled()
        qualifiedNormal = bot.nodeList.qualifiedNormal
        qualifiedUpgrade = bot.nodeList.qualifiedUpgrade
        upgradeModeDuration = bot.nodeList.remainingUpgradeModeDuration
        protocolRequirement = bot.nodeList.protocolRequirement()
        protocol90024 = bot.nodeList.count(90024)
        protocol90025 = bot.nodeList.count(90025)
        initialWait = bot.nodeList.minimumUptime()

        if upgradeModeDuration:
            upgradeModeDuration = util.secondsToText(upgradeModeDuration)

        bot.nodeList.release()

        response += messages.networkState(bot.messenger, lastBlock, created,
                                          enabled, qualifiedNormal,
                                          qualifiedUpgrade,
                                          upgradeModeDuration,
                                          protocolRequirement, protocol90024,
                                          protocol90025,
                                          util.secondsToText(initialWait))

    else:
        response += "*Sorry, the bot is currently not synced with the network. Try it again in few minutes...*"

    return response
    def positionString(self, minimumUptime, top10 = None):

        if self.position == POS_CALCULATING:
            return "Calculating..."
        elif self.position == POS_UPDATE_REQUIRED:
            return "Node update required!"
        elif self.position == POS_TOO_NEW:
            leftMessage = util.secondsToText(minimumUptime - self.activeSeconds)
            return "Initial wait time! <b>{}<b> left".format(leftMessage)
        elif self.position == POS_COLLATERAL_AGE:
            return "Collateral too new!"
        elif self.position == POS_NOT_QUALIFIED:
            return "Not qualified!"
        elif top10 and self.position <= top10:
            return str(self.position) + " - <b>Payout zone<b>"
        else:
            return str(self.position)
示例#12
0
def rewards(bot):

    with bot.nodeList as nodeList:

        if not nodeList.synced() or not nodeList.enabled():
            return messages.notSynced(bot.messenger)

        enabled = nodeList.enabled()
        minPosition = int(enabled * 0.1)
        qualified = nodeList.qualifiedNormal
        lastBlock = nodeList.lastBlock

        # Fallback if for whatever reason the top node could not filtered which
        # should actually not happen.
        top10Seconds = (int((qualified * 55) / 0.5) * (1 + bot.aberration))

        topNode = list(filter(lambda x: x.position == minPosition, nodeList.nodes.values()))

        if len(topNode) and topNode[0].lastPaidTime:
            top10FromList = time.time() - topNode[0].lastPaidTime
            if top10FromList < 1.2 * top10Seconds:
                top10Seconds = top10FromList

        payoutSeconds = top10Seconds + (10 * 60 * 60)
        payoutDays = payoutSeconds / 86400.0
        interval = util.secondsToText(int(payoutSeconds))
        currentReward = round(5000.0 * 143500.0 / lastBlock * 0.1,1) / 0.5
        perMonth = round((30.5 / payoutDays) * currentReward,1)

        return (
        "The SmartNode rewards are calculated by the following formula\n\n"
        "```reward = 5000 x 143500 / blockHeight * 0.1```\n\n"
        "At this moment our blockchain is at the height <b>{}<b> that means"
        "\n\n```5000 x 143500 / {} * 0.1 => {} SMART per block```\n\n"
        "Each block with an <b>even<b> blockheight one of the the nodes receive this reward for 2 blocks. With the current "
        "estimated payout interval of <b>{}<b> you can expect roughly"
        " <b>{:,} SMART<b> per month per SmartNode. This can vary a bit upwards and downwards though.\n\n"
        "Due to the constant increase of the <c>blockHeight<c> of the SmartCash blockchain"
        " the rewards will decrease a little bit every 55 seconds."
        " Also the increase of the number of qualified nodes will increase the payout interval."
        " As result your monthly payout will slightly decrease over the time.\n\n"
        "You can look at the chart in the link below to see the reward decrease "
        "for the first 4 years after the SmartNode launch.\n\n"
        ).format(lastBlock, lastBlock, currentReward, interval, perMonth)\
        + messages.link(bot.messenger, "https://goo.gl/Va817H", "Click here to open the chart")
    def lookup(self, ip):

        result = None

        node = self.getNodeByIp(ip)

        logger.info("lookup {} - found {}".format(ip, node != None))

        if node:

            result = {}

            uptimeString = None

            if node.activeSeconds > 0:
                uptimeString = util.secondsToText(node.activeSeconds)
            else:
                uptimeString = "No uptime!"

            result['ip'] = node.cleanIp()
            result['position'] = node.position < self.enabledWithMinProtocol(
            ) * 0.1 and node.position > 0
            result['position_string'] = node.positionString(
                self.minimumUptime())

            result['status'] = node.status == 'ENABLED'
            result['status_string'] = "{}".format(node.status)

            result['uptime'] = node.activeSeconds >= self.minimumUptime()
            result['uptime_string'] = uptimeString

            result['protocol'] = node.protocol == self.protocolRequirement()
            result['protocol_string'] = "{}".format(node.protocol)

            result['collateral'] = (self.lastBlock - node.collateral.block
                                    ) >= self.enabledWithMinProtocol()

            result['collateral_string'] = "{}".format(
                (self.lastBlock - node.collateral.block))

            result['upgrade_mode'] = self.qualifiedUpgrade != -1

        return result
def initial(bot):

    with bot.nodeList as nodeList:

        if not nodeList.synced() or not nodeList.enabled():
            return messages.notSynced(bot.messenger)

        initialWait = util.secondsToText(nodeList.minimumUptime())

        return (
        "When your node shows <b>Initial wait time<b> instead of a position it's"
        " uptime does not match the minimum uptime requirement. At this time the"
        " node must have a minimum uptime of <b>{}<b>."
        " Your uptime will be set to zero when your node was down for more than"
        " <b>1 hour<b> or when you issue a <b>new start<b> of the node with your desktop wallet"
        " by running <c>Start alias<c>.\n\n"
        "You can check your node's uptime when you send me <cb>detail<ca> or by"
        " running the <cb>lookup<ca> command.\n\n"
        ).format(initialWait)
def collateral(bot):

    with bot.nodeList as nodeList:

        if not nodeList.synced() or not nodeList.enabled():
            return messages.notSynced(bot.messenger)

        confirmations = nodeList.enabledWithMinProtocol()
        timeString = util.secondsToText(confirmations * 55)

        return (
        "A too new collateral means that your nodes collateral transaction (the 10k one) does not"
        " have the minimum required number of confirmations in the Curium blockchain."
        "This number of confirmations is currently <b>{}<b>.\nYour collateral gets 1 confirmation with each"
        " new mined block.\n\nMeans right now you need to wait {} x 55 seconds => <b>~{}<b> until"
        " your collateral transaction matches the requirement.\n\n"
        "You can check your nodes collateral confirmations"
        " by running the <cb>lookup<ca> command.\n\n"
        ).format(confirmations, confirmations, timeString)
def qualified(bot):

    with bot.nodeList as nodeList:

        if not nodeList.synced() or not nodeList.enabled():
            return messages.notSynced(bot.messenger)

        confirmations = nodeList.enabledWithMinProtocol()
        initialWait = util.secondsToText(nodeList.minimumUptime())
        protocolRequirement = nodeList.protocolRequirement()

        return (
        "Your node has to match the following requirements before it's ready to"
        " receive payouts\n\n"
        "- The node's status has to be <b>ENABLED<b>\n"
        "- The node's collateral transaction needs to have at least <b>{}<b>"
        " confirmations\n"
        "- The node must have a minimum uptime of <b>{}<b>\n"
        "- The node must run at least on the protocol <b>{}<b> number\n\n"
        "Once your node matches <b>all<b> the above requirements it will get a"
        " position in the payout queue.\n\n"
        "You can check all this requirements for your nodes when you send me"
        " <cb>detail<ca> or by running the <cb>lookup<ca> command.\n\n"
        ).format(confirmations, initialWait, protocolRequirement)
示例#17
0
    def updateList(self):

        try:
            self.updateSyncState()
        except RuntimeError as e:
            logger.error("updateList sync exception: {}".format(e))
            self.startTimer()
            return

        else:

            if not self.chainSynced or not self.nodeListSynced or not self.winnersListSynced:
                logger.error("Not synced! C {}, N {} W {}".format(self.chainSynced, self.nodeListSynced, self.winnersListSynced))

                # If nodelist or winnerslist was out of sync
                # wait 5 minutes after sync is done
                # to prevent false positive timeout notifications
                if not self.nodeListSynced or not self.winnersListSynced:
                    self.syncedTime = -2

                self.startTimer()

                return

        if self.syncedTime == -2:
            self.syncedTime = time.time()
            logger.info("Synced now! Wait 5 minutes and then start through...")
            self.startTimer()
            return

        # Wait 5 minutes here to prevent timeout notifications. Past showed that
        # the lastseen times are not good instantly after sync.
        elif self.syncedTime > -1 and (time.time() - self.syncedTime) < 300:
            logger.info("After sync wait {}".format(util.secondsToText(time.time() - self.syncedTime)))
            self.startTimer()
            return

        newNodes = []

        nodes = None
        info = None

        try:

            infoResult = subprocess.check_output(['smartcash-cli', 'getinfo'])
            info = json.loads(infoResult.decode('utf-8'))

            nodeResult = subprocess.check_output(['smartcash-cli', 'smartnodelist','full'])
            nodes = json.loads(nodeResult.decode('utf-8'))

        except Exception as e:

                logging.error('Error at %s', 'update list', exc_info=e)

                self.pushAdmin("Error at updateList")

        else:

            if not self.isValidDeamonResponse(nodes):
                self.pushAdmin("No valid nodeList")
                return

            if not self.isValidDeamonResponse(info):
                self.pushAdmin("No valid network info")
                return

            if "blocks" in info:
                self.lastBlock = info["blocks"]

            node = None
            currentList = []
            self.lastPaidVec = []
            currentTime = int(time.time())
            protocolRequirement = self.protocolRequirement()

            dbCount = self.db.getNodeCount()

            # Prevent mass deletion of nodes if something is wrong
            # with the fetched nodelist.
            if dbCount and len(nodes) and ( dbCount / len(nodes) ) > 1.25:
                self.pushAdmin("Node count differs too much!")
                logger.warning("Node count differs too much! - DB {}, CLI {}".format(dbCount,len(nodes)))
                self.startTimer()
                return

            # Prevent reading during the calculations
            self.acquire()

            # Reset the calculation vars
            self.qualifiedNormal = 0

            for key, data in nodes.items():

                collateral = Transaction.fromRaw(key)

                currentList.append(collateral)

                if collateral not in self.nodeList:

                    collateral.updateBlock(self.getCollateralAge(collateral.hash))

                    logger.info("Add node {}".format(key))
                    insert = SmartNode.fromRaw(collateral, data)

                    id = self.db.addNode(collateral,insert)

                    if id:
                        self.nodeList[collateral] = insert
                        newNodes.append(collateral)

                        logger.debug(" => added with collateral {}".format(insert.collateral))
                    else:
                        logger.error("Could not add the node {}".format(key))

                else:

                    node = self.nodeList[collateral]
                    collateral = node.collateral
                    update = node.update(data)

                    if update['status']\
                    or update['protocol']\
                    or update['payee']\
                    or update['lastPaid']\
                    or update['timeout']:
                        self.db.updateNode(collateral,node)

                    if sum(map(lambda x: x, update.values())):

                        if self.nodeChangeCB != None:
                            self.nodeChangeCB(update, node)

                #####
                ## Check if the collateral height is already detemined
                ## if not try it!
                #####

                if collateral.block <= 0:
                    logger.info("Collateral block missing {}".format(str(collateral)))

                    collateral.updateBlock(self.getCollateralAge(collateral.hash))

                    if collateral.block > 0:
                        self.db.updateNode(collateral,node)
                    else:
                        logger.warning("Could not fetch collateral block {}".format(str(collateral)))


            #####
            ## Invoke the callback if we have new nodes
            #####

            if len(newNodes) and self.networkCB:

                self.networkCB(newNodes, True)

                logger.info("Created: {}".format(len(nodes.values())))
                logger.info("Enabled: {}\n".format(sum(map(lambda x: x.split()[STATUS_INDEX]  == "ENABLED", list(nodes.values())))))

            #####
            ## Remove nodes from the DB that are not longer in the global list
            #####

            dbCount = self.db.getNodeCount()

            if dbCount > len(nodes):

                removedNodes = []

                logger.warning("Unequal node count - DB {}, CLI {}".format(dbCount,len(nodes)))

                dbNodes = self.db.getNodes(['collateral'])

                for dbNode in dbNodes:

                    collateral = Transaction.fromString(dbNode['collateral'])

                    if not collateral in currentList:
                        logger.info("Remove node {}".format(dbNode))
                        removedNodes.append(dbNode['collateral'])
                        self.db.deleteNode(collateral)
                        self.nodeList.pop(collateral,None)

                if len(removedNodes) != (dbCount - len(nodes)):
                    logger.warning("Remove nodes - something messed up.")

                if self.networkCB:
                    self.networkCB(removedNodes, False)

            logger.info("calculatePositions start")

            #####
            ## Update vars for calculations
            #
            ####

            nodes90024 = list(filter(lambda x: x.protocol == 90024, self.nodeList.values()))
            nodes90025 = list(filter(lambda x: x.protocol == 90025, self.nodeList.values()))

            self.protocol_90024 = len(nodes90024)
            self.protocol_90025 = len(nodes90025)

            self.enabled_90024 = len(list(filter(lambda x: x.status == "ENABLED", nodes90024)))
            self.enabled_90025 = len(list(filter(lambda x: x.status == "ENABLED", nodes90025)))

            #####
            ## Update the the position indicator of the node
            #
            # CURRENTL MISSING:
            #   https://github.com/SmartCash/smartcash/blob/1.1.1/src/smartnode/smartnodeman.cpp#L554
            #####

            def calculatePositions(upgradeMode):

                self.lastPaidVec = []

                for collateral, node in self.nodeList.items():

                    if not upgradeMode and node.activeSeconds < self.minimumUptime():# https://github.com/SmartCash/smartcash/blob/1.1.1/src/smartnode/smartnodeman.cpp#L561
                        node.updatePosition(POS_TOO_NEW)
                    elif node.protocol < protocolRequirement:# https://github.com/SmartCash/smartcash/blob/1.1.1/src/smartnode/smartnodeman.cpp#L545
                        node.updatePosition(POS_UPDATE_REQUIRED)
                    elif (self.lastBlock - node.collateral.block) < self.enabledWithMinProtocol():
                        node.updatePosition(POS_COLLATERAL_AGE)
                    elif node.status == 'ENABLED': #https://github.com/SmartCash/smartcash/blob/1.1.1/src/smartnode/smartnodeman.cpp#L539
                        self.lastPaidVec.append(LastPaid(node.lastPaidBlock, collateral))
                    else:
                        node.updatePosition(POS_NOT_QUALIFIED)

                if not upgradeMode and len(self.lastPaidVec) < (self.enabledWithMinProtocol() / 3):
                    self.qualifiedUpgrade = len(self.lastPaidVec)
                    logger.info("Start upgradeMode calculation: {}".format(self.qualifiedUpgrade))
                    calculatePositions(True)
                    return

                if not upgradeMode:
                    self.qualifiedUpgrade = -1

                self.qualifiedNormal = len(self.lastPaidVec)

            calculatePositions(False)


            #####
            ## Update positions
            #####

            self.lastPaidVec.sort()

            value = 0
            for lastPaid in self.lastPaidVec:
                value +=1
                self.nodeList[lastPaid.transaction].updatePosition(value)

            logger.info("calculatePositions done")

            if self.qualifiedUpgrade != -1:
                logger.info("calculateUpgradeModeDuration start")
                self.remainingUpgradeModeDuration = self.calculateUpgradeModeDuration()
                logger.info("calculateUpgradeModeDuration done {}".format("Success" if self.remainingUpgradeModeDuration else "Error?"))

            self.release()

        #####
        # Disabled rank updates due to confusion of the users
        #self.updateRanks()
        #####
        self.startTimer()
    def updateList(self):

        if not self.chainSynced or not self.nodeListSynced or not self.winnersListSynced:
            logger.error("Not synced! C {}, N {} W {}".format(
                self.chainSynced, self.nodeListSynced, self.winnersListSynced))

            # If nodelist or winnerslist was out of sync
            # wait 5 minutes after sync is done
            # to prevent false positive timeout notifications
            if not self.nodeListSynced or not self.winnersListSynced:
                self.syncedTime = -2

            return False

        if self.syncedTime == -2:
            self.syncedTime = time.time()
            logger.info(
                "Synced now! Wait {} minutes and then start through...".format(
                    self.waitAfterSync / 60))
            return False

        # Wait 5 minutes here to prevent timeout notifications. Past showed that
        # the lastseen times are not good instantly after sync.
        elif self.syncedTime > -1 and (time.time() -
                                       self.syncedTime) < self.waitAfterSync:
            logger.info("After sync wait {}".format(
                util.secondsToText(time.time() - self.syncedTime)))
            return False

        newNodes = []
        removedNodes = []

        info = self.rpc.getInfo()
        rpcNodes = self.rpc.getMasterNodeList('full')

        if info.error:
            msg = "updateList getInfo: {}".format(str(info.error))
            logging.error(msg)
            self.pushAdmin(msg)
            return False
        elif not "blocks" in info.data:
            self.pushAdmin("Block info missing?!")
        else:
            self.lastBlock = info.data["blocks"]

        if rpcNodes.error:
            msg = "updateList getMasterNodeList: {}".format(str(
                rpcNodes.error))
            logging.error(msg)
            self.pushAdmin(msg)
            return False

        rpcNodes = rpcNodes.data
        node = None

        currentList = []
        self.lastPaidVec = []
        currentTime = int(time.time())
        protocolRequirement = self.protocolRequirement()

        dbCount = self.db.getNodeCount()

        # Prevent mass deletion of nodes if something is wrong
        # with the fetched nodelist.
        if dbCount and len(rpcNodes) and (dbCount / len(rpcNodes)) > 1.25:
            self.pushAdmin("Node count differs too much!")
            logger.warning(
                "Node count differs too much! - DB {}, CLI {}".format(
                    dbCount, len(rpcNodes)))
            return False

        # Prevent reading during the calculations
        self.acquire()

        # Reset the calculation vars
        self.qualifiedNormal = 0

        for key, data in rpcNodes.items():

            collateral = Transaction.fromRaw(key)

            currentList.append(collateral)

            if collateral not in self.nodes:

                collateral.updateBlock(self.getCollateralAge(collateral.hash))

                logger.info("Add node {}".format(key))
                insert = MasterNode.fromRaw(collateral, data)

                id = self.db.addNode(collateral, insert)

                if id:
                    self.nodes[collateral] = insert
                    newNodes.append(collateral)

                    logger.debug(" => added with collateral {}".format(
                        insert.collateral))
                else:
                    logger.error("Could not add the node {}".format(key))

            else:

                node = self.nodes[collateral]
                collateral = node.collateral
                update = node.update(data)

                if update['status']\
                or update['protocol']\
                or update['payee']\
                or update['lastPaid']\
                or update['ip']\
                or update['timeout']:
                    self.db.updateNode(collateral, node)

                if sum(map(lambda x: x, update.values())):

                    if self.nodeChangeCB != None:
                        self.nodeChangeCB(update, node)

            #####
            ## Check if the collateral height is already detemined
            ## if not try it!
            #####

            if collateral.block <= 0:
                logger.info("Collateral block missing {}".format(
                    str(collateral)))

                collateral.updateBlock(self.getCollateralAge(collateral.hash))

                if collateral.block > 0:
                    self.db.updateNode(collateral, node)
                else:
                    logger.warning(
                        "Could not fetch collateral block {}".format(
                            str(collateral)))

        #####
        ## Remove nodes from the DB that are not longer in the global list
        #####

        dbCount = self.db.getNodeCount()

        if dbCount > len(rpcNodes):

            logger.warning("Unequal node count - DB {}, CLI {}".format(
                dbCount, len(rpcNodes)))

            dbNodes = self.db.getNodes(['collateral'])

            for dbNode in dbNodes:

                collateral = Transaction.fromString(dbNode['collateral'])

                if not collateral in currentList:
                    logger.info("Remove node {}".format(dbNode))
                    removedNodes.append(dbNode['collateral'])
                    self.db.deleteNode(collateral)
                    self.nodes.pop(collateral, None)

            if len(removedNodes) != (dbCount - len(rpcNodes)):
                err = "Remove nodes - something messed up."
                self.pushAdmin(err)
                logger.error(err)

        logger.info("calculatePositions start")

        #####
        ## Update vars for calculations
        #
        ####

        nodes90024 = list(
            filter(lambda x: x.protocol == 90024, self.nodes.values()))
        nodes90025 = list(
            filter(lambda x: x.protocol == 90025, self.nodes.values()))

        self.protocol_90024 = len(nodes90024)
        self.protocol_90025 = len(nodes90025)

        self.enabled_90024 = len(
            list(filter(lambda x: x.status == "ENABLED", nodes90024)))
        self.enabled_90025 = len(
            list(filter(lambda x: x.status == "ENABLED", nodes90025)))

        #####
        ## Update the the position indicator of the node
        #
        # CURRENTL MISSING:
        #   https://github.com/Curium/curium/blob/1.1.1/src/masternode/masternodeman.cpp#L554
        #####

        def calculatePositions(upgradeMode):

            self.lastPaidVec = []

            for collateral, node in self.nodes.items():

                if (self.lastBlock -
                        node.collateral.block) < self.enabledWithMinProtocol():
                    node.updatePosition(POS_COLLATERAL_AGE)
                elif node.protocol < protocolRequirement:  # https://github.com/Curium/curium/blob/1.1.1/src/masternode/masternodeman.cpp#L545
                    node.updatePosition(POS_UPDATE_REQUIRED)
                elif not upgradeMode and node.activeSeconds < self.minimumUptime(
                ):  # https://github.com/Curium/curium/blob/1.1.1/src/masternode/masternodeman.cpp#L561
                    node.updatePosition(POS_TOO_NEW)
                elif node.status != 'ENABLED':  #https://github.com/Curium/curium/blob/1.1.1/src/masternode/masternodeman.cpp#L539
                    node.updatePosition(POS_NOT_QUALIFIED)
                else:
                    self.lastPaidVec.append(
                        LastPaid(node.lastPaidBlock, collateral))

            if not upgradeMode and len(
                    self.lastPaidVec) < (self.enabledWithMinProtocol() / 3):
                self.qualifiedUpgrade = len(self.lastPaidVec)
                logger.info("Start upgradeMode calculation: {}".format(
                    self.qualifiedUpgrade))
                calculatePositions(True)
                return

            if not upgradeMode:
                self.qualifiedUpgrade = -1

            self.qualifiedNormal = len(self.lastPaidVec)

        calculatePositions(False)

        #####
        ## Update positions
        #####

        self.lastPaidVec.sort()

        value = 0
        for lastPaid in self.lastPaidVec:
            value += 1
            self.nodes[lastPaid.transaction].updatePosition(value)

        logger.info("calculatePositions done")

        if self.qualifiedUpgrade != -1:
            logger.info("calculateUpgradeModeDuration start")
            self.remainingUpgradeModeDuration = self.calculateUpgradeModeDuration(
            )
            logger.info("calculateUpgradeModeDuration done {}".format(
                "Success" if self.remainingUpgradeModeDuration else "Error?"))

        self.release()

        #####
        ## Invoke the callback if we have new nodes or nodes left
        #####

        if len(newNodes) and self.networkCB:
            self.networkCB(newNodes, True)

        if len(removedNodes) and self.networkCB:
            self.networkCB(removedNodes, False)

        return True
    def payoutTimeString(self):

        if self.lastPaidTime > 0:
            return util.secondsToText(int(time.time()) - self.lastPaidTime)

        return "No payout yet."
    def update(self, raw):

        update = {
            'status': False,
            'payee': False,
            'timeout': False,
            'lastPaid': False,
            'protocol': False,
            'ip': False
        }

        data = raw.split()

        status = data[STATUS_INDEX].replace(
            '_', '-')  # replace _ with - to avoid md problems

        if self.status != status:
            logger.info("[{}] Status updated {} => {}".format(
                self.collateral, self.status, status))
            update['status'] = True
            self.status = status

        if int(self.protocol) != int(data[PROTOCOL_INDEX]):
            logger.info("[{}] Protocol updated {} => {}".format(
                self.collateral, self.protocol, int(data[PROTOCOL_INDEX])))
            update['protocol'] = True
            self.protocol = int(data[PROTOCOL_INDEX])

        if self.payee != data[PAYEE_INDEX]:
            logger.info("[{}] Payee updated {} => {}".format(
                self.collateral, self.payee, data[PAYEE_INDEX]))
            update['payee'] = True
            self.payee = data[PAYEE_INDEX]

        self.lastSeen = int(data[SEEN_INDEX])
        lastSeenDiff = (int(time.time()) - self.lastSeen)
        if lastSeenDiff > 1800 and\
            lastSeenDiff < 3900: # > 30min < 65min

            if ( self.timeout == -1 or \
              ( int(time.time()) - self.timeout ) > 300 ) and\
              self.status == 'ENABLED':
                self.timeout = int(time.time())
                update['timeout'] = True

        elif self.timeout != -1 and self.status == 'ENABLED':
            self.timeout = -1
            update['timeout'] = True

        self.activeSeconds = int(data[ACTIVE_INDEX])

        lastPaidBlock = int(data[PAIDBLOCK_INDEX])

        if self.lastPaidBlock != lastPaidBlock and lastPaidBlock != 0:
            logger.info("[{}] Reward {} - Last: {}, P: {}, UP: {}".format(
                self.collateral, lastPaidBlock, self.payoutTimeString(),
                self.position, util.secondsToText(self.activeSeconds)))
            self.lastPaidBlock = lastPaidBlock
            self.lastPaidTime = int(data[PAIDTIME_INDEX])

            if self.lastPaidBlock != 0 and self.lastPaidBlock != -1:
                update['lastPaid'] = True

        if self.ip != data[IPINDEX_INDEX]:
            logger.info("[{}] IP updated {} => {}".format(
                self.collateral, self.ip, data[IPINDEX_INDEX]))
            update['ip'] = True
            self.ip = data[IPINDEX_INDEX]

        if update['timeout']:
            logger.debug("[{}] Timeout updated {}".format(
                self.collateral, self.timeout))

        return update
示例#21
0
    def run(self):

        self.sem.acquire()

        for chatId, queue in self.queues.items():

            if not self.ready():
                logger.debug("MessagingMachine not ready {}".format(self.leftover))
                break

            if not queue.ready():
                logger.debug("Queue not ready {}".format(queue))
                continue

            err = True

            message = queue.next()

            if message == None:
                continue

            try:
                self.bot.sendMessage(chat_id=chatId, text = str(message),parse_mode=telegram.ParseMode.MARKDOWN )

            except Unauthorized as e:
                logger.warning("Exception: Unauthorized {}".format(e))

                self.database.deleteNodesForUser(chatId)
                self.database.deleteUser(chatId)

                err = False

            except TimedOut as e:
                logger.warning("Exception: TimedOut {}".format(e))
            except NetworkError as e:
                logger.warning("Exception: NetworkError {}".format(e))
            except ChatMigrated as e:
                logger.warning("Exception: ChatMigrated from {} to {}".format(chatId, e.new_chat_id))
            except BadRequest as e:
                logger.warning("Exception: BadRequest {}".format(e))
            except RetryAfter as e:
                logger.warning("Exception: RetryAfter {}".format(e))

                queue.lock(e.retry_after)
                warnMessage = messages.rateLimitError(self.messenger, util.secondsToText(int(e.retry_after)))
                self.bot.sendMessage(chat_id=chatId, text = warnMessage ,parse_mode=telegram.ParseMode.MARKDOWN )

            except TelegramError as e:
                logger.warning("Exception: TelegramError {}".format(e))
            else:
                logger.debug("sendMessage - OK!")
                err = False

            if err:
                queue.error()
            else:
                queue.pop()

            self.leftover -= 1

        self.sem.release()

        self.startTimer()
示例#22
0
def handleNodeUpdate(bot, update, node):

    # If there is a new block available form the nodelist
    if update['lastPaid']:

        # Update the source of the reward in the rewardlist to be able to track the
        # number of missing blocks in the nodelist
        # If the reward was not available yet it gets added
        reward = MNReward(block=node.lastPaidBlock,
                          txtime=node.lastPaidTime,
                          payee=node.payee,
                          source=1,
                          meta=2)

        dbReward = bot.rewardList.getReward(node.lastPaidBlock)

        if not dbReward:

            reward = MNReward(block=node.lastPaidBlock,
                              payee=node.payee,
                              txtime=node.lastPaidTime,
                              source=1)

            bot.rewardList.addReward(reward)
        else:
            bot.rewardList.updateSource(reward)

    # Create notification response messages!

    responses = {}

    for userNode in bot.database.getNodes(node.collateral):

        dbUser = bot.database.getUser(userNode['user_id'])

        if dbUser:

            if not dbUser['id'] in responses:
                responses[dbUser['id']] = []

            nodeName = userNode['name']

            if update['status'] and dbUser['status_n']:

                response = messages.statusNotification(bot.messenger, nodeName,
                                                       node.status)
                responses[dbUser['id']].append(response)

            if update['timeout'] and dbUser['timeout_n']:

                if node.timeout != -1:
                    timeString = util.secondsToText(
                        int(time.time()) - node.lastSeen)
                    response = messages.panicNotification(
                        bot.messenger, nodeName, timeString)
                else:
                    response = messages.relaxNotification(
                        bot.messenger, nodeName)

                responses[dbUser['id']].append(response)

    return responses
示例#23
0
def history(bot, update):

    response = "<u><b>History<b><u>\n\n"

    userInfo = util.crossMessengerSplit(update)
    userId = userInfo['user'] if 'user' in userInfo else None
    userName = userInfo['name'] if 'name' in userInfo else None

    logger.debug("history - user: {}".format(userId))

    nodesFound = False

    user = bot.database.getUser(userId)
    userNodes = bot.database.getAllNodes(userId)

    if user == None or userNodes == None or len(userNodes) == 0:

        response += messages.nodesRequired(bot.messenger)

    else:

        with bot.nodeList as nodeList:

            collaterals = list(map(lambda x: x['collateral'], userNodes))
            nodes = nodeList.getNodes(collaterals)

            time30Days = time.time() - (2592000)  # now - 30d * 24h * 60m * 60s
            totalInvest = len(nodes) * 10000
            totalProfit = 0
            totalAvgInterval = 0
            totalFirst = 0
            countMultiplePayouts = 0
            totalProfit30Days = 0

            for masternode in nodes:

                userNode = bot.database.getNodes(masternode.collateral,
                                                 user['id'])
                rewards = bot.rewardList.getRewardsForPayee(masternode.payee)

                profit = sum(map(lambda x: x.amount, rewards))
                profit30Days = sum(
                    map(lambda x: x.amount
                        if x.txtime > time30Days else 0, rewards))
                totalProfit30Days += profit30Days

                totalProfit += round(profit, 1)
                avgInterval = 0
                masterPerDay = 0

                first = 0
                last = 0

                if len(rewards) == 1:

                    first = rewards[0].txtime

                if len(rewards) > 1:
                    countMultiplePayouts += 1

                    payoutTimes = list(map(lambda x: x.txtime, rewards))

                    first = min(payoutTimes)
                    last = max(payoutTimes)

                if not totalFirst or first and totalFirst > first:
                    totalFirst = first

                if last:

                    avgInterval = (last - first) / len(rewards)
                    totalAvgInterval += avgInterval

                    masterPerDay = round(
                        profit / ((time.time() - first) / 86400), 1)

                response += "<u><b>Node - " + userNode['name'] + "<b><u>\n\n"
                response += "<b>Payouts<b> {}\n".format(len(rewards))
                response += "<b>Profit<b> {:,} CRU\n".format(round(profit, 1))
                response += "<b>Profit (30 days)<b> {:,} CRU\n".format(
                    round(profit30Days, 1))

                if avgInterval:
                    response += "\n<b>Payout interval<b> " + util.secondsToText(
                        avgInterval)

                if masterPerDay:
                    response += "\n<b>CRU/day<b> {:,} CRU".format(masterPerDay)

                response += "\n<b>ROI (CRU)<b> {}%".format(
                    round((profit / 10000.0) * 100.0, 1))

                response += "\n\n"

            response += "<u><b>Total stats<b><u>\n\n"

            if totalFirst:
                response += "<b>First payout<b> {} ago\n\n".format(
                    util.secondsToText(time.time() - totalFirst))

            response += "<b>Profit (30 days)<b> {:,} CRU\n".format(
                round(totalProfit30Days, 1))
            response += "<b>CRU/day (30 days)<b> {:,} CRU\n\n".format(
                round(totalProfit30Days / 30, 1))

            if totalAvgInterval:
                totalAvgInterval = totalAvgInterval / countMultiplePayouts
                response += "<b>Total payout interval<b> {}\n".format(
                    util.secondsToText(totalAvgInterval))

            response += "<b>Total CRU/day<b> {:,} CRU\n\n".format(
                round(totalProfit / ((time.time() - totalFirst) / 86400), 1))
            response += "<b>Total profit<b> {:,} CRU\n".format(
                round(totalProfit, 1))
            response += "<b>Total ROI (CRU)<b> {}%\n\n".format(
                round((totalProfit / totalInvest) * 100, 1))

    return messages.markdown(response, bot.messenger)