main_txbump.py 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220
  1. #!/usr/bin/env python3
  2. #
  3. # MMGen Wallet, a terminal-based cryptocurrency wallet
  4. # Copyright (C)2013-2025 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. """
  19. mmgen-txbump: Create, and optionally send and sign, a replacement transaction
  20. on supporting networks
  21. """
  22. from .cfg import gc, Config
  23. from .util import msg, msg_r, die, async_run
  24. from .color import green
  25. opts_data = {
  26. 'filter_codes': ['-'],
  27. 'sets': [('yes', True, 'quiet', True)],
  28. 'text': {
  29. 'desc': """
  30. Create, and optionally send and sign, a replacement transaction
  31. on supporting networks
  32. """,
  33. 'usage2': (
  34. f'[opts] [{gc.proj_name} TX file] [seed source] ...',
  35. f'[opts] {{u_args}} [{gc.proj_name} TX file] [seed source] ...',
  36. ),
  37. 'options': """
  38. -- -h, --help Print this help message
  39. -- --, --longhelp Print help message for long (global) options
  40. -- -a, --autosign Bump the most recent transaction created and sent with
  41. + the --autosign option. The removable device is mounted
  42. + and unmounted automatically. The transaction file
  43. + argument must be omitted. Note that only sent trans-
  44. + actions may be bumped with this option. To redo an
  45. + unsent --autosign transaction, first delete it using
  46. + ‘mmgen-txsend --abort’ and then create a new one
  47. -- -b, --brain-params=l,p Use seed length 'l' and hash preset 'p' for
  48. + brainwallet input
  49. -- -c, --comment-file= f Source the transaction's comment from file 'f'
  50. -- -d, --outdir= d Specify an alternate directory 'd' for output
  51. -- -e, --echo-passphrase Print passphrase to screen when typing it
  52. -- -f, --fee= f Transaction fee, as a decimal {cu} amount or as
  53. + {fu} (an integer followed by {fl}).
  54. + See FEE SPECIFICATION below.
  55. e- -g, --gas=N Set the gas limit (see GAS LIMIT below)
  56. -- -H, --hidden-incog-input-params=f,o Read hidden incognito data from file
  57. + 'f' at offset 'o' (comma-separated)
  58. -- -i, --in-fmt= f Input is from wallet format 'f' (see FMT CODES below)
  59. -- -l, --seed-len= l Specify wallet seed length of 'l' bits. This option
  60. + is required only for brainwallet and incognito inputs
  61. + with non-standard (< {dsl}-bit) seed lengths.
  62. -- -k, --keys-from-file=f Provide additional keys for non-{pnm} addresses
  63. -- -K, --keygen-backend=n Use backend 'n' for public key generation. Options
  64. + for {coin_id}: {kgs}
  65. -- -M, --mmgen-keys-from-file=f Provide keys for {pnm} addresses in a key-
  66. + address file (output of '{pnl}-keygen'). Permits
  67. + online signing without an {pnm} seed source. The
  68. + key-address file is also used to verify {pnm}-to-{cu}
  69. + mappings, so the user should record its checksum.
  70. b- -o, --output-to-reduce=o Deduct the fee from output 'o' (an integer, or 'c'
  71. + for the transaction's change output, if present)
  72. -- -O, --old-incog-fmt Specify old-format incognito input
  73. -- -p, --hash-preset= p Use the scrypt hash parameters defined by preset 'p'
  74. + for password hashing (default: '{gc.dfl_hash_preset}')
  75. -- -P, --passwd-file= f Get {pnm} wallet passphrase from file 'f'
  76. -- -q, --quiet Suppress warnings; overwrite files without prompting
  77. -- -s, --send Sign and send the transaction (the default if seed
  78. + data is provided)
  79. -- -T, --txhex-idx=N Send only part ‘N’ of a multi-part transaction.
  80. + Indexing begins with one.
  81. -- -v, --verbose Produce more verbose output
  82. e- -w, --wait Wait for transaction confirmation
  83. -- -W, --allow-non-wallet-swap Allow signing of swap transactions that send funds
  84. + to non-wallet addresses
  85. -- -x, --proxy=P Fetch the swap quote via SOCKS5h proxy ‘P’ (host:port).
  86. + Use special value ‘env’ to honor *_PROXY environment
  87. + vars instead.
  88. -- -y, --yes Answer 'yes' to prompts, suppress non-essential output
  89. -- -z, --show-hash-presets Show information on available hash presets
  90. """,
  91. 'notes': """
  92. With --autosign, the TX file argument is omitted, and the last submitted TX
  93. file on the removable device will be used.
  94. If no outputs are specified, the original outputs will be used for the
  95. replacement transaction, otherwise a new transaction will be created with the
  96. outputs listed on the command line. The syntax for the output arguments is
  97. identical to that of ‘mmgen-txcreate’.
  98. The user should take care to select a fee sufficient to ensure the original
  99. transaction is replaced in the mempool.
  100. When bumping a swap transaction, the swap protocol’s quote server on the
  101. Internet must be reachable either directly or via the SOCKS5 proxy specified
  102. with the --proxy option. To improve privacy, it’s recommended to proxy
  103. requests to the quote server via Tor or some other anonymity network.
  104. {g}{F}{s}
  105. Seed source files must have the canonical extensions listed in the 'FileExt'
  106. column below:
  107. {f}
  108. """
  109. },
  110. 'code': {
  111. 'usage': lambda cfg, proto, help_notes, s: s.format(
  112. u_args = help_notes('txcreate_args')),
  113. 'options': lambda cfg, help_notes, proto, s: s.format(
  114. cfg = cfg,
  115. gc = gc,
  116. pnm = gc.proj_name,
  117. pnl = gc.proj_name.lower(),
  118. fu = help_notes('rel_fee_desc'),
  119. fl = help_notes('fee_spec_letters', use_quotes=True),
  120. kgs = help_notes('keygen_backends'),
  121. coin_id = help_notes('coin_id'),
  122. dsl = help_notes('dfl_seed_len'),
  123. cu = proto.coin),
  124. 'notes': lambda help_mod, help_notes, s: s.format(
  125. g = help_notes('gas_limit', target=None),
  126. F = help_notes('fee'),
  127. s = help_mod('txsign'),
  128. f = help_notes('fmt_codes')),
  129. }
  130. }
  131. cfg = Config(opts_data=opts_data)
  132. from .tx import CompletedTX, BumpTX, UnsignedTX, OnlineSignedTX
  133. from .tx.keys import TxKeys, pop_seedfiles, get_keylist, get_keyaddrlist
  134. seedfiles = pop_seedfiles(cfg, ignore_dfl_wallet=not cfg.send, empty_ok=not cfg.send)
  135. if cfg.autosign:
  136. if cfg.send:
  137. die(1, '--send cannot be used together with --autosign')
  138. else:
  139. tx_file = cfg._args.pop()
  140. from .fileutil import check_infile
  141. check_infile(tx_file)
  142. from .ui import do_license_msg
  143. do_license_msg(cfg)
  144. silent = cfg.yes and cfg.fee is not None and cfg.output_to_reduce is not None
  145. async def main():
  146. if cfg.autosign:
  147. from .tx.util import mount_removable_device
  148. from .autosign import Signable
  149. asi = mount_removable_device(cfg)
  150. si = Signable.automount_transaction(asi)
  151. if si.unsigned or si.unsent:
  152. state = 'unsigned' if si.unsigned else 'unsent'
  153. die(1,
  154. 'Only sent transactions can be bumped with --autosign. Instead of bumping\n'
  155. f'your {state} transaction, abort it with ‘mmgen-txsend --abort’ and create\n'
  156. 'a new one.')
  157. orig_tx = await si.get_last_created()
  158. sign_and_send = False
  159. else:
  160. orig_tx = await CompletedTX(cfg=cfg, filename=tx_file)
  161. kl = get_keylist(cfg)
  162. kal = get_keyaddrlist(cfg, orig_tx.proto)
  163. sign_and_send = any([seedfiles, kl, kal])
  164. if not silent:
  165. msg(green('ORIGINAL TRANSACTION'))
  166. msg(orig_tx.info.format(terse=True))
  167. from .tw.ctl import TwCtl
  168. tx = BumpTX(
  169. cfg = cfg,
  170. data = orig_tx.__dict__,
  171. automount = cfg.autosign,
  172. check_sent = cfg.autosign or sign_and_send,
  173. new_outputs = bool(cfg._args),
  174. twctl = await TwCtl(cfg, orig_tx.proto) if orig_tx.proto.tokensym else None)
  175. if tx.new_outputs:
  176. await tx.create(cfg._args, caller='txdo' if sign_and_send else 'txcreate')
  177. else:
  178. await tx.create_feebump(silent=silent)
  179. if not silent:
  180. msg(green('\nREPLACEMENT TRANSACTION:'))
  181. msg_r(tx.info.format(terse=True))
  182. if sign_and_send:
  183. tx2 = UnsignedTX(cfg=cfg, data=tx.__dict__)
  184. if tx3 := await tx2.sign(
  185. TxKeys(cfg, tx2, seedfiles=seedfiles, keylist=kl, keyaddrlist=kal).keys):
  186. tx4 = await OnlineSignedTX(cfg=cfg, data=tx3.__dict__)
  187. tx4.file.write(ask_write=False)
  188. await tx4.send(cfg, asi if cfg.autosign else None)
  189. else:
  190. die(2, 'Transaction could not be signed')
  191. else:
  192. tx.file.write(
  193. outdir = asi.txauto_dir if cfg.autosign else None,
  194. ask_write = not cfg.yes,
  195. ask_write_default_yes = False,
  196. ask_overwrite = not cfg.yes)
  197. async_run(main())