Browse Source

New 'mmgen-walletconv' utility converts to and from all available MMGen wallet
formats, thus obsoleting much of the functionality in '-walletgen' and
'-walletchk'. This is the first script to utilize the new OO wallet code in
'seed.py'.

New tests in the 'test.py' suite.

new file: mmgen-walletconv
new file: mmgen/main_walletconv.py
modified: mmgen/seed.py
modified: test/test.py

philemon 10 years ago
parent
commit
03b1b4b00e

+ 25 - 0
mmgen-walletconv

@@ -0,0 +1,25 @@
+#!/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/>.
+
+"""
+mmgen-walletconv: Convert an MMGen deterministic wallet from one format
+                  to another
+"""
+
+from mmgen.main import launch
+launch("walletconv")

+ 17 - 7
mmgen/addr.py

@@ -224,6 +224,13 @@ class AddrInfoList(MMGenObject):
 		if sid in self.data:
 		if sid in self.data:
 			return self.data[sid]
 			return self.data[sid]
 
 
+	def mmaddr2btcaddr(self,mmaddr):
+		btcaddr = ""
+		sid,idx = mmaddr.split(":")
+		if sid in self.seed_ids():
+			btcaddr = self.addrinfo(sid).btcaddr(int(idx))
+		return btcaddr
+
 	def add_wallet_data(self,c):
 	def add_wallet_data(self,c):
 		vmsg_r("Getting account data from wallet...")
 		vmsg_r("Getting account data from wallet...")
 		data,accts,i = {},c.listaccounts(minconf=0,includeWatchonly=True),0
 		data,accts,i = {},c.listaccounts(minconf=0,includeWatchonly=True),0
@@ -291,10 +298,12 @@ class AddrInfo(MMGenObject):
 		self.seed_id = seed_id
 		self.seed_id = seed_id
 		self.addrdata = addrdata
 		self.addrdata = addrdata
 		self.num_addrs = len(addrdata)
 		self.num_addrs = len(addrdata)
-		if self.source in ("wallet","txsign") or \
-				(self.source == "addrgen" and opt.gen_what == "k"):
+		if self.source in ("wallet","txsign"):
 			self.checksum = None
 			self.checksum = None
 			self.idxs_fmt = None
 			self.idxs_fmt = None
+		elif self.source == "addrgen" and opt.gen_what == "k":
+			self.checksum = None
+			self.fmt_addr_idxs()
 		else: # self.source in addrfile, addrgen
 		else: # self.source in addrfile, addrgen
 			self.make_addrdata_chksum()
 			self.make_addrdata_chksum()
 			self.fmt_addr_idxs()
 			self.fmt_addr_idxs()
@@ -383,10 +392,11 @@ class AddrInfo(MMGenObject):
 		out = []
 		out = []
 		from mmgen.addr import addrmsgs
 		from mmgen.addr import addrmsgs
 		out.append(addrmsgs['addrfile_header'] + "\n")
 		out.append(addrmsgs['addrfile_header'] + "\n")
-		w = "Key-address" if status[1] else "Address"
-		out.append("# {} data checksum for {}[{}]: {}".format(
-					w, self.seed_id, self.idxs_fmt, self.checksum))
-		out.append("# Record this value to a secure location\n")
+		if self.checksum:
+			w = "Key-address" if status[1] else "Address"
+			out.append("# {} data checksum for {}[{}]: {}".format(
+						w, self.seed_id, self.idxs_fmt, self.checksum))
+			out.append("# Record this value to a secure location\n")
 		out.append("%s {" % self.seed_id)
 		out.append("%s {" % self.seed_id)
 
 
 		# Body
 		# Body
@@ -409,7 +419,7 @@ class AddrInfo(MMGenObject):
 
 
 		out.append("}")
 		out.append("}")
 
 
-		return "\n".join([l.rstrip() for l in out])
+		return "\n".join([l.rstrip() for l in out]) + "\n"
 
 
 
 
 	def fmt_addr_idxs(self):
 	def fmt_addr_idxs(self):

+ 47 - 49
mmgen/crypto.py

@@ -17,7 +17,7 @@
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
 
 """
 """
-crypto.py:  Cryptographic and related routines for the 'mmgen-tool' utility
+crypto.py:  Cryptographic and related routines for the MMGen suite
 """
 """
 
 
 import sys
 import sys
@@ -58,7 +58,7 @@ to exit and re-run the program with the '--old-incog-fmt' option.
 }
 }
 
 
 def encrypt_seed(seed, key):
 def encrypt_seed(seed, key):
-	return encrypt_data(seed, key, iv=1, what="seed")
+	return encrypt_data(seed, key, iv=1, desc="seed")
 
 
 
 
 def decrypt_seed(enc_seed, key, seed_id, key_id):
 def decrypt_seed(enc_seed, key, seed_id, key_id):
@@ -66,21 +66,21 @@ def decrypt_seed(enc_seed, key, seed_id, key_id):
 	vmsg_r("Checking key...")
 	vmsg_r("Checking key...")
 	chk1 = make_chksum_8(key)
 	chk1 = make_chksum_8(key)
 	if key_id:
 	if key_id:
-		if not compare_chksums(key_id,"key id",chk1,"computed",die=False):
+		if not compare_chksums(key_id,"key ID",chk1,"computed"):
 			msg("Incorrect passphrase")
 			msg("Incorrect passphrase")
 			return False
 			return False
 
 
-	dec_seed = decrypt_data(enc_seed, key, iv=1, what="seed")
+	dec_seed = decrypt_data(enc_seed, key, iv=1, desc="seed")
 
 
 	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",die=False):
+		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:
 				msg_r("Checking key ID...")
 				msg_r("Checking key ID...")
-				if compare_chksums(key_id,"key id",chk1,"computed",die=False):
+				if compare_chksums(key_id,"key ID",chk1,"computed"):
 					msg("Key ID is correct but decryption of seed failed")
 					msg("Key ID is correct but decryption of seed failed")
 				else:
 				else:
 					msg("Incorrect passphrase")
 					msg("Incorrect passphrase")
@@ -90,29 +90,25 @@ def decrypt_seed(enc_seed, key, seed_id, key_id):
 #	else:
 #	else:
 #		qmsg("Generated IDs (Seed/Key): %s/%s" % (chk2,chk1))
 #		qmsg("Generated IDs (Seed/Key): %s/%s" % (chk2,chk1))
 
 
-	if opt.debug: Msg("Decrypted seed: %s" % hexlify(dec_seed))
+	dmsg("Decrypted seed: %s" % hexlify(dec_seed))
 
 
-	vmsg("OK")
 	return dec_seed
 	return dec_seed
 
 
 
 
-def encrypt_data(data, key, iv=1, what="data", verify=True):
-	"""
-	Encrypt arbitrary data using AES256 in counter mode
-	"""
+def encrypt_data(data, key, iv=1, desc="data", verify=True):
 
 
 	# 192-bit seed is 24 bytes -> not multiple of 16.  Must use MODE_CTR
 	# 192-bit seed is 24 bytes -> not multiple of 16.  Must use MODE_CTR
 	from Crypto.Cipher import AES
 	from Crypto.Cipher import AES
 	from Crypto.Util import Counter
 	from Crypto.Util import Counter
 
 
-	vmsg("Encrypting %s" % what)
+	vmsg("Encrypting %s" % desc)
 
 
 	c = AES.new(key, AES.MODE_CTR,
 	c = AES.new(key, AES.MODE_CTR,
 			counter=Counter.new(g.aesctr_iv_len*8,initial_value=iv))
 			counter=Counter.new(g.aesctr_iv_len*8,initial_value=iv))
 	enc_data = c.encrypt(data)
 	enc_data = c.encrypt(data)
 
 
 	if verify:
 	if verify:
-		vmsg_r("Performing a test decryption of the %s..." % what)
+		vmsg_r("Performing a test decryption of the %s..." % desc)
 
 
 		c = AES.new(key, AES.MODE_CTR,
 		c = AES.new(key, AES.MODE_CTR,
 				counter=Counter.new(g.aesctr_iv_len*8,initial_value=iv))
 				counter=Counter.new(g.aesctr_iv_len*8,initial_value=iv))
@@ -120,15 +116,15 @@ def encrypt_data(data, key, iv=1, what="data", verify=True):
 
 
 		if dec_data == data: vmsg("done")
 		if dec_data == data: vmsg("done")
 		else:
 		else:
-			msg("ERROR.\nDecrypted %s doesn't match original %s" % (what,what))
+			msg("ERROR.\nDecrypted %s doesn't match original %s" % (desc,desc))
 			sys.exit(2)
 			sys.exit(2)
 
 
 	return enc_data
 	return enc_data
 
 
 
 
-def decrypt_data(enc_data, key, iv=1, what="data"):
+def decrypt_data(enc_data, key, iv=1, desc="data"):
 
 
-	vmsg_r("Decrypting %s with key..." % what)
+	vmsg_r("Decrypting %s with key..." % desc)
 
 
 	from Crypto.Cipher import AES
 	from Crypto.Cipher import AES
 	from Crypto.Util import Counter
 	from Crypto.Util import Counter
@@ -151,18 +147,18 @@ def scrypt_hash_passphrase(passwd, salt, hash_preset, buflen=32):
 
 
 
 
 def make_key(passwd,salt,hash_preset,
 def make_key(passwd,salt,hash_preset,
-		what="encryption key",from_what="passphrase",verbose=False):
+		desc="encryption key",from_what="passphrase",verbose=False):
 
 
-	if from_what: what += " from "
+	if from_what: desc += " from "
 	if opt.verbose or verbose:
 	if opt.verbose or verbose:
-		msg_r("Generating %s%s..." % (what,from_what))
+		msg_r("Generating %s%s..." % (desc,from_what))
 	key = scrypt_hash_passphrase(passwd, salt, hash_preset)
 	key = scrypt_hash_passphrase(passwd, salt, hash_preset)
 	if opt.verbose or verbose: msg("done")
 	if opt.verbose or verbose: msg("done")
-	if opt.debug: Msg("Key: %s" % hexlify(key))
+	dmsg("Key: %s" % hexlify(key))
 	return key
 	return key
 
 
 
 
-def get_random_data_from_user(uchars):
+def _get_random_data_from_user(uchars):
 
 
 	if opt.quiet: msg("Enter %s random symbols" % uchars)
 	if opt.quiet: msg("Enter %s random symbols" % uchars)
 	else:       msg(crmsg['usr_rand_notice'] % uchars)
 	else:       msg(crmsg['usr_rand_notice'] % uchars)
@@ -189,8 +185,7 @@ def get_random_data_from_user(uchars):
 
 
 	fmt_time_data = ["{:.22f}".format(i) for i in time_data]
 	fmt_time_data = ["{:.22f}".format(i) for i in time_data]
 
 
-	if opt.debug:
-		msg("\nUser input:\n%s\nKeystroke time intervals:\n%s\n" %
+	dmsg("\nUser input:\n%s\nKeystroke time intervals:\n%s\n" %
 				(key_data,"\n".join(fmt_time_data)))
 				(key_data,"\n".join(fmt_time_data)))
 
 
 	prompt = "User random data successfully acquired.  Press ENTER to continue"
 	prompt = "User random data successfully acquired.  Press ENTER to continue"
@@ -202,23 +197,23 @@ def get_random_data_from_user(uchars):
 def get_random(length):
 def get_random(length):
 	from Crypto import Random
 	from Crypto import Random
 	os_rand = Random.new().read(length)
 	os_rand = Random.new().read(length)
-	if g.use_urandchars:
+	if g.use_urandchars and opt.usr_randchars:
 		from_what = "OS random data"
 		from_what = "OS random data"
 		if not g.user_entropy:
 		if not g.user_entropy:
 			g.user_entropy = \
 			g.user_entropy = \
-				sha256(get_random_data_from_user(opt.usr_randchars)).digest()
+				sha256(_get_random_data_from_user(opt.usr_randchars)).digest()
 			from_what += " plus user-supplied entropy"
 			from_what += " plus user-supplied entropy"
 		else:
 		else:
 			from_what += " plus saved user-supplied entropy"
 			from_what += " plus saved user-supplied entropy"
 		key = make_key(g.user_entropy, "", '2', from_what=from_what, verbose=True)
 		key = make_key(g.user_entropy, "", '2', from_what=from_what, verbose=True)
-		return encrypt_data(os_rand,key,what="random data",verify=False)
+		return encrypt_data(os_rand,key,desc="random data",verify=False)
 	else:
 	else:
 		return os_rand
 		return os_rand
 
 
 
 
 def get_seed_from_wallet(
 def get_seed_from_wallet(
 		infile,
 		infile,
-		prompt_info="{pnm} wallet".format(pnm=g.proj_name),
+		desc="{pnm} wallet".format(pnm=g.proj_name),
 		silent=False
 		silent=False
 		):
 		):
 
 
@@ -228,7 +223,7 @@ def get_seed_from_wallet(
 	if opt.debug: display_control_data(*wdata)
 	if opt.debug: display_control_data(*wdata)
 
 
 	padd = " "+infile if opt.quiet else ""
 	padd = " "+infile if opt.quiet else ""
-	passwd = get_mmgen_passphrase(prompt_info+padd)
+	passwd = get_mmgen_passphrase(desc+padd)
 
 
 	key = make_key(passwd, salt, hash_preset)
 	key = make_key(passwd, salt, hash_preset)
 
 
@@ -270,17 +265,17 @@ def confirm_old_format():
 
 
 def get_seed_from_incog_wallet(
 def get_seed_from_incog_wallet(
 		infile,
 		infile,
-		prompt_info="{pnm} incognito wallet".format(pnm=g.proj_name),
+		desc="{pnm} incognito wallet".format(pnm=g.proj_name),
 		silent=False,
 		silent=False,
 		hex_input=False
 		hex_input=False
 	):
 	):
 
 
-	what = "incognito wallet data"
+	desc = "incognito wallet data"
 
 
 	if opt.from_incog_hidden:
 	if opt.from_incog_hidden:
 		d = get_hidden_incog_data()
 		d = get_hidden_incog_data()
 	else:
 	else:
-		d = get_data_from_file(infile,what)
+		d = get_data_from_file(infile,desc)
 		if hex_input:
 		if hex_input:
 			try:
 			try:
 				d = unhexlify("".join(d.split()).strip())
 				d = unhexlify("".join(d.split()).strip())
@@ -310,10 +305,13 @@ def get_seed_from_incog_wallet(
 			else 'incog_iv_id'])
 			else 'incog_iv_id'])
 
 
 	while True:
 	while True:
-		passwd = get_mmgen_passphrase(prompt_info+" "+incog_id)
+		passwd = get_mmgen_passphrase(desc+" "+incog_id)
 
 
 		qmsg("Configured hash presets: %s" % " ".join(sorted(g.hash_presets)))
 		qmsg("Configured hash presets: %s" % " ".join(sorted(g.hash_presets)))
-		hp = get_hash_preset_from_user(what="incog wallet")
+		if 'hash_preset' in opt.set_by_user:
+			hp = opt.hash_preset
+		else:
+			hp = get_hash_preset_from_user(desc="incog wallet")
 
 
 		# IV is used BOTH to initialize counter and to salt password!
 		# IV is used BOTH to initialize counter and to salt password!
 		key = make_key(passwd, iv, hp, "wrapper key")
 		key = make_key(passwd, iv, hp, "wrapper key")
@@ -399,7 +397,7 @@ def _get_seed(infile,silent=False,seed_id=""):
 		msg("Invalid %s file '%s'" % (source,infile))
 		msg("Invalid %s file '%s'" % (source,infile))
 		sys.exit(2)
 		sys.exit(2)
 
 
-	if opt.debug: Msg("Seed: %s" % hexlify(seed))
+	dmsg("Seed: %s" % hexlify(seed))
 
 
 	return seed
 	return seed
 
 
@@ -415,9 +413,9 @@ def get_seed_retry(infile,seed_id=""):
 
 
 def _get_seed_from_brain_passphrase(words,silent=False):
 def _get_seed_from_brain_passphrase(words,silent=False):
 	bp = " ".join(words)
 	bp = " ".join(words)
-	if opt.debug: Msg("Sanitized brain passphrase: %s" % bp)
+	dmsg("Sanitized brain passphrase: %s" % bp)
 	seed_len,hash_preset = get_from_brain_opt_params()
 	seed_len,hash_preset = get_from_brain_opt_params()
-	if opt.debug: Msg("Brainwallet l = %s, p = %s" % (seed_len,hash_preset))
+	dmsg("Brainwallet l = %s, p = %s" % (seed_len,hash_preset))
 	vmsg_r("Hashing brainwallet data.  Please wait...")
 	vmsg_r("Hashing brainwallet data.  Please wait...")
 	# Use buflen arg of scrypt.hash() to get seed of desired length
 	# Use buflen arg of scrypt.hash() to get seed of desired length
 	seed = scrypt_hash_passphrase(bp, "", hash_preset, buflen=seed_len/8)
 	seed = scrypt_hash_passphrase(bp, "", hash_preset, buflen=seed_len/8)
@@ -432,31 +430,31 @@ def _get_seed_from_brain_passphrase(words,silent=False):
 # Vars for mmgen_*crypt functions only
 # Vars for mmgen_*crypt functions only
 salt_len,sha256_len,nonce_len = 32,32,32
 salt_len,sha256_len,nonce_len = 32,32,32
 
 
-def mmgen_encrypt(data,what="data",hash_preset=''):
+def mmgen_encrypt(data,desc="data",hash_preset=''):
 	salt,iv,nonce = get_random(salt_len),\
 	salt,iv,nonce = get_random(salt_len),\
 					get_random(g.aesctr_iv_len), \
 					get_random(g.aesctr_iv_len), \
 					get_random(nonce_len)
 					get_random(nonce_len)
-	hp = hash_preset or get_hash_preset_from_user('3',what)
+	hp = hash_preset or get_hash_preset_from_user('3',desc)
 	m = "default" if hp == '3' else "user-requested"
 	m = "default" if hp == '3' else "user-requested"
-	vmsg("Encrypting %s" % what)
+	vmsg("Encrypting %s" % desc)
 	qmsg("Using %s hash preset of '%s'" % (m,hp))
 	qmsg("Using %s hash preset of '%s'" % (m,hp))
-	passwd = get_new_passphrase(what, {})
+	passwd = get_new_passphrase(desc, {})
 	key = make_key(passwd, salt, hp)
 	key = make_key(passwd, salt, hp)
 	enc_d = encrypt_data(sha256(nonce+data).digest() + nonce + data, key,
 	enc_d = encrypt_data(sha256(nonce+data).digest() + nonce + data, key,
-				int(hexlify(iv),16), what=what)
+				int(hexlify(iv),16), desc=desc)
 	return salt+iv+enc_d
 	return salt+iv+enc_d
 
 
 
 
-def mmgen_decrypt(data,what="data",hash_preset=""):
+def mmgen_decrypt(data,desc="data",hash_preset=""):
 	dstart = salt_len + g.aesctr_iv_len
 	dstart = salt_len + g.aesctr_iv_len
 	salt,iv,enc_d = data[:salt_len],data[salt_len:dstart],data[dstart:]
 	salt,iv,enc_d = data[:salt_len],data[salt_len:dstart],data[dstart:]
-	vmsg("Preparing to decrypt %s" % what)
-	hp = hash_preset or get_hash_preset_from_user('3',what)
+	vmsg("Preparing to decrypt %s" % desc)
+	hp = hash_preset or get_hash_preset_from_user('3',desc)
 	m = "default" if hp == '3' else "user-requested"
 	m = "default" if hp == '3' else "user-requested"
 	qmsg("Using %s hash preset of '%s'" % (m,hp))
 	qmsg("Using %s hash preset of '%s'" % (m,hp))
-	passwd = get_mmgen_passphrase(what)
+	passwd = get_mmgen_passphrase(desc)
 	key = make_key(passwd, salt, hp)
 	key = make_key(passwd, salt, hp)
-	dec_d = decrypt_data(enc_d, key, int(hexlify(iv),16), what)
+	dec_d = decrypt_data(enc_d, key, int(hexlify(iv),16), desc)
 	if dec_d[:sha256_len] == sha256(dec_d[sha256_len:]).digest():
 	if dec_d[:sha256_len] == sha256(dec_d[sha256_len:]).digest():
 		vmsg("OK")
 		vmsg("OK")
 		return dec_d[sha256_len+nonce_len:]
 		return dec_d[sha256_len+nonce_len:]
@@ -464,8 +462,8 @@ def mmgen_decrypt(data,what="data",hash_preset=""):
 		msg("Incorrect passphrase or hash preset")
 		msg("Incorrect passphrase or hash preset")
 		return False
 		return False
 
 
-def mmgen_decrypt_retry(d,what="data"):
+def mmgen_decrypt_retry(d,desc="data"):
 	while True:
 	while True:
-		d_dec = mmgen_decrypt(d,what)
+		d_dec = mmgen_decrypt(d,desc)
 		if d_dec: return d_dec
 		if d_dec: return d_dec
 		msg("Trying again...")
 		msg("Trying again...")

+ 9 - 50
mmgen/filename.py

@@ -20,66 +20,25 @@
 filename.py:  Filename class and methods for the MMGen suite
 filename.py:  Filename class and methods for the MMGen suite
 """
 """
 import sys,os
 import sys,os
-import mmgen.opt as opt
 from mmgen.obj import *
 from mmgen.obj import *
-import mmgen.globalvars as g
-from mmgen.util import msg,fmt_code_to_sstype
+from mmgen.util import die,get_extension,check_infile
 
 
 class Filename(MMGenObject):
 class Filename(MMGenObject):
 
 
-	exts = {
-		'seed': {
-			"mmdat":   "Wallet",
-			"mmseed":  "SeedFile",
-			"mmwords": "Mnemonic",
-			"mmbrain": "Brainwallet",
-			"mmincog": "IncogWallet",
-			"mmincox": "IncogWalletHex",
-		},
-		'tx': {
-			"raw":         "RawTX",
-			"sig":         "SigTX",
-		},
-		'addr': {
-			"addrs":       "AddrInfo",
-			"keys":        "KeyInfo",
-			"akeys":       "KeyAddrInfo",
-			"akeys.mmenc": "KeyAddrInfoEnc",
-		},
-		'other': {
-			"chk":         "AddrInfoChecksum",
-			"mmenc":       "MMEncInfo",
-		},
-	}
-
-	ftypes = {
-		'seed': {
-			"hincog":   "IncogWalletHidden",
-		},
-	}
-
-	def __init__(self,fn,ftype=""):
+	def __init__(self,fn,ftype=None):
 		self.name     = fn
 		self.name     = fn
 		self.dirname  = os.path.dirname(fn)
 		self.dirname  = os.path.dirname(fn)
 		self.basename = os.path.basename(fn)
 		self.basename = os.path.basename(fn)
 		self.ext      = None
 		self.ext      = None
+		self.ftype    = ftype
 
 
-		def mf1(k): return k == ftype
-		def mf2(k): return '.'+k == fn[-len('.'+k):]
-
-		# find file info for ftype or extension
-		e,attr,have_match = (self.ftypes,"ftype",mf1) if ftype else \
-							(self.exts,"ext",mf2)
-
-		for k in e:
-			for j in e[k]:
-				if have_match(j):
-					setattr(self,attr,j)
-					self.fclass = k
-					self.sstype = e[k][j]
+# This should be done before license msg instead
+#		check_infile(fn)
 
 
-		if not hasattr(self,attr):
-			die(2,"Unrecognized %s for file '%s'" % (attr,fn))
+		if not ftype:
+			self.ext = get_extension(fn)
+			if not (self.ext):
+				die(2,"Unrecognized extension '.%s' for file '%s'" % (self.ext,fn))
 
 
 		# TODO: Check for Windows
 		# TODO: Check for Windows
 		import stat
 		import stat

+ 2 - 14
mmgen/globalvars.py

@@ -53,7 +53,8 @@ version   = '0.8.0'
 
 
 required_opts = [
 required_opts = [
 	"quiet","verbose","debug","outdir","echo_passphrase","passwd_file",
 	"quiet","verbose","debug","outdir","echo_passphrase","passwd_file",
-	"usr_randchars","stdout","show_hash_presets"
+	"usr_randchars","stdout","show_hash_presets","label",
+	"keep_passphrase","keep_hash_preset"
 ]
 ]
 min_screen_width = 80
 min_screen_width = 80
 
 
@@ -119,16 +120,3 @@ addr_label_symbols = wallet_label_symbols = printable_nonl
 max_addr_label_len = 32
 max_addr_label_len = 32
 max_wallet_label_len = 48
 max_wallet_label_len = 48
 max_tx_comment_len = 72   # Comment is b58 encoded, so can permit all UTF-8
 max_tx_comment_len = 72   # Comment is b58 encoded, so can permit all UTF-8
-
-wallet_fmt_codes = (
-	( "Wallet",            "wallet", "w" ),
-	( "SeedFile",          "mmseed","seed", "s" ),
-	( "Mnemonic",          "mmwords","words","mnemonic","mnem","m" ),
-	( "Brainwallet",       "mmbrain","brainwallet","brain","bw","b" ),
-	( "IncogWallet",       "mmincog","incog","i" ),
-	( "IncogWalletHex",    "mmincox","incog_hex","ix" ),
-	( "IncogWalletHidden", "incog_hidden","ih" ),
-)
-#addr_label_punc = ".","_",",","-"," ","(",")"
-#addr_label_symbols = tuple(ascii_letters + digits) + addr_label_punc
-#wallet_label_punc = addr_label_punc

+ 5 - 8
mmgen/license.py

@@ -19,18 +19,16 @@
 """
 """
 license.py:  Text of GPLv3
 license.py:  Text of GPLv3
 """
 """
+
 import mmgen.globalvars as g
 import mmgen.globalvars as g
 
 
-gpl = {
-	'warning': """
+warning = """
   {pnm} Copyright (C) {g.Cdates} by {g.author} {g.email}.  This
   {pnm} Copyright (C) {g.Cdates} by {g.author} {g.email}.  This
   program comes with ABSOLUTELY NO WARRANTY.  This is free software, and
   program comes with ABSOLUTELY NO WARRANTY.  This is free software, and
   you are welcome to redistribute it under certain conditions.
   you are welcome to redistribute it under certain conditions.
-""".format(g=g,pnm=g.proj_name),
-	'prompt': """
-Press 'w' for conditions and warranty info, or 'c' to continue:
-""",
-	'conditions': """
+""".format(g=g,pnm=g.proj_name)
+
+conditions = """
                        TERMS AND CONDITIONS
                        TERMS AND CONDITIONS
 
 
   0. Definitions.
   0. Definitions.
@@ -581,4 +579,3 @@ an absolute waiver of all civil liability in connection with the
 Program, unless a warranty or assumption of liability accompanies a
 Program, unless a warranty or assumption of liability accompanies a
 copy of the Program in return for a fee.
 copy of the Program in return for a fee.
 """
 """
-}

+ 1 - 0
mmgen/main.py

@@ -31,6 +31,7 @@ def launch(what):
 	def launch_txsend():     import mmgen.main_txsend
 	def launch_txsend():     import mmgen.main_txsend
 	def launch_txsign():     import mmgen.main_txsign
 	def launch_txsign():     import mmgen.main_txsign
 	def launch_walletchk():  import mmgen.main_walletchk
 	def launch_walletchk():  import mmgen.main_walletchk
+	def launch_walletconv(): import mmgen.main_walletconv
 	def launch_walletgen():  import mmgen.main_walletgen
 	def launch_walletgen():  import mmgen.main_walletgen
 
 
 	try: import termios
 	try: import termios

+ 2 - 2
mmgen/main_addrgen.py

