From ede7353ea2b73b2c26d0eac2d4c661f022e493b0 Mon Sep 17 00:00:00 2001 From: The MMGen Project Date: Wed, 13 Mar 2024 12:16:09 +0000 Subject: [PATCH] minor fixes and cleanups --- mmgen/main.py | 2 +- mmgen/obj.py | 31 ++++++++++----------- mmgen/proto/eth/tx/new.py | 10 ++++--- mmgen/tw/addresses.py | 8 ++++-- mmgen/tx/new.py | 13 ++++----- test/cmdtest_py_d/ct_regtest.py | 48 ++++++++++++++++++++++++++++----- 6 files changed, 77 insertions(+), 35 deletions(-) diff --git a/mmgen/main.py b/mmgen/main.py index 26af081f..8434db42 100755 --- a/mmgen/main.py +++ b/mmgen/main.py @@ -44,7 +44,7 @@ def launch(*, mod=None, func=None, fqmod=None, package='mmgen'): try: errmsg = str(e.args[0]) except: - errmsg = repr(e.args[0]) + errmsg = repr(e.args[0]) if e.args else repr(e) from collections import namedtuple from .color import nocolor,yellow,red diff --git a/mmgen/obj.py b/mmgen/obj.py index 454be258..9bc3c6e2 100755 --- a/mmgen/obj.py +++ b/mmgen/obj.py @@ -347,6 +347,7 @@ class MMGenLabel(HiliteStr,InitErrors): color = 'pink' allowed = [] forbidden = [] + first_char = [] max_len = 0 min_len = 0 max_screen_width = 0 # if != 0, overrides max_len @@ -354,21 +355,18 @@ class MMGenLabel(HiliteStr,InitErrors): def __new__(cls,s): if isinstance(s,cls): return s - for k in ( cls.forbidden, cls.allowed ): - assert isinstance(k,list) - for ch in k: - assert isinstance(ch,str) and len(ch) == 1 try: s = s.strip() - for ch in s: - # Allow: (L)etter,(N)umber,(P)unctuation,(S)ymbol,(Z)space - # Disallow: (C)ontrol,(M)combining - # Combining characters create width formatting issues, so disallow them for now - if unicodedata.category(ch)[0] in ('C','M'): - raise ValueError( - '{!a}: {} characters not allowed'.format( - ch, - { 'C':'control', 'M':'combining' }[unicodedata.category(ch)[0]] )) + if not cls.allowed: + for ch in s: + # Allow: (L)etter,(N)umber,(P)unctuation,(S)ymbol,(Z)space + # Disallow: (C)ontrol,(M)combining + # Combining characters create width formatting issues, so disallow them for now + if unicodedata.category(ch)[0] in ('C','M'): + raise ValueError( + '{!a}: {} characters not allowed'.format( + ch, + { 'C':'control', 'M':'combining' }[unicodedata.category(ch)[0]] )) me = str.__new__(cls,s) @@ -380,11 +378,14 @@ class MMGenLabel(HiliteStr,InitErrors): assert len(s) >= cls.min_len, f'too short (<{cls.min_len} symbols)' + if cls.first_char and s and not s[0] in cls.first_char: + raise ValueError('first character not in set ' + ' '.join(cls.first_char)) + if cls.allowed and not set(list(s)).issubset(set(cls.allowed)): - raise ValueError('contains non-allowed symbols: ' + ' '.join(set(list(s)) - set(cls.allowed)) ) + raise ValueError('contains symbols not in set: ' + ' '.join(cls.allowed)) if cls.forbidden and any(ch in s for ch in cls.forbidden): - raise ValueError('contains one of these forbidden symbols: ' + ' '.join(cls.forbidden) ) + raise ValueError('contains one of these forbidden symbols: ' + ' '.join(cls.forbidden)) return me except Exception as e: diff --git a/mmgen/proto/eth/tx/new.py b/mmgen/proto/eth/tx/new.py index 73bf019e..b0f08c24 100755 --- a/mmgen/proto/eth/tx/new.py +++ b/mmgen/proto/eth/tx/new.py @@ -79,14 +79,16 @@ class New(Base,TxBase.New): self.txid = MMGenTxID(make_chksum_6(self.serialized).upper()) async def process_cmd_args(self,cmd_args,ad_f,ad_w): + lc = len(cmd_args) + if lc == 0 and self.usr_contract_data and 'Token' not in self.name: return - if lc != 1: - die(1,f'{lc} output{suf(lc)} specified, but Ethereum transactions must have exactly one') - for a in cmd_args: - await self.process_cmd_arg(a,ad_f,ad_w) + if lc != 1: + die(1, f'{lc} output{suf(lc)} specified, but Ethereum transactions must have exactly one') + + await self.process_cmd_arg(cmd_args[0],ad_f,ad_w) def select_unspent(self,unspent): from ....ui import line_input diff --git a/mmgen/tw/addresses.py b/mmgen/tw/addresses.py index 71beb593..d7ea97e6 100755 --- a/mmgen/tw/addresses.py +++ b/mmgen/tw/addresses.py @@ -323,7 +323,10 @@ class TwAddresses(TwView): if start is not None: for d in data[start:]: if d.al_id == al_id: - if not d.recvd and (self.cfg.autochg_ignore_labels or not d.comment): + if ( + not d.recvd + and (self.cfg.autochg_ignore_labels or not d.comment) + ): if d.comment: msg('{} {} {} {}{}'.format( yellow('WARNING: address'), @@ -369,7 +372,8 @@ class TwAddresses(TwView): assert isinstance(mmtype,MMGenAddrType) - res = [self.get_change_address( f'{sid}:{mmtype}', r.bot, r.top ) for sid,r in self.sid_ranges.items()] + res = [self.get_change_address(f'{sid}:{mmtype}', r.bot, r.top) + for sid,r in self.sid_ranges.items()] if any(res): res = list(filter(None,res)) diff --git a/mmgen/tx/new.py b/mmgen/tx/new.py index 27fdd82f..91d2881f 100755 --- a/mmgen/tx/new.py +++ b/mmgen/tx/new.py @@ -221,17 +221,18 @@ class New(Base): await self.process_cmd_arg(a,ad_f,ad_w) if self.chg_idx is None: - die(2,( - fmt( self.msg_no_change_output.format(self.dcoin) ).strip() + die(2, + fmt(self.msg_no_change_output.format(self.dcoin)).strip() if len(self.outputs) == 1 else - 'ERROR: No change output specified' )) + 'ERROR: No change output specified') if self.has_segwit_outputs() and not self.rpc.info('segwit_is_active'): - die(2,f'{gc.proj_name} Segwit address requested on the command line, ' - + 'but Segwit is not active on this chain') + die(2, + f'{gc.proj_name} Segwit address requested on the command line, ' + 'but Segwit is not active on this chain') if not self.outputs: - die(2,'At least one output must be specified on the command line') + die(2, 'At least one output must be specified on the command line') async def get_outputs_from_cmdline(self,cmd_args): from ..addrdata import AddrData,TwAddrData diff --git a/test/cmdtest_py_d/ct_regtest.py b/test/cmdtest_py_d/ct_regtest.py index 753ca204..8d27dc88 100755 --- a/test/cmdtest_py_d/ct_regtest.py +++ b/test/cmdtest_py_d/ct_regtest.py @@ -438,6 +438,11 @@ class CmdTestRegtest(CmdTestBase,CmdTestShared): ('bob_auto_chg_bad3', 'error handling for auto change address transaction (no unused addresses)'), ('bob_auto_chg_bad4', 'error handling for auto change address transaction by addrtype ' '(no unused addresses)'), + ('bob_auto_chg_bad5', 'error handling (more than one chg address listed)'), + ('bob_auto_chg_bad6', 'error handling for auto change address transaction ' + '(more than one chg address, mixed)'), + ('bob_auto_chg_bad7', 'error handling for auto change address transaction ' + '(more than one chg address requested)'), ('carol_twimport2', 'recreating Carol’s tracking wallet from JSON dump'), ('carol_rescan_blockchain', 'rescanning the blockchain (full rescan)'), ('carol_auto_chg1', 'creating an automatic change address transaction (C)'), @@ -1882,16 +1887,19 @@ class CmdTestRegtest(CmdTestBase,CmdTestShared): idx, by_mmtype = False, include_dest = True, - ignore_labels = False): + ignore_labels = False, + add_args = []): + if mmtype in ('S','B') and not self.proto.cap('segwit'): return 'skip' sid = self._user_sid('bob') t = self.spawn( 'mmgen-txcreate', - [f'--outdir={self.tr.trash_dir}', '--no-blank', f'--{user}'] + - (['--autochg-ignore-labels'] if ignore_labels else []) + - [mmtype if by_mmtype else f'{sid}:{mmtype}'] + - ([self.burn_addr+',0.01'] if include_dest else []) + [f'--outdir={self.tr.trash_dir}', '--no-blank', f'--{user}'] + + (['--autochg-ignore-labels'] if ignore_labels else []) + + [mmtype if by_mmtype else f'{sid}:{mmtype}'] + + ([self.burn_addr+',0.01'] if include_dest else []) + + add_args ) return self.txcreate_ui_common(t, menu = [], @@ -1949,10 +1957,13 @@ class CmdTestRegtest(CmdTestBase,CmdTestShared): def bob_remove_comment_uua1(self): return self._bob_remove_comment_uua(':C:3') - def _usr_auto_chg_bad(self,user,al_id,expect): + def _usr_auto_chg_bad(self, user, al_id, expect, add_args=[]): t = self.spawn( 'mmgen-txcreate', - ['-d', self.tr.trash_dir, '-B', f'--{user}', self.burn_addr+', 0.01', al_id], + ['-d', self.tr.trash_dir, '-B', f'--{user}'] + + [f'{self.burn_addr},0.01'] + + ([al_id] if al_id else []) + + add_args, exit_val = 2) t.expect(expect) return t @@ -1981,6 +1992,29 @@ class CmdTestRegtest(CmdTestBase,CmdTestShared): 'L', 'contains no unused addresses of address type' ) + def bob_auto_chg_bad5(self): + sid = self._user_sid('bob') + return self._usr_auto_chg_bad( + 'bob', + None, + 'More than one change address listed', + add_args = [f'{sid}:C:4', f'{sid}:C:5']) + + def bob_auto_chg_bad6(self): + sid = self._user_sid('bob') + return self._usr_auto_chg_bad( + 'bob', + 'L', + 'More than one', + add_args = [f'{sid}:C:4']) + + def bob_auto_chg_bad7(self): + return self._usr_auto_chg_bad( + 'bob', + 'L', + 'More than one change address requested', + add_args = ['B']) + def carol_twimport2(self): u,b = (4,3) if self.proto.cap('segwit') else (3,2) return self.carol_twimport(