CoinAddr: add views, view_pref attrs

This commit is contained in:
The MMGen Project 2024-09-29 11:59:56 +00:00
commit eeb98869d3
Signed by: mmgen
GPG key ID: 3F8B1861E32B7DA2
17 changed files with 53 additions and 39 deletions

View file

@ -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 )

View file

@ -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 ))

View file

@ -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'

View file

@ -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()))

View file

@ -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()

View file

@ -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

View file

@ -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])

View file

@ -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 ))

View file

@ -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):

View file

@ -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' ),

View file

@ -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) )

View file

@ -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 = {

View file

@ -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,

View file

@ -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)

View file

@ -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): ')
),

View file

@ -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: ',

View file

@ -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()