@@ -161,8 +161,8 @@ if opt.stdout or not sys.stdout.isatty():
 	if enc_ext and sys.stdout.isatty():
 	if enc_ext and sys.stdout.isatty():
 		msg("Cannot write encrypted data to screen.  Exiting")
 		msg("Cannot write encrypted data to screen.  Exiting")
 		sys.exit(2)
 		sys.exit(2)
-	write_to_stdout(addrdata_str,what,
-		(what=="keys"and not opt.quiet and sys.stdout.isatty()))
+	write_to_stdout(addrdata_str,what,ask_terminal=(what=="keys"
+						and not opt.quiet and sys.stdout.isatty()))
 else:
 else:
 	outfile = "%s.%s%s" % (outfile_base, (
 	outfile = "%s.%s%s" % (outfile_base, (
 		g.keyaddrfile_ext if "ka" in opt.gen_what else (
 		g.keyaddrfile_ext if "ka" in opt.gen_what else (

+ 1 - 1
mmgen/main_addrimport.py

@@ -64,7 +64,7 @@ if len(cmd_args) == 1:
 		ai = AddrInfo(infile,has_keys=opt.keyaddr_file)
 		ai = AddrInfo(infile,has_keys=opt.keyaddr_file)
 else:
 else:
 	msg("""
 	msg("""
-"You must specify an mmgen address file (or a list of non-{pnm} addresses
+You must specify an {pnm} address file (or a list of non-{pnm} addresses
 with the '--addrlist' option)
 with the '--addrlist' option)
 """.strip().format(pnm=g.proj_name))
 """.strip().format(pnm=g.proj_name))
 	sys.exit(1)
 	sys.exit(1)

+ 2 - 2
mmgen/main_passchg.py

@@ -67,8 +67,8 @@ seed_id,key_id = metadata[:2]
 
 
 # Repeat on incorrect pw entry
 # Repeat on incorrect pw entry
 while True:
 while True:
-	p = "{pnm} wallet".format(pnm=g.proj_name)
-	passwd = get_mmgen_passphrase(p,not opt.keep_old_passphrase)
+	desc = "{pnm} wallet".format(pnm=g.proj_name)
+	passwd = get_mmgen_passphrase(desc,not opt.keep_old_passphrase)
 	key = make_key(passwd, salt, hash_preset)
 	key = make_key(passwd, salt, hash_preset)
 	seed = decrypt_seed(enc_seed, key, seed_id, key_id)
 	seed = decrypt_seed(enc_seed, key, seed_id, key_id)
 	if seed: break
 	if seed: break

+ 16 - 16
mmgen/main_pywallet.py

@@ -180,8 +180,8 @@ class AES(object):
 	def rotate(self, word):
 	def rotate(self, word):
 		""" Rijndael's key schedule rotate operation.
 		""" Rijndael's key schedule rotate operation.
 
 
-		Rotate a word eight bits to the left: eg, rotate(1d2c3a4f) == 2c3a4f1d
-		Word is an char list of size 4 (32 bits overall).
+        Rotate a word eight bits to the left: eg, rotate(1d2c3a4f) == 2c3a4f1d
+        Word is an char list of size 4 (32 bits overall).
 		"""
 		"""
 		return word[1:] + word[:1]
 		return word[1:] + word[:1]
 
 
@@ -230,10 +230,10 @@ class AES(object):
 	def expandKey(self, key, size, expandedKeySize):
 	def expandKey(self, key, size, expandedKeySize):
 		"""Rijndael's key expansion.
 		"""Rijndael's key expansion.
 
 
-		Expands an 128,192,256 key into an 176,208,240 bytes key
+        Expands an 128,192,256 key into an 176,208,240 bytes key
 
 
-		expandedKey is a char list of large enough size,
-		key is the non-expanded key.
+        expandedKey is a char list of large enough size,
+        key is the non-expanded key.
 		"""
 		"""
 		# current expanded keySize, in bytes
 		# current expanded keySize, in bytes
 		currentSize = 0
 		currentSize = 0
@@ -276,8 +276,8 @@ class AES(object):
 
 
 	def createRoundKey(self, expandedKey, roundKeyPointer):
 	def createRoundKey(self, expandedKey, roundKeyPointer):
 		"""Create a round key.
 		"""Create a round key.
-		Creates a round key from the given expanded key and the
-		position within the expanded key.
+        Creates a round key from the given expanded key and the
+        position within the expanded key.
 		"""
 		"""
 		roundKey = [0] * 16
 		roundKey = [0] * 16
 		for i in range(4):
 		for i in range(4):
@@ -1183,8 +1183,8 @@ def parse_BlockLocator(vds):
 		return d
 		return d
 
 
 def deserialize_BlockLocator(d):
 def deserialize_BlockLocator(d):
-  result = "Block Locator top: "+d['hashes'][0][::-1].encode('hex_codec')
-  return result
+	result = "Block Locator top: "+d['hashes'][0][::-1].encode('hex_codec')
+	return result
 
 
 def parse_setting(setting, vds):
 def parse_setting(setting, vds):
 	if setting[0] == "f":    # flag (boolean) settings
 	if setting[0] == "f":    # flag (boolean) settings
@@ -1404,11 +1404,11 @@ def parse_wallet(db, item_callback):
 
 
 def update_wallet(db, type, data):
 def update_wallet(db, type, data):
 	"""Write a single item to the wallet.
 	"""Write a single item to the wallet.
-	db must be open with writable=True.
-	type and data are the type code and data dictionary as parse_wallet would
-	give to item_callback.
-	data's __key__, __value__ and __type__ are ignored; only the primary data
-	fields are used.
+    db must be open with writable=True.
+    type and data are the type code and data dictionary as parse_wallet would
+    give to item_callback.
+    data's __key__, __value__ and __type__ are ignored; only the primary data
+    fields are used.
 	"""
 	"""
 	d = data
 	d = data
 	kds = BCDataStream()
 	kds = BCDataStream()
@@ -1656,8 +1656,8 @@ elif opt.keysforaddrs:
 		msg("Warning: not all requested keys found")
 		msg("Warning: not all requested keys found")
 
 
 len_arg = "%s" % len(wallet_addrs) \
 len_arg = "%s" % len(wallet_addrs) \
-   if len(data) == len(wallet_addrs) or ext == "json" \
-   else "%s:%s" % (len(data),len(wallet_addrs))
+	if len(data) == len(wallet_addrs) or ext == "json" \
+		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_to_file,write_to_stdout
 wallet_id = make_chksum_8(str(sorted(wallet_addrs)))
 wallet_id = make_chksum_8(str(sorted(wallet_addrs)))

+ 9 - 13
mmgen/main_txcreate.py

@@ -26,6 +26,7 @@ from decimal import Decimal
 
 
 import mmgen.globalvars as g
 import mmgen.globalvars as g
 import mmgen.opt as opt
 import mmgen.opt as opt
+from mmgen.util import dmsg
 from mmgen.tx import *
 from mmgen.tx import *
 
 
 pnm = g.proj_name
 pnm = g.proj_name
@@ -196,9 +197,9 @@ Display options: show [D]ays, [g]roup, show [m]mgen addr, r[e]draw screen
 					i.addr = "%s%s %s" % (
 					i.addr = "%s%s %s" % (
 						i.address[:btaddr_w-len(dots)],
 						i.address[:btaddr_w-len(dots)],
 						dots, (
 						dots, (
-                        ("{:<{w}} ".format(i.mmid,w=mmid_w) if i.mmid else "")
-                            + i.comment)[:acct_w]
-                        )
+						("{:<{w}} ".format(i.mmid,w=mmid_w) if i.mmid else "")
+							+ i.comment)[:acct_w]
+						)
 				else:
 				else:
 					i.addr = i.address
 					i.addr = i.address
 
 
@@ -294,16 +295,13 @@ def mmaddr2btcaddr_unspent(unspent,mmaddr):
 
 
 
 
 def mmaddr2btcaddr(c,mmaddr,ail_w,ail_f):
 def mmaddr2btcaddr(c,mmaddr,ail_w,ail_f):
-	# assume mmaddr has already been checked
-	sid,idx = mmaddr.split(":")
-	btcaddr = ""
 
 
-	if sid in ail_w.seed_ids():
-		btcaddr = ail_w.addrinfo(sid).btcaddr(int(idx))
+	# assume mmaddr has already been checked
+	btcaddr = ail_w.mmaddr2btcaddr(mmaddr)
 
 
 	if not btcaddr:
 	if not btcaddr:
-		if ail_f and sid in ail_f.seed_ids():
-			btcaddr = ail_f.addrinfo(sid).btcaddr(int(idx))
+		if ail_f:
+			btcaddr = ail_f.mmaddr2btcaddr(mmaddr)
 			if btcaddr:
 			if btcaddr:
 				msg(wmsg['addr_in_addrfile_only'].format(mmgenaddr=mmaddr))
 				msg(wmsg['addr_in_addrfile_only'].format(mmgenaddr=mmaddr))
 				if not keypress_confirm("Continue anyway?"):
 				if not keypress_confirm("Continue anyway?"):
@@ -449,9 +447,7 @@ if change > 0: tx_out[change_addr] = float(change)
 
 
 tx_in = [{"txid":i.txid, "vout":i.vout} for i in sel_unspent]
 tx_in = [{"txid":i.txid, "vout":i.vout} for i in sel_unspent]
 
 
-if opt.debug:
-	Msg("tx_in:  " + repr(tx_in))
-	Msg("tx_out: " + repr(tx_out))
+dmsg("tx_in:  %s\ntx_out: %s" % (repr(tx_in),repr(tx_out)))
 
 
 if opt.comment_file:
 if opt.comment_file:
 	if keypress_confirm("Edit comment?",False):
 	if keypress_confirm("Edit comment?",False):

+ 2 - 2
mmgen/main_txsend.py

@@ -68,12 +68,12 @@ if keypress_confirm("Edit transaction comment?"):
 	write_to_file(outfile,data,w,False,True,True)
 	write_to_file(outfile,data,w,False,True,True)
 
 
 warn   = "Once this transaction is sent, there's no taking it back!"
 warn   = "Once this transaction is sent, there's no taking it back!"
-what   = "broadcast this transaction to the network"
+action = "broadcast this transaction to the network"
 expect =  "YES, I REALLY WANT TO DO THIS"
 expect =  "YES, I REALLY WANT TO DO THIS"
 
 
 if opt.quiet: warn,expect = "","YES"
 if opt.quiet: warn,expect = "","YES"
 
 
-confirm_or_exit(warn, what, expect)
+confirm_or_exit(warn, action, expect)
 
 
 msg("Sending transaction")
 msg("Sending transaction")
 
 

+ 9 - 9
mmgen/main_txsign.py

@@ -25,7 +25,7 @@ import sys
 import mmgen.globalvars as g
 import mmgen.globalvars as g
 import mmgen.opt as opt
 import mmgen.opt as opt
 from mmgen.tx import *
 from mmgen.tx import *
-from mmgen.util import do_license_msg
+from mmgen.util import do_license_msg,dmsg
 
 
 pnm = g.proj_name
 pnm = g.proj_name
 pnl = pnm.lower()
 pnl = pnm.lower()
@@ -150,7 +150,7 @@ def sign_transaction(c,tx_hex,tx_num_str,sig_data,keys=None):
 
 
 	if keys:
 	if keys:
 		qmsg("Passing %s key%s to bitcoind" % (len(keys),suf(keys,"k")))
 		qmsg("Passing %s key%s to bitcoind" % (len(keys),suf(keys,"k")))
-		if opt.debug: Msg("Keys:\n  %s" % "\n  ".join(keys))
+		dmsg("Keys:\n  %s" % "\n  ".join(keys))
 
 
 	msg_r("Signing transaction{}...".format(tx_num_str))
 	msg_r("Signing transaction{}...".format(tx_num_str))
 	from mmgen.rpc import exceptions
 	from mmgen.rpc import exceptions
@@ -192,11 +192,11 @@ def sign_tx_with_bitcoind_wallet(c,tx_hex,tx_num_str,sig_data,keys):
 	return sig_tx
 	return sig_tx
 
 
 
 
-def check_maps_from_seeds(maplist,what,infiles,saved_seeds,return_keys=False):
+def check_maps_from_seeds(maplist,desc,infiles,saved_seeds,return_keys=False):
 
 
 	if not maplist: return []
 	if not maplist: return []
 	qmsg("Checking {pnm} -> BTC address mappings for {w}s (from seed(s))".format(
 	qmsg("Checking {pnm} -> BTC address mappings for {w}s (from seed(s))".format(
-				pnm=pnm,w=what))
+				pnm=pnm,w=desc))
 	d = get_keys_for_mmgen_addrs(maplist.keys(),infiles,saved_seeds)
 	d = get_keys_for_mmgen_addrs(maplist.keys(),infiles,saved_seeds)
 #	0=mmaddr 1=addr 2=wif
 #	0=mmaddr 1=addr 2=wif
 	m = dict([(e[0],e[1]) for e in d])
 	m = dict([(e[0],e[1]) for e in d])
@@ -253,9 +253,9 @@ def parse_keylist(from_file):
 
 
 
 
 # Check inputs and outputs maps against key-address file, deleting entries:
 # Check inputs and outputs maps against key-address file, deleting entries:
-def check_maps_from_kafile(imap,what,kadata,return_keys=False):
+def check_maps_from_kafile(imap,desc,kadata,return_keys=False):
 	if not kadata: return []
 	if not kadata: return []
-	qmsg("Checking {pnm} -> BTC address mappings for {w}s (from key-address file)".format(pnm=pnm,w=what))
+	qmsg("Checking {pnm} -> BTC address mappings for {w}s (from key-address file)".format(pnm=pnm,w=desc))
 	ret = []
 	ret = []
 	for k in imap.keys():
 	for k in imap.keys():
 		if k in kadata.keys():
 		if k in kadata.keys():
@@ -266,9 +266,9 @@ def check_maps_from_kafile(imap,what,kadata,return_keys=False):
 				kl,il = "key-address file:","tx file:"
 				kl,il = "key-address file:","tx file:"
 				msg(wmsg['mm2btc_mapping_error']%(kl,k,kadata[k][0],il,k,imap[k]))
 				msg(wmsg['mm2btc_mapping_error']%(kl,k,kadata[k][0],il,k,imap[k]))
 				sys.exit(2)
 				sys.exit(2)
-	if ret: vmsg("Removed %s address%s from %ss map" % (len(ret),suf(ret,"a"),what))
+	if ret: vmsg("Removed %s address%s from %ss map" % (len(ret),suf(ret,"a"),desc))
 	if return_keys:
 	if return_keys:
-		vmsg("Added %s wif key%s from %ss map" % (len(ret),suf(ret,"k"),what))
+		vmsg("Added %s wif key%s from %ss map" % (len(ret),suf(ret,"k"),desc))
 		return ret
 		return ret
 
 
 
 
@@ -288,7 +288,7 @@ infiles = opt.opts.init(opts_data,add_opts=["b16"])
 for l in (
 for l in (
 ('tx_id', 'info'),
 ('tx_id', 'info'),
 ('tx_id', 'terse_info'),
 ('tx_id', 'terse_info'),
-): opt.opts.warn_incompatible_opts(l)
+): opt.opts.die_on_incompatible_opts(l)
 
 
 if opt.from_incog_hex or opt.from_incog_hidden: opt.from_incog = True
 if opt.from_incog_hex or opt.from_incog_hidden: opt.from_incog = True
 
 

+ 2 - 3
mmgen/main_walletchk.py

@@ -95,8 +95,7 @@ def export_to_hidden_incog(incog_enc):
 	outfile,offset = opt.export_incog_hidden.split(",") #Already sanity-checked
 	outfile,offset = opt.export_incog_hidden.split(",") #Already sanity-checked
 	if opt.outdir: outfile = make_full_path(opt.outdir,outfile)
 	if opt.outdir: outfile = make_full_path(opt.outdir,outfile)
 
 
-	if opt.debug:
-		Msg("Incog data len %s, offset %s" % (len(incog_enc),offset))
+	dmsg("Incog data len %s, offset %s" % (len(incog_enc),offset))
 	check_data_fits_file_at_offset(outfile,int(offset),len(incog_enc),"write")
 	check_data_fits_file_at_offset(outfile,int(offset),len(incog_enc),"write")
 
 
 	if not opt.quiet: confirm_or_exit("","alter file '%s'" % outfile)
 	if not opt.quiet: confirm_or_exit("","alter file '%s'" % outfile)
@@ -162,7 +161,7 @@ if opt.export_mnemonic:
 
 
 elif opt.export_seed:
 elif opt.export_seed:
 	from mmgen.bitcoin import b58encode_pad
 	from mmgen.bitcoin import b58encode_pad
-	data = split_into_columns(4,b58encode_pad(seed))
+	data = split_into_cols(4,b58encode_pad(seed))
 	chk = make_chksum_6(b58encode_pad(seed))
 	chk = make_chksum_6(b58encode_pad(seed))
 	fn = "%s.%s" % (make_chksum_8(seed).upper(), g.seed_ext)
 	fn = "%s.%s" % (make_chksum_8(seed).upper(), g.seed_ext)
 	write_to_file_or_stdout(fn, "%s %s\n" % (chk,data), "seed data")
 	write_to_file_or_stdout(fn, "%s %s\n" % (chk,data), "seed data")

+ 89 - 0
mmgen/main_walletconv.py

@@ -0,0 +1,89 @@
+#!/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/>.
+
+"""
+mmgen-walletconv: Convert an MMGen deterministic wallet from one format
+                  to another
+"""
+
+import sys
+import mmgen.globalvars as g
+import mmgen.opt as opt
+from mmgen.util import die,msg,green,do_license_msg,check_infile
+from mmgen.seed import SeedSource
+
+opts_data = {
+	'sets_disabled': (
+		('hidden_incog_input_params',  bool, 'in_fmt',  'hi'),
+		('hidden_incog_output_params', bool, 'out_fmt', 'hi')
+	),
+	'desc': "Convert an {pnm} wallet from one format to another".format(
+				pnm=g.proj_name),
+	'usage':"[opts] [infile]",
+	'options': """
+-h, --help            Print this help message.
+-d, --outdir=      d  Output files to directory 'd' instead of working dir.
+-i, --in-fmt=      f  Convert from wallet format 'f' (see FMT CODES below).
+-o, --out-fmt=     f  Convert to wallet format 'f' (see FMT CODES below).
+-H, --hidden-incog-input-params=f,o  Use filename 'f' and offset 'o' (comma
+                      separated) for hidden incognito input.
+-J, --hidden-incog-output-params=f,o  Same above, but for output.  If file
+                      'f' doesn't exist, it will be created and filled with
+                      random data.
+-O, --old-incog-fmt   Specify old-format incognito input.
+-k, --keep-passphrase Reuse input wallet passphrase for output wallet.
+-K, --keep-hash-preset Reuse input wallet hash preset for output wallet.
+-l, --seed-len=    l  Specify wallet seed length of 'l' bits.  This option
+                      is required only for brainwallet and incognito inputs
+                      with non-standard (< {g.seed_len}-bit) seed lengths.
+-p, --hash-preset= p  Use the scrypt hash parameters defined by preset 'p'
+                      for password hashing (default: '{g.hash_preset}').
+-q, --quiet           Produce quieter output; suppress some warnings.
+-r, --usr-randchars=n Get 'n' characters of additional randomness from user
+                      (min={g.min_urandchars}, max={g.max_urandchars}, default={g.usr_randchars}).
+-S, --stdout          Write wallet data to stdout instead of file.
+-v, --verbose         Produce more verbose output.
+
+FMT CODES:
+  {f}
+""".format(g=g,f="\n  ".join(SeedSource.format_fmt_codes().split("\n")))
+}
+
+cmd_args = opt.opts.init(opts_data)
+
+if len(cmd_args) == 0 \
+	and not opt.hidden_incog_input_params \
+		and not opt.in_fmt:
+	die(1,"An input file or input format must be specified")
+
+if len(cmd_args) > 1 or (len(cmd_args) == 1 and opt.hidden_incog_input_params):
+	die(1,"Only one input file may be specified")
+
+if len(cmd_args) == 1:
+	check_infile(cmd_args[0])
+
+do_license_msg()
+
+msg(green("Processing input wallet"))
+
+ss_in = SeedSource(*cmd_args)
+
+msg(green("Processing output wallet"))
+
+ss_out = SeedSource(ss=ss_in)
+ss_out.write_to_file()

+ 7 - 7
mmgen/obj.py

@@ -20,7 +20,7 @@
 obj.py:  The MMGenObject class and methods
 obj.py:  The MMGenObject class and methods
 """
 """
 import mmgen.globalvars as g
 import mmgen.globalvars as g
-from mmgen.util import msgrepr_exit,msgrepr
+from mmgen.util import mdie,mmsg
 
 
 lvl = 0
 lvl = 0
 
 
@@ -72,18 +72,18 @@ class MMGenObject(object):
 		col_w = max(len(k) for k in keys)
 		col_w = max(len(k) for k in keys)
 		fs = "{}%-{}s: %s".format(indent,col_w)
 		fs = "{}%-{}s: %s".format(indent,col_w)
 
 
-  		methods = [k for k in keys if repr(getattr(self,k))[:14] == '<bound method ']
+		methods = [k for k in keys if repr(getattr(self,k))[:14] == '<bound method ']
 
 
-  		def f(k): return repr(getattr(self,k))[:14] == '<bound method '
-  		methods = filter(f,keys)
-  		def f(k): return repr(getattr(self,k))[:7] == '<mmgen.'
-  		objects = filter(f,keys)
+		def f(k): return repr(getattr(self,k))[:14] == '<bound method '
+		methods = filter(f,keys)
+		def f(k): return repr(getattr(self,k))[:7] == '<mmgen.'
+		objects = filter(f,keys)
 		other = list(set(keys) - set(methods) - set(objects))
 		other = list(set(keys) - set(methods) - set(objects))
 
 
 		for k in sorted(methods) + sorted(other) + sorted(objects):
 		for k in sorted(methods) + sorted(other) + sorted(objects):
 			val = getattr(self,k)
 			val = getattr(self,k)
 			if str(type(val))[:13] == "<class 'mmgen": # recurse into sub-objects
 			if str(type(val))[:13] == "<class 'mmgen": # recurse into sub-objects
-				out.append("\n%s%s (%s):" % (indent,k,repr(type(val))))
+				out.append("\n%s%s (%s):" % (indent,k,type(val)))
 				lvl += 1
 				lvl += 1
 				out.append(str(getattr(self,k))+"\n")
 				out.append(str(getattr(self,k))+"\n")
 				lvl -= 1
 				lvl -= 1

+ 101 - 44
mmgen/opts.py

@@ -24,7 +24,7 @@ import sys
 import mmgen.globalvars as g
 import mmgen.globalvars as g
 import mmgen.share.Opts
 import mmgen.share.Opts
 import opt
 import opt
-from mmgen.util import msg,msgrepr_exit,msgrepr,Msg,fmt_type
+from mmgen.util import msg,msg_r,mdie,mmsg,Msg,die
 
 
 def usage():
 def usage():
 	Msg("USAGE: %s %s" % (g.prog_name, usage_txt))
 	Msg("USAGE: %s %s" % (g.prog_name, usage_txt))
@@ -32,17 +32,15 @@ def usage():
 
 
 def print_version_info():
 def print_version_info():
 	Msg("""
 	Msg("""
-{progname_uc} version {g.version}
+{pgnm_uc} version {g.version}
 Part of the {pnm} suite, a Bitcoin cold-storage solution for the com-
 Part of the {pnm} suite, a Bitcoin cold-storage solution for the com-
 mand line.   Copyright (C) {g.Cdates} {g.author} {g.email}
 mand line.   Copyright (C) {g.Cdates} {g.author} {g.email}
-""".format(pnm=g.proj_name,g=g,progname_uc=g.prog_name.upper()).strip())
+""".format(pnm=g.proj_name, g=g, pgnm_uc=g.prog_name.upper()).strip())
 
 
-def warn_incompatible_opts(incompat_list):
+def die_on_incompatible_opts(incompat_list):
 	bad = [k for k in opt.__dict__ if opt.__dict__[k] and k in incompat_list]
 	bad = [k for k in opt.__dict__ if opt.__dict__[k] and k in incompat_list]
 	if len(bad) > 1:
 	if len(bad) > 1:
-		msg("Mutually exclusive options: %s" % " ".join(
-					["--"+b.replace("_","-") for b in bad]))
-		sys.exit(1)
+		die(1,"Conflicting options: %s" % ", ".join([fmt_opt(b) for b in bad]))
 
 
 def _typeconvert_from_dfl(key):
 def _typeconvert_from_dfl(key):
 
 
@@ -51,7 +49,6 @@ def _typeconvert_from_dfl(key):
 	gval = g.__dict__[key]
 	gval = g.__dict__[key]
 	uval = opt.__dict__[key]
 	uval = opt.__dict__[key]
 	gtype = type(gval)
 	gtype = type(gval)
-	utype = type(uval)
 
 
 	try:
 	try:
 		opt.__dict__[key] = gtype(opt.__dict__[key])
 		opt.__dict__[key] = gtype(opt.__dict__[key])
@@ -62,16 +59,18 @@ def _typeconvert_from_dfl(key):
 			'float': 'a float',
 			'float': 'a float',
 			'bool':  'a boolean value',
 			'bool':  'a boolean value',
 		}
 		}
-		m = [d[k] for k in d if __builtins__[k] == gtype]
-		fs = "'%s': invalid parameter for '--%s' option (not %s)"
-		msg(fs % (opt.__dict__[key],opt.replace("_","-"),m))
-		sys.exit(1)
+		die(1, "'%s': invalid parameter for '--%s' option (not %s)" % (
+			opt.__dict__[key],
+			key.replace("_","-"),
+			d[gtype.__name__]
+		))
 
 
 	if g.debug:
 	if g.debug:
 		Msg("Opt overriden by user:\n    %-18s: %s" % (
 		Msg("Opt overriden by user:\n    %-18s: %s" % (
 				key, ("%s -> %s" % (gval,uval))
 				key, ("%s -> %s" % (gval,uval))
 			))
 			))
 
 
+def fmt_opt(o): return "--" + o.replace("_","-")
 
 
 def _show_hash_presets():
 def _show_hash_presets():
 	fs = "  {:<7} {:<6} {:<3}  {}"
 	fs = "  {:<7} {:<6} {:<3}  {}"
@@ -116,21 +115,25 @@ def init(opts_data,add_opts=[]):
 	for o in [s.rstrip("=") for s in long_opts] + g.required_opts + add_opts:
 	for o in [s.rstrip("=") for s in long_opts] + g.required_opts + add_opts:
 		opt.__dict__[o] = uopts[o] if o in uopts else None
 		opt.__dict__[o] = uopts[o] if o in uopts else None
 
 
-	# check user-set opts without modifying them
-	if not check_opts(uopts): sys.exit(1)
-
 	# A special case - do this here, before opt gets set from g.dfl_vars
 	# A special case - do this here, before opt gets set from g.dfl_vars
 	if opt.usr_randchars: g.use_urandchars = True
 	if opt.usr_randchars: g.use_urandchars = True
 
 
 	# If user opt is set, convert its type based on value in mmgen.globalvars
 	# If user opt is set, convert its type based on value in mmgen.globalvars
 	# If unset, set it to default value in mmgen.globalvars (g):
 	# If unset, set it to default value in mmgen.globalvars (g):
+	opt.__dict__['set_by_user'] = []
 	for k in g.dfl_vars:
 	for k in g.dfl_vars:
 		if k in opt.__dict__ and opt.__dict__[k] != None:
 		if k in opt.__dict__ and opt.__dict__[k] != None:
 			_typeconvert_from_dfl(k)
 			_typeconvert_from_dfl(k)
-		else: opt.__dict__[k] = g.__dict__[k]
+			opt.set_by_user.append(k)
+		else:
+			opt.__dict__[k] = g.__dict__[k]
+
+	# Check user-set opts without modifying them
+	if not check_opts(uopts): sys.exit(1)
 
 
 	if opt.show_hash_presets:
 	if opt.show_hash_presets:
 		_show_hash_presets(); sys.exit(0)
 		_show_hash_presets(); sys.exit(0)
+
 	if opt.debug: opt.verbose = True
 	if opt.debug: opt.verbose = True
 
 
 	if g.debug:
 	if g.debug:
@@ -138,7 +141,7 @@ def init(opts_data,add_opts=[]):
 		for k in opt.__dict__:
 		for k in opt.__dict__:
 			v = opt.__dict__[k]
 			v = opt.__dict__[k]
 			if v != None and k != "opts":
 			if v != None and k != "opts":
- 				Msg("    %-18s: %-6s [%s]" % (k,v,fmt_type(v)))
+				Msg("    %-18s: %-6s [%s]" % (k,v,type(v).__name__))
 		Msg("### END OPTS.PY ###\n")
 		Msg("### END OPTS.PY ###\n")
 
 
 	for l in (
 	for l in (
@@ -146,7 +149,7 @@ def init(opts_data,add_opts=[]):
 	('export_incog','export_incog_hex','export_incog_hidden','export_mnemonic',
 	('export_incog','export_incog_hex','export_incog_hidden','export_mnemonic',
 	'export_seed'),
 	'export_seed'),
 	('quiet','verbose')
 	('quiet','verbose')
-	): warn_incompatible_opts(l)
+	): die_on_incompatible_opts(l)
 
 
 	return args
 	return args
 
 
@@ -160,50 +163,61 @@ def show_all_opts():
 
 
 def check_opts(usr_opts):       # Returns false if any check fails
 def check_opts(usr_opts):       # Returns false if any check fails
 
 
-	def opt_splits(val,sep,n,what):
+	def opt_splits(val,sep,n,desc):
 		sepword = "comma" if sep == "," else (
 		sepword = "comma" if sep == "," else (
 					"colon" if sep == ":" else ("'"+sep+"'"))
 					"colon" if sep == ":" else ("'"+sep+"'"))
 		try: l = val.split(sep)
 		try: l = val.split(sep)
 		except:
 		except:
-			msg("'%s': invalid %s (not %s-separated list)" % (val,what,sepword))
+			msg("'%s': invalid %s (not %s-separated list)" % (val,desc,sepword))
 			return False
 			return False
 
 
 		if len(l) == n: return True
 		if len(l) == n: return True
 		else:
 		else:
 			msg("'%s': invalid %s (%s %s-separated items required)" %
 			msg("'%s': invalid %s (%s %s-separated items required)" %
-					(val,what,n,sepword))
+					(val,desc,n,sepword))
 			return False
 			return False
 
 
-	def opt_compares(val,op,target,what):
+	def opt_compares(val,op,target,desc):
 		if not eval("%s %s %s" % (val, op, target)):
 		if not eval("%s %s %s" % (val, op, target)):
-			msg("%s: invalid %s (not %s %s)" % (val,what,op,target))
+			msg("%s: invalid %s (not %s %s)" % (val,desc,op,target))
 			return False
 			return False
 		return True
 		return True
 
 
-	def opt_is_int(val,what):
+	def opt_is_int(val,desc):
 		try: int(val)
 		try: int(val)
 		except:
 		except:
-			msg("'%s': invalid %s (not an integer)" % (val,what))
+			msg("'%s': invalid %s (not an integer)" % (val,desc))
 			return False
 			return False
 		return True
 		return True
 
 
-	def opt_is_in_list(val,lst,what):
+	def opt_is_in_list(val,lst,desc):
 		if val not in lst:
 		if val not in lst:
 			q,sep = ("'","','") if type(lst[0]) == str else ("",",")
 			q,sep = ("'","','") if type(lst[0]) == str else ("",",")
-			msg("{q}{}{q}: invalid {}\nValid options: {q}{}{q}".format(
-					val,what,sep.join([str(i) for i in sorted(lst)]),q=q))
+			msg("{q}{v}{q}: invalid {w}\nValid choices: {q}{o}{q}".format(
+					v=val,w=desc,q=q,
+					o=sep.join([str(i) for i in sorted(lst)])
+				))
 			return False
 			return False
 		return True
 		return True
 
 
+	def opt_unrecognized(key,val,desc):
+		msg("'%s': unrecognized %s for option '%s'"
+				% (val,desc,fmt_opt(key)))
+		return False
+
+	def opt_display(key,val='',beg="For selected",end=":\n"):
+		s = "%s=%s" % (fmt_opt(key),val) if val else fmt_opt(key)
+		msg_r("%s option '%s'%s" % (beg,s,end))
+
 	global opt
 	global opt
 	for key,val in [(k,getattr(opt,k)) for k in usr_opts]:
 	for key,val in [(k,getattr(opt,k)) for k in usr_opts]:
 
 
-		what = "parameter for '--%s' option" % key.replace("_","-")
+		desc = "parameter for '%s' option" % fmt_opt(key)
 
 
 		# Check for file existence and readability
 		# Check for file existence and readability
+		from mmgen.util import check_infile
 		if key in ('keys_from_file','mmgen_keys_from_file',
 		if key in ('keys_from_file','mmgen_keys_from_file',
 				'passwd_file','keysforaddrs','comment_file'):
 				'passwd_file','keysforaddrs','comment_file'):
-			from mmgen.util import check_infile
 			check_infile(val)  # exits on error
 			check_infile(val)  # exits on error
 			continue
 			continue
 
 
@@ -220,40 +234,83 @@ def check_opts(usr_opts):       # Returns false if any check fails
 			w = "character in label"
 			w = "character in label"
 			for ch in list(val):
 			for ch in list(val):
 				if not opt_is_in_list(ch,g.wallet_label_symbols,w): return False
 				if not opt_is_in_list(ch,g.wallet_label_symbols,w): return False
+		# NEW
+		elif key in ('in_fmt','out_fmt'):
+			from mmgen.seed import SeedSource,IncogWallet,Brainwallet,IncogWalletHidden
+			sstype = SeedSource.fmt_code_to_sstype(val)
+			if not sstype:
+				return opt_unrecognized(key,val,"format code")
+			if key == 'out_fmt':
+				p = 'hidden_incog_output_params'
+				if sstype == IncogWalletHidden and not getattr(opt,p):
+						die(1,"Hidden incog format output requested. You must supply"
+						+ " a file and offset with the '%s' option" % fmt_opt(p))
+				if issubclass(sstype,IncogWallet) and opt.old_incog_fmt:
+					opt_display(key,val,beg="Selected",end=" ")
+					opt_display('old_incog_fmt',beg="conflicts with",end=":\n")
+					die(1,"Export to old incog wallet format unsupported")
+				elif issubclass(sstype,Brainwallet):
+					die(1,"Output to brainwallet format unsupported")
+		elif key in ('hidden_incog_input_params','hidden_incog_output_params'):
+			if key == 'hidden_incog_input_params':
+				check_infile(val.split(",")[0])
+				key2 = 'in_fmt'
+			else:
+				key2 = 'out_fmt'
+			if hasattr(opt,key2):
+				val2 = getattr(opt,key2)
+				from mmgen.seed import IncogWalletHidden
+				if val2 and val2 not in IncogWalletHidden.fmt_codes:
+					die(1,
+						"Option conflict:\n  %s, with\n  %s=%s" % (
+						fmt_opt(key),fmt_opt(key2),val2
+					))
+
+		# begin OLD, deprecated
+		elif key == 'hidden_incog_params':
+			from mmgen.util import check_outfile
+			if not opt_splits(val,",",2,desc): return False
+			outfile,offset = val.split(",")
+			check_outfile(outfile)
+			w = "offset " + desc
+			if not opt_is_int(offset,w): return False
+			if not opt_compares(offset,">=",0,desc): return False
 		elif key == 'export_incog_hidden' or key == 'from_incog_hidden':
 		elif key == 'export_incog_hidden' or key == 'from_incog_hidden':
 			if key == 'from_incog_hidden':
 			if key == 'from_incog_hidden':
-				if not opt_splits(val,",",3,what): return False
+				if not opt_splits(val,",",3,desc): return False
 				infile,offset,seed_len = val.split(",")
 				infile,offset,seed_len = val.split(",")
 				from mmgen.util import check_infile
 				from mmgen.util import check_infile
 				check_infile(infile)
 				check_infile(infile)
-				w = "seed length " + what
+				w = "seed length " + desc
 				if not opt_is_int(seed_len,w): return False
 				if not opt_is_int(seed_len,w): return False
 				if not opt_is_in_list(int(seed_len),g.seed_lens,w): return False
 				if not opt_is_in_list(int(seed_len),g.seed_lens,w): return False
 			else:
 			else:
 				from mmgen.util import check_outfile
 				from mmgen.util import check_outfile
-				if not opt_splits(val,",",2,what): return False
+				if not opt_splits(val,",",2,desc): return False
 				outfile,offset = val.split(",")
 				outfile,offset = val.split(",")
 				check_outfile(outfile)
 				check_outfile(outfile)
-			w = "offset " + what
+			w = "offset " + desc
 			if not opt_is_int(offset,w): return False
 			if not opt_is_int(offset,w): return False
-			if not opt_compares(offset,">=",0,what): return False
+			if not opt_compares(offset,">=",0,desc): return False
 		elif key == 'from_brain':
 		elif key == 'from_brain':
-			if not opt_splits(val,",",2,what): return False
+			if not opt_splits(val,",",2,desc): return False
 			l,p = val.split(",")
 			l,p = val.split(",")
-			w = "seed length " + what
+			w = "seed length " + desc
 			if not opt_is_int(l,w): return False
 			if not opt_is_int(l,w): return False
 			if not opt_is_in_list(int(l),g.seed_lens,w): return False
 			if not opt_is_in_list(int(l),g.seed_lens,w): return False
-			w = "hash preset " + what
+			w = "hash preset " + desc
 			if not opt_is_in_list(p,g.hash_presets.keys(),w): return False
 			if not opt_is_in_list(p,g.hash_presets.keys(),w): return False
+		# end OLD
 		elif key == 'seed_len':
 		elif key == 'seed_len':
-			if not opt_is_int(val,what): return False
-			if not opt_is_in_list(int(val),g.seed_lens,what): return False
+			if not opt_is_int(val,desc): return False
+			if not opt_is_in_list(int(val),g.seed_lens,desc): return False
 		elif key == 'hash_preset':
 		elif key == 'hash_preset':
-			if not opt_is_in_list(val,g.hash_presets.keys(),what): return False
+			if not opt_is_in_list(val,g.hash_presets.keys(),desc): return False
 		elif key == 'usr_randchars':
 		elif key == 'usr_randchars':
-			if not opt_is_int(val,what): return False
-			if not opt_compares(val,">=",g.min_urandchars,what): return False
-			if not opt_compares(val,"<=",g.max_urandchars,what): return False
+			if val == 0: continue
+			if not opt_is_int(val,desc): return False
+			if not opt_compares(val,">=",g.min_urandchars,desc): return False
+			if not opt_compares(val,"<=",g.max_urandchars,desc): return False
 		else:
 		else:
 			if g.debug: Msg("check_opts(): No test for opt '%s'" % key)
 			if g.debug: Msg("check_opts(): No test for opt '%s'" % key)
 
 

+ 13 - 14
mmgen/rpc/__init__.py

@@ -23,7 +23,7 @@ bitcoin-python - Easy-to-use Bitcoin API client
 
 
 
 
 def connect_to_local(filename=None):
 def connect_to_local(filename=None):
-    """
+	"""
     Connect to default bitcoin instance owned by this user, on this machine.
     Connect to default bitcoin instance owned by this user, on this machine.
 
 
     Returns a :class:`~mmgen.rpc.connection.BitcoinConnection` object.
     Returns a :class:`~mmgen.rpc.connection.BitcoinConnection` object.
@@ -31,24 +31,23 @@ def connect_to_local(filename=None):
     Arguments:
     Arguments:
 
 
         - `filename`: Path to a configuration file in a non-standard location (optional)
         - `filename`: Path to a configuration file in a non-standard location (optional)
-    """
-    from mmgen.rpc.connection import BitcoinConnection
-    from mmgen.rpc.config import read_default_config
+	"""
+	from mmgen.rpc.connection import BitcoinConnection
+	from mmgen.rpc.config import read_default_config
 
 
-    cfg = read_default_config(filename)
-    port = int(cfg.get('rpcport', '18332' if cfg.get('testnet') else '8332'))
-    rcpuser = cfg.get('rpcuser', '')
+	cfg = read_default_config(filename)
+	port = int(cfg.get('rpcport', '18332' if cfg.get('testnet') else '8332'))
+	rcpuser = cfg.get('rpcuser', '')
 
 
-    return BitcoinConnection(rcpuser, cfg['rpcpassword'], 'localhost', port)
+	return BitcoinConnection(rcpuser, cfg['rpcpassword'], 'localhost', port)
 
 
 
 
-def connect_to_remote(user, password, host='localhost', port=8332,
-                      use_https=False):
-    """
+def connect_to_remote(user,password,host='localhost',port=8332,use_https=False):
+	"""
     Connect to remote or alternative local bitcoin client instance.
     Connect to remote or alternative local bitcoin client instance.
 
 
     Returns a :class:`~mmgen.rpc.connection.BitcoinConnection` object.
     Returns a :class:`~mmgen.rpc.connection.BitcoinConnection` object.
-    """
-    from mmgen.rpc.connection import BitcoinConnection
+	"""
+	from mmgen.rpc.connection import BitcoinConnection
 
 
-    return BitcoinConnection(user, password, host, port, use_https)
+	return BitcoinConnection(user, password, host, port, use_https)

+ 36 - 36
mmgen/rpc/config.py

@@ -23,53 +23,53 @@ Utilities for reading bitcoin configuration files.
 
 
 
 
 def read_config_file(filename):
 def read_config_file(filename):
-    """
+	"""
     Read a simple ``'='``-delimited config file.
     Read a simple ``'='``-delimited config file.
     Raises :const:`IOError` if unable to open file, or :const:`ValueError`
     Raises :const:`IOError` if unable to open file, or :const:`ValueError`
     if an parse error occurs.
     if an parse error occurs.
-    """
-    f = open(filename)
-    try:
-        cfg = {}
-        for line in f:
-            line = line.strip()
-            if line and not line.startswith("#"):
-                try:
-                    (key, value) = line.split('=', 1)
-                    cfg[key] = value
-                except ValueError:
-                    pass  # Happens when line has no '=', ignore
-    finally:
-        f.close()
-    return cfg
+	"""
+	f = open(filename)
+	try:
+		cfg = {}
+		for line in f:
+			line = line.strip()
+			if line and not line.startswith("#"):
+				try:
+					(key, value) = line.split('=', 1)
+					cfg[key] = value
+				except ValueError:
+					pass  # Happens when line has no '=', ignore
+	finally:
+		f.close()
+	return cfg
 
 
 
 
 def read_default_config(filename=None):
 def read_default_config(filename=None):
-    """
+	"""
     Read bitcoin default configuration from the current user's home directory.
     Read bitcoin default configuration from the current user's home directory.
 
 
     Arguments:
     Arguments:
 
 
     - `filename`: Path to a configuration file in a non-standard location (optional)
     - `filename`: Path to a configuration file in a non-standard location (optional)
-    """
-    if filename is None:
-        import os
-        import platform
-        home = os.getenv("HOME")
-        if not home:
-            raise IOError("Home directory not defined, don't know where to look for config file")
+	"""
+	if filename is None:
+		import os
+		import platform
+		home = os.getenv("HOME")
+		if not home:
+			raise IOError("Home directory not defined, don't know where to look for config file")
 
 
-        if platform.system() == "Darwin":
-            location = 'Library/Application Support/Bitcoin/bitcoin.conf'
-        else:
-            location = '.bitcoin/bitcoin.conf'
-        filename = os.path.join(home, location)
+		if platform.system() == "Darwin":
+			location = 'Library/Application Support/Bitcoin/bitcoin.conf'
+		else:
+			location = '.bitcoin/bitcoin.conf'
+		filename = os.path.join(home, location)
 
 
-    elif filename.startswith("~"):
-        import os
-        filename = os.path.expanduser(filename)
+	elif filename.startswith("~"):
+		import os
+		filename = os.path.expanduser(filename)
 
 
-    try:
-        return read_config_file(filename)
-    except (IOError, ValueError):
-        pass  # Cannot read config file, ignore
+	try:
+		return read_config_file(filename)
+	except (IOError, ValueError):
+		pass  # Cannot read config file, ignore

+ 201 - 205
mmgen/rpc/connection.py

@@ -32,21 +32,21 @@ from mmgen.rpc.data import (ServerInfo, AccountInfo, AddressInfo, TransactionInf
 
 
 class BitcoinConnection(object):
 class BitcoinConnection(object):
 	"""
 	"""
-	A BitcoinConnection object defines a connection to a bitcoin server.
-	It is a thin wrapper around a JSON-RPC API connection.
+    A BitcoinConnection object defines a connection to a bitcoin server.
+    It is a thin wrapper around a JSON-RPC API connection.
 
 
-	Up-to-date for SVN revision 198.
+    Up-to-date for SVN revision 198.
 
 
-	Arguments to constructor:
+    Arguments to constructor:
 
 
-	- *user* -- Authenticate as user.
-	- *password* -- Authentication password.
-	- *host* -- Bitcoin JSON-RPC host.
-	- *port* -- Bitcoin JSON-RPC port.
+    - *user* -- Authenticate as user.
+    - *password* -- Authentication password.
+    - *host* -- Bitcoin JSON-RPC host.
+    - *port* -- Bitcoin JSON-RPC port.
 	"""
 	"""
 	def __init__(self, user, password, host='localhost', port=8332, use_https=False):
 	def __init__(self, user, password, host='localhost', port=8332, use_https=False):
 		"""
 		"""
-		Create a new bitcoin server connection.
+        Create a new bitcoin server connection.
 		"""
 		"""
 		url = 'http{s}://{user}:{password}@{host}:{port}/'.format(
 		url = 'http{s}://{user}:{password}@{host}:{port}/'.format(
 			s='s' if use_https else '',
 			s='s' if use_https else '',
@@ -59,8 +59,6 @@ class BitcoinConnection(object):
 
 
 # importaddress <address> [label] [rescan=true]
 # importaddress <address> [label] [rescan=true]
 	def importaddress(self,address,label=None,rescan=True):
 	def importaddress(self,address,label=None,rescan=True):
-		"""
-		"""
 		try:
 		try:
 #			return self.proxy.badmethod(address,label) # DEBUG
 #			return self.proxy.badmethod(address,label) # DEBUG
 			return self.proxy.importaddress(address,label,rescan)
 			return self.proxy.importaddress(address,label,rescan)
@@ -78,8 +76,6 @@ ERROR: 'importaddress' not found.  Does your bitcoind support watch-only addrs?
 
 
 # sendrawtransaction <hex string> [allowhighfees=false]
 # sendrawtransaction <hex string> [allowhighfees=false]
 	def sendrawtransaction(self,tx):
 	def sendrawtransaction(self,tx):
-		"""
-		"""
 		try:
 		try:
 			return self.proxy.sendrawtransaction(tx)
 			return self.proxy.sendrawtransaction(tx)
 		except JSONRPCException as e:
 		except JSONRPCException as e:
@@ -87,7 +83,7 @@ ERROR: 'importaddress' not found.  Does your bitcoind support watch-only addrs?
 
 
 	def stop(self):
 	def stop(self):
 		"""
 		"""
-		Stop bitcoin server.
+        Stop bitcoin server.
 		"""
 		"""
 		try:
 		try:
 			self.proxy.stop()
 			self.proxy.stop()
@@ -96,7 +92,7 @@ ERROR: 'importaddress' not found.  Does your bitcoind support watch-only addrs?
 
 
 	def getblock(self, hash):
 	def getblock(self, hash):
 		"""
 		"""
-		Returns information about the given block hash.
+        Returns information about the given block hash.
 		"""
 		"""
 		try:
 		try:
 			return self.proxy.getblock(hash)
 			return self.proxy.getblock(hash)
@@ -105,7 +101,7 @@ ERROR: 'importaddress' not found.  Does your bitcoind support watch-only addrs?
 
 
 	def getblockcount(self):
 	def getblockcount(self):
 		"""
 		"""
-		Returns the number of blocks in the longest block chain.
+        Returns the number of blocks in the longest block chain.
 		"""
 		"""
 		try:
 		try:
 			return self.proxy.getblockcount()
 			return self.proxy.getblockcount()
@@ -114,9 +110,9 @@ ERROR: 'importaddress' not found.  Does your bitcoind support watch-only addrs?
 
 
 	def getblockhash(self, index):
 	def getblockhash(self, index):
 		"""
 		"""
-		Returns hash of block in best-block-chain at index.
+        Returns hash of block in best-block-chain at index.
 
 
-		:param index: index ob the block
+        :param index: index ob the block
 
 
 		"""
 		"""
 		try:
 		try:
@@ -126,14 +122,14 @@ ERROR: 'importaddress' not found.  Does your bitcoind support watch-only addrs?
 
 
 	def getblocknumber(self):
 	def getblocknumber(self):
 		"""
 		"""
-		Returns the block number of the latest block in the longest block chain.
-		Deprecated. Use getblockcount instead.
+        Returns the block number of the latest block in the longest block chain.
+        Deprecated. Use getblockcount instead.
 		"""
 		"""
 		return self.getblockcount()
 		return self.getblockcount()
 
 
 	def getconnectioncount(self):
 	def getconnectioncount(self):
 		"""
 		"""
-		Returns the number of connections to other nodes.
+        Returns the number of connections to other nodes.
 		"""
 		"""
 		try:
 		try:
 			return self.proxy.getconnectioncount()
 			return self.proxy.getconnectioncount()
@@ -142,7 +138,7 @@ ERROR: 'importaddress' not found.  Does your bitcoind support watch-only addrs?
 
 
 	def getdifficulty(self):
 	def getdifficulty(self):
 		"""
 		"""
-		Returns the proof-of-work difficulty as a multiple of the minimum difficulty.
+        Returns the proof-of-work difficulty as a multiple of the minimum difficulty.
 		"""
 		"""
 		try:
 		try:
 			return self.proxy.getdifficulty()
 			return self.proxy.getdifficulty()
@@ -151,8 +147,8 @@ ERROR: 'importaddress' not found.  Does your bitcoind support watch-only addrs?
 
 
 	def getgenerate(self):
 	def getgenerate(self):
 		"""
 		"""
-		Returns :const:`True` or :const:`False`, depending on whether
-		generation is enabled.
+        Returns :const:`True` or :const:`False`, depending on whether
+        generation is enabled.
 		"""
 		"""
 		try:
 		try:
 			return self.proxy.getgenerate()
 			return self.proxy.getgenerate()
@@ -161,14 +157,14 @@ ERROR: 'importaddress' not found.  Does your bitcoind support watch-only addrs?
 
 
 	def setgenerate(self, generate, genproclimit=None):
 	def setgenerate(self, generate, genproclimit=None):
 		"""
 		"""
-		Enable or disable generation (mining) of coins.
+        Enable or disable generation (mining) of coins.
 
 
-		Arguments:
+        Arguments:
 
 
-		- *generate* -- is :const:`True` or :const:`False` to turn generation
-		on or off.
-		- *genproclimit* -- Number of processors that are used for generation,
-		-1 is unlimited.
+        - *generate* -- is :const:`True` or :const:`False` to turn generation
+        on or off.
+        - *genproclimit* -- Number of processors that are used for generation,
+        -1 is unlimited.
 
 
 		"""
 		"""
 		try:
 		try:
@@ -181,7 +177,7 @@ ERROR: 'importaddress' not found.  Does your bitcoind support watch-only addrs?
 
 
 	def gethashespersec(self):
 	def gethashespersec(self):
 		"""
 		"""
-		Returns a recent hashes per second performance measurement while generating.
+        Returns a recent hashes per second performance measurement while generating.
 		"""
 		"""
 		try:
 		try:
 			return self.proxy.gethashespersec()
 			return self.proxy.gethashespersec()
@@ -190,8 +186,8 @@ ERROR: 'importaddress' not found.  Does your bitcoind support watch-only addrs?
 
 
 	def getinfo(self):
 	def getinfo(self):
 		"""
 		"""
-		Returns an :class:`~mmgen.rpc.data.ServerInfo` object containing
-		various state info.
+        Returns an :class:`~mmgen.rpc.data.ServerInfo` object containing
+        various state info.
 		"""
 		"""
 		try:
 		try:
 			return ServerInfo(**self.proxy.getinfo())
 			return ServerInfo(**self.proxy.getinfo())
@@ -200,8 +196,8 @@ ERROR: 'importaddress' not found.  Does your bitcoind support watch-only addrs?
 
 
 	def getmininginfo(self):
 	def getmininginfo(self):
 		"""
 		"""
-		Returns an :class:`~mmgen.rpc.data.MiningInfo` object containing various
-		mining state info.
+        Returns an :class:`~mmgen.rpc.data.MiningInfo` object containing various
+        mining state info.
 		"""
 		"""
 		try:
 		try:
 			return MiningInfo(**self.proxy.getmininginfo())
 			return MiningInfo(**self.proxy.getmininginfo())
@@ -210,13 +206,13 @@ ERROR: 'importaddress' not found.  Does your bitcoind support watch-only addrs?
 
 
 	def getnewaddress(self, account=None):
 	def getnewaddress(self, account=None):
 		"""
 		"""
-		Returns a new bitcoin address for receiving payments.
+        Returns a new bitcoin address for receiving payments.
 
 
-		Arguments:
+        Arguments:
 
 
-		- *account* -- If account is specified (recommended), it is added to the
-		address book so that payments received with the address will be
-		credited to it.
+        - *account* -- If account is specified (recommended), it is added to the
+        address book so that payments received with the address will be
+        credited to it.
 
 
 		"""
 		"""
 		try:
 		try:
@@ -229,11 +225,11 @@ ERROR: 'importaddress' not found.  Does your bitcoind support watch-only addrs?
 
 
 	def getaccountaddress(self, account):
 	def getaccountaddress(self, account):
 		"""
 		"""
-		Returns the current bitcoin address for receiving payments to an account.
+        Returns the current bitcoin address for receiving payments to an account.
 
 
-		Arguments:
+        Arguments:
 
 
-		- *account* -- Account for which the address should be returned.
+        - *account* -- Account for which the address should be returned.
 
 
 		"""
 		"""
 		try:
 		try:
@@ -243,12 +239,12 @@ ERROR: 'importaddress' not found.  Does your bitcoind support watch-only addrs?
 
 
 	def setaccount(self, bitcoinaddress, account):
 	def setaccount(self, bitcoinaddress, account):
 		"""
 		"""
-		Sets the account associated with the given address.
+        Sets the account associated with the given address.
 
 
-		Arguments:
+        Arguments:
 
 
-		- *bitcoinaddress* -- Bitcoin address to associate.
-		- *account* -- Account to associate the address to.
+        - *bitcoinaddress* -- Bitcoin address to associate.
+        - *account* -- Account to associate the address to.
 
 
 		"""
 		"""
 		try:
 		try:
@@ -258,11 +254,11 @@ ERROR: 'importaddress' not found.  Does your bitcoind support watch-only addrs?
 
 
 	def getaccount(self, bitcoinaddress):
 	def getaccount(self, bitcoinaddress):
 		"""
 		"""
-		Returns the account associated with the given address.
+        Returns the account associated with the given address.
 
 
-		Arguments:
+        Arguments:
 
 
-		- *bitcoinaddress* -- Bitcoin address to get account for.
+        - *bitcoinaddress* -- Bitcoin address to get account for.
 		"""
 		"""
 		try:
 		try:
 			return self.proxy.getaccount(bitcoinaddress)
 			return self.proxy.getaccount(bitcoinaddress)
@@ -271,11 +267,11 @@ ERROR: 'importaddress' not found.  Does your bitcoind support watch-only addrs?
 
 
 	def getaddressesbyaccount(self, account):
 	def getaddressesbyaccount(self, account):
 		"""
 		"""
-		Returns the list of addresses for the given account.
+        Returns the list of addresses for the given account.
 
 
-		Arguments:
+        Arguments:
 
 
-		- *account* -- Account to get list of addresses for.
+        - *account* -- Account to get list of addresses for.
 		"""
 		"""
 		try:
 		try:
 			return self.proxy.getaddressesbyaccount(account)
 			return self.proxy.getaddressesbyaccount(account)
@@ -284,16 +280,16 @@ ERROR: 'importaddress' not found.  Does your bitcoind support watch-only addrs?
 
 
 	def sendtoaddress(self, bitcoinaddress, amount, comment=None, comment_to=None):
 	def sendtoaddress(self, bitcoinaddress, amount, comment=None, comment_to=None):
 		"""
 		"""
-		Sends *amount* from the server's available balance to *bitcoinaddress*.
+        Sends *amount* from the server's available balance to *bitcoinaddress*.
 
 
-		Arguments:
+        Arguments:
 
 
-		- *bitcoinaddress* -- Bitcoin address to send to.
-		- *amount* -- Amount to send (float, rounded to the nearest 0.01).
-		- *minconf* -- Minimum number of confirmations required for transferred
-		balance.
-		- *comment* -- Comment for transaction.
-		- *comment_to* -- Comment for to-address.
+        - *bitcoinaddress* -- Bitcoin address to send to.
+        - *amount* -- Amount to send (float, rounded to the nearest 0.01).
+        - *minconf* -- Minimum number of confirmations required for transferred
+        balance.
+        - *comment* -- Comment for transaction.
+        - *comment_to* -- Comment for to-address.
 
 
 		"""
 		"""
 		try:
 		try:
@@ -308,14 +304,14 @@ ERROR: 'importaddress' not found.  Does your bitcoind support watch-only addrs?
 
 
 	def getreceivedbyaddress(self, bitcoinaddress, minconf=1):
 	def getreceivedbyaddress(self, bitcoinaddress, minconf=1):
 		"""
 		"""
-		Returns the total amount received by a bitcoin address in transactions
-		with at least a certain number of confirmations.
+        Returns the total amount received by a bitcoin address in transactions
+        with at least a certain number of confirmations.
 
 
-		Arguments:
+        Arguments:
 
 
-		- *bitcoinaddress* -- Address to query for total amount.
+        - *bitcoinaddress* -- Address to query for total amount.
 
 
-		- *minconf* -- Number of confirmations to require, defaults to 1.
+        - *minconf* -- Number of confirmations to require, defaults to 1.
 		"""
 		"""
 		try:
 		try:
 			return self.proxy.getreceivedbyaddress(bitcoinaddress, minconf)
 			return self.proxy.getreceivedbyaddress(bitcoinaddress, minconf)
@@ -324,13 +320,13 @@ ERROR: 'importaddress' not found.  Does your bitcoind support watch-only addrs?
 
 
 	def getreceivedbyaccount(self, account, minconf=1):
 	def getreceivedbyaccount(self, account, minconf=1):
 		"""
 		"""
-		Returns the total amount received by addresses with an account in
-		transactions with at least a certain number of confirmations.
+        Returns the total amount received by addresses with an account in
+        transactions with at least a certain number of confirmations.
 
 
-		Arguments:
+        Arguments:
 
 
-		- *account* -- Account to query for total amount.
-		- *minconf* -- Number of confirmations to require, defaults to 1.
+        - *account* -- Account to query for total amount.
+        - *minconf* -- Number of confirmations to require, defaults to 1.
 
 
 		"""
 		"""
 		try:
 		try:
@@ -340,11 +336,11 @@ ERROR: 'importaddress' not found.  Does your bitcoind support watch-only addrs?
 
 
 	def gettransaction(self, txid):
 	def gettransaction(self, txid):
 		"""
 		"""
-		Get detailed information about transaction
+        Get detailed information about transaction
 
 
-		Arguments:
+        Arguments:
 
 
-		- *txid* -- Transactiond id for which the info should be returned
+        - *txid* -- Transactiond id for which the info should be returned
 
 
 		"""
 		"""
 		try:
 		try:
@@ -354,12 +350,12 @@ ERROR: 'importaddress' not found.  Does your bitcoind support watch-only addrs?
 
 
 	def getrawtransaction(self, txid, verbose=True):
 	def getrawtransaction(self, txid, verbose=True):
 		"""
 		"""
-		Get transaction raw info
+        Get transaction raw info
 
 
-		Arguments:
+        Arguments:
 
 
-		- *txid* -- Transactiond id for which the info should be returned.
-		- *verbose* -- If False, return only the "hex" of the transaction.
+        - *txid* -- Transactiond id for which the info should be returned.
+        - *verbose* -- If False, return only the "hex" of the transaction.
 
 
 		"""
 		"""
 		try:
 		try:
@@ -371,24 +367,24 @@ ERROR: 'importaddress' not found.  Does your bitcoind support watch-only addrs?
 
 
 	def createrawtransaction(self, inputs, outputs):
 	def createrawtransaction(self, inputs, outputs):
 		"""
 		"""
-		Creates a raw transaction spending given inputs
-		(a list of dictionaries, each containing a transaction id and an output
-		number), sending to given address(es).
+        Creates a raw transaction spending given inputs
+        (a list of dictionaries, each containing a transaction id and an output
+        number), sending to given address(es).
 
 
-		Returns hex-encoded raw transaction.
+        Returns hex-encoded raw transaction.
 
 
-		Example usage:
-		>>> conn.createrawtransaction(
-				[{"txid": "a9d4599e15b53f3eb531608ddb31f48c695c3d0b3538a6bda871e8b34f2f430c",
-				"vout": 0}],
-				{"mkZBYBiq6DNoQEKakpMJegyDbw2YiNQnHT":50})
+        Example usage:
+        >>> conn.createrawtransaction(
+               [{"txid": "a9d4599e15b53f3eb531608ddb31f48c695c3d0b3538a6bda871e8b34f2f430c",
+               "vout": 0}],
+               {"mkZBYBiq6DNoQEKakpMJegyDbw2YiNQnHT":50})
 
 
 
 
-		Arguments:
+        Arguments:
 
 
-		- *inputs* -- A list of {"txid": txid, "vout": n} dictionaries.
-		- *outputs* -- A dictionary mapping (public) addresses to the amount
-					they are to be paid.
+        - *inputs* -- A list of {"txid": txid, "vout": n} dictionaries.
+        - *outputs* -- A dictionary mapping (public) addresses to the amount
+                    they are to be paid.
 		"""
 		"""
 		try:
 		try:
 			return self.proxy.createrawtransaction(inputs, outputs)
 			return self.proxy.createrawtransaction(inputs, outputs)
@@ -397,22 +393,22 @@ ERROR: 'importaddress' not found.  Does your bitcoind support watch-only addrs?
 
 
 	def signrawtransaction(self, hexstring, previous_transactions=None, private_keys=None):
 	def signrawtransaction(self, hexstring, previous_transactions=None, private_keys=None):
 		"""
 		"""
-		Sign inputs for raw transaction (serialized, hex-encoded).
+        Sign inputs for raw transaction (serialized, hex-encoded).
 
 
-		Returns a dictionary with the keys:
-			"hex": raw transaction with signature(s) (hex-encoded string)
-			"complete": 1 if transaction has a complete set of signature(s), 0 if not
+        Returns a dictionary with the keys:
+            "hex": raw transaction with signature(s) (hex-encoded string)
+            "complete": 1 if transaction has a complete set of signature(s), 0 if not
 
 
-		Arguments:
+        Arguments:
 
 
-		- *hexstring* -- A hex string of the transaction to sign.
-		- *previous_transactions* -- A (possibly empty) list of dictionaries of
-		the form:
-			{"txid": txid, "vout": n, "scriptPubKey": hex, "redeemScript": hex},
-			representing previous transaction outputs that this transaction depends
-			on but may not yet be in the block chain.
-		- *private_keys* -- A (possibly empty) list of base58-encoded private
-		keys that, if given, will be the only keys used to sign the transaction.
+        - *hexstring* -- A hex string of the transaction to sign.
+        - *previous_transactions* -- A (possibly empty) list of dictionaries of
+        the form:
+            {"txid": txid, "vout": n, "scriptPubKey": hex, "redeemScript": hex},
+            representing previous transaction outputs that this transaction depends
+            on but may not yet be in the block chain.
+        - *private_keys* -- A (possibly empty) list of base58-encoded private
+        keys that, if given, will be the only keys used to sign the transaction.
 		"""
 		"""
 		try:
 		try:
 			return dict(self.proxy.signrawtransaction(hexstring,
 			return dict(self.proxy.signrawtransaction(hexstring,
@@ -422,11 +418,11 @@ ERROR: 'importaddress' not found.  Does your bitcoind support watch-only addrs?
 
 
 	def decoderawtransaction(self, hexstring):
 	def decoderawtransaction(self, hexstring):
 		"""
 		"""
-		Produces a human-readable JSON object for a raw transaction.
+        Produces a human-readable JSON object for a raw transaction.
 
 
-		Arguments:
+        Arguments:
 
 
-		- *hexstring* -- A hex string of the transaction to be decoded.
+        - *hexstring* -- A hex string of the transaction to be decoded.
 		"""
 		"""
 		try:
 		try:
 			return dict(self.proxy.decoderawtransaction(hexstring))
 			return dict(self.proxy.decoderawtransaction(hexstring))
@@ -443,16 +439,16 @@ ERROR: 'importaddress' not found.  Does your bitcoind support watch-only addrs?
 
 
 	def listreceivedbyaddress(self, minconf=1, includeempty=False):
 	def listreceivedbyaddress(self, minconf=1, includeempty=False):
 		"""
 		"""
-		Returns a list of addresses.
+        Returns a list of addresses.
 
 
-		Each address is represented with a
-		:class:`~mmgen.rpc.data.AddressInfo` object.
+        Each address is represented with a
+        :class:`~mmgen.rpc.data.AddressInfo` object.
 
 
-		Arguments:
+        Arguments:
 
 
-		- *minconf* -- Minimum number of confirmations before payments are included.
-		- *includeempty* -- Whether to include addresses that haven't received
-		any payments.
+        - *minconf* -- Minimum number of confirmations before payments are included.
+        - *includeempty* -- Whether to include addresses that haven't received
+        any payments.
 
 
 		"""
 		"""
 		try:
 		try:
@@ -463,12 +459,12 @@ ERROR: 'importaddress' not found.  Does your bitcoind support watch-only addrs?
 
 
 	def listaccounts(self, minconf=1, includeWatchonly=False, as_dict=False):
 	def listaccounts(self, minconf=1, includeWatchonly=False, as_dict=False):
 		"""
 		"""
-		Returns a list of account names.
+        Returns a list of account names.
 
 
-		Arguments:
+        Arguments:
 
 
-		- *minconf* -- Minimum number of confirmations before payments are included.
-		- *as_dict* -- Returns a dictionary of account names, with their balance as values.
+        - *minconf* -- Minimum number of confirmations before payments are included.
+        - *as_dict* -- Returns a dictionary of account names, with their balance as values.
 		"""
 		"""
 		try:
 		try:
 			if as_dict:
 			if as_dict:
@@ -488,15 +484,15 @@ ERROR: 'listaccounts' failed.  Does your bitcoind support watch-only addresses?
 
 
 	def listreceivedbyaccount(self, minconf=1, includeempty=False):
 	def listreceivedbyaccount(self, minconf=1, includeempty=False):
 		"""
 		"""
-		Returns a list of accounts.
+        Returns a list of accounts.
 
 
-		Each account is represented with a :class:`~mmgen.rpc.data.AccountInfo` object.
+        Each account is represented with a :class:`~mmgen.rpc.data.AccountInfo` object.
 
 
-		Arguments:
+        Arguments:
 
 
-		- *minconf* -- Minimum number of confirmations before payments are included.
+        - *minconf* -- Minimum number of confirmations before payments are included.
 
 
-		- *includeempty* -- Whether to include addresses that haven't received any payments.
+        - *includeempty* -- Whether to include addresses that haven't received any payments.
 		"""
 		"""
 		try:
 		try:
 			return [AccountInfo(**x) for x in
 			return [AccountInfo(**x) for x in
@@ -506,17 +502,17 @@ ERROR: 'listaccounts' failed.  Does your bitcoind support watch-only addresses?
 
 
 	def listtransactions(self, account=None, count=10, from_=0, address=None):
 	def listtransactions(self, account=None, count=10, from_=0, address=None):
 		"""
 		"""
-		Returns a list of the last transactions for an account.
+        Returns a list of the last transactions for an account.
 
 
-		Each transaction is represented with a :class:`~mmgen.rpc.data.TransactionInfo` object.
+        Each transaction is represented with a :class:`~mmgen.rpc.data.TransactionInfo` object.
 
 
-		Arguments:
+        Arguments:
 
 
-		- *account* -- Account to list transactions from. Return transactions from
-					all accounts if None.
-		- *count* -- Number of transactions to return.
-		- *from_* -- Skip the first <from_> transactions.
-		- *address* -- Receive address to consider
+        - *account* -- Account to list transactions from. Return transactions from
+                    all accounts if None.
+        - *count* -- Number of transactions to return.
+        - *from_* -- Skip the first <from_> transactions.
+        - *address* -- Receive address to consider
 		"""
 		"""
 		accounts = [account] if account is not None else self.listaccounts(as_dict=True).iterkeys()
 		accounts = [account] if account is not None else self.listaccounts(as_dict=True).iterkeys()
 		try:
 		try:
@@ -528,11 +524,11 @@ ERROR: 'listaccounts' failed.  Does your bitcoind support watch-only addresses?
 
 
 	def backupwallet(self, destination):
 	def backupwallet(self, destination):
 		"""
 		"""
-		Safely copies ``wallet.dat`` to *destination*, which can be a directory or a path
-		with filename.
+        Safely copies ``wallet.dat`` to *destination*, which can be a directory or a path
+        with filename.
 
 
-		Arguments:
-		- *destination* -- directory or path with filename to backup wallet to.
+        Arguments:
+        - *destination* -- directory or path with filename to backup wallet to.
 
 
 		"""
 		"""
 		try:
 		try:
@@ -542,14 +538,14 @@ ERROR: 'listaccounts' failed.  Does your bitcoind support watch-only addresses?
 
 
 	def validateaddress(self, validateaddress):
 	def validateaddress(self, validateaddress):
 		"""
 		"""
-		Validate a bitcoin address and return information for it.
+        Validate a bitcoin address and return information for it.
 
 
-		The information is represented by a :class:`~mmgen.rpc.data.AddressValidation` object.
+        The information is represented by a :class:`~mmgen.rpc.data.AddressValidation` object.
 
 
-		Arguments: -- Address to validate.
+        Arguments: -- Address to validate.
 
 
 
 
-		- *validateaddress*
+        - *validateaddress*
 		"""
 		"""
 		try:
 		try:
 			return AddressValidation(**self.proxy.validateaddress(validateaddress))
 			return AddressValidation(**self.proxy.validateaddress(validateaddress))
@@ -558,11 +554,11 @@ ERROR: 'listaccounts' failed.  Does your bitcoind support watch-only addresses?
 
 
 	def getbalance(self, account=None, minconf=None):
 	def getbalance(self, account=None, minconf=None):
 		"""
 		"""
-		Get the current balance, either for an account or the total server balance.
+        Get the current balance, either for an account or the total server balance.
 
 
-		Arguments:
-		- *account* -- If this parameter is specified, returns the balance in the account.
-		- *minconf* -- Minimum number of confirmations required for transferred balance.
+        Arguments:
+        - *account* -- If this parameter is specified, returns the balance in the account.
+        - *minconf* -- Minimum number of confirmations required for transferred balance.
 
 
 		"""
 		"""
 		args = []
 		args = []
@@ -577,15 +573,15 @@ ERROR: 'listaccounts' failed.  Does your bitcoind support watch-only addresses?
 
 
 	def move(self, fromaccount, toaccount, amount, minconf=1, comment=None):
 	def move(self, fromaccount, toaccount, amount, minconf=1, comment=None):
 		"""
 		"""
-		Move from one account in your wallet to another.
+        Move from one account in your wallet to another.
 
 
-		Arguments:
+        Arguments:
 
 
-		- *fromaccount* -- Source account name.
-		- *toaccount* -- Destination account name.
-		- *amount* -- Amount to transfer.
-		- *minconf* -- Minimum number of confirmations required for transferred balance.
-		- *comment* -- Comment to add to transaction log.
+        - *fromaccount* -- Source account name.
+        - *toaccount* -- Destination account name.
+        - *amount* -- Amount to transfer.
+        - *minconf* -- Minimum number of confirmations required for transferred balance.
+        - *comment* -- Comment to add to transaction log.
 
 
 		"""
 		"""
 		try:
 		try:
@@ -599,19 +595,19 @@ ERROR: 'listaccounts' failed.  Does your bitcoind support watch-only addresses?
 	def sendfrom(self, fromaccount, tobitcoinaddress, amount, minconf=1, comment=None,
 	def sendfrom(self, fromaccount, tobitcoinaddress, amount, minconf=1, comment=None,
 				comment_to=None):
 				comment_to=None):
 		"""
 		"""
-		Sends amount from account's balance to bitcoinaddress. This method will fail
-		if there is less than amount bitcoins with minconf confirmations in the account's
-		balance (unless account is the empty-string-named default account; it
-		behaves like the sendtoaddress method). Returns transaction ID on success.
+        Sends amount from account's balance to bitcoinaddress. This method will fail
+        if there is less than amount bitcoins with minconf confirmations in the account's
+        balance (unless account is the empty-string-named default account; it
+        behaves like the sendtoaddress method). Returns transaction ID on success.
 
 
-		Arguments:
+        Arguments:
 
 
-		- *fromaccount* -- Account to send from.
-		- *tobitcoinaddress* -- Bitcoin address to send to.
-		- *amount* -- Amount to send (float, rounded to the nearest 0.01).
-		- *minconf* -- Minimum number of confirmations required for transferred balance.
-		- *comment* -- Comment for transaction.
-		- *comment_to* -- Comment for to-address.
+        - *fromaccount* -- Account to send from.
+        - *tobitcoinaddress* -- Bitcoin address to send to.
+        - *amount* -- Amount to send (float, rounded to the nearest 0.01).
+        - *minconf* -- Minimum number of confirmations required for transferred balance.
+        - *comment* -- Comment for transaction.
+        - *comment_to* -- Comment for to-address.
 
 
 		"""
 		"""
 		try:
 		try:
@@ -626,20 +622,20 @@ ERROR: 'listaccounts' failed.  Does your bitcoind support watch-only addresses?
 
 
 	def sendmany(self, fromaccount, todict, minconf=1, comment=None):
 	def sendmany(self, fromaccount, todict, minconf=1, comment=None):
 		"""
 		"""
-		Sends specified amounts from account's balance to bitcoinaddresses.
-		This method will fail if there is less than total amount bitcoins with
-		minconf confirmations in the account's balance (unless account is the
-		empty-string-named default account; Returns transaction ID on
-		success.
+        Sends specified amounts from account's balance to bitcoinaddresses.
+        This method will fail if there is less than total amount bitcoins with
+        minconf confirmations in the account's balance (unless account is the
+        empty-string-named default account; Returns transaction ID on
+        success.
 
 
-		Arguments:
+        Arguments:
 
 
-		- *fromaccount* -- Account to send from.
-		- *todict* -- Dictionary with Bitcoin addresses as keys and amounts as
-		values.
-		- *minconf* -- Minimum number of confirmations required for transferred
-		balance.
-		- *comment* -- Comment for transaction.
+        - *fromaccount* -- Account to send from.
+        - *todict* -- Dictionary with Bitcoin addresses as keys and amounts as
+        values.
+        - *minconf* -- Minimum number of confirmations required for transferred
+        balance.
+        - *comment* -- Comment for transaction.
 
 
 		"""
 		"""
 		try:
 		try:
@@ -652,15 +648,15 @@ ERROR: 'listaccounts' failed.  Does your bitcoind support watch-only addresses?
 
 
 	def verifymessage(self, bitcoinaddress, signature, message):
 	def verifymessage(self, bitcoinaddress, signature, message):
 		"""
 		"""
-		Verifies a signature given the bitcoinaddress used to sign,
-		the signature itself, and the message that was signed.
-		Returns :const:`True` if the signature is valid, and :const:`False` if it is invalid.
+        Verifies a signature given the bitcoinaddress used to sign,
+        the signature itself, and the message that was signed.
+        Returns :const:`True` if the signature is valid, and :const:`False` if it is invalid.
 
 
-		Arguments:
+        Arguments:
 
 
-		- *bitcoinaddress* -- the bitcoinaddress used to sign the message
-		- *signature* -- the signature to be verified
-		- *message* -- the message that was originally signed
+        - *bitcoinaddress* -- the bitcoinaddress used to sign the message
+        - *signature* -- the signature to be verified
+        - *message* -- the message that was originally signed
 
 
 		"""
 		"""
 		try:
 		try:
@@ -670,15 +666,15 @@ ERROR: 'listaccounts' failed.  Does your bitcoind support watch-only addresses?
 
 
 	def getwork(self, data=None):
 	def getwork(self, data=None):
 		"""
 		"""
-		Get work for remote mining, or submit result.
-		If data is specified, the server tries to solve the block
-		using the provided data and returns :const:`True` if it was successful.
-		If not, the function returns formatted hash data (:class:`~mmgen.rpc.data.WorkItem`)
-		to work on.
+        Get work for remote mining, or submit result.
+        If data is specified, the server tries to solve the block
+        using the provided data and returns :const:`True` if it was successful.
+        If not, the function returns formatted hash data (:class:`~mmgen.rpc.data.WorkItem`)
+        to work on.
 
 
-		Arguments:
+        Arguments:
 
 
-		- *data* -- Result from remote mining.
+        - *data* -- Result from remote mining.
 
 
 		"""
 		"""
 		try:
 		try:
@@ -692,13 +688,13 @@ ERROR: 'listaccounts' failed.  Does your bitcoind support watch-only addresses?
 
 
 	def listunspent(self, minconf=1, maxconf=999999):
 	def listunspent(self, minconf=1, maxconf=999999):
 		"""
 		"""
-		Returns a list of unspent transaction inputs in the wallet.
+        Returns a list of unspent transaction inputs in the wallet.
 
 
-		Arguments:
+        Arguments:
 
 
-		- *minconf* -- Minimum number of confirmations required to be listed.
+        - *minconf* -- Minimum number of confirmations required to be listed.
 
 
-		- *maxconf* -- Maximal number of confirmations allowed to be listed.
+        - *maxconf* -- Maximal number of confirmations allowed to be listed.
 
 
 
 
 		"""
 		"""
@@ -717,15 +713,15 @@ ERROR: 'listaccounts' failed.  Does your bitcoind support watch-only addresses?
 
 
 	def walletpassphrase(self, passphrase, timeout, dont_raise=False):
 	def walletpassphrase(self, passphrase, timeout, dont_raise=False):
 		"""
 		"""
-		Stores the wallet decryption key in memory for <timeout> seconds.
+        Stores the wallet decryption key in memory for <timeout> seconds.
 
 
-		- *passphrase* -- The wallet passphrase.
+        - *passphrase* -- The wallet passphrase.
 
 
-		- *timeout* -- Time in seconds to keep the wallet unlocked
-					(by keeping the passphrase in memory).
+        - *timeout* -- Time in seconds to keep the wallet unlocked
+                    (by keeping the passphrase in memory).
 
 
-		- *dont_raise* -- instead of raising `~mmgen.rpc.exceptions.WalletPassphraseIncorrect`
-						return False.
+        - *dont_raise* -- instead of raising `~mmgen.rpc.exceptions.WalletPassphraseIncorrect`
+                       return False.
 		"""
 		"""
 		try:
 		try:
 			self.proxy.walletpassphrase(passphrase, timeout)
 			self.proxy.walletpassphrase(passphrase, timeout)
@@ -741,10 +737,10 @@ ERROR: 'listaccounts' failed.  Does your bitcoind support watch-only addresses?
 
 
 	def walletlock(self):
 	def walletlock(self):
 		"""
 		"""
-		Removes the wallet encryption key from memory, locking the wallet.
-		After calling this method, you will need to call walletpassphrase
-		again before being able to call any methods which require the wallet
-		to be unlocked.
+        Removes the wallet encryption key from memory, locking the wallet.
+        After calling this method, you will need to call walletpassphrase
+        again before being able to call any methods which require the wallet
+        to be unlocked.
 		"""
 		"""
 		try:
 		try:
 			return self.proxy.walletlock()
 			return self.proxy.walletlock()
@@ -753,12 +749,12 @@ ERROR: 'listaccounts' failed.  Does your bitcoind support watch-only addresses?
 
 
 	def walletpassphrasechange(self, oldpassphrase, newpassphrase, dont_raise=False):
 	def walletpassphrasechange(self, oldpassphrase, newpassphrase, dont_raise=False):
 		"""
 		"""
-		Changes the wallet passphrase from <oldpassphrase> to <newpassphrase>.
+        Changes the wallet passphrase from <oldpassphrase> to <newpassphrase>.
 
 
-		Arguments:
+        Arguments:
 
 
-		- *dont_raise* -- instead of raising
-			`~mmgen.rpc.exceptions.WalletPassphraseIncorrect` return False.
+        - *dont_raise* -- instead of raising
+               `~mmgen.rpc.exceptions.WalletPassphraseIncorrect` return False.
 		"""
 		"""
 		try:
 		try:
 			self.proxy.walletpassphrasechange(oldpassphrase, newpassphrase)
 			self.proxy.walletpassphrasechange(oldpassphrase, newpassphrase)

+ 91 - 91
mmgen/rpc/exceptions.py

@@ -23,181 +23,181 @@ Exception definitions.
 
 
 
 
 class BitcoinException(Exception):
 class BitcoinException(Exception):
-    """
+	"""
     Base class for exceptions received from Bitcoin server.
     Base class for exceptions received from Bitcoin server.
 
 
     - *code* -- Error code from ``bitcoind``.
     - *code* -- Error code from ``bitcoind``.
-    """
-    # Standard JSON-RPC 2.0 errors
-    INVALID_REQUEST  = -32600,
-    METHOD_NOT_FOUND = -32601,
-    INVALID_PARAMS   = -32602,
-    INTERNAL_ERROR   = -32603,
-    PARSE_ERROR      = -32700,
-
-    # General application defined errors
-    MISC_ERROR                  = -1  # std::exception thrown in command handling
-    FORBIDDEN_BY_SAFE_MODE      = -2  # Server is in safe mode, and command is not allowed in safe mode
-    TYPE_ERROR                  = -3  # Unexpected type was passed as parameter
-    INVALID_ADDRESS_OR_KEY      = -5  # Invalid address or key
-    OUT_OF_MEMORY               = -7  # Ran out of memory during operation
-    INVALID_PARAMETER           = -8  # Invalid, missing or duplicate parameter
-    DATABASE_ERROR              = -20 # Database error
-    DESERIALIZATION_ERROR       = -22 # Error parsing or validating structure in raw format
-
-    # P2P client errors
-    CLIENT_NOT_CONNECTED        = -9  # Bitcoin is not connected
-    CLIENT_IN_INITIAL_DOWNLOAD  = -10 # Still downloading initial blocks
-
-    # Wallet errors
-    WALLET_ERROR                = -4  # Unspecified problem with wallet (key not found etc.)
-    WALLET_INSUFFICIENT_FUNDS   = -6  # Not enough funds in wallet or account
-    WALLET_INVALID_ACCOUNT_NAME = -11 # Invalid account name
-    WALLET_KEYPOOL_RAN_OUT      = -12 # Keypool ran out, call keypoolrefill first
-    WALLET_UNLOCK_NEEDED        = -13 # Enter the wallet passphrase with walletpassphrase first
-    WALLET_PASSPHRASE_INCORRECT = -14 # The wallet passphrase entered was incorrect
-    WALLET_WRONG_ENC_STATE      = -15 # Command given in wrong wallet encryption state (encrypting an encrypted wallet etc.)
-    WALLET_ENCRYPTION_FAILED    = -16 # Failed to encrypt the wallet
-    WALLET_ALREADY_UNLOCKED     = -17 # Wallet is already unlocked
-
-    def __init__(self, error):
-        Exception.__init__(self, error['message'])
-        self.code = error['code']
+	"""
+	# Standard JSON-RPC 2.0 errors
+	INVALID_REQUEST  = -32600,
+	METHOD_NOT_FOUND = -32601,
+	INVALID_PARAMS   = -32602,
+	INTERNAL_ERROR   = -32603,
+	PARSE_ERROR      = -32700,
+
+	# General application defined errors
+	MISC_ERROR                  = -1  # std::exception thrown in command handling
+	FORBIDDEN_BY_SAFE_MODE      = -2  # Server is in safe mode, and command is not allowed in safe mode
+	TYPE_ERROR                  = -3  # Unexpected type was passed as parameter
+	INVALID_ADDRESS_OR_KEY      = -5  # Invalid address or key
+	OUT_OF_MEMORY               = -7  # Ran out of memory during operation
+	INVALID_PARAMETER           = -8  # Invalid, missing or duplicate parameter
+	DATABASE_ERROR              = -20 # Database error
+	DESERIALIZATION_ERROR       = -22 # Error parsing or validating structure in raw format
+
+	# P2P client errors
+	CLIENT_NOT_CONNECTED        = -9  # Bitcoin is not connected
+	CLIENT_IN_INITIAL_DOWNLOAD  = -10 # Still downloading initial blocks
+
+	# Wallet errors
+	WALLET_ERROR                = -4  # Unspecified problem with wallet (key not found etc.)
+	WALLET_INSUFFICIENT_FUNDS   = -6  # Not enough funds in wallet or account
+	WALLET_INVALID_ACCOUNT_NAME = -11 # Invalid account name
+	WALLET_KEYPOOL_RAN_OUT      = -12 # Keypool ran out, call keypoolrefill first
+	WALLET_UNLOCK_NEEDED        = -13 # Enter the wallet passphrase with walletpassphrase first
+	WALLET_PASSPHRASE_INCORRECT = -14 # The wallet passphrase entered was incorrect
+	WALLET_WRONG_ENC_STATE      = -15 # Command given in wrong wallet encryption state (encrypting an encrypted wallet etc.)
+	WALLET_ENCRYPTION_FAILED    = -16 # Failed to encrypt the wallet
+	WALLET_ALREADY_UNLOCKED     = -17 # Wallet is already unlocked
+
+	def __init__(self, error):
+		Exception.__init__(self, error['message'])
+		self.code = error['code']
 
 
 
 
 ##### General application defined errors
 ##### General application defined errors
 class SafeMode(BitcoinException):
 class SafeMode(BitcoinException):
-    """
+	"""
     Operation denied in safe mode (run ``bitcoind`` with ``-disablesafemode``).
     Operation denied in safe mode (run ``bitcoind`` with ``-disablesafemode``).
-    """
+	"""
 
 
 
 
 class JSONTypeError(BitcoinException):
 class JSONTypeError(BitcoinException):
-    """
+	"""
     Unexpected type was passed as parameter
     Unexpected type was passed as parameter
-    """
+	"""
 InvalidAmount = JSONTypeError  # Backwards compatibility
 InvalidAmount = JSONTypeError  # Backwards compatibility
 
 
 
 
 class InvalidAddressOrKey(BitcoinException):
 class InvalidAddressOrKey(BitcoinException):
-    """
+	"""
     Invalid address or key.
     Invalid address or key.
-    """
+	"""
 InvalidTransactionID = InvalidAddressOrKey  # Backwards compatibility
 InvalidTransactionID = InvalidAddressOrKey  # Backwards compatibility
 
 
 
 
 class OutOfMemory(BitcoinException):
 class OutOfMemory(BitcoinException):
-    """
+	"""
     Out of memory during operation.
     Out of memory during operation.
-    """
+	"""
 
 
 
 
 class InvalidParameter(BitcoinException):
 class InvalidParameter(BitcoinException):
-    """
+	"""
     Invalid parameter provided to RPC call.
     Invalid parameter provided to RPC call.
-    """
+	"""
 
 
 
 
 ##### Client errors
 ##### Client errors
 class ClientException(BitcoinException):
 class ClientException(BitcoinException):
-    """
+	"""
     P2P network error.
     P2P network error.
     This exception is never raised but functions as a superclass
     This exception is never raised but functions as a superclass
     for other P2P client exceptions.
     for other P2P client exceptions.
-    """
+	"""
 
 
 
 
 class NotConnected(ClientException):
 class NotConnected(ClientException):
-    """
+	"""
     Not connected to any peers.
     Not connected to any peers.
-    """
+	"""
 
 
 
 
 class DownloadingBlocks(ClientException):
 class DownloadingBlocks(ClientException):
-    """
+	"""
     Client is still downloading blocks.
     Client is still downloading blocks.
-    """
+	"""
 
 
 
 
 ##### Wallet errors
 ##### Wallet errors
 class WalletError(BitcoinException):
 class WalletError(BitcoinException):
-    """
+	"""
     Unspecified problem with wallet (key not found etc.)
     Unspecified problem with wallet (key not found etc.)
-    """
+	"""
 SendError = WalletError  # Backwards compatibility
 SendError = WalletError  # Backwards compatibility
 
 
 class InsufficientFunds(WalletError):
 class InsufficientFunds(WalletError):
-    """
+	"""
     Insufficient funds to complete transaction in wallet or account
     Insufficient funds to complete transaction in wallet or account
-    """
+	"""
 
 
 class InvalidAccountName(WalletError):
 class InvalidAccountName(WalletError):
-    """
+	"""
     Invalid account name
     Invalid account name
-    """
+	"""
 
 
 
 
 class KeypoolRanOut(WalletError):
 class KeypoolRanOut(WalletError):
-    """
+	"""
     Keypool ran out, call keypoolrefill first
     Keypool ran out, call keypoolrefill first
-    """
+	"""
 
 
 
 
 class WalletUnlockNeeded(WalletError):
 class WalletUnlockNeeded(WalletError):
-    """
+	"""
     Enter the wallet passphrase with walletpassphrase first
     Enter the wallet passphrase with walletpassphrase first
-    """
+	"""
 
 
 
 
 class WalletPassphraseIncorrect(WalletError):
 class WalletPassphraseIncorrect(WalletError):
-    """
+	"""
     The wallet passphrase entered was incorrect
     The wallet passphrase entered was incorrect
-    """
+	"""
 
 
 
 
 class WalletWrongEncState(WalletError):
 class WalletWrongEncState(WalletError):
-    """
+	"""
     Command given in wrong wallet encryption state (encrypting an encrypted wallet etc.)
     Command given in wrong wallet encryption state (encrypting an encrypted wallet etc.)
-    """
+	"""
 
 
 
 
 class WalletEncryptionFailed(WalletError):
 class WalletEncryptionFailed(WalletError):
-    """
+	"""
     Failed to encrypt the wallet
     Failed to encrypt the wallet
-    """
+	"""
 
 
 
 
 class WalletAlreadyUnlocked(WalletError):
 class WalletAlreadyUnlocked(WalletError):
-    """
+	"""
     Wallet is already unlocked
     Wallet is already unlocked
-    """
+	"""
 
 
 
 
 # For convenience, we define more specific exception classes
 # For convenience, we define more specific exception classes
 # for the more common errors.
 # for the more common errors.
 _exception_map = {
 _exception_map = {
-    BitcoinException.FORBIDDEN_BY_SAFE_MODE: SafeMode,
-    BitcoinException.TYPE_ERROR: JSONTypeError,
-    BitcoinException.WALLET_ERROR: WalletError,
-    BitcoinException.INVALID_ADDRESS_OR_KEY: InvalidAddressOrKey,
-    BitcoinException.WALLET_INSUFFICIENT_FUNDS: InsufficientFunds,
-    BitcoinException.OUT_OF_MEMORY: OutOfMemory,
-    BitcoinException.INVALID_PARAMETER: InvalidParameter,
-    BitcoinException.CLIENT_NOT_CONNECTED: NotConnected,
-    BitcoinException.CLIENT_IN_INITIAL_DOWNLOAD: DownloadingBlocks,
-    BitcoinException.WALLET_INSUFFICIENT_FUNDS: InsufficientFunds,
-    BitcoinException.WALLET_INVALID_ACCOUNT_NAME: InvalidAccountName,
-    BitcoinException.WALLET_KEYPOOL_RAN_OUT: KeypoolRanOut,
-    BitcoinException.WALLET_UNLOCK_NEEDED: WalletUnlockNeeded,
-    BitcoinException.WALLET_PASSPHRASE_INCORRECT: WalletPassphraseIncorrect,
-    BitcoinException.WALLET_WRONG_ENC_STATE: WalletWrongEncState,
-    BitcoinException.WALLET_ENCRYPTION_FAILED: WalletEncryptionFailed,
-    BitcoinException.WALLET_ALREADY_UNLOCKED: WalletAlreadyUnlocked,
+	BitcoinException.FORBIDDEN_BY_SAFE_MODE: SafeMode,
+	BitcoinException.TYPE_ERROR: JSONTypeError,
+	BitcoinException.WALLET_ERROR: WalletError,
+	BitcoinException.INVALID_ADDRESS_OR_KEY: InvalidAddressOrKey,
+	BitcoinException.WALLET_INSUFFICIENT_FUNDS: InsufficientFunds,
+	BitcoinException.OUT_OF_MEMORY: OutOfMemory,
+	BitcoinException.INVALID_PARAMETER: InvalidParameter,
+	BitcoinException.CLIENT_NOT_CONNECTED: NotConnected,
+	BitcoinException.CLIENT_IN_INITIAL_DOWNLOAD: DownloadingBlocks,
+	BitcoinException.WALLET_INSUFFICIENT_FUNDS: InsufficientFunds,
+	BitcoinException.WALLET_INVALID_ACCOUNT_NAME: InvalidAccountName,
+	BitcoinException.WALLET_KEYPOOL_RAN_OUT: KeypoolRanOut,
+	BitcoinException.WALLET_UNLOCK_NEEDED: WalletUnlockNeeded,
+	BitcoinException.WALLET_PASSPHRASE_INCORRECT: WalletPassphraseIncorrect,
+	BitcoinException.WALLET_WRONG_ENC_STATE: WalletWrongEncState,
+	BitcoinException.WALLET_ENCRYPTION_FAILED: WalletEncryptionFailed,
+	BitcoinException.WALLET_ALREADY_UNLOCKED: WalletAlreadyUnlocked,
 }
 }
 
 
 
 
 def _wrap_exception(error):
 def _wrap_exception(error):
-    """
+	"""
     Convert a JSON error object to a more specific Bitcoin exception.
     Convert a JSON error object to a more specific Bitcoin exception.
-    """
-    return _exception_map.get(error['code'], BitcoinException)(error)
+	"""
+	return _exception_map.get(error['code'], BitcoinException)(error)

+ 1 - 1
mmgen/rpc/proxy.py

@@ -10,7 +10,7 @@
   ServiceProxy class:
   ServiceProxy class:
 
 
   - HTTP connections persist for the life of the AuthServiceProxy object
   - HTTP connections persist for the life of the AuthServiceProxy object
-	(if server supports HTTP/1.1)
+    (if server supports HTTP/1.1)
   - sends protocol 'version', per JSON-RPC 1.1
   - sends protocol 'version', per JSON-RPC 1.1
   - sends proper, incrementing 'id'
   - sends proper, incrementing 'id'
   - sends Basic HTTP authentication headers
   - sends Basic HTTP authentication headers

+ 21 - 21
mmgen/rpc/util.py

@@ -22,28 +22,28 @@ from copy import copy
 
 
 
 
 class DStruct(object):
 class DStruct(object):
-    """
+	"""
     Simple dynamic structure, like :const:`collections.namedtuple` but more flexible
     Simple dynamic structure, like :const:`collections.namedtuple` but more flexible
     (and less memory-efficient)
     (and less memory-efficient)
-    """
-    # Default arguments. Defaults are *shallow copied*, to allow defaults such as [].
-    _fields = []
-    _defaults = {}
+	"""
+	# Default arguments. Defaults are *shallow copied*, to allow defaults such as [].
+	_fields = []
+	_defaults = {}
 
 
-    def __init__(self, *args_t, **args_d):
-        # order
-        if len(args_t) > len(self._fields):
-            raise TypeError("Number of arguments is larger than of predefined fields")
-        # Copy default values
-        for (k, v) in self._defaults.iteritems():
-            self.__dict__[k] = copy(v)
-        # Set pass by value arguments
-        self.__dict__.update(zip(self._fields, args_t))
-        # dict
-        self.__dict__.update(args_d)
+	def __init__(self, *args_t, **args_d):
+		# order
+		if len(args_t) > len(self._fields):
+			raise TypeError("Number of arguments is larger than of predefined fields")
+		# Copy default values
+		for (k, v) in self._defaults.iteritems():
+			self.__dict__[k] = copy(v)
+		# Set pass by value arguments
+		self.__dict__.update(zip(self._fields, args_t))
+		# dict
+		self.__dict__.update(args_d)
 
 
-    def __repr__(self):
-        return '{module}.{classname}({slots})'.format(
-            module=self.__class__.__module__, classname=self.__class__.__name__,
-            slots=", ".join('{k}={v!r}'.format(k=k, v=v) for k, v in
-                            self.__dict__.iteritems()))
+	def __repr__(self):
+		return '{module}.{classname}({slots})'.format(
+			module=self.__class__.__module__, classname=self.__class__.__name__,
+			slots=", ".join('{k}={v!r}'.format(k=k, v=v) for k, v in
+							self.__dict__.iteritems()))

File diff suppressed because it is too large
+ 463 - 249
mmgen/seed.py


+ 15 - 0
mmgen/share/Opts.py

@@ -62,6 +62,21 @@ def process_opts(argv,opts_data,short_opts,long_opts):
 					opt[1:]+":")][:-1].replace("-","_")] = arg
 					opt[1:]+":")][:-1].replace("-","_")] = arg
 		else: assert False, "Invalid option"
 		else: assert False, "Invalid option"
 
 
+	if 'sets' in opts_data:
+		for o_in,v_in,o_out,v_out in opts_data['sets']:
+			if o_in in opts:
+				v = opts[o_in]
+				if (v and v_in == bool) or v == v_in:
+					if o_out in opts and opts[o_out] != v_out:
+						sys.stderr.write(
+				"Option conflict:\n  --%s=%s, with\n  --%s=%s\n" % (
+					o_out.replace("_","-"),opts[o_out],
+					o_in.replace("_","-"),opts[o_in]
+				))
+						sys.exit(1)
+					else:
+						opts[o_out] = v_out
+
 	return opts,args
 	return opts,args
 
 
 
 

+ 2 - 2
mmgen/test.py

@@ -77,14 +77,14 @@ def ok_or_die(val,chk_func,s,skip_ok=False):
 	try: ret = chk_func(val)
 	try: ret = chk_func(val)
 	except: ret = False
 	except: ret = False
 	if ret:
 	if ret:
-	   if not skip_ok: ok()
+		if not skip_ok: ok()
 	else:
 	else:
 		msg(red("Returned value '%s' is not a %s" % (val,s)))
 		msg(red("Returned value '%s' is not a %s" % (val,s)))
 		sys.exit(3)
 		sys.exit(3)
 
 
 def cmp_or_die(s,t,skip_ok=False):
 def cmp_or_die(s,t,skip_ok=False):
 	if s == t:
 	if s == t:
-	   if not skip_ok: ok()
+		if not skip_ok: ok()
 	else:
 	else:
 		sys.stderr.write(red(
 		sys.stderr.write(red(
 			"ERROR: recoded data:\n%s\ndiffers from original data:\n%s\n" %
 			"ERROR: recoded data:\n%s\ndiffers from original data:\n%s\n" %

+ 6 - 8
mmgen/tool.py

@@ -439,19 +439,17 @@ def add_label(mmaddr,label,remove=False):
 	check_addr_label(label)  # Exits on failure
 	check_addr_label(label)  # Exits on failure
 
 
 	c = connect_to_bitcoind()
 	c = connect_to_bitcoind()
+
 	from mmgen.addr import AddrInfoList
 	from mmgen.addr import AddrInfoList
-	ail = AddrInfoList(bitcoind_connection=c)
+	btcaddr = AddrInfoList(bitcoind_connection=c).mmaddr2btcaddr(mmaddr)
 
 
-	btcaddr = ""
-	sid,idx = mmaddr.split(":")
-	if sid in ail.seed_ids():
-		btcaddr = ail.addrinfo(sid).btcaddr(int(idx))
 	if not btcaddr:
 	if not btcaddr:
 		die(1,"{pnm} address {a} not found in tracking wallet".format(
 		die(1,"{pnm} address {a} not found in tracking wallet".format(
 				pnm=pnm,a=mmaddr))
 				pnm=pnm,a=mmaddr))
 
 
 	try:
 	try:
-		c.importaddress(btcaddr," ".join((mmaddr,label)),rescan=False)
+		l = " " + label if label else ""
+		c.importaddress(btcaddr,mmaddr+l,rescan=False)
 	except:
 	except:
 		die(1,"Unable to add label")
 		die(1,"Unable to add label")
 
 
@@ -508,7 +506,7 @@ def encrypt(infile,outfile="",hash_preset=""):
 	data = get_data_from_file(infile,"data for encryption")
 	data = get_data_from_file(infile,"data for encryption")
 	enc_d = mmgen_encrypt(data,"user data",hash_preset)
 	enc_d = mmgen_encrypt(data,"user data",hash_preset)
 	if outfile == '-':
 	if outfile == '-':
-		write_to_stdout(enc_d,"encrypted data",confirm=True)
+		write_to_stdout(enc_d,"encrypted data")
 	else:
 	else:
 		if not outfile:
 		if not outfile:
 			outfile = os.path.basename(infile) + "." + g.mmenc_ext
 			outfile = os.path.basename(infile) + "." + g.mmenc_ext
@@ -522,7 +520,7 @@ def decrypt(infile,outfile="",hash_preset=""):
 		if dec_d: break
 		if dec_d: break
 		msg("Trying again...")
 		msg("Trying again...")
 	if outfile == '-':
 	if outfile == '-':
-		write_to_stdout(dec_d,"decrypted data",confirm=not opt.quiet)
+		write_to_stdout(dec_d,"decrypted data",ask_terminal=not opt.quiet)
 	else:
 	else:
 		if not outfile:
 		if not outfile:
 			outfile = os.path.basename(infile)
 			outfile = os.path.basename(infile)

+ 1 - 2
mmgen/tx.py

@@ -46,8 +46,7 @@ def normalize_btc_amt(amt):
 		msg("%s: Invalid amount" % amt)
 		msg("%s: Invalid amount" % amt)
 		return False
 		return False
 
 
-	if opt.debug:
-		Msg("Decimal(amt): %s\nAs tuple: %s" % (amt,repr(ret.as_tuple())))
+	dmsg("Decimal(amt): %s\nAs tuple: %s" % (amt,repr(ret.as_tuple())))
 
 
 	if ret.as_tuple()[-1] < -8:
 	if ret.as_tuple()[-1] < -8:
 		msg("%s: Too many decimal places in amount" % amt)
 		msg("%s: Too many decimal places in amount" % amt)

+ 173 - 100
mmgen/util.py

@@ -42,10 +42,10 @@ def msg_r(s):  sys.stderr.write(s)
 def Msg(s):    sys.stdout.write(s + "\n")
 def Msg(s):    sys.stdout.write(s + "\n")
 def Msg_r(s):  sys.stdout.write(s)
 def Msg_r(s):  sys.stdout.write(s)
 def msgred(s): sys.stderr.write(red(s+"\n"))
 def msgred(s): sys.stderr.write(red(s+"\n"))
-def msgrepr(*args):
+def mmsg(*args):
 	for d in args:
 	for d in args:
 		sys.stdout.write(repr(d)+"\n")
 		sys.stdout.write(repr(d)+"\n")
-def msgrepr_exit(*args):
+def mdie(*args):
 	for d in args:
 	for d in args:
 		sys.stdout.write(repr(d)+"\n")
 		sys.stdout.write(repr(d)+"\n")
 	sys.exit()
 	sys.exit()
@@ -55,20 +55,8 @@ def die(ev,s):
 def Die(ev,s):
 def Die(ev,s):
 	sys.stdout.write(s+"\n"); sys.exit(ev)
 	sys.stdout.write(s+"\n"); sys.exit(ev)
 
 
-def fmt_type(x): return "%s" % str(type(x)).split("'")[1]
-
 import opt
 import opt
 
 
-def fmt_code_to_sstype(fmt_code):
-	for e in g.wallet_fmt_codes:
-		if fmt_code in e: return e[0]
-	die(2,"'%s': unrecognized format code" % fmt_code)
-
-def format_fmt_codes():
-	return "".join(
-		["%-20s  " % (e[0]+":") + ",".join(e[1:]) + "\n"
-			for e in g.wallet_fmt_codes])
-
 def qmsg(s,alt=False):
 def qmsg(s,alt=False):
 	if opt.quiet:
 	if opt.quiet:
 		if alt != False: sys.stderr.write(alt + "\n")
 		if alt != False: sys.stderr.write(alt + "\n")
@@ -87,7 +75,10 @@ def Vmsg(s):
 def Vmsg_r(s):
 def Vmsg_r(s):
 	if opt.verbose: sys.stdout.write(s)
 	if opt.verbose: sys.stdout.write(s)
 
 
-def suf(arg,what):
+def dmsg(s):
+	if opt.debug: sys.stdout.write(s + "\n")
+
+def suf(arg,suf_type):
 	t = type(arg)
 	t = type(arg)
 	if t == int:
 	if t == int:
 		n = arg
 		n = arg
@@ -97,9 +88,9 @@ def suf(arg,what):
 		msg("%s: invalid parameter" % arg)
 		msg("%s: invalid parameter" % arg)
 		return ""
 		return ""
 
 
-	if what in "a":
+	if suf_type in ("a","es"):
 		return "" if n == 1 else "es"
 		return "" if n == 1 else "es"
-	if what in "k":
+	if suf_type in ("k","s"):
 		return "" if n == 1 else "s"
 		return "" if n == 1 else "s"
 
 
 def get_extension(f):
 def get_extension(f):
@@ -125,10 +116,14 @@ def splitN(s,n,sep=None):                      # always return an n-element list
 def split2(s,sep=None): return splitN(s,2,sep) # always return a 2-element list
 def split2(s,sep=None): return splitN(s,2,sep) # always return a 2-element list
 def split3(s,sep=None): return splitN(s,3,sep) # always return a 3-element list
 def split3(s,sep=None): return splitN(s,3,sep) # always return a 3-element list
 
 
-def split_into_columns(col_wid,s):
+def split_into_cols(col_wid,s):
 	return " ".join([s[col_wid*i:col_wid*(i+1)]
 	return " ".join([s[col_wid*i:col_wid*(i+1)]
 					for i in range(len(s)/col_wid+1)]).rstrip()
 					for i in range(len(s)/col_wid+1)]).rstrip()
 
 
+def capfirst(s):
+	if len(s) == 0: return s
+	return s[0].upper() + (s[1:] if len(s) > 1 else "")
+
 def make_timestamp():
 def make_timestamp():
 	tv = time.gmtime(time.time())[:6]
 	tv = time.gmtime(time.time())[:6]
 	return "{:04d}{:02d}{:02d}_{:02d}{:02d}{:02d}".format(*tv)
 	return "{:04d}{:02d}{:02d}_{:02d}{:02d}{:02d}".format(*tv)
@@ -166,6 +161,13 @@ def is_utf8(s):
 def match_ext(addr,ext):
 def match_ext(addr,ext):
 	return addr.split(".")[-1] == ext
 	return addr.split(".")[-1] == ext
 
 
+def file_exists(f):
+	try:
+		os.stat(f)
+		return True
+	except:
+		return False
+
 import opt as opt
 import opt as opt
 
 
 def get_from_brain_opt_params():
 def get_from_brain_opt_params():
@@ -186,7 +188,11 @@ 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")]
 	lines = [re.sub('^['+hexdigits+']+:\s+','',l) for l in data.split("\n")]
-	return unhexlify("".join(("".join(lines).split())))
+	try:
+		return unhexlify("".join(("".join(lines).split())))
+	except:
+		msg("Data not in hexdump format")
+		return False
 
 
 def get_hash_params(hash_preset):
 def get_hash_params(hash_preset):
 	if hash_preset in g.hash_presets:
 	if hash_preset in g.hash_presets:
@@ -195,22 +201,25 @@ def get_hash_params(hash_preset):
 		msg("%s: invalid 'hash_preset' value" % hash_preset)
 		msg("%s: invalid 'hash_preset' value" % hash_preset)
 		sys.exit(3)
 		sys.exit(3)
 
 
-def compare_chksums(chk1, desc1, chk2, desc2, die=True):
+def compare_chksums(chk1, desc1, chk2, desc2, hdr="", die_on_fail=False):
 
 
 	if not chk1 == chk2:
 	if not chk1 == chk2:
-		if die:
-			die(3,"Checksum error: %s checksum (%s) doesn't match %s checksum (%s)"
-				% (desc2,chk2,desc1,chk1))
-		else: return False
+		m = "%s ERROR: %s checksum (%s) doesn't match %s checksum (%s)"\
+				% ((hdr+":\n   " if hdr else "CHECKSUM"),desc2,chk2,desc1,chk1)
+		if die_on_fail:
+			die(3,m)
+		else:
+			msg(m)
+			return False
 
 
-	vmsg("%s checksum OK (%s)" % (desc1.capitalize(),chk1))
+	vmsg("%s checksum OK (%s)" % (capfirst(desc1),chk1))
 	return True
 	return True
 
 
-def compare_or_die(val1, desc1, val2, desc2):
+def compare_or_die(val1, desc1, val2, desc2, e="Error"):
 	if cmp(val1,val2):
 	if cmp(val1,val2):
-		die(3,"Error: %s (%s) doesn't match %s (%s)"
-				% (desc2,val2,desc1,val1))
-	vmsg("%s OK (%s)" % (desc2.capitalize(),val2))
+		die(3,"%s: %s (%s) doesn't match %s (%s)"
+				% (e,desc2,val2,desc1,val1))
+	dmsg("%s OK (%s)" % (capfirst(desc2),val2))
 	return True
 	return True
 
 
 def get_default_wordlist():
 def get_default_wordlist():
@@ -224,8 +233,8 @@ def open_file_or_exit(filename,mode):
 	try:
 	try:
 		f = open(filename, mode)
 		f = open(filename, mode)
 	except:
 	except:
-		what = "reading" if 'r' in mode else "writing"
-		msg("Unable to open file '%s' for %s" % (filename,what))
+		op = "reading" if 'r' in mode else "writing"
+		msg("Unable to open file '%s' for %s" % (filename,op))
 		sys.exit(2)
 		sys.exit(2)
 	return f
 	return f
 
 
@@ -307,9 +316,9 @@ def parse_addr_idxs(arg,sep=","):
 	return sorted(set(ret))
 	return sorted(set(ret))
 
 
 
 
-def get_new_passphrase(what,passchg=False):
+def get_new_passphrase(desc,passchg=False):
 
 
-	w = "{}passphrase for {}".format("new " if passchg else "", what)
+	w = "{}passphrase for {}".format("new " if passchg else "", desc)
 	if opt.passwd_file:
 	if opt.passwd_file:
 		pw = " ".join(_get_words_from_file(opt.passwd_file,w))
 		pw = " ".join(_get_words_from_file(opt.passwd_file,w))
 	elif opt.echo_passphrase:
 	elif opt.echo_passphrase:
@@ -318,7 +327,7 @@ def get_new_passphrase(what,passchg=False):
 		for i in range(g.passwd_max_tries):
 		for i in range(g.passwd_max_tries):
 			pw = " ".join(_get_words_from_user("Enter {}: ".format(w)))
 			pw = " ".join(_get_words_from_user("Enter {}: ".format(w)))
 			pw2 = " ".join(_get_words_from_user("Repeat passphrase: "))
 			pw2 = " ".join(_get_words_from_user("Repeat passphrase: "))
-			if opt.debug: Msg("Passphrases: [%s] [%s]" % (pw,pw2))
+			dmsg("Passphrases: [%s] [%s]" % (pw,pw2))
 			if pw == pw2:
 			if pw == pw2:
 				vmsg("Passphrases match"); break
 				vmsg("Passphrases match"); break
 			else: msg("Passphrases do not match.  Try again.")
 			else: msg("Passphrases do not match.  Try again.")
@@ -332,27 +341,21 @@ def get_new_passphrase(what,passchg=False):
 
 
 
 
 def confirm_or_exit(message, question, expect="YES"):
 def confirm_or_exit(message, question, expect="YES"):
-	if not confirm_or_false(message, question, expect):
-		msg("Exiting at user request")
-		sys.exit(2)
-
-def confirm_or_false(message, question, expect="YES"):
 
 
 	m = message.strip()
 	m = message.strip()
 	if m: msg(m)
 	if m: msg(m)
 
 
-	conf_msg = "Type uppercase '%s' to confirm: " % expect
-
-	p = question+"  "+conf_msg if question[0].isupper() else \
-		"Are you sure you want to %s?\n%s" % (question,conf_msg)
+	a = question+"  " if question[0].isupper() else \
+			"Are you sure you want to %s?\n" % question
+	b = "Type uppercase '%s' to confirm: " % expect
 
 
-	vmsg("")
-	return my_raw_input(p).strip() == expect
+	if my_raw_input(a+b).strip() != expect:
+		die(2,"Exiting at user request")
 
 
 
 
-def write_to_stdout(data, what, confirm=True):
-	if sys.stdout.isatty() and confirm:
-		confirm_or_exit("",'output {} to screen'.format(what))
+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():
 	elif not sys.stdout.isatty():
 		try:
 		try:
 			of = os.readlink("/proc/%d/fd/1" % os.getpid())
 			of = os.readlink("/proc/%d/fd/1" % os.getpid())
@@ -363,8 +366,83 @@ def write_to_stdout(data, what, confirm=True):
 			msg("Redirecting output to file")
 			msg("Redirecting output to file")
 	sys.stdout.write(data)
 	sys.stdout.write(data)
 
 
+# New function
+def write_data_to_file(
+		outfile,
+		data,
+		desc="data",
+		ask_write=False,
+		ask_write_prompt="",
+		ask_write_default_yes=False,
+		ask_overwrite=True,
+		ask_tty=True,
+		no_tty=False,
+		silent=False
+	):
+	if opt.stdout or not sys.stdout.isatty():
+		qmsg("Output to STDOUT requested")
+		write_ok = False
+		if sys.stdout.isatty():
+			if no_tty:
+				die(2,"Printing %s to screen is not allowed" % desc)
+			if ask_tty:
+				confirm_or_exit("",'output %s to screen' % desc)
+		else:
+			try:    of = os.readlink("/proc/%d/fd/1" % os.getpid()) # Linux
+			except: of = None # Windows
+
+			if of:
+				if of[:5] == "pipe:":
+					if no_tty:
+						die(2,"Writing %s to pipe is not allowed" % desc)
+					if ask_tty:
+						confirm_or_exit("",'output %s to pipe' % desc)
+						msg("")
+				of2,pd = os.path.relpath(of),os.path.pardir
+				msg("Redirecting output to file '%s'" %
+						(of if of2[:len(pd)] == pd else of2))
+			else:
+				msg("Redirecting output to file")
+
+		sys.stdout.write(data)
+	else:
+		if opt.outdir: outfile = make_full_path(opt.outdir,outfile)
 
 
-def write_to_file(outfile,data,what="data",confirm_overwrite=False,verbose=False,exit_on_error=True,silent=False):
+		if ask_write:
+			if not keypress_confirm(ask_write_prompt,
+						default_yes=ask_write_default_yes):
+				die(1,"Exiting at user request")
+
+		hush = False
+		if file_exists(outfile):
+			if ask_overwrite:
+				q = "File '%s' already exists\nOverwrite?" % outfile
+				confirm_or_exit("",q)
+			if not silent: msg("Overwriting file '%s'" % outfile)
+			hush = True
+
+		f = open_file_or_exit(outfile,'wb')
+		try:
+			f.write(data)
+		except:
+			if not silent: msg("Failed to write %s to file '%s'" % (desc,outfile))
+			sys.exit(2)
+		f.close
+
+		if not hush:
+			msg("%s written to file '%s'" % (capfirst(desc),outfile))
+
+		return True
+
+
+def write_to_file(
+		outfile,
+		data,
+		desc="data",
+		confirm_overwrite=False,
+		verbose=False,
+		silent=False
+	):
 
 
 	if opt.outdir: outfile = make_full_path(opt.outdir,outfile)
 	if opt.outdir: outfile = make_full_path(opt.outdir,outfile)
 
 
@@ -373,12 +451,7 @@ def write_to_file(outfile,data,what="data",confirm_overwrite=False,verbose=False
 	else:
 	else:
 		if confirm_overwrite:
 		if confirm_overwrite:
 			q = "File '%s' already exists\nOverwrite?" % outfile
 			q = "File '%s' already exists\nOverwrite?" % outfile
-			if exit_on_error:
-				confirm_or_exit("",q)
-			else:
-				if not confirm_or_false("",q):
-					msg("Not overwriting file at user request")
-					return False
+			confirm_or_exit("",q)
 		else:
 		else:
 			if not silent: msg("Overwriting file '%s'" % outfile)
 			if not silent: msg("Overwriting file '%s'" % outfile)
 
 
@@ -386,20 +459,20 @@ def write_to_file(outfile,data,what="data",confirm_overwrite=False,verbose=False
 	try:
 	try:
 		f.write(data)
 		f.write(data)
 	except:
 	except:
-		if not silent: msg("Failed to write %s to file '%s'" % (what,outfile))
+		if not silent: msg("Failed to write %s to file '%s'" % (desc,outfile))
 		sys.exit(2)
 		sys.exit(2)
 	f.close
 	f.close
 
 
-	if verbose: msg("%s written to file '%s'" % (what.capitalize(),outfile))
+	if verbose: msg("%s written to file '%s'" % (capfirst(desc),outfile))
 	return True
 	return True
 
 
 
 
-def write_to_file_or_stdout(outfile, data,  what="data"):
+def write_to_file_or_stdout(outfile, data,  desc="data"):
 
 
 	if opt.stdout or not sys.stdout.isatty():
 	if opt.stdout or not sys.stdout.isatty():
-		write_to_stdout(data, what, confirm=True)
+		write_to_stdout(data, desc)
 	else:
 	else:
-		write_to_file(outfile,data,what,not opt.quiet,True)
+		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
@@ -437,7 +510,7 @@ def write_wallet_to_file(seed, passwd, key_id, salt, enc_seed):
 	seed_len = str(len(seed)*8)
 	seed_len = str(len(seed)*8)
 	pw_status = "NE" if len(passwd) else "E"
 	pw_status = "NE" if len(passwd) else "E"
 	hash_preset = opt.hash_preset
 	hash_preset = opt.hash_preset
-	label = opt.label if opt.label else "No Label"
+	label = opt.label or "No Label"
 	metadata = seed_id.lower(),key_id.lower(),seed_len,\
 	metadata = seed_id.lower(),key_id.lower(),seed_len,\
 		pw_status,make_timestamp()
 		pw_status,make_timestamp()
 	sf  = b58encode_pad(salt)
 	sf  = b58encode_pad(salt)
@@ -447,8 +520,8 @@ def write_wallet_to_file(seed, passwd, key_id, salt, enc_seed):
 		label,
 		label,
 		"{} {} {} {} {}".format(*metadata),
 		"{} {} {} {} {}".format(*metadata),
 		"{}: {} {} {}".format(hash_preset,*get_hash_params(hash_preset)),
 		"{}: {} {} {}".format(hash_preset,*get_hash_params(hash_preset)),
-		"{} {}".format(make_chksum_6(sf),  split_into_columns(4,sf)),
-		"{} {}".format(make_chksum_6(esf), split_into_columns(4,esf))
+		"{} {}".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))
 	chk = make_chksum_6(" ".join(lines))
@@ -465,18 +538,18 @@ def write_wallet_to_file(seed, passwd, key_id, salt, enc_seed):
 def _check_mmseed_format(words):
 def _check_mmseed_format(words):
 
 
 	valid = False
 	valid = False
-	what = "%s data" % g.seed_ext
+	desc = "%s data" % g.seed_ext
 	try:
 	try:
 		chklen = len(words[0])
 		chklen = len(words[0])
 	except:
 	except:
 		return False
 		return False
 
 
 	if len(words) < 3 or len(words) > 12:
 	if len(words) < 3 or len(words) > 12:
-		msg("Invalid data length (%s) in %s" % (len(words),what))
+		msg("Invalid data length (%s) in %s" % (len(words),desc))
 	elif not is_hexstring(words[0]):
 	elif not is_hexstring(words[0]):
-		msg("Invalid format of checksum '%s' in %s"%(words[0], what))
+		msg("Invalid format of checksum '%s' in %s"%(words[0], desc))
 	elif chklen != 6:
 	elif chklen != 6:
-		msg("Incorrect length of checksum (%s) in %s" % (chklen,what))
+		msg("Incorrect length of checksum (%s) in %s" % (chklen,desc))
 	else: valid = True
 	else: valid = True
 
 
 	return valid
 	return valid
@@ -484,19 +557,19 @@ def _check_mmseed_format(words):
 
 
 def _check_wallet_format(infile, lines):
 def _check_wallet_format(infile, lines):
 
 
-	what = "wallet file '%s'" % infile
+	desc = "wallet file '%s'" % infile
 	valid = False
 	valid = False
 	chklen = len(lines[0])
 	chklen = len(lines[0])
 	if len(lines) != 6:
 	if len(lines) != 6:
-		vmsg("Invalid number of lines (%s) in %s" % (len(lines),what))
+		vmsg("Invalid number of lines (%s) in %s" % (len(lines),desc))
 	elif chklen != 6:
 	elif chklen != 6:
-		vmsg("Incorrect length of Master checksum (%s) in %s" % (chklen,what))
+		vmsg("Incorrect length of Master checksum (%s) in %s" % (chklen,desc))
 	elif not is_hexstring(lines[0]):
 	elif not is_hexstring(lines[0]):
-		vmsg("Invalid format of Master checksum '%s' in %s"%(lines[0], what))
+		vmsg("Invalid format of Master checksum '%s' in %s"%(lines[0], desc))
 	else: valid = True
 	else: valid = True
 
 
 	if valid == False:
 	if valid == False:
-		msg("Invalid %s" % what)
+		msg("Invalid %s" % desc)
 		sys.exit(2)
 		sys.exit(2)
 
 
 
 
@@ -506,8 +579,7 @@ def _check_chksum_6(chk,val,desc,infile):
 		msg("%s checksum incorrect in file '%s'!" % (desc,infile))
 		msg("%s checksum incorrect in file '%s'!" % (desc,infile))
 		msg("Checksum: %s. Computed value: %s" % (chk,comp_chk))
 		msg("Checksum: %s. Computed value: %s" % (chk,comp_chk))
 		sys.exit(2)
 		sys.exit(2)
-	elif opt.debug:
-		Msg("%s checksum passed: %s" % (desc.capitalize(),chk))
+	dmsg("%s checksum passed: %s" % (capfirst(desc),chk))
 
 
 
 
 def get_data_from_wallet(infile,silent=False):
 def get_data_from_wallet(infile,silent=False):
@@ -557,23 +629,23 @@ def get_data_from_wallet(infile,silent=False):
 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()
-	if opt.debug: Msg("Sanitized input: [%s]" % " ".join(words))
+	dmsg("Sanitized input: [%s]" % " ".join(words))
 	return words
 	return words
 
 
 
 
-def _get_words_from_file(infile,what):
-	qmsg("Getting %s from file '%s'" % (what,infile))
+def _get_words_from_file(infile,desc):
+	qmsg("Getting %s from file '%s'" % (desc,infile))
 	f = open_file_or_exit(infile, 'r')
 	f = open_file_or_exit(infile, 'r')
 	# split() also strips
 	# split() also strips
 	words = f.read().split()
 	words = f.read().split()
 	f.close()
 	f.close()
-	if opt.debug: Msg("Sanitized input: [%s]" % " ".join(words))
+	dmsg("Sanitized input: [%s]" % " ".join(words))
 	return words
 	return words
 
 
 
 
-def get_words(infile,what,prompt):
+def get_words(infile,desc,prompt):
 	if infile:
 	if infile:
-		return _get_words_from_file(infile,what)
+		return _get_words_from_file(infile,desc)
 	else:
 	else:
 		return _get_words_from_user(prompt)
 		return _get_words_from_user(prompt)
 
 
@@ -586,24 +658,24 @@ def remove_comments(lines):
 		if i: ret.append(i)
 		if i: ret.append(i)
 	return ret
 	return ret
 
 
-def get_lines_from_file(infile,what="",trim_comments=False):
-	if what != "":
-		qmsg("Getting %s from file '%s'" % (what,infile))
+def get_lines_from_file(infile,desc="",trim_comments=False):
+	if desc != "":
+		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()
 	f.close()
 	f.close()
 	return remove_comments(lines) if trim_comments else lines
 	return remove_comments(lines) if trim_comments else lines
 
 
 
 
-def get_data_from_user(what="data",silent=False):
-	data = my_raw_input("Enter %s: " % what, echo=opt.echo_passphrase)
-	if opt.debug: Msg("User input: [%s]" % data)
+def get_data_from_user(desc="data",silent=False):
+	data = my_raw_input("Enter %s: " % desc, echo=opt.echo_passphrase)
+	dmsg("User input: [%s]" % data)
 	return data
 	return data
 
 
-def get_data_from_file(infile,what="data",dash=False,silent=False):
+def get_data_from_file(infile,desc="data",dash=False,silent=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'" % (what,infile))
+		qmsg("Getting %s from file '%s'" % (desc,infile))
 	f = open_file_or_exit(infile,'rb')
 	f = open_file_or_exit(infile,'rb')
 	data = f.read()
 	data = f.read()
 	f.close()
 	f.close()
@@ -622,7 +694,7 @@ def get_seed_from_seed_data(words):
 	chk = make_chksum_6(seed_b58)
 	chk = make_chksum_6(seed_b58)
 	vmsg_r("Validating %s checksum..." % g.seed_ext)
 	vmsg_r("Validating %s checksum..." % g.seed_ext)
 
 
-	if compare_chksums(chk, "seed", stored_chk, "input",die=False):
+	if compare_chksums(chk, "seed", stored_chk, "input"):
 		seed = b58decode_pad(seed_b58)
 		seed = b58decode_pad(seed_b58)
 		if seed == False:
 		if seed == False:
 			msg("Invalid b58 number: %s" % val)
 			msg("Invalid b58 number: %s" % val)
@@ -645,9 +717,8 @@ def mark_passwd_file_as_used():
 	passwd_file_used = True
 	passwd_file_used = True
 
 
 
 
-def get_mmgen_passphrase(prompt_info,passchg=False):
-	prompt = "Enter {}passphrase for {}: ".format(
-			"old " if passchg else "",prompt_info)
+def get_mmgen_passphrase(desc,passchg=False):
+	prompt ="Enter {}passphrase for {}: ".format("old " if passchg else "",desc)
 	if opt.passwd_file:
 	if opt.passwd_file:
 		mark_passwd_file_as_used()
 		mark_passwd_file_as_used()
 		return " ".join(_get_words_from_file(opt.passwd_file,"passphrase"))
 		return " ".join(_get_words_from_file(opt.passwd_file,"passphrase"))
@@ -674,17 +745,18 @@ def check_data_fits_file_at_offset(fname,offset,dlen,action):
 		fsize = os.stat(fname).st_size
 		fsize = os.stat(fname).st_size
 
 
 	if fsize < offset + dlen:
 	if fsize < offset + dlen:
+		m = "Destination" if action == "write" else "Input"
 		msg(
 		msg(
-"Destination file has length %s, too short to %s %s bytes of data at offset %s"
-			% (fsize,action,dlen,offset))
+	"%s file has length %s, too short to %s %s bytes of data at offset %s"
+			% (m,fsize,action,dlen,offset))
 		sys.exit(1)
 		sys.exit(1)
 
 
 
 
 from mmgen.term import kb_hold_protect,get_char
 from mmgen.term import kb_hold_protect,get_char
 
 
-def get_hash_preset_from_user(hp=g.hash_preset,what="data"):
+def get_hash_preset_from_user(hp=g.hash_preset,desc="data"):
 	p = """Enter hash preset for %s,
 	p = """Enter hash preset for %s,
-or hit ENTER to accept the default value ('%s'): """ % (what,hp)
+ or hit ENTER to accept the default value ('%s'): """ % (desc,hp)
 	while True:
 	while True:
 		ret = my_raw_input(p)
 		ret = my_raw_input(p)
 		if ret:
 		if ret:
@@ -751,17 +823,18 @@ def prompt_and_get_char(prompt,chars,enter_ok=False,verbose=False):
 
 
 def do_license_msg(immed=False):
 def do_license_msg(immed=False):
 
 
-	from mmgen.license import gpl
+	import mmgen.license as gpl
 	if opt.quiet or g.no_license: return
 	if opt.quiet or g.no_license: return
 
 
-	msg(gpl['warning'])
-	prompt = "%s " % gpl['prompt'].strip()
+	p = "Press 'w' for conditions and warranty info, or 'c' to continue:"
+	msg(gpl.warning)
+	prompt = "%s " % p.strip()
 
 
 	while True:
 	while True:
 		reply = get_char(prompt, immed_chars="wc" if immed else "")
 		reply = get_char(prompt, immed_chars="wc" if immed else "")
 		if reply == 'w':
 		if reply == 'w':
 			from mmgen.term import do_pager
 			from mmgen.term import do_pager
-			do_pager(gpl['conditions'])
+			do_pager(gpl.conditions)
 		elif reply == 'c':
 		elif reply == 'c':
 			msg(""); break
 			msg(""); break
 		else:
 		else:

+ 2 - 0
setup.py

@@ -59,6 +59,7 @@ setup(
 			'mmgen.main_txsend',
 			'mmgen.main_txsend',
 			'mmgen.main_txsign',
 			'mmgen.main_txsign',
 			'mmgen.main_walletchk',
 			'mmgen.main_walletchk',
+			'mmgen.main_walletconv',
 			'mmgen.main_walletgen',
 			'mmgen.main_walletgen',
 
 
 			'mmgen.share.__init__',
 			'mmgen.share.__init__',
@@ -83,6 +84,7 @@ setup(
 			'mmgen-addrimport',
 			'mmgen-addrimport',
 			'mmgen-passchg',
 			'mmgen-passchg',
 			'mmgen-walletchk',
 			'mmgen-walletchk',
+			'mmgen-walletconv',
 			'mmgen-walletgen',
 			'mmgen-walletgen',
 			'mmgen-txcreate',
 			'mmgen-txcreate',
 			'mmgen-txsign',
 			'mmgen-txsign',

+ 5 - 3
test/gentest.py

@@ -11,8 +11,8 @@ sys.path.__setitem__(0,os.path.abspath(os.curdir))
 from binascii import hexlify
 from binascii import hexlify
 
 
 import mmgen.opt as opt
 import mmgen.opt as opt
-import mmgen.config as g
-from mmgen.util import msg,msg_r,msgrepr,msgrepr_exit,red,green
+import mmgen.globalvars as g
+from mmgen.util import msg,msg_r,mmsg,mdie,red,green,vmsg
 from mmgen.bitcoin import hextowif,privnum2addr
 from mmgen.bitcoin import hextowif,privnum2addr
 
 
 rounds = 100
 rounds = 100
@@ -23,6 +23,7 @@ opts_data = {
 -h, --help         Print this help message
 -h, --help         Print this help message
 -s, --system       Test scripts and modules installed on system rather than
 -s, --system       Test scripts and modules installed on system rather than
                    those in the repo root
                    those in the repo root
+-v, --verbose      Produce more verbose output
 """,
 """,
 	'notes': """
 	'notes': """
 
 
@@ -63,6 +64,7 @@ for i in range(1,rounds+1):
 	sec = hexlify(os.urandom(32))
 	sec = hexlify(os.urandom(32))
 	wif = hextowif(sec)
 	wif = hextowif(sec)
 	a = privnum2addr(int(sec,16))
 	a = privnum2addr(int(sec,16))
+	vmsg("\nkey:  %s\naddr: %s\n" % (wif,a))
 	b = check_output(["keyconv", wif]).split()[1]
 	b = check_output(["keyconv", wif]).split()[1]
 	if a != b:
 	if a != b:
 		msg_r(red("\nERROR: Addresses do not match!"))
 		msg_r(red("\nERROR: Addresses do not match!"))
@@ -74,4 +76,4 @@ for i in range(1,rounds+1):
 """.format(sec,wif,a,b,pnm=g.proj_name).rstrip())
 """.format(sec,wif,a,b,pnm=g.proj_name).rstrip())
 		sys.exit(3)
 		sys.exit(3)
 
 
-msg(green("\nOK"))
+msg(green("%sOK" % ("" if opt.verbose else "\n")))

+ 2 - 0
test/ref/1378FC64-2907DE97-F980D21F[192,1].mmincog

@@ -0,0 +1,2 @@
+H@&оRLrT.╝у%╛е7© ычeчЬа╧:x%ё5┐НoЫ*ю!вИ	д
+Мё0╬sЪ│ХёR%┤Pьг

+ 4 - 0
test/ref/1378FC64-4DCB5174-872806A7[192,1].mmincox

@@ -0,0 +1,4 @@
+8e7a aa61 cf9d acba 10ec cda2 a54c a64e
+ee54 970a ebdc f44d 53d2 c005 46ea 1ab3
+d940 0d78 6050 16a8 4ef6 e228 661a 803a
+38ea 6874 189f b022 db94 3e11 051b 0302

+ 5 - 0
test/ref/98831F3A-1630A9F2-870376A9[256,1].mmincox

@@ -0,0 +1,5 @@
+078b 15e6 42c1 8447 d5a4 f03c b3ae aab9
+7d79 2640 c307 bf2a 78a6 847b 44f2 6d96
+7d9b 44ac 193c a47a 9a12 6e60 9f8f fe96
+720b 0682 01d8 7da4 9eea be67 60c6 cbcf
+d522 5a97 8bc2 5554

+ 1 - 0
test/ref/98831F3A-5482381C-18460FB1[256,1].mmincog

@@ -0,0 +1 @@
+▒╧и7м8╩≈vЮЛP  0,Ъ%▐║F╖║ДБNИ`зtсЛ╫$╟@≥pk%┘рL}нЯQГА▄≥p/╨=З╘-rО~GОС9

+ 4 - 0
test/ref/FE3C6545-BC4BE3F2-32586837[128,1].mmincox

@@ -0,0 +1,4 @@
+80f5 2531 04ab 4de4 11fd ccb8 aae4 26ed
+2520 8218 e5e1 8744 d367 9e9d 3491 ff8e
+f83e 523b 36f7 dbe5 3c92 aeca c935 15fa
+1bc5 2c54 0e4c 0825

BIN
test/ref/FE3C6545-E29303EA-5E229E30[128,1].mmincog


BIN
test/ref/sample-text.mmenc


+ 331 - 125
test/test.py

@@ -10,7 +10,7 @@ sys.path.__setitem__(0,os.path.abspath(os.curdir))
 
 
 import mmgen.globalvars as g
 import mmgen.globalvars as g
 import mmgen.opt as opt
 import mmgen.opt as opt
-from mmgen.util import msgrepr,msgrepr_exit,Msg,die
+from mmgen.util import mmsg,mdie,Msg,die
 from mmgen.test import *
 from mmgen.test import *
 
 
 hincog_fn      = "rand_data"
 hincog_fn      = "rand_data"
@@ -35,6 +35,9 @@ ref_kafile_pass        = "kafile password"
 ref_kafile_hash_preset = "1"
 ref_kafile_hash_preset = "1"
 
 
 ref_enc_fn = "sample-text.mmenc"
 ref_enc_fn = "sample-text.mmenc"
+tool_enc_passwd = "Scrypt it, don't hash it!"
+sample_text = \
+	"The Times 03/Jan/2009 Chancellor on brink of second bailout for banks\n"
 
 
 cfgs = {
 cfgs = {
 	'6': {
 	'6': {
@@ -46,8 +49,11 @@ cfgs = {
 		'keyaddrfile_chk': "CF83 32FB 8A8B 08E2 0F00 D601",
 		'keyaddrfile_chk': "CF83 32FB 8A8B 08E2 0F00 D601",
 		'wpasswd':         "reference password",
 		'wpasswd':         "reference password",
 		'ref_wallet':      "FE3C6545-D782B529[128,1].mmdat",
 		'ref_wallet':      "FE3C6545-D782B529[128,1].mmdat",
-		'ic_wallet':       "FE3C6545-161E495F-BEB7548E[128:1].incog-offset123",
-		'ic_wallet_old':   "FE3C6545-161E495F-9860A85B[128:1].incog-old.offset123",
+		'ic_wallet':       "FE3C6545-E29303EA-5E229E30[128,1].mmincog",
+		'ic_wallet_hex':   "FE3C6545-BC4BE3F2-32586837[128,1].mmincox",
+
+		'hic_wallet':       "FE3C6545-161E495F-BEB7548E[128:1].incog-offset123",
+		'hic_wallet_old':   "FE3C6545-161E495F-9860A85B[128:1].incog-old.offset123",
 
 
 		'tmpdir':        os.path.join("test","tmp6"),
 		'tmpdir':        os.path.join("test","tmp6"),
 		'kapasswd':      "",
 		'kapasswd':      "",
@@ -68,8 +74,11 @@ cfgs = {
 		'keyaddrfile_chk': "9648 5132 B98E 3AD9 6FC3 C5AD",
 		'keyaddrfile_chk': "9648 5132 B98E 3AD9 6FC3 C5AD",
 		'wpasswd':         "reference password",
 		'wpasswd':         "reference password",
 		'ref_wallet':      "1378FC64-6F0F9BB4[192,1].mmdat",
 		'ref_wallet':      "1378FC64-6F0F9BB4[192,1].mmdat",
-		'ic_wallet':       "1378FC64-B55E9958-77256FC1[192:1].incog.offset123",
-		'ic_wallet_old':   "1378FC64-B55E9958-D85FF20C[192:1].incog-old.offset123",
+		'ic_wallet':       "1378FC64-2907DE97-F980D21F[192,1].mmincog",
+		'ic_wallet_hex':   "1378FC64-4DCB5174-872806A7[192,1].mmincox",
+
+		'hic_wallet':       "1378FC64-B55E9958-77256FC1[192:1].incog.offset123",
+		'hic_wallet_old':   "1378FC64-B55E9958-D85FF20C[192:1].incog-old.offset123",
 
 
 		'tmpdir':        os.path.join("test","tmp7"),
 		'tmpdir':        os.path.join("test","tmp7"),
 		'kapasswd':      "",
 		'kapasswd':      "",
@@ -97,8 +106,11 @@ cfgs = {
 
 
 #		'ref_fake_unspent_data':"98831F3A_unspent.json",
 #		'ref_fake_unspent_data':"98831F3A_unspent.json",
 		'ref_tx_file':     "tx_FFB367[1.234].raw",
 		'ref_tx_file':     "tx_FFB367[1.234].raw",
-		'ic_wallet':       "98831F3A-F59B07A0-559CEF19[256:1].incog.offset123",
-		'ic_wallet_old':   "98831F3A-F59B07A0-848535F3[256:1].incog-old.offset123",
+		'ic_wallet':       "98831F3A-5482381C-18460FB1[256,1].mmincog",
+		'ic_wallet_hex':   "98831F3A-1630A9F2-870376A9[256,1].mmincox",
+
+		'hic_wallet':       "98831F3A-F59B07A0-559CEF19[256:1].incog.offset123",
+		'hic_wallet_old':   "98831F3A-F59B07A0-848535F3[256:1].incog-old.offset123",
 
 
 		'tmpdir':        os.path.join("test","tmp8"),
 		'tmpdir':        os.path.join("test","tmp8"),
 		'kapasswd':      "",
 		'kapasswd':      "",
@@ -108,7 +120,6 @@ cfgs = {
 			'addrs':       "refaddrgen3",
 			'addrs':       "refaddrgen3",
 			'akeys.mmenc': "refkeyaddrgen3"
 			'akeys.mmenc': "refkeyaddrgen3"
 		},
 		},
-
 	},
 	},
 	'1': {
 	'1': {
 		'tmpdir':        os.path.join("test","tmp1"),
 		'tmpdir':        os.path.join("test","tmp1"),
@@ -133,7 +144,7 @@ cfgs = {
 		'tmpdir':        os.path.join("test","tmp2"),
 		'tmpdir':        os.path.join("test","tmp2"),
 		'wpasswd':       "Hodling away",
 		'wpasswd':       "Hodling away",
 		'addr_idx_list': "37,45,3-6,22-23",  # 8 addresses
 		'addr_idx_list': "37,45,3-6,22-23",  # 8 addresses
-        'seed_len':      128,
+		'seed_len':      128,
 		'dep_generators': {
 		'dep_generators': {
 			'mmdat':       "walletgen2",
 			'mmdat':       "walletgen2",
 			'addrs':       "addrgen2",
 			'addrs':       "addrgen2",
@@ -157,7 +168,7 @@ cfgs = {
 		'tmpdir':        os.path.join("test","tmp4"),
 		'tmpdir':        os.path.join("test","tmp4"),
 		'wpasswd':       "Hashrate rising",
 		'wpasswd':       "Hashrate rising",
 		'addr_idx_list': "63,1004,542-544,7-9", # 8 addresses
 		'addr_idx_list': "63,1004,542-544,7-9", # 8 addresses
-        'seed_len':      192,
+		'seed_len':      192,
 		'dep_generators': {
 		'dep_generators': {
 			'mmdat':       "walletgen4",
 			'mmdat':       "walletgen4",
 			'mmbrain':     "walletgen4",
 			'mmbrain':     "walletgen4",
@@ -177,9 +188,6 @@ cfgs = {
 	},
 	},
 	'9': {
 	'9': {
 		'tmpdir':        os.path.join("test","tmp9"),
 		'tmpdir':        os.path.join("test","tmp9"),
-		'tool_enc_passwd': "Scrypt it, don't hash it!",
-		'sample_text':
-	"The Times 03/Jan/2009 Chancellor on brink of second bailout for banks\n",
 		'tool_enc_infn':      "tool_encrypt.in",
 		'tool_enc_infn':      "tool_encrypt.in",
 #		'tool_enc_ref_infn':  "tool_encrypt_ref.in",
 #		'tool_enc_ref_infn':  "tool_encrypt_ref.in",
 		'dep_generators': {
 		'dep_generators': {
@@ -191,46 +199,14 @@ cfgs = {
 	},
 	},
 }
 }
 
 
+from copy import deepcopy
+for a,b in ('6','11'),('7','12'),('8','13'):
+	cfgs[b] = deepcopy(cfgs[a])
+	cfgs[b]['tmpdir'] = os.path.join("test","tmp"+b)
+
 from collections import OrderedDict
 from collections import OrderedDict
 cmd_data = OrderedDict([
 cmd_data = OrderedDict([
 #     test               description                  depends
 #     test               description                  depends
-	# Check saved reference files:
-	['ref_wallet_chk1', (6,'saved reference wallet (128-bit)', [[[],6]])],
-	['ref_wallet_chk2', (7,'saved reference wallet (192-bit)', [[[],7]])],
-	['ref_wallet_chk3', (8,'saved reference wallet (256-bit)', [[[],8]])],
-	['ref_seed_chk1',   (6,'saved seed file (128-bit)', [[[],6]])],
-	['ref_seed_chk2',   (7,'saved seed file (192-bit)', [[[],7]])],
-	['ref_seed_chk3',   (8,'saved seed file (256-bit)', [[[],8]])],
-	['ref_mn_chk1',     (6,'saved mnemonic file (128-bit)', [[[],6]])],
-	['ref_mn_chk2',     (7,'saved mnemonic file (192-bit)', [[[],7]])],
-	['ref_mn_chk3',     (8,'saved mnemonic file (256-bit)', [[[],8]])],
-	['ref_incog_chk1',  (6,'saved incog reference wallet (128-bit)', [[[],6]])],
-	['ref_incog_chk2',  (7,'saved incog reference wallet (192-bit)', [[[],7]])],
-	['ref_incog_chk3',  (8,'saved incog reference wallet (256-bit)', [[[],8]])],
-	['ref_brain_chk1',  (6,'saved brainwallet (128-bit)', [[[],6]])],
-	['ref_brain_chk2',  (7,'saved brainwallet (192-bit)', [[[],7]])],
-	['ref_brain_chk3',  (8,'saved brainwallet (256-bit)', [[[],8]])],
-	['ref_brain_chk3_spc', (8,'saved brainwallet (256-bit, non-standard spacing)', [[[],8]])],
-
-	['ref_addrfile_chk',  (8,'saved reference address file', [[[],8]])],
-	['ref_keyaddrfile_chk', (8,'saved reference key-address file', [[[],8]])],
-# Create the fake inputs:
-#	['txcreate8',        (8,'transaction creation (8)',  [[["addrs"],8]])],
-	['ref_tx_chk',       (8,'saved reference tx file', [[[],8]])],
-
-	['ref_tool_decrypt', (9,'decryption of saved MMGen-encrypted file', [[[],9]])],
-
-	# Generate new reference ('abc' brainwallet) files:
-	['refwalletgen1', (6,'gen new refwallet (128-bit)', [[[],6]])],
-	['refwalletgen2', (7,'gen new refwallet (192-bit)', [[[],7]])],
-	['refwalletgen3', (8,'gen new refwallet (256-bit)', [[[],8]])],
-	['refaddrgen1',   (6,'new refwallet addr chksum (128-bit)', [[["mmdat"],6]])],
-	['refaddrgen2',   (7,'new refwallet addr chksum (192-bit)', [[["mmdat"],7]])],
-	['refaddrgen3',   (8,'new refwallet addr chksum (256-bit)', [[["mmdat"],8]])],
-	['refkeyaddrgen1', (6,'new refwallet key-addr chksum (128-bit)', [[["mmdat"],6]])],
-	['refkeyaddrgen2', (7,'new refwallet key-addr chksum (192-bit)', [[["mmdat"],7]])],
-	['refkeyaddrgen3', (8,'new refwallet key-addr chksum (256-bit)', [[["mmdat"],8]])],
-
 	['walletgen',       (1,'wallet generation',        [[[],1]])],
 	['walletgen',       (1,'wallet generation',        [[[],1]])],
 #	['walletchk',       (1,'wallet check',             [[["mmdat"],1]])],
 #	['walletchk',       (1,'wallet check',             [[["mmdat"],1]])],
 	['passchg',         (5,'password, label and hash preset change',[[["mmdat"],1]])],
 	['passchg',         (5,'password, label and hash preset change',[[["mmdat"],1]])],
@@ -256,13 +232,11 @@ cmd_data = OrderedDict([
 	['keyaddrgen',    (1,'key-address file generation', [[["mmdat"],1]])],
 	['keyaddrgen',    (1,'key-address file generation', [[["mmdat"],1]])],
 	['txsign_keyaddr',(1,'transaction signing with key-address file', [[["akeys.mmenc","raw"],1]])],
 	['txsign_keyaddr',(1,'transaction signing with key-address file', [[["akeys.mmenc","raw"],1]])],
 
 
-#	['walletgen2',(2,'wallet generation (2)',     [])],
 	['walletgen2',(2,'wallet generation (2), 128-bit seed',     [])],
 	['walletgen2',(2,'wallet generation (2), 128-bit seed',     [])],
 	['addrgen2',  (2,'address generation (2)',    [[["mmdat"],2]])],
 	['addrgen2',  (2,'address generation (2)',    [[["mmdat"],2]])],
 	['txcreate2', (2,'transaction creation (2)',  [[["addrs"],2]])],
 	['txcreate2', (2,'transaction creation (2)',  [[["addrs"],2]])],
 	['txsign2',   (2,'transaction signing, two transactions',[[["mmdat","raw"],1],[["mmdat","raw"],2]])],
 	['txsign2',   (2,'transaction signing, two transactions',[[["mmdat","raw"],1],[["mmdat","raw"],2]])],
 	['export_mnemonic2', (2,'seed export to mmwords format (2)',[[["mmdat"],2]])],
 	['export_mnemonic2', (2,'seed export to mmwords format (2)',[[["mmdat"],2]])],
-#	['export_mnemonic2', (2,'seed export to mmwords format (2), 128-bit seed (WIP)',[[["mmdat"],2]])],
 
 
 	['walletgen3',(3,'wallet generation (3)',                  [])],
 	['walletgen3',(3,'wallet generation (3)',                  [])],
 	['addrgen3',  (3,'address generation (3)',                 [[["mmdat"],3]])],
 	['addrgen3',  (3,'address generation (3)',                 [[["mmdat"],3]])],
@@ -270,18 +244,83 @@ cmd_data = OrderedDict([
 	['txsign3',   (3,'tx signing with inputs and outputs from two wallets',[[["mmdat"],1],[["mmdat","raw"],3]])],
 	['txsign3',   (3,'tx signing with inputs and outputs from two wallets',[[["mmdat"],1],[["mmdat","raw"],3]])],
 
 
 	['walletgen4',(4,'wallet generation (4) (brainwallet)',    [])],
 	['walletgen4',(4,'wallet generation (4) (brainwallet)',    [])],
-#	['walletgen4',(4,'wallet generation (4) (brainwallet, 192-bit seed (WIP))', [])],
 	['addrgen4',  (4,'address generation (4)',                 [[["mmdat"],4]])],
 	['addrgen4',  (4,'address generation (4)',                 [[["mmdat"],4]])],
 	['txcreate4', (4,'tx creation with inputs and outputs from four seed sources, plus non-MMGen inputs and outputs', [[["addrs"],1],[["addrs"],2],[["addrs"],3],[["addrs"],4]])],
 	['txcreate4', (4,'tx creation with inputs and outputs from four seed sources, plus non-MMGen inputs and outputs', [[["addrs"],1],[["addrs"],2],[["addrs"],3],[["addrs"],4]])],
 	['txsign4',   (4,'tx signing with inputs and outputs from incog file, mnemonic file, wallet and brainwallet, plus non-MMGen inputs and outputs', [[["mmincog"],1],[["mmwords"],2],[["mmdat"],3],[["mmbrain","raw"],4]])],
 	['txsign4',   (4,'tx signing with inputs and outputs from incog file, mnemonic file, wallet and brainwallet, plus non-MMGen inputs and outputs', [[["mmincog"],1],[["mmwords"],2],[["mmdat"],3],[["mmbrain","raw"],4]])],
 	['tool_encrypt',     (9,"'mmgen-tool encrypt' (random data)",     [])],
 	['tool_encrypt',     (9,"'mmgen-tool encrypt' (random data)",     [])],
-	['tool_decrypt',     (9,"'mmgen-tool decrypt' (random data)",
-		[[[cfgs['9']['tool_enc_infn'],
-		   cfgs['9']['tool_enc_infn']+".mmenc"],9]])],
+	['tool_decrypt',     (9,"'mmgen-tool decrypt' (random data)", [[[cfgs['9']['tool_enc_infn'],cfgs['9']['tool_enc_infn']+".mmenc"],9]])],
 #	['tool_encrypt_ref', (9,"'mmgen-tool encrypt' (reference text)",  [])],
 #	['tool_encrypt_ref', (9,"'mmgen-tool encrypt' (reference text)",  [])],
 	['tool_find_incog_data', (9,"'mmgen-tool find_incog_data'", [[[hincog_fn],1],[[incog_id_fn],1]])],
 	['tool_find_incog_data', (9,"'mmgen-tool find_incog_data'", [[[hincog_fn],1],[[incog_id_fn],1]])],
 ])
 ])
 
 
+# saved reference data
+cmd_data_ref = (
+	# reading
+	('ref_wallet_chk', ([],'saved reference wallet')),
+	('ref_seed_chk',   ([],'saved seed file')),
+	('ref_mn_chk',     ([],'saved mnemonic file')),
+	('ref_hincog_chk', ([],'saved hidden incog reference wallet')),
+	('ref_brain_chk',  ([],'saved brainwallet')),
+	# generating new reference ('abc' brainwallet) files:
+	('refwalletgen',   ([],'gen new refwallet')),
+	('refaddrgen',     (["mmdat"],'new refwallet addr chksum')),
+	('refkeyaddrgen',  (["mmdat"],'new refwallet key-addr chksum'))
+)
+
+# misc. saved reference data
+cmd_data_ref_other = (
+	('ref_addrfile_chk',   'saved reference address file'),
+	('ref_keyaddrfile_chk','saved reference key-address file'),
+#	Create the fake inputs:
+#	('txcreate8',          'transaction creation (8)'),
+	('ref_tx_chk',         'saved reference tx file'),
+	('ref_brain_chk_spc3', 'saved brainwallet (non-standard spacing)'),
+	('ref_tool_decrypt',   'decryption of saved MMGen-encrypted file'),
+)
+
+# mmgen-walletconv:
+cmd_data_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_brain_conv',     'conversion of ref brainwallet'),
+	('ref_incog_conv',     'conversion of saved incog wallet'),
+	('ref_incox_conv',     'conversion of saved hex incog wallet'),
+	('ref_hincog_conv',    'conversion of saved hidden incog wallet'),
+	('ref_hincog_conv_old','conversion of saved hidden incog wallet (old format)')
+)
+cmd_data_conv_out = ( # writing
+	('ref_wallet_conv_out', 'ref seed conversion to wallet'),
+	('ref_mn_conv_out',     'ref seed conversion to mnemonic'),
+	('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'),
+	('ref_hincog_conv_out', 'ref seed conversion to hidden incog data')
+)
+
+cmd_groups = OrderedDict([
+	('main',      cmd_data.keys()),
+	('ref',       [c[0]+str(i) for c in cmd_data_ref for i in (1,2,3)]),
+	('ref_other', [c[0] for c in cmd_data_ref_other]),
+	('conv_in',   [c[0]+str(i) for c in cmd_data_conv_in for i in (1,2,3)]),
+	('conv_out',  [c[0]+str(i) for c in cmd_data_conv_out for i in (1,2,3)]),
+])
+
+for a,b in cmd_data_ref:
+	for i,j in (1,128),(2,192),(3,256):
+		cmd_data[a+str(i)] = (5+i,"%s (%s-bit)" % (b[1],j),[[b[0],5+i]])
+
+for a,b in cmd_data_ref_other:
+	cmd_data[a] = (8,b,[[[],8]])
+
+for a,b in cmd_data_conv_in:
+	for i,j in (1,128),(2,192),(3,256):
+		cmd_data[a+str(i)] = (10+i,"%s (%s-bit)" % (b,j),[[[],10+i]])
+
+for a,b in cmd_data_conv_out:
+	for i,j in (1,128),(2,192),(3,256):
+		cmd_data[a+str(i)] = (10+i,"%s (%s-bit)" % (b,j),[[[],10+i]])
+
 utils = {
 utils = {
 	'check_deps': 'check dependencies for specified command',
 	'check_deps': 'check dependencies for specified command',
 	'clean':      'clean specified tmp dir(s) 1,2,3,4,5 or 6 (no arg = all dirs)',
 	'clean':      'clean specified tmp dir(s) 1,2,3,4,5 or 6 (no arg = all dirs)',
@@ -296,23 +335,34 @@ for k in cfgs.keys():
 		cfgs[k]['amts'][idx] = "%s.%s" % ((getrandnum(2) % mod), str(getrandnum(4))[:5])
 		cfgs[k]['amts'][idx] = "%s.%s" % ((getrandnum(2) % mod), str(getrandnum(4))[:5])
 
 
 meta_cmds = OrderedDict([
 meta_cmds = OrderedDict([
-	['saved_ref1', (6,("ref_wallet_chk1","ref_seed_chk1","ref_mn_chk1","ref_brain_chk1","ref_incog_chk1"))],
-	['saved_ref2', (7,("ref_wallet_chk2","ref_seed_chk2","ref_mn_chk2","ref_brain_chk2","ref_incog_chk2"))],
-	['saved_ref3', (8,("ref_wallet_chk3","ref_seed_chk3","ref_mn_chk3","ref_brain_chk3","ref_incog_chk3","ref_brain_chk3_spc"))],
-	['saved_ref_other',  (8,("ref_addrfile_chk","ref_tx_chk","ref_tool_decrypt"))],
-	['ref1', (6,("refwalletgen1","refaddrgen1","refkeyaddrgen1"))],
-	['ref2', (7,("refwalletgen2","refaddrgen2","refkeyaddrgen2"))],
-	['ref3', (8,("refwalletgen3","refaddrgen3","refkeyaddrgen3"))],
-	['gen',  (1,("walletgen","addrgen"))],
-	['pass', (5,("passchg","walletchk_newpass"))],
-	['tx',   (1,("txcreate","txsign","txsend"))],
-	['export', (1,[k for k in cmd_data if k[:7] == "export_" and cmd_data[k][0] == 1])],
-	['gen_sp', (1,[k for k in cmd_data if k[:8] == "addrgen_" and cmd_data[k][0] == 1])],
-	['online', (1,("keyaddrgen","txsign_keyaddr"))],
-	['2', (2,[k for k in cmd_data if cmd_data[k][0] == 2])],
-	['3', (3,[k for k in cmd_data if cmd_data[k][0] == 3])],
-	['4', (4,[k for k in cmd_data if cmd_data[k][0] == 4])],
-	['tool', (9,("tool_encrypt","tool_decrypt","tool_find_incog_data"))],
+	['ref1', ("refwalletgen1","refaddrgen1","refkeyaddrgen1")],
+	['ref2', ("refwalletgen2","refaddrgen2","refkeyaddrgen2")],
+	['ref3', ("refwalletgen3","refaddrgen3","refkeyaddrgen3")],
+	['gen',  ("walletgen","addrgen")],
+	['pass', ("passchg","walletchk_newpass")],
+	['tx',   ("addrimport","txcreate","txsign","txsend")],
+	['export', [k for k in cmd_data if k[:7] == "export_" and cmd_data[k][0] == 1]],
+	['gen_sp', [k for k in cmd_data if k[:8] == "addrgen_" and cmd_data[k][0] == 1]],
+	['online', ("keyaddrgen","txsign_keyaddr")],
+	['2', [k for k in cmd_data if cmd_data[k][0] == 2]],
+	['3', [k for k in cmd_data if cmd_data[k][0] == 3]],
+	['4', [k for k in cmd_data if cmd_data[k][0] == 4]],
+
+	['tool', ("tool_encrypt","tool_decrypt","tool_find_incog_data")],
+
+	['saved_ref1', [c[0]+"1" for c in cmd_data_ref]],
+	['saved_ref2', [c[0]+"2" for c in cmd_data_ref]],
+	['saved_ref3', [c[0]+"3" for c in cmd_data_ref]],
+
+	['saved_ref_other', [c[0] for c in cmd_data_ref_other]],
+
+	['saved_ref_conv_in1', [c[0]+"1" for c in cmd_data_conv_in]],
+	['saved_ref_conv_in2', [c[0]+"2" for c in cmd_data_conv_in]],
+	['saved_ref_conv_in3', [c[0]+"3" for c in cmd_data_conv_in]],
+
+	['saved_ref_conv_out1', [c[0]+"1" for c in cmd_data_conv_out]],
+	['saved_ref_conv_out2', [c[0]+"2" for c in cmd_data_conv_out]],
+	['saved_ref_conv_out3', [c[0]+"3" for c in cmd_data_conv_out]],
 ])
 ])
 
 
 opts_data = {
 opts_data = {
@@ -350,8 +400,6 @@ else:
 	send_delay = 0
 	send_delay = 0
 	os.environ["MMGEN_DISABLE_HOLD_PROTECT"] = "1"
 	os.environ["MMGEN_DISABLE_HOLD_PROTECT"] = "1"
 
 
-if opt.debug: opt.verbose = True
-
 if opt.exact_output:
 if opt.exact_output:
 	def msg(s): pass
 	def msg(s): pass
 	vmsg = vmsg_r = msg_r = msg
 	vmsg = vmsg_r = msg_r = msg
@@ -378,15 +426,22 @@ def errmsg_r(s): stderr_save.write(s)
 
 
 if opt.list_cmds:
 if opt.list_cmds:
 	fs = "  {:<{w}} - {}"
 	fs = "  {:<{w}} - {}"
-	Msg("Available commands:")
+	Msg("AVAILABLE COMMANDS:")
 	w = max([len(i) for i in cmd_data])
 	w = max([len(i) for i in cmd_data])
 	for cmd in cmd_data:
 	for cmd in cmd_data:
 		Msg(fs.format(cmd,cmd_data[cmd][1],w=w))
 		Msg(fs.format(cmd,cmd_data[cmd][1],w=w))
-	Msg("\nAvailable metacommands:")
+
 	w = max([len(i) for i in meta_cmds])
 	w = max([len(i) for i in meta_cmds])
+	Msg("\nAVAILABLE METACOMMANDS:")
 	for cmd in meta_cmds:
 	for cmd in meta_cmds:
-		Msg(fs.format(cmd," + ".join(meta_cmds[cmd][1]),w=w))
-	Msg("\nAvailable utilities:")
+		Msg(fs.format(cmd," ".join(meta_cmds[cmd]),w=w))
+
+	w = max([len(i) for i in cmd_groups.keys()])
+	Msg("\nAVAILABLE COMMAND GROUPS:")
+	for g in cmd_groups.keys():
+		Msg(fs.format(g," ".join(cmd_groups[g]),w=w))
+
+	Msg("\nAVAILABLE UTILITIES:")
 	w = max([len(i) for i in utils])
 	w = max([len(i) for i in utils])
 	for cmd in sorted(utils):
 	for cmd in sorted(utils):
 		Msg(fs.format(cmd,utils[cmd],w=w))
 		Msg(fs.format(cmd,utils[cmd],w=w))
@@ -428,7 +483,8 @@ def my_expect(p,s,t='',delay=send_delay,regex=False,nonl=False):
 	else:
 	else:
 		if t == '':
 		if t == '':
 			if not nonl: vmsg("")
 			if not nonl: vmsg("")
-		else: ret = my_send(p,t,delay,s)
+		else:
+			my_send(p,t,delay,s)
 		return ret
 		return ret
 
 
 def get_file_with_ext(ext,mydir,delete=True):
 def get_file_with_ext(ext,mydir,delete=True):
@@ -502,28 +558,32 @@ class MMGenExpect(object):
 			vmsg("EOT")
 			vmsg("EOT")
 		my_expect(self.p,"ENTER to continue: ",'\n')
 		my_expect(self.p,"ENTER to continue: ",'\n')
 
 
-	def passphrase_new(self,what,passphrase):
-		my_expect(self.p,("Enter passphrase for %s: " % what), passphrase+"\n")
+	def passphrase_new(self,desc,passphrase):
+		my_expect(self.p,("Enter passphrase for %s: " % desc), passphrase+"\n")
 		my_expect(self.p,"Repeat passphrase: ", passphrase+"\n")
 		my_expect(self.p,"Repeat passphrase: ", passphrase+"\n")
 
 
-	def passphrase(self,what,passphrase,pwtype=""):
+	def passphrase(self,desc,passphrase,pwtype=""):
 		if pwtype: pwtype += " "
 		if pwtype: pwtype += " "
-		my_expect(self.p,("Enter %spassphrase for %s.*?: " % (pwtype,what)),
+		my_expect(self.p,("Enter %spassphrase for %s.*?: " % (pwtype,desc)),
 				passphrase+"\n",regex=True)
 				passphrase+"\n",regex=True)
 
 
-	def hash_preset(self,what,preset=''):
-		my_expect(self.p,("Enter hash preset for %s," % what))
+	def hash_preset(self,desc,preset=''):
+		my_expect(self.p,("Enter hash preset for %s," % desc))
 		my_expect(self.p,("or hit ENTER .*?:"), str(preset)+"\n",regex=True)
 		my_expect(self.p,("or hit ENTER .*?:"), str(preset)+"\n",regex=True)
 
 
-	def written_to_file(self,what,overwrite_unlikely=False,query="Overwrite?  "):
-		s1 = "%s written to file " % what
+	def written_to_file(self,desc,overwrite_unlikely=False,query="Overwrite?  ",oo=False):
+		s1 = "%s written to file " % desc
 		s2 = query + "Type uppercase 'YES' to confirm: "
 		s2 = query + "Type uppercase 'YES' to confirm: "
 		ret = my_expect(self.p,s1 if overwrite_unlikely else [s1,s2])
 		ret = my_expect(self.p,s1 if overwrite_unlikely else [s1,s2])
 		if ret == 1:
 		if ret == 1:
 			my_send(self.p,"YES\n")
 			my_send(self.p,"YES\n")
-			ret = my_expect(self.p,s1)
+			if oo:
+				outfile = self.expect_getend("Overwriting file '").rstrip("'")
+				return outfile
+			else:
+				ret = my_expect(self.p,s1)
 		outfile = self.p.readline().strip().strip("'")
 		outfile = self.p.readline().strip().strip("'")
-		vmsg("%s file: %s" % (what,cyan(outfile.replace("'",""))))
+		vmsg("%s file: %s" % (desc,cyan(outfile.replace("'",""))))
 		return outfile
 		return outfile
 
 
 	def no_overwrite(self):
 	def no_overwrite(self):
@@ -679,15 +739,15 @@ def check_needs_rerun(ts,cmd,build=False,root=True,force_delete=False,dpy=False)
 
 
 	return rerun
 	return rerun
 
 
-def refcheck(what,chk,refchk):
-	vmsg("Comparing %s '%s' to stored reference" % (what,chk))
+def refcheck(desc,chk,refchk):
+	vmsg("Comparing %s '%s' to stored reference" % (desc,chk))
 	if chk == refchk:
 	if chk == refchk:
 		ok()
 		ok()
 	else:
 	else:
 		if not opt.verbose: errmsg("")
 		if not opt.verbose: errmsg("")
 		errmsg(red("""
 		errmsg(red("""
 Fatal error - %s '%s' does not match reference value '%s'.  Aborting test
 Fatal error - %s '%s' does not match reference value '%s'.  Aborting test
-""".strip() % (what,chk,refchk)))
+""".strip() % (desc,chk,refchk)))
 		sys.exit(3)
 		sys.exit(3)
 
 
 def check_deps(cmds):
 def check_deps(cmds):
@@ -711,7 +771,7 @@ def check_deps(cmds):
 		c = rebuild_list[cmd]
 		c = rebuild_list[cmd]
 		m = "Rebuild" if (c[0] and c[1]) else "Build" if c[0] else "OK"
 		m = "Rebuild" if (c[0] and c[1]) else "Build" if c[0] else "OK"
 		msg("cmd {:<{w}} {}".format(cmd+":", m, w=w))
 		msg("cmd {:<{w}} {}".format(cmd+":", m, w=w))
-#			msgrepr(cmd,c)
+#			mmsg(cmd,c)
 
 
 
 
 def clean(dirs=[]):
 def clean(dirs=[]):
@@ -738,7 +798,7 @@ class MMGenTestSuite(object):
 	def get_num_exts_for_cmd(self,cmd,dpy=False): # dpy ignored here
 	def get_num_exts_for_cmd(self,cmd,dpy=False): # dpy ignored here
 		num = str(cmd_data[cmd][0])
 		num = str(cmd_data[cmd][0])
 		dgl = cfgs[num]['dep_generators']
 		dgl = cfgs[num]['dep_generators']
-#	msgrepr(num,cmd,dgl)
+#	mmsg(num,cmd,dgl)
 		if cmd in dgl.values():
 		if cmd in dgl.values():
 			exts = [k for k in dgl if dgl[k] == cmd]
 			exts = [k for k in dgl if dgl[k] == cmd]
 			return (num,exts)
 			return (num,exts)
@@ -801,9 +861,9 @@ class MMGenTestSuite(object):
 		t.usr_rand(10)
 		t.usr_rand(10)
 		t.passphrase_new("new MMGen wallet",cfg['wpasswd'])
 		t.passphrase_new("new MMGen wallet",cfg['wpasswd'])
 		seed_id = t.written_to_file("Wallet").split("-")[0].split("/")[-1]
 		seed_id = t.written_to_file("Wallet").split("-")[0].split("/")[-1]
-		refcheck("seed id",seed_id,cfg['seed_id'])
+		refcheck("seed ID",seed_id,cfg['seed_id'])
 
 
- 	refwalletgen1 = refwalletgen2 = refwalletgen3 = refwalletgen
+	refwalletgen1 = refwalletgen2 = refwalletgen3 = refwalletgen
 
 
 	def passchg(self,name,walletfile):
 	def passchg(self,name,walletfile):
 
 
@@ -850,7 +910,7 @@ class MMGenTestSuite(object):
 		d = " (%s-bit seed)" % cfg['seed_len']
 		d = " (%s-bit seed)" % cfg['seed_len']
 		self.addrgen(name,walletfile,check_ref=True)
 		self.addrgen(name,walletfile,check_ref=True)
 
 
- 	refaddrgen1 = refaddrgen2 = refaddrgen3 = refaddrgen
+	refaddrgen1 = refaddrgen2 = refaddrgen3 = refaddrgen
 
 
 	def addrimport(self,name,addrfile):
 	def addrimport(self,name,addrfile):
 		outfile = os.path.join(cfg['tmpdir'],"addrfile_w_comments")
 		outfile = os.path.join(cfg['tmpdir'],"addrfile_w_comments")
@@ -1014,11 +1074,11 @@ class MMGenTestSuite(object):
 		t.written_to_file("Data",query="")
 		t.written_to_file("Data",query="")
 		ok()
 		ok()
 
 
-	def addrgen_seed(self,name,walletfile,foo,what="seed data",arg="-s"):
+	def addrgen_seed(self,name,walletfile,foo,desc="seed data",arg="-s"):
 		t = MMGenExpect(name,"mmgen-addrgen",
 		t = MMGenExpect(name,"mmgen-addrgen",
 				[arg,"-d",cfg['tmpdir'],walletfile,cfg['addr_idx_list']])
 				[arg,"-d",cfg['tmpdir'],walletfile,cfg['addr_idx_list']])
 		t.license()
 		t.license()
-		t.expect_getend("Valid %s for seed ID " % what)
+		t.expect_getend("Valid %s for seed ID " % desc)
 		vmsg("Comparing generated checksum with checksum from previous address file")
 		vmsg("Comparing generated checksum with checksum from previous address file")
 		chk = t.expect_getend(r"Checksum for address data .*?: ",regex=True)
 		chk = t.expect_getend(r"Checksum for address data .*?: ",regex=True)
 		verify_checksum_or_exit(get_addrfile_checksum(),chk)
 		verify_checksum_or_exit(get_addrfile_checksum(),chk)
@@ -1026,14 +1086,14 @@ class MMGenTestSuite(object):
 		ok()
 		ok()
 
 
 	def addrgen_mnemonic(self,name,walletfile,foo):
 	def addrgen_mnemonic(self,name,walletfile,foo):
-		self.addrgen_seed(name,walletfile,foo,what="mnemonic",arg="-m")
+		self.addrgen_seed(name,walletfile,foo,desc="mnemonic",arg="-m")
 
 
 	def addrgen_incog(self,name,walletfile,foo,args=["-g"]):
 	def addrgen_incog(self,name,walletfile,foo,args=["-g"]):
 		t = MMGenExpect(name,"mmgen-addrgen",args+["-d",
 		t = MMGenExpect(name,"mmgen-addrgen",args+["-d",
 				cfg['tmpdir'],walletfile,cfg['addr_idx_list']])
 				cfg['tmpdir'],walletfile,cfg['addr_idx_list']])
 		t.license()
 		t.license()
 		t.expect_getend("Incog ID: ")
 		t.expect_getend("Incog ID: ")
-		t.passphrase("MMGen incognito wallet \w{8}", cfg['wpasswd'])
+		t.passphrase("incognito wallet data \w{8}", cfg['wpasswd'])
 		t.hash_preset("incog wallet",'1')
 		t.hash_preset("incog wallet",'1')
 		vmsg("Comparing generated checksum with checksum from address file")
 		vmsg("Comparing generated checksum with checksum from address file")
 		chk = t.expect_getend(r"Checksum for address data .*?: ",regex=True)
 		chk = t.expect_getend(r"Checksum for address data .*?: ",regex=True)
@@ -1068,7 +1128,7 @@ class MMGenTestSuite(object):
 	def refkeyaddrgen(self,name,walletfile):
 	def refkeyaddrgen(self,name,walletfile):
 		self.keyaddrgen(name,walletfile,check_ref=True)
 		self.keyaddrgen(name,walletfile,check_ref=True)
 
 
- 	refkeyaddrgen1 = refkeyaddrgen2 = refkeyaddrgen3 = refkeyaddrgen
+	refkeyaddrgen1 = refkeyaddrgen2 = refkeyaddrgen3 = refkeyaddrgen
 
 
 	def txsign_keyaddr(self,name,keyaddr_file,txfile):
 	def txsign_keyaddr(self,name,keyaddr_file,txfile):
 		t = MMGenExpect(name,"mmgen-txsign", ["-d",cfg['tmpdir'],"-M",keyaddr_file,txfile])
 		t = MMGenExpect(name,"mmgen-txsign", ["-d",cfg['tmpdir'],"-M",keyaddr_file,txfile])
@@ -1136,9 +1196,9 @@ class MMGenTestSuite(object):
 		t.license()
 		t.license()
 		t.tx_view()
 		t.tx_view()
 
 
-		for cnum,what,app in ('1',"incognito"," incognito"),('3',"MMGen",""):
-			t.expect_getend("Getting %s wallet data from file " % what)
-			t.passphrase("MMGen%s wallet"%app,cfgs[cnum]['wpasswd'])
+		for cnum,desc,app in ('1',"incognito","incognito"),('3',"MMGen","MMGen"):
+			t.expect_getend("Getting %s wallet data from file " % desc)
+			t.passphrase("%s wallet"%app,cfgs[cnum]['wpasswd'])
 			if cnum == '1':
 			if cnum == '1':
 				t.hash_preset("incog wallet",'1')
 				t.hash_preset("incog wallet",'1')
 
 
@@ -1155,7 +1215,7 @@ class MMGenTestSuite(object):
 			infn = get_tmpfile_fn(cfg,tmp_fn)
 			infn = get_tmpfile_fn(cfg,tmp_fn)
 		t = MMGenExpect(name,"mmgen-tool",["-d",cfg['tmpdir'],"encrypt",infn])
 		t = MMGenExpect(name,"mmgen-tool",["-d",cfg['tmpdir'],"encrypt",infn])
 		t.hash_preset("user data",'1')
 		t.hash_preset("user data",'1')
-		t.passphrase_new("user data",cfg['tool_enc_passwd'])
+		t.passphrase_new("user data",tool_enc_passwd)
 		t.written_to_file("Encrypted data")
 		t.written_to_file("Encrypted data")
 		ok()
 		ok()
 # Generate the reference mmenc file
 # Generate the reference mmenc file
@@ -1168,7 +1228,7 @@ class MMGenTestSuite(object):
 		of = name + ".out"
 		of = name + ".out"
 		t = MMGenExpect(name,"mmgen-tool",
 		t = MMGenExpect(name,"mmgen-tool",
 			["-d",cfg['tmpdir'],"decrypt",f2,"outfile="+of,"hash_preset=1"])
 			["-d",cfg['tmpdir'],"decrypt",f2,"outfile="+of,"hash_preset=1"])
-		t.passphrase("user data",cfg['tool_enc_passwd'])
+		t.passphrase("user data",tool_enc_passwd)
 		t.written_to_file("Decrypted data")
 		t.written_to_file("Decrypted data")
 		d1 = read_from_file(f1)
 		d1 = read_from_file(f1)
 		d2 = read_from_file(get_tmpfile_fn(cfg,of))
 		d2 = read_from_file(get_tmpfile_fn(cfg,of))
@@ -1182,7 +1242,127 @@ class MMGenTestSuite(object):
 		o = t.expect_getend("Incog data for ID \w{8} found at offset ",regex=True)
 		o = t.expect_getend("Incog data for ID \w{8} found at offset ",regex=True)
 		cmp_or_die(hincog_offset,int(o))
 		cmp_or_die(hincog_offset,int(o))
 
 
+	def walletconv_out(self,name,desc,out_fmt="w",uopts=[],uopts_chk=[],pw=False):
+		opts = ["-d",cfg['tmpdir'],"-r10","-p1","-o",out_fmt] + uopts
+		infile = os.path.join(ref_dir,cfg['seed_id']+".mmwords")
+		d = "(convert)"
+		t = MMGenExpect(name,"mmgen-walletconv",opts+[infile],extra_desc=d)
+		t.license()
+		if pw:
+			t.passphrase_new("new "+desc,cfg['wpasswd'])
+			t.usr_rand(10)
+		if desc == "hidden incognito data":
+			ret = t.expect(["Create? (Y/n): ","'YES' to confirm: "],"YES\n")
+			if ret == 0:
+				t.expect("Enter file size: ","1234\n")
+		wf = t.written_to_file(desc[0].upper()+desc[1:],oo=True)
+		ok()
+
+		d = "(check)"
+		if desc == "hidden incognito data":
+			self.keygen_chksum_chk_hincog(name,cfg['seed_id'],uopts_chk)
+# 		elif pw:
+# 			self.walletchk_chksum_chk(name,wf,cfg['seed_id'],uopts=uopts_chk)
+		else:
+			self.keygen_chksum_chk(name,wf,cfg['seed_id'],pw=pw)
+
+	def walletconv_in(self,name,infile,desc,uopts=[],pw=False,oo=False):
+		opts = ["-d",cfg['tmpdir'],"-o","words","-r10"]
+		if_arg = [infile] if infile else []
+		d = "(convert)"
+		t = MMGenExpect(name,"mmgen-walletconv",opts+uopts+if_arg,extra_desc=d)
+		t.license()
+		if desc == "brainwallet":
+			t.expect("Enter brainwallet: ",ref_wallet_brainpass+"\n")
+		if pw:
+			t.passphrase(desc,cfg['wpasswd'])
+			if name[:19] == "ref_hincog_conv_old":
+				t.expect("Is the seed ID correct? (Y/n): ","\n")
+			else:
+				t.expect(["Passphrase is OK"," are correct"])
+		# Output
+		wf = t.written_to_file("Mnemonic data",oo=oo)
+		t.close()
+		ok()
+		# back check of result
+		d = "(check)"
+		self.keygen_chksum_chk(name,wf,cfg['seed_id'])
+
 	# Saved reference file tests
 	# Saved reference file tests
+	def ref_wallet_conv(self,name):
+		wf = os.path.join(ref_dir,cfg['ref_wallet'])
+		self.walletconv_in(name,wf,"MMGen wallet",pw=True,oo=True)
+
+	def ref_mn_conv(self,name,ext="mmwords",desc="Mnemonic data"):
+		wf = os.path.join(ref_dir,cfg['seed_id']+"."+ext)
+		self.walletconv_in(name,wf,desc,oo=True)
+
+	def ref_seed_conv(self,name):
+		self.ref_mn_conv(name,ext="mmseed",desc="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)
+
+	def ref_incog_conv(self,name,wfk="ic_wallet",in_fmt="i",desc="incognito data"):
+		uopts = ["-i",in_fmt,"-p","1","-l",str(cfg['seed_len'])]
+		wf = os.path.join(ref_dir,cfg[wfk])
+		self.walletconv_in(name,wf,desc,uopts,oo=True,pw=True)
+
+	def ref_incox_conv(self,name):
+		self.ref_incog_conv(name,in_fmt="xi",wfk="ic_wallet_hex",desc="hex incognito data")
+
+	def ref_hincog_conv(self,name,wfk='hic_wallet',add_uopts=[]):
+		ic_f = os.path.join(ref_dir,cfg[wfk])
+		uopts = ["-i","hi","-p","1","-l",str(cfg['seed_len'])] + add_uopts
+		hi_opt = ["-H","%s,%s" % (ic_f,ref_wallet_incog_offset)]
+		self.walletconv_in(name,None,"hidden incognito data",uopts+hi_opt,oo=True,pw=True)
+
+	def ref_hincog_conv_old(self,name):
+		self.ref_hincog_conv(name,wfk='hic_wallet_old',add_uopts=["-O"])
+
+	def ref_wallet_conv_out(self,name):
+		self.walletconv_out(name,"MMGen wallet","w",pw=True)
+
+	def ref_mn_conv_out(self,name):
+		self.walletconv_out(name,"mnemonic data","mn")
+
+	def ref_seed_conv_out(self,name):
+		self.walletconv_out(name,"seed data","seed")
+
+	def ref_incog_conv_out(self,name):
+		self.walletconv_out(name,"incognito data",out_fmt="i",pw=True)
+
+	def ref_incox_conv_out(self,name):
+		self.walletconv_out(name,"hex incognito data",out_fmt="xi",pw=True)
+
+	def ref_hincog_conv_out(self,name,extra_uopts=[]):
+		ic_f = os.path.join(cfg['tmpdir'],"rand.data")
+		hi_parms = "%s,%s" % (ic_f,ref_wallet_incog_offset)
+		hi_parms_legacy = "%s,%s,%s"%(ic_f,ref_wallet_incog_offset,cfg['seed_len'])
+		self.walletconv_out(name,
+			"hidden incognito data", "hi",
+			uopts=["-J",hi_parms] + extra_uopts,
+			uopts_chk=["-G",hi_parms_legacy],
+			pw=True
+		)
+
+	ref_wallet_conv1 = ref_wallet_conv2 = ref_wallet_conv3 = ref_wallet_conv
+	ref_mn_conv1 = ref_mn_conv2 = ref_mn_conv3 = ref_mn_conv
+	ref_seed_conv1 = ref_seed_conv2 = ref_seed_conv3 = ref_seed_conv
+	ref_brain_conv1 = ref_brain_conv2 = ref_brain_conv3 = ref_brain_conv
+	ref_incog_conv1 = ref_incog_conv2 = ref_incog_conv3 = ref_incog_conv
+	ref_incox_conv1 = ref_incox_conv2 = ref_incox_conv3 = ref_incox_conv
+	ref_hincog_conv1 = ref_hincog_conv2 = ref_hincog_conv3 = ref_hincog_conv
+	ref_hincog_conv_old1 = ref_hincog_conv_old2 = ref_hincog_conv_old3 = ref_hincog_conv_old
+
+	ref_wallet_conv_out1 = ref_wallet_conv_out2 = ref_wallet_conv_out3 = ref_wallet_conv_out
+	ref_mn_conv_out1 = ref_mn_conv_out2 = ref_mn_conv_out3 = ref_mn_conv_out
+	ref_seed_conv_out1 = ref_seed_conv_out2 = ref_seed_conv_out3 = ref_seed_conv_out
+	ref_incog_conv_out1 = ref_incog_conv_out2 = ref_incog_conv_out3 = ref_incog_conv_out
+	ref_incox_conv_out1 = ref_incox_conv_out2 = ref_incox_conv_out3 = ref_incox_conv_out
+	ref_hincog_conv_out1 = ref_hincog_conv_out2 = ref_hincog_conv_out3 = ref_hincog_conv_out
+
 	def ref_wallet_chk(self,name):
 	def ref_wallet_chk(self,name):
 		wf = os.path.join(ref_dir,cfg['ref_wallet'])
 		wf = os.path.join(ref_dir,cfg['ref_wallet'])
 		self.walletchk(name,wf)
 		self.walletchk(name,wf)
@@ -1191,8 +1371,8 @@ class MMGenTestSuite(object):
 
 
 	def ref_seed_chk(self,name,ext=g.seed_ext):
 	def ref_seed_chk(self,name,ext=g.seed_ext):
 		wf = os.path.join(ref_dir,"%s.%s" % (cfg['seed_id'],ext))
 		wf = os.path.join(ref_dir,"%s.%s" % (cfg['seed_id'],ext))
-		what = "seed data" if ext == g.seed_ext else "mnemonic"
-		self.keygen_chksum_chk(name,wf,cfg['seed_id'],what)
+		desc = "seed data" if ext == g.seed_ext else "mnemonic"
+		self.keygen_chksum_chk(name,wf,cfg['seed_id'])
 
 
 	ref_seed_chk1 = ref_seed_chk2 = ref_seed_chk3 = ref_seed_chk
 	ref_seed_chk1 = ref_seed_chk2 = ref_seed_chk3 = ref_seed_chk
 
 
@@ -1203,37 +1383,60 @@ class MMGenTestSuite(object):
 	def ref_brain_chk(self,name,bw_file=ref_bw_file):
 	def ref_brain_chk(self,name,bw_file=ref_bw_file):
 		wf = os.path.join(ref_dir,bw_file)
 		wf = os.path.join(ref_dir,bw_file)
 		arg = "-b%s,%s" % (cfg['seed_len'],ref_bw_hash_preset)
 		arg = "-b%s,%s" % (cfg['seed_len'],ref_bw_hash_preset)
-		self.keygen_chksum_chk(name,wf,cfg['ref_bw_seed_id'],"brainwallet",[arg])
+		self.keygen_chksum_chk(name,wf,cfg['ref_bw_seed_id'],[arg])
+
+	def keygen_chksum_chk_hincog(self,name,seed_id,hincog_parm):
+		t = MMGenExpect(name,"mmgen-keygen", ["-p1","-q","-S","-A"]+hincog_parm+["1"],extra_desc="(check)")
+		t.passphrase("",cfg['wpasswd'])
+		t.expect("Encrypt key list? (y/N): ","\n")
+		t.expect("any printable ASCII symbol.\r\n")
+		chk = t.readline()[:8]
+		vmsg("Seed ID: %s" % cyan(chk))
+		cmp_or_die(seed_id,chk)
+
+	def keygen_chksum_chk(self,name,wf,seed_id,args=[],pw=False):
+		hp_arg = ["-p1"] if pw else []
+		t = MMGenExpect(name,"mmgen-keygen", ["-q","-S","-A"]+args+hp_arg+[wf,"1"],extra_desc="(check)")
+		if pw:
+			t.passphrase("",cfg['wpasswd'])
+		t.expect("Encrypt key list? (y/N): ","\n")
+		t.expect("any printable ASCII symbol.\r\n")
+		chk = t.readline()[:8]
+		vmsg("Seed ID: %s" % cyan(chk))
+		cmp_or_die(seed_id,chk)
 
 
-	def keygen_chksum_chk(self,name,wf,seed_id,what,args=[]):
-		t = MMGenExpect(name,"mmgen-keygen", ["-q","-A"]+args+[wf,"1"])
-		chk = t.expect_getend("Valid %s for seed ID " % what)
+	# Use this for encrypted wallets instead of keygen_chksum_chk()
+	def walletchk_chksum_chk(self,name,wf,seed_id,uopts=[]):
+		t = MMGenExpect(name,"mmgen-walletchk",["-v", wf]+uopts,
+							extra_desc="(check)")
+		t.passphrase("",cfg['wpasswd'])
+		chk = t.expect_getend("Seed ID checksum OK (")[:8]
 		t.close()
 		t.close()
 		cmp_or_die(seed_id,chk)
 		cmp_or_die(seed_id,chk)
 
 
 	ref_brain_chk1 = ref_brain_chk2 = ref_brain_chk3 = ref_brain_chk
 	ref_brain_chk1 = ref_brain_chk2 = ref_brain_chk3 = ref_brain_chk
 
 
-	def ref_brain_chk3_spc(self,name):
+	def ref_brain_chk_spc3(self,name):
 		self.ref_brain_chk(name,bw_file=ref_bw_file_spc)
 		self.ref_brain_chk(name,bw_file=ref_bw_file_spc)
 
 
-	def ref_incog_chk(self,name):
-		for wtype,desc,earg in ('ic_wallet','',[]), \
-							   ('ic_wallet_old','(old format)',["-o"]):
+	def ref_hincog_chk(self,name):
+		for wtype,desc,earg in ('hic_wallet','',[]), \
+								('hic_wallet_old','(old format)',["-o"]):
 			ic_arg = "%s,%s,%s" % (
 			ic_arg = "%s,%s,%s" % (
 						os.path.join(ref_dir,cfg[wtype]),
 						os.path.join(ref_dir,cfg[wtype]),
 						ref_wallet_incog_offset,cfg['seed_len']
 						ref_wallet_incog_offset,cfg['seed_len']
 					)
 					)
 			t = MMGenExpect(name,"mmgen-keygen",
 			t = MMGenExpect(name,"mmgen-keygen",
 					["-q","-A"]+earg+["-G"]+[ic_arg]+['1'],extra_desc=desc)
 					["-q","-A"]+earg+["-G"]+[ic_arg]+['1'],extra_desc=desc)
-			t.passphrase("MMGen incognito wallet",cfg['wpasswd'])
+			t.passphrase("incognito wallet",cfg['wpasswd'])
 			t.hash_preset("incog wallet","1")
 			t.hash_preset("incog wallet","1")
-			if wtype == 'ic_wallet_old':
+			if wtype == 'hic_wallet_old':
 				t.expect("Is the seed ID correct? (Y/n): ","\n")
 				t.expect("Is the seed ID correct? (Y/n): ","\n")
 			chk = t.expect_getend("Valid incog data for seed ID ")
 			chk = t.expect_getend("Valid incog data for seed ID ")
 			t.close()
 			t.close()
 			cmp_or_die(cfg['seed_id'],chk)
 			cmp_or_die(cfg['seed_id'],chk)
 
 
- 	ref_incog_chk1 = ref_incog_chk2 = ref_incog_chk3 = ref_incog_chk
+	ref_hincog_chk1 = ref_hincog_chk2 = ref_hincog_chk3 = ref_hincog_chk
 
 
 	def ref_addrfile_chk(self,name,ftype="addr"):
 	def ref_addrfile_chk(self,name,ftype="addr"):
 		wf = os.path.join(ref_dir,cfg['ref_'+ftype+'file'])
 		wf = os.path.join(ref_dir,cfg['ref_'+ftype+'file'])
@@ -1261,11 +1464,11 @@ class MMGenTestSuite(object):
 		f = os.path.join(ref_dir,ref_enc_fn)
 		f = os.path.join(ref_dir,ref_enc_fn)
 		t = MMGenExpect(name,"mmgen-tool",
 		t = MMGenExpect(name,"mmgen-tool",
 				["-q","decrypt",f,"outfile=-","hash_preset=1"])
 				["-q","decrypt",f,"outfile=-","hash_preset=1"])
-		t.passphrase("user data",cfg['tool_enc_passwd'])
+		t.passphrase("user data",tool_enc_passwd)
 		t.readline()
 		t.readline()
 		import re
 		import re
 		o = re.sub('\r\n','\n',t.read())
 		o = re.sub('\r\n','\n',t.read())
-		cmp_or_die(cfg['sample_text'],o)
+		cmp_or_die(sample_text,o)
 
 
 # main()
 # main()
 if opt.pause:
 if opt.pause:
@@ -1288,8 +1491,11 @@ try:
 				globals()[arg](cmd_args[cmd_args.index(arg)+1:])
 				globals()[arg](cmd_args[cmd_args.index(arg)+1:])
 				sys.exit()
 				sys.exit()
 			elif arg in meta_cmds:
 			elif arg in meta_cmds:
-				for cmd in meta_cmds[arg][1]:
-					check_needs_rerun(ts,cmd,build=True,force_delete=True)
+				for cmd in meta_cmds[arg]:
+					check_needs_rerun(ts,cmd,build=True)
+			elif arg in cmd_groups.keys():
+				for cmd in cmd_groups[arg]:
+					check_needs_rerun(ts,cmd,build=True)
 			elif arg in cmd_data:
 			elif arg in cmd_data:
 				check_needs_rerun(ts,arg,build=True)
 				check_needs_rerun(ts,arg,build=True)
 			else:
 			else:

+ 1 - 3
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,msgrepr, msgrepr_exit
+from mmgen.util import msg,msg_r,vmsg,vmsg_r,Msg,mmsg,mdie
 from collections import OrderedDict
 from collections import OrderedDict
 
 
 cmd_data = OrderedDict([
 cmd_data = OrderedDict([
@@ -94,8 +94,6 @@ cmd_args = opt.opts.init(opts_data,add_opts=["exact_output"])
 
 
 if opt.system: sys.path.pop(0)
 if opt.system: sys.path.pop(0)
 
 
-if opt.debug: opt.verbose = True
-
 if opt.list_cmds:
 if opt.list_cmds:
 	fs = "  {:<{w}} - {}"
 	fs = "  {:<{w}} - {}"
 	Msg("Available commands:")
 	Msg("Available commands:")

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