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
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)))
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
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
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
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
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
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
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