main_xmrwallet.py 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283
  1. #!/usr/bin/env python3
  2. #
  3. # mmgen = Multi-Mode GENerator, command-line Bitcoin cold storage solution
  4. # Copyright (C)2013-2023 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-xmrwallet: Perform various Monero wallet operations for addresses
  20. in an MMGen XMR key-address file
  21. """
  22. from .common import *
  23. from .xmrwallet import xmrwallet_uarg_info,MoneroWalletOps
  24. opts_data = {
  25. 'text': {
  26. 'desc': """Perform various Monero wallet operations for addresses
  27. in an MMGen XMR key-address file""",
  28. 'usage2': [
  29. '[opts] create <xmr_keyaddrfile> [wallets]',
  30. '[opts] sync <xmr_keyaddrfile> [wallets]',
  31. '[opts] list <xmr_keyaddrfile> [wallets]',
  32. '[opts] label <xmr_keyaddrfile> LABEL_SPEC',
  33. '[opts] new <xmr_keyaddrfile> NEW_ADDRESS_SPEC',
  34. '[opts] transfer <xmr_keyaddrfile> TRANSFER_SPEC',
  35. '[opts] sweep <xmr_keyaddrfile> SWEEP_SPEC',
  36. '[opts] relay <TX_file>',
  37. '[opts] txview <TX_file> ...',
  38. ],
  39. 'options': """
  40. -h, --help Print this help message
  41. --, --longhelp Print help message for long options (common
  42. options)
  43. -b, --rescan-blockchain Rescan the blockchain if wallet fails to sync
  44. -d, --outdir=D Save transaction files to directory 'D'
  45. instead of the working directory
  46. -D, --daemon=H:P Connect to the monerod at {D}
  47. -R, --tx-relay-daemon=H:P[:H:P] Relay transactions via a monerod specified by
  48. {R}
  49. -k, --use-internal-keccak-module Force use of the internal keccak module
  50. -p, --hash-preset=P Use scrypt hash preset 'P' for password
  51. hashing (default: '{g.dfl_hash_preset}')
  52. -r, --restore-height=H Scan from height 'H' when creating wallets
  53. -R, --no-relay Save transaction to file instead of relaying
  54. -s, --no-start-wallet-daemon Don’t start the wallet daemon at startup
  55. -S, --no-stop-wallet-daemon Don’t stop the wallet daemon at exit
  56. -w, --wallet-dir=D Output or operate on wallets in directory 'D'
  57. instead of the working directory
  58. -H, --wallet-rpc-host=host Wallet RPC hostname (default: {g.monero_wallet_rpc_host!r})
  59. -U, --wallet-rpc-user=user Wallet RPC username (default: {g.monero_wallet_rpc_user!r})
  60. -P, --wallet-rpc-password=pass Wallet RPC password (default: {g.monero_wallet_rpc_password!r})
  61. """,
  62. 'notes': """
  63. All operations except for ‘relay’ require a running Monero daemon. Unless
  64. --daemon is specified, the monerod is assumed to be listening on localhost at
  65. the default RPC port.
  66. If --tx-relay-daemon is specified, the monerod at HOST:PORT will be used to
  67. relay any created transactions. PROXY_HOST:PROXY_PORT, if specified, may
  68. point to a SOCKS proxy, in which case HOST may be a Tor onion address.
  69. All communications use the RPC protocol via SSL (HTTPS) or Tor. RPC over
  70. plain HTTP is not supported.
  71. SUPPORTED OPERATIONS
  72. create - create wallet for all or specified addresses in key-address file
  73. sync - sync wallet for all or specified addresses in key-address file
  74. list - same as 'sync', but also list detailed address info for accounts
  75. label - set a label for an address
  76. new - create a new account in a wallet, or a new address in an account
  77. transfer - transfer specified XMR amount from specified wallet:account to
  78. specified address
  79. sweep - sweep funds in specified wallet:account to new address in same
  80. account or new account in another wallet
  81. relay - relay a transaction from a transaction file created using 'sweep'
  82. or 'transfer' with the --no-relay option
  83. txview - view a transaction file or files created using 'sweep' or
  84. 'transfer' with the --no-relay option
  85. 'CREATE', 'SYNC' AND 'LIST' OPERATION NOTES
  86. These operations take an optional `wallets` argument: one or more address
  87. indexes (expressed as a comma-separated list, hyphenated range, or both)
  88. in the specified key-address file, each corresponding to a Monero wallet
  89. to be created, synced or listed. If omitted, all wallets are operated upon.
  90. 'LABEL' OPERATION NOTES
  91. This operation takes a LABEL_SPEC arg with the following format:
  92. WALLET:ACCOUNT:ADDRESS,"label text"
  93. where WALLET is a wallet number, ACCOUNT an account index, and ADDRESS an
  94. address index.
  95. 'NEW' OPERATION NOTES
  96. This operation takes a NEW_ADDRESS_SPEC arg with the following format:
  97. WALLET[:ACCOUNT][,"label text"]
  98. where WALLET is a wallet number and ACCOUNT an account index. If ACCOUNT is
  99. omitted, a new account will be created in the wallet, otherwise a new address
  100. will be created in the specified account. An optional label text may be
  101. appended to the spec following a comma.
  102. 'TRANSFER' OPERATION NOTES
  103. The transfer operation takes a TRANSFER_SPEC arg with the following format:
  104. SOURCE:ACCOUNT:ADDRESS,AMOUNT
  105. where SOURCE is a wallet number; ACCOUNT the source account index; and ADDRESS
  106. and AMOUNT the destination Monero address and XMR amount, respectively.
  107. 'SWEEP' OPERATION NOTES
  108. The sweep operation takes a SWEEP_SPEC arg with the following format:
  109. SOURCE:ACCOUNT[,DEST]
  110. where SOURCE and DEST are wallet numbers and ACCOUNT an account index.
  111. If DEST is omitted, a new address will be created in ACCOUNT of SOURCE and
  112. all funds from ACCOUNT of SOURCE will be swept into it.
  113. If DEST is included, all funds from ACCOUNT of SOURCE will be swept into a
  114. newly created account in DEST, or the last existing account, if requested
  115. by the user.
  116. The user is prompted before addresses are created or funds are transferred.
  117. Note that multiple sweep operations may be required to sweep all the funds
  118. in an account.
  119. 'RELAY' OPERATION NOTES
  120. By default, transactions are relayed to a monerod running on localhost at the
  121. default RPC port. To relay transactions to a remote or non-default monerod
  122. via optional SOCKS proxy, use the --tx-relay-daemon option described above.
  123. WARNING
  124. To avoid exposing your private keys on a network-connected machine, you’re
  125. strongly advised to create all transactions offline using the --no-relay
  126. option. For this, a monerod with a fully synced blockchain must be running
  127. on the offline machine. The resulting transaction files are then sent using
  128. the 'relay' operation.
  129. EXAMPLES
  130. Generate an XMR key-address file with 5 addresses from your default wallet:
  131. $ mmgen-keygen --coin=xmr 1-5
  132. Create 3 Monero wallets from the key-address file:
  133. $ mmgen-xmrwallet create *.akeys.mmenc 1-3
  134. After updating the blockchain, sync wallets 1 and 2:
  135. $ mmgen-xmrwallet sync *.akeys.mmenc 1,2
  136. Sweep all funds from account #0 of wallet 1 to a new address:
  137. $ mmgen-xmrwallet sweep *.akeys.mmenc 1:0
  138. Same as above, but use a TX relay on the Tor network:
  139. $ mmgen-xmrwallet --tx-relay-daemon=abcdefghijklmnop.onion:127.0.0.1:9050 sweep *.akeys.mmenc 1:0
  140. Sweep all funds from account #0 of wallet 1 to wallet 2:
  141. $ mmgen-xmrwallet sweep *.akeys.mmenc 1:0,2
  142. Send 0.1 XMR from account #0 of wallet 2 to an external address:
  143. $ mmgen-xmrwallet transfer *.akeys.mmenc 2:0:<monero address>,0.1
  144. Sweep all funds from account #0 of wallet 2 to a new address, saving the
  145. transaction to a file:
  146. $ mmgen-xmrwallet --no-relay sweep *.akeys.mmenc 2:0
  147. Relay the created sweep transaction via a host on the Tor network:
  148. $ mmgen-xmrwallet --tx-relay-daemon=abcdefghijklmnop.onion:127.0.0.1:9050 relay *XMR*.sigtx
  149. Create a new account in wallet 2:
  150. $ mmgen-xmrwallet new *.akeys.mmenc 2
  151. Create a new address in account 1 of wallet 2, with label:
  152. $ mmgen-xmrwallet new *.akeys.mmenc 2:1,"from ABC exchange"
  153. View all the XMR transaction files in the current directory, sending output
  154. to pager:
  155. $ mmgen-xmrwallet --pager txview *XMR*.sigtx
  156. """
  157. },
  158. 'code': {
  159. 'options': lambda s: s.format(
  160. D=xmrwallet_uarg_info['daemon'].annot,
  161. R=xmrwallet_uarg_info['tx_relay_daemon'].annot,
  162. g=g,
  163. ),
  164. }
  165. }
  166. cmd_args = opts.init(opts_data)
  167. if len(cmd_args) < 2:
  168. opts.usage()
  169. op = cmd_args.pop(0)
  170. infile = cmd_args.pop(0)
  171. if op not in MoneroWalletOps.ops:
  172. die(1,f'{op!r}: unrecognized operation')
  173. wallets = spec = ''
  174. if op == 'relay':
  175. if len(cmd_args) != 0:
  176. opts.usage()
  177. elif op == 'txview':
  178. infile = [infile] + cmd_args
  179. elif op in ('create','sync','list'):
  180. if len(cmd_args) not in (0,1):
  181. opts.usage()
  182. if cmd_args:
  183. wallets = cmd_args[0]
  184. elif op in ('new','transfer','sweep','label'):
  185. if len(cmd_args) != 1:
  186. opts.usage()
  187. spec = cmd_args[0]
  188. ua = namedtuple('uargs',[ 'op', 'infile', 'wallets', 'spec' ])
  189. uo = namedtuple('uopts',[
  190. 'daemon',
  191. 'tx_relay_daemon',
  192. 'restore_height',
  193. 'rescan_blockchain',
  194. 'no_start_wallet_daemon',
  195. 'no_stop_wallet_daemon',
  196. 'no_relay',
  197. 'wallet_dir',
  198. ])
  199. uargs = ua( op, infile, wallets, spec )
  200. uopts = uo(
  201. opt.daemon or '',
  202. opt.tx_relay_daemon or '',
  203. opt.restore_height or 0,
  204. opt.rescan_blockchain,
  205. opt.no_start_wallet_daemon,
  206. opt.no_stop_wallet_daemon,
  207. opt.no_relay,
  208. opt.wallet_dir,
  209. )
  210. m = getattr(MoneroWalletOps,op)(uargs,uopts)
  211. try:
  212. if async_run(m.main()):
  213. m.post_main()
  214. except KeyboardInterrupt:
  215. ymsg('\nUser interrupt')
  216. finally:
  217. async_run(m.stop_wallet_daemon())