Browse Source

mmgen.tw.{common,addrs,txhistory,unspent}: cleanups

The MMGen Project 2 years ago
parent
commit
2b026f1ae7

+ 1 - 1
mmgen/objmethods.py

@@ -71,7 +71,7 @@ class Hilite:
 			assert trunc_ok, "If 'trunc_ok' is false, 'width' must be >= screen width of string"
 			s = truncate_str(s,width-add_len)
 		if s == '' and nullrepl:
-			s = nullrepl.center(width)
+			s = nullrepl.ljust(width)
 		else:
 			s = a+s+b
 			if center:

+ 1 - 0
mmgen/proto/btc/tw/common.py

@@ -96,6 +96,7 @@ class BitcoinTwCommon:
 				else:
 					lm.confs = d['confirmations']
 					lm.txid = d['txid']
+					lm.vout = d['vout']
 					lm.date = None
 					data[lm] = {
 						'amt': self.proto.coin_amt('0'),

+ 1 - 1
mmgen/proto/btc/tw/txhistory.py

@@ -231,7 +231,7 @@ class BitcoinTwTxHistory(TwTxHistory,BitcoinTwCommon):
 	prompt = """
 Sort options: [t]xid, [a]mt, total a[m]t, [A]ge, [b]locknum, [r]everse
 Column options: toggle [D]ays/date/confs/block, tx[i]d, [T]otal amt
-Filter options: show [u]nconfirmed
+Filters: show [u]nconfirmed
 View/Print: pager [v]iew, full [V]iew, screen [p]rint, full [P]rint
 Actions: [q]uit, r[e]draw:
 """

+ 9 - 5
mmgen/tool/rpc.py

@@ -97,12 +97,15 @@ class tool_cmd(tool_cmd_base):
 		return await al.format( showcoinaddrs, sort, show_age, age_fmt or 'confs' )
 
 	async def twops(self,
-			obj,pager,reverse,detail,sort,age_fmt,interactive,show_mmid):
+			obj,pager,reverse,detail,sort,age_fmt,interactive,
+			**kwargs ):
 
-		obj.interactive = interactive
 		obj.reverse = reverse
 		obj.age_fmt = age_fmt
-		obj.show_mmid = show_mmid
+		obj.interactive = interactive
+
+		for k,v in kwargs.items():
+			setattr(obj,k,v)
 
 		await obj.get_data(sort_key=sort,reverse_sort=reverse)
 
@@ -128,7 +131,8 @@ class tool_cmd(tool_cmd_base):
 		from ..tw.unspent import TwUnspentOutputs
 		obj = await TwUnspentOutputs(self.proto,minconf=minconf)
 		ret = await self.twops(
-			obj,pager,reverse,wide,sort,age_fmt,interactive,show_mmid)
+			obj,pager,reverse,wide,sort,age_fmt,interactive,
+			show_mmid = show_mmid )
 		del obj.wallet
 		return ret
 
@@ -144,7 +148,7 @@ class tool_cmd(tool_cmd_base):
 
 		obj = await TwTxHistory(self.proto,sinceblock=sinceblock)
 		return await self.twops(
-			obj,pager,reverse,detail,sort,age_fmt,interactive,show_mmid=None)
+			obj,pager,reverse,detail,sort,age_fmt,interactive )
 
 	async def add_label(self,mmgen_or_coin_addr:str,label:str):
 		"add descriptive label for address in tracking wallet"

+ 1 - 3
mmgen/tw/addrs.py

@@ -63,9 +63,7 @@ class TwAddrList(MMGenDict,TwCommon,metaclass=AsyncInit):
 
 		mmids = sorted(self,key=sort_algo,reverse=bool(sort and 'reverse' in sort))
 		if show_age:
-			await self.set_dates(
-				self.rpc,
-				[o for o in mmids if hasattr(o,'confs')] )
+			await self.set_dates( [o for o in mmids if hasattr(o,'confs')] )
 
 		def gen_output():
 

+ 20 - 14
mmgen/tw/common.py

@@ -32,6 +32,7 @@ from ..addr import MMGenID
 # mixin class for TwUnspentOutputs,TwAddrList,TwTxHistory:
 class TwCommon:
 
+	dates_set   = False
 	cols        = None
 	reverse     = False
 	group       = False
