main_split.py 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142
  1. #!/usr/bin/env python3
  2. #
  3. # mmgen = Multi-Mode GENerator, command-line Bitcoin cold storage solution
  4. # Copyright (C)2013-2020 The MMGen Project <mmgen@tuta.io>
  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. # TODO: check that balances of output addrs are zero?
  19. """
  20. mmgen-split: Split funds after a replayable chain fork using a timelocked transaction
  21. """
  22. import time
  23. from mmgen.common import *
  24. opts_data = {
  25. 'text': {
  26. 'desc': """
  27. Split funds in a {pnm} wallet after a chain fork using a
  28. timelocked transaction
  29. """.format(pnm=g.proj_name),
  30. 'usage':'[opts] [output addr1] [output addr2]',
  31. 'options': """
  32. -h, --help Print this help message
  33. --, --longhelp Print help message for long options (common options)
  34. -f, --tx-fees= f The transaction fees for each chain (comma-separated)
  35. -c, --other-coin= c The coin symbol of the other chain (default: {oc})
  36. -B, --no-blank Don't blank screen before displaying unspent outputs
  37. -d, --outdir= d Specify an alternate directory 'd' for output
  38. -m, --minconf= n Minimum number of confirmations required to spend
  39. outputs (default: 1)
  40. -q, --quiet Suppress warnings; overwrite files without prompting
  41. -r, --rbf Make transaction BIP 125 replaceable (replace-by-fee)
  42. -v, --verbose Produce more verbose output
  43. -y, --yes Answer 'yes' to prompts, suppress non-essential output
  44. -R, --rpc-host2= h Host the other coin daemon is running on (default: none)
  45. -L, --locktime= t Lock time (block height or unix seconds)
  46. (default: {bh})
  47. """,
  48. 'notes': """\n
  49. This command creates two transactions: one (with the timelock) to be broadcast
  50. on the long chain and one on the short chain after a replayable chain fork.
  51. Only {pnm} addresses may be spent to.
  52. The command must be run on the longest chain. The user is reponsible for
  53. ensuring that the current chain is the longest. The other chain is specified
  54. on the command line, or it defaults to the most recent replayable fork of the
  55. current chain.
  56. For the split to have a reasonable chance of succeeding, the long chain should
  57. be well ahead of the short one (by more than 20 blocks or so) and transactions
  58. should have a good chance of confirming quickly on both chains. For this
  59. larger than normal fees may be required. Fees may be specified on the command
  60. line, or network fee estimation may be used.
  61. If the split fails (i.e. the long-chain TX is broadcast and confirmed on the
  62. short chain), no funds are lost. A new split attempt can be made with the
  63. long-chain transaction's output as an input for the new split transaction.
  64. This process can be repeated as necessary until the split succeeds.
  65. IMPORTANT: Timelock replay protection offers NO PROTECTION against reorg
  66. attacks on the majority chain or reorg attacks on the minority chain if the
  67. minority chain is ahead of the timelock. If the reorg'd minority chain is
  68. behind the timelock, protection is contingent on getting the non-timelocked
  69. transaction reconfirmed before the timelock expires. Use at your own risk.
  70. """.format(pnm=g.proj_name)
  71. },
  72. 'code': {
  73. 'options': lambda s: s.format(
  74. oc=g.proto.forks[-1][2].upper(),
  75. bh='current block height'),
  76. }
  77. }
  78. cmd_args = opts.init(opts_data,add_opts=['tx_fee','tx_fee_adj','comment_file'])
  79. die(1,'This command is disabled')
  80. opt.other_coin = opt.other_coin.upper() if opt.other_coin else g.proto.forks[-1][2].upper()
  81. if opt.other_coin.lower() not in [e[2] for e in g.proto.forks if e[3] == True]:
  82. die(1,"'{}': not a replayable fork of {} chain".format(opt.other_coin,g.coin))
  83. if len(cmd_args) != 2:
  84. fs = 'This command requires exactly two {} addresses as arguments'
  85. die(1,fs.format(g.proj_name))
  86. from mmgen.obj import MMGenID
  87. try:
  88. mmids = [MMGenID(a,on_fail='die') for a in cmd_args]
  89. except:
  90. die(1,'Command line arguments must be valid MMGen IDs')
  91. if mmids[0] == mmids[1]:
  92. die(2,'Both transactions have the same output! ({})'.format(mmids[0]))
  93. from mmgen.tx import MMGenSplitTX
  94. from mmgen.protocol import init_coin
  95. if opt.tx_fees:
  96. for idx,g_coin in ((1,opt.other_coin),(0,g.coin)):
  97. init_coin(g_coin)
  98. opt.tx_fee = opt.tx_fees.split(',')[idx]
  99. opts.opt_is_tx_fee(opt.tx_fee,'transaction fee') or sys.exit(1)
  100. rpc_init(reinit=True)
  101. tx1 = MMGenSplitTX()
  102. opt.no_blank = True
  103. gmsg("Creating timelocked transaction for long chain ({})".format(g.coin))
  104. locktime = int(opt.locktime or 0) or g.rpch.getblockcount()
  105. tx1.create(mmids[0],locktime)
  106. tx1.format()
  107. tx1.create_fn()
  108. gmsg("\nCreating transaction for short chain ({})".format(opt.other_coin))
  109. init_coin(opt.other_coin)
  110. tx2 = MMGenSplitTX()
  111. tx2.inputs = tx1.inputs
  112. tx2.inputs.convert_coin()
  113. tx2.create_split(mmids[1])
  114. for tx,desc in ((tx1,'Long chain (timelocked)'),(tx2,'Short chain')):
  115. tx.desc = desc + ' transaction'
  116. tx.write_to_file(ask_write=False,ask_overwrite=not opt.yes,ask_write_default_yes=False)