From 392b3700affc433ac5feb1671b16207651e92fb5 Mon Sep 17 00:00:00 2001 From: The MMGen Project Date: Sun, 1 Feb 2026 09:11:08 +0000 Subject: [PATCH] mmgen-txsend: new `process_tx()` function: tx.online: cleanups --- mmgen/main_txbump.py | 7 ++-- mmgen/main_txdo.py | 6 ++-- mmgen/main_txsend.py | 60 ++++++++++++++++----------------- mmgen/proto/xmr/tx/completed.py | 1 + mmgen/proto/xmr/tx/online.py | 24 ++++++++----- mmgen/tx/online.py | 12 ++++--- test/cmdtest_d/automount.py | 13 +++++-- test/cmdtest_d/xmr_autosign.py | 2 +- 8 files changed, 72 insertions(+), 53 deletions(-) diff --git a/mmgen/main_txbump.py b/mmgen/main_txbump.py index 9c6ae37a..a1ca49b1 100755 --- a/mmgen/main_txbump.py +++ b/mmgen/main_txbump.py @@ -17,10 +17,11 @@ # along with this program. If not, see . """ -mmgen-txbump: Create, and optionally send and sign, a replacement transaction - on supporting networks +mmgen-txbump: Create, and optionally send and sign, a replacement transaction on supported networks """ +import sys + from .cfg import gc, Config from .util import msg, msg_r, die, async_run from .color import green @@ -207,7 +208,7 @@ async def main(): TxKeys(cfg, tx2, seedfiles=seedfiles, keylist=kl, keyaddrlist=kal).keys): tx4 = await OnlineSignedTX(cfg=cfg, data=tx3.__dict__) tx4.file.write(ask_write=False) - await tx4.send(cfg, asi if cfg.autosign else None) + sys.exit(await tx4.send(cfg, asi if cfg.autosign else None)) else: die(2, 'Transaction could not be signed') else: diff --git a/mmgen/main_txdo.py b/mmgen/main_txdo.py index 8cad5ee4..16a312e7 100755 --- a/mmgen/main_txdo.py +++ b/mmgen/main_txdo.py @@ -20,6 +20,8 @@ mmgen-txdo: Create, sign and send an online MMGen transaction """ +import sys + from .cfg import gc, Config from .util import die, fmt_list, async_run from .subseed import SubSeedIdxRange @@ -192,8 +194,8 @@ async def main(): if tx3: tx3.file.write(ask_write=False) tx4 = await SentTX(cfg=cfg, data=tx3.__dict__) - await tx4.send(cfg, asi=None) + return await tx4.send(cfg, asi=None) else: die(2, 'Transaction could not be signed') -async_run(cfg, main) +sys.exit(async_run(cfg, main)) diff --git a/mmgen/main_txsend.py b/mmgen/main_txsend.py index e33f3036..2f91f034 100755 --- a/mmgen/main_txsend.py +++ b/mmgen/main_txsend.py @@ -110,11 +110,9 @@ def init_autosign(arg): si = Signable.automount_transaction(asi) if cfg.abort: si.shred_abortable() # prompts user, then raises exception or exits + elif post_send_op and (si.unsent or si.unsigned): + die(1, 'Transaction is {}'.format('unsent' if si.unsent else 'unsigned')) elif post_send_op: - if si.unsent: - die(1, 'Transaction is unsent') - if si.unsigned: - die(1, 'Transaction is unsigned') if not is_int(arg): die(2, f'{arg}: invalid transaction index (must be a non-negative integer)') tx_idx = int(arg) @@ -139,38 +137,28 @@ if not cfg.status: from .tx import OnlineSignedTX -async def main(): +async def process_tx(tx): - global cfg - - if cfg.autosign and post_send_op: - tx = await si.get_last_sent(idx=tx_idx) - else: - tx = await OnlineSignedTX( - cfg = cfg, - filename = infile, - automount = cfg.autosign, - quiet_open = True) + cfg._util.vmsg(f'Getting {tx.desc} ‘{tx.infile}’') if tx.is_compat: return await tx.compat_send() - cfg = Config({'_clone': cfg, 'proto': tx.proto, 'coin': tx.proto.coin}) - - if cfg.tx_proxy: - from .tx.tx_proxy import check_client - check_client(cfg) - - from .rpc import rpc_init - tx.rpc = await rpc_init(cfg) - - cfg._util.vmsg(f'Getting {tx.desc} ‘{tx.infile}’') - - if cfg.mark_sent: - await tx.post_send(asi) - sys.exit(0) + txcfg = Config({'_clone': cfg, 'proto': tx.proto, 'coin': tx.proto.coin}) if not post_send_op: + if cfg.tx_proxy: + from .tx.tx_proxy import check_client + check_client(txcfg) + + from .rpc import rpc_init + tx.rpc = await rpc_init(txcfg) + + if not post_send_op: + if cfg.mark_sent: + 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') @@ -180,6 +168,16 @@ async def main(): if not cfg.autosign: tx.file.write(ask_write_default_yes=True) - await tx.send(cfg, asi) + return await tx.send(txcfg, asi) -async_run(cfg, main) +async def main(): + if cfg.autosign and post_send_op: + return await process_tx(await si.get_last_sent(idx=tx_idx)) + else: + return await process_tx(await OnlineSignedTX( + cfg = cfg, + filename = infile, + automount = cfg.autosign, + quiet_open = True)) + +sys.exit(async_run(cfg, main)) diff --git a/mmgen/proto/xmr/tx/completed.py b/mmgen/proto/xmr/tx/completed.py index ddcbefb4..c2603aa1 100755 --- a/mmgen/proto/xmr/tx/completed.py +++ b/mmgen/proto/xmr/tx/completed.py @@ -26,6 +26,7 @@ class Completed(Base): 'network': proto.network}) self.proto = proto self.filename = filename + self.infile = filename @cached_property def compat_tx(self): diff --git a/mmgen/proto/xmr/tx/online.py b/mmgen/proto/xmr/tx/online.py index a76b1155..b9a1380f 100755 --- a/mmgen/proto/xmr/tx/online.py +++ b/mmgen/proto/xmr/tx/online.py @@ -15,17 +15,20 @@ proto.xmr.tx.online: Monero online signed transaction class from .completed import Completed class OnlineSigned(Completed): + desc = 'signed transaction' async def compat_send(self): + """ + returns integer exit val to system + """ from ....xmrwallet import op as xmrwallet_op op_name = 'daemon' if self.cfg.status else 'submit' op = xmrwallet_op(op_name, self.cfg, self.filename, None, compat_call=True) if self.cfg.status: - from ....util import msg, ymsg, suf + from ....util import msg, msg_r, ymsg, suf + ret = 0 txid = self.compat_tx.data.txid - if self.cfg.verbose: - msg(self.compat_tx.get_info()) - elif not self.cfg.quiet: + if not (self.cfg.verbose or self.cfg.quiet): from ....obj import CoinTxID msg('{} TxID: {}'.format(self.cfg.coin, CoinTxID(txid).hl())) res = op.dc.call_raw('get_transactions', txs_hashes=[txid]) @@ -38,16 +41,19 @@ class OnlineSigned(Completed): msg('Transaction has {} confirmation{}'.format(confs, suf(confs))) else: ymsg('An RPC error occurred while fetching transaction data') - return False + ret = 1 + if self.cfg.verbose: + msg_r('\n' + self.compat_tx.get_info()) + return ret else: await op.restart_wallet_daemon() - return await op.main() + return int(not await op.main()) class Sent(OnlineSigned): - pass + desc = 'sent transaction' class AutomountOnlineSigned(OnlineSigned): - pass + desc = 'signed automount transaction' class AutomountSent(AutomountOnlineSigned): - pass + desc = 'sent automount transaction' diff --git a/mmgen/tx/online.py b/mmgen/tx/online.py index bb3b3c55..b3098fc3 100755 --- a/mmgen/tx/online.py +++ b/mmgen/tx/online.py @@ -12,7 +12,7 @@ tx.online: online signed transaction class """ -import sys, time, asyncio +import time, asyncio from ..util import msg, Msg, gmsg, ymsg, make_timestr, die from ..color import pink, yellow @@ -63,7 +63,9 @@ class OnlineSigned(Signed): ask_write = False) async def send(self, cfg, asi): - + """ + returns integer exit val to system + """ status_exitval = None sent_status = None all_ok = True @@ -85,9 +87,10 @@ class OnlineSigned(Signed): if coin_txid := getattr(self, f'coin_txid{idx}', None): txhex = getattr(self, f'serialized{idx}') if cfg.status: - cfg._util.qmsg(f'{self.proto.coin} TxID: {coin_txid.hl()}') if cfg.verbose: await self.post_network_send(coin_txid) + else: + cfg._util.qmsg(f'{self.proto.coin} TxID: {coin_txid.hl()}') status_exitval = await self.status.display(idx=idx) elif cfg.receipt: if res := await self.get_receipt(coin_txid, receipt_only=True): @@ -147,7 +150,8 @@ class OnlineSigned(Signed): if status_exitval is not None: if cfg.verbose: self.info.view_with_prompt('View transaction details?', pause=False) - sys.exit(status_exitval) + return status_exitval + return 0 class AutomountOnlineSigned(AutomountSigned, OnlineSigned): pass diff --git a/test/cmdtest_d/automount.py b/test/cmdtest_d/automount.py index f84c48b1..a73f33e2 100755 --- a/test/cmdtest_d/automount.py +++ b/test/cmdtest_d/automount.py @@ -224,7 +224,13 @@ class CmdTestAutosignAutomount(CmdTestAutosignThreaded, CmdTestRegtest): def alice_txsend5(self): return self._user_txsend('alice', need_rbf=True) - def _alice_txstatus(self, expect, exit_val=None, need_rbf=False, idx=None): + def _alice_txstatus( + self, + expect, + exit_val = None, + need_rbf = False, + idx = None, + verbose = True): if need_rbf and not self.proto.cap('rbf'): return 'skip' @@ -232,11 +238,12 @@ class CmdTestAutosignAutomount(CmdTestAutosignThreaded, CmdTestRegtest): self.insert_device_online() t = self.spawn( 'mmgen-txsend', - ['--alice', '--autosign', '--status', '--verbose'] + ['--alice', '--autosign', '--status'] + + (['--verbose'] if verbose else []) + ([] if idx is None else [str(idx)]), no_passthru_opts = ['coin'], exit_val = exit_val) - t.expect(expect) + t.expect(expect, regex=True) if not exit_val: t.expect('view: ', 'n') t.read() diff --git a/test/cmdtest_d/xmr_autosign.py b/test/cmdtest_d/xmr_autosign.py index 1a772770..3d1fed45 100755 --- a/test/cmdtest_d/xmr_autosign.py +++ b/test/cmdtest_d/xmr_autosign.py @@ -816,7 +816,7 @@ class CmdTestXMRCompat(CmdTestXMRAutosign): return self._alice_txstatus(['--quiet'], expect_str='confirmations') def alice_txstatus3(self): - return self._alice_txstatus(['--verbose'], expect_str='Info for transaction .* confirmations') + return self._alice_txstatus(['--verbose'], expect_str='confirmations.*Info for transaction') def _alice_txstatus(self, add_opts=[], expect_str=None): return self._alice_txops(