Show mempool content in explorer
This commit is contained in:
parent
f2dc4a56d8
commit
987aec47f9
13
api/types.go
13
api/types.go
@ -337,3 +337,16 @@ type SystemInfo struct {
|
||||
Blockbook *BlockbookInfo `json:"blockbook"`
|
||||
Backend *bchain.ChainInfo `json:"backend"`
|
||||
}
|
||||
|
||||
// MempoolTxid contains information about a transaction in mempool
|
||||
type MempoolTxid struct {
|
||||
Time int64 `json:"time"`
|
||||
Txid string `json:"txid"`
|
||||
}
|
||||
|
||||
// MempoolTxids contains a list of mempool txids with paging information
|
||||
type MempoolTxids struct {
|
||||
Paging
|
||||
Mempool []MempoolTxid `json:"mempool"`
|
||||
MempoolSize int `json:"mempoolSize"`
|
||||
}
|
||||
|
||||
@ -1070,3 +1070,26 @@ func (w *Worker) GetSystemInfo(internal bool) (*SystemInfo, error) {
|
||||
glog.Info("GetSystemInfo finished in ", time.Since(start))
|
||||
return &SystemInfo{bi, ci}, nil
|
||||
}
|
||||
|
||||
// GetMempool returns a page of mempool txids
|
||||
func (w *Worker) GetMempool(page int, itemsOnPage int) (*MempoolTxids, error) {
|
||||
page--
|
||||
if page < 0 {
|
||||
page = 0
|
||||
}
|
||||
entries := w.mempool.GetAllEntries()
|
||||
pg, from, to, page := computePaging(len(entries), page, itemsOnPage)
|
||||
r := &MempoolTxids{
|
||||
Paging: pg,
|
||||
MempoolSize: len(entries),
|
||||
}
|
||||
r.Mempool = make([]MempoolTxid, to-from)
|
||||
for i := from; i < to; i++ {
|
||||
entry := &entries[i]
|
||||
r.Mempool[i-from] = MempoolTxid{
|
||||
Txid: entry.Txid,
|
||||
Time: int64(entry.Time),
|
||||
}
|
||||
}
|
||||
return r, nil
|
||||
}
|
||||
|
||||
@ -311,3 +311,8 @@ func (c *mempoolWithMetrics) GetAddrDescTransactions(addrDesc bchain.AddressDesc
|
||||
defer func(s time.Time) { c.observeRPCLatency("GetMempoolTransactionsForAddrDesc", s, err) }(time.Now())
|
||||
return c.mempool.GetAddrDescTransactions(addrDesc)
|
||||
}
|
||||
|
||||
func (c *mempoolWithMetrics) GetAllEntries() (v bchain.MempoolTxidEntries) {
|
||||
defer func(s time.Time) { c.observeRPCLatency("GetAllEntries", s, nil) }(time.Now())
|
||||
return c.mempool.GetAllEntries()
|
||||
}
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
package bchain
|
||||
|
||||
import (
|
||||
"sort"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
@ -221,3 +222,35 @@ func (m *MempoolBitcoinType) Resync() (int, error) {
|
||||
glog.Info("mempool: resync finished in ", time.Since(start), ", ", len(m.txEntries), " transactions in mempool")
|
||||
return len(m.txEntries), nil
|
||||
}
|
||||
|
||||
func (a MempoolTxidEntries) Len() int { return len(a) }
|
||||
func (a MempoolTxidEntries) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
|
||||
func (a MempoolTxidEntries) Less(i, j int) bool {
|
||||
// if the Time is equal, sort by txid to make the order defined
|
||||
hi := a[i].Time
|
||||
hj := a[j].Time
|
||||
if hi == hj {
|
||||
return a[i].Txid > a[j].Txid
|
||||
}
|
||||
// order in reverse
|
||||
return hi > hj
|
||||
}
|
||||
|
||||
func getAllEntries(txEntries map[string]txEntry) MempoolTxidEntries {
|
||||
a := make(MempoolTxidEntries, len(txEntries))
|
||||
i := 0
|
||||
for txid, entry := range txEntries {
|
||||
a[i] = MempoolTxidEntry{
|
||||
Txid: txid,
|
||||
Time: entry.time,
|
||||
}
|
||||
i++
|
||||
}
|
||||
sort.Sort(a)
|
||||
return a
|
||||
}
|
||||
|
||||
// GetAllEntries returns all mempool entries sorted by fist seen time in descending order
|
||||
func (m *MempoolBitcoinType) GetAllEntries() MempoolTxidEntries {
|
||||
return getAllEntries(m.txEntries)
|
||||
}
|
||||
|
||||
@ -137,3 +137,8 @@ func (m *MempoolEthereumType) Resync() (int, error) {
|
||||
glog.Info("Mempool: resync finished in ", time.Since(start), ", ", len(m.txEntries), " transactions in mempool")
|
||||
return len(m.txEntries), nil
|
||||
}
|
||||
|
||||
// GetAllEntries returns all mempool entries sorted by fist seen time in descending order
|
||||
func (m *MempoolEthereumType) GetAllEntries() MempoolTxidEntries {
|
||||
return getAllEntries(m.txEntries)
|
||||
}
|
||||
|
||||
@ -185,6 +185,15 @@ type Erc20Transfer struct {
|
||||
Tokens big.Int
|
||||
}
|
||||
|
||||
// MempoolTxidEntry contains mempool txid with first seen time
|
||||
type MempoolTxidEntry struct {
|
||||
Txid string
|
||||
Time uint32
|
||||
}
|
||||
|
||||
// MempoolTxidEntries is array of MempoolTxidEntry
|
||||
type MempoolTxidEntries []MempoolTxidEntry
|
||||
|
||||
// OnNewBlockFunc is used to send notification about a new block
|
||||
type OnNewBlockFunc func(hash string, height uint32)
|
||||
|
||||
@ -281,4 +290,5 @@ type Mempool interface {
|
||||
Resync() (int, error)
|
||||
GetTransactions(address string) ([]Outpoint, error)
|
||||
GetAddrDescTransactions(addrDesc AddressDescriptor) ([]Outpoint, error)
|
||||
GetAllEntries() MempoolTxidEntries
|
||||
}
|
||||
|
||||
@ -26,6 +26,7 @@ import (
|
||||
|
||||
const txsOnPage = 25
|
||||
const blocksOnPage = 50
|
||||
const mempoolTxsOnPage = 50
|
||||
const txsInAPI = 1000
|
||||
|
||||
const (
|
||||
@ -139,6 +140,7 @@ func (s *PublicServer) ConnectFullPublicInterface() {
|
||||
serveMux.HandleFunc(path+"block/", s.htmlTemplateHandler(s.explorerBlock))
|
||||
serveMux.HandleFunc(path+"spending/", s.htmlTemplateHandler(s.explorerSpendingTx))
|
||||
serveMux.HandleFunc(path+"sendtx", s.htmlTemplateHandler(s.explorerSendTx))
|
||||
serveMux.HandleFunc(path+"mempool", s.htmlTemplateHandler(s.explorerMempool))
|
||||
} else {
|
||||
// redirect to wallet requests for tx and address, possibly to external site
|
||||
serveMux.HandleFunc(path+"tx/", s.txRedirect)
|
||||
@ -384,6 +386,7 @@ const (
|
||||
blocksTpl
|
||||
blockTpl
|
||||
sendTransactionTpl
|
||||
mempoolTpl
|
||||
|
||||
tplCount
|
||||
)
|
||||
@ -402,6 +405,7 @@ type TemplateData struct {
|
||||
Blocks *api.Blocks
|
||||
Block *api.Block
|
||||
Info *api.SystemInfo
|
||||
MempoolTxids *api.MempoolTxids
|
||||
Page int
|
||||
PrevPage int
|
||||
NextPage int
|
||||
@ -477,6 +481,7 @@ func (s *PublicServer) parseTemplates() []*template.Template {
|
||||
t[blockTpl] = createTemplate("./static/templates/block.html", "./static/templates/txdetail.html", "./static/templates/paging.html", "./static/templates/base.html")
|
||||
}
|
||||
t[xpubTpl] = createTemplate("./static/templates/xpub.html", "./static/templates/txdetail.html", "./static/templates/paging.html", "./static/templates/base.html")
|
||||
t[mempoolTpl] = createTemplate("./static/templates/mempool.html", "./static/templates/paging.html", "./static/templates/base.html")
|
||||
return t
|
||||
}
|
||||
|
||||
@ -798,6 +803,25 @@ func (s *PublicServer) explorerSendTx(w http.ResponseWriter, r *http.Request) (t
|
||||
return sendTransactionTpl, data, nil
|
||||
}
|
||||
|
||||
func (s *PublicServer) explorerMempool(w http.ResponseWriter, r *http.Request) (tpl, *TemplateData, error) {
|
||||
var mempoolTxids *api.MempoolTxids
|
||||
var err error
|
||||
s.metrics.ExplorerViews.With(common.Labels{"action": "mempool"}).Inc()
|
||||
page, ec := strconv.Atoi(r.URL.Query().Get("page"))
|
||||
if ec != nil {
|
||||
page = 0
|
||||
}
|
||||
mempoolTxids, err = s.api.GetMempool(page, mempoolTxsOnPage)
|
||||
if err != nil {
|
||||
return errorTpl, nil, err
|
||||
}
|
||||
data := s.newTemplateData()
|
||||
data.MempoolTxids = mempoolTxids
|
||||
data.Page = mempoolTxids.Page
|
||||
data.PagingRange, data.PrevPage, data.NextPage = getPagingRange(mempoolTxids.Page, mempoolTxids.TotalPages)
|
||||
return mempoolTpl, data, nil
|
||||
}
|
||||
|
||||
func getPagingRange(page int, total int) ([]int, int, int) {
|
||||
// total==-1 means total is unknown, show only prev/next buttons
|
||||
if total >= 0 && total < 2 {
|
||||
|
||||
@ -268,6 +268,11 @@ table.data-table table.data-table th {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.h-container h5 {
|
||||
margin-top: 6px;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.page-link {
|
||||
color: #428bca;
|
||||
}
|
||||
|
||||
@ -43,6 +43,10 @@
|
||||
<td>Last Mempool Update</td>
|
||||
<td class="data">{{formatTime $bb.LastMempoolTime}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Transactions in Mempool</td>
|
||||
<td class="data">{{if .InternalExplorer}}<a href="/mempool">{{$bb.MempoolSize}}</a>{{else}}{{$bb.MempoolSize}}{{end}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Size On Disk</td>
|
||||
<td class="data">{{$bb.DbSize}}</td>
|
||||
|
||||
27
static/templates/mempool.html
Normal file
27
static/templates/mempool.html
Normal file
@ -0,0 +1,27 @@
|
||||
{{define "specific"}}{{$txs := .MempoolTxids.Mempool}}{{$data := .}}
|
||||
<h1>Mempool Transactions <small class="text-muted">by first seen time</small>
|
||||
</h1>
|
||||
<div class="row h-container">
|
||||
<h5 class="col-md-6 col-sm-12">{{$.MempoolTxids.MempoolSize}} Transactions in mempool</h5>
|
||||
<nav class="col-md-6 col-sm-12">{{template "paging" $data }}</nav>
|
||||
</div>
|
||||
<div class="data-div">
|
||||
<table class="table table-striped data-table table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="width: 70%;">Transaction</th>
|
||||
<th style="width: 30%;">First Seen Time</span></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{{- range $tx := $txs -}}
|
||||
<tr>
|
||||
<td class="ellipsis"><a href="/tx/{{$tx.Txid}}">{{$tx.Txid}}</a></td>
|
||||
<td>{{formatUnixTime $tx.Time}}</td>
|
||||
</tr>
|
||||
{{- end -}}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<nav>{{template "paging" $data }}</nav>
|
||||
{{end}}
|
||||
Loading…
Reference in New Issue
Block a user