util2.py 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157
  1. #!/usr/bin/env python3
  2. #
  3. # mmgen = Multi-Mode GENerator, a command-line cryptocurrency wallet
  4. # Copyright (C)2013-2023 The MMGen Project <mmgen@tuta.io>
  5. # Licensed under the GNU General Public License, Version 3:
  6. # https://www.gnu.org/licenses
  7. # Public project repositories:
  8. # https://github.com/mmgen/mmgen
  9. # https://gitlab.com/mmgen/mmgen
  10. """
  11. util2: Less frequently-used variables, classes and utility functions for the MMGen suite
  12. """
  13. import re,time
  14. from .util import msg,qmsg,suf,hexdigits
  15. def die_wait(delay,ev=0,s=''):
  16. assert isinstance(delay,int)
  17. assert isinstance(ev,int)
  18. if s:
  19. msg(s)
  20. time.sleep(delay)
  21. sys.exit(ev)
  22. def die_pause(ev=0,s=''):
  23. assert isinstance(ev,int)
  24. if s:
  25. msg(s)
  26. input('Press ENTER to exit')
  27. sys.exit(ev)
  28. def removeprefix(s,pfx): # workaround for pre-Python 3.9
  29. return s[len(pfx):] if s.startswith(pfx) else s
  30. def removesuffix(s,sfx): # workaround for pre-Python 3.9
  31. return s[:-len(sfx)] if s.endswith(sfx) else s
  32. def get_keccak(cached_ret=[]):
  33. if not cached_ret:
  34. from .opts import opt
  35. # called in opts.init() via CoinProtocol, so must use getattr():
  36. if getattr(opt,'use_internal_keccak_module',False):
  37. qmsg('Using internal keccak module by user request')
  38. from .contrib.keccak import keccak_256
  39. else:
  40. try:
  41. from sha3 import keccak_256
  42. except:
  43. from .contrib.keccak import keccak_256
  44. cached_ret.append(keccak_256)
  45. return cached_ret[0]
  46. # From 'man dd':
  47. # c=1, w=2, b=512, kB=1000, K=1024, MB=1000*1000, M=1024*1024,
  48. # GB=1000*1000*1000, G=1024*1024*1024, and so on for T, P, E, Z, Y.
  49. bytespec_map = (
  50. ('c', 1),
  51. ('w', 2),
  52. ('b', 512),
  53. ('kB', 1000),
  54. ('K', 1024),
  55. ('MB', 1000000),
  56. ('M', 1048576),
  57. ('GB', 1000000000),
  58. ('G', 1073741824),
  59. ('TB', 1000000000000),
  60. ('T', 1099511627776),
  61. ('PB', 1000000000000000),
  62. ('P', 1125899906842624),
  63. ('EB', 1000000000000000000),
  64. ('E', 1152921504606846976),
  65. )
  66. def int2bytespec(n,spec,fmt,print_sym=True,strip=False,add_space=False):
  67. def spec2int(spec):
  68. for k,v in bytespec_map:
  69. if k == spec:
  70. return v
  71. else:
  72. die('{spec}: unrecognized bytespec')
  73. if strip:
  74. ret = '{:{}f}'.format(n/spec2int(spec),fmt).rstrip('0')
  75. return ret + ('0' if ret.endswith('.') else '') + ((' ' if add_space else '') + spec if print_sym else '')
  76. else:
  77. return '{:{}f}'.format(n/spec2int(spec),fmt) + ((' ' if add_space else '') + spec if print_sym else '')
  78. def parse_bytespec(nbytes):
  79. m = re.match(r'([0123456789.]+)(.*)',nbytes)
  80. if m:
  81. if m.group(2):
  82. for k,v in bytespec_map:
  83. if k == m.group(2):
  84. from decimal import Decimal
  85. return int(Decimal(m.group(1)) * v)
  86. else:
  87. msg("Valid byte specifiers: '{}'".format("' '".join([i[0] for i in bytespec_map])))
  88. elif '.' in nbytes:
  89. raise ValueError('fractional bytes not allowed')
  90. else:
  91. return int(nbytes)
  92. die(1,f'{nbytes!r}: invalid byte specifier')
  93. def format_elapsed_hr(t,now=None,cached={}):
  94. e = int((now or time.time()) - t)
  95. if not e in cached:
  96. abs_e = abs(e)
  97. cached[e] = ' '.join(
  98. '{} {}{}'.format(n,desc,suf(n)) for desc,n in (
  99. ('day', abs_e // 86400),
  100. ('hour', abs_e // 3600 % 24),
  101. ('minute', abs_e // 60 % 60),
  102. ) if n
  103. ) + (' ago' if e > 0 else ' in the future') if abs_e // 60 else 'just now'
  104. return cached[e]
  105. def pretty_format(s,width=80,pfx=''):
  106. out = []
  107. while(s):
  108. if len(s) <= width:
  109. out.append(s)
  110. break
  111. i = s[:width].rfind(' ')
  112. out.append(s[:i])
  113. s = s[i+1:]
  114. return pfx + ('\n'+pfx).join(out)
  115. def block_format(data,gw=2,cols=8,line_nums=None,data_is_hex=False):
  116. assert line_nums in (None,'hex','dec'),"'line_nums' must be one of None, 'hex' or 'dec'"
  117. ln_fs = '{:06x}: ' if line_nums == 'hex' else '{:06}: '
  118. bytes_per_chunk = gw
  119. if data_is_hex:
  120. gw *= 2
  121. nchunks = len(data)//gw + bool(len(data)%gw)
  122. return ''.join(
  123. ('' if (line_nums == None or i % cols) else ln_fs.format(i*bytes_per_chunk))
  124. + data[i*gw:i*gw+gw]
  125. + (' ' if (not cols or (i+1) % cols) else '\n')
  126. for i in range(nchunks)
  127. ).rstrip() + '\n'
  128. def pretty_hexdump(data,gw=2,cols=8,line_nums=None):
  129. return block_format(data.hex(),gw,cols,line_nums,data_is_hex=True)
  130. def decode_pretty_hexdump(data):
  131. pat = re.compile(fr'^[{hexdigits}]+:\s+')
  132. lines = [pat.sub('',line) for line in data.splitlines()]
  133. try:
  134. return bytes.fromhex(''.join((''.join(lines).split())))
  135. except:
  136. msg('Data not in hexdump format')
  137. return False