bump.py 2.7 KB

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