bump.py 2.7 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788
  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-wallet
  9. # https://gitlab.com/mmgen/mmgen-wallet
  10. """
  11. tx.bump: transaction bump class
  12. """
  13. from .new import New
  14. from .completed import Completed
  15. from ..util import msg,is_int,die
  16. class Bump(Completed,New):
  17. desc = 'fee-bumped transaction'
  18. ext = 'rawtx'
  19. bump_output_idx = None
  20. def __init__(self,send,*args,**kwargs):
  21. super().__init__(*args,**kwargs)
  22. if not self.is_replaceable():
  23. die(1,f'Transaction {self.txid} is not replaceable')
  24. # If sending, require original tx to be sent
  25. if send and not self.coin_txid:
  26. die(1,'Transaction {self.txid!r} was not broadcast to the network')
  27. self.coin_txid = ''
  28. def check_sufficient_funds_for_bump(self):
  29. if not [o.amt for o in self.outputs if o.amt >= self.min_fee]:
  30. die(1,
  31. 'Transaction cannot be bumped.\n' +
  32. f'All outputs contain less than the minimum fee ({self.min_fee} {self.coin})')
  33. def choose_output(self):
  34. def check_sufficient_funds(o_amt):
  35. if o_amt < self.min_fee:
  36. msg(f'Minimum fee ({self.min_fee} {self.coin}) is greater than output amount ({o_amt} {self.coin})')
  37. return False
  38. return True
  39. if len(self.outputs) == 1:
  40. if check_sufficient_funds(self.outputs[0].amt):
  41. self.bump_output_idx = 0
  42. return 0
  43. else:
  44. die(1,'Insufficient funds to bump transaction')
  45. init_reply = self.cfg.output_to_reduce
  46. chg_idx = self.chg_idx
  47. while True:
  48. if init_reply is None:
  49. from ..ui import line_input
  50. m = 'Choose an output to deduct the fee from (Hit ENTER for the change output): '
  51. reply = line_input( self.cfg, m ) or 'c'
  52. else:
  53. reply,init_reply = init_reply,None
  54. if chg_idx is None and not is_int(reply):
  55. msg('Output must be an integer')
  56. elif chg_idx is not None and not is_int(reply) and reply != 'c':
  57. msg("Output must be an integer, or 'c' for the change output")
  58. else:
  59. idx = chg_idx if reply == 'c' else (int(reply) - 1)
  60. if idx < 0 or idx >= len(self.outputs):
  61. msg(f'Output must be in the range 1-{len(self.outputs)}')
  62. else:
  63. o_amt = self.outputs[idx].amt
  64. cm = ' (change output)' if chg_idx == idx else ''
  65. prompt = f'Fee will be deducted from output {idx+1}{cm} ({o_amt} {self.coin})'
  66. if check_sufficient_funds(o_amt):
  67. if self.cfg.yes:
  68. msg(prompt)
  69. else:
  70. from ..ui import keypress_confirm
  71. if not keypress_confirm( self.cfg, prompt+'. OK?', default_yes=True ):
  72. continue
  73. self.bump_output_idx = idx
  74. return idx