Browse Source

CoinAddr: add `views`, `view_pref` attrs

The MMGen Project 2 months ago
parent
commit
eeb98869d3

+ 7 - 2
mmgen/addr.py

@@ -158,6 +158,8 @@ class CoinAddr(HiliteStr, InitErrors, MMGenObject):
 			ap = proto.decode_addr(addr)
 			assert ap, f'coin address {addr!r} could not be parsed'
 			me = str.__new__(cls, addr)
+			me.views = [addr]
+			me.view_pref = 0
 			me.addr_fmt = ap.fmt
 			me.bytes = ap.bytes
 			me.ver_bytes = ap.ver_bytes
@@ -177,10 +179,13 @@ class CoinAddr(HiliteStr, InitErrors, MMGenObject):
 	def fmtc(cls,s,width,color=False):
 		return super().fmtc( s=s[:width-2]+'..' if len(s) > width else s, width=width, color=color )
 
-	def fmt(self, width, color=False):
-		s = self
+	def fmt(self, view_pref, width, color=False):
+		s = self.views[view_pref]
 		return super().fmtc(f'{s[:width-2]}..' if len(s) > width else s, width=width, color=color)
 
+	def hl(self, view_pref, color=True):
+		return getattr(color_mod, self.color)(self.views[view_pref]) if color else self.views[view_pref]
+
 def is_coin_addr(proto,s):
 	return get_obj( CoinAddr, proto=proto, addr=s, silent=True, return_bool=True )
 

+ 1 - 1
mmgen/addrfile.py

@@ -124,7 +124,7 @@ class AddrFile(MMGenObject):
 			elif type(p).__name__ == 'PasswordList':
 				out.append(fs.format(e.idx,e.passwd,c))
 			else: # First line with idx
-				out.append(fs.format(e.idx,e.addr,c))
+				out.append(fs.format(e.idx, e.addr.views[e.addr.view_pref], c))
 				if p.has_keys:
 					if self.cfg.b16:
 						out.append(fs.format( '', f'orig_hex: {e.sec.orig_bytes.hex()}', c ))

+ 2 - 2
mmgen/addrlist.py

@@ -147,7 +147,7 @@ class AddrList(MMGenObject): # Address info for a single seed ID
 	gen_passwds  = False
 	gen_keys     = False
 	has_keys     = False
-	chksum_rec_f = lambda foo, e: (str(e.idx), e.addr)
+	chksum_rec_f = lambda foo, e: (str(e.idx), e.addr.views[e.addr.view_pref])
 
 	def dmsg_sc(self,desc,data):
 		Msg(f'sc_debug_{desc}: {data}')
@@ -415,7 +415,7 @@ class KeyAddrList(AddrList):
 	gen_desc_pl  = 's'
 	gen_keys     = True
 	has_keys     = True
-	chksum_rec_f = lambda foo, e: (str(e.idx), e.addr, e.sec.wif)
+	chksum_rec_f = lambda foo, e: (str(e.idx), e.addr.views[e.addr.view_pref], e.sec.wif)
 
 class ViewKeyAddrList(KeyAddrList):
 	desc         = 'viewkey-address'

+ 1 - 1
mmgen/autosign.py

@@ -305,7 +305,7 @@ class Signable:
 						for nm in non_mmgen:
 							yield fs.format(
 								tx.txid.fmt( width=t_wid, color=True ) if nm is non_mmgen[0] else ' '*t_wid,
-								nm.addr.fmt( width=a_wid, color=True ),
+								nm.addr.fmt(nm.addr.view_pref, width=a_wid, color=True),
 								nm.amt.hl() + ' ' + yellow(tx.coin))
 
 				msg('\n' + '\n'.join(gen()))

+ 1 - 1
mmgen/main_addrimport.py

@@ -147,7 +147,7 @@ async def main():
 		mode       = 'i' )
 
 	if cfg.token or cfg.token_addr:
-		msg(f'Importing for token {twctl.token.hl()} ({twctl.token.hlc(proto.tokensym)})')
+		msg(f'Importing for token {twctl.token.hl(0)} ({twctl.token.hlc(proto.tokensym)})')
 
 	for k,v in addrimport_msgs.items():
 		addrimport_msgs[k] = fmt(v,indent='  ',strip_char='\t').rstrip()

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

