addrdata.py 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121
  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. addrdata.py: MMGen AddrData and related classes
  20. """
  21. from .util import vmsg,base_proto_subclass
  22. from .base_obj import AsyncInit
  23. from .obj import MMGenObject,MMGenDict,get_obj
  24. from .addr import MMGenID,AddrListID
  25. from .addrlist import AddrListEntry,AddrListData,AddrList
  26. class AddrData(MMGenObject):
  27. msgs = {
  28. 'too_many_acct_addresses': """
  29. ERROR: More than one address found for account: '{}'.
  30. Your 'wallet.dat' file appears to have been altered by a non-{} program.
  31. Please restore your tracking wallet from a backup or create a new one and
  32. re-import your addresses.
  33. """.strip()
  34. }
  35. def __new__(cls,proto,*args,**kwargs):
  36. return MMGenObject.__new__(base_proto_subclass(cls,proto,'tw'))
  37. def __init__(self,proto,*args,**kwargs):
  38. self.al_ids = {}
  39. self.proto = proto
  40. self.rpc = None
  41. def seed_ids(self):
  42. return list(self.al_ids.keys())
  43. def addrlist(self,al_id):
  44. # TODO: Validate al_id
  45. if al_id in self.al_ids:
  46. return self.al_ids[al_id]
  47. def mmaddr2coinaddr(self,mmaddr):
  48. al_id,idx = MMGenID(self.proto,mmaddr).rsplit(':',1)
  49. coinaddr = ''
  50. if al_id in self.al_ids:
  51. coinaddr = self.addrlist(al_id).coinaddr(int(idx))
  52. return coinaddr or None
  53. def coinaddr2mmaddr(self,coinaddr):
  54. d = self.make_reverse_dict([coinaddr])
  55. return (list(d.values())[0][0]) if d else None
  56. def add(self,addrlist):
  57. from .addrlist import AddrList
  58. if type(addrlist) == AddrList:
  59. self.al_ids[addrlist.al_id] = addrlist
  60. return True
  61. else:
  62. raise TypeError(f'Error: object {addrlist!r} is not of type AddrList')
  63. def make_reverse_dict(self,coinaddrs):
  64. d = MMGenDict()
  65. for al_id in self.al_ids:
  66. d.update(self.al_ids[al_id].make_reverse_dict_addrlist(coinaddrs))
  67. return d
  68. class TwAddrData(AddrData,metaclass=AsyncInit):
  69. def __new__(cls,proto,*args,**kwargs):
  70. return MMGenObject.__new__(base_proto_subclass(cls,proto,'tw'))
  71. async def __init__(self,proto,wallet=None):
  72. from .rpc import rpc_init
  73. from .tw import TwLabel
  74. from .globalvars import g
  75. from .seed import SeedID
  76. self.proto = proto
  77. self.rpc = await rpc_init(proto)
  78. self.al_ids = {}
  79. twd = await self.get_tw_data(wallet)
  80. out,i = {},0
  81. for acct,addr_array in twd:
  82. l = get_obj(TwLabel,proto=self.proto,text=acct,silent=True)
  83. if l and l.mmid.type == 'mmgen':
  84. obj = l.mmid.obj
  85. if len(addr_array) != 1:
  86. die(2,self.msgs['too_many_acct_addresses'].format(acct,g.prog_name))
  87. al_id = AddrListID(SeedID(sid=obj.sid),self.proto.addr_type(obj.mmtype))
  88. if al_id not in out:
  89. out[al_id] = []
  90. out[al_id].append(AddrListEntry(self.proto,idx=obj.idx,addr=addr_array[0],label=l.comment))
  91. i += 1
  92. vmsg(f'{i} {g.prog_name} addresses found, {len(twd)} accounts total')
  93. for al_id in out:
  94. self.add(AddrList(self.proto,al_id=al_id,adata=AddrListData(sorted(out[al_id],key=lambda a: a.idx))))
  95. async def get_tw_data(self,wallet=None):
  96. vmsg('Getting address data from tracking wallet')
  97. c = self.rpc
  98. if 'label_api' in c.caps:
  99. accts = await c.call('listlabels')
  100. ll = await c.batch_call('getaddressesbylabel',[(k,) for k in accts])
  101. alists = [list(a.keys()) for a in ll]
  102. else:
  103. accts = await c.call('listaccounts',0,True)
  104. alists = await c.batch_call('getaddressesbyaccount',[(k,) for k in accts])
  105. return list(zip(accts,alists))