Browse Source

release/testing: UTF8 testing fixes, other fixes and improvements

The MMGen Project 5 years ago
parent
commit
5fe92460ad

+ 16 - 40
README.md

@@ -1,10 +1,6 @@
-***Note: This is the source code repository of the MMGen wallet system.  For an
-easier way to install MMGen, check out the prebuilt bootable USB images on the
-[MMGenLive][8] home page.***
-
 # MMGen = Multi-Mode GENerator
 
-##### a Bitcoin and altcoin online/offline software wallet for the command line
+##### An online/offline cryptocurrency wallet for the command line
 
 ### Description
 
@@ -15,7 +11,7 @@ offline computers to provide a robust solution for securely storing, tracking,
 sending and receiving your crypto assets.
 
 The online computer is used for tracking balances and creating and sending
-transactions, while the offline computer (typically an air-gapped, low-power
+transactions, while the offline machine (typically an air-gapped, low-power
 device such as a Raspberry Pi) takes care of wallet creation, address generation
 and transaction signing.  All operations involving secret data are handled
 offline: **your seed and private keys never come into contact with a
@@ -34,36 +30,11 @@ of address/key pairs from a single seed.  Your wallet never changes, so you need
 back it up only once.
 
 At the heart of the MMGen system is the seed, the “master key” providing access
-to all your crypto assets.  The seed can be stored in five different ways:
-
-  1. as a password-encrypted wallet.  The crack-resistant Scrypt hash function
-	 is used for password hashing.  Scrypt’s parameters can be tuned to make
-	 your wallet’s password very difficult to crack should it fall into the
-	 wrong hands.  The wallet is a compact, six-line text file suitable for
-	 printing or even writing out by hand;
-
-  2. as a seed file: a one-line, conveniently formatted base-58 representation
-	 of your unencrypted seed plus a checksum;
-
-  3. as an Electrum-like mnemonic seed phrase of 12, 18 or 24 words;
-
-  4. as a brainwallet passphrase (this option is recommended only for users who
-	 understand the risks of brainwallets and know how to create a strong
-	 brainwallet passphrase).  The brainwallet is hashed using Scrypt with
-	 tunable parameters, making it much harder to crack than standard SHA256
-	 brainwallets; or
-
-  5. as “incognito data”, a wallet encrypted to make it indistinguishable
-	 from random data.  This data can be hidden on a disk partition filled with
-	 random data, or in a file at an offset of your choice.  This makes it
-	 possible to hide a wallet at a non-private location—on cloud storage, for
-	 example.  Incognito wallet hiding/retrieval is seamlessly integrated into
-	 MMGen, making its use nearly as easy as that of the standard wallet.
-
-The best part is that all these methods can be combined.  If you forget your
-mnemonic seed phrase, for example, you can regenerate it from a stored wallet
-or seed file.  Correspondingly, a lost wallet can be regenerated from a mnemonic
-or seed or vice-versa.
+to all your crypto assets.  The seed can be stored in many different formats:
+as a password-encrypted wallet (the default), as a one-line base58 or
+hexidecimal seed file, as an Electrum-based mnemonic seed phrase, as a
+brainwallet passphrase, or as “incognito data” hideable within random data in a
+file or block device.  Conversion between all formats is supported.
 
 ***mmgen-txcreate running in a terminal window***
 ![mmgen-txcreate running in a terminal window][9]
@@ -94,7 +65,7 @@ the more prosaic 2048-word [BIP39 wordlist][bw] used in most wallets today.
   [Litecoin][bx], [Ethereum][E], Ethereum Classic and [ERC20 tokens][E].
 - **[Address generation support][ag]** for the above coins, plus [Monero][mx],
   [Zcash][zx] (t and z addresses) and [144 Bitcoin-derived altcoins][ax].
-- **Support for all Bitcoin address types** including segwit-p2sh and bech32.
+- **Support for all Bitcoin address types** including Segwit-P2SH and Bech32.
 - **Independent key derivation for each address type:** No two addresses ever
   share the same private key.  Certain wallets in wide use today regrettably
   fail to guarantee this property, leading to the danger of inadvertent key
@@ -118,11 +89,12 @@ the more prosaic 2048-word [BIP39 wordlist][bw] used in most wallets today.
   always be regenerated from their parent.
 - **[Transaction autosigning][X]:** This feature puts your offline signing
   machine into “hands-off” mode, allowing you to transact directly from cold
-  storage securely and conveniently.  Additional LED signaling support is
+  storage securely and conveniently.  Additional LED blinking support is
   provided for Raspbian and Armbian platforms.
 - **[Password generation][G]:** MMGen can be used to generate and manage your
-  online passwords.  Passwords are identified by arbitrarily chosen strings like
-  “alice@github” or “bob@reddit”.
+  online passwords.  Password lists are identified by arbitrarily chosen strings
+  like “alice@github” or “bob@reddit”.  Passwords of different lengths and
+  formats are supported.
 - **Selectable seed lengths** of 128, 192 or 256 bits.  Subwallets may have
   shorter seeds than their parent.
 - **User-enhanced entropy:** All operations requiring random data will prompt
@@ -145,6 +117,10 @@ the more prosaic 2048-word [BIP39 wordlist][bw] used in most wallets today.
   you to automate repetitive tasks using shell scripts.  Most of the
   `mmgen-tool` utility’s commands can be piped.
 
+#### Supported platforms:
+
+Linux, Armbian, Raspbian, Windows/MSYS2
+
 ### Download/Install
 
 > #### [Install a prebuilt bootable image (MMGenLive) on a USB stick][8]

+ 18 - 17
doc/release-notes/release-notes-v0.12.0.md

@@ -4,11 +4,11 @@
 
  - XOR seed splitting: 7311f474, 237567bc, c7ca0c3d (see
    [XOR-Seed-Splitting:-Theory-and-Practice][xo] for additional information)
- - ETH tracking-wallet balance caching, Parity light client optimizations:
-   d0f8c44b
  - Full BIP39 mnemonic support: 8519b68b, 8705e57b
  - Monero new-style mnemonic support: cfa16418
- - New die-roll wallet format, interactive die-roll entry: c7786369, 4714ef84
+ - New dieroll wallet format, interactive dieroll entry: c7786369, 4714ef84
+ - ETH tracking-wallet balance caching, Parity light client optimizations:
+   d0f8c44b
 
 #### Other changes/additions/improvements:
 
@@ -17,28 +17,26 @@
  - Monero wallet creation/syncing tool reimplemented, now works under MSYS2:
    3951925a
  - New Tool API interface: f8056630
- - Full automation of test suite, automatic starting/stopping of daemons
- - Plus lots of code cleanups, bugfixes, and additional tests!
+ - New [Daemon control interface][dc] and [test daemon start/stop utilities][ss]
+ - Full automation of test suite with automatic starting/stopping of daemons
+ - UTF8 password entry works reliably under MSYS2, warnings disabled
+ - Plus lots of code cleanups, bugfixes, new tests and [expanded documentation][w]
 
 This release has been tested on the following platforms:
 
         Debian Buster / x86_64
+        Ubuntu Bionic / x86_64 / qemu-x86_64
+        Armbian Bionic / Orange Pi PC2 (armv8) 
+        Raspbian Buster / Raspberry Pi B (armv7) (no Parity, no Monerod)
         Windows 10 Enterprise Eng. / MSYS2 / qemu-x86_64
 
 and with the following coin daemon versions:
 
-        Bitcoin Core v0.19.0.1
-        Bitcoin-ABC v0.20.9
-        Litecoin Core v0.17.1
-        Monerod v0.15.0.1
-        Parity Ethereum v2.7.2
-
-Testing TBD on the following platforms:
-
-        Ubuntu Bionic / x86_64 / qemu-x86_64
-        Ubuntu Xenial (+Python 3.6.7) / x86_64
-        Armbian Bionic / Orange Pi PC2 (no Parity or Monerod)
-        Raspbian Stretch / Raspberry Pi B (no Parity or Monerod)
+        Bitcoin Core 0.17.1, 0.19.0.1
+        Bitcoin-ABC 0.21.0
+        Litecoin Core 0.17.1
+        Monerod 0.15.0.1
+        Parity Ethereum 2.7.2
 
 Altcoin address generation has been additionally tested using the following
 tools as references:
@@ -48,3 +46,6 @@ tools as references:
         vanitygen-plus 22123128 (https://github.com/exploitagency/vanitygen-plus)
 
 [xo]: https://github.com/mmgen/mmgen/wiki/XOR-Seed-Splitting:-Theory-and-Practice.md
+[dc]: https://github.com/mmgen/mmgen/blob/master/mmgen/daemon.py
+[ss]: https://github.com/mmgen/mmgen/blob/master/test/start-coin-daemons.py
+[w]: https://github.com/mmgen/mmgen/wiki

+ 18 - 3
mmgen/daemon.py

@@ -158,13 +158,20 @@ class Daemon(MMGenObject):
 		self.stop(silent=silent)
 		return self.start(silent=silent)
 
+	def test_socket(self,host,port,timeout=10):
+		import socket
+		try: socket.create_connection((host,port),timeout=timeout).close()
+		except: return False
+		else: return True
+
 	def wait_for_state(self,req_state):
-		for i in range(200):
+		for i in range(300):
 			if self.state == req_state:
 				return True
 			time.sleep(0.2)
 		else:
-			die(2,'Daemon wait timeout for {} {} exceeded'.format(self.daemon_id.upper(),self.network))
+			m = 'Wait for state {!r} timeout exceeded for daemon {} {}'
+			die(2,m.format(req_state,self.daemon_id.upper(),self.network))
 
 	@classmethod
 	def check_implement(cls):
@@ -221,6 +228,8 @@ class MoneroWalletDaemon(Daemon):
 
 	@property
 	def state(self):
+		if not self.test_socket(g.monero_wallet_rpc_host,self.rpc_port):
+			return 'stopped'
 		from mmgen.rpc import MoneroWalletRPCConnection
 		try:
 			MoneroWalletRPCConnection(
@@ -291,7 +300,11 @@ class CoinDaemon(Daemon):
 		if network == 'regtest':
 			me.desc = 'regtest daemon'
 			if test_suite:
-				rel_datadir = os.path.join('test','data_dir','regtest',daemon_id)
+				rel_datadir = os.path.join(
+					'test',
+					'data_dir{}'.format('-α' if g.debug_utf8 else ''),
+					'regtest',
+					daemon_id )
 			else:
 				me.datadir = os.path.join(g.data_dir_root,'regtest',daemon_id)
 		elif test_suite:
@@ -431,6 +444,8 @@ class MoneroDaemon(CoinDaemon):
 
 	@property
 	def state(self):
+		if not self.test_socket(g.monero_wallet_rpc_host,self.rpc_port):
+			return 'stopped'
 		cp = self.run_cmd(
 			[self.coind_exec]
 			+ self.shared_args

+ 0 - 3
mmgen/globalvars.py

@@ -209,9 +209,6 @@ class g(object):
 	max_tx_file_size = 100000
 	max_input_size   = 1024 * 1024
 
-	# pexpect chokes on these utf8 chars under MSYS2
-	lq,rq = (('“','”'),('"','"'))[bool(os.getenv('MMGEN_TEST_SUITE')) and platform=='win']
-
 	passwd_max_tries = 5
 
 	max_urandchars = 80

+ 0 - 4
mmgen/opts.py

@@ -294,10 +294,6 @@ def init(opts_data,add_opts=[],opt_filter=None,parse_only=False):
 		if not 'code' in opts_data:
 			opts_data['code'] = {}
 		opts_data['code']['long_options'] = common_opts_data['code']
-		if g.debug_utf8:
-			for k in opts_data:
-				if type(opts_data[k]) == str:
-					opts_data[k] += '-α'
 		mmgen.share.Opts.print_help(opts_data,opt_filter) # exits
 
 	if g.bob or g.alice:

+ 2 - 2
mmgen/seed.py

@@ -866,12 +866,12 @@ class MMGenMnemonic(SeedSourceUnenc):
 
 		m  = 'Enter your {ml}-word seed phrase, hitting ENTER or SPACE after each word.\n'
 		m += "Optionally, you may use pad characters.  Anything you type that's not a\n"
-		m += 'lowercase letter will be treated as a {lq}pad character{rq}, i.e. it will simply\n'
+		m += 'lowercase letter will be treated as a “pad character”, i.e. it will simply\n'
 		m += 'be discarded.  Pad characters may be typed before, after, or in the middle\n'
 		m += "of words.  For each word, once you've typed {lw} characters total (including\n"
 		m += 'pad characters) any pad character will enter the word.'
 
-		msg(m.format(ml=mn_len,lw=longest_word,lq=g.lq,rq=g.rq))
+		msg(m.format(ml=mn_len,lw=longest_word))
 
 		from string import ascii_lowercase
 		from mmgen.term import get_char_raw

+ 14 - 5
mmgen/tool.py

@@ -495,8 +495,11 @@ class MMGenToolCmdMnemonic(MMGenToolCmdBase):
 
 	@staticmethod
 	def _xmr_reduce(bytestr):
-		from mmgen.protocol import MoneroProtocol
-		return MoneroProtocol.preprocess_key(bytestr,None)
+		from mmgen.protocol import MoneroProtocol as mp
+		if len(bytestr) != mp.privkey_len:
+			m = '{!r}: invalid bit length for Monero private key (must be {})'
+			die(1,m.format(len(bytestr*8),mp.privkey_len*8))
+		return mp.preprocess_key(bytestr,None)
 
 	def _do_random_mn(self,nbytes:int,fmt:str):
 		assert nbytes in (16,24,32), 'nbytes must be 16, 24 or 32'
@@ -891,7 +894,13 @@ class MMGenToolCmdRPC(MMGenToolCmdBase):
 		return ret
 
 class MMGenToolCmdMonero(MMGenToolCmdBase):
-	"Monero wallet utilities"
+	"""
+	Monero wallet utilities
+
+	Note that the use of these commands requires private data to be exposed on
+	a network-connected machine in order to unlock the Monero wallets.  This is
+	a violation of MMGen's security policy.
+	"""
 
 	_monero_chain_height = None
 	monerod_args = []
@@ -918,14 +927,14 @@ class MMGenToolCmdMonero(MMGenToolCmdBase):
 									xmr_keyaddrfile:str,
 									blockheight:'(default: current height)' = 0,
 									addrs:'(integer range or list)' = ''):
-		"create Monero wallets from key-address list"
+		"create Monero wallets from a key-address list"
 		return self.monero_wallet_ops(  infile = xmr_keyaddrfile,
 										op = 'create',
 										blockheight = blockheight,
 										addrs = addrs)
 
 	def syncmonerowallets(self,xmr_keyaddrfile:str,addrs:'(integer range or list)'=''):
-		"sync Monero wallets from key-address list"
+		"sync Monero wallets from a key-address list"
 		return self.monero_wallet_ops(infile=xmr_keyaddrfile,op='sync',addrs=addrs)
 
 	def monero_wallet_ops(self,infile:str,op:str,blockheight=0,addrs='',monerod_args=[]):

+ 7 - 2
test/common.py

@@ -23,7 +23,7 @@ common.py: Shared routines and data for the MMGen test suites
 class TestSuiteException(Exception): pass
 class TestSuiteFatalException(Exception): pass
 
-import os
+import os,time
 from mmgen.common import *
 from mmgen.devtools import *
 
@@ -166,13 +166,18 @@ def iqmsg_r(s):
 	if not opt.quiet: omsg_r(s)
 
 def start_test_daemons(*network_ids):
+	if hasattr(opt,'no_daemon_autostart') and opt.no_daemon_autostart:
+		return
 	return test_daemons_ops(*network_ids,op='start')
 
 def stop_test_daemons(*network_ids):
+	if hasattr(opt,'no_daemon_stop') and opt.no_daemon_stop:
+		return
 	return test_daemons_ops(*network_ids,op='stop')
 
 def restart_test_daemons(*network_ids):
-	return test_daemons_ops(*network_ids,op='restart')
+	stop_test_daemons(*network_ids)
+	return start_test_daemons(*network_ids)
 
 def test_daemons_ops(*network_ids,op):
 	if opt.no_daemon_autostart:

+ 17 - 14
test/test-release.sh

@@ -37,18 +37,19 @@ rounds=100 rounds_min=20 rounds_mid=250 rounds_max=500
 xmr_addrs='3,99,2,22-24,101-104'
 
 dfl_tests='misc obj color unit hash ref altref alts xmr eth autosign btc btc_tn btc_rt bch bch_rt ltc ltc_rt tool tool2 gen'
-extra_tests='autosign_minimal autosign_live etc ltc_tn bch_tn'
-noalt_tests='misc obj color unit hash ref autosign_minimal btc btc_tn btc_rt tool tool2 gen'
+extra_tests='autosign_btc autosign_live etc ltc_tn bch_tn'
+noalt_tests='misc obj color unit hash ref autosign_btc btc btc_tn btc_rt tool tool2 gen'
 quick_tests='misc obj color unit hash ref altref alts xmr eth autosign btc btc_rt tool tool2 gen'
 qskip_tests='btc_tn bch bch_rt ltc ltc_rt'
 
 PROGNAME=$(basename $0)
-while getopts hbCfFi:I:lOpRtvV OPT
+while getopts hAbCfFi:I:lOpRtvV OPT
 do
 	case "$OPT" in
 	h)  printf "  %-16s Test MMGen release\n" "${PROGNAME}:"
 		echo   "  USAGE:           $PROGNAME [options] [tests or test group]"
 		echo   "  OPTIONS: '-h'  Print this help message"
+		echo   "           '-A'  Skip tests requiring altcoin modules or daemons"
 		echo   "           '-b'  Buffer keypresses for all invocations of 'test/test.py'"
 		echo   "           '-C'  Run tests in coverage mode"
 		echo   "           '-f'  Speed up the tests by using fewer rounds"
@@ -100,6 +101,7 @@ do
 		echo
 		echo   "  By default, all tests are run"
 		exit ;;
+	A)  SKIP_ALT=1 ;;
 	b)  test_py+=" --buf-keypress" ;;
 	C)  mkdir -p 'test/trace'
 		touch 'test/trace.acc'
