unsigned.py 2.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384
  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. proto.btc.tx.unsigned: Bitcoin unsigned transaction class
  12. """
  13. import mmgen.tx.unsigned as TxBase
  14. from .completed import Completed
  15. from ....obj import CoinTxID,MMGenDict
  16. from ....util import msg,msg_r,ymsg,suf,die
  17. class Unsigned(Completed,TxBase.Unsigned):
  18. desc = 'unsigned transaction'
  19. async def sign(self,tx_num_str,keys): # return signed object or False; don't exit or raise exception
  20. from ....exception import TransactionChainMismatch
  21. try:
  22. self.check_correct_chain()
  23. except TransactionChainMismatch:
  24. return False
  25. if (self.has_segwit_inputs() or self.has_segwit_outputs()) and not self.proto.cap('segwit'):
  26. ymsg(f"TX has Segwit inputs or outputs, but {self.coin} doesn't support Segwit!")
  27. return False
  28. self.check_pubkey_scripts()
  29. self.cfg._util.qmsg(f'Passing {len(keys)} key{suf(keys)} to {self.rpc.daemon.exec_fn}')
  30. if self.has_segwit_inputs():
  31. from ....addrgen import KeyGenerator,AddrGenerator
  32. kg = KeyGenerator( self.cfg, self.proto, 'std' )
  33. ag = AddrGenerator( self.cfg, self.proto, 'segwit' )
  34. keydict = MMGenDict([(d.addr,d.sec) for d in keys])
  35. sig_data = []
  36. for d in self.inputs:
  37. e = {k:getattr(d,k) for k in ('txid','vout','scriptPubKey','amt')}
  38. e['amount'] = e['amt']
  39. del e['amt']
  40. if d.mmtype == 'S':
  41. e['redeemScript'] = ag.to_segwit_redeem_script(kg.gen_data(keydict[d.addr]))
  42. sig_data.append(e)
  43. msg_r(f'Signing transaction{tx_num_str}...')
  44. wifs = [d.sec.wif for d in keys]
  45. try:
  46. args = (
  47. ('signrawtransaction', self.serialized,sig_data,wifs,self.proto.sighash_type),
  48. ('signrawtransactionwithkey',self.serialized,wifs,sig_data,self.proto.sighash_type)
  49. )['sign_with_key' in self.rpc.caps]
  50. ret = await self.rpc.call(*args)
  51. except Exception as e:
  52. ymsg(self.rpc.daemon.sigfail_errmsg(e))
  53. return False
  54. from ....tx import SignedTX
  55. try:
  56. self.update_serialized(ret['hex'])
  57. new = await SignedTX(cfg=self.cfg,data=self.__dict__)
  58. tx_decoded = await self.rpc.call( 'decoderawtransaction', ret['hex'] )
  59. new.compare_size_and_estimated_size(tx_decoded)
  60. new.coin_txid = CoinTxID(self.deserialized.txid)
  61. if not new.coin_txid == tx_decoded['txid']:
  62. die( 'BadMMGenTxID', 'txid mismatch (after signing)' )
  63. msg('OK')
  64. return new
  65. except Exception as e:
  66. ymsg(f'\n{e.args[0]}')
  67. if self.cfg.exec_wrapper:
  68. import sys,traceback
  69. ymsg( '\n' + ''.join(traceback.format_exception(*sys.exc_info())) )
  70. return False