rpc.py 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235
  1. #!/usr/bin/env python3
  2. #
  3. # mmgen = Multi-Mode GENerator, command-line Bitcoin cold storage solution
  4. # Copyright (C)2013-2022 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.py: JSON/RPC routines for the 'mmgen-tool' utility
  20. """
  21. from .common import tool_cmd_base,options_annot_str
  22. from ..tw.common import TwCommon
  23. class tool_cmd(tool_cmd_base):
  24. "tracking wallet commands using the JSON-RPC interface"
  25. need_proto = True
  26. need_amt = True
  27. async def daemon_version(self):
  28. "print coin daemon version"
  29. from ..rpc import rpc_init
  30. r = await rpc_init( self.proto, ignore_daemon_version=True )
  31. return f'{r.daemon.coind_name} version {r.daemon_version} ({r.daemon_version_str})'
  32. async def getbalance(self,minconf=1,quiet=False,pager=False):
  33. "list confirmed/unconfirmed, spendable/unspendable balances in tracking wallet"
  34. from ..tw.bal import TwGetBalance
  35. return (await TwGetBalance(self.proto,minconf,quiet)).format()
  36. async def listaddress(self,
  37. mmgen_addr:str,
  38. minconf = 1,
  39. pager = False,
  40. showempty = True,
  41. showbtcaddr = True,
  42. age_fmt: options_annot_str(TwCommon.age_fmts) = 'confs' ):
  43. "list the specified MMGen address and its balance"
  44. return await self.listaddresses(
  45. mmgen_addrs = mmgen_addr,
  46. minconf = minconf,
  47. pager = pager,
  48. showempty = showempty,
  49. showbtcaddrs = showbtcaddr,
  50. age_fmt = age_fmt )
  51. async def listaddresses(self,
  52. mmgen_addrs:'(range or list)' = '',
  53. minconf = 1,
  54. showempty = False,
  55. pager = False,
  56. showbtcaddrs = True,
  57. all_labels = False,
  58. sort: options_annot_str(['reverse','age']) = '',
  59. age_fmt: options_annot_str(TwCommon.age_fmts) = 'confs' ):
  60. "list MMGen addresses and their balances"
  61. show_age = bool(age_fmt)
  62. if sort:
  63. sort = set(sort.split(','))
  64. sort_params = {'reverse','age'}
  65. if not sort.issubset( sort_params ):
  66. from ..util import die
  67. die(1,"The sort option takes the following parameters: '{}'".format( "','".join(sort_params) ))
  68. usr_addr_list = []
  69. if mmgen_addrs:
  70. a = mmgen_addrs.rsplit(':',1)
  71. if len(a) != 2:
  72. from ..util import die
  73. die(1,
  74. f'{mmgen_addrs}: invalid address list argument ' +
  75. '(must be in form <seed ID>:[<type>:]<idx list>)' )
  76. from ..addr import MMGenID
  77. from ..addrlist import AddrIdxList
  78. usr_addr_list = [MMGenID(self.proto,f'{a[0]}:{i}') for i in AddrIdxList(a[1])]
  79. from ..tw.addrs import TwAddrList
  80. al = await TwAddrList( self.proto, usr_addr_list, minconf, showempty, showbtcaddrs, all_labels )
  81. if not al:
  82. from ..util import die
  83. die(0,('No tracked addresses with balances!','No tracked addresses!')[showempty])
  84. return await al.format( showbtcaddrs, sort, show_age, age_fmt or 'confs' )
  85. async def twops(self,
  86. obj,pager,reverse,detail,sort,age_fmt,interactive,show_mmid):
  87. obj.interactive = interactive
  88. obj.reverse = reverse
  89. obj.age_fmt = age_fmt
  90. obj.show_mmid = show_mmid
  91. await obj.get_data(sort_key=sort,reverse_sort=reverse)
  92. if interactive:
  93. await obj.view_and_sort()
  94. return True
  95. elif detail:
  96. return await obj.format_detail( color=True )
  97. else:
  98. return await obj.format_squeezed()
  99. async def twview(self,
  100. pager = False,
  101. reverse = False,
  102. wide = False,
  103. minconf = 1,
  104. sort = 'age',
  105. age_fmt: options_annot_str(TwCommon.age_fmts) = 'confs',
  106. interactive = False,
  107. show_mmid = True ):
  108. "view tracking wallet unspent outputs"
  109. from ..tw.unspent import TwUnspentOutputs
  110. obj = await TwUnspentOutputs(self.proto,minconf=minconf)
  111. ret = await self.twops(
  112. obj,pager,reverse,wide,sort,age_fmt,interactive,show_mmid)
  113. del obj.wallet
  114. return ret
  115. async def txhist(self,
  116. pager = False,
  117. reverse = False,
  118. detail = False,
  119. sinceblock = 0,
  120. sort = 'age',
  121. age_fmt: options_annot_str(TwCommon.age_fmts) = 'confs',
  122. interactive = False ):
  123. "view transaction history"
  124. from ..tw.txhistory import TwTxHistory
  125. obj = await TwTxHistory(self.proto,sinceblock=sinceblock)
  126. return await self.twops(
  127. obj,pager,reverse,detail,sort,age_fmt,interactive,show_mmid=None)
  128. async def add_label(self,mmgen_or_coin_addr:str,label:str):
  129. "add descriptive label for address in tracking wallet"
  130. from ..tw.ctl import TrackingWallet
  131. await (await TrackingWallet(self.proto,mode='w')).add_label( mmgen_or_coin_addr, label, on_fail='raise' )
  132. return True
  133. async def remove_label(self,mmgen_or_coin_addr:str):
  134. "remove descriptive label for address in tracking wallet"
  135. await self.add_label( mmgen_or_coin_addr, '' )
  136. return True
  137. async def remove_address(self,mmgen_or_coin_addr:str):
  138. "remove an address from tracking wallet"
  139. from ..tw.ctl import TrackingWallet
  140. # returns None on failure:
  141. ret = await (await TrackingWallet(self.proto,mode='w')).remove_address(mmgen_or_coin_addr)
  142. if ret:
  143. from ..util import msg
  144. msg(f'Address {ret!r} deleted from tracking wallet')
  145. return ret
  146. async def resolve_address(self,mmgen_or_coin_addr:str):
  147. "resolve an MMGen address in the tracking wallet to a coin address or vice-versa"
  148. from ..tw.ctl import TrackingWallet
  149. ret = await (await TrackingWallet(self.proto,mode='w')).resolve_address( mmgen_or_coin_addr )
  150. if ret:
  151. from ..util import Msg
  152. from ..addr import is_coin_addr
  153. return ret.mmaddr if is_coin_addr(self.proto,mmgen_or_coin_addr) else ret.coinaddr
  154. else:
  155. return False
  156. async def rescan_address(self,mmgen_or_coin_addr:str):
  157. "rescan an address in the tracking wallet to update its balance"
  158. from ..tw.ctl import TrackingWallet
  159. return await (await TrackingWallet(self.proto,mode='w')).rescan_address( mmgen_or_coin_addr )
  160. async def rescan_blockchain(self,
  161. start_block: int = None,
  162. stop_block: int = None ):
  163. """
  164. rescan the blockchain to update historical transactions in the tracking wallet
  165. NOTE:
  166. The rescanning process typically takes several hours and may be interrupted
  167. using Ctrl-C. An interrupted rescan may be resumed using the ‘start_block’
  168. parameter.
  169. """
  170. from ..tw.ctl import TrackingWallet
  171. ret = await (await TrackingWallet(self.proto,mode='w')).rescan_blockchain(start_block,stop_block)
  172. return True
  173. async def twexport(self,include_amts=True,pretty=False):
  174. """
  175. export a tracking wallet to JSON format
  176. NOTES:
  177. If ‘include_amts’ is true (the default), Ethereum balances will be restored
  178. from the dump upon import. For Bitcoin and forks, amount fields in the dump
  179. are ignored.
  180. If ‘pretty’ is true, JSON will be dumped in human-readable format to allow
  181. for editing of comment fields.
  182. """
  183. from ..tw.json import TwJSON
  184. await TwJSON.Export( self.proto, include_amts=include_amts, pretty=pretty )
  185. return True
  186. async def twimport(self,filename:str,ignore_checksum=False,batch=False):
  187. """
  188. restore a tracking wallet from a JSON dump created by ‘twexport’
  189. NOTES:
  190. If comment fields in the JSON dump have been edited, ‘ignore_checksum’ must
  191. be set to true.
  192. The restored tracking wallet will have correct balances but no record of
  193. historical transactions. These may be restored by running ‘mmgen-tool
  194. ‘rescan_blockchain’.
  195. """
  196. from ..tw.json import TwJSON
  197. await TwJSON.Import( self.proto, filename, ignore_checksum=ignore_checksum, batch=batch )
  198. return True