modularize wallet classes
- all code has been relocated from `wallet.py` to individual modules under `wallet`, with each wallet type having its own module - the fully rewritten initialization code can be found in `__init__.py` and `base.py`
This commit is contained in:
parent
3b7e96ccfc
commit
9649f5b723
38 changed files with 1611 additions and 1276 deletions
|
|
@ -1 +1 @@
|
|||
13.1.dev016
|
||||
13.1.dev017
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ class Filename(MMGenObject):
|
|||
|
||||
def __init__(self,fn,base_class=None,subclass=None,proto=None,write=False):
|
||||
"""
|
||||
'base_class' - a base class with an 'ext_to_type' method
|
||||
'base_class' - a base class with an 'ext_to_cls' method
|
||||
'subclass' - a subclass with an 'ext' attribute
|
||||
|
||||
One or the other must be provided, but not both.
|
||||
|
|
@ -52,7 +52,7 @@ class Filename(MMGenObject):
|
|||
die(3,f'Class {(subclass or base_class).__name__!r} does not support the Filename API')
|
||||
|
||||
if base_class:
|
||||
subclass = base_class.ext_to_type(self.ext,proto)
|
||||
subclass = base_class.ext_to_cls( self.ext, proto )
|
||||
if not subclass:
|
||||
die( 'BadFileExtension', f'{self.ext!r}: not a recognized file extension for {base_class}' )
|
||||
|
||||
|
|
|
|||
|
|
@ -116,9 +116,9 @@ def get_seed_file(cmd_args,nargs,invoked_as=None):
|
|||
|
||||
from .opts import opt
|
||||
from .filename import find_file_in_dir
|
||||
from .wallet import MMGenWallet
|
||||
from .wallet.mmgen import wallet
|
||||
|
||||
wf = find_file_in_dir(MMGenWallet,g.data_dir)
|
||||
wf = find_file_in_dir(wallet,g.data_dir)
|
||||
|
||||
wd_from_opt = bool(opt.hidden_incog_input_params or opt.in_fmt) # have wallet data from opt?
|
||||
|
||||
|
|
|
|||
|
|
@ -81,8 +81,8 @@ def help_notes_func(proto,po,k):
|
|||
])
|
||||
|
||||
def fmt_codes():
|
||||
from .wallet import Wallet
|
||||
return '\n '.join( Wallet.format_fmt_codes().splitlines() )
|
||||
from .wallet import format_fmt_codes
|
||||
return '\n '.join( format_fmt_codes().splitlines() )
|
||||
|
||||
def coin_id():
|
||||
return proto.coin_id
|
||||
|
|
|
|||
|
|
@ -22,8 +22,7 @@ mmgen/main_wallet: Entry point for MMGen wallet-related scripts
|
|||
|
||||
import os
|
||||
from .common import *
|
||||
from .wallet import Wallet,MMGenWallet
|
||||
from .filename import find_file_in_dir
|
||||
from .wallet import Wallet,get_wallet_cls
|
||||
|
||||
usage = '[opts] [infile]'
|
||||
nargs = 1
|
||||
|
|
@ -234,7 +233,7 @@ if invoked_as == 'passchg' and ss_in.infile.dirname == g.data_dir:
|
|||
elif invoked_as == 'gen' and not opt.outdir and not opt.stdout:
|
||||
from .filename import find_file_in_dir
|
||||
if (
|
||||
not find_file_in_dir( MMGenWallet, g.data_dir )
|
||||
not find_file_in_dir( get_wallet_cls('mmgen'), g.data_dir )
|
||||
and keypress_confirm(
|
||||
'Make this wallet your default and move it to the data directory?',
|
||||
default_yes = True ) ):
|
||||
|
|
|
|||
|
|
@ -469,23 +469,23 @@ def check_usr_opts(usr_opts): # Raises an exception if any check fails
|
|||
end ))
|
||||
|
||||
def chk_in_fmt(key,val,desc):
|
||||
from .wallet import Wallet,IncogWallet,Brainwallet,IncogWalletHidden
|
||||
sstype = Wallet.fmt_code_to_type(val)
|
||||
if not sstype:
|
||||
from .wallet import get_wallet_data
|
||||
wd = get_wallet_data(fmt_code=val)
|
||||
if not wd:
|
||||
opt_unrecognized(key,val)
|
||||
if key == 'out_fmt':
|
||||
p = 'hidden_incog_output_params'
|
||||
|
||||
if sstype == IncogWalletHidden and not getattr(opt,p):
|
||||
if wd.type == 'incog_hidden' and not getattr(opt,p):
|
||||
die( 'UserOptError',
|
||||
'Hidden incog format output requested. ' +
|
||||
f'You must supply a file and offset with the {fmt_opt(p)!r} option' )
|
||||
|
||||
if issubclass(sstype,IncogWallet) and opt.old_incog_fmt:
|
||||
if wd.base_type == 'incog_base' and opt.old_incog_fmt:
|
||||
opt_display(key,val,beg='Selected',end=' ')
|
||||
opt_display('old_incog_fmt',beg='conflicts with',end=':\n')
|
||||
die( 'UserOptError', 'Export to old incog wallet format unsupported' )
|
||||
elif issubclass(sstype,Brainwallet):
|
||||
elif wd.type == 'brain':
|
||||
die( 'UserOptError', 'Output to brainwallet format unsupported' )
|
||||
|
||||
chk_out_fmt = chk_in_fmt
|
||||
|
|
@ -515,8 +515,9 @@ def check_usr_opts(usr_opts): # Raises an exception if any check fails
|
|||
|
||||
if hasattr(opt,key2):
|
||||
val2 = getattr(opt,key2)
|
||||
from .wallet import IncogWalletHidden
|
||||
if val2 and val2 not in IncogWalletHidden.fmt_codes:
|
||||
from .wallet import get_wallet_data
|
||||
wd = get_wallet_data('incog_hidden')
|
||||
if val2 and val2 not in wd.fmt_codes:
|
||||
die( 'UserOptError', f'Option conflict:\n {fmt_opt(key)}, with\n {fmt_opt(key2)}={val2}' )
|
||||
|
||||
chk_hidden_incog_output_params = chk_hidden_incog_input_params
|
||||
|
|
|
|||
|
|
@ -44,7 +44,7 @@ def _get_cls_info(clsname,modname,args,kwargs):
|
|||
from ..util import get_extension
|
||||
from .completed import Completed
|
||||
ext = get_extension( kwargs['filename'] )
|
||||
cls = Completed.ext_to_type( ext, proto )
|
||||
cls = Completed.ext_to_cls( ext, proto )
|
||||
if not cls:
|
||||
die(1,f'{ext!r}: unrecognized file extension for CompletedTX')
|
||||
clsname = cls.__name__
|
||||
|
|
|
|||
|
|
@ -53,7 +53,7 @@ class Completed(Base):
|
|||
return MMGenTxFile(self)
|
||||
|
||||
@classmethod
|
||||
def ext_to_type(cls,ext,proto):
|
||||
def ext_to_cls(cls,ext,proto):
|
||||
"""
|
||||
see twctl:import_token()
|
||||
"""
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ from .common import *
|
|||
from .obj import MMGenList
|
||||
from .addr import MMGenAddrType
|
||||
from .addrlist import AddrIdxList,KeyAddrList
|
||||
from .wallet import Wallet,WalletUnenc,WalletEnc,MMGenWallet
|
||||
from .wallet import Wallet,get_wallet_extensions,get_wallet_cls
|
||||
|
||||
saved_seeds = {}
|
||||
|
||||
|
|
@ -117,13 +117,12 @@ def get_tx_files(opt,args):
|
|||
|
||||
def get_seed_files(opt,args):
|
||||
# favor unencrypted seed sources first, as they don't require passwords
|
||||
u,e = WalletUnenc,WalletEnc
|
||||
ret = _pop_matching_fns(args,u.get_extensions())
|
||||
ret = _pop_matching_fns( args, get_wallet_extensions('unenc') )
|
||||
from .filename import find_file_in_dir
|
||||
wf = find_file_in_dir(MMGenWallet,g.data_dir) # Make this the first encrypted ss in the list
|
||||
wf = find_file_in_dir(get_wallet_cls('mmgen'),g.data_dir) # Make this the first encrypted ss in the list
|
||||
if wf:
|
||||
ret.append(wf)
|
||||
ret += _pop_matching_fns(args,e.get_extensions())
|
||||
ret += _pop_matching_fns( args, get_wallet_extensions('enc') )
|
||||
if not (ret or opt.mmgen_keys_from_file or opt.keys_from_file): # or opt.use_wallet_dat
|
||||
die(1,'You must specify a seed or key source!')
|
||||
return ret
|
||||
|
|
|
|||
|
|
@ -453,9 +453,8 @@ def compare_or_die(val1, desc1, val2, desc2, e='Error'):
|
|||
return True
|
||||
|
||||
def check_wallet_extension(fn):
|
||||
from .wallet import Wallet
|
||||
if not Wallet.ext_to_type(get_extension(fn)):
|
||||
die( 'BadFileExtension', f'{fn!r}: unrecognized seed source file extension' )
|
||||
from .wallet import get_wallet_data
|
||||
get_wallet_data( ext=get_extension(fn), die_on_fail=True ) # raises exception on failure
|
||||
|
||||
def make_full_path(outdir,outfile):
|
||||
return os.path.normpath(os.path.join(outdir, os.path.basename(outfile)))
|
||||
|
|
|
|||
1176
mmgen/wallet.py
1176
mmgen/wallet.py
File diff suppressed because it is too large
Load diff
158
mmgen/wallet/__init__.py
Executable file
158
mmgen/wallet/__init__.py
Executable file
|
|
@ -0,0 +1,158 @@
|
|||
#!/usr/bin/env python3
|
||||
#
|
||||
# mmgen = Multi-Mode GENerator, a command-line cryptocurrency wallet
|
||||
# Copyright (C)2013-2022 The MMGen Project <mmgen@tuta.io>
|
||||
# Licensed under the GNU General Public License, Version 3:
|
||||
# https://www.gnu.org/licenses
|
||||
# Public project repositories:
|
||||
# https://github.com/mmgen/mmgen
|
||||
# https://gitlab.com/mmgen/mmgen
|
||||
|
||||
"""
|
||||
wallet.__init__: wallet class initializer
|
||||
"""
|
||||
|
||||
import importlib
|
||||
from collections import namedtuple
|
||||
|
||||
from ..globalvars import g
|
||||
from ..opts import opt
|
||||
from ..util import die,get_extension
|
||||
from ..objmethods import MMGenObject
|
||||
from ..seed import Seed
|
||||
|
||||
_wd = namedtuple('wallet_data', ['type','name','ext','base_type','enc','fmt_codes'])
|
||||
_pd = namedtuple('partial_wallet_data',['name','ext','base_type','enc','fmt_codes'])
|
||||
|
||||
wallet_data = {
|
||||
'bip39': _pd('BIP39Mnemonic', 'bip39', 'mnemonic', False,('bip39',)),
|
||||
'brain': _pd('Brainwallet', 'mmbrain',None, True, ('mmbrain','brainwallet','brain','bw')),
|
||||
'dieroll': _pd('DieRollWallet', 'b6d', None, False,('b6d','die','dieroll')),
|
||||
'incog': _pd('IncogWallet', 'mmincog','incog_base',True, ('mmincog','incog','icg','i')),
|
||||
'incog_hex': _pd('IncogWalletHex', 'mmincox','incog_base',True, ('mmincox','incox','incog_hex','ix','xi')),
|
||||
'incog_hidden':_pd('IncogWalletHidden',None, 'incog_base',True, ('incog_hidden','hincog','ih','hi')),
|
||||
'mmgen': _pd('MMGenWallet', 'mmdat', None, True, ('wallet','w')),
|
||||
'mmhex': _pd('MMGenHexSeedFile', 'mmhex', None, False,('seedhex','hexseed','mmhex')),
|
||||
'plainhex': _pd('PlainHexSeedFile', 'hex', None, False,('hex','rawhex','plainhex')),
|
||||
'seed': _pd('MMGenSeedFile', 'mmseed', None, False,('mmseed','seed','s')),
|
||||
'words': _pd('MMGenMnemonic', 'mmwords','mnemonic', False,('mmwords','words','mnemonic','mn','m')),
|
||||
}
|
||||
|
||||
def get_wallet_data(*args,**kwargs):
|
||||
|
||||
if args:
|
||||
return _wd( args[0], *wallet_data[args[0]] )
|
||||
|
||||
for key in ('fmt_code','ext'):
|
||||
if key in kwargs:
|
||||
val = kwargs[key]
|
||||
break
|
||||
else:
|
||||
die('{!r}: unrecognized argument'.format( list(kwargs.keys())[0] ))
|
||||
|
||||
if key == 'fmt_code':
|
||||
for k,v in wallet_data.items():
|
||||
if val in v.fmt_codes:
|
||||
return _wd(k,*v)
|
||||
else:
|
||||
for k,v in wallet_data.items():
|
||||
if val == getattr(v,key):
|
||||
return _wd(k,*v)
|
||||
|
||||
if 'die_on_fail' in kwargs:
|
||||
die( *{
|
||||
'ext': ('BadFileExtension', f'{val!r}: unrecognized wallet file extension'),
|
||||
'fmt_code': (3, f'{val!r}: unrecognized wallet format code'),
|
||||
'type': (3, f'{val!r}: unrecognized wallet type'),
|
||||
}[key] )
|
||||
|
||||
def get_wallet_cls(*args,**kwargs):
|
||||
return getattr(
|
||||
importlib.import_module( 'mmgen.wallet.{}'.format(
|
||||
args[0] if args else get_wallet_data(*args,**kwargs).type)
|
||||
),
|
||||
'wallet' )
|
||||
|
||||
def get_wallet_extensions(key):
|
||||
return {
|
||||
'enc': [v.ext for v in wallet_data.values() if v.enc],
|
||||
'unenc': [v.ext for v in wallet_data.values() if not v.enc]
|
||||
}[key]
|
||||
|
||||
def format_fmt_codes():
|
||||
d = [(
|
||||
v.name,
|
||||
('.' + v.ext if v.ext else 'None'),
|
||||
','.join(v.fmt_codes)
|
||||
) for v in wallet_data.values()]
|
||||
w = max(len(i[0]) for i in d)
|
||||
ret = [f'{a:<{w}} {b:<9} {c}' for a,b,c in [
|
||||
('Format','FileExt','Valid codes'),
|
||||
('------','-------','-----------')
|
||||
] + sorted(d) ]
|
||||
return '\n'.join(ret) + ('','-α')[g.debug_utf8] + '\n'
|
||||
|
||||
def _get_me(modname):
|
||||
return MMGenObject.__new__( getattr( importlib.import_module(f'mmgen.wallet.{modname}'), 'wallet' ) )
|
||||
|
||||
def Wallet(
|
||||
fn = None,
|
||||
ss = None,
|
||||
seed_bin = None,
|
||||
seed = None,
|
||||
passchg = False,
|
||||
in_data = None,
|
||||
ignore_in_fmt = False,
|
||||
in_fmt = None,
|
||||
passwd_file = None ):
|
||||
|
||||
in_fmt = in_fmt or opt.in_fmt
|
||||
|
||||
if opt.out_fmt:
|
||||
ss_out = get_wallet_data(fmt_code=opt.out_fmt)
|
||||
if not ss_out:
|
||||
die(1,f'{opt.out_fmt!r}: unrecognized output format')
|
||||
else:
|
||||
ss_out = None
|
||||
|
||||
if seed or seed_bin:
|
||||
me = _get_me( ss_out.type if ss_out else 'mmgen' ) # default to native wallet format
|
||||
me.seed = seed or Seed(seed_bin=seed_bin)
|
||||
me.op = 'new'
|
||||
elif ss:
|
||||
me = _get_me( ss.type if passchg else ss_out.type if ss_out else 'mmgen' )
|
||||
me.seed = ss.seed
|
||||
me.ss_in = ss
|
||||
me.op = 'pwchg_new' if passchg else 'conv'
|
||||
elif fn or opt.hidden_incog_input_params:
|
||||
if fn:
|
||||
wd = get_wallet_data(ext=get_extension(fn),die_on_fail=True)
|
||||
if in_fmt and (not ignore_in_fmt) and in_fmt not in wd.fmt_codes:
|
||||
die(1,f'{in_fmt}: --in-fmt parameter does not match extension of input file')
|
||||
me = _get_me( wd.type )
|
||||
else:
|
||||
fn = ','.join(opt.hidden_incog_input_params.split(',')[:-1]) # permit comma in filename
|
||||
me = _get_me( 'incog_hidden' )
|
||||
from ..filename import Filename
|
||||
me.infile = Filename( fn, subclass=type(me) )
|
||||
me.op = 'pwchg_old' if passchg else 'old'
|
||||
elif in_fmt:
|
||||
me = _get_me( get_wallet_data(fmt_code=in_fmt).type )
|
||||
me.op = 'pwchg_old' if passchg else 'old'
|
||||
else: # called with no arguments: initialize with random seed
|
||||
me = _get_me( ss_out.type if ss_out else 'mmgen' ) # default to native wallet format
|
||||
me.seed = Seed()
|
||||
me.op = 'new'
|
||||
|
||||
me.__init__(
|
||||
fn = fn,
|
||||
ss = ss,
|
||||
seed_bin = seed_bin,
|
||||
seed = seed,
|
||||
passchg = passchg,
|
||||
in_data = in_data,
|
||||
ignore_in_fmt = ignore_in_fmt,
|
||||
in_fmt = in_fmt,
|
||||
passwd_file = passwd_file )
|
||||
|
||||
return me
|
||||
136
mmgen/wallet/base.py
Executable file
136
mmgen/wallet/base.py
Executable file
|
|
@ -0,0 +1,136 @@
|
|||
#!/usr/bin/env python3
|
||||
#
|
||||
# mmgen = Multi-Mode GENerator, a command-line cryptocurrency wallet
|
||||
# Copyright (C)2013-2022 The MMGen Project <mmgen@tuta.io>
|
||||
# Licensed under the GNU General Public License, Version 3:
|
||||
# https://www.gnu.org/licenses
|
||||
# Public project repositories:
|
||||
# https://github.com/mmgen/mmgen
|
||||
# https://gitlab.com/mmgen/mmgen
|
||||
|
||||
"""
|
||||
wallet.base: wallet base class
|
||||
"""
|
||||
|
||||
import os
|
||||
|
||||
from ..globalvars import g
|
||||
from ..opts import opt
|
||||
from ..util import msg,qmsg,die,get_data_from_user
|
||||
from ..objmethods import MMGenObject
|
||||
from . import Wallet,wallet_data,get_wallet_cls
|
||||
|
||||
class WalletMeta(type):
|
||||
|
||||
def __init__(cls,name,bases,namespace):
|
||||
cls.type = cls.__module__.split('.')[-1]
|
||||
if cls.type in wallet_data:
|
||||
for k,v in wallet_data[cls.type]._asdict().items():
|
||||
setattr(cls,k,v)
|
||||
|
||||
class wallet(MMGenObject,metaclass=WalletMeta):
|
||||
|
||||
desc = 'MMGen seed source'
|
||||
file_mode = 'text'
|
||||
filename_api = True
|
||||
stdin_ok = False
|
||||
ask_tty = True
|
||||
no_tty = False
|
||||
op = None
|
||||
|
||||
class WalletData(MMGenObject):
|
||||
pass
|
||||
|
||||
def __init__(self,
|
||||
fn = None,
|
||||
ss = None,
|
||||
seed_bin = None,
|
||||
seed = None,
|
||||
passchg = False,
|
||||
in_data = None,
|
||||
ignore_in_fmt = False,
|
||||
in_fmt = None,
|
||||
passwd_file = None ):
|
||||
|
||||
self.passwd_file = passwd_file or opt.passwd_file
|
||||
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,f'Reading from standard input not supported for {self.desc} format')
|
||||
self._deformat_retry()
|
||||
self._decrypt_retry()
|
||||
|
||||
qmsg('Valid {} for Seed ID {}{}'.format(
|
||||
self.desc,
|
||||
self.seed.sid.hl(),
|
||||
(f', seed length {self.seed.bitlen}' if self.seed.bitlen != 256 else '')
|
||||
))
|
||||
|
||||
def _get_data(self):
|
||||
if hasattr(self,'infile'):
|
||||
from ..fileutil import get_data_from_file
|
||||
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...')
|
||||
|
||||
@classmethod
|
||||
def ext_to_cls(cls,ext,proto):
|
||||
return get_wallet_cls(ext=ext)
|
||||
|
||||
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'
|
||||
}
|
||||
|
||||
if outdir:
|
||||
# write_data_to_file(): outfile with absolute path overrides opt.outdir
|
||||
of = os.path.abspath(os.path.join(outdir,self._filename()))
|
||||
|
||||
from ..fileutil import write_data_to_file
|
||||
write_data_to_file(
|
||||
of if outdir else self._filename(),
|
||||
self.fmt_data,
|
||||
**kwargs )
|
||||
|
||||
def check_usr_seed_len(self,bitlen=None):
|
||||
chk = bitlen or self.seed.bitlen
|
||||
if opt.seed_len and opt.seed_len != chk:
|
||||
die(1,f'ERROR: requested seed length ({opt.seed_len}) doesn’t match seed length of source ({chk})')
|
||||
26
mmgen/wallet/bip39.py
Executable file
26
mmgen/wallet/bip39.py
Executable file
|
|
@ -0,0 +1,26 @@
|
|||
#!/usr/bin/env python3
|
||||
#
|
||||
# mmgen = Multi-Mode GENerator, a command-line cryptocurrency wallet
|
||||
# Copyright (C)2013-2022 The MMGen Project <mmgen@tuta.io>
|
||||
# Licensed under the GNU General Public License, Version 3:
|
||||
# https://www.gnu.org/licenses
|
||||
# Public project repositories:
|
||||
# https://github.com/mmgen/mmgen
|
||||
# https://gitlab.com/mmgen/mmgen
|
||||
|
||||
"""
|
||||
wallet.bip39: BIP39 mnemonic wallet class
|
||||
"""
|
||||
|
||||
from .mnemonic import wallet
|
||||
|
||||
class wallet(wallet):
|
||||
|
||||
desc = 'BIP39 mnemonic data'
|
||||
mn_type = 'BIP39'
|
||||
wl_id = 'bip39'
|
||||
|
||||
def __init__(self,*args,**kwargs):
|
||||
from ..bip39 import bip39
|
||||
self.conv_cls = bip39
|
||||
super().__init__(*args,**kwargs)
|
||||
67
mmgen/wallet/brain.py
Executable file
67
mmgen/wallet/brain.py
Executable file
|
|
@ -0,0 +1,67 @@
|
|||
#!/usr/bin/env python3
|
||||
#
|
||||
# mmgen = Multi-Mode GENerator, a command-line cryptocurrency wallet
|
||||
# Copyright (C)2013-2022 The MMGen Project <mmgen@tuta.io>
|
||||
# Licensed under the GNU General Public License, Version 3:
|
||||
# https://www.gnu.org/licenses
|
||||
# Public project repositories:
|
||||
# https://github.com/mmgen/mmgen
|
||||
# https://gitlab.com/mmgen/mmgen
|
||||
|
||||
"""
|
||||
wallet.brain: brainwallet wallet class
|
||||
"""
|
||||
|
||||
from ..opts import opt
|
||||
from ..util import msg,qmsg,qmsg_r
|
||||
from ..color import yellow
|
||||
from .enc import wallet
|
||||
from .seed import Seed
|
||||
import mmgen.crypto as crypto
|
||||
|
||||
class wallet(wallet):
|
||||
|
||||
stdin_ok = True
|
||||
desc = 'brainwallet'
|
||||
# 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
|
||||
if opt.brain_params:
|
||||
"""
|
||||
Don't set opt.seed_len! When using multiple wallets, BW seed len might differ from others
|
||||
"""
|
||||
bw_seed_len,d.hash_preset = self.get_bw_params()
|
||||
else:
|
||||
if not opt.seed_len:
|
||||
qmsg(f'Using default seed length of {yellow(str(Seed.dfl_len))} bits\n'
|
||||
+ 'If this is not what you want, use the --seed-len option' )
|
||||
self._get_hash_preset()
|
||||
bw_seed_len = opt.seed_len or Seed.dfl_len
|
||||
qmsg_r('Hashing brainwallet data. Please wait...')
|
||||
# Use buflen arg of scrypt.hash() to get seed of desired length
|
||||
seed = crypto.scrypt_hash_passphrase(
|
||||
self.brainpasswd.encode(),
|
||||
b'',
|
||||
d.hash_preset,
|
||||
buflen = bw_seed_len // 8 )
|
||||
qmsg('Done')
|
||||
self.seed = Seed(seed)
|
||||
msg(f'Seed ID: {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')
|
||||
114
mmgen/wallet/dieroll.py
Executable file
114
mmgen/wallet/dieroll.py
Executable file
|
|
@ -0,0 +1,114 @@
|
|||
#!/usr/bin/env python3
|
||||
#
|
||||
# mmgen = Multi-Mode GENerator, a command-line cryptocurrency wallet
|
||||
# Copyright (C)2013-2022 The MMGen Project <mmgen@tuta.io>
|
||||
# Licensed under the GNU General Public License, Version 3:
|
||||
# https://www.gnu.org/licenses
|
||||
# Public project repositories:
|
||||
# https://github.com/mmgen/mmgen
|
||||
# https://gitlab.com/mmgen/mmgen
|
||||
|
||||
"""
|
||||
wallet.dieroll: dieroll wallet class
|
||||
"""
|
||||
|
||||
from ..globalvars import g
|
||||
from ..opts import opt
|
||||
from ..util import msg,msg_r,die,fmt,block_format,remove_whitespace,keypress_confirm
|
||||
from ..seed import Seed
|
||||
from ..baseconv import baseconv
|
||||
from .unenc import wallet
|
||||
|
||||
class wallet(wallet):
|
||||
|
||||
stdin_ok = True
|
||||
desc = 'base6d die roll seed data'
|
||||
conv_cls = baseconv
|
||||
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('b6d').frombytes(self.seed.data,pad='seed',tostr=True) + '\n'
|
||||
self.fmt_data = block_format(d,gw=5,cols=5)
|
||||
|
||||
def _deformat(self):
|
||||
|
||||
d = remove_whitespace(self.fmt_data)
|
||||
bc = baseconv('b6d')
|
||||
rmap = bc.seedlen_map_rev
|
||||
|
||||
if not len(d) in rmap:
|
||||
die( 'SeedLengthError', '{!r}: invalid length for {} (must be one of {})'.format(
|
||||
len(d),
|
||||
self.desc,
|
||||
list(rmap) ))
|
||||
|
||||
# truncate seed to correct length, discarding high bits
|
||||
seed_len = rmap[len(d)]
|
||||
seed_bytes = bc.tobytes( d, pad='seed' )[-seed_len:]
|
||||
|
||||
if self.interactive_input and opt.usr_randchars:
|
||||
if keypress_confirm(self.user_entropy_prompt):
|
||||
from ..crypto import add_user_random
|
||||
seed_bytes = add_user_random(
|
||||
rand_bytes = seed_bytes,
|
||||
desc = 'gathered from your die rolls' )
|
||||
self.desc += ' plus user-supplied entropy'
|
||||
|
||||
self.seed = Seed(seed_bytes)
|
||||
self.ssdata.hexseed = seed_bytes.hex()
|
||||
|
||||
self.check_usr_seed_len()
|
||||
return True
|
||||
|
||||
def _get_data_from_user(self,desc):
|
||||
|
||||
if not g.stdin_tty:
|
||||
return get_data_from_user(desc)
|
||||
|
||||
bc = baseconv('b6d')
|
||||
|
||||
seed_bitlen = self._choose_seedlen([ n*8 for n in sorted(bc.seedlen_map) ])
|
||||
nDierolls = bc.seedlen_map[seed_bitlen // 8]
|
||||
|
||||
message = """
|
||||
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(message.strip()).format(sb=seed_bitlen,nd=nDierolls)+'\n')
|
||||
|
||||
CUR_HIDE = '\033[?25l'
|
||||
CUR_SHOW = '\033[?25h'
|
||||
cr = '\n' if g.test_suite else '\r'
|
||||
prompt_fs = f'\b\b\b {cr}Enter die roll #{{}}: {CUR_SHOW}'
|
||||
clear_line = '' if g.test_suite else '\r' + ' ' * 25
|
||||
invalid_msg = CUR_HIDE + cr + 'Invalid entry' + ' ' * 11
|
||||
|
||||
from ..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 bc.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)
|
||||
96
mmgen/wallet/enc.py
Executable file
96
mmgen/wallet/enc.py
Executable file
|
|
@ -0,0 +1,96 @@
|
|||
#!/usr/bin/env python3
|
||||
#
|
||||
# mmgen = Multi-Mode GENerator, a command-line cryptocurrency wallet
|
||||
# Copyright (C)2013-2022 The MMGen Project <mmgen@tuta.io>
|
||||
# Licensed under the GNU General Public License, Version 3:
|
||||
# https://www.gnu.org/licenses
|
||||
# Public project repositories:
|
||||
# https://github.com/mmgen/mmgen
|
||||
# https://gitlab.com/mmgen/mmgen
|
||||
|
||||
"""
|
||||
wallet.enc: encrypted wallet base class
|
||||
"""
|
||||
|
||||
from ..globalvars import g
|
||||
from ..opts import opt
|
||||
from ..util import msg,qmsg,make_chksum_8
|
||||
|
||||
import mmgen.crypto as crypto
|
||||
|
||||
from .base import wallet
|
||||
|
||||
class wallet(wallet):
|
||||
|
||||
def _decrypt_retry(self):
|
||||
while True:
|
||||
if self._decrypt():
|
||||
break
|
||||
if self.passwd_file:
|
||||
die(2,'Passphrase from password file, so exiting')
|
||||
msg('Trying again...')
|
||||
|
||||
def _get_hash_preset_from_user(self,hp,add_desc=''):
|
||||
prompt = 'Enter {}hash preset for {}{}{},\nor hit ENTER to {} value ({!r}): '.format(
|
||||
('old ' if self.op=='pwchg_old' else 'new ' if self.op=='pwchg_new' else ''),
|
||||
('','new ')[self.op=='new'],
|
||||
self.desc,
|
||||
('',' '+add_desc)[bool(add_desc)],
|
||||
('accept the default','reuse the old')[self.op=='pwchg_new'],
|
||||
hp )
|
||||
return crypto.get_hash_preset_from_user( hash_preset=hp, prompt=prompt )
|
||||
|
||||
def _get_hash_preset(self,add_desc=''):
|
||||
if hasattr(self,'ss_in') and hasattr(self.ss_in.ssdata,'hash_preset'):
|
||||
old_hp = self.ss_in.ssdata.hash_preset
|
||||
if opt.keep_hash_preset:
|
||||
hp = old_hp
|
||||
qmsg(f'Reusing hash preset {hp!r} at user request')
|
||||
elif opt.hash_preset:
|
||||
hp = opt.hash_preset
|
||||
qmsg(f'Using hash preset {hp!r} requested on command line')
|
||||
else: # Prompt, using old value as default
|
||||
hp = self._get_hash_preset_from_user(old_hp,add_desc)
|
||||
if (not opt.keep_hash_preset) and self.op == 'pwchg_new':
|
||||
qmsg('Hash preset {}'.format( 'unchanged' if hp == old_hp else f'changed to {hp!r}' ))
|
||||
elif opt.hash_preset:
|
||||
hp = opt.hash_preset
|
||||
qmsg(f'Using hash preset {hp!r} requested on command line')
|
||||
else:
|
||||
hp = self._get_hash_preset_from_user(g.dfl_hash_preset,add_desc)
|
||||
self.ssdata.hash_preset = hp
|
||||
|
||||
def _get_new_passphrase(self):
|
||||
return crypto.get_new_passphrase(
|
||||
data_desc = ('new ' if self.op in ('new','conv') else '') + self.desc,
|
||||
hash_preset = self.ssdata.hash_preset,
|
||||
passwd_file = self.passwd_file,
|
||||
pw_desc = ('new ' if self.op=='pwchg_new' else '') + 'passphrase' )
|
||||
|
||||
def _get_passphrase(self,add_desc=''):
|
||||
return crypto.get_passphrase(
|
||||
data_desc = self.desc + (f' {add_desc}' if add_desc else ''),
|
||||
passwd_file = self.passwd_file,
|
||||
pw_desc = ('old ' if self.op == 'pwchg_old' else '') + 'passphrase' )
|
||||
|
||||
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:
|
||||
d.passwd = self._get_new_passphrase()
|
||||
if self.op == 'pwchg_new':
|
||||
qmsg('Passphrase {}'.format( 'unchanged' if d.passwd == old_pw else 'changed' ))
|
||||
else:
|
||||
d.passwd = self._get_new_passphrase()
|
||||
|
||||
from hashlib import sha256
|
||||
d.salt = sha256( crypto.get_random(128) ).digest()[:crypto.salt_len]
|
||||
key = crypto.make_key( d.passwd, d.salt, d.hash_preset )
|
||||
d.key_id = make_chksum_8(key)
|
||||
d.enc_seed = crypto.encrypt_seed( self.seed.data, key )
|
||||
21
mmgen/wallet/incog.py
Executable file
21
mmgen/wallet/incog.py
Executable file
|
|
@ -0,0 +1,21 @@
|
|||
#!/usr/bin/env python3
|
||||
#
|
||||
# mmgen = Multi-Mode GENerator, a command-line cryptocurrency wallet
|
||||
# Copyright (C)2013-2022 The MMGen Project <mmgen@tuta.io>
|
||||
# Licensed under the GNU General Public License, Version 3:
|
||||
# https://www.gnu.org/licenses
|
||||
# Public project repositories:
|
||||
# https://github.com/mmgen/mmgen
|
||||
# https://gitlab.com/mmgen/mmgen
|
||||
|
||||
"""
|
||||
wallet.incog: incognito wallet class
|
||||
"""
|
||||
|
||||
from .incog_base import wallet
|
||||
|
||||
class wallet(wallet):
|
||||
|
||||
desc = 'incognito data'
|
||||
file_mode = 'binary'
|
||||
no_tty = True
|
||||
169
mmgen/wallet/incog_base.py
Executable file
169
mmgen/wallet/incog_base.py
Executable file
|
|
@ -0,0 +1,169 @@
|
|||
#!/usr/bin/env python3
|
||||
#
|
||||
# mmgen = Multi-Mode GENerator, a command-line cryptocurrency wallet
|
||||
# Copyright (C)2013-2022 The MMGen Project <mmgen@tuta.io>
|
||||
# Licensed under the GNU General Public License, Version 3:
|
||||
# https://www.gnu.org/licenses
|
||||
# Public project repositories:
|
||||
# https://github.com/mmgen/mmgen
|
||||
# https://gitlab.com/mmgen/mmgen
|
||||
|
||||
"""
|
||||
wallet.incog_base: incognito wallet base class
|
||||
"""
|
||||
|
||||
from ..globalvars import g
|
||||
from ..opts import opt
|
||||
from ..seed import Seed
|
||||
from ..util import msg,vmsg,qmsg,make_chksum_8,keypress_confirm
|
||||
from .enc import wallet
|
||||
import mmgen.crypto as crypto
|
||||
|
||||
class wallet(wallet):
|
||||
|
||||
_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.
|
||||
""",
|
||||
'decrypt_params': " {} hash preset"
|
||||
}
|
||||
|
||||
def _make_iv_chksum(self,s):
|
||||
from hashlib import sha256
|
||||
return sha256(s).hexdigest()[:8].upper()
|
||||
|
||||
def _get_incog_data_len(self,seed_len):
|
||||
return (
|
||||
crypto.aesctr_iv_len
|
||||
+ crypto.salt_len
|
||||
+ (0 if opt.old_incog_fmt else crypto.hincog_chk_len)
|
||||
+ seed_len//8 )
|
||||
|
||||
def _incog_data_size_chk(self):
|
||||
# valid sizes: 56, 64, 72
|
||||
dlen = len(self.fmt_data)
|
||||
seed_len = opt.seed_len or Seed.dfl_len
|
||||
valid_dlen = self._get_incog_data_len(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?')
|
||||
msg(f'Invalid incognito data size ({dlen} bytes) for this seed length ({seed_len} bits)')
|
||||
msg(f'Valid data size for this seed length: {valid_dlen} bytes')
|
||||
for sl in Seed.lens:
|
||||
if dlen == self._get_incog_data_len(sl):
|
||||
die(1,f'Valid seed length for this data size: {sl} bits')
|
||||
msg(f'This data size ({dlen} bytes) is invalid for all available seed lengths')
|
||||
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 = crypto.get_random( crypto.aesctr_iv_len )
|
||||
d.iv_id = self._make_iv_chksum(d.iv)
|
||||
msg(f'New Incog Wallet ID: {d.iv_id}')
|
||||
qmsg('Make a record of this value')
|
||||
vmsg('\n ' + self.msg['record_incog_id'].strip()+'\n')
|
||||
|
||||
d.salt = crypto.get_random( crypto.salt_len )
|
||||
key = crypto.make_key( d.passwd, d.salt, d.hash_preset, 'incog wallet key' )
|
||||
from hashlib import sha256
|
||||
chk = sha256(self.seed.data).digest()[:8]
|
||||
d.enc_seed = crypto.encrypt_data(
|
||||
chk + self.seed.data,
|
||||
key,
|
||||
crypto.aesctr_dfl_iv,
|
||||
'seed' )
|
||||
|
||||
d.wrapper_key = crypto.make_key( d.passwd, d.iv, d.hash_preset, 'incog wrapper key' )
|
||||
d.key_id = make_chksum_8(d.wrapper_key)
|
||||
vmsg(f'Key ID: {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 + crypto.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:crypto.aesctr_iv_len]
|
||||
d.incog_id = self._make_iv_chksum(d.iv)
|
||||
d.enc_incog_data = self.fmt_data[crypto.aesctr_iv_len:]
|
||||
msg(f'Incog Wallet ID: {d.incog_id}')
|
||||
qmsg('Check this value against your records')
|
||||
vmsg('\n ' + self.msg['check_incog_id'].strip()+'\n')
|
||||
|
||||
return True
|
||||
|
||||
def _verify_seed_newfmt(self,data):
|
||||
chk,seed = data[:8],data[8:]
|
||||
from hashlib import sha256
|
||||
if sha256(seed).digest()[:8] == chk:
|
||||
qmsg('Passphrase{} are correct'.format( self.msg['decrypt_params'].format('and') ))
|
||||
return seed
|
||||
else:
|
||||
msg('Incorrect passphrase{}'.format( self.msg['decrypt_params'].format('or') ))
|
||||
return False
|
||||
|
||||
def _verify_seed_oldfmt(self,seed):
|
||||
m = f'Seed ID: {make_chksum_8(seed)}. Is the Seed ID correct?'
|
||||
if keypress_confirm(m, True):
|
||||
return seed
|
||||
else:
|
||||
return False
|
||||
|
||||
def _decrypt(self):
|
||||
d = self.ssdata
|
||||
self._get_hash_preset(add_desc=d.incog_id)
|
||||
d.passwd = self._get_passphrase(add_desc=d.incog_id)
|
||||
|
||||
# IV is used BOTH to initialize counter and to salt password!
|
||||
key = crypto.make_key( d.passwd, d.iv, d.hash_preset, 'wrapper key' )
|
||||
dd = crypto.decrypt_data( d.enc_incog_data, key, d.iv, 'incog data' )
|
||||
|
||||
d.salt = dd[0:crypto.salt_len]
|
||||
d.enc_seed = dd[crypto.salt_len:]
|
||||
|
||||
key = crypto.make_key( d.passwd, d.salt, d.hash_preset, 'main key' )
|
||||
qmsg(f'Key ID: {make_chksum_8(key)}')
|
||||
|
||||
verify_seed = getattr(self,'_verify_seed_'+
|
||||
('newfmt','oldfmt')[bool(opt.old_incog_fmt)])
|
||||
|
||||
seed = verify_seed( crypto.decrypt_seed(d.enc_seed, key, '', '') )
|
||||
|
||||
if seed:
|
||||
self.seed = Seed(seed)
|
||||
msg(f'Seed ID: {self.seed.sid}')
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
34
mmgen/wallet/incog_hex.py
Executable file
34
mmgen/wallet/incog_hex.py
Executable file
|
|
@ -0,0 +1,34 @@
|
|||
#!/usr/bin/env python3
|
||||
#
|
||||
# mmgen = Multi-Mode GENerator, a command-line cryptocurrency wallet
|
||||
# Copyright (C)2013-2022 The MMGen Project <mmgen@tuta.io>
|
||||
# Licensed under the GNU General Public License, Version 3:
|
||||
# https://www.gnu.org/licenses
|
||||
# Public project repositories:
|
||||
# https://github.com/mmgen/mmgen
|
||||
# https://gitlab.com/mmgen/mmgen
|
||||
|
||||
"""
|
||||
wallet.incog_hex: hexadecimal incognito wallet class
|
||||
"""
|
||||
|
||||
from ..util import pretty_hexdump,decode_pretty_hexdump
|
||||
from .incog_base import wallet
|
||||
|
||||
class wallet(wallet):
|
||||
|
||||
desc = 'hex incognito data'
|
||||
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)
|
||||
149
mmgen/wallet/incog_hidden.py
Executable file
149
mmgen/wallet/incog_hidden.py
Executable file
|
|
@ -0,0 +1,149 @@
|
|||
#!/usr/bin/env python3
|
||||
#
|
||||
# mmgen = Multi-Mode GENerator, a command-line cryptocurrency wallet
|
||||
# Copyright (C)2013-2022 The MMGen Project <mmgen@tuta.io>
|
||||
# Licensed under the GNU General Public License, Version 3:
|
||||
# https://www.gnu.org/licenses
|
||||
# Public project repositories:
|
||||
# https://github.com/mmgen/mmgen
|
||||
# https://gitlab.com/mmgen/mmgen
|
||||
|
||||
"""
|
||||
wallet.incog_hidden: hidden incognito wallet class
|
||||
"""
|
||||
|
||||
import os
|
||||
|
||||
from ..globalvars import g
|
||||
from ..opts import opt
|
||||
from ..seed import Seed
|
||||
from ..util import (
|
||||
msg,
|
||||
dmsg,
|
||||
qmsg,
|
||||
die,
|
||||
compare_or_die,
|
||||
keypress_confirm,
|
||||
parse_bytespec,
|
||||
line_input,
|
||||
capfirst,
|
||||
confirm_or_raise
|
||||
)
|
||||
from .incog_base import wallet
|
||||
|
||||
class wallet(wallet):
|
||||
|
||||
desc = 'hidden incognito data'
|
||||
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.
|
||||
""",
|
||||
'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 speci-
|
||||
fied 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.
|
||||
""",
|
||||
'decrypt_params': ', 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:
|
||||
die(1,'{} file {!r} has length {}, too short to {} {} bytes of data at offset {}'.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(f'Getting hidden incog data from file {self.infile.name!r}')
|
||||
|
||||
# Already sanity-checked:
|
||||
d.target_data_len = self._get_incog_data_len(opt.seed_len or Seed.dfl_len)
|
||||
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(f'Data read from file {self.infile.name!r} at offset {d.hincog_offset}')
|
||||
|
||||
# overrides method in Wallet
|
||||
def write_to_file(self):
|
||||
d = self.ssdata
|
||||
self._format()
|
||||
compare_or_die(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(
|
||||
f'Requested file {fn!r} does not exist. Create?',
|
||||
default_yes = True ):
|
||||
min_fsize = d.target_data_len + d.hincog_offset
|
||||
msg('\n ' + self.msg['choose_file_size'].strip().format(min_fsize)+'\n')
|
||||
while True:
|
||||
fsize = parse_bytespec(line_input('Enter file size: '))
|
||||
if fsize >= min_fsize:
|
||||
break
|
||||
msg(f'File size must be an integer no less than {min_fsize}')
|
||||
|
||||
from ..tool.fileutil import tool_cmd
|
||||
tool_cmd().rand2file(fn,str(fsize))
|
||||
check_offset = False
|
||||
else:
|
||||
die(1,'Exiting at user request')
|
||||
|
||||
from ..filename import Filename
|
||||
f = Filename(fn,subclass=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( '', f'alter file {f.name!r}' )
|
||||
|
||||
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 {!r} at offset {}'.format(
|
||||
capfirst(self.desc),
|
||||
f.name,
|
||||
d.hincog_offset ))
|
||||
191
mmgen/wallet/mmgen.py
Executable file
191
mmgen/wallet/mmgen.py
Executable file
|
|
@ -0,0 +1,191 @@
|
|||
#!/usr/bin/env python3
|
||||
#
|
||||
# mmgen = Multi-Mode GENerator, a command-line cryptocurrency wallet
|
||||
# Copyright (C)2013-2022 The MMGen Project <mmgen@tuta.io>
|
||||
# Licensed under the GNU General Public License, Version 3:
|
||||
# https://www.gnu.org/licenses
|
||||
# Public project repositories:
|
||||
# https://github.com/mmgen/mmgen
|
||||
# https://gitlab.com/mmgen/mmgen
|
||||
|
||||
"""
|
||||
wallet.mmgen: MMGen native wallet class
|
||||
"""
|
||||
|
||||
import os
|
||||
|
||||
from ..globalvars import g
|
||||
from ..opts import opt
|
||||
from ..seed import Seed
|
||||
from ..util import msg,qmsg,line_input,make_timestamp,make_chksum_6,split_into_cols,is_chksum_6,compare_chksums
|
||||
from ..obj import MMGenWalletLabel,get_obj
|
||||
from ..baseconv import baseconv
|
||||
|
||||
import mmgen.crypto as crypto
|
||||
|
||||
from .enc import wallet
|
||||
|
||||
class wallet(wallet):
|
||||
|
||||
desc = 'MMGen wallet'
|
||||
|
||||
def __init__(self,*args,**kwargs):
|
||||
if opt.label:
|
||||
self.label = MMGenWalletLabel(
|
||||
opt.label,
|
||||
msg = "Error in option '--label'" )
|
||||
else:
|
||||
self.label = None
|
||||
super().__init__(*args,**kwargs)
|
||||
|
||||
# logic identical to _get_hash_preset_from_user()
|
||||
def _get_label_from_user(self,old_lbl=''):
|
||||
prompt = 'Enter a wallet label, or hit ENTER {}: '.format(
|
||||
'to reuse the label {}'.format(old_lbl.hl(encl="''")) if old_lbl else
|
||||
'for no label' )
|
||||
while True:
|
||||
ret = line_input(prompt)
|
||||
if ret:
|
||||
lbl = get_obj(MMGenWalletLabel,s=ret)
|
||||
if lbl:
|
||||
return lbl
|
||||
else:
|
||||
msg('Invalid label. Trying again...')
|
||||
else:
|
||||
return old_lbl or MMGenWalletLabel('No Label')
|
||||
|
||||
# logic identical to _get_hash_preset()
|
||||
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:
|
||||
lbl = old_lbl
|
||||
qmsg('Reusing label {} at user request'.format( lbl.hl(encl="''") ))
|
||||
elif self.label:
|
||||
lbl = self.label
|
||||
qmsg('Using label {} requested on command line'.format( lbl.hl(encl="''") ))
|
||||
else: # Prompt, using old value as default
|
||||
lbl = self._get_label_from_user(old_lbl)
|
||||
if (not opt.keep_label) and self.op == 'pwchg_new':
|
||||
qmsg('Label {}'.format( 'unchanged' if lbl == old_lbl else f'changed to {lbl!r}' ))
|
||||
elif self.label:
|
||||
lbl = self.label
|
||||
qmsg('Using label {} requested on command line'.format( lbl.hl(encl="''") ))
|
||||
else:
|
||||
lbl = self._get_label_from_user()
|
||||
self.ssdata.label = lbl
|
||||
|
||||
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
|
||||
bc = baseconv('b58')
|
||||
slt_fmt = bc.frombytes(d.salt,pad='seed',tostr=True)
|
||||
es_fmt = bc.frombytes(d.enc_seed,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, *crypto.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(f'Invalid number of lines ({len(lines)}) in {desc} data')
|
||||
return False
|
||||
|
||||
if not is_chksum_6(lines[0]):
|
||||
msg(f'Incorrect master checksum ({lines[0]}) in {desc} data')
|
||||
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()
|
||||
self.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(f'Hash preset of wallet: {hp!r}')
|
||||
if opt.hash_preset and opt.hash_preset != hp:
|
||||
qmsg(f'Warning: ignoring user-requested hash preset {opt.hash_preset!r}')
|
||||
|
||||
hash_params = tuple(map(int,hpdata[1:]))
|
||||
|
||||
if hash_params != crypto.get_hash_params(d.hash_preset):
|
||||
msg(f'Hash parameters {" ".join(hash_params)!r} don’t match hash preset {d.hash_preset!r}')
|
||||
return False
|
||||
|
||||
lmin,foo,lmax = sorted(baseconv('b58').seedlen_map_rev) # 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(f'Invalid format for {key} in {self.desc}: {l}')
|
||||
return False
|
||||
|
||||
if not compare_chksums(chk,key,
|
||||
make_chksum_6(b58_val),'computed checksum',verbose=True):
|
||||
return False
|
||||
|
||||
val = baseconv('b58').tobytes(b58_val,pad='seed')
|
||||
if val == False:
|
||||
msg(f'Invalid base 58 number: {b58_val}')
|
||||
return False
|
||||
|
||||
setattr(d,key,val)
|
||||
|
||||
return True
|
||||
|
||||
def _decrypt(self):
|
||||
d = self.ssdata
|
||||
# Needed for multiple transactions with {}-txsign
|
||||
d.passwd = self._get_passphrase(
|
||||
add_desc = os.path.basename(self.infile.name) if opt.quiet else '' )
|
||||
key = crypto.make_key( d.passwd, d.salt, d.hash_preset )
|
||||
ret = crypto.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 '')
|
||||
66
mmgen/wallet/mmhex.py
Executable file
66
mmgen/wallet/mmhex.py
Executable file
|
|
@ -0,0 +1,66 @@
|
|||
#!/usr/bin/env python3
|
||||
#
|
||||
# mmgen = Multi-Mode GENerator, a command-line cryptocurrency wallet
|
||||
# Copyright (C)2013-2022 The MMGen Project <mmgen@tuta.io>
|
||||
# Licensed under the GNU General Public License, Version 3:
|
||||
# https://www.gnu.org/licenses
|
||||
# Public project repositories:
|
||||
# https://github.com/mmgen/mmgen
|
||||
# https://gitlab.com/mmgen/mmgen
|
||||
|
||||
"""
|
||||
wallet.mmhex: MMGen hexadecimal file wallet class
|
||||
"""
|
||||
|
||||
from ..util import make_chksum_6,split_into_cols
|
||||
from ..seed import Seed
|
||||
from ..util import msg,vmsg_r,is_chksum_6,is_hex_str,compare_chksums
|
||||
from .unenc import wallet
|
||||
|
||||
class wallet(wallet):
|
||||
|
||||
stdin_ok = True
|
||||
desc = 'hexadecimal seed data with checksum'
|
||||
|
||||
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(f'{self.fmt_data.strip()!r}: invalid {desc}')
|
||||
return False
|
||||
|
||||
if not len(hstr)*4 in Seed.lens:
|
||||
msg(f'Invalid data length ({len(hstr)}) in {desc}')
|
||||
return False
|
||||
|
||||
if not is_chksum_6(chk):
|
||||
msg(f'{chk!r}: invalid checksum format in {desc}')
|
||||
return False
|
||||
|
||||
if not is_hex_str(hstr):
|
||||
msg(f'{hstr!r}: not a hexadecimal string, in {desc}')
|
||||
return False
|
||||
|
||||
vmsg_r(f'Validating {desc} checksum...')
|
||||
|
||||
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
|
||||
|
||||
self.check_usr_seed_len()
|
||||
|
||||
return True
|
||||
86
mmgen/wallet/mnemonic.py
Executable file
86
mmgen/wallet/mnemonic.py
Executable file
|
|
@ -0,0 +1,86 @@
|
|||
#!/usr/bin/env python3
|
||||
#
|
||||
# mmgen = Multi-Mode GENerator, a command-line cryptocurrency wallet
|
||||
# Copyright (C)2013-2022 The MMGen Project <mmgen@tuta.io>
|
||||
# Licensed under the GNU General Public License, Version 3:
|
||||
# https://www.gnu.org/licenses
|
||||
# Public project repositories:
|
||||
# https://github.com/mmgen/mmgen
|
||||
# https://gitlab.com/mmgen/mmgen
|
||||
|
||||
"""
|
||||
wallet.mnemonic: MMGen mnemonic wallet base class
|
||||
"""
|
||||
|
||||
from ..globalvars import g
|
||||
from ..baseconv import baseconv
|
||||
from ..util import msg,compare_or_die,get_data_from_user
|
||||
from ..seed import Seed
|
||||
from .unenc import wallet
|
||||
|
||||
class wallet(wallet):
|
||||
|
||||
stdin_ok = True
|
||||
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(self.wl_id).seedlen_map_rev)
|
||||
|
||||
def _get_data_from_user(self,desc):
|
||||
|
||||
if not g.stdin_tty:
|
||||
return get_data_from_user(desc)
|
||||
|
||||
from ..mn_entry import mn_entry # import here to catch cfg var errors
|
||||
mn_len = self._choose_seedlen( self.mn_lens )
|
||||
return mn_entry(self.wl_id).get_mnemonic_from_user(mn_len)
|
||||
|
||||
def _format(self):
|
||||
|
||||
hexseed = self.seed.hexdata
|
||||
|
||||
bc = self.conv_cls(self.wl_id)
|
||||
mn = bc.fromhex( hexseed, 'seed' )
|
||||
rev = bc.tohex( mn, 'seed' )
|
||||
|
||||
# Internal error, so just die on fail
|
||||
compare_or_die( rev, 'recomputed seed', hexseed, 'original', e='Internal error' )
|
||||
|
||||
self.ssdata.mnemonic = mn
|
||||
self.fmt_data = ' '.join(mn) + '\n'
|
||||
|
||||
def _deformat(self):
|
||||
|
||||
bc = self.conv_cls(self.wl_id)
|
||||
mn = self.fmt_data.split()
|
||||
|
||||
if len(mn) not in self.mn_lens:
|
||||
msg('Invalid mnemonic ({} words). Valid numbers of words: {}'.format(
|
||||
len(mn),
|
||||
', '.join(map(str,self.mn_lens)) ))
|
||||
return False
|
||||
|
||||
for n,w in enumerate(mn,1):
|
||||
if w not in bc.digits:
|
||||
msg(f'Invalid mnemonic: word #{n} is not in the {self.wl_id.upper()} wordlist')
|
||||
return False
|
||||
|
||||
hexseed = bc.tohex( mn, 'seed' )
|
||||
rev = bc.fromhex( hexseed, 'seed' )
|
||||
|
||||
if len(hexseed) * 4 not in Seed.lens:
|
||||
msg('Invalid mnemonic (produces too large a number)')
|
||||
return False
|
||||
|
||||
# Internal error, so just die
|
||||
compare_or_die( ' '.join(rev), 'recomputed mnemonic', ' '.join(mn), 'original', e='Internal error' )
|
||||
|
||||
self.seed = Seed(bytes.fromhex(hexseed))
|
||||
self.ssdata.mnemonic = mn
|
||||
|
||||
self.check_usr_seed_len()
|
||||
|
||||
return True
|
||||
44
mmgen/wallet/plainhex.py
Executable file
44
mmgen/wallet/plainhex.py
Executable file
|
|
@ -0,0 +1,44 @@
|
|||
#!/usr/bin/env python3
|
||||
#
|
||||
# mmgen = Multi-Mode GENerator, a command-line cryptocurrency wallet
|
||||
# Copyright (C)2013-2022 The MMGen Project <mmgen@tuta.io>
|
||||
# Licensed under the GNU General Public License, Version 3:
|
||||
# https://www.gnu.org/licenses
|
||||
# Public project repositories:
|
||||
# https://github.com/mmgen/mmgen
|
||||
# https://gitlab.com/mmgen/mmgen
|
||||
|
||||
"""
|
||||
wallet.plainhex: plain hexadecimal wallet class
|
||||
"""
|
||||
|
||||
from ..util import msg,is_hex_str_lc
|
||||
from ..seed import Seed
|
||||
from .unenc import wallet
|
||||
|
||||
class wallet(wallet):
|
||||
|
||||
stdin_ok = True
|
||||
desc = 'plain hexadecimal seed data'
|
||||
|
||||
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(f'{d!r}: not a lowercase hexadecimal string, in {desc}')
|
||||
return False
|
||||
|
||||
if not len(d)*4 in Seed.lens:
|
||||
msg(f'Invalid data length ({len(d)}) in {desc}')
|
||||
return False
|
||||
|
||||
self.seed = Seed(bytes.fromhex(d))
|
||||
self.ssdata.hexseed = d
|
||||
|
||||
self.check_usr_seed_len()
|
||||
|
||||
return True
|
||||
68
mmgen/wallet/seed.py
Executable file
68
mmgen/wallet/seed.py
Executable file
|
|
@ -0,0 +1,68 @@
|
|||
#!/usr/bin/env python3
|
||||
#
|
||||
# mmgen = Multi-Mode GENerator, a command-line cryptocurrency wallet
|
||||
# Copyright (C)2013-2022 The MMGen Project <mmgen@tuta.io>
|
||||
# Licensed under the GNU General Public License, Version 3:
|
||||
# https://www.gnu.org/licenses
|
||||
# Public project repositories:
|
||||
# https://github.com/mmgen/mmgen
|
||||
# https://gitlab.com/mmgen/mmgen
|
||||
|
||||
"""
|
||||
wallet.seed: seed file wallet class
|
||||
"""
|
||||
|
||||
from ..util import msg,vmsg_r,make_chksum_6,split_into_cols,is_chksum_6,compare_chksums
|
||||
from ..baseconv import baseconv,is_b58_str
|
||||
from ..seed import Seed
|
||||
from .unenc import wallet
|
||||
|
||||
class wallet(wallet):
|
||||
|
||||
stdin_ok = True
|
||||
desc = 'seed data'
|
||||
|
||||
def _format(self):
|
||||
b58seed = baseconv('b58').frombytes(self.seed.data,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(f'Invalid data length ({len(ld)}) in {desc}')
|
||||
return False
|
||||
|
||||
a,b = ld[0],''.join(ld[1:])
|
||||
|
||||
if not is_chksum_6(a):
|
||||
msg(f'{a!r}: invalid checksum format in {desc}')
|
||||
return False
|
||||
|
||||
if not is_b58_str(b):
|
||||
msg(f'{b!r}: not a base 58 string, in {desc}')
|
||||
return False
|
||||
|
||||
vmsg_r(f'Validating {desc} checksum...')
|
||||
|
||||
if not compare_chksums(a,'file',make_chksum_6(b),'computed',verbose=True):
|
||||
return False
|
||||
|
||||
ret = baseconv('b58').tobytes(b,pad='seed')
|
||||
|
||||
if ret == False:
|
||||
msg(f'Invalid base-58 encoded seed: {val}')
|
||||
return False
|
||||
|
||||
self.seed = Seed(ret)
|
||||
self.ssdata.chksum = a
|
||||
self.ssdata.b58seed = b
|
||||
|
||||
self.check_usr_seed_len()
|
||||
|
||||
return True
|
||||
57
mmgen/wallet/unenc.py
Executable file
57
mmgen/wallet/unenc.py
Executable file
|
|
@ -0,0 +1,57 @@
|
|||
#!/usr/bin/env python3
|
||||
#
|
||||
# mmgen = Multi-Mode GENerator, a command-line cryptocurrency wallet
|
||||
# Copyright (C)2013-2022 The MMGen Project <mmgen@tuta.io>
|
||||
# Licensed under the GNU General Public License, Version 3:
|
||||
# https://www.gnu.org/licenses
|
||||
# Public project repositories:
|
||||
# https://github.com/mmgen/mmgen
|
||||
# https://gitlab.com/mmgen/mmgen
|
||||
|
||||
"""
|
||||
wallet.unenc: unencrypted wallet base class
|
||||
"""
|
||||
|
||||
from ..globalvars import g
|
||||
from ..color import blue,yellow
|
||||
from ..util import msg,msg_r,capfirst,is_int,keypress_confirm
|
||||
from .base import wallet
|
||||
|
||||
class wallet(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,ok_lens):
|
||||
|
||||
from ..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]
|
||||
|
||||
msg('{} {}'.format(
|
||||
blue(f'{capfirst(self.base_type or self.type)} type:'),
|
||||
yellow(self.mn_type)
|
||||
))
|
||||
|
||||
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
|
||||
21
mmgen/wallet/words.py
Executable file
21
mmgen/wallet/words.py
Executable file
|
|
@ -0,0 +1,21 @@
|
|||
#!/usr/bin/env python3
|
||||
#
|
||||
# mmgen = Multi-Mode GENerator, a command-line cryptocurrency wallet
|
||||
# Copyright (C)2013-2022 The MMGen Project <mmgen@tuta.io>
|
||||
# Licensed under the GNU General Public License, Version 3:
|
||||
# https://www.gnu.org/licenses
|
||||
# Public project repositories:
|
||||
# https://github.com/mmgen/mmgen
|
||||
# https://gitlab.com/mmgen/mmgen
|
||||
|
||||
"""
|
||||
wallet.words: MMGen mnemonic wallet class
|
||||
"""
|
||||
|
||||
from .mnemonic import wallet
|
||||
|
||||
class wallet(wallet):
|
||||
|
||||
desc = 'MMGen native mnemonic data'
|
||||
mn_type = 'MMGen native'
|
||||
wl_id = 'mmgen'
|
||||
|
|
@ -54,6 +54,7 @@ packages =
|
|||
mmgen.share
|
||||
mmgen.tool
|
||||
mmgen.tx
|
||||
mmgen.wallet
|
||||
|
||||
scripts =
|
||||
cmds/mmgen-addrgen
|
||||
|
|
|
|||
|
|
@ -51,7 +51,8 @@ def overlay_setup(repo_root):
|
|||
'mmgen.proto',
|
||||
'mmgen.share',
|
||||
'mmgen.tool',
|
||||
'mmgen.tx' ):
|
||||
'mmgen.tx',
|
||||
'mmgen.wallet' ):
|
||||
process_srcdir(d)
|
||||
|
||||
return overlay_dir
|
||||
|
|
|
|||
|
|
@ -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.wallet import Wallet
|
||||
from mmgen.wallet import get_wallet_cls
|
||||
|
||||
class TestSuiteInput(TestSuiteBase):
|
||||
'user input'
|
||||
|
|
@ -207,19 +207,19 @@ class TestSuiteInput(TestSuiteBase):
|
|||
return t
|
||||
|
||||
def _user_seed_entry(self,fmt,usr_rand=False,out_fmt=None,entry_mode='full',mn=None):
|
||||
wcls = Wallet.fmt_code_to_type(fmt)
|
||||
wcls = get_wallet_cls(fmt_code=fmt)
|
||||
wf = os.path.join(ref_dir,f'FE3C6545.{wcls.ext}')
|
||||
if wcls.wclass == 'mnemonic':
|
||||
if wcls.base_type == 'mnemonic':
|
||||
mn = mn or read_from_file(wf).strip().split()
|
||||
elif wcls.wclass == 'dieroll':
|
||||
elif wcls.type == 'dieroll':
|
||||
mn = mn or list(remove_whitespace(read_from_file(wf)))
|
||||
for idx,val in ((5,'x'),(18,'0'),(30,'7'),(44,'9')):
|
||||
mn.insert(idx,val)
|
||||
t = self.spawn('mmgen-walletconv',['-r10','-S','-i',fmt,'-o',out_fmt or fmt])
|
||||
t.expect(f'{capfirst(wcls.wclass)} type:.*{wcls.mn_type}',regex=True)
|
||||
t.expect(f'{capfirst(wcls.base_type or wcls.type)} type:.*{wcls.mn_type}',regex=True)
|
||||
t.expect(wcls.choose_seedlen_prompt,'1')
|
||||
t.expect('(Y/n): ','y')
|
||||
if wcls.wclass == 'mnemonic':
|
||||
if wcls.base_type == 'mnemonic':
|
||||
t.expect('Type a number.*: ','6',regex=True)
|
||||
t.expect('invalid')
|
||||
from mmgen.mn_entry import mn_entry
|
||||
|
|
@ -229,7 +229,7 @@ class TestSuiteInput(TestSuiteBase):
|
|||
mode = strip_ansi_escapes(t.p.match.group(1)).lower()
|
||||
assert mode == mne.em.name.lower(), f'{mode} != {mne.em.name.lower()}'
|
||||
stealth_mnemonic_entry(t,mne,mn,entry_mode=entry_mode)
|
||||
elif wcls.wclass == 'dieroll':
|
||||
elif wcls.type == 'dieroll':
|
||||
user_dieroll_entry(t,mn)
|
||||
if usr_rand:
|
||||
t.expect(wcls.user_entropy_prompt,'y')
|
||||
|
|
|
|||
|
|
@ -23,7 +23,9 @@ ts_main.py: Basic operations tests for the test.py test suite
|
|||
from mmgen.globalvars import g
|
||||
from mmgen.opts import opt
|
||||
from mmgen.fileutil import get_data_from_file,write_data_to_file
|
||||
from mmgen.wallet import Wallet,MMGenWallet,MMGenMnemonic,IncogWallet,MMGenSeedFile
|
||||
from mmgen.wallet import get_wallet_cls
|
||||
from mmgen.wallet.mmgen import wallet as MMGenWallet
|
||||
from mmgen.wallet.incog import wallet as IncogWallet
|
||||
from mmgen.rpc import rpc_init
|
||||
from ..include.common import *
|
||||
from .common import *
|
||||
|
|
@ -277,8 +279,8 @@ class TestSuiteMain(TestSuiteBase,TestSuiteShared):
|
|||
return t
|
||||
|
||||
def subwalletgen_mnemonic(self,wf):
|
||||
icls = Wallet.ext_to_type(get_extension(wf))
|
||||
ocls = MMGenMnemonic
|
||||
icls = get_wallet_cls(ext=get_extension(wf))
|
||||
ocls = get_wallet_cls('words')
|
||||
args = [self.usr_rand_arg,'-p1','-d',self.tr.trash_dir,'-o',ocls.fmt_codes[0],wf,'3L']
|
||||
t = self.spawn('mmgen-subwalletgen', args)
|
||||
t.license()
|
||||
|
|
@ -579,33 +581,34 @@ class TestSuiteMain(TestSuiteBase,TestSuiteShared):
|
|||
t.license()
|
||||
|
||||
if not pf:
|
||||
icls = Wallet.ext_to_type(get_extension(wf))
|
||||
icls = get_wallet_cls(ext=get_extension(wf))
|
||||
t.passphrase(icls.desc,self.wpasswd)
|
||||
|
||||
ocls = Wallet.fmt_code_to_type(out_fmt)
|
||||
ocls = get_wallet_cls(fmt_code=out_fmt)
|
||||
|
||||
if issubclass(ocls,WalletEnc) and ocls != Brainwallet:
|
||||
if ocls.enc and ocls.type != 'brain':
|
||||
t.passphrase_new('new '+ocls.desc,self.wpasswd)
|
||||
t.usr_rand(self.usr_rand_chars)
|
||||
|
||||
if ocls.__name__.startswith('Incog'):
|
||||
if ocls.type.startswith('incog'):
|
||||
m = 'Encrypting random data generated by your operating system with key'
|
||||
t.expect(m)
|
||||
t.expect(m)
|
||||
incog_id = t.expect_getend('New Incog Wallet ID: ')
|
||||
t.expect(m)
|
||||
|
||||
if ocls == IncogWalletHidden:
|
||||
if ocls.type == 'incog_hidden':
|
||||
self.write_to_tmpfile(incog_id_fn,incog_id)
|
||||
t.hincog_create(hincog_bytes)
|
||||
elif ocls == MMGenWallet:
|
||||
elif ocls.type == 'mmgen':
|
||||
t.label()
|
||||
|
||||
return t.written_to_file(capfirst(ocls.desc),oo=True),t
|
||||
|
||||
def export_seed(self,wf,out_fmt='seed',pf=None):
|
||||
f,t = self._walletconv_export(wf,out_fmt=out_fmt,pf=pf)
|
||||
silence()
|
||||
wcls = Wallet.fmt_code_to_type(out_fmt)
|
||||
wcls = get_wallet_cls(fmt_code=out_fmt)
|
||||
msg('==> {}: {}'.format(
|
||||
wcls.desc,
|
||||
cyan(get_data_from_file(f,wcls.desc)) ))
|
||||
|
|
@ -636,8 +639,8 @@ class TestSuiteMain(TestSuiteBase,TestSuiteShared):
|
|||
return self.export_incog(wf,out_fmt='hi',add_args=add_args)
|
||||
|
||||
def addrgen_seed(self,wf,foo,in_fmt='seed'):
|
||||
wcls = Wallet.fmt_code_to_type(in_fmt)
|
||||
stdout = wcls == MMGenSeedFile # capture output to screen once
|
||||
wcls = get_wallet_cls(fmt_code=in_fmt)
|
||||
stdout = wcls.type == 'seed' # capture output to screen once
|
||||
t = self.spawn(
|
||||
'mmgen-addrgen',
|
||||
(['-S'] if stdout else []) +
|
||||
|
|
@ -665,7 +668,7 @@ class TestSuiteMain(TestSuiteBase,TestSuiteShared):
|
|||
([],[wf])[bool(wf)] + [self.addr_idx_list])
|
||||
t.license()
|
||||
t.expect_getend('Incog Wallet ID: ')
|
||||
wcls = Wallet.fmt_code_to_type(in_fmt)
|
||||
wcls = get_wallet_cls(fmt_code=in_fmt)
|
||||
t.hash_preset(wcls.desc,'1')
|
||||
t.passphrase(rf'{wcls.desc} \w{{8}}',self.wpasswd)
|
||||
vmsg('Comparing generated checksum with checksum from address file')
|
||||
|
|
@ -707,7 +710,7 @@ class TestSuiteMain(TestSuiteBase,TestSuiteShared):
|
|||
t = self.spawn('mmgen-txsign', ['-d',self.tmpdir,txf1,wf1,txf2,wf2])
|
||||
t.license()
|
||||
for cnum,wf in (('1',wf1),('2',wf2)):
|
||||
wcls = Wallet.ext_to_type(get_extension(wf))
|
||||
wcls = get_wallet_cls(ext=get_extension(wf))
|
||||
t.view_tx('n')
|
||||
t.passphrase(wcls.desc,self.cfgs[cnum]['wpasswd'])
|
||||
self.txsign_end(t,cnum)
|
||||
|
|
@ -730,7 +733,7 @@ class TestSuiteMain(TestSuiteBase,TestSuiteShared):
|
|||
t.license()
|
||||
t.view_tx('n')
|
||||
for cnum,wf in (('1',wf1),('3',wf2)):
|
||||
wcls = Wallet.ext_to_type(get_extension(wf))
|
||||
wcls = get_wallet_cls(ext=get_extension(wf))
|
||||
t.passphrase(wcls.desc,self.cfgs[cnum]['wpasswd'])
|
||||
self.txsign_end(t)
|
||||
return t
|
||||
|
|
@ -743,7 +746,7 @@ class TestSuiteMain(TestSuiteBase,TestSuiteShared):
|
|||
bwf = joinpath(self.tmpdir,self.bw_filename)
|
||||
make_brainwallet_file(bwf)
|
||||
seed_len = str(self.seed_len)
|
||||
args = ['-d',self.tmpdir,'-p1',self.usr_rand_arg,'-l'+seed_len,'-ib']
|
||||
args = ['-d',self.tmpdir,'-p1',self.usr_rand_arg,'-l'+seed_len,'-ibw']
|
||||
t = self.spawn('mmgen-walletconv', args + [bwf])
|
||||
t.license()
|
||||
wcls = MMGenWallet
|
||||
|
|
@ -830,7 +833,7 @@ class TestSuiteMain(TestSuiteBase,TestSuiteShared):
|
|||
t = self.spawn('mmgen-txsign', add_args + ['-d',self.tmpdir,'-k',non_mm_file,txf,wf])
|
||||
t.license()
|
||||
t.view_tx('n')
|
||||
wcls = Wallet.ext_to_type(get_extension(wf))
|
||||
wcls = get_wallet_cls(ext=get_extension(wf))
|
||||
t.passphrase(wcls.desc,self.cfgs['20']['wpasswd'])
|
||||
if bad_vsize:
|
||||
t.expect('Estimated transaction vsize')
|
||||
|
|
|
|||
|
|
@ -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.wallet import MMGenMnemonic
|
||||
from mmgen.wallet import get_wallet_cls
|
||||
from ..include.common import *
|
||||
from .common import *
|
||||
|
||||
|
|
@ -163,7 +163,7 @@ class TestSuiteRef(TestSuiteBase,TestSuiteShared):
|
|||
|
||||
def ref_words_to_subwallet_chk(self,ss_idx):
|
||||
wf = dfl_words_file
|
||||
ocls = MMGenMnemonic
|
||||
ocls = get_wallet_cls('words')
|
||||
args = ['-d',self.tr.trash_dir,'-o',ocls.fmt_codes[-1],wf,ss_idx]
|
||||
|
||||
t = self.spawn('mmgen-subwalletgen',args,extra_desc='(generate subwallet)')
|
||||
|
|
|
|||
|
|
@ -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.wallet import *
|
||||
from mmgen.wallet import get_wallet_cls
|
||||
from ..include.common import *
|
||||
from .common import *
|
||||
from .ts_base import *
|
||||
|
|
@ -89,22 +89,22 @@ class TestSuiteRef3Seed(TestSuiteBase,TestSuiteShared):
|
|||
return self.walletchk(wf,pf=None,wcls=ss,sid=self.seed_id)
|
||||
|
||||
def ref_seed_chk(self):
|
||||
return self.ref_ss_chk(ss=MMGenSeedFile)
|
||||
return self.ref_ss_chk(ss=get_wallet_cls('seed'))
|
||||
|
||||
def ref_hex_chk(self):
|
||||
return self.ref_ss_chk(ss=MMGenHexSeedFile)
|
||||
return self.ref_ss_chk(ss=get_wallet_cls('mmhex'))
|
||||
|
||||
def ref_plainhex_chk(self):
|
||||
return self.ref_ss_chk(ss=PlainHexSeedFile)
|
||||
return self.ref_ss_chk(ss=get_wallet_cls('plainhex'))
|
||||
|
||||
def ref_dieroll_chk(self):
|
||||
return self.ref_ss_chk(ss=DieRollSeedFile)
|
||||
return self.ref_ss_chk(ss=get_wallet_cls('dieroll'))
|
||||
|
||||
def ref_mn_chk(self):
|
||||
return self.ref_ss_chk(ss=MMGenMnemonic)
|
||||
return self.ref_ss_chk(ss=get_wallet_cls('words'))
|
||||
|
||||
def ref_bip39_chk(self):
|
||||
return self.ref_ss_chk(ss=BIP39Mnemonic)
|
||||
return self.ref_ss_chk(ss=get_wallet_cls('bip39'))
|
||||
|
||||
def ref_hincog_chk(self,desc='hidden incognito data'):
|
||||
source = TestSuiteWalletConv.sources[str(self.seed_len)]
|
||||
|
|
@ -141,7 +141,7 @@ class TestSuiteRef3Seed(TestSuiteBase,TestSuiteShared):
|
|||
t = self.spawn('mmgen-walletconv', args + [self.usr_rand_arg])
|
||||
t.license()
|
||||
t.expect('Enter brainwallet: ', ref_wallet_brainpass+'\n')
|
||||
ocls = MMGenWallet
|
||||
ocls = get_wallet_cls('mmgen')
|
||||
t.passphrase_new('new '+ocls.desc,self.wpasswd)
|
||||
t.usr_rand(self.usr_rand_chars)
|
||||
fn = os.path.split(t.written_to_file(capfirst(ocls.desc)))[-1]
|
||||
|
|
@ -160,7 +160,7 @@ class TestSuiteRef3Seed(TestSuiteBase,TestSuiteShared):
|
|||
wf = self.get_file_with_ext('mmdat')
|
||||
pf = joinpath(self.tmpdir,pwfile)
|
||||
t = self.spawn('mmgen-walletconv',extra_args+['-d','test/trash','-o',ofmt,'-P'+pf,wf])
|
||||
wcls = Wallet.fmt_code_to_type(ofmt)
|
||||
wcls = get_wallet_cls(fmt_code=ofmt)
|
||||
fn = os.path.split(t.written_to_file(capfirst(wcls.desc)))[-1]
|
||||
idx = int(self.test_name[-1]) - 1
|
||||
sid = self.chk_data['sids'][idx]
|
||||
|
|
|
|||
|
|
@ -27,14 +27,15 @@ from mmgen.opts import opt
|
|||
from mmgen.util import die,gmsg
|
||||
from mmgen.protocol import init_proto
|
||||
from mmgen.addrlist import AddrList
|
||||
from mmgen.wallet import MMGenWallet
|
||||
from mmgen.wallet import Wallet,get_wallet_cls
|
||||
from ..include.common import *
|
||||
from .common import *
|
||||
|
||||
pat_date = r'\b\d\d-\d\d-\d\d\b'
|
||||
pat_date_time = r'\b\d\d\d\d-\d\d-\d\d\s+\d\d:\d\d\b'
|
||||
|
||||
dfl_wcls = MMGenWallet
|
||||
dfl_wcls = get_wallet_cls('mmgen')
|
||||
|
||||
rt_pw = 'abc-α'
|
||||
rt_data = {
|
||||
'tx_fee': {'btc':'0.0001','bch':'0.001','ltc':'0.01'},
|
||||
|
|
@ -338,7 +339,7 @@ class TestSuiteRegtest(TestSuiteBase,TestSuiteShared):
|
|||
return os.path.basename(get_file_with_ext(self._user_dir(user),'mmdat'))[:8]
|
||||
|
||||
def _get_user_subsid(self,user,subseed_idx):
|
||||
fn = get_file_with_ext(self._user_dir(user),MMGenWallet.ext)
|
||||
fn = get_file_with_ext(self._user_dir(user),dfl_wcls.ext)
|
||||
silence()
|
||||
w = Wallet( fn=fn, passwd_file=os.path.join(self.tmpdir,'wallet_password') )
|
||||
end_silence()
|
||||
|
|
|
|||
|
|
@ -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.wallet import Wallet,MMGenWallet,IncogWallet,IncogWalletHex,IncogWalletHidden,WalletEnc
|
||||
from mmgen.wallet import get_wallet_cls
|
||||
|
||||
from .ts_base import *
|
||||
|
||||
|
|
@ -30,7 +30,7 @@ ref_wf = 'test/ref/98831F3A.bip39'
|
|||
ref_sid = '98831F3A'
|
||||
wpasswd = 'abc'
|
||||
sh1_passwd = 'xyz'
|
||||
dfl_wcls = MMGenWallet
|
||||
dfl_wcls = get_wallet_cls('mmgen')
|
||||
|
||||
class TestSuiteSeedSplit(TestSuiteBase):
|
||||
'splitting and joining seeds'
|
||||
|
|
@ -111,11 +111,11 @@ class TestSuiteSeedSplit(TestSuiteBase):
|
|||
else:
|
||||
pat = f'master share #{master}'
|
||||
t.expect(pat,regex=True)
|
||||
ocls = Wallet.fmt_code_to_type(ofmt)
|
||||
if issubclass(ocls,WalletEnc):
|
||||
ocls = get_wallet_cls(fmt_code=ofmt)
|
||||
if ocls.enc:
|
||||
t.hash_preset('new '+ocls.desc,'1')
|
||||
t.passphrase_new('new '+ocls.desc,sh1_passwd)
|
||||
if ocls == IncogWalletHidden:
|
||||
if ocls.type == 'incog_hidden':
|
||||
t.hincog_create(1234)
|
||||
t.written_to_file(capfirst(ocls.desc))
|
||||
return t
|
||||
|
|
@ -134,23 +134,26 @@ class TestSuiteSeedSplit(TestSuiteBase):
|
|||
+ shares)
|
||||
if bad_invocation:
|
||||
return t
|
||||
icls = ( MMGenWallet if 'mmdat' in in_exts
|
||||
else IncogWallet if 'mmincog' in in_exts
|
||||
else IncogWalletHex if 'mmincox' in in_exts
|
||||
else IncogWalletHidden if '-H' in add_args
|
||||
icls = ( dfl_wcls if 'mmdat' in in_exts
|
||||
else get_wallet_cls('incog') if 'mmincog' in in_exts
|
||||
else get_wallet_cls('incog_hex') if 'mmincox' in in_exts
|
||||
else get_wallet_cls('incog_hidden') if '-H' in add_args
|
||||
else None )
|
||||
if icls in (IncogWallet,IncogWalletHex,IncogWalletHidden):
|
||||
if icls.type.startswith('incog'):
|
||||
t.hash_preset(icls.desc,'1')
|
||||
if icls:
|
||||
t.passphrase(icls.desc,sh1_passwd)
|
||||
if master:
|
||||
fs = "master share #{}, split id.*'{}'.*, share count {}"
|
||||
pat = fs.format(master,id_str or 'default',len(shares)+(icls==IncogWalletHidden))
|
||||
pat = fs.format(
|
||||
master,
|
||||
id_str or 'default',
|
||||
len(shares) + (icls.type=='incog_hidden') )
|
||||
t.expect(pat,regex=True)
|
||||
sid_cmp = strip_ansi_escapes(t.expect_getend('Joined Seed ID: '))
|
||||
cmp_or_die(sid,sid_cmp)
|
||||
ocls = Wallet.fmt_code_to_type(ofmt)
|
||||
if ocls == MMGenWallet:
|
||||
ocls = get_wallet_cls(fmt_code=ofmt)
|
||||
if ocls.type == 'mmgen':
|
||||
t.hash_preset('new '+ocls.desc,'1')
|
||||
t.passphrase_new('new '+ocls.desc,wpasswd)
|
||||
t.written_to_file(capfirst(ocls.desc))
|
||||
|
|
|
|||
|
|
@ -24,7 +24,8 @@ import os
|
|||
from mmgen.globalvars import g
|
||||
from mmgen.opts import opt
|
||||
from mmgen.util import ymsg
|
||||
from mmgen.wallet import Wallet,WalletEnc,Brainwallet,MMGenWallet,IncogWalletHidden
|
||||
from mmgen.wallet import get_wallet_cls
|
||||
|
||||
from ..include.common import *
|
||||
from .common import *
|
||||
|
||||
|
|
@ -167,8 +168,8 @@ class TestSuiteShared(object):
|
|||
t = self.spawn('mmgen-txsign', opts, extra_desc)
|
||||
t.license()
|
||||
t.view_tx(view)
|
||||
wcls = MMGenWallet if dfl_wallet else Wallet.ext_to_type(get_extension(wf))
|
||||
if issubclass(wcls,WalletEnc) and wcls != Brainwallet:
|
||||
wcls = get_wallet_cls( ext = 'mmdat' if dfl_wallet else get_extension(wf) )
|
||||
if wcls.enc and wcls.type != 'brain':
|
||||
t.passphrase(wcls.desc,self.wpasswd)
|
||||
if save:
|
||||
self.txsign_end(t,has_label=has_label)
|
||||
|
|
@ -185,15 +186,15 @@ class TestSuiteShared(object):
|
|||
|
||||
def walletchk(self,wf,pf,wcls=None,add_args=[],sid=None,extra_desc='',dfl_wallet=False):
|
||||
hp = self.hash_preset if hasattr(self,'hash_preset') else '1'
|
||||
wcls = wcls or Wallet.ext_to_type(get_extension(wf))
|
||||
wcls = wcls or get_wallet_cls(ext=get_extension(wf))
|
||||
t = self.spawn('mmgen-walletchk',
|
||||
([] if dfl_wallet else ['-i',wcls.fmt_codes[0]])
|
||||
+ add_args + ['-p',hp]
|
||||
+ ([wf] if wf else []),
|
||||
extra_desc=extra_desc)
|
||||
if wcls != IncogWalletHidden:
|
||||
if wcls.type != 'incog_hidden':
|
||||
t.expect(f"Getting {wcls.desc} from file '")
|
||||
if issubclass(wcls,WalletEnc) and wcls != Brainwallet:
|
||||
if wcls.enc and wcls.type != 'brain':
|
||||
t.passphrase(wcls.desc,self.wpasswd)
|
||||
t.expect(['Passphrase is OK', 'Passphrase.* are correct'],regex=True)
|
||||
chk = t.expect_getend(f'Valid {wcls.desc} for Seed ID ')[:8]
|
||||
|
|
@ -223,7 +224,7 @@ class TestSuiteShared(object):
|
|||
[getattr(self,f'{cmd_pfx}_idx_list')],
|
||||
extra_desc=f'({mmtype})' if mmtype in ('segwit','bech32') else '')
|
||||
t.license()
|
||||
wcls = MMGenWallet if dfl_wallet else Wallet.ext_to_type(get_extension(wf))
|
||||
wcls = get_wallet_cls( ext = 'mmdat' if dfl_wallet else get_extension(wf) )
|
||||
t.passphrase(wcls.desc,self.wpasswd)
|
||||
t.expect('Passphrase is OK')
|
||||
desc = ('address','password')[passgen]
|
||||
|
|
@ -245,7 +246,7 @@ class TestSuiteShared(object):
|
|||
([],['--type='+str(mmtype)])[bool(mmtype)] + args,
|
||||
extra_desc=f'({mmtype})' if mmtype in ('segwit','bech32') else '')
|
||||
t.license()
|
||||
wcls = Wallet.ext_to_type(get_extension(wf))
|
||||
wcls = get_wallet_cls(ext=get_extension(wf))
|
||||
t.passphrase(wcls.desc,self.wpasswd)
|
||||
chk = t.expect_getend(r'Checksum for key-address data .*?: ',regex=True)
|
||||
if check_ref:
|
||||
|
|
|
|||
|
|
@ -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.wallet import *
|
||||
from mmgen.wallet import get_wallet_cls
|
||||
from .common import *
|
||||
from .ts_base import *
|
||||
from .ts_shared import *
|
||||
|
|
@ -107,7 +107,7 @@ class TestSuiteWalletConv(TestSuiteBase,TestSuiteShared):
|
|||
|
||||
def ref_brain_conv(self):
|
||||
uopts = ['-i','bw','-p','1','-l',str(self.seed_len)]
|
||||
return self.walletconv_in(None,uopts,oo=True,icls=Brainwallet)
|
||||
return self.walletconv_in(None,uopts,oo=True,icls=get_wallet_cls('brain'))
|
||||
|
||||
def ref_incog_conv(self,wfk='ic_wallet',in_fmt='i'):
|
||||
uopts = ['-i',in_fmt,'-p','1','-l',str(self.seed_len)]
|
||||
|
|
@ -125,7 +125,7 @@ class TestSuiteWalletConv(TestSuiteBase,TestSuiteShared):
|
|||
None,
|
||||
uopts + hi_opt,
|
||||
oo = True,
|
||||
icls = IncogWalletHidden )
|
||||
icls = get_wallet_cls('incog_hidden') )
|
||||
|
||||
def ref_hincog_conv_old(self):
|
||||
return self.ref_hincog_conv(wfk='hic_wallet_old',add_uopts=['-O'])
|
||||
|
|
@ -173,16 +173,16 @@ class TestSuiteWalletConv(TestSuiteBase,TestSuiteShared):
|
|||
|
||||
# wallet conversion tests
|
||||
def walletconv_in(self,infile,uopts=[],oo=False,icls=None):
|
||||
ocls = MMGenMnemonic
|
||||
ocls = get_wallet_cls('words')
|
||||
opts = ['-d',self.tmpdir,'-o',ocls.fmt_codes[0],self.usr_rand_arg]
|
||||
if_arg = [infile] if infile else []
|
||||
d = '(convert)'
|
||||
t = self.spawn('mmgen-walletconv',opts+uopts+if_arg,extra_desc=d)
|
||||
t.license()
|
||||
icls = icls or Wallet.ext_to_type(get_extension(infile))
|
||||
if icls == Brainwallet:
|
||||
icls = icls or get_wallet_cls(ext=get_extension(infile))
|
||||
if icls.type == 'brain':
|
||||
t.expect('Enter brainwallet: ',ref_wallet_brainpass+'\n')
|
||||
if issubclass(icls,WalletEnc) and icls != Brainwallet:
|
||||
if icls.enc and icls.type != 'brain':
|
||||
t.passphrase(icls.desc,self.wpasswd)
|
||||
if self.test_name[:19] == 'ref_hincog_conv_old':
|
||||
t.expect('Is the Seed ID correct? (Y/n): ','\n')
|
||||
|
|
@ -198,27 +198,27 @@ class TestSuiteWalletConv(TestSuiteBase,TestSuiteShared):
|
|||
sid = self.seed_id )
|
||||
|
||||
def walletconv_out(self,out_fmt='w',uopts=[],uopts_chk=[]):
|
||||
wcls = Wallet.fmt_code_to_type(out_fmt)
|
||||
wcls = get_wallet_cls(fmt_code=out_fmt)
|
||||
opts = ['-d',self.tmpdir,'-p1','-o',out_fmt] + uopts
|
||||
infile = joinpath(ref_dir,self.seed_id+'.mmwords')
|
||||
t = self.spawn('mmgen-walletconv',[self.usr_rand_arg]+opts+[infile],extra_desc='(convert)')
|
||||
|
||||
add_args = [f'-l{self.seed_len}']
|
||||
t.license()
|
||||
if issubclass(wcls,WalletEnc) and wcls != Brainwallet:
|
||||
if wcls.enc and wcls.type != 'brain':
|
||||
t.passphrase_new('new '+wcls.desc,self.wpasswd)
|
||||
t.usr_rand(self.usr_rand_chars)
|
||||
if wcls in (IncogWallet,IncogWalletHex,IncogWalletHidden):
|
||||
if wcls.type.startswith('incog'):
|
||||
for i in (1,2,3):
|
||||
t.expect('Encrypting random data generated by your operating system with key')
|
||||
if wcls == IncogWalletHidden:
|
||||
if wcls.type == 'incog_hidden':
|
||||
t.hincog_create(hincog_bytes)
|
||||
if out_fmt == 'w':
|
||||
t.label()
|
||||
wf = t.written_to_file(capfirst(wcls.desc),oo=True)
|
||||
pf = None
|
||||
|
||||
if wcls == IncogWalletHidden:
|
||||
if wcls.type == 'incog_hidden':
|
||||
add_args += uopts_chk
|
||||
wf = None
|
||||
msg('' if opt.profile else ' OK')
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue