From 9ff6d5441db2260e7877724df65c0f2b8251d991 Mon Sep 17 00:00:00 2001 From: Sunny Aggarwal Date: Mon, 14 Feb 2022 02:36:52 -0800 Subject: [PATCH] feat: Add debug pubkey-raw cli command (#11006) ## Description Very handy to inspect pubkeys that were stored in legacy bech32 format. This is the case for a lot of multisig management. --- ### Author Checklist *All items are required. Please add a note to the item if the item is not applicable and please add links to any relevant follow up issues.* I have... - [ ] included the correct [type prefix](https://github.com/commitizen/conventional-commit-types/blob/v3.0.0/index.json) in the PR title - [ ] added `!` to the type prefix if API or client breaking change - [ ] targeted the correct branch (see [PR Targeting](https://github.com/cosmos/cosmos-sdk/blob/master/CONTRIBUTING.md#pr-targeting)) - [ ] provided a link to the relevant issue or specification - [ ] followed the guidelines for [building modules](https://github.com/cosmos/cosmos-sdk/blob/master/docs/building-modules) - [ ] included the necessary unit and integration [tests](https://github.com/cosmos/cosmos-sdk/blob/master/CONTRIBUTING.md#testing) - [x] added a changelog entry to `CHANGELOG.md` - [ ] included comments for [documenting Go code](https://blog.golang.org/godoc) - [ ] updated the relevant documentation or specification - [x] reviewed "Files changed" and left comments if necessary - [ ] confirmed all CI checks have passed ### Reviewers Checklist *All items are required. Please add a note if the item is not applicable and please add your handle next to the items reviewed if you only reviewed selected items.* I have... - [ ] confirmed the correct [type prefix](https://github.com/commitizen/conventional-commit-types/blob/v3.0.0/index.json) in the PR title - [ ] confirmed `!` in the type prefix if API or client breaking change - [ ] confirmed all author checklist items have been addressed - [ ] reviewed state machine logic - [ ] reviewed API design and naming - [ ] reviewed documentation is accurate - [ ] reviewed tests and test coverage - [ ] manually tested (if applicable) --- CHANGELOG.md | 1 + client/debug/main.go | 130 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 131 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 825f34e63ec4..ba46ff4f05c9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -62,6 +62,7 @@ Ref: https://keepachangelog.com/en/1.0.0/ * [\#10947](https://github.com/cosmos/cosmos-sdk/pull/10947) Add `AllowancesByGranter` query to the feegrant module * [\#10407](https://github.com/cosmos/cosmos-sdk/pull/10407) Add validation to `x/upgrade` module's `BeginBlock` to check accidental binary downgrades * (gov) [\#11036](https://github.com/cosmos/cosmos-sdk/pull/11036) Add in-place migrations for 0.43->0.46. Add a `migrate v0.46` CLI command for v0.43->0.46 JSON genesis migration. +* [\#11006](https://github.com/cosmos/cosmos-sdk/pull/11006) Add `debug pubkey-raw` command to allow inspecting of pubkeys in legacy bech32 format ### API Breaking Changes diff --git a/client/debug/main.go b/client/debug/main.go index 03358fd7c2e4..05a1b311337c 100644 --- a/client/debug/main.go +++ b/client/debug/main.go @@ -1,6 +1,7 @@ package debug import ( + "encoding/base64" "encoding/hex" "fmt" "strconv" @@ -9,9 +10,18 @@ import ( "github.com/spf13/cobra" "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/crypto/keys/ed25519" + "github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1" cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/errors" "github.com/cosmos/cosmos-sdk/version" + + legacybech32 "github.com/cosmos/cosmos-sdk/types/bech32/legacybech32" +) + +var ( + flagPubkeyType = "type" ) // Cmd creates a main CLI command @@ -23,6 +33,7 @@ func Cmd() *cobra.Command { } cmd.AddCommand(PubkeyCmd()) + cmd.AddCommand(PubkeyRawCmd()) cmd.AddCommand(AddrCmd()) cmd.AddCommand(RawBytesCmd()) @@ -59,6 +70,125 @@ $ %s debug pubkey '{"@type":"/cosmos.crypto.secp256k1.PubKey","key":"AurroA7jvfP } } +func bytesToPubkey(bz []byte, keytype string) (cryptotypes.PubKey, bool) { + if keytype == "ed25519" { + if len(bz) == ed25519.PubKeySize { + return &ed25519.PubKey{Key: bz}, true + } + } + + if len(bz) == secp256k1.PubKeySize { + return &secp256k1.PubKey{Key: bz}, true + } + return nil, false +} + +// getPubKeyFromRawString returns a PubKey (PubKeyEd25519 or PubKeySecp256k1) by attempting +// to decode the pubkey string from hex, base64, and finally bech32. If all +// encodings fail, an error is returned. +func getPubKeyFromRawString(pkstr string, keytype string) (cryptotypes.PubKey, error) { + // Try hex decoding + bz, err := hex.DecodeString(pkstr) + if err == nil { + pk, ok := bytesToPubkey(bz, keytype) + if ok { + return pk, nil + } + } + + bz, err = base64.StdEncoding.DecodeString(pkstr) + if err == nil { + pk, ok := bytesToPubkey(bz, keytype) + if ok { + return pk, nil + } + } + + pk, err := legacybech32.UnmarshalPubKey(legacybech32.AccPK, pkstr) + if err == nil { + return pk, nil + } + + pk, err = legacybech32.UnmarshalPubKey(legacybech32.ValPK, pkstr) + if err == nil { + return pk, nil + } + + pk, err = legacybech32.UnmarshalPubKey(legacybech32.ConsPK, pkstr) + if err == nil { + return pk, nil + } + + return nil, fmt.Errorf("pubkey '%s' invalid; expected hex, base64, or bech32 of correct size", pkstr) +} + +func PubkeyRawCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "pubkey-raw [pubkey] -t [{ed25519, secp256k1}]", + Short: "Decode a ED25519 or secp256k1 pubkey from hex, base64, or bech32", + Long: fmt.Sprintf(`Decode a pubkey from hex, base64, or bech32. +Example: +$ %s debug pubkey-raw TWFuIGlzIGRpc3Rpbmd1aXNoZWQsIG5vdCBvbmx5IGJ5IGhpcyByZWFzb24sIGJ1dCBieSB0aGlz +$ %s debug pubkey-raw cosmos1e0jnq2sun3dzjh8p2xq95kk0expwmd7shwjpfg + `, version.AppName, version.AppName), + Args: cobra.ExactArgs(1), + RunE: func(cmd *cobra.Command, args []string) error { + clientCtx := client.GetClientContextFromCmd(cmd) + + pubkeyType, err := cmd.Flags().GetString(flagPubkeyType) + if err != nil { + return err + } + pubkeyType = strings.ToLower(pubkeyType) + if pubkeyType != "secp256k1" && pubkeyType != "ed25519" { + return errors.Wrapf(errors.ErrInvalidType, "invalid pubkey type, expected oneof ed25519 or secp256k1") + } + + pk, err := getPubKeyFromRawString(args[0], pubkeyType) + if err != nil { + return err + } + + var consensusPub string + edPK, ok := pk.(*ed25519.PubKey) + if ok && pubkeyType == "ed25519" { + consensusPub, err = legacybech32.MarshalPubKey(legacybech32.ConsPK, edPK) + if err != nil { + return err + } + + cmd.Printf("Hex: %X\n", edPK.Key) + } + cmd.Println("Parsed key as", pk.Type()) + + pubKeyJSONBytes, err := clientCtx.LegacyAmino.MarshalJSON(pk) + if err != nil { + return err + } + accPub, err := legacybech32.MarshalPubKey(legacybech32.AccPK, pk) + if err != nil { + return err + } + valPub, err := legacybech32.MarshalPubKey(legacybech32.ValPK, pk) + if err != nil { + return err + } + cmd.Println("Address:", pk.Address()) + cmd.Println("JSON (base64):", string(pubKeyJSONBytes)) + cmd.Println("Bech32 Acc:", accPub) + cmd.Println("Bech32 Validator Operator:", valPub) + if pubkeyType == "ed25519" { + + cmd.Println("Bech32 Validator Consensus:", consensusPub) + } + + return nil + }, + } + cmd.Flags().StringP(flagPubkeyType, "t", "ed25519", "Pubkey type to decode (oneof secp256k1, ed25519)") + return cmd +} + func AddrCmd() *cobra.Command { return &cobra.Command{ Use: "addr [address]",