chore: rm tools/hubl (#23562)
This commit is contained in:
@@ -1 +0,0 @@
|
||||
/hubl
|
||||
@@ -1,32 +0,0 @@
|
||||
<!--
|
||||
Guiding Principles:
|
||||
|
||||
Changelogs are for humans, not machines.
|
||||
There should be an entry for every single version.
|
||||
The same types of changes should be grouped.
|
||||
Versions and sections should be linkable.
|
||||
The latest version comes first.
|
||||
The release date of each version is displayed.
|
||||
Mention whether you follow Semantic Versioning.
|
||||
|
||||
Usage:
|
||||
|
||||
Change log entries are to be added to the Unreleased section under the
|
||||
appropriate stanza (see below). Each entry should ideally include a tag and
|
||||
the Github issue reference in the following format:
|
||||
|
||||
* (<tag>) [#<issue-number>] Changelog message.
|
||||
|
||||
Types of changes (Stanzas):
|
||||
|
||||
"Features" for new features.
|
||||
"Improvements" for changes in existing functionality.
|
||||
"Deprecated" for soon-to-be removed features.
|
||||
"Bug Fixes" for any bug fixes.
|
||||
"API Breaking" for breaking exported APIs used by developers building on SDK.
|
||||
Ref: https://keepachangelog.com/en/1.0.0/
|
||||
-->
|
||||
|
||||
# Changelog
|
||||
|
||||
## [Unreleased]
|
||||
@@ -1,12 +0,0 @@
|
||||
#!/usr/bin/make -f
|
||||
|
||||
all: hubl test
|
||||
|
||||
hubl:
|
||||
go build -mod=readonly ./cmd/hubl
|
||||
@echo "hubl binary has been successfully built in tools/hubl/hubl"
|
||||
|
||||
test:
|
||||
go test -mod=readonly -race ./...
|
||||
|
||||
.PHONY: all hubl test
|
||||
@@ -1,73 +0,0 @@
|
||||
---
|
||||
sidebar_position: 1
|
||||
---
|
||||
|
||||
# Hubl
|
||||
|
||||
`Hubl` is a tool that allows you to query any Cosmos SDK based blockchain.
|
||||
It takes advantage of the new [AutoCLI](https://docs.cosmos.network/main/learn/advanced/autocli) feature of the Cosmos SDK.
|
||||
|
||||
## Installation
|
||||
|
||||
Hubl can be installed using `go install`:
|
||||
|
||||
```shell
|
||||
go install cosmossdk.io/tools/hubl/cmd/hubl@latest
|
||||
```
|
||||
|
||||
Or build from source:
|
||||
|
||||
```shell
|
||||
git clone --depth=1 https://github.com/cosmos/cosmos-sdk
|
||||
make hubl
|
||||
```
|
||||
|
||||
The binary will be located in `tools/hubl`.
|
||||
|
||||
## Usage
|
||||
|
||||
```shell
|
||||
hubl --help
|
||||
```
|
||||
|
||||
### Add chain
|
||||
|
||||
To configure a new chain just run this command using the --init flag and the name of the chain as it's listed in the chain registry (<https://github.com/cosmos/chain-registry>).
|
||||
|
||||
If the chain is not listed in the chain registry, you can use any unique name.
|
||||
|
||||
```shell
|
||||
hubl init [chain-name]
|
||||
hubl init regen
|
||||
```
|
||||
|
||||
The chain configuration is stored in `~/.hubl/config.toml`.
|
||||
|
||||
:::tip
|
||||
|
||||
When using an insecure gRPC endpoint, change the `insecure` field to `true` in the config file.
|
||||
|
||||
```toml
|
||||
[chains]
|
||||
[chains.regen]
|
||||
[[chains.regen.trusted-grpc-endpoints]]
|
||||
endpoint = 'localhost:9090'
|
||||
insecure = true
|
||||
```
|
||||
|
||||
Or use the `--insecure` flag:
|
||||
|
||||
```shell
|
||||
hubl init regen --insecure
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
### Query
|
||||
|
||||
To query a chain, you can use the `query` command.
|
||||
Then specify which module you want to query and the query itself.
|
||||
|
||||
```shell
|
||||
hubl regen query auth module-accounts
|
||||
```
|
||||
@@ -1,16 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"cosmossdk.io/tools/hubl/internal"
|
||||
)
|
||||
|
||||
func main() {
|
||||
cmd, err := internal.RootCommand()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
if err = cmd.Execute(); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
@@ -1,165 +0,0 @@
|
||||
module cosmossdk.io/tools/hubl
|
||||
|
||||
go 1.23.0
|
||||
|
||||
require (
|
||||
cosmossdk.io/api v0.7.6
|
||||
cosmossdk.io/client/v2 v2.0.0-beta.1.0.20240118210941-3897926e722e
|
||||
cosmossdk.io/core v0.11.1
|
||||
cosmossdk.io/errors v1.0.1
|
||||
github.com/cosmos/cosmos-sdk v0.50.11
|
||||
github.com/manifoldco/promptui v0.9.0
|
||||
github.com/pelletier/go-toml/v2 v2.2.3
|
||||
github.com/spf13/cobra v1.8.1
|
||||
google.golang.org/grpc v1.70.0
|
||||
google.golang.org/protobuf v1.36.4
|
||||
)
|
||||
|
||||
require (
|
||||
cosmossdk.io/collections v0.4.0 // indirect
|
||||
cosmossdk.io/depinject v1.1.0 // indirect
|
||||
cosmossdk.io/log v1.5.0 // indirect
|
||||
cosmossdk.io/math v1.5.0 // indirect
|
||||
cosmossdk.io/store v1.1.1 // indirect
|
||||
cosmossdk.io/x/tx v1.0.0-alpha.3 // indirect
|
||||
filippo.io/edwards25519 v1.1.0 // indirect
|
||||
github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4 // indirect
|
||||
github.com/99designs/keyring v1.2.2 // indirect
|
||||
github.com/DataDog/datadog-go v3.2.0+incompatible // indirect
|
||||
github.com/DataDog/zstd v1.5.6 // indirect
|
||||
github.com/beorn7/perks v1.0.1 // indirect
|
||||
github.com/bgentry/speakeasy v0.2.0 // indirect
|
||||
github.com/bytedance/sonic v1.12.8 // indirect
|
||||
github.com/bytedance/sonic/loader v0.2.3 // indirect
|
||||
github.com/cenkalti/backoff/v4 v4.2.1 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
||||
github.com/chzyer/readline v1.5.1 // indirect
|
||||
github.com/cloudwego/base64x v0.1.5 // indirect
|
||||
github.com/cockroachdb/apd/v3 v3.2.1 // indirect
|
||||
github.com/cockroachdb/errors v1.11.3 // indirect
|
||||
github.com/cockroachdb/fifo v0.0.0-20240816210425-c5d0cb0b6fc0 // indirect
|
||||
github.com/cockroachdb/logtags v0.0.0-20241215232642-bb51bb14a506 // indirect
|
||||
github.com/cockroachdb/pebble v1.1.2 // indirect
|
||||
github.com/cockroachdb/redact v1.1.5 // indirect
|
||||
github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06 // indirect
|
||||
github.com/cometbft/cometbft v0.38.15 // indirect
|
||||
github.com/cometbft/cometbft-db v0.14.1 // indirect
|
||||
github.com/cosmos/btcutil v1.0.5 // indirect
|
||||
github.com/cosmos/cosmos-db v1.1.1 // indirect
|
||||
github.com/cosmos/cosmos-proto v1.0.0-beta.5 // indirect
|
||||
github.com/cosmos/go-bip39 v1.0.0 // indirect
|
||||
github.com/cosmos/gogogateway v1.2.0 // indirect
|
||||
github.com/cosmos/gogoproto v1.7.0 // indirect
|
||||
github.com/cosmos/iavl v1.2.2 // indirect
|
||||
github.com/cosmos/ics23/go v0.11.0 // indirect
|
||||
github.com/cosmos/ledger-cosmos-go v0.13.3 // indirect
|
||||
github.com/danieljoos/wincred v1.2.0 // indirect
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
|
||||
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0 // indirect
|
||||
github.com/desertbit/timer v0.0.0-20180107155436-c41aec40b27f // indirect
|
||||
github.com/dgraph-io/badger/v4 v4.2.0 // indirect
|
||||
github.com/dgraph-io/ristretto v0.1.1 // indirect
|
||||
github.com/dustin/go-humanize v1.0.1 // indirect
|
||||
github.com/dvsekhvalnov/jose2go v1.6.0 // indirect
|
||||
github.com/emicklei/dot v1.6.2 // indirect
|
||||
github.com/fatih/color v1.18.0 // indirect
|
||||
github.com/felixge/httpsnoop v1.0.4 // indirect
|
||||
github.com/fsnotify/fsnotify v1.8.0 // indirect
|
||||
github.com/getsentry/sentry-go v0.30.0 // indirect
|
||||
github.com/go-kit/kit v0.13.0 // indirect
|
||||
github.com/go-kit/log v0.2.1 // indirect
|
||||
github.com/go-logfmt/logfmt v0.6.0 // indirect
|
||||
github.com/godbus/dbus v0.0.0-20190726142602-4481cbc300e2 // indirect
|
||||
github.com/gogo/googleapis v1.4.1 // indirect
|
||||
github.com/gogo/protobuf v1.3.2 // indirect
|
||||
github.com/golang/glog v1.2.4 // indirect
|
||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
|
||||
github.com/golang/protobuf v1.5.4 // indirect
|
||||
github.com/golang/snappy v0.0.4 // indirect
|
||||
github.com/google/btree v1.1.3 // indirect
|
||||
github.com/google/flatbuffers v1.12.1 // indirect
|
||||
github.com/google/go-cmp v0.6.0 // indirect
|
||||
github.com/google/orderedcode v0.0.1 // indirect
|
||||
github.com/gorilla/handlers v1.5.2 // indirect
|
||||
github.com/gorilla/mux v1.8.1 // indirect
|
||||
github.com/gorilla/websocket v1.5.3 // indirect
|
||||
github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 // indirect
|
||||
github.com/grpc-ecosystem/grpc-gateway v1.16.0 // indirect
|
||||
github.com/gsterjov/go-libsecret v0.0.0-20161001094733-a6f4afe4910c // indirect
|
||||
github.com/hashicorp/go-hclog v1.6.3 // indirect
|
||||
github.com/hashicorp/go-immutable-radix v1.3.1 // indirect
|
||||
github.com/hashicorp/go-metrics v0.5.4 // indirect
|
||||
github.com/hashicorp/go-plugin v1.6.2 // indirect
|
||||
github.com/hashicorp/golang-lru v1.0.2 // indirect
|
||||
github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect
|
||||
github.com/hashicorp/hcl v1.0.0 // indirect
|
||||
github.com/hashicorp/yamux v0.1.2 // indirect
|
||||
github.com/hdevalence/ed25519consensus v0.2.0 // indirect
|
||||
github.com/huandu/skiplist v1.2.1 // indirect
|
||||
github.com/iancoleman/strcase v0.3.0 // indirect
|
||||
github.com/improbable-eng/grpc-web v0.15.0 // indirect
|
||||
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||
github.com/jmhodges/levigo v1.0.0 // indirect
|
||||
github.com/klauspost/compress v1.17.11 // indirect
|
||||
github.com/klauspost/cpuid/v2 v2.2.9 // indirect
|
||||
github.com/kr/pretty v0.3.1 // indirect
|
||||
github.com/kr/text v0.2.0 // indirect
|
||||
github.com/lib/pq v1.10.9 // indirect
|
||||
github.com/linxGnu/grocksdb v1.9.7 // indirect
|
||||
github.com/magiconair/properties v1.8.9 // indirect
|
||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/minio/highwayhash v1.0.3 // indirect
|
||||
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
||||
github.com/mtibben/percent v0.2.1 // indirect
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
||||
github.com/oasisprotocol/curve25519-voi v0.0.0-20230904125328-1f23a7beb09a // indirect
|
||||
github.com/oklog/run v1.1.0 // indirect
|
||||
github.com/petermattis/goid v0.0.0-20240813172612-4fcff4a6cae7 // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
|
||||
github.com/prometheus/client_golang v1.20.5 // indirect
|
||||
github.com/prometheus/client_model v0.6.1 // indirect
|
||||
github.com/prometheus/common v0.62.0 // indirect
|
||||
github.com/prometheus/procfs v0.15.1 // indirect
|
||||
github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 // indirect
|
||||
github.com/rogpeppe/go-internal v1.13.1 // indirect
|
||||
github.com/rs/cors v1.11.1 // indirect
|
||||
github.com/rs/zerolog v1.33.0 // indirect
|
||||
github.com/sagikazarmark/locafero v0.4.0 // indirect
|
||||
github.com/sagikazarmark/slog-shim v0.1.0 // indirect
|
||||
github.com/sasha-s/go-deadlock v0.3.5 // indirect
|
||||
github.com/sourcegraph/conc v0.3.0 // indirect
|
||||
github.com/spf13/afero v1.11.0 // indirect
|
||||
github.com/spf13/cast v1.7.1 // indirect
|
||||
github.com/spf13/pflag v1.0.6 // indirect
|
||||
github.com/spf13/viper v1.19.0 // indirect
|
||||
github.com/stretchr/testify v1.10.0 // indirect
|
||||
github.com/subosito/gotenv v1.6.0 // indirect
|
||||
github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d // indirect
|
||||
github.com/tendermint/go-amino v0.16.0 // indirect
|
||||
github.com/tidwall/btree v1.7.0 // indirect
|
||||
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
|
||||
github.com/zondax/hid v0.9.2 // indirect
|
||||
github.com/zondax/ledger-go v0.14.3 // indirect
|
||||
go.etcd.io/bbolt v1.4.0-alpha.0.0.20240404170359-43604f3112c5 // indirect
|
||||
go.opencensus.io v0.24.0 // indirect
|
||||
go.uber.org/multierr v1.11.0 // indirect
|
||||
golang.org/x/arch v0.13.0 // indirect
|
||||
golang.org/x/crypto v0.32.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20250106191152-7588d65b2ba8 // indirect
|
||||
golang.org/x/net v0.34.0 // indirect
|
||||
golang.org/x/sync v0.10.0 // indirect
|
||||
golang.org/x/sys v0.29.0 // indirect
|
||||
golang.org/x/term v0.28.0 // indirect
|
||||
golang.org/x/text v0.21.0 // indirect
|
||||
google.golang.org/genproto v0.0.0-20240227224415-6ceb2ff114de // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20241202173237-19429a94021a // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250122153221-138b5a5a4fd4 // indirect
|
||||
gopkg.in/ini.v1 v1.67.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
gotest.tools/v3 v3.5.1 // indirect
|
||||
nhooyr.io/websocket v1.8.7 // indirect
|
||||
pgregory.net/rapid v1.1.0 // indirect
|
||||
sigs.k8s.io/yaml v1.4.0 // indirect
|
||||
)
|
||||
-1054
File diff suppressed because it is too large
Load Diff
@@ -1,298 +0,0 @@
|
||||
package internal
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"strings"
|
||||
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/reflection/grpc_reflection_v1alpha"
|
||||
"google.golang.org/protobuf/proto"
|
||||
"google.golang.org/protobuf/reflect/protoreflect"
|
||||
"google.golang.org/protobuf/reflect/protoregistry"
|
||||
"google.golang.org/protobuf/types/descriptorpb"
|
||||
|
||||
autocliv1 "cosmossdk.io/api/cosmos/autocli/v1"
|
||||
reflectionv1beta1 "cosmossdk.io/api/cosmos/base/reflection/v1beta1"
|
||||
)
|
||||
|
||||
// loadFileDescriptorsGRPCReflection attempts to load the file descriptor set using gRPC reflection when cosmos.reflection.v1
|
||||
// is unavailable.
|
||||
func loadFileDescriptorsGRPCReflection(ctx context.Context, client *grpc.ClientConn) (*descriptorpb.FileDescriptorSet, error) {
|
||||
fmt.Printf("This chain does not support cosmos.reflection.v1 yet... attempting to use a fallback. Some features may be unsupported and it may not be possible to read all data.\n")
|
||||
|
||||
var interfaceImplNames []string
|
||||
cosmosReflectBetaClient := reflectionv1beta1.NewReflectionServiceClient(client)
|
||||
interfacesRes, err := cosmosReflectBetaClient.ListAllInterfaces(ctx, &reflectionv1beta1.ListAllInterfacesRequest{})
|
||||
if err == nil {
|
||||
for _, iface := range interfacesRes.InterfaceNames {
|
||||
implRes, err := cosmosReflectBetaClient.ListImplementations(ctx, &reflectionv1beta1.ListImplementationsRequest{
|
||||
InterfaceName: iface,
|
||||
})
|
||||
if err == nil {
|
||||
interfaceImplNames = append(interfaceImplNames, implMsgNameCleanup(implRes.ImplementationMessageNames)...)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
reflectClient, err := grpc_reflection_v1alpha.NewServerReflectionClient(client).ServerReflectionInfo(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
fdMap := map[string]*descriptorpb.FileDescriptorProto{}
|
||||
waitListServiceRes := make(chan *grpc_reflection_v1alpha.ListServiceResponse) //nolint:staticcheck // we want to use the deprecated field
|
||||
waitc := make(chan struct{})
|
||||
go func() {
|
||||
for {
|
||||
in, err := reflectClient.Recv()
|
||||
if errors.Is(err, io.EOF) {
|
||||
// read done.
|
||||
close(waitc)
|
||||
return
|
||||
}
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
switch res := in.MessageResponse.(type) {
|
||||
case *grpc_reflection_v1alpha.ServerReflectionResponse_ErrorResponse:
|
||||
panic(res.ErrorResponse.String()) //nolint:staticcheck // we want to use the deprecated field
|
||||
case *grpc_reflection_v1alpha.ServerReflectionResponse_ListServicesResponse:
|
||||
waitListServiceRes <- res.ListServicesResponse //nolint:staticcheck // we want to use the deprecated field
|
||||
case *grpc_reflection_v1alpha.ServerReflectionResponse_FileDescriptorResponse:
|
||||
_ = processFileDescriptorsResponse(res, fdMap)
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
if err = reflectClient.Send(&grpc_reflection_v1alpha.ServerReflectionRequest{ //nolint:staticcheck // we want to use the deprecated field
|
||||
MessageRequest: &grpc_reflection_v1alpha.ServerReflectionRequest_ListServices{},
|
||||
}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
listServiceRes := <-waitListServiceRes
|
||||
|
||||
for _, response := range listServiceRes.Service { //nolint:staticcheck // we want to use the deprecated field
|
||||
err = reflectClient.Send(&grpc_reflection_v1alpha.ServerReflectionRequest{ //nolint:staticcheck // we want to use the deprecated field
|
||||
MessageRequest: &grpc_reflection_v1alpha.ServerReflectionRequest_FileContainingSymbol{
|
||||
FileContainingSymbol: response.Name, //nolint:staticcheck // we want to use the deprecated field
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
for _, msgName := range interfaceImplNames {
|
||||
err = reflectClient.Send(&grpc_reflection_v1alpha.ServerReflectionRequest{ //nolint:staticcheck // we want to use the deprecated field
|
||||
MessageRequest: &grpc_reflection_v1alpha.ServerReflectionRequest_FileContainingSymbol{
|
||||
FileContainingSymbol: msgName,
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
if err = reflectClient.CloseSend(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
<-waitc
|
||||
|
||||
// we loop through all the file descriptor dependencies to capture any file descriptors we haven't loaded yet
|
||||
cantFind := map[string]bool{}
|
||||
for {
|
||||
missing := missingFileDescriptors(fdMap, cantFind)
|
||||
if len(missing) == 0 {
|
||||
break
|
||||
}
|
||||
|
||||
err = addMissingFileDescriptors(ctx, client, fdMap, missing)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// mark all deps that we aren't able to resolve as can't find, so we don't keep looping and get a 429 error
|
||||
for _, dep := range missing {
|
||||
if fdMap[dep] == nil {
|
||||
cantFind[dep] = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for dep := range cantFind {
|
||||
fmt.Printf("Warning: can't find %s.\n", dep)
|
||||
}
|
||||
|
||||
fdSet := &descriptorpb.FileDescriptorSet{}
|
||||
for _, descriptorProto := range fdMap {
|
||||
fdSet.File = append(fdSet.File, descriptorProto)
|
||||
}
|
||||
|
||||
return fdSet, nil
|
||||
}
|
||||
|
||||
func processFileDescriptorsResponse(res *grpc_reflection_v1alpha.ServerReflectionResponse_FileDescriptorResponse, fdMap map[string]*descriptorpb.FileDescriptorProto) error {
|
||||
for _, bz := range res.FileDescriptorResponse.FileDescriptorProto { //nolint:staticcheck // we want to use the deprecated field
|
||||
fd := &descriptorpb.FileDescriptorProto{}
|
||||
err := proto.Unmarshal(bz, fd)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error unmarshalling file descriptor: %w", err)
|
||||
}
|
||||
|
||||
fdMap[fd.GetName()] = fd
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func missingFileDescriptors(fdMap map[string]*descriptorpb.FileDescriptorProto, cantFind map[string]bool) []string {
|
||||
var missing []string
|
||||
for _, descriptorProto := range fdMap {
|
||||
for _, dep := range descriptorProto.Dependency {
|
||||
if fdMap[dep] == nil && !cantFind[dep] /* skip deps we've marked as can't find */ {
|
||||
missing = append(missing, dep)
|
||||
}
|
||||
}
|
||||
}
|
||||
return missing
|
||||
}
|
||||
|
||||
func addMissingFileDescriptors(ctx context.Context, client *grpc.ClientConn, fdMap map[string]*descriptorpb.FileDescriptorProto, missingFiles []string) error {
|
||||
reflectClient, err := grpc_reflection_v1alpha.NewServerReflectionClient(client).ServerReflectionInfo(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
waitc := make(chan struct{})
|
||||
go func() {
|
||||
for {
|
||||
in, err := reflectClient.Recv()
|
||||
if errors.Is(err, io.EOF) {
|
||||
// read done.
|
||||
close(waitc)
|
||||
return
|
||||
}
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
if res, ok := in.MessageResponse.(*grpc_reflection_v1alpha.ServerReflectionResponse_FileDescriptorResponse); ok {
|
||||
_ = processFileDescriptorsResponse(res, fdMap)
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
for _, file := range missingFiles {
|
||||
err = reflectClient.Send(&grpc_reflection_v1alpha.ServerReflectionRequest{ //nolint:staticcheck // we want to use the deprecated field
|
||||
MessageRequest: &grpc_reflection_v1alpha.ServerReflectionRequest_FileByFilename{
|
||||
FileByFilename: file,
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
err = reflectClient.CloseSend()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
<-waitc
|
||||
return nil
|
||||
}
|
||||
|
||||
func guessAutocli(files *protoregistry.Files) *autocliv1.AppOptionsResponse {
|
||||
fmt.Printf("This chain does not support autocli directly yet. Using some default mappings in the meantime to support a subset of the available services.\n")
|
||||
res := map[string]*autocliv1.ModuleOptions{}
|
||||
files.RangeFiles(func(descriptor protoreflect.FileDescriptor) bool {
|
||||
services := descriptor.Services()
|
||||
n := services.Len()
|
||||
for i := 0; i < n; i++ {
|
||||
service := services.Get(i)
|
||||
serviceName := service.FullName()
|
||||
mapping, ok := defaultAutocliMappings[serviceName]
|
||||
if ok {
|
||||
parts := strings.Split(mapping, " ")
|
||||
numParts := len(parts)
|
||||
if numParts < 2 || numParts > 3 {
|
||||
fmt.Printf("Warning: bad mapping %q found for %q\n", mapping, serviceName)
|
||||
continue
|
||||
}
|
||||
|
||||
modOpts := res[parts[0]]
|
||||
if modOpts == nil {
|
||||
modOpts = &autocliv1.ModuleOptions{}
|
||||
res[parts[0]] = modOpts
|
||||
}
|
||||
|
||||
switch parts[1] {
|
||||
case "query":
|
||||
if modOpts.Query == nil {
|
||||
modOpts.Query = &autocliv1.ServiceCommandDescriptor{
|
||||
SubCommands: map[string]*autocliv1.ServiceCommandDescriptor{},
|
||||
}
|
||||
}
|
||||
if numParts == 3 {
|
||||
modOpts.Query.SubCommands[parts[2]] = &autocliv1.ServiceCommandDescriptor{Service: string(serviceName)}
|
||||
} else {
|
||||
modOpts.Query.Service = string(serviceName)
|
||||
}
|
||||
case "tx":
|
||||
if modOpts.Tx == nil {
|
||||
modOpts.Tx = &autocliv1.ServiceCommandDescriptor{
|
||||
SubCommands: map[string]*autocliv1.ServiceCommandDescriptor{},
|
||||
}
|
||||
}
|
||||
if numParts == 3 {
|
||||
modOpts.Tx.SubCommands[parts[2]] = &autocliv1.ServiceCommandDescriptor{Service: string(serviceName)}
|
||||
} else {
|
||||
modOpts.Tx.Service = string(serviceName)
|
||||
}
|
||||
default:
|
||||
fmt.Printf("Warning: bad mapping %q found for %q\n", mapping, serviceName)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
return true
|
||||
})
|
||||
|
||||
return &autocliv1.AppOptionsResponse{ModuleOptions: res}
|
||||
}
|
||||
|
||||
// Removes the first character "/" from the received name
|
||||
func implMsgNameCleanup(implMessages []string) (cleanImplMessages []string) {
|
||||
for _, implMessage := range implMessages {
|
||||
if len(implMessage) >= 1 && implMessage[0] == '/' {
|
||||
cleanImplMessages = append(cleanImplMessages, implMessage[1:])
|
||||
} else {
|
||||
cleanImplMessages = append(cleanImplMessages, implMessage)
|
||||
}
|
||||
}
|
||||
|
||||
return cleanImplMessages
|
||||
}
|
||||
|
||||
var defaultAutocliMappings = map[protoreflect.FullName]string{
|
||||
"cosmos.auth.v1beta1.Query": "auth query",
|
||||
"cosmos.authz.v1beta1.Query": "authz query",
|
||||
"cosmos.bank.v1beta1.Query": "bank query",
|
||||
"cosmos.distribution.v1beta1.Query": "distribution query",
|
||||
"cosmos.evidence.v1.Query": "evidence query",
|
||||
"cosmos.feegrant.v1beta1.Query": "feegrant query",
|
||||
"cosmos.gov.v1.Query": "gov query",
|
||||
"cosmos.gov.v1beta1.Query": "gov query v1beta1",
|
||||
"cosmos.group.v1.Query": "group query",
|
||||
"cosmos.mint.v1beta1.Query": "mint query",
|
||||
"cosmos.params.v1beta1.Query": "params query",
|
||||
"cosmos.slashing.v1beta1.Query": "slashing query",
|
||||
"cosmos.staking.v1beta1.Query": "staking query",
|
||||
"cosmos.upgrade.v1.Query": "upgrade query",
|
||||
}
|
||||
@@ -1,108 +0,0 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"os"
|
||||
"path"
|
||||
|
||||
"github.com/pelletier/go-toml/v2"
|
||||
|
||||
"cosmossdk.io/errors"
|
||||
"cosmossdk.io/tools/hubl/internal/flags"
|
||||
)
|
||||
|
||||
const (
|
||||
DefaultConfigDirName = ".hubl"
|
||||
GlobalKeyringDirName = "global"
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
Chains map[string]*ChainConfig `toml:"chains"`
|
||||
KeyringBackend string `toml:"keyring-backend"`
|
||||
}
|
||||
|
||||
type ChainConfig struct {
|
||||
GRPCEndpoints []GRPCEndpoint `toml:"trusted-grpc-endpoints"`
|
||||
AddressPrefix string `toml:"address-prefix"`
|
||||
KeyringBackend string `toml:"keyring-backend"`
|
||||
}
|
||||
|
||||
type GRPCEndpoint struct {
|
||||
Endpoint string `toml:"endpoint"`
|
||||
Insecure bool `toml:"insecure"`
|
||||
}
|
||||
|
||||
var EmptyConfig = &Config{
|
||||
Chains: map[string]*ChainConfig{},
|
||||
KeyringBackend: flags.DefaultKeyringBackend,
|
||||
}
|
||||
|
||||
func (cfg *Config) GetKeyringBackend(chainName string) (string, error) {
|
||||
if chainName == GlobalKeyringDirName {
|
||||
return cfg.KeyringBackend, nil
|
||||
} else {
|
||||
chainCfg, ok := cfg.Chains[chainName]
|
||||
if ok {
|
||||
return chainCfg.KeyringBackend, nil
|
||||
}
|
||||
}
|
||||
|
||||
return flags.DefaultKeyringBackend, nil
|
||||
}
|
||||
|
||||
func GetConfigDir() (string, error) {
|
||||
homeDir, err := os.UserHomeDir()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
configDir := path.Join(homeDir, DefaultConfigDirName)
|
||||
if _, err := os.Stat(configDir); os.IsNotExist(err) {
|
||||
return configDir, os.MkdirAll(configDir, 0o750)
|
||||
}
|
||||
|
||||
return configDir, nil
|
||||
}
|
||||
|
||||
func Load(configDir string) (*Config, error) {
|
||||
configPath := configFilename(configDir)
|
||||
|
||||
if _, err := os.Stat(configPath); os.IsNotExist(err) {
|
||||
return EmptyConfig, nil
|
||||
}
|
||||
|
||||
bz, err := os.ReadFile(configPath)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "can't read config file: %s", configPath)
|
||||
}
|
||||
|
||||
config := &Config{}
|
||||
if err = toml.Unmarshal(bz, config); err != nil {
|
||||
return nil, errors.Wrapf(err, "can't load config file: %s", configPath)
|
||||
}
|
||||
|
||||
return config, err
|
||||
}
|
||||
|
||||
func Save(configDir string, config *Config) error {
|
||||
buf := &bytes.Buffer{}
|
||||
enc := toml.NewEncoder(buf)
|
||||
if err := enc.Encode(config); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := os.MkdirAll(configDir, 0o750); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
configPath := configFilename(configDir)
|
||||
if err := os.WriteFile(configPath, buf.Bytes(), 0o600); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func configFilename(configDir string) string {
|
||||
return path.Join(configDir, "config.toml")
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
package flags
|
||||
|
||||
const (
|
||||
FlagInsecure = "insecure"
|
||||
FlagUpdate = "update"
|
||||
FlagConfig = "config"
|
||||
FlagLong = "long"
|
||||
FlagOutput = "output"
|
||||
|
||||
FlagKeyringBackend = "keyring-backend"
|
||||
)
|
||||
|
||||
const (
|
||||
OutputFormatText = "text"
|
||||
OutputFormatJSON = "json"
|
||||
|
||||
DefaultKeyringBackend = "os"
|
||||
)
|
||||
@@ -1,147 +0,0 @@
|
||||
package internal
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"context"
|
||||
"fmt"
|
||||
"path"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
_ "cosmossdk.io/api/cosmos/crypto/ed25519"
|
||||
_ "cosmossdk.io/api/cosmos/crypto/secp256k1"
|
||||
_ "cosmossdk.io/api/cosmos/crypto/secp256r1"
|
||||
"cosmossdk.io/tools/hubl/internal/config"
|
||||
"cosmossdk.io/tools/hubl/internal/flags"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/client"
|
||||
"github.com/cosmos/cosmos-sdk/client/keys"
|
||||
"github.com/cosmos/cosmos-sdk/codec"
|
||||
codectypes "github.com/cosmos/cosmos-sdk/codec/types"
|
||||
cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec"
|
||||
sdkkeyring "github.com/cosmos/cosmos-sdk/crypto/keyring"
|
||||
"github.com/cosmos/cosmos-sdk/types"
|
||||
)
|
||||
|
||||
func getKeyring(chainName string) (sdkkeyring.Keyring, error) {
|
||||
if chainName == "" {
|
||||
chainName = config.GlobalKeyringDirName
|
||||
}
|
||||
|
||||
registry := codectypes.NewInterfaceRegistry()
|
||||
cryptocodec.RegisterInterfaces(registry)
|
||||
cdc := codec.NewProtoCodec(registry)
|
||||
|
||||
configDir, err := config.GetConfigDir()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
cfg, err := config.Load(configDir)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
backend, err := cfg.GetKeyringBackend(chainName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
keyringDir := path.Join(configDir, "keyring", chainName)
|
||||
return sdkkeyring.New(chainName, backend, keyringDir, nil, cdc)
|
||||
}
|
||||
|
||||
func KeyringCmd(chainName string) *cobra.Command {
|
||||
shortDesc := fmt.Sprintf("Keyring management for %s", chainName)
|
||||
if chainName == "" {
|
||||
chainName = config.GlobalKeyringDirName
|
||||
shortDesc = "Global keyring management for Hubl"
|
||||
}
|
||||
|
||||
keyringCmd := &cobra.Command{
|
||||
Use: "keys",
|
||||
Short: shortDesc,
|
||||
PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
|
||||
registry := codectypes.NewInterfaceRegistry()
|
||||
cryptocodec.RegisterInterfaces(registry)
|
||||
cdc := codec.NewProtoCodec(registry)
|
||||
|
||||
configDir, err := config.GetConfigDir()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cfg, err := config.Load(configDir)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
backend, err := cfg.GetKeyringBackend(chainName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if changed := cmd.Flags().Changed(flags.FlagKeyringBackend); changed {
|
||||
b, err := cmd.Flags().GetString(flags.FlagKeyringBackend)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
backend = b
|
||||
}
|
||||
|
||||
keyringDir := path.Join(configDir, "keyring", chainName)
|
||||
inBuf := bufio.NewReader(cmd.InOrStdin())
|
||||
kr, err := sdkkeyring.New(chainName, backend, keyringDir, inBuf, cdc)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// addressCodec, validatorAddressCodec, consensusAddressCodec, err := getAddressCodecFromConfig(cfg, chainName)
|
||||
// if err != nil {
|
||||
// return err
|
||||
// }
|
||||
|
||||
addressPrefix, validatorAddressPrefix, consensusAddressPrefix, err := getAddressPrefixFromConfig(cfg, chainName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
types.GetConfig().SetBech32PrefixForAccount(addressPrefix, addressPrefix+types.PrefixPublic)
|
||||
types.GetConfig().SetBech32PrefixForValidator(validatorAddressPrefix, validatorAddressPrefix+types.PrefixPublic)
|
||||
types.GetConfig().SetBech32PrefixForConsensusNode(consensusAddressPrefix, consensusAddressPrefix+types.PrefixPublic)
|
||||
|
||||
clientCtx := client.Context{}.
|
||||
WithKeyring(kr).
|
||||
WithCodec(cdc).
|
||||
WithKeyringDir(keyringDir).
|
||||
WithInput(inBuf)
|
||||
// WithAddressCodec(addressCodec).
|
||||
// WithValidatorAddressCodec(validatorAddressCodec).
|
||||
// WithConsensusAddressCodec(consensusAddressCodec)
|
||||
|
||||
cmd.SetContext(context.WithValue(context.Background(), client.ClientContextKey, &clientCtx))
|
||||
if err := client.SetCmdClientContext(cmd, clientCtx); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
keyringCmd.AddCommand(
|
||||
keys.AddKeyCommand(),
|
||||
keys.DeleteKeyCommand(),
|
||||
keys.ExportKeyCommand(),
|
||||
keys.ImportKeyCommand(),
|
||||
keys.ImportKeyHexCommand(),
|
||||
keys.ListKeysCmd(),
|
||||
keys.ParseKeyStringCommand(),
|
||||
keys.RenameKeyCommand(),
|
||||
keys.ShowKeysCmd(),
|
||||
)
|
||||
keyringCmd.PersistentFlags().String(flags.FlagKeyringBackend, flags.DefaultKeyringBackend, "Select keyring's backend (os|file|kwallet|pass|test|memory)")
|
||||
keyringCmd.PersistentFlags().String(flags.FlagOutput, flags.OutputFormatText, "Output format (text|json)")
|
||||
|
||||
return keyringCmd
|
||||
}
|
||||
@@ -1,201 +0,0 @@
|
||||
package internal
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"path"
|
||||
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/credentials"
|
||||
"google.golang.org/grpc/credentials/insecure"
|
||||
"google.golang.org/protobuf/proto"
|
||||
"google.golang.org/protobuf/reflect/protodesc"
|
||||
"google.golang.org/protobuf/reflect/protoregistry"
|
||||
"google.golang.org/protobuf/types/descriptorpb"
|
||||
|
||||
authv1betav1 "cosmossdk.io/api/cosmos/auth/v1beta1"
|
||||
autocliv1 "cosmossdk.io/api/cosmos/autocli/v1"
|
||||
reflectionv1 "cosmossdk.io/api/cosmos/reflection/v1"
|
||||
"cosmossdk.io/tools/hubl/internal/config"
|
||||
)
|
||||
|
||||
type ChainInfo struct {
|
||||
client *grpc.ClientConn
|
||||
|
||||
Context context.Context
|
||||
ConfigDir string
|
||||
Chain string
|
||||
Config *config.ChainConfig
|
||||
|
||||
ProtoFiles *protoregistry.Files
|
||||
ModuleOptions map[string]*autocliv1.ModuleOptions
|
||||
}
|
||||
|
||||
func NewChainInfo(configDir, chain string, config *config.ChainConfig) *ChainInfo {
|
||||
return &ChainInfo{
|
||||
Context: context.Background(),
|
||||
Config: config,
|
||||
Chain: chain,
|
||||
ConfigDir: configDir,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *ChainInfo) getCacheDir() (string, error) {
|
||||
cacheDir := path.Join(c.ConfigDir, "cache")
|
||||
return cacheDir, os.MkdirAll(cacheDir, 0o750)
|
||||
}
|
||||
|
||||
func (c *ChainInfo) fdsCacheFilename() (string, error) {
|
||||
cacheDir, err := c.getCacheDir()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return path.Join(cacheDir, fmt.Sprintf("%s.fds", c.Chain)), nil
|
||||
}
|
||||
|
||||
func (c *ChainInfo) appOptsCacheFilename() (string, error) {
|
||||
cacheDir, err := c.getCacheDir()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return path.Join(cacheDir, fmt.Sprintf("%s.autocli", c.Chain)), nil
|
||||
}
|
||||
|
||||
func (c *ChainInfo) Load(reload bool) error {
|
||||
fdSet := &descriptorpb.FileDescriptorSet{}
|
||||
fdsFilename, err := c.fdsCacheFilename()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if _, err := os.Stat(fdsFilename); os.IsNotExist(err) || reload {
|
||||
client, err := c.OpenClient()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
reflectionClient := reflectionv1.NewReflectionServiceClient(client)
|
||||
fdRes, err := reflectionClient.FileDescriptors(c.Context, &reflectionv1.FileDescriptorsRequest{})
|
||||
if err != nil {
|
||||
fdSet, err = loadFileDescriptorsGRPCReflection(c.Context, client)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
fdSet = &descriptorpb.FileDescriptorSet{File: fdRes.Files}
|
||||
}
|
||||
|
||||
bz, err := proto.Marshal(fdSet)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err = os.WriteFile(fdsFilename, bz, 0o600); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
bz, err := os.ReadFile(fdsFilename)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err = proto.Unmarshal(bz, fdSet); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
c.ProtoFiles, err = protodesc.FileOptions{AllowUnresolvable: true}.NewFiles(fdSet)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error building protoregistry.Files: %w", err)
|
||||
}
|
||||
|
||||
appOptsFilename, err := c.appOptsCacheFilename()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if _, err := os.Stat(appOptsFilename); os.IsNotExist(err) || reload {
|
||||
client, err := c.OpenClient()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
autocliQueryClient := autocliv1.NewQueryClient(client)
|
||||
appOptsRes, err := autocliQueryClient.AppOptions(c.Context, &autocliv1.AppOptionsRequest{})
|
||||
if err != nil {
|
||||
appOptsRes = guessAutocli(c.ProtoFiles)
|
||||
}
|
||||
|
||||
bz, err := proto.Marshal(appOptsRes)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := os.WriteFile(appOptsFilename, bz, 0o600); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
c.ModuleOptions = appOptsRes.ModuleOptions
|
||||
} else {
|
||||
bz, err := os.ReadFile(appOptsFilename)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var appOptsRes autocliv1.AppOptionsResponse
|
||||
if err := proto.Unmarshal(bz, &appOptsRes); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
c.ModuleOptions = appOptsRes.ModuleOptions
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *ChainInfo) OpenClient() (*grpc.ClientConn, error) {
|
||||
if c.client != nil {
|
||||
return c.client, nil
|
||||
}
|
||||
|
||||
var res error
|
||||
for _, endpoint := range c.Config.GRPCEndpoints {
|
||||
var creds credentials.TransportCredentials
|
||||
if endpoint.Insecure {
|
||||
creds = insecure.NewCredentials()
|
||||
} else {
|
||||
creds = credentials.NewTLS(&tls.Config{
|
||||
MinVersion: tls.VersionTLS12,
|
||||
})
|
||||
}
|
||||
|
||||
var err error
|
||||
c.client, err = grpc.NewClient(endpoint.Endpoint, grpc.WithTransportCredentials(creds))
|
||||
if err != nil {
|
||||
res = errors.Join(res, err)
|
||||
continue
|
||||
}
|
||||
|
||||
return c.client, nil
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("error loading gRPC client: %w", res)
|
||||
}
|
||||
|
||||
// getAddressPrefix returns the address prefix of the chain.
|
||||
func getAddressPrefix(ctx context.Context, conn grpc.ClientConnInterface) (string, error) {
|
||||
authClient := authv1betav1.NewQueryClient(conn)
|
||||
resp, err := authClient.Bech32Prefix(ctx, &authv1betav1.Bech32PrefixRequest{})
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if resp == nil || resp.Bech32Prefix == "" {
|
||||
return "", errors.New("bech32 account address prefix is not set")
|
||||
}
|
||||
|
||||
return resp.Bech32Prefix, nil
|
||||
}
|
||||
@@ -1,104 +0,0 @@
|
||||
package internal
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/manifoldco/promptui"
|
||||
)
|
||||
|
||||
type ChainRegistryEntry struct {
|
||||
APIs struct {
|
||||
GRPC []*APIEntry `json:"grpc"`
|
||||
} `json:"apis"`
|
||||
}
|
||||
|
||||
type APIEntry struct {
|
||||
Address string
|
||||
Provider string
|
||||
}
|
||||
|
||||
func GetChainRegistryEntry(chain string) (*ChainRegistryEntry, error) {
|
||||
res, err := http.Get(fmt.Sprintf("https://raw.githubusercontent.com/cosmos/chain-registry/master/%v/chain.json", chain))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
bz, err := io.ReadAll(res.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err = res.Body.Close(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
data := &ChainRegistryEntry{}
|
||||
if err = json.Unmarshal(bz, data); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// clean-up the URL
|
||||
cleanEntries := make([]*APIEntry, 0)
|
||||
for i, apiEntry := range data.APIs.GRPC {
|
||||
// clean-up the http(s):// prefix
|
||||
if idx := strings.Index(apiEntry.Address, "://"); idx != -1 {
|
||||
data.APIs.GRPC[i].Address = apiEntry.Address[idx+3:]
|
||||
}
|
||||
|
||||
// remove trailing slashes
|
||||
data.APIs.GRPC[i].Address = strings.TrimSuffix(data.APIs.GRPC[i].Address, "/")
|
||||
|
||||
// remove addresses without a port
|
||||
if !strings.Contains(data.APIs.GRPC[i].Address, ":") {
|
||||
continue
|
||||
}
|
||||
|
||||
cleanEntries = append(cleanEntries, data.APIs.GRPC[i])
|
||||
}
|
||||
|
||||
data.APIs.GRPC = cleanEntries
|
||||
return data, nil
|
||||
}
|
||||
|
||||
func SelectGRPCEndpoints(chain string) (string, error) {
|
||||
entry, err := GetChainRegistryEntry(chain)
|
||||
if err != nil || len(entry.APIs.GRPC) == 0 {
|
||||
if err != nil {
|
||||
// print error here so that user can know what happened and decide what to do next
|
||||
fmt.Printf("Failed to load data for %s in the chain registry: %v\n", chain, err)
|
||||
} else {
|
||||
fmt.Printf("Found empty gRPC endpoint of %s in the chain registry.\n", chain)
|
||||
}
|
||||
fmt.Println("Specify a custom gRPC endpoint manually.")
|
||||
prompt := &promptui.Prompt{
|
||||
Label: "Enter a gRPC endpoint that you trust",
|
||||
}
|
||||
return prompt.Run()
|
||||
}
|
||||
fmt.Printf("Found data for %s in the chain registry\n", chain)
|
||||
|
||||
var items []string
|
||||
for _, apiEntry := range entry.APIs.GRPC {
|
||||
items = append(items, fmt.Sprintf("%s: %s", apiEntry.Provider, apiEntry.Address))
|
||||
}
|
||||
prompt := promptui.SelectWithAdd{
|
||||
Label: fmt.Sprintf("Select a gRPC endpoint that you trust for the %s network", chain),
|
||||
Items: items,
|
||||
AddLabel: "Custom endpoint:",
|
||||
}
|
||||
|
||||
i, ep, err := prompt.Run()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
// user selected a custom endpoint
|
||||
if i == -1 {
|
||||
return ep, nil
|
||||
}
|
||||
|
||||
return entry.APIs.GRPC[i].Address, nil
|
||||
}
|
||||
@@ -1,262 +0,0 @@
|
||||
package internal
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/protobuf/reflect/protoreflect"
|
||||
"google.golang.org/protobuf/reflect/protoregistry"
|
||||
"google.golang.org/protobuf/types/dynamicpb"
|
||||
|
||||
"cosmossdk.io/client/v2/autocli"
|
||||
"cosmossdk.io/client/v2/autocli/flag"
|
||||
"cosmossdk.io/tools/hubl/internal/config"
|
||||
"cosmossdk.io/tools/hubl/internal/flags"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/client"
|
||||
"github.com/cosmos/cosmos-sdk/client/grpc/cmtservice"
|
||||
"github.com/cosmos/cosmos-sdk/crypto/keyring"
|
||||
)
|
||||
|
||||
func InitCmd(config *config.Config, configDir string) *cobra.Command {
|
||||
var insecure bool
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "init <foochain>",
|
||||
Short: "Initialize a new chain",
|
||||
Long: `To configure a new chain, run this command using the --init flag and the name of the chain as it's listed in the chain registry (https://github.com/cosmos/chain-registry).
|
||||
If the chain is not listed in the chain registry, you can use any unique name.`,
|
||||
Args: cobra.ExactArgs(1),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
chainName := strings.ToLower(args[0])
|
||||
return reconfigure(cmd, config, configDir, chainName)
|
||||
},
|
||||
}
|
||||
|
||||
cmd.Flags().BoolVar(&insecure, flags.FlagInsecure, false, "allow setting up insecure gRPC connection")
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
func RemoteCommand(config *config.Config, configDir string) ([]*cobra.Command, error) {
|
||||
commands := []*cobra.Command{}
|
||||
|
||||
for chain, chainConfig := range config.Chains {
|
||||
|
||||
// load chain info
|
||||
chainInfo := NewChainInfo(configDir, chain, chainConfig)
|
||||
if err := chainInfo.Load(false); err != nil {
|
||||
commands = append(commands, RemoteErrorCommand(config, configDir, chain, chainConfig, err))
|
||||
continue
|
||||
}
|
||||
|
||||
// add comet commands
|
||||
cometCmds := cmtservice.NewCometBFTCommands()
|
||||
chainInfo.ModuleOptions[cometCmds.Name()] = cometCmds.AutoCLIOptions()
|
||||
|
||||
appOpts := autocli.AppOptions{
|
||||
ModuleOptions: chainInfo.ModuleOptions,
|
||||
}
|
||||
|
||||
addressCodec, validatorAddressCodec, consensusAddressCodec, err := getAddressCodecFromConfig(config, chain)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
kr, err := getKeyring(chain)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
autoCLIKeyring, err := keyring.NewAutoCLIKeyring(kr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
builder := &autocli.Builder{
|
||||
Builder: flag.Builder{
|
||||
TypeResolver: &dynamicTypeResolver{chainInfo},
|
||||
FileResolver: chainInfo.ProtoFiles,
|
||||
AddressCodec: addressCodec,
|
||||
ValidatorAddressCodec: validatorAddressCodec,
|
||||
ConsensusAddressCodec: consensusAddressCodec,
|
||||
Keyring: autoCLIKeyring,
|
||||
},
|
||||
GetClientConn: func(command *cobra.Command) (grpc.ClientConnInterface, error) {
|
||||
return chainInfo.OpenClient()
|
||||
},
|
||||
AddQueryConnFlags: func(command *cobra.Command) {},
|
||||
}
|
||||
|
||||
var (
|
||||
update bool
|
||||
reconfig bool
|
||||
insecure bool
|
||||
output string
|
||||
)
|
||||
|
||||
chainCmd := &cobra.Command{
|
||||
Use: chain,
|
||||
Short: fmt.Sprintf("Commands for the %s chain", chain),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
switch {
|
||||
case reconfig:
|
||||
return reconfigure(cmd, config, configDir, chain)
|
||||
case update:
|
||||
cmd.Printf("Updating AutoCLI data for %s\n", chain)
|
||||
return chainInfo.Load(true)
|
||||
default:
|
||||
return cmd.Help()
|
||||
}
|
||||
},
|
||||
}
|
||||
chainCmd.Flags().BoolVar(&update, flags.FlagUpdate, false, "update the CLI commands for the selected chain (should be used after every chain upgrade)")
|
||||
chainCmd.Flags().BoolVar(&reconfig, flags.FlagConfig, false, "re-configure the selected chain (allows choosing a new gRPC endpoint and refreshes data")
|
||||
chainCmd.Flags().BoolVar(&insecure, flags.FlagInsecure, false, "allow re-configuring the selected chain using an insecure gRPC connection")
|
||||
chainCmd.PersistentFlags().StringVar(&output, flags.FlagOutput, flags.OutputFormatJSON, fmt.Sprintf("output format (%s|%s)", flags.OutputFormatText, flags.OutputFormatJSON))
|
||||
|
||||
// add chain specific keyring
|
||||
chainCmd.AddCommand(KeyringCmd(chainInfo.Chain))
|
||||
|
||||
// add client context
|
||||
clientCtx := client.Context{}.WithKeyring(kr)
|
||||
chainCmd.SetContext(context.WithValue(context.Background(), client.ClientContextKey, &clientCtx))
|
||||
|
||||
if err := appOpts.EnhanceRootCommandWithBuilder(chainCmd, builder); err != nil {
|
||||
// when enriching the command with autocli fails, we add a command that
|
||||
// will print the error and allow the user to reconfigure the chain instead
|
||||
chainCmd.RunE = func(cmd *cobra.Command, args []string) error {
|
||||
cmd.Printf("Error while loading AutoCLI data for %s: %+v\n", chain, err)
|
||||
cmd.Printf("Attempt to reconfigure the chain using the %s flag\n", flags.FlagConfig)
|
||||
if cmd.Flags().Changed(flags.FlagConfig) {
|
||||
return reconfigure(cmd, config, configDir, chain)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
commands = append(commands, chainCmd)
|
||||
}
|
||||
|
||||
return commands, nil
|
||||
}
|
||||
|
||||
func RemoteErrorCommand(cfg *config.Config, configDir, chain string, chainConfig *config.ChainConfig, err error) *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: chain,
|
||||
Short: fmt.Sprintf("Unable to load %s data", chain),
|
||||
Long: fmt.Sprintf("Unable to load %s data, reconfiguration needed.", chain),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
cmd.Printf("Error loading chain data for %s: %+v\n", chain, err)
|
||||
return reconfigure(cmd, cfg, configDir, chain)
|
||||
},
|
||||
}
|
||||
|
||||
cmd.Flags().Bool(flags.FlagInsecure, chainConfig.GRPCEndpoints[0].Insecure, "allow setting up insecure gRPC connection")
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
func reconfigure(cmd *cobra.Command, cfg *config.Config, configDir, chain string) error {
|
||||
insecure, _ := cmd.Flags().GetBool(flags.FlagInsecure)
|
||||
|
||||
cmd.Printf("Configuring %s\n", chain)
|
||||
endpoint, err := SelectGRPCEndpoints(chain)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cmd.Printf("%s endpoint selected\n", endpoint)
|
||||
chainConfig := &config.ChainConfig{
|
||||
GRPCEndpoints: []config.GRPCEndpoint{
|
||||
{
|
||||
Endpoint: endpoint,
|
||||
Insecure: insecure,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
chainInfo := NewChainInfo(configDir, chain, chainConfig)
|
||||
if err = chainInfo.Load(true); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
client, err := chainInfo.OpenClient()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
addressPrefix, err := getAddressPrefix(context.Background(), client)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
chainConfig.KeyringBackend = flags.DefaultKeyringBackend
|
||||
chainConfig.AddressPrefix = addressPrefix
|
||||
cfg.Chains[chain] = chainConfig
|
||||
|
||||
if err := config.Save(configDir, cfg); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cmd.Printf("Configuration saved to %s\n", configDir)
|
||||
return nil
|
||||
}
|
||||
|
||||
type dynamicTypeResolver struct {
|
||||
*ChainInfo
|
||||
}
|
||||
|
||||
var (
|
||||
_ protoregistry.MessageTypeResolver = dynamicTypeResolver{}
|
||||
_ protoregistry.ExtensionTypeResolver = dynamicTypeResolver{}
|
||||
)
|
||||
|
||||
func (d dynamicTypeResolver) FindMessageByName(message protoreflect.FullName) (protoreflect.MessageType, error) {
|
||||
desc, err := d.ProtoFiles.FindDescriptorByName(message)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return dynamicpb.NewMessageType(desc.(protoreflect.MessageDescriptor)), nil
|
||||
}
|
||||
|
||||
func (d dynamicTypeResolver) FindMessageByURL(url string) (protoreflect.MessageType, error) {
|
||||
if i := strings.LastIndexByte(url, '/'); i >= 0 {
|
||||
url = url[i+len("/"):]
|
||||
}
|
||||
|
||||
return d.FindMessageByName(protoreflect.FullName(url))
|
||||
}
|
||||
|
||||
func (d dynamicTypeResolver) FindExtensionByName(field protoreflect.FullName) (protoreflect.ExtensionType, error) {
|
||||
desc, err := d.ProtoFiles.FindDescriptorByName(field)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return dynamicpb.NewExtensionType(desc.(protoreflect.ExtensionTypeDescriptor)), nil
|
||||
}
|
||||
|
||||
func (d dynamicTypeResolver) FindExtensionByNumber(message protoreflect.FullName, field protoreflect.FieldNumber) (protoreflect.ExtensionType, error) {
|
||||
desc, err := d.ProtoFiles.FindDescriptorByName(message)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
messageDesc := desc.(protoreflect.MessageDescriptor)
|
||||
exts := messageDesc.Extensions()
|
||||
n := exts.Len()
|
||||
for i := 0; i < n; i++ {
|
||||
ext := exts.Get(i)
|
||||
if ext.Number() == field {
|
||||
return dynamicpb.NewExtensionType(ext), nil
|
||||
}
|
||||
}
|
||||
|
||||
return nil, protoregistry.NotFound
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
package internal
|
||||
|
||||
import (
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"cosmossdk.io/tools/hubl/internal/config"
|
||||
)
|
||||
|
||||
func RootCommand() (*cobra.Command, error) {
|
||||
configDir, err := config.GetConfigDir()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
cfg, err := config.Load(configDir)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "hubl",
|
||||
Short: "Hubl is a CLI for interacting with Cosmos SDK chains",
|
||||
}
|
||||
|
||||
// add commands
|
||||
commands, err := RemoteCommand(cfg, configDir)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
commands = append(
|
||||
commands,
|
||||
InitCmd(cfg, configDir),
|
||||
KeyringCmd(""),
|
||||
VersionCmd(),
|
||||
)
|
||||
|
||||
cmd.AddCommand(commands...)
|
||||
return cmd, nil
|
||||
}
|
||||
@@ -1,43 +0,0 @@
|
||||
package internal
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"cosmossdk.io/core/address"
|
||||
"cosmossdk.io/tools/hubl/internal/config"
|
||||
|
||||
addresscodec "github.com/cosmos/cosmos-sdk/codec/address"
|
||||
)
|
||||
|
||||
// getAddressCodecFromConfig returns the address codecs for the given chain name
|
||||
func getAddressCodecFromConfig(cfg *config.Config, chainName string) (address.Codec, address.Codec, address.Codec, error) {
|
||||
addressPrefix := "cosmos"
|
||||
|
||||
if chainName != config.GlobalKeyringDirName {
|
||||
chainConfig, ok := cfg.Chains[chainName]
|
||||
if !ok {
|
||||
return nil, nil, nil, fmt.Errorf("chain %s not found in config", chainName)
|
||||
}
|
||||
|
||||
addressPrefix = chainConfig.AddressPrefix
|
||||
}
|
||||
|
||||
return addresscodec.NewBech32Codec(addressPrefix),
|
||||
addresscodec.NewBech32Codec(fmt.Sprintf("%svaloper", addressPrefix)),
|
||||
addresscodec.NewBech32Codec(fmt.Sprintf("%svalcons", addressPrefix)),
|
||||
nil
|
||||
}
|
||||
|
||||
// getAddressPrefixFromConfig returns the address prefixes for the given chain name
|
||||
func getAddressPrefixFromConfig(cfg *config.Config, chainName string) (string, string, string, error) {
|
||||
if chainName != config.GlobalKeyringDirName {
|
||||
chainConfig, ok := cfg.Chains[chainName]
|
||||
if !ok {
|
||||
return "", "", "", fmt.Errorf("chain %s not found in config", chainName)
|
||||
}
|
||||
|
||||
return chainConfig.AddressPrefix, fmt.Sprintf("%svaloper", chainConfig.AddressPrefix), fmt.Sprintf("%svalcons", chainConfig.AddressPrefix), nil
|
||||
}
|
||||
|
||||
return "cosmos", "cosmosvaloper", "cosmosvalcons", nil
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
package internal
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"runtime/debug"
|
||||
"strings"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"cosmossdk.io/tools/hubl/internal/flags"
|
||||
)
|
||||
|
||||
func VersionCmd() *cobra.Command {
|
||||
var long bool
|
||||
|
||||
versionCmd := &cobra.Command{
|
||||
Use: "version",
|
||||
Short: "Display hubl version",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
version, ok := debug.ReadBuildInfo()
|
||||
if !ok {
|
||||
return errors.New("failed to get hubl version")
|
||||
}
|
||||
|
||||
cmd.Printf("hubl version: %s\n", strings.TrimSpace(version.Main.Version))
|
||||
|
||||
if long {
|
||||
for _, dep := range version.Deps {
|
||||
if dep.Path == "cosmossdk.io/client/v2" {
|
||||
cmd.Printf("client/v2 version: %s\n", strings.TrimSpace(dep.Version))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
versionCmd.Flags().BoolVar(&long, flags.FlagLong, false, "display long version information")
|
||||
|
||||
return versionCmd
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
sonar.projectKey=cosmos-sdk-tools-hubl
|
||||
sonar.organization=cosmos
|
||||
|
||||
sonar.projectName=Cosmos SDK - Hubl
|
||||
sonar.project.monorepo.enabled=true
|
||||
|
||||
sonar.sources=.
|
||||
sonar.exclusions=**/*_test.go,**/*.pb.go,**/*.pulsar.go,**/*.pb.gw.go
|
||||
sonar.coverage.exclusions=**/*_test.go,**/testutil/**,**/*.pb.go,**/*.pb.gw.go,**/*.pulsar.go,test_helpers.go,docs/**
|
||||
sonar.tests=.
|
||||
sonar.test.inclusions=**/*_test.go
|
||||
sonar.go.coverage.reportPaths=coverage.out
|
||||
|
||||
sonar.sourceEncoding=UTF-8
|
||||
sonar.scm.provider=git
|
||||
sonar.scm.forceReloadAll=true
|
||||
Reference in New Issue
Block a user