obj.py,eth/obj.py,protocol.py: exception handling cleanups

This commit is contained in:
The MMGen Project 2019-05-13 10:09:32 +00:00
commit 338cb77199
Signed by: mmgen
GPG key ID: 3F8B1861E32B7DA2
4 changed files with 99 additions and 93 deletions

View file

@ -51,11 +51,11 @@ class ETHNonce(int,Hilite,InitErrors): # WIP
from mmgen.util import is_int
try:
assert is_int(n),"'{}': value is not an integer".format(n)
me = int.__new__(cls,n)
return me
n = int(n)
assert n >= 0,"'{}': value is negative".format(n)
return int.__new__(cls,n)
except Exception as e:
m = "{!r}: value cannot be converted to ETH nonce ({})"
return cls.init_fail(m.format(n,e.args[0]),on_fail)
return cls.init_fail(e,n)
@classmethod
def colorize(cls,s,color=True):

View file

@ -116,21 +116,34 @@ class AddrListList(list,MMGenObject): pass
class InitErrors(object):
on_fail='die'
@classmethod
def arg_chk(cls,on_fail):
cls.on_fail = on_fail
assert on_fail in ('die','return','silent','raise'),(
"'{}': invalid value for 'on_fail' in class {}".format(on_fail,cls.__name__) )
@staticmethod
def init_fail(m,on_fail):
@classmethod
def init_fail(cls,e,m,e2=None,m2=None,objname=None,preformat=False):
if preformat:
errmsg = m
else:
fs = "{!r}: value cannot be converted to {} {}({})"
e2_fmt = '({}) '.format(e2.args[0]) if e2 else ''
errmsg = fs.format(m,objname or cls.__name__,e2_fmt,e.args[0])
if m2: errmsg = '{!r}\n{}'.format(m2,errmsg)
from mmgen.globalvars import g
if g.traceback: on_fail == 'raise'
if g.traceback: cls.on_fail == 'raise'
from mmgen.util import die,msg
if on_fail == 'silent': return None # TODO: return False instead?
elif on_fail == 'raise': raise ValueError(m)
elif on_fail == 'die': die(1,m)
elif on_fail == 'return':
if m: msg(m)
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)
elif cls.on_fail == 'return':
if errmsg: msg(errmsg)
return None # TODO: here too?
class Hilite(object):
@ -189,6 +202,9 @@ class Hilite(object):
def __str__(self):
return self.colorize(self,color=False)
class Str(str,Hilite): pass
class Int(int,Hilite): pass
# For attrs that are always present in the data instance
# Reassignment and deletion forbidden
class MMGenImmutableAttr(object): # Descriptor
@ -284,8 +300,7 @@ class AddrIdx(int,InitErrors):
assert me > 0,'is less than one'
return me
except Exception as e:
m = "{!r}: value cannot be converted to address index ({})"
return cls.init_fail(m.format(num,e.args[0]),on_fail)
return cls.init_fail(e,num)
class AddrIdxList(list,InitErrors,MMGenObject):
max_len = 1000000
@ -314,8 +329,7 @@ class AddrIdxList(list,InitErrors,MMGenObject):
return list.__init__(self,sorted(set(ret))) # fell off end of loop - success
raise ValueError("{!r}: invalid range".format(i))
except Exception as e:
m = "{!r}: value cannot be converted to AddrIdxList ({})"
return type(self).init_fail(m.format(idx_list or fmt_str,e.args[0]),on_fail)
return type(self).init_fail(e,idx_list or fmt_str)
class MMGenRange(tuple,InitErrors,MMGenObject):
@ -343,8 +357,7 @@ class MMGenRange(tuple,InitErrors,MMGenObject):
assert last <= cls.max_idx, 'end of range > {:,}'.format(cls.max_idx)
return tuple.__new__(cls,(first,last))
except Exception as e:
m = "{!r}: value cannot be converted to {} ({})"
return cls.init_fail(m.format(s,cls.__name__,e.args[0]),on_fail)
return cls.init_fail(e,s)
@property
def first(self):
@ -396,8 +409,7 @@ class BTCAmt(Decimal,Hilite,InitErrors):
assert me >= 0,'coin amount cannot be negative'
return me
except Exception as e:
m = "{!r}: value cannot be converted to {} ({})"
return cls.init_fail(m.format(num,cls.__name__,e.args[0]),on_fail)
return cls.init_fail(e,num)
def toSatoshi(self):
return int(Decimal(self) // self.satoshi)
@ -479,8 +491,7 @@ class CoinAddr(str,Hilite,InitErrors,MMGenObject):
me.hex = va['hex']
return me
except Exception as e:
m = "{!r}: value cannot be converted to {} address ({})"
return cls.init_fail(m.format(s,g.proto.__name__,e.args[0]),on_fail)
return cls.init_fail(e,s,objname='{} address'.format(g.proto.__name__))
@classmethod
def fmtc(cls,s,**kwargs):
@ -548,8 +559,7 @@ class SeedID(str,Hilite,InitErrors):
return str.__new__(cls,sid)
raise ValueError('no arguments provided')
except Exception as e:
m = "{!r}: value cannot be converted to SeedID ({})"
return cls.init_fail(m.format(seed or sid,e.args[0]),on_fail)
return cls.init_fail(e,seed or sid)
class SubSeedIdx(str,Hilite,InitErrors):
color = 'red'
@ -572,8 +582,7 @@ class SubSeedIdx(str,Hilite,InitErrors):
me.type = sstype
return me
except Exception as e:
m = "{!r}: value cannot be converted to {} ({})"
return cls.init_fail(m.format(s,cls.__name__,e.args[0]),on_fail)
return cls.init_fail(e,s)
class MMGenID(str,Hilite,InitErrors,MMGenObject):
color = 'orange'
@ -595,8 +604,7 @@ class MMGenID(str,Hilite,InitErrors,MMGenObject):
me.sort_key = '{}:{}:{:0{w}}'.format(me.sid,me.mmtype,me.idx,w=me.idx.max_digits)
return me
except Exception as e:
m = "{}\n{!r}: value cannot be converted to MMGenID"
return cls.init_fail(m.format(e.args[0],s),on_fail)
return cls.init_fail(e,s)
class TwMMGenID(str,Hilite,InitErrors,MMGenObject):
color = 'orange'
@ -617,9 +625,8 @@ class TwMMGenID(str,Hilite,InitErrors,MMGenObject):
assert set(s[4:]) <= set(ascii_letters+digits),'contains non-alphanumeric characters'
assert len(s) > 4,'not more that four characters long'
ret,sort_key,idtype = str(s),'z_'+s,'non-mmgen'
except Exception as f:
m = "{}\nValue is {}\n{!r}: value cannot be converted to TwMMGenID"
return cls.init_fail(m.format(e.args[0],f.args[0],s),on_fail)
except Exception as e2:
return cls.init_fail(e,s,e2=e2)
me = str.__new__(cls,ret)
me.obj = ret
@ -641,48 +648,33 @@ class TwLabel(str,InitErrors,MMGenObject):
me.comment = comment
return me
except Exception as e:
m = "{}\n{!r}: value cannot be converted to TwLabel"
return cls.init_fail(m.format(e.args[0],s),on_fail)
return cls.init_fail(e,s)
class HexStr(str,Hilite,InitErrors):
color = 'red'
width = None
hexcase = 'lower'
trunc_ok = False
dtype = str
def __new__(cls,s,on_fail='die',case='lower'):
def __new__(cls,s,on_fail='die',case=None):
if type(s) == cls: return s
assert case in ('upper','lower')
cls.arg_chk(on_fail)
if case == None: case = cls.hexcase
try:
assert issubclass(type(s),str),'not a string or string subclass'
assert case in ('upper','lower'),"'{}' incorrect case specifier".format(case)
assert set(s) <= set(getattr(hexdigits,case)()),'not {}case hexadecimal symbols'.format(case)
assert not len(s) % 2,'odd-length string'
if cls.width:
assert len(s) == cls.width,'Value is not {} characters wide'.format(cls.width)
return cls.dtype.__new__(cls,s)
except Exception as e:
m = "{!r}: value cannot be converted to {} (value is {})"
return cls.init_fail(m.format(s,cls.__name__,e.args[0]),on_fail)
return cls.init_fail(e,s)
class Str(str,Hilite): pass
class Int(int,Hilite): pass
class HexStrWithWidth(HexStr):
color = 'nocolor'
hexcase = 'lower'
width = None
parent_cls = HexStr
def __new__(cls,s,on_fail='die'):
cls.arg_chk(on_fail)
try:
ret = cls.parent_cls.__new__(cls,s,case=cls.hexcase,on_fail='raise')
assert len(s) == cls.width,'Value is not {} characters wide'.format(cls.width)
return ret
except Exception as e:
m = "{}\n{!r}: value cannot be converted to {}"
return cls.init_fail(m.format(e.args[0],s,cls.__name__),on_fail)
class CoinTxID(HexStrWithWidth): color,width,hexcase = 'purple',64,'lower'
class WalletPassword(HexStrWithWidth): color,width,hexcase = 'blue',32,'lower'
class MoneroViewKey(HexStrWithWidth): color,width,hexcase = 'cyan',64,'lower'
class MMGenTxID(HexStrWithWidth): color,width,hexcase = 'red',6,'upper'
class CoinTxID(HexStr): color,width,hexcase = 'purple',64,'lower'
class WalletPassword(HexStr): color,width,hexcase = 'blue',32,'lower'
class MoneroViewKey(HexStr): color,width,hexcase = 'cyan',64,'lower'
class MMGenTxID(HexStr): color,width,hexcase = 'red',6,'upper'
class WifKey(str,Hilite,InitErrors):
width = 53
@ -696,19 +688,18 @@ class WifKey(str,Hilite,InitErrors):
g.proto.wif2hex(s) # raises exception on error
return str.__new__(cls,s)
except Exception as e:
m = '{!r}: invalid value for WIF key ({})'.format(s,e.args[0])
return cls.init_fail(m,on_fail)
return cls.init_fail(e,s)
class PubKey(HexStr,MMGenObject): # TODO: add some real checks
def __new__(cls,s,compressed,on_fail='die'):
try:
assert type(compressed) == bool,"'compressed' must be of type bool"
me = HexStr.__new__(cls,s,case='lower',on_fail='raise')
except Exception as e:
return cls.init_fail(e,s)
me = HexStr.__new__(cls,s,case='lower',on_fail=on_fail)
if me:
me.compressed = compressed
return me
except Exception as e:
m = '{!r}: invalid value for pubkey ({})'.format(s,e.args[0])
return cls.init_fail(m,on_fail)
class PrivKey(str,Hilite,InitErrors,MMGenObject):
@ -728,7 +719,7 @@ class PrivKey(str,Hilite,InitErrors,MMGenObject):
if wif:
try:
assert s == None
assert s == None,"'wif' and key hex args are mutually exclusive"
assert set(wif) <= set(ascii_letters+digits),'not an ascii alphanumeric string'
w2h = g.proto.wif2hex(wif) # raises exception on error
me = str.__new__(cls,w2h['hex'])
@ -738,25 +729,25 @@ class PrivKey(str,Hilite,InitErrors,MMGenObject):
me.orig_hex = None
return me
except Exception as e:
fs = "Value {!r} cannot be converted to {} WIF key ({})"
return cls.init_fail(fs.format(wif,g.coin,e.args[0]),on_fail)
try:
assert s and type(compressed) == bool and pubkey_type,'Incorrect args for PrivKey()'
assert len(s) == cls.width // 2,'Key length must be {}'.format(cls.width//2)
if pubkey_type == 'password': # skip WIF creation and pre-processing for passwds
me = str.__new__(cls,s.hex())
else:
me = str.__new__(cls,g.proto.preprocess_key(s.hex(),pubkey_type))
me.wif = WifKey(g.proto.hex2wif(me,pubkey_type,compressed),on_fail='raise')
me.compressed = compressed
me.pubkey_type = pubkey_type
me.orig_hex = s.hex() # save the non-preprocessed key
return me
except Exception as e:
fs = "Key={!r}\nCompressed={}\nValue pair cannot be converted to PrivKey\n({})"
return cls.init_fail(fs.format(s,compressed,e.args[0]),on_fail)
return cls.init_fail(e,s,objname='{} WIF key'.format(g.coin))
else:
try:
assert s,'private key hex data missing'
assert compressed is not None, "'compressed' arg missing"
assert pubkey_type is not None,"'pubkey_type' arg missing"
assert type(compressed) == bool,"{!r}: 'compressed' not of type 'bool'".format(compressed)
assert len(s) == cls.width // 2,'key length must be {}'.format(cls.width // 2)
if pubkey_type == 'password': # skip WIF creation and pre-processing for passwds
me = str.__new__(cls,s.hex())
else:
me = str.__new__(cls,g.proto.preprocess_key(s.hex(),pubkey_type))
me.wif = WifKey(g.proto.hex2wif(me,pubkey_type,compressed),on_fail='raise')
me.compressed = compressed
me.pubkey_type = pubkey_type
me.orig_hex = s.hex() # save the non-preprocessed key
return me
except Exception as e:
return cls.init_fail(e,s)
class AddrListID(str,Hilite,InitErrors,MMGenObject):
width = 10
@ -773,8 +764,7 @@ class AddrListID(str,Hilite,InitErrors,MMGenObject):
me.mmtype = mmtype
return me
except Exception as e:
m = "Cannot create AddrListID ({})".format(e.args[0])
return cls.init_fail(m,on_fail)
return cls.init_fail(e,'sid={}, mmtype={}'.format(sid,mmtype))
class MMGenLabel(str,Hilite,InitErrors):
color = 'pink'
@ -808,8 +798,7 @@ class MMGenLabel(str,Hilite,InitErrors):
"contains one of these forbidden symbols: '{}'".format("', '".join(cls.forbidden))
return str.__new__(cls,s)
except Exception as e:
m = "{!r}: value cannot be converted to {} ({})"
return cls.init_fail(m.format(s,cls.__name__,e.args[0]),on_fail)
return cls.init_fail(e,s)
class MMGenWalletLabel(MMGenLabel):
max_len = 48
@ -902,7 +891,7 @@ class MMGenAddrType(str,Hilite,InitErrors,MMGenObject):
except Exception as e:
m = '{}{!r}: invalid value for {} ({})'.format(
('{!r}\n'.format(errmsg) if errmsg else ''),s,cls.__name__,e.args[0])
return cls.init_fail(m,on_fail)
return cls.init_fail(e,m,preformat=True)
@classmethod
def get_names(cls):

View file

@ -56,8 +56,8 @@ def _b58chk_decode(s):
sum(_b58a.index(ch) * 58**n for n,ch in enumerate(s[::-1])) )
if len(hexstr) % 2: hexstr = '0' + hexstr
if hexstr[-8:] != hash256(hexstr[:-8])[:8]:
raise ValueError('_b58chk_decode(): {}: incorrect checksum for {}, expected {}'.format(
hexstr[-8:],hexstr[:-8],hash256(hexstr[:-8])[:8]))
fs = '_b58chk_decode(): {}: incorrect checksum for {!r}, expected {}'
raise ValueError(fs.format(hexstr[-8:],hexstr[:-8],hash256(hexstr[:-8])[:8]))
return hexstr[:-8]
# chainparams.cpp
@ -125,6 +125,7 @@ class BitcoinProtocol(MMGenObject):
@classmethod
def hex2wif(cls,hexpriv,pubkey_type,compressed): # PrivKey
assert len(hexpriv) == cls.privkey_len*2, '{} bytes: incorrect private key length!'.format(len(hexpriv)//2)
assert pubkey_type in cls.wif_ver_num, '{!r}: invalid pubkey_type'.format(pubkey_type)
return _b58chk_encode(cls.wif_ver_num[pubkey_type] + hexpriv + ('','01')[bool(compressed)])
@classmethod

View file

@ -28,6 +28,7 @@ sys.path.__setitem__(0,os.path.abspath(os.curdir))
# Import these _after_ local path's been added to sys.path
from mmgen.common import *
from mmgen.obj import *
from mmgen.altcoins.eth.obj import *
from mmgen.seed import *
opts_data = {
@ -203,7 +204,18 @@ tests = OrderedDict([
'good': ({'arg':'deadbeef','compressed':True},) # TODO: add real pubkeys
}),
('PrivKey', {
'bad': ({'wif':1},),
'bad': ( # TODO: add LTC, testnet
{'wif':1},
{'wif':'1'},
{'wif':'cMsqcmDYZP1LdKgqRh9L4ZRU9br28yvdmTPwW2YQwVSN9aQiMAoR'},
{'s':r32,'wif':'5KXEpVzjWreTcQoG5hX357s1969MUKNLuSfcszF6yu84kpsNZKb'},
{'pubkey_type':'std'},
{'s':r32},
{'s':r32,'compressed':'yes'},
{'s':r32,'compressed':'yes','pubkey_type':'std'},
{'s':r32,'compressed':True,'pubkey_type':'nonstd'},
{'s':r32+b'x','compressed':True,'pubkey_type':'std'}
),
'good': ({
'btc': (({'wif':'5KXEpVzjWreTcQoG5hX357s1969MUKNLuSfcszF6yu84kpsNZKb',
'ret':'e0aef965b905a2fedf907151df8e0a6bac832aa697801c51f58bd2ecb4fd381c'},
@ -269,6 +281,10 @@ tests = OrderedDict([
{'s':'password','ret':'P'},
{'s':'P','ret':'P'},
)}),
('ETHNonce', {
'bad': ('U','z','я',-1),
'good': (('0',0),('1',1),('100',100),1,100)
}),
])
def do_loop():