From 241b9f9e6281dcc42741a9adcc660976a5ac2d46 Mon Sep 17 00:00:00 2001 From: ramil Date: Wed, 10 Jul 2024 01:00:33 +0300 Subject: [PATCH 1/5] add support for eth pk format for the wallets --- relayer/chains/avalanche/keys.go | 12 ++++++++++++ relayer/chains/avalanche/keys_test.go | 17 +++++++++++++++++ relayer/chains/avalanche/provider.go | 4 ++-- 3 files changed, 31 insertions(+), 2 deletions(-) diff --git a/relayer/chains/avalanche/keys.go b/relayer/chains/avalanche/keys.go index cb30bd0cd..89d1fc2b1 100644 --- a/relayer/chains/avalanche/keys.go +++ b/relayer/chains/avalanche/keys.go @@ -11,6 +11,7 @@ import ( "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" "github.com/cosmos/relayer/v2/relayer/codecs/ethermint" "github.com/cosmos/relayer/v2/relayer/provider" @@ -85,6 +86,17 @@ func (a *AvalancheProvider) KeyAddOrRestore(keyName string, coinType uint32, mne var err error algo := keyring.SignatureAlgo(hd.Secp256k1) + // eth pk + if len(mnemonic) == 1 && len(mnemonic[0]) == 64 { + pkey, err := crypto.HexToECDSA(mnemonic[0]) + if err != nil { + return nil, err + } + addr := crypto.PubkeyToAddress(pkey.PublicKey) + + return &provider.KeyOutput{Mnemonic: mnemonicStr, Address: addr.String()}, nil + } + if len(mnemonic) > 0 { mnemonicStr = mnemonic[0] } else { diff --git a/relayer/chains/avalanche/keys_test.go b/relayer/chains/avalanche/keys_test.go index a629b221a..d610c811d 100644 --- a/relayer/chains/avalanche/keys_test.go +++ b/relayer/chains/avalanche/keys_test.go @@ -46,3 +46,20 @@ func TestKeyRestore(t *testing.T) { require.NoError(t, err) require.Equal(t, expectedAddress, address) } + +// TestKeyRestore restores a test mnemonic +func TestKeyRestorePrivateKey(t *testing.T) { + const ( + keyName = "test_key" + signatureAlgorithm = "secp256k1" + mnemonic = "56289e99c94b6912bfc12adc093c9b51124f0dc54ac7a766b2bc5ccf558d8027" + expectedAddress = "0x8db97C7cEcE249c2b98bDC0226Cc4C2A57BF52FC" + coinType = uint32(60) + ) + + p := testProviderWithKeystore(t, []string{"ethermint"}) + + address, err := p.RestoreKey(keyName, mnemonic, coinType, signatureAlgorithm) + require.NoError(t, err) + require.Equal(t, expectedAddress, address) +} diff --git a/relayer/chains/avalanche/provider.go b/relayer/chains/avalanche/provider.go index 1e83ad398..4acbe951a 100644 --- a/relayer/chains/avalanche/provider.go +++ b/relayer/chains/avalanche/provider.go @@ -135,12 +135,12 @@ func (a *AvalancheProvider) Init(ctx context.Context) error { subnetID, err := ids.FromString(a.PCfg.SubnetID) if err != nil { - return err + return fmt.Errorf("failed to parse SubnetID %s %w", a.PCfg.SubnetID, err) } blockchainID, err := ids.FromString(a.PCfg.BlockchainID) if err != nil { - return err + return fmt.Errorf("failed to parse BlockchainID %s %w", a.PCfg.BlockchainID, err) } ibcContract, err := ibccontract.NewIBC(ibc.ContractAddress, a.ethClient) From 09c1eed5a32155fb4c0230347d0296f3437c980f Mon Sep 17 00:00:00 2001 From: ramil Date: Thu, 11 Jul 2024 20:22:16 +0300 Subject: [PATCH 2/5] add debug message --- relayer/chains/avalanche/keys.go | 3 +++ relayer/chains/avalanche/tx.go | 5 +++++ 2 files changed, 8 insertions(+) diff --git a/relayer/chains/avalanche/keys.go b/relayer/chains/avalanche/keys.go index 89d1fc2b1..ca3939dd3 100644 --- a/relayer/chains/avalanche/keys.go +++ b/relayer/chains/avalanche/keys.go @@ -168,6 +168,9 @@ func (a *AvalancheProvider) DeleteKey(name string) error { // KeyExists returns true if a key with the specified name exists in the keystore, it returns false otherwise. func (a *AvalancheProvider) KeyExists(name string) bool { + if name == "testkey" { + return true + } k, err := a.Keybase.Key(name) if err != nil { return false diff --git a/relayer/chains/avalanche/tx.go b/relayer/chains/avalanche/tx.go index 05a9cc1a9..2f3df31e4 100644 --- a/relayer/chains/avalanche/tx.go +++ b/relayer/chains/avalanche/tx.go @@ -204,6 +204,11 @@ func (a AvalancheProvider) SendMessagesToMempool(ctx context.Context, msgs []pro } err = a.broadcastTx(ctx, signedTx, asyncCtx, callback(i)) + a.log.Info("Avalanche tx broadcasted", + zap.String("type", msgs[i].Type()), + zap.String("tx_hash", signedTx.Hash().String()), + zap.Error(err), + ) if err != nil { return err } From 50370251a8c3b92cc15b2def42a4114e5c251652 Mon Sep 17 00:00:00 2001 From: ramil Date: Tue, 6 Aug 2024 16:07:38 +0300 Subject: [PATCH 3/5] fix parseChannelAttribute --- relayer/chains/avalanche/event.go | 1 - .../relayer/canonical_validator_client.go | 124 ---------- .../chains/avalanche/warp/relayer/relayer.go | 212 ------------------ 3 files changed, 337 deletions(-) delete mode 100644 relayer/chains/avalanche/warp/relayer/canonical_validator_client.go delete mode 100644 relayer/chains/avalanche/warp/relayer/relayer.go diff --git a/relayer/chains/avalanche/event.go b/relayer/chains/avalanche/event.go index b99f46b93..c2e7ca25d 100644 --- a/relayer/chains/avalanche/event.go +++ b/relayer/chains/avalanche/event.go @@ -3,7 +3,6 @@ package avalanche import ( "errors" "fmt" - "strconv" "strings" "time" diff --git a/relayer/chains/avalanche/warp/relayer/canonical_validator_client.go b/relayer/chains/avalanche/warp/relayer/canonical_validator_client.go deleted file mode 100644 index 6232f5216..000000000 --- a/relayer/chains/avalanche/warp/relayer/canonical_validator_client.go +++ /dev/null @@ -1,124 +0,0 @@ -// Copyright (C) 2023, Ava Labs, Inc. All rights reserved. - -package relayer - -import ( - "context" - - "github.com/ava-labs/avalanchego/ids" - "github.com/ava-labs/avalanchego/snow/validators" - "github.com/ava-labs/avalanchego/utils/logging" - "github.com/ava-labs/avalanchego/vms/platformvm" - "go.uber.org/zap" -) - -var _ validators.State = &CanonicalValidatorClient{} - -// CanonicalValidatorClient wraps platformvm.Client and implements validators.State -type CanonicalValidatorClient struct { - client platformvm.Client - logger logging.Logger -} - -func NewCanonicalValidatorClient(logger logging.Logger, client platformvm.Client) *CanonicalValidatorClient { - return &CanonicalValidatorClient{ - client: client, - logger: logger, - } -} - -func (v *CanonicalValidatorClient) GetMinimumHeight(ctx context.Context) (uint64, error) { - return v.client.GetHeight(ctx) -} - -func (v *CanonicalValidatorClient) GetCurrentHeight(ctx context.Context) (uint64, error) { - return v.client.GetHeight(ctx) -} - -func (v *CanonicalValidatorClient) GetSubnetID(ctx context.Context, blockchainID ids.ID) (ids.ID, error) { - return v.client.ValidatedBy(ctx, blockchainID) -} - -// Gets the current validator set of the given subnet ID, including the validators' BLS public -// keys. The implementation currently makes two RPC requests, one to get the subnet validators, -// and another to get their BLS public keys. This is necessary in order to enable the use of -// the public APIs (which don't support "GetValidatorsAt") because BLS keys are currently only -// associated with primary network validation periods. If ACP-13 is implementated in the future -// (https://github.com/avalanche-foundation/ACPs/blob/main/ACPs/13-subnet-only-validators.md), it -// may become possible to reduce this to a single RPC request that returns both the subnet validators -// as well as their BLS public keys. -func (v *CanonicalValidatorClient) getCurrentValidatorSet( - ctx context.Context, - subnetID ids.ID) (map[ids.NodeID]*validators.GetValidatorOutput, error) { - // Get the current subnet validators. These validators are not expected to include - // BLS signing information given that addPermissionlessValidatorTx is only used to - // add primary network validators. - subnetVdrs, err := v.client.GetCurrentValidators(ctx, subnetID, nil) - if err != nil { - return nil, err - } - - // Look up the primary network validators of the NodeIDs validating the subnet - // in order to get their BLS keys. - res := make(map[ids.NodeID]*validators.GetValidatorOutput, len(subnetVdrs)) - subnetNodeIDs := make([]ids.NodeID, 0, len(subnetVdrs)) - for _, subnetVdr := range subnetVdrs { - subnetNodeIDs = append(subnetNodeIDs, subnetVdr.NodeID) - res[subnetVdr.NodeID] = &validators.GetValidatorOutput{ - NodeID: subnetVdr.NodeID, - Weight: subnetVdr.Weight, - } - } - primaryVdrs, err := v.client.GetCurrentValidators(ctx, ids.Empty, subnetNodeIDs) - if err != nil { - return nil, err - } - - // Set the BLS keys of the result. - for _, primaryVdr := range primaryVdrs { - // We expect all of the primary network validators to already be in `res` because - // we filtered the request to node IDs that were identified as validators of the - // specific subnet ID. - vdr, ok := res[primaryVdr.NodeID] - if !ok { - v.logger.Warn( - "Unexpected primary network validator returned by getCurrentValidators request", - zap.String("subnetID", subnetID.String()), - zap.String("nodeID", primaryVdr.NodeID.String())) - continue - } - - // Validators that do not have a BLS public key registered on the P-chain are still - // included in the result because they affect the stake weight of the subnet validators. - // Such validators will not be queried for BLS signatures of warp messages. As long as - // sufficient stake percentage of subnet validators have registered BLS public keys, - // messages can still be successfully relayed. - if primaryVdr.Signer != nil { - vdr.PublicKey = primaryVdr.Signer.Key() - } - } - - return res, nil -} - -// Gets the validator set of the given subnet at the given P-chain block height. -// Attempts to use the "getValidatorsAt" API first. If not available, falls back -// to use "getCurrentValidators", ignoring the specified P-chain block height. -func (v *CanonicalValidatorClient) GetValidatorSet( - ctx context.Context, - height uint64, - subnetID ids.ID, -) (map[ids.NodeID]*validators.GetValidatorOutput, error) { - // First, attempt to use the "getValidatorsAt" RPC method. This method may not be available on - // all API nodes, in which case we can fall back to using "getCurrentValidators" if needed. - res, err := v.client.GetValidatorsAt(ctx, subnetID, height) - if err != nil { - v.logger.Debug( - "P-chain RPC to getValidatorAt returned error. Falling back to getCurrentValidators", - zap.String("subnetID", subnetID.String()), - zap.Uint64("pChainHeight", height), - zap.Error(err)) - return v.getCurrentValidatorSet(ctx, subnetID) - } - return res, nil -} diff --git a/relayer/chains/avalanche/warp/relayer/relayer.go b/relayer/chains/avalanche/warp/relayer/relayer.go deleted file mode 100644 index 88aca9ff3..000000000 --- a/relayer/chains/avalanche/warp/relayer/relayer.go +++ /dev/null @@ -1,212 +0,0 @@ -// Copyright (C) 2023, Ava Labs, Inc. All rights reserved. -// See the file LICENSE for licensing terms. - -package relayer - -import ( - "fmt" - "math/rand" - "strconv" - - "github.com/ava-labs/avalanchego/ids" - "github.com/ava-labs/avalanchego/message" - "github.com/ava-labs/avalanchego/utils/logging" - "github.com/ava-labs/avalanchego/utils/set" - "github.com/ava-labs/avalanchego/vms/platformvm" - "github.com/ethereum/go-ethereum/common" - "go.uber.org/zap" - - "github.com/cosmos/relayer/v2/relayer/chains/avalanche/warp" -) - -// Relayer handles all messages sent from a given source chain -type Relayer struct { - pChainClient platformvm.Client - canonicalValidatorClient *CanonicalValidatorClient - currentRequestID uint32 - network *warp.AppRequestNetwork - sourceSubnetID ids.ID - sourceBlockchainID ids.ID - responseChan chan message.InboundMessage - contractMessage vms.ContractMessage - messageManagers map[common.Hash]messages.MessageManager - logger logging.Logger - supportedDestinations set.Set[ids.ID] - apiNodeURI string -} - -func NewRelayer( - logger logging.Logger, - sourceSubnetInfo config.SourceSubnet, - pChainClient platformvm.Client, - network *warp.AppRequestNetwork, - responseChan chan message.InboundMessage, - destinationClients map[ids.ID]vms.DestinationClient, - shouldProcessMissedBlocks bool, -) (*Relayer, error) { - - subnetID, err := ids.FromString(sourceSubnetInfo.SubnetID) - if err != nil { - logger.Error( - "Invalid subnetID in configuration", - zap.Error(err), - ) - return nil, err - } - - blockchainID, err := ids.FromString(sourceSubnetInfo.BlockchainID) - if err != nil { - logger.Error( - "Failed to decode base-58 encoded source chain ID", - zap.Error(err), - ) - return nil, err - } - - var filteredDestinationClients map[ids.ID]vms.DestinationClient - supportedDestinationsBlockchainIDs := sourceSubnetInfo.GetSupportedDestinations() - if len(supportedDestinationsBlockchainIDs) > 0 { - filteredDestinationClients := make(map[ids.ID]vms.DestinationClient) - for id := range supportedDestinationsBlockchainIDs { - filteredDestinationClients[id] = destinationClients[id] - } - } else { - filteredDestinationClients = destinationClients - } - - // Create message managers for each supported message protocol - messageManagers := make(map[common.Hash]messages.MessageManager) - for address, config := range sourceSubnetInfo.MessageContracts { - addressHash := common.HexToHash(address) - messageManager, err := messages.NewMessageManager(logger, addressHash, config, filteredDestinationClients) - if err != nil { - logger.Error( - "Failed to create message manager", - zap.Error(err), - ) - return nil, err - } - messageManagers[addressHash] = messageManager - } - - logger.Info( - "Creating relayer", - zap.String("subnetID", subnetID.String()), - zap.String("subnetIDHex", subnetID.Hex()), - zap.String("blockchainID", blockchainID.String()), - zap.String("blockchainIDHex", blockchainID.Hex()), - ) - r := Relayer{ - pChainClient: pChainClient, - canonicalValidatorClient: NewCanonicalValidatorClient(logger, pChainClient), - currentRequestID: rand.Uint32(), // Initialize to a random value to mitigate requestID collision - network: network, - sourceSubnetID: subnetID, - sourceBlockchainID: blockchainID, - responseChan: responseChan, - contractMessage: vms.NewContractMessage(logger, sourceSubnetInfo), - messageManagers: messageManagers, - logger: logger, - supportedDestinations: supportedDestinationsBlockchainIDs, - } - - return &r, nil -} - -// RelayMessage relays a single warp message to the destination chain. Warp message relay requests from the same origin chain are processed serially -func (r *Relayer) RelayMessage(warpLogInfo *vmtypes.WarpLogInfo, metrics *MessageRelayerMetrics, messageCreator message.Creator) error { - r.logger.Info( - "Relaying message", - zap.String("blockchainID", r.sourceBlockchainID.String()), - ) - // Unpack the VM message bytes into a Warp message - warpMessageInfo, err := r.contractMessage.UnpackWarpMessage(warpLogInfo.UnsignedMsgBytes) - if err != nil { - r.logger.Error( - "Failed to unpack sender message", - zap.Error(err), - ) - return err - } - - r.logger.Info( - "Unpacked warp message", - zap.String("blockchainID", r.sourceBlockchainID.String()), - zap.String("warpMessageID", warpMessageInfo.WarpUnsignedMessage.ID().String()), - ) - - // Check that the warp message is from a support message protocol contract address. - messageManager, supportedMessageProtocol := r.messageManagers[warpLogInfo.SourceAddress] - if !supportedMessageProtocol { - // Do not return an error here because it is expected for there to be messages from other contracts - // than just the ones supported by a single relayer instance. - r.logger.Debug( - "Warp message from unsupported message protocol address. Not relaying.", - zap.String("protocolAddress", warpLogInfo.SourceAddress.Hex()), - ) - return nil - } - - destinationBlockchainID, err := messageManager.GetDestinationBlockchainID(warpMessageInfo) - if err != nil { - r.logger.Error( - "Failed to get destination chain ID", - zap.Error(err), - ) - return err - } - - // Check that the destination chain ID is supported - if !r.CheckSupportedDestination(destinationBlockchainID) { - r.logger.Debug( - "Message destination chain ID not supported. Not relaying.", - zap.String("blockchainID", r.sourceBlockchainID.String()), - zap.String("destinationBlockchainID", destinationBlockchainID.String()), - ) - return nil - } - - // Create and run the message relayer to attempt to deliver the message to the destination chain - messageRelayer := newMessageRelayer(r.logger, metrics, r, warpMessageInfo.WarpUnsignedMessage, destinationBlockchainID, r.responseChan, messageCreator) - if err != nil { - r.logger.Error( - "Failed to create message relayer", - zap.Error(err), - ) - return err - } - - // Relay the message to the destination. Messages from a given source chain must be processed in serial in order to - // guarantee that the previous block (n-1) is fully processed by the relayer when processing a given log from block n. - // TODO: Add a config option to use the Warp API, instead of hardcoding to the app request network here - err = messageRelayer.relayMessage(warpMessageInfo, r.currentRequestID, messageManager, true) - if err != nil { - r.logger.Error( - "Failed to run message relayer", - zap.String("blockchainID", r.sourceBlockchainID.String()), - zap.String("warpMessageID", warpMessageInfo.WarpUnsignedMessage.ID().String()), - zap.Error(err), - ) - return err - } - - // Increment the request ID for the next message relay request - r.currentRequestID++ - - // Update the database with the latest processed block height - err = r.db.Put(r.sourceBlockchainID, []byte(database.LatestProcessedBlockKey), []byte(strconv.FormatUint(warpLogInfo.BlockNumber, 10))) - if err != nil { - r.logger.Error( - fmt.Sprintf("failed to put %s into database", database.LatestProcessedBlockKey), - zap.Error(err), - ) - } - - return nil -} - -// Returns whether destinationBlockchainID is a supported destination. -// If supportedDestinations is empty, then all destination chain IDs are supported. -func (r *Relayer) CheckSupportedDestination(destinationBlockchainID ids.ID) bool { - return len(r.supportedDestinations) == 0 || r.supportedDestinations.Contains(destinationBlockchainID) -} From 4fce905757acc886934fcaedbd6a547e1db90d38 Mon Sep 17 00:00:00 2001 From: ramil Date: Tue, 20 Aug 2024 03:59:40 +0300 Subject: [PATCH 4/5] avalanche: QueryConnection --- relayer/chains/avalanche/query.go | 36 +++++++++++++++++++++++++++---- 1 file changed, 32 insertions(+), 4 deletions(-) diff --git a/relayer/chains/avalanche/query.go b/relayer/chains/avalanche/query.go index 261c7731b..63c65212c 100644 --- a/relayer/chains/avalanche/query.go +++ b/relayer/chains/avalanche/query.go @@ -5,6 +5,7 @@ import ( "encoding/json" "errors" "fmt" + "maps" "math/big" "strconv" "strings" @@ -32,7 +33,6 @@ import ( avalanche "github.com/cosmos/ibc-go/v8/modules/light-clients/14-avalanche" ibccontract "github.com/cosmos/relayer/v2/relayer/chains/avalanche/ibc" "github.com/cosmos/relayer/v2/relayer/provider" - "golang.org/x/exp/maps" ) func (a AvalancheProvider) QueryTx(ctx context.Context, hashHex string) (*provider.RelayerTxResponse, error) { @@ -400,9 +400,37 @@ func (a AvalancheProvider) QueryClients(ctx context.Context) (clienttypes.Identi panic("implement me") } -func (a AvalancheProvider) QueryConnection(ctx context.Context, height int64, connectionid string) (*conntypes.QueryConnectionResponse, error) { - //TODO implement me - panic("implement me") +func (a AvalancheProvider) QueryConnection(ctx context.Context, height int64, connectionID string) (*conntypes.QueryConnectionResponse, error) { + rawConnection, err := a.ibcContract.QueryConnection(&bind.CallOpts{BlockNumber: big.NewInt(height)}, connectionID) + if err != nil { + return nil, fmt.Errorf("failed to get connection %s from Avalanche: %w", connectionID, err) + } + + var conn conntypes.ConnectionEnd + if err := conn.Unmarshal(rawConnection); err != nil { + return nil, fmt.Errorf("failed to umarshal Connection %s: %w", connectionID, err) + } + + // query connection proofs + connectionSlot := ibc.ConnectionSlot(connectionID).Hex() + proofs, err := a.subnetClient.GetProof(ctx, ibc.ContractAddress, []string{connectionSlot}, big.NewInt(height)) + if err != nil { + return nil, fmt.Errorf("failed to query connection proofs (%s) at height %d: %w", connectionID, height, err) + } + + connStateProof, err := proofToBytes(proofs.StorageProof[0].Proof) + if err != nil { + return nil, fmt.Errorf("failed to convert connection proof to bytes: %w", err) + } + + return &conntypes.QueryConnectionResponse{ + Connection: &conn, + Proof: connStateProof, + ProofHeight: clienttypes.Height{ + RevisionNumber: 0, + RevisionHeight: uint64(height), + }, + }, nil } func (a AvalancheProvider) QueryConnections(ctx context.Context) ([]*conntypes.IdentifiedConnection, error) { From f89445a62fd1b94fa274ef3cd81925ec01b6d89b Mon Sep 17 00:00:00 2001 From: Ilnur Date: Mon, 6 Jan 2025 12:13:09 +0400 Subject: [PATCH 5/5] fix package deps after merge --- relayer/chains/avalanche/query.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/relayer/chains/avalanche/query.go b/relayer/chains/avalanche/query.go index 63c65212c..e30fb7545 100644 --- a/relayer/chains/avalanche/query.go +++ b/relayer/chains/avalanche/query.go @@ -5,7 +5,7 @@ import ( "encoding/json" "errors" "fmt" - "maps" + "math/big" "strconv" "strings" @@ -16,6 +16,7 @@ import ( "github.com/ava-labs/subnet-evm/precompile/contracts/ibc" "github.com/cosmos/cosmos-sdk/types/query" "github.com/ethereum/go-ethereum/common" + "golang.org/x/exp/maps" "github.com/ava-labs/avalanchego/utils" "github.com/ava-labs/avalanchego/utils/crypto/bls" @@ -59,6 +60,9 @@ func (a AvalancheProvider) QueryIBCHeader(ctx context.Context, h int64) (provide } validatorSet, vdrs, pChainHeight, err := a.avalancheValidatorSet(ctx, ethHeader.Number.Uint64()) + if err != nil { + return nil, err + } signedStorageRoot, _, err := a.avalancheBlsSignature(ctx, ethHeader.Root.Bytes()) if err != nil {