diff --git a/main.go b/main.go index ba0214b..e2fe53d 100644 --- a/main.go +++ b/main.go @@ -2,14 +2,28 @@ package main import ( "fmt" - "github.com/blockpane/pvtop/prevotes" "log" "os" + "strconv" + "strings" "time" + + "github.com/blockpane/pvtop/prevotes" ) const refreshRate = time.Second +var lastValUpdate = time.Now() +var reqValUpdate = make(chan bool) + +func maintainValNames(v *prevotes.ValNames) { + for { + v.Update() + lastValUpdate = time.Now() + <-reqValUpdate + } +} + func main() { log.SetFlags(log.Lshortfile | log.LstdFlags) @@ -23,10 +37,8 @@ func main() { } fmt.Println("Please wait, getting validator information....") - v := prevotes.GetValNames(os.Args[1]) - if v == nil { - log.Fatal("no validators found") - } + v := prevotes.NewValNamesWithAddr(os.Args[1]) + go maintainValNames(v) voteChan := make(chan []prevotes.VoteState) votePctChan := make(chan float64) @@ -36,6 +48,7 @@ func main() { go prevotes.DrawScreen(networkName, voteChan, votePctChan, commitPctChan, SummaryChan) tick := time.NewTicker(refreshRate) + lastValUpdateHeight := 0 for range tick.C { votes, votePct, commitPct, hrs, dur, e := prevotes.GetHeightVoteStep(os.Args[1], v) if e != nil { @@ -45,9 +58,21 @@ func main() { if dur < 0 { dur = 0 } - SummaryChan <- fmt.Sprintf("height/round/step: %s - v: %.0f%% c: %.0f%% (%v)\n", hrs, votePct*100, commitPct*100, dur) + SummaryChan <- fmt.Sprintf("Height/Round/Step: %s - v: %.0f%% c: %.0f%% (%v)\nVote Power Updated @h=%d (%s)", + hrs, votePct*100, commitPct*100, dur, + lastValUpdateHeight, time.Now().Sub(lastValUpdate)) voteChan <- votes votePctChan <- votePct commitPctChan <- commitPct + + currentHeight, err := strconv.Atoi(strings.Split(hrs, "/")[0]) + if err == nil { + // Update every 10 blocks, or immediately for new chains + if (lastValUpdateHeight+10 < currentHeight) || (currentHeight < 3) { + reqValUpdate <- true + lastValUpdateHeight = currentHeight + } + } + } } diff --git a/prevotes/info.go b/prevotes/info.go index ab086b9..7056ffc 100644 --- a/prevotes/info.go +++ b/prevotes/info.go @@ -4,13 +4,15 @@ import ( "context" "encoding/json" "fmt" - "gopkg.in/yaml.v3" "io" "log" "net/http" "strconv" "strings" "sync" + "time" + + "gopkg.in/yaml.v3" "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/types/query" @@ -30,39 +32,48 @@ type ValNames struct { key map[string]int // pubkey -> position indice map[int]string // index -> moniker power map[int]float64 + + addr string } -func GetValNames(addr string) *ValNames { - addr = strings.Replace(addr, "tcp://", "http://", 1) - httpAddr := strings.TrimRight(addr, "/") +func NewValNamesWithAddr(addr string) *ValNames { v := &ValNames{ key: make(map[string]int), indice: make(map[int]string), power: make(map[int]float64), + addr: addr, } + return v +} + +func (v *ValNames) Update() error { + addr := strings.Replace(v.addr, "tcp://", "http://", 1) + httpAddr := strings.TrimRight(addr, "/") perPage := 100 page := 1 index := 0 - more := true - for more { - resp, err := http.Get(httpAddr + "/validators?per_page=" + strconv.Itoa(perPage) + "&page=" + strconv.Itoa(page)) + hclient := http.Client{ + Timeout: 5 * time.Second, + } + for { + resp, err := hclient.Get(httpAddr + "/validators?per_page=" + strconv.Itoa(perPage) + "&page=" + strconv.Itoa(page)) if err != nil { log.Println(err) - continue + return err } r, err := io.ReadAll(resp.Body) resp.Body.Close() if err != nil { log.Println(err) - continue + return err } valResp := &rpcValidatorsResp{} err = json.Unmarshal(r, valResp) if err != nil { log.Println(err) - continue + return err } for _, val := range valResp.Result.Validators { @@ -75,32 +86,31 @@ func GetValNames(addr string) *ValNames { if index < totalVals { page += 1 } else { - more = false + break } } page = 1 index = 0 - more = true // do it again, but get the % of voting power - for more { - resp, err := http.Get(httpAddr + "/validators?per_page=" + strconv.Itoa(perPage) + "&page=" + strconv.Itoa(page)) + for { + resp, err := hclient.Get(httpAddr + "/validators?per_page=" + strconv.Itoa(perPage) + "&page=" + strconv.Itoa(page)) if err != nil { log.Println(err) - continue + return err } r, err := io.ReadAll(resp.Body) resp.Body.Close() if err != nil { log.Println(err) - continue + return err } valResp := &rpcValidatorsResp{} err = json.Unmarshal(r, valResp) if err != nil { log.Println(err) - continue + return err } for _, val := range valResp.Result.Validators { @@ -112,11 +122,11 @@ func GetValNames(addr string) *ValNames { if index < totalVals { page += 1 } else { - more = false + break } } - client, err := rpchttp.New(addr, "/websocket") + client, err := rpchttp.NewWithTimeout(addr, "/websocket", 2) if err != nil { log.Fatal(err) } @@ -131,19 +141,19 @@ func GetValNames(addr string) *ValNames { q, e := valsQuery.Marshal() if e != nil { log.Println(e) - continue + return err } valsResult, e := client.ABCIQuery(context.Background(), "/cosmos.staking.v1beta1.Query/Validators", q) if e != nil { log.Println(e) - continue + return err } if len(valsResult.Response.Value) > 0 { valsResp := staketypes.QueryValidatorsResponse{} e = valsResp.Unmarshal(valsResult.Response.Value) if e != nil { log.Println(e) - continue + return err } for _, val := range valsResp.Validators { annoyed := make(map[string]interface{}) @@ -163,7 +173,7 @@ func GetValNames(addr string) *ValNames { } } - return v + return nil } func (v *ValNames) setKey(key string, position int) { diff --git a/prevotes/term.go b/prevotes/term.go index fbc8b82..22b3b6e 100644 --- a/prevotes/term.go +++ b/prevotes/term.go @@ -2,11 +2,12 @@ package prevotes import ( "fmt" - ui "github.com/gizak/termui/v3" - "github.com/gizak/termui/v3/widgets" "log" "os" "time" + + ui "github.com/gizak/termui/v3" + "github.com/gizak/termui/v3/widgets" ) func DrawScreen(network string, voteChan chan []VoteState, votePctChan, commitPctChan chan float64, summaryChan chan string) {