@@ -151,7 +153,7 @@ case $1 in
 	'')        tests=$dfl_tests ;;
 	'default') tests=$dfl_tests ;;
 	'extra')   tests=$extra_tests ;;
-	'noalt')   tests=$noalt_tests ;;
+	'noalt')   tests=$noalt_tests SKIP_ALT=1 ;;
 	'quick')   tests=$quick_tests ;;
 	'qskip')   tests=$qskip_tests ;;
 	*)         tests="$*" ;;
@@ -316,16 +318,16 @@ t_alts="
 	#   keyconv
 	$gentest_py --all --type=legacy 2:keyconv $rounds
 	$gentest_py --all --type=compressed 2:keyconv $rounds
+	#   ethkey
+	$gentest_py --all 2:ethkey $rounds
 "
 
-[ "$MSYS2" ] || { # no moneropy (pysha3), zcash-mini (golang), ethkey (?)
+[ "$MSYS2" ] || { # no moneropy (pysha3), zcash-mini (golang)
 	t_alts+="
 		#   moneropy
 		$gentest_py --all --coin=xmr 2:moneropy $rounds_min # very slow, be patient!
 		#   zcash-mini
 		$gentest_py --all 2:zcash-mini $rounds_mid
-		#   ethkey
-		$gentest_py --all 2:ethkey $rounds
 	"
 }
 
@@ -343,7 +345,7 @@ else
 	mkdir -p $TMPDIR
 fi
 
-mmgen_tool_xmr="$mmgen_tool -q --accept-defaults --outdir $TMPDIR"
+mmgen_tool_xmr="$mmgen_tool -q --accept-defaults --outdir $TMPDIR --monero-wallet-rpc-password=passw0rd"
 i_xmr='Monero'
 s_xmr='Testing key-address file generation and wallet creation and sync operations for Monero'
 s_xmr='The monerod (mainnet) daemon must be running for the following tests'
@@ -387,10 +389,10 @@ s_autosign='The bitcoin, bitcoin-abc and litecoin mainnet and testnet daemons mu
 t_autosign="$test_py autosign"
 f_autosign='Autosign test completed'
 
-i_autosign_minimal='Autosign Minimal'
-s_autosign_minimal='The bitcoin mainnet and testnet daemons must be running for the following test'
-t_autosign_minimal="$test_py autosign_minimal"
-f_autosign_minimal='Autosign Minimal test completed'
+i_autosign_btc='Autosign BTC'
+s_autosign_btc='The bitcoin mainnet and testnet daemons must be running for the following test'
+t_autosign_btc="$test_py autosign_btc"
+f_autosign_btc='Autosign BTC test completed'
 
 i_autosign_live='Autosign Live'
 s_autosign_live="The bitcoin mainnet and testnet daemons must be running for the following test\n"
@@ -402,7 +404,7 @@ f_autosign_live='Autosign Live test completed'
 i_btc='Bitcoin mainnet'
 s_btc='The bitcoin (mainnet) daemon must both be running for the following tests'
 t_btc="
-	$test_py --exclude regtest,autosign_minimal,ref_altcoin
+	$test_py --exclude regtest,autosign,ref_altcoin
 	$test_py --segwit
 	$test_py --segwit-random
 	$test_py --bech32
@@ -473,7 +475,6 @@ t_tool2="
 	$tooltest2_py --tool-api --coin=eth
 	$tooltest2_py --tool-api --coin=xmr
 	$tooltest2_py --tool-api --coin=zec
-	$tooltest2_py --fork # run once with --fork so commands are actually executed
 	$tooltest2_py
 	$tooltest2_py --testnet=1
 	$tooltest2_py --coin=ltc
@@ -488,8 +489,10 @@ t_tool2="
 	$tooltest2_py --coin=eth --token=mm1
 	$tooltest2_py --coin=eth --token=mm1 --testnet=1
 	$tooltest2_py --coin=etc
+	$tooltest2_py --fork # run once with --fork so commands are actually executed
 "
 f_tool2='tooltest2 tests completed'
+[ "$SKIP_ALT" ] && t_tool2_skip='17 18'
 
 i_tool='Tooltest'
 s_tool="The following tests will run '$tooltest_py' for all supported coins"

+ 14 - 2
test/test.py

@@ -476,7 +476,7 @@ class CmdGroupMgr(object):
 #		'chainsplit':       ('TestSuiteChainsplit',{}),
 		'ethdev':           ('TestSuiteEthdev',{}),
 		'autosign':         ('TestSuiteAutosign',{}),
-		'autosign_minimal': ('TestSuiteAutosignMinimal',{'modname':'autosign'}),
+		'autosign_btc':     ('TestSuiteAutosignBTC',{'modname':'autosign'}),
 		'autosign_live':    ('TestSuiteAutosignLive',{'modname':'autosign'}),
 		'create_ref_tx':    ('TestSuiteRefTX',{'modname':'misc','full_data':True}),
 	}
