Browse Source

Minor changes, bugfixes

philemon 10 years ago
parent
commit
8fc94a8c64

+ 0 - 1
INSTALL

@@ -1,4 +1,3 @@
-
 	MMGen is written in Pure Python and runs on MS Windows and Linux.
 	MMGen is written in Pure Python and runs on MS Windows and Linux.
 
 
 	Instructions for installation and use reside on MMGen's Github wiki:
 	Instructions for installation and use reside on MMGen's Github wiki:

+ 5 - 10
mmgen/addr.py

@@ -176,7 +176,7 @@ def _parse_addrfile_body(lines,has_keys=False,check=False):
 
 
 def _parse_addrfile(fn,buf=[],has_keys=False,exit_on_error=True):
 def _parse_addrfile(fn,buf=[],has_keys=False,exit_on_error=True):
 
 
-	if buf: lines = remove_comments(buf.split("\n"))
+	if buf: lines = remove_comments(buf.splitlines()) # DOS-safe
 	else:   lines = get_lines_from_file(fn,"address data",trim_comments=True)
 	else:   lines = get_lines_from_file(fn,"address data",trim_comments=True)
 
 
 	try:
 	try:
@@ -190,7 +190,7 @@ def _parse_addrfile(fn,buf=[],has_keys=False,exit_on_error=True):
 		elif cbrace != '}':
 		elif cbrace != '}':
 			errmsg = "'%s': invalid last line" % cbrace
 			errmsg = "'%s': invalid last line" % cbrace
 		elif not is_mmgen_seed_id(sid):
 		elif not is_mmgen_seed_id(sid):
-			errmsg = "'%s': invalid seed ID" % sid
+			errmsg = "'%s': invalid Seed ID" % sid
 		else:
 		else:
 			ret = _parse_addrfile_body(lines[1:-1],has_keys)
 			ret = _parse_addrfile_body(lines[1:-1],has_keys)
 			if type(ret) == list: return sid,ret
 			if type(ret) == list: return sid,ret
@@ -203,14 +203,9 @@ def _parse_addrfile(fn,buf=[],has_keys=False,exit_on_error=True):
 		return False
 		return False
 
 
 
 
-def _parse_keyaddr_file(infile):
-	d = get_data_from_file(infile,"{pnm} key-address file data".format(pnm=pnm))
-	enc_ext = get_extension(infile) == g.mmenc_ext
-	if enc_ext or not is_utf8(d):
-		m = "Decrypting" if enc_ext else "Attempting to decrypt"
-		msg("%s key-address file %s" % (m,infile))
-		from crypto import mmgen_decrypt_retry
-		d = mmgen_decrypt_retry(d,"key-address file")
+def _parse_keyaddr_file(fn):
+	from mmgen.crypto import mmgen_decrypt_file_maybe
+	d = mmgen_decrypt_file_maybe(fn,"key-address file")
 	return _parse_addrfile("",buf=d,has_keys=True,exit_on_error=False)
 	return _parse_addrfile("",buf=d,has_keys=True,exit_on_error=False)
 
 
 
 

+ 11 - 2
mmgen/crypto.py

@@ -52,7 +52,7 @@ keystrokes will also be used as a source of randomness.
 # Try again? (Y)es, (n)o, (m)ore information:
 # Try again? (Y)es, (n)o, (m)ore information:
 # """.strip(),
 # """.strip(),
 # 	'confirm_seed_id': """
 # 	'confirm_seed_id': """
-# If the seed ID above is correct but you're seeing this message, then you need
+# If the Seed ID above is correct but you're seeing this message, then you need
 # to exit and re-run the program with the '--old-incog-fmt' option.
 # to exit and re-run the program with the '--old-incog-fmt' option.
 # """.strip(),
 # """.strip(),
 }
 }
@@ -75,7 +75,7 @@ def decrypt_seed(enc_seed, key, seed_id, key_id):
 	chk2 = make_chksum_8(dec_seed)
 	chk2 = make_chksum_8(dec_seed)
 
 
 	if seed_id:
 	if seed_id:
-		if compare_chksums(seed_id,"seed ID",chk2,"decrypted seed"):
+		if compare_chksums(seed_id,"Seed ID",chk2,"decrypted seed"):
 			qmsg("Passphrase is OK")
 			qmsg("Passphrase is OK")
 		else:
 		else:
 			if not opt.debug:
 			if not opt.debug:
@@ -246,6 +246,15 @@ def mmgen_decrypt(data,desc="data",hash_preset=""):
 		msg("Incorrect passphrase or hash preset")
 		msg("Incorrect passphrase or hash preset")
 		return False
 		return False
 
 
+def mmgen_decrypt_file_maybe(fn,desc):
+	d = get_data_from_file(fn,"{} data".format(desc),binary=True)
+	have_enc_ext = get_extension(fn) == g.mmenc_ext
+	if have_enc_ext or not is_ascii(d):
+		m = ("Attempting to decrypt","Decrypting")[int(have_enc_ext)]
+		msg("%s %s %s" % (m,desc,fn))
+		d = mmgen_decrypt_retry(d,desc)
+	return d
+
 def mmgen_decrypt_retry(d,desc="data"):
 def mmgen_decrypt_retry(d,desc="data"):
 	while True:
 	while True:
 		d_dec = mmgen_decrypt(d,desc)
 		d_dec = mmgen_decrypt(d,desc)

+ 1 - 1
mmgen/main_addrgen.py

@@ -92,7 +92,7 @@ FMT CODES:
   {f}
   {f}
 """.format(
 """.format(
 		n=note1,
 		n=note1,
-		f="\n  ".join(SeedSource.format_fmt_codes().split("\n")),
+		f="\n  ".join(SeedSource.format_fmt_codes().splitlines()),
 		o=opt.opts
 		o=opt.opts
 	)
 	)
 }
 }

+ 1 - 1
mmgen/main_addrimport.py

@@ -76,7 +76,7 @@ for e in ai.addrdata:
 		msg("%s: invalid address" % e.addr)
 		msg("%s: invalid address" % e.addr)
 		sys.exit(2)
 		sys.exit(2)
 
 
-m = (" from seed ID %s" % ai.seed_id) if ai.seed_id else ""
+m = (" from Seed ID %s" % ai.seed_id) if ai.seed_id else ""
 qmsg("OK. %s addresses%s" % (ai.num_addrs,m))
 qmsg("OK. %s addresses%s" % (ai.num_addrs,m))
 
 
 g.http_timeout = 3600
 g.http_timeout = 3600

+ 2 - 2
mmgen/main_pywallet.py

@@ -61,7 +61,7 @@ import math
 
 
 import mmgen.globalvars as g
 import mmgen.globalvars as g
 import mmgen.opt as opt
 import mmgen.opt as opt
-from mmgen.util import msg,mdie,mmsg,write_data_to_file
+from mmgen.util import msg
 
 
 max_version = 60000
 max_version = 60000
 addrtype = 0
 addrtype = 0
@@ -1664,7 +1664,7 @@ len_arg = "%s" % len(wallet_addrs) \
 	if len(data) == len(wallet_addrs) or ext == "json" \
 	if len(data) == len(wallet_addrs) or ext == "json" \
 		else "%s:%s" % (len(data),len(wallet_addrs))
 		else "%s:%s" % (len(data),len(wallet_addrs))
 
 
-from mmgen.util import make_chksum_8,write_to_file,write_to_stdout
+from mmgen.util import make_chksum_8,write_data_to_file
 wallet_id = make_chksum_8(str(sorted(wallet_addrs)))
 wallet_id = make_chksum_8(str(sorted(wallet_addrs)))
 
 
 data = "\n".join(data) + "\n"
 data = "\n".join(data) + "\n"

+ 7 - 1
mmgen/main_tool.py

@@ -32,6 +32,7 @@ opts_data = {
 	'options': """
 	'options': """
 -d, --outdir=       d Specify an alternate directory 'd' for output
 -d, --outdir=       d Specify an alternate directory 'd' for output
 -h, --help            Print this help message
 -h, --help            Print this help message
+-P, --passwd-file= f  Get passphrase from file 'f'.
 -q, --quiet           Produce quieter output
 -q, --quiet           Produce quieter output
 -r, --usr-randchars=n Get 'n' characters of additional randomness from
 -r, --usr-randchars=n Get 'n' characters of additional randomness from
                       user (min={g.min_urandchars}, max={g.max_urandchars})
                       user (min={g.min_urandchars}, max={g.max_urandchars})
@@ -45,7 +46,12 @@ command
 """.format(tool.cmd_help,g.prog_name)
 """.format(tool.cmd_help,g.prog_name)
 }
 }
 
 
-cmd_args = opt.opts.init(opts_data,add_opts=["no_keyconv"])
+cmd_args = opt.opts.init(opts_data,
+	add_opts=[
+		"no_keyconv",
+		"hidden_incog_input_params",
+		"in_fmt"
+		])
 
 
 if len(cmd_args) < 1:
 if len(cmd_args) < 1:
 	opt.opts.usage()
 	opt.opts.usage()

+ 11 - 13
mmgen/main_txcreate.py

@@ -155,7 +155,7 @@ Display options: show [D]ays, [g]roup, show [m]mgen addr, r[e]draw screen
 	from copy import deepcopy
 	from copy import deepcopy
 	from mmgen.term import get_terminal_size
 	from mmgen.term import get_terminal_size
 
 
-	write_to_file_msg = ""
+	written_to_file_msg = ""
 	msg("")
 	msg("")
 
 
 	while True:
 	while True:
@@ -215,8 +215,8 @@ Display options: show [D]ays, [g]roup, show [m]mgen addr, r[e]draw screen
 		out += [fs % (str(n+1)+")",i.tx,i.vout,i.addr,i.amt,i.age)
 		out += [fs % (str(n+1)+")",i.tx,i.vout,i.addr,i.amt,i.age)
 					for n,i in enumerate(unsp)]
 					for n,i in enumerate(unsp)]
 
 
