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:
parent
6cd8856912
commit
c7adb56e38
197 changed files with 2218 additions and 2078 deletions
|
|
@ -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,)))
|
||||
|
|
|
|||
|
|
@ -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 ))
|
||||
|
|
|
|||
|
|
@ -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}')
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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'}
|
||||
|
|
|
|||
|
|
@ -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 )
|
||||
|
|
|
|||
|
|
@ -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'
|
||||
|
|
|
|||
|
|
@ -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():
|
||||
|
|
|
|||
|
|
@ -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 *
|
||||
|
|
|
|||
101
mmgen/crypto.py
101
mmgen/crypto.py
|
|
@ -23,23 +23,16 @@ crypto: Random number, password hashing and symmetric encryption routines for th
|
|||
import os
|
||||
from collections import namedtuple
|
||||
|
||||
from .globalvars import g,gc
|
||||
from .opts import opt
|
||||
from .globalvars import gc
|
||||
from .util import (
|
||||
msg,
|
||||
msg_r,
|
||||
dmsg,
|
||||
vmsg,
|
||||
vmsg_r,
|
||||
qmsg,
|
||||
fmt,
|
||||
die,
|
||||
make_chksum_8,
|
||||
compare_chksums,
|
||||
oneshot_warning,
|
||||
)
|
||||
|
||||
|
||||
class Crypto:
|
||||
|
||||
mmenc_ext = 'mmenc'
|
||||
|
|
@ -72,6 +65,10 @@ class Crypto:
|
|||
def __init__(self,fn):
|
||||
oneshot_warning.__init__(self,div=fn,fmt_args=[fn],reverse=True)
|
||||
|
||||
def __init__(self,cfg):
|
||||
self.cfg = cfg
|
||||
self.util = cfg._util
|
||||
|
||||
def get_hash_params(self,hash_preset):
|
||||
if hash_preset in self.hash_presets:
|
||||
return self.hash_presets[hash_preset] # N,r,p
|
||||
|
|
@ -87,7 +84,7 @@ class Crypto:
|
|||
def scramble_seed(self,seed,scramble_key):
|
||||
import hmac
|
||||
step1 = hmac.digest(seed,scramble_key,'sha256')
|
||||
if g.debug:
|
||||
if self.cfg.debug:
|
||||
msg(f'Seed: {seed.hex()!r}\nScramble key: {scramble_key}\nScrambled seed: {step1.hex()}\n')
|
||||
return self.sha256_rounds(step1)
|
||||
|
||||
|
|
@ -95,56 +92,56 @@ class Crypto:
|
|||
return self.encrypt_data(data,key,desc=desc)
|
||||
|
||||
def decrypt_seed(self,enc_seed,key,seed_id,key_id):
|
||||
vmsg_r('Checking key...')
|
||||
self.util.vmsg_r('Checking key...')
|
||||
chk1 = make_chksum_8(key)
|
||||
if key_id:
|
||||
if not compare_chksums(key_id,'key ID',chk1,'computed'):
|
||||
if not self.util.compare_chksums(key_id,'key ID',chk1,'computed'):
|
||||
msg('Incorrect passphrase or hash preset')
|
||||
return False
|
||||
|
||||
dec_seed = self.decrypt_data(enc_seed,key,desc='seed')
|
||||
chk2 = make_chksum_8(dec_seed)
|
||||
if seed_id:
|
||||
if compare_chksums(seed_id,'Seed ID',chk2,'decrypted seed'):
|
||||
qmsg('Passphrase is OK')
|
||||
if self.util.compare_chksums(seed_id,'Seed ID',chk2,'decrypted seed'):
|
||||
self.util.qmsg('Passphrase is OK')
|
||||
else:
|
||||
if not opt.debug:
|
||||
if not self.cfg.debug:
|
||||
msg_r('Checking key ID...')
|
||||
if compare_chksums(key_id,'key ID',chk1,'computed'):
|
||||
if self.util.compare_chksums(key_id,'key ID',chk1,'computed'):
|
||||
msg('Key ID is correct but decryption of seed failed')
|
||||
else:
|
||||
msg('Incorrect passphrase or hash preset')
|
||||
vmsg('')
|
||||
self.util.vmsg('')
|
||||
return False
|
||||
|
||||
dmsg(f'Decrypted seed: {dec_seed.hex()}')
|
||||
self.util.dmsg(f'Decrypted seed: {dec_seed.hex()}')
|
||||
return dec_seed
|
||||
|
||||
def encrypt_data(self,data,key,iv=aesctr_dfl_iv,desc='data',verify=True,silent=False):
|
||||
from cryptography.hazmat.primitives.ciphers import Cipher,algorithms,modes
|
||||
from cryptography.hazmat.backends import default_backend
|
||||
if not silent:
|
||||
vmsg(f'Encrypting {desc}')
|
||||
self.util.vmsg(f'Encrypting {desc}')
|
||||
c = Cipher(algorithms.AES(key),modes.CTR(iv),backend=default_backend())
|
||||
encryptor = c.encryptor()
|
||||
enc_data = encryptor.update(data) + encryptor.finalize()
|
||||
|
||||
if verify:
|
||||
vmsg_r(f'Performing a test decryption of the {desc}...')
|
||||
self.util.vmsg_r(f'Performing a test decryption of the {desc}...')
|
||||
c = Cipher(algorithms.AES(key),modes.CTR(iv),backend=default_backend())
|
||||
encryptor = c.encryptor()
|
||||
dec_data = encryptor.update(enc_data) + encryptor.finalize()
|
||||
if dec_data != data:
|
||||
die(2,f'ERROR.\nDecrypted {desc} doesn’t match original {desc}')
|
||||
if not silent:
|
||||
vmsg('done')
|
||||
self.util.vmsg('done')
|
||||
|
||||
return enc_data
|
||||
|
||||
def decrypt_data(self,enc_data,key,iv=aesctr_dfl_iv,desc='data'):
|
||||
from cryptography.hazmat.primitives.ciphers import Cipher,algorithms,modes
|
||||
from cryptography.hazmat.backends import default_backend
|
||||
vmsg_r(f'Decrypting {desc} with key...')
|
||||
self.util.vmsg_r(f'Decrypting {desc} with key...')
|
||||
c = Cipher(algorithms.AES(key),modes.CTR(iv),backend=default_backend())
|
||||
encryptor = c.encryptor()
|
||||
return encryptor.update(enc_data) + encryptor.finalize()
|
||||
|
|
@ -184,7 +181,7 @@ class Crypto:
|
|||
|
||||
# hashlib.scrypt doesn't support N > 14 (hash preset > 3)
|
||||
ret = (
|
||||
do_standalone_scrypt() if ps.N > 14 or g.force_standalone_scrypt_module else
|
||||
do_standalone_scrypt() if ps.N > 14 or self.cfg.force_standalone_scrypt_module else
|
||||
do_hashlib_scrypt() )
|
||||
|
||||
if int(hash_preset) > 3:
|
||||
|
|
@ -193,17 +190,17 @@ class Crypto:
|
|||
return ret
|
||||
|
||||
def make_key(self,passwd,salt,hash_preset,desc='encryption key',from_what='passphrase',verbose=False):
|
||||
if opt.verbose or verbose:
|
||||
if self.cfg.verbose or verbose:
|
||||
msg_r(f"Generating {desc}{' from ' + from_what if from_what else ''}...")
|
||||
key = self.scrypt_hash_passphrase(passwd,salt,hash_preset)
|
||||
if opt.verbose or verbose: msg('done')
|
||||
dmsg(f'Key: {key.hex()}')
|
||||
if self.cfg.verbose or verbose: msg('done')
|
||||
self.util.dmsg(f'Key: {key.hex()}')
|
||||
return key
|
||||
|
||||
def _get_random_data_from_user(self,uchars=None,desc='data'):
|
||||
|
||||
if uchars is None:
|
||||
uchars = opt.usr_randchars
|
||||
uchars = self.cfg.usr_randchars
|
||||
|
||||
info1 = f"""
|
||||
Now we're going to gather some additional input from the keyboard to further
|
||||
|
|
@ -225,7 +222,7 @@ class Crypto:
|
|||
on the screen.
|
||||
"""
|
||||
|
||||
msg(f'Enter {uchars} random symbols' if opt.quiet else
|
||||
msg(f'Enter {uchars} random symbols' if self.cfg.quiet else
|
||||
'\n' + fmt(info1,indent=' ') +
|
||||
'\n' + fmt(info2) )
|
||||
|
||||
|
|
@ -238,7 +235,7 @@ class Crypto:
|
|||
key_data += get_char_raw(f'\rYou may begin typing. {uchars-i} symbols left: ')
|
||||
time_data.append(time.time())
|
||||
|
||||
msg_r( '\r' if opt.quiet else f'\rThank you. That’s enough.{" "*18}\n\n' )
|
||||
msg_r( '\r' if self.cfg.quiet else f'\rThank you. That’s enough.{" "*18}\n\n' )
|
||||
|
||||
time_data = [f'{t:.22f}'.rstrip('0') for t in time_data]
|
||||
|
||||
|
|
@ -249,11 +246,11 @@ class Crypto:
|
|||
|
||||
ret = key_data + '\n' + '\n'.join(time_data)
|
||||
|
||||
if g.debug:
|
||||
if self.cfg.debug:
|
||||
msg(f'USER ENTROPY (user input + keystroke timings):\n{ret}')
|
||||
|
||||
from .ui import line_input
|
||||
line_input( 'User random data successfully acquired. Press ENTER to continue: ' )
|
||||
line_input( self.cfg, 'User random data successfully acquired. Press ENTER to continue: ' )
|
||||
|
||||
return ret.encode()
|
||||
|
||||
|
|
@ -274,7 +271,7 @@ class Crypto:
|
|||
|
||||
assert type(rand_bytes) == bytes, 'add_user_random_chk1'
|
||||
|
||||
if opt.usr_randchars:
|
||||
if self.cfg.usr_randchars:
|
||||
|
||||
if not urand['data']:
|
||||
from hashlib import sha256
|
||||
|
|
@ -310,7 +307,7 @@ class Crypto:
|
|||
|
||||
from .ui import line_input
|
||||
while True:
|
||||
ret = line_input( prompt )
|
||||
ret = line_input( self.cfg, prompt )
|
||||
if ret:
|
||||
if ret in self.hash_presets:
|
||||
return ret
|
||||
|
|
@ -329,29 +326,30 @@ class Crypto:
|
|||
if passwd_file:
|
||||
from .fileutil import get_words_from_file
|
||||
pw = ' '.join(get_words_from_file(
|
||||
cfg = self.cfg,
|
||||
infile = passwd_file,
|
||||
desc = f'{pw_desc} for {data_desc}',
|
||||
quiet = self.pwfile_reuse_warning(passwd_file).warning_shown ))
|
||||
else:
|
||||
qmsg('\n'+fmt(message,indent=' '))
|
||||
self.util.qmsg('\n'+fmt(message,indent=' '))
|
||||
from .ui import get_words_from_user
|
||||
if opt.echo_passphrase:
|
||||
pw = ' '.join(get_words_from_user( f'Enter {pw_desc} for {data_desc}: ' ))
|
||||
if self.cfg.echo_passphrase:
|
||||
pw = ' '.join(get_words_from_user( self.cfg, f'Enter {pw_desc} for {data_desc}: ' ))
|
||||
else:
|
||||
for i in range(gc.passwd_max_tries):
|
||||
pw = ' '.join(get_words_from_user( f'Enter {pw_desc} for {data_desc}: ' ))
|
||||
pw_chk = ' '.join(get_words_from_user( f'Repeat {pw_desc}: ' ))
|
||||
dmsg(f'Passphrases: [{pw}] [{pw_chk}]')
|
||||
pw = ' '.join(get_words_from_user( self.cfg, f'Enter {pw_desc} for {data_desc}: ' ))
|
||||
pw_chk = ' '.join(get_words_from_user( self.cfg, f'Repeat {pw_desc}: ' ))
|
||||
self.util.dmsg(f'Passphrases: [{pw}] [{pw_chk}]')
|
||||
if pw == pw_chk:
|
||||
vmsg('Passphrases match')
|
||||
self.util.vmsg('Passphrases match')
|
||||
break
|
||||
else:
|
||||
msg('Passphrases do not match. Try again.')
|
||||
else:
|
||||
die(2,f'User failed to duplicate passphrase in {g.passwd_max_tries} attempts')
|
||||
die(2,f'User failed to duplicate passphrase in {gc.passwd_max_tries} attempts')
|
||||
|
||||
if pw == '':
|
||||
qmsg('WARNING: Empty passphrase')
|
||||
self.util.qmsg('WARNING: Empty passphrase')
|
||||
|
||||
return pw
|
||||
|
||||
|
|
@ -359,48 +357,49 @@ class Crypto:
|
|||
if passwd_file:
|
||||
from .fileutil import get_words_from_file
|
||||
return ' '.join(get_words_from_file(
|
||||
cfg = self.cfg,
|
||||
infile = passwd_file,
|
||||
desc = f'{pw_desc} for {data_desc}',
|
||||
quiet = self.pwfile_reuse_warning(passwd_file).warning_shown ))
|
||||
else:
|
||||
from .ui import get_words_from_user
|
||||
return ' '.join(get_words_from_user( f'Enter {pw_desc} for {data_desc}: ' ))
|
||||
return ' '.join(get_words_from_user( self.cfg, f'Enter {pw_desc} for {data_desc}: ' ))
|
||||
|
||||
def mmgen_encrypt(self,data,desc='data',hash_preset=None):
|
||||
salt = self.get_random(self.mmenc_salt_len)
|
||||
iv = self.get_random(self.aesctr_iv_len)
|
||||
nonce = self.get_random(self.mmenc_nonce_len)
|
||||
hp = hash_preset or opt.hash_preset or self.get_hash_preset_from_user(data_desc=desc)
|
||||
hp = hash_preset or self.cfg.hash_preset or self.get_hash_preset_from_user(data_desc=desc)
|
||||
m = ('user-requested','default')[hp=='3']
|
||||
vmsg(f'Encrypting {desc}')
|
||||
qmsg(f'Using {m} hash preset of {hp!r}')
|
||||
self.util.vmsg(f'Encrypting {desc}')
|
||||
self.util.qmsg(f'Using {m} hash preset of {hp!r}')
|
||||
passwd = self.get_new_passphrase(
|
||||
data_desc = desc,
|
||||
hash_preset = hp,
|
||||
passwd_file = opt.passwd_file )
|
||||
passwd_file = self.cfg.passwd_file )
|
||||
key = self.make_key(passwd,salt,hp)
|
||||
from hashlib import sha256
|
||||
enc_d = self.encrypt_data( sha256(nonce+data).digest() + nonce + data, key, iv, desc=desc )
|
||||
return salt+iv+enc_d
|
||||
|
||||
def mmgen_decrypt(self,data,desc='data',hash_preset=None):
|
||||
vmsg(f'Preparing to decrypt {desc}')
|
||||
self.util.vmsg(f'Preparing to decrypt {desc}')
|
||||
dstart = self.mmenc_salt_len + self.aesctr_iv_len
|
||||
salt = data[:self.mmenc_salt_len]
|
||||
iv = data[self.mmenc_salt_len:dstart]
|
||||
enc_d = data[dstart:]
|
||||
hp = hash_preset or opt.hash_preset or self.get_hash_preset_from_user(data_desc=desc)
|
||||
hp = hash_preset or self.cfg.hash_preset or self.get_hash_preset_from_user(data_desc=desc)
|
||||
m = ('user-requested','default')[hp=='3']
|
||||
qmsg(f'Using {m} hash preset of {hp!r}')
|
||||
self.util.qmsg(f'Using {m} hash preset of {hp!r}')
|
||||
passwd = self.get_passphrase(
|
||||
data_desc = desc,
|
||||
passwd_file = opt.passwd_file )
|
||||
passwd_file = self.cfg.passwd_file )
|
||||
key = self.make_key(passwd,salt,hp)
|
||||
dec_d = self.decrypt_data( enc_d, key, iv, desc )
|
||||
sha256_len = 32
|
||||
from hashlib import sha256
|
||||
if dec_d[:sha256_len] == sha256(dec_d[sha256_len:]).digest():
|
||||
vmsg('OK')
|
||||
self.util.vmsg('OK')
|
||||
return dec_d[sha256_len+self.mmenc_nonce_len:]
|
||||
else:
|
||||
msg('Incorrect passphrase or hash preset')
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
13.3.dev41
|
||||
13.3.dev42
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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"""
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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())
|
||||
|
|
|
|||
|
|
@ -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')
|
||||
|
||||
|
|
|
|||
|
|
@ -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():
|
||||
|
|
|
|||
|
|
@ -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')
|
||||
|
|
|
|||
|
|
@ -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())
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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())
|
||||
|
|
|
|||
|
|
@ -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())
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -21,7 +21,11 @@ mmgen-xmrwallet: Perform various Monero wallet operations for addresses
|
|||
in an MMGen XMR key-address file
|
||||
"""
|
||||
|
||||
from .common import *
|
||||
from collections import namedtuple
|
||||
|
||||
import mmgen.opts as opts
|
||||
from .globalvars import gc
|
||||
from .util import ymsg,die,async_run
|
||||
from .xmrwallet import xmrwallet_uarg_info,MoneroWalletOps
|
||||
|
||||
opts_data = {
|
||||
|
|
@ -58,9 +62,9 @@ opts_data = {
|
|||
-S, --no-stop-wallet-daemon Don’t stop the wallet daemon at exit
|
||||
-w, --wallet-dir=D Output or operate on wallets in directory 'D'
|
||||
instead of the working directory
|
||||
-H, --wallet-rpc-host=host Wallet RPC hostname (default: {g.monero_wallet_rpc_host!r})
|
||||
-U, --wallet-rpc-user=user Wallet RPC username (default: {g.monero_wallet_rpc_user!r})
|
||||
-P, --wallet-rpc-password=pass Wallet RPC password (default: {g.monero_wallet_rpc_password!r})
|
||||
-H, --wallet-rpc-host=host Wallet RPC hostname (default: {cfg.monero_wallet_rpc_host!r})
|
||||
-U, --wallet-rpc-user=user Wallet RPC username (default: {cfg.monero_wallet_rpc_user!r})
|
||||
-P, --wallet-rpc-password=pass Wallet RPC password (default: {cfg.monero_wallet_rpc_password!r})
|
||||
""",
|
||||
'notes': """
|
||||
|
||||
|
|
@ -212,16 +216,18 @@ $ mmgen-xmrwallet --pager txview *XMR*.sigtx
|
|||
"""
|
||||
},
|
||||
'code': {
|
||||
'options': lambda s: s.format(
|
||||
'options': lambda cfg,s: s.format(
|
||||
D=xmrwallet_uarg_info['daemon'].annot,
|
||||
R=xmrwallet_uarg_info['tx_relay_daemon'].annot,
|
||||
g=g,
|
||||
cfg=cfg,
|
||||
gc=gc,
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
cmd_args = opts.init(opts_data)
|
||||
cfg = opts.init(opts_data)
|
||||
|
||||
cmd_args = cfg._args
|
||||
|
||||
if len(cmd_args) < 2:
|
||||
opts.usage()
|
||||
|
|
@ -263,17 +269,17 @@ uo = namedtuple('uopts',[
|
|||
|
||||
uargs = ua( op, infile, wallets, spec )
|
||||
uopts = uo(
|
||||
opt.daemon or '',
|
||||
opt.tx_relay_daemon or '',
|
||||
opt.restore_height or 0,
|
||||
opt.rescan_blockchain,
|
||||
opt.no_start_wallet_daemon,
|
||||
opt.no_stop_wallet_daemon,
|
||||
opt.no_relay,
|
||||
opt.wallet_dir,
|
||||
cfg.daemon or '',
|
||||
cfg.tx_relay_daemon or '',
|
||||
cfg.restore_height or 0,
|
||||
cfg.rescan_blockchain,
|
||||
cfg.no_start_wallet_daemon,
|
||||
cfg.no_stop_wallet_daemon,
|
||||
cfg.no_relay,
|
||||
cfg.wallet_dir,
|
||||
)
|
||||
|
||||
m = getattr(MoneroWalletOps,op)(uargs,uopts)
|
||||
m = getattr(MoneroWalletOps,op)(cfg,uargs,uopts)
|
||||
|
||||
try:
|
||||
if async_run(m.main()):
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
40
mmgen/msg.py
40
mmgen/msg.py
|
|
@ -51,6 +51,9 @@ class coin_msg:
|
|||
|
||||
class base(MMGenObject):
|
||||
|
||||
def __init__(self,cfg):
|
||||
self.cfg = cfg
|
||||
|
||||
ext = 'rawmsg.json'
|
||||
signed = False
|
||||
chksum_keys = ('addrlists','message','msghash_type','network')
|
||||
|
|
@ -85,10 +88,10 @@ class coin_msg:
|
|||
return f'{self.filename_stem}.{coin_msg.signed.ext}'
|
||||
|
||||
def get_proto_from_file(self,filename):
|
||||
data = json.loads(get_data_from_file(filename))
|
||||
data = json.loads(get_data_from_file( self.cfg, filename ))
|
||||
network_id = data['metadata']['network'] if 'metadata' in data else data['network'].lower()
|
||||
coin,network = network_id.split('_')
|
||||
return init_proto( coin=coin, network=network )
|
||||
return init_proto( cfg=self.cfg, coin=coin, network=network )
|
||||
|
||||
def write_to_file(self,outdir=None,ask_overwrite=False):
|
||||
data = {
|
||||
|
|
@ -98,6 +101,7 @@ class coin_msg:
|
|||
}
|
||||
|
||||
write_data_to_file(
|
||||
cfg = self.cfg,
|
||||
outfile = os.path.join(outdir or '',self.filename),
|
||||
data = json.dumps(data,sort_keys=True,indent=4),
|
||||
desc = self.desc,
|
||||
|
|
@ -105,10 +109,15 @@ class coin_msg:
|
|||
|
||||
class new(base):
|
||||
|
||||
def __init__(self,message,addrlists,msghash_type,*args,**kwargs):
|
||||
def __init__(self,cfg,message,addrlists,msghash_type,*args,**kwargs):
|
||||
|
||||
self.cfg = cfg
|
||||
|
||||
msghash_type = msghash_type or self.msg_cls.msghash_types[0]
|
||||
|
||||
if msghash_type not in self.msg_cls.msghash_types:
|
||||
die(2,f'msghash_type {msghash_type!r} not supported for {self.proto.base_proto} protocol')
|
||||
|
||||
self.data = {
|
||||
'network': '{}_{}'.format( self.proto.coin.lower(), self.proto.network ),
|
||||
'addrlists': [MMGenIDRange(self.proto,i) for i in addrlists.split()],
|
||||
|
|
@ -119,13 +128,16 @@ class coin_msg:
|
|||
|
||||
class completed(base):
|
||||
|
||||
def __init__(self,data,infile,*args,**kwargs):
|
||||
def __init__(self,cfg,data,infile,*args,**kwargs):
|
||||
|
||||
self.cfg = cfg
|
||||
|
||||
if data:
|
||||
self.__dict__ = data
|
||||
return
|
||||
|
||||
self.data = get_data_from_file(
|
||||
cfg = self.cfg,
|
||||
infile = infile,
|
||||
desc = self.desc )
|
||||
|
||||
|
|
@ -210,6 +222,7 @@ class coin_msg:
|
|||
|
||||
async def sign_list(al_in,seed):
|
||||
al = KeyAddrList(
|
||||
cfg = self.cfg,
|
||||
proto = self.proto,
|
||||
seed = seed,
|
||||
addr_idxs = al_in.idxlist,
|
||||
|
|
@ -238,11 +251,11 @@ class coin_msg:
|
|||
|
||||
if self.proto.sign_mode == 'daemon':
|
||||
from .rpc import rpc_init
|
||||
self.rpc = await rpc_init(self.proto)
|
||||
self.rpc = await rpc_init(self.cfg,self.proto)
|
||||
|
||||
from .wallet import Wallet
|
||||
from .addrlist import KeyAddrList
|
||||
wallet_seeds = [Wallet(fn=fn).seed for fn in wallet_files]
|
||||
wallet_seeds = [Wallet(cfg=self.cfg,fn=fn).seed for fn in wallet_files]
|
||||
need_sids = remove_dups([al.sid for al in self.addrlists], quiet=True)
|
||||
saved_seeds = list()
|
||||
|
||||
|
|
@ -302,7 +315,7 @@ class coin_msg:
|
|||
|
||||
if self.proto.sign_mode == 'daemon':
|
||||
from .rpc import rpc_init
|
||||
self.rpc = await rpc_init(self.proto)
|
||||
self.rpc = await rpc_init(self.cfg,self.proto)
|
||||
|
||||
for k,v in sigs.items():
|
||||
ret = await self.do_verify(
|
||||
|
|
@ -333,10 +346,13 @@ class coin_msg:
|
|||
|
||||
class exported_sigs(signed_online):
|
||||
|
||||
def __init__(self,infile,*args,**kwargs):
|
||||
def __init__(self,cfg,infile,*args,**kwargs):
|
||||
|
||||
self.cfg = cfg
|
||||
|
||||
self.data = json.loads(
|
||||
get_data_from_file(
|
||||
cfg = self.cfg,
|
||||
infile = infile,
|
||||
desc = self.desc )
|
||||
)
|
||||
|
|
@ -348,7 +364,7 @@ class coin_msg:
|
|||
self.data['signatures']
|
||||
)}
|
||||
|
||||
def _get_obj(clsname,coin=None,network='mainnet',infile=None,data=None,*args,**kwargs):
|
||||
def _get_obj(clsname,cfg,coin=None,network='mainnet',infile=None,data=None,*args,**kwargs):
|
||||
|
||||
assert not args, 'msg:_get_obj(): only keyword args allowed'
|
||||
|
||||
|
|
@ -359,8 +375,8 @@ def _get_obj(clsname,coin=None,network='mainnet',infile=None,data=None,*args,**k
|
|||
|
||||
proto = (
|
||||
data['proto'] if data else
|
||||
init_proto( coin=coin, network=network ) if coin else
|
||||
coin_msg.base().get_proto_from_file(infile) )
|
||||
init_proto( cfg=cfg, coin=coin, network=network ) if coin else
|
||||
coin_msg.base(cfg=cfg).get_proto_from_file(infile) )
|
||||
|
||||
try:
|
||||
msg_cls = getattr(
|
||||
|
|
@ -373,7 +389,7 @@ def _get_obj(clsname,coin=None,network='mainnet',infile=None,data=None,*args,**k
|
|||
me.msg_cls = msg_cls
|
||||
me.proto = proto
|
||||
|
||||
me.__init__(infile=infile,data=data,*args,**kwargs)
|
||||
me.__init__(cfg,infile=infile,data=data,*args,**kwargs)
|
||||
|
||||
return me
|
||||
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
244
mmgen/opts.py
244
mmgen/opts.py
|
|
@ -21,17 +21,12 @@ opts: MMGen-specific options processing after generic processing by share.Opts
|
|||
"""
|
||||
import sys,os
|
||||
|
||||
from .globalvars import g,gc
|
||||
from .globalvars import gc,Config
|
||||
from .base_obj import Lockable
|
||||
|
||||
import mmgen.share.Opts
|
||||
|
||||
class UserOpts(Lockable):
|
||||
_autolock = False
|
||||
_default_to_none = True
|
||||
_set_ok = ('usr_randchars',)
|
||||
_reset_ok = ('quiet','verbose','yes')
|
||||
|
||||
class UserOpts: pass
|
||||
opt = UserOpts()
|
||||
|
||||
def usage():
|
||||
|
|
@ -54,22 +49,23 @@ def delete_data(opts_data):
|
|||
del mmgen.share.Opts.process_uopts
|
||||
del mmgen.share.Opts.parse_opts
|
||||
|
||||
def post_init():
|
||||
def post_init(cfg):
|
||||
global opts_data_save,opt_filter_save
|
||||
if opt.help or opt.longhelp:
|
||||
print_help(opt,opts_data_save,opt_filter_save)
|
||||
if cfg.help or cfg.longhelp:
|
||||
print_help(cfg,opts_data_save,opt_filter_save)
|
||||
else:
|
||||
delete_data(opts_data_save)
|
||||
del opts_data_save,opt_filter_save
|
||||
|
||||
def print_help(opt,opts_data,opt_filter):
|
||||
def print_help(cfg,opts_data,opt_filter):
|
||||
|
||||
if not 'code' in opts_data:
|
||||
opts_data['code'] = {}
|
||||
|
||||
from .protocol import init_proto_from_opts
|
||||
proto = init_proto_from_opts(need_amt=True)
|
||||
from .protocol import init_proto_from_cfg
|
||||
proto = init_proto_from_cfg(cfg,need_amt=True)
|
||||
|
||||
if getattr(opt,'longhelp',None):
|
||||
if getattr(cfg,'longhelp',None):
|
||||
opts_data['code']['long_options'] = common_opts_data['code']
|
||||
def remove_unneeded_long_opts():
|
||||
d = opts_data['text']['long_options']
|
||||
|
|
@ -79,16 +75,16 @@ def print_help(opt,opts_data,opt_filter):
|
|||
remove_unneeded_long_opts()
|
||||
|
||||
from .ui import do_pager
|
||||
do_pager(mmgen.share.Opts.make_help( proto, opt, opts_data, opt_filter ))
|
||||
do_pager(mmgen.share.Opts.make_help( cfg, proto, opts_data, opt_filter ))
|
||||
|
||||
sys.exit(0)
|
||||
|
||||
def fmt_opt(o):
|
||||
return '--' + o.replace('_','-')
|
||||
|
||||
def die_on_incompatible_opts(incompat_list):
|
||||
for group in incompat_list:
|
||||
bad = [k for k in opt.__dict__ if k in group and getattr(opt,k) != None]
|
||||
def die_on_incompatible_opts(cfg):
|
||||
for group in cfg.incompatible_opts:
|
||||
bad = [k for k in cfg.__dict__ if k in group and getattr(cfg,k) != None]
|
||||
if len(bad) > 1:
|
||||
from .util import die
|
||||
die(1,'Conflicting options: {}'.format(', '.join(map(fmt_opt,bad))))
|
||||
|
|
@ -117,18 +113,14 @@ def opt_preproc_debug(po):
|
|||
for e in d:
|
||||
Msg(' {:<20}: {}'.format(*e))
|
||||
|
||||
def opt_postproc_debug():
|
||||
a = [k for k in dir(opt) if k[:2] != '__' and getattr(opt,k) != None]
|
||||
b = [k for k in dir(opt) if k[:2] != '__' and getattr(opt,k) == None]
|
||||
def opt_postproc_debug(cfg):
|
||||
a = [k for k in dir(cfg) if k[:2] != '__' and getattr(cfg,k) != None]
|
||||
b = [k for k in dir(cfg) if k[:2] != '__' and getattr(cfg,k) == None]
|
||||
from .util import Msg
|
||||
Msg(' Opts after processing:')
|
||||
for k in a:
|
||||
v = getattr(opt,k)
|
||||
Msg(' {:18}: {!r:<6} [{}]'.format(k,v,type(v).__name__))
|
||||
Msg(' Global vars:')
|
||||
for e in [d for d in dir(g) if d[:2] != '__']:
|
||||
Msg(' {:<20}: {}'.format(e, getattr(g,e)))
|
||||
Msg(" Opts set to 'None':")
|
||||
for e in [d for d in dir(cfg) if d[:2] != '__']:
|
||||
Msg(' {:<20}: {}'.format(e, getattr(cfg,e)))
|
||||
Msg(" Global vars set to 'None':")
|
||||
Msg(' {}\n'.format('\n '.join(b)))
|
||||
Msg('\n=== end opts.py debug ===\n')
|
||||
|
||||
|
|
@ -157,15 +149,18 @@ def set_for_type(val,refval,desc,invert_bool=False,src=None):
|
|||
type(refval).__name__) )
|
||||
|
||||
def override_globals_from_cfg_file(
|
||||
cfg,
|
||||
ucfg,
|
||||
autoset_opts,
|
||||
cfgfile_autoset_opts,
|
||||
cfgfile_auto_typeset_opts,
|
||||
env_globals,
|
||||
need_proto ):
|
||||
|
||||
if need_proto:
|
||||
from .protocol import init_proto
|
||||
|
||||
for d in ucfg.get_lines():
|
||||
if d.name in g.cfg_file_opts:
|
||||
if d.name in cfg.cfg_file_opts:
|
||||
ns = d.name.split('_')
|
||||
if ns[0] in gc.core_coins:
|
||||
if not need_proto:
|
||||
|
|
@ -174,10 +169,10 @@ def override_globals_from_cfg_file(
|
|||
(ns[2:],ns[1]=='testnet') if len(ns) > 2 and ns[1] in ('mainnet','testnet') else
|
||||
(ns[1:],False)
|
||||
)
|
||||
cls = type(init_proto( ns[0], tn, need_amt=True )) # no instance yet, so override _class_ attr
|
||||
cls = type(init_proto( cfg, ns[0], tn, need_amt=True )) # no instance yet, so override _class_ attr
|
||||
attr = '_'.join(nse)
|
||||
else:
|
||||
cls = g # g is "singleton" instance, so override _instance_ attr
|
||||
cls = cfg
|
||||
attr = d.name
|
||||
refval = getattr(cls,attr)
|
||||
val = ucfg.parse_value(d.value,refval)
|
||||
|
|
@ -187,29 +182,31 @@ def override_globals_from_cfg_file(
|
|||
val_conv = set_for_type(val,refval,attr,src=ucfg.fn)
|
||||
if attr not in env_globals:
|
||||
setattr(cls,attr,val_conv)
|
||||
elif d.name in g.autoset_opts:
|
||||
autoset_opts[d.name] = d.value
|
||||
elif d.name in cfg.autoset_opts:
|
||||
cfgfile_autoset_opts[d.name] = d.value
|
||||
elif d.name in cfg.auto_typeset_opts:
|
||||
cfgfile_auto_typeset_opts[d.name] = d.value
|
||||
else:
|
||||
from .util import die
|
||||
die( 'CfgFileParseError', f'{d.name!r}: unrecognized option in {ucfg.fn!r}, line {d.lineno}' )
|
||||
|
||||
def override_globals_from_env():
|
||||
def override_globals_from_env(cfg):
|
||||
for name,val in ((k,v) for k,v in os.environ.items() if k.startswith('MMGEN_')):
|
||||
if name == 'MMGEN_DEBUG_ALL':
|
||||
continue
|
||||
elif name in g.env_opts:
|
||||
elif name in cfg.env_opts:
|
||||
if val: # ignore empty string values; string value of '0' or 'false' sets variable to False
|
||||
disable = name.startswith('MMGEN_DISABLE_')
|
||||
gname = name[(6,14)[disable]:].lower()
|
||||
if hasattr(g,gname):
|
||||
setattr(g,gname,set_for_type(val,getattr(g,gname),name,disable))
|
||||
if hasattr(cfg,gname):
|
||||
setattr(cfg,gname,set_for_type(val,getattr(cfg,gname),name,disable))
|
||||
yield gname
|
||||
else:
|
||||
raise ValueError(f'Name {gname!r} not present in globals')
|
||||
else:
|
||||
raise ValueError(f'{name!r} is not a valid MMGen environment variable')
|
||||
|
||||
def show_common_opts_diff():
|
||||
def show_common_opts_diff(cfg):
|
||||
|
||||
def common_opts_data_to_list():
|
||||
for l in common_opts_data['text'].splitlines():
|
||||
|
|
@ -220,16 +217,16 @@ def show_common_opts_diff():
|
|||
from .util import fmt_list
|
||||
return fmt_list(['--'+s.replace('_','-') for s in set_data],fmt='col',indent=' ')
|
||||
|
||||
a = g.common_opts
|
||||
a = cfg.common_opts
|
||||
b = list(common_opts_data_to_list())
|
||||
a_minus_b = [e for e in a if e not in b]
|
||||
b_minus_a = [e for e in b if e not in a]
|
||||
a_and_b = [e for e in a if e in b]
|
||||
|
||||
from .util import msg
|
||||
msg(f'g.common_opts - common_opts_data:\n {do_fmt(a_minus_b) if a_minus_b else "None"}\n')
|
||||
msg(f'common_opts_data - g.common_opts (these do not set global var):\n{do_fmt(b_minus_a)}\n')
|
||||
msg(f'common_opts_data ^ g.common_opts (these set global var):\n{do_fmt(a_and_b)}\n')
|
||||
msg(f'cfg.common_opts - common_opts_data:\n {do_fmt(a_minus_b) if a_minus_b else "None"}\n')
|
||||
msg(f'common_opts_data - cfg.common_opts (these do not set global var):\n{do_fmt(b_minus_a)}\n')
|
||||
msg(f'common_opts_data ^ cfg.common_opts (these set global var):\n{do_fmt(a_and_b)}\n')
|
||||
|
||||
sys.exit(0)
|
||||
|
||||
|
|
@ -291,8 +288,8 @@ def init(
|
|||
parse_only = False,
|
||||
parsed_opts = None,
|
||||
need_proto = True,
|
||||
do_post_init = False,
|
||||
return_parsed = False ):
|
||||
need_amt = True,
|
||||
do_post_init = False ):
|
||||
|
||||
if opts_data is None:
|
||||
opts_data = opts_data_dfl
|
||||
|
|
@ -314,7 +311,9 @@ def init(
|
|||
if parse_only and not any(k in po.user_opts for k in ('version','help','longhelp')):
|
||||
return po
|
||||
|
||||
if g.debug_opts:
|
||||
cfg = Config()
|
||||
|
||||
if cfg.debug_opts: # TODO: this does nothing
|
||||
opt_preproc_debug(po)
|
||||
|
||||
# Copy parsed opts to opt, setting values to None if not set by user
|
||||
|
|
@ -323,8 +322,8 @@ def init(
|
|||
+ po.skipped_opts
|
||||
+ tuple(add_opts or [])
|
||||
+ tuple(init_opts or [])
|
||||
+ g.init_opts
|
||||
+ g.common_opts ):
|
||||
+ cfg.init_opts
|
||||
+ cfg.common_opts ):
|
||||
setattr(opt,o,po.user_opts[o] if o in po.user_opts else None)
|
||||
|
||||
if opt.version:
|
||||
|
|
@ -333,108 +332,110 @@ def init(
|
|||
if opt.show_hash_presets:
|
||||
_show_hash_presets() # exits
|
||||
|
||||
from .term import init_term
|
||||
init_term()
|
||||
|
||||
from .util import wrap_ripemd160
|
||||
wrap_ripemd160() # ripemd160 used by mmgen_cfg_file()
|
||||
|
||||
# === begin global var initialization === #
|
||||
# === begin Config() initialization === #
|
||||
|
||||
env_globals = tuple(override_globals_from_env())
|
||||
env_globals = tuple(override_globals_from_env(cfg))
|
||||
|
||||
# do this after setting ‘hold_protect_disable’ from env
|
||||
from .term import init_term
|
||||
init_term(cfg)
|
||||
|
||||
# --data-dir overrides computed value of data_dir_root
|
||||
g.data_dir_root_override = opt.data_dir
|
||||
cfg.data_dir_root_override = opt.data_dir
|
||||
if opt.data_dir:
|
||||
del opt.data_dir
|
||||
|
||||
from .fileutil import check_or_create_dir
|
||||
check_or_create_dir(g.data_dir_root)
|
||||
check_or_create_dir(cfg.data_dir_root)
|
||||
|
||||
cfgfile_autoset_opts = {}
|
||||
cfgfile_auto_typeset_opts = {}
|
||||
|
||||
if not opt.skip_cfg_file:
|
||||
from .cfgfile import mmgen_cfg_file
|
||||
# check for changes in system template file - term must be initialized
|
||||
mmgen_cfg_file('sample')
|
||||
mmgen_cfg_file(cfg,'sample')
|
||||
override_globals_from_cfg_file(
|
||||
mmgen_cfg_file('usr'),
|
||||
cfg,
|
||||
mmgen_cfg_file(cfg,'usr'),
|
||||
cfgfile_autoset_opts,
|
||||
cfgfile_auto_typeset_opts,
|
||||
env_globals,
|
||||
need_proto )
|
||||
|
||||
# Set globals from opts, setting type from original global value
|
||||
# Do here, before opts are set from globals below
|
||||
for k in (g.common_opts + g.opt_sets_global):
|
||||
if hasattr(opt,k):
|
||||
val = getattr(opt,k)
|
||||
if val != None and hasattr(g,k):
|
||||
setattr(g,k,set_for_type(val,getattr(g,k),'--'+k))
|
||||
# Set globals from opts, setting type from original global value if it exists:
|
||||
auto_keys = tuple(cfg.autoset_opts.keys()) + tuple(cfg.auto_typeset_opts.keys())
|
||||
for key,val in opt.__dict__.items():
|
||||
if val is not None and key not in auto_keys:
|
||||
setattr(cfg, key, set_for_type(val,getattr(cfg,key),'--'+key) if hasattr(cfg,key) else val)
|
||||
|
||||
if g.regtest or g.bob or g.alice or g.carol or gc.prog_name == 'mmgen-regtest':
|
||||
g.network = 'regtest'
|
||||
g.regtest_user = 'bob' if g.bob else 'alice' if g.alice else 'carol' if g.carol else None
|
||||
if cfg.regtest or cfg.bob or cfg.alice or cfg.carol or gc.prog_name == 'mmgen-regtest':
|
||||
cfg.network = 'regtest'
|
||||
cfg.regtest_user = 'bob' if cfg.bob else 'alice' if cfg.alice else 'carol' if cfg.carol else None
|
||||
else:
|
||||
g.network = 'testnet' if g.testnet else 'mainnet'
|
||||
cfg.network = 'testnet' if cfg.testnet else 'mainnet'
|
||||
|
||||
g.coin = g.coin.upper() or 'BTC'
|
||||
g.token = g.token.upper() or None
|
||||
cfg.coin = cfg.coin.upper()
|
||||
cfg.token = cfg.token.upper() or None
|
||||
|
||||
# === end global var initialization === #
|
||||
|
||||
"""
|
||||
g.color is finalized, so initialize color
|
||||
cfg.color is finalized, so initialize color
|
||||
"""
|
||||
if g.color: # MMGEN_DISABLE_COLOR sets this to False
|
||||
if cfg.color: # MMGEN_DISABLE_COLOR sets this to False
|
||||
from .color import init_color
|
||||
init_color(num_colors=('auto',256)[bool(g.force_256_color)])
|
||||
init_color(num_colors=('auto',256)[bool(cfg.force_256_color)])
|
||||
|
||||
# Set user opts from globals:
|
||||
# - if opt is unset, set it to global value
|
||||
# - if opt is set, convert its type to that of global value
|
||||
for k in g.global_sets_opt:
|
||||
if hasattr(opt,k) and getattr(opt,k) != None:
|
||||
setattr(opt,k,set_for_type(getattr(opt,k),getattr(g,k),'--'+k))
|
||||
else:
|
||||
setattr(opt,k,getattr(g,k))
|
||||
die_on_incompatible_opts(cfg)
|
||||
|
||||
if need_proto:
|
||||
from .protocol import warn_trustlevel
|
||||
warn_trustlevel(g.coin)
|
||||
|
||||
die_on_incompatible_opts(g.incompatible_opts)
|
||||
|
||||
check_or_create_dir(g.data_dir)
|
||||
|
||||
# Check user-set opts without modifying them
|
||||
check_usr_opts(po.user_opts)
|
||||
check_or_create_dir(cfg.data_dir)
|
||||
|
||||
# Check autoset opts, setting if unset
|
||||
for key in g.autoset_opts:
|
||||
for key in cfg.autoset_opts:
|
||||
if hasattr(opt,key):
|
||||
assert not hasattr(cfg,key), f'{key!r} is in cfg!'
|
||||
if getattr(opt,key) is not None:
|
||||
setattr(opt, key, get_autoset_opt(key,getattr(opt,key),src='cmdline'))
|
||||
setattr(cfg, key, get_autoset_opt(cfg,key,getattr(opt,key),src='cmdline'))
|
||||
elif key in cfgfile_autoset_opts:
|
||||
setattr(opt, key, get_autoset_opt(key,cfgfile_autoset_opts[key],src='cfgfile'))
|
||||
setattr(cfg, key, get_autoset_opt(cfg,key,cfgfile_autoset_opts[key],src='cfgfile'))
|
||||
else:
|
||||
setattr(opt, key, g.autoset_opts[key].choices[0])
|
||||
setattr(cfg, key, cfg.autoset_opts[key].choices[0])
|
||||
|
||||
set_auto_typeset_opts()
|
||||
set_auto_typeset_opts(cfg,cfgfile_auto_typeset_opts)
|
||||
|
||||
if opt.verbose:
|
||||
opt.quiet = None
|
||||
if cfg.debug and gc.prog_name != 'test.py':
|
||||
cfg.verbose = True
|
||||
|
||||
if g.debug and gc.prog_name != 'test.py':
|
||||
opt.verbose,opt.quiet = (True,None)
|
||||
if cfg.verbose:
|
||||
cfg.quiet = False
|
||||
|
||||
if g.debug_opts:
|
||||
opt_postproc_debug()
|
||||
if cfg.debug_opts:
|
||||
opt_postproc_debug(cfg)
|
||||
|
||||
g.lock()
|
||||
opt.lock()
|
||||
# Check user-set opts without modifying them
|
||||
check_usr_opts(cfg,po.user_opts)
|
||||
|
||||
# print help screen only after globals and opts initialized and locked:
|
||||
if opt.help or opt.longhelp:
|
||||
from .util import Util
|
||||
cfg._util = Util(cfg)
|
||||
cfg._uopts = po.user_opts
|
||||
cfg._args = po.cmd_args
|
||||
|
||||
cfg.lock()
|
||||
|
||||
if need_proto:
|
||||
from .protocol import warn_trustlevel,init_proto_from_cfg
|
||||
warn_trustlevel(cfg)
|
||||
# requires the default-to-none behavior, so do after the lock:
|
||||
cfg._proto = init_proto_from_cfg(cfg,need_amt=need_amt)
|
||||
|
||||
# print help screen only after globals initialized and locked:
|
||||
if cfg.help or cfg.longhelp:
|
||||
if not do_post_init:
|
||||
print_help(opt,opts_data,opt_filter) # exits
|
||||
print_help(cfg,opts_data,opt_filter) # exits
|
||||
|
||||
if do_post_init:
|
||||
global opts_data_save,opt_filter_save
|
||||
|
|
@ -443,9 +444,9 @@ def init(
|
|||
else:
|
||||
delete_data(opts_data)
|
||||
|
||||
return po if return_parsed else po.cmd_args
|
||||
return cfg
|
||||
|
||||
def check_usr_opts(usr_opts): # Raises an exception if any check fails
|
||||
def check_usr_opts(cfg,usr_opts): # Raises an exception if any check fails
|
||||
|
||||
def opt_splits(val,sep,n,desc):
|
||||
sepword = 'comma' if sep == ',' else 'colon' if sep == ':' else repr(sep)
|
||||
|
|
@ -577,8 +578,8 @@ def check_usr_opts(usr_opts): # Raises an exception if any check fails
|
|||
if val == 0:
|
||||
return
|
||||
opt_is_int(val,desc)
|
||||
opt_compares(val,'>=',g.min_urandchars,desc)
|
||||
opt_compares(val,'<=',g.max_urandchars,desc)
|
||||
opt_compares(val,'>=',cfg.min_urandchars,desc)
|
||||
opt_compares(val,'<=',cfg.max_urandchars,desc)
|
||||
|
||||
def chk_tx_fee(key,val,desc):
|
||||
pass
|
||||
|
|
@ -605,7 +606,7 @@ def check_usr_opts(usr_opts): # Raises an exception if any check fails
|
|||
# def chk_bob(key,val,desc):
|
||||
# from .proto.btc.regtest import MMGenRegtest
|
||||
# try:
|
||||
# os.stat(os.path.join(MMGenRegtest(g.coin).d.datadir,'regtest','debug.log'))
|
||||
# os.stat(os.path.join(MMGenRegtest(cfg,cfg.coin).d.datadir,'regtest','debug.log'))
|
||||
# except:
|
||||
# die( 'UserOptError',
|
||||
# 'Regtest (Bob and Alice) mode not set up yet. ' +
|
||||
|
|
@ -634,10 +635,10 @@ def check_usr_opts(usr_opts): # Raises an exception if any check fails
|
|||
cfuncs = { k:v for k,v in locals().items() if k.startswith('chk_') }
|
||||
|
||||
for key in usr_opts:
|
||||
val = getattr(opt,key)
|
||||
val = getattr(cfg,key)
|
||||
desc = f'parameter for {fmt_opt(key)!r} option'
|
||||
|
||||
if key in g.infile_opts:
|
||||
if key in cfg.infile_opts:
|
||||
from .fileutil import check_infile
|
||||
check_infile(val) # file exists and is readable - dies on error
|
||||
elif key == 'outdir':
|
||||
|
|
@ -645,19 +646,22 @@ def check_usr_opts(usr_opts): # Raises an exception if any check fails
|
|||
check_outdir(val) # dies on error
|
||||
elif 'chk_'+key in cfuncs:
|
||||
cfuncs['chk_'+key](key,val,desc)
|
||||
elif g.debug:
|
||||
elif cfg.debug:
|
||||
Msg(f'check_usr_opts(): No test for opt {key!r}')
|
||||
|
||||
def set_auto_typeset_opts():
|
||||
def set_auto_typeset_opts(cfg,cfgfile_auto_typeset_opts):
|
||||
|
||||
def do_set(key,val,ref_type):
|
||||
setattr(opt,key,None if val is None else ref_type(val))
|
||||
assert not hasattr(cfg,key), f'{key!r} is in cfg!'
|
||||
setattr(cfg,key,None if val is None else ref_type(val))
|
||||
|
||||
for key,ref_type in g.auto_typeset_opts.items():
|
||||
for key,ref_type in cfg.auto_typeset_opts.items():
|
||||
if hasattr(opt,key):
|
||||
do_set(key, getattr(opt,key), ref_type)
|
||||
elif key in cfgfile_auto_typeset_opts:
|
||||
do_set(key, cfgfile_auto_typeset_opts[key], ref_type)
|
||||
|
||||
def get_autoset_opt(key,val,src):
|
||||
def get_autoset_opt(cfg,key,val,src):
|
||||
|
||||
def die_on_err(desc):
|
||||
from .util import fmt_list,die
|
||||
|
|
@ -687,6 +691,6 @@ def get_autoset_opt(key,val,src):
|
|||
else:
|
||||
die_on_err('unique substring of')
|
||||
|
||||
data = g.autoset_opts[key]
|
||||
data = cfg.autoset_opts[key]
|
||||
|
||||
return getattr(opt_type,data.type)()
|
||||
|
|
|
|||
|
|
@ -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())
|
||||
|
|
|
|||
|
|
@ -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')
|
||||
|
|
|
|||
|
|
@ -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 (
|
||||
|
|
|
|||
|
|
@ -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 ')
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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'),
|
||||
|
|
|
|||
|
|
@ -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'):
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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: {}'
|
||||
|
|
|
|||
|
|
@ -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"""
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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())]
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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)) )
|
||||
)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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(),
|
||||
|
|
|
|||
|
|
@ -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!')
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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'])
|
||||
|
|
|
|||
|
|
@ -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'])
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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 )
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 )
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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):
|
||||
"""
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
26
mmgen/rpc.py
26
mmgen/rpc.py
|
|
@ -20,11 +20,12 @@
|
|||
rpc: Cryptocoin RPC library for the MMGen suite
|
||||
"""
|
||||
|
||||
import base64,json,asyncio,importlib
|
||||
import re,base64,json,asyncio,importlib
|
||||
from decimal import Decimal
|
||||
from collections import namedtuple
|
||||
|
||||
from .common import *
|
||||
from .globalvars import gc
|
||||
from .util import msg,die,fmt,fmt_list,pp_fmt
|
||||
from .base_obj import AsyncInit
|
||||
from .obj import NonNegativeInt
|
||||
from .objmethods import Hilite,InitErrors,MMGenObject
|
||||
|
|
@ -85,6 +86,7 @@ class RPCBackends:
|
|||
class base:
|
||||
|
||||
def __init__(self,caller):
|
||||
self.cfg = caller.cfg
|
||||
self.host = caller.host
|
||||
self.port = caller.port
|
||||
self.proxy = caller.proxy
|
||||
|
|
@ -109,7 +111,7 @@ class RPCBackends:
|
|||
async def __init__(self,caller):
|
||||
super().__init__(caller)
|
||||
import aiohttp
|
||||
self.connector = aiohttp.TCPConnector(limit_per_host=g.aiohttp_rpc_queue_len)
|
||||
self.connector = aiohttp.TCPConnector(limit_per_host=self.cfg.aiohttp_rpc_queue_len)
|
||||
self.session = aiohttp.ClientSession(
|
||||
headers = { 'Content-Type': 'application/json' },
|
||||
connector = self.connector,
|
||||
|
|
@ -261,14 +263,16 @@ class RPCClient(MMGenObject):
|
|||
network_proto = 'http'
|
||||
proxy = None
|
||||
|
||||
def __init__(self,host,port,test_connection=True):
|
||||
def __init__(self,cfg,host,port,test_connection=True):
|
||||
|
||||
self.cfg = cfg
|
||||
|
||||
# aiohttp workaround, and may speed up RPC performance overall on some systems:
|
||||
if gc.platform == 'win' and host == 'localhost':
|
||||
host = '127.0.0.1'
|
||||
|
||||
global dmsg_rpc,dmsg_rpc_backend
|
||||
if not g.debug_rpc:
|
||||
if not self.cfg.debug_rpc:
|
||||
dmsg_rpc = dmsg_rpc_backend = noop
|
||||
|
||||
dmsg_rpc(f'=== {type(self).__name__}.__init__() debug ===')
|
||||
|
|
@ -285,11 +289,11 @@ class RPCClient(MMGenObject):
|
|||
self.host_url = f'{self.network_proto}://{host}:{port}'
|
||||
self.host = host
|
||||
self.port = port
|
||||
self.timeout = g.http_timeout
|
||||
self.timeout = self.cfg.http_timeout
|
||||
self.auth = None
|
||||
|
||||
def _get_backend(self,backend):
|
||||
backend_id = backend or opt.rpc_backend
|
||||
backend_id = backend or self.cfg.rpc_backend
|
||||
if backend_id == 'auto':
|
||||
return {'linux':RPCBackends.httplib,'win':RPCBackends.requests}[gc.platform](self)
|
||||
else:
|
||||
|
|
@ -443,6 +447,7 @@ class RPCClient(MMGenObject):
|
|||
""",indent=' '))
|
||||
|
||||
async def rpc_init(
|
||||
cfg,
|
||||
proto,
|
||||
backend = None,
|
||||
daemon = None,
|
||||
|
|
@ -458,15 +463,16 @@ async def rpc_init(
|
|||
|
||||
from .daemon import CoinDaemon
|
||||
rpc = await cls(
|
||||
cfg = cfg,
|
||||
proto = proto,
|
||||
daemon = daemon or CoinDaemon(proto=proto,test_suite=g.test_suite),
|
||||
backend = backend or opt.rpc_backend,
|
||||
daemon = daemon or CoinDaemon(cfg,proto=proto,test_suite=cfg.test_suite),
|
||||
backend = backend or cfg.rpc_backend,
|
||||
ignore_wallet = ignore_wallet )
|
||||
|
||||
if rpc.daemon_version > rpc.daemon.coind_version:
|
||||
rpc.handle_unsupported_daemon_version(
|
||||
proto.name,
|
||||
ignore_daemon_version or proto.ignore_daemon_version or g.ignore_daemon_version )
|
||||
ignore_daemon_version or proto.ignore_daemon_version or cfg.ignore_daemon_version )
|
||||
|
||||
if rpc.chain not in proto.chain_names:
|
||||
die( 'RPCChainMismatch', '\n' + fmt(f"""
|
||||
|
|
|
|||
|
|
@ -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):
|
||||
|
|
|
|||
|
|
@ -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') )
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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):
|
||||
|
|
|
|||
|
|
@ -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 )
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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):
|
||||
|
|
|
|||
|
|
@ -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)),
|
||||
|
|
|
|||
|
|
@ -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')
|
||||
|
|
|
|||
|
|
@ -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']
|
||||
|
|
|
|||
|
|
@ -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}')
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 )
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
||||
|
|
|
|||
|
|
@ -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')
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
Loading…
Add table
Add a link
Reference in a new issue