Browse Source

[seed]: export seed to hexadecimal (mmhex) format

philemon 8 years ago
parent
commit
02256664fb

+ 3 - 2
doc/wiki/install-mswin/Install-MMGen-and-Its-Dependencies-on-Microsoft-Windows.md

@@ -165,8 +165,8 @@ Get the [zip archive][10] of the latest stable version from GitHub, unpack and i
 		$ python setup.py build --compiler=mingw32
 		$ sudo ./setup.py install
 
-If you wish, you may run the MMGen test suite to make sure your installation's
-working:
+After first installing and starting the [Bitcoin daemon][77], you may then run
+the MMGen test suite to make sure your installation's working:
 
 		$ test/test.py -s
 
@@ -184,3 +184,4 @@ working:
 [31]: https://sourceforge.net/projects/mingw/files/MinGW/Extension/autoconf/autoconf2.5/autoconf2.5-2.68-1/autoconf2.5-2.68-1-mingw32-bin.tar.lzma
 [32]: https://sourceforge.net/projects/mingw/files/MinGW/Extension/automake/automake1.11/automake1.11-1.11.1-1/automake1.11-1.11.1-1-mingw32-bin.tar.lzma
 [33]: https://sourceforge.net/projects/mingw/files/MinGW/Extension/libtool/libtool-2.4-1/libtool-2.4-1-mingw32-bin.tar.lzma
+[77]: Install-Bitcoind

+ 21 - 6
doc/wiki/using-mmgen/Getting-Started-with-MMGen.md

@@ -9,8 +9,8 @@
 * <a href='#a_st'>Send a transaction</a>
 
 #### <a href='#a_af'>Additional Features</a>
-* <a href='#a_ms'>Using the mnemonic and seed features</a>
-* <a href='#a_ai'>Mnemonics and seeds: additional information</a>
+* <a href='#a_ms'>Using the mnemonic, seed and hexseed formats</a>
+* <a href='#a_ai'>Mnemonics, seeds and hexseeds: additional information</a>
 * <a href='#a_ic'>Incognito wallets</a>
 	* <a href='#a_hi'>Hidden incognito wallets</a>
 
@@ -308,7 +308,7 @@ by invoking the desired command with the `-h` or `--help` switch.
 
 ### <a name='a_af'>Additional Features</a>
 
-#### <a name='a_ms'>Using the mnemonic and seed features:</a>
+#### <a name='a_ms'>Using the mnemonic, seed and hexseed formats:</a>
 
 Continuing our example above, generate a mnemonic from the default wallet:
 
@@ -387,10 +387,25 @@ Or you can do the same thing with 'mmgen-tool':
 		$ mmgen-tool str2id6 'XnyC NfPH piuW dQ2d nM47 VU'
 		0fe02f
 
-#### <a name='a_ai'>Mnemonics and seeds: additional information</a>
+Beginning with version 0.9.0, export to and generation from hexadecimal
+(hexseed) format is also supported.  Hexseed files are identical to seed files
+but encoded in hexadecimal rather than base 58.  They bear the extension
+'.mmhex':
 
-MMGen commands that take mnemonic and seed data may receive the data from a
-prompt instead of a file.  Just omit the file name and specify the input format:
+		$ cat FE3C6545.mmhex
+		afc3fe 456d 7f5f 1c4b fe3b c916 b875 60ae 6a3e
+
+You can easily check that a hexseed is correct by generating its Seed ID with
+standard command-line tools:
+
+		$ echo 456d 7f5f 1c4b fe3b c916 b875 60ae 6a3e | tr -d ' ' | xxd -r -p | sha256sum -b | xxd -r -p | sha256sum -b | cut -c 1-8
+		fe3c6545
+
+#### <a name='a_ai'>Mnemonics, seeds and hexseeds: additional information</a>
+
+MMGen commands that take mnemonic, seed or hexseed data may receive the data
+from a prompt instead of a file.  Just omit the file name and specify the input
+format:
 
 		$ mmgen-walletconv -i words
 		...

+ 53 - 9
mmgen/seed.py

@@ -223,6 +223,9 @@ class SeedSourceUnenc(SeedSource):
 	def _decrypt_retry(self): pass
 	def _encrypt(self): pass
 
