new class: SubSeedList
This commit is contained in:
parent
1ebd4ce18b
commit
2952797621
3 changed files with 105 additions and 77 deletions
121
mmgen/seed.py
121
mmgen/seed.py
|
|
@ -64,71 +64,82 @@ class SeedBase(MMGenObject):
|
|||
self.sid = SeedID(seed=self)
|
||||
self.length = len(seed_bin) * 8
|
||||
|
||||
class Seed(SeedBase):
|
||||
class SubSeedList(MMGenObject):
|
||||
|
||||
def __init__(self,seed_bin=None):
|
||||
def __init__(self,parent_seed,have_short=False):
|
||||
self.parent_seed = parent_seed
|
||||
self.have_short = have_short
|
||||
from collections import OrderedDict
|
||||
self.subseeds = { 'long': OrderedDict(), 'short': OrderedDict() }
|
||||
SeedBase.__init__(self,seed_bin=seed_bin)
|
||||
self.data = { 'long': OrderedDict(), 'short': OrderedDict() }
|
||||
|
||||
def subseed(self,ss_idx_in,print_msg=False):
|
||||
def __len__(self):
|
||||
return len(self.data['long'])
|
||||
|
||||
def get_params_by_ss_idx(self,ss_idx):
|
||||
sid = list(self.data[ss_idx.type].keys())[ss_idx.idx-1]
|
||||
idx,nonce = self.data[ss_idx.type][sid]
|
||||
return (sid,idx,nonce)
|
||||
|
||||
def get_subseed_by_ss_idx(self,ss_idx_in,print_msg=False):
|
||||
ss_idx = SubSeedIdx(ss_idx_in)
|
||||
if print_msg:
|
||||
msg_r('{} {} of {}...'.format(
|
||||
green('Generating subseed'),
|
||||
ss_idx.hl(),
|
||||
self.sid.hl(),
|
||||
self.parent_seed.sid.hl(),
|
||||
))
|
||||
if ss_idx.idx > len(self.subseeds['long']):
|
||||
self.gen_subseeds(ss_idx.idx)
|
||||
sid = list(self.subseeds[ss_idx.type].keys())[ss_idx.idx-1]
|
||||
idx,nonce = self.subseeds[ss_idx.type][sid]
|
||||
|
||||
if ss_idx.idx > len(self):
|
||||
self.generate(ss_idx.idx)
|
||||
|
||||
sid,idx,nonce = self.get_params_by_ss_idx(ss_idx)
|
||||
|
||||
if print_msg:
|
||||
msg('\b\b\b => {}'.format(SeedID.hlc(sid)))
|
||||
assert idx == ss_idx.idx, "{} != {}: subseed list idx does not match subseed idx!".format(idx,ss_idx.idx)
|
||||
return SubSeed(self,idx,nonce,length=ss_idx.type)
|
||||
return SubSeed(self.parent_seed,idx,nonce,length=ss_idx.type)
|
||||
|
||||
def existing_subseed_by_seed_id(self,sid):
|
||||
for k in ('long','short'):
|
||||
if sid in self.subseeds[k]:
|
||||
idx,nonce = self.subseeds[k][sid]
|
||||
return SubSeed(self,idx,nonce,length=k)
|
||||
def get_existing_subseed_by_seed_id(self,sid):
|
||||
for k in ('long','short') if self.have_short else ('long',):
|
||||
if sid in self.data[k]:
|
||||
idx,nonce = self.data[k][sid]
|
||||
return SubSeed(self.parent_seed,idx,nonce,length=k)
|
||||
|
||||
def subseed_by_seed_id(self,sid,last_idx=None,print_msg=False):
|
||||
def get_subseed_by_seed_id(self,sid,last_idx=None,print_msg=False):
|
||||
|
||||
def do_msg(seed):
|
||||
def do_msg(subseed):
|
||||
if print_msg:
|
||||
qmsg('{} {} ({}:{})'.format(
|
||||
green('Found subseed'),
|
||||
seed.sid.hl(),
|
||||
self.sid.hl(),
|
||||
seed.ss_idx.hl(),
|
||||
subseed.sid.hl(),
|
||||
self.parent_seed.sid.hl(),
|
||||
subseed.ss_idx.hl(),
|
||||
))
|
||||
|
||||
if last_idx == None:
|
||||
last_idx = g.subseeds
|
||||
|
||||
seed = self.existing_subseed_by_seed_id(sid)
|
||||
if seed:
|
||||
do_msg(seed)
|
||||
return seed
|
||||
subseed = self.get_existing_subseed_by_seed_id(sid)
|
||||
if subseed:
|
||||
do_msg(subseed)
|
||||
return subseed
|
||||
|
||||
if len(self.subseeds['long']) >= last_idx:
|
||||
if len(self) >= last_idx:
|
||||
return None
|
||||
|
||||
self.gen_subseeds(last_idx,last_sid=sid)
|
||||
self.generate(last_idx,last_sid=sid)
|
||||
|
||||
seed = self.existing_subseed_by_seed_id(sid)
|
||||
if seed:
|
||||
do_msg(seed)
|
||||
return seed
|
||||
subseed = self.get_existing_subseed_by_seed_id(sid)
|
||||
if subseed:
|
||||
do_msg(subseed)
|
||||
return subseed
|
||||
|
||||
def gen_subseeds(self,last_idx=None,last_sid=None):
|
||||
def generate(self,last_idx=None,last_sid=None):
|
||||
|
||||
if last_idx == None:
|
||||
last_idx = g.subseeds
|
||||
|
||||
first_idx = len(self.subseeds['long']) + 1
|
||||
first_idx = len(self) + 1
|
||||
|
||||
if first_idx > last_idx:
|
||||
return None
|
||||
|
|
@ -138,45 +149,59 @@ class Seed(SeedBase):
|
|||
|
||||
def add_subseed(idx,length):
|
||||
for nonce in range(SubSeed.max_nonce): # use nonce to handle Seed ID collisions
|
||||
sid = make_chksum_8(SubSeedBase.make_subseed_bin(self,idx,nonce,length))
|
||||
if not (sid in self.subseeds['long'] or sid in self.subseeds['short'] or sid == self.sid):
|
||||
self.subseeds[length][sid] = (idx,nonce)
|
||||
sid = make_chksum_8(SubSeedBase.make_subseed_bin(self.parent_seed,idx,nonce,length))
|
||||
if not (sid in self.data['long'] or sid in self.data['short'] or sid == self.parent_seed.sid):
|
||||
self.data[length][sid] = (idx,nonce)
|
||||
return last_sid == sid
|
||||
elif g.debug_subseed: # should get ≈450 collisions for first 1,000,000 subseeds
|
||||
k = ('long','short')[sid in self.subseeds['short']]
|
||||
k = ('long','short')[sid in self.data['short']]
|
||||
m1 = 'add_subseed(idx={},{}):'.format(idx,length)
|
||||
if sid == self.sid:
|
||||
if sid == self.parent_seed.sid:
|
||||
m2 = 'collision with parent Seed ID {},'.format(sid)
|
||||
else:
|
||||
m2 = 'collision with ID {} (idx={},{}),'.format(sid,self.subseeds[k][sid][0],k)
|
||||
m2 = 'collision with ID {} (idx={},{}),'.format(sid,self.data[k][sid][0],k)
|
||||
msg('{:30} {:46} incrementing nonce to {}'.format(m1,m2,nonce+1))
|
||||
else: # must exit here, as this could leave self.subseeds in inconsistent state
|
||||
else: # must exit here, as this could leave self.data in inconsistent state
|
||||
raise SubSeedNonceRangeExceeded('add_subseed(): nonce range exceeded')
|
||||
|
||||
for idx in SubSeedIdxRange(first_idx,last_idx).iterate():
|
||||
if add_subseed(idx,'long') + add_subseed(idx,'short'):
|
||||
break
|
||||
match1 = add_subseed(idx,'long')
|
||||
match2 = add_subseed(idx,'short') if self.have_short else False
|
||||
if match1 or match2: break
|
||||
|
||||
def fmt_subseeds(self,first_idx,last_idx):
|
||||
def format(self,first_idx,last_idx):
|
||||
|
||||
r = SubSeedIdxRange(first_idx,last_idx)
|
||||
|
||||
if len(self.subseeds['long']) < last_idx:
|
||||
self.gen_subseeds(last_idx)
|
||||
if len(self) < last_idx:
|
||||
self.generate(last_idx)
|
||||
|
||||
fs1 = '{:>18} {:>18}\n'
|
||||
fs2 = '{i:>7}L: {:8} {i:>7}S: {:8}\n'
|
||||
|
||||
hdr = '{:>16} {} ({} bits)\n\n'.format('Parent Seed:',self.sid.hl(),self.length)
|
||||
hdr = '{:>16} {} ({} bits)\n\n'.format('Parent Seed:',self.parent_seed.sid.hl(),self.parent_seed.length)
|
||||
hdr += fs1.format('Long Subseeds','Short Subseeds')
|
||||
hdr += fs1.format('-------------','--------------')
|
||||
|
||||
sl = tuple(self.subseeds['long'])
|
||||
ss = tuple(self.subseeds['short'])
|
||||
sl = tuple(self.data['long'])
|
||||
ss = tuple(self.data['short'])
|
||||
body = (fs2.format(sl[n-1],ss[n-1],i=n) for n in r.iterate())
|
||||
|
||||
return hdr + ''.join(body)
|
||||
|
||||
class Seed(SeedBase):
|
||||
|
||||
def __init__(self,seed_bin=None):
|
||||
self.subseeds = SubSeedList(self,have_short=True)
|
||||
SeedBase.__init__(self,seed_bin=seed_bin)
|
||||
|
||||
def subseed(self,ss_idx_in,print_msg=False):
|
||||
return self.subseeds.get_subseed_by_ss_idx(ss_idx_in,print_msg=print_msg)
|
||||
|
||||
def subseed_by_seed_id(self,sid,last_idx=None,print_msg=False):
|
||||
return self.subseeds.get_subseed_by_seed_id(sid,last_idx=last_idx,print_msg=print_msg)
|
||||
|
||||
|
||||
class SubSeedBase(MMGenObject):
|
||||
|
||||
max_nonce = 1000
|
||||
|
|
|
|||
|
|
@ -697,7 +697,7 @@ class MMGenToolCmdWallet(MMGenToolCmdBase):
|
|||
opt.quiet = True
|
||||
sf = get_seed_file([wallet] if wallet else [],1)
|
||||
from mmgen.seed import SeedSource
|
||||
return SeedSource(sf).seed.fmt_subseeds(*SubSeedIdxRange(subseed_idx_range))
|
||||
return SeedSource(sf).seed.subseeds.format(*SubSeedIdxRange(subseed_idx_range))
|
||||
|
||||
def gen_key(self,mmgen_addr:str,wallet=''):
|
||||
"generate a single MMGen WIF key from default or specified wallet"
|
||||
|
|
|
|||
|
|
@ -36,27 +36,26 @@ class subseed(object):
|
|||
assert subseed.ss_idx == h, subseed.ss_idx
|
||||
|
||||
seed2 = Seed(seed_bin)
|
||||
s2s = seed2.subseeds['short']
|
||||
s2l = seed2.subseeds['long']
|
||||
ss2_list = seed2.subseeds
|
||||
|
||||
seed2.gen_subseeds(1)
|
||||
assert len(s2s) == 1, len(s2s)
|
||||
seed2.subseeds.generate(1)
|
||||
assert len(ss2_list) == 1, len(ss2_list)
|
||||
|
||||
seed2.gen_subseeds(1) # do nothing
|
||||
seed2.gen_subseeds(2) # append one item
|
||||
seed2.subseeds.generate(1) # do nothing
|
||||
seed2.subseeds.generate(2) # append one item
|
||||
|
||||
seed2.gen_subseeds(5)
|
||||
assert len(s2s) == 5, len(s2s)
|
||||
seed2.subseeds.generate(5)
|
||||
assert len(ss2_list) == 5, len(ss2_list)
|
||||
|
||||
seed2.gen_subseeds(3) # do nothing
|
||||
assert len(s2l) == 5, len(s2l)
|
||||
seed2.subseeds.generate(3) # do nothing
|
||||
assert len(ss2_list) == 5, len(ss2_list)
|
||||
|
||||
seed2.gen_subseeds(10)
|
||||
assert len(s2s) == 10, len(s2s)
|
||||
seed2.subseeds.generate(10)
|
||||
assert len(ss2_list) == 10, len(ss2_list)
|
||||
|
||||
assert seed.pformat() == seed2.pformat()
|
||||
# assert seed.pformat() == seed2.pformat() # TODO: deal with recursion in pformat()
|
||||
|
||||
s = seed.fmt_subseeds(1,g.subseeds)
|
||||
s = seed.subseeds.format(1,g.subseeds)
|
||||
s_lines = s.strip().split('\n')
|
||||
assert len(s_lines) == g.subseeds + 4, s
|
||||
|
||||
|
|
@ -71,7 +70,7 @@ class subseed(object):
|
|||
b = [e for e in s_lines if ' 5S:' in e][0].strip().split()[3]
|
||||
assert a == b, b
|
||||
|
||||
s = seed.fmt_subseeds(g.subseeds+1,g.subseeds+2)
|
||||
s = seed.subseeds.format(g.subseeds+1,g.subseeds+2)
|
||||
s_lines = s.strip().split('\n')
|
||||
assert len(s_lines) == 6, s
|
||||
|
||||
|
|
@ -80,9 +79,11 @@ class subseed(object):
|
|||
b = [e for e in s_lines if ' {}:'.format(ss_idx) in e][0].strip().split()[3]
|
||||
assert a == b, b
|
||||
|
||||
s = seed.fmt_subseeds(1,2)
|
||||
s = seed.subseeds.format(1,10)
|
||||
s_lines = s.strip().split('\n')
|
||||
assert len(s_lines) == 6, s
|
||||
assert len(s_lines) == 14, s
|
||||
|
||||
vmsg_r('\n{}'.format(s))
|
||||
|
||||
msg('OK')
|
||||
|
||||
|
|
@ -91,24 +92,26 @@ class subseed(object):
|
|||
|
||||
seed_bin = bytes.fromhex('deadbeef' * 8)
|
||||
seed = Seed(seed_bin)
|
||||
seed.gen_subseeds()
|
||||
seed.subseeds.generate()
|
||||
ss = seed.subseeds
|
||||
assert len(ss['short']) == g.subseeds, ss['short']
|
||||
assert len(ss['long']) == g.subseeds, ss['long']
|
||||
assert len(ss.data['long']) == len(ss.data['short']), len(ss.data['short'])
|
||||
assert len(ss) == g.subseeds, len(ss)
|
||||
|
||||
seed = Seed(seed_bin)
|
||||
seed.subseed_by_seed_id('EEEEEEEE')
|
||||
ss = seed.subseeds
|
||||
assert len(ss['short']) == g.subseeds, ss['short']
|
||||
assert len(ss['long']) == g.subseeds, ss['long']
|
||||
assert len(ss.data['long']) == len(ss.data['short']), len(ss.data['short'])
|
||||
assert len(ss) == g.subseeds, len(ss)
|
||||
|
||||
seed = Seed(seed_bin)
|
||||
subseed = seed.subseed_by_seed_id('803B165C')
|
||||
assert len(ss.data['long']) == len(ss.data['short']), len(ss.data['short'])
|
||||
assert subseed.sid == '803B165C', subseed.sid
|
||||
assert subseed.idx == 3, subseed.idx
|
||||
|
||||
seed = Seed(seed_bin)
|
||||
subseed = seed.subseed_by_seed_id('803B165C',last_idx=1)
|
||||
assert len(ss.data['long']) == len(ss.data['short']), len(ss.data['short'])
|
||||
assert subseed == None, subseed
|
||||
|
||||
r = SubSeedIdxRange('1-5')
|
||||
|
|
@ -155,22 +158,22 @@ class subseed(object):
|
|||
seed_bin = bytes.fromhex('12abcdef' * 8)
|
||||
seed = Seed(seed_bin)
|
||||
|
||||
seed.gen_subseeds(ss_count)
|
||||
seed.subseeds.generate(ss_count)
|
||||
ss = seed.subseeds
|
||||
|
||||
assert seed.subseed(last_idx).sid == last_sid, seed.subseed(last_idx).sid
|
||||
|
||||
for sid in ss['long']:
|
||||
for sid in ss.data['long']:
|
||||
# msg(sid)
|
||||
assert sid not in ss['short']
|
||||
assert sid not in ss.data['short']
|
||||
|
||||
collisions = 0
|
||||
for k in ('short','long'):
|
||||
for sid in ss[k]:
|
||||
collisions += ss[k][sid][1]
|
||||
for sid in ss.data[k]:
|
||||
collisions += ss.data[k][sid][1]
|
||||
|
||||
assert collisions == collisions_chk, collisions
|
||||
msg_r('({} collisions) '.format(collisions))
|
||||
msg_r('({} collisions, last_sid {}) '.format(collisions,last_sid))
|
||||
msg('OK')
|
||||
|
||||
basic_ops()
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue