rpc.py 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264
  1. #!/usr/bin/env python3
  2. #
  3. # MMGen Wallet, a terminal-based cryptocurrency wallet
  4. # Copyright (C)2013-2026 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. tool.rpc: JSON/RPC routines for the 'mmgen-tool' utility
  20. """
  21. from .common import tool_cmd_base, options_annot_str
  22. from ..tw.view import TwView
  23. from ..tw.txhistory import TwTxHistory
  24. class tool_cmd(tool_cmd_base):
  25. "tracking-wallet commands using the JSON-RPC interface"
  26. need_proto = True
  27. need_amt = True
  28. async def daemon_version(self):
  29. "print coin daemon version"
  30. from ..daemon import CoinDaemon
  31. d = CoinDaemon(cfg=self.cfg, proto=self.proto, test_suite=self.cfg.test_suite)
  32. if self.proto.base_proto == 'Monero':
  33. from ..proto.xmr.rpc import MoneroRPCClient
  34. r = MoneroRPCClient(
  35. cfg = self.cfg,
  36. proto = self.proto,
  37. daemon = d,
  38. host = 'localhost',
  39. port = d.rpc_port,
  40. user = None,
  41. passwd = None,
  42. ignore_daemon_version = True)
  43. else:
  44. from ..rpc import rpc_init
  45. r = await rpc_init(self.cfg, self.proto, ignore_daemon_version=True, ignore_wallet=True)
  46. return f'{d.coind_name} version {r.daemon_version} ({r.daemon_version_str})'
  47. async def getbalance(self, *,
  48. minconf: 'minimum number of confirmations' = 1,
  49. quiet: 'produce quieter output' = False,
  50. pager: 'send output to pager' = False):
  51. "list confirmed/unconfirmed, spendable/unspendable balances in tracking wallet"
  52. from ..tw.bal import TwGetBalance
  53. return (await TwGetBalance(
  54. self.cfg, self.proto, minconf=minconf, quiet=quiet)).format(color=self.cfg.color)
  55. async def twops(self,
  56. obj, pager, reverse, detail, sort, age_fmt, interactive,
  57. **kwargs):
  58. obj.reverse = reverse
  59. obj.sort_key = sort or obj.sort_key
  60. obj.age_fmt = age_fmt
  61. for k, v in kwargs.items():
  62. setattr(obj, k, v)
  63. await obj.get_data()
  64. if interactive:
  65. await obj.view_filter_and_sort()
  66. ret = True
  67. else:
  68. ret = await obj.format('detail' if detail else 'squeezed')
  69. if hasattr(obj, 'twctl'):
  70. del obj.twctl
  71. return ret
  72. async def twview(self, *,
  73. pager: 'send output to pager' = False,
  74. reverse: 'reverse order of unspent outputs' = False,
  75. wide: 'display data in wide tabular format' = False,
  76. minconf: 'minimum number of confirmations' = 1,
  77. sort: 'unspent output sort order ' + options_annot_str(TwView.sort_funcs) = 'age',
  78. age_fmt: 'format for the Age/Date column ' + options_annot_str(TwView.age_fmts) = 'confs',
  79. interactive: 'enable interactive operation' = False,
  80. show_mmid: 'show MMGen IDs along with coin addresses' = True):
  81. "view tracking wallet unspent outputs"
  82. from ..tw.unspent import TwUnspentOutputs
  83. obj = await TwUnspentOutputs(self.cfg, self.proto, minconf=minconf)
  84. return await self.twops(
  85. obj, pager, reverse, wide, sort, age_fmt, interactive,
  86. show_mmid = show_mmid)
  87. async def txhist(self, *,
  88. pager: 'send output to pager' = False,
  89. reverse: 'reverse order of transactions' = False,
  90. detail: 'produce detailed, non-tabular output' = False,
  91. sinceblock: 'display transactions starting from this block' = 0,
  92. sort: 'transaction sort order ' + options_annot_str(TwTxHistory.sort_funcs) = 'age',
  93. age_fmt: 'format for the Age/Date column ' + options_annot_str(TwView.age_fmts) = 'confs',
  94. interactive: 'enable interactive operation' = False):
  95. "view transaction history of tracking wallet"
  96. obj = await TwTxHistory(self.cfg, self.proto, sinceblock=sinceblock)
  97. return await self.twops(
  98. obj, pager, reverse, detail, sort, age_fmt, interactive)
  99. async def listaddress(self,
  100. mmgen_addr: str,
  101. *,
  102. wide: 'display data in wide tabular format' = False,
  103. minconf: 'minimum number of confirmations' = 1,
  104. showcoinaddr: 'display coin address in addition to MMGen ID' = True,
  105. age_fmt: 'format for the Age/Date column ' + options_annot_str(TwView.age_fmts) = 'confs'):
  106. "list the specified MMGen address in the tracking wallet and its balance"
  107. return await self.listaddresses(
  108. mmgen_addrs = mmgen_addr,
  109. wide = wide,
  110. minconf = minconf,
  111. showcoinaddrs = showcoinaddr,
  112. age_fmt = age_fmt)
  113. async def listaddresses(self, *,
  114. pager: 'send output to pager' = False,
  115. reverse: 'reverse order of unspent outputs' = False,
  116. wide: 'display data in wide tabular format' = False,
  117. minconf: 'minimum number of confirmations' = 1,
  118. sort: 'address sort order ' + options_annot_str(['reverse', 'mmid', 'addr', 'amt']) = '',
  119. age_fmt: 'format for the Age/Date column ' + options_annot_str(TwView.age_fmts) = 'confs',
  120. interactive: 'enable interactive operation' = False,
  121. mmgen_addrs: 'hyphenated range or comma-separated list of addresses' = '',
  122. showcoinaddrs:'display coin addresses in addition to MMGen IDs' = True,
  123. showempty: 'show addresses with no balances' = True,
  124. showused: 'show used addresses (tristate: 0=no, 1=yes, 2=only)' = 1,
  125. all_labels: 'show all addresses with labels' = False):
  126. "list MMGen addresses in the tracking wallet and their balances"
  127. assert showused in (0, 1, 2), "‘showused’ must have a value of 0, 1 or 2"
  128. from ..tw.addresses import TwAddresses
  129. obj = await TwAddresses(self.cfg, self.proto, minconf=minconf, mmgen_addrs=mmgen_addrs)
  130. return await self.twops(
  131. obj, pager, reverse, wide, sort, age_fmt, interactive,
  132. showcoinaddrs = showcoinaddrs,
  133. showempty = showempty,
  134. showused = showused,
  135. all_labels = all_labels)
  136. async def add_label(self, mmgen_or_coin_addr: str, label: str):
  137. "add descriptive label for address in tracking wallet"
  138. from ..obj import TwComment
  139. from ..tw.ctl import TwCtl
  140. ret = await (await TwCtl(self.cfg, self.proto, mode='w')).set_comment(mmgen_or_coin_addr, label)
  141. return True if isinstance(ret, TwComment) else False
  142. async def remove_label(self, mmgen_or_coin_addr: str):
  143. "remove descriptive label for address in tracking wallet"
  144. return await self.add_label(mmgen_or_coin_addr, '')
  145. async def remove_address(self, mmgen_or_coin_addr: str):
  146. "remove an address from tracking wallet"
  147. from ..tw.ctl import TwCtl
  148. # returns None on failure:
  149. ret = await (await TwCtl(self.cfg, self.proto, mode='w')).remove_address(mmgen_or_coin_addr)
  150. if ret:
  151. from ..util import msg
  152. msg(f'Address {ret!r} deleted from tracking wallet')
  153. return ret
  154. async def resolve_address(self, mmgen_or_coin_addr: str):
  155. "resolve an MMGen address in the tracking wallet to a coin address or vice-versa"
  156. from ..tw.ctl import TwCtl
  157. ret = await (await TwCtl(self.cfg, self.proto, mode='w')).resolve_address(mmgen_or_coin_addr)
  158. if ret:
  159. from ..addr import is_coin_addr
  160. return ret.twmmid if is_coin_addr(self.proto, mmgen_or_coin_addr) else ret.coinaddr
  161. else:
  162. return False
  163. async def rescan_address(self, mmgen_or_coin_addr: str):
  164. "rescan an address in the tracking wallet to update its balance"
  165. from ..tw.ctl import TwCtl
  166. return await (await TwCtl(self.cfg, self.proto, mode='w')).rescan_address(mmgen_or_coin_addr)
  167. async def rescan_blockchain(self, *,
  168. start_block: int = None,
  169. stop_block: int = None):
  170. """
  171. rescan the blockchain to update historical transactions in the tracking wallet
  172. NOTE:
  173. The rescanning process typically takes several hours and may be interrupted
  174. using Ctrl-C. An interrupted rescan may be resumed using the ‘start_block’
  175. parameter.
  176. """
  177. from ..tw.ctl import TwCtl
  178. await (await TwCtl(self.cfg, self.proto, mode='w')).rescan_blockchain(start_block, stop_block)
  179. return True
  180. async def twexport(self, *,
  181. include_amts = True,
  182. pretty = False,
  183. prune = False,
  184. warn_used = False,
  185. force = False):
  186. """
  187. export a tracking wallet to JSON format
  188. NOTES:
  189. If ‘include_amts’ is true (the default), Ethereum balances will be restored
  190. from the dump upon import. For Bitcoin and forks, amount fields in the dump
  191. are ignored.
  192. If ‘pretty’ is true, JSON will be dumped in human-readable format to allow
  193. for editing of comment fields.
  194. If ‘prune’ is true, an interactive menu will be launched allowing the user
  195. to prune unwanted addresses before creating the JSON dump. Pruning has no
  196. effect on the existing tracking wallet.
  197. If ‘warn_used’ is true, the user will be prompted before pruning used
  198. addresses.
  199. If ‘force’ is true, any existing dump will be overwritten without prompting.
  200. """
  201. from ..tw.json import TwJSON
  202. await TwJSON.Export(
  203. self.cfg,
  204. self.proto,
  205. include_amts = include_amts,
  206. pretty = pretty,
  207. prune = prune,
  208. warn_used = warn_used,
  209. force_overwrite = force)
  210. return True
  211. async def twimport(self, filename: str, *, ignore_checksum=False, batch=False):
  212. """
  213. restore a tracking wallet from a JSON dump created by ‘twexport’
  214. NOTES:
  215. If comment fields in the JSON dump have been edited, ‘ignore_checksum’ must
  216. be set to true.
  217. The restored tracking wallet will have correct balances but no record of
  218. historical transactions. These may be restored by running ‘mmgen-tool
  219. rescan_blockchain’.
  220. """
  221. from ..tw.json import TwJSON
  222. await TwJSON.Import(self.cfg, self.proto, filename, ignore_checksum=ignore_checksum, batch=batch)
  223. return True