+	def _filename(self):
+		return '%s[%s].%s' % (self.seed.sid,self.seed.length,self.ext)
+
 class SeedSourceEnc(SeedSource):
 
 	_msg = {
@@ -470,9 +473,6 @@ class Mnemonic (SeedSourceUnenc):
 
 		return True
 
-	def _filename(self):
-		return '%s[%s].%s' % (self.seed.sid,self.seed.length,self.ext)
-
 class SeedFile (SeedSourceUnenc):
 
 	stdin_ok = True
@@ -509,8 +509,7 @@ class SeedFile (SeedSourceUnenc):
 
 		vmsg_r('Validating %s checksum...' % desc)
 
-		if not compare_chksums(
-				a,'checksum',make_chksum_6(b),'base 58 data'):
+		if not compare_chksums(a,'file',make_chksum_6(b),'computed',verbose=True):
 			return False
 
 		ret = b58decode_pad(b)
@@ -527,8 +526,53 @@ class SeedFile (SeedSourceUnenc):
 
 		return True
 
-	def _filename(self):
-		return '%s[%s].%s' % (self.seed.sid,self.seed.length,self.ext)
+class HexSeedFile (SeedSourceUnenc):
+
+	stdin_ok = True
+	fmt_codes = 'seedhex','hexseed','hex','mmhex'
+	desc = 'hexadecimal seed data'
+	ext = 'mmhex'
+
+	def _format(self):
+		h = self.seed.hexdata
+		self.ssdata.chksum = make_chksum_6(h)
+		self.ssdata.hexseed = h
+		self.fmt_data = '%s %s\n' % (self.ssdata.chksum, split_into_cols(4,h))
+
+	def _deformat(self):
+		desc = self.desc
+		d = self.fmt_data.split()
+		try:
+			d[1]
+			chk,hstr = d[0],''.join(d[1:])
+		except:
+			msg("'%s': invalid %s" % (self.fmt_data.strip(),desc))
+			return False
+
+		if not len(hstr)*4 in g.seed_lens:
+			msg('Invalid data length (%s) in %s' % (len(hstr),desc))
+			return False
+
+		if not is_chksum_6(chk):
+			msg("'%s': invalid checksum format in %s" % (chk, desc))
+			return False
+
+		if not is_hexstring(hstr):
+			msg("'%s': not a hexadecimal string, in %s" % (hstr, desc))
+			return False
+
+		vmsg_r('Validating %s checksum...' % desc)
+
+		if not compare_chksums(chk,'file',make_chksum_6(hstr),'computed',verbose=True):
+			return False
+
+		self.seed = Seed(unhexlify(hstr))
+		self.ssdata.chksum = chk
+		self.ssdata.hexseed = hstr
+
+		check_usr_seed_len(self.seed.length)
+
+		return True
 
 class Wallet (SeedSourceEnc):
 
@@ -614,7 +658,7 @@ class Wallet (SeedSourceEnc):
 
 			chk = make_chksum_6(' '.join(lines[1:]))
 			if not compare_chksums(lines[0],'master',chk,'computed',
-						hdr='For wallet master checksum'):
+						hdr='For wallet master checksum',verbose=True):
 				return False
 
 			return True
@@ -658,7 +702,7 @@ class Wallet (SeedSourceEnc):
 				return False
 
 			if not compare_chksums(chk,key,
-					make_chksum_6(b58_val),'computed checksum'):
+					make_chksum_6(b58_val),'computed checksum',verbose=True):
 				return False
 
 			val = b58decode_pad(b58_val)

+ 10 - 10
mmgen/util.py

@@ -117,14 +117,14 @@ def qmsg_r(s,alt=False):
 	if opt.quiet:
 		if alt != False: sys.stderr.write(alt)
 	else: sys.stderr.write(s)
-def vmsg(s):
-	if opt.verbose: sys.stderr.write(s + '\n')
-def vmsg_r(s):
-	if opt.verbose: sys.stderr.write(s)
-def Vmsg(s):
-	if opt.verbose: sys.stdout.write(s + '\n')
-def Vmsg_r(s):
-	if opt.verbose: sys.stdout.write(s)
+def vmsg(s,force=False):
+	if opt.verbose or force: sys.stderr.write(s + '\n')
+def vmsg_r(s,force=False):
+	if opt.verbose or force: sys.stderr.write(s)
+def Vmsg(s,force=False):
+	if opt.verbose or force: sys.stdout.write(s + '\n')
+def Vmsg_r(s,force=False):
+	if opt.verbose or force: sys.stdout.write(s)
 def dmsg(s):
 	if opt.debug: sys.stdout.write(s + '\n')
 
@@ -295,7 +295,7 @@ def get_hash_params(hash_preset):
 	else: # Shouldn't be here
 		die(3,"%s: invalid 'hash_preset' value" % hash_preset)
 
