addr.py 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165
  1. #!/usr/bin/env python
  2. #
  3. # mmgen = Multi-Mode GENerator, command-line Bitcoin cold storage solution
  4. # Copyright (C) 2013-2014 by 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. addr.py: Address generation/display routines for mmgen suite
  20. """
  21. import sys
  22. from hashlib import sha256, sha512
  23. from hashlib import new as hashlib_new
  24. from binascii import hexlify, unhexlify
  25. from mmgen.bitcoin import numtowif
  26. from mmgen.util import msg,qmsg,qmsg_r
  27. import mmgen.config as g
  28. addrmsgs = {
  29. 'addrfile_header': """
  30. # MMGen address file
  31. #
  32. # This file is editable.
  33. # Everything following a hash symbol '#' is a comment and ignored by {pnm}.
  34. # A text label of {} characters or less may be added to the right of each
  35. # address, and it will be appended to the bitcoind wallet label upon import.
  36. # The label may contain any printable ASCII symbol.
  37. """.strip().format(g.max_addr_label_len,pnm=g.proj_name),
  38. 'no_keyconv_msg': """
  39. Executable '{kcexe}' unavailable. Falling back on (slow) internal ECDSA library.
  40. Please install '{kcexe}' from the {vanityg} package on your system for much
  41. faster address generation.
  42. """.format(kcexe=g.keyconv_exec, vanityg="vanitygen")
  43. }
  44. def test_for_keyconv():
  45. from subprocess import Popen, PIPE
  46. try:
  47. p = Popen([g.keyconv_exec, '-h'], stdout=PIPE, stderr=PIPE)
  48. except:
  49. msg(addrmsgs['no_keyconv_msg'])
  50. return False
  51. return True
  52. def generate_addrs(seed, addrnums, opts):
  53. if 'addrs' in opts['gen_what']:
  54. if 'no_keyconv' in opts or test_for_keyconv() == False:
  55. msg("Using (slow) internal ECDSA library for address generation")
  56. from mmgen.bitcoin import privnum2addr
  57. keyconv = False
  58. else:
  59. from subprocess import Popen, PIPE
  60. keyconv = "keyconv"
  61. fmt = "num addr" if opts['gen_what'] == ("addrs") else (
  62. "num sec wif" if opts['gen_what'] == ("keys") else "num sec wif addr")
  63. from collections import namedtuple
  64. addrinfo = namedtuple("addrinfo",fmt)
  65. addrinfo_args = "%s" % ",".join(fmt.split())
  66. t_addrs,num,pos,out = len(addrnums),0,0,[]
  67. addrnums.sort() # needed only if caller didn't sort
  68. try:
  69. while pos != t_addrs:
  70. seed = sha512(seed).digest()
  71. num += 1 # round
  72. if g.debug: print "Seed round %s: %s" % (num, hexlify(seed))
  73. if num != addrnums[pos]: continue
  74. pos += 1
  75. qmsg_r("\rGenerating %s %s (%s of %s)" %
  76. (opts['gen_what'][-1],num,pos,t_addrs))
  77. # Secret key is double sha256 of seed hash round /num/
  78. sec = sha256(sha256(seed).digest()).hexdigest()
  79. wif = numtowif(int(sec,16))
  80. if 'addrs' in opts['gen_what']: addr = \
  81. Popen([keyconv, wif], stdout=PIPE).stdout.readline().split()[1] \
  82. if keyconv else privnum2addr(int(sec,16))
  83. out.append(eval("addrinfo("+addrinfo_args+")"))
  84. except KeyboardInterrupt:
  85. msg("\nUser interrupt")
  86. sys.exit(1)
  87. w = 'key' if 'keys' in opts['gen_what'] else 'address'
  88. if t_addrs != 1: w = w+"s" if w == 'key' else w+"es"
  89. qmsg("\rGenerated %s %s%s"%(t_addrs, w, " "*15))
  90. return out
  91. def format_addr_data(addr_data, addr_data_chksum, seed_id, addr_idxs, opts):
  92. if 'flat_list' in opts:
  93. return "\n\n".join(["# {}:{d.num} {d.addr}\n{d.wif}".format(seed_id,d=d)
  94. for d in addr_data])+"\n\n"
  95. fs = " {:<%s} {}" % len(str(addr_data[-1].num))
  96. if 'addrs' not in opts['gen_what']: out = []
  97. else:
  98. out = [] if 'stdout' in opts else [addrmsgs['addrfile_header']+"\n"]
  99. out.append("# Address data checksum for {}[{}]: {}".format(
  100. seed_id, fmt_addr_idxs(addr_idxs), addr_data_chksum))
  101. out.append("# Record this value to a secure location\n")
  102. out.append("%s {" % seed_id.upper())
  103. for d in addr_data:
  104. if 'addrs' in opts['gen_what']: # First line with number
  105. out.append(fs.format(d.num, d.addr))
  106. else:
  107. out.append(fs.format(d.num, "wif: "+d.wif))
  108. if 'keys' in opts['gen_what']: # Subsequent lines
  109. if 'b16' in opts:
  110. out.append(fs.format("", "hex: "+d.sec))
  111. if 'addrs' in opts['gen_what']:
  112. out.append(fs.format("", "wif: "+d.wif))
  113. out.append("}")
  114. return "\n".join(out) + "\n"
  115. def fmt_addr_idxs(addr_idxs):
  116. addr_idxs = list(sorted(set(addr_idxs)))
  117. prev = addr_idxs[0]
  118. ret = prev,
  119. for i in addr_idxs[1:]:
  120. if i == prev + 1:
  121. if i == addr_idxs[-1]: ret += "-", i
  122. else:
  123. if prev != ret[-1]: ret += "-", prev
  124. ret += ",", i
  125. prev = i
  126. return "".join([str(i) for i in ret])