@@ -145,7 +145,7 @@ class BitcoinTwTransaction:
 	def txid_disp(self,color,width=None):
 		return self.txid.hl(color=color) if width is None else self.txid.truncate(width=width,color=color)
 
-	def vouts_list_disp(self,src,color,indent=''):
+	def vouts_list_disp(self, src, color, indent, addr_view_pref):
 
 		fs1,fs2 = {
 			'inputs':  ('{i},{n} {a} {A}', '{i},{n} {a} {A} {l}'),
@@ -160,7 +160,7 @@ class BitcoinTwTransaction:
 						i = CoinTxID(e.txid).hl(color=color),
 						n = (nocolor,red)[color](str(e.data['n']).ljust(3)),
 						a = CoinAddr(self.proto, e.coin_addr).fmt(
-							width=self.max_addrlen[src], color=color),
+							addr_view_pref, width=self.max_addrlen[src], color=color),
 						A = self.proto.coin_amt( e.data['value'] ).fmt(color=color)
 					).rstrip()
 				else:
@@ -180,7 +180,7 @@ class BitcoinTwTransaction:
 
 		return f'\n{indent}'.join( gen_output() ).strip()
 
-	def vouts_disp(self,src,width,color):
+	def vouts_disp(self, src, width, color, addr_view_pref):
 
 		def gen_output():
 
@@ -192,7 +192,7 @@ class BitcoinTwTransaction:
 				if not mmid:
 					if width and space_left < addr_w:
 						break
-					yield CoinAddr(self.proto, e.coin_addr).fmt(width=addr_w, color=color)
+					yield CoinAddr(self.proto, e.coin_addr).fmt(addr_view_pref, width=addr_w, color=color)
 					space_left -= addr_w
 				elif mmid.type == 'mmgen':
 					mmid_disp = mmid + bal_star

+ 5 - 4
mmgen/proto/btc/tx/info.py

@@ -81,11 +81,11 @@ class TxInfo(TxInfo):
 
 			if terse:
 				iwidth = max(len(str(int(e.amt))) for e in io)
-				addr_w = max(len(e.addr) for f in (tx.inputs,tx.outputs) for e in f)
+				addr_w = max(len(e.addr.views[vp1]) for f in (tx.inputs,tx.outputs) for e in f)
 				for n,e in enumerate(io_sorted()):
 					yield '{:3} {} {} {} {}\n'.format(
 						n+1,
-						e.addr.fmt(width=addr_w, color=True),
+						e.addr.fmt(vp1, width=addr_w, color=True),
 						get_mmid_fmt(e, is_input),
 						e.amt.fmt(iwidth=iwidth,color=True),
 						tx.dcoin )
@@ -99,9 +99,9 @@ class TxInfo(TxInfo):
 					def gen():
 						if is_input:
 							yield (n+1, 'tx,vout:', f'{e.txid.hl()},{red(str(e.vout))}')
-							yield ('',  'address:', f'{e.addr.hl()} {mmid_fmt}')
+							yield ('',  'address:', f'{e.addr.hl(vp1)} {mmid_fmt}')
 						else:
-							yield (n+1, 'address:', f'{e.addr.hl()} {mmid_fmt}')
+							yield (n+1, 'address:', f'{e.addr.hl(vp1)} {mmid_fmt}')
 						if e.comment:
 							yield ('',  'comment:', e.comment.hl())
 						yield     ('',  'amount:',  f'{e.amt.hl()} {tx.dcoin}')
@@ -112,6 +112,7 @@ class TxInfo(TxInfo):
 					yield '\n'.join('{:>{w}} {:<8} {}'.format(*d,w=col1_w) for d in gen()) + '\n\n'
 
 		tx = self.tx
