Browse Source

globalvars.py: new GlobalConstants (gc), GlobalVars (gv) classes

- The attributes of GlobalConstants are non-configurable.  They’re constant
  for a given machine, user, executable and MMGen release.

- The attributes of GlobalVars, `stderr` and `stdout`, are used by the test
  suite to redirect msg() and friends to /dev/null.

- All the aforementioned attributes formerly belonged to GlobalConfig.
The MMGen Project 1 year ago
parent
commit
c3ce1ab8e3
70 changed files with 309 additions and 282 deletions
  1. 3 3
      mmgen/addrdata.py
  2. 2 2
      mmgen/addrfile.py
  3. 4 4
      mmgen/altcoin.py
  4. 2 2
      mmgen/cfgfile.py
  5. 2 2
      mmgen/color.py
  6. 4 4
      mmgen/crypto.py
  7. 3 3
      mmgen/daemon.py
  8. 2 2
      mmgen/filename.py
  9. 3 3
      mmgen/fileutil.py
  10. 76 61
      mmgen/globalvars.py
  11. 9 9
      mmgen/help.py
  12. 2 2
      mmgen/main.py
  13. 6 5
      mmgen/main_addrgen.py
  14. 5 5
      mmgen/main_addrimport.py
  15. 11 9
      mmgen/main_passgen.py
  16. 2 2
      mmgen/main_regtest.py
  17. 3 2
      mmgen/main_seedjoin.py
  18. 3 3
      mmgen/main_split.py
  19. 7 7
      mmgen/main_tool.py
  20. 7 7
      mmgen/main_txbump.py
  21. 2 2
      mmgen/main_txcreate.py
  22. 4 4
      mmgen/main_txdo.py
  23. 2 2
      mmgen/main_txsend.py
  24. 6 6
      mmgen/main_txsign.py
  25. 6 5
      mmgen/main_wallet.py
  26. 2 1
      mmgen/main_xmrwallet.py
  27. 2 2
      mmgen/msg.py
  28. 10 10
      mmgen/opts.py
  29. 4 4
      mmgen/proto/btc/daemon.py
  30. 4 4
      mmgen/proto/eth/daemon.py
  31. 2 2
      mmgen/proto/xmr/daemon.py
  32. 6 6
      mmgen/protocol.py
  33. 2 2
      mmgen/rpc.py
  34. 4 2
      mmgen/term.py
  35. 2 2
      mmgen/tool/fileutil.py
  36. 2 2
      mmgen/tool/help.py
  37. 2 2
      mmgen/tool/util.py
  38. 2 1
      mmgen/tw/unspent.py
  39. 2 1
      mmgen/tw/view.py
  40. 3 3
      mmgen/tx/base.py
  41. 2 2
      mmgen/tx/info.py
  42. 4 3
      mmgen/tx/new.py
  43. 4 4
      mmgen/tx/sign.py
  44. 2 2
      mmgen/ui.py
  45. 13 13
      mmgen/util.py
  46. 2 2
      mmgen/wallet/enc.py
  47. 3 3
      mmgen/wallet/incog_hidden.py
  48. 2 2
      scripts/uninstall-mmgen.py
  49. 2 2
      test/gentest.py
  50. 2 2
      test/include/coin_daemon_control.py
  51. 4 4
      test/include/common.py
  52. 1 1
      test/misc/get_passphrase.py
  53. 5 5
      test/misc/term.py
  54. 1 1
      test/scrambletest.py
  55. 6 6
      test/test.py
  56. 4 3
      test/test_py_d/common.py
  57. 3 3
      test/test_py_d/ts_autosign.py
  58. 2 2
      test/test_py_d/ts_base.py
  59. 1 1
      test/test_py_d/ts_cfgfile.py
  60. 2 2
      test/test_py_d/ts_ethdev.py
  61. 4 4
      test/test_py_d/ts_input.py
  62. 2 2
      test/test_py_d/ts_misc.py
  63. 1 1
      test/test_py_d/ts_tool.py
  64. 2 2
      test/test_py_d/ts_xmrwallet.py
  65. 1 1
      test/tooltest.py
  66. 2 2
      test/tooltest2.py
  67. 1 1
      test/unit_tests.py
  68. 4 4
      test/unit_tests_d/__init__.py
  69. 1 1
      test/unit_tests_d/ut_rpc.py
  70. 1 1
      test/unit_tests_d/ut_testdep.py

+ 3 - 3
mmgen/addrdata.py

@@ -74,7 +74,7 @@ class TwAddrData(AddrData,metaclass=AsyncInit):
 	async def __init__(self,proto,twctl=None):
 		from .rpc import rpc_init
 		from .tw.shared import TwLabel
-		from .globalvars import g
+		from .globalvars import gc
 		from .seed import SeedID
 		self.proto = proto
 		self.rpc = await rpc_init(proto)
@@ -86,7 +86,7 @@ class TwAddrData(AddrData,metaclass=AsyncInit):
 			if l and l.mmid.type == 'mmgen':
 				obj = l.mmid.obj
 				if len(addr_array) != 1:
