Config API, Part I

This patch eliminates the global configuration variables `opt` and `g`, making
all functions and class instances locally configurable.  Configuration data is
passed to functions and constructors via the `cfg` parameter and made available
to methods in `self.cfg`.

Local configuration free from dependence on the command line will enable the
creation of multiple, independently configured instances of MMGen’s data
objects within a single process.

Potential applications include testing (tracking wallets configured to interact
with spawned processes, for example) and the use of MMGen as a library for
other projects.

This patch completes most of the work required to enable the API.  The full
implementation will appear in a forthcoming commit.
This commit is contained in:
The MMGen Project 2023-03-28 18:14:37 +00:00
commit c7adb56e38
Signed by: mmgen
GPG key ID: 3F8B1861E32B7DA2
197 changed files with 2218 additions and 2078 deletions

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -1 +1 @@
13.3.dev41
13.3.dev42

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -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: {}'

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

Some files were not shown because too many files have changed in this diff Show more