From 0ec3eb1a0721aec966c2e2e5cdfbf3fc8c5ccded Mon Sep 17 00:00:00 2001 From: MMGen Date: Wed, 3 Jul 2019 17:40:43 +0000 Subject: [PATCH] tw.py: new get_tw_label() function; raise exception on bad tw label --- mmgen/obj.py | 20 ++++++++++++++------ mmgen/rpc.py | 9 +++++---- mmgen/tw.py | 22 +++++++++++++--------- test/objtest.py | 14 +++++++++++++- test/objtest_py_d/ot_btc_mainnet.py | 5 ++++- test/ref/bad-comment-unspent.json | 8 ++++++++ test/test_py_d/ts_misc.py | 11 +++++++++++ 7 files changed, 68 insertions(+), 21 deletions(-) create mode 100644 test/ref/bad-comment-unspent.json diff --git a/mmgen/obj.py b/mmgen/obj.py index 910c02bb..280cdcd9 100755 --- a/mmgen/obj.py +++ b/mmgen/obj.py @@ -22,9 +22,11 @@ obj.py: MMGen native classes import sys,os,unicodedata from decimal import * -from mmgen.color import * from string import hexdigits,ascii_letters,digits +from mmgen.color import * +from mmgen.exception import * + def is_mmgen_seed_id(s): return SeedID(sid=s,on_fail='silent') def is_mmgen_idx(s): return AddrIdx(s,on_fail='silent') def is_mmgen_id(s): return MMGenID(s,on_fail='silent') @@ -169,14 +171,19 @@ class InitErrors(object): if m2: errmsg = '{!r}\n{}'.format(m2,errmsg) from mmgen.globalvars import g - if g.traceback: cls.on_fail == 'raise' from mmgen.util import die,msg - if cls.on_fail == 'silent': return None # TODO: return False instead? - elif cls.on_fail == 'raise': raise ValueError(errmsg) - elif cls.on_fail == 'die': die(1,errmsg) + if cls.on_fail == 'silent': + return None # TODO: return False instead? elif cls.on_fail == 'return': if errmsg: msg(errmsg) - return None # TODO: here too? + return None # TODO: return False instead? + elif g.traceback or cls.on_fail == 'raise': + if hasattr(cls,'exc'): + raise cls.exc(errmsg) + else: + raise + elif cls.on_fail == 'die': + die(1,errmsg) class Hilite(object): @@ -860,6 +867,7 @@ class MMGenWalletLabel(MMGenLabel): class TwComment(MMGenLabel): max_screen_width = 80 desc = 'tracking wallet comment' + exc = BadTwComment class MMGenTXLabel(MMGenLabel): max_len = 72 diff --git a/mmgen/rpc.py b/mmgen/rpc.py index 00942906..d50da8a4 100755 --- a/mmgen/rpc.py +++ b/mmgen/rpc.py @@ -22,9 +22,11 @@ rpc.py: Cryptocoin RPC library for the MMGen suite import http.client,base64,json -from mmgen.common import * from decimal import Decimal +from mmgen.common import * +from mmgen.obj import MMGenObject + def dmsg_rpc(s): if g.debug_rpc: msg(s) @@ -261,6 +263,8 @@ class EthereumRPCConnection(CoinDaemonRPCConnection): def rpc_error(ret): return type(ret) is tuple and ret and ret[0] == 'rpcfail' +def rpc_errmsg(ret): return ret[1][2] + def init_daemon_parity(): def resolve_token_arg(token_arg): @@ -362,6 +366,3 @@ def init_daemon_bitcoind(): def init_daemon(name): return globals()['init_daemon_'+name]() - -def rpc_errmsg(ret): - return ret[1][2] diff --git a/mmgen/tw.py b/mmgen/tw.py index 41d43ef0..ab476315 100755 --- a/mmgen/tw.py +++ b/mmgen/tw.py @@ -27,6 +27,11 @@ from mmgen.tx import is_mmgen_id CUR_HOME,ERASE_ALL = '\033[H','\033[0J' def CUR_RIGHT(n): return '\033[{}C'.format(n) +def get_tw_label(s): + try: return TwLabel(s,on_fail='raise') + except BadTwComment: raise + except: return None + class TwUnspentOutputs(MMGenObject): def __new__(cls,*args,**kwargs): @@ -131,8 +136,8 @@ watch-only wallet using '{}-addrimport' and then re-run this program. tr_rpc = [] lbl_id = ('account','label')['label_api' in g.rpch.caps] for o in us_rpc: - if not lbl_id in o: continue # coinbase outputs have no account field - l = TwLabel(o[lbl_id],on_fail='silent') + if not lbl_id in o: continue # coinbase outputs have no account field + l = get_tw_label(o[lbl_id]) if l: o.update({ 'twmmid': l.mmid, @@ -427,7 +432,7 @@ class TwAddrList(MMGenDict): for d in g.rpch.listunspent(0): if not lbl_id in d: continue # skip coinbase outputs with missing account if d['confirmations'] < minconf: continue - label = TwLabel(d[lbl_id],on_fail='silent') + label = get_tw_label(d[lbl_id]) if label: if usr_addr_list and (label.mmid not in usr_addr_list): continue if label.mmid in self: @@ -452,7 +457,7 @@ class TwAddrList(MMGenDict): else: acct_list = list(g.rpch.listaccounts(0,True).keys()) # raw list, no 'L' acct_addrs = g.rpch.getaddressesbyaccount([[a] for a in acct_list],batch=True) # use raw list here - acct_labels = MMGenList([TwLabel(a,on_fail='silent') for a in acct_list]) + acct_labels = MMGenList([get_tw_label(a) for a in acct_list]) check_dup_mmid(acct_labels) assert len(acct_list) == len(acct_addrs),( 'listaccounts() and getaddressesbyaccount() not equal in length') @@ -645,17 +650,16 @@ class TwGetBalance(MMGenObject): # 0: unconfirmed, 1: below minconf, 2: confirmed, 3: spendable (privkey in wallet) lbl_id = ('account','label')['label_api' in g.rpch.caps] for d in g.rpch.listunspent(0): - try: - lbl = TwLabel(d[lbl_id],on_fail='silent') - except: - lbl,key = None,'Non-wallet' - else: + lbl = get_tw_label(d[lbl_id]) + if lbl: if lbl.mmid.type == 'mmgen': key = lbl.mmid.obj.sid if key not in self.data: self.data[key] = [g.proto.coin_amt('0')] * 4 else: key = 'Non-MMGen' + else: + lbl,key = None,'Non-wallet' if not d['confirmations']: self.data['TOTAL'][0] += d['amount'] diff --git a/test/objtest.py b/test/objtest.py index a863b3e2..1e31c927 100755 --- a/test/objtest.py +++ b/test/objtest.py @@ -25,6 +25,8 @@ pn = os.path.dirname(sys.argv[0]) os.chdir(os.path.join(pn,os.pardir)) sys.path.__setitem__(0,os.path.abspath(os.curdir)) +os.environ['MMGEN_TEST_SUITE'] = '1' + # Import these _after_ local path's been added to sys.path from mmgen.common import * from mmgen.obj import * @@ -53,6 +55,7 @@ def run_test(test,arg,input_data): arg_copy = arg kwargs = {'on_fail':'silent'} if opt.silent else {} ret_chk = arg + exc_type = None if input_data == 'good' and type(arg) == tuple: arg,ret_chk = arg if type(arg) == dict: # pass one arg + kwargs to constructor arg_copy = arg.copy() @@ -67,6 +70,10 @@ def run_test(test,arg,input_data): ret_chk = arg['ret'] del arg['ret'] del arg_copy['ret'] + if 'ExcType' in arg: + exc_type = arg['ExcType'] + del arg['ExcType'] + del arg_copy['ExcType'] kwargs.update(arg) elif type(arg) == tuple: args = arg @@ -91,7 +98,12 @@ def run_test(test,arg,input_data): if not opt.super_silent: msg('==> {}'.format(ret)) if opt.verbose and issubclass(cls,MMGenObject): - ret.pmsg() if hasattr(ret,'pmsg') else pmsg(ret) + ret.ppmsg() if hasattr(ret,'ppmsg') else ppmsg(ret) + except Exception as e: + if not type(e).__name__ == exc_type: + raise + msg_r(' {}'.format(yellow(exc_type+':'))) + msg(e.args[0]) except SystemExit as e: if input_data == 'good': raise ValueError('Error on good input data') diff --git a/test/objtest_py_d/ot_btc_mainnet.py b/test/objtest_py_d/ot_btc_mainnet.py index 2b1826a4..a5b9e14c 100755 --- a/test/objtest_py_d/ot_btc_mainnet.py +++ b/test/objtest_py_d/ot_btc_mainnet.py @@ -100,7 +100,10 @@ tests = OrderedDict([ }), ('TwLabel', { 'bad': ('x x','x я','я:я',1,'f00f00f','a:b','x:L:3','F00BAA12:0 x', - 'F00BAA12:Z:99',tw_pfx+' x',tw_pfx+'я x'), + 'F00BAA12:Z:99',tw_pfx+' x',tw_pfx+'я x', + 'F00BAA12:S:1 '+ utf8_ctrl[:40], + {'s':'F00BAA12:S:1 '+ utf8_ctrl[:40],'on_fail':'raise','ExcType':'BadTwComment'}, + ), 'good': ( ('F00BAA12:99 a comment','F00BAA12:L:99 a comment'), 'F00BAA12:L:99 comment (UTF-8) α', diff --git a/test/ref/bad-comment-unspent.json b/test/ref/bad-comment-unspent.json new file mode 100644 index 00000000..9e7e608a --- /dev/null +++ b/test/ref/bad-comment-unspent.json @@ -0,0 +1,8 @@ +[{'address': '153AwzcymCgiy7dBxHkpeSn8yzbfnRvVPx', + 'amount': BTCAmt('34.21677044'), + 'confirmations': 7528082, + 'label': '96325D83:L:99 This label has a control character (tab) here: [ ]', + 'scriptPubKey': '76a9142c49a3ad8d89af713197e55190a7c3bc47e0208e88ac', + 'spendable': False, + 'txid': '2588cbbea1f3413ee4556c0f12f825f8670771862864a44f5ad39a71e1afdb13', + 'vout': 3}] diff --git a/test/test_py_d/ts_misc.py b/test/test_py_d/ts_misc.py index 5d71a653..f88dac76 100755 --- a/test/test_py_d/ts_misc.py +++ b/test/test_py_d/ts_misc.py @@ -166,6 +166,7 @@ class TestSuiteTool(TestSuiteMain,TestSuiteBase): ('tool_rand2file', (9,"'mmgen-tool rand2file'", [])), ('tool_encrypt', (9,"'mmgen-tool encrypt' (random data)", [])), ('tool_decrypt', (9,"'mmgen-tool decrypt' (random data)", [[[enc_infn+'.mmenc'],9]])), + ('tool_twview_bad_comment',(9,"'mmgen-tool twview' (with bad comment)", [])), # ('tool_encrypt_ref', (9,"'mmgen-tool encrypt' (reference text)", [])), ) @@ -215,6 +216,16 @@ class TestSuiteTool(TestSuiteMain,TestSuiteBase): cmp_or_die(hincog_offset,int(o)) return t + def tool_twview_bad_comment(self): # test correct operation of get_tw_label() + bw_save = os.getenv('MMGEN_BOGUS_WALLET_DATA') + os.environ['MMGEN_BOGUS_WALLET_DATA'] = joinpath(ref_dir,'bad-comment-unspent.json') + t = self.spawn('mmgen-tool',['twview']) + if bw_save: + os.environ['MMGEN_BOGUS_WALLET_DATA'] = bw_save + t.read() + t.req_exit_val = 2 + return t + class TestSuiteRefTX(TestSuiteMain,TestSuiteBase): 'create a reference transaction file (administrative command)' segwit_opts_ok = False