+		vp1 = 0
 
 		return (
 			'Displaying inputs and outputs in {} sort order'.format({'raw':'raw','addr':'address'}[sort])

+ 3 - 3
mmgen/proto/eth/tx/info.py

@@ -48,8 +48,8 @@ class TxInfo(TxInfo):
 		td = t['data']
 		to_addr = t[self.to_addr_key]
 		return fs.format(
-			f      = t['from'].hl(),
-			t      = to_addr.hl() if to_addr else blue('None'),
+			f      = t['from'].hl(0),
+			t      = to_addr.hl(0) if to_addr else blue('None'),
 			a      = t['amt'].hl(),
 			n      = t['nonce'].hl(),
 			d      = '{}... ({} bytes)'.format(td[:40],len(td)//2) if len(td) else blue('None'),
@@ -82,6 +82,6 @@ class TokenTxInfo(TxInfo):
 
 	def format_body(self,*args,**kwargs):
 		return 'Token:     {d} {c}\n{r}'.format(
-			d = self.tx.txobj['token_addr'].hl(),
+			d = self.tx.txobj['token_addr'].hl(0),
 			c = blue('(' + self.tx.proto.dcoin + ')'),
 			r = super().format_body(*args,**kwargs ))

+ 1 - 1
mmgen/proto/eth/tx/online.py

@@ -56,7 +56,7 @@ class OnlineSigned(Signed,TxBase.OnlineSigned):
 
 	def print_contract_addr(self):
 		if 'token_addr' in self.txobj:
-			msg('Contract address: {}'.format(self.txobj['token_addr'].hl()))
+			msg('Contract address: {}'.format(self.txobj['token_addr'].hl(0)))
 
 class TokenOnlineSigned(TokenSigned,OnlineSigned):
 

+ 2 - 2
mmgen/tw/addresses.py

@@ -183,7 +183,7 @@ class TwAddresses(TwView):
 			n = str(n) + ')',
 			m = d.twmmid.fmt( width=cw.mmid, color=color ),
 			u = yes if d.recvd else no,
-			a = d.addr.fmt( color=color, width=cw.addr ),
+			a = d.addr.fmt(self.addr_view_pref, width=cw.addr, color=color),
 			c = d.comment.fmt2( width=cw.comment, color=color, nullrepl='-' ),
 			A = d.amt.fmt( color=color, iwidth=cw.iwidth, prec=self.disp_prec ),
 			d = self.age_disp( d, self.age_fmt )
@@ -194,7 +194,7 @@ class TwAddresses(TwView):
 			n = str(n) + ')',
 			m = d.twmmid.fmt( width=cw.mmid, color=color ),
 			u = yes if d.recvd else no,
-			a = d.addr.fmt( color=color, width=cw.addr ),
+			a = d.addr.fmt(self.addr_view_pref, width=cw.addr, color=color),
 			c = d.comment.fmt2( width=cw.comment, color=color, nullrepl='-' ),
 			A = d.amt.fmt( color=color, iwidth=cw.iwidth, prec=self.disp_prec ),
 			b = self.age_disp( d, 'block' ),

+ 2 - 1
mmgen/tw/ctl.py

@@ -280,7 +280,8 @@ class TwCtl(MMGenObject,metaclass=AsyncInit):
 			if not silent:
 				desc = '{t} address {a} in tracking wallet'.format(
 					t = res.twmmid.type.replace('mmgen','MMGen'),
-					a = res.twmmid.addr.hl())
+					a = res.twmmid.addr.hl() if res.twmmid.type == 'mmgen' else
+						res.twmmid.addr.hl(res.twmmid.addr.view_pref))
 				msg(
 					'Added label {} to {}'.format(comment.hl2(encl='‘’'),desc) if comment else
 					'Removed label from {}'.format(desc) )

+ 8 - 6
mmgen/tw/txhistory.py

@@ -62,8 +62,10 @@ class TwTxHistory(TwView):
 		# var cols: inputs outputs comment [txid]
 		if not hasattr(self,'varcol_maxwidths'):
 			self.varcol_maxwidths = {
-				'inputs': max(len(d.vouts_disp('inputs',width=None,color=False)) for d in data),
-				'outputs': max(len(d.vouts_disp('outputs',width=None,color=False)) for d in data),
+				'inputs': max(len(d.vouts_disp(
+					'inputs', width=None, color=False, addr_view_pref=self.addr_view_pref)) for d in data),
+				'outputs': max(len(d.vouts_disp(
+					'outputs', width=None, color=False, addr_view_pref=self.addr_view_pref)) for d in data),
 				'comment': max(len(d.comment) for d in data),
 			}
 
@@ -123,9 +125,9 @@ class TwTxHistory(TwView):
 				n = str(n) + ')',
 				t = d.txid_disp( width=cw.txid, color=color ) if hasattr(cw,'txid') else None,
 				d = d.age_disp( self.age_fmt, width=self.age_w, color=color ),
-				i = d.vouts_disp( 'inputs', width=cw.inputs, color=color ),
+				i = d.vouts_disp('inputs', width=cw.inputs, color=color, addr_view_pref=self.addr_view_pref),
 				A = d.amt_disp(self.show_total_amt).fmt( iwidth=cw.iwidth, prec=self.disp_prec, color=color ),
-				o = d.vouts_disp( 'outputs', width=cw.outputs, color=color ),
+				o = d.vouts_disp('outputs', width=cw.outputs, color=color, addr_view_pref=self.addr_view_pref),
 				c = d.comment.fmt2( width=cw.comment, color=color, nullrepl='-' ) )
 
 	def gen_detail_display(self,data,cw,fs,color,fmt_method):
@@ -153,9 +155,9 @@ class TwTxHistory(TwView):
 				A = d.amt_disp(show_total_amt=True).hl( color=color ),
 				B = d.amt_disp(show_total_amt=False).hl( color=color ),
 				f = d.fee_disp( color=color ),
-				i = d.vouts_list_disp( 'inputs', color=color, indent=' '*8 ),
+				i = d.vouts_list_disp('inputs', color=color, indent=' '*8, addr_view_pref=self.addr_view_pref),
 				N = d.nOutputs,
-				o = d.vouts_list_disp( 'outputs', color=color, indent=' '*8 ),
+				o = d.vouts_list_disp('outputs', color=color, indent=' '*8, addr_view_pref=self.addr_view_pref),
 			)
 
 	sort_disp = {

+ 2 - 2
mmgen/tw/unspent.py

@@ -187,7 +187,7 @@ class TwUnspentOutputs(TwView):
 					else d.txid.truncate( width=cw.txid, color=color )) if cw.txid else None,
 				v = ' ' + d.vout.fmt( width=cw.vout-1, color=color ) if cw.vout else None,
 				a = d.addr.fmtc( '|' + '.'*(cw.addr-1), width=cw.addr, color=color ) if d.skip == 'addr'
