Browse Source

Config API, Part I

This patch eliminates the global configuration variables `opt` and `g`, making
all functions and class instances locally configurable.  Configuration data is
passed to functions and constructors via the `cfg` parameter and made available
to methods in `self.cfg`.

Local configuration free from dependence on the command line will enable the
creation of multiple, independently configured instances of MMGen’s data
objects within a single process.

Potential applications include testing (tracking wallets configured to interact
with spawned processes, for example) and the use of MMGen as a library for
other projects.

This patch completes most of the work required to enable the API.  The full
implementation will appear in a forthcoming commit.
The MMGen Project 2 years ago
parent
commit
c7adb56e38
197 changed files with 2218 additions and 2078 deletions
  1. 4 6
      examples/halving-calculator.py
  2. 8 6
      mmgen/addrdata.py
  3. 19 20
      mmgen/addrfile.py
  4. 6 6
      mmgen/addrgen.py
  5. 16 15
      mmgen/addrlist.py
  6. 6 6
      mmgen/altcoin.py
  7. 2 3
      mmgen/baseconv.py
  8. 17 14
      mmgen/cfgfile.py
  9. 0 1
      mmgen/common.py
  10. 50 51
      mmgen/crypto.py
  11. 24 21
      mmgen/daemon.py
  12. 1 1
      mmgen/data/version
  13. 33 30
      mmgen/fileutil.py
  14. 6 21
      mmgen/globalvars.py
  15. 7 7
      mmgen/help.py
  16. 9 9
      mmgen/keygen.py
  17. 20 21
      mmgen/main_addrgen.py
  18. 22 20
      mmgen/main_addrimport.py
  19. 38 38
      mmgen/main_autosign.py
  20. 18 18
      mmgen/main_msg.py
  21. 21 19
      mmgen/main_passgen.py
  22. 5 3
      mmgen/main_regtest.py
  23. 23 24
      mmgen/main_seedjoin.py
  24. 17 18
      mmgen/main_split.py
  25. 10 9
      mmgen/main_tool.py
  26. 21 20
      mmgen/main_txbump.py
  27. 15 19
      mmgen/main_txcreate.py
  28. 17 20
      mmgen/main_txdo.py
  29. 12 12
      mmgen/main_txsend.py
  30. 21 19
      mmgen/main_txsign.py
  31. 30 23
      mmgen/main_wallet.py
  32. 22 16
      mmgen/main_xmrwallet.py
  33. 9 8
      mmgen/mn_entry.py
  34. 28 12
      mmgen/msg.py
  35. 0 1
      mmgen/objmethods.py
  36. 125 121
      mmgen/opts.py
  37. 6 4
      mmgen/passwdlist.py
  38. 1 2
      mmgen/proto/btc/addrdata.py
  39. 4 5
      mmgen/proto/btc/daemon.py
  40. 3 4
      mmgen/proto/btc/misc.py
  41. 17 17
      mmgen/proto/btc/regtest.py
  42. 16 16
      mmgen/proto/btc/rpc.py
  43. 5 6
      mmgen/proto/btc/tw/ctl.py
  44. 2 1
      mmgen/proto/btc/tw/json.py
  45. 3 4
      mmgen/proto/btc/tw/txhistory.py
  46. 3 4
      mmgen/proto/btc/tx/base.py
  47. 9 10
      mmgen/proto/btc/tx/new.py
  48. 3 4
      mmgen/proto/btc/tx/online.py
  49. 2 2
      mmgen/proto/btc/tx/signed.py
  50. 2 3
      mmgen/proto/btc/tx/status.py
  51. 7 8
      mmgen/proto/btc/tx/unsigned.py
  52. 2 3
      mmgen/proto/eth/addrdata.py
  53. 9 8
      mmgen/proto/eth/contract.py
  54. 9 9
      mmgen/proto/eth/misc.py
  55. 3 2
      mmgen/proto/eth/msg.py
  56. 1 2
      mmgen/proto/eth/params.py
  57. 3 2
      mmgen/proto/eth/rpc.py
  58. 3 3
      mmgen/proto/eth/tw/bal.py
  59. 4 4
      mmgen/proto/eth/tw/ctl.py
  60. 1 2
      mmgen/proto/eth/tw/view.py
  61. 1 2
      mmgen/proto/eth/tx/base.py
  62. 12 13
      mmgen/proto/eth/tx/new.py
  63. 3 5
      mmgen/proto/eth/tx/online.py
  64. 3 3
      mmgen/proto/eth/tx/unsigned.py
  65. 3 4
      mmgen/proto/secp256k1/keygen.py
  66. 9 6
      mmgen/proto/xmr/daemon.py
  67. 8 8
      mmgen/proto/xmr/keygen.py
  68. 5 4
      mmgen/proto/xmr/rpc.py
  69. 1 1
      mmgen/proto/zec/keygen.py
  70. 1 2
      mmgen/proto/zec/params.py
  71. 21 15
      mmgen/protocol.py
  72. 16 10
      mmgen/rpc.py
  73. 6 6
      mmgen/seed.py
  74. 18 13
      mmgen/seedsplit.py
  75. 4 4
      mmgen/share/Opts.py
  76. 5 5
      mmgen/subseed.py
  77. 8 4
      mmgen/term.py
  78. 13 13
      mmgen/tool/api.py
  79. 6 6
      mmgen/tool/coin.py
  80. 8 8
      mmgen/tool/common.py
  81. 5 5
      mmgen/tool/file.py
  82. 6 6
      mmgen/tool/filecrypt.py
  83. 7 9
      mmgen/tool/fileutil.py
  84. 5 5
      mmgen/tool/help.py
  85. 5 6
      mmgen/tool/mnemonic.py
  86. 14 14
      mmgen/tool/rpc.py
  87. 9 9
      mmgen/tool/util.py
  88. 14 13
      mmgen/tool/wallet.py
  89. 4 5
      mmgen/tw/addresses.py
  90. 3 3
      mmgen/tw/bal.py
  91. 16 15
      mmgen/tw/ctl.py
  92. 15 14
      mmgen/tw/json.py
  93. 1 1
      mmgen/tw/prune.py
  94. 2 2
      mmgen/tw/txhistory.py
  95. 2 3
      mmgen/tw/unspent.py
  96. 14 12
      mmgen/tw/view.py
  97. 6 5
      mmgen/tx/__init__.py
  98. 7 6
      mmgen/tx/base.py
  99. 4 5
      mmgen/tx/bump.py
  100. 2 2
      mmgen/tx/completed.py
  101. 11 10
      mmgen/tx/file.py
  102. 1 2
      mmgen/tx/info.py
  103. 35 35
      mmgen/tx/new.py
  104. 3 3
      mmgen/tx/online.py
  105. 28 24
      mmgen/tx/sign.py
  106. 16 15
      mmgen/ui.py
  107. 56 57
      mmgen/util.py
  108. 5 5
      mmgen/util2.py
  109. 10 9
      mmgen/wallet/__init__.py
  110. 10 10
      mmgen/wallet/base.py
  111. 10 11
      mmgen/wallet/brain.py
  112. 10 12
      mmgen/wallet/dieroll.py
  113. 16 17
      mmgen/wallet/enc.py
  114. 15 17
      mmgen/wallet/incog_base.py
  115. 14 13
      mmgen/wallet/incog_hidden.py
  116. 17 19
      mmgen/wallet/mmgen.py
  117. 4 4
      mmgen/wallet/mmhex.py
  118. 7 8
      mmgen/wallet/mnemonic.py
  119. 1 1
      mmgen/wallet/plainhex.py
  120. 4 4
      mmgen/wallet/seed.py
  121. 3 3
      mmgen/wallet/unenc.py
  122. 44 33
      mmgen/xmrwallet.py
  123. 3 3
      scripts/compute-file-chksum.py
  124. 18 20
      scripts/create-token.py
  125. 4 4
      scripts/tx-v2-to-v3.py
  126. 5 5
      scripts/uninstall-mmgen.py
  127. 55 50
      test/gentest.py
  128. 5 1
      test/hashfunc.py
  129. 24 23
      test/include/coin_daemon_control.py
  130. 46 18
      test/include/common.py
  131. 22 24
      test/include/pexpect.py
  132. 13 13
      test/misc/cfg.py
  133. 9 10
      test/misc/get_passphrase.py
  134. 14 7
      test/misc/input_func.py
  135. 1 1
      test/misc/oneshot_warning.py
  136. 12 8
      test/misc/opts.py
  137. 10 10
      test/misc/term.py
  138. 6 6
      test/misc/term_ni.py
  139. 5 2
      test/misc/tool_api_test.py
  140. 3 3
      test/misc/utf8_output.py
  141. 15 11
      test/objattrtest.py
  142. 4 3
      test/objattrtest_py_d/oat_btc_mainnet.py
  143. 4 4
      test/objattrtest_py_d/oat_common.py
  144. 25 22
      test/objtest.py
  145. 4 3
      test/objtest_py_d/ot_btc_mainnet.py
  146. 2 1
      test/objtest_py_d/ot_btc_testnet.py
  147. 0 1
      test/objtest_py_d/ot_common.py
  148. 2 1
      test/objtest_py_d/ot_ltc_mainnet.py
  149. 2 1
      test/objtest_py_d/ot_ltc_testnet.py
  150. 1 0
      test/overlay/fakemods/mmgen/proto/btc/tw/unspent.py
  151. 16 11
      test/scrambletest.py
  152. 94 88
      test/test.py
  153. 3 2
      test/test_py_d/cfg.py
  154. 7 6
      test/test_py_d/common.py
  155. 5 6
      test/test_py_d/ts_autosign.py
  156. 5 7
      test/test_py_d/ts_base.py
  157. 3 3
      test/test_py_d/ts_cfgfile.py
  158. 0 2
      test/test_py_d/ts_chainsplit.py
  159. 33 28
      test/test_py_d/ts_ethdev.py
  160. 3 3
      test/test_py_d/ts_input.py
  161. 26 25
      test/test_py_d/ts_main.py
  162. 3 3
      test/test_py_d/ts_misc.py
  163. 17 20
      test/test_py_d/ts_opts.py
  164. 2 4
      test/test_py_d/ts_ref.py
  165. 4 6
      test/test_py_d/ts_ref_3seed.py
  166. 2 3
      test/test_py_d/ts_ref_altcoin.py
  167. 24 25
      test/test_py_d/ts_regtest.py
  168. 0 2
      test/test_py_d/ts_seedsplit.py
  169. 0 2
      test/test_py_d/ts_shared.py
  170. 2 2
      test/test_py_d/ts_tool.py
  171. 2 3
      test/test_py_d/ts_wallet.py
  172. 15 11
      test/test_py_d/ts_xmrwallet.py
  173. 43 39
      test/tooltest.py
  174. 44 40
      test/tooltest2.py
  175. 14 13
      test/unit_tests.py
  176. 3 3
      test/unit_tests_d/__init__.py
  177. 11 9
      test/unit_tests_d/ut_addrlist.py
  178. 2 1
      test/unit_tests_d/ut_addrparse.py
  179. 4 3
      test/unit_tests_d/ut_baseconv.py
  180. 2 1
      test/unit_tests_d/ut_bip39.py
  181. 4 3
      test/unit_tests_d/ut_daemon.py
  182. 2 2
      test/unit_tests_d/ut_dep.py
  183. 4 2
      test/unit_tests_d/ut_devtools.py
  184. 1 0
      test/unit_tests_d/ut_flags.py
  185. 7 6
      test/unit_tests_d/ut_gen.py
  186. 1 0
      test/unit_tests_d/ut_indexed_dict.py
  187. 1 0
      test/unit_tests_d/ut_lockable.py
  188. 4 3
      test/unit_tests_d/ut_mn_entry.py
  189. 9 8
      test/unit_tests_d/ut_msg.py
  190. 1 0
      test/unit_tests_d/ut_obj.py
  191. 23 20
      test/unit_tests_d/ut_rpc.py
  192. 11 10
      test/unit_tests_d/ut_scrypt.py
  193. 7 6
      test/unit_tests_d/ut_seedsplit.py
  194. 10 9
      test/unit_tests_d/ut_subseed.py
  195. 6 5
      test/unit_tests_d/ut_tx.py
  196. 10 10
      test/unit_tests_d/ut_tx_deserialize.py
  197. 2 1
      test/unit_tests_d/ut_xmrseed.py

+ 4 - 6
examples/halving-calculator.py

@@ -15,10 +15,9 @@ examples.halving-calculator.py: Demonstrate use of the MMGen asyncio/aiohttp JSO
 import time
 import time
 
 
 import mmgen.opts as opts
 import mmgen.opts as opts
-from mmgen.opts import opt
 from mmgen.util import async_run
 from mmgen.util import async_run
 
 
-opts.init({
+cfg = opts.init({
 	'text': {
 	'text': {
 		'desc': 'Estimate date of next block subsidy halving',
 		'desc': 'Estimate date of next block subsidy halving',
 		'usage':'[opts]',
 		'usage':'[opts]',
@@ -55,16 +54,15 @@ def time_diff_warning(t_diff):
 
 
 async def main():
 async def main():
 
 
-	from mmgen.protocol import init_proto_from_opts
-	proto = init_proto_from_opts(need_amt=True)
+	proto = cfg._proto
 
 
 	from mmgen.rpc import rpc_init
 	from mmgen.rpc import rpc_init
-	c = await rpc_init(proto)
+	c = await rpc_init(cfg,proto)
 
 
 	tip = await c.call('getblockcount')
 	tip = await c.call('getblockcount')
 	assert tip > 1, 'block tip must be > 1'
 	assert tip > 1, 'block tip must be > 1'
 	remaining = proto.halving_interval - tip % proto.halving_interval
 	remaining = proto.halving_interval - tip % proto.halving_interval
-	sample_size = int(opt.sample_size) if opt.sample_size else min(tip-1,max(remaining,144))
+	sample_size = int(cfg.sample_size) if cfg.sample_size else min(tip-1,max(remaining,144))
 
 
 	# aiohttp backend will perform these two calls concurrently:
 	# aiohttp backend will perform these two calls concurrently:
 	cur,old = await c.gathered_call('getblockstats',((tip,),(tip - sample_size,)))
 	cur,old = await c.gathered_call('getblockstats',((tip,),(tip - sample_size,)))

+ 8 - 6
mmgen/addrdata.py

@@ -20,7 +20,8 @@
 addrdata: MMGen AddrData and related classes
 addrdata: MMGen AddrData and related classes
 """
 """
 
 
-from .util import vmsg,fmt,die
+from .globalvars import gc
+from .util import fmt,die
 from .base_obj import AsyncInit
 from .base_obj import AsyncInit
 from .obj import MMGenObject,MMGenDict,get_obj
 from .obj import MMGenObject,MMGenDict,get_obj
 from .addr import MMGenID,AddrListID
 from .addr import MMGenID,AddrListID
@@ -68,16 +69,16 @@ class AddrData(MMGenObject):
 
 
 class TwAddrData(AddrData,metaclass=AsyncInit):
 class TwAddrData(AddrData,metaclass=AsyncInit):
 
 
-	def __new__(cls,proto,*args,**kwargs):
+	def __new__(cls,cfg,proto,*args,**kwargs):
 		return MMGenObject.__new__(proto.base_proto_subclass(cls,'addrdata'))
 		return MMGenObject.__new__(proto.base_proto_subclass(cls,'addrdata'))
 
 
-	async def __init__(self,proto,twctl=None):
+	async def __init__(self,cfg,proto,twctl=None):
 		from .rpc import rpc_init
 		from .rpc import rpc_init
 		from .tw.shared import TwLabel
 		from .tw.shared import TwLabel
-		from .globalvars import gc
 		from .seed import SeedID
 		from .seed import SeedID
+		self.cfg = cfg
 		self.proto = proto
 		self.proto = proto
-		self.rpc = await rpc_init(proto)
+		self.rpc = await rpc_init(cfg,proto)
 		self.al_ids = {}
 		self.al_ids = {}
 		twd = await self.get_tw_data(twctl)
 		twd = await self.get_tw_data(twctl)
 		out,i = {},0
 		out,i = {},0
@@ -96,10 +97,11 @@ class TwAddrData(AddrData,metaclass=AsyncInit):
 				out[al_id].append(AddrListEntry(self.proto,idx=obj.idx,addr=addr_array[0],comment=l.comment))
 				out[al_id].append(AddrListEntry(self.proto,idx=obj.idx,addr=addr_array[0],comment=l.comment))
 				i += 1
 				i += 1
 
 
-		vmsg(f'{i} {gc.proj_name} addresses found, {len(twd)} accounts total')
+		self.cfg._util.vmsg(f'{i} {gc.proj_name} addresses found, {len(twd)} accounts total')
 
 
 		for al_id in out:
 		for al_id in out:
 			self.add(AddrList(
 			self.add(AddrList(
+				self.cfg,
 				self.proto,
 				self.proto,
 				al_id = al_id,
 				al_id = al_id,
 				adata = AddrListData(sorted( out[al_id], key=lambda a: a.idx ))
 				adata = AddrListData(sorted( out[al_id], key=lambda a: a.idx ))

+ 19 - 20
mmgen/addrfile.py

@@ -20,8 +20,8 @@
 addrfile: Address and password file classes for the MMGen suite
 addrfile: Address and password file classes for the MMGen suite
 """
 """
 
 
-from .globalvars import g
-from .util import msg,qmsg,qmsg_r,die,capfirst
+from .globalvars import gc
+from .util import msg,die,capfirst
 from .protocol import init_proto
 from .protocol import init_proto
 from .obj import MMGenObject,TwComment,WalletPassword,MMGenPWIDString
 from .obj import MMGenObject,TwComment,WalletPassword,MMGenPWIDString
 from .seed import SeedID,is_seed_id
 from .seed import SeedID,is_seed_id
@@ -44,13 +44,13 @@ class AddrFile(MMGenObject):
 """
 """
 
 
 	def __init__(self,parent):
 	def __init__(self,parent):
-
 		self.parent = parent
 		self.parent = parent
+		self.cfg    = parent.cfg
 		self.infile = None
 		self.infile = None
 
 
 	def encrypt(self):
 	def encrypt(self):
 		from .crypto import Crypto
 		from .crypto import Crypto
-		self.fmt_data = Crypto().mmgen_encrypt(
+		self.fmt_data = Crypto(self.cfg).mmgen_encrypt(
 			data = self.fmt_data.encode(),
 			data = self.fmt_data.encode(),
 			desc = f'new {self.parent.desc} list' )
 			desc = f'new {self.parent.desc} list' )
 		self.ext += f'.{Crypto.mmenc_ext}'
 		self.ext += f'.{Crypto.mmenc_ext}'
@@ -63,13 +63,13 @@ class AddrFile(MMGenObject):
 			self.ext )
 			self.ext )
 
 
 	def write(self,fn=None,ask_tty=True,ask_write_default_yes=False,binary=False,desc=None):
 	def write(self,fn=None,ask_tty=True,ask_write_default_yes=False,binary=False,desc=None):
-		from .opts import opt
 		from .fileutil import write_data_to_file
 		from .fileutil import write_data_to_file
 		write_data_to_file(
 		write_data_to_file(
+			self.cfg,
 			fn or self.filename,
 			fn or self.filename,
 			self.fmt_data,
 			self.fmt_data,
 			desc or self.desc,
 			desc or self.desc,
-			ask_tty = self.parent.has_keys and not opt.quiet,
+			ask_tty = self.parent.has_keys and not self.cfg.quiet,
 			binary = binary )
 			binary = binary )
 
 
 	def make_label(self):
 	def make_label(self):
@@ -87,8 +87,7 @@ class AddrFile(MMGenObject):
 			self.file_header_mn.format(p.pw_fmt.upper())
 			self.file_header_mn.format(p.pw_fmt.upper())
 				if p.gen_passwds and p.pw_fmt in ('bip39','xmrseed') else
 				if p.gen_passwds and p.pw_fmt in ('bip39','xmrseed') else
 			self.file_header ).strip()
 			self.file_header ).strip()
-		from .globalvars import gc
-		out = [fh.format(pnm=gc.proj_name,n=TwComment.max_screen_width) + '\n']
+		out = [fh.format( pnm=gc.proj_name, n=TwComment.max_screen_width ) + '\n']
 
 
 		if p.chksum:
 		if p.chksum:
 			out.append(f'# {capfirst(p.desc)} data checksum for {p.id_str}: {p.chksum}')
 			out.append(f'# {capfirst(p.desc)} data checksum for {p.id_str}: {p.chksum}')
@@ -108,8 +107,7 @@ class AddrFile(MMGenObject):
 			else: # First line with idx
 			else: # First line with idx
 				out.append(fs.format(e.idx,e.addr,c))
 				out.append(fs.format(e.idx,e.addr,c))
 				if p.has_keys:
 				if p.has_keys:
-					from .opts import opt
-					if opt.b16:
+					if self.cfg.b16:
 						out.append(fs.format( '', f'orig_hex: {e.sec.orig_bytes.hex()}', c ))
 						out.append(fs.format( '', f'orig_hex: {e.sec.orig_bytes.hex()}', c ))
 					out.append(fs.format( '', f'{p.al_id.mmtype.wif_label}: {e.sec.wif}', c ))
 					out.append(fs.format( '', f'{p.al_id.mmtype.wif_label}: {e.sec.wif}', c ))
 					for k in ('viewkey','wallet_passwd'):
 					for k in ('viewkey','wallet_passwd'):
@@ -160,21 +158,21 @@ class AddrFile(MMGenObject):
 
 
 			def verify_keys():
 			def verify_keys():
 				from .addrgen import KeyGenerator,AddrGenerator
 				from .addrgen import KeyGenerator,AddrGenerator
-				kg = KeyGenerator(p.proto,p.al_id.mmtype.pubkey_type)
-				ag = AddrGenerator(p.proto,p.al_id.mmtype)
+				kg = KeyGenerator( self.cfg, p.proto, p.al_id.mmtype.pubkey_type )
+				ag = AddrGenerator( self.cfg, p.proto, p.al_id.mmtype )
 				llen = len(ret)
 				llen = len(ret)
+				qmsg_r = p.cfg._util.qmsg_r
 				for n,e in enumerate(ret):
 				for n,e in enumerate(ret):
 					qmsg_r(f'\rVerifying keys {n+1}/{llen}')
 					qmsg_r(f'\rVerifying keys {n+1}/{llen}')
 					assert e.addr == ag.to_addr(kg.gen_data(e.sec)),(
 					assert e.addr == ag.to_addr(kg.gen_data(e.sec)),(
 						f'Key doesn’t match address!\n  {e.sec.wif}\n  {e.addr}')
 						f'Key doesn’t match address!\n  {e.sec.wif}\n  {e.addr}')
-				qmsg(' - done')
+				p.cfg._util.qmsg(' - done')
 
 
-			from .opts import opt
-			if opt.yes or p.ka_validity_chk == True:
+			if self.cfg.yes or p.ka_validity_chk == True:
 				verify_keys()
 				verify_keys()
 			else:
 			else:
 				from .ui import keypress_confirm
 				from .ui import keypress_confirm
-				if keypress_confirm('Check key-to-address validity?'):
+				if keypress_confirm( p.cfg, 'Check key-to-address validity?' ):
 					verify_keys()
 					verify_keys()
 
 
 		return ret
 		return ret
@@ -216,7 +214,7 @@ class AddrFile(MMGenObject):
 			else:            # only component is coin
 			else:            # only component is coin
 				coin,mmtype_key = ( lbl, None )
 				coin,mmtype_key = ( lbl, None )
 
 
-			proto = init_proto(coin=coin,network=network)
+			proto = init_proto( p.cfg, coin=coin, network=network )
 
 
 			if mmtype_key == None:
 			if mmtype_key == None:
 				mmtype_key = proto.mmtypes[0]
 				mmtype_key = proto.mmtypes[0]
@@ -224,8 +222,9 @@ class AddrFile(MMGenObject):
 			return ( proto, proto.addr_type(mmtype_key) )
 			return ( proto, proto.addr_type(mmtype_key) )
 
 
 		p = self.parent
 		p = self.parent
+
 		from .fileutil import get_lines_from_file
 		from .fileutil import get_lines_from_file
-		lines = get_lines_from_file(fn,p.desc+' data',trim_comments=True)
+		lines = get_lines_from_file( p.cfg, fn, p.desc+' data', trim_comments=True )
 
 
 		try:
 		try:
 			assert len(lines) >= 3, f'Too few lines in address file ({len(lines)})'
 			assert len(lines) >= 3, f'Too few lines in address file ({len(lines)})'
@@ -246,12 +245,12 @@ class AddrFile(MMGenObject):
 				modname,funcname = p.pw_info[p.pw_fmt].chk_func.split('.')
 				modname,funcname = p.pw_info[p.pw_fmt].chk_func.split('.')
 				import importlib
 				import importlib
 				p.chk_func = getattr(importlib.import_module('mmgen.'+modname),funcname)
 				p.chk_func = getattr(importlib.import_module('mmgen.'+modname),funcname)
-				proto = init_proto('btc') # FIXME: dummy protocol
+				proto = init_proto( p.cfg, 'btc' ) # FIXME: dummy protocol
 				mmtype = MMGenPasswordType(proto,'P')
 				mmtype = MMGenPasswordType(proto,'P')
 			elif len(ls) == 1:
 			elif len(ls) == 1:
 				proto,mmtype = parse_addrfile_label(ls[0])
 				proto,mmtype = parse_addrfile_label(ls[0])
 			elif len(ls) == 0:
 			elif len(ls) == 0:
-				proto = init_proto('btc')
+				proto = init_proto( p.cfg, 'btc' )
 				mmtype = proto.addr_type('L')
 				mmtype = proto.addr_type('L')
 			else:
 			else:
 				raise ValueError(f'{lines[0]}: Invalid first line for {p.gen_desc} file {fn!r}')
 				raise ValueError(f'{lines[0]}: Invalid first line for {p.gen_desc} file {fn!r}')

+ 6 - 6
mmgen/addrgen.py

@@ -36,7 +36,7 @@ class addr_generator:
 
 
 	class base:
 	class base:
 
 
-		def __init__(self,proto,addr_type):
+		def __init__(self,cfg,proto,addr_type):
 			self.proto = proto
 			self.proto = proto
 			self.pubkey_type = addr_type.pubkey_type
 			self.pubkey_type = addr_type.pubkey_type
 			self.compressed = addr_type.compressed
 			self.compressed = addr_type.compressed
@@ -44,12 +44,12 @@ class addr_generator:
 
 
 	class keccak(base):
 	class keccak(base):
 
 
-		def __init__(self,proto,addr_type):
-			super().__init__(proto,addr_type)
+		def __init__(self,cfg,proto,addr_type):
+			super().__init__(cfg,proto,addr_type)
 			from .util2 import get_keccak
 			from .util2 import get_keccak
-			self.keccak_256 = get_keccak()
+			self.keccak_256 = get_keccak(cfg)
 
 
-def AddrGenerator(proto,addr_type):
+def AddrGenerator(cfg,proto,addr_type):
 	"""
 	"""
 	factory function returning an address generator for the specified address type
 	factory function returning an address generator for the specified address type
 	"""
 	"""
@@ -76,4 +76,4 @@ def AddrGenerator(proto,addr_type):
 	import importlib
 	import importlib
 	return getattr(
 	return getattr(
 		importlib.import_module(f'mmgen.proto.{package_map[addr_type.name]}.addrgen'),
 		importlib.import_module(f'mmgen.proto.{package_map[addr_type.name]}.addrgen'),
-		addr_type.name )(proto,addr_type)
+		addr_type.name )(cfg,proto,addr_type)

+ 16 - 15
mmgen/addrlist.py

@@ -20,8 +20,7 @@
 addrlist: Address list classes for the MMGen suite
 addrlist: Address list classes for the MMGen suite
 """
 """
 
 
-from .globalvars import g
-from .util import qmsg,qmsg_r,suf,make_chksum_N,Msg,die
+from .util import suf,make_chksum_N,Msg,die
 from .objmethods import MMGenObject,Hilite,InitErrors
 from .objmethods import MMGenObject,Hilite,InitErrors
 from .obj import MMGenListItem,ListItemAttr,MMGenDict,TwComment,WalletPassword
 from .obj import MMGenListItem,ListItemAttr,MMGenDict,TwComment,WalletPassword
 from .key import PrivKey
 from .key import PrivKey
@@ -158,6 +157,7 @@ class AddrList(MMGenObject): # Address info for a single seed ID
 
 
 	def __init__(
 	def __init__(
 			self,
 			self,
+			cfg,
 			proto,
 			proto,
 			addrfile  = '',
 			addrfile  = '',
 			al_id     = '',
 			al_id     = '',
@@ -173,12 +173,13 @@ class AddrList(MMGenObject): # Address info for a single seed ID
 			skip_chksum_msg = False,
 			skip_chksum_msg = False,
 			add_p2pkh = False ):
 			add_p2pkh = False ):
 
 
+		self.cfg = cfg
 		self.ka_validity_chk = key_address_validity_check
 		self.ka_validity_chk = key_address_validity_check
 		self.add_p2pkh = add_p2pkh
 		self.add_p2pkh = add_p2pkh
 		self.proto = proto
 		self.proto = proto
 		do_chksum = False
 		do_chksum = False
 
 
-		if not g.debug_addrlist:
+		if not cfg.debug_addrlist:
 			self.dmsg_sc = self.noop
 			self.dmsg_sc = self.noop
 
 
 		if seed and addr_idxs:   # data from seed + idxs
 		if seed and addr_idxs:   # data from seed + idxs
@@ -230,7 +231,7 @@ class AddrList(MMGenObject): # Address info for a single seed ID
 	def do_chksum_msg(self,record):
 	def do_chksum_msg(self,record):
 		chk = 'Check this value against your records'
 		chk = 'Check this value against your records'
 		rec = f'Record this checksum: it will be used to verify the {self.desc} file in the future'
 		rec = f'Record this checksum: it will be used to verify the {self.desc} file in the future'
-		qmsg(
+		self.cfg._util.qmsg(
 			f'Checksum for {self.desc} data {self.id_str.hl()}: {self.chksum.hl()}\n' +
 			f'Checksum for {self.desc} data {self.id_str.hl()}: {self.chksum.hl()}\n' +
 			(chk,rec)[record] )
 			(chk,rec)[record] )
 
 
@@ -246,23 +247,23 @@ class AddrList(MMGenObject): # Address info for a single seed ID
 
 
 		if self.gen_addrs:
 		if self.gen_addrs:
 			from .addrgen import KeyGenerator,AddrGenerator
 			from .addrgen import KeyGenerator,AddrGenerator
-			kg = KeyGenerator( self.proto, mmtype.pubkey_type )
-			ag = AddrGenerator( self.proto, mmtype )
+			kg = KeyGenerator( self.cfg, self.proto, mmtype.pubkey_type )
+			ag = AddrGenerator( self.cfg, self.proto, mmtype )
 			if self.add_p2pkh:
 			if self.add_p2pkh:
-				ag2 = AddrGenerator( self.proto, 'compressed' )
+				ag2 = AddrGenerator( self.cfg, self.proto, 'compressed' )
 
 
-		from .globalvars import g
 		from .derive import derive_coin_privkey_bytes
 		from .derive import derive_coin_privkey_bytes
 
 
 		t_addrs = len(addr_idxs)
 		t_addrs = len(addr_idxs)
 		le = self.entry_type
 		le = self.entry_type
 		out = AddrListData()
 		out = AddrListData()
-		CR = '\n' if g.debug_addrlist else '\r'
+		CR = '\n' if self.cfg.debug_addrlist else '\r'
 
 
 		for pk_bytes in derive_coin_privkey_bytes(seed,addr_idxs):
 		for pk_bytes in derive_coin_privkey_bytes(seed,addr_idxs):
 
 
-			if not g.debug:
-				qmsg_r(f'{CR}Generating {self.gen_desc} #{pk_bytes.idx} ({pk_bytes.pos} of {t_addrs})')
+			if not self.cfg.debug:
+				self.cfg._util.qmsg_r(
+					f'{CR}Generating {self.gen_desc} #{pk_bytes.idx} ({pk_bytes.pos} of {t_addrs})' )
 
 
 			e = le( proto=self.proto, idx=pk_bytes.idx )
 			e = le( proto=self.proto, idx=pk_bytes.idx )
 
 
@@ -286,7 +287,7 @@ class AddrList(MMGenObject): # Address info for a single seed ID
 
 
 			out.append(e)
 			out.append(e)
 
 
-		qmsg('{}{}: {} {}{} generated{}'.format(
+		self.cfg._util.qmsg('{}{}: {} {}{} generated{}'.format(
 			CR,
 			CR,
 			self.al_id.hl(),
 			self.al_id.hl(),
 			t_addrs,
 			t_addrs,
@@ -316,7 +317,7 @@ class AddrList(MMGenObject): # Address info for a single seed ID
 		if self.proto.testnet:
 		if self.proto.testnet:
 			scramble_key += ':' + self.proto.network
 			scramble_key += ':' + self.proto.network
 		self.dmsg_sc('str',scramble_key)
 		self.dmsg_sc('str',scramble_key)
-		return Crypto().scramble_seed(seed,scramble_key.encode())
+		return Crypto(self.cfg).scramble_seed(seed,scramble_key.encode())
 
 
 	def idxs(self):
 	def idxs(self):
 		return [e.idx for e in self.data]
 		return [e.idx for e in self.data]
@@ -371,8 +372,8 @@ class AddrList(MMGenObject): # Address info for a single seed ID
 		def gen_addr(pk,t):
 		def gen_addr(pk,t):
 			at = self.proto.addr_type(t)
 			at = self.proto.addr_type(t)
 			from .addrgen import KeyGenerator,AddrGenerator
 			from .addrgen import KeyGenerator,AddrGenerator
-			kg = KeyGenerator(self.proto,at.pubkey_type)
-			ag = AddrGenerator(self.proto,at)
+			kg = KeyGenerator( self.cfg, self.proto, at.pubkey_type )
+			ag = AddrGenerator( self.cfg, self.proto, at )
 			return ag.to_addr(kg.gen_data(pk))
 			return ag.to_addr(kg.gen_data(pk))
 
 
 		compressed_types = set(self.proto.mmtypes) - {'L','E'}
 		compressed_types = set(self.proto.mmtypes) - {'L','E'}

+ 6 - 6
mmgen/altcoin.py

@@ -434,14 +434,14 @@ class CoinInfo(object):
 					test_equal('P2SH leading symbol',vn_info[1],ret,*cdata)
 					test_equal('P2SH leading symbol',vn_info[1],ret,*cdata)
 
 
 	@classmethod
 	@classmethod
-	def verify_core_coin_data(cls,quiet=False,verbose=False):
+	def verify_core_coin_data(cls,cfg,quiet=False,verbose=False):
 		from .protocol import CoinProtocol,init_proto
 		from .protocol import CoinProtocol,init_proto
 
 
 		for network in ('mainnet','testnet'):
 		for network in ('mainnet','testnet'):
 			for coin in gc.core_coins:
 			for coin in gc.core_coins:
 				e = cls.get_entry(coin,network)
 				e = cls.get_entry(coin,network)
 				if e:
 				if e:
-					proto = init_proto(coin,testnet=network=='testnet')
+					proto = init_proto( cfg, coin, network=network )
 					cdata = (network,coin,e,type(proto).__name__,verbose)
 					cdata = (network,coin,e,type(proto).__name__,verbose)
 					if not quiet:
 					if not quiet:
 						msg(f'Verifying {coin.upper()} {network}')
 						msg(f'Verifying {coin.upper()} {network}')
@@ -791,11 +791,11 @@ if __name__ == '__main__':
 		}
 		}
 	}
 	}
 
 
-	from mmgen.opts import init,opt
-	init( opts_data )
+	from mmgen.opts import init
+	cfg = init( opts_data, need_amt=False )
 
 
 	msg('Checking CoinInfo WIF/P2PKH/P2SH version numbers and trust levels against protocol.py')
 	msg('Checking CoinInfo WIF/P2PKH/P2SH version numbers and trust levels against protocol.py')
-	CoinInfo.verify_core_coin_data( quiet=opt.quiet, verbose=opt.verbose )
+	CoinInfo.verify_core_coin_data( cfg, cfg.quiet, cfg.verbose )
 
 
 	msg('Checking CoinInfo address leading symbols')
 	msg('Checking CoinInfo address leading symbols')
-	CoinInfo.verify_leading_symbols( quiet=opt.quiet, verbose=opt.verbose )
+	CoinInfo.verify_leading_symbols( cfg.quiet, cfg.verbose )

+ 2 - 3
mmgen/baseconv.py

@@ -100,14 +100,13 @@ class baseconv(object):
 		from hashlib import sha256
 		from hashlib import sha256
 		return sha256( ' '.join(self.digits).encode() ).hexdigest()[:8]
 		return sha256( ' '.join(self.digits).encode() ).hexdigest()[:8]
 
 
-	def check_wordlist(self):
+	def check_wordlist(self,cfg):
 
 
 		wl = self.digits
 		wl = self.digits
-		from .util import qmsg,compare_chksums
 		ret = f'Wordlist: {self.wl_id}\nLength: {len(wl)} words'
 		ret = f'Wordlist: {self.wl_id}\nLength: {len(wl)} words'
 		new_chksum = self.get_wordlist_chksum()
 		new_chksum = self.get_wordlist_chksum()
 
 
-		compare_chksums( new_chksum, 'generated', self.wl_chksum, 'saved', die_on_fail=True )
+		cfg._util.compare_chksums( new_chksum, 'generated', self.wl_chksum, 'saved', die_on_fail=True )
 
 
 		if tuple(sorted(wl)) == wl:
 		if tuple(sorted(wl)) == wl:
 			return ret + '\nList is sorted'
 			return ret + '\nList is sorted'

+ 17 - 14
mmgen/cfgfile.py

@@ -23,11 +23,10 @@ cfgfile: API for the MMGen runtime configuration file and related files
 import os,re
 import os,re
 from collections import namedtuple
 from collections import namedtuple
 
 
-from .globalvars import g,gc
 from .util import msg,ymsg,suf,fmt,fmt_list,oneshot_warning,strip_comment,capfirst
 from .util import msg,ymsg,suf,fmt,fmt_list,oneshot_warning,strip_comment,capfirst
 
 
-def mmgen_cfg_file(id_str):
-	return cfg_file.get_cls_by_id(id_str)()
+def mmgen_cfg_file(cfg,id_str):
+	return cfg_file.get_cls_by_id(id_str)(cfg)
 
 
 class cfg_file:
 class cfg_file:
 	cur_ver = 2
 	cur_ver = 2
@@ -53,7 +52,7 @@ class cfg_file:
 
 
 	def copy_system_data(self,fn):
 	def copy_system_data(self,fn):
 		assert self.write_ok, f'writing to file {fn!r} not allowed!'
 		assert self.write_ok, f'writing to file {fn!r} not allowed!'
-		src = mmgen_cfg_file('sys')
+		src = mmgen_cfg_file(self.cfg,'sys')
 		if src.data:
 		if src.data:
 			data = src.data + src.make_metadata() if self.write_metadata else src.data
 			data = src.data + src.make_metadata() if self.write_metadata else src.data
 			try:
 			try:
@@ -169,8 +168,9 @@ class CfgFileUsr(cfg_file):
 	warn_missing = False
 	warn_missing = False
 	write_ok = True
 	write_ok = True
 
 
-	def __init__(self):
-		self.fn = os.path.join(g.data_dir_root,self.fn_base)
+	def __init__(self,cfg):
+		self.cfg = cfg
+		self.fn = os.path.join(cfg.data_dir_root,self.fn_base)
 		self.data = self.get_data(self.fn)
 		self.data = self.get_data(self.fn)
 		if not self.data:
 		if not self.data:
 			self.copy_system_data(self.fn)
 			self.copy_system_data(self.fn)
@@ -179,14 +179,16 @@ class CfgFileSampleSys(cfg_file_sample):
 	desc = 'system sample configuration file'
 	desc = 'system sample configuration file'
 	test_fn_subdir = 'usr.local.share'
 	test_fn_subdir = 'usr.local.share'
 
 
-	def __init__(self):
-		if g.test_suite_cfgtest:
-			self.fn = os.path.join(g.data_dir_root,self.test_fn_subdir,self.fn_base)
+	def __init__(self,cfg):
+		self.cfg = cfg
+		if self.cfg.test_suite_cfgtest:
+			self.fn = os.path.join(cfg.data_dir_root,self.test_fn_subdir,self.fn_base)
 			with open(self.fn) as fp:
 			with open(self.fn) as fp:
 				self.data = fp.read().splitlines()
 				self.data = fp.read().splitlines()
 		else:
 		else:
 			# self.fn is used for error msgs only, so file need not exist on filesystem
 			# 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.fn = os.path.join(os.path.dirname(__file__),'data',self.fn_base)
+			from .globalvars import gc
 			self.data = gc.get_mmgen_data_file(self.fn_base).splitlines()
 			self.data = gc.get_mmgen_data_file(self.fn_base).splitlines()
 
 
 	def make_metadata(self):
 	def make_metadata(self):
@@ -202,11 +204,12 @@ class CfgFileSampleUsr(cfg_file_sample):
 	out_of_date_fs = 'File {!r} is out of date - replacing'
 	out_of_date_fs = 'File {!r} is out of date - replacing'
 	altered_by_user_fs = 'File {!r} was altered by user - replacing'
 	altered_by_user_fs = 'File {!r} was altered by user - replacing'
 
 
-	def __init__(self):
-		self.fn = os.path.join(g.data_dir_root,f'{self.fn_base}.sample')
+	def __init__(self,cfg):
+		self.cfg = cfg
+		self.fn = os.path.join(cfg.data_dir_root,f'{self.fn_base}.sample')
 		self.data = self.get_data(self.fn)
 		self.data = self.get_data(self.fn)
 
 
-		src = mmgen_cfg_file('sys')
+		src = mmgen_cfg_file(cfg,'sys')
 
 
 		if not src.data:
 		if not src.data:
 			return
 			return
@@ -255,7 +258,7 @@ class CfgFileSampleUsr(cfg_file_sample):
 				opts = fmt_list([i.name for i in data],fmt='bare')
 				opts = fmt_list([i.name for i in data],fmt='bare')
 				msg(f'  The following option{suf(data,verb="has")} been {desc}:\n    {opts}\n')
 				msg(f'  The following option{suf(data,verb="has")} been {desc}:\n    {opts}\n')
 				if desc == 'removed' and data:
 				if desc == 'removed' and data:
-					uc = mmgen_cfg_file('usr')
+					uc = mmgen_cfg_file(self.cfg,'usr')
 					usr_names = [i.name for i in uc.get_lines()]
 					usr_names = [i.name for i in uc.get_lines()]
 					rm_names = [i.name for i in data]
 					rm_names = [i.name for i in data]
 					bad = sorted(set(usr_names).intersection(rm_names))
 					bad = sorted(set(usr_names).intersection(rm_names))
@@ -269,7 +272,7 @@ class CfgFileSampleUsr(cfg_file_sample):
 
 
 		from .ui import keypress_confirm,do_pager
 		from .ui import keypress_confirm,do_pager
 		while True:
 		while True:
-			if not keypress_confirm(self.details_confirm_prompt,no_nl=True):
+			if not keypress_confirm( self.cfg, self.details_confirm_prompt, no_nl=True ):
 				return
 				return
 
 
 			def get_details():
 			def get_details():

+ 0 - 1
mmgen/common.py

@@ -23,5 +23,4 @@ common: Common imports for all MMGen scripts
 import sys,os
 import sys,os
 from .globalvars import *
 from .globalvars import *
 import mmgen.opts as opts
 import mmgen.opts as opts
-from .opts import opt
 from .util import *
 from .util import *

+ 50 - 51
mmgen/crypto.py

@@ -23,23 +23,16 @@ crypto: Random number, password hashing and symmetric encryption routines for th
 import os
 import os
 from collections import namedtuple
 from collections import namedtuple
 
 
-from .globalvars import g,gc
-from .opts import opt
+from .globalvars import gc
 from .util import (
 from .util import (
 	msg,
 	msg,
 	msg_r,
 	msg_r,
-	dmsg,
-	vmsg,
-	vmsg_r,
-	qmsg,
 	fmt,
 	fmt,
 	die,
 	die,
 	make_chksum_8,
 	make_chksum_8,
-	compare_chksums,
 	oneshot_warning,
 	oneshot_warning,
 )
 )
 
 
-
 class Crypto:
 class Crypto:
 
 
 	mmenc_ext = 'mmenc'
 	mmenc_ext = 'mmenc'
@@ -72,6 +65,10 @@ class Crypto:
 		def __init__(self,fn):
 		def __init__(self,fn):
 			oneshot_warning.__init__(self,div=fn,fmt_args=[fn],reverse=True)
 			oneshot_warning.__init__(self,div=fn,fmt_args=[fn],reverse=True)
 
 
+	def __init__(self,cfg):
+		self.cfg = cfg
+		self.util = cfg._util
+
 	def get_hash_params(self,hash_preset):
 	def get_hash_params(self,hash_preset):
 		if hash_preset in self.hash_presets:
 		if hash_preset in self.hash_presets:
 			return self.hash_presets[hash_preset] # N,r,p
 			return self.hash_presets[hash_preset] # N,r,p
@@ -87,7 +84,7 @@ class Crypto:
 	def scramble_seed(self,seed,scramble_key):
 	def scramble_seed(self,seed,scramble_key):
 		import hmac
 		import hmac
 		step1 = hmac.digest(seed,scramble_key,'sha256')
 		step1 = hmac.digest(seed,scramble_key,'sha256')
-		if g.debug:
+		if self.cfg.debug:
 			msg(f'Seed:  {seed.hex()!r}\nScramble key: {scramble_key}\nScrambled seed: {step1.hex()}\n')
 			msg(f'Seed:  {seed.hex()!r}\nScramble key: {scramble_key}\nScrambled seed: {step1.hex()}\n')
 		return self.sha256_rounds(step1)
 		return self.sha256_rounds(step1)
 
 
@@ -95,56 +92,56 @@ class Crypto:
 		return self.encrypt_data(data,key,desc=desc)
 		return self.encrypt_data(data,key,desc=desc)
 
 
 	def decrypt_seed(self,enc_seed,key,seed_id,key_id):
 	def decrypt_seed(self,enc_seed,key,seed_id,key_id):
-		vmsg_r('Checking key...')
+		self.util.vmsg_r('Checking key...')
 		chk1 = make_chksum_8(key)
 		chk1 = make_chksum_8(key)
 		if key_id:
 		if key_id:
-			if not compare_chksums(key_id,'key ID',chk1,'computed'):
+			if not self.util.compare_chksums(key_id,'key ID',chk1,'computed'):
 				msg('Incorrect passphrase or hash preset')
 				msg('Incorrect passphrase or hash preset')
 				return False
 				return False
 
 
 		dec_seed = self.decrypt_data(enc_seed,key,desc='seed')
 		dec_seed = self.decrypt_data(enc_seed,key,desc='seed')
 		chk2     = make_chksum_8(dec_seed)
 		chk2     = make_chksum_8(dec_seed)
 		if seed_id:
 		if seed_id:
-			if compare_chksums(seed_id,'Seed ID',chk2,'decrypted seed'):
-				qmsg('Passphrase is OK')
+			if self.util.compare_chksums(seed_id,'Seed ID',chk2,'decrypted seed'):
+				self.util.qmsg('Passphrase is OK')
 			else:
 			else:
-				if not opt.debug:
+				if not self.cfg.debug:
 					msg_r('Checking key ID...')
 					msg_r('Checking key ID...')
-					if compare_chksums(key_id,'key ID',chk1,'computed'):
+					if self.util.compare_chksums(key_id,'key ID',chk1,'computed'):
 						msg('Key ID is correct but decryption of seed failed')
 						msg('Key ID is correct but decryption of seed failed')
 					else:
 					else:
 						msg('Incorrect passphrase or hash preset')
 						msg('Incorrect passphrase or hash preset')
-				vmsg('')
+				self.util.vmsg('')
 				return False
 				return False
 
 
-		dmsg(f'Decrypted seed: {dec_seed.hex()}')
+		self.util.dmsg(f'Decrypted seed: {dec_seed.hex()}')
 		return dec_seed
 		return dec_seed
 
 
 	def encrypt_data(self,data,key,iv=aesctr_dfl_iv,desc='data',verify=True,silent=False):
 	def encrypt_data(self,data,key,iv=aesctr_dfl_iv,desc='data',verify=True,silent=False):
 		from cryptography.hazmat.primitives.ciphers import Cipher,algorithms,modes
 		from cryptography.hazmat.primitives.ciphers import Cipher,algorithms,modes
 		from cryptography.hazmat.backends import default_backend
 		from cryptography.hazmat.backends import default_backend
 		if not silent:
 		if not silent:
-			vmsg(f'Encrypting {desc}')
+			self.util.vmsg(f'Encrypting {desc}')
 		c = Cipher(algorithms.AES(key),modes.CTR(iv),backend=default_backend())
 		c = Cipher(algorithms.AES(key),modes.CTR(iv),backend=default_backend())
 		encryptor = c.encryptor()
 		encryptor = c.encryptor()
 		enc_data = encryptor.update(data) + encryptor.finalize()
 		enc_data = encryptor.update(data) + encryptor.finalize()
 
 
 		if verify:
 		if verify:
-			vmsg_r(f'Performing a test decryption of the {desc}...')
+			self.util.vmsg_r(f'Performing a test decryption of the {desc}...')
 			c = Cipher(algorithms.AES(key),modes.CTR(iv),backend=default_backend())
 			c = Cipher(algorithms.AES(key),modes.CTR(iv),backend=default_backend())
 			encryptor = c.encryptor()
 			encryptor = c.encryptor()
 			dec_data = encryptor.update(enc_data) + encryptor.finalize()
 			dec_data = encryptor.update(enc_data) + encryptor.finalize()
 			if dec_data != data:
 			if dec_data != data:
 				die(2,f'ERROR.\nDecrypted {desc} doesn’t match original {desc}')
 				die(2,f'ERROR.\nDecrypted {desc} doesn’t match original {desc}')
 			if not silent:
 			if not silent:
-				vmsg('done')
+				self.util.vmsg('done')
 
 
 		return enc_data
 		return enc_data
 
 
 	def decrypt_data(self,enc_data,key,iv=aesctr_dfl_iv,desc='data'):
 	def decrypt_data(self,enc_data,key,iv=aesctr_dfl_iv,desc='data'):
 		from cryptography.hazmat.primitives.ciphers import Cipher,algorithms,modes
 		from cryptography.hazmat.primitives.ciphers import Cipher,algorithms,modes
 		from cryptography.hazmat.backends import default_backend
 		from cryptography.hazmat.backends import default_backend
-		vmsg_r(f'Decrypting {desc} with key...')
+		self.util.vmsg_r(f'Decrypting {desc} with key...')
 		c = Cipher(algorithms.AES(key),modes.CTR(iv),backend=default_backend())
 		c = Cipher(algorithms.AES(key),modes.CTR(iv),backend=default_backend())
 		encryptor = c.encryptor()
 		encryptor = c.encryptor()
 		return encryptor.update(enc_data) + encryptor.finalize()
 		return encryptor.update(enc_data) + encryptor.finalize()
@@ -184,7 +181,7 @@ class Crypto:
 
 
 		# hashlib.scrypt doesn't support N > 14 (hash preset > 3)
 		# hashlib.scrypt doesn't support N > 14 (hash preset > 3)
 		ret = (
 		ret = (
-			do_standalone_scrypt() if ps.N > 14 or g.force_standalone_scrypt_module else
+			do_standalone_scrypt() if ps.N > 14 or self.cfg.force_standalone_scrypt_module else
 			do_hashlib_scrypt() )
 			do_hashlib_scrypt() )
 
 
 		if int(hash_preset) > 3:
 		if int(hash_preset) > 3:
@@ -193,17 +190,17 @@ class Crypto:
 		return ret
 		return ret
 
 
 	def make_key(self,passwd,salt,hash_preset,desc='encryption key',from_what='passphrase',verbose=False):
 	def make_key(self,passwd,salt,hash_preset,desc='encryption key',from_what='passphrase',verbose=False):
-		if opt.verbose or verbose:
+		if self.cfg.verbose or verbose:
 			msg_r(f"Generating {desc}{' from ' + from_what if from_what else ''}...")
 			msg_r(f"Generating {desc}{' from ' + from_what if from_what else ''}...")
 		key = self.scrypt_hash_passphrase(passwd,salt,hash_preset)
 		key = self.scrypt_hash_passphrase(passwd,salt,hash_preset)
-		if opt.verbose or verbose: msg('done')
-		dmsg(f'Key: {key.hex()}')
+		if self.cfg.verbose or verbose: msg('done')
+		self.util.dmsg(f'Key: {key.hex()}')
 		return key
 		return key
 
 
 	def _get_random_data_from_user(self,uchars=None,desc='data'):
 	def _get_random_data_from_user(self,uchars=None,desc='data'):
 
 
 		if uchars is None:
 		if uchars is None:
-			uchars = opt.usr_randchars
+			uchars = self.cfg.usr_randchars
 
 
 		info1 = f"""
 		info1 = f"""
 			Now we're going to gather some additional input from the keyboard to further
 			Now we're going to gather some additional input from the keyboard to further
@@ -225,7 +222,7 @@ class Crypto:
 			on the screen.
 			on the screen.
 		"""
 		"""
 
 
-		msg(f'Enter {uchars} random symbols' if opt.quiet else
+		msg(f'Enter {uchars} random symbols' if self.cfg.quiet else
 			'\n' + fmt(info1,indent='  ') +
 			'\n' + fmt(info1,indent='  ') +
 			'\n' + fmt(info2) )
 			'\n' + fmt(info2) )
 
 
@@ -238,7 +235,7 @@ class Crypto:
 			key_data += get_char_raw(f'\rYou may begin typing.  {uchars-i} symbols left: ')
 			key_data += get_char_raw(f'\rYou may begin typing.  {uchars-i} symbols left: ')
 			time_data.append(time.time())
 			time_data.append(time.time())
 
 
-		msg_r( '\r' if opt.quiet else f'\rThank you.  That’s enough.{" "*18}\n\n' )
+		msg_r( '\r' if self.cfg.quiet else f'\rThank you.  That’s enough.{" "*18}\n\n' )
 
 
 		time_data = [f'{t:.22f}'.rstrip('0') for t in time_data]
 		time_data = [f'{t:.22f}'.rstrip('0') for t in time_data]
 
 
@@ -249,11 +246,11 @@ class Crypto:
 
 
 		ret = key_data + '\n' + '\n'.join(time_data)
 		ret = key_data + '\n' + '\n'.join(time_data)
 
 
-		if g.debug:
+		if self.cfg.debug:
 			msg(f'USER ENTROPY (user input + keystroke timings):\n{ret}')
 			msg(f'USER ENTROPY (user input + keystroke timings):\n{ret}')
 
 
 		from .ui import line_input
 		from .ui import line_input
-		line_input( 'User random data successfully acquired.  Press ENTER to continue: ' )
+		line_input( self.cfg, 'User random data successfully acquired.  Press ENTER to continue: ' )
 
 
 		return ret.encode()
 		return ret.encode()
 
 
@@ -274,7 +271,7 @@ class Crypto:
 
 
 		assert type(rand_bytes) == bytes, 'add_user_random_chk1'
 		assert type(rand_bytes) == bytes, 'add_user_random_chk1'
 
 
-		if opt.usr_randchars:
+		if self.cfg.usr_randchars:
 
 
 			if not urand['data']:
 			if not urand['data']:
 				from hashlib import sha256
 				from hashlib import sha256
@@ -310,7 +307,7 @@ class Crypto:
 
 
 		from .ui import line_input
 		from .ui import line_input
 		while True:
 		while True:
-			ret = line_input( prompt )
+			ret = line_input( self.cfg, prompt )
 			if ret:
 			if ret:
 				if ret in self.hash_presets:
 				if ret in self.hash_presets:
 					return ret
 					return ret
@@ -329,29 +326,30 @@ class Crypto:
 		if passwd_file:
 		if passwd_file:
 			from .fileutil import get_words_from_file
 			from .fileutil import get_words_from_file
 			pw = ' '.join(get_words_from_file(
 			pw = ' '.join(get_words_from_file(
+				cfg    = self.cfg,
 				infile = passwd_file,
 				infile = passwd_file,
 				desc   = f'{pw_desc} for {data_desc}',
 				desc   = f'{pw_desc} for {data_desc}',
 				quiet  = self.pwfile_reuse_warning(passwd_file).warning_shown ))
 				quiet  = self.pwfile_reuse_warning(passwd_file).warning_shown ))
 		else:
 		else:
-			qmsg('\n'+fmt(message,indent='  '))
+			self.util.qmsg('\n'+fmt(message,indent='  '))
 			from .ui import get_words_from_user
 			from .ui import get_words_from_user
-			if opt.echo_passphrase:
-				pw = ' '.join(get_words_from_user( f'Enter {pw_desc} for {data_desc}: ' ))
+			if self.cfg.echo_passphrase:
+				pw = ' '.join(get_words_from_user( self.cfg, f'Enter {pw_desc} for {data_desc}: ' ))
 			else:
 			else:
 				for i in range(gc.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}]')
+					pw = ' '.join(get_words_from_user( self.cfg, f'Enter {pw_desc} for {data_desc}: ' ))
+					pw_chk = ' '.join(get_words_from_user( self.cfg, f'Repeat {pw_desc}: ' ))
+					self.util.dmsg(f'Passphrases: [{pw}] [{pw_chk}]')
 					if pw == pw_chk:
 					if pw == pw_chk:
-						vmsg('Passphrases match')
+						self.util.vmsg('Passphrases match')
 						break
 						break
 					else:
 					else:
 						msg('Passphrases do not match.  Try again.')
 						msg('Passphrases do not match.  Try again.')
 				else:
 				else:
-					die(2,f'User failed to duplicate passphrase in {g.passwd_max_tries} attempts')
+					die(2,f'User failed to duplicate passphrase in {gc.passwd_max_tries} attempts')
 
 
 		if pw == '':
 		if pw == '':
-			qmsg('WARNING: Empty passphrase')
+			self.util.qmsg('WARNING: Empty passphrase')
 
 
 		return pw
 		return pw
 
 
@@ -359,48 +357,49 @@ class Crypto:
 		if passwd_file:
 		if passwd_file:
 			from .fileutil import get_words_from_file
 			from .fileutil import get_words_from_file
 			return ' '.join(get_words_from_file(
 			return ' '.join(get_words_from_file(
+				cfg    = self.cfg,
 				infile = passwd_file,
 				infile = passwd_file,
 				desc   = f'{pw_desc} for {data_desc}',
 				desc   = f'{pw_desc} for {data_desc}',
 				quiet  = self.pwfile_reuse_warning(passwd_file).warning_shown ))
 				quiet  = self.pwfile_reuse_warning(passwd_file).warning_shown ))
 		else:
 		else:
 			from .ui import get_words_from_user
 			from .ui import get_words_from_user
-			return ' '.join(get_words_from_user( f'Enter {pw_desc} for {data_desc}: ' ))
+			return ' '.join(get_words_from_user( self.cfg, f'Enter {pw_desc} for {data_desc}: ' ))
 
 
 	def mmgen_encrypt(self,data,desc='data',hash_preset=None):
 	def mmgen_encrypt(self,data,desc='data',hash_preset=None):
 		salt  = self.get_random(self.mmenc_salt_len)
 		salt  = self.get_random(self.mmenc_salt_len)
 		iv    = self.get_random(self.aesctr_iv_len)
 		iv    = self.get_random(self.aesctr_iv_len)
 		nonce = self.get_random(self.mmenc_nonce_len)
 		nonce = self.get_random(self.mmenc_nonce_len)
-		hp    = hash_preset or opt.hash_preset or self.get_hash_preset_from_user(data_desc=desc)
+		hp    = hash_preset or self.cfg.hash_preset or self.get_hash_preset_from_user(data_desc=desc)
 		m     = ('user-requested','default')[hp=='3']
 		m     = ('user-requested','default')[hp=='3']
-		vmsg(f'Encrypting {desc}')
-		qmsg(f'Using {m} hash preset of {hp!r}')
+		self.util.vmsg(f'Encrypting {desc}')
+		self.util.qmsg(f'Using {m} hash preset of {hp!r}')
 		passwd = self.get_new_passphrase(
 		passwd = self.get_new_passphrase(
 			data_desc = desc,
 			data_desc = desc,
 			hash_preset = hp,
 			hash_preset = hp,
-			passwd_file = opt.passwd_file )
+			passwd_file = self.cfg.passwd_file )
 		key    = self.make_key(passwd,salt,hp)
 		key    = self.make_key(passwd,salt,hp)
 		from hashlib import sha256
 		from hashlib import sha256
 		enc_d  = self.encrypt_data( sha256(nonce+data).digest() + nonce + data, key, iv, desc=desc )
 		enc_d  = self.encrypt_data( sha256(nonce+data).digest() + nonce + data, key, iv, desc=desc )
 		return salt+iv+enc_d
 		return salt+iv+enc_d
 
 
 	def mmgen_decrypt(self,data,desc='data',hash_preset=None):
 	def mmgen_decrypt(self,data,desc='data',hash_preset=None):
-		vmsg(f'Preparing to decrypt {desc}')
+		self.util.vmsg(f'Preparing to decrypt {desc}')
 		dstart = self.mmenc_salt_len + self.aesctr_iv_len
 		dstart = self.mmenc_salt_len + self.aesctr_iv_len
 		salt   = data[:self.mmenc_salt_len]
 		salt   = data[:self.mmenc_salt_len]
 		iv     = data[self.mmenc_salt_len:dstart]
 		iv     = data[self.mmenc_salt_len:dstart]
 		enc_d  = data[dstart:]
 		enc_d  = data[dstart:]
-		hp     = hash_preset or opt.hash_preset or self.get_hash_preset_from_user(data_desc=desc)
+		hp     = hash_preset or self.cfg.hash_preset or self.get_hash_preset_from_user(data_desc=desc)
 		m  = ('user-requested','default')[hp=='3']
 		m  = ('user-requested','default')[hp=='3']
-		qmsg(f'Using {m} hash preset of {hp!r}')
+		self.util.qmsg(f'Using {m} hash preset of {hp!r}')
 		passwd = self.get_passphrase(
 		passwd = self.get_passphrase(
 			data_desc = desc,
 			data_desc = desc,
-			passwd_file = opt.passwd_file )
+			passwd_file = self.cfg.passwd_file )
 		key    = self.make_key(passwd,salt,hp)
 		key    = self.make_key(passwd,salt,hp)
 		dec_d  = self.decrypt_data( enc_d, key, iv, desc )
 		dec_d  = self.decrypt_data( enc_d, key, iv, desc )
 		sha256_len = 32
 		sha256_len = 32
 		from hashlib import sha256
 		from hashlib import sha256
 		if dec_d[:sha256_len] == sha256(dec_d[sha256_len:]).digest():
 		if dec_d[:sha256_len] == sha256(dec_d[sha256_len:]).digest():
-			vmsg('OK')
+			self.util.vmsg('OK')
 			return dec_d[sha256_len+self.mmenc_nonce_len:]
 			return dec_d[sha256_len+self.mmenc_nonce_len:]
 		else:
 		else:
 			msg('Incorrect passphrase or hash preset')
 			msg('Incorrect passphrase or hash preset')

+ 24 - 21
mmgen/daemon.py

@@ -24,7 +24,7 @@ import os,time,importlib
 from subprocess import run,PIPE,CompletedProcess
 from subprocess import run,PIPE,CompletedProcess
 from collections import namedtuple
 from collections import namedtuple
 
 
-from .globalvars import g,gc
+from .globalvars import gc
 from .color import set_vt100
 from .color import set_vt100
 from .util import msg,Msg_r,ymsg,die,remove_dups,oneshot_warning
 from .util import msg,Msg_r,ymsg,die,remove_dups,oneshot_warning
 from .flags import *
 from .flags import *
@@ -49,8 +49,9 @@ class Daemon(Lockable):
 	avail_flags = () # like opts, but can be set or unset after instantiation
 	avail_flags = () # like opts, but can be set or unset after instantiation
 	_reset_ok = ('debug','wait','pids')
 	_reset_ok = ('debug','wait','pids')
 
 
-	def __init__(self,opts=None,flags=None):
+	def __init__(self,cfg,opts=None,flags=None):
 
 
+		self.cfg = cfg
 		self.platform = gc.platform
 		self.platform = gc.platform
 		if self.platform == 'win':
 		if self.platform == 'win':
 			self.use_pidfile = False
 			self.use_pidfile = False
@@ -237,8 +238,8 @@ class RPCDaemon(Daemon):
 
 
 	avail_opts = ('no_daemonize',)
 	avail_opts = ('no_daemonize',)
 
 
-	def __init__(self):
-		super().__init__()
+	def __init__(self,cfg):
+		super().__init__(cfg)
 		self.desc = '{} {} {}RPC daemon'.format(
 		self.desc = '{} {} {}RPC daemon'.format(
 			self.rpc_type,
 			self.rpc_type,
 			getattr(self.proto.network_names,self.proto.network),
 			getattr(self.proto.network_names,self.proto.network),
@@ -280,13 +281,13 @@ class CoinDaemon(Daemon):
 		message = 'blacklisted daemon: {!r}'
 		message = 'blacklisted daemon: {!r}'
 
 
 	@classmethod
 	@classmethod
-	def get_daemon_ids(cls,coin):
+	def get_daemon_ids(cls,cfg,coin):
 
 
 		ret = cls.coins[coin].daemon_ids
 		ret = cls.coins[coin].daemon_ids
-		if 'erigon' in ret and not g.enable_erigon:
+		if 'erigon' in ret and not cfg.enable_erigon:
 			ret.remove('erigon')
 			ret.remove('erigon')
-		if g.blacklisted_daemons:
-			blacklist = g.blacklisted_daemons.split()
+		if cfg.blacklisted_daemons:
+			blacklist = cfg.blacklisted_daemons.split()
 			def gen():
 			def gen():
 				for daemon_id in ret:
 				for daemon_id in ret:
 					if daemon_id in blacklist:
 					if daemon_id in blacklist:
@@ -297,21 +298,21 @@ class CoinDaemon(Daemon):
 		return ret
 		return ret
 
 
 	@classmethod
 	@classmethod
-	def get_daemon(cls,coin,daemon_id,proto=None):
+	def get_daemon(cls,cfg,coin,daemon_id,proto=None):
 		if not proto:
 		if not proto:
 			from .protocol import init_proto
 			from .protocol import init_proto
-			proto = init_proto(coin)
+			proto = init_proto( cfg, coin )
 		return getattr(
 		return getattr(
 			importlib.import_module(f'mmgen.proto.{proto.base_proto_coin.lower()}.daemon'),
 			importlib.import_module(f'mmgen.proto.{proto.base_proto_coin.lower()}.daemon'),
 			daemon_id+'_daemon' )
 			daemon_id+'_daemon' )
 
 
 	@classmethod
 	@classmethod
-	def get_network_ids(cls):
+	def get_network_ids(cls,cfg):
 		from .protocol import CoinProtocol
 		from .protocol import CoinProtocol
 		def gen():
 		def gen():
 			for coin in cls.coins:
 			for coin in cls.coins:
-				for daemon_id in cls.get_daemon_ids(coin):
-					for network in cls.get_daemon(coin,daemon_id).networks:
+				for daemon_id in cls.get_daemon_ids(cfg,coin):
+					for network in cls.get_daemon( cfg, coin, daemon_id ).networks:
 						yield CoinProtocol.Base.create_network_id(coin,network)
 						yield CoinProtocol.Base.create_network_id(coin,network)
 		return remove_dups(list(gen()),quiet=True)
 		return remove_dups(list(gen()),quiet=True)
 
 
@@ -329,6 +330,7 @@ class CoinDaemon(Daemon):
 			return ( res[0] if len(res) == 1 else [s for s in res if 'ersion' in s][0] ).strip()
 			return ( res[0] if len(res) == 1 else [s for s in res if 'ersion' in s][0] ).strip()
 
 
 	def __new__(cls,
 	def __new__(cls,
+			cfg,
 			network_id = None,
 			network_id = None,
 			proto      = None,
 			proto      = None,
 			opts       = None,
 			opts       = None,
@@ -349,19 +351,19 @@ class CoinDaemon(Daemon):
 		else:
 		else:
 			network_id = network_id.lower()
 			network_id = network_id.lower()
 			from .protocol import CoinProtocol,init_proto
 			from .protocol import CoinProtocol,init_proto
-			proto = init_proto(network_id=network_id)
+			proto = init_proto( cfg, network_id=network_id )
 			coin,network = CoinProtocol.Base.parse_network_id(network_id)
 			coin,network = CoinProtocol.Base.parse_network_id(network_id)
 			coin = coin.upper()
 			coin = coin.upper()
 
 
-		daemon_ids = cls.get_daemon_ids(coin)
+		daemon_ids = cls.get_daemon_ids(cfg,coin)
 		if not daemon_ids:
 		if not daemon_ids:
 			die(1,f'No configured daemons for coin {coin}!')
 			die(1,f'No configured daemons for coin {coin}!')
-		daemon_id = daemon_id or g.daemon_id or daemon_ids[0]
+		daemon_id = daemon_id or cfg.daemon_id or daemon_ids[0]
 
 
 		if daemon_id not in daemon_ids:
 		if daemon_id not in daemon_ids:
 			die(1,f'{daemon_id!r}: invalid daemon_id - valid choices: {fmt_list(daemon_ids)}')
 			die(1,f'{daemon_id!r}: invalid daemon_id - valid choices: {fmt_list(daemon_ids)}')
 
 
-		me = Daemon.__new__(cls.get_daemon( None, daemon_id, proto=proto ))
+		me = Daemon.__new__(cls.get_daemon( cfg, None, daemon_id, proto=proto ))
 
 
 		assert network in me.networks, f'{network!r}: unsupported network for daemon {daemon_id}'
 		assert network in me.networks, f'{network!r}: unsupported network for daemon {daemon_id}'
 		me.network_id = network_id
 		me.network_id = network_id
@@ -373,6 +375,7 @@ class CoinDaemon(Daemon):
 		return me
 		return me
 
 
 	def __init__(self,
 	def __init__(self,
+			cfg,
 			network_id = None,
 			network_id = None,
 			proto      = None,
 			proto      = None,
 			opts       = None,
 			opts       = None,
@@ -385,7 +388,7 @@ class CoinDaemon(Daemon):
 
 
 		self.test_suite = test_suite
 		self.test_suite = test_suite
 
 
-		super().__init__(opts=opts,flags=flags)
+		super().__init__(cfg=cfg,opts=opts,flags=flags)
 
 
 		self._set_ok += ('shared_args','usr_coind_args')
 		self._set_ok += ('shared_args','usr_coind_args')
 		self.shared_args = []
 		self.shared_args = []
@@ -400,8 +403,8 @@ class CoinDaemon(Daemon):
 			'test suite ' if test_suite else '' )
 			'test suite ' if test_suite else '' )
 
 
 		# user-set values take precedence
 		# user-set values take precedence
-		self.datadir = os.path.abspath(datadir or g.daemon_data_dir or self.init_datadir())
-		self.non_dfl_datadir = bool(datadir or g.daemon_data_dir or test_suite or self.network == 'regtest')
+		self.datadir = os.path.abspath(datadir or cfg.daemon_data_dir or self.init_datadir())
+		self.non_dfl_datadir = bool(datadir or cfg.daemon_data_dir or test_suite or self.network == 'regtest')
 
 
 		# init_datadir() may have already initialized logdir
 		# init_datadir() may have already initialized logdir
 		self.logdir = os.path.abspath(getattr(self,'logdir',self.datadir))
 		self.logdir = os.path.abspath(getattr(self,'logdir',self.datadir))
@@ -409,7 +412,7 @@ class CoinDaemon(Daemon):
 		ps_adj = (port_shift or 0) + (self.test_suite_port_shift if test_suite else 0)
 		ps_adj = (port_shift or 0) + (self.test_suite_port_shift if test_suite else 0)
 
 
 		# user-set values take precedence
 		# user-set values take precedence
-		self.rpc_port = (g.rpc_port or 0) + (port_shift or 0) if g.rpc_port else ps_adj + self.get_rpc_port()
+		self.rpc_port = (cfg.rpc_port or 0) + (port_shift or 0) if cfg.rpc_port else ps_adj + self.get_rpc_port()
 		self.p2p_port = (
 		self.p2p_port = (
 			p2p_port or (
 			p2p_port or (
 				self.get_p2p_port() + ps_adj if self.get_p2p_port() and (test_suite or ps_adj) else None
 				self.get_p2p_port() + ps_adj if self.get_p2p_port() and (test_suite or ps_adj) else None

+ 1 - 1
mmgen/data/version

@@ -1 +1 @@
-13.3.dev41
+13.3.dev42

+ 33 - 30
mmgen/fileutil.py

@@ -22,12 +22,10 @@ fileutil: Routines that read, write, execute or stat files
 
 
 import sys,os
 import sys,os
 
 
-from .globalvars import g,gc
+from .globalvars import gc
 from .color import set_vt100
 from .color import set_vt100
 from .util import (
 from .util import (
 	msg,
 	msg,
-	qmsg,
-	dmsg,
 	die,
 	die,
 	get_extension,
 	get_extension,
 	is_utf8,
 	is_utf8,
@@ -114,15 +112,16 @@ def check_outfile(f,blkdev_ok=False):
 def check_outdir(f):
 def check_outdir(f):
 	return _check_file_type_and_access(f,'output directory')
 	return _check_file_type_and_access(f,'output directory')
 
 
-def get_seed_file(wallets,nargs,invoked_as=None):
+def get_seed_file(cfg,nargs,wallets=None,invoked_as=None):
+
+	wallets = wallets or cfg._args
 
 
-	from .opts import opt
 	from .filename import find_file_in_dir
 	from .filename import find_file_in_dir
 	from .wallet.mmgen import wallet
 	from .wallet.mmgen import wallet
 
 
-	wf = find_file_in_dir(wallet,g.data_dir)
+	wf = find_file_in_dir(wallet,cfg.data_dir)
 
 
-	wd_from_opt = bool(opt.hidden_incog_input_params or opt.in_fmt) # have wallet data from opt?
+	wd_from_opt = bool(cfg.hidden_incog_input_params or cfg.in_fmt) # have wallet data from opt?
 
 
 	import mmgen.opts as opts
 	import mmgen.opts as opts
 	if len(wallets) + (wd_from_opt or bool(wf)) < nargs:
 	if len(wallets) + (wd_from_opt or bool(wf)) < nargs:
@@ -132,7 +131,7 @@ def get_seed_file(wallets,nargs,invoked_as=None):
 	elif len(wallets) > nargs:
 	elif len(wallets) > nargs:
 		opts.usage()
 		opts.usage()
 	elif len(wallets) == nargs and wf and invoked_as != 'gen':
 	elif len(wallets) == nargs and wf and invoked_as != 'gen':
-		qmsg('Warning: overriding default wallet with user-supplied wallet')
+		cfg._util.qmsg('Warning: overriding default wallet with user-supplied wallet')
 
 
 	if wallets or wf:
 	if wallets or wf:
 		check_infile(wallets[0] if wallets else wf)
 		check_infile(wallets[0] if wallets else wf)
@@ -150,6 +149,7 @@ def _open_or_die(filename,mode,silent=False):
 			))
 			))
 
 
 def write_data_to_file(
 def write_data_to_file(
+		cfg,
 		outfile,
 		outfile,
 		data,
 		data,
 		desc                  = 'data',
 		desc                  = 'data',
@@ -165,25 +165,24 @@ def write_data_to_file(
 		check_data            = False,
 		check_data            = False,
 		cmp_data              = None):
 		cmp_data              = None):
 
 
-	from .opts import opt
-
 	if quiet:
 	if quiet:
 		ask_tty = ask_overwrite = False
 		ask_tty = ask_overwrite = False
 
 
-	if opt.quiet:
+	if cfg.quiet:
 		ask_overwrite = False
 		ask_overwrite = False
 
 
 	if ask_write_default_yes == False or ask_write_prompt:
 	if ask_write_default_yes == False or ask_write_prompt:
 		ask_write = True
 		ask_write = True
 
 
 	def do_stdout():
 	def do_stdout():
-		qmsg('Output to STDOUT requested')
-		if g.stdin_tty:
+		cfg._util.qmsg('Output to STDOUT requested')
+		if cfg.stdin_tty:
 			if no_tty:
 			if no_tty:
 				die(2,f'Printing {desc} to screen is not allowed')
 				die(2,f'Printing {desc} to screen is not allowed')
-			if (ask_tty and not opt.quiet) or binary:
+			if (ask_tty and not cfg.quiet) or binary:
 				from .ui import confirm_or_raise
 				from .ui import confirm_or_raise
 				confirm_or_raise(
 				confirm_or_raise(
+					cfg,
 					message = '',
 					message = '',
 					action  = f'output {desc} to screen' )
 					action  = f'output {desc} to screen' )
 		else:
 		else:
@@ -194,9 +193,10 @@ def write_data_to_file(
 				if of[:5] == 'pipe:':
 				if of[:5] == 'pipe:':
 					if no_tty:
 					if no_tty:
 						die(2,f'Writing {desc} to pipe is not allowed')
 						die(2,f'Writing {desc} to pipe is not allowed')
-					if ask_tty and not opt.quiet:
+					if ask_tty and not cfg.quiet:
 						from .ui import confirm_or_raise
 						from .ui import confirm_or_raise
 						confirm_or_raise(
 						confirm_or_raise(
+							cfg,
 							message = '',
 							message = '',
 							action  = f'output {desc} to pipe' )
 							action  = f'output {desc} to pipe' )
 						msg('')
 						msg('')
@@ -216,14 +216,15 @@ def write_data_to_file(
 			os.write(1,data if isinstance(data,bytes) else data.encode())
 			os.write(1,data if isinstance(data,bytes) else data.encode())
 
 
 	def do_file(outfile,ask_write_prompt):
 	def do_file(outfile,ask_write_prompt):
-		if opt.outdir and not ignore_opt_outdir and not os.path.isabs(outfile):
-			outfile = make_full_path(opt.outdir,outfile)
+		if cfg.outdir and not ignore_opt_outdir and not os.path.isabs(outfile):
+			outfile = make_full_path(cfg.outdir,outfile)
 
 
 		if ask_write:
 		if ask_write:
 			if not ask_write_prompt:
 			if not ask_write_prompt:
 				ask_write_prompt = f'Save {desc}?'
 				ask_write_prompt = f'Save {desc}?'
 			from .ui import keypress_confirm
 			from .ui import keypress_confirm
 			if not keypress_confirm(
 			if not keypress_confirm(
+					cfg,
 					ask_write_prompt,
 					ask_write_prompt,
 					default_yes = ask_write_default_yes ):
 					default_yes = ask_write_default_yes ):
 				die(1,f'{capfirst(desc)} not saved')
 				die(1,f'{capfirst(desc)} not saved')
@@ -232,6 +233,7 @@ def write_data_to_file(
 		if os.path.lexists(outfile) and ask_overwrite:
 		if os.path.lexists(outfile) and ask_overwrite:
 			from .ui import confirm_or_raise
 			from .ui import confirm_or_raise
 			confirm_or_raise(
 			confirm_or_raise(
+				cfg,
 				message = '',
 				message = '',
 				action  = f'File {outfile!r} already exists\nOverwrite?' )
 				action  = f'File {outfile!r} already exists\nOverwrite?' )
 			msg(f'Overwriting file {outfile!r}')
 			msg(f'Overwriting file {outfile!r}')
@@ -262,17 +264,17 @@ def write_data_to_file(
 
 
 		return True
 		return True
 
 
-	if opt.stdout or outfile in ('','-'):
+	if cfg.stdout or outfile in ('','-'):
 		do_stdout()
 		do_stdout()
 	elif sys.stdin.isatty() and not sys.stdout.isatty():
 	elif sys.stdin.isatty() and not sys.stdout.isatty():
 		do_stdout()
 		do_stdout()
 	else:
 	else:
 		do_file(outfile,ask_write_prompt)
 		do_file(outfile,ask_write_prompt)
 
 
-def get_words_from_file(infile,desc,quiet=False):
+def get_words_from_file(cfg,infile,desc,quiet=False):
 
 
 	if not quiet:
 	if not quiet:
-		qmsg(f'Getting {desc} from file {infile!r}')
+		cfg._util.qmsg(f'Getting {desc} from file {infile!r}')
 
 
 	with _open_or_die(infile, 'rb') as fp:
 	with _open_or_die(infile, 'rb') as fp:
 		data = fp.read()
 		data = fp.read()
@@ -282,11 +284,12 @@ def get_words_from_file(infile,desc,quiet=False):
 	except:
 	except:
 		die(1,f'{capfirst(desc)} data must be UTF-8 encoded.')
 		die(1,f'{capfirst(desc)} data must be UTF-8 encoded.')
 
 
-	dmsg('Sanitized input: [{}]'.format(' '.join(words)))
+	cfg._util.dmsg('Sanitized input: [{}]'.format(' '.join(words)))
 
 
 	return words
 	return words
 
 
 def get_data_from_file(
 def get_data_from_file(
+		cfg,
 		infile,
 		infile,
 		desc   = 'data',
 		desc   = 'data',
 		dash   = False,
 		dash   = False,
@@ -294,26 +297,26 @@ def get_data_from_file(
 		binary = False,
 		binary = False,
 		quiet  = False ):
 		quiet  = False ):
 
 
-	from .opts import opt
-	if not (opt.quiet or silent or quiet):
-		qmsg(f'Getting {desc} from file {infile!r}')
+	if not (cfg.quiet or silent or quiet):
+		cfg._util.qmsg(f'Getting {desc} from file {infile!r}')
 
 
 	with _open_or_die(
 	with _open_or_die(
 			(0 if dash and infile == '-' else infile),
 			(0 if dash and infile == '-' else infile),
 			'rb',
 			'rb',
 			silent=silent) as fp:
 			silent=silent) as fp:
-		data = fp.read(g.max_input_size+1)
+		data = fp.read(cfg.max_input_size+1)
 
 
 	if not binary:
 	if not binary:
 		data = data.decode()
 		data = data.decode()
 
 
-	if len(data) == g.max_input_size + 1:
+	if len(data) == cfg.max_input_size + 1:
 		die( 'MaxInputSizeExceeded',
 		die( 'MaxInputSizeExceeded',
 			f'Too much input data!  Max input data size: {f.max_input_size} bytes' )
 			f'Too much input data!  Max input data size: {f.max_input_size} bytes' )
 
 
 	return data
 	return data
 
 
 def get_lines_from_file(
 def get_lines_from_file(
+		cfg,
 		fn,
 		fn,
 		desc          = 'data',
 		desc          = 'data',
 		trim_comments = False,
 		trim_comments = False,
@@ -321,17 +324,17 @@ def get_lines_from_file(
 		silent        = False ):
 		silent        = False ):
 
 
 	def decrypt_file_maybe():
 	def decrypt_file_maybe():
-		data = get_data_from_file( fn, desc=desc, binary=True, quiet=quiet, silent=silent )
+		data = get_data_from_file( cfg, fn, desc=desc, binary=True, quiet=quiet, silent=silent )
 		from .crypto import Crypto
 		from .crypto import Crypto
 		have_enc_ext = get_extension(fn) == Crypto.mmenc_ext
 		have_enc_ext = get_extension(fn) == Crypto.mmenc_ext
 		if have_enc_ext or not is_utf8(data):
 		if have_enc_ext or not is_utf8(data):
 			m = ('Attempting to decrypt','Decrypting')[have_enc_ext]
 			m = ('Attempting to decrypt','Decrypting')[have_enc_ext]
-			qmsg(f'{m} {desc} {fn!r}')
-			data = Crypto().mmgen_decrypt_retry(data,desc)
+			cfg._util.qmsg(f'{m} {desc} {fn!r}')
+			data = Crypto(cfg).mmgen_decrypt_retry(data,desc)
 		return data
 		return data
 
 
 	lines = decrypt_file_maybe().decode().splitlines()
 	lines = decrypt_file_maybe().decode().splitlines()
 	if trim_comments:
 	if trim_comments:
 		lines = strip_comments(lines)
 		lines = strip_comments(lines)
-	dmsg(f'Got {len(lines)} lines from file {fn!r}')
+	cfg._util.dmsg(f'Got {len(lines)} lines from file {fn!r}')
 	return lines
 	return lines

+ 6 - 21
mmgen/globalvars.py

@@ -104,7 +104,7 @@ class GlobalVars:
 
 
 gv = GlobalVars()
 gv = GlobalVars()
 
 
-class GlobalConfig(Lockable):
+class Config(Lockable):
 	"""
 	"""
 	These values are configurable - RHS values are defaults
 	These values are configurable - RHS values are defaults
 	Globals are overridden with the following precedence:
 	Globals are overridden with the following precedence:
@@ -113,12 +113,13 @@ class GlobalConfig(Lockable):
 	  3 - config file
 	  3 - config file
 	"""
 	"""
 	_autolock = False
 	_autolock = False
-	_set_ok = ()
-	_reset_ok = ('accept_defaults',)
+	_set_ok = ('usr_randchars','_proto')
+	_reset_ok = ('accept_defaults','quiet','verbose','yes')
 	_use_class_attr = True
 	_use_class_attr = True
+	_default_to_none = True
 
 
 	# general
 	# general
-	coin        = ''
+	coin        = 'BTC'
 	token       = ''
 	token       = ''
 	outdir      = ''
 	outdir      = ''
 	passwd_file = ''
 	passwd_file = ''
@@ -189,7 +190,7 @@ class GlobalConfig(Lockable):
 	bob          = False
 	bob          = False
 	alice        = False
 	alice        = False
 	carol        = False
 	carol        = False
-	regtest_user = None
+	regtest_user = ''
 
 
 	# test suite:
 	# test suite:
 	bogus_send               = False
 	bogus_send               = False
@@ -216,20 +217,6 @@ class GlobalConfig(Lockable):
 	_proto = None
 	_proto = None
 	pager = False
 	pager = False
 
 
-	# global var sets user opt:
-	global_sets_opt = (
-		'autochg_ignore_labels',
-		'debug',
-		'minconf',
-		'quiet',
-		'fee_estimate_confs',
-		'fee_adjust',
-		'use_internal_keccak_module',
-		'usr_randchars' )
-
-	# user opt sets global var:
-	opt_sets_global = ( 'cached_balances', )
-
 	# 'long' opts (subset of common_opts_data):
 	# 'long' opts (subset of common_opts_data):
 	common_opts = (
 	common_opts = (
 		'accept_defaults',
 		'accept_defaults',
@@ -415,5 +402,3 @@ class GlobalConfig(Lockable):
 				'mainnet': (self.data_dir_root,),
 				'mainnet': (self.data_dir_root,),
 			}[self.network] ))
 			}[self.network] ))
 			return self._data_dir
 			return self._data_dir
-
-g = GlobalConfig()

+ 7 - 7
mmgen/help.py

@@ -22,7 +22,7 @@ help: help notes for MMGen suite commands
 
 
 from .globalvars import gc
 from .globalvars import gc
 
 
-def help_notes_func(proto,opt,k):
+def help_notes_func(proto,cfg,k):
 
 
 	def fee_spec_letters(use_quotes=False):
 	def fee_spec_letters(use_quotes=False):
 		cu = proto.coin_amt.units
 		cu = proto.coin_amt.units
@@ -36,7 +36,7 @@ def help_notes_func(proto,opt,k):
 	def coind_exec():
 	def coind_exec():
 		from .daemon import CoinDaemon
 		from .daemon import CoinDaemon
 		return (
 		return (
-			CoinDaemon(proto.coin).exec_fn if proto.coin in CoinDaemon.coins else 'bitcoind' )
+			CoinDaemon(cfg,proto.coin).exec_fn if proto.coin in CoinDaemon.coins else 'bitcoind' )
 
 
 	class help_notes:
 	class help_notes:
 
 
@@ -92,7 +92,7 @@ def help_notes_func(proto,opt,k):
 			from .keygen import get_backends
 			from .keygen import get_backends
 			from .addr import MMGenAddrType
 			from .addr import MMGenAddrType
 			backends = get_backends(
 			backends = get_backends(
-				MMGenAddrType(proto,opt.type or proto.dfl_mmtype).pubkey_type
+				MMGenAddrType(proto,cfg.type or proto.dfl_mmtype).pubkey_type
 			)
 			)
 			return ' '.join( f'{n}:{k}{" [default]" if n==1 else ""}' for n,k in enumerate(backends,1) )
 			return ' '.join( f'{n}:{k}{" [default]" if n==1 else ""}' for n,k in enumerate(backends,1) )
 
 
@@ -102,11 +102,11 @@ def help_notes_func(proto,opt,k):
 		def coin_daemon_network_ids():
 		def coin_daemon_network_ids():
 			from .daemon import CoinDaemon
 			from .daemon import CoinDaemon
 			from .util import fmt_list
 			from .util import fmt_list
-			return fmt_list(CoinDaemon.get_network_ids(),fmt='bare')
+			return fmt_list(CoinDaemon.get_network_ids(cfg),fmt='bare')
 
 
 		def rel_fee_desc():
 		def rel_fee_desc():
 			from .tx import BaseTX
 			from .tx import BaseTX
-			return BaseTX(proto=proto).rel_fee_desc
+			return BaseTX(cfg=cfg,proto=proto).rel_fee_desc
 
 
 		def fee_spec_letters():
 		def fee_spec_letters():
 			return fee_spec_letters()
 			return fee_spec_letters()
@@ -121,7 +121,7 @@ be specified as either absolute {c} amounts, using a plain decimal number, or
 as {r}, using an integer followed by '{l}', for {u}.
 as {r}, using an integer followed by '{l}', for {u}.
 """.format(
 """.format(
 	c = proto.coin,
 	c = proto.coin,
-	r = BaseTX(proto=proto).rel_fee_desc,
+	r = BaseTX(cfg=cfg,proto=proto).rel_fee_desc,
 	l = fee_spec_letters(use_quotes=True),
 	l = fee_spec_letters(use_quotes=True),
 	u = fee_spec_names() )
 	u = fee_spec_names() )
 
 
@@ -147,7 +147,7 @@ seed, the same seed length and hash preset parameters must always be used.
 
 
 			mmtype = 'S' if 'segwit' in proto.caps else 'C'
 			mmtype = 'S' if 'segwit' in proto.caps else 'C'
 			from .tool.coin import tool_cmd
 			from .tool.coin import tool_cmd
-			t = tool_cmd(mmtype=mmtype)
+			t = tool_cmd(cfg,mmtype=mmtype)
 			sample_addr = t.privhex2addr('bead'*16)
 			sample_addr = t.privhex2addr('bead'*16)
 
 
 			return f"""
 			return f"""

+ 9 - 9
mmgen/keygen.py

@@ -71,9 +71,9 @@ def get_pubkey_type_cls(pubkey_type):
 		importlib.import_module(f'mmgen.proto.{backend_data[pubkey_type]["package"]}.keygen'),
 		importlib.import_module(f'mmgen.proto.{backend_data[pubkey_type]["package"]}.keygen'),
 		'backend' )
 		'backend' )
 
 
-def _check_backend(backend,pubkey_type,desc='keygen backend'):
+def _check_backend(cfg,backend,pubkey_type,desc='keygen backend'):
 
 
-	from .util import is_int,qmsg,die
+	from .util import is_int,die
 
 
 	assert is_int(backend), f'illegal value for {desc} (must be an integer)'
 	assert is_int(backend), f'illegal value for {desc} (must be an integer)'
 
 
@@ -86,21 +86,22 @@ def _check_backend(backend,pubkey_type,desc='keygen backend'):
 			' '.join( f'{n}:{k}' for n,k in enumerate(backends,1) )
 			' '.join( f'{n}:{k}' for n,k in enumerate(backends,1) )
 		)
 		)
 
 
-	qmsg(f'Using backend {backends[int(backend)-1]!r} for public key generation')
+	cfg._util.qmsg(f'Using backend {backends[int(backend)-1]!r} for public key generation')
 
 
 	return True
 	return True
 
 
-def check_backend(proto,backend,addr_type):
+def check_backend(cfg,proto,backend,addr_type):
 
 
 	from .addr import MMGenAddrType
 	from .addr import MMGenAddrType
 	pubkey_type = MMGenAddrType(proto,addr_type or proto.dfl_mmtype).pubkey_type
 	pubkey_type = MMGenAddrType(proto,addr_type or proto.dfl_mmtype).pubkey_type
 
 
 	return  _check_backend(
 	return  _check_backend(
+		cfg,
 		backend,
 		backend,
 		pubkey_type,
 		pubkey_type,
 		desc = '--keygen-backend parameter' )
 		desc = '--keygen-backend parameter' )
 
 
-def KeyGenerator(proto,pubkey_type,backend=None,silent=False):
+def KeyGenerator(cfg,proto,pubkey_type,backend=None,silent=False):
 	"""
 	"""
 	factory function returning a key generator backend for the specified pubkey type
 	factory function returning a key generator backend for the specified pubkey type
 	"""
 	"""
@@ -108,11 +109,10 @@ def KeyGenerator(proto,pubkey_type,backend=None,silent=False):
 
 
 	pubkey_type_cls = get_pubkey_type_cls(pubkey_type)
 	pubkey_type_cls = get_pubkey_type_cls(pubkey_type)
 
 
-	from .opts import opt
-	backend = backend or opt.keygen_backend
+	backend = backend or cfg.keygen_backend
 
 
 	if backend:
 	if backend:
-		_check_backend(backend,pubkey_type)
+		_check_backend( cfg, backend, pubkey_type )
 
 
 	backend_id = backend_data[pubkey_type]['backends'][int(backend) - 1 if backend else 0]
 	backend_id = backend_data[pubkey_type]['backends'][int(backend) - 1 if backend else 0]
 
 
@@ -121,4 +121,4 @@ def KeyGenerator(proto,pubkey_type,backend=None,silent=False):
 		backend_id.replace('-','_')
 		backend_id.replace('-','_')
 			).test_avail(silent=silent)
 			).test_avail(silent=silent)
 
 
-	return getattr(pubkey_type_cls,backend_clsname)()
+	return getattr(pubkey_type_cls,backend_clsname)(cfg)

+ 20 - 21
mmgen/main_addrgen.py

@@ -22,8 +22,7 @@ mmgen-addrgen: Generate a series or range of addresses from an MMGen
 """
 """
 
 
 import mmgen.opts as opts
 import mmgen.opts as opts
-from .globalvars import g,gc
-from .opts import opt
+from .globalvars import gc
 from .addr import MMGenAddrType
 from .addr import MMGenAddrType
 from .addrfile import AddrFile
 from .addrfile import AddrFile
 from .wallet import Wallet
 from .wallet import Wallet
@@ -74,7 +73,7 @@ opts_data = {
 -P, --passwd-file= f  Get wallet passphrase from file 'f'
 -P, --passwd-file= f  Get wallet passphrase from file 'f'
 -q, --quiet           Produce quieter output; suppress some warnings
 -q, --quiet           Produce quieter output; suppress some warnings
 -r, --usr-randchars=n Get 'n' characters of additional randomness from user
 -r, --usr-randchars=n Get 'n' characters of additional randomness from user
-                      (min={g.min_urandchars}, max={g.max_urandchars}, default={g.usr_randchars})
+                      (min={cfg.min_urandchars}, max={cfg.max_urandchars}, default={cfg.usr_randchars})
 -S, --stdout          Print {what} to stdout
 -S, --stdout          Print {what} to stdout
 -t, --type=t          Choose address type. Options: see ADDRESS TYPES below
 -t, --type=t          Choose address type. Options: see ADDRESS TYPES below
                       (default: {dmat})
                       (default: {dmat})
@@ -106,14 +105,14 @@ FMT CODES:
 """
 """
 	},
 	},
 	'code': {
 	'code': {
-		'options': lambda proto,help_notes,s: s.format(
+		'options': lambda proto,help_notes,cfg,s: s.format(
 			dmat=help_notes('dfl_mmtype'),
 			dmat=help_notes('dfl_mmtype'),
 			kgs=help_notes('keygen_backends'),
 			kgs=help_notes('keygen_backends'),
 			coin_id=help_notes('coin_id'),
 			coin_id=help_notes('coin_id'),
 			dsl=help_notes('dfl_seed_len'),
 			dsl=help_notes('dfl_seed_len'),
 			pnm=gc.proj_name,
 			pnm=gc.proj_name,
 			what=gen_what,
 			what=gen_what,
-			g=g,
+			cfg=cfg,
 			gc=gc,
 			gc=gc,
 		),
 		),
 		'notes': lambda help_notes,s: s.format(
 		'notes': lambda help_notes,s: s.format(
@@ -127,39 +126,39 @@ FMT CODES:
 	}
 	}
 }
 }
 
 
-cmd_args = opts.init(opts_data,opt_filter=opt_filter)
+cfg = opts.init(opts_data,opt_filter=opt_filter,need_amt=False)
 
 
-from .protocol import init_proto_from_opts
-proto = init_proto_from_opts()
+proto = cfg._proto
 
 
 addr_type = MMGenAddrType(
 addr_type = MMGenAddrType(
 	proto = proto,
 	proto = proto,
-	id_str = opt.type or proto.dfl_mmtype,
-	errmsg = f'{opt.type!r}: invalid parameter for --type option' )
+	id_str = cfg.type or proto.dfl_mmtype,
+	errmsg = f'{cfg.type!r}: invalid parameter for --type option' )
 
 
-if len(cmd_args) < 1:
+if len(cfg._args) < 1:
 	opts.usage()
 	opts.usage()
 
 
-if opt.keygen_backend:
+if cfg.keygen_backend:
 	from .keygen import check_backend
 	from .keygen import check_backend
-	check_backend( proto, opt.keygen_backend, opt.type )
+	check_backend( cfg, proto, cfg.keygen_backend, cfg.type )
 
 
-idxs = mmgen.addrlist.AddrIdxList( fmt_str=cmd_args.pop() )
+idxs = mmgen.addrlist.AddrIdxList( fmt_str=cfg._args.pop() )
 
 
 from .fileutil import get_seed_file
 from .fileutil import get_seed_file
-sf = get_seed_file(cmd_args,1)
+sf = get_seed_file(cfg,1)
 
 
 from .ui import do_license_msg
 from .ui import do_license_msg
-do_license_msg()
+do_license_msg(cfg)
 
 
-ss = Wallet(sf)
+ss = Wallet(cfg,sf)
 
 
-ss_seed = ss.seed if opt.subwallet is None else ss.seed.subseed(opt.subwallet,print_msg=True)
+ss_seed = ss.seed if cfg.subwallet is None else ss.seed.subseed(cfg.subwallet,print_msg=True)
 
 
-if opt.no_addresses:
+if cfg.no_addresses:
 	gen_clsname = 'KeyList'
 	gen_clsname = 'KeyList'
 
 
 al = getattr( mmgen.addrlist, gen_clsname )(
 al = getattr( mmgen.addrlist, gen_clsname )(
+	cfg       = cfg,
 	proto     = proto,
 	proto     = proto,
 	seed      = ss_seed,
 	seed      = ss_seed,
 	addr_idxs = idxs,
 	addr_idxs = idxs,
@@ -169,11 +168,11 @@ af = al.get_file()
 
 
 af.format()
 af.format()
 
 
-if al.gen_addrs and opt.print_checksum:
+if al.gen_addrs and cfg.print_checksum:
 	Die(0,al.checksum)
 	Die(0,al.checksum)
 
 
 from .ui import keypress_confirm
 from .ui import keypress_confirm
-if al.gen_keys and keypress_confirm('Encrypt key list?'):
+if al.gen_keys and keypress_confirm( cfg, 'Encrypt key list?' ):
 	af.encrypt()
 	af.encrypt()
 	af.write(
 	af.write(
 		binary = True,
 		binary = True,

+ 22 - 20
mmgen/main_addrimport.py

@@ -24,8 +24,7 @@ from collections import namedtuple
 
 
 import mmgen.opts as opts
 import mmgen.opts as opts
 from .globalvars import gc
 from .globalvars import gc
-from .opts import opt
-from .util import msg,qmsg,suf,die,fmt,async_run
+from .util import msg,suf,die,fmt,async_run
 from .addrlist import AddrList,KeyAddrList
 from .addrlist import AddrList,KeyAddrList
 from .tw.shared import TwLabel
 from .tw.shared import TwLabel
 
 
@@ -87,7 +86,7 @@ addrimport_msgs = {
 def parse_cmd_args(rpc,cmd_args):
 def parse_cmd_args(rpc,cmd_args):
 
 
 	def import_mmgen_list(infile):
 	def import_mmgen_list(infile):
-		al = (AddrList,KeyAddrList)[bool(opt.keyaddr_file)](proto,infile)
+		al = (AddrList,KeyAddrList)[bool(cfg.keyaddr_file)](cfg,proto,infile)
 		if al.al_id.mmtype in ('S','B'):
 		if al.al_id.mmtype in ('S','B'):
 			if not rpc.info('segwit_is_active'):
 			if not rpc.info('segwit_is_active'):
 				die(2,'Segwit is not active on this chain. Cannot import Segwit addresses')
 				die(2,'Segwit is not active on this chain. Cannot import Segwit addresses')
@@ -97,17 +96,19 @@ def parse_cmd_args(rpc,cmd_args):
 		infile = cmd_args[0]
 		infile = cmd_args[0]
 		from .fileutil import check_infile,get_lines_from_file
 		from .fileutil import check_infile,get_lines_from_file
 		check_infile(infile)
 		check_infile(infile)
-		if opt.addrlist:
+		if cfg.addrlist:
 			al = AddrList(
 			al = AddrList(
-				proto = proto,
+				cfg      = cfg,
+				proto    = proto,
 				addrlist = get_lines_from_file(
 				addrlist = get_lines_from_file(
+					cfg,
 					infile,
 					infile,
 					f'non-{gc.proj_name} addresses',
 					f'non-{gc.proj_name} addresses',
 					trim_comments = True ) )
 					trim_comments = True ) )
 		else:
 		else:
 			al = import_mmgen_list(infile)
 			al = import_mmgen_list(infile)
-	elif len(cmd_args) == 0 and opt.address:
-		al = AddrList(proto=proto,addrlist=[opt.address])
+	elif len(cmd_args) == 0 and cfg.address:
+		al = AddrList( cfg, proto=proto, addrlist=[cfg.address] )
 		infile = 'command line'
 		infile = 'command line'
 	else:
 	else:
 		die(1,addrimport_msgs['bad_args'])
 		die(1,addrimport_msgs['bad_args'])
@@ -115,16 +116,17 @@ def parse_cmd_args(rpc,cmd_args):
 	return al,infile
 	return al,infile
 
 
 def check_opts(twctl):
 def check_opts(twctl):
-	batch = bool(opt.batch)
-	rescan = bool(opt.rescan)
+	batch = bool(cfg.batch)
+	rescan = bool(cfg.rescan)
 
 
 	if rescan and not 'rescan' in twctl.caps:
 	if rescan and not 'rescan' in twctl.caps:
 		msg(f"‘--rescan’ ignored: not supported by {type(twctl).__name__}")
 		msg(f"‘--rescan’ ignored: not supported by {type(twctl).__name__}")
 		rescan = False
 		rescan = False
 
 
-	if rescan and not opt.quiet:
+	if rescan and not cfg.quiet:
 		from .ui import keypress_confirm
 		from .ui import keypress_confirm
 		if not keypress_confirm(
 		if not keypress_confirm(
+				cfg,
 				'\n{}\n\nContinue?'.format(addrimport_msgs['rescan']),
 				'\n{}\n\nContinue?'.format(addrimport_msgs['rescan']),
 				default_yes = True ):
 				default_yes = True ):
 			die(1,'Exiting at user request')
 			die(1,'Exiting at user request')
@@ -137,32 +139,33 @@ def check_opts(twctl):
 
 
 async def main():
 async def main():
 	from .tw.ctl import TwCtl
 	from .tw.ctl import TwCtl
-	if opt.token_addr:
+	if cfg.token_addr:
 		proto.tokensym = 'foo' # hack to trigger 'Token' in proto.base_proto_subclass()
 		proto.tokensym = 'foo' # hack to trigger 'Token' in proto.base_proto_subclass()
 
 
 	twctl = await TwCtl(
 	twctl = await TwCtl(
+		cfg        = cfg,
 		proto      = proto,
 		proto      = proto,
-		token_addr = opt.token_addr,
+		token_addr = cfg.token_addr,
 		mode       = 'i' )
 		mode       = 'i' )
 
 
-	if opt.token or opt.token_addr:
+	if cfg.token or cfg.token_addr:
 		msg(f'Importing for token {twctl.token.hl()} ({twctl.token.hlc(proto.tokensym)})')
 		msg(f'Importing for token {twctl.token.hl()} ({twctl.token.hlc(proto.tokensym)})')
 
 
 	from .rpc import rpc_init
 	from .rpc import rpc_init
-	twctl.rpc = await rpc_init(proto)
+	twctl.rpc = await rpc_init(cfg,proto)
 
 
 	for k,v in addrimport_msgs.items():
 	for k,v in addrimport_msgs.items():
 		addrimport_msgs[k] = fmt(v,indent='  ',strip_char='\t').rstrip()
 		addrimport_msgs[k] = fmt(v,indent='  ',strip_char='\t').rstrip()
 
 
-	al,infile = parse_cmd_args(twctl.rpc,cmd_args)
+	al,infile = parse_cmd_args(twctl.rpc,cfg._args)
 
 
-	qmsg(
+	cfg._util.qmsg(
 		f'OK. {al.num_addrs} addresses'
 		f'OK. {al.num_addrs} addresses'
 		+ (f' from Seed ID {al.al_id.sid}' if hasattr(al.al_id,'sid') else '') )
 		+ (f' from Seed ID {al.al_id.sid}' if hasattr(al.al_id,'sid') else '') )
 
 
 	msg(
 	msg(
 		f'Importing {len(al.data)} address{suf(al.data,"es")} from {infile}'
 		f'Importing {len(al.data)} address{suf(al.data,"es")} from {infile}'
-		+ (' (batch mode)' if opt.batch else '') )
+		+ (' (batch mode)' if cfg.batch else '') )
 
 
 	batch,rescan = check_opts(twctl)
 	batch,rescan = check_opts(twctl)
 
 
@@ -183,9 +186,8 @@ async def main():
 
 
 	del twctl
 	del twctl
 
 
-cmd_args = opts.init(opts_data)
+cfg = opts.init(opts_data,need_amt=False)
 
 
-from .protocol import init_proto_from_opts
-proto = init_proto_from_opts()
+proto = cfg._proto
 
 
 async_run(main())
 async_run(main())

+ 38 - 38
mmgen/main_autosign.py

@@ -26,9 +26,7 @@ from collections import namedtuple
 from stat import *
 from stat import *
 
 
 import mmgen.opts as opts
 import mmgen.opts as opts
-from .globalvars import g
-from .opts import opt
-from .util import msg,msg_r,vmsg,qmsg,ymsg,rmsg,gmsg,bmsg,die,suf,fmt_list,async_run,exit_if_mswin
+from .util import msg,msg_r,ymsg,rmsg,gmsg,bmsg,die,suf,fmt_list,async_run,exit_if_mswin
 from .color import yellow,red,orange
 from .color import yellow,red,orange
 
 
 mountpoint   = '/mnt/tx'
 mountpoint   = '/mnt/tx'
@@ -122,7 +120,7 @@ This command is currently available only on Linux-based platforms.
 	}
 	}
 }
 }
 
 
-cmd_args = opts.init(
+cfg = opts.init(
 	opts_data,
 	opts_data,
 	add_opts = ['outdir','passwd_file'], # in _set_ok, so must be set
 	add_opts = ['outdir','passwd_file'], # in _set_ok, so must be set
 	init_opts = {
 	init_opts = {
@@ -133,14 +131,16 @@ cmd_args = opts.init(
 		'label': 'Autosign Wallet',
 		'label': 'Autosign Wallet',
 	})
 	})
 
 
-type(opt)._set_ok += ('outdir','passwd_file')
+cmd_args = cfg._args
+
+type(cfg)._set_ok += ('outdir','passwd_file')
 
 
 exit_if_mswin('autosigning')
 exit_if_mswin('autosigning')
 
 
-if opt.mnemonic_fmt:
-	if opt.mnemonic_fmt not in mn_fmts:
+if cfg.mnemonic_fmt:
+	if cfg.mnemonic_fmt not in mn_fmts:
 		die(1,'{!r}: invalid mnemonic format (must be one of: {})'.format(
 		die(1,'{!r}: invalid mnemonic format (must be one of: {})'.format(
-			opt.mnemonic_fmt,
+			cfg.mnemonic_fmt,
 			fmt_list(mn_fmts,fmt='no_spc') ))
 			fmt_list(mn_fmts,fmt='no_spc') ))
 
 
 from .wallet import Wallet
 from .wallet import Wallet
@@ -149,32 +149,32 @@ from .tx.sign import txsign
 from .protocol import init_proto
 from .protocol import init_proto
 from .rpc import rpc_init
 from .rpc import rpc_init
 
 
-if opt.mountpoint:
-	mountpoint = opt.mountpoint
+if cfg.mountpoint:
+	mountpoint = cfg.mountpoint
 
 
 keyfile = os.path.join(mountpoint,'autosign.key')
 keyfile = os.path.join(mountpoint,'autosign.key')
 msg_dir = os.path.join(mountpoint,'msg')
 msg_dir = os.path.join(mountpoint,'msg')
 tx_dir  = os.path.join(mountpoint,'tx')
 tx_dir  = os.path.join(mountpoint,'tx')
 
 
-opt.outdir = tx_dir
-opt.passwd_file = keyfile
+cfg.outdir = tx_dir
+cfg.passwd_file = keyfile
 
 
 async def check_daemons_running():
 async def check_daemons_running():
-	if opt.coin:
+	if cfg.coin != type(cfg).coin:
 		die(1,'--coin option not supported with this command.  Use --coins instead')
 		die(1,'--coin option not supported with this command.  Use --coins instead')
-	if opt.coins:
-		coins = opt.coins.upper().split(',')
+	if cfg.coins:
+		coins = cfg.coins.upper().split(',')
 	else:
 	else:
 		ymsg('Warning: no coins specified, defaulting to BTC')
 		ymsg('Warning: no coins specified, defaulting to BTC')
 		coins = ['BTC']
 		coins = ['BTC']
 
 
 	for coin in coins:
 	for coin in coins:
-		proto = init_proto( coin, testnet=g.network=='testnet', need_amt=True )
+		proto = init_proto( cfg,  coin, testnet=cfg.network=='testnet', need_amt=True )
 		if proto.sign_mode == 'daemon':
 		if proto.sign_mode == 'daemon':
-			vmsg(f'Checking {coin} daemon')
+			cfg._util.vmsg(f'Checking {coin} daemon')
 			from .exception import SocketError
 			from .exception import SocketError
 			try:
 			try:
-				await rpc_init(proto)
+				await rpc_init(cfg,proto)
 			except SocketError as e:
 			except SocketError as e:
 				die(2,f'{coin} daemon not running or not listening on port {proto.rpc_port}')
 				die(2,f'{coin} daemon not running or not listening on port {proto.rpc_port}')
 
 
@@ -213,10 +213,10 @@ def do_umount():
 async def sign_object(d,fn):
 async def sign_object(d,fn):
 	try:
 	try:
 		if d.desc == 'transaction':
 		if d.desc == 'transaction':
-			tx1 = UnsignedTX(filename=fn)
+			tx1 = UnsignedTX(cfg=cfg,filename=fn)
 			if tx1.proto.sign_mode == 'daemon':
 			if tx1.proto.sign_mode == 'daemon':
-				tx1.rpc = await rpc_init(tx1.proto)
-			tx2 = await txsign(tx1,wfs[:],None,None)
+				tx1.rpc = await rpc_init(cfg,tx1.proto)
+			tx2 = await txsign(cfg,tx1,wfs[:],None,None)
 			if tx2:
 			if tx2:
 				tx2.file.write(ask_write=False)
 				tx2.file.write(ask_write=False)
 				return tx2
 				return tx2
@@ -224,9 +224,9 @@ async def sign_object(d,fn):
 				return False
 				return False
 		elif d.desc == 'message file':
 		elif d.desc == 'message file':
 			from .msg import UnsignedMsg,SignedMsg
 			from .msg import UnsignedMsg,SignedMsg
-			m = UnsignedMsg(infile=fn)
+			m = UnsignedMsg(cfg,infile=fn)
 			await m.sign(wallet_files=wfs[:])
 			await m.sign(wallet_files=wfs[:])
-			m = SignedMsg(data=m.__dict__)
+			m = SignedMsg(cfg,data=m.__dict__)
 			m.write_to_file(
 			m.write_to_file(
 				outdir = os.path.abspath(msg_dir),
 				outdir = os.path.abspath(msg_dir),
 				ask_overwrite = False )
 				ask_overwrite = False )
@@ -259,12 +259,12 @@ async def sign(target):
 				ok.append(ret)
 				ok.append(ret)
 			else:
 			else:
 				bad.append(fn)
 				bad.append(fn)
-			qmsg('')
+			cfg._util.qmsg('')
 		await asyncio.sleep(0.3)
 		await asyncio.sleep(0.3)
 		msg(f'{len(ok)} {d.desc}{suf(ok)} signed')
 		msg(f'{len(ok)} {d.desc}{suf(ok)} signed')
 		if bad:
 		if bad:
 			rmsg(f'{len(bad)} {d.desc}{suf(bad)} failed to {d.fail_desc}')
 			rmsg(f'{len(bad)} {d.desc}{suf(bad)} failed to {d.fail_desc}')
-		if ok and not opt.no_summary:
+		if ok and not cfg.no_summary:
 			print_summary(d,ok)
 			print_summary(d,ok)
 		if bad:
 		if bad:
 			msg('')
 			msg('')
@@ -285,11 +285,11 @@ async def sign(target):
 		return True
 		return True
 
 
 def decrypt_wallets():
 def decrypt_wallets():
-	msg(f'Unlocking wallet{suf(wfs)} with key from {opt.passwd_file!r}')
+	msg(f'Unlocking wallet{suf(wfs)} with key from {cfg.passwd_file!r}')
 	fails = 0
 	fails = 0
 	for wf in wfs:
 	for wf in wfs:
 		try:
 		try:
-			Wallet(wf,ignore_in_fmt=True)
+			Wallet(cfg,wf,ignore_in_fmt=True)
 		except SystemExit as e:
 		except SystemExit as e:
 			if e.code != 0:
 			if e.code != 0:
 				fails += 1
 				fails += 1
@@ -304,7 +304,7 @@ def print_summary(d,signed_objects):
 			gmsg('  ' + os.path.join(msg_dir,m.signed_filename) )
 			gmsg('  ' + os.path.join(msg_dir,m.signed_filename) )
 		return
 		return
 
 
-	if opt.full_summary:
+	if cfg.full_summary:
 		bmsg('\nAutosign summary:\n')
 		bmsg('\nAutosign summary:\n')
 		def gen():
 		def gen():
 			for tx in signed_objects:
 			for tx in signed_objects:
@@ -340,23 +340,23 @@ def print_summary(d,signed_objects):
 		msg('No non-MMGen outputs')
 		msg('No non-MMGen outputs')
 
 
 async def do_sign():
 async def do_sign():
-	if not opt.stealth_led:
+	if not cfg.stealth_led:
 		led.set('busy')
 		led.set('busy')
 	do_mount()
 	do_mount()
 	key_ok = decrypt_wallets()
 	key_ok = decrypt_wallets()
 	if key_ok:
 	if key_ok:
-		if opt.stealth_led:
+		if cfg.stealth_led:
 			led.set('busy')
 			led.set('busy')
 		ret1 = await sign('tx')
 		ret1 = await sign('tx')
 		ret2 = await sign('msg') if have_msg_dir else True
 		ret2 = await sign('msg') if have_msg_dir else True
 		ret = ret1 and ret2
 		ret = ret1 and ret2
 		do_umount()
 		do_umount()
-		led.set(('standby','off','error')[(not ret)*2 or bool(opt.stealth_led)])
+		led.set(('standby','off','error')[(not ret)*2 or bool(cfg.stealth_led)])
 		return ret
 		return ret
 	else:
 	else:
 		msg('Password is incorrect!')
 		msg('Password is incorrect!')
 		do_umount()
 		do_umount()
-		if not opt.stealth_led:
+		if not cfg.stealth_led:
 			led.set('error')
 			led.set('error')
 		return False
 		return False
 
 
@@ -366,7 +366,7 @@ def wipe_existing_key():
 	else:
 	else:
 		from .fileutil import shred_file
 		from .fileutil import shred_file
 		msg(f'\nShredding existing key {keyfile!r}')
 		msg(f'\nShredding existing key {keyfile!r}')
-		shred_file( keyfile, verbose=opt.verbose )
+		shred_file( keyfile, verbose=cfg.verbose )
 
 
 def create_key():
 def create_key():
 	kdata = os.urandom(32).hex()
 	kdata = os.urandom(32).hex()
@@ -404,12 +404,12 @@ def create_wallet_dir():
 def setup():
 def setup():
 	remove_wallet_dir()
 	remove_wallet_dir()
 	gen_key(no_unmount=True)
 	gen_key(no_unmount=True)
-	ss_in = Wallet(in_fmt=mn_fmts[opt.mnemonic_fmt or mn_fmt_dfl])
-	ss_out = Wallet(ss=ss_in)
+	ss_in = Wallet(cfg,in_fmt=mn_fmts[cfg.mnemonic_fmt or mn_fmt_dfl])
+	ss_out = Wallet(cfg,ss=ss_in)
 	ss_out.write_to_file(desc='autosign wallet',outdir=wallet_dir)
 	ss_out.write_to_file(desc='autosign wallet',outdir=wallet_dir)
 
 
 def get_insert_status():
 def get_insert_status():
-	if opt.no_insert_check:
+	if cfg.no_insert_check:
 		return True
 		return True
 	try: os.stat(os.path.join('/dev/disk/by-label',part_label))
 	try: os.stat(os.path.join('/dev/disk/by-label',part_label))
 	except: return False
 	except: return False
@@ -417,7 +417,7 @@ def get_insert_status():
 
 
 async def do_loop():
 async def do_loop():
 	n,prev_status = 0,False
 	n,prev_status = 0,False
-	if not opt.stealth_led:
+	if not cfg.stealth_led:
 		led.set('standby')
 		led.set('standby')
 	while True:
 	while True:
 		status = get_insert_status()
 		status = get_insert_status()
@@ -459,7 +459,7 @@ signal.signal(signal.SIGINT,handler)
 
 
 from .led import LEDControl
 from .led import LEDControl
 led = LEDControl(
 led = LEDControl(
-	enabled = opt.led,
+	enabled = cfg.led,
 	simulate = os.getenv('MMGEN_TEST_SUITE_AUTOSIGN_LED_SIMULATE') )
 	simulate = os.getenv('MMGEN_TEST_SUITE_AUTOSIGN_LED_SIMULATE') )
 led.set('off')
 led.set('off')
 
 

+ 18 - 18
mmgen/main_msg.py

@@ -14,10 +14,8 @@ mmgen-msg: Message signing operations for the MMGen suite
 
 
 import sys
 import sys
 import mmgen.opts as opts
 import mmgen.opts as opts
-from .globalvars import g
-from .opts import opt
 from .base_obj import AsyncInit
 from .base_obj import AsyncInit
-from .util import msg,suf,async_run,stdout_or_pager
+from .util import msg,suf,async_run
 from .msg import (
 from .msg import (
 	NewMsg,
 	NewMsg,
 	CompletedMsg,
 	CompletedMsg,
@@ -33,30 +31,29 @@ class MsgOps:
 	class create:
 	class create:
 
 
 		def __init__(self,msg,addr_specs):
 		def __init__(self,msg,addr_specs):
-			from .protocol import init_proto_from_opts
-			proto = init_proto_from_opts()
 			NewMsg(
 			NewMsg(
-				coin      = proto.coin,
-				network   = proto.network,
+				cfg       = cfg,
+				coin      = cfg._proto.coin,
+				network   = cfg._proto.network,
 				message   = msg,
 				message   = msg,
 				addrlists = addr_specs,
 				addrlists = addr_specs,
-				msghash_type = opt.msghash_type
+				msghash_type = cfg.msghash_type
 			).write_to_file( ask_overwrite=False )
 			).write_to_file( ask_overwrite=False )
 
 
 	class sign(metaclass=AsyncInit):
 	class sign(metaclass=AsyncInit):
 
 
 		async def __init__(self,msgfile,wallet_files):
 		async def __init__(self,msgfile,wallet_files):
 
 
-			m = UnsignedMsg( infile=msgfile )
+			m = UnsignedMsg( cfg, infile=msgfile )
 
 
 			if not wallet_files:
 			if not wallet_files:
 				from .filename import find_file_in_dir
 				from .filename import find_file_in_dir
 				from .wallet import get_wallet_cls
 				from .wallet import get_wallet_cls
-				wallet_files = [find_file_in_dir( get_wallet_cls('mmgen'), g.data_dir )]
+				wallet_files = [find_file_in_dir( get_wallet_cls('mmgen'), cfg.data_dir )]
 
 
 			await m.sign(wallet_files)
 			await m.sign(wallet_files)
 
 
-			m = SignedMsg( data=m.__dict__ )
+			m = SignedMsg( cfg, data=m.__dict__ )
 
 
 			m.write_to_file( ask_overwrite=False )
 			m.write_to_file( ask_overwrite=False )
 
 
@@ -67,18 +64,18 @@ class MsgOps:
 
 
 		async def __init__(self,msgfile,addr=None):
 		async def __init__(self,msgfile,addr=None):
 			try:
 			try:
-				m = SignedOnlineMsg( infile=msgfile )
+				m = SignedOnlineMsg( cfg, infile=msgfile )
 			except:
 			except:
-				m = ExportedMsgSigs( infile=msgfile )
+				m = ExportedMsgSigs( cfg, infile=msgfile )
 
 
 			nSigs = await m.verify(addr)
 			nSigs = await m.verify(addr)
 
 
 			summary = f'{nSigs} signature{suf(nSigs)} verified'
 			summary = f'{nSigs} signature{suf(nSigs)} verified'
 
 
-			if opt.quiet:
+			if cfg.quiet:
 				msg(summary)
 				msg(summary)
 			else:
 			else:
-				stdout_or_pager(m.format(addr) + '\n\n' + summary + '\n')
+				cfg._util.stdout_or_pager(m.format(addr) + '\n\n' + summary + '\n')
 
 
 			if m.data.get('failed_sids'):
 			if m.data.get('failed_sids'):
 				sys.exit(1)
 				sys.exit(1)
@@ -89,8 +86,9 @@ class MsgOps:
 
 
 			from .fileutil import write_data_to_file
 			from .fileutil import write_data_to_file
 			write_data_to_file(
 			write_data_to_file(
+				cfg     = cfg,
 				outfile = 'signatures.json',
 				outfile = 'signatures.json',
-				data    = SignedOnlineMsg( infile=msgfile ).get_json_for_export( addr ),
+				data    = SignedOnlineMsg( cfg, infile=msgfile ).get_json_for_export( addr ),
 				desc    = 'signature data' )
 				desc    = 'signature data' )
 
 
 opts_data = {
 opts_data = {
@@ -205,14 +203,16 @@ $ mmgen-msg verify signatures.json
 	}
 	}
 }
 }
 
 
-cmd_args = opts.init(opts_data)
+cfg = opts.init(opts_data,need_amt=False)
+
+cmd_args = cfg._args
 
 
 if len(cmd_args) < 2:
 if len(cmd_args) < 2:
 	opts.usage()
 	opts.usage()
 
 
 op = cmd_args.pop(0)
 op = cmd_args.pop(0)
 
 
-if opt.msghash_type and op != 'create':
+if cfg.msghash_type and op != 'create':
 	die(1,'--msghash-type option may only be used with the "create" command')
 	die(1,'--msghash-type option may only be used with the "create" command')
 
 
 async def main():
 async def main():

+ 21 - 19
mmgen/main_passgen.py

@@ -22,8 +22,7 @@ mmgen-passgen: Generate a series or range of passwords from an MMGen
 """
 """
 
 
 import mmgen.opts as opts
 import mmgen.opts as opts
-from .globalvars import g,gc
-from .opts import opt
+from .globalvars import gc
 from .addrlist import AddrIdxList
 from .addrlist import AddrIdxList
 from .passwdlist import PasswordList
 from .passwdlist import PasswordList
 from .wallet import Wallet
 from .wallet import Wallet
@@ -62,7 +61,7 @@ opts_data = {
 -P, --passwd-file= f  Get wallet passphrase from file 'f'
 -P, --passwd-file= f  Get wallet passphrase from file 'f'
 -q, --quiet           Produce quieter output; suppress some warnings
 -q, --quiet           Produce quieter output; suppress some warnings
 -r, --usr-randchars=n Get 'n' characters of additional randomness from user
 -r, --usr-randchars=n Get 'n' characters of additional randomness from user
-                      (min={g.min_urandchars}, max={g.max_urandchars}, default={g.usr_randchars})
+                      (min={cfg.min_urandchars}, max={cfg.max_urandchars}, default={cfg.usr_randchars})
 -S, --stdout          Print passwords to stdout
 -S, --stdout          Print passwords to stdout
 -v, --verbose         Produce more verbose output
 -v, --verbose         Produce more verbose output
 """,
 """,
@@ -115,14 +114,15 @@ FMT CODES:
 """
 """
 	},
 	},
 	'code': {
 	'code': {
-		'options': lambda help_notes,s: s.format(
-			g=g,pnm=gc.proj_name,
+		'options': lambda cfg,help_notes,s: s.format(
+			pnm=gc.proj_name,
 			dsl=help_notes('dfl_seed_len'),
 			dsl=help_notes('dfl_seed_len'),
 			dpf=PasswordList.dfl_pw_fmt,
 			dpf=PasswordList.dfl_pw_fmt,
+			cfg=cfg,
 			gc=gc,
 			gc=gc,
 		),
 		),
-		'notes': lambda help_notes,s: s.format(
-				o=opts,g=g,i58=pwi['b58'],i32=pwi['b32'],i39=pwi['bip39'],
+		'notes': lambda cfg,help_notes,s: s.format(
+				o=opts,cfg=cfg,i58=pwi['b58'],i32=pwi['b32'],i39=pwi['bip39'],
 				ml=MMGenPWIDString.max_len,
 				ml=MMGenPWIDString.max_len,
 				fs="', '".join(MMGenPWIDString.forbidden),
 				fs="', '".join(MMGenPWIDString.forbidden),
 				n_pw=help_notes('passwd'),
 				n_pw=help_notes('passwd'),
@@ -134,24 +134,25 @@ FMT CODES:
 	}
 	}
 }
 }
 
 
-cmd_args = opts.init(opts_data)
+cfg = opts.init(opts_data)
 
 
-if len(cmd_args) < 2: opts.usage()
+if len(cfg._args) < 2: opts.usage()
 
 
-pw_idxs = AddrIdxList(fmt_str=cmd_args.pop())
+pw_idxs = AddrIdxList(fmt_str=cfg._args.pop())
 
 
-pw_id_str = cmd_args.pop()
+pw_id_str = cfg._args.pop()
 
 
 from .fileutil import get_seed_file
 from .fileutil import get_seed_file
-sf = get_seed_file(cmd_args,1)
+sf = get_seed_file(cfg,1)
 
 
-pw_fmt = opt.passwd_fmt or PasswordList.dfl_pw_fmt
-pw_len = pwi[pw_fmt].dfl_len // 2 if opt.passwd_len in ('h','H') else opt.passwd_len
+pw_fmt = cfg.passwd_fmt or PasswordList.dfl_pw_fmt
+pw_len = pwi[pw_fmt].dfl_len // 2 if cfg.passwd_len in ('h','H') else cfg.passwd_len
 
 
 from .protocol import init_proto
 from .protocol import init_proto
-proto = init_proto('btc') # TODO: get rid of dummy proto
+proto = init_proto( cfg, 'btc' ) # TODO: get rid of dummy proto
 
 
 PasswordList(
 PasswordList(
+	cfg             = cfg,
 	proto           = proto,
 	proto           = proto,
 	pw_id_str       = pw_id_str,
 	pw_id_str       = pw_id_str,
 	pw_len          = pw_len,
 	pw_len          = pw_len,
@@ -159,11 +160,12 @@ PasswordList(
 	chk_params_only = True )
 	chk_params_only = True )
 
 
 from .ui import do_license_msg
 from .ui import do_license_msg
-do_license_msg()
+do_license_msg(cfg)
 
 
-ss = Wallet(sf)
+ss = Wallet(cfg,sf)
 
 
 al = PasswordList(
 al = PasswordList(
+	cfg       = cfg,
 	proto     = proto,
 	proto     = proto,
 	seed      = ss.seed,
 	seed      = ss.seed,
 	pw_idxs   = pw_idxs,
 	pw_idxs   = pw_idxs,
@@ -176,11 +178,11 @@ af = al.get_file()
 af.format()
 af.format()
 
 
 from .ui import keypress_confirm
 from .ui import keypress_confirm
-if keypress_confirm('Encrypt password list?'):
+if keypress_confirm( cfg, 'Encrypt password list?' ):
 	af.encrypt()
 	af.encrypt()
 	af.write(binary=True,desc='encrypted password list')
 	af.write(binary=True,desc='encrypted password list')
 else:
 else:
-	if g.test_suite_popen_spawn and gc.platform == 'win':
+	if cfg.test_suite_popen_spawn and gc.platform == 'win':
 		import time
 		import time
 		time.sleep(0.1)
 		time.sleep(0.1)
 	af.write(desc='password list')
 	af.write(desc='password list')

+ 5 - 3
mmgen/main_regtest.py

@@ -22,7 +22,7 @@ mmgen-regtest: Coin daemon regression test mode setup and operations for the MMG
 """
 """
 
 
 import mmgen.opts as opts
 import mmgen.opts as opts
-from .globalvars import g,gc
+from .globalvars import gc
 from .util import die,async_run
 from .util import die,async_run
 
 
 opts_data = {
 opts_data = {
@@ -57,7 +57,9 @@ opts_data = {
 	}
 	}
 }
 }
 
 
-cmd_args = opts.init(opts_data)
+cfg = opts.init(opts_data)
+
+cmd_args = cfg._args
 
 
 from .proto.btc.regtest import MMGenRegtest
 from .proto.btc.regtest import MMGenRegtest
 
 
@@ -82,6 +84,6 @@ elif cmd_args[0] not in ('cli','wallet_cli','balances'):
 	check_num_args()
 	check_num_args()
 
 
 async def main():
 async def main():
-	await MMGenRegtest(g.coin).cmd(cmd_args)
+	await MMGenRegtest(cfg,cfg.coin).cmd(cmd_args)
 
 
 async_run(main())
 async_run(main())

+ 23 - 24
mmgen/main_seedjoin.py

@@ -22,9 +22,8 @@ mmgen-seedjoin: Regenerate an MMGen deterministic wallet from seed shares
 """
 """
 
 
 import mmgen.opts as opts
 import mmgen.opts as opts
-from .globalvars import g,gc
-from .opts import opt
-from .util import msg,msg_r,qmsg,die
+from .globalvars import gc
+from .util import msg,msg_r,die
 from .color import yellow
 from .color import yellow
 from .obj import MMGenWalletLabel
 from .obj import MMGenWalletLabel
 from .seed import Seed
 from .seed import Seed
@@ -58,7 +57,7 @@ opts_data = {
 -P, --passwd-file= f  Get wallet passphrase from file 'f'
 -P, --passwd-file= f  Get wallet passphrase from file 'f'
 -q, --quiet           Produce quieter output; suppress some warnings
 -q, --quiet           Produce quieter output; suppress some warnings
 -r, --usr-randchars=n Get 'n' characters of additional randomness from user
 -r, --usr-randchars=n Get 'n' characters of additional randomness from user
-                      (min={g.min_urandchars}, max={g.max_urandchars}, default={g.usr_randchars})
+                      (min={cfg.min_urandchars}, max={cfg.max_urandchars}, default={cfg.usr_randchars})
 -S, --stdout          Write wallet data to stdout instead of file
 -S, --stdout          Write wallet data to stdout instead of file
 -v, --verbose         Produce more verbose output
 -v, --verbose         Produce more verbose output
 """,
 """,
@@ -82,13 +81,13 @@ FMT CODES:
 """
 """
 	},
 	},
 	'code': {
 	'code': {
-		'options': lambda s: s.format(
+		'options': lambda cfg,s: s.format(
 			ms_min=MasterShareIdx.min_val,
 			ms_min=MasterShareIdx.min_val,
 			ms_max=MasterShareIdx.max_val,
 			ms_max=MasterShareIdx.max_val,
-			g=g,
+			cfg=cfg,
 			gc=gc,
 			gc=gc,
 		),
 		),
-		'notes': lambda help_notes,s: s.format(
+		'notes': lambda cfg,help_notes,s: s.format(
 			f=help_notes('fmt_codes'),
 			f=help_notes('fmt_codes'),
 			n_pw=help_notes('passwd'),
 			n_pw=help_notes('passwd'),
 		)
 		)
@@ -97,7 +96,7 @@ FMT CODES:
 
 
 def print_shares_info():
 def print_shares_info():
 	si,out = 0,'\nComputed shares:\n'
 	si,out = 0,'\nComputed shares:\n'
-	if opt.master_share:
+	if cfg.master_share:
 		fs = '{:3}: {}->{} ' + yellow('(master share #{}, split id ') + '{}' + yellow(', share count {})\n')
 		fs = '{:3}: {}->{} ' + yellow('(master share #{}, split id ') + '{}' + yellow(', share count {})\n')
 		out += fs.format(
 		out += fs.format(
 				1,
 				1,
@@ -109,36 +108,36 @@ def print_shares_info():
 		si = 1
 		si = 1
 	for n,s in enumerate(shares[si:],si+1):
 	for n,s in enumerate(shares[si:],si+1):
 		out += f'{n:3}: {s.sid}\n'
 		out += f'{n:3}: {s.sid}\n'
-	qmsg(out)
+	cfg._util.qmsg(out)
 
 
-cmd_args = opts.init(opts_data)
+cfg = opts.init(opts_data)
 
 
-if len(cmd_args) + bool(opt.hidden_incog_input_params) < 2:
+if len(cfg._args) + bool(cfg.hidden_incog_input_params) < 2:
 	opts.usage()
 	opts.usage()
 
 
-if opt.master_share:
-	master_idx = MasterShareIdx(opt.master_share)
-	id_str = SeedSplitIDString(opt.id_str or 'default')
+if cfg.master_share:
+	master_idx = MasterShareIdx(cfg.master_share)
+	id_str = SeedSplitIDString(cfg.id_str or 'default')
 
 
-if opt.id_str and not opt.master_share:
+if cfg.id_str and not cfg.master_share:
 	die(1,'--id-str option meaningless in context of non-master-share join')
 	die(1,'--id-str option meaningless in context of non-master-share join')
 
 
 from .fileutil import check_infile
 from .fileutil import check_infile
 from .wallet import check_wallet_extension
 from .wallet import check_wallet_extension
-for arg in cmd_args:
+for arg in cfg._args:
 	check_wallet_extension(arg)
 	check_wallet_extension(arg)
 	check_infile(arg)
 	check_infile(arg)
 
 
 from .ui import do_license_msg
 from .ui import do_license_msg
-do_license_msg()
+do_license_msg(cfg)
 
 
-qmsg('Input files:\n  {}\n'.format('\n  '.join(cmd_args)))
+cfg._util.qmsg('Input files:\n  {}\n'.format('\n  '.join(cfg._args)))
 
 
-shares = [Wallet().seed] if opt.hidden_incog_input_params else []
-shares += [Wallet(fn).seed for fn in cmd_args]
+shares = [Wallet(cfg).seed] if cfg.hidden_incog_input_params else []
+shares += [Wallet(cfg,fn).seed for fn in cfg._args]
 
 
-if opt.master_share:
-	share1 = SeedShareMasterJoining(master_idx,shares[0],id_str,len(shares)).derived_seed
+if cfg.master_share:
+	share1 = SeedShareMasterJoining( cfg, master_idx, shares[0], id_str, len(shares) ).derived_seed
 else:
 else:
 	share1 = shares[0]
 	share1 = shares[0]
 
 
@@ -146,8 +145,8 @@ print_shares_info()
 
 
 msg_r('Joining {n}-of-{n} XOR split...'.format(n=len(shares)))
 msg_r('Joining {n}-of-{n} XOR split...'.format(n=len(shares)))
 
 
-seed_out = Seed.join_shares([share1]+shares[1:])
+seed_out = Seed.join_shares( cfg, [share1] + shares[1:] )
 
 
 msg(f'OK\nJoined Seed ID: {seed_out.sid.hl()}')
 msg(f'OK\nJoined Seed ID: {seed_out.sid.hl()}')
 
 
-Wallet(seed=seed_out).write_to_file()
+Wallet(cfg,seed=seed_out).write_to_file()

+ 17 - 18
mmgen/main_split.py

@@ -86,24 +86,23 @@ transaction reconfirmed before the timelock expires. Use at your own risk.
 	}
 	}
 }
 }
 
 
-cmd_args = opts.init(opts_data)
+cfg = opts.init(opts_data,need_amt=False)
 
 
-from .protocol import init_proto_from_opts
-proto = init_proto_from_opts()
+proto = cfg._proto
 
 
 die(1,'This command is disabled')
 die(1,'This command is disabled')
 
 
 # the following code is broken:
 # the following code is broken:
-opt.other_coin = opt.other_coin.upper() if opt.other_coin else proto.forks[-1][2].upper()
-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')
+cfg.other_coin = cfg.other_coin.upper() if cfg.other_coin else proto.forks[-1][2].upper()
+if cfg.other_coin.lower() not in [e[2] for e in proto.forks if e[3] == True]:
+	die(1,f'{cfg.other_coin!r}: not a replayable fork of {proto.coin} chain')
 
 
-if len(cmd_args) != 2:
+if len(cfg._args) != 2:
 	die(1,f'This command requires exactly two {gc.proj_name} addresses as arguments')
 	die(1,f'This command requires exactly two {gc.proj_name} addresses as arguments')
 
 
 from .addr import MMGenID
 from .addr import MMGenID
 try:
 try:
-	mmids = [MMGenID(a) for a in cmd_args]
+	mmids = [MMGenID(a) for a in cfg._args]
 except:
 except:
 	die(1,'Command line arguments must be valid MMGen IDs')
 	die(1,'Command line arguments must be valid MMGen IDs')
 
 
@@ -113,18 +112,18 @@ if mmids[0] == mmids[1]:
 from .tx import MMGenSplitTX
 from .tx import MMGenSplitTX
 from .protocol import init_proto
 from .protocol import init_proto
 
 
-if opt.tx_fees:
-	for idx,g_coin in ((1,opt.other_coin),(0,proto.coin)):
-		proto = init_proto(g_coin)
-		opt.fee = opt.tx_fees.split(',')[idx]
-		opts.opt_is_tx_fee('foo',opt.fee,'transaction fee') # raises exception on error
+if cfg.tx_fees:
+	for idx,g_coin in ((1,cfg.other_coin),(0,proto.coin)):
+		proto = init_proto( cfg, g_coin )
+		cfg.fee = cfg.tx_fees.split(',')[idx]
+		opts.opt_is_tx_fee('foo',cfg.fee,'transaction fee') # raises exception on error
 
 
 tx1 = MMGenSplitTX()
 tx1 = MMGenSplitTX()
-opt.no_blank = True
+cfg.no_blank = True
 
 
 async def main():
 async def main():
 	gmsg(f'Creating timelocked transaction for long chain ({proto.coin})')
 	gmsg(f'Creating timelocked transaction for long chain ({proto.coin})')
-	locktime = int(opt.locktime)
+	locktime = int(cfg.locktime)
 	if not locktime:
 	if not locktime:
 		rpc = rpc_init(proto)
 		rpc = rpc_init(proto)
 		locktime = rpc.call('getblockcount')
 		locktime = rpc.call('getblockcount')
@@ -133,9 +132,9 @@ async def main():
 	tx1.format()
 	tx1.format()
 	tx1.create_fn()
 	tx1.create_fn()
 
 
-	gmsg(f'\nCreating transaction for short chain ({opt.other_coin})')
+	gmsg(f'\nCreating transaction for short chain ({cfg.other_coin})')
 
 
-	proto = init_proto(opt.other_coin)
+	proto = init_proto( self.cfg, cfg.other_coin )
 
 
 	tx2 = MMGenSplitTX()
 	tx2 = MMGenSplitTX()
 	tx2.inputs = tx1.inputs
 	tx2.inputs = tx1.inputs
@@ -145,4 +144,4 @@ async def main():
 
 
 	for tx,desc in ((tx1,'Long chain (timelocked)'),(tx2,'Short chain')):
 	for tx,desc in ((tx1,'Long chain (timelocked)'),(tx2,'Short chain')):
 		tx.desc = desc + ' transaction'
 		tx.desc = desc + ' transaction'
-		tx.file.write(ask_write=False,ask_overwrite=not opt.yes,ask_write_default_yes=False)
+		tx.file.write(ask_write=False,ask_overwrite=not cfg.yes,ask_write_default_yes=False)

+ 10 - 9
mmgen/main_tool.py

@@ -23,8 +23,7 @@ mmgen-tool:  Perform various MMGen- and cryptocoin-related operations.
 
 
 import sys,os,importlib
 import sys,os,importlib
 import mmgen.opts as opts
 import mmgen.opts as opts
-from .globalvars import g,gc
-from .opts import opt
+from .globalvars import gc
 from .util import msg,Msg,die,capfirst,suf,async_run
 from .util import msg,Msg,die,capfirst,suf,async_run
 
 
 opts_data = {
 opts_data = {
@@ -45,7 +44,7 @@ opts_data = {
 -P, --passwd-file= f   Get passphrase from file 'f'.
 -P, --passwd-file= f   Get passphrase from file 'f'.
 -q, --quiet            Produce quieter output
 -q, --quiet            Produce quieter output
 -r, --usr-randchars=n  Get 'n' characters of additional randomness from
 -r, --usr-randchars=n  Get 'n' characters of additional randomness from
-                       user (min={g.min_urandchars}, max={g.max_urandchars})
+                       user (min={cfg.min_urandchars}, max={cfg.max_urandchars})
 -t, --type=t           Specify address type (valid choices: 'legacy',
 -t, --type=t           Specify address type (valid choices: 'legacy',
                        'compressed', 'segwit', 'bech32', 'zcash_z')
                        'compressed', 'segwit', 'bech32', 'zcash_z')
 -v, --verbose          Produce more verbose output
 -v, --verbose          Produce more verbose output
@@ -61,13 +60,13 @@ Type ‘{pn} help <command>’ for help on a particular command
 """
 """
 	},
 	},
 	'code': {
 	'code': {
-		'options': lambda s, help_notes: s.format(
+		'options': lambda cfg,s, help_notes: s.format(
 			kgs=help_notes('keygen_backends'),
 			kgs=help_notes('keygen_backends'),
 			coin_id=help_notes('coin_id'),
 			coin_id=help_notes('coin_id'),
-			g=g,
+			cfg=cfg,
 			gc=gc,
 			gc=gc,
 		),
 		),
-		'notes': lambda s, help_notes: s.format(
+		'notes': lambda cfg,s, help_notes: s.format(
 			ch=help_notes('tool_help'),
 			ch=help_notes('tool_help'),
 			pn=gc.prog_name)
 			pn=gc.prog_name)
 	}
 	}
@@ -346,7 +345,7 @@ def get_cmd_cls(cmd):
 def get_mod_cls(modname):
 def get_mod_cls(modname):
 	return getattr(importlib.import_module(f'mmgen.tool.{modname}'),'tool_cmd')
 	return getattr(importlib.import_module(f'mmgen.tool.{modname}'),'tool_cmd')
 
 
-if gc.prog_name == 'mmgen-tool' and not opt._lock:
+if gc.prog_name == 'mmgen-tool':
 
 
 	po = opts.init( opts_data, parse_only=True )
 	po = opts.init( opts_data, parse_only=True )
 
 
@@ -372,18 +371,20 @@ if gc.prog_name == 'mmgen-tool' and not opt._lock:
 	if not cls:
 	if not cls:
 		die(1,f'{cmd!r}: no such command')
 		die(1,f'{cmd!r}: no such command')
 
 
-	cmd,*args = opts.init(
+	cfg = opts.init(
 		opts_data,
 		opts_data,
 		parsed_opts = po,
 		parsed_opts = po,
 		need_proto  = cls.need_proto,
 		need_proto  = cls.need_proto,
 		init_opts   = {'rpc_backend':'aiohttp'} if cmd == 'twimport' else None )
 		init_opts   = {'rpc_backend':'aiohttp'} if cmd == 'twimport' else None )
 
 
+	cmd,*args = cfg._args
+
 	if cmd in ('help','usage') and args:
 	if cmd in ('help','usage') and args:
 		args[0] = 'command_name=' + args[0]
 		args[0] = 'command_name=' + args[0]
 
 
 	args,kwargs = process_args(cmd,args,cls)
 	args,kwargs = process_args(cmd,args,cls)
 
 
-	ret = getattr(cls(cmdname=cmd),cmd)(*args,**kwargs)
+	ret = getattr(cls(cfg,cmdname=cmd),cmd)(*args,**kwargs)
 
 
 	if type(ret).__name__ == 'coroutine':
 	if type(ret).__name__ == 'coroutine':
 		ret = async_run(ret)
 		ret = async_run(ret)

+ 21 - 20
mmgen/main_txbump.py

@@ -23,8 +23,7 @@ mmgen-txbump: Increase the fee on a replaceable (replace-by-fee) MMGen
 
 
 import mmgen.opts as opts
 import mmgen.opts as opts
 from .globalvars import gc
 from .globalvars import gc
-from .opts import opt
-from .util import msg,msg_r,qmsg,die,async_run
+from .util import msg,msg_r,die,async_run
 from .color import green
 from .color import green
 from .wallet import Wallet
 from .wallet import Wallet
 
 
@@ -86,7 +85,8 @@ FMT CODES:
 """
 """
 	},
 	},
 	'code': {
 	'code': {
-		'options': lambda help_notes,proto,s: s.format(
+		'options': lambda cfg,help_notes,proto,s: s.format(
+			cfg=cfg,
 			gc=gc,
 			gc=gc,
 			pnm=gc.proj_name,
 			pnm=gc.proj_name,
 			pnl=gc.proj_name.lower(),
 			pnl=gc.proj_name.lower(),
@@ -103,9 +103,9 @@ FMT CODES:
 	}
 	}
 }
 }
 
 
-cmd_args = opts.init(opts_data)
+cfg = opts.init(opts_data)
 
 
-tx_file = cmd_args.pop(0)
+tx_file = cfg._args.pop(0)
 
 
 from .fileutil import check_infile
 from .fileutil import check_infile
 check_infile(tx_file)
 check_infile(tx_file)
@@ -113,33 +113,34 @@ check_infile(tx_file)
 from .tx import CompletedTX,BumpTX,UnsignedTX,OnlineSignedTX
 from .tx import CompletedTX,BumpTX,UnsignedTX,OnlineSignedTX
 from .tx.sign import txsign,get_seed_files,get_keyaddrlist,get_keylist
 from .tx.sign import txsign,get_seed_files,get_keyaddrlist,get_keylist
 
 
-seed_files = get_seed_files(opt,cmd_args) if (cmd_args or opt.send) else None
+seed_files = get_seed_files(cfg,cfg._args) if (cfg._args or cfg.send) else None
 
 
 from .ui import do_license_msg
 from .ui import do_license_msg
-do_license_msg()
+do_license_msg(cfg)
 
 
-silent = opt.yes and opt.fee != None and opt.output_to_reduce != None
+silent = cfg.yes and cfg.fee != None and cfg.output_to_reduce != None
 
 
 async def main():
 async def main():
 
 
-	orig_tx = await CompletedTX(filename=tx_file)
+	orig_tx = await CompletedTX(cfg=cfg,filename=tx_file)
 
 
 	if not silent:
 	if not silent:
 		msg(green('ORIGINAL TRANSACTION'))
 		msg(green('ORIGINAL TRANSACTION'))
 		msg(orig_tx.info.format(terse=True))
 		msg(orig_tx.info.format(terse=True))
 
 
-	kal = get_keyaddrlist(orig_tx.proto,opt)
-	kl = get_keylist(orig_tx.proto,opt)
+	kal = get_keyaddrlist(cfg,orig_tx.proto)
+	kl = get_keylist(cfg,orig_tx.proto)
 	sign_and_send = bool(seed_files or kl or kal)
 	sign_and_send = bool(seed_files or kl or kal)
 
 
 	from .tw.ctl import TwCtl
 	from .tw.ctl import TwCtl
 	tx = await BumpTX(
 	tx = await BumpTX(
+		cfg  = cfg,
 		data = orig_tx.__dict__,
 		data = orig_tx.__dict__,
 		send = sign_and_send,
 		send = sign_and_send,
-		twctl = await TwCtl(orig_tx.proto) if orig_tx.proto.tokensym else None )
+		twctl = await TwCtl(cfg,orig_tx.proto) if orig_tx.proto.tokensym else None )
 
 
 	from .rpc import rpc_init
 	from .rpc import rpc_init
-	tx.rpc = await rpc_init(tx.proto)
+	tx.rpc = await rpc_init(cfg,tx.proto)
 
 
 	msg('Creating replacement transaction')
 	msg('Creating replacement transaction')
 
 
@@ -150,13 +151,13 @@ async def main():
 	if not silent:
 	if not silent:
 		msg(f'Minimum fee for new transaction: {tx.min_fee.hl()} {tx.proto.coin}')
 		msg(f'Minimum fee for new transaction: {tx.min_fee.hl()} {tx.proto.coin}')
 
 
-	tx.usr_fee = tx.get_usr_fee_interactive(fee=opt.fee,desc='User-selected')
+	tx.usr_fee = tx.get_usr_fee_interactive(fee=cfg.fee,desc='User-selected')
 
 
 	tx.bump_fee(output_idx,tx.usr_fee)
 	tx.bump_fee(output_idx,tx.usr_fee)
 
 
 	assert tx.fee <= tx.proto.max_tx_fee
 	assert tx.fee <= tx.proto.max_tx_fee
 
 
-	if not opt.yes:
+	if not cfg.yes:
 		tx.add_comment()   # edits an existing comment
 		tx.add_comment()   # edits an existing comment
 
 
 	await tx.create_serialized(bump=True)
 	await tx.create_serialized(bump=True)
@@ -164,23 +165,23 @@ async def main():
 	tx.add_timestamp()
 	tx.add_timestamp()
 	tx.add_blockcount()
 	tx.add_blockcount()
 
 
-	qmsg('Fee successfully increased')
+	cfg._util.qmsg('Fee successfully increased')
 
 
 	if not silent:
 	if not silent:
 		msg(green('\nREPLACEMENT TRANSACTION:'))
 		msg(green('\nREPLACEMENT TRANSACTION:'))
 		msg_r(tx.info.format(terse=True))
 		msg_r(tx.info.format(terse=True))
 
 
 	if sign_and_send:
 	if sign_and_send:
-		tx2 = UnsignedTX(data=tx.__dict__)
-		tx3 = await txsign(tx2,seed_files,kl,kal)
+		tx2 = UnsignedTX(cfg=cfg,data=tx.__dict__)
+		tx3 = await txsign(cfg,tx2,seed_files,kl,kal)
 		if tx3:
 		if tx3:
-			tx4 = await OnlineSignedTX(data=tx3.__dict__)
+			tx4 = await OnlineSignedTX(cfg=cfg,data=tx3.__dict__)
 			tx4.file.write(ask_write=False)
 			tx4.file.write(ask_write=False)
 			await tx4.send(exit_on_fail=True)
 			await tx4.send(exit_on_fail=True)
 			tx4.file.write(ask_write=False)
 			tx4.file.write(ask_write=False)
 		else:
 		else:
 			die(2,'Transaction could not be signed')
 			die(2,'Transaction could not be signed')
 	else:
 	else:
-		tx.file.write(ask_write=not opt.yes,ask_write_default_yes=False,ask_overwrite=not opt.yes)
+		tx.file.write(ask_write=not cfg.yes,ask_write_default_yes=False,ask_overwrite=not cfg.yes)
 
 
 async_run(main())
 async_run(main())

+ 15 - 19
mmgen/main_txcreate.py

@@ -22,8 +22,7 @@ mmgen-txcreate: Create a cryptocoin transaction with MMGen- and/or non-MMGen
 """
 """
 
 
 import mmgen.opts as opts
 import mmgen.opts as opts
-from .globalvars import g,gc
-from .opts import opt
+from .globalvars import gc
 from .util import fmt_list,async_run
 from .util import fmt_list,async_run
 
 
 opts_data = {
 opts_data = {
@@ -38,7 +37,7 @@ opts_data = {
 -B, --no-blank        Don't blank screen before displaying unspent outputs
 -B, --no-blank        Don't blank screen before displaying unspent outputs
 -c, --comment-file=f  Source the transaction's comment from file 'f'
 -c, --comment-file=f  Source the transaction's comment from file 'f'
 -C, --fee-estimate-confs=c Desired number of confirmations for fee estimation
 -C, --fee-estimate-confs=c Desired number of confirmations for fee estimation
-                      (default: {g.fee_estimate_confs})
+                      (default: {cfg.fee_estimate_confs})
 -d, --outdir=      d  Specify an alternate directory 'd' for output
 -d, --outdir=      d  Specify an alternate directory 'd' for output
 -D, --contract-data=D Path to hex-encoded contract data (ETH only)
 -D, --contract-data=D Path to hex-encoded contract data (ETH only)
 -E, --fee-estimate-mode=M Specify the network fee estimate mode.  Choices:
 -E, --fee-estimate-mode=M Specify the network fee estimate mode.  Choices:
@@ -66,41 +65,38 @@ opts_data = {
 		'notes': '\n{c}\n{F}\n{x}',
 		'notes': '\n{c}\n{F}\n{x}',
 	},
 	},
 	'code': {
 	'code': {
-		'options': lambda proto,help_notes,s: s.format(
+		'options': lambda cfg,proto,help_notes,s: s.format(
 			fu=help_notes('rel_fee_desc'),
 			fu=help_notes('rel_fee_desc'),
 			fl=help_notes('fee_spec_letters'),
 			fl=help_notes('fee_spec_letters'),
-			fe_all=fmt_list(g.autoset_opts['fee_estimate_mode'].choices,fmt='no_spc'),
-			fe_dfl=g.autoset_opts['fee_estimate_mode'].choices[0],
+			fe_all=fmt_list(cfg.autoset_opts['fee_estimate_mode'].choices,fmt='no_spc'),
+			fe_dfl=cfg.autoset_opts['fee_estimate_mode'].choices[0],
 			cu=proto.coin,
 			cu=proto.coin,
-			g=g),
-		'notes': lambda help_notes,s: s.format(
+			cfg=cfg),
+		'notes': lambda cfg,help_notes,s: s.format(
 			c = help_notes('txcreate'),
 			c = help_notes('txcreate'),
 			F = help_notes('fee'),
 			F = help_notes('fee'),
 			x = help_notes('txcreate_examples') )
 			x = help_notes('txcreate_examples') )
 	}
 	}
 }
 }
 
 
-cmd_args = opts.init(opts_data)
+cfg = opts.init(opts_data)
 
 
 async def main():
 async def main():
 
 
-	from .protocol import init_proto_from_opts
-	proto = init_proto_from_opts(need_amt=True)
-
 	from .tx import NewTX
 	from .tx import NewTX
-	tx1 = await NewTX(proto=proto)
+	tx1 = await NewTX(cfg=cfg,proto=cfg._proto)
 
 
 	from .rpc import rpc_init
 	from .rpc import rpc_init
-	tx1.rpc = await rpc_init(proto)
+	tx1.rpc = await rpc_init(cfg,cfg._proto)
 
 
 	tx2 = await tx1.create(
 	tx2 = await tx1.create(
-		cmd_args = cmd_args,
-		locktime = int(opt.locktime or 0),
-		do_info  = opt.info )
+		cmd_args = cfg._args,
+		locktime = int(cfg.locktime or 0),
+		do_info  = cfg.info )
 
 
 	tx2.file.write(
 	tx2.file.write(
-		ask_write             = not opt.yes,
-		ask_overwrite         = not opt.yes,
+		ask_write             = not cfg.yes,
+		ask_overwrite         = not cfg.yes,
 		ask_write_default_yes = False )
 		ask_write_default_yes = False )
 
 
 async_run(main())
 async_run(main())

+ 17 - 20
mmgen/main_txdo.py

@@ -21,8 +21,7 @@ mmgen-txdo: Create, sign and broadcast an online MMGen transaction
 """
 """
 
 
 import mmgen.opts as opts
 import mmgen.opts as opts
-from .globalvars import g,gc
-from .opts import opt
+from .globalvars import gc
 from .util import die,fmt_list,async_run
 from .util import die,fmt_list,async_run
 from .wallet import Wallet
 from .wallet import Wallet
 from .subseed import SubSeedIdxRange
 from .subseed import SubSeedIdxRange
@@ -41,7 +40,7 @@ opts_data = {
 -B, --no-blank         Don't blank screen before displaying unspent outputs
 -B, --no-blank         Don't blank screen before displaying unspent outputs
 -c, --comment-file=  f Source the transaction's comment from file 'f'
 -c, --comment-file=  f Source the transaction's comment from file 'f'
 -C, --fee-estimate-confs=c Desired number of confirmations for fee estimation
 -C, --fee-estimate-confs=c Desired number of confirmations for fee estimation
-                       (default: {g.fee_estimate_confs})
+                       (default: {cfg.fee_estimate_confs})
 -d, --outdir=        d Specify an alternate directory 'd' for output
 -d, --outdir=        d Specify an alternate directory 'd' for output
 -D, --contract-data= D Path to hex-encoded contract data (ETH only)
 -D, --contract-data= D Path to hex-encoded contract data (ETH only)
 -e, --echo-passphrase  Print passphrase to screen when typing it
 -e, --echo-passphrase  Print passphrase to screen when typing it
@@ -103,19 +102,19 @@ FMT CODES:
 {x}"""
 {x}"""
 	},
 	},
 	'code': {
 	'code': {
-		'options': lambda proto,help_notes,s: s.format(
-			g=g,gc=gc,pnm=gc.proj_name,pnl=gc.proj_name.lower(),
+		'options': lambda cfg,proto,help_notes,s: s.format(
+			gc=gc,cfg=cfg,pnm=gc.proj_name,pnl=gc.proj_name.lower(),
 			kgs=help_notes('keygen_backends'),
 			kgs=help_notes('keygen_backends'),
 			coin_id=help_notes('coin_id'),
 			coin_id=help_notes('coin_id'),
 			fu=help_notes('rel_fee_desc'),
 			fu=help_notes('rel_fee_desc'),
 			fl=help_notes('fee_spec_letters'),
 			fl=help_notes('fee_spec_letters'),
 			ss=help_notes('dfl_subseeds'),
 			ss=help_notes('dfl_subseeds'),
 			ss_max=SubSeedIdxRange.max_idx,
 			ss_max=SubSeedIdxRange.max_idx,
-			fe_all=fmt_list(g.autoset_opts['fee_estimate_mode'].choices,fmt='no_spc'),
-			fe_dfl=g.autoset_opts['fee_estimate_mode'].choices[0],
+			fe_all=fmt_list(cfg.autoset_opts['fee_estimate_mode'].choices,fmt='no_spc'),
+			fe_dfl=cfg.autoset_opts['fee_estimate_mode'].choices[0],
 			dsl=help_notes('dfl_seed_len'),
 			dsl=help_notes('dfl_seed_len'),
 			cu=proto.coin),
 			cu=proto.coin),
-		'notes': lambda help_notes,s: s.format(
+		'notes': lambda cfg,help_notes,s: s.format(
 			c = help_notes('txcreate'),
 			c = help_notes('txcreate'),
 			F = help_notes('fee'),
 			F = help_notes('fee'),
 			s = help_notes('txsign'),
 			s = help_notes('txsign'),
@@ -124,34 +123,32 @@ FMT CODES:
 	}
 	}
 }
 }
 
 
-cmd_args = opts.init(opts_data)
+cfg = opts.init(opts_data)
 
 
 from .tx import NewTX,OnlineSignedTX
 from .tx import NewTX,OnlineSignedTX
 from .tx.sign import txsign,get_seed_files,get_keyaddrlist,get_keylist
 from .tx.sign import txsign,get_seed_files,get_keyaddrlist,get_keylist
 
 
-seed_files = get_seed_files(opt,cmd_args)
+seed_files = get_seed_files(cfg,cfg._args)
 
 
 async def main():
 async def main():
-	from .protocol import init_proto_from_opts
-	proto = init_proto_from_opts(need_amt=True)
 
 
-	tx1 = await NewTX(proto=proto)
+	tx1 = await NewTX(cfg=cfg,proto=cfg._proto)
 
 
 	from .rpc import rpc_init
 	from .rpc import rpc_init
-	tx1.rpc = await rpc_init(proto)
+	tx1.rpc = await rpc_init(cfg,cfg._proto)
 
 
 	tx2 = await tx1.create(
 	tx2 = await tx1.create(
-		cmd_args = cmd_args,
-		locktime = int(opt.locktime or 0),
+		cmd_args = cfg._args,
+		locktime = int(cfg.locktime or 0),
 		caller   = 'txdo' )
 		caller   = 'txdo' )
 
 
-	kal = get_keyaddrlist(proto,opt)
-	kl = get_keylist(proto,opt)
+	kal = get_keyaddrlist(cfg,cfg._proto)
+	kl = get_keylist(cfg,cfg._proto)
 
 
-	tx3 = await txsign(tx2,seed_files,kl,kal)
+	tx3 = await txsign(cfg,tx2,seed_files,kl,kal)
 
 
 	if tx3:
 	if tx3:
-		tx4 = await OnlineSignedTX(data=tx3.__dict__)
+		tx4 = await OnlineSignedTX(cfg=cfg,data=tx3.__dict__)
 		tx4.file.write(ask_write=False)
 		tx4.file.write(ask_write=False)
 		await tx4.send(exit_on_fail=True)
 		await tx4.send(exit_on_fail=True)
 		tx4.file.write(ask_overwrite=False,ask_write=False)
 		tx4.file.write(ask_overwrite=False,ask_write=False)

+ 12 - 12
mmgen/main_txsend.py

@@ -24,8 +24,7 @@ import sys
 
 
 import mmgen.opts as opts
 import mmgen.opts as opts
 from .globalvars import gc
 from .globalvars import gc
-from .opts import opt
-from .util import vmsg,qmsg,async_run
+from .util import async_run
 
 
 opts_data = {
 opts_data = {
 	'sets': [('yes', True, 'quiet', True)],
 	'sets': [('yes', True, 'quiet', True)],
@@ -43,39 +42,40 @@ opts_data = {
 	}
 	}
 }
 }
 
 
-cmd_args = opts.init(opts_data)
+cfg = opts.init(opts_data)
 
 
-if len(cmd_args) == 1:
-	infile = cmd_args[0]
+if len(cfg._args) == 1:
+	infile = cfg._args[0]
 	from .fileutil import check_infile
 	from .fileutil import check_infile
 	check_infile(infile)
 	check_infile(infile)
 else:
 else:
 	opts.usage()
 	opts.usage()
 
 
-if not opt.status:
+if not cfg.status:
 	from .ui import do_license_msg
 	from .ui import do_license_msg
-	do_license_msg()
+	do_license_msg(cfg)
 
 
 async def main():
 async def main():
 
 
 	from .tx import OnlineSignedTX
 	from .tx import OnlineSignedTX
 
 
 	tx = await OnlineSignedTX(
 	tx = await OnlineSignedTX(
+		cfg        = cfg,
 		filename   = infile,
 		filename   = infile,
 		quiet_open = True )
 		quiet_open = True )
 
 
 	from .rpc import rpc_init
 	from .rpc import rpc_init
-	tx.rpc = await rpc_init(tx.proto)
+	tx.rpc = await rpc_init(cfg,tx.proto)
 
 
-	vmsg(f'Signed transaction file {infile!r} is valid')
+	cfg._util.vmsg(f'Signed transaction file {infile!r} is valid')
 
 
-	if opt.status:
+	if cfg.status:
 		if tx.coin_txid:
 		if tx.coin_txid:
-			qmsg(f'{tx.proto.coin} txid: {tx.coin_txid.hl()}')
+			cfg._util.qmsg(f'{tx.proto.coin} txid: {tx.coin_txid.hl()}')
 		await tx.status.display(usr_req=True)
 		await tx.status.display(usr_req=True)
 		sys.exit(0)
 		sys.exit(0)
 
 
-	if not opt.yes:
+	if not cfg.yes:
 		tx.info.view_with_prompt('View transaction details?')
 		tx.info.view_with_prompt('View transaction details?')
 		if tx.add_comment(): # edits an existing comment, returns true if changed
 		if tx.add_comment(): # edits an existing comment, returns true if changed
 			tx.file.write(ask_write_default_yes=True)
 			tx.file.write(ask_write_default_yes=True)

+ 21 - 19
mmgen/main_txsign.py

@@ -22,7 +22,6 @@ mmgen-txsign: Sign a transaction generated by 'mmgen-txcreate'
 
 
 import mmgen.opts as opts
 import mmgen.opts as opts
 from .globalvars import gc
 from .globalvars import gc
-from .opts import opt
 from .util import msg,ymsg,die,async_run
 from .util import msg,ymsg,die,async_run
 from .subseed import SubSeedIdxRange
 from .subseed import SubSeedIdxRange
 from .wallet import Wallet
 from .wallet import Wallet
@@ -83,7 +82,8 @@ FMT CODES:
 """
 """
 	},
 	},
 	'code': {
 	'code': {
-		'options': lambda proto,help_notes,s: s.format(
+		'options': lambda cfg,proto,help_notes,s: s.format(
+			cfg=cfg,
 			gc=gc,
 			gc=gc,
 			pnm=gc.proj_name,
 			pnm=gc.proj_name,
 			pnl=gc.proj_name.lower(),
 			pnl=gc.proj_name.lower(),
@@ -99,7 +99,9 @@ FMT CODES:
 	}
 	}
 }
 }
 
 
-infiles = opts.init(opts_data)
+cfg = opts.init(opts_data)
+
+infiles = cfg._args
 
 
 if not infiles:
 if not infiles:
 	opts.usage()
 	opts.usage()
@@ -108,14 +110,14 @@ from .fileutil import check_infile
 for i in infiles:
 for i in infiles:
 	check_infile(i)
 	check_infile(i)
 
 
-if not opt.info and not opt.terse_info:
+if not cfg.info and not cfg.terse_info:
 	from .ui import do_license_msg
 	from .ui import do_license_msg
-	do_license_msg(immed=True)
+	do_license_msg(cfg,immed=True)
 
 
 from .tx.sign import *
 from .tx.sign import *
 
 
-tx_files   = get_tx_files(opt,infiles)
-seed_files = get_seed_files(opt,infiles)
+tx_files   = get_tx_files(cfg,infiles)
+seed_files = get_seed_files(cfg,infiles)
 
 
 async def main():
 async def main():
 
 
@@ -129,33 +131,33 @@ async def main():
 			msg(orange(f'\nTransaction{tx_num_disp} of {len(tx_files)}:'))
 			msg(orange(f'\nTransaction{tx_num_disp} of {len(tx_files)}:'))
 
 
 		from .tx import UnsignedTX
 		from .tx import UnsignedTX
-		tx1 = UnsignedTX(filename=tx_file)
+		tx1 = UnsignedTX(cfg=cfg,filename=tx_file)
 
 
-		vmsg(f'Successfully opened transaction file {tx_file!r}')
+		cfg._util.vmsg(f'Successfully opened transaction file {tx_file!r}')
 
 
 		if tx1.proto.sign_mode == 'daemon':
 		if tx1.proto.sign_mode == 'daemon':
 			from .rpc import rpc_init
 			from .rpc import rpc_init
-			tx1.rpc = await rpc_init(tx1.proto)
+			tx1.rpc = await rpc_init(cfg,tx1.proto)
 
 
-		if opt.tx_id:
+		if cfg.tx_id:
 			msg(tx1.txid)
 			msg(tx1.txid)
 			continue
 			continue
 
 
-		if opt.info or opt.terse_info:
-			tx1.view(pause=False,terse=opt.terse_info)
+		if cfg.info or cfg.terse_info:
+			tx1.view(pause=False,terse=cfg.terse_info)
 			continue
 			continue
 
 
-		if not opt.yes:
+		if not cfg.yes:
 			tx1.info.view_with_prompt(f'View data for transaction{tx_num_disp}?')
 			tx1.info.view_with_prompt(f'View data for transaction{tx_num_disp}?')
 
 
-		kal = get_keyaddrlist(tx1.proto,opt)
-		kl = get_keylist(tx1.proto,opt)
+		kal = get_keyaddrlist(cfg,tx1.proto)
+		kl = get_keylist(cfg,tx1.proto)
 
 
-		tx2 = await txsign(tx1,seed_files,kl,kal,tx_num_disp)
+		tx2 = await txsign(cfg,tx1,seed_files,kl,kal,tx_num_disp)
 		if tx2:
 		if tx2:
-			if not opt.yes:
+			if not cfg.yes:
 				tx2.add_comment() # edits an existing comment
 				tx2.add_comment() # edits an existing comment
-			tx2.file.write(ask_write=not opt.yes,ask_write_default_yes=True,add_desc=tx_num_disp)
+			tx2.file.write(ask_write=not cfg.yes,ask_write_default_yes=True,add_desc=tx_num_disp)
 		else:
 		else:
 			ymsg('Transaction could not be signed')
 			ymsg('Transaction could not be signed')
 			bad_tx_count += 1
 			bad_tx_count += 1

+ 30 - 23
mmgen/main_wallet.py

@@ -22,10 +22,9 @@ main_wallet: Entry point for MMGen wallet-related scripts
 
 
 import sys,os
 import sys,os
 import mmgen.opts as opts
 import mmgen.opts as opts
-from .globalvars import g,gc
-from .opts import opt
+from .globalvars import gc
 from .color import green,yellow
 from .color import green,yellow
-from .util import msg,qmsg,vmsg,gmsg_r,ymsg,bmsg,die,capfirst
+from .util import msg,gmsg_r,ymsg,bmsg,die,capfirst
 from .wallet import Wallet,get_wallet_cls
 from .wallet import Wallet,get_wallet_cls
 
 
 usage = '[opts] [infile]'
 usage = '[opts] [infile]'
@@ -113,7 +112,7 @@ opts_data = {
 -P, --passwd-file= f  Get wallet passphrase from file 'f'
 -P, --passwd-file= f  Get wallet passphrase from file 'f'
 -q, --quiet           Produce quieter output; suppress some warnings
 -q, --quiet           Produce quieter output; suppress some warnings
 -r, --usr-randchars=n Get 'n' characters of additional randomness from user
 -r, --usr-randchars=n Get 'n' characters of additional randomness from user
-                      (min={g.min_urandchars}, max={g.max_urandchars}, default={g.usr_randchars})
+                      (min={cfg.min_urandchars}, max={cfg.max_urandchars}, default={cfg.usr_randchars})
 -S, --stdout          Write wallet data to stdout instead of file
 -S, --stdout          Write wallet data to stdout instead of file
 -v, --verbose         Produce more verbose output
 -v, --verbose         Produce more verbose output
 """,
 """,
@@ -127,16 +126,16 @@ FMT CODES:
 """
 """
 	},
 	},
 	'code': {
 	'code': {
-		'options': lambda help_notes,s: s.format(
+		'options': lambda cfg,help_notes,s: s.format(
 			iaction=capfirst(iaction),
 			iaction=capfirst(iaction),
 			oaction=capfirst(oaction),
 			oaction=capfirst(oaction),
 			ms_min=help_notes('MasterShareIdx').min_val,
 			ms_min=help_notes('MasterShareIdx').min_val,
 			ms_max=help_notes('MasterShareIdx').max_val,
 			ms_max=help_notes('MasterShareIdx').max_val,
 			dsl=help_notes('dfl_seed_len'),
 			dsl=help_notes('dfl_seed_len'),
-			g=g,
+			cfg=cfg,
 			gc=gc,
 			gc=gc,
 		),
 		),
-		'notes': lambda help_notes,s: s.format(
+		'notes': lambda cfg,help_notes,s: s.format(
 			f=help_notes('fmt_codes'),
 			f=help_notes('fmt_codes'),
 			n_ss=('',help_notes('seedsplit')+'\n\n')[do_ss_note],
 			n_ss=('',help_notes('seedsplit')+'\n\n')[do_ss_note],
 			n_sw=('',help_notes('subwallet')+'\n\n')[do_sw_note],
 			n_sw=('',help_notes('subwallet')+'\n\n')[do_sw_note],
@@ -146,7 +145,9 @@ FMT CODES:
 	}
 	}
 }
 }
 
 
-cmd_args = opts.init(opts_data,opt_filter=opt_filter,need_proto=False)
+cfg = opts.init(opts_data,opt_filter=opt_filter,need_proto=False)
+
+cmd_args = cfg._args
 
 
 if invoked_as == 'subgen':
 if invoked_as == 'subgen':
 	from .subseed import SubSeedIdx
 	from .subseed import SubSeedIdx
@@ -154,7 +155,7 @@ if invoked_as == 'subgen':
 elif invoked_as == 'seedsplit':
 elif invoked_as == 'seedsplit':
 	from .obj import get_obj
 	from .obj import get_obj
 	from .seedsplit import SeedSplitSpecifier,MasterShareIdx
 	from .seedsplit import SeedSplitSpecifier,MasterShareIdx
-	master_share = MasterShareIdx(opt.master_share) if opt.master_share else None
+	master_share = MasterShareIdx(cfg.master_share) if cfg.master_share else None
 	if cmd_args:
 	if cmd_args:
 		sss = get_obj(SeedSplitSpecifier,s=cmd_args.pop(),silent=True)
 		sss = get_obj(SeedSplitSpecifier,s=cmd_args.pop(),silent=True)
 		if master_share:
 		if master_share:
@@ -178,26 +179,27 @@ if cmd_args:
 		opts.usage()
 		opts.usage()
 	check_infile(cmd_args[0])
 	check_infile(cmd_args[0])
 
 
-sf = get_seed_file(cmd_args,nargs,invoked_as=invoked_as)
+sf = get_seed_file(cfg,nargs,invoked_as=invoked_as)
 
 
 if invoked_as != 'chk':
 if invoked_as != 'chk':
 	from .ui import do_license_msg
 	from .ui import do_license_msg
-	do_license_msg()
+	do_license_msg(cfg)
 
 
 if invoked_as == 'gen':
 if invoked_as == 'gen':
 	ss_in = None
 	ss_in = None
 else:
 else:
 	ss_in = Wallet(
 	ss_in = Wallet(
-		sf,
+		cfg     = cfg,
+		fn      = sf,
 		passchg = invoked_as=='passchg' )
 		passchg = invoked_as=='passchg' )
 	m1 = green('Processing input wallet ')
 	m1 = green('Processing input wallet ')
 	m2 = ss_in.seed.sid.hl()
 	m2 = ss_in.seed.sid.hl()
-	m3 = yellow(' (default wallet)') if sf and os.path.dirname(sf) == g.data_dir else ''
+	m3 = yellow(' (default wallet)') if sf and os.path.dirname(sf) == cfg.data_dir else ''
 	msg(m1+m2+m3)
 	msg(m1+m2+m3)
 
 
 if invoked_as == 'chk':
 if invoked_as == 'chk':
 	lbl = ss_in.ssdata.label.hl() if hasattr(ss_in.ssdata,'label') else 'NONE'
 	lbl = ss_in.ssdata.label.hl() if hasattr(ss_in.ssdata,'label') else 'NONE'
-	vmsg(f'Wallet label: {lbl}')
+	cfg._util.vmsg(f'Wallet label: {lbl}')
 	# TODO: display creation date
 	# TODO: display creation date
 	sys.exit(0)
 	sys.exit(0)
 
 
@@ -206,20 +208,23 @@ if invoked_as != 'gen':
 
 
 if invoked_as == 'subgen':
 if invoked_as == 'subgen':
 	ss_out = Wallet(
 	ss_out = Wallet(
+		cfg      = cfg,
 		seed_bin = ss_in.seed.subseed(ss_idx,print_msg=True).data )
 		seed_bin = ss_in.seed.subseed(ss_idx,print_msg=True).data )
 elif invoked_as == 'seedsplit':
 elif invoked_as == 'seedsplit':
 	shares = ss_in.seed.split(sss.count,sss.id,master_share)
 	shares = ss_in.seed.split(sss.count,sss.id,master_share)
 	seed_out = shares.get_share_by_idx(sss.idx,base_seed=True)
 	seed_out = shares.get_share_by_idx(sss.idx,base_seed=True)
 	msg(seed_out.get_desc(ui=True))
 	msg(seed_out.get_desc(ui=True))
 	ss_out = Wallet(
 	ss_out = Wallet(
+		cfg  = cfg,
 		seed = seed_out )
 		seed = seed_out )
 else:
 else:
 	ss_out = Wallet(
 	ss_out = Wallet(
+		cfg     = cfg,
 		ss      = ss_in,
 		ss      = ss_in,
 		passchg = invoked_as == 'passchg' )
 		passchg = invoked_as == 'passchg' )
 
 
 if invoked_as == 'gen':
 if invoked_as == 'gen':
-	qmsg(f"This wallet's Seed ID: {ss_out.seed.sid.hl()}")
+	cfg._util.qmsg(f"This wallet's Seed ID: {ss_out.seed.sid.hl()}")
 
 
 if invoked_as == 'passchg':
 if invoked_as == 'passchg':
 	def data_changed(attrs):
 	def data_changed(attrs):
@@ -227,7 +232,7 @@ if invoked_as == 'passchg':
 			if getattr(ss_out.ssdata,attr) != getattr(ss_in.ssdata,attr):
 			if getattr(ss_out.ssdata,attr) != getattr(ss_in.ssdata,attr):
 				return True
 				return True
 		return False
 		return False
-	if not ( opt.force_update or data_changed(('passwd','hash_preset','label')) ):
+	if not ( cfg.force_update or data_changed(('passwd','hash_preset','label')) ):
 		die(1,'Password, hash preset and label are unchanged.  Taking no action')
 		die(1,'Password, hash preset and label are unchanged.  Taking no action')
 
 
 if invoked_as == 'passchg':
 if invoked_as == 'passchg':
@@ -235,7 +240,7 @@ if invoked_as == 'passchg':
 	def secure_delete(fn):
 	def secure_delete(fn):
 		bmsg('Securely deleting old wallet')
 		bmsg('Securely deleting old wallet')
 		from .fileutil import shred_file
 		from .fileutil import shred_file
-		shred_file( fn, verbose = opt.verbose )
+		shred_file( fn, verbose = cfg.verbose )
 
 
 	def rename_old_wallet_maybe(silent):
 	def rename_old_wallet_maybe(silent):
 		# though very unlikely, old and new wallets could have same Key ID and thus same filename.
 		# though very unlikely, old and new wallets could have same Key ID and thus same filename.
@@ -251,31 +256,33 @@ if invoked_as == 'passchg':
 		else:
 		else:
 			return old_fn
 			return old_fn
 
 
-	if ss_in.infile.dirname == g.data_dir:
+	if ss_in.infile.dirname == cfg.data_dir:
 		from .ui import confirm_or_raise
 		from .ui import confirm_or_raise
 		confirm_or_raise(
 		confirm_or_raise(
+			cfg      = cfg,
 			message  = yellow('Confirmation of default wallet update'),
 			message  = yellow('Confirmation of default wallet update'),
 			action   = 'update the default wallet',
 			action   = 'update the default wallet',
 			exit_msg = 'Password not changed' )
 			exit_msg = 'Password not changed' )
 		old_wallet = rename_old_wallet_maybe(silent=True)
 		old_wallet = rename_old_wallet_maybe(silent=True)
-		ss_out.write_to_file( desc='New wallet', outdir=g.data_dir )
+		ss_out.write_to_file( desc='New wallet', outdir=cfg.data_dir )
 		secure_delete( old_wallet )
 		secure_delete( old_wallet )
 	else:
 	else:
 		old_wallet = rename_old_wallet_maybe(silent=False)
 		old_wallet = rename_old_wallet_maybe(silent=False)
 		ss_out.write_to_file()
 		ss_out.write_to_file()
 		from .ui import keypress_confirm
 		from .ui import keypress_confirm
-		if keypress_confirm(f'Securely delete old wallet {old_wallet!r}?'):
+		if keypress_confirm( cfg, f'Securely delete old wallet {old_wallet!r}?' ):
 			secure_delete( old_wallet )
 			secure_delete( old_wallet )
-elif invoked_as == 'gen' and not opt.outdir and not opt.stdout:
+elif invoked_as == 'gen' and not cfg.outdir and not cfg.stdout:
 	from .filename import find_file_in_dir
 	from .filename import find_file_in_dir
-	if find_file_in_dir( get_wallet_cls('mmgen'), g.data_dir ):
+	if find_file_in_dir( get_wallet_cls('mmgen'), cfg.data_dir ):
 		ss_out.write_to_file()
 		ss_out.write_to_file()
 	else:
 	else:
 		from .ui import keypress_confirm
 		from .ui import keypress_confirm
 		if keypress_confirm(
 		if keypress_confirm(
+				cfg,
 				'Make this wallet your default and move it to the data directory?',
 				'Make this wallet your default and move it to the data directory?',
 				default_yes = True ):
 				default_yes = True ):
-			ss_out.write_to_file(outdir=g.data_dir)
+			ss_out.write_to_file(outdir=cfg.data_dir)
 		else:
 		else:
 			ss_out.write_to_file()
 			ss_out.write_to_file()
 else:
 else:

+ 22 - 16
mmgen/main_xmrwallet.py

@@ -21,7 +21,11 @@ mmgen-xmrwallet: Perform various Monero wallet operations for addresses
                  in an MMGen XMR key-address file
                  in an MMGen XMR key-address file
 """
 """
 
 
-from .common import *
+from collections import namedtuple
+
+import mmgen.opts as opts
+from .globalvars import gc
+from .util import ymsg,die,async_run
 from .xmrwallet import xmrwallet_uarg_info,MoneroWalletOps
 from .xmrwallet import xmrwallet_uarg_info,MoneroWalletOps
 
 
 opts_data = {
 opts_data = {
@@ -58,9 +62,9 @@ opts_data = {
 -S, --no-stop-wallet-daemon      Don’t stop the wallet daemon at exit
 -S, --no-stop-wallet-daemon      Don’t stop the wallet daemon at exit
 -w, --wallet-dir=D               Output or operate on wallets in directory 'D'
 -w, --wallet-dir=D               Output or operate on wallets in directory 'D'
                                  instead of the working directory
                                  instead of the working directory
--H, --wallet-rpc-host=host       Wallet RPC hostname (default: {g.monero_wallet_rpc_host!r})
--U, --wallet-rpc-user=user       Wallet RPC username (default: {g.monero_wallet_rpc_user!r})
--P, --wallet-rpc-password=pass   Wallet RPC password (default: {g.monero_wallet_rpc_password!r})
+-H, --wallet-rpc-host=host       Wallet RPC hostname (default: {cfg.monero_wallet_rpc_host!r})
+-U, --wallet-rpc-user=user       Wallet RPC username (default: {cfg.monero_wallet_rpc_user!r})
+-P, --wallet-rpc-password=pass   Wallet RPC password (default: {cfg.monero_wallet_rpc_password!r})
 """,
 """,
 	'notes': """
 	'notes': """
 
 
@@ -212,16 +216,18 @@ $ mmgen-xmrwallet --pager txview *XMR*.sigtx
 """
 """
 	},
 	},
 	'code': {
 	'code': {
-		'options': lambda s: s.format(
+		'options': lambda cfg,s: s.format(
 			D=xmrwallet_uarg_info['daemon'].annot,
 			D=xmrwallet_uarg_info['daemon'].annot,
 			R=xmrwallet_uarg_info['tx_relay_daemon'].annot,
 			R=xmrwallet_uarg_info['tx_relay_daemon'].annot,
-			g=g,
+			cfg=cfg,
 			gc=gc,
 			gc=gc,
 		),
 		),
 	}
 	}
 }
 }
 
 
-cmd_args = opts.init(opts_data)
+cfg = opts.init(opts_data)
+
+cmd_args = cfg._args
 
 
 if len(cmd_args) < 2:
 if len(cmd_args) < 2:
 	opts.usage()
 	opts.usage()
@@ -263,17 +269,17 @@ uo = namedtuple('uopts',[
 
 
 uargs = ua( op, infile, wallets, spec )
 uargs = ua( op, infile, wallets, spec )
 uopts = uo(
 uopts = uo(
-	opt.daemon or '',
-	opt.tx_relay_daemon or '',
-	opt.restore_height or 0,
-	opt.rescan_blockchain,
-	opt.no_start_wallet_daemon,
-	opt.no_stop_wallet_daemon,
-	opt.no_relay,
-	opt.wallet_dir,
+	cfg.daemon or '',
+	cfg.tx_relay_daemon or '',
+	cfg.restore_height or 0,
+	cfg.rescan_blockchain,
+	cfg.no_start_wallet_daemon,
+	cfg.no_stop_wallet_daemon,
+	cfg.no_relay,
+	cfg.wallet_dir,
 )
 )
 
 
-m = getattr(MoneroWalletOps,op)(uargs,uopts)
+m = getattr(MoneroWalletOps,op)(cfg,uargs,uopts)
 
 
 try:
 try:
 	if async_run(m.main()):
 	if async_run(m.main()):

+ 9 - 8
mmgen/mn_entry.py

@@ -23,7 +23,7 @@ mn_entry.py - Mnemonic user entry methods for the MMGen suite
 import time
 import time
 
 
 from .globalvars import *
 from .globalvars import *
-from .util import msg,msg_r,qmsg,fmt,fmt_list,capfirst,die,ascii_lowercase
+from .util import msg,msg_r,fmt,fmt_list,capfirst,die,ascii_lowercase
 from .term import get_char,get_char_raw
 from .term import get_char,get_char_raw
 from .color import cyan
 from .color import cyan
 
 
@@ -232,7 +232,8 @@ class MnemonicEntry(object):
 	_sw = None
 	_sw = None
 	_usl = None
 	_usl = None
 
 
-	def __init__(self):
+	def __init__(self,cfg):
+		self.cfg = cfg
 		self.set_dfl_entry_mode()
 		self.set_dfl_entry_mode()
 
 
 	@property
 	@property
@@ -321,7 +322,7 @@ class MnemonicEntry(object):
 				return em_objs[int(uret)-1]
 				return em_objs[int(uret)-1]
 			else:
 			else:
 				msg_r(f'\b {uret!r}: invalid choice ')
 				msg_r(f'\b {uret!r}: invalid choice ')
-				time.sleep(g.err_disp_timeout)
+				time.sleep(self.cfg.err_disp_timeout)
 				msg_r(erase)
 				msg_r(erase)
 
 
 	def get_mnemonic_from_user(self,mn_len,validate=True):
 	def get_mnemonic_from_user(self,mn_len,validate=True):
@@ -350,7 +351,7 @@ class MnemonicEntry(object):
 					sw       = self.shortest_word,
 					sw       = self.shortest_word,
 			))
 			))
 
 
-		clear_line = '\n' if g.test_suite else '{r}{s}{r}'.format(r='\r',s=' '*40)
+		clear_line = '\n' if self.cfg.test_suite else '{r}{s}{r}'.format(r='\r',s=' '*40)
 		idx,idxs = 1,[] # initialize idx to a non-None value
 		idx,idxs = 1,[] # initialize idx to a non-None value
 
 
 		while len(idxs) < mn_len:
 		while len(idxs) < mn_len:
@@ -366,7 +367,7 @@ class MnemonicEntry(object):
 
 
 		if validate:
 		if validate:
 			self.bconv.tohex(words)
 			self.bconv.tohex(words)
-			qmsg(
+			self.cfg._util.qmsg(
 				'Mnemonic is valid' if self.has_chksum else
 				'Mnemonic is valid' if self.has_chksum else
 				'Mnemonic is well-formed (mnemonic format has no checksum to validate)' )
 				'Mnemonic is well-formed (mnemonic format has no checksum to validate)' )
 
 
@@ -389,7 +390,7 @@ class MnemonicEntry(object):
 		In addition to setting the default entry mode for the current wordlist, checks validity
 		In addition to setting the default entry mode for the current wordlist, checks validity
 		of all user-configured entry modes
 		of all user-configured entry modes
 		"""
 		"""
-		for k,v in g.mnemonic_entry_modes.items():
+		for k,v in self.cfg.mnemonic_entry_modes.items():
 			cls = self.get_cls_by_wordlist(k)
 			cls = self.get_cls_by_wordlist(k)
 			if v not in cls.entry_modes:
 			if v not in cls.entry_modes:
 				errmsg = """
 				errmsg = """
@@ -422,10 +423,10 @@ class MnemonicEntryMonero(MnemonicEntry):
 	dfl_entry_mode = 'short'
 	dfl_entry_mode = 'short'
 	has_chksum = True
 	has_chksum = True
 
 
-def mn_entry(wl_id,entry_mode=None):
+def mn_entry(cfg,wl_id,entry_mode=None):
 	if wl_id == 'words':
 	if wl_id == 'words':
 		wl_id = 'mmgen'
 		wl_id = 'mmgen'
-	me = MnemonicEntry.get_cls_by_wordlist(wl_id)()
+	me = MnemonicEntry.get_cls_by_wordlist(wl_id)(cfg)
 	import importlib
 	import importlib
 	me.bconv = getattr(importlib.import_module(f'mmgen.{me.modname}'),me.modname)(wl_id)
 	me.bconv = getattr(importlib.import_module(f'mmgen.{me.modname}'),me.modname)(wl_id)
 	me.wl = me.bconv.digits
 	me.wl = me.bconv.digits

+ 28 - 12
mmgen/msg.py

@@ -51,6 +51,9 @@ class coin_msg:
 
 
 	class base(MMGenObject):
 	class base(MMGenObject):
 
 
+		def __init__(self,cfg):
+			self.cfg = cfg
+
 		ext = 'rawmsg.json'
 		ext = 'rawmsg.json'
 		signed = False
 		signed = False
 		chksum_keys = ('addrlists','message','msghash_type','network')
 		chksum_keys = ('addrlists','message','msghash_type','network')
@@ -85,10 +88,10 @@ class coin_msg:
 			return f'{self.filename_stem}.{coin_msg.signed.ext}'
 			return f'{self.filename_stem}.{coin_msg.signed.ext}'
 
 
 		def get_proto_from_file(self,filename):
 		def get_proto_from_file(self,filename):
-			data = json.loads(get_data_from_file(filename))
+			data = json.loads(get_data_from_file( self.cfg, filename ))
 			network_id = data['metadata']['network'] if 'metadata' in data else data['network'].lower()
 			network_id = data['metadata']['network'] if 'metadata' in data else data['network'].lower()
 			coin,network = network_id.split('_')
 			coin,network = network_id.split('_')
-			return init_proto( coin=coin, network=network )
+			return init_proto( cfg=self.cfg, coin=coin, network=network )
 
 
 		def write_to_file(self,outdir=None,ask_overwrite=False):
 		def write_to_file(self,outdir=None,ask_overwrite=False):
 			data = {
 			data = {
@@ -98,6 +101,7 @@ class coin_msg:
 			}
 			}
 
 
 			write_data_to_file(
 			write_data_to_file(
+				cfg           = self.cfg,
 				outfile       = os.path.join(outdir or '',self.filename),
 				outfile       = os.path.join(outdir or '',self.filename),
 				data          = json.dumps(data,sort_keys=True,indent=4),
 				data          = json.dumps(data,sort_keys=True,indent=4),
 				desc          = self.desc,
 				desc          = self.desc,
@@ -105,10 +109,15 @@ class coin_msg:
 
 
 	class new(base):
 	class new(base):
 
 
-		def __init__(self,message,addrlists,msghash_type,*args,**kwargs):
+		def __init__(self,cfg,message,addrlists,msghash_type,*args,**kwargs):
+
+			self.cfg = cfg
+
 			msghash_type = msghash_type or self.msg_cls.msghash_types[0]
 			msghash_type = msghash_type or self.msg_cls.msghash_types[0]
+
 			if msghash_type not in self.msg_cls.msghash_types:
 			if msghash_type not in self.msg_cls.msghash_types:
 				die(2,f'msghash_type {msghash_type!r} not supported for {self.proto.base_proto} protocol')
 				die(2,f'msghash_type {msghash_type!r} not supported for {self.proto.base_proto} protocol')
+
 			self.data = {
 			self.data = {
 				'network': '{}_{}'.format( self.proto.coin.lower(), self.proto.network ),
 				'network': '{}_{}'.format( self.proto.coin.lower(), self.proto.network ),
 				'addrlists': [MMGenIDRange(self.proto,i) for i in addrlists.split()],
 				'addrlists': [MMGenIDRange(self.proto,i) for i in addrlists.split()],
@@ -119,13 +128,16 @@ class coin_msg:
 
 
 	class completed(base):
 	class completed(base):
 
 
-		def __init__(self,data,infile,*args,**kwargs):
+		def __init__(self,cfg,data,infile,*args,**kwargs):
+
+			self.cfg = cfg
 
 
 			if data:
 			if data:
 				self.__dict__ = data
 				self.__dict__ = data
 				return
 				return
 
 
 			self.data = get_data_from_file(
 			self.data = get_data_from_file(
+				cfg    = self.cfg,
 				infile = infile,
 				infile = infile,
 				desc   = self.desc )
 				desc   = self.desc )
 
 
@@ -210,6 +222,7 @@ class coin_msg:
 
 
 			async def sign_list(al_in,seed):
 			async def sign_list(al_in,seed):
 				al = KeyAddrList(
 				al = KeyAddrList(
+					cfg         = self.cfg,
 					proto       = self.proto,
 					proto       = self.proto,
 					seed        = seed,
 					seed        = seed,
 					addr_idxs   = al_in.idxlist,
 					addr_idxs   = al_in.idxlist,
@@ -238,11 +251,11 @@ class coin_msg:
 
 
 			if self.proto.sign_mode == 'daemon':
 			if self.proto.sign_mode == 'daemon':
 				from .rpc import rpc_init
 				from .rpc import rpc_init
-				self.rpc = await rpc_init(self.proto)
+				self.rpc = await rpc_init(self.cfg,self.proto)
 
 
 			from .wallet import Wallet
 			from .wallet import Wallet
 			from .addrlist import KeyAddrList
 			from .addrlist import KeyAddrList
-			wallet_seeds = [Wallet(fn=fn).seed for fn in wallet_files]
+			wallet_seeds = [Wallet(cfg=self.cfg,fn=fn).seed for fn in wallet_files]
 			need_sids = remove_dups([al.sid for al in self.addrlists], quiet=True)
 			need_sids = remove_dups([al.sid for al in self.addrlists], quiet=True)
 			saved_seeds = list()
 			saved_seeds = list()
 
 
@@ -302,7 +315,7 @@ class coin_msg:
 
 
 			if self.proto.sign_mode == 'daemon':
 			if self.proto.sign_mode == 'daemon':
 				from .rpc import rpc_init
 				from .rpc import rpc_init
-				self.rpc = await rpc_init(self.proto)
+				self.rpc = await rpc_init(self.cfg,self.proto)
 
 
 			for k,v in sigs.items():
 			for k,v in sigs.items():
 				ret = await self.do_verify(
 				ret = await self.do_verify(
@@ -333,10 +346,13 @@ class coin_msg:
 
 
 	class exported_sigs(signed_online):
 	class exported_sigs(signed_online):
 
 
-		def __init__(self,infile,*args,**kwargs):
+		def __init__(self,cfg,infile,*args,**kwargs):
+
+			self.cfg = cfg
 
 
 			self.data = json.loads(
 			self.data = json.loads(
 				get_data_from_file(
 				get_data_from_file(
+					cfg    = self.cfg,
 					infile = infile,
 					infile = infile,
 					desc   = self.desc )
 					desc   = self.desc )
 				)
 				)
@@ -348,7 +364,7 @@ class coin_msg:
 				self.data['signatures']
 				self.data['signatures']
 			)}
 			)}
 
 
-def _get_obj(clsname,coin=None,network='mainnet',infile=None,data=None,*args,**kwargs):
+def _get_obj(clsname,cfg,coin=None,network='mainnet',infile=None,data=None,*args,**kwargs):
 
 
 	assert not args, 'msg:_get_obj(): only keyword args allowed'
 	assert not args, 'msg:_get_obj(): only keyword args allowed'
 
 
@@ -359,8 +375,8 @@ def _get_obj(clsname,coin=None,network='mainnet',infile=None,data=None,*args,**k
 
 
 	proto = (
 	proto = (
 		data['proto'] if data else
 		data['proto'] if data else
-		init_proto( coin=coin, network=network ) if coin else
-		coin_msg.base().get_proto_from_file(infile) )
+		init_proto( cfg=cfg, coin=coin, network=network ) if coin else
+		coin_msg.base(cfg=cfg).get_proto_from_file(infile) )
 
 
 	try:
 	try:
 		msg_cls = getattr(
 		msg_cls = getattr(
@@ -373,7 +389,7 @@ def _get_obj(clsname,coin=None,network='mainnet',infile=None,data=None,*args,**k
 	me.msg_cls = msg_cls
 	me.msg_cls = msg_cls
 	me.proto = proto
 	me.proto = proto
 
 
-	me.__init__(infile=infile,data=data,*args,**kwargs)
+	me.__init__(cfg,infile=infile,data=data,*args,**kwargs)
 
 
 	return me
 	return me
 
 

+ 0 - 1
mmgen/objmethods.py

@@ -21,7 +21,6 @@ objmethods: Mixin classes for MMGen data objects
 """
 """
 
 
 import unicodedata
 import unicodedata
-from .globalvars import g
 import mmgen.color as color_mod
 import mmgen.color as color_mod
 
 
 if 'MMGenObjectDevTools' in __builtins__: # added to builtins by devinit.init_dev()
 if 'MMGenObjectDevTools' in __builtins__: # added to builtins by devinit.init_dev()

+ 125 - 121
mmgen/opts.py

@@ -21,17 +21,12 @@ opts: MMGen-specific options processing after generic processing by share.Opts
 """
 """
 import sys,os
 import sys,os
 
 
-from .globalvars import g,gc
+from .globalvars import gc,Config
 from .base_obj import Lockable
 from .base_obj import Lockable
 
 
 import mmgen.share.Opts
 import mmgen.share.Opts
 
 
-class UserOpts(Lockable):
-	_autolock = False
-	_default_to_none = True
-	_set_ok = ('usr_randchars',)
-	_reset_ok = ('quiet','verbose','yes')
-
+class UserOpts: pass
 opt = UserOpts()
 opt = UserOpts()
 
 
 def usage():
 def usage():
@@ -54,22 +49,23 @@ def delete_data(opts_data):
 	del mmgen.share.Opts.process_uopts
 	del mmgen.share.Opts.process_uopts
 	del mmgen.share.Opts.parse_opts
 	del mmgen.share.Opts.parse_opts
 
 
-def post_init():
+def post_init(cfg):
 	global opts_data_save,opt_filter_save
 	global opts_data_save,opt_filter_save
-	if opt.help or opt.longhelp:
-		print_help(opt,opts_data_save,opt_filter_save)
+	if cfg.help or cfg.longhelp:
+		print_help(cfg,opts_data_save,opt_filter_save)
 	else:
 	else:
 		delete_data(opts_data_save)
 		delete_data(opts_data_save)
 		del opts_data_save,opt_filter_save
 		del opts_data_save,opt_filter_save
 
 
-def print_help(opt,opts_data,opt_filter):
+def print_help(cfg,opts_data,opt_filter):
+
 	if not 'code' in opts_data:
 	if not 'code' in opts_data:
 		opts_data['code'] = {}
 		opts_data['code'] = {}
 
 
-	from .protocol import init_proto_from_opts
-	proto = init_proto_from_opts(need_amt=True)
+	from .protocol import init_proto_from_cfg
+	proto = init_proto_from_cfg(cfg,need_amt=True)
 
 
-	if getattr(opt,'longhelp',None):
+	if getattr(cfg,'longhelp',None):
 		opts_data['code']['long_options'] = common_opts_data['code']
 		opts_data['code']['long_options'] = common_opts_data['code']
 		def remove_unneeded_long_opts():
 		def remove_unneeded_long_opts():
 			d = opts_data['text']['long_options']
 			d = opts_data['text']['long_options']
@@ -79,16 +75,16 @@ def print_help(opt,opts_data,opt_filter):
 		remove_unneeded_long_opts()
 		remove_unneeded_long_opts()
 
 
 	from .ui import do_pager
 	from .ui import do_pager
-	do_pager(mmgen.share.Opts.make_help( proto, opt, opts_data, opt_filter ))
+	do_pager(mmgen.share.Opts.make_help( cfg, proto, opts_data, opt_filter ))
 
 
 	sys.exit(0)
 	sys.exit(0)
 
 
 def fmt_opt(o):
 def fmt_opt(o):
 	return '--' + o.replace('_','-')
 	return '--' + o.replace('_','-')
 
 
-def die_on_incompatible_opts(incompat_list):
-	for group in incompat_list:
-		bad = [k for k in opt.__dict__ if k in group and getattr(opt,k) != None]
+def die_on_incompatible_opts(cfg):
+	for group in cfg.incompatible_opts:
+		bad = [k for k in cfg.__dict__ if k in group and getattr(cfg,k) != None]
 		if len(bad) > 1:
 		if len(bad) > 1:
 			from .util import die
 			from .util import die
 			die(1,'Conflicting options: {}'.format(', '.join(map(fmt_opt,bad))))
 			die(1,'Conflicting options: {}'.format(', '.join(map(fmt_opt,bad))))
@@ -117,18 +113,14 @@ def opt_preproc_debug(po):
 	for e in d:
 	for e in d:
 		Msg('    {:<20}: {}'.format(*e))
 		Msg('    {:<20}: {}'.format(*e))
 
 
-def opt_postproc_debug():
-	a = [k for k in dir(opt) if k[:2] != '__' and getattr(opt,k) != None]
-	b = [k for k in dir(opt) if k[:2] != '__' and getattr(opt,k) == None]
+def opt_postproc_debug(cfg):
+	a = [k for k in dir(cfg) if k[:2] != '__' and getattr(cfg,k) != None]
+	b = [k for k in dir(cfg) if k[:2] != '__' and getattr(cfg,k) == None]
 	from .util import Msg
 	from .util import Msg
-	Msg('    Opts after processing:')
-	for k in a:
-		v = getattr(opt,k)
-		Msg('        {:18}: {!r:<6} [{}]'.format(k,v,type(v).__name__))
 	Msg('    Global vars:')
 	Msg('    Global vars:')
-	for e in [d for d in dir(g) if d[:2] != '__']:
-		Msg('        {:<20}: {}'.format(e, getattr(g,e)))
-	Msg("    Opts set to 'None':")
+	for e in [d for d in dir(cfg) if d[:2] != '__']:
+		Msg('        {:<20}: {}'.format(e, getattr(cfg,e)))
+	Msg("    Global vars set to 'None':")
 	Msg('        {}\n'.format('\n        '.join(b)))
 	Msg('        {}\n'.format('\n        '.join(b)))
 	Msg('\n=== end opts.py debug ===\n')
 	Msg('\n=== end opts.py debug ===\n')
 
 
@@ -157,15 +149,18 @@ def set_for_type(val,refval,desc,invert_bool=False,src=None):
 		type(refval).__name__) )
 		type(refval).__name__) )
 
 
 def override_globals_from_cfg_file(
 def override_globals_from_cfg_file(
+		cfg,
 		ucfg,
 		ucfg,
-		autoset_opts,
+		cfgfile_autoset_opts,
+		cfgfile_auto_typeset_opts,
 		env_globals,
 		env_globals,
 		need_proto ):
 		need_proto ):
 
 
 	if need_proto:
 	if need_proto:
 		from .protocol import init_proto
 		from .protocol import init_proto
+
 	for d in ucfg.get_lines():
 	for d in ucfg.get_lines():
-		if d.name in g.cfg_file_opts:
+		if d.name in cfg.cfg_file_opts:
 			ns = d.name.split('_')
 			ns = d.name.split('_')
 			if ns[0] in gc.core_coins:
 			if ns[0] in gc.core_coins:
 				if not need_proto:
 				if not need_proto:
@@ -174,10 +169,10 @@ def override_globals_from_cfg_file(
 					(ns[2:],ns[1]=='testnet') if len(ns) > 2 and ns[1] in ('mainnet','testnet') else
 					(ns[2:],ns[1]=='testnet') if len(ns) > 2 and ns[1] in ('mainnet','testnet') else
 					(ns[1:],False)
 					(ns[1:],False)
 				)
 				)
-				cls = type(init_proto( ns[0], tn, need_amt=True )) # no instance yet, so override _class_ attr
+				cls = type(init_proto( cfg, ns[0], tn, need_amt=True )) # no instance yet, so override _class_ attr
 				attr = '_'.join(nse)
 				attr = '_'.join(nse)
 			else:
 			else:
-				cls = g                          # g is "singleton" instance, so override _instance_ attr
+				cls = cfg
 				attr = d.name
 				attr = d.name
 			refval = getattr(cls,attr)
 			refval = getattr(cls,attr)
 			val = ucfg.parse_value(d.value,refval)
 			val = ucfg.parse_value(d.value,refval)
@@ -187,29 +182,31 @@ def override_globals_from_cfg_file(
 			val_conv = set_for_type(val,refval,attr,src=ucfg.fn)
 			val_conv = set_for_type(val,refval,attr,src=ucfg.fn)
 			if attr not in env_globals:
 			if attr not in env_globals:
 				setattr(cls,attr,val_conv)
 				setattr(cls,attr,val_conv)
-		elif d.name in g.autoset_opts:
-			autoset_opts[d.name] = d.value
+		elif d.name in cfg.autoset_opts:
+			cfgfile_autoset_opts[d.name] = d.value
+		elif d.name in cfg.auto_typeset_opts:
+			cfgfile_auto_typeset_opts[d.name] = d.value
 		else:
 		else:
 			from .util import die
 			from .util import die
 			die( 'CfgFileParseError', f'{d.name!r}: unrecognized option in {ucfg.fn!r}, line {d.lineno}' )
 			die( 'CfgFileParseError', f'{d.name!r}: unrecognized option in {ucfg.fn!r}, line {d.lineno}' )
 
 
-def override_globals_from_env():
+def override_globals_from_env(cfg):
 	for name,val in ((k,v) for k,v in os.environ.items() if k.startswith('MMGEN_')):
 	for name,val in ((k,v) for k,v in os.environ.items() if k.startswith('MMGEN_')):
 		if name == 'MMGEN_DEBUG_ALL':
 		if name == 'MMGEN_DEBUG_ALL':
 			continue
 			continue
-		elif name in g.env_opts:
+		elif name in cfg.env_opts:
 			if val: # ignore empty string values; string value of '0' or 'false' sets variable to False
 			if val: # ignore empty string values; string value of '0' or 'false' sets variable to False
 				disable = name.startswith('MMGEN_DISABLE_')
 				disable = name.startswith('MMGEN_DISABLE_')
 				gname = name[(6,14)[disable]:].lower()
 				gname = name[(6,14)[disable]:].lower()
-				if hasattr(g,gname):
-					setattr(g,gname,set_for_type(val,getattr(g,gname),name,disable))
+				if hasattr(cfg,gname):
+					setattr(cfg,gname,set_for_type(val,getattr(cfg,gname),name,disable))
 					yield gname
 					yield gname
 				else:
 				else:
 					raise ValueError(f'Name {gname!r} not present in globals')
 					raise ValueError(f'Name {gname!r} not present in globals')
 		else:
 		else:
 			raise ValueError(f'{name!r} is not a valid MMGen environment variable')
 			raise ValueError(f'{name!r} is not a valid MMGen environment variable')
 
 
-def show_common_opts_diff():
+def show_common_opts_diff(cfg):
 
 
 	def common_opts_data_to_list():
 	def common_opts_data_to_list():
 		for l in common_opts_data['text'].splitlines():
 		for l in common_opts_data['text'].splitlines():
@@ -220,16 +217,16 @@ def show_common_opts_diff():
 		from .util import fmt_list
 		from .util import fmt_list
 		return fmt_list(['--'+s.replace('_','-') for s in set_data],fmt='col',indent='   ')
 		return fmt_list(['--'+s.replace('_','-') for s in set_data],fmt='col',indent='   ')
 
 
-	a = g.common_opts
+	a = cfg.common_opts
 	b = list(common_opts_data_to_list())
 	b = list(common_opts_data_to_list())
 	a_minus_b = [e for e in a if e not in b]
 	a_minus_b = [e for e in a if e not in b]
 	b_minus_a = [e for e in b if e not in a]
 	b_minus_a = [e for e in b if e not in a]
 	a_and_b   = [e for e in a if e in b]
 	a_and_b   = [e for e in a if e in b]
 
 
 	from .util import msg
 	from .util import msg
-	msg(f'g.common_opts - common_opts_data:\n   {do_fmt(a_minus_b) if a_minus_b else "None"}\n')
-	msg(f'common_opts_data - g.common_opts (these do not set global var):\n{do_fmt(b_minus_a)}\n')
-	msg(f'common_opts_data ^ g.common_opts (these set global var):\n{do_fmt(a_and_b)}\n')
+	msg(f'cfg.common_opts - common_opts_data:\n   {do_fmt(a_minus_b) if a_minus_b else "None"}\n')
+	msg(f'common_opts_data - cfg.common_opts (these do not set global var):\n{do_fmt(b_minus_a)}\n')
+	msg(f'common_opts_data ^ cfg.common_opts (these set global var):\n{do_fmt(a_and_b)}\n')
 
 
 	sys.exit(0)
 	sys.exit(0)
 
 
@@ -291,8 +288,8 @@ def init(
 	parse_only  = False,
 	parse_only  = False,
 	parsed_opts = None,
 	parsed_opts = None,
 	need_proto  = True,
 	need_proto  = True,
-	do_post_init = False,
-	return_parsed = False ):
+	need_amt    = True,
+	do_post_init = False ):
 
 
 	if opts_data is None:
 	if opts_data is None:
 		opts_data = opts_data_dfl
 		opts_data = opts_data_dfl
@@ -314,7 +311,9 @@ def init(
 	if parse_only and not any(k in po.user_opts for k in ('version','help','longhelp')):
 	if parse_only and not any(k in po.user_opts for k in ('version','help','longhelp')):
 		return po
 		return po
 
 
-	if g.debug_opts:
+	cfg = Config()
+
+	if cfg.debug_opts: # TODO: this does nothing
 		opt_preproc_debug(po)
 		opt_preproc_debug(po)
 
 
 	# Copy parsed opts to opt, setting values to None if not set by user
 	# Copy parsed opts to opt, setting values to None if not set by user
@@ -323,8 +322,8 @@ def init(
 			+ po.skipped_opts
 			+ po.skipped_opts
 			+ tuple(add_opts or [])
 			+ tuple(add_opts or [])
 			+ tuple(init_opts or [])
 			+ tuple(init_opts or [])
-			+ g.init_opts
-			+ g.common_opts ):
+			+ cfg.init_opts
+			+ cfg.common_opts ):
 		setattr(opt,o,po.user_opts[o] if o in po.user_opts else None)
 		setattr(opt,o,po.user_opts[o] if o in po.user_opts else None)
 
 
 	if opt.version:
 	if opt.version:
@@ -333,108 +332,110 @@ def init(
 	if opt.show_hash_presets:
 	if opt.show_hash_presets:
 		_show_hash_presets() # exits
 		_show_hash_presets() # exits
 
 
-	from .term import init_term
-	init_term()
-
 	from .util import wrap_ripemd160
 	from .util import wrap_ripemd160
 	wrap_ripemd160() # ripemd160 used by mmgen_cfg_file()
 	wrap_ripemd160() # ripemd160 used by mmgen_cfg_file()
 
 
-	# === begin global var initialization === #
+	# === begin Config() initialization === #
+
+	env_globals = tuple(override_globals_from_env(cfg))
 
 
-	env_globals = tuple(override_globals_from_env())
+	# do this after setting ‘hold_protect_disable’ from env
+	from .term import init_term
+	init_term(cfg)
 
 
 	# --data-dir overrides computed value of data_dir_root
 	# --data-dir overrides computed value of data_dir_root
-	g.data_dir_root_override = opt.data_dir
+	cfg.data_dir_root_override = opt.data_dir
+	if opt.data_dir:
+		del opt.data_dir
 
 
 	from .fileutil import check_or_create_dir
 	from .fileutil import check_or_create_dir
-	check_or_create_dir(g.data_dir_root)
+	check_or_create_dir(cfg.data_dir_root)
 
 
 	cfgfile_autoset_opts = {}
 	cfgfile_autoset_opts = {}
+	cfgfile_auto_typeset_opts = {}
 
 
 	if not opt.skip_cfg_file:
 	if not opt.skip_cfg_file:
 		from .cfgfile import mmgen_cfg_file
 		from .cfgfile import mmgen_cfg_file
 		# check for changes in system template file - term must be initialized
 		# check for changes in system template file - term must be initialized
-		mmgen_cfg_file('sample')
+		mmgen_cfg_file(cfg,'sample')
 		override_globals_from_cfg_file(
 		override_globals_from_cfg_file(
-			mmgen_cfg_file('usr'),
+			cfg,
+			mmgen_cfg_file(cfg,'usr'),
 			cfgfile_autoset_opts,
 			cfgfile_autoset_opts,
+			cfgfile_auto_typeset_opts,
 			env_globals,
 			env_globals,
 			need_proto )
 			need_proto )
 
 
-	# Set globals from opts, setting type from original global value
-	# Do here, before opts are set from globals below
-	for k in (g.common_opts + g.opt_sets_global):
-		if hasattr(opt,k):
-			val = getattr(opt,k)
-			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 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
+	# Set globals from opts, setting type from original global value if it exists:
+	auto_keys = tuple(cfg.autoset_opts.keys()) + tuple(cfg.auto_typeset_opts.keys())
+	for key,val in opt.__dict__.items():
+		if val is not None and key not in auto_keys:
+			setattr(cfg, key, set_for_type(val,getattr(cfg,key),'--'+key) if hasattr(cfg,key) else val)
+
+	if cfg.regtest or cfg.bob or cfg.alice or cfg.carol or gc.prog_name == 'mmgen-regtest':
+		cfg.network = 'regtest'
+		cfg.regtest_user = 'bob' if cfg.bob else 'alice' if cfg.alice else 'carol' if cfg.carol else None
 	else:
 	else:
-		g.network = 'testnet' if g.testnet else 'mainnet'
+		cfg.network = 'testnet' if cfg.testnet else 'mainnet'
 
 
-	g.coin = g.coin.upper() or 'BTC'
-	g.token = g.token.upper() or None
+	cfg.coin = cfg.coin.upper()
+	cfg.token = cfg.token.upper() or None
 
 
 	# === end global var initialization === #
 	# === end global var initialization === #
 
 
 	"""
 	"""
-	g.color is finalized, so initialize color
+	cfg.color is finalized, so initialize color
 	"""
 	"""
-	if g.color: # MMGEN_DISABLE_COLOR sets this to False
+	if cfg.color: # MMGEN_DISABLE_COLOR sets this to False
 		from .color import init_color
 		from .color import init_color
-		init_color(num_colors=('auto',256)[bool(g.force_256_color)])
-
-	# Set user opts from globals:
-	# - if opt is unset, set it to global value
-	# - if opt is set, convert its type to that of global value
-	for k in g.global_sets_opt:
-		if hasattr(opt,k) and getattr(opt,k) != None:
-			setattr(opt,k,set_for_type(getattr(opt,k),getattr(g,k),'--'+k))
-		else:
-			setattr(opt,k,getattr(g,k))
+		init_color(num_colors=('auto',256)[bool(cfg.force_256_color)])
 
 
-	if need_proto:
-		from .protocol import warn_trustlevel
-		warn_trustlevel(g.coin)
-
-	die_on_incompatible_opts(g.incompatible_opts)
+	die_on_incompatible_opts(cfg)
 
 
-	check_or_create_dir(g.data_dir)
-
-	# Check user-set opts without modifying them
-	check_usr_opts(po.user_opts)
+	check_or_create_dir(cfg.data_dir)
 
 
 	# Check autoset opts, setting if unset
 	# Check autoset opts, setting if unset
-	for key in g.autoset_opts:
+	for key in cfg.autoset_opts:
 		if hasattr(opt,key):
 		if hasattr(opt,key):
+			assert not hasattr(cfg,key), f'{key!r} is in cfg!'
 			if getattr(opt,key) is not None:
 			if getattr(opt,key) is not None:
-				setattr(opt, key, get_autoset_opt(key,getattr(opt,key),src='cmdline'))
+				setattr(cfg, key, get_autoset_opt(cfg,key,getattr(opt,key),src='cmdline'))
 			elif key in cfgfile_autoset_opts:
 			elif key in cfgfile_autoset_opts:
-				setattr(opt, key, get_autoset_opt(key,cfgfile_autoset_opts[key],src='cfgfile'))
+				setattr(cfg, key, get_autoset_opt(cfg,key,cfgfile_autoset_opts[key],src='cfgfile'))
 			else:
 			else:
-				setattr(opt, key, g.autoset_opts[key].choices[0])
+				setattr(cfg, key, cfg.autoset_opts[key].choices[0])
 
 
-	set_auto_typeset_opts()
+	set_auto_typeset_opts(cfg,cfgfile_auto_typeset_opts)
 
 
-	if opt.verbose:
-		opt.quiet = None
+	if cfg.debug and gc.prog_name != 'test.py':
+		cfg.verbose = True
 
 
-	if g.debug and gc.prog_name != 'test.py':
-		opt.verbose,opt.quiet = (True,None)
+	if cfg.verbose:
+		cfg.quiet = False
 
 
-	if g.debug_opts:
-		opt_postproc_debug()
+	if cfg.debug_opts:
+		opt_postproc_debug(cfg)
 
 
-	g.lock()
-	opt.lock()
+	# Check user-set opts without modifying them
+	check_usr_opts(cfg,po.user_opts)
+
+	from .util import Util
+	cfg._util = Util(cfg)
+	cfg._uopts = po.user_opts
+	cfg._args = po.cmd_args
+
+	cfg.lock()
+
+	if need_proto:
+		from .protocol import warn_trustlevel,init_proto_from_cfg
+		warn_trustlevel(cfg)
+		# requires the default-to-none behavior, so do after the lock:
+		cfg._proto = init_proto_from_cfg(cfg,need_amt=need_amt)
 
 
-	# print help screen only after globals and opts initialized and locked:
-	if opt.help or opt.longhelp:
+	# print help screen only after globals initialized and locked:
+	if cfg.help or cfg.longhelp:
 		if not do_post_init:
 		if not do_post_init:
-			print_help(opt,opts_data,opt_filter) # exits
+			print_help(cfg,opts_data,opt_filter) # exits
 
 
 	if do_post_init:
 	if do_post_init:
 		global opts_data_save,opt_filter_save
 		global opts_data_save,opt_filter_save
@@ -443,9 +444,9 @@ def init(
 	else:
 	else:
 		delete_data(opts_data)
 		delete_data(opts_data)
 
 
-	return po if return_parsed else po.cmd_args
+	return cfg
 
 
-def check_usr_opts(usr_opts): # Raises an exception if any check fails
+def check_usr_opts(cfg,usr_opts): # Raises an exception if any check fails
 
 
 	def opt_splits(val,sep,n,desc):
 	def opt_splits(val,sep,n,desc):
 		sepword = 'comma' if sep == ',' else 'colon' if sep == ':' else repr(sep)
 		sepword = 'comma' if sep == ',' else 'colon' if sep == ':' else repr(sep)
@@ -577,8 +578,8 @@ def check_usr_opts(usr_opts): # Raises an exception if any check fails
 		if val == 0:
 		if val == 0:
 			return
 			return
 		opt_is_int(val,desc)
 		opt_is_int(val,desc)
-		opt_compares(val,'>=',g.min_urandchars,desc)
-		opt_compares(val,'<=',g.max_urandchars,desc)
+		opt_compares(val,'>=',cfg.min_urandchars,desc)
+		opt_compares(val,'<=',cfg.max_urandchars,desc)
 
 
 	def chk_tx_fee(key,val,desc):
 	def chk_tx_fee(key,val,desc):
 		pass
 		pass
@@ -605,7 +606,7 @@ def check_usr_opts(usr_opts): # Raises an exception if any check fails
 #	def chk_bob(key,val,desc):
 #	def chk_bob(key,val,desc):
 #		from .proto.btc.regtest import MMGenRegtest
 #		from .proto.btc.regtest import MMGenRegtest
 #		try:
 #		try:
-#			os.stat(os.path.join(MMGenRegtest(g.coin).d.datadir,'regtest','debug.log'))
+#			os.stat(os.path.join(MMGenRegtest(cfg,cfg.coin).d.datadir,'regtest','debug.log'))
 #		except:
 #		except:
 #			die( 'UserOptError',
 #			die( 'UserOptError',
 #				'Regtest (Bob and Alice) mode not set up yet.  ' +
 #				'Regtest (Bob and Alice) mode not set up yet.  ' +
@@ -634,10 +635,10 @@ def check_usr_opts(usr_opts): # Raises an exception if any check fails
 	cfuncs = { k:v for k,v in locals().items() if k.startswith('chk_') }
 	cfuncs = { k:v for k,v in locals().items() if k.startswith('chk_') }
 
 
 	for key in usr_opts:
 	for key in usr_opts:
-		val = getattr(opt,key)
+		val = getattr(cfg,key)
 		desc = f'parameter for {fmt_opt(key)!r} option'
 		desc = f'parameter for {fmt_opt(key)!r} option'
 
 
-		if key in g.infile_opts:
+		if key in cfg.infile_opts:
 			from .fileutil import check_infile
 			from .fileutil import check_infile
 			check_infile(val) # file exists and is readable - dies on error
 			check_infile(val) # file exists and is readable - dies on error
 		elif key == 'outdir':
 		elif key == 'outdir':
@@ -645,19 +646,22 @@ def check_usr_opts(usr_opts): # Raises an exception if any check fails
 			check_outdir(val) # dies on error
 			check_outdir(val) # dies on error
 		elif 'chk_'+key in cfuncs:
 		elif 'chk_'+key in cfuncs:
 			cfuncs['chk_'+key](key,val,desc)
 			cfuncs['chk_'+key](key,val,desc)
-		elif g.debug:
+		elif cfg.debug:
 			Msg(f'check_usr_opts(): No test for opt {key!r}')
 			Msg(f'check_usr_opts(): No test for opt {key!r}')
 
 
-def set_auto_typeset_opts():
+def set_auto_typeset_opts(cfg,cfgfile_auto_typeset_opts):
 
 
 	def do_set(key,val,ref_type):
 	def do_set(key,val,ref_type):
-		setattr(opt,key,None if val is None else ref_type(val))
+		assert not hasattr(cfg,key), f'{key!r} is in cfg!'
+		setattr(cfg,key,None if val is None else ref_type(val))
 
 
-	for key,ref_type in g.auto_typeset_opts.items():
+	for key,ref_type in cfg.auto_typeset_opts.items():
 		if hasattr(opt,key):
 		if hasattr(opt,key):
 			do_set(key, getattr(opt,key), ref_type)
 			do_set(key, getattr(opt,key), ref_type)
+		elif key in cfgfile_auto_typeset_opts:
+			do_set(key, cfgfile_auto_typeset_opts[key], ref_type)
 
 
-def get_autoset_opt(key,val,src):
+def get_autoset_opt(cfg,key,val,src):
 
 
 	def die_on_err(desc):
 	def die_on_err(desc):
 		from .util import fmt_list,die
 		from .util import fmt_list,die
@@ -687,6 +691,6 @@ def get_autoset_opt(key,val,src):
 			else:
 			else:
 				die_on_err('unique substring of')
 				die_on_err('unique substring of')
 
 
-	data = g.autoset_opts[key]
+	data = cfg.autoset_opts[key]
 
 
 	return getattr(opt_type,data.type)()
 	return getattr(opt_type,data.type)()

+ 6 - 4
mmgen/passwdlist.py

@@ -22,7 +22,6 @@ passwdlist: Password list class for the MMGen suite
 
 
 from collections import namedtuple
 from collections import namedtuple
 
 
-from .globalvars import g
 from .util import ymsg,is_int,die
 from .util import ymsg,is_int,die
 from .obj import ImmutableAttr,ListItemAttr,MMGenPWIDString,TwComment
 from .obj import ImmutableAttr,ListItemAttr,MMGenPWIDString,TwComment
 from .key import PrivKey
 from .key import PrivKey
@@ -66,6 +65,7 @@ class PasswordList(AddrList):
 
 
 	def __init__(
 	def __init__(
 			self,
 			self,
+			cfg,
 			proto,
 			proto,
 			infile          = None,
 			infile          = None,
 			seed            = None,
 			seed            = None,
@@ -76,9 +76,10 @@ class PasswordList(AddrList):
 			chk_params_only = False,
 			chk_params_only = False,
 			skip_chksum_msg = False ):
 			skip_chksum_msg = False ):
 
 
+		self.cfg = cfg
 		self.proto = proto # proto is ignored
 		self.proto = proto # proto is ignored
 
 
-		if not g.debug_addrlist:
+		if not cfg.debug_addrlist:
 			self.dmsg_sc = self.noop
 			self.dmsg_sc = self.noop
 
 
 		if infile:
 		if infile:
@@ -167,7 +168,7 @@ class PasswordList(AddrList):
 			from .xmrseed import xmrseed
 			from .xmrseed import xmrseed
 			from .protocol import init_proto
 			from .protocol import init_proto
 			self.xmrseed = xmrseed()
 			self.xmrseed = xmrseed()
-			self.xmrproto = init_proto('xmr')
+			self.xmrproto = init_proto( self.cfg, 'xmr' )
 			pw_bytes = xmrseed().seedlen_map_rev[self.pw_len]
 			pw_bytes = xmrseed().seedlen_map_rev[self.pw_len]
 			try:
 			try:
 				good_pw_len = xmrseed().seedlen_map[seed.byte_len]
 				good_pw_len = xmrseed().seedlen_map[seed.byte_len]
@@ -193,6 +194,7 @@ class PasswordList(AddrList):
 		if pf in ('bip39','hex') and pw_bytes < seed.byte_len:
 		if pf in ('bip39','hex') and pw_bytes < seed.byte_len:
 			from .ui import keypress_confirm
 			from .ui import keypress_confirm
 			if not keypress_confirm(
 			if not keypress_confirm(
+					self.cfg,
 					f'WARNING: requested {self.pw_info[pf].desc} length has less entropy ' +
 					f'WARNING: requested {self.pw_info[pf].desc} length has less entropy ' +
 					'than underlying seed!\nIs this what you want?',
 					'than underlying seed!\nIs this what you want?',
 					default_yes = True ):
 					default_yes = True ):
@@ -240,4 +242,4 @@ class PasswordList(AddrList):
 
 
 		self.dmsg_sc('str',scramble_key)
 		self.dmsg_sc('str',scramble_key)
 		from .crypto import Crypto
 		from .crypto import Crypto
-		return Crypto().scramble_seed(seed,scramble_key.encode())
+		return Crypto(self.cfg).scramble_seed(seed,scramble_key.encode())

+ 1 - 2
mmgen/proto/btc/addrdata.py

@@ -13,7 +13,6 @@ proto.btc.addrdata: Bitcoin base protocol addrdata classes
 """
 """
 
 
 from ...addrdata import TwAddrData
 from ...addrdata import TwAddrData
-from ...util import vmsg
 
 
 class BitcoinTwAddrData(TwAddrData):
 class BitcoinTwAddrData(TwAddrData):
 
 
@@ -27,7 +26,7 @@ class BitcoinTwAddrData(TwAddrData):
 	}
 	}
 
 
 	async def get_tw_data(self,twctl=None):
 	async def get_tw_data(self,twctl=None):
-		vmsg('Getting address data from tracking wallet')
+		self.cfg._util.vmsg('Getting address data from tracking wallet')
 		c = self.rpc
 		c = self.rpc
 		if 'label_api' in c.caps:
 		if 'label_api' in c.caps:
 			accts = await c.call('listlabels')
 			accts = await c.call('listlabels')

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

@@ -14,8 +14,7 @@ proto.btc.daemon: Bitcoin base protocol daemon classes
 
 
 import os
 import os
 
 
-from ...globalvars import g,gc
-from ...opts import opt
+from ...globalvars import gc
 from ...util import list_gen
 from ...util import list_gen
 from ...daemon import CoinDaemon,_nw,_dd
 from ...daemon import CoinDaemon,_nw,_dd
 
 
@@ -36,7 +35,7 @@ class bitcoin_core_daemon(CoinDaemon):
 
 
 	def init_datadir(self):
 	def init_datadir(self):
 		if self.network == 'regtest' and not self.test_suite:
 		if self.network == 'regtest' and not self.test_suite:
-			return os.path.join( g.data_dir_root, 'regtest', g.coin.lower() )
+			return os.path.join( self.cfg.data_dir_root, 'regtest', self.cfg.coin.lower() )
 		else:
 		else:
 			return super().init_datadir()
 			return super().init_datadir()
 
 
@@ -120,7 +119,7 @@ class bitcoin_core_daemon(CoinDaemon):
 			return ('importaddress',coinaddr,lbl,False)
 			return ('importaddress',coinaddr,lbl,False)
 
 
 	def estimatefee_args(self,rpc):
 	def estimatefee_args(self,rpc):
-		return (opt.fee_estimate_confs,)
+		return (self.cfg.fee_estimate_confs,)
 
 
 	def sigfail_errmsg(self,e):
 	def sigfail_errmsg(self,e):
 		return e.args[0]
 		return e.args[0]
@@ -143,7 +142,7 @@ class bitcoin_cash_node_daemon(bitcoin_core_daemon):
 		return ('importaddress',coinaddr,lbl,False)
 		return ('importaddress',coinaddr,lbl,False)
 
 
 	def estimatefee_args(self,rpc):
 	def estimatefee_args(self,rpc):
-		return () if rpc.daemon_version >= 190100 else (opt.fee_estimate_confs,)
+		return () if rpc.daemon_version >= 190100 else (self.cfg.fee_estimate_confs,)
 
 
 	def sigfail_errmsg(self,e):
 	def sigfail_errmsg(self,e):
 		return (
 		return (

+ 3 - 4
mmgen/proto/btc/misc.py

@@ -12,10 +12,9 @@
 proto.btc.misc: miscellaneous functions for Bitcoin base protocol
 proto.btc.misc: miscellaneous functions for Bitcoin base protocol
 """
 """
 
 
-from ...globalvars import g
 from ...util import msg,msg_r
 from ...util import msg,msg_r
 
 
-async def scantxoutset(rpc,descriptor_list):
+async def scantxoutset(cfg,rpc,descriptor_list):
 
 
 	import asyncio
 	import asyncio
 
 
@@ -28,8 +27,8 @@ async def scantxoutset(rpc,descriptor_list):
 
 
 	async def do_status():
 	async def do_status():
 
 
-		CR = '\n' if g.test_suite else '\r'
-		sleep_secs = 0.1 if g.test_suite else 2
+		CR = '\n' if cfg.test_suite else '\r'
+		sleep_secs = 0.1 if cfg.test_suite else 2
 		m = f'{CR}Scanning UTXO set: '
 		m = f'{CR}Scanning UTXO set: '
 		msg_r(m + '0% completed ')
 		msg_r(m + '0% completed ')
 
 

+ 17 - 17
mmgen/proto/btc/regtest.py

@@ -21,19 +21,18 @@ proto.btc.regtest: Coin daemon regression test mode setup and operations
 """
 """
 
 
 import os,shutil,json
 import os,shutil,json
-from ...opts import opt
-from ...globalvars import g
 from ...util import msg,gmsg,die,capfirst,suf
 from ...util import msg,gmsg,die,capfirst,suf
 from ...protocol import init_proto
 from ...protocol import init_proto
 from ...rpc import rpc_init,json_encoder
 from ...rpc import rpc_init,json_encoder
 from ...objmethods import MMGenObject
 from ...objmethods import MMGenObject
 
 
-def create_data_dir(data_dir):
+def create_data_dir(cfg,data_dir):
 	try: os.stat(os.path.join(data_dir,'regtest'))
 	try: os.stat(os.path.join(data_dir,'regtest'))
 	except: pass
 	except: pass
 	else:
 	else:
 		from ...ui import keypress_confirm
 		from ...ui import keypress_confirm
 		if keypress_confirm(
 		if keypress_confirm(
+				cfg,
 				f'Delete your existing MMGen regtest setup at {data_dir!r} and create a new one?'):
 				f'Delete your existing MMGen regtest setup at {data_dir!r} and create a new one?'):
 			shutil.rmtree(data_dir)
 			shutil.rmtree(data_dir)
 		else:
 		else:
@@ -69,18 +68,19 @@ class MMGenRegtest(MMGenObject):
 		'bch': 'n2fxhNx27GhHAWQhyuZ5REcBNrJqCJsJ12',
 		'bch': 'n2fxhNx27GhHAWQhyuZ5REcBNrJqCJsJ12',
 	}
 	}
 
 
-	def __init__(self,coin):
+	def __init__(self,cfg,coin):
+		self.cfg = cfg
 		self.coin = coin.lower()
 		self.coin = coin.lower()
 		assert self.coin in self.coins, f'{coin!r}: invalid coin for regtest'
 		assert self.coin in self.coins, f'{coin!r}: invalid coin for regtest'
 
 
 		from ...daemon import CoinDaemon
 		from ...daemon import CoinDaemon
-		self.proto = init_proto(self.coin,regtest=True,need_amt=True)
-		self.d = CoinDaemon(self.coin+'_rt',test_suite=g.test_suite)
+		self.proto = init_proto( cfg, self.coin, regtest=True, need_amt=True )
+		self.d = CoinDaemon(cfg,self.coin+'_rt',test_suite=cfg.test_suite)
 		self.miner_addr = self.miner_addrs[self.coin]
 		self.miner_addr = self.miner_addrs[self.coin]
 
 
 	def create_hdseed_wif(self):
 	def create_hdseed_wif(self):
 		from ...tool.api import tool_api
 		from ...tool.api import tool_api
-		t = tool_api()
+		t = tool_api(self.cfg)
 		t.init_coin(self.proto.coin,self.proto.network)
 		t.init_coin(self.proto.coin,self.proto.network)
 		t.addrtype = 'compressed' if self.proto.coin == 'BCH' else 'bech32'
 		t.addrtype = 'compressed' if self.proto.coin == 'BCH' else 'bech32'
 		return t.hex2wif(self.hdseed)
 		return t.hex2wif(self.hdseed)
@@ -114,13 +114,13 @@ class MMGenRegtest(MMGenObject):
 		if self.d.state != 'stopped':
 		if self.d.state != 'stopped':
 			await self.rpc_call('stop')
 			await self.rpc_call('stop')
 
 
-		create_data_dir(self.d.datadir)
+		create_data_dir( self.cfg, self.d.datadir )
 
 
 		gmsg(f'Starting {self.coin.upper()} regtest setup')
 		gmsg(f'Starting {self.coin.upper()} regtest setup')
 
 
 		self.d.start(silent=True)
 		self.d.start(silent=True)
 
 
-		rpc = await rpc_init(self.proto,backend=None,daemon=self.d)
+		rpc = await rpc_init(self.cfg,self.proto,backend=None,daemon=self.d)
 		for user in ('miner','bob','alice'):
 		for user in ('miner','bob','alice'):
 			gmsg(f'Creating {capfirst(user)}’s tracking wallet')
 			gmsg(f'Creating {capfirst(user)}’s tracking wallet')
 			await rpc.icall(
 			await rpc.icall(
@@ -149,7 +149,7 @@ class MMGenRegtest(MMGenObject):
 
 
 		gmsg('Setup complete')
 		gmsg('Setup complete')
 
 
-		if opt.setup_no_stop_daemon:
+		if self.cfg.setup_no_stop_daemon:
 			msg('Leaving regtest daemon running')
 			msg('Leaving regtest daemon running')
 		else:
 		else:
 			msg('Stopping regtest daemon')
 			msg('Stopping regtest daemon')
@@ -169,20 +169,20 @@ class MMGenRegtest(MMGenObject):
 	async def rpc_call(self,*args,wallet=None,start_daemon=True):
 	async def rpc_call(self,*args,wallet=None,start_daemon=True):
 		if start_daemon and self.d.state == 'stopped':
 		if start_daemon and self.d.state == 'stopped':
 			await self.start_daemon()
 			await self.start_daemon()
-		rpc = await rpc_init(self.proto,backend=None,daemon=self.d)
+		rpc = await rpc_init(self.cfg,self.proto,backend=None,daemon=self.d)
 		return await rpc.call(*args,wallet=wallet)
 		return await rpc.call(*args,wallet=wallet)
 
 
 	async def start(self):
 	async def start(self):
 		if self.d.state == 'stopped':
 		if self.d.state == 'stopped':
 			await self.start_daemon(silent=False)
 			await self.start_daemon(silent=False)
 		else:
 		else:
-			msg(f'{g.coin} regtest daemon already started')
+			msg(f'{self.cfg.coin} regtest daemon already started')
 
 
 	async def stop(self):
 	async def stop(self):
 		if self.d.state == 'stopped':
 		if self.d.state == 'stopped':
-			msg(f'{g.coin} regtest daemon already stopped')
+			msg(f'{self.cfg.coin} regtest daemon already stopped')
 		else:
 		else:
-			msg(f'Stopping {g.coin} regtest daemon')
+			msg(f'Stopping {self.cfg.coin} regtest daemon')
 			self.d.stop(silent=True)
 			self.d.stop(silent=True)
 
 
 	def state(self):
 	def state(self):
@@ -222,13 +222,13 @@ class MMGenRegtest(MMGenObject):
 
 
 	async def fork(self,coin): # currently disabled
 	async def fork(self,coin): # currently disabled
 
 
-		proto = init_proto(coin,False)
+		proto = init_proto( self.cfg, coin, False )
 		if not [f for f in proto.forks if f[2] == proto.coin.lower() and f[3] == True]:
 		if not [f for f in proto.forks if f[2] == proto.coin.lower() and f[3] == True]:
 			die(1,f'Coin {proto.coin} is not a replayable fork of coin {coin}')
 			die(1,f'Coin {proto.coin} is not a replayable fork of coin {coin}')
 
 
 		gmsg(f'Creating fork from coin {coin} to coin {proto.coin}')
 		gmsg(f'Creating fork from coin {coin} to coin {proto.coin}')
 
 
-		source_rt = MMGenRegtest(coin)
+		source_rt = MMGenRegtest( self.cfg, coin )
 
 
 		try:
 		try:
 			os.stat(source_rt.d.datadir)
 			os.stat(source_rt.d.datadir)
@@ -246,7 +246,7 @@ class MMGenRegtest(MMGenObject):
 		try: os.makedirs(self.d.datadir)
 		try: os.makedirs(self.d.datadir)
 		except: pass
 		except: pass
 
 
-		create_data_dir(self.d.datadir)
+		create_data_dir( self.cfg, self.d.datadir )
 		os.rmdir(self.d.datadir)
 		os.rmdir(self.d.datadir)
 		shutil.copytree(source_data_dir,self.d.datadir,symlinks=True)
 		shutil.copytree(source_data_dir,self.d.datadir,symlinks=True)
 		await self.start_daemon(reindex=True)
 		await self.start_daemon(reindex=True)

+ 16 - 16
mmgen/proto/btc/rpc.py

@@ -14,9 +14,8 @@ proto.btc.rpc: Bitcoin base protocol RPC client class
 
 
 import os
 import os
 
 
-from ...globalvars import g
 from ...base_obj import AsyncInit
 from ...base_obj import AsyncInit
-from ...util import ymsg,vmsg,die,fmt
+from ...util import ymsg,die,fmt
 from ...fileutil import get_lines_from_file
 from ...fileutil import get_lines_from_file
 from ...rpc import RPCClient,auth_data
 from ...rpc import RPCClient,auth_data
 
 
@@ -105,6 +104,7 @@ class BitcoinRPCClient(RPCClient,metaclass=AsyncInit):
 
 
 	async def __init__(
 	async def __init__(
 			self,
 			self,
+			cfg,
 			proto,
 			proto,
 			daemon,
 			daemon,
 			backend,
 			backend,
@@ -115,7 +115,8 @@ class BitcoinRPCClient(RPCClient,metaclass=AsyncInit):
 		self.call_sigs = getattr(CallSigs,daemon.id,None)
 		self.call_sigs = getattr(CallSigs,daemon.id,None)
 
 
 		super().__init__(
 		super().__init__(
-			host = 'localhost' if g.test_suite else (g.rpc_host or 'localhost'),
+			cfg  = cfg,
+			host = 'localhost' if cfg.test_suite else (cfg.rpc_host or 'localhost'),
 			port = daemon.rpc_port )
 			port = daemon.rpc_port )
 
 
 		self.set_auth()
 		self.set_auth()
@@ -182,15 +183,15 @@ class BitcoinRPCClient(RPCClient,metaclass=AsyncInit):
 			await self.check_or_create_daemon_wallet()
 			await self.check_or_create_daemon_wallet()
 
 
 		# for regtest, wallet path must remain '/' until Carol’s user wallet has been created
 		# for regtest, wallet path must remain '/' until Carol’s user wallet has been created
-		if g.regtest_user:
-			self.wallet_path = f'/wallet/{g.regtest_user}'
+		if cfg.regtest_user:
+			self.wallet_path = f'/wallet/{cfg.regtest_user}'
 
 
 	def set_auth(self):
 	def set_auth(self):
 		"""
 		"""
 		MMGen's credentials override coin daemon's
 		MMGen's credentials override coin daemon's
 		"""
 		"""
-		if g.rpc_user:
-			user,passwd = (g.rpc_user,g.rpc_password)
+		if self.cfg.rpc_user:
+			user,passwd = (self.cfg.rpc_user,self.cfg.rpc_password)
 		else:
 		else:
 			user,passwd = self.get_daemon_cfg_options(('rpcuser','rpcpassword')).values()
 			user,passwd = self.get_daemon_cfg_options(('rpcuser','rpcpassword')).values()
 
 
@@ -221,7 +222,7 @@ class BitcoinRPCClient(RPCClient,metaclass=AsyncInit):
 		is created, False otherwise
 		is created, False otherwise
 		"""
 		"""
 
 
-		if called or (self.chain == 'regtest' and g.regtest_user != 'carol'):
+		if called or (self.chain == 'regtest' and self.cfg.regtest_user != 'carol'):
 			return False
 			return False
 
 
 		twname = self.daemon.tracking_wallet_name
 		twname = self.daemon.tracking_wallet_name
@@ -230,7 +231,7 @@ class BitcoinRPCClient(RPCClient,metaclass=AsyncInit):
 		m = f'Please fix your {self.daemon.desc} wallet installation or cmdline options'
 		m = f'Please fix your {self.daemon.desc} wallet installation or cmdline options'
 		ret = False
 		ret = False
 
 
-		if g.carol:
+		if self.cfg.carol:
 			if 'carol' in loaded_wnames:
 			if 'carol' in loaded_wnames:
 				ret = True
 				ret = True
 			elif wallet_create:
 			elif wallet_create:
@@ -266,17 +267,16 @@ class BitcoinRPCClient(RPCClient,metaclass=AsyncInit):
 	def get_daemon_cfg_fn(self):
 	def get_daemon_cfg_fn(self):
 		# Use dirname() to remove 'bob' or 'alice' component
 		# Use dirname() to remove 'bob' or 'alice' component
 		return os.path.join(
 		return os.path.join(
-			(os.path.dirname(g.data_dir) if self.proto.regtest else self.daemon.datadir),
+			(os.path.dirname(self.cfg.data_dir) if self.proto.regtest else self.daemon.datadir),
 			self.daemon.cfg_file )
 			self.daemon.cfg_file )
 
 
 	def get_daemon_cfg_options(self,req_keys):
 	def get_daemon_cfg_options(self,req_keys):
 
 
 		fn = self.get_daemon_cfg_fn()
 		fn = self.get_daemon_cfg_fn()
-		from ...opts import opt
 		try:
 		try:
-			lines = get_lines_from_file(fn,'daemon config file',silent=not opt.verbose)
+			lines = get_lines_from_file( self.cfg, fn, 'daemon config file', silent=not self.cfg.verbose )
 		except:
 		except:
-			vmsg(f'Warning: {fn!r} does not exist or is unreadable')
+			self.cfg._util.vmsg(f'Warning: {fn!r} does not exist or is unreadable')
 			return dict((k,None) for k in req_keys)
 			return dict((k,None) for k in req_keys)
 
 
 		def gen():
 		def gen():
@@ -293,7 +293,7 @@ class BitcoinRPCClient(RPCClient,metaclass=AsyncInit):
 
 
 	def get_daemon_auth_cookie(self):
 	def get_daemon_auth_cookie(self):
 		fn = self.daemon.auth_cookie_fn
 		fn = self.daemon.auth_cookie_fn
-		return get_lines_from_file(fn,'cookie',quiet=True)[0] if os.access(fn,os.R_OK) else ''
+		return get_lines_from_file( self.cfg, fn, 'cookie', quiet=True )[0] if os.access(fn,os.R_OK) else ''
 
 
 	def info(self,info_id):
 	def info(self,info_id):
 
 
@@ -302,7 +302,7 @@ class BitcoinRPCClient(RPCClient,metaclass=AsyncInit):
 			if 'deployment_info' in self.caps:
 			if 'deployment_info' in self.caps:
 				return (
 				return (
 					self.cached['deploymentinfo']['deployments']['segwit']['active']
 					self.cached['deploymentinfo']['deployments']['segwit']['active']
-					or ( g.test_suite and not self.chain == 'regtest' )
+					or ( self.cfg.test_suite and not self.chain == 'regtest' )
 				)
 				)
 
 
 			d = self.cached['blockchaininfo']
 			d = self.cached['blockchaininfo']
@@ -319,7 +319,7 @@ class BitcoinRPCClient(RPCClient,metaclass=AsyncInit):
 			except:
 			except:
 				pass
 				pass
 
 
-			if g.test_suite and not self.chain == 'regtest':
+			if self.cfg.test_suite and not self.chain == 'regtest':
 				return True
 				return True
 
 
 			return False
 			return False

+ 5 - 6
mmgen/proto/btc/tw/ctl.py

@@ -12,9 +12,8 @@
 proto.btc.tw.ctl: Bitcoin base protocol tracking wallet control class
 proto.btc.tw.ctl: Bitcoin base protocol tracking wallet control class
 """
 """
 
 
-from ....globalvars import g
 from ....tw.ctl import TwCtl,write_mode
 from ....tw.ctl import TwCtl,write_mode
-from ....util import msg,msg_r,rmsg,vmsg,die,suf,fmt_list
+from ....util import msg,msg_r,rmsg,die,suf,fmt_list
 
 
 class BitcoinTwCtl(TwCtl):
 class BitcoinTwCtl(TwCtl):
 
 
@@ -54,7 +53,7 @@ class BitcoinTwCtl(TwCtl):
 
 
 		start = start or 0
 		start = start or 0
 		endless = stop == None
 		endless = stop == None
-		CR = '\n' if g.test_suite else '\r'
+		CR = '\n' if self.cfg.test_suite else '\r'
 
 
 		if not ( start >= 0 and (stop if stop is not None else start) >= start ):
 		if not ( start >= 0 and (stop if stop is not None else start) >= start ):
 			die(1,f'{start} {stop}: invalid range')
 			die(1,f'{start} {stop}: invalid range')
@@ -101,7 +100,7 @@ class BitcoinTwCtl(TwCtl):
 	async def rescan_addresses(self,coin_addrs):
 	async def rescan_addresses(self,coin_addrs):
 
 
 		from ..misc import scantxoutset
 		from ..misc import scantxoutset
-		res = await scantxoutset( self.rpc, [f'addr({addr})' for addr in coin_addrs] )
+		res = await scantxoutset( self.cfg, self.rpc, [f'addr({addr})' for addr in coin_addrs] )
 
 
 		if not res['success']:
 		if not res['success']:
 			msg('UTXO scanning failed or was interrupted')
 			msg('UTXO scanning failed or was interrupted')
@@ -113,8 +112,8 @@ class BitcoinTwCtl(TwCtl):
 				suf(res['unspents']),
 				suf(res['unspents']),
 				len(blocks),
 				len(blocks),
 				suf(blocks) ))
 				suf(blocks) ))
-			vmsg(f'Blocks to rescan: {fmt_list(blocks,fmt="bare")}')
-			CR = '\n' if g.test_suite else '\r'
+			self.cfg._util.vmsg(f'Blocks to rescan: {fmt_list(blocks,fmt="bare")}')
+			CR = '\n' if self.cfg.test_suite else '\r'
 			for n,block in enumerate(blocks):
 			for n,block in enumerate(blocks):
 				msg_r(f'{CR}Rescanning block: {block} ({n+1}/{len(blocks)})')
 				msg_r(f'{CR}Rescanning block: {block} ({n+1}/{len(blocks)})')
 				# httplib seems to require fresh connection here, so specify timeout
 				# httplib seems to require fresh connection here, so specify timeout

+ 2 - 1
mmgen/proto/btc/tw/json.py

@@ -81,6 +81,7 @@ class BitcoinTwJSON(TwJSON):
 				if self.prune:
 				if self.prune:
 					from .prune import TwAddressesPrune
 					from .prune import TwAddressesPrune
 					self._addrlist = al = await TwAddressesPrune(
 					self._addrlist = al = await TwAddressesPrune(
+						self.cfg,
 						self.proto,
 						self.proto,
 						get_data  = True,
 						get_data  = True,
 						warn_used = self.warn_used )
 						warn_used = self.warn_used )
@@ -88,7 +89,7 @@ class BitcoinTwJSON(TwJSON):
 					self.pruned = al.do_prune()
 					self.pruned = al.do_prune()
 				else:
 				else:
 					from .addresses import TwAddresses
 					from .addresses import TwAddresses
-					self._addrlist = await TwAddresses(self.proto,get_data=True)
+					self._addrlist = await TwAddresses(self.cfg,self.proto,get_data=True)
 			return self._addrlist
 			return self._addrlist
 
 
 		async def get_entries(self): # TODO: include 'received' field
 		async def get_entries(self): # TODO: include 'received' field

+ 3 - 4
mmgen/proto/btc/tw/txhistory.py

@@ -13,7 +13,6 @@ proto.btc.tw.txhistory: Bitcoin base protocol tracking wallet transaction histor
 """
 """
 
 
 from collections import namedtuple
 from collections import namedtuple
-from ....globalvars import g
 from ....tw.txhistory import TwTxHistory
 from ....tw.txhistory import TwTxHistory
 from ....tw.shared import get_tw_label,TwMMGenID
 from ....tw.shared import get_tw_label,TwMMGenID
 from ....addr import CoinAddr
 from ....addr import CoinAddr
@@ -291,7 +290,7 @@ Filters/Actions: show [u]nconfirmed, [q]uit menu, r[e]draw:
 
 
 		data = list(gen_parsed_data())
 		data = list(gen_parsed_data())
 
 
-		if g.debug_tw:
+		if self.cfg.debug_tw:
 			import json
 			import json
 			from ....rpc import json_encoder
 			from ....rpc import json_encoder
 			def do_json_dump(*data):
 			def do_json_dump(*data):
@@ -328,7 +327,7 @@ Filters/Actions: show [u]nconfirmed, [q]uit menu, r[e]draw:
 			for tx in _wallet_txs:
 			for tx in _wallet_txs:
 				tx['decoded'] = next(_decoded_txs)
 				tx['decoded'] = next(_decoded_txs)
 
 
-		if g.debug_tw:
+		if self.cfg.debug_tw:
 			do_json_dump((_wallet_txs, 'wallet-txs'),)
 			do_json_dump((_wallet_txs, 'wallet-txs'),)
 
 
 		_wip = namedtuple('prevout',['txid','vout'])
 		_wip = namedtuple('prevout',['txid','vout'])
@@ -353,7 +352,7 @@ Filters/Actions: show [u]nconfirmed, [q]uit menu, r[e]draw:
 		for d in txdata:
 		for d in txdata:
 			d['prevout_txs'] = [_prevout_txs_dict[txid] for txid in {i.txid for i in d['prevouts']} ]
 			d['prevout_txs'] = [_prevout_txs_dict[txid] for txid in {i.txid for i in d['prevouts']} ]
 
 
-		if g.debug_tw:
+		if self.cfg.debug_tw:
 			do_json_dump(
 			do_json_dump(
 				(rpc_data,     'txhist-rpc'),
 				(rpc_data,     'txhist-rpc'),
 				(data,         'txhist'),
 				(data,         'txhist'),

+ 3 - 4
mmgen/proto/btc/tx/base.py

@@ -15,9 +15,8 @@ proto.btc.tx.base: Bitcoin base transaction class
 from collections import namedtuple
 from collections import namedtuple
 
 
 import mmgen.tx.base as TxBase
 import mmgen.tx.base as TxBase
-from ....opts import opt
 from ....obj import MMGenObject,MMGenList,HexStr
 from ....obj import MMGenObject,MMGenList,HexStr
-from ....util import msg,dmsg,make_chksum_6,die,pp_fmt
+from ....util import msg,make_chksum_6,die,pp_fmt
 
 
 def addr2scriptPubKey(proto,addr):
 def addr2scriptPubKey(proto,addr):
 
 
@@ -247,12 +246,12 @@ class Base(TxBase.Base):
 
 
 		ret = (old_size * 3 + new_size) // 4
 		ret = (old_size * 3 + new_size) // 4
 
 
-		dmsg(
+		self.cfg._util.dmsg(
 			'\nData from estimate_size():\n' +
 			'\nData from estimate_size():\n' +
 			f'  inputs size: {isize}, outputs size: {osize}, witness size: {wsize}\n' +
 			f'  inputs size: {isize}, outputs size: {osize}, witness size: {wsize}\n' +
 			f'  size: {new_size}, vsize: {ret}, old_size: {old_size}' )
 			f'  size: {new_size}, vsize: {ret}, old_size: {old_size}' )
 
 
-		return int(ret * (opt.vsize_adj or 1))
+		return int(ret * (self.cfg.vsize_adj or 1))
 
 
 	# convert absolute CoinAmt fee to sat/byte using estimated size
 	# convert absolute CoinAmt fee to sat/byte using estimated size
 	def fee_abs2rel(self,abs_fee,to_unit='satoshi'):
 	def fee_abs2rel(self,abs_fee,to_unit='satoshi'):

+ 9 - 10
mmgen/proto/btc/tx/new.py

@@ -14,9 +14,8 @@ proto.btc.tx.new: Bitcoin new transaction class
 
 
 import mmgen.tx.new as TxBase
 import mmgen.tx.new as TxBase
 from .base import Base
 from .base import Base
-from ....opts import opt
 from ....obj import HexStr,MMGenTxID
 from ....obj import HexStr,MMGenTxID
-from ....util import msg,dmsg,vmsg,make_chksum_6,die
+from ....util import msg,make_chksum_6,die
 
 
 class New(Base,TxBase.New):
 class New(Base,TxBase.New):
 	usr_fee_prompt = 'Enter transaction fee: '
 	usr_fee_prompt = 'Enter transaction fee: '
@@ -27,15 +26,15 @@ class New(Base,TxBase.New):
 	def relay_fee(self):
 	def relay_fee(self):
 		kb_fee = self.proto.coin_amt(self.rpc.cached['networkinfo']['relayfee'])
 		kb_fee = self.proto.coin_amt(self.rpc.cached['networkinfo']['relayfee'])
 		ret = kb_fee * self.estimate_size() / 1024
 		ret = kb_fee * self.estimate_size() / 1024
-		vmsg('Relay fee: {} {c}/kB, for transaction: {} {c}'.format(kb_fee,ret,c=self.coin))
+		self.cfg._util.vmsg('Relay fee: {} {c}/kB, for transaction: {} {c}'.format(kb_fee,ret,c=self.coin))
 		return ret
 		return ret
 
 
 	async def get_rel_fee_from_network(self):
 	async def get_rel_fee_from_network(self):
 		try:
 		try:
 			ret = await self.rpc.call(
 			ret = await self.rpc.call(
 				'estimatesmartfee',
 				'estimatesmartfee',
-				opt.fee_estimate_confs,
-				opt.fee_estimate_mode.upper() )
+				self.cfg.fee_estimate_confs,
+				self.cfg.fee_estimate_mode.upper() )
 			fee_per_kb = ret['feerate'] if 'feerate' in ret else -2
 			fee_per_kb = ret['feerate'] if 'feerate' in ret else -2
 			fe_type = 'estimatesmartfee'
 			fe_type = 'estimatesmartfee'
 		except:
 		except:
@@ -57,13 +56,13 @@ class New(Base,TxBase.New):
 		from decimal import Decimal
 		from decimal import Decimal
 		tx_size = self.estimate_size()
 		tx_size = self.estimate_size()
 		ret = self.proto.coin_amt(
 		ret = self.proto.coin_amt(
-			fee_per_kb * Decimal(opt.fee_adjust) * tx_size / 1024,
+			fee_per_kb * Decimal(self.cfg.fee_adjust) * tx_size / 1024,
 			from_decimal = True )
 			from_decimal = True )
-		if opt.verbose:
+		if self.cfg.verbose:
 			msg(fmt(f"""
 			msg(fmt(f"""
-				{fe_type.upper()} fee for {opt.fee_estimate_confs} confirmations: {fee_per_kb} {self.coin}/kB
+				{fe_type.upper()} fee for {self.cfg.fee_estimate_confs} confirmations: {fee_per_kb} {self.coin}/kB
 				TX size (estimated): {tx_size} bytes
 				TX size (estimated): {tx_size} bytes
-				Fee adjustment factor: {opt.fee_adjust:.2f}
+				Fee adjustment factor: {self.cfg.fee_adjust:.2f}
 				Absolute fee (fee_per_kb * adj_factor * tx_size / 1024): {ret} {self.coin}
 				Absolute fee (fee_per_kb * adj_factor * tx_size / 1024): {ret} {self.coin}
 			""").strip())
 			""").strip())
 		return ret
 		return ret
@@ -114,7 +113,7 @@ class New(Base,TxBase.New):
 		if not bump:
 		if not bump:
 			self.inputs.sort_bip69()
 			self.inputs.sort_bip69()
 			# Set all sequence numbers to the same value, in conformity with the behavior of most modern wallets:
 			# Set all sequence numbers to the same value, in conformity with the behavior of most modern wallets:
-			seqnum_val = self.proto.max_int - (2 if opt.rbf else 1 if locktime else 0)
+			seqnum_val = self.proto.max_int - (2 if self.cfg.rbf else 1 if locktime else 0)
 			for i in self.inputs:
 			for i in self.inputs:
 				i.sequence = seqnum_val
 				i.sequence = seqnum_val
 
 

+ 3 - 4
mmgen/proto/btc/tx/online.py

@@ -14,7 +14,6 @@ proto.btc.tx.online: Bitcoin online signed transaction class
 
 
 import mmgen.tx.online as TxBase
 import mmgen.tx.online as TxBase
 from .signed import Signed
 from .signed import Signed
-from ....globalvars import *
 from ....util import msg,ymsg,rmsg
 from ....util import msg,ymsg,rmsg
 
 
 class OnlineSigned(Signed,TxBase.OnlineSigned):
 class OnlineSigned(Signed,TxBase.OnlineSigned):
@@ -23,7 +22,7 @@ class OnlineSigned(Signed,TxBase.OnlineSigned):
 
 
 		self.check_correct_chain()
 		self.check_correct_chain()
 
 
-		if not g.bogus_send:
+		if not self.cfg.bogus_send:
 			if self.has_segwit_outputs() and not self.rpc.info('segwit_is_active'):
 			if self.has_segwit_outputs() and not self.rpc.info('segwit_is_active'):
 				die(2,'Transaction has Segwit outputs, but this blockchain does not support Segwit'
 				die(2,'Transaction has Segwit outputs, but this blockchain does not support Segwit'
 						+ ' at the current height')
 						+ ' at the current height')
@@ -40,7 +39,7 @@ class OnlineSigned(Signed,TxBase.OnlineSigned):
 		if prompt_user:
 		if prompt_user:
 			self.confirm_send()
 			self.confirm_send()
 
 
-		if g.bogus_send:
+		if self.cfg.bogus_send:
 			ret = None
 			ret = None
 		else:
 		else:
 			try:
 			try:
@@ -67,7 +66,7 @@ class OnlineSigned(Signed,TxBase.OnlineSigned):
 				sys.exit(1)
 				sys.exit(1)
 			return False
 			return False
 		else:
 		else:
-			if g.bogus_send:
+			if self.cfg.bogus_send:
 				m = 'BOGUS transaction NOT sent: {}'
 				m = 'BOGUS transaction NOT sent: {}'
 			else:
 			else:
 				m = 'Transaction sent: {}'
 				m = 'Transaction sent: {}'

+ 2 - 2
mmgen/proto/btc/tx/signed.py

@@ -14,7 +14,7 @@ proto.btc.tx.signed: Bitcoin signed transaction class
 
 
 import mmgen.tx.signed as TxBase
 import mmgen.tx.signed as TxBase
 from .completed import Completed
 from .completed import Completed
-from ....util import fmt,vmsg,die
+from ....util import fmt,die
 
 
 class Signed(Completed,TxBase.Signed):
 class Signed(Completed,TxBase.Signed):
 
 
@@ -22,7 +22,7 @@ class Signed(Completed,TxBase.Signed):
 		est_vsize = self.estimate_size()
 		est_vsize = self.estimate_size()
 		d = tx_decoded
 		d = tx_decoded
 		vsize = d['vsize'] if 'vsize' in d else d['size']
 		vsize = d['vsize'] if 'vsize' in d else d['size']
-		vmsg(f'\nVsize: {vsize} (true) {est_vsize} (estimated)')
+		self.cfg._util.vmsg(f'\nVsize: {vsize} (true) {est_vsize} (estimated)')
 		ratio = float(est_vsize) / vsize
 		ratio = float(est_vsize) / vsize
 		if not (0.95 < ratio < 1.05): # allow for 5% error
 		if not (0.95 < ratio < 1.05): # allow for 5% error
 			die( 'BadTxSizeEstimate', fmt(f"""
 			die( 'BadTxSizeEstimate', fmt(f"""

+ 2 - 3
mmgen/proto/btc/tx/status.py

@@ -15,7 +15,6 @@ proto.btc.tx.status: Bitcoin transaction status class
 import time
 import time
 
 
 import mmgen.tx.status as TxBase
 import mmgen.tx.status as TxBase
-from ....opts import opt
 from ....util import msg,suf,die,secs_to_dhms
 from ....util import msg,suf,die,secs_to_dhms
 
 
 class Status(TxBase.Status):
 class Status(TxBase.Status):
@@ -82,7 +81,7 @@ class Status(TxBase.Status):
 					verbose           = False )
 					verbose           = False )
 				rep = ('' if d.get('bip125-replaceable') == 'yes' else 'NOT ') + 'replaceable'
 				rep = ('' if d.get('bip125-replaceable') == 'yes' else 'NOT ') + 'replaceable'
 				t = d['timereceived']
 				t = d['timereceived']
-				if opt.quiet:
+				if tx.cfg.quiet:
 					msg('Transaction is in mempool')
 					msg('Transaction is in mempool')
 				else:
 				else:
 					msg(f'TX status: in mempool, {rep}')
 					msg(f'TX status: in mempool, {rep}')
@@ -101,7 +100,7 @@ class Status(TxBase.Status):
 					f'has {r.replacing_confs} confirmation{suf(r.replacing_confs)}'
 					f'has {r.replacing_confs} confirmation{suf(r.replacing_confs)}'
 				if r.replacing_confs else
 				if r.replacing_confs else
 					'is in mempool' ) )
 					'is in mempool' ) )
-			if not opt.quiet:
+			if not tx.cfg.quiet:
 				msg('Replacing transactions:')
 				msg('Replacing transactions:')
 				d = []
 				d = []
 				for txid in r.replacing_txs:
 				for txid in r.replacing_txs:

+ 7 - 8
mmgen/proto/btc/tx/unsigned.py

@@ -14,9 +14,8 @@ proto.btc.tx.unsigned: Bitcoin unsigned transaction class
 
 
 import mmgen.tx.unsigned as TxBase
 import mmgen.tx.unsigned as TxBase
 from .completed import Completed
 from .completed import Completed
-from ....globalvars import *
 from ....obj import HexStr,CoinTxID,MMGenDict
 from ....obj import HexStr,CoinTxID,MMGenDict
-from ....util import msg,msg_r,ymsg,qmsg,suf
+from ....util import msg,msg_r,ymsg,suf
 
 
 class Unsigned(Completed,TxBase.Unsigned):
 class Unsigned(Completed,TxBase.Unsigned):
 	desc = 'unsigned transaction'
 	desc = 'unsigned transaction'
@@ -34,12 +33,12 @@ class Unsigned(Completed,TxBase.Unsigned):
 
 
 		self.check_pubkey_scripts()
 		self.check_pubkey_scripts()
 
 
-		qmsg(f'Passing {len(keys)} key{suf(keys)} to {self.rpc.daemon.exec_fn}')
+		self.cfg._util.qmsg(f'Passing {len(keys)} key{suf(keys)} to {self.rpc.daemon.exec_fn}')
 
 
 		if self.has_segwit_inputs():
 		if self.has_segwit_inputs():
 			from ....addrgen import KeyGenerator,AddrGenerator
 			from ....addrgen import KeyGenerator,AddrGenerator
-			kg = KeyGenerator(self.proto,'std')
-			ag = AddrGenerator(self.proto,'segwit')
+			kg = KeyGenerator( self.cfg, self.proto, 'std' )
+			ag = AddrGenerator( self.cfg, self.proto, 'segwit' )
 			keydict = MMGenDict([(d.addr,d.sec) for d in keys])
 			keydict = MMGenDict([(d.addr,d.sec) for d in keys])
 
 
 		sig_data = []
 		sig_data = []
@@ -68,7 +67,7 @@ class Unsigned(Completed,TxBase.Unsigned):
 
 
 		try:
 		try:
 			self.update_serialized(ret['hex'])
 			self.update_serialized(ret['hex'])
-			new = await SignedTX(data=self.__dict__)
+			new = await SignedTX(cfg=self.cfg,data=self.__dict__)
 			tx_decoded = await self.rpc.call( 'decoderawtransaction', ret['hex'] )
 			tx_decoded = await self.rpc.call( 'decoderawtransaction', ret['hex'] )
 			new.compare_size_and_estimated_size(tx_decoded)
 			new.compare_size_and_estimated_size(tx_decoded)
 			new.coin_txid = CoinTxID(self.deserialized.txid)
 			new.coin_txid = CoinTxID(self.deserialized.txid)
@@ -78,7 +77,7 @@ class Unsigned(Completed,TxBase.Unsigned):
 			return new
 			return new
 		except Exception as e:
 		except Exception as e:
 			ymsg(f'\n{e.args[0]}')
 			ymsg(f'\n{e.args[0]}')
-			if g.exec_wrapper:
-				import traceback
+			if self.cfg.exec_wrapper:
+				import sys,traceback
 				ymsg( '\n' + ''.join(traceback.format_exception(*sys.exc_info())) )
 				ymsg( '\n' + ''.join(traceback.format_exception(*sys.exc_info())) )
 			return False
 			return False

+ 2 - 3
mmgen/proto/eth/addrdata.py

@@ -33,9 +33,8 @@ class EthereumTwAddrData(TwAddrData):
 
 
 	async def get_tw_data(self,twctl=None):
 	async def get_tw_data(self,twctl=None):
 		from ...tw.ctl import TwCtl
 		from ...tw.ctl import TwCtl
-		from ...util import vmsg
-		vmsg('Getting address data from tracking wallet')
-		twctl = (twctl or await TwCtl(self.proto)).mmid_ordered_dict
+		self.cfg._util.vmsg('Getting address data from tracking wallet')
+		twctl = (twctl or await TwCtl(self.cfg,self.proto)).mmid_ordered_dict
 		# emulate the output of RPC 'listaccounts' and 'getaddressesbyaccount'
 		# emulate the output of RPC 'listaccounts' and 'getaddressesbyaccount'
 		return [(mmid+' '+d['comment'],[d['addr']]) for mmid,d in list(twctl.items())]
 		return [(mmid+' '+d['comment'],[d['addr']]) for mmid,d in list(twctl.items())]
 
 

+ 9 - 8
mmgen/proto/eth/contract.py

@@ -25,7 +25,6 @@ from . import rlp
 
 
 from . import erigon_sleep
 from . import erigon_sleep
 from ...util import msg,pp_msg
 from ...util import msg,pp_msg
-from ...globalvars import g
 from ...base_obj import AsyncInit
 from ...base_obj import AsyncInit
 from ...obj import MMGenObject,CoinTxID
 from ...obj import MMGenObject,CoinTxID
 from ...addr import CoinAddr,TokenAddr
 from ...addr import CoinAddr,TokenAddr
@@ -46,7 +45,7 @@ class TokenCommon(MMGenObject):
 
 
 	async def do_call(self,method_sig,method_args='',toUnit=False):
 	async def do_call(self,method_sig,method_args='',toUnit=False):
 		data = self.create_method_id(method_sig) + method_args
 		data = self.create_method_id(method_sig) + method_args
-		if g.debug:
+		if self.cfg.debug:
 			msg('ETH_CALL {}:  {}'.format(
 			msg('ETH_CALL {}:  {}'.format(
 				method_sig,
 				method_sig,
 				'\n  '.join(parse_abi(data)) ))
 				'\n  '.join(parse_abi(data)) ))
@@ -121,7 +120,7 @@ class TokenCommon(MMGenObject):
 		if tx.sender.hex() != from_addr:
 		if tx.sender.hex() != from_addr:
 			die(3,f'Sender address {from_addr!r} does not match address of key {tx.sender.hex()!r}!')
 			die(3,f'Sender address {from_addr!r} does not match address of key {tx.sender.hex()!r}!')
 
 
-		if g.debug:
+		if self.cfg.debug:
 			msg('TOKEN DATA:')
 			msg('TOKEN DATA:')
 			pp_msg(tx.to_dict())
 			pp_msg(tx.to_dict())
 			msg('PARSED ABI DATA:\n  {}'.format(
 			msg('PARSED ABI DATA:\n  {}'.format(
@@ -152,10 +151,11 @@ class TokenCommon(MMGenObject):
 
 
 class Token(TokenCommon):
 class Token(TokenCommon):
 
 
-	def __init__(self,proto,addr,decimals,rpc=None):
+	def __init__(self,cfg,proto,addr,decimals,rpc=None):
 		if type(self).__name__ == 'Token':
 		if type(self).__name__ == 'Token':
 			from ...util2 import get_keccak
 			from ...util2 import get_keccak
-			self.keccak_256 = get_keccak()
+			self.keccak_256 = get_keccak(cfg)
+		self.cfg = cfg
 		self.proto = proto
 		self.proto = proto
 		self.addr = TokenAddr(proto,addr)
 		self.addr = TokenAddr(proto,addr)
 		assert isinstance(decimals,int),f'decimals param must be int instance, not {type(decimals)}'
 		assert isinstance(decimals,int),f'decimals param must be int instance, not {type(decimals)}'
@@ -165,13 +165,14 @@ class Token(TokenCommon):
 
 
 class TokenResolve(TokenCommon,metaclass=AsyncInit):
 class TokenResolve(TokenCommon,metaclass=AsyncInit):
 
 
-	async def __init__(self,proto,rpc,addr):
+	async def __init__(self,cfg,proto,rpc,addr):
 		from ...util2 import get_keccak
 		from ...util2 import get_keccak
-		self.keccak_256 = get_keccak()
+		self.keccak_256 = get_keccak(cfg)
+		self.cfg = cfg
 		self.proto = proto
 		self.proto = proto
 		self.rpc = rpc
 		self.rpc = rpc
 		self.addr = TokenAddr(proto,addr)
 		self.addr = TokenAddr(proto,addr)
 		decimals = await self.get_decimals() # requires self.addr!
 		decimals = await self.get_decimals() # requires self.addr!
 		if not decimals:
 		if not decimals:
 			die( 'TokenNotInBlockchain', f'Token {addr!r} not in blockchain' )
 			die( 'TokenNotInBlockchain', f'Token {addr!r} not in blockchain' )
-		Token.__init__(self,proto,addr,decimals,rpc)
+		Token.__init__(self,cfg,proto,addr,decimals,rpc)

+ 9 - 9
mmgen/proto/eth/misc.py

@@ -15,7 +15,7 @@ proto.eth.misc: miscellaneous utilities for Ethereum base protocol
 from ...util import die
 from ...util import die
 from ...util2 import get_keccak
 from ...util2 import get_keccak
 
 
-def extract_key_from_geth_keystore_wallet(wallet_fn,passwd,check_addr=True):
+def extract_key_from_geth_keystore_wallet(cfg,wallet_fn,passwd,check_addr=True):
 	"""
 	"""
 	Decrypt the encrypted private key in a Geth keystore wallet, returning the decrypted key
 	Decrypt the encrypted private key in a Geth keystore wallet, returning the decrypted key
 	"""
 	"""
@@ -42,7 +42,7 @@ def extract_key_from_geth_keystore_wallet(wallet_fn,passwd,check_addr=True):
 		dklen    = sp['dklen'] )
 		dklen    = sp['dklen'] )
 
 
 	# Check password by comparing generated MAC to stored MAC
 	# Check password by comparing generated MAC to stored MAC
-	mac_chk = get_keccak()(hashed_pw[16:32] + bytes.fromhex( cdata['ciphertext'] )).digest().hex()
+	mac_chk = get_keccak(cfg)(hashed_pw[16:32] + bytes.fromhex( cdata['ciphertext'] )).digest().hex()
 	if mac_chk != cdata['mac']:
 	if mac_chk != cdata['mac']:
 		die(1,'Incorrect passphrase')
 		die(1,'Incorrect passphrase')
 
 
@@ -60,32 +60,32 @@ def extract_key_from_geth_keystore_wallet(wallet_fn,passwd,check_addr=True):
 	if check_addr:
 	if check_addr:
 		from ...tool.coin import tool_cmd
 		from ...tool.coin import tool_cmd
 		from ...protocol import init_proto
 		from ...protocol import init_proto
-		t = tool_cmd( proto=init_proto('eth') )
+		t = tool_cmd( cfg=cfg, proto=init_proto(cfg,'eth') )
 		addr = t.wif2addr(key.hex())
 		addr = t.wif2addr(key.hex())
 		addr_chk = wallet_data['address']
 		addr_chk = wallet_data['address']
 		assert addr == addr_chk, f'incorrect address: ({addr} != {addr_chk})'
 		assert addr == addr_chk, f'incorrect address: ({addr} != {addr_chk})'
 
 
 	return key
 	return key
 
 
-def hash_message(message,msghash_type):
-	return get_keccak()(
+def hash_message(cfg,message,msghash_type):
+	return get_keccak(cfg)(
 		{
 		{
 			'raw': message,
 			'raw': message,
 			'eth_sign': '\x19Ethereum Signed Message:\n{}{}'.format( len(message), message ),
 			'eth_sign': '\x19Ethereum Signed Message:\n{}{}'.format( len(message), message ),
 		}[msghash_type].encode()
 		}[msghash_type].encode()
 	).digest()
 	).digest()
 
 
-def ec_sign_message_with_privkey(message,key,msghash_type):
+def ec_sign_message_with_privkey(cfg,message,key,msghash_type):
 	"""
 	"""
 	Sign an arbitrary string with an Ethereum private key, returning the signature
 	Sign an arbitrary string with an Ethereum private key, returning the signature
 
 
 	Conforms to the standard defined by the Geth `eth_sign` JSON-RPC call
 	Conforms to the standard defined by the Geth `eth_sign` JSON-RPC call
 	"""
 	"""
 	from py_ecc.secp256k1 import ecdsa_raw_sign
 	from py_ecc.secp256k1 import ecdsa_raw_sign
-	v,r,s = ecdsa_raw_sign( hash_message(message,msghash_type), key )
+	v,r,s = ecdsa_raw_sign( hash_message(cfg,message,msghash_type), key )
 	return '{:064x}{:064x}{:02x}'.format(r,s,v)
 	return '{:064x}{:064x}{:02x}'.format(r,s,v)
 
 
-def ec_recover_pubkey(message,sig,msghash_type):
+def ec_recover_pubkey(cfg,message,sig,msghash_type):
 	"""
 	"""
 	Given a message and signature, recover the public key associated with the private key
 	Given a message and signature, recover the public key associated with the private key
 	used to make the signature
 	used to make the signature
@@ -95,5 +95,5 @@ def ec_recover_pubkey(message,sig,msghash_type):
 	from py_ecc.secp256k1 import ecdsa_raw_recover
 	from py_ecc.secp256k1 import ecdsa_raw_recover
 	r,s,v = ( sig[:64], sig[64:128], sig[128:] )
 	r,s,v = ( sig[:64], sig[64:128], sig[128:] )
 	return '{:064x}{:064x}'.format(
 	return '{:064x}{:064x}'.format(
-		*ecdsa_raw_recover( hash_message(message,msghash_type), tuple(int(hexstr,16) for hexstr in (v,r,s)) )
+		*ecdsa_raw_recover( hash_message(cfg,message,msghash_type), tuple(int(hexstr,16) for hexstr in (v,r,s)) )
 	)
 	)

+ 3 - 2
mmgen/proto/eth/msg.py

@@ -24,7 +24,7 @@ class coin_msg(coin_msg):
 
 
 		async def do_sign(self,wif,message,msghash_type):
 		async def do_sign(self,wif,message,msghash_type):
 			from .misc import ec_sign_message_with_privkey
 			from .misc import ec_sign_message_with_privkey
-			return ec_sign_message_with_privkey( message, bytes.fromhex(wif), msghash_type )
+			return ec_sign_message_with_privkey( self.cfg, message, bytes.fromhex(wif), msghash_type )
 
 
 	class signed_online(coin_msg.signed_online):
 	class signed_online(coin_msg.signed_online):
 
 
@@ -32,7 +32,8 @@ class coin_msg(coin_msg):
 			from ...tool.coin import tool_cmd
 			from ...tool.coin import tool_cmd
 			from .misc import ec_recover_pubkey
 			from .misc import ec_recover_pubkey
 			return tool_cmd(
 			return tool_cmd(
+				self.cfg,
 				proto = self.proto).pubhex2addr(
 				proto = self.proto).pubhex2addr(
-					ec_recover_pubkey( message, sig, msghash_type )) == addr
+					ec_recover_pubkey( self.cfg, message, sig, msghash_type )) == addr
 
 
 	class exported_sigs(coin_msg.exported_sigs,signed_online): pass
 	class exported_sigs(coin_msg.exported_sigs,signed_online): pass

+ 1 - 2
mmgen/proto/eth/params.py

@@ -12,7 +12,6 @@
 proto.eth.params: Ethereum protocol
 proto.eth.params: Ethereum protocol
 """
 """
 
 
-from ...globalvars import g
 from ...protocol import CoinProtocol,_nw,decoded_addr
 from ...protocol import CoinProtocol,_nw,decoded_addr
 from ...util import is_hex_str_lc,Msg
 from ...util import is_hex_str_lc,Msg
 
 
@@ -57,7 +56,7 @@ class mainnet(CoinProtocol.DummyWIF,CoinProtocol.Secp256k1):
 	def decode_addr(self,addr):
 	def decode_addr(self,addr):
 		if is_hex_str_lc(addr) and len(addr) == self.addr_len * 2:
 		if is_hex_str_lc(addr) and len(addr) == self.addr_len * 2:
 			return decoded_addr( bytes.fromhex(addr), None, 'ethereum' )
 			return decoded_addr( bytes.fromhex(addr), None, 'ethereum' )
-		if g.debug:
+		if self.cfg.debug:
 			Msg(f'Invalid address: {addr}')
 			Msg(f'Invalid address: {addr}')
 		return False
 		return False
 
 

+ 3 - 2
mmgen/proto/eth/rpc.py

@@ -14,7 +14,6 @@ proto.eth.rpc: Ethereum base protocol RPC client class
 
 
 import re
 import re
 
 
-from ...globalvars import g
 from ...base_obj import AsyncInit
 from ...base_obj import AsyncInit
 from ...obj import Int
 from ...obj import Int
 from ...util import die,oneshot_warning_group
 from ...util import die,oneshot_warning_group
@@ -37,6 +36,7 @@ class EthereumRPCClient(RPCClient,metaclass=AsyncInit):
 
 
 	async def __init__(
 	async def __init__(
 			self,
 			self,
+			cfg,
 			proto,
 			proto,
 			daemon,
 			daemon,
 			backend,
 			backend,
@@ -47,7 +47,8 @@ class EthereumRPCClient(RPCClient,metaclass=AsyncInit):
 		self.call_sigs = getattr(CallSigs,daemon.id,None)
 		self.call_sigs = getattr(CallSigs,daemon.id,None)
 
 
 		super().__init__(
 		super().__init__(
-			host = 'localhost' if g.test_suite else (g.rpc_host or 'localhost'),
+			cfg  = cfg,
+			host = 'localhost' if cfg.test_suite else (cfg.rpc_host or 'localhost'),
 			port = daemon.rpc_port )
 			port = daemon.rpc_port )
 
 
 		await self.set_backend_async(backend)
 		await self.set_backend_async(backend)

+ 3 - 3
mmgen/proto/eth/tw/bal.py

@@ -30,9 +30,9 @@ class EthereumTwGetBalance(TwGetBalance):
 		'ge_minconf': 'Balance',
 		'ge_minconf': 'Balance',
 	}
 	}
 
 
-	async def __init__(self,proto,*args,**kwargs):
-		self.twctl = await TwCtl(proto,mode='w')
-		await super().__init__(proto,*args,**kwargs)
+	async def __init__(self,cfg,proto,*args,**kwargs):
+		self.twctl = await TwCtl(cfg,proto,mode='w')
+		await super().__init__(cfg,proto,*args,**kwargs)
 
 
 	async def create_data(self):
 	async def create_data(self):
 		in_data = self.twctl.mmid_ordered_dict
 		in_data = self.twctl.mmid_ordered_dict

+ 4 - 4
mmgen/proto/eth/tw/ctl.py

@@ -172,9 +172,9 @@ class EthereumTokenTwCtl(EthereumTwCtl):
 	symbol = None
 	symbol = None
 	cur_eth_balances = {}
 	cur_eth_balances = {}
 
 
-	async def __init__(self,proto,mode='r',token_addr=None):
+	async def __init__(self,cfg,proto,mode='r',token_addr=None):
 
 
-		await super().__init__(proto,mode=mode)
+		await super().__init__(cfg,proto,mode=mode)
 
 
 		for v in self.data['tokens'].values():
 		for v in self.data['tokens'].values():
 			self.conv_types(v)
 			self.conv_types(v)
@@ -211,7 +211,7 @@ class EthereumTokenTwCtl(EthereumTwCtl):
 		return 'token ' + self.get_param('symbol')
 		return 'token ' + self.get_param('symbol')
 
 
 	async def rpc_get_balance(self,addr):
 	async def rpc_get_balance(self,addr):
-		return await Token(self.proto,self.token,self.decimals,self.rpc).get_balance(addr)
+		return await Token(self.cfg,self.proto,self.token,self.decimals,self.rpc).get_balance(addr)
 
 
 	async def get_eth_balance(self,addr,force_rpc=False):
 	async def get_eth_balance(self,addr,force_rpc=False):
 		cache = self.cur_eth_balances
 		cache = self.cur_eth_balances
@@ -232,7 +232,7 @@ class EthereumTokenTwCtl(EthereumTwCtl):
 		once, upon token import.  Thereafter, token address, symbol and decimals are resolved
 		once, upon token import.  Thereafter, token address, symbol and decimals are resolved
 		either from the tracking wallet (online operations) or transaction file (when signing).
 		either from the tracking wallet (online operations) or transaction file (when signing).
 		"""
 		"""
-		t = await TokenResolve(self.proto,self.rpc,tokenaddr)
+		t = await TokenResolve(self.cfg,self.proto,self.rpc,tokenaddr)
 		self.data['tokens'][tokenaddr] = {
 		self.data['tokens'][tokenaddr] = {
 			'params': {
 			'params': {
 				'symbol': await t.get_symbol(),
 				'symbol': await t.get_symbol(),

+ 1 - 2
mmgen/proto/eth/tw/view.py

@@ -12,7 +12,6 @@
 proto.eth.tw.view: Ethereum base protocol base class for tracking wallet view classes
 proto.eth.tw.view: Ethereum base protocol base class for tracking wallet view classes
 """
 """
 
 
-from ....globalvars import g
 from ....tw.view import TwView
 from ....tw.view import TwView
 
 
 class EthereumTwView(TwView):
 class EthereumTwView(TwView):
@@ -26,6 +25,6 @@ class EthereumTwView(TwView):
 	def gen_subheader(self,cw,color):
 	def gen_subheader(self,cw,color):
 		if self.disp_prec == 8:
 		if self.disp_prec == 8:
 			yield 'Balances truncated to 8 decimal points'
 			yield 'Balances truncated to 8 decimal points'
-		if g.cached_balances:
+		if self.cfg.cached_balances:
 			from ....color import nocolor,yellow
 			from ....color import nocolor,yellow
 			yield (nocolor,yellow)[color]('WARNING: Using cached balances. These may be out of date!')
 			yield (nocolor,yellow)[color]('WARNING: Using cached balances. These may be out of date!')

+ 1 - 2
mmgen/proto/eth/tx/base.py

@@ -16,7 +16,6 @@ from collections import namedtuple
 
 
 import mmgen.tx.base as TxBase
 import mmgen.tx.base as TxBase
 from ....obj import HexStr,Int
 from ....obj import HexStr,Int
-from ....util import dmsg
 
 
 class Base(TxBase.Base):
 class Base(TxBase.Base):
 
 
@@ -33,7 +32,7 @@ class Base(TxBase.Base):
 	# given absolute fee in ETH, return gas price in Gwei using self.gas
 	# given absolute fee in ETH, return gas price in Gwei using self.gas
 	def fee_abs2rel(self,abs_fee,to_unit='Gwei'):
 	def fee_abs2rel(self,abs_fee,to_unit='Gwei'):
 		ret = self.proto.coin_amt(int(abs_fee.toWei() // self.gas.toWei()),'wei')
 		ret = self.proto.coin_amt(int(abs_fee.toWei() // self.gas.toWei()),'wei')
-		dmsg(f'fee_abs2rel() ==> {ret} ETH')
+		self.cfg._util.dmsg(f'fee_abs2rel() ==> {ret} ETH')
 		return ret if to_unit == 'eth' else ret.to_unit(to_unit,show_decimal=True)
 		return ret if to_unit == 'eth' else ret.to_unit(to_unit,show_decimal=True)
 
 
 	# given rel fee (gasPrice) in wei, return absolute fee using self.gas (Ethereum-only method)
 	# given rel fee (gasPrice) in wei, return absolute fee using self.gas (Ethereum-only method)

+ 12 - 13
mmgen/proto/eth/tx/new.py

@@ -16,7 +16,6 @@ import json
 
 
 import mmgen.tx.new as TxBase
 import mmgen.tx.new as TxBase
 from .base import Base,TokenBase
 from .base import Base,TokenBase
-from ....opts import opt
 from ....obj import Int,ETHNonce,MMGenTxID,Str,HexStr
 from ....obj import Int,ETHNonce,MMGenTxID,Str,HexStr
 from ....util import msg,is_int,is_hex_str,make_chksum_6
 from ....util import msg,is_int,is_hex_str,make_chksum_6
 from ....tw.ctl import TwCtl
 from ....tw.ctl import TwCtl
@@ -34,16 +33,16 @@ class New(Base,TxBase.New):
 
 
 		super().__init__(*args,**kwargs)
 		super().__init__(*args,**kwargs)
 
 
-		if opt.gas:
-			self.gas = self.start_gas = self.proto.coin_amt(int(opt.gas),'wei')
+		if self.cfg.gas:
+			self.gas = self.start_gas = self.proto.coin_amt(int(self.cfg.gas),'wei')
 		else:
 		else:
 			self.gas = self.proto.coin_amt(self.dfl_gas,'wei')
 			self.gas = self.proto.coin_amt(self.dfl_gas,'wei')
 			self.start_gas = self.proto.coin_amt(self.dfl_start_gas,'wei')
 			self.start_gas = self.proto.coin_amt(self.dfl_start_gas,'wei')
 
 
-		if opt.contract_data:
+		if self.cfg.contract_data:
 			m = "'--contract-data' option may not be used with token transaction"
 			m = "'--contract-data' option may not be used with token transaction"
 			assert not 'Token' in type(self).__name__, m
 			assert not 'Token' in type(self).__name__, m
-			with open(opt.contract_data) as fp:
+			with open(self.cfg.contract_data) as fp:
 				self.usr_contract_data = HexStr(fp.read().strip())
 				self.usr_contract_data = HexStr(fp.read().strip())
 			self.disable_fee_check = True
 			self.disable_fee_check = True
 
 
@@ -93,7 +92,7 @@ class New(Base,TxBase.New):
 	def select_unspent(self,unspent):
 	def select_unspent(self,unspent):
 		from ....ui import line_input
 		from ....ui import line_input
 		while True:
 		while True:
-			reply = line_input('Enter an account to spend from: ').strip()
+			reply = line_input( self.cfg, 'Enter an account to spend from: ' ).strip()
 			if reply:
 			if reply:
 				if not is_int(reply):
 				if not is_int(reply):
 					msg('Account number must be an integer')
 					msg('Account number must be an integer')
@@ -119,10 +118,10 @@ class New(Base,TxBase.New):
 			from_unit='wei'
 			from_unit='wei'
 		)
 		)
 
 
-	# given fee estimate (gas price) in wei, return absolute fee, adjusting by opt.fee_adjust
+	# given fee estimate (gas price) in wei, return absolute fee, adjusting by self.cfg.fee_adjust
 	def fee_est2abs(self,rel_fee,fe_type=None):
 	def fee_est2abs(self,rel_fee,fe_type=None):
-		ret = self.fee_gasPrice2abs(rel_fee) * opt.fee_adjust
-		if opt.verbose:
+		ret = self.fee_gasPrice2abs(rel_fee) * self.cfg.fee_adjust
+		if self.cfg.verbose:
 			msg(f'Estimated fee: {ret} ETH')
 			msg(f'Estimated fee: {ret} ETH')
 		return ret
 		return ret
 
 
@@ -146,10 +145,10 @@ class New(Base,TxBase.New):
 
 
 	async def get_input_addrs_from_cmdline(self):
 	async def get_input_addrs_from_cmdline(self):
 		ret = []
 		ret = []
-		if opt.inputs:
-			data_root = (await TwCtl(self.proto)).data_root # must create new instance here
+		if self.cfg.inputs:
+			data_root = (await TwCtl(self.cfg,self.proto)).data_root # must create new instance here
 			errmsg = 'Address {!r} not in tracking wallet'
 			errmsg = 'Address {!r} not in tracking wallet'
-			for addr in opt.inputs.split(','):
+			for addr in self.cfg.inputs.split(','):
 				if is_mmgen_id(self.proto,addr):
 				if is_mmgen_id(self.proto,addr):
 					for waddr in data_root:
 					for waddr in data_root:
 						if data_root[waddr]['mmid'] == addr:
 						if data_root[waddr]['mmid'] == addr:
@@ -178,7 +177,7 @@ class TokenNew(TokenBase,New):
 
 
 	async def make_txobj(self): # called by create_serialized()
 	async def make_txobj(self): # called by create_serialized()
 		await super().make_txobj()
 		await super().make_txobj()
-		t = Token(self.proto,self.twctl.token,self.twctl.decimals)
+		t = Token(self.cfg,self.proto,self.twctl.token,self.twctl.decimals)
 		o = self.txobj
 		o = self.txobj
 		o['token_addr'] = t.addr
 		o['token_addr'] = t.addr
 		o['decimals'] = t.decimals
 		o['decimals'] = t.decimals

+ 3 - 5
mmgen/proto/eth/tx/online.py

@@ -12,8 +12,6 @@
 proto.eth.tx.online: Ethereum online signed transaction class
 proto.eth.tx.online: Ethereum online signed transaction class
 """
 """
 
 
-from ....globalvars import *
-
 import mmgen.tx.online as TxBase
 import mmgen.tx.online as TxBase
 from .signed import Signed,TokenSigned
 from .signed import Signed,TokenSigned
 from .. import erigon_sleep
 from .. import erigon_sleep
@@ -37,7 +35,7 @@ class OnlineSigned(Signed,TxBase.OnlineSigned):
 		if prompt_user:
 		if prompt_user:
 			self.confirm_send()
 			self.confirm_send()
 
 
-		if g.bogus_send:
+		if self.cfg.bogus_send:
 			ret = None
 			ret = None
 		else:
 		else:
 			try:
 			try:
@@ -52,7 +50,7 @@ class OnlineSigned(Signed,TxBase.OnlineSigned):
 				sys.exit(1)
 				sys.exit(1)
 			return False
 			return False
 		else:
 		else:
-			if g.bogus_send:
+			if self.cfg.bogus_send:
 				m = 'BOGUS transaction NOT sent: {}'
 				m = 'BOGUS transaction NOT sent: {}'
 			else:
 			else:
 				m = 'Transaction sent: {}'
 				m = 'Transaction sent: {}'
@@ -78,6 +76,6 @@ class TokenOnlineSigned(TokenSigned,OnlineSigned):
 		assert self.twctl.token == o['to']
 		assert self.twctl.token == o['to']
 		o['token_addr'] = TokenAddr(self.proto,o['to'])
 		o['token_addr'] = TokenAddr(self.proto,o['to'])
 		o['decimals']   = self.twctl.decimals
 		o['decimals']   = self.twctl.decimals
-		t = Token(self.proto,o['token_addr'],o['decimals'])
+		t = Token(self.cfg,self.proto,o['token_addr'],o['decimals'])
 		o['amt'] = t.transferdata2amt(o['data'])
 		o['amt'] = t.transferdata2amt(o['data'])
 		o['token_to'] = t.transferdata2sendaddr(o['data'])
 		o['token_to'] = t.transferdata2sendaddr(o['data'])

+ 3 - 3
mmgen/proto/eth/tx/unsigned.py

@@ -79,7 +79,7 @@ class Unsigned(Completed,TxBase.Unsigned):
 			await self.do_sign(keys[0].sec.wif,tx_num_str)
 			await self.do_sign(keys[0].sec.wif,tx_num_str)
 			msg('OK')
 			msg('OK')
 			from ....tx import SignedTX
 			from ....tx import SignedTX
-			return await SignedTX(data=self.__dict__)
+			return await SignedTX(cfg=self.cfg,data=self.__dict__)
 		except Exception as e:
 		except Exception as e:
 			msg(f'{e}: transaction signing failed!')
 			msg(f'{e}: transaction signing failed!')
 			return False
 			return False
@@ -92,12 +92,12 @@ class TokenUnsigned(TokenCompleted,Unsigned):
 		o = self.txobj
 		o = self.txobj
 		o['token_addr'] = TokenAddr(self.proto,d['token_addr'])
 		o['token_addr'] = TokenAddr(self.proto,d['token_addr'])
 		o['decimals'] = Int(d['decimals'])
 		o['decimals'] = Int(d['decimals'])
-		t = Token(self.proto,o['token_addr'],o['decimals'])
+		t = Token(self.cfg,self.proto,o['token_addr'],o['decimals'])
 		o['data'] = t.create_data(o['to'],o['amt'])
 		o['data'] = t.create_data(o['to'],o['amt'])
 		o['token_to'] = t.transferdata2sendaddr(o['data'])
 		o['token_to'] = t.transferdata2sendaddr(o['data'])
 
 
 	async def do_sign(self,wif,tx_num_str):
 	async def do_sign(self,wif,tx_num_str):
 		o = self.txobj
 		o = self.txobj
-		t = Token(self.proto,o['token_addr'],o['decimals'])
+		t = Token(self.cfg,self.proto,o['token_addr'],o['decimals'])
 		tx_in = t.make_tx_in(o['from'],o['to'],o['amt'],self.start_gas,o['gasPrice'],nonce=o['nonce'])
 		tx_in = t.make_tx_in(o['from'],o['to'],o['amt'],self.start_gas,o['gasPrice'],nonce=o['nonce'])
 		(self.serialized,self.coin_txid) = await t.txsign(tx_in,wif,o['from'],chain_id=o['chainId'])
 		(self.serialized,self.coin_txid) = await t.txsign(tx_in,wif,o['from'],chain_id=o['chainId'])

+ 3 - 4
mmgen/proto/secp256k1/keygen.py

@@ -19,7 +19,7 @@ class backend:
 
 
 	class libsecp256k1(keygen_base):
 	class libsecp256k1(keygen_base):
 
 
-		def __init__(self):
+		def __init__(self,cfg):
 			from .secp256k1 import priv2pub
 			from .secp256k1 import priv2pub
 			self.priv2pub = priv2pub
 			self.priv2pub = priv2pub
 
 
@@ -41,13 +41,12 @@ class backend:
 				if not silent:
 				if not silent:
 					from ...util import ymsg
 					from ...util import ymsg
 					ymsg(str(e))
 					ymsg(str(e))
-				from ...util import qmsg
-				qmsg('Using (slow) native Python ECDSA library for public key generation')
+				self.cfg._util.qmsg('Using (slow) native Python ECDSA library for public key generation')
 				return 'python_ecdsa'
 				return 'python_ecdsa'
 
 
 	class python_ecdsa(keygen_base):
 	class python_ecdsa(keygen_base):
 
 
-		def __init__(self):
+		def __init__(self,cfg):
 			import ecdsa
 			import ecdsa
 			self.ecdsa = ecdsa
 			self.ecdsa = ecdsa
 
 

+ 9 - 6
mmgen/proto/xmr/daemon.py

@@ -14,8 +14,7 @@ proto.xmr.daemon: Monero base protocol daemon classes
 
 
 import os
 import os
 
 
-from ...globalvars import g,gc
-from ...opts import opt
+from ...globalvars import gc
 from ...util import list_gen,die,contains_any
 from ...util import list_gen,die,contains_any
 from ...daemon import CoinDaemon,RPCDaemon,_nw,_dd
 from ...daemon import CoinDaemon,RPCDaemon,_nw,_dd
 
 
@@ -46,6 +45,7 @@ class monero_daemon(CoinDaemon):
 
 
 		from .rpc import MoneroRPCClient
 		from .rpc import MoneroRPCClient
 		self.rpc = MoneroRPCClient(
 		self.rpc = MoneroRPCClient(
+			cfg    = self.cfg,
 			proto  = self.proto,
 			proto  = self.proto,
 			host   = self.host,
 			host   = self.host,
 			port   = self.rpc_port,
 			port   = self.rpc_port,
@@ -91,6 +91,7 @@ class MoneroWalletDaemon(RPCDaemon):
 
 
 	def __init__(
 	def __init__(
 			self,
 			self,
+			cfg,
 			proto,
 			proto,
 			wallet_dir,
 			wallet_dir,
 			test_suite  = False,
 			test_suite  = False,
@@ -105,7 +106,7 @@ class MoneroWalletDaemon(RPCDaemon):
 		self.proto = proto
 		self.proto = proto
 		self.test_suite = test_suite
 		self.test_suite = test_suite
 
 
-		super().__init__()
+		super().__init__(cfg)
 
 
 		self.network = proto.network
 		self.network = proto.network
 		self.wallet_dir = wallet_dir
 		self.wallet_dir = wallet_dir
@@ -123,13 +124,14 @@ class MoneroWalletDaemon(RPCDaemon):
 		self.daemon_port = (
 		self.daemon_port = (
 			None if daemon_addr else
 			None if daemon_addr else
 			CoinDaemon(
 			CoinDaemon(
+				cfg        = self.cfg,
 				proto      = proto,
 				proto      = proto,
 				test_suite = test_suite).rpc_port
 				test_suite = test_suite).rpc_port
 		)
 		)
 
 
-		self.host = host or opt.wallet_rpc_host or g.monero_wallet_rpc_host
-		self.user = user or opt.wallet_rpc_user or g.monero_wallet_rpc_user
-		self.passwd = passwd or opt.wallet_rpc_password or g.monero_wallet_rpc_password
+		self.host = host or self.cfg.wallet_rpc_host or self.cfg.monero_wallet_rpc_host
+		self.user = user or self.cfg.wallet_rpc_user or self.cfg.monero_wallet_rpc_user
+		self.passwd = passwd or self.cfg.wallet_rpc_password or self.cfg.monero_wallet_rpc_password
 
 
 		assert self.host
 		assert self.host
 		assert self.user
 		assert self.user
@@ -157,5 +159,6 @@ class MoneroWalletDaemon(RPCDaemon):
 
 
 		from .rpc import MoneroWalletRPCClient
 		from .rpc import MoneroWalletRPCClient
 		self.rpc = MoneroWalletRPCClient(
 		self.rpc = MoneroWalletRPCClient(
+			cfg             = self.cfg,
 			daemon          = self,
 			daemon          = self,
 			test_connection = False )
 			test_connection = False )

+ 8 - 8
mmgen/proto/xmr/keygen.py

@@ -19,13 +19,13 @@ class backend:
 
 
 	class base(keygen_base):
 	class base(keygen_base):
 
 
-		def __init__(self):
+		def __init__(self,cfg):
 
 
 			from ...proto.xmr.params import mainnet
 			from ...proto.xmr.params import mainnet
 			self.proto_cls = mainnet
 			self.proto_cls = mainnet
 
 
 			from ...util2 import get_keccak
 			from ...util2 import get_keccak
-			self.keccak_256 = get_keccak()
+			self.keccak_256 = get_keccak(cfg)
 
 
 		def to_viewkey(self,privkey):
 		def to_viewkey(self,privkey):
 			return self.proto_cls.preprocess_key(
 			return self.proto_cls.preprocess_key(
@@ -35,8 +35,8 @@ class backend:
 
 
 	class nacl(base):
 	class nacl(base):
 
 
-		def __init__(self):
-			super().__init__()
+		def __init__(self,cfg):
+			super().__init__(cfg)
 			from nacl.bindings import crypto_scalarmult_ed25519_base_noclamp
 			from nacl.bindings import crypto_scalarmult_ed25519_base_noclamp
 			self.scalarmultbase = crypto_scalarmult_ed25519_base_noclamp
 			self.scalarmultbase = crypto_scalarmult_ed25519_base_noclamp
 
 
@@ -49,8 +49,8 @@ class backend:
 
 
 	class ed25519(base):
 	class ed25519(base):
 
 
-		def __init__(self):
-			super().__init__()
+		def __init__(self,cfg):
+			super().__init__(cfg)
 			from ...contrib.ed25519 import edwards,encodepoint,B,scalarmult
 			from ...contrib.ed25519 import edwards,encodepoint,B,scalarmult
 			self.edwards     = edwards
 			self.edwards     = edwards
 			self.encodepoint = encodepoint
 			self.encodepoint = encodepoint
@@ -85,7 +85,7 @@ class backend:
 
 
 	class ed25519ll_djbec(ed25519):
 	class ed25519ll_djbec(ed25519):
 
 
-		def __init__(self):
-			super().__init__()
+		def __init__(self,cfg):
+			super().__init__(cfg)
 			from ...contrib.ed25519ll_djbec import scalarmult
 			from ...contrib.ed25519ll_djbec import scalarmult
 			self.scalarmult = scalarmult
 			self.scalarmult = scalarmult

+ 5 - 4
mmgen/proto/xmr/rpc.py

@@ -13,7 +13,6 @@ proto.xmr.rpc: Monero base protocol RPC client class
 """
 """
 
 
 import re
 import re
-from ...globalvars import g
 from ...rpc import RPCClient,IPPort,auth_data
 from ...rpc import RPCClient,IPPort,auth_data
 
 
 class MoneroRPCClient(RPCClient):
 class MoneroRPCClient(RPCClient):
@@ -24,6 +23,7 @@ class MoneroRPCClient(RPCClient):
 
 
 	def __init__(
 	def __init__(
 			self,
 			self,
+			cfg,
 			proto,
 			proto,
 			host,
 			host,
 			port,
 			port,
@@ -42,7 +42,7 @@ class MoneroRPCClient(RPCClient):
 			if host.endswith('.onion'):
 			if host.endswith('.onion'):
 				self.network_proto = 'http'
 				self.network_proto = 'http'
 
 
-		super().__init__(host,port,test_connection)
+		super().__init__(cfg,host,port,test_connection)
 
 
 		if self.auth_type:
 		if self.auth_type:
 			self.auth = auth_data(user,passwd)
 			self.auth = auth_data(user,passwd)
@@ -69,7 +69,7 @@ class MoneroRPCClient(RPCClient):
 				if self.daemon and self.daemon_version > self.daemon.coind_version:
 				if self.daemon and self.daemon_version > self.daemon.coind_version:
 					self.handle_unsupported_daemon_version(
 					self.handle_unsupported_daemon_version(
 						proto.name,
 						proto.name,
-						ignore_daemon_version or proto.ignore_daemon_version or g.ignore_daemon_version )
+						ignore_daemon_version or proto.ignore_daemon_version or self.cfg.ignore_daemon_version )
 			else: # restricted (public) node:
 			else: # restricted (public) node:
 				self.daemon_version_str = None
 				self.daemon_version_str = None
 				self.daemon_version = None
 				self.daemon_version = None
@@ -100,10 +100,11 @@ class MoneroWalletRPCClient(MoneroRPCClient):
 
 
 	auth_type = 'digest'
 	auth_type = 'digest'
 
 
-	def __init__(self,daemon,test_connection=True):
+	def __init__(self,cfg,daemon,test_connection=True):
 
 
 		RPCClient.__init__(
 		RPCClient.__init__(
 			self,
 			self,
+			cfg,
 			daemon.host,
 			daemon.host,
 			daemon.rpc_port,
 			daemon.rpc_port,
 			test_connection = test_connection )
 			test_connection = test_connection )

+ 1 - 1
mmgen/proto/zec/keygen.py

@@ -19,7 +19,7 @@ class backend:
 
 
 	class nacl(keygen_base):
 	class nacl(keygen_base):
 
 
-		def __init__(self):
+		def __init__(self,cfg):
 			from nacl.bindings import crypto_scalarmult_base
 			from nacl.bindings import crypto_scalarmult_base
 			self.crypto_scalarmult_base = crypto_scalarmult_base
 			self.crypto_scalarmult_base = crypto_scalarmult_base
 			from ...sha2 import Sha256
 			from ...sha2 import Sha256

+ 1 - 2
mmgen/proto/zec/params.py

@@ -32,8 +32,7 @@ class mainnet(mainnet):
 
 
 	def __init__(self,*args,**kwargs):
 	def __init__(self,*args,**kwargs):
 		super().__init__(*args,**kwargs)
 		super().__init__(*args,**kwargs)
-		from ...opts import opt
-		self.coin_id = 'ZEC-Z' if opt.type in ('zcash_z','Z') else 'ZEC-T'
+		self.coin_id = 'ZEC-Z' if self.cfg.type in ('zcash_z','Z') else 'ZEC-T'
 
 
 	def get_wif_ver_bytes_len(self,key_data):
 	def get_wif_ver_bytes_len(self,key_data):
 		"""
 		"""

+ 21 - 15
mmgen/protocol.py

@@ -22,7 +22,7 @@ protocol: Coin protocol base classes and initializer
 
 
 from collections import namedtuple
 from collections import namedtuple
 
 
-from .globalvars import g,gc
+from .globalvars import gc
 from .objmethods import MMGenObject
 from .objmethods import MMGenObject
 
 
 decoded_wif = namedtuple('decoded_wif',['sec','pubkey_type','compressed'])
 decoded_wif = namedtuple('decoded_wif',['sec','pubkey_type','compressed'])
@@ -54,7 +54,8 @@ class CoinProtocol(MMGenObject):
 		is_fork_of = None
 		is_fork_of = None
 		networks   = ('mainnet','testnet','regtest')
 		networks   = ('mainnet','testnet','regtest')
 
 
-		def __init__(self,coin,name,network,tokensym=None,need_amt=False):
+		def __init__(self,cfg,coin,name,network,tokensym=None,need_amt=False):
+			self.cfg        = cfg
 			self.coin       = coin.upper()
 			self.coin       = coin.upper()
 			self.coin_id    = self.coin
 			self.coin_id    = self.coin
 			self.name       = name
 			self.name       = name
@@ -96,7 +97,7 @@ class CoinProtocol(MMGenObject):
 
 
 			if self.base_coin in ('ETH','XMR'):
 			if self.base_coin in ('ETH','XMR'):
 				from .util2 import get_keccak
 				from .util2 import get_keccak
-				self.keccak_256 = get_keccak()
+				self.keccak_256 = get_keccak(cfg)
 
 
 			if need_amt:
 			if need_amt:
 				import mmgen.amt
 				import mmgen.amt
@@ -111,7 +112,7 @@ class CoinProtocol(MMGenObject):
 			return self.coin
 			return self.coin
 
 
 		@classmethod
 		@classmethod
-		def chain_name_to_network(cls,coin,chain_name):
+		def chain_name_to_network(cls,cfg,coin,chain_name):
 			"""
 			"""
 			The generic networks 'mainnet', 'testnet' and 'regtest' are required for all coins
 			The generic networks 'mainnet', 'testnet' and 'regtest' are required for all coins
 			that support transaction operations.
 			that support transaction operations.
@@ -121,7 +122,7 @@ class CoinProtocol(MMGenObject):
 			For Bitcoin and Bitcoin forks, 'network' and 'chain_name' are equivalent.
 			For Bitcoin and Bitcoin forks, 'network' and 'chain_name' are equivalent.
 			"""
 			"""
 			for network in ('mainnet','testnet','regtest'):
 			for network in ('mainnet','testnet','regtest'):
-				proto = init_proto(coin,network=network)
+				proto = init_proto( cfg, coin, network=network )
 				for proto_chain_name in proto.chain_names:
 				for proto_chain_name in proto.chain_names:
 					if chain_name == proto_chain_name:
 					if chain_name == proto_chain_name:
 						return network
 						return network
@@ -211,7 +212,7 @@ class CoinProtocol(MMGenObject):
 				elif pk == self.secp256k1_ge: # ditto
 				elif pk == self.secp256k1_ge: # ditto
 					die(4,'Private key == secp256k1_ge!')
 					die(4,'Private key == secp256k1_ge!')
 				else:
 				else:
-					if not g.test_suite:
+					if not self.cfg.test_suite:
 						ymsg(f'Warning: private key is greater than secp256k1 group order!:\n  {hexpriv}')
 						ymsg(f'Warning: private key is greater than secp256k1 group order!:\n  {hexpriv}')
 					return (pk % self.secp256k1_ge).to_bytes(self.privkey_len,'big')
 					return (pk % self.secp256k1_ge).to_bytes(self.privkey_len,'big')
 
 
@@ -231,6 +232,7 @@ class CoinProtocol(MMGenObject):
 				compressed  = False )
 				compressed  = False )
 
 
 def init_proto(
 def init_proto(
+		cfg,
 		coin       = None,
 		coin       = None,
 		testnet    = False,
 		testnet    = False,
 		regtest    = False,
 		regtest    = False,
@@ -271,20 +273,24 @@ def init_proto(
 		)
 		)
 
 
 	return getattr(CoinProtocol,proto_name)(
 	return getattr(CoinProtocol,proto_name)(
+		cfg       = cfg,
 		coin      = coin,
 		coin      = coin,
 		name      = name,
 		name      = name,
 		network   = network,
 		network   = network,
 		tokensym  = tokensym,
 		tokensym  = tokensym,
 		need_amt  = need_amt )
 		need_amt  = need_amt )
 
 
-def init_proto_from_opts(need_amt=False):
+def init_proto_from_cfg(cfg,need_amt):
 	return init_proto(
 	return init_proto(
-		coin      = g.coin,
-		network   = g.network,
-		tokensym  = g.token,
+		cfg       = cfg,
+		coin      = cfg.coin,
+		network   = cfg.network,
+		tokensym  = cfg.token,
 		need_amt  = need_amt )
 		need_amt  = need_amt )
 
 
-def warn_trustlevel(coinsym):
+def warn_trustlevel(cfg):
+
+	coinsym = cfg.coin
 
 
 	if coinsym.lower() in CoinProtocol.coins:
 	if coinsym.lower() in CoinProtocol.coins:
 		trust_level = CoinProtocol.coins[coinsym.lower()].trust_level
 		trust_level = CoinProtocol.coins[coinsym.lower()].trust_level
@@ -306,7 +312,7 @@ def warn_trustlevel(coinsym):
 		Are you sure you want to continue?
 		Are you sure you want to continue?
 	"""
 	"""
 
 
-	from .util import qmsg,fmt
+	from .util import fmt
 	from .color import red,yellow,green
 	from .color import red,yellow,green
 
 
 	warning = fmt(m).strip().format(
 	warning = fmt(m).strip().format(
@@ -319,11 +325,11 @@ def warn_trustlevel(coinsym):
 		}[trust_level],
 		}[trust_level],
 		p = gc.proj_name )
 		p = gc.proj_name )
 
 
-	if g.test_suite:
-		qmsg(warning)
+	if cfg.test_suite:
+		cfg._util.qmsg(warning)
 		return
 		return
 
 
 	from .ui import keypress_confirm
 	from .ui import keypress_confirm
-	if not keypress_confirm(warning,default_yes=True):
+	if not keypress_confirm( cfg, warning, default_yes=True ):
 		import sys
 		import sys
 		sys.exit(0)
 		sys.exit(0)

+ 16 - 10
mmgen/rpc.py

@@ -20,11 +20,12 @@
 rpc: Cryptocoin RPC library for the MMGen suite
 rpc: Cryptocoin RPC library for the MMGen suite
 """
 """
 
 
-import base64,json,asyncio,importlib
+import re,base64,json,asyncio,importlib
 from decimal import Decimal
 from decimal import Decimal
 from collections import namedtuple
 from collections import namedtuple
 
 
-from .common import *
+from .globalvars import gc
+from .util import msg,die,fmt,fmt_list,pp_fmt
 from .base_obj import AsyncInit
 from .base_obj import AsyncInit
 from .obj import NonNegativeInt
 from .obj import NonNegativeInt
 from .objmethods import Hilite,InitErrors,MMGenObject
 from .objmethods import Hilite,InitErrors,MMGenObject
@@ -85,6 +86,7 @@ class RPCBackends:
 	class base:
 	class base:
 
 
 		def __init__(self,caller):
 		def __init__(self,caller):
+			self.cfg            = caller.cfg
 			self.host           = caller.host
 			self.host           = caller.host
 			self.port           = caller.port
 			self.port           = caller.port
 			self.proxy          = caller.proxy
 			self.proxy          = caller.proxy
@@ -109,7 +111,7 @@ class RPCBackends:
 		async def __init__(self,caller):
 		async def __init__(self,caller):
 			super().__init__(caller)
 			super().__init__(caller)
 			import aiohttp
 			import aiohttp
-			self.connector = aiohttp.TCPConnector(limit_per_host=g.aiohttp_rpc_queue_len)
+			self.connector = aiohttp.TCPConnector(limit_per_host=self.cfg.aiohttp_rpc_queue_len)
 			self.session = aiohttp.ClientSession(
 			self.session = aiohttp.ClientSession(
 				headers = { 'Content-Type': 'application/json' },
 				headers = { 'Content-Type': 'application/json' },
 				connector = self.connector,
 				connector = self.connector,
@@ -261,14 +263,16 @@ class RPCClient(MMGenObject):
 	network_proto = 'http'
 	network_proto = 'http'
 	proxy = None
 	proxy = None
 
 
-	def __init__(self,host,port,test_connection=True):
+	def __init__(self,cfg,host,port,test_connection=True):
+
+		self.cfg = cfg
 
 
 		# aiohttp workaround, and may speed up RPC performance overall on some systems:
 		# aiohttp workaround, and may speed up RPC performance overall on some systems:
 		if gc.platform == 'win' and host == 'localhost':
 		if gc.platform == 'win' and host == 'localhost':
 			host = '127.0.0.1'
 			host = '127.0.0.1'
 
 
 		global dmsg_rpc,dmsg_rpc_backend
 		global dmsg_rpc,dmsg_rpc_backend
-		if not g.debug_rpc:
+		if not self.cfg.debug_rpc:
 			dmsg_rpc = dmsg_rpc_backend = noop
 			dmsg_rpc = dmsg_rpc_backend = noop
 
 
 		dmsg_rpc(f'=== {type(self).__name__}.__init__() debug ===')
 		dmsg_rpc(f'=== {type(self).__name__}.__init__() debug ===')
@@ -285,11 +289,11 @@ class RPCClient(MMGenObject):
 		self.host_url = f'{self.network_proto}://{host}:{port}'
 		self.host_url = f'{self.network_proto}://{host}:{port}'
 		self.host = host
 		self.host = host
 		self.port = port
 		self.port = port
-		self.timeout = g.http_timeout
+		self.timeout = self.cfg.http_timeout
 		self.auth = None
 		self.auth = None
 
 
 	def _get_backend(self,backend):
 	def _get_backend(self,backend):
-		backend_id = backend or opt.rpc_backend
+		backend_id = backend or self.cfg.rpc_backend
 		if backend_id == 'auto':
 		if backend_id == 'auto':
 			return {'linux':RPCBackends.httplib,'win':RPCBackends.requests}[gc.platform](self)
 			return {'linux':RPCBackends.httplib,'win':RPCBackends.requests}[gc.platform](self)
 		else:
 		else:
@@ -443,6 +447,7 @@ class RPCClient(MMGenObject):
 				""",indent='    '))
 				""",indent='    '))
 
 
 async def rpc_init(
 async def rpc_init(
+		cfg,
 		proto,
 		proto,
 		backend               = None,
 		backend               = None,
 		daemon                = None,
 		daemon                = None,
@@ -458,15 +463,16 @@ async def rpc_init(
 
 
 	from .daemon import CoinDaemon
 	from .daemon import CoinDaemon
 	rpc = await cls(
 	rpc = await cls(
+		cfg           = cfg,
 		proto         = proto,
 		proto         = proto,
-		daemon        = daemon or CoinDaemon(proto=proto,test_suite=g.test_suite),
-		backend       = backend or opt.rpc_backend,
+		daemon        = daemon or CoinDaemon(cfg,proto=proto,test_suite=cfg.test_suite),
+		backend       = backend or cfg.rpc_backend,
 		ignore_wallet = ignore_wallet )
 		ignore_wallet = ignore_wallet )
 
 
 	if rpc.daemon_version > rpc.daemon.coind_version:
 	if rpc.daemon_version > rpc.daemon.coind_version:
 		rpc.handle_unsupported_daemon_version(
 		rpc.handle_unsupported_daemon_version(
 			proto.name,
 			proto.name,
-			ignore_daemon_version or proto.ignore_daemon_version or g.ignore_daemon_version )
+			ignore_daemon_version or proto.ignore_daemon_version or cfg.ignore_daemon_version )
 
 
 	if rpc.chain not in proto.chain_names:
 	if rpc.chain not in proto.chain_names:
 		die( 'RPCChainMismatch', '\n' + fmt(f"""
 		die( 'RPCChainMismatch', '\n' + fmt(f"""

+ 6 - 6
mmgen/seed.py

@@ -54,19 +54,20 @@ class SeedBase(MMGenObject):
 	data = ImmutableAttr(bytes,typeconv=False)
 	data = ImmutableAttr(bytes,typeconv=False)
 	sid  = ImmutableAttr(SeedID,typeconv=False)
 	sid  = ImmutableAttr(SeedID,typeconv=False)
 
 
-	def __init__(self,seed_bin=None,nSubseeds=None):
+	def __init__(self,cfg,seed_bin=None,nSubseeds=None):
+
 		if not seed_bin:
 		if not seed_bin:
-			from .opts import opt
 			from .crypto import Crypto
 			from .crypto import Crypto
 			from hashlib import sha256
 			from hashlib import sha256
 			# Truncate random data for smaller seed lengths
 			# Truncate random data for smaller seed lengths
-			seed_bin = sha256(Crypto().get_random(1033)).digest()[:(opt.seed_len or self.dfl_len)//8]
+			seed_bin = sha256(Crypto(cfg).get_random(1033)).digest()[:(cfg.seed_len or self.dfl_len)//8]
 		elif len(seed_bin)*8 not in self.lens:
 		elif len(seed_bin)*8 not in self.lens:
 			die(3,f'{len(seed_bin)*8}: invalid seed bit length')
 			die(3,f'{len(seed_bin)*8}: invalid seed bit length')
 
 
+		self.cfg = cfg
 		self.data = seed_bin
 		self.data = seed_bin
 		self.sid  = SeedID(seed=self)
 		self.sid  = SeedID(seed=self)
-		self.nSubseeds = nSubseeds # will override opt.subseeds
+		self.nSubseeds = nSubseeds # overrides cfg.subseeds
 
 
 	@property
 	@property
 	def bitlen(self):
 	def bitlen(self):
@@ -90,10 +91,9 @@ class Seed(SeedBase):
 	def subseeds(self):
 	def subseeds(self):
 		if not hasattr(self,'_subseeds'):
 		if not hasattr(self,'_subseeds'):
 			from .subseed import SubSeedList
 			from .subseed import SubSeedList
-			from .opts import opt
 			self._subseeds = SubSeedList(
 			self._subseeds = SubSeedList(
 				self,
 				self,
-				length = self.nSubseeds or opt.subseeds )
+				length = self.nSubseeds or self.cfg.subseeds )
 		return self._subseeds
 		return self._subseeds
 
 
 	def subseed(self,*args,**kwargs):
 	def subseed(self,*args,**kwargs):

+ 18 - 13
mmgen/seedsplit.py

@@ -20,7 +20,6 @@
 seedsplit: Seed split classes and methods for the MMGen suite
 seedsplit: Seed split classes and methods for the MMGen suite
 """
 """
 
 
-from .globalvars import g
 from .color import yellow
 from .color import yellow
 from .obj import MMGenPWIDString,MMGenIdx
 from .obj import MMGenPWIDString,MMGenIdx
 from .subseed import *
 from .subseed import *
@@ -77,7 +76,7 @@ class SeedShareList(SubSeedList):
 			for nonce in range(SeedShare.max_nonce+1):
 			for nonce in range(SeedShare.max_nonce+1):
 				ms = SeedShareMaster(self,master_idx,nonce)
 				ms = SeedShareMaster(self,master_idx,nonce)
 				if ms.sid == parent_seed.sid:
 				if ms.sid == parent_seed.sid:
-					if g.debug_subseed:
+					if parent_seed.cfg.debug_subseed:
 						msg(f'master_share seed ID collision with parent seed, incrementing nonce to {nonce+1}')
 						msg(f'master_share seed ID collision with parent seed, incrementing nonce to {nonce+1}')
 				else:
 				else:
 					return ms
 					return ms
@@ -103,7 +102,7 @@ class SeedShareList(SubSeedList):
 			self.last_share = ls = SeedShareLast(self)
 			self.last_share = ls = SeedShareLast(self)
 			if last_share_debug(ls) or ls.sid in self.data['long'] or ls.sid == parent_seed.sid:
 			if last_share_debug(ls) or ls.sid in self.data['long'] or ls.sid == parent_seed.sid:
 				# collision: throw out entire split list and redo with new start nonce
 				# collision: throw out entire split list and redo with new start nonce
-				if g.debug_subseed:
+				if parent_seed.cfg.debug_subseed:
 					self._collision_debug_msg(ls.sid,count,nonce,'nonce_start',debug_last_share)
 					self._collision_debug_msg(ls.sid,count,nonce,'nonce_start',debug_last_share)
 			else:
 			else:
 				self.data['long'][ls.sid] = (count,nonce)
 				self.data['long'][ls.sid] = (count,nonce)
@@ -111,7 +110,7 @@ class SeedShareList(SubSeedList):
 		else:
 		else:
 			die( 'SubSeedNonceRangeExceeded', 'nonce range exceeded' )
 			die( 'SubSeedNonceRangeExceeded', 'nonce range exceeded' )
 
 
-		if g.debug_subseed:
+		if parent_seed.cfg.debug_subseed:
 			A = parent_seed.data
 			A = parent_seed.data
 			B = self.join().data
 			B = self.join().data
 			assert A == B, f'Data mismatch!\noriginal seed: {A!r}\nrejoined seed: {B!r}'
 			assert A == B, f'Data mismatch!\noriginal seed: {A!r}\nrejoined seed: {B!r}'
@@ -137,6 +136,7 @@ class SeedShareList(SubSeedList):
 
 
 	def join(self):
 	def join(self):
 		return Seed.join_shares(
 		return Seed.join_shares(
+			self.parent_seed.cfg,
 			[self.get_share_by_idx(i+1) for i in range(len(self))] )
 			[self.get_share_by_idx(i+1) for i in range(len(self))] )
 
 
 	def format(self):
 	def format(self):
@@ -210,7 +210,7 @@ class SeedShare(SeedShareBase,SubSeed):
 				b':master:' +
 				b':master:' +
 				parent_list.master_share.idx.to_bytes(2,'big')
 				parent_list.master_share.idx.to_bytes(2,'big')
 			)
 			)
-		return Crypto().scramble_seed(seed.data,scramble_key)[:seed.byte_len]
+		return Crypto(parent_list.parent_seed.cfg).scramble_seed(seed.data,scramble_key)[:seed.byte_len]
 
 
 class SeedShareLast(SeedShareBase,SeedBase):
 class SeedShareLast(SeedShareBase,SeedBase):
 
 
@@ -222,6 +222,7 @@ class SeedShareLast(SeedShareBase,SeedBase):
 		self.parent_list = parent_list
 		self.parent_list = parent_list
 		SeedBase.__init__(
 		SeedBase.__init__(
 			self,
 			self,
+			parent_list.parent_seed.cfg,
 			seed_bin=self.make_subseed_bin(parent_list) )
 			seed_bin=self.make_subseed_bin(parent_list) )
 
 
 	@staticmethod
 	@staticmethod
@@ -244,10 +245,12 @@ class SeedShareMaster(SeedBase,SeedShareBase):
 		self.idx = idx
 		self.idx = idx
 		self.nonce = nonce
 		self.nonce = nonce
 		self.parent_list = parent_list
 		self.parent_list = parent_list
+		self.cfg = parent_list.parent_seed.cfg
 
 
-		SeedBase.__init__( self, self.make_base_seed_bin() )
+		SeedBase.__init__( self, self.cfg, self.make_base_seed_bin() )
 
 
 		self.derived_seed = SeedBase(
 		self.derived_seed = SeedBase(
+			self.cfg,
 			self.make_derived_seed_bin( parent_list.id_str, parent_list.count )
 			self.make_derived_seed_bin( parent_list.id_str, parent_list.count )
 		)
 		)
 
 
@@ -262,14 +265,14 @@ class SeedShareMaster(SeedBase,SeedShareBase):
 		seed = self.parent_list.parent_seed
 		seed = self.parent_list.parent_seed
 		# field maximums: idx: 65535 (1024)
 		# field maximums: idx: 65535 (1024)
 		scramble_key = b'master_share:' + self.idx.to_bytes(2,'big') + self.nonce.to_bytes(2,'big')
 		scramble_key = b'master_share:' + self.idx.to_bytes(2,'big') + self.nonce.to_bytes(2,'big')
-		return Crypto().scramble_seed(seed.data,scramble_key)[:seed.byte_len]
+		return Crypto(self.cfg).scramble_seed(seed.data,scramble_key)[:seed.byte_len]
 
 
 	# Don't bother with avoiding seed ID collision here, as sid of derived seed is not used
 	# Don't bother with avoiding seed ID collision here, as sid of derived seed is not used
 	# by user as an identifier
 	# by user as an identifier
 	def make_derived_seed_bin(self,id_str,count):
 	def make_derived_seed_bin(self,id_str,count):
 		# field maximums: id_str: none (256 chars), count: 65535 (1024)
 		# field maximums: id_str: none (256 chars), count: 65535 (1024)
 		scramble_key = id_str.encode() + b':' + count.to_bytes(2,'big')
 		scramble_key = id_str.encode() + b':' + count.to_bytes(2,'big')
-		return Crypto().scramble_seed(self.data,scramble_key)[:self.byte_len]
+		return Crypto(self.cfg).scramble_seed(self.data,scramble_key)[:self.byte_len]
 
 
 	def get_desc(self,ui=False):
 	def get_desc(self,ui=False):
 		psid = self.parent_list.parent_seed.sid
 		psid = self.parent_list.parent_seed.sid
@@ -281,15 +284,17 @@ class SeedShareMasterJoining(SeedShareMaster):
 	id_str = ImmutableAttr(SeedSplitIDString)
 	id_str = ImmutableAttr(SeedSplitIDString)
 	count  = ImmutableAttr(SeedShareCount)
 	count  = ImmutableAttr(SeedShareCount)
 
 
-	def __init__(self,idx,base_seed,id_str,count):
+	def __init__(self,cfg,idx,base_seed,id_str,count):
 
 
-		SeedBase.__init__(self,seed_bin=base_seed.data)
+		SeedBase.__init__( self, cfg, seed_bin=base_seed.data )
 
 
+		self.cfg = cfg
 		self.id_str = id_str or 'default'
 		self.id_str = id_str or 'default'
 		self.count = count
 		self.count = count
-		self.derived_seed = SeedBase( self.make_derived_seed_bin(self.id_str,self.count) )
+		self.derived_seed = SeedBase( cfg, self.make_derived_seed_bin(self.id_str,self.count) )
 
 
 def join_shares(
 def join_shares(
+		cfg,
 		seed_list,
 		seed_list,
 		master_idx = None,
 		master_idx = None,
 		id_str     = None ):
 		id_str     = None ):
@@ -315,8 +320,8 @@ def join_shares(
 		add_share(ss)
 		add_share(ss)
 
 
 	if master_idx:
 	if master_idx:
-		add_share(SeedShareMasterJoining(master_idx,master_share,id_str,d.count+1).derived_seed)
+		add_share(SeedShareMasterJoining( cfg, master_idx, master_share, id_str, d.count+1 ).derived_seed)
 
 
 	SeedShareCount(d.count) # check that d.count is in valid range
 	SeedShareCount(d.count) # check that d.count is in valid range
 
 
-	return Seed(seed_bin=d.ret.to_bytes(d.byte_len,'big'))
+	return Seed( cfg, seed_bin=d.ret.to_bytes(d.byte_len,'big') )

+ 4 - 4
mmgen/share/Opts.py

@@ -49,7 +49,7 @@ def print_help(*args):
 	print(make_help(*args))
 	print(make_help(*args))
 	sys.exit(0)
 	sys.exit(0)
 
 
-def make_help(proto,opt,opts_data,opt_filter):
+def make_help(cfg,proto,opts_data,opt_filter):
 
 
 	def parse_lines(text):
 	def parse_lines(text):
 		filtered = False
 		filtered = False
@@ -62,7 +62,7 @@ def make_help(proto,opt,opts_data,opt_filter):
 			elif not filtered:
 			elif not filtered:
 				yield line
 				yield line
 
 
-	opts_type,fs = ('options','{:<3} --{} {}') if opt.help else ('long_options','{}  --{} {}')
+	opts_type,fs = ('options','{:<3} --{} {}') if cfg.help else ('long_options','{}  --{} {}')
 	t = opts_data['text']
 	t = opts_data['text']
 	c = opts_data['code']
 	c = opts_data['code']
 	nl = '\n  '
 	nl = '\n  '
@@ -71,10 +71,10 @@ def make_help(proto,opt,opts_data,opt_filter):
 
 
 	from mmgen.help import help_notes_func
 	from mmgen.help import help_notes_func
 	def help_notes(k):
 	def help_notes(k):
-		return help_notes_func(proto,opt,k)
+		return help_notes_func(proto,cfg,k)
 
 
 	def gen_arg_tuple(func,text):
 	def gen_arg_tuple(func,text):
-		d = {'proto': proto,'help_notes':help_notes}
+		d = {'proto': proto,'help_notes':help_notes,'cfg':cfg}
 		for arg in func.__code__.co_varnames:
 		for arg in func.__code__.co_varnames:
 			yield d[arg] if arg in d else text
 			yield d[arg] if arg in d else text
 
 

+ 5 - 5
mmgen/subseed.py

@@ -21,7 +21,7 @@ subseed: Subseed classes and methods for the MMGen suite
 """
 """
 
 
 from .color import green
 from .color import green
-from .util import msg_r,msg,qmsg,die
+from .util import msg_r,msg,die
 from .obj import MMGenRange,IndexedDict
 from .obj import MMGenRange,IndexedDict
 from .seed import *
 from .seed import *
 
 
@@ -66,6 +66,7 @@ class SubSeed(SeedBase):
 		self.parent_list = parent_list
 		self.parent_list = parent_list
 		SeedBase.__init__(
 		SeedBase.__init__(
 			self,
 			self,
+			parent_list.parent_seed.cfg,
 			seed_bin=self.make_subseed_bin( parent_list, idx, nonce, length ))
 			seed_bin=self.make_subseed_bin( parent_list, idx, nonce, length ))
 
 
 	@staticmethod
 	@staticmethod
@@ -75,7 +76,7 @@ class SubSeed(SeedBase):
 		# field maximums: idx: 4294967295 (1000000), nonce: 65535 (1000), short: 255 (1)
 		# field maximums: idx: 4294967295 (1000000), nonce: 65535 (1000), short: 255 (1)
 		scramble_key  = idx.to_bytes(4,'big') + nonce.to_bytes(2,'big') + short.to_bytes(1,'big')
 		scramble_key  = idx.to_bytes(4,'big') + nonce.to_bytes(2,'big') + short.to_bytes(1,'big')
 		from .crypto import Crypto
 		from .crypto import Crypto
-		return Crypto().scramble_seed(seed.data,scramble_key)[:16 if short else seed.byte_len]
+		return Crypto(parent_list.parent_seed.cfg).scramble_seed(seed.data,scramble_key)[:16 if short else seed.byte_len]
 
 
 class SubSeedList(MMGenObject):
 class SubSeedList(MMGenObject):
 	have_short = True
 	have_short = True
@@ -129,7 +130,7 @@ class SubSeedList(MMGenObject):
 
 
 		def do_msg(subseed):
 		def do_msg(subseed):
 			if print_msg:
 			if print_msg:
-				qmsg('{} {} ({}:{})'.format(
+				self.parent_seed.cfg._util.qmsg('{} {} ({}:{})'.format(
 					green('Found subseed'),
 					green('Found subseed'),
 					subseed.sid.hl(),
 					subseed.sid.hl(),
 					self.parent_seed.sid.hl(),
 					self.parent_seed.sid.hl(),
@@ -186,8 +187,7 @@ class SubSeedList(MMGenObject):
 			for nonce in range(self.nonce_start,self.member_type.max_nonce+1): # handle SeedID collisions
 			for nonce in range(self.nonce_start,self.member_type.max_nonce+1): # handle SeedID collisions
 				sid = make_chksum_8(self.member_type.make_subseed_bin(self,idx,nonce,length))
 				sid = make_chksum_8(self.member_type.make_subseed_bin(self,idx,nonce,length))
 				if sid in self.data['long'] or sid in self.data['short'] or sid == self.parent_seed.sid:
 				if sid in self.data['long'] or sid in self.data['short'] or sid == self.parent_seed.sid:
-					from .globalvars import g
-					if g.debug_subseed: # should get ≈450 collisions for first 1,000,000 subseeds
+					if self.parent_seed.cfg.debug_subseed: # should get ≈450 collisions for first 1,000,000 subseeds
 						self._collision_debug_msg(sid,idx,nonce)
 						self._collision_debug_msg(sid,idx,nonce)
 				else:
 				else:
 					self.data[length][sid] = (idx,nonce)
 					self.data[length][sid] = (idx,nonce)

+ 8 - 4
mmgen/term.py

@@ -20,10 +20,12 @@
 term: Terminal classes for the MMGen suite
 term: Terminal classes for the MMGen suite
 """
 """
 
 
+# TODO: reimplement as instance instead of class
+
 import sys,os,time
 import sys,os,time
 from collections import namedtuple
 from collections import namedtuple
 
 
-from .globalvars import g,gc
+from .globalvars import gc
 from .util import msg,msg_r,die
 from .util import msg,msg_r,die
 
 
 try:
 try:
@@ -108,7 +110,7 @@ class MMGenTermLinux(MMGenTerm):
 
 
 	@classmethod
 	@classmethod
 	def kb_hold_protect(cls):
 	def kb_hold_protect(cls):
-		if g.hold_protect_disable:
+		if cls.cfg.hold_protect_disable:
 			return
 			return
 		tty.setcbreak(cls.stdin_fd)
 		tty.setcbreak(cls.stdin_fd)
 		timeout = 0.3
 		timeout = 0.3
@@ -130,7 +132,7 @@ class MMGenTermLinux(MMGenTerm):
 		timeout = 0.3
 		timeout = 0.3
 		tty.setcbreak(cls.stdin_fd)
 		tty.setcbreak(cls.stdin_fd)
 		msg_r(prompt)
 		msg_r(prompt)
-		if g.hold_protect_disable:
+		if cls.cfg.hold_protect_disable:
 			prehold_protect = False
 			prehold_protect = False
 		while True:
 		while True:
 			# Protect against held-down key before read()
 			# Protect against held-down key before read()
@@ -271,7 +273,7 @@ def get_term():
 		'mswin': (MMGenTermMSWin if sys.stdin.isatty() else MMGenTermMSWinStub),
 		'mswin': (MMGenTermMSWin if sys.stdin.isatty() else MMGenTermMSWinStub),
 	}[_platform]
 	}[_platform]
 
 
-def init_term(noecho=False):
+def init_term(cfg,noecho=False):
 
 
 	term = get_term()
 	term = get_term()
 
 
@@ -281,5 +283,7 @@ def init_term(noecho=False):
 	for var in ('get_char','get_char_raw','kb_hold_protect','get_terminal_size'):
 	for var in ('get_char','get_char_raw','kb_hold_protect','get_terminal_size'):
 		setattr( self, var, getattr(term,var) )
 		setattr( self, var, getattr(term,var) )
 
 
+	term.cfg = cfg # setting the _class_ attribute
+
 def reset_term():
 def reset_term():
 	get_term().reset()
 	get_term().reset()

+ 13 - 13
mmgen/tool/api.py

@@ -35,7 +35,10 @@ class tool_api(
 
 
 	Example:
 	Example:
 		from mmgen.tool.api import tool_api
 		from mmgen.tool.api import tool_api
-		tool = tool_api()
+		from mmgen.opts import init
+
+		# Initialize a tool API instance:
+		tool = tool_api(init())
 
 
 		# Set the coin and network:
 		# Set the coin and network:
 		tool.init_coin('btc','mainnet')
 		tool.init_coin('btc','mainnet')
@@ -63,16 +66,15 @@ class tool_api(
 	need_proto = True
 	need_proto = True
 	need_addrtype = True
 	need_addrtype = True
 
 
-	def __init__(self):
+	def __init__(self,cfg):
 		"""
 		"""
 		Initializer - takes no arguments
 		Initializer - takes no arguments
 		"""
 		"""
-		import mmgen.opts as opts
-		from ..opts import opt
-		opts.UserOpts._reset_ok += ('usr_randchars',)
-		if not opt._lock:
+		type(cfg)._reset_ok += ('usr_randchars',)
+		if not cfg._lock:
+			import mmgen.opts as opts
 			opts.init()
 			opts.init()
-		super().__init__()
+		super().__init__(cfg)
 
 
 	def init_coin(self,coinsym,network):
 	def init_coin(self,coinsym,network):
 		"""
 		"""
@@ -81,8 +83,8 @@ class tool_api(
 		Valid choices for network: 'mainnet','testnet','regtest'
 		Valid choices for network: 'mainnet','testnet','regtest'
 		"""
 		"""
 		from ..protocol import init_proto,warn_trustlevel
 		from ..protocol import init_proto,warn_trustlevel
-		warn_trustlevel(coinsym)
-		self.proto = init_proto(coinsym,network=network,need_amt=True)
+		warn_trustlevel(self.cfg)
+		self.proto = init_proto( self.cfg, coinsym, network=network, need_amt=True )
 		return self.proto
 		return self.proto
 
 
 	@property
 	@property
@@ -139,10 +141,8 @@ class tool_api(
 		The number of keystrokes of entropy to be gathered from the user.
 		The number of keystrokes of entropy to be gathered from the user.
 		Setting to zero disables user entropy gathering.
 		Setting to zero disables user entropy gathering.
 		"""
 		"""
-		from ..opts import opt
-		return opt.usr_randchars
+		return self.cfg.usr_randchars
 
 
 	@usr_randchars.setter
 	@usr_randchars.setter
 	def usr_randchars(self,val):
 	def usr_randchars(self,val):
-		from ..opts import opt
-		opt.usr_randchars = val
+		self.cfg.usr_randchars = val

+ 6 - 6
mmgen/tool/coin.py

@@ -45,8 +45,8 @@ class tool_cmd(tool_cmd_base):
 
 
 	def _init_generators(self,arg=None):
 	def _init_generators(self,arg=None):
 		return generator_data(
 		return generator_data(
-			kg = KeyGenerator( self.proto, self.mmtype.pubkey_type ),
-			ag = AddrGenerator( self.proto, self.mmtype ),
+			kg = KeyGenerator( self.cfg, self.proto, self.mmtype.pubkey_type ),
+			ag = AddrGenerator( self.cfg, self.proto, self.mmtype ),
 		)
 		)
 
 
 	def randwif(self):
 	def randwif(self):
@@ -54,7 +54,7 @@ class tool_cmd(tool_cmd_base):
 		from ..crypto import Crypto
 		from ..crypto import Crypto
 		return PrivKey(
 		return PrivKey(
 			self.proto,
 			self.proto,
-			Crypto().get_random(32),
+			Crypto(self.cfg).get_random(32),
 			pubkey_type = self.mmtype.pubkey_type,
 			pubkey_type = self.mmtype.pubkey_type,
 			compressed  = self.mmtype.compressed ).wif
 			compressed  = self.mmtype.compressed ).wif
 
 
@@ -64,7 +64,7 @@ class tool_cmd(tool_cmd_base):
 		from ..crypto import Crypto
 		from ..crypto import Crypto
 		privkey = PrivKey(
 		privkey = PrivKey(
 			self.proto,
 			self.proto,
-			Crypto().get_random(32),
+			Crypto(self.cfg).get_random(32),
 			pubkey_type = self.mmtype.pubkey_type,
 			pubkey_type = self.mmtype.pubkey_type,
 			compressed  = self.mmtype.compressed )
 			compressed  = self.mmtype.compressed )
 		return (
 		return (
@@ -136,7 +136,7 @@ class tool_cmd(tool_cmd_base):
 		if self.proto.base_proto == 'Ethereum' and len(pubkeyhex) == 128: # support raw ETH pubkeys
 		if self.proto.base_proto == 'Ethereum' and len(pubkeyhex) == 128: # support raw ETH pubkeys
 			pubkeyhex = '04' + pubkeyhex
 			pubkeyhex = '04' + pubkeyhex
 		from ..keygen import keygen_public_data
 		from ..keygen import keygen_public_data
-		ag = AddrGenerator( self.proto, self.mmtype )
+		ag = AddrGenerator( self.cfg, self.proto, self.mmtype )
 		return ag.to_addr(keygen_public_data(
 		return ag.to_addr(keygen_public_data(
 			pubkey        = bytes.fromhex(pubkeyhex),
 			pubkey        = bytes.fromhex(pubkeyhex),
 			viewkey_bytes = None,
 			viewkey_bytes = None,
@@ -192,4 +192,4 @@ class tool_cmd(tool_cmd_base):
 	def eth_checksummed_addr(self,addr:'sstr'):
 	def eth_checksummed_addr(self,addr:'sstr'):
 		"create a checksummed Ethereum address"
 		"create a checksummed Ethereum address"
 		from ..protocol import init_proto
 		from ..protocol import init_proto
-		return init_proto('eth').checksummed_addr(addr)
+		return init_proto( self.cfg, 'eth' ).checksummed_addr(addr)

+ 8 - 8
mmgen/tool/common.py

@@ -31,21 +31,21 @@ class tool_cmd_base(MMGenObject):
 	need_addrtype = False
 	need_addrtype = False
 	need_amt = False
 	need_amt = False
 
 
-	def __init__(self,cmdname=None,proto=None,mmtype=None):
+	def __init__(self,cfg,cmdname=None,proto=None,mmtype=None):
+
+		self.cfg = cfg
 
 
 		if self.need_proto:
 		if self.need_proto:
-			from ..protocol import init_proto_from_opts
-			self.proto = proto or init_proto_from_opts(need_amt=self.need_amt)
-			from ..globalvars import g
-			if g.token:
-				self.proto.tokensym = g.token.upper()
+			from ..protocol import init_proto_from_cfg
+			self.proto = proto or cfg._proto or init_proto_from_cfg(cfg,need_amt=True)
+			if cfg.token:
+				self.proto.tokensym = cfg.token.upper()
 
 
 		if self.need_addrtype:
 		if self.need_addrtype:
-			from ..opts import opt
 			from ..addr import MMGenAddrType
 			from ..addr import MMGenAddrType
 			self.mmtype = MMGenAddrType(
 			self.mmtype = MMGenAddrType(
 				self.proto,
 				self.proto,
-				mmtype or opt.type or self.proto.dfl_mmtype )
+				mmtype or cfg.type or self.proto.dfl_mmtype )
 
 
 	@property
 	@property
 	def user_commands(self):
 	def user_commands(self):

+ 5 - 5
mmgen/tool/file.py

@@ -27,18 +27,17 @@ class tool_cmd(tool_cmd_base):
 
 
 	need_proto = True
 	need_proto = True
 
 
-	def __init__(self,cmdname=None,proto=None,mmtype=None):
+	def __init__(self,cfg,cmdname=None,proto=None,mmtype=None):
 		if cmdname == 'txview':
 		if cmdname == 'txview':
 			self.need_amt = True
 			self.need_amt = True
-		super().__init__(cmdname=cmdname,proto=proto,mmtype=mmtype)
+		super().__init__(cfg=cfg,cmdname=cmdname,proto=proto,mmtype=mmtype)
 
 
 	def _file_chksum(self,mmgen_addrfile,obj):
 	def _file_chksum(self,mmgen_addrfile,obj):
 		kwargs = {'skip_chksum_msg':True}
 		kwargs = {'skip_chksum_msg':True}
 		if not obj.__name__ == 'PasswordList':
 		if not obj.__name__ == 'PasswordList':
 			kwargs.update({'key_address_validity_check':False})
 			kwargs.update({'key_address_validity_check':False})
-		ret = obj( self.proto, mmgen_addrfile, **kwargs )
-		from ..opts import opt
-		if opt.verbose:
+		ret = obj( self.cfg, self.proto, mmgen_addrfile, **kwargs )
+		if self.cfg.verbose:
 			from ..util import msg,capfirst
 			from ..util import msg,capfirst
 			if ret.al_id.mmtype.name == 'password':
 			if ret.al_id.mmtype.name == 'password':
 				msg('Passwd fmt:  {}\nPasswd len:  {}\nID string:   {}'.format(
 				msg('Passwd fmt:  {}\nPasswd len:  {}\nID string:   {}'.format(
@@ -99,6 +98,7 @@ class tool_cmd(tool_cmd_base):
 
 
 		async def process_file(f):
 		async def process_file(f):
 			return (await CompletedTX(
 			return (await CompletedTX(
+				cfg        = self.cfg,
 				filename   = f.name,
 				filename   = f.name,
 				quiet_open = True)).info.format( terse=terse, sort=tx_sort )
 				quiet_open = True)).info.format( terse=terse, sort=tx_sort )
 
 

+ 6 - 6
mmgen/tool/filecrypt.py

@@ -37,18 +37,18 @@ class tool_cmd(tool_cmd_base):
 	"""
 	"""
 	def encrypt(self,infile:str,outfile='',hash_preset=''):
 	def encrypt(self,infile:str,outfile='',hash_preset=''):
 		"encrypt a file"
 		"encrypt a file"
-		data = get_data_from_file( infile, 'data for encryption', binary=True )
-		enc_d = Crypto().mmgen_encrypt( data, 'data', hash_preset )
+		data = get_data_from_file( self.cfg, infile, 'data for encryption', binary=True )
+		enc_d = Crypto(self.cfg).mmgen_encrypt( data, 'data', hash_preset )
 		if not outfile:
 		if not outfile:
 			outfile = f'{os.path.basename(infile)}.{Crypto.mmenc_ext}'
 			outfile = f'{os.path.basename(infile)}.{Crypto.mmenc_ext}'
-		write_data_to_file( outfile, enc_d, 'encrypted data', binary=True )
+		write_data_to_file( self.cfg, outfile, enc_d, 'encrypted data', binary=True )
 		return True
 		return True
 
 
 	def decrypt(self,infile:str,outfile='',hash_preset=''):
 	def decrypt(self,infile:str,outfile='',hash_preset=''):
 		"decrypt a file"
 		"decrypt a file"
-		enc_d = get_data_from_file( infile, 'encrypted data', binary=True )
+		enc_d = get_data_from_file( self.cfg, infile, 'encrypted data', binary=True )
 		while True:
 		while True:
-			dec_d = Crypto().mmgen_decrypt( enc_d, 'data', hash_preset )
+			dec_d = Crypto(self.cfg).mmgen_decrypt( enc_d, 'data', hash_preset )
 			if dec_d:
 			if dec_d:
 				break
 				break
 			msg('Trying again...')
 			msg('Trying again...')
@@ -58,5 +58,5 @@ class tool_cmd(tool_cmd_base):
 			outfile = remove_extension(o,Crypto.mmenc_ext)
 			outfile = remove_extension(o,Crypto.mmenc_ext)
 			if outfile == o:
 			if outfile == o:
 				outfile += '.dec'
 				outfile += '.dec'
-		write_data_to_file( outfile, dec_d, 'decrypted data', binary=True )
+		write_data_to_file( self.cfg, outfile, dec_d, 'decrypted data', binary=True )
 		return True
 		return True

+ 7 - 9
mmgen/tool/fileutil.py

@@ -24,7 +24,7 @@ import os
 
 
 from .common import tool_cmd_base
 from .common import tool_cmd_base
 from ..globalvars import gc
 from ..globalvars import gc
-from ..util import msg,msg_r,qmsg,die,suf,make_full_path
+from ..util import msg,msg_r,die,suf,make_full_path
 from ..crypto import Crypto
 from ..crypto import Crypto
 
 
 class tool_cmd(tool_cmd_base):
 class tool_cmd(tool_cmd_base):
@@ -94,7 +94,6 @@ class tool_cmd(tool_cmd_base):
 		from cryptography.hazmat.primitives.ciphers import Cipher,algorithms,modes
 		from cryptography.hazmat.primitives.ciphers import Cipher,algorithms,modes
 		from cryptography.hazmat.backends import default_backend
 		from cryptography.hazmat.backends import default_backend
 
 
-		from ..opts import opt
 		from ..util2 import parse_bytespec
 		from ..util2 import parse_bytespec
 
 
 		def encrypt_worker(wid):
 		def encrypt_worker(wid):
@@ -111,11 +110,11 @@ class tool_cmd(tool_cmd_base):
 				q2.task_done()
 				q2.task_done()
 
 
 		nbytes = parse_bytespec(nbytes)
 		nbytes = parse_bytespec(nbytes)
-		if opt.outdir:
-			outfile = make_full_path( opt.outdir, outfile )
+		if self.cfg.outdir:
+			outfile = make_full_path( self.cfg.outdir, outfile )
 		f = open(outfile,'wb')
 		f = open(outfile,'wb')
 
 
-		key = Crypto().get_random(32)
+		key = Crypto(self.cfg).get_random(32)
 		q1,q2 = ( Queue(), Queue() )
 		q1,q2 = ( Queue(), Queue() )
 
 
 		for i in range(max(1,threads-2)):
 		for i in range(max(1,threads-2)):
@@ -146,14 +145,13 @@ class tool_cmd(tool_cmd_base):
 
 
 		if not silent:
 		if not silent:
 			msg(f'\rRead: {nbytes} bytes')
 			msg(f'\rRead: {nbytes} bytes')
-			qmsg(f'\r{nbytes} byte{suf(nbytes)} of random data written to file {outfile!r}')
+			self.cfg._util.qmsg(f'\r{nbytes} byte{suf(nbytes)} of random data written to file {outfile!r}')
 
 
 		return True
 		return True
 
 
 	def extract_key_from_geth_wallet( self, wallet_file:str, check_addr=True ):
 	def extract_key_from_geth_wallet( self, wallet_file:str, check_addr=True ):
 		"decrypt the encrypted private key in a Geth keystore wallet, returning the decrypted key"
 		"decrypt the encrypted private key in a Geth keystore wallet, returning the decrypted key"
 		from ..ui import line_input
 		from ..ui import line_input
-		from ..opts import opt
 		from ..proto.eth.misc import extract_key_from_geth_keystore_wallet
 		from ..proto.eth.misc import extract_key_from_geth_keystore_wallet
-		passwd = line_input( 'Enter passphrase: ', echo=opt.echo_passphrase ).strip().encode()
-		return extract_key_from_geth_keystore_wallet( wallet_file, passwd, check_addr ).hex()
+		passwd = line_input( self.cfg, 'Enter passphrase: ', echo=self.cfg.echo_passphrase ).strip().encode()
+		return extract_key_from_geth_keystore_wallet( self.cfg, wallet_file, passwd, check_addr ).hex()

+ 5 - 5
mmgen/tool/help.py

@@ -131,7 +131,7 @@ def gen_tool_usage():
 	for line in m2.rstrip().split('\n'):
 	for line in m2.rstrip().split('\n'):
 		yield line.lstrip('\t')
 		yield line.lstrip('\t')
 
 
-def gen_tool_cmd_usage(mod,cmdname):
+def gen_tool_cmd_usage(cfg,mod,cmdname):
 
 
 	from ..globalvars import gc
 	from ..globalvars import gc
 	from ..util import capfirst
 	from ..util import capfirst
@@ -182,14 +182,14 @@ def gen_tool_cmd_usage(mod,cmdname):
 		for line in docstr.split('\n')[1:]:
 		for line in docstr.split('\n')[1:]:
 			yield line.lstrip('\t')
 			yield line.lstrip('\t')
 
 
-def usage(cmdname=None,exit_val=1):
+def usage(cfg,cmdname=None,exit_val=1):
 
 
 	from ..util import Msg,die
 	from ..util import Msg,die
 
 
 	if cmdname:
 	if cmdname:
 		for mod,cmdlist in main_tool.mods.items():
 		for mod,cmdlist in main_tool.mods.items():
 			if cmdname in cmdlist:
 			if cmdname in cmdlist:
-				Msg('\n'.join(gen_tool_cmd_usage(mod,cmdname)))
+				Msg('\n'.join(gen_tool_cmd_usage(cfg,mod,cmdname)))
 				break
 				break
 		else:
 		else:
 			die(1,f'{cmdname!r}: no such tool command')
 			die(1,f'{cmdname!r}: no such tool command')
@@ -205,8 +205,8 @@ class tool_cmd(tool_cmd_base):
 
 
 	def help(self,command_name=''):
 	def help(self,command_name=''):
 		"display usage information for a single command or all commands"
 		"display usage information for a single command or all commands"
-		usage(command_name,exit_val=0)
+		usage(self.cfg,command_name,exit_val=0)
 
 
 	def usage(self,command_name=''):
 	def usage(self,command_name=''):
 		"display usage information for a single command or all commands"
 		"display usage information for a single command or all commands"
-		usage(command_name,exit_val=0)
+		usage(self.cfg,command_name,exit_val=0)

+ 5 - 6
mmgen/tool/mnemonic.py

@@ -62,7 +62,7 @@ class tool_cmd(tool_cmd_base):
 
 
 	def _xmr_reduce(self,bytestr):
 	def _xmr_reduce(self,bytestr):
 		from ..protocol import init_proto
 		from ..protocol import init_proto
-		proto = init_proto('xmr')
+		proto = init_proto( self.cfg, 'xmr' )
 		if len(bytestr) != proto.privkey_len:
 		if len(bytestr) != proto.privkey_len:
 			die(1,'{!r}: invalid bit length for Monero private key (must be {})'.format(
 			die(1,'{!r}: invalid bit length for Monero private key (must be {})'.format(
 				len(bytestr*8),
 				len(bytestr*8),
@@ -72,11 +72,10 @@ class tool_cmd(tool_cmd_base):
 	def _do_random_mn(self,nbytes:int,fmt:str):
 	def _do_random_mn(self,nbytes:int,fmt:str):
 		assert nbytes in (16,24,32), 'nbytes must be 16, 24 or 32'
 		assert nbytes in (16,24,32), 'nbytes must be 16, 24 or 32'
 		from ..crypto import Crypto
 		from ..crypto import Crypto
-		randbytes = Crypto().get_random(nbytes)
+		randbytes = Crypto(self.cfg).get_random(nbytes)
 		if fmt == 'xmrseed':
 		if fmt == 'xmrseed':
 			randbytes = self._xmr_reduce(randbytes)
 			randbytes = self._xmr_reduce(randbytes)
-		from ..opts import opt
-		if opt.verbose:
+		if self.cfg.verbose:
 			from ..util import msg
 			from ..util import msg
 			msg(f'Seed: {randbytes.hex()}')
 			msg(f'Seed: {randbytes.hex()}')
 		return self.hex2mn(randbytes.hex(),fmt=fmt)
 		return self.hex2mn(randbytes.hex(),fmt=fmt)
@@ -111,7 +110,7 @@ class tool_cmd(tool_cmd_base):
 			print_mn: 'print the seed phrase after entry' = False ):
 			print_mn: 'print the seed phrase after entry' = False ):
 		"convert an interactively supplied mnemonic seed phrase to a hexadecimal string"
 		"convert an interactively supplied mnemonic seed phrase to a hexadecimal string"
 		from ..mn_entry import mn_entry
 		from ..mn_entry import mn_entry
-		mn = mn_entry(fmt).get_mnemonic_from_user(25 if fmt == 'xmrseed' else mn_len,validate=False)
+		mn = mn_entry( self.cfg, fmt ).get_mnemonic_from_user(25 if fmt == 'xmrseed' else mn_len,validate=False)
 		if print_mn:
 		if print_mn:
 			from ..util import msg
 			from ..util import msg
 			msg(mn)
 			msg(mn)
@@ -119,7 +118,7 @@ class tool_cmd(tool_cmd_base):
 
 
 	def mn_stats(self, fmt:mn_opts_disp = dfl_mnemonic_fmt ):
 	def mn_stats(self, fmt:mn_opts_disp = dfl_mnemonic_fmt ):
 		"show stats for a mnemonic wordlist"
 		"show stats for a mnemonic wordlist"
-		return mnemonic_fmts[fmt].conv_cls(fmt).check_wordlist()
+		return mnemonic_fmts[fmt].conv_cls(fmt).check_wordlist(self.cfg)
 
 
 	def mn_printlist(self,
 	def mn_printlist(self,
 			fmt: mn_opts_disp = dfl_mnemonic_fmt,
 			fmt: mn_opts_disp = dfl_mnemonic_fmt,

+ 14 - 14
mmgen/tool/rpc.py

@@ -33,11 +33,11 @@ class tool_cmd(tool_cmd_base):
 	async def daemon_version(self):
 	async def daemon_version(self):
 		"print coin daemon version"
 		"print coin daemon version"
 		from ..daemon import CoinDaemon
 		from ..daemon import CoinDaemon
-		from ..globalvars import g
-		d = CoinDaemon( proto=self.proto, test_suite=g.test_suite )
+		d = CoinDaemon( cfg=self.cfg, proto=self.proto, test_suite=self.cfg.test_suite )
 		if self.proto.base_proto == 'Monero':
 		if self.proto.base_proto == 'Monero':
 			from ..proto.xmr.rpc import MoneroRPCClient
 			from ..proto.xmr.rpc import MoneroRPCClient
 			r = MoneroRPCClient(
 			r = MoneroRPCClient(
+				cfg    = self.cfg,
 				proto  = self.proto,
 				proto  = self.proto,
 				daemon = d,
 				daemon = d,
 				host   = d.host,
 				host   = d.host,
@@ -47,7 +47,7 @@ class tool_cmd(tool_cmd_base):
 				ignore_daemon_version = True )
 				ignore_daemon_version = True )
 		else:
 		else:
 			from ..rpc import rpc_init
 			from ..rpc import rpc_init
-			r = await rpc_init( self.proto, ignore_daemon_version=True )
+			r = await rpc_init( self.cfg, self.proto, ignore_daemon_version=True )
 		return f'{d.coind_name} version {r.daemon_version} ({r.daemon_version_str})'
 		return f'{d.coind_name} version {r.daemon_version} ({r.daemon_version_str})'
 
 
 	async def getbalance(self,
 	async def getbalance(self,
@@ -56,8 +56,7 @@ class tool_cmd(tool_cmd_base):
 			pager:   'send output to pager' = False ):
 			pager:   'send output to pager' = False ):
 		"list confirmed/unconfirmed, spendable/unspendable balances in tracking wallet"
 		"list confirmed/unconfirmed, spendable/unspendable balances in tracking wallet"
 		from ..tw.bal import TwGetBalance
 		from ..tw.bal import TwGetBalance
-		from ..globalvars import g
-		return (await TwGetBalance(self.proto,minconf,quiet)).format(color=g.color)
+		return (await TwGetBalance(self.cfg,self.proto,minconf,quiet)).format(color=self.cfg.color)
 
 
 	async def twops(self,
 	async def twops(self,
 			obj,pager,reverse,detail,sort,age_fmt,interactive,
 			obj,pager,reverse,detail,sort,age_fmt,interactive,
@@ -94,7 +93,7 @@ class tool_cmd(tool_cmd_base):
 		"view tracking wallet unspent outputs"
 		"view tracking wallet unspent outputs"
 
 
 		from ..tw.unspent import TwUnspentOutputs
 		from ..tw.unspent import TwUnspentOutputs
-		obj = await TwUnspentOutputs(self.proto,minconf=minconf)
+		obj = await TwUnspentOutputs(self.cfg,self.proto,minconf=minconf)
 		return await self.twops(
 		return await self.twops(
 			obj,pager,reverse,wide,sort,age_fmt,interactive,
 			obj,pager,reverse,wide,sort,age_fmt,interactive,
 			show_mmid = show_mmid )
 			show_mmid = show_mmid )
@@ -109,7 +108,7 @@ class tool_cmd(tool_cmd_base):
 			interactive: 'enable interactive operation' = False ):
 			interactive: 'enable interactive operation' = False ):
 		"view transaction history of tracking wallet"
 		"view transaction history of tracking wallet"
 
 
-		obj = await TwTxHistory(self.proto,sinceblock=sinceblock)
+		obj = await TwTxHistory(self.cfg,self.proto,sinceblock=sinceblock)
 		return await self.twops(
 		return await self.twops(
 			obj,pager,reverse,detail,sort,age_fmt,interactive )
 			obj,pager,reverse,detail,sort,age_fmt,interactive )
 
 
@@ -146,7 +145,7 @@ class tool_cmd(tool_cmd_base):
 		assert showused in (0,1,2), f"‘showused’ must have a value of 0, 1 or 2"
 		assert showused in (0,1,2), f"‘showused’ must have a value of 0, 1 or 2"
 
 
 		from ..tw.addresses import TwAddresses
 		from ..tw.addresses import TwAddresses
-		obj = await TwAddresses(self.proto,minconf=minconf,mmgen_addrs=mmgen_addrs)
+		obj = await TwAddresses(self.cfg,self.proto,minconf=minconf,mmgen_addrs=mmgen_addrs)
 		return await self.twops(
 		return await self.twops(
 			obj,pager,reverse,wide,sort,age_fmt,interactive,
 			obj,pager,reverse,wide,sort,age_fmt,interactive,
 			showcoinaddrs = showcoinaddrs,
 			showcoinaddrs = showcoinaddrs,
@@ -157,7 +156,7 @@ class tool_cmd(tool_cmd_base):
 	async def add_label(self,mmgen_or_coin_addr:str,label:str):
 	async def add_label(self,mmgen_or_coin_addr:str,label:str):
 		"add descriptive label for address in tracking wallet"
 		"add descriptive label for address in tracking wallet"
 		from ..tw.ctl import TwCtl
 		from ..tw.ctl import TwCtl
-		return await (await TwCtl(self.proto,mode='w')).set_comment(mmgen_or_coin_addr,label)
+		return await (await TwCtl(self.cfg,self.proto,mode='w')).set_comment(mmgen_or_coin_addr,label)
 
 
 	async def remove_label(self,mmgen_or_coin_addr:str):
 	async def remove_label(self,mmgen_or_coin_addr:str):
 		"remove descriptive label for address in tracking wallet"
 		"remove descriptive label for address in tracking wallet"
@@ -168,7 +167,7 @@ class tool_cmd(tool_cmd_base):
 		"remove an address from tracking wallet"
 		"remove an address from tracking wallet"
 		from ..tw.ctl import TwCtl
 		from ..tw.ctl import TwCtl
 		# returns None on failure:
 		# returns None on failure:
-		ret = await (await TwCtl(self.proto,mode='w')).remove_address(mmgen_or_coin_addr)
+		ret = await (await TwCtl(self.cfg,self.proto,mode='w')).remove_address(mmgen_or_coin_addr)
 		if ret:
 		if ret:
 			from ..util import msg
 			from ..util import msg
 			msg(f'Address {ret!r} deleted from tracking wallet')
 			msg(f'Address {ret!r} deleted from tracking wallet')
@@ -177,7 +176,7 @@ class tool_cmd(tool_cmd_base):
 	async def resolve_address(self,mmgen_or_coin_addr:str):
 	async def resolve_address(self,mmgen_or_coin_addr:str):
 		"resolve an MMGen address in the tracking wallet to a coin address or vice-versa"
 		"resolve an MMGen address in the tracking wallet to a coin address or vice-versa"
 		from ..tw.ctl import TwCtl
 		from ..tw.ctl import TwCtl
-		ret = await (await TwCtl(self.proto,mode='w')).resolve_address( mmgen_or_coin_addr )
+		ret = await (await TwCtl(self.cfg,self.proto,mode='w')).resolve_address( mmgen_or_coin_addr )
 		if ret:
 		if ret:
 			from ..util import Msg
 			from ..util import Msg
 			from ..addr import is_coin_addr
 			from ..addr import is_coin_addr
@@ -188,7 +187,7 @@ class tool_cmd(tool_cmd_base):
 	async def rescan_address(self,mmgen_or_coin_addr:str):
 	async def rescan_address(self,mmgen_or_coin_addr:str):
 		"rescan an address in the tracking wallet to update its balance"
 		"rescan an address in the tracking wallet to update its balance"
 		from ..tw.ctl import TwCtl
 		from ..tw.ctl import TwCtl
-		return await (await TwCtl(self.proto,mode='w')).rescan_address( mmgen_or_coin_addr )
+		return await (await TwCtl(self.cfg,self.proto,mode='w')).rescan_address( mmgen_or_coin_addr )
 
 
 	async def rescan_blockchain(self,
 	async def rescan_blockchain(self,
 			start_block: int = None,
 			start_block: int = None,
@@ -203,7 +202,7 @@ class tool_cmd(tool_cmd_base):
 		  parameter.
 		  parameter.
 		"""
 		"""
 		from ..tw.ctl import TwCtl
 		from ..tw.ctl import TwCtl
-		ret = await (await TwCtl(self.proto,mode='w')).rescan_blockchain(start_block,stop_block)
+		ret = await (await TwCtl(self.cfg,self.proto,mode='w')).rescan_blockchain(start_block,stop_block)
 		return True
 		return True
 
 
 	async def twexport(self,include_amts=True,pretty=False,prune=False,warn_used=False):
 	async def twexport(self,include_amts=True,pretty=False,prune=False,warn_used=False):
@@ -228,6 +227,7 @@ class tool_cmd(tool_cmd_base):
 		"""
 		"""
 		from ..tw.json import TwJSON
 		from ..tw.json import TwJSON
 		await TwJSON.Export(
 		await TwJSON.Export(
+			self.cfg,
 			self.proto,
 			self.proto,
 			include_amts = include_amts,
 			include_amts = include_amts,
 			pretty       = pretty,
 			pretty       = pretty,
@@ -249,5 +249,5 @@ class tool_cmd(tool_cmd_base):
 		  rescan_blockchain’.
 		  rescan_blockchain’.
 		"""
 		"""
 		from ..tw.json import TwJSON
 		from ..tw.json import TwJSON
-		await TwJSON.Import( self.proto, filename, ignore_checksum=ignore_checksum, batch=batch )
+		await TwJSON.Import( self.cfg, self.proto, filename, ignore_checksum=ignore_checksum, batch=batch )
 		return True
 		return True

+ 9 - 9
mmgen/tool/util.py

@@ -94,7 +94,7 @@ class tool_cmd(tool_cmd_base):
 			nbytes: 'number of bytes to output' = 32 ):
 			nbytes: 'number of bytes to output' = 32 ):
 		"print 'n' bytes (default 32) of random data in hex format"
 		"print 'n' bytes (default 32) of random data in hex format"
 		from ..crypto import Crypto
 		from ..crypto import Crypto
-		return Crypto().get_random( nbytes ).hex()
+		return Crypto(self.cfg).get_random( nbytes ).hex()
 
 
 	def hexreverse(self,hexstr:'sstr'):
 	def hexreverse(self,hexstr:'sstr'):
 		"reverse bytes of a hexadecimal string"
 		"reverse bytes of a hexadecimal string"
@@ -103,7 +103,7 @@ class tool_cmd(tool_cmd_base):
 	def hexlify(self,infile:str):
 	def hexlify(self,infile:str):
 		"convert bytes in file to hexadecimal (use '-' for stdin)"
 		"convert bytes in file to hexadecimal (use '-' for stdin)"
 		from ..fileutil import get_data_from_file
 		from ..fileutil import get_data_from_file
-		data = get_data_from_file( infile, dash=True, quiet=True, binary=True )
+		data = get_data_from_file( self.cfg, infile, dash=True, quiet=True, binary=True )
 		return data.hex()
 		return data.hex()
 
 
 	def unhexlify(self,hexstr:'sstr'):
 	def unhexlify(self,hexstr:'sstr'):
@@ -117,7 +117,7 @@ class tool_cmd(tool_cmd_base):
 		"create hexdump of data from file (use '-' for stdin)"
 		"create hexdump of data from file (use '-' for stdin)"
 		from ..fileutil import get_data_from_file
 		from ..fileutil import get_data_from_file
 		from ..util2 import pretty_hexdump
 		from ..util2 import pretty_hexdump
-		data = get_data_from_file( infile, dash=True, quiet=True, binary=True )
+		data = get_data_from_file( self.cfg, infile, dash=True, quiet=True, binary=True )
 		return pretty_hexdump( data, cols=cols, line_nums=line_nums ).rstrip()
 		return pretty_hexdump( data, cols=cols, line_nums=line_nums ).rstrip()
 
 
 	def unhexdump(self,infile:str):
 	def unhexdump(self,infile:str):
@@ -127,7 +127,7 @@ class tool_cmd(tool_cmd_base):
 			msvcrt.setmode( sys.stdout.fileno(), os.O_BINARY )
 			msvcrt.setmode( sys.stdout.fileno(), os.O_BINARY )
 		from ..fileutil import get_data_from_file
 		from ..fileutil import get_data_from_file
 		from ..util2 import decode_pretty_hexdump
 		from ..util2 import decode_pretty_hexdump
-		hexdata = get_data_from_file( infile, dash=True, quiet=True )
+		hexdata = get_data_from_file( self.cfg, infile, dash=True, quiet=True )
 		return decode_pretty_hexdump(hexdata)
 		return decode_pretty_hexdump(hexdata)
 
 
 	def hash160(self,hexstr:'sstr'):
 	def hash160(self,hexstr:'sstr'):
@@ -144,7 +144,7 @@ class tool_cmd(tool_cmd_base):
 		from hashlib import sha256
 		from hashlib import sha256
 		if file_input:
 		if file_input:
 			from ..fileutil import get_data_from_file
 			from ..fileutil import get_data_from_file
-			b = get_data_from_file( data, binary=True )
+			b = get_data_from_file( self.cfg, data, binary=True )
 		elif hex_input:
 		elif hex_input:
 			from ..util2 import decode_pretty_hexdump
 			from ..util2 import decode_pretty_hexdump
 			b = decode_pretty_hexdump(data)
 			b = decode_pretty_hexdump(data)
@@ -157,7 +157,7 @@ class tool_cmd(tool_cmd_base):
 		from ..util import make_chksum_6
 		from ..util import make_chksum_6
 		from ..fileutil import get_data_from_file
 		from ..fileutil import get_data_from_file
 		return make_chksum_6(
 		return make_chksum_6(
-			get_data_from_file( infile, dash=True, quiet=True, binary=True ))
+			get_data_from_file( self.cfg, infile, dash=True, quiet=True, binary=True ))
 
 
 	def str2id6(self,string:'sstr'): # retain ignoring of space for backwards compat
 	def str2id6(self,string:'sstr'): # retain ignoring of space for backwards compat
 		"generate 6-character MMGen ID for a string, ignoring spaces in string"
 		"generate 6-character MMGen ID for a string, ignoring spaces in string"
@@ -169,7 +169,7 @@ class tool_cmd(tool_cmd_base):
 		from ..util import make_chksum_8
 		from ..util import make_chksum_8
 		from ..fileutil import get_data_from_file
 		from ..fileutil import get_data_from_file
 		return make_chksum_8(
 		return make_chksum_8(
-			get_data_from_file( infile, dash=True, quiet=True, binary=True ))
+			get_data_from_file( self.cfg, infile, dash=True, quiet=True, binary=True ))
 
 
 	def randb58(self,
 	def randb58(self,
 			nbytes: 'number of bytes to output' = 32,
 			nbytes: 'number of bytes to output' = 32,
@@ -177,13 +177,13 @@ class tool_cmd(tool_cmd_base):
 		"generate random data (default: 32 bytes) and convert it to base 58"
 		"generate random data (default: 32 bytes) and convert it to base 58"
 		from ..baseconv import baseconv
 		from ..baseconv import baseconv
 		from ..crypto import Crypto
 		from ..crypto import Crypto
-		return baseconv('b58').frombytes( Crypto().get_random(nbytes), pad=pad, tostr=True )
+		return baseconv('b58').frombytes( Crypto(self.cfg).get_random(nbytes), pad=pad, tostr=True )
 
 
 	def bytestob58(self,infile:str,pad: 'pad output to this width' = 0):
 	def bytestob58(self,infile:str,pad: 'pad output to this width' = 0):
 		"convert bytes to base 58 (supply data via STDIN)"
 		"convert bytes to base 58 (supply data via STDIN)"
 		from ..fileutil import get_data_from_file
 		from ..fileutil import get_data_from_file
 		from ..baseconv import baseconv
 		from ..baseconv import baseconv
-		data = get_data_from_file( infile, dash=True, quiet=True, binary=True )
+		data = get_data_from_file( self.cfg, infile, dash=True, quiet=True, binary=True )
 		return baseconv('b58').frombytes( data, pad=pad, tostr=True )
 		return baseconv('b58').frombytes( data, pad=pad, tostr=True )
 
 
 	def b58tobytes(self,b58_str:'sstr',pad: 'pad output to this width' = 0):
 	def b58tobytes(self,b58_str:'sstr',pad: 'pad output to this width' = 0):

+ 14 - 13
mmgen/tool/wallet.py

@@ -22,7 +22,6 @@ tool.wallet: Wallet routines for the 'mmgen-tool' utility
 
 
 from .common import tool_cmd_base
 from .common import tool_cmd_base
 
 
-from ..opts import opt
 from ..subseed import SubSeedList
 from ..subseed import SubSeedList
 from ..seedsplit import MasterShareIdx
 from ..seedsplit import MasterShareIdx
 from ..wallet import Wallet
 from ..wallet import Wallet
@@ -30,32 +29,33 @@ from ..wallet import Wallet
 class tool_cmd(tool_cmd_base):
 class tool_cmd(tool_cmd_base):
 	"key, address or subseed generation from an MMGen wallet"
 	"key, address or subseed generation from an MMGen wallet"
 
 
-	def __init__(self,cmdname=None,proto=None,mmtype=None):
+	def __init__(self,cfg,cmdname=None,proto=None,mmtype=None):
 		self.need_proto = cmdname in ('gen_key','gen_addr')
 		self.need_proto = cmdname in ('gen_key','gen_addr')
-		super().__init__(cmdname=cmdname,proto=proto,mmtype=mmtype)
+		super().__init__(cfg,cmdname=cmdname,proto=proto,mmtype=mmtype)
 
 
 	def _get_seed_file(self,wallet):
 	def _get_seed_file(self,wallet):
 		from ..fileutil import get_seed_file
 		from ..fileutil import get_seed_file
 		return get_seed_file(
 		return get_seed_file(
+			cfg     = self.cfg,
 			wallets = [wallet] if wallet else [],
 			wallets = [wallet] if wallet else [],
 			nargs   = 1 )
 			nargs   = 1 )
 
 
 	def get_subseed(self,subseed_idx:str,wallet=''):
 	def get_subseed(self,subseed_idx:str,wallet=''):
 		"get the Seed ID of a single subseed by Subseed Index for default or specified wallet"
 		"get the Seed ID of a single subseed by Subseed Index for default or specified wallet"
-		opt.quiet = True
-		return Wallet(self._get_seed_file(wallet)).seed.subseed(subseed_idx).sid
+		self.cfg.quiet = True
+		return Wallet(self.cfg,self._get_seed_file(wallet)).seed.subseed(subseed_idx).sid
 
 
 	def get_subseed_by_seed_id(self,seed_id:str,wallet='',last_idx=SubSeedList.dfl_len):
 	def get_subseed_by_seed_id(self,seed_id:str,wallet='',last_idx=SubSeedList.dfl_len):
 		"get the Subseed Index of a single subseed by Seed ID for default or specified wallet"
 		"get the Subseed Index of a single subseed by Seed ID for default or specified wallet"
-		opt.quiet = True
-		ret = Wallet(self._get_seed_file(wallet)).seed.subseed_by_seed_id( seed_id, last_idx )
+		self.cfg.quiet = True
+		ret = Wallet(self.cfg,self._get_seed_file(wallet)).seed.subseed_by_seed_id( seed_id, last_idx )
 		return ret.ss_idx if ret else None
 		return ret.ss_idx if ret else None
 
 
 	def list_subseeds(self,subseed_idx_range:str,wallet=''):
 	def list_subseeds(self,subseed_idx_range:str,wallet=''):
 		"list a range of subseed Seed IDs for default or specified wallet"
 		"list a range of subseed Seed IDs for default or specified wallet"
-		opt.quiet = True
+		self.cfg.quiet = True
 		from ..subseed import SubSeedIdxRange
 		from ..subseed import SubSeedIdxRange
-		return Wallet(self._get_seed_file(wallet)).seed.subseeds.format( *SubSeedIdxRange(subseed_idx_range) )
+		return Wallet(self.cfg,self._get_seed_file(wallet)).seed.subseeds.format( *SubSeedIdxRange(subseed_idx_range) )
 
 
 	def list_shares(self,
 	def list_shares(self,
 			share_count: int,
 			share_count: int,
@@ -63,8 +63,8 @@ class tool_cmd(tool_cmd_base):
 			master_share: f'(min:1, max:{MasterShareIdx.max_val}, 0=no master share)' = 0,
 			master_share: f'(min:1, max:{MasterShareIdx.max_val}, 0=no master share)' = 0,
 			wallet = '' ):
 			wallet = '' ):
 		"list the Seed IDs of the shares resulting from a split of default or specified wallet"
 		"list the Seed IDs of the shares resulting from a split of default or specified wallet"
-		opt.quiet = True
-		return Wallet(self._get_seed_file(wallet)).seed.split( share_count, id_str, master_share ).format()
+		self.cfg.quiet = True
+		return Wallet(self.cfg,self._get_seed_file(wallet)).seed.split( share_count, id_str, master_share ).format()
 
 
 	def gen_key(self,mmgen_addr:str,wallet=''):
 	def gen_key(self,mmgen_addr:str,wallet=''):
 		"generate a single WIF key for specified MMGen address from default or specified wallet"
 		"generate a single WIF key for specified MMGen address from default or specified wallet"
@@ -79,14 +79,15 @@ class tool_cmd(tool_cmd_base):
 		from ..addrlist import AddrList,AddrIdxList
 		from ..addrlist import AddrList,AddrIdxList
 
 
 		addr = MMGenID( self.proto, mmgen_addr )
 		addr = MMGenID( self.proto, mmgen_addr )
-		opt.quiet = True
-		ss = Wallet(self._get_seed_file(wallet))
+		self.cfg.quiet = True
+		ss = Wallet(self.cfg,self._get_seed_file(wallet))
 
 
 		if ss.seed.sid != addr.sid:
 		if ss.seed.sid != addr.sid:
 			from ..util import die
 			from ..util import die
 			die(1,f'Seed ID of requested address ({addr.sid}) does not match wallet ({ss.seed.sid})')
 			die(1,f'Seed ID of requested address ({addr.sid}) does not match wallet ({ss.seed.sid})')
 
 
 		d = AddrList(
 		d = AddrList(
+			cfg       = self.cfg,
 			proto     = self.proto,
 			proto     = self.proto,
 			seed      = ss.seed,
 			seed      = ss.seed,
 			addr_idxs = AddrIdxList(str(addr.idx)),
 			addr_idxs = AddrIdxList(str(addr.idx)),

+ 4 - 5
mmgen/tw/addresses.py

@@ -74,9 +74,9 @@ class TwAddresses(TwView):
 	def coinaddr_list(self):
 	def coinaddr_list(self):
 		return [d.addr for d in self.data]
 		return [d.addr for d in self.data]
 
 
-	async def __init__(self,proto,minconf=1,mmgen_addrs='',get_data=False):
+	async def __init__(self,cfg,proto,minconf=1,mmgen_addrs='',get_data=False):
 
 
-		await super().__init__(proto)
+		await super().__init__(cfg,proto)
 
 
 		self.minconf = NonNegativeInt(minconf)
 		self.minconf = NonNegativeInt(minconf)
 
 
@@ -320,10 +320,9 @@ class TwAddresses(TwView):
 			top = len(data) - 1 if top is None else top )
 			top = len(data) - 1 if top is None else top )
 
 
 		if start is not None:
 		if start is not None:
-			from ..opts import opt
 			for d in data[start:]:
 			for d in data[start:]:
 				if d.al_id == al_id:
 				if d.al_id == al_id:
-					if not d.recvd and (opt.autochg_ignore_labels or not d.comment):
+					if not d.recvd and (self.cfg.autochg_ignore_labels or not d.comment):
 						if d.comment:
 						if d.comment:
 							msg('{} {} {} {}{}'.format(
 							msg('{} {} {} {}{}'.format(
 								yellow('WARNING: address'),
 								yellow('WARNING: address'),
@@ -362,7 +361,7 @@ class TwAddresses(TwView):
 
 
 			from ..ui import line_input
 			from ..ui import line_input
 			while True:
 			while True:
-				res = line_input(prompt)
+				res = line_input( self.cfg, prompt )
 				if is_int(res) and 0 < int(res) <= len(addrs):
 				if is_int(res) and 0 < int(res) <= len(addrs):
 					return addrs[int(res)-1]
 					return addrs[int(res)-1]
 				msg(f'{res}: invalid entry')
 				msg(f'{res}: invalid entry')

+ 3 - 3
mmgen/tw/bal.py

@@ -29,10 +29,10 @@ from ..rpc import rpc_init
 
 
 class TwGetBalance(MMGenObject,metaclass=AsyncInit):
 class TwGetBalance(MMGenObject,metaclass=AsyncInit):
 
 
-	def __new__(cls,proto,*args,**kwargs):
+	def __new__(cls,cfg,proto,*args,**kwargs):
 		return MMGenObject.__new__(proto.base_proto_subclass(cls,'tw.bal'))
 		return MMGenObject.__new__(proto.base_proto_subclass(cls,'tw.bal'))
 
 
-	async def __init__(self,proto,minconf,quiet):
+	async def __init__(self,cfg,proto,minconf,quiet):
 
 
 		class BalanceInfo(dict):
 		class BalanceInfo(dict):
 			def __init__(self):
 			def __init__(self):
@@ -50,7 +50,7 @@ class TwGetBalance(MMGenObject,metaclass=AsyncInit):
 		self.quiet = quiet
 		self.quiet = quiet
 		self.proto = proto
 		self.proto = proto
 		self.data = {k:self.balance_info() for k in self.start_labels}
 		self.data = {k:self.balance_info() for k in self.start_labels}
-		self.rpc = await rpc_init(proto)
+		self.rpc = await rpc_init(cfg,proto)
 
 
 		if minconf < 2 and 'lt_minconf' in self.conf_cols:
 		if minconf < 2 and 'lt_minconf' in self.conf_cols:
 			del self.conf_cols['lt_minconf']
 			del self.conf_cols['lt_minconf']

+ 16 - 15
mmgen/tw/ctl.py

@@ -23,8 +23,7 @@ tw.ctl: Tracking wallet control class for the MMGen suite
 import json
 import json
 from collections import namedtuple
 from collections import namedtuple
 
 
-from ..globalvars import g
-from ..util import msg,msg_r,qmsg,dmsg,suf,die
+from ..util import msg,msg_r,suf,die
 from ..base_obj import AsyncInit
 from ..base_obj import AsyncInit
 from ..objmethods import MMGenObject
 from ..objmethods import MMGenObject
 from ..obj import TwComment,get_obj
 from ..obj import TwComment,get_obj
@@ -53,10 +52,10 @@ class TwCtl(MMGenObject,metaclass=AsyncInit):
 	aggressive_sync = False
 	aggressive_sync = False
 	importing = False
 	importing = False
 
 
-	def __new__(cls,proto,*args,**kwargs):
+	def __new__(cls,cfg,proto,*args,**kwargs):
 		return MMGenObject.__new__(proto.base_proto_subclass(cls,'tw.ctl'))
 		return MMGenObject.__new__(proto.base_proto_subclass(cls,'tw.ctl'))
 
 
-	async def __init__(self,proto,mode='r',token_addr=None,rpc_ignore_wallet=False):
+	async def __init__(self,cfg,proto,mode='r',token_addr=None,rpc_ignore_wallet=False):
 
 
 		assert mode in ('r','w','i'), f"{mode!r}: wallet mode must be 'r','w' or 'i'"
 		assert mode in ('r','w','i'), f"{mode!r}: wallet mode must be 'r','w' or 'i'"
 		if mode == 'i':
 		if mode == 'i':
@@ -64,7 +63,8 @@ class TwCtl(MMGenObject,metaclass=AsyncInit):
 			mode = 'w'
 			mode = 'w'
 
 
 		# TODO: create on demand - only certain ops require RPC
 		# TODO: create on demand - only certain ops require RPC
-		self.rpc = await rpc_init( proto, ignore_wallet=rpc_ignore_wallet )
+		self.cfg = cfg
+		self.rpc = await rpc_init( cfg, proto, ignore_wallet=rpc_ignore_wallet )
 		self.proto = proto
 		self.proto = proto
 		self.mode = mode
 		self.mode = mode
 		self.desc = self.base_desc = f'{self.proto.name} tracking wallet'
 		self.desc = self.base_desc = f'{self.proto.name} tracking wallet'
@@ -86,9 +86,9 @@ class TwCtl(MMGenObject,metaclass=AsyncInit):
 	def init_from_wallet_file(self):
 	def init_from_wallet_file(self):
 		import os
 		import os
 		tw_dir = (
 		tw_dir = (
-			os.path.join(g.data_dir) if self.proto.coin == 'BTC' else
+			os.path.join(self.cfg.data_dir) if self.proto.coin == 'BTC' else
 			os.path.join(
 			os.path.join(
-				g.data_dir_root,
+				self.cfg.data_dir_root,
 				'altcoins',
 				'altcoins',
 				self.proto.coin.lower(),
 				self.proto.coin.lower(),
 				('' if self.proto.network == 'mainnet' else 'testnet')
 				('' if self.proto.network == 'mainnet' else 'testnet')
@@ -99,7 +99,7 @@ class TwCtl(MMGenObject,metaclass=AsyncInit):
 		check_or_create_dir(tw_dir)
 		check_or_create_dir(tw_dir)
 
 
 		try:
 		try:
-			self.orig_data = get_data_from_file(self.tw_fn,quiet=True)
+			self.orig_data = get_data_from_file( self.cfg, self.tw_fn, quiet=True )
 			self.data = json.loads(self.orig_data)
 			self.data = json.loads(self.orig_data)
 		except:
 		except:
 			try: os.stat(self.tw_fn)
 			try: os.stat(self.tw_fn)
@@ -116,7 +116,7 @@ class TwCtl(MMGenObject,metaclass=AsyncInit):
 		if self.mode == 'w':
 		if self.mode == 'w':
 			import atexit
 			import atexit
 			def del_twctl(twctl):
 			def del_twctl(twctl):
-				dmsg(f'Running exit handler del_twctl() for {twctl!r}')
+				self.cfg._util.dmsg(f'Running exit handler del_twctl() for {twctl!r}')
 				del twctl
 				del twctl
 			atexit.register(del_twctl,self)
 			atexit.register(del_twctl,self)
 
 
@@ -135,7 +135,7 @@ class TwCtl(MMGenObject,metaclass=AsyncInit):
 		"""
 		"""
 		if getattr(self,'mode',None) == 'w': # mode attr might not exist in this state
 		if getattr(self,'mode',None) == 'w': # mode attr might not exist in this state
 			self.write()
 			self.write()
-		elif g.debug:
+		elif self.cfg.debug:
 			msg('read-only wallet, doing nothing')
 			msg('read-only wallet, doing nothing')
 
 
 	def conv_types(self,ad):
 	def conv_types(self,ad):
@@ -163,7 +163,7 @@ class TwCtl(MMGenObject,metaclass=AsyncInit):
 	def get_cached_balance(self,addr,session_cache,data_root):
 	def get_cached_balance(self,addr,session_cache,data_root):
 		if addr in session_cache:
 		if addr in session_cache:
 			return self.proto.coin_amt(session_cache[addr])
 			return self.proto.coin_amt(session_cache[addr])
-		if not g.cached_balances:
+		if not self.cfg.cached_balances:
 			return None
 			return None
 		if addr in data_root and 'balance' in data_root[addr]:
 		if addr in data_root and 'balance' in data_root[addr]:
 			return self.proto.coin_amt(data_root[addr]['balance'])
 			return self.proto.coin_amt(data_root[addr]['balance'])
@@ -185,6 +185,7 @@ class TwCtl(MMGenObject,metaclass=AsyncInit):
 	def write_changed(self,data,quiet):
 	def write_changed(self,data,quiet):
 		from ..fileutil import write_data_to_file
 		from ..fileutil import write_data_to_file
 		write_data_to_file(
 		write_data_to_file(
+			self.cfg,
 			self.tw_fn,
 			self.tw_fn,
 			data,
 			data,
 			desc              = f'{self.base_desc} data',
 			desc              = f'{self.base_desc} data',
@@ -198,14 +199,14 @@ class TwCtl(MMGenObject,metaclass=AsyncInit):
 
 
 	def write(self,quiet=True):
 	def write(self,quiet=True):
 		if not self.use_tw_file:
 		if not self.use_tw_file:
-			dmsg("'use_tw_file' is False, doing nothing")
+			self.cfg._util.dmsg("'use_tw_file' is False, doing nothing")
 			return
 			return
-		dmsg(f'write(): checking if {self.desc} data has changed')
+		self.cfg._util.dmsg(f'write(): checking if {self.desc} data has changed')
 
 
 		wdata = json.dumps(self.data)
 		wdata = json.dumps(self.data)
 		if self.orig_data != wdata:
 		if self.orig_data != wdata:
 			self.write_changed(wdata,quiet=quiet)
 			self.write_changed(wdata,quiet=quiet)
-		elif g.debug:
+		elif self.cfg.debug:
 			msg('Data is unchanged\n')
 			msg('Data is unchanged\n')
 
 
 	async def resolve_address(self,addrspec):
 	async def resolve_address(self,addrspec):
@@ -296,7 +297,7 @@ class TwCtl(MMGenObject,metaclass=AsyncInit):
 		async def do_import(address,comment,message):
 		async def do_import(address,comment,message):
 			try:
 			try:
 				res = await self.import_address( address, comment )
 				res = await self.import_address( address, comment )
-				qmsg(message)
+				self.cfg._util.qmsg(message)
 				return res
 				return res
 			except Exception as e:
 			except Exception as e:
 				die(2,f'\nImport of address {address!r} failed: {e.args[0]!r}')
 				die(2,f'\nImport of address {address!r} failed: {e.args[0]!r}')

+ 15 - 14
mmgen/tw/json.py

@@ -15,8 +15,7 @@ tw.json: export and import tracking wallet to JSON format
 import os,json
 import os,json
 from collections import namedtuple
 from collections import namedtuple
 
 
-from ..opts import opt
-from ..util import msg,ymsg,fmt,suf,die,make_timestamp,make_chksum_8,compare_or_die
+from ..util import msg,ymsg,fmt,suf,die,make_timestamp,make_chksum_8
 from ..base_obj import AsyncInit
 from ..base_obj import AsyncInit
 from ..objmethods import MMGenObject
 from ..objmethods import MMGenObject
 from ..rpc import json_encoder
 from ..rpc import json_encoder
@@ -30,10 +29,11 @@ class TwJSON:
 		pruned = None
 		pruned = None
 		fn_pfx = 'mmgen-tracking-wallet-dump'
 		fn_pfx = 'mmgen-tracking-wallet-dump'
 
 
-		def __new__(cls,proto,*args,**kwargs):
+		def __new__(cls,cfg,proto,*args,**kwargs):
 			return MMGenObject.__new__(proto.base_proto_subclass(TwJSON,'tw.json',cls.__name__))
 			return MMGenObject.__new__(proto.base_proto_subclass(TwJSON,'tw.json',cls.__name__))
 
 
-		def __init__(self,proto):
+		def __init__(self,cfg,proto):
+			self.cfg = cfg
 			self.proto = proto
 			self.proto = proto
 			self.coin = proto.coin_id.lower()
 			self.coin = proto.coin_id.lower()
 			self.network = proto.network
 			self.network = proto.network
@@ -54,7 +54,7 @@ class TwJSON:
 				from ..addrlist import AddrIdxList
 				from ..addrlist import AddrIdxList
 				prune_id = AddrIdxList(idx_list=self.pruned).id_str
 				prune_id = AddrIdxList(idx_list=self.pruned).id_str
 				fn = get_fn(prune_id)
 				fn = get_fn(prune_id)
-				if len(fn) > os.statvfs(opt.outdir or os.curdir).f_namemax:
+				if len(fn) > os.statvfs(self.cfg.outdir or os.curdir).f_namemax:
 					fn = get_fn(f'idhash={make_chksum_8(prune_id.encode()).lower()}')
 					fn = get_fn(f'idhash={make_chksum_8(prune_id.encode()).lower()}')
 			else:
 			else:
 				fn = get_fn(None)
 				fn = get_fn(None)
@@ -84,11 +84,11 @@ class TwJSON:
 
 
 		blockchain_rescan_warning = None
 		blockchain_rescan_warning = None
 
 
-		async def __init__(self,proto,filename,ignore_checksum=False,batch=False):
+		async def __init__(self,cfg,proto,filename,ignore_checksum=False,batch=False):
 
 
-			super().__init__(proto)
+			super().__init__(cfg,proto)
 
 
-			self.twctl = await TwCtl( proto, mode='i', rpc_ignore_wallet=True )
+			self.twctl = await TwCtl( cfg, proto, mode='i', rpc_ignore_wallet=True )
 
 
 			def check_network(data):
 			def check_network(data):
 				coin,network = data['network'].split('_')
 				coin,network = data['network'].split('_')
@@ -108,7 +108,7 @@ class TwJSON:
 			def verify_data(d):
 			def verify_data(d):
 				check_network(d['data'])
 				check_network(d['data'])
 				check_chksum(d)
 				check_chksum(d)
-				compare_or_die(
+				self.cfg._util.compare_or_die(
 					val1  = self.mappings_chksum,
 					val1  = self.mappings_chksum,
 					val2  = d['data']['mappings_checksum'],
 					val2  = d['data']['mappings_checksum'],
 					desc1 = 'computed mappings checksum',
 					desc1 = 'computed mappings checksum',
@@ -118,7 +118,7 @@ class TwJSON:
 				return True
 				return True
 
 
 			from ..fileutil import get_data_from_file
 			from ..fileutil import get_data_from_file
-			self.data = json.loads(get_data_from_file(filename,quiet=True))
+			self.data = json.loads(get_data_from_file( self.cfg, filename, quiet=True ))
 			self.keys = self.data['data']['entries_keys']
 			self.keys = self.data['data']['entries_keys']
 			self.entries = await self.get_entries()
 			self.entries = await self.get_entries()
 
 
@@ -141,7 +141,7 @@ class TwJSON:
 			msg('\n'+fmt(self.info_msg.strip(),indent='  '))
 			msg('\n'+fmt(self.info_msg.strip(),indent='  '))
 
 
 			from ..ui import keypress_confirm
 			from ..ui import keypress_confirm
-			if not keypress_confirm('Continue?'):
+			if not keypress_confirm( self.cfg, 'Continue?' ):
 				msg('Exiting at user request')
 				msg('Exiting at user request')
 				return False
 				return False
 
 
@@ -152,7 +152,7 @@ class TwJSON:
 
 
 	class Export(Base,metaclass=AsyncInit):
 	class Export(Base,metaclass=AsyncInit):
 
 
-		async def __init__(self,proto,include_amts=True,pretty=False,prune=False,warn_used=False):
+		async def __init__(self,cfg,proto,include_amts=True,pretty=False,prune=False,warn_used=False):
 
 
 			if prune and not self.can_prune:
 			if prune and not self.can_prune:
 				die(1,f'Pruning not supported for {proto.name} protocol')
 				die(1,f'Pruning not supported for {proto.name} protocol')
@@ -160,12 +160,12 @@ class TwJSON:
 			self.prune = prune
 			self.prune = prune
 			self.warn_used = warn_used
 			self.warn_used = warn_used
 
 
-			super().__init__(proto)
+			super().__init__(cfg,proto)
 
 
 			if not include_amts:
 			if not include_amts:
 				self.keys.remove('amount')
 				self.keys.remove('amount')
 
 
-			self.twctl = await TwCtl( proto )
+			self.twctl = await TwCtl( cfg, proto )
 
 
 			self.entries = await self.get_entries()
 			self.entries = await self.get_entries()
 
 
@@ -190,6 +190,7 @@ class TwJSON:
 
 
 			from ..fileutil import write_data_to_file
 			from ..fileutil import write_data_to_file
 			write_data_to_file(
 			write_data_to_file(
+				cfg     = self.cfg,
 				outfile = self.dump_fn,
 				outfile = self.dump_fn,
 				data    = self.json_dump(
 				data    = self.json_dump(
 					{
 					{

+ 1 - 1
mmgen/tw/prune.py

@@ -70,7 +70,7 @@ class TwAddressesPrune(TwAddresses):
 			from ..ui import line_input
 			from ..ui import line_input
 			msg('')
 			msg('')
 			while True:
 			while True:
-				reply = line_input(prompt).strip()
+				reply = line_input( parent.cfg, prompt ).strip()
 				if reply:
 				if reply:
 					from ..addrlist import AddrIdxList
 					from ..addrlist import AddrIdxList
 					from ..obj import get_obj
 					from ..obj import get_obj

+ 2 - 2
mmgen/tw/txhistory.py

@@ -42,8 +42,8 @@ class TwTxHistory(TwView):
 	filters = ('show_unconfirmed',)
 	filters = ('show_unconfirmed',)
 	mod_subpath = 'tw.txhistory'
 	mod_subpath = 'tw.txhistory'
 
 
-	async def __init__(self,proto,sinceblock=0):
-		await super().__init__(proto)
+	async def __init__(self,cfg,proto,sinceblock=0):
+		await super().__init__(cfg,proto)
 		self.sinceblock = NonNegativeInt( sinceblock if sinceblock >= 0 else self.rpc.blockcount + sinceblock )
 		self.sinceblock = NonNegativeInt( sinceblock if sinceblock >= 0 else self.rpc.blockcount + sinceblock )
 
 
 	@property
 	@property

+ 2 - 3
mmgen/tw/unspent.py

@@ -20,7 +20,6 @@
 tw.unspent: Tracking wallet unspent outputs class for the MMGen suite
 tw.unspent: Tracking wallet unspent outputs class for the MMGen suite
 """
 """
 
 
-from ..globalvars import g
 from ..util import msg,suf,fmt
 from ..util import msg,suf,fmt
 from ..objmethods import MMGenObject
 from ..objmethods import MMGenObject
 from ..obj import (
 from ..obj import (
@@ -77,8 +76,8 @@ class TwUnspentOutputs(TwView):
 			def amt2(self,value):
 			def amt2(self,value):
 				return self.proto.coin_amt(value)
 				return self.proto.coin_amt(value)
 
 
-	async def __init__(self,proto,minconf=1,addrs=[]):
-		await super().__init__(proto)
+	async def __init__(self,cfg,proto,minconf=1,addrs=[]):
+		await super().__init__(cfg,proto)
 		self.minconf  = minconf
 		self.minconf  = minconf
 		self.addrs    = addrs
 		self.addrs    = addrs
 		from ..globalvars import gc
 		from ..globalvars import gc

+ 14 - 12
mmgen/tw/view.py

@@ -23,8 +23,6 @@ tw.view: base class for tracking wallet view classes
 import sys,time,asyncio
 import sys,time,asyncio
 from collections import namedtuple
 from collections import namedtuple
 
 
-from ..globalvars import g
-from ..opts import opt
 from ..objmethods import Hilite,InitErrors,MMGenObject
 from ..objmethods import Hilite,InitErrors,MMGenObject
 from ..obj import get_obj,MMGenIdx,MMGenList
 from ..obj import get_obj,MMGenIdx,MMGenList
 from ..color import nocolor,yellow,green,red,blue
 from ..color import nocolor,yellow,green,red,blue
@@ -182,15 +180,16 @@ class TwView(MMGenObject,metaclass=AsyncInit):
 		}
 		}
 	}
 	}
 
 
-	def __new__(cls,proto,*args,**kwargs):
+	def __new__(cls,cfg,proto,*args,**kwargs):
 		return MMGenObject.__new__(proto.base_proto_subclass(cls,cls.mod_subpath))
 		return MMGenObject.__new__(proto.base_proto_subclass(cls,cls.mod_subpath))
 
 
-	async def __init__(self,proto):
+	async def __init__(self,cfg,proto):
+		self.cfg = cfg
 		self.proto = proto
 		self.proto = proto
-		self.rpc = await rpc_init(proto)
+		self.rpc = await rpc_init(cfg,proto)
 		if self.has_wallet:
 		if self.has_wallet:
 			from .ctl import TwCtl
 			from .ctl import TwCtl
-			self.twctl = await TwCtl(proto,mode='w')
+			self.twctl = await TwCtl(cfg,proto,mode='w')
 		self.amt_keys = {'amt':'iwidth','amt2':'iwidth2'} if self.has_amt2 else {'amt':'iwidth'}
 		self.amt_keys = {'amt':'iwidth','amt2':'iwidth2'} if self.has_amt2 else {'amt':'iwidth'}
 
 
 	@property
 	@property
@@ -283,15 +282,15 @@ class TwView(MMGenObject,metaclass=AsyncInit):
 		user_resized = False
 		user_resized = False
 		while True:
 		while True:
 			ts = get_terminal_size()
 			ts = get_terminal_size()
-			cols = g.columns or ts.width
+			cols = self.cfg.columns or ts.width
 			lines = ts.height
 			lines = ts.height
 			if cols >= min_cols and (min_lines is None or lines >= min_lines):
 			if cols >= min_cols and (min_lines is None or lines >= min_lines):
 				if user_resized:
 				if user_resized:
 					msg_r(CUR_HOME + ERASE_ALL)
 					msg_r(CUR_HOME + ERASE_ALL)
 				return _term_dimensions(cols,ts.height)
 				return _term_dimensions(cols,ts.height)
 			if sys.stdout.isatty():
 			if sys.stdout.isatty():
-				if g.columns and cols < min_cols:
-					die(1,'\n'+fmt(self.twidth_diemsg.format(g.columns,self.desc,min_cols),indent='  '))
+				if self.cfg.columns and cols < min_cols:
+					die(1,'\n'+fmt(self.twidth_diemsg.format(self.cfg.columns,self.desc,min_cols),indent='  '))
 				else:
 				else:
 					m,dim = (self.twidth_errmsg,min_cols) if cols < min_cols else (self.theight_errmsg,min_lines)
 					m,dim = (self.twidth_errmsg,min_cols) if cols < min_cols else (self.theight_errmsg,min_lines)
 					get_char_raw( CUR_HOME + ERASE_ALL + fmt( m.format(self.desc,dim), append='' ))
 					get_char_raw( CUR_HOME + ERASE_ALL + fmt( m.format(self.desc,dim), append='' ))
@@ -520,7 +519,7 @@ class TwView(MMGenObject,metaclass=AsyncInit):
 				self.key_mappings.update(self.scroll_keys[gc.platform])
 				self.key_mappings.update(self.scroll_keys[gc.platform])
 			return self.key_mappings
 			return self.key_mappings
 
 
-		scroll = self.scroll = g.scroll
+		scroll = self.scroll = self.cfg.scroll
 
 
 		key_mappings = make_key_mappings(scroll)
 		key_mappings = make_key_mappings(scroll)
 		action_classes = { k: getattr(self,action_map[v[:2]])() for k,v in key_mappings.items() }
 		action_classes = { k: getattr(self,action_map[v[:2]])() for k,v in key_mappings.items() }
@@ -533,7 +532,7 @@ class TwView(MMGenObject,metaclass=AsyncInit):
 		self.oneshot_msg = ''
 		self.oneshot_msg = ''
 		prompt += '\b'
 		prompt += '\b'
 
 
-		clear_screen = '\n\n' if opt.no_blank else CUR_HOME + ('' if scroll else ERASE_ALL)
+		clear_screen = '\n\n' if self.cfg.no_blank else CUR_HOME + ('' if scroll else ERASE_ALL)
 
 
 		from ..term import get_term,get_char,get_char_raw
 		from ..term import get_term,get_char,get_char_raw
 
 
@@ -581,7 +580,7 @@ class TwView(MMGenObject,metaclass=AsyncInit):
 
 
 	def keypress_confirm(self,*args,**kwargs):
 	def keypress_confirm(self,*args,**kwargs):
 		from ..ui import keypress_confirm
 		from ..ui import keypress_confirm
-		if keypress_confirm(*args,no_nl=self.scroll,**kwargs):
+		if keypress_confirm( self.cfg, *args, no_nl=self.scroll, **kwargs ):
 			return True
 			return True
 		else:
 		else:
 			if self.scroll:
 			if self.scroll:
@@ -620,6 +619,7 @@ class TwView(MMGenObject,metaclass=AsyncInit):
 			from ..exception import UserNonConfirmation
 			from ..exception import UserNonConfirmation
 			try:
 			try:
 				write_data_to_file(
 				write_data_to_file(
+					cfg     = parent.cfg,
 					outfile = outfile,
 					outfile = outfile,
 					data    = print_hdr + await parent.format(
 					data    = print_hdr + await parent.format(
 						display_type    = output_type,
 						display_type    = output_type,
@@ -654,6 +654,7 @@ class TwView(MMGenObject,metaclass=AsyncInit):
 			while True:
 			while True:
 				msg_r(parent.blank_prompt if parent.scroll else '\n')
 				msg_r(parent.blank_prompt if parent.scroll else '\n')
 				ret = line_input(
 				ret = line_input(
+					parent.cfg,
 					f'Enter {parent.item_desc} number (or ENTER to return to main menu): ' )
 					f'Enter {parent.item_desc} number (or ENTER to return to main menu): ' )
 				if ret == '':
 				if ret == '':
 					if parent.scroll:
 					if parent.scroll:
@@ -734,6 +735,7 @@ class TwView(MMGenObject,metaclass=AsyncInit):
 
 
 			from ..ui import line_input
 			from ..ui import line_input
 			res = line_input(
 			res = line_input(
+				parent.cfg,
 				'Enter label text for {} {}: '.format(parent.item_desc,red(f'#{idx}')),
 				'Enter label text for {} {}: '.format(parent.item_desc,red(f'#{idx}')),
 				insert_txt = cur_comment )
 				insert_txt = cur_comment )
 
 

+ 6 - 5
mmgen/tx/__init__.py

@@ -33,7 +33,7 @@ def _get_cls_info(clsname,modname,args,kwargs):
 		proto = kwargs['data']['proto']
 		proto = kwargs['data']['proto']
 	elif 'filename' in kwargs:
 	elif 'filename' in kwargs:
 		from .file import MMGenTxFile
 		from .file import MMGenTxFile
-		proto = MMGenTxFile.get_proto( kwargs['filename'], quiet_open=True )
+		proto = MMGenTxFile.get_proto( kwargs['cfg'], kwargs['filename'], quiet_open=True )
 	elif clsname == 'Base':
 	elif clsname == 'Base':
 		proto = None
 		proto = None
 	else:
 	else:
@@ -52,19 +52,20 @@ def _get_cls_info(clsname,modname,args,kwargs):
 
 
 	kwargs['proto'] = proto
 	kwargs['proto'] = proto
 
 
-	return ( proto, clsname, modname, kwargs )
+	return ( kwargs['cfg'], proto, clsname, modname, kwargs )
+
 
 
 def _get_obj( _clsname, _modname, *args, **kwargs ):
 def _get_obj( _clsname, _modname, *args, **kwargs ):
 	"""
 	"""
 	determine cls/mod/proto and pass them to _base_proto_subclass() to get a transaction instance
 	determine cls/mod/proto and pass them to _base_proto_subclass() to get a transaction instance
 	"""
 	"""
-	proto,clsname,modname,kwargs = _get_cls_info(_clsname,_modname,args,kwargs)
+	cfg,proto,clsname,modname,kwargs = _get_cls_info(_clsname,_modname,args,kwargs)
 
 
 	return _base_proto_subclass( clsname, modname, proto )(*args,**kwargs)
 	return _base_proto_subclass( clsname, modname, proto )(*args,**kwargs)
 
 
 async def _get_obj_async( _clsname, _modname, *args, **kwargs ):
 async def _get_obj_async( _clsname, _modname, *args, **kwargs ):
 
 
-	proto,clsname,modname,kwargs = _get_cls_info(_clsname,_modname,args,kwargs)
+	cfg,proto,clsname,modname,kwargs = _get_cls_info(_clsname,_modname,args,kwargs)
 
 
 	# NB: tracking wallet needed to retrieve the 'symbol' and 'decimals' parameters of token addr
 	# NB: tracking wallet needed to retrieve the 'symbol' and 'decimals' parameters of token addr
 	# (see twctl:import_token()).
 	# (see twctl:import_token()).
@@ -72,7 +73,7 @@ async def _get_obj_async( _clsname, _modname, *args, **kwargs ):
 	# signing.
 	# signing.
 	if proto and proto.tokensym and clsname in ('New','OnlineSigned'):
 	if proto and proto.tokensym and clsname in ('New','OnlineSigned'):
 		from ..tw.ctl import TwCtl
 		from ..tw.ctl import TwCtl
-		kwargs['twctl'] = await TwCtl(proto)
+		kwargs['twctl'] = await TwCtl(cfg,proto)
 
 
 	return _base_proto_subclass( clsname, modname, proto )(*args,**kwargs)
 	return _base_proto_subclass( clsname, modname, proto )(*args,**kwargs)
 
 

+ 7 - 6
mmgen/tx/base.py

@@ -26,7 +26,6 @@ from ..obj import (
 )
 )
 from ..addr import MMGenID,CoinAddr
 from ..addr import MMGenID,CoinAddr
 from ..util import msg,ymsg,fmt,remove_dups,make_timestamp,die
 from ..util import msg,ymsg,fmt,remove_dups,make_timestamp,die
-from ..opts import opt
 
 
 class MMGenTxIO(MMGenListItem):
 class MMGenTxIO(MMGenListItem):
 	vout     = ListItemAttr(NonNegativeInt)
 	vout     = ListItemAttr(NonNegativeInt)
@@ -110,6 +109,7 @@ class Base(MMGenObject):
 		desc = 'transaction outputs'
 		desc = 'transaction outputs'
 
 
 	def __init__(self,*args,**kwargs):
 	def __init__(self,*args,**kwargs):
+		self.cfg      = kwargs['cfg']
 		self.inputs   = self.InputList(self)
 		self.inputs   = self.InputList(self)
 		self.outputs  = self.OutputList(self)
 		self.outputs  = self.OutputList(self)
 		self.name     = type(self).__name__
 		self.name     = type(self).__name__
@@ -171,13 +171,14 @@ class Base(MMGenObject):
 	def add_comment(self,infile=None):
 	def add_comment(self,infile=None):
 		if infile:
 		if infile:
 			from ..fileutil import get_data_from_file
 			from ..fileutil import get_data_from_file
-			self.comment = MMGenTxComment(get_data_from_file(infile,'transaction comment'))
+			self.comment = MMGenTxComment(get_data_from_file( self.cfg, infile, 'transaction comment' ))
 		else:
 		else:
 			from ..ui import keypress_confirm,line_input
 			from ..ui import keypress_confirm,line_input
 			if keypress_confirm(
 			if keypress_confirm(
+					self.cfg,
 					prompt = 'Edit transaction comment?' if self.comment else 'Add a comment to transaction?',
 					prompt = 'Edit transaction comment?' if self.comment else 'Add a comment to transaction?',
 					default_yes = False ):
 					default_yes = False ):
-				res = MMGenTxComment(line_input('Comment: ',insert_txt=self.comment))
+				res = MMGenTxComment(line_input( self.cfg, 'Comment: ', insert_txt=self.comment ))
 				if not res:
 				if not res:
 					ymsg('Warning: comment is empty')
 					ymsg('Warning: comment is empty')
 				changed = res != self.comment
 				changed = res != self.comment
@@ -199,11 +200,11 @@ class Base(MMGenObject):
 			fs = fmt(self.non_mmgen_inputs_msg,strip_char='\t',indent=indent).strip()
 			fs = fmt(self.non_mmgen_inputs_msg,strip_char='\t',indent=indent).strip()
 			m = fs.format('\n    '.join(non_mmaddrs))
 			m = fs.format('\n    '.join(non_mmaddrs))
 			if caller in ('txdo','txsign'):
 			if caller in ('txdo','txsign'):
-				if not opt.keys_from_file:
+				if not self.cfg.keys_from_file:
 					die( 'UserOptError', f'\n{indent}ERROR: {m}\n' )
 					die( 'UserOptError', f'\n{indent}ERROR: {m}\n' )
 			else:
 			else:
 				msg(f'\n{indent}WARNING: {m}\n')
 				msg(f'\n{indent}WARNING: {m}\n')
-				if not opt.yes:
+				if not self.cfg.yes:
 					from ..ui import keypress_confirm
 					from ..ui import keypress_confirm
-					if not keypress_confirm('Continue?',default_yes=True):
+					if not keypress_confirm( self.cfg, 'Continue?', default_yes=True ):
 						die(1,'Exiting at user request')
 						die(1,'Exiting at user request')

+ 4 - 5
mmgen/tx/bump.py

@@ -14,7 +14,6 @@ tx.bump: transaction bump class
 
 
 from .new import New
 from .new import New
 from .completed import Completed
 from .completed import Completed
-from ..opts import opt
 from ..util import is_int
 from ..util import is_int
 
 
 class Bump(Completed,New):
 class Bump(Completed,New):
@@ -56,14 +55,14 @@ class Bump(Completed,New):
 			else:
 			else:
 				die(1,'Insufficient funds to bump transaction')
 				die(1,'Insufficient funds to bump transaction')
 
 
-		init_reply = opt.output_to_reduce
+		init_reply = self.cfg.output_to_reduce
 		chg_idx = self.chg_idx
 		chg_idx = self.chg_idx
 
 
 		while True:
 		while True:
 			if init_reply == None:
 			if init_reply == None:
 				from ..ui import line_input
 				from ..ui import line_input
 				m = 'Choose an output to deduct the fee from (Hit ENTER for the change output): '
 				m = 'Choose an output to deduct the fee from (Hit ENTER for the change output): '
-				reply = line_input(m) or 'c'
+				reply = line_input( self.cfg, m ) or 'c'
 			else:
 			else:
 				reply,init_reply = init_reply,None
 				reply,init_reply = init_reply,None
 			if chg_idx == None and not is_int(reply):
 			if chg_idx == None and not is_int(reply):
@@ -79,11 +78,11 @@ class Bump(Completed,New):
 					cm = ' (change output)' if chg_idx == idx else ''
 					cm = ' (change output)' if chg_idx == idx else ''
 					prompt = f'Fee will be deducted from output {idx+1}{cm} ({o_amt} {self.coin})'
 					prompt = f'Fee will be deducted from output {idx+1}{cm} ({o_amt} {self.coin})'
 					if check_sufficient_funds(o_amt):
 					if check_sufficient_funds(o_amt):
-						if opt.yes:
+						if self.cfg.yes:
 							msg(prompt)
 							msg(prompt)
 						else:
 						else:
 							from ..ui import keypress_confirm
 							from ..ui import keypress_confirm
-							if not keypress_confirm(prompt+'.  OK?',default_yes=True):
+							if not keypress_confirm( self.cfg, prompt+'.  OK?', default_yes=True ):
 								continue
 								continue
 						self.bump_output_idx = idx
 						self.bump_output_idx = idx
 						return idx
 						return idx

+ 2 - 2
mmgen/tx/completed.py

@@ -20,11 +20,11 @@ class Completed(Base):
 	"""
 	"""
 	filename_api = True
 	filename_api = True
 
 
-	def __init__(self,filename=None,data=None,quiet_open=False,*args,**kwargs):
+	def __init__(self,cfg,filename=None,data=None,quiet_open=False,*args,**kwargs):
 
 
 		assert (filename or data) and not (filename and data), 'CompletedTX_chk1'
 		assert (filename or data) and not (filename and data), 'CompletedTX_chk1'
 
 
-		super().__init__(*args,**kwargs)
+		super().__init__(cfg=cfg,*args,**kwargs)
 
 
 		if data:
 		if data:
 			data['twctl'] = self.twctl
 			data['twctl'] = self.twctl

+ 11 - 10
mmgen/tx/file.py

@@ -20,7 +20,7 @@
 tx.file: Transaction file operations for the MMGen suite
 tx.file: Transaction file operations for the MMGen suite
 """
 """
 
 
-from ..common import *
+from ..util import ymsg,make_chksum_6,die
 from ..obj import MMGenObject,HexStr,MMGenTxID,CoinTxID,MMGenTxComment
 from ..obj import MMGenObject,HexStr,MMGenTxID,CoinTxID,MMGenTxComment
 
 
 class MMGenTxFile(MMGenObject):
 class MMGenTxFile(MMGenObject):
@@ -58,12 +58,12 @@ class MMGenTxFile(MMGenObject):
 			return io_list( parent=tx, data=[io(tx.proto,**e) for e in d] )
 			return io_list( parent=tx, data=[io(tx.proto,**e) for e in d] )
 
 
 		from ..fileutil import get_data_from_file
 		from ..fileutil import get_data_from_file
-		tx_data = get_data_from_file(infile,tx.desc+' data',quiet=quiet_open)
+		tx_data = get_data_from_file( tx.cfg, infile, tx.desc+' data', quiet=quiet_open )
 
 
 		try:
 		try:
 			desc = 'data'
 			desc = 'data'
-			if len(tx_data) > g.max_tx_file_size:
-				die( 'MaxFileSizeExceeded', f'Transaction file size exceeds limit ({g.max_tx_file_size} bytes)' )
+			if len(tx_data) > tx.cfg.max_tx_file_size:
+				die( 'MaxFileSizeExceeded', f'Transaction file size exceeds limit ({tx.cfg.max_tx_file_size} bytes)' )
 			tx_data = tx_data.splitlines()
 			tx_data = tx_data.splitlines()
 			assert len(tx_data) >= 5,'number of lines less than 5'
 			assert len(tx_data) >= 5,'number of lines less than 5'
 			assert len(tx_data[0]) == 6,'invalid length of first line'
 			assert len(tx_data[0]) == 6,'invalid length of first line'
@@ -104,10 +104,10 @@ class MMGenTxFile(MMGenObject):
 			tx.chain = metadata.pop(0).lower() if len(metadata) == 5 else 'mainnet'
 			tx.chain = metadata.pop(0).lower() if len(metadata) == 5 else 'mainnet'
 
 
 			from ..protocol import CoinProtocol,init_proto
 			from ..protocol import CoinProtocol,init_proto
-			network = CoinProtocol.Base.chain_name_to_network(coin,tx.chain)
+			network = CoinProtocol.Base.chain_name_to_network(tx.cfg,coin,tx.chain)
 
 
 			desc = 'initialization of protocol'
 			desc = 'initialization of protocol'
-			tx.proto = init_proto(coin,network=network,need_amt=True)
+			tx.proto = init_proto( tx.cfg, coin, network=network, need_amt=True )
 			if tokensym:
 			if tokensym:
 				tx.proto.tokensym = tokensym
 				tx.proto.tokensym = tokensym
 
 
@@ -186,8 +186,8 @@ class MMGenTxFile(MMGenObject):
 
 
 		self.chksum = make_chksum_6(' '.join(lines))
 		self.chksum = make_chksum_6(' '.join(lines))
 		fmt_data = '\n'.join([self.chksum] + lines) + '\n'
 		fmt_data = '\n'.join([self.chksum] + lines) + '\n'
-		if len(fmt_data) > g.max_tx_file_size:
-			die( 'MaxFileSizeExceeded', f'Transaction file size exceeds limit ({g.max_tx_file_size} bytes)' )
+		if len(fmt_data) > tx.cfg.max_tx_file_size:
+			die( 'MaxFileSizeExceeded', f'Transaction file size exceeds limit ({tx.cfg.max_tx_file_size} bytes)' )
 		return fmt_data
 		return fmt_data
 
 
 	def write(self,
 	def write(self,
@@ -208,6 +208,7 @@ class MMGenTxFile(MMGenObject):
 
 
 		from ..fileutil import write_data_to_file
 		from ..fileutil import write_data_to_file
 		write_data_to_file(
 		write_data_to_file(
+			cfg                   = self.tx.cfg,
 			outfile               = self.filename,
 			outfile               = self.filename,
 			data                  = self.fmt_data,
 			data                  = self.fmt_data,
 			desc                  = self.tx.desc + add_desc,
 			desc                  = self.tx.desc + add_desc,
@@ -217,8 +218,8 @@ class MMGenTxFile(MMGenObject):
 			ask_write_default_yes = ask_write_default_yes )
 			ask_write_default_yes = ask_write_default_yes )
 
 
 	@classmethod
 	@classmethod
-	def get_proto(cls,filename,quiet_open=False):
+	def get_proto(cls,cfg,filename,quiet_open=False):
 		from . import BaseTX
 		from . import BaseTX
-		tmp_tx = BaseTX()
+		tmp_tx = BaseTX(cfg=cfg)
 		cls(tmp_tx).parse(filename,metadata_only=True,quiet_open=quiet_open)
 		cls(tmp_tx).parse(filename,metadata_only=True,quiet_open=quiet_open)
 		return tmp_tx.proto
 		return tmp_tx.proto

+ 1 - 2
mmgen/tx/info.py

@@ -14,7 +14,6 @@ tx.info: transaction info class
 
 
 from ..globalvars import gc
 from ..globalvars import gc
 from ..color import red,green,orange
 from ..color import red,green,orange
-from ..opts import opt
 from ..util import msg,msg_r
 from ..util import msg,msg_r
 
 
 import importlib
 import importlib
@@ -84,7 +83,7 @@ class TxInfo:
 				d = tx.dcoin,
 				d = tx.dcoin,
 				c = tx.coin )
 				c = tx.coin )
 
 
-			if opt.verbose:
+			if tx.cfg.verbose:
 				yield self.format_verbose_footer()
 				yield self.format_verbose_footer()
 
 
 		return ''.join(gen_view()) # TX label might contain non-ascii chars
 		return ''.join(gen_view()) # TX label might contain non-ascii chars

+ 35 - 35
mmgen/tx/new.py

@@ -12,13 +12,11 @@
 tx.new: new transaction class
 tx.new: new transaction class
 """
 """
 
 
-from ..globalvars import *
-from ..opts import opt
 from .base import Base
 from .base import Base
 from ..globalvars import gc
 from ..globalvars import gc
 from ..color import pink,yellow
 from ..color import pink,yellow
 from ..obj import get_obj,MMGenList
 from ..obj import get_obj,MMGenList
-from ..util import msg,qmsg,fmt,die,suf,remove_dups,get_extension
+from ..util import msg,fmt,die,suf,remove_dups,get_extension
 from ..addr import (
 from ..addr import (
 	is_mmgen_id,
 	is_mmgen_id,
 	MMGenAddrType,
 	MMGenAddrType,
@@ -28,7 +26,7 @@ from ..addr import (
 	is_addrlist_id
 	is_addrlist_id
 )
 )
 
 
-def mmaddr2coinaddr(mmaddr,ad_w,ad_f,proto):
+def mmaddr2coinaddr(cfg,mmaddr,ad_w,ad_f,proto):
 
 
 	def wmsg(k):
 	def wmsg(k):
 		messages = {
 		messages = {
@@ -59,7 +57,7 @@ def mmaddr2coinaddr(mmaddr,ad_w,ad_f,proto):
 			if coin_addr:
 			if coin_addr:
 				msg(wmsg('addr_in_addrfile_only'))
 				msg(wmsg('addr_in_addrfile_only'))
 				from ..ui import keypress_confirm
 				from ..ui import keypress_confirm
-				if not (opt.yes or keypress_confirm('Continue anyway?')):
+				if not (cfg.yes or keypress_confirm( cfg, 'Continue anyway?' )):
 					sys.exit(1)
 					sys.exit(1)
 			else:
 			else:
 				die(2,wmsg('addr_not_found'))
 				die(2,wmsg('addr_not_found'))
@@ -125,8 +123,8 @@ class New(Base):
 			if abs_fee:
 			if abs_fee:
 				prompt = '{} TX fee{}: {}{} {} ({} {})\n'.format(
 				prompt = '{} TX fee{}: {}{} {} ({} {})\n'.format(
 						desc,
 						desc,
-						(f' (after {opt.fee_adjust:.2f}X adjustment)'
-							if opt.fee_adjust != 1 and desc.startswith('Network-estimated')
+						(f' (after {self.cfg.fee_adjust:.2f}X adjustment)'
+							if self.cfg.fee_adjust != 1 and desc.startswith('Network-estimated')
 								else ''),
 								else ''),
 						('','≈')[self.fee_is_approximate],
 						('','≈')[self.fee_is_approximate],
 						abs_fee.hl(),
 						abs_fee.hl(),
@@ -134,11 +132,11 @@ class New(Base):
 						pink(str(self.fee_abs2rel(abs_fee))),
 						pink(str(self.fee_abs2rel(abs_fee))),
 						self.rel_fee_disp)
 						self.rel_fee_disp)
 				from ..ui import keypress_confirm
 				from ..ui import keypress_confirm
-				if opt.yes or keypress_confirm(prompt+'OK?',default_yes=True):
-					if opt.yes:
+				if self.cfg.yes or keypress_confirm( self.cfg, prompt+'OK?', default_yes=True ):
+					if self.cfg.yes:
 						msg(prompt)
 						msg(prompt)
 					return abs_fee
 					return abs_fee
-			fee = line_input(self.usr_fee_prompt)
+			fee = line_input( self.cfg, self.usr_fee_prompt )
 			desc = 'User-selected'
 			desc = 'User-selected'
 
 
 	# we don't know fee yet, so perform preliminary check with fee == 0
 	# we don't know fee yet, so perform preliminary check with fee == 0
@@ -153,19 +151,19 @@ class New(Base):
 
 
 	async def get_fee_from_user(self,have_estimate_fail=[]):
 	async def get_fee_from_user(self,have_estimate_fail=[]):
 
 
-		if opt.fee:
+		if self.cfg.fee:
 			desc = 'User-selected'
 			desc = 'User-selected'
-			start_fee = opt.fee
+			start_fee = self.cfg.fee
 		else:
 		else:
 			desc = 'Network-estimated ({}, {} conf{})'.format(
 			desc = 'Network-estimated ({}, {} conf{})'.format(
-				opt.fee_estimate_mode.upper(),
-				pink(str(opt.fee_estimate_confs)),
-				suf(opt.fee_estimate_confs) )
+				self.cfg.fee_estimate_mode.upper(),
+				pink(str(self.cfg.fee_estimate_confs)),
+				suf(self.cfg.fee_estimate_confs) )
 			fee_per_kb,fe_type = await self.get_rel_fee_from_network()
 			fee_per_kb,fe_type = await self.get_rel_fee_from_network()
 
 
 			if fee_per_kb < 0:
 			if fee_per_kb < 0:
 				if not have_estimate_fail:
 				if not have_estimate_fail:
-					msg(self.fee_fail_fs.format(c=opt.fee_estimate_confs,t=fe_type))
+					msg(self.fee_fail_fs.format(c=self.cfg.fee_estimate_confs,t=fe_type))
 					have_estimate_fail.append(True)
 					have_estimate_fail.append(True)
 				start_fee = None
 				start_fee = None
 			else:
 			else:
@@ -181,7 +179,7 @@ class New(Base):
 		arg,amt = arg_in.split(',',1) if ',' in arg_in else (arg_in,None)
 		arg,amt = arg_in.split(',',1) if ',' in arg_in else (arg_in,None)
 
 
 		if is_mmgen_id(self.proto,arg):
 		if is_mmgen_id(self.proto,arg):
-			coin_addr = mmaddr2coinaddr(arg,ad_w,ad_f,self.proto)
+			coin_addr = mmaddr2coinaddr(self.cfg,arg,ad_w,ad_f,self.proto)
 		elif is_coin_addr(self.proto,arg):
 		elif is_coin_addr(self.proto,arg):
 			coin_addr = CoinAddr(self.proto,arg)
 			coin_addr = CoinAddr(self.proto,arg)
 		elif is_mmgen_addrtype(self.proto,arg) or is_addrlist_id(self.proto,arg):
 		elif is_mmgen_addrtype(self.proto,arg) or is_addrlist_id(self.proto,arg):
@@ -189,7 +187,7 @@ class New(Base):
 				die(2,f'Change addresses not supported for {self.proto.name} protocol')
 				die(2,f'Change addresses not supported for {self.proto.name} protocol')
 
 
 			from ..tw.addresses import TwAddresses
 			from ..tw.addresses import TwAddresses
-			al = await TwAddresses(self.proto,get_data=True)
+			al = await TwAddresses(self.cfg,self.proto,get_data=True)
 
 
 			if is_mmgen_addrtype(self.proto,arg):
 			if is_mmgen_addrtype(self.proto,arg):
 				arg = MMGenAddrType(self.proto,arg)
 				arg = MMGenAddrType(self.proto,arg)
@@ -253,9 +251,9 @@ class New(Base):
 		from ..fileutil import check_infile
 		from ..fileutil import check_infile
 		for addrfile in addrfiles:
 		for addrfile in addrfiles:
 			check_infile(addrfile)
 			check_infile(addrfile)
-			ad_f.add(AddrList( self.proto, addrfile ))
+			ad_f.add(AddrList( self.cfg, self.proto, addrfile ))
 
 
-		ad_w = await TwAddrData(self.proto,twctl=self.twctl)
+		ad_w = await TwAddrData(self.cfg,self.proto,twctl=self.twctl)
 
 
 		await self.process_cmd_args(cmd_args,ad_f,ad_w)
 		await self.process_cmd_args(cmd_args,ad_f,ad_w)
 
 
@@ -271,6 +269,7 @@ class New(Base):
 	def confirm_autoselected_addr(self,chg):
 	def confirm_autoselected_addr(self,chg):
 		from ..ui import keypress_confirm
 		from ..ui import keypress_confirm
 		if not keypress_confirm(
 		if not keypress_confirm(
+				self.cfg,
 				'Using {a} as {b} address. OK?'.format(
 				'Using {a} as {b} address. OK?'.format(
 					a = chg.mmid.hl(),
 					a = chg.mmid.hl(),
 					b = 'single output' if len(self.outputs) == 1 else 'change' ),
 					b = 'single output' if len(self.outputs) == 1 else 'change' ),
@@ -279,9 +278,10 @@ class New(Base):
 
 
 	async def warn_chg_addr_used(self,chg):
 	async def warn_chg_addr_used(self,chg):
 		from ..tw.addresses import TwAddresses
 		from ..tw.addresses import TwAddresses
-		if (await TwAddresses(self.proto,get_data=True)).is_used(chg.addr):
+		if (await TwAddresses(self.cfg,self.proto,get_data=True)).is_used(chg.addr):
 			from ..ui import keypress_confirm
 			from ..ui import keypress_confirm
 			if not keypress_confirm(
 			if not keypress_confirm(
+					self.cfg,
 					'{a} {b} {c}\n{d}'.format(
 					'{a} {b} {c}\n{d}'.format(
 						a = yellow(f'Requested change address'),
 						a = yellow(f'Requested change address'),
 						b = (chg.mmid or chg.addr).hl(),
 						b = (chg.mmid or chg.addr).hl(),
@@ -297,7 +297,7 @@ class New(Base):
 		prompt = 'Enter a range or space-separated list of outputs to spend: '
 		prompt = 'Enter a range or space-separated list of outputs to spend: '
 		from ..ui import line_input
 		from ..ui import line_input
 		while True:
 		while True:
-			reply = line_input(prompt).strip()
+			reply = line_input( self.cfg, prompt ).strip()
 			if reply:
 			if reply:
 				from ..addrlist import AddrIdxList
 				from ..addrlist import AddrIdxList
 				selected = get_obj(AddrIdxList, fmt_str=','.join(reply.split()) )
 				selected = get_obj(AddrIdxList, fmt_str=','.join(reply.split()) )
@@ -315,7 +315,7 @@ class New(Base):
 			return idx + 1
 			return idx + 1
 
 
 		def get_uo_nums():
 		def get_uo_nums():
-			for addr in opt.inputs.split(','):
+			for addr in self.cfg.inputs.split(','):
 				if is_mmgen_id(self.proto,addr):
 				if is_mmgen_id(self.proto,addr):
 					attr = 'twmmid'
 					attr = 'twmmid'
 				elif is_coin_addr(self.proto,addr):
 				elif is_coin_addr(self.proto,addr):
@@ -354,7 +354,7 @@ class New(Base):
 	async def get_inputs_from_user(self,outputs_sum):
 	async def get_inputs_from_user(self,outputs_sum):
 
 
 		while True:
 		while True:
-			us_f = self.select_unspent_cmdline if opt.inputs else self.select_unspent
+			us_f = self.select_unspent_cmdline if self.cfg.inputs else self.select_unspent
 			sel_nums = us_f(self.twuo.data)
 			sel_nums = us_f(self.twuo.data)
 
 
 			msg(f'Selected output{suf(sel_nums)}: {{}}'.format(' '.join(str(n) for n in sel_nums)))
 			msg(f'Selected output{suf(sel_nums)}: {{}}'.format(' '.join(str(n) for n in sel_nums)))
@@ -373,8 +373,8 @@ class New(Base):
 			if funds_left >= 0:
 			if funds_left >= 0:
 				p = self.final_inputs_ok_msg(funds_left)
 				p = self.final_inputs_ok_msg(funds_left)
 				from ..ui import keypress_confirm
 				from ..ui import keypress_confirm
-				if opt.yes or keypress_confirm(p+'. OK?',default_yes=True):
-					if opt.yes:
+				if self.cfg.yes or keypress_confirm( self.cfg, p+'. OK?', default_yes=True ):
+					if self.cfg.yes:
 						msg(p)
 						msg(p)
 					return funds_left
 					return funds_left
 			else:
 			else:
@@ -386,21 +386,21 @@ class New(Base):
 
 
 		from ..tw.unspent import TwUnspentOutputs
 		from ..tw.unspent import TwUnspentOutputs
 
 
-		if opt.comment_file:
-			self.add_comment(opt.comment_file)
+		if self.cfg.comment_file:
+			self.add_comment(self.cfg.comment_file)
 
 
 		twuo_addrs = await self.get_input_addrs_from_cmdline()
 		twuo_addrs = await self.get_input_addrs_from_cmdline()
 
 
-		self.twuo = await TwUnspentOutputs(self.proto,minconf=opt.minconf,addrs=twuo_addrs)
+		self.twuo = await TwUnspentOutputs(self.cfg,self.proto,minconf=self.cfg.minconf,addrs=twuo_addrs)
 		await self.twuo.get_data()
 		await self.twuo.get_data()
 
 
 		if not do_info:
 		if not do_info:
 			await self.get_outputs_from_cmdline(cmd_args)
 			await self.get_outputs_from_cmdline(cmd_args)
 
 
 		from ..ui import do_license_msg
 		from ..ui import do_license_msg
-		do_license_msg()
+		do_license_msg(self.cfg)
 
 
-		if not opt.inputs:
+		if not self.cfg.inputs:
 			await self.twuo.view_filter_and_sort()
 			await self.twuo.view_filter_and_sort()
 
 
 		self.twuo.display_total()
 		self.twuo.display_total()
@@ -422,7 +422,7 @@ class New(Base):
 
 
 		self.update_change_output(funds_left)
 		self.update_change_output(funds_left)
 
 
-		if not opt.yes:
+		if not self.cfg.yes:
 			self.add_comment()  # edits an existing comment
 			self.add_comment()  # edits an existing comment
 
 
 		await self.create_serialized(locktime=locktime) # creates self.txid too
 		await self.create_serialized(locktime=locktime) # creates self.txid too
@@ -432,12 +432,12 @@ class New(Base):
 		self.chain = self.proto.chain_name
 		self.chain = self.proto.chain_name
 		self.check_fee()
 		self.check_fee()
 
 
-		qmsg('Transaction successfully created')
+		self.cfg._util.qmsg('Transaction successfully created')
 
 
 		from . import UnsignedTX
 		from . import UnsignedTX
-		new = UnsignedTX(data=self.__dict__)
+		new = UnsignedTX(cfg=self.cfg,data=self.__dict__)
 
 
-		if not opt.yes:
+		if not self.cfg.yes:
 			new.info.view_with_prompt('View transaction details?')
 			new.info.view_with_prompt('View transaction details?')
 
 
 		del new.twuo.twctl
 		del new.twuo.twctl

+ 3 - 3
mmgen/tx/online.py

@@ -22,11 +22,11 @@ class OnlineSigned(Signed):
 		return _base_proto_subclass('Status','status',self.proto)(self)
 		return _base_proto_subclass('Status','status',self.proto)(self)
 
 
 	def confirm_send(self):
 	def confirm_send(self):
-		from ..opts import opt
 		from ..util import msg
 		from ..util import msg
 		from ..ui import confirm_or_raise
 		from ..ui import confirm_or_raise
 		confirm_or_raise(
 		confirm_or_raise(
-			message = '' if opt.quiet else 'Once this transaction is sent, there’s no taking it back!',
+			cfg     = self.cfg,
+			message = '' if self.cfg.quiet else 'Once this transaction is sent, there’s no taking it back!',
 			action  = f'broadcast this transaction to the {self.proto.coin} {self.proto.network.upper()} network',
 			action  = f'broadcast this transaction to the {self.proto.coin} {self.proto.network.upper()} network',
-			expect  = 'YES' if opt.quiet or opt.yes else 'YES, I REALLY WANT TO DO THIS' )
+			expect  = 'YES' if self.cfg.quiet or self.cfg.yes else 'YES, I REALLY WANT TO DO THIS' )
 		msg('Sending transaction')
 		msg('Sending transaction')

+ 28 - 24
mmgen/tx/sign.py

@@ -20,9 +20,8 @@
 tx.sign: Sign a transaction generated by 'mmgen-txcreate'
 tx.sign: Sign a transaction generated by 'mmgen-txcreate'
 """
 """
 
 
-from ..globalvars import g,gc
-from ..opts import opt
-from ..util import msg,vmsg,qmsg,suf,fmt,die,remove_dups,get_extension
+from ..globalvars import gc
+from ..util import msg,suf,fmt,die,remove_dups,get_extension
 from ..obj import MMGenList
 from ..obj import MMGenList
 from ..addr import MMGenAddrType
 from ..addr import MMGenAddrType
 from ..addrlist import AddrIdxList,KeyAddrList
 from ..addrlist import AddrIdxList,KeyAddrList
@@ -38,14 +37,14 @@ def get_seed_for_seed_id(sid,infiles,saved_seeds):
 	subseeds_checked = False
 	subseeds_checked = False
 	while True:
 	while True:
 		if infiles:
 		if infiles:
-			seed = Wallet(infiles.pop(0),ignore_in_fmt=True).seed
+			seed = Wallet(cfg,infiles.pop(0),ignore_in_fmt=True).seed
 		elif subseeds_checked == False:
 		elif subseeds_checked == False:
 			seed = saved_seeds[list(saved_seeds)[0]].subseed_by_seed_id(sid,print_msg=True)
 			seed = saved_seeds[list(saved_seeds)[0]].subseed_by_seed_id(sid,print_msg=True)
 			subseeds_checked = True
 			subseeds_checked = True
 			if not seed: continue
 			if not seed: continue
-		elif opt.in_fmt:
-			qmsg(f'Need seed data for Seed ID {sid}')
-			seed = Wallet().seed
+		elif cfg.in_fmt:
+			cfg._util.qmsg(f'Need seed data for Seed ID {sid}')
+			seed = Wallet(cfg).seed
 			msg(f'User input produced Seed ID {seed.sid}')
 			msg(f'User input produced Seed ID {seed.sid}')
 			if not seed.sid == sid: # TODO: add test
 			if not seed.sid == sid: # TODO: add test
 				seed = seed.subseed_by_seed_id(sid,print_msg=True)
 				seed = seed.subseed_by_seed_id(sid,print_msg=True)
@@ -60,7 +59,7 @@ def get_seed_for_seed_id(sid,infiles,saved_seeds):
 def generate_kals_for_mmgen_addrs(need_keys,infiles,saved_seeds,proto):
 def generate_kals_for_mmgen_addrs(need_keys,infiles,saved_seeds,proto):
 	mmids = [e.mmid for e in need_keys]
 	mmids = [e.mmid for e in need_keys]
 	sids = remove_dups((i.sid for i in mmids),quiet=True)
 	sids = remove_dups((i.sid for i in mmids),quiet=True)
-	vmsg(f"Need seed{suf(sids)}: {' '.join(sids)}")
+	cfg._util.vmsg(f"Need seed{suf(sids)}: {' '.join(sids)}")
 	def gen_kals():
 	def gen_kals():
 		for sid in sids:
 		for sid in sids:
 			# Returns only if seed is found
 			# Returns only if seed is found
@@ -69,6 +68,7 @@ def generate_kals_for_mmgen_addrs(need_keys,infiles,saved_seeds,proto):
 				idx_list = [i.idx for i in mmids if i.sid == sid and i.mmtype == id_str]
 				idx_list = [i.idx for i in mmids if i.sid == sid and i.mmtype == id_str]
 				if idx_list:
 				if idx_list:
 					yield KeyAddrList(
 					yield KeyAddrList(
+						cfg       = cfg,
 						proto     = proto,
 						proto     = proto,
 						seed      = seed,
 						seed      = seed,
 						addr_idxs = AddrIdxList(idx_list=idx_list),
 						addr_idxs = AddrIdxList(idx_list=idx_list),
@@ -83,7 +83,7 @@ def add_keys(tx,src,infiles=None,saved_seeds=None,keyaddr_list=None):
 	desc,src_desc = (
 	desc,src_desc = (
 		('key-address file','From key-address file:') if keyaddr_list else
 		('key-address file','From key-address file:') if keyaddr_list else
 		('seed(s)','Generated from seed:') )
 		('seed(s)','Generated from seed:') )
-	qmsg(f'Checking {gc.proj_name} -> {tx.proto.coin} address mappings for {src} (from {desc})')
+	cfg._util.qmsg(f'Checking {gc.proj_name} -> {tx.proto.coin} address mappings for {src} (from {desc})')
 	d = (
 	d = (
 		MMGenList([keyaddr_list]) if keyaddr_list else
 		MMGenList([keyaddr_list]) if keyaddr_list else
 		generate_kals_for_mmgen_addrs(need_keys,infiles,saved_seeds,tx.proto) )
 		generate_kals_for_mmgen_addrs(need_keys,infiles,saved_seeds,tx.proto) )
@@ -104,52 +104,56 @@ def add_keys(tx,src,infiles=None,saved_seeds=None,keyaddr_list=None):
 							{{'tx file:':<23}} {{e.mmid}} -> {{e.addr}}
 							{{'tx file:':<23}} {{e.mmid}} -> {{e.addr}}
 							""").strip())
 							""").strip())
 	if new_keys:
 	if new_keys:
-		vmsg(f'Added {len(new_keys)} wif key{suf(new_keys)} from {desc}')
+		cfg._util.vmsg(f'Added {len(new_keys)} wif key{suf(new_keys)} from {desc}')
 	return new_keys
 	return new_keys
 
 
 def _pop_matching_fns(args,cmplist): # strips found args
 def _pop_matching_fns(args,cmplist): # strips found args
 	return list(reversed([args.pop(args.index(a)) for a in reversed(args) if get_extension(a) in cmplist]))
 	return list(reversed([args.pop(args.index(a)) for a in reversed(args) if get_extension(a) in cmplist]))
 
 
-def get_tx_files(opt,args):
+def get_tx_files(cfg,args):
 	from .unsigned import Unsigned
 	from .unsigned import Unsigned
 	ret = _pop_matching_fns(args,[Unsigned.ext])
 	ret = _pop_matching_fns(args,[Unsigned.ext])
 	if not ret:
 	if not ret:
 		die(1,'You must specify a raw transaction file!')
 		die(1,'You must specify a raw transaction file!')
 	return ret
 	return ret
 
 
-def get_seed_files(opt,args):
+def get_seed_files(cfg,args):
 	# favor unencrypted seed sources first, as they don't require passwords
 	# favor unencrypted seed sources first, as they don't require passwords
 	ret = _pop_matching_fns( args, get_wallet_extensions('unenc') )
 	ret = _pop_matching_fns( args, get_wallet_extensions('unenc') )
 	from ..filename import find_file_in_dir
 	from ..filename import find_file_in_dir
-	wf = find_file_in_dir(get_wallet_cls('mmgen'),g.data_dir) # Make this the first encrypted ss in the list
+	wf = find_file_in_dir(get_wallet_cls('mmgen'),cfg.data_dir) # Make this the first encrypted ss in the list
 	if wf:
 	if wf:
 		ret.append(wf)
 		ret.append(wf)
 	ret += _pop_matching_fns( args, get_wallet_extensions('enc') )
 	ret += _pop_matching_fns( args, get_wallet_extensions('enc') )
-	if not (ret or opt.mmgen_keys_from_file or opt.keys_from_file): # or opt.use_wallet_dat
+	if not (ret or cfg.mmgen_keys_from_file or cfg.keys_from_file): # or cfg.use_wallet_dat
 		die(1,'You must specify a seed or key source!')
 		die(1,'You must specify a seed or key source!')
 	return ret
 	return ret
 
 
-def get_keyaddrlist(proto,opt):
-	if opt.mmgen_keys_from_file:
-		return KeyAddrList(proto,opt.mmgen_keys_from_file)
+def get_keyaddrlist(cfg,proto):
+	if cfg.mmgen_keys_from_file:
+		return KeyAddrList( cfg, proto, cfg.mmgen_keys_from_file )
 	return None
 	return None
 
 
-def get_keylist(proto,opt):
-	if opt.keys_from_file:
+def get_keylist(cfg,proto):
+	if cfg.keys_from_file:
 		from ..fileutil import get_lines_from_file
 		from ..fileutil import get_lines_from_file
-		return get_lines_from_file(opt.keys_from_file,'key-address data',trim_comments=True)
+		return get_lines_from_file( cfg, cfg.keys_from_file, 'key-address data', trim_comments=True )
 	return None
 	return None
 
 
-async def txsign(tx,seed_files,kl,kal,tx_num_str=''):
+async def txsign(cfg_parm,tx,seed_files,kl,kal,tx_num_str=''):
 
 
 	keys = MMGenList() # list of AddrListEntry objects
 	keys = MMGenList() # list of AddrListEntry objects
 	non_mmaddrs = tx.get_non_mmaddrs('inputs')
 	non_mmaddrs = tx.get_non_mmaddrs('inputs')
 
 
+	global cfg
+	cfg = cfg_parm
+
 	if non_mmaddrs:
 	if non_mmaddrs:
 		tx.check_non_mmgen_inputs(caller='txsign',non_mmaddrs=non_mmaddrs)
 		tx.check_non_mmgen_inputs(caller='txsign',non_mmaddrs=non_mmaddrs)
 		tmp = KeyAddrList(
 		tmp = KeyAddrList(
-			proto = tx.proto,
-			addrlist = non_mmaddrs,
+			cfg         = cfg,
+			proto       = tx.proto,
+			addrlist    = non_mmaddrs,
 			skip_chksum = True )
 			skip_chksum = True )
 		if kl:
 		if kl:
 			tmp.add_wifs(kl)
 			tmp.add_wifs(kl)
@@ -162,7 +166,7 @@ async def txsign(tx,seed_files,kl,kal,tx_num_str=''):
 				sep + sep.join(missing) ))
 				sep + sep.join(missing) ))
 		keys += tmp.data
 		keys += tmp.data
 
 
-	if opt.mmgen_keys_from_file:
+	if cfg.mmgen_keys_from_file:
 		keys += add_keys(tx,'inputs',keyaddr_list=kal)
 		keys += add_keys(tx,'inputs',keyaddr_list=kal)
 		add_keys(tx,'outputs',keyaddr_list=kal)
 		add_keys(tx,'outputs',keyaddr_list=kal)
 
 

+ 16 - 15
mmgen/ui.py

@@ -14,32 +14,32 @@ ui: Interactive user interface functions for the MMGen suite
 
 
 import sys,os
 import sys,os
 
 
-from .globalvars import g,gc
-from .opts import opt
-from .util import msg,msg_r,Msg,dmsg,die
+from .globalvars import gc
+from .util import msg,msg_r,Msg,die
 
 
-def confirm_or_raise(message,action,expect='YES',exit_msg='Exiting at user request'):
+def confirm_or_raise(cfg,message,action,expect='YES',exit_msg='Exiting at user request'):
 	if message:
 	if message:
 		msg(message)
 		msg(message)
 	if line_input(
 	if line_input(
+			cfg,
 			(f'{action}  ' if action[0].isupper() else f'Are you sure you want to {action}?\n') +
 			(f'{action}  ' if action[0].isupper() else f'Are you sure you want to {action}?\n') +
 			f'Type uppercase {expect!r} to confirm: '
 			f'Type uppercase {expect!r} to confirm: '
 		).strip() != expect:
 		).strip() != expect:
 		die( 'UserNonConfirmation', exit_msg )
 		die( 'UserNonConfirmation', exit_msg )
 
 
-def get_words_from_user(prompt):
-	words = line_input(prompt, echo=opt.echo_passphrase).split()
-	if g.debug:
+def get_words_from_user(cfg,prompt):
+	words = line_input( cfg, prompt, echo=cfg.echo_passphrase ).split()
+	if cfg.debug:
 		msg('Sanitized input: [{}]'.format(' '.join(words)))
 		msg('Sanitized input: [{}]'.format(' '.join(words)))
 	return words
 	return words
 
 
-def get_data_from_user(desc='data'): # user input MUST be UTF-8
-	data = line_input(f'Enter {desc}: ',echo=opt.echo_passphrase)
-	if g.debug:
+def get_data_from_user(cfg,desc='data'): # user input MUST be UTF-8
+	data = line_input( cfg, f'Enter {desc}: ', echo=cfg.echo_passphrase )
+	if cfg.debug:
 		msg(f'User input: [{data}]')
 		msg(f'User input: [{data}]')
 	return data
 	return data
 
 
-def line_input(prompt,echo=True,insert_txt='',hold_protect=True):
+def line_input(cfg,prompt,echo=True,insert_txt='',hold_protect=True):
 	"""
 	"""
 	multi-line prompts OK
 	multi-line prompts OK
 	one-line prompts must begin at beginning of line
 	one-line prompts must begin at beginning of line
@@ -62,7 +62,7 @@ def line_input(prompt,echo=True,insert_txt='',hold_protect=True):
 		from .term import kb_hold_protect
 		from .term import kb_hold_protect
 		kb_hold_protect()
 		kb_hold_protect()
 
 
-	if g.test_suite_popen_spawn:
+	if cfg.test_suite_popen_spawn:
 		msg(prompt)
 		msg(prompt)
 		sys.stderr.flush()
 		sys.stderr.flush()
 		reply = os.read(0,4096).decode().rstrip('\n') # strip NL to mimic behavior of input()
 		reply = os.read(0,4096).decode().rstrip('\n') # strip NL to mimic behavior of input()
@@ -83,6 +83,7 @@ def line_input(prompt,echo=True,insert_txt='',hold_protect=True):
 	return reply.strip()
 	return reply.strip()
 
 
 def keypress_confirm(
 def keypress_confirm(
+	cfg,
 	prompt,
 	prompt,
 	default_yes     = False,
 	default_yes     = False,
 	verbose         = False,
 	verbose         = False,
@@ -94,7 +95,7 @@ def keypress_confirm(
 
 
 	nl = f'\r{" "*len(prompt)}\r' if no_nl else '\n'
 	nl = f'\r{" "*len(prompt)}\r' if no_nl else '\n'
 
 
-	if g.accept_defaults:
+	if cfg.accept_defaults:
 		msg(prompt)
 		msg(prompt)
 		return default_yes
 		return default_yes
 
 
@@ -136,9 +137,9 @@ def do_pager(text):
 		Msg(text+end_msg)
 		Msg(text+end_msg)
 	set_vt100()
 	set_vt100()
 
 
-def do_license_msg(immed=False):
+def do_license_msg(cfg,immed=False):
 
 
-	if opt.quiet or g.no_license or opt.yes or not g.stdin_tty:
+	if cfg.quiet or cfg.no_license or cfg.yes or not cfg.stdin_tty:
 		return
 		return
 
 
 	import mmgen.contrib.license as gpl
 	import mmgen.contrib.license as gpl

+ 56 - 57
mmgen/util.py

@@ -23,8 +23,7 @@ util: Frequently-used variables, classes and utility functions for the MMGen sui
 import sys,os,time,re
 import sys,os,time,re
 
 
 from .color import *
 from .color import *
-from .globalvars import g,gc,gv
-from .opts import opt
+from .globalvars import gv,gc
 
 
 ascii_lowercase = 'abcdefghijklmnopqrstuvwxyz'
 ascii_lowercase = 'abcdefghijklmnopqrstuvwxyz'
 
 
@@ -32,6 +31,61 @@ hexdigits = '0123456789abcdefABCDEF'
 hexdigits_uc = '0123456789ABCDEF'
 hexdigits_uc = '0123456789ABCDEF'
 hexdigits_lc = '0123456789abcdef'
 hexdigits_lc = '0123456789abcdef'
 
 
+def noop(*args,**kwargs):
+	pass
+
+class Util:
+
+	def __init__(self,cfg):
+
+		self.cfg = cfg
+
+		if cfg.quiet:
+			self.qmsg = self.qmsg_r = noop
+		else:
+			self.qmsg = msg
+			self.qmsg_r = msg_r
+
+		if cfg.verbose:
+			self.vmsg = msg
+			self.vmsg_r = msg_r
+			self.Vmsg = Msg
+			self.Vmsg_r = Msg_r
+		else:
+			self.vmsg = self.vmsg_r = self.Vmsg = self.Vmsg_r = noop
+
+		self.dmsg = msg if cfg.debug else noop
+
+		if cfg.pager:
+			from .ui import do_pager
+			self.stdout_or_pager = do_pager
+		else:
+			self.stdout_or_pager = Msg_r
+
+	def compare_chksums(self,chk1,desc1,chk2,desc2,hdr='',die_on_fail=False,verbose=False):
+
+		if not chk1 == chk2:
+			fs = "{} ERROR: {} checksum ({}) doesn't match {} checksum ({})"
+			m = fs.format((hdr+':\n   ' if hdr else 'CHECKSUM'),desc2,chk2,desc1,chk1)
+			if die_on_fail:
+				die(3,m)
+			else:
+				if verbose or self.cfg.verbose:
+					msg(m)
+				return False
+
+		if self.cfg.verbose:
+			msg(f'{capfirst(desc1)} checksum OK ({chk1})')
+
+		return True
+
+	def compare_or_die(self, val1, desc1, val2, desc2, e='Error'):
+		if val1 != val2:
+			die(3,f"{e}: {desc2} ({val2}) doesn't match {desc1} ({val1})")
+		if self.cfg.debug:
+			msg(f'{capfirst(desc2)} OK ({val2})')
+		return True
+
 if gc.platform == 'win':
 if gc.platform == 'win':
 	def msg_r(s):
 	def msg_r(s):
 		try:
 		try:
@@ -85,34 +139,6 @@ def bmsg(s):
 def pumsg(s):
 def pumsg(s):
 	msg(purple(s))
 	msg(purple(s))
 
 
-def qmsg(s):
-	if not opt.quiet:
-		msg(s)
-
-def qmsg_r(s):
-	if not opt.quiet:
-		msg_r(s)
-
-def vmsg(s,force=False):
-	if opt.verbose or force:
-		msg(s)
-
-def vmsg_r(s,force=False):
-	if opt.verbose or force:
-		msg_r(s)
-
-def Vmsg(s,force=False):
-	if opt.verbose or force:
-		Msg(s)
-
-def Vmsg_r(s,force=False):
-	if opt.verbose or force:
-		Msg_r(s)
-
-def dmsg(s):
-	if opt.debug:
-		msg(s)
-
 def mmsg(*args):
 def mmsg(*args):
 	for d in args:
 	for d in args:
 		Msg(repr(d))
 		Msg(repr(d))
@@ -319,26 +345,6 @@ def strip_comments(lines):
 	pat = re.compile('#.*')
 	pat = re.compile('#.*')
 	return [m for m in [pat.sub('',l).rstrip() for l in lines] if m != '']
 	return [m for m in [pat.sub('',l).rstrip() for l in lines] if m != '']
 
 
-def compare_chksums(chk1,desc1,chk2,desc2,hdr='',die_on_fail=False,verbose=False):
-
-	if not chk1 == chk2:
-		fs = "{} ERROR: {} checksum ({}) doesn't match {} checksum ({})"
-		m = fs.format((hdr+':\n   ' if hdr else 'CHECKSUM'),desc2,chk2,desc1,chk1)
-		if die_on_fail:
-			die(3,m)
-		else:
-			vmsg(m,force=verbose)
-			return False
-
-	vmsg(f'{capfirst(desc1)} checksum OK ({chk1})')
-	return True
-
-def compare_or_die(val1, desc1, val2, desc2, e='Error'):
-	if val1 != val2:
-		die(3,f"{e}: {desc2} ({val2}) doesn't match {desc1} ({val1})")
-	dmsg(f'{capfirst(desc2)} OK ({val2})')
-	return True
-
 def make_full_path(outdir,outfile):
 def make_full_path(outdir,outfile):
 	return os.path.normpath(os.path.join(outdir, os.path.basename(outfile)))
 	return os.path.normpath(os.path.join(outdir, os.path.basename(outfile)))
 
 
@@ -377,13 +383,6 @@ class oneshot_warning_group(oneshot_warning):
 	def __init__(self,wcls,div=None,fmt_args=[],reverse=False):
 	def __init__(self,wcls,div=None,fmt_args=[],reverse=False):
 		self.do(getattr(self,wcls),div,fmt_args,reverse)
 		self.do(getattr(self,wcls),div,fmt_args,reverse)
 
 
-def stdout_or_pager(s):
-	if opt.pager:
-		from .ui import do_pager
-		do_pager(s)
-	else:
-		Msg_r(s)
-
 def get_subclasses(cls,names=False):
 def get_subclasses(cls,names=False):
 	def gen(cls):
 	def gen(cls):
 		for i in cls.__subclasses__():
 		for i in cls.__subclasses__():

+ 5 - 5
mmgen/util2.py

@@ -13,7 +13,7 @@ util2: Less frequently-used variables, classes and utility functions for the MMG
 """
 """
 
 
 import re,time
 import re,time
-from .util import msg,qmsg,suf,hexdigits
+from .util import msg,suf,hexdigits
 
 
 def die_wait(delay,ev=0,s=''):
 def die_wait(delay,ev=0,s=''):
 	assert isinstance(delay,int)
 	assert isinstance(delay,int)
@@ -36,12 +36,12 @@ def removeprefix(s,pfx): # workaround for pre-Python 3.9
 def removesuffix(s,sfx): # workaround for pre-Python 3.9
 def removesuffix(s,sfx): # workaround for pre-Python 3.9
 	return s[:-len(sfx)] if s.endswith(sfx) else s
 	return s[:-len(sfx)] if s.endswith(sfx) else s
 
 
-def get_keccak(cached_ret=[]):
+# called with no arguments by pyethereum.utils:
+def get_keccak(cfg=None,cached_ret=[]):
 
 
 	if not cached_ret:
 	if not cached_ret:
-		from .opts import opt
-		if getattr(opt,'use_internal_keccak_module',False):
-			qmsg('Using internal keccak module by user request')
+		if cfg and cfg.use_internal_keccak_module:
+			cfg._util.qmsg('Using internal keccak module by user request')
 			from .contrib.keccak import keccak_256
 			from .contrib.keccak import keccak_256
 		else:
 		else:
 			try:
 			try:

+ 10 - 9
mmgen/wallet/__init__.py

@@ -15,8 +15,6 @@ wallet.__init__: wallet class initializer
 import importlib
 import importlib
 from collections import namedtuple
 from collections import namedtuple
 
 
-from ..globalvars import g
-from ..opts import opt
 from ..util import die,get_extension
 from ..util import die,get_extension
 from ..objmethods import MMGenObject
 from ..objmethods import MMGenObject
 from ..seed import Seed
 from ..seed import Seed
@@ -101,6 +99,7 @@ def _get_me(modname):
 	return MMGenObject.__new__( getattr( importlib.import_module(f'mmgen.wallet.{modname}'), 'wallet' ) )
 	return MMGenObject.__new__( getattr( importlib.import_module(f'mmgen.wallet.{modname}'), 'wallet' ) )
 
 
 def Wallet(
 def Wallet(
+	cfg,
 	fn            = None,
 	fn            = None,
 	ss            = None,
 	ss            = None,
 	seed_bin      = None,
 	seed_bin      = None,
@@ -111,31 +110,31 @@ def Wallet(
 	in_fmt        = None,
 	in_fmt        = None,
 	passwd_file   = None ):
 	passwd_file   = None ):
 
 
-	in_fmt = in_fmt or opt.in_fmt
+	in_fmt = in_fmt or cfg.in_fmt
 
 
 	ss_out = (
 	ss_out = (
 		get_wallet_data(
 		get_wallet_data(
-			fmt_code    = opt.out_fmt,
+			fmt_code    = cfg.out_fmt,
 			die_on_fail = True ).type
 			die_on_fail = True ).type
-		if opt.out_fmt else None )
+		if cfg.out_fmt else None )
 
 
 	if seed or seed_bin:
 	if seed or seed_bin:
 		me = _get_me( ss_out or 'mmgen' ) # default to native wallet format
 		me = _get_me( ss_out or 'mmgen' ) # default to native wallet format
-		me.seed = seed or Seed(seed_bin=seed_bin)
+		me.seed = seed or Seed( cfg, seed_bin=seed_bin )
 		me.op = 'new'
 		me.op = 'new'
 	elif ss:
 	elif ss:
 		me = _get_me( ss.type if passchg else (ss_out or 'mmgen') )
 		me = _get_me( ss.type if passchg else (ss_out or 'mmgen') )
 		me.seed = ss.seed
 		me.seed = ss.seed
 		me.ss_in = ss
 		me.ss_in = ss
 		me.op = 'pwchg_new' if passchg else 'conv'
 		me.op = 'pwchg_new' if passchg else 'conv'
-	elif fn or opt.hidden_incog_input_params:
+	elif fn or cfg.hidden_incog_input_params:
 		if fn:
 		if fn:
 			wd = get_wallet_data(ext=get_extension(fn),die_on_fail=True)
 			wd = get_wallet_data(ext=get_extension(fn),die_on_fail=True)
 			if in_fmt and (not ignore_in_fmt) and in_fmt not in wd.fmt_codes:
 			if in_fmt and (not ignore_in_fmt) and in_fmt not in wd.fmt_codes:
 				die(1,f'{in_fmt}: --in-fmt parameter does not match extension of input file')
 				die(1,f'{in_fmt}: --in-fmt parameter does not match extension of input file')
 			me = _get_me( wd.type )
 			me = _get_me( wd.type )
 		else:
 		else:
-			fn = ','.join(opt.hidden_incog_input_params.split(',')[:-1]) # permit comma in filename
+			fn = ','.join(cfg.hidden_incog_input_params.split(',')[:-1]) # permit comma in filename
 			me = _get_me( 'incog_hidden' )
 			me = _get_me( 'incog_hidden' )
 		from ..filename import MMGenFile
 		from ..filename import MMGenFile
 		me.infile = MMGenFile( fn, subclass=type(me) )
 		me.infile = MMGenFile( fn, subclass=type(me) )
@@ -145,9 +144,11 @@ def Wallet(
 		me.op = 'pwchg_old' if passchg else 'old'
 		me.op = 'pwchg_old' if passchg else 'old'
 	else: # called with no arguments: initialize with random seed
 	else: # called with no arguments: initialize with random seed
 		me = _get_me( ss_out or 'mmgen' ) # default to native wallet format
 		me = _get_me( ss_out or 'mmgen' ) # default to native wallet format
-		me.seed = Seed()
+		me.seed = Seed(cfg)
 		me.op = 'new'
 		me.op = 'new'
 
 
+	me.cfg = cfg
+
 	me.__init__(
 	me.__init__(
 		in_data     = in_data,
 		in_data     = in_data,
 		passwd_file = passwd_file )
 		passwd_file = passwd_file )

+ 10 - 10
mmgen/wallet/base.py

@@ -14,9 +14,7 @@ wallet.base: wallet base class
 
 
 import os
 import os
 
 
-from ..globalvars import g
-from ..opts import opt
-from ..util import msg,qmsg,die
+from ..util import msg,die
 from ..objmethods import MMGenObject
 from ..objmethods import MMGenObject
 from . import Wallet,wallet_data,get_wallet_cls
 from . import Wallet,wallet_data,get_wallet_cls
 
 
@@ -45,7 +43,7 @@ class wallet(MMGenObject,metaclass=WalletMeta):
 		in_data       = None,
 		in_data       = None,
 		passwd_file   = None ):
 		passwd_file   = None ):
 
 
-		self.passwd_file = passwd_file or opt.passwd_file
+		self.passwd_file = passwd_file or self.cfg.passwd_file
 		self.ssdata = self.WalletData()
 		self.ssdata = self.WalletData()
 		self.msg = {}
 		self.msg = {}
 		self.in_data = in_data
 		self.in_data = in_data
@@ -57,7 +55,7 @@ class wallet(MMGenObject,metaclass=WalletMeta):
 		if hasattr(self,'seed'):
 		if hasattr(self,'seed'):
 			self._encrypt()
 			self._encrypt()
 			return
 			return
-		elif hasattr(self,'infile') or self.in_data or not g.stdin_tty:
+		elif hasattr(self,'infile') or self.in_data or not self.cfg.stdin_tty:
 			self._deformat_once()
 			self._deformat_once()
 			self._decrypt_retry()
 			self._decrypt_retry()
 		else:
 		else:
@@ -66,7 +64,7 @@ class wallet(MMGenObject,metaclass=WalletMeta):
 			self._deformat_retry()
 			self._deformat_retry()
 			self._decrypt_retry()
 			self._decrypt_retry()
 
 
-		qmsg('Valid {} for Seed ID {}{}'.format(
+		self.cfg._util.qmsg('Valid {} for Seed ID {}{}'.format(
 			self.desc,
 			self.desc,
 			self.seed.sid.hl(),
 			self.seed.sid.hl(),
 			(f', seed length {self.seed.bitlen}' if self.seed.bitlen != 256 else '')
 			(f', seed length {self.seed.bitlen}' if self.seed.bitlen != 256 else '')
@@ -76,6 +74,7 @@ class wallet(MMGenObject,metaclass=WalletMeta):
 		if hasattr(self,'infile'):
 		if hasattr(self,'infile'):
 			from ..fileutil import get_data_from_file
 			from ..fileutil import get_data_from_file
 			self.fmt_data = get_data_from_file(
 			self.fmt_data = get_data_from_file(
+				self.cfg,
 				self.infile.name,
 				self.infile.name,
 				self.desc,
 				self.desc,
 				binary = self.file_mode=='binary' )
 				binary = self.file_mode=='binary' )
@@ -86,7 +85,7 @@ class wallet(MMGenObject,metaclass=WalletMeta):
 
 
 	def _get_data_from_user(self,desc):
 	def _get_data_from_user(self,desc):
 		from ..ui import get_data_from_user
 		from ..ui import get_data_from_user
-		return get_data_from_user(desc)
+		return get_data_from_user( self.cfg, desc )
 
 
 	def _deformat_once(self):
 	def _deformat_once(self):
 		self._get_data()
 		self._get_data()
@@ -118,16 +117,17 @@ class wallet(MMGenObject,metaclass=WalletMeta):
 		}
 		}
 
 
 		if outdir:
 		if outdir:
-			# write_data_to_file(): outfile with absolute path overrides opt.outdir
+			# write_data_to_file(): outfile with absolute path overrides self.cfg.outdir
 			of = os.path.abspath(os.path.join(outdir,self._filename()))
 			of = os.path.abspath(os.path.join(outdir,self._filename()))
 
 
 		from ..fileutil import write_data_to_file
 		from ..fileutil import write_data_to_file
 		write_data_to_file(
 		write_data_to_file(
+			self.cfg,
 			of if outdir else self._filename(),
 			of if outdir else self._filename(),
 			self.fmt_data,
 			self.fmt_data,
 			**kwargs )
 			**kwargs )
 
 
 	def check_usr_seed_len(self,bitlen=None):
 	def check_usr_seed_len(self,bitlen=None):
 		chk = bitlen or self.seed.bitlen
 		chk = bitlen or self.seed.bitlen
-		if opt.seed_len and opt.seed_len != chk:
-			die(1,f'ERROR: requested seed length ({opt.seed_len}) doesn’t match seed length of source ({chk})')
+		if self.cfg.seed_len and self.cfg.seed_len != chk:
+			die(1,f'ERROR: requested seed length ({self.cfg.seed_len}) doesn’t match seed length of source ({chk})')

+ 10 - 11
mmgen/wallet/brain.py

@@ -12,8 +12,7 @@
 wallet.brain: brainwallet wallet class
 wallet.brain: brainwallet wallet class
 """
 """
 
 
-from ..opts import opt
-from ..util import msg,qmsg,qmsg_r
+from ..util import msg
 from ..color import yellow
 from ..color import yellow
 from .enc import wallet
 from .enc import wallet
 from .seed import Seed
 from .seed import Seed
@@ -26,7 +25,7 @@ class wallet(wallet):
 
 
 	def get_bw_params(self):
 	def get_bw_params(self):
 		# already checked
 		# already checked
-		a = opt.brain_params.split(',')
+		a = self.cfg.brain_params.split(',')
 		return int(a[0]),a[1]
 		return int(a[0]),a[1]
 
 
 	def _deformat(self):
 	def _deformat(self):
@@ -35,25 +34,25 @@ class wallet(wallet):
 
 
 	def _decrypt(self):
 	def _decrypt(self):
 		d = self.ssdata
 		d = self.ssdata
-		if opt.brain_params:
+		if self.cfg.brain_params:
 			bw_seed_len,d.hash_preset = self.get_bw_params()
 			bw_seed_len,d.hash_preset = self.get_bw_params()
 		else:
 		else:
-			if not opt.seed_len:
-				qmsg(f'Using default seed length of {yellow(str(Seed.dfl_len))} bits\n'
+			if not self.cfg.seed_len:
+				self.cfg._util.qmsg(f'Using default seed length of {yellow(str(Seed.dfl_len))} bits\n'
 					+ 'If this is not what you want, use the --seed-len option' )
 					+ 'If this is not what you want, use the --seed-len option' )
 			self._get_hash_preset()
 			self._get_hash_preset()
-			bw_seed_len = opt.seed_len or Seed.dfl_len
-		qmsg_r('Hashing brainwallet data.  Please wait...')
+			bw_seed_len = self.cfg.seed_len or Seed.dfl_len
+		self.cfg._util.qmsg_r('Hashing brainwallet data.  Please wait...')
 		# Use buflen arg of scrypt.hash() to get seed of desired length
 		# Use buflen arg of scrypt.hash() to get seed of desired length
 		seed = self.crypto.scrypt_hash_passphrase(
 		seed = self.crypto.scrypt_hash_passphrase(
 			self.brainpasswd.encode(),
 			self.brainpasswd.encode(),
 			b'',
 			b'',
 			d.hash_preset,
 			d.hash_preset,
 			buflen = bw_seed_len // 8 )
 			buflen = bw_seed_len // 8 )
-		qmsg('Done')
-		self.seed = Seed(seed)
+		self.cfg._util.qmsg('Done')
+		self.seed = Seed( self.cfg, seed )
 		msg(f'Seed ID: {self.seed.sid}')
 		msg(f'Seed ID: {self.seed.sid}')
-		qmsg('Check this value against your records')
+		self.cfg._util.qmsg('Check this value against your records')
 		return True
 		return True
 
 
 	def _format(self):
 	def _format(self):

+ 10 - 12
mmgen/wallet/dieroll.py

@@ -13,8 +13,6 @@ wallet.dieroll: dieroll wallet class
 """
 """
 
 
 import time
 import time
-from ..globalvars import g
-from ..opts import opt
 from ..util import msg,msg_r,die,fmt,remove_whitespace
 from ..util import msg,msg_r,die,fmt,remove_whitespace
 from ..util2 import block_format
 from ..util2 import block_format
 from ..seed import Seed
 from ..seed import Seed
@@ -53,16 +51,16 @@ class wallet(wallet):
 		seed_len = rmap[len(d)]
 		seed_len = rmap[len(d)]
 		seed_bytes = bc.tobytes( d, pad='seed' )[-seed_len:]
 		seed_bytes = bc.tobytes( d, pad='seed' )[-seed_len:]
 
 
-		if self.interactive_input and opt.usr_randchars:
+		if self.interactive_input and self.cfg.usr_randchars:
 			from ..ui import keypress_confirm
 			from ..ui import keypress_confirm
-			if keypress_confirm(self.user_entropy_prompt):
+			if keypress_confirm( self.cfg, self.user_entropy_prompt ):
 				from ..crypto import Crypto
 				from ..crypto import Crypto
-				seed_bytes = Crypto().add_user_random(
+				seed_bytes = Crypto(self.cfg).add_user_random(
 					rand_bytes = seed_bytes,
 					rand_bytes = seed_bytes,
 					desc       = 'gathered from your die rolls' )
 					desc       = 'gathered from your die rolls' )
 				self.desc += ' plus user-supplied entropy'
 				self.desc += ' plus user-supplied entropy'
 
 
-		self.seed = Seed(seed_bytes)
+		self.seed = Seed( self.cfg, seed_bytes )
 		self.ssdata.hexseed = seed_bytes.hex()
 		self.ssdata.hexseed = seed_bytes.hex()
 
 
 		self.check_usr_seed_len()
 		self.check_usr_seed_len()
@@ -70,9 +68,9 @@ class wallet(wallet):
 
 
 	def _get_data_from_user(self,desc):
 	def _get_data_from_user(self,desc):
 
 
-		if not g.stdin_tty:
+		if not self.cfg.stdin_tty:
 			from ..ui import get_data_from_user
 			from ..ui import get_data_from_user
-			return get_data_from_user(desc)
+			return get_data_from_user( self.cfg, desc )
 
 
 		bc = baseconv('b6d')
 		bc = baseconv('b6d')
 
 
@@ -88,23 +86,23 @@ class wallet(wallet):
 
 
 		CUR_HIDE = '\033[?25l'
 		CUR_HIDE = '\033[?25l'
 		CUR_SHOW = '\033[?25h'
 		CUR_SHOW = '\033[?25h'
-		cr = '\n' if g.test_suite else '\r'
+		cr = '\n' if self.cfg.test_suite else '\r'
 		prompt_fs = f'\b\b\b   {cr}Enter die roll #{{}}: {CUR_SHOW}'
 		prompt_fs = f'\b\b\b   {cr}Enter die roll #{{}}: {CUR_SHOW}'
-		clear_line = '' if g.test_suite else '\r' + ' ' * 25
+		clear_line = '' if self.cfg.test_suite else '\r' + ' ' * 25
 		invalid_msg = CUR_HIDE + cr + 'Invalid entry' + ' ' * 11
 		invalid_msg = CUR_HIDE + cr + 'Invalid entry' + ' ' * 11
 
 
 		from ..term import get_char
 		from ..term import get_char
 		def get_digit(n):
 		def get_digit(n):
 			p = prompt_fs
 			p = prompt_fs
 			while True:
 			while True:
-				time.sleep(g.short_disp_timeout)
+				time.sleep(self.cfg.short_disp_timeout)
 				ch = get_char(p.format(n),num_bytes=1)
 				ch = get_char(p.format(n),num_bytes=1)
 				if ch in bc.digits:
 				if ch in bc.digits:
 					msg_r(CUR_HIDE + ' OK')
 					msg_r(CUR_HIDE + ' OK')
 					return ch
 					return ch
 				else:
 				else:
 					msg_r(invalid_msg)
 					msg_r(invalid_msg)
-					sleep = g.err_disp_timeout
+					sleep = self.cfg.err_disp_timeout
 					p = clear_line + prompt_fs
 					p = clear_line + prompt_fs
 
 
 		dierolls,n = [],1
 		dierolls,n = [],1

+ 16 - 17
mmgen/wallet/enc.py

@@ -13,15 +13,14 @@ wallet.enc: encrypted wallet base class
 """
 """
 
 
 from ..globalvars import gc
 from ..globalvars import gc
-from ..opts import opt
-from ..util import msg,qmsg,make_chksum_8
+from ..util import msg,make_chksum_8
 from .base import wallet
 from .base import wallet
 
 
 class wallet(wallet):
 class wallet(wallet):
 
 
 	def __init__(self,*args,**kwargs):
 	def __init__(self,*args,**kwargs):
 		from mmgen.crypto import Crypto
 		from mmgen.crypto import Crypto
-		self.crypto = Crypto()
+		self.crypto = Crypto(self.cfg)
 		return super().__init__(*args,**kwargs)
 		return super().__init__(*args,**kwargs)
 
 
 	def _decrypt_retry(self):
 	def _decrypt_retry(self):
@@ -45,19 +44,19 @@ class wallet(wallet):
 	def _get_hash_preset(self,add_desc=''):
 	def _get_hash_preset(self,add_desc=''):
 		if hasattr(self,'ss_in') and hasattr(self.ss_in.ssdata,'hash_preset'):
 		if hasattr(self,'ss_in') and hasattr(self.ss_in.ssdata,'hash_preset'):
 			old_hp = self.ss_in.ssdata.hash_preset
 			old_hp = self.ss_in.ssdata.hash_preset
-			if opt.keep_hash_preset:
+			if self.cfg.keep_hash_preset:
 				hp = old_hp
 				hp = old_hp
-				qmsg(f'Reusing hash preset {hp!r} at user request')
-			elif opt.hash_preset:
-				hp = opt.hash_preset
-				qmsg(f'Using hash preset {hp!r} requested on command line')
+				self.cfg._util.qmsg(f'Reusing hash preset {hp!r} at user request')
+			elif self.cfg.hash_preset:
+				hp = self.cfg.hash_preset
+				self.cfg._util.qmsg(f'Using hash preset {hp!r} requested on command line')
 			else: # Prompt, using old value as default
 			else: # Prompt, using old value as default
-				hp = self._get_hash_preset_from_user(old_hp,add_desc)
-			if (not opt.keep_hash_preset) and self.op == 'pwchg_new':
-				qmsg('Hash preset {}'.format( 'unchanged' if hp == old_hp else f'changed to {hp!r}' ))
-		elif opt.hash_preset:
-			hp = opt.hash_preset
-			qmsg(f'Using hash preset {hp!r} requested on command line')
+				hp = self._get_hash_preset_from_user( old_preset=old_hp, add_desc=add_desc )
+			if (not self.cfg.keep_hash_preset) and self.op == 'pwchg_new':
+				self.cfg._util.qmsg('Hash preset {}'.format( 'unchanged' if hp == old_hp else f'changed to {hp!r}' ))
+		elif self.cfg.hash_preset:
+			hp = self.cfg.hash_preset
+			self.cfg._util.qmsg(f'Using hash preset {hp!r} requested on command line')
 		else:
 		else:
 			hp = self._get_hash_preset_from_user(
 			hp = self._get_hash_preset_from_user(
 				old_preset = gc.dfl_hash_preset,
 				old_preset = gc.dfl_hash_preset,
@@ -83,13 +82,13 @@ class wallet(wallet):
 
 
 		if hasattr(self,'ss_in') and hasattr(self.ss_in.ssdata,'passwd'):
 		if hasattr(self,'ss_in') and hasattr(self.ss_in.ssdata,'passwd'):
 			old_pw = self.ss_in.ssdata.passwd
 			old_pw = self.ss_in.ssdata.passwd
-			if opt.keep_passphrase:
+			if self.cfg.keep_passphrase:
 				d.passwd = old_pw
 				d.passwd = old_pw
-				qmsg('Reusing passphrase at user request')
+				self.cfg._util.qmsg('Reusing passphrase at user request')
 			else:
 			else:
 				d.passwd = self._get_new_passphrase()
 				d.passwd = self._get_new_passphrase()
 				if self.op == 'pwchg_new':
 				if self.op == 'pwchg_new':
-					qmsg('Passphrase {}'.format( 'unchanged' if d.passwd == old_pw else 'changed' ))
+					self.cfg._util.qmsg('Passphrase {}'.format( 'unchanged' if d.passwd == old_pw else 'changed' ))
 		else:
 		else:
 			d.passwd = self._get_new_passphrase()
 			d.passwd = self._get_new_passphrase()
 
 

+ 15 - 17
mmgen/wallet/incog_base.py

@@ -12,10 +12,8 @@
 wallet.incog_base: incognito wallet base class
 wallet.incog_base: incognito wallet base class
 """
 """
 
 
-from ..globalvars import g
-from ..opts import opt
 from ..seed import Seed
 from ..seed import Seed
-from ..util import msg,vmsg,qmsg,make_chksum_8
+from ..util import msg,make_chksum_8
 from .enc import wallet
 from .enc import wallet
 
 
 class wallet(wallet):
 class wallet(wallet):
@@ -40,18 +38,18 @@ class wallet(wallet):
 		return (
 		return (
 			self.crypto.aesctr_iv_len
 			self.crypto.aesctr_iv_len
 			+ self.crypto.salt_len
 			+ self.crypto.salt_len
-			+ (0 if opt.old_incog_fmt else self.crypto.hincog_chk_len)
+			+ (0 if self.cfg.old_incog_fmt else self.crypto.hincog_chk_len)
 			+ seed_len//8 )
 			+ seed_len//8 )
 
 
 	def _incog_data_size_chk(self):
 	def _incog_data_size_chk(self):
 		# valid sizes: 56, 64, 72
 		# valid sizes: 56, 64, 72
 		dlen = len(self.fmt_data)
 		dlen = len(self.fmt_data)
-		seed_len = opt.seed_len or Seed.dfl_len
+		seed_len = self.cfg.seed_len or Seed.dfl_len
 		valid_dlen = self._get_incog_data_len(seed_len)
 		valid_dlen = self._get_incog_data_len(seed_len)
 		if dlen == valid_dlen:
 		if dlen == valid_dlen:
 			return True
 			return True
 		else:
 		else:
-			if opt.old_incog_fmt:
+			if self.cfg.old_incog_fmt:
 				msg('WARNING: old-style incognito format requested.  Are you sure this is correct?')
 				msg('WARNING: old-style incognito format requested.  Are you sure this is correct?')
 			msg(f'Invalid incognito data size ({dlen} bytes) for this seed length ({seed_len} bits)')
 			msg(f'Invalid incognito data size ({dlen} bytes) for this seed length ({seed_len} bits)')
 			msg(f'Valid data size for this seed length: {valid_dlen} bytes')
 			msg(f'Valid data size for this seed length: {valid_dlen} bytes')
@@ -63,7 +61,7 @@ class wallet(wallet):
 
 
 	def _encrypt (self):
 	def _encrypt (self):
 		self._get_first_pw_and_hp_and_encrypt_seed()
 		self._get_first_pw_and_hp_and_encrypt_seed()
-		if opt.old_incog_fmt:
+		if self.cfg.old_incog_fmt:
 			die(1,'Writing old-format incognito wallets is unsupported')
 			die(1,'Writing old-format incognito wallets is unsupported')
 		d = self.ssdata
 		d = self.ssdata
 		crypto = self.crypto
 		crypto = self.crypto
@@ -71,8 +69,8 @@ class wallet(wallet):
 		d.iv = crypto.get_random( crypto.aesctr_iv_len )
 		d.iv = crypto.get_random( crypto.aesctr_iv_len )
 		d.iv_id = self._make_iv_chksum(d.iv)
 		d.iv_id = self._make_iv_chksum(d.iv)
 		msg(f'New Incog Wallet ID: {d.iv_id}')
 		msg(f'New Incog Wallet ID: {d.iv_id}')
-		qmsg('Make a record of this value')
-		vmsg('\n  ' + self.msg['record_incog_id'].strip()+'\n')
+		self.cfg._util.qmsg('Make a record of this value')
+		self.cfg._util.vmsg('\n  ' + self.msg['record_incog_id'].strip()+'\n')
 
 
 		d.salt = crypto.get_random( crypto.salt_len )
 		d.salt = crypto.get_random( crypto.salt_len )
 		seed_key = crypto.make_key(
 		seed_key = crypto.make_key(
@@ -95,7 +93,7 @@ class wallet(wallet):
 			desc        = 'incog wrapper key' )
 			desc        = 'incog wrapper key' )
 
 
 		d.key_id = make_chksum_8(d.wrapper_key)
 		d.key_id = make_chksum_8(d.wrapper_key)
-		vmsg(f'Key ID: {d.key_id}')
+		self.cfg._util.vmsg(f'Key ID: {d.key_id}')
 
 
 		d.target_data_len = self._get_incog_data_len(self.seed.bitlen)
 		d.target_data_len = self._get_incog_data_len(self.seed.bitlen)
 
 
@@ -128,8 +126,8 @@ class wallet(wallet):
 		d.incog_id       = self._make_iv_chksum(d.iv)
 		d.incog_id       = self._make_iv_chksum(d.iv)
 		d.enc_incog_data = self.fmt_data[self.crypto.aesctr_iv_len:]
 		d.enc_incog_data = self.fmt_data[self.crypto.aesctr_iv_len:]
 		msg(f'Incog Wallet ID: {d.incog_id}')
 		msg(f'Incog Wallet ID: {d.incog_id}')
-		qmsg('Check this value against your records')
-		vmsg('\n  ' + self.msg['check_incog_id'].strip()+'\n')
+		self.cfg._util.qmsg('Check this value against your records')
+		self.cfg._util.vmsg('\n  ' + self.msg['check_incog_id'].strip()+'\n')
 
 
 		return True
 		return True
 
 
@@ -137,7 +135,7 @@ class wallet(wallet):
 		chk,seed = data[:8],data[8:]
 		chk,seed = data[:8],data[8:]
 		from hashlib import sha256
 		from hashlib import sha256
 		if sha256(seed).digest()[:8] == chk:
 		if sha256(seed).digest()[:8] == chk:
-			qmsg('Passphrase{} are correct'.format( self.msg['decrypt_params'].format('and') ))
+			self.cfg._util.qmsg('Passphrase{} are correct'.format( self.msg['decrypt_params'].format('and') ))
 			return seed
 			return seed
 		else:
 		else:
 			msg('Incorrect passphrase{}'.format( self.msg['decrypt_params'].format('or') ))
 			msg('Incorrect passphrase{}'.format( self.msg['decrypt_params'].format('or') ))
@@ -146,7 +144,7 @@ class wallet(wallet):
 	def _verify_seed_oldfmt(self,seed):
 	def _verify_seed_oldfmt(self,seed):
 		m = f'Seed ID: {make_chksum_8(seed)}.  Is the Seed ID correct?'
 		m = f'Seed ID: {make_chksum_8(seed)}.  Is the Seed ID correct?'
 		from ..ui import keypress_confirm
 		from ..ui import keypress_confirm
-		if keypress_confirm(m, True):
+		if keypress_confirm( self.cfg, m, True ):
 			return seed
 			return seed
 		else:
 		else:
 			return False
 			return False
@@ -179,9 +177,9 @@ class wallet(wallet):
 			hash_preset = d.hash_preset,
 			hash_preset = d.hash_preset,
 			desc        = 'main key' )
 			desc        = 'main key' )
 
 
-		qmsg(f'Key ID: {make_chksum_8(seed_key)}')
+		self.cfg._util.qmsg(f'Key ID: {make_chksum_8(seed_key)}')
 
 
-		verify_seed_func = getattr( self, '_verify_seed_'+ ('oldfmt' if opt.old_incog_fmt else 'newfmt') )
+		verify_seed_func = getattr( self, '_verify_seed_'+ ('oldfmt' if self.cfg.old_incog_fmt else 'newfmt') )
 
 
 		seed = verify_seed_func(
 		seed = verify_seed_func(
 			crypto.decrypt_seed(
 			crypto.decrypt_seed(
@@ -191,7 +189,7 @@ class wallet(wallet):
 				key_id   = '' ))
 				key_id   = '' ))
 
 
 		if seed:
 		if seed:
-			self.seed = Seed(seed)
+			self.seed = Seed( self.cfg, seed )
 			msg(f'Seed ID: {self.seed.sid}')
 			msg(f'Seed ID: {self.seed.sid}')
 			return True
 			return True
 		else:
 		else:

+ 14 - 13
mmgen/wallet/incog_hidden.py

@@ -15,9 +15,8 @@ wallet.incog_hidden: hidden incognito wallet class
 import os
 import os
 
 
 from ..globalvars import gc
 from ..globalvars import gc
-from ..opts import opt
 from ..seed import Seed
 from ..seed import Seed
-from ..util import msg,dmsg,qmsg,die,compare_or_die,capfirst
+from ..util import msg,die,capfirst
 from ..util2 import parse_bytespec
 from ..util2 import parse_bytespec
 from .incog_base import wallet
 from .incog_base import wallet
 
 
@@ -49,7 +48,7 @@ class wallet(wallet):
 	}
 	}
 
 
 	def _get_hincog_params(self,wtype):
 	def _get_hincog_params(self,wtype):
-		a = getattr(opt,'hidden_incog_'+ wtype +'_params').split(',')
+		a = getattr(self.cfg,'hidden_incog_'+ wtype +'_params').split(',')
 		return ','.join(a[:-1]),int(a[-1]) # permit comma in filename
 		return ','.join(a[:-1]),int(a[-1]) # permit comma in filename
 
 
 	def _check_valid_offset(self,fn,action):
 	def _check_valid_offset(self,fn,action):
@@ -68,10 +67,10 @@ class wallet(wallet):
 		d = self.ssdata
 		d = self.ssdata
 		d.hincog_offset = self._get_hincog_params('input')[1]
 		d.hincog_offset = self._get_hincog_params('input')[1]
 
 
-		qmsg(f'Getting hidden incog data from file {self.infile.name!r}')
+		self.cfg._util.qmsg(f'Getting hidden incog data from file {self.infile.name!r}')
 
 
 		# Already sanity-checked:
 		# Already sanity-checked:
-		d.target_data_len = self._get_incog_data_len(opt.seed_len or Seed.dfl_len)
+		d.target_data_len = self._get_incog_data_len(self.cfg.seed_len or Seed.dfl_len)
 		self._check_valid_offset(self.infile,'read')
 		self._check_valid_offset(self.infile,'read')
 
 
 		flgs = os.O_RDONLY|os.O_BINARY if gc.platform == 'win' else os.O_RDONLY
 		flgs = os.O_RDONLY|os.O_BINARY if gc.platform == 'win' else os.O_RDONLY
@@ -79,13 +78,13 @@ class wallet(wallet):
 		os.lseek(fh,int(d.hincog_offset),os.SEEK_SET)
 		os.lseek(fh,int(d.hincog_offset),os.SEEK_SET)
 		self.fmt_data = os.read(fh,d.target_data_len)
 		self.fmt_data = os.read(fh,d.target_data_len)
 		os.close(fh)
 		os.close(fh)
-		qmsg(f'Data read from file {self.infile.name!r} at offset {d.hincog_offset}')
+		self.cfg._util.qmsg(f'Data read from file {self.infile.name!r} at offset {d.hincog_offset}')
 
 
 	# overrides method in Wallet
 	# overrides method in Wallet
 	def write_to_file(self):
 	def write_to_file(self):
 		d = self.ssdata
 		d = self.ssdata
 		self._format()
 		self._format()
-		compare_or_die(
+		self.cfg._util.compare_or_die(
 			val1  = d.target_data_len,
 			val1  = d.target_data_len,
 			desc1 = 'target data length',
 			desc1 = 'target data length',
 			val2  = len(self.fmt_data),
 			val2  = len(self.fmt_data),
@@ -94,8 +93,8 @@ class wallet(wallet):
 		k = ('output','input')[self.op=='pwchg_new']
 		k = ('output','input')[self.op=='pwchg_new']
 		fn,d.hincog_offset = self._get_hincog_params(k)
 		fn,d.hincog_offset = self._get_hincog_params(k)
 
 
-		if opt.outdir and not os.path.dirname(fn):
-			fn = os.path.join(opt.outdir,fn)
+		if self.cfg.outdir and not os.path.dirname(fn):
+			fn = os.path.join(self.cfg.outdir,fn)
 
 
 		check_offset = True
 		check_offset = True
 		try:
 		try:
@@ -103,18 +102,19 @@ class wallet(wallet):
 		except:
 		except:
 			from ..ui import keypress_confirm,line_input
 			from ..ui import keypress_confirm,line_input
 			if keypress_confirm(
 			if keypress_confirm(
+					self.cfg,
 					f'Requested file {fn!r} does not exist.  Create?',
 					f'Requested file {fn!r} does not exist.  Create?',
 					default_yes = True ):
 					default_yes = True ):
 				min_fsize = d.target_data_len + d.hincog_offset
 				min_fsize = d.target_data_len + d.hincog_offset
 				msg('\n  ' + self.msg['choose_file_size'].strip().format(min_fsize)+'\n')
 				msg('\n  ' + self.msg['choose_file_size'].strip().format(min_fsize)+'\n')
 				while True:
 				while True:
-					fsize = parse_bytespec(line_input('Enter file size: '))
+					fsize = parse_bytespec(line_input( self.cfg, 'Enter file size: ' ))
 					if fsize >= min_fsize:
 					if fsize >= min_fsize:
 						break
 						break
 					msg(f'File size must be an integer no less than {min_fsize}')
 					msg(f'File size must be an integer no less than {min_fsize}')
 
 
 				from ..tool.fileutil import tool_cmd
 				from ..tool.fileutil import tool_cmd
-				tool_cmd().rand2file(fn,str(fsize))
+				tool_cmd(self.cfg).rand2file(fn,str(fsize))
 				check_offset = False
 				check_offset = False
 			else:
 			else:
 				die(1,'Exiting at user request')
 				die(1,'Exiting at user request')
@@ -122,16 +122,17 @@ class wallet(wallet):
 		from ..filename import MMGenFile
 		from ..filename import MMGenFile
 		f = MMGenFile(fn,subclass=type(self),write=True)
 		f = MMGenFile(fn,subclass=type(self),write=True)
 
 
-		dmsg('{} data len {}, offset {}'.format(
+		self.cfg._util.dmsg('{} data len {}, offset {}'.format(
 			capfirst(self.desc),
 			capfirst(self.desc),
 			d.target_data_len,
 			d.target_data_len,
 			d.hincog_offset ))
 			d.hincog_offset ))
 
 
 		if check_offset:
 		if check_offset:
 			self._check_valid_offset(f,'write')
 			self._check_valid_offset(f,'write')
-			if not opt.quiet:
+			if not self.cfg.quiet:
 				from ..ui import confirm_or_raise
 				from ..ui import confirm_or_raise
 				confirm_or_raise(
 				confirm_or_raise(
+					self.cfg,
 					message = '',
 					message = '',
 					action  = f'alter file {f.name!r}' )
 					action  = f'alter file {f.name!r}' )
 
 

+ 17 - 19
mmgen/wallet/mmgen.py

@@ -14,10 +14,8 @@ wallet.mmgen: MMGen native wallet class
 
 
 import os
 import os
 
 
-from ..globalvars import g
-from ..opts import opt
 from ..seed import Seed
 from ..seed import Seed
-from ..util import msg,qmsg,make_timestamp,make_chksum_6,split_into_cols,is_chksum_6,compare_chksums
+from ..util import msg,make_timestamp,make_chksum_6,split_into_cols,is_chksum_6
 from ..obj import MMGenWalletLabel,get_obj
 from ..obj import MMGenWalletLabel,get_obj
 from ..baseconv import baseconv
 from ..baseconv import baseconv
 
 
@@ -28,9 +26,9 @@ class wallet(wallet):
 	desc = 'MMGen wallet'
 	desc = 'MMGen wallet'
 
 
 	def __init__(self,*args,**kwargs):
 	def __init__(self,*args,**kwargs):
-		if opt.label:
+		if self.cfg.label:
 			self.label = MMGenWalletLabel(
 			self.label = MMGenWalletLabel(
-				opt.label,
+				self.cfg.label,
 				msg = "Error in option '--label'" )
 				msg = "Error in option '--label'" )
 		else:
 		else:
 			self.label = None
 			self.label = None
@@ -43,7 +41,7 @@ class wallet(wallet):
 			'for no label' )
 			'for no label' )
 		from ..ui import line_input
 		from ..ui import line_input
 		while True:
 		while True:
-			ret = line_input(prompt)
+			ret = line_input( self.cfg, prompt )
 			if ret:
 			if ret:
 				lbl = get_obj(MMGenWalletLabel,s=ret)
 				lbl = get_obj(MMGenWalletLabel,s=ret)
 				if lbl:
 				if lbl:
@@ -57,19 +55,19 @@ class wallet(wallet):
 	def _get_label(self):
 	def _get_label(self):
 		if hasattr(self,'ss_in') and hasattr(self.ss_in.ssdata,'label'):
 		if hasattr(self,'ss_in') and hasattr(self.ss_in.ssdata,'label'):
 			old_lbl = self.ss_in.ssdata.label
 			old_lbl = self.ss_in.ssdata.label
-			if opt.keep_label:
+			if self.cfg.keep_label:
 				lbl = old_lbl
 				lbl = old_lbl
-				qmsg('Reusing label {} at user request'.format( lbl.hl2(encl='‘’') ))
+				self.cfg._util.qmsg('Reusing label {} at user request'.format( lbl.hl2(encl='‘’') ))
 			elif self.label:
 			elif self.label:
 				lbl = self.label
 				lbl = self.label
-				qmsg('Using label {} requested on command line'.format( lbl.hl2(encl='‘’') ))
+				self.cfg._util.qmsg('Using label {} requested on command line'.format( lbl.hl2(encl='‘’') ))
 			else: # Prompt, using old value as default
 			else: # Prompt, using old value as default
 				lbl = self._get_label_from_user(old_lbl)
 				lbl = self._get_label_from_user(old_lbl)
-			if (not opt.keep_label) and self.op == 'pwchg_new':
-				qmsg('Label {}'.format( 'unchanged' if lbl == old_lbl else f'changed to {lbl!r}' ))
+			if (not self.cfg.keep_label) and self.op == 'pwchg_new':
+				self.cfg._util.qmsg('Label {}'.format( 'unchanged' if lbl == old_lbl else f'changed to {lbl!r}' ))
 		elif self.label:
 		elif self.label:
 			lbl = self.label
 			lbl = self.label
-			qmsg('Using label {} requested on command line'.format( lbl.hl2(encl='‘’') ))
+			self.cfg._util.qmsg('Using label {} requested on command line'.format( lbl.hl2(encl='‘’') ))
 		else:
 		else:
 			lbl = self._get_label_from_user()
 			lbl = self._get_label_from_user()
 		self.ssdata.label = lbl
 		self.ssdata.label = lbl
@@ -110,7 +108,7 @@ class wallet(wallet):
 				return False
 				return False
 
 
 			chk = make_chksum_6(' '.join(lines[1:]))
 			chk = make_chksum_6(' '.join(lines[1:]))
-			if not compare_chksums(lines[0],'master',chk,'computed',
+			if not self.cfg._util.compare_chksums(lines[0],'master',chk,'computed',
 						hdr='For wallet master checksum',verbose=True):
 						hdr='For wallet master checksum',verbose=True):
 				return False
 				return False
 
 
@@ -132,9 +130,9 @@ class wallet(wallet):
 		hpdata = lines[3].split()
 		hpdata = lines[3].split()
 
 
 		d.hash_preset = hp = hpdata[0][:-1]  # a string!
 		d.hash_preset = hp = hpdata[0][:-1]  # a string!
-		qmsg(f'Hash preset of wallet: {hp!r}')
-		if opt.hash_preset and opt.hash_preset != hp:
-			qmsg(f'Warning: ignoring user-requested hash preset {opt.hash_preset!r}')
+		self.cfg._util.qmsg(f'Hash preset of wallet: {hp!r}')
+		if self.cfg.hash_preset and self.cfg.hash_preset != hp:
+			self.cfg._util.qmsg(f'Warning: ignoring user-requested hash preset {self.cfg.hash_preset!r}')
 
 
 		hash_params = tuple(map(int,hpdata[1:]))
 		hash_params = tuple(map(int,hpdata[1:]))
 
 
@@ -152,7 +150,7 @@ class wallet(wallet):
 				msg(f'Invalid format for {key} in {self.desc}: {l}')
 				msg(f'Invalid format for {key} in {self.desc}: {l}')
 				return False
 				return False
 
 
-			if not compare_chksums(chk,key,
+			if not self.cfg._util.compare_chksums(chk,key,
 					make_chksum_6(b58_val),'computed checksum',verbose=True):
 					make_chksum_6(b58_val),'computed checksum',verbose=True):
 				return False
 				return False
 
 
@@ -169,11 +167,11 @@ class wallet(wallet):
 		d = self.ssdata
 		d = self.ssdata
 		# Needed for multiple transactions with {}-txsign
 		# Needed for multiple transactions with {}-txsign
 		d.passwd = self._get_passphrase(
 		d.passwd = self._get_passphrase(
-			add_desc = os.path.basename(self.infile.name) if opt.quiet else '' )
+			add_desc = os.path.basename(self.infile.name) if self.cfg.quiet else '' )
 		key = self.crypto.make_key( d.passwd, d.salt, d.hash_preset )
 		key = self.crypto.make_key( d.passwd, d.salt, d.hash_preset )
 		ret = self.crypto.decrypt_seed( d.enc_seed, key, d.seed_id, d.key_id )
 		ret = self.crypto.decrypt_seed( d.enc_seed, key, d.seed_id, d.key_id )
 		if ret:
 		if ret:
-			self.seed = Seed(ret)
+			self.seed = Seed( self.cfg, ret )
 			return True
 			return True
 		else:
 		else:
 			return False
 			return False

+ 4 - 4
mmgen/wallet/mmhex.py

@@ -14,7 +14,7 @@ wallet.mmhex: MMGen hexadecimal file wallet class
 
 
 from ..util import make_chksum_6,split_into_cols
 from ..util import make_chksum_6,split_into_cols
 from ..seed import Seed
 from ..seed import Seed
-from ..util import msg,vmsg_r,is_chksum_6,is_hex_str,compare_chksums
+from ..util import msg,is_chksum_6,is_hex_str
 from .unenc import wallet
 from .unenc import wallet
 
 
 class wallet(wallet):
 class wallet(wallet):
@@ -52,12 +52,12 @@ class wallet(wallet):
 			msg(f'{hstr!r}: not a hexadecimal string, in {desc}')
 			msg(f'{hstr!r}: not a hexadecimal string, in {desc}')
 			return False
 			return False
 
 
-		vmsg_r(f'Validating {desc} checksum...')
+		self.cfg._util.vmsg_r(f'Validating {desc} checksum...')
 
 
-		if not compare_chksums(chk,'file',make_chksum_6(hstr),'computed',verbose=True):
+		if not self.cfg._util.compare_chksums(chk,'file',make_chksum_6(hstr),'computed',verbose=True):
 			return False
 			return False
 
 
-		self.seed = Seed(bytes.fromhex(hstr))
+		self.seed = Seed( self.cfg, bytes.fromhex(hstr) )
 		self.ssdata.chksum = chk
 		self.ssdata.chksum = chk
 		self.ssdata.hexseed = hstr
 		self.ssdata.hexseed = hstr
 
 

+ 7 - 8
mmgen/wallet/mnemonic.py

@@ -12,9 +12,8 @@
 wallet.mnemonic: MMGen mnemonic wallet base class
 wallet.mnemonic: MMGen mnemonic wallet base class
 """
 """
 
 
-from ..globalvars import g
 from ..baseconv import baseconv
 from ..baseconv import baseconv
-from ..util import msg,compare_or_die
+from ..util import msg
 from ..seed import Seed
 from ..seed import Seed
 from .unenc import wallet
 from .unenc import wallet
 
 
@@ -31,14 +30,14 @@ class wallet(wallet):
 
 
 	def _get_data_from_user(self,desc):
 	def _get_data_from_user(self,desc):
 
 
-		if not g.stdin_tty:
+		if not self.cfg.stdin_tty:
 			from ..ui import get_data_from_user
 			from ..ui import get_data_from_user
-			return get_data_from_user(desc)
+			return get_data_from_user( self.cfg, desc )
 
 
 		mn_len = self._choose_seedlen( self.mn_lens )
 		mn_len = self._choose_seedlen( self.mn_lens )
 
 
 		from ..mn_entry import mn_entry
 		from ..mn_entry import mn_entry
-		return mn_entry(self.wl_id).get_mnemonic_from_user(mn_len)
+		return mn_entry( self.cfg, self.wl_id ).get_mnemonic_from_user(mn_len)
 
 
 	def _format(self):
 	def _format(self):
 
 
@@ -49,7 +48,7 @@ class wallet(wallet):
 		rev = bc.tohex( mn, 'seed' )
 		rev = bc.tohex( mn, 'seed' )
 
 
 		# Internal error, so just die on fail
 		# Internal error, so just die on fail
-		compare_or_die( rev, 'recomputed seed', hexseed, 'original', e='Internal error' )
+		self.cfg._util.compare_or_die( rev, 'recomputed seed', hexseed, 'original', e='Internal error' )
 
 
 		self.ssdata.mnemonic = mn
 		self.ssdata.mnemonic = mn
 		self.fmt_data = ' '.join(mn) + '\n'
 		self.fmt_data = ' '.join(mn) + '\n'
@@ -78,14 +77,14 @@ class wallet(wallet):
 			return False
 			return False
 
 
 		# Internal error, so just die:
 		# Internal error, so just die:
-		compare_or_die(
+		self.cfg._util.compare_or_die(
 			val1  = ' '.join(rev),
 			val1  = ' '.join(rev),
 			val2  = ' '.join(mn),
 			val2  = ' '.join(mn),
 			desc1 = 'recomputed mnemonic',
 			desc1 = 'recomputed mnemonic',
 			desc2 = 'original mnemonic',
 			desc2 = 'original mnemonic',
 			e     = 'Internal error' )
 			e     = 'Internal error' )
 
 
-		self.seed = Seed(bytes.fromhex(hexseed))
+		self.seed = Seed( self.cfg, bytes.fromhex(hexseed) )
 		self.ssdata.mnemonic = mn
 		self.ssdata.mnemonic = mn
 
 
 		self.check_usr_seed_len()
 		self.check_usr_seed_len()

+ 1 - 1
mmgen/wallet/plainhex.py

@@ -36,7 +36,7 @@ class wallet(wallet):
 			msg(f'Invalid data length ({len(d)}) in {desc}')
 			msg(f'Invalid data length ({len(d)}) in {desc}')
 			return False
 			return False
 
 
-		self.seed = Seed(bytes.fromhex(d))
+		self.seed = Seed( self.cfg, bytes.fromhex(d) )
 		self.ssdata.hexseed = d
 		self.ssdata.hexseed = d
 
 
 		self.check_usr_seed_len()
 		self.check_usr_seed_len()

+ 4 - 4
mmgen/wallet/seed.py

@@ -12,7 +12,7 @@
 wallet.seed: seed file wallet class
 wallet.seed: seed file wallet class
 """
 """
 
 
-from ..util import msg,vmsg_r,make_chksum_6,split_into_cols,is_chksum_6,compare_chksums
+from ..util import msg,make_chksum_6,split_into_cols,is_chksum_6
 from ..baseconv import baseconv,is_b58_str
 from ..baseconv import baseconv,is_b58_str
 from ..seed import Seed
 from ..seed import Seed
 from .unenc import wallet
 from .unenc import wallet
@@ -48,9 +48,9 @@ class wallet(wallet):
 			msg(f'{b!r}: not a base 58 string, in {desc}')
 			msg(f'{b!r}: not a base 58 string, in {desc}')
 			return False
 			return False
 
 
-		vmsg_r(f'Validating {desc} checksum...')
+		self.cfg._util.vmsg_r(f'Validating {desc} checksum...')
 
 
-		if not compare_chksums(a,'file',make_chksum_6(b),'computed',verbose=True):
+		if not self.cfg._util.compare_chksums(a,'file',make_chksum_6(b),'computed',verbose=True):
 			return False
 			return False
 
 
 		ret = baseconv('b58').tobytes(b,pad='seed')
 		ret = baseconv('b58').tobytes(b,pad='seed')
@@ -59,7 +59,7 @@ class wallet(wallet):
 			msg(f'Invalid base-58 encoded seed: {val}')
 			msg(f'Invalid base-58 encoded seed: {val}')
 			return False
 			return False
 
 
-		self.seed = Seed(ret)
+		self.seed = Seed( self.cfg, ret )
 		self.ssdata.chksum = a
 		self.ssdata.chksum = a
 		self.ssdata.b58seed = b
 		self.ssdata.b58seed = b
 
 

+ 3 - 3
mmgen/wallet/unenc.py

@@ -12,7 +12,6 @@
 wallet.unenc: unencrypted wallet base class
 wallet.unenc: unencrypted wallet base class
 """
 """
 
 
-from ..globalvars import g
 from ..color import blue,yellow
 from ..color import blue,yellow
 from ..util import msg,msg_r,capfirst,is_int
 from ..util import msg,msg_r,capfirst,is_int
 from .base import wallet
 from .base import wallet
@@ -41,7 +40,7 @@ class wallet(wallet):
 				r = get_char('\r'+prompt)
 				r = get_char('\r'+prompt)
 				if is_int(r) and 1 <= int(r) <= len(ok_lens):
 				if is_int(r) and 1 <= int(r) <= len(ok_lens):
 					break
 					break
-			msg_r(('\r','\n')[g.test_suite] + ' '*len(prompt) + '\r')
+			msg_r(('\r','\n')[self.cfg.test_suite] + ' '*len(prompt) + '\r')
 			return ok_lens[int(r)-1]
 			return ok_lens[int(r)-1]
 
 
 		msg('{} {}'.format(
 		msg('{} {}'.format(
@@ -54,7 +53,8 @@ class wallet(wallet):
 			prompt = self.choose_seedlen_confirm.format(usr_len)
 			prompt = self.choose_seedlen_confirm.format(usr_len)
 			from ..ui import keypress_confirm
 			from ..ui import keypress_confirm
 			if keypress_confirm(
 			if keypress_confirm(
+					self.cfg,
 					prompt,
 					prompt,
 					default_yes = True,
 					default_yes = True,
-					no_nl       = not g.test_suite ):
+					no_nl       = not self.cfg.test_suite ):
 				return usr_len
 				return usr_len

+ 44 - 33
mmgen/xmrwallet.py

@@ -22,9 +22,6 @@ xmrwallet.py - MoneroWalletOps class
 
 
 import os,re,time,json
 import os,re,time,json
 from collections import namedtuple
 from collections import namedtuple
-
-from .globalvars import g
-from .opts import opt
 from .objmethods import MMGenObject,Hilite,InitErrors
 from .objmethods import MMGenObject,Hilite,InitErrors
 from .obj import CoinTxID
 from .obj import CoinTxID
 from .color import red,yellow,green,blue,cyan,pink,orange
 from .color import red,yellow,green,blue,cyan,pink,orange
@@ -42,7 +39,6 @@ from .util import (
 	make_timestr,
 	make_timestr,
 	make_chksum_6,
 	make_chksum_6,
 	capfirst,
 	capfirst,
-	stdout_or_pager,
 )
 )
 from .seed import SeedID
 from .seed import SeedID
 from .protocol import init_proto
 from .protocol import init_proto
@@ -203,6 +199,7 @@ class MoneroMMGenTX:
 			)
 			)
 			from .fileutil import write_data_to_file
 			from .fileutil import write_data_to_file
 			write_data_to_file(
 			write_data_to_file(
+				cfg                   = self.cfg,
 				outfile               = fn,
 				outfile               = fn,
 				data                  = out,
 				data                  = out,
 				desc                  = 'MoneroMMGenTX data',
 				desc                  = 'MoneroMMGenTX data',
@@ -216,7 +213,10 @@ class MoneroMMGenTX:
 			assert not args, 'Non-keyword args not permitted'
 			assert not args, 'Non-keyword args not permitted'
 
 
 			d = namedtuple('kwargs_tuple',kwargs)(**kwargs)
 			d = namedtuple('kwargs_tuple',kwargs)(**kwargs)
-			proto = init_proto( 'xmr', network=d.network, need_amt=True )
+			self.cfg = d.cfg
+
+			proto = init_proto( self.cfg, 'xmr', network=d.network, need_amt=True )
+
 			now = int(time.time())
 			now = int(time.time())
 
 
 			self.data = self.xmrwallet_tx_data(
 			self.data = self.xmrwallet_tx_data(
@@ -237,12 +237,13 @@ class MoneroMMGenTX:
 
 
 	class Signed(Base):
 	class Signed(Base):
 
 
-		def __init__(self,fn):
+		def __init__(self,cfg,fn):
 			from .fileutil import get_data_from_file
 			from .fileutil import get_data_from_file
+			self.cfg = cfg
 			self.fn = fn
 			self.fn = fn
-			d_wrap = json.loads(get_data_from_file(fn))['MoneroMMGenTX']
+			d_wrap = json.loads(get_data_from_file( cfg, fn ))['MoneroMMGenTX']
 			d = self.xmrwallet_tx_data(**d_wrap['data'])
 			d = self.xmrwallet_tx_data(**d_wrap['data'])
-			proto = init_proto( 'xmr', network=d.network, need_amt=True )
+			proto = init_proto( cfg, 'xmr', network=d.network, need_amt=True )
 			self.data = self.xmrwallet_tx_data(
 			self.data = self.xmrwallet_tx_data(
 				op             = d.op,
 				op             = d.op,
 				create_time    = d.create_time,
 				create_time    = d.create_time,
@@ -283,7 +284,7 @@ class MoneroWalletOps:
 
 
 		opts = ('wallet_dir',)
 		opts = ('wallet_dir',)
 
 
-		def __init__(self,uarg_tuple,uopt_tuple):
+		def __init__(self,cfg,uarg_tuple,uopt_tuple):
 
 
 			def gen_classes():
 			def gen_classes():
 				for cls in type(self).__mro__:
 				for cls in type(self).__mro__:
@@ -291,6 +292,7 @@ class MoneroWalletOps:
 					if cls.__name__ == 'base':
 					if cls.__name__ == 'base':
 						break
 						break
 
 
+			self.cfg = cfg
 			classes = tuple(gen_classes())
 			classes = tuple(gen_classes())
 			self.opts = tuple(set(opt for cls in classes for opt in cls.opts))
 			self.opts = tuple(set(opt for cls in classes for opt in cls.opts))
 
 
@@ -314,7 +316,7 @@ class MoneroWalletOps:
 					cls.check_uopts(self)
 					cls.check_uopts(self)
 					id_cur = id(cls.check_uopts)
 					id_cur = id(cls.check_uopts)
 
 
-			self.proto = init_proto( 'xmr', network=g.network, need_amt=True )
+			self.proto = init_proto( cfg, 'xmr', network=self.cfg.network, need_amt=True )
 
 
 		def check_uopts(self):
 		def check_uopts(self):
 
 
@@ -363,7 +365,7 @@ class MoneroWalletOps:
 		)
 		)
 		wallet_exists = True
 		wallet_exists = True
 
 
-		def __init__(self,uarg_tuple,uopt_tuple):
+		def __init__(self,cfg,uarg_tuple,uopt_tuple):
 
 
 			def wallet_exists(fn):
 			def wallet_exists(fn):
 				try: os.stat(fn)
 				try: os.stat(fn)
@@ -379,9 +381,10 @@ class MoneroWalletOps:
 					elif not exists and self.wallet_exists:
 					elif not exists and self.wallet_exists:
 						die(1,f'Wallet {fn!r} not found!')
 						die(1,f'Wallet {fn!r} not found!')
 
 
-			super().__init__(uarg_tuple,uopt_tuple)
+			super().__init__(cfg,uarg_tuple,uopt_tuple)
 
 
 			self.kal = KeyAddrList(
 			self.kal = KeyAddrList(
+				cfg,
 				self.proto,
 				self.proto,
 				uarg.infile,
 				uarg.infile,
 				key_address_validity_check = True )
 				key_address_validity_check = True )
@@ -391,13 +394,15 @@ class MoneroWalletOps:
 			check_wallets()
 			check_wallets()
 
 
 			self.wd = MoneroWalletDaemon(
 			self.wd = MoneroWalletDaemon(
+				cfg         = self.cfg,
 				proto       = self.proto,
 				proto       = self.proto,
 				wallet_dir  = uopt.wallet_dir or '.',
 				wallet_dir  = uopt.wallet_dir or '.',
-				test_suite  = g.test_suite,
+				test_suite  = self.cfg.test_suite,
 				daemon_addr = uopt.daemon or None,
 				daemon_addr = uopt.daemon or None,
 			)
 			)
 
 
 			self.c = MoneroWalletRPCClient(
 			self.c = MoneroWalletRPCClient(
+				cfg             = self.cfg,
 				daemon          = self.wd,
 				daemon          = self.wd,
 				test_connection = False,
 				test_connection = False,
 			)
 			)
@@ -423,7 +428,7 @@ class MoneroWalletOps:
 				uopt.wallet_dir or '.','{}-{}-MoneroWallet{}'.format(
 				uopt.wallet_dir or '.','{}-{}-MoneroWallet{}'.format(
 					self.kal.al_id.sid,
 					self.kal.al_id.sid,
 					d.idx,
 					d.idx,
-					f'.{g.network}' if g.network != 'mainnet' else ''))
+					f'.{self.cfg.network}' if self.cfg.network != 'mainnet' else ''))
 
 
 		async def main(self):
 		async def main(self):
 			gmsg('\n{}ing {} wallet{}'.format(
 			gmsg('\n{}ing {} wallet{}'.format(
@@ -582,6 +587,7 @@ class MoneroWalletOps:
 					get_tx_metadata = True
 					get_tx_metadata = True
 				)
 				)
 				return MoneroMMGenTX.NewSigned(
 				return MoneroMMGenTX.NewSigned(
+					cfg            = self.parent.cfg,
 					op             = uarg.op,
 					op             = uarg.op,
 					network        = self.parent.proto.network,
 					network        = self.parent.proto.network,
 					seed_id        = self.parent.kal.al_id.sid,
 					seed_id        = self.parent.kal.al_id.sid,
@@ -609,6 +615,7 @@ class MoneroWalletOps:
 					die(3,'More than one TX required.  Cannot perform this sweep')
 					die(3,'More than one TX required.  Cannot perform this sweep')
 
 
 				return MoneroMMGenTX.NewSigned(
 				return MoneroMMGenTX.NewSigned(
+					cfg            = self.parent.cfg,
 					op             = uarg.op,
 					op             = uarg.op,
 					network        = self.parent.proto.network,
 					network        = self.parent.proto.network,
 					seed_id        = self.parent.kal.al_id.sid,
 					seed_id        = self.parent.kal.al_id.sid,
@@ -654,23 +661,24 @@ class MoneroWalletOps:
 				restore_height = uopt.restore_height,
 				restore_height = uopt.restore_height,
 				language       = 'English' )
 				language       = 'English' )
 
 
-			pp_msg(ret) if opt.debug else msg('  Address: {}'.format( ret['address'] ))
+			pp_msg(ret) if self.cfg.debug else msg('  Address: {}'.format( ret['address'] ))
 			return True
 			return True
 
 
 	class sync(wallet):
 	class sync(wallet):
 		name    = 'sync'
 		name    = 'sync'
 		opts    = ('rescan_blockchain',)
 		opts    = ('rescan_blockchain',)
 
 
-		def __init__(self,uarg_tuple,uopt_tuple):
+		def __init__(self,cfg,uarg_tuple,uopt_tuple):
 
 
-			super().__init__(uarg_tuple,uopt_tuple)
+			super().__init__(cfg,uarg_tuple,uopt_tuple)
 
 
 			host,port = uopt.daemon.split(':') if uopt.daemon else ('localhost',self.wd.daemon_port)
 			host,port = uopt.daemon.split(':') if uopt.daemon else ('localhost',self.wd.daemon_port)
 
 
 			from .daemon import CoinDaemon
 			from .daemon import CoinDaemon
 			self.dc = MoneroRPCClient(
 			self.dc = MoneroRPCClient(
+				cfg    = self.cfg,
 				proto  = self.proto,
 				proto  = self.proto,
-				daemon = CoinDaemon('xmr'),
+				daemon = CoinDaemon( self.cfg, 'xmr' ),
 				host   = host,
 				host   = host,
 				port   = int(port),
 				port   = int(port),
 				user   = None,
 				user   = None,
@@ -843,18 +851,20 @@ class MoneroWalletOps:
 			m = re.fullmatch(uarg_info['tx_relay_daemon'].pat,uopt.tx_relay_daemon,re.ASCII)
 			m = re.fullmatch(uarg_info['tx_relay_daemon'].pat,uopt.tx_relay_daemon,re.ASCII)
 
 
 			wd2 = MoneroWalletDaemon(
 			wd2 = MoneroWalletDaemon(
+				cfg         = self.cfg,
 				proto       = self.proto,
 				proto       = self.proto,
 				wallet_dir  = uopt.wallet_dir or '.',
 				wallet_dir  = uopt.wallet_dir or '.',
-				test_suite  = g.test_suite,
+				test_suite  = self.cfg.test_suite,
 				daemon_addr = m[1],
 				daemon_addr = m[1],
 				proxy       = m[2] )
 				proxy       = m[2] )
 
 
-			if g.test_suite:
+			if self.cfg.test_suite:
 				wd2.usr_daemon_args = ['--daemon-ssl-allow-any-cert']
 				wd2.usr_daemon_args = ['--daemon-ssl-allow-any-cert']
 
 
 			wd2.start()
 			wd2.start()
 
 
 			self.c = MoneroWalletRPCClient(
 			self.c = MoneroWalletRPCClient(
+				cfg    = self.cfg,
 				daemon = wd2 )
 				daemon = wd2 )
 
 
 		async def main(self):
 		async def main(self):
@@ -879,9 +889,9 @@ class MoneroWalletOps:
 				dest_addr = self.dest_addr
 				dest_addr = self.dest_addr
 			elif self.dest == None:
 			elif self.dest == None:
 				dest_acct = self.account
 				dest_acct = self.account
-				if keypress_confirm(f'\nCreate new address for account #{self.account}?'):
+				if keypress_confirm( self.cfg, f'\nCreate new address for account #{self.account}?' ):
 					dest_addr_chk = h.create_new_addr(self.account)
 					dest_addr_chk = h.create_new_addr(self.account)
-				elif keypress_confirm(f'Sweep to last existing address of account #{self.account}?'):
+				elif keypress_confirm( self.cfg, f'Sweep to last existing address of account #{self.account}?' ):
 					dest_addr_chk = None
 					dest_addr_chk = None
 				else:
 				else:
 					die(1,'Exiting at user request')
 					die(1,'Exiting at user request')
@@ -895,11 +905,11 @@ class MoneroWalletOps:
 				h2.open_wallet('destination')
 				h2.open_wallet('destination')
 				accts_data = h2.get_accts()[0]
 				accts_data = h2.get_accts()[0]
 
 
-				if keypress_confirm(f'\nCreate new account for wallet {bn!r}?'):
+				if keypress_confirm( self.cfg, f'\nCreate new account for wallet {bn!r}?' ):
 					dest_acct,dest_addr = h2.create_acct()
 					dest_acct,dest_addr = h2.create_acct()
 					dest_addr_idx = 0
 					dest_addr_idx = 0
 					h2.get_accts()
 					h2.get_accts()
-				elif keypress_confirm(f'Sweep to last existing account of wallet {bn!r}?'):
+				elif keypress_confirm( self.cfg, f'Sweep to last existing account of wallet {bn!r}?' ):
 					dest_acct,dest_addr_chk = h2.get_last_acct(accts_data)
 					dest_acct,dest_addr_chk = h2.get_last_acct(accts_data)
 					dest_addr,dest_addr_idx = h2.get_last_addr(dest_acct,display=False)
 					dest_addr,dest_addr_idx = h2.get_last_addr(dest_acct,display=False)
 					assert dest_addr_chk == dest_addr, 'dest_addr_chk2'
 					assert dest_addr_chk == dest_addr, 'dest_addr_chk2'
@@ -927,7 +937,7 @@ class MoneroWalletOps:
 			if uopt.no_relay:
 			if uopt.no_relay:
 				return True
 				return True
 
 
-			if keypress_confirm(f'Relay {self.name} transaction?'):
+			if keypress_confirm( self.cfg, f'Relay {self.name} transaction?' ):
 				w_desc = 'source'
 				w_desc = 'source'
 				if uopt.tx_relay_daemon:
 				if uopt.tx_relay_daemon:
 					await h.stop_wallet('source')
 					await h.stop_wallet('source')
@@ -1019,7 +1029,7 @@ class MoneroWalletOps:
 
 
 			if addr['label'] == self.label:
 			if addr['label'] == self.label:
 				ymsg('\nLabel is unchanged, operation cancelled')
 				ymsg('\nLabel is unchanged, operation cancelled')
-			elif keypress_confirm('  {} label?'.format('Set' if self.label else 'Remove')):
+			elif keypress_confirm( self.cfg, '  {} label?'.format('Set' if self.label else 'Remove') ):
 				h.set_label( self.account, self.address_idx, self.label )
 				h.set_label( self.account, self.address_idx, self.label )
 				accts_data = h.get_accts(print=False)[0]
 				accts_data = h.get_accts(print=False)[0]
 				ret = h.print_addrs(accts_data,self.account)
 				ret = h.print_addrs(accts_data,self.account)
@@ -1036,9 +1046,9 @@ class MoneroWalletOps:
 		name = 'relay'
 		name = 'relay'
 		opts = ('tx_relay_daemon',)
 		opts = ('tx_relay_daemon',)
 
 
-		def __init__(self,uarg_tuple,uopt_tuple):
+		def __init__(self,cfg,uarg_tuple,uopt_tuple):
 
 
-			super().__init__(uarg_tuple,uopt_tuple)
+			super().__init__(cfg,uarg_tuple,uopt_tuple)
 
 
 			if uopt.tx_relay_daemon:
 			if uopt.tx_relay_daemon:
 				m = re.fullmatch(uarg_info['tx_relay_daemon'].pat,uopt.tx_relay_daemon,re.ASCII)
 				m = re.fullmatch(uarg_info['tx_relay_daemon'].pat,uopt.tx_relay_daemon,re.ASCII)
@@ -1047,11 +1057,12 @@ class MoneroWalletOps:
 				md = None
 				md = None
 			else:
 			else:
 				from .daemon import CoinDaemon
 				from .daemon import CoinDaemon
-				md = CoinDaemon('xmr',test_suite=g.test_suite)
+				md = CoinDaemon( self.cfg, 'xmr', test_suite=self.cfg.test_suite )
 				host,port = md.host,md.rpc_port
 				host,port = md.host,md.rpc_port
 				proxy = None
 				proxy = None
 
 
 			self.dc = MoneroRPCClient(
 			self.dc = MoneroRPCClient(
+				cfg    = self.cfg,
 				proto  = self.proto,
 				proto  = self.proto,
 				daemon = md,
 				daemon = md,
 				host   = host,
 				host   = host,
@@ -1061,7 +1072,7 @@ class MoneroWalletOps:
 				test_connection = False, # relay is presumably a public node, so avoid extra connections
 				test_connection = False, # relay is presumably a public node, so avoid extra connections
 				proxy  = proxy )
 				proxy  = proxy )
 
 
-			self.tx = MoneroMMGenTX.Signed(uarg.infile)
+			self.tx = MoneroMMGenTX.Signed( self.cfg, uarg.infile )
 
 
 		async def main(self):
 		async def main(self):
 			msg('\n' + self.tx.get_info())
 			msg('\n' + self.tx.get_info())
@@ -1069,7 +1080,7 @@ class MoneroWalletOps:
 			if uopt.tx_relay_daemon:
 			if uopt.tx_relay_daemon:
 				self.display_tx_relay_info()
 				self.display_tx_relay_info()
 
 
-			if keypress_confirm('Relay transaction?'):
+			if keypress_confirm( self.cfg, 'Relay transaction?' ):
 				res = self.dc.call_raw(
 				res = self.dc.call_raw(
 					'send_raw_transaction',
 					'send_raw_transaction',
 					tx_as_hex = self.tx.data.blob
 					tx_as_hex = self.tx.data.blob
@@ -1088,10 +1099,10 @@ class MoneroWalletOps:
 		name = 'txview'
 		name = 'txview'
 
 
 		async def main(self):
 		async def main(self):
-			stdout_or_pager(
+			self.cfg._util.stdout_or_pager(
 				'\n'.join(
 				'\n'.join(
 					tx.get_info() for tx in
 					tx.get_info() for tx in
 					sorted(
 					sorted(
-						(MoneroMMGenTX.Signed(fn) for fn in uarg.infile),
+						(MoneroMMGenTX.Signed( self.cfg, fn ) for fn in uarg.infile),
 						key = lambda x: x.data.sign_time )
 						key = lambda x: x.data.sign_time )
 			))
 			))

+ 3 - 3
scripts/compute-file-chksum.py

@@ -17,11 +17,11 @@ opts_data = {
 	}
 	}
 }
 }
 
 
-cmd_args = opts.init(opts_data)
+cfg = opts.init(opts_data)
 
 
 from mmgen.fileutil import get_lines_from_file
 from mmgen.fileutil import get_lines_from_file
-lines = get_lines_from_file(cmd_args[0])
-start = (1,0)[bool(opt.include_first_line)]
+lines = get_lines_from_file( cfg, cfg._args[0] )
+start = (1,0)[bool(cfg.include_first_line)]
 a = make_chksum_6(' '.join(lines[start:]).encode())
 a = make_chksum_6(' '.join(lines[start:]).encode())
 if start == 1:
 if start == 1:
 	b = lines[0]
 	b = lines[0]

+ 18 - 20
scripts/create-token.py

@@ -175,19 +175,20 @@ contract Token is ERC20Interface, Owned, SafeMath {
 }
 }
 """ % req_solc_ver_pat
 """ % req_solc_ver_pat
 
 
-def create_src(proto,template,token_data,owner_addr):
+def create_src(cfg,template,token_data):
 
 
 	def gen():
 	def gen():
 		for k in token_data.fields:
 		for k in token_data.fields:
 			field = getattr(token_data,k)
 			field = getattr(token_data,k)
 			if k == 'owner_addr':
 			if k == 'owner_addr':
+				owner_addr = cfg._args[0]
 				from mmgen.addr import is_coin_addr
 				from mmgen.addr import is_coin_addr
-				if not is_coin_addr( proto, owner_addr.lower() ):
-					die(1,f'{owner_addr}: not a valid {proto.coin} coin address')
+				if not is_coin_addr( cfg._proto, owner_addr.lower() ):
+					die(1,f'{owner_addr}: not a valid {cfg._proto.coin} coin address')
 				val = '0x' + owner_addr
 				val = '0x' + owner_addr
 			else:
 			else:
 				val = (
 				val = (
-					getattr(opt,k)
+					getattr(cfg,k)
 					or getattr(field,'default',None)
 					or getattr(field,'default',None)
 					or die(1,f'The --{k} option must be specified')
 					or die(1,f'The --{k} option must be specified')
 				)
 				)
@@ -231,10 +232,10 @@ def check_solc_version():
 		Msg(f'solc version ({version_str}) does not match requirement ({req_solc_ver_pat})')
 		Msg(f'solc version ({version_str}) does not match requirement ({req_solc_ver_pat})')
 		return False
 		return False
 
 
-def compile_code(code):
+def compile_code(cfg,code):
 	cmd = ['solc','--optimize','--bin','--overwrite']
 	cmd = ['solc','--optimize','--bin','--overwrite']
-	if not opt.stdout:
-		cmd += ['--output-dir', opt.outdir or '.']
+	if not cfg.stdout:
+		cmd += ['--output-dir', cfg.outdir or '.']
 	cmd += ['-']
 	cmd += ['-']
 	msg(f"Executing: {' '.join(cmd)}")
 	msg(f"Executing: {' '.join(cmd)}")
 	cp = run(cmd,input=code.encode(),stdout=PIPE,stderr=PIPE)
 	cp = run(cmd,input=code.encode(),stdout=PIPE,stderr=PIPE)
@@ -247,37 +248,34 @@ def compile_code(code):
 	if err:
 	if err:
 		ymsg('Solidity compiler produced the following warning:')
 		ymsg('Solidity compiler produced the following warning:')
 		msg(err)
 		msg(err)
-	if opt.stdout:
+	if cfg.stdout:
 		o = out.split('\n')
 		o = out.split('\n')
 		return {k:o[i+2] for k in ('SafeMath','Owned','Token') for i in range(len(o)) if k in o[i]}
 		return {k:o[i+2] for k in ('SafeMath','Owned','Token') for i in range(len(o)) if k in o[i]}
 	else:
 	else:
-		vmsg(out)
+		cfg._util.vmsg(out)
 
 
 if __name__ == '__main__':
 if __name__ == '__main__':
 
 
-	cmd_args = opts.init(opts_data)
+	cfg = opts.init(opts_data)
 
 
-	if opt.check_solc_version:
+	if cfg.check_solc_version:
 		sys.exit(0 if check_solc_version() else 1)
 		sys.exit(0 if check_solc_version() else 1)
 
 
-	from mmgen.protocol import init_proto_from_opts
-	proto = init_proto_from_opts()
-
-	if not proto.coin in ('ETH','ETC'):
+	if not cfg._proto.coin in ('ETH','ETC'):
 		die(1,'--coin option must be ETH or ETC')
 		die(1,'--coin option must be ETH or ETC')
 
 
-	if not len(cmd_args) == 1:
+	if not len(cfg._args) == 1:
 		opts.usage()
 		opts.usage()
 
 
-	code = create_src( proto, solidity_code_template, token_data, cmd_args[0] )
+	code = create_src( cfg, solidity_code_template, token_data )
 
 
-	if opt.preprocess:
+	if cfg.preprocess:
 		Msg(code)
 		Msg(code)
 		sys.exit(0)
 		sys.exit(0)
 
 
-	out = compile_code(code)
+	out = compile_code( cfg, code )
 
 
-	if opt.stdout:
+	if cfg.stdout:
 		print(json.dumps(out))
 		print(json.dumps(out))
 
 
 	msg('Contract successfully compiled')
 	msg('Contract successfully compiled')

+ 4 - 4
scripts/tx-v2-to-v3.py

@@ -22,13 +22,13 @@ opts_data = {
 	}
 	}
 }
 }
 
 
-cmd_args = opts.init(opts_data)
+cfg = opts.init(opts_data)
 
 
 import asyncio
 import asyncio
 from mmgen.tx import CompletedTX
 from mmgen.tx import CompletedTX
 
 
-if len(cmd_args) != 1:
+if len(cfg._args) != 1:
 	opts.usage()
 	opts.usage()
 
 
-tx = asyncio.run(CompletedTX(cmd_args[0],quiet_open=True))
-tx.file.write(ask_tty=False,ask_overwrite=not opt.quiet,ask_write=not opt.quiet)
+tx = asyncio.run(CompletedTX(cfg._args[0],quiet_open=True))
+tx.file.write(ask_tty=False,ask_overwrite=not cfg.quiet,ask_write=not cfg.quiet)

+ 5 - 5
scripts/uninstall-mmgen.py

@@ -51,12 +51,12 @@ opts_data = {
 	}
 	}
 }
 }
 
 
-cmd_args = opts.init(opts_data)
+cfg = opts.init(opts_data)
 
 
 if gc.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')
 	die(1,'This program must be run as root')
 
 
-if len(cmd_args):
+if len(cfg._args):
 	opts.usage()
 	opts.usage()
 
 
 mod_dir = os.path.split(normalize_path(modpath_save))[0]
 mod_dir = os.path.split(normalize_path(modpath_save))[0]
@@ -77,13 +77,13 @@ for d in (ulb,mod_pardir):
 	# add files only, not directories
 	# add files only, not directories
 	del_list += [os.path.join(d,e) for e in os.listdir(d) if is_reg(os.path.join(d,e)) and e[:6] == 'mmgen-']
 	del_list += [os.path.join(d,e) for e in os.listdir(d) if is_reg(os.path.join(d,e)) and e[:6] == 'mmgen-']
 
 
-if opt.list_paths:
+if cfg.list_paths:
 	die(1,'\n'.join(del_list))
 	die(1,'\n'.join(del_list))
 
 
-if not opt.no_prompt:
+if not cfg.no_prompt:
 	m = 'Deleting the following paths and files:\n  {}\nProceed?'
 	m = 'Deleting the following paths and files:\n  {}\nProceed?'
 	from mmgen.ui import keypress_confirm
 	from mmgen.ui import keypress_confirm
-	if not keypress_confirm(m.format('\n  '.join(del_list))):
+	if not keypress_confirm( cfg, m.format('\n  '.join(del_list)) ):
 		die(1,'Exiting at user request')
 		die(1,'Exiting at user request')
 
 
 import shutil
 import shutil

+ 55 - 50
test/gentest.py

@@ -28,10 +28,9 @@ sys.path.insert(0,overlay_setup(repo_root))
 
 
 # Import these _after_ local path's been added to sys.path
 # Import these _after_ local path's been added to sys.path
 import mmgen.opts as opts
 import mmgen.opts as opts
-from mmgen.globalvars import g,gc
-from mmgen.opts import opt
+from mmgen.globalvars import gc
 from mmgen.color import green,red,purple
 from mmgen.color import green,red,purple
-from mmgen.util import msg,qmsg,qmsg_r,vmsg,capfirst,is_int,die
+from mmgen.util import msg,capfirst,is_int,die
 
 
 results_file = 'gentest.out.json'
 results_file = 'gentest.out.json'
 
 
@@ -149,7 +148,7 @@ class GenTool(object):
 		self.data = {}
 		self.data = {}
 
 
 	def __del__(self):
 	def __del__(self):
-		if opt.save_results:
+		if cfg.save_results:
 			key = f'{self.proto.coin}-{self.proto.network}-{self.addr_type.name}-{self.desc}'.lower()
 			key = f'{self.proto.coin}-{self.proto.network}-{self.addr_type.name}-{self.desc}'.lower()
 			saved_results[key] = {k.hex():v._asdict() for k,v in self.data.items()}
 			saved_results[key] = {k.hex():v._asdict() for k,v in self.data.items()}
 
 
@@ -251,14 +250,14 @@ def find_or_check_tool(proto,addr_type,toolname):
 	if toolname not in ext_progs + ['ext']:
 	if toolname not in ext_progs + ['ext']:
 		die(1,f'{toolname!r}: unsupported tool for network {proto.network}')
 		die(1,f'{toolname!r}: unsupported tool for network {proto.network}')
 
 
-	if opt.all_coins and toolname == 'ext':
+	if cfg.all_coins and toolname == 'ext':
 		die(1,"'--all-coins' must be combined with a specific external testing tool")
 		die(1,"'--all-coins' must be combined with a specific external testing tool")
 	else:
 	else:
 		tool = cinfo.get_test_support(
 		tool = cinfo.get_test_support(
 			proto.coin,
 			proto.coin,
 			addr_type.name,
 			addr_type.name,
 			proto.network,
 			proto.network,
-			verbose = not opt.quiet,
+			verbose = not cfg.quiet,
 			toolname = toolname if toolname != 'ext' else None )
 			toolname = toolname if toolname != 'ext' else None )
 		if tool and toolname in ext_progs and toolname != tool:
 		if tool and toolname in ext_progs and toolname != tool:
 			sys.exit(3)
 			sys.exit(3)
@@ -283,11 +282,11 @@ def test_equal(desc,a_val,b_val,in_bytes,sec,wif,a_desc,b_desc):
 				w=max(len(e) for e in (a_desc,b_desc)) + 1
 				w=max(len(e) for e in (a_desc,b_desc)) + 1
 		).rstrip())
 		).rstrip())
 
 
-def do_ab_test(proto,cfg,addr_type,gen1,kg2,ag,tool,cache_data):
+def do_ab_test(proto,scfg,addr_type,gen1,kg2,ag,tool,cache_data):
 
 
 	def do_ab_inner(n,trounds,in_bytes):
 	def do_ab_inner(n,trounds,in_bytes):
 		global last_t
 		global last_t
-		if opt.verbose or time.time() - last_t >= 0.1:
+		if cfg.verbose or time.time() - last_t >= 0.1:
 			qmsg_r(f'\rRound {i+1}/{trounds} ')
 			qmsg_r(f'\rRound {i+1}/{trounds} ')
 			last_t = time.time()
 			last_t = time.time()
 		sec = PrivKey(proto,in_bytes,compressed=addr_type.compressed,pubkey_type=addr_type.pubkey_type)
 		sec = PrivKey(proto,in_bytes,compressed=addr_type.compressed,pubkey_type=addr_type.pubkey_type)
@@ -296,7 +295,7 @@ def do_ab_test(proto,cfg,addr_type,gen1,kg2,ag,tool,cache_data):
 		tinfo = ( in_bytes, sec, sec.wif, type(kg1).__name__, type(kg2).__name__ if kg2 else tool.desc )
 		tinfo = ( in_bytes, sec, sec.wif, type(kg1).__name__, type(kg2).__name__ if kg2 else tool.desc )
 
 
 		def do_msg():
 		def do_msg():
-			if opt.verbose:
+			if cfg.verbose:
 				msg( fs.format( b=in_bytes.hex(), r=sec.hex(), k=sec.wif, v=vk2, a=addr1 ))
 				msg( fs.format( b=in_bytes.hex(), r=sec.hex(), k=sec.wif, v=vk2, a=addr1 ))
 
 
 		if tool:
 		if tool:
@@ -321,10 +320,10 @@ def do_ab_test(proto,cfg,addr_type,gen1,kg2,ag,tool,cache_data):
 			for privbytes in tuple(tool.data)[len(edgecase_sks):]:
 			for privbytes in tuple(tool.data)[len(edgecase_sks):]:
 				yield privbytes
 				yield privbytes
 		else:
 		else:
-			for i in range(cfg.rounds):
+			for i in range(scfg.rounds):
 				yield getrand(32)
 				yield getrand(32)
 
 
-	kg1 = KeyGenerator( proto, addr_type.pubkey_type, gen1 )
+	kg1 = KeyGenerator( cfg, proto, addr_type.pubkey_type, gen1 )
 	if type(kg1) == type(kg2):
 	if type(kg1) == type(kg2):
 		die(4,'Key generators are the same!')
 		die(4,'Key generators are the same!')
 
 
@@ -364,39 +363,39 @@ def do_ab_test(proto,cfg,addr_type,gen1,kg2,ag,tool,cache_data):
 	qmsg(purple('edge cases:'))
 	qmsg(purple('edge cases:'))
 	for i,privbytes in enumerate(edgecase_sks):
 	for i,privbytes in enumerate(edgecase_sks):
 		do_ab_inner(i,len(edgecase_sks),privbytes)
 		do_ab_inner(i,len(edgecase_sks),privbytes)
-	qmsg(green('\rOK            ' if opt.verbose else 'OK'))
+	qmsg(green('\rOK            ' if cfg.verbose else 'OK'))
 
 
 	qmsg(purple('random input:'))
 	qmsg(purple('random input:'))
 	for i,privbytes in enumerate(get_randbytes()):
 	for i,privbytes in enumerate(get_randbytes()):
-		do_ab_inner(i,cfg.rounds,privbytes)
-	qmsg(green('\rOK            ' if opt.verbose else 'OK'))
+		do_ab_inner(i,scfg.rounds,privbytes)
+	qmsg(green('\rOK            ' if cfg.verbose else 'OK'))
 
 
 def init_tool(proto,addr_type,toolname):
 def init_tool(proto,addr_type,toolname):
 	return globals()['GenTool'+capfirst(toolname.replace('-','_'))](proto,addr_type)
 	return globals()['GenTool'+capfirst(toolname.replace('-','_'))](proto,addr_type)
 
 
-def ab_test(proto,cfg):
+def ab_test(proto,scfg):
 
 
-	addr_type = MMGenAddrType( proto=proto, id_str=opt.type or proto.dfl_mmtype )
+	addr_type = MMGenAddrType( proto=proto, id_str=cfg.type or proto.dfl_mmtype )
 
 
-	if cfg.gen2:
-		assert cfg.gen1 != 'all', "'all' must be used only with external tool"
-		kg2 = KeyGenerator( proto, addr_type.pubkey_type, cfg.gen2 )
+	if scfg.gen2:
+		assert scfg.gen1 != 'all', "'all' must be used only with external tool"
+		kg2 = KeyGenerator( cfg, proto, addr_type.pubkey_type, scfg.gen2 )
 		tool = None
 		tool = None
 	else:
 	else:
-		toolname = find_or_check_tool( proto, addr_type, cfg.tool )
+		toolname = find_or_check_tool( proto, addr_type, scfg.tool )
 		if toolname == None:
 		if toolname == None:
-			ymsg(f'Warning: skipping tool {cfg.tool!r} for {proto.coin} {addr_type.name}')
+			ymsg(f'Warning: skipping tool {scfg.tool!r} for {proto.coin} {addr_type.name}')
 			return
 			return
 		tool = init_tool( proto, addr_type, toolname )
 		tool = init_tool( proto, addr_type, toolname )
 		kg2 = None
 		kg2 = None
 
 
-	ag = AddrGenerator( proto, addr_type )
+	ag = AddrGenerator( cfg, proto, addr_type )
 
 
-	if cfg.all_backends: # check all backends against external tool
+	if scfg.all_backends: # check all backends against external tool
 		for n in range(len(get_backends(addr_type.pubkey_type))):
 		for n in range(len(get_backends(addr_type.pubkey_type))):
-			do_ab_test( proto, cfg, addr_type, gen1=n+1, kg2=kg2, ag=ag, tool=tool, cache_data=cfg.rounds < 1000 and not n )
+			do_ab_test( proto, scfg, addr_type, gen1=n+1, kg2=kg2, ag=ag, tool=tool, cache_data=scfg.rounds < 1000 and not n )
 	else:                # check specific backend against external tool or another backend
 	else:                # check specific backend against external tool or another backend
-		do_ab_test( proto, cfg, addr_type, gen1=cfg.gen1, kg2=kg2, ag=ag, tool=tool, cache_data=False )
+		do_ab_test( proto, scfg, addr_type, gen1=scfg.gen1, kg2=kg2, ag=ag, tool=tool, cache_data=False )
 
 
 def speed_test(proto,kg,ag,rounds):
 def speed_test(proto,kg,ag,rounds):
 	qmsg(green('Testing speed of address generator {!r} for coin {}'.format(
 	qmsg(green('Testing speed of address generator {!r} for coin {}'.format(
@@ -419,7 +418,7 @@ def speed_test(proto,kg,ag,rounds):
 	qmsg(
 	qmsg(
 		f'\rRound {i+1}/{rounds} ' +
 		f'\rRound {i+1}/{rounds} ' +
 		f'\n{rounds} addresses generated' +
 		f'\n{rounds} addresses generated' +
-		('' if g.test_suite_deterministic else f' in {time.time()-start:.2f} seconds')
+		('' if cfg.test_suite_deterministic else f' in {time.time()-start:.2f} seconds')
 	)
 	)
 
 
 def dump_test(proto,kg,ag,filename):
 def dump_test(proto,kg,ag,filename):
@@ -446,7 +445,7 @@ def dump_test(proto,kg,ag,filename):
 		tinfo = (b_sec,b_sec.hex(),b_wif,type(kg).__name__,filename)
 		tinfo = (b_sec,b_sec.hex(),b_wif,type(kg).__name__,filename)
 		test_equal('addresses',a_addr,b_addr,*tinfo)
 		test_equal('addresses',a_addr,b_addr,*tinfo)
 
 
-	qmsg(green(('\n','')[bool(opt.verbose)] + 'OK'))
+	qmsg(green(('\n','')[bool(cfg.verbose)] + 'OK'))
 
 
 def get_protos(proto,addr_type,toolname):
 def get_protos(proto,addr_type,toolname):
 
 
@@ -455,17 +454,17 @@ def get_protos(proto,addr_type,toolname):
 	for coin in cinfo.external_tests[proto.network][toolname]:
 	for coin in cinfo.external_tests[proto.network][toolname]:
 		if coin.lower() not in CoinProtocol.coins:
 		if coin.lower() not in CoinProtocol.coins:
 			continue
 			continue
-		ret = init_proto(coin,testnet=proto.testnet)
+		ret = init_proto( cfg, coin, testnet=proto.testnet )
 		if addr_type not in ret.mmtypes:
 		if addr_type not in ret.mmtypes:
 			continue
 			continue
 		yield ret
 		yield ret
 
 
 def parse_args():
 def parse_args():
 
 
-	if len(cmd_args) != 2:
+	if len(cfg._args) != 2:
 		opts.usage()
 		opts.usage()
 
 
-	arg1,arg2 = cmd_args
+	arg1,arg2 = cfg._args
 	gen1,gen2,rounds = (0,0,0)
 	gen1,gen2,rounds = (0,0,0)
 	tool,all_backends,dumpfile = (None,None,None)
 	tool,all_backends,dumpfile = (None,None,None)
 
 
@@ -498,13 +497,12 @@ def parse_args():
 				die(1,"First part of first argument must be a generator backend number or 'all'")
 				die(1,"First part of first argument must be a generator backend number or 'all'")
 
 
 		if is_int(b):
 		if is_int(b):
-			if opt.all_coins:
+			if cfg.all_coins:
 				die(1,'--all-coins must be used with external tool only')
 				die(1,'--all-coins must be used with external tool only')
 			gen2 = b
 			gen2 = b
 		else:
 		else:
 			tool = b
 			tool = b
-			proto = init_proto_from_opts()
-			ext_progs = list(cinfo.external_tests[proto.network]) + ['ext']
+			ext_progs = list(cinfo.external_tests[cfg._proto.network]) + ['ext']
 			if b not in ext_progs:
 			if b not in ext_progs:
 				die(1,f'Second part of first argument must be a generator backend number or one of {ext_progs}')
 				die(1,f'Second part of first argument must be a generator backend number or one of {ext_progs}')
 
 
@@ -519,21 +517,21 @@ def parse_args():
 
 
 def main():
 def main():
 
 
-	cfg = parse_args()
-	proto = init_proto_from_opts()
-	addr_type = MMGenAddrType( proto=proto, id_str=opt.type or proto.dfl_mmtype )
+	scfg = parse_args()
 
 
-	if cfg.test == 'ab':
-		protos = get_protos(proto,addr_type,cfg.tool) if opt.all_coins else [proto]
-		for proto in protos:
-			ab_test( proto, cfg )
+	addr_type = MMGenAddrType( proto=proto, id_str=cfg.type or proto.dfl_mmtype )
+
+	if scfg.test == 'ab':
+		protos = get_protos(proto,addr_type,scfg.tool) if cfg.all_coins else [proto]
+		for p in protos:
+			ab_test( p, scfg )
 	else:
 	else:
-		kg = KeyGenerator( proto, addr_type.pubkey_type, cfg.gen1 )
-		ag = AddrGenerator( proto, addr_type )
-		if cfg.test == 'speed':
-			speed_test( proto, kg, ag, cfg.rounds )
-		elif cfg.test == 'dump':
-			dump_test( proto, kg, ag, cfg.dumpfile )
+		kg = KeyGenerator( cfg, proto, addr_type.pubkey_type, scfg.gen1 )
+		ag = AddrGenerator( cfg, proto, addr_type )
+		if scfg.test == 'speed':
+			speed_test( proto, kg, ag, scfg.rounds )
+		elif scfg.test == 'dump':
+			dump_test( proto, kg, ag, scfg.dumpfile )
 
 
 	if saved_results:
 	if saved_results:
 		import json
 		import json
@@ -542,19 +540,26 @@ def main():
 
 
 from subprocess import run,PIPE,DEVNULL
 from subprocess import run,PIPE,DEVNULL
 from collections import namedtuple
 from collections import namedtuple
-from mmgen.protocol import init_proto,init_proto_from_opts,CoinProtocol
+from mmgen.protocol import init_proto,CoinProtocol
 from mmgen.altcoin import init_genonly_altcoins,CoinInfo as cinfo
 from mmgen.altcoin import init_genonly_altcoins,CoinInfo as cinfo
 from mmgen.key import PrivKey
 from mmgen.key import PrivKey
 from mmgen.addr import MMGenAddrType
 from mmgen.addr import MMGenAddrType
 from mmgen.addrgen import KeyGenerator,AddrGenerator
 from mmgen.addrgen import KeyGenerator,AddrGenerator
 from mmgen.keygen import get_backends
 from mmgen.keygen import get_backends
-
-from test.include.common import getrand,get_ethkey
+from test.include.common import getrand,get_ethkey,set_globals
 
 
 gtr = namedtuple('gen_tool_result',['wif','addr','viewkey'])
 gtr = namedtuple('gen_tool_result',['wif','addr','viewkey'])
 sd = namedtuple('saved_data_item',['reduced','wif','addr','viewkey'])
 sd = namedtuple('saved_data_item',['reduced','wif','addr','viewkey'])
 
 
 sys.argv = [sys.argv[0]] + ['--skip-cfg-file'] + sys.argv[1:]
 sys.argv = [sys.argv[0]] + ['--skip-cfg-file'] + sys.argv[1:]
-cmd_args = opts.init(opts_data)
+
+cfg = opts.init(opts_data)
+set_globals(cfg)
+
+qmsg = cfg._util.qmsg
+qmsg_r = cfg._util.qmsg_r
+vmsg = cfg._util.vmsg
+
+proto = cfg._proto
 
 
 main()
 main()

+ 5 - 1
test/hashfunc.py

@@ -23,7 +23,6 @@ test/hashfunc.py: Test internal implementations of SHA256, SHA512 and Keccak256
 import sys,os
 import sys,os
 import include.tests_header
 import include.tests_header
 from mmgen.util import die
 from mmgen.util import die
-from test.include.common import getrand
 
 
 assert len(sys.argv) in (2,3),"Test takes 1 or 2 arguments: test name, plus optional rounds count"
 assert len(sys.argv) in (2,3),"Test takes 1 or 2 arguments: test name, plus optional rounds count"
 test = sys.argv[1].capitalize()
 test = sys.argv[1].capitalize()
@@ -137,6 +136,11 @@ class TestSha512(TestSha2):
 		0x113f9804bef90dae, 0x1b710b35131c471b, 0x28db77f523047d84, 0x32caab7b40c72493, 0x3c9ebe0a15c9bebc,
 		0x113f9804bef90dae, 0x1b710b35131c471b, 0x28db77f523047d84, 0x32caab7b40c72493, 0x3c9ebe0a15c9bebc,
 		0x431d67c49c100d4c, 0x4cc5d4becb3e42b6, 0x597f299cfc657e2a, 0x5fcb6fab3ad6faec, 0x6c44198c4a475817 )
 		0x431d67c49c100d4c, 0x4cc5d4becb3e42b6, 0x597f299cfc657e2a, 0x5fcb6fab3ad6faec, 0x6c44198c4a475817 )
 
 
+from test.include.common import getrand,set_globals
+from mmgen.globalvars import Config
+
+set_globals(Config())
+
 t = globals()['Test'+test]()
 t = globals()['Test'+test]()
 msg(f'Testing internal implementation of {t.desc}\n')
 msg(f'Testing internal implementation of {t.desc}\n')
 t.test_constants()
 t.test_constants()

+ 24 - 23
test/include/coin_daemon_control.py

@@ -49,7 +49,7 @@ Valid network IDs: {nid}, all, or no_xmr
 	}
 	}
 }
 }
 
 
-cmd_args = opts.init(opts_data)
+cfg = opts.init(opts_data)
 
 
 from mmgen.daemon import *
 from mmgen.daemon import *
 
 
@@ -60,58 +60,59 @@ class warn_missing_exec(oneshot_warning):
 def run(network_id=None,proto=None,daemon_id=None,missing_exec_ok=True):
 def run(network_id=None,proto=None,daemon_id=None,missing_exec_ok=True):
 
 
 	d = CoinDaemon(
 	d = CoinDaemon(
+		cfg,
 		network_id = network_id,
 		network_id = network_id,
 		proto      = proto,
 		proto      = proto,
-		test_suite = not opt.usermode,
-		opts       = ['no_daemonize'] if opt.no_daemonize else None,
-		port_shift = int(opt.port_shift or 0),
-		datadir    = opt.datadir,
+		test_suite = not cfg.usermode,
+		opts       = ['no_daemonize'] if cfg.no_daemonize else None,
+		port_shift = int(cfg.port_shift or 0),
+		datadir    = cfg.datadir,
 		daemon_id  = daemon_id )
 		daemon_id  = daemon_id )
 
 
-	if opt.mainnet_only and d.network != 'mainnet':
+	if cfg.mainnet_only and d.network != 'mainnet':
 		return
 		return
 
 
-	d.debug = d.debug or opt.debug
-	d.wait = not opt.no_wait
+	d.debug = d.debug or cfg.debug
+	d.wait = not cfg.no_wait
 
 
 	if missing_exec_ok:
 	if missing_exec_ok:
 		try:
 		try:
 			d.get_exec_version_str()
 			d.get_exec_version_str()
 		except:
 		except:
-			if not opt.quiet:
+			if not cfg.quiet:
 				warn_missing_exec( div=d.exec_fn, fmt_args=(d.exec_fn,) )
 				warn_missing_exec( div=d.exec_fn, fmt_args=(d.exec_fn,) )
 			return
 			return
-	if opt.print_version:
+	if cfg.print_version:
 		msg('{:16} {}'.format( d.exec_fn+':', d.get_exec_version_str() ))
 		msg('{:16} {}'.format( d.exec_fn+':', d.get_exec_version_str() ))
-	elif opt.get_state:
+	elif cfg.get_state:
 		print(d.state_msg())
 		print(d.state_msg())
-	elif opt.testing:
+	elif cfg.testing:
 		for cmd in d.start_cmds if action == 'start' else [d.stop_cmd]:
 		for cmd in d.start_cmds if action == 'start' else [d.stop_cmd]:
 			print(' '.join(cmd))
 			print(' '.join(cmd))
 	else:
 	else:
 		if action == 'stop' and hasattr(d,'rpc'):
 		if action == 'stop' and hasattr(d,'rpc'):
-			async_run(d.rpc.stop_daemon(quiet=opt.quiet))
+			async_run(d.rpc.stop_daemon(quiet=cfg.quiet))
 		else:
 		else:
-			d.cmd(action,quiet=opt.quiet)
+			d.cmd(action,quiet=cfg.quiet)
 
 
-if opt.daemon_ids:
+if cfg.daemon_ids:
 	print('\n'.join(CoinDaemon.all_daemon_ids()))
 	print('\n'.join(CoinDaemon.all_daemon_ids()))
-elif 'all' in cmd_args or 'no_xmr' in cmd_args:
-	if len(cmd_args) != 1:
+elif 'all' in cfg._args or 'no_xmr' in cfg._args:
+	if len(cfg._args) != 1:
 		die(1,"'all' or 'no_xmr' must be the sole argument")
 		die(1,"'all' or 'no_xmr' must be the sole argument")
 	from mmgen.protocol import init_proto
 	from mmgen.protocol import init_proto
 	for coin in CoinDaemon.coins:
 	for coin in CoinDaemon.coins:
-		if coin == 'XMR' and cmd_args[0] == 'no_xmr':
+		if coin == 'XMR' and cfg._args[0] == 'no_xmr':
 			continue
 			continue
-		for daemon_id in CoinDaemon.get_daemon_ids(coin):
-			for network in CoinDaemon.get_daemon(coin,daemon_id).networks:
+		for daemon_id in CoinDaemon.get_daemon_ids(cfg,coin):
+			for network in CoinDaemon.get_daemon(cfg,coin,daemon_id).networks:
 				run(
 				run(
-					proto           = init_proto(coin=coin,network=network),
+					proto           = init_proto( cfg, coin=coin, network=network ),
 					daemon_id       = daemon_id,
 					daemon_id       = daemon_id,
 					missing_exec_ok = True )
 					missing_exec_ok = True )
 else:
 else:
-	ids = cmd_args
-	network_ids = CoinDaemon.get_network_ids()
+	ids = cfg._args
+	network_ids = CoinDaemon.get_network_ids(cfg)
 	if not ids:
 	if not ids:
 		opts.usage()
 		opts.usage()
 	for i in ids:
 	for i in ids:

+ 46 - 18
test/include/common.py

@@ -25,6 +25,33 @@ from subprocess import run,PIPE
 from mmgen.common import *
 from mmgen.common import *
 from mmgen.fileutil import write_data_to_file,get_data_from_file
 from mmgen.fileutil import write_data_to_file,get_data_from_file
 
 
+def noop(*args,**kwargs):
+	pass
+
+def set_globals(cfg):
+	"""
+	make `cfg`, `qmsg`, `vmsg`, etc. available as globals to scripts by setting
+	the module attr
+	"""
+	import test.include.common as this
+	this.cfg = cfg
+
+	if cfg.quiet:
+		this.qmsg = this.qmsg_r = noop
+	else:
+		this.qmsg = msg
+		this.qmsg_r = msg_r
+
+	if cfg.verbose:
+		this.vmsg = msg
+		this.vmsg_r = msg_r
+		this.Vmsg = Msg
+		this.Vmsg_r = Msg_r
+	else:
+		this.vmsg = this.vmsg_r = this.Vmsg = this.Vmsg_r = noop
+
+	this.dmsg = msg if cfg.debug else noop
+
 def strip_ansi_escapes(s):
 def strip_ansi_escapes(s):
 	import re
 	import re
 	return re.sub('\x1b' + r'\[[;0-9]+?m','',s)
 	return re.sub('\x1b' + r'\[[;0-9]+?m','',s)
@@ -65,7 +92,7 @@ ref_kafile_pass = 'kafile password'
 ref_kafile_hash_preset = '1'
 ref_kafile_hash_preset = '1'
 
 
 def getrand(n):
 def getrand(n):
-	if g.test_suite_deterministic:
+	if cfg.test_suite_deterministic:
 		from mmgen.test import fake_urandom
 		from mmgen.test import fake_urandom
 		return fake_urandom(n)
 		return fake_urandom(n)
 	else:
 	else:
@@ -120,6 +147,7 @@ def get_tmpfile(cfg,fn):
 
 
 def write_to_file(fn,data,binary=False):
 def write_to_file(fn,data,binary=False):
 	write_data_to_file(
 	write_data_to_file(
+		cfg,
 		fn,
 		fn,
 		data,
 		data,
 		quiet = True,
 		quiet = True,
@@ -130,7 +158,7 @@ def write_to_tmpfile(cfg,fn,data,binary=False):
 	write_to_file(  os.path.join(cfg['tmpdir'],fn), data=data, binary=binary )
 	write_to_file(  os.path.join(cfg['tmpdir'],fn), data=data, binary=binary )
 
 
 def read_from_file(fn,binary=False):
 def read_from_file(fn,binary=False):
-	return get_data_from_file(fn,quiet=True,binary=binary)
+	return get_data_from_file( cfg, fn, quiet=True, binary=binary )
 
 
 def read_from_tmpfile(cfg,fn,binary=False):
 def read_from_tmpfile(cfg,fn,binary=False):
 	return read_from_file(os.path.join(cfg['tmpdir'],fn),binary=binary)
 	return read_from_file(os.path.join(cfg['tmpdir'],fn),binary=binary)
@@ -139,9 +167,9 @@ def joinpath(*args,**kwargs):
 	return os.path.join(*args,**kwargs)
 	return os.path.join(*args,**kwargs)
 
 
 def ok():
 def ok():
-	if opt.profile:
+	if cfg.profile:
 		return
 		return
-	if opt.verbose or opt.exact_output:
+	if cfg.verbose or cfg.exact_output:
 		gmsg('OK')
 		gmsg('OK')
 	else:
 	else:
 		msg(' OK')
 		msg(' OK')
@@ -161,11 +189,11 @@ def init_coverage():
 	return coverdir,acc_file
 	return coverdir,acc_file
 
 
 def silence():
 def silence():
-	if not (opt.verbose or opt.exact_output):
+	if not (cfg.verbose or cfg.exact_output):
 		gv.stdout = gv.stderr = open(os.devnull,'w')
 		gv.stdout = gv.stderr = open(os.devnull,'w')
 
 
 def end_silence():
 def end_silence():
-	if not (opt.verbose or opt.exact_output):
+	if not (cfg.verbose or cfg.exact_output):
 		gv.stdout.close()
 		gv.stdout.close()
 		gv.stdout = sys.stdout
 		gv.stdout = sys.stdout
 		gv.stderr = sys.stderr
 		gv.stderr = sys.stderr
@@ -177,38 +205,38 @@ def omsg_r(s):
 	sys.stderr.flush()
 	sys.stderr.flush()
 
 
 def imsg(s):
 def imsg(s):
-	if opt.verbose or opt.exact_output:
+	if cfg.verbose or cfg.exact_output:
 		omsg(s)
 		omsg(s)
 def imsg_r(s):
 def imsg_r(s):
-	if opt.verbose or opt.exact_output:
+	if cfg.verbose or cfg.exact_output:
 		omsg_r(s)
 		omsg_r(s)
 
 
 def iqmsg(s):
 def iqmsg(s):
-	if not opt.quiet:
+	if not cfg.quiet:
 		omsg(s)
 		omsg(s)
 def iqmsg_r(s):
 def iqmsg_r(s):
-	if not opt.quiet:
+	if not cfg.quiet:
 		omsg_r(s)
 		omsg_r(s)
 
 
 def oqmsg(s):
 def oqmsg(s):
-	if not (opt.verbose or opt.exact_output):
+	if not (cfg.verbose or cfg.exact_output):
 		omsg(s)
 		omsg(s)
 def oqmsg_r(s):
 def oqmsg_r(s):
-	if not (opt.verbose or opt.exact_output):
+	if not (cfg.verbose or cfg.exact_output):
 		omsg_r(s)
 		omsg_r(s)
 
 
 def end_msg(t):
 def end_msg(t):
 	omsg(green(
 	omsg(green(
 		'All requested tests finished OK' +
 		'All requested tests finished OK' +
-		('' if g.test_suite_deterministic else f', elapsed time: {t//60:02d}:{t%60:02d}')
+		('' if cfg.test_suite_deterministic else f', elapsed time: {t//60:02d}:{t%60:02d}')
 	))
 	))
 
 
 def start_test_daemons(*network_ids,remove_datadir=False):
 def start_test_daemons(*network_ids,remove_datadir=False):
-	if not opt.no_daemon_autostart:
+	if not cfg.no_daemon_autostart:
 		return test_daemons_ops(*network_ids,op='start',remove_datadir=remove_datadir)
 		return test_daemons_ops(*network_ids,op='start',remove_datadir=remove_datadir)
 
 
 def stop_test_daemons(*network_ids,force=False,remove_datadir=False):
 def stop_test_daemons(*network_ids,force=False,remove_datadir=False):
-	if force or not opt.no_daemon_stop:
+	if force or not cfg.no_daemon_stop:
 		return test_daemons_ops(*network_ids,op='stop',remove_datadir=remove_datadir)
 		return test_daemons_ops(*network_ids,op='stop',remove_datadir=remove_datadir)
 
 
 def restart_test_daemons(*network_ids,remove_datadir=False):
 def restart_test_daemons(*network_ids,remove_datadir=False):
@@ -217,12 +245,12 @@ def restart_test_daemons(*network_ids,remove_datadir=False):
 	return start_test_daemons(*network_ids,remove_datadir=remove_datadir)
 	return start_test_daemons(*network_ids,remove_datadir=remove_datadir)
 
 
 def test_daemons_ops(*network_ids,op,remove_datadir=False):
 def test_daemons_ops(*network_ids,op,remove_datadir=False):
-	if not opt.no_daemon_autostart:
+	if not cfg.no_daemon_autostart:
 		from mmgen.daemon import CoinDaemon
 		from mmgen.daemon import CoinDaemon
-		silent = not (opt.verbose or opt.exact_output)
+		silent = not (cfg.verbose or cfg.exact_output)
 		ret = False
 		ret = False
 		for network_id in network_ids:
 		for network_id in network_ids:
-			d = CoinDaemon(network_id,test_suite=True)
+			d = CoinDaemon(cfg,network_id,test_suite=True)
 			if remove_datadir:
 			if remove_datadir:
 				d.stop(silent=True)
 				d.stop(silent=True)
 				d.remove_datadir()
 				d.remove_datadir()

+ 22 - 24
test/include/pexpect.py

@@ -21,10 +21,8 @@ test.include.pexpect: pexpect implementation for MMGen test suites
 """
 """
 
 
 import sys,os,time
 import sys,os,time
-from mmgen.globalvars import g
-from mmgen.opts import opt
-from mmgen.util import msg,msg_r,vmsg,vmsg_r,rmsg,red,yellow,green,cyan,die
-from .common import *
+from mmgen.util import msg,msg_r,rmsg,red,yellow,green,cyan,die
+from .common import cfg,vmsg,vmsg_r,getrandstr,strip_ansi_escapes
 
 
 try:
 try:
 	import pexpect
 	import pexpect
@@ -48,18 +46,18 @@ class MMGenPexpect:
 		self.skip_ok = False
 		self.skip_ok = False
 		self.sent_value = None
 		self.sent_value = None
 
 
-		if opt.direct_exec:
+		if cfg.direct_exec:
 			msg('')
 			msg('')
 			from subprocess import run,DEVNULL
 			from subprocess import run,DEVNULL
 			run([args[0]] + args[1:],check=True,stdout=DEVNULL if no_output else None)
 			run([args[0]] + args[1:],check=True,stdout=DEVNULL if no_output else None)
 		else:
 		else:
-			timeout = int(timeout or opt.pexpect_timeout or 0) or (60,5)[bool(opt.debug_pexpect)]
+			timeout = int(timeout or cfg.pexpect_timeout or 0) or (60,5)[bool(cfg.debug_pexpect)]
 			if pexpect_spawn:
 			if pexpect_spawn:
 				self.p = pexpect.spawn(args[0],args[1:],encoding='utf8',timeout=timeout,env=env)
 				self.p = pexpect.spawn(args[0],args[1:],encoding='utf8',timeout=timeout,env=env)
 			else:
 			else:
 				self.p = PopenSpawn(args,encoding='utf8',timeout=timeout,env=env)
 				self.p = PopenSpawn(args,encoding='utf8',timeout=timeout,env=env)
 
 
-			if opt.exact_output:
+			if cfg.exact_output:
 				self.p.logfile = sys.stdout
 				self.p.logfile = sys.stdout
 
 
 	def do_decrypt_ka_data(self,hp,pw,desc='key-address data',check=True,have_yes_opt=False):
 	def do_decrypt_ka_data(self,hp,pw,desc='key-address data',check=True,have_yes_opt=False):
@@ -84,13 +82,13 @@ class MMGenPexpect:
 			self.p.sendeof()
 			self.p.sendeof()
 		self.p.read()
 		self.p.read()
 		ret = self.p.wait()
 		ret = self.p.wait()
-		if ret != self.req_exit_val and not opt.coverage:
+		if ret != self.req_exit_val and not cfg.coverage:
 			die(1,red(f'test.py: spawned program exited with value {ret}'))
 			die(1,red(f'test.py: spawned program exited with value {ret}'))
-		if opt.profile:
+		if cfg.profile:
 			return
 			return
 		if not self.skip_ok:
 		if not self.skip_ok:
 			m = 'OK\n' if ret == 0 else f'OK[{ret}]\n'
 			m = 'OK\n' if ret == 0 else f'OK[{ret}]\n'
-			sys.stderr.write( green(m) if opt.exact_output or opt.verbose else ' '+m )
+			sys.stderr.write( green(m) if cfg.exact_output or cfg.verbose else ' '+m )
 		return self
 		return self
 
 
 	def license(self):
 	def license(self):
@@ -101,7 +99,7 @@ class MMGenPexpect:
 		self.expect('Enter a wallet label, or hit ENTER for no label: ',label+'\n')
 		self.expect('Enter a wallet label, or hit ENTER for no label: ',label+'\n')
 
 
 	def usr_rand(self,num_chars):
 	def usr_rand(self,num_chars):
-		if opt.usr_random:
+		if cfg.usr_random:
 			self.interactive()
 			self.interactive()
 			self.send('\n')
 			self.send('\n')
 		else:
 		else:
@@ -109,7 +107,7 @@ class MMGenPexpect:
 			vmsg_r('SEND ')
 			vmsg_r('SEND ')
 			while rand_chars:
 			while rand_chars:
 				ch = rand_chars.pop(0)
 				ch = rand_chars.pop(0)
-				msg_r(yellow(ch)+' ' if opt.verbose else '+')
+				msg_r(yellow(ch)+' ' if cfg.verbose else '+')
 				ret = self.expect('left: ',ch,delay=0.005)
 				ret = self.expect('left: ',ch,delay=0.005)
 			self.expect('ENTER to continue: ','\n')
 			self.expect('ENTER to continue: ','\n')
 
 
@@ -134,7 +132,7 @@ class MMGenPexpect:
 			return self.expect_getend("Overwriting file '").rstrip("'")
 			return self.expect_getend("Overwriting file '").rstrip("'")
 		self.expect(NL,nonl=True)
 		self.expect(NL,nonl=True)
 		outfile = self.p.before.strip().strip("'")
 		outfile = self.p.before.strip().strip("'")
-		if opt.debug_pexpect:
+		if cfg.debug_pexpect:
 			rmsg(f'Outfile [{outfile}]')
 			rmsg(f'Outfile [{outfile}]')
 		vmsg('{} file: {}'.format( desc, cyan(outfile.replace('"',"")) ))
 		vmsg('{} file: {}'.format( desc, cyan(outfile.replace('"',"")) ))
 		return outfile
 		return outfile
@@ -154,14 +152,14 @@ class MMGenPexpect:
 
 
 	def expect_getend(self,s,regex=False):
 	def expect_getend(self,s,regex=False):
 		ret = self.expect(s,regex=regex,nonl=True)
 		ret = self.expect(s,regex=regex,nonl=True)
-		if opt.debug_pexpect:
+		if cfg.debug_pexpect:
 			debug_pexpect_msg(self.p)
 			debug_pexpect_msg(self.p)
 		# readline() of partial lines doesn't work with PopenSpawn, so do this instead:
 		# readline() of partial lines doesn't work with PopenSpawn, so do this instead:
 		self.expect(NL,nonl=True,silent=True)
 		self.expect(NL,nonl=True,silent=True)
-		if opt.debug_pexpect:
+		if cfg.debug_pexpect:
 			debug_pexpect_msg(self.p)
 			debug_pexpect_msg(self.p)
 		end = self.p.before.rstrip()
 		end = self.p.before.rstrip()
-		if not g.debug:
+		if not cfg.debug:
 			vmsg(f' ==> {cyan(end)}')
 			vmsg(f' ==> {cyan(end)}')
 		return end
 		return end
 
 
@@ -186,25 +184,25 @@ class MMGenPexpect:
 	def expect(self,s,t='',delay=None,regex=False,nonl=False,silent=False):
 	def expect(self,s,t='',delay=None,regex=False,nonl=False,silent=False):
 
 
 		if not silent:
 		if not silent:
-			if opt.verbose:
+			if cfg.verbose:
 				msg_r('EXPECT ' + yellow(str(s)))
 				msg_r('EXPECT ' + yellow(str(s)))
-			elif not opt.exact_output:
+			elif not cfg.exact_output:
 				msg_r('+')
 				msg_r('+')
 
 
 		try:
 		try:
 			ret = (self.p.expect_exact,self.p.expect)[bool(regex)](s) if s else 0
 			ret = (self.p.expect_exact,self.p.expect)[bool(regex)](s) if s else 0
 		except pexpect.TIMEOUT:
 		except pexpect.TIMEOUT:
-			if opt.debug_pexpect:
+			if cfg.debug_pexpect:
 				raise
 				raise
 			m1 = f'\nERROR.  Expect {s!r} timed out.  Exiting\n'
 			m1 = f'\nERROR.  Expect {s!r} timed out.  Exiting\n'
 			m2 = f'before: [{self.p.before}]\n'
 			m2 = f'before: [{self.p.before}]\n'
 			m3 = f'sent value: [{self.sent_value}]' if self.sent_value != None else ''
 			m3 = f'sent value: [{self.sent_value}]' if self.sent_value != None else ''
 			raise pexpect.TIMEOUT(m1+m2+m3)
 			raise pexpect.TIMEOUT(m1+m2+m3)
 
 
-		if opt.debug_pexpect:
+		if cfg.debug_pexpect:
 			debug_pexpect_msg(self.p)
 			debug_pexpect_msg(self.p)
 
 
-		if opt.verbose and type(s) != str:
+		if cfg.verbose and type(s) != str:
 			msg_r(f' ==> {ret} ')
 			msg_r(f' ==> {ret} ')
 
 
 		if ret == -1:
 		if ret == -1:
@@ -223,10 +221,10 @@ class MMGenPexpect:
 			time.sleep(delay)
 			time.sleep(delay)
 		ret = self.p.send(t) # returns num bytes written
 		ret = self.p.send(t) # returns num bytes written
 		self.sent_value = t if ret else None
 		self.sent_value = t if ret else None
-		if opt.demo and delay:
+		if cfg.demo and delay:
 			time.sleep(delay)
 			time.sleep(delay)
-		if opt.verbose:
-			ls = '' if opt.debug or not s else ' '
+		if cfg.verbose:
+			ls = '' if cfg.debug or not s else ' '
 			es = '' if s else '  '
 			es = '' if s else '  '
 			yt = yellow('{!r}'.format( t.replace('\n',r'\n') ))
 			yt = yellow('{!r}'.format( t.replace('\n',r'\n') ))
 			msg(f'{ls}SEND {es}{yt}')
 			msg(f'{ls}SEND {es}{yt}')

+ 13 - 13
test/misc/cfg.py

@@ -2,13 +2,15 @@
 
 
 from mmgen.common import *
 from mmgen.common import *
 
 
-cmd_args = opts.init()
+cfg = opts.init()
+
+cmd_args = cfg._args
 
 
 from mmgen.cfgfile import mmgen_cfg_file
 from mmgen.cfgfile import mmgen_cfg_file
 
 
-cf_usr = mmgen_cfg_file('usr')
-cf_sys = mmgen_cfg_file('sys')
-cf_sample = mmgen_cfg_file('sample')
+cf_usr = mmgen_cfg_file(cfg,'usr')
+cf_sys = mmgen_cfg_file(cfg,'sys')
+cf_sample = mmgen_cfg_file(cfg,'sample')
 
 
 msg(f'Usr cfg file:    {os.path.relpath(cf_usr.fn)}')
 msg(f'Usr cfg file:    {os.path.relpath(cf_usr.fn)}')
 msg(f'Sys cfg file:    {os.path.relpath(cf_sys.fn)}')
 msg(f'Sys cfg file:    {os.path.relpath(cf_sys.fn)}')
@@ -21,21 +23,19 @@ if cmd_args:
 		pu = cf_usr.get_lines()
 		pu = cf_usr.get_lines()
 		msg('usr cfg: {}'.format( ' '.join(f'{i.name}={i.value}' for i in pu) ))
 		msg('usr cfg: {}'.format( ' '.join(f'{i.name}={i.value}' for i in pu) ))
 	elif cmd_args[0] == 'coin_specific_vars':
 	elif cmd_args[0] == 'coin_specific_vars':
-		from mmgen.protocol import init_proto_from_opts
-		proto = init_proto_from_opts(need_amt=True)
 		for varname in cmd_args[1:]:
 		for varname in cmd_args[1:]:
 			msg('{}.{}: {}'.format(
 			msg('{}.{}: {}'.format(
-				type(proto).__name__,
+				type(cfg._proto).__name__,
 				varname,
 				varname,
-				getattr(proto,varname)
+				getattr(cfg._proto,varname)
 			))
 			))
 	elif cmd_args[0] == 'autoset_opts':
 	elif cmd_args[0] == 'autoset_opts':
-		assert opt.rpc_backend == 'aiohttp', "opt.rpc_backend != 'aiohttp'"
+		assert cfg.rpc_backend == 'aiohttp', "cfg.rpc_backend != 'aiohttp'"
 	elif cmd_args[0] == 'autoset_opts_cmdline':
 	elif cmd_args[0] == 'autoset_opts_cmdline':
-		assert opt.rpc_backend == 'curl', "opt.rpc_backend != 'curl'"
+		assert cfg.rpc_backend == 'curl', "cfg.rpc_backend != 'curl'"
 	elif cmd_args[0] == 'mnemonic_entry_modes':
 	elif cmd_args[0] == 'mnemonic_entry_modes':
 		from mmgen.mn_entry import mn_entry
 		from mmgen.mn_entry import mn_entry
 		msg('mnemonic_entry_modes: {}\nmmgen: {}\nbip39: {}'.format(
 		msg('mnemonic_entry_modes: {}\nmmgen: {}\nbip39: {}'.format(
-			g.mnemonic_entry_modes,
-			mn_entry('mmgen').usr_dfl_entry_mode,
-			mn_entry('bip39').usr_dfl_entry_mode ))
+			cfg.mnemonic_entry_modes,
+			mn_entry(cfg,'mmgen').usr_dfl_entry_mode,
+			mn_entry(cfg,'bip39').usr_dfl_entry_mode ))

+ 9 - 10
test/misc/get_passphrase.py

@@ -6,9 +6,8 @@ os.chdir(os.path.dirname(os.path.dirname(pn)))
 sys.path[0] = os.curdir
 sys.path[0] = os.curdir
 
 
 from mmgen.common import *
 from mmgen.common import *
-g.color = True
 
 
-cmd_args = opts.init({
+cfg = opts.init({
 	'text': {
 	'text': {
 		'desc':    '',
 		'desc':    '',
 		'usage':   '',
 		'usage':   '',
@@ -19,15 +18,13 @@ cmd_args = opts.init({
 -L, --label=l         d
 -L, --label=l         d
 -m, --keep-label      e
 -m, --keep-label      e
 		"""
 		"""
-	}})
-
-from mmgen.wallet import Wallet
+	}},init_opts={'color':True})
 
 
 def crypto():
 def crypto():
 	desc = 'test data'
 	desc = 'test data'
 
 
 	from mmgen.crypto import Crypto
 	from mmgen.crypto import Crypto
-	crypto = Crypto()
+	crypto = Crypto(cfg)
 
 
 	pw = crypto.get_new_passphrase(data_desc=desc,hash_preset=gc.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')
 	msg(f'==> got new passphrase: [{pw}]\n')
@@ -44,16 +41,18 @@ def crypto():
 def seed():
 def seed():
 	for n in range(1,3):
 	for n in range(1,3):
 		msg(f'------- NEW WALLET {n} -------\n')
 		msg(f'------- NEW WALLET {n} -------\n')
-		w1 = Wallet()
+		w1 = Wallet(cfg)
 		msg(f'\n==> got pw,preset,lbl: [{w1.ssdata.passwd}][{w1.ssdata.hash_preset}][{w1.ssdata.label}]\n')
 		msg(f'\n==> got pw,preset,lbl: [{w1.ssdata.passwd}][{w1.ssdata.hash_preset}][{w1.ssdata.label}]\n')
 
 
 	for n in range(1,3):
 	for n in range(1,3):
 		msg(f'------- PASSCHG {n} -------\n')
 		msg(f'------- PASSCHG {n} -------\n')
-		w2 = Wallet(ss=w1,passchg=True)
+		w2 = Wallet(cfg,ss=w1,passchg=True)
 		msg(f'\n==> got pw,preset,lbl: [{w2.ssdata.passwd}][{w2.ssdata.hash_preset}][{w2.ssdata.label}]\n')
 		msg(f'\n==> got pw,preset,lbl: [{w2.ssdata.passwd}][{w2.ssdata.hash_preset}][{w2.ssdata.label}]\n')
 
 
 	msg(f'------- WALLET FROM FILE -------\n')
 	msg(f'------- WALLET FROM FILE -------\n')
-	w3 = Wallet(fn='test/ref/FE3C6545-D782B529[128,1].mmdat') # passphrase: 'reference password'
+	w3 = Wallet(cfg,fn='test/ref/FE3C6545-D782B529[128,1].mmdat') # passphrase: 'reference password'
 	msg(f'\n==> got pw,preset,lbl: [{w3.ssdata.passwd}][{w3.ssdata.hash_preset}][{w3.ssdata.label}]\n')
 	msg(f'\n==> got pw,preset,lbl: [{w3.ssdata.passwd}][{w3.ssdata.hash_preset}][{w3.ssdata.label}]\n')
 
 
-globals()[cmd_args[0]]()
+from mmgen.wallet import Wallet
+
+globals()[cfg._args[0]]()

+ 14 - 7
test/misc/input_func.py

@@ -7,23 +7,30 @@ sys.path[0] = os.curdir
 
 
 from mmgen.common import *
 from mmgen.common import *
 
 
-cmd_args = opts.init({'text': { 'desc': '', 'usage':'', 'options':'-e, --echo-passphrase foo' }})
+cfg = opts.init({'text': { 'desc': '', 'usage':'', 'options':'-e, --echo-passphrase foo' }})
 
 
-if cmd_args[0] == 'passphrase':
+cmd_args = cfg._args
+
+cmd = cmd_args[0]
+
+if cmd == 'passphrase':
 	from mmgen.ui import get_words_from_user
 	from mmgen.ui import get_words_from_user
 	pw = get_words_from_user(
 	pw = get_words_from_user(
-		('Enter passphrase: ','Enter passphrase (echoed): ')[bool(opt.echo_passphrase)] )
+		cfg,
+		('Enter passphrase: ','Enter passphrase (echoed): ')[bool(cfg.echo_passphrase)] )
 	msg('Entered: {}'.format(' '.join(pw)))
 	msg('Entered: {}'.format(' '.join(pw)))
-elif cmd_args[0] in ('get_char','line_input'):
+elif cmd in ('get_char','line_input'):
 	from mmgen.term import get_char
 	from mmgen.term import get_char
 	from mmgen.ui import line_input
 	from mmgen.ui import line_input
 	from ast import literal_eval
 	from ast import literal_eval
 	func_args = literal_eval(cmd_args[1])
 	func_args = literal_eval(cmd_args[1])
 	Msg(f'\n  term: {get_char.__self__.__name__}')
 	Msg(f'\n  term: {get_char.__self__.__name__}')
-	Msg(f'  g.hold_protect_disable: {g.hold_protect_disable}')
+	Msg(f'  cfg.hold_protect_disable: {cfg.hold_protect_disable}')
+	if cmd == 'line_input':
+		func_args.update({'cfg':cfg})
 	Msg('  {name}( {args} )'.format(
 	Msg('  {name}( {args} )'.format(
-		name = cmd_args[0],
+		name = cmd,
 		args = ', '.join(f'{k}={v!r}' for k,v in func_args.items())
 		args = ', '.join(f'{k}={v!r}' for k,v in func_args.items())
 		))
 		))
-	ret = locals()[cmd_args[0]](**func_args)
+	ret = locals()[cmd](**func_args)
 	Msg('  ==> {!r}'.format(ret))
 	Msg('  ==> {!r}'.format(ret))

+ 1 - 1
test/misc/oneshot_warning.py

@@ -2,7 +2,7 @@
 
 
 from mmgen.common import *
 from mmgen.common import *
 
 
-cmd_args = opts.init()
+cfg = opts.init()
 
 
 class foo(oneshot_warning):
 class foo(oneshot_warning):
 
 

+ 12 - 8
test/misc/opts.py

@@ -37,38 +37,42 @@ sample note: {nn}
 """
 """
 	},
 	},
 	'code': {
 	'code': {
-		'options': lambda help_notes,s: s.format(
+		'options': lambda cfg,help_notes,s: s.format(
 			kgs=help_notes('keygen_backends'),
 			kgs=help_notes('keygen_backends'),
 			coin_id=help_notes('coin_id'),
 			coin_id=help_notes('coin_id'),
-			g=g,
 		),
 		),
 		'notes': lambda s: s.format(nn='a note'),
 		'notes': lambda s: s.format(nn='a note'),
 	}
 	}
 }
 }
 
 
-cmd_args = opts.init(opts_data)
+cfg = opts.init(opts_data)
 
 
-if cmd_args == ['show_common_opts_diff']:
+if cfg._args == ['show_common_opts_diff']:
 	from mmgen.opts import show_common_opts_diff
 	from mmgen.opts import show_common_opts_diff
-	show_common_opts_diff()
+	show_common_opts_diff(cfg)
 	sys.exit(0)
 	sys.exit(0)
 
 
 for k in (
 for k in (
 	'foo',               # added opt
 	'foo',               # added opt
 	'print_checksum',    # sets 'quiet'
 	'print_checksum',    # sets 'quiet'
 	'quiet','verbose',   # init_opts, incompatible_opts
 	'quiet','verbose',   # init_opts, incompatible_opts
-	'fee_estimate_mode', # autoset_opts
 	'passwd_file',       # infile_opts - check_infile()
 	'passwd_file',       # infile_opts - check_infile()
 	'outdir',            # check_outdir()
 	'outdir',            # check_outdir()
 	'cached_balances',   # opt_sets_global
 	'cached_balances',   # opt_sets_global
 	'minconf',           # global_sets_opt
 	'minconf',           # global_sets_opt
 	'hidden_incog_input_params',
 	'hidden_incog_input_params',
 	):
 	):
-	msg('{:30} {}'.format( f'opt.{k}:', getattr(opt,k) ))
+	msg('{:30} {}'.format( f'cfg.{k}:', getattr(cfg,k) ))
 
 
 msg('')
 msg('')
 for k in (
 for k in (
 	'cached_balances',   # opt_sets_global
 	'cached_balances',   # opt_sets_global
 	'minconf',           # global_sets_opt
 	'minconf',           # global_sets_opt
 	):
 	):
-	msg('{:30} {}'.format( f'g.{k}:', getattr(opt,k) ))
+	msg('{:30} {}'.format( f'cfg.{k}:', getattr(cfg,k) ))
+
+msg('')
+for k in (
+	'fee_estimate_mode', # autoset_opts
+	):
+	msg('{:30} {}'.format( f'cfg.{k}:', getattr(cfg,k) ))

+ 10 - 10
test/misc/term.py

@@ -44,7 +44,7 @@ available commands for platform {gc.platform!r}:
 	}
 	}
 }
 }
 
 
-cmd_args = opts.init(opts_data)
+cfg = opts.init(opts_data)
 
 
 from mmgen.term import get_char,get_char_raw,get_terminal_size,get_term
 from mmgen.term import get_char,get_char_raw,get_terminal_size,get_term
 from mmgen.ui import line_input,keypress_confirm,do_license_msg
 from mmgen.ui import line_input,keypress_confirm,do_license_msg
@@ -54,8 +54,8 @@ def cmsg(m):
 	msg('\n'+cyan(m))
 	msg('\n'+cyan(m))
 
 
 def confirm(m):
 def confirm(m):
-	if not keypress_confirm(m):
-		if keypress_confirm('Are you sure you want to exit test?'):
+	if not keypress_confirm( cfg, m ):
+		if keypress_confirm( cfg, 'Are you sure you want to exit test?' ):
 			die(1,'Exiting test at user request')
 			die(1,'Exiting test at user request')
 		else:
 		else:
 			msg('Continuing...')
 			msg('Continuing...')
@@ -80,7 +80,7 @@ def tt_color():
 def tt_license():
 def tt_license():
 	cmsg('Testing do_license_msg() with pager')
 	cmsg('Testing do_license_msg() with pager')
 	ymsg('Press "w" to test the pager, then "c" to continue')
 	ymsg('Press "w" to test the pager, then "c" to continue')
-	do_license_msg()
+	do_license_msg(cfg)
 
 
 def tt_line_input():
 def tt_line_input():
 	set_vt100()
 	set_vt100()
@@ -92,7 +92,7 @@ def tt_line_input():
 		on screen or entered text.
 		on screen or entered text.
 	"""))
 	"""))
 	get_char_raw('Ready? ',num_bytes=1)
 	get_char_raw('Ready? ',num_bytes=1)
-	reply = line_input('\nEnter text: ')
+	reply = line_input( cfg, '\nEnter text: ' )
 	confirm(f'Did you enter the text {reply!r}?')
 	confirm(f'Did you enter the text {reply!r}?')
 
 
 def _tt_get_char(raw=False,one_char=False,immed_chars=''):
 def _tt_get_char(raw=False,one_char=False,immed_chars=''):
@@ -145,7 +145,7 @@ def _tt_get_char(raw=False,one_char=False,immed_chars=''):
 def tt_urand():
 def tt_urand():
 	cmsg('Testing _get_random_data_from_user():')
 	cmsg('Testing _get_random_data_from_user():')
 	from mmgen.crypto import Crypto
 	from mmgen.crypto import Crypto
-	ret = Crypto()._get_random_data_from_user(uchars=10,desc='data').decode()
+	ret = Crypto(cfg)._get_random_data_from_user(uchars=10,desc='data').decode()
 	msg(f'USER ENTROPY (user input + keystroke timings):\n\n{fmt(ret,"  ")}')
 	msg(f'USER ENTROPY (user input + keystroke timings):\n\n{fmt(ret,"  ")}')
 	times = ret.splitlines()[1:]
 	times = ret.splitlines()[1:]
 	avg_prec = sum(len(t.split('.')[1]) for t in times) // len(times)
 	avg_prec = sum(len(t.split('.')[1]) for t in times) // len(times)
@@ -153,7 +153,7 @@ def tt_urand():
 		ymsg(f'WARNING: Avg. time precision of only {avg_prec} decimal points.  User entropy quality is degraded!')
 		ymsg(f'WARNING: Avg. time precision of only {avg_prec} decimal points.  User entropy quality is degraded!')
 	else:
 	else:
 		msg(f'Average time precision: {avg_prec} decimal points - OK')
 		msg(f'Average time precision: {avg_prec} decimal points - OK')
-	line_input('Press ENTER to continue: ')
+	line_input( cfg, 'Press ENTER to continue: ' )
 
 
 def tt_txview():
 def tt_txview():
 	cmsg('Testing tx.info.view_with_prompt() (try each viewing option)')
 	cmsg('Testing tx.info.view_with_prompt() (try each viewing option)')
@@ -163,7 +163,7 @@ def tt_txview():
 	while True:
 	while True:
 		tx.info.view_with_prompt('View data for transaction?',pause=False)
 		tx.info.view_with_prompt('View data for transaction?',pause=False)
 		set_vt100()
 		set_vt100()
-		if not keypress_confirm('Continue testing transaction view?',default_yes=True):
+		if not keypress_confirm( cfg, 'Continue testing transaction view?', default_yes=True ):
 			break
 			break
 
 
 def tt_get_char_one():
 def tt_get_char_one():
@@ -186,8 +186,8 @@ def tt_get_char_one_char_immed_chars():
 
 
 get_term().register_cleanup()
 get_term().register_cleanup()
 
 
-if cmd_args:
-	locals()['tt_'+cmd_args[0]]()
+if cfg._args:
+	locals()['tt_'+cfg._args[0]]()
 else:
 else:
 	for command in commands:
 	for command in commands:
 		locals()['tt_'+command]()
 		locals()['tt_'+command]()

+ 6 - 6
test/misc/term_ni.py

@@ -6,27 +6,27 @@ sys.path[0] = os.curdir
 
 
 from mmgen.common import *
 from mmgen.common import *
 
 
-cmd_args = opts.init()
+cfg = opts.init()
 
 
 from mmgen.term import get_term,get_char_raw
 from mmgen.term import get_term,get_char_raw
 term = get_term()
 term = get_term()
 
 
-if cmd_args[0] == 'echo':
+if cfg._args[0] == 'echo':
 
 
 	from mmgen.ui import line_input
 	from mmgen.ui import line_input
 
 
 	term.init(noecho=True)
 	term.init(noecho=True)
-	line_input('noecho> ')
+	line_input( cfg, 'noecho> ' )
 	get_char_raw()
 	get_char_raw()
 
 
 	term.set('echo')
 	term.set('echo')
-	line_input('echo> ')
+	line_input( cfg, 'echo> ' )
 
 
 	term.set('noecho')
 	term.set('noecho')
-	line_input('noecho> ')
+	line_input( cfg, 'noecho> ' )
 	get_char_raw()
 	get_char_raw()
 
 
-elif cmd_args[0] == 'cleanup':
+elif cfg._args[0] == 'cleanup':
 
 
 	term.register_cleanup()
 	term.register_cleanup()
 
 

+ 5 - 2
test/misc/tool_api_test.py

@@ -8,7 +8,6 @@ test.misc.tool_api_test: test the MMGen suite tool API
 """
 """
 
 
 import sys,os
 import sys,os
-from mmgen.common import *
 from mmgen.key import PrivKey
 from mmgen.key import PrivKey
 from mmgen.addr import CoinAddr
 from mmgen.addr import CoinAddr
 
 
@@ -64,7 +63,7 @@ def test_triplet(tool,coin,network,addrtype,key_idx,wif_chk,addr_chk):
 def run_test():
 def run_test():
 
 
 	from mmgen.tool.api import tool_api
 	from mmgen.tool.api import tool_api
-	tool = tool_api()
+	tool = tool_api(cfg)
 
 
 	tool.coins
 	tool.coins
 	tool.print_addrtypes()
 	tool.print_addrtypes()
@@ -110,4 +109,8 @@ def run_test():
 			'SKxuS56e99jpCeD9mMQ5o63zoGPakNdM9HCvt4Vt2cypvRjCdvGJ',
 			'SKxuS56e99jpCeD9mMQ5o63zoGPakNdM9HCvt4Vt2cypvRjCdvGJ',
 			'zchFELwBxqsAubsLQ8yZgPCDDGukjXJssgCbiTPwFNmFwn9haLnDatzfhLdZzJT4PcU4o2yr92B52UFirUzEdF6ZYM2gBkM' )
 			'zchFELwBxqsAubsLQ8yZgPCDDGukjXJssgCbiTPwFNmFwn9haLnDatzfhLdZzJT4PcU4o2yr92B52UFirUzEdF6ZYM2gBkM' )
 
 
+from mmgen.opts import init
+
+cfg = init()
+
 run_test()
 run_test()

+ 3 - 3
test/misc/utf8_output.py

@@ -2,7 +2,7 @@
 
 
 from mmgen.common import *
 from mmgen.common import *
 
 
-cmd_args = opts.init()
+cfg = opts.init()
 
 
 from mmgen.util import msg
 from mmgen.util import msg
 
 
@@ -13,7 +13,7 @@ text = {
 	'jp': 'Japanese text: {}'.format('必要なのは、信用ではなく暗号化された証明に基づく電子取引システムであり、')
 	'jp': 'Japanese text: {}'.format('必要なのは、信用ではなく暗号化された証明に基づく電子取引システムであり、')
 }
 }
 
 
-if not cmd_args or not cmd_args[0] in text:
+if not cfg._args or not cfg._args[0] in text:
 	die(2,'argument must be one of {}'.format(list(text.keys())))
 	die(2,'argument must be one of {}'.format(list(text.keys())))
 
 
-msg(text[cmd_args[0]])
+msg(text[cfg._args[0]])

+ 15 - 11
test/objattrtest.py

@@ -30,7 +30,6 @@ sys.path.__setitem__(0,os.path.abspath(os.curdir))
 os.environ['MMGEN_TEST_SUITE'] = '1'
 os.environ['MMGEN_TEST_SUITE'] = '1'
 
 
 # Import these _after_ local path's been added to sys.path
 # Import these _after_ local path's been added to sys.path
-from test.objattrtest_py_d.oat_common import *
 from mmgen.common import *
 from mmgen.common import *
 from mmgen.addrlist import *
 from mmgen.addrlist import *
 from mmgen.passwdlist import *
 from mmgen.passwdlist import *
@@ -55,7 +54,12 @@ opts_data = {
 	}
 	}
 }
 }
 
 
-cmd_args = opts.init(opts_data)
+cfg = opts.init(opts_data)
+
+from test.include.common import set_globals
+set_globals(cfg)
+
+from test.objattrtest_py_d.oat_common import *
 
 
 pd = namedtuple('permission_bits', ['read_ok','delete_ok','reassign_ok'])
 pd = namedtuple('permission_bits', ['read_ok','delete_ok','reassign_ok'])
 
 
@@ -120,10 +124,10 @@ def test_attr(data,obj,attrname,dobj,bits,attrval_type):
 				if d[k] != bits[k]:
 				if d[k] != bits[k]:
 					fs = 'init value {iv}={a} for attr {n!r} does not match test data ({iv}={b})'
 					fs = 'init value {iv}={a} for attr {n!r} does not match test data ({iv}={b})'
 					die(4,fs.format(iv=k,n=attrname,a=d[k],b=bits[k]))
 					die(4,fs.format(iv=k,n=attrname,a=d[k],b=bits[k]))
-				if opt.verbose and d[k] == True:
+				if cfg.verbose and d[k] == True:
 					msg_r(f' {k}={d[k]!r}')
 					msg_r(f' {k}={d[k]!r}')
 
 
-		if opt.show_nonstandard_init:
+		if cfg.show_nonstandard_init:
 			for k,v in (('typeconv',False),('set_none_ok',True)):
 			for k,v in (('typeconv',False),('set_none_ok',True)):
 				if d[k] == v:
 				if d[k] == v:
 					msg_r(f' {k}={v}')
 					msg_r(f' {k}={v}')
@@ -136,19 +140,19 @@ def test_object(test_data,objname):
 	else:
 	else:
 		cls = globals()[objname]
 		cls = globals()[objname]
 
 
-	fs = 'Testing attribute ' + ('{!r:<15}{dt:13}' if opt.show_descriptor_type else '{!r}')
+	fs = 'Testing attribute ' + ('{!r:<15}{dt:13}' if cfg.show_descriptor_type else '{!r}')
 	data = test_data[objname]
 	data = test_data[objname]
 	obj = cls(*data.args,**data.kwargs)
 	obj = cls(*data.args,**data.kwargs)
 
 
 	for attrname,adata in data.attrs.items():
 	for attrname,adata in data.attrs.items():
 		dobj = get_descriptor_obj(type(obj),attrname)
 		dobj = get_descriptor_obj(type(obj),attrname)
-		if opt.verbose:
+		if cfg.verbose:
 			msg_r(fs.format(attrname,dt=type(dobj).__name__.replace('MMGen','')))
 			msg_r(fs.format(attrname,dt=type(dobj).__name__.replace('MMGen','')))
 		bits = parse_permbits(adata[0])
 		bits = parse_permbits(adata[0])
 		test_attr(data,obj,attrname,dobj,bits,adata[1])
 		test_attr(data,obj,attrname,dobj,bits,adata[1])
 		for perm_name,perm_value in bits._asdict().items():
 		for perm_name,perm_value in bits._asdict().items():
 			test_attr_perm(obj,attrname,perm_name,perm_value,dobj,adata[1])
 			test_attr_perm(obj,attrname,perm_name,perm_value,dobj,adata[1])
-		vmsg('')
+		cfg._util.vmsg('')
 
 
 def do_loop():
 def do_loop():
 	import importlib
 	import importlib
@@ -156,12 +160,12 @@ def do_loop():
 	test_data = importlib.import_module(modname).tests
 	test_data = importlib.import_module(modname).tests
 	gmsg(f'Running immutable attribute tests for {proto.coin} {proto.network}')
 	gmsg(f'Running immutable attribute tests for {proto.coin} {proto.network}')
 
 
-	utests = cmd_args
+	utests = cfg._args
 	for obj in test_data:
 	for obj in test_data:
 		if utests and obj not in utests: continue
 		if utests and obj not in utests: continue
-		msg((blue if opt.verbose else nocolor)(f'Testing {obj}'))
+		msg((blue if cfg.verbose else nocolor)(f'Testing {obj}'))
 		test_object(test_data,obj)
 		test_object(test_data,obj)
 
 
-from mmgen.protocol import init_proto_from_opts
-proto = init_proto_from_opts(need_amt=True)
+proto = cfg._proto
+
 do_loop()
 do_loop()

+ 4 - 3
test/objattrtest_py_d/oat_btc_mainnet.py

@@ -8,10 +8,11 @@ test.objattrtest_py_d.oat_btc_mainnet: BTC mainnet test vectors for MMGen data o
 """
 """
 
 
 from .oat_common import *
 from .oat_common import *
+from ..include.common import cfg
 from mmgen.protocol import init_proto
 from mmgen.protocol import init_proto
 from mmgen.amt import BTCAmt
 from mmgen.amt import BTCAmt
 
 
-proto = init_proto('btc',need_amt=True)
+proto = init_proto( cfg, 'btc', need_amt=True )
 
 
 sample_objs.update({
 sample_objs.update({
 	'PrivKey':   PrivKey(proto,seed_bin,compressed=True,pubkey_type='std'),
 	'PrivKey':   PrivKey(proto,seed_bin,compressed=True,pubkey_type='std'),
@@ -70,7 +71,7 @@ tests = {
 		'data': (0b001, bytes),
 		'data': (0b001, bytes),
 		'sid':  (0b001, SeedID),
 		'sid':  (0b001, SeedID),
 		},
 		},
-		[seed_bin],
+		[cfg,seed_bin],
 		{},
 		{},
 	),
 	),
 	'SubSeed': atd({
 	'SubSeed': atd({
@@ -105,7 +106,7 @@ tests = {
 		'id_str': (0b001, SeedSplitIDString),
 		'id_str': (0b001, SeedSplitIDString),
 		'count':  (0b001, SeedShareCount),
 		'count':  (0b001, SeedShareCount),
 		},
 		},
-		[sample_objs['MasterShareIdx'], sample_objs['Seed'], 'foo', 2],
+		[cfg,sample_objs['MasterShareIdx'], sample_objs['Seed'], 'foo', 2],
 		{},
 		{},
 	),
 	),
 	# twuo.py
 	# twuo.py

+ 4 - 4
test/objattrtest_py_d/oat_common.py

@@ -17,7 +17,7 @@ from mmgen.addr import *
 from mmgen.tx import *
 from mmgen.tx import *
 from mmgen.tw.unspent import *
 from mmgen.tw.unspent import *
 from mmgen.key import *
 from mmgen.key import *
-from ..include.common import getrand
+from ..include.common import cfg,getrand
 
 
 from collections import namedtuple
 from collections import namedtuple
 atd = namedtuple('attrtest_entry',['attrs','args','kwargs'])
 atd = namedtuple('attrtest_entry',['attrs','args','kwargs'])
@@ -43,13 +43,13 @@ sample_objs = {
 	'CoinTxID':  CoinTxID('aa'*32),
 	'CoinTxID':  CoinTxID('aa'*32),
 
 
 	'SeedID':    SeedID(sid='F00F00BB'),
 	'SeedID':    SeedID(sid='F00F00BB'),
-	'Seed':      Seed(seed_bin=seed_bin),
+	'Seed':      Seed(cfg,seed_bin=seed_bin),
 
 
-	'SubSeedList': SubSeedList(Seed(seed_bin=seed_bin)),
+	'SubSeedList': SubSeedList(Seed(cfg,seed_bin=seed_bin)),
 	'SubSeedIdx':  SubSeedIdx('1S'),
 	'SubSeedIdx':  SubSeedIdx('1S'),
 
 
 	'SeedSplitIDString': SeedSplitIDString('alice'),
 	'SeedSplitIDString': SeedSplitIDString('alice'),
-	'SeedShareList':     SeedShareList(Seed(seed_bin=seed_bin),SeedShareCount(2)),
+	'SeedShareList':     SeedShareList(Seed(cfg,seed_bin=seed_bin),SeedShareCount(2)),
 	'SeedShareIdx':      SeedShareIdx(1),
 	'SeedShareIdx':      SeedShareIdx(1),
 	'SeedShareCount':    SeedShareCount(2),
 	'SeedShareCount':    SeedShareCount(2),
 	'MasterShareIdx':    MasterShareIdx(7),
 	'MasterShareIdx':    MasterShareIdx(7),

+ 25 - 22
test/objtest.py

@@ -60,7 +60,10 @@ opts_data = {
 	}
 	}
 }
 }
 
 
-cmd_args = opts.init(opts_data)
+cfg = opts.init(opts_data)
+
+from test.include.common import set_globals
+set_globals(cfg)
 
 
 def run_test(test,arg,input_data,arg1,exc_name):
 def run_test(test,arg,input_data,arg1,exc_name):
 	arg_copy = arg
 	arg_copy = arg
@@ -96,22 +99,22 @@ def run_test(test,arg,input_data,arg1,exc_name):
 	else:
 	else:
 		args = [arg]
 		args = [arg]
 
 
-	if opt.getobj:
+	if cfg.getobj:
 		if args:
 		if args:
 			assert len(args) == 1, 'objtest_chk1: only one positional arg is allowed'
 			assert len(args) == 1, 'objtest_chk1: only one positional arg is allowed'
 			kwargs.update( { arg1: args[0] } )
 			kwargs.update( { arg1: args[0] } )
-		if opt.silent:
+		if cfg.silent:
 			kwargs.update( { 'silent': True } )
 			kwargs.update( { 'silent': True } )
 
 
 	try:
 	try:
-		if not opt.super_silent:
+		if not cfg.super_silent:
 			arg_disp = repr(arg_copy[0] if type(arg_copy) == tuple else arg_copy)
 			arg_disp = repr(arg_copy[0] if type(arg_copy) == tuple else arg_copy)
-			if g.test_suite_deterministic and isinstance(arg_copy,dict):
+			if cfg.test_suite_deterministic and isinstance(arg_copy,dict):
 				arg_disp = re.sub(r'object at 0x[0-9a-f]+','object at [SCRUBBED]',arg_disp)
 				arg_disp = re.sub(r'object at 0x[0-9a-f]+','object at [SCRUBBED]',arg_disp)
 			msg_r((green if input_data=='good' else orange)(f'{arg_disp+":":<22}'))
 			msg_r((green if input_data=='good' else orange)(f'{arg_disp+":":<22}'))
 		cls = globals()[test]
 		cls = globals()[test]
 
 
-		if opt.getobj:
+		if cfg.getobj:
 			ret = get_obj(globals()[test],**kwargs)
 			ret = get_obj(globals()[test],**kwargs)
 		else:
 		else:
 			ret = cls(*args,**kwargs)
 			ret = cls(*args,**kwargs)
@@ -121,14 +124,14 @@ def run_test(test,arg,input_data,arg1,exc_name):
 		if isinstance(ret_chk,str): ret_chk = ret_chk.encode()
 		if isinstance(ret_chk,str): ret_chk = ret_chk.encode()
 		if isinstance(ret,str): ret = ret.encode()
 		if isinstance(ret,str): ret = ret.encode()
 
 
-		if opt.getobj:
+		if cfg.getobj:
 			if input_data == 'bad':
 			if input_data == 'bad':
 				assert ret == False, 'non-False return on bad input data'
 				assert ret == False, 'non-False return on bad input data'
 		else:
 		else:
-			if (opt.silent and input_data=='bad' and ret!=bad_ret) or (not opt.silent and input_data=='bad'):
+			if (cfg.silent and input_data=='bad' and ret!=bad_ret) or (not cfg.silent and input_data=='bad'):
 				raise UserWarning(f"Non-'None' return value {ret!r} with bad input data")
 				raise UserWarning(f"Non-'None' return value {ret!r} with bad input data")
 
 
-		if opt.silent and input_data=='good' and ret==bad_ret:
+		if cfg.silent and input_data=='good' and ret==bad_ret:
 			raise UserWarning("'None' returned with good input data")
 			raise UserWarning("'None' returned with good input data")
 
 
 		if input_data=='good':
 		if input_data=='good':
@@ -137,17 +140,17 @@ def run_test(test,arg,input_data,arg1,exc_name):
 			if ret != ret_chk and repr(ret) != repr(ret_chk):
 			if ret != ret_chk and repr(ret) != repr(ret_chk):
 				raise UserWarning(f"Return value ({ret!r}) doesn't match expected value ({ret_chk!r})")
 				raise UserWarning(f"Return value ({ret!r}) doesn't match expected value ({ret_chk!r})")
 
 
-		if opt.super_silent:
+		if cfg.super_silent:
 			return
 			return
 
 
-		if opt.getobj and (not opt.silent and input_data == 'bad'):
+		if cfg.getobj and (not cfg.silent and input_data == 'bad'):
 			pass
 			pass
 		else:
 		else:
 			try: ret_disp = ret.decode()
 			try: ret_disp = ret.decode()
 			except: ret_disp = ret
 			except: ret_disp = ret
 			msg(f'==> {ret_disp!r}')
 			msg(f'==> {ret_disp!r}')
 
 
-		if opt.verbose and issubclass(cls,MMGenObject):
+		if cfg.verbose and issubclass(cls,MMGenObject):
 			ret.pmsg() if hasattr(ret,'pmsg') else pmsg(ret)
 			ret.pmsg() if hasattr(ret,'pmsg') else pmsg(ret)
 
 
 	except Exception as e:
 	except Exception as e:
@@ -156,16 +159,16 @@ def run_test(test,arg,input_data,arg1,exc_name):
 		if not type(e).__name__ == exc_name:
 		if not type(e).__name__ == exc_name:
 			msg(f'Incorrect exception: expected {exc_name} but got {type(e).__name__}')
 			msg(f'Incorrect exception: expected {exc_name} but got {type(e).__name__}')
 			raise
 			raise
-		if opt.super_silent:
+		if cfg.super_silent:
 			pass
 			pass
-		elif opt.silent:
+		elif cfg.silent:
 			msg(f'==> {exc_name}')
 			msg(f'==> {exc_name}')
 		else:
 		else:
 			msg( yellow(f' {exc_name}:') + str(e) )
 			msg( yellow(f' {exc_name}:') + str(e) )
 	except SystemExit as e:
 	except SystemExit as e:
 		if input_data == 'good':
 		if input_data == 'good':
 			raise ValueError('Error on good input data')
 			raise ValueError('Error on good input data')
-		if opt.verbose:
+		if cfg.verbose:
 			msg(f'exitval: {e.code}')
 			msg(f'exitval: {e.code}')
 	except UserWarning as e:
 	except UserWarning as e:
 		msg(f'==> {ret!r}')
 		msg(f'==> {ret!r}')
@@ -178,21 +181,21 @@ def do_loop():
 	gmsg(f'Running data object tests for {proto.coin} {proto.network}')
 	gmsg(f'Running data object tests for {proto.coin} {proto.network}')
 
 
 	clr = None
 	clr = None
-	utests = cmd_args
+	utests = cfg._args
 	for test in test_data:
 	for test in test_data:
 		arg1 = test_data[test].get('arg1')
 		arg1 = test_data[test].get('arg1')
 		if utests and test not in utests: continue
 		if utests and test not in utests: continue
-		nl = ('\n','')[bool(opt.super_silent) or clr == None]
-		clr = (blue,nocolor)[bool(opt.super_silent)]
+		nl = ('\n','')[bool(cfg.super_silent) or clr == None]
+		clr = (blue,nocolor)[bool(cfg.super_silent)]
 
 
-		if opt.getobj and arg1 is None:
+		if cfg.getobj and arg1 is None:
 			msg(gray(f'{nl}Skipping {test}'))
 			msg(gray(f'{nl}Skipping {test}'))
 			continue
 			continue
 		else:
 		else:
 			msg(clr(f'{nl}Testing {test}'))
 			msg(clr(f'{nl}Testing {test}'))
 
 
 		for k in ('bad','good'):
 		for k in ('bad','good'):
-			if not opt.super_silent:
+			if not cfg.super_silent:
 				msg(purple(capfirst(k)+' input:'))
 				msg(purple(capfirst(k)+' input:'))
 			for arg in test_data[test][k]:
 			for arg in test_data[test][k]:
 				run_test(
 				run_test(
@@ -203,6 +206,6 @@ def do_loop():
 					exc_name   = test_data[test].get('exc_name') or ('ObjectInitError','None')[k=='good'],
 					exc_name   = test_data[test].get('exc_name') or ('ObjectInitError','None')[k=='good'],
 				)
 				)
 
 
-from mmgen.protocol import init_proto_from_opts
-proto = init_proto_from_opts(need_amt=True)
+proto = cfg._proto
+
 do_loop()
 do_loop()

+ 4 - 3
test/objtest_py_d/ot_btc_mainnet.py

@@ -14,9 +14,10 @@ from mmgen.addrlist import AddrIdxList
 from mmgen.seedsplit import *
 from mmgen.seedsplit import *
 from mmgen.key import *
 from mmgen.key import *
 from .ot_common import *
 from .ot_common import *
+from ..include.common import cfg
 
 
 from mmgen.protocol import init_proto
 from mmgen.protocol import init_proto
-proto = init_proto('btc',need_amt=True)
+proto = init_proto( cfg, 'btc', need_amt=True )
 tw_pfx = proto.base_coin.lower() + ':'
 tw_pfx = proto.base_coin.lower() + ':'
 zero_addr = '1111111111111111111114oLvT2'
 zero_addr = '1111111111111111111114oLvT2'
 
 
@@ -123,8 +124,8 @@ tests = {
 			),
 			),
 		'good': (
 		'good': (
 			{'sid':'F00BAA12'},
 			{'sid':'F00BAA12'},
-			{'seed': Seed(r16),    'ret': SeedID(seed=Seed(r16))},
-			{'sid': Seed(r16).sid, 'ret': SeedID(seed=Seed(r16))}
+			{'seed': Seed(cfg,r16),     'ret': SeedID(seed=Seed(cfg,r16))},
+			{'sid':  Seed(cfg,r16).sid, 'ret': SeedID(seed=Seed(cfg,r16))}
 			)
 			)
 	},
 	},
 	'SubSeedIdx': {
 	'SubSeedIdx': {

+ 2 - 1
test/objtest_py_d/ot_btc_testnet.py

@@ -9,9 +9,10 @@ test.objtest_py_d.ot_btc_testnet: BTC testnet test vectors for MMGen data object
 
 
 from mmgen.obj import *
 from mmgen.obj import *
 from .ot_common import *
 from .ot_common import *
+from ..include.common import cfg
 
 
 from mmgen.protocol import init_proto
 from mmgen.protocol import init_proto
-proto = init_proto('btc',network='testnet',need_amt=True)
+proto = init_proto( cfg, 'btc', network='testnet', need_amt=True )
 
 
 tests = {
 tests = {
 	'CoinAddr': {
 	'CoinAddr': {

+ 0 - 1
test/objtest_py_d/ot_common.py

@@ -8,7 +8,6 @@ test.objtest_py_d.ot_common: shared data for MMGen data objects tests
 """
 """
 
 
 import os
 import os
-from mmgen.globalvars import g
 from ..include.common import *
 from ..include.common import *
 
 
 r32,r24,r16,r17,r18 = getrand(32),getrand(24),getrand(16),getrand(17),getrand(18)
 r32,r24,r16,r17,r18 = getrand(32),getrand(24),getrand(16),getrand(17),getrand(18)

+ 2 - 1
test/objtest_py_d/ot_ltc_mainnet.py

@@ -11,9 +11,10 @@ from decimal import Decimal
 
 
 from mmgen.obj import *
 from mmgen.obj import *
 from .ot_common import *
 from .ot_common import *
+from ..include.common import cfg
 
 
 from mmgen.protocol import init_proto
 from mmgen.protocol import init_proto
-proto = init_proto('ltc',need_amt=True)
+proto = init_proto( cfg, 'ltc', need_amt=True )
 
 
 tests = {
 tests = {
 	'LTCAmt': {
 	'LTCAmt': {

+ 2 - 1
test/objtest_py_d/ot_ltc_testnet.py

@@ -9,9 +9,10 @@ test.objtest_py_d.ot_ltc_testnet: LTC testnet test vectors for MMGen data object
 
 
 from mmgen.obj import *
 from mmgen.obj import *
 from .ot_common import *
 from .ot_common import *
+from ..include.common import cfg
 
 
 from mmgen.protocol import init_proto
 from mmgen.protocol import init_proto
-proto = init_proto('ltc',network='testnet',need_amt=True)
+proto = init_proto( cfg, 'ltc', network='testnet', need_amt=True )
 
 
 tests = {
 tests = {
 	'CoinAddr': {
 	'CoinAddr': {

+ 1 - 0
test/overlay/fakemods/mmgen/proto/btc/tw/unspent.py

@@ -10,6 +10,7 @@ if overlay_fake_os.getenv('MMGEN_BOGUS_UNSPENT_DATA'):
 			import json
 			import json
 			from ....fileutil import get_data_from_file
 			from ....fileutil import get_data_from_file
 			return json.loads(get_data_from_file(
 			return json.loads(get_data_from_file(
+				self.cfg,
 				overlay_fake_os.getenv('MMGEN_BOGUS_UNSPENT_DATA')
 				overlay_fake_os.getenv('MMGEN_BOGUS_UNSPENT_DATA')
 			), parse_float=Decimal)
 			), parse_float=Decimal)
 
 

+ 16 - 11
test/scrambletest.py

@@ -21,12 +21,13 @@ test/scrambletest.py: seed scrambling and addrlist data generation tests for all
                       supported coins + passwords
                       supported coins + passwords
 """
 """
 
 
-import sys,os
+import sys,os,time
 from subprocess import run,PIPE
 from subprocess import run,PIPE
 
 
 from include.tests_header import repo_root
 from include.tests_header import repo_root
-from mmgen.common import *
-from test.include.common import *
+import mmgen.opts as opts
+from mmgen.globalvars import gc
+from mmgen.util import msg,msg_r,bmsg
 
 
 opts_data = {
 opts_data = {
 	'text': {
 	'text': {
@@ -49,10 +50,14 @@ If no command is given, the whole suite of tests is run.
 	}
 	}
 }
 }
 
 
-cmd_args = opts.init(opts_data)
+cfg = opts.init(opts_data)
+
+from test.include.common import set_globals,end_msg,green
+
+set_globals(cfg)
 
 
 os.environ['MMGEN_DEBUG_ADDRLIST'] = '1'
 os.environ['MMGEN_DEBUG_ADDRLIST'] = '1'
-if not opt.system:
+if not cfg.system:
 	os.environ['PYTHONPATH'] = repo_root
 	os.environ['PYTHONPATH'] = repo_root
 
 
 from collections import namedtuple
 from collections import namedtuple
@@ -95,7 +100,7 @@ passwd_data = {
 'xmrseed_dfl_αω':td('62f5b72a5ca89cab', 'xmrseed:25:αω','-αω-xmrseed-25','αω xmrseed:25','tequila eden skulls giving jester hospital dreams bakery adjust nanny cactus inwardly films amply nanny soggy vials muppet yellow woken ashtray organs exhale foes eden'),
 'xmrseed_dfl_αω':td('62f5b72a5ca89cab', 'xmrseed:25:αω','-αω-xmrseed-25','αω xmrseed:25','tequila eden skulls giving jester hospital dreams bakery adjust nanny cactus inwardly films amply nanny soggy vials muppet yellow woken ashtray organs exhale foes eden'),
 }
 }
 
 
-cvr_opts = ' -m trace --count --coverdir={} --file={}'.format( *init_coverage() ) if opt.coverage else ''
+cvr_opts = ' -m trace --count --coverdir={} --file={}'.format( *init_coverage() ) if cfg.coverage else ''
 cmd_base = f'python3{cvr_opts} cmds/mmgen-{{}}gen -qS'
 cmd_base = f'python3{cvr_opts} cmds/mmgen-{{}}gen -qS'
 
 
 def get_cmd_output(cmd):
 def get_cmd_output(cmd):
@@ -105,7 +110,7 @@ def get_cmd_output(cmd):
 	return cp.stdout.decode().splitlines()
 	return cp.stdout.decode().splitlines()
 
 
 def do_test(cmd,tdata,msg_str,addr_desc):
 def do_test(cmd,tdata,msg_str,addr_desc):
-	vmsg(green(f'Executing: {cmd}'))
+	cfg._util.vmsg(green(f'Executing: {cmd}'))
 	msg_r('Testing: ' + msg_str)
 	msg_r('Testing: ' + msg_str)
 
 
 	lines = get_cmd_output(cmd)
 	lines = get_cmd_output(cmd)
@@ -113,11 +118,11 @@ def do_test(cmd,tdata,msg_str,addr_desc):
 	cmd_out['addr'] = lines[-2].split(None,1)[-1]
 	cmd_out['addr'] = lines[-2].split(None,1)[-1]
 
 
 	ref_data = tdata._asdict()
 	ref_data = tdata._asdict()
-	vmsg('')
+	cfg._util.vmsg('')
 	for k in ref_data:
 	for k in ref_data:
 		if cmd_out[k] == ref_data[k]:
 		if cmd_out[k] == ref_data[k]:
 			s = k.replace('seed','seed[:8]').replace('addr',addr_desc)
 			s = k.replace('seed','seed[:8]').replace('addr',addr_desc)
-			vmsg(f'  {s:9}: {cmd_out[k]}')
+			cfg._util.vmsg(f'  {s:9}: {cmd_out[k]}')
 		else:
 		else:
 			die(4,f'\nError: sc_{k} value {cmd_out[k]} does not match reference value {ref_data[k]}')
 			die(4,f'\nError: sc_{k} value {cmd_out[k]} does not match reference value {ref_data[k]}')
 	msg('OK')
 	msg('OK')
@@ -126,7 +131,7 @@ def do_coin_tests():
 	bmsg('Testing address scramble strings and list IDs')
 	bmsg('Testing address scramble strings and list IDs')
 	for tname,tdata in (
 	for tname,tdata in (
 			tuple(bitcoin_data.items()) +
 			tuple(bitcoin_data.items()) +
-			tuple(altcoin_data.items() if not opt.no_altcoin else []) ):
+			tuple(altcoin_data.items() if not cfg.no_altcoin else []) ):
 		if tname == 'zec_zcash_z' and gc.platform == 'win':
 		if tname == 'zec_zcash_z' and gc.platform == 'win':
 			msg("Skipping 'zec_zcash_z' test for Windows platform")
 			msg("Skipping 'zec_zcash_z' test for Windows platform")
 			continue
 			continue
@@ -148,7 +153,7 @@ def do_passwd_tests():
 
 
 start_time = int(time.time())
 start_time = int(time.time())
 
 
-cmds = cmd_args or ('coin','pw')
+cmds = cfg._args or ('coin','pw')
 for cmd in cmds:
 for cmd in cmds:
 	{'coin': do_coin_tests, 'pw': do_passwd_tests }[cmd]()
 	{'coin': do_coin_tests, 'pw': do_passwd_tests }[cmd]()
 
 

+ 94 - 88
test/test.py

@@ -22,7 +22,7 @@ test/test.py: Test suite for the MMGen wallet system
 
 
 def check_segwit_opts():
 def check_segwit_opts():
 	for k,m in (('segwit','S'),('segwit_random','S'),('bech32','B')):
 	for k,m in (('segwit','S'),('segwit_random','S'),('bech32','B')):
-		if getattr(opt,k) and m not in proto.mmtypes:
+		if getattr(cfg,k) and m not in proto.mmtypes:
 			die(1,f'--{k.replace("_","-")} option incompatible with {proto.cls_name}')
 			die(1,f'--{k.replace("_","-")} option incompatible with {proto.cls_name}')
 
 
 def create_shm_dir(data_dir,trash_dir):
 def create_shm_dir(data_dir,trash_dir):
@@ -92,7 +92,6 @@ try:
 except:
 except:
 	pass
 	pass
 
 
-g.quiet = False # if 'quiet' was set in config file, disable here
 os.environ['MMGEN_QUIET'] = '0' # for this script and spawned scripts
 os.environ['MMGEN_QUIET'] = '0' # for this script and spawned scripts
 
 
 opts_data = {
 opts_data = {
@@ -170,20 +169,23 @@ environment var
 # we need some opt values before running opts.init, so parse without initializing:
 # we need some opt values before running opts.init, so parse without initializing:
 po = opts.init(opts_data,parse_only=True)
 po = opts.init(opts_data,parse_only=True)
 
 
-from test.include.common import *
-from test.test_py_d.common import *
+from test.include.common import set_globals,get_test_data_dir
 
 
 data_dir = get_test_data_dir() # include/common.py
 data_dir = get_test_data_dir() # include/common.py
 
 
 # step 1: delete data_dir symlink in ./test;
 # step 1: delete data_dir symlink in ./test;
-opt.resuming = any(k in po.user_opts for k in ('resume','resume_after'))
-opt.skipping_deps = opt.resuming or 'skip_deps' in po.user_opts
-
-if not opt.skipping_deps:
+if not po.user_opts.get('skipping_deps'):
 	try: os.unlink(data_dir)
 	try: os.unlink(data_dir)
 	except: pass
 	except: pass
 
 
-opts.UserOpts._reset_ok += (
+# step 2: opts.init will create new data_dir in ./test (if not cfg.skipping_deps)
+cfg = opts.init(opts_data)
+
+set_globals(cfg)
+
+from test.test_py_d.common import * # this must be loaded after set_globals()
+
+type(cfg)._reset_ok += (
 	'no_daemon_autostart',
 	'no_daemon_autostart',
 	'names',
 	'names',
 	'no_timings',
 	'no_timings',
@@ -191,45 +193,49 @@ opts.UserOpts._reset_ok += (
 	'resuming',
 	'resuming',
 	'skipping_deps' )
 	'skipping_deps' )
 
 
-# step 2: opts.init will create new data_dir in ./test (if not opt.skipping_deps)
-parsed_opts = opts.init(opts_data,return_parsed=True)
-usr_args = parsed_opts.cmd_args
+cfg.resuming = any(k in po.user_opts for k in ('resume','resume_after'))
+cfg.skipping_deps = cfg.resuming or 'skip_deps' in po.user_opts
+
+cmd_args = cfg._args
 
 
-if opt.pexpect_spawn and gc.platform == 'win':
+if cfg.pexpect_spawn and gc.platform == 'win':
 	die(1,'--pexpect-spawn option not supported on Windows platform, exiting')
 	die(1,'--pexpect-spawn option not supported on Windows platform, exiting')
 
 
-if opt.daemon_id and opt.daemon_id in g.blacklisted_daemons.split():
-	die(1,f'test.py: daemon {opt.daemon_id!r} blacklisted, exiting')
+if cfg.daemon_id and cfg.daemon_id in cfg.blacklisted_daemons.split():
+	die(1,f'test.py: daemon {cfg.daemon_id!r} blacklisted, exiting')
 
 
-network_id = g.coin.lower() + ('_tn' if opt.testnet else '')
+network_id = cfg.coin.lower() + ('_tn' if cfg.testnet else '')
 
 
-from mmgen.protocol import init_proto_from_opts
-proto = init_proto_from_opts()
+proto = cfg._proto
 
 
 # step 3: move data_dir to /dev/shm and symlink it back to ./test:
 # step 3: move data_dir to /dev/shm and symlink it back to ./test:
 trash_dir = os.path.join('test','trash')
 trash_dir = os.path.join('test','trash')
 
 
-if not opt.skipping_deps:
+if not cfg.skipping_deps:
 	shm_dir = create_shm_dir(data_dir,trash_dir)
 	shm_dir = create_shm_dir(data_dir,trash_dir)
 
 
 check_segwit_opts()
 check_segwit_opts()
 
 
-testing_segwit = opt.segwit or opt.segwit_random or opt.bech32
+testing_segwit = cfg.segwit or cfg.segwit_random or cfg.bech32
 
 
-if g.test_suite_deterministic:
-	opt.no_timings = True
+if cfg.test_suite_deterministic:
+	cfg.no_timings = True
 	init_color(num_colors=0)
 	init_color(num_colors=0)
 	os.environ['MMGEN_DISABLE_COLOR'] = '1'
 	os.environ['MMGEN_DISABLE_COLOR'] = '1'
 
 
-if opt.profile:
-	opt.names = True
+if cfg.profile:
+	cfg.names = True
 
 
-if opt.exact_output:
-	def msg(s): pass
-	qmsg = qmsg_r = vmsg = vmsg_r = msg_r = msg
+if cfg.exact_output:
+	def noop(s):
+		pass
+	qmsg = qmsg_r = msg = noop
+else:
+	qmsg = cfg._util.qmsg
+	qmsg_r = cfg._util.qmsg_r
 
 
-if opt.skipping_deps:
-	opt.no_daemon_autostart = True
+if cfg.skipping_deps:
+	cfg.no_daemon_autostart = True
 
 
 from test.test_py_d.cfg import cfgs,fixup_cfgs
 from test.test_py_d.cfg import cfgs,fixup_cfgs
 
 
@@ -280,16 +286,16 @@ def list_cmds():
 	sys.exit(0)
 	sys.exit(0)
 
 
 def do_between():
 def do_between():
-	if opt.pause:
+	if cfg.pause:
 		confirm_continue()
 		confirm_continue()
-	elif (opt.verbose or opt.exact_output) and not opt.skipping_deps:
+	elif (cfg.verbose or cfg.exact_output) and not cfg.skipping_deps:
 		sys.stderr.write('\n')
 		sys.stderr.write('\n')
 
 
 def list_tmpdirs():
 def list_tmpdirs():
 	return {k:cfgs[k]['tmpdir'] for k in cfgs}
 	return {k:cfgs[k]['tmpdir'] for k in cfgs}
 
 
 def clean(usr_dirs=None,clean_overlay=True):
 def clean(usr_dirs=None,clean_overlay=True):
-	if opt.skipping_deps:
+	if cfg.skipping_deps:
 		return
 		return
 	all_dirs = list_tmpdirs()
 	all_dirs = list_tmpdirs()
 	dirnums = map(int,(usr_dirs if usr_dirs is not None else all_dirs))
 	dirnums = map(int,(usr_dirs if usr_dirs is not None else all_dirs))
@@ -335,11 +341,11 @@ def set_environ_for_spawned_scripts():
 	os.environ['MMGEN_COLUMNS'] = str(get_terminal_size().width)
 	os.environ['MMGEN_COLUMNS'] = str(get_terminal_size().width)
 
 
 	if os.getenv('MMGEN_DEBUG_ALL'):
 	if os.getenv('MMGEN_DEBUG_ALL'):
-		for name in g.env_opts:
+		for name in cfg.env_opts:
 			if name[:11] == 'MMGEN_DEBUG':
 			if name[:11] == 'MMGEN_DEBUG':
 				os.environ[name] = '1'
 				os.environ[name] = '1'
 
 
-	if not opt.system:
+	if not cfg.system:
 		os.environ['PYTHONPATH'] = repo_root
 		os.environ['PYTHONPATH'] = repo_root
 
 
 	os.environ['MMGEN_NO_LICENSE'] = '1'
 	os.environ['MMGEN_NO_LICENSE'] = '1'
@@ -392,12 +398,12 @@ class CmdGroupMgr(object):
 					if sg_name in (None,sg_key):
 					if sg_name in (None,sg_key):
 						for e in add_entries(
 						for e in add_entries(
 								sg_key,
 								sg_key,
-								add_deps = sg_name and not opt.skipping_deps,
-								added_subgroups = [sg_name] if opt.deps_only else [] ):
+								add_deps = sg_name and not cfg.skipping_deps,
+								added_subgroups = [sg_name] if cfg.deps_only else [] ):
 							yield e
 							yield e
-					if opt.deps_only and sg_key == sg_name:
+					if cfg.deps_only and sg_key == sg_name:
 						return
 						return
-				elif not opt.skipping_deps:
+				elif not cfg.skipping_deps:
 					yield (name,data)
 					yield (name,data)
 
 
 		return tuple(gen())
 		return tuple(gen())
@@ -470,12 +476,12 @@ class CmdGroupMgr(object):
 		for gname in self.cmd_groups:
 		for gname in self.cmd_groups:
 			ginfo.append(( gname, self.get_cls_by_gname(gname) ))
 			ginfo.append(( gname, self.get_cls_by_gname(gname) ))
 
 
-		if opt.list_current_cmd_groups:
-			exclude = (opt.exclude_groups or '').split(',')
+		if cfg.list_current_cmd_groups:
+			exclude = (cfg.exclude_groups or '').split(',')
 			ginfo = [g for g in ginfo
 			ginfo = [g for g in ginfo
 						if network_id in g[1].networks
 						if network_id in g[1].networks
 							and not g[0] in exclude
 							and not g[0] in exclude
-							and g[0] in tuple(self.cmd_groups_dfl) + tuple(usr_args) ]
+							and g[0] in tuple(self.cmd_groups_dfl) + tuple(cmd_args) ]
 			desc = 'CONFIGURED'
 			desc = 'CONFIGURED'
 		else:
 		else:
 			desc = 'AVAILABLE'
 			desc = 'AVAILABLE'
@@ -529,7 +535,7 @@ class TestSuiteRunner(object):
 	'test suite runner'
 	'test suite runner'
 
 
 	def __del__(self):
 	def __del__(self):
-		if opt.log:
+		if cfg.log:
 			self.log_fd.close()
 			self.log_fd.close()
 
 
 	def __init__(self,data_dir,trash_dir):
 	def __init__(self,data_dir,trash_dir):
@@ -544,21 +550,21 @@ class TestSuiteRunner(object):
 		self.resume_cmd = None
 		self.resume_cmd = None
 		self.deps_only = None
 		self.deps_only = None
 
 
-		if opt.log:
+		if cfg.log:
 			self.log_fd = open(log_file,'a')
 			self.log_fd = open(log_file,'a')
 			self.log_fd.write(f'\nLog started: {make_timestr()} UTC\n')
 			self.log_fd.write(f'\nLog started: {make_timestr()} UTC\n')
 			omsg(f'INFO → Logging to file {log_file!r}')
 			omsg(f'INFO → Logging to file {log_file!r}')
 		else:
 		else:
 			self.log_fd = None
 			self.log_fd = None
 
 
-		if opt.coverage:
+		if cfg.coverage:
 			coverdir,accfile = init_coverage()
 			coverdir,accfile = init_coverage()
 			omsg(f'INFO → Writing coverage files to {coverdir!r}')
 			omsg(f'INFO → Writing coverage files to {coverdir!r}')
 			self.pre_args = ['python3','-m','trace','--count','--coverdir='+coverdir,'--file='+accfile]
 			self.pre_args = ['python3','-m','trace','--count','--coverdir='+coverdir,'--file='+accfile]
 		else:
 		else:
 			self.pre_args = ['python3'] if gc.platform == 'win' else []
 			self.pre_args = ['python3'] if gc.platform == 'win' else []
 
 
-		if opt.pexpect_spawn:
+		if cfg.pexpect_spawn:
 			omsg(f'INFO → Using pexpect.spawn() for real terminal emulation')
 			omsg(f'INFO → Using pexpect.spawn() for real terminal emulation')
 
 
 	def spawn_wrapper(self,cmd,
 	def spawn_wrapper(self,cmd,
@@ -572,12 +578,12 @@ class TestSuiteRunner(object):
 			timeout       = None,
 			timeout       = None,
 			pexpect_spawn = None ):
 			pexpect_spawn = None ):
 
 
-		desc = self.ts.test_name if opt.names else self.gm.dpy_data[self.ts.test_name][1]
+		desc = self.ts.test_name if cfg.names else self.gm.dpy_data[self.ts.test_name][1]
 		if extra_desc:
 		if extra_desc:
 			desc += ' ' + extra_desc
 			desc += ' ' + extra_desc
 
 
 		cmd_path = (
 		cmd_path = (
-			cmd if opt.system # opt.system is broken for main test group with overlay tree
+			cmd if cfg.system # cfg.system is broken for main test group with overlay tree
 			else os.path.relpath(os.path.join(repo_root,cmd_dir,cmd)) )
 			else os.path.relpath(os.path.join(repo_root,cmd_dir,cmd)) )
 
 
 		args = (
 		args = (
@@ -591,7 +597,7 @@ class TestSuiteRunner(object):
 		qargs = ['{q}{}{q}'.format( a, q = "'" if ' ' in a else '' ) for a in args]
 		qargs = ['{q}{}{q}'.format( a, q = "'" if ' ' in a else '' ) for a in args]
 		cmd_disp = ' '.join(qargs).replace('\\','/') # for mingw
 		cmd_disp = ' '.join(qargs).replace('\\','/') # for mingw
 
 
-		if opt.log:
+		if cfg.log:
 			self.log_fd.write('[{}][{}:{}] {}\n'.format(
 			self.log_fd.write('[{}][{}:{}] {}\n'.format(
 				proto.coin.lower(),
 				proto.coin.lower(),
 				self.ts.group_name,
 				self.ts.group_name,
@@ -605,11 +611,11 @@ class TestSuiteRunner(object):
 					args ))
 					args ))
 
 
 		if not no_msg:
 		if not no_msg:
-			t_pfx = '' if opt.no_timings else f'[{time.time() - self.start_time:08.2f}] '
-			if opt.verbose or opt.print_cmdline or opt.exact_output:
+			t_pfx = '' if cfg.no_timings else f'[{time.time() - self.start_time:08.2f}] '
+			if cfg.verbose or cfg.print_cmdline or cfg.exact_output:
 				omsg(green(f'{t_pfx}Testing: {desc}'))
 				omsg(green(f'{t_pfx}Testing: {desc}'))
 				if not msg_only:
 				if not msg_only:
-					clr1,clr2 = (nocolor,nocolor) if opt.print_cmdline else (green,cyan)
+					clr1,clr2 = (nocolor,nocolor) if cfg.print_cmdline else (green,cyan)
 					omsg(
 					omsg(
 						clr1('Executing: ') +
 						clr1('Executing: ') +
 						clr2(repr(cmd_disp) if gc.platform == 'win' else cmd_disp)
 						clr2(repr(cmd_disp) if gc.platform == 'win' else cmd_disp)
@@ -623,8 +629,8 @@ class TestSuiteRunner(object):
 		# NB: the `pexpect_spawn` arg enables hold_protect and send_delay while the corresponding cmdline
 		# NB: the `pexpect_spawn` arg enables hold_protect and send_delay while the corresponding cmdline
 		# option does not.  For performance reasons, this is the desired behavior.  For full emulation of
 		# option does not.  For performance reasons, this is the desired behavior.  For full emulation of
 		# the user experience with hold protect enabled, specify --buf-keypress or --demo.
 		# the user experience with hold protect enabled, specify --buf-keypress or --demo.
-		send_delay = 0.4 if pexpect_spawn is True or opt.buf_keypress else None
-		pexpect_spawn = pexpect_spawn if pexpect_spawn is not None else bool(opt.pexpect_spawn)
+		send_delay = 0.4 if pexpect_spawn is True or cfg.buf_keypress else None
+		pexpect_spawn = pexpect_spawn if pexpect_spawn is not None else bool(cfg.pexpect_spawn)
 
 
 		os.environ['MMGEN_HOLD_PROTECT_DISABLE'] = '' if send_delay else '1'
 		os.environ['MMGEN_HOLD_PROTECT_DISABLE'] = '' if send_delay else '1'
 		os.environ['MMGEN_TEST_SUITE_POPEN_SPAWN'] = '' if pexpect_spawn else '1'
 		os.environ['MMGEN_TEST_SUITE_POPEN_SPAWN'] = '' if pexpect_spawn else '1'
@@ -649,7 +655,7 @@ class TestSuiteRunner(object):
 		t = int(time.time() - self.start_time)
 		t = int(time.time() - self.start_time)
 		sys.stderr.write(green(
 		sys.stderr.write(green(
 			f'{self.cmd_total} test{suf(self.cmd_total)} performed' +
 			f'{self.cmd_total} test{suf(self.cmd_total)} performed' +
-			('\n' if opt.no_timings else f'.  Elapsed time: {t//60:02d}:{t%60:02d}\n')
+			('\n' if cfg.no_timings else f'.  Elapsed time: {t//60:02d}:{t%60:02d}\n')
 		))
 		))
 
 
 	def init_group(self,gname,sg_name=None,cmd=None,quiet=False,do_clean=True):
 	def init_group(self,gname,sg_name=None,cmd=None,quiet=False,do_clean=True):
@@ -657,7 +663,7 @@ class TestSuiteRunner(object):
 		ts_cls = CmdGroupMgr().load_mod(gname)
 		ts_cls = CmdGroupMgr().load_mod(gname)
 
 
 		for k in ('segwit','segwit_random','bech32'):
 		for k in ('segwit','segwit_random','bech32'):
-			if getattr(opt,k):
+			if getattr(cfg,k):
 				segwit_opt = k
 				segwit_opt = k
 				break
 				break
 		else:
 		else:
@@ -706,28 +712,28 @@ class TestSuiteRunner(object):
 		# pass through opts from cmdline (po.user_opts)
 		# pass through opts from cmdline (po.user_opts)
 		self.passthru_opts = ['--{}{}'.format(
 		self.passthru_opts = ['--{}{}'.format(
 				k.replace('_','-'),
 				k.replace('_','-'),
-				'=' + getattr(opt,k) if getattr(opt,k) != True else ''
-			) for k in self.ts.base_passthru_opts + self.ts.passthru_opts if k in parsed_opts.user_opts]
+				'' if cfg._uopts[k] is True else '=' + cfg._uopts[k]
+			) for k in cfg._uopts if k in self.ts.base_passthru_opts + self.ts.passthru_opts]
 
 
-		if opt.resuming:
-			rc = opt.resume or opt.resume_after
-			offset = 1 if opt.resume_after else 0
+		if cfg.resuming:
+			rc = cfg.resume or cfg.resume_after
+			offset = 1 if cfg.resume_after else 0
 			self.resume_cmd = self.gm.cmd_list[self.gm.cmd_list.index(rc)+offset]
 			self.resume_cmd = self.gm.cmd_list[self.gm.cmd_list.index(rc)+offset]
 			omsg(f'INFO → Resuming at command {self.resume_cmd!r}')
 			omsg(f'INFO → Resuming at command {self.resume_cmd!r}')
-			if opt.step:
-				opt.exit_after = self.resume_cmd
+			if cfg.step:
+				cfg.exit_after = self.resume_cmd
 
 
-		if opt.exit_after and opt.exit_after not in self.gm.cmd_list:
-			die(1,f'{opt.exit_after!r}: command not recognized')
+		if cfg.exit_after and cfg.exit_after not in self.gm.cmd_list:
+			die(1,f'{cfg.exit_after!r}: command not recognized')
 
 
 		return True
 		return True
 
 
-	def run_tests(self,usr_args):
+	def run_tests(self,cmd_args):
 		self.start_time = time.time()
 		self.start_time = time.time()
 		self.daemon_started = False
 		self.daemon_started = False
 		gname_save = None
 		gname_save = None
-		if usr_args:
-			for arg in usr_args:
+		if cmd_args:
+			for arg in cmd_args:
 				if arg in self.gm.cmd_groups:
 				if arg in self.gm.cmd_groups:
 					if not self.init_group(arg):
 					if not self.init_group(arg):
 						continue
 						continue
@@ -752,7 +758,7 @@ class TestSuiteRunner(object):
 						if not self.init_group(gname,sg_name,cmdname,quiet=same_grp,do_clean=not same_grp):
 						if not self.init_group(gname,sg_name,cmdname,quiet=same_grp,do_clean=not same_grp):
 							continue
 							continue
 						if cmdname:
 						if cmdname:
-							if opt.deps_only:
+							if cfg.deps_only:
 								self.deps_only = cmdname
 								self.deps_only = cmdname
 							try:
 							try:
 								self.check_needs_rerun(cmdname,build=True)
 								self.check_needs_rerun(cmdname,build=True)
@@ -772,13 +778,13 @@ class TestSuiteRunner(object):
 					else:
 					else:
 						die(1,f'{arg!r}: command not recognized')
 						die(1,f'{arg!r}: command not recognized')
 		else:
 		else:
-			if opt.exclude_groups:
-				exclude = opt.exclude_groups.split(',')
+			if cfg.exclude_groups:
+				exclude = cfg.exclude_groups.split(',')
 				for e in exclude:
 				for e in exclude:
 					if e not in self.gm.cmd_groups_dfl:
 					if e not in self.gm.cmd_groups_dfl:
 						die(1,f'{e!r}: group not recognized')
 						die(1,f'{e!r}: group not recognized')
 			for gname in self.gm.cmd_groups_dfl:
 			for gname in self.gm.cmd_groups_dfl:
-				if opt.exclude_groups and gname in exclude:
+				if cfg.exclude_groups and gname in exclude:
 					continue
 					continue
 				if not self.init_group(gname):
 				if not self.init_group(gname):
 					continue
 					continue
@@ -832,7 +838,7 @@ class TestSuiteRunner(object):
 				for fn in fns:
 				for fn in fns:
 					if not root:
 					if not root:
 						os.unlink(fn)
 						os.unlink(fn)
-				if not (dpy and opt.skipping_deps):
+				if not (dpy and cfg.skipping_deps):
 					self.run_test(cmd)
 					self.run_test(cmd)
 				if not root:
 				if not root:
 					do_between()
 					do_between()
@@ -862,10 +868,10 @@ class TestSuiteRunner(object):
 				return
 				return
 			bmsg(f'Resuming at {self.resume_cmd!r}')
 			bmsg(f'Resuming at {self.resume_cmd!r}')
 			self.resume_cmd = None
 			self.resume_cmd = None
-			opt.skipping_deps = False
-			opt.resuming = False
+			cfg.skipping_deps = False
+			cfg.resuming = False
 
 
-		if opt.profile:
+		if cfg.profile:
 			start = time.time()
 			start = time.time()
 
 
 		self.ts.test_name = cmd # NB: Do not remove, this needs to be set twice
 		self.ts.test_name = cmd # NB: Do not remove, this needs to be set twice
@@ -873,24 +879,24 @@ class TestSuiteRunner(object):
 #		self.ts.test_dpydata = cdata
 #		self.ts.test_dpydata = cdata
 		self.ts.tmpdir_num = cdata[0]
 		self.ts.tmpdir_num = cdata[0]
 #		self.ts.cfg = cfgs[str(cdata[0])] # will remove this eventually
 #		self.ts.cfg = cfgs[str(cdata[0])] # will remove this eventually
-		cfg = cfgs[str(cdata[0])]
+		test_cfg = cfgs[str(cdata[0])]
 		for k in (  'seed_len', 'seed_id',
 		for k in (  'seed_len', 'seed_id',
 					'wpasswd', 'kapasswd',
 					'wpasswd', 'kapasswd',
 					'segwit', 'hash_preset',
 					'segwit', 'hash_preset',
 					'bw_filename', 'bw_params', 'ref_bw_seed_id',
 					'bw_filename', 'bw_params', 'ref_bw_seed_id',
 					'addr_idx_list', 'pass_idx_list' ):
 					'addr_idx_list', 'pass_idx_list' ):
-			if k in cfg:
-				setattr(self.ts,k,cfg[k])
+			if k in test_cfg:
+				setattr(self.ts,k,test_cfg[k])
 
 
 		ret = getattr(self.ts,cmd)(*arg_list) # run the test
 		ret = getattr(self.ts,cmd)(*arg_list) # run the test
 		if type(ret).__name__ == 'coroutine':
 		if type(ret).__name__ == 'coroutine':
 			ret = async_run(ret)
 			ret = async_run(ret)
 		self.process_retval(cmd,ret)
 		self.process_retval(cmd,ret)
 
 
-		if opt.profile:
+		if cfg.profile:
 			omsg('\r\033[50C{:.4f}'.format( time.time() - start ))
 			omsg('\r\033[50C{:.4f}'.format( time.time() - start ))
 
 
-		if cmd == opt.exit_after:
+		if cmd == cfg.exit_after:
 			sys.exit(0)
 			sys.exit(0)
 
 
 	def warn_skipped(self):
 	def warn_skipped(self):
@@ -925,7 +931,7 @@ class TestSuiteRunner(object):
 		if cmd not in self.gm.cmd_list:
 		if cmd not in self.gm.cmd_list:
 			die(1,f'{cmd!r}: unrecognized command')
 			die(1,f'{cmd!r}: unrecognized command')
 
 
-		if not opt.quiet:
+		if not cfg.quiet:
 			omsg(f'Checking dependencies for {cmd!r}')
 			omsg(f'Checking dependencies for {cmd!r}')
 
 
 		self.check_needs_rerun(self.ts,cmd,build=False)
 		self.check_needs_rerun(self.ts,cmd,build=False)
@@ -966,18 +972,18 @@ class TestSuiteRunner(object):
 
 
 # main()
 # main()
 
 
-if not opt.skipping_deps: # do this before list cmds exit, so we stay in sync with shm_dir
+if not cfg.skipping_deps: # do this before list cmds exit, so we stay in sync with shm_dir
 	create_tmp_dirs(shm_dir)
 	create_tmp_dirs(shm_dir)
 
 
-if opt.list_cmd_groups:
+if cfg.list_cmd_groups:
 	CmdGroupMgr().list_cmd_groups()
 	CmdGroupMgr().list_cmd_groups()
-elif opt.list_cmds:
+elif cfg.list_cmds:
 	list_cmds()
 	list_cmds()
-elif usr_args and usr_args[0] in utils:
-	globals()[usr_args[0]](*usr_args[1:])
+elif cmd_args and cmd_args[0] in utils:
+	globals()[cmd_args[0]](*cmd_args[1:])
 	sys.exit(0)
 	sys.exit(0)
 
 
-if opt.pause:
+if cfg.pause:
 	set_restore_term_at_exit()
 	set_restore_term_at_exit()
 
 
 set_environ_for_spawned_scripts()
 set_environ_for_spawned_scripts()
@@ -986,7 +992,7 @@ from mmgen.exception import TestSuiteException,TestSuiteFatalException
 
 
 try:
 try:
 	tr = TestSuiteRunner(data_dir,trash_dir)
 	tr = TestSuiteRunner(data_dir,trash_dir)
-	tr.run_tests(usr_args)
+	tr.run_tests(cmd_args)
 	tr.warn_skipped()
 	tr.warn_skipped()
 	if tr.daemon_started:
 	if tr.daemon_started:
 		stop_test_daemons(network_id)
 		stop_test_daemons(network_id)

+ 3 - 2
test/test_py_d/cfg.py

@@ -13,6 +13,7 @@ test.test_py_d.cfg: configuration data for test.py
 """
 """
 
 
 from .common import *
 from .common import *
+from ..include.common import cfg
 
 
 cmd_groups_dfl = {
 cmd_groups_dfl = {
 	'misc':             ('TestSuiteMisc',{}),
 	'misc':             ('TestSuiteMisc',{}),
@@ -229,8 +230,8 @@ def fixup_cfgs():
 		cfgs[target]['tmpdir'] = os.path.join('test','tmp',target)
 		cfgs[target]['tmpdir'] = os.path.join('test','tmp',target)
 
 
 	for k in cfgs:
 	for k in cfgs:
-		cfgs[k]['segwit'] = randbool() if opt.segwit_random else bool(opt.segwit or opt.bech32)
+		cfgs[k]['segwit'] = randbool() if cfg.segwit_random else bool(cfg.segwit or cfg.bech32)
 
 
-	if g.debug_utf8:
+	if cfg.debug_utf8:
 		for k in cfgs:
 		for k in cfgs:
 			cfgs[k]['tmpdir'] += '-α'
 			cfgs[k]['tmpdir'] += '-α'

+ 7 - 6
test/test_py_d/common.py

@@ -21,7 +21,7 @@ test.test_py_d.common: Shared routines and data for the test.py test suite
 """
 """
 
 
 import sys,os
 import sys,os
-from mmgen.globalvars import g,gc
+from mmgen.globalvars import gc
 from mmgen.util import msg
 from mmgen.util import msg
 from ..include.common import *
 from ..include.common import *
 
 
@@ -71,9 +71,9 @@ chksum_pat = r'\b[A-F0-9]{4} [A-F0-9]{4} [A-F0-9]{4} [A-F0-9]{4}\b'
 Ctrl_U = '\x15'
 Ctrl_U = '\x15'
 
 
 def ok_msg():
 def ok_msg():
-	if opt.profile:
+	if cfg.profile:
 		return
 		return
-	sys.stderr.write(green('\nOK\n') if opt.exact_output or opt.verbose else ' OK\n')
+	sys.stderr.write(green('\nOK\n') if cfg.exact_output or cfg.verbose else ' OK\n')
 
 
 def skip(name,reason=None):
 def skip(name,reason=None):
 	msg('Skipping {}{}'.format( name, f' ({reason})' if reason else '' ))
 	msg('Skipping {}{}'.format( name, f' ({reason})' if reason else '' ))
@@ -82,10 +82,11 @@ def skip(name,reason=None):
 def confirm_continue():
 def confirm_continue():
 	from mmgen.ui import keypress_confirm
 	from mmgen.ui import keypress_confirm
 	if keypress_confirm(
 	if keypress_confirm(
+			cfg,
 			blue('Continue? (Y/n): '),
 			blue('Continue? (Y/n): '),
 			default_yes     = True,
 			default_yes     = True,
 			complete_prompt = True ):
 			complete_prompt = True ):
-		if opt.verbose or opt.exact_output:
+		if cfg.verbose or cfg.exact_output:
 			sys.stderr.write('\n')
 			sys.stderr.write('\n')
 	else:
 	else:
 		raise KeyboardInterrupt('Exiting at user request')
 		raise KeyboardInterrupt('Exiting at user request')
@@ -96,7 +97,7 @@ def randbool():
 def disable_debug():
 def disable_debug():
 	global save_debug
 	global save_debug
 	save_debug = {}
 	save_debug = {}
-	for k in g.env_opts:
+	for k in cfg.env_opts:
 		if k[:11] == 'MMGEN_DEBUG':
 		if k[:11] == 'MMGEN_DEBUG':
 			save_debug[k] = os.getenv(k)
 			save_debug[k] = os.getenv(k)
 			os.environ[k] = ''
 			os.environ[k] = ''
@@ -118,7 +119,7 @@ def get_file_with_ext(tdir,ext,delete=True,no_dot=False,return_list=False,delete
 
 
 	if len(flist) > 1 or delete_all:
 	if len(flist) > 1 or delete_all:
 		if delete or delete_all:
 		if delete or delete_all:
-			if (opt.exact_output or opt.verbose) and not opt.quiet:
+			if (cfg.exact_output or cfg.verbose) and not cfg.quiet:
 				if delete_all:
 				if delete_all:
 					msg(f'Deleting all *.{ext} files in {tdir!r}')
 					msg(f'Deleting all *.{ext} files in {tdir!r}')
 				else:
 				else:

+ 5 - 6
test/test_py_d/ts_autosign.py

@@ -23,8 +23,7 @@ test.test_py_d.ts_autosign: Autosign tests for the test.py test suite
 import os,shutil
 import os,shutil
 from subprocess import run
 from subprocess import run
 
 
-from mmgen.globalvars import g,gc
-from mmgen.opts import opt
+from mmgen.globalvars import gc
 
 
 from ..include.common import *
 from ..include.common import *
 from .common import *
 from .common import *
@@ -88,7 +87,7 @@ class TestSuiteAutosignBase(TestSuiteBase):
 			die(1,f'Test {type(self).__name__} not supported for Windows platform')
 			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
 		self.network_ids = [c+'_tn' for c in self.daemon_coins] + self.daemon_coins
 
 
-		if self.simulate and not opt.exact_output:
+		if self.simulate and not cfg.exact_output:
 			die(1,red('This command must be run with --exact-output enabled!'))
 			die(1,red('This command must be run with --exact-output enabled!'))
 
 
 		if self.simulate or not self.live:
 		if self.simulate or not self.live:
@@ -168,7 +167,7 @@ class TestSuiteAutosignBase(TestSuiteBase):
 		mn = read_from_file(mn_file).strip().split()
 		mn = read_from_file(mn_file).strip().split()
 		from mmgen.mn_entry import mn_entry
 		from mmgen.mn_entry import mn_entry
 		entry_mode = 'full'
 		entry_mode = 'full'
-		mne = mn_entry(mn_type,entry_mode)
+		mne = mn_entry( cfg, mn_type, entry_mode )
 		t.expect('Type a number.*: ',str(mne.entry_modes.index(entry_mode)+1),regex=True)
 		t.expect('Type a number.*: ',str(mne.entry_modes.index(entry_mode)+1),regex=True)
 		stealth_mnemonic_entry(t,mne,mn,entry_mode)
 		stealth_mnemonic_entry(t,mne,mn,entry_mode)
 		wf = t.written_to_file('Autosign wallet')
 		wf = t.written_to_file('Autosign wallet')
@@ -203,7 +202,7 @@ class TestSuiteAutosignBase(TestSuiteBase):
 
 
 		for f,fn in zip(tfs,tfns):
 		for f,fn in zip(tfs,tfns):
 			if fn: # use empty fn to skip file
 			if fn: # use empty fn to skip file
-				if g.debug_utf8:
+				if cfg.debug_utf8:
 					ext = '.testnet.rawtx' if fn.endswith('.testnet.rawtx') else '.rawtx'
 					ext = '.testnet.rawtx' if fn.endswith('.testnet.rawtx') else '.rawtx'
 					fn = fn[:-len(ext)] + '-α' + ext
 					fn = fn[:-len(ext)] + '-α' + ext
 				target = joinpath(self.mountpoint,'tx',fn)
 				target = joinpath(self.mountpoint,'tx',fn)
@@ -432,7 +431,7 @@ class TestSuiteAutosignLive(TestSuiteAutosignBTC):
 		t = self.spawn(
 		t = self.spawn(
 			'mmgen-autosign',
 			'mmgen-autosign',
 			self.opts + led_opts + ['--quiet','--no-summary','wait'])
 			self.opts + led_opts + ['--quiet','--no-summary','wait'])
-		if not opt.exact_output:
+		if not cfg.exact_output:
 			omsg('')
 			omsg('')
 		prompt_insert_sign(t)
 		prompt_insert_sign(t)
 
 

+ 5 - 7
test/test_py_d/ts_base.py

@@ -21,8 +21,7 @@ test.test_py_d.ts_base: Base class for the test.py test suite
 """
 """
 
 
 import os
 import os
-from mmgen.globalvars import g,gc
-from mmgen.opts import opt
+from mmgen.globalvars import gc
 from ..include.common import *
 from ..include.common import *
 from .common import *
 from .common import *
 
 
@@ -37,13 +36,12 @@ class TestSuiteBase(object):
 	need_daemon = False
 	need_daemon = False
 
 
 	def __init__(self,trunner,cfgs,spawn):
 	def __init__(self,trunner,cfgs,spawn):
-		from mmgen.protocol import init_proto_from_opts
-		self.proto = init_proto_from_opts(need_amt=True)
+		self.proto = cfg._proto
 		self.tr = trunner
 		self.tr = trunner
 		self.cfgs = cfgs
 		self.cfgs = cfgs
 		self.spawn = spawn
 		self.spawn = spawn
 		self.have_dfl_wallet = False
 		self.have_dfl_wallet = False
-		self.usr_rand_chars = (5,30)[bool(opt.usr_random)]
+		self.usr_rand_chars = (5,30)[bool(cfg.usr_random)]
 		self.usr_rand_arg = f'-r{self.usr_rand_chars}'
 		self.usr_rand_arg = f'-r{self.usr_rand_chars}'
 		self.altcoin_pfx = '' if self.proto.base_coin == 'BTC' else '-'+self.proto.base_coin
 		self.altcoin_pfx = '' if self.proto.base_coin == 'BTC' else '-'+self.proto.base_coin
 		self.tn_ext = ('','.testnet')[self.proto.testnet]
 		self.tn_ext = ('','.testnet')[self.proto.testnet]
@@ -54,11 +52,11 @@ class TestSuiteBase(object):
 
 
 	@property
 	@property
 	def tmpdir(self):
 	def tmpdir(self):
-		return os.path.join('test','tmp','{}{}'.format(self.tmpdir_num,'-α' if g.debug_utf8 else ''))
+		return os.path.join('test','tmp','{}{}'.format(self.tmpdir_num,'-α' if cfg.debug_utf8 else ''))
 
 
 	@property
 	@property
 	def segwit_mmtype(self):
 	def segwit_mmtype(self):
-		return ('segwit','bech32')[bool(opt.bech32)] if self.segwit else None
+		return ('segwit','bech32')[bool(cfg.bech32)] if self.segwit else None
 
 
 	@property
 	@property
 	def segwit_arg(self):
 	def segwit_arg(self):

+ 3 - 3
test/test_py_d/ts_cfgfile.py

@@ -130,7 +130,7 @@ class TestSuiteCfgFile(TestSuiteBase):
 			t.expect(s)
 			t.expect(s)
 
 
 		if t.pexpect_spawn: # view and exit pager
 		if t.pexpect_spawn: # view and exit pager
-			time.sleep(1 if opt.exact_output else t.send_delay)
+			time.sleep(1 if cfg.exact_output else t.send_delay)
 			t.send('q')
 			t.send('q')
 
 
 		t.expect(cp,'n')
 		t.expect(cp,'n')
@@ -202,7 +202,7 @@ class TestSuiteCfgFile(TestSuiteBase):
 			('ETH','True', '5.4321',True),
 			('ETH','True', '5.4321',True),
 			('ETC','False','5.4321',False)
 			('ETC','False','5.4321',False)
 		):
 		):
-			if opt.no_altcoin and coin != 'BTC':
+			if cfg.no_altcoin and coin != 'BTC':
 				continue
 				continue
 			t = self.spawn_test(
 			t = self.spawn_test(
 				args = [
 				args = [
@@ -244,7 +244,7 @@ class TestSuiteCfgFile(TestSuiteBase):
 
 
 	def chain_names(self):
 	def chain_names(self):
 
 
-		if opt.no_altcoin:
+		if cfg.no_altcoin:
 			return 'skip'
 			return 'skip'
 
 
 		def run(chk,testnet):
 		def run(chk,testnet):

+ 0 - 2
test/test_py_d/ts_chainsplit.py

@@ -21,8 +21,6 @@ test.test_py_d.ts_chainsplit: Forking scenario tests for the test.py test suite
 This module is unmaintained and currently non-functional
 This module is unmaintained and currently non-functional
 """
 """
 import os
 import os
-from mmgen.globalvars import g
-from mmgen.opts import opt
 from mmgen.util import die
 from mmgen.util import die
 from ..include.common import *
 from ..include.common import *
 from .common import *
 from .common import *

+ 33 - 28
test/test_py_d/ts_ethdev.py

@@ -25,8 +25,7 @@ from decimal import Decimal
 from collections import namedtuple
 from collections import namedtuple
 from subprocess import run,PIPE,DEVNULL
 from subprocess import run,PIPE,DEVNULL
 
 
-from mmgen.globalvars import g,gc
-from mmgen.opts import opt
+from mmgen.globalvars import gc
 from mmgen.util import die
 from mmgen.util import die
 from mmgen.protocol import CoinProtocol
 from mmgen.protocol import CoinProtocol
 from ..include.common import *
 from ..include.common import *
@@ -119,7 +118,7 @@ token_bals_getbalance = {
 from .ts_base import *
 from .ts_base import *
 from .ts_shared import *
 from .ts_shared import *
 
 
-coin = g.coin
+coin = cfg.coin
 
 
 class TestSuiteEthdev(TestSuiteBase,TestSuiteShared):
 class TestSuiteEthdev(TestSuiteBase,TestSuiteShared):
 	'Ethereum transacting, token deployment and tracking wallet operations'
 	'Ethereum transacting, token deployment and tracking wallet operations'
@@ -365,10 +364,10 @@ class TestSuiteEthdev(TestSuiteBase,TestSuiteShared):
 			return
 			return
 
 
 		from mmgen.protocol import init_proto
 		from mmgen.protocol import init_proto
-		self.proto = init_proto(g.coin,network='regtest',need_amt=True)
+		self.proto = init_proto( cfg, cfg.coin, network='regtest', need_amt=True )
 
 
 		from mmgen.daemon import CoinDaemon
 		from mmgen.daemon import CoinDaemon
-		self.daemon = CoinDaemon(self.proto.coin+'_rt',test_suite=True)
+		self.daemon = CoinDaemon( cfg, self.proto.coin+'_rt', test_suite=True )
 
 
 		self.using_solc = check_solc_ver()
 		self.using_solc = check_solc_ver()
 		if not self.using_solc:
 		if not self.using_solc:
@@ -405,7 +404,7 @@ class TestSuiteEthdev(TestSuiteBase,TestSuiteShared):
 	@property
 	@property
 	async def rpc(self):
 	async def rpc(self):
 		from mmgen.rpc import rpc_init
 		from mmgen.rpc import rpc_init
-		return await rpc_init(self.proto)
+		return await rpc_init(cfg,self.proto)
 
 
 	async def setup(self):
 	async def setup(self):
 		self.spawn('',msg_only=True)
 		self.spawn('',msg_only=True)
@@ -431,11 +430,11 @@ class TestSuiteEthdev(TestSuiteBase,TestSuiteShared):
 		if d.id in ('geth','erigon'):
 		if d.id in ('geth','erigon'):
 			imsg('  {:19} {}'.format('Cmdline:',' '.join(e for e in d.start_cmd if not 'verbosity' in e)))
 			imsg('  {:19} {}'.format('Cmdline:',' '.join(e for e in d.start_cmd if not 'verbosity' in e)))
 
 
-		if not opt.no_daemon_autostart:
+		if not cfg.no_daemon_autostart:
 			if not d.id in ('geth','erigon'):
 			if not d.id in ('geth','erigon'):
 				d.stop(silent=True)
 				d.stop(silent=True)
 				d.remove_datadir()
 				d.remove_datadir()
-			d.start( silent = not (opt.verbose or opt.exact_output) )
+			d.start( silent = not (cfg.verbose or cfg.exact_output) )
 			rpc = await self.rpc
 			rpc = await self.rpc
 			imsg(f'Daemon: {rpc.daemon.coind_name} v{rpc.daemon_version_str}')
 			imsg(f'Daemon: {rpc.daemon.coind_name} v{rpc.daemon_version_str}')
 
 
@@ -449,6 +448,7 @@ class TestSuiteEthdev(TestSuiteBase,TestSuiteShared):
 
 
 			from mmgen.proto.eth.misc import extract_key_from_geth_keystore_wallet
 			from mmgen.proto.eth.misc import extract_key_from_geth_keystore_wallet
 			key = extract_key_from_geth_keystore_wallet(
 			key = extract_key_from_geth_keystore_wallet(
+				cfg       = cfg,
 				wallet_fn = wallet_fn,
 				wallet_fn = wallet_fn,
 				passwd = b'' )
 				passwd = b'' )
 
 
@@ -552,7 +552,7 @@ class TestSuiteEthdev(TestSuiteBase,TestSuiteShared):
 		return t
 		return t
 
 
 	def addrimport(self,ext='21-23]{}.regtest.addrs',expect='9/9',add_args=[],bad_input=False):
 	def addrimport(self,ext='21-23]{}.regtest.addrs',expect='9/9',add_args=[],bad_input=False):
-		ext = ext.format('-α' if g.debug_utf8 else '')
+		ext = ext.format('-α' if cfg.debug_utf8 else '')
 		fn = self.get_file_with_ext(ext,no_dot=True,delete=False)
 		fn = self.get_file_with_ext(ext,no_dot=True,delete=False)
 		t = self.spawn('mmgen-addrimport', self.eth_args[1:-1] + add_args + [fn])
 		t = self.spawn('mmgen-addrimport', self.eth_args[1:-1] + add_args + [fn])
 		if bad_input:
 		if bad_input:
@@ -605,7 +605,7 @@ class TestSuiteEthdev(TestSuiteBase,TestSuiteShared):
 		return t
 		return t
 
 
 	def txsign(self,ni=False,ext='{}.regtest.rawtx',add_args=[],dev_send=False):
 	def txsign(self,ni=False,ext='{}.regtest.rawtx',add_args=[],dev_send=False):
-		ext = ext.format('-α' if g.debug_utf8 else '')
+		ext = ext.format('-α' if cfg.debug_utf8 else '')
 		keyfile = joinpath(self.tmpdir,parity_devkey_fn)
 		keyfile = joinpath(self.tmpdir,parity_devkey_fn)
 		txfile = self.get_file_with_ext(ext,no_dot=True)
 		txfile = self.get_file_with_ext(ext,no_dot=True)
 		t = self.spawn( 'mmgen-txsign',
 		t = self.spawn( 'mmgen-txsign',
@@ -620,17 +620,17 @@ class TestSuiteEthdev(TestSuiteBase,TestSuiteShared):
 		return self.txsign_ui_common(t,ni=ni,has_label=True)
 		return self.txsign_ui_common(t,ni=ni,has_label=True)
 
 
 	def txsend(self,ni=False,ext='{}.regtest.sigtx',add_args=[]):
 	def txsend(self,ni=False,ext='{}.regtest.sigtx',add_args=[]):
-		ext = ext.format('-α' if g.debug_utf8 else '')
+		ext = ext.format('-α' if cfg.debug_utf8 else '')
 		txfile = self.get_file_with_ext(ext,no_dot=True)
 		txfile = self.get_file_with_ext(ext,no_dot=True)
 		t = self.spawn('mmgen-txsend', self.eth_args + add_args + [txfile])
 		t = self.spawn('mmgen-txsend', self.eth_args + add_args + [txfile])
 		txid = self.txsend_ui_common(t,
 		txid = self.txsend_ui_common(t,
-			quiet      = not g.debug,
+			quiet      = not cfg.debug,
 			bogus_send = False,
 			bogus_send = False,
 			has_label  = True )
 			has_label  = True )
 		return t
 		return t
 
 
 	def txview(self,ext_fs):
 	def txview(self,ext_fs):
-		ext = ext_fs.format('-α' if g.debug_utf8 else '')
+		ext = ext_fs.format('-α' if cfg.debug_utf8 else '')
 		txfile = self.get_file_with_ext(ext,no_dot=True)
 		txfile = self.get_file_with_ext(ext,no_dot=True)
 		return self.spawn( 'mmgen-tool',['--verbose','txview',txfile] )
 		return self.spawn( 'mmgen-tool',['--verbose','txview',txfile] )
 
 
@@ -650,7 +650,7 @@ class TestSuiteEthdev(TestSuiteBase,TestSuiteShared):
 			caller  = 'txdo',
 			caller  = 'txdo',
 			acct    = '1',
 			acct    = '1',
 			no_read = True )
 			no_read = True )
-		self._do_confirm_send(t,quiet=not g.debug,sure=False)
+		self._do_confirm_send(t,quiet=not cfg.debug,sure=False)
 		t.read()
 		t.read()
 		self.get_file_with_ext('sigtx',delete_all=True)
 		self.get_file_with_ext('sigtx',delete_all=True)
 		return t
 		return t
@@ -686,7 +686,7 @@ class TestSuiteEthdev(TestSuiteBase,TestSuiteShared):
 	def bal3(self):    return self.bal(n='3')
 	def bal3(self):    return self.bal(n='3')
 
 
 	def tx_status(self,ext,expect_str,expect_str2='',add_args=[],exit_val=0):
 	def tx_status(self,ext,expect_str,expect_str2='',add_args=[],exit_val=0):
-		ext = ext.format('-α' if g.debug_utf8 else '')
+		ext = ext.format('-α' if cfg.debug_utf8 else '')
 		txfile = self.get_file_with_ext(ext,no_dot=True)
 		txfile = self.get_file_with_ext(ext,no_dot=True)
 		t = self.spawn('mmgen-txsend', self.eth_args + add_args + ['--status',txfile])
 		t = self.spawn('mmgen-txsend', self.eth_args + add_args + ['--status',txfile])
 		t.expect(expect_str)
 		t.expect(expect_str)
@@ -707,7 +707,7 @@ class TestSuiteEthdev(TestSuiteBase,TestSuiteShared):
 			key = self.keystore_data['key']
 			key = self.keystore_data['key']
 			imsg(f'Key:       {key}')
 			imsg(f'Key:       {key}')
 			from mmgen.proto.eth.misc import ec_sign_message_with_privkey
 			from mmgen.proto.eth.misc import ec_sign_message_with_privkey
-			return ec_sign_message_with_privkey(self.message,bytes.fromhex(key),'eth_sign')
+			return ec_sign_message_with_privkey(cfg,self.message,bytes.fromhex(key),'eth_sign')
 
 
 		async def create_signature_rpc():
 		async def create_signature_rpc():
 			addr = self.read_from_tmpfile('signer_addr').strip()
 			addr = self.read_from_tmpfile('signer_addr').strip()
@@ -791,7 +791,7 @@ class TestSuiteEthdev(TestSuiteBase,TestSuiteShared):
 			eth_fee_res      = True )
 			eth_fee_res      = True )
 
 
 	def txbump(self,ext=',40000]{}.regtest.rawtx',fee='50G',add_args=[]):
 	def txbump(self,ext=',40000]{}.regtest.rawtx',fee='50G',add_args=[]):
-		ext = ext.format('-α' if g.debug_utf8 else '')
+		ext = ext.format('-α' if cfg.debug_utf8 else '')
 		txfile = self.get_file_with_ext(ext,no_dot=True)
 		txfile = self.get_file_with_ext(ext,no_dot=True)
 		t = self.spawn('mmgen-txbump', self.eth_args + add_args + ['--yes',txfile])
 		t = self.spawn('mmgen-txbump', self.eth_args + add_args + ['--yes',txfile])
 		t.expect('or gas price: ',fee+'\n')
 		t.expect('or gas price: ',fee+'\n')
@@ -905,7 +905,7 @@ class TestSuiteEthdev(TestSuiteBase,TestSuiteShared):
 
 
 	async def get_tx_receipt(self,txid):
 	async def get_tx_receipt(self,txid):
 		from mmgen.tx import NewTX
 		from mmgen.tx import NewTX
-		tx = await NewTX(proto=self.proto)
+		tx = await NewTX(cfg=cfg,proto=self.proto)
 		tx.rpc = await self.rpc
 		tx.rpc = await self.rpc
 		res = await tx.get_receipt(txid)
 		res = await tx.get_receipt(txid)
 		imsg(f'Gas sent:  {res.gas_sent.hl():<9} {(res.gas_sent*res.gas_price).hl2(encl="()")}')
 		imsg(f'Gas sent:  {res.gas_sent.hl():<9} {(res.gas_sent*res.gas_price).hl2(encl="()")}')
@@ -931,7 +931,7 @@ class TestSuiteEthdev(TestSuiteBase,TestSuiteShared):
 		t = self.spawn( 'mmgen-'+mmgen_cmd, self.eth_args + args)
 		t = self.spawn( 'mmgen-'+mmgen_cmd, self.eth_args + args)
 		if mmgen_cmd == 'txcreate':
 		if mmgen_cmd == 'txcreate':
 			t.written_to_file('transaction')
 			t.written_to_file('transaction')
-			ext = '[0,8000]{}.regtest.rawtx'.format('-α' if g.debug_utf8 else '')
+			ext = '[0,8000]{}.regtest.rawtx'.format('-α' if cfg.debug_utf8 else '')
 			txfile = self.get_file_with_ext(ext,no_dot=True)
 			txfile = self.get_file_with_ext(ext,no_dot=True)
 			t = self.spawn('mmgen-txsign', self.eth_args + ['--yes','-k',keyfile,txfile],no_msg=True)
 			t = self.spawn('mmgen-txsign', self.eth_args + ['--yes','-k',keyfile,txfile],no_msg=True)
 			self.txsign_ui_common(t,ni=True)
 			self.txsign_ui_common(t,ni=True)
@@ -940,7 +940,7 @@ class TestSuiteEthdev(TestSuiteBase,TestSuiteShared):
 
 
 		txid = self.txsend_ui_common(t,
 		txid = self.txsend_ui_common(t,
 			caller = mmgen_cmd,
 			caller = mmgen_cmd,
-			quiet  = mmgen_cmd == 'txdo' or not g.debug,
+			quiet  = mmgen_cmd == 'txdo' or not cfg.debug,
 			bogus_send = False )
 			bogus_send = False )
 		addr = strip_ansi_escapes(t.expect_getend('Contract address: '))
 		addr = strip_ansi_escapes(t.expect_getend('Contract address: '))
 		if (await self.get_tx_receipt(txid)).status == 0:
 		if (await self.get_tx_receipt(txid)).status == 0:
@@ -971,12 +971,12 @@ class TestSuiteEthdev(TestSuiteBase,TestSuiteShared):
 		sid = dfl_sid
 		sid = dfl_sid
 		from mmgen.tool.wallet import tool_cmd
 		from mmgen.tool.wallet import tool_cmd
 		usr_mmaddrs = [f'{sid}:E:{i}' for i in (11,21)]
 		usr_mmaddrs = [f'{sid}:E:{i}' for i in (11,21)]
-		usr_addrs = [tool_cmd(cmdname='gen_addr',proto=self.proto).gen_addr(addr,dfl_words_file) for addr in usr_mmaddrs]
 
 
 		from mmgen.proto.eth.contract import TokenResolve
 		from mmgen.proto.eth.contract import TokenResolve
 		async def do_transfer(rpc):
 		async def do_transfer(rpc):
 			for i in range(2):
 			for i in range(2):
 				tk = await TokenResolve(
 				tk = await TokenResolve(
+					cfg,
 					self.proto,
 					self.proto,
 					rpc,
 					rpc,
 					self.read_from_tmpfile(f'token_addr{i+1}').strip() )
 					self.read_from_tmpfile(f'token_addr{i+1}').strip() )
@@ -998,6 +998,7 @@ class TestSuiteEthdev(TestSuiteBase,TestSuiteShared):
 		async def show_bals(rpc):
 		async def show_bals(rpc):
 			for i in range(2):
 			for i in range(2):
 				tk = await TokenResolve(
 				tk = await TokenResolve(
+					cfg,
 					self.proto,
 					self.proto,
 					rpc,
 					rpc,
 					self.read_from_tmpfile(f'token_addr{i+1}').strip() )
 					self.read_from_tmpfile(f'token_addr{i+1}').strip() )
@@ -1008,7 +1009,11 @@ class TestSuiteEthdev(TestSuiteBase,TestSuiteShared):
 					usr_mmaddrs[i],
 					usr_mmaddrs[i],
 					usr_addrs[i] ))
 					usr_addrs[i] ))
 
 
+		def gen_addr(addr):
+			return tool_cmd(cfg,cmdname='gen_addr',proto=self.proto).gen_addr(addr,dfl_words_file)
+
 		silence()
 		silence()
+		usr_addrs = list(map(gen_addr,usr_mmaddrs))
 		if op == 'show_bals':
 		if op == 'show_bals':
 			await show_bals(await self.rpc)
 			await show_bals(await self.rpc)
 		elif op == 'do_transfer':
 		elif op == 'do_transfer':
@@ -1191,7 +1196,7 @@ class TestSuiteEthdev(TestSuiteBase,TestSuiteShared):
 			add_args = ['98831F3A:E:3,0.4321']):
 			add_args = ['98831F3A:E:3,0.4321']):
 		args = ['--fee=20G','--cached-balances'] + add_args + [dfl_words_file]
 		args = ['--fee=20G','--cached-balances'] + add_args + [dfl_words_file]
 		t = self.txcreate(args=args,acct=acct,caller='txdo',fee_res_data=fee_res_data,no_read=True)
 		t = self.txcreate(args=args,acct=acct,caller='txdo',fee_res_data=fee_res_data,no_read=True)
-		self._do_confirm_send(t,quiet=not g.debug,sure=False)
+		self._do_confirm_send(t,quiet=not cfg.debug,sure=False)
 		return t
 		return t
 
 
 	def txcreate_refresh_balances(self,
 	def txcreate_refresh_balances(self,
@@ -1322,7 +1327,7 @@ class TestSuiteEthdev(TestSuiteBase,TestSuiteShared):
 	async def twmove(self):
 	async def twmove(self):
 		self.spawn('',msg_only=True)
 		self.spawn('',msg_only=True)
 		from mmgen.tw.ctl import TwCtl
 		from mmgen.tw.ctl import TwCtl
-		twctl = await TwCtl(self.proto)
+		twctl = await TwCtl(cfg,self.proto)
 		imsg(f'Moving tracking wallet')
 		imsg(f'Moving tracking wallet')
 		bakfile = twctl.tw_fn + '.bak.json'
 		bakfile = twctl.tw_fn + '.bak.json'
 		if os.path.exists(bakfile):
 		if os.path.exists(bakfile):
@@ -1332,7 +1337,7 @@ class TestSuiteEthdev(TestSuiteBase,TestSuiteShared):
 
 
 	def twimport(self,add_args=[],expect_str=None):
 	def twimport(self,add_args=[],expect_str=None):
 		from mmgen.tw.json import TwJSON
 		from mmgen.tw.json import TwJSON
-		fn = joinpath( self.tmpdir, TwJSON.Base(self.proto).dump_fn )
+		fn = joinpath( self.tmpdir, TwJSON.Base(cfg,self.proto).dump_fn )
 		t = self.spawn('mmgen-tool',self.eth_args_noquiet + ['twimport',fn] + add_args)
 		t = self.spawn('mmgen-tool',self.eth_args_noquiet + ['twimport',fn] + add_args)
 		t.expect('(y/N): ','y')
 		t.expect('(y/N): ','y')
 		if expect_str:
 		if expect_str:
@@ -1346,7 +1351,7 @@ class TestSuiteEthdev(TestSuiteBase,TestSuiteShared):
 	def tw_chktotal(self):
 	def tw_chktotal(self):
 		self.spawn('',msg_only=True)
 		self.spawn('',msg_only=True)
 		from mmgen.tw.json import TwJSON
 		from mmgen.tw.json import TwJSON
-		fn = joinpath( self.tmpdir, TwJSON.Base(self.proto).dump_fn )
+		fn = joinpath( self.tmpdir, TwJSON.Base(cfg,self.proto).dump_fn )
 		res = json.loads(read_from_file(fn))
 		res = json.loads(read_from_file(fn))
 		cmp_or_die( res['data']['value'], vbal6, 'value in tracking wallet JSON dump' )
 		cmp_or_die( res['data']['value'], vbal6, 'value in tracking wallet JSON dump' )
 		return 'ok'
 		return 'ok'
@@ -1354,7 +1359,7 @@ class TestSuiteEthdev(TestSuiteBase,TestSuiteShared):
 	async def twcompare(self):
 	async def twcompare(self):
 		self.spawn('',msg_only=True)
 		self.spawn('',msg_only=True)
 		from mmgen.tw.ctl import TwCtl
 		from mmgen.tw.ctl import TwCtl
-		twctl = await TwCtl(self.proto)
+		twctl = await TwCtl(cfg,self.proto)
 		fn = twctl.tw_fn
 		fn = twctl.tw_fn
 		imsg(f'Comparing imported tracking wallet with original')
 		imsg(f'Comparing imported tracking wallet with original')
 		data = [json.dumps(json.loads(read_from_file(f)),sort_keys=True) for f in (fn,fn+'.bak.json')]
 		data = [json.dumps(json.loads(read_from_file(f)),sort_keys=True) for f in (fn,fn+'.bak.json')]
@@ -1364,7 +1369,7 @@ class TestSuiteEthdev(TestSuiteBase,TestSuiteShared):
 	def edit_json_twdump(self):
 	def edit_json_twdump(self):
 		self.spawn('',msg_only=True)
 		self.spawn('',msg_only=True)
 		from mmgen.tw.json import TwJSON
 		from mmgen.tw.json import TwJSON
-		fn = TwJSON.Base(self.proto).dump_fn
+		fn = TwJSON.Base(cfg,self.proto).dump_fn
 		text = json.loads(self.read_from_tmpfile(fn))
 		text = json.loads(self.read_from_tmpfile(fn))
 		token_addr = self.read_from_tmpfile('token_addr2').strip()
 		token_addr = self.read_from_tmpfile('token_addr2').strip()
 		text['data']['entries']['tokens'][token_addr][2][3] = f'edited comment [фубар] [{gr_uc}]'
 		text['data']['entries']['tokens'][token_addr][2][3] = f'edited comment [фубар] [{gr_uc}]'
@@ -1373,7 +1378,7 @@ class TestSuiteEthdev(TestSuiteBase,TestSuiteShared):
 
 
 	def stop(self):
 	def stop(self):
 		self.spawn('',msg_only=True)
 		self.spawn('',msg_only=True)
-		if not opt.no_daemon_stop:
+		if not cfg.no_daemon_stop:
 			if not stop_test_daemons(self.proto.coin+'_rt'):
 			if not stop_test_daemons(self.proto.coin+'_rt'):
 				return False
 				return False
 		set_vt100()
 		set_vt100()

+ 3 - 3
test/test_py_d/ts_input.py

@@ -352,7 +352,7 @@ class TestSuiteInput(TestSuiteBase):
 		mn = mn or sample_mn[fmt]['mn'].split()
 		mn = mn or sample_mn[fmt]['mn'].split()
 		t = self.spawn('mmgen-tool',['mn2hex_interactive','fmt='+fmt,'mn_len=12','print_mn=1'])
 		t = self.spawn('mmgen-tool',['mn2hex_interactive','fmt='+fmt,'mn_len=12','print_mn=1'])
 		from mmgen.mn_entry import mn_entry
 		from mmgen.mn_entry import mn_entry
-		mne = mn_entry(fmt,entry_mode)
+		mne = mn_entry( cfg, fmt, entry_mode )
 		t.expect(
 		t.expect(
 			'Type a number.*: ',
 			'Type a number.*: ',
 			('\n' if enter_for_dfl else str(mne.entry_modes.index(entry_mode)+1)),
 			('\n' if enter_for_dfl else str(mne.entry_modes.index(entry_mode)+1)),
@@ -381,7 +381,7 @@ class TestSuiteInput(TestSuiteBase):
 			t.expect('Type a number.*: ','6',regex=True)
 			t.expect('Type a number.*: ','6',regex=True)
 			t.expect('invalid')
 			t.expect('invalid')
 			from mmgen.mn_entry import mn_entry
 			from mmgen.mn_entry import mn_entry
-			mne = mn_entry(fmt,entry_mode)
+			mne = mn_entry( cfg, fmt, entry_mode )
 			t.expect('Type a number.*: ',str(mne.entry_modes.index(entry_mode)+1),regex=True)
 			t.expect('Type a number.*: ',str(mne.entry_modes.index(entry_mode)+1),regex=True)
 			t.expect('Using (.+) entry mode',regex=True)
 			t.expect('Using (.+) entry mode',regex=True)
 			mode = strip_ansi_escapes(t.p.match.group(1)).lower()
 			mode = strip_ansi_escapes(t.p.match.group(1)).lower()
@@ -405,7 +405,7 @@ class TestSuiteInput(TestSuiteBase):
 	def mnemonic_entry_mmgen_minimal(self):
 	def mnemonic_entry_mmgen_minimal(self):
 		from mmgen.mn_entry import mn_entry
 		from mmgen.mn_entry import mn_entry
 		# erase_chars: '\b\x7f'
 		# erase_chars: '\b\x7f'
-		m = mn_entry('mmgen','minimal')
+		m = mn_entry( cfg, 'mmgen', 'minimal' )
 		np = 2
 		np = 2
 		mn = (
 		mn = (
 			'z',
 			'z',

+ 26 - 25
test/test_py_d/ts_main.py

@@ -20,8 +20,6 @@
 test.test_py_d.ts_main: Basic operations tests for the test.py test suite
 test.test_py_d.ts_main: Basic operations tests for the test.py test suite
 """
 """
 
 
-from mmgen.globalvars import g
-from mmgen.opts import opt
 from mmgen.fileutil import get_data_from_file,write_data_to_file
 from mmgen.fileutil import get_data_from_file,write_data_to_file
 from mmgen.wallet import get_wallet_cls
 from mmgen.wallet import get_wallet_cls
 from mmgen.wallet.mmgen import wallet as MMGenWallet
 from mmgen.wallet.mmgen import wallet as MMGenWallet
@@ -41,9 +39,9 @@ def make_brainwallet_file(fn):
 		return ''.join([ws_list[getrandnum_range(1,200) % len(ws_list)] for i in range(nchars)])
 		return ''.join([ws_list[getrandnum_range(1,200) % len(ws_list)] for i in range(nchars)])
 	rand_pairs = [wl[getrandnum_range(1,200) % len(wl)] + rand_ws_seq() for i in range(nwords)]
 	rand_pairs = [wl[getrandnum_range(1,200) % len(wl)] + rand_ws_seq() for i in range(nwords)]
 	d = ''.join(rand_pairs).rstrip() + '\n'
 	d = ''.join(rand_pairs).rstrip() + '\n'
-	if opt.verbose:
+	if cfg.verbose:
 		msg_r(f'Brainwallet password:\n{cyan(d)}')
 		msg_r(f'Brainwallet password:\n{cyan(d)}')
-	write_data_to_file(fn,d,'brainwallet password',quiet=True,ignore_opt_outdir=True)
+	write_data_to_file(cfg,fn,d,'brainwallet password',quiet=True,ignore_opt_outdir=True)
 
 
 def verify_checksum_or_exit(checksum,chk):
 def verify_checksum_or_exit(checksum,chk):
 	chk = strip_ansi_escapes(chk)
 	chk = strip_ansi_escapes(chk)
@@ -194,7 +192,7 @@ class TestSuiteMain(TestSuiteBase,TestSuiteShared):
 		TestSuiteBase.__init__(self,trunner,cfgs,spawn)
 		TestSuiteBase.__init__(self,trunner,cfgs,spawn)
 		if trunner == None or self.proto.coin.lower() not in self.networks:
 		if trunner == None or self.proto.coin.lower() not in self.networks:
 			return
 			return
-		self.rpc = async_run(rpc_init(self.proto))
+		self.rpc = async_run(rpc_init(cfg,self.proto))
 		self.lbl_id = ('account','label')['label_api' in self.rpc.caps]
 		self.lbl_id = ('account','label')['label_api' in self.rpc.caps]
 		if self.proto.coin in ('BTC','BCH','LTC'):
 		if self.proto.coin in ('BTC','BCH','LTC'):
 			self.tx_fee     = {'btc':'0.0001','bch':'0.001','ltc':'0.01'}[self.proto.coin.lower()]
 			self.tx_fee     = {'btc':'0.0001','bch':'0.001','ltc':'0.01'}[self.proto.coin.lower()]
@@ -207,9 +205,9 @@ class TestSuiteMain(TestSuiteBase,TestSuiteShared):
 		addrfile = self.get_file_with_ext('addrs')
 		addrfile = self.get_file_with_ext('addrs')
 		from mmgen.addrlist import AddrList
 		from mmgen.addrlist import AddrList
 		silence()
 		silence()
-		chk = AddrList( self.proto, addrfile ).chksum
+		chk = AddrList( cfg, self.proto, addrfile ).chksum
 		end_silence()
 		end_silence()
-		if opt.verbose and display:
+		if cfg.verbose and display:
 			msg(f'Checksum: {cyan(chk)}')
 			msg(f'Checksum: {cyan(chk)}')
 		return chk
 		return chk
 
 
@@ -239,10 +237,10 @@ class TestSuiteMain(TestSuiteBase,TestSuiteShared):
 
 
 	def delete_dfl_wallet(self,pf):
 	def delete_dfl_wallet(self,pf):
 		self.write_to_tmpfile('del_dw_run',b'',binary=True)
 		self.write_to_tmpfile('del_dw_run',b'',binary=True)
-		if opt.no_dw_delete:
+		if cfg.no_dw_delete:
 			return 'skip'
 			return 'skip'
-		for wf in [f for f in os.listdir(g.data_dir) if f[-6:]=='.mmdat']:
-			os.unlink(joinpath(g.data_dir,wf))
+		for wf in [f for f in os.listdir(cfg.data_dir) if f[-6:]=='.mmdat']:
+			os.unlink(joinpath(cfg.data_dir,wf))
 		self.spawn('',msg_only=True)
 		self.spawn('',msg_only=True)
 		self.have_dfl_wallet = False
 		self.have_dfl_wallet = False
 		return 'ok'
 		return 'ok'
@@ -294,7 +292,7 @@ class TestSuiteMain(TestSuiteBase,TestSuiteShared):
 
 
 	def passchg(self,wf,pf,label_action='cmdline',dfl_wallet=False,delete=False):
 	def passchg(self,wf,pf,label_action='cmdline',dfl_wallet=False,delete=False):
 		silence()
 		silence()
-		self.write_to_tmpfile(pwfile,get_data_from_file(pf))
+		self.write_to_tmpfile( pwfile, get_data_from_file(cfg,pf) )
 		end_silence()
 		end_silence()
 		add_args = {'cmdline': ['-d',self.tmpdir,'-L','Changed label (UTF-8) α'],
 		add_args = {'cmdline': ['-d',self.tmpdir,'-L','Changed label (UTF-8) α'],
 					'keep':    ['-d',self.tr.trash_dir,'--keep-label'],
 					'keep':    ['-d',self.tr.trash_dir,'--keep-label'],
@@ -336,8 +334,8 @@ class TestSuiteMain(TestSuiteBase,TestSuiteShared):
 		return self.walletchk(wf,pf,wcls=wcls,dfl_wallet=dfl_wallet)
 		return self.walletchk(wf,pf,wcls=wcls,dfl_wallet=dfl_wallet)
 
 
 	def _write_fake_data_to_file(self,d):
 	def _write_fake_data_to_file(self,d):
-		write_data_to_file(self.unspent_data_file,d,'Unspent outputs',quiet=True,ignore_opt_outdir=True)
-		if opt.verbose or opt.exact_output:
+		write_data_to_file(cfg,self.unspent_data_file,d,'Unspent outputs',quiet=True,ignore_opt_outdir=True)
+		if cfg.verbose or cfg.exact_output:
 			sys.stderr.write(f'Fake transaction wallet data written to file {self.unspent_data_file!r}\n')
 			sys.stderr.write(f'Fake transaction wallet data written to file {self.unspent_data_file!r}\n')
 
 
 	def _create_fake_unspent_entry(self,coinaddr,al_id=None,idx=None,comment=None,non_mmgen=False,segwit=False):
 	def _create_fake_unspent_entry(self,coinaddr,al_id=None,idx=None,comment=None,non_mmgen=False,segwit=False):
@@ -373,7 +371,7 @@ class TestSuiteMain(TestSuiteBase,TestSuiteShared):
 		for d in tx_data.values():
 		for d in tx_data.values():
 			al = adata.addrlist(al_id=d['al_id'])
 			al = adata.addrlist(al_id=d['al_id'])
 			for n,(idx,coinaddr) in enumerate(al.addrpairs()):
 			for n,(idx,coinaddr) in enumerate(al.addrpairs()):
-				comment = get_comment(do_shuffle=not g.test_suite_deterministic)
+				comment = get_comment(do_shuffle=not cfg.test_suite_deterministic)
 				out.append(self._create_fake_unspent_entry(coinaddr,d['al_id'],idx,comment,segwit=d['segwit']))
 				out.append(self._create_fake_unspent_entry(coinaddr,d['al_id'],idx,comment,segwit=d['segwit']))
 				if n == 0:  # create a duplicate address. This means addrs_per_wallet += 1
 				if n == 0:  # create a duplicate address. This means addrs_per_wallet += 1
 					out.append(self._create_fake_unspent_entry(coinaddr,d['al_id'],idx,comment,segwit=d['segwit']))
 					out.append(self._create_fake_unspent_entry(coinaddr,d['al_id'],idx,comment,segwit=d['segwit']))
@@ -387,11 +385,13 @@ class TestSuiteMain(TestSuiteBase,TestSuiteShared):
 				pubkey_type = 'std' )
 				pubkey_type = 'std' )
 			from mmgen.addrgen import KeyGenerator,AddrGenerator
 			from mmgen.addrgen import KeyGenerator,AddrGenerator
 			rand_coinaddr = AddrGenerator(
 			rand_coinaddr = AddrGenerator(
+				cfg,
 				self.proto,
 				self.proto,
 				('legacy','compressed')[non_mmgen_input_compressed]
 				('legacy','compressed')[non_mmgen_input_compressed]
-				).to_addr(KeyGenerator(self.proto,'std').gen_data(privkey))
+				).to_addr(KeyGenerator( cfg, self.proto, 'std' ).gen_data(privkey))
 			of = joinpath(self.cfgs[non_mmgen_input]['tmpdir'],non_mmgen_fn)
 			of = joinpath(self.cfgs[non_mmgen_input]['tmpdir'],non_mmgen_fn)
 			write_data_to_file(
 			write_data_to_file(
+				cfg               = cfg,
 				outfile           = of,
 				outfile           = of,
 				data              = privkey.wif + '\n',
 				data              = privkey.wif + '\n',
 				desc              = f'compressed {self.proto.name} key',
 				desc              = f'compressed {self.proto.name} key',
@@ -406,14 +406,14 @@ class TestSuiteMain(TestSuiteBase,TestSuiteShared):
 		from mmgen.addrdata import AddrData
 		from mmgen.addrdata import AddrData
 		tx_data,ad = {},AddrData(self.proto)
 		tx_data,ad = {},AddrData(self.proto)
 		for s in sources:
 		for s in sources:
-			afile = get_file_with_ext(self.cfgs[s]['tmpdir'],'addrs')
-			al = AddrList(self.proto,afile)
+			addrfile = get_file_with_ext(self.cfgs[s]['tmpdir'],'addrs')
+			al = AddrList( cfg, self.proto, addrfile )
 			ad.add(al)
 			ad.add(al)
 			aix = AddrIdxList(fmt_str=self.cfgs[s]['addr_idx_list'])
 			aix = AddrIdxList(fmt_str=self.cfgs[s]['addr_idx_list'])
 			if len(aix) != addrs_per_wallet:
 			if len(aix) != addrs_per_wallet:
 				die( 'TestSuiteFatalException', f'Address index list length != {addrs_per_wallet}: {repr(aix)}' )
 				die( 'TestSuiteFatalException', f'Address index list length != {addrs_per_wallet}: {repr(aix)}' )
 			tx_data[s] = {
 			tx_data[s] = {
-				'addrfile': afile,
+				'addrfile': addrfile,
 				'chk': al.chksum,
 				'chk': al.chksum,
 				'al_id': al.al_id,
 				'al_id': al.al_id,
 				'addr_idxs': aix[-2:],
 				'addr_idxs': aix[-2:],
@@ -426,8 +426,8 @@ class TestSuiteMain(TestSuiteBase,TestSuiteShared):
 		privkey = PrivKey(self.proto,getrand(32),compressed=True,pubkey_type='std')
 		privkey = PrivKey(self.proto,getrand(32),compressed=True,pubkey_type='std')
 		t = ('compressed','segwit')['S' in self.proto.mmtypes]
 		t = ('compressed','segwit')['S' in self.proto.mmtypes]
 		from mmgen.addrgen import KeyGenerator,AddrGenerator
 		from mmgen.addrgen import KeyGenerator,AddrGenerator
-		rand_coinaddr = AddrGenerator(self.proto,t).to_addr(
-			KeyGenerator(self.proto,'std').gen_data(privkey)
+		rand_coinaddr = AddrGenerator( cfg, self.proto, t ).to_addr(
+			KeyGenerator( cfg, self.proto, 'std' ).gen_data(privkey)
 		)
 		)
 
 
 		# total of two outputs must be < 10 BTC (<1000 LTC)
 		# total of two outputs must be < 10 BTC (<1000 LTC)
@@ -472,7 +472,7 @@ class TestSuiteMain(TestSuiteBase,TestSuiteShared):
 						cmdline_inputs             = False,
 						cmdline_inputs             = False,
 						tweaks                     = [] ):
 						tweaks                     = [] ):
 
 
-		if opt.verbose or opt.exact_output:
+		if cfg.verbose or cfg.exact_output:
 			sys.stderr.write(green('Generating fake tracking wallet info\n'))
 			sys.stderr.write(green('Generating fake tracking wallet info\n'))
 
 
 		silence()
 		silence()
@@ -496,7 +496,7 @@ class TestSuiteMain(TestSuiteBase,TestSuiteShared):
 
 
 		end_silence()
 		end_silence()
 
 
-		if opt.verbose or opt.exact_output:
+		if cfg.verbose or cfg.exact_output:
 			sys.stderr.write('\n')
 			sys.stderr.write('\n')
 
 
 		t = self.spawn(
 		t = self.spawn(
@@ -557,8 +557,8 @@ class TestSuiteMain(TestSuiteBase,TestSuiteShared):
 			t.do_comment(False,has_label=True)
 			t.do_comment(False,has_label=True)
 			for cnum,wcls in (('1',IncogWallet),('3',MMGenWallet),('4',MMGenWallet)):
 			for cnum,wcls in (('1',IncogWallet),('3',MMGenWallet),('4',MMGenWallet)):
 				t.passphrase(wcls.desc,self.cfgs[cnum]['wpasswd'])
 				t.passphrase(wcls.desc,self.cfgs[cnum]['wpasswd'])
-			self._do_confirm_send(t,quiet=not g.debug,confirm_send=True)
-			if g.debug:
+			self._do_confirm_send(t,quiet=not cfg.debug,confirm_send=True)
+			if cfg.debug:
 				t.written_to_file('Transaction')
 				t.written_to_file('Transaction')
 		else:
 		else:
 			t.do_comment(False)
 			t.do_comment(False)
@@ -617,7 +617,8 @@ class TestSuiteMain(TestSuiteBase,TestSuiteShared):
 		wcls = get_wallet_cls(fmt_code=out_fmt)
 		wcls = get_wallet_cls(fmt_code=out_fmt)
 		msg('==> {}: {}'.format(
 		msg('==> {}: {}'.format(
 			wcls.desc,
 			wcls.desc,
-			cyan(get_data_from_file(f,wcls.desc)) ))
+			cyan(get_data_from_file( cfg, f, wcls.desc ))
+		))
 		end_silence()
 		end_silence()
 		return t
 		return t
 
 

+ 3 - 3
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
 test.test_py_d.ts_misc: Miscellaneous test groups for the test.py test suite
 """
 """
 
 
-from mmgen.globalvars import g,gc
+from mmgen.globalvars import gc
 from ..include.common import *
 from ..include.common import *
 from .common import *
 from .common import *
 from .ts_base import *
 from .ts_base import *
@@ -41,7 +41,7 @@ class TestSuiteMisc(TestSuiteBase):
 	color = True
 	color = True
 
 
 	def rpc_backends(self):
 	def rpc_backends(self):
-		backends = g.autoset_opts['rpc_backend'][1]
+		backends = cfg.autoset_opts['rpc_backend'][1]
 		for b in backends:
 		for b in backends:
 			t = self.spawn_chk('mmgen-tool',[f'--rpc-backend={b}','daemon_version'],extra_desc=f'({b})')
 			t = self.spawn_chk('mmgen-tool',[f'--rpc-backend={b}','daemon_version'],extra_desc=f'({b})')
 		return t
 		return t
@@ -76,7 +76,7 @@ class TestSuiteMisc(TestSuiteBase):
 
 
 		t = self.spawn('test/misc/term_ni.py',['echo'],cmd_dir='.',pexpect_spawn=True,timeout=1)
 		t = self.spawn('test/misc/term_ni.py',['echo'],cmd_dir='.',pexpect_spawn=True,timeout=1)
 		t.p.logfile = None
 		t.p.logfile = None
-		t.p.logfile_read = sys.stdout if opt.verbose or opt.exact_output else None
+		t.p.logfile_read = sys.stdout if cfg.verbose or cfg.exact_output else None
 		t.p.logfile_send = None
 		t.p.logfile_send = None
 
 
 		test_noecho()
 		test_noecho()

+ 17 - 20
test/test_py_d/ts_opts.py

@@ -45,7 +45,7 @@ class TestSuiteOpts(TestSuiteBase):
 
 
 	def opt_helpscreen(self):
 	def opt_helpscreen(self):
 		expect = r'OPTS.PY: Opts test.*USAGE:\s+opts.py'
 		expect = r'OPTS.PY: Opts test.*USAGE:\s+opts.py'
-		if not opt.pexpect_spawn:
+		if not cfg.pexpect_spawn:
 			expect += r'.*--minconf.*NOTES FOR THIS.*a note'
 			expect += r'.*--minconf.*NOTES FOR THIS.*a note'
 		t = self.do_run( ['--help'], expect, 0, regex=True )
 		t = self.do_run( ['--help'], expect, 0, regex=True )
 		if t.pexpect_spawn:
 		if t.pexpect_spawn:
@@ -57,17 +57,15 @@ class TestSuiteOpts(TestSuiteBase):
 		return self.check_vals(
 		return self.check_vals(
 				[],
 				[],
 				(
 				(
-					('opt.foo',               'None'),         # added opt
-					('opt.print_checksum',    'None'),         # sets 'quiet'
-					('opt.quiet',             'False'),        # init_opts, incompatible_opts
-					('opt.verbose',           'None'),         # init_opts, incompatible_opts
-					('opt.fee_estimate_mode', 'conservative'), # autoset_opts
-					('opt.passwd_file',       'None'),         # infile_opts - check_infile()
-					('opt.outdir',            'None'),         # check_outdir()
-					('opt.cached_balances',   'None'),         # opt_sets_global
-					('opt.minconf',           '1'),            # global_sets_opt
-					('g.cached_balances',     'None'),
-					('g.minconf',             '1'),
+					('cfg.foo',                 'None'),         # added opt
+					('cfg.print_checksum',      'None'),         # sets 'quiet'
+					('cfg.quiet',               'False'),        # init_opts, incompatible_opts
+					('cfg.verbose',             'False'),        # init_opts, incompatible_opts
+					('cfg.passwd_file',         ''),             # infile_opts - check_infile()
+					('cfg.outdir',              ''),             # check_outdir()
+					('cfg.cached_balances',     'False'),
+					('cfg.minconf',             '1'),
+					('cfg.fee_estimate_mode',   'conservative'), # autoset_opts
 				)
 				)
 			)
 			)
 
 
@@ -85,14 +83,13 @@ class TestSuiteOpts(TestSuiteBase):
 					f'--hidden-incog-input-params={pf},123',
 					f'--hidden-incog-input-params={pf},123',
 				],
 				],
 				(
 				(
-					('opt.print_checksum',    'True'),
-					('opt.quiet',             'True'), # set by print_checksum
-					('opt.fee_estimate_mode', 'economical'),
-					('opt.passwd_file',       pf),
-					('opt.outdir',            self.tmpdir),
-					('opt.cached_balances',   'True'),
-					('opt.hidden_incog_input_params', pf+',123'),
-					('g.cached_balances',     'True'),
+					('cfg.print_checksum',           'True'),
+					('cfg.quiet',                    'True'), # set by print_checksum
+					('cfg.passwd_file',              pf),
+					('cfg.outdir',                   self.tmpdir),
+					('cfg.cached_balances',          'True'),
+					('cfg.hidden_incog_input_params', pf+',123'),
+					('cfg.fee_estimate_mode',         'economical'),
 				)
 				)
 			)
 			)
 
 

+ 2 - 4
test/test_py_d/ts_ref.py

@@ -21,8 +21,6 @@ test.test_py_d.ts_ref: Reference file tests for the test.py test suite
 """
 """
 
 
 import os
 import os
-from mmgen.globalvars import g
-from mmgen.opts import opt
 from mmgen.wallet import get_wallet_cls
 from mmgen.wallet import get_wallet_cls
 from ..include.common import *
 from ..include.common import *
 from .common import *
 from .common import *
@@ -282,11 +280,11 @@ class TestSuiteRef(TestSuiteBase,TestSuiteShared):
 
 
 	def ref_tool_decrypt(self):
 	def ref_tool_decrypt(self):
 		f = joinpath(ref_dir,ref_enc_fn)
 		f = joinpath(ref_dir,ref_enc_fn)
-		if not g.debug_utf8:
+		if not cfg.debug_utf8:
 			disable_debug()
 			disable_debug()
 		dec_file = joinpath(self.tmpdir,'famous.txt')
 		dec_file = joinpath(self.tmpdir,'famous.txt')
 		t = self.spawn('mmgen-tool', ['-q','decrypt',f,'outfile='+dec_file,'hash_preset=1'])
 		t = self.spawn('mmgen-tool', ['-q','decrypt',f,'outfile='+dec_file,'hash_preset=1'])
-		if not g.debug_utf8:
+		if not cfg.debug_utf8:
 			restore_debug()
 			restore_debug()
 		t.passphrase('data',tool_enc_passwd)
 		t.passphrase('data',tool_enc_passwd)
 		t.written_to_file('Decrypted data')
 		t.written_to_file('Decrypted data')

+ 4 - 6
test/test_py_d/ts_ref_3seed.py

@@ -21,8 +21,6 @@ test.test_py_d.ts_ref_3seed: Saved and generated reference file tests for 128,
                              192 and 256-bit seeds for the test.py test suite
                              192 and 256-bit seeds for the test.py test suite
 """
 """
 
 
-from mmgen.globalvars import g
-from mmgen.opts import opt
 from mmgen.wallet import get_wallet_cls
 from mmgen.wallet import get_wallet_cls
 from ..include.common import *
 from ..include.common import *
 from .common import *
 from .common import *
@@ -121,7 +119,7 @@ class TestSuiteRef3Seed(TestSuiteBase,TestSuiteShared):
 			]
 			]
 			slarg = [f'-l{self.seed_len} ']
 			slarg = [f'-l{self.seed_len} ']
 			hparg = ['-p1']
 			hparg = ['-p1']
-			if wtype == 'hic_wallet_old' and opt.profile:
+			if wtype == 'hic_wallet_old' and cfg.profile:
 				msg('')
 				msg('')
 			t = self.spawn('mmgen-walletchk',
 			t = self.spawn('mmgen-walletchk',
 				slarg + hparg + of_arg + ic_arg,
 				slarg + hparg + of_arg + ic_arg,
@@ -155,7 +153,7 @@ class TestSuiteRef3Seed(TestSuiteBase,TestSuiteShared):
 		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['sids'][idx],
 			self.chk_data['lens'][idx],
 			self.chk_data['lens'][idx],
-			'-α' if g.debug_utf8 else '')
+			'-α' if cfg.debug_utf8 else '')
 		assert re.match(pat,fn), f'{pat} != {fn}'
 		assert re.match(pat,fn), f'{pat} != {fn}'
 		sid = os.path.basename(fn.split('-')[0])
 		sid = os.path.basename(fn.split('-')[0])
 		cmp_or_die(sid,self.seed_id,desc='Seed ID')
 		cmp_or_die(sid,self.seed_id,desc='Seed ID')
@@ -178,7 +176,7 @@ class TestSuiteRef3Seed(TestSuiteBase,TestSuiteShared):
 			cmp_or_die('{}[{}]{}.{}'.format(
 			cmp_or_die('{}[{}]{}.{}'.format(
 				sid,
 				sid,
 				slen,
 				slen,
-				'-α' if g.debug_utf8 else '',
+				'-α' if cfg.debug_utf8 else '',
 				wcls.ext),
 				wcls.ext),
 				fn )
 				fn )
 		return t
 		return t
@@ -192,7 +190,7 @@ class TestSuiteRef3Seed(TestSuiteBase,TestSuiteShared):
 
 
 	def ref_walletconv_incog(self,ofmt='incog',ext='mmincog'):
 	def ref_walletconv_incog(self,ofmt='incog',ext='mmincog'):
 		args = ['-r0','-p1']
 		args = ['-r0','-p1']
-		pat = r'{}-[0-9A-F]{{8}}-[0-9A-F]{{8}}\[{},1\]' + ('-α' if g.debug_utf8 else '') + '.' + ext
+		pat = r'{}-[0-9A-F]{{8}}-[0-9A-F]{{8}}\[{},1\]' + ('-α' if cfg.debug_utf8 else '') + '.' + ext
 		return self.ref_walletconv(ofmt=ofmt,extra_args=args,re_pat=pat)
 		return self.ref_walletconv(ofmt=ofmt,extra_args=args,re_pat=pat)
 
 
 	def ref_walletconv_hexincog(self):
 	def ref_walletconv_hexincog(self):

+ 2 - 3
test/test_py_d/ts_ref_altcoin.py

@@ -21,9 +21,8 @@ test.test_py_d.ts_ref_altcoin: Altcoin reference file tests for the test.py test
 """
 """
 
 
 import os
 import os
-from mmgen.globalvars import g
-from mmgen.opts import opt
 from .common import *
 from .common import *
+from ..include.common import cfg
 from .ts_ref import *
 from .ts_ref import *
 from .ts_base import *
 from .ts_base import *
 
 
@@ -101,7 +100,7 @@ class TestSuiteRefAltcoin(TestSuiteRef,TestSuiteBase):
 					ref_dir,
 					ref_dir,
 					self._get_ref_subdir_by_coin(coin),
 					self._get_ref_subdir_by_coin(coin),
 					fn )
 					fn )
-				proto = MMGenTxFile.get_proto(txfile,quiet_open=True)
+				proto = MMGenTxFile.get_proto(cfg,txfile,quiet_open=True)
 				if proto.sign_mode == 'daemon':
 				if proto.sign_mode == 'daemon':
 					start_test_daemons(proto.network_id)
 					start_test_daemons(proto.network_id)
 					set_vt100()
 					set_vt100()

+ 24 - 25
test/test_py_d/ts_regtest.py

@@ -22,8 +22,6 @@ test.test_py_d.ts_regtest: Regtest tests for the test.py test suite
 
 
 import os,json,time
 import os,json,time
 from decimal import Decimal
 from decimal import Decimal
-from mmgen.globalvars import g
-from mmgen.opts import opt
 from mmgen.util import die,gmsg
 from mmgen.util import die,gmsg
 from mmgen.protocol import init_proto
 from mmgen.protocol import init_proto
 from mmgen.addrlist import AddrList
 from mmgen.addrlist import AddrList
@@ -135,6 +133,7 @@ rt_data = {
 def make_burn_addr(proto):
 def make_burn_addr(proto):
 	from mmgen.tool.coin import tool_cmd
 	from mmgen.tool.coin import tool_cmd
 	return tool_cmd(
 	return tool_cmd(
+		cfg     = cfg,
 		cmdname = 'pubhash2addr',
 		cmdname = 'pubhash2addr',
 		proto   = proto,
 		proto   = proto,
 		mmtype  = 'compressed' ).pubhash2addr('00'*20)
 		mmtype  = 'compressed' ).pubhash2addr('00'*20)
@@ -416,7 +415,7 @@ class TestSuiteRegtest(TestSuiteBase,TestSuiteShared):
 			return
 			return
 		if self.proto.testnet:
 		if self.proto.testnet:
 			die(2,'--testnet and --regtest options incompatible with regtest test suite')
 			die(2,'--testnet and --regtest options incompatible with regtest test suite')
-		self.proto = init_proto(self.proto.coin,network='regtest',need_amt=True)
+		self.proto = init_proto( cfg, self.proto.coin, network='regtest', need_amt=True )
 		coin = self.proto.coin.lower()
 		coin = self.proto.coin.lower()
 
 
 		import test.test_py_d.ts_regtest as rt_mod
 		import test.test_py_d.ts_regtest as rt_mod
@@ -425,7 +424,7 @@ class TestSuiteRegtest(TestSuiteBase,TestSuiteShared):
 
 
 		if self.proto.coin == 'BTC':
 		if self.proto.coin == 'BTC':
 			self.test_rbf = True # tests are non-coin-dependent, so run just once for BTC
 			self.test_rbf = True # tests are non-coin-dependent, so run just once for BTC
-			if g.test_suite_deterministic:
+			if cfg.test_suite_deterministic:
 				self.deterministic = True
 				self.deterministic = True
 				self.miner_addr = 'bcrt1qaq8t3pakcftpk095tnqfv5cmmczysls024atnd' # regtest.create_hdseed()
 				self.miner_addr = 'bcrt1qaq8t3pakcftpk095tnqfv5cmmczysls024atnd' # regtest.create_hdseed()
 				self.miner_wif = 'cTyMdQ2BgfAsjopRVZrj7AoEGp97pKfrC2NkqLuwHr4KHfPNAKwp'
 				self.miner_wif = 'cTyMdQ2BgfAsjopRVZrj7AoEGp97pKfrC2NkqLuwHr4KHfPNAKwp'
@@ -443,7 +442,7 @@ class TestSuiteRegtest(TestSuiteBase,TestSuiteShared):
 	def _add_comments_to_addr_file(self,addrfile,outfile,use_comments=False):
 	def _add_comments_to_addr_file(self,addrfile,outfile,use_comments=False):
 		silence()
 		silence()
 		gmsg(f'Adding comments to address file {addrfile!r}')
 		gmsg(f'Adding comments to address file {addrfile!r}')
-		a = AddrList(self.proto,addrfile)
+		a = AddrList( cfg, self.proto, addrfile )
 		for n,idx in enumerate(a.idxs(),1):
 		for n,idx in enumerate(a.idxs(),1):
 			if use_comments:
 			if use_comments:
 				a.set_comment(idx,get_comment())
 				a.set_comment(idx,get_comment())
@@ -452,7 +451,7 @@ class TestSuiteRegtest(TestSuiteBase,TestSuiteShared):
 		af = a.get_file()
 		af = a.get_file()
 		af.format(add_comments=True)
 		af.format(add_comments=True)
 		from mmgen.fileutil import write_data_to_file
 		from mmgen.fileutil import write_data_to_file
-		write_data_to_file(outfile,af.fmt_data,quiet=True,ignore_opt_outdir=True)
+		write_data_to_file(cfg,outfile,af.fmt_data,quiet=True,ignore_opt_outdir=True)
 		end_silence()
 		end_silence()
 
 
 	def setup(self):
 	def setup(self):
@@ -499,7 +498,7 @@ class TestSuiteRegtest(TestSuiteBase,TestSuiteShared):
 	def _get_user_subsid(self,user,subseed_idx):
 	def _get_user_subsid(self,user,subseed_idx):
 		fn = get_file_with_ext(self._user_dir(user),dfl_wcls.ext)
 		fn = get_file_with_ext(self._user_dir(user),dfl_wcls.ext)
 		silence()
 		silence()
-		w = Wallet( fn=fn, passwd_file=os.path.join(self.tmpdir,'wallet_password') )
+		w = Wallet( cfg, fn=fn, passwd_file=os.path.join(self.tmpdir,'wallet_password') )
 		end_silence()
 		end_silence()
 		return w.seed.subseed(subseed_idx).sid
 		return w.seed.subseed(subseed_idx).sid
 
 
@@ -530,7 +529,7 @@ class TestSuiteRegtest(TestSuiteBase,TestSuiteShared):
 			addrfile = joinpath(self._user_dir(user),
 			addrfile = joinpath(self._user_dir(user),
 				'{}{}{}[{}]{x}.regtest.addrs'.format(
 				'{}{}{}[{}]{x}.regtest.addrs'.format(
 					sid,self.altcoin_pfx,id_strs[desc],addr_range,
 					sid,self.altcoin_pfx,id_strs[desc],addr_range,
-					x='-α' if g.debug_utf8 else ''))
+					x='-α' if cfg.debug_utf8 else ''))
 			if mmtype == self.proto.mmtypes[0] and user == 'bob':
 			if mmtype == self.proto.mmtypes[0] and user == 'bob':
 				self._add_comments_to_addr_file(addrfile,addrfile,use_comments=True)
 				self._add_comments_to_addr_file(addrfile,addrfile,use_comments=True)
 			t = self.spawn(
 			t = self.spawn(
@@ -541,7 +540,7 @@ class TestSuiteRegtest(TestSuiteBase,TestSuiteShared):
 					(['--batch'] if batch else []) +
 					(['--batch'] if batch else []) +
 					[addrfile] ),
 					[addrfile] ),
 				extra_desc = f'({desc})' )
 				extra_desc = f'({desc})' )
-			if g.debug:
+			if cfg.debug:
 				t.expect("Type uppercase 'YES' to confirm: ",'YES\n')
 				t.expect("Type uppercase 'YES' to confirm: ",'YES\n')
 			t.expect('Importing')
 			t.expect('Importing')
 			if batch:
 			if batch:
@@ -598,7 +597,7 @@ class TestSuiteRegtest(TestSuiteBase,TestSuiteShared):
 			return 'skip'
 			return 'skip'
 		self.spawn('',msg_only=True)
 		self.spawn('',msg_only=True)
 		from mmgen.proto.btc.regtest import MMGenRegtest
 		from mmgen.proto.btc.regtest import MMGenRegtest
-		rt = MMGenRegtest(self.proto.coin)
+		rt = MMGenRegtest(cfg,self.proto.coin)
 		await rt.stop()
 		await rt.stop()
 		from shutil import rmtree
 		from shutil import rmtree
 		imsg(f'Deleting Bob’s old tracking wallet')
 		imsg(f'Deleting Bob’s old tracking wallet')
@@ -888,10 +887,10 @@ class TestSuiteRegtest(TestSuiteBase,TestSuiteShared):
 	def get_addr_from_addrlist(self,user,sid,mmtype,idx,addr_range='1-5'):
 	def get_addr_from_addrlist(self,user,sid,mmtype,idx,addr_range='1-5'):
 		id_str = { 'L':'', 'S':'-S', 'C':'-C', 'B':'-B' }[mmtype]
 		id_str = { 'L':'', 'S':'-S', 'C':'-C', 'B':'-B' }[mmtype]
 		ext = '{}{}{}[{}]{x}.regtest.addrs'.format(
 		ext = '{}{}{}[{}]{x}.regtest.addrs'.format(
-			sid,self.altcoin_pfx,id_str,addr_range,x='-α' if g.debug_utf8 else '')
+			sid,self.altcoin_pfx,id_str,addr_range,x='-α' if cfg.debug_utf8 else '')
 		addrfile = get_file_with_ext(self._user_dir(user),ext,no_dot=True)
 		addrfile = get_file_with_ext(self._user_dir(user),ext,no_dot=True)
 		silence()
 		silence()
-		addr = AddrList(self.proto,addrfile).data[idx].addr
+		addr = AddrList( cfg, self.proto, addrfile ).data[idx].addr
 		end_silence()
 		end_silence()
 		return addr
 		return addr
 
 
@@ -909,7 +908,7 @@ class TestSuiteRegtest(TestSuiteBase,TestSuiteShared):
 	def bob_rbf_1output_bump(self):
 	def bob_rbf_1output_bump(self):
 		if not self.test_rbf:
 		if not self.test_rbf:
 			return 'skip'
 			return 'skip'
-		ext = '9343,3]{x}.regtest.rawtx'.format(x='-α' if g.debug_utf8 else '')
+		ext = '9343,3]{x}.regtest.rawtx'.format(x='-α' if cfg.debug_utf8 else '')
 		txfile = get_file_with_ext(self.tr.trash_dir,ext,delete=False,no_dot=True)
 		txfile = get_file_with_ext(self.tr.trash_dir,ext,delete=False,no_dot=True)
 		return self.user_txbump('bob',
 		return self.user_txbump('bob',
 			self.tr.trash_dir,
 			self.tr.trash_dir,
@@ -958,7 +957,7 @@ class TestSuiteRegtest(TestSuiteBase,TestSuiteShared):
 		return t
 		return t
 
 
 	def bob_rbf_bump(self):
 	def bob_rbf_bump(self):
-		ext = ',{}]{x}.regtest.sigtx'.format(rtFee[1][:-1],x='-α' if g.debug_utf8 else '')
+		ext = ',{}]{x}.regtest.sigtx'.format(rtFee[1][:-1],x='-α' if cfg.debug_utf8 else '')
 		txfile = self.get_file_with_ext(ext,delete=False,no_dot=True)
 		txfile = self.get_file_with_ext(ext,delete=False,no_dot=True)
 		return self.user_txbump('bob',self.tmpdir,txfile,rtFee[2],add_args=['--send'])
 		return self.user_txbump('bob',self.tmpdir,txfile,rtFee[2],add_args=['--send'])
 
 
@@ -969,10 +968,10 @@ class TestSuiteRegtest(TestSuiteBase,TestSuiteShared):
 		return t
 		return t
 
 
 	def _get_mempool(self):
 	def _get_mempool(self):
-		if not g.debug_utf8:
+		if not cfg.debug_utf8:
 			disable_debug()
 			disable_debug()
 		ret = self.spawn('mmgen-regtest',['mempool']).read()
 		ret = self.spawn('mmgen-regtest',['mempool']).read()
-		if not g.debug_utf8:
+		if not cfg.debug_utf8:
 			restore_debug()
 			restore_debug()
 		m = re.search(r'(\[\s*"[a-f0-9]{64}"\s*])',ret) # allow for extra output by handler at end
 		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))
 		return json.loads(m.group(1))
@@ -987,7 +986,7 @@ class TestSuiteRegtest(TestSuiteBase,TestSuiteShared):
 	def bob_rbf_status(self,fee,exp1,exp2=''):
 	def bob_rbf_status(self,fee,exp1,exp2=''):
 		if not self.proto.cap('rbf'):
 		if not self.proto.cap('rbf'):
 			return 'skip'
 			return 'skip'
-		ext = ',{}]{x}.regtest.sigtx'.format(fee[:-1],x='-α' if g.debug_utf8 else '')
+		ext = ',{}]{x}.regtest.sigtx'.format(fee[:-1],x='-α' if cfg.debug_utf8 else '')
 		txfile = self.get_file_with_ext(ext,delete=False,no_dot=True)
 		txfile = self.get_file_with_ext(ext,delete=False,no_dot=True)
 		return self.user_txsend_status('bob',txfile,exp1,exp2)
 		return self.user_txsend_status('bob',txfile,exp1,exp2)
 
 
@@ -1043,7 +1042,7 @@ class TestSuiteRegtest(TestSuiteBase,TestSuiteShared):
 
 
 	def _gen_pairs(self,n):
 	def _gen_pairs(self,n):
 		from mmgen.tool.api import tool_api
 		from mmgen.tool.api import tool_api
-		t = tool_api()
+		t = tool_api(cfg)
 		t.init_coin(self.proto.coin,self.proto.network)
 		t.init_coin(self.proto.coin,self.proto.network)
 
 
 		def gen_addr(Type):
 		def gen_addr(Type):
@@ -1061,7 +1060,7 @@ class TestSuiteRegtest(TestSuiteBase,TestSuiteShared):
 
 
 	def user_import(self,user,args,nAddr):
 	def user_import(self,user,args,nAddr):
 		t = self.spawn('mmgen-addrimport',['--'+user]+args)
 		t = self.spawn('mmgen-addrimport',['--'+user]+args)
-		if g.debug:
+		if cfg.debug:
 			t.expect("Type uppercase 'YES' to confirm: ",'YES\n')
 			t.expect("Type uppercase 'YES' to confirm: ",'YES\n')
 		t.expect(f'Importing {nAddr} address')
 		t.expect(f'Importing {nAddr} address')
 		if '--rescan' in args:
 		if '--rescan' in args:
@@ -1272,7 +1271,7 @@ class TestSuiteRegtest(TestSuiteBase,TestSuiteShared):
 	def bob_edit_json_twdump(self):
 	def bob_edit_json_twdump(self):
 		self.spawn('',msg_only=True)
 		self.spawn('',msg_only=True)
 		from mmgen.tw.json import TwJSON
 		from mmgen.tw.json import TwJSON
-		fn = TwJSON.Base(self.proto).dump_fn
+		fn = TwJSON.Base(cfg,self.proto).dump_fn
 		text = json.loads(self.read_from_tmpfile(fn))
 		text = json.loads(self.read_from_tmpfile(fn))
 		text['data']['entries'][3][3] = f'edited comment [фубар] [{gr_uc}]'
 		text['data']['entries'][3][3] = f'edited comment [фубар] [{gr_uc}]'
 		self.write_to_tmpfile( fn, json.dumps(text,indent=4) )
 		self.write_to_tmpfile( fn, json.dumps(text,indent=4) )
@@ -1280,7 +1279,7 @@ class TestSuiteRegtest(TestSuiteBase,TestSuiteShared):
 
 
 	def carol_twimport(self,rpc_backend='http',add_parms=[],expect_str=None,expect_str2='Found 1 unspent output'):
 	def carol_twimport(self,rpc_backend='http',add_parms=[],expect_str=None,expect_str2='Found 1 unspent output'):
 		from mmgen.tw.json import TwJSON
 		from mmgen.tw.json import TwJSON
-		fn = joinpath( self.tmpdir, TwJSON.Base(self.proto).dump_fn )
+		fn = joinpath( self.tmpdir, TwJSON.Base(cfg,self.proto).dump_fn )
 		t = self.spawn(
 		t = self.spawn(
 			'mmgen-tool',
 			'mmgen-tool',
 			([f'--rpc-backend={rpc_backend}'] if rpc_backend else [])
 			([f'--rpc-backend={rpc_backend}'] if rpc_backend else [])
@@ -1313,7 +1312,7 @@ class TestSuiteRegtest(TestSuiteBase,TestSuiteShared):
 		t = self.spawn('mmgen-regtest',['cli','unloadwallet','carol'])
 		t = self.spawn('mmgen-regtest',['cli','unloadwallet','carol'])
 		t.ok()
 		t.ok()
 		from mmgen.rpc import rpc_init
 		from mmgen.rpc import rpc_init
-		rpc = await rpc_init(self.proto)
+		rpc = await rpc_init(cfg,self.proto)
 		wdir = joinpath(rpc.daemon.network_datadir,'wallets','carol')
 		wdir = joinpath(rpc.daemon.network_datadir,'wallets','carol')
 		from shutil import rmtree
 		from shutil import rmtree
 		imsg(f'Deleting Carol’s tracking wallet')
 		imsg(f'Deleting Carol’s tracking wallet')
@@ -1386,7 +1385,7 @@ class TestSuiteRegtest(TestSuiteBase,TestSuiteShared):
 
 
 	def alice_add_comment_badaddr2(self):
 	def alice_add_comment_badaddr2(self):
 		# mainnet zero address:
 		# mainnet zero address:
-		addr = init_proto(self.proto.coin,network='mainnet').pubhash2addr(bytes(20),False) # mainnet zero address
+		addr = init_proto( cfg, self.proto.coin, network='mainnet' ).pubhash2addr(bytes(20),False)
 		return self.alice_add_comment_badaddr( addr, 'invalid address', 2 )
 		return self.alice_add_comment_badaddr( addr, 'invalid address', 2 )
 
 
 	def alice_add_comment_badaddr3(self):
 	def alice_add_comment_badaddr3(self):
@@ -1513,7 +1512,7 @@ class TestSuiteRegtest(TestSuiteBase,TestSuiteShared):
 				s,
 				s,
 				regex=True )
 				regex=True )
 			if t.pexpect_spawn and s == 'w':
 			if t.pexpect_spawn and s == 'w':
-				t.expect(r'Total.*','q',regex=True,delay=1 if opt.exact_output else t.send_delay)
+				t.expect(r'Total.*','q',regex=True,delay=1 if cfg.exact_output else t.send_delay)
 				time.sleep(t.send_delay)
 				time.sleep(t.send_delay)
 				t.send('e')
 				t.send('e')
 		return t
 		return t
@@ -1799,7 +1798,7 @@ class TestSuiteRegtest(TestSuiteBase,TestSuiteShared):
 			'contains no unused addresses of address type' )
 			'contains no unused addresses of address type' )
 
 
 	def stop(self):
 	def stop(self):
-		if opt.no_daemon_stop:
+		if cfg.no_daemon_stop:
 			self.spawn('',msg_only=True)
 			self.spawn('',msg_only=True)
 			msg_r('(leaving daemon running by user request)')
 			msg_r('(leaving daemon running by user request)')
 			return 'ok'
 			return 'ok'

+ 0 - 2
test/test_py_d/ts_seedsplit.py

@@ -20,8 +20,6 @@
 test.test_py_d.ts_seedsplit: Seed split/join tests for the test.py test suite
 test.test_py_d.ts_seedsplit: Seed split/join tests for the test.py test suite
 """
 """
 
 
-from mmgen.globalvars import g
-from mmgen.opts import opt
 from mmgen.wallet import get_wallet_cls
 from mmgen.wallet import get_wallet_cls
 
 
 from .ts_base import *
 from .ts_base import *

+ 0 - 2
test/test_py_d/ts_shared.py

@@ -21,8 +21,6 @@ test.test_py_d.ts_shared: Shared methods for the test.py test suite
 """
 """
 
 
 import os
 import os
-from mmgen.globalvars import g
-from mmgen.opts import opt
 from mmgen.util import ymsg
 from mmgen.util import ymsg
 from mmgen.wallet import get_wallet_cls
 from mmgen.wallet import get_wallet_cls
 
 

+ 2 - 2
test/test_py_d/ts_tool.py

@@ -87,7 +87,7 @@ class TestSuiteTool(TestSuiteMain,TestSuiteBase):
 		return t
 		return t
 
 
 	def tool_extract_key_from_geth_wallet(self):
 	def tool_extract_key_from_geth_wallet(self):
-		if opt.no_altcoin:
+		if cfg.no_altcoin:
 			return 'skip'
 			return 'skip'
 		fn = 'test/ref/ethereum/geth-wallet.json'
 		fn = 'test/ref/ethereum/geth-wallet.json'
 		key = '9627ddb68354f5e0ff45fb2da49d7a20a013b7257a83ef4adbbbd87aeaccc75e'
 		key = '9627ddb68354f5e0ff45fb2da49d7a20a013b7257a83ef4adbbbd87aeaccc75e'
@@ -99,7 +99,7 @@ class TestSuiteTool(TestSuiteMain,TestSuiteBase):
 	def tool_api(self):
 	def tool_api(self):
 		t = self.spawn(
 		t = self.spawn(
 				'tool_api_test.py',
 				'tool_api_test.py',
-				(['no_altcoin'] if opt.no_altcoin else []),
+				(['no_altcoin'] if cfg.no_altcoin else []),
 				cmd_dir = 'test/misc' )
 				cmd_dir = 'test/misc' )
 		t.expect('legacy.*compressed.*segwit.*bech32',regex=True)
 		t.expect('legacy.*compressed.*segwit.*bech32',regex=True)
 		return t
 		return t

+ 2 - 3
test/test_py_d/ts_wallet.py

@@ -21,7 +21,6 @@ test.test_py_d.ts_wallet: Wallet conversion tests for the test.py test suite
 """
 """
 
 
 import os
 import os
-from mmgen.opts import opt
 from mmgen.wallet import get_wallet_cls
 from mmgen.wallet import get_wallet_cls
 from .common import *
 from .common import *
 from .ts_base import *
 from .ts_base import *
@@ -192,7 +191,7 @@ class TestSuiteWalletConv(TestSuiteBase,TestSuiteShared):
 		wf = t.written_to_file(capfirst(ocls.desc),oo=oo)
 		wf = t.written_to_file(capfirst(ocls.desc),oo=oo)
 		t.p.wait()
 		t.p.wait()
 		# back check of result
 		# back check of result
-		msg('' if opt.profile else ' OK')
+		msg('' if cfg.profile else ' OK')
 		return self.walletchk(  wf,
 		return self.walletchk(  wf,
 								pf         = None,
 								pf         = None,
 								extra_desc = '(check)',
 								extra_desc = '(check)',
@@ -222,7 +221,7 @@ class TestSuiteWalletConv(TestSuiteBase,TestSuiteShared):
 		if wcls.type == 'incog_hidden':
 		if wcls.type == 'incog_hidden':
 			add_args += uopts_chk
 			add_args += uopts_chk
 			wf = None
 			wf = None
-		msg('' if opt.profile else ' OK')
+		msg('' if cfg.profile else ' OK')
 		return self.walletchk(  wf,
 		return self.walletchk(  wf,
 								pf         = pf,
 								pf         = pf,
 								wcls       = wcls,
 								wcls       = wcls,

+ 15 - 11
test/test_py_d/ts_xmrwallet.py

@@ -24,7 +24,6 @@ import sys,os,atexit,asyncio,shutil
 from subprocess import run,PIPE
 from subprocess import run,PIPE
 
 
 from mmgen.globalvars import gc
 from mmgen.globalvars import gc
-from mmgen.opts import opt
 from mmgen.obj import MMGenRange
 from mmgen.obj import MMGenRange
 from mmgen.amt import XMRAmt
 from mmgen.amt import XMRAmt
 from mmgen.addrlist import KeyAddrList,AddrIdxList
 from mmgen.addrlist import KeyAddrList,AddrIdxList
@@ -84,7 +83,7 @@ class TestSuiteXMRWallet(TestSuiteBase):
 			return
 			return
 
 
 		from mmgen.protocol import init_proto
 		from mmgen.protocol import init_proto
-		self.proto = init_proto('XMR',network='mainnet')
+		self.proto = init_proto( cfg, 'XMR', network='mainnet' )
 		self.datadir_base  = os.path.join('test','daemons','xmrtest')
 		self.datadir_base  = os.path.join('test','daemons','xmrtest')
 		self.extra_opts = ['--wallet-rpc-password=passw0rd']
 		self.extra_opts = ['--wallet-rpc-password=passw0rd']
 		self.init_users()
 		self.init_users()
@@ -99,11 +98,11 @@ class TestSuiteXMRWallet(TestSuiteBase):
 		self.tx_relay_daemon_proxy_parm = (
 		self.tx_relay_daemon_proxy_parm = (
 			self.tx_relay_daemon_parm + f':127.0.0.1:{self.socks_port}' ) # must be IP, not 'localhost'
 			self.tx_relay_daemon_parm + f':127.0.0.1:{self.socks_port}' ) # must be IP, not 'localhost'
 
 
-		if not opt.no_daemon_stop:
+		if not cfg.no_daemon_stop:
 			atexit.register(self.stop_daemons)
 			atexit.register(self.stop_daemons)
 			atexit.register(self.stop_miner_wallet_daemon)
 			atexit.register(self.stop_miner_wallet_daemon)
 
 
-		if not opt.no_daemon_autostart:
+		if not cfg.no_daemon_autostart:
 			self.stop_daemons()
 			self.stop_daemons()
 			shutil.rmtree(self.datadir_base,ignore_errors=True)
 			shutil.rmtree(self.datadir_base,ignore_errors=True)
 			os.makedirs(self.datadir_base)
 			os.makedirs(self.datadir_base)
@@ -123,7 +122,7 @@ class TestSuiteXMRWallet(TestSuiteBase):
 			else: return True
 			else: return True
 
 
 		def start_proxy():
 		def start_proxy():
-			if external_call or not opt.no_daemon_autostart:
+			if external_call or not cfg.no_daemon_autostart:
 				run(a+b2)
 				run(a+b2)
 				omsg(f'SSH SOCKS server started, listening at localhost:{cls.socks_port}')
 				omsg(f'SSH SOCKS server started, listening at localhost:{cls.socks_port}')
 
 
@@ -174,7 +173,7 @@ class TestSuiteXMRWallet(TestSuiteBase):
 				""",indent='    ',strip_char='\t'))
 				""",indent='    ',strip_char='\t'))
 
 
 				from mmgen.ui import keypress_confirm
 				from mmgen.ui import keypress_confirm
-				if keypress_confirm('Continue?'):
+				if keypress_confirm(cfg,'Continue?'):
 					start_proxy()
 					start_proxy()
 				else:
 				else:
 					die(1,'Exiting at user request')
 					die(1,'Exiting at user request')
@@ -187,7 +186,7 @@ class TestSuiteXMRWallet(TestSuiteBase):
 					Then restart the test.
 					Then restart the test.
 				""",indent='    '))
 				""",indent='    '))
 
 
-		if not (external_call or opt.no_daemon_stop):
+		if not (external_call or cfg.no_daemon_stop):
 			atexit.register(kill_proxy)
 			atexit.register(kill_proxy)
 
 
 		return True
 		return True
@@ -222,6 +221,7 @@ class TestSuiteXMRWallet(TestSuiteBase):
 			udir = os.path.join('test',f'tmp{n}',user)
 			udir = os.path.join('test',f'tmp{n}',user)
 			datadir = os.path.join(self.datadir_base,user)
 			datadir = os.path.join(self.datadir_base,user)
 			md = CoinDaemon(
 			md = CoinDaemon(
+				cfg        = cfg,
 				proto      = self.proto,
 				proto      = self.proto,
 				test_suite = True,
 				test_suite = True,
 				port_shift = shift,
 				port_shift = shift,
@@ -229,6 +229,7 @@ class TestSuiteXMRWallet(TestSuiteBase):
 				datadir    = datadir
 				datadir    = datadir
 			)
 			)
 			md_rpc = MoneroRPCClient(
 			md_rpc = MoneroRPCClient(
+				cfg    = cfg,
 				proto  = self.proto,
 				proto  = self.proto,
 				host   = md.host,
 				host   = md.host,
 				port   = md.rpc_port,
 				port   = md.rpc_port,
@@ -238,6 +239,7 @@ class TestSuiteXMRWallet(TestSuiteBase):
 				daemon = md,
 				daemon = md,
 			)
 			)
 			wd = MoneroWalletDaemon(
 			wd = MoneroWalletDaemon(
+				cfg        = cfg,
 				proto      = self.proto,
 				proto      = self.proto,
 				test_suite = True,
 				test_suite = True,
 				wallet_dir = udir,
 				wallet_dir = udir,
@@ -248,6 +250,7 @@ class TestSuiteXMRWallet(TestSuiteBase):
 				daemon_addr = f'127.0.0.1:{md.rpc_port}',
 				daemon_addr = f'127.0.0.1:{md.rpc_port}',
 			)
 			)
 			wd_rpc = MoneroWalletRPCClient(
 			wd_rpc = MoneroWalletRPCClient(
+				cfg             = cfg,
 				daemon          = wd,
 				daemon          = wd,
 				test_connection = False,
 				test_connection = False,
 			)
 			)
@@ -316,6 +319,7 @@ class TestSuiteXMRWallet(TestSuiteBase):
 			self.extra_opts + dir_opt + [ 'create', data.kafile, (wallet or data.kal_range) ] )
 			self.extra_opts + dir_opt + [ 'create', data.kafile, (wallet or data.kal_range) ] )
 		for i in MMGenRange(wallet or data.kal_range).items:
 		for i in MMGenRange(wallet or data.kal_range).items:
 			write_data_to_file(
 			write_data_to_file(
+				cfg,
 				self.users[user].addrfile_fs.format(i),
 				self.users[user].addrfile_fs.format(i),
 				t.expect_getend('Address: '),
 				t.expect_getend('Address: '),
 				quiet = True
 				quiet = True
@@ -570,16 +574,16 @@ class TestSuiteXMRWallet(TestSuiteBase):
 	async def open_wallet_user(self,user,wnum):
 	async def open_wallet_user(self,user,wnum):
 		data = self.users[user]
 		data = self.users[user]
 		silence()
 		silence()
-		kal = KeyAddrList(self.proto,data.kafile,key_address_validity_check=False)
+		kal = KeyAddrList( cfg, self.proto, data.kafile, key_address_validity_check=False )
 		end_silence()
 		end_silence()
-		self.users[user].wd.start(silent=not (opt.exact_output or opt.verbose))
+		self.users[user].wd.start(silent=not (cfg.exact_output or cfg.verbose))
 		return data.wd_rpc.call(
 		return data.wd_rpc.call(
 			'open_wallet',
 			'open_wallet',
 			filename = os.path.basename(data.walletfile_fs.format(wnum)),
 			filename = os.path.basename(data.walletfile_fs.format(wnum)),
 			password = kal.entry(wnum).wallet_passwd )
 			password = kal.entry(wnum).wallet_passwd )
 
 
 	async def stop_wallet_user(self,user):
 	async def stop_wallet_user(self,user):
-		await self.users[user].wd_rpc.stop_daemon(silent=not (opt.exact_output or opt.verbose))
+		await self.users[user].wd_rpc.stop_daemon(silent=not (cfg.exact_output or cfg.verbose))
 		return 'ok'
 		return 'ok'
 
 
 	# mining methods
 	# mining methods
@@ -639,7 +643,7 @@ class TestSuiteXMRWallet(TestSuiteBase):
 
 
 		async def send_random_txs():
 		async def send_random_txs():
 			from mmgen.tool.api import tool_api
 			from mmgen.tool.api import tool_api
-			t = tool_api()
+			t = tool_api(cfg)
 			t.init_coin('XMR','mainnet')
 			t.init_coin('XMR','mainnet')
 			t.usr_randchars = 0
 			t.usr_randchars = 0
 			imsg_r(f'Sending random transactions: ')
 			imsg_r(f'Sending random transactions: ')

+ 43 - 39
test/tooltest.py

@@ -55,12 +55,15 @@ If no command is given, the whole suite of tests is run.
 
 
 sys.argv = [sys.argv[0]] + ['--skip-cfg-file'] + sys.argv[1:]
 sys.argv = [sys.argv[0]] + ['--skip-cfg-file'] + sys.argv[1:]
 
 
-cmd_args = opts.init(opts_data)
+cfg = opts.init(opts_data)
 
 
-assert opt.type in (None,'zcash_z'), 'Only zcash-z permitted for --type argument'
+set_globals(cfg)
 
 
-from mmgen.protocol import init_proto_from_opts
-proto = init_proto_from_opts()
+vmsg = cfg._util.vmsg
+
+proto = cfg._proto
+
+assert cfg.type in (None,'zcash_z'), 'Only zcash-z permitted for --type argument'
 
 
 cmd_data = {
 cmd_data = {
 	'cryptocoin': {
 	'cryptocoin': {
@@ -100,11 +103,11 @@ if proto.coin in ('BTC','LTC'):
 		'pipetest':             ('randpair','o3')
 		'pipetest':             ('randpair','o3')
 	})
 	})
 
 
-if proto.coin == 'XMR' or opt.type == 'zcash_z':
+if proto.coin == 'XMR' or cfg.type == 'zcash_z':
 	del cmd_data['cryptocoin']['cmd_data']['pubhash2addr']
 	del cmd_data['cryptocoin']['cmd_data']['pubhash2addr']
 	del cmd_data['cryptocoin']['cmd_data']['addr2pubhash']
 	del cmd_data['cryptocoin']['cmd_data']['addr2pubhash']
 
 
-cfg = {
+tcfg = {
 	'name':          'the tool utility',
 	'name':          'the tool utility',
 	'enc_passwd':    'Ten Satoshis',
 	'enc_passwd':    'Ten Satoshis',
 	'tmpdir':        'test/tmp/10',
 	'tmpdir':        'test/tmp/10',
@@ -132,23 +135,24 @@ tn_ext = ('','.testnet')[proto.testnet]
 
 
 mmgen_cmd = 'mmgen-tool'
 mmgen_cmd = 'mmgen-tool'
 
 
-if not opt.system:
+if not cfg.system:
 	os.environ['PYTHONPATH'] = repo_root
 	os.environ['PYTHONPATH'] = repo_root
 	mmgen_cmd = os.path.relpath(os.path.join(repo_root,'cmds',mmgen_cmd))
 	mmgen_cmd = os.path.relpath(os.path.join(repo_root,'cmds',mmgen_cmd))
 
 
 spawn_cmd = ['scripts/exec_wrapper.py',mmgen_cmd]
 spawn_cmd = ['scripts/exec_wrapper.py',mmgen_cmd]
 
 
-if opt.coverage:
+if cfg.coverage:
 	d,f = init_coverage()
 	d,f = init_coverage()
 	spawn_cmd = ['python3','-m','trace','--count','--coverdir='+d,'--file='+f] + spawn_cmd
 	spawn_cmd = ['python3','-m','trace','--count','--coverdir='+d,'--file='+f] + spawn_cmd
 elif gc.platform == 'win':
 elif gc.platform == 'win':
 	spawn_cmd = ['python3'] + spawn_cmd
 	spawn_cmd = ['python3'] + spawn_cmd
 
 
-add_spawn_args = ['--data-dir='+cfg['tmpdir']] + ['--{}{}'.format(
-		k.replace('_','-'),'='+getattr(opt,k) if getattr(opt,k) != True else '')
-			for k in ('testnet','rpc_host','regtest','coin','type') if getattr(opt,k)]
+add_spawn_args = ['--data-dir='+tcfg['tmpdir']] + ['--{}{}'.format(
+		k.replace('_','-'),
+		'='+getattr(cfg,k) if getattr(cfg,k) != True else '')
+			for k in ('testnet','rpc_host','regtest','coin','type') if getattr(cfg,k)]
 
 
-if opt.list_cmds:
+if cfg.list_cmds:
 	fs = '  {:<{w}} - {}'
 	fs = '  {:<{w}} - {}'
 	Msg('Available commands:')
 	Msg('Available commands:')
 	w = max(map(len,cmd_data))
 	w = max(map(len,cmd_data))
@@ -157,7 +161,7 @@ if opt.list_cmds:
 	Msg('\nAvailable utilities:')
 	Msg('\nAvailable utilities:')
 	Msg(fs.format('clean','Clean the tmp directory',w=w))
 	Msg(fs.format('clean','Clean the tmp directory',w=w))
 	sys.exit(0)
 	sys.exit(0)
-if opt.list_names:
+if cfg.list_names:
 	tcmd = ['python3','test/tooltest2.py','--list-tested-cmds']
 	tcmd = ['python3','test/tooltest2.py','--list-tested-cmds']
 	tested_in = {
 	tested_in = {
 		'tooltest.py': [],
 		'tooltest.py': [],
@@ -198,12 +202,12 @@ def is_coin_addr_loc(s):
 msg_w = 35
 msg_w = 35
 def test_msg(m):
 def test_msg(m):
 	m2 = f'Testing {m}'
 	m2 = f'Testing {m}'
-	msg_r(green(m2+'\n') if opt.verbose else '{:{w}}'.format( m2, w=msg_w+8 ))
+	msg_r(green(m2+'\n') if cfg.verbose else '{:{w}}'.format( m2, w=msg_w+8 ))
 
 
-compressed = opt.type or ('','compressed')['C' in proto.mmtypes]
+compressed = cfg.type or ('','compressed')['C' in proto.mmtypes]
 segwit     = ('','segwit')['S' in proto.mmtypes]
 segwit     = ('','segwit')['S' in proto.mmtypes]
 bech32     = ('','bech32')['B' in proto.mmtypes]
 bech32     = ('','bech32')['B' in proto.mmtypes]
-type_compressed_arg = ([],['--type=' + (opt.type or 'compressed')])[bool(opt.type) or 'C' in proto.mmtypes]
+type_compressed_arg = ([],['--type=' + (cfg.type or 'compressed')])[bool(cfg.type) or 'C' in proto.mmtypes]
 type_segwit_arg     = ([],['--type=segwit'])['S' in proto.mmtypes]
 type_segwit_arg     = ([],['--type=segwit'])['S' in proto.mmtypes]
 type_bech32_arg     = ([],['--type=bech32'])['B' in proto.mmtypes]
 type_bech32_arg     = ([],['--type=bech32'])['B' in proto.mmtypes]
 
 
@@ -213,7 +217,7 @@ class MMGenToolTestUtils(object):
 		sys_cmd = (
 		sys_cmd = (
 			spawn_cmd +
 			spawn_cmd +
 			add_spawn_args +
 			add_spawn_args +
-			['-r0','-d',cfg['tmpdir']] +
+			['-r0','-d',tcfg['tmpdir']] +
 			add_opts +
 			add_opts +
 			[name.lower()] +
 			[name.lower()] +
 			tool_args +
 			tool_args +
@@ -222,7 +226,7 @@ class MMGenToolTestUtils(object):
 		if extra_msg: extra_msg = f'({extra_msg})'
 		if extra_msg: extra_msg = f'({extra_msg})'
 		full_name = ' '.join([name.lower()]+add_opts+kwargs.split()+extra_msg.split())
 		full_name = ' '.join([name.lower()]+add_opts+kwargs.split()+extra_msg.split())
 		if not silent:
 		if not silent:
-			if opt.verbose:
+			if cfg.verbose:
 				sys.stderr.write(green(f'Testing {full_name}\nExecuting '))
 				sys.stderr.write(green(f'Testing {full_name}\nExecuting '))
 				sys.stderr.write(cyan(' '.join(sys_cmd)+'\n'))
 				sys.stderr.write(cyan(' '.join(sys_cmd)+'\n'))
 			else:
 			else:
@@ -231,7 +235,7 @@ class MMGenToolTestUtils(object):
 		cp = run(sys_cmd,stdout=PIPE,stderr=PIPE)
 		cp = run(sys_cmd,stdout=PIPE,stderr=PIPE)
 		out = cp.stdout
 		out = cp.stdout
 		err = cp.stderr
 		err = cp.stderr
-		if opt.debug:
+		if cfg.debug:
 			try: dmsg(err.decode())
 			try: dmsg(err.decode())
 			except: dmsg(repr(err))
 			except: dmsg(repr(err))
 		if not binary:
 		if not binary:
@@ -267,14 +271,14 @@ class MMGenToolTestUtils(object):
 	def run_cmd_out(self,name,carg=None,Return=False,kwargs='',fn_idx='',extra_msg='',
 	def run_cmd_out(self,name,carg=None,Return=False,kwargs='',fn_idx='',extra_msg='',
 						literal=False,chkdata='',hush=False,add_opts=[]):
 						literal=False,chkdata='',hush=False,add_opts=[]):
 		if carg:
 		if carg:
-			write_to_tmpfile(cfg,f'{name}{fn_idx}.in',carg+'\n')
+			write_to_tmpfile(tcfg,f'{name}{fn_idx}.in',carg+'\n')
 		ret = self.run_cmd(name,([],[carg])[bool(carg)],kwargs=kwargs,
 		ret = self.run_cmd(name,([],[carg])[bool(carg)],kwargs=kwargs,
 								extra_msg=extra_msg,add_opts=add_opts)
 								extra_msg=extra_msg,add_opts=add_opts)
 		if carg:
 		if carg:
 			vmsg('In:   ' + repr(carg))
 			vmsg('In:   ' + repr(carg))
 		vmsg('Out:  ' + (repr(ret),ret)[literal])
 		vmsg('Out:  ' + (repr(ret),ret)[literal])
 		if ret or ret == '':
 		if ret or ret == '':
-			write_to_tmpfile(cfg,f'{name}{fn_idx}.out',ret+'\n')
+			write_to_tmpfile(tcfg,f'{name}{fn_idx}.out',ret+'\n')
 			if chkdata:
 			if chkdata:
 				cmp_or_die(ret,chkdata)
 				cmp_or_die(ret,chkdata)
 				return
 				return
@@ -287,10 +291,10 @@ class MMGenToolTestUtils(object):
 	def run_cmd_randinput(self,name,strip=True,add_opts=[]):
 	def run_cmd_randinput(self,name,strip=True,add_opts=[]):
 		s = getrand(128)
 		s = getrand(128)
 		fn = name+'.in'
 		fn = name+'.in'
-		write_to_tmpfile(cfg,fn,s,binary=True)
-		ret = self.run_cmd(name,[get_tmpfile(cfg,fn)],strip=strip,add_opts=add_opts)
+		write_to_tmpfile(tcfg,fn,s,binary=True)
+		ret = self.run_cmd(name,[get_tmpfile(tcfg,fn)],strip=strip,add_opts=add_opts)
 		fn = name+'.out'
 		fn = name+'.out'
-		write_to_tmpfile(cfg,fn,ret+'\n')
+		write_to_tmpfile(tcfg,fn,ret+'\n')
 		ok()
 		ok()
 		vmsg(f'Returned: {ret}')
 		vmsg(f'Returned: {ret}')
 
 
@@ -342,7 +346,7 @@ class MMGenToolTestCmds(object):
 		for n,k in enumerate(('',compressed,segwit,bech32)):
 		for n,k in enumerate(('',compressed,segwit,bech32)):
 			ao = ['--type='+k] if k else []
 			ao = ['--type='+k] if k else []
 			ret = tu.run_cmd(name,[keys[n]],add_opts=ao).rstrip()
 			ret = tu.run_cmd(name,[keys[n]],add_opts=ao).rstrip()
-			iaddr = read_from_tmpfile(cfg,f'randpair{n+1}.out').split()[-1]
+			iaddr = read_from_tmpfile(tcfg,f'randpair{n+1}.out').split()[-1]
 			vmsg(f'Out: {ret}')
 			vmsg(f'Out: {ret}')
 			cmp_or_die(iaddr,ret)
 			cmp_or_die(iaddr,ret)
 			ok()
 			ok()
@@ -371,16 +375,16 @@ class MMGenToolTestCmds(object):
 	def pubhex2redeem_script(self,name,f1,f2,f3): # from above
 	def pubhex2redeem_script(self,name,f1,f2,f3): # from above
 		addr = read_from_file(f3).strip()
 		addr = read_from_file(f3).strip()
 		tu.run_cmd_out(name,addr,add_opts=type_segwit_arg,fn_idx=3)
 		tu.run_cmd_out(name,addr,add_opts=type_segwit_arg,fn_idx=3)
-		rs = read_from_tmpfile(cfg,'privhex2pubhex3.out').strip()
+		rs = read_from_tmpfile(tcfg,'privhex2pubhex3.out').strip()
 		tu.run_cmd_out('pubhex2addr',rs,add_opts=type_segwit_arg,fn_idx=3,hush=True)
 		tu.run_cmd_out('pubhex2addr',rs,add_opts=type_segwit_arg,fn_idx=3,hush=True)
-		addr1 = read_from_tmpfile(cfg,'pubhex2addr3.out').strip()
-		addr2 = read_from_tmpfile(cfg,'randpair3.out').split()[1]
+		addr1 = read_from_tmpfile(tcfg,'pubhex2addr3.out').strip()
+		addr2 = read_from_tmpfile(tcfg,'randpair3.out').split()[1]
 		cmp_or_die(addr1,addr2)
 		cmp_or_die(addr1,addr2)
 		ok()
 		ok()
 	def wif2redeem_script(self,name,f1,f2,f3): # compare output with above
 	def wif2redeem_script(self,name,f1,f2,f3): # compare output with above
 		wif = read_from_file(f3).split()[0]
 		wif = read_from_file(f3).split()[0]
 		ret1 = tu.run_cmd_out(name,wif,add_opts=type_segwit_arg,fn_idx=3,Return=True)
 		ret1 = tu.run_cmd_out(name,wif,add_opts=type_segwit_arg,fn_idx=3,Return=True)
-		ret2 = read_from_tmpfile(cfg,'pubhex2redeem_script3.out').strip()
+		ret2 = read_from_tmpfile(tcfg,'pubhex2redeem_script3.out').strip()
 		cmp_or_die(ret1,ret2)
 		cmp_or_die(ret1,ret2)
 		ok()
 		ok()
 	def wif2segwit_pair(self,name,f1,f2): # does its own checking, so just run
 	def wif2segwit_pair(self,name,f1,f2): # does its own checking, so just run
@@ -400,10 +404,10 @@ class MMGenToolTestCmds(object):
 					a=' '.join(add_spawn_args),
 					a=' '.join(add_spawn_args),
 					wif=wif)
 					wif=wif)
 		test_msg('command piping')
 		test_msg('command piping')
-		if opt.verbose:
+		if cfg.verbose:
 			sys.stderr.write(green('Executing ') + cyan(cmd) + '\n')
 			sys.stderr.write(green('Executing ') + cyan(cmd) + '\n')
 		res = run(cmd,stdout=PIPE,shell=True).stdout.decode().strip()
 		res = run(cmd,stdout=PIPE,shell=True).stdout.decode().strip()
-		addr = read_from_tmpfile(cfg,'wif2addr3.out').strip()
+		addr = read_from_tmpfile(tcfg,'wif2addr3.out').strip()
 		cmp_or_die(addr,res)
 		cmp_or_die(addr,res)
 		ok()
 		ok()
 
 
@@ -426,7 +430,7 @@ class MMGenToolTestCmds(object):
 # main()
 # main()
 import time
 import time
 start_time = int(time.time())
 start_time = int(time.time())
-mk_tmpdir(cfg['tmpdir'])
+mk_tmpdir(tcfg['tmpdir'])
 
 
 def gen_deps_for_cmd(cmd,cdata):
 def gen_deps_for_cmd(cmd,cdata):
 	fns = []
 	fns = []
@@ -446,25 +450,25 @@ def do_cmds(cmd_group):
 	gdata = cmd_data[cmd_group]['cmd_data']
 	gdata = cmd_data[cmd_group]['cmd_data']
 	for cmd in gdata:
 	for cmd in gdata:
 		fns = gen_deps_for_cmd(cmd,gdata[cmd])
 		fns = gen_deps_for_cmd(cmd,gdata[cmd])
-		cmdline = [cmd] + [os.path.join(cfg['tmpdir'],fn) for fn in fns]
+		cmdline = [cmd] + [os.path.join(tcfg['tmpdir'],fn) for fn in fns]
 		getattr(tc,cmd)(*cmdline)
 		getattr(tc,cmd)(*cmdline)
 
 
 try:
 try:
-	if cmd_args:
-		if len(cmd_args) != 1:
+	if cfg._args:
+		if len(cfg._args) != 1:
 			die(1,'Only one command may be specified')
 			die(1,'Only one command may be specified')
-		cmd = cmd_args[0]
+		cmd = cfg._args[0]
 		if cmd in cmd_data:
 		if cmd in cmd_data:
-			cleandir(cfg['tmpdir'],do_msg=True)
+			cleandir(tcfg['tmpdir'],do_msg=True)
 			msg('Running tests for {}:'.format( cmd_data[cmd]['desc'] ))
 			msg('Running tests for {}:'.format( cmd_data[cmd]['desc'] ))
 			do_cmds(cmd)
 			do_cmds(cmd)
 		elif cmd == 'clean':
 		elif cmd == 'clean':
-			cleandir(cfg['tmpdir'],do_msg=True)
+			cleandir(tcfg['tmpdir'],do_msg=True)
 			sys.exit(0)
 			sys.exit(0)
 		else:
 		else:
 			die(1,f'{cmd!r}: unrecognized command')
 			die(1,f'{cmd!r}: unrecognized command')
 	else:
 	else:
-		cleandir(cfg['tmpdir'],do_msg=True)
+		cleandir(tcfg['tmpdir'],do_msg=True)
 		for cmd in cmd_data:
 		for cmd in cmd_data:
 			msg('Running tests for {}:'.format( cmd_data[cmd]['desc'] ))
 			msg('Running tests for {}:'.format( cmd_data[cmd]['desc'] ))
 			do_cmds(cmd)
 			do_cmds(cmd)

+ 44 - 40
test/tooltest2.py

@@ -32,7 +32,7 @@ from test.overlay import overlay_setup
 sys.path.insert(0,overlay_setup(repo_root))
 sys.path.insert(0,overlay_setup(repo_root))
 
 
 from mmgen.common import *
 from mmgen.common import *
-from test.include.common import *
+from test.include.common import set_globals,end_msg,sample_text
 from mmgen.bip39 import is_bip39_mnemonic
 from mmgen.bip39 import is_bip39_mnemonic
 from mmgen.baseconv import is_mmgen_mnemonic
 from mmgen.baseconv import is_mmgen_mnemonic
 from mmgen.xmrseed import is_xmrseed
 from mmgen.xmrseed import is_xmrseed
@@ -89,6 +89,18 @@ If no command is given, the whole suite of tests is run.
 	}
 	}
 }
 }
 
 
+sys.argv = [sys.argv[0]] + ['--skip-cfg-file'] + sys.argv[1:]
+
+cfg = opts.init(
+	opts_data,
+	init_opts = {
+		'usr_randchars': 0,
+		'hash_preset': '1',
+		'passwd_file': 'test/ref/keyaddrfile_password',
+	})
+
+set_globals(cfg)
+
 sample_text_hexdump = (
 sample_text_hexdump = (
 	'000000: 5468 6520 5469 6d65 7320 3033 2f4a 616e{n}' +
 	'000000: 5468 6520 5469 6d65 7320 3033 2f4a 616e{n}' +
 	'000010: 2f32 3030 3920 4368 616e 6365 6c6c 6f72{n}' +
 	'000010: 2f32 3030 3920 4368 616e 6365 6c6c 6f72{n}' +
@@ -764,9 +776,9 @@ async def call_method(cls,method,cmd_name,args,out,opts,mmtype,stdin_input):
 			' '.join([cmd_name]+[repr(e) for e in args]),
 			' '.join([cmd_name]+[repr(e) for e in args]),
 			' '+mmtype if mmtype else '' ))
 			' '+mmtype if mmtype else '' ))
 	aargs,kwargs = main_tool.process_args(cmd_name,args,cls)
 	aargs,kwargs = main_tool.process_args(cmd_name,args,cls)
-	oq_save = bool(opt.quiet)
-	if not opt.verbose:
-		opt.quiet = True
+	oq_save = bool(cfg.quiet)
+	if not cfg.verbose:
+		cfg.quiet = True
 	if stdin_input:
 	if stdin_input:
 		fd0,fd1 = os.pipe()
 		fd0,fd1 = os.pipe()
 		if os.fork(): # parent
 		if os.fork(): # parent
@@ -776,7 +788,7 @@ async def call_method(cls,method,cmd_name,args,out,opts,mmtype,stdin_input):
 			cmd_out = method(*aargs,**kwargs)
 			cmd_out = method(*aargs,**kwargs)
 			os.dup2(stdin_save,0)
 			os.dup2(stdin_save,0)
 			os.wait()
 			os.wait()
-			opt.quiet = oq_save
+			cfg.quiet = oq_save
 			return cmd_out
 			return cmd_out
 		else: # child
 		else: # child
 			os.close(fd0)
 			os.close(fd0)
@@ -787,12 +799,12 @@ async def call_method(cls,method,cmd_name,args,out,opts,mmtype,stdin_input):
 		ret = method(*aargs,**kwargs)
 		ret = method(*aargs,**kwargs)
 		if type(ret).__name__ == 'coroutine':
 		if type(ret).__name__ == 'coroutine':
 			ret = await ret
 			ret = await ret
-		opt.quiet = oq_save
+		cfg.quiet = oq_save
 		return ret
 		return ret
 
 
 def tool_api(cls,cmd_name,args,out,opts):
 def tool_api(cls,cmd_name,args,out,opts):
 	from mmgen.tool.api import tool_api
 	from mmgen.tool.api import tool_api
-	tool = tool_api()
+	tool = tool_api(cfg)
 	if opts:
 	if opts:
 		for o in opts:
 		for o in opts:
 			if o.startswith('--type='):
 			if o.startswith('--type='):
@@ -832,7 +844,7 @@ async def run_test(cls,gid,cmd_name):
 	# behavior is like test.py: run coin-dependent tests only if proto.testnet or proto.coin != BTC
 	# behavior is like test.py: run coin-dependent tests only if proto.testnet or proto.coin != BTC
 	if gid in coin_dependent_groups:
 	if gid in coin_dependent_groups:
 		k = '{}_{}'.format(
 		k = '{}_{}'.format(
-			( g.token.lower() if proto.tokensym else proto.coin.lower() ),
+			( cfg.token.lower() if proto.tokensym else proto.coin.lower() ),
 			('mainnet','testnet')[proto.testnet] )
 			('mainnet','testnet')[proto.testnet] )
 		if k in data:
 		if k in data:
 			data = data[k]
 			data = data[k]
@@ -847,10 +859,10 @@ async def run_test(cls,gid,cmd_name):
 
 
 	m = '{} {}{}'.format(
 	m = '{} {}{}'.format(
 		purple('Testing'),
 		purple('Testing'),
-		cmd_name if opt.names else docstring_head(getattr(cls,cmd_name)),
+		cmd_name if cfg.names else docstring_head(getattr(cls,cmd_name)),
 		m2 )
 		m2 )
 
 
-	msg_r(green(m)+'\n' if opt.verbose else m)
+	msg_r(green(m)+'\n' if cfg.verbose else m)
 
 
 	for d in data:
 	for d in data:
 		args,out,opts,mmtype = d + tuple([None] * (4-len(d)))
 		args,out,opts,mmtype = d + tuple([None] * (4-len(d)))
@@ -859,17 +871,17 @@ async def run_test(cls,gid,cmd_name):
 			stdin_input = args[0]
 			stdin_input = args[0]
 			args[0] = '-'
 			args[0] = '-'
 
 
-		if opt.tool_api:
+		if cfg.tool_api:
 			if args and args[0 ]== '-':
 			if args and args[0 ]== '-':
 				continue
 				continue
 			cmd_out = tool_api(cls,cmd_name,args,out,opts)
 			cmd_out = tool_api(cls,cmd_name,args,out,opts)
-		elif opt.fork:
+		elif cfg.fork:
 			cmd_out = fork_cmd(cmd_name,args,out,opts,stdin_input)
 			cmd_out = fork_cmd(cmd_name,args,out,opts,stdin_input)
 		else:
 		else:
 			if stdin_input and gc.platform == 'win':
 			if stdin_input and gc.platform == 'win':
 				msg('Skipping for MSWin - no os.fork()')
 				msg('Skipping for MSWin - no os.fork()')
 				continue
 				continue
-			method = getattr(cls(cmdname=cmd_name,proto=proto,mmtype=mmtype),cmd_name)
+			method = getattr(cls(cfg,cmdname=cmd_name,proto=proto,mmtype=mmtype),cmd_name)
 			cmd_out = await call_method(cls,method,cmd_name,args,out,opts,mmtype,stdin_input)
 			cmd_out = await call_method(cls,method,cmd_name,args,out,opts,mmtype,stdin_input)
 
 
 		try:
 		try:
@@ -886,15 +898,15 @@ async def run_test(cls,gid,cmd_name):
 					out[1],
 					out[1],
 					func_out ))
 					func_out ))
 		elif isinstance(out,(list,tuple)):
 		elif isinstance(out,(list,tuple)):
-			for co,o in zip(cmd_out.split(NL) if opt.fork else cmd_out,out):
+			for co,o in zip(cmd_out.split(NL) if cfg.fork else cmd_out,out):
 				check_output(co,o)
 				check_output(co,o)
 		else:
 		else:
 			check_output(cmd_out,out)
 			check_output(cmd_out,out)
 
 
-		if not opt.verbose:
+		if not cfg.verbose:
 			msg_r('.')
 			msg_r('.')
 
 
-	if not opt.verbose:
+	if not cfg.verbose:
 		msg('OK')
 		msg('OK')
 
 
 def docstring_head(obj):
 def docstring_head(obj):
@@ -904,16 +916,16 @@ async def do_group(gid):
 	desc = f'command group {gid!r}'
 	desc = f'command group {gid!r}'
 	cls = main_tool.get_mod_cls(gid.lower())
 	cls = main_tool.get_mod_cls(gid.lower())
 	qmsg(blue('Testing ' +
 	qmsg(blue('Testing ' +
-		desc if opt.names else
+		desc if cfg.names else
 		( docstring_head(cls) or desc )
 		( docstring_head(cls) or desc )
 	))
 	))
 
 
-	for cmdname in cls().user_commands:
+	for cmdname in cls(cfg).user_commands:
 		if cmdname in skipped_tests:
 		if cmdname in skipped_tests:
 			continue
 			continue
 		if cmdname not in tests[gid]:
 		if cmdname not in tests[gid]:
 			m = f'No test for command {cmdname!r} in group {gid!r}!'
 			m = f'No test for command {cmdname!r} in group {gid!r}!'
-			if opt.die_on_missing:
+			if cfg.die_on_missing:
 				die(1,m+'  Aborting')
 				die(1,m+'  Aborting')
 			else:
 			else:
 				msg(m)
 				msg(m)
@@ -933,52 +945,44 @@ def list_tested_cmds():
 	for gid in tests:
 	for gid in tests:
 		Msg('\n'.join(tests[gid]))
 		Msg('\n'.join(tests[gid]))
 
 
-sys.argv = [sys.argv[0]] + ['--skip-cfg-file'] + sys.argv[1:]
-
-cmd_args = opts.init(
-	opts_data,
-	init_opts = {
-		'usr_randchars': 0,
-		'hash_preset': '1',
-		'passwd_file': 'test/ref/keyaddrfile_password',
-	})
+qmsg = cfg._util.qmsg
+vmsg = cfg._util.vmsg
 
 
-from mmgen.protocol import init_proto_from_opts
-proto = init_proto_from_opts(need_amt=True)
+proto = cfg._proto
 
 
-if opt.tool_api:
+if cfg.tool_api:
 	del tests['Wallet']
 	del tests['Wallet']
 	del tests['File']
 	del tests['File']
 
 
 import mmgen.main_tool as main_tool
 import mmgen.main_tool as main_tool
 
 
-if opt.list_tests:
+if cfg.list_tests:
 	Msg('Available tests:')
 	Msg('Available tests:')
 	for modname,cmdlist in main_tool.mods.items():
 	for modname,cmdlist in main_tool.mods.items():
 		cls = getattr(importlib.import_module(f'mmgen.tool.{modname}'),'tool_cmd')
 		cls = getattr(importlib.import_module(f'mmgen.tool.{modname}'),'tool_cmd')
 		Msg('  {:6} - {}'.format( modname, docstring_head(cls) ))
 		Msg('  {:6} - {}'.format( modname, docstring_head(cls) ))
 	sys.exit(0)
 	sys.exit(0)
 
 
-if opt.list_tested_cmds:
+if cfg.list_tested_cmds:
 	list_tested_cmds()
 	list_tested_cmds()
 	sys.exit(0)
 	sys.exit(0)
 
 
-if opt.system:
+if cfg.system:
 	tool_exec = 'mmgen-tool'
 	tool_exec = 'mmgen-tool'
 	sys.path.pop(0)
 	sys.path.pop(0)
 else:
 else:
 	os.environ['PYTHONPATH'] = repo_root
 	os.environ['PYTHONPATH'] = repo_root
 	tool_exec = os.path.relpath(os.path.join('cmds','mmgen-tool'))
 	tool_exec = os.path.relpath(os.path.join('cmds','mmgen-tool'))
 
 
-if opt.fork:
+if cfg.fork:
 	passthru_args = ['coin','type','testnet','token']
 	passthru_args = ['coin','type','testnet','token']
 	tool_cmd = [ tool_exec, '--skip-cfg-file' ] + [
 	tool_cmd = [ tool_exec, '--skip-cfg-file' ] + [
 		'--{}{}'.format(
 		'--{}{}'.format(
 			k.replace('_','-'),
 			k.replace('_','-'),
-			'='+getattr(opt,k) if getattr(opt,k) != True else '')
-		for k in passthru_args if getattr(opt,k) ]
+			'='+getattr(cfg,k) if getattr(cfg,k) != True else '')
+		for k in passthru_args if getattr(cfg,k) ]
 
 
-	if opt.coverage:
+	if cfg.coverage:
 		d,f = init_coverage()
 		d,f = init_coverage()
 		tool_cmd_preargs = ['python3','-m','trace','--count','--coverdir='+d,'--file='+f]
 		tool_cmd_preargs = ['python3','-m','trace','--count','--coverdir='+d,'--file='+f]
 	else:
 	else:
@@ -988,8 +992,8 @@ start_time = int(time.time())
 
 
 async def main():
 async def main():
 	try:
 	try:
-		if cmd_args:
-			for cmd in cmd_args:
+		if cfg._args:
+			for cmd in cfg._args:
 				if cmd in tests:
 				if cmd in tests:
 					await do_group(cmd)
 					await do_group(cmd)
 				else:
 				else:

+ 14 - 13
test/unit_tests.py

@@ -23,12 +23,12 @@ test/unit_tests.py: Unit tests for the MMGen suite
 import sys,os,time,importlib,platform
 import sys,os,time,importlib,platform
 
 
 from include.tests_header import repo_root
 from include.tests_header import repo_root
-from include.common import end_msg
 
 
 from mmgen.devinit import init_dev
 from mmgen.devinit import init_dev
 init_dev()
 init_dev()
 
 
 from mmgen.common import *
 from mmgen.common import *
+from test.include.common import set_globals,end_msg
 
 
 opts_data = {
 opts_data = {
 	'text': {
 	'text': {
@@ -54,10 +54,11 @@ If no test is specified, all available tests are run
 
 
 sys.argv.insert(1,'--skip-cfg-file')
 sys.argv.insert(1,'--skip-cfg-file')
 
 
-opts.UserOpts._reset_ok += ('use_internal_keccak_module',)
-g._reset_ok += ('debug_addrlist',)
+cfg = opts.init(opts_data)
 
 
-cmd_args = opts.init(opts_data)
+type(cfg)._reset_ok += ('use_internal_keccak_module','debug_addrlist')
+
+set_globals(cfg)
 
 
 os.environ['PYTHONPATH'] = repo_root
 os.environ['PYTHONPATH'] = repo_root
 
 
@@ -66,7 +67,7 @@ file_pfx = 'ut_'
 all_tests = sorted(
 all_tests = sorted(
 	[fn[3:-3] for fn in os.listdir(os.path.join(repo_root,'test','unit_tests_d')) if fn[:3] == file_pfx])
 	[fn[3:-3] for fn in os.listdir(os.path.join(repo_root,'test','unit_tests_d')) if fn[:3] == file_pfx])
 
 
-exclude = opt.exclude.split(',') if opt.exclude else []
+exclude = cfg.exclude.split(',') if cfg.exclude else []
 
 
 for e in exclude:
 for e in exclude:
 	if e not in all_tests:
 	if e not in all_tests:
@@ -74,7 +75,7 @@ for e in exclude:
 
 
 start_time = int(time.time())
 start_time = int(time.time())
 
 
-if opt.list:
+if cfg.list:
 	Msg(' '.join(all_tests))
 	Msg(' '.join(all_tests))
 	sys.exit(0)
 	sys.exit(0)
 
 
@@ -93,12 +94,12 @@ class UnitTestHelpers(object):
 		m_noraise = "\nillegal action 'bad {}' failed to raise exception {!r}"
 		m_noraise = "\nillegal action 'bad {}' failed to raise exception {!r}"
 		for (desc,exc_chk,emsg_chk,func) in data:
 		for (desc,exc_chk,emsg_chk,func) in data:
 			try:
 			try:
-				vmsg_r('  bad {:{w}}'.format( desc+':', w=desc_w+1 ))
+				cfg._util.vmsg_r('  bad {:{w}}'.format( desc+':', w=desc_w+1 ))
 				func()
 				func()
 			except Exception as e:
 			except Exception as e:
 				exc = type(e).__name__
 				exc = type(e).__name__
 				emsg = e.args[0]
 				emsg = e.args[0]
-				vmsg(' {:{w}} [{}]'.format( exc, emsg, w=exc_w ))
+				cfg._util.vmsg(' {:{w}} [{}]'.format( exc, emsg, w=exc_w ))
 				assert exc == exc_chk, m_exc.format(exc,exc_chk)
 				assert exc == exc_chk, m_exc.format(exc,exc_chk)
 				assert re.search(emsg_chk,emsg), m_err.format(emsg,emsg_chk)
 				assert re.search(emsg_chk,emsg), m_err.format(emsg,emsg_chk)
 			else:
 			else:
@@ -139,14 +140,14 @@ def run_test(test,subtest=None):
 			subtests = [k for k,v in t.__dict__.items() if type(v).__name__ == 'function' and k[0] != '_']
 			subtests = [k for k,v in t.__dict__.items() if type(v).__name__ == 'function' and k[0] != '_']
 			for subtest in subtests:
 			for subtest in subtests:
 				subtest_disp = subtest.replace('_','-')
 				subtest_disp = subtest.replace('_','-')
-				if opt.no_altcoin_deps and subtest in altcoin_deps:
-					qmsg(gray(f'Invoked with --no-altcoin-deps, so skipping {subtest_disp!r}'))
+				if cfg.no_altcoin_deps and subtest in altcoin_deps:
+					cfg._util.qmsg(gray(f'Invoked with --no-altcoin-deps, so skipping {subtest_disp!r}'))
 					continue
 					continue
 				if gc.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'))
+					cfg._util.qmsg(gray(f'Skipping {subtest_disp!r} for Windows platform'))
 					continue
 					continue
 				elif platform.machine() == 'aarch64' and subtest in arm_skip:
 				elif platform.machine() == 'aarch64' and subtest in arm_skip:
-					qmsg(gray(f'Skipping {subtest_disp!r} for ARM platform'))
+					cfg._util.qmsg(gray(f'Skipping {subtest_disp!r} for ARM platform'))
 					continue
 					continue
 				run_subtest(subtest)
 				run_subtest(subtest)
 		else:
 		else:
@@ -154,7 +155,7 @@ def run_test(test,subtest=None):
 				die(4,'Unit test {test!r} failed')
 				die(4,'Unit test {test!r} failed')
 
 
 try:
 try:
-	for test in (cmd_args or all_tests):
+	for test in (cfg._args or all_tests):
 		if '.' in test:
 		if '.' in test:
 			test,subtest = test.split('.')
 			test,subtest = test.split('.')
 		else:
 		else:

+ 3 - 3
test/unit_tests_d/__init__.py

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

+ 11 - 9
test/unit_tests_d/ut_addrlist.py

@@ -10,17 +10,19 @@ from mmgen.addr import MMGenAddrType
 from mmgen.addrlist import AddrIdxList,AddrList,KeyList,KeyAddrList
 from mmgen.addrlist import AddrIdxList,AddrList,KeyList,KeyAddrList
 from mmgen.passwdlist import PasswordList
 from mmgen.passwdlist import PasswordList
 from mmgen.protocol import init_proto
 from mmgen.protocol import init_proto
+from ..include.common import cfg,qmsg,vmsg
 
 
 def do_test(list_type,chksum,idx_spec=None,pw_id_str=None,add_kwargs=None):
 def do_test(list_type,chksum,idx_spec=None,pw_id_str=None,add_kwargs=None):
+
 	qmsg(blue(f'Testing {list_type.__name__}'))
 	qmsg(blue(f'Testing {list_type.__name__}'))
-	proto = init_proto('btc')
-	seed = Seed(seed_bin=bytes.fromhex('feedbead'*8))
+	proto = init_proto( cfg, 'btc' )
+	seed = Seed(cfg,seed_bin=bytes.fromhex('feedbead'*8))
 	mmtype = MMGenAddrType(proto,'C')
 	mmtype = MMGenAddrType(proto,'C')
 	idxs = AddrIdxList(idx_spec or '1-3')
 	idxs = AddrIdxList(idx_spec or '1-3')
 
 
-	if opt.verbose:
-		debug_addrlist_save = g.debug_addrlist
-		g.debug_addrlist = True
+	if cfg.verbose:
+		debug_addrlist_save = cfg.debug_addrlist
+		cfg.debug_addrlist = True
 
 
 	kwargs = {
 	kwargs = {
 		'seed': seed,
 		'seed': seed,
@@ -36,7 +38,7 @@ def do_test(list_type,chksum,idx_spec=None,pw_id_str=None,add_kwargs=None):
 	if add_kwargs:
 	if add_kwargs:
 		kwargs.update(add_kwargs)
 		kwargs.update(add_kwargs)
 
 
-	al = list_type( proto, **kwargs )
+	al = list_type( cfg, proto, **kwargs )
 
 
 	af = al.get_file()
 	af = al.get_file()
 	af.format()
 	af.format()
@@ -48,8 +50,8 @@ def do_test(list_type,chksum,idx_spec=None,pw_id_str=None,add_kwargs=None):
 	if chksum:
 	if chksum:
 		assert al.chksum == chksum, f'{al.chksum} != {chksum}'
 		assert al.chksum == chksum, f'{al.chksum} != {chksum}'
 
 
-	if opt.verbose:
-		g.debug_addrlist = debug_addrlist_save
+	if cfg.verbose:
+		cfg.debug_addrlist = debug_addrlist_save
 
 
 	return True
 	return True
 
 
@@ -73,7 +75,7 @@ class unit_tests:
 				('',              ''),
 				('',              ''),
 			):
 			):
 			l = AddrIdxList(i)
 			l = AddrIdxList(i)
-			if opt.verbose:
+			if cfg.verbose:
 				msg('list: {}\nin:   {}\nout:  {}\n'.format(list(l),i,o))
 				msg('list: {}\nin:   {}\nout:  {}\n'.format(list(l),i,o))
 			assert l.id_str == o, f'{l.id_str} != {o}'
 			assert l.id_str == o, f'{l.id_str} != {o}'
 
 

+ 2 - 1
test/unit_tests_d/ut_addrparse.py

@@ -5,6 +5,7 @@ test/unit_tests_d/ut_addrparse: address parsing tests for the MMGen suite
 """
 """
 
 
 from mmgen.common import *
 from mmgen.common import *
+from ..include.common import cfg,vmsg
 
 
 vectors = {
 vectors = {
 	'btc_mainnet': [
 	'btc_mainnet': [
@@ -92,7 +93,7 @@ class unit_test(object):
 		for net_id,addrs in vectors.items():
 		for net_id,addrs in vectors.items():
 			coin,network = net_id.split('_')
 			coin,network = net_id.split('_')
 			test_network(
 			test_network(
-				init_proto(coin,network=network),
+				init_proto( cfg, coin, network=network ),
 				addrs )
 				addrs )
 
 
 		return True
 		return True

+ 4 - 3
test/unit_tests_d/ut_baseconv.py

@@ -5,6 +5,7 @@ test.unit_tests_d.ut_baseconv: Base conversion unit test for the MMGen suite
 """
 """
 
 
 from mmgen.common import *
 from mmgen.common import *
+from ..include.common import cfg,qmsg,qmsg_r,vmsg,vmsg_r
 
 
 class unit_test(object):
 class unit_test(object):
 
 
@@ -164,7 +165,7 @@ class unit_test(object):
 		qmsg_r('\nChecking hex-to-base conversion:')
 		qmsg_r('\nChecking hex-to-base conversion:')
 		for base,data in self.vectors.items():
 		for base,data in self.vectors.items():
 			fs = "  {h:%s}  {p:<6} {r}" % max(len(d[0][0]) for d in data)
 			fs = "  {h:%s}  {p:<6} {r}" % max(len(d[0][0]) for d in data)
-			if not opt.verbose:
+			if not cfg.verbose:
 				qmsg_r(f' {base}')
 				qmsg_r(f' {base}')
 			vmsg(f'\nBase: {base}')
 			vmsg(f'\nBase: {base}')
 			vmsg(fs.format(h='Input',p='Pad',r='Output'))
 			vmsg(fs.format(h='Input',p='Pad',r='Output'))
@@ -180,7 +181,7 @@ class unit_test(object):
 		qmsg_r('\nChecking base-to-hex conversion:')
 		qmsg_r('\nChecking base-to-hex conversion:')
 		for base,data in self.vectors.items():
 		for base,data in self.vectors.items():
 			fs = "  {h:%s}  {p:<6} {r}" % max(len(d[1]) for d in data)
 			fs = "  {h:%s}  {p:<6} {r}" % max(len(d[1]) for d in data)
-			if not opt.verbose:
+			if not cfg.verbose:
 				qmsg_r(f' {base}')
 				qmsg_r(f' {base}')
 			vmsg(f'\nBase: {base}')
 			vmsg(f'\nBase: {base}')
 			vmsg(fs.format(h='Input',p='Pad',r='Output'))
 			vmsg(fs.format(h='Input',p='Pad',r='Output'))
@@ -200,7 +201,7 @@ class unit_test(object):
 
 
 		for wl_id in baseconv.constants['wl_chksum']:
 		for wl_id in baseconv.constants['wl_chksum']:
 			vmsg_r(f'  {wl_id+":":9}')
 			vmsg_r(f'  {wl_id+":":9}')
-			baseconv(wl_id).check_wordlist()
+			baseconv(wl_id).check_wordlist(cfg)
 
 
 		qmsg('')
 		qmsg('')
 
 

+ 2 - 1
test/unit_tests_d/ut_bip39.py

@@ -5,6 +5,7 @@ test/unit_tests_d/ut_bip39: BIP39 unit test for the MMGen suite
 """
 """
 
 
 from mmgen.common import *
 from mmgen.common import *
+from ..include.common import cfg,qmsg,vmsg
 
 
 class unit_test(object):
 class unit_test(object):
 
 
@@ -91,7 +92,7 @@ class unit_test(object):
 		from mmgen.bip39 import bip39
 		from mmgen.bip39 import bip39
 
 
 		b = bip39()
 		b = bip39()
-		b.check_wordlist()
+		b.check_wordlist(cfg)
 
 
 		vmsg('')
 		vmsg('')
 		qmsg('Checking seed to mnemonic conversion:')
 		qmsg('Checking seed to mnemonic conversion:')

+ 4 - 3
test/unit_tests_d/ut_daemon.py

@@ -8,9 +8,10 @@ from subprocess import run,DEVNULL
 from mmgen.common import *
 from mmgen.common import *
 from mmgen.daemon import *
 from mmgen.daemon import *
 from mmgen.protocol import init_proto
 from mmgen.protocol import init_proto
+from ..include.common import cfg,qmsg,qmsg_r,vmsg
 
 
 def test_flags():
 def test_flags():
-	d = CoinDaemon('eth')
+	d = CoinDaemon(cfg,'eth')
 	vmsg(f'Available opts:  {fmt_list(d.avail_opts,fmt="bare")}')
 	vmsg(f'Available opts:  {fmt_list(d.avail_opts,fmt="bare")}')
 	vmsg(f'Available flags: {fmt_list(d.avail_flags,fmt="bare")}')
 	vmsg(f'Available flags: {fmt_list(d.avail_flags,fmt="bare")}')
 	vals = namedtuple('vals',['online','no_daemonize','keep_cfg_file'])
 	vals = namedtuple('vals',['online','no_daemonize','keep_cfg_file'])
@@ -22,7 +23,7 @@ def test_flags():
 				(['online'],['keep_cfg_file'],                vals(True,False,True)),
 				(['online'],['keep_cfg_file'],                vals(True,False,True)),
 				(['online','no_daemonize'],['keep_cfg_file'], vals(True,True,True)),
 				(['online','no_daemonize'],['keep_cfg_file'], vals(True,True,True)),
 			):
 			):
-			d = CoinDaemon('eth',opts=opts,flags=flags)
+			d = CoinDaemon(cfg,'eth',opts=opts,flags=flags)
 			assert d.flag.keep_cfg_file == val.keep_cfg_file
 			assert d.flag.keep_cfg_file == val.keep_cfg_file
 			assert d.opt.online == val.online
 			assert d.opt.online == val.online
 			assert d.opt.no_daemonize == val.no_daemonize
 			assert d.opt.no_daemonize == val.no_daemonize
@@ -56,7 +57,7 @@ def test_cmd(args_in,message):
 	qmsg_r(message)
 	qmsg_r(message)
 	args = ['python3', f'test/{args_in[0]}-coin-daemons.py'] + list(args_in[1:])
 	args = ['python3', f'test/{args_in[0]}-coin-daemons.py'] + list(args_in[1:])
 	vmsg('\n' + orange(f"Running '{' '.join(args)}':"))
 	vmsg('\n' + orange(f"Running '{' '.join(args)}':"))
-	pipe = None if opt.verbose else PIPE
+	pipe = None if cfg.verbose else PIPE
 	cp = run( args, stdout=pipe, stderr=pipe, check=True )
 	cp = run( args, stdout=pipe, stderr=pipe, check=True )
 	qmsg('OK')
 	qmsg('OK')
 	return True
 	return True

+ 2 - 2
test/unit_tests_d/ut_dep.py

@@ -11,7 +11,7 @@ from subprocess import run,PIPE
 
 
 from mmgen.common import *
 from mmgen.common import *
 from mmgen.exception import NoLEDSupport
 from mmgen.exception import NoLEDSupport
-from ..include.common import check_solc_ver
+from ..include.common import cfg,vmsg,check_solc_ver
 
 
 class unit_tests:
 class unit_tests:
 
 
@@ -127,7 +127,7 @@ class unit_tests:
 				'--supply=100000000000000000000000000',
 				'--supply=100000000000000000000000000',
 				'--decimals=18',
 				'--decimals=18',
 				'--stdout',
 				'--stdout',
-				init_proto('eth').checksummed_addr('deadbeef'*5),
+				init_proto( cfg, 'eth' ).checksummed_addr('deadbeef'*5),
 			]
 			]
 			cp = run(cmd,stdout=PIPE,stderr=PIPE)
 			cp = run(cmd,stdout=PIPE,stderr=PIPE)
 			vmsg(cp.stderr.decode())
 			vmsg(cp.stderr.decode())

+ 4 - 2
test/unit_tests_d/ut_devtools.py

@@ -78,10 +78,12 @@ class unit_tests(unit_tests_base):
 		from mmgen.protocol import init_proto
 		from mmgen.protocol import init_proto
 		from mmgen.seed import Seed
 		from mmgen.seed import Seed
 		from mmgen.addrlist import AddrList
 		from mmgen.addrlist import AddrList
+		from ..include.common import cfg
 		print_hdr('MMGenObject.pmsg()')
 		print_hdr('MMGenObject.pmsg()')
 		AddrList(
 		AddrList(
-			proto       = init_proto('btc'),
-			seed        = Seed(seed_bin=bytes.fromhex('bead'*16)),
+			cfg         = cfg,
+			proto       = init_proto( cfg, 'btc' ),
+			seed        = Seed(cfg,seed_bin=bytes.fromhex('bead'*16)),
 			addr_idxs   = '1',
 			addr_idxs   = '1',
 			mmtype      = 'B',
 			mmtype      = 'B',
 			skip_chksum = True ).pmsg()
 			skip_chksum = True ).pmsg()

+ 1 - 0
test/unit_tests_d/ut_flags.py

@@ -6,6 +6,7 @@ test.unit_tests_d.ut_flags: unit test for the MMGen suite's ClassFlags class
 
 
 from mmgen.common import *
 from mmgen.common import *
 from mmgen.flags import *
 from mmgen.flags import *
+from ..include.common import qmsg,qmsg_r,vmsg
 
 
 class unit_test(object):
 class unit_test(object):
 
 

+ 7 - 6
test/unit_tests_d/ut_gen.py

@@ -10,6 +10,7 @@ from mmgen.key import PrivKey
 from mmgen.addr import MMGenAddrType
 from mmgen.addr import MMGenAddrType
 from mmgen.addrgen import KeyGenerator,AddrGenerator
 from mmgen.addrgen import KeyGenerator,AddrGenerator
 from mmgen.keygen import get_backends
 from mmgen.keygen import get_backends
+from ..include.common import cfg,qmsg
 
 
 # TODO: add viewkey checks
 # TODO: add viewkey checks
 vectors = { # from tooltest2
 vectors = { # from tooltest2
@@ -50,7 +51,7 @@ vectors = { # from tooltest2
 def do_test(proto,wif,addr_chk,addr_type,internal_keccak):
 def do_test(proto,wif,addr_chk,addr_type,internal_keccak):
 
 
 	if internal_keccak:
 	if internal_keccak:
-		opt.use_internal_keccak_module = True
+		cfg.use_internal_keccak_module = True
 		add_msg = ' (internal keccak module)'
 		add_msg = ' (internal keccak module)'
 	else:
 	else:
 		add_msg = ''
 		add_msg = ''
@@ -60,7 +61,7 @@ def do_test(proto,wif,addr_chk,addr_type,internal_keccak):
 
 
 	for n,backend in enumerate(get_backends(at.pubkey_type)):
 	for n,backend in enumerate(get_backends(at.pubkey_type)):
 
 
-		kg = KeyGenerator(proto,at.pubkey_type,n+1)
+		kg = KeyGenerator( cfg, proto, at.pubkey_type, n+1 )
 		qmsg(blue(f'  Testing backend {backend!r} for addr type {addr_type!r}{add_msg}'))
 		qmsg(blue(f'  Testing backend {backend!r} for addr type {addr_type!r}{add_msg}'))
 
 
 		data = kg.gen_data(privkey)
 		data = kg.gen_data(privkey)
@@ -69,16 +70,16 @@ def do_test(proto,wif,addr_chk,addr_type,internal_keccak):
 			if v and k in ('pubkey','viewkey_bytes'):
 			if v and k in ('pubkey','viewkey_bytes'):
 				qmsg(f'    {k+":":19} {v.hex()}')
 				qmsg(f'    {k+":":19} {v.hex()}')
 
 
-		ag = AddrGenerator(proto,addr_type)
+		ag = AddrGenerator( cfg, proto, addr_type )
 		addr = ag.to_addr(data)
 		addr = ag.to_addr(data)
 		qmsg(f'    addr:               {addr}\n')
 		qmsg(f'    addr:               {addr}\n')
 
 
 		assert addr == addr_chk, f'{addr} != {addr_chk}'
 		assert addr == addr_chk, f'{addr} != {addr_chk}'
 
 
-	opt.use_internal_keccak_module = False
+	cfg.use_internal_keccak_module = False
 
 
 def do_tests(coin,internal_keccak=False):
 def do_tests(coin,internal_keccak=False):
-	proto = init_proto(coin)
+	proto = init_proto( cfg, coin )
 	for wif,addr,addr_type in vectors[coin]:
 	for wif,addr,addr_type in vectors[coin]:
 		do_test(proto,wif,addr,addr_type,internal_keccak)
 		do_test(proto,wif,addr,addr_type,internal_keccak)
 	return True
 	return True
@@ -95,7 +96,7 @@ class unit_tests:
 		return do_tests('eth',internal_keccak=True)
 		return do_tests('eth',internal_keccak=True)
 
 
 	def xmr(self,name,ut):
 	def xmr(self,name,ut):
-		if not opt.fast:
+		if not cfg.fast:
 			do_tests('xmr')
 			do_tests('xmr')
 		return do_tests('xmr',internal_keccak=True)
 		return do_tests('xmr',internal_keccak=True)
 
 

+ 1 - 0
test/unit_tests_d/ut_indexed_dict.py

@@ -5,6 +5,7 @@ test/unit_tests_d/ut_indexed_dict: IndexedDict class unit test for the MMGen sui
 """
 """
 
 
 from mmgen.common import *
 from mmgen.common import *
+from ..include.common import vmsg
 
 
 class unit_test(object):
 class unit_test(object):
 
 

+ 1 - 0
test/unit_tests_d/ut_lockable.py

@@ -5,6 +5,7 @@ test.unit_tests_d.ut_lockable: unit test for the MMGen suite's Lockable class
 """
 """
 
 
 from mmgen.common import *
 from mmgen.common import *
+from ..include.common import qmsg,qmsg_r,vmsg
 
 
 class unit_test(object):
 class unit_test(object):
 
 

+ 4 - 3
test/unit_tests_d/ut_mn_entry.py

@@ -4,7 +4,8 @@
 test.unit_tests_d.ut_mn_entry: Mnemonic user entry unit test for the MMGen suite
 test.unit_tests_d.ut_mn_entry: Mnemonic user entry unit test for the MMGen suite
 """
 """
 
 
-from mmgen.util import msg,msg_r,qmsg,qmsg_r
+from mmgen.util import msg,msg_r
+from ..include.common import cfg,qmsg
 
 
 class unit_test(object):
 class unit_test(object):
 
 
@@ -49,7 +50,7 @@ class unit_test(object):
 		usl = {}
 		usl = {}
 		for wl_id in self.vectors:
 		for wl_id in self.vectors:
 			for j,k in (('uniq_ss_len','usl'),('shortest_word','sw'),('longest_word','lw')):
 			for j,k in (('uniq_ss_len','usl'),('shortest_word','sw'),('longest_word','lw')):
-				a = getattr(mn_entry(wl_id),j)
+				a = getattr(mn_entry( cfg, wl_id ),j)
 				b = self.vectors[wl_id][k]
 				b = self.vectors[wl_id][k]
 				assert a == b, f'{wl_id}:{j} {a} != {b}'
 				assert a == b, f'{wl_id}:{j} {a} != {b}'
 		msg('OK')
 		msg('OK')
@@ -58,7 +59,7 @@ class unit_test(object):
 		qmsg('')
 		qmsg('')
 		junk = 'a g z aa gg zz aaa ggg zzz aaaa gggg zzzz aaaaaaaaaaaaaa gggggggggggggg zzzzzzzzzzzzzz'
 		junk = 'a g z aa gg zz aaa ggg zzz aaaa gggg zzzz aaaaaaaaaaaaaa gggggggggggggg zzzzzzzzzzzzzz'
 		for wl_id in self.vectors:
 		for wl_id in self.vectors:
-			m = mn_entry(wl_id)
+			m = mn_entry( cfg, wl_id )
 			qmsg('Wordlist: '+wl_id)
 			qmsg('Wordlist: '+wl_id)
 			for entry_mode in ('full','short'):
 			for entry_mode in ('full','short'):
 				for a,word in enumerate(m.wl):
 				for a,word in enumerate(m.wl):

+ 9 - 8
test/unit_tests_d/ut_msg.py

@@ -6,8 +6,7 @@ test.unit_tests_d.ut_msg: message signing unit tests for the MMGen suite
 
 
 import os
 import os
 
 
-from test.include.common import silence,end_silence,restart_test_daemons,stop_test_daemons
-from mmgen.opts import opt
+from ..include.common import cfg,silence,end_silence,restart_test_daemons,stop_test_daemons
 from mmgen.util import msg,bmsg,pumsg,suf
 from mmgen.util import msg,bmsg,pumsg,suf
 from mmgen.protocol import CoinProtocol
 from mmgen.protocol import CoinProtocol
 from mmgen.msg import NewMsg,UnsignedMsg,SignedMsg,SignedOnlineMsg,ExportedMsgSigs
 from mmgen.msg import NewMsg,UnsignedMsg,SignedMsg,SignedOnlineMsg,ExportedMsgSigs
@@ -24,6 +23,7 @@ def get_obj(coin,network,msghash_type):
 		addrlists = 'DEADBEEF:C:1-20 98831F3A:B:8,2 A091ABAA:S:10-11 A091ABAA:111 A091ABAA:C:1'
 		addrlists = 'DEADBEEF:C:1-20 98831F3A:B:8,2 A091ABAA:S:10-11 A091ABAA:111 A091ABAA:C:1'
 
 
 	return NewMsg(
 	return NewMsg(
+		cfg       = cfg,
 		coin      = coin,
 		coin      = coin,
 		network   = network,
 		network   = network,
 		message   = '08/Jun/2021 Bitcoin Law Enacted by El Salvador Legislative Assembly',
 		message   = '08/Jun/2021 Bitcoin Law Enacted by El Salvador Legislative Assembly',
@@ -37,7 +37,7 @@ async def run_test(network_id,chksum,msghash_type='raw'):
 
 
 	coin,network = CoinProtocol.Base.parse_network_id(network_id)
 	coin,network = CoinProtocol.Base.parse_network_id(network_id)
 
 
-	if not opt.verbose:
+	if not cfg.verbose:
 		silence()
 		silence()
 
 
 	bmsg(f'\nTesting {coin.upper()} {network.upper()}:\n')
 	bmsg(f'\nTesting {coin.upper()} {network.upper()}:\n')
@@ -61,17 +61,17 @@ async def run_test(network_id,chksum,msghash_type='raw'):
 
 
 	pumsg('\nTesting signing:\n')
 	pumsg('\nTesting signing:\n')
 
 
-	m = UnsignedMsg( infile = os.path.join(tmpdir,get_obj(coin,network,msghash_type).filename) )
+	m = UnsignedMsg( cfg, infile = os.path.join(tmpdir,get_obj(coin,network,msghash_type).filename) )
 	await m.sign(wallet_files=['test/ref/98831F3A.mmwords'])
 	await m.sign(wallet_files=['test/ref/98831F3A.mmwords'])
 
 
-	m = SignedMsg( data=m.__dict__ )
+	m = SignedMsg( cfg, data=m.__dict__ )
 	m.write_to_file(
 	m.write_to_file(
 		outdir        = tmpdir,
 		outdir        = tmpdir,
 		ask_overwrite = False )
 		ask_overwrite = False )
 
 
 	pumsg('\nTesting display:\n')
 	pumsg('\nTesting display:\n')
 
 
-	m = SignedOnlineMsg( infile = os.path.join(tmpdir,get_obj(coin,network,msghash_type).signed_filename) )
+	m = SignedOnlineMsg( cfg, infile = os.path.join(tmpdir,get_obj(coin,network,msghash_type).signed_filename) )
 
 
 	msg(m.format())
 	msg(m.format())
 
 
@@ -96,12 +96,13 @@ async def run_test(network_id,chksum,msghash_type='raw'):
 	from mmgen.fileutil import write_data_to_file
 	from mmgen.fileutil import write_data_to_file
 	exported_sigs = os.path.join(tmpdir,'signatures.json')
 	exported_sigs = os.path.join(tmpdir,'signatures.json')
 	write_data_to_file(
 	write_data_to_file(
+		cfg     = cfg,
 		outfile = exported_sigs,
 		outfile = exported_sigs,
 		data    = m.get_json_for_export(),
 		data    = m.get_json_for_export(),
 		desc    = 'signature data',
 		desc    = 'signature data',
 		ask_overwrite = False )
 		ask_overwrite = False )
 
 
-	m = ExportedMsgSigs( infile=exported_sigs )
+	m = ExportedMsgSigs( cfg, infile=exported_sigs )
 
 
 	pumsg('\nTesting verification (exported data):\n')
 	pumsg('\nTesting verification (exported data):\n')
 	print_total( await m.verify() )
 	print_total( await m.verify() )
@@ -120,7 +121,7 @@ async def run_test(network_id,chksum,msghash_type='raw'):
 
 
 	msg('\n')
 	msg('\n')
 
 
-	if not opt.verbose:
+	if not cfg.verbose:
 		end_silence()
 		end_silence()
 
 
 	return True
 	return True

+ 1 - 0
test/unit_tests_d/ut_obj.py

@@ -7,6 +7,7 @@ test.unit_tests_d.ut_obj: data object unit tests for the MMGen suite
 from decimal import Decimal
 from decimal import Decimal
 
 
 from mmgen.common import *
 from mmgen.common import *
+from ..include.common import qmsg,qmsg_r,vmsg
 
 
 class unit_tests:
 class unit_tests:
 
 

+ 23 - 20
test/unit_tests_d/ut_rpc.py

@@ -13,6 +13,7 @@ from mmgen.rpc import rpc_init
 from mmgen.daemon import CoinDaemon
 from mmgen.daemon import CoinDaemon
 from mmgen.proto.xmr.rpc import MoneroRPCClient,MoneroWalletRPCClient
 from mmgen.proto.xmr.rpc import MoneroRPCClient,MoneroWalletRPCClient
 from mmgen.proto.xmr.daemon import MoneroWalletDaemon
 from mmgen.proto.xmr.daemon import MoneroWalletDaemon
+from ..include.common import cfg,qmsg,vmsg
 
 
 def cfg_file_auth_test(proto,d,bad_auth=False):
 def cfg_file_auth_test(proto,d,bad_auth=False):
 	m = 'missing credentials' if bad_auth else f'credentials from {d.cfg_file}'
 	m = 'missing credentials' if bad_auth else f'credentials from {d.cfg_file}'
@@ -32,14 +33,14 @@ def cfg_file_auth_test(proto,d,bad_auth=False):
 	if bad_auth:
 	if bad_auth:
 		os.rename(d.auth_cookie_fn,d.auth_cookie_fn+'.bak')
 		os.rename(d.auth_cookie_fn,d.auth_cookie_fn+'.bak')
 		try:
 		try:
-			async_run(rpc_init(proto))
+			async_run(rpc_init(cfg,proto))
 		except Exception as e:
 		except Exception as e:
 			vmsg(yellow(str(e)))
 			vmsg(yellow(str(e)))
 		else:
 		else:
 			die(3,'No error on missing credentials!')
 			die(3,'No error on missing credentials!')
 		os.rename(d.auth_cookie_fn+'.bak',d.auth_cookie_fn)
 		os.rename(d.auth_cookie_fn+'.bak',d.auth_cookie_fn)
 	else:
 	else:
-		rpc = async_run(rpc_init(proto))
+		rpc = async_run(rpc_init(cfg,proto))
 		assert rpc.auth.user == 'ut_rpc', f'{rpc.auth.user}: user is not ut_rpc!'
 		assert rpc.auth.user == 'ut_rpc', f'{rpc.auth.user}: user is not ut_rpc!'
 
 
 	d.stop()
 	d.stop()
@@ -79,7 +80,7 @@ def do_msg(rpc,backend):
 class init_test:
 class init_test:
 
 
 	async def btc(proto,backend,daemon):
 	async def btc(proto,backend,daemon):
-		rpc = await rpc_init(proto,backend,daemon)
+		rpc = await rpc_init(cfg,proto,backend,daemon)
 		do_msg(rpc,backend)
 		do_msg(rpc,backend)
 
 
 		bh = (await rpc.call('getblockchaininfo',timeout=300))['bestblockhash']
 		bh = (await rpc.call('getblockchaininfo',timeout=300))['bestblockhash']
@@ -88,14 +89,14 @@ class init_test:
 		return rpc
 		return rpc
 
 
 	async def bch(proto,backend,daemon):
 	async def bch(proto,backend,daemon):
-		rpc = await rpc_init(proto,backend,daemon)
+		rpc = await rpc_init(cfg,proto,backend,daemon)
 		do_msg(rpc,backend)
 		do_msg(rpc,backend)
 		return rpc
 		return rpc
 
 
 	ltc = bch
 	ltc = bch
 
 
 	async def eth(proto,backend,daemon):
 	async def eth(proto,backend,daemon):
-		rpc = await rpc_init(proto,backend,daemon)
+		rpc = await rpc_init(cfg,proto,backend,daemon)
 		do_msg(rpc,backend)
 		do_msg(rpc,backend)
 		await rpc.call('eth_blockNumber',timeout=300)
 		await rpc.call('eth_blockNumber',timeout=300)
 		return rpc
 		return rpc
@@ -106,20 +107,20 @@ def run_test(network_ids,test_cf_auth=False,daemon_ids=None):
 
 
 	def do_test(d):
 	def do_test(d):
 
 
-		if not opt.no_daemon_stop:
+		if not cfg.no_daemon_stop:
 			d.stop()
 			d.stop()
 
 
-		if not opt.no_daemon_autostart:
+		if not cfg.no_daemon_autostart:
 			d.remove_datadir()
 			d.remove_datadir()
 			d.start()
 			d.start()
 
 
-		for n,backend in enumerate(g.autoset_opts['rpc_backend'].choices):
+		for n,backend in enumerate(cfg.autoset_opts['rpc_backend'].choices):
 			test = getattr(init_test,d.proto.coin.lower())
 			test = getattr(init_test,d.proto.coin.lower())
 			rpc = async_run(test(d.proto,backend,d))
 			rpc = async_run(test(d.proto,backend,d))
-			if not n and opt.verbose:
+			if not n and cfg.verbose:
 				print_daemon_info(rpc)
 				print_daemon_info(rpc)
 
 
-		if not opt.no_daemon_stop:
+		if not cfg.no_daemon_stop:
 			d.stop()
 			d.stop()
 
 
 		if test_cf_auth and gc.platform != 'win':
 		if test_cf_auth and gc.platform != 'win':
@@ -129,12 +130,12 @@ def run_test(network_ids,test_cf_auth=False,daemon_ids=None):
 		qmsg('')
 		qmsg('')
 
 
 	for network_id in network_ids:
 	for network_id in network_ids:
-		proto = init_proto(network_id=network_id)
+		proto = init_proto( cfg, network_id=network_id )
 		ids = (lambda x:
 		ids = (lambda x:
 			set(daemon_ids) & set(x) if daemon_ids else x
 			set(daemon_ids) & set(x) if daemon_ids else x
-			)(CoinDaemon.get_daemon_ids(proto.coin))
+			)(CoinDaemon.get_daemon_ids(cfg,proto.coin))
 		for daemon_id in ids:
 		for daemon_id in ids:
-			do_test( CoinDaemon(proto=proto,test_suite=True,daemon_id=daemon_id) )
+			do_test( CoinDaemon(cfg, proto=proto,test_suite=True,daemon_id=daemon_id) )
 
 
 	return True
 	return True
 
 
@@ -169,6 +170,7 @@ class unit_tests:
 
 
 		def test_monerod_rpc(md):
 		def test_monerod_rpc(md):
 			rpc = MoneroRPCClient(
 			rpc = MoneroRPCClient(
+				cfg    = cfg,
 				proto  = md.proto,
 				proto  = md.proto,
 				host   = md.host,
 				host   = md.host,
 				port   = md.rpc_port,
 				port   = md.rpc_port,
@@ -176,30 +178,31 @@ class unit_tests:
 				passwd = None,
 				passwd = None,
 				daemon = md,
 				daemon = md,
 			)
 			)
-			if opt.verbose:
+			if cfg.verbose:
 				print_daemon_info(rpc)
 				print_daemon_info(rpc)
 			rpc.call_raw('get_height')
 			rpc.call_raw('get_height')
 			rpc.call('get_last_block_header')
 			rpc.call('get_last_block_header')
 
 
 		async def run():
 		async def run():
-			networks = init_proto('xmr').networks
+			networks = init_proto( cfg, 'xmr' ).networks
 			daemons = [(
 			daemons = [(
-					CoinDaemon(proto=proto,test_suite=True),
+					CoinDaemon( cfg, proto=proto, test_suite=True ),
 					MoneroWalletDaemon(
 					MoneroWalletDaemon(
+						cfg        = cfg,
 						proto      = proto,
 						proto      = proto,
 						test_suite = True,
 						test_suite = True,
 						wallet_dir = 'test/trash2',
 						wallet_dir = 'test/trash2',
 						passwd     = 'ut_rpc_passw0rd' )
 						passwd     = 'ut_rpc_passw0rd' )
-				) for proto in (init_proto( 'xmr', network=network ) for network in networks) ]
+				) for proto in (init_proto( cfg, 'xmr', network=network ) for network in networks) ]
 
 
 			for md,wd in daemons:
 			for md,wd in daemons:
-				if not opt.no_daemon_autostart:
+				if not cfg.no_daemon_autostart:
 					md.start()
 					md.start()
 				wd.start()
 				wd.start()
 
 
 				test_monerod_rpc(md)
 				test_monerod_rpc(md)
 
 
-				c = MoneroWalletRPCClient(daemon=wd)
+				c = MoneroWalletRPCClient( cfg=cfg, daemon=wd )
 				fn = f'monero-{wd.network}-junk-wallet'
 				fn = f'monero-{wd.network}-junk-wallet'
 				qmsg(f'Creating {wd.network} wallet')
 				qmsg(f'Creating {wd.network} wallet')
 				c.call(
 				c.call(
@@ -213,7 +216,7 @@ class unit_tests:
 			for md,wd in daemons:
 			for md,wd in daemons:
 				wd.wait = False
 				wd.wait = False
 				await wd.rpc.stop_daemon()
 				await wd.rpc.stop_daemon()
-				if not opt.no_daemon_stop:
+				if not cfg.no_daemon_stop:
 					md.wait = False
 					md.wait = False
 					await md.rpc.stop_daemon()
 					await md.rpc.stop_daemon()
 
 

+ 11 - 10
test/unit_tests_d/ut_scrypt.py

@@ -4,7 +4,8 @@
 test.unit_tests_d.ut_scrypt: password hashing unit test for the MMGen suite
 test.unit_tests_d.ut_scrypt: password hashing unit test for the MMGen suite
 """
 """
 
 
-from ..include.common import *
+from ..include.common import cfg,qmsg,vmsg,omsg_r,silence,end_silence
+from mmgen.util import msg,msg_r
 
 
 class unit_test(object):
 class unit_test(object):
 
 
@@ -15,7 +16,7 @@ class unit_test(object):
 		qmsg('')
 		qmsg('')
 
 
 		from mmgen.crypto import Crypto
 		from mmgen.crypto import Crypto
-		crypto = Crypto()
+		crypto = Crypto(cfg)
 
 
 		salt = bytes.fromhex('f00f' * 16)
 		salt = bytes.fromhex('f00f' * 16)
 
 
@@ -35,7 +36,7 @@ class unit_test(object):
 			for pw_base,res in pws:
 			for pw_base,res in pws:
 				for pw in (pw_base,pw_base.encode()):
 				for pw in (pw_base,pw_base.encode()):
 					pw_disp = "'"+pw+"'" if type(pw) == str else "b'"+pw.decode()+"'"
 					pw_disp = "'"+pw+"'" if type(pw) == str else "b'"+pw.decode()+"'"
-					if opt.quiet:
+					if cfg.quiet:
 						omsg_r('.')
 						omsg_r('.')
 					else:
 					else:
 						msg_r(f'\n  password {pw_disp:9} ')
 						msg_r(f'\n  password {pw_disp:9} ')
@@ -47,32 +48,32 @@ class unit_test(object):
 				hp = str(hp)
 				hp = str(hp)
 				res = presets[hp]
 				res = presets[hp]
 				pw = 'φυβαρ'
 				pw = 'φυβαρ'
-				if opt.quiet:
+				if cfg.quiet:
 					omsg_r('.')
 					omsg_r('.')
 				else:
 				else:
 					msg_r(f'\n  {hp!r:3}: {crypto.hash_presets[hp]!r:12}  ')
 					msg_r(f'\n  {hp!r:3}: {crypto.hash_presets[hp]!r:12}  ')
 				st = time.time()
 				st = time.time()
 				ret = crypto.scrypt_hash_passphrase(pw,salt,hp).hex()
 				ret = crypto.scrypt_hash_passphrase(pw,salt,hp).hex()
 				t = time.time() - st
 				t = time.time() - st
-				vmsg('' if g.test_suite_deterministic else f'  {t:0.4f} secs')
+				vmsg('' if cfg.test_suite_deterministic else f'  {t:0.4f} secs')
 				assert ret == res, ret
 				assert ret == res, ret
 
 
-		if opt.quiet:
+		if cfg.quiet:
 			silence()
 			silence()
 
 
-		g.force_standalone_scrypt_module = False
+		cfg.force_standalone_scrypt_module = False
 		vmsg('Passwords (auto module selection):')
 		vmsg('Passwords (auto module selection):')
 		test_passwords()
 		test_passwords()
 		vmsg('Hash presets (auto module selection):')
 		vmsg('Hash presets (auto module selection):')
-		test_presets((1,2,3,4) if opt.fast else (1,2,3,4,5,6,7))
+		test_presets((1,2,3,4) if cfg.fast else (1,2,3,4,5,6,7))
 
 
-		g.force_standalone_scrypt_module = True
+		cfg.force_standalone_scrypt_module = True
 		vmsg('Passwords (force standalone scrypt module):')
 		vmsg('Passwords (force standalone scrypt module):')
 		test_passwords()
 		test_passwords()
 		vmsg('Hash presets (force standalone scrypt module):')
 		vmsg('Hash presets (force standalone scrypt module):')
 		test_presets((1,2,3))
 		test_presets((1,2,3))
 
 
-		if opt.quiet:
+		if cfg.quiet:
 			end_silence()
 			end_silence()
 
 
 		msg('OK')
 		msg('OK')

+ 7 - 6
test/unit_tests_d/ut_seedsplit.py

@@ -5,6 +5,7 @@ test/unit_tests_d/ut_seedsplit: seed splitting unit test for the MMGen suite
 """
 """
 
 
 from mmgen.common import *
 from mmgen.common import *
+from ..include.common import cfg,vmsg,vmsg_r
 
 
 class unit_test(object):
 class unit_test(object):
 
 
@@ -12,7 +13,7 @@ class unit_test(object):
 		from mmgen.seed import Seed
 		from mmgen.seed import Seed
 		from mmgen.seedsplit import SeedShareList,SeedShareIdx
 		from mmgen.seedsplit import SeedShareList,SeedShareIdx
 
 
-		g.debug_subseed = bool(opt.verbose)
+		cfg.debug_subseed = bool(cfg.verbose)
 
 
 		def basic_ops(master_idx):
 		def basic_ops(master_idx):
 			test_data = {
 			test_data = {
@@ -56,7 +57,7 @@ class unit_test(object):
 
 
 				for a,b,c,d,e,f,h,i,p in test_data[id_str if id_str is not None else 'default']:
 				for a,b,c,d,e,f,h,i,p in test_data[id_str if id_str is not None else 'default']:
 					seed_bin = bytes.fromhex('deadbeef' * a)
 					seed_bin = bytes.fromhex('deadbeef' * a)
-					seed = Seed(seed_bin)
+					seed = Seed( cfg, seed_bin )
 					assert seed.sid == b, seed.sid
 					assert seed.sid == b, seed.sid
 
 
 					for share_count,j,k,l,m in ((2,c,c,d,i),(5,e,f,h,p)):
 					for share_count,j,k,l,m in ((2,c,c,d,i),(5,e,f,h,p)):
@@ -91,7 +92,7 @@ class unit_test(object):
 
 
 						if master_idx:
 						if master_idx:
 							slist = [shares.get_share_by_idx(i+1,base_seed=True) for i in range(len(shares))]
 							slist = [shares.get_share_by_idx(i+1,base_seed=True) for i in range(len(shares))]
-							A = Seed.join_shares(slist,master_idx,id_str).sid
+							A = Seed.join_shares( cfg, slist, master_idx, id_str ).sid
 							assert A == b, A
 							assert A == b, A
 
 
 				msg('OK')
 				msg('OK')
@@ -100,7 +101,7 @@ class unit_test(object):
 			msg_r('Testing defaults and limits...')
 			msg_r('Testing defaults and limits...')
 
 
 			seed_bin = bytes.fromhex('deadbeef' * 8)
 			seed_bin = bytes.fromhex('deadbeef' * 8)
-			seed = Seed(seed_bin)
+			seed = Seed( cfg, seed_bin )
 
 
 			shares = seed.split(SeedShareIdx.max_val)
 			shares = seed.split(SeedShareIdx.max_val)
 			s = shares.format()
 			s = shares.format()
@@ -124,7 +125,7 @@ class unit_test(object):
 			vmsg('')
 			vmsg('')
 
 
 			seed_bin = bytes.fromhex(seed_hex)
 			seed_bin = bytes.fromhex(seed_hex)
-			seed = Seed(seed_bin)
+			seed = Seed( cfg, seed_bin )
 
 
 			SeedShareIdx.max_val = ss_count
 			SeedShareIdx.max_val = ss_count
 			shares = seed.split(ss_count,master_idx=master_idx)
 			shares = seed.split(ss_count,master_idx=master_idx)
@@ -147,7 +148,7 @@ class unit_test(object):
 			msg_r('Testing last share collisions with shortened Seed IDs')
 			msg_r('Testing last share collisions with shortened Seed IDs')
 			vmsg('')
 			vmsg('')
 			seed_bin = bytes.fromhex('2eadbeef'*8)
 			seed_bin = bytes.fromhex('2eadbeef'*8)
-			seed = Seed(seed_bin)
+			seed = Seed( cfg, seed_bin )
 			ssm_save = SeedShareIdx.max_val
 			ssm_save = SeedShareIdx.max_val
 			ssm = SeedShareIdx.max_val = 2048
 			ssm = SeedShareIdx.max_val = 2048
 			shares = SeedShareList(seed,count=ssm,id_str='foo',master_idx=1,debug_last_share=True)
 			shares = SeedShareList(seed,count=ssm,id_str='foo',master_idx=1,debug_last_share=True)

+ 10 - 9
test/unit_tests_d/ut_subseed.py

@@ -5,6 +5,7 @@ test/unit_tests_d/ut_subseed: subseed unit test for the MMGen suite
 """
 """
 
 
 from mmgen.common import *
 from mmgen.common import *
+from ..include.common import cfg,vmsg_r
 
 
 class unit_test(object):
 class unit_test(object):
 
 
@@ -23,7 +24,7 @@ class unit_test(object):
 				):
 				):
 
 
 				seed_bin = bytes.fromhex('deadbeef' * a)
 				seed_bin = bytes.fromhex('deadbeef' * a)
-				seed = Seed(seed_bin)
+				seed = Seed( cfg, seed_bin )
 				assert seed.sid == b, seed.sid
 				assert seed.sid == b, seed.sid
 
 
 				subseed = seed.subseed('2s')
 				subseed = seed.subseed('2s')
@@ -38,7 +39,7 @@ class unit_test(object):
 				assert subseed.idx == 10, subseed.idx
 				assert subseed.idx == 10, subseed.idx
 				assert subseed.ss_idx == h, subseed.ss_idx
 				assert subseed.ss_idx == h, subseed.ss_idx
 
 
-				seed2 = Seed(seed_bin)
+				seed2 = Seed( cfg, seed_bin )
 				ss2_list = seed2.subseeds
 				ss2_list = seed2.subseeds
 
 
 				seed2.subseeds._generate(1)
 				seed2.subseeds._generate(1)
@@ -96,31 +97,31 @@ class unit_test(object):
 
 
 			seed_bin = bytes.fromhex('deadbeef' * 8)
 			seed_bin = bytes.fromhex('deadbeef' * 8)
 
 
-			seed = Seed(seed_bin,nSubseeds=11)
+			seed = Seed( cfg, seed_bin, nSubseeds=11 )
 			seed.subseeds._generate()
 			seed.subseeds._generate()
 			ss = seed.subseeds
 			ss = seed.subseeds
 			assert len(ss.data['long']) == len(ss.data['short']), len(ss.data['short'])
 			assert len(ss.data['long']) == len(ss.data['short']), len(ss.data['short'])
 			assert len(ss) == 11, len(ss)
 			assert len(ss) == 11, len(ss)
 
 
-			seed = Seed(seed_bin)
+			seed = Seed( cfg, seed_bin )
 			seed.subseeds._generate()
 			seed.subseeds._generate()
 			ss = seed.subseeds
 			ss = seed.subseeds
 			assert len(ss.data['long']) == len(ss.data['short']), len(ss.data['short'])
 			assert len(ss.data['long']) == len(ss.data['short']), len(ss.data['short'])
 			assert len(ss) == nSubseeds, len(ss)
 			assert len(ss) == nSubseeds, len(ss)
 
 
-			seed = Seed(seed_bin)
+			seed = Seed( cfg, seed_bin )
 			seed.subseed_by_seed_id('EEEEEEEE')
 			seed.subseed_by_seed_id('EEEEEEEE')
 			ss = seed.subseeds
 			ss = seed.subseeds
 			assert len(ss.data['long']) == len(ss.data['short']), len(ss.data['short'])
 			assert len(ss.data['long']) == len(ss.data['short']), len(ss.data['short'])
 			assert len(ss) == nSubseeds, len(ss)
 			assert len(ss) == nSubseeds, len(ss)
 
 
-			seed = Seed(seed_bin)
+			seed = Seed( cfg, seed_bin )
 			subseed = seed.subseed_by_seed_id('803B165C')
 			subseed = seed.subseed_by_seed_id('803B165C')
 			assert len(ss.data['long']) == len(ss.data['short']), len(ss.data['short'])
 			assert len(ss.data['long']) == len(ss.data['short']), len(ss.data['short'])
 			assert subseed.sid == '803B165C', subseed.sid
 			assert subseed.sid == '803B165C', subseed.sid
 			assert subseed.idx == 3, subseed.idx
 			assert subseed.idx == 3, subseed.idx
 
 
-			seed = Seed(seed_bin)
+			seed = Seed( cfg, seed_bin )
 			subseed = seed.subseed_by_seed_id('803B165C',last_idx=1)
 			subseed = seed.subseed_by_seed_id('803B165C',last_idx=1)
 			assert len(ss.data['long']) == len(ss.data['short']), len(ss.data['short'])
 			assert len(ss.data['long']) == len(ss.data['short']), len(ss.data['short'])
 			assert subseed == None, subseed
 			assert subseed == None, subseed
@@ -160,14 +161,14 @@ class unit_test(object):
 			ss_count,ltr,last_sid,collisions_chk = (
 			ss_count,ltr,last_sid,collisions_chk = (
 				(SubSeedIdxRange.max_idx,'S','2788F26B',470),
 				(SubSeedIdxRange.max_idx,'S','2788F26B',470),
 				(49509,'L','8D1FE500',2)
 				(49509,'L','8D1FE500',2)
-			)[bool(opt.fast)]
+			)[bool(cfg.fast)]
 
 
 			last_idx = str(ss_count) + ltr
 			last_idx = str(ss_count) + ltr
 
 
 			msg_r(f'Testing Seed ID collisions ({ss_count} subseed pairs)...')
 			msg_r(f'Testing Seed ID collisions ({ss_count} subseed pairs)...')
 
 
 			seed_bin = bytes.fromhex('12abcdef' * 8) # 95B3D78D
 			seed_bin = bytes.fromhex('12abcdef' * 8) # 95B3D78D
-			seed = Seed(seed_bin)
+			seed = Seed( cfg, seed_bin )
 
 
 			seed.subseeds._generate(ss_count)
 			seed.subseeds._generate(ss_count)
 			ss = seed.subseeds
 			ss = seed.subseeds

+ 6 - 5
test/unit_tests_d/ut_tx.py

@@ -11,20 +11,21 @@ from mmgen.tx import NewTX,CompletedTX
 from mmgen.tx.file import MMGenTxFile
 from mmgen.tx.file import MMGenTxFile
 from mmgen.daemon import CoinDaemon
 from mmgen.daemon import CoinDaemon
 from mmgen.protocol import init_proto
 from mmgen.protocol import init_proto
+from ..include.common import cfg,qmsg,vmsg
 
 
 async def do_txfile_test(desc,fns):
 async def do_txfile_test(desc,fns):
 	qmsg(f'  Testing CompletedTX initializer ({desc})')
 	qmsg(f'  Testing CompletedTX initializer ({desc})')
 	for fn in fns:
 	for fn in fns:
 		qmsg(f'     parsing: {os.path.basename(fn)}')
 		qmsg(f'     parsing: {os.path.basename(fn)}')
 		fpath = os.path.join('test','ref',fn)
 		fpath = os.path.join('test','ref',fn)
-		tx = await CompletedTX(filename=fpath,quiet_open=True)
+		tx = await CompletedTX( cfg=cfg, filename=fpath, quiet_open=True )
 
 
 		vmsg(tx.info.format())
 		vmsg(tx.info.format())
 
 
 		f = MMGenTxFile(tx)
 		f = MMGenTxFile(tx)
 		fn_gen = f.make_filename()
 		fn_gen = f.make_filename()
 
 
-		if g.debug_utf8:
+		if cfg.debug_utf8:
 			fn_gen = fn_gen.replace('-α','')
 			fn_gen = fn_gen.replace('-α','')
 
 
 		assert fn_gen == os.path.basename(fn), f'{fn_gen} != {fn}'
 		assert fn_gen == os.path.basename(fn), f'{fn_gen} != {fn}'
@@ -57,11 +58,11 @@ class unit_tests:
 
 
 	async def tx(self,name,ut):
 	async def tx(self,name,ut):
 		qmsg('  Testing NewTX initializer')
 		qmsg('  Testing NewTX initializer')
-		d = CoinDaemon('btc',test_suite=True)
+		d = CoinDaemon( cfg, 'btc', test_suite=True )
 		d.start()
 		d.start()
 
 
-		proto = init_proto('btc',need_amt=True)
-		tx = await NewTX(proto=proto)
+		proto = init_proto( cfg, 'btc', need_amt=True )
+		tx = await NewTX( cfg=cfg, proto=proto )
 
 
 		d.stop()
 		d.stop()
 		qmsg('  OK')
 		qmsg('  OK')

+ 10 - 10
test/unit_tests_d/ut_tx_deserialize.py

@@ -7,7 +7,7 @@ test/unit_tests_d/ut_tx_deserialize: TX deserialization unit tests for the MMGen
 import os,json
 import os,json
 
 
 from mmgen.common import *
 from mmgen.common import *
-from ..include.common import *
+from ..include.common import cfg,start_test_daemons,stop_test_daemons
 from mmgen.protocol import init_proto
 from mmgen.protocol import init_proto
 from mmgen.tx import CompletedTX
 from mmgen.tx import CompletedTX
 from mmgen.proto.btc.tx.base import DeserializeTX
 from mmgen.proto.btc.tx.base import DeserializeTX
@@ -15,14 +15,14 @@ from mmgen.rpc import rpc_init
 from mmgen.daemon import CoinDaemon
 from mmgen.daemon import CoinDaemon
 
 
 def print_info(name,extra_desc):
 def print_info(name,extra_desc):
-	if opt.names:
+	if cfg.names:
 		Msg_r('{} {} {}'.format(
 		Msg_r('{} {} {}'.format(
 			purple('Testing'),
 			purple('Testing'),
 			cyan(f'{name} ({extra_desc})'),
 			cyan(f'{name} ({extra_desc})'),
-			'' if opt.quiet else '\n'))
+			'' if cfg.quiet else '\n'))
 	else:
 	else:
 		Msg_r(f'Testing {extra_desc}')
 		Msg_r(f'Testing {extra_desc}')
-		if not opt.quiet:
+		if not cfg.quiet:
 			Msg('')
 			Msg('')
 
 
 async def test_tx(tx_proto,tx_hex,desc,n):
 async def test_tx(tx_proto,tx_hex,desc,n):
@@ -34,17 +34,17 @@ async def test_tx(tx_proto,tx_hex,desc,n):
 				return True
 				return True
 		return False
 		return False
 
 
-	rpc = await rpc_init(proto=tx_proto)
+	rpc = await rpc_init( cfg, proto=tx_proto )
 	d = await rpc.call('decoderawtransaction',tx_hex)
 	d = await rpc.call('decoderawtransaction',tx_hex)
 
 
 	if has_nonstandard_outputs(d['vout']): return False
 	if has_nonstandard_outputs(d['vout']): return False
 
 
 	dt = DeserializeTX(tx_proto,tx_hex)
 	dt = DeserializeTX(tx_proto,tx_hex)
 
 
-	if opt.verbose:
+	if cfg.verbose:
 		Msg('\n\n================================ Core vector: ==================================')
 		Msg('\n\n================================ Core vector: ==================================')
-	Msg_r('.' if opt.quiet else f'{n:>3}) {desc}\n')
-	if opt.verbose:
+	Msg_r('.' if cfg.quiet else f'{n:>3}) {desc}\n')
+	if cfg.verbose:
 		Pmsg(d)
 		Pmsg(d)
 		Msg('\n------------------------------ MMGen deserialized: -----------------------------')
 		Msg('\n------------------------------ MMGen deserialized: -----------------------------')
 		Pmsg(dt._asdict())
 		Pmsg(dt._asdict())
@@ -88,7 +88,7 @@ async def do_mmgen_ref(daemons,fns,name,desc):
 	start_test_daemons(*daemons)
 	start_test_daemons(*daemons)
 	print_info(name,desc)
 	print_info(name,desc)
 	for n,fn in enumerate(fns):
 	for n,fn in enumerate(fns):
-		tx = await CompletedTX(filename=fn,quiet_open=True)
+		tx = await CompletedTX( cfg=cfg, filename=fn, quiet_open=True )
 		await test_tx(
 		await test_tx(
 			tx_proto = tx.proto,
 			tx_proto = tx.proto,
 			tx_hex   = tx.serialized,
 			tx_hex   = tx.serialized,
@@ -119,7 +119,7 @@ class unit_tests:
 		for e in core_data:
 		for e in core_data:
 			if type(e[0]) == list:
 			if type(e[0]) == list:
 				await test_tx(
 				await test_tx(
-					tx_proto = init_proto('btc',need_amt=True),
+					tx_proto = init_proto( cfg, 'btc', need_amt=True ),
 					tx_hex   = e[1],
 					tx_hex   = e[1],
 					desc     = desc,
 					desc     = desc,
 					n        = n )
 					n        = n )

+ 2 - 1
test/unit_tests_d/ut_xmrseed.py

@@ -5,6 +5,7 @@ test/unit_tests_d/ut_xmrseed: Monero mnemonic unit test for the MMGen suite
 """
 """
 
 
 from mmgen.common import *
 from mmgen.common import *
+from ..include.common import cfg,qmsg,vmsg
 
 
 class unit_test(object):
 class unit_test(object):
 
 
@@ -82,7 +83,7 @@ class unit_test(object):
 		from mmgen.xmrseed import xmrseed
 		from mmgen.xmrseed import xmrseed
 
 
 		b = xmrseed()
 		b = xmrseed()
-		b.check_wordlist()
+		b.check_wordlist(cfg)
 
 
 		try:
 		try:
 			from monero.wordlists.english import English
 			from monero.wordlists.english import English