예제 #1
0
 def isMongodDbRunning(self):
     cmd = "%s %s" % (Utils.MongoPath, self.mongoEndpointArgs)
     subcommand = "db.version()"
     if Utils.Debug: Utils.Print("echo %s | %s" % (subcommand, cmd))
     ret, outs, errs = Node.stdinAndCheckOutput(cmd.split(), subcommand)
     if ret is not 0:
         Utils.Print("ERROR: Failed to check database version: %s" %
                     (Node.byteArrToStr(errs)))
         return False
     if Utils.Debug: Utils.Print("MongoDb response: %s" % (outs))
     return True
예제 #2
0
    def cleanup(self):
        for f in glob.glob("var/lib/node_*"):
            shutil.rmtree(f)
        for f in glob.glob("etc/eosio/node_*"):
            shutil.rmtree(f)

        if self.enableMongo:
            cmd = "%s %s" % (Utils.MongoPath, self.mongoEndpointArgs)
            subcommand = "db.dropDatabase()"
            if Utils.Debug: Utils.Print("echo %s | %s" % (subcommand, cmd))
            ret, _, errs = Node.stdinAndCheckOutput(cmd.split(), subcommand)
            if ret is not 0:
                Utils.Print("ERROR: Failed to drop database: %s" %
                            (Node.byteArrToStr(errs)))
예제 #3
0
    def discoverLocalNodes(self, totalNodes, timeout=0):
        nodes = []

        pgrepOpts = "-fl"
        # pylint: disable=deprecated-method
        if platform.linux_distribution()[0] in [
                "Ubuntu", "LinuxMint", "Fedora", "CentOS Linux", "arch"
        ]:
            pgrepOpts = "-a"

        cmd = "pgrep %s %s" % (pgrepOpts, Utils.EosServerName)

        def myFunc():
            psOut = None
            try:
                if Utils.Debug: Utils.Print("cmd: %s" % (cmd))
                psOut = subprocess.check_output(cmd.split()).decode("utf-8")
                return psOut
            except subprocess.CalledProcessError as _:
                pass
            return None

        psOut = Utils.waitForObj(myFunc, timeout)
        if psOut is None:
            Utils.Print("ERROR: No nodes discovered.")
            return nodes

        if Utils.Debug: Utils.Print("pgrep output: \"%s\"" % psOut)
        for i in range(0, totalNodes):
            pattern = r"[\n]?(\d+) (.* --data-dir var/lib/node_%02d)" % (i)
            m = re.search(pattern, psOut, re.MULTILINE)
            if m is None:
                Utils.Print("ERROR: Failed to find %s pid. Pattern %s" %
                            (Utils.EosServerName, pattern))
                break
            instance = Node(self.host,
                            self.port + i,
                            pid=int(m.group(1)),
                            cmd=m.group(2),
                            enableMongo=self.enableMongo,
                            mongoHost=self.mongoHost,
                            mongoPort=self.mongoPort,
                            mongoDb=self.mongoDb)
            instance.setWalletEndpointArgs(self.walletEndpointArgs)
            if Utils.Debug: Utils.Print("Node>", instance)
            nodes.append(instance)

        return nodes
예제 #4
0
    def createAccounts(self,
                       creator,
                       waitForTransBlock=True,
                       stakedDeposit=1000):
        if self.accounts is None:
            return True

        transId = None
        for account in self.accounts:
            if Utils.Debug: Utils.Print("Create account %s." % (account.name))
            trans = self.createAccountAndVerify(account, creator,
                                                stakedDeposit)
            if trans is None:
                Utils.Print("ERROR: Failed to create account %s." %
                            (account.name))
                return False
            if Utils.Debug: Utils.Print("Account %s created." % (account.name))
            transId = Node.getTransId(trans)

        if waitForTransBlock and transId is not None:
            node = self.nodes[0]
            if Utils.Debug:
                Utils.Print("Wait for transaction id %s on server port %d." %
                            (transId, node.port))
            if node.waitForTransIdOnNode(transId) is False:
                Utils.Print(
                    "ERROR: Failed waiting for transaction id %s on server port %d."
                    % (transId, node.port))
                return False

        return True
