Browse Source

tw.py: new get_tw_label() function; raise exception on bad tw label

MMGen 5 years ago
parent
commit
0ec3eb1a07
7 changed files with 68 additions and 21 deletions
  1. 14 6
      mmgen/obj.py
  2. 5 4
      mmgen/rpc.py
  3. 13 9
      mmgen/tw.py
  4. 13 1
      test/objtest.py
  5. 4 1
      test/objtest_py_d/ot_btc_mainnet.py
  6. 8 0
      test/ref/bad-comment-unspent.json
  7. 11 0
      test/test_py_d/ts_misc.py

+ 14 - 6
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

+ 5 - 4
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]

+ 13 - 9
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']

+ 13 - 1
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')

+ 4 - 1
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) α',

+ 8 - 0
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}]

+ 11 - 0
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