json.py 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162
  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. tw.json: export and import tracking wallet to JSON format
  12. """
  13. import json
  14. from collections import namedtuple
  15. from ..util import msg,ymsg,fmt,die,make_timestamp,make_chksum_8,compare_or_die
  16. from ..base_obj import AsyncInit
  17. from ..objmethods import MMGenObject
  18. from ..rpc import json_encoder
  19. from .ctl import TrackingWallet
  20. class TwJSON:
  21. class Base(MMGenObject):
  22. fn_pfx = 'mmgen-tracking-wallet-dump'
  23. def __new__(cls,proto,*args,**kwargs):
  24. return MMGenObject.__new__(proto.base_proto_subclass(TwJSON,'tw.json',cls.__name__))
  25. def __init__(self,proto):
  26. self.proto = proto
  27. self.coin = proto.coin_id.lower()
  28. self.network = proto.network
  29. self.keys = ['mmgen_id','address','amount','comment']
  30. self.entry_tuple = namedtuple('tw_entry',self.keys)
  31. @property
  32. def dump_fn(self):
  33. return f'{self.fn_pfx}-{self.coin}-{self.network}.json'
  34. def json_dump(self,data,pretty=False):
  35. return json.dumps(
  36. data,
  37. cls = json_encoder,
  38. sort_keys = True,
  39. separators = None if pretty else (',', ':'),
  40. indent = 4 if pretty else None )
  41. def make_chksum(self,data):
  42. return make_chksum_8( self.json_dump(data).encode() ).lower()
  43. @property
  44. def mappings_chksum(self):
  45. return self.make_chksum(self.mappings_json)
  46. @property
  47. def entry_tuple_in(self):
  48. return namedtuple('entry_tuple_in',self.keys)
  49. class Import(Base,metaclass=AsyncInit):
  50. async def __init__(self,proto,filename,ignore_checksum=False,batch=False):
  51. super().__init__(proto)
  52. self.tw = await TrackingWallet( proto, mode='i', rpc_ignore_wallet=True )
  53. def check_network(data):
  54. coin,network = data['network'].split('_')
  55. if coin != self.coin:
  56. die(2,f'Coin in wallet dump is {coin.upper()}, but configured coin is {self.coin.upper()}')
  57. if network != self.network:
  58. die(2,f'Network in wallet dump is {network}, but configured network is {self.network}')
  59. def check_chksum(d):
  60. chksum = self.make_chksum(d['data'])
  61. if chksum != d['checksum']:
  62. if ignore_checksum:
  63. ymsg(f'Warning: ignoring incorrect checksum {chksum}')
  64. else:
  65. die(3,'File checksum incorrect! ({} != {})'.format(chksum,d['checksum']))
  66. def verify_data(d):
  67. check_network(d['data'])
  68. check_chksum(d)
  69. compare_or_die(
  70. self.mappings_chksum, 'computed mappings checksum',
  71. d['data']['mappings_checksum'], 'saved checksum' )
  72. if not await self.check_and_create_wallet():
  73. return True
  74. from ..fileutil import get_data_from_file
  75. self.data = json.loads(get_data_from_file(filename,quiet=True))
  76. self.keys = self.data['data']['entries_keys']
  77. self.entries = await self.get_entries()
  78. verify_data(self.data)
  79. addrs = await self.do_import(batch)
  80. await self.tw.rescan_addresses(addrs)
  81. async def check_and_create_wallet(self):
  82. if await self.tracking_wallet_exists:
  83. die(3,
  84. f'Existing {self.tw.rpc.daemon.desc} wallet detected!\n' +
  85. 'It must be moved, or backed up and securely deleted, before running this command' )
  86. msg('\n'+fmt(self.info_msg.strip(),indent=' '))
  87. from ..ui import keypress_confirm
  88. if not keypress_confirm('Continue?'):
  89. msg('Exiting at user request')
  90. return False
  91. if not await self.create_tracking_wallet():
  92. die(3,'Wallet could not be created')
  93. return True
  94. class Export(Base,metaclass=AsyncInit):
  95. async def __init__(self,proto,include_amts=True,pretty=False):
  96. super().__init__(proto)
  97. if not include_amts:
  98. self.keys.remove('amount')
  99. self.tw = await TrackingWallet( proto )
  100. self.entries = await self.get_entries()
  101. data = {
  102. 'id': 'mmgen_tracking_wallet',
  103. 'version': 1,
  104. 'network': f'{self.coin}_{self.network}',
  105. 'blockheight': self.tw.rpc.blockcount,
  106. 'time': make_timestamp(),
  107. 'mappings_checksum': self.mappings_chksum,
  108. 'entries_keys': self.keys,
  109. 'entries': await self.entries_out,
  110. 'num_entries': self.num_entries,
  111. }
  112. if include_amts:
  113. data['value'] = await self.total
  114. from ..fileutil import write_data_to_file
  115. write_data_to_file(
  116. outfile = self.dump_fn,
  117. data = self.json_dump(
  118. {
  119. 'checksum': self.make_chksum(data),
  120. 'data': data
  121. },
  122. pretty = pretty ),
  123. desc = f'tracking wallet JSON data' )