Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add use command to "rly keys" #1282

Merged
merged 11 commits into from
Sep 7, 2023
4 changes: 2 additions & 2 deletions cmd/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -395,8 +395,8 @@ func UnmarshalJSONProviderConfig(data []byte, customTypes map[string]reflect.Typ
}

typeName, ok := m["type"].(string)
if !ok {
return nil, errors.New("cannot find type");
if !ok {
return nil, errors.New("cannot find type")
}

var provCfg provider.ProviderConfig
Expand Down
52 changes: 52 additions & 0 deletions cmd/keys.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import (
"github.com/cosmos/cosmos-sdk/crypto/hd"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/relayer/v2/relayer/chains/cosmos"
"github.com/cosmos/relayer/v2/utils"
"github.com/spf13/cobra"
"go.uber.org/zap"
)
Expand All @@ -45,6 +46,7 @@ func keysCmd(a *appState) *cobra.Command {

cmd.AddCommand(
keysAddCmd(a),
keysUseCmd(a),
keysRestoreCmd(a),
keysDeleteCmd(a),
keysListCmd(a),
Expand All @@ -55,6 +57,56 @@ func keysCmd(a *appState) *cobra.Command {
return cmd
}

func keysUseCmd(a *appState) *cobra.Command {

cmd := &cobra.Command{
Use: "use chain_name key_name",
Aliases: []string{"a"},
Short: "Use a key from the keychain associated with a particular chain. Look at ~/.relayer/keys/ibc-0/keyring-test ",
vimystic marked this conversation as resolved.
Show resolved Hide resolved
Args: withUsage(cobra.ExactArgs(2)),
Example: strings.TrimSpace(fmt.Sprintf(`
$ %s keys use ibc-0 key_name`, appName)),
RunE: func(cmd *cobra.Command, args []string) error {
chainName := args[0]
key := args[1]

chain, exists := a.config.Chains[chainName]
if !exists {
return fmt.Errorf("chain %s not found in config", chainName)
}

cc := chain.ChainProvider

info, err := cc.ListAddresses()
if err != nil {
return err
}
value, exists := info[key]

kvpath := []string{utils.Chains, cc.ChainName(), utils.Value, utils.Key}

currentValue, success := utils.GetValueFromPath(a.configPath(), kvpath)

if !success {
return fmt.Errorf("unable to get value from path %s", kvpath)
}

if currentValue == key {
return fmt.Errorf("config is already using %s -> %s for %s", key, value, cc.ChainName())
}

if exists {
fmt.Printf("Config will now use %s -> %s for %s\n", key, value, cc.ChainName())
} else {
return fmt.Errorf("key %s does not exist for chain %s", key, cc.ChainName())
}

return chain.ChainProvider.UseKey(a.configPath(), kvpath, key)
},
}
return cmd
}

// keysAddCmd respresents the `keys add` command
func keysAddCmd(a *appState) *cobra.Command {
cmd := &cobra.Command{
Expand Down
11 changes: 10 additions & 1 deletion relayer/chains/cosmos/keys.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ import (
"errors"
"os"

"github.com/cosmos/relayer/v2/relayer/provider"
"github.com/cosmos/relayer/v2/utils"

ckeys "github.com/cosmos/cosmos-sdk/client/keys"
"github.com/cosmos/cosmos-sdk/crypto/hd"
"github.com/cosmos/cosmos-sdk/crypto/keyring"
Expand All @@ -12,7 +15,6 @@ import (
"github.com/cosmos/relayer/v2/relayer/chains/cosmos/keys/sr25519"
"github.com/cosmos/relayer/v2/relayer/codecs/ethermint"
"github.com/cosmos/relayer/v2/relayer/codecs/injective"
"github.com/cosmos/relayer/v2/relayer/provider"
)

const ethereumCoinType = uint32(60)
Expand Down Expand Up @@ -69,6 +71,13 @@ func (cc *CosmosProvider) AddKey(name string, coinType uint32, signingAlgorithm
return ko, nil
}

// Updates config.yaml chain with the specified key.
// It fails if config.yaml is already using the same key or if the key does not exist
// Note , this is not a runtime update cmd.
func (cc *CosmosProvider) UseKey(configPath string, kvpath []string, key string) error {
return utils.UpdateConfig(configPath, kvpath, key)
}

// RestoreKey converts a mnemonic to a private key and BIP-39 HD Path and persists it to the keystore.
// It fails if there is an existing key with the same address.
func (cc *CosmosProvider) RestoreKey(name, mnemonic string, coinType uint32, signingAlgorithm string) (address string, err error) {
Expand Down
8 changes: 8 additions & 0 deletions relayer/chains/penumbra/keys.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (
"github.com/cosmos/relayer/v2/relayer/codecs/ethermint"
"github.com/cosmos/relayer/v2/relayer/codecs/injective"
"github.com/cosmos/relayer/v2/relayer/provider"
"github.com/cosmos/relayer/v2/utils"
)

const ethereumCoinType = uint32(60)
Expand Down Expand Up @@ -66,6 +67,13 @@ func (cc *PenumbraProvider) AddKey(name string, coinType uint32, signingAlgorith
return ko, nil
}

// Updates config.yaml chain with the specified key.
// It fails if config.yaml is already using the same key or if the key does not exist
// Note , this is not a runtime update cmd.
func (cc *PenumbraProvider) UseKey(configPath string, kvpath []string, key string) error {
return utils.UpdateConfig(configPath, kvpath, key)
}

// RestoreKey converts a mnemonic to a private key and BIP-39 HD Path and persists it to the keystore.
// It fails if there is an existing key with the same address.
func (cc *PenumbraProvider) RestoreKey(name, mnemonic string, coinType uint32, signingAlgorithm string) (address string, err error) {
Expand Down
1 change: 1 addition & 0 deletions relayer/provider/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,7 @@ type KeyProvider interface {
CreateKeystore(path string) error
KeystoreCreated(path string) bool
AddKey(name string, coinType uint32, signingAlgorithm string) (output *KeyOutput, err error)
UseKey(configPath string, kvpath []string, key string) error
RestoreKey(name, mnemonic string, coinType uint32, signingAlgorithm string) (address string, err error)
ShowAddress(name string) (address string, err error)
ListAddresses() (map[string]string, error)
Expand Down
108 changes: 108 additions & 0 deletions utils/configUtil.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
package utils
vimystic marked this conversation as resolved.
Show resolved Hide resolved

import (
"fmt"
"os"

"gopkg.in/yaml.v2"
)

// Constants used in the config utility.Add more as required.
const (
Chains = "chains"
Value = "value"
Key = "key"
)

// convertMap recursively converts map[interface{}]interface{} to map[string]interface{}.
func convertMap(input map[interface{}]interface{}) map[string]interface{} {
result := make(map[string]interface{})
for key, value := range input {
strKey, ok := key.(string)
if !ok {
continue
}
if nestedMap, ok := value.(map[interface{}]interface{}); ok {
result[strKey] = convertMap(nestedMap)
} else {
result[strKey] = value
}
}
return result
}

// ReadConfig reads and parses a YAML configuration file.
func ReadConfig(configPath string) (map[string]interface{}, error) {
content, err := os.ReadFile(configPath)
if err != nil {
return nil, fmt.Errorf("failed to read config file: %w", err)
}

var rawData map[interface{}]interface{}
err = yaml.Unmarshal(content, &rawData)
if err != nil {
return nil, fmt.Errorf("failed to unmarshal YAML: %w", err)
}
return convertMap(rawData), nil
}

// GetValueFromPath retrieves a value from the configuration using a given path.
func GetValueFromPath(configPath string, path []string) (interface{}, bool) {
data, err := ReadConfig(configPath)
if err != nil {
return nil, false
}

current := interface{}(data)
for _, key := range path {
asMap, ok := current.(map[string]interface{})
if !ok {
return nil, false
}

current, ok = asMap[key]
if !ok {
return nil, false
}
}

return current, true
}

// UpdateConfig updates a value in the configuration using a given path.
func UpdateConfig(configPath string, pathKeys []string, newValue interface{}) error {
data, err := ReadConfig(configPath)
if err != nil {
return err
}

currentMap := data
for i, key := range pathKeys {
value, exists := currentMap[key]
if !exists {
return fmt.Errorf("key %s does not exist", key)
}

if i == len(pathKeys)-1 {
currentMap[key] = newValue
} else {
nextMap, ok := value.(map[string]interface{})
if !ok {
return fmt.Errorf("%s does not lead to a nested map", key)
}
currentMap = nextMap
}
}

updatedContent, err := yaml.Marshal(data)
if err != nil {
return fmt.Errorf("failed to marshal updated data: %w", err)
}

err = os.WriteFile(configPath, updatedContent, 0644)
if err != nil {
return fmt.Errorf("failed to write updated config: %w", err)
}

return nil
}
Loading