-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(connect): introduce ignite connect (#102)
- Loading branch information
1 parent
df7f61a
commit 7867ed9
Showing
24 changed files
with
2,754 additions
and
5 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
# Connect App Changelog | ||
|
||
## [`v0.1.0`](https://github.com/ignite/apps/releases/tag/connect/v0.1.0) | ||
|
||
* First release of the Connect app compatible with Ignite >= v28.x.y |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
# Connect | ||
|
||
This Ignite App extends [Ignite CLI](https://github.com/ignite/cli) to let a user interact with any Cosmos SDK based chain. | ||
|
||
## Installation | ||
|
||
```shell | ||
ignite app install -g github.com/ignite/apps/connect | ||
``` | ||
|
||
### Usage | ||
|
||
* Discover available chains | ||
|
||
```shell | ||
ignite connect discover | ||
``` | ||
|
||
* Add a chain to interact with | ||
|
||
```shell | ||
ignite connect add atomone | ||
``` | ||
|
||
* (Or) Add a local chain to interact with | ||
|
||
```shell | ||
ignite connect add simapp localhost:9090 | ||
``` | ||
|
||
* List all connected chains | ||
|
||
```shell | ||
ignite connect | ||
``` | ||
|
||
* Remove a connected chain | ||
|
||
```shell | ||
ignite connect rm atomone | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,87 @@ | ||
package chains | ||
|
||
import ( | ||
"fmt" | ||
"os" | ||
"path" | ||
|
||
"gopkg.in/yaml.v3" | ||
|
||
igniteconfig "github.com/ignite/cli/v28/ignite/config" | ||
) | ||
|
||
var ( | ||
configName = "connect.yaml" | ||
|
||
// ErrConfigNotFound is returned when the config file is not found | ||
ErrConfigNotFound = fmt.Errorf("config file not found") | ||
) | ||
|
||
type Config struct { | ||
Chains map[string]*ChainConfig `yaml:"chains"` | ||
} | ||
|
||
type ChainConfig struct { | ||
ChainID string `yaml:"chain_id"` | ||
Bech32Prefix string `yaml:"bech32_prefix"` | ||
GRPCEndpoint string `yaml:"grpc_endpoint"` | ||
} | ||
|
||
func (c *Config) Save() error { | ||
out, err := yaml.Marshal(c) | ||
if err != nil { | ||
return fmt.Errorf("failed to marshal config: %w", err) | ||
} | ||
|
||
configDir, err := ConfigDir() | ||
if err != nil { | ||
return err | ||
} | ||
|
||
connectConfigPath := path.Join(configDir, configName) | ||
if err := os.WriteFile(connectConfigPath, out, 0o644); err != nil { | ||
return fmt.Errorf("error saving config: %w", err) | ||
} | ||
|
||
return nil | ||
} | ||
|
||
func ReadConfig() (*Config, error) { | ||
configDir, err := ConfigDir() | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
connectConfigPath := path.Join(configDir, configName) | ||
if _, err := os.Stat(connectConfigPath); os.IsNotExist(err) { | ||
return &Config{map[string]*ChainConfig{}}, ErrConfigNotFound | ||
} else if err != nil { | ||
return nil, fmt.Errorf("failed to check config file: %w", err) | ||
} | ||
|
||
data, err := os.ReadFile(connectConfigPath) | ||
if err != nil { | ||
return nil, fmt.Errorf("failed to read config: %w", err) | ||
} | ||
|
||
var c Config | ||
if err := yaml.Unmarshal(data, &c); err != nil { | ||
return nil, fmt.Errorf("failed to unmarshal config: %w", err) | ||
} | ||
|
||
return &c, nil | ||
} | ||
|
||
func ConfigDir() (string, error) { | ||
igniteConfigDir, err := igniteconfig.DirPath() | ||
if err != nil { | ||
return "", fmt.Errorf("failed to get ignite config directory: %w", err) | ||
} | ||
|
||
dir := path.Join(igniteConfigDir, "apps", "connect") | ||
if err := os.MkdirAll(dir, 0o755); err != nil { | ||
return "", fmt.Errorf("failed to create config directory: %w", err) | ||
} | ||
|
||
return dir, nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,164 @@ | ||
package chains | ||
|
||
import ( | ||
"context" | ||
"crypto/tls" | ||
"fmt" | ||
"os" | ||
"path" | ||
|
||
authv1betav1 "cosmossdk.io/api/cosmos/auth/v1beta1" | ||
autocliv1 "cosmossdk.io/api/cosmos/autocli/v1" | ||
reflectionv1 "cosmossdk.io/api/cosmos/reflection/v1" | ||
"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" | ||
) | ||
|
||
type Conn struct { | ||
chainName string | ||
config *ChainConfig | ||
configDir string | ||
client *grpc.ClientConn | ||
|
||
ProtoFiles *protoregistry.Files | ||
ModuleOptions map[string]*autocliv1.ModuleOptions | ||
} | ||
|
||
func NewConn(chainName string, cfg *ChainConfig) (*Conn, error) { | ||
configDir, err := ConfigDir() | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
return &Conn{ | ||
chainName: chainName, | ||
config: cfg, | ||
configDir: configDir, | ||
}, nil | ||
} | ||
|
||
// fdsCacheFilename returns the filename for the cached file descriptor set. | ||
func (c *Conn) fdsCacheFilename() string { | ||
return path.Join(c.configDir, fmt.Sprintf("%s.fds", c.chainName)) | ||
} | ||
|
||
// appOptsCacheFilename returns the filename for the app options cache file. | ||
func (c *Conn) appOptsCacheFilename() string { | ||
return path.Join(c.configDir, fmt.Sprintf("%s.autocli", c.chainName)) | ||
} | ||
|
||
func (c *Conn) Load(ctx context.Context) error { | ||
var err error | ||
fdSet := &descriptorpb.FileDescriptorSet{} | ||
fdsFilename := c.fdsCacheFilename() | ||
|
||
if _, err := os.Stat(fdsFilename); os.IsNotExist(err) { | ||
client, err := c.Connect() | ||
if err != nil { | ||
return err | ||
} | ||
|
||
reflectionClient := reflectionv1.NewReflectionServiceClient(client) | ||
fdRes, err := reflectionClient.FileDescriptors(ctx, &reflectionv1.FileDescriptorsRequest{}) | ||
if err != nil { | ||
return fmt.Errorf("error getting file descriptors: %w", err) | ||
} | ||
|
||
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 := c.appOptsCacheFilename() | ||
if _, err := os.Stat(appOptsFilename); os.IsNotExist(err) { | ||
client, err := c.Connect() | ||
if err != nil { | ||
return err | ||
} | ||
|
||
autocliQueryClient := autocliv1.NewQueryClient(client) | ||
appOptsRes, err := autocliQueryClient.AppOptions(ctx, &autocliv1.AppOptionsRequest{}) | ||
if err != nil { | ||
return fmt.Errorf("error getting autocli config: %w", err) | ||
} | ||
|
||
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 *Conn) Connect() (*grpc.ClientConn, error) { | ||
if c.client != nil { | ||
return c.client, nil | ||
} | ||
|
||
var err error | ||
creds := credentials.NewTLS(&tls.Config{ | ||
MinVersion: tls.VersionTLS12, | ||
}) | ||
|
||
c.client, err = grpc.NewClient(c.config.GRPCEndpoint, grpc.WithTransportCredentials(creds)) | ||
if err != nil { | ||
return nil, fmt.Errorf("failed to connect to gRPC server: %w", err) | ||
} | ||
|
||
// try connection by querying an endpoint | ||
// fallback to insecure if it doesn't work | ||
authClient := authv1betav1.NewQueryClient(c.client) | ||
if _, err = authClient.Params(context.Background(), &authv1betav1.QueryParamsRequest{}); err != nil { | ||
creds = insecure.NewCredentials() | ||
c.client, err = grpc.NewClient(c.config.GRPCEndpoint, grpc.WithTransportCredentials(creds)) | ||
if err != nil { | ||
return nil, fmt.Errorf("failed to connect to gRPC server: %w", err) | ||
} | ||
} | ||
|
||
return c.client, nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
package chains | ||
|
||
// TODO(@julienrbrt): Implement in follow-up. |
Oops, something went wrong.