From c96908ee36703360ed562763b6b33d8d3ff893db Mon Sep 17 00:00:00 2001 From: The MMGen Project Date: Sun, 3 Mar 2024 09:59:01 +0000 Subject: [PATCH] mmgen-xmrwallet: new `export-outputs-sign` operation --- mmgen/autosign.py | 10 +++++++--- mmgen/help/xmrwallet.py | 14 +++++++++----- mmgen/main_xmrwallet.py | 6 +++--- mmgen/xmrwallet.py | 27 ++++++++++++++++++--------- test/cmdtest_py_d/ct_xmr_autosign.py | 16 ++++++++++------ 5 files changed, 47 insertions(+), 26 deletions(-) diff --git a/mmgen/autosign.py b/mmgen/autosign.py index 22a56789..f9698568 100755 --- a/mmgen/autosign.py +++ b/mmgen/autosign.py @@ -39,6 +39,7 @@ class Signable: clean_all = False multiple_ok = True + action_desc = 'signed' def __init__(self,parent): self.parent = parent @@ -266,7 +267,6 @@ class Signable: dir_name = 'xmr_outputs_dir' clean_all = True summary_footer = '\n' - action_desc = 'imported and signed' async def sign(self,f): from .xmrwallet import MoneroWalletOps,xmrwallet_uargs @@ -278,8 +278,12 @@ class Signable: wallets = str(wallet_idx), spec = None ), ) - obj = await m.main( f, wallet_idx, restart_daemon=self.need_daemon_restart(m,wallet_idx) ) - obj.write() + obj = await m.main(f, wallet_idx, restart_daemon=self.need_daemon_restart(m,wallet_idx)) + if obj.data.sign: + obj.write() + self.action_desc = 'imported and signed' + else: + self.action_desc = 'imported' return obj class message(base): diff --git a/mmgen/help/xmrwallet.py b/mmgen/help/xmrwallet.py index eea3e7af..e5eb4c12 100755 --- a/mmgen/help/xmrwallet.py +++ b/mmgen/help/xmrwallet.py @@ -63,6 +63,8 @@ restore - same as ‘create’, but additionally restore wallet metadata from the corresponding JSON dump files created with ‘dump’ export-outputs - export outputs of watch-only wallets for import into their corresponding offline wallets +export-outputs-sign - same as above, plus request offline wallet to create + signed key images for import by ‘import-key-images’ import-key-images - import key images signed by offline wallets into their corresponding watch-only wallets @@ -357,12 +359,14 @@ Then insert the removable device on the offline machine. This will import the outputs into the corresponding signing wallet(s) (and optionally redo any failed transaction signing operation). -Following a ‘resubmit’, add the --rescan-blockchain option: +Following a ‘resubmit’, use the ‘export-outputs-sign’ operation instead, and +add the --rescan-blockchain option: -$ mmgen-xmrwallet --autosign --rescan-blockchain export-outputs +$ mmgen-xmrwallet --autosign --rescan-blockchain export-outputs-sign -Insert the removable device on your online machine and import the signed key -images into your online wallet as follows: +Here the offline signing wallet(s) will also create signed key images. Insert +the removable device on your online machine and import the signed key images +into your online wallet as follows: $ mmgen-xmrwallet --autosign import-key-images @@ -421,7 +425,7 @@ in again and resume where you left off. Once your watch-only wallets are synced, you need to export their outputs: -$ mmgen-xmrwallet --autosign export-outputs +$ mmgen-xmrwallet --autosign export-outputs-sign Now insert the removable device on the offline machine and wait until the LED stops flashing (or ‘safe to extract’). The wallet outputs are now imported diff --git a/mmgen/main_xmrwallet.py b/mmgen/main_xmrwallet.py index db463a39..f5df6c2f 100755 --- a/mmgen/main_xmrwallet.py +++ b/mmgen/main_xmrwallet.py @@ -48,7 +48,7 @@ opts_data = { '[opts] relay ', '[opts] resubmit | abort (for use with --autosign only)', '[opts] txview | txlist [TX_file] ...', - '[opts] export-outputs | import-key-images [wallets]', + '[opts] export-outputs | export-outputs-sign | import-key-images [wallets]', ], 'options': """ -h, --help Print this help message @@ -107,7 +107,7 @@ cmd_args = cfg._args if cmd_args and cfg.autosign and ( cmd_args[0] in ( MoneroWalletOps.kafile_arg_ops - + ('export-outputs','import-key-images','txview','txlist') + + ('export-outputs', 'export-outputs-sign', 'import-key-images', 'txview', 'txlist') ) or len(cmd_args) == 1 and cmd_args[0] in ('submit', 'resubmit', 'abort') ): @@ -133,7 +133,7 @@ elif op in ('new','transfer','sweep','label'): if len(cmd_args) != 1: cfg._opts.usage() spec = cmd_args[0] -elif op in ('export-outputs','import-key-images'): +elif op in ('export-outputs', 'export-outputs-sign', 'import-key-images'): if not cfg.autosign: # --autosign only for now - TODO die(f'--autosign must be used with command {op!r}') if len(cmd_args) > 1: diff --git a/mmgen/xmrwallet.py b/mmgen/xmrwallet.py index 3421eee5..b63cfe97 100755 --- a/mmgen/xmrwallet.py +++ b/mmgen/xmrwallet.py @@ -486,6 +486,7 @@ class MoneroWalletOutputsFile: 'wallet_index', 'outputs_data_hex', 'signed_key_images', + 'sign', ]) def __init__(self,cfg): @@ -527,7 +528,7 @@ class MoneroWalletOutputsFile: class New(Base): ext = 'raw' - def __init__( self, parent, wallet_fn, data, wallet_idx=None ): + def __init__(self, parent, wallet_fn, data, wallet_idx=None, sign=False): super().__init__(parent.cfg) self.wallet_fn = wallet_fn init_data = dict.fromkeys(self.data_tuple._fields) @@ -535,6 +536,8 @@ class MoneroWalletOutputsFile: 'seed_id': parent.kal.al_id.sid, 'wallet_index': wallet_idx or parent.get_idx_from_fn(wallet_fn), }) + if sign: + init_data['sign'] = sign init_data.update({k:v for k,v in data.items() if k in init_data}) self.data = self.data_tuple(**init_data) @@ -1882,6 +1885,7 @@ class MoneroWalletOps: action = 'exporting outputs from' stem = 'process' opts = ('rescan_blockchain',) + sign = False async def process_wallet(self,d,fn,last): h = self.rpc(self,d) @@ -1904,10 +1908,14 @@ class MoneroWalletOps: parent = self, wallet_fn = fn, data = self.c.call('export_outputs', all=True), + sign = self.sign, ) m.write() return True + class export_outputs_sign(export_outputs): + sign = True + class import_outputs(wallet): action = 'importing wallet outputs into' start_daemon = False @@ -1927,14 +1935,15 @@ class MoneroWalletOps: outputs_data_hex = m.data.outputs_data_hex ) idata = res['num_imported'] bmsg(f'\n {idata} output{suf(idata)} imported') - data = m.data._asdict() - data.update(self.c.call('export_key_images', all=True)) - m = MoneroWalletOutputsFile.SignedNew( - parent = self, - wallet_fn = m.get_wallet_fn(fn), - data = data ) - idata = m.data.signed_key_images or [] - bmsg(f' {len(idata)} key image{suf(idata)} signed') + if m.data.sign: + data = m.data._asdict() + data.update(self.c.call('export_key_images', all=True)) + m = MoneroWalletOutputsFile.SignedNew( + parent = self, + wallet_fn = m.get_wallet_fn(fn), + data = data) + idata = m.data.signed_key_images or [] + bmsg(f' {len(idata)} key image{suf(idata)} signed') return m class import_key_images(wallet): diff --git a/test/cmdtest_py_d/ct_xmr_autosign.py b/test/cmdtest_py_d/ct_xmr_autosign.py index 6358d47f..e129c01c 100755 --- a/test/cmdtest_py_d/ct_xmr_autosign.py +++ b/test/cmdtest_py_d/ct_xmr_autosign.py @@ -67,10 +67,11 @@ class CmdTestXMRAutosign(CmdTestXMRWallet,CmdTestAutosignThreaded): ('fund_alice1', 'sending funds to Alice (wallet #1)'), ('fund_alice2', 'sending funds to Alice (wallet #2)'), ('autosign_start_thread', 'starting autosign wait loop'), + ('export_outputs1', 'exporting outputs from Alice’s watch-only wallet #1'), ('create_transfer_tx1', 'creating a 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_outputs2', 'exporting outputs from Alice’s watch-only wallet #1'), ('import_key_images1', 'importing signed key images into Alice’s online wallets'), ('sync_chkbal1', 'syncing Alice’s wallet #1'), ('abort_tx1', 'aborting the current transaction (error)'), @@ -83,7 +84,7 @@ class CmdTestXMRAutosign(CmdTestXMRWallet,CmdTestAutosignThreaded): ('delete_wallets', 'deleting Alice’s wallets'), ('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_outputs3', 'exporting outputs from Alice’s watch-only 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'), @@ -353,10 +354,10 @@ class CmdTestXMRAutosign(CmdTestXMRWallet,CmdTestAutosignThreaded): else: return t - def _export_outputs(self,wallet_arg,add_opts=[]): + def _export_outputs(self, wallet_arg, op, add_opts=[]): self.insert_device_online() t = self._xmr_autosign_op( - op = 'export-outputs', + op = op, wallet_arg = wallet_arg, add_opts = add_opts) t.written_to_file('Wallet outputs') @@ -364,10 +365,13 @@ class CmdTestXMRAutosign(CmdTestXMRWallet,CmdTestAutosignThreaded): return t def export_outputs1(self): - return self._export_outputs('1',['--rescan-blockchain']) + return self._export_outputs('1', op='export-outputs') def export_outputs2(self): - return self._export_outputs('1-2') + return self._export_outputs('1', op='export-outputs-sign', add_opts=['--rescan-blockchain']) + + def export_outputs3(self): + return self._export_outputs('1-2', op='export-outputs-sign') def _import_key_images(self,wallet_arg): self.insert_device_online()