diff --git a/relayer/chains/avalanche/keys.go b/relayer/chains/avalanche/keys.go index ca3939dd3..5a3e8efae 100644 --- a/relayer/chains/avalanche/keys.go +++ b/relayer/chains/avalanche/keys.go @@ -5,11 +5,9 @@ import ( "fmt" "os" - "github.com/cometbft/cometbft/libs/bytes" ckeys "github.com/cosmos/cosmos-sdk/client/keys" "github.com/cosmos/cosmos-sdk/crypto/hd" "github.com/cosmos/cosmos-sdk/crypto/keyring" - sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/go-bip39" "github.com/ethereum/go-ethereum/crypto" @@ -41,7 +39,14 @@ func KeyringAlgoOptions() keyring.Option { // CreateKeystore initializes a new instance of a keyring at the specified path in the local filesystem. func (a *AvalancheProvider) CreateKeystore(_ string) error { - keybase, err := keyring.New(a.PCfg.ChainID, a.PCfg.KeyringBackend, a.PCfg.KeyDirectory, a.Input, a.Codec.Marshaler, KeyringAlgoOptions()) + keybase, err := keyring.New( + a.PCfg.ChainID, + a.PCfg.KeyringBackend, + a.PCfg.KeyDirectory, + a.Input, + a.Codec.Marshaler, + a.KeyringOptions..., + ) if err != nil { return err } @@ -110,33 +115,28 @@ func (a *AvalancheProvider) KeyAddOrRestore(keyName string, coinType uint32, mne algo = keyring.SignatureAlgo(ethermint.EthSecp256k1) } - info, err := a.Keybase.NewAccount(keyName, mnemonicStr, "", hd.CreateHDPath(coinType, 0, 0).String(), algo) + _, err = a.Keybase.NewAccount( + keyName, + mnemonicStr, + "", + hd.CreateHDPath(coinType, 0, 0).String(), + algo, + ) if err != nil { return nil, err } - acc, err := info.GetAddress() + out, err := a.EncodeAccAddr(keyName) if err != nil { return nil, err } - out := a.EncodeAccAddr(acc) return &provider.KeyOutput{Mnemonic: mnemonicStr, Address: out}, nil } // ShowAddress retrieves a key by name from the keystore and returns the bech32 encoded string representation of that key. func (a *AvalancheProvider) ShowAddress(name string) (address string, err error) { - info, err := a.Keybase.Key(name) - if err != nil { - return "", err - } - acc, err := info.GetAddress() - if err != nil { - return "", nil - } - out := a.EncodeAccAddr(acc) - - return out, nil + return a.EncodeAccAddr(name) } // ListAddresses returns a map of bech32 encoded strings representing all keys currently in the keystore. @@ -147,12 +147,10 @@ func (a *AvalancheProvider) ListAddresses() (map[string]string, error) { return nil, err } for _, k := range info { - acc, err := k.GetAddress() + addr, err := a.EncodeAccAddr(k.Name) if err != nil { return nil, err } - addr := a.EncodeAccAddr(acc) - out[k.Name] = addr } return out, nil @@ -198,8 +196,17 @@ func CreateMnemonic() (string, error) { return mnemonic, nil } -func (a *AvalancheProvider) EncodeAccAddr(addr sdk.AccAddress) string { - var data bytes.HexBytes = addr.Bytes() +// EncodeAccAddr returns the encoded string representation of the account address for the specified key. +func (a *AvalancheProvider) EncodeAccAddr(name string) (string, error) { + old := a.PCfg.Key // save the old key + + a.PCfg.Key = name // set the key to the name of the key we want to get the address of + address, err := a.Address() + if err != nil { + return "", err + } + + a.PCfg.Key = old // reset the key to the old key - return fmt.Sprintf("0x%s", data.String()) + return fmt.Sprintf("%s", address), nil } diff --git a/relayer/chains/avalanche/keys_test.go b/relayer/chains/avalanche/keys_test.go index d610c811d..55fcc631b 100644 --- a/relayer/chains/avalanche/keys_test.go +++ b/relayer/chains/avalanche/keys_test.go @@ -11,7 +11,7 @@ import ( "github.com/cosmos/relayer/v2/relayer/provider" ) -func testProviderWithKeystore(t *testing.T, extraCodecs []string) provider.ChainProvider { +func testProviderWithKeystore(t *testing.T) provider.ChainProvider { homePath := t.TempDir() cfg := avalanche.AvalancheProviderConfig{ ChainID: "test", @@ -36,11 +36,11 @@ func TestKeyRestore(t *testing.T) { keyName = "test_key" signatureAlgorithm = "secp256k1" mnemonic = "three elevator silk family street child flip also leaf inmate call frame shock little legal october vivid enable fetch siege sell burger dolphin green" - expectedAddress = "0x6E7BE67F3619731AB38875999873E8FACE935735" - coinType = uint32(60) + expectedAddress = "0x836E7e82deDE708Ba83ADe38216F5e30AC0fFB03" + coinType = uint32(118) ) - p := testProviderWithKeystore(t, []string{"ethermint"}) + p := testProviderWithKeystore(t) address, err := p.RestoreKey(keyName, mnemonic, coinType, signatureAlgorithm) require.NoError(t, err) @@ -54,10 +54,10 @@ func TestKeyRestorePrivateKey(t *testing.T) { signatureAlgorithm = "secp256k1" mnemonic = "56289e99c94b6912bfc12adc093c9b51124f0dc54ac7a766b2bc5ccf558d8027" expectedAddress = "0x8db97C7cEcE249c2b98bDC0226Cc4C2A57BF52FC" - coinType = uint32(60) + coinType = uint32(118) ) - p := testProviderWithKeystore(t, []string{"ethermint"}) + p := testProviderWithKeystore(t) address, err := p.RestoreKey(keyName, mnemonic, coinType, signatureAlgorithm) require.NoError(t, err) diff --git a/relayer/chains/avalanche/provider.go b/relayer/chains/avalanche/provider.go index 4acbe951a..9f896d52c 100644 --- a/relayer/chains/avalanche/provider.go +++ b/relayer/chains/avalanche/provider.go @@ -2,6 +2,7 @@ package avalanche import ( "context" + "crypto/ecdsa" _ "embed" "fmt" "io" @@ -18,6 +19,7 @@ import ( "github.com/ava-labs/subnet-evm/rpc" "github.com/avast/retry-go/v4" "github.com/cosmos/cosmos-sdk/crypto/keyring" + cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" "github.com/cosmos/gogoproto/proto" ibcexported "github.com/cosmos/ibc-go/v8/modules/core/exported" avalanche "github.com/cosmos/ibc-go/v8/modules/light-clients/14-avalanche" @@ -69,7 +71,7 @@ func (h AvalancheIBCHeader) ConsensusState() ibcexported.ConsensusState { Timestamp: time.Unix(int64(h.EthHeader.Time), 0), StorageRoot: h.EthHeader.Root.Bytes(), SignedStorageRoot: h.SignedStorageRoot[:], - //ValidatorSet: h.ValidatorSet, + // ValidatorSet: h.ValidatorSet, SignedValidatorSet: h.SignedValidatorSet[:], Vdrs: h.Vdrs, SignersInput: h.SignersInput, @@ -109,25 +111,35 @@ func (a *AvalancheProvider) Init(ctx context.Context) error { a.ethClient = ethclient.NewClient(rpcClient) a.subnetClient = subnetevmclient.New(rpcClient) a.pClient = platformvm.NewClient(a.PCfg.BaseRPCAddr) - ibcClient, err := NewIbcClient(a.PCfg.RPCAddr) - a.ibcClient = ibcClient + a.ibcClient, err = NewIbcClient(a.PCfg.RPCAddr) + a.Keybase, err = keyring.New( + a.PCfg.ChainID, + a.PCfg.KeyringBackend, + a.PCfg.KeyDirectory, + a.Input, + a.Codec.Marshaler, + a.KeyringOptions..., + ) + if err != nil { + return err + } + + ethPrivKey, err := a.GetPrivKey(a.Key()) + if err != nil { + ethPrivKey = tempKey + } chainId, ok := new(big.Int).SetString(a.PCfg.ChainID, 10) if !ok { return fmt.Errorf("invalid chain id %s", a.PCfg.ChainID) } - a.txAuth, err = bind.NewKeyedTransactorWithChainID(tempKey, chainId) + a.txAuth, err = bind.NewKeyedTransactorWithChainID(ethPrivKey, chainId) a.txAuth.GasLimit = 1000000 if err != nil { return err } - keybase, err := keyring.New(a.PCfg.ChainID, a.PCfg.KeyringBackend, a.PCfg.KeyDirectory, a.Input, a.Codec.Marshaler, a.KeyringOptions...) - if err != nil { - return err - } - contractAbi, err := abi.JSON(strings.NewReader(ibccontract.IBCABI)) if err != nil { return err @@ -148,7 +160,6 @@ func (a *AvalancheProvider) Init(ctx context.Context) error { return err } - a.Keybase = keybase a.abi = contractAbi a.subnetID = subnetID a.blockchainID = blockchainID @@ -157,6 +168,35 @@ func (a *AvalancheProvider) Init(ctx context.Context) error { return nil } +// GetPrivKey extracts a private key from a keyring record +func (a AvalancheProvider) GetPrivKey(name string) (*ecdsa.PrivateKey, error) { + info, err := a.Keybase.Key(name) + if err != nil { + return nil, err + } + + local := info.GetLocal() + if local == nil { + return nil, fmt.Errorf("key is not a local key") + } + + if local.PrivKey == nil { + return nil, fmt.Errorf("private key not available in record") + } + + priv, ok := local.PrivKey.GetCachedValue().(cryptotypes.PrivKey) + if !ok { + return nil, fmt.Errorf("failed to cast private key to cryptotypes.PrivKey") + } + + ethPrivKey, err := crypto.ToECDSA(priv.Bytes()) + if err != nil { + return nil, fmt.Errorf("failed to convert private key to ECDSA: %w", err) + } + + return ethPrivKey, nil +} + func (a AvalancheProvider) SetRpcAddr(rpcAddr string) error { // TODO implement me panic("implement me") @@ -188,16 +228,17 @@ func (a AvalancheProvider) Key() string { } func (a AvalancheProvider) Address() (string, error) { - info, err := a.Keybase.Key(a.PCfg.Key) + ethPrivKey, err := a.GetPrivKey(a.Key()) if err != nil { return "", err } - acc, err := info.GetAddress() - if err != nil { - return "", err + publicKeyECDSA, ok := ethPrivKey.Public().(*ecdsa.PublicKey) + if !ok { + return "", fmt.Errorf("error casting public key to ECDSA") } - out := a.EncodeAccAddr(acc) + + out := crypto.PubkeyToAddress(*publicKeyECDSA).Hex() return out, nil }