예제 #5
0
    def initializeNodesFromJson(self, nodesJsonStr):
        nodesObj = json.loads(nodesJsonStr)
        if nodesObj is None:
            Utils.Print("ERROR: Invalid Json string.")
            return False

        if "keys" in nodesObj:
            keysMap = nodesObj["keys"]

            if "defproduceraPrivateKey" in keysMap:
                defproduceraPrivateKey = keysMap["defproduceraPrivateKey"]
                self.defproduceraAccount.ownerPrivateKey = defproduceraPrivateKey

            if "defproducerbPrivateKey" in keysMap:
                defproducerbPrivateKey = keysMap["defproducerbPrivateKey"]
                self.defproducerbAccount.ownerPrivateKey = defproducerbPrivateKey

        nArr = nodesObj["nodes"]
        nodes = []
        for n in nArr:
            port = n["port"]
            host = n["host"]
            node = Node(host, port)
            node.setWalletEndpointArgs(self.walletEndpointArgs)
            if Utils.Debug: Utils.Print("Node:", node)

            node.checkPulse()
            nodes.append(node)

        self.nodes = nodes
        return True
예제 #6
0
    def initializeNodes(self,
                        defproduceraPrvtKey=None,
                        defproducerbPrvtKey=None,
                        onlyBios=False):
        port = Cluster.__BiosPort if onlyBios else self.port
        host = Cluster.__BiosHost if onlyBios else self.host
        node = Node(host,
                    port,
                    enableMongo=self.enableMongo,
                    mongoHost=self.mongoHost,
                    mongoPort=self.mongoPort,
                    mongoDb=self.mongoDb)
        node.setWalletEndpointArgs(self.walletEndpointArgs)
        if Utils.Debug: Utils.Print("Node:", node)

        node.checkPulse()
        self.nodes = [node]

        if defproduceraPrvtKey is not None:
            self.defproduceraAccount.ownerPrivateKey = defproduceraPrvtKey
            self.defproduceraAccount.activePrivateKey = defproduceraPrvtKey

        if defproducerbPrvtKey is not None:
            self.defproducerbAccount.ownerPrivateKey = defproducerbPrvtKey
            self.defproducerbAccount.activePrivateKey = defproducerbPrvtKey

        return True
