github.com/InjectiveLabs/sdk-go@v1.53.0/client/chain/keys.go (about) 1 package chain 2 3 import ( 4 "bytes" 5 "crypto/rand" 6 "io" 7 "log" 8 "os" 9 "path/filepath" 10 11 "github.com/cosmos/cosmos-sdk/codec" 12 cosmcrypto "github.com/cosmos/cosmos-sdk/crypto" 13 "github.com/cosmos/cosmos-sdk/crypto/keyring" 14 cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" 15 cosmtypes "github.com/cosmos/cosmos-sdk/types" 16 "github.com/pkg/errors" 17 18 crypto_cdc "github.com/InjectiveLabs/sdk-go/chain/crypto/codec" 19 "github.com/InjectiveLabs/sdk-go/chain/crypto/ethsecp256k1" 20 "github.com/InjectiveLabs/sdk-go/chain/crypto/hd" 21 "github.com/InjectiveLabs/sdk-go/client/common" 22 ) 23 24 const defaultKeyringKeyName = "validator" 25 26 var emptyCosmosAddress = cosmtypes.AccAddress{} 27 28 func InitCosmosKeyring( 29 cosmosKeyringDir string, 30 cosmosKeyringAppName string, 31 cosmosKeyringBackend string, 32 cosmosKeyFrom string, 33 cosmosKeyPassphrase string, 34 cosmosPrivKey string, 35 cosmosUseLedger bool, 36 ) (cosmtypes.AccAddress, keyring.Keyring, error) { 37 switch { 38 case cosmosPrivKey != "": 39 if cosmosUseLedger { 40 err := errors.New("cannot combine ledger and privkey options") 41 return emptyCosmosAddress, nil, err 42 } 43 44 pkBytes, err := common.HexToBytes(cosmosPrivKey) 45 if err != nil { 46 err = errors.Wrap(err, "failed to hex-decode cosmos account privkey") 47 return emptyCosmosAddress, nil, err 48 } 49 50 // Specific to Injective chain with Ethermint keys 51 // Should be secp256k1.PrivKey for generic Cosmos chain 52 cosmosAccPk := ðsecp256k1.PrivKey{ 53 Key: pkBytes, 54 } 55 56 addressFromPk := cosmtypes.AccAddress(cosmosAccPk.PubKey().Address().Bytes()) 57 58 var keyName string 59 60 // check that if cosmos 'From' specified separately, it must match the provided privkey, 61 if cosmosKeyFrom != "" { 62 addressFrom, err := cosmtypes.AccAddressFromBech32(cosmosKeyFrom) 63 if err == nil { 64 if !bytes.Equal(addressFrom.Bytes(), addressFromPk.Bytes()) { 65 err = errors.Errorf("expected account address %s but got %s from the private key", addressFrom.String(), addressFromPk.String()) 66 return emptyCosmosAddress, nil, err 67 } 68 } else { 69 // use it as a name then 70 keyName = cosmosKeyFrom 71 } 72 } 73 74 if keyName == "" { 75 keyName = defaultKeyringKeyName 76 } 77 78 // wrap a PK into a Keyring 79 kb, err := KeyringForPrivKey(keyName, cosmosAccPk) 80 return addressFromPk, kb, err 81 82 case cosmosKeyFrom != "": 83 var fromIsAddress bool 84 addressFrom, err := cosmtypes.AccAddressFromBech32(cosmosKeyFrom) 85 if err == nil { 86 fromIsAddress = true 87 } 88 89 var passReader io.Reader = os.Stdin 90 if cosmosKeyPassphrase != "" { 91 passReader = newPassReader(cosmosKeyPassphrase) 92 } 93 94 var absoluteKeyringDir string 95 if filepath.IsAbs(cosmosKeyringDir) { 96 absoluteKeyringDir = cosmosKeyringDir 97 } else { 98 absoluteKeyringDir, _ = filepath.Abs(cosmosKeyringDir) 99 } 100 101 kb, err := keyring.New( 102 cosmosKeyringAppName, 103 cosmosKeyringBackend, 104 absoluteKeyringDir, 105 passReader, 106 getCryptoCodec(), 107 hd.EthSecp256k1Option(), 108 ) 109 if err != nil { 110 err = errors.Wrap(err, "failed to init keyring") 111 return emptyCosmosAddress, nil, err 112 } 113 114 var keyInfo *keyring.Record 115 if fromIsAddress { 116 if keyInfo, err = kb.KeyByAddress(addressFrom); err != nil { 117 err = errors.Wrapf(err, "couldn't find an entry for the key %s in keybase", addressFrom.String()) 118 return emptyCosmosAddress, nil, err 119 } 120 } else { 121 if keyInfo, err = kb.Key(cosmosKeyFrom); err != nil { 122 err = errors.Wrapf(err, "could not find an entry for the key '%s' in keybase", cosmosKeyFrom) 123 return emptyCosmosAddress, nil, err 124 } 125 } 126 127 switch keyType := keyInfo.GetType(); keyType { 128 case keyring.TypeLocal: 129 // kb has a key and it's totally usable 130 addr, err := keyInfo.GetAddress() 131 if err != nil { 132 err = errors.Wrapf(err, "failed to get address for key '%s'", keyInfo.Name) 133 return emptyCosmosAddress, nil, err 134 } 135 return addr, kb, nil 136 case keyring.TypeLedger: 137 // the kb stores references to ledger keys, so we must explicitly 138 // check that. kb doesn't know how to scan HD keys - they must be added manually before 139 if cosmosUseLedger { 140 addr, err := keyInfo.GetAddress() 141 if err != nil { 142 err = errors.Wrapf(err, "failed to get address for key '%s'", keyInfo.Name) 143 return emptyCosmosAddress, nil, err 144 } 145 return addr, kb, nil 146 } 147 err := errors.Errorf("'%s' key is a ledger reference, enable ledger option", keyInfo.Name) 148 return emptyCosmosAddress, nil, err 149 case keyring.TypeOffline: 150 err := errors.Errorf("'%s' key is an offline key, not supported yet", keyInfo.Name) 151 return emptyCosmosAddress, nil, err 152 case keyring.TypeMulti: 153 err := errors.Errorf("'%s' key is an multisig key, not supported yet", keyInfo.Name) 154 return emptyCosmosAddress, nil, err 155 default: 156 err := errors.Errorf("'%s' key has unsupported type: %s", keyInfo.Name, keyType) 157 return emptyCosmosAddress, nil, err 158 } 159 160 default: 161 err := errors.New("insufficient cosmos key details provided") 162 return emptyCosmosAddress, nil, err 163 } 164 } 165 166 func newPassReader(pass string) io.Reader { 167 return &passReader{ 168 pass: pass, 169 buf: new(bytes.Buffer), 170 } 171 } 172 173 type passReader struct { 174 pass string 175 buf *bytes.Buffer 176 } 177 178 var _ io.Reader = &passReader{} 179 180 func (r *passReader) Read(p []byte) (n int, err error) { 181 n, err = r.buf.Read(p) 182 if err == io.EOF || n == 0 { 183 r.buf.WriteString(r.pass + "\n") 184 185 n, err = r.buf.Read(p) 186 } 187 188 return 189 } 190 191 func getCryptoCodec() *codec.ProtoCodec { 192 registry := NewInterfaceRegistry() 193 crypto_cdc.RegisterInterfaces(registry) 194 return codec.NewProtoCodec(registry) 195 } 196 197 // KeyringForPrivKey creates a temporary in-mem keyring for a PrivKey. 198 // Allows to init Context when the key has been provided in plaintext and parsed. 199 func KeyringForPrivKey(name string, privKey cryptotypes.PrivKey) (keyring.Keyring, error) { 200 kb := keyring.NewInMemory(getCryptoCodec(), hd.EthSecp256k1Option()) 201 tmpPhrase := randPhrase(64) 202 armored := cosmcrypto.EncryptArmorPrivKey(privKey, tmpPhrase, privKey.Type()) 203 err := kb.ImportPrivKey(name, armored, tmpPhrase) 204 if err != nil { 205 err = errors.Wrap(err, "failed to import privkey") 206 return nil, err 207 } 208 209 return kb, nil 210 } 211 212 func randPhrase(size int) string { 213 buf := make([]byte, size) 214 _, err := rand.Read(buf) 215 orPanic(err) 216 217 return string(buf) 218 } 219 220 func orPanic(err error) { 221 if err != nil { 222 log.Panicln() 223 } 224 }