Browse Source

Add g.proto.witness_ver_num, minor fixes and changes

MMGen 7 years ago
parent
commit
e9385725cf
12 changed files with 121 additions and 87 deletions
  1. 12 14
      mmgen/addr.py
  2. 2 2
      mmgen/crypto.py
  3. 6 0
      mmgen/globalvars.py
  4. 3 3
      mmgen/main_tool.py
  5. 4 2
      mmgen/obj.py
  6. 3 2
      mmgen/opts.py
  7. 10 6
      mmgen/protocol.py
  8. 10 7
      mmgen/rpc.py
  9. 8 10
      mmgen/tx.py
  10. 2 2
      mmgen/util.py
  11. 5 2
      test/mmgen_pexpect.py
  12. 56 37
      test/test.py

+ 12 - 14
mmgen/addr.py

@@ -27,9 +27,8 @@ from mmgen.obj import *
 
 pnm = g.proj_name
 
-def sc_dmsg(desc,data):
-	if os.getenv('MMGEN_DEBUG_ADDRLIST'):
-		Msg('sc_debug_{}: {}'.format(desc,data))
+def dmsg_sc(desc,data):
+	if g.debug_addrlist: Msg('sc_debug_{}: {}'.format(desc,data))
 
 class AddrGenerator(MMGenObject):
 	def __new__(cls,addr_type):
@@ -45,8 +44,7 @@ class AddrGenerator(MMGenObject):
 			'segwit':   AddrGeneratorSegwit,
 			'ethereum': AddrGeneratorEthereum,
 			'zcash_z':  AddrGeneratorZcashZ,
-			'monero':   AddrGeneratorMonero
-		}
+			'monero':   AddrGeneratorMonero}
 		assert gen_method in gen_methods
 		me = super(cls,cls).__new__(gen_methods[gen_method])
 		me.desc = gen_methods
@@ -59,7 +57,7 @@ class AddrGeneratorP2PKH(AddrGenerator):
 		return CoinAddr(g.proto.pubhash2addr(hash160(pubhex),p2sh=False))
 
 	def to_segwit_redeem_script(self,pubhex):
-		raise NotImplementedError,'Coin/type pair incompatible with Segwit'
+		raise NotImplementedError,'Segwit redeem script not supported by this address type'
 
 class AddrGeneratorSegwit(AddrGenerator):
 	def to_addr(self,pubhex):
@@ -77,7 +75,7 @@ class AddrGeneratorEthereum(AddrGenerator):
 		return CoinAddr(sha3.keccak_256(pubhex[2:].decode('hex')).digest()[12:].encode('hex'))
 
 	def to_segwit_redeem_script(self,pubhex):
-		raise NotImplementedError,'Coin/type pair incompatible with Segwit'
+		raise NotImplementedError,'Segwit redeem script not supported by this address type'
 
 # github.com/FiloSottile/zcash-mini/zcash/address.go
 class AddrGeneratorZcashZ(AddrGenerator):
@@ -197,7 +195,7 @@ class KeyGenerator(MMGenObject):
 	def test_for_secp256k1(self,silent=False):
 		try:
 			from mmgen.secp256k1 import priv2pub
-			assert priv2pub(os.urandom(32),1)
+			assert priv2pub(('deadbeef'*8).decode('hex'),1)
 			return True
 		except:
 			return False
@@ -303,7 +301,7 @@ class AddrListIDStr(unicode,Hilite):
 			bc = (g.proto.base_coin,g.coin)[g.proto.base_coin=='ETH']
 			mt = addrlist.al_id.mmtype
 			ret = '{}{}{}[{}]'.format(addrlist.al_id.sid,('-'+bc,'')[bc=='BTC'],('-'+mt,'')[mt in ('L','E')],s)
-			sc_dmsg('id_str',ret[8:].split('[')[0])
+			dmsg_sc('id_str',ret[8:].split('[')[0])
 
 		return unicode.__new__(cls,ret)
 
