From ad569cbeb7fbf284eee9773d737210e476ee67c9 Mon Sep 17 00:00:00 2001 From: The MMGen Project Date: Sat, 22 Jan 2022 14:26:13 +0000 Subject: [PATCH] import cleanups throughout --- mmgen/amt.py | 2 -- mmgen/baseconv.py | 5 +++-- mmgen/bip39.py | 2 +- mmgen/daemon.py | 9 ++++++--- mmgen/fileutil.py | 3 ++- mmgen/main_autosign.py | 12 ++++------- mmgen/passwdlist.py | 2 +- mmgen/seedsplit.py | 4 +++- mmgen/subseed.py | 2 +- mmgen/tool.py | 13 ++++-------- mmgen/twaddrs.py | 2 +- mmgen/twctl.py | 3 ++- mmgen/twuo.py | 2 +- mmgen/tx.py | 45 ++++++++++++++++++++++++++++++++++++++---- mmgen/txfile.py | 4 ++-- mmgen/util.py | 43 ++++++++++++++++++++-------------------- mmgen/xmrseed.py | 5 ++++- test/objattrtest.py | 1 + 18 files changed, 98 insertions(+), 61 deletions(-) diff --git a/mmgen/amt.py b/mmgen/amt.py index 466fb6d5..882c9607 100755 --- a/mmgen/amt.py +++ b/mmgen/amt.py @@ -23,8 +23,6 @@ amt.py: MMGen CoinAmt and related classes from decimal import Decimal from .objmethods import Hilite,InitErrors -class UnknownCoinAmt(Decimal): pass - class DecimalNegateResult(Decimal): pass class CoinAmt(Decimal,Hilite,InitErrors): # abstract class diff --git a/mmgen/baseconv.py b/mmgen/baseconv.py index 67d2a9b4..157fa670 100755 --- a/mmgen/baseconv.py +++ b/mmgen/baseconv.py @@ -21,10 +21,11 @@ baseconv.py: base conversion class for the MMGen suite """ from hashlib import sha256 -from .exception import * -from .util import die from collections import namedtuple +from .exception import BaseConversionError,BaseConversionPadError,HexadecimalStringError,SeedLengthError +from .util import die + def is_b58_str(s): return set(list(s)) <= set(baseconv('b58').digits) diff --git a/mmgen/bip39.py b/mmgen/bip39.py index 0cb2fcb3..301247fd 100755 --- a/mmgen/bip39.py +++ b/mmgen/bip39.py @@ -22,7 +22,7 @@ bip39.py - Data and routines for BIP39 mnemonic seed phrases from hashlib import sha256 -from .exception import * +from .exception import MnemonicError from .baseconv import baseconv from .util import is_hex_str diff --git a/mmgen/daemon.py b/mmgen/daemon.py index 9d7b3982..62f9da37 100755 --- a/mmgen/daemon.py +++ b/mmgen/daemon.py @@ -20,11 +20,13 @@ daemon.py: Daemon control interface for the MMGen suite """ -import shutil +import os,shutil,time from subprocess import run,PIPE,CompletedProcess from collections import namedtuple -from .exception import * -from .common import * + +from .globalvars import g +from .opts import opt +from .util import msg,die,list_gen,get_subclasses from .flags import * _dd = namedtuple('daemon_data',['coind_name','coind_version','coind_version_str']) # latest tested version @@ -78,6 +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}]') if self.debug: print(cp) diff --git a/mmgen/fileutil.py b/mmgen/fileutil.py index da7b3d78..2efe8c98 100755 --- a/mmgen/fileutil.py +++ b/mmgen/fileutil.py @@ -23,7 +23,6 @@ fileutil.py: Routines that read, write, execute or stat files import sys,os from .globalvars import g -from .exception import FileNotFound,MaxInputSizeExceeded from .util import ( msg, qmsg, @@ -90,6 +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') for t in ok_types: @@ -289,6 +289,7 @@ 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') return data diff --git a/mmgen/main_autosign.py b/mmgen/main_autosign.py index 514a0fb1..f2037838 100755 --- a/mmgen/main_autosign.py +++ b/mmgen/main_autosign.py @@ -24,6 +24,8 @@ import sys,os,time,signal,shutil from subprocess import run,PIPE,DEVNULL from stat import * +from .common import * + mountpoint = '/mnt/tx' tx_dir = '/mnt/tx/tx' part_label = 'MMGEN_TX' @@ -35,10 +37,8 @@ mn_fmts = { } mn_fmt_dfl = 'mmgen' -from .common import * opts.UserOpts._set_ok += ('outdir','passwd_file') -prog_name = os.path.basename(sys.argv[0]) opts_data = { 'sets': [('stealth_led', True, 'led', True)], 'text': { @@ -118,7 +118,7 @@ This command is currently available only on Linux-based platforms. cmd_args = opts.init( opts_data, - add_opts = ['outdir','passwd_file'], # required, because in _set_ok + add_opts = ['outdir','passwd_file'], # in _set_ok, so must be set init_opts = { 'quiet': True, 'out_fmt': 'wallet', @@ -135,15 +135,12 @@ if opt.mnemonic_fmt: opt.mnemonic_fmt, fmt_list(mn_fmts,fmt='no_spc') )) -import mmgen.tx from .wallet import Wallet +from .tx import MMGenTX from .txsign import txsign from .protocol import init_proto from .rpc import rpc_init -if g.test_suite: - from .daemon import CoinDaemon - if opt.mountpoint: mountpoint = opt.mountpoint @@ -198,7 +195,6 @@ def do_umount(): run(['umount',mountpoint],check=True) async def sign_tx_file(txfile): - from .tx import MMGenTX try: tx1 = MMGenTX.Unsigned(filename=txfile) if tx1.proto.sign_mode == 'daemon': diff --git a/mmgen/passwdlist.py b/mmgen/passwdlist.py index 6de7b414..2f2a0d1c 100755 --- a/mmgen/passwdlist.py +++ b/mmgen/passwdlist.py @@ -22,7 +22,6 @@ passwdlist.py: Password list class for the MMGen suite from collections import namedtuple -from .exception import InvalidPasswdFormat from .util import ymsg,is_hex_str,is_int,keypress_confirm from .obj import ImmutableAttr,ListItemAttr,MMGenPWIDString from .baseconv import baseconv,is_b32_str,is_b58_str @@ -115,6 +114,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( '{!r}: invalid password format. Valid formats: {}'.format( self.pw_fmt, diff --git a/mmgen/seedsplit.py b/mmgen/seedsplit.py index 099f1c4b..bf64318b 100755 --- a/mmgen/seedsplit.py +++ b/mmgen/seedsplit.py @@ -22,7 +22,6 @@ seedsplit.py: Seed split classes and methods for the MMGen suite from .globalvars import g from .color import yellow -from .exception import RangeError from .obj import MMGenPWIDString,MMGenIdx from .subseed import * @@ -81,6 +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') def last_share_debug(last_share): @@ -109,6 +109,7 @@ class SeedShareList(SubSeedList): self.data['long'][ls.sid] = (count,nonce) break else: + from .exception import SubSeedNonceRangeExceeded raise SubSeedNonceRangeExceeded('nonce range exceeded') if g.debug_subseed: @@ -118,6 +119,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') elif idx == self.count: return self.last_share diff --git a/mmgen/subseed.py b/mmgen/subseed.py index 82a899a1..a04c24ed 100755 --- a/mmgen/subseed.py +++ b/mmgen/subseed.py @@ -22,7 +22,6 @@ subseed.py: Subseed classes and methods for the MMGen suite from .color import green from .util import msg_r,msg,qmsg -from .exception import SubSeedNonceRangeExceeded from .obj import MMGenRange,IndexedDict from .seed import * from .crypto import scramble_seed @@ -192,6 +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') for idx in SubSeedIdxRange(first_idx,last_idx).iterate(): diff --git a/mmgen/tool.py b/mmgen/tool.py index 1d50e3cf..b3892a06 100755 --- a/mmgen/tool.py +++ b/mmgen/tool.py @@ -20,19 +20,20 @@ tool.py: Routines for the 'mmgen-tool' utility """ -from .protocol import hash160 from .common import * +from .protocol import hash160 +from .fileutil import get_seed_file,get_data_from_file,write_data_to_file from .crypto import get_random from .key import PrivKey from .subseed import SubSeedList from .seedsplit import MasterShareIdx from .addr import * -from .addrlist import AddrList,KeyAddrList +from .addrlist import AddrList,KeyAddrList,AddrIdxList from .passwdlist import PasswordList from .baseconv import baseconv from .xmrseed import xmrseed from .bip39 import bip39 -from .fileutil import get_seed_file,get_data_from_file,write_data_to_file +from .tw import TwCommon NL = ('\n','\r\n')[g.platform=='win'] @@ -234,8 +235,6 @@ def _process_result(ret,pager=False,print_result=False): else: ydie(1,f'tool.py: can’t handle return value of type {type(ret).__name__!r}') -from .addr import MMGenAddrType - dfl_mnemonic_fmt = 'mmgen' mft = namedtuple('mnemonic_format',['fmt','pad','conv_cls']) mnemonic_fmts = { @@ -895,7 +894,6 @@ class MMGenToolCmdWallet(MMGenToolCmds): ss = Wallet(sf) if ss.seed.sid != addr.sid: die(1,f'Seed ID of requested address ({addr.sid}) does not match wallet ({ss.seed.sid})') - from .addrlist import AddrList,AddrIdxList al = AddrList( proto = self.proto, seed = ss.seed, @@ -905,8 +903,6 @@ class MMGenToolCmdWallet(MMGenToolCmds): ret = d.sec.wif if target=='wif' else d.addr return ret -from .tw import TwCommon - class MMGenToolCmdRPC(MMGenToolCmds): "tracking wallet commands using the JSON-RPC interface" @@ -964,7 +960,6 @@ class MMGenToolCmdRPC(MMGenToolCmds): die(1, f'{mmgen_addrs}: invalid address list argument ' + '(must be in form :[:])' ) - from .addrlist import AddrIdxList usr_addr_list = [MMGenID(self.proto,f'{a[0]}:{i}') for i in AddrIdxList(a[1])] from .twaddrs import TwAddrList diff --git a/mmgen/twaddrs.py b/mmgen/twaddrs.py index 80608cd1..81d44a2e 100755 --- a/mmgen/twaddrs.py +++ b/mmgen/twaddrs.py @@ -21,7 +21,6 @@ twaddrs: Tracking wallet listaddresses class for the MMGen suite """ from .color import green -from .exception import BadAgeFormat from .util import msg,die,altcoin_subclass from .base_obj import AsyncInit from .obj import MMGenList,MMGenDict,TwComment @@ -125,6 +124,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})') fs = '{mid}' + ('',' {addr}')[showbtcaddrs] + ' {cmt} {amt}' + ('',' {age}')[show_age] mmaddrs = [k for k in self.keys() if k.type == 'mmgen'] diff --git a/mmgen/twctl.py b/mmgen/twctl.py index 55ebf5bd..1b6dcb93 100755 --- a/mmgen/twctl.py +++ b/mmgen/twctl.py @@ -21,7 +21,6 @@ twctl: Tracking wallet control class for the MMGen suite """ from .globalvars import g -from .exception import WalletFileError from .util import msg,dmsg,write_mode,altcoin_subclass from .base_obj import AsyncInit from .objmethods import MMGenObject @@ -62,6 +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( 'Tracking wallet coin ({}) does not match current coin ({})!'.format( self.data['coin'], @@ -98,6 +98,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') else: self.upgrade_wallet_maybe() diff --git a/mmgen/twuo.py b/mmgen/twuo.py index bdd0a5e4..f74dfa95 100755 --- a/mmgen/twuo.py +++ b/mmgen/twuo.py @@ -25,7 +25,6 @@ from collections import namedtuple from .globalvars import g from .color import red,yellow,green -from .exception import BadAgeFormat from .util import ( msg, msg_r, @@ -129,6 +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})') self._age_fmt = val diff --git a/mmgen/tx.py b/mmgen/tx.py index 3763c333..f9774505 100755 --- a/mmgen/tx.py +++ b/mmgen/tx.py @@ -20,10 +20,45 @@ tx.py: Transaction routines for the MMGen suite """ -import sys,os,json -from stat import * -from .common import * -from .obj import * +import sys,time +from .globalvars import g +from .opts import opt +from .color import * +from .util import ( + msg, + ymsg, + dmsg, + vmsg, + qmsg, + msg_r, + die, + is_int, + fmt, + suf, + altcoin_subclass, + confirm_or_raise, + remove_dups, + get_extension, + keypress_confirm, + do_license_msg, + line_input, + make_chksum_6, + make_timestamp, + secs_to_dhms, +) +from .objmethods import MMGenObject +from .obj import ( + ImmutableAttr, + ListItemAttr, + MMGenList, + MMGenListItem, + MMGenTxLabel, + HexStr, + MMGenTxID, + MMGenDict, + CoinTxID, + get_obj, +) from .addr import MMGenID,CoinAddr,is_mmgen_id,is_coin_addr wmsg = lambda k: { @@ -148,6 +183,7 @@ class DeserializedTX(dict,MMGenObject): return int(vbytes[::-1].hex(),16) def make_txid(tx_bytes): + from hashlib import sha256 return sha256(sha256(tx_bytes).digest()).digest()[::-1].hex() self.idx = 0 @@ -1356,6 +1392,7 @@ class MMGenTX: 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""" Estimated transaction vsize is {ratio:1.2f} times the true vsize Your transaction fee estimates will be inaccurate diff --git a/mmgen/txfile.py b/mmgen/txfile.py index bbdb6a2b..3e6c87a4 100755 --- a/mmgen/txfile.py +++ b/mmgen/txfile.py @@ -23,8 +23,6 @@ txfile.py: Transaction file operations for the MMGen suite from .common import * from .obj import HexStr,MMGenTxID,CoinTxID,MMGenTxLabel from .tx import MMGenTxOutput,MMGenTxOutputList,MMGenTxInput,MMGenTxInputList -from .amt import UnknownCoinAmt -from .exception import MaxFileSizeExceeded class MMGenTxFile: @@ -63,6 +61,7 @@ class MMGenTxFile: 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)') tx_data = tx_data.splitlines() assert len(tx_data) >= 5,'number of lines less than 5' @@ -188,6 +187,7 @@ class MMGenTxFile: 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)') return fmt_data diff --git a/mmgen/util.py b/mmgen/util.py index 3852f76e..4da596df 100755 --- a/mmgen/util.py +++ b/mmgen/util.py @@ -25,8 +25,8 @@ from hashlib import sha256 from string import hexdigits,digits from .color import * -from .exception import BadFileExtension,UserNonConfirmation from .globalvars import g +from .opts import opt CUR_HIDE = '\033[?25l' CUR_SHOW = '\033[?25h' @@ -65,6 +65,24 @@ def gmsg(s): msg(green(s)) def gmsg_r(s): msg_r(green(s)) def bmsg(s): msg(blue(s)) def bmsg_r(s): msg_r(blue(s)) +def qmsg(s,alt=None): + if opt.quiet: + if alt != None: msg(alt) + else: msg(s) +def qmsg_r(s,alt=None): + if opt.quiet: + if alt != None: msg_r(alt) + else: msg_r(s) +def vmsg(s,force=False): + if opt.verbose or force: msg(s) +def vmsg_r(s,force=False): + if opt.verbose or force: msg_r(s) +def Vmsg(s,force=False): + if opt.verbose or force: Msg(s) +def Vmsg_r(s,force=False): + if opt.verbose or force: Msg_r(s) +def dmsg(s): + if opt.debug: msg(s) def mmsg(*args): for d in args: Msg(repr(d)) @@ -239,27 +257,6 @@ def parse_bytespec(nbytes): die(1,f'{nbytes!r}: invalid byte specifier') -from .opts import opt - -def qmsg(s,alt=None): - if opt.quiet: - if alt != None: msg(alt) - else: msg(s) -def qmsg_r(s,alt=None): - if opt.quiet: - if alt != None: msg_r(alt) - else: msg_r(s) -def vmsg(s,force=False): - if opt.verbose or force: msg(s) -def vmsg_r(s,force=False): - if opt.verbose or force: msg_r(s) -def Vmsg(s,force=False): - if opt.verbose or force: Msg(s) -def Vmsg_r(s,force=False): - if opt.verbose or force: Msg_r(s) -def dmsg(s): - if opt.debug: msg(s) - def suf(arg,suf_type='s',verb='none'): suf_types = { 'none': { @@ -449,6 +446,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') def make_full_path(outdir,outfile): @@ -460,6 +458,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) def get_words_from_user(prompt): diff --git a/mmgen/xmrseed.py b/mmgen/xmrseed.py index 220df8a5..fe070c33 100755 --- a/mmgen/xmrseed.py +++ b/mmgen/xmrseed.py @@ -20,7 +20,6 @@ xmrseed.py: Monero mnemonic conversion class for the MMGen suite """ -from .exception import * from .baseconv import baseconv from .util import die @@ -55,6 +54,8 @@ 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' ) @@ -64,6 +65,7 @@ class xmrseed(baseconv): z = self.monero_mn_checksum(words[:-1]) if z != words[-1]: raise MnemonicError(f'invalid {desc} checksum') + words = tuple(words[:-1]) def gen(): @@ -82,6 +84,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}') def num2base_monero(num): diff --git a/test/objattrtest.py b/test/objattrtest.py index d2bf1791..fca3de7a 100755 --- a/test/objattrtest.py +++ b/test/objattrtest.py @@ -31,6 +31,7 @@ os.environ['MMGEN_TEST_SUITE'] = '1' # Import these _after_ local path's been added to sys.path from test.objattrtest_py_d.oat_common import * +from mmgen.common import * from mmgen.addrlist import * from mmgen.passwdlist import *