-		msg("\n".join(out) +"\n\n" + write_to_file_msg + options_msg)
-		write_to_file_msg = ""
+		msg("\n".join(out) +"\n\n" + written_to_file_msg + options_msg)
+		written_to_file_msg = ""
 
 
 		skip_prompt = False
 		skip_prompt = False
 
 
@@ -241,8 +241,8 @@ Display options: show [D]ays, [g]roup, show [m]mgen addr, r[e]draw screen
 			elif reply == 'p':
 			elif reply == 'p':
 				d = format_unspent_outputs_for_printing(unsp,sort_info,total)
 				d = format_unspent_outputs_for_printing(unsp,sort_info,total)
 				of = "listunspent[%s].out" % ",".join(sort_info)
 				of = "listunspent[%s].out" % ",".join(sort_info)
-				write_to_file(of, d, "",False,False)
-				write_to_file_msg = "Data written to '%s'\n\n" % of
+				write_data_to_file(of,d,"unspent outputs listing")
+				written_to_file_msg = "Data written to '%s'\n\n" % of
 			elif reply == 'v':
 			elif reply == 'v':
 				do_pager("\n".join(out))
 				do_pager("\n".join(out))
 				continue
 				continue
@@ -390,7 +390,8 @@ if g.bogus_wallet_data:  # for debugging purposes only
 	us = eval(get_data_from_file(g.bogus_wallet_data))
 	us = eval(get_data_from_file(g.bogus_wallet_data))
 else:
 else:
 	us = c.listunspent()
 	us = c.listunspent()
-#	write_to_file("bogus_unspent.json", repr(us)); sys.exit()
+#	write_data_to_file("bogus_unspent.json", repr(us), "bogus unspent data")
+#	sys.exit()
 
 
 if not us: msg(wmsg['no_spendable_outputs']); sys.exit(2)
 if not us: msg(wmsg['no_spendable_outputs']); sys.exit(2)
 for o in us:
 for o in us:
@@ -470,10 +471,7 @@ b2m_map = make_b2m_map(sel_unspent,tx_out,ail_w,ail_f)
 prompt_and_view_tx_data(c,"View decoded transaction?",
 prompt_and_view_tx_data(c,"View decoded transaction?",
 		sel_unspent,tx_hex,b2m_map,comment,metadata)
 		sel_unspent,tx_hex,b2m_map,comment,metadata)
 
 
-if keypress_confirm("Save transaction?",default_yes=False):
-	outfile = "tx_%s[%s].%s" % (tx_id,amt,g.rawtx_ext)
-	data = make_tx_data("{} {} {}".format(*metadata),
-				tx_hex,sel_unspent,b2m_map,comment)
-	write_to_file(outfile,data,"transaction",False,True)
-else:
-	msg("Transaction not saved")
+outfile = "tx_%s[%s].%s" % (tx_id,amt,g.rawtx_ext)
+data = make_tx_data("{} {} {}".format(*metadata),
+			tx_hex,sel_unspent,b2m_map,comment)
+write_data_to_file(outfile,data,"transaction",ask_write_default_yes=False)

+ 2 - 4
mmgen/main_txsend.py

@@ -63,9 +63,7 @@ if keypress_confirm("Edit transaction comment?"):
 	comment = get_tx_comment_from_user(comment)
 	comment = get_tx_comment_from_user(comment)
 	data = make_tx_data("{} {} {}".format(*metadata), tx_hex,
 	data = make_tx_data("{} {} {}".format(*metadata), tx_hex,
 				inputs_data, b2m_map, comment)
 				inputs_data, b2m_map, comment)
-	w = "signed transaction with edited comment"
-	outfile = infile
-	write_to_file(outfile,data,w,False,True,True)
+	write_data_to_file(infile,data,"signed transaction with edited comment")
 
 
 warn   = "Once this transaction is sent, there's no taking it back!"
 warn   = "Once this transaction is sent, there's no taking it back!"
 action = "broadcast this transaction to the network"
 action = "broadcast this transaction to the network"
@@ -86,4 +84,4 @@ except:
 msg("Transaction sent: %s" % tx_id)
 msg("Transaction sent: %s" % tx_id)
 
 
 of = "tx_{}[{}].txid".format(*metadata[:2])
 of = "tx_{}[{}].txid".format(*metadata[:2])
-write_to_file(of, tx_id+"\n","transaction ID",True,True)
+write_data_to_file(of, tx_id+"\n","transaction ID",ask_overwrite=True)

+ 20 - 21
mmgen/main_txsign.py

@@ -101,7 +101,7 @@ Seed data supplied in files must have the following extensions:
 FMT CODES:
 FMT CODES:
   {f}
   {f}
 """.format(
 """.format(
-		f="\n  ".join(SeedSource.format_fmt_codes().split("\n")),
+		f="\n  ".join(SeedSource.format_fmt_codes().splitlines()),
 		g=g,pnm=pnm,pnl=pnl
 		g=g,pnm=pnm,pnl=pnl
 	)
 	)
 }
 }
@@ -126,11 +126,11 @@ def get_seed_for_seed_id(seed_id,infiles,saved_seeds):
 		if infiles:
 		if infiles:
 			ss = SeedSource(infiles.pop(0),ignore_in_fmt=True)
 			ss = SeedSource(infiles.pop(0),ignore_in_fmt=True)
 		elif opt.in_fmt:
 		elif opt.in_fmt:
-			qmsg("Need seed data for seed ID %s" % seed_id)
+			qmsg("Need seed data for Seed ID %s" % seed_id)
 			ss = SeedSource()
 			ss = SeedSource()
-			msg("User input produced seed ID %s" % make_chksum_8(seed))
+			msg("User input produced Seed ID %s" % make_chksum_8(seed))
 		else:
 		else:
-			msg("ERROR: No seed source found for seed ID: %s" % seed_id)
+			msg("ERROR: No seed source found for Seed ID: %s" % seed_id)
 			sys.exit(2)
 			sys.exit(2)
 
 
 		saved_seeds[ss.seed.sid] = ss.seed.data
 		saved_seeds[ss.seed.sid] = ss.seed.data