예제 #7
0
    def bootstrap(totalNodes,
                  prodCount,
                  biosHost,
                  biosPort,
                  dontKill=False,
                  onlyBios=False):
        """Create 'prodCount' init accounts and deposits 10000000000 EOS in each. If prodCount is -1 will initialize all possible producers.
        Ensure nodes are inter-connected prior to this call. One way to validate this will be to check if every node has block 1."""

        Utils.Print("Starting cluster bootstrap.")
        biosNode = Node(biosHost, biosPort)
        if not biosNode.checkPulse():
            Utils.Print("ERROR: Bios node doesn't appear to be running...")
            return False

        producerKeys = Cluster.parseClusterKeys(totalNodes)
        # should have totalNodes node plus bios node
        if producerKeys is None or len(producerKeys) < (totalNodes + 1):
            Utils.Print(
                "ERROR: Failed to parse private keys from cluster config files."
            )
            return False

        walletMgr = WalletMgr(True)
        walletMgr.killall()
        walletMgr.cleanup()

        if not walletMgr.launch():
            Utils.Print("ERROR: Failed to launch bootstrap wallet.")
            return False
        biosNode.setWalletEndpointArgs(walletMgr.walletEndpointArgs)

        try:
            ignWallet = walletMgr.create("ignition")
            if ignWallet is None:
                Utils.Print("ERROR: Failed to create ignition wallet.")
                return False

            eosioName = "eosio"
            eosioKeys = producerKeys[eosioName]
            eosioAccount = Account(eosioName)
            eosioAccount.ownerPrivateKey = eosioKeys["private"]
            eosioAccount.ownerPublicKey = eosioKeys["public"]
            eosioAccount.activePrivateKey = eosioKeys["private"]
            eosioAccount.activePublicKey = eosioKeys["public"]

            if not walletMgr.importKey(eosioAccount, ignWallet):
                Utils.Print(
                    "ERROR: Failed to import %s account keys into ignition wallet."
                    % (eosioName))
                return False

            contract = "eosio.bios"
            contractDir = "contracts/%s" % (contract)
            wastFile = "contracts/%s/%s.wast" % (contract, contract)
            abiFile = "contracts/%s/%s.abi" % (contract, contract)
            Utils.Print("Publish %s contract" % (contract))
            trans = biosNode.publishContract(eosioAccount.name,
                                             contractDir,
                                             wastFile,
                                             abiFile,
                                             waitForTransBlock=True)
            if trans is None:
                Utils.Print("ERROR: Failed to publish contract %s." %
                            (contract))
                return False

            Node.validateTransaction(trans)

            Utils.Print("Creating accounts: %s " %
                        ", ".join(producerKeys.keys()))
            producerKeys.pop(eosioName)
            accounts = []
            for name, keys in producerKeys.items():
                initx = None
                initx = Account(name)
                initx.ownerPrivateKey = keys["private"]
                initx.ownerPublicKey = keys["public"]
                initx.activePrivateKey = keys["private"]
                initx.activePublicKey = keys["public"]
                trans = biosNode.createAccount(initx, eosioAccount, 0)
                if trans is None:
                    Utils.Print("ERROR: Failed to create account %s" % (name))
                    return False
                Node.validateTransaction(trans)
                accounts.append(initx)

            transId = Node.getTransId(trans)
            biosNode.waitForTransIdOnNode(transId)

            Utils.Print("Validating system accounts within bootstrap")
            biosNode.validateAccounts(accounts)

            if not onlyBios:
                if prodCount == -1:
                    setProdsFile = "setprods.json"
                    if Utils.Debug:
                        Utils.Print("Reading in setprods file %s." %
                                    (setProdsFile))
                    with open(setProdsFile, "r") as f:
                        setProdsStr = f.read()

                        Utils.Print("Setting producers.")
                        opts = "--permission eosio@active"
                        myTrans = biosNode.pushMessage("eosio", "setprods",
                                                       setProdsStr, opts)
                        if myTrans is None or not myTrans[0]:
                            Utils.Print("ERROR: Failed to set producers.")
                            return False
                else:
                    counts = dict.fromkeys(
                        range(totalNodes),
                        0)  # initialize node prods count to 0
                    setProdsStr = '{"schedule": ['
                    firstTime = True
                    prodNames = []
                    for name, keys in producerKeys.items():
                        if counts[keys["node"]] >= prodCount:
                            continue
                        if firstTime:
                            firstTime = False
                        else:
                            setProdsStr += ','

                        setProdsStr += ' { "producer_name": "%s", "block_signing_key": "%s" }' % (
                            keys["name"], keys["public"])
                        prodNames.append(keys["name"])
                        counts[keys["node"]] += 1

                    setProdsStr += ' ] }'
                    if Utils.Debug: Utils.Print("setprods: %s" % (setProdsStr))
                    Utils.Print("Setting producers: %s." %
                                (", ".join(prodNames)))
                    opts = "--permission eosio@active"
                    # pylint: disable=redefined-variable-type
                    trans = biosNode.pushMessage("eosio", "setprods",
                                                 setProdsStr, opts)
                    if trans is None or not trans[0]:
                        Utils.Print("ERROR: Failed to set producer %s." %
                                    (keys["name"]))
                        return False

                trans = trans[1]
                transId = Node.getTransId(trans)
                if not biosNode.waitForTransIdOnNode(transId):
                    return False

                # wait for block production handover (essentially a block produced by anyone but eosio).
                lam = lambda: biosNode.getInfo()["head_block_producer"
                                                 ] != "eosio"
                ret = Utils.waitForBool(lam)
                if not ret:
                    Utils.Print("ERROR: Block production handover failed.")
                    return False

            eosioTokenAccount = copy.deepcopy(eosioAccount)
            eosioTokenAccount.name = "eosio.token"
            trans = biosNode.createAccount(eosioTokenAccount, eosioAccount, 0)
            if trans is None:
                Utils.Print("ERROR: Failed to create account %s" %
                            (eosioTokenAccount.name))
                return False

            Node.validateTransaction(trans)
            transId = Node.getTransId(trans)
            biosNode.waitForTransIdOnNode(transId)

            contract = "eosio.token"
            contractDir = "contracts/%s" % (contract)
            wastFile = "contracts/%s/%s.wast" % (contract, contract)
            abiFile = "contracts/%s/%s.abi" % (contract, contract)
            Utils.Print("Publish %s contract" % (contract))
            trans = biosNode.publishContract(eosioTokenAccount.name,
                                             contractDir,
                                             wastFile,
                                             abiFile,
                                             waitForTransBlock=True)
            if trans is None:
                Utils.Print("ERROR: Failed to publish contract %s." %
                            (contract))
                return False

            # Create currency0000, followed by issue currency0000
            contract = eosioTokenAccount.name
            Utils.Print("push create action to %s contract" % (contract))
            action = "create"
            data = "{\"issuer\":\"%s\",\"maximum_supply\":\"1000000000.0000 %s\",\"can_freeze\":\"0\",\"can_recall\":\"0\",\"can_whitelist\":\"0\"}" % (
                eosioTokenAccount.name, CORE_SYMBOL)
            opts = "--permission %s@active" % (contract)
            trans = biosNode.pushMessage(contract, action, data, opts)
            if trans is None or not trans[0]:
                Utils.Print(
                    "ERROR: Failed to push create action to eosio contract.")
                return False

            Node.validateTransaction(trans[1])
            transId = Node.getTransId(trans[1])
            biosNode.waitForTransIdOnNode(transId)

            contract = eosioTokenAccount.name
            Utils.Print("push issue action to %s contract" % (contract))
            action = "issue"
            data = "{\"to\":\"%s\",\"quantity\":\"1000000000.0000 %s\",\"memo\":\"initial issue\"}" % (
                eosioAccount.name, CORE_SYMBOL)
            opts = "--permission %s@active" % (contract)
            trans = biosNode.pushMessage(contract, action, data, opts)
            if trans is None or not trans[0]:
                Utils.Print(
                    "ERROR: Failed to push issue action to eosio contract.")
                return False

            Node.validateTransaction(trans[1])
            Utils.Print(
                "Wait for issue action transaction to become finalized.")
            transId = Node.getTransId(trans[1])
            biosNode.waitForTransIdOnNode(transId)

            expectedAmount = "1000000000.0000 {0}".format(CORE_SYMBOL)
            Utils.Print("Verify eosio issue, Expected: %s" % (expectedAmount))
            actualAmount = biosNode.getAccountEosBalanceStr(eosioAccount.name)
            if expectedAmount != actualAmount:
                Utils.Print(
                    "ERROR: Issue verification failed. Excepted %s, actual: %s"
                    % (expectedAmount, actualAmount))
                return False

            contract = "eosio.system"
            contractDir = "contracts/%s" % (contract)
            wastFile = "contracts/%s/%s.wast" % (contract, contract)
            abiFile = "contracts/%s/%s.abi" % (contract, contract)
            Utils.Print("Publish %s contract" % (contract))
            trans = biosNode.publishContract(eosioAccount.name,
                                             contractDir,
                                             wastFile,
                                             abiFile,
                                             waitForTransBlock=True)
            if trans is None:
                Utils.Print("ERROR: Failed to publish contract %s." %
                            (contract))
                return False

            Node.validateTransaction(trans)

            initialFunds = "1000000.0000 {0}".format(CORE_SYMBOL)
            Utils.Print("Transfer initial fund %s to individual accounts." %
                        (initialFunds))
            trans = None
            contract = eosioTokenAccount.name
            action = "transfer"
            for name, keys in producerKeys.items():
                data = "{\"from\":\"%s\",\"to\":\"%s\",\"quantity\":\"%s\",\"memo\":\"%s\"}" % (
                    eosioAccount.name, name, initialFunds, "init transfer")
                opts = "--permission %s@active" % (eosioAccount.name)
                trans = biosNode.pushMessage(contract, action, data, opts)
                if trans is None or not trans[0]:
                    Utils.Print(
                        "ERROR: Failed to transfer funds from %s to %s." %
                        (eosioTokenAccount.name, name))
                    return False

                Node.validateTransaction(trans[1])

            Utils.Print(
                "Wait for last transfer transaction to become finalized.")
            transId = Node.getTransId(trans[1])
            if not biosNode.waitForTransIdOnNode(transId):
                return False

            Utils.Print("Cluster bootstrap done.")
        finally:
            if not dontKill:
                walletMgr.killall()
                walletMgr.cleanup()

        return True
