From c4f0954614f1583826acd4cd66d1f5dd3539776b Mon Sep 17 00:00:00 2001 From: The MMGen Project Date: Tue, 18 Apr 2023 18:35:58 +0000 Subject: [PATCH] autosign,xmrwallet: various fixes and cleanups --- mmgen/autosign.py | 3 +- mmgen/main_xmrwallet.py | 4 +- mmgen/wallet/enc.py | 2 +- mmgen/xmrwallet.py | 93 +++++++++++++++++++--------------- test/test_py_d/common.py | 17 +++++-- test/test_py_d/ts_autosign.py | 6 +-- test/test_py_d/ts_misc.py | 6 +-- test/test_py_d/ts_xmrwallet.py | 18 ++++--- 8 files changed, 83 insertions(+), 66 deletions(-) diff --git a/mmgen/autosign.py b/mmgen/autosign.py index 058a11b0..c900f953 100755 --- a/mmgen/autosign.py +++ b/mmgen/autosign.py @@ -131,7 +131,6 @@ class Signable: gmsg('\nSigned message files:') for m in messages: gmsg(' ' + os.path.join( self.dir, m.signed_filename )) - return def gen_bad_list(self,bad_files): for f in bad_files: @@ -185,7 +184,7 @@ class Autosign: cfg.outdir = self.tx_dir cfg.passwd_file = self.keyfile - if 'coin' in cfg._uopts: + if 'coin' in cfg._uopts and not any(k in cfg._uopts for k in ('help','longhelp')): die(1,'--coin option not supported with this command. Use --coins instead') self.coins = cfg.coins.upper().split(',') if cfg.coins else [] diff --git a/mmgen/main_xmrwallet.py b/mmgen/main_xmrwallet.py index 29c314f0..9d298e35 100755 --- a/mmgen/main_xmrwallet.py +++ b/mmgen/main_xmrwallet.py @@ -37,9 +37,7 @@ opts_data = { 'desc': """Perform various Monero wallet operations for addresses in an MMGen XMR key-address file""", 'usage2': [ - '[opts] create [wallets]', - '[opts] sync [wallets]', - '[opts] list [wallets]', + '[opts] create | sync | list [wallets]', '[opts] label LABEL_SPEC', '[opts] new NEW_ADDRESS_SPEC', '[opts] transfer TRANSFER_SPEC', diff --git a/mmgen/wallet/enc.py b/mmgen/wallet/enc.py index 673639cb..7cb1033a 100755 --- a/mmgen/wallet/enc.py +++ b/mmgen/wallet/enc.py @@ -13,7 +13,7 @@ wallet.enc: encrypted wallet base class """ from ..cfg import gc -from ..util import msg,make_chksum_8 +from ..util import msg,make_chksum_8,die from .base import wallet class wallet(wallet): diff --git a/mmgen/xmrwallet.py b/mmgen/xmrwallet.py index 819658a7..cd5ecb57 100755 --- a/mmgen/xmrwallet.py +++ b/mmgen/xmrwallet.py @@ -40,6 +40,7 @@ from .util import ( make_chksum_6, capfirst, ) +from .fileutil import get_data_from_file from .seed import SeedID from .protocol import init_proto from .proto.btc.common import b58a @@ -111,12 +112,7 @@ def is_xmr_tx_file(cfg,fn): ymsg(f'\n{type(e).__name__}: {e}') return False -class MoneroMMGenTX: - - class Base: - - def __init__(self): - self.name = type(self).__name__ +class MoneroMMGenFile: def make_chksum(self,keys=None): res = json.dumps( @@ -127,14 +123,39 @@ class MoneroMMGenTX: @property def base_chksum(self): - return self.make_chksum( - ('op','create_time','network','seed_id','source','dest','amount') - ) + return self.make_chksum(self.base_chksum_fields) @property def full_chksum(self): - return self.make_chksum(set(self.data._fields) - {'metadata'}) + return self.make_chksum(self.full_chksum_fields) + def check_checksums(self,d_wrap): + for k in ('base_chksum','full_chksum'): + a = getattr(self,k) + b = d_wrap[k] + assert a == b, f'{k} mismatch: {a} != {b}' + + def make_wrapped_data(self,in_data): + return json.dumps( + { self.data_label: { + 'base_chksum': self.base_chksum, + 'full_chksum': self.full_chksum, + 'data': in_data, + } + }, + cls = json_encoder, + ) + + def extract_data_from_file(self,cfg,fn): + return json.loads( get_data_from_file( cfg, fn, self.desc ))[self.data_label] + +class MoneroMMGenTX: + + class Base(MoneroMMGenFile): + + data_label = 'MoneroMMGenTX' + base_chksum_fields = ('op','create_time','network','seed_id','source','dest','amount') + full_chksum_fields = ('op','create_time','network','seed_id','source','dest','amount','fee','blob') xmrwallet_tx_data = namedtuple('xmrwallet_tx_data',[ 'op', 'create_time', @@ -151,10 +172,13 @@ class MoneroMMGenTX: 'metadata', ]) + def __init__(self): + self.name = type(self).__name__ + def get_info(self,indent=''): d = self.data if d.dest: - to_entry = f'\n{indent} To: ' + ( + to_entry = f'\n{indent} To: ' + ( 'Wallet {}, account {}, address {}'.format( d.dest.wallet.hl(), red(f'#{d.dest.account}'), @@ -203,16 +227,6 @@ class MoneroMMGenTX: if delete_metadata: dict_data['metadata'] = None - out = json.dumps( - { 'MoneroMMGenTX': { - 'base_chksum': self.base_chksum, - 'full_chksum': self.full_chksum, - 'data': dict_data, - } - }, - cls = json_encoder, - ) - fn = '{a}{b}-XMR[{c!s}]{d}.{e}'.format( a = self.base_chksum.upper(), b = (lambda s: f'-{s.upper()}' if s else '')(self.full_chksum), @@ -225,7 +239,7 @@ class MoneroMMGenTX: write_data_to_file( cfg = self.cfg, outfile = fn, - data = out, + data = self.make_wrapped_data(dict_data), desc = self.desc, ask_write = ask_write, ask_write_default_yes = not ask_write, @@ -276,10 +290,8 @@ class MoneroMMGenTX: self.cfg = cfg self.fn = fn - from .fileutil import get_data_from_file - try: - d_wrap = json.loads(get_data_from_file( cfg, fn ))['MoneroMMGenTX'] + d_wrap = self.extract_data_from_file( cfg, fn ) except Exception as e: die( 'MoneroMMGenTXFileParseError', f'{type(e).__name__}: {e}\nCould not load transaction file' ) @@ -307,10 +319,7 @@ class MoneroMMGenTX: metadata = d.metadata, ) - for k in ('base_chksum','full_chksum'): - a = getattr(self,k) - b = d_wrap[k] - assert a == b, f'{k} mismatch: {a} != {b}' + self.check_checksums(d_wrap) class Signed(Completed): desc = 'signed transaction' @@ -404,11 +413,14 @@ class MoneroWalletOps: if getattr(self.cfg,opt,None): check_pat_opt(opt) - def display_tx_relay_info(self,indent=''): - m = re.fullmatch( + def parse_tx_relay_opt(self): + return re.fullmatch( uarg_info['tx_relay_daemon'].pat, self.cfg.tx_relay_daemon, re.ASCII ) + + def display_tx_relay_info(self,indent=''): + m = self.parse_tx_relay_opt() msg(fmt(f""" TX relay info: Host: {blue(m[1])} @@ -431,6 +443,7 @@ class MoneroWalletOps: 'no_stop_wallet_daemon', ) wallet_exists = True + skip_wallet_check = False # for debugging def __init__(self,cfg,uarg_tuple): @@ -456,9 +469,12 @@ class MoneroWalletOps: addrfile = uarg.infile, key_address_validity_check = True ) + msg('') + self.create_addr_data() - check_wallets() + if not self.skip_wallet_check: + check_wallets() self.wd = MoneroWalletDaemon( cfg = self.cfg, @@ -468,8 +484,9 @@ class MoneroWalletOps: daemon_addr = self.cfg.daemon or None, ) + u = self.wd.usr_daemon_args = [] if self.name == 'create' and self.cfg.restore_height is None: - self.wd.usr_daemon_args = ['--offline'] + u.append('--offline') self.c = MoneroWalletRPCClient( cfg = self.cfg, @@ -927,10 +944,7 @@ class MoneroWalletOps: def init_tx_relay_daemon(self): - m = re.fullmatch( - uarg_info['tx_relay_daemon'].pat, - self.cfg.tx_relay_daemon, - re.ASCII ) + m = self.parse_tx_relay_opt() wd2 = MoneroWalletDaemon( cfg = self.cfg, @@ -1131,10 +1145,7 @@ class MoneroWalletOps: self.tx = MoneroMMGenTX.Signed( self.cfg, uarg.infile ) if self.cfg.tx_relay_daemon: - m = re.fullmatch( - uarg_info['tx_relay_daemon'].pat, - self.cfg.tx_relay_daemon, - re.ASCII ) + m = self.parse_tx_relay_opt() host,port = m[1].split(':') proxy = m[2] md = None diff --git a/test/test_py_d/common.py b/test/test_py_d/common.py index 8f243e29..52399ead 100755 --- a/test/test_py_d/common.py +++ b/test/test_py_d/common.py @@ -106,10 +106,17 @@ def restore_debug(): for k in save_debug: os.environ[k] = save_debug[k] or '' -def get_file_with_ext(tdir,ext,delete=True,no_dot=False,return_list=False,delete_all=False): +def get_file_with_ext(tdir,ext,delete=True,no_dot=False,return_list=False,delete_all=False,substr=False): - dot = ('.','')[bool(no_dot)] - flist = [os.path.join(tdir,f) for f in os.listdir(tdir) if f == ext or f[-len(dot+ext):] == dot+ext] + dot = '' if no_dot else '.' + + def have_match(fn): + return ( + fn == ext + or fn.endswith( dot + ext ) + or (substr and ext in fn) ) + + flist = [f.path for f in os.scandir(tdir) if have_match(f.name)] if not flist: return False @@ -121,9 +128,9 @@ def get_file_with_ext(tdir,ext,delete=True,no_dot=False,return_list=False,delete if delete or delete_all: if (cfg.exact_output or cfg.verbose) and not cfg.quiet: if delete_all: - msg(f'Deleting all *.{ext} files in {tdir!r}') + msg(f'Deleting all *{dot}{ext} files in {tdir!r}') else: - msg(f'Multiple *.{ext} files in {tdir!r} - deleting') + msg(f'Multiple *{dot}{ext} files in {tdir!r} - deleting') for f in flist: os.unlink(f) return False diff --git a/test/test_py_d/ts_autosign.py b/test/test_py_d/ts_autosign.py index 22394d78..453fc42d 100755 --- a/test/test_py_d/ts_autosign.py +++ b/test/test_py_d/ts_autosign.py @@ -296,14 +296,14 @@ class TestSuiteAutosignBase(TestSuiteBase): os.unlink(os.path.join( destdir, os.path.basename(fn).replace('rawmsg','sigmsg') )) return 'ok' - def do_sign(self,args,have_msg=False): + def do_sign(self,args,have_msg=False,tx_name='transaction'): t = self.spawn('mmgen-autosign', self.opts + args ) t.expect( - f'{self.tx_count} transaction{suf(self.tx_count)} signed' if self.tx_count else + f'{self.tx_count} {tx_name}{suf(self.tx_count)} signed' if self.tx_count else 'No unsigned transactions' ) if self.bad_tx_count: - t.expect(f'{self.bad_tx_count} transaction{suf(self.bad_tx_count)} failed to sign') + t.expect(f'{self.bad_tx_count} {tx_name}{suf(self.bad_tx_count)} failed to sign') t.req_exit_val = 1 if have_msg: diff --git a/test/test_py_d/ts_misc.py b/test/test_py_d/ts_misc.py index 964dbc37..acdd7a0e 100755 --- a/test/test_py_d/ts_misc.py +++ b/test/test_py_d/ts_misc.py @@ -51,10 +51,10 @@ class TestSuiteMisc(TestSuiteBase): t = self.spawn(f'mmgen-xmrwallet',['txview','test/ref/monero/3EBD06-2D6E3B-XMR[0.74].testnet.sigtx']) res = strip_ansi_escapes(t.read()).replace('\r','') for s in ( - 'Amount: 0.74 XMR', - 'Dest: 56VQ9M6k', + 'Amount: 0.74 XMR', + 'Dest: 56VQ9M6k', ): - assert s in res, s + assert s in res, f'{s} not in {res}' return t def coin_daemon_info(self): diff --git a/test/test_py_d/ts_xmrwallet.py b/test/test_py_d/ts_xmrwallet.py index f72bf7a9..72158dd5 100755 --- a/test/test_py_d/ts_xmrwallet.py +++ b/test/test_py_d/ts_xmrwallet.py @@ -315,16 +315,18 @@ class TestSuiteXMRWallet(TestSuiteBase): def create_wallets(self,user,wallet=None,add_opts=[]): assert wallet is None or is_int(wallet), 'wallet arg' data = self.users[user] - run( - 'rm -f {}*'.format( data.walletfile_fs.format(wallet or '*') ), - shell = True - ) - dir_opt = [f'--wallet-dir={data.udir}'] + stem_glob = data.walletfile_fs.format(wallet or '*') + for glob in ( + stem_glob, + stem_glob + '.keys', + stem_glob + '.address.txt' ): +# imsg(f'rm -f {glob}') + run( f'rm -f {glob}', shell=True ) t = self.spawn( 'mmgen-xmrwallet', - self.extra_opts + [f'--wallet-dir={data.udir}'] + + self.extra_opts + add_opts - + dir_opt + ['create'] + [data.kafile] + [wallet or data.kal_range] @@ -350,7 +352,7 @@ class TestSuiteXMRWallet(TestSuiteBase): [ 'new', data.kafile, spec ] ) res = strip_ansi_escapes(t.read()).replace('\r','') m = re.search(expect,res,re.DOTALL) - assert m, m + assert m, f'no match found for {expect!r}' return t na_idx = 1