Browse Source

Add return codes to exception classes, handle on exit

MMGen 6 years ago
parent
commit
abbdda0dcf
8 changed files with 63 additions and 40 deletions
  1. 1 1
      mmgen/altcoins/eth/contract.py
  2. 1 1
      mmgen/altcoins/eth/tw.py
  3. 16 9
      mmgen/exception.py
  4. 10 7
      mmgen/main.py
  5. 6 7
      mmgen/main_addrimport.py
  6. 7 8
      mmgen/rpc.py
  7. 6 4
      mmgen/tx.py
  8. 16 3
      test/test.py

+ 1 - 1
mmgen/altcoins/eth/contract.py

@@ -44,7 +44,7 @@ class Token(MMGenObject): # ERC20
 		if decimals is None:
 			ret_hex = self.do_call('decimals()')
 			try: decimals = int(ret_hex,16)
-			except: raise TokenNotInBlockchain,"token '{}' not in blockchain".format(addr)
+			except: raise TokenNotInBlockchain,"Token '{}' not in blockchain".format(addr)
 		self.base_unit = Decimal(10) ** -decimals
 
 	def transferdata2amt(self,data): # online

+ 1 - 1
mmgen/altcoins/eth/tw.py

@@ -145,7 +145,7 @@ class EthereumTrackingWallet(TrackingWallet):
 				d['comment'] = lbl.comment
 				self.write()
 				return None
-		else: # emulate RPC library
+		else: # emulate on_fail='return' of RPC library
 			m = "Address '{}' not found in '{}' section of tracking wallet"
 			return ('rpcfail',(None,2,m.format(coinaddr,self.data_root_desc())))
 

+ 16 - 9
mmgen/exception.py

