common.py 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288
  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. test.include.common: Shared routines and data for the MMGen test suites
  20. """
  21. import os
  22. from subprocess import run,PIPE
  23. from mmgen.common import *
  24. from mmgen.fileutil import write_data_to_file,get_data_from_file
  25. def noop(*args,**kwargs):
  26. pass
  27. def set_globals(cfg):
  28. """
  29. make `cfg`, `qmsg`, `vmsg`, etc. available as globals to scripts by setting
  30. the module attr
  31. """
  32. import test.include.common as this
  33. this.cfg = cfg
  34. if cfg.quiet:
  35. this.qmsg = this.qmsg_r = noop
  36. else:
  37. this.qmsg = msg
  38. this.qmsg_r = msg_r
  39. if cfg.verbose:
  40. this.vmsg = msg
  41. this.vmsg_r = msg_r
  42. this.Vmsg = Msg
  43. this.Vmsg_r = Msg_r
  44. else:
  45. this.vmsg = this.vmsg_r = this.Vmsg = this.Vmsg_r = noop
  46. this.dmsg = msg if cfg.debug else noop
  47. def strip_ansi_escapes(s):
  48. import re
  49. return re.sub('\x1b' + r'\[[;0-9]+?m','',s)
  50. ascii_uc = ''.join(map(chr,list(range(65,91)))) # 26 chars
  51. ascii_lc = ''.join(map(chr,list(range(97,123)))) # 26 chars
  52. lat_accent = ''.join(map(chr,list(range(192,383)))) # 191 chars, L,S
  53. ru_uc = ''.join(map(chr,list(range(1040,1072)))) # 32 chars
  54. gr_uc = ''.join(map(chr,list(range(913,930)) + list(range(931,940)))) # 26 chars (930 is ctrl char)
  55. gr_uc_w_ctrl = ''.join(map(chr,list(range(913,940)))) # 27 chars, L,C
  56. lat_cyr_gr = lat_accent[:130:5] + ru_uc + gr_uc # 84 chars
  57. ascii_cyr_gr = ascii_uc + ru_uc + gr_uc # 84 chars
  58. utf8_text = '[α-$ample UTF-8 text-ω]' * 10 # 230 chars, L,N,P,S,Z
  59. utf8_combining = '[α-$ámple UTF-8 téxt-ω]' * 10 # L,N,P,S,Z,M
  60. utf8_ctrl = '[α-$ample\nUTF-8\ntext-ω]' * 10 # L,N,P,S,Z,C
  61. text_jp = '必要なのは、信用ではなく暗号化された証明に基づく電子取引システムであり、これにより希望する二者が信用できる第三者機関を介さずに直接取引できるよう' # 72 chars ('W'ide)
  62. text_zh = '所以,我們非常需要這樣一種電子支付系統,它基於密碼學原理而不基於信用,使得任何達成一致的雙方,能夠直接進行支付,從而不需要協力廠商仲介的參與。。' # 72 chars ('F'ull + 'W'ide)
  63. sample_text = 'The Times 03/Jan/2009 Chancellor on brink of second bailout for banks'
  64. sample_mn = {
  65. 'mmgen': { # 'able': 0, 'youth': 1625, 'after' == 'afternoon'[:5]
  66. 'mn': 'able cast forgive master funny gaze after afternoon million paint moral youth',
  67. 'hex': '0005685ab4e94cbe3b228cf92112bc5f',
  68. },
  69. 'bip39': { # len('sun') < uniq_ss_len
  70. 'mn': 'vessel ladder alter error federal sibling chat ability sun glass valve picture',
  71. 'hex': 'f30f8c1da665478f49b001d94c5fc452',
  72. },
  73. 'xmrseed': {
  74. 'mn': 'viewpoint donuts ardent template unveil agile meant unafraid urgent athlete rustled mime azure jaded hawk baby jagged haystack baby jagged haystack ramped oncoming point template',
  75. 'hex': 'e8164dda6d42bd1e261a3406b2038dcbddadbeefdeadbeefdeadbeefdeadbe0f',
  76. },
  77. }
  78. ref_kafile_pass = 'kafile password'
  79. ref_kafile_hash_preset = '1'
  80. def getrand(n):
  81. if cfg.test_suite_deterministic:
  82. from mmgen.test import fake_urandom
  83. return fake_urandom(n)
  84. else:
  85. return os.urandom(n)
  86. def getrandnum(n):
  87. return int(getrand(n).hex(),16)
  88. def getrandhex(n):
  89. return getrand(n).hex()
  90. def getrandnum_range(nbytes,rn_max):
  91. while True:
  92. rn = int(getrand(nbytes).hex(),16)
  93. if rn < rn_max:
  94. return rn
  95. def getrandstr(num_chars,no_space=False):
  96. n,m = (94,33) if no_space else (95,32)
  97. return ''.join( chr(i % n + m) for i in list(getrand(num_chars)) )
  98. def get_test_data_dir():
  99. return os.path.join('test','data_dir' + ('','-α')[bool(os.getenv('MMGEN_DEBUG_UTF8'))])
  100. # Windows uses non-UTF8 encodings in filesystem, so use raw bytes here
  101. def cleandir(d,do_msg=False):
  102. d_enc = d.encode()
  103. try: files = os.listdir(d_enc)
  104. except: return
  105. from shutil import rmtree
  106. if do_msg:
  107. gmsg(f'Cleaning directory {d!r}')
  108. for f in files:
  109. try:
  110. os.unlink(os.path.join(d_enc,f))
  111. except:
  112. rmtree(os.path.join(d_enc,f),ignore_errors=True)
  113. def mk_tmpdir(d):
  114. try:
  115. os.makedirs( d, mode=0o755, exist_ok=True )
  116. except OSError as e:
  117. if e.errno != 17:
  118. raise
  119. else:
  120. vmsg(f'Created directory {d!r}')
  121. def get_tmpfile(cfg,fn):
  122. return os.path.join(cfg['tmpdir'],fn)
  123. def write_to_file(fn,data,binary=False):
  124. write_data_to_file(
  125. cfg,
  126. fn,
  127. data,
  128. quiet = True,
  129. binary = binary,
  130. ignore_opt_outdir = True )
  131. def write_to_tmpfile(cfg,fn,data,binary=False):
  132. write_to_file( os.path.join(cfg['tmpdir'],fn), data=data, binary=binary )
  133. def read_from_file(fn,binary=False):
  134. return get_data_from_file( cfg, fn, quiet=True, binary=binary )
  135. def read_from_tmpfile(cfg,fn,binary=False):
  136. return read_from_file(os.path.join(cfg['tmpdir'],fn),binary=binary)
  137. def joinpath(*args,**kwargs):
  138. return os.path.join(*args,**kwargs)
  139. def ok():
  140. if cfg.profile:
  141. return
  142. if cfg.verbose or cfg.exact_output:
  143. gmsg('OK')
  144. else:
  145. msg(' OK')
  146. def cmp_or_die(s,t,desc=None):
  147. if s != t:
  148. die( 'TestSuiteFatalException',
  149. (f'For {desc}:\n' if desc else '') +
  150. f'ERROR: recoded data:\n{t!r}\ndiffers from original data:\n{s!r}'
  151. )
  152. def init_coverage():
  153. coverdir = os.path.join('test','trace')
  154. acc_file = os.path.join('test','trace.acc')
  155. try: os.mkdir(coverdir,0o755)
  156. except: pass
  157. return coverdir,acc_file
  158. def silence():
  159. if not (cfg.verbose or cfg.exact_output):
  160. gv.stdout = gv.stderr = open(os.devnull,'w')
  161. def end_silence():
  162. if not (cfg.verbose or cfg.exact_output):
  163. gv.stdout.close()
  164. gv.stdout = sys.stdout
  165. gv.stderr = sys.stderr
  166. def omsg(s):
  167. sys.stderr.write(s + '\n')
  168. def omsg_r(s):
  169. sys.stderr.write(s)
  170. sys.stderr.flush()
  171. def imsg(s):
  172. if cfg.verbose or cfg.exact_output:
  173. omsg(s)
  174. def imsg_r(s):
  175. if cfg.verbose or cfg.exact_output:
  176. omsg_r(s)
  177. def iqmsg(s):
  178. if not cfg.quiet:
  179. omsg(s)
  180. def iqmsg_r(s):
  181. if not cfg.quiet:
  182. omsg_r(s)
  183. def oqmsg(s):
  184. if not (cfg.verbose or cfg.exact_output):
  185. omsg(s)
  186. def oqmsg_r(s):
  187. if not (cfg.verbose or cfg.exact_output):
  188. omsg_r(s)
  189. def end_msg(t):
  190. omsg(green(
  191. 'All requested tests finished OK' +
  192. ('' if cfg.test_suite_deterministic else f', elapsed time: {t//60:02d}:{t%60:02d}')
  193. ))
  194. def start_test_daemons(*network_ids,remove_datadir=False):
  195. if not cfg.no_daemon_autostart:
  196. return test_daemons_ops(*network_ids,op='start',remove_datadir=remove_datadir)
  197. def stop_test_daemons(*network_ids,force=False,remove_datadir=False):
  198. if force or not cfg.no_daemon_stop:
  199. return test_daemons_ops(*network_ids,op='stop',remove_datadir=remove_datadir)
  200. def restart_test_daemons(*network_ids,remove_datadir=False):
  201. if not stop_test_daemons(*network_ids):
  202. return False
  203. return start_test_daemons(*network_ids,remove_datadir=remove_datadir)
  204. def test_daemons_ops(*network_ids,op,remove_datadir=False):
  205. if not cfg.no_daemon_autostart:
  206. from mmgen.daemon import CoinDaemon
  207. silent = not (cfg.verbose or cfg.exact_output)
  208. ret = False
  209. for network_id in network_ids:
  210. d = CoinDaemon(cfg,network_id,test_suite=True)
  211. if remove_datadir:
  212. d.stop(silent=True)
  213. d.remove_datadir()
  214. ret = d.cmd(op,silent=silent)
  215. return ret
  216. tested_solc_ver = '0.8.7'
  217. def check_solc_ver():
  218. cmd = 'python3 scripts/create-token.py --check-solc-version'
  219. try:
  220. cp = run(cmd.split(),check=False,stdout=PIPE)
  221. except Exception as e:
  222. die(4,f'Unable to execute {cmd!r}: {e}')
  223. res = cp.stdout.decode().strip()
  224. if cp.returncode == 0:
  225. omsg(
  226. orange(f'Found supported solc version {res}') if res == tested_solc_ver else
  227. yellow(f'WARNING: solc version ({res}) does not match tested version ({tested_solc_ver})')
  228. )
  229. return True
  230. else:
  231. omsg(yellow('Warning: Solidity compiler (solc) could not be executed or has unsupported version'))
  232. omsg(res)
  233. return False
  234. def get_ethkey():
  235. cmdnames = ('ethkey','openethereum-ethkey')
  236. for cmdname in cmdnames:
  237. try: run([cmdname,'--help'],stdout=PIPE)
  238. except: pass
  239. else:
  240. return cmdname
  241. else:
  242. die(1,f'ethkey executable not found (tried {cmdnames})')