@@ -55,12 +56,21 @@ class TwCommon:
 		'days': lambda rpc,secs: (rpc.cur_date - secs) // 86400 if secs else 0,
 		'date': (
 			lambda rpc,secs: '{}-{:02}-{:02}'.format(*time.gmtime(secs)[:3])[2:]
-				if secs else '--------' ),
+				if secs else '-       '),
 		'date_time': (
 			lambda rpc,secs: '{}-{:02}-{:02} {:02}:{:02}'.format(*time.gmtime(secs)[:5])
-				if secs else '---------- -----' ),
+				if secs else '-               '),
 	}
 
+	tcols_errmsg = """
+		--columns or MMGEN_COLUMNS value ({}) is too small to display the {}.
+		Minimum value for this configuration: {}
+	"""
+	twid_errmsg = """
+		Screen is too narrow to display the {}
+		Please resize your screen to at least {} characters and hit any key:
+	"""
+
 	def age_disp(self,o,age_fmt):
 		if age_fmt == 'confs':
 			return o.confs
@@ -86,14 +96,14 @@ class TwCommon:
 
 		self.do_sort(key=sort_key,reverse=reverse_sort)
 
-	@staticmethod
-	async def set_dates(rpc,us):
-		if us and us[0].date is None:
+	async def set_dates(self,us):
+		if not self.dates_set:
 			# 'blocktime' differs from 'time', is same as getblockheader['time']
 			dates = [ o.get('blocktime',0)
-				for o in await rpc.gathered_icall('gettransaction',[(o.txid,True,False) for o in us]) ]
+				for o in await self.rpc.gathered_icall('gettransaction',[(o.txid,True,False) for o in us]) ]
 			for idx,o in enumerate(us):
 				o.date = dates[idx]
+			self.dates_set = True
 
 	@property
 	def age_w(self):
@@ -129,13 +139,9 @@ class TwCommon:
 				return cols
 			if sys.stdout.isatty():
 				if g.columns:
-					die(1,
-						f'\n--columns or MMGEN_COLUMNS value ({g.columns}) is too small to display the {self.desc}.\n'
-						+ f'Minimum value for this configuration: {min_cols}' )
+					die(1,'\n'+fmt(self.tcols_errmsg.format(g.columns,self.desc,min_cols),indent='  '))
 				else:
-					get_char_raw(
-						f'\nScreen is too narrow to display the {self.desc}\n'
-						+ f'Please resize your screen to at least {min_cols} characters and hit any key: ' )
+					get_char_raw('\n'+fmt(self.twid_errmsg.format(self.desc,min_cols),append=''))
 			else:
 				return min_cols
 
@@ -175,7 +181,7 @@ class TwCommon:
 		if not cached:
 			data = self.data
 			if self.has_age and self.age_fmt in self.age_fmts_date_dependent:
-				await self.set_dates(self.rpc,data)
+				await self.set_dates(data)
 
 			if not getattr(self,'column_widths',None):
 				self.set_column_params()
@@ -202,7 +208,7 @@ class TwCommon:
 
 	async def format_detail(self,color):
 		if self.has_age:
-			await self.set_dates(self.rpc,self.data)
+			await self.set_dates(self.data)
 
 		sep = self.detail_display_separator
 

+ 4 - 5
mmgen/tw/txhistory.py