@@ -21,12 +21,19 @@
 mmgen.exception: Exception classes for the MMGen suite
 """
 
-class UnrecognizedTokenSymbol(Exception): pass
-class TokenNotInBlockchain(Exception): pass
-class UserNonConfirmation(Exception): pass
-class BadMMGenTxID(Exception): pass
-class BadTxSizeEstimate(Exception): pass
-class IllegalWitnessFlagValue(Exception): pass
-class WitnessSizeMismatch(Exception): pass
-class TxHexMismatch(Exception): pass
-class RPCFailure(Exception): pass
+# 1: no hl, message only
+class UserNonConfirmation(Exception):     mmcode = 1
+
+# 2: yellow hl, message only
+class UnrecognizedTokenSymbol(Exception): mmcode = 2
+class TokenNotInBlockchain(Exception):    mmcode = 2
+
+# 3: yellow hl, 'MMGen Error' + exception + message
+class RPCFailure(Exception):              mmcode = 3
+class BadTxSizeEstimate(Exception):       mmcode = 3
+
+# 4: red hl, 'MMGen Fatal Error' + exception + message
+class BadMMGenTxID(Exception):            mmcode = 4
+class IllegalWitnessFlagValue(Exception): mmcode = 4
+class WitnessSizeMismatch(Exception):     mmcode = 4
+class TxHexMismatch(Exception):           mmcode = 4

+ 10 - 7
mmgen/main.py

@@ -56,11 +56,14 @@ def launch(what):
 				raise
 			else:
 				try: m = u'{}'.format(e.message)
-				except:
-					try: m = e.message.decode('utf8')
-					except: m = repr(e.message)
+				except: m = repr(e.message)
 
-				from mmgen.util import die,ydie
-				if type(e).__name__ == 'UserNonConfirmation': die(1,m)
-				if type(e).__name__ == 'RPCFailure': ydie(2,m)
-				ydie(2,u'\nERROR: ' + m)
+				from mmgen.util import die,ydie,rdie
+				d = [   (ydie,2,u'\nMMGen Unhandled Exception ({n}): {m}'),
+						(die, 1,u'{m}'),
+						(ydie,2,u'{m}'),
+						(ydie,3,u'\nMMGen Error ({n}): {m}'),
+						(rdie,4,u'\nMMGen Fatal Error ({n}): {m}')
+					][e.mmcode if hasattr(e,'mmcode') else 0]
+
+				d[0](d[1],d[2].format(n=type(e).__name__,m=m))

+ 6 - 7
mmgen/main_addrimport.py

@@ -79,7 +79,11 @@ def import_mmgen_list(infile):
 			rdie(2,'Segwit is not active on this chain. Cannot import Segwit addresses')
 	return al
 
-rpc_init()
+try:
+	rpc_init()
+except UnrecognizedTokenSymbol as e:
+	m = "When importing addresses for a new token, the token must be specified by address, not symbol."
+	raise type(e),'{}\n{}'.format(e.message,m)
 
 if len(cmd_args) == 1:
 	infile = cmd_args[0]
@@ -103,12 +107,7 @@ qmsg('OK. {} addresses{}'.format(al.num_addrs,m))
 err_msg = None
 
 from mmgen.tw import TrackingWallet
-try:
-	tw = TrackingWallet(mode='w')
-except UnrecognizedTokenSymbolError as e:
-	m1 = "Note: when importing addresses for a new token, the token must be specified"
-	m2 = "by address, not symbol."
-	die(1,'{}\n{}\n{}'.format(e.message,m1,m2))
+tw = TrackingWallet(mode='w')
 
 if opt.rescan and not 'rescan' in tw.caps:
 	msg("'--rescan' ignored: not supported by {}".format(type(tw).__name__))

+ 7 - 8
mmgen/rpc.py

@@ -76,7 +76,7 @@ class CoinDaemonRPCConnection(object):
 	# kwargs are for local use and are not passed to server
 
 	# By default, raises RPCFailure exception with an error msg on all errors and exceptions
-	# on_fail is one of 'raise' (default), 'return', 'silent' or 'die'
+	# on_fail is one of 'raise' (default), 'return' or 'silent'
 	# With on_fail='return', returns 'rpcfail',(resp_object,(die_args))
 	def request(self,cmd,*args,**kwargs):
 
@@ -85,6 +85,9 @@ class CoinDaemonRPCConnection(object):
 
 		cf = { 'timeout':g.http_timeout, 'batch':False, 'on_fail':'raise' }
 
+		if cf['on_fail'] not in ('raise','return','silent'):
+			raise ValueError, "request(): {}: illegal value for 'on_fail'".format(cf['on_fail'])
+
 		for k in cf:
 			if k in kwargs and kwargs[k]: cf[k] = kwargs[k]
 
@@ -96,16 +99,12 @@ class CoinDaemonRPCConnection(object):
 			p = {'method':cmd,'params':args,'id':1,'jsonrpc':'2.0'}
 
 		def do_fail(*args):
-			if cf['on_fail'] in ('return','silent'):
-				return 'rpcfail',args
+			if cf['on_fail'] in ('return','silent'): return 'rpcfail',args
 
 			try:    s = u'{}'.format(args[2])
 			except: s = repr(args[2])
 
-			if cf['on_fail'] == 'raise':
-				raise RPCFailure,s
-			elif cf['on_fail'] == 'die':
-				die(args[1],yellow(s))
+			raise RPCFailure,s
 
 		dmsg_rpc('=== request() debug ===')
 		dmsg_rpc('    RPC POST data ==> {}\n'.format(p))
@@ -155,7 +154,7 @@ class CoinDaemonRPCConnection(object):
 		dmsg_rpc(u'    RPC REPLY data ==> {}\n'.format(r2))
 
 		if not r2:
-			return do_fail(r,2,'Error: empty reply')
+			return do_fail(r,2,'Empty reply')
 
 		r3 = json.loads(r2,parse_float=Decimal)
 		ret = []

+ 6 - 4
mmgen/tx.py

@@ -402,7 +402,7 @@ Selected non-{pnm} inputs: {{}}""".strip().format(pnm=g.proj_name,pnl=g.proj_nam
 		d = g.rpch.decoderawtransaction(self.hex)
 		vsize = d['vsize'] if 'vsize' in d else d['size']
 		vmsg('\nSize: {}, Vsize: {} (true) {} (estimated)'.format(d['size'],vsize,est_vsize))
-		m1 = '\nERROR: Estimated transaction vsize is {:1.2f} times the true vsize\n'
+		m1 = 'Estimated transaction vsize is {:1.2f} times the true vsize\n'
 		m2 = 'Your transaction fee estimates will be inaccurate\n'
 		m3 = 'Please re-create and re-sign the transaction using the option --vsize-adj={:1.2f}'
 		# allow for 5% error
@@ -740,7 +740,9 @@ Selected non-{pnm} inputs: {{}}""".strip().format(pnm=g.proj_name,pnl=g.proj_nam
 			msg('OK')
 			return True
 		except Exception as e:
-			msg(yellow(repr(e.message)))
+			try: m = u'{}'.format(e.message)
+			except: m = repr(e.message)
+			msg('\n'+yellow(m))
 			return False
 
 	def mark_raw(self):
@@ -758,11 +760,11 @@ Selected non-{pnm} inputs: {{}}""".strip().format(pnm=g.proj_name,pnl=g.proj_nam
 	# check that a malicious, compromised or malfunctioning coin daemon hasn't altered hex tx data:
 	# does not check witness or signature data
 	def check_hex_tx_matches_mmgen_tx(self,deserial_tx):
-		m = 'Fatal error: a malicious or malfunctioning coin daemon or other program may have altered your data!'
+		m = 'A malicious or malfunctioning coin daemon or other program may have altered your data!'
 
 		lt = deserial_tx['lock_time']
 		if lt != int(self.locktime or 0):
-			m2 = '\nTransaction hex locktime ({}) does not match MMGen transaction locktime ({})\n{}'
+			m2 = 'Transaction hex locktime ({}) does not match MMGen transaction locktime ({})\n{}'
 			raise TxHexMismatch,m2.format(lt,self.locktime,m)
 
 		def check_equal(desc,hexio,mmio):

+ 16 - 3
test/test.py

@@ -985,6 +985,8 @@ cmd_group['ethdev'] = (
 
 	('ethdev_token_transfer_funds','transferring token funds from dev to user'),
 	('ethdev_token_addrgen',       'generating token addresses'),
+	('ethdev_token_addrimport_badaddr1','importing token addresses (no token address)'),
+	('ethdev_token_addrimport_badaddr2','importing token addresses (bad token address)'),
 	('ethdev_token_addrimport',    'importing token addresses'),
 
 	('ethdev_bal7',                'the {} balance'.format(g.coin)),
@@ -2345,12 +2347,15 @@ class MMGenTestSuite(object):
 		t.view_tx('n')
 		t.passphrase('MMGen wallet',cfgs['20']['wpasswd'])
 		if bad_vsize:
-			t.expect('ERROR: Estimated transaction vsize is')
+			t.expect('Estimated transaction vsize')
+			t.expect('1 transaction could not be signed')
+			exit_val = 2
 		else:
 			t.do_comment(False)
 			t.expect('Save signed transaction? (Y/n): ','y')
+			exit_val = 0
 		t.read()
-		t.ok(exit_val=(0,2)[bad_vsize])
+		t.ok(exit_val=exit_val)
 
 	def walletgen6(self,name,del_dw_run='dummy'):
 		self.walletgen(name)
@@ -3321,10 +3326,12 @@ class MMGenTestSuite(object):
 		t.read()
 		t.ok()
 
-	def ethdev_addrimport(self,name,ext=u'21-23]{}.addrs',expect='9/9',add_args=[]):
+	def ethdev_addrimport(self,name,ext=u'21-23]{}.addrs',expect='9/9',add_args=[],bad_input=False):
 		ext = ext.format(u'-α' if g.debug_utf8 else '')
 		fn = get_file_with_ext(ext,cfg['tmpdir'],no_dot=True,delete=False)
 		t = MMGenExpect(name,'mmgen-addrimport', eth_args()[1:] + add_args + [fn])
+		if bad_input:
+			t.read(); t.ok(2); return
 		if g.debug: t.expect("Type uppercase 'YES' to confirm: ",'YES\n')
 		t.expect('Importing')
 		t.expect(expect)
@@ -3576,6 +3583,12 @@ class MMGenTestSuite(object):
 		self.ethdev_addrgen(name,addrs='11-13')
 		self.ethdev_addrgen(name,addrs='21-23')
 
+	def ethdev_token_addrimport_badaddr1(self,name):
+		self.ethdev_addrimport(name,ext=u'[11-13]{}.addrs',add_args=['--token=abc'],bad_input=True)
+
+	def ethdev_token_addrimport_badaddr2(self,name):
+		self.ethdev_addrimport(name,ext=u'[11-13]{}.addrs',add_args=['--token='+'00deadbeef'*4],bad_input=True)
+
 	def ethdev_token_addrimport(self,name):
 		for n,r in ('1','11-13'),('2','21-23'):
 			tk_addr = read_from_tmpfile(cfg,'token_addr'+n).strip()