diff --git a/tests/TestHarness/Node.py b/tests/TestHarness/Node.py index 8e349f9fcc..0ccedfda0c 100644 --- a/tests/TestHarness/Node.py +++ b/tests/TestHarness/Node.py @@ -506,10 +506,33 @@ def unstartedFile(nodeId): Utils.errorExit("Cannot find unstarted node since %s file does not exist" % startFile) return startFile - def launchUnstarted(self): + def launchUnstarted(self, waitForAlive=True): Utils.Print("launchUnstarted cmd: %s" % (self.cmd)) self.popenProc = self.launchCmd(self.cmd, self.data_dir, self.launch_time) + if not waitForAlive: + return + + def isNodeAlive(): + """wait for node to be responsive.""" + try: + return True if self.checkPulse() else False + except (TypeError) as _: + pass + return False + + isAlive=Utils.waitForBool(isNodeAlive) + + if isAlive: + if Utils.Debug: Utils.Print("Node launch was successful.") + else: + Utils.Print("ERROR: Node launch Failed.") + # Ensure the node process is really killed + if self.popenProc: + self.popenProc.send_signal(signal.SIGTERM) + self.popenProc.wait() + self.pid=None + def launchCmd(self, cmd: List[str], data_dir: Path, launch_time: str): dd = data_dir out = dd / 'stdout.txt' diff --git a/tests/gelf_test.py b/tests/gelf_test.py index d0ed7f1888..facc59e748 100755 --- a/tests/gelf_test.py +++ b/tests/gelf_test.py @@ -86,6 +86,7 @@ def gelfServer(stop): data_dir = Path(Utils.getNodeDataDir(node_id)) config_dir = Path(Utils.getNodeConfigDir(node_id)) +# It is good to have at least one integration test that does not use eosio::chain_api_plugin or eosio::http_plugin start_nodeos_cmd = shlex.split(f"{Utils.EosServerPath} -e -p eosio --data-dir={data_dir} --config-dir={config_dir}") if os.path.exists(data_dir): shutil.rmtree(data_dir) @@ -110,7 +111,8 @@ def cleanup(): t1.start() - nodeos.launchUnstarted() + # waitForAlive=False since isNodeAlive depends on get_info of chain_api_plugin + nodeos.launchUnstarted(waitForAlive=False) time.sleep(nodeos_run_time_in_sec) finally: cleanup() diff --git a/tests/p2p_sync_throttle_test.py b/tests/p2p_sync_throttle_test.py index 41430a67b9..18f5f33791 100755 --- a/tests/p2p_sync_throttle_test.py +++ b/tests/p2p_sync_throttle_test.py @@ -32,7 +32,7 @@ delay=args.d debug=args.v prod_count = 2 -total_nodes=4 +total_nodes=5 activateIF=args.activate_if dumpErrorDetails=args.dump_error_details @@ -42,13 +42,6 @@ cluster=Cluster(unshared=args.unshared, keepRunning=args.leave_running, keepLogs=args.keep_logs) walletMgr=WalletMgr(True) -def extractPrometheusMetric(connID: str, metric: str, text: str): - searchStr = f'nodeos_p2p_{metric}{{connid="{connID}"}} ' - begin = text.find(searchStr) + len(searchStr) - return int(text[begin:text.find('\n', begin)]) - -prometheusHostPortPattern = re.compile(r'^nodeos_p2p_port.connid="([a-f0-9]*)". ([0-9]*)', re.MULTILINE) - try: TestHelper.printSystemInfo("BEGIN") @@ -57,12 +50,14 @@ def extractPrometheusMetric(connID: str, metric: str, text: str): Print(f'producing nodes: {pnodes}, delay between nodes launch: {delay} second{"s" if delay != 1 else ""}') Print("Stand up cluster") - extraNodeosArgs = '--plugin eosio::prometheus_plugin --connection-cleanup-period 3' - # Custom topology is a line of singly connected nodes from the highest node number in sequence to lowest, - # the reverse of the usual TestHarness line topology. - if cluster.launch(pnodes=pnodes, unstartedNodes=2, totalNodes=total_nodes, prodCount=prod_count, - topo='./tests/p2p_sync_throttle_test_shape.json', delay=delay, activateIF=activateIF, - extraNodeosArgs=extraNodeosArgs) is False: + # Custom topology: + # prodNode <-> nonProdNode + # <-> throttlingNode <-> throttledNode + # <-> unThrottledNode + # + # Compare the sync time of throttledNode and unThrottledNode + if cluster.launch(pnodes=pnodes, unstartedNodes=3, totalNodes=total_nodes, prodCount=prod_count, + topo='./tests/p2p_sync_throttle_test_shape.json', delay=delay, activateIF=activateIF) is False: errorExit("Failed to stand up eos cluster.") prodNode = cluster.getNode(0) @@ -109,128 +104,43 @@ def extractPrometheusMetric(connID: str, metric: str, text: str): throttleListenAddr = throttlingNode.cmd[i+1] # Using 40 Kilobytes per second to allow syncing of ~250 transaction blocks at ~175 bytes per transaction # (250*175=43750 per block or 87500 per second) - # resulting from the trx generators in a reasonable amount of time, while still being able to capture - # throttling state within the Prometheus update window (3 seconds in this test). + # resulting from the trx generators in a reasonable amount of time throttlingNode.cmd[i+1] = throttlingNode.cmd[i+1] + ':40KB/s' throttleListenIP, throttleListenPort = throttleListenAddr.split(':') throttlingNode.cmd.append('--p2p-listen-endpoint') throttlingNode.cmd.append(f'{throttleListenIP}:{int(throttleListenPort)+100}:1TB/s') cluster.biosNode.kill(signal.SIGTERM) + + Print("Launch throttling node") + cluster.launchUnstarted(1) + + assert throttlingNode.verifyAlive(), "throttling node did not launch" + + # Throttling node was offline during block generation and once online receives blocks as fast as possible + assert throttlingNode.waitForBlock(endLargeBlocksHeadBlock), f'wait for block {endLargeBlocksHeadBlock} on throttled node timed out' + + Print("Launch throttled and un-throttled nodes") clusterStart = time.time() cluster.launchUnstarted(2) - errorLimit = 40 # Approximately 20 retries required throttledNode = cluster.getNode(3) - throttledNodeConnId = None - throttlingNodeConnId = None - while errorLimit > 0: - try: - response = throttlingNode.processUrllibRequest('prometheus', 'metrics', returnType=ReturnType.raw, printReturnLimit=16).decode() - except urllib.error.URLError: - # catch ConnectionRefusedEror waiting for node to finish startup and respond - errorLimit -= 1 - time.sleep(0.5) - continue - else: - if len(response) < 100: - # tolerate HTTPError as well (method returns only the exception code) - errorLimit -= 1 - time.sleep(0.5) - continue - connPorts = prometheusHostPortPattern.findall(response) - Print(connPorts) - if len(connPorts) < 3: - # wait for node to be connected - errorLimit -= 1 - time.sleep(0.5) - continue - Print('Throttling Node Start State') - throttlingNodePortMap = {port: id for id, port in connPorts if port != '0' and port != '9877'} - throttlingNodeConnId = next(iter(throttlingNodePortMap.values())) # 9879 - startSyncThrottlingBytesSent = extractPrometheusMetric(throttlingNodeConnId, - 'block_sync_bytes_sent', - response) - startSyncThrottlingState = extractPrometheusMetric(throttlingNodeConnId, - 'block_sync_throttling', - response) - Print(f'Start sync throttling bytes sent: {startSyncThrottlingBytesSent}') - Print(f'Start sync throttling node throttling: {"True" if startSyncThrottlingState else "False"}') - if time.time() > clusterStart + 30: errorExit('Timed out') - break - else: - errorExit('Exceeded error retry limit waiting for throttling node') - - errorLimit = 40 # Few if any retries required but for consistency... - while errorLimit > 0: - try: - response = throttledNode.processUrllibRequest('prometheus', 'metrics', returnType=ReturnType.raw, printReturnLimit=16).decode() - except urllib.error.URLError: - # catch ConnectionRefusedError waiting for node to finish startup and respond - errorLimit -= 1 - time.sleep(0.5) - continue - else: - if len(response) < 100: - # tolerate HTTPError as well (method returns only the exception code) - errorLimit -= 1 - time.sleep(0.5) - continue - connPorts = prometheusHostPortPattern.findall(response) - Print(connPorts) - if len(connPorts) < 2: - # wait for sending node to be connected - errorLimit -= 1 - time.sleep(0.5) - continue - Print('Throttled Node Start State') - throttledNodePortMap = {port: id for id, port in connPorts if port != '0'} - throttledNodeConnId = next(iter(throttledNodePortMap.values())) # 9878 - Print(throttledNodeConnId) - startSyncThrottledBytesReceived = extractPrometheusMetric(throttledNodeConnId, - 'block_sync_bytes_received', - response) - Print(f'Start sync throttled bytes received: {startSyncThrottledBytesReceived}') - break - else: - errorExit('Exceeded error retry limit waiting for throttled node') - - # Throttling node was offline during block generation and once online receives blocks as fast as possible while - # transmitting blocks to the next node in line at the above throttle setting. - assert throttlingNode.waitForBlock(endLargeBlocksHeadBlock), f'wait for block {endLargeBlocksHeadBlock} on throttled node timed out' - endThrottlingSync = time.time() - response = throttlingNode.processUrllibRequest('prometheus', 'metrics', exitOnError=True, returnType=ReturnType.raw, printReturnLimit=16).decode() - Print('Throttling Node End State') - endSyncThrottlingBytesSent = extractPrometheusMetric(throttlingNodeConnId, - 'block_sync_bytes_sent', - response) - Print(f'End sync throttling bytes sent: {endSyncThrottlingBytesSent}') - assert throttledNode.waitForBlock(beginLargeBlocksHeadBlock, timeout=120), f'Wait for begin block {beginLargeBlocksHeadBlock} on throttled sync node timed out' - # Throttled node is connecting to a listen port with a block sync throttle applied so it will receive - # blocks more slowly during syncing than an unthrottled node. - wasThrottled = False - while time.time() < endThrottlingSync + 30: - response = throttlingNode.processUrllibRequest('prometheus', 'metrics', exitOnError=True, - returnType=ReturnType.raw, printReturnLimit=16).decode() - throttledState = extractPrometheusMetric(throttlingNodeConnId, - 'block_sync_throttling', - response) - if throttledState: - wasThrottled = True - break - assert throttledNode.waitForBlock(endLargeBlocksHeadBlock, timeout=120), f'Wait for block {endLargeBlocksHeadBlock} on sync node timed out' + unThrottledNode = cluster.getNode(4) + assert throttledNode.verifyAlive(), "throttled node did not launch" + assert unThrottledNode.verifyAlive(), "un-throttled node did not launch" + + assert unThrottledNode.waitForBlock(endLargeBlocksHeadBlock), f'wait for block {endLargeBlocksHeadBlock} on un-throttled node timed out' + endUnThrottledSync = time.time() + + assert throttledNode.waitForBlock(endLargeBlocksHeadBlock, timeout=120), f'Wait for block {endLargeBlocksHeadBlock} on throttled node timed out' endThrottledSync = time.time() - response = throttledNode.processUrllibRequest('prometheus', 'metrics', exitOnError=True, returnType=ReturnType.raw, printReturnLimit=16).decode() - Print('Throttled Node End State') - endSyncThrottledBytesReceived = extractPrometheusMetric(throttledNodeConnId, - 'block_sync_bytes_received', - response) - Print(f'End sync throttled bytes received: {endSyncThrottledBytesReceived}') - throttlingElapsed = endThrottlingSync - clusterStart + throttledElapsed = endThrottledSync - clusterStart - Print(f'Unthrottled sync time: {throttlingElapsed} seconds') + unThrottledElapsed = endUnThrottledSync - clusterStart + Print(f'Un-throttled sync time: {unThrottledElapsed} seconds') Print(f'Throttled sync time: {throttledElapsed} seconds') - assert wasThrottled, 'Throttling node never reported throttling its transmission rate' + + assert throttledElapsed > 2 * unThrottledElapsed, f'Throttled node did not sync slower {throttledElapsed} <= {2 * unThrottledElapsed}' testSuccessful=True finally: diff --git a/tests/p2p_sync_throttle_test_shape.json b/tests/p2p_sync_throttle_test_shape.json index 497a66f281..4212304331 100644 --- a/tests/p2p_sync_throttle_test_shape.json +++ b/tests/p2p_sync_throttle_test_shape.json @@ -2,105 +2,105 @@ "name": "testnet_", "nodes": { "bios": { - "index": -100, - "name": "bios", + "index": -100, + "name": "bios", "keys": [ { - "pubkey": "EOS6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV", + "pubkey": "EOS6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV", "privkey": "5KQwrPbwdL6PhXujxW37FSSQZ1JiwsST4cqQzDeyXtP79zkvFD3", "blspubkey":"PUB_BLS_sGOyYNtpmmjfsNbQaiGJrPxeSg9sdx0nRtfhI_KnWoACXLL53FIf1HjpcN8wX0cYQyOE60NLSI9iPY8mIlT4GkiFMT3ez7j2IbBBzR0D1MthC0B_fYlgYWwjcbqCOowSaH48KA", "blsprivkey":"PVT_BLS_QgHHJ5vprZcjG7P0xWoIdX4yKPQXoG4k3e28TpLIQicB7GL_", "blspop":"SIG_BLS_HDzwmlF7wSJGetlSfhIGKVtjiMTeYoM4oCbNoHi1tyh0_KnZCsdLUzplexSXD80P0NAkCjlA6YFt2M5_JsZkRTqn2faFSnH6zwKIK9yr2cV3a14W4WcIC90mTP2D-HEPOBjM2gTmWCA0gYfPdV3tB3I0matrYh5R0I1FG0V6p_RVKacXMgV_M3lNUokRI84MPZlc8OVbJ0RbjoBnYylVeYtR31vSJvvk6RvykIjTktZOA0s32-TR5EcxuaFSsVQU7nSQxA" } - ], + ], "peers": [ "testnet_00" - ], + ], "producers": [ "eosio" - ], - "dont_start": false, - "p2p_port": 9776, - "http_port": 8788, - "host_name": "localhost", - "public_name": "localhost", - "listen_addr": "0.0.0.0", + ], + "dont_start": false, + "p2p_port": 9776, + "http_port": 8788, + "host_name": "localhost", + "public_name": "localhost", + "listen_addr": "0.0.0.0", "_dot_label": "localhost:9776\nbios\nprod=eosio" - }, + }, "testnet_00": { - "index": 0, - "name": "testnet_00", + "index": 0, + "name": "testnet_00", "keys": [ { - "pubkey": "EOS7D6jfN6bbJD9cYheyhnBT4bmUWc3Qf4Yphf5GBeAAy58okcwHU", + "pubkey": "EOS7D6jfN6bbJD9cYheyhnBT4bmUWc3Qf4Yphf5GBeAAy58okcwHU", "privkey": "5KkmnyunnpCQzgFoLMEtU3j7BRBa5aWmsBNru49ke7LdnZKFhmt", "blspubkey":"PUB_BLS_X6Wzge0CMkDLu0svywBWGBdIuMfol_hAG7zeukAddsbQsArgcuZ6tz3LLoLRurUMhz6ZpOHdYCPU0Rg8Fo8n4UDsT6pcHSmwWMKWIhyS-Ms0O_dYCRQ2Q5HLxBGMxyIWaltxlw", "blsprivkey":"PVT_BLS_TvIkGjiwy3b5k9yc6YnwHPQp1n_9x8yP4mZQl5Ke1yvp2_vv", "blspop":"SIG_BLS_Zzi_eRG51GhBgAFhnG048Pa3OjlenLwKtO03CBkZxQB4sdhyYWmqrJDdjpgPwvcPwbRK1jIlaUG9mJVPjJHrmocC-br8_t1EqLAHN3lyuyJ7UZWkzj2E339zNJ8aE28NmF4rmZ0UV3sUP54qZw9k75G7y0toL8djkMkPNzbz9OD0vZQDjQ-PVWQg11t-eP4MbFt8uONuk2NpEBEbT8JXPvnzh1e1-WBxId0Mra5-Pa1ca3zkrqgHdnpWKCUjBr0Kj8yZPg" } - ], + ], "peers": [], "producers": [ - "defproducera", - "defproducerb", - "defproducerc", - "defproducerd", - "defproducere", - "defproducerf", - "defproducerg", - "defproducerh", - "defproduceri", - "defproducerj", - "defproducerk", - "defproducerl", - "defproducerm", - "defproducern", - "defproducero", - "defproducerp", - "defproducerq", - "defproducerr", - "defproducers", - "defproducert", + "defproducera", + "defproducerb", + "defproducerc", + "defproducerd", + "defproducere", + "defproducerf", + "defproducerg", + "defproducerh", + "defproduceri", + "defproducerj", + "defproducerk", + "defproducerl", + "defproducerm", + "defproducern", + "defproducero", + "defproducerp", + "defproducerq", + "defproducerr", + "defproducers", + "defproducert", "defproduceru" - ], - "dont_start": false, - "p2p_port": 9876, - "http_port": 8888, - "host_name": "localhost", - "public_name": "localhost", - "listen_addr": "0.0.0.0", + ], + "dont_start": false, + "p2p_port": 9876, + "http_port": 8888, + "host_name": "localhost", + "public_name": "localhost", + "listen_addr": "0.0.0.0", "_dot_label": "localhost:9876\ntestnet_00\nprod=defproducera\ndefproducerb\ndefproducerc\ndefproducerd\ndefproducere\ndefproducerf\ndefproducerg\ndefproducerh\ndefproduceri\ndefproducerj\ndefproducerk\ndefproducerl\ndefproducerm\ndefproducern\ndefproducero\ndefproducerp\ndefproducerq\ndefproducerr\ndefproducers\ndefproducert\ndefproduceru" - }, + }, "testnet_01": { - "index": 1, - "name": "testnet_01", + "index": 1, + "name": "testnet_01", "keys": [ { - "pubkey": "EOS5tZqxLB8y9q2yHkgcXU4QFBEV6QKN3NQ54ygaFLWHJbjqYzFhw", + "pubkey": "EOS5tZqxLB8y9q2yHkgcXU4QFBEV6QKN3NQ54ygaFLWHJbjqYzFhw", "privkey": "5KBs4qR7T8shJjCJUeFQXd77iKrok5TCtZiQhWJpCpc1VRxpNAs", "blspubkey":"PUB_BLS_UmHR2Ez-gUJVkptOXXlWBCSu2aPQ3EBk69L7IzXn-pAXiWv5gP6fgQv5Js4n3VcJL6TK1M9rB9wAPhnr7b6xdKg2_zWD62qUoal9GYmBS5doxlCdKDY8ZFj6fbGS02oY_-ItrQ", "blsprivkey":"PVT_BLS_IRjJHkfSSII-mDq7iVOsznvka_sRMsmxJSJwXQyr5mqmERAV", "blspop":"SIG_BLS_wzTA_EfQTVoWRO4HZqoyDcQGCnlvHCkqoZXVSRbwSf7az4U4nbveWgCMRCgQZsgEJbPt6-NslwwRXJDLnFN0Hnm8F5qhmsGlWMP9tH7syPibNvldJ0RUFDH7azSZulcJ2uMxQAobCB-21c3PiUQc8JbuJFbUp9klAnXIJP60P-PT6ZUNmhNjLqHl2IlMsq8ZdFPvHVF3Z8HpfhJVKedI4yTvzWAIIOW2uSHkOmKbLP_QYc2YLRHUWV56mM-hsRwP4-hWVA" } - ], + ], "peers": [ "testnet_00" - ], - "producers": [], - "dont_start": false, - "p2p_port": 9877, - "http_port": 8889, - "host_name": "localhost", - "public_name": "localhost", - "listen_addr": "0.0.0.0", + ], + "producers": [], + "dont_start": false, + "p2p_port": 9877, + "http_port": 8889, + "host_name": "localhost", + "public_name": "localhost", + "listen_addr": "0.0.0.0", "_dot_label": "localhost:9877\ntestnet_01\nprod=" - }, + }, "testnet_02": { - "index": 2, - "name": "testnet_02", + "index": 2, + "name": "testnet_02", "keys": [ { - "pubkey": "EOS5FBPf5EN9bYEqmsKfPx9bxyUZ9grDiE24zqLFXtPa6UpVzMjE7", + "pubkey": "EOS5FBPf5EN9bYEqmsKfPx9bxyUZ9grDiE24zqLFXtPa6UpVzMjE7", "privkey": "5HtVDiAsD24seDm5sdswTcdZpx672XbBW9gBkyrzbsj2j9Y9JeC", "blspubkey":"PUB_BLS_JzblSr2sf_UhxQjGxOtHbRCBkHgSB1RG4xUbKKl-fKtUjx6hyOHajnVQT4IvBF4PutlX7JTC14IqIjADlP-3_G2MXRhBlkB57r2u59OCwRQQEDqmVSADf6CoT8zFUXcSgHFw7w", "blsprivkey":"PVT_BLS_QRxLAVbe2n7RaPWx2wHbur8erqUlAs-V_wXasGhjEA78KlBq", @@ -109,39 +109,63 @@ ], "peers": [ "testnet_01" - ], - "producers": [], - "dont_start": true, - "p2p_port": 9878, - "http_port": 8890, - "host_name": "localhost", - "public_name": "localhost", - "listen_addr": "0.0.0.0", + ], + "producers": [], + "dont_start": true, + "p2p_port": 9878, + "http_port": 8890, + "host_name": "localhost", + "public_name": "localhost", + "listen_addr": "0.0.0.0", "_dot_label": "localhost:9878\ntestnet_02\nprod=" - }, + }, "testnet_03": { - "index": 3, - "name": "testnet_03", + "index": 3, + "name": "testnet_03", "keys": [ { - "pubkey": "EOS8XH2gKxsef9zxmMHm4vaSvxQUhg7W4GC3nK2KSRxyYrNG5gZFS", + "pubkey": "EOS8XH2gKxsef9zxmMHm4vaSvxQUhg7W4GC3nK2KSRxyYrNG5gZFS", "privkey": "5JcoRRhDcgm51dkBrRTmErceTqrYhrq22UnmUjTZToMpH91B9N1", "blspubkey":"PUB_BLS_rYRa_-bT7uLOSAfPIBy6NlXFB0YxwROeSuqHzw6s-1cuK_-GJUKqp20ktyAnsO4ZuHdx3BEPDaLronpnL22MXKWM7bvZnkCfbGCD6OzizQqxXkM9N5z5R-OUA4Ime6cF5YTSFg", "blsprivkey":"PVT_BLS_GQjR0E8Hu8KrsTCvLKnlOCIwQijAj2-5KDizQwF-bAY6pise", "blspop":"SIG_BLS_syFMuifUnX2zQQKr0cuHYzQQjsuPrNG75_z6y8fOyYg_twqMICZ0kT7ObbwIOUsLfXx9PVb4-QLEgUYGSRg1NSfeHGjIGkhea82wa3ayfI8elUEU1MStKbeKpys7xUAQz1PEgwcz5dClq3HyLQmMAjpoL74N_Znf0KiNEVZMte-DLF7x_6sAfp_834LthyYHjZYTmdG7belyzlYHKJb6upnZy9nR_zoKpx9jeTd3tzVhoTCuAN6aFw68D_ItY5cWiY2dhA" } - ], + ], "peers": [ "testnet_02" ], "producers": [], - "dont_start": true, - "p2p_port": 9879, - "http_port": 8891, - "host_name": "localhost", - "public_name": "localhost", - "listen_addr": "0.0.0.0", + "dont_start": true, + "p2p_port": 9879, + "http_port": 8891, + "host_name": "localhost", + "public_name": "localhost", + "listen_addr": "0.0.0.0", "_dot_label": "localhost:9879\ntestnet_03\nprod=" + }, + "testnet_04": { + "index": 4, + "name": "testnet_04", + "keys": [ + { + "pubkey":"EOS7K5pQCk22ojetRdyumrqp6nJX6eiQiTWWcGkZAMGhoBxgcsxhK", + "privkey":"5K3h9XiAmrx9EuqD8CRxHgQwEVDaWpqrhrnpdvwHtVzwJFMhNmE", + "blspubkey":"PUB_BLS_kGOCEX1MM5Xl928OOvGLyNo3_GpV8av1HnoaCEGOD8bAu3MDvazu0gCZGA1G7msTh1ZTPMEMVdXMuRVS0tv_9bW9Ohz9XvgtjgbPpxxc_NaeENkGg4uDBOro0Rk8DCEW4ToLKA", + "blsprivkey":"PVT_BLS_EnQXObGKvYqfubrKjxpCqNkHeLlkQg7LERjDGm1RKjgyFZnk", + "blspop":"SIG_BLS_bXrzPVc-ahxOCWrcl-iWIMuS8ego54iz7vi38A8h_ViqtxklH9O3A2z0eiw5j40M08ejiTm7JbCY_GOwulv1oXb9SaLYQkCTZjzCVssDkghLBRTVCZW2oJmU9WbZXikNw6nkygTs5sUTtCda2a_M5jqY_Rw92_NWmbolgBNkFvMcAgSHexdETA-b7QgJX_oYBWkyP0Pt8LzO6bJueZSjH8wZ8VuPc9o8taY85mt_qgdOTbXVBG2m5ud0eAUps2UHAHt-Ig" + } + ], + "peers": [ + "testnet_01" + ], + "producers": [], + "dont_start": true, + "p2p_port": 9880, + "http_port": 8892, + "host_name": "localhost", + "public_name": "localhost", + "listen_addr": "0.0.0.0", + "_dot_label": "localhost:9880\ntestnet_04\nprod=" } } }