From d6def8f8192bbcf71b093c389b58a970b8b973b7 Mon Sep 17 00:00:00 2001 From: The MMGen Project Date: Fri, 14 May 2021 11:26:33 +0000 Subject: [PATCH] test.py: new `xmrwallet` test Testing: $ test/test.py --coin=xmr -e xmrwallet --- mmgen/xmrwallet.py | 7 +- test/test-release.sh | 51 +--- test/test.py | 8 +- test/test_py_d/ts_xmrwallet.py | 523 +++++++++++++++++++++++++++++++++ 4 files changed, 543 insertions(+), 46 deletions(-) create mode 100755 test/test_py_d/ts_xmrwallet.py diff --git a/mmgen/xmrwallet.py b/mmgen/xmrwallet.py index 9f5d8083..f3132ad3 100755 --- a/mmgen/xmrwallet.py +++ b/mmgen/xmrwallet.py @@ -241,6 +241,7 @@ class MoneroWalletOps: wallet_dir = opt.outdir or '.', test_suite = g.test_suite, daemon_addr = uarg.daemon or None, + testnet = g.testnet, ) if uarg.start_wallet_daemon: @@ -275,10 +276,11 @@ class MoneroWalletOps: def get_wallet_fn(self,d): return os.path.join( - opt.outdir or '.','{}-{}-MoneroWallet{}'.format( + opt.outdir or '.','{}-{}-MoneroWallet{}{}'.format( self.kal.al_id.sid, d.idx, - '-α' if g.debug_utf8 else '')) + '.testnet' if g.testnet else '', + '-α' if g.debug_utf8 else '' )) async def process_wallets(self): gmsg('\n{}ing {} wallet{}'.format(self.desc,len(self.addr_data),suf(self.addr_data))) @@ -441,6 +443,7 @@ class MoneroWalletOps: daemon_addr = m[1], proxy = m[2], port_shift = 16, + testnet = g.testnet, ) if g.test_suite: diff --git a/test/test-release.sh b/test/test-release.sh index c81a05a6..b95a47b9 100755 --- a/test/test-release.sh +++ b/test/test-release.sh @@ -34,7 +34,6 @@ mmgen_tool='cmds/mmgen-tool' mmgen_keygen='cmds/mmgen-keygen' python='python3' rounds=100 rounds_min=20 rounds_mid=250 rounds_max=500 -xmr_addrs='3,99,2,22-24,101-104' dfl_tests='misc obj color unit hash ref altref alts xmr eth autosign btc btc_tn btc_rt bch bch_rt ltc ltc_rt tool tool2 gen' extra_tests='autosign_btc autosign_live etc ltc_tn bch_tn' @@ -43,7 +42,7 @@ quick_tests='misc obj color unit hash ref altref alts xmr eth autosign btc btc_r qskip_tests='btc_tn bch bch_rt ltc ltc_rt' PROGNAME=$(basename $0) -while getopts hAbCfFi:I:lOpRtvV OPT +while getopts hAbCfFi:I:lOptvV OPT do case "$OPT" in h) printf " %-16s Test MMGen release\n" "${PROGNAME}:" @@ -60,7 +59,6 @@ do echo " '-l' List the test name symbols" echo " '-O' Use pexpect.spawn rather than popen_spawn for applicable tests" echo " '-p' Pause between tests" - echo " '-R' Re-run fast XMR test with existing tmp data, leave daemon running" echo " '-t' Print the tests without running them" echo " '-v' Run test/test.py with '--exact-output' and other commands with" echo " '--verbose' switch" @@ -74,7 +72,7 @@ do echo " ref - reference file checks" echo " altref - altcoin reference file checks" echo " alts - operations for all supported gen-only altcoins" - echo " xmr - operations for Monero" + echo " xmr - Monero xmrwallet operations" echo " eth - operations for Ethereum" echo " etc - operations for Ethereum Classic" echo " autosign - autosign" @@ -116,8 +114,8 @@ do gentest_py="$python $gentest_py" mmgen_tool="$python $mmgen_tool" mmgen_keygen="$python $mmgen_keygen" ;& - f) FAST=1 rounds=10 rounds_min=3 rounds_mid=25 rounds_max=50 xmr_addrs='3,23' unit_tests_py+=" --fast" ;; - F) FAST=1 rounds=3 rounds_min=1 rounds_mid=3 rounds_max=5 xmr_addrs='3,23' unit_tests_py+=" --fast" ;; + f) FAST=1 rounds=10 rounds_min=3 rounds_mid=25 rounds_max=50 unit_tests_py+=" --fast" ;; + F) FAST=1 rounds=3 rounds_min=1 rounds_mid=3 rounds_max=5 unit_tests_py+=" --fast" ;; i) INSTALL=$OPTARG ;; I) INSTALL=$OPTARG INSTALL_ONLY=1 ;; l) echo -e "Default tests:\n $dfl_tests" @@ -128,7 +126,6 @@ do exit ;; O) test_py+=" --pexpect-spawn" ;; p) PAUSE=1 ;; - R) RERUN_XMR=1 ;; t) LIST_CMDS=1 ;; v) EXACT_OUTPUT=1 test_py+=" --exact-output" ;& V) VERBOSE=1 @@ -332,41 +329,15 @@ create_tmpdir() { mkdir -p $TMPDIR } -if [ "$RERUN_XMR" ]; then - TMPDIR=/tmp/mmgen-test-release* - if [ -e $TMPDIR ]; then HAVE_XMR_DATA=1; else create_tmpdir; fi -else - rm -rf /tmp/mmgen-test-release* - create_tmpdir -fi +rm -rf /tmp/mmgen-test-release* +create_tmpdir -# xmr_addrs: normal: 3,99,2,22-24,101-104 fast: 3,23 -mmgen_tool_xmr="$mmgen_tool -q --yes --outdir $TMPDIR --monero-wallet-rpc-password=passw0rd" -i_xmr='Monero' -s_xmr='Testing key-address file generation and wallet creation and sync operations for Monero' -s_xmr='The monerod (mainnet) daemon must be running for the following tests' +i_xmr='Monero xmrwallet' +s_xmr='Testing xmrwallet operations' t_xmr=" - a cmds/mmgen-walletgen -q -r0 -p1 -Llabel --outdir $TMPDIR -o words - a $mmgen_keygen -q --accept-defaults --use-internal-keccak-module --outdir $TMPDIR --coin=xmr $TMPDIR/*.mmwords $xmr_addrs - a cs1=\$(cmds/mmgen-tool -q --accept-defaults --coin=xmr keyaddrfile_chksum $TMPDIR/*-XMR*.akeys) - x $mmgen_keygen -q --use-old-ed25519 --accept-defaults --outdir $TMPDIR --coin=xmr $TMPDIR/*.mmwords $xmr_addrs - x cs2=\$(cmds/mmgen-tool -q --accept-defaults --coin=xmr keyaddrfile_chksum $TMPDIR/*-XMR*.akeys) - x [ \"\$cs1\" == \"\$cs2\" ] - a test/start-coin-daemons.py xmr - x $mmgen_tool_xmr xmrwallet create $TMPDIR/*-XMR*.akeys wallets=23 - x $mmgen_tool_xmr xmrwallet create $TMPDIR/*-XMR*.akeys wallets=101-104 - x rm $TMPDIR/*-MoneroWallet* - a $mmgen_tool_xmr xmrwallet create $TMPDIR/*-XMR*.akeys - - $mmgen_tool_xmr xmrwallet sync $TMPDIR/*-XMR*.akeys wallets=3,23 - x $mmgen_tool_xmr xmrwallet sync $TMPDIR/*-XMR*.akeys wallets=101-104 - x $mmgen_tool_xmr xmrwallet sync $TMPDIR/*-XMR*.akeys - s test/stop-coin-daemons.py -W xmr + - $test_py --coin=xmr xmrwallet " -f_xmr='Monero tests completed' - -[ "$FAST" ] && t_xmr_skip='x' -[ "$RERUN_XMR" ] && t_xmr_skip='x s' -[ "$RERUN_XMR" -a "$HAVE_XMR_DATA" ] && t_xmr_skip='a x s' +f_xmr='Monero xmrwallet tests completed' i_eth='Ethereum' s_eth='Testing transaction and tracking wallet operations for Ethereum' @@ -593,6 +564,4 @@ run_tests "$tests" TIME=$(($(date +%s)-START)) MS=$(printf %02d:%02d $((TIME/60)) $((TIME%60))) -[ "$RERUN_XMR" ] || rm -rf /tmp/mmgen-test-release-* - [ "$LIST_CMDS" ] || echo -e "${GREEN}All OK. Total elapsed time: $MS$RESET" diff --git a/test/test.py b/test/test.py index eb3b2b59..6693737f 100755 --- a/test/test.py +++ b/test/test.py @@ -356,6 +356,7 @@ cfgs = { # addr_idx_lists (except 31,32,33,34) must contain exactly 8 addresses '22': {}, '23': {}, # 26,27,28 are taken + '29': {}, # xmrwallet '31': {}, '32': {}, '33': {}, @@ -509,6 +510,7 @@ class CmdGroupMgr(object): 'regtest': ('TestSuiteRegtest',{}), # 'chainsplit': ('TestSuiteChainsplit',{}), 'ethdev': ('TestSuiteEthdev',{}), + 'xmrwallet': ('TestSuiteXMRWallet',{}), } cmd_groups_extra = { @@ -1000,17 +1002,17 @@ if opt.pause: set_restore_term_at_exit() set_environ_for_spawned_scripts() -if network_id not in ('eth','etc'): +if network_id not in ('eth','etc','xmr'): start_test_daemons(network_id,remove_datadir=True) try: tr = TestSuiteRunner(data_dir,trash_dir) tr.run_tests(usr_args) tr.warn_skipped() - if network_id not in ('eth','etc'): + if network_id not in ('eth','etc','xmr'): stop_test_daemons(network_id) except KeyboardInterrupt: - if network_id not in ('eth','etc'): + if network_id not in ('eth','etc','xmr'): stop_test_daemons(network_id) tr.warn_skipped() die(1,'\ntest.py exiting at user request') diff --git a/test/test_py_d/ts_xmrwallet.py b/test/test_py_d/ts_xmrwallet.py new file mode 100755 index 00000000..fc006d72 --- /dev/null +++ b/test/test_py_d/ts_xmrwallet.py @@ -0,0 +1,523 @@ +#!/usr/bin/env python3 +# +# mmgen = Multi-Mode GENerator, command-line Bitcoin cold storage solution +# Copyright (C)2013-2021 The MMGen Project +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +""" +ts_xmrwallet.py: xmrwallet tests for the test.py test suite +""" + +import sys,os,atexit,asyncio +from subprocess import run,PIPE + +from mmgen.globalvars import g +from mmgen.opts import opt +from mmgen.obj import MMGenRange,XMRAmt +from mmgen.addr import KeyAddrList,AddrIdxList +from ..include.common import * +from .common import * + +from .ts_base import * + +class TestSuiteXMRWallet(TestSuiteBase): + """ + Monero wallet operations + """ + networks = ('xmr',) + passthru_opts = ('coin',) + tmpdir_nums = [29] + dfl_random_txs = 3 + cmd_group = ( + ('gen_kafiles', 'generating key-address files'), + ('create_wallets', 'creating Monero wallets'), + + ('set_dest_miner', 'opening miner wallet'), + ('mine_blocks', 'mining blocks'), + + ('fund_alice', 'sending funds'), + ('mine_blocks_tx', 'mining blocks'), + + ('sync_wallets', 'syncing all wallets'), + ('sync_wallets_selected', 'syncing selected wallets'), + + ('sweep_to_address_proxy', 'sweeping to new address (via TX relay + proxy)'), + ('mine_blocks', 'mining blocks'), + + ('sweep_to_account', 'sweeping to new account'), + ('mine_blocks', 'mining blocks'), + + ('sweep_to_address_noproxy', 'sweeping to new address (via TX relay)'), + ('mine_blocks', 'mining blocks'), + ) + + def __init__(self,trunner,cfgs,spawn): + TestSuiteBase.__init__(self,trunner,cfgs,spawn) + if trunner == None: + return + + from mmgen.protocol import init_proto + self.proto = init_proto('XMR',network='testnet') + self.datadir_base = os.path.join('test','daemons','xmrtest') + self.tool_args = ['--testnet=1', '--monero-wallet-rpc-password=passw0rd'] + self.init_users() + self.init_daemon_args() + + for v in self.users.values(): + run(['mkdir','-p',v.udir]) + + if not opt.no_daemon_autostart: + self.start_daemons() + self.start_wallet_daemons() + + if not opt.no_daemon_stop: + atexit.register(self.stop_daemons) + atexit.register(self.stop_wallet_daemons) + + self.init_proxy() + self.balance = None + + # init methods + + def init_proxy(self): + + def kill_proxy(): + omsg(f'Killing SSH SOCKS server at localhost:{self.socks_port}') + cmd = [ 'pkill', '-f', ' '.join(a + b2) ] + run(cmd) + + self.use_proxy = False + self.socks_port = 9060 + a = ['ssh','-x','-o','ExitOnForwardFailure=True','-D',f'localhost:{self.socks_port}'] + b1 = ['localhost','true'] + b2 = ['-fN','-E','txrelay-proxy.debug','localhost'] + + cp = run(a+b1,stdout=PIPE,stderr=PIPE) + + if cp.returncode == 0: + if not opt.no_daemon_autostart: + run(a+b2) + omsg(f'SSH SOCKS server started, listening at localhost:{self.socks_port}') + self.use_proxy = True + elif b'already in use' in cp.stderr: + omsg(f'Port {self.socks_port} already in use. Assuming SSH SOCKS server is running') + self.use_proxy = True + else: + m1 = 'Unable to start command {!r}\n'.format(' '.join(a + b)) + m2 = 'Will not test proxied TX relay daemon' + omsg(cp.stderr.decode()) + omsg(yellow(m1+m2)) + + if not opt.no_daemon_stop: + atexit.register(kill_proxy) + + def init_users(self): + from mmgen.daemon import CoinDaemon,MoneroWalletDaemon + from mmgen.rpc import MoneroRPCClient,MoneroRPCClientRaw,MoneroWalletRPCClient + self.users = {} + n = self.tmpdir_nums[0] + ud = namedtuple('user_data',[ + 'sid', + 'mmwords', + 'udir', + 'datadir', + 'kal_range', + 'kafile', + 'walletfile_fs', + 'addrfile_fs', + 'md', + 'md_rpc', + 'md_json_rpc', + 'wd', + 'wd_rpc', + ]) + for user,sid,shift,kal_range in ( # kal_range must be None, a single digit, or a single hyphenated range + ('miner', '98831F3A', 130, '1'), + ('bob', '1378FC64', 140, None), + ('alice', 'FE3C6545', 150, '1-4'), + ): + udir = os.path.join('test',f'tmp{n}',user) + datadir = os.path.join(self.datadir_base,user) + md = CoinDaemon( + proto = self.proto, + test_suite = True, + port_shift = shift, + opts = ['online'], + datadir = datadir + ) + md_rpc = MoneroRPCClientRaw( + host = md.host, + port = md.rpc_port, + user = None, + passwd = None, + test_connection = False, + ) + md_json_rpc = MoneroRPCClient( + host = md.host, + port = md.rpc_port, + user = None, + passwd = None, + test_connection = False, + ) + wd = MoneroWalletDaemon( + user = 'foo', + passwd = 'bar', + wallet_dir = udir, + test_suite = True, + port_shift = shift, + datadir = os.path.join('test','daemons'), + daemon_addr = f'127.0.0.1:{md.rpc_port}', + testnet = True + ) + wd_rpc = MoneroWalletRPCClient( + host = wd.host, + port = wd.rpc_port, + user = wd.user, + passwd = wd.passwd, + test_connection = False, + ) + self.users[user] = ud( + sid = sid, + mmwords = f'test/ref/{sid}.mmwords', + udir = udir, + datadir = datadir, + kal_range = kal_range, + kafile = f'{udir}/{sid}-XMR-M[{kal_range}].testnet.akeys', + walletfile_fs = f'{udir}/{sid}-{{}}-MoneroWallet.testnet', + addrfile_fs = f'{udir}/{sid}-{{}}-MoneroWallet.testnet.address.txt', + md = md, + md_rpc = md_rpc, + md_json_rpc = md_json_rpc, + wd = wd, + wd_rpc = wd_rpc, + ) + + def init_daemon_args(self): + common_args = ['--p2p-bind-ip=127.0.0.1','--fixed-difficulty=1'] # ,'--rpc-ssl-allow-any-cert'] + for u in self.users: + other_ports = [self.users[u2].md.p2p_port for u2 in self.users if u2 != u] + node_args = [f'--add-exclusive-node=127.0.0.1:{p}' for p in other_ports] + self.users[u].md.usr_coind_args = common_args + node_args + + # cmd_group methods + + def gen_kafiles(self): + for user,data in self.users.items(): + if not data.kal_range: + continue + run(['mkdir','-p',data.udir]) + run(f'rm -f {data.kafile}',shell=True) + t = self.spawn( + 'mmgen-keygen', [ + '--testnet=1','-q', '--accept-defaults', '--coin=xmr', + f'--outdir={data.udir}', data.mmwords, data.kal_range + ], + extra_desc = f'({capfirst(user)})' ) + t.read() + t.ok() + t.skip_ok = True + return t + + def create_wallets(self): + for user,data in self.users.items(): + if not data.kal_range: + continue + run('rm -f {}*'.format( data.walletfile_fs.format('*') ),shell=True) + dir_arg = [f'--outdir='+data.udir] + cmd_opts = ['wallets={}'.format(data.kal_range)] + t = self.spawn( + 'mmgen-tool', + self.tool_args + dir_arg + [ 'xmrwallet', 'create', data.kafile ] + cmd_opts, + extra_desc = f'({capfirst(user)})' ) + t.expect('Check key-to-address validity? (y/N): ','n') + for i in MMGenRange(data.kal_range).items: + t.expect('Address: ') + t.read() + t.ok() + t.skip_ok = True + return t + + async def set_dest_miner(self): + self.do_msg() + self.set_dest('miner',1,0,lambda x: x > 20,'unlocked balance > 20') + await self.open_wallet_user('miner',1) + return 'ok' + + async def fund_alice(self): + self.do_msg() + await self.transfer( + 'miner', + 1234567891234, + read_from_file(self.users['alice'].addrfile_fs.format(1)), + ) + self.set_dest('alice',1,0,lambda x: x > 1,'unlocked balance > 1') + return 'ok' + + def sync_wallets_selected(self): + return self.sync_wallets(wallets='1,3-4') + + def sync_wallets(self,wallets=None): + data = self.users['alice'] + dir_arg = [f'--outdir={data.udir}'] + cmd_opts = list_gen( + [f'daemon=localhost:{data.md.rpc_port}'], + [f'wallets={wallets}', wallets], + ) + t = self.spawn( + 'mmgen-tool', + self.tool_args + dir_arg + [ 'xmrwallet', 'sync', data.kafile ] + cmd_opts ) + t.expect('Check key-to-address validity? (y/N): ','n') + wlist = AddrIdxList(wallets) if wallets else MMGenRange(data.kal_range).items + for n,wnum in enumerate(wlist): + t.expect('Syncing wallet {}/{} ({})'.format( + n+1, + len(wlist), + os.path.basename(data.walletfile_fs.format(wnum)), + )) + t.expect('Chain height: ') + t.expect('Wallet height: ') + t.expect('Balance: ') + t.read() + return t + + def _sweep_user(self,user,spec,tx_relay_daemon=None): + data = self.users[user] + dir_arg = [f'--outdir='+data.udir] + cmd_opts = list_gen( + [f'daemon=localhost:{data.md.rpc_port}'], + [f'wallets={spec}'], + [f'tx_relay_daemon={tx_relay_daemon}', tx_relay_daemon] + ) + t = self.spawn( + 'mmgen-tool', + self.tool_args + dir_arg + [ 'xmrwallet', 'sweep', data.kafile ] + cmd_opts, + extra_desc = f'({capfirst(user)})' ) + t.expect('Check key-to-address validity? (y/N): ','n') + t.expect( + 'Create new {} .* \(y/N\): '.format('account' if ',' in spec else 'address'), + 'y', regex=True ) + t.expect('Relay sweep transaction? (y/N): ','y') + t.read() + return t + + def sweep_to_address_proxy(self): + ret = self._sweep_user( + 'alice', + '1:0', + tx_relay_daemon = 'localhost:{}:127.0.0.1:{}'.format( # proxy must be IP, not 'localhost' + self.users['bob'].md.rpc_port, + self.socks_port + ) if self.use_proxy else None + ) + self.set_dest('alice',1,0,lambda x: x > 1,'unlocked balance > 1') + return ret + + def sweep_to_account(self): + ret = self._sweep_user('alice','1:0,2') + self.set_dest('alice',2,1,lambda x: x > 1,'unlocked balance > 1') + return ret + + def sweep_to_address_noproxy(self): + ret = self._sweep_user( + 'alice', + '2:1', + tx_relay_daemon = 'localhost:{}'.format(self.users['bob'].md.rpc_port) + ) + self.set_dest('alice',2,1,lambda x: x > 1,'unlocked balance > 1') + return ret + + # wallet methods + + async def open_wallet_user(self,user,wnum): + data = self.users[user] + silence() + kal = KeyAddrList(self.proto,data.kafile,skip_key_address_validity_check=True) + end_silence() + return await data.wd_rpc.call( + 'open_wallet', + filename = os.path.basename(data.walletfile_fs.format(wnum)), + password = kal.entry(wnum).wallet_passwd ) + + async def close_wallet_user(self,user): + ret = await self.users[user].wd_rpc.call('close_wallet') + return 'ok' + + # mining methods + + async def start_mining(self): + data = self.users['miner'] + addr = read_from_file(data.addrfile_fs.format(1)) # mine to wallet #1, account 0 + + for i in range(20): + ret = await data.md_rpc.call( + 'start_mining', + do_background_mining = False, # run mining in background or foreground + ignore_battery = True, # ignore battery state (on laptop) + miner_address = addr, # account address to mine to + threads_count = 3 ) # number of mining threads to run + status = self.get_status(ret) + if status == 'OK': + return True + elif status == 'BUSY': + await asyncio.sleep(5) + omsg('Daemon busy. Attempting to start mining...') + else: + die(2,f'Monerod returned status {status}') + else: + die(2,'Max retries exceeded') + + async def stop_mining(self): + ret = await self.users['miner'].md_rpc.call('stop_mining') + return self.get_status(ret) + + async def mine_blocks(self,random_txs=None): + """ + - open destination wallet + - optionally create and broadcast random TXs + - start mining + - mine until funds appear in wallet + - stop mining + - close wallet + """ + + async def get_height(): + u = self.users['miner'] + for i in range(20): + try: + return (await u.md_json_rpc.call('get_last_block_header'))['block_header']['height'] + except Exception as e: + if 'onnection refused' in str(e): + omsg(f'{e}\nMonerod appears to have crashed. Attempting to restart...') + await asyncio.sleep(5) + u.md.restart() + await asyncio.sleep(5) + await self.start_mining() + else: + raise + else: + die(2,'Restart attempt limit exceeded') + + async def send_random_txs(): + from mmgen.tool import tool_api + t = tool_api() + t.init_coin('XMR','testnet') + t.usr_randchars = 0 + imsg_r(f'Sending random transactions: ') + for i in range(random_txs): + await self.transfer( + 'miner', + 123456789, + t.randpair()[1], + ) + imsg_r(f'{i+1} ') + oqmsg_r('+') + await asyncio.sleep(0.5) + imsg('') + + def print_balance(dest,ub): + imsg('Total balance in {}’s wallet #{}, account {}: {}'.format( + capfirst(dest.user), + dest.wnum, + dest.account, + ub.hl() + )) + + async def get_balance(dest): + data = self.users[dest.user] + await data.wd_rpc.call('refresh') + ret = await data.wd_rpc.call('get_accounts') + return XMRAmt(ret['subaddress_accounts'][dest.account]['unlocked_balance'],from_unit='atomic') + + self.do_msg(extra_desc=f'+{random_txs} random TXs' if random_txs else None) + + if self.dest.user != 'miner': + await self.open_wallet_user(self.dest.user,self.dest.wnum) + + if random_txs: + await send_random_txs() + + await self.start_mining() + + h = await get_height() + imsg_r(f'Chain height: {h} ') + + while True: + ub = await get_balance(self.dest) + if self.dest.test(ub): + imsg('') + oqmsg_r('+') + print_balance(self.dest,ub) + break +# else: +# imsg(f'Test {self.dest.test_desc!r} failed') + await asyncio.sleep(2) + h = await get_height() + imsg_r(f'{h} ') + oqmsg_r('+') + + await self.stop_mining() + + if self.dest.user != 'miner': + await self.close_wallet_user(self.dest.user) + + return 'ok' + + async def mine_blocks_tx(self): + return await self.mine_blocks(random_txs=self.dfl_random_txs) + + # util methods + + def get_status(self,ret): + if ret['status'] != 'OK': + imsg( 'RPC status: {}'.format(ret['status']) ) + return ret['status'] + + def do_msg(self,extra_desc=None): + self.spawn( + '', + msg_only = True, + extra_desc = f'({extra_desc})' if extra_desc else None + ) + + def set_dest(self,user,wnum,account,test,test_desc): + self.dest = namedtuple( + 'dest_info',['user','wnum','account','test','test_desc'])(user,wnum,account,test,test_desc) + + async def transfer(self,user,amt,addr): + return await self.users[user].wd_rpc.call('transfer',destinations=[{'amount':amt,'address':addr}]) + + # daemon start/stop methods + + def start_daemons(self): + self.stop_daemons() + for v in self.users.values(): + run(['mkdir','-p',v.datadir]) + v.md.start() + + def stop_daemons(self): + for v in self.users.values(): + if v.md.state != 'stopped': + v.md.stop() + run(['rm','-rf',self.datadir_base]) + + def start_wallet_daemons(self): + for v in self.users.values(): + if v.kal_range: + v.wd.start() + + def stop_wallet_daemons(self): + for v in self.users.values(): + if v.kal_range and v.wd.state != 'stopped': + v.wd.stop()