From 99b9081640a06d21cbee2b18475b92934086c880 Mon Sep 17 00:00:00 2001 From: The MMGen Project Date: Wed, 7 Oct 2026 16:40:39 +0000 Subject: [PATCH] mmgen-swaptxcreate: check network before creating and sending TX --- mmgen/main_txsend.py | 6 ++-- mmgen/swap/proto/thorchain/__init__.py | 1 + mmgen/tx/base.py | 14 ++++++++ mmgen/tx/new_swap.py | 2 ++ test/cmdtest_d/httpd/thornode/rpc.py | 6 ++++ test/cmdtest_d/swap.py | 44 ++++++++++++++++++++++++-- 6 files changed, 69 insertions(+), 4 deletions(-) diff --git a/mmgen/main_txsend.py b/mmgen/main_txsend.py index 81f6cd88..b6733d7d 100644 --- a/mmgen/main_txsend.py +++ b/mmgen/main_txsend.py @@ -175,8 +175,10 @@ async def process_tx(tx): await tx.post_send(asi) return 0 - if tx.is_swap and not tx.check_swap_expiry(): - die(1, 'Swap quote has expired. Please re-create the transaction') + if tx.is_swap: + await tx.check_swap_network() + if not tx.check_swap_expiry(): + die(1, 'Swap quote has expired. Please re-create the transaction') if not cfg.yes: tx.info.view_with_prompt('View transaction details?') diff --git a/mmgen/swap/proto/thorchain/__init__.py b/mmgen/swap/proto/thorchain/__init__.py index 76f4c94a..14b0fea6 100644 --- a/mmgen/swap/proto/thorchain/__init__.py +++ b/mmgen/swap/proto/thorchain/__init__.py @@ -13,6 +13,7 @@ swap.proto.thorchain: THORChain swap protocol implementation for the MMGen Walle __all__ = ['SwapCfg', 'SwapAsset', 'Memo'] name = 'THORChain' +coin = 'RUNE' exp_prec = 4 from ....util2 import ExpInt diff --git a/mmgen/tx/base.py b/mmgen/tx/base.py index f0b64564..377db79b 100644 --- a/mmgen/tx/base.py +++ b/mmgen/tx/base.py @@ -243,6 +243,20 @@ class Base(MMGenObject): x = '[unknown]' return SwapAsset._ad(x, x, x, x, x) + async def check_swap_network(self): + from ..protocol import init_proto + from ..rpc import rpc_init + proto = init_proto(self.cfg, self.swap_proto_mod.coin, network=self.cfg.network) + rpc = await rpc_init(self.cfg, proto) + data = rpc.inbound_addrs() + for asset in (self.send_asset, self.recv_asset): + for key, state in ( + ('global_trading_paused', 'paused'), + ('chain_trading_paused', f'paused for asset {asset.name}'), + ('halted', f'halted for asset {asset.name}')): + if rpc.data_for_coin(data, asset.coin)[key]: + die(2, f'Trading is {state} on the {self.swap_proto_mod.name} network') + # token methods: @property def token_op(self): diff --git a/mmgen/tx/new_swap.py b/mmgen/tx/new_swap.py index cd411e72..313e03fe 100644 --- a/mmgen/tx/new_swap.py +++ b/mmgen/tx/new_swap.py @@ -121,6 +121,8 @@ class NewSwap(New): await parse() + await self.check_swap_network() + for asset in (self.send_asset, self.recv_asset): if asset.name not in sa.tested: from ..util import msg, ymsg diff --git a/test/cmdtest_d/httpd/thornode/rpc.py b/test/cmdtest_d/httpd/thornode/rpc.py index b108e0e7..e05fa157 100644 --- a/test/cmdtest_d/httpd/thornode/rpc.py +++ b/test/cmdtest_d/httpd/thornode/rpc.py @@ -51,6 +51,12 @@ class ThornodeRPCServer(ThornodeServer): inbounds_info = get_inbounds_info() + chain_var_actions = { + 'ltc_global_paused': ('inbounds_info', 'LTC', 'global_trading_paused'), + 'ltc_paused': ('inbounds_info', 'LTC', 'chain_trading_paused'), + 'ltc_halted': ('inbounds_info', 'LTC', 'halted'), + 'bch_halted': ('inbounds_info', 'BCH', 'halted')} + def make_response_body(self, method, environ): class responses: diff --git a/test/cmdtest_d/swap.py b/test/cmdtest_d/swap.py index 97182fd8..f1893788 100644 --- a/test/cmdtest_d/swap.py +++ b/test/cmdtest_d/swap.py @@ -124,8 +124,8 @@ class CmdTestSwapMethods: exit_val = exit_val, no_passthru_opts = ['coin']) - def _swaptxcreate_bad(self, args, *, exit_val=1, expect1=None, expect2=None): - t = self._swaptxcreate(args, exit_val=exit_val) + def _swaptxcreate_bad(self, args, *, add_opts=[], exit_val=1, expect1=None, expect2=None): + t = self._swaptxcreate(args, add_opts=add_opts, exit_val=exit_val) if expect1: t.expect(expect1) if expect2: @@ -297,6 +297,7 @@ class CmdTestSwap( ('subgroup.init_swap', []), ('subgroup.create', ['init_swap']), ('subgroup.create_bad', ['init_swap']), + ('subgroup.create_neterror', ['init_swap']), ('subgroup.signsend', ['init_swap']), ('subgroup.signsend_bad', ['init_swap']), ('subgroup.autosign', ['signsend']), @@ -345,6 +346,12 @@ class CmdTestSwap( ('swaptxcreate_bad8', 'creating a swap transaction (bad, non-MMGen change address)'), ('swaptxcreate_bad9', 'creating a swap transaction (bad, invalid addrtype)'), ), + 'create_neterror': ( + 'Swap TX create operations: network error handling', + ('swaptxcreate_neterror1', 'creating a swap transaction (network error, trading paused)'), + ('swaptxcreate_neterror2', 'creating a swap transaction (network error, trading for coin paused)'), + ('swaptxcreate_neterror3', 'creating a swap transaction (network error, trading for coin halted)'), + ), 'signsend': ( 'Swap TX create, sign and send operations (LTC => BCH)', ('swaptxsign1_create', 'creating a swap transaction (full args)'), @@ -376,6 +383,8 @@ class CmdTestSwap( ('swaptxsign_bad1', 'signing the transaction (non-wallet swap address)'), ('swaptxsign_bad2_create', 'creating a swap transaction'), ('swaptxsign_bad2', 'signing the transaction'), + ('swaptxsend_neterror1', 'sending the transaction (LTC swaps halted)'), + ('swaptxsend_neterror2', 'sending the transaction (BCH swaps halted)'), ('swaptxsend_bad2', 'sending the transaction (swap quote expired)'), ), 'autosign': ( @@ -567,6 +576,23 @@ class CmdTestSwap( def swaptxcreate_bad9(self): return self._swaptxcreate_bad(['BCH', '1.234', 'S', 'LTC', 'B'], exit_val=2, expect1='invalid command-') + def swaptxcreate_neterror1(self): + return self._swaptxcreate_neterror('ltc_global_paused', 'Trading is paused on the') + + def swaptxcreate_neterror2(self): + return self._swaptxcreate_neterror('ltc_paused', 'Trading is paused for asset') + + def swaptxcreate_neterror3(self): + return self._swaptxcreate_neterror('ltc_halted', 'Trading is halted for asset') + + def _swaptxcreate_neterror(self, key, expect_str, *, add_opts=[]): + if key: + self.rpc_server.setvar(key, True) + ret = self._swaptxcreate_bad(['BCH', 'LTC'], add_opts=add_opts, exit_val=2, expect1=expect_str) + if key: + self.rpc_server.setvar(key, False) + return ret + def swaptxsign1_create(self): return self._swaptxcreate_ui_common( self._swaptxcreate(['LTC', '4.321', f'{self.sid}:S:2', 'BCH', f'{self.sid}:C:2'])) @@ -642,6 +668,20 @@ class CmdTestSwap( def swaptxsign_bad2(self): return self._swaptxsign() + def swaptxsend_neterror1(self): + return self._swaptxsend_bad_neterror('ltc_halted') + + def swaptxsend_neterror2(self): + return self._swaptxsend_bad_neterror('bch_halted') + + def _swaptxsend_bad_neterror(self, key): + fn = self.get_file_with_ext('sigtx') + self.rpc_server.setvar(key, True) + t = self.spawn('mmgen-txsend', ['-d', self.tmpdir, '--bob', fn], exit_val=2) + t.expect('halted') + self.rpc_server.setvar(key, False) + return t + def swaptxsend_bad2(self): fn = self.get_file_with_ext('sigtx') with open(fn) as fh: