Browse Source

master_share: add nonce; use base, not derived, seed ID as share identifier

MMGen 5 years ago
parent
commit
c2e7421ff9
2 changed files with 54 additions and 39 deletions
  1. 29 18
      mmgen/seed.py
  2. 25 21
      test/unit_tests_d/ut_seedsplit.py

+ 29 - 18
mmgen/seed.py

@@ -268,7 +268,6 @@ class SubSeed(SeedBase):
 		return scramble_seed(seed.data,scramble_key)[:16 if short else seed.byte_len]
 
 class SeedShareList(SubSeedList):
-	master_share = None
 	have_short = False
 	split_type = 'N-of-N'
 
@@ -281,24 +280,35 @@ class SeedShareList(SubSeedList):
 		self.id_str = id_str or 'default'
 		self.count = count
 
-		if master_idx:
-			self.master_share = SeedShareMaster(self,master_idx)
+		def make_master_share():
+			for nonce in range(SeedShare.max_nonce+1):
+				ms = SeedShareMaster(self,master_idx,nonce)
+				if ms.sid == parent_seed.sid:
+					if g.debug_subseed:
+						m = 'master_share seed ID collision with parent seed, incrementing nonce to {}'
+						msg(m.format(nonce+1))
+				else:
+					return ms
+			raise SubSeedNonceRangeExceeded('nonce range exceeded')
 
-		while True:
-			self.data = { 'long': IndexedDict(), 'short': IndexedDict() }
-			if master_idx:
-				self.data['long'][self.master_share.derived_seed.sid] = (1,master_idx)
+		self.master_share = make_master_share() if master_idx else None
+
+		for nonce in range(SeedShare.max_nonce+1):
+			self.nonce_start = nonce
+			self.data = { 'long': IndexedDict(), 'short': IndexedDict() } # 'short' is required as a placeholder
+			if self.master_share:
+				self.data['long'][self.master_share.sid] = (1,self.master_share.nonce)
 			self._generate(count-1)
-			self.last_share = SeedShareLast(self)
-			sid = self.last_share.sid
-			if sid in self.data['long'] or sid == parent_seed.sid:
+			self.last_share = ls = SeedShareLast(self)
+			if ls.sid in self.data['long'].keys + [parent_seed.sid]:
 				# collision: throw out entire split list and redo with new start nonce
 				if g.debug_subseed:
-					self._collision_debug_msg(sid,count,self.nonce_start,nonce_desc='nonce_start')
-				self.nonce_start += 1
+					self._collision_debug_msg(ls.sid,count,nonce,nonce_desc='nonce_start')
 			else:
-				self.data['long'][sid] = (self.count,self.nonce_start)
+				self.data['long'][ls.sid] = (self.count,nonce)
 				break
+		else:
+			raise SubSeedNonceRangeExceeded('nonce range exceeded')
 
 		if g.debug_subseed:
 			A = parent_seed.data
@@ -331,7 +341,7 @@ class SeedShareList(SubSeedList):
 		fs2 = '{i:>5}: {}\n'
 		mfs1,mfs2,midx,msid = ('','','','')
 		if self.master_share:
-			mfs1,mfs2 = (' with master share #{} ({})',' master #{} ({})')
+			mfs1,mfs2 = (' with master share #{} ({})',' master share #{}')
 			midx,msid = (self.master_share.idx,self.master_share.sid)
 
 		hdr  = '    {} {} ({} bits)\n'.format('Seed:',self.parent_seed.sid.hl(),self.parent_seed.bitlen)
@@ -341,7 +351,7 @@ class SeedShareList(SubSeedList):
 		hdr += fs1.format('------')
 
 		sl = self.data['long'].keys
-		body1 = fs2.format(sl[0]+mfs2.format(midx,msid),i=1)
+		body1 = fs2.format(sl[0]+mfs2.format(midx),i=1)
 		body = (fs2.format(sl[n],i=n+1) for n in range(1,len(self)))
 
 		return hdr + body1 + ''.join(body)
@@ -384,10 +394,11 @@ class SeedShareLast(SeedBase):
 class SeedShareMaster(SeedBase):
 
 	idx = MMGenImmutableAttr('idx',MasterShareIdx)
-	nonce = 0
+	nonce = MMGenImmutableAttr('nonce',int,typeconv=False)
 
-	def __init__(self,parent_list,idx):
+	def __init__(self,parent_list,idx,nonce):
 		self.idx = idx
+		self.nonce = nonce
 		self.parent_list = parent_list
 		SeedBase.__init__(self,self.make_base_seed_bin())
 
@@ -396,7 +407,7 @@ class SeedShareMaster(SeedBase):
 	def make_base_seed_bin(self):
 		seed = self.parent_list.parent_seed
 		# field maximums: idx: 65535 (1024)
-		scramble_key = b'master:' + self.idx.to_bytes(2,'big')
+		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]
 
 	def make_derived_seed_bin(self,id_str,count):

+ 25 - 21
test/unit_tests_d/ut_seedsplit.py