@@ -95,7 +95,7 @@ class TwTxHistory(MMGenObject,TwCommon,metaclass=AsyncInit):
 			freew[k] = min( total_freew - sum(freew[k2] for k2 in varcols-{k}), varw[k] )
 
 		self.column_widths = namedtuple('column_params',
-			['col1','txid','addr1','amt','addr2','comment'])(
+			['num','txid','addr1','amt','addr2','comment'])(
 				col1_w,
 				min(
 					# max txid was reduced by txid_adj, so stretch to fill available space, if any
@@ -118,14 +118,14 @@ class TwTxHistory(MMGenObject,TwCommon,metaclass=AsyncInit):
 		yield ''
 
 		hdr_fs = self.squeezed_hdr_fs_fs.format(
-			nw = cw.col1,
+			nw = cw.num,
 			dw = self.age_w,
 			txid_fs = f'{{i:{cw.txid}}} ' if self.show_txid else '',
 			aw = cw.addr1,
 			a2w = cw.addr2 )
 
 		fs = self.squeezed_fs_fs.format(
-			nw = cw.col1,
+			nw = cw.num,
 			dw = self.age_w,
 			txid_fs = f'{{i:{cw.txid}}} ' if self.show_txid else '' )
 
@@ -206,8 +206,7 @@ class TwTxHistory(MMGenObject,TwCommon,metaclass=AsyncInit):
 		'txid':        lambda i: i.txid,
 	}
 
-	@staticmethod
-	async def set_dates(rpc,us):
+	async def set_dates(self,us):
 		pass
 
 	@property

+ 23 - 21
mmgen/tw/unspent.py

@@ -126,50 +126,50 @@ class TwUnspentOutputs(MMGenObject,TwCommon,metaclass=AsyncInit):
 
 		self.column_widths = namedtuple(
 			'column_widths',
-			['col1_w','mmid_w','addr_w','btaddr_w','comment_w','tx_w','txdots']
-			)(col1_w,  mmid_w,  addr_w,  btaddr_w,  comment_w,  tx_w,  txdots)
+			['num','mmid','addr','btaddr','comment','tx']
+			)(col1_w,  mmid_w,  addr_w,  btaddr_w,  comment_w,  tx_w)
 
-	def gen_squeezed_display(self,c,color):
-		fs     = self.squeezed_fs_fs.format(     cw=c.col1_w, tw=c.tx_w )
-		hdr_fs = self.squeezed_hdr_fs_fs.format( cw=c.col1_w, tw=c.tx_w )
+	def gen_squeezed_display(self,cw,color):
+		fs     = self.squeezed_fs_fs.format(     cw=cw.num, tw=cw.tx )
+		hdr_fs = self.squeezed_hdr_fs_fs.format( cw=cw.num, tw=cw.tx )
 		yield hdr_fs.format(
 			n  = 'Num',
-			t  = 'TXid'.ljust(c.tx_w - 2) + ' Vout',
-			a  = 'Address'.ljust(c.addr_w),
+			t  = 'TXid'.ljust(cw.tx - 2) + ' Vout',
+			a  = 'Address'.ljust(cw.addr),
 			A  = f'Amt({self.proto.dcoin})'.ljust(self.disp_prec+5),
 			A2 = f' Amt({self.proto.coin})'.ljust(self.disp_prec+4),
 			c  = self.age_hdr ).rstrip()
 
 		for n,i in enumerate(self.data):
-			addr_dots = '|' + '.'*(c.addr_w-1)
+			addr_dots = '|' + '.'*(cw.addr-1)
 			mmid_disp = MMGenID.fmtc(
 				(
-					'.'*c.mmid_w if i.skip == 'addr' else
+					'.'*cw.mmid if i.skip == 'addr' else
 					i.twmmid if i.twmmid.type == 'mmgen' else
 					f'Non-{g.proj_name}'
 				),
-				width = c.mmid_w,
+				width = cw.mmid,
 				color = color )
 
 			if self.show_mmid:
 				addr_out = '{} {}{}'.format((
-					type(i.addr).fmtc(addr_dots,width=c.btaddr_w,color=color) if i.skip == 'addr' else
-					i.addr.fmt(width=c.btaddr_w,color=color)
+					type(i.addr).fmtc(addr_dots,width=cw.btaddr,color=color) if i.skip == 'addr' else
+					i.addr.fmt(width=cw.btaddr,color=color)
 				),
 					mmid_disp,
-					(' ' + i.comment.fmt(width=c.comment_w,color=color)) if c.comment_w > 0 else ''
+					(' ' + i.comment.fmt(width=cw.comment,color=color)) if cw.comment > 0 else ''
 				)
 			else:
 				addr_out = (
-					type(i.addr).fmtc(addr_dots,width=c.addr_w,color=color) if i.skip=='addr' else
-					i.addr.fmt(width=c.addr_w,color=color) )
+					type(i.addr).fmtc(addr_dots,width=cw.addr,color=color) if i.skip=='addr' else
+					i.addr.fmt(width=cw.addr,color=color) )
 
 			yield fs.format(
 				n  = str(n+1)+')',
 				t  = (
 					'' if not i.txid else
-					' ' * (c.tx_w-4) + '|...' if i.skip  == 'txid' else
-					i.txid[:c.tx_w-len(c.txdots)] + c.txdots ),
+					' ' * (cw.tx-4) + '|...' if i.skip  == 'txid' else
+					i.txid.truncate(width=cw.tx,color=True) ),
 				v  = i.vout,
 				a  = addr_out,
 				A  = i.amt.fmt(color=color,prec=self.disp_prec),