예제 #8
0
    def spreadFunds(self, amount=1):
        Utils.Print("len(self.accounts): %d" % (len(self.accounts)))
        if len(self.accounts) == 0:
            return True

        count = len(self.accounts)
        transferAmount = (count * amount) + amount
        node = self.nodes[0]
        fromm = self.defproduceraAccount
        to = self.accounts[0]
        Utils.Print(
            "Transfer %d units from account %s to %s on eos server port %d" %
            (transferAmount, fromm.name, to.name, node.port))
        trans = node.transferFunds(fromm, to, transferAmount)
        transId = Node.getTransId(trans)
        if transId is None:
            return False
        self.__lastTrans = transId
        if Utils.Debug:
            Utils.Print("Funds transfered on transaction id %s." % (transId))
        self.accounts[0].balance += transferAmount

        nextEosIdx = -1
        for i in range(0, count):
            account = self.accounts[i]
            nextInstanceFound = False
            for _ in range(0, count):
                # Utils.Print("nextEosIdx: %d, n: %d" % (nextEosIdx, n))
                nextEosIdx = (nextEosIdx + 1) % count
                if not self.nodes[nextEosIdx].killed:
                    # Utils.Print("nextEosIdx: %d" % (nextEosIdx))
                    nextInstanceFound = True
                    break

            if nextInstanceFound is False:
                Utils.Print("ERROR: No active nodes found.")
                return False

            # Utils.Print("nextEosIdx: %d, count: %d" % (nextEosIdx, count))
            node = self.nodes[nextEosIdx]
            if Utils.Debug:
                Utils.Print("Wait for trasaction id %s on node port %d" %
                            (transId, node.port))
            if node.waitForTransIdOnNode(transId) is False:
                Utils.Print(
                    "ERROR: Selected node never received transaction id %s" %
                    (transId))
                return False

            transferAmount -= amount
            fromm = account
            to = self.accounts[i + 1] if i < (count -
                                              1) else self.defproduceraAccount
            Utils.Print(
                "Transfer %d units from account %s to %s on eos server port %d."
                % (transferAmount, fromm.name, to.name, node.port))

            trans = node.transferFunds(fromm, to, transferAmount)
            transId = Node.getTransId(trans)
            if transId is None:
                return False
            self.__lastTrans = transId
            if Utils.Debug:
                Utils.Print("Funds transfered on block num %s." % (transId))

            self.accounts[i].balance -= transferAmount
            if i < (count - 1):
                self.accounts[i + 1].balance += transferAmount

        # As an extra step wait for last transaction on the root node
        node = self.nodes[0]
        if Utils.Debug:
            Utils.Print("Wait for trasaction id %s on node port %d" %
                        (transId, node.port))
        if node.waitForTransIdOnNode(transId) is False:
            Utils.Print(
                "ERROR: Selected node never received transaction id %s" %
                (transId))
            return False

        return True