-def compare_chksums(chk1, desc1, chk2, desc2, hdr='', die_on_fail=False):
+def compare_chksums(chk1,desc1,chk2,desc2,hdr='',die_on_fail=False,verbose=False):
 
 	if not chk1 == chk2:
 		m = "%s ERROR: %s checksum (%s) doesn't match %s checksum (%s)"\
@@ -303,7 +303,7 @@ def compare_chksums(chk1, desc1, chk2, desc2, hdr='', die_on_fail=False):
 		if die_on_fail:
 			die(3,m)
 		else:
-			vmsg(m)
+			vmsg(m,force=verbose)
 			return False
 
 	vmsg('%s checksum OK (%s)' % (capfirst(desc1),chk1))

+ 1 - 0
test/ref/1378FC64.mmhex

@@ -0,0 +1 @@
+8a0088 456d 7f5f 1c4b fe3b c916 b875 60ae 6a3e 20da 3969 1cf5 3ded

+ 1 - 0
test/ref/98831F3A.mmhex

@@ -0,0 +1 @@
+091c8f 456d 7f5f 1c4b fe3b c916 b875 60ae 6a3e 20da 3969 1cf5 3ded 010e 90a5 6e04 8e62

+ 1 - 0
test/ref/FE3C6545.mmhex

@@ -0,0 +1 @@
+afc3fe 456d 7f5f 1c4b fe3b c916 b875 60ae 6a3e

+ 32 - 6
test/test.py

