util.py 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839
  1. #!/usr/bin/env python
  2. #
  3. # mmgen = Multi-Mode GENerator, command-line Bitcoin cold storage solution
  4. # Copyright (C)2013-2017 Philemon <mmgen-py@yandex.com>
  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. util.py: Low-level routines imported by other modules in the MMGen suite
  20. """
  21. import sys,os,time,stat,re
  22. from hashlib import sha256
  23. from binascii import hexlify,unhexlify
  24. from string import hexdigits
  25. from mmgen.color import *
  26. def msg(s): sys.stderr.write(s.encode('utf8') + '\n')
  27. def msg_r(s): sys.stderr.write(s.encode('utf8'))
  28. def Msg(s): sys.stdout.write(s.encode('utf8') + '\n')
  29. def Msg_r(s): sys.stdout.write(s.encode('utf8'))
  30. def msgred(s): msg(red(s))
  31. def mmsg(*args):
  32. for d in args: Msg(repr(d))
  33. def mdie(*args):
  34. mmsg(*args); sys.exit(0)
  35. def die_wait(delay,ev=0,s=''):
  36. assert type(delay) == int
  37. assert type(ev) == int
  38. if s: msg(s)
  39. time.sleep(delay)
  40. sys.exit(ev)
  41. def die_pause(ev=0,s=''):
  42. assert type(ev) == int
  43. if s: msg(s)
  44. raw_input('Press ENTER to exit')
  45. sys.exit(ev)
  46. def die(ev=0,s=''):
  47. assert type(ev) == int
  48. if s: msg(s)
  49. sys.exit(ev)
  50. def Die(ev=0,s=''):
  51. assert type(ev) == int
  52. if s: Msg(s)
  53. sys.exit(ev)
  54. def rdie(ev=0,s=''): die(ev,red(s))
  55. def wdie(ev=0,s=''): die(ev,yellow(s))
  56. def hi(): sys.stdout.write(yellow('hi'))
  57. def pformat(d):
  58. import pprint
  59. return pprint.PrettyPrinter(indent=4).pformat(d)
  60. def pmsg(*args):
  61. if not args: return
  62. Msg(pformat(args if len(args) > 1 else args[0]))
  63. def pdie(*args):
  64. if not args: sys.exit(1)
  65. Die(1,(pformat(args if len(args) > 1 else args[0])))
  66. def set_for_type(val,refval,desc,invert_bool=False,src=None):
  67. src_str = (''," in '{}'".format(src))[bool(src)]
  68. if type(refval) == bool:
  69. v = str(val).lower()
  70. if v in ('true','yes','1'): ret = True
  71. elif v in ('false','no','none','0'): ret = False
  72. else: die(1,"'{}': invalid value for '{}'{} (must be of type '{}')".format(
  73. val,desc,src_str,'bool'))
  74. if invert_bool: ret = not ret
  75. else:
  76. try:
  77. ret = type(refval)((val,not val)[invert_bool])
  78. except:
  79. die(1,"'{}': invalid value for '{}'{} (must be of type '{}')".format(
  80. val,desc,src_str,type(refval).__name__))
  81. return ret
  82. # From 'man dd':
  83. # c=1, w=2, b=512, kB=1000, K=1024, MB=1000*1000, M=1024*1024,
  84. # GB=1000*1000*1000, G=1024*1024*1024, and so on for T, P, E, Z, Y.
  85. def parse_nbytes(nbytes):
  86. import re
  87. m = re.match(r'([0123456789]+)(.*)',nbytes)
  88. smap = ('c',1),('w',2),('b',512),('kB',1000),('K',1024),('MB',1000*1000),\
  89. ('M',1024*1024),('GB',1000*1000*1000),('G',1024*1024*1024)
  90. if m:
  91. if m.group(2):
  92. for k,v in smap:
  93. if k == m.group(2):
  94. return int(m.group(1)) * v
  95. else:
  96. msg("Valid byte specifiers: '%s'" % "' '".join([i[0] for i in smap]))
  97. else:
  98. return int(nbytes)
  99. die(1,"'%s': invalid byte specifier" % nbytes)
  100. def check_or_create_dir(path):
  101. try:
  102. os.listdir(path)
  103. except:
  104. try:
  105. os.makedirs(path,0700)
  106. except:
  107. die(2,"ERROR: unable to read or create path '{}'".format(path))
  108. from mmgen.opts import opt
  109. def qmsg(s,alt=None):
  110. if opt.quiet:
  111. if alt != None: msg(alt)
  112. else: msg(s)
  113. def qmsg_r(s,alt=None):
  114. if opt.quiet:
  115. if alt != None: msg_r(alt)
  116. else: msg_r(s)
  117. def vmsg(s,force=False):
  118. if opt.verbose or force: msg(s)
  119. def vmsg_r(s,force=False):
  120. if opt.verbose or force: msg_r(s)
  121. def Vmsg(s,force=False):
  122. if opt.verbose or force: Msg(s)
  123. def Vmsg_r(s,force=False):
  124. if opt.verbose or force: Msg_r(s)
  125. def dmsg(s):
  126. if opt.debug: msg(s)
  127. def suf(arg,suf_type):
  128. suf_types = { 's': ('s',''), 'es': ('es','') }
  129. assert suf_type in suf_types
  130. t = type(arg)
  131. if t == int:
  132. n = arg
  133. elif any(issubclass(t,c) for c in (list,tuple,set,dict)):
  134. n = len(arg)
  135. else:
  136. die(2,'%s: invalid parameter for suf()' % arg)
  137. return suf_types[suf_type][n==1]
  138. def get_extension(f):
  139. a,b = os.path.splitext(f)
  140. return ('',b[1:])[len(b) > 1]
  141. def remove_extension(f,e):
  142. a,b = os.path.splitext(f)
  143. return (f,a)[len(b)>1 and b[1:]==e]
  144. def make_chksum_N(s,nchars,sep=False):
  145. if nchars%4 or not (4 <= nchars <= 64): return False
  146. s = sha256(sha256(s).digest()).hexdigest().upper()
  147. sep = ('',' ')[bool(sep)]
  148. return sep.join([s[i*4:i*4+4] for i in range(nchars/4)])
  149. def make_chksum_8(s,sep=False):
  150. from mmgen.obj import HexStr
  151. s = HexStr(sha256(sha256(s).digest()).hexdigest()[:8].upper(),case='upper')
  152. return '{} {}'.format(s[:4],s[4:]) if sep else s
  153. def make_chksum_6(s):
  154. from mmgen.obj import HexStr
  155. return HexStr(sha256(s).hexdigest()[:6])
  156. def is_chksum_6(s): return len(s) == 6 and is_hex_str_lc(s)
  157. def make_iv_chksum(s): return sha256(s).hexdigest()[:8].upper()
  158. def splitN(s,n,sep=None): # always return an n-element list
  159. ret = s.split(sep,n-1)
  160. return ret + ['' for i in range(n-len(ret))]
  161. def split2(s,sep=None): return splitN(s,2,sep) # always return a 2-element list
  162. def split3(s,sep=None): return splitN(s,3,sep) # always return a 3-element list
  163. def split_into_cols(col_wid,s):
  164. return ' '.join([s[col_wid*i:col_wid*(i+1)]
  165. for i in range(len(s)/col_wid+1)]).rstrip()
  166. def capfirst(s): # different from str.capitalize() - doesn't downcase any uc in string
  167. return s if len(s) == 0 else s[0].upper() + s[1:]
  168. def decode_timestamp(s):
  169. # with open('/etc/timezone') as f:
  170. # tz_save = f.read().rstrip()
  171. os.environ['TZ'] = 'UTC'
  172. ts = time.strptime(s,'%Y%m%d_%H%M%S')
  173. t = time.mktime(ts)
  174. # os.environ['TZ'] = tz_save
  175. return int(t)
  176. def make_timestamp(secs=None):
  177. t = int(secs) if secs else time.time()
  178. tv = time.gmtime(t)[:6]
  179. return '{:04d}{:02d}{:02d}_{:02d}{:02d}{:02d}'.format(*tv)
  180. def make_timestr(secs=None):
  181. t = int(secs) if secs else time.time()
  182. tv = time.gmtime(t)[:6]
  183. return '{:04d}/{:02d}/{:02d} {:02d}:{:02d}:{:02d}'.format(*tv)
  184. def secs_to_hms(secs):
  185. return '{:02d}:{:02d}:{:02d}'.format(secs/3600, (secs/60) % 60, secs % 60)
  186. def secs_to_ms(secs):
  187. return '{:02d}:{:02d}'.format(secs/60, secs % 60)
  188. def is_int(s):
  189. try:
  190. int(str(s))
  191. return True
  192. except:
  193. return False
  194. # https://en.wikipedia.org/wiki/Base32#RFC_4648_Base32_alphabet
  195. # https://tools.ietf.org/html/rfc4648
  196. def is_hex_str(s): return set(list(s.lower())) <= set(list(hexdigits.lower()))
  197. def is_hex_str_lc(s): return set(list(s)) <= set(list(hexdigits.lower()))
  198. def is_hex_str_uc(s): return set(list(s)) <= set(list(hexdigits.upper()))
  199. def is_b58_str(s): return set(list(s)) <= set(baseconv.digits['b58'])
  200. def is_b32_str(s): return set(list(s)) <= set(baseconv.digits['b32'])
  201. def is_ascii(s,enc='ascii'):
  202. try: s.decode(enc)
  203. except: return False
  204. else: return True
  205. def is_utf8(s): return is_ascii(s,enc='utf8')
  206. class baseconv(object):
  207. mn_base = 1626 # tirosh list is 1633 words long!
  208. digits = {
  209. 'electrum': tuple(__import__('mmgen.mn_electrum',fromlist=['words']).words.split()),
  210. 'tirosh': tuple(__import__('mmgen.mn_tirosh',fromlist=['words']).words.split()[:mn_base]),
  211. 'b58': tuple('123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz'),
  212. 'b32': tuple('ABCDEFGHIJKLMNOPQRSTUVWXYZ234567'),
  213. 'b16': tuple('0123456789abcdef'),
  214. 'b10': tuple('0123456789'),
  215. 'b8': tuple('01234567'),
  216. }
  217. wl_chksums = {
  218. 'electrum': '5ca31424',
  219. 'tirosh': '48f05e1f', # tirosh truncated to mn_base (1626)
  220. # 'tirosh1633': '1a5faeff'
  221. }
  222. b58pad_lens = [(16,22), (24,33), (32,44)]
  223. b58pad_lens_rev = [(v,k) for k,v in b58pad_lens]
  224. @classmethod
  225. def b58encode(cls,s,pad=None):
  226. pad = cls.get_pad(s,pad,'en',cls.b58pad_lens,[bytes])
  227. return ''.join(cls.fromhex(hexlify(s),'b58',pad=pad))
  228. @classmethod
  229. def b58decode(cls,s,pad=None):
  230. pad = cls.get_pad(s,pad,'de',cls.b58pad_lens_rev,[bytes,unicode])
  231. return unhexlify(cls.tohex(s,'b58',pad=pad*2 if pad else None))
  232. @staticmethod
  233. def get_pad(s,pad,op,pad_map,ok_types):
  234. m = "b58{}code() input must be one of {}, not '{}'"
  235. assert type(s) in ok_types, m.format(op,repr([t.__name__ for t in ok_types]),type(s).__name__)
  236. if pad:
  237. assert type(pad) == bool, "'pad' must be boolean type"
  238. d = dict(pad_map)
  239. assert len(s) in d, 'Invalid data length for b58{}code(pad=True)'.format(op)
  240. return d[len(s)]
  241. else:
  242. return None
  243. @classmethod
  244. def get_wordlist_chksum(cls,wl_id):
  245. return sha256(' '.join(cls.digits[wl_id])).hexdigest()[:8]
  246. @classmethod
  247. def check_wordlists(cls):
  248. for k,v in cls.wl_chksums.items(): assert cls.get_wordlist_chksum(k) == v
  249. @classmethod
  250. def check_wordlist(cls,wl_id):
  251. wl = baseconv.digits[wl_id]
  252. Msg('Wordlist: %s\nLength: %i words' % (capfirst(wl_id),len(wl)))
  253. new_chksum = cls.get_wordlist_chksum(wl_id)
  254. a,b = 'generated checksum','saved checksum'
  255. compare_chksums(new_chksum,a,cls.wl_chksums[wl_id],b,die_on_fail=True)
  256. Msg('Checksum %s matches' % new_chksum)
  257. Msg('List is sorted') if tuple(sorted(wl)) == wl else die(3,'ERROR: List is not sorted!')
  258. @classmethod
  259. def tohex(cls,words_arg,wl_id,pad=None):
  260. words = words_arg if type(words_arg) in (list,tuple) else tuple(words_arg.strip())
  261. wl = cls.digits[wl_id]
  262. base = len(wl)
  263. if not set(words) <= set(wl):
  264. die(2,'{} is not in {} (base{}) format'.format(repr(words_arg),wl_id,base))
  265. deconv = [wl.index(words[::-1][i])*(base**i) for i in range(len(words))]
  266. ret = ('{:0{w}x}'.format(sum(deconv),w=pad or 0))
  267. return ('','0')[len(ret) % 2] + ret
  268. @classmethod
  269. def fromhex(cls,hexnum,wl_id,pad=None):
  270. hexnum = hexnum.strip()
  271. if not is_hex_str(hexnum):
  272. die(2,"'%s': not a hexadecimal number" % hexnum)
  273. wl = cls.digits[wl_id]
  274. base = len(wl)
  275. num,ret = int(hexnum,16),[]
  276. while num:
  277. ret.append(num % base)
  278. num /= base
  279. return [wl[n] for n in [0] * ((pad or 0)-len(ret)) + ret[::-1]]
  280. baseconv.check_wordlists()
  281. def match_ext(addr,ext):
  282. return addr.split('.')[-1] == ext
  283. def file_exists(f):
  284. try:
  285. os.stat(f)
  286. return True
  287. except:
  288. return False
  289. def file_is_readable(f):
  290. from stat import S_IREAD
  291. try:
  292. assert os.stat(f).st_mode & S_IREAD
  293. except:
  294. return False
  295. else:
  296. return True
  297. def get_from_brain_opt_params():
  298. l,p = opt.from_brain.split(',')
  299. return(int(l),p)
  300. def pretty_hexdump(data,gw=2,cols=8,line_nums=False):
  301. r = (0,1)[bool(len(data) % gw)]
  302. return ''.join(
  303. [
  304. ('' if (line_nums == False or i % cols) else '{:06x}: '.format(i*gw)) +
  305. hexlify(data[i*gw:i*gw+gw]) + ('\n',' ')[bool((i+1) % cols)]
  306. for i in range(len(data)/gw + r)
  307. ]
  308. ).rstrip() + '\n'
  309. def decode_pretty_hexdump(data):
  310. from string import hexdigits
  311. pat = r'^[%s]+:\s+' % hexdigits
  312. lines = [re.sub(pat,'',l) for l in data.splitlines()]
  313. try:
  314. return unhexlify(''.join((''.join(lines).split())))
  315. except:
  316. msg('Data not in hexdump format')
  317. return False
  318. def strip_comments(line):
  319. return re.sub(ur'\s+$',u'',re.sub(ur'#.*',u'',line,1))
  320. def remove_comments(lines):
  321. return [m for m in [strip_comments(l) for l in lines] if m != '']
  322. from mmgen.globalvars import g
  323. def start_mscolor():
  324. try:
  325. import colorama
  326. colorama.init(strip=True,convert=True)
  327. except:
  328. msg('Import of colorama module failed')
  329. def get_hash_params(hash_preset):
  330. if hash_preset in g.hash_presets:
  331. return g.hash_presets[hash_preset] # N,p,r,buflen
  332. else: # Shouldn't be here
  333. die(3,"%s: invalid 'hash_preset' value" % hash_preset)
  334. def compare_chksums(chk1,desc1,chk2,desc2,hdr='',die_on_fail=False,verbose=False):
  335. if not chk1 == chk2:
  336. m = "%s ERROR: %s checksum (%s) doesn't match %s checksum (%s)"\
  337. % ((hdr+':\n ' if hdr else 'CHECKSUM'),desc2,chk2,desc1,chk1)
  338. if die_on_fail:
  339. die(3,m)
  340. else:
  341. vmsg(m,force=verbose)
  342. return False
  343. vmsg('%s checksum OK (%s)' % (capfirst(desc1),chk1))
  344. return True
  345. def compare_or_die(val1, desc1, val2, desc2, e='Error'):
  346. if cmp(val1,val2):
  347. die(3,"%s: %s (%s) doesn't match %s (%s)"
  348. % (e,desc2,val2,desc1,val1))
  349. dmsg('%s OK (%s)' % (capfirst(desc2),val2))
  350. return True
  351. def open_file_or_exit(filename,mode):
  352. try:
  353. f = open(filename, mode)
  354. except:
  355. op = ('writing','reading')['r' in mode]
  356. die(2,"Unable to open file '%s' for %s" % (filename,op))
  357. return f
  358. def check_file_type_and_access(fname,ftype,blkdev_ok=False):
  359. a = ((os.R_OK,'read'),(os.W_OK,'writ'))
  360. access,m = a[ftype in ('output file','output directory')]
  361. ok_types = [
  362. (stat.S_ISREG,'regular file'),
  363. (stat.S_ISLNK,'symbolic link')
  364. ]
  365. if blkdev_ok: ok_types.append((stat.S_ISBLK,'block device'))
  366. if ftype == 'output directory': ok_types = [(stat.S_ISDIR, 'output directory')]
  367. try: mode = os.stat(fname).st_mode
  368. except:
  369. die(1,"Unable to stat requested %s '%s'" % (ftype,fname))
  370. for t in ok_types:
  371. if t[0](mode): break
  372. else:
  373. die(1,"Requested %s '%s' is not a %s" % (ftype,fname,
  374. ' or '.join([t[1] for t in ok_types])))
  375. if not os.access(fname, access):
  376. die(1,"Requested %s '%s' is not %sable by you" % (ftype,fname,m))
  377. return True
  378. def check_infile(f,blkdev_ok=False):
  379. return check_file_type_and_access(f,'input file',blkdev_ok=blkdev_ok)
  380. def check_outfile(f,blkdev_ok=False):
  381. return check_file_type_and_access(f,'output file',blkdev_ok=blkdev_ok)
  382. def check_outdir(f):
  383. return check_file_type_and_access(f,'output directory')
  384. def make_full_path(outdir,outfile):
  385. return os.path.normpath(os.path.join(outdir, os.path.basename(outfile)))
  386. def get_seed_file(cmd_args,nargs,invoked_as=None):
  387. from mmgen.filename import find_file_in_dir
  388. from mmgen.seed import Wallet
  389. wf = find_file_in_dir(Wallet,g.data_dir)
  390. wd_from_opt = bool(opt.hidden_incog_input_params or opt.in_fmt) # have wallet data from opt?
  391. import mmgen.opts as opts
  392. if len(cmd_args) + (wd_from_opt or bool(wf)) < nargs:
  393. opts.usage()
  394. elif len(cmd_args) > nargs:
  395. opts.usage()
  396. elif len(cmd_args) == nargs and wf and invoked_as != 'gen':
  397. msg('Warning: overriding default wallet with user-supplied wallet')
  398. if cmd_args or wf:
  399. check_infile(cmd_args[0] if cmd_args else wf)
  400. return cmd_args[0] if cmd_args else (wf,None)[wd_from_opt]
  401. def get_new_passphrase(desc,passchg=False):
  402. w = '{}passphrase for {}'.format(('','new ')[bool(passchg)], desc)
  403. if opt.passwd_file:
  404. pw = ' '.join(get_words_from_file(opt.passwd_file,w))
  405. elif opt.echo_passphrase:
  406. pw = ' '.join(get_words_from_user('Enter {}: '.format(w)))
  407. else:
  408. for i in range(g.passwd_max_tries):
  409. pw = ' '.join(get_words_from_user('Enter {}: '.format(w)))
  410. pw2 = ' '.join(get_words_from_user('Repeat passphrase: '))
  411. dmsg('Passphrases: [%s] [%s]' % (pw,pw2))
  412. if pw == pw2:
  413. vmsg('Passphrases match'); break
  414. else: msg('Passphrases do not match. Try again.')
  415. else:
  416. die(2,'User failed to duplicate passphrase in %s attempts' %
  417. g.passwd_max_tries)
  418. if pw == '': qmsg('WARNING: Empty passphrase')
  419. return pw
  420. def confirm_or_exit(message,question,expect='YES',exit_msg='Exiting at user request'):
  421. m = message.strip()
  422. if m: msg(m)
  423. a = question+' ' if question[0].isupper() else \
  424. 'Are you sure you want to %s?\n' % question
  425. b = "Type uppercase '%s' to confirm: " % expect
  426. if my_raw_input(a+b).strip() != expect:
  427. die(1,exit_msg)
  428. # New function
  429. def write_data_to_file(
  430. outfile,
  431. data,
  432. desc='data',
  433. ask_write=False,
  434. ask_write_prompt='',
  435. ask_write_default_yes=True,
  436. ask_overwrite=True,
  437. ask_tty=True,
  438. no_tty=False,
  439. silent=False,
  440. binary=False
  441. ):
  442. if silent: ask_tty = ask_overwrite = False
  443. if opt.quiet: ask_overwrite = False
  444. if ask_write_default_yes == False or ask_write_prompt:
  445. ask_write = True
  446. if not binary and type(data) == unicode:
  447. data = data.encode('utf8')
  448. def do_stdout():
  449. qmsg('Output to STDOUT requested')
  450. if sys.stdout.isatty():
  451. if no_tty:
  452. die(2,'Printing %s to screen is not allowed' % desc)
  453. if (ask_tty and not opt.quiet) or binary:
  454. confirm_or_exit('','output %s to screen' % desc)
  455. else:
  456. try: of = os.readlink('/proc/%d/fd/1' % os.getpid()) # Linux
  457. except: of = None # Windows
  458. if of:
  459. if of[:5] == 'pipe:':
  460. if no_tty:
  461. die(2,'Writing %s to pipe is not allowed' % desc)
  462. if ask_tty and not opt.quiet:
  463. confirm_or_exit('','output %s to pipe' % desc)
  464. msg('')
  465. of2,pd = os.path.relpath(of),os.path.pardir
  466. msg("Redirecting output to file '%s'" % (of2,of)[of2[:len(pd)] == pd])
  467. else:
  468. msg('Redirecting output to file')
  469. if binary and g.platform == 'win':
  470. import msvcrt
  471. msvcrt.setmode(sys.stdout.fileno(),os.O_BINARY)
  472. sys.stdout.write(data)
  473. def do_file(outfile,ask_write_prompt):
  474. if opt.outdir and not os.path.isabs(outfile):
  475. outfile = make_full_path(opt.outdir,outfile)
  476. if ask_write:
  477. if not ask_write_prompt: ask_write_prompt = 'Save %s?' % desc
  478. if not keypress_confirm(ask_write_prompt,
  479. default_yes=ask_write_default_yes):
  480. die(1,'%s not saved' % capfirst(desc))
  481. hush = False
  482. if file_exists(outfile) and ask_overwrite:
  483. q = "File '%s' already exists\nOverwrite?" % outfile
  484. confirm_or_exit('',q)
  485. msg("Overwriting file '%s'" % outfile)
  486. hush = True
  487. f = open_file_or_exit(outfile,('w','wb')[bool(binary)])
  488. try:
  489. f.write(data)
  490. except:
  491. die(2,"Failed to write %s to file '%s'" % (desc,outfile))
  492. f.close
  493. if not (hush or silent):
  494. msg("%s written to file '%s'" % (capfirst(desc),outfile))
  495. return True
  496. if opt.stdout or outfile in ('','-'):
  497. do_stdout()
  498. elif sys.stdin.isatty() and not sys.stdout.isatty():
  499. do_stdout()
  500. else:
  501. do_file(outfile,ask_write_prompt)
  502. def get_words_from_user(prompt):
  503. # split() also strips
  504. words = my_raw_input(prompt, echo=opt.echo_passphrase).split()
  505. dmsg('Sanitized input: [%s]' % ' '.join(words))
  506. return words
  507. def get_words_from_file(infile,desc,silent=False):
  508. if not silent:
  509. qmsg("Getting %s from file '%s'" % (desc,infile))
  510. f = open_file_or_exit(infile, 'r')
  511. # split() also strips
  512. words = f.read().split()
  513. f.close()
  514. dmsg('Sanitized input: [%s]' % ' '.join(words))
  515. return words
  516. def get_words(infile,desc,prompt):
  517. if infile:
  518. return get_words_from_file(infile,desc)
  519. else:
  520. return get_words_from_user(prompt)
  521. def mmgen_decrypt_file_maybe(fn,desc=''):
  522. d = get_data_from_file(fn,desc,binary=True)
  523. have_enc_ext = get_extension(fn) == g.mmenc_ext
  524. if have_enc_ext or not is_utf8(d):
  525. m = ('Attempting to decrypt','Decrypting')[have_enc_ext]
  526. msg("%s %s '%s'" % (m,desc,fn))
  527. from mmgen.crypto import mmgen_decrypt_retry
  528. d = mmgen_decrypt_retry(d,desc)
  529. return d
  530. def get_lines_from_file(fn,desc='',trim_comments=False):
  531. dec = mmgen_decrypt_file_maybe(fn,desc)
  532. ret = dec.decode('utf8').splitlines() # DOS-safe
  533. if trim_comments: ret = remove_comments(ret)
  534. vmsg(u"Got {} lines from file '{}'".format(len(ret),fn))
  535. return ret
  536. def get_data_from_user(desc='data',silent=False):
  537. p = ('','Enter {}: '.format(desc))[g.stdin_tty]
  538. data = my_raw_input(p,echo=opt.echo_passphrase)
  539. dmsg('User input: [%s]' % data)
  540. return data
  541. def get_data_from_file(infile,desc='data',dash=False,silent=False,binary=False):
  542. if dash and infile == '-': return sys.stdin.read()
  543. if not silent and desc:
  544. qmsg("Getting %s from file '%s'" % (desc,infile))
  545. f = open_file_or_exit(infile,('r','rb')[bool(binary)])
  546. data = f.read()
  547. f.close()
  548. return data
  549. def pwfile_reuse_warning():
  550. if 'passwd_file_used' in globals():
  551. qmsg("Reusing passphrase from file '%s' at user request" % opt.passwd_file)
  552. return True
  553. globals()['passwd_file_used'] = True
  554. return False
  555. def get_mmgen_passphrase(desc,passchg=False):
  556. prompt ='Enter {}passphrase for {}: '.format(('','old ')[bool(passchg)],desc)
  557. if opt.passwd_file:
  558. pwfile_reuse_warning()
  559. return ' '.join(get_words_from_file(opt.passwd_file,'passphrase'))
  560. else:
  561. return ' '.join(get_words_from_user(prompt))
  562. def my_raw_input(prompt,echo=True,insert_txt='',use_readline=True):
  563. try: import readline
  564. except: use_readline = False # Windows
  565. if use_readline and sys.stdout.isatty():
  566. def st_hook(): readline.insert_text(insert_txt)
  567. readline.set_startup_hook(st_hook)
  568. else:
  569. msg_r(prompt)
  570. prompt = ''
  571. from mmgen.term import kb_hold_protect
  572. kb_hold_protect()
  573. if echo or not sys.stdin.isatty():
  574. reply = raw_input(prompt.encode('utf8'))
  575. else:
  576. from getpass import getpass
  577. reply = getpass(prompt)
  578. kb_hold_protect()
  579. return reply.strip()
  580. def keypress_confirm(prompt,default_yes=False,verbose=False,no_nl=False):
  581. from mmgen.term import get_char
  582. q = ('(y/N)','(Y/n)')[bool(default_yes)]
  583. p = '{} {}: '.format(prompt,q)
  584. nl = ('\n','\r{}\r'.format(' '*len(p)))[no_nl]
  585. while True:
  586. reply = get_char(p).strip('\n\r')
  587. if not reply:
  588. if default_yes: msg_r(nl); return True
  589. else: msg_r(nl); return False
  590. elif reply in 'yY': msg_r(nl); return True
  591. elif reply in 'nN': msg_r(nl); return False
  592. else:
  593. if verbose: msg('\nInvalid reply')
  594. else: msg_r('\r')
  595. def prompt_and_get_char(prompt,chars,enter_ok=False,verbose=False):
  596. from mmgen.term import get_char
  597. while True:
  598. reply = get_char('%s: ' % prompt).strip('\n\r')
  599. if reply in chars or (enter_ok and not reply):
  600. msg('')
  601. return reply
  602. if verbose: msg('\nInvalid reply')
  603. else: msg_r('\r')
  604. def do_pager(text):
  605. pagers,shell = ['less','more'],False
  606. # --- Non-MSYS Windows code deleted ---
  607. # raw, chop, horiz scroll 8 chars, disable buggy line chopping in MSYS
  608. os.environ['LESS'] = (('--shift 8 -RS'),('-cR -#1'))[g.platform=='win']
  609. if 'PAGER' in os.environ and os.environ['PAGER'] != pagers[0]:
  610. pagers = [os.environ['PAGER']] + pagers
  611. text = text.encode('utf8')
  612. for pager in pagers:
  613. end = ('\n(end of text)\n','')[pager=='less']
  614. try:
  615. from subprocess import Popen,PIPE,STDOUT
  616. p = Popen([pager], stdin=PIPE, shell=shell)
  617. except: pass
  618. else:
  619. p.communicate(text+end+'\n')
  620. msg_r('\r')
  621. break
  622. else: Msg(text+end)
  623. def do_license_msg(immed=False):
  624. if opt.quiet or g.no_license or opt.yes or not g.stdin_tty: return
  625. import mmgen.license as gpl
  626. p = "Press 'w' for conditions and warranty info, or 'c' to continue:"
  627. msg(gpl.warning)
  628. prompt = '%s ' % p.strip()
  629. from mmgen.term import get_char
  630. while True:
  631. reply = get_char(prompt, immed_chars=('','wc')[bool(immed)])
  632. if reply == 'w':
  633. do_pager(gpl.conditions)
  634. elif reply == 'c':
  635. msg(''); break
  636. else:
  637. msg_r('\r')
  638. msg('')
  639. def get_bitcoind_cfg_options(cfg_keys):
  640. cfg_file = os.path.join(g.bitcoin_data_dir,'bitcoin.conf')
  641. cfg = dict([(k,v) for k,v in [split2(str(line).translate(None,'\t '),'=')
  642. for line in get_lines_from_file(cfg_file,'')] if k in cfg_keys]) \
  643. if file_is_readable(cfg_file) else {}
  644. for k in set(cfg_keys) - set(cfg.keys()): cfg[k] = ''
  645. return cfg
  646. def get_bitcoind_auth_cookie():
  647. f = os.path.join(g.bitcoin_data_dir,('',g.testnet_name)[g.testnet],'.cookie')
  648. if file_is_readable(f):
  649. return get_lines_from_file(f,'')[0]
  650. else:
  651. return ''
  652. def bitcoin_connection():
  653. def check_coin_mismatch(c):
  654. fb = '00000000000000000019f112ec0a9982926f1258cdcc558dd7c3b7e5dc7fa148'
  655. err = []
  656. if int(c.getblockchaininfo()['blocks']) <= 478558 or c.getblockhash(478559) == fb:
  657. if g.coin == 'BCH': err = 'BCH','BTC'
  658. elif g.coin == 'BTC': err = 'BTC','BCH'
  659. if err: wdie(2,"'{}' requested, but this is the {} chain!".format(*err))
  660. def check_chain_mismatch():
  661. err = None
  662. if g.regtest and g.chain != 'regtest':
  663. err = '--regtest option'
  664. elif g.testnet and g.chain == 'mainnet':
  665. err = '--testnet option'
  666. # we won't actually get here, as connect will fail first
  667. elif (not g.testnet) and g.chain != 'mainnet':
  668. err = 'mainnet'
  669. if err:
  670. die(1,'{} selected but chain is {}'.format(err,g.chain))
  671. cfg = get_bitcoind_cfg_options(('rpcuser','rpcpassword'))
  672. import mmgen.rpc
  673. c = mmgen.rpc.BitcoinRPCConnection(
  674. g.rpc_host or 'localhost',
  675. g.rpc_port or g.ports[g.coin][g.testnet],
  676. g.rpc_user or cfg['rpcuser'], # MMGen's rpcuser,rpcpassword override bitcoind's
  677. g.rpc_password or cfg['rpcpassword'],
  678. auth_cookie=get_bitcoind_auth_cookie())
  679. if not g.bitcoind_version: # First call
  680. g.bitcoind_version = int(c.getnetworkinfo()['version'])
  681. g.chain = c.getblockchaininfo()['chain']
  682. if g.chain != 'regtest':
  683. g.chain += 'net'
  684. assert g.chain in g.chains
  685. if g.chain == 'mainnet':
  686. check_coin_mismatch(c)
  687. return c