rpc.py 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112
  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. proto.btc.tw.rpc: Bitcoin base protocol tracking wallet RPC classes
  12. """
  13. from ....addr import CoinAddr
  14. from ....util import die,msg,rmsg
  15. from ....obj import MMGenList
  16. from ....tw.shared import get_tw_label
  17. class BitcoinTwRPC:
  18. async def get_addr_label_pairs(self,twmmid=None):
  19. """
  20. Get all the accounts in the tracking wallet and their associated addresses.
  21. Returns list of (label,address) tuples.
  22. """
  23. def check_dup_mmid(acct_labels):
  24. mmid_prev,err = None,False
  25. for mmid in sorted(label.mmid for label in acct_labels if label):
  26. if mmid == mmid_prev:
  27. err = True
  28. msg(f'Duplicate MMGen ID ({mmid}) discovered in tracking wallet!\n')
  29. mmid_prev = mmid
  30. if err:
  31. die(4,'Tracking wallet is corrupted!')
  32. async def get_acct_list():
  33. if 'label_api' in self.rpc.caps:
  34. return await self.rpc.call('listlabels')
  35. else:
  36. return (await self.rpc.call('listaccounts',0,True)).keys()
  37. async def get_acct_addrs(acct_list):
  38. if 'label_api' in self.rpc.caps:
  39. return [list(a.keys())
  40. for a in await self.rpc.batch_call('getaddressesbylabel',[(k,) for k in acct_list])]
  41. else:
  42. return await self.rpc.batch_call('getaddressesbyaccount',[(a,) for a in acct_list])
  43. acct_labels = [get_tw_label(self.proto,a) for a in await get_acct_list()]
  44. if twmmid:
  45. acct_labels = [lbl for lbl in acct_labels if lbl.mmid == twmmid]
  46. if not acct_labels:
  47. return None
  48. check_dup_mmid(acct_labels)
  49. acct_addrs = await get_acct_addrs(acct_labels)
  50. for n,a in enumerate(acct_addrs):
  51. if len(a) != 1:
  52. raise ValueError(f'{a}: label {acct_labels[n]!r} has != 1 associated address!')
  53. return [(
  54. label,
  55. CoinAddr(self.proto,addrs[0])
  56. ) for label,addrs in zip(acct_labels,acct_addrs)]
  57. async def get_unspent_by_mmid(self,minconf=1,mmid_filter=[]):
  58. """
  59. get unspent outputs in tracking wallet, compute balances per address
  60. and return a dict with elements { 'twmmid': {'addr','lbl','amt'} }
  61. """
  62. data = {}
  63. lbl_id = ('account','label')['label_api' in self.rpc.caps]
  64. amt0 = self.proto.coin_amt('0')
  65. for d in await self.rpc.call('listunspent',0):
  66. if not lbl_id in d:
  67. continue # skip coinbase outputs with missing account
  68. if d['confirmations'] < minconf:
  69. continue
  70. label = get_tw_label(self.proto,d[lbl_id])
  71. if label:
  72. lm = label.mmid
  73. if mmid_filter and (lm not in mmid_filter):
  74. continue
  75. if lm in data:
  76. if data[lm]['addr'] != d['address']:
  77. die(2,'duplicate {} address ({}) for this MMGen address! ({})'.format(
  78. self.proto.coin,
  79. d['address'],
  80. data[lm]['addr'] ))
  81. else:
  82. lm.confs = d['confirmations']
  83. lm.txid = d['txid']
  84. lm.vout = d['vout']
  85. lm.date = None
  86. data[lm] = {
  87. 'amt': amt0,
  88. 'lbl': label,
  89. 'addr': CoinAddr(self.proto,d['address']) }
  90. amt = self.proto.coin_amt(d['amount'])
  91. data[lm]['amt'] += amt
  92. return data