minor changes and cleanups
This commit is contained in:
parent
c90bc087e0
commit
4d07d53ff6
9 changed files with 67 additions and 34 deletions
|
|
@ -6,7 +6,7 @@ from difflib import unified_diff
|
|||
|
||||
def pmsg(*args,out=sys.stderr):
|
||||
d = args if len(args) > 1 else '' if not args else args[0]
|
||||
out.write(pprint.PrettyPrinter(indent=4,compact=True).pformat(d) + '\n')
|
||||
out.write('\n' + pprint.PrettyPrinter(indent=4).pformat(d) + '\n')
|
||||
def pdie(*args,exit_val=1,out=sys.stderr):
|
||||
pmsg(*args,out=out)
|
||||
sys.exit(exit_val)
|
||||
|
|
|
|||
|
|
@ -24,10 +24,12 @@ mmgen.exception: Exception classes for the MMGen suite
|
|||
class UserNonConfirmation(Exception): mmcode = 1
|
||||
class BadAgeFormat(Exception): mmcode = 1
|
||||
class BadFilename(Exception): mmcode = 1
|
||||
class BadFileExtension(Exception): mmcode = 1
|
||||
class SocketError(Exception): mmcode = 1
|
||||
class UserAddressNotInWallet(Exception): mmcode = 1
|
||||
class MnemonicError(Exception): mmcode = 1
|
||||
class RangeError(Exception): mmcode = 1
|
||||
class FileNotFound(Exception): mmcode = 1
|
||||
|
||||
# 2: yellow hl, message only
|
||||
class InvalidTokenAddress(Exception): mmcode = 2
|
||||
|
|
|
|||
|
|
@ -19,7 +19,10 @@
|
|||
"""
|
||||
filename.py: Filename class and methods for the MMGen suite
|
||||
"""
|
||||
|
||||
import sys,os
|
||||
|
||||
from mmgen.exception import BadFileExtension,FileNotFound
|
||||
from mmgen.obj import *
|
||||
from mmgen.util import die,get_extension
|
||||
from mmgen.seed import *
|
||||
|
|
@ -51,11 +54,16 @@ class Filename(MMGenObject):
|
|||
# TODO: other file types
|
||||
self.ftype = SeedSource.ext_to_type(self.ext)
|
||||
if not self.ftype:
|
||||
die(3,"'{}': not a recognized extension for SeedSource".format(self.ext))
|
||||
m = "'{}': not a recognized SeedSource file extension".format(self.ext)
|
||||
raise BadFileExtension(m)
|
||||
|
||||
try:
|
||||
st = os.stat(fn)
|
||||
except:
|
||||
raise FileNotFound('{!r}: file not found'.format(fn))
|
||||
|
||||
import stat
|
||||
if stat.S_ISBLK(os.stat(fn).st_mode):
|
||||
if stat.S_ISBLK(st.st_mode):
|
||||
mode = (os.O_RDONLY,os.O_RDWR)[bool(write)]
|
||||
if g.platform == 'win': mode |= os.O_BINARY
|
||||
try:
|
||||
|
|
@ -68,10 +76,10 @@ class Filename(MMGenObject):
|
|||
self.size = os.lseek(fd, 0, os.SEEK_END)
|
||||
os.close(fd)
|
||||
else:
|
||||
self.size = os.stat(fn).st_size
|
||||
self.mtime = os.stat(fn).st_mtime
|
||||
self.ctime = os.stat(fn).st_ctime
|
||||
self.atime = os.stat(fn).st_atime
|
||||
self.size = st.st_size
|
||||
self.mtime = st.st_mtime
|
||||
self.ctime = st.st_ctime
|
||||
self.atime = st.st_atime
|
||||
|
||||
class MMGenFileList(list,MMGenObject):
|
||||
|
||||
|
|
|
|||
|
|
@ -66,6 +66,8 @@ elif invoked_as == 'subgen':
|
|||
desc = 'Generate a subwallet from ' + dsw
|
||||
opt_filter = 'dehHiJkKlLmoOpPqrSvz-' # omitted: f
|
||||
usage = '[opts] [infile] <Subseed Index>'
|
||||
iaction = 'input'
|
||||
oaction = 'output'
|
||||
do_sw_note = True
|
||||
|
||||
opts_data = {
|
||||
|
|
@ -136,16 +138,24 @@ if invoked_as == 'subgen':
|
|||
from mmgen.obj import SubSeedIdx
|
||||
ss_idx = SubSeedIdx(cmd_args.pop())
|
||||
|
||||
if cmd_args:
|
||||
if invoked_as == 'gen' or len(cmd_args) > 1:
|
||||
opts.usage()
|
||||
check_infile(cmd_args[0])
|
||||
|
||||
sf = get_seed_file(cmd_args,nargs,invoked_as=invoked_as)
|
||||
|
||||
if not invoked_as == 'chk': do_license_msg()
|
||||
if invoked_as != 'chk':
|
||||
do_license_msg()
|
||||
|
||||
if invoked_as in ('conv','passchg','subgen'):
|
||||
m1 = green('Processing input wallet')
|
||||
m2 = yellow(' (default wallet)') if sf and os.path.dirname(sf) == g.data_dir else ''
|
||||
msg(m1+m2)
|
||||
|
||||
ss_in = None if invoked_as == 'gen' else SeedSource(sf,passchg=(invoked_as=='passchg'))
|
||||
if invoked_as == 'gen':
|
||||
ss_in = None
|
||||
else:
|
||||
ss_in = SeedSource(sf,passchg=(invoked_as=='passchg'))
|
||||
m1 = green('Processing input wallet ')
|
||||
m2 = ss_in.seed.sid.hl()
|
||||
m3 = yellow(' (default wallet)') if sf and os.path.dirname(sf) == g.data_dir else ''
|
||||
msg(m1+m2+m3)
|
||||
|
||||
if invoked_as == 'chk':
|
||||
lbl = ss_in.ssdata.label.hl() if hasattr(ss_in.ssdata,'label') else 'NONE'
|
||||
|
|
@ -153,7 +163,7 @@ if invoked_as == 'chk':
|
|||
# TODO: display creation date
|
||||
sys.exit(0)
|
||||
|
||||
if invoked_as in ('conv','passchg','subgen'):
|
||||
if invoked_as != 'gen':
|
||||
gmsg('Processing output wallet')
|
||||
|
||||
if invoked_as == 'subgen':
|
||||
|
|
|
|||
16
mmgen/obj.py
16
mmgen/obj.py
|
|
@ -169,11 +169,15 @@ class Hilite(object):
|
|||
return self.fmtc(self,*args,**kwargs)
|
||||
|
||||
@classmethod
|
||||
def hlc(cls,s,color=True):
|
||||
def hlc(cls,s,color=True,encl=''):
|
||||
if encl:
|
||||
assert isinstance(encl,str) and len(encl) == 2, "'encl' must be 2-character str"
|
||||
s = encl[0] + s + encl[1]
|
||||
return cls.colorize(s,color=color)
|
||||
|
||||
def hl(self,color=True):
|
||||
return self.colorize(self,color=color)
|
||||
def hl(self,*args,**kwargs):
|
||||
assert args == () # forbid invocation w/o keywords
|
||||
return self.hlc(self,*args,**kwargs)
|
||||
|
||||
def __str__(self):
|
||||
return self.colorize(self,color=False)
|
||||
|
|
@ -816,9 +820,11 @@ class MMGenPWIDString(MMGenLabel):
|
|||
min_len = 1
|
||||
desc = 'password ID string'
|
||||
forbidden = list(' :/\\')
|
||||
trunc_ok = False
|
||||
|
||||
class SeedShareIDString(MMGenPWIDString):
|
||||
desc = 'seed share ID string'
|
||||
|
||||
class SeedSplitIDString(MMGenPWIDString):
|
||||
desc = 'seed split ID string'
|
||||
|
||||
class MMGenAddrType(str,Hilite,InitErrors,MMGenObject):
|
||||
width = 1
|
||||
|
|
|
|||
|
|
@ -50,8 +50,8 @@ def is_mmgen_mnemonic(s): return _is_mnemonic(s,fmt='words')
|
|||
|
||||
class SeedBase(MMGenObject):
|
||||
|
||||
data = MMGenImmutableAttr('data',bytes,typeconv=False)
|
||||
sid = MMGenImmutableAttr('sid',SeedID,typeconv=False)
|
||||
data = MMGenImmutableAttr('data',bytes,typeconv=False)
|
||||
sid = MMGenImmutableAttr('sid',SeedID,typeconv=False)
|
||||
|
||||
def __init__(self,seed_bin=None):
|
||||
if not seed_bin:
|
||||
|
|
@ -60,8 +60,8 @@ class SeedBase(MMGenObject):
|
|||
elif len(seed_bin)*8 not in g.seed_lens:
|
||||
die(3,'{}: invalid seed length'.format(len(seed_bin)))
|
||||
|
||||
self.data = seed_bin
|
||||
self.sid = SeedID(seed=self)
|
||||
self.data = seed_bin
|
||||
self.sid = SeedID(seed=self)
|
||||
|
||||
@property
|
||||
def bitlen(self):
|
||||
|
|
@ -176,11 +176,12 @@ class SubSeedList(MMGenObject):
|
|||
def add_subseed(idx,length):
|
||||
for nonce in range(self.nonce_start,self.member_type.max_nonce+1): # handle SeedID collisions
|
||||
sid = make_chksum_8(self.member_type.make_subseed_bin(self,idx,nonce,length))
|
||||
if not (sid in self.data['long'] or sid in self.data['short'] or sid == self.parent_seed.sid):
|
||||
if sid in self.data['long'] or sid in self.data['short'] or sid == self.parent_seed.sid:
|
||||
if g.debug_subseed: # should get ≈450 collisions for first 1,000,000 subseeds
|
||||
self._collision_debug_msg(sid,idx,nonce)
|
||||
else:
|
||||
self.data[length][sid] = (idx,nonce)
|
||||
return last_sid == sid
|
||||
elif g.debug_subseed: # should get ≈450 collisions for first 1,000,000 subseeds
|
||||
self._collision_debug_msg(sid,idx,nonce)
|
||||
else: # must exit here, as this could leave self.data in inconsistent state
|
||||
raise SubSeedNonceRangeExceeded('add_subseed(): nonce range exceeded')
|
||||
|
||||
|
|
@ -279,7 +280,7 @@ class SeedShareList(SubSeedList):
|
|||
split_type = 'N-of-N'
|
||||
|
||||
count = MMGenImmutableAttr('count',SeedShareCount)
|
||||
id_str = MMGenImmutableAttr('id_str',SeedShareIDString)
|
||||
id_str = MMGenImmutableAttr('id_str',SeedSplitIDString)
|
||||
|
||||
def __init__(self,parent_seed,count,id_str=None,master_idx=None):
|
||||
self.member_type = SeedShare
|
||||
|
|
@ -307,12 +308,12 @@ class SeedShareList(SubSeedList):
|
|||
self.data['long'][self.master_share.sid] = (1,self.master_share.nonce)
|
||||
self._generate(count-1)
|
||||
self.last_share = ls = SeedShareLast(self)
|
||||
if ls.sid in self.data['long'].keys + [parent_seed.sid]:
|
||||
if ls.sid in self.data['long'] or ls.sid == parent_seed.sid:
|
||||
# collision: throw out entire split list and redo with new start nonce
|
||||
if g.debug_subseed:
|
||||
self._collision_debug_msg(ls.sid,count,nonce,nonce_desc='nonce_start')
|
||||
else:
|
||||
self.data['long'][ls.sid] = (self.count,nonce)
|
||||
self.data['long'][ls.sid] = (count,nonce)
|
||||
break
|
||||
else:
|
||||
raise SubSeedNonceRangeExceeded('nonce range exceeded')
|
||||
|
|
@ -419,6 +420,8 @@ class SeedShareMaster(SeedBase):
|
|||
scramble_key = b'master_share:' + self.idx.to_bytes(2,'big') + self.nonce.to_bytes(2,'big')
|
||||
return scramble_seed(seed.data,scramble_key)[:seed.byte_len]
|
||||
|
||||
# Don't bother with avoiding seed ID collision here, as sid of derived seed is not used
|
||||
# by user as an identifier
|
||||
def make_derived_seed_bin(self,id_str,count):
|
||||
# field maximums: id_str: none (256 chars), count: 65535 (1024)
|
||||
scramble_key = id_str.encode() + b':' + count.to_bytes(2,'big')
|
||||
|
|
@ -426,7 +429,7 @@ class SeedShareMaster(SeedBase):
|
|||
|
||||
class SeedShareMasterJoining(SeedShareMaster):
|
||||
|
||||
id_str = MMGenImmutableAttr('id_str',SeedShareIDString)
|
||||
id_str = MMGenImmutableAttr('id_str',SeedSplitIDString)
|
||||
count = MMGenImmutableAttr('count',SeedShareCount)
|
||||
|
||||
def __init__(self,idx,base_seed,id_str,count):
|
||||
|
|
|
|||
|
|
@ -503,7 +503,7 @@ def check_file_type_and_access(fname,ftype,blkdev_ok=False):
|
|||
|
||||
try: mode = os.stat(fname).st_mode
|
||||
except:
|
||||
die(1,"Unable to stat requested {} '{}'".format(ftype,fname))
|
||||
raise FileNotFound("Requested {} '{}' not found".format(ftype,fname))
|
||||
|
||||
for t in ok_types:
|
||||
if t[0](mode): break
|
||||
|
|
@ -521,6 +521,10 @@ def check_outfile(f,blkdev_ok=False):
|
|||
return check_file_type_and_access(f,'output file',blkdev_ok=blkdev_ok)
|
||||
def check_outdir(f):
|
||||
return check_file_type_and_access(f,'output directory')
|
||||
def check_wallet_extension(fn):
|
||||
from mmgen.seed import SeedSource
|
||||
if not SeedSource.ext_to_type(get_extension(fn)):
|
||||
raise BadFileExtension("'{}': unrecognized seed source file extension".format(fn))
|
||||
def make_full_path(outdir,outfile):
|
||||
return os.path.normpath(os.path.join(outdir, os.path.basename(outfile)))
|
||||
|
||||
|
|
|
|||
|
|
@ -119,7 +119,7 @@ def do_loop():
|
|||
network = ('mainnet','testnet')[bool(g.testnet)]
|
||||
gl = globals()
|
||||
exec('from test.objtest_py_d.ot_{}_{} import tests'.format(g.coin.lower(),network),gl,gl)
|
||||
gmsg('Running data objest tests for {} {}'.format(g.coin,network))
|
||||
gmsg('Running data object tests for {} {}'.format(g.coin,network))
|
||||
clr = None
|
||||
for test in tests:
|
||||
if utests and test not in utests: continue
|
||||
|
|
|
|||
|
|
@ -155,7 +155,7 @@ class unit_test(object):
|
|||
|
||||
msg_r('Testing Seed ID collisions ({} subseed pairs)...'.format(ss_count))
|
||||
|
||||
seed_bin = bytes.fromhex('12abcdef' * 8)
|
||||
seed_bin = bytes.fromhex('12abcdef' * 8) # 95B3D78D
|
||||
seed = Seed(seed_bin)
|
||||
|
||||
seed.subseeds._generate(ss_count)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue