Browse Source

mmgen-txsend --status: support transaction ranges

Example (assumes --autosign):

    # Display status of last four sent transactions:
    $ mmgen-txsend -s 0-3

Testing/demo:

    $ test/cmdtest.py -e -X alice_txstatus9 autosign_automount
The MMGen Project 1 month ago
parent
commit
48edcf412c
6 changed files with 71 additions and 35 deletions
  1. 9 6
      mmgen/autosign.py
  2. 1 1
      mmgen/data/release_date
  3. 1 1
      mmgen/data/version
  4. 30 15
      mmgen/main_txsend.py
  5. 10 2
      mmgen/tx/online.py
  6. 20 10
      test/cmdtest_d/automount.py

+ 9 - 6
mmgen/autosign.py

@@ -334,22 +334,25 @@ class Signable:
 				shred_file(self.cfg, fn, iterations=15)
 			sys.exit(0)
 
-		async def get_last_sent(self, *, idx=0):
+		async def get_last_sent(self, *, tx_range=None):
 			return await self.get_last_created(
 				# compat fallback - ‘sent_timestamp’ attr is missing in some old TX files:
 				sort_key = lambda x: x.sent_timestamp or x.timestamp,
-				idx = idx)
+				tx_range = tx_range)
 
-		async def get_last_created(self, *, sort_key=lambda x: x.timestamp, idx=0):
+		async def get_last_created(self, *, sort_key=lambda x: x.timestamp, tx_range=None):
 			from .tx import CompletedTX
 			fns = [f for f in self.dir.iterdir() if f.name.endswith(self.subext)]
 			files = sorted(
 				[await CompletedTX(cfg=self.cfg, filename=str(txfile), quiet_open=True)
 					for txfile in fns],
 				key = sort_key)
-			if not (0 <= idx < len(files)):
-				die(2, f'{idx}: invalid transaction index (must be less than {len(files)})')
-			return files[-1 - idx]
+			if files:
+				return (
+					files[-1] if tx_range is None else
+					files[len(files) - 1 - tx_range.last:len(files) - tx_range.first])
+			else:
+				die(1, 'No sent automount transactions!')
 
 	class xmr_signable: # mixin class
 		automount = True

+ 1 - 1
mmgen/data/release_date

@@ -1 +1 @@
-January 2026
+February 2026

+ 1 - 1
mmgen/data/version

@@ -1 +1 @@
-16.1.dev28
+16.1.dev29

+ 30 - 15
mmgen/main_txsend.py

@@ -23,7 +23,7 @@ mmgen-txsend: Broadcast a transaction signed by 'mmgen-txsign' to the network
 import sys
 
 from .cfg import gc, Config