예제 #9
0
    def launch(self,
               pnodes=1,
               totalNodes=1,
               prodCount=1,
               topo="mesh",
               delay=1,
               onlyBios=False,
               dontKill=False):
        """Launch cluster.
        pnodes: producer nodes count
        totalNodes: producer + non-producer nodes count
        prodCount: producers per prodcuer node count
        topo: cluster topology (as defined by launcher)
        delay: delay between individual nodes launch (as defined by launcher)
          delay 0 exposes a bootstrap bug where producer handover may have a large gap confusing nodes and bringing system to a halt.
        """
        if not self.localCluster:
            Utils.Print("WARNING: Cluster not local, not launching %s." %
                        (Utils.EosServerName))
            return True

        if len(self.nodes) > 0:
            raise RuntimeError("Cluster already running.")

        cmd = "%s -p %s -n %s -s %s -d %s -f" % (Utils.EosLauncherPath, pnodes,
                                                 totalNodes, topo, delay)
        cmdArr = cmd.split()
        if self.staging:
            cmdArr.append("--nogen")

        nodeosArgs = "--max-transaction-time 5000"
        if not self.walletd:
            nodeosArgs += " --plugin eosio::wallet_api_plugin"
        if self.enableMongo:
            nodeosArgs += " --plugin eosio::mongo_db_plugin --resync --mongodb-uri %s" % self.mongoUri

        if nodeosArgs:
            cmdArr.append("--nodeos")
            cmdArr.append(nodeosArgs)

        s = " ".join(cmdArr)
        if Utils.Debug: Utils.Print("cmd: %s" % (s))
        if 0 != subprocess.call(cmdArr):
            Utils.Print("ERROR: Launcher failed to launch.")
            return False

        self.nodes = list(
            range(totalNodes))  # placeholder for cleanup purposes only

        nodes = self.discoverLocalNodes(totalNodes,
                                        timeout=Utils.systemWaitTimeout)
        if nodes is None or totalNodes != len(nodes):
            Utils.Print(
                "ERROR: Unable to validate %s instances, expected: %d, actual: %d"
                % (Utils.EosServerName, totalNodes, len(nodes)))
            return False

        self.nodes = nodes

        if onlyBios:
            biosNode = Node(Cluster.__BiosHost, Cluster.__BiosPort)
            biosNode.setWalletEndpointArgs(self.walletEndpointArgs)
            if not biosNode.checkPulse():
                Utils.Print("ERROR: Bios node doesn't appear to be running...")
                return False

            self.nodes = [biosNode]

        # ensure cluster node are inter-connected by ensuring everyone has block 1
        Utils.Print(
            "Cluster viability smoke test. Validate every cluster node has block 1. "
        )
        if not self.waitOnClusterBlockNumSync(1):
            Utils.Print(
                "ERROR: Cluster doesn't seem to be in sync. Some nodes missing block 1"
            )
            return False

        Utils.Print("Bootstrap cluster.")
        if not Cluster.bootstrap(totalNodes, prodCount, Cluster.__BiosHost,
                                 Cluster.__BiosPort, dontKill, onlyBios):
            Utils.Print("ERROR: Bootstrap failed.")
            return False

        # validate iniX accounts can be retrieved

        producerKeys = Cluster.parseClusterKeys(totalNodes)
        if producerKeys is None:
            Utils.Print("ERROR: Unable to parse cluster info")
            return False

        init1Keys = producerKeys["defproducera"]
        init2Keys = producerKeys["defproducerb"]
        if init1Keys is None or init2Keys is None:
            Utils.Print(
                "ERROR: Failed to parse defproducera or intb private keys from cluster config files."
            )
        self.defproduceraAccount.ownerPrivateKey = init1Keys["private"]
        self.defproduceraAccount.ownerPublicKey = init1Keys["public"]
        self.defproduceraAccount.activePrivateKey = init1Keys["private"]
        self.defproduceraAccount.activePublicKey = init1Keys["public"]
        self.defproducerbAccount.ownerPrivateKey = init2Keys["private"]
        self.defproducerbAccount.ownerPublicKey = init2Keys["public"]
        self.defproducerbAccount.activePrivateKey = init2Keys["private"]
        self.defproducerbAccount.activePublicKey = init2Keys["public"]
        producerKeys.pop("eosio")

        return True