@@ -491,7 +491,7 @@ class CmdGroupMgr(object):
 					'tool',
 					'input',
 					'output',
-					'autosign_minimal',
+					'autosign',
 					'regtest',
 					'ethdev')
 
@@ -600,6 +600,7 @@ class TestSuiteRunner(object):
 		self.rebuild_list = OrderedDict()
 		self.gm = CmdGroupMgr()
 		self.repo_root = repo_root
+		self.skipped_warnings = []
 
 		if opt.log:
 			self.log_fd = open(log_file,'a')
@@ -863,6 +864,12 @@ class TestSuiteRunner(object):
 		if cmd == opt.exit_after:
 			sys.exit(0)
 
+	def warn_skipped(self):
+		if self.skipped_warnings:
+			print(yellow('The following tests were skipped and may require attention:'))
+			r = '-' * 72 + '\n'
+			print(r+('\n'+r).join(self.skipped_warnings))
+
 	def process_retval(self,cmd,ret):
 		if type(ret).__name__ == 'MMGenPexpect':
 			ret.ok()
@@ -872,6 +879,9 @@ class TestSuiteRunner(object):
 			self.cmd_total += 1
 		elif ret == 'skip':
 			pass
+		elif type(ret) == tuple and ret[0] == 'skip_warn':
+			self.skipped_warnings.append(
+				'Test {!r} was skipped:\n  {}'.format(cmd,'\n  '.join(ret[1].split('\n'))))
 		else:
 			rdie(1,'{!r} returned {}'.format(cmd,ret))
 
