globalvars.py 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395
  1. #!/usr/bin/env python3
  2. #
  3. # mmgen = Multi-Mode GENerator, command-line Bitcoin cold storage solution
  4. # Copyright (C)2013-2023 The MMGen Project <mmgen@tuta.io>
  5. #
  6. # This program is free software: you can redistribute it and/or modify
  7. # it under the terms of the GNU General Public License as published by
  8. # the Free Software Foundation, either version 3 of the License, or
  9. # (at your option) any later version.
  10. #
  11. # This program is distributed in the hope that it will be useful,
  12. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. # GNU General Public License for more details.
  15. #
  16. # You should have received a copy of the GNU General Public License
  17. # along with this program. If not, see <http://www.gnu.org/licenses/>.
  18. """
  19. globalvars: Constants and configuration options for the MMGen suite
  20. """
  21. import sys,os
  22. from collections import namedtuple
  23. from .base_obj import Lockable
  24. def die(exit_val,s=''):
  25. if s:
  26. sys.stderr.write(s+'\n')
  27. sys.exit(exit_val)
  28. class GlobalConfig(Lockable):
  29. """
  30. Set global vars to default values
  31. Globals are overridden in this order:
  32. 1 - config file
  33. 2 - environmental vars
  34. 3 - command line
  35. """
  36. _autolock = False
  37. _set_ok = ()
  38. _reset_ok = ('stdout','stderr','accept_defaults')
  39. _use_class_attr = True
  40. # Constants:
  41. proj_name = 'MMGen'
  42. proj_url = 'https://github.com/mmgen/mmgen'
  43. prog_name = os.path.basename(sys.argv[0])
  44. author = 'The MMGen Project'
  45. email = '<mmgen@tuta.io>'
  46. Cdates = '2013-2023'
  47. is_txprog = prog_name == 'mmgen-regtest' or prog_name.startswith('mmgen-tx')
  48. stdin_tty = sys.stdin.isatty()
  49. stdout = sys.stdout
  50. stderr = sys.stderr
  51. http_timeout = 60
  52. err_disp_timeout = 0.7
  53. short_disp_timeout = 0.3
  54. min_time_precision = 18
  55. # Variables - these might be altered at runtime:
  56. dfl_hash_preset = '3'
  57. usr_randchars = 30
  58. fee_adjust = 1.0
  59. fee_estimate_confs = 3
  60. # Constant vars - some of these might be overridden in opts.py, but they don't change thereafter
  61. coin = ''
  62. token = ''
  63. debug = False
  64. debug_opts = False
  65. debug_rpc = False
  66. debug_addrlist = False
  67. debug_subseed = False
  68. debug_tw = False
  69. quiet = False
  70. no_license = False
  71. force_color = False
  72. force_256_color = False
  73. testnet = False
  74. regtest = False
  75. accept_defaults = False
  76. autochg_ignore_labels = False
  77. # rpc:
  78. rpc_host = ''
  79. rpc_port = 0
  80. rpc_user = ''
  81. rpc_password = ''
  82. ignore_daemon_version = False
  83. monero_wallet_rpc_host = 'localhost'
  84. monero_wallet_rpc_user = 'monero'
  85. monero_wallet_rpc_password = ''
  86. aiohttp_rpc_queue_len = 16
  87. cached_balances = False
  88. # regtest:
  89. bob = False
  90. alice = False
  91. carol = False
  92. regtest_user = None
  93. # miscellaneous features:
  94. use_internal_keccak_module = False
  95. enable_erigon = False
  96. # test suite:
  97. bogus_send = False
  98. bogus_unspent_data = ''
  99. debug_utf8 = False
  100. exec_wrapper = False
  101. test_suite = False
  102. test_suite_autosign_led_simulate = False
  103. test_suite_cfgtest = False
  104. test_suite_deterministic = False
  105. test_suite_pexpect = False
  106. test_suite_popen_spawn = False
  107. hold_protect_disable = False
  108. mnemonic_entry_modes = {}
  109. # display:
  110. scroll = False
  111. columns = 0
  112. color = bool(
  113. ( sys.stdout.isatty() and not os.getenv('MMGEN_TEST_SUITE_PEXPECT') ) or
  114. os.getenv('MMGEN_FORCE_COLOR')
  115. )
  116. for k in ('linux','win','msys'):
  117. if sys.platform.startswith(k):
  118. platform = { 'linux':'linux', 'win':'win', 'msys':'win' }[k]
  119. break
  120. else:
  121. die(1,f'{sys.platform!r}: platform not supported by {proj_name}')
  122. if os.getenv('HOME'): # Linux or MSYS2
  123. home_dir = os.getenv('HOME')
  124. elif platform == 'win': # Windows without MSYS2 - not supported
  125. die(1,f'$HOME not set! {proj_name} for Windows must be run in MSYS2 environment')
  126. else:
  127. die(2,'$HOME is not set! Unable to determine home directory')
  128. daemon_data_dir = '' # set by user
  129. daemon_id = ''
  130. blacklisted_daemons = ''
  131. # must match CoinProtocol.coins
  132. core_coins = ('btc','bch','ltc','eth','etc','zec','xmr')
  133. # global var sets user opt:
  134. global_sets_opt = (
  135. 'autochg_ignore_labels',
  136. 'debug',
  137. 'minconf',
  138. 'quiet',
  139. 'fee_estimate_confs',
  140. 'fee_adjust',
  141. 'use_internal_keccak_module',
  142. 'usr_randchars' )
  143. # user opt sets global var:
  144. opt_sets_global = ( 'cached_balances', )
  145. # 'long' opts (subset of common_opts_data):
  146. common_opts = (
  147. 'accept_defaults',
  148. 'aiohttp_rpc_queue_len',
  149. 'bob',
  150. 'alice',
  151. 'carol',
  152. 'coin',
  153. 'color',
  154. 'columns',
  155. 'daemon_data_dir',
  156. 'daemon_id',
  157. 'force_256_color',
  158. 'http_timeout',
  159. 'ignore_daemon_version',
  160. 'no_license',
  161. 'regtest',
  162. 'rpc_host',
  163. 'rpc_password',
  164. 'rpc_port',
  165. 'rpc_user',
  166. 'scroll',
  167. 'testnet',
  168. 'token' )
  169. # opts not in common_opts but required to be set during opts initialization
  170. init_opts = ('show_hash_presets','yes','verbose')
  171. incompatible_opts = (
  172. ('help','longhelp'),
  173. ('bob','alice','carol'),
  174. ('label','keep_label'),
  175. ('tx_id','info'),
  176. ('tx_id','terse_info'),
  177. )
  178. cfg_file_opts = (
  179. 'autochg_ignore_labels',
  180. 'color',
  181. 'daemon_data_dir',
  182. 'debug',
  183. 'fee_adjust',
  184. 'force_256_color',
  185. 'hash_preset',
  186. 'http_timeout',
  187. 'max_input_size',
  188. 'max_tx_file_size',
  189. 'mnemonic_entry_modes',
  190. 'monero_wallet_rpc_host',
  191. 'monero_wallet_rpc_password',
  192. 'monero_wallet_rpc_user',
  193. 'no_license',
  194. 'quiet',
  195. 'regtest',
  196. 'rpc_host',
  197. 'rpc_password',
  198. 'rpc_port',
  199. 'rpc_user',
  200. 'scroll',
  201. 'subseeds',
  202. 'testnet',
  203. 'usr_randchars',
  204. 'bch_max_tx_fee',
  205. 'btc_max_tx_fee',
  206. 'eth_max_tx_fee',
  207. 'ltc_max_tx_fee',
  208. 'bch_ignore_daemon_version',
  209. 'btc_ignore_daemon_version',
  210. 'etc_ignore_daemon_version',
  211. 'eth_ignore_daemon_version',
  212. 'ltc_ignore_daemon_version',
  213. 'xmr_ignore_daemon_version',
  214. 'eth_mainnet_chain_names',
  215. 'eth_testnet_chain_names' )
  216. # Supported environmental vars
  217. # The corresponding vars (lowercase, minus 'mmgen_') must be initialized in g
  218. # 'DISABLE_' env vars disable the corresponding var in g
  219. env_opts = (
  220. 'MMGEN_DEBUG_ALL', # special: there is no g.debug_all var
  221. 'MMGEN_COLUMNS',
  222. 'MMGEN_TEST_SUITE',
  223. 'MMGEN_TEST_SUITE_AUTOSIGN_LED_SIMULATE',
  224. 'MMGEN_TEST_SUITE_CFGTEST',
  225. 'MMGEN_TEST_SUITE_DETERMINISTIC',
  226. 'MMGEN_TEST_SUITE_PEXPECT',
  227. 'MMGEN_TEST_SUITE_POPEN_SPAWN',
  228. 'MMGEN_BLACKLIST_DAEMONS',
  229. 'MMGEN_BOGUS_SEND',
  230. 'MMGEN_BOGUS_UNSPENT_DATA',
  231. 'MMGEN_DEBUG',
  232. 'MMGEN_DEBUG_OPTS',
  233. 'MMGEN_DEBUG_RPC',
  234. 'MMGEN_DEBUG_ADDRLIST',
  235. 'MMGEN_DEBUG_TW',
  236. 'MMGEN_DEBUG_UTF8',
  237. 'MMGEN_DEBUG_SUBSEED',
  238. 'MMGEN_FORCE_COLOR',
  239. 'MMGEN_FORCE_256_COLOR',
  240. 'MMGEN_HOLD_PROTECT_DISABLE',
  241. 'MMGEN_QUIET',
  242. 'MMGEN_NO_LICENSE',
  243. 'MMGEN_RPC_HOST',
  244. 'MMGEN_RPC_FAIL_ON_COMMAND',
  245. 'MMGEN_TESTNET',
  246. 'MMGEN_REGTEST',
  247. 'MMGEN_EXEC_WRAPPER',
  248. 'MMGEN_RPC_BACKEND',
  249. 'MMGEN_IGNORE_DAEMON_VERSION',
  250. 'MMGEN_USE_STANDALONE_SCRYPT_MODULE',
  251. 'MMGEN_ENABLE_ERIGON',
  252. 'MMGEN_DISABLE_COLOR',
  253. )
  254. infile_opts = (
  255. 'keys_from_file',
  256. 'mmgen_keys_from_file',
  257. 'passwd_file',
  258. 'keysforaddrs',
  259. 'comment_file',
  260. 'contract_data',
  261. )
  262. # Auto-typechecked and auto-set opts - first value in list is the default
  263. _ov = namedtuple('autoset_opt_info',['type','choices'])
  264. autoset_opts = {
  265. 'fee_estimate_mode': _ov('nocase_pfx', ['conservative','economical']),
  266. 'rpc_backend': _ov('nocase_pfx', ['auto','httplib','curl','aiohttp','requests']),
  267. }
  268. if platform == 'win':
  269. _skip_type_check = ('stdout','stderr')
  270. auto_typeset_opts = {
  271. 'seed_len': int,
  272. 'subseeds': int,
  273. 'vsize_adj': float,
  274. }
  275. min_screen_width = 80
  276. minconf = 1
  277. max_tx_file_size = 100000
  278. max_input_size = 1024 * 1024
  279. passwd_max_tries = 5
  280. max_urandchars = 80
  281. min_urandchars = 10
  282. force_standalone_scrypt_module = False
  283. if os.getenv('MMGEN_TEST_SUITE'):
  284. min_urandchars = 3
  285. err_disp_timeout = 0.1
  286. short_disp_timeout = 0.1
  287. if os.getenv('MMGEN_TEST_SUITE_POPEN_SPAWN'):
  288. stdin_tty = True
  289. if prog_name == 'unit_tests.py':
  290. _set_ok += ('debug_subseed',)
  291. _reset_ok += ('force_standalone_scrypt_module',)
  292. if os.getenv('MMGEN_DEBUG_ALL'):
  293. for name in env_opts:
  294. if name[:11] == 'MMGEN_DEBUG':
  295. os.environ[name] = '1'
  296. def get_mmgen_data_file(self,filename,package='mmgen'):
  297. """
  298. this is an expensive import, so do only when required
  299. """
  300. # Resource will be unpacked and then cleaned up if necessary, see:
  301. # https://docs.python.org/3/library/importlib.html:
  302. # Note: This module provides functionality similar to pkg_resources Basic
  303. # Resource Access without the performance overhead of that package.
  304. # https://importlib-resources.readthedocs.io/en/latest/migration.html
  305. # https://setuptools.readthedocs.io/en/latest/pkg_resources.html
  306. try:
  307. from importlib.resources import files # Python 3.9
  308. except ImportError:
  309. from importlib_resources import files
  310. return files(package).joinpath('data',filename).read_text()
  311. @property
  312. def version(self):
  313. return self.get_mmgen_data_file(
  314. filename = 'version',
  315. package = 'mmgen_node_tools' if self.prog_name.startswith('mmnode-') else 'mmgen'
  316. ).strip()
  317. @property
  318. def release_date(self):
  319. return self.get_mmgen_data_file(filename='release_date').strip()
  320. @property
  321. def data_dir_root(self):
  322. """
  323. location of mmgen.cfg
  324. """
  325. if hasattr(self,'_data_dir_root'):
  326. return self._data_dir_root
  327. else:
  328. if self.data_dir_root_override:
  329. self._data_dir_root = os.path.normpath(os.path.abspath(self.data_dir_root_override))
  330. elif self.test_suite:
  331. from test.include.common import get_test_data_dir
  332. self._data_dir_root = get_test_data_dir()
  333. else:
  334. self._data_dir_root = os.path.join(self.home_dir,'.'+self.proj_name.lower())
  335. return self._data_dir_root
  336. @property
  337. def data_dir(self):
  338. """
  339. location of wallet and other data - same as data_dir_root for mainnet
  340. """
  341. if hasattr(self,'_data_dir'):
  342. return self._data_dir
  343. else:
  344. self._data_dir = os.path.normpath(os.path.join(*{
  345. 'regtest': (self.data_dir_root, 'regtest', self.coin.lower(), (self.regtest_user or 'none') ),
  346. 'testnet': (self.data_dir_root, 'testnet'),
  347. 'mainnet': (self.data_dir_root,),
  348. }[self.network] ))
  349. return self._data_dir
  350. g = GlobalConfig()