diff --git a/go.mod b/go.mod index e8141f7..bbd5e0b 100644 --- a/go.mod +++ b/go.mod @@ -5,8 +5,8 @@ go 1.21 require ( github.com/cosmos/cosmos-sdk v0.47.10 github.com/cosmos/interchain-security/v3 v3.3.0 - github.com/gdamore/tcell/v2 v2.6.0 - github.com/rivo/tview v0.0.0-20231022175332-f7f32ad28104 + github.com/gdamore/tcell/v2 v2.7.1 + github.com/rivo/tview v0.0.0-20240420134618-e119d15762fe github.com/rs/zerolog v1.32.0 github.com/spf13/cobra v1.8.0 ) @@ -84,7 +84,7 @@ require ( github.com/magiconair/properties v1.8.7 // indirect github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.20 // indirect - github.com/mattn/go-runewidth v0.0.14 // indirect + github.com/mattn/go-runewidth v0.0.15 // indirect github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect github.com/mimoo/StrobeGo v0.0.0-20210601165009-122bf33a46e0 // indirect github.com/mitchellh/mapstructure v1.5.0 // indirect @@ -98,7 +98,7 @@ require ( github.com/prometheus/common v0.42.0 // indirect github.com/prometheus/procfs v0.9.0 // indirect github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 // indirect - github.com/rivo/uniseg v0.4.3 // indirect + github.com/rivo/uniseg v0.4.7 // indirect github.com/rogpeppe/go-internal v1.11.0 // indirect github.com/sasha-s/go-deadlock v0.3.1 // indirect github.com/spf13/afero v1.9.5 // indirect @@ -118,8 +118,8 @@ require ( golang.org/x/exp v0.0.0-20230711153332-06a737ee72cb // indirect golang.org/x/net v0.19.0 // indirect golang.org/x/sync v0.4.0 // indirect - golang.org/x/sys v0.16.0 // indirect - golang.org/x/term v0.15.0 // indirect + golang.org/x/sys v0.17.0 // indirect + golang.org/x/term v0.17.0 // indirect golang.org/x/text v0.14.0 // indirect google.golang.org/genproto v0.0.0-20240102182953-50ed04b92917 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20231212172506-995d672761c0 // indirect diff --git a/go.sum b/go.sum index 3350c42..bef1163 100644 --- a/go.sum +++ b/go.sum @@ -212,8 +212,8 @@ github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4 github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= github.com/gdamore/encoding v1.0.0 h1:+7OoQ1Bc6eTm5niUzBa0Ctsh6JbMW6Ra+YNuAtDBdko= github.com/gdamore/encoding v1.0.0/go.mod h1:alR0ol34c49FCSBLjhosxzcPHQbf2trDkoo5dl+VrEg= -github.com/gdamore/tcell/v2 v2.6.0 h1:OKbluoP9VYmJwZwq/iLb4BxwKcwGthaa1YNBJIyCySg= -github.com/gdamore/tcell/v2 v2.6.0/go.mod h1:be9omFATkdr0D9qewWW3d+MEvl5dha+Etb5y65J2H8Y= +github.com/gdamore/tcell/v2 v2.7.1 h1:TiCcmpWHiAU7F0rA2I3S2Y4mmLmO9KHxJ7E1QhYzQbc= +github.com/gdamore/tcell/v2 v2.7.1/go.mod h1:dSXtXTSK0VsW1biw65DZLZ2NKr7j0qP/0J7ONmsraWg= github.com/getsentry/sentry-go v0.23.0 h1:dn+QRCeJv4pPt9OjVXiMcGIBIefaTJPw/h0bZWO05nE= github.com/getsentry/sentry-go v0.23.0/go.mod h1:lc76E2QywIyW8WuBnwl8Lc4bkmQH4+w1gwTf25trprY= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= @@ -428,8 +428,8 @@ github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/ github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= -github.com/mattn/go-runewidth v0.0.14 h1:+xnbZSEeDbOIg5/mE6JF0w6n9duR1l3/WmbinWVwUuU= -github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= +github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U= +github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= @@ -513,11 +513,12 @@ github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 h1:N/ElC8H3+5X github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= github.com/regen-network/protobuf v1.3.3-alpha.regen.1 h1:OHEc+q5iIAXpqiqFKeLpu5NwTIkVXUs48vFMwzqpqY4= github.com/regen-network/protobuf v1.3.3-alpha.regen.1/go.mod h1:2DjTFR1HhMQhiWC5sZ4OhQ3+NtdbZ6oBDKQwq5Ou+FI= -github.com/rivo/tview v0.0.0-20231022175332-f7f32ad28104 h1:wKaxPrOZ2TmfUt+Y+aIqFdBNDMwaEVPftRA3UclgNlc= -github.com/rivo/tview v0.0.0-20231022175332-f7f32ad28104/go.mod h1:nVwGv4MP47T0jvlk7KuTTjjuSmrGO4JF0iaiNt4bufE= +github.com/rivo/tview v0.0.0-20240420134618-e119d15762fe h1:mrtt67gZgxP6+s+zRkiyAQ+9oyDQrM3uL3jhF9tEgGk= +github.com/rivo/tview v0.0.0-20240420134618-e119d15762fe/go.mod h1:02iFIz7K/A9jGCvrizLPvoqr4cEIx7q54RH5Qudkrss= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= -github.com/rivo/uniseg v0.4.3 h1:utMvzDsuh3suAEnhH0RdHmoPbU648o6CvXxTx4SBMOw= github.com/rivo/uniseg v0.4.3/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= +github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ= +github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= @@ -663,6 +664,7 @@ golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -703,6 +705,7 @@ golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qx golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220607020251-c690dde0001d/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.19.0 h1:zTwKpTd2XuCqf8huc7Fo2iSy+4RHPd10s4KzeTnVr1c= golang.org/x/net v0.19.0/go.mod h1:CfAk/cbD4CthTvqiEl8NpboMuiuOYsAr/7NOjZJtv1U= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= @@ -727,6 +730,7 @@ golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.4.0 h1:zxkM55ReGkDlKSM+Fu41A+zmbZuaPVbGMzvvdUPznYQ= golang.org/x/sync v0.4.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -785,13 +789,13 @@ golang.org/x/sys v0.0.0-20221010170243-090e33056c14/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU= -golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.17.0 h1:25cE3gD+tdBA7lp7QfhuV+rJiE9YXTcS3VG1SqssI/Y= +golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= -golang.org/x/term v0.15.0 h1:y/Oo/a/q3IXu26lQgl04j/gjuBDOBlx7X6Om1j2CPW4= -golang.org/x/term v0.15.0/go.mod h1:BDl952bC7+uMoWR75FIrCDx79TPU9oHkTZ9yRbYOrX0= +golang.org/x/term v0.17.0 h1:mkTF7LCd6WGJNL3K1Ad7kwxNfYAW6a8a8QqtMblp/4U= +golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -857,6 +861,7 @@ golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4f golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= diff --git a/pkg/display/all_rounds_table.go b/pkg/display/all_rounds_table.go index feef20d..24b0752 100644 --- a/pkg/display/all_rounds_table.go +++ b/pkg/display/all_rounds_table.go @@ -1,6 +1,7 @@ package display import ( + "fmt" "main/pkg/types" "strconv" @@ -9,43 +10,104 @@ import ( "github.com/rivo/tview" ) +type AllRoundsTableHeader struct { + Title string + Value func(index int, votes types.RoundVotes, validators []types.ValidatorWithChainValidator) string +} + type AllRoundsTableData struct { tview.TableContentReadOnly + Headers []AllRoundsTableHeader Validators types.ValidatorsWithInfoAndAllRoundVotes DisableEmojis bool } func NewAllRoundsTableData(disableEmojis bool) *AllRoundsTableData { + headers := []AllRoundsTableHeader{ + { + Title: "round #", + Value: func(index int, votes types.RoundVotes, validators []types.ValidatorWithChainValidator) string { + return strconv.Itoa(index) + }, + }, + { + Title: "prevoted count/total", + Value: func(index int, votes types.RoundVotes, validators []types.ValidatorWithChainValidator) string { + count := 0 + for _, vote := range votes { + if vote.Prevote != types.VotedNil { + count += 1 + } + } + return fmt.Sprintf("%d/%d", count, len(validators)) + }, + }, + { + Title: "prevoted %", + Value: func(index int, votes types.RoundVotes, validators []types.ValidatorWithChainValidator) string { + prevotedPercent := types. + ValidatorsWithRoundVoteFrom(validators, votes). + GetTotalVotingPowerPrevotedPercent(true) + return fmt.Sprintf("%.2f%%", prevotedPercent) + }, + }, + { + Title: "prevoted agreed %", + Value: func(index int, votes types.RoundVotes, validators []types.ValidatorWithChainValidator) string { + prevotedPercent := types. + ValidatorsWithRoundVoteFrom(validators, votes). + GetTotalVotingPowerPrevotedPercent(false) + return fmt.Sprintf("%.2f%%", prevotedPercent) + }, + }, + { + Title: "precommitted %", + Value: func(index int, votes types.RoundVotes, validators []types.ValidatorWithChainValidator) string { + prevotedPercent := types. + ValidatorsWithRoundVoteFrom(validators, votes). + GetTotalVotingPowerPrecommittedPercent(true) + return fmt.Sprintf("%.2f%%", prevotedPercent) + }, + }, + } + return &AllRoundsTableData{ Validators: types.ValidatorsWithInfoAndAllRoundVotes{}, + Headers: headers, DisableEmojis: disableEmojis, } } func (d *AllRoundsTableData) GetCell(row, column int) *tview.TableCell { + headersCount := len(d.Headers) + // Table header. - if row == 0 { - text := "validator" - if column != 0 { - text = strconv.Itoa(column - 1) + if row < headersCount { + // First column is title. + if column == 0 { + return tview. + NewTableCell(d.Headers[row].Title). + SetAlign(tview.AlignCenter). + SetStyle(tcell.StyleDefault.Bold(true)) } + roundVotes := d.Validators.RoundsVotes[column-1] + return tview. - NewTableCell(text). - SetAlign(tview.AlignCenter). - SetStyle(tcell.StyleDefault.Bold(true)) + NewTableCell(d.Headers[row].Value(column-1, roundVotes, d.Validators.Validators)). + SetAlign(tview.AlignCenter) } // First column is always validators list. if column == 0 { - text := d.Validators.Validators[row-1].Serialize() + text := d.Validators.Validators[row-headersCount].Serialize() cell := tview.NewTableCell(text) return cell } roundVotes := d.Validators.RoundsVotes[column-1] - roundVote := roundVotes[row-1] + roundVote := roundVotes[row-headersCount] text := roundVote.Serialize(d.DisableEmojis) cell := tview.NewTableCell(text) @@ -58,11 +120,11 @@ func (d *AllRoundsTableData) GetCell(row, column int) *tview.TableCell { } func (d *AllRoundsTableData) GetRowCount() int { - return len(d.Validators.Validators) + 1 + return len(d.Validators.Validators) + len(d.Headers) } func (d *AllRoundsTableData) GetColumnCount() int { - return len(d.Validators.RoundsVotes) + 1 + return len(d.Validators.RoundsVotes) + 1 // first column is header } func (d *AllRoundsTableData) SetValidators(validators types.ValidatorsWithInfoAndAllRoundVotes) { diff --git a/pkg/display/wrapper.go b/pkg/display/wrapper.go index 5b0560c..fc4badc 100644 --- a/pkg/display/wrapper.go +++ b/pkg/display/wrapper.go @@ -76,7 +76,21 @@ func NewWrapper( SetBorders(false). SetSelectable(false, false). SetContent(allRoundsTableData). - SetFixed(0, 1) + SetFixed(len(allRoundsTableData.Headers), 1) + + allRoundsTable.SetInputCapture(func(event *tcell.EventKey) *tcell.EventKey { + if event.Key() == tcell.KeyRight && event.Modifiers() == tcell.ModCtrl { + prevOffset, _ := allRoundsTable.GetOffset() + allRoundsTable.SetOffset(prevOffset, allRoundsTable.GetColumnCount()) + } + + if event.Key() == tcell.KeyLeft && event.Modifiers() == tcell.ModCtrl { + prevOffset, _ := allRoundsTable.GetOffset() + allRoundsTable.SetOffset(prevOffset, 0) + } + + return event + }) consensusInfoTextView := tview.NewTextView(). SetDynamicColors(true). diff --git a/pkg/types/validator.go b/pkg/types/validator.go index 6944064..37f45e7 100644 --- a/pkg/types/validator.go +++ b/pkg/types/validator.go @@ -40,6 +40,19 @@ type ValidatorWithRoundVote struct { type ValidatorsWithRoundVote []ValidatorWithRoundVote +func ValidatorsWithRoundVoteFrom(validators []ValidatorWithChainValidator, roundVotes RoundVotes) ValidatorsWithRoundVote { + response := make([]ValidatorWithRoundVote, len(validators)) + + for index, validator := range validators { + response[index] = ValidatorWithRoundVote{ + Validator: validator.Validator, + RoundVote: roundVotes[index], + } + } + + return response +} + type ValidatorsWithAllRoundsVotes struct { Validators []Validator RoundsVotes []RoundVotes diff --git a/static/help.txt b/static/help.txt index 336f343..44111da 100644 --- a/static/help.txt +++ b/static/help.txt @@ -16,3 +16,5 @@ You can use the following shortcuts: You can also press [Tab[] to switch between modes, which are: - display last round prevotes/precommits - display prevotes/precommits for all rounds + +If you're on the all rounds mode, you can use Ctrl+left/right arrow to scroll to first/last round.