Add return codes to exception classes, handle on exit

This commit is contained in:
The MMGen Project 2018-10-24 14:09:51 +00:00
commit abbdda0dcf
Signed by: mmgen
GPG key ID: 3F8B1861E32B7DA2
8 changed files with 63 additions and 40 deletions

View file

@ -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

View file

@ -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())))

View file

@ -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

View file

@ -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))

View file

@ -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__))

View file

@ -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 = []

View file

@ -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):

View file

@ -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()