-from .util import async_run, die, is_int
+from .util import msg, async_run, die
 
 opts_data = {
 	'sets': [
@@ -35,7 +35,7 @@ opts_data = {
 		'usage2': [
 			'[opts] <signed transaction file>',
 			'[opts] --autosign',
-			'[opts] --autosign (--status | --receipt) [IDX]',
+			'[opts] --autosign (--status | --receipt) [index or range]',
 		],
 		'options': """
 -h, --help       Print this help message
@@ -71,10 +71,13 @@ opts_data = {
 -y, --yes        Answer 'yes' to prompts, suppress non-essential output
 """,
 		'notes': """
-With --autosign, combined with --status or --receipt, the optional IDX arg
-represents an index into the list of sent transaction files on the removable
-device, in reverse chronological order.  ‘0’ (the default) specifies the
-last sent transaction, ‘1’ the next-to-last, and so on.
+With --autosign, combined with --status or --receipt, the optional index or
+range arg represents an index or range into the list of sent transaction files
+on the removable device, in reverse chronological order.  ‘0’ (the default)
+specifies the last sent transaction, ‘1’ the next-to-last, and so on.  Hyphen-
+separated ranges are also supported.  For example, specifying a range ‘0-3’
+would output data for the last four sent transactions, beginning with the most
+recent.
 """
 	},
 	'code': {
@@ -99,12 +102,12 @@ if cfg.dump_hex and cfg.dump_hex != '-':
 	check_outfile_dir(cfg.dump_hex)
 
 post_send_op = cfg.status or cfg.receipt
-
-asi = None
+asi, tx_range = (None, None)
 
 def init_autosign(arg):
-	global asi, si, infile, tx_idx
+	global asi, si, infile, tx_range
 	from .tx.util import mount_removable_device
+	from .tx.online import SentTXRange
 	from .autosign import Signable
 	asi = mount_removable_device(cfg)
 	si = Signable.automount_transaction(asi)
@@ -113,9 +116,11 @@ def init_autosign(arg):
 	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 not is_int(arg):
-			die(2, f'{arg}: invalid transaction index (must be a non-negative integer)')
-		tx_idx = int(arg)
+		try:
+			tx_range = SentTXRange(arg)
+		except:
+			die(2, f'{arg}: invalid transaction index arg '
+			'(must be a non-negative integer or hyphen-separated range)')
 	else:
 		infile = si.get_unsent()
 		cfg._util.qmsg(f'Got signed transaction file ‘{infile}’')
@@ -124,7 +129,7 @@ match cfg._args:
 	case [arg] if cfg.autosign and post_send_op:
 		init_autosign(arg)
 	case [] if cfg.autosign:
-		init_autosign(0)
+		init_autosign('0')
 	case [infile]:
 		from .fileutil import check_infile
 		check_infile(infile)
@@ -137,8 +142,15 @@ if not cfg.status:
 
 from .tx import OnlineSignedTX
 
+batch = tx_range and (tx_range.last != tx_range.first)
+
+def do_sep():
+	if batch:
+		msg('-' * 74)
+
 async def process_tx(tx):
 
+	do_sep()
 	cfg._util.vmsg(f'Getting {tx.desc} ‘{tx.infile}’')
 
 	if tx.is_compat:
@@ -168,11 +180,14 @@ async def process_tx(tx):
 				if not cfg.autosign:
 					tx.file.write(ask_write_default_yes=True)
 
-	return await tx.send(txcfg, asi)
+	return await tx.send(txcfg, asi, batch=batch)
 
 async def main():
 	if cfg.autosign and post_send_op:
-		return await process_tx(await si.get_last_sent(idx=tx_idx))
+		exitvals = [await process_tx(tx)
+			for tx in reversed(await si.get_last_sent(tx_range=tx_range))]
+		do_sep()
+		return max(exitvals)
 	else:
 		return await process_tx(await OnlineSignedTX(
 			cfg        = cfg,

+ 10 - 2
mmgen/tx/online.py

@@ -14,11 +14,16 @@ tx.online: online signed transaction class
 
 import time, asyncio
 
+from ..obj import MMGenRange
 from ..util import msg, Msg, gmsg, ymsg, make_timestr, die
 from ..color import pink, yellow
 
 from .signed import Signed, AutomountSigned
 
+class SentTXRange(MMGenRange):
+	min_idx = 0
+	max_idx = 1_000_000
+
 class OnlineSigned(Signed):
 
 	@property
@@ -62,7 +67,7 @@ class OnlineSigned(Signed):
 			ask_overwrite = False,
 			ask_write     = False)
 
-	async def send(self, cfg, asi):
+	async def send(self, cfg, asi, batch=False):
 		"""
 		returns integer exit val to system
 		"""
@@ -149,7 +154,10 @@ class OnlineSigned(Signed):
 
 		if status_exitval is not None:
 			if cfg.verbose:
-				self.info.view_with_prompt('View transaction details?', pause=False)
+				if batch:
+					self.info.view(pause=False, terse=True)
+				else:
+					self.info.view_with_prompt('View transaction details?', pause=False)
 			return status_exitval
 		return 0
 

+ 20 - 10
test/cmdtest_d/automount.py

@@ -82,9 +82,10 @@ class CmdTestAutosignAutomount(CmdTestAutosignThreaded, CmdTestRegtest):
 		('alice_txbump5',                    'bumping the transaction (new outputs)'),
 		('alice_txsend5',                    'sending the bumped transaction'),
 		('alice_txstatus5',                  'getting transaction status (in mempool)'),
-		('alice_txstatus6',                  'getting transaction status (idx=0, in mempool)'),
-		('alice_txstatus7',                  'getting transaction status (idx=1, replaced)'),
-		('alice_txstatus8',                  'getting transaction status (idx=3, 2 confirmations)'),
+		('alice_txstatus6',                  'getting transaction status (tx_range=0, in mempool)'),
+		('alice_txstatus7',                  'getting transaction status (tx_range=1, replaced)'),
+		('alice_txstatus8',                  'getting transaction status (tx_range=3, 2 confirmations)'),
+		('alice_txstatus9',                  'getting transaction status (tx_range=0-3)'),
 		('generate',                         'mining a block'),
 		('alice_bal2',                       'checking Alice’s balance'),
 		('wait_loop_kill',                   'stopping autosign wait loop'),
@@ -229,8 +230,9 @@ class CmdTestAutosignAutomount(CmdTestAutosignThreaded, CmdTestRegtest):
 			expect,
 			exit_val = None,
 			need_rbf = False,
-			idx      = None,
-			verbose  = True):
+			tx_range = None,
+			verbose  = True,
+			batch    = False):
 
 		if need_rbf and not self.proto.cap('rbf'):
 			return 'skip'
@@ -240,11 +242,11 @@ class CmdTestAutosignAutomount(CmdTestAutosignThreaded, CmdTestRegtest):
 				'mmgen-txsend',
 				['--alice', '--autosign', '--status']
 				+ (['--verbose'] if verbose else [])
-				+ ([] if idx is None else [str(idx)]),
+				+ ([] if tx_range is None else [tx_range]),
 				no_passthru_opts = ['coin'],
 				exit_val = exit_val)
 		t.expect(expect, regex=True)
-		if not exit_val:
+		if not (exit_val or batch):
 			t.expect('view: ', 'n')
 		t.read()
 		self.remove_device_online()
@@ -267,13 +269,21 @@ class CmdTestAutosignAutomount(CmdTestAutosignThreaded, CmdTestRegtest):
 		return self._alice_txstatus('in mempool', need_rbf=True)
 
 	def alice_txstatus6(self):
-		return self._alice_txstatus('in mempool', need_rbf=True, idx=0)
+		return self._alice_txstatus('in mempool', need_rbf=True, tx_range='0')
 
 	def alice_txstatus7(self):
-		return self._alice_txstatus('replaced', need_rbf=True, idx=1)
+		return self._alice_txstatus('replaced', need_rbf=True, tx_range='1')
 
 	def alice_txstatus8(self):
-		return self._alice_txstatus('2 confirmations', need_rbf=True, idx=3)
+		return self._alice_txstatus('2 confirmations', need_rbf=True, tx_range='3')
+
+	def alice_txstatus9(self):
+		return self._alice_txstatus(
+			'in mempool.*replaced.*replaced.*2 confirmations',
+			need_rbf = True,
+			tx_range = '0-3',
+			verbose = False,
+			batch = True)
 
 	def alice_txsend_bad_no_unsent(self):
 		self.insert_device_online()