@@ -179,8 +179,10 @@ class TwUnspentOutputs(MMGenObject,TwCommon,metaclass=AsyncInit):
 
 	def gen_detail_display(self,color):
 
-		addr_w = max(len(i.addr) for i in self.data)
-		mmid_w = max(len(('',i.twmmid)[i.twmmid.type=='mmgen']) for i in self.data) or 12 # DEADBEEF:S:1
+		data = self.data
+
+		addr_w = max(len(i.addr) for i in data)
+		mmid_w = max(len(('',i.twmmid)[i.twmmid.type=='mmgen']) for i in data) or 12 # DEADBEEF:S:1
 
 		fs = self.wide_fs_fs.format(
 			tw = self.txid_w + 3,
@@ -199,9 +201,9 @@ class TwUnspentOutputs(MMGenObject,TwCommon,metaclass=AsyncInit):
 			D  = 'Date',
 			l  = 'Label' )
 
-		max_comment_len = max([len(i.comment) for i in self.data if i.comment] or [2])
+		max_comment_len = max([len(i.comment) for i in data if i.comment] or [2])
 
-		for n,i in enumerate(self.data):
+		for n,i in enumerate(data):
 			yield fs.format(
 				n  = str(n+1) + ')',
 				t  = '{},{}'.format(

+ 2 - 2
mmgen/util.py

@@ -144,9 +144,9 @@ def pp_fmt(d):
 def pp_msg(d):
 	msg(pp_fmt(d))
 
-def fmt(s,indent='',strip_char=None):
+def fmt(s,indent='',strip_char=None,append='\n'):
 	"de-indent multiple lines of text, or indent with specified string"
-	return indent + ('\n'+indent).join([l.strip(strip_char) for l in s.strip().splitlines()]) + '\n'
+	return indent + ('\n'+indent).join([l.strip(strip_char) for l in s.strip().splitlines()]) + append
 
 def fmt_list(iterable,fmt='dfl',indent=''):
 	"pretty-format a list"

+ 1 - 1
test/overlay/fakemods/mmgen/tw/common.py

@@ -17,7 +17,7 @@ if overlay_fake_os.getenv('MMGEN_BOGUS_UNSPENT_DATA'):
 
 	class overlay_fake_data2:
 
-		async def set_dates(foo,rpc,us):
+		async def set_dates(foo,us):
 			for o in us:
 				o.date = 1831006505 - int(9.7 * 60 * (o.confs - 1))
 

+ 9 - 8
test/test_py_d/ts_ethdev.py

@@ -605,7 +605,7 @@ class TestSuiteEthdev(TestSuiteBase,TestSuiteShared):
 			t.read()
 		return t
 
-	def txsign(self,ni=False,ext='{}.regtest.rawtx',add_args=[]):
+	def txsign(self,ni=False,ext='{}.regtest.rawtx',add_args=[],dev_send=False):
 		ext = ext.format('-α' if g.debug_utf8 else '')
 		keyfile = joinpath(self.tmpdir,parity_devkey_fn)
 		txfile = self.get_file_with_ext(ext,no_dot=True)
@@ -616,7 +616,8 @@ class TestSuiteEthdev(TestSuiteBase,TestSuiteShared):
 						+ ['--rpc-host=bad_host'] # ETH signing must work without RPC
 						+ add_args
 						+ ([],['--yes'])[ni]
-						+ ['-k', keyfile, txfile, dfl_words_file] )
+						+ ([f'--keys-from-file={keyfile}'] if dev_send else [])
+						+ [txfile, dfl_words_file] )
 		return self.txsign_ui_common(t,ni=ni,has_label=True)
 
 	def txsend(self,ni=False,ext='{}.regtest.sigtx',add_args=[]):