@@ -942,9 +952,11 @@ start_test_daemons(network_id)
 try:
 	tr = TestSuiteRunner(data_dir,trash_dir)
 	tr.run_tests(usr_args)
+	tr.warn_skipped()
 	stop_test_daemons(network_id)
 except KeyboardInterrupt:
 	stop_test_daemons(network_id)
+	tr.warn_skipped()
 	die(1,'\ntest.py exiting at user request')
 except TestSuiteException as e:
 	ydie(1,e.args[0])

+ 9 - 9
test/test_py_d/ts_autosign.py

@@ -40,14 +40,14 @@ class TestSuiteAutosign(TestSuiteBase):
 	)
 
 	def autosign_live(self):
-		return self.autosign_minimal(live=True)
+		return self.autosign_btc(live=True)
 
-	def autosign_minimal(self,live=False):
+	def autosign_btc(self,live=False):
 		return self.autosign(
-					coins=['btc','eth'],
+					coins=['btc'],
 					daemon_coins=['btc'],
-					txfiles=['btc','eth','mm1','etc'],
-					txcount=8,
+					txfiles=['btc'],
+					txcount=3,
 					live=live)
 
 	# tests everything except device detection, mount/unmount
@@ -228,13 +228,13 @@ class TestSuiteAutosign(TestSuiteBase):
 		stop_test_daemons(*network_ids)
 		return ret
 
