addr.py 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198
  1. #!/usr/bin/env python
  2. #
  3. # mmgen = Multi-Mode GENerator, command-line Bitcoin cold storage solution
  4. # Copyright (C) 2013 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 binascii import hexlify, unhexlify
  24. from mmgen.bitcoin import numtowif
  25. from mmgen.config import *
  26. def test_for_keyconv():
  27. """
  28. Test for the presence of 'keyconv' utility on system
  29. """
  30. keyconv_exec = "keyconv"
  31. from subprocess import Popen, PIPE
  32. try:
  33. p = Popen([keyconv_exec, '-h'], stdout=PIPE, stderr=PIPE)
  34. except:
  35. sys.stderr.write("""
  36. Executable '%s' unavailable. Falling back on (slow) internal ECDSA library.
  37. Please install '%s' from the %s package on your system for much faster
  38. address generation.
  39. """ % (keyconv_exec, keyconv_exec, "vanitygen"))
  40. return False
  41. else:
  42. return True
  43. def generate_addrs(seed, addrnums, opts):
  44. """
  45. generate_addresses(start, end, seed, opts) => None
  46. Generate a Bitcoin address or addresses end based on a seed, optionally
  47. outputting secret keys
  48. The 'keyconv' utility will be used for address generation if installed.
  49. Otherwise an internal function is used
  50. Supported options:
  51. print_secret, no_addresses, no_keyconv, gen_what
  52. Addresses are returned in a list of dictionaries with the following keys:
  53. num, sec, wif, addr
  54. """
  55. if not 'no_addresses' in opts:
  56. if 'no_keyconv' in opts or test_for_keyconv() == False:
  57. sys.stderr.write("Using (slow) internal ECDSA library for address generation\n")
  58. from mmgen.bitcoin import privnum2addr
  59. keyconv = ""
  60. else:
  61. from subprocess import Popen, PIPE
  62. keyconv = "keyconv"
  63. a,t_addrs,i,out = sorted(addrnums),len(addrnums),0,[]
  64. while a:
  65. seed = sha512(seed).digest(); i += 1 # round /i/
  66. if i < a[0]: continue
  67. a.pop(0)
  68. sys.stderr.write("\rGenerating %s %s (%s of %s)" %
  69. (opts['gen_what'], i, t_addrs-len(a), t_addrs))
  70. # Secret key is double sha256 of seed hash round /i/
  71. sec = sha256(sha256(seed).digest()).hexdigest()
  72. wif = numtowif(int(sec,16))
  73. el = { 'num': i }
  74. if not 'print_addresses_only' in opts:
  75. el['sec'] = sec
  76. el['wif'] = wif
  77. if not 'no_addresses' in opts:
  78. if keyconv:
  79. p = Popen([keyconv, wif], stdout=PIPE)
  80. addr = dict([j.split() for j in \
  81. p.stdout.readlines()])['Address:']
  82. else:
  83. addr = privnum2addr(int(sec,16))
  84. el['addr'] = addr
  85. out.append(el)
  86. sys.stderr.write("\rGenerated %s %s%s\n"%(t_addrs,opts['gen_what']," "*15))
  87. return out
  88. def format_addr_data(addrlist, seed_chksum, opts):
  89. """
  90. print_addresses(addrs, opts) => None
  91. Print out the addresses and/or keys generated by generate_addresses()
  92. By default, prints addresses only
  93. Output can be customized with the following command line options:
  94. print_secret
  95. no_addresses
  96. b16
  97. """
  98. start = addrlist[0]['num']
  99. end = addrlist[-1]['num']
  100. wif_msg = ""
  101. if ('b16' in opts and 'print_secret' in opts) \
  102. or 'no_addresses' in opts:
  103. wif_msg = " (wif)"
  104. fa = "%s%%-%ss %%-%ss %%s" % (
  105. " "*2, len(str(end)) + (0 if 'no_addresses' in opts else 1),
  106. (5 if 'print_secret' in opts else 1) + len(wif_msg)
  107. )
  108. header = """
  109. # MMGen address file
  110. #
  111. # This file is editable.
  112. # Everything following a hash symbol '#' is ignored.
  113. # A label may be added to the right of each address, and it will be
  114. # appended to the bitcoind wallet label upon import (max. {} characters,
  115. # allowed characters: A-Za-z0-9, plus '{}').
  116. """.format(max_wallet_addr_label_len,
  117. "', '".join(wallet_addr_label_symbols)).strip()
  118. data = [header + "\n"]
  119. data.append("%s {" % seed_chksum.upper())
  120. for el in addrlist:
  121. col1 = el['num']
  122. if 'no_addresses' in opts:
  123. if 'b16' in opts:
  124. data.append(fa % (col1, " (hex):", el['sec']))
  125. col1 = ""
  126. data.append(fa % (col1, " (wif):", el['wif']))
  127. if 'b16' in opts: data.append("")
  128. elif 'print_secret' in opts:
  129. if 'b16' in opts:
  130. data.append(fa % (col1, "sec (hex):", el['sec']))
  131. col1 = ""
  132. data.append(fa % (col1, "sec"+wif_msg+":", el['wif']))
  133. data.append(fa % ("", "addr:", el['addr']))
  134. data.append("")
  135. else:
  136. data.append(fa % (col1, "", el['addr']))
  137. if not data[-1]: data.pop()
  138. data.append("}")
  139. return "\n".join(data) + "\n"
  140. def write_addr_data_to_file(seed, data, addr_list, opts):
  141. if 'print_addresses_only' in opts: ext = "addrs"
  142. elif 'no_addresses' in opts: ext = "keys"
  143. else: ext = "akeys"
  144. if 'b16' in opts: ext = ext.replace("keys","xkeys")
  145. beg = addr_list[0]
  146. end = addr_list[-1]
  147. sep = "-" if (end - beg == len(addr_list) - 1) else ".."
  148. from mmgen.utils import write_to_file, make_chksum_8, msg
  149. addr_range = beg if beg == end else "%s%s%s" % (beg,sep,end)
  150. outfile = "{}[{}].{}".format(make_chksum_8(seed),addr_range,ext)
  151. if 'outdir' in opts:
  152. outfile = "%s/%s" % (opts['outdir'], outfile)
  153. write_to_file(outfile,data)
  154. dtype = "Address" if 'print_addresses_only' in opts else "Key"
  155. msg("%s data saved to file '%s'" % (dtype,outfile))