This commit is contained in:
parent
98f1ccbfdc
commit
1ec586f409
@ -15,7 +15,7 @@ go get -u github.com/gchaincl/mempool
|
||||
```
|
||||
|
||||
# TODO
|
||||
- [ ] Transaction Tracking (by Tx ID)
|
||||
- [x] Transaction Tracking (by Tx ID) (using 'f' key)
|
||||
- [x] Block details on click
|
||||
- [ ] Graphs
|
||||
- [ ] Tx weight per second progress bar
|
||||
|
||||
@ -44,6 +44,17 @@ type ProjectedBlock struct {
|
||||
HasMyTx bool `json:"hasMytx"`
|
||||
}
|
||||
|
||||
type TrackTx struct {
|
||||
Tracking bool `json:"tracking"`
|
||||
BlockHeight int `json:"blockHeight"`
|
||||
Message string `json:"message"`
|
||||
TX struct {
|
||||
Status struct {
|
||||
Confirmed bool
|
||||
}
|
||||
} `json:"tx"`
|
||||
}
|
||||
|
||||
type Response struct {
|
||||
MempoolInfo *MempoolInfo `json:"mempoolInfo"`
|
||||
|
||||
@ -51,6 +62,7 @@ type Response struct {
|
||||
Blocks []Block `json:"blocks"`
|
||||
|
||||
ProjectedBlocks []ProjectedBlock `json:"projectedBlocks"`
|
||||
TrackTx TrackTx `json:"track-tx"`
|
||||
|
||||
TxPerSecond float64 `json:"txPerSecond"`
|
||||
VBytesPerSecond int `json:"vBytesPerSecond"`
|
||||
@ -70,12 +82,6 @@ func New() (*Client, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := conn.WriteMessage(websocket.TextMessage, []byte(
|
||||
`{"action":"want","data":["stats","blocks","projected-blocks"]}`,
|
||||
)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &Client{conn: conn}, nil
|
||||
}
|
||||
|
||||
@ -87,6 +93,18 @@ func (c *Client) Read() (*Response, error) {
|
||||
return &resp, nil
|
||||
}
|
||||
|
||||
func (c *Client) Want() error {
|
||||
return c.conn.WriteMessage(websocket.TextMessage, []byte(
|
||||
`{"action":"want","data":["stats","blocks","projected-blocks"]}`,
|
||||
))
|
||||
}
|
||||
|
||||
func (c *Client) Track(txId string) error {
|
||||
return c.conn.WriteMessage(websocket.TextMessage, []byte(
|
||||
fmt.Sprintf(`{"action":"track-tx","txId":"%s"}`, txId),
|
||||
))
|
||||
}
|
||||
|
||||
type Fees []struct {
|
||||
FPV float64 `json:"fpv"`
|
||||
}
|
||||
|
||||
17
main.go
17
main.go
@ -3,7 +3,6 @@ package main
|
||||
import (
|
||||
"log"
|
||||
|
||||
"github.com/gchaincl/mempool/client"
|
||||
"github.com/gchaincl/mempool/ui"
|
||||
)
|
||||
|
||||
@ -14,22 +13,6 @@ func main() {
|
||||
}
|
||||
defer gui.Close()
|
||||
|
||||
go func() {
|
||||
c, err := client.New()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
for {
|
||||
resp, err := c.Read()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
gui.Render(resp)
|
||||
}
|
||||
}()
|
||||
|
||||
if err := gui.Loop(); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
88
ui/tx_search.go
Normal file
88
ui/tx_search.go
Normal file
@ -0,0 +1,88 @@
|
||||
package ui
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/jroimartin/gocui"
|
||||
)
|
||||
|
||||
type TXSearch struct {
|
||||
gui *gocui.Gui
|
||||
|
||||
opened bool
|
||||
txid string
|
||||
cb func(string) error
|
||||
}
|
||||
|
||||
func NewTXSearch(gui *gocui.Gui) *TXSearch {
|
||||
ts := &TXSearch{gui: gui}
|
||||
return ts
|
||||
}
|
||||
|
||||
func (s *TXSearch) Callback(fn func(txId string) error) {
|
||||
s.cb = fn
|
||||
}
|
||||
|
||||
func (s *TXSearch) SetKeybinding() {
|
||||
s.gui.SetKeybinding("", 'f', gocui.ModNone, func(*gocui.Gui, *gocui.View) error {
|
||||
s.gui.DeleteKeybinding("", 'f', gocui.ModNone)
|
||||
s.Open()
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func (s *TXSearch) Layout(g *gocui.Gui) error {
|
||||
name := "tx_search"
|
||||
if !s.opened {
|
||||
g.Cursor = false
|
||||
g.DeleteView(name)
|
||||
return nil
|
||||
}
|
||||
|
||||
g.Cursor = true
|
||||
x, y := g.Size()
|
||||
v, err := g.SetView(name, x/2-35, y/2-1, x/2+35, y/2+1)
|
||||
if err != nil {
|
||||
if err != gocui.ErrUnknownView {
|
||||
return err
|
||||
}
|
||||
v.Title = "Track transaction (txid)"
|
||||
v.Editable = true
|
||||
g.SetCurrentView(name)
|
||||
v.Editor = gocui.EditorFunc(s.editFn)
|
||||
v.Autoscroll = false
|
||||
fmt.Fprintf(v, "%s", s.txid)
|
||||
v.SetCursor(len(s.txid), 0)
|
||||
|
||||
g.SetKeybinding(v.Name(), gocui.KeyEsc, gocui.ModNone, func(*gocui.Gui, *gocui.View) error {
|
||||
s.Close()
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *TXSearch) editFn(v *gocui.View, key gocui.Key, ch rune, mod gocui.Modifier) {
|
||||
switch key {
|
||||
case gocui.KeyEnter:
|
||||
if id := s.txid; id != "" {
|
||||
s.cb(id)
|
||||
}
|
||||
s.Close()
|
||||
case gocui.KeyArrowDown, gocui.KeyArrowUp:
|
||||
return
|
||||
}
|
||||
|
||||
gocui.DefaultEditor.Edit(v, key, ch, mod)
|
||||
s.txid, _ = v.Line(0)
|
||||
}
|
||||
|
||||
func (s *TXSearch) Open() {
|
||||
s.opened = true
|
||||
}
|
||||
|
||||
func (s *TXSearch) Close() {
|
||||
s.SetKeybinding()
|
||||
s.opened = false
|
||||
}
|
||||
61
ui/ui.go
61
ui/ui.go
@ -2,6 +2,7 @@ package ui
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
@ -20,12 +21,15 @@ type state struct {
|
||||
projected []client.ProjectedBlock
|
||||
vBytesPerSecond int
|
||||
info *client.MempoolInfo
|
||||
tracking *client.TrackTx
|
||||
}
|
||||
|
||||
type UI struct {
|
||||
gui *gocui.Gui
|
||||
fd *FeeDistribution
|
||||
state state
|
||||
client *client.Client
|
||||
gui *gocui.Gui
|
||||
fd *FeeDistribution
|
||||
ts *TXSearch
|
||||
state state
|
||||
}
|
||||
|
||||
func New() (*UI, error) {
|
||||
@ -36,14 +40,40 @@ func New() (*UI, error) {
|
||||
|
||||
ui := &UI{gui: gui}
|
||||
ui.fd = NewFeeDistribution(gui)
|
||||
gui.SetManager(ui, ui.fd)
|
||||
ui.ts = NewTXSearch(gui)
|
||||
gui.SetManager(ui, ui.fd, ui.ts)
|
||||
|
||||
gui.SetKeybinding("", gocui.KeyCtrlC, gocui.ModNone, quit)
|
||||
gui.SetKeybinding("", 'q', gocui.ModNone, quit)
|
||||
ui.ts.SetKeybinding()
|
||||
|
||||
gui.Mouse = true
|
||||
gui.Highlight = true
|
||||
gui.InputEsc = true
|
||||
gui.SelFgColor = gocui.ColorWhite
|
||||
|
||||
go func() {
|
||||
c, err := client.New()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
if err := c.Want(); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
ui.client = c
|
||||
ui.ts.Callback(func(txId string) error {
|
||||
return c.Track(txId)
|
||||
})
|
||||
|
||||
for {
|
||||
resp, err := c.Read()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
ui.Render(resp)
|
||||
}
|
||||
|
||||
}()
|
||||
|
||||
return ui, nil
|
||||
}
|
||||
|
||||
@ -87,6 +117,9 @@ func (ui *UI) Render(resp *client.Response) {
|
||||
ui.state.info = info
|
||||
}
|
||||
|
||||
// Update tracking info
|
||||
ui.state.tracking = &resp.TrackTx
|
||||
|
||||
// delete all the views
|
||||
for _, v := range ui.gui.Views() {
|
||||
ui.gui.DeleteView(v.Name())
|
||||
@ -108,6 +141,8 @@ func (ui *UI) Layout(g *gocui.Gui) error {
|
||||
// and the blockchain in the bottom
|
||||
vertical := BLOCK_WIDTH*6 > x
|
||||
|
||||
track := ui.state.tracking
|
||||
|
||||
// draw projected blocks (mempool)
|
||||
for i, _ := range ui.state.projected {
|
||||
name := fmt.Sprintf("projected-block-%d", i)
|
||||
@ -131,6 +166,14 @@ func (ui *UI) Layout(g *gocui.Gui) error {
|
||||
}
|
||||
v.BgColor = gocui.ColorBlack
|
||||
g.SetKeybinding(v.Name(), gocui.MouseLeft, gocui.ModNone, ui.onBlockClick)
|
||||
|
||||
if track.Tracking && !track.TX.Status.Confirmed {
|
||||
if track.BlockHeight == i {
|
||||
v.SelBgColor = gocui.ColorRed
|
||||
v.SelFgColor = gocui.ColorRed
|
||||
g.SetCurrentView(v.Name())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
v.Clear()
|
||||
@ -166,9 +209,17 @@ func (ui *UI) Layout(g *gocui.Gui) error {
|
||||
}
|
||||
v.BgColor = gocui.ColorBlack
|
||||
g.SetKeybinding(v.Name(), gocui.MouseLeft, gocui.ModNone, ui.onBlockClick)
|
||||
|
||||
}
|
||||
|
||||
v.Title = fmt.Sprintf("#%d", block.Height)
|
||||
if track.Tracking && track.TX.Status.Confirmed {
|
||||
if track.BlockHeight == block.Height {
|
||||
v.SelBgColor = gocui.ColorRed
|
||||
v.SelFgColor = gocui.ColorRed
|
||||
g.SetCurrentView(v.Name())
|
||||
}
|
||||
}
|
||||
v.Clear()
|
||||
if _, err := v.Write(ui.printBlock(i, x1-x0, y1-y0)); err != nil {
|
||||
return err
|
||||
|
||||
Loading…
Reference in New Issue
Block a user