diff --git a/doc/wiki/install-mswin/Install-MMGen-and-Its-Dependencies-on-Microsoft-Windows.md b/doc/wiki/install-mswin/Install-MMGen-and-Its-Dependencies-on-Microsoft-Windows.md
index 6177224f..7f5280de 100644
--- a/doc/wiki/install-mswin/Install-MMGen-and-Its-Dependencies-on-Microsoft-Windows.md
+++ b/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
diff --git a/doc/wiki/using-mmgen/Getting-Started-with-MMGen.md b/doc/wiki/using-mmgen/Getting-Started-with-MMGen.md
index 8af8b7f9..d29ab080 100644
--- a/doc/wiki/using-mmgen/Getting-Started-with-MMGen.md
+++ b/doc/wiki/using-mmgen/Getting-Started-with-MMGen.md
@@ -9,8 +9,8 @@
* Send a transaction
#### Additional Features
-* Using the mnemonic and seed features
-* Mnemonics and seeds: additional information
+* Using the mnemonic, seed and hexseed formats
+* Mnemonics, seeds and hexseeds: additional information
* Incognito wallets
* Hidden incognito wallets
@@ -308,7 +308,7 @@ by invoking the desired command with the `-h` or `--help` switch.
### Additional Features
-#### Using the mnemonic and seed features:
+#### Using the mnemonic, seed and hexseed formats:
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
-#### Mnemonics and seeds: additional information
+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
+
+#### Mnemonics, seeds and hexseeds: additional information
+
+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
...
diff --git a/mmgen/seed.py b/mmgen/seed.py
index c722f2ff..f6aebd5b 100755
--- a/mmgen/seed.py
+++ b/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)
diff --git a/mmgen/util.py b/mmgen/util.py
index 0f3af1c5..88c1af3d 100755
--- a/mmgen/util.py
+++ b/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))
diff --git a/test/ref/1378FC64.mmhex b/test/ref/1378FC64.mmhex
new file mode 100644
index 00000000..a88443aa
--- /dev/null
+++ b/test/ref/1378FC64.mmhex
@@ -0,0 +1 @@
+8a0088 456d 7f5f 1c4b fe3b c916 b875 60ae 6a3e 20da 3969 1cf5 3ded
diff --git a/test/ref/98831F3A.mmhex b/test/ref/98831F3A.mmhex
new file mode 100644
index 00000000..4d3f833a
--- /dev/null
+++ b/test/ref/98831F3A.mmhex
@@ -0,0 +1 @@
+091c8f 456d 7f5f 1c4b fe3b c916 b875 60ae 6a3e 20da 3969 1cf5 3ded 010e 90a5 6e04 8e62
diff --git a/test/ref/FE3C6545.mmhex b/test/ref/FE3C6545.mmhex
new file mode 100644
index 00000000..634a38fd
--- /dev/null
+++ b/test/ref/FE3C6545.mmhex
@@ -0,0 +1 @@
+afc3fe 456d 7f5f 1c4b fe3b c916 b875 60ae 6a3e
diff --git a/test/test.py b/test/test.py
index 63cbf716..17bebd5b 100755
--- a/test/test.py
+++ b/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',