Browse Source

mmgen-xmrwallet: new `txlist` operation

Testing:

    $ test/test.py -e xmrwallet_txlist
The MMGen Project 1 year ago
parent
commit
a1986fe6

+ 1 - 1
mmgen/data/version

@@ -1 +1 @@
-13.3.dev49
+13.3.dev50

+ 2 - 1
mmgen/help/xmrwallet.py

@@ -48,6 +48,7 @@ relay     - relay a transaction from a transaction file created using ‘sweep
             or ‘transfer’ with the --no-relay option
 submit    - submit an autosigned transaction to a wallet and the network
 txview    - display detailed information about a transaction file or files
+txlist    - same as above, but display terse information in tabular format
 dump      - produce JSON dumps of wallet metadata (accounts, addresses and
             labels) for a list or range of wallets
 restore   - same as ‘create’, but additionally restore wallet metadata from
@@ -134,7 +135,7 @@ wallets, creating the dumps is as easy as executing ‘mmgen-xmrwallet
 JSON and thus suitable for efficient incremental backup using git.
 
 
-                          ‘TXVIEW’ OPERATION NOTES
+                    ‘TXVIEW’ AND ‘TXLIST’ OPERATION NOTES
 
 Transactions are displayed in chronological order based on submit time or
 creation time.  With --autosign, submitted transactions on the removable

+ 3 - 3
mmgen/main_xmrwallet.py

@@ -49,7 +49,7 @@ opts_data = {
 			'[opts] sweep    [xmr_keyaddrfile] SWEEP_SPEC',
 			'[opts] submit   [TX_file]',
 			'[opts] relay    <TX_file>',
-			'[opts] txview   [TX_file] ...',
+			'[opts] txview | txlist [TX_file] ...',
 			'[opts] export-outputs    [wallets]',
 			'[opts] import-key-images [wallets]',
 		],
@@ -113,7 +113,7 @@ cmd_args = cfg._args
 if cmd_args and cfg.autosign and (
 		cmd_args[0] in (
 			MoneroWalletOps.kafile_arg_ops
-			+ ('export-outputs','import-key-images','txview')
+			+ ('export-outputs','import-key-images','txview','txlist')
 		)
 		or len(cmd_args) == 1 and cmd_args[0] == 'submit'
 	):
@@ -132,7 +132,7 @@ if op.replace('-','_') not in MoneroWalletOps.ops:
 if op in ('relay','submit'):
 	if len(cmd_args) != 0:
 		cfg._opts.usage()
-elif op == 'txview':
+elif op in ('txview','txlist'):
 	infile = [infile] + cmd_args
 elif op in ('create','sync','list','dump','restore'): # kafile_arg_ops
 	if len(cmd_args) > 1:

+ 64 - 4
mmgen/xmrwallet.py

@@ -26,7 +26,7 @@ from pathlib import PosixPath as Path
 
 from .objmethods import MMGenObject,Hilite,InitErrors
 from .obj import CoinTxID,Int
-from .color import red,yellow,green,blue,cyan,pink,orange
+from .color import red,yellow,green,blue,cyan,pink,orange,purple
 from .util import (
 	msg,
 	msg_r,
@@ -190,6 +190,7 @@ class MoneroMMGenTX:
 			'amount',
 			'fee',
 			'blob' }