@@ -662,10 +663,10 @@ class TestSuiteEthdev(TestSuiteBase,TestSuiteShared):
 		return self.txcreate(args=args,menu=menu,acct='1',tweaks=['confirm_non_mmgen'])
 	def txview1_raw(self):
 		return self.txview(ext_fs='{}.regtest.rawtx')
-	def txsign1(self):    return self.txsign(add_args=['--use-internal-keccak-module'])
+	def txsign1(self):    return self.txsign(add_args=['--use-internal-keccak-module'],dev_send=True)
 	def tx_status0_bad(self):
 		return self.tx_status(ext='{}.regtest.sigtx',expect_str='neither in mempool nor blockchain',exit_val=1)
-	def txsign1_ni(self): return self.txsign(ni=True)
+	def txsign1_ni(self): return self.txsign(ni=True,dev_send=True)
 	def txsend1(self):    return self.txsend()
 	def txview1_sig(self): # do after send so that TxID is displayed
 		return self.txview(ext_fs='{}.regtest.sigtx')
@@ -674,14 +675,14 @@ class TestSuiteEthdev(TestSuiteBase,TestSuiteShared):
 	def txcreate2(self):
 		args = ['98831F3A:E:11,1.234']
 		return self.txcreate(args=args,acct='10',tweaks=['confirm_non_mmgen'])
-	def txsign2(self): return self.txsign(ni=True,ext='1.234,50000]{}.regtest.rawtx')
+	def txsign2(self): return self.txsign(ni=True,ext='1.234,50000]{}.regtest.rawtx',dev_send=True)
 	def txsend2(self): return self.txsend(ext='1.234,50000]{}.regtest.sigtx')
 	def bal2(self):    return self.bal(n='2')
 
 	def txcreate3(self):
 		args = ['98831F3A:E:21,2.345']
 		return self.txcreate(args=args,acct='10',tweaks=['confirm_non_mmgen'])
-	def txsign3(self): return self.txsign(ni=True,ext='2.345,50000]{}.regtest.rawtx')
+	def txsign3(self): return self.txsign(ni=True,ext='2.345,50000]{}.regtest.rawtx',dev_send=True)
 	def txsend3(self): return self.txsend(ext='2.345,50000]{}.regtest.sigtx')
 	def bal3(self):    return self.bal(n='3')
 
@@ -797,14 +798,14 @@ class TestSuiteEthdev(TestSuiteBase,TestSuiteShared):
 		t.expect('or gas price: ',fee+'\n')
 		return t
 
-	def txsign4(self): return self.txsign(ni=True,ext='.45495,50000]{}.regtest.rawtx')
+	def txsign4(self): return self.txsign(ni=True,ext='.45495,50000]{}.regtest.rawtx',dev_send=True)
 	def txsend4(self): return self.txsend(ext='.45495,50000]{}.regtest.sigtx')
 	def bal4(self):    return self.bal(n='4')
 
 	def txcreate5(self):
 		args = [burn_addr + ','+amt1]
 		return self.txcreate(args=args,acct='10',tweaks=['confirm_non_mmgen'])
-	def txsign5(self): return self.txsign(ni=True,ext=amt1+',50000]{}.regtest.rawtx')
+	def txsign5(self): return self.txsign(ni=True,ext=amt1+',50000]{}.regtest.rawtx',dev_send=True)
 	def txsend5(self): return self.txsend(ext=amt1+',50000]{}.regtest.sigtx')
 	def bal5(self):    return self.bal(n='5')
 

+ 3 - 1
test/test_py_d/ts_regtest.py

@@ -1367,7 +1367,9 @@ class TestSuiteRegtest(TestSuiteBase,TestSuiteShared):
 	def bob_msgverify_export_single(self):
 		sid = self._user_sid('bob')
 		mmid = f'{sid}:{self.dfl_mmtype}:1'
-		t = self.spawn('mmgen-tool', [ '--bob', '--color=0', 'listaddress', mmid ], no_msg=True)
+		args = [ '--bob', '--color=0', 'listaddress', mmid ]
+		imsg(f'Running mmgen-tool {fmt_list(args,fmt="bare")}')
+		t = self.spawn('mmgen-tool', args, no_msg=True)
 		addr = t.expect_getend(mmid).split()[0]
 		t.close()
 		return self.bob_msgverify(