@@ -27,22 +27,21 @@ class unit_test(object):
 			test_data_master = {
 				'1': {
 					'default': (
-						(8,'4710FBF0','B512A312','3588E156','9374255D','3E87A907','752A2E4E',0,0),
-						(4,'43670520','05880E2B','C6B438D4','5FF9B5DF','778E9C60','2C01F046',0,0) ),
+						(8,'4710FBF0','6AE6177F','AC12090C','6AE6177F','3E87A907','7D1FEA56','BFEBFFFF','629A9808'),
+						(4,'43670520','6739535C','ABF4DD38','6739535C','778E9C60','89CBCFD2','689FABF5','70BED76B'),
+					),
 					'φυβαρ': (
-						(8,'4710FBF0','5FA963B0','69A1F56A','25789CC4','9777A750','E17B9B8B',0,0),
-						(4,'43670520','AF8BFDF8','66F319BE','A5E40978','927549D2','93B2418B',0,0),
-					)
+						(8,'4710FBF0','6AE6177F','AC5FA32E','6AE6177F','9777A750','C7CF2AFC','035AAACB','C777FBE4'),
+						(4,'43670520','6739535C','37EBA2F5','6739535C','927549D2','29BADEE7','9CA73A03','313F5528'))
 				},
 				'5': {
 					'default': (
-						(8,'4710FBF0','A8A34BC0','F69B6CF8','234B5DCD','BB004DC5','08DC9776',0,0),
-						(4,'43670520','C887A2D6','86AE9445','3188AD3D','07339882','BE3FE72A',0,0) ),
-
+						(8,'4710FBF0','5EFAC3D6','B489167D','5EFAC3D6','BB004DC5','1A0381C0','4EA182E3','547FB2DC'),
+						(4,'43670520','EE93DB0E','44962A7D','EE93DB0E','07339882','376A05B1','CE51D022','00149CA3'),
+					),
 					'φυβαρ': (
-						(8,'4710FBF0','89C35D99','B1CD5854','8414652C','32C24668','17CA1E19',0,0),
-						(4,'43670520','06929789','32E8E375','C6AC3C9D','4BEA2AB2','15AFC7F2',0,0)
-					)
+						(8,'4710FBF0','5EFAC3D6','A6E27EE3','5EFAC3D6','32C24668','B4C54297','1EC9B71B','8C5C6B1C'),
+						(4,'43670520','EE93DB0E','B584E963','EE93DB0E','4BEA2AB2','4BEA65C7','140FC43F','BBD19461'))
 				}
 			}
 			if master_idx:
@@ -67,12 +66,17 @@ class unit_test(object):
 						vmsg_r('\n{}'.format(s))
 						assert len(s.strip().split('\n')) == share_count+6, s
 
-						A = shares.get_share_by_idx(1).sid
-						B = shares.get_share_by_seed_id(j).sid
+						if master_idx:
+							A = shares.get_share_by_idx(1,base_seed=False).sid
+							B = shares.get_share_by_seed_id(j,base_seed=False).sid
+							assert A == B == m, A
+
+						A = shares.get_share_by_idx(1,base_seed=True).sid
+						B = shares.get_share_by_seed_id(j,base_seed=True).sid
 						assert A == B == j, A
 
-						A = shares.get_share_by_idx(share_count-1).sid
-						B = shares.get_share_by_seed_id(k).sid
+						A = shares.get_share_by_idx(share_count-1,base_seed=True).sid
+						B = shares.get_share_by_seed_id(k,base_seed=True).sid
 						assert A == B == k, A
 
 						A = shares.get_share_by_idx(share_count).sid
@@ -111,17 +115,16 @@ class unit_test(object):
 
 			msg('OK')
 
-		def collisions():
-			ss_count,last_sid,collisions_chk = (65535,'B5CBCE0A',3)
+		def collisions(seed_hex,ss_count,last_sid,collisions_chk,master_idx):
 
-			msg_r('Testing Seed ID collisions ({} seed shares)...'.format(ss_count))
+			msg_r('Testing Seed ID collisions ({} seed shares, master_idx={})...'.format(ss_count,master_idx))
 			vmsg('')
 
-			seed_bin = bytes.fromhex('1dabcdef' * 4)
+			seed_bin = bytes.fromhex(seed_hex)
 			seed = Seed(seed_bin)
 
 			SeedShareIdx.max_val = ss_count
-			shares = seed.split(ss_count)
+			shares = seed.split(ss_count,use_master=bool(master_idx),master_idx=master_idx)
 			A = shares.get_share_by_idx(ss_count).sid
 			B = shares.get_share_by_seed_id(last_sid).sid
 			assert A == last_sid, A
@@ -141,6 +144,7 @@ class unit_test(object):
 		basic_ops(master_idx=1)
 		basic_ops(master_idx=5)
 		defaults_and_limits()
-		collisions()
+		collisions('1dabcdef'*4,65535,'B5CBCE0A',3,master_idx=None)
+		collisions('18abcdef'*4,65535,'FF03CE82',3,master_idx=1)
 
 		return True