addrs.py 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108
  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. twaddrs: Tracking wallet listaddresses class for the MMGen suite
  20. """
  21. from ..color import green
  22. from ..util import msg,die,base_proto_subclass
  23. from ..base_obj import AsyncInit
  24. from ..obj import MMGenDict,TwComment
  25. from ..addr import CoinAddr,MMGenID
  26. from .common import TwCommon
  27. class TwAddrList(MMGenDict,TwCommon,metaclass=AsyncInit):
  28. def __new__(cls,proto,*args,**kwargs):
  29. return MMGenDict.__new__(base_proto_subclass(cls,proto,'tw','addrs'),*args,**kwargs)
  30. def raw_list(self):
  31. return [((k if k.type == 'mmgen' else 'Non-MMGen'),self[k]['addr'],self[k]['amt']) for k in self]
  32. def coinaddr_list(self):
  33. return [self[k]['addr'] for k in self]
  34. async def format(self,showbtcaddrs,sort,show_age,age_fmt):
  35. if not self.has_age:
  36. show_age = False
  37. if age_fmt not in self.age_fmts:
  38. die( 'BadAgeFormat', f'{age_fmt!r}: invalid age format (must be one of {self.age_fmts!r})' )
  39. fs = '{mid}' + ('',' {addr}')[showbtcaddrs] + ' {cmt} {amt}' + ('',' {age}')[show_age]
  40. mmaddrs = [k for k in self.keys() if k.type == 'mmgen']
  41. max_mmid_len = max(len(k) for k in mmaddrs) + 2 if mmaddrs else 10
  42. max_cmt_width = max(max(v['lbl'].comment.screen_width for v in self.values()),7)
  43. addr_width = max(len(self[mmid]['addr']) for mmid in self)
  44. max_fp_len = max([len(a.split('.')[1]) for a in [str(v['amt']) for v in self.values()] if '.' in a] or [1])
  45. def sort_algo(j):
  46. if sort and 'age' in sort:
  47. return '{}_{:>012}_{}'.format(
  48. j.obj.rsplit(':',1)[0],
  49. # Hack, but OK for the foreseeable future:
  50. (1000000000-(j.confs or 0) if hasattr(j,'confs') else 0),
  51. j.sort_key)
  52. else:
  53. return j.sort_key
  54. mmids = sorted(self,key=sort_algo,reverse=bool(sort and 'reverse' in sort))
  55. if show_age:
  56. await self.set_dates(
  57. self.rpc,
  58. [o for o in mmids if hasattr(o,'confs')] )
  59. def gen_output():
  60. if self.proto.chain_name != 'mainnet':
  61. yield 'Chain: '+green(self.proto.chain_name.upper())
  62. yield fs.format(
  63. mid=MMGenID.fmtc('MMGenID',width=max_mmid_len),
  64. addr=(CoinAddr.fmtc('ADDRESS',width=addr_width) if showbtcaddrs else None),
  65. cmt=TwComment.fmtc('COMMENT',width=max_cmt_width+1),
  66. amt='BALANCE'.ljust(max_fp_len+4),
  67. age=age_fmt.upper(),
  68. ).rstrip()
  69. al_id_save = None
  70. for mmid in mmids:
  71. if mmid.type == 'mmgen':
  72. if al_id_save and al_id_save != mmid.obj.al_id:
  73. yield ''
  74. al_id_save = mmid.obj.al_id
  75. mmid_disp = mmid
  76. else:
  77. if al_id_save:
  78. yield ''
  79. al_id_save = None
  80. mmid_disp = 'Non-MMGen'
  81. e = self[mmid]
  82. yield fs.format(
  83. mid=MMGenID.fmtc(mmid_disp,width=max_mmid_len,color=True),
  84. addr=(e['addr'].fmt(color=True,width=addr_width) if showbtcaddrs else None),
  85. cmt=e['lbl'].comment.fmt(width=max_cmt_width,color=True,nullrepl='-'),
  86. amt=e['amt'].fmt('4.{}'.format(max(max_fp_len,3)),color=True),
  87. age=self.age_disp(mmid,age_fmt) if show_age and hasattr(mmid,'confs') else '-'
  88. ).rstrip()
  89. yield '\nTOTAL: {} {}'.format(
  90. self.total.hl(color=True),
  91. self.proto.dcoin )
  92. return '\n'.join(gen_output())