From 24990fb0dee3778ba4815a2fd5e0d61e139b7401 Mon Sep 17 00:00:00 2001 From: Guillermo Paoletti Date: Sat, 7 Sep 2024 16:59:51 +0200 Subject: [PATCH] feat: chain explorer TUI (#57) * wip ui for explorer * md renderer * container ui * panel key handler * inputs * block info * txn support * keys references * reorg files * fix: linter issues * truncate very long words * docs * tidy deps * chore: reenable cache --- cmd/playground/explorer/{start.go => ui.go} | 20 ++- cmd/root.go | 11 -- .../pages/hanchond/playground/explorer/ui.mdx | 29 ++++ go.mod | 37 ++++- go.sum | 90 +++++++++-- main.go | 4 +- playground/explorer/database.go | 35 +++++ .../explorer/database/explorerquery.sql | 3 + .../explorer/database/explorerquery.sql.go | 32 ++++ playground/explorer/explorer.go | 60 ++++++-- playground/explorer/explorerui/block_data.go | 60 ++++++++ playground/explorer/explorerui/constants.go | 33 ++++ playground/explorer/explorerui/container.go | 55 +++++++ playground/explorer/explorerui/header.go | 16 ++ .../explorer/explorerui/height_frame.go | 93 ++++++++++++ playground/explorer/explorerui/model.go | 142 ++++++++++++++++++ playground/explorer/explorerui/trucate.go | 16 ++ playground/explorer/explorerui/tui.go | 44 ++++++ playground/explorer/explorerui/txn_data.go | 93 ++++++++++++ vocs.config.ts | 10 ++ 20 files changed, 830 insertions(+), 53 deletions(-) rename cmd/playground/explorer/{start.go => ui.go} (64%) create mode 100644 docs/pages/hanchond/playground/explorer/ui.mdx create mode 100644 playground/explorer/explorerui/block_data.go create mode 100644 playground/explorer/explorerui/constants.go create mode 100644 playground/explorer/explorerui/container.go create mode 100644 playground/explorer/explorerui/header.go create mode 100644 playground/explorer/explorerui/height_frame.go create mode 100644 playground/explorer/explorerui/model.go create mode 100644 playground/explorer/explorerui/trucate.go create mode 100644 playground/explorer/explorerui/tui.go create mode 100644 playground/explorer/explorerui/txn_data.go diff --git a/cmd/playground/explorer/start.go b/cmd/playground/explorer/ui.go similarity index 64% rename from cmd/playground/explorer/start.go rename to cmd/playground/explorer/ui.go index 6a13998..a22a145 100644 --- a/cmd/playground/explorer/start.go +++ b/cmd/playground/explorer/ui.go @@ -6,13 +6,14 @@ import ( "github.com/hanchon/hanchond/playground/evmos" "github.com/hanchon/hanchond/playground/explorer" + "github.com/hanchon/hanchond/playground/explorer/explorerui" "github.com/hanchon/hanchond/playground/sql" "github.com/spf13/cobra" ) -// represents the query command -var startCmd = &cobra.Command{ - Use: "start", +// ui represents the query command +var uiCmd = &cobra.Command{ + Use: "ui", Args: cobra.ExactArgs(0), Short: "Start the node explorer", Run: func(cmd *cobra.Command, _ []string) { @@ -31,16 +32,19 @@ var startCmd = &cobra.Command{ // TODO: move the newFromDB to cosmos daemon e := evmos.NewEvmosFromDB(queries, nodeID) - + // TODO: support mainnet and testnet endpoints ex := explorer.NewLocalExplorerClient(e.Ports.P8545, e.Ports.P1317, e.HomeDir) - if err := ex.ProcessMissingBlocks(int64(startingHeight)); err != nil { - fmt.Println("error getting missing blocks: ", err.Error()) + + p := explorerui.CreateExplorerTUI(startingHeight, ex) + if _, err := p.Run(); err != nil { + fmt.Fprintf(os.Stderr, "Error: %v\n", err) os.Exit(1) } + os.Exit(0) }, } func init() { - ExplorerCmd.AddCommand(startCmd) - startCmd.Flags().Int("starting-height", 1, "Starting height to index the chain.") + ExplorerCmd.AddCommand(uiCmd) + uiCmd.Flags().Int("starting-height", 1, "Starting height to index the chain.") } diff --git a/cmd/root.go b/cmd/root.go index 630292d..803bafb 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -16,8 +16,6 @@ var rootCmd = &cobra.Command{ Long: `Stop re-writing the same scripts one million times`, } -// Execute adds all child commands to the root command and sets flags appropriately. -// This is called by main.main(). It only needs to happen once to the rootCmd. func Execute() { err := rootCmd.Execute() if err != nil { @@ -26,16 +24,7 @@ func Execute() { } func init() { - // Here you will define your flags and configuration settings. - // Cobra supports persistent flags, which, if defined here, - // will be global for your application. - - // rootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file (default is $HOME/.hanchond.yaml)") - - // Cobra also supports local flags, which will only run - // when this action is called directly. rootCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle") - rootCmd.AddCommand(convert.ConvertCmd) rootCmd.AddCommand(playground.PlaygroundCmd) } diff --git a/docs/pages/hanchond/playground/explorer/ui.mdx b/docs/pages/hanchond/playground/explorer/ui.mdx new file mode 100644 index 0000000..809ccb2 --- /dev/null +++ b/docs/pages/hanchond/playground/explorer/ui.mdx @@ -0,0 +1,29 @@ +# Block Explorer + +The command `playground explorer ui` can be used to run the terminal block explorer. + +```bash +hanchond playground explorer ui +``` + +:::info +The `--node` flag can be used to select RPC provider. + +The `--starting-height` flag can be used to start indexing from a height different from 1. +::: + +## Features: + +- Indexes all the blocks +- Indexes all the transactions +- Gets the block information using the Cosmos and Ethereum endpoints +- Get the transaction Cosmos information +- Get the transaction Ethereum information + +## Screenshots: + +![Syncing](https://i.imgur.com/4KAlwZp.png) + +![Blocks](https://i.imgur.com/kmvCGBN.png) + +![Transactions](https://i.imgur.com/mh2a5Mn.png) diff --git a/go.mod b/go.mod index ea8e477..6589747 100644 --- a/go.mod +++ b/go.mod @@ -3,6 +3,10 @@ module github.com/hanchon/hanchond go 1.22.2 require ( + github.com/charmbracelet/bubbles v0.19.0 + github.com/charmbracelet/bubbletea v1.1.0 + github.com/charmbracelet/glamour v0.8.0 + github.com/charmbracelet/lipgloss v0.13.0 github.com/cosmos/cosmos-sdk v0.47.12 github.com/cosmos/go-bip39 v1.0.0 github.com/cosmos/ibc-go/v7 v7.6.0 @@ -13,7 +17,7 @@ require ( github.com/miguelmota/go-ethereum-hdwallet v0.1.1 github.com/spf13/cobra v1.8.1 github.com/valyala/fasthttp v1.55.0 - golang.org/x/crypto v0.24.0 + golang.org/x/crypto v0.25.0 golang.org/x/exp v0.0.0-20231006140011-7918f672742d golang.org/x/text v0.16.0 ) @@ -31,8 +35,12 @@ require ( github.com/ChainSafe/go-schnorrkel v1.0.0 // indirect github.com/StackExchange/wmi v1.2.1 // indirect github.com/VictoriaMetrics/fastcache v1.6.0 // indirect + github.com/alecthomas/chroma/v2 v2.14.0 // indirect github.com/andybalholm/brotli v1.1.0 // indirect github.com/armon/go-metrics v0.4.1 // indirect + github.com/atotto/clipboard v0.1.4 // indirect + github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect + github.com/aymerick/douceur v0.2.0 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/bgentry/speakeasy v0.1.1-0.20220910012023-760eaf8b6816 // indirect github.com/btcsuite/btcd v0.23.5-0.20231215221805-96c9fd8078fd // indirect @@ -42,6 +50,9 @@ require ( github.com/btcsuite/btcutil v1.0.3-0.20201208143702-a53e38424cce // indirect github.com/cespare/xxhash v1.1.0 // indirect github.com/cespare/xxhash/v2 v2.2.0 // indirect + github.com/charmbracelet/harmonica v0.2.0 // indirect + github.com/charmbracelet/x/ansi v0.2.3 // indirect + github.com/charmbracelet/x/term v0.2.0 // indirect github.com/chzyer/readline v1.5.1 // indirect github.com/cockroachdb/errors v1.10.0 // indirect github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b // indirect @@ -62,8 +73,10 @@ require ( github.com/dgraph-io/badger/v2 v2.2007.4 // indirect github.com/dgraph-io/ristretto v0.1.1 // indirect github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13 // indirect + github.com/dlclark/regexp2 v1.11.0 // indirect github.com/dustin/go-humanize v1.0.1 // indirect github.com/dvsekhvalnov/jose2go v1.6.0 // indirect + github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f // indirect github.com/fsnotify/fsnotify v1.7.0 // indirect github.com/getsentry/sentry-go v0.23.0 // indirect github.com/go-kit/kit v0.12.0 // indirect @@ -78,6 +91,7 @@ require ( github.com/golang/snappy v0.0.4 // indirect github.com/google/btree v1.1.2 // indirect github.com/google/go-cmp v0.6.0 // indirect + github.com/gorilla/css v1.0.1 // indirect github.com/gorilla/websocket v1.5.1 // indirect github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 // indirect github.com/grpc-ecosystem/grpc-gateway v1.16.0 // indirect @@ -99,14 +113,21 @@ require ( github.com/kr/text v0.2.0 // indirect github.com/libp2p/go-buffer-pool v0.1.0 // indirect github.com/linxGnu/grocksdb v1.8.5 // indirect + github.com/lucasb-eyer/go-colorful v1.2.0 // indirect github.com/magiconair/properties v1.8.7 // indirect github.com/manifoldco/promptui v0.9.0 // indirect github.com/mattn/go-isatty v0.0.20 // indirect - github.com/mattn/go-runewidth v0.0.9 // indirect + github.com/mattn/go-localereader v0.0.1 // indirect + github.com/mattn/go-runewidth v0.0.16 // indirect github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect + github.com/microcosm-cc/bluemonday v1.0.27 // indirect github.com/mimoo/StrobeGo v0.0.0-20210601165009-122bf33a46e0 // indirect github.com/mitchellh/mapstructure v1.5.0 // indirect github.com/mtibben/percent v0.2.1 // indirect + github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 // indirect + github.com/muesli/cancelreader v0.2.2 // indirect + github.com/muesli/reflow v0.3.0 // indirect + github.com/muesli/termenv v0.15.3-0.20240618155329-98d742f6907a // indirect github.com/olekukonko/tablewriter v0.0.5 // indirect github.com/pelletier/go-toml/v2 v2.1.0 // indirect github.com/petermattis/goid v0.0.0-20230518223814-80aa455d8761 // indirect @@ -118,9 +139,11 @@ require ( github.com/prometheus/procfs v0.11.0 // indirect github.com/prometheus/tsdb v0.10.0 // indirect github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 // indirect + github.com/rivo/uniseg v0.4.7 // indirect github.com/rogpeppe/go-internal v1.11.0 // indirect github.com/sagikazarmark/locafero v0.4.0 // indirect github.com/sagikazarmark/slog-shim v0.1.0 // indirect + github.com/sahilm/fuzzy v0.1.1 // indirect github.com/sasha-s/go-deadlock v0.3.1 // indirect github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible // indirect github.com/sourcegraph/conc v0.3.0 // indirect @@ -141,15 +164,17 @@ require ( github.com/tklauser/numcpus v0.6.0 // indirect github.com/tyler-smith/go-bip39 v1.1.0 // indirect github.com/valyala/bytebufferpool v1.0.0 // indirect + github.com/yuin/goldmark v1.7.4 // indirect + github.com/yuin/goldmark-emoji v1.0.3 // indirect github.com/zondax/hid v0.9.2 // indirect github.com/zondax/ledger-go v0.14.3 // indirect go.etcd.io/bbolt v1.3.7 // indirect go.uber.org/atomic v1.10.0 // indirect go.uber.org/multierr v1.9.0 // indirect - golang.org/x/net v0.26.0 // indirect - golang.org/x/sync v0.7.0 // indirect - golang.org/x/sys v0.21.0 // indirect - golang.org/x/term v0.21.0 // indirect + golang.org/x/net v0.27.0 // indirect + golang.org/x/sync v0.8.0 // indirect + golang.org/x/sys v0.24.0 // indirect + golang.org/x/term v0.22.0 // indirect google.golang.org/genproto v0.0.0-20240123012728-ef4313101c80 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20240123012728-ef4313101c80 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20240123012728-ef4313101c80 // indirect diff --git a/go.sum b/go.sum index faffadc..f9cd7b6 100644 --- a/go.sum +++ b/go.sum @@ -71,6 +71,12 @@ github.com/VividCortex/gohistogram v1.0.0 h1:6+hBz+qvs0JOrrNhhmR7lFxo5sINxBCGXrd github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g= github.com/aead/siphash v1.0.1/go.mod h1:Nywa3cDsYNNK3gaciGTWPwHt0wlpNV15vwmswBAUSII= github.com/ajstarks/svgo v0.0.0-20180226025133-644b8db467af/go.mod h1:K08gAheRH3/J6wwsYMMT4xOr94bZjxIelGM0+d/wbFw= +github.com/alecthomas/assert/v2 v2.7.0 h1:QtqSACNS3tF7oasA8CU6A6sXZSBDqnm7RfpLl9bZqbE= +github.com/alecthomas/assert/v2 v2.7.0/go.mod h1:Bze95FyfUr7x34QZrjL+XP+0qgp/zg8yS+TtBj1WA3k= +github.com/alecthomas/chroma/v2 v2.14.0 h1:R3+wzpnUArGcQz7fCETQBzO5n9IMNi13iIs46aU4V9E= +github.com/alecthomas/chroma/v2 v2.14.0/go.mod h1:QolEbTfmUHIMVpBqxeDnNBj2uoeI4EbYP4i6n68SG4I= +github.com/alecthomas/repr v0.4.0 h1:GhI2A8MACjfegCPVq9f1FLvIBS+DrQ2KQBFZP1iFzXc= +github.com/alecthomas/repr v0.4.0/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= @@ -88,6 +94,8 @@ github.com/apache/arrow/go/arrow v0.0.0-20191024131854-af6fa24be0db/go.mod h1:VT github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= github.com/armon/go-metrics v0.4.1 h1:hR91U9KYmb6bLBYLQjyM+3j+rcd/UhE+G78SFnF8gJA= github.com/armon/go-metrics v0.4.1/go.mod h1:E6amYzXo6aW1tqzoZGT755KkbgrJsSdpwZ+3JqfkOG4= +github.com/atotto/clipboard v0.1.4 h1:EH0zSVneZPSuFR11BlR9YppQTVDbh5+16AmcJi4g1z4= +github.com/atotto/clipboard v0.1.4/go.mod h1:ZY9tmq7sm5xIbd9bOK4onWV4S6X0u6GY7Vn0Yu86PYI= github.com/aws/aws-sdk-go v1.44.224 h1:09CiaaF35nRmxrzWZ2uRq5v6Ghg/d2RiPjZnSgtt+RQ= github.com/aws/aws-sdk-go v1.44.224/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8Phjh7fkwI= github.com/aws/aws-sdk-go-v2 v1.2.0/go.mod h1:zEQs02YRBw1DjK0PoJv3ygDYOFTre1ejlJWl8FwAuQo= @@ -99,6 +107,12 @@ github.com/aws/aws-sdk-go-v2/service/route53 v1.1.1/go.mod h1:rLiOUrPLW/Er5kRcQ7 github.com/aws/aws-sdk-go-v2/service/sso v1.1.1/go.mod h1:SuZJxklHxLAXgLTc1iFXbEWkXs7QRTQpCLGaKIprQW0= github.com/aws/aws-sdk-go-v2/service/sts v1.1.1/go.mod h1:Wi0EBZwiz/K44YliU0EKxqTCJGUfYTWXrrBwkq736bM= github.com/aws/smithy-go v1.1.0/go.mod h1:EzMw8dbp/YJL4A5/sbhGddag+NPT7q084agLbB9LgIw= +github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k= +github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8= +github.com/aymanbagabas/go-udiff v0.2.0 h1:TK0fH4MteXUDspT88n8CKzvK0X9O2xu9yQjWpi6yML8= +github.com/aymanbagabas/go-udiff v0.2.0/go.mod h1:RE4Ex0qsGkTAJoQdQQCA0uG+nAzJO/pI/QwceO5fgrA= +github.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk= +github.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4= github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= @@ -150,6 +164,22 @@ github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghf github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/charmbracelet/bubbles v0.19.0 h1:gKZkKXPP6GlDk6EcfujDK19PCQqRjaJZQ7QRERx1UF0= +github.com/charmbracelet/bubbles v0.19.0/go.mod h1:WILteEqZ+krG5c3ntGEMeG99nCupcuIk7V0/zOP0tOA= +github.com/charmbracelet/bubbletea v1.1.0 h1:FjAl9eAL3HBCHenhz/ZPjkKdScmaS5SK69JAK2YJK9c= +github.com/charmbracelet/bubbletea v1.1.0/go.mod h1:9Ogk0HrdbHolIKHdjfFpyXJmiCzGwy+FesYkZr7hYU4= +github.com/charmbracelet/glamour v0.8.0 h1:tPrjL3aRcQbn++7t18wOpgLyl8wrOHUEDS7IZ68QtZs= +github.com/charmbracelet/glamour v0.8.0/go.mod h1:ViRgmKkf3u5S7uakt2czJ272WSg2ZenlYEZXT2x7Bjw= +github.com/charmbracelet/harmonica v0.2.0 h1:8NxJWRWg/bzKqqEaaeFNipOu77YR5t8aSwG4pgaUBiQ= +github.com/charmbracelet/harmonica v0.2.0/go.mod h1:KSri/1RMQOZLbw7AHqgcBycp8pgJnQMYYT8QZRqZ1Ao= +github.com/charmbracelet/lipgloss v0.13.0 h1:4X3PPeoWEDCMvzDvGmTajSyYPcZM4+y8sCA/SsA3cjw= +github.com/charmbracelet/lipgloss v0.13.0/go.mod h1:nw4zy0SBX/F/eAO1cWdcvy6qnkDUxr8Lw7dvFrAIbbY= +github.com/charmbracelet/x/ansi v0.2.3 h1:VfFN0NUpcjBRd4DnKfRaIRo53KRgey/nhOoEqosGDEY= +github.com/charmbracelet/x/ansi v0.2.3/go.mod h1:dk73KoMTT5AX5BsX0KrqhsTqAnhZZoCBjs7dGWp4Ktw= +github.com/charmbracelet/x/exp/golden v0.0.0-20240815200342-61de596daa2b h1:MnAMdlwSltxJyULnrYbkZpp4k58Co7Tah3ciKhSNo0Q= +github.com/charmbracelet/x/exp/golden v0.0.0-20240815200342-61de596daa2b/go.mod h1:wDlXFlCrmJ8J+swcL/MnGUuYnqgQdW9rhSD61oNMb6U= +github.com/charmbracelet/x/term v0.2.0 h1:cNB9Ot9q8I711MyZ7myUR5HFWL/lc3OpU8jZ4hwm0x0= +github.com/charmbracelet/x/term v0.2.0/go.mod h1:GVxgxAbjUrmpvIINHIQnJJKpMlHiZ4cktEQCN6GWyF0= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/logex v1.2.1 h1:XHDu3E6q+gdHgsdTPH6ImJMIp436vR6MPtH8gP05QzM= github.com/chzyer/logex v1.2.1/go.mod h1:JLbx6lG2kDbNRFnfkgvh4eRJRPX1QCoOIWomwysCBrQ= @@ -252,8 +282,9 @@ github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUn github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13 h1:fAjc9m62+UWV/WAFKLNi6ZS0675eEUC9y3AlwSbQu1Y= github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= -github.com/dlclark/regexp2 v1.4.1-0.20201116162257-a2a8dda75c91 h1:Izz0+t1Z5nI16/II7vuEo/nHjodOg0p7+OiDpjX5t1E= github.com/dlclark/regexp2 v1.4.1-0.20201116162257-a2a8dda75c91/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc= +github.com/dlclark/regexp2 v1.11.0 h1:G/nrcoOa7ZXlpoa/91N3X7mM3r8eIlMBBJZvsz/mxKI= +github.com/dlclark/regexp2 v1.11.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= github.com/dnaeon/go-vcr v1.1.0/go.mod h1:M7tiix8f0r6mKKJ3Yq/kqU1OYf3MnfmBWVbPx/yU9ko= github.com/dnaeon/go-vcr v1.2.0/go.mod h1:R4UdLID7HZT3taECzJs4YgbbH6PIGXB6W/sc5OLb6RQ= github.com/docker/docker v1.6.2/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= @@ -273,6 +304,8 @@ github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymF github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f h1:Y/CXytFA4m6baUTXGLOoWe4PQhGxaX0KpnayAqC48p4= +github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f/go.mod h1:vw97MGsxSvLiUE2X8qFplwetxpGLQrlU1Q9AUEIzCaM= github.com/evmos/cosmos-sdk v0.47.8-evmos h1:XOkJq3MeWDnTrNfDZk1bvCdrmjyyoxyL5gaTrguHPuA= github.com/evmos/cosmos-sdk v0.47.8-evmos/go.mod h1:VTAtthIsmfplanhFfUTfT6ED4F+kkJxT7nmvmKXRthI= github.com/evmos/evmos/v18 v18.1.0 h1:2YaxDtUuNoLhZ8VT4MsOtPsvEFNEWViOa9jNTFgJYhE= @@ -428,6 +461,8 @@ github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5m github.com/googleapis/gax-go/v2 v2.12.0 h1:A+gCJKdRfqXkr+BIRGtZLibNXf0m1f9E4HG56etFpas= github.com/googleapis/gax-go/v2 v2.12.0/go.mod h1:y+aIqrI5eb1YGMVJfuV3185Ts/D7qKpsEkdD5+I6QGU= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= +github.com/gorilla/css v1.0.1 h1:ntNaBIghp6JmvWnxbZKANoLyuXTPZ4cAMlo6RyhlbO8= +github.com/gorilla/css v1.0.1/go.mod h1:BvnYkspnSzMmwRK+b8/xgNPLiIuNZr6vbZBTPQ2A3b0= github.com/gorilla/handlers v1.5.1 h1:9lRY6j8DEeeBT10CvO9hGW0gmky0BprnvDI5vfhUHH4= github.com/gorilla/handlers v1.5.1/go.mod h1:t8XrUpc4KVXb7HGyJ4/cEnwQiaxrX/hz1Zv/4g96P1Q= github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= @@ -473,6 +508,8 @@ github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/hdevalence/ed25519consensus v0.1.0 h1:jtBwzzcHuTmFrQN6xQZn6CQEO/V9f7HsjsjeEZ6auqU= github.com/hdevalence/ed25519consensus v0.1.0/go.mod h1:w3BHWjwJbFU29IRHL1Iqkw3sus+7FctEyM4RqDxYNzo= +github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM= +github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg= github.com/holiman/bloomfilter/v2 v2.0.3 h1:73e0e/V0tCydx14a0SCYS/EWCxgwLZ18CZcZKVu0fao= github.com/holiman/bloomfilter/v2 v2.0.3/go.mod h1:zpoh+gs7qcpqrHr3dB55AMiJwo0iURXE7ZOP9L9hSkA= github.com/holiman/uint256 v1.2.0/go.mod h1:y4ga/t+u+Xwd7CpDgZESaRcWy0I7XMlTMA25ApIH5Jw= @@ -549,6 +586,7 @@ github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/kylelemons/godebug v0.0.0-20170224010052-a616ab194758/go.mod h1:B69LEHPfb2qLo0BaaOLcbitczOKLWTsrBG9LczfCD4k= +github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= github.com/labstack/echo/v4 v4.2.1/go.mod h1:AA49e0DZ8kk5jTOOCKNuPR6oTnBS0dYiM4FW1e6jwpg= github.com/labstack/gommon v0.3.0/go.mod h1:MULnywXg0yavhxWKc+lOruYdAhDwPK9wf0OL7NoOu+k= @@ -562,6 +600,8 @@ github.com/libp2p/go-buffer-pool v0.1.0 h1:oK4mSFcQz7cTQIfqbe4MIj9gLW+mnanjyFtc6 github.com/libp2p/go-buffer-pool v0.1.0/go.mod h1:N+vh8gMqimBzdKkSMVuydVDq+UV5QTWy5HSiZacSbPg= github.com/linxGnu/grocksdb v1.8.5 h1:Okfk5B1h0ikCYdDM7Tc5yJUS8LTwAmMBq5IPWTmOLPs= github.com/linxGnu/grocksdb v1.8.5/go.mod h1:xZCIb5Muw+nhbDK4Y5UJuOrin5MceOuiXkVUR7vp4WY= +github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY= +github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY= github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= @@ -584,9 +624,13 @@ github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2y github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= 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-localereader v0.0.1 h1:ygSAOl7ZXTx4RdPYinUpg6W99U8jWvWi9Ye2JC/oIi4= +github.com/mattn/go-localereader v0.0.1/go.mod h1:8fBrzywKY7BI3czFoHkuzRoWE9C+EiG4R1k4Cjx5p88= github.com/mattn/go-runewidth v0.0.3/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= -github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0= github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= +github.com/mattn/go-runewidth v0.0.12/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk= +github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc= +github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/mattn/go-sqlite3 v1.11.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= github.com/mattn/go-sqlite3 v1.14.23 h1:gbShiuAP1W5j9UOksQ06aiiqPMxYecovVGwmTxWtuw0= github.com/mattn/go-sqlite3 v1.14.23/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= @@ -594,6 +638,8 @@ github.com/mattn/go-tty v0.0.0-20180907095812-13ff1204f104/go.mod h1:XPvLUNfbS4f 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= +github.com/microcosm-cc/bluemonday v1.0.27 h1:MpEUotklkwCSLeH+Qdx1VJgNqLlpY2KXwXFM08ygZfk= +github.com/microcosm-cc/bluemonday v1.0.27/go.mod h1:jFi9vgW+H7c3V0lb6nR74Ib/DIB5OBs92Dimizgw2cA= github.com/miguelmota/go-ethereum-hdwallet v0.1.1 h1:zdXGlHao7idpCBjEGTXThVAtMKs+IxAgivZ75xqkWK0= github.com/miguelmota/go-ethereum-hdwallet v0.1.1/go.mod h1:f9m9uXokAHA6WNoYOPjj4AqjJS5pquQRiYYj/XSyPYc= github.com/mimoo/StrobeGo v0.0.0-20181016162300-f8f6d4d2b643/go.mod h1:43+3pMjjKimDBf5Kr4ZFNGbLql1zKkbImw+fZbw3geM= @@ -618,6 +664,14 @@ github.com/modocache/gover v0.0.0-20171022184752-b58185e213c5/go.mod h1:caMODM3P github.com/mschoch/smat v0.0.0-20160514031455-90eadee771ae/go.mod h1:qAyveg+e4CE+eKJXWVjKXM4ck2QobLqTDytGJbLLhJg= github.com/mtibben/percent v0.2.1 h1:5gssi8Nqo8QU/r2pynCm+hBQHpkB/uNK7BJCFogWdzs= github.com/mtibben/percent v0.2.1/go.mod h1:KG9uO+SZkUp+VkRHsCdYQV3XSZrrSpR3O9ibNBTZrns= +github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 h1:ZK8zHtRHOkbHy6Mmr5D264iyp3TiX5OmNcI5cIARiQI= +github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6/go.mod h1:CJlz5H+gyd6CUWT45Oy4q24RdLyn7Md9Vj2/ldJBSIo= +github.com/muesli/cancelreader v0.2.2 h1:3I4Kt4BQjOR54NavqnDogx/MIoWBFa0StPA8ELUXHmA= +github.com/muesli/cancelreader v0.2.2/go.mod h1:3XuTXfFS2VjM+HTLZY9Ak0l6eUKfijIfMUZ4EgX0QYo= +github.com/muesli/reflow v0.3.0 h1:IFsN6K9NfGtjeggFP+68I4chLZV2yIKsXJFNZ+eWh6s= +github.com/muesli/reflow v0.3.0/go.mod h1:pbwTDkVPibjO2kyvBQRBxTWEEGDGq0FlB1BIKtnHY/8= +github.com/muesli/termenv v0.15.3-0.20240618155329-98d742f6907a h1:2MaM6YC3mGu54x+RKAA6JiFFHlHDY1UbkxqppT7wYOg= +github.com/muesli/termenv v0.15.3-0.20240618155329-98d742f6907a/go.mod h1:hxSnBBYLK21Vtq/PHd0S2FYCxBXzBua8ov5s1RobyRQ= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/naoina/go-stringutil v0.1.0/go.mod h1:XJ2SJL9jCtBh+P9q5btrd/Ylo8XwT/h1USek5+NqSA0= github.com/naoina/toml v0.1.2-0.20170918210437-9fafd6967416/go.mod h1:NBIhNtsFMo3G2szEBne+bO4gS192HuIYRqfvOWb4i1E= @@ -704,6 +758,10 @@ github.com/rakyll/statik v0.1.7/go.mod h1:AlZONWzMtEnMs7W4e/1LURLiI49pIMmp6V9Ung github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 h1:N/ElC8H3+5XpJzTSTfLsJV/mx9Q9g7kxmchpfZyxgzM= github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= github.com/retailnext/hllpp v1.0.1-0.20180308014038-101a6d2f8b52/go.mod h1:RDpi1RftBQPUCDRw6SmxeaREsAaRKnOclghuzp/WRzc= +github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= +github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= +github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ= +github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= github.com/rjeczalik/notify v0.9.1/go.mod h1:rKwnCoCGeuQnwBtTSPL9Dad03Vh2n40ePRrjvIXnJho= github.com/rjeczalik/notify v0.9.3 h1:6rJAzHTGKXGj76sbRgDiDcYj/HniypXmSJo1SWakZeY= github.com/rjeczalik/notify v0.9.3/go.mod h1:gF3zSOrafR9DQEWSE8TjfI9NkooDxbyT4UgRGKZA0lc= @@ -724,6 +782,8 @@ github.com/sagikazarmark/locafero v0.4.0 h1:HApY1R9zGo4DBgr7dqsTH/JJxLTTsOt7u6ke github.com/sagikazarmark/locafero v0.4.0/go.mod h1:Pe1W6UlPYUk/+wc/6KFhbORCfqzgYEpgQ3O5fPuL3H4= github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6gto+ugjYE= github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ= +github.com/sahilm/fuzzy v0.1.1 h1:ceu5RHF8DGgoi+/dR5PsECjCDH1BE3Fnmpo7aVXOdRA= +github.com/sahilm/fuzzy v0.1.1/go.mod h1:VFvziUEIMCrT6A6tw2RFIXPXXmzXbOsSHF0DOI8ZK9Y= github.com/sasha-s/go-deadlock v0.3.1 h1:sqv7fDNShgjcaxkO0JNcOAlr8B9+cV5Ey/OB71efZx0= github.com/sasha-s/go-deadlock v0.3.1/go.mod h1:F73l+cr82YSh10GxyRI6qZiCgK64VaZjwesgfQ1/iLM= github.com/segmentio/kafka-go v0.1.0/go.mod h1:X6itGqS9L4jDletMsxZ7Dz+JFWxM6JHfPOCvTvk+EJo= @@ -832,6 +892,11 @@ github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673/go.mod h1:N3UwUGtsr github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= +github.com/yuin/goldmark v1.7.1/go.mod h1:uzxRWxtg69N339t3louHJ7+O03ezfj6PlliRlaOzY1E= +github.com/yuin/goldmark v1.7.4 h1:BDXOHExt+A7gwPCJgPIIq7ENvceR7we7rOS9TNoLZeg= +github.com/yuin/goldmark v1.7.4/go.mod h1:uzxRWxtg69N339t3louHJ7+O03ezfj6PlliRlaOzY1E= +github.com/yuin/goldmark-emoji v1.0.3 h1:aLRkLHOuBR2czCY4R8olwMjID+tENfhyFDMCRhbIQY4= +github.com/yuin/goldmark-emoji v1.0.3/go.mod h1:tTkZEbwu5wkPmgTcitqddVxY9osFZiavD+r4AzQrh1U= github.com/zbiljic/go-filelock v0.0.0-20170914061330-1dbf7103ab7d h1:XQyeLr7N9iY9mi+TGgsBFkj54+j3fdoo8e2u6zrGP5A= github.com/zbiljic/go-filelock v0.0.0-20170914061330-1dbf7103ab7d/go.mod h1:hoMeDjlNXTNqVwrCk8YDyaBS2g5vFfEX2ezMi4vb6CY= github.com/zondax/hid v0.9.2 h1:WCJFnEDMiqGF64nlZz28E9qLVZ0KSJ7xpc5DLEyma2U= @@ -882,8 +947,8 @@ golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPh golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.24.0 h1:mnl8DM0o513X8fdIkmyFE/5hTYxbwYOjDS/+rK6qpRI= -golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5DM= +golang.org/x/crypto v0.25.0 h1:ypSNr+bnYL2YhwoMt2zPxHFmbAN1KZs/njMG3hxUp30= +golang.org/x/crypto v0.25.0/go.mod h1:T+wALwcMOSE0kXgUAnPAHqTLW+XHgcELELW8VaDgm/M= golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= @@ -949,8 +1014,8 @@ golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qx golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= 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.26.0 h1:soB7SVo0PWrY4vPW/+ay0jKDNScG2X9wFeYlXIvJsOQ= -golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE= +golang.org/x/net v0.27.0 h1:5K3Njcw06/l2y9vpGCSdcxWOYHOUk3dVNGDXN+FvAys= +golang.org/x/net v0.27.0/go.mod h1:dDi0PyhWNoiUOrAS8uXv/vnScO4wnHQO4mj9fn/RytE= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -966,8 +1031,8 @@ golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M= -golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ= +golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -1012,6 +1077,7 @@ golang.org/x/sys v0.0.0-20210324051608-47abb6519492/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210420205809-ac73e9fd8988/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210819135213-f52c844e1c1c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -1022,13 +1088,13 @@ golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20221010170243-090e33056c14/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws= -golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.24.0 h1:Twjiwq9dn6R1fQcyiK+wQyHWfaz/BJB+YIpzU/Cv3Xg= +golang.org/x/sys v0.24.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= 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.21.0 h1:WVXCp+/EBEHOj53Rvu+7KiT/iElMrO8ACK16SMZ3jaA= -golang.org/x/term v0.21.0/go.mod h1:ooXLefLobQVslOqselCNF4SxFAaoS6KujMbsGzSDmX0= +golang.org/x/term v0.22.0 h1:BbsgPEJULsl2fV/AT3v15Mjva5yXKQDyKf+TbDz7QJk= +golang.org/x/term v0.22.0/go.mod h1:F3qCibpT5AMpCRfhfT53vVJwhLtIVHhB9XDjfFvnMI4= 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= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= diff --git a/main.go b/main.go index 114e9ba..29a30ae 100644 --- a/main.go +++ b/main.go @@ -1,6 +1,8 @@ package main -import "github.com/hanchon/hanchond/cmd" +import ( + "github.com/hanchon/hanchond/cmd" +) func main() { cmd.Execute() diff --git a/playground/explorer/database.go b/playground/explorer/database.go index a096f23..38a5206 100644 --- a/playground/explorer/database.go +++ b/playground/explorer/database.go @@ -9,11 +9,19 @@ import ( "github.com/hanchon/hanchond/playground/explorer/database" ) +type cache struct { + valid bool + blocks []database.Block + txns []database.Transaction +} + type Database struct { db *sql.DB queries *database.Queries mutex *sync.Mutex ctx context.Context + + cache cache } func NewDatabase(ctx context.Context, db *sql.DB, queries *database.Queries) *Database { @@ -22,9 +30,35 @@ func NewDatabase(ctx context.Context, db *sql.DB, queries *database.Queries) *Da queries: queries, mutex: &sync.Mutex{}, ctx: ctx, + + cache: cache{valid: false}, } } +func (d *Database) GetDisplayInfo(limit int) ([]database.Block, []database.Transaction, error) { + d.mutex.Lock() + defer d.mutex.Unlock() + + if d.cache.valid { + return d.cache.blocks, d.cache.txns, nil + } + + blocks, err := d.queries.GetLimitedBlocks(d.ctx, int64(limit)) + if err != nil { + return []database.Block{}, []database.Transaction{}, err + } + txns, err := d.queries.GetLimitedTransactions(d.ctx, int64(limit)) + if err != nil { + return []database.Block{}, []database.Transaction{}, err + } + + d.cache.valid = true + d.cache.blocks = blocks + d.cache.txns = txns + + return blocks, txns, nil +} + func (d *Database) GetLatestBlock() (database.Block, error) { d.mutex.Lock() defer d.mutex.Unlock() @@ -43,6 +77,7 @@ func (d *Database) AddBlocks(blocks []Block) error { func (d *Database) AddBlock(b Block) error { d.mutex.Lock() defer d.mutex.Unlock() + d.cache.valid = false // SQL Transaction: to avoid corrupting the db when closing the program tx, err := d.db.Begin() if err != nil { diff --git a/playground/explorer/database/explorerquery.sql b/playground/explorer/database/explorerquery.sql index 318ca70..d379174 100644 --- a/playground/explorer/database/explorerquery.sql +++ b/playground/explorer/database/explorerquery.sql @@ -26,3 +26,6 @@ SELECT * FROM transactions; -- name: GetLimitedTransactions :many SELECT * FROM transactions ORDER BY id DESC LIMIT ?; +-- name: GetLimitedBlocks :many +SELECT * FROM blocks ORDER BY id DESC LIMIT ?; + diff --git a/playground/explorer/database/explorerquery.sql.go b/playground/explorer/database/explorerquery.sql.go index d424ad1..2d4fd4c 100644 --- a/playground/explorer/database/explorerquery.sql.go +++ b/playground/explorer/database/explorerquery.sql.go @@ -34,6 +34,38 @@ func (q *Queries) GetLatestBlock(ctx context.Context) (Block, error) { return i, err } +const getLimitedBlocks = `-- name: GetLimitedBlocks :many +SELECT id, height, txcount, hash FROM blocks ORDER BY id DESC LIMIT ? +` + +func (q *Queries) GetLimitedBlocks(ctx context.Context, limit int64) ([]Block, error) { + rows, err := q.db.QueryContext(ctx, getLimitedBlocks, limit) + if err != nil { + return nil, err + } + defer rows.Close() + var items []Block + for rows.Next() { + var i Block + if err := rows.Scan( + &i.ID, + &i.Height, + &i.Txcount, + &i.Hash, + ); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Close(); err != nil { + return nil, err + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} + const getLimitedTransactions = `-- name: GetLimitedTransactions :many SELECT id, cosmoshash, ethhash, typeurl, sender, blockheight FROM transactions ORDER BY id DESC LIMIT ? ` diff --git a/playground/explorer/explorer.go b/playground/explorer/explorer.go index 9535eb8..16efaa1 100644 --- a/playground/explorer/explorer.go +++ b/playground/explorer/explorer.go @@ -4,6 +4,7 @@ import ( "context" "fmt" "log" + "sync" "github.com/hanchon/hanchond/lib/converter" "github.com/hanchon/hanchond/lib/protoencoder/codec" @@ -14,14 +15,18 @@ import ( ) type Client struct { + mutex sync.Mutex web3Endpoint string cosmosEndpoint string - client *requester.Client + Client *requester.Client ctx context.Context - db *Database + DB *Database + + NetworkHeight int + DBHeight int } func NewLocalExplorerClient(web3Port, cosmosPort int, homeFolder string) *Client { @@ -31,36 +36,55 @@ func NewLocalExplorerClient(web3Port, cosmosPort int, homeFolder string) *Client } c := &Client{ + mutex: sync.Mutex{}, web3Endpoint: fmt.Sprintf("http://localhost:%d", web3Port), cosmosEndpoint: fmt.Sprintf("http://localhost:%d", cosmosPort), ctx: context.Background(), + NetworkHeight: 0, + DBHeight: 0, } - c.db = NewDatabase(c.ctx, db, queries) - c.client = requester.NewClient().WithUnsecureWeb3Endpoint(c.web3Endpoint).WithUnsecureRestEndpoint(c.cosmosEndpoint) + c.DB = NewDatabase(c.ctx, db, queries) + c.Client = requester.NewClient().WithUnsecureWeb3Endpoint(c.web3Endpoint).WithUnsecureRestEndpoint(c.cosmosEndpoint) return c } +// ProcessMissingBlocks process up to 500 blocks at the time func (c *Client) ProcessMissingBlocks(startBlock int64) error { + if !c.mutex.TryLock() { + return nil + } + defer c.mutex.Unlock() blocksData := []Block{} - currentBlockDB := startBlock + nextBlockToIndex := startBlock - // TODO: Delete the last bock in case the last execution was not completed - block, err := c.db.GetLatestBlock() + block, err := c.DB.GetLatestBlock() if err == nil { if block.Height > startBlock { - currentBlockDB = block.Height + 1 + nextBlockToIndex = block.Height + 1 } } - networkHeight, err := c.client.GetBlockNumber() + networkHeight, err := c.Client.GetBlockNumber() if err != nil { return fmt.Errorf("error getting latest block: %s", err.Error()) } - // TODO: if network height < current sleep and retry - for networkHeight > currentBlockDB { - blockData, err := c.client.GetBlockCosmos(fmt.Sprintf("0x%x", currentBlockDB)) + c.DBHeight = int(block.Height) + c.NetworkHeight = int(networkHeight) + + if networkHeight < nextBlockToIndex { + // We are up to date + return nil + } + + if networkHeight-500 > nextBlockToIndex { + // Batch no more than 500 blocks + networkHeight = nextBlockToIndex + 500 + } + + for networkHeight >= nextBlockToIndex { + blockData, err := c.Client.GetBlockCosmos(fmt.Sprintf("0x%x", nextBlockToIndex)) if err != nil { return fmt.Errorf("error getting cosmos block: %s", err.Error()) } @@ -69,7 +93,7 @@ func (c *Client) ProcessMissingBlocks(startBlock int64) error { return fmt.Errorf("error getting cosmos block hash: %s", err.Error()) } - data := NewBlock(currentBlockDB, int64(len(blockData.Block.Data.Txs)), blockHash) + data := NewBlock(nextBlockToIndex, int64(len(blockData.Block.Data.Txs)), blockHash) for i, txBase64 := range blockData.Block.Data.Txs { tx, err := codec.Base64ToTx(txBase64) @@ -105,10 +129,16 @@ func (c *Client) ProcessMissingBlocks(startBlock int64) error { } data.AddTransaction(i, cosmosTxHash, ethTxHash, typeURL, sender) } - currentBlockDB++ + nextBlockToIndex++ blocksData = append(blocksData, *data) } // Save it to the database - return c.db.AddBlocks(blocksData) + err = c.DB.AddBlocks(blocksData) + if err != nil { + return err + } + + c.DBHeight = int(nextBlockToIndex) + return nil } diff --git a/playground/explorer/explorerui/block_data.go b/playground/explorer/explorerui/block_data.go new file mode 100644 index 0000000..7cf4737 --- /dev/null +++ b/playground/explorer/explorerui/block_data.go @@ -0,0 +1,60 @@ +package explorerui + +import ( + "encoding/json" + "fmt" + + "github.com/charmbracelet/bubbles/list" + "github.com/hanchon/hanchond/playground/explorer" + "github.com/hanchon/hanchond/playground/explorer/database" +) + +type Block struct { + text string + desc string + hash string + height int64 +} + +func (i Block) Title() string { return i.text } +func (i Block) Description() string { return i.desc } +func (i Block) FilterValue() string { return i.text } + +func BDBlockToItem(blocks []database.Block) []list.Item { + res := make([]list.Item, len(blocks)) + for k := range res { + res[k] = Block{ + text: fmt.Sprintf("%d", blocks[k].Height), + desc: fmt.Sprintf("%s...%s", blocks[k].Hash[0:4], blocks[k].Hash[len(blocks[k].Hash)-5:]), + height: blocks[k].Height, + hash: blocks[k].Hash, + } + } + return res +} + +func RenderBlock(b Block, client *explorer.Client) string { + blockData, err := client.Client.GetBlockCosmos(fmt.Sprintf("%d", b.height)) + if err != nil { + return "# Error getting block info\n\n" + err.Error() + } + + data, err := json.MarshalIndent(blockData, "", " ") + if err != nil { + return "# Error getting block info\n\n" + err.Error() + } + + cosmosBlock := fmt.Sprintf("# Block %d\n\n## Cosmos Block\n\n```json\n%s\n```", b.height, processJSON(string(data))) + + ethBlock, err := client.Client.GetBlockByNumber(fmt.Sprintf("%d", b.height), true) + if err != nil { + return "# Error getting eth block info\n\n" + err.Error() + } + + data, err = json.MarshalIndent(ethBlock.Result, "", " ") + if err != nil { + return "# Error getting block info\n\n" + err.Error() + } + + return cosmosBlock + fmt.Sprintf("\n\n## Ethereum Block\n\n```json\n%s\n```", processJSON(string(data))) +} diff --git a/playground/explorer/explorerui/constants.go b/playground/explorer/explorerui/constants.go new file mode 100644 index 0000000..ffe1636 --- /dev/null +++ b/playground/explorer/explorerui/constants.go @@ -0,0 +1,33 @@ +package explorerui + +import "github.com/charmbracelet/lipgloss" + +var ColorHighlight = lipgloss.AdaptiveColor{Light: "#874BFD", Dark: "#7D56F4"} + +var ( + ColorLowPink = lipgloss.Color("205") + ColorHighPink = lipgloss.Color("201") + ColorPurple = lipgloss.Color("111") +) + +var noClosingBot = lipgloss.Border{ + Top: "─", + Bottom: "─", + Left: "│", + Right: "│", + TopLeft: "╭", + TopRight: "╮", + BottomLeft: "│", + BottomRight: "│", +} + +var noClosingTopBot = lipgloss.Border{ + Top: "─", + Bottom: "─", + Left: "│", + Right: "│", + TopLeft: "│", + TopRight: "│", + BottomLeft: "│", + BottomRight: "│", +} diff --git a/playground/explorer/explorerui/container.go b/playground/explorer/explorerui/container.go new file mode 100644 index 0000000..8a23ba2 --- /dev/null +++ b/playground/explorer/explorerui/container.go @@ -0,0 +1,55 @@ +package explorerui + +import ( + "github.com/charmbracelet/lipgloss" +) + +var botContainerStyle = lipgloss.NewStyle(). + Height(25). + AlignVertical(lipgloss.Center). + Align(lipgloss.Center). + Border(lipgloss.NormalBorder(), true). + BorderTop(false). + BorderForeground(ColorHighlight) + +var test = lipgloss.NewStyle(). + Foreground(ColorLowPink). + Align(lipgloss.Center) + +var ( + blocksFrame = lipgloss.NewStyle().Border(lipgloss.NormalBorder(), true).Height(24) + txFrame = lipgloss.NewStyle().Border(lipgloss.NormalBorder(), true).Height(24) + infoFrame = lipgloss.NewStyle().Border(lipgloss.NormalBorder(), true).Height(24) +) + +func BotContainer(width int, list1, list2 string, infoText string, activeFrame int) string { + blocks := blocksFrame.Width(20) + transactions := txFrame.Width((width-20)/2 - 2) + info := infoFrame.Width((width-20)/2 - 4) + switch activeFrame { + case 0: + blocks = blocks.BorderForeground(ColorHighPink) + case 1: + transactions = transactions.BorderForeground(ColorHighPink) + case 2: + info = info.BorderForeground(ColorHighPink) + } + + temp := lipgloss.JoinHorizontal( + lipgloss.Center, + blocks.Render(list1), + transactions.Render(list2), + info.Render(infoText), + ) + + return botContainerStyle. + Height(26). + Width(width). + Render( + lipgloss.JoinVertical( + lipgloss.Center, + test.Render("\"tab\": to change panels || \"enter\": to select || \"q\": to quit"), + temp, + ), + ) +} diff --git a/playground/explorer/explorerui/header.go b/playground/explorer/explorerui/header.go new file mode 100644 index 0000000..23ab831 --- /dev/null +++ b/playground/explorer/explorerui/header.go @@ -0,0 +1,16 @@ +package explorerui + +import "github.com/charmbracelet/lipgloss" + +var headerStyle = lipgloss.NewStyle(). + Foreground(ColorHighPink). + Height(1). + Padding(1, 1). + Align(lipgloss.Center). + Border(noClosingBot, true). + Bold(true). + BorderForeground(ColorHighlight) + +func Header(width int) string { + return headerStyle.Width(width).Render("Block Explorer") +} diff --git a/playground/explorer/explorerui/height_frame.go b/playground/explorer/explorerui/height_frame.go new file mode 100644 index 0000000..eeaf999 --- /dev/null +++ b/playground/explorer/explorerui/height_frame.go @@ -0,0 +1,93 @@ +package explorerui + +import ( + "strconv" + + "github.com/charmbracelet/bubbles/progress" + "github.com/charmbracelet/lipgloss" +) + +var chainFrameStyle = lipgloss.NewStyle(). + Foreground(ColorPurple). + Height(1). + Align(lipgloss.Center). + BorderForeground(ColorHighlight) + +var chainFramStyleText = lipgloss.NewStyle(). + Foreground(ColorPurple). + Height(1). + Align(lipgloss.Left) + +var ( + graphModel = progress.New(progress.WithDefaultGradient()) + graphModelWidth = graphModel.Width +) + +var heightFrameStyle = lipgloss.NewStyle(). + Padding(0, 3). + Border(lipgloss.NormalBorder(), false, true, false, false). + BorderForeground(ColorHighlight). + Width(graphModelWidth). + AlignHorizontal(lipgloss.Right) + +var graphFrameStyle = lipgloss.NewStyle(). + PaddingTop(1). + PaddingLeft(1). + Height(4) + +func ChainHeightFrame(width, currentHeightInt, indexerHeightInt int) string { + currentHeight := strconv.Itoa(currentHeightInt) + bHeight := lipgloss.JoinHorizontal( + lipgloss.Left, + chainFramStyleText.Render("۩ Current Height: "), + chainFramStyleText.Render(currentHeight), + ) + + indexerHeight := strconv.Itoa(indexerHeightInt) + for range len(currentHeight) - len(indexerHeight) { + indexerHeight = " " + indexerHeight + } + + iHeight := lipgloss.JoinHorizontal( + lipgloss.Left, + chainFrameStyle.Render("֍ Indexer Height: "), + chainFrameStyle.Render(indexerHeight), + ) + + heightFrame := heightFrameStyle. + Render( + lipgloss.JoinVertical( + lipgloss.Center, + chainFrameStyle.Bold(true).Foreground(ColorLowPink).Render("Blocks Info"), + bHeight, + iHeight, + ), + ) + + graphFrame := graphFrameStyle.Render( + graphModel.ViewAs(float64(indexerHeightInt) / float64(currentHeightInt)), + ) + + content := lipgloss.JoinHorizontal( + lipgloss.Top, + heightFrame, + graphFrame, + ) + + return lipgloss.JoinVertical( + lipgloss.Top, + chainFrameStyle. + Border(noClosingTopBot, false, true, false). + Foreground(lipgloss.Color("32")). + PaddingBottom(1). + Bold(true). + Width(width). + Render("Chain Height"), + chainFrameStyle. + Border(noClosingTopBot, false, true, true). + Align(lipgloss.Center). + AlignVertical(lipgloss.Center). + Width(width). + Render(content), + ) +} diff --git a/playground/explorer/explorerui/model.go b/playground/explorer/explorerui/model.go new file mode 100644 index 0000000..131cb19 --- /dev/null +++ b/playground/explorer/explorerui/model.go @@ -0,0 +1,142 @@ +package explorerui + +import ( + "fmt" + "time" + + "github.com/charmbracelet/bubbles/list" + "github.com/charmbracelet/bubbles/viewport" + tea "github.com/charmbracelet/bubbletea" + "github.com/charmbracelet/glamour" + "github.com/charmbracelet/lipgloss" + explorerClient "github.com/hanchon/hanchond/playground/explorer" +) + +var mdRendered *glamour.TermRenderer + +var basicStyle = lipgloss.NewStyle(). + Foreground(lipgloss.Color("205")). + Align(lipgloss.Center). + AlignVertical(lipgloss.Center). + AlignHorizontal(lipgloss.Center) + +type explorerModel struct { + width int + height int + + activeList int + lists []list.Model + viewport viewport.Model + + mdValues string + + client *explorerClient.Client + startingHeight int64 + + resolutionError bool +} + +func (m explorerModel) Init() tea.Cmd { + return nil +} + +type tickMsg struct{} + +func indexerTickerCmd() tea.Cmd { + return tea.Tick(300*time.Millisecond, func(_ time.Time) tea.Msg { + return tickMsg{} + }) +} + +func (m explorerModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) { + switch v := msg.(type) { + case tickMsg: + // If it is already running it will return a no-op + go m.client.ProcessMissingBlocks(m.startingHeight) //nolint: errcheck + b, t, err := m.client.DB.GetDisplayInfo(50) + if err == nil { + m.lists[0].SetItems(BDBlockToItem(b)) + m.lists[1].SetItems(BDTxToItem(t)) + } + return m, indexerTickerCmd() + case tea.WindowSizeMsg: + m.height = v.Height + m.width = v.Width - 2 + m.mdValues = fmt.Sprintf("%d %d", msg.(tea.WindowSizeMsg).Height, msg.(tea.WindowSizeMsg).Width) + basicStyle = basicStyle. + Width(m.width - 2). + Height(m.height) + + if m.height < 48 || m.width+2 < 190 { + m.resolutionError = true + } else { + m.resolutionError = false + } + return m, indexerTickerCmd() + case tea.KeyMsg: + key := v.String() + if key == "ctrl+c" || key == "q" { + return m, tea.Quit + } + if key == "tab" { + m.activeList = (m.activeList + 1) % 3 + return m, nil + } + + if key == "shift+tab" { + m.activeList = (m.activeList - 1) % 3 + return m, nil + } + if key == "enter" { + switch m.activeList { + case 0: + selectedItem := m.lists[0].SelectedItem().(Block) + info, _ := mdRendered.Render(RenderBlock(selectedItem, m.client)) + m.viewport.SetContent(info) + _ = m.viewport.GotoTop() + return m, nil + case 1: + selectedItem := m.lists[1].SelectedItem().(Txn) + info, _ := mdRendered.Render(RenderTx(selectedItem, m.client)) + m.viewport.SetContent(info) + _ = m.viewport.GotoTop() + return m, nil + } + } + } + var cmd tea.Cmd + switch m.activeList { + case 0: + m.lists[0], cmd = m.lists[0].Update(msg) + return m, cmd + case 1: + m.lists[1], cmd = m.lists[1].Update(msg) + return m, cmd + case 2: + m.viewport, cmd = m.viewport.Update(msg) + return m, cmd + } + + return m, nil +} + +func (m explorerModel) View() string { + if m.resolutionError { + return lipgloss.NewStyle(). + Width(m.width). + Height(m.height). + Foreground(ColorHighPink). + Align(lipgloss.Center). + AlignVertical(lipgloss.Center). + Render("Your resolution is too low, please reduce the zoom to display the dashboard") + } + + value := lipgloss.JoinVertical( + lipgloss.Top, + Header(m.width-4), + ChainHeightFrame(m.width-4, m.client.NetworkHeight, m.client.DBHeight), + BotContainer(m.width-4, m.lists[0].View(), m.lists[1].View(), m.viewport.View(), m.activeList), + ) + + return basicStyle.Render(value) +} diff --git a/playground/explorer/explorerui/trucate.go b/playground/explorer/explorerui/trucate.go new file mode 100644 index 0000000..03aa620 --- /dev/null +++ b/playground/explorer/explorerui/trucate.go @@ -0,0 +1,16 @@ +package explorerui + +import "regexp" + +func shortenWord(word string) string { + if len(word) > 600 { + return word[:10] + "..." + word[len(word)-10:] + } + return word +} + +func processJSON(input string) string { + wordRegex := regexp.MustCompile(`\S{601,}`) + result := wordRegex.ReplaceAllStringFunc(input, shortenWord) + return result +} diff --git a/playground/explorer/explorerui/tui.go b/playground/explorer/explorerui/tui.go new file mode 100644 index 0000000..f2b0147 --- /dev/null +++ b/playground/explorer/explorerui/tui.go @@ -0,0 +1,44 @@ +package explorerui + +import ( + "github.com/charmbracelet/bubbles/list" + "github.com/charmbracelet/bubbles/viewport" + tea "github.com/charmbracelet/bubbletea" + "github.com/charmbracelet/glamour" + "github.com/charmbracelet/lipgloss" + + explorerClient "github.com/hanchon/hanchond/playground/explorer" +) + +func CreateExplorerTUI(startHeight int, client *explorerClient.Client) *tea.Program { + mdRendered, _ = glamour.NewTermRenderer( + glamour.WithAutoStyle(), + glamour.WithWordWrap(78), + ) + + m := explorerModel{ + client: client, + mdValues: "", + } + + list1 := list.New([]list.Item{}, list.NewDefaultDelegate(), 20, 14) + list1.Title = "Latest Blocks" + list1.SetWidth(20) + list1.SetHeight(23) + list1.Styles.TitleBar.Align(lipgloss.Center) + + list2 := list.New([]list.Item{}, list.NewDefaultDelegate(), 20, 14) + list2.Title = "Latest Transactions" + list2.SetWidth(80) + list2.SetHeight(23) + + m.lists = append(m.lists, list1) + m.lists = append(m.lists, list2) + + m.viewport = viewport.New(78, 23) + m.startingHeight = int64(startHeight) + + go client.ProcessMissingBlocks(int64(startHeight)) //nolint: errcheck + + return tea.NewProgram(m) +} diff --git a/playground/explorer/explorerui/txn_data.go b/playground/explorer/explorerui/txn_data.go new file mode 100644 index 0000000..2741a98 --- /dev/null +++ b/playground/explorer/explorerui/txn_data.go @@ -0,0 +1,93 @@ +package explorerui + +import ( + "encoding/json" + "fmt" + "strings" + + "github.com/charmbracelet/bubbles/list" + "github.com/hanchon/hanchond/playground/explorer" + "github.com/hanchon/hanchond/playground/explorer/database" +) + +type Txn struct { + cosmosHash string + ethHash string + typeURL string + sender string + blockHeight int +} + +func (i Txn) Title() string { + if i.ethHash != "" { + return i.ethHash + } + return i.cosmosHash +} + +func (i Txn) Description() string { + return i.typeURL +} + +// TODO: this should filter by everything +func (i Txn) FilterValue() string { return strings.ToLower(i.typeURL) } + +func BDTxToItem(txns []database.Transaction) []list.Item { + res := make([]list.Item, len(txns)) + for k := range res { + res[k] = Txn{ + cosmosHash: txns[k].Cosmoshash, + ethHash: txns[k].Ethhash, + typeURL: txns[k].Typeurl, + sender: txns[k].Sender, + blockHeight: int(txns[k].Blockheight), + } + } + return res +} + +func RenderTx(b Txn, client *explorer.Client) string { + cosmosTX, err := client.Client.GetCosmosTx(b.cosmosHash) + if err != nil { + return "# Error getting cosmos tx\n\n" + err.Error() + } + + data, err := json.MarshalIndent(cosmosTX, "", " ") + if err != nil { + return "# Error getting cosmos tx\n\n" + err.Error() + } + + if !strings.Contains(b.typeURL, "ethermint.evm.v1.MsgEthereumTx") { + return fmt.Sprintf("# Transaction Details\n\n## Cosmos TX:\n- Status: %v\n- TxHash: %s\n```json\n%s\n```", cosmosTX.TxResponse.Code == 0, b.cosmosHash, string(data)) + } + + ethReceipt, err := client.Client.GetTransactionReceipt(b.ethHash) + if err != nil { + return "# Error getting eth receipt\n\n" + err.Error() + } + + ethReceiptString, err := json.MarshalIndent(ethReceipt.Result, "", " ") + if err != nil { + return "# Error getting eth receipt\n\n" + err.Error() + } + + ethTrace, err := client.Client.GetTransactionTrace(b.ethHash) + if err != nil { + return "# Error getting eth trace\n\n" + err.Error() + } + + ethTraceString, err := json.MarshalIndent(ethTrace.Result, "", " ") + if err != nil { + return "# Error getting eth trace\n\n" + err.Error() + } + + return fmt.Sprintf("# Transaction Details\n\n## Ethereum Transaction:\n- Status: %v\n- TxHash: %s\n ### Receipt:\n```json\n%s\n```\n### Trace:\n```json\n%s\n```\n## Cosmos Transaction:\n- Status: %v\n- TxHash: %s\n```json\n%s\n```", + ethReceipt.Result.Status == "0x1", + b.ethHash, + processJSON(string(ethReceiptString)), + processJSON(string(ethTraceString)), + cosmosTX.TxResponse.Code == 0, + b.cosmosHash, + processJSON(string(data)), + ) +} diff --git a/vocs.config.ts b/vocs.config.ts index 3f25372..e42c43c 100644 --- a/vocs.config.ts +++ b/vocs.config.ts @@ -260,6 +260,16 @@ export default defineConfig({ }, ], }, + { + text: "Indexer", + collapsed: true, + items: [ + { + text: "Block Explorer", + link: "/hanchond/playground/explorer/ui", + }, + ], + }, { text: "Examples", collapsed: true,