minor changes and cleanups

This commit is contained in:
The MMGen Project 2019-10-13 17:30:54 +00:00
commit 4d07d53ff6
Signed by: mmgen
GPG key ID: 3F8B1861E32B7DA2
9 changed files with 67 additions and 34 deletions

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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