From 08319227c5131ade7e9b6514c9d9e6f0f598e4cd Mon Sep 17 00:00:00 2001 From: The MMGen Project Date: Sat, 6 May 2023 15:14:07 +0000 Subject: [PATCH] ts_xmr_autosign: run signing process in separate thread --- mmgen/autosign.py | 22 +++++++-- mmgen/cfg.py | 2 + mmgen/proto/xmr/daemon.py | 4 +- test/test_py_d/ts_autosign.py | 5 +- test/test_py_d/ts_xmr_autosign.py | 82 ++++++++++++++++++++----------- 5 files changed, 80 insertions(+), 35 deletions(-) diff --git a/mmgen/autosign.py b/mmgen/autosign.py index 0cc30274..f2dbcbc6 100755 --- a/mmgen/autosign.py +++ b/mmgen/autosign.py @@ -215,7 +215,8 @@ class Autosign: dfl_wallet_dir = '/dev/shm/autosign' old_dfl_mountpoint = '/mnt/tx' - dev_disk_path = Path('/dev/disk/by-label/MMGEN_TX') + dfl_dev_disk_path = '/dev/disk/by-label/MMGEN_TX' + fake_dev_disk_path = '/tmp/mmgen-test-suite-dev.disk.by-label.MMGEN_TX' old_dfl_mountpoint_errmsg = f""" Mountpoint '{old_dfl_mountpoint}' is no longer supported! @@ -246,6 +247,9 @@ class Autosign: cfg.mnemonic_fmt, fmt_list( self.mn_fmts, fmt='no_spc' ) )) + self.dev_disk_path = Path( + self.fake_dev_disk_path if cfg.test_suite_xmr_autosign else + self.dfl_dev_disk_path ) self.mountpoint = Path(cfg.mountpoint or self.dfl_mountpoint) self.wallet_dir = Path(cfg.wallet_dir or self.dfl_wallet_dir) @@ -488,6 +492,7 @@ class Autosign: 'coin': 'xmr', 'wallet_rpc_user': 'autosigner', 'wallet_rpc_password': 'my very secret password', + 'wallet_rpc_port': 23232 if self.cfg.test_suite_xmr_autosign else None, 'wallet_dir': str(self.wallet_dir), 'autosign': True, 'autosign_mountpoint': str(self.mountpoint), @@ -571,26 +576,33 @@ class Autosign: else: return True async def do_loop(self): - n,prev_status = 0,False if not self.cfg.stealth_led: self.led.set('standby') + testing_xmr = self.cfg.test_suite_xmr_autosign + if testing_xmr: + msg('Waiting for fake device insertion') + n = 1 if testing_xmr else 0 + prev_status = False while True: status = self.get_insert_status() if status and not prev_status: msg('Device insertion detected') await self.do_sign() + if testing_xmr: + self.dev_disk_path.unlink(missing_ok=True) prev_status = status if not n % 10: msg_r(f"\r{' '*17}\rWaiting") await asyncio.sleep(1) - msg_r('.') - n += 1 + if not testing_xmr: + msg_r('.') + n += 1 def at_exit(self,exit_val,message=None): if message: msg(message) self.led.stop() - sys.exit(int(exit_val)) + sys.exit(0 if self.cfg.test_suite_xmr_autosign else int(exit_val)) def init_exit_handler(self): diff --git a/mmgen/cfg.py b/mmgen/cfg.py index 0b84eb35..b4b40218 100755 --- a/mmgen/cfg.py +++ b/mmgen/cfg.py @@ -202,6 +202,7 @@ class Config(Lockable): exec_wrapper = False test_suite = False test_suite_autosign_led_simulate = False + test_suite_xmr_autosign = False test_suite_cfgtest = False test_suite_deterministic = False test_suite_pexpect = False @@ -283,6 +284,7 @@ class Config(Lockable): 'MMGEN_COLUMNS', 'MMGEN_TEST_SUITE', 'MMGEN_TEST_SUITE_AUTOSIGN_LED_SIMULATE', + 'MMGEN_TEST_SUITE_XMR_AUTOSIGN', 'MMGEN_TEST_SUITE_CFGTEST', 'MMGEN_TEST_SUITE_DETERMINISTIC', 'MMGEN_TEST_SUITE_PEXPECT', diff --git a/mmgen/proto/xmr/daemon.py b/mmgen/proto/xmr/daemon.py index 0494f759..eeec8113 100755 --- a/mmgen/proto/xmr/daemon.py +++ b/mmgen/proto/xmr/daemon.py @@ -111,7 +111,9 @@ class MoneroWalletDaemon(RPCDaemon): self.network = proto.network self.wallet_dir = wallet_dir - self.rpc_port = getattr(self.rpc_ports,self.network) + (11 if test_suite else 0) + self.rpc_port = ( + self.cfg.wallet_rpc_port or + getattr(self.rpc_ports,self.network) + (11 if test_suite else 0) ) if port_shift: self.rpc_port += port_shift diff --git a/test/test_py_d/ts_autosign.py b/test/test_py_d/ts_autosign.py index 8d87cbe1..e053a902 100755 --- a/test/test_py_d/ts_autosign.py +++ b/test/test_py_d/ts_autosign.py @@ -80,6 +80,7 @@ class TestSuiteAutosignBase(TestSuiteBase): tmpdir_nums = [18] color = True mountpoint_basename = 'mmgen_autosign' + no_insert_check = True def __init__(self,trunner,cfgs,spawn): @@ -104,6 +105,7 @@ class TestSuiteAutosignBase(TestSuiteBase): os.path.join(self.tmpdir,self.mountpoint_basename) ), 'wallet_dir': None if self.live else self.wallet_dir, + 'test_suite_xmr_autosign': self.name == 'TestSuiteXMRAutosign', }) ) self.mountpoint = self.asi.mountpoint @@ -126,8 +128,9 @@ class TestSuiteAutosignBase(TestSuiteBase): self.opts.extend([ f'--mountpoint={self.mountpoint}', f'--wallet-dir={self.wallet_dir}', - '--no-insert-check', ]) + if self.no_insert_check: + self.opts.append('--no-insert-check') self.tx_file_ops('set_count') # initialize tx_count here so we can resume anywhere diff --git a/test/test_py_d/ts_xmr_autosign.py b/test/test_py_d/ts_xmr_autosign.py index dfd68335..c65110e9 100755 --- a/test/test_py_d/ts_xmr_autosign.py +++ b/test/test_py_d/ts_xmr_autosign.py @@ -46,6 +46,7 @@ class TestSuiteXMRAutosign(TestSuiteXMRWallet,TestSuiteAutosignBase): simulate = False bad_tx_count = 0 tx_relay_user = 'miner' + no_insert_check = False cmd_group = ( ('daemon_version', 'checking daemon version'), @@ -63,16 +64,14 @@ class TestSuiteXMRAutosign(TestSuiteXMRWallet,TestSuiteAutosignBase): ('create_wallets_miner', 'creating Monero wallets for Miner'), ('mine_initial_coins', 'mining initial coins'), ('fund_alice', 'sending funds to Alice'), + ('autosign_start_thread', 'starting autosign wait loop'), ('create_transfer_tx1', 'creating a transfer TX'), - ('sign_transfer_tx1', 'signing the transfer TX'), ('submit_transfer_tx1', 'submitting the transfer TX'), ('resubmit_transfer_tx1', 'resubmitting the transfer TX'), ('export_outputs1', 'exporting outputs from Alice’s watch-only wallet #1'), - ('export_key_images1', 'exporting signed key images from Alice’s offline wallets'), ('import_key_images1', 'importing signed key images into Alice’s online wallets'), ('sync_chkbal1', 'syncing Alice’s wallet #1'), ('create_transfer_tx2', 'creating a transfer TX (for relaying via proxy)'), - ('sign_transfer_tx2', 'signing the transfer TX (for relaying via proxy)'), ('submit_transfer_tx2', 'submitting the transfer TX (relaying via proxy)'), ('sync_chkbal2', 'syncing Alice’s wallets and checking balance'), ('dump_wallets', 'dumping Alice’s wallets'), @@ -80,15 +79,17 @@ class TestSuiteXMRAutosign(TestSuiteXMRWallet,TestSuiteAutosignBase): ('restore_wallets', 'creating online (watch-only) wallets for Alice'), ('delete_dump_files', 'deleting Alice’s dump files'), ('export_outputs2', 'exporting outputs from Alice’s watch-only wallets'), - ('export_key_images2', 'exporting signed key images from Alice’s offline wallets'), ('import_key_images2', 'importing signed key images into Alice’s online wallets'), ('sync_chkbal3', 'syncing Alice’s wallets and checking balance'), ('txlist', 'listing Alice’s submitted transactions'), ('check_tx_dirs', 'cleaning and checking signable file directories'), + ('autosign_kill_thread', 'stopping autosign wait loop'), ) def __init__(self,trunner,cfgs,spawn): + os.environ['MMGEN_TEST_SUITE_XMR_AUTOSIGN'] = '1' + TestSuiteXMRWallet.__init__(self,trunner,cfgs,spawn) TestSuiteAutosignBase.__init__(self,trunner,cfgs,spawn) @@ -109,6 +110,9 @@ class TestSuiteXMRAutosign(TestSuiteXMRWallet,TestSuiteAutosignBase): self.autosign_opts = [f'--autosign-mountpoint={self.mountpoint}'] # mmgen-xmrwallet opts self.tx_count = 1 + def __del__(self): + os.environ['MMGEN_TEST_SUITE_XMR_AUTOSIGN'] = '' + def create_tmp_wallets(self): self.spawn('',msg_only=True) data = self.users['alice'] @@ -191,7 +195,11 @@ class TestSuiteXMRAutosign(TestSuiteXMRWallet,TestSuiteAutosignBase): def delete_dump_files(self): return self._delete_files( '.dump' ) + def insert_device(self): + self.asi.dev_disk_path.touch() + def autosign_setup(self): + self.insert_device() Path(self.autosign_xmr_dir).mkdir(parents=True,exist_ok=True) Path(self.autosign_xmr_dir,'old.vkeys').touch() t = self.run_setup( @@ -202,6 +210,28 @@ class TestSuiteXMRAutosign(TestSuiteXMRWallet,TestSuiteAutosignBase): t.written_to_file('View keys') return t + def autosign_start_thread(self): + self.asi.dev_disk_path.unlink(missing_ok=True) + def run(): + t = self.spawn('mmgen-autosign', self.opts + ['wait'], direct_exec=True ) + self.write_to_tmpfile('autosign_thread_pid',str(t.ep.pid)) + import threading + threading.Thread( target=run, name='Autosign wait loop' ).start() + time.sleep(0.2) + return 'silent' + + def autosign_kill_thread(self): + self.spawn('',msg_only=True) + pid = int(self.read_from_tmpfile('autosign_thread_pid')) + self.delete_tmpfile('autosign_thread_pid') + from signal import SIGTERM + imsg(purple(f'Killing autosign wait loop [PID {pid}]')) + try: + os.kill(pid,SIGTERM) + except: + imsg(yellow(f'{pid}: no such process')) + return 'ok' + def create_watchonly_wallets(self): return self.create_wallets( 'alice', op='restore' ) @@ -209,7 +239,10 @@ class TestSuiteXMRAutosign(TestSuiteXMRWallet,TestSuiteAutosignBase): return self.create_wallets( 'alice', op='restore' ) def _create_transfer_tx(self,amt): - return self.do_op('transfer','alice',f'1:0:{self.burn_addr},{amt}',no_relay=True,do_ret=True) + t = self.do_op('transfer','alice',f'1:0:{self.burn_addr},{amt}',no_relay=True,do_ret=True) + t.read() # required! + self.insert_device() + return t def create_transfer_tx1(self): return self._create_transfer_tx('0.124') @@ -219,16 +252,14 @@ class TestSuiteXMRAutosign(TestSuiteXMRWallet,TestSuiteAutosignBase): get_file_with_ext(self.asi.xmr_tx_dir,'sigtx',delete_all=True) return self._create_transfer_tx('0.257') - def _sign_transfer_tx(self): - return self.do_sign(['--full-summary'],tx_name='Monero transaction') - - def sign_transfer_tx1(self): - return self._sign_transfer_tx() - - def sign_transfer_tx2(self): - return self._sign_transfer_tx() - - def _xmr_autosign_op(self,op,desc=None,dtype=None,ext=None,wallet_arg=None,add_opts=[]): + def _xmr_autosign_op(self,op,desc=None,dtype=None,ext=None,wallet_arg=None,add_opts=[],wait_signed=False): + if wait_signed: + oqmsg_r(gray('[waiting for signing to complete]...')) + while True: + if not self.asi.dev_disk_path.exists(): + break + time.sleep(0.2) + oqmsg(gray('OK')) data = self.users['alice'] args = ( self.extra_opts @@ -283,7 +314,8 @@ class TestSuiteXMRAutosign(TestSuiteXMRWallet,TestSuiteAutosignBase): t = self._xmr_autosign_op( op = op, add_opts = [f'--tx-relay-daemon={relay_parm}'] if relay_parm else [], - ext = ext ) + ext = ext, + wait_signed = op == 'submit' ) t.expect( f'{op.capitalize()} transaction? (y/N): ', 'y' ) t.written_to_file('Submitted transaction') if check_bal: @@ -293,11 +325,14 @@ class TestSuiteXMRAutosign(TestSuiteXMRWallet,TestSuiteAutosignBase): return t def _export_outputs(self,wallet_arg,add_opts=[]): - return self._xmr_autosign_op( + t = self._xmr_autosign_op( op = 'export-outputs', dtype = 'wallet outputs', wallet_arg = wallet_arg, add_opts = add_opts ) + t.read() # required! + self.insert_device() + return t def export_outputs1(self): return self._export_outputs('1',['--rescan-blockchain']) @@ -305,20 +340,11 @@ class TestSuiteXMRAutosign(TestSuiteXMRWallet,TestSuiteAutosignBase): def export_outputs2(self): return self._export_outputs('1-2') - def _export_key_images(self,tx_count): - self.tx_count = tx_count - return self.do_sign(['--full-summary'],tx_name='Monero wallet outputs file') - - def export_key_images1(self): - return self._export_key_images(1) - - def export_key_images2(self): - return self._export_key_images(2) - def _import_key_images(self,wallet_arg): return self._xmr_autosign_op( op = 'import-key-images', - wallet_arg = wallet_arg ) + wallet_arg = wallet_arg, + wait_signed = True ) def import_key_images1(self): return self._import_key_images(None)