From 44da0c8c41cd17580215994f85f502345093b1ef Mon Sep 17 00:00:00 2001 From: Martin Boehm Date: Thu, 27 Sep 2018 13:44:13 +0200 Subject: [PATCH] Show spending transactions in explorer --- api/worker.go | 87 ++++++++++++++++++++++------------ server/public.go | 24 +++++++++- static/templates/txdetail.html | 2 +- 3 files changed, 81 insertions(+), 32 deletions(-) diff --git a/api/worker.go b/api/worker.go index 3aba7a10..2dd6cb89 100644 --- a/api/worker.go +++ b/api/worker.go @@ -44,6 +44,61 @@ func (w *Worker) getAddressesFromVout(vout *bchain.Vout) (bchain.AddressDescript return addrDesc, a, s, err } +// setSpendingTxToVout is helper function, that finds transaction that spent given output and sets it to the output +// there is not an index, it must be found using addresses -> txaddresses -> tx +func (w *Worker) setSpendingTxToVout(vout *Vout, txid string, height, bestheight uint32) error { + err := w.db.GetAddrDescTransactions(vout.ScriptPubKey.AddrDesc, height, ^uint32(0), func(t string, index uint32, isOutput bool) error { + if isOutput == false { + tsp, err := w.db.GetTxAddresses(t) + if err != nil { + glog.Warning("DB inconsistency: tx ", t, ": not found in txAddresses") + } else { + if len(tsp.Inputs) > int(index) { + if tsp.Inputs[index].ValueSat.Cmp(&vout.ValueSat) == 0 { + spentTx, spentHeight, err := w.txCache.GetTransaction(t, bestheight) + if err != nil { + glog.Warning("Tx ", t, ": not found") + } else { + if len(spentTx.Vin) > int(index) { + if spentTx.Vin[index].Txid == txid { + vout.SpentTxID = t + vout.SpentHeight = int(spentHeight) + vout.SpentIndex = int(index) + return &db.StopIteration{} + } + } + } + } + } + } + } + return nil + }) + return err +} + +// GetSpendingTxid returns transaction id of transaction that spent given output +func (w *Worker) GetSpendingTxid(txid string, n int) (string, error) { + start := time.Now() + bestheight, _, err := w.db.GetBestBlock() + if err != nil { + return "", err + } + tx, err := w.GetTransaction(txid, bestheight, false) + if err != nil { + return "", err + } + if n >= len(tx.Vout) || n < 0 { + return "", NewApiError(fmt.Sprintf("Passed incorrect vout index %v for tx %v, len vout %v", n, tx.Txid, len(tx.Vout)), false) + } + err = w.setSpendingTxToVout(&tx.Vout[n], tx.Txid, uint32(tx.Blockheight), bestheight) + if err != nil { + return "", err + } + glog.Info("GetSpendingTxid ", txid, " ", n, " finished in ", time.Since(start)) + return tx.Vout[n].SpentTxID, nil +} + // GetTransaction reads transaction data from txid func (w *Worker) GetTransaction(txid string, bestheight uint32, spendingTxs bool) (*Tx, error) { start := time.Now() @@ -125,37 +180,9 @@ func (w *Worker) GetTransaction(txid string, bestheight uint32, spendingTxs bool if ta != nil { vout.Spent = ta.Outputs[i].Spent if spendingTxs && vout.Spent { - // find transaction that spent this output - // there is not an index, it must be found in addresses -> txaddresses -> tx - err = w.db.GetAddrDescTransactions(vout.ScriptPubKey.AddrDesc, height, ^uint32(0), func(t string, index uint32, isOutput bool) error { - if isOutput == false { - tsp, err := w.db.GetTxAddresses(t) - if err != nil { - glog.Warning("DB inconsistency: tx ", t, ": not found in txAddresses") - } else { - if len(tsp.Inputs) > int(index) { - if tsp.Inputs[index].ValueSat.Cmp(&vout.ValueSat) == 0 { - spentTx, spentHeight, err := w.txCache.GetTransaction(t, bestheight) - if err != nil { - glog.Warning("Tx ", t, ": not found") - } else { - if len(spentTx.Vin) > int(index) { - if spentTx.Vin[index].Txid == bchainTx.Txid { - vout.SpentTxID = t - vout.SpentHeight = int(spentHeight) - vout.SpentIndex = int(index) - return &db.StopIteration{} - } - } - } - } - } - } - } - return nil - }) + err = w.setSpendingTxToVout(vout, bchainTx.Txid, height, bestheight) if err != nil { - glog.Errorf("GetAddrDescTransactions error %v, %v, output %v", err, vout.ScriptPubKey.AddrDesc) + glog.Errorf("setSpendingTxToVout error %v, %v, output %v", err, vout.ScriptPubKey.AddrDesc, vout.N) } } } diff --git a/server/public.go b/server/public.go index eb9258ab..b39aa796 100644 --- a/server/public.go +++ b/server/public.go @@ -115,6 +115,7 @@ func (s *PublicServer) ConnectFullPublicInterface() { serveMux.HandleFunc(path+"search/", s.htmlTemplateHandler(s.explorerSearch)) serveMux.HandleFunc(path+"blocks", s.htmlTemplateHandler(s.explorerBlocks)) serveMux.HandleFunc(path+"block/", s.htmlTemplateHandler(s.explorerBlock)) + serveMux.HandleFunc(path+"spending/", s.htmlTemplateHandler(s.explorerSpendingTx)) } else { // redirect to wallet requests for tx and address, possibly to external site serveMux.HandleFunc(path+"tx/", s.txRedirect) @@ -370,7 +371,7 @@ func (s *PublicServer) explorerTx(w http.ResponseWriter, r *http.Request) (tpl, txid := r.URL.Path[i+1:] bestheight, _, err := s.db.GetBestBlock() if err == nil { - tx, err = s.api.GetTransaction(txid, bestheight, true) + tx, err = s.api.GetTransaction(txid, bestheight, false) } if err != nil { return errorTpl, nil, err @@ -381,6 +382,27 @@ func (s *PublicServer) explorerTx(w http.ResponseWriter, r *http.Request) (tpl, return txTpl, data, nil } +func (s *PublicServer) explorerSpendingTx(w http.ResponseWriter, r *http.Request) (tpl, *TemplateData, error) { + s.metrics.ExplorerViews.With(common.Labels{"action": "spendingtx"}).Inc() + var err error + parts := strings.Split(r.URL.Path, "/") + if len(parts) > 2 { + tx := parts[len(parts)-2] + n, ec := strconv.Atoi(parts[len(parts)-1]) + if ec == nil { + spendingTx, err := s.api.GetSpendingTxid(tx, n) + if err == nil && spendingTx != "" { + http.Redirect(w, r, joinURL("/tx/", spendingTx), 302) + return noTpl, nil, nil + } + } + } + if err == nil { + err = api.NewApiError("Transaction not found", true) + } + return errorTpl, nil, err +} + func (s *PublicServer) explorerAddress(w http.ResponseWriter, r *http.Request) (tpl, *TemplateData, error) { var address *api.Address var err error diff --git a/static/templates/txdetail.html b/static/templates/txdetail.html index 65183ea8..25a893c4 100644 --- a/static/templates/txdetail.html +++ b/static/templates/txdetail.html @@ -59,7 +59,7 @@ Unparsed address {{- end -}} - {{formatAmount $vout.Value}} {{$cs}} {{if $vout.Spent}}{{if $vout.SpentTxID}}{{else}}{{end}}{{else -}} + {{formatAmount $vout.Value}} {{$cs}} {{if $vout.Spent}}{{else -}} × {{- end -}}