@@ -203,6 +203,7 @@ cfgs = {
 			'sigtx':         'txsign',
 			'mmwords':     'export_mnemonic',
 			'mmseed':      'export_seed',
+			'mmhex':       'export_hex',
 			'mmincog':     'export_incog',
 			'mmincox':     'export_incog_hex',
 			hincog_fn:     'export_incog_hidden',
@@ -407,12 +408,14 @@ cmd_group['main'] = OrderedDict([
 	# txdo must go after txsign
 	['txdo',            (1,'online transaction',       [[['sigtx','mmdat'],1]])],
 
+	['export_hex',      (1,'seed export to hexadecimal format',  [[['mmdat'],1]])],
 	['export_seed',     (1,'seed export to mmseed format',   [[['mmdat'],1]])],
 	['export_mnemonic', (1,'seed export to mmwords format',  [[['mmdat'],1]])],
 	['export_incog',    (1,'seed export to mmincog format',  [[['mmdat'],1]])],
 	['export_incog_hex',(1,'seed export to mmincog hex format', [[['mmdat'],1]])],
 	['export_incog_hidden',(1,'seed export to hidden mmincog format', [[['mmdat'],1]])],
 
+	['addrgen_hex',     (1,'address generation from mmhex file', [[['mmhex','addrs'],1]])],
 	['addrgen_seed',    (1,'address generation from mmseed file', [[['mmseed','addrs'],1]])],
 	['addrgen_mnemonic',(1,'address generation from mmwords file',[[['mmwords','addrs'],1]])],
 	['addrgen_incog',   (1,'address generation from mmincog file',[[['mmincog','addrs'],1]])],
@@ -456,6 +459,7 @@ cmd_group['ref'] = (
 	# reading
 	('ref_wallet_chk', ([],'saved reference wallet')),
 	('ref_seed_chk',   ([],'saved seed file')),
+	('ref_hex_chk',    ([],'saved mmhex file')),
 	('ref_mn_chk',     ([],'saved mnemonic file')),
 	('ref_hincog_chk', ([],'saved hidden incog reference wallet')),
 	('ref_brain_chk',  ([],'saved brainwallet')),
@@ -481,6 +485,7 @@ cmd_group['conv_in'] = ( # reading
 	('ref_wallet_conv',    'conversion of saved reference wallet'),
 	('ref_mn_conv',        'conversion of saved mnemonic'),
 	('ref_seed_conv',      'conversion of saved seed file'),
+	('ref_hex_conv',       'conversion of saved hexadecimal seed file'),
 	('ref_brain_conv',     'conversion of ref brainwallet'),
 	('ref_incog_conv',     'conversion of saved incog wallet'),
 	('ref_incox_conv',     'conversion of saved hex incog wallet'),
@@ -491,6 +496,7 @@ cmd_group['conv_in'] = ( # reading
 cmd_group['conv_out'] = ( # writing
 	('ref_wallet_conv_out', 'ref seed conversion to wallet'),
 	('ref_mn_conv_out',     'ref seed conversion to mnemonic'),
+	('ref_hex_conv_out',    'ref seed conversion to hex seed'),
 	('ref_seed_conv_out',   'ref seed conversion to seed'),
 	('ref_incog_conv_out',  'ref seed conversion to incog data'),
 	('ref_incox_conv_out',  'ref seed conversion to hex incog data'),
@@ -1547,6 +1553,9 @@ class MMGenTestSuite(object):
 		end_silence()
 		ok()
 
+	def export_hex(self,name,wf,desc='hexadecimal seed data',out_fmt='hex',pf=None):
+		self.export_seed(name,wf,desc=desc,out_fmt=out_fmt,pf=pf)
+
 	def export_seed_dfl_wallet(self,name,pf,desc='seed data',out_fmt='seed'):
 		self.export_seed(name,wf=None,desc=desc,out_fmt=out_fmt,pf=pf)
 
@@ -1582,6 +1591,9 @@ class MMGenTestSuite(object):
 #		t.no_overwrite()
 		ok()
 
+	def addrgen_hex(self,name,wf,foo,desc='hexadecimal seed data',in_fmt='hex'):
+		self.addrgen_seed(name,wf,foo,desc=desc,in_fmt=in_fmt)
+
 	def addrgen_mnemonic(self,name,wf,foo):
 		self.addrgen_seed(name,wf,foo,desc='mnemonic data',in_fmt='words')
 
@@ -1795,6 +1807,9 @@ class MMGenTestSuite(object):
 	def ref_seed_conv(self,name):
 		self.ref_mn_conv(name,ext='mmseed',desc='Seed data')
 
+	def ref_hex_conv(self,name):
+		self.ref_mn_conv(name,ext='mmhex',desc='Hexadecimal seed data')
+
 	def ref_brain_conv(self,name):
 		uopts = ['-i','b','-p','1','-l',str(cfg['seed_len'])]
 		self.walletconv_in(name,None,'brainwallet',uopts,oo=True)
@@ -1825,6 +1840,9 @@ class MMGenTestSuite(object):
 	def ref_seed_conv_out(self,name):
 		self.walletconv_out(name,'seed data','seed')
 
+	def ref_hex_conv_out(self,name):
+		self.walletconv_out(name,'hexadecimal seed data','hexseed')
+
 	def ref_incog_conv_out(self,name):
 		self.walletconv_out(name,'incognito data',out_fmt='i',pw=True)
 
@@ -1851,16 +1869,21 @@ class MMGenTestSuite(object):
 			pf = None
 		self.walletchk(name,wf,pf=pf,pw=True,sid=cfg['seed_id'])
 
-	from mmgen.seed import SeedFile
-	def ref_seed_chk(self,name,ext=SeedFile.ext):
-		wf = os.path.join(ref_dir,'%s.%s' % (cfg['seed_id'],ext))
+	def ref_ss_chk(self,name,ss=None):
+		wf = os.path.join(ref_dir,'%s.%s' % (cfg['seed_id'],ss.ext))
+		self.walletchk(name,wf,pf=None,desc=ss.desc,sid=cfg['seed_id'])
+
+	def ref_seed_chk(self,name):
 		from mmgen.seed import SeedFile
-		desc = ('mnemonic data','seed data')[ext==SeedFile.ext]
-		self.walletchk(name,wf,pf=None,desc=desc,sid=cfg['seed_id'])
+		self.ref_ss_chk(name,ss=SeedFile)
+
+	def ref_hex_chk(self,name):
+		from mmgen.seed import HexSeedFile
+		self.ref_ss_chk(name,ss=HexSeedFile)
 
 	def ref_mn_chk(self,name):
 		from mmgen.seed import Mnemonic
-		self.ref_seed_chk(name,ext=Mnemonic.ext)
+		self.ref_ss_chk(name,ss=Mnemonic)
 
 	def ref_brain_chk(self,name,bw_file=ref_bw_file):
 		wf = os.path.join(ref_dir,bw_file)
@@ -2060,6 +2083,7 @@ class MMGenTestSuite(object):
 			'ref_wallet_conv',
 			'ref_mn_conv',
 			'ref_seed_conv',
+			'ref_hex_conv',
 			'ref_brain_conv',
 			'ref_incog_conv',
 			'ref_incox_conv',
@@ -2068,6 +2092,7 @@ class MMGenTestSuite(object):
 			'ref_wallet_conv_out',
 			'ref_mn_conv_out',
 			'ref_seed_conv_out',
+			'ref_hex_conv_out',
 			'ref_incog_conv_out',
 			'ref_incox_conv_out',
 			'ref_hincog_conv_out',
@@ -2075,6 +2100,7 @@ class MMGenTestSuite(object):
 			'refwalletgen',
 			'refaddrgen',
 			'ref_seed_chk',
+			'ref_hex_chk',
 			'ref_mn_chk',
 			'ref_brain_chk',
 			'ref_hincog_chk',