Browse Source

ts_xmr_autosign: run signing process in separate thread

The MMGen Project 1 year ago
parent
commit
08319227c5
5 changed files with 80 additions and 35 deletions
  1. 17 5
      mmgen/autosign.py
  2. 2 0
      mmgen/cfg.py
  3. 3 1
      mmgen/proto/xmr/daemon.py
  4. 4 1
      test/test_py_d/ts_autosign.py
  5. 54 28
      test/test_py_d/ts_xmr_autosign.py

+ 17 - 5
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):
 

+ 2 - 0
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',

+ 3 - 1
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
 

+ 4 - 1
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
 

+ 54 - 28
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)