-					message = self.msgs['multiple_acct_addrs'].strip().format( acct=acct, proj=g.proj_name )
+					message = self.msgs['multiple_acct_addrs'].strip().format( acct=acct, proj=gc.proj_name )
 					die(3, fmt( message, indent='  ' ))
 				al_id = AddrListID(
 					sid = SeedID(sid=obj.sid),
@@ -96,7 +96,7 @@ class TwAddrData(AddrData,metaclass=AsyncInit):
 				out[al_id].append(AddrListEntry(self.proto,idx=obj.idx,addr=addr_array[0],comment=l.comment))
 				i += 1
 
-		vmsg(f'{i} {g.proj_name} addresses found, {len(twd)} accounts total')
+		vmsg(f'{i} {gc.proj_name} addresses found, {len(twd)} accounts total')
 
 		for al_id in out:
 			self.add(AddrList(

+ 2 - 2
mmgen/addrfile.py

@@ -87,8 +87,8 @@ class AddrFile(MMGenObject):
 			self.file_header_mn.format(p.pw_fmt.upper())
 				if p.gen_passwds and p.pw_fmt in ('bip39','xmrseed') else
 			self.file_header ).strip()
-		from .globalvars import g
-		out = [fh.format(pnm=g.proj_name,n=TwComment.max_screen_width) + '\n']
+		from .globalvars import gc
+		out = [fh.format(pnm=gc.proj_name,n=TwComment.max_screen_width) + '\n']
 
 		if p.chksum:
 			out.append(f'# {capfirst(p.desc)} data checksum for {p.id_str}: {p.chksum}')

+ 4 - 4
mmgen/altcoin.py

@@ -36,6 +36,8 @@ altcoin.py - Coin constants for Bitcoin-derived altcoins
 #   NBT:  150/191 c/u,  25/('B'),  26/('B')
 
 import sys
+
+from .globalvars import gc
 from .util import msg
 
 def test_equal(desc,a,b,*cdata):
@@ -434,10 +436,9 @@ class CoinInfo(object):
 	@classmethod
 	def verify_core_coin_data(cls,quiet=False,verbose=False):
 		from .protocol import CoinProtocol,init_proto
-		from .globalvars import g
 
 		for network in ('mainnet','testnet'):
-			for coin in g.core_coins:
+			for coin in gc.core_coins:
 				e = cls.get_entry(coin,network)
 				if e:
 					proto = init_proto(coin,testnet=network=='testnet')
@@ -718,8 +719,7 @@ def init_genonly_altcoins(usr_coin=None,testnet=False):
 			data[network] = CoinInfo.get_supported_coins(network)
 		trust_level = 0
 	else:
-		from .globalvars import g
-		if usr_coin.lower() in g.core_coins: # core coin, so return immediately
+		if usr_coin.lower() in gc.core_coins: # core coin, so return immediately
 			from .protocol import CoinProtocol
 			return CoinProtocol.coins[usr_coin.lower()].trust_level
 		for network in networks:

+ 2 - 2
mmgen/cfgfile.py

@@ -23,7 +23,7 @@ cfgfile: API for the MMGen runtime configuration file and related files
 import os,re
 from collections import namedtuple
 
-from .globalvars import g
+from .globalvars import g,gc
 from .util import msg,ymsg,suf,fmt,fmt_list,oneshot_warning,strip_comment,capfirst
 
 def mmgen_cfg_file(id_str):
@@ -187,7 +187,7 @@ class CfgFileSampleSys(cfg_file_sample):
 		else:
 			# self.fn is used for error msgs only, so file need not exist on filesystem
 			self.fn = os.path.join(os.path.dirname(__file__),'data',self.fn_base)
-			self.data = g.get_mmgen_data_file(self.fn_base).splitlines()
+			self.data = gc.get_mmgen_data_file(self.fn_base).splitlines()
 
 	def make_metadata(self):
 		return [f'# Version {self.cur_ver} {self.computed_chksum}']

+ 2 - 2
mmgen/color.py

@@ -47,8 +47,8 @@ def nocolor(s):
 
 def set_vt100():
 	'hack to put term into VT100 mode under MSWin'
-	from .globalvars import g
-	if g.platform == 'win':
+	from .globalvars import gc
+	if gc.platform == 'win':
 		from subprocess import run
 		run([],shell=True)
 

+ 4 - 4
mmgen/crypto.py

@@ -23,7 +23,7 @@ crypto: Random number, password hashing and symmetric encryption routines for th
 import os
 from collections import namedtuple
 
-from .globalvars import g
+from .globalvars import g,gc
 from .opts import opt
 from .util import (
 	msg,
@@ -244,7 +244,7 @@ class Crypto:
 
 		avg_prec = sum(len(t.split('.')[1]) for t in time_data) // len(time_data)
 
-		if avg_prec < g.min_time_precision:
+		if avg_prec < gc.min_time_precision:
 			ymsg(f'WARNING: Avg. time precision of only {avg_prec} decimal points. User entropy quality is degraded!')
 
 		ret = key_data + '\n' + '\n'.join(time_data)
@@ -300,7 +300,7 @@ class Crypto:
 
 	def get_hash_preset_from_user(
 			self,
-			old_preset = g.dfl_hash_preset,
+			old_preset = gc.dfl_hash_preset,
 			data_desc  = 'data',
 			prompt     = None ):
 
@@ -338,7 +338,7 @@ class Crypto:
 			if opt.echo_passphrase:
 				pw = ' '.join(get_words_from_user( f'Enter {pw_desc} for {data_desc}: ' ))
 			else:
-				for i in range(g.passwd_max_tries):
+				for i in range(gc.passwd_max_tries):
 					pw = ' '.join(get_words_from_user( f'Enter {pw_desc} for {data_desc}: ' ))
 					pw_chk = ' '.join(get_words_from_user( f'Repeat {pw_desc}: ' ))
 					dmsg(f'Passphrases: [{pw}] [{pw_chk}]')

+ 3 - 3
mmgen/daemon.py

@@ -24,7 +24,7 @@ import os,time,importlib
 from subprocess import run,PIPE,CompletedProcess
 from collections import namedtuple
 
-from .globalvars import g
+from .globalvars import g,gc
 from .color import set_vt100
 from .util import msg,Msg_r,ymsg,die,remove_dups,oneshot_warning
 from .flags import *
@@ -51,7 +51,7 @@ class Daemon(Lockable):
 
 	def __init__(self,opts=None,flags=None):
 
-		self.platform = g.platform
+		self.platform = gc.platform
 		if self.platform == 'win':
 			self.use_pidfile = False
 			self.use_threads = True
@@ -478,7 +478,7 @@ class CoinDaemon(Daemon):
 		assert self.test_suite, 'datadir removal restricted to test suite'
 		if self.state == 'stopped':
 			run([
-				('rm' if g.platform == 'win' else '/bin/rm'),
+				('rm' if gc.platform == 'win' else '/bin/rm'),
 				'-rf',
 				self.datadir ])
 			set_vt100()

+ 2 - 2
mmgen/filename.py

@@ -43,8 +43,8 @@ class File:
 		import stat
 		if stat.S_ISBLK(st.st_mode):
 			mode = (os.O_RDONLY,os.O_RDWR)[bool(write)]
-			from .globalvars import g
-			if g.platform == 'win':
+			from .globalvars import gc
+			if gc.platform == 'win':
 				mode |= os.O_BINARY
 			try:
 				fd = os.open(fn, mode)

+ 3 - 3
mmgen/fileutil.py

@@ -22,7 +22,7 @@ fileutil: Routines that read, write, execute or stat files
 
 import sys,os
 
-from .globalvars import g
+from .globalvars import g,gc
 from .color import set_vt100
 from .util import (
 	msg,
@@ -43,7 +43,7 @@ def check_or_create_dir(path):
 		if os.getenv('MMGEN_TEST_SUITE'):
 			from subprocess import run
 			run([
-				('rm' if g.platform == 'win' else '/bin/rm'),
+				('rm' if gc.platform == 'win' else '/bin/rm'),
 				'-rf',
 				path ])
 			set_vt100()
@@ -205,7 +205,7 @@ def write_data_to_file(
 			else:
 				msg('Redirecting output to file')
 
-		if binary and g.platform == 'win':
+		if binary and gc.platform == 'win':
 			import msvcrt
 			msvcrt.setmode(sys.stdout.fileno(),os.O_BINARY)
 

+ 76 - 61
mmgen/globalvars.py

@@ -29,6 +29,79 @@ def die(exit_val,s=''):
 		sys.stderr.write(s+'\n')
 	sys.exit(exit_val)
 
+class GlobalConstants(Lockable):
+	"""
+	These values are non-configurable.  They’re constant for a given machine,
+	user, executable and MMGen release.
+	"""
+	proj_name          = 'MMGen'
+	proj_url           = 'https://github.com/mmgen/mmgen'
+	author             = 'The MMGen Project'
+	email              = '<mmgen@tuta.io>'
+	Cdates             = '2013-2023'
+	dfl_hash_preset    = '3'
+	passwd_max_tries   = 5
+	min_screen_width   = 80
+	min_time_precision = 18
+
+	# must match CoinProtocol.coins
+	core_coins = ('btc','bch','ltc','eth','etc','zec','xmr')
+
+	prog_name = os.path.basename(sys.argv[0])
+	is_txprog = prog_name == 'mmgen-regtest' or prog_name.startswith('mmgen-tx')
+
+	for k in ('linux','win','msys'):
+		if sys.platform.startswith(k):
+			platform = { 'linux':'linux', 'win':'win', 'msys':'win' }[k]
+			break
+	else:
+		die(1,f'{sys.platform!r}: platform not supported by {proj_name}')
+
+	if os.getenv('HOME'):   # Linux or MSYS2
+		home_dir = os.getenv('HOME')
+	elif platform == 'win': # Windows without MSYS2 - not supported
+		die(1,f'$HOME not set!  {proj_name} for Windows must be run in MSYS2 environment')
+	else:
+		die(2,'$HOME is not set!  Unable to determine home directory')
+
+	def get_mmgen_data_file(self,filename,package='mmgen'):
+		"""
+		this is an expensive import, so do only when required
+		"""
+		# Resource will be unpacked and then cleaned up if necessary, see:
+		#    https://docs.python.org/3/library/importlib.html:
+		#        Note: This module provides functionality similar to pkg_resources Basic
+		#        Resource Access without the performance overhead of that package.
+		#    https://importlib-resources.readthedocs.io/en/latest/migration.html
+		#    https://setuptools.readthedocs.io/en/latest/pkg_resources.html
+		try:
+			from importlib.resources import files # Python 3.9
+		except ImportError:
+			from importlib_resources import files
+		return files(package).joinpath('data',filename).read_text()
+
+	@property
+	def version(self):
+		return self.get_mmgen_data_file(
+				filename = 'version',
+				package  = 'mmgen_node_tools' if self.prog_name.startswith('mmnode-') else 'mmgen'
+			).strip()
+
+	@property
+	def release_date(self):
+		return self.get_mmgen_data_file(filename='release_date').strip()
+
+gc = GlobalConstants()
+
+class GlobalVars:
+	"""
+	These are used only by the test suite to redirect msg() and friends to /dev/null
+	"""
+	stdout = sys.stdout
+	stderr = sys.stderr
+
+gv = GlobalVars()
+
 class GlobalConfig(Lockable):
 	"""
 	Set global vars to default values
@@ -44,15 +117,6 @@ class GlobalConfig(Lockable):
 
 	# Constants:
 
-	proj_name = 'MMGen'
-	proj_url  = 'https://github.com/mmgen/mmgen'
-	prog_name = os.path.basename(sys.argv[0])
-	author    = 'The MMGen Project'
-	email     = '<mmgen@tuta.io>'
-	Cdates    = '2013-2023'
-
-	is_txprog = prog_name == 'mmgen-regtest' or prog_name.startswith('mmgen-tx')
-
 	stdin_tty = sys.stdin.isatty()
 	stdout = sys.stdout
 	stderr = sys.stderr
@@ -60,11 +124,9 @@ class GlobalConfig(Lockable):
 	http_timeout = 60
 	err_disp_timeout = 0.7
 	short_disp_timeout = 0.3
-	min_time_precision = 18
 
 	# Variables - these might be altered at runtime:
 
-	dfl_hash_preset = '3'
 	usr_randchars   = 30
 
 	fee_adjust = 1.0
@@ -134,27 +196,10 @@ class GlobalConfig(Lockable):
 		os.getenv('MMGEN_FORCE_COLOR')
 	)
 
-	for k in ('linux','win','msys'):
-		if sys.platform.startswith(k):
-			platform = { 'linux':'linux', 'win':'win', 'msys':'win' }[k]
-			break
-	else:
-		die(1,f'{sys.platform!r}: platform not supported by {proj_name}')
-
-	if os.getenv('HOME'):   # Linux or MSYS2
-		home_dir = os.getenv('HOME')
-	elif platform == 'win': # Windows without MSYS2 - not supported
-		die(1,f'$HOME not set!  {proj_name} for Windows must be run in MSYS2 environment')
-	else:
-		die(2,'$HOME is not set!  Unable to determine home directory')
-
 	daemon_data_dir = '' # set by user
 	daemon_id = ''
 	blacklisted_daemons = ''
 
-	# must match CoinProtocol.coins
-	core_coins = ('btc','bch','ltc','eth','etc','zec','xmr')
-
 	# global var sets user opt:
 	global_sets_opt = (
 		'autochg_ignore_labels',
@@ -297,7 +342,7 @@ class GlobalConfig(Lockable):
 		'fee_estimate_mode': _ov('nocase_pfx', ['conservative','economical']),
 		'rpc_backend':       _ov('nocase_pfx', ['auto','httplib','curl','aiohttp','requests']),
 	}
-	if platform == 'win':
+	if gc.platform == 'win':
 		_skip_type_check = ('stdout','stderr')
 
 	auto_typeset_opts = {
@@ -306,13 +351,10 @@ class GlobalConfig(Lockable):
 		'vsize_adj': float,
 	}
 
-	min_screen_width = 80
 	minconf = 1
 	max_tx_file_size = 100000
 	max_input_size   = 1024 * 1024
 
-	passwd_max_tries = 5
-
 	max_urandchars = 80
 	min_urandchars = 10
 
@@ -324,7 +366,7 @@ class GlobalConfig(Lockable):
 		short_disp_timeout = 0.1
 		if os.getenv('MMGEN_TEST_SUITE_POPEN_SPAWN'):
 			stdin_tty = True
-		if prog_name == 'unit_tests.py':
+		if gc.prog_name == 'unit_tests.py':
 			_set_ok += ('debug_subseed',)
 			_reset_ok += ('force_standalone_scrypt_module',)
 
@@ -333,33 +375,6 @@ class GlobalConfig(Lockable):
 			if name[:11] == 'MMGEN_DEBUG':
 				os.environ[name] = '1'
 
-	def get_mmgen_data_file(self,filename,package='mmgen'):
-		"""
-		this is an expensive import, so do only when required
-		"""
-		# Resource will be unpacked and then cleaned up if necessary, see:
-		#    https://docs.python.org/3/library/importlib.html:
-		#        Note: This module provides functionality similar to pkg_resources Basic
-		#        Resource Access without the performance overhead of that package.
-		#    https://importlib-resources.readthedocs.io/en/latest/migration.html
-		#    https://setuptools.readthedocs.io/en/latest/pkg_resources.html
-		try:
-			from importlib.resources import files # Python 3.9
-		except ImportError:
-			from importlib_resources import files
-		return files(package).joinpath('data',filename).read_text()
-
-	@property
-	def version(self):
-		return self.get_mmgen_data_file(
-				filename = 'version',
-				package  = 'mmgen_node_tools' if self.prog_name.startswith('mmnode-') else 'mmgen'
-			).strip()
-
-	@property
-	def release_date(self):
-		return self.get_mmgen_data_file(filename='release_date').strip()
-
 	@property
 	def data_dir_root(self):
 		"""
@@ -374,7 +389,7 @@ class GlobalConfig(Lockable):
 				from test.include.common import get_test_data_dir
 				self._data_dir_root = get_test_data_dir()
 			else:
-				self._data_dir_root = os.path.join(self.home_dir,'.'+self.proj_name.lower())
+				self._data_dir_root = os.path.join(gc.home_dir,'.'+gc.proj_name.lower())
 			return self._data_dir_root
 
 	@property

+ 9 - 9
mmgen/help.py

@@ -20,7 +20,7 @@
 help: help notes for MMGen suite commands
 """
 
-from .globalvars import g
+from .globalvars import gc
 
 def help_notes_func(proto,opt,k):
 
@@ -156,30 +156,30 @@ EXAMPLES:
   Send 0.123 {proto.coin} to an external {proto.name} address, returning the change to a
   specific MMGen address in the tracking wallet:
 
-    $ {g.prog_name} {sample_addr},0.123 01ABCDEF:{mmtype}:7
+    $ {gc.prog_name} {sample_addr},0.123 01ABCDEF:{mmtype}:7
 
   Same as above, but select the change address automatically:
 
-    $ {g.prog_name} {sample_addr},0.123 01ABCDEF:{mmtype}
+    $ {gc.prog_name} {sample_addr},0.123 01ABCDEF:{mmtype}
 
   Same as above, but select the change address automatically by address type:
 
-    $ {g.prog_name} {sample_addr},0.123 {mmtype}
+    $ {gc.prog_name} {sample_addr},0.123 {mmtype}
 
   Same as above, but reduce verbosity and specify fee of 20 satoshis
   per byte:
 
-    $ {g.prog_name} -q -f 20s {sample_addr},0.123 {mmtype}
+    $ {gc.prog_name} -q -f 20s {sample_addr},0.123 {mmtype}
 
   Send entire balance of selected inputs minus fee to an external {proto.name}
   address:
 
-    $ {g.prog_name} {sample_addr}
+    $ {gc.prog_name} {sample_addr}
 
   Send entire balance of selected inputs minus fee to first unused wallet
   address of specified type:
 
-    $ {g.prog_name} {mmtype}
+    $ {gc.prog_name} {mmtype}
 """
 
 		def txcreate():
@@ -235,9 +235,9 @@ must also be supplied on the command line if the data can't be found in the
 default wallet.
 """.format(
 	wd  = (f'{coind_exec()} wallet dump or ' if isinstance(proto,mainnet) else ''),
-	pnm = g.proj_name,
+	pnm = gc.proj_name,
 	pnu = proto.name,
-	pnl = g.proj_name.lower() )
+	pnl = gc.proj_name.lower() )
 
 		def seedsplit():
 			from .seedsplit import SeedShareIdx,SeedShareCount,MasterShareIdx

+ 2 - 2
mmgen/main.py

@@ -29,9 +29,9 @@ def launch(mod,package='mmgen'):
 		mod = 'addrgen'
 
 	import sys,os
-	from .globalvars import g
+	from .globalvars import gc
 
-	if g.platform == 'linux' and sys.stdin.isatty():
+	if gc.platform == 'linux' and sys.stdin.isatty():
 		import termios,atexit
 		fd = sys.stdin.fileno()
 		old = termios.tcgetattr(fd)

+ 6 - 5
mmgen/main_addrgen.py

@@ -22,7 +22,7 @@ mmgen-addrgen: Generate a series or range of addresses from an MMGen
 """
 
 import mmgen.opts as opts
-from .globalvars import g
+from .globalvars import g,gc
 from .opts import opt
 from .addr import MMGenAddrType
 from .addrfile import AddrFile
@@ -30,7 +30,7 @@ from .wallet import Wallet
 
 import mmgen.addrlist
 
-if g.prog_name == 'mmgen-keygen':
+if gc.prog_name == 'mmgen-keygen':
 	gen_what = 'keys'
 	gen_clsname = 'KeyAddrList'
 	gen_desc = 'secret keys'
@@ -49,7 +49,7 @@ opts_data = {
 		'desc': """
                  Generate a range or list of {desc} from an {pnm} wallet,
                  mnemonic, seed or brainwallet
-			  """.format(desc=gen_desc,pnm=g.proj_name),
+			  """.format(desc=gen_desc,pnm=gc.proj_name),
 		'usage':'[opts] [seed source] <index list or range(s)>',
 		'options': """
 -h, --help            Print this help message
@@ -69,7 +69,7 @@ opts_data = {
                       is required only for brainwallet and incognito inputs
                       with non-standard (< {dsl}-bit) seed lengths.
 -p, --hash-preset= p  Use the scrypt hash parameters defined by preset 'p'
-                      for password hashing (default: '{g.dfl_hash_preset}')
+                      for password hashing (default: '{gc.dfl_hash_preset}')
 -z, --show-hash-presets Show information on available hash presets
 -P, --passwd-file= f  Get wallet passphrase from file 'f'
 -q, --quiet           Produce quieter output; suppress some warnings
@@ -111,9 +111,10 @@ FMT CODES:
 			kgs=help_notes('keygen_backends'),
 			coin_id=help_notes('coin_id'),
 			dsl=help_notes('dfl_seed_len'),
-			pnm=g.proj_name,
+			pnm=gc.proj_name,
 			what=gen_what,
 			g=g,
+			gc=gc,
 		),
 		'notes': lambda help_notes,s: s.format(
 			n_addrkey=note_addrkey,

+ 5 - 5
mmgen/main_addrimport.py

@@ -23,7 +23,7 @@ mmgen-addrimport: Import addresses into a MMGen coin daemon tracking wallet
 from collections import namedtuple
 
 import mmgen.opts as opts
-from .globalvars import g
+from .globalvars import gc
 from .opts import opt
 from .util import msg,qmsg,suf,die,fmt,async_run
 from .addrlist import AddrList,KeyAddrList
@@ -31,7 +31,7 @@ from .tw.shared import TwLabel
 
 opts_data = {
 	'text': {
-		'desc': f'Import addresses into an {g.proj_name} tracking wallet',
+		'desc': f'Import addresses into an {gc.proj_name} tracking wallet',
 		'usage':'[opts] [MMGen address file]',
 		'options': """
 -h, --help         Print this help message
@@ -78,8 +78,8 @@ addrimport_msgs = {
 		by the number of addresses imported and typically takes just a few minutes.
 	""",
 	'bad_args': f"""
-		You must specify either an {g.proj_name} address file, a single address with
-		the ‘--address’ option, or a flat list of non-{g.proj_name} addresses with
+		You must specify either an {gc.proj_name} address file, a single address with
+		the ‘--address’ option, or a flat list of non-{gc.proj_name} addresses with
 		the ‘--addrlist’ option.
 	"""
 }
@@ -102,7 +102,7 @@ def parse_cmd_args(rpc,cmd_args):
 				proto = proto,
 				addrlist = get_lines_from_file(
 					infile,
-					f'non-{g.proj_name} addresses',
+					f'non-{gc.proj_name} addresses',
 					trim_comments = True ) )
 		else:
 			al = import_mmgen_list(infile)

+ 11 - 9
mmgen/main_passgen.py

@@ -22,7 +22,7 @@ mmgen-passgen: Generate a series or range of passwords from an MMGen
 """
 
 import mmgen.opts as opts
-from .globalvars import g
+from .globalvars import g,gc
 from .opts import opt
 from .addrlist import AddrIdxList
 from .passwdlist import PasswordList
@@ -35,7 +35,7 @@ opts_data = {
 	'sets': [('print_checksum',True,'quiet',True)],
 	'text': {
 		'desc': f"""
-                 Generate a range or list of passwords from an {g.proj_name} wallet,
+                 Generate a range or list of passwords from an {gc.proj_name} wallet,
                  mnemonic, seed or brainwallet for the given ID string
 		 """,
 		'usage':'[opts] [seed source] <ID string> <index list or range(s)>',
@@ -57,7 +57,7 @@ opts_data = {
                       is required only for brainwallet and incognito inputs
                       with non-standard (< {dsl}-bit) seed lengths.
 -p, --hash-preset= p  Use the scrypt hash parameters defined by preset 'p'
-                      for password hashing (default: '{g.dfl_hash_preset}')
+                      for password hashing (default: '{gc.dfl_hash_preset}')
 -z, --show-hash-presets Show information on available hash presets
 -P, --passwd-file= f  Get wallet passphrase from file 'f'
 -q, --quiet           Produce quieter output; suppress some warnings
@@ -86,17 +86,17 @@ PASSWORD FORMATS:
 EXAMPLES:
 
   Generate ten base58 passwords of length {i58.dfl_len} for Alice's email account:
-  {g.prog_name} alice@nowhere.com 1-10
+  {gc.prog_name} alice@nowhere.com 1-10
 
   Generate ten base58 passwords of length 16 for Alice's email account:
-  {g.prog_name} --passwd-len=16 alice@nowhere.com 1-10
+  {gc.prog_name} --passwd-len=16 alice@nowhere.com 1-10
 
   Generate ten base32 passwords of length {i32.dfl_len} for Alice's email account:
-  {g.prog_name} --passwd-fmt=b32 alice@nowhere.com 1-10
+  {gc.prog_name} --passwd-fmt=b32 alice@nowhere.com 1-10
 
   Generate three BIP39 mnemonic seed phrases of length {i39.dfl_len} for Alice's
   Trezor device:
-  {g.prog_name} --passwd-fmt=bip39 mytrezor 1-3
+  {gc.prog_name} --passwd-fmt=bip39 mytrezor 1-3
 
   All passwords are cryptographically unlinkable with each other, including
   passwords with the same format but different length, so Alice needn't worry
@@ -116,9 +116,10 @@ FMT CODES:
 	},
 	'code': {
 		'options': lambda help_notes,s: s.format(
-			g=g,pnm=g.proj_name,
+			g=g,pnm=gc.proj_name,
 			dsl=help_notes('dfl_seed_len'),
 			dpf=PasswordList.dfl_pw_fmt,
+			gc=gc,
 		),
 		'notes': lambda help_notes,s: s.format(
 				o=opts,g=g,i58=pwi['b58'],i32=pwi['b32'],i39=pwi['bip39'],
@@ -128,6 +129,7 @@ FMT CODES:
 				n_bw=help_notes('brainwallet'),
 				pfi=help_notes('password_formats'),
 				n_fmt=help_notes('fmt_codes'),
+				gc=gc,
 		)
 	}
 }
@@ -178,7 +180,7 @@ if keypress_confirm('Encrypt password list?'):
 	af.encrypt()
 	af.write(binary=True,desc='encrypted password list')
 else:
-	if g.test_suite_popen_spawn and g.platform == 'win':
+	if g.test_suite_popen_spawn and gc.platform == 'win':
 		import time
 		time.sleep(0.1)
 	af.write(desc='password list')

+ 2 - 2
mmgen/main_regtest.py

@@ -22,13 +22,13 @@ mmgen-regtest: Coin daemon regression test mode setup and operations for the MMG
 """
 
 import mmgen.opts as opts
-from .globalvars import g
+from .globalvars import g,gc
 from .util import die,async_run
 
 opts_data = {
 	'sets': [('yes', True, 'quiet', True)],
 	'text': {
-		'desc': f'Coin daemon regression test mode setup and operations for the {g.proj_name} suite',
+		'desc': f'Coin daemon regression test mode setup and operations for the {gc.proj_name} suite',
 		'usage':   '[opts] <command>',
 		'options': """
 -h, --help          Print this help message

+ 3 - 2
mmgen/main_seedjoin.py

@@ -22,7 +22,7 @@ mmgen-seedjoin: Regenerate an MMGen deterministic wallet from seed shares
 """
 
 import mmgen.opts as opts
-from .globalvars import g
+from .globalvars import g,gc
 from .opts import opt
 from .util import msg,msg_r,qmsg,die
 from .color import yellow
@@ -53,7 +53,7 @@ opts_data = {
 -L, --label=       l  Specify a label 'l' for output wallet
 -M, --master-share=i  Use a master share with index 'i' (min:{ms_min}, max:{ms_max})
 -p, --hash-preset= p  Use the scrypt hash parameters defined by preset 'p'
-                      for password hashing (default: '{g.dfl_hash_preset}')
+                      for password hashing (default: '{gc.dfl_hash_preset}')
 -z, --show-hash-presets Show information on available hash presets
 -P, --passwd-file= f  Get wallet passphrase from file 'f'
 -q, --quiet           Produce quieter output; suppress some warnings
@@ -86,6 +86,7 @@ FMT CODES:
 			ms_min=MasterShareIdx.min_val,
 			ms_max=MasterShareIdx.max_val,
 			g=g,
+			gc=gc,
 		),
 		'notes': lambda help_notes,s: s.format(
 			f=help_notes('fmt_codes'),

+ 3 - 3
mmgen/main_split.py

@@ -30,7 +30,7 @@ from .common import *
 opts_data = {
 	'text': {
 		'desc': f"""
-               Split funds in an {g.proj_name} wallet after a chain fork using a
+               Split funds in an {gc.proj_name} wallet after a chain fork using a
                timelocked transaction
 		 """,
 		'usage':'[opts] [output addr1] [output addr2]',
@@ -54,7 +54,7 @@ opts_data = {
 	'notes': f"""\n
 This command creates two transactions: one (with the timelock) to be broadcast
 on the long chain and one on the short chain after a replayable chain fork.
-Only {g.proj_name} addresses may be spent to.
+Only {gc.proj_name} addresses may be spent to.
 
 The command must be run on the longest chain.  The user is reponsible for
 ensuring that the current chain is the longest.  The other chain is specified
@@ -99,7 +99,7 @@ if opt.other_coin.lower() not in [e[2] for e in proto.forks if e[3] == True]:
 	die(1,f'{opt.other_coin!r}: not a replayable fork of {proto.coin} chain')
 
 if len(cmd_args) != 2:
-	die(1,f'This command requires exactly two {g.proj_name} addresses as arguments')
+	die(1,f'This command requires exactly two {gc.proj_name} addresses as arguments')
 
 from .addr import MMGenID
 try:

+ 7 - 7
mmgen/main_tool.py

@@ -23,13 +23,13 @@ mmgen-tool:  Perform various MMGen- and cryptocoin-related operations.
 
 import sys,os,importlib
 import mmgen.opts as opts
-from .globalvars import g
+from .globalvars import g,gc
 from .opts import opt
 from .util import msg,Msg,die,capfirst,suf,async_run
 
 opts_data = {
 	'text': {
-		'desc':    f'Perform various {g.proj_name}- and cryptocoin-related operations',
+		'desc':    f'Perform various {gc.proj_name}- and cryptocoin-related operations',
 		'usage':   '[opts] <command> <command args>',
 		'options': """
 -d, --outdir=       d  Specify an alternate directory 'd' for output
@@ -41,7 +41,7 @@ opts_data = {
                        for {coin_id}: {kgs}
 -l, --list             List available commands
 -p, --hash-preset= p   Use the scrypt hash parameters defined by preset 'p'
-                       for password hashing (default: '{g.dfl_hash_preset}')
+                       for password hashing (default: '{gc.dfl_hash_preset}')
 -P, --passwd-file= f   Get passphrase from file 'f'.
 -q, --quiet            Produce quieter output
 -r, --usr-randchars=n  Get 'n' characters of additional randomness from
@@ -65,10 +65,11 @@ Type ‘{pn} help <command>’ for help on a particular command
 			kgs=help_notes('keygen_backends'),
 			coin_id=help_notes('coin_id'),
 			g=g,
+			gc=gc,
 		),
 		'notes': lambda s, help_notes: s.format(
 			ch=help_notes('tool_help'),
-			pn=g.prog_name)
+			pn=gc.prog_name)
 	}
 }
 
@@ -269,8 +270,7 @@ def process_args(cmd,cmd_args,cls):
 			die(1,"'Binary input data must be supplied via STDIN")
 
 		if have_stdin_input and arg_type == 'str' and isinstance(arg,bytes):
-			from .globalvars import g
-			NL = '\r\n' if g.platform == 'win' else '\n'
+			NL = '\r\n' if gc.platform == 'win' else '\n'
 			arg = arg.decode()
 			if arg[-len(NL):] == NL: # rstrip one newline
 				arg = arg[:-len(NL)]
@@ -346,7 +346,7 @@ def get_cmd_cls(cmd):
 def get_mod_cls(modname):
 	return getattr(importlib.import_module(f'mmgen.tool.{modname}'),'tool_cmd')
 
-if g.prog_name == 'mmgen-tool' and not opt._lock:
+if gc.prog_name == 'mmgen-tool' and not opt._lock:
 
 	po = opts.init( opts_data, parse_only=True )
 

+ 7 - 7
mmgen/main_txbump.py

@@ -22,7 +22,7 @@ mmgen-txbump: Increase the fee on a replaceable (replace-by-fee) MMGen
 """
 
 import mmgen.opts as opts
-from .globalvars import g
+from .globalvars import gc
 from .opts import opt
 from .util import msg,msg_r,qmsg,die,async_run
 from .color import green
@@ -32,11 +32,11 @@ opts_data = {
 	'sets': [('yes', True, 'quiet', True)],
 	'text': {
 		'desc': f"""
-                Increase the fee on a replaceable (RBF) {g.proj_name} transaction,
+                Increase the fee on a replaceable (RBF) {gc.proj_name} transaction,
                 creating a new transaction, and optionally sign and send the
                 new transaction
 		 """,
-		'usage':   f'[opts] <{g.proj_name} TX file> [seed source] ...',
+		'usage':   f'[opts] <{gc.proj_name} TX file> [seed source] ...',
 		'options': """
 -h, --help             Print this help message
 --, --longhelp         Print help message for long options (common options)
@@ -66,7 +66,7 @@ opts_data = {
                        for the transaction's change output, if present)
 -O, --old-incog-fmt    Specify old-format incognito input
 -p, --hash-preset=   p Use the scrypt hash parameters defined by preset 'p'
-                       for password hashing (default: '{g.dfl_hash_preset}')
+                       for password hashing (default: '{gc.dfl_hash_preset}')
 -P, --passwd-file=   f Get {pnm} wallet passphrase from file 'f'
 -q, --quiet            Suppress warnings; overwrite files without prompting
 -s, --send             Sign and send the transaction (the default if seed
@@ -87,9 +87,9 @@ FMT CODES:
 	},
 	'code': {
 		'options': lambda help_notes,proto,s: s.format(
-			g=g,
-			pnm=g.proj_name,
-			pnl=g.proj_name.lower(),
+			gc=gc,
+			pnm=gc.proj_name,
+			pnl=gc.proj_name.lower(),
 			fu=help_notes('rel_fee_desc'),
 			fl=help_notes('fee_spec_letters'),
 			kgs=help_notes('keygen_backends'),

+ 2 - 2
mmgen/main_txcreate.py

@@ -22,14 +22,14 @@ mmgen-txcreate: Create a cryptocoin transaction with MMGen- and/or non-MMGen
 """
 
 import mmgen.opts as opts
-from .globalvars import g
+from .globalvars import g,gc
 from .opts import opt
 from .util import fmt_list,async_run
 
 opts_data = {
 	'sets': [('yes', True, 'quiet', True)],
 	'text': {
-		'desc': f'Create a transaction with outputs to specified coin or {g.proj_name} addresses',
+		'desc': f'Create a transaction with outputs to specified coin or {gc.proj_name} addresses',
 		'usage':   '[opts]  [<addr,amt> ...] <change addr, addrlist ID or addr type> [addr file ...]',
 		'options': """
 -h, --help            Print this help message

+ 4 - 4
mmgen/main_txdo.py

@@ -21,7 +21,7 @@ mmgen-txdo: Create, sign and broadcast an online MMGen transaction
 """
 
 import mmgen.opts as opts
-from .globalvars import g
+from .globalvars import g,gc
 from .opts import opt
 from .util import die,fmt_list,async_run
 from .wallet import Wallet
@@ -30,7 +30,7 @@ from .subseed import SubSeedIdxRange
 opts_data = {
 	'sets': [('yes', True, 'quiet', True)],
 	'text': {
-		'desc': f'Create, sign and send an {g.proj_name} transaction',
+		'desc': f'Create, sign and send an {gc.proj_name} transaction',
 		'usage':   '[opts]  [<addr,amt> ...] <change addr, addrlist ID or addr type> [addr file ...] [seed source ...]',
 		'options': """
 -h, --help             Print this help message
@@ -75,7 +75,7 @@ opts_data = {
                        mappings, so the user should record its checksum.
 -O, --old-incog-fmt    Specify old-format incognito input
 -p, --hash-preset=   p Use the scrypt hash parameters defined by preset 'p'
-                       for password hashing (default: '{g.dfl_hash_preset}')
+                       for password hashing (default: '{gc.dfl_hash_preset}')
 -P, --passwd-file=   f Get {pnm} wallet passphrase from file 'f'
 -r, --rbf              Make transaction BIP 125 (replace-by-fee) replaceable
 -q, --quiet            Suppress warnings; overwrite files without prompting
@@ -104,7 +104,7 @@ FMT CODES:
 	},
 	'code': {
 		'options': lambda proto,help_notes,s: s.format(
-			g=g,pnm=g.proj_name,pnl=g.proj_name.lower(),
+			g=g,gc=gc,pnm=gc.proj_name,pnl=gc.proj_name.lower(),
 			kgs=help_notes('keygen_backends'),
 			coin_id=help_notes('coin_id'),
 			fu=help_notes('rel_fee_desc'),

+ 2 - 2
mmgen/main_txsend.py

@@ -23,14 +23,14 @@ mmgen-txsend: Broadcast a transaction signed by 'mmgen-txsign' to the network
 import sys
 
 import mmgen.opts as opts
-from .globalvars import g
+from .globalvars import gc
 from .opts import opt
 from .util import vmsg,qmsg,async_run
 
 opts_data = {
 	'sets': [('yes', True, 'quiet', True)],
 	'text': {
-		'desc':    f'Send a signed {g.proj_name} cryptocoin transaction',
+		'desc':    f'Send a signed {gc.proj_name} cryptocoin transaction',
 		'usage':   '[opts] <signed transaction file>',
 		'options': """
 -h, --help      Print this help message

+ 6 - 6
mmgen/main_txsign.py

@@ -21,7 +21,7 @@ mmgen-txsign: Sign a transaction generated by 'mmgen-txcreate'
 """
 
 import mmgen.opts as opts
-from .globalvars import g
+from .globalvars import gc
 from .opts import opt
 from .util import msg,ymsg,die,async_run
 from .subseed import SubSeedIdxRange
@@ -32,7 +32,7 @@ from .color import orange
 opts_data = {
 	'sets': [('yes', True, 'quiet', True)],
 	'text': {
-		'desc':    f'Sign cryptocoin transactions generated by {g.proj_name.lower()}-txcreate',
+		'desc':    f'Sign cryptocoin transactions generated by {gc.proj_name.lower()}-txcreate',
 		'usage':   '[opts] <transaction file>... [seed source]...',
 		'options': """
 -h, --help            Print this help message
@@ -51,7 +51,7 @@ opts_data = {
                       is required only for brainwallet and incognito inputs
                       with non-standard (< {dsl}-bit) seed lengths.
 -p, --hash-preset=p   Use the scrypt hash parameters defined by preset 'p'
-                      for password hashing (default: '{g.dfl_hash_preset}')
+                      for password hashing (default: '{gc.dfl_hash_preset}')
 -z, --show-hash-presets Show information on available hash presets
 -k, --keys-from-file=f Provide additional keys for non-{pnm} addresses
 -K, --keygen-backend=n Use backend 'n' for public key generation.  Options
@@ -84,9 +84,9 @@ FMT CODES:
 	},
 	'code': {
 		'options': lambda proto,help_notes,s: s.format(
-			g=g,
-			pnm=g.proj_name,
-			pnl=g.proj_name.lower(),
+			gc=gc,
+			pnm=gc.proj_name,
+			pnl=gc.proj_name.lower(),
 			kgs=help_notes('keygen_backends'),
 			coin_id=help_notes('coin_id'),
 			dsl=help_notes('dfl_seed_len'),

+ 6 - 5
mmgen/main_wallet.py

@@ -22,7 +22,7 @@ main_wallet: Entry point for MMGen wallet-related scripts
 
 import sys,os
 import mmgen.opts as opts
-from .globalvars import g
+from .globalvars import g,gc
 from .opts import opt
 from .color import green,yellow
 from .util import msg,qmsg,vmsg,gmsg_r,ymsg,bmsg,die,capfirst
@@ -43,13 +43,13 @@ invoked_as = {
 	'mmgen-passchg':      'passchg',
 	'mmgen-subwalletgen': 'subgen',
 	'mmgen-seedsplit':    'seedsplit',
-}[g.prog_name]
+}[gc.prog_name]
 
-dsw = f'the default or specified {g.proj_name} wallet'
+dsw = f'the default or specified {gc.proj_name} wallet'
 
 # full: defhHiJkKlLmoOpPqrSvz-
 if invoked_as == 'gen':
-	desc = f'Generate an {g.proj_name} wallet from a random seed'
+	desc = f'Generate an {gc.proj_name} wallet from a random seed'
 	opt_filter = 'ehdoJlLpPqrSvz-'
 	usage = '[opts]'
 	oaction = 'output'
@@ -108,7 +108,7 @@ opts_data = {
 -m, --keep-label      Reuse label of input wallet for output wallet
 -M, --master-share=i  Use a master share with index 'i' (min:{ms_min}, max:{ms_max})
 -p, --hash-preset= p  Use the scrypt hash parameters defined by preset 'p'
-                      for password hashing (default: '{g.dfl_hash_preset}')
+                      for password hashing (default: '{gc.dfl_hash_preset}')
 -z, --show-hash-presets Show information on available hash presets
 -P, --passwd-file= f  Get wallet passphrase from file 'f'
 -q, --quiet           Produce quieter output; suppress some warnings
@@ -134,6 +134,7 @@ FMT CODES:
 			ms_max=help_notes('MasterShareIdx').max_val,
 			dsl=help_notes('dfl_seed_len'),
 			g=g,
+			gc=gc,
 		),
 		'notes': lambda help_notes,s: s.format(
 			f=help_notes('fmt_codes'),

+ 2 - 1
mmgen/main_xmrwallet.py

@@ -51,7 +51,7 @@ opts_data = {
                                  {R}
 -k, --use-internal-keccak-module Force use of the internal keccak module
 -p, --hash-preset=P              Use scrypt hash preset 'P' for password
-                                 hashing (default: '{g.dfl_hash_preset}')
+                                 hashing (default: '{gc.dfl_hash_preset}')
 -r, --restore-height=H           Scan from height 'H' when creating wallets
 -R, --no-relay                   Save transaction to file instead of relaying
 -s, --no-start-wallet-daemon     Don’t start the wallet daemon at startup
@@ -216,6 +216,7 @@ $ mmgen-xmrwallet --pager txview *XMR*.sigtx
 			D=xmrwallet_uarg_info['daemon'].annot,
 			R=xmrwallet_uarg_info['tx_relay_daemon'].annot,
 			g=g,
+			gc=gc,
 		),
 	}
 }

+ 2 - 2
mmgen/msg.py

@@ -13,7 +13,7 @@ msg: base message signing classes
 """
 
 import os,importlib,json
-from .globalvars import g
+from .globalvars import gc
 from .objmethods import MMGenObject,Hilite,InitErrors
 from .util import msg,die,suf,make_chksum_6,fmt_list,remove_dups
 from .color import red,orange,grnbg
@@ -92,7 +92,7 @@ class coin_msg:
 
 		def write_to_file(self,outdir=None,ask_overwrite=False):
 			data = {
-				'id': f'{g.proj_name} {self.desc}',
+				'id': f'{gc.proj_name} {self.desc}',
 				'metadata': self.data,
 				'signatures': self.sigs,
 			}

+ 10 - 10
mmgen/opts.py

@@ -21,7 +21,7 @@ opts: MMGen-specific options processing after generic processing by share.Opts
 """
 import sys,os
 
-from .globalvars import g
+from .globalvars import g,gc
 from .base_obj import Lockable
 
 import mmgen.share.Opts
@@ -36,14 +36,14 @@ opt = UserOpts()
 
 def usage():
 	from .util import Die
-	Die(1,mmgen.share.Opts.make_usage_str(g.prog_name,'user',usage_data))
+	Die(1,mmgen.share.Opts.make_usage_str(gc.prog_name,'user',usage_data))
 
 def version():
 	from .util import Die,fmt
 	Die(0,fmt(f"""
-		{g.prog_name.upper()} version {g.version}
-		Part of the {g.proj_name} suite, an online/offline cryptocurrency wallet for the
-		command line.  Copyright (C){g.Cdates} {g.author} {g.email}
+		{gc.prog_name.upper()} version {gc.version}
+		Part of the {gc.proj_name} suite, an online/offline cryptocurrency wallet for the
+		command line.  Copyright (C){gc.Cdates} {gc.author} {gc.email}
 	""",indent='  ').rstrip())
 
 def delete_data(opts_data):
@@ -167,7 +167,7 @@ def override_globals_from_cfg_file(
 	for d in ucfg.get_lines():
 		if d.name in g.cfg_file_opts:
 			ns = d.name.split('_')
-			if ns[0] in g.core_coins:
+			if ns[0] in gc.core_coins:
 				if not need_proto:
 					continue
 				nse,tn = (
@@ -267,7 +267,7 @@ common_opts_data = {
 --, --carol                Specify user “Carol” in MMGen regtest mode
 	""",
 	'code': lambda help_notes,proto,s: s.format(
-			pnm    = g.proj_name,
+			pnm    = gc.proj_name,
 			cu_dfl = proto.coin,
 		)
 }
@@ -369,7 +369,7 @@ def init(
 			if val != None and hasattr(g,k):
 				setattr(g,k,set_for_type(val,getattr(g,k),'--'+k))
 
-	if g.regtest or g.bob or g.alice or g.carol or g.prog_name == 'mmgen-regtest':
+	if g.regtest or g.bob or g.alice or g.carol or gc.prog_name == 'mmgen-regtest':
 		g.network = 'regtest'
 		g.regtest_user = 'bob' if g.bob else 'alice' if g.alice else 'carol' if g.carol else None
 	else:
@@ -422,7 +422,7 @@ def init(
 	if opt.verbose:
 		opt.quiet = None
 
-	if g.debug and g.prog_name != 'test.py':
+	if g.debug and gc.prog_name != 'test.py':
 		opt.verbose,opt.quiet = (True,None)
 
 	if g.debug_opts:
@@ -609,7 +609,7 @@ def check_usr_opts(usr_opts): # Raises an exception if any check fails
 #		except:
 #			die( 'UserOptError',
 #				'Regtest (Bob and Alice) mode not set up yet.  ' +
-#				f"Run '{g.proj_name.lower()}-regtest setup' to initialize." )
+#				f"Run '{gc.proj_name.lower()}-regtest setup' to initialize." )
 #
 #	chk_alice = chk_bob
 

+ 4 - 4
mmgen/proto/btc/daemon.py

@@ -14,7 +14,7 @@ proto.btc.daemon: Bitcoin base protocol daemon classes
 
 import os
 
-from ...globalvars import g
+from ...globalvars import g,gc
 from ...opts import opt
 from ...util import list_gen
 from ...daemon import CoinDaemon,_nw,_dd
@@ -29,7 +29,7 @@ class bitcoin_core_daemon(CoinDaemon):
 	rpc_ports = _nw(8332, 18332, 18443)
 	cfg_file = 'bitcoin.conf'
 	datadirs = {
-		'linux': [g.home_dir,'.bitcoin'],
+		'linux': [gc.home_dir,'.bitcoin'],
 		'win':   [os.getenv('APPDATA'),'Bitcoin']
 	}
 	nonstd_datadir = False
@@ -133,7 +133,7 @@ class bitcoin_cash_node_daemon(bitcoin_core_daemon):
 	cfg_file_hdr = '# Bitcoin Cash Node config file\n'
 	nonstd_datadir = True
 	datadirs = {
-		'linux': [g.home_dir,'.bitcoin-bchn'],
+		'linux': [gc.home_dir,'.bitcoin-bchn'],
 		'win':   [os.getenv('APPDATA'),'Bitcoin_ABC']
 	}
 
@@ -162,6 +162,6 @@ class litecoin_core_daemon(bitcoin_core_daemon):
 	cfg_file = 'litecoin.conf'
 	cfg_file_hdr = '# Litecoin Core config file\n'
 	datadirs = {
-		'linux': [g.home_dir,'.litecoin'],
+		'linux': [gc.home_dir,'.litecoin'],
 		'win':   [os.getenv('APPDATA'),'Litecoin']
 	}

+ 4 - 4
mmgen/proto/eth/daemon.py

@@ -14,7 +14,7 @@ proto.eth.daemon: Ethereum base protocol daemon classes
 
 import os
 
-from ...globalvars import g
+from ...globalvars import gc
 from ...util import list_gen,get_subclasses
 from ...daemon import CoinDaemon,RPCDaemon,_nw,_dd
 
@@ -60,7 +60,7 @@ class openethereum_daemon(ethereum_daemon):
 	exec_fn = 'openethereum'
 	cfg_file = 'parity.conf'
 	datadirs = {
-		'linux': [g.home_dir,'.local','share','io.parity.ethereum'],
+		'linux': [gc.home_dir,'.local','share','io.parity.ethereum'],
 		'win':   [os.getenv('LOCALAPPDATA'),'Parity','Ethereum']
 	}
 
@@ -97,7 +97,7 @@ class geth_daemon(ethereum_daemon):
 	avail_opts = ('no_daemonize','online')
 	version_info_arg = 'version'
 	datadirs = {
-		'linux': [g.home_dir,'.ethereum','geth'],
+		'linux': [gc.home_dir,'.ethereum','geth'],
 		'win':   [os.getenv('LOCALAPPDATA'),'Geth'] # FIXME
 	}
 
@@ -133,7 +133,7 @@ class erigon_daemon(geth_daemon):
 	torrent_ports = _nw(42069,42070,None) # testnet is non-standard
 	version_info_arg = '--version'
 	datadirs = {
-		'linux': [g.home_dir,'.local','share','erigon'],
+		'linux': [gc.home_dir,'.local','share','erigon'],
 		'win':   [os.getenv('LOCALAPPDATA'),'Erigon'] # FIXME
 	}
 

+ 2 - 2
mmgen/proto/xmr/daemon.py

@@ -14,7 +14,7 @@ proto.xmr.daemon: Monero base protocol daemon classes
 
 import os
 
-from ...globalvars import g
+from ...globalvars import g,gc
 from ...opts import opt
 from ...util import list_gen,die,contains_any
 from ...daemon import CoinDaemon,RPCDaemon,_nw,_dd
@@ -29,7 +29,7 @@ class monero_daemon(CoinDaemon):
 	rpc_ports = _nw(18081, 38081, None) # testnet is stagenet
 	cfg_file = 'bitmonero.conf'
 	datadirs = {
-		'linux': [g.home_dir,'.bitmonero'],
+		'linux': [gc.home_dir,'.bitmonero'],
 		'win':   ['/','c','ProgramData','bitmonero']
 	}
 

+ 6 - 6
mmgen/protocol.py

@@ -22,7 +22,7 @@ protocol: Coin protocol base classes and initializer
 
 from collections import namedtuple
 
-from .globalvars import g
+from .globalvars import g,gc
 from .objmethods import MMGenObject
 
 decoded_wif = namedtuple('decoded_wif',['sec','pubkey_type','compressed'])
@@ -36,7 +36,7 @@ class CoinProtocol(MMGenObject):
 
 	proto_info = namedtuple('proto_info',['name','trust_level']) # trust levels: see altcoin.py
 
-	# keys are mirrored in g.core_coins:
+	# keys are mirrored in gc.core_coins:
 	coins = {
 		'btc': proto_info('Bitcoin',         5),
 		'bch': proto_info('BitcoinCash',     5),
@@ -81,9 +81,9 @@ class CoinProtocol(MMGenObject):
 				self.addr_fmt_to_ver_bytes = {v:k for k,v in self.addr_ver_bytes.items()}
 				self.addr_ver_bytes_len = len(list(self.addr_ver_bytes)[0])
 
-			if 'tx' not in self.mmcaps and g.is_txprog:
+			if 'tx' not in self.mmcaps and gc.is_txprog:
 				from .util import die
-				die(2,f'Command {g.prog_name!r} not supported for coin {self.coin}')
+				die(2,f'Command {gc.prog_name!r} not supported for coin {self.coin}')
 
 			if hasattr(self,'chain_names'):
 				self.chain_name = self.chain_names[0] # first chain name is default
@@ -294,7 +294,7 @@ def warn_trustlevel(coinsym):
 		trust_level = e.trust_level if e else None
 		if trust_level in (None,-1):
 			from .util import die
-			die(1,f'Coin {coinsym} is not supported by {g.proj_name}')
+			die(1,f'Coin {coinsym} is not supported by {gc.proj_name}')
 
 	if trust_level > 3:
 		return
@@ -317,7 +317,7 @@ def warn_trustlevel(coinsym):
 			2: yellow('MEDIUM'),
 			3: green('OK'),
 		}[trust_level],
-		p = g.proj_name )
+		p = gc.proj_name )
 
 	if g.test_suite:
 		qmsg(warning)

+ 2 - 2
mmgen/rpc.py

@@ -264,7 +264,7 @@ class RPCClient(MMGenObject):
 	def __init__(self,host,port,test_connection=True):
 
 		# aiohttp workaround, and may speed up RPC performance overall on some systems:
-		if g.platform == 'win' and host == 'localhost':
+		if gc.platform == 'win' and host == 'localhost':
 			host = '127.0.0.1'
 
 		global dmsg_rpc,dmsg_rpc_backend
@@ -291,7 +291,7 @@ class RPCClient(MMGenObject):
 	def _get_backend(self,backend):
 		backend_id = backend or opt.rpc_backend
 		if backend_id == 'auto':
-			return {'linux':RPCBackends.httplib,'win':RPCBackends.requests}[g.platform](self)
+			return {'linux':RPCBackends.httplib,'win':RPCBackends.requests}[gc.platform](self)
 		else:
 			return getattr(RPCBackends,backend_id)(self)
 

+ 4 - 2
mmgen/term.py

@@ -22,7 +22,9 @@ term: Terminal classes for the MMGen suite
 
 import sys,os,time
 from collections import namedtuple
-from .common import *
+
+from .globalvars import g,gc
+from .util import msg,msg_r,die
 
 try:
 	import tty,termios
@@ -43,7 +45,7 @@ class MMGenTerm(object):
 
 	@classmethod
 	def register_cleanup(cls):
-		if g.platform == 'linux' and not hasattr(cls,'cleanup_registered'):
+		if gc.platform == 'linux' and not hasattr(cls,'cleanup_registered'):
 			import atexit
 			atexit.register(
 				lambda: termios.tcsetattr(

+ 2 - 2
mmgen/tool/fileutil.py

@@ -23,6 +23,7 @@ tool.fileutil: File routines for the 'mmgen-tool' utility
 import os
 
 from .common import tool_cmd_base
+from ..globalvars import gc
 from ..util import msg,msg_r,qmsg,die,suf,make_full_path
 from ..crypto import Crypto
 
@@ -36,11 +37,10 @@ class tool_cmd(tool_cmd_base):
 		"Use an Incog ID to find hidden incognito wallet data"
 
 		from hashlib import sha256
-		from ..globalvars import g
 
 		ivsize,bsize,mod = ( Crypto.aesctr_iv_len, 4096, 4096*8 )
 		n,carry = 0,b' '*ivsize
-		flgs = os.O_RDONLY|os.O_BINARY if g.platform == 'win' else os.O_RDONLY
+		flgs = os.O_RDONLY|os.O_BINARY if gc.platform == 'win' else os.O_RDONLY
 		f = os.open(filename,flgs)
 		for ch in incog_id:
 			if ch not in '0123456789ABCDEF':

+ 2 - 2
mmgen/tool/help.py

@@ -133,7 +133,7 @@ def gen_tool_usage():
 
 def gen_tool_cmd_usage(mod,cmdname):
 
-	from ..globalvars import g
+	from ..globalvars import gc
 	from ..util import capfirst
 
 	cls = main_tool.get_mod_cls(mod)
@@ -145,7 +145,7 @@ def gen_tool_cmd_usage(mod,cmdname):
 	yield capfirst( docstr.split('\n')[0].strip() )
 	yield ''
 	yield 'USAGE: {b} [OPTS] {c}{d}{e}'.format(
-		b = g.prog_name,
+		b = gc.prog_name,
 		c = cmdname,
 		d = f' {ARGS}' if ARGS else '',
 		e = f' [{KWARGS}]' if KWARGS else '' )

+ 2 - 2
mmgen/tool/util.py

@@ -20,6 +20,7 @@
 tool.util: Utility commands for the 'mmgen-tool' utility
 """
 
+from ..globalvars import gc
 from .common import tool_cmd_base
 
 class tool_cmd(tool_cmd_base):
@@ -121,8 +122,7 @@ class tool_cmd(tool_cmd_base):
 
 	def unhexdump(self,infile:str):
 		"decode hexdump from file (use '-' for stdin) (warning: outputs binary data)"
-		from ..globalvars import g
-		if g.platform == 'win':
+		if gc.platform == 'win':
 			import sys,os,msvcrt
 			msvcrt.setmode( sys.stdout.fileno(), os.O_BINARY )
 		from ..fileutil import get_data_from_file

+ 2 - 1
mmgen/tw/unspent.py

@@ -81,7 +81,8 @@ class TwUnspentOutputs(TwView):
 		await super().__init__(proto)
 		self.minconf  = minconf
 		self.addrs    = addrs
-		self.min_cols = g.min_screen_width
+		from ..globalvars import gc
+		self.min_cols = gc.min_screen_width
 
 	@property
 	def total(self):

+ 2 - 1
mmgen/tw/view.py

@@ -516,7 +516,8 @@ class TwView(MMGenObject,metaclass=AsyncInit):
 				for k in self.scroll_keys['vi']:
 					assert k not in self.key_mappings, f'{k!r} is in key_mappings'
 				self.key_mappings.update(self.scroll_keys['vi'])
-				self.key_mappings.update(self.scroll_keys[g.platform])
+				from ..globalvars import gc
+				self.key_mappings.update(self.scroll_keys[gc.platform])
 			return self.key_mappings
 
 		scroll = self.scroll = g.scroll

+ 3 - 3
mmgen/tx/base.py

@@ -12,7 +12,7 @@
 tx.base: base transaction class
 """
 
-from ..globalvars import *
+from ..globalvars import gc
 from ..objmethods import MMGenObject
 from ..obj import (
 	ImmutableAttr,
@@ -85,13 +85,13 @@ class Base(MMGenObject):
 	chain        = None
 	signed       = False
 	non_mmgen_inputs_msg = f"""
-		This transaction includes inputs with non-{g.proj_name} addresses.  When
+		This transaction includes inputs with non-{gc.proj_name} addresses.  When
 		signing the transaction, private keys for the addresses listed below must
 		be supplied using the --keys-from-file option.  The key file must contain
 		one key per line.  Please note that this transaction cannot be autosigned,
 		as autosigning does not support the use of key files.
 
-		Non-{g.proj_name} addresses found in inputs:
+		Non-{gc.proj_name} addresses found in inputs:
 		    {{}}
 	"""
 

+ 2 - 2
mmgen/tx/info.py

@@ -12,7 +12,7 @@
 tx.info: transaction info class
 """
 
-from ..globalvars import *
+from ..globalvars import gc
 from ..color import red,green,orange
 from ..opts import opt
 from ..util import msg,msg_r
@@ -43,7 +43,7 @@ class TxInfo:
 				sel_f = lambda o: len(o.mmid) + (2,8)[bool(o.is_chg)] # + len(' (chg)')
 			return  max(max([sel_f(o) for o in io if o.mmid] or [0]),len(nonmm_str))
 
-		nonmm_str = f'(non-{g.proj_name} address)'
+		nonmm_str = f'(non-{gc.proj_name} address)'
 		max_mmwid = max(get_max_mmwid(tx.inputs),get_max_mmwid(tx.outputs))
 
 		def gen_view():

+ 4 - 3
mmgen/tx/new.py

@@ -15,6 +15,7 @@ tx.new: new transaction class
 from ..globalvars import *
 from ..opts import opt
 from .base import Base
+from ..globalvars import gc
 from ..color import pink,yellow
 from ..obj import get_obj,MMGenList
 from ..util import msg,qmsg,fmt,die,suf,remove_dups,get_extension
@@ -37,12 +38,12 @@ def mmaddr2coinaddr(mmaddr,ad_w,ad_f,proto):
 				the address into your tracking wallet before broadcasting this transaction.
 			""",
 			'addr_not_found': f"""
-				No data for {g.proj_name} address {mmaddr} could be found in either the
+				No data for {gc.proj_name} address {mmaddr} could be found in either the
 				tracking wallet or the supplied address file.  Please import this address
 				into your tracking wallet, or supply an address file on the command line.
 			""",
 			'addr_not_found_no_addrfile': f"""
-				No data for {g.proj_name} address {mmaddr} could be found in the tracking
+				No data for {gc.proj_name} address {mmaddr} could be found in the tracking
 				wallet.  Please import this address into your tracking wallet or supply an
 				address file for it on the command line.
 			"""
@@ -227,7 +228,7 @@ class New(Base):
 				'ERROR: No change output specified' ))
 
 		if self.has_segwit_outputs() and not self.rpc.info('segwit_is_active'):
-			die(2,f'{g.proj_name} Segwit address requested on the command line, '
+			die(2,f'{gc.proj_name} Segwit address requested on the command line, '
 					+ 'but Segwit is not active on this chain')
 
 		if not self.outputs:

+ 4 - 4
mmgen/tx/sign.py

@@ -20,7 +20,7 @@
 tx.sign: Sign a transaction generated by 'mmgen-txcreate'
 """
 
-from ..globalvars import g
+from ..globalvars import g,gc
 from ..opts import opt
 from ..util import msg,vmsg,qmsg,suf,fmt,die,remove_dups,get_extension
 from ..obj import MMGenList
@@ -83,7 +83,7 @@ def add_keys(tx,src,infiles=None,saved_seeds=None,keyaddr_list=None):
 	desc,src_desc = (
 		('key-address file','From key-address file:') if keyaddr_list else
 		('seed(s)','Generated from seed:') )
-	qmsg(f'Checking {g.proj_name} -> {tx.proto.coin} address mappings for {src} (from {desc})')
+	qmsg(f'Checking {gc.proj_name} -> {tx.proto.coin} address mappings for {src} (from {desc})')
 	d = (
 		MMGenList([keyaddr_list]) if keyaddr_list else
 		generate_kals_for_mmgen_addrs(need_keys,infiles,saved_seeds,tx.proto) )
@@ -99,7 +99,7 @@ def add_keys(tx,src,infiles=None,saved_seeds=None,keyaddr_list=None):
 							new_keys.append(f)
 					else:
 						die(3,fmt(f"""
-							{g.proj_name} -> {tx.proto.coin} address mappings differ!
+							{gc.proj_name} -> {tx.proto.coin} address mappings differ!
 							{{src_desc:<23}} {{mmid}} -> {{f.addr}}
 							{{'tx file:':<23}} {{e.mmid}} -> {{e.addr}}
 							""").strip())
@@ -157,7 +157,7 @@ async def txsign(tx,seed_files,kl,kal,tx_num_str=''):
 		if missing:
 			sep = '\n    '
 			die(2,'ERROR: a key file must be supplied for the following non-{} address{}:{}'.format(
-				g.proj_name,
+				gc.proj_name,
 				suf(missing,'es'),
 				sep + sep.join(missing) ))
 		keys += tmp.data

+ 2 - 2
mmgen/ui.py

@@ -14,7 +14,7 @@ ui: Interactive user interface functions for the MMGen suite
 
 import sys,os
 
-from .globalvars import g
+from .globalvars import g,gc
 from .opts import opt
 from .util import msg,msg_r,Msg,dmsg,die
 
@@ -116,7 +116,7 @@ def do_pager(text):
 	end_msg = '\n(end of text)\n\n'
 	# --- Non-MSYS Windows code deleted ---
 	# raw, chop, horiz scroll 8 chars, disable buggy line chopping in MSYS
-	os.environ['LESS'] = (('--shift 8 -RS'),('--shift 16 -RS'))[g.platform=='win']
+	os.environ['LESS'] = (('--shift 8 -RS'),('--shift 16 -RS'))[gc.platform=='win']
 
 	if 'PAGER' in os.environ and os.environ['PAGER'] != pagers[0]:
 		pagers = [os.environ['PAGER']] + pagers

+ 13 - 13
mmgen/util.py

@@ -23,7 +23,7 @@ util: Frequently-used variables, classes and utility functions for the MMGen sui
 import sys,os,time,re
 
 from .color import *
-from .globalvars import g
+from .globalvars import g,gc,gv
 from .opts import opt
 
 ascii_lowercase = 'abcdefghijklmnopqrstuvwxyz'
@@ -32,11 +32,11 @@ hexdigits = '0123456789abcdefABCDEF'
 hexdigits_uc = '0123456789ABCDEF'
 hexdigits_lc = '0123456789abcdef'
 
-if g.platform == 'win':
+if gc.platform == 'win':
 	def msg_r(s):
 		try:
-			g.stderr.write(s)
-			g.stderr.flush()
+			gv.stderr.write(s)
+			gv.stderr.flush()
 		except:
 			os.write(2,s.encode())
 
@@ -45,8 +45,8 @@ if g.platform == 'win':
 
 	def Msg_r(s):
 		try:
-			g.stdout.write(s)
-			g.stdout.flush()
+			gv.stdout.write(s)
+			gv.stdout.flush()
 		except:
 			os.write(1,s.encode())
 
@@ -54,18 +54,18 @@ if g.platform == 'win':
 		Msg_r(s + '\n')
 else:
 	def msg(s):
-		g.stderr.write(s + '\n')
+		gv.stderr.write(s + '\n')
 
 	def msg_r(s):
-		g.stderr.write(s)
-		g.stderr.flush()
+		gv.stderr.write(s)
+		gv.stderr.flush()
 
 	def Msg(s):
-		g.stdout.write(s + '\n')
+		gv.stdout.write(s + '\n')
 
 	def Msg_r(s):
-		g.stdout.write(s)
-		g.stdout.flush()
+		gv.stdout.write(s)
+		gv.stdout.flush()
 
 def rmsg(s):
 	msg(red(s))
@@ -413,5 +413,5 @@ def wrap_ripemd160(called=[]):
 		called.append(True)
 
 def exit_if_mswin(feature):
-	if g.platform == 'win':
+	if gc.platform == 'win':
 		die(2, capfirst(feature) + ' not supported on the MSWin / MSYS2 platform' )

+ 2 - 2
mmgen/wallet/enc.py

@@ -12,7 +12,7 @@
 wallet.enc: encrypted wallet base class
 """
 
-from ..globalvars import g
+from ..globalvars import gc
 from ..opts import opt
 from ..util import msg,qmsg,make_chksum_8
 from .base import wallet
@@ -60,7 +60,7 @@ class wallet(wallet):
 			qmsg(f'Using hash preset {hp!r} requested on command line')
 		else:
 			hp = self._get_hash_preset_from_user(
-				old_preset = g.dfl_hash_preset,
+				old_preset = gc.dfl_hash_preset,
 				add_desc   = add_desc )
 		self.ssdata.hash_preset = hp
 

+ 3 - 3
mmgen/wallet/incog_hidden.py

@@ -14,7 +14,7 @@ wallet.incog_hidden: hidden incognito wallet class
 
 import os
 
-from ..globalvars import g
+from ..globalvars import gc
 from ..opts import opt
 from ..seed import Seed
 from ..util import msg,dmsg,qmsg,die,compare_or_die,capfirst
@@ -74,7 +74,7 @@ class wallet(wallet):
 		d.target_data_len = self._get_incog_data_len(opt.seed_len or Seed.dfl_len)
 		self._check_valid_offset(self.infile,'read')
 
-		flgs = os.O_RDONLY|os.O_BINARY if g.platform == 'win' else os.O_RDONLY
+		flgs = os.O_RDONLY|os.O_BINARY if gc.platform == 'win' else os.O_RDONLY
 		fh = os.open(self.infile.name,flgs)
 		os.lseek(fh,int(d.hincog_offset),os.SEEK_SET)
 		self.fmt_data = os.read(fh,d.target_data_len)
@@ -135,7 +135,7 @@ class wallet(wallet):
 					message = '',
 					action  = f'alter file {f.name!r}' )
 
-		flgs = os.O_RDWR|os.O_BINARY if g.platform == 'win' else os.O_RDWR
+		flgs = os.O_RDWR|os.O_BINARY if gc.platform == 'win' else os.O_RDWR
 		fh = os.open(f.name,flgs)
 		os.lseek(fh, int(d.hincog_offset), os.SEEK_SET)
 		os.write(fh, self.fmt_data)

+ 2 - 2
scripts/uninstall-mmgen.py

@@ -19,7 +19,7 @@
 import sys,os
 
 import mmgen.opts as opts
-from mmgen.globalvars import g
+from mmgen.globalvars import gc
 from mmgen.util import msg,die
 
 def normalize_path(p):
@@ -53,7 +53,7 @@ opts_data = {
 
 cmd_args = opts.init(opts_data)
 
-if g.platform == 'linux' and os.getenv('USER') != 'root':
+if gc.platform == 'linux' and os.getenv('USER') != 'root':
 	die(1,'This program must be run as root')
 
 if len(cmd_args):

+ 2 - 2
test/gentest.py

@@ -28,7 +28,7 @@ sys.path.insert(0,overlay_setup(repo_root))
 
 # Import these _after_ local path's been added to sys.path
 import mmgen.opts as opts
-from mmgen.globalvars import g
+from mmgen.globalvars import g,gc
 from mmgen.opts import opt
 from mmgen.color import green,red,purple
 from mmgen.util import msg,qmsg,qmsg_r,vmsg,capfirst,is_int,die
@@ -131,7 +131,7 @@ SUPPORTED EXTERNAL TOOLS:
 		),
 		'notes': lambda s: s.format(
 			prog='test/gentest.py',
-			pnm=g.proj_name,
+			pnm=gc.proj_name,
 			snum=rounds )
 	}
 }

+ 2 - 2
test/include/coin_daemon_control.py

@@ -15,7 +15,7 @@ test.include.coin_daemon_control: Start and stop daemons for the MMGen test suit
 from .tests_header import repo_root
 from mmgen.common import *
 
-action = g.prog_name.split('-')[0]
+action = gc.prog_name.split('-')[0]
 
 opts_data = {
 	'sets': [('debug',True,'verbose',True)],
@@ -44,7 +44,7 @@ Valid network IDs: {nid}, all, or no_xmr
 """
 	},
 	'code': {
-		'options': lambda s: s.format(a=action.capitalize(),pn=g.prog_name),
+		'options': lambda s: s.format(a=action.capitalize(),pn=gc.prog_name),
 		'notes': lambda s,help_notes: s.format(nid=help_notes('coin_daemon_network_ids'))
 	}
 }

+ 4 - 4
test/include/common.py

@@ -162,13 +162,13 @@ def init_coverage():
 
 def silence():
 	if not (opt.verbose or opt.exact_output):
-		g.stdout = g.stderr = open(os.devnull,'w')
+		gv.stdout = gv.stderr = open(os.devnull,'w')
 
 def end_silence():
 	if not (opt.verbose or opt.exact_output):
-		g.stdout.close()
-		g.stdout = sys.stdout
-		g.stderr = sys.stderr
+		gv.stdout.close()
+		gv.stdout = sys.stdout
+		gv.stderr = sys.stderr
 
 def omsg(s):
 	sys.stderr.write(s + '\n')

+ 1 - 1
test/misc/get_passphrase.py

@@ -29,7 +29,7 @@ def crypto():
 	from mmgen.crypto import Crypto
 	crypto = Crypto()
 
-	pw = crypto.get_new_passphrase(data_desc=desc,hash_preset=g.dfl_hash_preset,passwd_file=None)
+	pw = crypto.get_new_passphrase(data_desc=desc,hash_preset=gc.dfl_hash_preset,passwd_file=None)
 	msg(f'==> got new passphrase: [{pw}]\n')
 
 	pw = crypto.get_passphrase(data_desc=desc,passwd_file=None)

+ 5 - 5
test/misc/term.py

@@ -19,13 +19,13 @@ commands = [
 	'get_char_one',
 	'get_char_one_raw',
 ]
-if g.platform == 'linux':
+if gc.platform == 'linux':
 	commands.extend([
 		'get_char',
 		'get_char_immed_chars',
 		'get_char_raw',
 	])
-elif g.platform == 'win':
+elif gc.platform == 'win':
 	commands.extend([
 		'get_char_one_char_immed_chars',
 	])
@@ -38,7 +38,7 @@ opts_data = {
 -h, --help     Print this help message
 """,
 	'notes': f"""
-available commands for platform {g.platform!r}:
+available commands for platform {gc.platform!r}:
 {fmt_list(commands,fmt='col',indent='    ')}
 """
 	}
@@ -118,7 +118,7 @@ def _tt_get_char(raw=False,one_char=False,immed_chars=''):
 			if one_char else
 		'echoed as a FULL CONTROL SEQUENCE.'
 	)
-	if g.platform == 'win':
+	if gc.platform == 'win':
 		if raw:
 			m3 = 'The Escape and F1-F12 keys will be returned as two-character strings.'
 		else:
@@ -149,7 +149,7 @@ def tt_urand():
 	msg(f'USER ENTROPY (user input + keystroke timings):\n\n{fmt(ret,"  ")}')
 	times = ret.splitlines()[1:]
 	avg_prec = sum(len(t.split('.')[1]) for t in times) // len(times)
-	if avg_prec < g.min_time_precision:
+	if avg_prec < gc.min_time_precision:
 		ymsg(f'WARNING: Avg. time precision of only {avg_prec} decimal points.  User entropy quality is degraded!')
 	else:
 		msg(f'Average time precision: {avg_prec} decimal points - OK')

+ 1 - 1
test/scrambletest.py

@@ -127,7 +127,7 @@ def do_coin_tests():
 	for tname,tdata in (
 			tuple(bitcoin_data.items()) +
 			tuple(altcoin_data.items() if not opt.no_altcoin else []) ):
-		if tname == 'zec_zcash_z' and g.platform == 'win':
+		if tname == 'zec_zcash_z' and gc.platform == 'win':
 			msg("Skipping 'zec_zcash_z' test for Windows platform")
 			continue
 		coin,mmtype = tname.split('_',1) if '_' in tname else (tname,None)

+ 6 - 6
test/test.py

@@ -30,7 +30,7 @@ def create_shm_dir(data_dir,trash_dir):
 	# under '/dev/shm' and put datadir and tmpdirs here.
 	import shutil
 	from subprocess import run
-	if g.platform == 'win':
+	if gc.platform == 'win':
 		for tdir in (data_dir,trash_dir):
 			try: os.listdir(tdir)
 			except: pass
@@ -195,7 +195,7 @@ opts.UserOpts._reset_ok += (
 parsed_opts = opts.init(opts_data,return_parsed=True)
 usr_args = parsed_opts.cmd_args
 
-if opt.pexpect_spawn and g.platform == 'win':
+if opt.pexpect_spawn and gc.platform == 'win':
 	die(1,'--pexpect-spawn option not supported on Windows platform, exiting')
 
 if opt.daemon_id and opt.daemon_id in g.blacklisted_daemons.split():
@@ -313,7 +313,7 @@ def clean(usr_dirs=None,clean_overlay=True):
 		iqmsg(green(f'Cleaned directory {os.path.relpath(overlay_tree_dir)!r}'))
 
 def create_tmp_dirs(shm_dir):
-	if g.platform == 'win':
+	if gc.platform == 'win':
 		for cfg in sorted(cfgs):
 			mk_tmpdir(cfgs[cfg]['tmpdir'])
 	else:
@@ -556,7 +556,7 @@ class TestSuiteRunner(object):
 			omsg(f'INFO → Writing coverage files to {coverdir!r}')
 			self.pre_args = ['python3','-m','trace','--count','--coverdir='+coverdir,'--file='+accfile]
 		else:
-			self.pre_args = ['python3'] if g.platform == 'win' else []
+			self.pre_args = ['python3'] if gc.platform == 'win' else []
 
 		if opt.pexpect_spawn:
 			omsg(f'INFO → Using pexpect.spawn() for real terminal emulation')
@@ -612,7 +612,7 @@ class TestSuiteRunner(object):
 					clr1,clr2 = (nocolor,nocolor) if opt.print_cmdline else (green,cyan)
 					omsg(
 						clr1('Executing: ') +
-						clr2(repr(cmd_disp) if g.platform == 'win' else cmd_disp)
+						clr2(repr(cmd_disp) if gc.platform == 'win' else cmd_disp)
 					)
 			else:
 				omsg_r(f'{t_pfx}Testing {desc}: ')
@@ -918,7 +918,7 @@ class TestSuiteRunner(object):
 
 	def check_deps(self,cmds): # TODO: broken
 		if len(cmds) != 1:
-			die(1,f'Usage: {g.prog_name} check_deps <command>')
+			die(1,f'Usage: {gc.prog_name} check_deps <command>')
 
 		cmd = cmds[0]
 

+ 4 - 3
test/test_py_d/common.py

@@ -20,8 +20,9 @@
 test.test_py_d.common: Shared routines and data for the test.py test suite
 """
 
-import os
-from mmgen.common import *
+import sys,os
+from mmgen.globalvars import g,gc
+from mmgen.util import msg
 from ..include.common import *
 
 log_file = 'test.py.log'
@@ -53,7 +54,7 @@ from mmgen.obj import MMGenTxComment,TwComment
 tx_comment_jp = text_jp
 tx_comment_zh = text_zh
 
-lcg = ascii_cyr_gr if g.platform == 'win' else lat_cyr_gr # MSYS2 popen_spawn issue
+lcg = ascii_cyr_gr if gc.platform == 'win' else lat_cyr_gr # MSYS2 popen_spawn issue
 tx_comment_lat_cyr_gr = lcg[:MMGenTxComment.max_len] # 72 chars
 
 tw_comment_zh         = text_zh[:TwComment.max_screen_width // 2]

+ 3 - 3
test/test_py_d/ts_autosign.py

@@ -23,7 +23,7 @@ test.test_py_d.ts_autosign: Autosign tests for the test.py test suite
 import os,shutil
 from subprocess import run
 
-from mmgen.globalvars import g
+from mmgen.globalvars import g,gc
 from mmgen.opts import opt
 
 from ..include.common import *
@@ -84,7 +84,7 @@ class TestSuiteAutosignBase(TestSuiteBase):
 		super().__init__(trunner,cfgs,spawn)
 		if trunner == None:
 			return
-		if g.platform == 'win':
+		if gc.platform == 'win':
 			die(1,f'Test {type(self).__name__} not supported for Windows platform')
 		self.network_ids = [c+'_tn' for c in self.daemon_coins] + self.daemon_coins
 
@@ -126,7 +126,7 @@ class TestSuiteAutosignBase(TestSuiteBase):
 		self.bad_msg_count = 0
 
 	def __del__(self):
-		if g.platform == 'win' or self.tr == None:
+		if gc.platform == 'win' or self.tr == None:
 			return
 		if self.simulate or not self.live:
 			LEDControl.delete_dummy_control_files()

+ 2 - 2
test/test_py_d/ts_base.py

@@ -21,7 +21,7 @@ test.test_py_d.ts_base: Base class for the test.py test suite
 """
 
 import os
-from mmgen.globalvars import g
+from mmgen.globalvars import g,gc
 from mmgen.opts import opt
 from ..include.common import *
 from .common import *
@@ -74,7 +74,7 @@ class TestSuiteBase(object):
 		return write_to_file(os.path.join(self.tmpdir,fn),data,binary=binary)
 
 	def skip_for_win(self):
-		if g.platform == 'win':
+		if gc.platform == 'win':
 			msg(f'Skipping test {self.test_name!r}: not supported on MSys2 platform')
 			return True
 		else:

+ 1 - 1
test/test_py_d/ts_cfgfile.py

@@ -158,7 +158,7 @@ class TestSuiteCfgFile(TestSuiteBase):
 		write_to_file(self.path('usr'),'\n'.join(d) + '\n')
 		return self.old_sample_common(
 			old_set       = True,
-			pexpect_spawn = False if g.platform == 'win' else True )
+			pexpect_spawn = False if gc.platform == 'win' else True )
 
 	def _autoset_opts(self,args=[],text='rpc_backend aiohttp\n'):
 		write_to_file( self.path('usr'), text )

+ 2 - 2
test/test_py_d/ts_ethdev.py

@@ -25,7 +25,7 @@ from decimal import Decimal
 from collections import namedtuple
 from subprocess import run,PIPE,DEVNULL
 
-from mmgen.globalvars import g
+from mmgen.globalvars import g,gc
 from mmgen.opts import opt
 from mmgen.util import die
 from mmgen.protocol import CoinProtocol
@@ -1290,7 +1290,7 @@ class TestSuiteEthdev(TestSuiteBase,TestSuiteShared):
 	def edit_comment1(self):
 		return self.edit_comment(out_num=del_addrs[0],comment_text=tw_comment_zh[:3])
 	def edit_comment2(self):
-		spawn = False if g.platform == 'win' else True
+		spawn = False if gc.platform == 'win' else True
 		return self.edit_comment(out_num=del_addrs[0],comment_text=tw_comment_zh[3:],changed=True,pexpect_spawn=spawn)
 	def edit_comment3(self):
 		return self.edit_comment(out_num=del_addrs[1],comment_text=tw_comment_lat_cyr_gr)

+ 4 - 4
test/test_py_d/ts_input.py

@@ -219,12 +219,12 @@ class TestSuiteInput(TestSuiteBase):
 
 		# hash preset (default)
 		t.expect('accept the default .*: ', '\n', regex=True)
-		t.expect(f'[{g.dfl_hash_preset}]')
+		t.expect(f'[{gc.dfl_hash_preset}]')
 
 		return t
 
 	def _input_func(self,func_name,arg_dfls,func_args,text,expect,term):
-		if term and g.platform == 'win':
+		if term and gc.platform == 'win':
 			return ('skip_warn','pexpect_spawn not supported on Windows platform')
 		func_args = {k:v for k,v in zip(arg_dfls.keys(),func_args)}
 		t = self.spawn(
@@ -268,7 +268,7 @@ class TestSuiteInput(TestSuiteBase):
 		return self._get_char(['prompt> ','',True,5],'x','x',False)
 
 	def get_char2(self):
-		expect = 'x' if g.platform == 'win' else 'xxxxx'
+		expect = 'x' if gc.platform == 'win' else 'xxxxx'
 		return self._get_char(['prompt> ','',True,5],'xxxxx',expect,False)
 
 	def get_char3(self):
@@ -314,7 +314,7 @@ class TestSuiteInput(TestSuiteBase):
 		return self._line_input(['prompt> ',True,'foobarbaz',True],Ctrl_U+'foobar','foobar',True)
 
 	def _password_entry(self,prompt,opts=[],term=False):
-		if term and g.platform == 'win':
+		if term and gc.platform == 'win':
 			return ('skip_warn','pexpect_spawn not supported on Windows platform')
 		t = self.spawn( 'test/misc/input_func.py', opts + ['passphrase'], cmd_dir='.', pexpect_spawn=term )
 		imsg('Terminal: {}'.format(term))

+ 2 - 2
test/test_py_d/ts_misc.py

@@ -20,7 +20,7 @@
 test.test_py_d.ts_misc: Miscellaneous test groups for the test.py test suite
 """
 
-from mmgen.globalvars import g
+from mmgen.globalvars import g,gc
 from ..include.common import *
 from .common import *
 from .ts_base import *
@@ -233,7 +233,7 @@ class TestSuiteOutput(TestSuiteBase):
 
 	def oneshot_warning(self,pexpect_spawn=None):
 		t = self.spawn('test/misc/oneshot_warning.py',cmd_dir='.',pexpect_spawn=pexpect_spawn)
-		nl = '\r\n' if g.platform == 'win' or t.pexpect_spawn else '\n'
+		nl = '\r\n' if gc.platform == 'win' or t.pexpect_spawn else '\n'
 		for s in (
 			f'pw{nl}wg1',
 			'foo is experimental',

+ 1 - 1
test/test_py_d/ts_tool.py

@@ -74,7 +74,7 @@ class TestSuiteTool(TestSuiteMain,TestSuiteBase):
 		vmsg(f'Incog ID: {cyan(i_id)}')
 		t = self.spawn('mmgen-tool',['-d',self.tmpdir,'find_incog_data',f1,i_id])
 		o = t.expect_getend(f'Incog data for ID {i_id} found at offset ')
-		if not g.platform == 'win':
+		if not gc.platform == 'win':
 			os.unlink(f1) # causes problems with MSYS2
 		cmp_or_die(hincog_offset,int(o))
 		return t

+ 2 - 2
test/test_py_d/ts_xmrwallet.py

@@ -23,7 +23,7 @@ test.test_py_d.ts_xmrwallet: xmrwallet tests for the test.py test suite
 import sys,os,atexit,asyncio,shutil
 from subprocess import run,PIPE
 
-from mmgen.globalvars import g
+from mmgen.globalvars import gc
 from mmgen.opts import opt
 from mmgen.obj import MMGenRange
 from mmgen.amt import XMRAmt
@@ -128,7 +128,7 @@ class TestSuiteXMRWallet(TestSuiteBase):
 				omsg(f'SSH SOCKS server started, listening at localhost:{cls.socks_port}')
 
 		def kill_proxy():
-			if g.platform == 'linux':
+			if gc.platform == 'linux':
 				omsg(f'Killing SSH SOCKS server at localhost:{cls.socks_port}')
 				cmd = [ 'pkill', '-f', ' '.join(a + b2) ]
 				run(cmd)

+ 1 - 1
test/tooltest.py

@@ -141,7 +141,7 @@ spawn_cmd = ['scripts/exec_wrapper.py',mmgen_cmd]
 if opt.coverage:
 	d,f = init_coverage()
 	spawn_cmd = ['python3','-m','trace','--count','--coverdir='+d,'--file='+f] + spawn_cmd
-elif g.platform == 'win':
+elif gc.platform == 'win':
 	spawn_cmd = ['python3'] + spawn_cmd
 
 add_spawn_args = ['--data-dir='+cfg['tmpdir']] + ['--{}{}'.format(

+ 2 - 2
test/tooltest2.py

@@ -40,7 +40,7 @@ from mmgen.baseconv import *
 
 skipped_tests = ['mn2hex_interactive']
 
-NL = ('\n','\r\n')[g.platform=='win']
+NL = ('\n','\r\n')[gc.platform=='win']
 
 def is_str(s):
 	return type(s) == str
@@ -866,7 +866,7 @@ async def run_test(cls,gid,cmd_name):
 		elif opt.fork:
 			cmd_out = fork_cmd(cmd_name,args,out,opts,stdin_input)
 		else:
-			if stdin_input and g.platform == 'win':
+			if stdin_input and gc.platform == 'win':
 				msg('Skipping for MSWin - no os.fork()')
 				continue
 			method = getattr(cls(cmdname=cmd_name,proto=proto,mmtype=mmtype),cmd_name)

+ 1 - 1
test/unit_tests.py

@@ -142,7 +142,7 @@ def run_test(test,subtest=None):
 				if opt.no_altcoin_deps and subtest in altcoin_deps:
 					qmsg(gray(f'Invoked with --no-altcoin-deps, so skipping {subtest_disp!r}'))
 					continue
-				if g.platform == 'win' and subtest in win_skip:
+				if gc.platform == 'win' and subtest in win_skip:
 					qmsg(gray(f'Skipping {subtest_disp!r} for Windows platform'))
 					continue
 				elif platform.machine() == 'aarch64' and subtest in arm_skip:

+ 4 - 4
test/unit_tests_d/__init__.py

@@ -6,7 +6,7 @@ test.unit_tests_d.__init__: shared data for unit tests for the MMGen suite
 
 import sys,os
 
-from mmgen.globalvars import g
+from mmgen.globalvars import gv
 from mmgen.opts import opt
 
 class unit_tests_base:
@@ -15,9 +15,9 @@ class unit_tests_base:
 		if not opt.verbose:
 			self.stdout = sys.stdout
 			self.stderr = sys.stderr
-			sys.stdout = sys.stderr = g.stdout = g.stderr = open(os.devnull,'w')
+			sys.stdout = sys.stderr = gv.stdout = gv.stderr = open(os.devnull,'w')
 
 	def _end_silence(self):
 		if not opt.verbose:
-			sys.stdout = g.stdout = self.stdout
-			sys.stderr = g.stderr = self.stderr
+			sys.stdout = gv.stdout = self.stdout
+			sys.stderr = gv.stderr = self.stderr

+ 1 - 1
test/unit_tests_d/ut_rpc.py

@@ -120,7 +120,7 @@ def run_test(network_ids,test_cf_auth=False,daemon_ids=None):
 		if not opt.no_daemon_stop:
 			d.stop()
 
-		if test_cf_auth and g.platform != 'win':
+		if test_cf_auth and gc.platform != 'win':
 			cfg_file_auth_test(d.proto,d)
 			cfg_file_auth_test(d.proto,d,bad_auth=True)
 

+ 1 - 1
test/unit_tests_d/ut_testdep.py

@@ -48,7 +48,7 @@ class unit_tests:
 		return True
 
 	def ethkey(self,name,ut):
-		if g.platform == 'linux' and os.uname().machine != 'x86_64':
+		if gc.platform == 'linux' and os.uname().machine != 'x86_64':
 			distro = [l for l in open('/etc/os-release').read().split('\n') if l.startswith('ID=')][0][3:]
 			if distro != 'archarm':
 				ymsg('Skipping ethkey availability test for distro {!r} on architecture {!r}'.format(