-class TestSuiteAutosignMinimal(TestSuiteAutosign):
-	'autosigning with BTC, ETH and ETC'
+class TestSuiteAutosignBTC(TestSuiteAutosign):
+	'autosigning with BTC'
 	cmd_group = (
-		('autosign_minimal', 'transaction autosigning (BTC,ETH,ETC)'),
+		('autosign_btc', 'transaction autosigning (BTC only)'),
 	)
 
-class TestSuiteAutosignLive(TestSuiteAutosignMinimal):
+class TestSuiteAutosignLive(TestSuiteAutosignBTC):
 	'live autosigning operations with device insertion/removal and LED check'
 	cmd_group = (
 		('autosign_live', 'transaction autosigning (BTC,ETH,ETC - test device insertion/removal + LED)'),

+ 8 - 6
test/test_py_d/ts_misc.py

@@ -135,16 +135,18 @@ class TestSuiteInput(TestSuiteBase):
 
 	def password_entry_noecho(self):
 		if self.skip_for_win():
-			msg('Perform this test by hand on MSWin with non-ASCII password abc-α:')
-			msg('  test/misc/password_entry.py')
-			return 'skip' # getpass() can't handle utf8, and pexpect double-escapes utf8, so skip
+			m = "getpass() doesn't work with pexpect.popen_spawn!\n"
+			m += 'Perform the following test by hand with non-ASCII password abc-α:\n'
+			m += '  test/misc/password_entry.py'
+			return ('skip_warn',m)
 		return self.password_entry('Enter passphrase: ',[])
 
 	def password_entry_echo(self):
 		if self.skip_for_win():
-			msg('Perform this test by hand on MSWin with non-ASCII password abc-α:')
-			msg('  test/misc/password_entry.py --echo-passphrase')
-			return 'skip' # pexpect double-escapes utf8, so skip
+			m = "getpass() doesn't work with pexpect.popen_spawn!\n"
+			m += 'Perform the following test by hand with non-ASCII password abc-α:\n'
+			m += '  test/misc/password_entry.py --echo-passphrase'
+			return ('skip_warn',m)
 		return self.password_entry('Enter passphrase (echoed): ',['--echo-passphrase'])
 
 	def _user_seed_entry(self,fmt,usr_rand=False,out_fmt=None):

+ 2 - 2
test/test_py_d/ts_ref.py

@@ -231,12 +231,12 @@ class TestSuiteRef(TestSuiteBase,TestSuiteShared):
 
 	def ref_segwitaddrfile_chk(self):
 		if not 'S' in g.proto.mmtypes:
-			return skip('not supported')
+			return skip('not supported by {}'.format(g.proto.__name__))
 		return self.ref_addrfile_chk(ftype='segwitaddr',pat='{}.*Segwit'.format(nw_name))
 
 	def ref_bech32addrfile_chk(self):
 		if not 'B' in g.proto.mmtypes:
-			return skip('not supported')
+			return skip('not supported by {}'.format(g.proto.__name__))
 		return self.ref_addrfile_chk(ftype='bech32addr',pat='{}.*Bech32'.format(nw_name))
 
 	def ref_keyaddrfile_chk(self):

+ 7 - 6
test/test_py_d/ts_ref_3seed.py

@@ -150,10 +150,11 @@ class TestSuiteRef3Seed(TestSuiteBase,TestSuiteShared):
 		fn = os.path.split(t.written_to_file(capfirst(ocls.desc)))[-1]
 		import re
 		idx = int(self.test_name[-1]) - 1
-		pat = r'{}-[0-9A-F]{{8}}\[{},1\].mmdat'.format(
+		pat = r'{}-[0-9A-F]{{8}}\[{},1\]{}.mmdat'.format(
 			self.chk_data['sids'][idx],
-			self.chk_data['lens'][idx] )
-		assert re.match(pat,fn)
+			self.chk_data['lens'][idx],
+			'-α' if g.debug_utf8 else '')
+		assert re.match(pat,fn),'{} != {}'.format(pat,fn)
 		sid = os.path.basename(fn.split('-')[0])
 		cmp_or_die(sid,self.seed_id,desc='Seed ID')
 		return t
@@ -170,9 +171,9 @@ class TestSuiteRef3Seed(TestSuiteBase,TestSuiteShared):
 		if re_pat:
 			import re
 			pat = re_pat.format(sid,slen)
-			assert re.match(pat,fn),'{} {}'.format(pat,fn)
+			assert re.match(pat,fn),'{} != {}'.format(pat,fn)
 		else:
-			cmp_or_die('{}[{}].{}'.format(sid,slen,wcls.ext),fn)
+			cmp_or_die('{}[{}]{}.{}'.format(sid,slen,'-α' if g.debug_utf8 else '',wcls.ext),fn)
 		return t
 
 	def ref_walletconv_words(self):        return self.ref_walletconv(ofmt='mn')
@@ -184,7 +185,7 @@ class TestSuiteRef3Seed(TestSuiteBase,TestSuiteShared):
 
 	def ref_walletconv_incog(self,ofmt='incog',ext='mmincog'):
 		args = ['-r0','-p1']
-		pat = r'{}-[0-9A-F]{{8}}-[0-9A-F]{{8}}\[{},1\].' + ext
+		pat = r'{}-[0-9A-F]{{8}}-[0-9A-F]{{8}}\[{},1\]' + ('-α' if g.debug_utf8 else '') + '.' + ext
 		return self.ref_walletconv(ofmt=ofmt,extra_args=args,re_pat=pat)
 
 	def ref_walletconv_xincog(self):

+ 4 - 2
test/test_py_d/ts_regtest.py

@@ -630,9 +630,11 @@ class TestSuiteRegtest(TestSuiteBase,TestSuiteShared):
 		return t
 
 	def _get_mempool(self):
-		disable_debug()
+		if not g.debug_utf8:
+			disable_debug()
 		ret = self.spawn('mmgen-regtest',['mempool']).read()
-		restore_debug()
+		if not g.debug_utf8:
+			restore_debug()
 		m = re.search(r'(\[\s*"[a-f0-9]{64}"\s*])',ret) # allow for extra output by handler at end
 		return json.loads(m.group(1))