@@ -397,7 +395,7 @@ Removed %s duplicate WIF key%s from keylist (also in {pnm} key-address file
 
 		seed = seed.get_data()
 		seed = self.scramble_seed(seed)
-		sc_dmsg('seed',seed[:8].encode('hex'))
+		dmsg_sc('seed',seed[:8].encode('hex'))
 
 		compressed = self.al_id.mmtype.compressed
 		pubkey_type = self.al_id.mmtype.pubkey_type
@@ -441,7 +439,7 @@ Removed %s duplicate WIF key%s from keylist (also in {pnm} key-address file
 				dmsg('Key {:>03}: {}'.format(pos,e.passwd))
 
 			out.append(e)
-			if g.debug: Msg('generate():\n', e.pformat())
+			if g.debug: Msg('generate():\n{}'.format(e.pformat()))
 
 		qmsg('\r%s: %s %s%s generated%s' % (
 				self.al_id.hl(),t_addrs,self.gen_desc,suf(t_addrs,self.gen_desc_pl),' '*15))
@@ -452,13 +450,13 @@ Removed %s duplicate WIF key%s from keylist (also in {pnm} key-address file
 	def scramble_seed(self,seed):
 		is_btcfork = g.proto.base_coin == 'BTC'
 		if is_btcfork and self.al_id.mmtype == 'L':
-			sc_dmsg('str','(none)')
+			dmsg_sc('str','(none)')
 			return seed
 		if g.proto.base_coin == 'ETH':
 			scramble_key = g.coin.lower()
 		else:
 			scramble_key = (g.coin.lower()+':','')[is_btcfork] + self.al_id.mmtype.name
-		sc_dmsg('str',scramble_key)
+		dmsg_sc('str',scramble_key)
 		from mmgen.crypto import scramble_seed
 		return scramble_seed(seed,scramble_key,self.scramble_hash_rounds)
 
@@ -559,7 +557,7 @@ Removed %s duplicate WIF key%s from keylist (also in {pnm} key-address file
 			lbl_p2 = ':'.join(l_coin+l_type)
 			lbl = self.al_id.sid + ('',' ')[bool(lbl_p2)] + lbl_p2
 
-		sc_dmsg('lbl',lbl[9:])
+		dmsg_sc('lbl',lbl[9:])
 		out.append(u'{} {{'.format(lbl))
 
 		fs = '  {:<%s}  {:<34}{}' % len(str(self.data[-1].idx))

+ 2 - 2
mmgen/crypto.py

@@ -45,8 +45,8 @@ def sha256_rounds(s,n):
 def scramble_seed(seed,scramble_key,hash_rounds):
 	import hmac
 	scr_seed = hmac.new(seed,scramble_key,sha256).digest()
-	fs = 'Seed:  {}\nScramble key: {}\nScrambled seed: {}\nScrambled seed len: {}'
-	dmsg(fs.format(hexlify(seed),scramble_key,hexlify(scr_seed),len(scr_seed)))
+	fs = 'Seed:  {}\nScramble key: {}\nScrambled seed: {}'
+	dmsg(fs.format(hexlify(seed),scramble_key,hexlify(scr_seed)))
 	return sha256_rounds(scr_seed,hash_rounds)
 
 def encrypt_seed(seed,key):

+ 6 - 0
mmgen/globalvars.py

@@ -66,6 +66,9 @@ class g(object):
 
 	coin                 = 'BTC'
 	debug                = False
+	debug_opts           = False
+	debug_rpc            = False
+	debug_addrlist       = False
 	quiet                = False
 	no_license           = False
 	hold_protect         = True
@@ -136,6 +139,9 @@ class g(object):
 	env_opts = (
 		'MMGEN_BOGUS_WALLET_DATA',
 		'MMGEN_DEBUG',
+		'MMGEN_DEBUG_OPTS',
+		'MMGEN_DEBUG_RPC',
+		'MMGEN_DEBUG_ADDRLIST',
 		'MMGEN_QUIET',
 		'MMGEN_DISABLE_COLOR',
 		'MMGEN_FORCE_256_COLOR',

+ 3 - 3
mmgen/main_tool.py

@@ -36,9 +36,9 @@ Cryptocoin address/key operations (compressed public keys supported):
   privhex2addr   - generate coin address from private key in hex format
   privhex2pubhex - generate a hex public key from a hex private key
   pubhex2addr    - convert a hex pubkey to an address
-  pubhex2redeem_script - convert a hex pubkey to a witness redeem script
-  wif2redeem_script - convert a WIF private key to a witness redeem script
-  wif2segwit_pair - generate both a Segwit redeem script and address from WIF
+  pubhex2redeem_script - convert a hex pubkey to a Segwit P2SH-P2WPKH redeem script
+  wif2redeem_script - convert a WIF private key to a Segwit P2SH-P2WPKH redeem script
+  wif2segwit_pair - generate both a Segwit P2SH-P2WPKH redeem script and address from WIF
   pubkey2addr    - convert coin public key to address
   randpair       - generate a random private key/address pair
   randwif        - generate a random private key in WIF format

+ 4 - 2
mmgen/obj.py

@@ -383,8 +383,6 @@ class CoinAddr(str,Hilite,InitErrors,MMGenObject):
 		return Hilite.fmtc(s,**kwargs)
 
 	def is_for_chain(self,chain):
-		from mmgen.globalvars import g
-		vn = g.proto.get_protocol_by_chain(chain).addr_ver_num
 
 		def pfx_ok(pfx):
 			if type(pfx) == tuple:
@@ -392,6 +390,10 @@ class CoinAddr(str,Hilite,InitErrors,MMGenObject):
 			elif self[:len(pfx)] == pfx: return True
 			return False
 
+		from mmgen.globalvars import g
+		proto = g.proto.get_protocol_by_chain(chain)
+		vn = proto.addr_ver_num
+
 		if self.addr_fmt == 'p2sh' and 'p2sh2' in vn:
 			return pfx_ok(vn['p2sh'][1]) or pfx_ok(vn['p2sh2'][1])
 		else:

+ 3 - 2
mmgen/opts.py

@@ -224,7 +224,7 @@ def init(opts_f,add_opts=[],opt_filter=None):
 	uopts,args,short_opts,long_opts,skipped_opts,do_help = \
 		mmgen.share.Opts.parse_opts(sys.argv,opts_data,opt_filter=opt_filter,defer_help=True)
 
-	if g.debug: opt_preproc_debug(short_opts,long_opts,skipped_opts,uopts,args)
+	if g.debug_opts: opt_preproc_debug(short_opts,long_opts,skipped_opts,uopts,args)
 
 	# Save this for usage()
 	global usage_txt
@@ -292,6 +292,7 @@ def init(opts_f,add_opts=[],opt_filter=None):
 
 	if g.bob or g.alice:
 		g.testnet = True
+		g.regtest = True
 		g.proto = CoinProtocol(g.coin,g.testnet)
 		g.data_dir = os.path.join(g.data_dir_root,'regtest',g.coin.lower(),('alice','bob')[g.bob])
 		check_or_create_dir(g.data_dir)
@@ -309,7 +310,7 @@ def init(opts_f,add_opts=[],opt_filter=None):
 		ymsg("Warning: config file options have changed! See '{}' for details".format(g.cfg_file+'.sample'))
 		my_raw_input('Hit ENTER to continue: ')
 
-	if g.debug: opt_postproc_debug()
+	if g.debug_opts: opt_postproc_debug()
 
 	# We don't need this data anymore
 	del mmgen.share.Opts

+ 10 - 6
mmgen/protocol.py

@@ -83,10 +83,14 @@ class BitcoinProtocol(MMGenObject):
 		(478559,'00000000000000000019f112ec0a9982926f1258cdcc558dd7c3b7e5dc7fa148','bch',False),
 		(None,'','b2x',True)
 	]
-	caps = ('rbf','segwit')
-	mmcaps = ('key','addr','rpc','tx')
-	base_coin = 'BTC'
-	addr_width = 34
+	caps               = ('rbf','segwit')
+	mmcaps             = ('key','addr','rpc','tx')
+	base_coin          = 'BTC'
+	addr_width         = 34
+	# From BIP173: witness version 'n' is stored as 'OP_n'. OP_0 is encoded as 0x00,
+	# but OP_1 through OP_16 are encoded as 0x51 though 0x60 (81 to 96 in decimal).
+	witness_vernum_hex = '00'
+	witness_vernum     = int(witness_vernum_hex,16)
 
 	@staticmethod
 	def get_protocol_by_chain(chain):
@@ -147,7 +151,7 @@ class BitcoinProtocol(MMGenObject):
 			else:
 				if g.debug: Msg('Invalid checksum in address')
 				break
-		if g.debug: Msg("Invalid address '{}'".format(addr))
+
 		return False
 
 	@classmethod
@@ -163,7 +167,7 @@ class BitcoinProtocol(MMGenObject):
 		# https://bitcoincore.org/en/segwit_wallet_dev/
 		# The P2SH redeemScript is always 22 bytes. It starts with a OP_0, followed
 		# by a canonical push of the keyhash (i.e. 0x0014{20-byte keyhash})
-		return '0014' + hash160(pubhex)
+		return cls.witness_vernum_hex + '14' + hash160(pubhex)
 
 	@classmethod
 	def pubhex2segwitaddr(cls,pubhex):

+ 10 - 7
mmgen/rpc.py

@@ -25,14 +25,17 @@ import httplib,base64,json
 from mmgen.common import *
 from decimal import Decimal
 
+def dmsg_rpc(s):
+	if g.debug_rpc: msg(s)
+
 class RPCFailure(Exception): pass
 
 class CoinDaemonRPCConnection(object):
 
 	def __init__(self,host=None,port=None,user=None,passwd=None,auth_cookie=None):
 
-		dmsg('=== CoinDaemonRPCConnection.__init__() debug ===')
-		dmsg('    host [{}] port [{}] user [{}] passwd [{}] auth_cookie [{}]\n'.format(
+		dmsg_rpc('=== CoinDaemonRPCConnection.__init__() debug ===')
+		dmsg_rpc('    host [{}] port [{}] user [{}] passwd [{}] auth_cookie [{}]\n'.format(
 			host,port,user,passwd,auth_cookie))
 
 		import socket
@@ -98,8 +101,8 @@ class CoinDaemonRPCConnection(object):
 			elif cf['on_fail'] == 'die':
 				die(args[1],yellow(s))
 
-		dmsg('=== request() debug ===')
-		dmsg('    RPC POST data ==> %s\n' % p)
+		dmsg_rpc('=== request() debug ===')
+		dmsg_rpc('    RPC POST data ==> %s\n' % p)
 		caller = self
 		class MyJSONEncoder(json.JSONEncoder):
 			def default(self, obj):
@@ -112,7 +115,7 @@ class CoinDaemonRPCConnection(object):
 		# 	dump = json.dumps(p,cls=MyJSONEncoder,ensure_ascii=False)
 		# 	print(dump)
 
-		dmsg('    RPC AUTHORIZATION data ==> raw: [{}]\n{}enc: [Basic {}]\n'.format(
+		dmsg_rpc('    RPC AUTHORIZATION data ==> raw: [{}]\n{}enc: [Basic {}]\n'.format(
 			self.auth_str,' '*31,base64.b64encode(self.auth_str)))
 		try:
 			hc.request('POST', '/', json.dumps(p,cls=MyJSONEncoder), {
@@ -129,7 +132,7 @@ class CoinDaemonRPCConnection(object):
 			m = 'Unable to connect to {} at {}:{} (but port is bound?)'
 			return do_fail(None,2,m.format(g.proto.daemon_name,self.host,self.port))
 
-		dmsg('    RPC GETRESPONSE data ==> %s\n' % r.__dict__)
+		dmsg_rpc('    RPC GETRESPONSE data ==> %s\n' % r.__dict__)
 
 		if r.status != 200:
 			if cf['on_fail'] not in ('silent','raise'):
@@ -145,7 +148,7 @@ class CoinDaemonRPCConnection(object):
 
 		r2 = r.read()
 
-		dmsg('    RPC REPLY data ==> %s\n' % r2)
+		dmsg_rpc('    RPC REPLY data ==> %s\n' % r2)
 
 		if not r2:
 			return do_fail(r,2,'Error: empty reply')

+ 8 - 10
mmgen/tx.py

@@ -131,10 +131,12 @@ def bytes2coin_amt(hex_bytes):
 	return g.proto.coin_amt(bytes2int(hex_bytes) * g.proto.coin_amt.min_coin_unit)
 
 def scriptPubKey2addr(s):
-	if len(s) == 50 and s[:6] == '76a914' and s[-4:] == '88ac': addr_hex,p2sh = s[6:-4],False
-	elif len(s) == 46 and s[:4] == 'a914' and s[-2:] == '87':   addr_hex,p2sh = s[4:-2],True
-	else: raise NotImplementedError,'Unknown scriptPubKey'
-	return g.proto.pubhash2addr(addr_hex,p2sh)
+	if len(s) == 50 and s[:6] == '76a914' and s[-4:] == '88ac':
+		return g.proto.pubhash2addr(s[6:-4],p2sh=False)
+	elif len(s) == 46 and s[:4] == 'a914' and s[-2:] == '87':
+		return g.proto.pubhash2addr(s[4:-2],p2sh=True)
+	else:
+		raise NotImplementedError,'Unknown scriptPubKey ({})'.format(s)
 
 from collections import OrderedDict
 class DeserializedTX(OrderedDict,MMGenObject): # need to add MMGen types
@@ -162,7 +164,7 @@ class DeserializedTX(OrderedDict,MMGenObject): # need to add MMGen types
 			return ret
 
 		d['version'] = bytes2int(hshift(tx,4))
-		has_witness = (False,True)[hexlify(tx[0])=='00']
+		has_witness = tx[0] == '\x00'
 		if has_witness:
 			u = hshift(tx,2,skip=True)[2:]
 			if u != '01':
@@ -383,7 +385,7 @@ class MMGenTX(MMGenObject):
 	# serialization, divide the result by 4 and round up to the next integer.
 
 	# TODO: results differ slightly from actual transaction size
-	def estimate_vsize(self):
+	def estimate_size(self):
 		if not self.inputs or not self.outputs: return None
 
 		sig_size = 72 # sig in DER format
@@ -439,8 +441,6 @@ class MMGenTX(MMGenObject):
 # 		pmsg('estimate_size_old',self.estimate_size_old())
 		return ret
 
-	estimate_size = estimate_vsize
-
 	def get_fee(self):
 		return self.sum_inputs() - self.sum_outputs()
 
@@ -609,8 +609,6 @@ class MMGenTX(MMGenObject):
 
 		msg_r('Signing transaction{}...'.format(tx_num_str))
 		wifs = [d.sec.wif for d in keys]
-#		keys.pmsg()
-#		pmsg(wifs)
 		ret = g.rpch.signrawtransaction(self.hex,sig_data,wifs,g.proto.sighash_type,on_fail='return')
 
 		from mmgen.rpc import rpc_error,rpc_errmsg

+ 2 - 2
mmgen/util.py

@@ -70,10 +70,10 @@ def pformat(d):
 	return pprint.PrettyPrinter(indent=4).pformat(d)
 def pmsg(*args):
 	if not args: return
-	Msg(pformat(args if len(args) > 1 else args[0]))
+	msg(pformat(args if len(args) > 1 else args[0]))
 def pdie(*args):
 	if not args: sys.exit(1)
-	Die(1,(pformat(args if len(args) > 1 else args[0])))
+	die(1,(pformat(args if len(args) > 1 else args[0])))
 
 def set_for_type(val,refval,desc,invert_bool=False,src=None):
 	src_str = (''," in '{}'".format(src))[bool(src)]

+ 5 - 2
test/mmgen_pexpect.py

@@ -235,8 +235,11 @@ class MMGenPexpect(object):
 		self.expect("Overwrite?  Type uppercase 'YES' to confirm: ",'\n')
 		self.expect('Exiting at user request')
 
-	def tx_view(self):
-		my_expect(self.p,r'View .*?transaction.*? \(y\)es, \(N\)o, pager \(v\)iew.*?: ','\n',regex=True)
+	def tx_view(self,view=None):
+		repl = { 'terse':'t', 'full':'v' }[view] if view else 'n'
+		my_expect(self.p,r'View .*?transaction.*? \(y\)es, \(N\)o, pager \(v\)iew.*?: ',repl,regex=True)
+		if repl in ('t','v'):
+			my_expect(self.p,r'any key to continue: ','\n')
 
 	def expect_getend(self,s,regex=False):
 		ret = self.expect(s,regex=regex,nonl=True)

+ 56 - 37
test/test.py

@@ -177,8 +177,14 @@ if opt.segwit and 'S' not in g.proto.mmtypes:
 
 def randbool():
 	return hexlify(os.urandom(1))[1] in '12345678'
-def get_segwit_val():
+def get_segwit_bool():
 	return randbool() if opt.segwit_random else True if opt.segwit else False
+def disable_debug():
+	ds = os.getenv('MMGEN_DEBUG')
+	if ds is not None: os.environ['MMGEN_DEBUG'] = ''
+	return ds
+def restore_debug(ds):
+	if ds is not None: os.environ['MMGEN_DEBUG'] = ds
 
 cfgs = {
 	'15': {
@@ -194,7 +200,7 @@ cfgs = {
 			'mmseed':      'export_seed_dfl_wallet',
 			'del_dw_run':  'delete_dfl_wallet',
 		},
-		'segwit': get_segwit_val()
+		'segwit': get_segwit_bool()
 	},
 	'16': {
 		'tmpdir':        os.path.join('test','tmp16'),
@@ -203,7 +209,7 @@ cfgs = {
 		'dep_generators': {
 			pwfile:        'passchg_dfl_wallet',
 		},
-		'segwit': get_segwit_val()
+		'segwit': get_segwit_bool()
 	},
 	'17': { 'tmpdir': os.path.join('test','tmp17') },
 	'18': { 'tmpdir': os.path.join('test','tmp18') },
@@ -229,7 +235,7 @@ cfgs = {
 			incog_id_fn:   'export_incog_hidden',
 			'akeys.mmenc': 'keyaddrgen'
 		},
-		'segwit': get_segwit_val()
+		'segwit': get_segwit_bool()
 	},
 	'2': {
 		'tmpdir':        os.path.join('test','tmp2'),
@@ -243,7 +249,7 @@ cfgs = {
 			'sigtx':         'txsign2',
 			'mmwords':     'export_mnemonic2',
 		},
-		'segwit': get_segwit_val()
+		'segwit': get_segwit_bool()
 	},
 	'3': {
 		'tmpdir':        os.path.join('test','tmp3'),
@@ -255,7 +261,7 @@ cfgs = {
 			'rawtx':         'txcreate3',
 			'sigtx':         'txsign3'
 		},
-		'segwit': get_segwit_val()
+		'segwit': get_segwit_bool()
 	},
 	'4': {
 		'tmpdir':        os.path.join('test','tmp4'),
@@ -272,7 +278,7 @@ cfgs = {
 		},
 		'bw_filename': 'brainwallet.mmbrain',
 		'bw_params':   '192,1',
-		'segwit': get_segwit_val()
+		'segwit': get_segwit_bool()
 	},
 	'14': {
 		'kapasswd':      'Maxwell',
@@ -285,7 +291,7 @@ cfgs = {
 			'addrs':       'addrgen14',
 			'akeys.mmenc': 'keyaddrgen14',
 		},
-		'segwit': get_segwit_val()
+		'segwit': get_segwit_bool()
 	},
 	'5': {
 		'tmpdir':        os.path.join('test','tmp5'),
@@ -295,7 +301,7 @@ cfgs = {
 			'mmdat':       'passchg',
 			pwfile:        'passchg',
 		},
-		'segwit': get_segwit_val()
+		'segwit': get_segwit_bool()
 	},
 	'6': {
 		'name':            'reference wallet check (128-bit)',
@@ -347,7 +353,7 @@ cfgs = {
 			'addrs':       'refaddrgen1',
 			'akeys.mmenc': 'refkeyaddrgen1'
 		},
-		'segwit': get_segwit_val()
+		'segwit': get_segwit_bool()
 	},
 	'7': {
 		'name':            'reference wallet check (192-bit)',
@@ -399,7 +405,7 @@ cfgs = {
 			'addrs':       'refaddrgen2',
 			'akeys.mmenc': 'refkeyaddrgen2'
 		},
-		'segwit': get_segwit_val()
+		'segwit': get_segwit_bool()
 	},
 	'8': {
 		'name':            'reference wallet check (256-bit)',
@@ -488,7 +494,7 @@ cfgs = {
 			'addrs':       'refaddrgen3',
 			'akeys.mmenc': 'refkeyaddrgen3'
 		},
-		'segwit': get_segwit_val()
+		'segwit': get_segwit_bool()
 	},
 	'9': {
 		'tmpdir':        os.path.join('test','tmp9'),
@@ -1013,7 +1019,10 @@ class MMGenExpect(MMGenPexpect):
 def create_fake_unspent_entry(coinaddr,al_id=None,idx=None,lbl=None,non_mmgen=False,segwit=False):
 	if 'S' not in g.proto.mmtypes: segwit = False
 	if lbl: lbl = ' ' + lbl
-	spk1,spk2 = (('76a914','88ac'),('a914','87'))[segwit and coinaddr.addr_fmt=='p2sh']
+	spk_beg,spk_end = (
+		('76a914','88ac'),
+		('a914','87'),
+		)[segwit and coinaddr.addr_fmt=='p2sh']
 	amt1,amt2 = {'btc':(10,40),'bch':(10,40),'ltc':(1000,4000)}[coin_sel]
 	return {
 		'account': '{}:{}'.format(g.proto.base_coin.lower(),coinaddr) if non_mmgen \
@@ -1023,7 +1032,7 @@ def create_fake_unspent_entry(coinaddr,al_id=None,idx=None,lbl=None,non_mmgen=Fa
 		'amount': g.proto.coin_amt('%s.%s' % (amt1+(getrandnum(4) % amt2), getrandnum(4) % 100000000)),
 		'address': coinaddr,
 		'spendable': False,
-		'scriptPubKey': '{}{}{}'.format(spk1,coinaddr.hex,spk2),
+		'scriptPubKey': '{}{}{}'.format(spk_beg,coinaddr.hex,spk_end),
 		'confirmations': getrandnum(4) % 50000
 	}
 
@@ -1067,10 +1076,10 @@ def create_fake_unspent_data(adata,tx_data,non_mmgen_input=''):
 
 	if non_mmgen_input:
 		privkey = PrivKey(os.urandom(32),compressed=True,pubkey_type='std')
-		coinaddr = AddrGenerator('p2pkh').to_addr(KeyGenerator('std').to_pubhex(privkey))
+		rand_coinaddr = AddrGenerator('p2pkh').to_addr(KeyGenerator('std').to_pubhex(privkey))
 		of = os.path.join(cfgs[non_mmgen_input]['tmpdir'],non_mmgen_fn)
 		write_data_to_file(of,privkey.wif+'\n','compressed {} key'.format(g.proto.name),silent=True)
-		out.append(create_fake_unspent_entry(coinaddr,non_mmgen=True,segwit=False))
+		out.append(create_fake_unspent_entry(rand_coinaddr,non_mmgen=True,segwit=False))
 
 #	msg('\n'.join([repr(o) for o in out])); sys.exit(0)
 	return out
@@ -1108,7 +1117,7 @@ def create_tx_data(sources):
 def make_txcreate_cmdline(tx_data):
 	privkey = PrivKey(os.urandom(32),compressed=True,pubkey_type='std')
 	t = ('p2pkh','segwit')['S' in g.proto.mmtypes]
-	coinaddr = AddrGenerator(t).to_addr(KeyGenerator('std').to_pubhex(privkey))
+	rand_coinaddr = AddrGenerator(t).to_addr(KeyGenerator('std').to_pubhex(privkey))
 
 	# total of two outputs must be < 10 BTC (<1000 LTC)
 	mods = {'btc':(6,4),'bch':(6,4),'ltc':(600,400)}[coin_sel]
@@ -1127,7 +1136,7 @@ def make_txcreate_cmdline(tx_data):
 		# + one change address and one BTC address
 		if num is tx_data.keys()[-1]:
 			cmd_args += ['{}:{}'.format(s['al_id'],s['addr_idxs'][1])]
-			cmd_args += ['{},{}'.format(coinaddr,cfgs[num]['amts'][1])]
+			cmd_args += ['{},{}'.format(rand_coinaddr,cfgs[num]['amts'][1])]
 
 	return cmd_args + [tx_data[num]['addrfile'] for num in tx_data]
 
@@ -1441,7 +1450,8 @@ class MMGenTestSuite(object):
 		ok()
 
 	def addrgen(self,name,wf,pf=None,check_ref=False,ftype='addr',id_str=None,extra_args=[],mmtype=None):
-		if cfg['segwit'] and ftype[:4] != 'pass' and not mmtype: mmtype = 'segwit'
+		if ftype[:4] != 'pass' and not mmtype:
+			if cfg['segwit']: mmtype = 'segwit'
 		cmd_pfx = (ftype,'pass')[ftype[:4]=='pass']
 		t = MMGenExpect(name,'mmgen-{}gen'.format(cmd_pfx),
 				['-d',cfg['tmpdir']] +
@@ -1455,16 +1465,20 @@ class MMGenTestSuite(object):
 		t.expect('Passphrase is OK')
 		desc = ('address','password')[ftype[:4]=='pass']
 		chk = t.expect_getend(r'Checksum for {} data .*?: '.format(desc),regex=True)
+		if ftype[:4] == 'pass':
+			t.expect('Encrypt password list? (y/N): ','\n')
+			t.written_to_file('Password list',oo=True)
+		else:
+			t.written_to_file('Addresses',oo=True)
 		if check_ref:
 			k = 'passfile32_chk' if ftype == 'pass32' \
 					else 'passfilehex_chk' if ftype == 'passhex' \
 						else 'passfile_chk' if ftype == 'pass' \
 							else '{}file{}_chk'.format(ftype,'_'+mmtype if mmtype else '')
 			chk_ref = cfg[k] if ftype[:4] == 'pass' else cfg[k][fork][g.testnet]
-			refcheck('address data checksum',chk,chk_ref)
-			return
-		t.written_to_file('Addresses',oo=True)
-		t.ok()
+			refcheck('{}list data checksum'.format(ftype),chk,chk_ref)
+		else:
+			t.ok()
 
 	def addrgen_dfl_wallet(self,name,pf=None,check_ref=False):
 		return self.addrgen(name,wf=None,pf=pf,check_ref=check_ref)
@@ -1486,7 +1500,7 @@ class MMGenTestSuite(object):
 		vmsg('This is a simulation, so no addresses were actually imported into the tracking\nwallet')
 		t.ok(exit_val=1)
 
-	def txcreate_common(self,name,sources=['1'],non_mmgen_input='',do_label=False,txdo_args=[],add_args=[]):
+	def txcreate_common(self,name,sources=['1'],non_mmgen_input='',do_label=False,txdo_args=[],add_args=[],view=None):
 		if opt.verbose or opt.exact_output:
 			sys.stderr.write(green('Generating fake tracking wallet info\n'))
 
@@ -1538,7 +1552,7 @@ class MMGenTestSuite(object):
 			t.expect('Comment: ',ref_tx_label.encode('utf8')+'\n')
 		else:
 			t.expect('Add a comment to transaction? (y/N): ','\n')
-		t.tx_view()
+		t.tx_view(view=view)
 		if txdo_args: return t
 		t.expect('Save transaction? (y/N): ','y')
 		t.written_to_file('Transaction')
@@ -1619,7 +1633,7 @@ class MMGenTestSuite(object):
 			t = MMGenExpect(name,'mmgen-txsend', extra_opts + ['-d',cfg['tmpdir'],sigfile])
 			if really_send: os.environ['MMGEN_BOGUS_SEND'] = '1'
 			t.license()
-			t.tx_view()
+			t.tx_view(view='terse')
 			t.expect('Add a comment to transaction? (y/N): ','\n')
 		t.expect('Are you sure you want to broadcast this')
 		m = 'YES, I REALLY WANT TO DO THIS'
@@ -1735,7 +1749,8 @@ class MMGenTestSuite(object):
 			args=['-H','%s,%s'%(rf,hincog_offset),'-l',str(hincog_seedlen)])
 
 	def keyaddrgen(self,name,wf,pf=None,check_ref=False,mmtype=None):
-		if cfg['segwit'] and not mmtype: mmtype = 'segwit'
+		if cfg['segwit'] and not mmtype:
+			mmtype = 'segwit'
 		args = ['-d',cfg['tmpdir'],usr_rand_arg,wf,cfg['addr_idx_list']]
 		t = MMGenExpect(name,'mmgen-keygen',
 				([],['--type='+str(mmtype)])[bool(mmtype)] + args,extra_desc=('','(segwit)')[mmtype=='segwit'])
@@ -1866,7 +1881,7 @@ class MMGenTestSuite(object):
 			t.hash_preset('key-address data','1')
 			t.passphrase('key-address data',cfgs['14']['kapasswd'])
 			t.expect('Check key-to-address validity? (y/N): ','y')
-			t.tx_view()
+			t.tx_view(view='terse')
 
 		for cnum,desc in (('1','incognito data'),('3','MMGen wallet')):
 			t.passphrase(('%s' % desc),cfgs[cnum]['wpasswd'])
@@ -2134,8 +2149,7 @@ class MMGenTestSuite(object):
 
 	def ref_segwitaddrfile_chk(self,name):
 		if not 'S' in g.proto.mmtypes:
-			msg_r('Skipping {} (not supported)'.format(name))
-			ok()
+			msg_r('Skipping {} (not supported)'.format(name)); ok()
 		else:
 			self.ref_addrfile_chk(name,ftype='segwitaddr')
 
@@ -2294,7 +2308,7 @@ class MMGenTestSuite(object):
 	def regtest_user_bal(self,name,user,bal):
 		t = MMGenExpect(name,'mmgen-tool',['--'+user,'listaddresses','showempty=1'])
 		total = t.expect_getend('TOTAL: ')
-		cmp_or_die(total,'{} {}'.format(bal,g.coin))
+		cmp_or_die('{} {}'.format(bal,g.coin),total)
 
 	def regtest_alice_bal1(self,name):
 		return self.regtest_user_bal(name,'alice',rtFundAmt)
@@ -2318,11 +2332,11 @@ class MMGenTestSuite(object):
 		t = MMGenExpect(name,'mmgen-regtest',['get_balances'])
 		t.expect('Switching')
 		ret = t.expect_getend("Bob's balance:").strip()
-		cmp_or_die(ret,rtBals[4],skip_ok=True)
+		cmp_or_die(rtBals[4],ret,skip_ok=True)
 		ret = t.expect_getend("Alice's balance:").strip()
-		cmp_or_die(ret,rtBals[5],skip_ok=True)
+		cmp_or_die(rtBals[5],ret,skip_ok=True)
 		ret = t.expect_getend("Total balance:").strip()
-		cmp_or_die(ret,rtBals[6],skip_ok=True)
+		cmp_or_die(rtBals[6],ret,skip_ok=True)
 		t.ok()
 
 	def regtest_user_txdo(  self,name,user,fee,
@@ -2437,9 +2451,11 @@ class MMGenTestSuite(object):
 		t.ok()
 
 	def regtest_get_mempool(self,name):
-		t = MMGenExpect(name,'mmgen-regtest',['show_mempool'])
+		ds = disable_debug()
+		ret = MMGenExpect(name,'mmgen-regtest',['show_mempool']).read()
+		restore_debug(ds)
 		from ast import literal_eval
-		return literal_eval(t.read())
+		return literal_eval(ret)
 
 	def regtest_get_mempool1(self,name):
 		mp = self.regtest_get_mempool(name)
@@ -2461,11 +2477,14 @@ class MMGenTestSuite(object):
 
 	@staticmethod
 	def gen_pairs(n):
-		return [subprocess.check_output(
+		ds = disable_debug()
+		ret = [subprocess.check_output(
 						['python',os.path.join('cmds','mmgen-tool'),'--testnet=1'] +
 						([],['--type=compressed'])[bool((i+1)%2)] +
 						['-r0','randpair']
 					).split() for i in range(n)]
+		restore_debug(ds)
+		return ret
 
 	def regtest_bob_pre_import(self,name):
 		pairs = self.gen_pairs(5)