@@ -229,7 +229,7 @@ for the following non-{pnm} address{suf}:\n    {l}""".format(
 def parse_mmgen_keyaddr_file():
 def parse_mmgen_keyaddr_file():
 	from mmgen.addr import AddrInfo
 	from mmgen.addr import AddrInfo
 	ai = AddrInfo(opt.mmgen_keys_from_file,has_keys=True)
 	ai = AddrInfo(opt.mmgen_keys_from_file,has_keys=True)
-	vmsg("Found %s wif key%s for seed ID %s" %
+	vmsg("Found %s wif key%s for Seed ID %s" %
 			(ai.num_addrs, suf(ai.num_addrs,"k"), ai.seed_id))
 			(ai.num_addrs, suf(ai.num_addrs,"k"), ai.seed_id))
 	# idx: (0=addr, 1=comment 2=wif) -> mmaddr: (0=addr, 1=wif)
 	# idx: (0=addr, 1=comment 2=wif) -> mmaddr: (0=addr, 1=wif)
 	return dict(
 	return dict(
@@ -238,14 +238,10 @@ def parse_mmgen_keyaddr_file():
 
 
 def parse_keylist(from_file):
 def parse_keylist(from_file):
 	fn = opt.keys_from_file
 	fn = opt.keys_from_file
-	d = get_data_from_file(fn,"non-{pnm} keylist".format(pnm=pnm))
-	enc_ext = get_extension(fn) == g.mmenc_ext
-	if enc_ext or not is_utf8(d):
-		if not enc_ext: qmsg("Keylist file appears to be encrypted")
-		from crypto import mmgen_decrypt_retry
-		d = mmgen_decrypt_retry(d,"encrypted keylist")
-	# Check for duplication with key-address file
-	keys_all = set(remove_comments(d.split("\n")))
+	from mmgen.crypto import mmgen_decrypt_file_maybe
+	dec = mmgen_decrypt_file_maybe(fn,"non-{} keylist file".format(pnm))
+	# Remove possible dups from key-address file
+	keys_all = set(remove_comments(dec.splitlines())) # DOS-safe
 	d = from_file['mmdata']
 	d = from_file['mmdata']
 	kawifs = [d[k][1] for k in d.keys()]
 	kawifs = [d[k][1] for k in d.keys()]
 	keys = [k for k in keys_all if k not in kawifs]
 	keys = [k for k in keys_all if k not in kawifs]
@@ -359,7 +355,7 @@ for tx_num,tx_file in enumerate(tx_files,1):
 
 
 	extra_sids = set(saved_seeds.keys()) - sids
 	extra_sids = set(saved_seeds.keys()) - sids
 	if extra_sids:
 	if extra_sids:
-		msg("Unused seed ID%s: %s" %
+		msg("Unused Seed ID%s: %s" %
 			(suf(extra_sids,"k")," ".join(extra_sids)))
 			(suf(extra_sids,"k")," ".join(extra_sids)))
 
 
 	# Begin signing
 	# Begin signing
@@ -378,13 +374,16 @@ for tx_num,tx_file in enumerate(tx_files,1):
 		if keypress_confirm("Edit transaction comment?"):
 		if keypress_confirm("Edit transaction comment?"):
 			comment = get_tx_comment_from_user(comment)
 			comment = get_tx_comment_from_user(comment)
 		outfile = "tx_%s[%s].%s" % (metadata[0],metadata[1],g.sigtx_ext)
 		outfile = "tx_%s[%s].%s" % (metadata[0],metadata[1],g.sigtx_ext)
-		data = make_tx_data("{} {} {t}".format(*metadata[:2], t=make_timestamp()),
-				sig_tx['hex'], inputs_data, b2m_map, comment)
-		w = "signed transaction{}".format(tx_num_str)
-		if keypress_confirm("Save signed transaction?",default_yes=False):
-			write_to_file(outfile,data,w,(not opt.quiet),True,False)
-		else:
-			msg("Signed transaction not saved")
+		data = make_tx_data(
+				"{} {} {t}".format(*metadata[:2],
+				t=make_timestamp()),
+				sig_tx['hex'], inputs_data, b2m_map, comment
+			)
+		write_data_to_file(
+			outfile,data,
+			"signed transaction{}".format(tx_num_str),
+			ask_write_prompt="Save signed transaction?"
+		)
 	else:
 	else:
 		msg_r("failed\nSome keys were missing.  ")
 		msg_r("failed\nSome keys were missing.  ")
 		msg("Transaction %scould not be signed." % tx_num_str)
 		msg("Transaction %scould not be signed." % tx_num_str)

+ 1 - 1
mmgen/main_wallet.py

@@ -103,7 +103,7 @@ opts_data = {
 FMT CODES:
 FMT CODES:
   {f}
   {f}
 """.format(
 """.format(
-	f="\n  ".join(SeedSource.format_fmt_codes().split("\n")),
+	f="\n  ".join(SeedSource.format_fmt_codes().splitlines()),
 	pw_note=pw_note,
 	pw_note=pw_note,
 	bw_note=("","\n\n" + bw_note)[int(bool(bw_note))]
 	bw_note=("","\n\n" + bw_note)[int(bool(bw_note))]
 	)
 	)

+ 0 - 149
mmgen/mnemonic.py

@@ -1,149 +0,0 @@
-#!/usr/bin/env python
-#
-# mmgen = Multi-Mode GENerator, command-line Bitcoin cold storage solution
-# Copyright (C)2013-2015 Philemon <mmgen-py@yandex.com>
-#
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program.  If not, see <http://www.gnu.org/licenses/>.
-
-"""
-mnemonic.py:  Mnemonic routines for the MMGen suite
-"""
-
-import sys
-from binascii import hexlify
-from mmgen.util import msg,msg_r,make_chksum_8,Vmsg,Msg
-from mmgen.crypto import get_random
-import mmgen.globalvars as g
-import mmgen.opt as opt
-
-wl_checksums = {
-	"electrum": '5ca31424',
-	"tirosh":   '1a5faeff'
-}
-
-# These are the only base-1626 specific configs:
-mn_base = 1626
-def mn2hex_pad(mn):     return len(mn) * 8 / 3
-def hex2mn_pad(hexnum): return len(hexnum) * 3 / 8
-
-# Universal base-conversion routines:
-def baseNtohex(base,words,wordlist,pad=0):
-	deconv = \
-		[wordlist.index(words[::-1][i])*(base**i) for i in range(len(words))]
-	ret = ("{:0%sx}" % pad).format(sum(deconv))
-	return "%s%s" % (('0' if len(ret) % 2 else ''), ret)
-
-def hextobaseN(base,hexnum,wordlist,pad=0):
-	num,ret = int(hexnum,16),[]
-	while num:
-		ret.append(num % base)
-		num /= base
-	return [wordlist[n] for n in [0] * (pad-len(ret)) + ret[::-1]]
-
-def get_seed_from_mnemonic(mn,wl,silent=False,label=""):
-
-	if len(mn) not in g.mn_lens:
-		msg("Invalid mnemonic (%i words).  Allowed numbers of words: %s" %
-				(len(mn),", ".join([str(i) for i in g.mn_lens])))
-		sys.exit(3)
-
-	for n,w in enumerate(mn,1):
-		if w not in wl:
-			msg("Invalid mnemonic: word #%s is not in the wordlist" % n)
-			sys.exit(3)
-
-	from binascii import unhexlify
-	hseed = baseNtohex(mn_base,mn,wl,mn2hex_pad(mn))
-
-	rev = hextobaseN(mn_base,hseed,wl,hex2mn_pad(hseed))
-	if rev != mn:
-		msg("ERROR: mnemonic recomputed from seed not the same as original")
-		msg("Recomputed mnemonic:\n%s" % " ".join(rev))
-		sys.exit(3)
-
-	if not silent:
-		msg("Valid mnemonic for seed ID %s" % make_chksum_8(unhexlify(hseed)))
-
-	return unhexlify(hseed)
-
-
-def get_mnemonic_from_seed(seed, wl, label="", verbose=False):
-
-	if len(seed)*8 not in g.seed_lens:
-		msg("%s: invalid seed length" % len(seed))
-		sys.exit(3)
-
-	from binascii import hexlify
-
-	hseed = hexlify(seed)
-	mn = hextobaseN(mn_base,hseed,wl,hex2mn_pad(hseed))
-
-	if verbose:
-		msg("Wordlist:    %s"          % label.capitalize())
-		msg("Seed length: %s bits"     % (len(seed) * 8))
-		msg("Seed:        %s"          % hseed)
-		msg("mnemonic (%s words):\n%s" % (len(mn), " ".join(mn)))
-
-	rev = baseNtohex(mn_base,mn,wl,mn2hex_pad(mn))
-	if rev != hseed:
-		msg("ERROR: seed recomputed from wordlist not the same as original seed!")
-		msg("Original seed:   %s" % hseed)
-		msg("Recomputed seed: %s" % rev)
-		sys.exit(3)
-
-	return mn
-
-
-def check_wordlist(wl,label):
-
-	Msg("Wordlist: %s" % label.capitalize())
-
-	from hashlib import sha256
-
-	Msg("Length:   %i words" % len(wl))
-	new_chksum = sha256(" ".join(wl)).hexdigest()[:8]
-
-	if new_chksum != wl_checksums[label]:
-		Msg("ERROR: Checksum mismatch.  Computed: %s, Saved: %s" % \
-			(new_chksum,wl_checksums[label]))
-		sys.exit(3)
-
-	Msg("Checksum: %s (matches)" % new_chksum)
-
-	if (sorted(wl) == wl):
-		Msg("List is sorted")
-	else:
-		Msg("ERROR: List is not sorted!")
-		sys.exit(3)
-
-from mmgen.mn_electrum  import words as el
-from mmgen.mn_tirosh    import words as tl
-wordlists = sorted(wl_checksums)
-
-def get_wordlist(wordlist):
-	wordlist = wordlist.lower()
-	if wordlist not in wordlists:
-		Msg('"%s": invalid wordlist.  Valid choices: %s' %
-			(wordlist,'"'+'" "'.join(wordlists)+'"'))
-		sys.exit(1)
-	return (el if wordlist == "electrum" else tl).strip().split("\n")
-
-def do_random_mn(nbytes,wordlist):
-	r = get_random(nbytes)
-	Vmsg("Seed: %s" % hexlify(r))
-	for wlname in (wordlists if wordlist == "all" else [wordlist]):
-		wl = get_wordlist(wlname)
-		mn = get_mnemonic_from_seed(r,wl,wordlist)
-		Vmsg("%s wordlist mnemonic:" % (wlname.capitalize()))
-		Msg(" ".join(mn))

+ 2 - 4
mmgen/rpc/proxy.py

@@ -104,10 +104,8 @@ class AuthServiceProxy(object):
 					'Authorization' : self.__authhdr,
 					'Authorization' : self.__authhdr,
 					'Content-type' : 'application/json' })
 					'Content-type' : 'application/json' })
 		except:
 		except:
-			from mmgen.util import msg
-			import sys
-			msg("Unable to connect to bitcoind")
-			sys.exit(2)
+			from mmgen.util import die,red
+			die(1,red("Unable to connect to bitcoind"))
 
 
 		httpresp = self.__conn.getresponse()
 		httpresp = self.__conn.getresponse()
 		if httpresp is None:
 		if httpresp is None:

+ 68 - 30
mmgen/seed.py

@@ -56,6 +56,7 @@ class Seed(MMGenObject):
 class SeedSource(MMGenObject):
 class SeedSource(MMGenObject):
 
 
 	desc = g.proj_name + " seed source"
 	desc = g.proj_name + " seed source"
+	file_mode = "text"
 	stdin_ok = False
 	stdin_ok = False
 	ask_tty = True
 	ask_tty = True
 	no_tty  = False
 	no_tty  = False
@@ -64,7 +65,8 @@ class SeedSource(MMGenObject):
 
 
 	class SeedSourceData(MMGenObject): pass
 	class SeedSourceData(MMGenObject): pass
 
 
-	def __new__(cls,fn=None,ss=None,ignore_in_fmt=False,passchg=False):
+	def __new__(cls,fn=None,ss=None,seed=None,
+				ignore_in_fmt=False,passchg=False):
 
 
 		def die_on_opt_mismatch(opt,sstype):
 		def die_on_opt_mismatch(opt,sstype):
 			opt_sstype = cls.fmt_code_to_sstype(opt)
 			opt_sstype = cls.fmt_code_to_sstype(opt)
@@ -104,12 +106,13 @@ class SeedSource(MMGenObject):
 		else: # Called with no inputs - initialize with random seed
 		else: # Called with no inputs - initialize with random seed
 			sstype = cls.fmt_code_to_sstype(opt.out_fmt)
 			sstype = cls.fmt_code_to_sstype(opt.out_fmt)
 			me = super(cls,cls).__new__(sstype or Wallet) # default: Wallet
 			me = super(cls,cls).__new__(sstype or Wallet) # default: Wallet
-			me.seed = Seed()
+			me.seed = Seed(seed_bin=seed or None)
 			me.op = "new"
 			me.op = "new"
 
 
 		return me
 		return me
 
 
-	def __init__(self,fn=None,ss=None,ignore_in_fmt=False,passchg=False):
+	def __init__(self,fn=None,ss=None,seed=None,
+					ignore_in_fmt=False,passchg=False):
 
 
 		self.ssdata = self.SeedSourceData()
 		self.ssdata = self.SeedSourceData()
 		self.msg = {}
 		self.msg = {}
@@ -132,12 +135,13 @@ class SeedSource(MMGenObject):
 			self._deformat_retry()
 			self._deformat_retry()
 			self._decrypt_retry()
 			self._decrypt_retry()
 
 
-		m = (""," length %s" % self.seed.length)[int(self.seed.length != 256)]
-		qmsg("Valid %s for seed ID %s%s" % (self.desc,self.seed.sid,m))
+		m = ("",", seed length %s" % self.seed.length)[int(self.seed.length != 256)]
+		qmsg("Valid %s for Seed ID %s%s" % (self.desc,self.seed.sid,m))
 
 
 	def _get_data(self):
 	def _get_data(self):
 		if hasattr(self,'infile'):
 		if hasattr(self,'infile'):
-			self.fmt_data = get_data_from_file(self.infile.name,self.desc)
+			self.fmt_data = get_data_from_file(self.infile.name,self.desc,
+								binary=self.file_mode=="binary")
 		else:
 		else:
 			self.fmt_data = get_data_from_user(self.desc)
 			self.fmt_data = get_data_from_user(self.desc)
 
 
@@ -204,12 +208,17 @@ class SeedSource(MMGenObject):
 			] + sorted(d)]
 			] + sorted(d)]
 		return "\n".join(ret) + "\n"
 		return "\n".join(ret) + "\n"
 
 
+	def get_fmt_data(self):
+		self._format()
+		return self.fmt_data
+
 	def write_to_file(self):
 	def write_to_file(self):
 		self._format()
 		self._format()
 		kwargs = {
 		kwargs = {
 			'desc':     self.desc,
 			'desc':     self.desc,
 			'ask_tty':  self.ask_tty,
 			'ask_tty':  self.ask_tty,
-			'no_tty':   self.no_tty
+			'no_tty':   self.no_tty,
+			'binary':   self.file_mode == "binary"
 		}
 		}
 		write_data_to_file(self._filename(),self.fmt_data,**kwargs)
 		write_data_to_file(self._filename(),self.fmt_data,**kwargs)
 
 
@@ -358,43 +367,70 @@ class Mnemonic (SeedSourceUnenc):
 	mn_base = 1626
 	mn_base = 1626
 	wordlists = sorted(wl_checksums)
 	wordlists = sorted(wl_checksums)
 
 
-	def _mn2hex_pad(self,mn): return len(mn) * 8 / 3
-	def _hex2mn_pad(self,hexnum): return len(hexnum) * 3 / 8
+	@staticmethod
+	def _mn2hex_pad(mn): return len(mn) * 8 / 3
+
+	@staticmethod
+	def _hex2mn_pad(hexnum): return len(hexnum) * 3 / 8
 
 
-	def _baseNtohex(self,base,words,wl,pad=0):
+	@staticmethod
+	def baseNtohex(base,words,wl,pad=0):
 		deconv =  [wl.index(words[::-1][i])*(base**i)
 		deconv =  [wl.index(words[::-1][i])*(base**i)
 					for i in range(len(words))]
 					for i in range(len(words))]
 		ret = ("{:0%sx}" % pad).format(sum(deconv))
 		ret = ("{:0%sx}" % pad).format(sum(deconv))
 		return ('','0')[len(ret) % 2] + ret
 		return ('','0')[len(ret) % 2] + ret
 
 
-	def _hextobaseN(self,base,hexnum,wl,pad=0):
+	@staticmethod
+	def hextobaseN(base,hexnum,wl,pad=0):
 		num,ret = int(hexnum,16),[]
 		num,ret = int(hexnum,16),[]
 		while num:
 		while num:
 			ret.append(num % base)
 			ret.append(num % base)
 			num /= base
 			num /= base
 		return [wl[n] for n in [0] * (pad-len(ret)) + ret[::-1]]
 		return [wl[n] for n in [0] * (pad-len(ret)) + ret[::-1]]
 
 
-	def _get_wordlist(self,wordlist=g.default_wordlist):
-		wordlist = wordlist.lower()
-		if wordlist not in self.wordlists:
+	@classmethod
+	def hex2mn(cls,hexnum,wordlist):
+		wl = cls.get_wordlist(wordlist)
+		return cls.hextobaseN(cls.mn_base,hexnum,wl,cls._hex2mn_pad(hexnum))
+
+	@classmethod
+	def mn2hex(cls,mn,wordlist):
+		wl = cls.get_wordlist(wordlist)
+		return cls.baseNtohex(cls.mn_base,mn,wl,cls._mn2hex_pad(mn))
+
+	@classmethod
+	def get_wordlist(cls,wordlist=None):
+		wordlist = wordlist or g.default_wordlist
+		if wordlist not in cls.wordlists:
 			die(1,'"%s": invalid wordlist.  Valid choices: %s' %
 			die(1,'"%s": invalid wordlist.  Valid choices: %s' %
-				(wordlist,'"'+'" "'.join(self.wordlists)+'"'))
+				(wordlist,'"'+'" "'.join(cls.wordlists)+'"'))
+
+		return __import__("mmgen.mn_"+wordlist,fromlist=["words"]).words.split()
+
+	@classmethod
+	def check_wordlist(cls,wlname):
+
+		wl = cls.get_wordlist(wlname)
+		Msg("Wordlist: %s\nLength: %i words" % (capfirst(wlname),len(wl)))
+		new_chksum = sha256(" ".join(wl)).hexdigest()[:8]
 
 
-		if wordlist == "electrum":
-			from mmgen.mn_electrum  import words
-		elif wordlist == "tirosh":
-			from mmgen.mn_tirosh    import words
+		if (sorted(wl) == wl):
+			Msg("List is sorted")
 		else:
 		else:
-			die(3,"Internal error: unknown wordlist")
+			die(3,"ERROR: List is not sorted!")
 
 
-		return words.strip().split("\n")
+		compare_chksums(
+			new_chksum,"generated checksum",
+			cls.wl_checksums[wlname],"saved checksum",
+			die_on_fail=True)
+		Msg("Checksum %s matches" % new_chksum)
 
 
 	def _format(self):
 	def _format(self):
-		wl = self._get_wordlist()
+		wl = self.get_wordlist()
 		seed_hex = self.seed.hexdata
 		seed_hex = self.seed.hexdata
-		mn = self._hextobaseN(self.mn_base,seed_hex,wl,self._hex2mn_pad(seed_hex))
+		mn = self.hextobaseN(self.mn_base,seed_hex,wl,self._hex2mn_pad(seed_hex))
 
 
-		ret = self._baseNtohex(self.mn_base,mn,wl,self._mn2hex_pad(mn))
+		ret = self.baseNtohex(self.mn_base,mn,wl,self._mn2hex_pad(mn))
 		# Internal error, so just die on fail
 		# Internal error, so just die on fail
 		compare_or_die(ret,"recomputed seed",
 		compare_or_die(ret,"recomputed seed",
 						seed_hex,"original",e="Internal error")
 						seed_hex,"original",e="Internal error")
@@ -405,7 +441,7 @@ class Mnemonic (SeedSourceUnenc):
 	def _deformat(self):
 	def _deformat(self):
 
 
 		mn = self.fmt_data.split()
 		mn = self.fmt_data.split()
-		wl = self._get_wordlist()
+		wl = self.get_wordlist()
 
 
 		if len(mn) not in g.mn_lens:
 		if len(mn) not in g.mn_lens:
 			msg("Invalid mnemonic (%i words).  Allowed numbers of words: %s" %
 			msg("Invalid mnemonic (%i words).  Allowed numbers of words: %s" %
@@ -417,9 +453,9 @@ class Mnemonic (SeedSourceUnenc):
 				msg("Invalid mnemonic: word #%s is not in the wordlist" % n)
 				msg("Invalid mnemonic: word #%s is not in the wordlist" % n)
 				return False
 				return False
 
 
-		seed_hex = self._baseNtohex(self.mn_base,mn,wl,self._mn2hex_pad(mn))
+		seed_hex = self.baseNtohex(self.mn_base,mn,wl,self._mn2hex_pad(mn))
 
 
-		ret = self._hextobaseN(self.mn_base,seed_hex,wl,self._hex2mn_pad(seed_hex))
+		ret = self.hextobaseN(self.mn_base,seed_hex,wl,self._hex2mn_pad(seed_hex))
 
 
 		# Internal error, so just die
 		# Internal error, so just die
 		compare_or_die(" ".join(ret),"recomputed mnemonic",
 		compare_or_die(" ".join(ret),"recomputed mnemonic",
@@ -580,7 +616,7 @@ class Wallet (SeedSourceEnc):
 
 
 			return True
 			return True
 
 
-		lines = self.fmt_data.rstrip().split("\n")
+		lines = self.fmt_data.splitlines()
 		if not check_master_chksum(lines,self.desc): return False
 		if not check_master_chksum(lines,self.desc): return False
 
 
 		d = self.ssdata
 		d = self.ssdata
@@ -723,6 +759,7 @@ class Brainwallet (SeedSourceEnc):
 
 
 class IncogWallet (SeedSourceEnc):
 class IncogWallet (SeedSourceEnc):
 
 
+	file_mode = "binary"
 	fmt_codes = "mmincog","incog","icg","i"
 	fmt_codes = "mmincog","incog","icg","i"
 	desc = "incognito data"
 	desc = "incognito data"
 	ext = "mmincog"
 	ext = "mmincog"
@@ -742,7 +779,7 @@ Incorrect passphrase, hash preset, or maybe old-format incog wallet.
 Try again? (Y)es, (n)o, (m)ore information:
 Try again? (Y)es, (n)o, (m)ore information:
 """.strip(),
 """.strip(),
 		'confirm_seed_id': """
 		'confirm_seed_id': """
-If the seed ID above is correct but you're seeing this message, then you need
+If the Seed ID above is correct but you're seeing this message, then you need
 to exit and re-run the program with the '--old-incog-fmt' option.
 to exit and re-run the program with the '--old-incog-fmt' option.
 """.strip(),
 """.strip(),
 		'dec_chk': " %s hash preset"
 		'dec_chk': " %s hash preset"
@@ -841,7 +878,7 @@ to exit and re-run the program with the '--old-incog-fmt' option.
 			return False
 			return False
 
 
 	def _verify_seed_oldfmt(self,seed):
 	def _verify_seed_oldfmt(self,seed):
-		m = "Seed ID: %s.  Is the seed ID correct?" % make_chksum_8(seed)
+		m = "Seed ID: %s.  Is the Seed ID correct?" % make_chksum_8(seed)
 		if keypress_confirm(m, True):
 		if keypress_confirm(m, True):
 			return seed
 			return seed
 		else:
 		else:
@@ -878,6 +915,7 @@ to exit and re-run the program with the '--old-incog-fmt' option.
 
 
 class IncogWalletHex (IncogWallet):
 class IncogWalletHex (IncogWallet):
 
 
+	file_mode = "text"
 	desc = "hex incognito data"
 	desc = "hex incognito data"
 	fmt_codes = "mmincox","incox","incog_hex","xincog","ix","xi"
 	fmt_codes = "mmincox","incox","incog_hex","xincog","ix","xi"
 	ext = "mmincox"
 	ext = "mmincox"

+ 3 - 3
mmgen/share/Opts.py

@@ -28,9 +28,9 @@ def print_help(opts_data):
 	print ("  %-"+pn_len+"s %s") % (pn.upper()+":", opts_data['desc'].strip())
 	print ("  %-"+pn_len+"s %s") % (pn.upper()+":", opts_data['desc'].strip())
 	print ("  %-"+pn_len+"s %s %s")%("USAGE:", pn, opts_data['usage'].strip())
 	print ("  %-"+pn_len+"s %s %s")%("USAGE:", pn, opts_data['usage'].strip())
 	sep = "\n    "
 	sep = "\n    "
-	print "  OPTIONS:"+sep+"%s" % sep.join(opts_data['options'].strip().split("\n"))
+	print "  OPTIONS:"+sep+"%s" % sep.join(opts_data['options'].strip().splitlines())
 	if "notes" in opts_data:
 	if "notes" in opts_data:
-		print "  %s" % "\n  ".join(opts_data['notes'][1:-1].split("\n"))
+		print "  %s" % "\n  ".join(opts_data['notes'][1:-1].splitlines())
 
 
 
 
 def process_opts(argv,opts_data,short_opts,long_opts):
 def process_opts(argv,opts_data,short_opts,long_opts):
@@ -86,7 +86,7 @@ def parse_opts(argv,opts_data,opt_filter=None):
 	pat = r"^-([a-zA-Z0-9]), --([a-zA-Z0-9-]{2,64})(=| )(.+)"
 	pat = r"^-([a-zA-Z0-9]), --([a-zA-Z0-9-]{2,64})(=| )(.+)"
 	od,skip = [],True
 	od,skip = [],True
 
 
-	for l in opts_data['options'].strip().split("\n"):
+	for l in opts_data['options'].strip().splitlines():
 		m = re.match(pat,l)
 		m = re.match(pat,l)
 		if m:
 		if m:
 			skip = True if (opt_filter and m.group(1) not in opt_filter) else False
 			skip = True if (opt_filter and m.group(1) not in opt_filter) else False

+ 12 - 8
mmgen/test.py

@@ -22,7 +22,7 @@ test.py:  Shared routines for the test suites
 
 
 import sys,os
 import sys,os
 from binascii import hexlify
 from binascii import hexlify
-from mmgen.util import msg,write_to_file,red,green
+from mmgen.util import msg,write_data_to_file,red,green
 import mmgen.opt as opt
 import mmgen.opt as opt
 
 
 def cleandir(d):
 def cleandir(d):
@@ -49,16 +49,20 @@ def mk_tmpdir(cfg):
 def get_tmpfile_fn(cfg,fn):
 def get_tmpfile_fn(cfg,fn):
 	return os.path.join(cfg['tmpdir'],fn)
 	return os.path.join(cfg['tmpdir'],fn)
 
 
-def write_to_tmpfile(cfg,fn,data,mode='wb'):
-	write_to_file(os.path.join(cfg['tmpdir'],fn),data,silent=True,mode=mode)
+def write_to_tmpfile(cfg,fn,data,binary=False):
+	write_data_to_file(
+		os.path.join(cfg['tmpdir'],fn),
+		data,
+		silent=True,
+		binary=binary
+	)
 
 
-def read_from_tmpfile(cfg,fn):
+def read_from_file(fn,binary=False):
 	from mmgen.util import get_data_from_file
 	from mmgen.util import get_data_from_file
-	return get_data_from_file(os.path.join(cfg['tmpdir'],fn),silent=True)
+	return get_data_from_file(fn,silent=True,binary=binary)
 
 
-def read_from_file(fn):
-	from mmgen.util import get_data_from_file
-	return get_data_from_file(fn,silent=True)
+def read_from_tmpfile(cfg,fn,binary=False):
+	return read_from_file(os.path.join(cfg['tmpdir'],fn),binary=binary)
 
 
 def ok():
 def ok():
 	if opt.verbose or opt.exact_output:
 	if opt.verbose or opt.exact_output:

+ 53 - 45
mmgen/tool.py

@@ -252,8 +252,9 @@ def usage(cmd):
 help = usage
 help = usage
 
 
 def hexdump(infile, cols=8, line_nums=True):
 def hexdump(infile, cols=8, line_nums=True):
-	Msg(pretty_hexdump(get_data_from_file(infile,dash=True,silent=True),
-			cols=cols, line_nums=line_nums))
+	Msg(pretty_hexdump(
+			get_data_from_file(infile,dash=True,silent=True,binary=True),
+				cols=cols,line_nums=line_nums))
 
 
 def unhexdump(infile):
 def unhexdump(infile):
 	if sys.platform[:3] == "win":
 	if sys.platform[:3] == "win":
@@ -316,42 +317,54 @@ def wif2addr(wif,compressed=False):
 	addr = bitcoin.privnum2addr(int(s_enc,16),compressed)
 	addr = bitcoin.privnum2addr(int(s_enc,16),compressed)
 	Vmsg_r("Addr: "); Msg(addr)
 	Vmsg_r("Addr: "); Msg(addr)
 
 
-from mmgen.mnemonic import *
+wordlists = "electrum","tirosh"
+dfl_wordlist = "electrum"
 
 
-def mn_rand128(wordlist="electrum"): do_random_mn(16,wordlist)
-def mn_rand192(wordlist="electrum"): do_random_mn(24,wordlist)
-def mn_rand256(wordlist="electrum"): do_random_mn(32,wordlist)
+from mmgen.seed import Mnemonic
+def do_random_mn(nbytes,wordlist):
+	hexrand = ba.hexlify(get_random(nbytes))
+	Vmsg("Seed: %s" % hexrand)
+	for wlname in (wordlists if wordlist == "all" else [wordlist]):
+		if wordlist == "all":
+			Msg("%s mnemonic:" % (wlname.capitalize()))
+		mn = Mnemonic.hex2mn(hexrand,wordlist=wlname)
+		Msg(" ".join(mn))
 
 
-def hex2mn(s,wordlist="electrum"):
-	import mmgen.mnemonic
-	wl = get_wordlist(wordlist)
-	Msg(" ".join(get_mnemonic_from_seed(ba.unhexlify(s), wl, wordlist)))
+def mn_rand128(wordlist=dfl_wordlist): do_random_mn(16,wordlist)
+def mn_rand192(wordlist=dfl_wordlist): do_random_mn(24,wordlist)
+def mn_rand256(wordlist=dfl_wordlist): do_random_mn(32,wordlist)
 
 
-def mn2hex(s,wordlist="electrum"):
-	import mmgen.mnemonic
-	wl = get_wordlist(wordlist)
-	Msg(ba.hexlify(get_seed_from_mnemonic(s.split(),wl,True)))
+def hex2mn(s,wordlist=dfl_wordlist):
+	mn = Mnemonic.hex2mn(s,wordlist)
+	Msg(" ".join(mn))
+
+def mn2hex(s,wordlist=dfl_wordlist):
+	hexnum = Mnemonic.mn2hex(s.split(),wordlist)
+	Msg(hexnum)
 
 
 def b32tohex(s):
 def b32tohex(s):
 	b32a = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567"
 	b32a = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567"
-	import mmgen.mnemonic
-	Msg(baseNtohex(32,s,b32a))
+	Msg(Mnemonic.baseNtohex(32,s,b32a))
 
 
 def hextob32(s):
 def hextob32(s):
 	b32a = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567"
 	b32a = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567"
-	import mmgen.mnemonic
-	Msg("".join(hextobaseN(32,s,b32a)))
+	Msg("".join(Mnemonic.hextobaseN(32,s,b32a)))
 
 
-def mn_stats(wordlist="electrum"):
-	l = get_wordlist(wordlist)
-	check_wordlist(l,wordlist)
+def mn_stats(wordlist=dfl_wordlist):
+	Mnemonic.check_wordlist(wordlist)
 
 
-def mn_printlist(wordlist="electrum"):
-	wl = get_wordlist(wordlist)
+def mn_printlist(wordlist=dfl_wordlist):
+	wl = Mnemonic.get_wordlist(wordlist)
 	Msg("\n".join(wl))
 	Msg("\n".join(wl))
 
 
-def id8(infile): Msg(make_chksum_8(get_data_from_file(infile,dash=True,silent=True)))
-def id6(infile): Msg(make_chksum_6(get_data_from_file(infile,dash=True,silent=True)))
+def id8(infile):
+	Msg(make_chksum_8(
+		get_data_from_file(infile,dash=True,silent=True,binary=True)
+	))
+def id6(infile):
+	Msg(make_chksum_6(
+		get_data_from_file(infile,dash=True,silent=True,binary=True)
+	))
 def str2id6(s):  Msg(make_chksum_6("".join(s.split())))
 def str2id6(s):  Msg(make_chksum_6("".join(s.split())))
 
 
 # List MMGen addresses and their balances:
 # List MMGen addresses and their balances:
@@ -478,7 +491,7 @@ def hexlify(s):
 
 
 def sha256x2(s, file_input=False, hex_input=False):
 def sha256x2(s, file_input=False, hex_input=False):
 	from hashlib import sha256
 	from hashlib import sha256
-	if file_input:  b = get_data_from_file(s)
+	if file_input:  b = get_data_from_file(s,binary=True)
 	elif hex_input: b = decode_pretty_hexdump(s)
 	elif hex_input: b = decode_pretty_hexdump(s)
 	else:           b = s
 	else:           b = s
 	Msg(sha256(sha256(b).digest()).hexdigest())
 	Msg(sha256(sha256(b).digest()).hexdigest())
@@ -506,32 +519,27 @@ def hex2wif(hexpriv,compressed=False):
 
 
 
 
 def encrypt(infile,outfile="",hash_preset=""):
 def encrypt(infile,outfile="",hash_preset=""):
-	data = get_data_from_file(infile,"data for encryption")
+	data = get_data_from_file(infile,"data for encryption",binary=True)
 	enc_d = mmgen_encrypt(data,"user data",hash_preset)
 	enc_d = mmgen_encrypt(data,"user data",hash_preset)
-	if outfile == '-':
-		write_to_stdout(enc_d,"encrypted data")
-	else:
-		if not outfile:
-			outfile = os.path.basename(infile) + "." + g.mmenc_ext
-		write_to_file(outfile,enc_d,"encrypted data",True,True)
+	if not outfile:
+		outfile = "%s.%s" % (os.path.basename(infile),g.mmenc_ext)
+
+	write_data_to_file(outfile,enc_d,"encrypted data",binary=True)
 
 
 
 
 def decrypt(infile,outfile="",hash_preset=""):
 def decrypt(infile,outfile="",hash_preset=""):
-	enc_d = get_data_from_file(infile,"encrypted data")
+	enc_d = get_data_from_file(infile,"encrypted data",binary=True)
 	while True:
 	while True:
 		dec_d = mmgen_decrypt(enc_d,"user data",hash_preset)
 		dec_d = mmgen_decrypt(enc_d,"user data",hash_preset)
 		if dec_d: break
 		if dec_d: break
 		msg("Trying again...")
 		msg("Trying again...")
-	if outfile == '-':
-		write_to_stdout(dec_d,"decrypted data",ask_terminal=not opt.quiet)
-	else:
-		if not outfile:
-			outfile = os.path.basename(infile)
-			if outfile[-len(g.mmenc_ext)-1:] == "."+g.mmenc_ext:
-				outfile = outfile[:-len(g.mmenc_ext)-1]
-			else:
-				outfile = outfile + ".dec"
-		write_to_file(outfile, dec_d, "decrypted data",True,True)
+
+	if not outfile:
+		o = os.path.basename(infile)
+		outfile = remove_extension(o,g.mmenc_ext)
+		if outfile == o: outfile += ".dec"
+
+	write_data_to_file(outfile,dec_d,"decrypted data",binary=True)
 
 
 
 
 def find_incog_data(filename,iv_id,keep_searching=False):
 def find_incog_data(filename,iv_id,keep_searching=False):
@@ -618,4 +626,4 @@ def rand2file(outfile, nbytes, threads=4, silent=False):
 	q2.join()
 	q2.join()
 	f.close()
 	f.close()
 
 
-def bytespec(s): Msg(parse_nbytes(s))
+def bytespec(s): Msg(str(parse_nbytes(s)))

+ 51 - 195
mmgen/util.py

@@ -29,11 +29,12 @@ import mmgen.globalvars as g
 
 
 pnm = g.proj_name
 pnm = g.proj_name
 
 
-_red,_grn,_yel,_cya,_reset = \
-	["\033[%sm" % c for c in "31;1","32;1","33;1","36;1","0"]
+_red,_grn,_yel,_cya,_reset,_grnbg = \
+	["\033[%sm" % c for c in "31;1","32;1","33;1","36;1","0","30;102"]
 
 
 def red(s):     return _red+s+_reset
 def red(s):     return _red+s+_reset
 def green(s):   return _grn+s+_reset
 def green(s):   return _grn+s+_reset
+def grnbg(s):    return _grnbg+s+_reset
 def yellow(s):  return _yel+s+_reset
 def yellow(s):  return _yel+s+_reset
 def cyan(s):    return _cya+s+_reset
 def cyan(s):    return _cya+s+_reset
 def nocolor(s): return s
 def nocolor(s): return s
@@ -43,13 +44,13 @@ def start_mscolor():
 		global red,green,yellow,cyan,nocolor
 		global red,green,yellow,cyan,nocolor
 		import os
 		import os
 		if "MMGEN_NOMSCOLOR" in os.environ:
 		if "MMGEN_NOMSCOLOR" in os.environ:
-			red = green = yellow = cyan = nocolor
+			red = green = yellow = cyan = grnbg = nocolor
 		else:
 		else:
 			try:
 			try:
 				import colorama
 				import colorama
 				colorama.init(strip=True,convert=True)
 				colorama.init(strip=True,convert=True)
 			except:
 			except:
-				red = green = yellow = cyan = nocolor
+				red = green = yellow = cyan = grnbg = nocolor
 
 
 def msg(s):    sys.stderr.write(s+"\n")
 def msg(s):    sys.stderr.write(s+"\n")
 def msg_r(s):  sys.stderr.write(s)
 def msg_r(s):  sys.stderr.write(s)
@@ -144,7 +145,12 @@ def suf(arg,suf_type):
 		return "" if n == 1 else "s"
 		return "" if n == 1 else "s"
 
 
 def get_extension(f):
 def get_extension(f):
-	return os.path.splitext(f)[1][1:]
+	a,b = os.path.splitext(f)
+	return ('',b[1:])[int(len(b) > 1)]
+
+def remove_extension(f,e):
+	a,b = os.path.splitext(f)
+	return (f,a)[int(len(b) > 1 and b[1:] == e)]
 
 
 def make_chksum_N(s,nchars,sep=False):
 def make_chksum_N(s,nchars,sep=False):
 	if nchars%4 or not (4 <= nchars <= 64): return False
 	if nchars%4 or not (4 <= nchars <= 64): return False
@@ -208,6 +214,11 @@ def is_utf8(s):
 	except: return False
 	except: return False
 	else: return True
 	else: return True
 
 
+def is_ascii(s):
+	try: s.decode("ascii")
+	except: return False
+	else: return True
+
 def match_ext(addr,ext):
 def match_ext(addr,ext):
 	return addr.split(".")[-1] == ext
 	return addr.split(".")[-1] == ext
 
 
@@ -235,7 +246,8 @@ def pretty_hexdump(data,gw=2,cols=8,line_nums=False):
 
 
 def decode_pretty_hexdump(data):
 def decode_pretty_hexdump(data):
 	from string import hexdigits
 	from string import hexdigits
-	lines = [re.sub('^['+hexdigits+']+:\s+','',l) for l in data.split("\n")]
+	pat = r'^[%s]+:\s+' % hexdigits
+	lines = [re.sub(pat,'',l) for l in data.splitlines()]
 	try:
 	try:
 		return unhexlify("".join(("".join(lines).split())))
 		return unhexlify("".join(("".join(lines).split())))
 	except:
 	except:
@@ -270,20 +282,12 @@ def compare_or_die(val1, desc1, val2, desc2, e="Error"):
 	dmsg("%s OK (%s)" % (capfirst(desc2),val2))
 	dmsg("%s OK (%s)" % (capfirst(desc2),val2))
 	return True
 	return True
 
 
-def get_default_wordlist():
-
-	wl_id = g.default_wordlist
-	if wl_id == "electrum": from mmgen.mn_electrum import words as wl
-	elif wl_id == "tirosh": from mmgen.mn_tirosh   import words as wl
-	return wl.strip().split("\n")
-
 def open_file_or_exit(filename,mode):
 def open_file_or_exit(filename,mode):
 	try:
 	try:
 		f = open(filename, mode)
 		f = open(filename, mode)
 	except:
 	except:
-		op = "reading" if 'r' in mode else "writing"
-		msg("Unable to open file '%s' for %s" % (filename,op))
-		sys.exit(2)
+		op = ("writing","reading")[int('r' in mode)]
+		die(2,"Unable to open file '%s' for %s" % (filename,op))
 	return f
 	return f
 
 
 
 
@@ -408,19 +412,6 @@ def confirm_or_exit(message, question, expect="YES"):
 		die(2,"Exiting at user request")
 		die(2,"Exiting at user request")
 
 
 
 
-def write_to_stdout(data, desc, ask_terminal=True):
-	if sys.stdout.isatty() and ask_terminal:
-		confirm_or_exit("",'output {} to screen'.format(desc))
-	elif not sys.stdout.isatty():
-		try:
-			of = os.readlink("/proc/%d/fd/1" % os.getpid())
-			of_maybe = os.path.relpath(of)
-			of = of if of_maybe.find(os.path.pardir) == 0 else of_maybe
-			msg("Redirecting output to file '%s'" % of)
-		except:
-			msg("Redirecting output to file")
-	sys.stdout.write(data)
-
 # New function
 # New function
 def write_data_to_file(
 def write_data_to_file(
 		outfile,
 		outfile,
@@ -428,19 +419,26 @@ def write_data_to_file(
 		desc="data",
 		desc="data",
 		ask_write=False,
 		ask_write=False,
 		ask_write_prompt="",
 		ask_write_prompt="",
-		ask_write_default_yes=False,
+		ask_write_default_yes=True,
 		ask_overwrite=True,
 		ask_overwrite=True,
 		ask_tty=True,
 		ask_tty=True,
 		no_tty=False,
 		no_tty=False,
-		silent=False
+		silent=False,
+		binary=False
 	):
 	):
-	if opt.stdout or not sys.stdout.isatty():
+
+	if silent: ask_tty = ask_overwrite = False
+	if opt.quiet: ask_overwrite = False
+
+	if ask_write_default_yes == False or ask_write_prompt:
+		ask_write = True
+
+	if opt.stdout or not sys.stdout.isatty() or outfile in ('','-'):
 		qmsg("Output to STDOUT requested")
 		qmsg("Output to STDOUT requested")
-		write_ok = False
 		if sys.stdout.isatty():
 		if sys.stdout.isatty():
 			if no_tty:
 			if no_tty:
 				die(2,"Printing %s to screen is not allowed" % desc)
 				die(2,"Printing %s to screen is not allowed" % desc)
-			if ask_tty:
+			if ask_tty and not opt.quiet:
 				confirm_or_exit("",'output %s to screen' % desc)
 				confirm_or_exit("",'output %s to screen' % desc)
 		else:
 		else:
 			try:    of = os.readlink("/proc/%d/fd/1" % os.getpid()) # Linux
 			try:    of = os.readlink("/proc/%d/fd/1" % os.getpid()) # Linux
@@ -450,7 +448,7 @@ def write_data_to_file(
 				if of[:5] == "pipe:":
 				if of[:5] == "pipe:":
 					if no_tty:
 					if no_tty:
 						die(2,"Writing %s to pipe is not allowed" % desc)
 						die(2,"Writing %s to pipe is not allowed" % desc)
-					if ask_tty:
+					if ask_tty and not opt.quiet:
 						confirm_or_exit("",'output %s to pipe' % desc)
 						confirm_or_exit("",'output %s to pipe' % desc)
 						msg("")
 						msg("")
 				of2,pd = os.path.relpath(of),os.path.pardir
 				of2,pd = os.path.relpath(of),os.path.pardir
@@ -459,138 +457,41 @@ def write_data_to_file(
 			else:
 			else:
 				msg("Redirecting output to file")
 				msg("Redirecting output to file")
 
 
+		if binary and sys.platform[:3] == "win":
+			import msvcrt
+			msvcrt.setmode(sys.stdout.fileno(),os.O_BINARY)
+
 		sys.stdout.write(data)
 		sys.stdout.write(data)
 	else:
 	else:
 		if opt.outdir: outfile = make_full_path(opt.outdir,outfile)
 		if opt.outdir: outfile = make_full_path(opt.outdir,outfile)
 
 
 		if ask_write:
 		if ask_write:
+			if not ask_write_prompt: ask_write_prompt = "Save %s?" % desc
 			if not keypress_confirm(ask_write_prompt,
 			if not keypress_confirm(ask_write_prompt,
 						default_yes=ask_write_default_yes):
 						default_yes=ask_write_default_yes):
-				die(1,"Exiting at user request")
+				die(1,"%s not saved" % capfirst(desc))
 
 
 		hush = False
 		hush = False
-		if file_exists(outfile):
-			if ask_overwrite and not silent:
-					q = "File '%s' already exists\nOverwrite?" % outfile
-					confirm_or_exit("",q)
-					msg("Overwriting file '%s'" % outfile)
+		if file_exists(outfile) and ask_overwrite:
+			q = "File '%s' already exists\nOverwrite?" % outfile
+			confirm_or_exit("",q)
+			msg("Overwriting file '%s'" % outfile)
 			hush = True
 			hush = True
 
 
-		f = open_file_or_exit(outfile,'wb')
+		f = open_file_or_exit(outfile,'w'+('','b')[int(binary)])
 		try:
 		try:
 			f.write(data)
 			f.write(data)
 		except:
 		except:
-			if not silent: msg("Failed to write %s to file '%s'" % (desc,outfile))
-			sys.exit(2)
+			die(2,"Failed to write %s to file '%s'" % (desc,outfile))
 		f.close
 		f.close
 
 
-		if not hush:
+		if not (hush or silent):
 			msg("%s written to file '%s'" % (capfirst(desc),outfile))
 			msg("%s written to file '%s'" % (capfirst(desc),outfile))
 
 
 		return True
 		return True
 
 
-
-def write_to_file(
-		outfile,
-		data,
-		desc="data",
-		confirm_overwrite=False,
-		verbose=False,
-		silent=False,
-		mode='wb'
-	):
-
-	if opt.outdir: outfile = make_full_path(opt.outdir,outfile)
-
-	try:    os.stat(outfile)
-	except: pass
-	else:
-		if confirm_overwrite:
-			q = "File '%s' already exists\nOverwrite?" % outfile
-			confirm_or_exit("",q)
-		else:
-			if not silent: msg("Overwriting file '%s'" % outfile)
-
-	f = open_file_or_exit(outfile,mode)
-	try:
-		f.write(data)
-	except:
-		if not silent: msg("Failed to write %s to file '%s'" % (desc,outfile))
-		sys.exit(2)
-	f.close
-
-	if verbose: msg("%s written to file '%s'" % (capfirst(desc),outfile))
-	return True
-
-
-def write_to_file_or_stdout(outfile, data,  desc="data"):
-
-	if opt.stdout or not sys.stdout.isatty():
-		write_to_stdout(data, desc)
-	else:
-		write_to_file(outfile,data,desc,not opt.quiet,True)
-
-
 from mmgen.bitcoin import b58decode_pad,b58encode_pad
 from mmgen.bitcoin import b58decode_pad,b58encode_pad
 
 
-def display_control_data(label,metadata,hash_preset,salt,enc_seed):
-	Msg("WALLET DATA")
-	fs = "  {:18} {}"
-	pw_empty = "yes" if metadata[3] == "E" else "no"
-	for i in (
-		("Label:",               label),
-		("Seed ID:",             metadata[0].upper()),
-		("Key  ID:",             metadata[1].upper()),
-		("Seed length:",         "%s bits (%s bytes)" %
-				(metadata[2],int(metadata[2])/8)),
-		("Scrypt params:",  "Preset '%s' (%s)" % (hash_preset,
-				" ".join([str(i) for i in get_hash_params(hash_preset)]))),
-		("Passphrase empty?", pw_empty.capitalize()),
-		("Timestamp:",           "%s UTC" % metadata[4]),
-	): Msg(fs.format(*i))
-
-	fs = "  {:6} {}"
-	for i in (
-		("Salt:",    ""),
-		("  b58:",      b58encode_pad(salt)),
-		("  hex:",      hexlify(salt)),
-		("Encrypted seed:", ""),
-		("  b58:",      b58encode_pad(enc_seed)),
-		("  hex:",      hexlify(enc_seed))
-	): Msg(fs.format(*i))
-
-
-def write_wallet_to_file(seed, passwd, key_id, salt, enc_seed):
-
-	seed_id = make_chksum_8(seed)
-	seed_len = str(len(seed)*8)
-	pw_status = "NE" if len(passwd) else "E"
-	hash_preset = opt.hash_preset
-	label = opt.label or "No Label"
-	metadata = seed_id.lower(),key_id.lower(),seed_len,\
-		pw_status,make_timestamp()
-	sf  = b58encode_pad(salt)
-	esf = b58encode_pad(enc_seed)
-
-	lines = (
-		label,
-		"{} {} {} {} {}".format(*metadata),
-		"{}: {} {} {}".format(hash_preset,*get_hash_params(hash_preset)),
-		"{} {}".format(make_chksum_6(sf),  split_into_cols(4,sf)),
-		"{} {}".format(make_chksum_6(esf), split_into_cols(4,esf))
-	)
-
-	chk = make_chksum_6(" ".join(lines))
-	outfile="{}-{}[{},{}].{}".format(
-		seed_id,key_id,seed_len,hash_preset,g.wallet_ext)
-
-	d = "\n".join((chk,)+lines)+"\n"
-	write_to_file(outfile,d,"wallet",not opt.quiet,True)
-
-	if opt.debug:
-		display_control_data(label,metadata,hash_preset,salt,enc_seed)
-
-
 def _check_mmseed_format(words):
 def _check_mmseed_format(words):
 
 
 	valid = False
 	valid = False
@@ -638,50 +539,6 @@ def _check_chksum_6(chk,val,desc,infile):
 	dmsg("%s checksum passed: %s" % (capfirst(desc),chk))
 	dmsg("%s checksum passed: %s" % (capfirst(desc),chk))
 
 
 
 
-def get_data_from_wallet(infile,silent=False):
-
-	# Don't make this a qmsg: User will be prompted for passphrase and must see
-	# the filename.
-	if not silent and not opt.quiet:
-		msg("Getting {pnm} wallet data from file '{f}'".format(pnm=pnm,f=infile))
-
-	f = open_file_or_exit(infile, 'r')
-
-	lines = [i.strip() for i in f.readlines()]
-	f.close()
-
-	_check_wallet_format(infile, lines)
-
-	label = lines[1]
-
-	metadata = lines[2].split()
-
-	for i in 0,1: metadata[i] = metadata[i].upper()
-
-	hd = lines[3].split()
-	hash_preset = hd[0][:-1]
-	hash_params = [int(i) for i in hd[1:]]
-
-	if hash_params != get_hash_params(hash_preset):
-		msg("Hash parameters '%s' don't match hash preset '%s'" %
-				(" ".join(hash_params), hash_preset))
-		sys.exit(9)
-
-	res = {}
-	for i,key in (4,"salt"),(5,"enc_seed"):
-		l = lines[i].split()
-		val = "".join(l[1:])
-		_check_chksum_6(l[0], val, key, infile)
-		res[key] = b58decode_pad(val)
-		if res[key] == False:
-			msg("Invalid b58 number: %s" % val)
-			sys.exit(9)
-
-	_check_chksum_6(lines[0], " ".join(lines[1:]), "Master", infile)
-
-	return label,metadata,hash_preset,res['salt'],res['enc_seed']
-
-
 def get_words_from_user(prompt):
 def get_words_from_user(prompt):
 	# split() also strips
 	# split() also strips
 	words = my_raw_input(prompt, echo=opt.echo_passphrase).split()
 	words = my_raw_input(prompt, echo=opt.echo_passphrase).split()
@@ -719,7 +576,7 @@ def get_lines_from_file(infile,desc="",trim_comments=False):
 	if desc != "":
 	if desc != "":
 		qmsg("Getting %s from file '%s'" % (desc,infile))
 		qmsg("Getting %s from file '%s'" % (desc,infile))
 	f = open_file_or_exit(infile,'r')
 	f = open_file_or_exit(infile,'r')
-	lines = f.read().splitlines()
+	lines = f.read().splitlines() # DOS-safe
 	f.close()
 	f.close()
 	return remove_comments(lines) if trim_comments else lines
 	return remove_comments(lines) if trim_comments else lines
 
 
@@ -729,11 +586,11 @@ def get_data_from_user(desc="data",silent=False):
 	dmsg("User input: [%s]" % data)
 	dmsg("User input: [%s]" % data)
 	return data
 	return data
 
 
-def get_data_from_file(infile,desc="data",dash=False,silent=False):
+def get_data_from_file(infile,desc="data",dash=False,silent=False,binary=False):
 	if dash and infile == "-": return sys.stdin.read()
 	if dash and infile == "-": return sys.stdin.read()
 	if not silent:
 	if not silent:
 		qmsg("Getting %s from file '%s'" % (desc,infile))
 		qmsg("Getting %s from file '%s'" % (desc,infile))
-	f = open_file_or_exit(infile,'rb')
+	f = open_file_or_exit(infile,'r'+('','b')[int(binary)])
 	data = f.read()
 	data = f.read()
 	f.close()
 	f.close()
 	return data
 	return data
@@ -757,7 +614,7 @@ def get_seed_from_seed_data(words):
 			msg("Invalid b58 number: %s" % val)
 			msg("Invalid b58 number: %s" % val)
 			return False
 			return False
 
 
-		msg("Valid seed data for seed ID %s" % make_chksum_8(seed))
+		msg("Valid seed data for Seed ID %s" % make_chksum_8(seed))
 		return seed
 		return seed
 	else:
 	else:
 		msg("Invalid checksum for {pnm} seed".format(pnm=pnm))
 		msg("Invalid checksum for {pnm} seed".format(pnm=pnm))
@@ -787,8 +644,7 @@ def get_mmgen_passphrase(desc,passchg=False):
 def get_bitcoind_passphrase(prompt):
 def get_bitcoind_passphrase(prompt):
 	if opt.passwd_file:
 	if opt.passwd_file:
 		pwfile_reuse_warning()
 		pwfile_reuse_warning()
-		return get_data_from_file(opt.passwd_file,
-				"passphrase").strip("\r\n")
+		return get_data_from_file(opt.passwd_file,"passphrase").strip("\r\n")
 	else:
 	else:
 		return my_raw_input(prompt, echo=opt.echo_passphrase)
 		return my_raw_input(prompt, echo=opt.echo_passphrase)
 
 

+ 0 - 1
setup.py

@@ -37,7 +37,6 @@ setup(
 			'mmgen.filename',
 			'mmgen.filename',
 			'mmgen.license',
 			'mmgen.license',
 			'mmgen.mn_electrum',
 			'mmgen.mn_electrum',
-			'mmgen.mnemonic',
 			'mmgen.mn_tirosh',
 			'mmgen.mn_tirosh',
 			'mmgen.obj',
 			'mmgen.obj',
 			'mmgen.opts',
 			'mmgen.opts',

File diff suppressed because it is too large
+ 403 - 251
test/test.py


+ 9 - 11
test/tooltest.py

@@ -9,7 +9,7 @@ os.chdir(os.path.join(pn,os.pardir))
 sys.path.__setitem__(0,os.path.abspath(os.curdir))
 sys.path.__setitem__(0,os.path.abspath(os.curdir))
 
 
 import mmgen.opt as opt
 import mmgen.opt as opt
-from mmgen.util import msg,msg_r,vmsg,vmsg_r,Msg,mmsg,mdie,start_mscolor
+from mmgen.util import *
 from collections import OrderedDict
 from collections import OrderedDict
 
 
 start_mscolor()
 start_mscolor()
@@ -108,9 +108,7 @@ if opt.list_cmds:
 
 
 import binascii
 import binascii
 from mmgen.test import *
 from mmgen.test import *
-from mmgen.util import get_data_from_file,write_to_file,get_lines_from_file
 from mmgen.tx import is_wif,is_btc_addr,is_b58_str
 from mmgen.tx import is_wif,is_btc_addr,is_b58_str
-from mmgen.mnemonic import get_seed_from_mnemonic
 
 
 class MMGenToolTestSuite(object):
 class MMGenToolTestSuite(object):
 
 
@@ -192,12 +190,12 @@ class MMGenToolTestSuite(object):
 		return ret
 		return ret
 
 
 	def run_cmd_out(self,name,carg=None,Return=False,kwargs="",fn_idx="",extra_msg=""):
 	def run_cmd_out(self,name,carg=None,Return=False,kwargs="",fn_idx="",extra_msg=""):
-		if carg: write_to_tmpfile(cfg,"%s%s.in" % (name,fn_idx),carg+"\n",mode='w')
+		if carg: write_to_tmpfile(cfg,"%s%s.in" % (name,fn_idx),carg+"\n")
 		ret = self.run_cmd(name,[carg] if carg else [],kwargs=kwargs,extra_msg=extra_msg)
 		ret = self.run_cmd(name,[carg] if carg else [],kwargs=kwargs,extra_msg=extra_msg)
 		if carg: vmsg("In:   " + repr(carg))
 		if carg: vmsg("In:   " + repr(carg))
 		vmsg("Out:  " + repr(ret))
 		vmsg("Out:  " + repr(ret))
 		if ret:
 		if ret:
-			write_to_tmpfile(cfg,"%s%s.out" % (name,fn_idx),ret+"\n",mode='w')
+			write_to_tmpfile(cfg,"%s%s.out" % (name,fn_idx),ret+"\n")
 			if Return: return ret
 			if Return: return ret
 			else:   ok()
 			else:   ok()
 		else:
 		else:
@@ -207,10 +205,10 @@ class MMGenToolTestSuite(object):
 	def run_cmd_randinput(self,name,strip=True):
 	def run_cmd_randinput(self,name,strip=True):
 		s = os.urandom(128)
 		s = os.urandom(128)
 		fn = name+".in"
 		fn = name+".in"
-		write_to_tmpfile(cfg,fn,s)
+		write_to_tmpfile(cfg,fn,s,binary=True)
 		ret = self.run_cmd(name,[get_tmpfile_fn(cfg,fn)],strip=strip)
 		ret = self.run_cmd(name,[get_tmpfile_fn(cfg,fn)],strip=strip)
 		fn = name+".out"
 		fn = name+".out"
-		write_to_tmpfile(cfg,fn,ret+"\n",mode='w')
+		write_to_tmpfile(cfg,fn,ret+"\n")
 		ok()
 		ok()
 		vmsg("Returned: %s" % ret)
 		vmsg("Returned: %s" % ret)
 
 
@@ -244,14 +242,14 @@ class MMGenToolTestSuite(object):
 
 
 	def unhexdump(self,name,fn1,fn2):
 	def unhexdump(self,name,fn1,fn2):
 		ret = self.run_cmd(name,[fn2],strip=False)
 		ret = self.run_cmd(name,[fn2],strip=False)
-		orig = read_from_file(fn1)
+		orig = read_from_file(fn1,binary=True)
 		cmp_or_die(orig,ret)
 		cmp_or_die(orig,ret)
 
 
 	def rand2file(self,name):
 	def rand2file(self,name):
 		of = name + ".out"
 		of = name + ".out"
 		dlen = 1024
 		dlen = 1024
 		self.run_cmd(name,[of,str(1024),"threads=4","silent=1"],strip=False)
 		self.run_cmd(name,[of,str(1024),"threads=4","silent=1"],strip=False)
-		d = read_from_tmpfile(cfg,of)
+		d = read_from_tmpfile(cfg,of,binary=True)
 		cmp_or_die(dlen,len(d))
 		cmp_or_die(dlen,len(d))
 
 
 	def strtob58(self,name):       self.run_cmd_out(name,getrandstr(16))
 	def strtob58(self,name):       self.run_cmd_out(name,getrandstr(16))
@@ -297,8 +295,8 @@ class MMGenToolTestSuite(object):
 		for n,fi,fo,m in (1,f1,f2,""),(2,f3,f4,"from compressed"):
 		for n,fi,fo,m in (1,f1,f2,""),(2,f3,f4,"from compressed"):
 			self.run_cmd_chk(name,fi,fo,extra_msg=m)
 			self.run_cmd_chk(name,fi,fo,extra_msg=m)
 	def privhex2addr(self,name,f1,f2):
 	def privhex2addr(self,name,f1,f2):
-		key1 = read_from_file(f1)
-		key2 = read_from_file(f2)
+		key1 = read_from_file(f1).rstrip()
+		key2 = read_from_file(f2).rstrip()
 		for n,args in enumerate([[key1],[key2,"compressed=1"]]):
 		for n,args in enumerate([[key1],[key2,"compressed=1"]]):
 			ret = self.run_cmd(name,args).rstrip()
 			ret = self.run_cmd(name,args).rstrip()
 			iaddr = read_from_tmpfile(cfg,"randpair%s.out" % (n+1)).split()[-1]
 			iaddr = read_from_tmpfile(cfg,"randpair%s.out" % (n+1)).split()[-1]

Some files were not shown because too many files changed in this diff