info.py 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129
  1. #!/usr/bin/env python3
  2. #
  3. # mmgen = Multi-Mode GENerator, a command-line 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. tx.info: transaction info class
  12. """
  13. import importlib
  14. from ..cfg import gc
  15. from ..color import red,green,orange
  16. from ..util import msg,msg_r,decode_timestamp,make_timestr
  17. from ..util2 import format_elapsed_hr
  18. class TxInfo:
  19. def __init__(self,tx):
  20. self.tx = tx
  21. def format(self,terse=False,sort='addr'):
  22. tx = self.tx
  23. if tx.proto.base_proto == 'Ethereum':
  24. blockcount = None
  25. else:
  26. try:
  27. blockcount = tx.rpc.blockcount
  28. except:
  29. blockcount = None
  30. def get_max_mmwid(io):
  31. sel_f = (
  32. (lambda o: len(o.mmid) + 2) if io == tx.inputs else # 2 = len('()')
  33. (lambda o: len(o.mmid) + (2,8)[bool(o.is_chg)]) ) # 6 = len(' (chg)')
  34. return max(max([sel_f(o) for o in io if o.mmid] or [0]),len(nonmm_str))
  35. nonmm_str = f'(non-{gc.proj_name} address)'
  36. max_mmwid = max(get_max_mmwid(tx.inputs),get_max_mmwid(tx.outputs))
  37. def gen_view():
  38. yield (self.txinfo_hdr_fs_short if terse else self.txinfo_hdr_fs).format(
  39. i = tx.txid.hl(),
  40. a = tx.send_amt.hl(),
  41. c = tx.dcoin,
  42. r = green('True') if tx.is_replaceable() else red('False'),
  43. s = green('True') if tx.signed else red('False'),
  44. l = (
  45. orange(self.strfmt_locktime(terse=True)) if tx.locktime else
  46. green('None') ))
  47. for attr,label in [('timestamp','Created:'),('sent_timestamp','Sent:')]:
  48. if (val := getattr(tx,attr)) is not None:
  49. _ = decode_timestamp(val)
  50. yield f'{label:8} {make_timestr(_)} ({format_elapsed_hr(_)})\n'
  51. if not terse:
  52. yield '\n'
  53. if tx.chain != 'mainnet': # if mainnet has a coin-specific name, display it
  54. yield green(f'Chain: {tx.chain.upper()}') + '\n'
  55. if tx.coin_txid:
  56. yield f'{tx.coin} TxID: {tx.coin_txid.hl()}\n'
  57. enl = ('\n','')[bool(terse)]
  58. yield enl
  59. if tx.comment:
  60. yield f'Comment: {tx.comment.hl()}\n{enl}'
  61. yield self.format_body(blockcount,nonmm_str,max_mmwid,enl,terse=terse,sort=sort)
  62. iwidth = len(str(int(tx.sum_inputs())))
  63. yield self.txinfo_ftr_fs.format(
  64. i = tx.sum_inputs().fmt(color=True,iwidth=iwidth),
  65. o = tx.sum_outputs().fmt(color=True,iwidth=iwidth),
  66. C = tx.change.fmt(color=True,iwidth=iwidth),
  67. s = tx.send_amt.fmt(color=True,iwidth=iwidth),
  68. a = self.format_abs_fee(color=True,iwidth=iwidth),
  69. r = self.format_rel_fee(),
  70. d = tx.dcoin,
  71. c = tx.coin )
  72. if tx.cfg.verbose:
  73. yield self.format_verbose_footer()
  74. return ''.join(gen_view()) # TX label might contain non-ascii chars
  75. def view_with_prompt(self,prompt,pause=True):
  76. prompt += ' (y)es, (N)o, pager (v)iew, (t)erse view: '
  77. from ..term import get_char
  78. while True:
  79. reply = get_char( prompt, immed_chars='YyNnVvTt' ).strip('\n\r')
  80. msg('')
  81. if reply == '' or reply in 'Nn':
  82. break
  83. if reply in 'YyVvTt':
  84. self.view(
  85. pager = reply in 'Vv',
  86. pause = pause,
  87. terse = reply in 'Tt' )
  88. break
  89. msg('Invalid reply')
  90. def view(self,pager=False,pause=True,terse=False):
  91. o = self.format(terse=terse)
  92. if pager:
  93. from ..ui import do_pager
  94. do_pager(o)
  95. else:
  96. msg_r(o)
  97. from ..term import get_char
  98. if pause:
  99. get_char('Press any key to continue: ')
  100. msg('')
  101. def init_info(tx):
  102. return getattr(
  103. importlib.import_module(f'mmgen.proto.{tx.proto.base_proto_coin.lower()}.tx.info'),
  104. ('Token' if tx.proto.tokensym else '') + 'TxInfo' )(tx)