handle MMGen exceptions via die()

- exception module is imported only after an error occurs
This commit is contained in:
The MMGen Project 2022-02-05 13:32:56 +00:00
commit aad252a077
Signed by: mmgen
GPG key ID: 3F8B1861E32B7DA2
50 changed files with 143 additions and 169 deletions

View file

@ -17,7 +17,7 @@ from collections import namedtuple
import mmgen.tx.base as TxBase
from ....opts import opt
from ....obj import MMGenObject,MMGenList,HexStr
from ....util import msg,dmsg,make_chksum_6
from ....util import msg,dmsg,make_chksum_6,die
def addr2pubhash(proto,addr):
ap = proto.parse_addr(addr)
@ -100,7 +100,7 @@ def DeserializeTX(proto,txhex):
if has_witness:
u = bshift(2,skip=True).hex()
if u != '0001':
raise IllegalWitnessFlagValue(f'{u!r}: Illegal value for flag in transaction!')
die( 'IllegalWitnessFlagValue', f'{u!r}: Illegal value for flag in transaction!' )
d['num_txins'] = readVInt()
@ -140,7 +140,7 @@ def DeserializeTX(proto,txhex):
d['witness_size'] = 0
if len(tx) - var.idx != 4:
raise TxHexParseError('TX hex has invalid length: {} extra bytes'.format(len(tx)-var.idx-4))
die( 'TxHexParseError', 'TX hex has invalid length: {} extra bytes'.format(len(tx)-var.idx-4) )
d['locktime'] = bytes2int(bshift(4))
d['unsigned_hex'] = var.raw_tx.hex()
@ -283,8 +283,7 @@ class Base(TxBase.Base):
"""
def do_error(errmsg):
from ....exception import TxHexMismatch
raise TxHexMismatch(errmsg+'\n'+hdr)
die( 'TxHexMismatch', errmsg+'\n'+hdr )
def check_equal(desc,hexio,mmio):
if mmio != hexio:

View file

@ -16,7 +16,7 @@ import mmgen.tx.new as TxBase
from .base import Base
from ....opts import opt
from ....obj import HexStr,MMGenTxID
from ....util import dmsg,vmsg,make_chksum_6
from ....util import dmsg,vmsg,make_chksum_6,die
class New(Base,TxBase.New):
usr_fee_prompt = 'Enter transaction fee: '
@ -98,7 +98,7 @@ class New(Base,TxBase.New):
fee = self.sum_inputs() - self.sum_outputs()
if fee > self.proto.max_tx_fee:
c = self.proto.coin
raise MaxFeeExceeded(f'Transaction fee of {fee} {c} too high! (> {self.proto.max_tx_fee} {c})')
die( 'MaxFeeExceeded', f'Transaction fee of {fee} {c} too high! (> {self.proto.max_tx_fee} {c})' )
def final_inputs_ok_msg(self,funds_left):
return 'Transaction produces {} {} in change'.format(

View file

@ -14,7 +14,7 @@ base_proto.bitcoin.tx.signed: Bitcoin signed transaction class
import mmgen.tx.signed as TxBase
from .completed import Completed
from ....util import fmt,vmsg
from ....util import fmt,vmsg,die
class Signed(Completed,TxBase.Signed):
@ -25,8 +25,7 @@ class Signed(Completed,TxBase.Signed):
vmsg(f'\nVsize: {vsize} (true) {est_vsize} (estimated)')
ratio = float(est_vsize) / vsize
if not (0.95 < ratio < 1.05): # allow for 5% error
from ....exception import BadTxSizeEstimate
raise BadTxSizeEstimate(fmt(f"""
die( 'BadTxSizeEstimate', fmt(f"""
Estimated transaction vsize is {ratio:1.2f} times the true vsize
Your transaction fee estimates will be inaccurate
Please re-create and re-sign the transaction using the option --vsize-adj={1/ratio:1.2f}

View file

@ -73,7 +73,7 @@ class Unsigned(Completed,TxBase.Unsigned):
new.compare_size_and_estimated_size(tx_decoded)
new.coin_txid = CoinTxID(self.deserialized.txid)
if not new.coin_txid == tx_decoded['txid']:
raise BadMMGenTxID('txid mismatch (after signing)')
die( 'BadMMGenTxID', 'txid mismatch (after signing)' )
msg('OK')
return new
except Exception as e:

View file

@ -174,5 +174,5 @@ class TokenResolve(TokenCommon,metaclass=AsyncInit):
self.addr = TokenAddr(proto,addr)
decimals = await self.get_decimals() # requires self.addr!
if not decimals:
raise TokenNotInBlockchain(f'Token {addr!r} not in blockchain')
die( 'TokenNotInBlockchain', f'Token {addr!r} not in blockchain' )
Token.__init__(self,proto,addr,decimals,rpc)

View file

@ -20,7 +20,7 @@
altcoins.base_proto.ethereum.twctl: Ethereum tracking wallet control class
"""
from ...util import msg,ymsg,write_mode
from ...util import msg,ymsg,write_mode,die
from ...twctl import TrackingWallet
from ...addr import is_coin_addr,is_mmgen_id
from ...amt import ETHAmt
@ -160,13 +160,12 @@ class EthereumTokenTrackingWallet(EthereumTrackingWallet):
if self.importing and token_addr:
if not is_coin_addr(proto,token_addr):
raise InvalidTokenAddress(f'{token_addr!r}: invalid token address')
die( 'InvalidTokenAddress', f'{token_addr!r}: invalid token address' )
else:
assert token_addr == None,'EthereumTokenTrackingWallet_chk1'
token_addr = await self.sym2addr(proto.tokensym) # returns None on failure
if not is_coin_addr(proto,token_addr):
from ...exception import UnrecognizedTokenSymbol
raise UnrecognizedTokenSymbol(f'Specified token {proto.tokensym!r} could not be resolved!')
die( 'UnrecognizedTokenSymbol', f'Specified token {proto.tokensym!r} could not be resolved!' )
from ...addr import TokenAddr
self.token = TokenAddr(proto,token_addr)
@ -175,7 +174,7 @@ class EthereumTokenTrackingWallet(EthereumTrackingWallet):
if self.importing:
await self.import_token(self.token)
else:
raise TokenNotInWallet(f'Specified token {self.token!r} not in wallet!')
die( 'TokenNotInWallet', f'Specified token {self.token!r} not in wallet!' )
self.decimals = self.get_param('decimals')
self.symbol = self.get_param('symbol')

View file

@ -139,10 +139,10 @@ class New(Base,TxBase.New):
ret.append(waddr)
break
else:
raise UserAddressNotInWallet(errmsg.format(addr))
die( 'UserAddressNotInWallet', errmsg.format(addr) )
elif is_coin_addr(self.proto,addr):
if not addr in data_root:
raise UserAddressNotInWallet(errmsg.format(addr))
die( 'UserAddressNotInWallet', errmsg.format(addr) )
ret.append(addr)
else:
die(1,f'{addr!r}: not an MMGen ID or coin address')

View file

@ -22,7 +22,6 @@ baseconv.py: base conversion class for the MMGen suite
from collections import namedtuple
from .exception import BaseConversionError,BaseConversionPadError,HexadecimalStringError,SeedLengthError
from .util import die
def is_b58_str(s):
@ -123,7 +122,7 @@ class baseconv(object):
elif pad == 'seed':
return seed_pad_func()
else:
raise BaseConversionPadError(f"{pad!r}: illegal value for 'pad' (must be None,'seed' or int)")
die('BaseConversionPadError',f"{pad!r}: illegal value for 'pad' (must be None,'seed' or int)")
def tohex(self,words_arg,pad=None):
"convert string or list data of instance base to hex string"
@ -136,13 +135,13 @@ class baseconv(object):
desc = self.desc.short
if len(words) == 0:
raise BaseConversionError(f'empty {desc} data')
die('BaseConversionError',f'empty {desc} data')
def get_seed_pad():
assert hasattr(self,'seedlen_map_rev'), f'seed padding not supported for base {self.wl_id!r}'
d = self.seedlen_map_rev
if not len(words) in d:
raise BaseConversionError(
die( 'BaseConversionError',
f'{len(words)}: invalid length for seed-padded {desc} data in base conversion' )
return d[len(words)]
@ -151,7 +150,7 @@ class baseconv(object):
base = len(wl)
if not set(words) <= set(wl):
raise BaseConversionError(
die( 'BaseConversionError',
( 'seed data' if pad == 'seed' else f'{words_arg!r}:' ) +
f' not in {desc} format' )
@ -164,7 +163,7 @@ class baseconv(object):
from .util import is_hex_str
if not is_hex_str(hexstr):
raise HexadecimalStringError(
die( 'HexadecimalStringError',
( 'seed data' if pad == 'seed' else f'{hexstr!r}:' ) +
' not a hexadecimal string' )
@ -174,13 +173,13 @@ class baseconv(object):
"convert byte string to list or string data of instance base"
if not bytestr:
raise BaseConversionError('empty data not allowed in base conversion')
die( 'BaseConversionError', 'empty data not allowed in base conversion' )
def get_seed_pad():
assert hasattr(self,'seedlen_map'), f'seed padding not supported for base {self.wl_id!r}'
d = self.seedlen_map
if not len(bytestr) in d:
raise SeedLengthError(
die( 'SeedLengthError',
f'{len(bytestr)}: invalid byte length for seed data in seed-padded base conversion' )
return d[len(bytestr)]

View file

@ -22,9 +22,8 @@ bip39.py - Data and routines for BIP39 mnemonic seed phrases
from hashlib import sha256
from .exception import MnemonicError
from .baseconv import baseconv
from .util import is_hex_str
from .util import is_hex_str,die
def is_bip39_str(s):
return bool( bip39().tohex(s.split()) )
@ -59,7 +58,7 @@ class bip39(baseconv):
for k,v in cls.constants.items():
if v.mn_len == nwords:
return k//8 if in_bytes else k//4 if in_hex else k
raise MnemonicError(f'{nwords!r}: invalid word length for BIP39 mnemonic')
die( 'MnemonicError', f'{nwords!r}: invalid word length for BIP39 mnemonic' )
@classmethod
def seedlen2nwords(cls,seed_len,in_bytes=False,in_hex=False):
@ -83,7 +82,7 @@ class bip39(baseconv):
for n,w in enumerate(words):
if w not in wl:
raise MnemonicError(f'word #{n+1} is not in the BIP39 word list')
die( 'MnemonicError', f'word #{n+1} is not in the BIP39 word list' )
res = ''.join(['{:011b}'.format(wl.index(w)) for w in words])
@ -92,7 +91,7 @@ class bip39(baseconv):
bitlen = k
break
else:
raise MnemonicError(f'{len(words)}: invalid BIP39 seed phrase length')
die( 'MnemonicError', f'{len(words)}: invalid BIP39 seed phrase length' )
seed_bin = res[:bitlen]
chk_bin = res[bitlen:]
@ -105,7 +104,7 @@ class bip39(baseconv):
chk_bin_chk = '{:0{w}b}'.format(int(chk_hex_chk,16),w=256)[:chk_len]
if chk_bin != chk_bin_chk:
raise MnemonicError('invalid BIP39 seed phrase checksum')
die( 'MnemonicError', 'invalid BIP39 seed phrase checksum' )
return seed_hex

View file

@ -28,7 +28,6 @@ import sys,os,re
from collections import namedtuple
from .globalvars import *
from .exception import CfgFileParseError
from .util import *
def cfg_file(id_str):
@ -92,7 +91,7 @@ class CfgFile(object):
if m:
yield self.line_data(m[1],m[3],lineno,None)
else:
raise CfgFileParseError(f'Parse error in file {self.fn!r}, line {lineno}')
die( 'CfgFileParseError', f'Parse error in file {self.fn!r}, line {lineno}' )
return gen_lines()
@classmethod
@ -135,7 +134,7 @@ class CfgFileSample(CfgFile):
if m:
return self.line_data(m[2],m[4],lineno,chunk)
else:
raise CfgFileParseError(f'Parse error in file {self.fn!r}, line {lineno}')
die( 'CfgFileParseError', f'Parse error in file {self.fn!r}, line {lineno}' )
def gen_chunks(lines):
hdr = True
@ -163,7 +162,7 @@ class CfgFileSample(CfgFile):
chunk.append(line)
last_nonblank = lineno
else:
raise CfgFileParseError(f'Parse error in file {self.fn!r}, line {lineno}')
die( 'CfgFileParseError', f'Parse error in file {self.fn!r}, line {lineno}' )
if chunk:
yield process_chunk(chunk,last_nonblank)

View file

@ -80,8 +80,7 @@ class Daemon(Lockable):
try:
cp = run(cmd,check=False,stdout=out,stderr=out)
except Exception as e:
from .exception import MMGenCalledProcessError
raise MMGenCalledProcessError(f'Error starting executable: {type(e).__name__} [Errno {e.errno}]')
die( 'MMGenCalledProcessError', f'Error starting executable: {type(e).__name__} [Errno {e.errno}]' )
if self.debug:
print(cp)
return cp

View file

@ -54,16 +54,14 @@ class Filename(MMGenObject):
if base_class:
subclass = base_class.ext_to_type(self.ext,proto)
if not subclass:
from .exception import BadFileExtension
raise BadFileExtension(f'{self.ext!r}: not a recognized file extension for {base_class}')
die( 'BadFileExtension', f'{self.ext!r}: not a recognized file extension for {base_class}' )
self.subclass = subclass
try:
st = os.stat(fn)
except:
from .exception import FileNotFound
raise FileNotFound(f'{fn!r}: file not found')
die( 'FileNotFound', f'{fn!r}: file not found' )
import stat
if stat.S_ISBLK(st.st_mode):

View file

@ -89,8 +89,7 @@ def _check_file_type_and_access(fname,ftype,blkdev_ok=False):
try:
mode = os.stat(fname).st_mode
except:
from .exception import FileNotFound
raise FileNotFound(f'Requested {ftype} {fname!r} not found')
die( 'FileNotFound', f'Requested {ftype} {fname!r} not found' )
for t in ok_types:
if t[0](mode):
@ -289,8 +288,8 @@ def get_data_from_file(infile,desc='data',dash=False,silent=False,binary=False,q
data = data.decode()
if len(data) == g.max_input_size + 1:
from .exception import MaxInputSizeExceeded
raise MaxInputSizeExceeded(f'Too much input data! Max input data size: {f.max_input_size} bytes')
die( 'MaxInputSizeExceeded',
f'Too much input data! Max input data size: {f.max_input_size} bytes' )
return data

View file

@ -20,9 +20,8 @@
flags.py: Class flags and opts for the MMGen suite
"""
from .exception import ClassFlagsError
from .base_obj import AttrCtrl,Lockable
from .util import fmt_list
from .util import fmt_list,die
class ClassFlags(AttrCtrl):
_name = 'flags'
@ -35,10 +34,10 @@ class ClassFlags(AttrCtrl):
for a in self._available:
if a.startswith('_'):
raise ClassFlagsError(f'{a!r}: {self._desc} cannot begin with an underscore')
die( 'ClassFlagsError', f'{a!r}: {self._desc} cannot begin with an underscore' )
for b in self.reserved_attrs:
if a == b:
raise ClassFlagsError(f'{a!r}: {b} is a reserved name for {self._desc}')
die( 'ClassFlagsError', f'{a!r}: {b} is a reserved name for {self._desc}' )
if arg:
assert type(arg) in (list,tuple), f"{arg!r}: {self._name!r} must be list or tuple"
@ -69,14 +68,14 @@ class ClassFlags(AttrCtrl):
assert type(val) is bool, f'{val!r} not boolean'
old_val = getattr(self,name)
if val and old_val:
raise ClassFlagsError(f'{self._desc} {name!r} already set')
die( 'ClassFlagsError', f'{self._desc} {name!r} already set' )
if not val and not old_val:
raise ClassFlagsError(f'{self._desc} {name!r} not set, so cannot be unset')
die( 'ClassFlagsError', f'{self._desc} {name!r} not set, so cannot be unset' )
super().__setattr__(name,val)
def not_available_error(self,name):
raise ClassFlagsError('{!r}: unrecognized {} for {}: (available {}: {})'.format(
die( 'ClassFlagsError', '{!r}: unrecognized {} for {}: (available {}: {})'.format(
name,
self._desc,
type(self._parent).__name__,

View file

@ -83,9 +83,9 @@ class PrivKey(bytes,Hilite,InitErrors,MMGenObject):
me.wif = str.__new__(WifKey,wif) # check has been done
me.orig_bytes = None
if k.sec != proto.preprocess_key(k.sec,k.pubkey_type):
from .exception import PrivateKeyError
raise PrivateKeyError(
f'{proto.cls_name} WIF key {me.wif!r} encodes private key with invalid value {me}')
from .util import die
die( 'PrivateKeyError',
f'{proto.cls_name} WIF key {me.wif!r} encodes private key with invalid value {me}' )
me.proto = proto
return me
except Exception as e:

View file

@ -64,8 +64,9 @@ class keygen_backend:
try:
from .secp256k1 import priv2pub
if not priv2pub(bytes.fromhex('deadbeef'*8),1):
from .exception import ExtensionModuleError
raise ExtensionModuleError('Unable to execute priv2pub() from secp256k1 extension module')
from .util import die
die( 'ExtensionModuleError',
'Unable to execute priv2pub() from secp256k1 extension module' )
return True
except Exception as e:
if not silent:

View file

@ -69,8 +69,8 @@ class LEDControl:
except: pass
else: break
else:
from mmgen.exception import NoLEDSupport
raise NoLEDSupport('Control files not found! LED control not supported on this system')
from .util import die
die( 'NoLEDSupport', 'Control files not found! LED control not supported on this system' )
msg(f'{board.name} board detected')
@ -89,6 +89,7 @@ class LEDControl:
fp.write(f'{init_val}\n')
return True
except PermissionError:
from .util import die
die(2,'\n'+fmt(f"""
You do not have access to the {desc} file
To allow access, run the following command:

View file

@ -57,6 +57,7 @@ def launch(mod):
_o = namedtuple('exit_data',['color','exit_val','fs'])
d = {
0: _o(nocolor, 1, '{message}'),
1: _o(nocolor, 1, '{message}'),
2: _o(yellow, 2, '{message}'),
3: _o(yellow, 3, '\nMMGen Error ({name}): {message}'),

View file

@ -215,7 +215,7 @@ def process_args(cmd,cmd_args,cls):
# If we're reading from a pipe, replace '-' with output of previous command
if flag == 'STDIN_OK' and u_args and u_args[0] == '-':
if sys.stdin.isatty():
raise BadFilename("Standard input is a TTY. Can't use '-' as a filename")
die( 'BadFilename', "Standard input is a TTY. Can't use '-' as a filename" )
else:
max_dlen_spec = '10kB' # limit input to 10KB for now
max_dlen = parse_bytespec(max_dlen_spec)

View file

@ -23,7 +23,6 @@ obj.py: MMGen native classes
import sys,os,re,unicodedata
from .objmethods import *
from .exception import BadTwComment
def get_obj(objname,*args,**kwargs):
"""
@ -378,7 +377,7 @@ class MMGenWalletLabel(MMGenLabel):
class TwComment(MMGenLabel):
max_screen_width = 80
desc = 'tracking wallet comment'
exc = BadTwComment
exc = 'BadTwComment'
class MMGenTxLabel(MMGenLabel):
max_len = 72

View file

@ -115,13 +115,14 @@ class InitErrors:
if m2:
errmsg = repr(m2) + '\n' + errmsg
if hasattr(cls,'passthru_excs') and type(e) in cls.passthru_excs:
from .util import die
if hasattr(cls,'passthru_excs') and type(e).__name__ in cls.passthru_excs:
raise
elif hasattr(cls,'exc'):
raise cls.exc(errmsg)
die( cls.exc, errmsg )
else:
from .exception import ObjectInitError
raise ObjectInitError(errmsg)
die( 'ObjectInitError', errmsg )
@classmethod
def method_not_implemented(cls):

View file

@ -21,7 +21,6 @@ opts.py: MMGen-specific options processing after generic processing by share.Op
"""
import sys,os,stat
from .exception import UserOptError,CfgFileParseError
from .globalvars import g
from .base_obj import Lockable
@ -163,11 +162,13 @@ def override_globals_from_cfg_file(ucfg,need_proto):
refval = getattr(cls,attr)
val = ucfg.parse_value(d.value,refval)
if not val:
raise CfgFileParseError(f'Parse error in file {ucfg.fn!r}, line {d.lineno}')
from .util import die
die( 'CfgFileParseError', f'Parse error in file {ucfg.fn!r}, line {d.lineno}' )
val_conv = set_for_type(val,refval,attr,src=ucfg.fn)
setattr(cls,attr,val_conv)
else:
raise CfgFileParseError(f'{d.name!r}: unrecognized option in {ucfg.fn!r}, line {d.lineno}')
from .util import die
die( 'CfgFileParseError', f'{d.name!r}: unrecognized option in {ucfg.fn!r}, line {d.lineno}' )
def override_globals_and_set_opts_from_env(opt):
for name in g.env_opts:
@ -426,39 +427,39 @@ def check_usr_opts(usr_opts): # Raises an exception if any check fails
try:
l = val.split(sep)
except:
raise UserOptError(f'{val!r}: invalid {desc} (not {sepword}-separated list)')
die( 'UserOptError', f'{val!r}: invalid {desc} (not {sepword}-separated list)' )
if len(l) != n:
raise UserOptError(f'{val!r}: invalid {desc} ({n} {sepword}-separated items required)')
die( 'UserOptError', f'{val!r}: invalid {desc} ({n} {sepword}-separated items required)' )
def opt_compares(val,op_str,target,desc,desc2=''):
import operator as o
op_f = { '<':o.lt, '<=':o.le, '>':o.gt, '>=':o.ge, '=':o.eq }[op_str]
if not op_f(val,target):
d2 = desc2 + ' ' if desc2 else ''
raise UserOptError(f'{val}: invalid {desc} ({d2}not {op_str} {target})')
die( 'UserOptError', f'{val}: invalid {desc} ({d2}not {op_str} {target})' )
def opt_is_int(val,desc):
if not is_int(val):
raise UserOptError(f'{val!r}: invalid {desc} (not an integer)')
die( 'UserOptError', f'{val!r}: invalid {desc} (not an integer)' )
def opt_is_float(val,desc):
try:
float(val)
except:
raise UserOptError(f'{val!r}: invalid {desc} (not a floating-point number)')
die( 'UserOptError', f'{val!r}: invalid {desc} (not a floating-point number)' )
def opt_is_in_list(val,tlist,desc):
if val not in tlist:
q,sep = (('',','),("'","','"))[type(tlist[0]) == str]
raise UserOptError('{q}{v}{q}: invalid {w}\nValid choices: {q}{o}{q}'.format(
die( 'UserOptError', '{q}{v}{q}: invalid {w}\nValid choices: {q}{o}{q}'.format(
v = val,
w = desc,
q = q,
o = sep.join(map(str,sorted(tlist))) ))
def opt_unrecognized(key,val,desc='value'):
raise UserOptError(f'{val!r}: unrecognized {desc} for option {fmt_opt(key)!r}')
die( 'UserOptError', f'{val!r}: unrecognized {desc} for option {fmt_opt(key)!r}' )
def opt_display(key,val='',beg='For selected',end=':\n'):
from .util import msg_r
@ -475,15 +476,15 @@ def check_usr_opts(usr_opts): # Raises an exception if any check fails
if key == 'out_fmt':
p = 'hidden_incog_output_params'
if sstype == IncogWalletHidden and not getattr(opt,p):
raise UserOptError(
die( 'UserOptError',
'Hidden incog format output requested. ' +
f'You must supply a file and offset with the {fmt_opt(p)!r} option' )
if issubclass(sstype,IncogWallet) and opt.old_incog_fmt:
opt_display(key,val,beg='Selected',end=' ')
opt_display('old_incog_fmt',beg='conflicts with',end=':\n')
raise UserOptError('Export to old incog wallet format unsupported')
die( 'UserOptError', 'Export to old incog wallet format unsupported' )
elif issubclass(sstype,Brainwallet):
raise UserOptError('Output to brainwallet format unsupported')
die( 'UserOptError', 'Output to brainwallet format unsupported' )
chk_out_fmt = chk_in_fmt
@ -491,7 +492,7 @@ def check_usr_opts(usr_opts): # Raises an exception if any check fails
a = val.rsplit(',',1) # permit comma in filename
if len(a) != 2:
opt_display(key,val)
raise UserOptError('Option requires two comma-separated arguments')
die( 'UserOptError', 'Option requires two comma-separated arguments' )
fn,offset = a
opt_is_int(offset,desc)
@ -514,7 +515,7 @@ def check_usr_opts(usr_opts): # Raises an exception if any check fails
val2 = getattr(opt,key2)
from .wallet import IncogWalletHidden
if val2 and val2 not in IncogWalletHidden.fmt_codes:
raise UserOptError(f'Option conflict:\n {fmt_opt(key)}, with\n {fmt_opt(key2)}={val2}')
die( 'UserOptError', f'Option conflict:\n {fmt_opt(key)}, with\n {fmt_opt(key2)}={val2}' )
chk_hidden_incog_output_params = chk_hidden_incog_input_params
@ -539,7 +540,7 @@ def check_usr_opts(usr_opts): # Raises an exception if any check fails
a = val.split(',')
if len(a) != 2:
opt_display(key,val)
raise UserOptError('Option requires two comma-separated arguments')
die( 'UserOptError', 'Option requires two comma-separated arguments' )
opt_is_int(a[0],'seed length '+desc)
opt_is_in_list(int(a[0]),Seed.lens,'seed length '+desc)
opt_is_in_list(a[1],list(hash_presets.keys()),'hash preset '+desc)
@ -567,14 +568,14 @@ def check_usr_opts(usr_opts): # Raises an exception if any check fails
# TODO: move this check elsewhere
# def chk_rbf(key,val,desc):
# if not proto.cap('rbf'):
# raise UserOptError(f'--rbf requested, but {proto.coin} does not support replace-by-fee transactions')
# die( 'UserOptError', f'--rbf requested, but {proto.coin} does not support replace-by-fee transactions' )
# def chk_bob(key,val,desc):
# from .regtest import MMGenRegtest
# try:
# os.stat(os.path.join(MMGenRegtest(g.coin).d.datadir,'regtest','debug.log'))
# except:
# raise UserOptError(
# die( 'UserOptError',
# 'Regtest (Bob and Alice) mode not set up yet. ' +
# f"Run '{g.proj_name.lower()}-regtest setup' to initialize." )
#
@ -587,13 +588,13 @@ def check_usr_opts(usr_opts): # Raises an exception if any check fails
# TODO: move this check elsewhere
# def chk_token(key,val,desc):
# if not 'token' in proto.caps:
# raise UserOptError(f'Coin {tx.coin!r} does not support the --token option')
# die( 'UserOptError', f'Coin {tx.coin!r} does not support the --token option' )
# if len(val) == 40 and is_hex_str(val):
# return
# if len(val) > 20 or not all(s.isalnum() for s in val):
# raise UserOptError(f'{val!r}: invalid parameter for --token option')
# die( 'UserOptError', f'{val!r}: invalid parameter for --token option' )
from .util import is_int
from .util import is_int,die
cfuncs = { k:v for k,v in locals().items() if k.startswith('chk_') }
@ -642,8 +643,8 @@ def check_and_set_autoset_opts(): # Raises exception if any check fails
else:
ret = locals()[asd.type](key,val,asd)
if type(ret) is str:
from .util import fmt_list
raise UserOptError(
from .util import fmt_list,die
die( 'UserOptError',
'{!r}: invalid parameter for option --{} (not {}: {})'.format(
val,
key.replace('_','-'),

View file

@ -22,7 +22,7 @@ passwdlist.py: Password list class for the MMGen suite
from collections import namedtuple
from .util import ymsg,is_int,keypress_confirm
from .util import ymsg,is_int,keypress_confirm,die
from .obj import ImmutableAttr,ListItemAttr,MMGenPWIDString,TwComment
from .key import PrivKey
from .addr import MMGenPasswordType,AddrIdx,AddrListID
@ -112,8 +112,7 @@ class PasswordList(AddrList):
self.pw_fmt = pw_fmt
self.pw_fmt_disp = pw_fmt
if self.pw_fmt not in self.pw_info:
from .exception import InvalidPasswdFormat
raise InvalidPasswdFormat(
die( 'InvalidPasswdFormat',
'{!r}: invalid password format. Valid formats: {}'.format(
self.pw_fmt,
', '.join(self.pw_info) ))

View file

@ -190,7 +190,7 @@ class RPCBackends:
headers = self.http_hdrs )
r = s.getresponse() # => http.client.HTTPResponse instance
except Exception as e:
raise RPCFailure(str(e))
die( 'RPCFailure', str(e) )
if timeout:
ret = ( r.read(), r.status )
@ -303,8 +303,7 @@ class RPCClient(MMGenObject):
try:
socket.create_connection((host,port),timeout=1).close()
except:
from .exception import SocketError
raise SocketError(f'Unable to connect to {host}:{port}')
die( 'SocketError', f'Unable to connect to {host}:{port}' )
self.http_hdrs = { 'Content-Type': 'application/json' }
self.url = f'{self.network_proto}://{host}:{port}{self.host_path}'
@ -435,7 +434,7 @@ class RPCClient(MMGenObject):
except:
try: m = t['error']
except: m = t
raise RPCFailure(m)
die( 'RPCFailure', m )
else:
import http
m,s = ( '', http.HTTPStatus(status) )
@ -445,7 +444,7 @@ class RPCClient(MMGenObject):
except:
try: m = text.decode()
except: m = text
raise RPCFailure(f'{s.value} {s.name}: {m}')
die( 'RPCFailure', f'{s.value} {s.name}: {m}' )
async def stop_daemon(self,quiet=False,silent=False):
if self.daemon.state == 'ready':
@ -852,10 +851,10 @@ async def rpc_init(proto,backend=None,daemon=None,ignore_daemon_version=False):
ignore_daemon_version or proto.ignore_daemon_version or g.ignore_daemon_version )
if rpc.chain not in proto.chain_names:
raise RPCChainMismatch('\n'+fmt(f"""
die( 'RPCChainMismatch', '\n' + fmt(f"""
Protocol: {proto.cls_name}
Valid chain names: {fmt_list(proto.chain_names,fmt='bare')}
RPC client chain: {rpc.chain}
""",indent=' ').rstrip())
""",indent=' ').rstrip() )
return rpc

View file

@ -80,8 +80,7 @@ class SeedShareList(SubSeedList):
msg(f'master_share seed ID collision with parent seed, incrementing nonce to {nonce+1}')
else:
return ms
from .exception import SubSeedNonceRangeExceeded
raise SubSeedNonceRangeExceeded('nonce range exceeded')
die( 'SubSeedNonceRangeExceeded', 'nonce range exceeded' )
def last_share_debug(last_share):
if not debug_last_share:
@ -109,8 +108,7 @@ class SeedShareList(SubSeedList):
self.data['long'][ls.sid] = (count,nonce)
break
else:
from .exception import SubSeedNonceRangeExceeded
raise SubSeedNonceRangeExceeded('nonce range exceeded')
die( 'SubSeedNonceRangeExceeded', 'nonce range exceeded' )
if g.debug_subseed:
A = parent_seed.data
@ -119,8 +117,7 @@ class SeedShareList(SubSeedList):
def get_share_by_idx(self,idx,base_seed=False):
if idx < 1 or idx > self.count:
from .exception import RangeError
raise RangeError(f'{idx}: share index out of range')
die( 'RangeError', f'{idx}: share index out of range' )
elif idx == self.count:
return self.last_share
elif self.master_share and idx == 1:

View file

@ -21,7 +21,7 @@ subseed.py: Subseed classes and methods for the MMGen suite
"""
from .color import green
from .util import msg_r,msg,qmsg
from .util import msg_r,msg,qmsg,die
from .obj import MMGenRange,IndexedDict
from .seed import *
from .crypto import scramble_seed
@ -191,8 +191,7 @@ class SubSeedList(MMGenObject):
self.data[length][sid] = (idx,nonce)
return last_sid == sid
else: # must exit here, as this could leave self.data in inconsistent state
from .exception import SubSeedNonceRangeExceeded
raise SubSeedNonceRangeExceeded('add_subseed(): nonce range exceeded')
die( 'SubSeedNonceRangeExceeded', 'add_subseed(): nonce range exceeded' )
for idx in SubSeedIdxRange(first_idx,last_idx).iterate():
match1 = add_subseed(idx,'long')

View file

@ -22,7 +22,6 @@ tw: Tracking wallet dependency classes for the MMGen suite
import time
from .exception import BadTwLabel,BadTwComment
from .objmethods import Hilite,InitErrors,MMGenObject
from .obj import TwComment
from .addr import MMGenID
@ -86,8 +85,8 @@ class TwMMGenID(str,Hilite,InitErrors,MMGenObject):
# non-displaying container for TwMMGenID,TwComment
class TwLabel(str,InitErrors,MMGenObject):
exc = BadTwLabel
passthru_excs = (BadTwComment,)
exc = 'BadTwLabel'
passthru_excs = ('BadTwComment',)
def __new__(cls,proto,text):
if type(text) == cls:
return text
@ -109,8 +108,8 @@ def get_tw_label(proto,s):
"""
try:
return TwLabel(proto,s)
except BadTwComment:
raise
except Exception as e:
# print(e)
return None
if type(e).__name__ == 'BadTwComment': # do it this way to avoid importing .exception
raise
else:
return None

View file

@ -126,8 +126,7 @@ class TwAddrList(MMGenDict,TwCommon,metaclass=AsyncInit):
if not self.has_age:
show_age = False
if age_fmt not in self.age_fmts:
from .exception import BadAgeFormat
raise BadAgeFormat(f'{age_fmt!r}: invalid age format (must be one of {self.age_fmts!r})')
die( 'BadAgeFormat', f'{age_fmt!r}: invalid age format (must be one of {self.age_fmts!r})' )
fs = '{mid}' + ('',' {addr}')[showbtcaddrs] + ' {cmt} {amt}' + ('',' {age}')[show_age]
mmaddrs = [k for k in self.keys() if k.type == 'mmgen']
max_mmid_len = max(len(k) for k in mmaddrs) + 2 if mmaddrs else 10

View file

@ -21,7 +21,7 @@ twctl: Tracking wallet control class for the MMGen suite
"""
from .globalvars import g
from .util import msg,dmsg,write_mode,base_proto_subclass
from .util import msg,dmsg,write_mode,base_proto_subclass,die
from .base_obj import AsyncInit
from .objmethods import MMGenObject
from .obj import TwComment,get_obj
@ -61,8 +61,7 @@ class TrackingWallet(MMGenObject,metaclass=AsyncInit):
self.init_empty()
if self.data['coin'] != self.proto.coin: # TODO remove?
from .exception import WalletFileError
raise WalletFileError(
die( 'WalletFileError',
'Tracking wallet coin ({}) does not match current coin ({})!'.format(
self.data['coin'],
self.proto.coin ))
@ -98,8 +97,7 @@ class TrackingWallet(MMGenObject,metaclass=AsyncInit):
self.init_empty()
self.force_write()
else:
from .exception import WalletFileError
raise WalletFileError(f'File {self.tw_fn!r} exists but does not contain valid json data')
die( 'WalletFileError', f'File {self.tw_fn!r} exists but does not contain valid json data' )
else:
self.upgrade_wallet_maybe()

View file

@ -128,8 +128,7 @@ Actions: [q]uit view, [p]rint to file, pager [v]iew, [w]ide view, add [l]abel:
@age_fmt.setter
def age_fmt(self,val):
if val not in self.age_fmts:
from .exception import BadAgeFormat
raise BadAgeFormat(f'{val!r}: invalid age format (must be one of {self.age_fmts!r})')
die( 'BadAgeFormat', f'{val!r}: invalid age format (must be one of {self.age_fmts!r})' )
self._age_fmt = val
def get_display_precision(self):

View file

@ -16,7 +16,7 @@ from ..globalvars import *
from ..objmethods import MMGenObject
from ..obj import ImmutableAttr,ListItemAttr,MMGenListItem,MMGenTxLabel,TwComment,CoinTxID,HexStr
from ..addr import MMGenID,CoinAddr
from ..util import msg,ymsg,fmt,remove_dups,keypress_confirm,make_timestamp,line_input
from ..util import msg,ymsg,fmt,remove_dups,keypress_confirm,make_timestamp,line_input,die
from ..opts import opt
class MMGenTxIO(MMGenListItem):
@ -118,8 +118,8 @@ class Base(MMGenObject):
def check_correct_chain(self):
if hasattr(self,'rpc'):
if self.chain != self.rpc.chain:
raise TransactionChainMismatch(
f'Transaction is for {self.chain}, but coin daemon chain is {self.rpc.chain}!')
die( 'TransactionChainMismatch',
f'Transaction is for {self.chain}, but coin daemon chain is {self.rpc.chain}!' )
def sum_inputs(self):
return sum(e.amt for e in self.inputs)
@ -177,7 +177,7 @@ class Base(MMGenObject):
m = fs.format('\n '.join(non_mmaddrs))
if caller in ('txdo','txsign'):
if not opt.keys_from_file:
raise UserOptError(f'\n{indent}ERROR: {m}\n')
die( 'UserOptError', f'\n{indent}ERROR: {m}\n' )
else:
msg(f'\n{indent}WARNING: {m}\n')
if not (opt.yes or keypress_confirm('Continue?',default_yes=True)):

View file

@ -60,8 +60,7 @@ class MMGenTxFile(MMGenObject):
try:
desc = 'data'
if len(tx_data) > g.max_tx_file_size:
from .exception import MaxFileSizeExceeded
raise MaxFileSizeExceeded(f'Transaction file size exceeds limit ({g.max_tx_file_size} bytes)')
die( 'MaxFileSizeExceeded', f'Transaction file size exceeds limit ({g.max_tx_file_size} bytes)' )
tx_data = tx_data.splitlines()
assert len(tx_data) >= 5,'number of lines less than 5'
assert len(tx_data[0]) == 6,'invalid length of first line'
@ -187,8 +186,7 @@ class MMGenTxFile(MMGenObject):
self.chksum = make_chksum_6(' '.join(lines))
fmt_data = '\n'.join([self.chksum] + lines) + '\n'
if len(fmt_data) > g.max_tx_file_size:
from .exception import MaxFileSizeExceeded
raise MaxFileSizeExceeded(f'Transaction file size exceeds limit ({g.max_tx_file_size} bytes)')
die( 'MaxFileSizeExceeded', f'Transaction file size exceeds limit ({g.max_tx_file_size} bytes)' )
return fmt_data
def write(self,

View file

@ -121,12 +121,17 @@ def mdie(*args):
sys.exit(0)
def die(ev,s='',stdout=False):
assert isinstance(ev,int)
from .exception import MMGenSystemExit,MMGenError
if ev <= 2:
raise MMGenSystemExit(ev,s,stdout)
if isinstance(ev,int):
from .exception import MMGenSystemExit,MMGenError
if ev <= 2:
raise MMGenSystemExit(ev,s,stdout)
else:
raise MMGenError(ev,s,stdout)
elif isinstance(ev,str):
import mmgen.exception
raise getattr(mmgen.exception,ev)(s)
else:
raise MMGenError(ev,s,stdout)
raise ValueError(f'{ev}: exit value must be string or int instance')
def die_wait(delay,ev=0,s=''):
assert isinstance(delay,int)
@ -460,8 +465,7 @@ def compare_or_die(val1, desc1, val2, desc2, e='Error'):
def check_wallet_extension(fn):
from .wallet import Wallet
if not Wallet.ext_to_type(get_extension(fn)):
from .exception import BadFileExtension
raise BadFileExtension(f'{fn!r}: unrecognized seed source file extension')
die( 'BadFileExtension', f'{fn!r}: unrecognized seed source file extension' )
def make_full_path(outdir,outfile):
return os.path.normpath(os.path.join(outdir, os.path.basename(outfile)))
@ -472,8 +476,7 @@ def confirm_or_raise(message,q,expect='YES',exit_msg='Exiting at user request'):
a = f'{q} ' if q[0].isupper() else f'Are you sure you want to {q}?\n'
b = f'Type uppercase {expect!r} to confirm: '
if line_input(a+b).strip() != expect:
from .exception import UserNonConfirmation
raise UserNonConfirmation(exit_msg)
die( 'UserNonConfirmation', exit_msg )
def get_words_from_user(prompt):
words = line_input(prompt, echo=opt.echo_passphrase).split()

View file

@ -521,7 +521,7 @@ class DieRollSeedFile(WalletUnenc):
rmap = bc.seedlen_map_rev
if not len(d) in rmap:
raise SeedLengthError('{!r}: invalid length for {} (must be one of {})'.format(
die( 'SeedLengthError', '{!r}: invalid length for {} (must be one of {})'.format(
len(d),
self.desc,
list(rmap) ))

View file

@ -54,17 +54,15 @@ class xmrseed(baseconv):
wl = self.digits
base = len(wl)
from .exception import MnemonicError
if not set(words) <= set(wl):
raise MnemonicError( f'{words!r}: not in {desc} format' )
die( 'MnemonicError', f'{words!r}: not in {desc} format' )
if len(words) not in self.seedlen_map_rev:
raise MnemonicError( f'{len(words)}: invalid seed phrase length for {desc}' )
die( 'MnemonicError', f'{len(words)}: invalid seed phrase length for {desc}' )
z = self.monero_mn_checksum(words[:-1])
if z != words[-1]:
raise MnemonicError(f'invalid {desc} checksum')
die( 'MnemonicError', f'invalid {desc} checksum' )
words = tuple(words[:-1])
@ -84,8 +82,7 @@ class xmrseed(baseconv):
base = len(wl)
if len(bytestr) not in self.seedlen_map:
from .exception import SeedLengthError
raise SeedLengthError(f'{len(bytestr)}: invalid seed byte length for {desc}')
die( 'SeedLengthError', f'{len(bytestr)}: invalid seed byte length for {desc}' )
def num2base_monero(num):
w1 = num % base

View file

@ -888,6 +888,6 @@ class MoneroWalletOps:
ymsg('Transaction not relayed')
return True
else:
raise RPCFailure(repr(res))
die( 'RPCFailure', repr(res) )
else:
die(1,'Exiting at user request')

View file

@ -112,7 +112,8 @@ def cleandir(d,do_msg=False):
def mk_tmpdir(d):
try: os.mkdir(d,0o755)
except OSError as e:
if e.errno != 17: raise
if e.errno != 17:
raise
else:
vmsg(f'Created directory {d!r}')
@ -149,7 +150,7 @@ def ok():
def cmp_or_die(s,t,desc=None):
if s != t:
raise TestSuiteFatalException(
die( 'TestSuiteFatalException',
(f'For {desc}:\n' if desc else '') +
f'ERROR: recoded data:\n{t!r}\ndiffers from original data:\n{s!r}'
)

View file

@ -85,7 +85,7 @@ def test_attr_perm(obj,attrname,perm_name,perm_value,dobj,attrval_type):
try:
so = sample_objs[attrval_type.__name__]
except:
raise SampleObjError(f'unable to find sample object of type {attrval_type.__name__!r}')
die( 'SampleObjError', f'unable to find sample object of type {attrval_type.__name__!r}' )
# ListItemAttr allows setting an attribute if its value is None
if type(dobj) == ListItemAttr and getattr(obj,attrname) == None:
setattr(obj,attrname,so)

View file

@ -460,7 +460,8 @@ def create_tmp_dirs(shm_dir):
try:
os.unlink(cfgs[cfg]['tmpdir'])
except OSError as e:
if e.errno != 2: raise
if e.errno != 2:
raise
finally:
os.symlink(src,cfgs[cfg]['tmpdir'])

View file

@ -28,7 +28,6 @@ from subprocess import run,PIPE,DEVNULL
from mmgen.globalvars import g
from mmgen.opts import opt
from mmgen.util import die
from mmgen.exception import *
from mmgen.amt import ETHAmt
from mmgen.protocol import CoinProtocol
from ..include.common import *

View file

@ -46,7 +46,7 @@ def make_brainwallet_file(fn):
def verify_checksum_or_exit(checksum,chk):
chk = strip_ansi_escapes(chk)
if checksum != chk:
raise TestSuiteFatalException(f'Checksum error: {chk}')
die( 'TestSuiteFatalException', f'Checksum error: {chk}' )
vmsg(green('Checksums match: ') + cyan(chk))
addrs_per_wallet = 8
@ -403,7 +403,7 @@ class TestSuiteMain(TestSuiteBase,TestSuiteShared):
ad.add(al)
aix = AddrIdxList(fmt_str=self.cfgs[s]['addr_idx_list'])
if len(aix) != addrs_per_wallet:
raise TestSuiteFatalException(f'Address index list length != {addrs_per_wallet}: {repr(aix)}')
die( 'TestSuiteFatalException', f'Address index list length != {addrs_per_wallet}: {repr(aix)}' )
tx_data[s] = {
'addrfile': afile,
'chk': al.chksum,
@ -497,7 +497,7 @@ class TestSuiteMain(TestSuiteBase,TestSuiteShared):
['-f',self.tx_fee,'-B'] + add_args + cmd_args + txdo_args)
if t.expect([('Get','Unsigned transac')[cmdline_inputs],r'Unable to connect to \S+'],regex=True) == 1:
raise TestSuiteException('\n'+t.p.after)
die( 'TestSuiteException', '\n'+t.p.after )
if cmdline_inputs:
t.written_to_file('tion')

View file

@ -58,8 +58,8 @@ class TestSuiteHelp(TestSuiteBase):
def usage(self):
t = self.spawn(f'mmgen-walletgen',['foo'])
t.expect('USAGE: mmgen-walletgen')
t.expect('MMGenSystemExit(1)')
t.expect('USAGE: mmgen-walletgen')
t.req_exit_val = 1
return t

View file

@ -778,7 +778,7 @@ def fork_cmd(cmd_name,args,out,opts,stdin_input):
vmsg(cp.stderr.strip().decode())
if cp.returncode != 0:
import re
m = re.match(b'tool command returned (None|False)'+NL.encode(),cp.stderr)
m = re.search(b'tool command returned (None|False)',cp.stdout)
if m:
return { b'None': None, b'False': False }[m.group(1)]
else:

View file

@ -4,7 +4,6 @@ test/unit_tests_d/ut_baseconv.py: Base conversion unit test for the MMGen suite
"""
from mmgen.common import *
from mmgen.exception import *
class unit_test(object):

View file

@ -4,7 +4,6 @@ test/unit_tests_d/ut_bip39: BIP39 unit test for the MMGen suite
"""
from mmgen.common import *
from mmgen.exception import *
class unit_test(object):

View file

@ -5,7 +5,6 @@ test/unit_tests_d/ut_daemon.py: unit test for the MMGen suite's Daemon class
from subprocess import run,DEVNULL
from mmgen.common import *
from mmgen.exception import *
from mmgen.daemon import *
from mmgen.protocol import init_proto

View file

@ -4,7 +4,6 @@ test/unit_tests_d/ut_flags.py: unit test for the MMGen suite's ClassFlags class
"""
from mmgen.common import *
from mmgen.exception import *
from mmgen.flags import *
class unit_test(object):

View file

@ -4,7 +4,6 @@ test/unit_tests_d/ut_lockable.py: unit test for the MMGen suite's Lockable class
"""
from mmgen.common import *
from mmgen.exception import *
class unit_test(object):

View file

@ -4,7 +4,6 @@ test.unit_tests_d.ut_rpc: RPC unit test for the MMGen suite
"""
from mmgen.common import *
from mmgen.exception import *
from mmgen.protocol import init_proto
from mmgen.rpc import rpc_init,MoneroWalletRPCClient

View file

@ -4,7 +4,6 @@ test/unit_tests_d/ut_xmrseed: Monero mnemonic unit test for the MMGen suite
"""
from mmgen.common import *
from mmgen.exception import *
class unit_test(object):