From 1f166ce458ed7bc05a46df14d5ab1e4a3f5f4b40 Mon Sep 17 00:00:00 2001 From: The MMGen Project Date: Sat, 15 Mar 2025 18:24:53 +0000 Subject: [PATCH] mmgen-txsend: add --test option MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Test whether a transaction is sendable without sending it - for BTC and friends, uses the ‘testmempoolaccept’ RPC call - not implemented for ETH --- mmgen/main_txsend.py | 6 ++++++ mmgen/proto/btc/tx/online.py | 25 +++++++++++++++++++++++-- mmgen/proto/eth/tx/online.py | 3 +++ test/cmdtest_d/ct_regtest.py | 7 +++++++ test/cmdtest_d/ct_shared.py | 10 +++++++--- 5 files changed, 46 insertions(+), 5 deletions(-) diff --git a/mmgen/main_txsend.py b/mmgen/main_txsend.py index cf35fe33..b7cb3711 100755 --- a/mmgen/main_txsend.py +++ b/mmgen/main_txsend.py @@ -52,6 +52,7 @@ opts_data = { -q, --quiet Suppress warnings; overwrite files without prompting -s, --status Get status of a sent transaction (or current transaction, whether sent or unsent, when used with --autosign) +-t, --test Test whether the transaction can be sent without sending it -v, --verbose Be more verbose -y, --yes Answer 'yes' to prompts, suppress non-essential output """ @@ -66,6 +67,9 @@ if cfg.autosign and cfg.outdir: if cfg.mark_sent and not cfg.autosign: die(1, '--mark-sent is used only in combination with --autosign') +if cfg.test and cfg.dump_hex: + die(1, '--test cannot be used in combination with --dump-hex') + if cfg.dump_hex and cfg.dump_hex != '-': from .fileutil import check_outfile_dir check_outfile_dir(cfg.dump_hex) @@ -158,6 +162,8 @@ async def main(): await post_send(tx) else: await post_send(tx) + elif cfg.test: + await tx.test_sendable() elif await tx.send(): await post_send(tx) diff --git a/mmgen/proto/btc/tx/online.py b/mmgen/proto/btc/tx/online.py index 4e551fd8..5c5596b6 100755 --- a/mmgen/proto/btc/tx/online.py +++ b/mmgen/proto/btc/tx/online.py @@ -13,13 +13,13 @@ proto.btc.tx.online: Bitcoin online signed transaction class """ from ....tx import online as TxBase -from ....util import msg, die +from ....util import msg, ymsg, die from ....color import orange from .signed import Signed class OnlineSigned(Signed, TxBase.OnlineSigned): - async def send(self, *, prompt_user=True): + async def send_checks(self): self.check_correct_chain() @@ -37,6 +37,27 @@ class OnlineSigned(Signed, TxBase.OnlineSigned): await self.status.display() + async def test_sendable(self): + + await self.send_checks() + + res = await self.rpc.call('testmempoolaccept', (self.serialized,)) + ret = res[0] + + if ret['allowed']: + from ....obj import CoinTxID + msg('TxID: {}'.format(CoinTxID(ret['txid']).hl())) + msg('Transaction can be sent') + return True + else: + ymsg('Transaction cannot be sent') + msg(ret['reject-reason']) + return False + + async def send(self, *, prompt_user=True): + + await self.send_checks() + if prompt_user: self.confirm_send() diff --git a/mmgen/proto/eth/tx/online.py b/mmgen/proto/eth/tx/online.py index f8c7e94d..03fb8369 100755 --- a/mmgen/proto/eth/tx/online.py +++ b/mmgen/proto/eth/tx/online.py @@ -20,6 +20,9 @@ from .signed import Signed, TokenSigned class OnlineSigned(Signed, TxBase.OnlineSigned): + async def test_sendable(self): + raise NotImplementedError('transaction testing not implemented for Ethereum') + async def send(self, *, prompt_user=True): self.check_correct_chain() diff --git a/test/cmdtest_d/ct_regtest.py b/test/cmdtest_d/ct_regtest.py index 9187945b..0ca0dec8 100755 --- a/test/cmdtest_d/ct_regtest.py +++ b/test/cmdtest_d/ct_regtest.py @@ -465,6 +465,7 @@ class CmdTestRegtest(CmdTestBase, CmdTestShared): ('bob_dump_hex_sign', 'dump_hex transaction - signing'), ('bob_dump_hex_dump_stdout', 'dump_hex transaction - dumping tx hex to stdout'), ('bob_dump_hex_dump', 'dump_hex transaction - dumping tx hex to file'), + ('bob_dump_hex_test', 'dump_hex transaction - test whether TX can be sent'), ('bob_dump_hex_send_cli', 'dump_hex transaction - sending via cli'), ('generate', 'mining a block'), ('bob_bal7', 'Bob’s balance'), @@ -2241,6 +2242,12 @@ class CmdTestRegtest(CmdTestBase, CmdTestShared): assert is_hex_str(txid) and len(txid) == 64 return t + def bob_dump_hex_test(self): + txfile = get_file_with_ext(self.dump_hex_subdir, 'sigtx') + t = self.spawn('mmgen-txsend', ['--bob', '--test', txfile]) + self.txsend_ui_common(t, bogus_send=False, test=True) + return t + def bob_dump_hex_send_cli(self): return self._user_dump_hex_send_cli('bob', subdir='nochg_tx') diff --git a/test/cmdtest_d/ct_shared.py b/test/cmdtest_d/ct_shared.py index 3d52a03f..976227e6 100755 --- a/test/cmdtest_d/ct_shared.py +++ b/test/cmdtest_d/ct_shared.py @@ -162,6 +162,7 @@ class CmdTestShared: file_desc = 'Sent transaction', confirm_send = True, bogus_send = True, + test = False, quiet = False, has_label = False): @@ -172,16 +173,19 @@ class CmdTestShared: t.view_tx(view) t.do_comment(add_comment, has_label=has_label) - self._do_confirm_send(t, quiet=quiet, confirm_send=confirm_send) + if not test: + self._do_confirm_send(t, quiet=quiet, confirm_send=confirm_send) if bogus_send: txid = '' t.expect('BOGUS transaction NOT sent') else: - txid = strip_ansi_escapes(t.expect_getend('Transaction sent: ')) + m = 'TxID: ' if test else 'Transaction sent: ' + txid = strip_ansi_escapes(t.expect_getend(m)) assert len(txid) == 64, f'{txid!r}: Incorrect txid length!' - t.written_to_file(file_desc) + if not test: + t.written_to_file(file_desc) return txid