halving-calculator.py 2.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990
  1. #!/usr/bin/env python3
  2. #
  3. # MMGen Wallet, a terminal-based cryptocurrency wallet
  4. # Copyright (C)2013-2024 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. examples.halving-calculator.py: Demonstrate use of the MMGen asyncio/aiohttp JSON-RPC interface
  12. """
  13. import time
  14. from mmgen.cfg import Config
  15. from mmgen.util import async_run
  16. opts_data = {
  17. 'text': {
  18. 'desc': 'Estimate date of next block subsidy halving',
  19. 'usage':'[opts]',
  20. 'options': """
  21. -h, --help Print this help message
  22. --, --longhelp Print help message for long (global) options
  23. -s, --sample-size=N Specify sample block range for block discovery time
  24. estimate
  25. """,
  26. 'notes': """
  27. Requires a running coin daemon
  28. Specify coin with --coin=btc (default)/--coin=bch/--coin=ltc
  29. If necessary, invoke with --rpc-host/--rpc-port/--rpc-user/--rpc-password
  30. Specify aiohttp backend with --rpc-backend=aiohttp (Linux only)
  31. A more full-featured version of this program can be found in the
  32. mmgen-node-tools repository.
  33. """
  34. }
  35. }
  36. cfg = Config(opts_data=opts_data)
  37. def date(t):
  38. return '{}-{:02}-{:02} {:02}:{:02}:{:02}'.format(*time.gmtime(t)[:6])
  39. def dhms(t):
  40. t, neg = (-t, '-') if t < 0 else (t, ' ')
  41. return f'{neg}{t//60//60//24} days, {t//60//60%24:02}:{t//60%60:02}:{t%60:02} h/m/s'
  42. def time_diff_warning(t_diff):
  43. if abs(t_diff) > 60*60:
  44. print('Warning: block tip time is {} {} clock time!'.format(
  45. dhms(abs(t_diff)),
  46. ('behind', 'ahead of')[t_diff<0]))
  47. async def main():
  48. proto = cfg._proto
  49. from mmgen.rpc import rpc_init
  50. c = await rpc_init(cfg, proto, ignore_wallet=True)
  51. tip = await c.call('getblockcount')
  52. assert tip > 1, 'block tip must be > 1'
  53. remaining = proto.halving_interval - tip % proto.halving_interval
  54. sample_size = int(cfg.sample_size) if cfg.sample_size else min(tip-1, max(remaining, 144))
  55. # aiohttp backend will perform these two calls concurrently:
  56. cur, old = await c.gathered_call('getblockstats', ((tip,), (tip - sample_size,)))
  57. clock_time = int(time.time())
  58. time_diff_warning(clock_time - cur['time'])
  59. bdr = (cur['time'] - old['time']) / sample_size
  60. t_rem = remaining * int(bdr)
  61. sub = proto.coin_amt(cur['subsidy'], from_unit='satoshi' if isinstance(cur['subsidy'], int) else None)
  62. print(
  63. f'Current block: {tip}\n'
  64. f'Next halving block: {tip + remaining}\n'
  65. f'Blocks until halving: {remaining}\n'
  66. f'Current block subsidy: {str(sub).rstrip("0")} {proto.coin}\n'
  67. f'Current block discovery rate (over last {sample_size} blocks): {bdr/60:0.1f} minutes\n'
  68. f'Current clock time (UTC): {date(clock_time)}\n'
  69. f'Est. halving date (UTC): {date(cur["time"] + t_rem)}\n'
  70. f'Est. time until halving: {dhms(cur["time"] + t_rem - clock_time)}\n'
  71. )
  72. async_run(main())