Show coin specific transaction data in explorer and api

pull/76/head
Martin Boehm 2018-10-16 12:25:31 +02:00
parent 71969ebd23
commit 8140af1a69
7 changed files with 99 additions and 8 deletions

View File

@ -180,6 +180,11 @@ func (c *blockChainWithMetrics) GetTransaction(txid string) (v *bchain.Tx, err e
return c.b.GetTransaction(txid)
}
func (c *blockChainWithMetrics) GetTransactionSpecific(txid string) (v json.RawMessage, err error) {
defer func(s time.Time) { c.observeRPCLatency("GetTransactionSpecific", s, err) }(time.Now())
return c.b.GetTransactionSpecific(txid)
}
func (c *blockChainWithMetrics) GetTransactionForMempool(txid string) (v *bchain.Tx, err error) {
defer func(s time.Time) { c.observeRPCLatency("GetTransactionForMempool", s, err) }(time.Now())
return c.b.GetTransactionForMempool(txid)

View File

@ -670,6 +670,19 @@ func (b *BitcoinRPC) GetTransactionForMempool(txid string) (*bchain.Tx, error) {
// GetTransaction returns a transaction by the transaction ID.
func (b *BitcoinRPC) GetTransaction(txid string) (*bchain.Tx, error) {
r, err := b.GetTransactionSpecific(txid)
if err != nil {
return nil, err
}
tx, err := b.Parser.ParseTxFromJson(r)
if err != nil {
return nil, errors.Annotatef(err, "txid %v", txid)
}
return tx, nil
}
// GetTransactionSpecific returns json as returned by backend, with all coin specific data
func (b *BitcoinRPC) GetTransactionSpecific(txid string) (json.RawMessage, error) {
glog.V(1).Info("rpc: getrawtransaction ", txid)
res := ResGetRawTransaction{}
@ -684,11 +697,7 @@ func (b *BitcoinRPC) GetTransaction(txid string) (*bchain.Tx, error) {
if res.Error != nil {
return nil, errors.Annotatef(res.Error, "txid %v", txid)
}
tx, err := b.Parser.ParseTxFromJson(res.Result)
if err != nil {
return nil, errors.Annotatef(err, "txid %v", txid)
}
return tx, nil
return res.Result, nil
}
// ResyncMempool gets mempool transactions and maps output scripts to transactions.

View File

@ -491,6 +491,20 @@ func (b *EthereumRPC) GetTransaction(txid string) (*bchain.Tx, error) {
return btx, nil
}
// GetTransactionSpecific returns json as returned by backend, with all coin specific data
func (b *EthereumRPC) GetTransactionSpecific(txid string) (json.RawMessage, error) {
ctx, cancel := context.WithTimeout(context.Background(), b.timeout)
defer cancel()
var tx json.RawMessage
err := b.rpc.CallContext(ctx, &tx, "eth_getTransactionByHash", ethcommon.HexToHash(txid))
if err != nil {
return nil, err
} else if tx == nil {
return nil, ethereum.NotFound
}
return tx, nil
}
type rpcMempoolBlock struct {
Transactions []string `json:"transactions"`
}

View File

@ -167,6 +167,7 @@ type BlockChain interface {
GetMempool() ([]string, error)
GetTransaction(txid string) (*Tx, error)
GetTransactionForMempool(txid string) (*Tx, error)
GetTransactionSpecific(txid string) (json.RawMessage, error)
EstimateSmartFee(blocks int, conservative bool) (big.Int, error)
EstimateFee(blocks int) (big.Int, error)
SendRawTransaction(tx string) (string, error)

View File

@ -124,6 +124,7 @@ func (s *PublicServer) ConnectFullPublicInterface() {
// API calls
serveMux.HandleFunc(path+"api/block-index/", s.jsonHandler(s.apiBlockIndex))
serveMux.HandleFunc(path+"api/tx/", s.jsonHandler(s.apiTx))
serveMux.HandleFunc(path+"api/tx-specific/", s.jsonHandler(s.apiTxSpecific))
serveMux.HandleFunc(path+"api/address/", s.jsonHandler(s.apiAddress))
serveMux.HandleFunc(path+"api/block/", s.jsonHandler(s.apiBlock))
// socket.io interface
@ -318,6 +319,7 @@ type TemplateData struct {
Address *api.Address
AddrStr string
Tx *api.Tx
TxSpecific json.RawMessage
Error *api.ApiError
Blocks *api.Blocks
Block *api.Block
@ -370,6 +372,7 @@ func setTxToTemplateData(td *TemplateData, tx *api.Tx) *TemplateData {
func (s *PublicServer) explorerTx(w http.ResponseWriter, r *http.Request) (tpl, *TemplateData, error) {
var tx *api.Tx
var txSpecific json.RawMessage
var err error
s.metrics.ExplorerViews.With(common.Labels{"action": "tx"}).Inc()
if i := strings.LastIndexByte(r.URL.Path, '/'); i > 0 {
@ -378,9 +381,14 @@ func (s *PublicServer) explorerTx(w http.ResponseWriter, r *http.Request) (tpl,
if err != nil {
return errorTpl, nil, err
}
txSpecific, err = s.chain.GetTransactionSpecific(txid)
if err != nil {
return errorTpl, nil, err
}
}
data := s.newTemplateData()
data.Tx = tx
data.TxSpecific = txSpecific
return txTpl, data, nil
}
@ -603,6 +611,17 @@ func (s *PublicServer) apiTx(r *http.Request) (interface{}, error) {
return tx, err
}
func (s *PublicServer) apiTxSpecific(r *http.Request) (interface{}, error) {
var tx json.RawMessage
var err error
s.metrics.ExplorerViews.With(common.Labels{"action": "api-tx-specific"}).Inc()
if i := strings.LastIndexByte(r.URL.Path, '/'); i > 0 {
txid := r.URL.Path[i+1:]
tx, err = s.chain.GetTransactionSpecific(txid)
}
return tx, err
}
func (s *PublicServer) apiAddress(r *http.Request) (interface{}, error) {
var address *api.Address
var err error

View File

@ -271,4 +271,24 @@ h3 {
.page-item.active .page-link {
background-color: #428bca;
}
#txSpecific {
margin: 0;
}
.string {
color: darkgreen;
}
.number, .boolean {
color: darkred;
}
.null {
color: red;
}
.key {
color: #333 ;
}

View File

@ -1,4 +1,4 @@
{{define "specific"}}{{$cs := .CoinShortcut}}{{$tx := .Tx}}
{{define "specific"}}{{$cs := .CoinShortcut}}{{$tx := .Tx}}{{$txSpecific := .TxSpecific}}
<h1>Transaction</h1>
<div class="alert alert-data ellipsis">
<span class="data">{{$tx.Txid}}</span>
@ -42,9 +42,32 @@
{{template "txdetail" .}}
</div>
<div class="data-div">
<h5>Hex</h5>
<h5>Transaction Data</h5>
<div class="alert alert-data" style="word-wrap: break-word; font-size: smaller;">
<span>{{$tx.Hex}}</span>
<pre id="txSpecific"></pre>
</div>
<script type="text/javascript">
txSpecific = {{ $txSpecific }};
function syntaxHighlight(json) {
json = JSON.stringify(json, undefined, 2);
json = json.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
return json.replace(/("(\\u[a-zA-Z0-9]{4}|\\[^u]|[^\\"])*"(\s*:)?|\b(true|false|null)\b|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?)/g, function (match) {
var cls = 'number';
if (/^"/.test(match)) {
if (/:$/.test(match)) {
cls = 'key';
} else {
cls = 'string';
}
} else if (/true|false/.test(match)) {
cls = 'boolean';
} else if (/null/.test(match)) {
cls = 'null';
}
return '<span class="' + cls + '">' + match + '</span>';
});
}
document.getElementById('txSpecific').innerHTML = syntaxHighlight(txSpecific);
</script>
</div>
{{end}}