chore: rm tools/hubl (#23562)

This commit is contained in:
Alex | Interchain Labs 2025-01-30 12:51:44 -05:00 committed by GitHub
parent 97c61cfb24
commit b87acd2cc0
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
33 changed files with 7 additions and 2695 deletions

1
.github/CODEOWNERS vendored
View File

@ -35,7 +35,6 @@
/store/ @cosmos/sdk-core-dev
/store/v2/ @cosmos/sdk-core-dev
/types/mempool/ @cosmos/sdk-core-dev
/tools/hubl @julienrbrt @JulianToledano @cosmos/sdk-core-dev
/tools/cosmovisor @julienrbrt @cosmos/sdk-core-dev
/tools/confix @julienrbrt @cosmos/sdk-core-dev
/tests/integration/aminojson @cosmos/sdk-core-dev

View File

@ -160,15 +160,6 @@ updates:
labels:
- "A:automerge"
- dependencies
- package-ecosystem: gomod
directory: "/tools/hubl"
schedule:
interval: weekly
day: thursday
time: "02:15"
labels:
- "A:automerge"
- dependencies
- package-ecosystem: gomod
directory: "/collections"
schedule:

View File

@ -5,8 +5,6 @@
- tools/confix/**/*
"C:Cosmovisor":
- tools/cosmovisor/**/*
"C:Hubl":
- tools/hubl/**/*
"C:Keys":
- client/keys/**/*
"C:Simulations":

View File

@ -46,5 +46,3 @@ jobs:
run: GOARCH=${{ matrix.go-arch }} make cosmovisor
- name: Build Confix
run: GOARCH=${{ matrix.go-arch }} make confix
- name: Build Hubl
run: GOARCH=${{ matrix.go-arch }} make hubl

View File

@ -630,37 +630,6 @@ jobs:
with:
projectBaseDir: tools/confix/
test-hubl:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
with:
go-version: "1.23"
check-latest: true
cache: true
cache-dependency-path: tools/hubl/go.sum
- uses: technote-space/get-diff-action@v6.1.2
id: git_diff
with:
PATTERNS: |
tools/hubl/**/*.go
tools/hubl/go.mod
tools/hubl/go.sum
- name: tests
if: env.GIT_DIFF
run: |
cd tools/hubl
go test -mod=readonly -timeout 30m -coverprofile=coverage.out -covermode=atomic -tags='norace ledger test_ledger_mock' ./...
- name: sonarcloud
if: ${{ env.GIT_DIFF && !github.event.pull_request.draft && env.SONAR_TOKEN != null }}
uses: SonarSource/sonarcloud-github-action@master
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
with:
projectBaseDir: tools/hubl/
test-store:
runs-on: ubuntu-latest
steps:

View File

@ -49,6 +49,10 @@ Every module contains its own CHANGELOG.md. Please refer to the module you are i
### Bug Fixes
### Removed
* (tools/hub) [#23562](https://github.com/cosmos/cosmos-sdk/pull/23562) Remove `tools/hubl`. A similar tool will be maintained in [ignite](https://www.github.com/ignite/cli).
### API Breaking Changes
* (x/params) [#22995](https://github.com/cosmos/cosmos-sdk/pull/22995) Remove `x/params`. Migrate to the new params system introduced in `v0.47` as demonstrated [here](https://github.com/cosmos/cosmos-sdk/blob/main/UPGRADING.md#xparams).

View File

@ -31,7 +31,7 @@ Welcome to the Cosmos SDK's team roadmap.
* [x] Multi-chain command **(Done)**
* Release a cmd line tool that can be pointed a grpc endpoint which then can produce cmd lines to interact with the chain
* [x] Auto-cli tx support
* Tx support for auto-cli/hubl
* Tx support for auto-cli
* This would fully remove the need for application developers to write cli commands for their modules
* [ ] [Consensus Key Rotation](https://github.com/cosmos/cosmos-sdk/issues/5231)
@ -121,9 +121,8 @@ Issue: https://github.com/cosmos/iavl/issues/548
### Client UX
* [ ] Hubl/AutoCLI
* [ ] AutoCLI
* Objective:
* Allow users to sign and submit transactions using hubl
* Add module support for autocli
* Deprecate/remove legacy cli (optional)
* Progress:

View File

@ -1236,8 +1236,6 @@ For example, assuming you put all your proto files in subfolders inside your roo
If you are using a custom folder structure for your proto files, please reorganize them so that their OS path matches their proto package name.
This is to allow the proto FileDescriptSets to be correctly registered, and this standardized OS import paths allows [Hubl](https://github.com/cosmos/cosmos-sdk/tree/main/tools/hubl) to reflectively talk to any chain.
#### `{accepts,implements}_interface` proto annotations
The SDK is normalizing the strings inside the Protobuf `accepts_interface` and `implements_interface` annotations. We require them to be fully-scoped names. They will soon be used by code generators like Pulsar and Telescope to match which messages can or cannot be packed inside `Any`s.

View File

@ -94,7 +94,7 @@ The keyring is then converted to the `client/v2/autocli/keyring` interface.
If no keyring is provided, the `autocli` generated command will not be able to sign transactions, but will still be able to query the chain.
:::tip
The Cosmos SDK keyring and Hubl keyring both implement the `client/v2/autocli/keyring` interface, thanks to the following wrapper:
The Cosmos SDK keyring implements the `client/v2/autocli/keyring` interface, thanks to the following wrapper:
```go
keyring.NewAutoCLIKeyring(kb)
@ -283,10 +283,6 @@ According to the [Cobra documentation](https://pkg.go.dev/github.com/spf13/cobra
`autocli` lets you generate CLI to your Cosmos SDK-based applications without any cobra boilerplate. It allows you to easily generate CLI commands and flags from your protobuf messages, and provides many options for customising the behavior of your CLI application.
To further enhance your CLI experience with Cosmos SDK-based blockchains, you can use `hubl`. `hubl` is a tool that allows you to query any Cosmos SDK-based blockchain using the new AutoCLI feature of the Cosmos SDK. With `hubl`, you can easily configure a new chain and query modules with just a few simple commands.
For more information on `hubl`, including how to configure a new chain and query a module, see the [Hubl documentation](https://docs.cosmos.network/main/build/tooling/hubl).
# Off-Chain
Off-chain is a `client/v2` package providing functionalities for allowing to sign and verify files with two commands:

1
docs/.gitignore vendored
View File

@ -4,7 +4,6 @@ docs/architecture
docs/rfc
docs/tooling/01-cosmovisor.md
docs/tooling/02-confix.md
docs/tooling/03-hubl.md
docs/core/17-autocli.md
docs/packages/01-depinject.md
docs/packages/02-collections.md

View File

@ -11,7 +11,6 @@ This includes tools for development, operating a node, and ease of use of a Cosm
* [Cosmovisor](./01-cosmovisor.md)
* [Confix](./02-confix.md)
* [Hubl](./03-hubl.md)
* [Rosetta](https://docs.cosmos.network/main/run-node/rosetta)
## Other Tools

View File

@ -3,7 +3,6 @@
find build/modules ! -name '_category_.json' -type f -exec rm -rf {} +
rm -rf build/tooling/01-cosmovisor.md
rm -rf build/tooling/02-confix.md
rm -rf build/tooling/03-hubl.md
rm -rf build/packages/01-depinject.md
rm -rf build/packages/02-collections.md
rm -rf build/packages/03-orm.md

View File

@ -28,7 +28,6 @@ cat ../x/README.md | sed 's/\.\.\/\/build\/building-modules\/README\.md/\/buildi
## Add tooling documentation
cp ../tools/cosmovisor/README.md ./build/tooling/01-cosmovisor.md
cp ../tools/confix/README.md ./build/tooling/02-confix.md
cp ../tools/hubl/README.md ./build/tooling/03-hubl.md
## Add package documentation
cp ../client/v2/README.md ./learn/advanced/17-autocli.md

View File

@ -25,7 +25,6 @@ use (
./runtime/v2
./tools/cosmovisor
./tools/confix
./tools/hubl
./tools/benchmark
./x/accounts
./x/accounts/defaults/base

View File

@ -149,10 +149,6 @@ cosmovisor:
confix:
$(MAKE) -C tools/confix confix
#? hubl: Build hubl
hubl:
$(MAKE) -C tools/hubl hubl
.PHONY: build build-linux-amd64 build-linux-arm64 cosmovisor confix
#? mocks: Generate mock file

View File

@ -1 +0,0 @@
/hubl

View File

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

View File

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

View File

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

View File

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

View File

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

File diff suppressed because it is too large Load Diff

View File

@ -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",
}

View File

@ -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")
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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