completed.py 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105
  1. #!/usr/bin/env python3
  2. #
  3. # MMGen Wallet, a terminal-based cryptocurrency wallet
  4. # Copyright (C)2013-2026 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.completed: completed transaction class
  12. """
  13. from .base import Base
  14. class Completed(Base):
  15. """
  16. signed or unsigned transaction with associated file
  17. """
  18. filename_api = True
  19. def __init__(self, cfg, *args, filename=None, data=None, quiet_open=False, **kwargs):
  20. assert (filename or data) and not (filename and data), 'CompletedTX_chk1'
  21. super().__init__(cfg=cfg, *args, **kwargs)
  22. if data:
  23. self.__dict__ = data | {'twctl': self.twctl}
  24. self.name = type(self).__name__
  25. else:
  26. from .file import MMGenTxFile
  27. try:
  28. MMGenTxFile(self).parse(str(filename), quiet_open=quiet_open)
  29. self.check_serialized_integrity()
  30. except Exception as e:
  31. from ..color import orange
  32. from ..util import msg
  33. msg(orange(
  34. f'Something is wrong with transaction file ‘{filename}’\n'
  35. 'To fix this problem, please move or delete the file'))
  36. raise e
  37. # repeat with sign and send, because coin daemon could be restarted
  38. self.check_correct_chain()
  39. if self.check_sigs() != self.signed:
  40. from ..util import die
  41. die(1, 'Transaction is {}signed!'.format('not ' if self.signed else ''))
  42. self.infile = filename
  43. @property
  44. def file(self):
  45. from .file import MMGenTxFile
  46. return MMGenTxFile(self)
  47. @staticmethod
  48. def ext_to_cls(ext, proto):
  49. """
  50. see twctl:import_token()
  51. """
  52. from .unsigned import Unsigned, AutomountUnsigned
  53. from .online import Sent, AutomountSent
  54. for cls in (Unsigned, AutomountUnsigned, Sent, AutomountSent):
  55. if ext == getattr(cls, 'ext'):
  56. return cls
  57. if proto.tokensym:
  58. from .online import OnlineSigned as Signed
  59. from .online import AutomountOnlineSigned as AutomountSigned
  60. else:
  61. from .signed import Signed, AutomountSigned
  62. for cls in (Signed, AutomountSigned):
  63. if ext == getattr(cls, 'ext'):
  64. return cls
  65. def check_swap_memo(self):
  66. if memo_bytes := self.get_swap_memo_maybe():
  67. from ..swap.proto.thorchain import Memo
  68. if Memo.is_partial_memo(memo_bytes):
  69. from ..protocol import init_proto
  70. text = memo_bytes.decode('ascii')
  71. p = Memo.parse(text)
  72. r = self.recv_asset
  73. assert p.function == 'SWAP', f'‘{p.function}’: unsupported function in swap memo ‘{text}’'
  74. assert p.asset.name == r.name, f'invalid memo: {p.asset.name} != {r.name}'
  75. proto = init_proto(
  76. self.cfg,
  77. r.coin,
  78. network = self.cfg.network,
  79. tokensym = r.tokensym,
  80. need_amt = True)
  81. if mmid := getattr(self, 'swap_recv_addr_mmid', None):
  82. pass
  83. elif self.cfg.allow_non_wallet_swap:
  84. from ..util import ymsg
  85. ymsg('Warning: allowing swap to non-wallet address (--allow-non-wallet-swap)')
  86. else:
  87. raise ValueError('Swap to non-wallet address forbidden (override with --allow-non-wallet-swap)')
  88. return self.Output(proto, addr=p.address, mmid=mmid, amt=proto.coin_amt('0'))
  89. if self.is_swap:
  90. raise ValueError('missing or invalid memo in swap transaction')