-					else d.addr.fmt( width=cw.addr, color=color ),
+					else d.addr.fmt(self.addr_view_pref, width=cw.addr, color=color),
 				m = (d.twmmid.fmtc( '.'*cw.mmid, width=cw.mmid, color=color ) if d.skip == 'addr'
 					else d.twmmid.fmt( width=cw.mmid, color=color )) if cw.mmid else None,
 				c = d.comment.fmt2( width=cw.comment, color=color, nullrepl='-' ) if cw.comment else None,
@@ -203,7 +203,7 @@ class TwUnspentOutputs(TwView):
 				n = str(n+1) + ')',
 				t = d.txid.fmt( width=cw.txid, color=color ) if cw.txid else None,
 				v = ' ' + d.vout.fmt( width=cw.vout-1, color=color ) if cw.vout else None,
-				a = d.addr.fmt( width=cw.addr, color=color ),
+				a = d.addr.fmt(self.addr_view_pref, width=cw.addr, color=color),
 				m = d.twmmid.fmt( width=cw.mmid, color=color ),
 				A = d.amt.fmt( color=color, iwidth=cw.iwidth, prec=self.disp_prec ),
 				B = d.amt2.fmt( color=color, iwidth=cw.iwidth2, prec=self.disp_prec ) if cw.amt2 else None,

+ 4 - 0
mmgen/tw/view.py

@@ -96,6 +96,7 @@ class TwView(MMGenObject,metaclass=AsyncInit):
 	term_width = 0
 	scrollable_height = 0
 	min_scrollable_height = 5
+	addr_view_pref = 0
 	pos = 0
 	filters = ()
 
@@ -827,3 +828,6 @@ class TwView(MMGenObject,metaclass=AsyncInit):
 
 		def d_redraw(self,parent):
 			msg_r(CUR_HOME + ERASE_ALL)
+
+		def d_addr_view_pref(self,parent):
+			parent.addr_view_pref = (parent.addr_view_pref + 1) % len(parent.bch_addr_fmts)

+ 1 - 1
mmgen/tx/new.py

@@ -297,7 +297,7 @@ class New(Base):
 					self.cfg,
 					'{a} {b} {c}\n{d}'.format(
 						a = yellow('Requested change address'),
-						b = (chg.mmid or chg.addr).hl(),
+						b = chg.mmid.hl() if chg.mmid else chg.addr.hl(chg.addr.view_pref),
 						c = yellow('is already used!'),
 						d = yellow('Address reuse harms your privacy and security. Continue anyway? (y/N): ')
 					),

+ 6 - 6
mmgen/xmrwallet.py

