Highlight xpub addresses in explorer

This commit is contained in:
Martin Boehm 2019-02-04 16:53:49 +01:00
parent 9d3cd3b3e9
commit 266b0575b6
8 changed files with 87 additions and 37 deletions

View File

@ -233,7 +233,9 @@ type Address struct {
TotalTokens int `json:"totalTokens,omitempty"`
Tokens []Token `json:"tokens,omitempty"`
Erc20Contract *bchain.Erc20Contract `json:"erc20contract,omitempty"`
Filter string `json:"-"`
// helpers for explorer
Filter string `json:"-"`
XPubAddresses map[string]struct{} `json:"-"`
}
// AddressUtxo holds information about address and its transactions

View File

@ -235,6 +235,10 @@ func (w *Worker) GetAddressForXpub(xpub string, page int, txsOnPage int, option
}
// gap is increased one as there must be gap of empty addresses before the derivation is stopped
gap++
page--
if page < 0 {
page = 0
}
var processedHash string
cachedXpubsMux.Lock()
data, found := cachedXpubs[xpub]
@ -305,7 +309,7 @@ func (w *Worker) GetAddressForXpub(xpub string, page int, txsOnPage int, option
if option >= TxidHistory {
txc := make(xpubTxids, 0, 32)
var addTxids func(ad *xpubAddress)
if filter.FromHeight != 0 || filter.ToHeight != 0 || filter.Vout != AddressFilterVoutOff {
if filter.FromHeight == 0 && filter.ToHeight == 0 && filter.Vout == AddressFilterVoutOff {
addTxids = func(ad *xpubAddress) {
txc = append(txc, ad.txids...)
}
@ -372,13 +376,21 @@ func (w *Worker) GetAddressForXpub(xpub string, page int, txsOnPage int, option
}
}
totalTokens := 0
xpubAddresses := make(map[string]struct{})
tokens := make([]Token, 0, 4)
for i := range data.addresses {
ad := &data.addresses[i]
if ad.balance != nil {
totalTokens++
if filter.AllTokens || !IsZeroBigInt(&ad.balance.BalanceSat) {
tokens = append(tokens, w.tokenFromXpubAddress(ad, 0, i))
t := w.tokenFromXpubAddress(ad, 0, i)
tokens = append(tokens, t)
xpubAddresses[t.Name] = struct{}{}
} else {
a, _, _ := w.chainParser.GetAddressesFromAddrDesc(ad.addrDesc)
if len(a) > 0 {
xpubAddresses[a[0]] = struct{}{}
}
}
}
}
@ -387,7 +399,14 @@ func (w *Worker) GetAddressForXpub(xpub string, page int, txsOnPage int, option
if ad.balance != nil {
totalTokens++
if filter.AllTokens || !IsZeroBigInt(&ad.balance.BalanceSat) {
tokens = append(tokens, w.tokenFromXpubAddress(ad, 1, i))
t := w.tokenFromXpubAddress(ad, 1, i)
tokens = append(tokens, t)
xpubAddresses[t.Name] = struct{}{}
} else {
a, _, _ := w.chainParser.GetAddressesFromAddrDesc(ad.addrDesc)
if len(a) > 0 {
xpubAddresses[a[0]] = struct{}{}
}
}
}
}
@ -402,10 +421,11 @@ func (w *Worker) GetAddressForXpub(xpub string, page int, txsOnPage int, option
Txs: int(data.txs),
// UnconfirmedBalanceSat: (*Amount)(&uBalSat),
// UnconfirmedTxs: len(txm),
Transactions: txs,
Txids: txids,
TotalTokens: totalTokens,
Tokens: tokens,
Transactions: txs,
Txids: txids,
TotalTokens: totalTokens,
Tokens: tokens,
XPubAddresses: xpubAddresses,
}
glog.Info("GetAddressForXpub ", xpub[:16], ", ", len(data.addresses)+len(data.changeAddresses), " derived addresses, ", data.txs, " total txs finished in ", time.Since(start))
return &addr, nil

View File

@ -415,7 +415,7 @@ func (s *PublicServer) parseTemplates() []*template.Template {
"formatAmount": s.formatAmount,
"formatAmountWithDecimals": formatAmountWithDecimals,
"setTxToTemplateData": setTxToTemplateData,
"stringInSlice": stringInSlice,
"isOwnAddress": isOwnAddress,
}
var createTemplate func(filenames ...string) *template.Template
if s.debug {
@ -504,6 +504,23 @@ func setTxToTemplateData(td *TemplateData, tx *api.Tx) *TemplateData {
return td
}
// returns true if addresses are "own",
// i.e. either the address of the address detail or belonging to the xpub
func isOwnAddress(td *TemplateData, addresses []string) bool {
if len(addresses) == 1 {
a := addresses[0]
if a == td.AddrStr {
return true
}
if td.Address != nil && td.Address.XPubAddresses != nil {
if _, found := td.Address.XPubAddresses[a]; found {
return true
}
}
}
return false
}
func (s *PublicServer) explorerTx(w http.ResponseWriter, r *http.Request) (tpl, *TemplateData, error) {
var tx *api.Tx
var err error

View File

@ -151,7 +151,7 @@ func httpTests_BitcoinType(t *testing.T, ts *httptest.Server) {
`td class="data">0 FAKE</td>`,
`<a href="/address/mzVznVsCHkVHX9UN8WPFASWUUHtxnNn4Jj">mzVznVsCHkVHX9UN8WPFASWUUHtxnNn4Jj</a>`,
`13.60030331 FAKE`,
`<td><span class="float-left">No Inputs (Newly Generated Coins)</span></td>`,
`<td><span class="tx-addr">No Inputs (Newly Generated Coins)</span></td>`,
`</html>`,
},
},
@ -168,8 +168,8 @@ func httpTests_BitcoinType(t *testing.T, ts *httptest.Server) {
`<td class="data">0.00012345 FAKE</td>`,
`<a href="/tx/7c3be24063f268aaa1ed81b64776798f56088757641a34fb156c4f51ed2e9d25">7c3be24063f268aaa1ed81b64776798f56088757641a34fb156c4f51ed2e9d25</a>`,
`3172.83951061 FAKE <a class="text-danger" href="/spending/7c3be24063f268aaa1ed81b64776798f56088757641a34fb156c4f51ed2e9d25/0" title="Spent">➡</a></span>`,
`<td><span class="ellipsis float-left"><a href="/address/mtR97eM2HPWVM6c8FGLGcukgaHHQv7THoL">mtR97eM2HPWVM6c8FGLGcukgaHHQv7THoL</a></span><span class="float-right">`,
`td><span class="ellipsis float-left"><a href="/address/mtR97eM2HPWVM6c8FGLGcukgaHHQv7THoL">mtR97eM2HPWVM6c8FGLGcukgaHHQv7THoL</a></span><span class="float-right">`,
`<td><span class="ellipsis tx-addr"><a href="/address/mtR97eM2HPWVM6c8FGLGcukgaHHQv7THoL">mtR97eM2HPWVM6c8FGLGcukgaHHQv7THoL</a></span><span class="tx-amt">`,
`td><span class="ellipsis tx-addr"><a href="/address/mtR97eM2HPWVM6c8FGLGcukgaHHQv7THoL">mtR97eM2HPWVM6c8FGLGcukgaHHQv7THoL</a></span><span class="tx-amt">`,
`9172.83951061 FAKE <span class="text-success" title="Unspent"> <b>×</b></span></span>`,
`<a href="/tx/00b2c06055e5e90e9c82bd4181fde310104391a7fa4f289b1704e5d90caa3840">00b2c06055e5e90e9c82bd4181fde310104391a7fa4f289b1704e5d90caa3840</a>`,
`</html>`,
@ -290,7 +290,7 @@ func httpTests_BitcoinType(t *testing.T, ts *httptest.Server) {
`td class="data">0 FAKE</td>`,
`<a href="/address/mzVznVsCHkVHX9UN8WPFASWUUHtxnNn4Jj">mzVznVsCHkVHX9UN8WPFASWUUHtxnNn4Jj</a>`,
`13.60030331 FAKE`,
`<td><span class="float-left">No Inputs (Newly Generated Coins)</span></td>`,
`<td><span class="tx-addr">No Inputs (Newly Generated Coins)</span></td>`,
`</html>`,
},
},
@ -307,8 +307,8 @@ func httpTests_BitcoinType(t *testing.T, ts *httptest.Server) {
`<td class="data">0.00012345 FAKE</td>`,
`<a href="/tx/7c3be24063f268aaa1ed81b64776798f56088757641a34fb156c4f51ed2e9d25">7c3be24063f268aaa1ed81b64776798f56088757641a34fb156c4f51ed2e9d25</a>`,
`3172.83951061 FAKE <a class="text-danger" href="/spending/7c3be24063f268aaa1ed81b64776798f56088757641a34fb156c4f51ed2e9d25/0" title="Spent">➡</a></span>`,
`<td><span class="ellipsis float-left"><a href="/address/mtR97eM2HPWVM6c8FGLGcukgaHHQv7THoL">mtR97eM2HPWVM6c8FGLGcukgaHHQv7THoL</a></span><span class="float-right">`,
`td><span class="ellipsis float-left"><a href="/address/mtR97eM2HPWVM6c8FGLGcukgaHHQv7THoL">mtR97eM2HPWVM6c8FGLGcukgaHHQv7THoL</a></span><span class="float-right">`,
`<td><span class="ellipsis tx-addr"><a href="/address/mtR97eM2HPWVM6c8FGLGcukgaHHQv7THoL">mtR97eM2HPWVM6c8FGLGcukgaHHQv7THoL</a></span><span class="tx-amt">`,
`td><span class="ellipsis tx-addr"><a href="/address/mtR97eM2HPWVM6c8FGLGcukgaHHQv7THoL">mtR97eM2HPWVM6c8FGLGcukgaHHQv7THoL</a></span><span class="tx-amt">`,
`9172.83951061 FAKE <span class="text-success" title="Unspent"> <b>×</b></span></span>`,
`<a href="/tx/00b2c06055e5e90e9c82bd4181fde310104391a7fa4f289b1704e5d90caa3840">00b2c06055e5e90e9c82bd4181fde310104391a7fa4f289b1704e5d90caa3840</a>`,
`</html>`,

View File

@ -292,15 +292,6 @@ type resultGetAddressHistory struct {
} `json:"result"`
}
func stringInSlice(a string, list []string) bool {
for _, b := range list {
if b == a {
return true
}
}
return false
}
func txToResTx(tx *api.Tx) resTx {
inputs := make([]txInputs, len(tx.Vin))
for i := range tx.Vin {

View File

@ -182,6 +182,26 @@ h3 {
text-transform: uppercase;
}
.tx-own {
background-color: #fff6e6;
}
.tx-amt {
float: right!important;
}
.tx-in .tx-own .tx-amt {
color: #dc3545!important;
}
.tx-out .tx-own .tx-amt {
color: #28a745!important;
}
.tx-addr {
float: left!important;
}
.ellipsis {
overflow: hidden;
text-overflow: ellipsis;
@ -283,5 +303,5 @@ table.data-table table.data-table th {
}
.key {
color: #333 ;
color: #333;
}

View File

@ -1,4 +1,4 @@
{{define "txdetail"}}{{$cs := .CoinShortcut}}{{$addr := .AddrStr}}{{$tx := .Tx}}
{{define "txdetail"}}{{$cs := .CoinShortcut}}{{$addr := .AddrStr}}{{$tx := .Tx}}{{$data := .}}
<div class="alert alert-data">
<div class="row line-bot">
<div class="col-xs-7 col-md-8 ellipsis">
@ -10,23 +10,23 @@
</div>
<div class="row line-mid">
<div class="col-md-5">
<div class="row">
<div class="row tx-in">
<table class="table data-table">
<tbody>
{{- range $vin := $tx.Vin -}}
<tr>
<tr{{if isOwnAddress $data $vin.Addresses}} class="tx-own"{{end}}>
<td>
{{- if $vin.Txid -}}
<a class="float-left text-muted" href="/tx/{{$vin.Txid}}" title="Outpoint {{$vin.Txid}},{{$vin.Vout}}">&nbsp;</a>
{{- end -}}
{{- range $a := $vin.Addresses -}}
<span class="ellipsis float-left">
<span class="ellipsis tx-addr">
{{if and (ne $a $addr) $vin.Searchable}}<a href="/address/{{$a}}">{{$a}}</a>{{else}}{{$a}}{{end}}
</span>
{{- else -}}
<span class="float-left">{{- if $vin.Hex -}}Unparsed address{{- else -}}No Inputs (Newly Generated Coins){{- end -}}</span>
<span class="tx-addr">{{- if $vin.Hex -}}Unparsed address{{- else -}}No Inputs (Newly Generated Coins){{- end -}}</span>
{{- end -}}{{- if $vin.Addresses -}}
<span class="float-right{{if stringInSlice $addr $vin.Addresses}} text-danger{{end}}">{{formatAmount $vin.ValueSat}} {{$cs}}</span>
<span class="tx-amt">{{formatAmount $vin.ValueSat}} {{$cs}}</span>
{{- end -}}
</td>
</tr>
@ -45,20 +45,20 @@
</svg>
</div>
<div class="col-md-6">
<div class="row">
<div class="row tx-out">
<table class="table data-table">
<tbody>
{{- range $vout := $tx.Vout -}}
<tr>
<tr{{if isOwnAddress $data $vout.Addresses}} class="tx-own"{{end}}>
<td>
{{- range $a := $vout.Addresses -}}
<span class="ellipsis float-left">
<span class="ellipsis tx-addr">
{{- if and (ne $a $addr) $vout.Searchable}}<a href="/address/{{$a}}">{{$a}}</a>{{else}}{{$a}}{{- end -}}
</span>
{{- else -}}
<span class="float-left">Unparsed address</span>
<span class="tx-addr">Unparsed address</span>
{{- end -}}
<span class="float-right{{if stringInSlice $addr $vout.Addresses}} text-success{{end}}">
<span class="tx-amt">
{{formatAmount $vout.ValueSat}} {{$cs}} {{if $vout.Spent}}<a class="text-danger" href="{{if $vout.SpentTxID}}/tx/{{$vout.SpentTxID}}{{else}}/spending/{{$tx.Txid}}/{{$vout.N}}{{end}}" title="Spent"></a>{{else -}}
<span class="text-success" title="Unspent"> <b>×</b></span>
{{- end -}}

View File

@ -29,7 +29,7 @@
<td>Total XPUB Addresses</td>
<td class="data">{{$addr.TotalTokens}}</td>
</tr>
{{- if $addr.Tokens -}}
{{- if $addr.TotalTokens -}}
<tr>
<td>{{if $data.AllTokens}}XPUB Addresses{{else}}XPUB Addresses with Balance{{end}}</td>
<td style="padding: 0;">