Add tx tracking
Some checks failed
release / goreleaser (push) Has been cancelled

This commit is contained in:
Gustavo Chain 2020-02-27 17:46:55 +01:00
parent 98f1ccbfdc
commit 1ec586f409
No known key found for this signature in database
GPG Key ID: DA7C1746DC118A46
5 changed files with 169 additions and 29 deletions

View File

@ -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

View File

@ -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
View File

@ -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
View 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
}

View File

@ -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