sync.py 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134
  1. #!/usr/bin/env python3
  2. #
  3. # MMGen Wallet, a terminal-based cryptocurrency wallet
  4. # Copyright (C)2013-2025 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-wallet
  9. # https://gitlab.com/mmgen/mmgen-wallet
  10. """
  11. xmrwallet.ops.sync: Monero wallet ops for the MMGen Suite
  12. """
  13. import time
  14. from ...util import msg, msg_r, ymsg, die
  15. from ..rpc import MoneroWalletRPC
  16. from .wallet import OpWallet
  17. class OpSync(OpWallet):
  18. opts = ('rescan_blockchain', 'skip_empty_accounts', 'skip_empty_addresses')
  19. def check_uopts(self):
  20. if self.cfg.rescan_blockchain and self.cfg.watch_only:
  21. die(1,
  22. f'Operation ‘{self.name}’ does not support --rescan-blockchain with watch-only wallets')
  23. def __init__(self, cfg, uarg_tuple):
  24. super().__init__(cfg, uarg_tuple)
  25. if not self.wallet_offline:
  26. self.dc = self.get_coin_daemon_rpc()
  27. self.wallets_data = {}
  28. async def process_wallet(self, d, fn, last):
  29. chain_height = self.dc.call_raw('get_height')['height']
  30. msg(f' Chain height: {chain_height}')
  31. t_start = time.time()
  32. msg_r(' Opening wallet...')
  33. self.c.call(
  34. 'open_wallet',
  35. filename = fn.name,
  36. password = d.wallet_passwd)
  37. msg('done')
  38. msg_r(' Getting wallet height (be patient, this could take a long time)...')
  39. wallet_height = self.c.call('get_height')['height']
  40. msg_r('\r' + ' '*68 + '\r')
  41. msg(f' Wallet height: {wallet_height} ')
  42. behind = chain_height - wallet_height
  43. if behind > 1000:
  44. msg_r(f' Wallet is {behind} blocks behind chain tip. Please be patient. Syncing...')
  45. ret = self.c.call('refresh')
  46. if behind > 1000:
  47. msg('done')
  48. if ret['received_money']:
  49. msg(' Wallet has received funds')
  50. for i in range(2):
  51. wallet_height = self.c.call('get_height')['height']
  52. if wallet_height >= chain_height:
  53. break
  54. ymsg(' Wallet failed to sync '
  55. f'(wallet height [{wallet_height}] < chain height [{chain_height}])')
  56. if i or not self.cfg.rescan_blockchain:
  57. break
  58. msg_r(' Rescanning blockchain, please be patient...')
  59. self.c.call('rescan_blockchain')
  60. self.c.call('refresh')
  61. msg('done')
  62. t_elapsed = int(time.time() - t_start)
  63. wd = MoneroWalletRPC(self, d).get_wallet_data(print=False, skip_empty_ok=True)
  64. from . import hl_amt
  65. msg(' Balance: {} Unlocked balance: {}'.format(
  66. hl_amt(wd.accts_data['total_balance']),
  67. hl_amt(wd.accts_data['total_unlocked_balance']),
  68. ))
  69. self.wallets_data[fn.name] = wd
  70. msg(f' Wallet height: {wallet_height}')
  71. msg(f' Sync time: {t_elapsed//60:02}:{t_elapsed%60:02}')
  72. if not last:
  73. self.c.call('close_wallet')
  74. return wallet_height >= chain_height
  75. def gen_body(self, wallets_data):
  76. for wnum, (_, wallet_data) in enumerate(wallets_data.items()):
  77. yield from MoneroWalletRPC(self, self.addr_data[wnum]).gen_accts_info(
  78. wallet_data.accts_data,
  79. wallet_data.addrs_data,
  80. indent = '',
  81. skip_empty_ok = True)
  82. yield ''
  83. def post_main_success(self):
  84. def gen_info(data):
  85. yield from self.gen_body(data)
  86. col1_w = max(map(len, data)) + 1
  87. fs = '{:%s} {} {}' % col1_w
  88. tbals = [0, 0]
  89. yield fs.format('Wallet', 'Balance ', 'Unlocked Balance')
  90. from . import fmt_amt
  91. for k in data:
  92. b = data[k].accts_data['total_balance']
  93. ub = data[k].accts_data['total_unlocked_balance']
  94. yield fs.format(k + ':', fmt_amt(b), fmt_amt(ub))
  95. tbals[0] += b
  96. tbals[1] += ub
  97. yield fs.format('-'*col1_w, '-'*18, '-'*18)
  98. yield fs.format('TOTAL:', fmt_amt(tbals[0]), fmt_amt(tbals[1]))
  99. self.cfg._util.stdout_or_pager('\n'.join(gen_info(self.wallets_data)) + '\n')