diff --git a/examples/halving-calculator.py b/examples/halving-calculator.py index cef312cb..d366a749 100755 --- a/examples/halving-calculator.py +++ b/examples/halving-calculator.py @@ -15,10 +15,9 @@ examples.halving-calculator.py: Demonstrate use of the MMGen asyncio/aiohttp JSO import time import mmgen.opts as opts -from mmgen.opts import opt from mmgen.util import async_run -opts.init({ +cfg = opts.init({ 'text': { 'desc': 'Estimate date of next block subsidy halving', 'usage':'[opts]', @@ -55,16 +54,15 @@ def time_diff_warning(t_diff): async def main(): - from mmgen.protocol import init_proto_from_opts - proto = init_proto_from_opts(need_amt=True) + proto = cfg._proto from mmgen.rpc import rpc_init - c = await rpc_init(proto) + c = await rpc_init(cfg,proto) tip = await c.call('getblockcount') assert tip > 1, 'block tip must be > 1' remaining = proto.halving_interval - tip % proto.halving_interval - sample_size = int(opt.sample_size) if opt.sample_size else min(tip-1,max(remaining,144)) + sample_size = int(cfg.sample_size) if cfg.sample_size else min(tip-1,max(remaining,144)) # aiohttp backend will perform these two calls concurrently: cur,old = await c.gathered_call('getblockstats',((tip,),(tip - sample_size,))) diff --git a/mmgen/addrdata.py b/mmgen/addrdata.py index b302b40e..fcd4b355 100755 --- a/mmgen/addrdata.py +++ b/mmgen/addrdata.py @@ -20,7 +20,8 @@ addrdata: MMGen AddrData and related classes """ -from .util import vmsg,fmt,die +from .globalvars import gc +from .util import fmt,die from .base_obj import AsyncInit from .obj import MMGenObject,MMGenDict,get_obj from .addr import MMGenID,AddrListID @@ -68,16 +69,16 @@ class AddrData(MMGenObject): class TwAddrData(AddrData,metaclass=AsyncInit): - def __new__(cls,proto,*args,**kwargs): + def __new__(cls,cfg,proto,*args,**kwargs): return MMGenObject.__new__(proto.base_proto_subclass(cls,'addrdata')) - async def __init__(self,proto,twctl=None): + async def __init__(self,cfg,proto,twctl=None): from .rpc import rpc_init from .tw.shared import TwLabel - from .globalvars import gc from .seed import SeedID + self.cfg = cfg self.proto = proto - self.rpc = await rpc_init(proto) + self.rpc = await rpc_init(cfg,proto) self.al_ids = {} twd = await self.get_tw_data(twctl) out,i = {},0 @@ -96,10 +97,11 @@ class TwAddrData(AddrData,metaclass=AsyncInit): out[al_id].append(AddrListEntry(self.proto,idx=obj.idx,addr=addr_array[0],comment=l.comment)) i += 1 - vmsg(f'{i} {gc.proj_name} addresses found, {len(twd)} accounts total') + self.cfg._util.vmsg(f'{i} {gc.proj_name} addresses found, {len(twd)} accounts total') for al_id in out: self.add(AddrList( + self.cfg, self.proto, al_id = al_id, adata = AddrListData(sorted( out[al_id], key=lambda a: a.idx )) diff --git a/mmgen/addrfile.py b/mmgen/addrfile.py index 83544ee9..10abba07 100755 --- a/mmgen/addrfile.py +++ b/mmgen/addrfile.py @@ -20,8 +20,8 @@ addrfile: Address and password file classes for the MMGen suite """ -from .globalvars import g -from .util import msg,qmsg,qmsg_r,die,capfirst +from .globalvars import gc +from .util import msg,die,capfirst from .protocol import init_proto from .obj import MMGenObject,TwComment,WalletPassword,MMGenPWIDString from .seed import SeedID,is_seed_id @@ -44,13 +44,13 @@ class AddrFile(MMGenObject): """ def __init__(self,parent): - self.parent = parent + self.cfg = parent.cfg self.infile = None def encrypt(self): from .crypto import Crypto - self.fmt_data = Crypto().mmgen_encrypt( + self.fmt_data = Crypto(self.cfg).mmgen_encrypt( data = self.fmt_data.encode(), desc = f'new {self.parent.desc} list' ) self.ext += f'.{Crypto.mmenc_ext}' @@ -63,13 +63,13 @@ class AddrFile(MMGenObject): self.ext ) def write(self,fn=None,ask_tty=True,ask_write_default_yes=False,binary=False,desc=None): - from .opts import opt from .fileutil import write_data_to_file write_data_to_file( + self.cfg, fn or self.filename, self.fmt_data, desc or self.desc, - ask_tty = self.parent.has_keys and not opt.quiet, + ask_tty = self.parent.has_keys and not self.cfg.quiet, binary = binary ) def make_label(self): @@ -87,8 +87,7 @@ class AddrFile(MMGenObject): self.file_header_mn.format(p.pw_fmt.upper()) if p.gen_passwds and p.pw_fmt in ('bip39','xmrseed') else self.file_header ).strip() - from .globalvars import gc - out = [fh.format(pnm=gc.proj_name,n=TwComment.max_screen_width) + '\n'] + out = [fh.format( pnm=gc.proj_name, n=TwComment.max_screen_width ) + '\n'] if p.chksum: out.append(f'# {capfirst(p.desc)} data checksum for {p.id_str}: {p.chksum}') @@ -108,8 +107,7 @@ class AddrFile(MMGenObject): else: # First line with idx out.append(fs.format(e.idx,e.addr,c)) if p.has_keys: - from .opts import opt - if opt.b16: + if self.cfg.b16: out.append(fs.format( '', f'orig_hex: {e.sec.orig_bytes.hex()}', c )) out.append(fs.format( '', f'{p.al_id.mmtype.wif_label}: {e.sec.wif}', c )) for k in ('viewkey','wallet_passwd'): @@ -160,21 +158,21 @@ class AddrFile(MMGenObject): def verify_keys(): from .addrgen import KeyGenerator,AddrGenerator - kg = KeyGenerator(p.proto,p.al_id.mmtype.pubkey_type) - ag = AddrGenerator(p.proto,p.al_id.mmtype) + kg = KeyGenerator( self.cfg, p.proto, p.al_id.mmtype.pubkey_type ) + ag = AddrGenerator( self.cfg, p.proto, p.al_id.mmtype ) llen = len(ret) + qmsg_r = p.cfg._util.qmsg_r for n,e in enumerate(ret): qmsg_r(f'\rVerifying keys {n+1}/{llen}') assert e.addr == ag.to_addr(kg.gen_data(e.sec)),( f'Key doesn’t match address!\n {e.sec.wif}\n {e.addr}') - qmsg(' - done') + p.cfg._util.qmsg(' - done') - from .opts import opt - if opt.yes or p.ka_validity_chk == True: + if self.cfg.yes or p.ka_validity_chk == True: verify_keys() else: from .ui import keypress_confirm - if keypress_confirm('Check key-to-address validity?'): + if keypress_confirm( p.cfg, 'Check key-to-address validity?' ): verify_keys() return ret @@ -216,7 +214,7 @@ class AddrFile(MMGenObject): else: # only component is coin coin,mmtype_key = ( lbl, None ) - proto = init_proto(coin=coin,network=network) + proto = init_proto( p.cfg, coin=coin, network=network ) if mmtype_key == None: mmtype_key = proto.mmtypes[0] @@ -224,8 +222,9 @@ class AddrFile(MMGenObject): return ( proto, proto.addr_type(mmtype_key) ) p = self.parent + from .fileutil import get_lines_from_file - lines = get_lines_from_file(fn,p.desc+' data',trim_comments=True) + lines = get_lines_from_file( p.cfg, fn, p.desc+' data', trim_comments=True ) try: assert len(lines) >= 3, f'Too few lines in address file ({len(lines)})' @@ -246,12 +245,12 @@ class AddrFile(MMGenObject): modname,funcname = p.pw_info[p.pw_fmt].chk_func.split('.') import importlib p.chk_func = getattr(importlib.import_module('mmgen.'+modname),funcname) - proto = init_proto('btc') # FIXME: dummy protocol + proto = init_proto( p.cfg, 'btc' ) # FIXME: dummy protocol mmtype = MMGenPasswordType(proto,'P') elif len(ls) == 1: proto,mmtype = parse_addrfile_label(ls[0]) elif len(ls) == 0: - proto = init_proto('btc') + proto = init_proto( p.cfg, 'btc' ) mmtype = proto.addr_type('L') else: raise ValueError(f'{lines[0]}: Invalid first line for {p.gen_desc} file {fn!r}') diff --git a/mmgen/addrgen.py b/mmgen/addrgen.py index db8fd606..5a212bf2 100755 --- a/mmgen/addrgen.py +++ b/mmgen/addrgen.py @@ -36,7 +36,7 @@ class addr_generator: class base: - def __init__(self,proto,addr_type): + def __init__(self,cfg,proto,addr_type): self.proto = proto self.pubkey_type = addr_type.pubkey_type self.compressed = addr_type.compressed @@ -44,12 +44,12 @@ class addr_generator: class keccak(base): - def __init__(self,proto,addr_type): - super().__init__(proto,addr_type) + def __init__(self,cfg,proto,addr_type): + super().__init__(cfg,proto,addr_type) from .util2 import get_keccak - self.keccak_256 = get_keccak() + self.keccak_256 = get_keccak(cfg) -def AddrGenerator(proto,addr_type): +def AddrGenerator(cfg,proto,addr_type): """ factory function returning an address generator for the specified address type """ @@ -76,4 +76,4 @@ def AddrGenerator(proto,addr_type): import importlib return getattr( importlib.import_module(f'mmgen.proto.{package_map[addr_type.name]}.addrgen'), - addr_type.name )(proto,addr_type) + addr_type.name )(cfg,proto,addr_type) diff --git a/mmgen/addrlist.py b/mmgen/addrlist.py index b09eab3b..a6a6d5ff 100755 --- a/mmgen/addrlist.py +++ b/mmgen/addrlist.py @@ -20,8 +20,7 @@ addrlist: Address list classes for the MMGen suite """ -from .globalvars import g -from .util import qmsg,qmsg_r,suf,make_chksum_N,Msg,die +from .util import suf,make_chksum_N,Msg,die from .objmethods import MMGenObject,Hilite,InitErrors from .obj import MMGenListItem,ListItemAttr,MMGenDict,TwComment,WalletPassword from .key import PrivKey @@ -158,6 +157,7 @@ class AddrList(MMGenObject): # Address info for a single seed ID def __init__( self, + cfg, proto, addrfile = '', al_id = '', @@ -173,12 +173,13 @@ class AddrList(MMGenObject): # Address info for a single seed ID skip_chksum_msg = False, add_p2pkh = False ): + self.cfg = cfg self.ka_validity_chk = key_address_validity_check self.add_p2pkh = add_p2pkh self.proto = proto do_chksum = False - if not g.debug_addrlist: + if not cfg.debug_addrlist: self.dmsg_sc = self.noop if seed and addr_idxs: # data from seed + idxs @@ -230,7 +231,7 @@ class AddrList(MMGenObject): # Address info for a single seed ID def do_chksum_msg(self,record): chk = 'Check this value against your records' rec = f'Record this checksum: it will be used to verify the {self.desc} file in the future' - qmsg( + self.cfg._util.qmsg( f'Checksum for {self.desc} data {self.id_str.hl()}: {self.chksum.hl()}\n' + (chk,rec)[record] ) @@ -246,23 +247,23 @@ class AddrList(MMGenObject): # Address info for a single seed ID if self.gen_addrs: from .addrgen import KeyGenerator,AddrGenerator - kg = KeyGenerator( self.proto, mmtype.pubkey_type ) - ag = AddrGenerator( self.proto, mmtype ) + kg = KeyGenerator( self.cfg, self.proto, mmtype.pubkey_type ) + ag = AddrGenerator( self.cfg, self.proto, mmtype ) if self.add_p2pkh: - ag2 = AddrGenerator( self.proto, 'compressed' ) + ag2 = AddrGenerator( self.cfg, self.proto, 'compressed' ) - from .globalvars import g from .derive import derive_coin_privkey_bytes t_addrs = len(addr_idxs) le = self.entry_type out = AddrListData() - CR = '\n' if g.debug_addrlist else '\r' + CR = '\n' if self.cfg.debug_addrlist else '\r' for pk_bytes in derive_coin_privkey_bytes(seed,addr_idxs): - if not g.debug: - qmsg_r(f'{CR}Generating {self.gen_desc} #{pk_bytes.idx} ({pk_bytes.pos} of {t_addrs})') + if not self.cfg.debug: + self.cfg._util.qmsg_r( + f'{CR}Generating {self.gen_desc} #{pk_bytes.idx} ({pk_bytes.pos} of {t_addrs})' ) e = le( proto=self.proto, idx=pk_bytes.idx ) @@ -286,7 +287,7 @@ class AddrList(MMGenObject): # Address info for a single seed ID out.append(e) - qmsg('{}{}: {} {}{} generated{}'.format( + self.cfg._util.qmsg('{}{}: {} {}{} generated{}'.format( CR, self.al_id.hl(), t_addrs, @@ -316,7 +317,7 @@ class AddrList(MMGenObject): # Address info for a single seed ID if self.proto.testnet: scramble_key += ':' + self.proto.network self.dmsg_sc('str',scramble_key) - return Crypto().scramble_seed(seed,scramble_key.encode()) + return Crypto(self.cfg).scramble_seed(seed,scramble_key.encode()) def idxs(self): return [e.idx for e in self.data] @@ -371,8 +372,8 @@ class AddrList(MMGenObject): # Address info for a single seed ID def gen_addr(pk,t): at = self.proto.addr_type(t) from .addrgen import KeyGenerator,AddrGenerator - kg = KeyGenerator(self.proto,at.pubkey_type) - ag = AddrGenerator(self.proto,at) + kg = KeyGenerator( self.cfg, self.proto, at.pubkey_type ) + ag = AddrGenerator( self.cfg, self.proto, at ) return ag.to_addr(kg.gen_data(pk)) compressed_types = set(self.proto.mmtypes) - {'L','E'} diff --git a/mmgen/altcoin.py b/mmgen/altcoin.py index b06828fd..0fa53c91 100755 --- a/mmgen/altcoin.py +++ b/mmgen/altcoin.py @@ -434,14 +434,14 @@ class CoinInfo(object): test_equal('P2SH leading symbol',vn_info[1],ret,*cdata) @classmethod - def verify_core_coin_data(cls,quiet=False,verbose=False): + def verify_core_coin_data(cls,cfg,quiet=False,verbose=False): from .protocol import CoinProtocol,init_proto for network in ('mainnet','testnet'): for coin in gc.core_coins: e = cls.get_entry(coin,network) if e: - proto = init_proto(coin,testnet=network=='testnet') + proto = init_proto( cfg, coin, network=network ) cdata = (network,coin,e,type(proto).__name__,verbose) if not quiet: msg(f'Verifying {coin.upper()} {network}') @@ -791,11 +791,11 @@ if __name__ == '__main__': } } - from mmgen.opts import init,opt - init( opts_data ) + from mmgen.opts import init + cfg = init( opts_data, need_amt=False ) msg('Checking CoinInfo WIF/P2PKH/P2SH version numbers and trust levels against protocol.py') - CoinInfo.verify_core_coin_data( quiet=opt.quiet, verbose=opt.verbose ) + CoinInfo.verify_core_coin_data( cfg, cfg.quiet, cfg.verbose ) msg('Checking CoinInfo address leading symbols') - CoinInfo.verify_leading_symbols( quiet=opt.quiet, verbose=opt.verbose ) + CoinInfo.verify_leading_symbols( cfg.quiet, cfg.verbose ) diff --git a/mmgen/baseconv.py b/mmgen/baseconv.py index 7fca0ba1..a9e53db5 100755 --- a/mmgen/baseconv.py +++ b/mmgen/baseconv.py @@ -100,14 +100,13 @@ class baseconv(object): from hashlib import sha256 return sha256( ' '.join(self.digits).encode() ).hexdigest()[:8] - def check_wordlist(self): + def check_wordlist(self,cfg): wl = self.digits - from .util import qmsg,compare_chksums ret = f'Wordlist: {self.wl_id}\nLength: {len(wl)} words' new_chksum = self.get_wordlist_chksum() - compare_chksums( new_chksum, 'generated', self.wl_chksum, 'saved', die_on_fail=True ) + cfg._util.compare_chksums( new_chksum, 'generated', self.wl_chksum, 'saved', die_on_fail=True ) if tuple(sorted(wl)) == wl: return ret + '\nList is sorted' diff --git a/mmgen/cfgfile.py b/mmgen/cfgfile.py index 154fafdd..cab78416 100755 --- a/mmgen/cfgfile.py +++ b/mmgen/cfgfile.py @@ -23,11 +23,10 @@ cfgfile: API for the MMGen runtime configuration file and related files import os,re from collections import namedtuple -from .globalvars import g,gc from .util import msg,ymsg,suf,fmt,fmt_list,oneshot_warning,strip_comment,capfirst -def mmgen_cfg_file(id_str): - return cfg_file.get_cls_by_id(id_str)() +def mmgen_cfg_file(cfg,id_str): + return cfg_file.get_cls_by_id(id_str)(cfg) class cfg_file: cur_ver = 2 @@ -53,7 +52,7 @@ class cfg_file: def copy_system_data(self,fn): assert self.write_ok, f'writing to file {fn!r} not allowed!' - src = mmgen_cfg_file('sys') + src = mmgen_cfg_file(self.cfg,'sys') if src.data: data = src.data + src.make_metadata() if self.write_metadata else src.data try: @@ -169,8 +168,9 @@ class CfgFileUsr(cfg_file): warn_missing = False write_ok = True - def __init__(self): - self.fn = os.path.join(g.data_dir_root,self.fn_base) + def __init__(self,cfg): + self.cfg = cfg + self.fn = os.path.join(cfg.data_dir_root,self.fn_base) self.data = self.get_data(self.fn) if not self.data: self.copy_system_data(self.fn) @@ -179,14 +179,16 @@ class CfgFileSampleSys(cfg_file_sample): desc = 'system sample configuration file' test_fn_subdir = 'usr.local.share' - def __init__(self): - if g.test_suite_cfgtest: - self.fn = os.path.join(g.data_dir_root,self.test_fn_subdir,self.fn_base) + def __init__(self,cfg): + self.cfg = cfg + if self.cfg.test_suite_cfgtest: + self.fn = os.path.join(cfg.data_dir_root,self.test_fn_subdir,self.fn_base) with open(self.fn) as fp: self.data = fp.read().splitlines() else: # self.fn is used for error msgs only, so file need not exist on filesystem self.fn = os.path.join(os.path.dirname(__file__),'data',self.fn_base) + from .globalvars import gc self.data = gc.get_mmgen_data_file(self.fn_base).splitlines() def make_metadata(self): @@ -202,11 +204,12 @@ class CfgFileSampleUsr(cfg_file_sample): out_of_date_fs = 'File {!r} is out of date - replacing' altered_by_user_fs = 'File {!r} was altered by user - replacing' - def __init__(self): - self.fn = os.path.join(g.data_dir_root,f'{self.fn_base}.sample') + def __init__(self,cfg): + self.cfg = cfg + self.fn = os.path.join(cfg.data_dir_root,f'{self.fn_base}.sample') self.data = self.get_data(self.fn) - src = mmgen_cfg_file('sys') + src = mmgen_cfg_file(cfg,'sys') if not src.data: return @@ -255,7 +258,7 @@ class CfgFileSampleUsr(cfg_file_sample): opts = fmt_list([i.name for i in data],fmt='bare') msg(f' The following option{suf(data,verb="has")} been {desc}:\n {opts}\n') if desc == 'removed' and data: - uc = mmgen_cfg_file('usr') + uc = mmgen_cfg_file(self.cfg,'usr') usr_names = [i.name for i in uc.get_lines()] rm_names = [i.name for i in data] bad = sorted(set(usr_names).intersection(rm_names)) @@ -269,7 +272,7 @@ class CfgFileSampleUsr(cfg_file_sample): from .ui import keypress_confirm,do_pager while True: - if not keypress_confirm(self.details_confirm_prompt,no_nl=True): + if not keypress_confirm( self.cfg, self.details_confirm_prompt, no_nl=True ): return def get_details(): diff --git a/mmgen/common.py b/mmgen/common.py index a18e8b64..fc4f1190 100755 --- a/mmgen/common.py +++ b/mmgen/common.py @@ -23,5 +23,4 @@ common: Common imports for all MMGen scripts import sys,os from .globalvars import * import mmgen.opts as opts -from .opts import opt from .util import * diff --git a/mmgen/crypto.py b/mmgen/crypto.py index b9fef3f0..35ddf54e 100755 --- a/mmgen/crypto.py +++ b/mmgen/crypto.py @@ -23,23 +23,16 @@ crypto: Random number, password hashing and symmetric encryption routines for th import os from collections import namedtuple -from .globalvars import g,gc -from .opts import opt +from .globalvars import gc from .util import ( msg, msg_r, - dmsg, - vmsg, - vmsg_r, - qmsg, fmt, die, make_chksum_8, - compare_chksums, oneshot_warning, ) - class Crypto: mmenc_ext = 'mmenc' @@ -72,6 +65,10 @@ class Crypto: def __init__(self,fn): oneshot_warning.__init__(self,div=fn,fmt_args=[fn],reverse=True) + def __init__(self,cfg): + self.cfg = cfg + self.util = cfg._util + def get_hash_params(self,hash_preset): if hash_preset in self.hash_presets: return self.hash_presets[hash_preset] # N,r,p @@ -87,7 +84,7 @@ class Crypto: def scramble_seed(self,seed,scramble_key): import hmac step1 = hmac.digest(seed,scramble_key,'sha256') - if g.debug: + if self.cfg.debug: msg(f'Seed: {seed.hex()!r}\nScramble key: {scramble_key}\nScrambled seed: {step1.hex()}\n') return self.sha256_rounds(step1) @@ -95,56 +92,56 @@ class Crypto: return self.encrypt_data(data,key,desc=desc) def decrypt_seed(self,enc_seed,key,seed_id,key_id): - vmsg_r('Checking key...') + self.util.vmsg_r('Checking key...') chk1 = make_chksum_8(key) if key_id: - if not compare_chksums(key_id,'key ID',chk1,'computed'): + if not self.util.compare_chksums(key_id,'key ID',chk1,'computed'): msg('Incorrect passphrase or hash preset') return False dec_seed = self.decrypt_data(enc_seed,key,desc='seed') chk2 = make_chksum_8(dec_seed) if seed_id: - if compare_chksums(seed_id,'Seed ID',chk2,'decrypted seed'): - qmsg('Passphrase is OK') + if self.util.compare_chksums(seed_id,'Seed ID',chk2,'decrypted seed'): + self.util.qmsg('Passphrase is OK') else: - if not opt.debug: + if not self.cfg.debug: msg_r('Checking key ID...') - if compare_chksums(key_id,'key ID',chk1,'computed'): + if self.util.compare_chksums(key_id,'key ID',chk1,'computed'): msg('Key ID is correct but decryption of seed failed') else: msg('Incorrect passphrase or hash preset') - vmsg('') + self.util.vmsg('') return False - dmsg(f'Decrypted seed: {dec_seed.hex()}') + self.util.dmsg(f'Decrypted seed: {dec_seed.hex()}') return dec_seed def encrypt_data(self,data,key,iv=aesctr_dfl_iv,desc='data',verify=True,silent=False): from cryptography.hazmat.primitives.ciphers import Cipher,algorithms,modes from cryptography.hazmat.backends import default_backend if not silent: - vmsg(f'Encrypting {desc}') + self.util.vmsg(f'Encrypting {desc}') c = Cipher(algorithms.AES(key),modes.CTR(iv),backend=default_backend()) encryptor = c.encryptor() enc_data = encryptor.update(data) + encryptor.finalize() if verify: - vmsg_r(f'Performing a test decryption of the {desc}...') + self.util.vmsg_r(f'Performing a test decryption of the {desc}...') c = Cipher(algorithms.AES(key),modes.CTR(iv),backend=default_backend()) encryptor = c.encryptor() dec_data = encryptor.update(enc_data) + encryptor.finalize() if dec_data != data: die(2,f'ERROR.\nDecrypted {desc} doesn’t match original {desc}') if not silent: - vmsg('done') + self.util.vmsg('done') return enc_data def decrypt_data(self,enc_data,key,iv=aesctr_dfl_iv,desc='data'): from cryptography.hazmat.primitives.ciphers import Cipher,algorithms,modes from cryptography.hazmat.backends import default_backend - vmsg_r(f'Decrypting {desc} with key...') + self.util.vmsg_r(f'Decrypting {desc} with key...') c = Cipher(algorithms.AES(key),modes.CTR(iv),backend=default_backend()) encryptor = c.encryptor() return encryptor.update(enc_data) + encryptor.finalize() @@ -184,7 +181,7 @@ class Crypto: # hashlib.scrypt doesn't support N > 14 (hash preset > 3) ret = ( - do_standalone_scrypt() if ps.N > 14 or g.force_standalone_scrypt_module else + do_standalone_scrypt() if ps.N > 14 or self.cfg.force_standalone_scrypt_module else do_hashlib_scrypt() ) if int(hash_preset) > 3: @@ -193,17 +190,17 @@ class Crypto: return ret def make_key(self,passwd,salt,hash_preset,desc='encryption key',from_what='passphrase',verbose=False): - if opt.verbose or verbose: + if self.cfg.verbose or verbose: msg_r(f"Generating {desc}{' from ' + from_what if from_what else ''}...") key = self.scrypt_hash_passphrase(passwd,salt,hash_preset) - if opt.verbose or verbose: msg('done') - dmsg(f'Key: {key.hex()}') + if self.cfg.verbose or verbose: msg('done') + self.util.dmsg(f'Key: {key.hex()}') return key def _get_random_data_from_user(self,uchars=None,desc='data'): if uchars is None: - uchars = opt.usr_randchars + uchars = self.cfg.usr_randchars info1 = f""" Now we're going to gather some additional input from the keyboard to further @@ -225,7 +222,7 @@ class Crypto: on the screen. """ - msg(f'Enter {uchars} random symbols' if opt.quiet else + msg(f'Enter {uchars} random symbols' if self.cfg.quiet else '\n' + fmt(info1,indent=' ') + '\n' + fmt(info2) ) @@ -238,7 +235,7 @@ class Crypto: key_data += get_char_raw(f'\rYou may begin typing. {uchars-i} symbols left: ') time_data.append(time.time()) - msg_r( '\r' if opt.quiet else f'\rThank you. That’s enough.{" "*18}\n\n' ) + msg_r( '\r' if self.cfg.quiet else f'\rThank you. That’s enough.{" "*18}\n\n' ) time_data = [f'{t:.22f}'.rstrip('0') for t in time_data] @@ -249,11 +246,11 @@ class Crypto: ret = key_data + '\n' + '\n'.join(time_data) - if g.debug: + if self.cfg.debug: msg(f'USER ENTROPY (user input + keystroke timings):\n{ret}') from .ui import line_input - line_input( 'User random data successfully acquired. Press ENTER to continue: ' ) + line_input( self.cfg, 'User random data successfully acquired. Press ENTER to continue: ' ) return ret.encode() @@ -274,7 +271,7 @@ class Crypto: assert type(rand_bytes) == bytes, 'add_user_random_chk1' - if opt.usr_randchars: + if self.cfg.usr_randchars: if not urand['data']: from hashlib import sha256 @@ -310,7 +307,7 @@ class Crypto: from .ui import line_input while True: - ret = line_input( prompt ) + ret = line_input( self.cfg, prompt ) if ret: if ret in self.hash_presets: return ret @@ -329,29 +326,30 @@ class Crypto: if passwd_file: from .fileutil import get_words_from_file pw = ' '.join(get_words_from_file( + cfg = self.cfg, infile = passwd_file, desc = f'{pw_desc} for {data_desc}', quiet = self.pwfile_reuse_warning(passwd_file).warning_shown )) else: - qmsg('\n'+fmt(message,indent=' ')) + self.util.qmsg('\n'+fmt(message,indent=' ')) from .ui import get_words_from_user - if opt.echo_passphrase: - pw = ' '.join(get_words_from_user( f'Enter {pw_desc} for {data_desc}: ' )) + if self.cfg.echo_passphrase: + pw = ' '.join(get_words_from_user( self.cfg, f'Enter {pw_desc} for {data_desc}: ' )) else: for i in range(gc.passwd_max_tries): - pw = ' '.join(get_words_from_user( f'Enter {pw_desc} for {data_desc}: ' )) - pw_chk = ' '.join(get_words_from_user( f'Repeat {pw_desc}: ' )) - dmsg(f'Passphrases: [{pw}] [{pw_chk}]') + pw = ' '.join(get_words_from_user( self.cfg, f'Enter {pw_desc} for {data_desc}: ' )) + pw_chk = ' '.join(get_words_from_user( self.cfg, f'Repeat {pw_desc}: ' )) + self.util.dmsg(f'Passphrases: [{pw}] [{pw_chk}]') if pw == pw_chk: - vmsg('Passphrases match') + self.util.vmsg('Passphrases match') break else: msg('Passphrases do not match. Try again.') else: - die(2,f'User failed to duplicate passphrase in {g.passwd_max_tries} attempts') + die(2,f'User failed to duplicate passphrase in {gc.passwd_max_tries} attempts') if pw == '': - qmsg('WARNING: Empty passphrase') + self.util.qmsg('WARNING: Empty passphrase') return pw @@ -359,48 +357,49 @@ class Crypto: if passwd_file: from .fileutil import get_words_from_file return ' '.join(get_words_from_file( + cfg = self.cfg, infile = passwd_file, desc = f'{pw_desc} for {data_desc}', quiet = self.pwfile_reuse_warning(passwd_file).warning_shown )) else: from .ui import get_words_from_user - return ' '.join(get_words_from_user( f'Enter {pw_desc} for {data_desc}: ' )) + return ' '.join(get_words_from_user( self.cfg, f'Enter {pw_desc} for {data_desc}: ' )) def mmgen_encrypt(self,data,desc='data',hash_preset=None): salt = self.get_random(self.mmenc_salt_len) iv = self.get_random(self.aesctr_iv_len) nonce = self.get_random(self.mmenc_nonce_len) - hp = hash_preset or opt.hash_preset or self.get_hash_preset_from_user(data_desc=desc) + hp = hash_preset or self.cfg.hash_preset or self.get_hash_preset_from_user(data_desc=desc) m = ('user-requested','default')[hp=='3'] - vmsg(f'Encrypting {desc}') - qmsg(f'Using {m} hash preset of {hp!r}') + self.util.vmsg(f'Encrypting {desc}') + self.util.qmsg(f'Using {m} hash preset of {hp!r}') passwd = self.get_new_passphrase( data_desc = desc, hash_preset = hp, - passwd_file = opt.passwd_file ) + passwd_file = self.cfg.passwd_file ) key = self.make_key(passwd,salt,hp) from hashlib import sha256 enc_d = self.encrypt_data( sha256(nonce+data).digest() + nonce + data, key, iv, desc=desc ) return salt+iv+enc_d def mmgen_decrypt(self,data,desc='data',hash_preset=None): - vmsg(f'Preparing to decrypt {desc}') + self.util.vmsg(f'Preparing to decrypt {desc}') dstart = self.mmenc_salt_len + self.aesctr_iv_len salt = data[:self.mmenc_salt_len] iv = data[self.mmenc_salt_len:dstart] enc_d = data[dstart:] - hp = hash_preset or opt.hash_preset or self.get_hash_preset_from_user(data_desc=desc) + hp = hash_preset or self.cfg.hash_preset or self.get_hash_preset_from_user(data_desc=desc) m = ('user-requested','default')[hp=='3'] - qmsg(f'Using {m} hash preset of {hp!r}') + self.util.qmsg(f'Using {m} hash preset of {hp!r}') passwd = self.get_passphrase( data_desc = desc, - passwd_file = opt.passwd_file ) + passwd_file = self.cfg.passwd_file ) key = self.make_key(passwd,salt,hp) dec_d = self.decrypt_data( enc_d, key, iv, desc ) sha256_len = 32 from hashlib import sha256 if dec_d[:sha256_len] == sha256(dec_d[sha256_len:]).digest(): - vmsg('OK') + self.util.vmsg('OK') return dec_d[sha256_len+self.mmenc_nonce_len:] else: msg('Incorrect passphrase or hash preset') diff --git a/mmgen/daemon.py b/mmgen/daemon.py index 999252d9..9aaf65bb 100755 --- a/mmgen/daemon.py +++ b/mmgen/daemon.py @@ -24,7 +24,7 @@ import os,time,importlib from subprocess import run,PIPE,CompletedProcess from collections import namedtuple -from .globalvars import g,gc +from .globalvars import gc from .color import set_vt100 from .util import msg,Msg_r,ymsg,die,remove_dups,oneshot_warning from .flags import * @@ -49,8 +49,9 @@ class Daemon(Lockable): avail_flags = () # like opts, but can be set or unset after instantiation _reset_ok = ('debug','wait','pids') - def __init__(self,opts=None,flags=None): + def __init__(self,cfg,opts=None,flags=None): + self.cfg = cfg self.platform = gc.platform if self.platform == 'win': self.use_pidfile = False @@ -237,8 +238,8 @@ class RPCDaemon(Daemon): avail_opts = ('no_daemonize',) - def __init__(self): - super().__init__() + def __init__(self,cfg): + super().__init__(cfg) self.desc = '{} {} {}RPC daemon'.format( self.rpc_type, getattr(self.proto.network_names,self.proto.network), @@ -280,13 +281,13 @@ class CoinDaemon(Daemon): message = 'blacklisted daemon: {!r}' @classmethod - def get_daemon_ids(cls,coin): + def get_daemon_ids(cls,cfg,coin): ret = cls.coins[coin].daemon_ids - if 'erigon' in ret and not g.enable_erigon: + if 'erigon' in ret and not cfg.enable_erigon: ret.remove('erigon') - if g.blacklisted_daemons: - blacklist = g.blacklisted_daemons.split() + if cfg.blacklisted_daemons: + blacklist = cfg.blacklisted_daemons.split() def gen(): for daemon_id in ret: if daemon_id in blacklist: @@ -297,21 +298,21 @@ class CoinDaemon(Daemon): return ret @classmethod - def get_daemon(cls,coin,daemon_id,proto=None): + def get_daemon(cls,cfg,coin,daemon_id,proto=None): if not proto: from .protocol import init_proto - proto = init_proto(coin) + proto = init_proto( cfg, coin ) return getattr( importlib.import_module(f'mmgen.proto.{proto.base_proto_coin.lower()}.daemon'), daemon_id+'_daemon' ) @classmethod - def get_network_ids(cls): + def get_network_ids(cls,cfg): from .protocol import CoinProtocol def gen(): for coin in cls.coins: - for daemon_id in cls.get_daemon_ids(coin): - for network in cls.get_daemon(coin,daemon_id).networks: + for daemon_id in cls.get_daemon_ids(cfg,coin): + for network in cls.get_daemon( cfg, coin, daemon_id ).networks: yield CoinProtocol.Base.create_network_id(coin,network) return remove_dups(list(gen()),quiet=True) @@ -329,6 +330,7 @@ class CoinDaemon(Daemon): return ( res[0] if len(res) == 1 else [s for s in res if 'ersion' in s][0] ).strip() def __new__(cls, + cfg, network_id = None, proto = None, opts = None, @@ -349,19 +351,19 @@ class CoinDaemon(Daemon): else: network_id = network_id.lower() from .protocol import CoinProtocol,init_proto - proto = init_proto(network_id=network_id) + proto = init_proto( cfg, network_id=network_id ) coin,network = CoinProtocol.Base.parse_network_id(network_id) coin = coin.upper() - daemon_ids = cls.get_daemon_ids(coin) + daemon_ids = cls.get_daemon_ids(cfg,coin) if not daemon_ids: die(1,f'No configured daemons for coin {coin}!') - daemon_id = daemon_id or g.daemon_id or daemon_ids[0] + daemon_id = daemon_id or cfg.daemon_id or daemon_ids[0] if daemon_id not in daemon_ids: die(1,f'{daemon_id!r}: invalid daemon_id - valid choices: {fmt_list(daemon_ids)}') - me = Daemon.__new__(cls.get_daemon( None, daemon_id, proto=proto )) + me = Daemon.__new__(cls.get_daemon( cfg, None, daemon_id, proto=proto )) assert network in me.networks, f'{network!r}: unsupported network for daemon {daemon_id}' me.network_id = network_id @@ -373,6 +375,7 @@ class CoinDaemon(Daemon): return me def __init__(self, + cfg, network_id = None, proto = None, opts = None, @@ -385,7 +388,7 @@ class CoinDaemon(Daemon): self.test_suite = test_suite - super().__init__(opts=opts,flags=flags) + super().__init__(cfg=cfg,opts=opts,flags=flags) self._set_ok += ('shared_args','usr_coind_args') self.shared_args = [] @@ -400,8 +403,8 @@ class CoinDaemon(Daemon): 'test suite ' if test_suite else '' ) # user-set values take precedence - self.datadir = os.path.abspath(datadir or g.daemon_data_dir or self.init_datadir()) - self.non_dfl_datadir = bool(datadir or g.daemon_data_dir or test_suite or self.network == 'regtest') + self.datadir = os.path.abspath(datadir or cfg.daemon_data_dir or self.init_datadir()) + self.non_dfl_datadir = bool(datadir or cfg.daemon_data_dir or test_suite or self.network == 'regtest') # init_datadir() may have already initialized logdir self.logdir = os.path.abspath(getattr(self,'logdir',self.datadir)) @@ -409,7 +412,7 @@ class CoinDaemon(Daemon): ps_adj = (port_shift or 0) + (self.test_suite_port_shift if test_suite else 0) # user-set values take precedence - self.rpc_port = (g.rpc_port or 0) + (port_shift or 0) if g.rpc_port else ps_adj + self.get_rpc_port() + self.rpc_port = (cfg.rpc_port or 0) + (port_shift or 0) if cfg.rpc_port else ps_adj + self.get_rpc_port() self.p2p_port = ( p2p_port or ( self.get_p2p_port() + ps_adj if self.get_p2p_port() and (test_suite or ps_adj) else None diff --git a/mmgen/data/version b/mmgen/data/version index ece4cb50..98742796 100644 --- a/mmgen/data/version +++ b/mmgen/data/version @@ -1 +1 @@ -13.3.dev41 +13.3.dev42 diff --git a/mmgen/fileutil.py b/mmgen/fileutil.py index 9d277072..0fab2195 100755 --- a/mmgen/fileutil.py +++ b/mmgen/fileutil.py @@ -22,12 +22,10 @@ fileutil: Routines that read, write, execute or stat files import sys,os -from .globalvars import g,gc +from .globalvars import gc from .color import set_vt100 from .util import ( msg, - qmsg, - dmsg, die, get_extension, is_utf8, @@ -114,15 +112,16 @@ def check_outfile(f,blkdev_ok=False): def check_outdir(f): return _check_file_type_and_access(f,'output directory') -def get_seed_file(wallets,nargs,invoked_as=None): +def get_seed_file(cfg,nargs,wallets=None,invoked_as=None): + + wallets = wallets or cfg._args - from .opts import opt from .filename import find_file_in_dir from .wallet.mmgen import wallet - wf = find_file_in_dir(wallet,g.data_dir) + wf = find_file_in_dir(wallet,cfg.data_dir) - wd_from_opt = bool(opt.hidden_incog_input_params or opt.in_fmt) # have wallet data from opt? + wd_from_opt = bool(cfg.hidden_incog_input_params or cfg.in_fmt) # have wallet data from opt? import mmgen.opts as opts if len(wallets) + (wd_from_opt or bool(wf)) < nargs: @@ -132,7 +131,7 @@ def get_seed_file(wallets,nargs,invoked_as=None): elif len(wallets) > nargs: opts.usage() elif len(wallets) == nargs and wf and invoked_as != 'gen': - qmsg('Warning: overriding default wallet with user-supplied wallet') + cfg._util.qmsg('Warning: overriding default wallet with user-supplied wallet') if wallets or wf: check_infile(wallets[0] if wallets else wf) @@ -150,6 +149,7 @@ def _open_or_die(filename,mode,silent=False): )) def write_data_to_file( + cfg, outfile, data, desc = 'data', @@ -165,25 +165,24 @@ def write_data_to_file( check_data = False, cmp_data = None): - from .opts import opt - if quiet: ask_tty = ask_overwrite = False - if opt.quiet: + if cfg.quiet: ask_overwrite = False if ask_write_default_yes == False or ask_write_prompt: ask_write = True def do_stdout(): - qmsg('Output to STDOUT requested') - if g.stdin_tty: + cfg._util.qmsg('Output to STDOUT requested') + if cfg.stdin_tty: if no_tty: die(2,f'Printing {desc} to screen is not allowed') - if (ask_tty and not opt.quiet) or binary: + if (ask_tty and not cfg.quiet) or binary: from .ui import confirm_or_raise confirm_or_raise( + cfg, message = '', action = f'output {desc} to screen' ) else: @@ -194,9 +193,10 @@ def write_data_to_file( if of[:5] == 'pipe:': if no_tty: die(2,f'Writing {desc} to pipe is not allowed') - if ask_tty and not opt.quiet: + if ask_tty and not cfg.quiet: from .ui import confirm_or_raise confirm_or_raise( + cfg, message = '', action = f'output {desc} to pipe' ) msg('') @@ -216,14 +216,15 @@ def write_data_to_file( os.write(1,data if isinstance(data,bytes) else data.encode()) def do_file(outfile,ask_write_prompt): - if opt.outdir and not ignore_opt_outdir and not os.path.isabs(outfile): - outfile = make_full_path(opt.outdir,outfile) + if cfg.outdir and not ignore_opt_outdir and not os.path.isabs(outfile): + outfile = make_full_path(cfg.outdir,outfile) if ask_write: if not ask_write_prompt: ask_write_prompt = f'Save {desc}?' from .ui import keypress_confirm if not keypress_confirm( + cfg, ask_write_prompt, default_yes = ask_write_default_yes ): die(1,f'{capfirst(desc)} not saved') @@ -232,6 +233,7 @@ def write_data_to_file( if os.path.lexists(outfile) and ask_overwrite: from .ui import confirm_or_raise confirm_or_raise( + cfg, message = '', action = f'File {outfile!r} already exists\nOverwrite?' ) msg(f'Overwriting file {outfile!r}') @@ -262,17 +264,17 @@ def write_data_to_file( return True - if opt.stdout or outfile in ('','-'): + if cfg.stdout or outfile in ('','-'): do_stdout() elif sys.stdin.isatty() and not sys.stdout.isatty(): do_stdout() else: do_file(outfile,ask_write_prompt) -def get_words_from_file(infile,desc,quiet=False): +def get_words_from_file(cfg,infile,desc,quiet=False): if not quiet: - qmsg(f'Getting {desc} from file {infile!r}') + cfg._util.qmsg(f'Getting {desc} from file {infile!r}') with _open_or_die(infile, 'rb') as fp: data = fp.read() @@ -282,11 +284,12 @@ def get_words_from_file(infile,desc,quiet=False): except: die(1,f'{capfirst(desc)} data must be UTF-8 encoded.') - dmsg('Sanitized input: [{}]'.format(' '.join(words))) + cfg._util.dmsg('Sanitized input: [{}]'.format(' '.join(words))) return words def get_data_from_file( + cfg, infile, desc = 'data', dash = False, @@ -294,26 +297,26 @@ def get_data_from_file( binary = False, quiet = False ): - from .opts import opt - if not (opt.quiet or silent or quiet): - qmsg(f'Getting {desc} from file {infile!r}') + if not (cfg.quiet or silent or quiet): + cfg._util.qmsg(f'Getting {desc} from file {infile!r}') with _open_or_die( (0 if dash and infile == '-' else infile), 'rb', silent=silent) as fp: - data = fp.read(g.max_input_size+1) + data = fp.read(cfg.max_input_size+1) if not binary: data = data.decode() - if len(data) == g.max_input_size + 1: + if len(data) == cfg.max_input_size + 1: die( 'MaxInputSizeExceeded', f'Too much input data! Max input data size: {f.max_input_size} bytes' ) return data def get_lines_from_file( + cfg, fn, desc = 'data', trim_comments = False, @@ -321,17 +324,17 @@ def get_lines_from_file( silent = False ): def decrypt_file_maybe(): - data = get_data_from_file( fn, desc=desc, binary=True, quiet=quiet, silent=silent ) + data = get_data_from_file( cfg, fn, desc=desc, binary=True, quiet=quiet, silent=silent ) from .crypto import Crypto have_enc_ext = get_extension(fn) == Crypto.mmenc_ext if have_enc_ext or not is_utf8(data): m = ('Attempting to decrypt','Decrypting')[have_enc_ext] - qmsg(f'{m} {desc} {fn!r}') - data = Crypto().mmgen_decrypt_retry(data,desc) + cfg._util.qmsg(f'{m} {desc} {fn!r}') + data = Crypto(cfg).mmgen_decrypt_retry(data,desc) return data lines = decrypt_file_maybe().decode().splitlines() if trim_comments: lines = strip_comments(lines) - dmsg(f'Got {len(lines)} lines from file {fn!r}') + cfg._util.dmsg(f'Got {len(lines)} lines from file {fn!r}') return lines diff --git a/mmgen/globalvars.py b/mmgen/globalvars.py index fb9785dc..4a9b4478 100755 --- a/mmgen/globalvars.py +++ b/mmgen/globalvars.py @@ -104,7 +104,7 @@ class GlobalVars: gv = GlobalVars() -class GlobalConfig(Lockable): +class Config(Lockable): """ These values are configurable - RHS values are defaults Globals are overridden with the following precedence: @@ -113,12 +113,13 @@ class GlobalConfig(Lockable): 3 - config file """ _autolock = False - _set_ok = () - _reset_ok = ('accept_defaults',) + _set_ok = ('usr_randchars','_proto') + _reset_ok = ('accept_defaults','quiet','verbose','yes') _use_class_attr = True + _default_to_none = True # general - coin = '' + coin = 'BTC' token = '' outdir = '' passwd_file = '' @@ -189,7 +190,7 @@ class GlobalConfig(Lockable): bob = False alice = False carol = False - regtest_user = None + regtest_user = '' # test suite: bogus_send = False @@ -216,20 +217,6 @@ class GlobalConfig(Lockable): _proto = None pager = False - # global var sets user opt: - global_sets_opt = ( - 'autochg_ignore_labels', - 'debug', - 'minconf', - 'quiet', - 'fee_estimate_confs', - 'fee_adjust', - 'use_internal_keccak_module', - 'usr_randchars' ) - - # user opt sets global var: - opt_sets_global = ( 'cached_balances', ) - # 'long' opts (subset of common_opts_data): common_opts = ( 'accept_defaults', @@ -415,5 +402,3 @@ class GlobalConfig(Lockable): 'mainnet': (self.data_dir_root,), }[self.network] )) return self._data_dir - -g = GlobalConfig() diff --git a/mmgen/help.py b/mmgen/help.py index 95a8e2b8..733e5fa1 100755 --- a/mmgen/help.py +++ b/mmgen/help.py @@ -22,7 +22,7 @@ help: help notes for MMGen suite commands from .globalvars import gc -def help_notes_func(proto,opt,k): +def help_notes_func(proto,cfg,k): def fee_spec_letters(use_quotes=False): cu = proto.coin_amt.units @@ -36,7 +36,7 @@ def help_notes_func(proto,opt,k): def coind_exec(): from .daemon import CoinDaemon return ( - CoinDaemon(proto.coin).exec_fn if proto.coin in CoinDaemon.coins else 'bitcoind' ) + CoinDaemon(cfg,proto.coin).exec_fn if proto.coin in CoinDaemon.coins else 'bitcoind' ) class help_notes: @@ -92,7 +92,7 @@ def help_notes_func(proto,opt,k): from .keygen import get_backends from .addr import MMGenAddrType backends = get_backends( - MMGenAddrType(proto,opt.type or proto.dfl_mmtype).pubkey_type + MMGenAddrType(proto,cfg.type or proto.dfl_mmtype).pubkey_type ) return ' '.join( f'{n}:{k}{" [default]" if n==1 else ""}' for n,k in enumerate(backends,1) ) @@ -102,11 +102,11 @@ def help_notes_func(proto,opt,k): def coin_daemon_network_ids(): from .daemon import CoinDaemon from .util import fmt_list - return fmt_list(CoinDaemon.get_network_ids(),fmt='bare') + return fmt_list(CoinDaemon.get_network_ids(cfg),fmt='bare') def rel_fee_desc(): from .tx import BaseTX - return BaseTX(proto=proto).rel_fee_desc + return BaseTX(cfg=cfg,proto=proto).rel_fee_desc def fee_spec_letters(): return fee_spec_letters() @@ -121,7 +121,7 @@ be specified as either absolute {c} amounts, using a plain decimal number, or as {r}, using an integer followed by '{l}', for {u}. """.format( c = proto.coin, - r = BaseTX(proto=proto).rel_fee_desc, + r = BaseTX(cfg=cfg,proto=proto).rel_fee_desc, l = fee_spec_letters(use_quotes=True), u = fee_spec_names() ) @@ -147,7 +147,7 @@ seed, the same seed length and hash preset parameters must always be used. mmtype = 'S' if 'segwit' in proto.caps else 'C' from .tool.coin import tool_cmd - t = tool_cmd(mmtype=mmtype) + t = tool_cmd(cfg,mmtype=mmtype) sample_addr = t.privhex2addr('bead'*16) return f""" diff --git a/mmgen/keygen.py b/mmgen/keygen.py index 74ebf496..e5eee262 100755 --- a/mmgen/keygen.py +++ b/mmgen/keygen.py @@ -71,9 +71,9 @@ def get_pubkey_type_cls(pubkey_type): importlib.import_module(f'mmgen.proto.{backend_data[pubkey_type]["package"]}.keygen'), 'backend' ) -def _check_backend(backend,pubkey_type,desc='keygen backend'): +def _check_backend(cfg,backend,pubkey_type,desc='keygen backend'): - from .util import is_int,qmsg,die + from .util import is_int,die assert is_int(backend), f'illegal value for {desc} (must be an integer)' @@ -86,21 +86,22 @@ def _check_backend(backend,pubkey_type,desc='keygen backend'): ' '.join( f'{n}:{k}' for n,k in enumerate(backends,1) ) ) - qmsg(f'Using backend {backends[int(backend)-1]!r} for public key generation') + cfg._util.qmsg(f'Using backend {backends[int(backend)-1]!r} for public key generation') return True -def check_backend(proto,backend,addr_type): +def check_backend(cfg,proto,backend,addr_type): from .addr import MMGenAddrType pubkey_type = MMGenAddrType(proto,addr_type or proto.dfl_mmtype).pubkey_type return _check_backend( + cfg, backend, pubkey_type, desc = '--keygen-backend parameter' ) -def KeyGenerator(proto,pubkey_type,backend=None,silent=False): +def KeyGenerator(cfg,proto,pubkey_type,backend=None,silent=False): """ factory function returning a key generator backend for the specified pubkey type """ @@ -108,11 +109,10 @@ def KeyGenerator(proto,pubkey_type,backend=None,silent=False): pubkey_type_cls = get_pubkey_type_cls(pubkey_type) - from .opts import opt - backend = backend or opt.keygen_backend + backend = backend or cfg.keygen_backend if backend: - _check_backend(backend,pubkey_type) + _check_backend( cfg, backend, pubkey_type ) backend_id = backend_data[pubkey_type]['backends'][int(backend) - 1 if backend else 0] @@ -121,4 +121,4 @@ def KeyGenerator(proto,pubkey_type,backend=None,silent=False): backend_id.replace('-','_') ).test_avail(silent=silent) - return getattr(pubkey_type_cls,backend_clsname)() + return getattr(pubkey_type_cls,backend_clsname)(cfg) diff --git a/mmgen/main_addrgen.py b/mmgen/main_addrgen.py index a66bed7b..57edda3c 100755 --- a/mmgen/main_addrgen.py +++ b/mmgen/main_addrgen.py @@ -22,8 +22,7 @@ mmgen-addrgen: Generate a series or range of addresses from an MMGen """ import mmgen.opts as opts -from .globalvars import g,gc -from .opts import opt +from .globalvars import gc from .addr import MMGenAddrType from .addrfile import AddrFile from .wallet import Wallet @@ -74,7 +73,7 @@ opts_data = { -P, --passwd-file= f Get wallet passphrase from file 'f' -q, --quiet Produce quieter output; suppress some warnings -r, --usr-randchars=n Get 'n' characters of additional randomness from user - (min={g.min_urandchars}, max={g.max_urandchars}, default={g.usr_randchars}) + (min={cfg.min_urandchars}, max={cfg.max_urandchars}, default={cfg.usr_randchars}) -S, --stdout Print {what} to stdout -t, --type=t Choose address type. Options: see ADDRESS TYPES below (default: {dmat}) @@ -106,14 +105,14 @@ FMT CODES: """ }, 'code': { - 'options': lambda proto,help_notes,s: s.format( + 'options': lambda proto,help_notes,cfg,s: s.format( dmat=help_notes('dfl_mmtype'), kgs=help_notes('keygen_backends'), coin_id=help_notes('coin_id'), dsl=help_notes('dfl_seed_len'), pnm=gc.proj_name, what=gen_what, - g=g, + cfg=cfg, gc=gc, ), 'notes': lambda help_notes,s: s.format( @@ -127,39 +126,39 @@ FMT CODES: } } -cmd_args = opts.init(opts_data,opt_filter=opt_filter) +cfg = opts.init(opts_data,opt_filter=opt_filter,need_amt=False) -from .protocol import init_proto_from_opts -proto = init_proto_from_opts() +proto = cfg._proto addr_type = MMGenAddrType( proto = proto, - id_str = opt.type or proto.dfl_mmtype, - errmsg = f'{opt.type!r}: invalid parameter for --type option' ) + id_str = cfg.type or proto.dfl_mmtype, + errmsg = f'{cfg.type!r}: invalid parameter for --type option' ) -if len(cmd_args) < 1: +if len(cfg._args) < 1: opts.usage() -if opt.keygen_backend: +if cfg.keygen_backend: from .keygen import check_backend - check_backend( proto, opt.keygen_backend, opt.type ) + check_backend( cfg, proto, cfg.keygen_backend, cfg.type ) -idxs = mmgen.addrlist.AddrIdxList( fmt_str=cmd_args.pop() ) +idxs = mmgen.addrlist.AddrIdxList( fmt_str=cfg._args.pop() ) from .fileutil import get_seed_file -sf = get_seed_file(cmd_args,1) +sf = get_seed_file(cfg,1) from .ui import do_license_msg -do_license_msg() +do_license_msg(cfg) -ss = Wallet(sf) +ss = Wallet(cfg,sf) -ss_seed = ss.seed if opt.subwallet is None else ss.seed.subseed(opt.subwallet,print_msg=True) +ss_seed = ss.seed if cfg.subwallet is None else ss.seed.subseed(cfg.subwallet,print_msg=True) -if opt.no_addresses: +if cfg.no_addresses: gen_clsname = 'KeyList' al = getattr( mmgen.addrlist, gen_clsname )( + cfg = cfg, proto = proto, seed = ss_seed, addr_idxs = idxs, @@ -169,11 +168,11 @@ af = al.get_file() af.format() -if al.gen_addrs and opt.print_checksum: +if al.gen_addrs and cfg.print_checksum: Die(0,al.checksum) from .ui import keypress_confirm -if al.gen_keys and keypress_confirm('Encrypt key list?'): +if al.gen_keys and keypress_confirm( cfg, 'Encrypt key list?' ): af.encrypt() af.write( binary = True, diff --git a/mmgen/main_addrimport.py b/mmgen/main_addrimport.py index abda5c75..aedb75f9 100755 --- a/mmgen/main_addrimport.py +++ b/mmgen/main_addrimport.py @@ -24,8 +24,7 @@ from collections import namedtuple import mmgen.opts as opts from .globalvars import gc -from .opts import opt -from .util import msg,qmsg,suf,die,fmt,async_run +from .util import msg,suf,die,fmt,async_run from .addrlist import AddrList,KeyAddrList from .tw.shared import TwLabel @@ -87,7 +86,7 @@ addrimport_msgs = { def parse_cmd_args(rpc,cmd_args): def import_mmgen_list(infile): - al = (AddrList,KeyAddrList)[bool(opt.keyaddr_file)](proto,infile) + al = (AddrList,KeyAddrList)[bool(cfg.keyaddr_file)](cfg,proto,infile) if al.al_id.mmtype in ('S','B'): if not rpc.info('segwit_is_active'): die(2,'Segwit is not active on this chain. Cannot import Segwit addresses') @@ -97,17 +96,19 @@ def parse_cmd_args(rpc,cmd_args): infile = cmd_args[0] from .fileutil import check_infile,get_lines_from_file check_infile(infile) - if opt.addrlist: + if cfg.addrlist: al = AddrList( - proto = proto, + cfg = cfg, + proto = proto, addrlist = get_lines_from_file( + cfg, infile, f'non-{gc.proj_name} addresses', trim_comments = True ) ) else: al = import_mmgen_list(infile) - elif len(cmd_args) == 0 and opt.address: - al = AddrList(proto=proto,addrlist=[opt.address]) + elif len(cmd_args) == 0 and cfg.address: + al = AddrList( cfg, proto=proto, addrlist=[cfg.address] ) infile = 'command line' else: die(1,addrimport_msgs['bad_args']) @@ -115,16 +116,17 @@ def parse_cmd_args(rpc,cmd_args): return al,infile def check_opts(twctl): - batch = bool(opt.batch) - rescan = bool(opt.rescan) + batch = bool(cfg.batch) + rescan = bool(cfg.rescan) if rescan and not 'rescan' in twctl.caps: msg(f"‘--rescan’ ignored: not supported by {type(twctl).__name__}") rescan = False - if rescan and not opt.quiet: + if rescan and not cfg.quiet: from .ui import keypress_confirm if not keypress_confirm( + cfg, '\n{}\n\nContinue?'.format(addrimport_msgs['rescan']), default_yes = True ): die(1,'Exiting at user request') @@ -137,32 +139,33 @@ def check_opts(twctl): async def main(): from .tw.ctl import TwCtl - if opt.token_addr: + if cfg.token_addr: proto.tokensym = 'foo' # hack to trigger 'Token' in proto.base_proto_subclass() twctl = await TwCtl( + cfg = cfg, proto = proto, - token_addr = opt.token_addr, + token_addr = cfg.token_addr, mode = 'i' ) - if opt.token or opt.token_addr: + if cfg.token or cfg.token_addr: msg(f'Importing for token {twctl.token.hl()} ({twctl.token.hlc(proto.tokensym)})') from .rpc import rpc_init - twctl.rpc = await rpc_init(proto) + twctl.rpc = await rpc_init(cfg,proto) for k,v in addrimport_msgs.items(): addrimport_msgs[k] = fmt(v,indent=' ',strip_char='\t').rstrip() - al,infile = parse_cmd_args(twctl.rpc,cmd_args) + al,infile = parse_cmd_args(twctl.rpc,cfg._args) - qmsg( + cfg._util.qmsg( f'OK. {al.num_addrs} addresses' + (f' from Seed ID {al.al_id.sid}' if hasattr(al.al_id,'sid') else '') ) msg( f'Importing {len(al.data)} address{suf(al.data,"es")} from {infile}' - + (' (batch mode)' if opt.batch else '') ) + + (' (batch mode)' if cfg.batch else '') ) batch,rescan = check_opts(twctl) @@ -183,9 +186,8 @@ async def main(): del twctl -cmd_args = opts.init(opts_data) +cfg = opts.init(opts_data,need_amt=False) -from .protocol import init_proto_from_opts -proto = init_proto_from_opts() +proto = cfg._proto async_run(main()) diff --git a/mmgen/main_autosign.py b/mmgen/main_autosign.py index 59c35b11..96c319cf 100755 --- a/mmgen/main_autosign.py +++ b/mmgen/main_autosign.py @@ -26,9 +26,7 @@ from collections import namedtuple from stat import * import mmgen.opts as opts -from .globalvars import g -from .opts import opt -from .util import msg,msg_r,vmsg,qmsg,ymsg,rmsg,gmsg,bmsg,die,suf,fmt_list,async_run,exit_if_mswin +from .util import msg,msg_r,ymsg,rmsg,gmsg,bmsg,die,suf,fmt_list,async_run,exit_if_mswin from .color import yellow,red,orange mountpoint = '/mnt/tx' @@ -122,7 +120,7 @@ This command is currently available only on Linux-based platforms. } } -cmd_args = opts.init( +cfg = opts.init( opts_data, add_opts = ['outdir','passwd_file'], # in _set_ok, so must be set init_opts = { @@ -133,14 +131,16 @@ cmd_args = opts.init( 'label': 'Autosign Wallet', }) -type(opt)._set_ok += ('outdir','passwd_file') +cmd_args = cfg._args + +type(cfg)._set_ok += ('outdir','passwd_file') exit_if_mswin('autosigning') -if opt.mnemonic_fmt: - if opt.mnemonic_fmt not in mn_fmts: +if cfg.mnemonic_fmt: + if cfg.mnemonic_fmt not in mn_fmts: die(1,'{!r}: invalid mnemonic format (must be one of: {})'.format( - opt.mnemonic_fmt, + cfg.mnemonic_fmt, fmt_list(mn_fmts,fmt='no_spc') )) from .wallet import Wallet @@ -149,32 +149,32 @@ from .tx.sign import txsign from .protocol import init_proto from .rpc import rpc_init -if opt.mountpoint: - mountpoint = opt.mountpoint +if cfg.mountpoint: + mountpoint = cfg.mountpoint keyfile = os.path.join(mountpoint,'autosign.key') msg_dir = os.path.join(mountpoint,'msg') tx_dir = os.path.join(mountpoint,'tx') -opt.outdir = tx_dir -opt.passwd_file = keyfile +cfg.outdir = tx_dir +cfg.passwd_file = keyfile async def check_daemons_running(): - if opt.coin: + if cfg.coin != type(cfg).coin: die(1,'--coin option not supported with this command. Use --coins instead') - if opt.coins: - coins = opt.coins.upper().split(',') + if cfg.coins: + coins = cfg.coins.upper().split(',') else: ymsg('Warning: no coins specified, defaulting to BTC') coins = ['BTC'] for coin in coins: - proto = init_proto( coin, testnet=g.network=='testnet', need_amt=True ) + proto = init_proto( cfg, coin, testnet=cfg.network=='testnet', need_amt=True ) if proto.sign_mode == 'daemon': - vmsg(f'Checking {coin} daemon') + cfg._util.vmsg(f'Checking {coin} daemon') from .exception import SocketError try: - await rpc_init(proto) + await rpc_init(cfg,proto) except SocketError as e: die(2,f'{coin} daemon not running or not listening on port {proto.rpc_port}') @@ -213,10 +213,10 @@ def do_umount(): async def sign_object(d,fn): try: if d.desc == 'transaction': - tx1 = UnsignedTX(filename=fn) + tx1 = UnsignedTX(cfg=cfg,filename=fn) if tx1.proto.sign_mode == 'daemon': - tx1.rpc = await rpc_init(tx1.proto) - tx2 = await txsign(tx1,wfs[:],None,None) + tx1.rpc = await rpc_init(cfg,tx1.proto) + tx2 = await txsign(cfg,tx1,wfs[:],None,None) if tx2: tx2.file.write(ask_write=False) return tx2 @@ -224,9 +224,9 @@ async def sign_object(d,fn): return False elif d.desc == 'message file': from .msg import UnsignedMsg,SignedMsg - m = UnsignedMsg(infile=fn) + m = UnsignedMsg(cfg,infile=fn) await m.sign(wallet_files=wfs[:]) - m = SignedMsg(data=m.__dict__) + m = SignedMsg(cfg,data=m.__dict__) m.write_to_file( outdir = os.path.abspath(msg_dir), ask_overwrite = False ) @@ -259,12 +259,12 @@ async def sign(target): ok.append(ret) else: bad.append(fn) - qmsg('') + cfg._util.qmsg('') await asyncio.sleep(0.3) msg(f'{len(ok)} {d.desc}{suf(ok)} signed') if bad: rmsg(f'{len(bad)} {d.desc}{suf(bad)} failed to {d.fail_desc}') - if ok and not opt.no_summary: + if ok and not cfg.no_summary: print_summary(d,ok) if bad: msg('') @@ -285,11 +285,11 @@ async def sign(target): return True def decrypt_wallets(): - msg(f'Unlocking wallet{suf(wfs)} with key from {opt.passwd_file!r}') + msg(f'Unlocking wallet{suf(wfs)} with key from {cfg.passwd_file!r}') fails = 0 for wf in wfs: try: - Wallet(wf,ignore_in_fmt=True) + Wallet(cfg,wf,ignore_in_fmt=True) except SystemExit as e: if e.code != 0: fails += 1 @@ -304,7 +304,7 @@ def print_summary(d,signed_objects): gmsg(' ' + os.path.join(msg_dir,m.signed_filename) ) return - if opt.full_summary: + if cfg.full_summary: bmsg('\nAutosign summary:\n') def gen(): for tx in signed_objects: @@ -340,23 +340,23 @@ def print_summary(d,signed_objects): msg('No non-MMGen outputs') async def do_sign(): - if not opt.stealth_led: + if not cfg.stealth_led: led.set('busy') do_mount() key_ok = decrypt_wallets() if key_ok: - if opt.stealth_led: + if cfg.stealth_led: led.set('busy') ret1 = await sign('tx') ret2 = await sign('msg') if have_msg_dir else True ret = ret1 and ret2 do_umount() - led.set(('standby','off','error')[(not ret)*2 or bool(opt.stealth_led)]) + led.set(('standby','off','error')[(not ret)*2 or bool(cfg.stealth_led)]) return ret else: msg('Password is incorrect!') do_umount() - if not opt.stealth_led: + if not cfg.stealth_led: led.set('error') return False @@ -366,7 +366,7 @@ def wipe_existing_key(): else: from .fileutil import shred_file msg(f'\nShredding existing key {keyfile!r}') - shred_file( keyfile, verbose=opt.verbose ) + shred_file( keyfile, verbose=cfg.verbose ) def create_key(): kdata = os.urandom(32).hex() @@ -404,12 +404,12 @@ def create_wallet_dir(): def setup(): remove_wallet_dir() gen_key(no_unmount=True) - ss_in = Wallet(in_fmt=mn_fmts[opt.mnemonic_fmt or mn_fmt_dfl]) - ss_out = Wallet(ss=ss_in) + ss_in = Wallet(cfg,in_fmt=mn_fmts[cfg.mnemonic_fmt or mn_fmt_dfl]) + ss_out = Wallet(cfg,ss=ss_in) ss_out.write_to_file(desc='autosign wallet',outdir=wallet_dir) def get_insert_status(): - if opt.no_insert_check: + if cfg.no_insert_check: return True try: os.stat(os.path.join('/dev/disk/by-label',part_label)) except: return False @@ -417,7 +417,7 @@ def get_insert_status(): async def do_loop(): n,prev_status = 0,False - if not opt.stealth_led: + if not cfg.stealth_led: led.set('standby') while True: status = get_insert_status() @@ -459,7 +459,7 @@ signal.signal(signal.SIGINT,handler) from .led import LEDControl led = LEDControl( - enabled = opt.led, + enabled = cfg.led, simulate = os.getenv('MMGEN_TEST_SUITE_AUTOSIGN_LED_SIMULATE') ) led.set('off') diff --git a/mmgen/main_msg.py b/mmgen/main_msg.py index 72fc1c31..600b9a07 100755 --- a/mmgen/main_msg.py +++ b/mmgen/main_msg.py @@ -14,10 +14,8 @@ mmgen-msg: Message signing operations for the MMGen suite import sys import mmgen.opts as opts -from .globalvars import g -from .opts import opt from .base_obj import AsyncInit -from .util import msg,suf,async_run,stdout_or_pager +from .util import msg,suf,async_run from .msg import ( NewMsg, CompletedMsg, @@ -33,30 +31,29 @@ class MsgOps: class create: def __init__(self,msg,addr_specs): - from .protocol import init_proto_from_opts - proto = init_proto_from_opts() NewMsg( - coin = proto.coin, - network = proto.network, + cfg = cfg, + coin = cfg._proto.coin, + network = cfg._proto.network, message = msg, addrlists = addr_specs, - msghash_type = opt.msghash_type + msghash_type = cfg.msghash_type ).write_to_file( ask_overwrite=False ) class sign(metaclass=AsyncInit): async def __init__(self,msgfile,wallet_files): - m = UnsignedMsg( infile=msgfile ) + m = UnsignedMsg( cfg, infile=msgfile ) if not wallet_files: from .filename import find_file_in_dir from .wallet import get_wallet_cls - wallet_files = [find_file_in_dir( get_wallet_cls('mmgen'), g.data_dir )] + wallet_files = [find_file_in_dir( get_wallet_cls('mmgen'), cfg.data_dir )] await m.sign(wallet_files) - m = SignedMsg( data=m.__dict__ ) + m = SignedMsg( cfg, data=m.__dict__ ) m.write_to_file( ask_overwrite=False ) @@ -67,18 +64,18 @@ class MsgOps: async def __init__(self,msgfile,addr=None): try: - m = SignedOnlineMsg( infile=msgfile ) + m = SignedOnlineMsg( cfg, infile=msgfile ) except: - m = ExportedMsgSigs( infile=msgfile ) + m = ExportedMsgSigs( cfg, infile=msgfile ) nSigs = await m.verify(addr) summary = f'{nSigs} signature{suf(nSigs)} verified' - if opt.quiet: + if cfg.quiet: msg(summary) else: - stdout_or_pager(m.format(addr) + '\n\n' + summary + '\n') + cfg._util.stdout_or_pager(m.format(addr) + '\n\n' + summary + '\n') if m.data.get('failed_sids'): sys.exit(1) @@ -89,8 +86,9 @@ class MsgOps: from .fileutil import write_data_to_file write_data_to_file( + cfg = cfg, outfile = 'signatures.json', - data = SignedOnlineMsg( infile=msgfile ).get_json_for_export( addr ), + data = SignedOnlineMsg( cfg, infile=msgfile ).get_json_for_export( addr ), desc = 'signature data' ) opts_data = { @@ -205,14 +203,16 @@ $ mmgen-msg verify signatures.json } } -cmd_args = opts.init(opts_data) +cfg = opts.init(opts_data,need_amt=False) + +cmd_args = cfg._args if len(cmd_args) < 2: opts.usage() op = cmd_args.pop(0) -if opt.msghash_type and op != 'create': +if cfg.msghash_type and op != 'create': die(1,'--msghash-type option may only be used with the "create" command') async def main(): diff --git a/mmgen/main_passgen.py b/mmgen/main_passgen.py index b96a5626..a1d8eac6 100755 --- a/mmgen/main_passgen.py +++ b/mmgen/main_passgen.py @@ -22,8 +22,7 @@ mmgen-passgen: Generate a series or range of passwords from an MMGen """ import mmgen.opts as opts -from .globalvars import g,gc -from .opts import opt +from .globalvars import gc from .addrlist import AddrIdxList from .passwdlist import PasswordList from .wallet import Wallet @@ -62,7 +61,7 @@ opts_data = { -P, --passwd-file= f Get wallet passphrase from file 'f' -q, --quiet Produce quieter output; suppress some warnings -r, --usr-randchars=n Get 'n' characters of additional randomness from user - (min={g.min_urandchars}, max={g.max_urandchars}, default={g.usr_randchars}) + (min={cfg.min_urandchars}, max={cfg.max_urandchars}, default={cfg.usr_randchars}) -S, --stdout Print passwords to stdout -v, --verbose Produce more verbose output """, @@ -115,14 +114,15 @@ FMT CODES: """ }, 'code': { - 'options': lambda help_notes,s: s.format( - g=g,pnm=gc.proj_name, + 'options': lambda cfg,help_notes,s: s.format( + pnm=gc.proj_name, dsl=help_notes('dfl_seed_len'), dpf=PasswordList.dfl_pw_fmt, + cfg=cfg, gc=gc, ), - 'notes': lambda help_notes,s: s.format( - o=opts,g=g,i58=pwi['b58'],i32=pwi['b32'],i39=pwi['bip39'], + 'notes': lambda cfg,help_notes,s: s.format( + o=opts,cfg=cfg,i58=pwi['b58'],i32=pwi['b32'],i39=pwi['bip39'], ml=MMGenPWIDString.max_len, fs="', '".join(MMGenPWIDString.forbidden), n_pw=help_notes('passwd'), @@ -134,24 +134,25 @@ FMT CODES: } } -cmd_args = opts.init(opts_data) +cfg = opts.init(opts_data) -if len(cmd_args) < 2: opts.usage() +if len(cfg._args) < 2: opts.usage() -pw_idxs = AddrIdxList(fmt_str=cmd_args.pop()) +pw_idxs = AddrIdxList(fmt_str=cfg._args.pop()) -pw_id_str = cmd_args.pop() +pw_id_str = cfg._args.pop() from .fileutil import get_seed_file -sf = get_seed_file(cmd_args,1) +sf = get_seed_file(cfg,1) -pw_fmt = opt.passwd_fmt or PasswordList.dfl_pw_fmt -pw_len = pwi[pw_fmt].dfl_len // 2 if opt.passwd_len in ('h','H') else opt.passwd_len +pw_fmt = cfg.passwd_fmt or PasswordList.dfl_pw_fmt +pw_len = pwi[pw_fmt].dfl_len // 2 if cfg.passwd_len in ('h','H') else cfg.passwd_len from .protocol import init_proto -proto = init_proto('btc') # TODO: get rid of dummy proto +proto = init_proto( cfg, 'btc' ) # TODO: get rid of dummy proto PasswordList( + cfg = cfg, proto = proto, pw_id_str = pw_id_str, pw_len = pw_len, @@ -159,11 +160,12 @@ PasswordList( chk_params_only = True ) from .ui import do_license_msg -do_license_msg() +do_license_msg(cfg) -ss = Wallet(sf) +ss = Wallet(cfg,sf) al = PasswordList( + cfg = cfg, proto = proto, seed = ss.seed, pw_idxs = pw_idxs, @@ -176,11 +178,11 @@ af = al.get_file() af.format() from .ui import keypress_confirm -if keypress_confirm('Encrypt password list?'): +if keypress_confirm( cfg, 'Encrypt password list?' ): af.encrypt() af.write(binary=True,desc='encrypted password list') else: - if g.test_suite_popen_spawn and gc.platform == 'win': + if cfg.test_suite_popen_spawn and gc.platform == 'win': import time time.sleep(0.1) af.write(desc='password list') diff --git a/mmgen/main_regtest.py b/mmgen/main_regtest.py index 9045ac0c..a17cda47 100755 --- a/mmgen/main_regtest.py +++ b/mmgen/main_regtest.py @@ -22,7 +22,7 @@ mmgen-regtest: Coin daemon regression test mode setup and operations for the MMG """ import mmgen.opts as opts -from .globalvars import g,gc +from .globalvars import gc from .util import die,async_run opts_data = { @@ -57,7 +57,9 @@ opts_data = { } } -cmd_args = opts.init(opts_data) +cfg = opts.init(opts_data) + +cmd_args = cfg._args from .proto.btc.regtest import MMGenRegtest @@ -82,6 +84,6 @@ elif cmd_args[0] not in ('cli','wallet_cli','balances'): check_num_args() async def main(): - await MMGenRegtest(g.coin).cmd(cmd_args) + await MMGenRegtest(cfg,cfg.coin).cmd(cmd_args) async_run(main()) diff --git a/mmgen/main_seedjoin.py b/mmgen/main_seedjoin.py index e618acbd..4117f187 100755 --- a/mmgen/main_seedjoin.py +++ b/mmgen/main_seedjoin.py @@ -22,9 +22,8 @@ mmgen-seedjoin: Regenerate an MMGen deterministic wallet from seed shares """ import mmgen.opts as opts -from .globalvars import g,gc -from .opts import opt -from .util import msg,msg_r,qmsg,die +from .globalvars import gc +from .util import msg,msg_r,die from .color import yellow from .obj import MMGenWalletLabel from .seed import Seed @@ -58,7 +57,7 @@ opts_data = { -P, --passwd-file= f Get wallet passphrase from file 'f' -q, --quiet Produce quieter output; suppress some warnings -r, --usr-randchars=n Get 'n' characters of additional randomness from user - (min={g.min_urandchars}, max={g.max_urandchars}, default={g.usr_randchars}) + (min={cfg.min_urandchars}, max={cfg.max_urandchars}, default={cfg.usr_randchars}) -S, --stdout Write wallet data to stdout instead of file -v, --verbose Produce more verbose output """, @@ -82,13 +81,13 @@ FMT CODES: """ }, 'code': { - 'options': lambda s: s.format( + 'options': lambda cfg,s: s.format( ms_min=MasterShareIdx.min_val, ms_max=MasterShareIdx.max_val, - g=g, + cfg=cfg, gc=gc, ), - 'notes': lambda help_notes,s: s.format( + 'notes': lambda cfg,help_notes,s: s.format( f=help_notes('fmt_codes'), n_pw=help_notes('passwd'), ) @@ -97,7 +96,7 @@ FMT CODES: def print_shares_info(): si,out = 0,'\nComputed shares:\n' - if opt.master_share: + if cfg.master_share: fs = '{:3}: {}->{} ' + yellow('(master share #{}, split id ') + '{}' + yellow(', share count {})\n') out += fs.format( 1, @@ -109,36 +108,36 @@ def print_shares_info(): si = 1 for n,s in enumerate(shares[si:],si+1): out += f'{n:3}: {s.sid}\n' - qmsg(out) + cfg._util.qmsg(out) -cmd_args = opts.init(opts_data) +cfg = opts.init(opts_data) -if len(cmd_args) + bool(opt.hidden_incog_input_params) < 2: +if len(cfg._args) + bool(cfg.hidden_incog_input_params) < 2: opts.usage() -if opt.master_share: - master_idx = MasterShareIdx(opt.master_share) - id_str = SeedSplitIDString(opt.id_str or 'default') +if cfg.master_share: + master_idx = MasterShareIdx(cfg.master_share) + id_str = SeedSplitIDString(cfg.id_str or 'default') -if opt.id_str and not opt.master_share: +if cfg.id_str and not cfg.master_share: die(1,'--id-str option meaningless in context of non-master-share join') from .fileutil import check_infile from .wallet import check_wallet_extension -for arg in cmd_args: +for arg in cfg._args: check_wallet_extension(arg) check_infile(arg) from .ui import do_license_msg -do_license_msg() +do_license_msg(cfg) -qmsg('Input files:\n {}\n'.format('\n '.join(cmd_args))) +cfg._util.qmsg('Input files:\n {}\n'.format('\n '.join(cfg._args))) -shares = [Wallet().seed] if opt.hidden_incog_input_params else [] -shares += [Wallet(fn).seed for fn in cmd_args] +shares = [Wallet(cfg).seed] if cfg.hidden_incog_input_params else [] +shares += [Wallet(cfg,fn).seed for fn in cfg._args] -if opt.master_share: - share1 = SeedShareMasterJoining(master_idx,shares[0],id_str,len(shares)).derived_seed +if cfg.master_share: + share1 = SeedShareMasterJoining( cfg, master_idx, shares[0], id_str, len(shares) ).derived_seed else: share1 = shares[0] @@ -146,8 +145,8 @@ print_shares_info() msg_r('Joining {n}-of-{n} XOR split...'.format(n=len(shares))) -seed_out = Seed.join_shares([share1]+shares[1:]) +seed_out = Seed.join_shares( cfg, [share1] + shares[1:] ) msg(f'OK\nJoined Seed ID: {seed_out.sid.hl()}') -Wallet(seed=seed_out).write_to_file() +Wallet(cfg,seed=seed_out).write_to_file() diff --git a/mmgen/main_split.py b/mmgen/main_split.py index 5a17ecd6..84ac06c8 100755 --- a/mmgen/main_split.py +++ b/mmgen/main_split.py @@ -86,24 +86,23 @@ transaction reconfirmed before the timelock expires. Use at your own risk. } } -cmd_args = opts.init(opts_data) +cfg = opts.init(opts_data,need_amt=False) -from .protocol import init_proto_from_opts -proto = init_proto_from_opts() +proto = cfg._proto die(1,'This command is disabled') # the following code is broken: -opt.other_coin = opt.other_coin.upper() if opt.other_coin else proto.forks[-1][2].upper() -if opt.other_coin.lower() not in [e[2] for e in proto.forks if e[3] == True]: - die(1,f'{opt.other_coin!r}: not a replayable fork of {proto.coin} chain') +cfg.other_coin = cfg.other_coin.upper() if cfg.other_coin else proto.forks[-1][2].upper() +if cfg.other_coin.lower() not in [e[2] for e in proto.forks if e[3] == True]: + die(1,f'{cfg.other_coin!r}: not a replayable fork of {proto.coin} chain') -if len(cmd_args) != 2: +if len(cfg._args) != 2: die(1,f'This command requires exactly two {gc.proj_name} addresses as arguments') from .addr import MMGenID try: - mmids = [MMGenID(a) for a in cmd_args] + mmids = [MMGenID(a) for a in cfg._args] except: die(1,'Command line arguments must be valid MMGen IDs') @@ -113,18 +112,18 @@ if mmids[0] == mmids[1]: from .tx import MMGenSplitTX from .protocol import init_proto -if opt.tx_fees: - for idx,g_coin in ((1,opt.other_coin),(0,proto.coin)): - proto = init_proto(g_coin) - opt.fee = opt.tx_fees.split(',')[idx] - opts.opt_is_tx_fee('foo',opt.fee,'transaction fee') # raises exception on error +if cfg.tx_fees: + for idx,g_coin in ((1,cfg.other_coin),(0,proto.coin)): + proto = init_proto( cfg, g_coin ) + cfg.fee = cfg.tx_fees.split(',')[idx] + opts.opt_is_tx_fee('foo',cfg.fee,'transaction fee') # raises exception on error tx1 = MMGenSplitTX() -opt.no_blank = True +cfg.no_blank = True async def main(): gmsg(f'Creating timelocked transaction for long chain ({proto.coin})') - locktime = int(opt.locktime) + locktime = int(cfg.locktime) if not locktime: rpc = rpc_init(proto) locktime = rpc.call('getblockcount') @@ -133,9 +132,9 @@ async def main(): tx1.format() tx1.create_fn() - gmsg(f'\nCreating transaction for short chain ({opt.other_coin})') + gmsg(f'\nCreating transaction for short chain ({cfg.other_coin})') - proto = init_proto(opt.other_coin) + proto = init_proto( self.cfg, cfg.other_coin ) tx2 = MMGenSplitTX() tx2.inputs = tx1.inputs @@ -145,4 +144,4 @@ async def main(): for tx,desc in ((tx1,'Long chain (timelocked)'),(tx2,'Short chain')): tx.desc = desc + ' transaction' - tx.file.write(ask_write=False,ask_overwrite=not opt.yes,ask_write_default_yes=False) + tx.file.write(ask_write=False,ask_overwrite=not cfg.yes,ask_write_default_yes=False) diff --git a/mmgen/main_tool.py b/mmgen/main_tool.py index 7e0d814e..08d0b7e8 100755 --- a/mmgen/main_tool.py +++ b/mmgen/main_tool.py @@ -23,8 +23,7 @@ mmgen-tool: Perform various MMGen- and cryptocoin-related operations. import sys,os,importlib import mmgen.opts as opts -from .globalvars import g,gc -from .opts import opt +from .globalvars import gc from .util import msg,Msg,die,capfirst,suf,async_run opts_data = { @@ -45,7 +44,7 @@ opts_data = { -P, --passwd-file= f Get passphrase from file 'f'. -q, --quiet Produce quieter output -r, --usr-randchars=n Get 'n' characters of additional randomness from - user (min={g.min_urandchars}, max={g.max_urandchars}) + user (min={cfg.min_urandchars}, max={cfg.max_urandchars}) -t, --type=t Specify address type (valid choices: 'legacy', 'compressed', 'segwit', 'bech32', 'zcash_z') -v, --verbose Produce more verbose output @@ -61,13 +60,13 @@ Type ‘{pn} help ’ for help on a particular command """ }, 'code': { - 'options': lambda s, help_notes: s.format( + 'options': lambda cfg,s, help_notes: s.format( kgs=help_notes('keygen_backends'), coin_id=help_notes('coin_id'), - g=g, + cfg=cfg, gc=gc, ), - 'notes': lambda s, help_notes: s.format( + 'notes': lambda cfg,s, help_notes: s.format( ch=help_notes('tool_help'), pn=gc.prog_name) } @@ -346,7 +345,7 @@ def get_cmd_cls(cmd): def get_mod_cls(modname): return getattr(importlib.import_module(f'mmgen.tool.{modname}'),'tool_cmd') -if gc.prog_name == 'mmgen-tool' and not opt._lock: +if gc.prog_name == 'mmgen-tool': po = opts.init( opts_data, parse_only=True ) @@ -372,18 +371,20 @@ if gc.prog_name == 'mmgen-tool' and not opt._lock: if not cls: die(1,f'{cmd!r}: no such command') - cmd,*args = opts.init( + cfg = opts.init( opts_data, parsed_opts = po, need_proto = cls.need_proto, init_opts = {'rpc_backend':'aiohttp'} if cmd == 'twimport' else None ) + cmd,*args = cfg._args + if cmd in ('help','usage') and args: args[0] = 'command_name=' + args[0] args,kwargs = process_args(cmd,args,cls) - ret = getattr(cls(cmdname=cmd),cmd)(*args,**kwargs) + ret = getattr(cls(cfg,cmdname=cmd),cmd)(*args,**kwargs) if type(ret).__name__ == 'coroutine': ret = async_run(ret) diff --git a/mmgen/main_txbump.py b/mmgen/main_txbump.py index 34562387..9c5a7c64 100755 --- a/mmgen/main_txbump.py +++ b/mmgen/main_txbump.py @@ -23,8 +23,7 @@ mmgen-txbump: Increase the fee on a replaceable (replace-by-fee) MMGen import mmgen.opts as opts from .globalvars import gc -from .opts import opt -from .util import msg,msg_r,qmsg,die,async_run +from .util import msg,msg_r,die,async_run from .color import green from .wallet import Wallet @@ -86,7 +85,8 @@ FMT CODES: """ }, 'code': { - 'options': lambda help_notes,proto,s: s.format( + 'options': lambda cfg,help_notes,proto,s: s.format( + cfg=cfg, gc=gc, pnm=gc.proj_name, pnl=gc.proj_name.lower(), @@ -103,9 +103,9 @@ FMT CODES: } } -cmd_args = opts.init(opts_data) +cfg = opts.init(opts_data) -tx_file = cmd_args.pop(0) +tx_file = cfg._args.pop(0) from .fileutil import check_infile check_infile(tx_file) @@ -113,33 +113,34 @@ check_infile(tx_file) from .tx import CompletedTX,BumpTX,UnsignedTX,OnlineSignedTX from .tx.sign import txsign,get_seed_files,get_keyaddrlist,get_keylist -seed_files = get_seed_files(opt,cmd_args) if (cmd_args or opt.send) else None +seed_files = get_seed_files(cfg,cfg._args) if (cfg._args or cfg.send) else None from .ui import do_license_msg -do_license_msg() +do_license_msg(cfg) -silent = opt.yes and opt.fee != None and opt.output_to_reduce != None +silent = cfg.yes and cfg.fee != None and cfg.output_to_reduce != None async def main(): - orig_tx = await CompletedTX(filename=tx_file) + orig_tx = await CompletedTX(cfg=cfg,filename=tx_file) if not silent: msg(green('ORIGINAL TRANSACTION')) msg(orig_tx.info.format(terse=True)) - kal = get_keyaddrlist(orig_tx.proto,opt) - kl = get_keylist(orig_tx.proto,opt) + kal = get_keyaddrlist(cfg,orig_tx.proto) + kl = get_keylist(cfg,orig_tx.proto) sign_and_send = bool(seed_files or kl or kal) from .tw.ctl import TwCtl tx = await BumpTX( + cfg = cfg, data = orig_tx.__dict__, send = sign_and_send, - twctl = await TwCtl(orig_tx.proto) if orig_tx.proto.tokensym else None ) + twctl = await TwCtl(cfg,orig_tx.proto) if orig_tx.proto.tokensym else None ) from .rpc import rpc_init - tx.rpc = await rpc_init(tx.proto) + tx.rpc = await rpc_init(cfg,tx.proto) msg('Creating replacement transaction') @@ -150,13 +151,13 @@ async def main(): if not silent: msg(f'Minimum fee for new transaction: {tx.min_fee.hl()} {tx.proto.coin}') - tx.usr_fee = tx.get_usr_fee_interactive(fee=opt.fee,desc='User-selected') + tx.usr_fee = tx.get_usr_fee_interactive(fee=cfg.fee,desc='User-selected') tx.bump_fee(output_idx,tx.usr_fee) assert tx.fee <= tx.proto.max_tx_fee - if not opt.yes: + if not cfg.yes: tx.add_comment() # edits an existing comment await tx.create_serialized(bump=True) @@ -164,23 +165,23 @@ async def main(): tx.add_timestamp() tx.add_blockcount() - qmsg('Fee successfully increased') + cfg._util.qmsg('Fee successfully increased') if not silent: msg(green('\nREPLACEMENT TRANSACTION:')) msg_r(tx.info.format(terse=True)) if sign_and_send: - tx2 = UnsignedTX(data=tx.__dict__) - tx3 = await txsign(tx2,seed_files,kl,kal) + tx2 = UnsignedTX(cfg=cfg,data=tx.__dict__) + tx3 = await txsign(cfg,tx2,seed_files,kl,kal) if tx3: - tx4 = await OnlineSignedTX(data=tx3.__dict__) + tx4 = await OnlineSignedTX(cfg=cfg,data=tx3.__dict__) tx4.file.write(ask_write=False) await tx4.send(exit_on_fail=True) tx4.file.write(ask_write=False) else: die(2,'Transaction could not be signed') else: - tx.file.write(ask_write=not opt.yes,ask_write_default_yes=False,ask_overwrite=not opt.yes) + tx.file.write(ask_write=not cfg.yes,ask_write_default_yes=False,ask_overwrite=not cfg.yes) async_run(main()) diff --git a/mmgen/main_txcreate.py b/mmgen/main_txcreate.py index 79e9573d..1655fdf7 100755 --- a/mmgen/main_txcreate.py +++ b/mmgen/main_txcreate.py @@ -22,8 +22,7 @@ mmgen-txcreate: Create a cryptocoin transaction with MMGen- and/or non-MMGen """ import mmgen.opts as opts -from .globalvars import g,gc -from .opts import opt +from .globalvars import gc from .util import fmt_list,async_run opts_data = { @@ -38,7 +37,7 @@ opts_data = { -B, --no-blank Don't blank screen before displaying unspent outputs -c, --comment-file=f Source the transaction's comment from file 'f' -C, --fee-estimate-confs=c Desired number of confirmations for fee estimation - (default: {g.fee_estimate_confs}) + (default: {cfg.fee_estimate_confs}) -d, --outdir= d Specify an alternate directory 'd' for output -D, --contract-data=D Path to hex-encoded contract data (ETH only) -E, --fee-estimate-mode=M Specify the network fee estimate mode. Choices: @@ -66,41 +65,38 @@ opts_data = { 'notes': '\n{c}\n{F}\n{x}', }, 'code': { - 'options': lambda proto,help_notes,s: s.format( + 'options': lambda cfg,proto,help_notes,s: s.format( fu=help_notes('rel_fee_desc'), fl=help_notes('fee_spec_letters'), - fe_all=fmt_list(g.autoset_opts['fee_estimate_mode'].choices,fmt='no_spc'), - fe_dfl=g.autoset_opts['fee_estimate_mode'].choices[0], + fe_all=fmt_list(cfg.autoset_opts['fee_estimate_mode'].choices,fmt='no_spc'), + fe_dfl=cfg.autoset_opts['fee_estimate_mode'].choices[0], cu=proto.coin, - g=g), - 'notes': lambda help_notes,s: s.format( + cfg=cfg), + 'notes': lambda cfg,help_notes,s: s.format( c = help_notes('txcreate'), F = help_notes('fee'), x = help_notes('txcreate_examples') ) } } -cmd_args = opts.init(opts_data) +cfg = opts.init(opts_data) async def main(): - from .protocol import init_proto_from_opts - proto = init_proto_from_opts(need_amt=True) - from .tx import NewTX - tx1 = await NewTX(proto=proto) + tx1 = await NewTX(cfg=cfg,proto=cfg._proto) from .rpc import rpc_init - tx1.rpc = await rpc_init(proto) + tx1.rpc = await rpc_init(cfg,cfg._proto) tx2 = await tx1.create( - cmd_args = cmd_args, - locktime = int(opt.locktime or 0), - do_info = opt.info ) + cmd_args = cfg._args, + locktime = int(cfg.locktime or 0), + do_info = cfg.info ) tx2.file.write( - ask_write = not opt.yes, - ask_overwrite = not opt.yes, + ask_write = not cfg.yes, + ask_overwrite = not cfg.yes, ask_write_default_yes = False ) async_run(main()) diff --git a/mmgen/main_txdo.py b/mmgen/main_txdo.py index 20c3260d..9cc8348c 100755 --- a/mmgen/main_txdo.py +++ b/mmgen/main_txdo.py @@ -21,8 +21,7 @@ mmgen-txdo: Create, sign and broadcast an online MMGen transaction """ import mmgen.opts as opts -from .globalvars import g,gc -from .opts import opt +from .globalvars import gc from .util import die,fmt_list,async_run from .wallet import Wallet from .subseed import SubSeedIdxRange @@ -41,7 +40,7 @@ opts_data = { -B, --no-blank Don't blank screen before displaying unspent outputs -c, --comment-file= f Source the transaction's comment from file 'f' -C, --fee-estimate-confs=c Desired number of confirmations for fee estimation - (default: {g.fee_estimate_confs}) + (default: {cfg.fee_estimate_confs}) -d, --outdir= d Specify an alternate directory 'd' for output -D, --contract-data= D Path to hex-encoded contract data (ETH only) -e, --echo-passphrase Print passphrase to screen when typing it @@ -103,19 +102,19 @@ FMT CODES: {x}""" }, 'code': { - 'options': lambda proto,help_notes,s: s.format( - g=g,gc=gc,pnm=gc.proj_name,pnl=gc.proj_name.lower(), + 'options': lambda cfg,proto,help_notes,s: s.format( + gc=gc,cfg=cfg,pnm=gc.proj_name,pnl=gc.proj_name.lower(), kgs=help_notes('keygen_backends'), coin_id=help_notes('coin_id'), fu=help_notes('rel_fee_desc'), fl=help_notes('fee_spec_letters'), ss=help_notes('dfl_subseeds'), ss_max=SubSeedIdxRange.max_idx, - fe_all=fmt_list(g.autoset_opts['fee_estimate_mode'].choices,fmt='no_spc'), - fe_dfl=g.autoset_opts['fee_estimate_mode'].choices[0], + fe_all=fmt_list(cfg.autoset_opts['fee_estimate_mode'].choices,fmt='no_spc'), + fe_dfl=cfg.autoset_opts['fee_estimate_mode'].choices[0], dsl=help_notes('dfl_seed_len'), cu=proto.coin), - 'notes': lambda help_notes,s: s.format( + 'notes': lambda cfg,help_notes,s: s.format( c = help_notes('txcreate'), F = help_notes('fee'), s = help_notes('txsign'), @@ -124,34 +123,32 @@ FMT CODES: } } -cmd_args = opts.init(opts_data) +cfg = opts.init(opts_data) from .tx import NewTX,OnlineSignedTX from .tx.sign import txsign,get_seed_files,get_keyaddrlist,get_keylist -seed_files = get_seed_files(opt,cmd_args) +seed_files = get_seed_files(cfg,cfg._args) async def main(): - from .protocol import init_proto_from_opts - proto = init_proto_from_opts(need_amt=True) - tx1 = await NewTX(proto=proto) + tx1 = await NewTX(cfg=cfg,proto=cfg._proto) from .rpc import rpc_init - tx1.rpc = await rpc_init(proto) + tx1.rpc = await rpc_init(cfg,cfg._proto) tx2 = await tx1.create( - cmd_args = cmd_args, - locktime = int(opt.locktime or 0), + cmd_args = cfg._args, + locktime = int(cfg.locktime or 0), caller = 'txdo' ) - kal = get_keyaddrlist(proto,opt) - kl = get_keylist(proto,opt) + kal = get_keyaddrlist(cfg,cfg._proto) + kl = get_keylist(cfg,cfg._proto) - tx3 = await txsign(tx2,seed_files,kl,kal) + tx3 = await txsign(cfg,tx2,seed_files,kl,kal) if tx3: - tx4 = await OnlineSignedTX(data=tx3.__dict__) + tx4 = await OnlineSignedTX(cfg=cfg,data=tx3.__dict__) tx4.file.write(ask_write=False) await tx4.send(exit_on_fail=True) tx4.file.write(ask_overwrite=False,ask_write=False) diff --git a/mmgen/main_txsend.py b/mmgen/main_txsend.py index df941a25..f2cd0b77 100755 --- a/mmgen/main_txsend.py +++ b/mmgen/main_txsend.py @@ -24,8 +24,7 @@ import sys import mmgen.opts as opts from .globalvars import gc -from .opts import opt -from .util import vmsg,qmsg,async_run +from .util import async_run opts_data = { 'sets': [('yes', True, 'quiet', True)], @@ -43,39 +42,40 @@ opts_data = { } } -cmd_args = opts.init(opts_data) +cfg = opts.init(opts_data) -if len(cmd_args) == 1: - infile = cmd_args[0] +if len(cfg._args) == 1: + infile = cfg._args[0] from .fileutil import check_infile check_infile(infile) else: opts.usage() -if not opt.status: +if not cfg.status: from .ui import do_license_msg - do_license_msg() + do_license_msg(cfg) async def main(): from .tx import OnlineSignedTX tx = await OnlineSignedTX( + cfg = cfg, filename = infile, quiet_open = True ) from .rpc import rpc_init - tx.rpc = await rpc_init(tx.proto) + tx.rpc = await rpc_init(cfg,tx.proto) - vmsg(f'Signed transaction file {infile!r} is valid') + cfg._util.vmsg(f'Signed transaction file {infile!r} is valid') - if opt.status: + if cfg.status: if tx.coin_txid: - qmsg(f'{tx.proto.coin} txid: {tx.coin_txid.hl()}') + cfg._util.qmsg(f'{tx.proto.coin} txid: {tx.coin_txid.hl()}') await tx.status.display(usr_req=True) sys.exit(0) - if not opt.yes: + if not cfg.yes: tx.info.view_with_prompt('View transaction details?') if tx.add_comment(): # edits an existing comment, returns true if changed tx.file.write(ask_write_default_yes=True) diff --git a/mmgen/main_txsign.py b/mmgen/main_txsign.py index 7fc77559..d211a9f8 100755 --- a/mmgen/main_txsign.py +++ b/mmgen/main_txsign.py @@ -22,7 +22,6 @@ mmgen-txsign: Sign a transaction generated by 'mmgen-txcreate' import mmgen.opts as opts from .globalvars import gc -from .opts import opt from .util import msg,ymsg,die,async_run from .subseed import SubSeedIdxRange from .wallet import Wallet @@ -83,7 +82,8 @@ FMT CODES: """ }, 'code': { - 'options': lambda proto,help_notes,s: s.format( + 'options': lambda cfg,proto,help_notes,s: s.format( + cfg=cfg, gc=gc, pnm=gc.proj_name, pnl=gc.proj_name.lower(), @@ -99,7 +99,9 @@ FMT CODES: } } -infiles = opts.init(opts_data) +cfg = opts.init(opts_data) + +infiles = cfg._args if not infiles: opts.usage() @@ -108,14 +110,14 @@ from .fileutil import check_infile for i in infiles: check_infile(i) -if not opt.info and not opt.terse_info: +if not cfg.info and not cfg.terse_info: from .ui import do_license_msg - do_license_msg(immed=True) + do_license_msg(cfg,immed=True) from .tx.sign import * -tx_files = get_tx_files(opt,infiles) -seed_files = get_seed_files(opt,infiles) +tx_files = get_tx_files(cfg,infiles) +seed_files = get_seed_files(cfg,infiles) async def main(): @@ -129,33 +131,33 @@ async def main(): msg(orange(f'\nTransaction{tx_num_disp} of {len(tx_files)}:')) from .tx import UnsignedTX - tx1 = UnsignedTX(filename=tx_file) + tx1 = UnsignedTX(cfg=cfg,filename=tx_file) - vmsg(f'Successfully opened transaction file {tx_file!r}') + cfg._util.vmsg(f'Successfully opened transaction file {tx_file!r}') if tx1.proto.sign_mode == 'daemon': from .rpc import rpc_init - tx1.rpc = await rpc_init(tx1.proto) + tx1.rpc = await rpc_init(cfg,tx1.proto) - if opt.tx_id: + if cfg.tx_id: msg(tx1.txid) continue - if opt.info or opt.terse_info: - tx1.view(pause=False,terse=opt.terse_info) + if cfg.info or cfg.terse_info: + tx1.view(pause=False,terse=cfg.terse_info) continue - if not opt.yes: + if not cfg.yes: tx1.info.view_with_prompt(f'View data for transaction{tx_num_disp}?') - kal = get_keyaddrlist(tx1.proto,opt) - kl = get_keylist(tx1.proto,opt) + kal = get_keyaddrlist(cfg,tx1.proto) + kl = get_keylist(cfg,tx1.proto) - tx2 = await txsign(tx1,seed_files,kl,kal,tx_num_disp) + tx2 = await txsign(cfg,tx1,seed_files,kl,kal,tx_num_disp) if tx2: - if not opt.yes: + if not cfg.yes: tx2.add_comment() # edits an existing comment - tx2.file.write(ask_write=not opt.yes,ask_write_default_yes=True,add_desc=tx_num_disp) + tx2.file.write(ask_write=not cfg.yes,ask_write_default_yes=True,add_desc=tx_num_disp) else: ymsg('Transaction could not be signed') bad_tx_count += 1 diff --git a/mmgen/main_wallet.py b/mmgen/main_wallet.py index 4bb9544f..1bbaa049 100755 --- a/mmgen/main_wallet.py +++ b/mmgen/main_wallet.py @@ -22,10 +22,9 @@ main_wallet: Entry point for MMGen wallet-related scripts import sys,os import mmgen.opts as opts -from .globalvars import g,gc -from .opts import opt +from .globalvars import gc from .color import green,yellow -from .util import msg,qmsg,vmsg,gmsg_r,ymsg,bmsg,die,capfirst +from .util import msg,gmsg_r,ymsg,bmsg,die,capfirst from .wallet import Wallet,get_wallet_cls usage = '[opts] [infile]' @@ -113,7 +112,7 @@ opts_data = { -P, --passwd-file= f Get wallet passphrase from file 'f' -q, --quiet Produce quieter output; suppress some warnings -r, --usr-randchars=n Get 'n' characters of additional randomness from user - (min={g.min_urandchars}, max={g.max_urandchars}, default={g.usr_randchars}) + (min={cfg.min_urandchars}, max={cfg.max_urandchars}, default={cfg.usr_randchars}) -S, --stdout Write wallet data to stdout instead of file -v, --verbose Produce more verbose output """, @@ -127,16 +126,16 @@ FMT CODES: """ }, 'code': { - 'options': lambda help_notes,s: s.format( + 'options': lambda cfg,help_notes,s: s.format( iaction=capfirst(iaction), oaction=capfirst(oaction), ms_min=help_notes('MasterShareIdx').min_val, ms_max=help_notes('MasterShareIdx').max_val, dsl=help_notes('dfl_seed_len'), - g=g, + cfg=cfg, gc=gc, ), - 'notes': lambda help_notes,s: s.format( + 'notes': lambda cfg,help_notes,s: s.format( f=help_notes('fmt_codes'), n_ss=('',help_notes('seedsplit')+'\n\n')[do_ss_note], n_sw=('',help_notes('subwallet')+'\n\n')[do_sw_note], @@ -146,7 +145,9 @@ FMT CODES: } } -cmd_args = opts.init(opts_data,opt_filter=opt_filter,need_proto=False) +cfg = opts.init(opts_data,opt_filter=opt_filter,need_proto=False) + +cmd_args = cfg._args if invoked_as == 'subgen': from .subseed import SubSeedIdx @@ -154,7 +155,7 @@ if invoked_as == 'subgen': elif invoked_as == 'seedsplit': from .obj import get_obj from .seedsplit import SeedSplitSpecifier,MasterShareIdx - master_share = MasterShareIdx(opt.master_share) if opt.master_share else None + master_share = MasterShareIdx(cfg.master_share) if cfg.master_share else None if cmd_args: sss = get_obj(SeedSplitSpecifier,s=cmd_args.pop(),silent=True) if master_share: @@ -178,26 +179,27 @@ if cmd_args: opts.usage() check_infile(cmd_args[0]) -sf = get_seed_file(cmd_args,nargs,invoked_as=invoked_as) +sf = get_seed_file(cfg,nargs,invoked_as=invoked_as) if invoked_as != 'chk': from .ui import do_license_msg - do_license_msg() + do_license_msg(cfg) if invoked_as == 'gen': ss_in = None else: ss_in = Wallet( - sf, + cfg = cfg, + fn = sf, passchg = invoked_as=='passchg' ) m1 = green('Processing input wallet ') m2 = ss_in.seed.sid.hl() - m3 = yellow(' (default wallet)') if sf and os.path.dirname(sf) == g.data_dir else '' + m3 = yellow(' (default wallet)') if sf and os.path.dirname(sf) == cfg.data_dir else '' msg(m1+m2+m3) if invoked_as == 'chk': lbl = ss_in.ssdata.label.hl() if hasattr(ss_in.ssdata,'label') else 'NONE' - vmsg(f'Wallet label: {lbl}') + cfg._util.vmsg(f'Wallet label: {lbl}') # TODO: display creation date sys.exit(0) @@ -206,20 +208,23 @@ if invoked_as != 'gen': if invoked_as == 'subgen': ss_out = Wallet( + cfg = cfg, seed_bin = ss_in.seed.subseed(ss_idx,print_msg=True).data ) elif invoked_as == 'seedsplit': shares = ss_in.seed.split(sss.count,sss.id,master_share) seed_out = shares.get_share_by_idx(sss.idx,base_seed=True) msg(seed_out.get_desc(ui=True)) ss_out = Wallet( + cfg = cfg, seed = seed_out ) else: ss_out = Wallet( + cfg = cfg, ss = ss_in, passchg = invoked_as == 'passchg' ) if invoked_as == 'gen': - qmsg(f"This wallet's Seed ID: {ss_out.seed.sid.hl()}") + cfg._util.qmsg(f"This wallet's Seed ID: {ss_out.seed.sid.hl()}") if invoked_as == 'passchg': def data_changed(attrs): @@ -227,7 +232,7 @@ if invoked_as == 'passchg': if getattr(ss_out.ssdata,attr) != getattr(ss_in.ssdata,attr): return True return False - if not ( opt.force_update or data_changed(('passwd','hash_preset','label')) ): + if not ( cfg.force_update or data_changed(('passwd','hash_preset','label')) ): die(1,'Password, hash preset and label are unchanged. Taking no action') if invoked_as == 'passchg': @@ -235,7 +240,7 @@ if invoked_as == 'passchg': def secure_delete(fn): bmsg('Securely deleting old wallet') from .fileutil import shred_file - shred_file( fn, verbose = opt.verbose ) + shred_file( fn, verbose = cfg.verbose ) def rename_old_wallet_maybe(silent): # though very unlikely, old and new wallets could have same Key ID and thus same filename. @@ -251,31 +256,33 @@ if invoked_as == 'passchg': else: return old_fn - if ss_in.infile.dirname == g.data_dir: + if ss_in.infile.dirname == cfg.data_dir: from .ui import confirm_or_raise confirm_or_raise( + cfg = cfg, message = yellow('Confirmation of default wallet update'), action = 'update the default wallet', exit_msg = 'Password not changed' ) old_wallet = rename_old_wallet_maybe(silent=True) - ss_out.write_to_file( desc='New wallet', outdir=g.data_dir ) + ss_out.write_to_file( desc='New wallet', outdir=cfg.data_dir ) secure_delete( old_wallet ) else: old_wallet = rename_old_wallet_maybe(silent=False) ss_out.write_to_file() from .ui import keypress_confirm - if keypress_confirm(f'Securely delete old wallet {old_wallet!r}?'): + if keypress_confirm( cfg, f'Securely delete old wallet {old_wallet!r}?' ): secure_delete( old_wallet ) -elif invoked_as == 'gen' and not opt.outdir and not opt.stdout: +elif invoked_as == 'gen' and not cfg.outdir and not cfg.stdout: from .filename import find_file_in_dir - if find_file_in_dir( get_wallet_cls('mmgen'), g.data_dir ): + if find_file_in_dir( get_wallet_cls('mmgen'), cfg.data_dir ): ss_out.write_to_file() else: from .ui import keypress_confirm if keypress_confirm( + cfg, 'Make this wallet your default and move it to the data directory?', default_yes = True ): - ss_out.write_to_file(outdir=g.data_dir) + ss_out.write_to_file(outdir=cfg.data_dir) else: ss_out.write_to_file() else: diff --git a/mmgen/main_xmrwallet.py b/mmgen/main_xmrwallet.py index 68b478ae..5450d2fc 100755 --- a/mmgen/main_xmrwallet.py +++ b/mmgen/main_xmrwallet.py @@ -21,7 +21,11 @@ mmgen-xmrwallet: Perform various Monero wallet operations for addresses in an MMGen XMR key-address file """ -from .common import * +from collections import namedtuple + +import mmgen.opts as opts +from .globalvars import gc +from .util import ymsg,die,async_run from .xmrwallet import xmrwallet_uarg_info,MoneroWalletOps opts_data = { @@ -58,9 +62,9 @@ opts_data = { -S, --no-stop-wallet-daemon Don’t stop the wallet daemon at exit -w, --wallet-dir=D Output or operate on wallets in directory 'D' instead of the working directory --H, --wallet-rpc-host=host Wallet RPC hostname (default: {g.monero_wallet_rpc_host!r}) --U, --wallet-rpc-user=user Wallet RPC username (default: {g.monero_wallet_rpc_user!r}) --P, --wallet-rpc-password=pass Wallet RPC password (default: {g.monero_wallet_rpc_password!r}) +-H, --wallet-rpc-host=host Wallet RPC hostname (default: {cfg.monero_wallet_rpc_host!r}) +-U, --wallet-rpc-user=user Wallet RPC username (default: {cfg.monero_wallet_rpc_user!r}) +-P, --wallet-rpc-password=pass Wallet RPC password (default: {cfg.monero_wallet_rpc_password!r}) """, 'notes': """ @@ -212,16 +216,18 @@ $ mmgen-xmrwallet --pager txview *XMR*.sigtx """ }, 'code': { - 'options': lambda s: s.format( + 'options': lambda cfg,s: s.format( D=xmrwallet_uarg_info['daemon'].annot, R=xmrwallet_uarg_info['tx_relay_daemon'].annot, - g=g, + cfg=cfg, gc=gc, ), } } -cmd_args = opts.init(opts_data) +cfg = opts.init(opts_data) + +cmd_args = cfg._args if len(cmd_args) < 2: opts.usage() @@ -263,17 +269,17 @@ uo = namedtuple('uopts',[ uargs = ua( op, infile, wallets, spec ) uopts = uo( - opt.daemon or '', - opt.tx_relay_daemon or '', - opt.restore_height or 0, - opt.rescan_blockchain, - opt.no_start_wallet_daemon, - opt.no_stop_wallet_daemon, - opt.no_relay, - opt.wallet_dir, + cfg.daemon or '', + cfg.tx_relay_daemon or '', + cfg.restore_height or 0, + cfg.rescan_blockchain, + cfg.no_start_wallet_daemon, + cfg.no_stop_wallet_daemon, + cfg.no_relay, + cfg.wallet_dir, ) -m = getattr(MoneroWalletOps,op)(uargs,uopts) +m = getattr(MoneroWalletOps,op)(cfg,uargs,uopts) try: if async_run(m.main()): diff --git a/mmgen/mn_entry.py b/mmgen/mn_entry.py index d7d6bb8f..5fd7621e 100755 --- a/mmgen/mn_entry.py +++ b/mmgen/mn_entry.py @@ -23,7 +23,7 @@ mn_entry.py - Mnemonic user entry methods for the MMGen suite import time from .globalvars import * -from .util import msg,msg_r,qmsg,fmt,fmt_list,capfirst,die,ascii_lowercase +from .util import msg,msg_r,fmt,fmt_list,capfirst,die,ascii_lowercase from .term import get_char,get_char_raw from .color import cyan @@ -232,7 +232,8 @@ class MnemonicEntry(object): _sw = None _usl = None - def __init__(self): + def __init__(self,cfg): + self.cfg = cfg self.set_dfl_entry_mode() @property @@ -321,7 +322,7 @@ class MnemonicEntry(object): return em_objs[int(uret)-1] else: msg_r(f'\b {uret!r}: invalid choice ') - time.sleep(g.err_disp_timeout) + time.sleep(self.cfg.err_disp_timeout) msg_r(erase) def get_mnemonic_from_user(self,mn_len,validate=True): @@ -350,7 +351,7 @@ class MnemonicEntry(object): sw = self.shortest_word, )) - clear_line = '\n' if g.test_suite else '{r}{s}{r}'.format(r='\r',s=' '*40) + clear_line = '\n' if self.cfg.test_suite else '{r}{s}{r}'.format(r='\r',s=' '*40) idx,idxs = 1,[] # initialize idx to a non-None value while len(idxs) < mn_len: @@ -366,7 +367,7 @@ class MnemonicEntry(object): if validate: self.bconv.tohex(words) - qmsg( + self.cfg._util.qmsg( 'Mnemonic is valid' if self.has_chksum else 'Mnemonic is well-formed (mnemonic format has no checksum to validate)' ) @@ -389,7 +390,7 @@ class MnemonicEntry(object): In addition to setting the default entry mode for the current wordlist, checks validity of all user-configured entry modes """ - for k,v in g.mnemonic_entry_modes.items(): + for k,v in self.cfg.mnemonic_entry_modes.items(): cls = self.get_cls_by_wordlist(k) if v not in cls.entry_modes: errmsg = """ @@ -422,10 +423,10 @@ class MnemonicEntryMonero(MnemonicEntry): dfl_entry_mode = 'short' has_chksum = True -def mn_entry(wl_id,entry_mode=None): +def mn_entry(cfg,wl_id,entry_mode=None): if wl_id == 'words': wl_id = 'mmgen' - me = MnemonicEntry.get_cls_by_wordlist(wl_id)() + me = MnemonicEntry.get_cls_by_wordlist(wl_id)(cfg) import importlib me.bconv = getattr(importlib.import_module(f'mmgen.{me.modname}'),me.modname)(wl_id) me.wl = me.bconv.digits diff --git a/mmgen/msg.py b/mmgen/msg.py index ada61e1d..df4278af 100755 --- a/mmgen/msg.py +++ b/mmgen/msg.py @@ -51,6 +51,9 @@ class coin_msg: class base(MMGenObject): + def __init__(self,cfg): + self.cfg = cfg + ext = 'rawmsg.json' signed = False chksum_keys = ('addrlists','message','msghash_type','network') @@ -85,10 +88,10 @@ class coin_msg: return f'{self.filename_stem}.{coin_msg.signed.ext}' def get_proto_from_file(self,filename): - data = json.loads(get_data_from_file(filename)) + data = json.loads(get_data_from_file( self.cfg, filename )) network_id = data['metadata']['network'] if 'metadata' in data else data['network'].lower() coin,network = network_id.split('_') - return init_proto( coin=coin, network=network ) + return init_proto( cfg=self.cfg, coin=coin, network=network ) def write_to_file(self,outdir=None,ask_overwrite=False): data = { @@ -98,6 +101,7 @@ class coin_msg: } write_data_to_file( + cfg = self.cfg, outfile = os.path.join(outdir or '',self.filename), data = json.dumps(data,sort_keys=True,indent=4), desc = self.desc, @@ -105,10 +109,15 @@ class coin_msg: class new(base): - def __init__(self,message,addrlists,msghash_type,*args,**kwargs): + def __init__(self,cfg,message,addrlists,msghash_type,*args,**kwargs): + + self.cfg = cfg + msghash_type = msghash_type or self.msg_cls.msghash_types[0] + if msghash_type not in self.msg_cls.msghash_types: die(2,f'msghash_type {msghash_type!r} not supported for {self.proto.base_proto} protocol') + self.data = { 'network': '{}_{}'.format( self.proto.coin.lower(), self.proto.network ), 'addrlists': [MMGenIDRange(self.proto,i) for i in addrlists.split()], @@ -119,13 +128,16 @@ class coin_msg: class completed(base): - def __init__(self,data,infile,*args,**kwargs): + def __init__(self,cfg,data,infile,*args,**kwargs): + + self.cfg = cfg if data: self.__dict__ = data return self.data = get_data_from_file( + cfg = self.cfg, infile = infile, desc = self.desc ) @@ -210,6 +222,7 @@ class coin_msg: async def sign_list(al_in,seed): al = KeyAddrList( + cfg = self.cfg, proto = self.proto, seed = seed, addr_idxs = al_in.idxlist, @@ -238,11 +251,11 @@ class coin_msg: if self.proto.sign_mode == 'daemon': from .rpc import rpc_init - self.rpc = await rpc_init(self.proto) + self.rpc = await rpc_init(self.cfg,self.proto) from .wallet import Wallet from .addrlist import KeyAddrList - wallet_seeds = [Wallet(fn=fn).seed for fn in wallet_files] + wallet_seeds = [Wallet(cfg=self.cfg,fn=fn).seed for fn in wallet_files] need_sids = remove_dups([al.sid for al in self.addrlists], quiet=True) saved_seeds = list() @@ -302,7 +315,7 @@ class coin_msg: if self.proto.sign_mode == 'daemon': from .rpc import rpc_init - self.rpc = await rpc_init(self.proto) + self.rpc = await rpc_init(self.cfg,self.proto) for k,v in sigs.items(): ret = await self.do_verify( @@ -333,10 +346,13 @@ class coin_msg: class exported_sigs(signed_online): - def __init__(self,infile,*args,**kwargs): + def __init__(self,cfg,infile,*args,**kwargs): + + self.cfg = cfg self.data = json.loads( get_data_from_file( + cfg = self.cfg, infile = infile, desc = self.desc ) ) @@ -348,7 +364,7 @@ class coin_msg: self.data['signatures'] )} -def _get_obj(clsname,coin=None,network='mainnet',infile=None,data=None,*args,**kwargs): +def _get_obj(clsname,cfg,coin=None,network='mainnet',infile=None,data=None,*args,**kwargs): assert not args, 'msg:_get_obj(): only keyword args allowed' @@ -359,8 +375,8 @@ def _get_obj(clsname,coin=None,network='mainnet',infile=None,data=None,*args,**k proto = ( data['proto'] if data else - init_proto( coin=coin, network=network ) if coin else - coin_msg.base().get_proto_from_file(infile) ) + init_proto( cfg=cfg, coin=coin, network=network ) if coin else + coin_msg.base(cfg=cfg).get_proto_from_file(infile) ) try: msg_cls = getattr( @@ -373,7 +389,7 @@ def _get_obj(clsname,coin=None,network='mainnet',infile=None,data=None,*args,**k me.msg_cls = msg_cls me.proto = proto - me.__init__(infile=infile,data=data,*args,**kwargs) + me.__init__(cfg,infile=infile,data=data,*args,**kwargs) return me diff --git a/mmgen/objmethods.py b/mmgen/objmethods.py index c138260c..b677f625 100755 --- a/mmgen/objmethods.py +++ b/mmgen/objmethods.py @@ -21,7 +21,6 @@ objmethods: Mixin classes for MMGen data objects """ import unicodedata -from .globalvars import g import mmgen.color as color_mod if 'MMGenObjectDevTools' in __builtins__: # added to builtins by devinit.init_dev() diff --git a/mmgen/opts.py b/mmgen/opts.py index 1dd87ca9..64d1c2c8 100755 --- a/mmgen/opts.py +++ b/mmgen/opts.py @@ -21,17 +21,12 @@ opts: MMGen-specific options processing after generic processing by share.Opts """ import sys,os -from .globalvars import g,gc +from .globalvars import gc,Config from .base_obj import Lockable import mmgen.share.Opts -class UserOpts(Lockable): - _autolock = False - _default_to_none = True - _set_ok = ('usr_randchars',) - _reset_ok = ('quiet','verbose','yes') - +class UserOpts: pass opt = UserOpts() def usage(): @@ -54,22 +49,23 @@ def delete_data(opts_data): del mmgen.share.Opts.process_uopts del mmgen.share.Opts.parse_opts -def post_init(): +def post_init(cfg): global opts_data_save,opt_filter_save - if opt.help or opt.longhelp: - print_help(opt,opts_data_save,opt_filter_save) + if cfg.help or cfg.longhelp: + print_help(cfg,opts_data_save,opt_filter_save) else: delete_data(opts_data_save) del opts_data_save,opt_filter_save -def print_help(opt,opts_data,opt_filter): +def print_help(cfg,opts_data,opt_filter): + if not 'code' in opts_data: opts_data['code'] = {} - from .protocol import init_proto_from_opts - proto = init_proto_from_opts(need_amt=True) + from .protocol import init_proto_from_cfg + proto = init_proto_from_cfg(cfg,need_amt=True) - if getattr(opt,'longhelp',None): + if getattr(cfg,'longhelp',None): opts_data['code']['long_options'] = common_opts_data['code'] def remove_unneeded_long_opts(): d = opts_data['text']['long_options'] @@ -79,16 +75,16 @@ def print_help(opt,opts_data,opt_filter): remove_unneeded_long_opts() from .ui import do_pager - do_pager(mmgen.share.Opts.make_help( proto, opt, opts_data, opt_filter )) + do_pager(mmgen.share.Opts.make_help( cfg, proto, opts_data, opt_filter )) sys.exit(0) def fmt_opt(o): return '--' + o.replace('_','-') -def die_on_incompatible_opts(incompat_list): - for group in incompat_list: - bad = [k for k in opt.__dict__ if k in group and getattr(opt,k) != None] +def die_on_incompatible_opts(cfg): + for group in cfg.incompatible_opts: + bad = [k for k in cfg.__dict__ if k in group and getattr(cfg,k) != None] if len(bad) > 1: from .util import die die(1,'Conflicting options: {}'.format(', '.join(map(fmt_opt,bad)))) @@ -117,18 +113,14 @@ def opt_preproc_debug(po): for e in d: Msg(' {:<20}: {}'.format(*e)) -def opt_postproc_debug(): - a = [k for k in dir(opt) if k[:2] != '__' and getattr(opt,k) != None] - b = [k for k in dir(opt) if k[:2] != '__' and getattr(opt,k) == None] +def opt_postproc_debug(cfg): + a = [k for k in dir(cfg) if k[:2] != '__' and getattr(cfg,k) != None] + b = [k for k in dir(cfg) if k[:2] != '__' and getattr(cfg,k) == None] from .util import Msg - Msg(' Opts after processing:') - for k in a: - v = getattr(opt,k) - Msg(' {:18}: {!r:<6} [{}]'.format(k,v,type(v).__name__)) Msg(' Global vars:') - for e in [d for d in dir(g) if d[:2] != '__']: - Msg(' {:<20}: {}'.format(e, getattr(g,e))) - Msg(" Opts set to 'None':") + for e in [d for d in dir(cfg) if d[:2] != '__']: + Msg(' {:<20}: {}'.format(e, getattr(cfg,e))) + Msg(" Global vars set to 'None':") Msg(' {}\n'.format('\n '.join(b))) Msg('\n=== end opts.py debug ===\n') @@ -157,15 +149,18 @@ def set_for_type(val,refval,desc,invert_bool=False,src=None): type(refval).__name__) ) def override_globals_from_cfg_file( + cfg, ucfg, - autoset_opts, + cfgfile_autoset_opts, + cfgfile_auto_typeset_opts, env_globals, need_proto ): if need_proto: from .protocol import init_proto + for d in ucfg.get_lines(): - if d.name in g.cfg_file_opts: + if d.name in cfg.cfg_file_opts: ns = d.name.split('_') if ns[0] in gc.core_coins: if not need_proto: @@ -174,10 +169,10 @@ def override_globals_from_cfg_file( (ns[2:],ns[1]=='testnet') if len(ns) > 2 and ns[1] in ('mainnet','testnet') else (ns[1:],False) ) - cls = type(init_proto( ns[0], tn, need_amt=True )) # no instance yet, so override _class_ attr + cls = type(init_proto( cfg, ns[0], tn, need_amt=True )) # no instance yet, so override _class_ attr attr = '_'.join(nse) else: - cls = g # g is "singleton" instance, so override _instance_ attr + cls = cfg attr = d.name refval = getattr(cls,attr) val = ucfg.parse_value(d.value,refval) @@ -187,29 +182,31 @@ def override_globals_from_cfg_file( val_conv = set_for_type(val,refval,attr,src=ucfg.fn) if attr not in env_globals: setattr(cls,attr,val_conv) - elif d.name in g.autoset_opts: - autoset_opts[d.name] = d.value + elif d.name in cfg.autoset_opts: + cfgfile_autoset_opts[d.name] = d.value + elif d.name in cfg.auto_typeset_opts: + cfgfile_auto_typeset_opts[d.name] = d.value else: from .util import die die( 'CfgFileParseError', f'{d.name!r}: unrecognized option in {ucfg.fn!r}, line {d.lineno}' ) -def override_globals_from_env(): +def override_globals_from_env(cfg): for name,val in ((k,v) for k,v in os.environ.items() if k.startswith('MMGEN_')): if name == 'MMGEN_DEBUG_ALL': continue - elif name in g.env_opts: + elif name in cfg.env_opts: if val: # ignore empty string values; string value of '0' or 'false' sets variable to False disable = name.startswith('MMGEN_DISABLE_') gname = name[(6,14)[disable]:].lower() - if hasattr(g,gname): - setattr(g,gname,set_for_type(val,getattr(g,gname),name,disable)) + if hasattr(cfg,gname): + setattr(cfg,gname,set_for_type(val,getattr(cfg,gname),name,disable)) yield gname else: raise ValueError(f'Name {gname!r} not present in globals') else: raise ValueError(f'{name!r} is not a valid MMGen environment variable') -def show_common_opts_diff(): +def show_common_opts_diff(cfg): def common_opts_data_to_list(): for l in common_opts_data['text'].splitlines(): @@ -220,16 +217,16 @@ def show_common_opts_diff(): from .util import fmt_list return fmt_list(['--'+s.replace('_','-') for s in set_data],fmt='col',indent=' ') - a = g.common_opts + a = cfg.common_opts b = list(common_opts_data_to_list()) a_minus_b = [e for e in a if e not in b] b_minus_a = [e for e in b if e not in a] a_and_b = [e for e in a if e in b] from .util import msg - msg(f'g.common_opts - common_opts_data:\n {do_fmt(a_minus_b) if a_minus_b else "None"}\n') - msg(f'common_opts_data - g.common_opts (these do not set global var):\n{do_fmt(b_minus_a)}\n') - msg(f'common_opts_data ^ g.common_opts (these set global var):\n{do_fmt(a_and_b)}\n') + msg(f'cfg.common_opts - common_opts_data:\n {do_fmt(a_minus_b) if a_minus_b else "None"}\n') + msg(f'common_opts_data - cfg.common_opts (these do not set global var):\n{do_fmt(b_minus_a)}\n') + msg(f'common_opts_data ^ cfg.common_opts (these set global var):\n{do_fmt(a_and_b)}\n') sys.exit(0) @@ -291,8 +288,8 @@ def init( parse_only = False, parsed_opts = None, need_proto = True, - do_post_init = False, - return_parsed = False ): + need_amt = True, + do_post_init = False ): if opts_data is None: opts_data = opts_data_dfl @@ -314,7 +311,9 @@ def init( if parse_only and not any(k in po.user_opts for k in ('version','help','longhelp')): return po - if g.debug_opts: + cfg = Config() + + if cfg.debug_opts: # TODO: this does nothing opt_preproc_debug(po) # Copy parsed opts to opt, setting values to None if not set by user @@ -323,8 +322,8 @@ def init( + po.skipped_opts + tuple(add_opts or []) + tuple(init_opts or []) - + g.init_opts - + g.common_opts ): + + cfg.init_opts + + cfg.common_opts ): setattr(opt,o,po.user_opts[o] if o in po.user_opts else None) if opt.version: @@ -333,108 +332,110 @@ def init( if opt.show_hash_presets: _show_hash_presets() # exits - from .term import init_term - init_term() - from .util import wrap_ripemd160 wrap_ripemd160() # ripemd160 used by mmgen_cfg_file() - # === begin global var initialization === # + # === begin Config() initialization === # - env_globals = tuple(override_globals_from_env()) + env_globals = tuple(override_globals_from_env(cfg)) + + # do this after setting ‘hold_protect_disable’ from env + from .term import init_term + init_term(cfg) # --data-dir overrides computed value of data_dir_root - g.data_dir_root_override = opt.data_dir + cfg.data_dir_root_override = opt.data_dir + if opt.data_dir: + del opt.data_dir from .fileutil import check_or_create_dir - check_or_create_dir(g.data_dir_root) + check_or_create_dir(cfg.data_dir_root) cfgfile_autoset_opts = {} + cfgfile_auto_typeset_opts = {} if not opt.skip_cfg_file: from .cfgfile import mmgen_cfg_file # check for changes in system template file - term must be initialized - mmgen_cfg_file('sample') + mmgen_cfg_file(cfg,'sample') override_globals_from_cfg_file( - mmgen_cfg_file('usr'), + cfg, + mmgen_cfg_file(cfg,'usr'), cfgfile_autoset_opts, + cfgfile_auto_typeset_opts, env_globals, need_proto ) - # Set globals from opts, setting type from original global value - # Do here, before opts are set from globals below - for k in (g.common_opts + g.opt_sets_global): - if hasattr(opt,k): - val = getattr(opt,k) - if val != None and hasattr(g,k): - setattr(g,k,set_for_type(val,getattr(g,k),'--'+k)) + # Set globals from opts, setting type from original global value if it exists: + auto_keys = tuple(cfg.autoset_opts.keys()) + tuple(cfg.auto_typeset_opts.keys()) + for key,val in opt.__dict__.items(): + if val is not None and key not in auto_keys: + setattr(cfg, key, set_for_type(val,getattr(cfg,key),'--'+key) if hasattr(cfg,key) else val) - if g.regtest or g.bob or g.alice or g.carol or gc.prog_name == 'mmgen-regtest': - g.network = 'regtest' - g.regtest_user = 'bob' if g.bob else 'alice' if g.alice else 'carol' if g.carol else None + if cfg.regtest or cfg.bob or cfg.alice or cfg.carol or gc.prog_name == 'mmgen-regtest': + cfg.network = 'regtest' + cfg.regtest_user = 'bob' if cfg.bob else 'alice' if cfg.alice else 'carol' if cfg.carol else None else: - g.network = 'testnet' if g.testnet else 'mainnet' + cfg.network = 'testnet' if cfg.testnet else 'mainnet' - g.coin = g.coin.upper() or 'BTC' - g.token = g.token.upper() or None + cfg.coin = cfg.coin.upper() + cfg.token = cfg.token.upper() or None # === end global var initialization === # """ - g.color is finalized, so initialize color + cfg.color is finalized, so initialize color """ - if g.color: # MMGEN_DISABLE_COLOR sets this to False + if cfg.color: # MMGEN_DISABLE_COLOR sets this to False from .color import init_color - init_color(num_colors=('auto',256)[bool(g.force_256_color)]) + init_color(num_colors=('auto',256)[bool(cfg.force_256_color)]) - # Set user opts from globals: - # - if opt is unset, set it to global value - # - if opt is set, convert its type to that of global value - for k in g.global_sets_opt: - if hasattr(opt,k) and getattr(opt,k) != None: - setattr(opt,k,set_for_type(getattr(opt,k),getattr(g,k),'--'+k)) - else: - setattr(opt,k,getattr(g,k)) + die_on_incompatible_opts(cfg) - if need_proto: - from .protocol import warn_trustlevel - warn_trustlevel(g.coin) - - die_on_incompatible_opts(g.incompatible_opts) - - check_or_create_dir(g.data_dir) - - # Check user-set opts without modifying them - check_usr_opts(po.user_opts) + check_or_create_dir(cfg.data_dir) # Check autoset opts, setting if unset - for key in g.autoset_opts: + for key in cfg.autoset_opts: if hasattr(opt,key): + assert not hasattr(cfg,key), f'{key!r} is in cfg!' if getattr(opt,key) is not None: - setattr(opt, key, get_autoset_opt(key,getattr(opt,key),src='cmdline')) + setattr(cfg, key, get_autoset_opt(cfg,key,getattr(opt,key),src='cmdline')) elif key in cfgfile_autoset_opts: - setattr(opt, key, get_autoset_opt(key,cfgfile_autoset_opts[key],src='cfgfile')) + setattr(cfg, key, get_autoset_opt(cfg,key,cfgfile_autoset_opts[key],src='cfgfile')) else: - setattr(opt, key, g.autoset_opts[key].choices[0]) + setattr(cfg, key, cfg.autoset_opts[key].choices[0]) - set_auto_typeset_opts() + set_auto_typeset_opts(cfg,cfgfile_auto_typeset_opts) - if opt.verbose: - opt.quiet = None + if cfg.debug and gc.prog_name != 'test.py': + cfg.verbose = True - if g.debug and gc.prog_name != 'test.py': - opt.verbose,opt.quiet = (True,None) + if cfg.verbose: + cfg.quiet = False - if g.debug_opts: - opt_postproc_debug() + if cfg.debug_opts: + opt_postproc_debug(cfg) - g.lock() - opt.lock() + # Check user-set opts without modifying them + check_usr_opts(cfg,po.user_opts) - # print help screen only after globals and opts initialized and locked: - if opt.help or opt.longhelp: + from .util import Util + cfg._util = Util(cfg) + cfg._uopts = po.user_opts + cfg._args = po.cmd_args + + cfg.lock() + + if need_proto: + from .protocol import warn_trustlevel,init_proto_from_cfg + warn_trustlevel(cfg) + # requires the default-to-none behavior, so do after the lock: + cfg._proto = init_proto_from_cfg(cfg,need_amt=need_amt) + + # print help screen only after globals initialized and locked: + if cfg.help or cfg.longhelp: if not do_post_init: - print_help(opt,opts_data,opt_filter) # exits + print_help(cfg,opts_data,opt_filter) # exits if do_post_init: global opts_data_save,opt_filter_save @@ -443,9 +444,9 @@ def init( else: delete_data(opts_data) - return po if return_parsed else po.cmd_args + return cfg -def check_usr_opts(usr_opts): # Raises an exception if any check fails +def check_usr_opts(cfg,usr_opts): # Raises an exception if any check fails def opt_splits(val,sep,n,desc): sepword = 'comma' if sep == ',' else 'colon' if sep == ':' else repr(sep) @@ -577,8 +578,8 @@ def check_usr_opts(usr_opts): # Raises an exception if any check fails if val == 0: return opt_is_int(val,desc) - opt_compares(val,'>=',g.min_urandchars,desc) - opt_compares(val,'<=',g.max_urandchars,desc) + opt_compares(val,'>=',cfg.min_urandchars,desc) + opt_compares(val,'<=',cfg.max_urandchars,desc) def chk_tx_fee(key,val,desc): pass @@ -605,7 +606,7 @@ def check_usr_opts(usr_opts): # Raises an exception if any check fails # def chk_bob(key,val,desc): # from .proto.btc.regtest import MMGenRegtest # try: -# os.stat(os.path.join(MMGenRegtest(g.coin).d.datadir,'regtest','debug.log')) +# os.stat(os.path.join(MMGenRegtest(cfg,cfg.coin).d.datadir,'regtest','debug.log')) # except: # die( 'UserOptError', # 'Regtest (Bob and Alice) mode not set up yet. ' + @@ -634,10 +635,10 @@ def check_usr_opts(usr_opts): # Raises an exception if any check fails cfuncs = { k:v for k,v in locals().items() if k.startswith('chk_') } for key in usr_opts: - val = getattr(opt,key) + val = getattr(cfg,key) desc = f'parameter for {fmt_opt(key)!r} option' - if key in g.infile_opts: + if key in cfg.infile_opts: from .fileutil import check_infile check_infile(val) # file exists and is readable - dies on error elif key == 'outdir': @@ -645,19 +646,22 @@ def check_usr_opts(usr_opts): # Raises an exception if any check fails check_outdir(val) # dies on error elif 'chk_'+key in cfuncs: cfuncs['chk_'+key](key,val,desc) - elif g.debug: + elif cfg.debug: Msg(f'check_usr_opts(): No test for opt {key!r}') -def set_auto_typeset_opts(): +def set_auto_typeset_opts(cfg,cfgfile_auto_typeset_opts): def do_set(key,val,ref_type): - setattr(opt,key,None if val is None else ref_type(val)) + assert not hasattr(cfg,key), f'{key!r} is in cfg!' + setattr(cfg,key,None if val is None else ref_type(val)) - for key,ref_type in g.auto_typeset_opts.items(): + for key,ref_type in cfg.auto_typeset_opts.items(): if hasattr(opt,key): do_set(key, getattr(opt,key), ref_type) + elif key in cfgfile_auto_typeset_opts: + do_set(key, cfgfile_auto_typeset_opts[key], ref_type) -def get_autoset_opt(key,val,src): +def get_autoset_opt(cfg,key,val,src): def die_on_err(desc): from .util import fmt_list,die @@ -687,6 +691,6 @@ def get_autoset_opt(key,val,src): else: die_on_err('unique substring of') - data = g.autoset_opts[key] + data = cfg.autoset_opts[key] return getattr(opt_type,data.type)() diff --git a/mmgen/passwdlist.py b/mmgen/passwdlist.py index 396c21e7..c7237a01 100755 --- a/mmgen/passwdlist.py +++ b/mmgen/passwdlist.py @@ -22,7 +22,6 @@ passwdlist: Password list class for the MMGen suite from collections import namedtuple -from .globalvars import g from .util import ymsg,is_int,die from .obj import ImmutableAttr,ListItemAttr,MMGenPWIDString,TwComment from .key import PrivKey @@ -66,6 +65,7 @@ class PasswordList(AddrList): def __init__( self, + cfg, proto, infile = None, seed = None, @@ -76,9 +76,10 @@ class PasswordList(AddrList): chk_params_only = False, skip_chksum_msg = False ): + self.cfg = cfg self.proto = proto # proto is ignored - if not g.debug_addrlist: + if not cfg.debug_addrlist: self.dmsg_sc = self.noop if infile: @@ -167,7 +168,7 @@ class PasswordList(AddrList): from .xmrseed import xmrseed from .protocol import init_proto self.xmrseed = xmrseed() - self.xmrproto = init_proto('xmr') + self.xmrproto = init_proto( self.cfg, 'xmr' ) pw_bytes = xmrseed().seedlen_map_rev[self.pw_len] try: good_pw_len = xmrseed().seedlen_map[seed.byte_len] @@ -193,6 +194,7 @@ class PasswordList(AddrList): if pf in ('bip39','hex') and pw_bytes < seed.byte_len: from .ui import keypress_confirm if not keypress_confirm( + self.cfg, f'WARNING: requested {self.pw_info[pf].desc} length has less entropy ' + 'than underlying seed!\nIs this what you want?', default_yes = True ): @@ -240,4 +242,4 @@ class PasswordList(AddrList): self.dmsg_sc('str',scramble_key) from .crypto import Crypto - return Crypto().scramble_seed(seed,scramble_key.encode()) + return Crypto(self.cfg).scramble_seed(seed,scramble_key.encode()) diff --git a/mmgen/proto/btc/addrdata.py b/mmgen/proto/btc/addrdata.py index 0d44d969..28dae6e2 100755 --- a/mmgen/proto/btc/addrdata.py +++ b/mmgen/proto/btc/addrdata.py @@ -13,7 +13,6 @@ proto.btc.addrdata: Bitcoin base protocol addrdata classes """ from ...addrdata import TwAddrData -from ...util import vmsg class BitcoinTwAddrData(TwAddrData): @@ -27,7 +26,7 @@ class BitcoinTwAddrData(TwAddrData): } async def get_tw_data(self,twctl=None): - vmsg('Getting address data from tracking wallet') + self.cfg._util.vmsg('Getting address data from tracking wallet') c = self.rpc if 'label_api' in c.caps: accts = await c.call('listlabels') diff --git a/mmgen/proto/btc/daemon.py b/mmgen/proto/btc/daemon.py index a35b50ec..0409c612 100755 --- a/mmgen/proto/btc/daemon.py +++ b/mmgen/proto/btc/daemon.py @@ -14,8 +14,7 @@ proto.btc.daemon: Bitcoin base protocol daemon classes import os -from ...globalvars import g,gc -from ...opts import opt +from ...globalvars import gc from ...util import list_gen from ...daemon import CoinDaemon,_nw,_dd @@ -36,7 +35,7 @@ class bitcoin_core_daemon(CoinDaemon): def init_datadir(self): if self.network == 'regtest' and not self.test_suite: - return os.path.join( g.data_dir_root, 'regtest', g.coin.lower() ) + return os.path.join( self.cfg.data_dir_root, 'regtest', self.cfg.coin.lower() ) else: return super().init_datadir() @@ -120,7 +119,7 @@ class bitcoin_core_daemon(CoinDaemon): return ('importaddress',coinaddr,lbl,False) def estimatefee_args(self,rpc): - return (opt.fee_estimate_confs,) + return (self.cfg.fee_estimate_confs,) def sigfail_errmsg(self,e): return e.args[0] @@ -143,7 +142,7 @@ class bitcoin_cash_node_daemon(bitcoin_core_daemon): return ('importaddress',coinaddr,lbl,False) def estimatefee_args(self,rpc): - return () if rpc.daemon_version >= 190100 else (opt.fee_estimate_confs,) + return () if rpc.daemon_version >= 190100 else (self.cfg.fee_estimate_confs,) def sigfail_errmsg(self,e): return ( diff --git a/mmgen/proto/btc/misc.py b/mmgen/proto/btc/misc.py index 7b0cbd93..112f6fe0 100755 --- a/mmgen/proto/btc/misc.py +++ b/mmgen/proto/btc/misc.py @@ -12,10 +12,9 @@ proto.btc.misc: miscellaneous functions for Bitcoin base protocol """ -from ...globalvars import g from ...util import msg,msg_r -async def scantxoutset(rpc,descriptor_list): +async def scantxoutset(cfg,rpc,descriptor_list): import asyncio @@ -28,8 +27,8 @@ async def scantxoutset(rpc,descriptor_list): async def do_status(): - CR = '\n' if g.test_suite else '\r' - sleep_secs = 0.1 if g.test_suite else 2 + CR = '\n' if cfg.test_suite else '\r' + sleep_secs = 0.1 if cfg.test_suite else 2 m = f'{CR}Scanning UTXO set: ' msg_r(m + '0% completed ') diff --git a/mmgen/proto/btc/regtest.py b/mmgen/proto/btc/regtest.py index c21af03e..a7a28563 100755 --- a/mmgen/proto/btc/regtest.py +++ b/mmgen/proto/btc/regtest.py @@ -21,19 +21,18 @@ proto.btc.regtest: Coin daemon regression test mode setup and operations """ import os,shutil,json -from ...opts import opt -from ...globalvars import g from ...util import msg,gmsg,die,capfirst,suf from ...protocol import init_proto from ...rpc import rpc_init,json_encoder from ...objmethods import MMGenObject -def create_data_dir(data_dir): +def create_data_dir(cfg,data_dir): try: os.stat(os.path.join(data_dir,'regtest')) except: pass else: from ...ui import keypress_confirm if keypress_confirm( + cfg, f'Delete your existing MMGen regtest setup at {data_dir!r} and create a new one?'): shutil.rmtree(data_dir) else: @@ -69,18 +68,19 @@ class MMGenRegtest(MMGenObject): 'bch': 'n2fxhNx27GhHAWQhyuZ5REcBNrJqCJsJ12', } - def __init__(self,coin): + def __init__(self,cfg,coin): + self.cfg = cfg self.coin = coin.lower() assert self.coin in self.coins, f'{coin!r}: invalid coin for regtest' from ...daemon import CoinDaemon - self.proto = init_proto(self.coin,regtest=True,need_amt=True) - self.d = CoinDaemon(self.coin+'_rt',test_suite=g.test_suite) + self.proto = init_proto( cfg, self.coin, regtest=True, need_amt=True ) + self.d = CoinDaemon(cfg,self.coin+'_rt',test_suite=cfg.test_suite) self.miner_addr = self.miner_addrs[self.coin] def create_hdseed_wif(self): from ...tool.api import tool_api - t = tool_api() + t = tool_api(self.cfg) t.init_coin(self.proto.coin,self.proto.network) t.addrtype = 'compressed' if self.proto.coin == 'BCH' else 'bech32' return t.hex2wif(self.hdseed) @@ -114,13 +114,13 @@ class MMGenRegtest(MMGenObject): if self.d.state != 'stopped': await self.rpc_call('stop') - create_data_dir(self.d.datadir) + create_data_dir( self.cfg, self.d.datadir ) gmsg(f'Starting {self.coin.upper()} regtest setup') self.d.start(silent=True) - rpc = await rpc_init(self.proto,backend=None,daemon=self.d) + rpc = await rpc_init(self.cfg,self.proto,backend=None,daemon=self.d) for user in ('miner','bob','alice'): gmsg(f'Creating {capfirst(user)}’s tracking wallet') await rpc.icall( @@ -149,7 +149,7 @@ class MMGenRegtest(MMGenObject): gmsg('Setup complete') - if opt.setup_no_stop_daemon: + if self.cfg.setup_no_stop_daemon: msg('Leaving regtest daemon running') else: msg('Stopping regtest daemon') @@ -169,20 +169,20 @@ class MMGenRegtest(MMGenObject): async def rpc_call(self,*args,wallet=None,start_daemon=True): if start_daemon and self.d.state == 'stopped': await self.start_daemon() - rpc = await rpc_init(self.proto,backend=None,daemon=self.d) + rpc = await rpc_init(self.cfg,self.proto,backend=None,daemon=self.d) return await rpc.call(*args,wallet=wallet) async def start(self): if self.d.state == 'stopped': await self.start_daemon(silent=False) else: - msg(f'{g.coin} regtest daemon already started') + msg(f'{self.cfg.coin} regtest daemon already started') async def stop(self): if self.d.state == 'stopped': - msg(f'{g.coin} regtest daemon already stopped') + msg(f'{self.cfg.coin} regtest daemon already stopped') else: - msg(f'Stopping {g.coin} regtest daemon') + msg(f'Stopping {self.cfg.coin} regtest daemon') self.d.stop(silent=True) def state(self): @@ -222,13 +222,13 @@ class MMGenRegtest(MMGenObject): async def fork(self,coin): # currently disabled - proto = init_proto(coin,False) + proto = init_proto( self.cfg, coin, False ) if not [f for f in proto.forks if f[2] == proto.coin.lower() and f[3] == True]: die(1,f'Coin {proto.coin} is not a replayable fork of coin {coin}') gmsg(f'Creating fork from coin {coin} to coin {proto.coin}') - source_rt = MMGenRegtest(coin) + source_rt = MMGenRegtest( self.cfg, coin ) try: os.stat(source_rt.d.datadir) @@ -246,7 +246,7 @@ class MMGenRegtest(MMGenObject): try: os.makedirs(self.d.datadir) except: pass - create_data_dir(self.d.datadir) + create_data_dir( self.cfg, self.d.datadir ) os.rmdir(self.d.datadir) shutil.copytree(source_data_dir,self.d.datadir,symlinks=True) await self.start_daemon(reindex=True) diff --git a/mmgen/proto/btc/rpc.py b/mmgen/proto/btc/rpc.py index be0ab4f9..4b234f34 100755 --- a/mmgen/proto/btc/rpc.py +++ b/mmgen/proto/btc/rpc.py @@ -14,9 +14,8 @@ proto.btc.rpc: Bitcoin base protocol RPC client class import os -from ...globalvars import g from ...base_obj import AsyncInit -from ...util import ymsg,vmsg,die,fmt +from ...util import ymsg,die,fmt from ...fileutil import get_lines_from_file from ...rpc import RPCClient,auth_data @@ -105,6 +104,7 @@ class BitcoinRPCClient(RPCClient,metaclass=AsyncInit): async def __init__( self, + cfg, proto, daemon, backend, @@ -115,7 +115,8 @@ class BitcoinRPCClient(RPCClient,metaclass=AsyncInit): self.call_sigs = getattr(CallSigs,daemon.id,None) super().__init__( - host = 'localhost' if g.test_suite else (g.rpc_host or 'localhost'), + cfg = cfg, + host = 'localhost' if cfg.test_suite else (cfg.rpc_host or 'localhost'), port = daemon.rpc_port ) self.set_auth() @@ -182,15 +183,15 @@ class BitcoinRPCClient(RPCClient,metaclass=AsyncInit): await self.check_or_create_daemon_wallet() # for regtest, wallet path must remain '/' until Carol’s user wallet has been created - if g.regtest_user: - self.wallet_path = f'/wallet/{g.regtest_user}' + if cfg.regtest_user: + self.wallet_path = f'/wallet/{cfg.regtest_user}' def set_auth(self): """ MMGen's credentials override coin daemon's """ - if g.rpc_user: - user,passwd = (g.rpc_user,g.rpc_password) + if self.cfg.rpc_user: + user,passwd = (self.cfg.rpc_user,self.cfg.rpc_password) else: user,passwd = self.get_daemon_cfg_options(('rpcuser','rpcpassword')).values() @@ -221,7 +222,7 @@ class BitcoinRPCClient(RPCClient,metaclass=AsyncInit): is created, False otherwise """ - if called or (self.chain == 'regtest' and g.regtest_user != 'carol'): + if called or (self.chain == 'regtest' and self.cfg.regtest_user != 'carol'): return False twname = self.daemon.tracking_wallet_name @@ -230,7 +231,7 @@ class BitcoinRPCClient(RPCClient,metaclass=AsyncInit): m = f'Please fix your {self.daemon.desc} wallet installation or cmdline options' ret = False - if g.carol: + if self.cfg.carol: if 'carol' in loaded_wnames: ret = True elif wallet_create: @@ -266,17 +267,16 @@ class BitcoinRPCClient(RPCClient,metaclass=AsyncInit): def get_daemon_cfg_fn(self): # Use dirname() to remove 'bob' or 'alice' component return os.path.join( - (os.path.dirname(g.data_dir) if self.proto.regtest else self.daemon.datadir), + (os.path.dirname(self.cfg.data_dir) if self.proto.regtest else self.daemon.datadir), self.daemon.cfg_file ) def get_daemon_cfg_options(self,req_keys): fn = self.get_daemon_cfg_fn() - from ...opts import opt try: - lines = get_lines_from_file(fn,'daemon config file',silent=not opt.verbose) + lines = get_lines_from_file( self.cfg, fn, 'daemon config file', silent=not self.cfg.verbose ) except: - vmsg(f'Warning: {fn!r} does not exist or is unreadable') + self.cfg._util.vmsg(f'Warning: {fn!r} does not exist or is unreadable') return dict((k,None) for k in req_keys) def gen(): @@ -293,7 +293,7 @@ class BitcoinRPCClient(RPCClient,metaclass=AsyncInit): def get_daemon_auth_cookie(self): fn = self.daemon.auth_cookie_fn - return get_lines_from_file(fn,'cookie',quiet=True)[0] if os.access(fn,os.R_OK) else '' + return get_lines_from_file( self.cfg, fn, 'cookie', quiet=True )[0] if os.access(fn,os.R_OK) else '' def info(self,info_id): @@ -302,7 +302,7 @@ class BitcoinRPCClient(RPCClient,metaclass=AsyncInit): if 'deployment_info' in self.caps: return ( self.cached['deploymentinfo']['deployments']['segwit']['active'] - or ( g.test_suite and not self.chain == 'regtest' ) + or ( self.cfg.test_suite and not self.chain == 'regtest' ) ) d = self.cached['blockchaininfo'] @@ -319,7 +319,7 @@ class BitcoinRPCClient(RPCClient,metaclass=AsyncInit): except: pass - if g.test_suite and not self.chain == 'regtest': + if self.cfg.test_suite and not self.chain == 'regtest': return True return False diff --git a/mmgen/proto/btc/tw/ctl.py b/mmgen/proto/btc/tw/ctl.py index ed050957..72b79a54 100755 --- a/mmgen/proto/btc/tw/ctl.py +++ b/mmgen/proto/btc/tw/ctl.py @@ -12,9 +12,8 @@ proto.btc.tw.ctl: Bitcoin base protocol tracking wallet control class """ -from ....globalvars import g from ....tw.ctl import TwCtl,write_mode -from ....util import msg,msg_r,rmsg,vmsg,die,suf,fmt_list +from ....util import msg,msg_r,rmsg,die,suf,fmt_list class BitcoinTwCtl(TwCtl): @@ -54,7 +53,7 @@ class BitcoinTwCtl(TwCtl): start = start or 0 endless = stop == None - CR = '\n' if g.test_suite else '\r' + CR = '\n' if self.cfg.test_suite else '\r' if not ( start >= 0 and (stop if stop is not None else start) >= start ): die(1,f'{start} {stop}: invalid range') @@ -101,7 +100,7 @@ class BitcoinTwCtl(TwCtl): async def rescan_addresses(self,coin_addrs): from ..misc import scantxoutset - res = await scantxoutset( self.rpc, [f'addr({addr})' for addr in coin_addrs] ) + res = await scantxoutset( self.cfg, self.rpc, [f'addr({addr})' for addr in coin_addrs] ) if not res['success']: msg('UTXO scanning failed or was interrupted') @@ -113,8 +112,8 @@ class BitcoinTwCtl(TwCtl): suf(res['unspents']), len(blocks), suf(blocks) )) - vmsg(f'Blocks to rescan: {fmt_list(blocks,fmt="bare")}') - CR = '\n' if g.test_suite else '\r' + self.cfg._util.vmsg(f'Blocks to rescan: {fmt_list(blocks,fmt="bare")}') + CR = '\n' if self.cfg.test_suite else '\r' for n,block in enumerate(blocks): msg_r(f'{CR}Rescanning block: {block} ({n+1}/{len(blocks)})') # httplib seems to require fresh connection here, so specify timeout diff --git a/mmgen/proto/btc/tw/json.py b/mmgen/proto/btc/tw/json.py index 8dec2191..37b2f568 100755 --- a/mmgen/proto/btc/tw/json.py +++ b/mmgen/proto/btc/tw/json.py @@ -81,6 +81,7 @@ class BitcoinTwJSON(TwJSON): if self.prune: from .prune import TwAddressesPrune self._addrlist = al = await TwAddressesPrune( + self.cfg, self.proto, get_data = True, warn_used = self.warn_used ) @@ -88,7 +89,7 @@ class BitcoinTwJSON(TwJSON): self.pruned = al.do_prune() else: from .addresses import TwAddresses - self._addrlist = await TwAddresses(self.proto,get_data=True) + self._addrlist = await TwAddresses(self.cfg,self.proto,get_data=True) return self._addrlist async def get_entries(self): # TODO: include 'received' field diff --git a/mmgen/proto/btc/tw/txhistory.py b/mmgen/proto/btc/tw/txhistory.py index dfa72f33..bc8cdd50 100755 --- a/mmgen/proto/btc/tw/txhistory.py +++ b/mmgen/proto/btc/tw/txhistory.py @@ -13,7 +13,6 @@ proto.btc.tw.txhistory: Bitcoin base protocol tracking wallet transaction histor """ from collections import namedtuple -from ....globalvars import g from ....tw.txhistory import TwTxHistory from ....tw.shared import get_tw_label,TwMMGenID from ....addr import CoinAddr @@ -291,7 +290,7 @@ Filters/Actions: show [u]nconfirmed, [q]uit menu, r[e]draw: data = list(gen_parsed_data()) - if g.debug_tw: + if self.cfg.debug_tw: import json from ....rpc import json_encoder def do_json_dump(*data): @@ -328,7 +327,7 @@ Filters/Actions: show [u]nconfirmed, [q]uit menu, r[e]draw: for tx in _wallet_txs: tx['decoded'] = next(_decoded_txs) - if g.debug_tw: + if self.cfg.debug_tw: do_json_dump((_wallet_txs, 'wallet-txs'),) _wip = namedtuple('prevout',['txid','vout']) @@ -353,7 +352,7 @@ Filters/Actions: show [u]nconfirmed, [q]uit menu, r[e]draw: for d in txdata: d['prevout_txs'] = [_prevout_txs_dict[txid] for txid in {i.txid for i in d['prevouts']} ] - if g.debug_tw: + if self.cfg.debug_tw: do_json_dump( (rpc_data, 'txhist-rpc'), (data, 'txhist'), diff --git a/mmgen/proto/btc/tx/base.py b/mmgen/proto/btc/tx/base.py index f2eaa8ff..185c4f4d 100755 --- a/mmgen/proto/btc/tx/base.py +++ b/mmgen/proto/btc/tx/base.py @@ -15,9 +15,8 @@ proto.btc.tx.base: Bitcoin base transaction class 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,die,pp_fmt +from ....util import msg,make_chksum_6,die,pp_fmt def addr2scriptPubKey(proto,addr): @@ -247,12 +246,12 @@ class Base(TxBase.Base): ret = (old_size * 3 + new_size) // 4 - dmsg( + self.cfg._util.dmsg( '\nData from estimate_size():\n' + f' inputs size: {isize}, outputs size: {osize}, witness size: {wsize}\n' + f' size: {new_size}, vsize: {ret}, old_size: {old_size}' ) - return int(ret * (opt.vsize_adj or 1)) + return int(ret * (self.cfg.vsize_adj or 1)) # convert absolute CoinAmt fee to sat/byte using estimated size def fee_abs2rel(self,abs_fee,to_unit='satoshi'): diff --git a/mmgen/proto/btc/tx/new.py b/mmgen/proto/btc/tx/new.py index 4c992ba5..cf63d267 100755 --- a/mmgen/proto/btc/tx/new.py +++ b/mmgen/proto/btc/tx/new.py @@ -14,9 +14,8 @@ proto.btc.tx.new: Bitcoin new transaction class import mmgen.tx.new as TxBase from .base import Base -from ....opts import opt from ....obj import HexStr,MMGenTxID -from ....util import msg,dmsg,vmsg,make_chksum_6,die +from ....util import msg,make_chksum_6,die class New(Base,TxBase.New): usr_fee_prompt = 'Enter transaction fee: ' @@ -27,15 +26,15 @@ class New(Base,TxBase.New): def relay_fee(self): kb_fee = self.proto.coin_amt(self.rpc.cached['networkinfo']['relayfee']) ret = kb_fee * self.estimate_size() / 1024 - vmsg('Relay fee: {} {c}/kB, for transaction: {} {c}'.format(kb_fee,ret,c=self.coin)) + self.cfg._util.vmsg('Relay fee: {} {c}/kB, for transaction: {} {c}'.format(kb_fee,ret,c=self.coin)) return ret async def get_rel_fee_from_network(self): try: ret = await self.rpc.call( 'estimatesmartfee', - opt.fee_estimate_confs, - opt.fee_estimate_mode.upper() ) + self.cfg.fee_estimate_confs, + self.cfg.fee_estimate_mode.upper() ) fee_per_kb = ret['feerate'] if 'feerate' in ret else -2 fe_type = 'estimatesmartfee' except: @@ -57,13 +56,13 @@ class New(Base,TxBase.New): from decimal import Decimal tx_size = self.estimate_size() ret = self.proto.coin_amt( - fee_per_kb * Decimal(opt.fee_adjust) * tx_size / 1024, + fee_per_kb * Decimal(self.cfg.fee_adjust) * tx_size / 1024, from_decimal = True ) - if opt.verbose: + if self.cfg.verbose: msg(fmt(f""" - {fe_type.upper()} fee for {opt.fee_estimate_confs} confirmations: {fee_per_kb} {self.coin}/kB + {fe_type.upper()} fee for {self.cfg.fee_estimate_confs} confirmations: {fee_per_kb} {self.coin}/kB TX size (estimated): {tx_size} bytes - Fee adjustment factor: {opt.fee_adjust:.2f} + Fee adjustment factor: {self.cfg.fee_adjust:.2f} Absolute fee (fee_per_kb * adj_factor * tx_size / 1024): {ret} {self.coin} """).strip()) return ret @@ -114,7 +113,7 @@ class New(Base,TxBase.New): if not bump: self.inputs.sort_bip69() # Set all sequence numbers to the same value, in conformity with the behavior of most modern wallets: - seqnum_val = self.proto.max_int - (2 if opt.rbf else 1 if locktime else 0) + seqnum_val = self.proto.max_int - (2 if self.cfg.rbf else 1 if locktime else 0) for i in self.inputs: i.sequence = seqnum_val diff --git a/mmgen/proto/btc/tx/online.py b/mmgen/proto/btc/tx/online.py index c5eee906..cab6fceb 100755 --- a/mmgen/proto/btc/tx/online.py +++ b/mmgen/proto/btc/tx/online.py @@ -14,7 +14,6 @@ proto.btc.tx.online: Bitcoin online signed transaction class import mmgen.tx.online as TxBase from .signed import Signed -from ....globalvars import * from ....util import msg,ymsg,rmsg class OnlineSigned(Signed,TxBase.OnlineSigned): @@ -23,7 +22,7 @@ class OnlineSigned(Signed,TxBase.OnlineSigned): self.check_correct_chain() - if not g.bogus_send: + if not self.cfg.bogus_send: if self.has_segwit_outputs() and not self.rpc.info('segwit_is_active'): die(2,'Transaction has Segwit outputs, but this blockchain does not support Segwit' + ' at the current height') @@ -40,7 +39,7 @@ class OnlineSigned(Signed,TxBase.OnlineSigned): if prompt_user: self.confirm_send() - if g.bogus_send: + if self.cfg.bogus_send: ret = None else: try: @@ -67,7 +66,7 @@ class OnlineSigned(Signed,TxBase.OnlineSigned): sys.exit(1) return False else: - if g.bogus_send: + if self.cfg.bogus_send: m = 'BOGUS transaction NOT sent: {}' else: m = 'Transaction sent: {}' diff --git a/mmgen/proto/btc/tx/signed.py b/mmgen/proto/btc/tx/signed.py index df2f1540..4e93844c 100755 --- a/mmgen/proto/btc/tx/signed.py +++ b/mmgen/proto/btc/tx/signed.py @@ -14,7 +14,7 @@ proto.btc.tx.signed: Bitcoin signed transaction class import mmgen.tx.signed as TxBase from .completed import Completed -from ....util import fmt,vmsg,die +from ....util import fmt,die class Signed(Completed,TxBase.Signed): @@ -22,7 +22,7 @@ class Signed(Completed,TxBase.Signed): est_vsize = self.estimate_size() d = tx_decoded vsize = d['vsize'] if 'vsize' in d else d['size'] - vmsg(f'\nVsize: {vsize} (true) {est_vsize} (estimated)') + self.cfg._util.vmsg(f'\nVsize: {vsize} (true) {est_vsize} (estimated)') ratio = float(est_vsize) / vsize if not (0.95 < ratio < 1.05): # allow for 5% error die( 'BadTxSizeEstimate', fmt(f""" diff --git a/mmgen/proto/btc/tx/status.py b/mmgen/proto/btc/tx/status.py index 9ec16da3..0f8e4504 100755 --- a/mmgen/proto/btc/tx/status.py +++ b/mmgen/proto/btc/tx/status.py @@ -15,7 +15,6 @@ proto.btc.tx.status: Bitcoin transaction status class import time import mmgen.tx.status as TxBase -from ....opts import opt from ....util import msg,suf,die,secs_to_dhms class Status(TxBase.Status): @@ -82,7 +81,7 @@ class Status(TxBase.Status): verbose = False ) rep = ('' if d.get('bip125-replaceable') == 'yes' else 'NOT ') + 'replaceable' t = d['timereceived'] - if opt.quiet: + if tx.cfg.quiet: msg('Transaction is in mempool') else: msg(f'TX status: in mempool, {rep}') @@ -101,7 +100,7 @@ class Status(TxBase.Status): f'has {r.replacing_confs} confirmation{suf(r.replacing_confs)}' if r.replacing_confs else 'is in mempool' ) ) - if not opt.quiet: + if not tx.cfg.quiet: msg('Replacing transactions:') d = [] for txid in r.replacing_txs: diff --git a/mmgen/proto/btc/tx/unsigned.py b/mmgen/proto/btc/tx/unsigned.py index 04a5cced..f9a50bf8 100755 --- a/mmgen/proto/btc/tx/unsigned.py +++ b/mmgen/proto/btc/tx/unsigned.py @@ -14,9 +14,8 @@ proto.btc.tx.unsigned: Bitcoin unsigned transaction class import mmgen.tx.unsigned as TxBase from .completed import Completed -from ....globalvars import * from ....obj import HexStr,CoinTxID,MMGenDict -from ....util import msg,msg_r,ymsg,qmsg,suf +from ....util import msg,msg_r,ymsg,suf class Unsigned(Completed,TxBase.Unsigned): desc = 'unsigned transaction' @@ -34,12 +33,12 @@ class Unsigned(Completed,TxBase.Unsigned): self.check_pubkey_scripts() - qmsg(f'Passing {len(keys)} key{suf(keys)} to {self.rpc.daemon.exec_fn}') + self.cfg._util.qmsg(f'Passing {len(keys)} key{suf(keys)} to {self.rpc.daemon.exec_fn}') if self.has_segwit_inputs(): from ....addrgen import KeyGenerator,AddrGenerator - kg = KeyGenerator(self.proto,'std') - ag = AddrGenerator(self.proto,'segwit') + kg = KeyGenerator( self.cfg, self.proto, 'std' ) + ag = AddrGenerator( self.cfg, self.proto, 'segwit' ) keydict = MMGenDict([(d.addr,d.sec) for d in keys]) sig_data = [] @@ -68,7 +67,7 @@ class Unsigned(Completed,TxBase.Unsigned): try: self.update_serialized(ret['hex']) - new = await SignedTX(data=self.__dict__) + new = await SignedTX(cfg=self.cfg,data=self.__dict__) tx_decoded = await self.rpc.call( 'decoderawtransaction', ret['hex'] ) new.compare_size_and_estimated_size(tx_decoded) new.coin_txid = CoinTxID(self.deserialized.txid) @@ -78,7 +77,7 @@ class Unsigned(Completed,TxBase.Unsigned): return new except Exception as e: ymsg(f'\n{e.args[0]}') - if g.exec_wrapper: - import traceback + if self.cfg.exec_wrapper: + import sys,traceback ymsg( '\n' + ''.join(traceback.format_exception(*sys.exc_info())) ) return False diff --git a/mmgen/proto/eth/addrdata.py b/mmgen/proto/eth/addrdata.py index 16a57f75..b3365b3d 100755 --- a/mmgen/proto/eth/addrdata.py +++ b/mmgen/proto/eth/addrdata.py @@ -33,9 +33,8 @@ class EthereumTwAddrData(TwAddrData): async def get_tw_data(self,twctl=None): from ...tw.ctl import TwCtl - from ...util import vmsg - vmsg('Getting address data from tracking wallet') - twctl = (twctl or await TwCtl(self.proto)).mmid_ordered_dict + self.cfg._util.vmsg('Getting address data from tracking wallet') + twctl = (twctl or await TwCtl(self.cfg,self.proto)).mmid_ordered_dict # emulate the output of RPC 'listaccounts' and 'getaddressesbyaccount' return [(mmid+' '+d['comment'],[d['addr']]) for mmid,d in list(twctl.items())] diff --git a/mmgen/proto/eth/contract.py b/mmgen/proto/eth/contract.py index 3338b2d5..56863f4e 100755 --- a/mmgen/proto/eth/contract.py +++ b/mmgen/proto/eth/contract.py @@ -25,7 +25,6 @@ from . import rlp from . import erigon_sleep from ...util import msg,pp_msg -from ...globalvars import g from ...base_obj import AsyncInit from ...obj import MMGenObject,CoinTxID from ...addr import CoinAddr,TokenAddr @@ -46,7 +45,7 @@ class TokenCommon(MMGenObject): async def do_call(self,method_sig,method_args='',toUnit=False): data = self.create_method_id(method_sig) + method_args - if g.debug: + if self.cfg.debug: msg('ETH_CALL {}: {}'.format( method_sig, '\n '.join(parse_abi(data)) )) @@ -121,7 +120,7 @@ class TokenCommon(MMGenObject): if tx.sender.hex() != from_addr: die(3,f'Sender address {from_addr!r} does not match address of key {tx.sender.hex()!r}!') - if g.debug: + if self.cfg.debug: msg('TOKEN DATA:') pp_msg(tx.to_dict()) msg('PARSED ABI DATA:\n {}'.format( @@ -152,10 +151,11 @@ class TokenCommon(MMGenObject): class Token(TokenCommon): - def __init__(self,proto,addr,decimals,rpc=None): + def __init__(self,cfg,proto,addr,decimals,rpc=None): if type(self).__name__ == 'Token': from ...util2 import get_keccak - self.keccak_256 = get_keccak() + self.keccak_256 = get_keccak(cfg) + self.cfg = cfg self.proto = proto self.addr = TokenAddr(proto,addr) assert isinstance(decimals,int),f'decimals param must be int instance, not {type(decimals)}' @@ -165,13 +165,14 @@ class Token(TokenCommon): class TokenResolve(TokenCommon,metaclass=AsyncInit): - async def __init__(self,proto,rpc,addr): + async def __init__(self,cfg,proto,rpc,addr): from ...util2 import get_keccak - self.keccak_256 = get_keccak() + self.keccak_256 = get_keccak(cfg) + self.cfg = cfg self.proto = proto self.rpc = rpc self.addr = TokenAddr(proto,addr) decimals = await self.get_decimals() # requires self.addr! if not decimals: die( 'TokenNotInBlockchain', f'Token {addr!r} not in blockchain' ) - Token.__init__(self,proto,addr,decimals,rpc) + Token.__init__(self,cfg,proto,addr,decimals,rpc) diff --git a/mmgen/proto/eth/misc.py b/mmgen/proto/eth/misc.py index 928b09e5..88357bb9 100755 --- a/mmgen/proto/eth/misc.py +++ b/mmgen/proto/eth/misc.py @@ -15,7 +15,7 @@ proto.eth.misc: miscellaneous utilities for Ethereum base protocol from ...util import die from ...util2 import get_keccak -def extract_key_from_geth_keystore_wallet(wallet_fn,passwd,check_addr=True): +def extract_key_from_geth_keystore_wallet(cfg,wallet_fn,passwd,check_addr=True): """ Decrypt the encrypted private key in a Geth keystore wallet, returning the decrypted key """ @@ -42,7 +42,7 @@ def extract_key_from_geth_keystore_wallet(wallet_fn,passwd,check_addr=True): dklen = sp['dklen'] ) # Check password by comparing generated MAC to stored MAC - mac_chk = get_keccak()(hashed_pw[16:32] + bytes.fromhex( cdata['ciphertext'] )).digest().hex() + mac_chk = get_keccak(cfg)(hashed_pw[16:32] + bytes.fromhex( cdata['ciphertext'] )).digest().hex() if mac_chk != cdata['mac']: die(1,'Incorrect passphrase') @@ -60,32 +60,32 @@ def extract_key_from_geth_keystore_wallet(wallet_fn,passwd,check_addr=True): if check_addr: from ...tool.coin import tool_cmd from ...protocol import init_proto - t = tool_cmd( proto=init_proto('eth') ) + t = tool_cmd( cfg=cfg, proto=init_proto(cfg,'eth') ) addr = t.wif2addr(key.hex()) addr_chk = wallet_data['address'] assert addr == addr_chk, f'incorrect address: ({addr} != {addr_chk})' return key -def hash_message(message,msghash_type): - return get_keccak()( +def hash_message(cfg,message,msghash_type): + return get_keccak(cfg)( { 'raw': message, 'eth_sign': '\x19Ethereum Signed Message:\n{}{}'.format( len(message), message ), }[msghash_type].encode() ).digest() -def ec_sign_message_with_privkey(message,key,msghash_type): +def ec_sign_message_with_privkey(cfg,message,key,msghash_type): """ Sign an arbitrary string with an Ethereum private key, returning the signature Conforms to the standard defined by the Geth `eth_sign` JSON-RPC call """ from py_ecc.secp256k1 import ecdsa_raw_sign - v,r,s = ecdsa_raw_sign( hash_message(message,msghash_type), key ) + v,r,s = ecdsa_raw_sign( hash_message(cfg,message,msghash_type), key ) return '{:064x}{:064x}{:02x}'.format(r,s,v) -def ec_recover_pubkey(message,sig,msghash_type): +def ec_recover_pubkey(cfg,message,sig,msghash_type): """ Given a message and signature, recover the public key associated with the private key used to make the signature @@ -95,5 +95,5 @@ def ec_recover_pubkey(message,sig,msghash_type): from py_ecc.secp256k1 import ecdsa_raw_recover r,s,v = ( sig[:64], sig[64:128], sig[128:] ) return '{:064x}{:064x}'.format( - *ecdsa_raw_recover( hash_message(message,msghash_type), tuple(int(hexstr,16) for hexstr in (v,r,s)) ) + *ecdsa_raw_recover( hash_message(cfg,message,msghash_type), tuple(int(hexstr,16) for hexstr in (v,r,s)) ) ) diff --git a/mmgen/proto/eth/msg.py b/mmgen/proto/eth/msg.py index 54d10167..5cc201f9 100755 --- a/mmgen/proto/eth/msg.py +++ b/mmgen/proto/eth/msg.py @@ -24,7 +24,7 @@ class coin_msg(coin_msg): async def do_sign(self,wif,message,msghash_type): from .misc import ec_sign_message_with_privkey - return ec_sign_message_with_privkey( message, bytes.fromhex(wif), msghash_type ) + return ec_sign_message_with_privkey( self.cfg, message, bytes.fromhex(wif), msghash_type ) class signed_online(coin_msg.signed_online): @@ -32,7 +32,8 @@ class coin_msg(coin_msg): from ...tool.coin import tool_cmd from .misc import ec_recover_pubkey return tool_cmd( + self.cfg, proto = self.proto).pubhex2addr( - ec_recover_pubkey( message, sig, msghash_type )) == addr + ec_recover_pubkey( self.cfg, message, sig, msghash_type )) == addr class exported_sigs(coin_msg.exported_sigs,signed_online): pass diff --git a/mmgen/proto/eth/params.py b/mmgen/proto/eth/params.py index 9f3967bf..fe975f0c 100755 --- a/mmgen/proto/eth/params.py +++ b/mmgen/proto/eth/params.py @@ -12,7 +12,6 @@ proto.eth.params: Ethereum protocol """ -from ...globalvars import g from ...protocol import CoinProtocol,_nw,decoded_addr from ...util import is_hex_str_lc,Msg @@ -57,7 +56,7 @@ class mainnet(CoinProtocol.DummyWIF,CoinProtocol.Secp256k1): def decode_addr(self,addr): if is_hex_str_lc(addr) and len(addr) == self.addr_len * 2: return decoded_addr( bytes.fromhex(addr), None, 'ethereum' ) - if g.debug: + if self.cfg.debug: Msg(f'Invalid address: {addr}') return False diff --git a/mmgen/proto/eth/rpc.py b/mmgen/proto/eth/rpc.py index b2796e4e..84e89269 100755 --- a/mmgen/proto/eth/rpc.py +++ b/mmgen/proto/eth/rpc.py @@ -14,7 +14,6 @@ proto.eth.rpc: Ethereum base protocol RPC client class import re -from ...globalvars import g from ...base_obj import AsyncInit from ...obj import Int from ...util import die,oneshot_warning_group @@ -37,6 +36,7 @@ class EthereumRPCClient(RPCClient,metaclass=AsyncInit): async def __init__( self, + cfg, proto, daemon, backend, @@ -47,7 +47,8 @@ class EthereumRPCClient(RPCClient,metaclass=AsyncInit): self.call_sigs = getattr(CallSigs,daemon.id,None) super().__init__( - host = 'localhost' if g.test_suite else (g.rpc_host or 'localhost'), + cfg = cfg, + host = 'localhost' if cfg.test_suite else (cfg.rpc_host or 'localhost'), port = daemon.rpc_port ) await self.set_backend_async(backend) diff --git a/mmgen/proto/eth/tw/bal.py b/mmgen/proto/eth/tw/bal.py index 13a5bc89..6a79776a 100755 --- a/mmgen/proto/eth/tw/bal.py +++ b/mmgen/proto/eth/tw/bal.py @@ -30,9 +30,9 @@ class EthereumTwGetBalance(TwGetBalance): 'ge_minconf': 'Balance', } - async def __init__(self,proto,*args,**kwargs): - self.twctl = await TwCtl(proto,mode='w') - await super().__init__(proto,*args,**kwargs) + async def __init__(self,cfg,proto,*args,**kwargs): + self.twctl = await TwCtl(cfg,proto,mode='w') + await super().__init__(cfg,proto,*args,**kwargs) async def create_data(self): in_data = self.twctl.mmid_ordered_dict diff --git a/mmgen/proto/eth/tw/ctl.py b/mmgen/proto/eth/tw/ctl.py index 38ad7744..095d79f4 100755 --- a/mmgen/proto/eth/tw/ctl.py +++ b/mmgen/proto/eth/tw/ctl.py @@ -172,9 +172,9 @@ class EthereumTokenTwCtl(EthereumTwCtl): symbol = None cur_eth_balances = {} - async def __init__(self,proto,mode='r',token_addr=None): + async def __init__(self,cfg,proto,mode='r',token_addr=None): - await super().__init__(proto,mode=mode) + await super().__init__(cfg,proto,mode=mode) for v in self.data['tokens'].values(): self.conv_types(v) @@ -211,7 +211,7 @@ class EthereumTokenTwCtl(EthereumTwCtl): return 'token ' + self.get_param('symbol') async def rpc_get_balance(self,addr): - return await Token(self.proto,self.token,self.decimals,self.rpc).get_balance(addr) + return await Token(self.cfg,self.proto,self.token,self.decimals,self.rpc).get_balance(addr) async def get_eth_balance(self,addr,force_rpc=False): cache = self.cur_eth_balances @@ -232,7 +232,7 @@ class EthereumTokenTwCtl(EthereumTwCtl): once, upon token import. Thereafter, token address, symbol and decimals are resolved either from the tracking wallet (online operations) or transaction file (when signing). """ - t = await TokenResolve(self.proto,self.rpc,tokenaddr) + t = await TokenResolve(self.cfg,self.proto,self.rpc,tokenaddr) self.data['tokens'][tokenaddr] = { 'params': { 'symbol': await t.get_symbol(), diff --git a/mmgen/proto/eth/tw/view.py b/mmgen/proto/eth/tw/view.py index bbb5a311..6e2b24a9 100755 --- a/mmgen/proto/eth/tw/view.py +++ b/mmgen/proto/eth/tw/view.py @@ -12,7 +12,6 @@ proto.eth.tw.view: Ethereum base protocol base class for tracking wallet view classes """ -from ....globalvars import g from ....tw.view import TwView class EthereumTwView(TwView): @@ -26,6 +25,6 @@ class EthereumTwView(TwView): def gen_subheader(self,cw,color): if self.disp_prec == 8: yield 'Balances truncated to 8 decimal points' - if g.cached_balances: + if self.cfg.cached_balances: from ....color import nocolor,yellow yield (nocolor,yellow)[color]('WARNING: Using cached balances. These may be out of date!') diff --git a/mmgen/proto/eth/tx/base.py b/mmgen/proto/eth/tx/base.py index 9aeeeee8..b0b81200 100755 --- a/mmgen/proto/eth/tx/base.py +++ b/mmgen/proto/eth/tx/base.py @@ -16,7 +16,6 @@ from collections import namedtuple import mmgen.tx.base as TxBase from ....obj import HexStr,Int -from ....util import dmsg class Base(TxBase.Base): @@ -33,7 +32,7 @@ class Base(TxBase.Base): # given absolute fee in ETH, return gas price in Gwei using self.gas def fee_abs2rel(self,abs_fee,to_unit='Gwei'): ret = self.proto.coin_amt(int(abs_fee.toWei() // self.gas.toWei()),'wei') - dmsg(f'fee_abs2rel() ==> {ret} ETH') + self.cfg._util.dmsg(f'fee_abs2rel() ==> {ret} ETH') return ret if to_unit == 'eth' else ret.to_unit(to_unit,show_decimal=True) # given rel fee (gasPrice) in wei, return absolute fee using self.gas (Ethereum-only method) diff --git a/mmgen/proto/eth/tx/new.py b/mmgen/proto/eth/tx/new.py index f815c6ec..323ba120 100755 --- a/mmgen/proto/eth/tx/new.py +++ b/mmgen/proto/eth/tx/new.py @@ -16,7 +16,6 @@ import json import mmgen.tx.new as TxBase from .base import Base,TokenBase -from ....opts import opt from ....obj import Int,ETHNonce,MMGenTxID,Str,HexStr from ....util import msg,is_int,is_hex_str,make_chksum_6 from ....tw.ctl import TwCtl @@ -34,16 +33,16 @@ class New(Base,TxBase.New): super().__init__(*args,**kwargs) - if opt.gas: - self.gas = self.start_gas = self.proto.coin_amt(int(opt.gas),'wei') + if self.cfg.gas: + self.gas = self.start_gas = self.proto.coin_amt(int(self.cfg.gas),'wei') else: self.gas = self.proto.coin_amt(self.dfl_gas,'wei') self.start_gas = self.proto.coin_amt(self.dfl_start_gas,'wei') - if opt.contract_data: + if self.cfg.contract_data: m = "'--contract-data' option may not be used with token transaction" assert not 'Token' in type(self).__name__, m - with open(opt.contract_data) as fp: + with open(self.cfg.contract_data) as fp: self.usr_contract_data = HexStr(fp.read().strip()) self.disable_fee_check = True @@ -93,7 +92,7 @@ class New(Base,TxBase.New): def select_unspent(self,unspent): from ....ui import line_input while True: - reply = line_input('Enter an account to spend from: ').strip() + reply = line_input( self.cfg, 'Enter an account to spend from: ' ).strip() if reply: if not is_int(reply): msg('Account number must be an integer') @@ -119,10 +118,10 @@ class New(Base,TxBase.New): from_unit='wei' ) - # given fee estimate (gas price) in wei, return absolute fee, adjusting by opt.fee_adjust + # given fee estimate (gas price) in wei, return absolute fee, adjusting by self.cfg.fee_adjust def fee_est2abs(self,rel_fee,fe_type=None): - ret = self.fee_gasPrice2abs(rel_fee) * opt.fee_adjust - if opt.verbose: + ret = self.fee_gasPrice2abs(rel_fee) * self.cfg.fee_adjust + if self.cfg.verbose: msg(f'Estimated fee: {ret} ETH') return ret @@ -146,10 +145,10 @@ class New(Base,TxBase.New): async def get_input_addrs_from_cmdline(self): ret = [] - if opt.inputs: - data_root = (await TwCtl(self.proto)).data_root # must create new instance here + if self.cfg.inputs: + data_root = (await TwCtl(self.cfg,self.proto)).data_root # must create new instance here errmsg = 'Address {!r} not in tracking wallet' - for addr in opt.inputs.split(','): + for addr in self.cfg.inputs.split(','): if is_mmgen_id(self.proto,addr): for waddr in data_root: if data_root[waddr]['mmid'] == addr: @@ -178,7 +177,7 @@ class TokenNew(TokenBase,New): async def make_txobj(self): # called by create_serialized() await super().make_txobj() - t = Token(self.proto,self.twctl.token,self.twctl.decimals) + t = Token(self.cfg,self.proto,self.twctl.token,self.twctl.decimals) o = self.txobj o['token_addr'] = t.addr o['decimals'] = t.decimals diff --git a/mmgen/proto/eth/tx/online.py b/mmgen/proto/eth/tx/online.py index b3b161e7..3d6bfc2b 100755 --- a/mmgen/proto/eth/tx/online.py +++ b/mmgen/proto/eth/tx/online.py @@ -12,8 +12,6 @@ proto.eth.tx.online: Ethereum online signed transaction class """ -from ....globalvars import * - import mmgen.tx.online as TxBase from .signed import Signed,TokenSigned from .. import erigon_sleep @@ -37,7 +35,7 @@ class OnlineSigned(Signed,TxBase.OnlineSigned): if prompt_user: self.confirm_send() - if g.bogus_send: + if self.cfg.bogus_send: ret = None else: try: @@ -52,7 +50,7 @@ class OnlineSigned(Signed,TxBase.OnlineSigned): sys.exit(1) return False else: - if g.bogus_send: + if self.cfg.bogus_send: m = 'BOGUS transaction NOT sent: {}' else: m = 'Transaction sent: {}' @@ -78,6 +76,6 @@ class TokenOnlineSigned(TokenSigned,OnlineSigned): assert self.twctl.token == o['to'] o['token_addr'] = TokenAddr(self.proto,o['to']) o['decimals'] = self.twctl.decimals - t = Token(self.proto,o['token_addr'],o['decimals']) + t = Token(self.cfg,self.proto,o['token_addr'],o['decimals']) o['amt'] = t.transferdata2amt(o['data']) o['token_to'] = t.transferdata2sendaddr(o['data']) diff --git a/mmgen/proto/eth/tx/unsigned.py b/mmgen/proto/eth/tx/unsigned.py index 8a8f6f22..dbb5f058 100755 --- a/mmgen/proto/eth/tx/unsigned.py +++ b/mmgen/proto/eth/tx/unsigned.py @@ -79,7 +79,7 @@ class Unsigned(Completed,TxBase.Unsigned): await self.do_sign(keys[0].sec.wif,tx_num_str) msg('OK') from ....tx import SignedTX - return await SignedTX(data=self.__dict__) + return await SignedTX(cfg=self.cfg,data=self.__dict__) except Exception as e: msg(f'{e}: transaction signing failed!') return False @@ -92,12 +92,12 @@ class TokenUnsigned(TokenCompleted,Unsigned): o = self.txobj o['token_addr'] = TokenAddr(self.proto,d['token_addr']) o['decimals'] = Int(d['decimals']) - t = Token(self.proto,o['token_addr'],o['decimals']) + t = Token(self.cfg,self.proto,o['token_addr'],o['decimals']) o['data'] = t.create_data(o['to'],o['amt']) o['token_to'] = t.transferdata2sendaddr(o['data']) async def do_sign(self,wif,tx_num_str): o = self.txobj - t = Token(self.proto,o['token_addr'],o['decimals']) + t = Token(self.cfg,self.proto,o['token_addr'],o['decimals']) tx_in = t.make_tx_in(o['from'],o['to'],o['amt'],self.start_gas,o['gasPrice'],nonce=o['nonce']) (self.serialized,self.coin_txid) = await t.txsign(tx_in,wif,o['from'],chain_id=o['chainId']) diff --git a/mmgen/proto/secp256k1/keygen.py b/mmgen/proto/secp256k1/keygen.py index 5959a7c8..ccb54b88 100755 --- a/mmgen/proto/secp256k1/keygen.py +++ b/mmgen/proto/secp256k1/keygen.py @@ -19,7 +19,7 @@ class backend: class libsecp256k1(keygen_base): - def __init__(self): + def __init__(self,cfg): from .secp256k1 import priv2pub self.priv2pub = priv2pub @@ -41,13 +41,12 @@ class backend: if not silent: from ...util import ymsg ymsg(str(e)) - from ...util import qmsg - qmsg('Using (slow) native Python ECDSA library for public key generation') + self.cfg._util.qmsg('Using (slow) native Python ECDSA library for public key generation') return 'python_ecdsa' class python_ecdsa(keygen_base): - def __init__(self): + def __init__(self,cfg): import ecdsa self.ecdsa = ecdsa diff --git a/mmgen/proto/xmr/daemon.py b/mmgen/proto/xmr/daemon.py index f9a6d762..0b878453 100755 --- a/mmgen/proto/xmr/daemon.py +++ b/mmgen/proto/xmr/daemon.py @@ -14,8 +14,7 @@ proto.xmr.daemon: Monero base protocol daemon classes import os -from ...globalvars import g,gc -from ...opts import opt +from ...globalvars import gc from ...util import list_gen,die,contains_any from ...daemon import CoinDaemon,RPCDaemon,_nw,_dd @@ -46,6 +45,7 @@ class monero_daemon(CoinDaemon): from .rpc import MoneroRPCClient self.rpc = MoneroRPCClient( + cfg = self.cfg, proto = self.proto, host = self.host, port = self.rpc_port, @@ -91,6 +91,7 @@ class MoneroWalletDaemon(RPCDaemon): def __init__( self, + cfg, proto, wallet_dir, test_suite = False, @@ -105,7 +106,7 @@ class MoneroWalletDaemon(RPCDaemon): self.proto = proto self.test_suite = test_suite - super().__init__() + super().__init__(cfg) self.network = proto.network self.wallet_dir = wallet_dir @@ -123,13 +124,14 @@ class MoneroWalletDaemon(RPCDaemon): self.daemon_port = ( None if daemon_addr else CoinDaemon( + cfg = self.cfg, proto = proto, test_suite = test_suite).rpc_port ) - self.host = host or opt.wallet_rpc_host or g.monero_wallet_rpc_host - self.user = user or opt.wallet_rpc_user or g.monero_wallet_rpc_user - self.passwd = passwd or opt.wallet_rpc_password or g.monero_wallet_rpc_password + self.host = host or self.cfg.wallet_rpc_host or self.cfg.monero_wallet_rpc_host + self.user = user or self.cfg.wallet_rpc_user or self.cfg.monero_wallet_rpc_user + self.passwd = passwd or self.cfg.wallet_rpc_password or self.cfg.monero_wallet_rpc_password assert self.host assert self.user @@ -157,5 +159,6 @@ class MoneroWalletDaemon(RPCDaemon): from .rpc import MoneroWalletRPCClient self.rpc = MoneroWalletRPCClient( + cfg = self.cfg, daemon = self, test_connection = False ) diff --git a/mmgen/proto/xmr/keygen.py b/mmgen/proto/xmr/keygen.py index 0e3d9fc8..d89a7500 100755 --- a/mmgen/proto/xmr/keygen.py +++ b/mmgen/proto/xmr/keygen.py @@ -19,13 +19,13 @@ class backend: class base(keygen_base): - def __init__(self): + def __init__(self,cfg): from ...proto.xmr.params import mainnet self.proto_cls = mainnet from ...util2 import get_keccak - self.keccak_256 = get_keccak() + self.keccak_256 = get_keccak(cfg) def to_viewkey(self,privkey): return self.proto_cls.preprocess_key( @@ -35,8 +35,8 @@ class backend: class nacl(base): - def __init__(self): - super().__init__() + def __init__(self,cfg): + super().__init__(cfg) from nacl.bindings import crypto_scalarmult_ed25519_base_noclamp self.scalarmultbase = crypto_scalarmult_ed25519_base_noclamp @@ -49,8 +49,8 @@ class backend: class ed25519(base): - def __init__(self): - super().__init__() + def __init__(self,cfg): + super().__init__(cfg) from ...contrib.ed25519 import edwards,encodepoint,B,scalarmult self.edwards = edwards self.encodepoint = encodepoint @@ -85,7 +85,7 @@ class backend: class ed25519ll_djbec(ed25519): - def __init__(self): - super().__init__() + def __init__(self,cfg): + super().__init__(cfg) from ...contrib.ed25519ll_djbec import scalarmult self.scalarmult = scalarmult diff --git a/mmgen/proto/xmr/rpc.py b/mmgen/proto/xmr/rpc.py index 09a10a6a..cac7da39 100755 --- a/mmgen/proto/xmr/rpc.py +++ b/mmgen/proto/xmr/rpc.py @@ -13,7 +13,6 @@ proto.xmr.rpc: Monero base protocol RPC client class """ import re -from ...globalvars import g from ...rpc import RPCClient,IPPort,auth_data class MoneroRPCClient(RPCClient): @@ -24,6 +23,7 @@ class MoneroRPCClient(RPCClient): def __init__( self, + cfg, proto, host, port, @@ -42,7 +42,7 @@ class MoneroRPCClient(RPCClient): if host.endswith('.onion'): self.network_proto = 'http' - super().__init__(host,port,test_connection) + super().__init__(cfg,host,port,test_connection) if self.auth_type: self.auth = auth_data(user,passwd) @@ -69,7 +69,7 @@ class MoneroRPCClient(RPCClient): if self.daemon and self.daemon_version > self.daemon.coind_version: self.handle_unsupported_daemon_version( proto.name, - ignore_daemon_version or proto.ignore_daemon_version or g.ignore_daemon_version ) + ignore_daemon_version or proto.ignore_daemon_version or self.cfg.ignore_daemon_version ) else: # restricted (public) node: self.daemon_version_str = None self.daemon_version = None @@ -100,10 +100,11 @@ class MoneroWalletRPCClient(MoneroRPCClient): auth_type = 'digest' - def __init__(self,daemon,test_connection=True): + def __init__(self,cfg,daemon,test_connection=True): RPCClient.__init__( self, + cfg, daemon.host, daemon.rpc_port, test_connection = test_connection ) diff --git a/mmgen/proto/zec/keygen.py b/mmgen/proto/zec/keygen.py index 3b0f1e2d..a4b6a8c9 100755 --- a/mmgen/proto/zec/keygen.py +++ b/mmgen/proto/zec/keygen.py @@ -19,7 +19,7 @@ class backend: class nacl(keygen_base): - def __init__(self): + def __init__(self,cfg): from nacl.bindings import crypto_scalarmult_base self.crypto_scalarmult_base = crypto_scalarmult_base from ...sha2 import Sha256 diff --git a/mmgen/proto/zec/params.py b/mmgen/proto/zec/params.py index bd701be8..6f1575e5 100755 --- a/mmgen/proto/zec/params.py +++ b/mmgen/proto/zec/params.py @@ -32,8 +32,7 @@ class mainnet(mainnet): def __init__(self,*args,**kwargs): super().__init__(*args,**kwargs) - from ...opts import opt - self.coin_id = 'ZEC-Z' if opt.type in ('zcash_z','Z') else 'ZEC-T' + self.coin_id = 'ZEC-Z' if self.cfg.type in ('zcash_z','Z') else 'ZEC-T' def get_wif_ver_bytes_len(self,key_data): """ diff --git a/mmgen/protocol.py b/mmgen/protocol.py index c9b7c80d..725ab9de 100755 --- a/mmgen/protocol.py +++ b/mmgen/protocol.py @@ -22,7 +22,7 @@ protocol: Coin protocol base classes and initializer from collections import namedtuple -from .globalvars import g,gc +from .globalvars import gc from .objmethods import MMGenObject decoded_wif = namedtuple('decoded_wif',['sec','pubkey_type','compressed']) @@ -54,7 +54,8 @@ class CoinProtocol(MMGenObject): is_fork_of = None networks = ('mainnet','testnet','regtest') - def __init__(self,coin,name,network,tokensym=None,need_amt=False): + def __init__(self,cfg,coin,name,network,tokensym=None,need_amt=False): + self.cfg = cfg self.coin = coin.upper() self.coin_id = self.coin self.name = name @@ -96,7 +97,7 @@ class CoinProtocol(MMGenObject): if self.base_coin in ('ETH','XMR'): from .util2 import get_keccak - self.keccak_256 = get_keccak() + self.keccak_256 = get_keccak(cfg) if need_amt: import mmgen.amt @@ -111,7 +112,7 @@ class CoinProtocol(MMGenObject): return self.coin @classmethod - def chain_name_to_network(cls,coin,chain_name): + def chain_name_to_network(cls,cfg,coin,chain_name): """ The generic networks 'mainnet', 'testnet' and 'regtest' are required for all coins that support transaction operations. @@ -121,7 +122,7 @@ class CoinProtocol(MMGenObject): For Bitcoin and Bitcoin forks, 'network' and 'chain_name' are equivalent. """ for network in ('mainnet','testnet','regtest'): - proto = init_proto(coin,network=network) + proto = init_proto( cfg, coin, network=network ) for proto_chain_name in proto.chain_names: if chain_name == proto_chain_name: return network @@ -211,7 +212,7 @@ class CoinProtocol(MMGenObject): elif pk == self.secp256k1_ge: # ditto die(4,'Private key == secp256k1_ge!') else: - if not g.test_suite: + if not self.cfg.test_suite: ymsg(f'Warning: private key is greater than secp256k1 group order!:\n {hexpriv}') return (pk % self.secp256k1_ge).to_bytes(self.privkey_len,'big') @@ -231,6 +232,7 @@ class CoinProtocol(MMGenObject): compressed = False ) def init_proto( + cfg, coin = None, testnet = False, regtest = False, @@ -271,20 +273,24 @@ def init_proto( ) return getattr(CoinProtocol,proto_name)( + cfg = cfg, coin = coin, name = name, network = network, tokensym = tokensym, need_amt = need_amt ) -def init_proto_from_opts(need_amt=False): +def init_proto_from_cfg(cfg,need_amt): return init_proto( - coin = g.coin, - network = g.network, - tokensym = g.token, + cfg = cfg, + coin = cfg.coin, + network = cfg.network, + tokensym = cfg.token, need_amt = need_amt ) -def warn_trustlevel(coinsym): +def warn_trustlevel(cfg): + + coinsym = cfg.coin if coinsym.lower() in CoinProtocol.coins: trust_level = CoinProtocol.coins[coinsym.lower()].trust_level @@ -306,7 +312,7 @@ def warn_trustlevel(coinsym): Are you sure you want to continue? """ - from .util import qmsg,fmt + from .util import fmt from .color import red,yellow,green warning = fmt(m).strip().format( @@ -319,11 +325,11 @@ def warn_trustlevel(coinsym): }[trust_level], p = gc.proj_name ) - if g.test_suite: - qmsg(warning) + if cfg.test_suite: + cfg._util.qmsg(warning) return from .ui import keypress_confirm - if not keypress_confirm(warning,default_yes=True): + if not keypress_confirm( cfg, warning, default_yes=True ): import sys sys.exit(0) diff --git a/mmgen/rpc.py b/mmgen/rpc.py index df62496a..f00ae825 100755 --- a/mmgen/rpc.py +++ b/mmgen/rpc.py @@ -20,11 +20,12 @@ rpc: Cryptocoin RPC library for the MMGen suite """ -import base64,json,asyncio,importlib +import re,base64,json,asyncio,importlib from decimal import Decimal from collections import namedtuple -from .common import * +from .globalvars import gc +from .util import msg,die,fmt,fmt_list,pp_fmt from .base_obj import AsyncInit from .obj import NonNegativeInt from .objmethods import Hilite,InitErrors,MMGenObject @@ -85,6 +86,7 @@ class RPCBackends: class base: def __init__(self,caller): + self.cfg = caller.cfg self.host = caller.host self.port = caller.port self.proxy = caller.proxy @@ -109,7 +111,7 @@ class RPCBackends: async def __init__(self,caller): super().__init__(caller) import aiohttp - self.connector = aiohttp.TCPConnector(limit_per_host=g.aiohttp_rpc_queue_len) + self.connector = aiohttp.TCPConnector(limit_per_host=self.cfg.aiohttp_rpc_queue_len) self.session = aiohttp.ClientSession( headers = { 'Content-Type': 'application/json' }, connector = self.connector, @@ -261,14 +263,16 @@ class RPCClient(MMGenObject): network_proto = 'http' proxy = None - def __init__(self,host,port,test_connection=True): + def __init__(self,cfg,host,port,test_connection=True): + + self.cfg = cfg # aiohttp workaround, and may speed up RPC performance overall on some systems: if gc.platform == 'win' and host == 'localhost': host = '127.0.0.1' global dmsg_rpc,dmsg_rpc_backend - if not g.debug_rpc: + if not self.cfg.debug_rpc: dmsg_rpc = dmsg_rpc_backend = noop dmsg_rpc(f'=== {type(self).__name__}.__init__() debug ===') @@ -285,11 +289,11 @@ class RPCClient(MMGenObject): self.host_url = f'{self.network_proto}://{host}:{port}' self.host = host self.port = port - self.timeout = g.http_timeout + self.timeout = self.cfg.http_timeout self.auth = None def _get_backend(self,backend): - backend_id = backend or opt.rpc_backend + backend_id = backend or self.cfg.rpc_backend if backend_id == 'auto': return {'linux':RPCBackends.httplib,'win':RPCBackends.requests}[gc.platform](self) else: @@ -443,6 +447,7 @@ class RPCClient(MMGenObject): """,indent=' ')) async def rpc_init( + cfg, proto, backend = None, daemon = None, @@ -458,15 +463,16 @@ async def rpc_init( from .daemon import CoinDaemon rpc = await cls( + cfg = cfg, proto = proto, - daemon = daemon or CoinDaemon(proto=proto,test_suite=g.test_suite), - backend = backend or opt.rpc_backend, + daemon = daemon or CoinDaemon(cfg,proto=proto,test_suite=cfg.test_suite), + backend = backend or cfg.rpc_backend, ignore_wallet = ignore_wallet ) if rpc.daemon_version > rpc.daemon.coind_version: rpc.handle_unsupported_daemon_version( proto.name, - ignore_daemon_version or proto.ignore_daemon_version or g.ignore_daemon_version ) + ignore_daemon_version or proto.ignore_daemon_version or cfg.ignore_daemon_version ) if rpc.chain not in proto.chain_names: die( 'RPCChainMismatch', '\n' + fmt(f""" diff --git a/mmgen/seed.py b/mmgen/seed.py index 8ab929ba..1d51a705 100755 --- a/mmgen/seed.py +++ b/mmgen/seed.py @@ -54,19 +54,20 @@ class SeedBase(MMGenObject): data = ImmutableAttr(bytes,typeconv=False) sid = ImmutableAttr(SeedID,typeconv=False) - def __init__(self,seed_bin=None,nSubseeds=None): + def __init__(self,cfg,seed_bin=None,nSubseeds=None): + if not seed_bin: - from .opts import opt from .crypto import Crypto from hashlib import sha256 # Truncate random data for smaller seed lengths - seed_bin = sha256(Crypto().get_random(1033)).digest()[:(opt.seed_len or self.dfl_len)//8] + seed_bin = sha256(Crypto(cfg).get_random(1033)).digest()[:(cfg.seed_len or self.dfl_len)//8] elif len(seed_bin)*8 not in self.lens: die(3,f'{len(seed_bin)*8}: invalid seed bit length') + self.cfg = cfg self.data = seed_bin self.sid = SeedID(seed=self) - self.nSubseeds = nSubseeds # will override opt.subseeds + self.nSubseeds = nSubseeds # overrides cfg.subseeds @property def bitlen(self): @@ -90,10 +91,9 @@ class Seed(SeedBase): def subseeds(self): if not hasattr(self,'_subseeds'): from .subseed import SubSeedList - from .opts import opt self._subseeds = SubSeedList( self, - length = self.nSubseeds or opt.subseeds ) + length = self.nSubseeds or self.cfg.subseeds ) return self._subseeds def subseed(self,*args,**kwargs): diff --git a/mmgen/seedsplit.py b/mmgen/seedsplit.py index c25639df..d0fffb0c 100755 --- a/mmgen/seedsplit.py +++ b/mmgen/seedsplit.py @@ -20,7 +20,6 @@ seedsplit: Seed split classes and methods for the MMGen suite """ -from .globalvars import g from .color import yellow from .obj import MMGenPWIDString,MMGenIdx from .subseed import * @@ -77,7 +76,7 @@ class SeedShareList(SubSeedList): for nonce in range(SeedShare.max_nonce+1): ms = SeedShareMaster(self,master_idx,nonce) if ms.sid == parent_seed.sid: - if g.debug_subseed: + if parent_seed.cfg.debug_subseed: msg(f'master_share seed ID collision with parent seed, incrementing nonce to {nonce+1}') else: return ms @@ -103,7 +102,7 @@ class SeedShareList(SubSeedList): self.last_share = ls = SeedShareLast(self) if last_share_debug(ls) or ls.sid in self.data['long'] or ls.sid == parent_seed.sid: # collision: throw out entire split list and redo with new start nonce - if g.debug_subseed: + if parent_seed.cfg.debug_subseed: self._collision_debug_msg(ls.sid,count,nonce,'nonce_start',debug_last_share) else: self.data['long'][ls.sid] = (count,nonce) @@ -111,7 +110,7 @@ class SeedShareList(SubSeedList): else: die( 'SubSeedNonceRangeExceeded', 'nonce range exceeded' ) - if g.debug_subseed: + if parent_seed.cfg.debug_subseed: A = parent_seed.data B = self.join().data assert A == B, f'Data mismatch!\noriginal seed: {A!r}\nrejoined seed: {B!r}' @@ -137,6 +136,7 @@ class SeedShareList(SubSeedList): def join(self): return Seed.join_shares( + self.parent_seed.cfg, [self.get_share_by_idx(i+1) for i in range(len(self))] ) def format(self): @@ -210,7 +210,7 @@ class SeedShare(SeedShareBase,SubSeed): b':master:' + parent_list.master_share.idx.to_bytes(2,'big') ) - return Crypto().scramble_seed(seed.data,scramble_key)[:seed.byte_len] + return Crypto(parent_list.parent_seed.cfg).scramble_seed(seed.data,scramble_key)[:seed.byte_len] class SeedShareLast(SeedShareBase,SeedBase): @@ -222,6 +222,7 @@ class SeedShareLast(SeedShareBase,SeedBase): self.parent_list = parent_list SeedBase.__init__( self, + parent_list.parent_seed.cfg, seed_bin=self.make_subseed_bin(parent_list) ) @staticmethod @@ -244,10 +245,12 @@ class SeedShareMaster(SeedBase,SeedShareBase): self.idx = idx self.nonce = nonce self.parent_list = parent_list + self.cfg = parent_list.parent_seed.cfg - SeedBase.__init__( self, self.make_base_seed_bin() ) + SeedBase.__init__( self, self.cfg, self.make_base_seed_bin() ) self.derived_seed = SeedBase( + self.cfg, self.make_derived_seed_bin( parent_list.id_str, parent_list.count ) ) @@ -262,14 +265,14 @@ class SeedShareMaster(SeedBase,SeedShareBase): seed = self.parent_list.parent_seed # field maximums: idx: 65535 (1024) scramble_key = b'master_share:' + self.idx.to_bytes(2,'big') + self.nonce.to_bytes(2,'big') - return Crypto().scramble_seed(seed.data,scramble_key)[:seed.byte_len] + return Crypto(self.cfg).scramble_seed(seed.data,scramble_key)[:seed.byte_len] # Don't bother with avoiding seed ID collision here, as sid of derived seed is not used # by user as an identifier def make_derived_seed_bin(self,id_str,count): # field maximums: id_str: none (256 chars), count: 65535 (1024) scramble_key = id_str.encode() + b':' + count.to_bytes(2,'big') - return Crypto().scramble_seed(self.data,scramble_key)[:self.byte_len] + return Crypto(self.cfg).scramble_seed(self.data,scramble_key)[:self.byte_len] def get_desc(self,ui=False): psid = self.parent_list.parent_seed.sid @@ -281,15 +284,17 @@ class SeedShareMasterJoining(SeedShareMaster): id_str = ImmutableAttr(SeedSplitIDString) count = ImmutableAttr(SeedShareCount) - def __init__(self,idx,base_seed,id_str,count): + def __init__(self,cfg,idx,base_seed,id_str,count): - SeedBase.__init__(self,seed_bin=base_seed.data) + SeedBase.__init__( self, cfg, seed_bin=base_seed.data ) + self.cfg = cfg self.id_str = id_str or 'default' self.count = count - self.derived_seed = SeedBase( self.make_derived_seed_bin(self.id_str,self.count) ) + self.derived_seed = SeedBase( cfg, self.make_derived_seed_bin(self.id_str,self.count) ) def join_shares( + cfg, seed_list, master_idx = None, id_str = None ): @@ -315,8 +320,8 @@ def join_shares( add_share(ss) if master_idx: - add_share(SeedShareMasterJoining(master_idx,master_share,id_str,d.count+1).derived_seed) + add_share(SeedShareMasterJoining( cfg, master_idx, master_share, id_str, d.count+1 ).derived_seed) SeedShareCount(d.count) # check that d.count is in valid range - return Seed(seed_bin=d.ret.to_bytes(d.byte_len,'big')) + return Seed( cfg, seed_bin=d.ret.to_bytes(d.byte_len,'big') ) diff --git a/mmgen/share/Opts.py b/mmgen/share/Opts.py index 1c3bf367..33eb4560 100755 --- a/mmgen/share/Opts.py +++ b/mmgen/share/Opts.py @@ -49,7 +49,7 @@ def print_help(*args): print(make_help(*args)) sys.exit(0) -def make_help(proto,opt,opts_data,opt_filter): +def make_help(cfg,proto,opts_data,opt_filter): def parse_lines(text): filtered = False @@ -62,7 +62,7 @@ def make_help(proto,opt,opts_data,opt_filter): elif not filtered: yield line - opts_type,fs = ('options','{:<3} --{} {}') if opt.help else ('long_options','{} --{} {}') + opts_type,fs = ('options','{:<3} --{} {}') if cfg.help else ('long_options','{} --{} {}') t = opts_data['text'] c = opts_data['code'] nl = '\n ' @@ -71,10 +71,10 @@ def make_help(proto,opt,opts_data,opt_filter): from mmgen.help import help_notes_func def help_notes(k): - return help_notes_func(proto,opt,k) + return help_notes_func(proto,cfg,k) def gen_arg_tuple(func,text): - d = {'proto': proto,'help_notes':help_notes} + d = {'proto': proto,'help_notes':help_notes,'cfg':cfg} for arg in func.__code__.co_varnames: yield d[arg] if arg in d else text diff --git a/mmgen/subseed.py b/mmgen/subseed.py index 22dafc86..4ec02164 100755 --- a/mmgen/subseed.py +++ b/mmgen/subseed.py @@ -21,7 +21,7 @@ subseed: Subseed classes and methods for the MMGen suite """ from .color import green -from .util import msg_r,msg,qmsg,die +from .util import msg_r,msg,die from .obj import MMGenRange,IndexedDict from .seed import * @@ -66,6 +66,7 @@ class SubSeed(SeedBase): self.parent_list = parent_list SeedBase.__init__( self, + parent_list.parent_seed.cfg, seed_bin=self.make_subseed_bin( parent_list, idx, nonce, length )) @staticmethod @@ -75,7 +76,7 @@ class SubSeed(SeedBase): # field maximums: idx: 4294967295 (1000000), nonce: 65535 (1000), short: 255 (1) scramble_key = idx.to_bytes(4,'big') + nonce.to_bytes(2,'big') + short.to_bytes(1,'big') from .crypto import Crypto - return Crypto().scramble_seed(seed.data,scramble_key)[:16 if short else seed.byte_len] + return Crypto(parent_list.parent_seed.cfg).scramble_seed(seed.data,scramble_key)[:16 if short else seed.byte_len] class SubSeedList(MMGenObject): have_short = True @@ -129,7 +130,7 @@ class SubSeedList(MMGenObject): def do_msg(subseed): if print_msg: - qmsg('{} {} ({}:{})'.format( + self.parent_seed.cfg._util.qmsg('{} {} ({}:{})'.format( green('Found subseed'), subseed.sid.hl(), self.parent_seed.sid.hl(), @@ -186,8 +187,7 @@ class SubSeedList(MMGenObject): for nonce in range(self.nonce_start,self.member_type.max_nonce+1): # handle SeedID collisions sid = make_chksum_8(self.member_type.make_subseed_bin(self,idx,nonce,length)) if sid in self.data['long'] or sid in self.data['short'] or sid == self.parent_seed.sid: - from .globalvars import g - if g.debug_subseed: # should get ≈450 collisions for first 1,000,000 subseeds + if self.parent_seed.cfg.debug_subseed: # should get ≈450 collisions for first 1,000,000 subseeds self._collision_debug_msg(sid,idx,nonce) else: self.data[length][sid] = (idx,nonce) diff --git a/mmgen/term.py b/mmgen/term.py index 3fcfa540..4c7f0b55 100755 --- a/mmgen/term.py +++ b/mmgen/term.py @@ -20,10 +20,12 @@ term: Terminal classes for the MMGen suite """ +# TODO: reimplement as instance instead of class + import sys,os,time from collections import namedtuple -from .globalvars import g,gc +from .globalvars import gc from .util import msg,msg_r,die try: @@ -108,7 +110,7 @@ class MMGenTermLinux(MMGenTerm): @classmethod def kb_hold_protect(cls): - if g.hold_protect_disable: + if cls.cfg.hold_protect_disable: return tty.setcbreak(cls.stdin_fd) timeout = 0.3 @@ -130,7 +132,7 @@ class MMGenTermLinux(MMGenTerm): timeout = 0.3 tty.setcbreak(cls.stdin_fd) msg_r(prompt) - if g.hold_protect_disable: + if cls.cfg.hold_protect_disable: prehold_protect = False while True: # Protect against held-down key before read() @@ -271,7 +273,7 @@ def get_term(): 'mswin': (MMGenTermMSWin if sys.stdin.isatty() else MMGenTermMSWinStub), }[_platform] -def init_term(noecho=False): +def init_term(cfg,noecho=False): term = get_term() @@ -281,5 +283,7 @@ def init_term(noecho=False): for var in ('get_char','get_char_raw','kb_hold_protect','get_terminal_size'): setattr( self, var, getattr(term,var) ) + term.cfg = cfg # setting the _class_ attribute + def reset_term(): get_term().reset() diff --git a/mmgen/tool/api.py b/mmgen/tool/api.py index 067f7295..38ef8f2e 100755 --- a/mmgen/tool/api.py +++ b/mmgen/tool/api.py @@ -35,7 +35,10 @@ class tool_api( Example: from mmgen.tool.api import tool_api - tool = tool_api() + from mmgen.opts import init + + # Initialize a tool API instance: + tool = tool_api(init()) # Set the coin and network: tool.init_coin('btc','mainnet') @@ -63,16 +66,15 @@ class tool_api( need_proto = True need_addrtype = True - def __init__(self): + def __init__(self,cfg): """ Initializer - takes no arguments """ - import mmgen.opts as opts - from ..opts import opt - opts.UserOpts._reset_ok += ('usr_randchars',) - if not opt._lock: + type(cfg)._reset_ok += ('usr_randchars',) + if not cfg._lock: + import mmgen.opts as opts opts.init() - super().__init__() + super().__init__(cfg) def init_coin(self,coinsym,network): """ @@ -81,8 +83,8 @@ class tool_api( Valid choices for network: 'mainnet','testnet','regtest' """ from ..protocol import init_proto,warn_trustlevel - warn_trustlevel(coinsym) - self.proto = init_proto(coinsym,network=network,need_amt=True) + warn_trustlevel(self.cfg) + self.proto = init_proto( self.cfg, coinsym, network=network, need_amt=True ) return self.proto @property @@ -139,10 +141,8 @@ class tool_api( The number of keystrokes of entropy to be gathered from the user. Setting to zero disables user entropy gathering. """ - from ..opts import opt - return opt.usr_randchars + return self.cfg.usr_randchars @usr_randchars.setter def usr_randchars(self,val): - from ..opts import opt - opt.usr_randchars = val + self.cfg.usr_randchars = val diff --git a/mmgen/tool/coin.py b/mmgen/tool/coin.py index 46062aaa..f917365b 100755 --- a/mmgen/tool/coin.py +++ b/mmgen/tool/coin.py @@ -45,8 +45,8 @@ class tool_cmd(tool_cmd_base): def _init_generators(self,arg=None): return generator_data( - kg = KeyGenerator( self.proto, self.mmtype.pubkey_type ), - ag = AddrGenerator( self.proto, self.mmtype ), + kg = KeyGenerator( self.cfg, self.proto, self.mmtype.pubkey_type ), + ag = AddrGenerator( self.cfg, self.proto, self.mmtype ), ) def randwif(self): @@ -54,7 +54,7 @@ class tool_cmd(tool_cmd_base): from ..crypto import Crypto return PrivKey( self.proto, - Crypto().get_random(32), + Crypto(self.cfg).get_random(32), pubkey_type = self.mmtype.pubkey_type, compressed = self.mmtype.compressed ).wif @@ -64,7 +64,7 @@ class tool_cmd(tool_cmd_base): from ..crypto import Crypto privkey = PrivKey( self.proto, - Crypto().get_random(32), + Crypto(self.cfg).get_random(32), pubkey_type = self.mmtype.pubkey_type, compressed = self.mmtype.compressed ) return ( @@ -136,7 +136,7 @@ class tool_cmd(tool_cmd_base): if self.proto.base_proto == 'Ethereum' and len(pubkeyhex) == 128: # support raw ETH pubkeys pubkeyhex = '04' + pubkeyhex from ..keygen import keygen_public_data - ag = AddrGenerator( self.proto, self.mmtype ) + ag = AddrGenerator( self.cfg, self.proto, self.mmtype ) return ag.to_addr(keygen_public_data( pubkey = bytes.fromhex(pubkeyhex), viewkey_bytes = None, @@ -192,4 +192,4 @@ class tool_cmd(tool_cmd_base): def eth_checksummed_addr(self,addr:'sstr'): "create a checksummed Ethereum address" from ..protocol import init_proto - return init_proto('eth').checksummed_addr(addr) + return init_proto( self.cfg, 'eth' ).checksummed_addr(addr) diff --git a/mmgen/tool/common.py b/mmgen/tool/common.py index dbec9628..7b643126 100755 --- a/mmgen/tool/common.py +++ b/mmgen/tool/common.py @@ -31,21 +31,21 @@ class tool_cmd_base(MMGenObject): need_addrtype = False need_amt = False - def __init__(self,cmdname=None,proto=None,mmtype=None): + def __init__(self,cfg,cmdname=None,proto=None,mmtype=None): + + self.cfg = cfg if self.need_proto: - from ..protocol import init_proto_from_opts - self.proto = proto or init_proto_from_opts(need_amt=self.need_amt) - from ..globalvars import g - if g.token: - self.proto.tokensym = g.token.upper() + from ..protocol import init_proto_from_cfg + self.proto = proto or cfg._proto or init_proto_from_cfg(cfg,need_amt=True) + if cfg.token: + self.proto.tokensym = cfg.token.upper() if self.need_addrtype: - from ..opts import opt from ..addr import MMGenAddrType self.mmtype = MMGenAddrType( self.proto, - mmtype or opt.type or self.proto.dfl_mmtype ) + mmtype or cfg.type or self.proto.dfl_mmtype ) @property def user_commands(self): diff --git a/mmgen/tool/file.py b/mmgen/tool/file.py index fe776a1e..c1a1ea62 100755 --- a/mmgen/tool/file.py +++ b/mmgen/tool/file.py @@ -27,18 +27,17 @@ class tool_cmd(tool_cmd_base): need_proto = True - def __init__(self,cmdname=None,proto=None,mmtype=None): + def __init__(self,cfg,cmdname=None,proto=None,mmtype=None): if cmdname == 'txview': self.need_amt = True - super().__init__(cmdname=cmdname,proto=proto,mmtype=mmtype) + super().__init__(cfg=cfg,cmdname=cmdname,proto=proto,mmtype=mmtype) def _file_chksum(self,mmgen_addrfile,obj): kwargs = {'skip_chksum_msg':True} if not obj.__name__ == 'PasswordList': kwargs.update({'key_address_validity_check':False}) - ret = obj( self.proto, mmgen_addrfile, **kwargs ) - from ..opts import opt - if opt.verbose: + ret = obj( self.cfg, self.proto, mmgen_addrfile, **kwargs ) + if self.cfg.verbose: from ..util import msg,capfirst if ret.al_id.mmtype.name == 'password': msg('Passwd fmt: {}\nPasswd len: {}\nID string: {}'.format( @@ -99,6 +98,7 @@ class tool_cmd(tool_cmd_base): async def process_file(f): return (await CompletedTX( + cfg = self.cfg, filename = f.name, quiet_open = True)).info.format( terse=terse, sort=tx_sort ) diff --git a/mmgen/tool/filecrypt.py b/mmgen/tool/filecrypt.py index 51823fea..f38bf7b8 100755 --- a/mmgen/tool/filecrypt.py +++ b/mmgen/tool/filecrypt.py @@ -37,18 +37,18 @@ class tool_cmd(tool_cmd_base): """ def encrypt(self,infile:str,outfile='',hash_preset=''): "encrypt a file" - data = get_data_from_file( infile, 'data for encryption', binary=True ) - enc_d = Crypto().mmgen_encrypt( data, 'data', hash_preset ) + data = get_data_from_file( self.cfg, infile, 'data for encryption', binary=True ) + enc_d = Crypto(self.cfg).mmgen_encrypt( data, 'data', hash_preset ) if not outfile: outfile = f'{os.path.basename(infile)}.{Crypto.mmenc_ext}' - write_data_to_file( outfile, enc_d, 'encrypted data', binary=True ) + write_data_to_file( self.cfg, outfile, enc_d, 'encrypted data', binary=True ) return True def decrypt(self,infile:str,outfile='',hash_preset=''): "decrypt a file" - enc_d = get_data_from_file( infile, 'encrypted data', binary=True ) + enc_d = get_data_from_file( self.cfg, infile, 'encrypted data', binary=True ) while True: - dec_d = Crypto().mmgen_decrypt( enc_d, 'data', hash_preset ) + dec_d = Crypto(self.cfg).mmgen_decrypt( enc_d, 'data', hash_preset ) if dec_d: break msg('Trying again...') @@ -58,5 +58,5 @@ class tool_cmd(tool_cmd_base): outfile = remove_extension(o,Crypto.mmenc_ext) if outfile == o: outfile += '.dec' - write_data_to_file( outfile, dec_d, 'decrypted data', binary=True ) + write_data_to_file( self.cfg, outfile, dec_d, 'decrypted data', binary=True ) return True diff --git a/mmgen/tool/fileutil.py b/mmgen/tool/fileutil.py index ce67e674..57a2048a 100755 --- a/mmgen/tool/fileutil.py +++ b/mmgen/tool/fileutil.py @@ -24,7 +24,7 @@ import os from .common import tool_cmd_base from ..globalvars import gc -from ..util import msg,msg_r,qmsg,die,suf,make_full_path +from ..util import msg,msg_r,die,suf,make_full_path from ..crypto import Crypto class tool_cmd(tool_cmd_base): @@ -94,7 +94,6 @@ class tool_cmd(tool_cmd_base): from cryptography.hazmat.primitives.ciphers import Cipher,algorithms,modes from cryptography.hazmat.backends import default_backend - from ..opts import opt from ..util2 import parse_bytespec def encrypt_worker(wid): @@ -111,11 +110,11 @@ class tool_cmd(tool_cmd_base): q2.task_done() nbytes = parse_bytespec(nbytes) - if opt.outdir: - outfile = make_full_path( opt.outdir, outfile ) + if self.cfg.outdir: + outfile = make_full_path( self.cfg.outdir, outfile ) f = open(outfile,'wb') - key = Crypto().get_random(32) + key = Crypto(self.cfg).get_random(32) q1,q2 = ( Queue(), Queue() ) for i in range(max(1,threads-2)): @@ -146,14 +145,13 @@ class tool_cmd(tool_cmd_base): if not silent: msg(f'\rRead: {nbytes} bytes') - qmsg(f'\r{nbytes} byte{suf(nbytes)} of random data written to file {outfile!r}') + self.cfg._util.qmsg(f'\r{nbytes} byte{suf(nbytes)} of random data written to file {outfile!r}') return True def extract_key_from_geth_wallet( self, wallet_file:str, check_addr=True ): "decrypt the encrypted private key in a Geth keystore wallet, returning the decrypted key" from ..ui import line_input - from ..opts import opt from ..proto.eth.misc import extract_key_from_geth_keystore_wallet - passwd = line_input( 'Enter passphrase: ', echo=opt.echo_passphrase ).strip().encode() - return extract_key_from_geth_keystore_wallet( wallet_file, passwd, check_addr ).hex() + passwd = line_input( self.cfg, 'Enter passphrase: ', echo=self.cfg.echo_passphrase ).strip().encode() + return extract_key_from_geth_keystore_wallet( self.cfg, wallet_file, passwd, check_addr ).hex() diff --git a/mmgen/tool/help.py b/mmgen/tool/help.py index 5f4908a9..35483b6d 100755 --- a/mmgen/tool/help.py +++ b/mmgen/tool/help.py @@ -131,7 +131,7 @@ def gen_tool_usage(): for line in m2.rstrip().split('\n'): yield line.lstrip('\t') -def gen_tool_cmd_usage(mod,cmdname): +def gen_tool_cmd_usage(cfg,mod,cmdname): from ..globalvars import gc from ..util import capfirst @@ -182,14 +182,14 @@ def gen_tool_cmd_usage(mod,cmdname): for line in docstr.split('\n')[1:]: yield line.lstrip('\t') -def usage(cmdname=None,exit_val=1): +def usage(cfg,cmdname=None,exit_val=1): from ..util import Msg,die if cmdname: for mod,cmdlist in main_tool.mods.items(): if cmdname in cmdlist: - Msg('\n'.join(gen_tool_cmd_usage(mod,cmdname))) + Msg('\n'.join(gen_tool_cmd_usage(cfg,mod,cmdname))) break else: die(1,f'{cmdname!r}: no such tool command') @@ -205,8 +205,8 @@ class tool_cmd(tool_cmd_base): def help(self,command_name=''): "display usage information for a single command or all commands" - usage(command_name,exit_val=0) + usage(self.cfg,command_name,exit_val=0) def usage(self,command_name=''): "display usage information for a single command or all commands" - usage(command_name,exit_val=0) + usage(self.cfg,command_name,exit_val=0) diff --git a/mmgen/tool/mnemonic.py b/mmgen/tool/mnemonic.py index 9c543317..ce9e4b18 100755 --- a/mmgen/tool/mnemonic.py +++ b/mmgen/tool/mnemonic.py @@ -62,7 +62,7 @@ class tool_cmd(tool_cmd_base): def _xmr_reduce(self,bytestr): from ..protocol import init_proto - proto = init_proto('xmr') + proto = init_proto( self.cfg, 'xmr' ) if len(bytestr) != proto.privkey_len: die(1,'{!r}: invalid bit length for Monero private key (must be {})'.format( len(bytestr*8), @@ -72,11 +72,10 @@ class tool_cmd(tool_cmd_base): def _do_random_mn(self,nbytes:int,fmt:str): assert nbytes in (16,24,32), 'nbytes must be 16, 24 or 32' from ..crypto import Crypto - randbytes = Crypto().get_random(nbytes) + randbytes = Crypto(self.cfg).get_random(nbytes) if fmt == 'xmrseed': randbytes = self._xmr_reduce(randbytes) - from ..opts import opt - if opt.verbose: + if self.cfg.verbose: from ..util import msg msg(f'Seed: {randbytes.hex()}') return self.hex2mn(randbytes.hex(),fmt=fmt) @@ -111,7 +110,7 @@ class tool_cmd(tool_cmd_base): print_mn: 'print the seed phrase after entry' = False ): "convert an interactively supplied mnemonic seed phrase to a hexadecimal string" from ..mn_entry import mn_entry - mn = mn_entry(fmt).get_mnemonic_from_user(25 if fmt == 'xmrseed' else mn_len,validate=False) + mn = mn_entry( self.cfg, fmt ).get_mnemonic_from_user(25 if fmt == 'xmrseed' else mn_len,validate=False) if print_mn: from ..util import msg msg(mn) @@ -119,7 +118,7 @@ class tool_cmd(tool_cmd_base): def mn_stats(self, fmt:mn_opts_disp = dfl_mnemonic_fmt ): "show stats for a mnemonic wordlist" - return mnemonic_fmts[fmt].conv_cls(fmt).check_wordlist() + return mnemonic_fmts[fmt].conv_cls(fmt).check_wordlist(self.cfg) def mn_printlist(self, fmt: mn_opts_disp = dfl_mnemonic_fmt, diff --git a/mmgen/tool/rpc.py b/mmgen/tool/rpc.py index c32547ee..219408c2 100755 --- a/mmgen/tool/rpc.py +++ b/mmgen/tool/rpc.py @@ -33,11 +33,11 @@ class tool_cmd(tool_cmd_base): async def daemon_version(self): "print coin daemon version" from ..daemon import CoinDaemon - from ..globalvars import g - d = CoinDaemon( proto=self.proto, test_suite=g.test_suite ) + d = CoinDaemon( cfg=self.cfg, proto=self.proto, test_suite=self.cfg.test_suite ) if self.proto.base_proto == 'Monero': from ..proto.xmr.rpc import MoneroRPCClient r = MoneroRPCClient( + cfg = self.cfg, proto = self.proto, daemon = d, host = d.host, @@ -47,7 +47,7 @@ class tool_cmd(tool_cmd_base): ignore_daemon_version = True ) else: from ..rpc import rpc_init - r = await rpc_init( self.proto, ignore_daemon_version=True ) + r = await rpc_init( self.cfg, self.proto, ignore_daemon_version=True ) return f'{d.coind_name} version {r.daemon_version} ({r.daemon_version_str})' async def getbalance(self, @@ -56,8 +56,7 @@ class tool_cmd(tool_cmd_base): pager: 'send output to pager' = False ): "list confirmed/unconfirmed, spendable/unspendable balances in tracking wallet" from ..tw.bal import TwGetBalance - from ..globalvars import g - return (await TwGetBalance(self.proto,minconf,quiet)).format(color=g.color) + return (await TwGetBalance(self.cfg,self.proto,minconf,quiet)).format(color=self.cfg.color) async def twops(self, obj,pager,reverse,detail,sort,age_fmt,interactive, @@ -94,7 +93,7 @@ class tool_cmd(tool_cmd_base): "view tracking wallet unspent outputs" from ..tw.unspent import TwUnspentOutputs - obj = await TwUnspentOutputs(self.proto,minconf=minconf) + obj = await TwUnspentOutputs(self.cfg,self.proto,minconf=minconf) return await self.twops( obj,pager,reverse,wide,sort,age_fmt,interactive, show_mmid = show_mmid ) @@ -109,7 +108,7 @@ class tool_cmd(tool_cmd_base): interactive: 'enable interactive operation' = False ): "view transaction history of tracking wallet" - obj = await TwTxHistory(self.proto,sinceblock=sinceblock) + obj = await TwTxHistory(self.cfg,self.proto,sinceblock=sinceblock) return await self.twops( obj,pager,reverse,detail,sort,age_fmt,interactive ) @@ -146,7 +145,7 @@ class tool_cmd(tool_cmd_base): assert showused in (0,1,2), f"‘showused’ must have a value of 0, 1 or 2" from ..tw.addresses import TwAddresses - obj = await TwAddresses(self.proto,minconf=minconf,mmgen_addrs=mmgen_addrs) + obj = await TwAddresses(self.cfg,self.proto,minconf=minconf,mmgen_addrs=mmgen_addrs) return await self.twops( obj,pager,reverse,wide,sort,age_fmt,interactive, showcoinaddrs = showcoinaddrs, @@ -157,7 +156,7 @@ class tool_cmd(tool_cmd_base): async def add_label(self,mmgen_or_coin_addr:str,label:str): "add descriptive label for address in tracking wallet" from ..tw.ctl import TwCtl - return await (await TwCtl(self.proto,mode='w')).set_comment(mmgen_or_coin_addr,label) + return await (await TwCtl(self.cfg,self.proto,mode='w')).set_comment(mmgen_or_coin_addr,label) async def remove_label(self,mmgen_or_coin_addr:str): "remove descriptive label for address in tracking wallet" @@ -168,7 +167,7 @@ class tool_cmd(tool_cmd_base): "remove an address from tracking wallet" from ..tw.ctl import TwCtl # returns None on failure: - ret = await (await TwCtl(self.proto,mode='w')).remove_address(mmgen_or_coin_addr) + ret = await (await TwCtl(self.cfg,self.proto,mode='w')).remove_address(mmgen_or_coin_addr) if ret: from ..util import msg msg(f'Address {ret!r} deleted from tracking wallet') @@ -177,7 +176,7 @@ class tool_cmd(tool_cmd_base): async def resolve_address(self,mmgen_or_coin_addr:str): "resolve an MMGen address in the tracking wallet to a coin address or vice-versa" from ..tw.ctl import TwCtl - ret = await (await TwCtl(self.proto,mode='w')).resolve_address( mmgen_or_coin_addr ) + ret = await (await TwCtl(self.cfg,self.proto,mode='w')).resolve_address( mmgen_or_coin_addr ) if ret: from ..util import Msg from ..addr import is_coin_addr @@ -188,7 +187,7 @@ class tool_cmd(tool_cmd_base): async def rescan_address(self,mmgen_or_coin_addr:str): "rescan an address in the tracking wallet to update its balance" from ..tw.ctl import TwCtl - return await (await TwCtl(self.proto,mode='w')).rescan_address( mmgen_or_coin_addr ) + return await (await TwCtl(self.cfg,self.proto,mode='w')).rescan_address( mmgen_or_coin_addr ) async def rescan_blockchain(self, start_block: int = None, @@ -203,7 +202,7 @@ class tool_cmd(tool_cmd_base): parameter. """ from ..tw.ctl import TwCtl - ret = await (await TwCtl(self.proto,mode='w')).rescan_blockchain(start_block,stop_block) + ret = await (await TwCtl(self.cfg,self.proto,mode='w')).rescan_blockchain(start_block,stop_block) return True async def twexport(self,include_amts=True,pretty=False,prune=False,warn_used=False): @@ -228,6 +227,7 @@ class tool_cmd(tool_cmd_base): """ from ..tw.json import TwJSON await TwJSON.Export( + self.cfg, self.proto, include_amts = include_amts, pretty = pretty, @@ -249,5 +249,5 @@ class tool_cmd(tool_cmd_base): rescan_blockchain’. """ from ..tw.json import TwJSON - await TwJSON.Import( self.proto, filename, ignore_checksum=ignore_checksum, batch=batch ) + await TwJSON.Import( self.cfg, self.proto, filename, ignore_checksum=ignore_checksum, batch=batch ) return True diff --git a/mmgen/tool/util.py b/mmgen/tool/util.py index cc0f753d..1b10ae00 100755 --- a/mmgen/tool/util.py +++ b/mmgen/tool/util.py @@ -94,7 +94,7 @@ class tool_cmd(tool_cmd_base): nbytes: 'number of bytes to output' = 32 ): "print 'n' bytes (default 32) of random data in hex format" from ..crypto import Crypto - return Crypto().get_random( nbytes ).hex() + return Crypto(self.cfg).get_random( nbytes ).hex() def hexreverse(self,hexstr:'sstr'): "reverse bytes of a hexadecimal string" @@ -103,7 +103,7 @@ class tool_cmd(tool_cmd_base): def hexlify(self,infile:str): "convert bytes in file to hexadecimal (use '-' for stdin)" from ..fileutil import get_data_from_file - data = get_data_from_file( infile, dash=True, quiet=True, binary=True ) + data = get_data_from_file( self.cfg, infile, dash=True, quiet=True, binary=True ) return data.hex() def unhexlify(self,hexstr:'sstr'): @@ -117,7 +117,7 @@ class tool_cmd(tool_cmd_base): "create hexdump of data from file (use '-' for stdin)" from ..fileutil import get_data_from_file from ..util2 import pretty_hexdump - data = get_data_from_file( infile, dash=True, quiet=True, binary=True ) + data = get_data_from_file( self.cfg, infile, dash=True, quiet=True, binary=True ) return pretty_hexdump( data, cols=cols, line_nums=line_nums ).rstrip() def unhexdump(self,infile:str): @@ -127,7 +127,7 @@ class tool_cmd(tool_cmd_base): msvcrt.setmode( sys.stdout.fileno(), os.O_BINARY ) from ..fileutil import get_data_from_file from ..util2 import decode_pretty_hexdump - hexdata = get_data_from_file( infile, dash=True, quiet=True ) + hexdata = get_data_from_file( self.cfg, infile, dash=True, quiet=True ) return decode_pretty_hexdump(hexdata) def hash160(self,hexstr:'sstr'): @@ -144,7 +144,7 @@ class tool_cmd(tool_cmd_base): from hashlib import sha256 if file_input: from ..fileutil import get_data_from_file - b = get_data_from_file( data, binary=True ) + b = get_data_from_file( self.cfg, data, binary=True ) elif hex_input: from ..util2 import decode_pretty_hexdump b = decode_pretty_hexdump(data) @@ -157,7 +157,7 @@ class tool_cmd(tool_cmd_base): from ..util import make_chksum_6 from ..fileutil import get_data_from_file return make_chksum_6( - get_data_from_file( infile, dash=True, quiet=True, binary=True )) + get_data_from_file( self.cfg, infile, dash=True, quiet=True, binary=True )) def str2id6(self,string:'sstr'): # retain ignoring of space for backwards compat "generate 6-character MMGen ID for a string, ignoring spaces in string" @@ -169,7 +169,7 @@ class tool_cmd(tool_cmd_base): from ..util import make_chksum_8 from ..fileutil import get_data_from_file return make_chksum_8( - get_data_from_file( infile, dash=True, quiet=True, binary=True )) + get_data_from_file( self.cfg, infile, dash=True, quiet=True, binary=True )) def randb58(self, nbytes: 'number of bytes to output' = 32, @@ -177,13 +177,13 @@ class tool_cmd(tool_cmd_base): "generate random data (default: 32 bytes) and convert it to base 58" from ..baseconv import baseconv from ..crypto import Crypto - return baseconv('b58').frombytes( Crypto().get_random(nbytes), pad=pad, tostr=True ) + return baseconv('b58').frombytes( Crypto(self.cfg).get_random(nbytes), pad=pad, tostr=True ) def bytestob58(self,infile:str,pad: 'pad output to this width' = 0): "convert bytes to base 58 (supply data via STDIN)" from ..fileutil import get_data_from_file from ..baseconv import baseconv - data = get_data_from_file( infile, dash=True, quiet=True, binary=True ) + data = get_data_from_file( self.cfg, infile, dash=True, quiet=True, binary=True ) return baseconv('b58').frombytes( data, pad=pad, tostr=True ) def b58tobytes(self,b58_str:'sstr',pad: 'pad output to this width' = 0): diff --git a/mmgen/tool/wallet.py b/mmgen/tool/wallet.py index 84ed2fb6..5ecc3896 100755 --- a/mmgen/tool/wallet.py +++ b/mmgen/tool/wallet.py @@ -22,7 +22,6 @@ tool.wallet: Wallet routines for the 'mmgen-tool' utility from .common import tool_cmd_base -from ..opts import opt from ..subseed import SubSeedList from ..seedsplit import MasterShareIdx from ..wallet import Wallet @@ -30,32 +29,33 @@ from ..wallet import Wallet class tool_cmd(tool_cmd_base): "key, address or subseed generation from an MMGen wallet" - def __init__(self,cmdname=None,proto=None,mmtype=None): + def __init__(self,cfg,cmdname=None,proto=None,mmtype=None): self.need_proto = cmdname in ('gen_key','gen_addr') - super().__init__(cmdname=cmdname,proto=proto,mmtype=mmtype) + super().__init__(cfg,cmdname=cmdname,proto=proto,mmtype=mmtype) def _get_seed_file(self,wallet): from ..fileutil import get_seed_file return get_seed_file( + cfg = self.cfg, wallets = [wallet] if wallet else [], nargs = 1 ) def get_subseed(self,subseed_idx:str,wallet=''): "get the Seed ID of a single subseed by Subseed Index for default or specified wallet" - opt.quiet = True - return Wallet(self._get_seed_file(wallet)).seed.subseed(subseed_idx).sid + self.cfg.quiet = True + return Wallet(self.cfg,self._get_seed_file(wallet)).seed.subseed(subseed_idx).sid def get_subseed_by_seed_id(self,seed_id:str,wallet='',last_idx=SubSeedList.dfl_len): "get the Subseed Index of a single subseed by Seed ID for default or specified wallet" - opt.quiet = True - ret = Wallet(self._get_seed_file(wallet)).seed.subseed_by_seed_id( seed_id, last_idx ) + self.cfg.quiet = True + ret = Wallet(self.cfg,self._get_seed_file(wallet)).seed.subseed_by_seed_id( seed_id, last_idx ) return ret.ss_idx if ret else None def list_subseeds(self,subseed_idx_range:str,wallet=''): "list a range of subseed Seed IDs for default or specified wallet" - opt.quiet = True + self.cfg.quiet = True from ..subseed import SubSeedIdxRange - return Wallet(self._get_seed_file(wallet)).seed.subseeds.format( *SubSeedIdxRange(subseed_idx_range) ) + return Wallet(self.cfg,self._get_seed_file(wallet)).seed.subseeds.format( *SubSeedIdxRange(subseed_idx_range) ) def list_shares(self, share_count: int, @@ -63,8 +63,8 @@ class tool_cmd(tool_cmd_base): master_share: f'(min:1, max:{MasterShareIdx.max_val}, 0=no master share)' = 0, wallet = '' ): "list the Seed IDs of the shares resulting from a split of default or specified wallet" - opt.quiet = True - return Wallet(self._get_seed_file(wallet)).seed.split( share_count, id_str, master_share ).format() + self.cfg.quiet = True + return Wallet(self.cfg,self._get_seed_file(wallet)).seed.split( share_count, id_str, master_share ).format() def gen_key(self,mmgen_addr:str,wallet=''): "generate a single WIF key for specified MMGen address from default or specified wallet" @@ -79,14 +79,15 @@ class tool_cmd(tool_cmd_base): from ..addrlist import AddrList,AddrIdxList addr = MMGenID( self.proto, mmgen_addr ) - opt.quiet = True - ss = Wallet(self._get_seed_file(wallet)) + self.cfg.quiet = True + ss = Wallet(self.cfg,self._get_seed_file(wallet)) if ss.seed.sid != addr.sid: from ..util import die die(1,f'Seed ID of requested address ({addr.sid}) does not match wallet ({ss.seed.sid})') d = AddrList( + cfg = self.cfg, proto = self.proto, seed = ss.seed, addr_idxs = AddrIdxList(str(addr.idx)), diff --git a/mmgen/tw/addresses.py b/mmgen/tw/addresses.py index 84b9f596..2a44fd79 100755 --- a/mmgen/tw/addresses.py +++ b/mmgen/tw/addresses.py @@ -74,9 +74,9 @@ class TwAddresses(TwView): def coinaddr_list(self): return [d.addr for d in self.data] - async def __init__(self,proto,minconf=1,mmgen_addrs='',get_data=False): + async def __init__(self,cfg,proto,minconf=1,mmgen_addrs='',get_data=False): - await super().__init__(proto) + await super().__init__(cfg,proto) self.minconf = NonNegativeInt(minconf) @@ -320,10 +320,9 @@ class TwAddresses(TwView): top = len(data) - 1 if top is None else top ) if start is not None: - from ..opts import opt for d in data[start:]: if d.al_id == al_id: - if not d.recvd and (opt.autochg_ignore_labels or not d.comment): + if not d.recvd and (self.cfg.autochg_ignore_labels or not d.comment): if d.comment: msg('{} {} {} {}{}'.format( yellow('WARNING: address'), @@ -362,7 +361,7 @@ class TwAddresses(TwView): from ..ui import line_input while True: - res = line_input(prompt) + res = line_input( self.cfg, prompt ) if is_int(res) and 0 < int(res) <= len(addrs): return addrs[int(res)-1] msg(f'{res}: invalid entry') diff --git a/mmgen/tw/bal.py b/mmgen/tw/bal.py index 75c4c201..93ecc1aa 100755 --- a/mmgen/tw/bal.py +++ b/mmgen/tw/bal.py @@ -29,10 +29,10 @@ from ..rpc import rpc_init class TwGetBalance(MMGenObject,metaclass=AsyncInit): - def __new__(cls,proto,*args,**kwargs): + def __new__(cls,cfg,proto,*args,**kwargs): return MMGenObject.__new__(proto.base_proto_subclass(cls,'tw.bal')) - async def __init__(self,proto,minconf,quiet): + async def __init__(self,cfg,proto,minconf,quiet): class BalanceInfo(dict): def __init__(self): @@ -50,7 +50,7 @@ class TwGetBalance(MMGenObject,metaclass=AsyncInit): self.quiet = quiet self.proto = proto self.data = {k:self.balance_info() for k in self.start_labels} - self.rpc = await rpc_init(proto) + self.rpc = await rpc_init(cfg,proto) if minconf < 2 and 'lt_minconf' in self.conf_cols: del self.conf_cols['lt_minconf'] diff --git a/mmgen/tw/ctl.py b/mmgen/tw/ctl.py index 87fd98fc..7e84e4b8 100755 --- a/mmgen/tw/ctl.py +++ b/mmgen/tw/ctl.py @@ -23,8 +23,7 @@ tw.ctl: Tracking wallet control class for the MMGen suite import json from collections import namedtuple -from ..globalvars import g -from ..util import msg,msg_r,qmsg,dmsg,suf,die +from ..util import msg,msg_r,suf,die from ..base_obj import AsyncInit from ..objmethods import MMGenObject from ..obj import TwComment,get_obj @@ -53,10 +52,10 @@ class TwCtl(MMGenObject,metaclass=AsyncInit): aggressive_sync = False importing = False - def __new__(cls,proto,*args,**kwargs): + def __new__(cls,cfg,proto,*args,**kwargs): return MMGenObject.__new__(proto.base_proto_subclass(cls,'tw.ctl')) - async def __init__(self,proto,mode='r',token_addr=None,rpc_ignore_wallet=False): + async def __init__(self,cfg,proto,mode='r',token_addr=None,rpc_ignore_wallet=False): assert mode in ('r','w','i'), f"{mode!r}: wallet mode must be 'r','w' or 'i'" if mode == 'i': @@ -64,7 +63,8 @@ class TwCtl(MMGenObject,metaclass=AsyncInit): mode = 'w' # TODO: create on demand - only certain ops require RPC - self.rpc = await rpc_init( proto, ignore_wallet=rpc_ignore_wallet ) + self.cfg = cfg + self.rpc = await rpc_init( cfg, proto, ignore_wallet=rpc_ignore_wallet ) self.proto = proto self.mode = mode self.desc = self.base_desc = f'{self.proto.name} tracking wallet' @@ -86,9 +86,9 @@ class TwCtl(MMGenObject,metaclass=AsyncInit): def init_from_wallet_file(self): import os tw_dir = ( - os.path.join(g.data_dir) if self.proto.coin == 'BTC' else + os.path.join(self.cfg.data_dir) if self.proto.coin == 'BTC' else os.path.join( - g.data_dir_root, + self.cfg.data_dir_root, 'altcoins', self.proto.coin.lower(), ('' if self.proto.network == 'mainnet' else 'testnet') @@ -99,7 +99,7 @@ class TwCtl(MMGenObject,metaclass=AsyncInit): check_or_create_dir(tw_dir) try: - self.orig_data = get_data_from_file(self.tw_fn,quiet=True) + self.orig_data = get_data_from_file( self.cfg, self.tw_fn, quiet=True ) self.data = json.loads(self.orig_data) except: try: os.stat(self.tw_fn) @@ -116,7 +116,7 @@ class TwCtl(MMGenObject,metaclass=AsyncInit): if self.mode == 'w': import atexit def del_twctl(twctl): - dmsg(f'Running exit handler del_twctl() for {twctl!r}') + self.cfg._util.dmsg(f'Running exit handler del_twctl() for {twctl!r}') del twctl atexit.register(del_twctl,self) @@ -135,7 +135,7 @@ class TwCtl(MMGenObject,metaclass=AsyncInit): """ if getattr(self,'mode',None) == 'w': # mode attr might not exist in this state self.write() - elif g.debug: + elif self.cfg.debug: msg('read-only wallet, doing nothing') def conv_types(self,ad): @@ -163,7 +163,7 @@ class TwCtl(MMGenObject,metaclass=AsyncInit): def get_cached_balance(self,addr,session_cache,data_root): if addr in session_cache: return self.proto.coin_amt(session_cache[addr]) - if not g.cached_balances: + if not self.cfg.cached_balances: return None if addr in data_root and 'balance' in data_root[addr]: return self.proto.coin_amt(data_root[addr]['balance']) @@ -185,6 +185,7 @@ class TwCtl(MMGenObject,metaclass=AsyncInit): def write_changed(self,data,quiet): from ..fileutil import write_data_to_file write_data_to_file( + self.cfg, self.tw_fn, data, desc = f'{self.base_desc} data', @@ -198,14 +199,14 @@ class TwCtl(MMGenObject,metaclass=AsyncInit): def write(self,quiet=True): if not self.use_tw_file: - dmsg("'use_tw_file' is False, doing nothing") + self.cfg._util.dmsg("'use_tw_file' is False, doing nothing") return - dmsg(f'write(): checking if {self.desc} data has changed') + self.cfg._util.dmsg(f'write(): checking if {self.desc} data has changed') wdata = json.dumps(self.data) if self.orig_data != wdata: self.write_changed(wdata,quiet=quiet) - elif g.debug: + elif self.cfg.debug: msg('Data is unchanged\n') async def resolve_address(self,addrspec): @@ -296,7 +297,7 @@ class TwCtl(MMGenObject,metaclass=AsyncInit): async def do_import(address,comment,message): try: res = await self.import_address( address, comment ) - qmsg(message) + self.cfg._util.qmsg(message) return res except Exception as e: die(2,f'\nImport of address {address!r} failed: {e.args[0]!r}') diff --git a/mmgen/tw/json.py b/mmgen/tw/json.py index 62a6e76e..0dffb6bd 100755 --- a/mmgen/tw/json.py +++ b/mmgen/tw/json.py @@ -15,8 +15,7 @@ tw.json: export and import tracking wallet to JSON format import os,json from collections import namedtuple -from ..opts import opt -from ..util import msg,ymsg,fmt,suf,die,make_timestamp,make_chksum_8,compare_or_die +from ..util import msg,ymsg,fmt,suf,die,make_timestamp,make_chksum_8 from ..base_obj import AsyncInit from ..objmethods import MMGenObject from ..rpc import json_encoder @@ -30,10 +29,11 @@ class TwJSON: pruned = None fn_pfx = 'mmgen-tracking-wallet-dump' - def __new__(cls,proto,*args,**kwargs): + def __new__(cls,cfg,proto,*args,**kwargs): return MMGenObject.__new__(proto.base_proto_subclass(TwJSON,'tw.json',cls.__name__)) - def __init__(self,proto): + def __init__(self,cfg,proto): + self.cfg = cfg self.proto = proto self.coin = proto.coin_id.lower() self.network = proto.network @@ -54,7 +54,7 @@ class TwJSON: from ..addrlist import AddrIdxList prune_id = AddrIdxList(idx_list=self.pruned).id_str fn = get_fn(prune_id) - if len(fn) > os.statvfs(opt.outdir or os.curdir).f_namemax: + if len(fn) > os.statvfs(self.cfg.outdir or os.curdir).f_namemax: fn = get_fn(f'idhash={make_chksum_8(prune_id.encode()).lower()}') else: fn = get_fn(None) @@ -84,11 +84,11 @@ class TwJSON: blockchain_rescan_warning = None - async def __init__(self,proto,filename,ignore_checksum=False,batch=False): + async def __init__(self,cfg,proto,filename,ignore_checksum=False,batch=False): - super().__init__(proto) + super().__init__(cfg,proto) - self.twctl = await TwCtl( proto, mode='i', rpc_ignore_wallet=True ) + self.twctl = await TwCtl( cfg, proto, mode='i', rpc_ignore_wallet=True ) def check_network(data): coin,network = data['network'].split('_') @@ -108,7 +108,7 @@ class TwJSON: def verify_data(d): check_network(d['data']) check_chksum(d) - compare_or_die( + self.cfg._util.compare_or_die( val1 = self.mappings_chksum, val2 = d['data']['mappings_checksum'], desc1 = 'computed mappings checksum', @@ -118,7 +118,7 @@ class TwJSON: return True from ..fileutil import get_data_from_file - self.data = json.loads(get_data_from_file(filename,quiet=True)) + self.data = json.loads(get_data_from_file( self.cfg, filename, quiet=True )) self.keys = self.data['data']['entries_keys'] self.entries = await self.get_entries() @@ -141,7 +141,7 @@ class TwJSON: msg('\n'+fmt(self.info_msg.strip(),indent=' ')) from ..ui import keypress_confirm - if not keypress_confirm('Continue?'): + if not keypress_confirm( self.cfg, 'Continue?' ): msg('Exiting at user request') return False @@ -152,7 +152,7 @@ class TwJSON: class Export(Base,metaclass=AsyncInit): - async def __init__(self,proto,include_amts=True,pretty=False,prune=False,warn_used=False): + async def __init__(self,cfg,proto,include_amts=True,pretty=False,prune=False,warn_used=False): if prune and not self.can_prune: die(1,f'Pruning not supported for {proto.name} protocol') @@ -160,12 +160,12 @@ class TwJSON: self.prune = prune self.warn_used = warn_used - super().__init__(proto) + super().__init__(cfg,proto) if not include_amts: self.keys.remove('amount') - self.twctl = await TwCtl( proto ) + self.twctl = await TwCtl( cfg, proto ) self.entries = await self.get_entries() @@ -190,6 +190,7 @@ class TwJSON: from ..fileutil import write_data_to_file write_data_to_file( + cfg = self.cfg, outfile = self.dump_fn, data = self.json_dump( { diff --git a/mmgen/tw/prune.py b/mmgen/tw/prune.py index 3fdc8375..9f2ce467 100755 --- a/mmgen/tw/prune.py +++ b/mmgen/tw/prune.py @@ -70,7 +70,7 @@ class TwAddressesPrune(TwAddresses): from ..ui import line_input msg('') while True: - reply = line_input(prompt).strip() + reply = line_input( parent.cfg, prompt ).strip() if reply: from ..addrlist import AddrIdxList from ..obj import get_obj diff --git a/mmgen/tw/txhistory.py b/mmgen/tw/txhistory.py index 64187509..19f878dd 100755 --- a/mmgen/tw/txhistory.py +++ b/mmgen/tw/txhistory.py @@ -42,8 +42,8 @@ class TwTxHistory(TwView): filters = ('show_unconfirmed',) mod_subpath = 'tw.txhistory' - async def __init__(self,proto,sinceblock=0): - await super().__init__(proto) + async def __init__(self,cfg,proto,sinceblock=0): + await super().__init__(cfg,proto) self.sinceblock = NonNegativeInt( sinceblock if sinceblock >= 0 else self.rpc.blockcount + sinceblock ) @property diff --git a/mmgen/tw/unspent.py b/mmgen/tw/unspent.py index 9a36edbc..eb80d03e 100755 --- a/mmgen/tw/unspent.py +++ b/mmgen/tw/unspent.py @@ -20,7 +20,6 @@ tw.unspent: Tracking wallet unspent outputs class for the MMGen suite """ -from ..globalvars import g from ..util import msg,suf,fmt from ..objmethods import MMGenObject from ..obj import ( @@ -77,8 +76,8 @@ class TwUnspentOutputs(TwView): def amt2(self,value): return self.proto.coin_amt(value) - async def __init__(self,proto,minconf=1,addrs=[]): - await super().__init__(proto) + async def __init__(self,cfg,proto,minconf=1,addrs=[]): + await super().__init__(cfg,proto) self.minconf = minconf self.addrs = addrs from ..globalvars import gc diff --git a/mmgen/tw/view.py b/mmgen/tw/view.py index 71423a8a..31ab5ac2 100755 --- a/mmgen/tw/view.py +++ b/mmgen/tw/view.py @@ -23,8 +23,6 @@ tw.view: base class for tracking wallet view classes import sys,time,asyncio from collections import namedtuple -from ..globalvars import g -from ..opts import opt from ..objmethods import Hilite,InitErrors,MMGenObject from ..obj import get_obj,MMGenIdx,MMGenList from ..color import nocolor,yellow,green,red,blue @@ -182,15 +180,16 @@ class TwView(MMGenObject,metaclass=AsyncInit): } } - def __new__(cls,proto,*args,**kwargs): + def __new__(cls,cfg,proto,*args,**kwargs): return MMGenObject.__new__(proto.base_proto_subclass(cls,cls.mod_subpath)) - async def __init__(self,proto): + async def __init__(self,cfg,proto): + self.cfg = cfg self.proto = proto - self.rpc = await rpc_init(proto) + self.rpc = await rpc_init(cfg,proto) if self.has_wallet: from .ctl import TwCtl - self.twctl = await TwCtl(proto,mode='w') + self.twctl = await TwCtl(cfg,proto,mode='w') self.amt_keys = {'amt':'iwidth','amt2':'iwidth2'} if self.has_amt2 else {'amt':'iwidth'} @property @@ -283,15 +282,15 @@ class TwView(MMGenObject,metaclass=AsyncInit): user_resized = False while True: ts = get_terminal_size() - cols = g.columns or ts.width + cols = self.cfg.columns or ts.width lines = ts.height if cols >= min_cols and (min_lines is None or lines >= min_lines): if user_resized: msg_r(CUR_HOME + ERASE_ALL) return _term_dimensions(cols,ts.height) if sys.stdout.isatty(): - if g.columns and cols < min_cols: - die(1,'\n'+fmt(self.twidth_diemsg.format(g.columns,self.desc,min_cols),indent=' ')) + if self.cfg.columns and cols < min_cols: + die(1,'\n'+fmt(self.twidth_diemsg.format(self.cfg.columns,self.desc,min_cols),indent=' ')) else: m,dim = (self.twidth_errmsg,min_cols) if cols < min_cols else (self.theight_errmsg,min_lines) get_char_raw( CUR_HOME + ERASE_ALL + fmt( m.format(self.desc,dim), append='' )) @@ -520,7 +519,7 @@ class TwView(MMGenObject,metaclass=AsyncInit): self.key_mappings.update(self.scroll_keys[gc.platform]) return self.key_mappings - scroll = self.scroll = g.scroll + scroll = self.scroll = self.cfg.scroll key_mappings = make_key_mappings(scroll) action_classes = { k: getattr(self,action_map[v[:2]])() for k,v in key_mappings.items() } @@ -533,7 +532,7 @@ class TwView(MMGenObject,metaclass=AsyncInit): self.oneshot_msg = '' prompt += '\b' - clear_screen = '\n\n' if opt.no_blank else CUR_HOME + ('' if scroll else ERASE_ALL) + clear_screen = '\n\n' if self.cfg.no_blank else CUR_HOME + ('' if scroll else ERASE_ALL) from ..term import get_term,get_char,get_char_raw @@ -581,7 +580,7 @@ class TwView(MMGenObject,metaclass=AsyncInit): def keypress_confirm(self,*args,**kwargs): from ..ui import keypress_confirm - if keypress_confirm(*args,no_nl=self.scroll,**kwargs): + if keypress_confirm( self.cfg, *args, no_nl=self.scroll, **kwargs ): return True else: if self.scroll: @@ -620,6 +619,7 @@ class TwView(MMGenObject,metaclass=AsyncInit): from ..exception import UserNonConfirmation try: write_data_to_file( + cfg = parent.cfg, outfile = outfile, data = print_hdr + await parent.format( display_type = output_type, @@ -654,6 +654,7 @@ class TwView(MMGenObject,metaclass=AsyncInit): while True: msg_r(parent.blank_prompt if parent.scroll else '\n') ret = line_input( + parent.cfg, f'Enter {parent.item_desc} number (or ENTER to return to main menu): ' ) if ret == '': if parent.scroll: @@ -734,6 +735,7 @@ class TwView(MMGenObject,metaclass=AsyncInit): from ..ui import line_input res = line_input( + parent.cfg, 'Enter label text for {} {}: '.format(parent.item_desc,red(f'#{idx}')), insert_txt = cur_comment ) diff --git a/mmgen/tx/__init__.py b/mmgen/tx/__init__.py index 6316f7a8..73001247 100755 --- a/mmgen/tx/__init__.py +++ b/mmgen/tx/__init__.py @@ -33,7 +33,7 @@ def _get_cls_info(clsname,modname,args,kwargs): proto = kwargs['data']['proto'] elif 'filename' in kwargs: from .file import MMGenTxFile - proto = MMGenTxFile.get_proto( kwargs['filename'], quiet_open=True ) + proto = MMGenTxFile.get_proto( kwargs['cfg'], kwargs['filename'], quiet_open=True ) elif clsname == 'Base': proto = None else: @@ -52,19 +52,20 @@ def _get_cls_info(clsname,modname,args,kwargs): kwargs['proto'] = proto - return ( proto, clsname, modname, kwargs ) + return ( kwargs['cfg'], proto, clsname, modname, kwargs ) + def _get_obj( _clsname, _modname, *args, **kwargs ): """ determine cls/mod/proto and pass them to _base_proto_subclass() to get a transaction instance """ - proto,clsname,modname,kwargs = _get_cls_info(_clsname,_modname,args,kwargs) + cfg,proto,clsname,modname,kwargs = _get_cls_info(_clsname,_modname,args,kwargs) return _base_proto_subclass( clsname, modname, proto )(*args,**kwargs) async def _get_obj_async( _clsname, _modname, *args, **kwargs ): - proto,clsname,modname,kwargs = _get_cls_info(_clsname,_modname,args,kwargs) + cfg,proto,clsname,modname,kwargs = _get_cls_info(_clsname,_modname,args,kwargs) # NB: tracking wallet needed to retrieve the 'symbol' and 'decimals' parameters of token addr # (see twctl:import_token()). @@ -72,7 +73,7 @@ async def _get_obj_async( _clsname, _modname, *args, **kwargs ): # signing. if proto and proto.tokensym and clsname in ('New','OnlineSigned'): from ..tw.ctl import TwCtl - kwargs['twctl'] = await TwCtl(proto) + kwargs['twctl'] = await TwCtl(cfg,proto) return _base_proto_subclass( clsname, modname, proto )(*args,**kwargs) diff --git a/mmgen/tx/base.py b/mmgen/tx/base.py index 46af6fcf..b1639122 100755 --- a/mmgen/tx/base.py +++ b/mmgen/tx/base.py @@ -26,7 +26,6 @@ from ..obj import ( ) from ..addr import MMGenID,CoinAddr from ..util import msg,ymsg,fmt,remove_dups,make_timestamp,die -from ..opts import opt class MMGenTxIO(MMGenListItem): vout = ListItemAttr(NonNegativeInt) @@ -110,6 +109,7 @@ class Base(MMGenObject): desc = 'transaction outputs' def __init__(self,*args,**kwargs): + self.cfg = kwargs['cfg'] self.inputs = self.InputList(self) self.outputs = self.OutputList(self) self.name = type(self).__name__ @@ -171,13 +171,14 @@ class Base(MMGenObject): def add_comment(self,infile=None): if infile: from ..fileutil import get_data_from_file - self.comment = MMGenTxComment(get_data_from_file(infile,'transaction comment')) + self.comment = MMGenTxComment(get_data_from_file( self.cfg, infile, 'transaction comment' )) else: from ..ui import keypress_confirm,line_input if keypress_confirm( + self.cfg, prompt = 'Edit transaction comment?' if self.comment else 'Add a comment to transaction?', default_yes = False ): - res = MMGenTxComment(line_input('Comment: ',insert_txt=self.comment)) + res = MMGenTxComment(line_input( self.cfg, 'Comment: ', insert_txt=self.comment )) if not res: ymsg('Warning: comment is empty') changed = res != self.comment @@ -199,11 +200,11 @@ class Base(MMGenObject): fs = fmt(self.non_mmgen_inputs_msg,strip_char='\t',indent=indent).strip() m = fs.format('\n '.join(non_mmaddrs)) if caller in ('txdo','txsign'): - if not opt.keys_from_file: + if not self.cfg.keys_from_file: die( 'UserOptError', f'\n{indent}ERROR: {m}\n' ) else: msg(f'\n{indent}WARNING: {m}\n') - if not opt.yes: + if not self.cfg.yes: from ..ui import keypress_confirm - if not keypress_confirm('Continue?',default_yes=True): + if not keypress_confirm( self.cfg, 'Continue?', default_yes=True ): die(1,'Exiting at user request') diff --git a/mmgen/tx/bump.py b/mmgen/tx/bump.py index ff52f9c1..2d0d6030 100755 --- a/mmgen/tx/bump.py +++ b/mmgen/tx/bump.py @@ -14,7 +14,6 @@ tx.bump: transaction bump class from .new import New from .completed import Completed -from ..opts import opt from ..util import is_int class Bump(Completed,New): @@ -56,14 +55,14 @@ class Bump(Completed,New): else: die(1,'Insufficient funds to bump transaction') - init_reply = opt.output_to_reduce + init_reply = self.cfg.output_to_reduce chg_idx = self.chg_idx while True: if init_reply == None: from ..ui import line_input m = 'Choose an output to deduct the fee from (Hit ENTER for the change output): ' - reply = line_input(m) or 'c' + reply = line_input( self.cfg, m ) or 'c' else: reply,init_reply = init_reply,None if chg_idx == None and not is_int(reply): @@ -79,11 +78,11 @@ class Bump(Completed,New): cm = ' (change output)' if chg_idx == idx else '' prompt = f'Fee will be deducted from output {idx+1}{cm} ({o_amt} {self.coin})' if check_sufficient_funds(o_amt): - if opt.yes: + if self.cfg.yes: msg(prompt) else: from ..ui import keypress_confirm - if not keypress_confirm(prompt+'. OK?',default_yes=True): + if not keypress_confirm( self.cfg, prompt+'. OK?', default_yes=True ): continue self.bump_output_idx = idx return idx diff --git a/mmgen/tx/completed.py b/mmgen/tx/completed.py index 9942030f..83958c5b 100755 --- a/mmgen/tx/completed.py +++ b/mmgen/tx/completed.py @@ -20,11 +20,11 @@ class Completed(Base): """ filename_api = True - def __init__(self,filename=None,data=None,quiet_open=False,*args,**kwargs): + def __init__(self,cfg,filename=None,data=None,quiet_open=False,*args,**kwargs): assert (filename or data) and not (filename and data), 'CompletedTX_chk1' - super().__init__(*args,**kwargs) + super().__init__(cfg=cfg,*args,**kwargs) if data: data['twctl'] = self.twctl diff --git a/mmgen/tx/file.py b/mmgen/tx/file.py index afa62e46..a211cce9 100755 --- a/mmgen/tx/file.py +++ b/mmgen/tx/file.py @@ -20,7 +20,7 @@ tx.file: Transaction file operations for the MMGen suite """ -from ..common import * +from ..util import ymsg,make_chksum_6,die from ..obj import MMGenObject,HexStr,MMGenTxID,CoinTxID,MMGenTxComment class MMGenTxFile(MMGenObject): @@ -58,12 +58,12 @@ class MMGenTxFile(MMGenObject): return io_list( parent=tx, data=[io(tx.proto,**e) for e in d] ) from ..fileutil import get_data_from_file - tx_data = get_data_from_file(infile,tx.desc+' data',quiet=quiet_open) + tx_data = get_data_from_file( tx.cfg, infile, tx.desc+' data', quiet=quiet_open ) try: desc = 'data' - if len(tx_data) > g.max_tx_file_size: - die( 'MaxFileSizeExceeded', f'Transaction file size exceeds limit ({g.max_tx_file_size} bytes)' ) + if len(tx_data) > tx.cfg.max_tx_file_size: + die( 'MaxFileSizeExceeded', f'Transaction file size exceeds limit ({tx.cfg.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' @@ -104,10 +104,10 @@ class MMGenTxFile(MMGenObject): tx.chain = metadata.pop(0).lower() if len(metadata) == 5 else 'mainnet' from ..protocol import CoinProtocol,init_proto - network = CoinProtocol.Base.chain_name_to_network(coin,tx.chain) + network = CoinProtocol.Base.chain_name_to_network(tx.cfg,coin,tx.chain) desc = 'initialization of protocol' - tx.proto = init_proto(coin,network=network,need_amt=True) + tx.proto = init_proto( tx.cfg, coin, network=network, need_amt=True ) if tokensym: tx.proto.tokensym = tokensym @@ -186,8 +186,8 @@ 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: - die( 'MaxFileSizeExceeded', f'Transaction file size exceeds limit ({g.max_tx_file_size} bytes)' ) + if len(fmt_data) > tx.cfg.max_tx_file_size: + die( 'MaxFileSizeExceeded', f'Transaction file size exceeds limit ({tx.cfg.max_tx_file_size} bytes)' ) return fmt_data def write(self, @@ -208,6 +208,7 @@ class MMGenTxFile(MMGenObject): from ..fileutil import write_data_to_file write_data_to_file( + cfg = self.tx.cfg, outfile = self.filename, data = self.fmt_data, desc = self.tx.desc + add_desc, @@ -217,8 +218,8 @@ class MMGenTxFile(MMGenObject): ask_write_default_yes = ask_write_default_yes ) @classmethod - def get_proto(cls,filename,quiet_open=False): + def get_proto(cls,cfg,filename,quiet_open=False): from . import BaseTX - tmp_tx = BaseTX() + tmp_tx = BaseTX(cfg=cfg) cls(tmp_tx).parse(filename,metadata_only=True,quiet_open=quiet_open) return tmp_tx.proto diff --git a/mmgen/tx/info.py b/mmgen/tx/info.py index 4453ed9b..de094ed2 100755 --- a/mmgen/tx/info.py +++ b/mmgen/tx/info.py @@ -14,7 +14,6 @@ tx.info: transaction info class from ..globalvars import gc from ..color import red,green,orange -from ..opts import opt from ..util import msg,msg_r import importlib @@ -84,7 +83,7 @@ class TxInfo: d = tx.dcoin, c = tx.coin ) - if opt.verbose: + if tx.cfg.verbose: yield self.format_verbose_footer() return ''.join(gen_view()) # TX label might contain non-ascii chars diff --git a/mmgen/tx/new.py b/mmgen/tx/new.py index a78bcaf5..cba6857e 100755 --- a/mmgen/tx/new.py +++ b/mmgen/tx/new.py @@ -12,13 +12,11 @@ tx.new: new transaction class """ -from ..globalvars import * -from ..opts import opt from .base import Base from ..globalvars import gc from ..color import pink,yellow from ..obj import get_obj,MMGenList -from ..util import msg,qmsg,fmt,die,suf,remove_dups,get_extension +from ..util import msg,fmt,die,suf,remove_dups,get_extension from ..addr import ( is_mmgen_id, MMGenAddrType, @@ -28,7 +26,7 @@ from ..addr import ( is_addrlist_id ) -def mmaddr2coinaddr(mmaddr,ad_w,ad_f,proto): +def mmaddr2coinaddr(cfg,mmaddr,ad_w,ad_f,proto): def wmsg(k): messages = { @@ -59,7 +57,7 @@ def mmaddr2coinaddr(mmaddr,ad_w,ad_f,proto): if coin_addr: msg(wmsg('addr_in_addrfile_only')) from ..ui import keypress_confirm - if not (opt.yes or keypress_confirm('Continue anyway?')): + if not (cfg.yes or keypress_confirm( cfg, 'Continue anyway?' )): sys.exit(1) else: die(2,wmsg('addr_not_found')) @@ -125,8 +123,8 @@ class New(Base): if abs_fee: prompt = '{} TX fee{}: {}{} {} ({} {})\n'.format( desc, - (f' (after {opt.fee_adjust:.2f}X adjustment)' - if opt.fee_adjust != 1 and desc.startswith('Network-estimated') + (f' (after {self.cfg.fee_adjust:.2f}X adjustment)' + if self.cfg.fee_adjust != 1 and desc.startswith('Network-estimated') else ''), ('','≈')[self.fee_is_approximate], abs_fee.hl(), @@ -134,11 +132,11 @@ class New(Base): pink(str(self.fee_abs2rel(abs_fee))), self.rel_fee_disp) from ..ui import keypress_confirm - if opt.yes or keypress_confirm(prompt+'OK?',default_yes=True): - if opt.yes: + if self.cfg.yes or keypress_confirm( self.cfg, prompt+'OK?', default_yes=True ): + if self.cfg.yes: msg(prompt) return abs_fee - fee = line_input(self.usr_fee_prompt) + fee = line_input( self.cfg, self.usr_fee_prompt ) desc = 'User-selected' # we don't know fee yet, so perform preliminary check with fee == 0 @@ -153,19 +151,19 @@ class New(Base): async def get_fee_from_user(self,have_estimate_fail=[]): - if opt.fee: + if self.cfg.fee: desc = 'User-selected' - start_fee = opt.fee + start_fee = self.cfg.fee else: desc = 'Network-estimated ({}, {} conf{})'.format( - opt.fee_estimate_mode.upper(), - pink(str(opt.fee_estimate_confs)), - suf(opt.fee_estimate_confs) ) + self.cfg.fee_estimate_mode.upper(), + pink(str(self.cfg.fee_estimate_confs)), + suf(self.cfg.fee_estimate_confs) ) fee_per_kb,fe_type = await self.get_rel_fee_from_network() if fee_per_kb < 0: if not have_estimate_fail: - msg(self.fee_fail_fs.format(c=opt.fee_estimate_confs,t=fe_type)) + msg(self.fee_fail_fs.format(c=self.cfg.fee_estimate_confs,t=fe_type)) have_estimate_fail.append(True) start_fee = None else: @@ -181,7 +179,7 @@ class New(Base): arg,amt = arg_in.split(',',1) if ',' in arg_in else (arg_in,None) if is_mmgen_id(self.proto,arg): - coin_addr = mmaddr2coinaddr(arg,ad_w,ad_f,self.proto) + coin_addr = mmaddr2coinaddr(self.cfg,arg,ad_w,ad_f,self.proto) elif is_coin_addr(self.proto,arg): coin_addr = CoinAddr(self.proto,arg) elif is_mmgen_addrtype(self.proto,arg) or is_addrlist_id(self.proto,arg): @@ -189,7 +187,7 @@ class New(Base): die(2,f'Change addresses not supported for {self.proto.name} protocol') from ..tw.addresses import TwAddresses - al = await TwAddresses(self.proto,get_data=True) + al = await TwAddresses(self.cfg,self.proto,get_data=True) if is_mmgen_addrtype(self.proto,arg): arg = MMGenAddrType(self.proto,arg) @@ -253,9 +251,9 @@ class New(Base): from ..fileutil import check_infile for addrfile in addrfiles: check_infile(addrfile) - ad_f.add(AddrList( self.proto, addrfile )) + ad_f.add(AddrList( self.cfg, self.proto, addrfile )) - ad_w = await TwAddrData(self.proto,twctl=self.twctl) + ad_w = await TwAddrData(self.cfg,self.proto,twctl=self.twctl) await self.process_cmd_args(cmd_args,ad_f,ad_w) @@ -271,6 +269,7 @@ class New(Base): def confirm_autoselected_addr(self,chg): from ..ui import keypress_confirm if not keypress_confirm( + self.cfg, 'Using {a} as {b} address. OK?'.format( a = chg.mmid.hl(), b = 'single output' if len(self.outputs) == 1 else 'change' ), @@ -279,9 +278,10 @@ class New(Base): async def warn_chg_addr_used(self,chg): from ..tw.addresses import TwAddresses - if (await TwAddresses(self.proto,get_data=True)).is_used(chg.addr): + if (await TwAddresses(self.cfg,self.proto,get_data=True)).is_used(chg.addr): from ..ui import keypress_confirm if not keypress_confirm( + self.cfg, '{a} {b} {c}\n{d}'.format( a = yellow(f'Requested change address'), b = (chg.mmid or chg.addr).hl(), @@ -297,7 +297,7 @@ class New(Base): prompt = 'Enter a range or space-separated list of outputs to spend: ' from ..ui import line_input while True: - reply = line_input(prompt).strip() + reply = line_input( self.cfg, prompt ).strip() if reply: from ..addrlist import AddrIdxList selected = get_obj(AddrIdxList, fmt_str=','.join(reply.split()) ) @@ -315,7 +315,7 @@ class New(Base): return idx + 1 def get_uo_nums(): - for addr in opt.inputs.split(','): + for addr in self.cfg.inputs.split(','): if is_mmgen_id(self.proto,addr): attr = 'twmmid' elif is_coin_addr(self.proto,addr): @@ -354,7 +354,7 @@ class New(Base): async def get_inputs_from_user(self,outputs_sum): while True: - us_f = self.select_unspent_cmdline if opt.inputs else self.select_unspent + us_f = self.select_unspent_cmdline if self.cfg.inputs else self.select_unspent sel_nums = us_f(self.twuo.data) msg(f'Selected output{suf(sel_nums)}: {{}}'.format(' '.join(str(n) for n in sel_nums))) @@ -373,8 +373,8 @@ class New(Base): if funds_left >= 0: p = self.final_inputs_ok_msg(funds_left) from ..ui import keypress_confirm - if opt.yes or keypress_confirm(p+'. OK?',default_yes=True): - if opt.yes: + if self.cfg.yes or keypress_confirm( self.cfg, p+'. OK?', default_yes=True ): + if self.cfg.yes: msg(p) return funds_left else: @@ -386,21 +386,21 @@ class New(Base): from ..tw.unspent import TwUnspentOutputs - if opt.comment_file: - self.add_comment(opt.comment_file) + if self.cfg.comment_file: + self.add_comment(self.cfg.comment_file) twuo_addrs = await self.get_input_addrs_from_cmdline() - self.twuo = await TwUnspentOutputs(self.proto,minconf=opt.minconf,addrs=twuo_addrs) + self.twuo = await TwUnspentOutputs(self.cfg,self.proto,minconf=self.cfg.minconf,addrs=twuo_addrs) await self.twuo.get_data() if not do_info: await self.get_outputs_from_cmdline(cmd_args) from ..ui import do_license_msg - do_license_msg() + do_license_msg(self.cfg) - if not opt.inputs: + if not self.cfg.inputs: await self.twuo.view_filter_and_sort() self.twuo.display_total() @@ -422,7 +422,7 @@ class New(Base): self.update_change_output(funds_left) - if not opt.yes: + if not self.cfg.yes: self.add_comment() # edits an existing comment await self.create_serialized(locktime=locktime) # creates self.txid too @@ -432,12 +432,12 @@ class New(Base): self.chain = self.proto.chain_name self.check_fee() - qmsg('Transaction successfully created') + self.cfg._util.qmsg('Transaction successfully created') from . import UnsignedTX - new = UnsignedTX(data=self.__dict__) + new = UnsignedTX(cfg=self.cfg,data=self.__dict__) - if not opt.yes: + if not self.cfg.yes: new.info.view_with_prompt('View transaction details?') del new.twuo.twctl diff --git a/mmgen/tx/online.py b/mmgen/tx/online.py index c4ec79c9..409fecb3 100755 --- a/mmgen/tx/online.py +++ b/mmgen/tx/online.py @@ -22,11 +22,11 @@ class OnlineSigned(Signed): return _base_proto_subclass('Status','status',self.proto)(self) def confirm_send(self): - from ..opts import opt from ..util import msg from ..ui import confirm_or_raise confirm_or_raise( - message = '' if opt.quiet else 'Once this transaction is sent, there’s no taking it back!', + cfg = self.cfg, + message = '' if self.cfg.quiet else 'Once this transaction is sent, there’s no taking it back!', action = f'broadcast this transaction to the {self.proto.coin} {self.proto.network.upper()} network', - expect = 'YES' if opt.quiet or opt.yes else 'YES, I REALLY WANT TO DO THIS' ) + expect = 'YES' if self.cfg.quiet or self.cfg.yes else 'YES, I REALLY WANT TO DO THIS' ) msg('Sending transaction') diff --git a/mmgen/tx/sign.py b/mmgen/tx/sign.py index 047f3c39..61a313fd 100755 --- a/mmgen/tx/sign.py +++ b/mmgen/tx/sign.py @@ -20,9 +20,8 @@ tx.sign: Sign a transaction generated by 'mmgen-txcreate' """ -from ..globalvars import g,gc -from ..opts import opt -from ..util import msg,vmsg,qmsg,suf,fmt,die,remove_dups,get_extension +from ..globalvars import gc +from ..util import msg,suf,fmt,die,remove_dups,get_extension from ..obj import MMGenList from ..addr import MMGenAddrType from ..addrlist import AddrIdxList,KeyAddrList @@ -38,14 +37,14 @@ def get_seed_for_seed_id(sid,infiles,saved_seeds): subseeds_checked = False while True: if infiles: - seed = Wallet(infiles.pop(0),ignore_in_fmt=True).seed + seed = Wallet(cfg,infiles.pop(0),ignore_in_fmt=True).seed elif subseeds_checked == False: seed = saved_seeds[list(saved_seeds)[0]].subseed_by_seed_id(sid,print_msg=True) subseeds_checked = True if not seed: continue - elif opt.in_fmt: - qmsg(f'Need seed data for Seed ID {sid}') - seed = Wallet().seed + elif cfg.in_fmt: + cfg._util.qmsg(f'Need seed data for Seed ID {sid}') + seed = Wallet(cfg).seed msg(f'User input produced Seed ID {seed.sid}') if not seed.sid == sid: # TODO: add test seed = seed.subseed_by_seed_id(sid,print_msg=True) @@ -60,7 +59,7 @@ def get_seed_for_seed_id(sid,infiles,saved_seeds): def generate_kals_for_mmgen_addrs(need_keys,infiles,saved_seeds,proto): mmids = [e.mmid for e in need_keys] sids = remove_dups((i.sid for i in mmids),quiet=True) - vmsg(f"Need seed{suf(sids)}: {' '.join(sids)}") + cfg._util.vmsg(f"Need seed{suf(sids)}: {' '.join(sids)}") def gen_kals(): for sid in sids: # Returns only if seed is found @@ -69,6 +68,7 @@ def generate_kals_for_mmgen_addrs(need_keys,infiles,saved_seeds,proto): idx_list = [i.idx for i in mmids if i.sid == sid and i.mmtype == id_str] if idx_list: yield KeyAddrList( + cfg = cfg, proto = proto, seed = seed, addr_idxs = AddrIdxList(idx_list=idx_list), @@ -83,7 +83,7 @@ def add_keys(tx,src,infiles=None,saved_seeds=None,keyaddr_list=None): desc,src_desc = ( ('key-address file','From key-address file:') if keyaddr_list else ('seed(s)','Generated from seed:') ) - qmsg(f'Checking {gc.proj_name} -> {tx.proto.coin} address mappings for {src} (from {desc})') + cfg._util.qmsg(f'Checking {gc.proj_name} -> {tx.proto.coin} address mappings for {src} (from {desc})') d = ( MMGenList([keyaddr_list]) if keyaddr_list else generate_kals_for_mmgen_addrs(need_keys,infiles,saved_seeds,tx.proto) ) @@ -104,52 +104,56 @@ def add_keys(tx,src,infiles=None,saved_seeds=None,keyaddr_list=None): {{'tx file:':<23}} {{e.mmid}} -> {{e.addr}} """).strip()) if new_keys: - vmsg(f'Added {len(new_keys)} wif key{suf(new_keys)} from {desc}') + cfg._util.vmsg(f'Added {len(new_keys)} wif key{suf(new_keys)} from {desc}') return new_keys def _pop_matching_fns(args,cmplist): # strips found args return list(reversed([args.pop(args.index(a)) for a in reversed(args) if get_extension(a) in cmplist])) -def get_tx_files(opt,args): +def get_tx_files(cfg,args): from .unsigned import Unsigned ret = _pop_matching_fns(args,[Unsigned.ext]) if not ret: die(1,'You must specify a raw transaction file!') return ret -def get_seed_files(opt,args): +def get_seed_files(cfg,args): # favor unencrypted seed sources first, as they don't require passwords ret = _pop_matching_fns( args, get_wallet_extensions('unenc') ) from ..filename import find_file_in_dir - wf = find_file_in_dir(get_wallet_cls('mmgen'),g.data_dir) # Make this the first encrypted ss in the list + wf = find_file_in_dir(get_wallet_cls('mmgen'),cfg.data_dir) # Make this the first encrypted ss in the list if wf: ret.append(wf) ret += _pop_matching_fns( args, get_wallet_extensions('enc') ) - if not (ret or opt.mmgen_keys_from_file or opt.keys_from_file): # or opt.use_wallet_dat + if not (ret or cfg.mmgen_keys_from_file or cfg.keys_from_file): # or cfg.use_wallet_dat die(1,'You must specify a seed or key source!') return ret -def get_keyaddrlist(proto,opt): - if opt.mmgen_keys_from_file: - return KeyAddrList(proto,opt.mmgen_keys_from_file) +def get_keyaddrlist(cfg,proto): + if cfg.mmgen_keys_from_file: + return KeyAddrList( cfg, proto, cfg.mmgen_keys_from_file ) return None -def get_keylist(proto,opt): - if opt.keys_from_file: +def get_keylist(cfg,proto): + if cfg.keys_from_file: from ..fileutil import get_lines_from_file - return get_lines_from_file(opt.keys_from_file,'key-address data',trim_comments=True) + return get_lines_from_file( cfg, cfg.keys_from_file, 'key-address data', trim_comments=True ) return None -async def txsign(tx,seed_files,kl,kal,tx_num_str=''): +async def txsign(cfg_parm,tx,seed_files,kl,kal,tx_num_str=''): keys = MMGenList() # list of AddrListEntry objects non_mmaddrs = tx.get_non_mmaddrs('inputs') + global cfg + cfg = cfg_parm + if non_mmaddrs: tx.check_non_mmgen_inputs(caller='txsign',non_mmaddrs=non_mmaddrs) tmp = KeyAddrList( - proto = tx.proto, - addrlist = non_mmaddrs, + cfg = cfg, + proto = tx.proto, + addrlist = non_mmaddrs, skip_chksum = True ) if kl: tmp.add_wifs(kl) @@ -162,7 +166,7 @@ async def txsign(tx,seed_files,kl,kal,tx_num_str=''): sep + sep.join(missing) )) keys += tmp.data - if opt.mmgen_keys_from_file: + if cfg.mmgen_keys_from_file: keys += add_keys(tx,'inputs',keyaddr_list=kal) add_keys(tx,'outputs',keyaddr_list=kal) diff --git a/mmgen/ui.py b/mmgen/ui.py index 6ab54bfa..3a5f8211 100755 --- a/mmgen/ui.py +++ b/mmgen/ui.py @@ -14,32 +14,32 @@ ui: Interactive user interface functions for the MMGen suite import sys,os -from .globalvars import g,gc -from .opts import opt -from .util import msg,msg_r,Msg,dmsg,die +from .globalvars import gc +from .util import msg,msg_r,Msg,die -def confirm_or_raise(message,action,expect='YES',exit_msg='Exiting at user request'): +def confirm_or_raise(cfg,message,action,expect='YES',exit_msg='Exiting at user request'): if message: msg(message) if line_input( + cfg, (f'{action} ' if action[0].isupper() else f'Are you sure you want to {action}?\n') + f'Type uppercase {expect!r} to confirm: ' ).strip() != expect: die( 'UserNonConfirmation', exit_msg ) -def get_words_from_user(prompt): - words = line_input(prompt, echo=opt.echo_passphrase).split() - if g.debug: +def get_words_from_user(cfg,prompt): + words = line_input( cfg, prompt, echo=cfg.echo_passphrase ).split() + if cfg.debug: msg('Sanitized input: [{}]'.format(' '.join(words))) return words -def get_data_from_user(desc='data'): # user input MUST be UTF-8 - data = line_input(f'Enter {desc}: ',echo=opt.echo_passphrase) - if g.debug: +def get_data_from_user(cfg,desc='data'): # user input MUST be UTF-8 + data = line_input( cfg, f'Enter {desc}: ', echo=cfg.echo_passphrase ) + if cfg.debug: msg(f'User input: [{data}]') return data -def line_input(prompt,echo=True,insert_txt='',hold_protect=True): +def line_input(cfg,prompt,echo=True,insert_txt='',hold_protect=True): """ multi-line prompts OK one-line prompts must begin at beginning of line @@ -62,7 +62,7 @@ def line_input(prompt,echo=True,insert_txt='',hold_protect=True): from .term import kb_hold_protect kb_hold_protect() - if g.test_suite_popen_spawn: + if cfg.test_suite_popen_spawn: msg(prompt) sys.stderr.flush() reply = os.read(0,4096).decode().rstrip('\n') # strip NL to mimic behavior of input() @@ -83,6 +83,7 @@ def line_input(prompt,echo=True,insert_txt='',hold_protect=True): return reply.strip() def keypress_confirm( + cfg, prompt, default_yes = False, verbose = False, @@ -94,7 +95,7 @@ def keypress_confirm( nl = f'\r{" "*len(prompt)}\r' if no_nl else '\n' - if g.accept_defaults: + if cfg.accept_defaults: msg(prompt) return default_yes @@ -136,9 +137,9 @@ def do_pager(text): Msg(text+end_msg) set_vt100() -def do_license_msg(immed=False): +def do_license_msg(cfg,immed=False): - if opt.quiet or g.no_license or opt.yes or not g.stdin_tty: + if cfg.quiet or cfg.no_license or cfg.yes or not cfg.stdin_tty: return import mmgen.contrib.license as gpl diff --git a/mmgen/util.py b/mmgen/util.py index aa19b3ae..e0f7cf64 100755 --- a/mmgen/util.py +++ b/mmgen/util.py @@ -23,8 +23,7 @@ util: Frequently-used variables, classes and utility functions for the MMGen sui import sys,os,time,re from .color import * -from .globalvars import g,gc,gv -from .opts import opt +from .globalvars import gv,gc ascii_lowercase = 'abcdefghijklmnopqrstuvwxyz' @@ -32,6 +31,61 @@ hexdigits = '0123456789abcdefABCDEF' hexdigits_uc = '0123456789ABCDEF' hexdigits_lc = '0123456789abcdef' +def noop(*args,**kwargs): + pass + +class Util: + + def __init__(self,cfg): + + self.cfg = cfg + + if cfg.quiet: + self.qmsg = self.qmsg_r = noop + else: + self.qmsg = msg + self.qmsg_r = msg_r + + if cfg.verbose: + self.vmsg = msg + self.vmsg_r = msg_r + self.Vmsg = Msg + self.Vmsg_r = Msg_r + else: + self.vmsg = self.vmsg_r = self.Vmsg = self.Vmsg_r = noop + + self.dmsg = msg if cfg.debug else noop + + if cfg.pager: + from .ui import do_pager + self.stdout_or_pager = do_pager + else: + self.stdout_or_pager = Msg_r + + def compare_chksums(self,chk1,desc1,chk2,desc2,hdr='',die_on_fail=False,verbose=False): + + if not chk1 == chk2: + fs = "{} ERROR: {} checksum ({}) doesn't match {} checksum ({})" + m = fs.format((hdr+':\n ' if hdr else 'CHECKSUM'),desc2,chk2,desc1,chk1) + if die_on_fail: + die(3,m) + else: + if verbose or self.cfg.verbose: + msg(m) + return False + + if self.cfg.verbose: + msg(f'{capfirst(desc1)} checksum OK ({chk1})') + + return True + + def compare_or_die(self, val1, desc1, val2, desc2, e='Error'): + if val1 != val2: + die(3,f"{e}: {desc2} ({val2}) doesn't match {desc1} ({val1})") + if self.cfg.debug: + msg(f'{capfirst(desc2)} OK ({val2})') + return True + if gc.platform == 'win': def msg_r(s): try: @@ -85,34 +139,6 @@ def bmsg(s): def pumsg(s): msg(purple(s)) -def qmsg(s): - if not opt.quiet: - msg(s) - -def qmsg_r(s): - if not opt.quiet: - 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)) @@ -319,26 +345,6 @@ def strip_comments(lines): pat = re.compile('#.*') return [m for m in [pat.sub('',l).rstrip() for l in lines] if m != ''] -def compare_chksums(chk1,desc1,chk2,desc2,hdr='',die_on_fail=False,verbose=False): - - if not chk1 == chk2: - fs = "{} ERROR: {} checksum ({}) doesn't match {} checksum ({})" - m = fs.format((hdr+':\n ' if hdr else 'CHECKSUM'),desc2,chk2,desc1,chk1) - if die_on_fail: - die(3,m) - else: - vmsg(m,force=verbose) - return False - - vmsg(f'{capfirst(desc1)} checksum OK ({chk1})') - return True - -def compare_or_die(val1, desc1, val2, desc2, e='Error'): - if val1 != val2: - die(3,f"{e}: {desc2} ({val2}) doesn't match {desc1} ({val1})") - dmsg(f'{capfirst(desc2)} OK ({val2})') - return True - def make_full_path(outdir,outfile): return os.path.normpath(os.path.join(outdir, os.path.basename(outfile))) @@ -377,13 +383,6 @@ class oneshot_warning_group(oneshot_warning): def __init__(self,wcls,div=None,fmt_args=[],reverse=False): self.do(getattr(self,wcls),div,fmt_args,reverse) -def stdout_or_pager(s): - if opt.pager: - from .ui import do_pager - do_pager(s) - else: - Msg_r(s) - def get_subclasses(cls,names=False): def gen(cls): for i in cls.__subclasses__(): diff --git a/mmgen/util2.py b/mmgen/util2.py index 118d1a7b..4d45ddb9 100755 --- a/mmgen/util2.py +++ b/mmgen/util2.py @@ -13,7 +13,7 @@ util2: Less frequently-used variables, classes and utility functions for the MMG """ import re,time -from .util import msg,qmsg,suf,hexdigits +from .util import msg,suf,hexdigits def die_wait(delay,ev=0,s=''): assert isinstance(delay,int) @@ -36,12 +36,12 @@ def removeprefix(s,pfx): # workaround for pre-Python 3.9 def removesuffix(s,sfx): # workaround for pre-Python 3.9 return s[:-len(sfx)] if s.endswith(sfx) else s -def get_keccak(cached_ret=[]): +# called with no arguments by pyethereum.utils: +def get_keccak(cfg=None,cached_ret=[]): if not cached_ret: - from .opts import opt - if getattr(opt,'use_internal_keccak_module',False): - qmsg('Using internal keccak module by user request') + if cfg and cfg.use_internal_keccak_module: + cfg._util.qmsg('Using internal keccak module by user request') from .contrib.keccak import keccak_256 else: try: diff --git a/mmgen/wallet/__init__.py b/mmgen/wallet/__init__.py index 2fed8ccb..b0e82b24 100755 --- a/mmgen/wallet/__init__.py +++ b/mmgen/wallet/__init__.py @@ -15,8 +15,6 @@ wallet.__init__: wallet class initializer import importlib from collections import namedtuple -from ..globalvars import g -from ..opts import opt from ..util import die,get_extension from ..objmethods import MMGenObject from ..seed import Seed @@ -101,6 +99,7 @@ def _get_me(modname): return MMGenObject.__new__( getattr( importlib.import_module(f'mmgen.wallet.{modname}'), 'wallet' ) ) def Wallet( + cfg, fn = None, ss = None, seed_bin = None, @@ -111,31 +110,31 @@ def Wallet( in_fmt = None, passwd_file = None ): - in_fmt = in_fmt or opt.in_fmt + in_fmt = in_fmt or cfg.in_fmt ss_out = ( get_wallet_data( - fmt_code = opt.out_fmt, + fmt_code = cfg.out_fmt, die_on_fail = True ).type - if opt.out_fmt else None ) + if cfg.out_fmt else None ) if seed or seed_bin: me = _get_me( ss_out or 'mmgen' ) # default to native wallet format - me.seed = seed or Seed(seed_bin=seed_bin) + me.seed = seed or Seed( cfg, seed_bin=seed_bin ) me.op = 'new' elif ss: me = _get_me( ss.type if passchg else (ss_out or 'mmgen') ) me.seed = ss.seed me.ss_in = ss me.op = 'pwchg_new' if passchg else 'conv' - elif fn or opt.hidden_incog_input_params: + elif fn or cfg.hidden_incog_input_params: if fn: wd = get_wallet_data(ext=get_extension(fn),die_on_fail=True) if in_fmt and (not ignore_in_fmt) and in_fmt not in wd.fmt_codes: die(1,f'{in_fmt}: --in-fmt parameter does not match extension of input file') me = _get_me( wd.type ) else: - fn = ','.join(opt.hidden_incog_input_params.split(',')[:-1]) # permit comma in filename + fn = ','.join(cfg.hidden_incog_input_params.split(',')[:-1]) # permit comma in filename me = _get_me( 'incog_hidden' ) from ..filename import MMGenFile me.infile = MMGenFile( fn, subclass=type(me) ) @@ -145,9 +144,11 @@ def Wallet( me.op = 'pwchg_old' if passchg else 'old' else: # called with no arguments: initialize with random seed me = _get_me( ss_out or 'mmgen' ) # default to native wallet format - me.seed = Seed() + me.seed = Seed(cfg) me.op = 'new' + me.cfg = cfg + me.__init__( in_data = in_data, passwd_file = passwd_file ) diff --git a/mmgen/wallet/base.py b/mmgen/wallet/base.py index 3ba9b5a6..0db3dd84 100755 --- a/mmgen/wallet/base.py +++ b/mmgen/wallet/base.py @@ -14,9 +14,7 @@ wallet.base: wallet base class import os -from ..globalvars import g -from ..opts import opt -from ..util import msg,qmsg,die +from ..util import msg,die from ..objmethods import MMGenObject from . import Wallet,wallet_data,get_wallet_cls @@ -45,7 +43,7 @@ class wallet(MMGenObject,metaclass=WalletMeta): in_data = None, passwd_file = None ): - self.passwd_file = passwd_file or opt.passwd_file + self.passwd_file = passwd_file or self.cfg.passwd_file self.ssdata = self.WalletData() self.msg = {} self.in_data = in_data @@ -57,7 +55,7 @@ class wallet(MMGenObject,metaclass=WalletMeta): if hasattr(self,'seed'): self._encrypt() return - elif hasattr(self,'infile') or self.in_data or not g.stdin_tty: + elif hasattr(self,'infile') or self.in_data or not self.cfg.stdin_tty: self._deformat_once() self._decrypt_retry() else: @@ -66,7 +64,7 @@ class wallet(MMGenObject,metaclass=WalletMeta): self._deformat_retry() self._decrypt_retry() - qmsg('Valid {} for Seed ID {}{}'.format( + self.cfg._util.qmsg('Valid {} for Seed ID {}{}'.format( self.desc, self.seed.sid.hl(), (f', seed length {self.seed.bitlen}' if self.seed.bitlen != 256 else '') @@ -76,6 +74,7 @@ class wallet(MMGenObject,metaclass=WalletMeta): if hasattr(self,'infile'): from ..fileutil import get_data_from_file self.fmt_data = get_data_from_file( + self.cfg, self.infile.name, self.desc, binary = self.file_mode=='binary' ) @@ -86,7 +85,7 @@ class wallet(MMGenObject,metaclass=WalletMeta): def _get_data_from_user(self,desc): from ..ui import get_data_from_user - return get_data_from_user(desc) + return get_data_from_user( self.cfg, desc ) def _deformat_once(self): self._get_data() @@ -118,16 +117,17 @@ class wallet(MMGenObject,metaclass=WalletMeta): } if outdir: - # write_data_to_file(): outfile with absolute path overrides opt.outdir + # write_data_to_file(): outfile with absolute path overrides self.cfg.outdir of = os.path.abspath(os.path.join(outdir,self._filename())) from ..fileutil import write_data_to_file write_data_to_file( + self.cfg, of if outdir else self._filename(), self.fmt_data, **kwargs ) def check_usr_seed_len(self,bitlen=None): chk = bitlen or self.seed.bitlen - if opt.seed_len and opt.seed_len != chk: - die(1,f'ERROR: requested seed length ({opt.seed_len}) doesn’t match seed length of source ({chk})') + if self.cfg.seed_len and self.cfg.seed_len != chk: + die(1,f'ERROR: requested seed length ({self.cfg.seed_len}) doesn’t match seed length of source ({chk})') diff --git a/mmgen/wallet/brain.py b/mmgen/wallet/brain.py index 47da1c12..0549cd1c 100755 --- a/mmgen/wallet/brain.py +++ b/mmgen/wallet/brain.py @@ -12,8 +12,7 @@ wallet.brain: brainwallet wallet class """ -from ..opts import opt -from ..util import msg,qmsg,qmsg_r +from ..util import msg from ..color import yellow from .enc import wallet from .seed import Seed @@ -26,7 +25,7 @@ class wallet(wallet): def get_bw_params(self): # already checked - a = opt.brain_params.split(',') + a = self.cfg.brain_params.split(',') return int(a[0]),a[1] def _deformat(self): @@ -35,25 +34,25 @@ class wallet(wallet): def _decrypt(self): d = self.ssdata - if opt.brain_params: + if self.cfg.brain_params: bw_seed_len,d.hash_preset = self.get_bw_params() else: - if not opt.seed_len: - qmsg(f'Using default seed length of {yellow(str(Seed.dfl_len))} bits\n' + if not self.cfg.seed_len: + self.cfg._util.qmsg(f'Using default seed length of {yellow(str(Seed.dfl_len))} bits\n' + 'If this is not what you want, use the --seed-len option' ) self._get_hash_preset() - bw_seed_len = opt.seed_len or Seed.dfl_len - qmsg_r('Hashing brainwallet data. Please wait...') + bw_seed_len = self.cfg.seed_len or Seed.dfl_len + self.cfg._util.qmsg_r('Hashing brainwallet data. Please wait...') # Use buflen arg of scrypt.hash() to get seed of desired length seed = self.crypto.scrypt_hash_passphrase( self.brainpasswd.encode(), b'', d.hash_preset, buflen = bw_seed_len // 8 ) - qmsg('Done') - self.seed = Seed(seed) + self.cfg._util.qmsg('Done') + self.seed = Seed( self.cfg, seed ) msg(f'Seed ID: {self.seed.sid}') - qmsg('Check this value against your records') + self.cfg._util.qmsg('Check this value against your records') return True def _format(self): diff --git a/mmgen/wallet/dieroll.py b/mmgen/wallet/dieroll.py index 9b1f24cc..8b0a3e86 100755 --- a/mmgen/wallet/dieroll.py +++ b/mmgen/wallet/dieroll.py @@ -13,8 +13,6 @@ wallet.dieroll: dieroll wallet class """ import time -from ..globalvars import g -from ..opts import opt from ..util import msg,msg_r,die,fmt,remove_whitespace from ..util2 import block_format from ..seed import Seed @@ -53,16 +51,16 @@ class wallet(wallet): seed_len = rmap[len(d)] seed_bytes = bc.tobytes( d, pad='seed' )[-seed_len:] - if self.interactive_input and opt.usr_randchars: + if self.interactive_input and self.cfg.usr_randchars: from ..ui import keypress_confirm - if keypress_confirm(self.user_entropy_prompt): + if keypress_confirm( self.cfg, self.user_entropy_prompt ): from ..crypto import Crypto - seed_bytes = Crypto().add_user_random( + seed_bytes = Crypto(self.cfg).add_user_random( rand_bytes = seed_bytes, desc = 'gathered from your die rolls' ) self.desc += ' plus user-supplied entropy' - self.seed = Seed(seed_bytes) + self.seed = Seed( self.cfg, seed_bytes ) self.ssdata.hexseed = seed_bytes.hex() self.check_usr_seed_len() @@ -70,9 +68,9 @@ class wallet(wallet): def _get_data_from_user(self,desc): - if not g.stdin_tty: + if not self.cfg.stdin_tty: from ..ui import get_data_from_user - return get_data_from_user(desc) + return get_data_from_user( self.cfg, desc ) bc = baseconv('b6d') @@ -88,23 +86,23 @@ class wallet(wallet): CUR_HIDE = '\033[?25l' CUR_SHOW = '\033[?25h' - cr = '\n' if g.test_suite else '\r' + cr = '\n' if self.cfg.test_suite else '\r' prompt_fs = f'\b\b\b {cr}Enter die roll #{{}}: {CUR_SHOW}' - clear_line = '' if g.test_suite else '\r' + ' ' * 25 + clear_line = '' if self.cfg.test_suite else '\r' + ' ' * 25 invalid_msg = CUR_HIDE + cr + 'Invalid entry' + ' ' * 11 from ..term import get_char def get_digit(n): p = prompt_fs while True: - time.sleep(g.short_disp_timeout) + time.sleep(self.cfg.short_disp_timeout) ch = get_char(p.format(n),num_bytes=1) if ch in bc.digits: msg_r(CUR_HIDE + ' OK') return ch else: msg_r(invalid_msg) - sleep = g.err_disp_timeout + sleep = self.cfg.err_disp_timeout p = clear_line + prompt_fs dierolls,n = [],1 diff --git a/mmgen/wallet/enc.py b/mmgen/wallet/enc.py index b05fdcc7..55459c1e 100755 --- a/mmgen/wallet/enc.py +++ b/mmgen/wallet/enc.py @@ -13,15 +13,14 @@ wallet.enc: encrypted wallet base class """ from ..globalvars import gc -from ..opts import opt -from ..util import msg,qmsg,make_chksum_8 +from ..util import msg,make_chksum_8 from .base import wallet class wallet(wallet): def __init__(self,*args,**kwargs): from mmgen.crypto import Crypto - self.crypto = Crypto() + self.crypto = Crypto(self.cfg) return super().__init__(*args,**kwargs) def _decrypt_retry(self): @@ -45,19 +44,19 @@ class wallet(wallet): def _get_hash_preset(self,add_desc=''): if hasattr(self,'ss_in') and hasattr(self.ss_in.ssdata,'hash_preset'): old_hp = self.ss_in.ssdata.hash_preset - if opt.keep_hash_preset: + if self.cfg.keep_hash_preset: hp = old_hp - qmsg(f'Reusing hash preset {hp!r} at user request') - elif opt.hash_preset: - hp = opt.hash_preset - qmsg(f'Using hash preset {hp!r} requested on command line') + self.cfg._util.qmsg(f'Reusing hash preset {hp!r} at user request') + elif self.cfg.hash_preset: + hp = self.cfg.hash_preset + self.cfg._util.qmsg(f'Using hash preset {hp!r} requested on command line') else: # Prompt, using old value as default - hp = self._get_hash_preset_from_user(old_hp,add_desc) - if (not opt.keep_hash_preset) and self.op == 'pwchg_new': - qmsg('Hash preset {}'.format( 'unchanged' if hp == old_hp else f'changed to {hp!r}' )) - elif opt.hash_preset: - hp = opt.hash_preset - qmsg(f'Using hash preset {hp!r} requested on command line') + hp = self._get_hash_preset_from_user( old_preset=old_hp, add_desc=add_desc ) + if (not self.cfg.keep_hash_preset) and self.op == 'pwchg_new': + self.cfg._util.qmsg('Hash preset {}'.format( 'unchanged' if hp == old_hp else f'changed to {hp!r}' )) + elif self.cfg.hash_preset: + hp = self.cfg.hash_preset + self.cfg._util.qmsg(f'Using hash preset {hp!r} requested on command line') else: hp = self._get_hash_preset_from_user( old_preset = gc.dfl_hash_preset, @@ -83,13 +82,13 @@ class wallet(wallet): if hasattr(self,'ss_in') and hasattr(self.ss_in.ssdata,'passwd'): old_pw = self.ss_in.ssdata.passwd - if opt.keep_passphrase: + if self.cfg.keep_passphrase: d.passwd = old_pw - qmsg('Reusing passphrase at user request') + self.cfg._util.qmsg('Reusing passphrase at user request') else: d.passwd = self._get_new_passphrase() if self.op == 'pwchg_new': - qmsg('Passphrase {}'.format( 'unchanged' if d.passwd == old_pw else 'changed' )) + self.cfg._util.qmsg('Passphrase {}'.format( 'unchanged' if d.passwd == old_pw else 'changed' )) else: d.passwd = self._get_new_passphrase() diff --git a/mmgen/wallet/incog_base.py b/mmgen/wallet/incog_base.py index c41a3535..857cfa13 100755 --- a/mmgen/wallet/incog_base.py +++ b/mmgen/wallet/incog_base.py @@ -12,10 +12,8 @@ wallet.incog_base: incognito wallet base class """ -from ..globalvars import g -from ..opts import opt from ..seed import Seed -from ..util import msg,vmsg,qmsg,make_chksum_8 +from ..util import msg,make_chksum_8 from .enc import wallet class wallet(wallet): @@ -40,18 +38,18 @@ class wallet(wallet): return ( self.crypto.aesctr_iv_len + self.crypto.salt_len - + (0 if opt.old_incog_fmt else self.crypto.hincog_chk_len) + + (0 if self.cfg.old_incog_fmt else self.crypto.hincog_chk_len) + seed_len//8 ) def _incog_data_size_chk(self): # valid sizes: 56, 64, 72 dlen = len(self.fmt_data) - seed_len = opt.seed_len or Seed.dfl_len + seed_len = self.cfg.seed_len or Seed.dfl_len valid_dlen = self._get_incog_data_len(seed_len) if dlen == valid_dlen: return True else: - if opt.old_incog_fmt: + if self.cfg.old_incog_fmt: msg('WARNING: old-style incognito format requested. Are you sure this is correct?') msg(f'Invalid incognito data size ({dlen} bytes) for this seed length ({seed_len} bits)') msg(f'Valid data size for this seed length: {valid_dlen} bytes') @@ -63,7 +61,7 @@ class wallet(wallet): def _encrypt (self): self._get_first_pw_and_hp_and_encrypt_seed() - if opt.old_incog_fmt: + if self.cfg.old_incog_fmt: die(1,'Writing old-format incognito wallets is unsupported') d = self.ssdata crypto = self.crypto @@ -71,8 +69,8 @@ class wallet(wallet): d.iv = crypto.get_random( crypto.aesctr_iv_len ) d.iv_id = self._make_iv_chksum(d.iv) msg(f'New Incog Wallet ID: {d.iv_id}') - qmsg('Make a record of this value') - vmsg('\n ' + self.msg['record_incog_id'].strip()+'\n') + self.cfg._util.qmsg('Make a record of this value') + self.cfg._util.vmsg('\n ' + self.msg['record_incog_id'].strip()+'\n') d.salt = crypto.get_random( crypto.salt_len ) seed_key = crypto.make_key( @@ -95,7 +93,7 @@ class wallet(wallet): desc = 'incog wrapper key' ) d.key_id = make_chksum_8(d.wrapper_key) - vmsg(f'Key ID: {d.key_id}') + self.cfg._util.vmsg(f'Key ID: {d.key_id}') d.target_data_len = self._get_incog_data_len(self.seed.bitlen) @@ -128,8 +126,8 @@ class wallet(wallet): d.incog_id = self._make_iv_chksum(d.iv) d.enc_incog_data = self.fmt_data[self.crypto.aesctr_iv_len:] msg(f'Incog Wallet ID: {d.incog_id}') - qmsg('Check this value against your records') - vmsg('\n ' + self.msg['check_incog_id'].strip()+'\n') + self.cfg._util.qmsg('Check this value against your records') + self.cfg._util.vmsg('\n ' + self.msg['check_incog_id'].strip()+'\n') return True @@ -137,7 +135,7 @@ class wallet(wallet): chk,seed = data[:8],data[8:] from hashlib import sha256 if sha256(seed).digest()[:8] == chk: - qmsg('Passphrase{} are correct'.format( self.msg['decrypt_params'].format('and') )) + self.cfg._util.qmsg('Passphrase{} are correct'.format( self.msg['decrypt_params'].format('and') )) return seed else: msg('Incorrect passphrase{}'.format( self.msg['decrypt_params'].format('or') )) @@ -146,7 +144,7 @@ class wallet(wallet): def _verify_seed_oldfmt(self,seed): m = f'Seed ID: {make_chksum_8(seed)}. Is the Seed ID correct?' from ..ui import keypress_confirm - if keypress_confirm(m, True): + if keypress_confirm( self.cfg, m, True ): return seed else: return False @@ -179,9 +177,9 @@ class wallet(wallet): hash_preset = d.hash_preset, desc = 'main key' ) - qmsg(f'Key ID: {make_chksum_8(seed_key)}') + self.cfg._util.qmsg(f'Key ID: {make_chksum_8(seed_key)}') - verify_seed_func = getattr( self, '_verify_seed_'+ ('oldfmt' if opt.old_incog_fmt else 'newfmt') ) + verify_seed_func = getattr( self, '_verify_seed_'+ ('oldfmt' if self.cfg.old_incog_fmt else 'newfmt') ) seed = verify_seed_func( crypto.decrypt_seed( @@ -191,7 +189,7 @@ class wallet(wallet): key_id = '' )) if seed: - self.seed = Seed(seed) + self.seed = Seed( self.cfg, seed ) msg(f'Seed ID: {self.seed.sid}') return True else: diff --git a/mmgen/wallet/incog_hidden.py b/mmgen/wallet/incog_hidden.py index 1ef86bba..e9a5bdc3 100755 --- a/mmgen/wallet/incog_hidden.py +++ b/mmgen/wallet/incog_hidden.py @@ -15,9 +15,8 @@ wallet.incog_hidden: hidden incognito wallet class import os from ..globalvars import gc -from ..opts import opt from ..seed import Seed -from ..util import msg,dmsg,qmsg,die,compare_or_die,capfirst +from ..util import msg,die,capfirst from ..util2 import parse_bytespec from .incog_base import wallet @@ -49,7 +48,7 @@ class wallet(wallet): } def _get_hincog_params(self,wtype): - a = getattr(opt,'hidden_incog_'+ wtype +'_params').split(',') + a = getattr(self.cfg,'hidden_incog_'+ wtype +'_params').split(',') return ','.join(a[:-1]),int(a[-1]) # permit comma in filename def _check_valid_offset(self,fn,action): @@ -68,10 +67,10 @@ class wallet(wallet): d = self.ssdata d.hincog_offset = self._get_hincog_params('input')[1] - qmsg(f'Getting hidden incog data from file {self.infile.name!r}') + self.cfg._util.qmsg(f'Getting hidden incog data from file {self.infile.name!r}') # Already sanity-checked: - d.target_data_len = self._get_incog_data_len(opt.seed_len or Seed.dfl_len) + d.target_data_len = self._get_incog_data_len(self.cfg.seed_len or Seed.dfl_len) self._check_valid_offset(self.infile,'read') flgs = os.O_RDONLY|os.O_BINARY if gc.platform == 'win' else os.O_RDONLY @@ -79,13 +78,13 @@ class wallet(wallet): os.lseek(fh,int(d.hincog_offset),os.SEEK_SET) self.fmt_data = os.read(fh,d.target_data_len) os.close(fh) - qmsg(f'Data read from file {self.infile.name!r} at offset {d.hincog_offset}') + self.cfg._util.qmsg(f'Data read from file {self.infile.name!r} at offset {d.hincog_offset}') # overrides method in Wallet def write_to_file(self): d = self.ssdata self._format() - compare_or_die( + self.cfg._util.compare_or_die( val1 = d.target_data_len, desc1 = 'target data length', val2 = len(self.fmt_data), @@ -94,8 +93,8 @@ class wallet(wallet): k = ('output','input')[self.op=='pwchg_new'] fn,d.hincog_offset = self._get_hincog_params(k) - if opt.outdir and not os.path.dirname(fn): - fn = os.path.join(opt.outdir,fn) + if self.cfg.outdir and not os.path.dirname(fn): + fn = os.path.join(self.cfg.outdir,fn) check_offset = True try: @@ -103,18 +102,19 @@ class wallet(wallet): except: from ..ui import keypress_confirm,line_input if keypress_confirm( + self.cfg, f'Requested file {fn!r} does not exist. Create?', default_yes = True ): min_fsize = d.target_data_len + d.hincog_offset msg('\n ' + self.msg['choose_file_size'].strip().format(min_fsize)+'\n') while True: - fsize = parse_bytespec(line_input('Enter file size: ')) + fsize = parse_bytespec(line_input( self.cfg, 'Enter file size: ' )) if fsize >= min_fsize: break msg(f'File size must be an integer no less than {min_fsize}') from ..tool.fileutil import tool_cmd - tool_cmd().rand2file(fn,str(fsize)) + tool_cmd(self.cfg).rand2file(fn,str(fsize)) check_offset = False else: die(1,'Exiting at user request') @@ -122,16 +122,17 @@ class wallet(wallet): from ..filename import MMGenFile f = MMGenFile(fn,subclass=type(self),write=True) - dmsg('{} data len {}, offset {}'.format( + self.cfg._util.dmsg('{} data len {}, offset {}'.format( capfirst(self.desc), d.target_data_len, d.hincog_offset )) if check_offset: self._check_valid_offset(f,'write') - if not opt.quiet: + if not self.cfg.quiet: from ..ui import confirm_or_raise confirm_or_raise( + self.cfg, message = '', action = f'alter file {f.name!r}' ) diff --git a/mmgen/wallet/mmgen.py b/mmgen/wallet/mmgen.py index 5c27c58e..edc151c6 100755 --- a/mmgen/wallet/mmgen.py +++ b/mmgen/wallet/mmgen.py @@ -14,10 +14,8 @@ wallet.mmgen: MMGen native wallet class import os -from ..globalvars import g -from ..opts import opt from ..seed import Seed -from ..util import msg,qmsg,make_timestamp,make_chksum_6,split_into_cols,is_chksum_6,compare_chksums +from ..util import msg,make_timestamp,make_chksum_6,split_into_cols,is_chksum_6 from ..obj import MMGenWalletLabel,get_obj from ..baseconv import baseconv @@ -28,9 +26,9 @@ class wallet(wallet): desc = 'MMGen wallet' def __init__(self,*args,**kwargs): - if opt.label: + if self.cfg.label: self.label = MMGenWalletLabel( - opt.label, + self.cfg.label, msg = "Error in option '--label'" ) else: self.label = None @@ -43,7 +41,7 @@ class wallet(wallet): 'for no label' ) from ..ui import line_input while True: - ret = line_input(prompt) + ret = line_input( self.cfg, prompt ) if ret: lbl = get_obj(MMGenWalletLabel,s=ret) if lbl: @@ -57,19 +55,19 @@ class wallet(wallet): def _get_label(self): if hasattr(self,'ss_in') and hasattr(self.ss_in.ssdata,'label'): old_lbl = self.ss_in.ssdata.label - if opt.keep_label: + if self.cfg.keep_label: lbl = old_lbl - qmsg('Reusing label {} at user request'.format( lbl.hl2(encl='‘’') )) + self.cfg._util.qmsg('Reusing label {} at user request'.format( lbl.hl2(encl='‘’') )) elif self.label: lbl = self.label - qmsg('Using label {} requested on command line'.format( lbl.hl2(encl='‘’') )) + self.cfg._util.qmsg('Using label {} requested on command line'.format( lbl.hl2(encl='‘’') )) else: # Prompt, using old value as default lbl = self._get_label_from_user(old_lbl) - if (not opt.keep_label) and self.op == 'pwchg_new': - qmsg('Label {}'.format( 'unchanged' if lbl == old_lbl else f'changed to {lbl!r}' )) + if (not self.cfg.keep_label) and self.op == 'pwchg_new': + self.cfg._util.qmsg('Label {}'.format( 'unchanged' if lbl == old_lbl else f'changed to {lbl!r}' )) elif self.label: lbl = self.label - qmsg('Using label {} requested on command line'.format( lbl.hl2(encl='‘’') )) + self.cfg._util.qmsg('Using label {} requested on command line'.format( lbl.hl2(encl='‘’') )) else: lbl = self._get_label_from_user() self.ssdata.label = lbl @@ -110,7 +108,7 @@ class wallet(wallet): return False chk = make_chksum_6(' '.join(lines[1:])) - if not compare_chksums(lines[0],'master',chk,'computed', + if not self.cfg._util.compare_chksums(lines[0],'master',chk,'computed', hdr='For wallet master checksum',verbose=True): return False @@ -132,9 +130,9 @@ class wallet(wallet): hpdata = lines[3].split() d.hash_preset = hp = hpdata[0][:-1] # a string! - qmsg(f'Hash preset of wallet: {hp!r}') - if opt.hash_preset and opt.hash_preset != hp: - qmsg(f'Warning: ignoring user-requested hash preset {opt.hash_preset!r}') + self.cfg._util.qmsg(f'Hash preset of wallet: {hp!r}') + if self.cfg.hash_preset and self.cfg.hash_preset != hp: + self.cfg._util.qmsg(f'Warning: ignoring user-requested hash preset {self.cfg.hash_preset!r}') hash_params = tuple(map(int,hpdata[1:])) @@ -152,7 +150,7 @@ class wallet(wallet): msg(f'Invalid format for {key} in {self.desc}: {l}') return False - if not compare_chksums(chk,key, + if not self.cfg._util.compare_chksums(chk,key, make_chksum_6(b58_val),'computed checksum',verbose=True): return False @@ -169,11 +167,11 @@ class wallet(wallet): d = self.ssdata # Needed for multiple transactions with {}-txsign d.passwd = self._get_passphrase( - add_desc = os.path.basename(self.infile.name) if opt.quiet else '' ) + add_desc = os.path.basename(self.infile.name) if self.cfg.quiet else '' ) key = self.crypto.make_key( d.passwd, d.salt, d.hash_preset ) ret = self.crypto.decrypt_seed( d.enc_seed, key, d.seed_id, d.key_id ) if ret: - self.seed = Seed(ret) + self.seed = Seed( self.cfg, ret ) return True else: return False diff --git a/mmgen/wallet/mmhex.py b/mmgen/wallet/mmhex.py index 4b6f1e87..582c0930 100755 --- a/mmgen/wallet/mmhex.py +++ b/mmgen/wallet/mmhex.py @@ -14,7 +14,7 @@ wallet.mmhex: MMGen hexadecimal file wallet class from ..util import make_chksum_6,split_into_cols from ..seed import Seed -from ..util import msg,vmsg_r,is_chksum_6,is_hex_str,compare_chksums +from ..util import msg,is_chksum_6,is_hex_str from .unenc import wallet class wallet(wallet): @@ -52,12 +52,12 @@ class wallet(wallet): msg(f'{hstr!r}: not a hexadecimal string, in {desc}') return False - vmsg_r(f'Validating {desc} checksum...') + self.cfg._util.vmsg_r(f'Validating {desc} checksum...') - if not compare_chksums(chk,'file',make_chksum_6(hstr),'computed',verbose=True): + if not self.cfg._util.compare_chksums(chk,'file',make_chksum_6(hstr),'computed',verbose=True): return False - self.seed = Seed(bytes.fromhex(hstr)) + self.seed = Seed( self.cfg, bytes.fromhex(hstr) ) self.ssdata.chksum = chk self.ssdata.hexseed = hstr diff --git a/mmgen/wallet/mnemonic.py b/mmgen/wallet/mnemonic.py index 12648f87..b485c1ee 100755 --- a/mmgen/wallet/mnemonic.py +++ b/mmgen/wallet/mnemonic.py @@ -12,9 +12,8 @@ wallet.mnemonic: MMGen mnemonic wallet base class """ -from ..globalvars import g from ..baseconv import baseconv -from ..util import msg,compare_or_die +from ..util import msg from ..seed import Seed from .unenc import wallet @@ -31,14 +30,14 @@ class wallet(wallet): def _get_data_from_user(self,desc): - if not g.stdin_tty: + if not self.cfg.stdin_tty: from ..ui import get_data_from_user - return get_data_from_user(desc) + return get_data_from_user( self.cfg, desc ) mn_len = self._choose_seedlen( self.mn_lens ) from ..mn_entry import mn_entry - return mn_entry(self.wl_id).get_mnemonic_from_user(mn_len) + return mn_entry( self.cfg, self.wl_id ).get_mnemonic_from_user(mn_len) def _format(self): @@ -49,7 +48,7 @@ class wallet(wallet): rev = bc.tohex( mn, 'seed' ) # Internal error, so just die on fail - compare_or_die( rev, 'recomputed seed', hexseed, 'original', e='Internal error' ) + self.cfg._util.compare_or_die( rev, 'recomputed seed', hexseed, 'original', e='Internal error' ) self.ssdata.mnemonic = mn self.fmt_data = ' '.join(mn) + '\n' @@ -78,14 +77,14 @@ class wallet(wallet): return False # Internal error, so just die: - compare_or_die( + self.cfg._util.compare_or_die( val1 = ' '.join(rev), val2 = ' '.join(mn), desc1 = 'recomputed mnemonic', desc2 = 'original mnemonic', e = 'Internal error' ) - self.seed = Seed(bytes.fromhex(hexseed)) + self.seed = Seed( self.cfg, bytes.fromhex(hexseed) ) self.ssdata.mnemonic = mn self.check_usr_seed_len() diff --git a/mmgen/wallet/plainhex.py b/mmgen/wallet/plainhex.py index a59941b3..c0c1e4a0 100755 --- a/mmgen/wallet/plainhex.py +++ b/mmgen/wallet/plainhex.py @@ -36,7 +36,7 @@ class wallet(wallet): msg(f'Invalid data length ({len(d)}) in {desc}') return False - self.seed = Seed(bytes.fromhex(d)) + self.seed = Seed( self.cfg, bytes.fromhex(d) ) self.ssdata.hexseed = d self.check_usr_seed_len() diff --git a/mmgen/wallet/seed.py b/mmgen/wallet/seed.py index 5b864d43..b58032b8 100755 --- a/mmgen/wallet/seed.py +++ b/mmgen/wallet/seed.py @@ -12,7 +12,7 @@ wallet.seed: seed file wallet class """ -from ..util import msg,vmsg_r,make_chksum_6,split_into_cols,is_chksum_6,compare_chksums +from ..util import msg,make_chksum_6,split_into_cols,is_chksum_6 from ..baseconv import baseconv,is_b58_str from ..seed import Seed from .unenc import wallet @@ -48,9 +48,9 @@ class wallet(wallet): msg(f'{b!r}: not a base 58 string, in {desc}') return False - vmsg_r(f'Validating {desc} checksum...') + self.cfg._util.vmsg_r(f'Validating {desc} checksum...') - if not compare_chksums(a,'file',make_chksum_6(b),'computed',verbose=True): + if not self.cfg._util.compare_chksums(a,'file',make_chksum_6(b),'computed',verbose=True): return False ret = baseconv('b58').tobytes(b,pad='seed') @@ -59,7 +59,7 @@ class wallet(wallet): msg(f'Invalid base-58 encoded seed: {val}') return False - self.seed = Seed(ret) + self.seed = Seed( self.cfg, ret ) self.ssdata.chksum = a self.ssdata.b58seed = b diff --git a/mmgen/wallet/unenc.py b/mmgen/wallet/unenc.py index 5695c389..72f04f6d 100755 --- a/mmgen/wallet/unenc.py +++ b/mmgen/wallet/unenc.py @@ -12,7 +12,6 @@ wallet.unenc: unencrypted wallet base class """ -from ..globalvars import g from ..color import blue,yellow from ..util import msg,msg_r,capfirst,is_int from .base import wallet @@ -41,7 +40,7 @@ class wallet(wallet): r = get_char('\r'+prompt) if is_int(r) and 1 <= int(r) <= len(ok_lens): break - msg_r(('\r','\n')[g.test_suite] + ' '*len(prompt) + '\r') + msg_r(('\r','\n')[self.cfg.test_suite] + ' '*len(prompt) + '\r') return ok_lens[int(r)-1] msg('{} {}'.format( @@ -54,7 +53,8 @@ class wallet(wallet): prompt = self.choose_seedlen_confirm.format(usr_len) from ..ui import keypress_confirm if keypress_confirm( + self.cfg, prompt, default_yes = True, - no_nl = not g.test_suite ): + no_nl = not self.cfg.test_suite ): return usr_len diff --git a/mmgen/xmrwallet.py b/mmgen/xmrwallet.py index 30052d7d..0dd1a853 100755 --- a/mmgen/xmrwallet.py +++ b/mmgen/xmrwallet.py @@ -22,9 +22,6 @@ xmrwallet.py - MoneroWalletOps class import os,re,time,json from collections import namedtuple - -from .globalvars import g -from .opts import opt from .objmethods import MMGenObject,Hilite,InitErrors from .obj import CoinTxID from .color import red,yellow,green,blue,cyan,pink,orange @@ -42,7 +39,6 @@ from .util import ( make_timestr, make_chksum_6, capfirst, - stdout_or_pager, ) from .seed import SeedID from .protocol import init_proto @@ -203,6 +199,7 @@ class MoneroMMGenTX: ) from .fileutil import write_data_to_file write_data_to_file( + cfg = self.cfg, outfile = fn, data = out, desc = 'MoneroMMGenTX data', @@ -216,7 +213,10 @@ class MoneroMMGenTX: assert not args, 'Non-keyword args not permitted' d = namedtuple('kwargs_tuple',kwargs)(**kwargs) - proto = init_proto( 'xmr', network=d.network, need_amt=True ) + self.cfg = d.cfg + + proto = init_proto( self.cfg, 'xmr', network=d.network, need_amt=True ) + now = int(time.time()) self.data = self.xmrwallet_tx_data( @@ -237,12 +237,13 @@ class MoneroMMGenTX: class Signed(Base): - def __init__(self,fn): + def __init__(self,cfg,fn): from .fileutil import get_data_from_file + self.cfg = cfg self.fn = fn - d_wrap = json.loads(get_data_from_file(fn))['MoneroMMGenTX'] + d_wrap = json.loads(get_data_from_file( cfg, fn ))['MoneroMMGenTX'] d = self.xmrwallet_tx_data(**d_wrap['data']) - proto = init_proto( 'xmr', network=d.network, need_amt=True ) + proto = init_proto( cfg, 'xmr', network=d.network, need_amt=True ) self.data = self.xmrwallet_tx_data( op = d.op, create_time = d.create_time, @@ -283,7 +284,7 @@ class MoneroWalletOps: opts = ('wallet_dir',) - def __init__(self,uarg_tuple,uopt_tuple): + def __init__(self,cfg,uarg_tuple,uopt_tuple): def gen_classes(): for cls in type(self).__mro__: @@ -291,6 +292,7 @@ class MoneroWalletOps: if cls.__name__ == 'base': break + self.cfg = cfg classes = tuple(gen_classes()) self.opts = tuple(set(opt for cls in classes for opt in cls.opts)) @@ -314,7 +316,7 @@ class MoneroWalletOps: cls.check_uopts(self) id_cur = id(cls.check_uopts) - self.proto = init_proto( 'xmr', network=g.network, need_amt=True ) + self.proto = init_proto( cfg, 'xmr', network=self.cfg.network, need_amt=True ) def check_uopts(self): @@ -363,7 +365,7 @@ class MoneroWalletOps: ) wallet_exists = True - def __init__(self,uarg_tuple,uopt_tuple): + def __init__(self,cfg,uarg_tuple,uopt_tuple): def wallet_exists(fn): try: os.stat(fn) @@ -379,9 +381,10 @@ class MoneroWalletOps: elif not exists and self.wallet_exists: die(1,f'Wallet {fn!r} not found!') - super().__init__(uarg_tuple,uopt_tuple) + super().__init__(cfg,uarg_tuple,uopt_tuple) self.kal = KeyAddrList( + cfg, self.proto, uarg.infile, key_address_validity_check = True ) @@ -391,13 +394,15 @@ class MoneroWalletOps: check_wallets() self.wd = MoneroWalletDaemon( + cfg = self.cfg, proto = self.proto, wallet_dir = uopt.wallet_dir or '.', - test_suite = g.test_suite, + test_suite = self.cfg.test_suite, daemon_addr = uopt.daemon or None, ) self.c = MoneroWalletRPCClient( + cfg = self.cfg, daemon = self.wd, test_connection = False, ) @@ -423,7 +428,7 @@ class MoneroWalletOps: uopt.wallet_dir or '.','{}-{}-MoneroWallet{}'.format( self.kal.al_id.sid, d.idx, - f'.{g.network}' if g.network != 'mainnet' else '')) + f'.{self.cfg.network}' if self.cfg.network != 'mainnet' else '')) async def main(self): gmsg('\n{}ing {} wallet{}'.format( @@ -582,6 +587,7 @@ class MoneroWalletOps: get_tx_metadata = True ) return MoneroMMGenTX.NewSigned( + cfg = self.parent.cfg, op = uarg.op, network = self.parent.proto.network, seed_id = self.parent.kal.al_id.sid, @@ -609,6 +615,7 @@ class MoneroWalletOps: die(3,'More than one TX required. Cannot perform this sweep') return MoneroMMGenTX.NewSigned( + cfg = self.parent.cfg, op = uarg.op, network = self.parent.proto.network, seed_id = self.parent.kal.al_id.sid, @@ -654,23 +661,24 @@ class MoneroWalletOps: restore_height = uopt.restore_height, language = 'English' ) - pp_msg(ret) if opt.debug else msg(' Address: {}'.format( ret['address'] )) + pp_msg(ret) if self.cfg.debug else msg(' Address: {}'.format( ret['address'] )) return True class sync(wallet): name = 'sync' opts = ('rescan_blockchain',) - def __init__(self,uarg_tuple,uopt_tuple): + def __init__(self,cfg,uarg_tuple,uopt_tuple): - super().__init__(uarg_tuple,uopt_tuple) + super().__init__(cfg,uarg_tuple,uopt_tuple) host,port = uopt.daemon.split(':') if uopt.daemon else ('localhost',self.wd.daemon_port) from .daemon import CoinDaemon self.dc = MoneroRPCClient( + cfg = self.cfg, proto = self.proto, - daemon = CoinDaemon('xmr'), + daemon = CoinDaemon( self.cfg, 'xmr' ), host = host, port = int(port), user = None, @@ -843,18 +851,20 @@ class MoneroWalletOps: m = re.fullmatch(uarg_info['tx_relay_daemon'].pat,uopt.tx_relay_daemon,re.ASCII) wd2 = MoneroWalletDaemon( + cfg = self.cfg, proto = self.proto, wallet_dir = uopt.wallet_dir or '.', - test_suite = g.test_suite, + test_suite = self.cfg.test_suite, daemon_addr = m[1], proxy = m[2] ) - if g.test_suite: + if self.cfg.test_suite: wd2.usr_daemon_args = ['--daemon-ssl-allow-any-cert'] wd2.start() self.c = MoneroWalletRPCClient( + cfg = self.cfg, daemon = wd2 ) async def main(self): @@ -879,9 +889,9 @@ class MoneroWalletOps: dest_addr = self.dest_addr elif self.dest == None: dest_acct = self.account - if keypress_confirm(f'\nCreate new address for account #{self.account}?'): + if keypress_confirm( self.cfg, f'\nCreate new address for account #{self.account}?' ): dest_addr_chk = h.create_new_addr(self.account) - elif keypress_confirm(f'Sweep to last existing address of account #{self.account}?'): + elif keypress_confirm( self.cfg, f'Sweep to last existing address of account #{self.account}?' ): dest_addr_chk = None else: die(1,'Exiting at user request') @@ -895,11 +905,11 @@ class MoneroWalletOps: h2.open_wallet('destination') accts_data = h2.get_accts()[0] - if keypress_confirm(f'\nCreate new account for wallet {bn!r}?'): + if keypress_confirm( self.cfg, f'\nCreate new account for wallet {bn!r}?' ): dest_acct,dest_addr = h2.create_acct() dest_addr_idx = 0 h2.get_accts() - elif keypress_confirm(f'Sweep to last existing account of wallet {bn!r}?'): + elif keypress_confirm( self.cfg, f'Sweep to last existing account of wallet {bn!r}?' ): dest_acct,dest_addr_chk = h2.get_last_acct(accts_data) dest_addr,dest_addr_idx = h2.get_last_addr(dest_acct,display=False) assert dest_addr_chk == dest_addr, 'dest_addr_chk2' @@ -927,7 +937,7 @@ class MoneroWalletOps: if uopt.no_relay: return True - if keypress_confirm(f'Relay {self.name} transaction?'): + if keypress_confirm( self.cfg, f'Relay {self.name} transaction?' ): w_desc = 'source' if uopt.tx_relay_daemon: await h.stop_wallet('source') @@ -1019,7 +1029,7 @@ class MoneroWalletOps: if addr['label'] == self.label: ymsg('\nLabel is unchanged, operation cancelled') - elif keypress_confirm(' {} label?'.format('Set' if self.label else 'Remove')): + elif keypress_confirm( self.cfg, ' {} label?'.format('Set' if self.label else 'Remove') ): h.set_label( self.account, self.address_idx, self.label ) accts_data = h.get_accts(print=False)[0] ret = h.print_addrs(accts_data,self.account) @@ -1036,9 +1046,9 @@ class MoneroWalletOps: name = 'relay' opts = ('tx_relay_daemon',) - def __init__(self,uarg_tuple,uopt_tuple): + def __init__(self,cfg,uarg_tuple,uopt_tuple): - super().__init__(uarg_tuple,uopt_tuple) + super().__init__(cfg,uarg_tuple,uopt_tuple) if uopt.tx_relay_daemon: m = re.fullmatch(uarg_info['tx_relay_daemon'].pat,uopt.tx_relay_daemon,re.ASCII) @@ -1047,11 +1057,12 @@ class MoneroWalletOps: md = None else: from .daemon import CoinDaemon - md = CoinDaemon('xmr',test_suite=g.test_suite) + md = CoinDaemon( self.cfg, 'xmr', test_suite=self.cfg.test_suite ) host,port = md.host,md.rpc_port proxy = None self.dc = MoneroRPCClient( + cfg = self.cfg, proto = self.proto, daemon = md, host = host, @@ -1061,7 +1072,7 @@ class MoneroWalletOps: test_connection = False, # relay is presumably a public node, so avoid extra connections proxy = proxy ) - self.tx = MoneroMMGenTX.Signed(uarg.infile) + self.tx = MoneroMMGenTX.Signed( self.cfg, uarg.infile ) async def main(self): msg('\n' + self.tx.get_info()) @@ -1069,7 +1080,7 @@ class MoneroWalletOps: if uopt.tx_relay_daemon: self.display_tx_relay_info() - if keypress_confirm('Relay transaction?'): + if keypress_confirm( self.cfg, 'Relay transaction?' ): res = self.dc.call_raw( 'send_raw_transaction', tx_as_hex = self.tx.data.blob @@ -1088,10 +1099,10 @@ class MoneroWalletOps: name = 'txview' async def main(self): - stdout_or_pager( + self.cfg._util.stdout_or_pager( '\n'.join( tx.get_info() for tx in sorted( - (MoneroMMGenTX.Signed(fn) for fn in uarg.infile), + (MoneroMMGenTX.Signed( self.cfg, fn ) for fn in uarg.infile), key = lambda x: x.data.sign_time ) )) diff --git a/scripts/compute-file-chksum.py b/scripts/compute-file-chksum.py index 4971fa09..b4ae85a9 100755 --- a/scripts/compute-file-chksum.py +++ b/scripts/compute-file-chksum.py @@ -17,11 +17,11 @@ opts_data = { } } -cmd_args = opts.init(opts_data) +cfg = opts.init(opts_data) from mmgen.fileutil import get_lines_from_file -lines = get_lines_from_file(cmd_args[0]) -start = (1,0)[bool(opt.include_first_line)] +lines = get_lines_from_file( cfg, cfg._args[0] ) +start = (1,0)[bool(cfg.include_first_line)] a = make_chksum_6(' '.join(lines[start:]).encode()) if start == 1: b = lines[0] diff --git a/scripts/create-token.py b/scripts/create-token.py index 97e9b0f3..d16e06a3 100755 --- a/scripts/create-token.py +++ b/scripts/create-token.py @@ -175,19 +175,20 @@ contract Token is ERC20Interface, Owned, SafeMath { } """ % req_solc_ver_pat -def create_src(proto,template,token_data,owner_addr): +def create_src(cfg,template,token_data): def gen(): for k in token_data.fields: field = getattr(token_data,k) if k == 'owner_addr': + owner_addr = cfg._args[0] from mmgen.addr import is_coin_addr - if not is_coin_addr( proto, owner_addr.lower() ): - die(1,f'{owner_addr}: not a valid {proto.coin} coin address') + if not is_coin_addr( cfg._proto, owner_addr.lower() ): + die(1,f'{owner_addr}: not a valid {cfg._proto.coin} coin address') val = '0x' + owner_addr else: val = ( - getattr(opt,k) + getattr(cfg,k) or getattr(field,'default',None) or die(1,f'The --{k} option must be specified') ) @@ -231,10 +232,10 @@ def check_solc_version(): Msg(f'solc version ({version_str}) does not match requirement ({req_solc_ver_pat})') return False -def compile_code(code): +def compile_code(cfg,code): cmd = ['solc','--optimize','--bin','--overwrite'] - if not opt.stdout: - cmd += ['--output-dir', opt.outdir or '.'] + if not cfg.stdout: + cmd += ['--output-dir', cfg.outdir or '.'] cmd += ['-'] msg(f"Executing: {' '.join(cmd)}") cp = run(cmd,input=code.encode(),stdout=PIPE,stderr=PIPE) @@ -247,37 +248,34 @@ def compile_code(code): if err: ymsg('Solidity compiler produced the following warning:') msg(err) - if opt.stdout: + if cfg.stdout: o = out.split('\n') return {k:o[i+2] for k in ('SafeMath','Owned','Token') for i in range(len(o)) if k in o[i]} else: - vmsg(out) + cfg._util.vmsg(out) if __name__ == '__main__': - cmd_args = opts.init(opts_data) + cfg = opts.init(opts_data) - if opt.check_solc_version: + if cfg.check_solc_version: sys.exit(0 if check_solc_version() else 1) - from mmgen.protocol import init_proto_from_opts - proto = init_proto_from_opts() - - if not proto.coin in ('ETH','ETC'): + if not cfg._proto.coin in ('ETH','ETC'): die(1,'--coin option must be ETH or ETC') - if not len(cmd_args) == 1: + if not len(cfg._args) == 1: opts.usage() - code = create_src( proto, solidity_code_template, token_data, cmd_args[0] ) + code = create_src( cfg, solidity_code_template, token_data ) - if opt.preprocess: + if cfg.preprocess: Msg(code) sys.exit(0) - out = compile_code(code) + out = compile_code( cfg, code ) - if opt.stdout: + if cfg.stdout: print(json.dumps(out)) msg('Contract successfully compiled') diff --git a/scripts/tx-v2-to-v3.py b/scripts/tx-v2-to-v3.py index c08b38d8..0be1b099 100755 --- a/scripts/tx-v2-to-v3.py +++ b/scripts/tx-v2-to-v3.py @@ -22,13 +22,13 @@ opts_data = { } } -cmd_args = opts.init(opts_data) +cfg = opts.init(opts_data) import asyncio from mmgen.tx import CompletedTX -if len(cmd_args) != 1: +if len(cfg._args) != 1: opts.usage() -tx = asyncio.run(CompletedTX(cmd_args[0],quiet_open=True)) -tx.file.write(ask_tty=False,ask_overwrite=not opt.quiet,ask_write=not opt.quiet) +tx = asyncio.run(CompletedTX(cfg._args[0],quiet_open=True)) +tx.file.write(ask_tty=False,ask_overwrite=not cfg.quiet,ask_write=not cfg.quiet) diff --git a/scripts/uninstall-mmgen.py b/scripts/uninstall-mmgen.py index 152bc7eb..9be2875d 100755 --- a/scripts/uninstall-mmgen.py +++ b/scripts/uninstall-mmgen.py @@ -51,12 +51,12 @@ opts_data = { } } -cmd_args = opts.init(opts_data) +cfg = opts.init(opts_data) if gc.platform == 'linux' and os.getenv('USER') != 'root': die(1,'This program must be run as root') -if len(cmd_args): +if len(cfg._args): opts.usage() mod_dir = os.path.split(normalize_path(modpath_save))[0] @@ -77,13 +77,13 @@ for d in (ulb,mod_pardir): # add files only, not directories del_list += [os.path.join(d,e) for e in os.listdir(d) if is_reg(os.path.join(d,e)) and e[:6] == 'mmgen-'] -if opt.list_paths: +if cfg.list_paths: die(1,'\n'.join(del_list)) -if not opt.no_prompt: +if not cfg.no_prompt: m = 'Deleting the following paths and files:\n {}\nProceed?' from mmgen.ui import keypress_confirm - if not keypress_confirm(m.format('\n '.join(del_list))): + if not keypress_confirm( cfg, m.format('\n '.join(del_list)) ): die(1,'Exiting at user request') import shutil diff --git a/test/gentest.py b/test/gentest.py index f294742a..7fe45a23 100755 --- a/test/gentest.py +++ b/test/gentest.py @@ -28,10 +28,9 @@ sys.path.insert(0,overlay_setup(repo_root)) # Import these _after_ local path's been added to sys.path import mmgen.opts as opts -from mmgen.globalvars import g,gc -from mmgen.opts import opt +from mmgen.globalvars import gc from mmgen.color import green,red,purple -from mmgen.util import msg,qmsg,qmsg_r,vmsg,capfirst,is_int,die +from mmgen.util import msg,capfirst,is_int,die results_file = 'gentest.out.json' @@ -149,7 +148,7 @@ class GenTool(object): self.data = {} def __del__(self): - if opt.save_results: + if cfg.save_results: key = f'{self.proto.coin}-{self.proto.network}-{self.addr_type.name}-{self.desc}'.lower() saved_results[key] = {k.hex():v._asdict() for k,v in self.data.items()} @@ -251,14 +250,14 @@ def find_or_check_tool(proto,addr_type,toolname): if toolname not in ext_progs + ['ext']: die(1,f'{toolname!r}: unsupported tool for network {proto.network}') - if opt.all_coins and toolname == 'ext': + if cfg.all_coins and toolname == 'ext': die(1,"'--all-coins' must be combined with a specific external testing tool") else: tool = cinfo.get_test_support( proto.coin, addr_type.name, proto.network, - verbose = not opt.quiet, + verbose = not cfg.quiet, toolname = toolname if toolname != 'ext' else None ) if tool and toolname in ext_progs and toolname != tool: sys.exit(3) @@ -283,11 +282,11 @@ def test_equal(desc,a_val,b_val,in_bytes,sec,wif,a_desc,b_desc): w=max(len(e) for e in (a_desc,b_desc)) + 1 ).rstrip()) -def do_ab_test(proto,cfg,addr_type,gen1,kg2,ag,tool,cache_data): +def do_ab_test(proto,scfg,addr_type,gen1,kg2,ag,tool,cache_data): def do_ab_inner(n,trounds,in_bytes): global last_t - if opt.verbose or time.time() - last_t >= 0.1: + if cfg.verbose or time.time() - last_t >= 0.1: qmsg_r(f'\rRound {i+1}/{trounds} ') last_t = time.time() sec = PrivKey(proto,in_bytes,compressed=addr_type.compressed,pubkey_type=addr_type.pubkey_type) @@ -296,7 +295,7 @@ def do_ab_test(proto,cfg,addr_type,gen1,kg2,ag,tool,cache_data): tinfo = ( in_bytes, sec, sec.wif, type(kg1).__name__, type(kg2).__name__ if kg2 else tool.desc ) def do_msg(): - if opt.verbose: + if cfg.verbose: msg( fs.format( b=in_bytes.hex(), r=sec.hex(), k=sec.wif, v=vk2, a=addr1 )) if tool: @@ -321,10 +320,10 @@ def do_ab_test(proto,cfg,addr_type,gen1,kg2,ag,tool,cache_data): for privbytes in tuple(tool.data)[len(edgecase_sks):]: yield privbytes else: - for i in range(cfg.rounds): + for i in range(scfg.rounds): yield getrand(32) - kg1 = KeyGenerator( proto, addr_type.pubkey_type, gen1 ) + kg1 = KeyGenerator( cfg, proto, addr_type.pubkey_type, gen1 ) if type(kg1) == type(kg2): die(4,'Key generators are the same!') @@ -364,39 +363,39 @@ def do_ab_test(proto,cfg,addr_type,gen1,kg2,ag,tool,cache_data): qmsg(purple('edge cases:')) for i,privbytes in enumerate(edgecase_sks): do_ab_inner(i,len(edgecase_sks),privbytes) - qmsg(green('\rOK ' if opt.verbose else 'OK')) + qmsg(green('\rOK ' if cfg.verbose else 'OK')) qmsg(purple('random input:')) for i,privbytes in enumerate(get_randbytes()): - do_ab_inner(i,cfg.rounds,privbytes) - qmsg(green('\rOK ' if opt.verbose else 'OK')) + do_ab_inner(i,scfg.rounds,privbytes) + qmsg(green('\rOK ' if cfg.verbose else 'OK')) def init_tool(proto,addr_type,toolname): return globals()['GenTool'+capfirst(toolname.replace('-','_'))](proto,addr_type) -def ab_test(proto,cfg): +def ab_test(proto,scfg): - addr_type = MMGenAddrType( proto=proto, id_str=opt.type or proto.dfl_mmtype ) + addr_type = MMGenAddrType( proto=proto, id_str=cfg.type or proto.dfl_mmtype ) - if cfg.gen2: - assert cfg.gen1 != 'all', "'all' must be used only with external tool" - kg2 = KeyGenerator( proto, addr_type.pubkey_type, cfg.gen2 ) + if scfg.gen2: + assert scfg.gen1 != 'all', "'all' must be used only with external tool" + kg2 = KeyGenerator( cfg, proto, addr_type.pubkey_type, scfg.gen2 ) tool = None else: - toolname = find_or_check_tool( proto, addr_type, cfg.tool ) + toolname = find_or_check_tool( proto, addr_type, scfg.tool ) if toolname == None: - ymsg(f'Warning: skipping tool {cfg.tool!r} for {proto.coin} {addr_type.name}') + ymsg(f'Warning: skipping tool {scfg.tool!r} for {proto.coin} {addr_type.name}') return tool = init_tool( proto, addr_type, toolname ) kg2 = None - ag = AddrGenerator( proto, addr_type ) + ag = AddrGenerator( cfg, proto, addr_type ) - if cfg.all_backends: # check all backends against external tool + if scfg.all_backends: # check all backends against external tool for n in range(len(get_backends(addr_type.pubkey_type))): - do_ab_test( proto, cfg, addr_type, gen1=n+1, kg2=kg2, ag=ag, tool=tool, cache_data=cfg.rounds < 1000 and not n ) + do_ab_test( proto, scfg, addr_type, gen1=n+1, kg2=kg2, ag=ag, tool=tool, cache_data=scfg.rounds < 1000 and not n ) else: # check specific backend against external tool or another backend - do_ab_test( proto, cfg, addr_type, gen1=cfg.gen1, kg2=kg2, ag=ag, tool=tool, cache_data=False ) + do_ab_test( proto, scfg, addr_type, gen1=scfg.gen1, kg2=kg2, ag=ag, tool=tool, cache_data=False ) def speed_test(proto,kg,ag,rounds): qmsg(green('Testing speed of address generator {!r} for coin {}'.format( @@ -419,7 +418,7 @@ def speed_test(proto,kg,ag,rounds): qmsg( f'\rRound {i+1}/{rounds} ' + f'\n{rounds} addresses generated' + - ('' if g.test_suite_deterministic else f' in {time.time()-start:.2f} seconds') + ('' if cfg.test_suite_deterministic else f' in {time.time()-start:.2f} seconds') ) def dump_test(proto,kg,ag,filename): @@ -446,7 +445,7 @@ def dump_test(proto,kg,ag,filename): tinfo = (b_sec,b_sec.hex(),b_wif,type(kg).__name__,filename) test_equal('addresses',a_addr,b_addr,*tinfo) - qmsg(green(('\n','')[bool(opt.verbose)] + 'OK')) + qmsg(green(('\n','')[bool(cfg.verbose)] + 'OK')) def get_protos(proto,addr_type,toolname): @@ -455,17 +454,17 @@ def get_protos(proto,addr_type,toolname): for coin in cinfo.external_tests[proto.network][toolname]: if coin.lower() not in CoinProtocol.coins: continue - ret = init_proto(coin,testnet=proto.testnet) + ret = init_proto( cfg, coin, testnet=proto.testnet ) if addr_type not in ret.mmtypes: continue yield ret def parse_args(): - if len(cmd_args) != 2: + if len(cfg._args) != 2: opts.usage() - arg1,arg2 = cmd_args + arg1,arg2 = cfg._args gen1,gen2,rounds = (0,0,0) tool,all_backends,dumpfile = (None,None,None) @@ -498,13 +497,12 @@ def parse_args(): die(1,"First part of first argument must be a generator backend number or 'all'") if is_int(b): - if opt.all_coins: + if cfg.all_coins: die(1,'--all-coins must be used with external tool only') gen2 = b else: tool = b - proto = init_proto_from_opts() - ext_progs = list(cinfo.external_tests[proto.network]) + ['ext'] + ext_progs = list(cinfo.external_tests[cfg._proto.network]) + ['ext'] if b not in ext_progs: die(1,f'Second part of first argument must be a generator backend number or one of {ext_progs}') @@ -519,21 +517,21 @@ def parse_args(): def main(): - cfg = parse_args() - proto = init_proto_from_opts() - addr_type = MMGenAddrType( proto=proto, id_str=opt.type or proto.dfl_mmtype ) + scfg = parse_args() - if cfg.test == 'ab': - protos = get_protos(proto,addr_type,cfg.tool) if opt.all_coins else [proto] - for proto in protos: - ab_test( proto, cfg ) + addr_type = MMGenAddrType( proto=proto, id_str=cfg.type or proto.dfl_mmtype ) + + if scfg.test == 'ab': + protos = get_protos(proto,addr_type,scfg.tool) if cfg.all_coins else [proto] + for p in protos: + ab_test( p, scfg ) else: - kg = KeyGenerator( proto, addr_type.pubkey_type, cfg.gen1 ) - ag = AddrGenerator( proto, addr_type ) - if cfg.test == 'speed': - speed_test( proto, kg, ag, cfg.rounds ) - elif cfg.test == 'dump': - dump_test( proto, kg, ag, cfg.dumpfile ) + kg = KeyGenerator( cfg, proto, addr_type.pubkey_type, scfg.gen1 ) + ag = AddrGenerator( cfg, proto, addr_type ) + if scfg.test == 'speed': + speed_test( proto, kg, ag, scfg.rounds ) + elif scfg.test == 'dump': + dump_test( proto, kg, ag, scfg.dumpfile ) if saved_results: import json @@ -542,19 +540,26 @@ def main(): from subprocess import run,PIPE,DEVNULL from collections import namedtuple -from mmgen.protocol import init_proto,init_proto_from_opts,CoinProtocol +from mmgen.protocol import init_proto,CoinProtocol from mmgen.altcoin import init_genonly_altcoins,CoinInfo as cinfo from mmgen.key import PrivKey from mmgen.addr import MMGenAddrType from mmgen.addrgen import KeyGenerator,AddrGenerator from mmgen.keygen import get_backends - -from test.include.common import getrand,get_ethkey +from test.include.common import getrand,get_ethkey,set_globals gtr = namedtuple('gen_tool_result',['wif','addr','viewkey']) sd = namedtuple('saved_data_item',['reduced','wif','addr','viewkey']) sys.argv = [sys.argv[0]] + ['--skip-cfg-file'] + sys.argv[1:] -cmd_args = opts.init(opts_data) + +cfg = opts.init(opts_data) +set_globals(cfg) + +qmsg = cfg._util.qmsg +qmsg_r = cfg._util.qmsg_r +vmsg = cfg._util.vmsg + +proto = cfg._proto main() diff --git a/test/hashfunc.py b/test/hashfunc.py index 97ea4c9e..3d6ac5b0 100755 --- a/test/hashfunc.py +++ b/test/hashfunc.py @@ -23,7 +23,6 @@ test/hashfunc.py: Test internal implementations of SHA256, SHA512 and Keccak256 import sys,os import include.tests_header from mmgen.util import die -from test.include.common import getrand assert len(sys.argv) in (2,3),"Test takes 1 or 2 arguments: test name, plus optional rounds count" test = sys.argv[1].capitalize() @@ -137,6 +136,11 @@ class TestSha512(TestSha2): 0x113f9804bef90dae, 0x1b710b35131c471b, 0x28db77f523047d84, 0x32caab7b40c72493, 0x3c9ebe0a15c9bebc, 0x431d67c49c100d4c, 0x4cc5d4becb3e42b6, 0x597f299cfc657e2a, 0x5fcb6fab3ad6faec, 0x6c44198c4a475817 ) +from test.include.common import getrand,set_globals +from mmgen.globalvars import Config + +set_globals(Config()) + t = globals()['Test'+test]() msg(f'Testing internal implementation of {t.desc}\n') t.test_constants() diff --git a/test/include/coin_daemon_control.py b/test/include/coin_daemon_control.py index 16250ed5..1c1b06fb 100755 --- a/test/include/coin_daemon_control.py +++ b/test/include/coin_daemon_control.py @@ -49,7 +49,7 @@ Valid network IDs: {nid}, all, or no_xmr } } -cmd_args = opts.init(opts_data) +cfg = opts.init(opts_data) from mmgen.daemon import * @@ -60,58 +60,59 @@ class warn_missing_exec(oneshot_warning): def run(network_id=None,proto=None,daemon_id=None,missing_exec_ok=True): d = CoinDaemon( + cfg, network_id = network_id, proto = proto, - test_suite = not opt.usermode, - opts = ['no_daemonize'] if opt.no_daemonize else None, - port_shift = int(opt.port_shift or 0), - datadir = opt.datadir, + test_suite = not cfg.usermode, + opts = ['no_daemonize'] if cfg.no_daemonize else None, + port_shift = int(cfg.port_shift or 0), + datadir = cfg.datadir, daemon_id = daemon_id ) - if opt.mainnet_only and d.network != 'mainnet': + if cfg.mainnet_only and d.network != 'mainnet': return - d.debug = d.debug or opt.debug - d.wait = not opt.no_wait + d.debug = d.debug or cfg.debug + d.wait = not cfg.no_wait if missing_exec_ok: try: d.get_exec_version_str() except: - if not opt.quiet: + if not cfg.quiet: warn_missing_exec( div=d.exec_fn, fmt_args=(d.exec_fn,) ) return - if opt.print_version: + if cfg.print_version: msg('{:16} {}'.format( d.exec_fn+':', d.get_exec_version_str() )) - elif opt.get_state: + elif cfg.get_state: print(d.state_msg()) - elif opt.testing: + elif cfg.testing: for cmd in d.start_cmds if action == 'start' else [d.stop_cmd]: print(' '.join(cmd)) else: if action == 'stop' and hasattr(d,'rpc'): - async_run(d.rpc.stop_daemon(quiet=opt.quiet)) + async_run(d.rpc.stop_daemon(quiet=cfg.quiet)) else: - d.cmd(action,quiet=opt.quiet) + d.cmd(action,quiet=cfg.quiet) -if opt.daemon_ids: +if cfg.daemon_ids: print('\n'.join(CoinDaemon.all_daemon_ids())) -elif 'all' in cmd_args or 'no_xmr' in cmd_args: - if len(cmd_args) != 1: +elif 'all' in cfg._args or 'no_xmr' in cfg._args: + if len(cfg._args) != 1: die(1,"'all' or 'no_xmr' must be the sole argument") from mmgen.protocol import init_proto for coin in CoinDaemon.coins: - if coin == 'XMR' and cmd_args[0] == 'no_xmr': + if coin == 'XMR' and cfg._args[0] == 'no_xmr': continue - for daemon_id in CoinDaemon.get_daemon_ids(coin): - for network in CoinDaemon.get_daemon(coin,daemon_id).networks: + for daemon_id in CoinDaemon.get_daemon_ids(cfg,coin): + for network in CoinDaemon.get_daemon(cfg,coin,daemon_id).networks: run( - proto = init_proto(coin=coin,network=network), + proto = init_proto( cfg, coin=coin, network=network ), daemon_id = daemon_id, missing_exec_ok = True ) else: - ids = cmd_args - network_ids = CoinDaemon.get_network_ids() + ids = cfg._args + network_ids = CoinDaemon.get_network_ids(cfg) if not ids: opts.usage() for i in ids: diff --git a/test/include/common.py b/test/include/common.py index f2000be3..1b8e2e02 100755 --- a/test/include/common.py +++ b/test/include/common.py @@ -25,6 +25,33 @@ from subprocess import run,PIPE from mmgen.common import * from mmgen.fileutil import write_data_to_file,get_data_from_file +def noop(*args,**kwargs): + pass + +def set_globals(cfg): + """ + make `cfg`, `qmsg`, `vmsg`, etc. available as globals to scripts by setting + the module attr + """ + import test.include.common as this + this.cfg = cfg + + if cfg.quiet: + this.qmsg = this.qmsg_r = noop + else: + this.qmsg = msg + this.qmsg_r = msg_r + + if cfg.verbose: + this.vmsg = msg + this.vmsg_r = msg_r + this.Vmsg = Msg + this.Vmsg_r = Msg_r + else: + this.vmsg = this.vmsg_r = this.Vmsg = this.Vmsg_r = noop + + this.dmsg = msg if cfg.debug else noop + def strip_ansi_escapes(s): import re return re.sub('\x1b' + r'\[[;0-9]+?m','',s) @@ -65,7 +92,7 @@ ref_kafile_pass = 'kafile password' ref_kafile_hash_preset = '1' def getrand(n): - if g.test_suite_deterministic: + if cfg.test_suite_deterministic: from mmgen.test import fake_urandom return fake_urandom(n) else: @@ -120,6 +147,7 @@ def get_tmpfile(cfg,fn): def write_to_file(fn,data,binary=False): write_data_to_file( + cfg, fn, data, quiet = True, @@ -130,7 +158,7 @@ def write_to_tmpfile(cfg,fn,data,binary=False): write_to_file( os.path.join(cfg['tmpdir'],fn), data=data, binary=binary ) def read_from_file(fn,binary=False): - return get_data_from_file(fn,quiet=True,binary=binary) + return get_data_from_file( cfg, fn, quiet=True, binary=binary ) def read_from_tmpfile(cfg,fn,binary=False): return read_from_file(os.path.join(cfg['tmpdir'],fn),binary=binary) @@ -139,9 +167,9 @@ def joinpath(*args,**kwargs): return os.path.join(*args,**kwargs) def ok(): - if opt.profile: + if cfg.profile: return - if opt.verbose or opt.exact_output: + if cfg.verbose or cfg.exact_output: gmsg('OK') else: msg(' OK') @@ -161,11 +189,11 @@ def init_coverage(): return coverdir,acc_file def silence(): - if not (opt.verbose or opt.exact_output): + if not (cfg.verbose or cfg.exact_output): gv.stdout = gv.stderr = open(os.devnull,'w') def end_silence(): - if not (opt.verbose or opt.exact_output): + if not (cfg.verbose or cfg.exact_output): gv.stdout.close() gv.stdout = sys.stdout gv.stderr = sys.stderr @@ -177,38 +205,38 @@ def omsg_r(s): sys.stderr.flush() def imsg(s): - if opt.verbose or opt.exact_output: + if cfg.verbose or cfg.exact_output: omsg(s) def imsg_r(s): - if opt.verbose or opt.exact_output: + if cfg.verbose or cfg.exact_output: omsg_r(s) def iqmsg(s): - if not opt.quiet: + if not cfg.quiet: omsg(s) def iqmsg_r(s): - if not opt.quiet: + if not cfg.quiet: omsg_r(s) def oqmsg(s): - if not (opt.verbose or opt.exact_output): + if not (cfg.verbose or cfg.exact_output): omsg(s) def oqmsg_r(s): - if not (opt.verbose or opt.exact_output): + if not (cfg.verbose or cfg.exact_output): omsg_r(s) def end_msg(t): omsg(green( 'All requested tests finished OK' + - ('' if g.test_suite_deterministic else f', elapsed time: {t//60:02d}:{t%60:02d}') + ('' if cfg.test_suite_deterministic else f', elapsed time: {t//60:02d}:{t%60:02d}') )) def start_test_daemons(*network_ids,remove_datadir=False): - if not opt.no_daemon_autostart: + if not cfg.no_daemon_autostart: return test_daemons_ops(*network_ids,op='start',remove_datadir=remove_datadir) def stop_test_daemons(*network_ids,force=False,remove_datadir=False): - if force or not opt.no_daemon_stop: + if force or not cfg.no_daemon_stop: return test_daemons_ops(*network_ids,op='stop',remove_datadir=remove_datadir) def restart_test_daemons(*network_ids,remove_datadir=False): @@ -217,12 +245,12 @@ def restart_test_daemons(*network_ids,remove_datadir=False): return start_test_daemons(*network_ids,remove_datadir=remove_datadir) def test_daemons_ops(*network_ids,op,remove_datadir=False): - if not opt.no_daemon_autostart: + if not cfg.no_daemon_autostart: from mmgen.daemon import CoinDaemon - silent = not (opt.verbose or opt.exact_output) + silent = not (cfg.verbose or cfg.exact_output) ret = False for network_id in network_ids: - d = CoinDaemon(network_id,test_suite=True) + d = CoinDaemon(cfg,network_id,test_suite=True) if remove_datadir: d.stop(silent=True) d.remove_datadir() diff --git a/test/include/pexpect.py b/test/include/pexpect.py index d2506958..110e8742 100755 --- a/test/include/pexpect.py +++ b/test/include/pexpect.py @@ -21,10 +21,8 @@ test.include.pexpect: pexpect implementation for MMGen test suites """ import sys,os,time -from mmgen.globalvars import g -from mmgen.opts import opt -from mmgen.util import msg,msg_r,vmsg,vmsg_r,rmsg,red,yellow,green,cyan,die -from .common import * +from mmgen.util import msg,msg_r,rmsg,red,yellow,green,cyan,die +from .common import cfg,vmsg,vmsg_r,getrandstr,strip_ansi_escapes try: import pexpect @@ -48,18 +46,18 @@ class MMGenPexpect: self.skip_ok = False self.sent_value = None - if opt.direct_exec: + if cfg.direct_exec: msg('') from subprocess import run,DEVNULL run([args[0]] + args[1:],check=True,stdout=DEVNULL if no_output else None) else: - timeout = int(timeout or opt.pexpect_timeout or 0) or (60,5)[bool(opt.debug_pexpect)] + timeout = int(timeout or cfg.pexpect_timeout or 0) or (60,5)[bool(cfg.debug_pexpect)] if pexpect_spawn: self.p = pexpect.spawn(args[0],args[1:],encoding='utf8',timeout=timeout,env=env) else: self.p = PopenSpawn(args,encoding='utf8',timeout=timeout,env=env) - if opt.exact_output: + if cfg.exact_output: self.p.logfile = sys.stdout def do_decrypt_ka_data(self,hp,pw,desc='key-address data',check=True,have_yes_opt=False): @@ -84,13 +82,13 @@ class MMGenPexpect: self.p.sendeof() self.p.read() ret = self.p.wait() - if ret != self.req_exit_val and not opt.coverage: + if ret != self.req_exit_val and not cfg.coverage: die(1,red(f'test.py: spawned program exited with value {ret}')) - if opt.profile: + if cfg.profile: return if not self.skip_ok: m = 'OK\n' if ret == 0 else f'OK[{ret}]\n' - sys.stderr.write( green(m) if opt.exact_output or opt.verbose else ' '+m ) + sys.stderr.write( green(m) if cfg.exact_output or cfg.verbose else ' '+m ) return self def license(self): @@ -101,7 +99,7 @@ class MMGenPexpect: self.expect('Enter a wallet label, or hit ENTER for no label: ',label+'\n') def usr_rand(self,num_chars): - if opt.usr_random: + if cfg.usr_random: self.interactive() self.send('\n') else: @@ -109,7 +107,7 @@ class MMGenPexpect: vmsg_r('SEND ') while rand_chars: ch = rand_chars.pop(0) - msg_r(yellow(ch)+' ' if opt.verbose else '+') + msg_r(yellow(ch)+' ' if cfg.verbose else '+') ret = self.expect('left: ',ch,delay=0.005) self.expect('ENTER to continue: ','\n') @@ -134,7 +132,7 @@ class MMGenPexpect: return self.expect_getend("Overwriting file '").rstrip("'") self.expect(NL,nonl=True) outfile = self.p.before.strip().strip("'") - if opt.debug_pexpect: + if cfg.debug_pexpect: rmsg(f'Outfile [{outfile}]') vmsg('{} file: {}'.format( desc, cyan(outfile.replace('"',"")) )) return outfile @@ -154,14 +152,14 @@ class MMGenPexpect: def expect_getend(self,s,regex=False): ret = self.expect(s,regex=regex,nonl=True) - if opt.debug_pexpect: + if cfg.debug_pexpect: debug_pexpect_msg(self.p) # readline() of partial lines doesn't work with PopenSpawn, so do this instead: self.expect(NL,nonl=True,silent=True) - if opt.debug_pexpect: + if cfg.debug_pexpect: debug_pexpect_msg(self.p) end = self.p.before.rstrip() - if not g.debug: + if not cfg.debug: vmsg(f' ==> {cyan(end)}') return end @@ -186,25 +184,25 @@ class MMGenPexpect: def expect(self,s,t='',delay=None,regex=False,nonl=False,silent=False): if not silent: - if opt.verbose: + if cfg.verbose: msg_r('EXPECT ' + yellow(str(s))) - elif not opt.exact_output: + elif not cfg.exact_output: msg_r('+') try: ret = (self.p.expect_exact,self.p.expect)[bool(regex)](s) if s else 0 except pexpect.TIMEOUT: - if opt.debug_pexpect: + if cfg.debug_pexpect: raise m1 = f'\nERROR. Expect {s!r} timed out. Exiting\n' m2 = f'before: [{self.p.before}]\n' m3 = f'sent value: [{self.sent_value}]' if self.sent_value != None else '' raise pexpect.TIMEOUT(m1+m2+m3) - if opt.debug_pexpect: + if cfg.debug_pexpect: debug_pexpect_msg(self.p) - if opt.verbose and type(s) != str: + if cfg.verbose and type(s) != str: msg_r(f' ==> {ret} ') if ret == -1: @@ -223,10 +221,10 @@ class MMGenPexpect: time.sleep(delay) ret = self.p.send(t) # returns num bytes written self.sent_value = t if ret else None - if opt.demo and delay: + if cfg.demo and delay: time.sleep(delay) - if opt.verbose: - ls = '' if opt.debug or not s else ' ' + if cfg.verbose: + ls = '' if cfg.debug or not s else ' ' es = '' if s else ' ' yt = yellow('{!r}'.format( t.replace('\n',r'\n') )) msg(f'{ls}SEND {es}{yt}') diff --git a/test/misc/cfg.py b/test/misc/cfg.py index 38e8c7a6..ea44b900 100755 --- a/test/misc/cfg.py +++ b/test/misc/cfg.py @@ -2,13 +2,15 @@ from mmgen.common import * -cmd_args = opts.init() +cfg = opts.init() + +cmd_args = cfg._args from mmgen.cfgfile import mmgen_cfg_file -cf_usr = mmgen_cfg_file('usr') -cf_sys = mmgen_cfg_file('sys') -cf_sample = mmgen_cfg_file('sample') +cf_usr = mmgen_cfg_file(cfg,'usr') +cf_sys = mmgen_cfg_file(cfg,'sys') +cf_sample = mmgen_cfg_file(cfg,'sample') msg(f'Usr cfg file: {os.path.relpath(cf_usr.fn)}') msg(f'Sys cfg file: {os.path.relpath(cf_sys.fn)}') @@ -21,21 +23,19 @@ if cmd_args: pu = cf_usr.get_lines() msg('usr cfg: {}'.format( ' '.join(f'{i.name}={i.value}' for i in pu) )) elif cmd_args[0] == 'coin_specific_vars': - from mmgen.protocol import init_proto_from_opts - proto = init_proto_from_opts(need_amt=True) for varname in cmd_args[1:]: msg('{}.{}: {}'.format( - type(proto).__name__, + type(cfg._proto).__name__, varname, - getattr(proto,varname) + getattr(cfg._proto,varname) )) elif cmd_args[0] == 'autoset_opts': - assert opt.rpc_backend == 'aiohttp', "opt.rpc_backend != 'aiohttp'" + assert cfg.rpc_backend == 'aiohttp', "cfg.rpc_backend != 'aiohttp'" elif cmd_args[0] == 'autoset_opts_cmdline': - assert opt.rpc_backend == 'curl', "opt.rpc_backend != 'curl'" + assert cfg.rpc_backend == 'curl', "cfg.rpc_backend != 'curl'" elif cmd_args[0] == 'mnemonic_entry_modes': from mmgen.mn_entry import mn_entry msg('mnemonic_entry_modes: {}\nmmgen: {}\nbip39: {}'.format( - g.mnemonic_entry_modes, - mn_entry('mmgen').usr_dfl_entry_mode, - mn_entry('bip39').usr_dfl_entry_mode )) + cfg.mnemonic_entry_modes, + mn_entry(cfg,'mmgen').usr_dfl_entry_mode, + mn_entry(cfg,'bip39').usr_dfl_entry_mode )) diff --git a/test/misc/get_passphrase.py b/test/misc/get_passphrase.py index 28ae54fe..6563d851 100755 --- a/test/misc/get_passphrase.py +++ b/test/misc/get_passphrase.py @@ -6,9 +6,8 @@ os.chdir(os.path.dirname(os.path.dirname(pn))) sys.path[0] = os.curdir from mmgen.common import * -g.color = True -cmd_args = opts.init({ +cfg = opts.init({ 'text': { 'desc': '', 'usage': '', @@ -19,15 +18,13 @@ cmd_args = opts.init({ -L, --label=l d -m, --keep-label e """ - }}) - -from mmgen.wallet import Wallet + }},init_opts={'color':True}) def crypto(): desc = 'test data' from mmgen.crypto import Crypto - crypto = Crypto() + crypto = Crypto(cfg) pw = crypto.get_new_passphrase(data_desc=desc,hash_preset=gc.dfl_hash_preset,passwd_file=None) msg(f'==> got new passphrase: [{pw}]\n') @@ -44,16 +41,18 @@ def crypto(): def seed(): for n in range(1,3): msg(f'------- NEW WALLET {n} -------\n') - w1 = Wallet() + w1 = Wallet(cfg) msg(f'\n==> got pw,preset,lbl: [{w1.ssdata.passwd}][{w1.ssdata.hash_preset}][{w1.ssdata.label}]\n') for n in range(1,3): msg(f'------- PASSCHG {n} -------\n') - w2 = Wallet(ss=w1,passchg=True) + w2 = Wallet(cfg,ss=w1,passchg=True) msg(f'\n==> got pw,preset,lbl: [{w2.ssdata.passwd}][{w2.ssdata.hash_preset}][{w2.ssdata.label}]\n') msg(f'------- WALLET FROM FILE -------\n') - w3 = Wallet(fn='test/ref/FE3C6545-D782B529[128,1].mmdat') # passphrase: 'reference password' + w3 = Wallet(cfg,fn='test/ref/FE3C6545-D782B529[128,1].mmdat') # passphrase: 'reference password' msg(f'\n==> got pw,preset,lbl: [{w3.ssdata.passwd}][{w3.ssdata.hash_preset}][{w3.ssdata.label}]\n') -globals()[cmd_args[0]]() +from mmgen.wallet import Wallet + +globals()[cfg._args[0]]() diff --git a/test/misc/input_func.py b/test/misc/input_func.py index 38657ba7..7866be96 100755 --- a/test/misc/input_func.py +++ b/test/misc/input_func.py @@ -7,23 +7,30 @@ sys.path[0] = os.curdir from mmgen.common import * -cmd_args = opts.init({'text': { 'desc': '', 'usage':'', 'options':'-e, --echo-passphrase foo' }}) +cfg = opts.init({'text': { 'desc': '', 'usage':'', 'options':'-e, --echo-passphrase foo' }}) -if cmd_args[0] == 'passphrase': +cmd_args = cfg._args + +cmd = cmd_args[0] + +if cmd == 'passphrase': from mmgen.ui import get_words_from_user pw = get_words_from_user( - ('Enter passphrase: ','Enter passphrase (echoed): ')[bool(opt.echo_passphrase)] ) + cfg, + ('Enter passphrase: ','Enter passphrase (echoed): ')[bool(cfg.echo_passphrase)] ) msg('Entered: {}'.format(' '.join(pw))) -elif cmd_args[0] in ('get_char','line_input'): +elif cmd in ('get_char','line_input'): from mmgen.term import get_char from mmgen.ui import line_input from ast import literal_eval func_args = literal_eval(cmd_args[1]) Msg(f'\n term: {get_char.__self__.__name__}') - Msg(f' g.hold_protect_disable: {g.hold_protect_disable}') + Msg(f' cfg.hold_protect_disable: {cfg.hold_protect_disable}') + if cmd == 'line_input': + func_args.update({'cfg':cfg}) Msg(' {name}( {args} )'.format( - name = cmd_args[0], + name = cmd, args = ', '.join(f'{k}={v!r}' for k,v in func_args.items()) )) - ret = locals()[cmd_args[0]](**func_args) + ret = locals()[cmd](**func_args) Msg(' ==> {!r}'.format(ret)) diff --git a/test/misc/oneshot_warning.py b/test/misc/oneshot_warning.py index 727f3581..f70e2d03 100755 --- a/test/misc/oneshot_warning.py +++ b/test/misc/oneshot_warning.py @@ -2,7 +2,7 @@ from mmgen.common import * -cmd_args = opts.init() +cfg = opts.init() class foo(oneshot_warning): diff --git a/test/misc/opts.py b/test/misc/opts.py index 7a74012a..36bad3ca 100755 --- a/test/misc/opts.py +++ b/test/misc/opts.py @@ -37,38 +37,42 @@ sample note: {nn} """ }, 'code': { - 'options': lambda help_notes,s: s.format( + 'options': lambda cfg,help_notes,s: s.format( kgs=help_notes('keygen_backends'), coin_id=help_notes('coin_id'), - g=g, ), 'notes': lambda s: s.format(nn='a note'), } } -cmd_args = opts.init(opts_data) +cfg = opts.init(opts_data) -if cmd_args == ['show_common_opts_diff']: +if cfg._args == ['show_common_opts_diff']: from mmgen.opts import show_common_opts_diff - show_common_opts_diff() + show_common_opts_diff(cfg) sys.exit(0) for k in ( 'foo', # added opt 'print_checksum', # sets 'quiet' 'quiet','verbose', # init_opts, incompatible_opts - 'fee_estimate_mode', # autoset_opts 'passwd_file', # infile_opts - check_infile() 'outdir', # check_outdir() 'cached_balances', # opt_sets_global 'minconf', # global_sets_opt 'hidden_incog_input_params', ): - msg('{:30} {}'.format( f'opt.{k}:', getattr(opt,k) )) + msg('{:30} {}'.format( f'cfg.{k}:', getattr(cfg,k) )) msg('') for k in ( 'cached_balances', # opt_sets_global 'minconf', # global_sets_opt ): - msg('{:30} {}'.format( f'g.{k}:', getattr(opt,k) )) + msg('{:30} {}'.format( f'cfg.{k}:', getattr(cfg,k) )) + +msg('') +for k in ( + 'fee_estimate_mode', # autoset_opts + ): + msg('{:30} {}'.format( f'cfg.{k}:', getattr(cfg,k) )) diff --git a/test/misc/term.py b/test/misc/term.py index d27c814c..7a6a10d4 100755 --- a/test/misc/term.py +++ b/test/misc/term.py @@ -44,7 +44,7 @@ available commands for platform {gc.platform!r}: } } -cmd_args = opts.init(opts_data) +cfg = opts.init(opts_data) from mmgen.term import get_char,get_char_raw,get_terminal_size,get_term from mmgen.ui import line_input,keypress_confirm,do_license_msg @@ -54,8 +54,8 @@ def cmsg(m): msg('\n'+cyan(m)) def confirm(m): - if not keypress_confirm(m): - if keypress_confirm('Are you sure you want to exit test?'): + if not keypress_confirm( cfg, m ): + if keypress_confirm( cfg, 'Are you sure you want to exit test?' ): die(1,'Exiting test at user request') else: msg('Continuing...') @@ -80,7 +80,7 @@ def tt_color(): def tt_license(): cmsg('Testing do_license_msg() with pager') ymsg('Press "w" to test the pager, then "c" to continue') - do_license_msg() + do_license_msg(cfg) def tt_line_input(): set_vt100() @@ -92,7 +92,7 @@ def tt_line_input(): on screen or entered text. """)) get_char_raw('Ready? ',num_bytes=1) - reply = line_input('\nEnter text: ') + reply = line_input( cfg, '\nEnter text: ' ) confirm(f'Did you enter the text {reply!r}?') def _tt_get_char(raw=False,one_char=False,immed_chars=''): @@ -145,7 +145,7 @@ def _tt_get_char(raw=False,one_char=False,immed_chars=''): def tt_urand(): cmsg('Testing _get_random_data_from_user():') from mmgen.crypto import Crypto - ret = Crypto()._get_random_data_from_user(uchars=10,desc='data').decode() + ret = Crypto(cfg)._get_random_data_from_user(uchars=10,desc='data').decode() msg(f'USER ENTROPY (user input + keystroke timings):\n\n{fmt(ret," ")}') times = ret.splitlines()[1:] avg_prec = sum(len(t.split('.')[1]) for t in times) // len(times) @@ -153,7 +153,7 @@ def tt_urand(): ymsg(f'WARNING: Avg. time precision of only {avg_prec} decimal points. User entropy quality is degraded!') else: msg(f'Average time precision: {avg_prec} decimal points - OK') - line_input('Press ENTER to continue: ') + line_input( cfg, 'Press ENTER to continue: ' ) def tt_txview(): cmsg('Testing tx.info.view_with_prompt() (try each viewing option)') @@ -163,7 +163,7 @@ def tt_txview(): while True: tx.info.view_with_prompt('View data for transaction?',pause=False) set_vt100() - if not keypress_confirm('Continue testing transaction view?',default_yes=True): + if not keypress_confirm( cfg, 'Continue testing transaction view?', default_yes=True ): break def tt_get_char_one(): @@ -186,8 +186,8 @@ def tt_get_char_one_char_immed_chars(): get_term().register_cleanup() -if cmd_args: - locals()['tt_'+cmd_args[0]]() +if cfg._args: + locals()['tt_'+cfg._args[0]]() else: for command in commands: locals()['tt_'+command]() diff --git a/test/misc/term_ni.py b/test/misc/term_ni.py index 92fdf7fd..b412d7b3 100755 --- a/test/misc/term_ni.py +++ b/test/misc/term_ni.py @@ -6,27 +6,27 @@ sys.path[0] = os.curdir from mmgen.common import * -cmd_args = opts.init() +cfg = opts.init() from mmgen.term import get_term,get_char_raw term = get_term() -if cmd_args[0] == 'echo': +if cfg._args[0] == 'echo': from mmgen.ui import line_input term.init(noecho=True) - line_input('noecho> ') + line_input( cfg, 'noecho> ' ) get_char_raw() term.set('echo') - line_input('echo> ') + line_input( cfg, 'echo> ' ) term.set('noecho') - line_input('noecho> ') + line_input( cfg, 'noecho> ' ) get_char_raw() -elif cmd_args[0] == 'cleanup': +elif cfg._args[0] == 'cleanup': term.register_cleanup() diff --git a/test/misc/tool_api_test.py b/test/misc/tool_api_test.py index 44b9fe89..e10911c3 100755 --- a/test/misc/tool_api_test.py +++ b/test/misc/tool_api_test.py @@ -8,7 +8,6 @@ test.misc.tool_api_test: test the MMGen suite tool API """ import sys,os -from mmgen.common import * from mmgen.key import PrivKey from mmgen.addr import CoinAddr @@ -64,7 +63,7 @@ def test_triplet(tool,coin,network,addrtype,key_idx,wif_chk,addr_chk): def run_test(): from mmgen.tool.api import tool_api - tool = tool_api() + tool = tool_api(cfg) tool.coins tool.print_addrtypes() @@ -110,4 +109,8 @@ def run_test(): 'SKxuS56e99jpCeD9mMQ5o63zoGPakNdM9HCvt4Vt2cypvRjCdvGJ', 'zchFELwBxqsAubsLQ8yZgPCDDGukjXJssgCbiTPwFNmFwn9haLnDatzfhLdZzJT4PcU4o2yr92B52UFirUzEdF6ZYM2gBkM' ) +from mmgen.opts import init + +cfg = init() + run_test() diff --git a/test/misc/utf8_output.py b/test/misc/utf8_output.py index 43c15963..887e501b 100755 --- a/test/misc/utf8_output.py +++ b/test/misc/utf8_output.py @@ -2,7 +2,7 @@ from mmgen.common import * -cmd_args = opts.init() +cfg = opts.init() from mmgen.util import msg @@ -13,7 +13,7 @@ text = { 'jp': 'Japanese text: {}'.format('必要なのは、信用ではなく暗号化された証明に基づく電子取引システムであり、') } -if not cmd_args or not cmd_args[0] in text: +if not cfg._args or not cfg._args[0] in text: die(2,'argument must be one of {}'.format(list(text.keys()))) -msg(text[cmd_args[0]]) +msg(text[cfg._args[0]]) diff --git a/test/objattrtest.py b/test/objattrtest.py index 26fc3116..eff3480b 100755 --- a/test/objattrtest.py +++ b/test/objattrtest.py @@ -30,7 +30,6 @@ sys.path.__setitem__(0,os.path.abspath(os.curdir)) 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 * @@ -55,7 +54,12 @@ opts_data = { } } -cmd_args = opts.init(opts_data) +cfg = opts.init(opts_data) + +from test.include.common import set_globals +set_globals(cfg) + +from test.objattrtest_py_d.oat_common import * pd = namedtuple('permission_bits', ['read_ok','delete_ok','reassign_ok']) @@ -120,10 +124,10 @@ def test_attr(data,obj,attrname,dobj,bits,attrval_type): if d[k] != bits[k]: fs = 'init value {iv}={a} for attr {n!r} does not match test data ({iv}={b})' die(4,fs.format(iv=k,n=attrname,a=d[k],b=bits[k])) - if opt.verbose and d[k] == True: + if cfg.verbose and d[k] == True: msg_r(f' {k}={d[k]!r}') - if opt.show_nonstandard_init: + if cfg.show_nonstandard_init: for k,v in (('typeconv',False),('set_none_ok',True)): if d[k] == v: msg_r(f' {k}={v}') @@ -136,19 +140,19 @@ def test_object(test_data,objname): else: cls = globals()[objname] - fs = 'Testing attribute ' + ('{!r:<15}{dt:13}' if opt.show_descriptor_type else '{!r}') + fs = 'Testing attribute ' + ('{!r:<15}{dt:13}' if cfg.show_descriptor_type else '{!r}') data = test_data[objname] obj = cls(*data.args,**data.kwargs) for attrname,adata in data.attrs.items(): dobj = get_descriptor_obj(type(obj),attrname) - if opt.verbose: + if cfg.verbose: msg_r(fs.format(attrname,dt=type(dobj).__name__.replace('MMGen',''))) bits = parse_permbits(adata[0]) test_attr(data,obj,attrname,dobj,bits,adata[1]) for perm_name,perm_value in bits._asdict().items(): test_attr_perm(obj,attrname,perm_name,perm_value,dobj,adata[1]) - vmsg('') + cfg._util.vmsg('') def do_loop(): import importlib @@ -156,12 +160,12 @@ def do_loop(): test_data = importlib.import_module(modname).tests gmsg(f'Running immutable attribute tests for {proto.coin} {proto.network}') - utests = cmd_args + utests = cfg._args for obj in test_data: if utests and obj not in utests: continue - msg((blue if opt.verbose else nocolor)(f'Testing {obj}')) + msg((blue if cfg.verbose else nocolor)(f'Testing {obj}')) test_object(test_data,obj) -from mmgen.protocol import init_proto_from_opts -proto = init_proto_from_opts(need_amt=True) +proto = cfg._proto + do_loop() diff --git a/test/objattrtest_py_d/oat_btc_mainnet.py b/test/objattrtest_py_d/oat_btc_mainnet.py index 0823509f..753d185b 100755 --- a/test/objattrtest_py_d/oat_btc_mainnet.py +++ b/test/objattrtest_py_d/oat_btc_mainnet.py @@ -8,10 +8,11 @@ test.objattrtest_py_d.oat_btc_mainnet: BTC mainnet test vectors for MMGen data o """ from .oat_common import * +from ..include.common import cfg from mmgen.protocol import init_proto from mmgen.amt import BTCAmt -proto = init_proto('btc',need_amt=True) +proto = init_proto( cfg, 'btc', need_amt=True ) sample_objs.update({ 'PrivKey': PrivKey(proto,seed_bin,compressed=True,pubkey_type='std'), @@ -70,7 +71,7 @@ tests = { 'data': (0b001, bytes), 'sid': (0b001, SeedID), }, - [seed_bin], + [cfg,seed_bin], {}, ), 'SubSeed': atd({ @@ -105,7 +106,7 @@ tests = { 'id_str': (0b001, SeedSplitIDString), 'count': (0b001, SeedShareCount), }, - [sample_objs['MasterShareIdx'], sample_objs['Seed'], 'foo', 2], + [cfg,sample_objs['MasterShareIdx'], sample_objs['Seed'], 'foo', 2], {}, ), # twuo.py diff --git a/test/objattrtest_py_d/oat_common.py b/test/objattrtest_py_d/oat_common.py index a4d74329..0ac0e359 100755 --- a/test/objattrtest_py_d/oat_common.py +++ b/test/objattrtest_py_d/oat_common.py @@ -17,7 +17,7 @@ from mmgen.addr import * from mmgen.tx import * from mmgen.tw.unspent import * from mmgen.key import * -from ..include.common import getrand +from ..include.common import cfg,getrand from collections import namedtuple atd = namedtuple('attrtest_entry',['attrs','args','kwargs']) @@ -43,13 +43,13 @@ sample_objs = { 'CoinTxID': CoinTxID('aa'*32), 'SeedID': SeedID(sid='F00F00BB'), - 'Seed': Seed(seed_bin=seed_bin), + 'Seed': Seed(cfg,seed_bin=seed_bin), - 'SubSeedList': SubSeedList(Seed(seed_bin=seed_bin)), + 'SubSeedList': SubSeedList(Seed(cfg,seed_bin=seed_bin)), 'SubSeedIdx': SubSeedIdx('1S'), 'SeedSplitIDString': SeedSplitIDString('alice'), - 'SeedShareList': SeedShareList(Seed(seed_bin=seed_bin),SeedShareCount(2)), + 'SeedShareList': SeedShareList(Seed(cfg,seed_bin=seed_bin),SeedShareCount(2)), 'SeedShareIdx': SeedShareIdx(1), 'SeedShareCount': SeedShareCount(2), 'MasterShareIdx': MasterShareIdx(7), diff --git a/test/objtest.py b/test/objtest.py index 077e9946..745d67b0 100755 --- a/test/objtest.py +++ b/test/objtest.py @@ -60,7 +60,10 @@ opts_data = { } } -cmd_args = opts.init(opts_data) +cfg = opts.init(opts_data) + +from test.include.common import set_globals +set_globals(cfg) def run_test(test,arg,input_data,arg1,exc_name): arg_copy = arg @@ -96,22 +99,22 @@ def run_test(test,arg,input_data,arg1,exc_name): else: args = [arg] - if opt.getobj: + if cfg.getobj: if args: assert len(args) == 1, 'objtest_chk1: only one positional arg is allowed' kwargs.update( { arg1: args[0] } ) - if opt.silent: + if cfg.silent: kwargs.update( { 'silent': True } ) try: - if not opt.super_silent: + if not cfg.super_silent: arg_disp = repr(arg_copy[0] if type(arg_copy) == tuple else arg_copy) - if g.test_suite_deterministic and isinstance(arg_copy,dict): + if cfg.test_suite_deterministic and isinstance(arg_copy,dict): arg_disp = re.sub(r'object at 0x[0-9a-f]+','object at [SCRUBBED]',arg_disp) msg_r((green if input_data=='good' else orange)(f'{arg_disp+":":<22}')) cls = globals()[test] - if opt.getobj: + if cfg.getobj: ret = get_obj(globals()[test],**kwargs) else: ret = cls(*args,**kwargs) @@ -121,14 +124,14 @@ def run_test(test,arg,input_data,arg1,exc_name): if isinstance(ret_chk,str): ret_chk = ret_chk.encode() if isinstance(ret,str): ret = ret.encode() - if opt.getobj: + if cfg.getobj: if input_data == 'bad': assert ret == False, 'non-False return on bad input data' else: - if (opt.silent and input_data=='bad' and ret!=bad_ret) or (not opt.silent and input_data=='bad'): + if (cfg.silent and input_data=='bad' and ret!=bad_ret) or (not cfg.silent and input_data=='bad'): raise UserWarning(f"Non-'None' return value {ret!r} with bad input data") - if opt.silent and input_data=='good' and ret==bad_ret: + if cfg.silent and input_data=='good' and ret==bad_ret: raise UserWarning("'None' returned with good input data") if input_data=='good': @@ -137,17 +140,17 @@ def run_test(test,arg,input_data,arg1,exc_name): if ret != ret_chk and repr(ret) != repr(ret_chk): raise UserWarning(f"Return value ({ret!r}) doesn't match expected value ({ret_chk!r})") - if opt.super_silent: + if cfg.super_silent: return - if opt.getobj and (not opt.silent and input_data == 'bad'): + if cfg.getobj and (not cfg.silent and input_data == 'bad'): pass else: try: ret_disp = ret.decode() except: ret_disp = ret msg(f'==> {ret_disp!r}') - if opt.verbose and issubclass(cls,MMGenObject): + if cfg.verbose and issubclass(cls,MMGenObject): ret.pmsg() if hasattr(ret,'pmsg') else pmsg(ret) except Exception as e: @@ -156,16 +159,16 @@ def run_test(test,arg,input_data,arg1,exc_name): if not type(e).__name__ == exc_name: msg(f'Incorrect exception: expected {exc_name} but got {type(e).__name__}') raise - if opt.super_silent: + if cfg.super_silent: pass - elif opt.silent: + elif cfg.silent: msg(f'==> {exc_name}') else: msg( yellow(f' {exc_name}:') + str(e) ) except SystemExit as e: if input_data == 'good': raise ValueError('Error on good input data') - if opt.verbose: + if cfg.verbose: msg(f'exitval: {e.code}') except UserWarning as e: msg(f'==> {ret!r}') @@ -178,21 +181,21 @@ def do_loop(): gmsg(f'Running data object tests for {proto.coin} {proto.network}') clr = None - utests = cmd_args + utests = cfg._args for test in test_data: arg1 = test_data[test].get('arg1') if utests and test not in utests: continue - nl = ('\n','')[bool(opt.super_silent) or clr == None] - clr = (blue,nocolor)[bool(opt.super_silent)] + nl = ('\n','')[bool(cfg.super_silent) or clr == None] + clr = (blue,nocolor)[bool(cfg.super_silent)] - if opt.getobj and arg1 is None: + if cfg.getobj and arg1 is None: msg(gray(f'{nl}Skipping {test}')) continue else: msg(clr(f'{nl}Testing {test}')) for k in ('bad','good'): - if not opt.super_silent: + if not cfg.super_silent: msg(purple(capfirst(k)+' input:')) for arg in test_data[test][k]: run_test( @@ -203,6 +206,6 @@ def do_loop(): exc_name = test_data[test].get('exc_name') or ('ObjectInitError','None')[k=='good'], ) -from mmgen.protocol import init_proto_from_opts -proto = init_proto_from_opts(need_amt=True) +proto = cfg._proto + do_loop() diff --git a/test/objtest_py_d/ot_btc_mainnet.py b/test/objtest_py_d/ot_btc_mainnet.py index 224db7fe..6001bec4 100755 --- a/test/objtest_py_d/ot_btc_mainnet.py +++ b/test/objtest_py_d/ot_btc_mainnet.py @@ -14,9 +14,10 @@ from mmgen.addrlist import AddrIdxList from mmgen.seedsplit import * from mmgen.key import * from .ot_common import * +from ..include.common import cfg from mmgen.protocol import init_proto -proto = init_proto('btc',need_amt=True) +proto = init_proto( cfg, 'btc', need_amt=True ) tw_pfx = proto.base_coin.lower() + ':' zero_addr = '1111111111111111111114oLvT2' @@ -123,8 +124,8 @@ tests = { ), 'good': ( {'sid':'F00BAA12'}, - {'seed': Seed(r16), 'ret': SeedID(seed=Seed(r16))}, - {'sid': Seed(r16).sid, 'ret': SeedID(seed=Seed(r16))} + {'seed': Seed(cfg,r16), 'ret': SeedID(seed=Seed(cfg,r16))}, + {'sid': Seed(cfg,r16).sid, 'ret': SeedID(seed=Seed(cfg,r16))} ) }, 'SubSeedIdx': { diff --git a/test/objtest_py_d/ot_btc_testnet.py b/test/objtest_py_d/ot_btc_testnet.py index 02a354f2..a6114b49 100755 --- a/test/objtest_py_d/ot_btc_testnet.py +++ b/test/objtest_py_d/ot_btc_testnet.py @@ -9,9 +9,10 @@ test.objtest_py_d.ot_btc_testnet: BTC testnet test vectors for MMGen data object from mmgen.obj import * from .ot_common import * +from ..include.common import cfg from mmgen.protocol import init_proto -proto = init_proto('btc',network='testnet',need_amt=True) +proto = init_proto( cfg, 'btc', network='testnet', need_amt=True ) tests = { 'CoinAddr': { diff --git a/test/objtest_py_d/ot_common.py b/test/objtest_py_d/ot_common.py index d2935070..cfb3f73c 100755 --- a/test/objtest_py_d/ot_common.py +++ b/test/objtest_py_d/ot_common.py @@ -8,7 +8,6 @@ test.objtest_py_d.ot_common: shared data for MMGen data objects tests """ import os -from mmgen.globalvars import g from ..include.common import * r32,r24,r16,r17,r18 = getrand(32),getrand(24),getrand(16),getrand(17),getrand(18) diff --git a/test/objtest_py_d/ot_ltc_mainnet.py b/test/objtest_py_d/ot_ltc_mainnet.py index ada8cebf..ee82be9d 100755 --- a/test/objtest_py_d/ot_ltc_mainnet.py +++ b/test/objtest_py_d/ot_ltc_mainnet.py @@ -11,9 +11,10 @@ from decimal import Decimal from mmgen.obj import * from .ot_common import * +from ..include.common import cfg from mmgen.protocol import init_proto -proto = init_proto('ltc',need_amt=True) +proto = init_proto( cfg, 'ltc', need_amt=True ) tests = { 'LTCAmt': { diff --git a/test/objtest_py_d/ot_ltc_testnet.py b/test/objtest_py_d/ot_ltc_testnet.py index 15668da8..99979962 100755 --- a/test/objtest_py_d/ot_ltc_testnet.py +++ b/test/objtest_py_d/ot_ltc_testnet.py @@ -9,9 +9,10 @@ test.objtest_py_d.ot_ltc_testnet: LTC testnet test vectors for MMGen data object from mmgen.obj import * from .ot_common import * +from ..include.common import cfg from mmgen.protocol import init_proto -proto = init_proto('ltc',network='testnet',need_amt=True) +proto = init_proto( cfg, 'ltc', network='testnet', need_amt=True ) tests = { 'CoinAddr': { diff --git a/test/overlay/fakemods/mmgen/proto/btc/tw/unspent.py b/test/overlay/fakemods/mmgen/proto/btc/tw/unspent.py index f69d68b2..3e6f8ab8 100644 --- a/test/overlay/fakemods/mmgen/proto/btc/tw/unspent.py +++ b/test/overlay/fakemods/mmgen/proto/btc/tw/unspent.py @@ -10,6 +10,7 @@ if overlay_fake_os.getenv('MMGEN_BOGUS_UNSPENT_DATA'): import json from ....fileutil import get_data_from_file return json.loads(get_data_from_file( + self.cfg, overlay_fake_os.getenv('MMGEN_BOGUS_UNSPENT_DATA') ), parse_float=Decimal) diff --git a/test/scrambletest.py b/test/scrambletest.py index c1633819..8670e46b 100755 --- a/test/scrambletest.py +++ b/test/scrambletest.py @@ -21,12 +21,13 @@ test/scrambletest.py: seed scrambling and addrlist data generation tests for all supported coins + passwords """ -import sys,os +import sys,os,time from subprocess import run,PIPE from include.tests_header import repo_root -from mmgen.common import * -from test.include.common import * +import mmgen.opts as opts +from mmgen.globalvars import gc +from mmgen.util import msg,msg_r,bmsg opts_data = { 'text': { @@ -49,10 +50,14 @@ If no command is given, the whole suite of tests is run. } } -cmd_args = opts.init(opts_data) +cfg = opts.init(opts_data) + +from test.include.common import set_globals,end_msg,green + +set_globals(cfg) os.environ['MMGEN_DEBUG_ADDRLIST'] = '1' -if not opt.system: +if not cfg.system: os.environ['PYTHONPATH'] = repo_root from collections import namedtuple @@ -95,7 +100,7 @@ passwd_data = { 'xmrseed_dfl_αω':td('62f5b72a5ca89cab', 'xmrseed:25:αω','-αω-xmrseed-25','αω xmrseed:25','tequila eden skulls giving jester hospital dreams bakery adjust nanny cactus inwardly films amply nanny soggy vials muppet yellow woken ashtray organs exhale foes eden'), } -cvr_opts = ' -m trace --count --coverdir={} --file={}'.format( *init_coverage() ) if opt.coverage else '' +cvr_opts = ' -m trace --count --coverdir={} --file={}'.format( *init_coverage() ) if cfg.coverage else '' cmd_base = f'python3{cvr_opts} cmds/mmgen-{{}}gen -qS' def get_cmd_output(cmd): @@ -105,7 +110,7 @@ def get_cmd_output(cmd): return cp.stdout.decode().splitlines() def do_test(cmd,tdata,msg_str,addr_desc): - vmsg(green(f'Executing: {cmd}')) + cfg._util.vmsg(green(f'Executing: {cmd}')) msg_r('Testing: ' + msg_str) lines = get_cmd_output(cmd) @@ -113,11 +118,11 @@ def do_test(cmd,tdata,msg_str,addr_desc): cmd_out['addr'] = lines[-2].split(None,1)[-1] ref_data = tdata._asdict() - vmsg('') + cfg._util.vmsg('') for k in ref_data: if cmd_out[k] == ref_data[k]: s = k.replace('seed','seed[:8]').replace('addr',addr_desc) - vmsg(f' {s:9}: {cmd_out[k]}') + cfg._util.vmsg(f' {s:9}: {cmd_out[k]}') else: die(4,f'\nError: sc_{k} value {cmd_out[k]} does not match reference value {ref_data[k]}') msg('OK') @@ -126,7 +131,7 @@ def do_coin_tests(): bmsg('Testing address scramble strings and list IDs') for tname,tdata in ( tuple(bitcoin_data.items()) + - tuple(altcoin_data.items() if not opt.no_altcoin else []) ): + tuple(altcoin_data.items() if not cfg.no_altcoin else []) ): if tname == 'zec_zcash_z' and gc.platform == 'win': msg("Skipping 'zec_zcash_z' test for Windows platform") continue @@ -148,7 +153,7 @@ def do_passwd_tests(): start_time = int(time.time()) -cmds = cmd_args or ('coin','pw') +cmds = cfg._args or ('coin','pw') for cmd in cmds: {'coin': do_coin_tests, 'pw': do_passwd_tests }[cmd]() diff --git a/test/test.py b/test/test.py index 09fccc78..d6e89c64 100755 --- a/test/test.py +++ b/test/test.py @@ -22,7 +22,7 @@ test/test.py: Test suite for the MMGen wallet system def check_segwit_opts(): for k,m in (('segwit','S'),('segwit_random','S'),('bech32','B')): - if getattr(opt,k) and m not in proto.mmtypes: + if getattr(cfg,k) and m not in proto.mmtypes: die(1,f'--{k.replace("_","-")} option incompatible with {proto.cls_name}') def create_shm_dir(data_dir,trash_dir): @@ -92,7 +92,6 @@ try: except: pass -g.quiet = False # if 'quiet' was set in config file, disable here os.environ['MMGEN_QUIET'] = '0' # for this script and spawned scripts opts_data = { @@ -170,20 +169,23 @@ environment var # we need some opt values before running opts.init, so parse without initializing: po = opts.init(opts_data,parse_only=True) -from test.include.common import * -from test.test_py_d.common import * +from test.include.common import set_globals,get_test_data_dir data_dir = get_test_data_dir() # include/common.py # step 1: delete data_dir symlink in ./test; -opt.resuming = any(k in po.user_opts for k in ('resume','resume_after')) -opt.skipping_deps = opt.resuming or 'skip_deps' in po.user_opts - -if not opt.skipping_deps: +if not po.user_opts.get('skipping_deps'): try: os.unlink(data_dir) except: pass -opts.UserOpts._reset_ok += ( +# step 2: opts.init will create new data_dir in ./test (if not cfg.skipping_deps) +cfg = opts.init(opts_data) + +set_globals(cfg) + +from test.test_py_d.common import * # this must be loaded after set_globals() + +type(cfg)._reset_ok += ( 'no_daemon_autostart', 'names', 'no_timings', @@ -191,45 +193,49 @@ opts.UserOpts._reset_ok += ( 'resuming', 'skipping_deps' ) -# step 2: opts.init will create new data_dir in ./test (if not opt.skipping_deps) -parsed_opts = opts.init(opts_data,return_parsed=True) -usr_args = parsed_opts.cmd_args +cfg.resuming = any(k in po.user_opts for k in ('resume','resume_after')) +cfg.skipping_deps = cfg.resuming or 'skip_deps' in po.user_opts -if opt.pexpect_spawn and gc.platform == 'win': +cmd_args = cfg._args + +if cfg.pexpect_spawn and gc.platform == 'win': die(1,'--pexpect-spawn option not supported on Windows platform, exiting') -if opt.daemon_id and opt.daemon_id in g.blacklisted_daemons.split(): - die(1,f'test.py: daemon {opt.daemon_id!r} blacklisted, exiting') +if cfg.daemon_id and cfg.daemon_id in cfg.blacklisted_daemons.split(): + die(1,f'test.py: daemon {cfg.daemon_id!r} blacklisted, exiting') -network_id = g.coin.lower() + ('_tn' if opt.testnet else '') +network_id = cfg.coin.lower() + ('_tn' if cfg.testnet else '') -from mmgen.protocol import init_proto_from_opts -proto = init_proto_from_opts() +proto = cfg._proto # step 3: move data_dir to /dev/shm and symlink it back to ./test: trash_dir = os.path.join('test','trash') -if not opt.skipping_deps: +if not cfg.skipping_deps: shm_dir = create_shm_dir(data_dir,trash_dir) check_segwit_opts() -testing_segwit = opt.segwit or opt.segwit_random or opt.bech32 +testing_segwit = cfg.segwit or cfg.segwit_random or cfg.bech32 -if g.test_suite_deterministic: - opt.no_timings = True +if cfg.test_suite_deterministic: + cfg.no_timings = True init_color(num_colors=0) os.environ['MMGEN_DISABLE_COLOR'] = '1' -if opt.profile: - opt.names = True +if cfg.profile: + cfg.names = True -if opt.exact_output: - def msg(s): pass - qmsg = qmsg_r = vmsg = vmsg_r = msg_r = msg +if cfg.exact_output: + def noop(s): + pass + qmsg = qmsg_r = msg = noop +else: + qmsg = cfg._util.qmsg + qmsg_r = cfg._util.qmsg_r -if opt.skipping_deps: - opt.no_daemon_autostart = True +if cfg.skipping_deps: + cfg.no_daemon_autostart = True from test.test_py_d.cfg import cfgs,fixup_cfgs @@ -280,16 +286,16 @@ def list_cmds(): sys.exit(0) def do_between(): - if opt.pause: + if cfg.pause: confirm_continue() - elif (opt.verbose or opt.exact_output) and not opt.skipping_deps: + elif (cfg.verbose or cfg.exact_output) and not cfg.skipping_deps: sys.stderr.write('\n') def list_tmpdirs(): return {k:cfgs[k]['tmpdir'] for k in cfgs} def clean(usr_dirs=None,clean_overlay=True): - if opt.skipping_deps: + if cfg.skipping_deps: return all_dirs = list_tmpdirs() dirnums = map(int,(usr_dirs if usr_dirs is not None else all_dirs)) @@ -335,11 +341,11 @@ def set_environ_for_spawned_scripts(): os.environ['MMGEN_COLUMNS'] = str(get_terminal_size().width) if os.getenv('MMGEN_DEBUG_ALL'): - for name in g.env_opts: + for name in cfg.env_opts: if name[:11] == 'MMGEN_DEBUG': os.environ[name] = '1' - if not opt.system: + if not cfg.system: os.environ['PYTHONPATH'] = repo_root os.environ['MMGEN_NO_LICENSE'] = '1' @@ -392,12 +398,12 @@ class CmdGroupMgr(object): if sg_name in (None,sg_key): for e in add_entries( sg_key, - add_deps = sg_name and not opt.skipping_deps, - added_subgroups = [sg_name] if opt.deps_only else [] ): + add_deps = sg_name and not cfg.skipping_deps, + added_subgroups = [sg_name] if cfg.deps_only else [] ): yield e - if opt.deps_only and sg_key == sg_name: + if cfg.deps_only and sg_key == sg_name: return - elif not opt.skipping_deps: + elif not cfg.skipping_deps: yield (name,data) return tuple(gen()) @@ -470,12 +476,12 @@ class CmdGroupMgr(object): for gname in self.cmd_groups: ginfo.append(( gname, self.get_cls_by_gname(gname) )) - if opt.list_current_cmd_groups: - exclude = (opt.exclude_groups or '').split(',') + if cfg.list_current_cmd_groups: + exclude = (cfg.exclude_groups or '').split(',') ginfo = [g for g in ginfo if network_id in g[1].networks and not g[0] in exclude - and g[0] in tuple(self.cmd_groups_dfl) + tuple(usr_args) ] + and g[0] in tuple(self.cmd_groups_dfl) + tuple(cmd_args) ] desc = 'CONFIGURED' else: desc = 'AVAILABLE' @@ -529,7 +535,7 @@ class TestSuiteRunner(object): 'test suite runner' def __del__(self): - if opt.log: + if cfg.log: self.log_fd.close() def __init__(self,data_dir,trash_dir): @@ -544,21 +550,21 @@ class TestSuiteRunner(object): self.resume_cmd = None self.deps_only = None - if opt.log: + if cfg.log: self.log_fd = open(log_file,'a') self.log_fd.write(f'\nLog started: {make_timestr()} UTC\n') omsg(f'INFO → Logging to file {log_file!r}') else: self.log_fd = None - if opt.coverage: + if cfg.coverage: coverdir,accfile = init_coverage() omsg(f'INFO → Writing coverage files to {coverdir!r}') self.pre_args = ['python3','-m','trace','--count','--coverdir='+coverdir,'--file='+accfile] else: self.pre_args = ['python3'] if gc.platform == 'win' else [] - if opt.pexpect_spawn: + if cfg.pexpect_spawn: omsg(f'INFO → Using pexpect.spawn() for real terminal emulation') def spawn_wrapper(self,cmd, @@ -572,12 +578,12 @@ class TestSuiteRunner(object): timeout = None, pexpect_spawn = None ): - desc = self.ts.test_name if opt.names else self.gm.dpy_data[self.ts.test_name][1] + desc = self.ts.test_name if cfg.names else self.gm.dpy_data[self.ts.test_name][1] if extra_desc: desc += ' ' + extra_desc cmd_path = ( - cmd if opt.system # opt.system is broken for main test group with overlay tree + cmd if cfg.system # cfg.system is broken for main test group with overlay tree else os.path.relpath(os.path.join(repo_root,cmd_dir,cmd)) ) args = ( @@ -591,7 +597,7 @@ class TestSuiteRunner(object): qargs = ['{q}{}{q}'.format( a, q = "'" if ' ' in a else '' ) for a in args] cmd_disp = ' '.join(qargs).replace('\\','/') # for mingw - if opt.log: + if cfg.log: self.log_fd.write('[{}][{}:{}] {}\n'.format( proto.coin.lower(), self.ts.group_name, @@ -605,11 +611,11 @@ class TestSuiteRunner(object): args )) if not no_msg: - t_pfx = '' if opt.no_timings else f'[{time.time() - self.start_time:08.2f}] ' - if opt.verbose or opt.print_cmdline or opt.exact_output: + t_pfx = '' if cfg.no_timings else f'[{time.time() - self.start_time:08.2f}] ' + if cfg.verbose or cfg.print_cmdline or cfg.exact_output: omsg(green(f'{t_pfx}Testing: {desc}')) if not msg_only: - clr1,clr2 = (nocolor,nocolor) if opt.print_cmdline else (green,cyan) + clr1,clr2 = (nocolor,nocolor) if cfg.print_cmdline else (green,cyan) omsg( clr1('Executing: ') + clr2(repr(cmd_disp) if gc.platform == 'win' else cmd_disp) @@ -623,8 +629,8 @@ class TestSuiteRunner(object): # NB: the `pexpect_spawn` arg enables hold_protect and send_delay while the corresponding cmdline # option does not. For performance reasons, this is the desired behavior. For full emulation of # the user experience with hold protect enabled, specify --buf-keypress or --demo. - send_delay = 0.4 if pexpect_spawn is True or opt.buf_keypress else None - pexpect_spawn = pexpect_spawn if pexpect_spawn is not None else bool(opt.pexpect_spawn) + send_delay = 0.4 if pexpect_spawn is True or cfg.buf_keypress else None + pexpect_spawn = pexpect_spawn if pexpect_spawn is not None else bool(cfg.pexpect_spawn) os.environ['MMGEN_HOLD_PROTECT_DISABLE'] = '' if send_delay else '1' os.environ['MMGEN_TEST_SUITE_POPEN_SPAWN'] = '' if pexpect_spawn else '1' @@ -649,7 +655,7 @@ class TestSuiteRunner(object): t = int(time.time() - self.start_time) sys.stderr.write(green( f'{self.cmd_total} test{suf(self.cmd_total)} performed' + - ('\n' if opt.no_timings else f'. Elapsed time: {t//60:02d}:{t%60:02d}\n') + ('\n' if cfg.no_timings else f'. Elapsed time: {t//60:02d}:{t%60:02d}\n') )) def init_group(self,gname,sg_name=None,cmd=None,quiet=False,do_clean=True): @@ -657,7 +663,7 @@ class TestSuiteRunner(object): ts_cls = CmdGroupMgr().load_mod(gname) for k in ('segwit','segwit_random','bech32'): - if getattr(opt,k): + if getattr(cfg,k): segwit_opt = k break else: @@ -706,28 +712,28 @@ class TestSuiteRunner(object): # pass through opts from cmdline (po.user_opts) self.passthru_opts = ['--{}{}'.format( k.replace('_','-'), - '=' + getattr(opt,k) if getattr(opt,k) != True else '' - ) for k in self.ts.base_passthru_opts + self.ts.passthru_opts if k in parsed_opts.user_opts] + '' if cfg._uopts[k] is True else '=' + cfg._uopts[k] + ) for k in cfg._uopts if k in self.ts.base_passthru_opts + self.ts.passthru_opts] - if opt.resuming: - rc = opt.resume or opt.resume_after - offset = 1 if opt.resume_after else 0 + if cfg.resuming: + rc = cfg.resume or cfg.resume_after + offset = 1 if cfg.resume_after else 0 self.resume_cmd = self.gm.cmd_list[self.gm.cmd_list.index(rc)+offset] omsg(f'INFO → Resuming at command {self.resume_cmd!r}') - if opt.step: - opt.exit_after = self.resume_cmd + if cfg.step: + cfg.exit_after = self.resume_cmd - if opt.exit_after and opt.exit_after not in self.gm.cmd_list: - die(1,f'{opt.exit_after!r}: command not recognized') + if cfg.exit_after and cfg.exit_after not in self.gm.cmd_list: + die(1,f'{cfg.exit_after!r}: command not recognized') return True - def run_tests(self,usr_args): + def run_tests(self,cmd_args): self.start_time = time.time() self.daemon_started = False gname_save = None - if usr_args: - for arg in usr_args: + if cmd_args: + for arg in cmd_args: if arg in self.gm.cmd_groups: if not self.init_group(arg): continue @@ -752,7 +758,7 @@ class TestSuiteRunner(object): if not self.init_group(gname,sg_name,cmdname,quiet=same_grp,do_clean=not same_grp): continue if cmdname: - if opt.deps_only: + if cfg.deps_only: self.deps_only = cmdname try: self.check_needs_rerun(cmdname,build=True) @@ -772,13 +778,13 @@ class TestSuiteRunner(object): else: die(1,f'{arg!r}: command not recognized') else: - if opt.exclude_groups: - exclude = opt.exclude_groups.split(',') + if cfg.exclude_groups: + exclude = cfg.exclude_groups.split(',') for e in exclude: if e not in self.gm.cmd_groups_dfl: die(1,f'{e!r}: group not recognized') for gname in self.gm.cmd_groups_dfl: - if opt.exclude_groups and gname in exclude: + if cfg.exclude_groups and gname in exclude: continue if not self.init_group(gname): continue @@ -832,7 +838,7 @@ class TestSuiteRunner(object): for fn in fns: if not root: os.unlink(fn) - if not (dpy and opt.skipping_deps): + if not (dpy and cfg.skipping_deps): self.run_test(cmd) if not root: do_between() @@ -862,10 +868,10 @@ class TestSuiteRunner(object): return bmsg(f'Resuming at {self.resume_cmd!r}') self.resume_cmd = None - opt.skipping_deps = False - opt.resuming = False + cfg.skipping_deps = False + cfg.resuming = False - if opt.profile: + if cfg.profile: start = time.time() self.ts.test_name = cmd # NB: Do not remove, this needs to be set twice @@ -873,24 +879,24 @@ class TestSuiteRunner(object): # self.ts.test_dpydata = cdata self.ts.tmpdir_num = cdata[0] # self.ts.cfg = cfgs[str(cdata[0])] # will remove this eventually - cfg = cfgs[str(cdata[0])] + test_cfg = cfgs[str(cdata[0])] for k in ( 'seed_len', 'seed_id', 'wpasswd', 'kapasswd', 'segwit', 'hash_preset', 'bw_filename', 'bw_params', 'ref_bw_seed_id', 'addr_idx_list', 'pass_idx_list' ): - if k in cfg: - setattr(self.ts,k,cfg[k]) + if k in test_cfg: + setattr(self.ts,k,test_cfg[k]) ret = getattr(self.ts,cmd)(*arg_list) # run the test if type(ret).__name__ == 'coroutine': ret = async_run(ret) self.process_retval(cmd,ret) - if opt.profile: + if cfg.profile: omsg('\r\033[50C{:.4f}'.format( time.time() - start )) - if cmd == opt.exit_after: + if cmd == cfg.exit_after: sys.exit(0) def warn_skipped(self): @@ -925,7 +931,7 @@ class TestSuiteRunner(object): if cmd not in self.gm.cmd_list: die(1,f'{cmd!r}: unrecognized command') - if not opt.quiet: + if not cfg.quiet: omsg(f'Checking dependencies for {cmd!r}') self.check_needs_rerun(self.ts,cmd,build=False) @@ -966,18 +972,18 @@ class TestSuiteRunner(object): # main() -if not opt.skipping_deps: # do this before list cmds exit, so we stay in sync with shm_dir +if not cfg.skipping_deps: # do this before list cmds exit, so we stay in sync with shm_dir create_tmp_dirs(shm_dir) -if opt.list_cmd_groups: +if cfg.list_cmd_groups: CmdGroupMgr().list_cmd_groups() -elif opt.list_cmds: +elif cfg.list_cmds: list_cmds() -elif usr_args and usr_args[0] in utils: - globals()[usr_args[0]](*usr_args[1:]) +elif cmd_args and cmd_args[0] in utils: + globals()[cmd_args[0]](*cmd_args[1:]) sys.exit(0) -if opt.pause: +if cfg.pause: set_restore_term_at_exit() set_environ_for_spawned_scripts() @@ -986,7 +992,7 @@ from mmgen.exception import TestSuiteException,TestSuiteFatalException try: tr = TestSuiteRunner(data_dir,trash_dir) - tr.run_tests(usr_args) + tr.run_tests(cmd_args) tr.warn_skipped() if tr.daemon_started: stop_test_daemons(network_id) diff --git a/test/test_py_d/cfg.py b/test/test_py_d/cfg.py index b308f83d..94b56cf1 100755 --- a/test/test_py_d/cfg.py +++ b/test/test_py_d/cfg.py @@ -13,6 +13,7 @@ test.test_py_d.cfg: configuration data for test.py """ from .common import * +from ..include.common import cfg cmd_groups_dfl = { 'misc': ('TestSuiteMisc',{}), @@ -229,8 +230,8 @@ def fixup_cfgs(): cfgs[target]['tmpdir'] = os.path.join('test','tmp',target) for k in cfgs: - cfgs[k]['segwit'] = randbool() if opt.segwit_random else bool(opt.segwit or opt.bech32) + cfgs[k]['segwit'] = randbool() if cfg.segwit_random else bool(cfg.segwit or cfg.bech32) - if g.debug_utf8: + if cfg.debug_utf8: for k in cfgs: cfgs[k]['tmpdir'] += '-α' diff --git a/test/test_py_d/common.py b/test/test_py_d/common.py index d63e1212..3ca1e187 100755 --- a/test/test_py_d/common.py +++ b/test/test_py_d/common.py @@ -21,7 +21,7 @@ test.test_py_d.common: Shared routines and data for the test.py test suite """ import sys,os -from mmgen.globalvars import g,gc +from mmgen.globalvars import gc from mmgen.util import msg from ..include.common import * @@ -71,9 +71,9 @@ chksum_pat = r'\b[A-F0-9]{4} [A-F0-9]{4} [A-F0-9]{4} [A-F0-9]{4}\b' Ctrl_U = '\x15' def ok_msg(): - if opt.profile: + if cfg.profile: return - sys.stderr.write(green('\nOK\n') if opt.exact_output or opt.verbose else ' OK\n') + sys.stderr.write(green('\nOK\n') if cfg.exact_output or cfg.verbose else ' OK\n') def skip(name,reason=None): msg('Skipping {}{}'.format( name, f' ({reason})' if reason else '' )) @@ -82,10 +82,11 @@ def skip(name,reason=None): def confirm_continue(): from mmgen.ui import keypress_confirm if keypress_confirm( + cfg, blue('Continue? (Y/n): '), default_yes = True, complete_prompt = True ): - if opt.verbose or opt.exact_output: + if cfg.verbose or cfg.exact_output: sys.stderr.write('\n') else: raise KeyboardInterrupt('Exiting at user request') @@ -96,7 +97,7 @@ def randbool(): def disable_debug(): global save_debug save_debug = {} - for k in g.env_opts: + for k in cfg.env_opts: if k[:11] == 'MMGEN_DEBUG': save_debug[k] = os.getenv(k) os.environ[k] = '' @@ -118,7 +119,7 @@ def get_file_with_ext(tdir,ext,delete=True,no_dot=False,return_list=False,delete if len(flist) > 1 or delete_all: if delete or delete_all: - if (opt.exact_output or opt.verbose) and not opt.quiet: + if (cfg.exact_output or cfg.verbose) and not cfg.quiet: if delete_all: msg(f'Deleting all *.{ext} files in {tdir!r}') else: diff --git a/test/test_py_d/ts_autosign.py b/test/test_py_d/ts_autosign.py index 5bbf5a70..c5e8f026 100755 --- a/test/test_py_d/ts_autosign.py +++ b/test/test_py_d/ts_autosign.py @@ -23,8 +23,7 @@ test.test_py_d.ts_autosign: Autosign tests for the test.py test suite import os,shutil from subprocess import run -from mmgen.globalvars import g,gc -from mmgen.opts import opt +from mmgen.globalvars import gc from ..include.common import * from .common import * @@ -88,7 +87,7 @@ class TestSuiteAutosignBase(TestSuiteBase): die(1,f'Test {type(self).__name__} not supported for Windows platform') self.network_ids = [c+'_tn' for c in self.daemon_coins] + self.daemon_coins - if self.simulate and not opt.exact_output: + if self.simulate and not cfg.exact_output: die(1,red('This command must be run with --exact-output enabled!')) if self.simulate or not self.live: @@ -168,7 +167,7 @@ class TestSuiteAutosignBase(TestSuiteBase): mn = read_from_file(mn_file).strip().split() from mmgen.mn_entry import mn_entry entry_mode = 'full' - mne = mn_entry(mn_type,entry_mode) + mne = mn_entry( cfg, mn_type, entry_mode ) t.expect('Type a number.*: ',str(mne.entry_modes.index(entry_mode)+1),regex=True) stealth_mnemonic_entry(t,mne,mn,entry_mode) wf = t.written_to_file('Autosign wallet') @@ -203,7 +202,7 @@ class TestSuiteAutosignBase(TestSuiteBase): for f,fn in zip(tfs,tfns): if fn: # use empty fn to skip file - if g.debug_utf8: + if cfg.debug_utf8: ext = '.testnet.rawtx' if fn.endswith('.testnet.rawtx') else '.rawtx' fn = fn[:-len(ext)] + '-α' + ext target = joinpath(self.mountpoint,'tx',fn) @@ -432,7 +431,7 @@ class TestSuiteAutosignLive(TestSuiteAutosignBTC): t = self.spawn( 'mmgen-autosign', self.opts + led_opts + ['--quiet','--no-summary','wait']) - if not opt.exact_output: + if not cfg.exact_output: omsg('') prompt_insert_sign(t) diff --git a/test/test_py_d/ts_base.py b/test/test_py_d/ts_base.py index 97420e0e..eb2971b3 100755 --- a/test/test_py_d/ts_base.py +++ b/test/test_py_d/ts_base.py @@ -21,8 +21,7 @@ test.test_py_d.ts_base: Base class for the test.py test suite """ import os -from mmgen.globalvars import g,gc -from mmgen.opts import opt +from mmgen.globalvars import gc from ..include.common import * from .common import * @@ -37,13 +36,12 @@ class TestSuiteBase(object): need_daemon = False def __init__(self,trunner,cfgs,spawn): - from mmgen.protocol import init_proto_from_opts - self.proto = init_proto_from_opts(need_amt=True) + self.proto = cfg._proto self.tr = trunner self.cfgs = cfgs self.spawn = spawn self.have_dfl_wallet = False - self.usr_rand_chars = (5,30)[bool(opt.usr_random)] + self.usr_rand_chars = (5,30)[bool(cfg.usr_random)] self.usr_rand_arg = f'-r{self.usr_rand_chars}' self.altcoin_pfx = '' if self.proto.base_coin == 'BTC' else '-'+self.proto.base_coin self.tn_ext = ('','.testnet')[self.proto.testnet] @@ -54,11 +52,11 @@ class TestSuiteBase(object): @property def tmpdir(self): - return os.path.join('test','tmp','{}{}'.format(self.tmpdir_num,'-α' if g.debug_utf8 else '')) + return os.path.join('test','tmp','{}{}'.format(self.tmpdir_num,'-α' if cfg.debug_utf8 else '')) @property def segwit_mmtype(self): - return ('segwit','bech32')[bool(opt.bech32)] if self.segwit else None + return ('segwit','bech32')[bool(cfg.bech32)] if self.segwit else None @property def segwit_arg(self): diff --git a/test/test_py_d/ts_cfgfile.py b/test/test_py_d/ts_cfgfile.py index af36354f..1ea63bd3 100755 --- a/test/test_py_d/ts_cfgfile.py +++ b/test/test_py_d/ts_cfgfile.py @@ -130,7 +130,7 @@ class TestSuiteCfgFile(TestSuiteBase): t.expect(s) if t.pexpect_spawn: # view and exit pager - time.sleep(1 if opt.exact_output else t.send_delay) + time.sleep(1 if cfg.exact_output else t.send_delay) t.send('q') t.expect(cp,'n') @@ -202,7 +202,7 @@ class TestSuiteCfgFile(TestSuiteBase): ('ETH','True', '5.4321',True), ('ETC','False','5.4321',False) ): - if opt.no_altcoin and coin != 'BTC': + if cfg.no_altcoin and coin != 'BTC': continue t = self.spawn_test( args = [ @@ -244,7 +244,7 @@ class TestSuiteCfgFile(TestSuiteBase): def chain_names(self): - if opt.no_altcoin: + if cfg.no_altcoin: return 'skip' def run(chk,testnet): diff --git a/test/test_py_d/ts_chainsplit.py b/test/test_py_d/ts_chainsplit.py index 37bfda30..1edb4408 100755 --- a/test/test_py_d/ts_chainsplit.py +++ b/test/test_py_d/ts_chainsplit.py @@ -21,8 +21,6 @@ test.test_py_d.ts_chainsplit: Forking scenario tests for the test.py test suite This module is unmaintained and currently non-functional """ import os -from mmgen.globalvars import g -from mmgen.opts import opt from mmgen.util import die from ..include.common import * from .common import * diff --git a/test/test_py_d/ts_ethdev.py b/test/test_py_d/ts_ethdev.py index a01c9d11..8928db99 100755 --- a/test/test_py_d/ts_ethdev.py +++ b/test/test_py_d/ts_ethdev.py @@ -25,8 +25,7 @@ from decimal import Decimal from collections import namedtuple from subprocess import run,PIPE,DEVNULL -from mmgen.globalvars import g,gc -from mmgen.opts import opt +from mmgen.globalvars import gc from mmgen.util import die from mmgen.protocol import CoinProtocol from ..include.common import * @@ -119,7 +118,7 @@ token_bals_getbalance = { from .ts_base import * from .ts_shared import * -coin = g.coin +coin = cfg.coin class TestSuiteEthdev(TestSuiteBase,TestSuiteShared): 'Ethereum transacting, token deployment and tracking wallet operations' @@ -365,10 +364,10 @@ class TestSuiteEthdev(TestSuiteBase,TestSuiteShared): return from mmgen.protocol import init_proto - self.proto = init_proto(g.coin,network='regtest',need_amt=True) + self.proto = init_proto( cfg, cfg.coin, network='regtest', need_amt=True ) from mmgen.daemon import CoinDaemon - self.daemon = CoinDaemon(self.proto.coin+'_rt',test_suite=True) + self.daemon = CoinDaemon( cfg, self.proto.coin+'_rt', test_suite=True ) self.using_solc = check_solc_ver() if not self.using_solc: @@ -405,7 +404,7 @@ class TestSuiteEthdev(TestSuiteBase,TestSuiteShared): @property async def rpc(self): from mmgen.rpc import rpc_init - return await rpc_init(self.proto) + return await rpc_init(cfg,self.proto) async def setup(self): self.spawn('',msg_only=True) @@ -431,11 +430,11 @@ class TestSuiteEthdev(TestSuiteBase,TestSuiteShared): if d.id in ('geth','erigon'): imsg(' {:19} {}'.format('Cmdline:',' '.join(e for e in d.start_cmd if not 'verbosity' in e))) - if not opt.no_daemon_autostart: + if not cfg.no_daemon_autostart: if not d.id in ('geth','erigon'): d.stop(silent=True) d.remove_datadir() - d.start( silent = not (opt.verbose or opt.exact_output) ) + d.start( silent = not (cfg.verbose or cfg.exact_output) ) rpc = await self.rpc imsg(f'Daemon: {rpc.daemon.coind_name} v{rpc.daemon_version_str}') @@ -449,6 +448,7 @@ class TestSuiteEthdev(TestSuiteBase,TestSuiteShared): from mmgen.proto.eth.misc import extract_key_from_geth_keystore_wallet key = extract_key_from_geth_keystore_wallet( + cfg = cfg, wallet_fn = wallet_fn, passwd = b'' ) @@ -552,7 +552,7 @@ class TestSuiteEthdev(TestSuiteBase,TestSuiteShared): return t def addrimport(self,ext='21-23]{}.regtest.addrs',expect='9/9',add_args=[],bad_input=False): - ext = ext.format('-α' if g.debug_utf8 else '') + ext = ext.format('-α' if cfg.debug_utf8 else '') fn = self.get_file_with_ext(ext,no_dot=True,delete=False) t = self.spawn('mmgen-addrimport', self.eth_args[1:-1] + add_args + [fn]) if bad_input: @@ -605,7 +605,7 @@ class TestSuiteEthdev(TestSuiteBase,TestSuiteShared): return t def txsign(self,ni=False,ext='{}.regtest.rawtx',add_args=[],dev_send=False): - ext = ext.format('-α' if g.debug_utf8 else '') + ext = ext.format('-α' if cfg.debug_utf8 else '') keyfile = joinpath(self.tmpdir,parity_devkey_fn) txfile = self.get_file_with_ext(ext,no_dot=True) t = self.spawn( 'mmgen-txsign', @@ -620,17 +620,17 @@ class TestSuiteEthdev(TestSuiteBase,TestSuiteShared): return self.txsign_ui_common(t,ni=ni,has_label=True) def txsend(self,ni=False,ext='{}.regtest.sigtx',add_args=[]): - ext = ext.format('-α' if g.debug_utf8 else '') + ext = ext.format('-α' if cfg.debug_utf8 else '') txfile = self.get_file_with_ext(ext,no_dot=True) t = self.spawn('mmgen-txsend', self.eth_args + add_args + [txfile]) txid = self.txsend_ui_common(t, - quiet = not g.debug, + quiet = not cfg.debug, bogus_send = False, has_label = True ) return t def txview(self,ext_fs): - ext = ext_fs.format('-α' if g.debug_utf8 else '') + ext = ext_fs.format('-α' if cfg.debug_utf8 else '') txfile = self.get_file_with_ext(ext,no_dot=True) return self.spawn( 'mmgen-tool',['--verbose','txview',txfile] ) @@ -650,7 +650,7 @@ class TestSuiteEthdev(TestSuiteBase,TestSuiteShared): caller = 'txdo', acct = '1', no_read = True ) - self._do_confirm_send(t,quiet=not g.debug,sure=False) + self._do_confirm_send(t,quiet=not cfg.debug,sure=False) t.read() self.get_file_with_ext('sigtx',delete_all=True) return t @@ -686,7 +686,7 @@ class TestSuiteEthdev(TestSuiteBase,TestSuiteShared): def bal3(self): return self.bal(n='3') def tx_status(self,ext,expect_str,expect_str2='',add_args=[],exit_val=0): - ext = ext.format('-α' if g.debug_utf8 else '') + ext = ext.format('-α' if cfg.debug_utf8 else '') txfile = self.get_file_with_ext(ext,no_dot=True) t = self.spawn('mmgen-txsend', self.eth_args + add_args + ['--status',txfile]) t.expect(expect_str) @@ -707,7 +707,7 @@ class TestSuiteEthdev(TestSuiteBase,TestSuiteShared): key = self.keystore_data['key'] imsg(f'Key: {key}') from mmgen.proto.eth.misc import ec_sign_message_with_privkey - return ec_sign_message_with_privkey(self.message,bytes.fromhex(key),'eth_sign') + return ec_sign_message_with_privkey(cfg,self.message,bytes.fromhex(key),'eth_sign') async def create_signature_rpc(): addr = self.read_from_tmpfile('signer_addr').strip() @@ -791,7 +791,7 @@ class TestSuiteEthdev(TestSuiteBase,TestSuiteShared): eth_fee_res = True ) def txbump(self,ext=',40000]{}.regtest.rawtx',fee='50G',add_args=[]): - ext = ext.format('-α' if g.debug_utf8 else '') + ext = ext.format('-α' if cfg.debug_utf8 else '') txfile = self.get_file_with_ext(ext,no_dot=True) t = self.spawn('mmgen-txbump', self.eth_args + add_args + ['--yes',txfile]) t.expect('or gas price: ',fee+'\n') @@ -905,7 +905,7 @@ class TestSuiteEthdev(TestSuiteBase,TestSuiteShared): async def get_tx_receipt(self,txid): from mmgen.tx import NewTX - tx = await NewTX(proto=self.proto) + tx = await NewTX(cfg=cfg,proto=self.proto) tx.rpc = await self.rpc res = await tx.get_receipt(txid) imsg(f'Gas sent: {res.gas_sent.hl():<9} {(res.gas_sent*res.gas_price).hl2(encl="()")}') @@ -931,7 +931,7 @@ class TestSuiteEthdev(TestSuiteBase,TestSuiteShared): t = self.spawn( 'mmgen-'+mmgen_cmd, self.eth_args + args) if mmgen_cmd == 'txcreate': t.written_to_file('transaction') - ext = '[0,8000]{}.regtest.rawtx'.format('-α' if g.debug_utf8 else '') + ext = '[0,8000]{}.regtest.rawtx'.format('-α' if cfg.debug_utf8 else '') txfile = self.get_file_with_ext(ext,no_dot=True) t = self.spawn('mmgen-txsign', self.eth_args + ['--yes','-k',keyfile,txfile],no_msg=True) self.txsign_ui_common(t,ni=True) @@ -940,7 +940,7 @@ class TestSuiteEthdev(TestSuiteBase,TestSuiteShared): txid = self.txsend_ui_common(t, caller = mmgen_cmd, - quiet = mmgen_cmd == 'txdo' or not g.debug, + quiet = mmgen_cmd == 'txdo' or not cfg.debug, bogus_send = False ) addr = strip_ansi_escapes(t.expect_getend('Contract address: ')) if (await self.get_tx_receipt(txid)).status == 0: @@ -971,12 +971,12 @@ class TestSuiteEthdev(TestSuiteBase,TestSuiteShared): sid = dfl_sid from mmgen.tool.wallet import tool_cmd usr_mmaddrs = [f'{sid}:E:{i}' for i in (11,21)] - usr_addrs = [tool_cmd(cmdname='gen_addr',proto=self.proto).gen_addr(addr,dfl_words_file) for addr in usr_mmaddrs] from mmgen.proto.eth.contract import TokenResolve async def do_transfer(rpc): for i in range(2): tk = await TokenResolve( + cfg, self.proto, rpc, self.read_from_tmpfile(f'token_addr{i+1}').strip() ) @@ -998,6 +998,7 @@ class TestSuiteEthdev(TestSuiteBase,TestSuiteShared): async def show_bals(rpc): for i in range(2): tk = await TokenResolve( + cfg, self.proto, rpc, self.read_from_tmpfile(f'token_addr{i+1}').strip() ) @@ -1008,7 +1009,11 @@ class TestSuiteEthdev(TestSuiteBase,TestSuiteShared): usr_mmaddrs[i], usr_addrs[i] )) + def gen_addr(addr): + return tool_cmd(cfg,cmdname='gen_addr',proto=self.proto).gen_addr(addr,dfl_words_file) + silence() + usr_addrs = list(map(gen_addr,usr_mmaddrs)) if op == 'show_bals': await show_bals(await self.rpc) elif op == 'do_transfer': @@ -1191,7 +1196,7 @@ class TestSuiteEthdev(TestSuiteBase,TestSuiteShared): add_args = ['98831F3A:E:3,0.4321']): args = ['--fee=20G','--cached-balances'] + add_args + [dfl_words_file] t = self.txcreate(args=args,acct=acct,caller='txdo',fee_res_data=fee_res_data,no_read=True) - self._do_confirm_send(t,quiet=not g.debug,sure=False) + self._do_confirm_send(t,quiet=not cfg.debug,sure=False) return t def txcreate_refresh_balances(self, @@ -1322,7 +1327,7 @@ class TestSuiteEthdev(TestSuiteBase,TestSuiteShared): async def twmove(self): self.spawn('',msg_only=True) from mmgen.tw.ctl import TwCtl - twctl = await TwCtl(self.proto) + twctl = await TwCtl(cfg,self.proto) imsg(f'Moving tracking wallet') bakfile = twctl.tw_fn + '.bak.json' if os.path.exists(bakfile): @@ -1332,7 +1337,7 @@ class TestSuiteEthdev(TestSuiteBase,TestSuiteShared): def twimport(self,add_args=[],expect_str=None): from mmgen.tw.json import TwJSON - fn = joinpath( self.tmpdir, TwJSON.Base(self.proto).dump_fn ) + fn = joinpath( self.tmpdir, TwJSON.Base(cfg,self.proto).dump_fn ) t = self.spawn('mmgen-tool',self.eth_args_noquiet + ['twimport',fn] + add_args) t.expect('(y/N): ','y') if expect_str: @@ -1346,7 +1351,7 @@ class TestSuiteEthdev(TestSuiteBase,TestSuiteShared): def tw_chktotal(self): self.spawn('',msg_only=True) from mmgen.tw.json import TwJSON - fn = joinpath( self.tmpdir, TwJSON.Base(self.proto).dump_fn ) + fn = joinpath( self.tmpdir, TwJSON.Base(cfg,self.proto).dump_fn ) res = json.loads(read_from_file(fn)) cmp_or_die( res['data']['value'], vbal6, 'value in tracking wallet JSON dump' ) return 'ok' @@ -1354,7 +1359,7 @@ class TestSuiteEthdev(TestSuiteBase,TestSuiteShared): async def twcompare(self): self.spawn('',msg_only=True) from mmgen.tw.ctl import TwCtl - twctl = await TwCtl(self.proto) + twctl = await TwCtl(cfg,self.proto) fn = twctl.tw_fn imsg(f'Comparing imported tracking wallet with original') data = [json.dumps(json.loads(read_from_file(f)),sort_keys=True) for f in (fn,fn+'.bak.json')] @@ -1364,7 +1369,7 @@ class TestSuiteEthdev(TestSuiteBase,TestSuiteShared): def edit_json_twdump(self): self.spawn('',msg_only=True) from mmgen.tw.json import TwJSON - fn = TwJSON.Base(self.proto).dump_fn + fn = TwJSON.Base(cfg,self.proto).dump_fn text = json.loads(self.read_from_tmpfile(fn)) token_addr = self.read_from_tmpfile('token_addr2').strip() text['data']['entries']['tokens'][token_addr][2][3] = f'edited comment [фубар] [{gr_uc}]' @@ -1373,7 +1378,7 @@ class TestSuiteEthdev(TestSuiteBase,TestSuiteShared): def stop(self): self.spawn('',msg_only=True) - if not opt.no_daemon_stop: + if not cfg.no_daemon_stop: if not stop_test_daemons(self.proto.coin+'_rt'): return False set_vt100() diff --git a/test/test_py_d/ts_input.py b/test/test_py_d/ts_input.py index 7deadeeb..35ca2bb8 100755 --- a/test/test_py_d/ts_input.py +++ b/test/test_py_d/ts_input.py @@ -352,7 +352,7 @@ class TestSuiteInput(TestSuiteBase): mn = mn or sample_mn[fmt]['mn'].split() t = self.spawn('mmgen-tool',['mn2hex_interactive','fmt='+fmt,'mn_len=12','print_mn=1']) from mmgen.mn_entry import mn_entry - mne = mn_entry(fmt,entry_mode) + mne = mn_entry( cfg, fmt, entry_mode ) t.expect( 'Type a number.*: ', ('\n' if enter_for_dfl else str(mne.entry_modes.index(entry_mode)+1)), @@ -381,7 +381,7 @@ class TestSuiteInput(TestSuiteBase): t.expect('Type a number.*: ','6',regex=True) t.expect('invalid') from mmgen.mn_entry import mn_entry - mne = mn_entry(fmt,entry_mode) + mne = mn_entry( cfg, fmt, entry_mode ) t.expect('Type a number.*: ',str(mne.entry_modes.index(entry_mode)+1),regex=True) t.expect('Using (.+) entry mode',regex=True) mode = strip_ansi_escapes(t.p.match.group(1)).lower() @@ -405,7 +405,7 @@ class TestSuiteInput(TestSuiteBase): def mnemonic_entry_mmgen_minimal(self): from mmgen.mn_entry import mn_entry # erase_chars: '\b\x7f' - m = mn_entry('mmgen','minimal') + m = mn_entry( cfg, 'mmgen', 'minimal' ) np = 2 mn = ( 'z', diff --git a/test/test_py_d/ts_main.py b/test/test_py_d/ts_main.py index f0c09946..4cf81da8 100755 --- a/test/test_py_d/ts_main.py +++ b/test/test_py_d/ts_main.py @@ -20,8 +20,6 @@ test.test_py_d.ts_main: Basic operations tests for the test.py test suite """ -from mmgen.globalvars import g -from mmgen.opts import opt from mmgen.fileutil import get_data_from_file,write_data_to_file from mmgen.wallet import get_wallet_cls from mmgen.wallet.mmgen import wallet as MMGenWallet @@ -41,9 +39,9 @@ def make_brainwallet_file(fn): return ''.join([ws_list[getrandnum_range(1,200) % len(ws_list)] for i in range(nchars)]) rand_pairs = [wl[getrandnum_range(1,200) % len(wl)] + rand_ws_seq() for i in range(nwords)] d = ''.join(rand_pairs).rstrip() + '\n' - if opt.verbose: + if cfg.verbose: msg_r(f'Brainwallet password:\n{cyan(d)}') - write_data_to_file(fn,d,'brainwallet password',quiet=True,ignore_opt_outdir=True) + write_data_to_file(cfg,fn,d,'brainwallet password',quiet=True,ignore_opt_outdir=True) def verify_checksum_or_exit(checksum,chk): chk = strip_ansi_escapes(chk) @@ -194,7 +192,7 @@ class TestSuiteMain(TestSuiteBase,TestSuiteShared): TestSuiteBase.__init__(self,trunner,cfgs,spawn) if trunner == None or self.proto.coin.lower() not in self.networks: return - self.rpc = async_run(rpc_init(self.proto)) + self.rpc = async_run(rpc_init(cfg,self.proto)) self.lbl_id = ('account','label')['label_api' in self.rpc.caps] if self.proto.coin in ('BTC','BCH','LTC'): self.tx_fee = {'btc':'0.0001','bch':'0.001','ltc':'0.01'}[self.proto.coin.lower()] @@ -207,9 +205,9 @@ class TestSuiteMain(TestSuiteBase,TestSuiteShared): addrfile = self.get_file_with_ext('addrs') from mmgen.addrlist import AddrList silence() - chk = AddrList( self.proto, addrfile ).chksum + chk = AddrList( cfg, self.proto, addrfile ).chksum end_silence() - if opt.verbose and display: + if cfg.verbose and display: msg(f'Checksum: {cyan(chk)}') return chk @@ -239,10 +237,10 @@ class TestSuiteMain(TestSuiteBase,TestSuiteShared): def delete_dfl_wallet(self,pf): self.write_to_tmpfile('del_dw_run',b'',binary=True) - if opt.no_dw_delete: + if cfg.no_dw_delete: return 'skip' - for wf in [f for f in os.listdir(g.data_dir) if f[-6:]=='.mmdat']: - os.unlink(joinpath(g.data_dir,wf)) + for wf in [f for f in os.listdir(cfg.data_dir) if f[-6:]=='.mmdat']: + os.unlink(joinpath(cfg.data_dir,wf)) self.spawn('',msg_only=True) self.have_dfl_wallet = False return 'ok' @@ -294,7 +292,7 @@ class TestSuiteMain(TestSuiteBase,TestSuiteShared): def passchg(self,wf,pf,label_action='cmdline',dfl_wallet=False,delete=False): silence() - self.write_to_tmpfile(pwfile,get_data_from_file(pf)) + self.write_to_tmpfile( pwfile, get_data_from_file(cfg,pf) ) end_silence() add_args = {'cmdline': ['-d',self.tmpdir,'-L','Changed label (UTF-8) α'], 'keep': ['-d',self.tr.trash_dir,'--keep-label'], @@ -336,8 +334,8 @@ class TestSuiteMain(TestSuiteBase,TestSuiteShared): return self.walletchk(wf,pf,wcls=wcls,dfl_wallet=dfl_wallet) def _write_fake_data_to_file(self,d): - write_data_to_file(self.unspent_data_file,d,'Unspent outputs',quiet=True,ignore_opt_outdir=True) - if opt.verbose or opt.exact_output: + write_data_to_file(cfg,self.unspent_data_file,d,'Unspent outputs',quiet=True,ignore_opt_outdir=True) + if cfg.verbose or cfg.exact_output: sys.stderr.write(f'Fake transaction wallet data written to file {self.unspent_data_file!r}\n') def _create_fake_unspent_entry(self,coinaddr,al_id=None,idx=None,comment=None,non_mmgen=False,segwit=False): @@ -373,7 +371,7 @@ class TestSuiteMain(TestSuiteBase,TestSuiteShared): for d in tx_data.values(): al = adata.addrlist(al_id=d['al_id']) for n,(idx,coinaddr) in enumerate(al.addrpairs()): - comment = get_comment(do_shuffle=not g.test_suite_deterministic) + comment = get_comment(do_shuffle=not cfg.test_suite_deterministic) out.append(self._create_fake_unspent_entry(coinaddr,d['al_id'],idx,comment,segwit=d['segwit'])) if n == 0: # create a duplicate address. This means addrs_per_wallet += 1 out.append(self._create_fake_unspent_entry(coinaddr,d['al_id'],idx,comment,segwit=d['segwit'])) @@ -387,11 +385,13 @@ class TestSuiteMain(TestSuiteBase,TestSuiteShared): pubkey_type = 'std' ) from mmgen.addrgen import KeyGenerator,AddrGenerator rand_coinaddr = AddrGenerator( + cfg, self.proto, ('legacy','compressed')[non_mmgen_input_compressed] - ).to_addr(KeyGenerator(self.proto,'std').gen_data(privkey)) + ).to_addr(KeyGenerator( cfg, self.proto, 'std' ).gen_data(privkey)) of = joinpath(self.cfgs[non_mmgen_input]['tmpdir'],non_mmgen_fn) write_data_to_file( + cfg = cfg, outfile = of, data = privkey.wif + '\n', desc = f'compressed {self.proto.name} key', @@ -406,14 +406,14 @@ class TestSuiteMain(TestSuiteBase,TestSuiteShared): from mmgen.addrdata import AddrData tx_data,ad = {},AddrData(self.proto) for s in sources: - afile = get_file_with_ext(self.cfgs[s]['tmpdir'],'addrs') - al = AddrList(self.proto,afile) + addrfile = get_file_with_ext(self.cfgs[s]['tmpdir'],'addrs') + al = AddrList( cfg, self.proto, addrfile ) ad.add(al) aix = AddrIdxList(fmt_str=self.cfgs[s]['addr_idx_list']) if len(aix) != addrs_per_wallet: die( 'TestSuiteFatalException', f'Address index list length != {addrs_per_wallet}: {repr(aix)}' ) tx_data[s] = { - 'addrfile': afile, + 'addrfile': addrfile, 'chk': al.chksum, 'al_id': al.al_id, 'addr_idxs': aix[-2:], @@ -426,8 +426,8 @@ class TestSuiteMain(TestSuiteBase,TestSuiteShared): privkey = PrivKey(self.proto,getrand(32),compressed=True,pubkey_type='std') t = ('compressed','segwit')['S' in self.proto.mmtypes] from mmgen.addrgen import KeyGenerator,AddrGenerator - rand_coinaddr = AddrGenerator(self.proto,t).to_addr( - KeyGenerator(self.proto,'std').gen_data(privkey) + rand_coinaddr = AddrGenerator( cfg, self.proto, t ).to_addr( + KeyGenerator( cfg, self.proto, 'std' ).gen_data(privkey) ) # total of two outputs must be < 10 BTC (<1000 LTC) @@ -472,7 +472,7 @@ class TestSuiteMain(TestSuiteBase,TestSuiteShared): cmdline_inputs = False, tweaks = [] ): - if opt.verbose or opt.exact_output: + if cfg.verbose or cfg.exact_output: sys.stderr.write(green('Generating fake tracking wallet info\n')) silence() @@ -496,7 +496,7 @@ class TestSuiteMain(TestSuiteBase,TestSuiteShared): end_silence() - if opt.verbose or opt.exact_output: + if cfg.verbose or cfg.exact_output: sys.stderr.write('\n') t = self.spawn( @@ -557,8 +557,8 @@ class TestSuiteMain(TestSuiteBase,TestSuiteShared): t.do_comment(False,has_label=True) for cnum,wcls in (('1',IncogWallet),('3',MMGenWallet),('4',MMGenWallet)): t.passphrase(wcls.desc,self.cfgs[cnum]['wpasswd']) - self._do_confirm_send(t,quiet=not g.debug,confirm_send=True) - if g.debug: + self._do_confirm_send(t,quiet=not cfg.debug,confirm_send=True) + if cfg.debug: t.written_to_file('Transaction') else: t.do_comment(False) @@ -617,7 +617,8 @@ class TestSuiteMain(TestSuiteBase,TestSuiteShared): wcls = get_wallet_cls(fmt_code=out_fmt) msg('==> {}: {}'.format( wcls.desc, - cyan(get_data_from_file(f,wcls.desc)) )) + cyan(get_data_from_file( cfg, f, wcls.desc )) + )) end_silence() return t diff --git a/test/test_py_d/ts_misc.py b/test/test_py_d/ts_misc.py index 0fc43b22..54541d95 100755 --- a/test/test_py_d/ts_misc.py +++ b/test/test_py_d/ts_misc.py @@ -20,7 +20,7 @@ test.test_py_d.ts_misc: Miscellaneous test groups for the test.py test suite """ -from mmgen.globalvars import g,gc +from mmgen.globalvars import gc from ..include.common import * from .common import * from .ts_base import * @@ -41,7 +41,7 @@ class TestSuiteMisc(TestSuiteBase): color = True def rpc_backends(self): - backends = g.autoset_opts['rpc_backend'][1] + backends = cfg.autoset_opts['rpc_backend'][1] for b in backends: t = self.spawn_chk('mmgen-tool',[f'--rpc-backend={b}','daemon_version'],extra_desc=f'({b})') return t @@ -76,7 +76,7 @@ class TestSuiteMisc(TestSuiteBase): t = self.spawn('test/misc/term_ni.py',['echo'],cmd_dir='.',pexpect_spawn=True,timeout=1) t.p.logfile = None - t.p.logfile_read = sys.stdout if opt.verbose or opt.exact_output else None + t.p.logfile_read = sys.stdout if cfg.verbose or cfg.exact_output else None t.p.logfile_send = None test_noecho() diff --git a/test/test_py_d/ts_opts.py b/test/test_py_d/ts_opts.py index 21a18570..8ebeaa6c 100755 --- a/test/test_py_d/ts_opts.py +++ b/test/test_py_d/ts_opts.py @@ -45,7 +45,7 @@ class TestSuiteOpts(TestSuiteBase): def opt_helpscreen(self): expect = r'OPTS.PY: Opts test.*USAGE:\s+opts.py' - if not opt.pexpect_spawn: + if not cfg.pexpect_spawn: expect += r'.*--minconf.*NOTES FOR THIS.*a note' t = self.do_run( ['--help'], expect, 0, regex=True ) if t.pexpect_spawn: @@ -57,17 +57,15 @@ class TestSuiteOpts(TestSuiteBase): return self.check_vals( [], ( - ('opt.foo', 'None'), # added opt - ('opt.print_checksum', 'None'), # sets 'quiet' - ('opt.quiet', 'False'), # init_opts, incompatible_opts - ('opt.verbose', 'None'), # init_opts, incompatible_opts - ('opt.fee_estimate_mode', 'conservative'), # autoset_opts - ('opt.passwd_file', 'None'), # infile_opts - check_infile() - ('opt.outdir', 'None'), # check_outdir() - ('opt.cached_balances', 'None'), # opt_sets_global - ('opt.minconf', '1'), # global_sets_opt - ('g.cached_balances', 'None'), - ('g.minconf', '1'), + ('cfg.foo', 'None'), # added opt + ('cfg.print_checksum', 'None'), # sets 'quiet' + ('cfg.quiet', 'False'), # init_opts, incompatible_opts + ('cfg.verbose', 'False'), # init_opts, incompatible_opts + ('cfg.passwd_file', ''), # infile_opts - check_infile() + ('cfg.outdir', ''), # check_outdir() + ('cfg.cached_balances', 'False'), + ('cfg.minconf', '1'), + ('cfg.fee_estimate_mode', 'conservative'), # autoset_opts ) ) @@ -85,14 +83,13 @@ class TestSuiteOpts(TestSuiteBase): f'--hidden-incog-input-params={pf},123', ], ( - ('opt.print_checksum', 'True'), - ('opt.quiet', 'True'), # set by print_checksum - ('opt.fee_estimate_mode', 'economical'), - ('opt.passwd_file', pf), - ('opt.outdir', self.tmpdir), - ('opt.cached_balances', 'True'), - ('opt.hidden_incog_input_params', pf+',123'), - ('g.cached_balances', 'True'), + ('cfg.print_checksum', 'True'), + ('cfg.quiet', 'True'), # set by print_checksum + ('cfg.passwd_file', pf), + ('cfg.outdir', self.tmpdir), + ('cfg.cached_balances', 'True'), + ('cfg.hidden_incog_input_params', pf+',123'), + ('cfg.fee_estimate_mode', 'economical'), ) ) diff --git a/test/test_py_d/ts_ref.py b/test/test_py_d/ts_ref.py index c51fbabc..caa47eaf 100755 --- a/test/test_py_d/ts_ref.py +++ b/test/test_py_d/ts_ref.py @@ -21,8 +21,6 @@ test.test_py_d.ts_ref: Reference file tests for the test.py test suite """ import os -from mmgen.globalvars import g -from mmgen.opts import opt from mmgen.wallet import get_wallet_cls from ..include.common import * from .common import * @@ -282,11 +280,11 @@ class TestSuiteRef(TestSuiteBase,TestSuiteShared): def ref_tool_decrypt(self): f = joinpath(ref_dir,ref_enc_fn) - if not g.debug_utf8: + if not cfg.debug_utf8: disable_debug() dec_file = joinpath(self.tmpdir,'famous.txt') t = self.spawn('mmgen-tool', ['-q','decrypt',f,'outfile='+dec_file,'hash_preset=1']) - if not g.debug_utf8: + if not cfg.debug_utf8: restore_debug() t.passphrase('data',tool_enc_passwd) t.written_to_file('Decrypted data') diff --git a/test/test_py_d/ts_ref_3seed.py b/test/test_py_d/ts_ref_3seed.py index af116f00..13d69f23 100755 --- a/test/test_py_d/ts_ref_3seed.py +++ b/test/test_py_d/ts_ref_3seed.py @@ -21,8 +21,6 @@ test.test_py_d.ts_ref_3seed: Saved and generated reference file tests for 128, 192 and 256-bit seeds for the test.py test suite """ -from mmgen.globalvars import g -from mmgen.opts import opt from mmgen.wallet import get_wallet_cls from ..include.common import * from .common import * @@ -121,7 +119,7 @@ class TestSuiteRef3Seed(TestSuiteBase,TestSuiteShared): ] slarg = [f'-l{self.seed_len} '] hparg = ['-p1'] - if wtype == 'hic_wallet_old' and opt.profile: + if wtype == 'hic_wallet_old' and cfg.profile: msg('') t = self.spawn('mmgen-walletchk', slarg + hparg + of_arg + ic_arg, @@ -155,7 +153,7 @@ class TestSuiteRef3Seed(TestSuiteBase,TestSuiteShared): pat = r'{}-[0-9A-F]{{8}}\[{},1\]{}.mmdat'.format( self.chk_data['sids'][idx], self.chk_data['lens'][idx], - '-α' if g.debug_utf8 else '') + '-α' if cfg.debug_utf8 else '') assert re.match(pat,fn), f'{pat} != {fn}' sid = os.path.basename(fn.split('-')[0]) cmp_or_die(sid,self.seed_id,desc='Seed ID') @@ -178,7 +176,7 @@ class TestSuiteRef3Seed(TestSuiteBase,TestSuiteShared): cmp_or_die('{}[{}]{}.{}'.format( sid, slen, - '-α' if g.debug_utf8 else '', + '-α' if cfg.debug_utf8 else '', wcls.ext), fn ) return t @@ -192,7 +190,7 @@ class TestSuiteRef3Seed(TestSuiteBase,TestSuiteShared): def ref_walletconv_incog(self,ofmt='incog',ext='mmincog'): args = ['-r0','-p1'] - pat = r'{}-[0-9A-F]{{8}}-[0-9A-F]{{8}}\[{},1\]' + ('-α' if g.debug_utf8 else '') + '.' + ext + pat = r'{}-[0-9A-F]{{8}}-[0-9A-F]{{8}}\[{},1\]' + ('-α' if cfg.debug_utf8 else '') + '.' + ext return self.ref_walletconv(ofmt=ofmt,extra_args=args,re_pat=pat) def ref_walletconv_hexincog(self): diff --git a/test/test_py_d/ts_ref_altcoin.py b/test/test_py_d/ts_ref_altcoin.py index 623cde97..fcf86764 100755 --- a/test/test_py_d/ts_ref_altcoin.py +++ b/test/test_py_d/ts_ref_altcoin.py @@ -21,9 +21,8 @@ test.test_py_d.ts_ref_altcoin: Altcoin reference file tests for the test.py test """ import os -from mmgen.globalvars import g -from mmgen.opts import opt from .common import * +from ..include.common import cfg from .ts_ref import * from .ts_base import * @@ -101,7 +100,7 @@ class TestSuiteRefAltcoin(TestSuiteRef,TestSuiteBase): ref_dir, self._get_ref_subdir_by_coin(coin), fn ) - proto = MMGenTxFile.get_proto(txfile,quiet_open=True) + proto = MMGenTxFile.get_proto(cfg,txfile,quiet_open=True) if proto.sign_mode == 'daemon': start_test_daemons(proto.network_id) set_vt100() diff --git a/test/test_py_d/ts_regtest.py b/test/test_py_d/ts_regtest.py index a4ff238a..daab4cf4 100755 --- a/test/test_py_d/ts_regtest.py +++ b/test/test_py_d/ts_regtest.py @@ -22,8 +22,6 @@ test.test_py_d.ts_regtest: Regtest tests for the test.py test suite import os,json,time from decimal import Decimal -from mmgen.globalvars import g -from mmgen.opts import opt from mmgen.util import die,gmsg from mmgen.protocol import init_proto from mmgen.addrlist import AddrList @@ -135,6 +133,7 @@ rt_data = { def make_burn_addr(proto): from mmgen.tool.coin import tool_cmd return tool_cmd( + cfg = cfg, cmdname = 'pubhash2addr', proto = proto, mmtype = 'compressed' ).pubhash2addr('00'*20) @@ -416,7 +415,7 @@ class TestSuiteRegtest(TestSuiteBase,TestSuiteShared): return if self.proto.testnet: die(2,'--testnet and --regtest options incompatible with regtest test suite') - self.proto = init_proto(self.proto.coin,network='regtest',need_amt=True) + self.proto = init_proto( cfg, self.proto.coin, network='regtest', need_amt=True ) coin = self.proto.coin.lower() import test.test_py_d.ts_regtest as rt_mod @@ -425,7 +424,7 @@ class TestSuiteRegtest(TestSuiteBase,TestSuiteShared): if self.proto.coin == 'BTC': self.test_rbf = True # tests are non-coin-dependent, so run just once for BTC - if g.test_suite_deterministic: + if cfg.test_suite_deterministic: self.deterministic = True self.miner_addr = 'bcrt1qaq8t3pakcftpk095tnqfv5cmmczysls024atnd' # regtest.create_hdseed() self.miner_wif = 'cTyMdQ2BgfAsjopRVZrj7AoEGp97pKfrC2NkqLuwHr4KHfPNAKwp' @@ -443,7 +442,7 @@ class TestSuiteRegtest(TestSuiteBase,TestSuiteShared): def _add_comments_to_addr_file(self,addrfile,outfile,use_comments=False): silence() gmsg(f'Adding comments to address file {addrfile!r}') - a = AddrList(self.proto,addrfile) + a = AddrList( cfg, self.proto, addrfile ) for n,idx in enumerate(a.idxs(),1): if use_comments: a.set_comment(idx,get_comment()) @@ -452,7 +451,7 @@ class TestSuiteRegtest(TestSuiteBase,TestSuiteShared): af = a.get_file() af.format(add_comments=True) from mmgen.fileutil import write_data_to_file - write_data_to_file(outfile,af.fmt_data,quiet=True,ignore_opt_outdir=True) + write_data_to_file(cfg,outfile,af.fmt_data,quiet=True,ignore_opt_outdir=True) end_silence() def setup(self): @@ -499,7 +498,7 @@ class TestSuiteRegtest(TestSuiteBase,TestSuiteShared): def _get_user_subsid(self,user,subseed_idx): fn = get_file_with_ext(self._user_dir(user),dfl_wcls.ext) silence() - w = Wallet( fn=fn, passwd_file=os.path.join(self.tmpdir,'wallet_password') ) + w = Wallet( cfg, fn=fn, passwd_file=os.path.join(self.tmpdir,'wallet_password') ) end_silence() return w.seed.subseed(subseed_idx).sid @@ -530,7 +529,7 @@ class TestSuiteRegtest(TestSuiteBase,TestSuiteShared): addrfile = joinpath(self._user_dir(user), '{}{}{}[{}]{x}.regtest.addrs'.format( sid,self.altcoin_pfx,id_strs[desc],addr_range, - x='-α' if g.debug_utf8 else '')) + x='-α' if cfg.debug_utf8 else '')) if mmtype == self.proto.mmtypes[0] and user == 'bob': self._add_comments_to_addr_file(addrfile,addrfile,use_comments=True) t = self.spawn( @@ -541,7 +540,7 @@ class TestSuiteRegtest(TestSuiteBase,TestSuiteShared): (['--batch'] if batch else []) + [addrfile] ), extra_desc = f'({desc})' ) - if g.debug: + if cfg.debug: t.expect("Type uppercase 'YES' to confirm: ",'YES\n') t.expect('Importing') if batch: @@ -598,7 +597,7 @@ class TestSuiteRegtest(TestSuiteBase,TestSuiteShared): return 'skip' self.spawn('',msg_only=True) from mmgen.proto.btc.regtest import MMGenRegtest - rt = MMGenRegtest(self.proto.coin) + rt = MMGenRegtest(cfg,self.proto.coin) await rt.stop() from shutil import rmtree imsg(f'Deleting Bob’s old tracking wallet') @@ -888,10 +887,10 @@ class TestSuiteRegtest(TestSuiteBase,TestSuiteShared): def get_addr_from_addrlist(self,user,sid,mmtype,idx,addr_range='1-5'): id_str = { 'L':'', 'S':'-S', 'C':'-C', 'B':'-B' }[mmtype] ext = '{}{}{}[{}]{x}.regtest.addrs'.format( - sid,self.altcoin_pfx,id_str,addr_range,x='-α' if g.debug_utf8 else '') + sid,self.altcoin_pfx,id_str,addr_range,x='-α' if cfg.debug_utf8 else '') addrfile = get_file_with_ext(self._user_dir(user),ext,no_dot=True) silence() - addr = AddrList(self.proto,addrfile).data[idx].addr + addr = AddrList( cfg, self.proto, addrfile ).data[idx].addr end_silence() return addr @@ -909,7 +908,7 @@ class TestSuiteRegtest(TestSuiteBase,TestSuiteShared): def bob_rbf_1output_bump(self): if not self.test_rbf: return 'skip' - ext = '9343,3]{x}.regtest.rawtx'.format(x='-α' if g.debug_utf8 else '') + ext = '9343,3]{x}.regtest.rawtx'.format(x='-α' if cfg.debug_utf8 else '') txfile = get_file_with_ext(self.tr.trash_dir,ext,delete=False,no_dot=True) return self.user_txbump('bob', self.tr.trash_dir, @@ -958,7 +957,7 @@ class TestSuiteRegtest(TestSuiteBase,TestSuiteShared): return t def bob_rbf_bump(self): - ext = ',{}]{x}.regtest.sigtx'.format(rtFee[1][:-1],x='-α' if g.debug_utf8 else '') + ext = ',{}]{x}.regtest.sigtx'.format(rtFee[1][:-1],x='-α' if cfg.debug_utf8 else '') txfile = self.get_file_with_ext(ext,delete=False,no_dot=True) return self.user_txbump('bob',self.tmpdir,txfile,rtFee[2],add_args=['--send']) @@ -969,10 +968,10 @@ class TestSuiteRegtest(TestSuiteBase,TestSuiteShared): return t def _get_mempool(self): - if not g.debug_utf8: + if not cfg.debug_utf8: disable_debug() ret = self.spawn('mmgen-regtest',['mempool']).read() - if not g.debug_utf8: + if not cfg.debug_utf8: restore_debug() m = re.search(r'(\[\s*"[a-f0-9]{64}"\s*])',ret) # allow for extra output by handler at end return json.loads(m.group(1)) @@ -987,7 +986,7 @@ class TestSuiteRegtest(TestSuiteBase,TestSuiteShared): def bob_rbf_status(self,fee,exp1,exp2=''): if not self.proto.cap('rbf'): return 'skip' - ext = ',{}]{x}.regtest.sigtx'.format(fee[:-1],x='-α' if g.debug_utf8 else '') + ext = ',{}]{x}.regtest.sigtx'.format(fee[:-1],x='-α' if cfg.debug_utf8 else '') txfile = self.get_file_with_ext(ext,delete=False,no_dot=True) return self.user_txsend_status('bob',txfile,exp1,exp2) @@ -1043,7 +1042,7 @@ class TestSuiteRegtest(TestSuiteBase,TestSuiteShared): def _gen_pairs(self,n): from mmgen.tool.api import tool_api - t = tool_api() + t = tool_api(cfg) t.init_coin(self.proto.coin,self.proto.network) def gen_addr(Type): @@ -1061,7 +1060,7 @@ class TestSuiteRegtest(TestSuiteBase,TestSuiteShared): def user_import(self,user,args,nAddr): t = self.spawn('mmgen-addrimport',['--'+user]+args) - if g.debug: + if cfg.debug: t.expect("Type uppercase 'YES' to confirm: ",'YES\n') t.expect(f'Importing {nAddr} address') if '--rescan' in args: @@ -1272,7 +1271,7 @@ class TestSuiteRegtest(TestSuiteBase,TestSuiteShared): def bob_edit_json_twdump(self): self.spawn('',msg_only=True) from mmgen.tw.json import TwJSON - fn = TwJSON.Base(self.proto).dump_fn + fn = TwJSON.Base(cfg,self.proto).dump_fn text = json.loads(self.read_from_tmpfile(fn)) text['data']['entries'][3][3] = f'edited comment [фубар] [{gr_uc}]' self.write_to_tmpfile( fn, json.dumps(text,indent=4) ) @@ -1280,7 +1279,7 @@ class TestSuiteRegtest(TestSuiteBase,TestSuiteShared): def carol_twimport(self,rpc_backend='http',add_parms=[],expect_str=None,expect_str2='Found 1 unspent output'): from mmgen.tw.json import TwJSON - fn = joinpath( self.tmpdir, TwJSON.Base(self.proto).dump_fn ) + fn = joinpath( self.tmpdir, TwJSON.Base(cfg,self.proto).dump_fn ) t = self.spawn( 'mmgen-tool', ([f'--rpc-backend={rpc_backend}'] if rpc_backend else []) @@ -1313,7 +1312,7 @@ class TestSuiteRegtest(TestSuiteBase,TestSuiteShared): t = self.spawn('mmgen-regtest',['cli','unloadwallet','carol']) t.ok() from mmgen.rpc import rpc_init - rpc = await rpc_init(self.proto) + rpc = await rpc_init(cfg,self.proto) wdir = joinpath(rpc.daemon.network_datadir,'wallets','carol') from shutil import rmtree imsg(f'Deleting Carol’s tracking wallet') @@ -1386,7 +1385,7 @@ class TestSuiteRegtest(TestSuiteBase,TestSuiteShared): def alice_add_comment_badaddr2(self): # mainnet zero address: - addr = init_proto(self.proto.coin,network='mainnet').pubhash2addr(bytes(20),False) # mainnet zero address + addr = init_proto( cfg, self.proto.coin, network='mainnet' ).pubhash2addr(bytes(20),False) return self.alice_add_comment_badaddr( addr, 'invalid address', 2 ) def alice_add_comment_badaddr3(self): @@ -1513,7 +1512,7 @@ class TestSuiteRegtest(TestSuiteBase,TestSuiteShared): s, regex=True ) if t.pexpect_spawn and s == 'w': - t.expect(r'Total.*','q',regex=True,delay=1 if opt.exact_output else t.send_delay) + t.expect(r'Total.*','q',regex=True,delay=1 if cfg.exact_output else t.send_delay) time.sleep(t.send_delay) t.send('e') return t @@ -1799,7 +1798,7 @@ class TestSuiteRegtest(TestSuiteBase,TestSuiteShared): 'contains no unused addresses of address type' ) def stop(self): - if opt.no_daemon_stop: + if cfg.no_daemon_stop: self.spawn('',msg_only=True) msg_r('(leaving daemon running by user request)') return 'ok' diff --git a/test/test_py_d/ts_seedsplit.py b/test/test_py_d/ts_seedsplit.py index d09b8bd8..fcafdf7f 100755 --- a/test/test_py_d/ts_seedsplit.py +++ b/test/test_py_d/ts_seedsplit.py @@ -20,8 +20,6 @@ test.test_py_d.ts_seedsplit: Seed split/join tests for the test.py test suite """ -from mmgen.globalvars import g -from mmgen.opts import opt from mmgen.wallet import get_wallet_cls from .ts_base import * diff --git a/test/test_py_d/ts_shared.py b/test/test_py_d/ts_shared.py index 56fe7899..1d6789e9 100755 --- a/test/test_py_d/ts_shared.py +++ b/test/test_py_d/ts_shared.py @@ -21,8 +21,6 @@ test.test_py_d.ts_shared: Shared methods for the test.py test suite """ import os -from mmgen.globalvars import g -from mmgen.opts import opt from mmgen.util import ymsg from mmgen.wallet import get_wallet_cls diff --git a/test/test_py_d/ts_tool.py b/test/test_py_d/ts_tool.py index d236e4c8..bad2c55d 100755 --- a/test/test_py_d/ts_tool.py +++ b/test/test_py_d/ts_tool.py @@ -87,7 +87,7 @@ class TestSuiteTool(TestSuiteMain,TestSuiteBase): return t def tool_extract_key_from_geth_wallet(self): - if opt.no_altcoin: + if cfg.no_altcoin: return 'skip' fn = 'test/ref/ethereum/geth-wallet.json' key = '9627ddb68354f5e0ff45fb2da49d7a20a013b7257a83ef4adbbbd87aeaccc75e' @@ -99,7 +99,7 @@ class TestSuiteTool(TestSuiteMain,TestSuiteBase): def tool_api(self): t = self.spawn( 'tool_api_test.py', - (['no_altcoin'] if opt.no_altcoin else []), + (['no_altcoin'] if cfg.no_altcoin else []), cmd_dir = 'test/misc' ) t.expect('legacy.*compressed.*segwit.*bech32',regex=True) return t diff --git a/test/test_py_d/ts_wallet.py b/test/test_py_d/ts_wallet.py index 83391e3c..11a72fb9 100755 --- a/test/test_py_d/ts_wallet.py +++ b/test/test_py_d/ts_wallet.py @@ -21,7 +21,6 @@ test.test_py_d.ts_wallet: Wallet conversion tests for the test.py test suite """ import os -from mmgen.opts import opt from mmgen.wallet import get_wallet_cls from .common import * from .ts_base import * @@ -192,7 +191,7 @@ class TestSuiteWalletConv(TestSuiteBase,TestSuiteShared): wf = t.written_to_file(capfirst(ocls.desc),oo=oo) t.p.wait() # back check of result - msg('' if opt.profile else ' OK') + msg('' if cfg.profile else ' OK') return self.walletchk( wf, pf = None, extra_desc = '(check)', @@ -222,7 +221,7 @@ class TestSuiteWalletConv(TestSuiteBase,TestSuiteShared): if wcls.type == 'incog_hidden': add_args += uopts_chk wf = None - msg('' if opt.profile else ' OK') + msg('' if cfg.profile else ' OK') return self.walletchk( wf, pf = pf, wcls = wcls, diff --git a/test/test_py_d/ts_xmrwallet.py b/test/test_py_d/ts_xmrwallet.py index 25ed8d94..696f4d10 100755 --- a/test/test_py_d/ts_xmrwallet.py +++ b/test/test_py_d/ts_xmrwallet.py @@ -24,7 +24,6 @@ import sys,os,atexit,asyncio,shutil from subprocess import run,PIPE from mmgen.globalvars import gc -from mmgen.opts import opt from mmgen.obj import MMGenRange from mmgen.amt import XMRAmt from mmgen.addrlist import KeyAddrList,AddrIdxList @@ -84,7 +83,7 @@ class TestSuiteXMRWallet(TestSuiteBase): return from mmgen.protocol import init_proto - self.proto = init_proto('XMR',network='mainnet') + self.proto = init_proto( cfg, 'XMR', network='mainnet' ) self.datadir_base = os.path.join('test','daemons','xmrtest') self.extra_opts = ['--wallet-rpc-password=passw0rd'] self.init_users() @@ -99,11 +98,11 @@ class TestSuiteXMRWallet(TestSuiteBase): self.tx_relay_daemon_proxy_parm = ( self.tx_relay_daemon_parm + f':127.0.0.1:{self.socks_port}' ) # must be IP, not 'localhost' - if not opt.no_daemon_stop: + if not cfg.no_daemon_stop: atexit.register(self.stop_daemons) atexit.register(self.stop_miner_wallet_daemon) - if not opt.no_daemon_autostart: + if not cfg.no_daemon_autostart: self.stop_daemons() shutil.rmtree(self.datadir_base,ignore_errors=True) os.makedirs(self.datadir_base) @@ -123,7 +122,7 @@ class TestSuiteXMRWallet(TestSuiteBase): else: return True def start_proxy(): - if external_call or not opt.no_daemon_autostart: + if external_call or not cfg.no_daemon_autostart: run(a+b2) omsg(f'SSH SOCKS server started, listening at localhost:{cls.socks_port}') @@ -174,7 +173,7 @@ class TestSuiteXMRWallet(TestSuiteBase): """,indent=' ',strip_char='\t')) from mmgen.ui import keypress_confirm - if keypress_confirm('Continue?'): + if keypress_confirm(cfg,'Continue?'): start_proxy() else: die(1,'Exiting at user request') @@ -187,7 +186,7 @@ class TestSuiteXMRWallet(TestSuiteBase): Then restart the test. """,indent=' ')) - if not (external_call or opt.no_daemon_stop): + if not (external_call or cfg.no_daemon_stop): atexit.register(kill_proxy) return True @@ -222,6 +221,7 @@ class TestSuiteXMRWallet(TestSuiteBase): udir = os.path.join('test',f'tmp{n}',user) datadir = os.path.join(self.datadir_base,user) md = CoinDaemon( + cfg = cfg, proto = self.proto, test_suite = True, port_shift = shift, @@ -229,6 +229,7 @@ class TestSuiteXMRWallet(TestSuiteBase): datadir = datadir ) md_rpc = MoneroRPCClient( + cfg = cfg, proto = self.proto, host = md.host, port = md.rpc_port, @@ -238,6 +239,7 @@ class TestSuiteXMRWallet(TestSuiteBase): daemon = md, ) wd = MoneroWalletDaemon( + cfg = cfg, proto = self.proto, test_suite = True, wallet_dir = udir, @@ -248,6 +250,7 @@ class TestSuiteXMRWallet(TestSuiteBase): daemon_addr = f'127.0.0.1:{md.rpc_port}', ) wd_rpc = MoneroWalletRPCClient( + cfg = cfg, daemon = wd, test_connection = False, ) @@ -316,6 +319,7 @@ class TestSuiteXMRWallet(TestSuiteBase): self.extra_opts + dir_opt + [ 'create', data.kafile, (wallet or data.kal_range) ] ) for i in MMGenRange(wallet or data.kal_range).items: write_data_to_file( + cfg, self.users[user].addrfile_fs.format(i), t.expect_getend('Address: '), quiet = True @@ -570,16 +574,16 @@ class TestSuiteXMRWallet(TestSuiteBase): async def open_wallet_user(self,user,wnum): data = self.users[user] silence() - kal = KeyAddrList(self.proto,data.kafile,key_address_validity_check=False) + kal = KeyAddrList( cfg, self.proto, data.kafile, key_address_validity_check=False ) end_silence() - self.users[user].wd.start(silent=not (opt.exact_output or opt.verbose)) + self.users[user].wd.start(silent=not (cfg.exact_output or cfg.verbose)) return data.wd_rpc.call( 'open_wallet', filename = os.path.basename(data.walletfile_fs.format(wnum)), password = kal.entry(wnum).wallet_passwd ) async def stop_wallet_user(self,user): - await self.users[user].wd_rpc.stop_daemon(silent=not (opt.exact_output or opt.verbose)) + await self.users[user].wd_rpc.stop_daemon(silent=not (cfg.exact_output or cfg.verbose)) return 'ok' # mining methods @@ -639,7 +643,7 @@ class TestSuiteXMRWallet(TestSuiteBase): async def send_random_txs(): from mmgen.tool.api import tool_api - t = tool_api() + t = tool_api(cfg) t.init_coin('XMR','mainnet') t.usr_randchars = 0 imsg_r(f'Sending random transactions: ') diff --git a/test/tooltest.py b/test/tooltest.py index 0c1f863c..8595e6d2 100755 --- a/test/tooltest.py +++ b/test/tooltest.py @@ -55,12 +55,15 @@ If no command is given, the whole suite of tests is run. sys.argv = [sys.argv[0]] + ['--skip-cfg-file'] + sys.argv[1:] -cmd_args = opts.init(opts_data) +cfg = opts.init(opts_data) -assert opt.type in (None,'zcash_z'), 'Only zcash-z permitted for --type argument' +set_globals(cfg) -from mmgen.protocol import init_proto_from_opts -proto = init_proto_from_opts() +vmsg = cfg._util.vmsg + +proto = cfg._proto + +assert cfg.type in (None,'zcash_z'), 'Only zcash-z permitted for --type argument' cmd_data = { 'cryptocoin': { @@ -100,11 +103,11 @@ if proto.coin in ('BTC','LTC'): 'pipetest': ('randpair','o3') }) -if proto.coin == 'XMR' or opt.type == 'zcash_z': +if proto.coin == 'XMR' or cfg.type == 'zcash_z': del cmd_data['cryptocoin']['cmd_data']['pubhash2addr'] del cmd_data['cryptocoin']['cmd_data']['addr2pubhash'] -cfg = { +tcfg = { 'name': 'the tool utility', 'enc_passwd': 'Ten Satoshis', 'tmpdir': 'test/tmp/10', @@ -132,23 +135,24 @@ tn_ext = ('','.testnet')[proto.testnet] mmgen_cmd = 'mmgen-tool' -if not opt.system: +if not cfg.system: os.environ['PYTHONPATH'] = repo_root mmgen_cmd = os.path.relpath(os.path.join(repo_root,'cmds',mmgen_cmd)) spawn_cmd = ['scripts/exec_wrapper.py',mmgen_cmd] -if opt.coverage: +if cfg.coverage: d,f = init_coverage() spawn_cmd = ['python3','-m','trace','--count','--coverdir='+d,'--file='+f] + spawn_cmd elif gc.platform == 'win': spawn_cmd = ['python3'] + spawn_cmd -add_spawn_args = ['--data-dir='+cfg['tmpdir']] + ['--{}{}'.format( - k.replace('_','-'),'='+getattr(opt,k) if getattr(opt,k) != True else '') - for k in ('testnet','rpc_host','regtest','coin','type') if getattr(opt,k)] +add_spawn_args = ['--data-dir='+tcfg['tmpdir']] + ['--{}{}'.format( + k.replace('_','-'), + '='+getattr(cfg,k) if getattr(cfg,k) != True else '') + for k in ('testnet','rpc_host','regtest','coin','type') if getattr(cfg,k)] -if opt.list_cmds: +if cfg.list_cmds: fs = ' {:<{w}} - {}' Msg('Available commands:') w = max(map(len,cmd_data)) @@ -157,7 +161,7 @@ if opt.list_cmds: Msg('\nAvailable utilities:') Msg(fs.format('clean','Clean the tmp directory',w=w)) sys.exit(0) -if opt.list_names: +if cfg.list_names: tcmd = ['python3','test/tooltest2.py','--list-tested-cmds'] tested_in = { 'tooltest.py': [], @@ -198,12 +202,12 @@ def is_coin_addr_loc(s): msg_w = 35 def test_msg(m): m2 = f'Testing {m}' - msg_r(green(m2+'\n') if opt.verbose else '{:{w}}'.format( m2, w=msg_w+8 )) + msg_r(green(m2+'\n') if cfg.verbose else '{:{w}}'.format( m2, w=msg_w+8 )) -compressed = opt.type or ('','compressed')['C' in proto.mmtypes] +compressed = cfg.type or ('','compressed')['C' in proto.mmtypes] segwit = ('','segwit')['S' in proto.mmtypes] bech32 = ('','bech32')['B' in proto.mmtypes] -type_compressed_arg = ([],['--type=' + (opt.type or 'compressed')])[bool(opt.type) or 'C' in proto.mmtypes] +type_compressed_arg = ([],['--type=' + (cfg.type or 'compressed')])[bool(cfg.type) or 'C' in proto.mmtypes] type_segwit_arg = ([],['--type=segwit'])['S' in proto.mmtypes] type_bech32_arg = ([],['--type=bech32'])['B' in proto.mmtypes] @@ -213,7 +217,7 @@ class MMGenToolTestUtils(object): sys_cmd = ( spawn_cmd + add_spawn_args + - ['-r0','-d',cfg['tmpdir']] + + ['-r0','-d',tcfg['tmpdir']] + add_opts + [name.lower()] + tool_args + @@ -222,7 +226,7 @@ class MMGenToolTestUtils(object): if extra_msg: extra_msg = f'({extra_msg})' full_name = ' '.join([name.lower()]+add_opts+kwargs.split()+extra_msg.split()) if not silent: - if opt.verbose: + if cfg.verbose: sys.stderr.write(green(f'Testing {full_name}\nExecuting ')) sys.stderr.write(cyan(' '.join(sys_cmd)+'\n')) else: @@ -231,7 +235,7 @@ class MMGenToolTestUtils(object): cp = run(sys_cmd,stdout=PIPE,stderr=PIPE) out = cp.stdout err = cp.stderr - if opt.debug: + if cfg.debug: try: dmsg(err.decode()) except: dmsg(repr(err)) if not binary: @@ -267,14 +271,14 @@ class MMGenToolTestUtils(object): def run_cmd_out(self,name,carg=None,Return=False,kwargs='',fn_idx='',extra_msg='', literal=False,chkdata='',hush=False,add_opts=[]): if carg: - write_to_tmpfile(cfg,f'{name}{fn_idx}.in',carg+'\n') + write_to_tmpfile(tcfg,f'{name}{fn_idx}.in',carg+'\n') ret = self.run_cmd(name,([],[carg])[bool(carg)],kwargs=kwargs, extra_msg=extra_msg,add_opts=add_opts) if carg: vmsg('In: ' + repr(carg)) vmsg('Out: ' + (repr(ret),ret)[literal]) if ret or ret == '': - write_to_tmpfile(cfg,f'{name}{fn_idx}.out',ret+'\n') + write_to_tmpfile(tcfg,f'{name}{fn_idx}.out',ret+'\n') if chkdata: cmp_or_die(ret,chkdata) return @@ -287,10 +291,10 @@ class MMGenToolTestUtils(object): def run_cmd_randinput(self,name,strip=True,add_opts=[]): s = getrand(128) fn = name+'.in' - write_to_tmpfile(cfg,fn,s,binary=True) - ret = self.run_cmd(name,[get_tmpfile(cfg,fn)],strip=strip,add_opts=add_opts) + write_to_tmpfile(tcfg,fn,s,binary=True) + ret = self.run_cmd(name,[get_tmpfile(tcfg,fn)],strip=strip,add_opts=add_opts) fn = name+'.out' - write_to_tmpfile(cfg,fn,ret+'\n') + write_to_tmpfile(tcfg,fn,ret+'\n') ok() vmsg(f'Returned: {ret}') @@ -342,7 +346,7 @@ class MMGenToolTestCmds(object): for n,k in enumerate(('',compressed,segwit,bech32)): ao = ['--type='+k] if k else [] ret = tu.run_cmd(name,[keys[n]],add_opts=ao).rstrip() - iaddr = read_from_tmpfile(cfg,f'randpair{n+1}.out').split()[-1] + iaddr = read_from_tmpfile(tcfg,f'randpair{n+1}.out').split()[-1] vmsg(f'Out: {ret}') cmp_or_die(iaddr,ret) ok() @@ -371,16 +375,16 @@ class MMGenToolTestCmds(object): def pubhex2redeem_script(self,name,f1,f2,f3): # from above addr = read_from_file(f3).strip() tu.run_cmd_out(name,addr,add_opts=type_segwit_arg,fn_idx=3) - rs = read_from_tmpfile(cfg,'privhex2pubhex3.out').strip() + rs = read_from_tmpfile(tcfg,'privhex2pubhex3.out').strip() tu.run_cmd_out('pubhex2addr',rs,add_opts=type_segwit_arg,fn_idx=3,hush=True) - addr1 = read_from_tmpfile(cfg,'pubhex2addr3.out').strip() - addr2 = read_from_tmpfile(cfg,'randpair3.out').split()[1] + addr1 = read_from_tmpfile(tcfg,'pubhex2addr3.out').strip() + addr2 = read_from_tmpfile(tcfg,'randpair3.out').split()[1] cmp_or_die(addr1,addr2) ok() def wif2redeem_script(self,name,f1,f2,f3): # compare output with above wif = read_from_file(f3).split()[0] ret1 = tu.run_cmd_out(name,wif,add_opts=type_segwit_arg,fn_idx=3,Return=True) - ret2 = read_from_tmpfile(cfg,'pubhex2redeem_script3.out').strip() + ret2 = read_from_tmpfile(tcfg,'pubhex2redeem_script3.out').strip() cmp_or_die(ret1,ret2) ok() def wif2segwit_pair(self,name,f1,f2): # does its own checking, so just run @@ -400,10 +404,10 @@ class MMGenToolTestCmds(object): a=' '.join(add_spawn_args), wif=wif) test_msg('command piping') - if opt.verbose: + if cfg.verbose: sys.stderr.write(green('Executing ') + cyan(cmd) + '\n') res = run(cmd,stdout=PIPE,shell=True).stdout.decode().strip() - addr = read_from_tmpfile(cfg,'wif2addr3.out').strip() + addr = read_from_tmpfile(tcfg,'wif2addr3.out').strip() cmp_or_die(addr,res) ok() @@ -426,7 +430,7 @@ class MMGenToolTestCmds(object): # main() import time start_time = int(time.time()) -mk_tmpdir(cfg['tmpdir']) +mk_tmpdir(tcfg['tmpdir']) def gen_deps_for_cmd(cmd,cdata): fns = [] @@ -446,25 +450,25 @@ def do_cmds(cmd_group): gdata = cmd_data[cmd_group]['cmd_data'] for cmd in gdata: fns = gen_deps_for_cmd(cmd,gdata[cmd]) - cmdline = [cmd] + [os.path.join(cfg['tmpdir'],fn) for fn in fns] + cmdline = [cmd] + [os.path.join(tcfg['tmpdir'],fn) for fn in fns] getattr(tc,cmd)(*cmdline) try: - if cmd_args: - if len(cmd_args) != 1: + if cfg._args: + if len(cfg._args) != 1: die(1,'Only one command may be specified') - cmd = cmd_args[0] + cmd = cfg._args[0] if cmd in cmd_data: - cleandir(cfg['tmpdir'],do_msg=True) + cleandir(tcfg['tmpdir'],do_msg=True) msg('Running tests for {}:'.format( cmd_data[cmd]['desc'] )) do_cmds(cmd) elif cmd == 'clean': - cleandir(cfg['tmpdir'],do_msg=True) + cleandir(tcfg['tmpdir'],do_msg=True) sys.exit(0) else: die(1,f'{cmd!r}: unrecognized command') else: - cleandir(cfg['tmpdir'],do_msg=True) + cleandir(tcfg['tmpdir'],do_msg=True) for cmd in cmd_data: msg('Running tests for {}:'.format( cmd_data[cmd]['desc'] )) do_cmds(cmd) diff --git a/test/tooltest2.py b/test/tooltest2.py index ae55ed65..e27e36e6 100755 --- a/test/tooltest2.py +++ b/test/tooltest2.py @@ -32,7 +32,7 @@ from test.overlay import overlay_setup sys.path.insert(0,overlay_setup(repo_root)) from mmgen.common import * -from test.include.common import * +from test.include.common import set_globals,end_msg,sample_text from mmgen.bip39 import is_bip39_mnemonic from mmgen.baseconv import is_mmgen_mnemonic from mmgen.xmrseed import is_xmrseed @@ -89,6 +89,18 @@ If no command is given, the whole suite of tests is run. } } +sys.argv = [sys.argv[0]] + ['--skip-cfg-file'] + sys.argv[1:] + +cfg = opts.init( + opts_data, + init_opts = { + 'usr_randchars': 0, + 'hash_preset': '1', + 'passwd_file': 'test/ref/keyaddrfile_password', + }) + +set_globals(cfg) + sample_text_hexdump = ( '000000: 5468 6520 5469 6d65 7320 3033 2f4a 616e{n}' + '000010: 2f32 3030 3920 4368 616e 6365 6c6c 6f72{n}' + @@ -764,9 +776,9 @@ async def call_method(cls,method,cmd_name,args,out,opts,mmtype,stdin_input): ' '.join([cmd_name]+[repr(e) for e in args]), ' '+mmtype if mmtype else '' )) aargs,kwargs = main_tool.process_args(cmd_name,args,cls) - oq_save = bool(opt.quiet) - if not opt.verbose: - opt.quiet = True + oq_save = bool(cfg.quiet) + if not cfg.verbose: + cfg.quiet = True if stdin_input: fd0,fd1 = os.pipe() if os.fork(): # parent @@ -776,7 +788,7 @@ async def call_method(cls,method,cmd_name,args,out,opts,mmtype,stdin_input): cmd_out = method(*aargs,**kwargs) os.dup2(stdin_save,0) os.wait() - opt.quiet = oq_save + cfg.quiet = oq_save return cmd_out else: # child os.close(fd0) @@ -787,12 +799,12 @@ async def call_method(cls,method,cmd_name,args,out,opts,mmtype,stdin_input): ret = method(*aargs,**kwargs) if type(ret).__name__ == 'coroutine': ret = await ret - opt.quiet = oq_save + cfg.quiet = oq_save return ret def tool_api(cls,cmd_name,args,out,opts): from mmgen.tool.api import tool_api - tool = tool_api() + tool = tool_api(cfg) if opts: for o in opts: if o.startswith('--type='): @@ -832,7 +844,7 @@ async def run_test(cls,gid,cmd_name): # behavior is like test.py: run coin-dependent tests only if proto.testnet or proto.coin != BTC if gid in coin_dependent_groups: k = '{}_{}'.format( - ( g.token.lower() if proto.tokensym else proto.coin.lower() ), + ( cfg.token.lower() if proto.tokensym else proto.coin.lower() ), ('mainnet','testnet')[proto.testnet] ) if k in data: data = data[k] @@ -847,10 +859,10 @@ async def run_test(cls,gid,cmd_name): m = '{} {}{}'.format( purple('Testing'), - cmd_name if opt.names else docstring_head(getattr(cls,cmd_name)), + cmd_name if cfg.names else docstring_head(getattr(cls,cmd_name)), m2 ) - msg_r(green(m)+'\n' if opt.verbose else m) + msg_r(green(m)+'\n' if cfg.verbose else m) for d in data: args,out,opts,mmtype = d + tuple([None] * (4-len(d))) @@ -859,17 +871,17 @@ async def run_test(cls,gid,cmd_name): stdin_input = args[0] args[0] = '-' - if opt.tool_api: + if cfg.tool_api: if args and args[0 ]== '-': continue cmd_out = tool_api(cls,cmd_name,args,out,opts) - elif opt.fork: + elif cfg.fork: cmd_out = fork_cmd(cmd_name,args,out,opts,stdin_input) else: if stdin_input and gc.platform == 'win': msg('Skipping for MSWin - no os.fork()') continue - method = getattr(cls(cmdname=cmd_name,proto=proto,mmtype=mmtype),cmd_name) + method = getattr(cls(cfg,cmdname=cmd_name,proto=proto,mmtype=mmtype),cmd_name) cmd_out = await call_method(cls,method,cmd_name,args,out,opts,mmtype,stdin_input) try: @@ -886,15 +898,15 @@ async def run_test(cls,gid,cmd_name): out[1], func_out )) elif isinstance(out,(list,tuple)): - for co,o in zip(cmd_out.split(NL) if opt.fork else cmd_out,out): + for co,o in zip(cmd_out.split(NL) if cfg.fork else cmd_out,out): check_output(co,o) else: check_output(cmd_out,out) - if not opt.verbose: + if not cfg.verbose: msg_r('.') - if not opt.verbose: + if not cfg.verbose: msg('OK') def docstring_head(obj): @@ -904,16 +916,16 @@ async def do_group(gid): desc = f'command group {gid!r}' cls = main_tool.get_mod_cls(gid.lower()) qmsg(blue('Testing ' + - desc if opt.names else + desc if cfg.names else ( docstring_head(cls) or desc ) )) - for cmdname in cls().user_commands: + for cmdname in cls(cfg).user_commands: if cmdname in skipped_tests: continue if cmdname not in tests[gid]: m = f'No test for command {cmdname!r} in group {gid!r}!' - if opt.die_on_missing: + if cfg.die_on_missing: die(1,m+' Aborting') else: msg(m) @@ -933,52 +945,44 @@ def list_tested_cmds(): for gid in tests: Msg('\n'.join(tests[gid])) -sys.argv = [sys.argv[0]] + ['--skip-cfg-file'] + sys.argv[1:] +qmsg = cfg._util.qmsg +vmsg = cfg._util.vmsg -cmd_args = opts.init( - opts_data, - init_opts = { - 'usr_randchars': 0, - 'hash_preset': '1', - 'passwd_file': 'test/ref/keyaddrfile_password', - }) +proto = cfg._proto -from mmgen.protocol import init_proto_from_opts -proto = init_proto_from_opts(need_amt=True) - -if opt.tool_api: +if cfg.tool_api: del tests['Wallet'] del tests['File'] import mmgen.main_tool as main_tool -if opt.list_tests: +if cfg.list_tests: Msg('Available tests:') for modname,cmdlist in main_tool.mods.items(): cls = getattr(importlib.import_module(f'mmgen.tool.{modname}'),'tool_cmd') Msg(' {:6} - {}'.format( modname, docstring_head(cls) )) sys.exit(0) -if opt.list_tested_cmds: +if cfg.list_tested_cmds: list_tested_cmds() sys.exit(0) -if opt.system: +if cfg.system: tool_exec = 'mmgen-tool' sys.path.pop(0) else: os.environ['PYTHONPATH'] = repo_root tool_exec = os.path.relpath(os.path.join('cmds','mmgen-tool')) -if opt.fork: +if cfg.fork: passthru_args = ['coin','type','testnet','token'] tool_cmd = [ tool_exec, '--skip-cfg-file' ] + [ '--{}{}'.format( k.replace('_','-'), - '='+getattr(opt,k) if getattr(opt,k) != True else '') - for k in passthru_args if getattr(opt,k) ] + '='+getattr(cfg,k) if getattr(cfg,k) != True else '') + for k in passthru_args if getattr(cfg,k) ] - if opt.coverage: + if cfg.coverage: d,f = init_coverage() tool_cmd_preargs = ['python3','-m','trace','--count','--coverdir='+d,'--file='+f] else: @@ -988,8 +992,8 @@ start_time = int(time.time()) async def main(): try: - if cmd_args: - for cmd in cmd_args: + if cfg._args: + for cmd in cfg._args: if cmd in tests: await do_group(cmd) else: diff --git a/test/unit_tests.py b/test/unit_tests.py index 47cde32e..6de98c50 100755 --- a/test/unit_tests.py +++ b/test/unit_tests.py @@ -23,12 +23,12 @@ test/unit_tests.py: Unit tests for the MMGen suite import sys,os,time,importlib,platform from include.tests_header import repo_root -from include.common import end_msg from mmgen.devinit import init_dev init_dev() from mmgen.common import * +from test.include.common import set_globals,end_msg opts_data = { 'text': { @@ -54,10 +54,11 @@ If no test is specified, all available tests are run sys.argv.insert(1,'--skip-cfg-file') -opts.UserOpts._reset_ok += ('use_internal_keccak_module',) -g._reset_ok += ('debug_addrlist',) +cfg = opts.init(opts_data) -cmd_args = opts.init(opts_data) +type(cfg)._reset_ok += ('use_internal_keccak_module','debug_addrlist') + +set_globals(cfg) os.environ['PYTHONPATH'] = repo_root @@ -66,7 +67,7 @@ file_pfx = 'ut_' all_tests = sorted( [fn[3:-3] for fn in os.listdir(os.path.join(repo_root,'test','unit_tests_d')) if fn[:3] == file_pfx]) -exclude = opt.exclude.split(',') if opt.exclude else [] +exclude = cfg.exclude.split(',') if cfg.exclude else [] for e in exclude: if e not in all_tests: @@ -74,7 +75,7 @@ for e in exclude: start_time = int(time.time()) -if opt.list: +if cfg.list: Msg(' '.join(all_tests)) sys.exit(0) @@ -93,12 +94,12 @@ class UnitTestHelpers(object): m_noraise = "\nillegal action 'bad {}' failed to raise exception {!r}" for (desc,exc_chk,emsg_chk,func) in data: try: - vmsg_r(' bad {:{w}}'.format( desc+':', w=desc_w+1 )) + cfg._util.vmsg_r(' bad {:{w}}'.format( desc+':', w=desc_w+1 )) func() except Exception as e: exc = type(e).__name__ emsg = e.args[0] - vmsg(' {:{w}} [{}]'.format( exc, emsg, w=exc_w )) + cfg._util.vmsg(' {:{w}} [{}]'.format( exc, emsg, w=exc_w )) assert exc == exc_chk, m_exc.format(exc,exc_chk) assert re.search(emsg_chk,emsg), m_err.format(emsg,emsg_chk) else: @@ -139,14 +140,14 @@ def run_test(test,subtest=None): subtests = [k for k,v in t.__dict__.items() if type(v).__name__ == 'function' and k[0] != '_'] for subtest in subtests: subtest_disp = subtest.replace('_','-') - if opt.no_altcoin_deps and subtest in altcoin_deps: - qmsg(gray(f'Invoked with --no-altcoin-deps, so skipping {subtest_disp!r}')) + if cfg.no_altcoin_deps and subtest in altcoin_deps: + cfg._util.qmsg(gray(f'Invoked with --no-altcoin-deps, so skipping {subtest_disp!r}')) continue if gc.platform == 'win' and subtest in win_skip: - qmsg(gray(f'Skipping {subtest_disp!r} for Windows platform')) + cfg._util.qmsg(gray(f'Skipping {subtest_disp!r} for Windows platform')) continue elif platform.machine() == 'aarch64' and subtest in arm_skip: - qmsg(gray(f'Skipping {subtest_disp!r} for ARM platform')) + cfg._util.qmsg(gray(f'Skipping {subtest_disp!r} for ARM platform')) continue run_subtest(subtest) else: @@ -154,7 +155,7 @@ def run_test(test,subtest=None): die(4,'Unit test {test!r} failed') try: - for test in (cmd_args or all_tests): + for test in (cfg._args or all_tests): if '.' in test: test,subtest = test.split('.') else: diff --git a/test/unit_tests_d/__init__.py b/test/unit_tests_d/__init__.py index 554f89cf..2f490f20 100755 --- a/test/unit_tests_d/__init__.py +++ b/test/unit_tests_d/__init__.py @@ -7,17 +7,17 @@ test.unit_tests_d.__init__: shared data for unit tests for the MMGen suite import sys,os from mmgen.globalvars import gv -from mmgen.opts import opt +from ..include.common import cfg class unit_tests_base: def _silence(self): - if not opt.verbose: + if not cfg.verbose: self.stdout = sys.stdout self.stderr = sys.stderr sys.stdout = sys.stderr = gv.stdout = gv.stderr = open(os.devnull,'w') def _end_silence(self): - if not opt.verbose: + if not cfg.verbose: sys.stdout = gv.stdout = self.stdout sys.stderr = gv.stderr = self.stderr diff --git a/test/unit_tests_d/ut_addrlist.py b/test/unit_tests_d/ut_addrlist.py index 4a29266e..c3630e4e 100755 --- a/test/unit_tests_d/ut_addrlist.py +++ b/test/unit_tests_d/ut_addrlist.py @@ -10,17 +10,19 @@ from mmgen.addr import MMGenAddrType from mmgen.addrlist import AddrIdxList,AddrList,KeyList,KeyAddrList from mmgen.passwdlist import PasswordList from mmgen.protocol import init_proto +from ..include.common import cfg,qmsg,vmsg def do_test(list_type,chksum,idx_spec=None,pw_id_str=None,add_kwargs=None): + qmsg(blue(f'Testing {list_type.__name__}')) - proto = init_proto('btc') - seed = Seed(seed_bin=bytes.fromhex('feedbead'*8)) + proto = init_proto( cfg, 'btc' ) + seed = Seed(cfg,seed_bin=bytes.fromhex('feedbead'*8)) mmtype = MMGenAddrType(proto,'C') idxs = AddrIdxList(idx_spec or '1-3') - if opt.verbose: - debug_addrlist_save = g.debug_addrlist - g.debug_addrlist = True + if cfg.verbose: + debug_addrlist_save = cfg.debug_addrlist + cfg.debug_addrlist = True kwargs = { 'seed': seed, @@ -36,7 +38,7 @@ def do_test(list_type,chksum,idx_spec=None,pw_id_str=None,add_kwargs=None): if add_kwargs: kwargs.update(add_kwargs) - al = list_type( proto, **kwargs ) + al = list_type( cfg, proto, **kwargs ) af = al.get_file() af.format() @@ -48,8 +50,8 @@ def do_test(list_type,chksum,idx_spec=None,pw_id_str=None,add_kwargs=None): if chksum: assert al.chksum == chksum, f'{al.chksum} != {chksum}' - if opt.verbose: - g.debug_addrlist = debug_addrlist_save + if cfg.verbose: + cfg.debug_addrlist = debug_addrlist_save return True @@ -73,7 +75,7 @@ class unit_tests: ('', ''), ): l = AddrIdxList(i) - if opt.verbose: + if cfg.verbose: msg('list: {}\nin: {}\nout: {}\n'.format(list(l),i,o)) assert l.id_str == o, f'{l.id_str} != {o}' diff --git a/test/unit_tests_d/ut_addrparse.py b/test/unit_tests_d/ut_addrparse.py index d61ab455..802299ee 100755 --- a/test/unit_tests_d/ut_addrparse.py +++ b/test/unit_tests_d/ut_addrparse.py @@ -5,6 +5,7 @@ test/unit_tests_d/ut_addrparse: address parsing tests for the MMGen suite """ from mmgen.common import * +from ..include.common import cfg,vmsg vectors = { 'btc_mainnet': [ @@ -92,7 +93,7 @@ class unit_test(object): for net_id,addrs in vectors.items(): coin,network = net_id.split('_') test_network( - init_proto(coin,network=network), + init_proto( cfg, coin, network=network ), addrs ) return True diff --git a/test/unit_tests_d/ut_baseconv.py b/test/unit_tests_d/ut_baseconv.py index c7ecfae9..dd482be5 100755 --- a/test/unit_tests_d/ut_baseconv.py +++ b/test/unit_tests_d/ut_baseconv.py @@ -5,6 +5,7 @@ test.unit_tests_d.ut_baseconv: Base conversion unit test for the MMGen suite """ from mmgen.common import * +from ..include.common import cfg,qmsg,qmsg_r,vmsg,vmsg_r class unit_test(object): @@ -164,7 +165,7 @@ class unit_test(object): qmsg_r('\nChecking hex-to-base conversion:') for base,data in self.vectors.items(): fs = " {h:%s} {p:<6} {r}" % max(len(d[0][0]) for d in data) - if not opt.verbose: + if not cfg.verbose: qmsg_r(f' {base}') vmsg(f'\nBase: {base}') vmsg(fs.format(h='Input',p='Pad',r='Output')) @@ -180,7 +181,7 @@ class unit_test(object): qmsg_r('\nChecking base-to-hex conversion:') for base,data in self.vectors.items(): fs = " {h:%s} {p:<6} {r}" % max(len(d[1]) for d in data) - if not opt.verbose: + if not cfg.verbose: qmsg_r(f' {base}') vmsg(f'\nBase: {base}') vmsg(fs.format(h='Input',p='Pad',r='Output')) @@ -200,7 +201,7 @@ class unit_test(object): for wl_id in baseconv.constants['wl_chksum']: vmsg_r(f' {wl_id+":":9}') - baseconv(wl_id).check_wordlist() + baseconv(wl_id).check_wordlist(cfg) qmsg('') diff --git a/test/unit_tests_d/ut_bip39.py b/test/unit_tests_d/ut_bip39.py index f7bb591c..dcba42e8 100755 --- a/test/unit_tests_d/ut_bip39.py +++ b/test/unit_tests_d/ut_bip39.py @@ -5,6 +5,7 @@ test/unit_tests_d/ut_bip39: BIP39 unit test for the MMGen suite """ from mmgen.common import * +from ..include.common import cfg,qmsg,vmsg class unit_test(object): @@ -91,7 +92,7 @@ class unit_test(object): from mmgen.bip39 import bip39 b = bip39() - b.check_wordlist() + b.check_wordlist(cfg) vmsg('') qmsg('Checking seed to mnemonic conversion:') diff --git a/test/unit_tests_d/ut_daemon.py b/test/unit_tests_d/ut_daemon.py index f8e44aea..2d509a63 100755 --- a/test/unit_tests_d/ut_daemon.py +++ b/test/unit_tests_d/ut_daemon.py @@ -8,9 +8,10 @@ from subprocess import run,DEVNULL from mmgen.common import * from mmgen.daemon import * from mmgen.protocol import init_proto +from ..include.common import cfg,qmsg,qmsg_r,vmsg def test_flags(): - d = CoinDaemon('eth') + d = CoinDaemon(cfg,'eth') vmsg(f'Available opts: {fmt_list(d.avail_opts,fmt="bare")}') vmsg(f'Available flags: {fmt_list(d.avail_flags,fmt="bare")}') vals = namedtuple('vals',['online','no_daemonize','keep_cfg_file']) @@ -22,7 +23,7 @@ def test_flags(): (['online'],['keep_cfg_file'], vals(True,False,True)), (['online','no_daemonize'],['keep_cfg_file'], vals(True,True,True)), ): - d = CoinDaemon('eth',opts=opts,flags=flags) + d = CoinDaemon(cfg,'eth',opts=opts,flags=flags) assert d.flag.keep_cfg_file == val.keep_cfg_file assert d.opt.online == val.online assert d.opt.no_daemonize == val.no_daemonize @@ -56,7 +57,7 @@ def test_cmd(args_in,message): qmsg_r(message) args = ['python3', f'test/{args_in[0]}-coin-daemons.py'] + list(args_in[1:]) vmsg('\n' + orange(f"Running '{' '.join(args)}':")) - pipe = None if opt.verbose else PIPE + pipe = None if cfg.verbose else PIPE cp = run( args, stdout=pipe, stderr=pipe, check=True ) qmsg('OK') return True diff --git a/test/unit_tests_d/ut_dep.py b/test/unit_tests_d/ut_dep.py index 31f9f97e..97949993 100755 --- a/test/unit_tests_d/ut_dep.py +++ b/test/unit_tests_d/ut_dep.py @@ -11,7 +11,7 @@ from subprocess import run,PIPE from mmgen.common import * from mmgen.exception import NoLEDSupport -from ..include.common import check_solc_ver +from ..include.common import cfg,vmsg,check_solc_ver class unit_tests: @@ -127,7 +127,7 @@ class unit_tests: '--supply=100000000000000000000000000', '--decimals=18', '--stdout', - init_proto('eth').checksummed_addr('deadbeef'*5), + init_proto( cfg, 'eth' ).checksummed_addr('deadbeef'*5), ] cp = run(cmd,stdout=PIPE,stderr=PIPE) vmsg(cp.stderr.decode()) diff --git a/test/unit_tests_d/ut_devtools.py b/test/unit_tests_d/ut_devtools.py index 729fab04..d2ff528e 100755 --- a/test/unit_tests_d/ut_devtools.py +++ b/test/unit_tests_d/ut_devtools.py @@ -78,10 +78,12 @@ class unit_tests(unit_tests_base): from mmgen.protocol import init_proto from mmgen.seed import Seed from mmgen.addrlist import AddrList + from ..include.common import cfg print_hdr('MMGenObject.pmsg()') AddrList( - proto = init_proto('btc'), - seed = Seed(seed_bin=bytes.fromhex('bead'*16)), + cfg = cfg, + proto = init_proto( cfg, 'btc' ), + seed = Seed(cfg,seed_bin=bytes.fromhex('bead'*16)), addr_idxs = '1', mmtype = 'B', skip_chksum = True ).pmsg() diff --git a/test/unit_tests_d/ut_flags.py b/test/unit_tests_d/ut_flags.py index bda3662c..5f122616 100755 --- a/test/unit_tests_d/ut_flags.py +++ b/test/unit_tests_d/ut_flags.py @@ -6,6 +6,7 @@ test.unit_tests_d.ut_flags: unit test for the MMGen suite's ClassFlags class from mmgen.common import * from mmgen.flags import * +from ..include.common import qmsg,qmsg_r,vmsg class unit_test(object): diff --git a/test/unit_tests_d/ut_gen.py b/test/unit_tests_d/ut_gen.py index f520610e..bd3721fc 100755 --- a/test/unit_tests_d/ut_gen.py +++ b/test/unit_tests_d/ut_gen.py @@ -10,6 +10,7 @@ from mmgen.key import PrivKey from mmgen.addr import MMGenAddrType from mmgen.addrgen import KeyGenerator,AddrGenerator from mmgen.keygen import get_backends +from ..include.common import cfg,qmsg # TODO: add viewkey checks vectors = { # from tooltest2 @@ -50,7 +51,7 @@ vectors = { # from tooltest2 def do_test(proto,wif,addr_chk,addr_type,internal_keccak): if internal_keccak: - opt.use_internal_keccak_module = True + cfg.use_internal_keccak_module = True add_msg = ' (internal keccak module)' else: add_msg = '' @@ -60,7 +61,7 @@ def do_test(proto,wif,addr_chk,addr_type,internal_keccak): for n,backend in enumerate(get_backends(at.pubkey_type)): - kg = KeyGenerator(proto,at.pubkey_type,n+1) + kg = KeyGenerator( cfg, proto, at.pubkey_type, n+1 ) qmsg(blue(f' Testing backend {backend!r} for addr type {addr_type!r}{add_msg}')) data = kg.gen_data(privkey) @@ -69,16 +70,16 @@ def do_test(proto,wif,addr_chk,addr_type,internal_keccak): if v and k in ('pubkey','viewkey_bytes'): qmsg(f' {k+":":19} {v.hex()}') - ag = AddrGenerator(proto,addr_type) + ag = AddrGenerator( cfg, proto, addr_type ) addr = ag.to_addr(data) qmsg(f' addr: {addr}\n') assert addr == addr_chk, f'{addr} != {addr_chk}' - opt.use_internal_keccak_module = False + cfg.use_internal_keccak_module = False def do_tests(coin,internal_keccak=False): - proto = init_proto(coin) + proto = init_proto( cfg, coin ) for wif,addr,addr_type in vectors[coin]: do_test(proto,wif,addr,addr_type,internal_keccak) return True @@ -95,7 +96,7 @@ class unit_tests: return do_tests('eth',internal_keccak=True) def xmr(self,name,ut): - if not opt.fast: + if not cfg.fast: do_tests('xmr') return do_tests('xmr',internal_keccak=True) diff --git a/test/unit_tests_d/ut_indexed_dict.py b/test/unit_tests_d/ut_indexed_dict.py index 9be7d45d..f4fd1ac8 100755 --- a/test/unit_tests_d/ut_indexed_dict.py +++ b/test/unit_tests_d/ut_indexed_dict.py @@ -5,6 +5,7 @@ test/unit_tests_d/ut_indexed_dict: IndexedDict class unit test for the MMGen sui """ from mmgen.common import * +from ..include.common import vmsg class unit_test(object): diff --git a/test/unit_tests_d/ut_lockable.py b/test/unit_tests_d/ut_lockable.py index 8e37383e..f2681a97 100755 --- a/test/unit_tests_d/ut_lockable.py +++ b/test/unit_tests_d/ut_lockable.py @@ -5,6 +5,7 @@ test.unit_tests_d.ut_lockable: unit test for the MMGen suite's Lockable class """ from mmgen.common import * +from ..include.common import qmsg,qmsg_r,vmsg class unit_test(object): diff --git a/test/unit_tests_d/ut_mn_entry.py b/test/unit_tests_d/ut_mn_entry.py index 519eca87..326b437b 100755 --- a/test/unit_tests_d/ut_mn_entry.py +++ b/test/unit_tests_d/ut_mn_entry.py @@ -4,7 +4,8 @@ test.unit_tests_d.ut_mn_entry: Mnemonic user entry unit test for the MMGen suite """ -from mmgen.util import msg,msg_r,qmsg,qmsg_r +from mmgen.util import msg,msg_r +from ..include.common import cfg,qmsg class unit_test(object): @@ -49,7 +50,7 @@ class unit_test(object): usl = {} for wl_id in self.vectors: for j,k in (('uniq_ss_len','usl'),('shortest_word','sw'),('longest_word','lw')): - a = getattr(mn_entry(wl_id),j) + a = getattr(mn_entry( cfg, wl_id ),j) b = self.vectors[wl_id][k] assert a == b, f'{wl_id}:{j} {a} != {b}' msg('OK') @@ -58,7 +59,7 @@ class unit_test(object): qmsg('') junk = 'a g z aa gg zz aaa ggg zzz aaaa gggg zzzz aaaaaaaaaaaaaa gggggggggggggg zzzzzzzzzzzzzz' for wl_id in self.vectors: - m = mn_entry(wl_id) + m = mn_entry( cfg, wl_id ) qmsg('Wordlist: '+wl_id) for entry_mode in ('full','short'): for a,word in enumerate(m.wl): diff --git a/test/unit_tests_d/ut_msg.py b/test/unit_tests_d/ut_msg.py index 954a3ea8..a18eb85e 100755 --- a/test/unit_tests_d/ut_msg.py +++ b/test/unit_tests_d/ut_msg.py @@ -6,8 +6,7 @@ test.unit_tests_d.ut_msg: message signing unit tests for the MMGen suite import os -from test.include.common import silence,end_silence,restart_test_daemons,stop_test_daemons -from mmgen.opts import opt +from ..include.common import cfg,silence,end_silence,restart_test_daemons,stop_test_daemons from mmgen.util import msg,bmsg,pumsg,suf from mmgen.protocol import CoinProtocol from mmgen.msg import NewMsg,UnsignedMsg,SignedMsg,SignedOnlineMsg,ExportedMsgSigs @@ -24,6 +23,7 @@ def get_obj(coin,network,msghash_type): addrlists = 'DEADBEEF:C:1-20 98831F3A:B:8,2 A091ABAA:S:10-11 A091ABAA:111 A091ABAA:C:1' return NewMsg( + cfg = cfg, coin = coin, network = network, message = '08/Jun/2021 Bitcoin Law Enacted by El Salvador Legislative Assembly', @@ -37,7 +37,7 @@ async def run_test(network_id,chksum,msghash_type='raw'): coin,network = CoinProtocol.Base.parse_network_id(network_id) - if not opt.verbose: + if not cfg.verbose: silence() bmsg(f'\nTesting {coin.upper()} {network.upper()}:\n') @@ -61,17 +61,17 @@ async def run_test(network_id,chksum,msghash_type='raw'): pumsg('\nTesting signing:\n') - m = UnsignedMsg( infile = os.path.join(tmpdir,get_obj(coin,network,msghash_type).filename) ) + m = UnsignedMsg( cfg, infile = os.path.join(tmpdir,get_obj(coin,network,msghash_type).filename) ) await m.sign(wallet_files=['test/ref/98831F3A.mmwords']) - m = SignedMsg( data=m.__dict__ ) + m = SignedMsg( cfg, data=m.__dict__ ) m.write_to_file( outdir = tmpdir, ask_overwrite = False ) pumsg('\nTesting display:\n') - m = SignedOnlineMsg( infile = os.path.join(tmpdir,get_obj(coin,network,msghash_type).signed_filename) ) + m = SignedOnlineMsg( cfg, infile = os.path.join(tmpdir,get_obj(coin,network,msghash_type).signed_filename) ) msg(m.format()) @@ -96,12 +96,13 @@ async def run_test(network_id,chksum,msghash_type='raw'): from mmgen.fileutil import write_data_to_file exported_sigs = os.path.join(tmpdir,'signatures.json') write_data_to_file( + cfg = cfg, outfile = exported_sigs, data = m.get_json_for_export(), desc = 'signature data', ask_overwrite = False ) - m = ExportedMsgSigs( infile=exported_sigs ) + m = ExportedMsgSigs( cfg, infile=exported_sigs ) pumsg('\nTesting verification (exported data):\n') print_total( await m.verify() ) @@ -120,7 +121,7 @@ async def run_test(network_id,chksum,msghash_type='raw'): msg('\n') - if not opt.verbose: + if not cfg.verbose: end_silence() return True diff --git a/test/unit_tests_d/ut_obj.py b/test/unit_tests_d/ut_obj.py index 8744f465..8ba188c2 100755 --- a/test/unit_tests_d/ut_obj.py +++ b/test/unit_tests_d/ut_obj.py @@ -7,6 +7,7 @@ test.unit_tests_d.ut_obj: data object unit tests for the MMGen suite from decimal import Decimal from mmgen.common import * +from ..include.common import qmsg,qmsg_r,vmsg class unit_tests: diff --git a/test/unit_tests_d/ut_rpc.py b/test/unit_tests_d/ut_rpc.py index 61d04daf..36ece368 100755 --- a/test/unit_tests_d/ut_rpc.py +++ b/test/unit_tests_d/ut_rpc.py @@ -13,6 +13,7 @@ from mmgen.rpc import rpc_init from mmgen.daemon import CoinDaemon from mmgen.proto.xmr.rpc import MoneroRPCClient,MoneroWalletRPCClient from mmgen.proto.xmr.daemon import MoneroWalletDaemon +from ..include.common import cfg,qmsg,vmsg def cfg_file_auth_test(proto,d,bad_auth=False): m = 'missing credentials' if bad_auth else f'credentials from {d.cfg_file}' @@ -32,14 +33,14 @@ def cfg_file_auth_test(proto,d,bad_auth=False): if bad_auth: os.rename(d.auth_cookie_fn,d.auth_cookie_fn+'.bak') try: - async_run(rpc_init(proto)) + async_run(rpc_init(cfg,proto)) except Exception as e: vmsg(yellow(str(e))) else: die(3,'No error on missing credentials!') os.rename(d.auth_cookie_fn+'.bak',d.auth_cookie_fn) else: - rpc = async_run(rpc_init(proto)) + rpc = async_run(rpc_init(cfg,proto)) assert rpc.auth.user == 'ut_rpc', f'{rpc.auth.user}: user is not ut_rpc!' d.stop() @@ -79,7 +80,7 @@ def do_msg(rpc,backend): class init_test: async def btc(proto,backend,daemon): - rpc = await rpc_init(proto,backend,daemon) + rpc = await rpc_init(cfg,proto,backend,daemon) do_msg(rpc,backend) bh = (await rpc.call('getblockchaininfo',timeout=300))['bestblockhash'] @@ -88,14 +89,14 @@ class init_test: return rpc async def bch(proto,backend,daemon): - rpc = await rpc_init(proto,backend,daemon) + rpc = await rpc_init(cfg,proto,backend,daemon) do_msg(rpc,backend) return rpc ltc = bch async def eth(proto,backend,daemon): - rpc = await rpc_init(proto,backend,daemon) + rpc = await rpc_init(cfg,proto,backend,daemon) do_msg(rpc,backend) await rpc.call('eth_blockNumber',timeout=300) return rpc @@ -106,20 +107,20 @@ def run_test(network_ids,test_cf_auth=False,daemon_ids=None): def do_test(d): - if not opt.no_daemon_stop: + if not cfg.no_daemon_stop: d.stop() - if not opt.no_daemon_autostart: + if not cfg.no_daemon_autostart: d.remove_datadir() d.start() - for n,backend in enumerate(g.autoset_opts['rpc_backend'].choices): + for n,backend in enumerate(cfg.autoset_opts['rpc_backend'].choices): test = getattr(init_test,d.proto.coin.lower()) rpc = async_run(test(d.proto,backend,d)) - if not n and opt.verbose: + if not n and cfg.verbose: print_daemon_info(rpc) - if not opt.no_daemon_stop: + if not cfg.no_daemon_stop: d.stop() if test_cf_auth and gc.platform != 'win': @@ -129,12 +130,12 @@ def run_test(network_ids,test_cf_auth=False,daemon_ids=None): qmsg('') for network_id in network_ids: - proto = init_proto(network_id=network_id) + proto = init_proto( cfg, network_id=network_id ) ids = (lambda x: set(daemon_ids) & set(x) if daemon_ids else x - )(CoinDaemon.get_daemon_ids(proto.coin)) + )(CoinDaemon.get_daemon_ids(cfg,proto.coin)) for daemon_id in ids: - do_test( CoinDaemon(proto=proto,test_suite=True,daemon_id=daemon_id) ) + do_test( CoinDaemon(cfg, proto=proto,test_suite=True,daemon_id=daemon_id) ) return True @@ -169,6 +170,7 @@ class unit_tests: def test_monerod_rpc(md): rpc = MoneroRPCClient( + cfg = cfg, proto = md.proto, host = md.host, port = md.rpc_port, @@ -176,30 +178,31 @@ class unit_tests: passwd = None, daemon = md, ) - if opt.verbose: + if cfg.verbose: print_daemon_info(rpc) rpc.call_raw('get_height') rpc.call('get_last_block_header') async def run(): - networks = init_proto('xmr').networks + networks = init_proto( cfg, 'xmr' ).networks daemons = [( - CoinDaemon(proto=proto,test_suite=True), + CoinDaemon( cfg, proto=proto, test_suite=True ), MoneroWalletDaemon( + cfg = cfg, proto = proto, test_suite = True, wallet_dir = 'test/trash2', passwd = 'ut_rpc_passw0rd' ) - ) for proto in (init_proto( 'xmr', network=network ) for network in networks) ] + ) for proto in (init_proto( cfg, 'xmr', network=network ) for network in networks) ] for md,wd in daemons: - if not opt.no_daemon_autostart: + if not cfg.no_daemon_autostart: md.start() wd.start() test_monerod_rpc(md) - c = MoneroWalletRPCClient(daemon=wd) + c = MoneroWalletRPCClient( cfg=cfg, daemon=wd ) fn = f'monero-{wd.network}-junk-wallet' qmsg(f'Creating {wd.network} wallet') c.call( @@ -213,7 +216,7 @@ class unit_tests: for md,wd in daemons: wd.wait = False await wd.rpc.stop_daemon() - if not opt.no_daemon_stop: + if not cfg.no_daemon_stop: md.wait = False await md.rpc.stop_daemon() diff --git a/test/unit_tests_d/ut_scrypt.py b/test/unit_tests_d/ut_scrypt.py index 23c4073b..10681593 100755 --- a/test/unit_tests_d/ut_scrypt.py +++ b/test/unit_tests_d/ut_scrypt.py @@ -4,7 +4,8 @@ test.unit_tests_d.ut_scrypt: password hashing unit test for the MMGen suite """ -from ..include.common import * +from ..include.common import cfg,qmsg,vmsg,omsg_r,silence,end_silence +from mmgen.util import msg,msg_r class unit_test(object): @@ -15,7 +16,7 @@ class unit_test(object): qmsg('') from mmgen.crypto import Crypto - crypto = Crypto() + crypto = Crypto(cfg) salt = bytes.fromhex('f00f' * 16) @@ -35,7 +36,7 @@ class unit_test(object): for pw_base,res in pws: for pw in (pw_base,pw_base.encode()): pw_disp = "'"+pw+"'" if type(pw) == str else "b'"+pw.decode()+"'" - if opt.quiet: + if cfg.quiet: omsg_r('.') else: msg_r(f'\n password {pw_disp:9} ') @@ -47,32 +48,32 @@ class unit_test(object): hp = str(hp) res = presets[hp] pw = 'φυβαρ' - if opt.quiet: + if cfg.quiet: omsg_r('.') else: msg_r(f'\n {hp!r:3}: {crypto.hash_presets[hp]!r:12} ') st = time.time() ret = crypto.scrypt_hash_passphrase(pw,salt,hp).hex() t = time.time() - st - vmsg('' if g.test_suite_deterministic else f' {t:0.4f} secs') + vmsg('' if cfg.test_suite_deterministic else f' {t:0.4f} secs') assert ret == res, ret - if opt.quiet: + if cfg.quiet: silence() - g.force_standalone_scrypt_module = False + cfg.force_standalone_scrypt_module = False vmsg('Passwords (auto module selection):') test_passwords() vmsg('Hash presets (auto module selection):') - test_presets((1,2,3,4) if opt.fast else (1,2,3,4,5,6,7)) + test_presets((1,2,3,4) if cfg.fast else (1,2,3,4,5,6,7)) - g.force_standalone_scrypt_module = True + cfg.force_standalone_scrypt_module = True vmsg('Passwords (force standalone scrypt module):') test_passwords() vmsg('Hash presets (force standalone scrypt module):') test_presets((1,2,3)) - if opt.quiet: + if cfg.quiet: end_silence() msg('OK') diff --git a/test/unit_tests_d/ut_seedsplit.py b/test/unit_tests_d/ut_seedsplit.py index 9400aad4..b9d39823 100755 --- a/test/unit_tests_d/ut_seedsplit.py +++ b/test/unit_tests_d/ut_seedsplit.py @@ -5,6 +5,7 @@ test/unit_tests_d/ut_seedsplit: seed splitting unit test for the MMGen suite """ from mmgen.common import * +from ..include.common import cfg,vmsg,vmsg_r class unit_test(object): @@ -12,7 +13,7 @@ class unit_test(object): from mmgen.seed import Seed from mmgen.seedsplit import SeedShareList,SeedShareIdx - g.debug_subseed = bool(opt.verbose) + cfg.debug_subseed = bool(cfg.verbose) def basic_ops(master_idx): test_data = { @@ -56,7 +57,7 @@ class unit_test(object): for a,b,c,d,e,f,h,i,p in test_data[id_str if id_str is not None else 'default']: seed_bin = bytes.fromhex('deadbeef' * a) - seed = Seed(seed_bin) + seed = Seed( cfg, seed_bin ) assert seed.sid == b, seed.sid for share_count,j,k,l,m in ((2,c,c,d,i),(5,e,f,h,p)): @@ -91,7 +92,7 @@ class unit_test(object): if master_idx: slist = [shares.get_share_by_idx(i+1,base_seed=True) for i in range(len(shares))] - A = Seed.join_shares(slist,master_idx,id_str).sid + A = Seed.join_shares( cfg, slist, master_idx, id_str ).sid assert A == b, A msg('OK') @@ -100,7 +101,7 @@ class unit_test(object): msg_r('Testing defaults and limits...') seed_bin = bytes.fromhex('deadbeef' * 8) - seed = Seed(seed_bin) + seed = Seed( cfg, seed_bin ) shares = seed.split(SeedShareIdx.max_val) s = shares.format() @@ -124,7 +125,7 @@ class unit_test(object): vmsg('') seed_bin = bytes.fromhex(seed_hex) - seed = Seed(seed_bin) + seed = Seed( cfg, seed_bin ) SeedShareIdx.max_val = ss_count shares = seed.split(ss_count,master_idx=master_idx) @@ -147,7 +148,7 @@ class unit_test(object): msg_r('Testing last share collisions with shortened Seed IDs') vmsg('') seed_bin = bytes.fromhex('2eadbeef'*8) - seed = Seed(seed_bin) + seed = Seed( cfg, seed_bin ) ssm_save = SeedShareIdx.max_val ssm = SeedShareIdx.max_val = 2048 shares = SeedShareList(seed,count=ssm,id_str='foo',master_idx=1,debug_last_share=True) diff --git a/test/unit_tests_d/ut_subseed.py b/test/unit_tests_d/ut_subseed.py index 11df41b8..dc13ba10 100755 --- a/test/unit_tests_d/ut_subseed.py +++ b/test/unit_tests_d/ut_subseed.py @@ -5,6 +5,7 @@ test/unit_tests_d/ut_subseed: subseed unit test for the MMGen suite """ from mmgen.common import * +from ..include.common import cfg,vmsg_r class unit_test(object): @@ -23,7 +24,7 @@ class unit_test(object): ): seed_bin = bytes.fromhex('deadbeef' * a) - seed = Seed(seed_bin) + seed = Seed( cfg, seed_bin ) assert seed.sid == b, seed.sid subseed = seed.subseed('2s') @@ -38,7 +39,7 @@ class unit_test(object): assert subseed.idx == 10, subseed.idx assert subseed.ss_idx == h, subseed.ss_idx - seed2 = Seed(seed_bin) + seed2 = Seed( cfg, seed_bin ) ss2_list = seed2.subseeds seed2.subseeds._generate(1) @@ -96,31 +97,31 @@ class unit_test(object): seed_bin = bytes.fromhex('deadbeef' * 8) - seed = Seed(seed_bin,nSubseeds=11) + seed = Seed( cfg, seed_bin, nSubseeds=11 ) seed.subseeds._generate() ss = seed.subseeds assert len(ss.data['long']) == len(ss.data['short']), len(ss.data['short']) assert len(ss) == 11, len(ss) - seed = Seed(seed_bin) + seed = Seed( cfg, seed_bin ) seed.subseeds._generate() ss = seed.subseeds assert len(ss.data['long']) == len(ss.data['short']), len(ss.data['short']) assert len(ss) == nSubseeds, len(ss) - seed = Seed(seed_bin) + seed = Seed( cfg, seed_bin ) seed.subseed_by_seed_id('EEEEEEEE') ss = seed.subseeds assert len(ss.data['long']) == len(ss.data['short']), len(ss.data['short']) assert len(ss) == nSubseeds, len(ss) - seed = Seed(seed_bin) + seed = Seed( cfg, seed_bin ) subseed = seed.subseed_by_seed_id('803B165C') assert len(ss.data['long']) == len(ss.data['short']), len(ss.data['short']) assert subseed.sid == '803B165C', subseed.sid assert subseed.idx == 3, subseed.idx - seed = Seed(seed_bin) + seed = Seed( cfg, seed_bin ) subseed = seed.subseed_by_seed_id('803B165C',last_idx=1) assert len(ss.data['long']) == len(ss.data['short']), len(ss.data['short']) assert subseed == None, subseed @@ -160,14 +161,14 @@ class unit_test(object): ss_count,ltr,last_sid,collisions_chk = ( (SubSeedIdxRange.max_idx,'S','2788F26B',470), (49509,'L','8D1FE500',2) - )[bool(opt.fast)] + )[bool(cfg.fast)] last_idx = str(ss_count) + ltr msg_r(f'Testing Seed ID collisions ({ss_count} subseed pairs)...') seed_bin = bytes.fromhex('12abcdef' * 8) # 95B3D78D - seed = Seed(seed_bin) + seed = Seed( cfg, seed_bin ) seed.subseeds._generate(ss_count) ss = seed.subseeds diff --git a/test/unit_tests_d/ut_tx.py b/test/unit_tests_d/ut_tx.py index c66b72a1..4a7f1c7f 100755 --- a/test/unit_tests_d/ut_tx.py +++ b/test/unit_tests_d/ut_tx.py @@ -11,20 +11,21 @@ from mmgen.tx import NewTX,CompletedTX from mmgen.tx.file import MMGenTxFile from mmgen.daemon import CoinDaemon from mmgen.protocol import init_proto +from ..include.common import cfg,qmsg,vmsg async def do_txfile_test(desc,fns): qmsg(f' Testing CompletedTX initializer ({desc})') for fn in fns: qmsg(f' parsing: {os.path.basename(fn)}') fpath = os.path.join('test','ref',fn) - tx = await CompletedTX(filename=fpath,quiet_open=True) + tx = await CompletedTX( cfg=cfg, filename=fpath, quiet_open=True ) vmsg(tx.info.format()) f = MMGenTxFile(tx) fn_gen = f.make_filename() - if g.debug_utf8: + if cfg.debug_utf8: fn_gen = fn_gen.replace('-α','') assert fn_gen == os.path.basename(fn), f'{fn_gen} != {fn}' @@ -57,11 +58,11 @@ class unit_tests: async def tx(self,name,ut): qmsg(' Testing NewTX initializer') - d = CoinDaemon('btc',test_suite=True) + d = CoinDaemon( cfg, 'btc', test_suite=True ) d.start() - proto = init_proto('btc',need_amt=True) - tx = await NewTX(proto=proto) + proto = init_proto( cfg, 'btc', need_amt=True ) + tx = await NewTX( cfg=cfg, proto=proto ) d.stop() qmsg(' OK') diff --git a/test/unit_tests_d/ut_tx_deserialize.py b/test/unit_tests_d/ut_tx_deserialize.py index cc3134dd..ca31f9c6 100755 --- a/test/unit_tests_d/ut_tx_deserialize.py +++ b/test/unit_tests_d/ut_tx_deserialize.py @@ -7,7 +7,7 @@ test/unit_tests_d/ut_tx_deserialize: TX deserialization unit tests for the MMGen import os,json from mmgen.common import * -from ..include.common import * +from ..include.common import cfg,start_test_daemons,stop_test_daemons from mmgen.protocol import init_proto from mmgen.tx import CompletedTX from mmgen.proto.btc.tx.base import DeserializeTX @@ -15,14 +15,14 @@ from mmgen.rpc import rpc_init from mmgen.daemon import CoinDaemon def print_info(name,extra_desc): - if opt.names: + if cfg.names: Msg_r('{} {} {}'.format( purple('Testing'), cyan(f'{name} ({extra_desc})'), - '' if opt.quiet else '\n')) + '' if cfg.quiet else '\n')) else: Msg_r(f'Testing {extra_desc}') - if not opt.quiet: + if not cfg.quiet: Msg('') async def test_tx(tx_proto,tx_hex,desc,n): @@ -34,17 +34,17 @@ async def test_tx(tx_proto,tx_hex,desc,n): return True return False - rpc = await rpc_init(proto=tx_proto) + rpc = await rpc_init( cfg, proto=tx_proto ) d = await rpc.call('decoderawtransaction',tx_hex) if has_nonstandard_outputs(d['vout']): return False dt = DeserializeTX(tx_proto,tx_hex) - if opt.verbose: + if cfg.verbose: Msg('\n\n================================ Core vector: ==================================') - Msg_r('.' if opt.quiet else f'{n:>3}) {desc}\n') - if opt.verbose: + Msg_r('.' if cfg.quiet else f'{n:>3}) {desc}\n') + if cfg.verbose: Pmsg(d) Msg('\n------------------------------ MMGen deserialized: -----------------------------') Pmsg(dt._asdict()) @@ -88,7 +88,7 @@ async def do_mmgen_ref(daemons,fns,name,desc): start_test_daemons(*daemons) print_info(name,desc) for n,fn in enumerate(fns): - tx = await CompletedTX(filename=fn,quiet_open=True) + tx = await CompletedTX( cfg=cfg, filename=fn, quiet_open=True ) await test_tx( tx_proto = tx.proto, tx_hex = tx.serialized, @@ -119,7 +119,7 @@ class unit_tests: for e in core_data: if type(e[0]) == list: await test_tx( - tx_proto = init_proto('btc',need_amt=True), + tx_proto = init_proto( cfg, 'btc', need_amt=True ), tx_hex = e[1], desc = desc, n = n ) diff --git a/test/unit_tests_d/ut_xmrseed.py b/test/unit_tests_d/ut_xmrseed.py index c09eae90..50350622 100755 --- a/test/unit_tests_d/ut_xmrseed.py +++ b/test/unit_tests_d/ut_xmrseed.py @@ -5,6 +5,7 @@ test/unit_tests_d/ut_xmrseed: Monero mnemonic unit test for the MMGen suite """ from mmgen.common import * +from ..include.common import cfg,qmsg,vmsg class unit_test(object): @@ -82,7 +83,7 @@ class unit_test(object): from mmgen.xmrseed import xmrseed b = xmrseed() - b.check_wordlist() + b.check_wordlist(cfg) try: from monero.wordlists.english import English