From 35d037a4da404815524c488fdf2651db4506016e Mon Sep 17 00:00:00 2001 From: MMGen Date: Wed, 5 Jun 2019 14:10:53 +0000 Subject: [PATCH] SubSeed: add 'nonce_start' attr; minor changes and fixes --- mmgen/obj.py | 4 +-- mmgen/seed.py | 59 +++++++++++++++++++----------------- test/test_py_d/ts_regtest.py | 1 + 3 files changed, 35 insertions(+), 29 deletions(-) diff --git a/mmgen/obj.py b/mmgen/obj.py index ced0bdbb..70d19890 100755 --- a/mmgen/obj.py +++ b/mmgen/obj.py @@ -586,8 +586,8 @@ class SeedID(str,Hilite,InitErrors): cls.arg_chk(on_fail) try: if seed: - from mmgen.seed import Seed,SubSeed - assert type(seed) in (Seed,SubSeed),'not a Seed or SubSeed instance' + from mmgen.seed import SeedBase + assert issubclass(type(seed),SeedBase),'not a subclass of SeedBase' from mmgen.util import make_chksum_8 return str.__new__(cls,make_chksum_8(seed.data)) elif sid: diff --git a/mmgen/seed.py b/mmgen/seed.py index 42fd2226..7c852a50 100755 --- a/mmgen/seed.py +++ b/mmgen/seed.py @@ -66,6 +66,7 @@ class SeedBase(MMGenObject): class SubSeedList(MMGenObject): have_short = True + nonce_start = 0 def __init__(self,parent_seed): self.member_type = SubSeed @@ -75,11 +76,6 @@ class SubSeedList(MMGenObject): def __len__(self): return len(self.data['long']) - def get_params_by_ss_idx(self,ss_idx): - sid = self.data[ss_idx.type].key(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: @@ -92,21 +88,27 @@ class SubSeedList(MMGenObject): if ss_idx.idx > len(self): self._generate(ss_idx.idx) - sid,idx,nonce = self.get_params_by_ss_idx(ss_idx) + sid = self.data[ss_idx.type].key(ss_idx.idx-1) + idx,nonce = self.data[ss_idx.type][sid] + if idx != ss_idx.idx: + m = "{} != {}: self.data[{t!r}].key(i) does not match self.data[{t!r}][i]!" + die(3,m.format(idx,ss_idx.idx,t=ss_idx.type)) 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 self.member_type(self,idx,nonce,length=ss_idx.type) - 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 self.member_type(self,idx,nonce,length=k) + seed = self.member_type(self,idx,nonce,length=ss_idx.type) + assert seed.sid == sid,'{} != {}: Seed ID mismatch!'.format(seed.sid,sid) + return seed def get_subseed_by_seed_id(self,sid,last_idx=None,print_msg=False): + def get_existing_subseed_by_seed_id(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 self.member_type(self,idx,nonce,length=k) + def do_msg(subseed): if print_msg: qmsg('{} {} ({}:{})'.format( @@ -119,7 +121,7 @@ class SubSeedList(MMGenObject): if last_idx == None: last_idx = g.subseeds - subseed = self.get_existing_subseed_by_seed_id(sid) + subseed = get_existing_subseed_by_seed_id(sid) if subseed: do_msg(subseed) return subseed @@ -129,11 +131,20 @@ class SubSeedList(MMGenObject): self._generate(last_idx,last_sid=sid) - subseed = self.get_existing_subseed_by_seed_id(sid) + subseed = get_existing_subseed_by_seed_id(sid) if subseed: do_msg(subseed) return subseed + def _collision_debug_msg(self,sid,idx,nonce,nonce_desc='nonce'): + slen = 'short' if sid in self.data['short'] else 'long' + m1 = 'add_subseed(idx={},{}):'.format(idx,slen) + if sid == self.parent_seed.sid: + m2 = 'collision with parent Seed ID {},'.format(sid) + else: + m2 = 'collision with ID {} (idx={},{}),'.format(sid,self.data[slen][sid][0],slen) + msg('{:30} {:46} incrementing {} to {}'.format(m1,m2,nonce_desc,nonce+1)) + def _generate(self,last_idx=None,last_sid=None): if last_idx == None: @@ -148,19 +159,13 @@ class SubSeedList(MMGenObject): last_sid = SeedID(sid=last_sid) def add_subseed(idx,length): - for nonce in range(self.member_type.max_nonce): # use nonce to handle Seed ID collisions + for nonce in range(self.nonce_start,self.member_type.max_nonce): # use nonce to 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): 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.data['short']] - m1 = 'add_subseed(idx={},{}):'.format(idx,length) - if sid == self.parent_seed.sid: - m2 = 'collision with parent Seed ID {},'.format(sid) - else: - m2 = 'collision with ID {} (idx={},{}),'.format(sid,self.data[k][sid][0],k) - msg('{:30} {:46} incrementing nonce to {}'.format(m1,m2,nonce+1)) + 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') @@ -213,13 +218,13 @@ class SubSeed(SeedBase): self.idx = idx self.nonce = nonce self.ss_idx = SubSeedIdx(str(idx) + { 'long': 'L', 'short': 'S' }[length]) - SeedBase.__init__(self,seed_bin=SubSeed.make_subseed_bin(parent_list,idx,nonce,length)) + SeedBase.__init__(self,seed_bin=type(self).make_subseed_bin(parent_list,idx,nonce,length)) @staticmethod def make_subseed_bin(parent_list,idx:int,nonce:int,length:str): seed = parent_list.parent_seed short = { 'short': True, 'long': False }[length] - # field maximums: idx: 4294967295, nonce: 65535, short (bool): 255 + # field maximums: idx: 4294967295 (1000000), nonce: 65535 (1000), short: 255 (1) scramble_key = idx.to_bytes(4,'big',signed=False) + \ nonce.to_bytes(2,'big',signed=False) + \ short.to_bytes(1,'big',signed=False) @@ -438,11 +443,11 @@ an empty passphrase, just hit ENTER twice. while True: ret = my_raw_input(p) if ret: - if ret in g.hash_presets.keys(): + if ret in g.hash_presets: self.ssdata.hash_preset = ret return ret else: - msg('Invalid input. Valid choices are {}'.format(', '.join(sorted(g.hash_presets.keys())))) + msg('Invalid input. Valid choices are {}'.format(', '.join(g.hash_presets))) else: self.ssdata.hash_preset = hp return hp diff --git a/test/test_py_d/ts_regtest.py b/test/test_py_d/ts_regtest.py index b8f47b59..ee0f6352 100755 --- a/test/test_py_d/ts_regtest.py +++ b/test/test_py_d/ts_regtest.py @@ -275,6 +275,7 @@ class TestSuiteRegtest(TestSuiteBase,TestSuiteShared): fn = get_file_with_ext(self._user_dir(user),'mmdat') t = self.spawn('mmgen-tool',['get_subseed',subseed_idx,'wallet='+fn],no_msg=True) t.passphrase('MMGen wallet',rt_pw) + t.expect('Hashing passphrase...done') sid = t.read().strip()[:8] self.usr_subsids[user][subseed_idx] = sid return sid