@@ -116,7 +116,7 @@ def gen_acct_addr_info(self, wallet_data, account, indent=''):
 			continue
 		yield fs.format(
 			I = addr['address_index'],
-			A = ca.hl() if self.cfg.full_address else ca.fmt(color=True, width=addr_width),
+			A = ca.hl(0) if self.cfg.full_address else ca.fmt(0, color=True, width=addr_width),
 			U = (red('True ') if addr['used'] else green('False')),
 			B = fmt_amt(bal),
 			L = pink(addr['label']))
@@ -277,7 +277,7 @@ class MoneroMMGenTX:
 					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=addr_w, color=True) if addr_w else d.dest_address.hl(),
+					j = d.dest_address.fmt(0, width=addr_w, color=True) if addr_w else d.dest_address.hl(0),
 					x = '->'
 				)
 
@@ -317,8 +317,8 @@ class MoneroMMGenTX:
 					m = d.amount.hl(),
 					F = (Int(d.priority).hl() + f' [{tx_priorities[d.priority]}]') if d.priority else None,
 					n = d.fee.hl(),
-					o = d.dest_address.hl() if self.cfg.full_address
-						else d.dest_address.fmt(width=addr_width, color=True),
+					o = d.dest_address.hl(0) if self.cfg.full_address
+						else d.dest_address.fmt(0, width=addr_width, color=True),
 					P = pink(pmt_id.hex()) if pmt_id else None,
 					s = make_timestr(d.submit_time) if d.submit_time else None,
 					S = pink(f" [cold signed{', submitted' if d.complete else ''}]") if d.signed_txset else '',
@@ -1079,7 +1079,7 @@ class MoneroWalletOps:
 					ca = CoinAddr(self.proto, e['base_address'])
 					yield fs.format(
 						I = str(e['account_index']),
-						A = ca.hl() if self.cfg.full_address else ca.fmt(color=True, width=addr_width),
+						A = ca.hl(0) if self.cfg.full_address else ca.fmt(0, color=True, width=addr_width),
 						N = red(str(len(addrs_data[i]['addresses'])).ljust(6)),
 						B = fmt_amt(e['unlocked_balance']),
 						L = pink(e['label']))
@@ -1831,7 +1831,7 @@ class MoneroWalletOps:
 			ca = CoinAddr(self.proto, addr['address'])
 			msg('\n  {a} {b}\n  {c} {d}\n  {e} {f}'.format(
 					a = 'Address:       ',
-					b = ca.hl() if self.cfg.full_address else ca.fmt(color=True, width=addr_width),
+					b = ca.hl(0) if self.cfg.full_address else ca.fmt(0, color=True, width=addr_width),
 					c = 'Existing label:',
 					d = pink(addr['label']) if addr['label'] else gray('[none]'),
 					e = 'New label:     ',

+ 3 - 2
test/gentest.py

@@ -294,6 +294,7 @@ def do_ab_test(proto,scfg,addr_type,gen1,kg2,ag,tool,cache_data):
 		sec = PrivKey(proto,in_bytes,compressed=addr_type.compressed,pubkey_type=addr_type.pubkey_type)
 		data = kg1.gen_data(sec)
 		addr1 = ag.to_addr(data)
+		view_pref = 0
 		tinfo = ( in_bytes, sec, sec.wif, type(kg1).__name__, type(kg2).__name__ if kg2 else tool.desc )
 
 		def do_msg():
@@ -304,14 +305,14 @@ def do_ab_test(proto,scfg,addr_type,gen1,kg2,ag,tool,cache_data):
 			def run_tool():
 				o = tool.run_tool(sec,cache_data)
 				test_equal( 'WIF keys', sec.wif, o.wif, *tinfo )
-				test_equal( 'addresses', addr1, o.addr, *tinfo )
+				test_equal('addresses', addr1.views[view_pref], o.addr, *tinfo)
 				if o.viewkey:
 					test_equal( 'view keys', ag.to_viewkey(data), o.viewkey, *tinfo )
 				return o.viewkey
 			vk2 = run_tool()
 			do_msg()
 		else:
-			test_equal( 'addresses', addr1, ag.to_addr(kg2.gen_data(sec)), *tinfo )
+			test_equal('addresses', addr1.views[view_pref], ag.to_addr(kg2.gen_data(sec)), *tinfo)
 			vk2 = None
 			do_msg()