From ca1cdacaf11ae4bc632a56772f7b742872cee37f Mon Sep 17 00:00:00 2001 From: The MMGen Project Date: Thu, 9 Apr 2020 17:11:59 +0000 Subject: [PATCH] move wallet classes from seed.py to new module wallet.py --- mmgen/common.py | 2 +- mmgen/filename.py | 4 +- mmgen/main_addrgen.py | 2 +- mmgen/main_autosign.py | 4 +- mmgen/main_passgen.py | 2 +- mmgen/main_seedjoin.py | 3 +- mmgen/main_txbump.py | 2 +- mmgen/main_txdo.py | 2 +- mmgen/main_txsign.py | 2 +- mmgen/main_wallet.py | 2 +- mmgen/opts.py | 4 +- mmgen/seed.py | 1145 +------------------------------- mmgen/tool.py | 10 +- mmgen/txsign.py | 2 +- mmgen/util.py | 4 +- setup.py | 5 +- test/test_py_d/ts_input.py | 2 +- test/test_py_d/ts_main.py | 2 +- test/test_py_d/ts_ref.py | 2 +- test/test_py_d/ts_ref_3seed.py | 8 +- test/test_py_d/ts_regtest.py | 2 +- test/test_py_d/ts_seedsplit.py | 2 +- test/test_py_d/ts_shared.py | 2 +- test/test_py_d/ts_wallet.py | 2 +- test/tooltest2.py | 2 +- 25 files changed, 36 insertions(+), 1183 deletions(-) diff --git a/mmgen/common.py b/mmgen/common.py index 98af2f0d..e576e0e7 100755 --- a/mmgen/common.py +++ b/mmgen/common.py @@ -29,7 +29,7 @@ from mmgen.util import * def help_notes(k): from mmgen.obj import SubSeedIdxRange,SeedShareIdx,SeedShareCount,MasterShareIdx - from mmgen.seed import Wallet + from .wallet import Wallet from mmgen.tx import MMGenTX def fee_spec_letters(use_quotes=False): cu = g.proto.coin_amt.units diff --git a/mmgen/filename.py b/mmgen/filename.py index f11a24bb..220fb445 100755 --- a/mmgen/filename.py +++ b/mmgen/filename.py @@ -39,7 +39,7 @@ class Filename(MMGenObject): self.ctime = None self.atime = None - from mmgen.seed import Wallet + from .wallet import Wallet from mmgen.tx import MMGenTX if ftype: if isinstance(ftype,type): @@ -99,7 +99,7 @@ def find_files_in_dir(ftype,fdir,no_dups=False): if not isinstance(ftype,type): die(3,"'{}': is of type {} (not a subclass of type 'type')".format(ftype,type(ftype))) - from mmgen.seed import Wallet + from .wallet import Wallet if not issubclass(ftype,Wallet): die(3,"'{}': not a recognized file type".format(ftype)) diff --git a/mmgen/main_addrgen.py b/mmgen/main_addrgen.py index 7e2a6123..93facc71 100755 --- a/mmgen/main_addrgen.py +++ b/mmgen/main_addrgen.py @@ -24,7 +24,7 @@ mmgen-addrgen: Generate a series or range of addresses from an MMGen from mmgen.common import * from mmgen.crypto import * from mmgen.addr import * -from mmgen.seed import Wallet +from .wallet import Wallet if g.prog_name == 'mmgen-keygen': gen_what = 'keys' diff --git a/mmgen/main_autosign.py b/mmgen/main_autosign.py index 247a38aa..55db26fd 100755 --- a/mmgen/main_autosign.py +++ b/mmgen/main_autosign.py @@ -249,7 +249,7 @@ def decrypt_wallets(): opt.set_by_user = ['hash_preset'] opt.passwd_file = os.path.join(tx_dir,key_fn) # opt.passwd_file = '/tmp/key' - from mmgen.seed import Wallet + from .wallet import Wallet msg("Unlocking wallet{} with key from '{}'".format(suf(wfs),opt.passwd_file)) fails = 0 for wf in wfs: @@ -352,7 +352,7 @@ def create_wallet_dir(): def setup(): remove_wallet_dir() gen_key(no_unmount=True) - from mmgen.seed import Wallet + from .wallet import Wallet opt.hidden_incog_input_params = None opt.quiet = True opt.in_fmt = 'words' diff --git a/mmgen/main_passgen.py b/mmgen/main_passgen.py index 88047a7c..a0f351e1 100755 --- a/mmgen/main_passgen.py +++ b/mmgen/main_passgen.py @@ -24,7 +24,7 @@ mmgen-passgen: Generate a series or range of passwords from an MMGen from mmgen.common import * from mmgen.crypto import * from mmgen.addr import PasswordList,AddrIdxList -from mmgen.seed import Wallet +from .wallet import Wallet from mmgen.obj import MMGenPWIDString pwi = PasswordList.pw_info diff --git a/mmgen/main_seedjoin.py b/mmgen/main_seedjoin.py index 3c7c4596..352e68a9 100755 --- a/mmgen/main_seedjoin.py +++ b/mmgen/main_seedjoin.py @@ -23,7 +23,8 @@ mmgen/main_seedjoin: Regenerate an MMGen deterministic wallet from seed shares from mmgen.common import * from mmgen.obj import MasterShareIdx,SeedSplitIDString,MMGenWalletLabel -from mmgen.seed import Seed,Wallet,SeedShareMasterJoining +from .seed import Seed,SeedShareMasterJoining +from .wallet import Wallet opts_data = { 'text': { diff --git a/mmgen/main_txbump.py b/mmgen/main_txbump.py index ecf28940..10baf013 100755 --- a/mmgen/main_txbump.py +++ b/mmgen/main_txbump.py @@ -22,7 +22,7 @@ mmgen-txbump: Increase the fee on a replaceable (replace-by-fee) MMGen """ from mmgen.common import * -from mmgen.seed import Wallet +from .wallet import Wallet opts_data = { 'sets': [('yes', True, 'quiet', True)], diff --git a/mmgen/main_txdo.py b/mmgen/main_txdo.py index e03c2fb8..52688632 100755 --- a/mmgen/main_txdo.py +++ b/mmgen/main_txdo.py @@ -21,7 +21,7 @@ mmgen-txdo: Create, sign and broadcast an online MMGen transaction """ from mmgen.common import * -from mmgen.seed import Wallet +from .wallet import Wallet from mmgen.obj import SubSeedIdxRange opts_data = { diff --git a/mmgen/main_txsign.py b/mmgen/main_txsign.py index b74f5e9e..4825b633 100755 --- a/mmgen/main_txsign.py +++ b/mmgen/main_txsign.py @@ -22,7 +22,7 @@ mmgen-txsign: Sign a transaction generated by 'mmgen-txcreate' from mmgen.common import * from mmgen.obj import SubSeedIdxRange -from mmgen.seed import Wallet +from .wallet import Wallet # -w, --use-wallet-dat (keys from running coin daemon) removed: use walletdump rpc instead opts_data = { diff --git a/mmgen/main_wallet.py b/mmgen/main_wallet.py index ad2f14b3..17def138 100755 --- a/mmgen/main_wallet.py +++ b/mmgen/main_wallet.py @@ -22,7 +22,7 @@ mmgen/main_wallet: Entry point for MMGen wallet-related scripts import os from mmgen.common import * -from mmgen.seed import Wallet,MMGenWallet +from .wallet import Wallet,MMGenWallet from mmgen.filename import find_file_in_dir from mmgen.obj import MMGenWalletLabel,MasterShareIdx diff --git a/mmgen/opts.py b/mmgen/opts.py index 0fb7d6b6..0bcbc908 100755 --- a/mmgen/opts.py +++ b/mmgen/opts.py @@ -392,7 +392,7 @@ def check_usr_opts(usr_opts): # Raises an exception if any check fails msg_r('{} option {!r}{}'.format(beg,s,end)) def chk_in_fmt(key,val,desc): - from mmgen.seed import Wallet,IncogWallet,Brainwallet,IncogWalletHidden + from .wallet import Wallet,IncogWallet,Brainwallet,IncogWalletHidden sstype = Wallet.fmt_code_to_type(val) if not sstype: opt_unrecognized(key,val) @@ -434,7 +434,7 @@ def check_usr_opts(usr_opts): # Raises an exception if any check fails if hasattr(opt,key2): val2 = getattr(opt,key2) - from mmgen.seed import IncogWalletHidden + from .wallet import IncogWalletHidden if val2 and val2 not in IncogWalletHidden.fmt_codes: fs = 'Option conflict:\n {}, with\n {}={}' raise UserOptError(fs.format(fmt_opt(key),fmt_opt(key2),val2)) diff --git a/mmgen/seed.py b/mmgen/seed.py index 1dcdccb3..37e413d6 100755 --- a/mmgen/seed.py +++ b/mmgen/seed.py @@ -20,34 +20,9 @@ seed.py: Seed-related classes and methods for the MMGen suite """ -import os - from mmgen.common import * from mmgen.obj import * -from mmgen.crypto import * -from mmgen.baseconv import * - -pnm = g.proj_name - -def check_usr_seed_len(seed_len): - if opt.seed_len != seed_len and 'seed_len' in opt.set_by_user: - m = "ERROR: requested seed length ({}) doesn't match seed length of source ({})" - die(1,m.format((opt.seed_len,seed_len))) - -def _is_mnemonic(s,fmt): - oq_save = opt.quiet - opt.quiet = True - try: - Wallet(in_data=s,in_fmt=fmt) - ret = True - except: - ret = False - finally: - opt.quiet = oq_save - return ret - -def is_bip39_mnemonic(s): return _is_mnemonic(s,fmt='bip39') -def is_mmgen_mnemonic(s): return _is_mnemonic(s,fmt='words') +from .crypto import get_random,scramble_seed class SeedBase(MMGenObject): @@ -498,1121 +473,3 @@ class SeedShareMasterJoining(SeedShareMaster): self.id_str = id_str or 'default' self.count = count self.derived_seed = SeedBase(self.make_derived_seed_bin(self.id_str,self.count)) - -class WalletMeta(type): - wallet_classes = set() # one-instance class, so store data in class attr - def __init__(cls,name,bases,namespace): - cls.wallet_classes.add(cls) - cls.wallet_classes -= set(bases) - -class Wallet(MMGenObject,metaclass=WalletMeta): - - desc = g.proj_name + ' seed source' - file_mode = 'text' - stdin_ok = False - ask_tty = True - no_tty = False - op = None - _msg = {} - - class WalletData(MMGenObject): pass - - def __new__(cls,fn=None,ss=None,seed_bin=None,seed=None, - passchg=False,in_data=None,ignore_in_fmt=False,in_fmt=None): - - in_fmt = in_fmt or opt.in_fmt - - if hasattr(opt,'out_fmt') and opt.out_fmt: - out_cls = cls.fmt_code_to_type(opt.out_fmt) - if not out_cls: - die(1,'{!r}: unrecognized output format'.format(opt.out_fmt)) - else: - out_cls = None - - def die_on_opt_mismatch(opt,sstype): - compare_or_die( - cls.fmt_code_to_type(opt).__name__, 'input format requested on command line', - sstype.__name__, 'input file format' ) - - if seed or seed_bin: - me = super(cls,cls).__new__(out_cls or MMGenWallet) # default to MMGenWallet - me.seed = seed or Seed(seed_bin=seed_bin) - me.op = 'new' - elif ss: - me = super(cls,cls).__new__((ss.__class__ if passchg else out_cls) or MMGenWallet) - me.seed = ss.seed - me.ss_in = ss - me.op = ('conv','pwchg_new')[bool(passchg)] - elif fn or opt.hidden_incog_input_params: - from mmgen.filename import Filename - if fn: - f = Filename(fn) - else: - # permit comma in filename - fn = ','.join(opt.hidden_incog_input_params.split(',')[:-1]) - f = Filename(fn,ftype=IncogWalletHidden) - if in_fmt and not ignore_in_fmt: - die_on_opt_mismatch(in_fmt,f.ftype) - me = super(cls,cls).__new__(f.ftype) - me.infile = f - me.op = ('old','pwchg_old')[bool(passchg)] - elif in_fmt: - me = super(cls,cls).__new__(cls.fmt_code_to_type(in_fmt)) - me.op = ('old','pwchg_old')[bool(passchg)] - else: # called with no arguments: initialize with random seed - me = super(cls,cls).__new__(out_cls or MMGenWallet) - me.seed = Seed(None) - me.op = 'new' - - return me - - def __init__(self,fn=None,ss=None,seed_bin=None,seed=None, - passchg=False,in_data=None,ignore_in_fmt=False,in_fmt=None): - - self.ssdata = self.WalletData() - self.msg = {} - self.in_data = in_data - - for c in reversed(self.__class__.__mro__): - if hasattr(c,'_msg'): - self.msg.update(c._msg) - - if hasattr(self,'seed'): - self._encrypt() - return - elif hasattr(self,'infile') or self.in_data or not g.stdin_tty: - self._deformat_once() - self._decrypt_retry() - else: - if not self.stdin_ok: - die(1,'Reading from standard input not supported for {} format'.format(self.desc)) - self._deformat_retry() - self._decrypt_retry() - - m = ('',', seed length {}'.format(self.seed.bitlen))[self.seed.bitlen!=256] - qmsg('Valid {} for Seed ID {}{}'.format(self.desc,self.seed.sid.hl(),m)) - - def _get_data(self): - if hasattr(self,'infile'): - self.fmt_data = get_data_from_file(self.infile.name,self.desc,binary=self.file_mode=='binary') - elif self.in_data: - self.fmt_data = self.in_data - else: - self.fmt_data = self._get_data_from_user(self.desc) - - def _get_data_from_user(self,desc): - return get_data_from_user(desc) - - def _deformat_once(self): - self._get_data() - if not self._deformat(): - die(2,'Invalid format for input data') - - def _deformat_retry(self): - while True: - self._get_data() - if self._deformat(): - break - msg('Trying again...') - - def _decrypt_retry(self): - while True: - if self._decrypt(): - break - if opt.passwd_file: - die(2,'Passphrase from password file, so exiting') - msg('Trying again...') - - @classmethod - def get_extensions(cls): - return [c.ext for c in cls.wallet_classes if hasattr(c,'ext')] - - @classmethod - def fmt_code_to_type(cls,fmt_code): - if fmt_code: - for c in cls.wallet_classes: - if fmt_code in getattr(c,'fmt_codes',[]): - return c - return None - - @classmethod - def ext_to_type(cls,ext): - if ext: - for c in cls.wallet_classes: - if ext == getattr(c,'ext',None): - return c - return None - - @classmethod - def format_fmt_codes(cls): - d = [(c.__name__,('.'+c.ext if c.ext else str(c.ext)),','.join(c.fmt_codes)) - for c in cls.wallet_classes - if hasattr(c,'fmt_codes')] - w = max(len(i[0]) for i in d) - ret = ['{:<{w}} {:<9} {}'.format(a,b,c,w=w) for a,b,c in [ - ('Format','FileExt','Valid codes'), - ('------','-------','-----------') - ] + sorted(d)] - return '\n'.join(ret) + ('','-α')[g.debug_utf8] + '\n' - - def get_fmt_data(self): - self._format() - return self.fmt_data - - def write_to_file(self,outdir='',desc=''): - self._format() - kwargs = { - 'desc': desc or self.desc, - 'ask_tty': self.ask_tty, - 'no_tty': self.no_tty, - 'binary': self.file_mode == 'binary' - } - # write_data_to_file(): outfile with absolute path overrides opt.outdir - if outdir: - of = os.path.abspath(os.path.join(outdir,self._filename())) - write_data_to_file(of if outdir else self._filename(),self.fmt_data,**kwargs) - -class WalletUnenc(Wallet): - - def _decrypt_retry(self): pass - def _encrypt(self): pass - - def _filename(self): - s = self.seed - return '{}[{}]{x}.{}'.format( - s.fn_stem, - s.bitlen, - self.ext, - x='-α' if g.debug_utf8 else '') - - def _choose_seedlen(self,desc,ok_lens,subtype): - - from mmgen.term import get_char - def choose_len(): - prompt = self.choose_seedlen_prompt - while True: - r = get_char('\r'+prompt) - if is_int(r) and 1 <= int(r) <= len(ok_lens): - break - msg_r(('\r','\n')[g.test_suite] + ' '*len(prompt) + '\r') - return ok_lens[int(r)-1] - - m1 = blue('{} type:'.format(capfirst(desc))) - m2 = yellow(subtype) - msg('{} {}'.format(m1,m2)) - - while True: - usr_len = choose_len() - prompt = self.choose_seedlen_confirm.format(usr_len) - if keypress_confirm(prompt,default_yes=True,no_nl=not g.test_suite): - return usr_len - -class WalletEnc(Wallet): - - _msg = { - 'choose_passphrase': """ -You must choose a passphrase to encrypt your new {} with. -A key will be generated from your passphrase using a hash preset of '{}'. -Please note that no strength checking of passphrases is performed. For -an empty passphrase, just hit ENTER twice. - """.strip() - } - - def _get_hash_preset_from_user(self,hp,desc_suf=''): - n = ('','old ')[self.op=='pwchg_old'] - m,n = (('to accept the default',n),('to reuse the old','new '))[self.op=='pwchg_new'] - fs = "Enter {}hash preset for {}{}{},\n or hit ENTER {} value ('{}'): " - p = fs.format( - n, - ('','new ')[self.op=='new'], - self.desc, - ('',' '+desc_suf)[bool(desc_suf)], - m, - hp - ) - while True: - ret = my_raw_input(p) - if ret: - if ret in g.hash_presets: - self.ssdata.hash_preset = ret - return ret - else: - msg('Invalid input. Valid choices are {}'.format(', '.join(g.hash_presets))) - else: - self.ssdata.hash_preset = hp - return hp - - def _get_hash_preset(self,desc_suf=''): - if hasattr(self,'ss_in') and hasattr(self.ss_in.ssdata,'hash_preset'): - old_hp = self.ss_in.ssdata.hash_preset - if opt.keep_hash_preset: - qmsg("Reusing hash preset '{}' at user request".format(old_hp)) - self.ssdata.hash_preset = old_hp - elif 'hash_preset' in opt.set_by_user: - hp = self.ssdata.hash_preset = opt.hash_preset - qmsg("Using hash preset '{}' requested on command line".format(opt.hash_preset)) - else: # Prompt, using old value as default - hp = self._get_hash_preset_from_user(old_hp,desc_suf) - - if (not opt.keep_hash_preset) and self.op == 'pwchg_new': - m = ("changed to '{}'".format(hp),'unchanged')[hp==old_hp] - qmsg('Hash preset {}'.format(m)) - elif 'hash_preset' in opt.set_by_user: - self.ssdata.hash_preset = opt.hash_preset - qmsg("Using hash preset '{}' requested on command line".format(opt.hash_preset)) - else: - self._get_hash_preset_from_user(opt.hash_preset,desc_suf) - - def _get_new_passphrase(self): - desc = '{}passphrase for {}{}'.format( - ('','new ')[self.op=='pwchg_new'], - ('','new ')[self.op in ('new','conv')], - self.desc - ) - if opt.passwd_file: - w = pwfile_reuse_warning() - pw = ' '.join(get_words_from_file(opt.passwd_file,desc,quiet=w)) - elif opt.echo_passphrase: - pw = ' '.join(get_words_from_user('Enter {}: '.format(desc))) - else: - for i in range(g.passwd_max_tries): - pw = ' '.join(get_words_from_user('Enter {}: '.format(desc))) - pw2 = ' '.join(get_words_from_user('Repeat passphrase: ')) - dmsg('Passphrases: [{}] [{}]'.format(pw,pw2)) - if pw == pw2: - vmsg('Passphrases match'); break - else: msg('Passphrases do not match. Try again.') - else: - die(2,'User failed to duplicate passphrase in {} attempts'.format(g.passwd_max_tries)) - - if pw == '': - qmsg('WARNING: Empty passphrase') - self.ssdata.passwd = pw - return pw - - def _get_passphrase(self,desc_suf=''): - desc = '{}passphrase for {}{}'.format( - ('','old ')[self.op=='pwchg_old'], - self.desc, - ('',' '+desc_suf)[bool(desc_suf)] - ) - if opt.passwd_file: - w = pwfile_reuse_warning() - ret = ' '.join(get_words_from_file(opt.passwd_file,desc,quiet=w)) - else: - ret = ' '.join(get_words_from_user('Enter {}: '.format(desc))) - self.ssdata.passwd = ret - - def _get_first_pw_and_hp_and_encrypt_seed(self): - d = self.ssdata - self._get_hash_preset() - - if hasattr(self,'ss_in') and hasattr(self.ss_in.ssdata,'passwd'): - old_pw = self.ss_in.ssdata.passwd - if opt.keep_passphrase: - d.passwd = old_pw - qmsg('Reusing passphrase at user request') - else: - pw = self._get_new_passphrase() - if self.op == 'pwchg_new': - m = ('changed','unchanged')[pw==old_pw] - qmsg('Passphrase {}'.format(m)) - else: - qmsg(self.msg['choose_passphrase'].format(self.desc,d.hash_preset)) - self._get_new_passphrase() - - d.salt = sha256(get_random(128)).digest()[:g.salt_len] - key = make_key(d.passwd, d.salt, d.hash_preset) - d.key_id = make_chksum_8(key) - d.enc_seed = encrypt_seed(self.seed.data,key) - -class Mnemonic(WalletUnenc): - - stdin_ok = True - wclass = 'mnemonic' - conv_cls = baseconv - choose_seedlen_prompt = 'Choose a mnemonic length: 1) 12 words, 2) 18 words, 3) 24 words: ' - choose_seedlen_confirm = 'Mnemonic length of {} words chosen. OK?' - - @property - def mn_lens(self): - return sorted(self.conv_cls.seedlen_map_rev[self.wl_id]) - - def _get_data_from_user(self,desc): - - if not g.stdin_tty: - return get_data_from_user(desc) - - from mmgen.mn_entry import mn_entry # import here to catch cfg var errors - mn_len = self._choose_seedlen(self.wclass,self.mn_lens,self.mn_type) - return mn_entry(self.wl_id).get_mnemonic_from_user(mn_len) - - @staticmethod - def _mn2hex_pad(mn): return len(mn) * 8 // 3 - - @staticmethod - def _hex2mn_pad(hexnum): return len(hexnum) * 3 // 8 - - def _format(self): - - hexseed = self.seed.hexdata - - mn = self.conv_cls.fromhex(hexseed,self.wl_id,self._hex2mn_pad(hexseed)) - ret = self.conv_cls.tohex(mn,self.wl_id,self._mn2hex_pad(mn)) - - # Internal error, so just die on fail - compare_or_die(ret,'recomputed seed',hexseed,'original',e='Internal error') - - self.ssdata.mnemonic = mn - self.fmt_data = ' '.join(mn) + '\n' - - def _deformat(self): - - self.conv_cls.init_mn(self.wl_id) - mn = self.fmt_data.split() - - if len(mn) not in self.mn_lens: - m = 'Invalid mnemonic ({} words). Valid numbers of words: {}' - msg(m.format(len(mn),', '.join(map(str,self.mn_lens)))) - return False - - for n,w in enumerate(mn,1): - if w not in self.conv_cls.digits[self.wl_id]: - msg('Invalid mnemonic: word #{} is not in the {} wordlist'.format(n,self.wl_id.upper())) - return False - - hexseed = self.conv_cls.tohex(mn,self.wl_id,self._mn2hex_pad(mn)) - ret = self.conv_cls.fromhex(hexseed,self.wl_id,self._hex2mn_pad(hexseed)) - - if len(hexseed) * 4 not in g.seed_lens: - msg('Invalid mnemonic (produces too large a number)') - return False - - # Internal error, so just die - compare_or_die(' '.join(ret),'recomputed mnemonic',' '.join(mn),'original',e='Internal error') - - self.seed = Seed(bytes.fromhex(hexseed)) - self.ssdata.mnemonic = mn - - check_usr_seed_len(self.seed.bitlen) - - return True - -class MMGenMnemonic(Mnemonic): - - fmt_codes = ('mmwords','words','mnemonic','mnem','mn','m') - desc = 'MMGen native mnemonic data' - mn_type = 'MMGen native' - ext = 'mmwords' - wl_id = 'mmgen' - -class BIP39Mnemonic(Mnemonic): - - fmt_codes = ('bip39',) - desc = 'BIP39 mnemonic data' - mn_type = 'BIP39' - ext = 'bip39' - wl_id = 'bip39' - - def __init__(self,*args,**kwargs): - from mmgen.bip39 import bip39 - self.conv_cls = bip39 - super().__init__(*args,**kwargs) - -class MMGenSeedFile(WalletUnenc): - - stdin_ok = True - fmt_codes = ('mmseed','seed','s') - desc = 'seed data' - ext = 'mmseed' - - def _format(self): - b58seed = baseconv.frombytes(self.seed.data,'b58',pad='seed',tostr=True) - self.ssdata.chksum = make_chksum_6(b58seed) - self.ssdata.b58seed = b58seed - self.fmt_data = '{} {}\n'.format(self.ssdata.chksum,split_into_cols(4,b58seed)) - - def _deformat(self): - desc = self.desc - ld = self.fmt_data.split() - - if not (7 <= len(ld) <= 12): # 6 <= padded b58 data (ld[1:]) <= 11 - msg('Invalid data length ({}) in {}'.format(len(ld),desc)) - return False - - a,b = ld[0],''.join(ld[1:]) - - if not is_chksum_6(a): - msg("'{}': invalid checksum format in {}".format(a, desc)) - return False - - if not is_b58_str(b): - msg("'{}': not a base 58 string, in {}".format(b, desc)) - return False - - vmsg_r('Validating {} checksum...'.format(desc)) - - if not compare_chksums(a,'file',make_chksum_6(b),'computed',verbose=True): - return False - - ret = baseconv.tobytes(b,'b58',pad='seed') - - if ret == False: - msg('Invalid base-58 encoded seed: {}'.format(val)) - return False - - self.seed = Seed(ret) - self.ssdata.chksum = a - self.ssdata.b58seed = b - - check_usr_seed_len(self.seed.bitlen) - - return True - -class DieRollSeedFile(WalletUnenc): - - stdin_ok = True - fmt_codes = ('b6d','die','dieroll') - desc = 'base6d die roll seed data' - ext = 'b6d' - conv_cls = baseconv - wclass = 'dieroll' - wl_id = 'b6d' - mn_type = 'base6d' - choose_seedlen_prompt = 'Choose a seed length: 1) 128 bits, 2) 192 bits, 3) 256 bits: ' - choose_seedlen_confirm = 'Seed length of {} bits chosen. OK?' - user_entropy_prompt = 'Would you like to provide some additional entropy from the keyboard?' - interactive_input = False - - def _format(self): - d = baseconv.frombytes(self.seed.data,'b6d',pad='seed',tostr=True) + '\n' - self.fmt_data = block_format(d,gw=5,cols=5) - - def _deformat(self): - - d = remove_whitespace(self.fmt_data) - - rmap = self.conv_cls.seedlen_map_rev['b6d'] - if not len(d) in rmap: - m = '{!r}: invalid length for {} (must be one of {})' - raise SeedLengthError(m.format(len(d),self.desc,list(rmap))) - - # truncate seed to correct length, discarding high bits - seed_len = rmap[len(d)] - seed_bytes = baseconv.tobytes(d,'b6d',pad='seed')[-seed_len:] - - if self.interactive_input and opt.usr_randchars: - if keypress_confirm(self.user_entropy_prompt): - seed_bytes = add_user_random(seed_bytes,'die roll data') - self.desc += ' plus user-supplied entropy' - - self.seed = Seed(seed_bytes) - self.ssdata.hexseed = seed_bytes.hex() - - check_usr_seed_len(self.seed.bitlen) - return True - - def _get_data_from_user(self,desc): - - if not g.stdin_tty: - return get_data_from_user(desc) - - seed_bitlens = [n*8 for n in sorted(self.conv_cls.seedlen_map['b6d'])] - seed_bitlen = self._choose_seedlen(self.wclass,seed_bitlens,self.mn_type) - nDierolls = self.conv_cls.seedlen_map['b6d'][seed_bitlen // 8] - - m = """ - For a {sb}-bit seed you must roll the die {nd} times. After each die roll, - enter the result on the keyboard as a digit. If you make an invalid entry, - you'll be prompted to re-enter it. - """ - msg('\n'+fmt(m.strip()).format(sb=seed_bitlen,nd=nDierolls)+'\n') - - b6d_digits = self.conv_cls.digits['b6d'] - - cr = '\n' if g.test_suite else '\r' - prompt_fs = '\b\b\b {}Enter die roll #{{}}: {}'.format(cr,CUR_SHOW) - clear_line = '' if g.test_suite else '\r' + ' ' * 25 - invalid_msg = CUR_HIDE + cr + 'Invalid entry' + ' ' * 11 - - from mmgen.term import get_char - def get_digit(n): - p = prompt_fs - sleep = g.short_disp_timeout - while True: - ch = get_char(p.format(n),num_chars=1,sleep=sleep) - if ch in b6d_digits: - msg_r(CUR_HIDE + ' OK') - return ch - else: - msg_r(invalid_msg) - sleep = g.err_disp_timeout - p = clear_line + prompt_fs - - dierolls,n = [],1 - while len(dierolls) < nDierolls: - dierolls.append(get_digit(n)) - n += 1 - - msg('Die rolls successfully entered' + CUR_SHOW) - self.interactive_input = True - - return ''.join(dierolls) - -class PlainHexSeedFile(WalletUnenc): - - stdin_ok = True - fmt_codes = ('hex','rawhex','plainhex') - desc = 'plain hexadecimal seed data' - ext = 'hex' - - def _format(self): - self.fmt_data = self.seed.hexdata + '\n' - - def _deformat(self): - desc = self.desc - d = self.fmt_data.strip() - - if not is_hex_str_lc(d): - msg("'{}': not a lowercase hexadecimal string, in {}".format(d,desc)) - return False - - if not len(d)*4 in g.seed_lens: - msg('Invalid data length ({}) in {}'.format(len(d),desc)) - return False - - self.seed = Seed(bytes.fromhex(d)) - self.ssdata.hexseed = d - - check_usr_seed_len(self.seed.bitlen) - - return True - -class MMGenHexSeedFile(WalletUnenc): - - stdin_ok = True - fmt_codes = ('seedhex','hexseed','mmhex') - desc = 'hexadecimal seed data with checksum' - ext = 'mmhex' - - def _format(self): - h = self.seed.hexdata - self.ssdata.chksum = make_chksum_6(h) - self.ssdata.hexseed = h - self.fmt_data = '{} {}\n'.format(self.ssdata.chksum, split_into_cols(4,h)) - - def _deformat(self): - desc = self.desc - d = self.fmt_data.split() - try: - d[1] - chk,hstr = d[0],''.join(d[1:]) - except: - msg("'{}': invalid {}".format(self.fmt_data.strip(),desc)) - return False - - if not len(hstr)*4 in g.seed_lens: - msg('Invalid data length ({}) in {}'.format(len(hstr),desc)) - return False - - if not is_chksum_6(chk): - msg("'{}': invalid checksum format in {}".format(chk, desc)) - return False - - if not is_hex_str(hstr): - msg("'{}': not a hexadecimal string, in {}".format(hstr, desc)) - return False - - vmsg_r('Validating {} checksum...'.format(desc)) - - if not compare_chksums(chk,'file',make_chksum_6(hstr),'computed',verbose=True): - return False - - self.seed = Seed(bytes.fromhex(hstr)) - self.ssdata.chksum = chk - self.ssdata.hexseed = hstr - - check_usr_seed_len(self.seed.bitlen) - - return True - -class MMGenWallet(WalletEnc): - - fmt_codes = ('wallet','w') - desc = g.proj_name + ' wallet' - ext = 'mmdat' - - def _get_label_from_user(self,old_lbl=''): - d = "to reuse the label '{}'".format(old_lbl.hl()) if old_lbl else 'for no label' - p = 'Enter a wallet label, or hit ENTER {}: '.format(d) - while True: - msg_r(p) - ret = my_raw_input('') - if ret: - self.ssdata.label = MMGenWalletLabel(ret,on_fail='return') - if self.ssdata.label: - break - else: - msg('Invalid label. Trying again...') - else: - self.ssdata.label = old_lbl or MMGenWalletLabel('No Label') - break - return self.ssdata.label - - # nearly identical to _get_hash_preset() - factor? - def _get_label(self): - if hasattr(self,'ss_in') and hasattr(self.ss_in.ssdata,'label'): - old_lbl = self.ss_in.ssdata.label - if opt.keep_label: - qmsg("Reusing label '{}' at user request".format(old_lbl.hl())) - self.ssdata.label = old_lbl - elif opt.label: - qmsg("Using label '{}' requested on command line".format(opt.label.hl())) - lbl = self.ssdata.label = opt.label - else: # Prompt, using old value as default - lbl = self._get_label_from_user(old_lbl) - - if (not opt.keep_label) and self.op == 'pwchg_new': - m = ("changed to '{}'".format(lbl),'unchanged')[lbl==old_lbl] - qmsg('Label {}'.format(m)) - elif opt.label: - qmsg("Using label '{}' requested on command line".format(opt.label.hl())) - self.ssdata.label = opt.label - else: - self._get_label_from_user() - - def _encrypt(self): - self._get_first_pw_and_hp_and_encrypt_seed() - self._get_label() - d = self.ssdata - d.pw_status = ('NE','E')[len(d.passwd)==0] - d.timestamp = make_timestamp() - - def _format(self): - d = self.ssdata - s = self.seed - slt_fmt = baseconv.frombytes(d.salt,'b58',pad='seed',tostr=True) - es_fmt = baseconv.frombytes(d.enc_seed,'b58',pad='seed',tostr=True) - lines = ( - d.label, - '{} {} {} {} {}'.format(s.sid.lower(), d.key_id.lower(), - s.bitlen, d.pw_status, d.timestamp), - '{}: {} {} {}'.format(d.hash_preset,*get_hash_params(d.hash_preset)), - '{} {}'.format(make_chksum_6(slt_fmt),split_into_cols(4,slt_fmt)), - '{} {}'.format(make_chksum_6(es_fmt), split_into_cols(4,es_fmt)) - ) - chksum = make_chksum_6(' '.join(lines).encode()) - self.fmt_data = '\n'.join((chksum,)+lines) + '\n' - - def _deformat(self): - - def check_master_chksum(lines,desc): - - if len(lines) != 6: - msg('Invalid number of lines ({}) in {} data'.format(len(lines),desc)) - return False - - if not is_chksum_6(lines[0]): - msg('Incorrect master checksum ({}) in {} data'.format(lines[0],desc)) - return False - - chk = make_chksum_6(' '.join(lines[1:])) - if not compare_chksums(lines[0],'master',chk,'computed', - hdr='For wallet master checksum',verbose=True): - return False - - return True - - lines = self.fmt_data.splitlines() - if not check_master_chksum(lines,self.desc): - return False - - d = self.ssdata - d.label = MMGenWalletLabel(lines[1]) - - d1,d2,d3,d4,d5 = lines[2].split() - d.seed_id = d1.upper() - d.key_id = d2.upper() - check_usr_seed_len(int(d3)) - d.pw_status,d.timestamp = d4,d5 - - hpdata = lines[3].split() - - d.hash_preset = hp = hpdata[0][:-1] # a string! - qmsg("Hash preset of wallet: '{}'".format(hp)) - if 'hash_preset' in opt.set_by_user: - uhp = opt.hash_preset - if uhp != hp: - qmsg("Warning: ignoring user-requested hash preset '{}'".format(uhp)) - - hash_params = list(map(int,hpdata[1:])) - - if hash_params != get_hash_params(d.hash_preset): - msg("Hash parameters '{}' don't match hash preset '{}'".format(' '.join(hash_params),d.hash_preset)) - return False - - lmin,foo,lmax = sorted(baseconv.seedlen_map_rev['b58']) # 22,33,44 - for i,key in (4,'salt'),(5,'enc_seed'): - l = lines[i].split(' ') - chk = l.pop(0) - b58_val = ''.join(l) - - if len(b58_val) < lmin or len(b58_val) > lmax: - msg('Invalid format for {} in {}: {}'.format(key,self.desc,l)) - return False - - if not compare_chksums(chk,key, - make_chksum_6(b58_val),'computed checksum',verbose=True): - return False - - val = baseconv.tobytes(b58_val,'b58',pad='seed') - if val == False: - msg('Invalid base 58 number: {}'.format(b58_val)) - return False - - setattr(d,key,val) - - return True - - def _decrypt(self): - d = self.ssdata - # Needed for multiple transactions with {}-txsign - suf = ('',os.path.basename(self.infile.name))[bool(opt.quiet)] - self._get_passphrase(desc_suf=suf) - key = make_key(d.passwd, d.salt, d.hash_preset) - ret = decrypt_seed(d.enc_seed, key, d.seed_id, d.key_id) - if ret: - self.seed = Seed(ret) - return True - else: - return False - - def _filename(self): - s = self.seed - d = self.ssdata - return '{}-{}[{},{}]{x}.{}'.format( - s.fn_stem, - d.key_id, - s.bitlen, - d.hash_preset, - self.ext, - x='-α' if g.debug_utf8 else '') - -class Brainwallet(WalletEnc): - - stdin_ok = True - fmt_codes = ('mmbrain','brainwallet','brain','bw','b') - desc = 'brainwallet' - ext = 'mmbrain' - # brainwallet warning message? TODO - - def get_bw_params(self): - # already checked - a = opt.brain_params.split(',') - return int(a[0]),a[1] - - def _deformat(self): - self.brainpasswd = ' '.join(self.fmt_data.split()) - return True - - def _decrypt(self): - d = self.ssdata - # Don't set opt.seed_len! In txsign, BW seed len might differ from other seed srcs - if opt.brain_params: - seed_len,d.hash_preset = self.get_bw_params() - else: - if 'seed_len' not in opt.set_by_user: - m1 = 'Using default seed length of {} bits\n' - m2 = 'If this is not what you want, use the --seed-len option' - qmsg((m1+m2).format(yellow(str(opt.seed_len)))) - self._get_hash_preset() - seed_len = opt.seed_len - qmsg_r('Hashing brainwallet data. Please wait...') - # Use buflen arg of scrypt.hash() to get seed of desired length - seed = scrypt_hash_passphrase(self.brainpasswd.encode(),b'',d.hash_preset,buflen=seed_len//8) - qmsg('Done') - self.seed = Seed(seed) - msg('Seed ID: {}'.format(self.seed.sid)) - qmsg('Check this value against your records') - return True - - def _format(self): - raise NotImplementedError('Brainwallet not supported as an output format') - - def _encrypt(self): - raise NotImplementedError('Brainwallet not supported as an output format') - -class IncogWalletBase(WalletEnc): - - _msg = { - 'check_incog_id': """ - Check the generated Incog ID above against your records. If it doesn't - match, then your incognito data is incorrect or corrupted. - """, - 'record_incog_id': """ - Make a record of the Incog ID but keep it secret. You will use it to - identify your incog wallet data in the future. - """, - 'incorrect_incog_passphrase_try_again': """ -Incorrect passphrase, hash preset, or maybe old-format incog wallet. -Try again? (Y)es, (n)o, (m)ore information: -""".strip(), - 'confirm_seed_id': """ -If the Seed ID above is correct but you're seeing this message, then you need -to exit and re-run the program with the '--old-incog-fmt' option. -""".strip(), - 'dec_chk': " {} hash preset" - } - - def _make_iv_chksum(self,s): return sha256(s).hexdigest()[:8].upper() - - def _get_incog_data_len(self,seed_len): - e = (g.hincog_chk_len,0)[bool(opt.old_incog_fmt)] - return g.aesctr_iv_len + g.salt_len + e + seed_len//8 - - def _incog_data_size_chk(self): - # valid sizes: 56, 64, 72 - dlen = len(self.fmt_data) - valid_dlen = self._get_incog_data_len(opt.seed_len) - if dlen == valid_dlen: - return True - else: - if opt.old_incog_fmt: - msg('WARNING: old-style incognito format requested. Are you sure this is correct?') - m = 'Invalid incognito data size ({} bytes) for this seed length ({} bits)' - msg(m.format(dlen,opt.seed_len)) - msg('Valid data size for this seed length: {} bytes'.format(valid_dlen)) - for sl in g.seed_lens: - if dlen == self._get_incog_data_len(sl): - die(1,'Valid seed length for this data size: {} bits'.format(sl)) - msg('This data size ({} bytes) is invalid for all available seed lengths'.format(dlen)) - return False - - def _encrypt (self): - self._get_first_pw_and_hp_and_encrypt_seed() - if opt.old_incog_fmt: - die(1,'Writing old-format incog wallets is unsupported') - d = self.ssdata - # IV is used BOTH to initialize counter and to salt password! - d.iv = get_random(g.aesctr_iv_len) - d.iv_id = self._make_iv_chksum(d.iv) - msg('New Incog Wallet ID: {}'.format(d.iv_id)) - qmsg('Make a record of this value') - vmsg(self.msg['record_incog_id']) - - d.salt = get_random(g.salt_len) - key = make_key(d.passwd, d.salt, d.hash_preset, 'incog wallet key') - chk = sha256(self.seed.data).digest()[:8] - d.enc_seed = encrypt_data(chk+self.seed.data, key, g.aesctr_dfl_iv, 'seed') - - d.wrapper_key = make_key(d.passwd, d.iv, d.hash_preset, 'incog wrapper key') - d.key_id = make_chksum_8(d.wrapper_key) - vmsg('Key ID: {}'.format(d.key_id)) - d.target_data_len = self._get_incog_data_len(self.seed.bitlen) - - def _format(self): - d = self.ssdata - self.fmt_data = d.iv + encrypt_data(d.salt+d.enc_seed, d.wrapper_key, d.iv, self.desc) - - def _filename(self): - s = self.seed - d = self.ssdata - return '{}-{}-{}[{},{}]{x}.{}'.format( - s.fn_stem, - d.key_id, - d.iv_id, - s.bitlen, - d.hash_preset, - self.ext, - x='-α' if g.debug_utf8 else '') - - def _deformat(self): - - if not self._incog_data_size_chk(): - return False - - d = self.ssdata - d.iv = self.fmt_data[0:g.aesctr_iv_len] - d.incog_id = self._make_iv_chksum(d.iv) - d.enc_incog_data = self.fmt_data[g.aesctr_iv_len:] - msg('Incog Wallet ID: {}'.format(d.incog_id)) - qmsg('Check this value against your records') - vmsg(self.msg['check_incog_id']) - - return True - - def _verify_seed_newfmt(self,data): - chk,seed = data[:8],data[8:] - if sha256(seed).digest()[:8] == chk: - qmsg('Passphrase{} are correct'.format(self.msg['dec_chk'].format('and'))) - return seed - else: - msg('Incorrect passphrase{}'.format(self.msg['dec_chk'].format('or'))) - return False - - def _verify_seed_oldfmt(self,seed): - m = 'Seed ID: {}. Is the Seed ID correct?'.format(make_chksum_8(seed)) - if keypress_confirm(m, True): - return seed - else: - return False - - def _decrypt(self): - d = self.ssdata - self._get_hash_preset(desc_suf=d.incog_id) - self._get_passphrase(desc_suf=d.incog_id) - - # IV is used BOTH to initialize counter and to salt password! - key = make_key(d.passwd, d.iv, d.hash_preset, 'wrapper key') - dd = decrypt_data(d.enc_incog_data, key, d.iv, 'incog data') - - d.salt = dd[0:g.salt_len] - d.enc_seed = dd[g.salt_len:] - - key = make_key(d.passwd, d.salt, d.hash_preset, 'main key') - qmsg('Key ID: {}'.format(make_chksum_8(key))) - - verify_seed = getattr(self,'_verify_seed_'+ - ('newfmt','oldfmt')[bool(opt.old_incog_fmt)]) - - seed = verify_seed(decrypt_seed(d.enc_seed, key, '', '')) - - if seed: - self.seed = Seed(seed) - msg('Seed ID: {}'.format(self.seed.sid)) - return True - else: - return False - -class IncogWallet(IncogWalletBase): - - desc = 'incognito data' - fmt_codes = ('mmincog','incog','icg','i') - ext = 'mmincog' - file_mode = 'binary' - no_tty = True - -class IncogWalletHex(IncogWalletBase): - - desc = 'hex incognito data' - fmt_codes = ('mmincox','incox','incog_hex','xincog','ix','xi') - ext = 'mmincox' - file_mode = 'text' - no_tty = False - - def _deformat(self): - ret = decode_pretty_hexdump(self.fmt_data) - if ret: - self.fmt_data = ret - return super()._deformat() - else: - return False - - def _format(self): - super()._format() - self.fmt_data = pretty_hexdump(self.fmt_data) - -class IncogWalletHidden(IncogWalletBase): - - desc = 'hidden incognito data' - fmt_codes = ('incog_hidden','hincog','ih','hi') - ext = None - file_mode = 'binary' - no_tty = True - - _msg = { - 'choose_file_size': """ -You must choose a size for your new hidden incog data. The minimum size is -{} bytes, which puts the incog data right at the end of the file. Since you -probably want to hide your data somewhere in the middle of the file where it's -harder to find, you're advised to choose a much larger file size than this. - """.strip(), - 'check_incog_id': """ - Check generated Incog ID above against your records. If it doesn't - match, then your incognito data is incorrect or corrupted, or you - may have specified an incorrect offset. - """, - 'record_incog_id': """ - Make a record of the Incog ID but keep it secret. You will used it to - identify the incog wallet data in the future and to locate the offset - where the data is hidden in the event you forget it. - """, - 'dec_chk': ', hash preset, offset {} seed length' - } - - def _get_hincog_params(self,wtype): - a = getattr(opt,'hidden_incog_'+ wtype +'_params').split(',') - return ','.join(a[:-1]),int(a[-1]) # permit comma in filename - - def _check_valid_offset(self,fn,action): - d = self.ssdata - m = ('Input','Destination')[action=='write'] - if fn.size < d.hincog_offset + d.target_data_len: - fs = "{} file '{}' has length {}, too short to {} {} bytes of data at offset {}" - die(1,fs.format(m,fn.name,fn.size,action,d.target_data_len,d.hincog_offset)) - - def _get_data(self): - d = self.ssdata - d.hincog_offset = self._get_hincog_params('input')[1] - - qmsg("Getting hidden incog data from file '{}'".format(self.infile.name)) - - # Already sanity-checked: - d.target_data_len = self._get_incog_data_len(opt.seed_len) - self._check_valid_offset(self.infile,'read') - - flgs = os.O_RDONLY|os.O_BINARY if g.platform == 'win' else os.O_RDONLY - fh = os.open(self.infile.name,flgs) - os.lseek(fh,int(d.hincog_offset),os.SEEK_SET) - self.fmt_data = os.read(fh,d.target_data_len) - os.close(fh) - qmsg("Data read from file '{}' at offset {}".format(self.infile.name,d.hincog_offset)) - - # overrides method in Wallet - def write_to_file(self): - d = self.ssdata - self._format() - compare_or_die(d.target_data_len, 'target data length', - len(self.fmt_data),'length of formatted ' + self.desc) - - k = ('output','input')[self.op=='pwchg_new'] - fn,d.hincog_offset = self._get_hincog_params(k) - - if opt.outdir and not os.path.dirname(fn): - fn = os.path.join(opt.outdir,fn) - - check_offset = True - try: - os.stat(fn) - except: - if keypress_confirm("Requested file '{}' does not exist. Create?".format(fn),default_yes=True): - min_fsize = d.target_data_len + d.hincog_offset - msg(self.msg['choose_file_size'].format(min_fsize)) - while True: - fsize = parse_bytespec(my_raw_input('Enter file size: ')) - if fsize >= min_fsize: - break - msg('File size must be an integer no less than {}'.format(min_fsize)) - - from mmgen.tool import MMGenToolCmdFileUtil - MMGenToolCmdFileUtil().rand2file(fn,str(fsize)) - check_offset = False - else: - die(1,'Exiting at user request') - - from mmgen.filename import Filename - f = Filename(fn,ftype=type(self),write=True) - - dmsg('{} data len {}, offset {}'.format(capfirst(self.desc),d.target_data_len,d.hincog_offset)) - - if check_offset: - self._check_valid_offset(f,'write') - if not opt.quiet: - confirm_or_raise('',"alter file '{}'".format(f.name)) - - flgs = os.O_RDWR|os.O_BINARY if g.platform == 'win' else os.O_RDWR - fh = os.open(f.name,flgs) - os.lseek(fh, int(d.hincog_offset), os.SEEK_SET) - os.write(fh, self.fmt_data) - os.close(fh) - msg("{} written to file '{}' at offset {}".format(capfirst(self.desc),f.name,d.hincog_offset)) diff --git a/mmgen/tool.py b/mmgen/tool.py index eb807d79..2f425076 100755 --- a/mmgen/tool.py +++ b/mmgen/tool.py @@ -786,14 +786,14 @@ class MMGenToolCmdWallet(MMGenToolCmds): "get the Seed ID of a single subseed by Subseed Index for default or specified wallet" opt.quiet = True sf = get_seed_file([wallet] if wallet else [],1) - from mmgen.seed import Wallet + from .wallet import Wallet return Wallet(sf).seed.subseed(subseed_idx).sid def get_subseed_by_seed_id(self,seed_id:str,wallet='',last_idx=g.subseeds): "get the Subseed Index of a single subseed by Seed ID for default or specified wallet" opt.quiet = True sf = get_seed_file([wallet] if wallet else [],1) - from mmgen.seed import Wallet + from .wallet import Wallet ret = Wallet(sf).seed.subseed_by_seed_id(seed_id,last_idx) return ret.ss_idx if ret else None @@ -801,7 +801,7 @@ class MMGenToolCmdWallet(MMGenToolCmds): "list a range of subseed Seed IDs for default or specified wallet" opt.quiet = True sf = get_seed_file([wallet] if wallet else [],1) - from mmgen.seed import Wallet + from .wallet import Wallet return Wallet(sf).seed.subseeds.format(*SubSeedIdxRange(subseed_idx_range)) def list_shares(self, @@ -812,7 +812,7 @@ class MMGenToolCmdWallet(MMGenToolCmds): "list the Seed IDs of the shares resulting from a split of default or specified wallet" opt.quiet = True sf = get_seed_file([wallet] if wallet else [],1) - from mmgen.seed import Wallet + from .wallet import Wallet return Wallet(sf).seed.split(share_count,id_str,master_share).format() def gen_key(self,mmgen_addr:str,wallet=''): @@ -824,7 +824,7 @@ class MMGenToolCmdWallet(MMGenToolCmds): addr = MMGenID(mmgen_addr) opt.quiet = True sf = get_seed_file([wallet] if wallet else [],1) - from mmgen.seed import Wallet + from .wallet import Wallet ss = Wallet(sf) if ss.seed.sid != addr.sid: m = 'Seed ID of requested address ({}) does not match wallet ({})' diff --git a/mmgen/txsign.py b/mmgen/txsign.py index b3b15414..bee9f97b 100755 --- a/mmgen/txsign.py +++ b/mmgen/txsign.py @@ -21,7 +21,7 @@ txsign: Sign a transaction generated by 'mmgen-txcreate' """ from mmgen.common import * -from mmgen.seed import * +from .wallet import * from mmgen.tx import * from mmgen.addr import * diff --git a/mmgen/util.py b/mmgen/util.py index 81b27f03..51936672 100755 --- a/mmgen/util.py +++ b/mmgen/util.py @@ -470,7 +470,7 @@ def check_outfile(f,blkdev_ok=False): def check_outdir(f): return check_file_type_and_access(f,'output directory') def check_wallet_extension(fn): - from mmgen.seed import Wallet + from .wallet import Wallet if not Wallet.ext_to_type(get_extension(fn)): raise BadFileExtension("'{}': unrecognized seed source file extension".format(fn)) def make_full_path(outdir,outfile): @@ -478,7 +478,7 @@ def make_full_path(outdir,outfile): def get_seed_file(cmd_args,nargs,invoked_as=None): from mmgen.filename import find_file_in_dir - from mmgen.seed import MMGenWallet + from .wallet import MMGenWallet wf = find_file_in_dir(MMGenWallet,g.data_dir) diff --git a/setup.py b/setup.py index 28120431..94f933c6 100755 --- a/setup.py +++ b/setup.py @@ -106,6 +106,7 @@ setup( 'mmgen.color', 'mmgen.common', 'mmgen.crypto', + 'mmgen.daemon', 'mmgen.devtools', 'mmgen.ed25519', 'mmgen.ed25519ll_djbec', @@ -114,8 +115,8 @@ setup( 'mmgen.globalvars', 'mmgen.keccak', 'mmgen.license', - 'mmgen.mn_entry', 'mmgen.mn_electrum', + 'mmgen.mn_entry', 'mmgen.mn_monero', 'mmgen.mn_tirosh', 'mmgen.obj', @@ -126,12 +127,12 @@ setup( 'mmgen.seed', 'mmgen.sha2', 'mmgen.term', - 'mmgen.daemon', 'mmgen.tool', 'mmgen.tw', 'mmgen.tx', 'mmgen.txsign', 'mmgen.util', + 'mmgen.wallet', 'mmgen.altcoins.__init__', diff --git a/test/test_py_d/ts_input.py b/test/test_py_d/ts_input.py index 5546a9f4..3b2bd4d3 100755 --- a/test/test_py_d/ts_input.py +++ b/test/test_py_d/ts_input.py @@ -13,7 +13,7 @@ ts_input.py: user input tests for the MMGen test.py test suite from ..include.common import * from .ts_base import * from .input import * -from mmgen.seed import Wallet +from mmgen.wallet import Wallet class TestSuiteInput(TestSuiteBase): 'user input' diff --git a/test/test_py_d/ts_main.py b/test/test_py_d/ts_main.py index 79a46ce4..647a9789 100755 --- a/test/test_py_d/ts_main.py +++ b/test/test_py_d/ts_main.py @@ -22,7 +22,7 @@ ts_main.py: Basic operations tests for the test.py test suite from mmgen.globalvars import g from mmgen.opts import opt -from mmgen.seed import Wallet,MMGenWallet,MMGenMnemonic,IncogWallet,MMGenSeedFile +from mmgen.wallet import Wallet,MMGenWallet,MMGenMnemonic,IncogWallet,MMGenSeedFile from ..include.common import * from .common import * from .ts_base import * diff --git a/test/test_py_d/ts_ref.py b/test/test_py_d/ts_ref.py index 7e16750e..a442a3e3 100755 --- a/test/test_py_d/ts_ref.py +++ b/test/test_py_d/ts_ref.py @@ -23,7 +23,7 @@ ts_ref.py: Reference file tests for the test.py test suite import os from mmgen.globalvars import g from mmgen.opts import opt -from mmgen.seed import MMGenMnemonic +from mmgen.wallet import MMGenMnemonic from ..include.common import * from .common import * diff --git a/test/test_py_d/ts_ref_3seed.py b/test/test_py_d/ts_ref_3seed.py index d630be21..b2290ddc 100755 --- a/test/test_py_d/ts_ref_3seed.py +++ b/test/test_py_d/ts_ref_3seed.py @@ -23,7 +23,7 @@ ts_ref_3seed.py: Saved and generated reference file tests for 128, 192 and from mmgen.globalvars import g from mmgen.opts import opt -from mmgen.seed import Wallet +from mmgen.wallet import * from ..include.common import * from .common import * from .ts_base import * @@ -89,27 +89,21 @@ class TestSuiteRef3Seed(TestSuiteBase,TestSuiteShared): return self.walletchk(wf,pf=None,wcls=ss,sid=self.seed_id) def ref_seed_chk(self): - from mmgen.seed import MMGenSeedFile return self.ref_ss_chk(ss=MMGenSeedFile) def ref_hex_chk(self): - from mmgen.seed import MMGenHexSeedFile return self.ref_ss_chk(ss=MMGenHexSeedFile) def ref_plainhex_chk(self): - from mmgen.seed import PlainHexSeedFile return self.ref_ss_chk(ss=PlainHexSeedFile) def ref_dieroll_chk(self): - from mmgen.seed import DieRollSeedFile return self.ref_ss_chk(ss=DieRollSeedFile) def ref_mn_chk(self): - from mmgen.seed import MMGenMnemonic return self.ref_ss_chk(ss=MMGenMnemonic) def ref_bip39_chk(self): - from mmgen.seed import BIP39Mnemonic return self.ref_ss_chk(ss=BIP39Mnemonic) def ref_hincog_chk(self,desc='hidden incognito data'): diff --git a/test/test_py_d/ts_regtest.py b/test/test_py_d/ts_regtest.py index aa5616e0..a92855aa 100755 --- a/test/test_py_d/ts_regtest.py +++ b/test/test_py_d/ts_regtest.py @@ -27,7 +27,7 @@ from mmgen.opts import opt from mmgen.util import die,gmsg,write_data_to_file from mmgen.protocol import CoinProtocol from mmgen.addr import AddrList -from mmgen.seed import MMGenWallet +from mmgen.wallet import MMGenWallet from ..include.common import * from .common import * diff --git a/test/test_py_d/ts_seedsplit.py b/test/test_py_d/ts_seedsplit.py index a217df73..0fb548a5 100755 --- a/test/test_py_d/ts_seedsplit.py +++ b/test/test_py_d/ts_seedsplit.py @@ -22,7 +22,7 @@ ts_seedsplit.py: Seed split/join tests for the test.py test suite from mmgen.globalvars import g from mmgen.opts import opt -from mmgen.seed import Wallet,MMGenWallet,IncogWallet,IncogWalletHex,IncogWalletHidden,WalletEnc +from mmgen.wallet import Wallet,MMGenWallet,IncogWallet,IncogWalletHex,IncogWalletHidden,WalletEnc from .ts_base import * diff --git a/test/test_py_d/ts_shared.py b/test/test_py_d/ts_shared.py index c849f76c..3814d2ae 100755 --- a/test/test_py_d/ts_shared.py +++ b/test/test_py_d/ts_shared.py @@ -24,7 +24,7 @@ import os from mmgen.globalvars import g from mmgen.opts import opt from mmgen.util import ymsg -from mmgen.seed import Wallet,WalletEnc,Brainwallet,MMGenWallet,IncogWalletHidden +from mmgen.wallet import Wallet,WalletEnc,Brainwallet,MMGenWallet,IncogWalletHidden from ..include.common import * from .common import * diff --git a/test/test_py_d/ts_wallet.py b/test/test_py_d/ts_wallet.py index 2e2384b1..6e319548 100755 --- a/test/test_py_d/ts_wallet.py +++ b/test/test_py_d/ts_wallet.py @@ -22,7 +22,7 @@ ts_wallet.py: Wallet conversion tests for the test.py test suite import os from mmgen.opts import opt -from mmgen.seed import * +from mmgen.wallet import * from .common import * from .ts_base import * from .ts_shared import * diff --git a/test/tooltest2.py b/test/tooltest2.py index 21e069a8..28f9050e 100755 --- a/test/tooltest2.py +++ b/test/tooltest2.py @@ -31,7 +31,7 @@ from include.tests_header import repo_root from mmgen.common import * from test.include.common import * from mmgen.obj import is_wif,is_coin_addr -from mmgen.seed import is_bip39_mnemonic,is_mmgen_mnemonic +from mmgen.wallet import is_bip39_mnemonic,is_mmgen_mnemonic from mmgen.addr import is_xmrseed from mmgen.baseconv import *