From 463eab9d2d01ef80e9eee817535028f5aed7649d Mon Sep 17 00:00:00 2001 From: Martin Boehm Date: Wed, 28 Nov 2018 14:56:45 +0100 Subject: [PATCH] Show ethereum specific data in tx detail in explorer --- api/worker.go | 5 +- bchain/coins/eth/ethparser.go | 2 +- configs/coins/ethereum-classic.json | 2 +- configs/coins/ethereum.json | 2 +- configs/coins/ethereum_testnet_ropsten.json | 2 +- server/public.go | 21 +++- server/socketio.go | 3 + static/templates/tx.html | 37 +++++- static/templates/txdetail_ethereumtype.html | 129 ++++++++++++++++++++ 9 files changed, 187 insertions(+), 16 deletions(-) create mode 100644 static/templates/txdetail_ethereumtype.html diff --git a/api/worker.go b/api/worker.go index 168b6d00..36a41bbc 100644 --- a/api/worker.go +++ b/api/worker.go @@ -241,7 +241,10 @@ func (w *Worker) GetTransaction(txid string, spendingTxs bool, specificJSON bool } } ethSpecific = eth.GetEthereumTxData(bchainTx) - feesSat.Mul(ethSpecific.GasPriceNum, ethSpecific.GasUsed) + // mempool txs do not have fees yet + if ethSpecific.GasUsed != nil { + feesSat.Mul(ethSpecific.GasPriceNum, ethSpecific.GasUsed) + } if len(bchainTx.Vout) > 0 { valInSat = bchainTx.Vout[0].ValueSat } diff --git a/bchain/coins/eth/ethparser.go b/bchain/coins/eth/ethparser.go index fbaeff15..0256099b 100644 --- a/bchain/coins/eth/ethparser.go +++ b/bchain/coins/eth/ethparser.go @@ -430,7 +430,7 @@ func GetErc20FromTx(tx *bchain.Tx) ([]Erc20Transfer, error) { var r []Erc20Transfer var err error csd, ok := tx.CoinSpecificData.(completeTransaction) - if ok { + if ok && csd.Receipt != nil { r, err = erc20GetTransfersFromLog(csd.Receipt.Logs) if err != nil { return nil, err diff --git a/configs/coins/ethereum-classic.json b/configs/coins/ethereum-classic.json index 7e472a81..fc4e5fa3 100644 --- a/configs/coins/ethereum-classic.json +++ b/configs/coins/ethereum-classic.json @@ -40,7 +40,7 @@ "system_user": "blockbook-ethereum-classic", "internal_binding_template": ":{{.Ports.BlockbookInternal}}", "public_binding_template": ":{{.Ports.BlockbookPublic}}", - "explorer_url": "https://gastracker.io/", + "explorer_url": "", "additional_params": "-resyncindexperiod=4441", "block_chain": { "parse": true, diff --git a/configs/coins/ethereum.json b/configs/coins/ethereum.json index a940a696..2f08fff3 100644 --- a/configs/coins/ethereum.json +++ b/configs/coins/ethereum.json @@ -42,7 +42,7 @@ "system_user": "blockbook-ethereum", "internal_binding_template": ":{{.Ports.BlockbookInternal}}", "public_binding_template": ":{{.Ports.BlockbookPublic}}", - "explorer_url": "https://etherscan.io/", + "explorer_url": "", "additional_params": "", "block_chain": { "parse": true, diff --git a/configs/coins/ethereum_testnet_ropsten.json b/configs/coins/ethereum_testnet_ropsten.json index 82127a88..03d1a6b8 100644 --- a/configs/coins/ethereum_testnet_ropsten.json +++ b/configs/coins/ethereum_testnet_ropsten.json @@ -41,7 +41,7 @@ "system_user": "blockbook-ethereum", "internal_binding_template": ":{{.Ports.BlockbookInternal}}", "public_binding_template": ":{{.Ports.BlockbookPublic}}", - "explorer_url": "https://ropsten.etherscan.io/", + "explorer_url": "", "additional_params": "", "block_chain": { "parse": true, diff --git a/server/public.go b/server/public.go index 4a882cd9..76e065dc 100644 --- a/server/public.go +++ b/server/public.go @@ -14,6 +14,7 @@ import ( "net/http" "reflect" "runtime" + "runtime/debug" "strconv" "strings" "time" @@ -81,7 +82,7 @@ func NewPublicServer(binding string, certFiles string, db *db.RocksDB, chain bch is: is, debug: debugMode, } - s.templates = parseTemplates() + s.templates = s.parseTemplates() // map only basic functions, the rest is enabled by method MapFullPublicInterface serveMux.Handle(path+"favicon.ico", http.FileServer(http.Dir("./static/"))) @@ -202,6 +203,7 @@ func (s *PublicServer) jsonHandler(handler func(r *http.Request) (interface{}, e defer func() { if e := recover(); e != nil { glog.Error(getFunctionName(handler), " recovered from panic: ", e) + debug.PrintStack() if s.debug { data = jsonError{fmt.Sprint("Internal server error: recovered from panic ", e), http.StatusInternalServerError} } else { @@ -264,6 +266,7 @@ func (s *PublicServer) htmlTemplateHandler(handler func(w http.ResponseWriter, r defer func() { if e := recover(); e != nil { glog.Error(getFunctionName(handler), " recovered from panic: ", e) + debug.PrintStack() t = errorInternalTpl if s.debug { data = s.newTemplateDataWithError(fmt.Sprint("Internal server error: recovered from panic ", e)) @@ -286,7 +289,7 @@ func (s *PublicServer) htmlTemplateHandler(handler func(w http.ResponseWriter, r if s.debug { // reload templates on each request // to reflect changes during development - s.templates = parseTemplates() + s.templates = s.parseTemplates() } t, data, err = handler(w, r) if err != nil || (data == nil && t != noTpl) { @@ -349,7 +352,7 @@ type TemplateData struct { Status string } -func parseTemplates() []*template.Template { +func (s *PublicServer) parseTemplates() []*template.Template { templateFuncMap := template.FuncMap{ "formatTime": formatTime, "formatUnixTime": formatUnixTime, @@ -361,11 +364,17 @@ func parseTemplates() []*template.Template { t[errorTpl] = template.Must(template.New("error").Funcs(templateFuncMap).ParseFiles("./static/templates/error.html", "./static/templates/base.html")) t[errorInternalTpl] = template.Must(template.New("error").Funcs(templateFuncMap).ParseFiles("./static/templates/error.html", "./static/templates/base.html")) t[indexTpl] = template.Must(template.New("index").Funcs(templateFuncMap).ParseFiles("./static/templates/index.html", "./static/templates/base.html")) - t[txTpl] = template.Must(template.New("tx").Funcs(templateFuncMap).ParseFiles("./static/templates/tx.html", "./static/templates/txdetail.html", "./static/templates/base.html")) - t[addressTpl] = template.Must(template.New("address").Funcs(templateFuncMap).ParseFiles("./static/templates/address.html", "./static/templates/txdetail.html", "./static/templates/paging.html", "./static/templates/base.html")) t[blocksTpl] = template.Must(template.New("blocks").Funcs(templateFuncMap).ParseFiles("./static/templates/blocks.html", "./static/templates/paging.html", "./static/templates/base.html")) - t[blockTpl] = template.Must(template.New("block").Funcs(templateFuncMap).ParseFiles("./static/templates/block.html", "./static/templates/txdetail.html", "./static/templates/paging.html", "./static/templates/base.html")) t[sendTransactionTpl] = template.Must(template.New("block").Funcs(templateFuncMap).ParseFiles("./static/templates/sendtx.html", "./static/templates/base.html")) + if s.chainParser.GetChainType() == bchain.ChainEthereumType { + t[txTpl] = template.Must(template.New("tx").Funcs(templateFuncMap).ParseFiles("./static/templates/tx.html", "./static/templates/txdetail_ethereumtype.html", "./static/templates/base.html")) + t[addressTpl] = template.Must(template.New("address").Funcs(templateFuncMap).ParseFiles("./static/templates/address.html", "./static/templates/txdetail_ethereumtype.html", "./static/templates/paging.html", "./static/templates/base.html")) + t[blockTpl] = template.Must(template.New("block").Funcs(templateFuncMap).ParseFiles("./static/templates/block.html", "./static/templates/txdetail_ethereumtype.html", "./static/templates/paging.html", "./static/templates/base.html")) + } else { + t[txTpl] = template.Must(template.New("tx").Funcs(templateFuncMap).ParseFiles("./static/templates/tx.html", "./static/templates/txdetail.html", "./static/templates/base.html")) + t[addressTpl] = template.Must(template.New("address").Funcs(templateFuncMap).ParseFiles("./static/templates/address.html", "./static/templates/txdetail.html", "./static/templates/paging.html", "./static/templates/base.html")) + t[blockTpl] = template.Must(template.New("block").Funcs(templateFuncMap).ParseFiles("./static/templates/block.html", "./static/templates/txdetail.html", "./static/templates/paging.html", "./static/templates/base.html")) + } return t } diff --git a/server/socketio.go b/server/socketio.go index 7ea7a432..7a12a0d7 100644 --- a/server/socketio.go +++ b/server/socketio.go @@ -8,6 +8,7 @@ import ( "encoding/json" "math/big" "net/http" + "runtime/debug" "strconv" "strings" "time" @@ -161,6 +162,7 @@ func (s *SocketIoServer) onMessage(c *gosocketio.Channel, req map[string]json.Ra defer func() { if r := recover(); r != nil { glog.Error(c.Id(), " onMessage ", method, " recovered from panic: ", r) + debug.PrintStack() e := resultError{} e.Error.Message = "Internal error" rv = e @@ -664,6 +666,7 @@ func (s *SocketIoServer) onSubscribe(c *gosocketio.Channel, req []byte) interfac defer func() { if r := recover(); r != nil { glog.Error(c.Id(), " onSubscribe recovered from panic: ", r) + debug.PrintStack() } }() diff --git a/static/templates/tx.html b/static/templates/tx.html index 98a5dc10..cea201d3 100644 --- a/static/templates/tx.html +++ b/static/templates/tx.html @@ -7,20 +7,46 @@
- {{if $tx.Confirmations}} + {{- if $tx.Confirmations -}} - {{end}} + {{end -}} - {{if $tx.Confirmations}} + {{- if $tx.Confirmations -}} {{end}} + {{- if $tx.EthereumSpecific -}} + + + {{- if $tx.EthereumSpecific.Status -}} + {{- if ne $tx.EthereumSpecific.Status 1 -}} + + {{- else -}} + + {{- end -}} + {{- else -}} + + {{- end -}} + + + + + + + + + + + + + + {{- else -}} @@ -29,11 +55,12 @@ - {{if $tx.Fees}} + {{- end -}} + {{- if $tx.Fees -}} - {{end}} + {{end -}}
Mined Time {{formatUnixTime $tx.Blocktime}}
In Block {{if $tx.Confirmations}}{{$tx.Blockhash}}{{else}}Unconfirmed{{end}}
In Block Height {{$tx.Blockheight}}
StatusPendingSuccessFail
Value{{formatAmount $tx.ValueOut}} {{$cs}}
Gas Used / Limit{{if $tx.EthereumSpecific.GasUsed}}{{$tx.EthereumSpecific.GasUsed}}{{else}}pending{{end}} / {{$tx.EthereumSpecific.GasLimit}}
Gas Price{{formatAmount $tx.EthereumSpecific.GasPrice}} {{$cs}}
Total Input {{formatAmount $tx.ValueIn}} {{$cs}}Total Output {{formatAmount $tx.ValueOut}} {{$cs}}
Fees {{formatAmount $tx.Fees}} {{$cs}}
diff --git a/static/templates/txdetail_ethereumtype.html b/static/templates/txdetail_ethereumtype.html new file mode 100644 index 00000000..a46cde45 --- /dev/null +++ b/static/templates/txdetail_ethereumtype.html @@ -0,0 +1,129 @@ +{{define "txdetail"}}{{$cs := .CoinShortcut}}{{$addr := .AddrStr}}{{$tx := .Tx}} +
+
+ + {{- if $tx.Confirmations -}} +
mined {{formatUnixTime $tx.Blocktime}}
+ {{- end -}} +
+
+
+
+ + + {{- range $vin := $tx.Vin -}} + + + + {{- else -}} + + + + {{- end -}} + +
+ {{- range $a := $vin.Addresses -}} + + {{if and (ne $a $addr) $vin.Searchable}}{{$a}}{{else}}{{$a}}{{end}} + + {{- else -}} + Unparsed address + {{- end -}} +
No Inputs
+
+
+
+ + + +
+
+
+ + + {{- range $vout := $tx.Vout -}} + + + + {{- else -}} + + + + {{- end -}} + +
+ {{- range $a := $vout.ScriptPubKey.Addresses -}} + + {{- if and (ne $a $addr) $vout.ScriptPubKey.Searchable}}{{$a}}{{else}}{{$a}}{{- end -}} + + {{- else -}} + Unparsed address + {{- end -}} +
No Outputs
+
+
+
+ {{formatAmount $tx.ValueOut}} {{$cs}} +
+
+ {{- if $tx.Erc20Transfers -}} +
+ ERC20 Token Transfers +
+ {{- range $erc20 := $tx.Erc20Transfers -}} +
+
+
+ + + + + + +
+ {{if ne $erc20.From $addr}}{{$erc20.From}}{{else}}{{$erc20.From}}{{end}} +
+
+
+
+ + + +
+
+
+ + + + + + +
+ {{if ne $erc20.To $addr}}{{$erc20.To}}{{else}}{{$erc20.To}}{{end}} +
+
+
+
{{formatAmount $erc20.Tokens}} {{$erc20.Symbol}}
+
+ {{- end -}} +
+ {{- end -}} +
+
+ {{- if $tx.Fees -}} + Fee: {{formatAmount $tx.Fees}} {{$cs}} + {{- end -}} +
+
+ {{- if $tx.Confirmations -}} + {{$tx.Confirmations}} Confirmations + {{- else -}} + Unconfirmed Transaction! + {{- end -}} + {{formatAmount $tx.ValueOut}} {{$cs}} +
+
+
+{{end}} \ No newline at end of file