From 97662918a64efeb2e53340e617bad751f2d52bfb Mon Sep 17 00:00:00 2001 From: Milap Sheth Date: Tue, 30 Jul 2024 16:58:08 -0400 Subject: [PATCH] fix(axelarnet)!: allow routing messages from gov module to wasm (#2168) --- x/axelarnet/handler.go | 16 +++- x/axelarnet/handler_test.go | 125 ++++++++++++++++++++++++++++++ x/axelarnet/message_handler.go | 7 +- x/evm/keeper/vote_handler.go | 5 +- x/evm/keeper/vote_handler_test.go | 4 + 5 files changed, 150 insertions(+), 7 deletions(-) create mode 100644 x/axelarnet/handler_test.go diff --git a/x/axelarnet/handler.go b/x/axelarnet/handler.go index a467f9002..2f9703862 100644 --- a/x/axelarnet/handler.go +++ b/x/axelarnet/handler.go @@ -3,7 +3,9 @@ package axelarnet import ( "encoding/hex" "fmt" + "strings" + "github.com/CosmWasm/wasmd/x/wasm" sdk "github.com/cosmos/cosmos-sdk/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" govtypes "github.com/cosmos/cosmos-sdk/x/gov/types" @@ -14,7 +16,7 @@ import ( "github.com/axelarnetwork/axelar-core/x/axelarnet/keeper" "github.com/axelarnetwork/axelar-core/x/axelarnet/types" nexus "github.com/axelarnetwork/axelar-core/x/nexus/exported" - "github.com/axelarnetwork/utils/funcs" + tss "github.com/axelarnetwork/axelar-core/x/tss/exported" ) // NewHandler returns the handler of the Cosmos module @@ -106,10 +108,18 @@ func NewProposalHandler(k keeper.Keeper, nexusK types.Nexus, accountK types.Acco case *types.CallContractsProposal: for _, contractCall := range c.ContractCalls { sender := nexus.CrossChainAddress{Chain: exported.Axelarnet, Address: accountK.GetModuleAddress(govtypes.ModuleName).String()} - recipient := nexus.CrossChainAddress{Chain: funcs.MustOk(nexusK.GetChain(ctx, contractCall.Chain)), Address: contractCall.ContractAddress} + + destChain, ok := nexusK.GetChain(ctx, contractCall.Chain) + if !ok { + // Try forwarding it to wasm router if destination chain is not registered + // Wasm chain names are always lower case, so normalize it for consistency in core + destChainName := nexus.ChainName(strings.ToLower(contractCall.Chain.String())) + destChain = nexus.Chain{Name: destChainName, SupportsForeignAssets: false, KeyType: tss.None, Module: wasm.ModuleName} + } + recipient := nexus.CrossChainAddress{Chain: destChain, Address: contractCall.ContractAddress} + // axelar gateway expects keccak256 hashes for payloads payloadHash := crypto.Keccak256(contractCall.Payload) - msgID, txID, nonce := nexusK.GenerateMessageID(ctx) msg := nexus.NewGeneralMessage(msgID, sender, recipient, payloadHash, txID, nonce, nil) diff --git a/x/axelarnet/handler_test.go b/x/axelarnet/handler_test.go new file mode 100644 index 000000000..ddd73f318 --- /dev/null +++ b/x/axelarnet/handler_test.go @@ -0,0 +1,125 @@ +package axelarnet_test + +import ( + "encoding/hex" + "fmt" + "strings" + "testing" + + "github.com/CosmWasm/wasmd/x/wasm" + sdk "github.com/cosmos/cosmos-sdk/types" + govtypes "github.com/cosmos/cosmos-sdk/x/gov/types" + "github.com/ethereum/go-ethereum/crypto" + "github.com/stretchr/testify/assert" + + "github.com/axelarnetwork/axelar-core/testutils/rand" + "github.com/axelarnetwork/axelar-core/x/axelarnet" + "github.com/axelarnetwork/axelar-core/x/axelarnet/exported" + "github.com/axelarnetwork/axelar-core/x/axelarnet/keeper" + "github.com/axelarnetwork/axelar-core/x/axelarnet/types" + "github.com/axelarnetwork/axelar-core/x/axelarnet/types/mock" + evmtypes "github.com/axelarnetwork/axelar-core/x/evm/types" + evmtestutils "github.com/axelarnetwork/axelar-core/x/evm/types/testutils" + nexusTypes "github.com/axelarnetwork/axelar-core/x/nexus/exported" + nexustestutils "github.com/axelarnetwork/axelar-core/x/nexus/exported/testutils" + . "github.com/axelarnetwork/utils/test" +) + +func TestProposalHandler(t *testing.T) { + var ( + ctx sdk.Context + k keeper.Keeper + n *mock.NexusMock + a *mock.AccountKeeperMock + handler govtypes.Handler + proposal *types.CallContractsProposal + + destChain nexusTypes.Chain + contractCall types.ContractCall + genMsg nexusTypes.GeneralMessage + governanceAccount sdk.AccAddress + ) + + givenProposal := Given("a CallContractsProposal", func() { + ctx, k, _ = setup() + + destChain = nexustestutils.RandomChain() + destChain.Module = evmtypes.ModuleName + governanceAccount = rand.AccAddr() + + contractCall = types.ContractCall{ + Chain: destChain.Name, + ContractAddress: evmtestutils.RandomAddress().Hex(), + Payload: rand.BytesBetween(100, 500), + } + + proposal = &types.CallContractsProposal{ + Title: "Test Proposal", + Description: "This is a test proposal", + ContractCalls: []types.ContractCall{contractCall}, + } + + n = &mock.NexusMock{ + SetNewMessageFunc: func(ctx sdk.Context, msg nexusTypes.GeneralMessage) error { + genMsg = msg + return nil + }, + GenerateMessageIDFunc: func(ctx sdk.Context) (string, []byte, uint64) { + hash := rand.Bytes(32) + return fmt.Sprintf("%s-%d", hex.EncodeToString(hash[:]), 0), hash[:], 0 + }, + } + + a = &mock.AccountKeeperMock{ + GetModuleAddressFunc: func(name string) sdk.AccAddress { + return governanceAccount + }, + } + + handler = axelarnet.NewProposalHandler(k, n, a) + }) + + whenDestChainIsFound := givenProposal. + When("destination chain is found in nexus", func() { + n.GetChainFunc = func(ctx sdk.Context, chain nexusTypes.ChainName) (nexusTypes.Chain, bool) { + return destChain, true + } + }) + + whenDestChainIsNotFound := givenProposal. + When("destination chain is not found in nexus", func() { + n.GetChainFunc = func(ctx sdk.Context, chain nexusTypes.ChainName) (nexusTypes.Chain, bool) { + if chain == exported.Axelarnet.Name { + return exported.Axelarnet, true + } + return nexusTypes.Chain{}, false + } + }) + + whenDestChainIsFound. + Then("should set new message in nexus", func(t *testing.T) { + err := handler(ctx, proposal) + assert.NoError(t, err) + + assert.Equal(t, genMsg.Sender.Address, governanceAccount.String()) + assert.Equal(t, genMsg.Sender.Chain, exported.Axelarnet) + assert.Equal(t, genMsg.Recipient.Chain, destChain) + assert.Equal(t, genMsg.Recipient.Address, contractCall.ContractAddress) + assert.Equal(t, genMsg.PayloadHash, crypto.Keccak256(contractCall.Payload)) + }). + Run(t) + + whenDestChainIsNotFound. + Then("should set new message in nexus with wasm chain", func(t *testing.T) { + err := handler(ctx, proposal) + assert.NoError(t, err) + + assert.Equal(t, genMsg.Sender.Address, governanceAccount.String()) + assert.Equal(t, genMsg.Sender.Chain, exported.Axelarnet) + assert.Equal(t, genMsg.Recipient.Chain.Name, nexusTypes.ChainName(strings.ToLower(contractCall.Chain.String()))) + assert.Equal(t, genMsg.Recipient.Chain.Module, wasm.ModuleName) + assert.Equal(t, genMsg.Recipient.Address, contractCall.ContractAddress) + assert.Equal(t, genMsg.PayloadHash, crypto.Keccak256(contractCall.Payload)) + }). + Run(t) +} diff --git a/x/axelarnet/message_handler.go b/x/axelarnet/message_handler.go index 897f0079f..8c92bde01 100644 --- a/x/axelarnet/message_handler.go +++ b/x/axelarnet/message_handler.go @@ -252,11 +252,12 @@ func handleMessage(ctx sdk.Context, n types.Nexus, b types.BankKeeper, sourceAdd return err } - chainName := nexus.ChainName(msg.DestinationChain) - destChain, ok := n.GetChain(ctx, chainName) + destChain, ok := n.GetChain(ctx, nexus.ChainName(msg.DestinationChain)) if !ok { // try forwarding it to wasm router if destination chain is not registered - destChain = nexus.Chain{Name: chainName, SupportsForeignAssets: false, KeyType: tss.None, Module: wasm.ModuleName} + // Wasm chain names are always lower case, so normalize it for consistency in core + destChainName := nexus.ChainName(strings.ToLower(msg.DestinationChain)) + destChain = nexus.Chain{Name: destChainName, SupportsForeignAssets: false, KeyType: tss.None, Module: wasm.ModuleName} } recipient := nexus.CrossChainAddress{Chain: destChain, Address: msg.DestinationAddress} diff --git a/x/evm/keeper/vote_handler.go b/x/evm/keeper/vote_handler.go index fa6191abc..8c759df6c 100644 --- a/x/evm/keeper/vote_handler.go +++ b/x/evm/keeper/vote_handler.go @@ -2,6 +2,7 @@ package keeper import ( "fmt" + "strings" "github.com/CosmWasm/wasmd/x/wasm" "github.com/cosmos/cosmos-sdk/codec" @@ -242,7 +243,9 @@ func mustToGeneralMessage(ctx sdk.Context, n types.Nexus, event types.Event) nex destinationChain, ok := n.GetChain(ctx, contractCall.DestinationChain) if !ok { // try forwarding it to wasm router if destination chain is not registered - destinationChain = nexus.Chain{Name: contractCall.DestinationChain, SupportsForeignAssets: false, KeyType: tss.None, Module: wasm.ModuleName} + // Wasm chain names are always lower case, so normalize it for consistency in core + destChainName := nexus.ChainName(strings.ToLower(contractCall.DestinationChain.String())) + destinationChain = nexus.Chain{Name: destChainName, SupportsForeignAssets: false, KeyType: tss.None, Module: wasm.ModuleName} } recipient := nexus.CrossChainAddress{Chain: destinationChain, Address: contractCall.ContractAddress} diff --git a/x/evm/keeper/vote_handler_test.go b/x/evm/keeper/vote_handler_test.go index 93283a5a3..b1e45afde 100644 --- a/x/evm/keeper/vote_handler_test.go +++ b/x/evm/keeper/vote_handler_test.go @@ -3,6 +3,7 @@ package keeper_test import ( "fmt" mathRand "math/rand" + "strings" "testing" "github.com/CosmWasm/wasmd/x/wasm" @@ -401,6 +402,9 @@ func TestHandleResult(t *testing.T) { for _, call := range nexusK.SetNewMessageCalls() { assert.Equal(t, wasm.ModuleName, call.M.Recipient.Chain.Module) + + destChainName := call.M.Recipient.Chain.Name.String() + assert.Equal(t, strings.ToLower(destChainName), destChainName) } }), ).