mmgen-txsign 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170
  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. mmgen-txsign: Sign a Bitcoin transaction generated by mmgen-txcreate
  20. """
  21. import sys
  22. from mmgen.Opts import *
  23. from mmgen.license import *
  24. import mmgen.config as g
  25. from mmgen.tx import *
  26. from mmgen.util import msg,qmsg
  27. help_data = {
  28. 'prog_name': sys.argv[0].split("/")[-1],
  29. 'desc': "Sign Bitcoin transactions generated by mmgen-txcreate",
  30. 'usage': "[opts] <transaction file>,.. [mmgen wallet/seed/words/brainwallet file]...",
  31. 'options': """
  32. -h, --help Print this help message
  33. -d, --outdir= d Specify an alternate directory 'd' for output
  34. -e, --echo-passphrase Print passphrase to screen when typing it
  35. -i, --info Display information about the transaction and exit
  36. -I, --tx-id Display transaction ID and exit
  37. -k, --keys-from-file= k Provide additional key data from file 'k'
  38. -P, --passwd-file= f Get passphrase from file 'f'
  39. -q, --quiet Suppress warnings; overwrite files without
  40. prompting
  41. -V, --skip-key-preverify Skip optional key pre-verification step
  42. -b, --from-brain= l,p Generate keys from a user-created password,
  43. i.e. a "brainwallet", using seed length 'l' and
  44. hash preset 'p'
  45. -w, --use-wallet-dat Get keys from a running bitcoind
  46. -g, --from-incog Generate keys from an incognito wallet
  47. -X, --from-incog-hex Generate keys from an incognito hexadecimal wallet
  48. -G, --from-incog-hidden= f,o,l Generate keys from incognito data in file
  49. 'f' at offset 'o', with seed length of 'l'
  50. -m, --from-mnemonic Generate keys from an electrum-like mnemonic
  51. -s, --from-seed Generate keys from a seed in .{g.seed_ext} format
  52. """.format(g=g),
  53. 'notes': """
  54. Transactions with either mmgen or non-mmgen input addresses may be signed.
  55. For non-mmgen inputs, the bitcoind wallet.dat is used as the key source.
  56. For mmgen inputs, key data is generated from your seed as with the
  57. mmgen-addrgen and mmgen-keygen utilities.
  58. Data for the --from-<what> options will be taken from a file if a second
  59. file is specified on the command line. Otherwise, the user will be
  60. prompted to enter the data.
  61. In cases of transactions with mixed mmgen and non-mmgen inputs, non-mmgen
  62. keys must be supplied in a separate file (WIF format, one key per line)
  63. using the '--keys-from-file' option. Alternatively, one may get keys from
  64. a running bitcoind using the '--force-wallet-dat' option. First import the
  65. required mmgen keys using 'bitcoind importprivkey'.
  66. For transaction outputs that are MMGen addresses, MMGen-to-Bitcoin address
  67. mappings are verified. Therefore, seed material for these addresses must
  68. be supplied on the command line.
  69. Seed data supplied in files must have the following extensions:
  70. wallet: '.{g.wallet_ext}'
  71. seed: '.{g.seed_ext}'
  72. mnemonic: '.{g.mn_ext}'
  73. brainwallet: '.{g.brain_ext}'
  74. """.format(g=g)
  75. }
  76. opts,infiles = parse_opts(sys.argv,help_data)
  77. if "quiet" in opts: g.quiet = True
  78. if 'from_incog_hex' in opts or 'from_incog_hidden' in opts:
  79. opts['from_incog'] = True
  80. if not infiles: usage(help_data)
  81. for i in infiles: check_infile(i)
  82. c = connect_to_bitcoind()
  83. saved_seeds = {}
  84. tx_files = [i for i in set(infiles) if get_extension(i) == g.rawtx_ext]
  85. infiles = list(set(infiles) - set(tx_files))
  86. if not "info" in opts: do_license_msg(immed=True)
  87. keys_from_file = get_lines_from_file(opts['keys_from_file'],"key data",
  88. remove_comments=True) if 'keys_from_file' in opts else []
  89. for tx_file in tx_files:
  90. m = "" if 'tx_id' in opts else "transaction data"
  91. tx_data = get_lines_from_file(tx_file,m)
  92. metadata,tx_hex,inputs_data,b2m_map = parse_tx_data(tx_data,tx_file)
  93. qmsg("Successfully opened transaction file '%s'" % tx_file)
  94. if 'tx_id' in opts:
  95. msg(metadata[0])
  96. sys.exit(0)
  97. if 'info' in opts:
  98. view_tx_data(c,inputs_data,tx_hex,b2m_map,metadata)
  99. sys.exit(0)
  100. # Are inputs mmgen addresses?
  101. mmgen_addrs = [i for i in inputs_data if parse_mmgen_label(i['account'])[0]]
  102. other_addrs = [i for i in inputs_data if not parse_mmgen_label(i['account'])[0]]
  103. keys = keys_from_file
  104. if other_addrs and not keys and not 'use_wallet_dat' in opts:
  105. missing_keys_errormsg(other_addrs)
  106. sys.exit(2)
  107. if other_addrs and keys and not 'skip_key_preverify' in opts:
  108. a = [i['address'] for i in other_addrs]
  109. preverify_keys(a, keys)
  110. opts['skip_key_preverify'] = True
  111. check_mmgen_to_btc_addr_mappings(inputs_data,b2m_map,infiles,saved_seeds,opts)
  112. if len(tx_files) > 1:
  113. msg("\nTransaction %s/%s:" % (tx_files.index(tx_file)+1,len(tx_files)))
  114. prompt = "View transaction data? (y)es, (N)o, (v)iew in pager"
  115. reply = prompt_and_get_char(prompt,"YyNnVv",enter_ok=True)
  116. if reply and reply in "YyVv":
  117. p = True if reply in "Vv" else False
  118. view_tx_data(c,inputs_data,tx_hex,b2m_map,metadata,pager=p)
  119. sig_data = [
  120. {"txid":i['txid'],"vout":i['vout'],"scriptPubKey":i['scriptPubKey']}
  121. for i in inputs_data]
  122. if mmgen_addrs:
  123. ml = [i['account'].split()[0] for i in mmgen_addrs]
  124. keys += get_keys_for_mmgen_addrs(ml,infiles,saved_seeds,opts)
  125. if 'use_wallet_dat' in opts:
  126. sig_tx = sign_tx_with_bitcoind_wallet(c,tx_hex,sig_data,keys,opts)
  127. else:
  128. sig_tx = sign_transaction(c,tx_hex,sig_data,keys)
  129. elif other_addrs:
  130. if keys:
  131. sig_tx = sign_transaction(c,tx_hex,sig_data,keys)
  132. else:
  133. sig_tx = sign_tx_with_bitcoind_wallet(c,tx_hex,sig_data,keys,opts)
  134. if sig_tx['complete']:
  135. prompt = "OK\nSave signed transaction?"
  136. if user_confirm(prompt,default_yes=True):
  137. write_signed_tx_to_file(tx_hex,sig_tx['hex'],metadata,inputs_data,b2m_map,opts)
  138. else:
  139. msg("failed\nSome keys were missing. Transaction could not be signed.")
  140. sys.exit(3)