From f0a3805f5b2bdc0000a6d96597db5161b43b6063 Mon Sep 17 00:00:00 2001 From: Leeren Chang Date: Mon, 7 Oct 2024 14:00:03 -0700 Subject: [PATCH 1/5] feat(cli): add key conversion subcommand --- client/cmd/cmd.go | 1 + client/cmd/flags.go | 28 ++++++++ client/cmd/key_utils.go | 145 +++++++++++++++++++++++++++++++++++++--- client/cmd/keys.go | 88 ++++++++++++++++++++++++ client/cmd/validator.go | 73 ++++++-------------- 5 files changed, 274 insertions(+), 61 deletions(-) create mode 100644 client/cmd/keys.go diff --git a/client/cmd/cmd.go b/client/cmd/cmd.go index 295ac820..91e20cdb 100644 --- a/client/cmd/cmd.go +++ b/client/cmd/cmd.go @@ -26,6 +26,7 @@ func New() *cobra.Command { buildinfo.NewVersionCmd(), newValidatorCmds(), newStatusCmd(), + newKeyCmds(), newRollbackCmd(app.CreateApp), ) } diff --git a/client/cmd/flags.go b/client/cmd/flags.go index b0de2471..63764e1b 100644 --- a/client/cmd/flags.go +++ b/client/cmd/flags.go @@ -120,6 +120,13 @@ func bindStatusFlags(flags *pflag.FlagSet, cfg *StatusConfig) { libcmd.BindHomeFlag(flags, &cfg.HomeDir) } +func bindKeyConvertFlags(cmd *cobra.Command, cfg *keyConfig) { + cmd.Flags().StringVar(&cfg.ValidatorKeyFile, "validator-key-file", "", "Path to the validator key file") + cmd.Flags().StringVar(&cfg.PrivateKeyFile, "private-key-file", "", "Path to the EVM private key env file") + cmd.Flags().StringVar(&cfg.PubKeyHex, "pubkey-hex", "", "Public key in hex format") + cmd.Flags().StringVar(&cfg.PubKeyBase64, "pubkey-base64", "", "Public key in base64 format") +} + func bindRollbackFlags(cmd *cobra.Command, cfg *config.Config) { cmd.Flags().BoolVar(&cfg.RemoveBlock, "hard", false, "remove last block as well as state") } @@ -189,3 +196,24 @@ func validateValidatorUnstakeOnBehalfFlags(cfg stakeConfig) error { "unstake": cfg.StakeAmount, }) } + +func validateKeyConvertFlags(cfg keyConfig) error { + flagMap := map[string]string{ + "validator-key-file": cfg.ValidatorKeyFile, + "private-key-file": cfg.PrivateKeyFile, + "pubkey-hex": cfg.PubKeyHex, + "pubkey-base64": cfg.PubKeyBase64, + } + + for _, value := range flagMap { + if value != "" { + return nil + } + } + + flagNames := make([]string, 0, len(flagMap)) + for flag := range flagMap { + flagNames = append(flagNames, "--"+flag) + } + return fmt.Errorf("at least one of %s must be provided", strings.Join(flagNames, ", ")) +} diff --git a/client/cmd/key_utils.go b/client/cmd/key_utils.go index cb5b0746..236d6884 100644 --- a/client/cmd/key_utils.go +++ b/client/cmd/key_utils.go @@ -4,11 +4,16 @@ import ( "crypto/elliptic" "encoding/base64" "encoding/hex" + "encoding/json" "fmt" + "os" + cosmosk1 "github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1" + cosmostypes "github.com/cosmos/cosmos-sdk/types" "github.com/decred/dcrd/dcrec/secp256k1" "github.com/ethereum/go-ethereum/crypto" + "github.com/joho/godotenv" "github.com/piplabs/story/lib/errors" ) @@ -23,24 +28,85 @@ type KeyInfo struct { Value string `json:"value"` } -func uncompressPubKey(compressedPubKeyBase64 string) (string, error) { - compressedPubKeyBytes, err := base64.StdEncoding.DecodeString(compressedPubKeyBase64) +func loadValidatorFile(path string) ([]byte, error) { + keyFileBytes, err := os.ReadFile(path) if err != nil { - return "", errors.Wrap(err, "failed to decode base64 public key") + return nil, errors.Wrap(err, "failed to read validator key file") } + + var keyData ValidatorKey + if err := json.Unmarshal(keyFileBytes, &keyData); err != nil { + return nil, errors.Wrap(err, "failed to unmarshal validator key file") + } + + privKeyBytes, err := base64.StdEncoding.DecodeString(keyData.PrivKey.Value) + if err != nil { + return nil, errors.Wrap(err, "failed to decode private key") + } + return privKeyBytes, nil +} + +func loadPrivKeyFile(path string) ([]byte, error) { + envMap, err := godotenv.Read(path) + if err != nil { + return nil, errors.Wrap(err, "failed to read .env file") + } + + privateKey, exists := envMap["PRIVATE_KEY"] + if !exists || privateKey == "" { + return nil, errors.New("no private key found in file") + } + + privKeyBytes, err := hex.DecodeString(privateKey) + if err != nil { + return nil, errors.Wrap(err, "failed to decode private key") + } + + return privKeyBytes, nil +} + +func privKeyFileToCmpPubKey(path string) ([]byte, error) { + privKeyBytes, err := loadPrivKeyFile(path) + if err != nil { + return nil, errors.Wrap(err, "failed to load priv key file") + } + + return privKeyToCmpPubKey(privKeyBytes) +} + +func validatorKeyFileToCmpPubKey(path string) ([]byte, error) { + privKeyBytes, err := loadValidatorFile(path) + if err != nil { + return nil, errors.Wrap(err, "failed to load validator key file") + } + return privKeyToCmpPubKey(privKeyBytes) +} + +func privKeyToCmpPubKey(privateKeyBytes []byte) ([]byte, error) { + privateKey, err := crypto.ToECDSA(privateKeyBytes) + if err != nil { + return nil, errors.Wrap(err, "invalid private key") + } + + publicKey := &privateKey.PublicKey + + compressedPubKeyBytes := crypto.CompressPubkey(publicKey) + + return compressedPubKeyBytes, nil +} + +func cmpPubKeyToUncmpPubKey(compressedPubKeyBytes []byte) ([]byte, error) { if len(compressedPubKeyBytes) != secp256k1.PubKeyBytesLenCompressed { - return "", fmt.Errorf("invalid compressed public key length: %d", len(compressedPubKeyBytes)) + return nil, fmt.Errorf("invalid compressed public key length: %d", len(compressedPubKeyBytes)) } pubKey, err := secp256k1.ParsePubKey(compressedPubKeyBytes) if err != nil { - return "", errors.Wrap(err, "failed to parse compressed public key") + return nil, errors.Wrap(err, "failed to parse compressed public key") } uncompressedPubKeyBytes := pubKey.SerializeUncompressed() - uncompressedPubKeyHex := hex.EncodeToString(uncompressedPubKeyBytes) - - return uncompressedPubKeyHex, nil + return uncompressedPubKeyBytes, nil } func uncompressPrivateKey(privateKeyHex string) ([]byte, error) { @@ -57,3 +123,66 @@ func uncompressPrivateKey(privateKeyHex string) ([]byte, error) { return uncompressedPubKey, nil } + +func cmpPubKeyToEVMAddress(cmpPubKey []byte) (string, error) { + if len(cmpPubKey) != secp256k1.PubKeyBytesLenCompressed { + return "", fmt.Errorf("invalid compressed public key length: %d", len(cmpPubKey)) + } + + pubKey, err := crypto.DecompressPubkey(cmpPubKey) + if err != nil { + return "", errors.Wrap(err, "failed to decompress public key") + } + evmAddress := crypto.PubkeyToAddress(*pubKey).Hex() + return evmAddress, nil +} + +func cmpPubKeyToDelegatorAddress(cmpPubKey []byte) (string, error) { + if len(cmpPubKey) != secp256k1.PubKeyBytesLenCompressed { + return "", fmt.Errorf("invalid compressed public key length: %d", len(cmpPubKey)) + } + + pubKey := &cosmosk1.PubKey{Key: cmpPubKey} + return cosmostypes.AccAddress(pubKey.Address().Bytes()).String(), nil +} + +func cmpPubKeyToValidatorAddress(cmpPubKey []byte) (string, error) { + if len(cmpPubKey) != secp256k1.PubKeyBytesLenCompressed { + return "", fmt.Errorf("invalid compressed public key length: %d", len(cmpPubKey)) + } + + pubKey := &cosmosk1.PubKey{Key: cmpPubKey} + return cosmostypes.ValAddress(pubKey.Address().Bytes()).String(), nil +} + +func printKeyFormats(compressedPubKeyBytes []byte) error { + compressedPubKeyBase64 := base64.StdEncoding.EncodeToString(compressedPubKeyBytes) + evmAddress, err := cmpPubKeyToEVMAddress(compressedPubKeyBytes) + if err != nil { + return errors.Wrap(err, "failed to convert compressed pub key to EVM address") + } + + uncompressedPubKeyHex, err := cmpPubKeyToUncmpPubKey(compressedPubKeyBytes) + if err != nil { + return errors.Wrap(err, "failed to convert compressed pub key to uncompressed format") + } + + validatorAddress, err := cmpPubKeyToValidatorAddress(compressedPubKeyBytes) + if err != nil { + return errors.Wrap(err, "failed to convert compressed pub key to validator address") + } + + delegatorAddress, err := cmpPubKeyToDelegatorAddress(compressedPubKeyBytes) + if err != nil { + return errors.Wrap(err, "failed to convert compressed pub key to delegator address") + } + + fmt.Println("Compressed Public Key (hex):", hex.EncodeToString(compressedPubKeyBytes)) + fmt.Println("Compressed Public Key (base64):", compressedPubKeyBase64) + fmt.Println("Uncompressed Public Key (hex):", uncompressedPubKeyHex) + fmt.Println("EVM Address:", evmAddress) + fmt.Println("Validator Address:", validatorAddress) + fmt.Println("Delegator Address:", delegatorAddress) + + return nil +} diff --git a/client/cmd/keys.go b/client/cmd/keys.go new file mode 100644 index 00000000..e7fdb659 --- /dev/null +++ b/client/cmd/keys.go @@ -0,0 +1,88 @@ +package cmd + +import ( + "context" + "encoding/base64" + "encoding/hex" + "fmt" + "strings" + + "github.com/pkg/errors" + "github.com/spf13/cobra" +) + +type keyConfig struct { + ValidatorKeyFile string + PrivateKeyFile string + PubKeyHex string + PubKeyBase64 string +} + +func newKeyCmds() *cobra.Command { + cmd := &cobra.Command{ + Use: "key", + Short: "Commands for key management", + Args: cobra.NoArgs, + } + + cmd.AddCommand( + newKeyConvertCmd(), + ) + + return cmd +} + +func newKeyConvertCmd() *cobra.Command { + var cfg keyConfig + + cmd := &cobra.Command{ + Use: "convert", + Short: "Convert between various key formats", + Args: cobra.NoArgs, + RunE: runValidatorCommand( + func() error { return validateKeyConvertFlags(cfg) }, + func(ctx context.Context) error { return convertKey(ctx, cfg) }, + ), + } + + bindKeyConvertFlags(cmd, &cfg) + + return cmd +} + +func convertKey(ctx context.Context, cfg keyConfig) error { + var compressedPubKeyBytes []byte + var err error + + switch { + case cfg.ValidatorKeyFile != "": + compressedPubKeyBytes, err = validatorKeyFileToCmpPubKey(cfg.ValidatorKeyFile) + if err != nil { + return errors.Wrap(err, "failed to load validator private key") + } + case cfg.PrivateKeyFile != "": + compressedPubKeyBytes, err = privKeyFileToCmpPubKey(cfg.PrivateKeyFile) + if err != nil { + return errors.Wrap(err, "failed to load private key file") + } + case cfg.PubKeyHex != "": + pubKeyHex := strings.TrimPrefix(cfg.PubKeyHex, "0x") + compressedPubKeyBytes, err = hex.DecodeString(pubKeyHex) + if err != nil { + return errors.Wrap(err, "failed to decode hex public key") + } + case cfg.PubKeyBase64 != "": + compressedPubKeyBytes, err = base64.StdEncoding.DecodeString(cfg.PubKeyBase64) + if err != nil { + return errors.Wrap(err, "failed to decode base64 public key") + } + default: + return fmt.Errorf("no valid key input provided") + } + + if err != nil { + return err + } + + return printKeyFormats(compressedPubKeyBytes) +} diff --git a/client/cmd/validator.go b/client/cmd/validator.go index 18e16763..b0dbe4a9 100644 --- a/client/cmd/validator.go +++ b/client/cmd/validator.go @@ -2,10 +2,8 @@ package cmd import ( "context" - "crypto/ecdsa" "encoding/base64" "encoding/hex" - "encoding/json" "fmt" "math/big" "os" @@ -272,6 +270,9 @@ func newValidatorKeyExportCmd() *cobra.Command { cmd := &cobra.Command{ Use: "export", Short: "Export the EVM private key from the Tendermint key file", + PreRunE: func(_ *cobra.Command, _ []string) error { + return nil + }, RunE: runValidatorCommand( func() error { return nil }, func(ctx context.Context) error { return exportKey(ctx, cfg) }, @@ -298,51 +299,25 @@ func runValidatorCommand( } func exportKey(_ context.Context, cfg exportKeyConfig) error { - keyFileBytes, err := os.ReadFile(cfg.ValidatorKeyFile) - if err != nil { - return errors.Wrap(err, "failed to read key file") - } - - var keyData ValidatorKey - if err := json.Unmarshal(keyFileBytes, &keyData); err != nil { - return errors.Wrap(err, "failed to unmarshal key file") - } - - privKeyBytes, err := base64.StdEncoding.DecodeString(keyData.PrivKey.Value) - if err != nil { - return errors.Wrap(err, "failed to decode private key") - } - - privateKey, err := crypto.ToECDSA(privKeyBytes) + privKeyBytes, err := loadValidatorFile(cfg.ValidatorKeyFile) if err != nil { - return errors.Wrap(err, "invalid private key") - } - - publicKey, ok := privateKey.Public().(*ecdsa.PublicKey) - if !ok { - return errors.New("failed to cast public key to ecdsa.PublicKey") + return errors.Wrap(err, "failed to load validator key file") } - evmPublicKey := crypto.PubkeyToAddress(*publicKey).Hex() - compressedPubKeyBytes, err := base64.StdEncoding.DecodeString(keyData.PubKey.Value) + compressedPubKeyBytes, err := privKeyToCmpPubKey(privKeyBytes) if err != nil { - return errors.Wrap(err, "failed to decode base64 pub key") + return errors.Wrap(err, "failed to decode compressed pub key") } - compressedPubKeyHex := hex.EncodeToString(compressedPubKeyBytes) - uncompressedPubKeyHex, err := uncompressPubKey(keyData.PubKey.Value) - if err != nil { + if err := printKeyFormats(compressedPubKeyBytes); err != nil { return err } - fmt.Println("------------------------------------------------------") - fmt.Println("EVM Public Key:", evmPublicKey) - fmt.Println("Compressed Public Key (base64):", keyData.PubKey.Value) - fmt.Println("Compressed Public Key (hex):", compressedPubKeyHex) - fmt.Println("Uncompressed Public Key:", uncompressedPubKeyHex) - fmt.Println("------------------------------------------------------") - if cfg.ExportEVMKey { + privateKey, err := crypto.ToECDSA(privKeyBytes) + if err != nil { + return errors.Wrap(err, "invalid private key") + } evmPrivateKey := hex.EncodeToString(crypto.FromECDSA(privateKey)) keyContent := "PRIVATE_KEY=" + evmPrivateKey if err := os.WriteFile(cfg.EvmKeyFile, []byte(keyContent), 0600); err != nil { @@ -357,24 +332,15 @@ func exportKey(_ context.Context, cfg exportKeyConfig) error { } func createValidator(ctx context.Context, cfg createValidatorConfig) error { - keyFileBytes, err := os.ReadFile(cfg.ValidatorKeyFile) + compressedPubKeyBytes, err := validatorKeyFileToCmpPubKey(cfg.ValidatorKeyFile) if err != nil { - return errors.Wrap(err, "invalid key file") - } - - var keyFileData ValidatorKey - if err := json.Unmarshal(keyFileBytes, &keyFileData); err != nil { - return errors.Wrap(err, "failed to unmarshal priv_validator_key.json") + return errors.Wrap(err, "failed to extract compressed pub key") } - uncompressedPubKeyHex, err := uncompressPubKey(keyFileData.PubKey.Value) + uncompressedPubKeyBytes, err := cmpPubKeyToUncmpPubKey(compressedPubKeyBytes) if err != nil { return err } - uncompressedPubKeyBytes, err := hex.DecodeString(uncompressedPubKeyHex) - if err != nil { - return errors.Wrap(err, "failed to decode uncompressed public key hex") - } stakeAmount, ok := new(big.Int).SetString(cfg.StakeAmount, 10) if !ok { @@ -472,13 +438,14 @@ func stake(ctx context.Context, cfg stakeConfig) error { } func stakeOnBehalf(ctx context.Context, cfg stakeConfig) error { - uncompressedDelegatorPubKeyHex, err := uncompressPubKey(cfg.DelegatorPubKey) + delegatorPubKeyBytes, err := base64.StdEncoding.DecodeString(cfg.DelegatorPubKey) if err != nil { - return err + return errors.Wrap(err, "failed to decode base64 delegator public key") } - uncompressedDelegatorPubKeyBytes, err := hex.DecodeString(uncompressedDelegatorPubKeyHex) + + uncompressedDelegatorPubKeyBytes, err := cmpPubKeyToUncmpPubKey(delegatorPubKeyBytes) if err != nil { - return errors.Wrap(err, "failed to decode uncompressed delegator public key") + return errors.Wrap(err, "failed to uncompress delegator public key") } validatorPubKeyBytes, err := base64.StdEncoding.DecodeString(cfg.ValidatorPubKey) From 86d62f7ff73db0afb5fdeb68e3d24f0b94116bd3 Mon Sep 17 00:00:00 2001 From: Leeren Chang Date: Mon, 7 Oct 2024 14:29:05 -0700 Subject: [PATCH 2/5] fix: formatting --- client/cmd/flags.go | 1 + client/cmd/key_utils.go | 6 +++++- client/cmd/keys.go | 9 ++------- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/client/cmd/flags.go b/client/cmd/flags.go index 63764e1b..f087bdb5 100644 --- a/client/cmd/flags.go +++ b/client/cmd/flags.go @@ -215,5 +215,6 @@ func validateKeyConvertFlags(cfg keyConfig) error { for flag := range flagMap { flagNames = append(flagNames, "--"+flag) } + return fmt.Errorf("at least one of %s must be provided", strings.Join(flagNames, ", ")) } diff --git a/client/cmd/key_utils.go b/client/cmd/key_utils.go index 236d6884..daeb77de 100644 --- a/client/cmd/key_utils.go +++ b/client/cmd/key_utils.go @@ -43,6 +43,7 @@ func loadValidatorFile(path string) ([]byte, error) { if err != nil { return nil, errors.Wrap(err, "failed to decode private key") } + return privKeyBytes, nil } @@ -79,6 +80,7 @@ func validatorKeyFileToCmpPubKey(path string) ([]byte, error) { if err != nil { return nil, errors.Wrap(err, "failed to load validator key file") } + return privKeyToCmpPubKey(privKeyBytes) } @@ -106,6 +108,7 @@ func cmpPubKeyToUncmpPubKey(compressedPubKeyBytes []byte) ([]byte, error) { } uncompressedPubKeyBytes := pubKey.SerializeUncompressed() + return uncompressedPubKeyBytes, nil } @@ -134,6 +137,7 @@ func cmpPubKeyToEVMAddress(cmpPubKey []byte) (string, error) { return "", errors.Wrap(err, "failed to decompress public key") } evmAddress := crypto.PubkeyToAddress(*pubKey).Hex() + return evmAddress, nil } @@ -150,8 +154,8 @@ func cmpPubKeyToValidatorAddress(cmpPubKey []byte) (string, error) { if len(cmpPubKey) != secp256k1.PubKeyBytesLenCompressed { return "", fmt.Errorf("invalid compressed public key length: %d", len(cmpPubKey)) } - pubKey := &cosmosk1.PubKey{Key: cmpPubKey} + return cosmostypes.ValAddress(pubKey.Address().Bytes()).String(), nil } diff --git a/client/cmd/keys.go b/client/cmd/keys.go index e7fdb659..5071ba97 100644 --- a/client/cmd/keys.go +++ b/client/cmd/keys.go @@ -4,7 +4,6 @@ import ( "context" "encoding/base64" "encoding/hex" - "fmt" "strings" "github.com/pkg/errors" @@ -50,7 +49,7 @@ func newKeyConvertCmd() *cobra.Command { return cmd } -func convertKey(ctx context.Context, cfg keyConfig) error { +func convertKey(_ context.Context, cfg keyConfig) error { var compressedPubKeyBytes []byte var err error @@ -77,11 +76,7 @@ func convertKey(ctx context.Context, cfg keyConfig) error { return errors.Wrap(err, "failed to decode base64 public key") } default: - return fmt.Errorf("no valid key input provided") - } - - if err != nil { - return err + return errors.New("no valid key input provided") } return printKeyFormats(compressedPubKeyBytes) From 897b74f634671e5ad67cc2aa3ce014a5b7aa026c Mon Sep 17 00:00:00 2001 From: Leeren Chang Date: Mon, 7 Oct 2024 14:54:01 -0700 Subject: [PATCH 3/5] fix: format --- client/cmd/key_utils.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/client/cmd/key_utils.go b/client/cmd/key_utils.go index daeb77de..f8cc6d90 100644 --- a/client/cmd/key_utils.go +++ b/client/cmd/key_utils.go @@ -12,8 +12,8 @@ import ( cosmostypes "github.com/cosmos/cosmos-sdk/types" "github.com/decred/dcrd/dcrec/secp256k1" "github.com/ethereum/go-ethereum/crypto" - "github.com/joho/godotenv" + "github.com/piplabs/story/lib/errors" ) @@ -147,6 +147,7 @@ func cmpPubKeyToDelegatorAddress(cmpPubKey []byte) (string, error) { } pubKey := &cosmosk1.PubKey{Key: cmpPubKey} + return cosmostypes.AccAddress(pubKey.Address().Bytes()).String(), nil } From f7d7ae6c02dc133f07159492e6de4412dc04cbab Mon Sep 17 00:00:00 2001 From: Leeren Chang Date: Tue, 8 Oct 2024 08:49:52 -0700 Subject: [PATCH 4/5] adds uncompressed pubkey (hex) as input --- client/cmd/flags.go | 10 ++++++---- client/cmd/key_utils.go | 23 ++++++++++++++++++++++- client/cmd/keys.go | 19 +++++++++++++++---- 3 files changed, 43 insertions(+), 9 deletions(-) diff --git a/client/cmd/flags.go b/client/cmd/flags.go index f087bdb5..74ef07a0 100644 --- a/client/cmd/flags.go +++ b/client/cmd/flags.go @@ -125,6 +125,7 @@ func bindKeyConvertFlags(cmd *cobra.Command, cfg *keyConfig) { cmd.Flags().StringVar(&cfg.PrivateKeyFile, "private-key-file", "", "Path to the EVM private key env file") cmd.Flags().StringVar(&cfg.PubKeyHex, "pubkey-hex", "", "Public key in hex format") cmd.Flags().StringVar(&cfg.PubKeyBase64, "pubkey-base64", "", "Public key in base64 format") + cmd.Flags().StringVar(&cfg.PubKeyHexUncompressed, "pubkey-hex-uncompressed", "", "Uncompressed public key in hex format") } func bindRollbackFlags(cmd *cobra.Command, cfg *config.Config) { @@ -199,10 +200,11 @@ func validateValidatorUnstakeOnBehalfFlags(cfg stakeConfig) error { func validateKeyConvertFlags(cfg keyConfig) error { flagMap := map[string]string{ - "validator-key-file": cfg.ValidatorKeyFile, - "private-key-file": cfg.PrivateKeyFile, - "pubkey-hex": cfg.PubKeyHex, - "pubkey-base64": cfg.PubKeyBase64, + "validator-key-file": cfg.ValidatorKeyFile, + "private-key-file": cfg.PrivateKeyFile, + "pubkey-hex": cfg.PubKeyHex, + "pubkey-base64": cfg.PubKeyBase64, + "pubkey-hex-uncompressed": cfg.PubKeyHexUncompressed, } for _, value := range flagMap { diff --git a/client/cmd/key_utils.go b/client/cmd/key_utils.go index f8cc6d90..8eb8b68f 100644 --- a/client/cmd/key_utils.go +++ b/client/cmd/key_utils.go @@ -1,12 +1,15 @@ package cmd import ( + "crypto/ecdsa" "crypto/elliptic" "encoding/base64" "encoding/hex" "encoding/json" "fmt" "os" + "math/big" + cosmosk1 "github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1" cosmostypes "github.com/cosmos/cosmos-sdk/types" @@ -160,6 +163,23 @@ func cmpPubKeyToValidatorAddress(cmpPubKey []byte) (string, error) { return cosmostypes.ValAddress(pubKey.Address().Bytes()).String(), nil } +func uncmpPubKeyToCmpPubKey(uncmpPubKey []byte) ([]byte, error) { + if len(uncmpPubKey) != 65 || uncmpPubKey[0] != 0x04 { + return nil, errors.New("invalid uncompressed public key length or format") + } + + x := new(big.Int).SetBytes(uncmpPubKey[1:33]) + y := new(big.Int).SetBytes(uncmpPubKey[33:]) + + pubKey := ecdsa.PublicKey{ + Curve: elliptic.P256(), + X: x, + Y: y, + } + + return crypto.CompressPubkey(&pubKey), nil +} + func printKeyFormats(compressedPubKeyBytes []byte) error { compressedPubKeyBase64 := base64.StdEncoding.EncodeToString(compressedPubKeyBytes) evmAddress, err := cmpPubKeyToEVMAddress(compressedPubKeyBytes) @@ -167,10 +187,11 @@ func printKeyFormats(compressedPubKeyBytes []byte) error { return errors.Wrap(err, "failed to convert compressed pub key to EVM address") } - uncompressedPubKeyHex, err := cmpPubKeyToUncmpPubKey(compressedPubKeyBytes) + uncompressedPubKeyBytes, err := cmpPubKeyToUncmpPubKey(compressedPubKeyBytes) if err != nil { return errors.Wrap(err, "failed to convert compressed pub key to uncompressed format") } + uncompressedPubKeyHex := hex.EncodeToString(uncompressedPubKeyBytes) validatorAddress, err := cmpPubKeyToValidatorAddress(compressedPubKeyBytes) if err != nil { diff --git a/client/cmd/keys.go b/client/cmd/keys.go index 5071ba97..e70940b9 100644 --- a/client/cmd/keys.go +++ b/client/cmd/keys.go @@ -11,10 +11,11 @@ import ( ) type keyConfig struct { - ValidatorKeyFile string - PrivateKeyFile string - PubKeyHex string - PubKeyBase64 string + ValidatorKeyFile string + PrivateKeyFile string + PubKeyHex string + PubKeyBase64 string + PubKeyHexUncompressed string } func newKeyCmds() *cobra.Command { @@ -75,6 +76,16 @@ func convertKey(_ context.Context, cfg keyConfig) error { if err != nil { return errors.Wrap(err, "failed to decode base64 public key") } + case cfg.PubKeyHexUncompressed != "": + pubKeyHex := strings.TrimPrefix(cfg.PubKeyHexUncompressed, "0x") + uncompressedPubKeyBytes, err := hex.DecodeString(pubKeyHex) + if err != nil { + return errors.Wrap(err, "failed to decode hex public key") + } + compressedPubKeyBytes, err = uncmpPubKeyToCmpPubKey(uncompressedPubKeyBytes) + if err != nil { + return errors.Wrap(err, "failed to convert uncompressed pub key") + } default: return errors.New("no valid key input provided") } From 9d96fdc81099f4c47f5793968a0de65e5864539b Mon Sep 17 00:00:00 2001 From: Leeren Chang Date: Tue, 8 Oct 2024 08:54:49 -0700 Subject: [PATCH 5/5] fix: formatting --- client/cmd/key_utils.go | 25 ++++++++++++------------- client/cmd/keys.go | 10 +++++----- 2 files changed, 17 insertions(+), 18 deletions(-) diff --git a/client/cmd/key_utils.go b/client/cmd/key_utils.go index 8eb8b68f..8541e466 100644 --- a/client/cmd/key_utils.go +++ b/client/cmd/key_utils.go @@ -7,9 +7,8 @@ import ( "encoding/hex" "encoding/json" "fmt" - "os" "math/big" - + "os" cosmosk1 "github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1" cosmostypes "github.com/cosmos/cosmos-sdk/types" @@ -164,20 +163,20 @@ func cmpPubKeyToValidatorAddress(cmpPubKey []byte) (string, error) { } func uncmpPubKeyToCmpPubKey(uncmpPubKey []byte) ([]byte, error) { - if len(uncmpPubKey) != 65 || uncmpPubKey[0] != 0x04 { - return nil, errors.New("invalid uncompressed public key length or format") - } + if len(uncmpPubKey) != 65 || uncmpPubKey[0] != 0x04 { + return nil, errors.New("invalid uncompressed public key length or format") + } - x := new(big.Int).SetBytes(uncmpPubKey[1:33]) - y := new(big.Int).SetBytes(uncmpPubKey[33:]) + x := new(big.Int).SetBytes(uncmpPubKey[1:33]) + y := new(big.Int).SetBytes(uncmpPubKey[33:]) - pubKey := ecdsa.PublicKey{ - Curve: elliptic.P256(), - X: x, - Y: y, - } + pubKey := ecdsa.PublicKey{ + Curve: elliptic.P256(), + X: x, + Y: y, + } - return crypto.CompressPubkey(&pubKey), nil + return crypto.CompressPubkey(&pubKey), nil } func printKeyFormats(compressedPubKeyBytes []byte) error { diff --git a/client/cmd/keys.go b/client/cmd/keys.go index e70940b9..ac0d1225 100644 --- a/client/cmd/keys.go +++ b/client/cmd/keys.go @@ -11,11 +11,11 @@ import ( ) type keyConfig struct { - ValidatorKeyFile string - PrivateKeyFile string - PubKeyHex string - PubKeyBase64 string - PubKeyHexUncompressed string + ValidatorKeyFile string + PrivateKeyFile string + PubKeyHex string + PubKeyBase64 string + PubKeyHexUncompressed string } func newKeyCmds() *cobra.Command {