+		oneline_fs = '{a:7} {b:8} {c:19} {d:13} {e:8} {f:6} {x:2} {g:6} {h:17} {j}'
 		chksum_nchars = 6
 		xmrwallet_tx_data = namedtuple('xmrwallet_tx_data',[
 			'op',
@@ -218,7 +219,22 @@ class MoneroMMGenTX:
 		def src_wallet_idx(self):
 			return int(self.data.source.split(':')[0])
 
-		def get_info(self,indent=''):
+		def get_info_oneline(self,indent='',cols=None):
+			d = self.data
+			return self.oneline_fs.format(
+					a = yellow(d.network),
+					b = d.seed_id.hl(),
+					c = make_timestr(d.submit_time if d.submit_time is not None else d.create_time),
+					d = orange(self.file_id),
+					e = purple(capfirst(d.op.ljust(8))),
+					f = red('{}:{}'.format(d.source.wallet,d.source.account).ljust(6)),
+					g = red('{}:{}'.format(d.dest.wallet,d.dest.account).ljust(6)) if d.dest else cyan('ext   '),
+					h = d.amount.fmt( color=True, iwidth=4, prec=12 ),
+					j = d.dest_address.fmt(width=cols-95,color=True) if cols else d.dest_address.hl(),
+					x = '->'
+				)
+
+		def get_info(self,indent='',cols=None):
 			d = self.data
 			pmt_id = d.dest_address.parsed.payment_id
 			fs = '\n'.join(list_gen(
@@ -584,6 +600,7 @@ class MoneroWalletOps:
 		'sweep',
 		'relay',
 		'txview',
+		'txlist',
 		'label',
 		'sign',
 		'submit',
@@ -1819,10 +1836,15 @@ class MoneroWalletOps:
 				die(1,'Exiting at user request')
 
 	class txview(base):
+		view_method = 'get_info'
 		opts = ('watch_only','autosign')
+		hdr = ''
+		col_hdr = ''
+		footer = ''
+		cols = None
 		do_umount = False
 
-		async def main(self):
+		async def main(self,cols=None):
 			if self.cfg.autosign:
 				asi = get_autosign_obj(self.cfg,'xmr')
 				files = [f for f in asi.xmr_tx_dir.iterdir() if f.name.endswith('.'+MoneroMMGenTX.Submitted.ext)]
@@ -1836,5 +1858,43 @@ class MoneroWalletOps:
 			if self.cfg.autosign:
 				asi.do_umount()
 			self.cfg._util.stdout_or_pager(
-				'\n'.join(tx.get_info() for tx in txs)
+				(self.hdr if len(files) > 1 else '')
+				+ self.col_hdr
+				+ '\n'.join(getattr(tx,self.view_method)(cols=cols) for tx in txs)
+				+ self.footer
 			)
+
+	class txlist(txview):
+		view_method = 'get_info_oneline'
+		add_nl = True
+		footer = '\n'
+
+		@property
+		def hdr(self):
+			return ('SUBMITTED ' if self.cfg.autosign else '') + 'MONERO TRANSACTIONS\n'
+
+		@property
+		def col_hdr(self):
+			return MoneroMMGenTX.View.oneline_fs.format(
+				a = 'Network',
+				b = 'Seed ID',
+				c = 'Submitted' if self.cfg.autosign else 'Date',
+				d = 'TxID',
+				e = 'Type',
+				f = 'Src',
+				g = 'Dest',
+				h = '  Amount',
+				j = 'Dest Address',
+				x = '',
+			) + '\n'
+
+		async def main(self):
+			if self.cfg.pager:
+				cols = None
+			else:
+				from .term import get_terminal_size
+				cols = self.cfg.columns or get_terminal_size().width
+				if cols < 106:
+					die(1, 'A terminal at least 106 columns wide is required to display this output'
+						+ ' (or use --columns or --pager)' )
+			await super().main(cols=cols)

+ 14 - 7
test/test_py_d/ts_misc.py

@@ -34,6 +34,7 @@ class TestSuiteMisc(TestSuiteBase):
 	cmd_group = (
 		('rpc_backends',     'RPC backends'),
 		('xmrwallet_txview', "'mmgen-xmrwallet' txview"),
+		('xmrwallet_txlist', "'mmgen-xmrwallet' txlist"),
 		('coin_daemon_info', "'examples/coin-daemon-info.py'"),
 		('term_echo',        "term.set('echo')"),
 		('term_cleanup',     'term.register_cleanup()'),
@@ -47,17 +48,23 @@ class TestSuiteMisc(TestSuiteBase):
 			t = self.spawn_chk('mmgen-tool',[f'--rpc-backend={b}','daemon_version'],extra_desc=f'({b})')
 		return t
 
-	def xmrwallet_txview(self):
+	def xmrwallet_txview(self,op='txview'):
 		files = get_file_with_ext('test/ref/monero','tx',no_dot=True,delete=False,return_list=True)
-		t = self.spawn( f'mmgen-xmrwallet', ['txview'] + files )
+		t = self.spawn( f'mmgen-xmrwallet', [op] + files )
 		res = strip_ansi_escapes(t.read()).replace('\r','')
-		for s in (
-			'Amount:    0.74 XMR',
-			'Dest:      56VQ9M6k',
-		):
-			assert s in res, f'{s} not in {res}'
+		if op == 'txview':
+			for s in (
+				'Amount:    0.74 XMR',
+				'Dest:      56VQ9M6k',
+			):
+				assert s in res, f'{s} not in {res}'
+		elif op == 'txlist':
+			assert re.search( '3EBD06-.*D94583-.*8BFA29-', res, re.DOTALL )
 		return t
 
+	def xmrwallet_txlist(self):
+		return self.xmrwallet_txview(op='txlist')
+
 	def coin_daemon_info(self):
 		start_test_daemons('ltc','eth')
 		t = self.spawn(f'examples/coin-daemon-info.py',['btc','ltc','eth'],cmd_dir='.')

+ 11 - 0
test/test_py_d/ts_xmr_autosign.py

@@ -78,6 +78,7 @@ class TestSuiteXMRAutosign(TestSuiteXMRWallet,TestSuiteAutosignBase):
 		('export_key_images',        'exporting signed key images from Alice’s offline wallets'),
 		('import_key_images',        'importing signed key images into Alice’s online wallets'),
 		('list_wallets',             'listing Alice’s wallets and checking balance'),
+		('txlist',                   'listing Alice’s submitted transactions'),
 	)
 
 	def __init__(self,trunner,cfgs,spawn):
@@ -358,3 +359,13 @@ class TestSuiteXMRAutosign(TestSuiteXMRWallet,TestSuiteAutosignBase):
 		assert after + '\n' == fmt(chk), f'\n{after}\n!=\n{fmt(chk)}'
 
 		return t
+
+	def txlist(self):
+		t = self.spawn( 'mmgen-xmrwallet', self.autosign_opts + ['txlist'] )
+		t.match_expect_list([
+			'SUBMITTED',
+			'Network','Submitted',
+			'Transfer 1:0','-> ext',
+			'Transfer 1:0','-> ext'
+		])
+		return t