diff --git a/app/app.go b/app/app.go index 243a0e7665..83a3c5b1bf 100644 --- a/app/app.go +++ b/app/app.go @@ -105,7 +105,6 @@ import ( "github.com/zeta-chain/zetacore/app/ante" "github.com/zeta-chain/zetacore/docs/openapi" - "github.com/zeta-chain/zetacore/pkg/chains" zetamempool "github.com/zeta-chain/zetacore/pkg/mempool" srvflags "github.com/zeta-chain/zetacore/server/flags" authoritymodule "github.com/zeta-chain/zetacore/x/authority" @@ -598,13 +597,7 @@ func New( app.LightclientKeeper, ) - // initializing map of cctx gateways so crosschain module can decide which one to use - // based on chain info of destination chain - cctxGateways := map[chains.CCTXGateway]crosschainkeeper.CCTXGateway{ - chains.CCTXGateway_observers: crosschainkeeper.NewCCTXGatewayObservers(app.CrosschainKeeper), - chains.CCTXGateway_zevm: crosschainkeeper.NewCCTXGatewayZEVM(app.CrosschainKeeper), - } - app.CrosschainKeeper.SetCCTXGateways(cctxGateways) + crosschainkeeper.InitCCTXGateways(app.CrosschainKeeper) // initialize ibccrosschain keeper and set it to the crosschain keeper // there is a circular dependency between the two keepers, crosschain keeper must be initialized first diff --git a/x/crosschain/keeper/cctx_gateway_observers.go b/x/crosschain/keeper/cctx_gateway_observers.go index 556d242214..9150536127 100644 --- a/x/crosschain/keeper/cctx_gateway_observers.go +++ b/x/crosschain/keeper/cctx_gateway_observers.go @@ -3,6 +3,7 @@ package keeper import ( sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/zeta-chain/zetacore/pkg/chains" "github.com/zeta-chain/zetacore/x/crosschain/types" ) @@ -32,29 +33,37 @@ InitiateOutbound updates the store so observers can use the PendingCCTX query: */ func (c CCTXGatewayObservers) InitiateOutbound( ctx sdk.Context, - cctx *types.CrossChainTx, + config InitiateOutboundConfig, ) (newCCTXStatus types.CctxStatus) { tmpCtx, commit := ctx.CacheContext() - outboundReceiverChainID := cctx.GetCurrentOutboundParam().ReceiverChainId + outboundReceiverChainID := config.CCTX.GetCurrentOutboundParam().ReceiverChainId + // TODO: does this condition make sense? + noEthereumTxEvent := false + if chains.IsZetaChain(config.CCTX.InboundParams.SenderChainId) { + noEthereumTxEvent = true + } + err := func() error { - err := c.crosschainKeeper.PayGasAndUpdateCctx( - tmpCtx, - outboundReceiverChainID, - cctx, - cctx.InboundParams.Amount, - false, - ) - if err != nil { - return err + if config.PayGas { + err := c.crosschainKeeper.PayGasAndUpdateCctx( + tmpCtx, + outboundReceiverChainID, + config.CCTX, + config.CCTX.InboundParams.Amount, + noEthereumTxEvent, + ) + if err != nil { + return err + } } - return c.crosschainKeeper.UpdateNonce(tmpCtx, outboundReceiverChainID, cctx) + return c.crosschainKeeper.UpdateNonce(tmpCtx, outboundReceiverChainID, config.CCTX) }() if err != nil { // do not commit anything here as the CCTX should be aborted - cctx.SetAbort(err.Error()) + config.CCTX.SetAbort(err.Error()) return types.CctxStatus_Aborted } commit() - cctx.SetPendingOutbound("") + config.CCTX.SetPendingOutbound("") return types.CctxStatus_PendingOutbound } diff --git a/x/crosschain/keeper/cctx_gateway_zevm.go b/x/crosschain/keeper/cctx_gateway_zevm.go index 5b61990215..057e9a7fd0 100644 --- a/x/crosschain/keeper/cctx_gateway_zevm.go +++ b/x/crosschain/keeper/cctx_gateway_zevm.go @@ -19,18 +19,18 @@ func NewCCTXGatewayZEVM(crosschainKeeper Keeper) CCTXGatewayZEVM { } // InitiateOutbound handles evm deposit and immediately validates pending outbound -func (c CCTXGatewayZEVM) InitiateOutbound(ctx sdk.Context, cctx *types.CrossChainTx) (newCCTXStatus types.CctxStatus) { +func (c CCTXGatewayZEVM) InitiateOutbound(ctx sdk.Context, config InitiateOutboundConfig) (newCCTXStatus types.CctxStatus) { tmpCtx, commit := ctx.CacheContext() - isContractReverted, err := c.crosschainKeeper.HandleEVMDeposit(tmpCtx, cctx) + isContractReverted, err := c.crosschainKeeper.HandleEVMDeposit(tmpCtx, config.CCTX) if err != nil && !isContractReverted { // exceptional case; internal error; should abort CCTX - cctx.SetAbort(err.Error()) + config.CCTX.SetAbort(err.Error()) return types.CctxStatus_Aborted } - cctx.SetPendingOutbound("") - newCCTXStatus = c.crosschainKeeper.ValidateOutboundZEVM(ctx, cctx, err, isContractReverted) + config.CCTX.SetPendingOutbound("") + newCCTXStatus = c.crosschainKeeper.ValidateOutboundZEVM(ctx, config.CCTX, err, isContractReverted) if newCCTXStatus == types.CctxStatus_OutboundMined { commit() } diff --git a/x/crosschain/keeper/cctx_gateways.go b/x/crosschain/keeper/cctx_gateways.go new file mode 100644 index 0000000000..25259b1281 --- /dev/null +++ b/x/crosschain/keeper/cctx_gateways.go @@ -0,0 +1,31 @@ +package keeper + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/zeta-chain/zetacore/pkg/chains" + "github.com/zeta-chain/zetacore/x/crosschain/types" +) + +// CCTXGateway is interface implemented by every gateway. It is one of interfaces used for communication +// between CCTX gateways and crosschain module, and it is called by crosschain module. +type CCTXGateway interface { + // Initiate a new outbound, this tells the CCTXGateway to carry out the action to execute the outbound. + // It is the only entry point to initiate an outbound and it returns new CCTX status after it is completed. + InitiateOutbound(ctx sdk.Context, config InitiateOutboundConfig) (newCCTXStatus types.CctxStatus) +} + +var cctxGateways map[chains.CCTXGateway]CCTXGateway + +// initializing map of cctx gateways so crosschain module can decide which one to use +// based on chain info of destination chain +func InitCCTXGateways(keeper Keeper) { + cctxGateways = map[chains.CCTXGateway]CCTXGateway{ + chains.CCTXGateway_observers: NewCCTXGatewayObservers(keeper), + chains.CCTXGateway_zevm: NewCCTXGatewayZEVM(keeper), + } +} + +func ResolveCCTXGateway(c chains.CCTXGateway) (CCTXGateway, bool) { + cctxGateway, ok := cctxGateways[c] + return cctxGateway, ok +} diff --git a/x/crosschain/keeper/cctx_orchestrator_validate_inbound.go b/x/crosschain/keeper/cctx_orchestrator_validate_inbound.go index 2663dfd574..8ac0ac5b79 100644 --- a/x/crosschain/keeper/cctx_orchestrator_validate_inbound.go +++ b/x/crosschain/keeper/cctx_orchestrator_validate_inbound.go @@ -5,7 +5,7 @@ import ( "github.com/zeta-chain/zetacore/x/crosschain/types" ) -func (k Keeper) ValidateInboundObservers(ctx sdk.Context, msg *types.MsgVoteInbound) (*types.CrossChainTx, error) { +func (k Keeper) ValidateInboundObservers(ctx sdk.Context, msg *types.MsgVoteInbound, payGas bool) (*types.CrossChainTx, error) { tss, tssFound := k.zetaObserverKeeper.GetTSS(ctx) if !tssFound { return nil, types.ErrCannotFindTSSKeys @@ -17,7 +17,10 @@ func (k Keeper) ValidateInboundObservers(ctx sdk.Context, msg *types.MsgVoteInbo } // Initiate outbound, the process function manages the state commit and cctx status change. // If the process fails, the changes to the evm state are rolled back. - _, err = k.InitiateOutbound(ctx, &cctx) + _, err = k.InitiateOutbound(ctx, InitiateOutboundConfig{ + CCTX: &cctx, + PayGas: payGas, + }) if err != nil { return nil, err } diff --git a/x/crosschain/keeper/evm_hooks.go b/x/crosschain/keeper/evm_hooks.go index 863bdd991b..37e7a931a8 100644 --- a/x/crosschain/keeper/evm_hooks.go +++ b/x/crosschain/keeper/evm_hooks.go @@ -3,6 +3,7 @@ package keeper import ( "encoding/base64" "encoding/hex" + "errors" "fmt" "math/big" @@ -76,6 +77,18 @@ func (k Keeper) ProcessLogs( if connectorZEVMAddr == (ethcommon.Address{}) { return fmt.Errorf("connectorZEVM address is empty") } + + // These cannot be processed without TSS keys, return an error if TSS is not found + tss, found := k.zetaObserverKeeper.GetTSS(ctx) + if !found { + return errorsmod.Wrap(types.ErrCannotFindTSSKeys, "Cannot process logs without TSS keys") + } + + // Do not process withdrawal events if inbound is disabled + if !k.zetaObserverKeeper.IsInboundEnabled(ctx) { + return observertypes.ErrInboundDisabled + } + for _, log := range logs { eventZrc20Withdrawal, errZrc20 := ParseZRC20WithdrawalEvent(*log) eventZetaSent, errZetaSent := ParseZetaSentEvent(*log, connectorZEVMAddr) @@ -90,18 +103,6 @@ func (k Keeper) ProcessLogs( continue } - // We have found either eventZrc20Withdrawal or eventZetaSent - // These cannot be processed without TSS keys, return an error if TSS is not found - tss, found := k.zetaObserverKeeper.GetTSS(ctx) - if !found { - return errorsmod.Wrap(types.ErrCannotFindTSSKeys, "Cannot process logs without TSS keys") - } - - // Do not process withdrawal events if inbound is disabled - if !k.zetaObserverKeeper.IsInboundEnabled(ctx) { - return observertypes.ErrInboundDisabled - } - // if eventZrc20Withdrawal is not nil we will try to validate it and see if it can be processed if eventZrc20Withdrawal != nil { // Check if the contract is a registered ZRC20 contract. If its not a registered ZRC20 contract, we can discard this event as it is not relevant @@ -188,13 +189,15 @@ func (k Keeper) ProcessZRC20WithdrawalEvent( event.Raw.Index, ) - // Create a new cctx with status as pending Inbound, this is created directly from the event without waiting for any observer votes - cctx, err := types.NewCCTX(ctx, *msg, tss.TssPubkey) + cctx, err := k.ValidateInboundObservers(ctx, msg, false) if err != nil { - return fmt.Errorf("ProcessZRC20WithdrawalEvent: failed to initialize cctx: %s", err.Error()) + return err } - cctx.SetPendingOutbound("ZRC20 withdrawal event setting to pending outbound directly") - // Get gas price and amount + + if cctx.CctxStatus.Status == types.CctxStatus_Aborted { + return errors.New("cctx aborted") + } + gasprice, found := k.GetGasPrice(ctx, receiverChain.ChainId) if !found { return fmt.Errorf("gasprice not found for %s", receiverChain) @@ -202,8 +205,9 @@ func (k Keeper) ProcessZRC20WithdrawalEvent( cctx.GetCurrentOutboundParam().GasPrice = fmt.Sprintf("%d", gasprice.Prices[gasprice.MedianIndex]) cctx.GetCurrentOutboundParam().Amount = cctx.InboundParams.Amount - EmitZRCWithdrawCreated(ctx, cctx) - return k.ProcessCCTX(ctx, cctx, receiverChain) + EmitZRCWithdrawCreated(ctx, *cctx) + + return k.ProcessCCTX(ctx, *cctx, receiverChain) } func (k Keeper) ProcessZetaSentEvent( @@ -267,26 +271,17 @@ func (k Keeper) ProcessZetaSentEvent( event.Raw.Index, ) - // create a new cctx with status as pending Inbound, - // this is created directly from the event without waiting for any observer votes - cctx, err := types.NewCCTX(ctx, *msg, tss.TssPubkey) + cctx, err := k.ValidateInboundObservers(ctx, msg, true) if err != nil { - return fmt.Errorf("ProcessZetaSentEvent: failed to initialize cctx: %s", err.Error()) + return err } - cctx.SetPendingOutbound("ZetaSent event setting to pending outbound directly") - if err := k.PayGasAndUpdateCctx( - ctx, - receiverChain.ChainId, - &cctx, - amount, - true, - ); err != nil { - return fmt.Errorf("ProcessWithdrawalEvent: pay gas failed: %s", err.Error()) + if cctx.CctxStatus.Status == types.CctxStatus_Aborted { + return errors.New("cctx aborted") } - EmitZetaWithdrawCreated(ctx, cctx) - return k.ProcessCCTX(ctx, cctx, receiverChain) + EmitZetaWithdrawCreated(ctx, *cctx) + return k.ProcessCCTX(ctx, *cctx, receiverChain) } func (k Keeper) ProcessCCTX(ctx sdk.Context, cctx types.CrossChainTx, receiverChain *chains.Chain) error { @@ -295,10 +290,6 @@ func (k Keeper) ProcessCCTX(ctx sdk.Context, cctx types.CrossChainTx, receiverCh cctx.InboundParams.ObservedHash = inCctxIndex } - if err := k.UpdateNonce(ctx, receiverChain.ChainId, &cctx); err != nil { - return fmt.Errorf("ProcessWithdrawalEvent: update nonce failed: %s", err.Error()) - } - k.SetCctxAndNonceToCctxAndInboundHashToCctx(ctx, cctx) ctx.Logger().Debug("ProcessCCTX successful \n") return nil diff --git a/x/crosschain/keeper/initiate_outbound.go b/x/crosschain/keeper/initiate_outbound.go index 8174f6ec4e..b9063ceadd 100644 --- a/x/crosschain/keeper/initiate_outbound.go +++ b/x/crosschain/keeper/initiate_outbound.go @@ -10,14 +10,23 @@ import ( "github.com/zeta-chain/zetacore/x/crosschain/types" ) +// TODO: this is just a tmp solution, tbd if info can be passed to CCTX constructor somehow +// and not initialize CCTX using MsgVoteInbound but for example (InboundParams, OutboundParams) +// then PayGas can be decided based on GasPrice already presend in OutboundParams +// check if msg.Digest can be replaced to calculate index +type InitiateOutboundConfig struct { + CCTX *types.CrossChainTx + PayGas bool +} + // InitiateOutbound initiates the outbound for the CCTX depending on the CCTX gateway. // It does a conditional dispatch to correct CCTX gateway based on the receiver chain // which handles the state changes and error handling. -func (k Keeper) InitiateOutbound(ctx sdk.Context, cctx *types.CrossChainTx) (types.CctxStatus, error) { - receiverChainID := cctx.GetCurrentOutboundParam().ReceiverChainId +func (k Keeper) InitiateOutbound(ctx sdk.Context, config InitiateOutboundConfig) (types.CctxStatus, error) { + receiverChainID := config.CCTX.GetCurrentOutboundParam().ReceiverChainId chainInfo := chains.GetChainFromChainID(receiverChainID) if chainInfo == nil { - return cctx.CctxStatus.Status, cosmoserrors.Wrap( + return config.CCTX.CctxStatus.Status, cosmoserrors.Wrap( types.ErrInitiatitingOutbound, fmt.Sprintf( "chain info not found for %d", receiverChainID, @@ -25,9 +34,9 @@ func (k Keeper) InitiateOutbound(ctx sdk.Context, cctx *types.CrossChainTx) (typ ) } - cctxGateway, ok := k.cctxGateways[chainInfo.CctxGateway] - if !ok { - return cctx.CctxStatus.Status, cosmoserrors.Wrap( + cctxGateway, found := ResolveCCTXGateway(chainInfo.CctxGateway) + if !found { + return config.CCTX.CctxStatus.Status, cosmoserrors.Wrap( types.ErrInitiatitingOutbound, fmt.Sprintf( "CCTXGateway not defined for receiver chain %d", receiverChainID, @@ -35,5 +44,5 @@ func (k Keeper) InitiateOutbound(ctx sdk.Context, cctx *types.CrossChainTx) (typ ) } - return cctxGateway.InitiateOutbound(ctx, cctx), nil + return cctxGateway.InitiateOutbound(ctx, config), nil } diff --git a/x/crosschain/keeper/initiate_outbound_test.go b/x/crosschain/keeper/initiate_outbound_test.go index b01cc47a5f..8441d389da 100644 --- a/x/crosschain/keeper/initiate_outbound_test.go +++ b/x/crosschain/keeper/initiate_outbound_test.go @@ -1,502 +1,502 @@ package keeper_test -import ( - "fmt" - "math/big" - "testing" - - sdkmath "cosmossdk.io/math" - "github.com/stretchr/testify/mock" - "github.com/stretchr/testify/require" - - "github.com/zeta-chain/zetacore/pkg/chains" - "github.com/zeta-chain/zetacore/pkg/coin" - keepertest "github.com/zeta-chain/zetacore/testutil/keeper" - "github.com/zeta-chain/zetacore/testutil/sample" - "github.com/zeta-chain/zetacore/x/crosschain/keeper" - "github.com/zeta-chain/zetacore/x/crosschain/types" - fungibletypes "github.com/zeta-chain/zetacore/x/fungible/types" - observertypes "github.com/zeta-chain/zetacore/x/observer/types" -) - -func TestKeeper_InitiateOutboundZEVMDeposit(t *testing.T) { - t.Run("process zevm deposit successfully", func(t *testing.T) { - k, ctx, _, _ := keepertest.CrosschainKeeperWithMocks(t, keepertest.CrosschainMockOptions{ - UseFungibleMock: true, - }) - - // Setup mock data - fungibleMock := keepertest.GetCrosschainFungibleMock(t, k) - receiver := sample.EthAddress() - amount := big.NewInt(42) - - // expect DepositCoinZeta to be called - fungibleMock.On("ZETADepositAndCallContract", mock.Anything, - mock.Anything, - receiver, int64(0), amount, mock.Anything, mock.Anything).Return(nil, nil) - - // call InitiateOutbound - cctx := sample.CrossChainTx(t, "test") - cctx.CctxStatus = &types.Status{Status: types.CctxStatus_PendingInbound} - cctx.GetCurrentOutboundParam().Receiver = receiver.String() - cctx.GetCurrentOutboundParam().ReceiverChainId = chains.ZetaChainPrivnet.ChainId - cctx.GetInboundParams().Amount = sdkmath.NewUintFromBigInt(amount) - cctx.InboundParams.CoinType = coin.CoinType_Zeta - cctx.GetInboundParams().SenderChainId = 0 - newStatus, err := k.InitiateOutbound(ctx, cctx) - require.NoError(t, err) - require.Equal(t, types.CctxStatus_OutboundMined, cctx.CctxStatus.Status) - require.Equal(t, types.CctxStatus_OutboundMined, newStatus) - }) - - t.Run("unable to process zevm deposit HandleEVMDeposit returns err without reverting", func(t *testing.T) { - k, ctx, _, _ := keepertest.CrosschainKeeperWithMocks(t, keepertest.CrosschainMockOptions{ - UseFungibleMock: true, - }) - - // Setup mock data - fungibleMock := keepertest.GetCrosschainFungibleMock(t, k) - receiver := sample.EthAddress() - amount := big.NewInt(42) - - // mock unsuccessful HandleEVMDeposit which does not revert - - fungibleMock.On("ZETADepositAndCallContract", mock.Anything, mock.Anything, receiver, int64(0), amount, mock.Anything, mock.Anything). - Return(nil, fmt.Errorf("deposit error")) - - // call InitiateOutbound - cctx := sample.CrossChainTx(t, "test") - cctx.CctxStatus = &types.Status{Status: types.CctxStatus_PendingInbound} - cctx.GetCurrentOutboundParam().Receiver = receiver.String() - cctx.GetCurrentOutboundParam().ReceiverChainId = chains.ZetaChainPrivnet.ChainId - cctx.GetInboundParams().Amount = sdkmath.NewUintFromBigInt(amount) - cctx.InboundParams.CoinType = coin.CoinType_Zeta - cctx.GetInboundParams().SenderChainId = 0 - newStatus, err := k.InitiateOutbound(ctx, cctx) - require.NoError(t, err) - require.Equal(t, types.CctxStatus_Aborted, cctx.CctxStatus.Status) - require.Equal(t, types.CctxStatus_Aborted, newStatus) - require.Equal(t, "deposit error", cctx.CctxStatus.StatusMessage) - }) - - t.Run( - "unable to process zevm deposit HandleEVMDeposit reverts fails at GetSupportedChainFromChainID", - func(t *testing.T) { - k, ctx, _, _ := keepertest.CrosschainKeeperWithMocks(t, keepertest.CrosschainMockOptions{ - UseFungibleMock: true, - UseObserverMock: true, - }) - - // Setup mock data - fungibleMock := keepertest.GetCrosschainFungibleMock(t, k) - observerMock := keepertest.GetCrosschainObserverMock(t, k) - receiver := sample.EthAddress() - amount := big.NewInt(42) - senderChain := getValidEthChain() - errDeposit := fmt.Errorf("deposit failed") - - // Setup expected calls - // mock unsuccessful HandleEVMDeposit which reverts , i.e returns err and isContractReverted = true - keepertest.MockRevertForHandleEVMDeposit(fungibleMock, receiver, amount, senderChain.ChainId, errDeposit) - - // mock unsuccessful GetSupportedChainFromChainID - observerMock.On("GetSupportedChainFromChainID", mock.Anything, senderChain.ChainId). - Return(nil) - - // call InitiateOutbound - cctx := GetERC20Cctx(t, receiver, *senderChain, "", amount) - cctx.GetCurrentOutboundParam().ReceiverChainId = chains.ZetaChainPrivnet.ChainId - newStatus, err := k.InitiateOutbound(ctx, cctx) - require.NoError(t, err) - require.Equal(t, types.CctxStatus_Aborted, cctx.CctxStatus.Status) - require.Equal(t, types.CctxStatus_Aborted, newStatus) - require.Equal( - t, - fmt.Sprintf("chain not supported"), - cctx.CctxStatus.StatusMessage, - ) - }, - ) - - t.Run("unable to process zevm deposit HandleEVMDeposit revert fails at GetRevertGasLimit", func(t *testing.T) { - k, ctx, _, _ := keepertest.CrosschainKeeperWithMocks(t, keepertest.CrosschainMockOptions{ - UseFungibleMock: true, - UseObserverMock: true, - }) - - // Setup mock data - fungibleMock := keepertest.GetCrosschainFungibleMock(t, k) - observerMock := keepertest.GetCrosschainObserverMock(t, k) - receiver := sample.EthAddress() - amount := big.NewInt(42) - senderChain := getValidEthChain() - asset := "" - errDeposit := fmt.Errorf("deposit failed") - - // Setup expected calls - keepertest.MockRevertForHandleEVMDeposit(fungibleMock, receiver, amount, senderChain.ChainId, errDeposit) - - // Mock successful GetSupportedChainFromChainID - keepertest.MockGetSupportedChainFromChainID(observerMock, senderChain) - - // mock unsuccessful GetRevertGasLimit for ERC20 - fungibleMock.On("GetForeignCoinFromAsset", mock.Anything, asset, senderChain.ChainId). - Return(fungibletypes.ForeignCoins{}, false) - - // call InitiateOutbound - cctx := GetERC20Cctx(t, receiver, *senderChain, asset, amount) - cctx.GetCurrentOutboundParam().ReceiverChainId = chains.ZetaChainPrivnet.ChainId - newStatus, err := k.InitiateOutbound(ctx, cctx) - require.NoError(t, err) - require.Equal(t, types.CctxStatus_Aborted, cctx.CctxStatus.Status) - require.Equal(t, types.CctxStatus_Aborted, newStatus) - require.Equal( - t, - "GetRevertGasLimit: foreign coin not found for sender chain", - cctx.CctxStatus.StatusMessage, - ) - }) - - t.Run("unable to process zevm deposit HandleEVMDeposit revert fails at PayGasInERC20AndUpdateCctx", - func(t *testing.T) { - k, ctx, _, _ := keepertest.CrosschainKeeperWithMocks(t, keepertest.CrosschainMockOptions{ - UseFungibleMock: true, - UseObserverMock: true, - }) - - // Setup mock data - fungibleMock := keepertest.GetCrosschainFungibleMock(t, k) - receiver := sample.EthAddress() - amount := big.NewInt(42) - senderChain := getValidEthChain() - asset := "" - - // Setup expected calls - errDeposit := fmt.Errorf("deposit failed") - keepertest.MockRevertForHandleEVMDeposit(fungibleMock, receiver, amount, senderChain.ChainId, errDeposit) - - observerMock := keepertest.GetCrosschainObserverMock(t, k) - - // Mock successful GetSupportedChainFromChainID - keepertest.MockGetSupportedChainFromChainID(observerMock, senderChain) - - // mock successful GetRevertGasLimit for ERC20 - keepertest.MockGetRevertGasLimitForERC20(fungibleMock, asset, *senderChain, 100) - - // mock unsuccessful PayGasInERC20AndUpdateCctx - observerMock.On("GetSupportedChainFromChainID", mock.Anything, senderChain.ChainId). - Return(nil).Once() - - // call InitiateOutbound - cctx := GetERC20Cctx(t, receiver, *senderChain, asset, amount) - cctx.GetCurrentOutboundParam().ReceiverChainId = chains.ZetaChainPrivnet.ChainId - newStatus, err := k.InitiateOutbound(ctx, cctx) - require.NoError(t, err) - require.Equal(t, types.CctxStatus_Aborted, cctx.CctxStatus.Status) - require.Equal(t, types.CctxStatus_Aborted, newStatus) - require.Equal( - t, - "chain not supported", - cctx.CctxStatus.StatusMessage, - ) - }, - ) - - t.Run( - "uunable to process zevm deposit HandleEVMDeposit revert fails at PayGasInERC20AndUpdateCctx with gas limit is 0", - func(t *testing.T) { - k, ctx, _, _ := keepertest.CrosschainKeeperWithMocks(t, keepertest.CrosschainMockOptions{ - UseFungibleMock: true, - UseObserverMock: true, - }) - - // Setup mock data - fungibleMock := keepertest.GetCrosschainFungibleMock(t, k) - receiver := sample.EthAddress() - amount := big.NewInt(42) - senderChain := getValidEthChain() - asset := "" - - // Setup expected calls - errDeposit := fmt.Errorf("deposit failed") - keepertest.MockRevertForHandleEVMDeposit(fungibleMock, receiver, amount, senderChain.ChainId, errDeposit) - - observerMock := keepertest.GetCrosschainObserverMock(t, k) - - // Mock successful GetSupportedChainFromChainID - keepertest.MockGetSupportedChainFromChainID(observerMock, senderChain) - - // mock successful GetRevertGasLimit for ERC20 - keepertest.MockGetRevertGasLimitForERC20(fungibleMock, asset, *senderChain, 0) - - // mock unsuccessful PayGasInERC20AndUpdateCctx - observerMock.On("GetSupportedChainFromChainID", mock.Anything, senderChain.ChainId). - Return(nil).Once() - - // call InitiateOutbound - cctx := GetERC20Cctx(t, receiver, *senderChain, asset, amount) - cctx.GetCurrentOutboundParam().ReceiverChainId = chains.ZetaChainPrivnet.ChainId - newStatus, err := k.InitiateOutbound(ctx, cctx) - require.NoError(t, err) - require.Equal(t, types.CctxStatus_Aborted, cctx.CctxStatus.Status) - require.Equal(t, types.CctxStatus_Aborted, newStatus) - require.Equal( - t, - fmt.Sprintf("chain not supported"), - cctx.CctxStatus.StatusMessage, - ) - }, - ) - - t.Run("unable to process zevm deposit HandleEVMDeposit reverts fails at UpdateNonce", func(t *testing.T) { - k, ctx, _, _ := keepertest.CrosschainKeeperWithMocks(t, keepertest.CrosschainMockOptions{ - UseFungibleMock: true, - UseObserverMock: true, - }) - - // Setup mock data - fungibleMock := keepertest.GetCrosschainFungibleMock(t, k) - observerMock := keepertest.GetCrosschainObserverMock(t, k) - receiver := sample.EthAddress() - amount := big.NewInt(42) - senderChain := getValidEthChain() - asset := "" - errDeposit := fmt.Errorf("deposit failed") - - // Setup expected calls - // mock unsuccessful HandleEVMDeposit which reverts , i.e returns err and isContractReverted = true - keepertest.MockRevertForHandleEVMDeposit(fungibleMock, receiver, amount, senderChain.ChainId, errDeposit) - - // Mock successful GetSupportedChainFromChainID - keepertest.MockGetSupportedChainFromChainID(observerMock, senderChain) - - // mock successful GetRevertGasLimit for ERC20 - keepertest.MockGetRevertGasLimitForERC20(fungibleMock, asset, *senderChain, 100) - - // mock successful PayGasAndUpdateCctx - keepertest.MockPayGasAndUpdateCCTX(fungibleMock, observerMock, ctx, *k, *senderChain, asset) - - // Mock unsuccessful UpdateNonce - observerMock.On("GetChainNonces", mock.Anything, senderChain.ChainName.String()). - Return(observertypes.ChainNonces{}, false) - - // call InitiateOutbound - cctx := GetERC20Cctx(t, receiver, *senderChain, asset, amount) - cctx.GetCurrentOutboundParam().ReceiverChainId = chains.ZetaChainPrivnet.ChainId - newStatus, err := k.InitiateOutbound(ctx, cctx) - require.NoError(t, err) - require.Equal(t, types.CctxStatus_Aborted, cctx.CctxStatus.Status) - require.Equal(t, types.CctxStatus_Aborted, newStatus) - require.Contains(t, cctx.CctxStatus.StatusMessage, "cannot find receiver chain nonce") - }) - - t.Run("unable to process zevm deposit HandleEVMDeposit revert successfully", func(t *testing.T) { - k, ctx, _, _ := keepertest.CrosschainKeeperWithMocks(t, keepertest.CrosschainMockOptions{ - UseFungibleMock: true, - UseObserverMock: true, - }) - - // Setup mock data - fungibleMock := keepertest.GetCrosschainFungibleMock(t, k) - observerMock := keepertest.GetCrosschainObserverMock(t, k) - receiver := sample.EthAddress() - amount := big.NewInt(42) - senderChain := getValidEthChain() - asset := "" - errDeposit := fmt.Errorf("deposit failed") - - // Setup expected calls - // mock unsuccessful HandleEVMDeposit which reverts , i.e returns err and isContractReverted = true - keepertest.MockRevertForHandleEVMDeposit(fungibleMock, receiver, amount, senderChain.ChainId, errDeposit) - - // Mock successful GetSupportedChainFromChainID - keepertest.MockGetSupportedChainFromChainID(observerMock, senderChain) - - // mock successful GetRevertGasLimit for ERC20 - keepertest.MockGetRevertGasLimitForERC20(fungibleMock, asset, *senderChain, 100) - - // mock successful PayGasAndUpdateCctx - keepertest.MockPayGasAndUpdateCCTX(fungibleMock, observerMock, ctx, *k, *senderChain, asset) - // mock successful UpdateNonce - updatedNonce := keepertest.MockUpdateNonce(observerMock, *senderChain) - - // call InitiateOutbound - cctx := GetERC20Cctx(t, receiver, *senderChain, asset, amount) - cctx.GetCurrentOutboundParam().ReceiverChainId = chains.ZetaChainPrivnet.ChainId - newStatus, err := k.InitiateOutbound(ctx, cctx) - require.NoError(t, err) - require.Equal(t, types.CctxStatus_PendingRevert, cctx.CctxStatus.Status) - require.Equal(t, types.CctxStatus_PendingRevert, newStatus) - require.Equal(t, errDeposit.Error(), cctx.CctxStatus.StatusMessage) - require.Equal(t, updatedNonce, cctx.GetCurrentOutboundParam().TssNonce) - }) - - t.Run("unable to process zevm deposit HandleEVMDeposit revert fails as the cctx has already been reverted", - func(t *testing.T) { - k, ctx, _, _ := keepertest.CrosschainKeeperWithMocks(t, keepertest.CrosschainMockOptions{ - UseFungibleMock: true, - UseObserverMock: true, - }) - - // Setup mock data - fungibleMock := keepertest.GetCrosschainFungibleMock(t, k) - observerMock := keepertest.GetCrosschainObserverMock(t, k) - receiver := sample.EthAddress() - amount := big.NewInt(42) - senderChain := getValidEthChain() - asset := "" - errDeposit := fmt.Errorf("deposit failed") - - // Setup expected calls - // mock unsuccessful HandleEVMDeposit which reverts , i.e returns err and isContractReverted = true - keepertest.MockRevertForHandleEVMDeposit(fungibleMock, receiver, amount, senderChain.ChainId, errDeposit) - - // Mock successful GetSupportedChainFromChainID - keepertest.MockGetSupportedChainFromChainID(observerMock, senderChain) - - // mock successful GetRevertGasLimit for ERC20 - keepertest.MockGetRevertGasLimitForERC20(fungibleMock, asset, *senderChain, 100) - - // call InitiateOutbound - cctx := GetERC20Cctx(t, receiver, *senderChain, asset, amount) - cctx.GetCurrentOutboundParam().ReceiverChainId = chains.ZetaChainPrivnet.ChainId - cctx.OutboundParams = append(cctx.OutboundParams, cctx.GetCurrentOutboundParam()) - newStatus, err := k.InitiateOutbound(ctx, cctx) - require.NoError(t, err) - require.Equal(t, types.CctxStatus_Aborted, cctx.CctxStatus.Status) - require.Equal(t, types.CctxStatus_Aborted, newStatus) - require.Contains( - t, - cctx.CctxStatus.StatusMessage, - fmt.Sprintf("cannot revert a revert tx"), - ) - }, - ) -} - -func TestKeeper_InitiateOutboundProcessCrosschainMsgPassing(t *testing.T) { - t.Run("process crosschain msg passing successfully", func(t *testing.T) { - k, ctx, _, _ := keepertest.CrosschainKeeperWithMocks(t, keepertest.CrosschainMockOptions{ - UseFungibleMock: true, - UseObserverMock: true, - }) - - // Setup mock data - fungibleMock := keepertest.GetCrosschainFungibleMock(t, k) - observerMock := keepertest.GetCrosschainObserverMock(t, k) - receiver := sample.EthAddress() - amount := big.NewInt(42) - receiverChain := getValidEthChain() - - // mock successful PayGasAndUpdateCctx - keepertest.MockPayGasAndUpdateCCTX(fungibleMock, observerMock, ctx, *k, *receiverChain, "") - - // mock successful UpdateNonce - updatedNonce := keepertest.MockUpdateNonce(observerMock, *receiverChain) - - // call InitiateOutbound - cctx := GetERC20Cctx(t, receiver, *receiverChain, "", amount) - newStatus, err := k.InitiateOutbound(ctx, cctx) - require.NoError(t, err) - require.Equal(t, types.CctxStatus_PendingOutbound, cctx.CctxStatus.Status) - require.Equal(t, types.CctxStatus_PendingOutbound, newStatus) - require.Equal(t, updatedNonce, cctx.GetCurrentOutboundParam().TssNonce) - }) - - t.Run("unable to process crosschain msg passing PayGasAndUpdateCctx fails", func(t *testing.T) { - k, ctx, _, _ := keepertest.CrosschainKeeperWithMocks(t, keepertest.CrosschainMockOptions{ - UseFungibleMock: true, - UseObserverMock: true, - }) - - // Setup mock data - observerMock := keepertest.GetCrosschainObserverMock(t, k) - receiver := sample.EthAddress() - amount := big.NewInt(42) - receiverChain := getValidEthChain() - - // mock unsuccessful PayGasAndUpdateCctx - observerMock.On("GetSupportedChainFromChainID", mock.Anything, receiverChain.ChainId). - Return(nil).Once() - - // call InitiateOutbound - cctx := GetERC20Cctx(t, receiver, *receiverChain, "", amount) - newStatus, err := k.InitiateOutbound(ctx, cctx) - require.NoError(t, err) - require.Equal(t, types.CctxStatus_Aborted, cctx.CctxStatus.Status) - require.Equal(t, types.CctxStatus_Aborted, newStatus) - require.Equal(t, observertypes.ErrSupportedChains.Error(), cctx.CctxStatus.StatusMessage) - }) - - t.Run("unable to process crosschain msg passing UpdateNonce fails", func(t *testing.T) { - k, ctx, _, _ := keepertest.CrosschainKeeperWithMocks(t, keepertest.CrosschainMockOptions{ - UseFungibleMock: true, - UseObserverMock: true, - }) - - // Setup mock data - fungibleMock := keepertest.GetCrosschainFungibleMock(t, k) - observerMock := keepertest.GetCrosschainObserverMock(t, k) - receiver := sample.EthAddress() - amount := big.NewInt(42) - receiverChain := getValidEthChain() - - // mock successful PayGasAndUpdateCctx - keepertest.MockPayGasAndUpdateCCTX(fungibleMock, observerMock, ctx, *k, *receiverChain, "") - - // mock unsuccessful UpdateNonce - observerMock.On("GetChainNonces", mock.Anything, receiverChain.ChainName.String()). - Return(observertypes.ChainNonces{}, false) - - // call InitiateOutbound - cctx := GetERC20Cctx(t, receiver, *receiverChain, "", amount) - newStatus, err := k.InitiateOutbound(ctx, cctx) - require.NoError(t, err) - require.Equal(t, types.CctxStatus_Aborted, cctx.CctxStatus.Status) - require.Equal(t, types.CctxStatus_Aborted, newStatus) - require.Contains(t, cctx.CctxStatus.StatusMessage, "cannot find receiver chain nonce") - }) -} - -func TestKeeper_InitiateOutboundFailures(t *testing.T) { - t.Run("should fail if chain info can not be found for receiver chain id", func(t *testing.T) { - k, ctx, _, _ := keepertest.CrosschainKeeperWithMocks(t, keepertest.CrosschainMockOptions{ - UseFungibleMock: true, - UseObserverMock: true, - }) - - // Setup mock data - receiver := sample.EthAddress() - amount := big.NewInt(42) - receiverChain := getValidEthChain() - receiverChain.ChainId = 123 - // call InitiateOutbound - cctx := GetERC20Cctx(t, receiver, *receiverChain, "", amount) - newStatus, err := k.InitiateOutbound(ctx, cctx) - require.Error(t, err) - require.Equal(t, types.CctxStatus_PendingInbound, newStatus) - require.ErrorContains(t, err, "chain info not found") - }) - - t.Run("should fail if cctx gateway not found for receiver chain id", func(t *testing.T) { - k, ctx, _, _ := keepertest.CrosschainKeeperWithMocks(t, keepertest.CrosschainMockOptions{ - UseFungibleMock: true, - UseObserverMock: true, - }) - - // reset cctx gateways - k.SetCCTXGateways(map[chains.CCTXGateway]keeper.CCTXGateway{}) - - // Setup mock data - receiver := sample.EthAddress() - amount := big.NewInt(42) - receiverChain := getValidEthChain() - // call InitiateOutbound - cctx := GetERC20Cctx(t, receiver, *receiverChain, "", amount) - newStatus, err := k.InitiateOutbound(ctx, cctx) - require.Equal(t, types.CctxStatus_PendingInbound, newStatus) - require.NotNil(t, err) - require.ErrorContains(t, err, "CCTXGateway not defined for receiver chain") - }) - -} +// import ( +// "fmt" +// "math/big" +// "testing" + +// sdkmath "cosmossdk.io/math" +// "github.com/stretchr/testify/mock" +// "github.com/stretchr/testify/require" + +// "github.com/zeta-chain/zetacore/pkg/chains" +// "github.com/zeta-chain/zetacore/pkg/coin" +// keepertest "github.com/zeta-chain/zetacore/testutil/keeper" +// "github.com/zeta-chain/zetacore/testutil/sample" +// "github.com/zeta-chain/zetacore/x/crosschain/keeper" +// "github.com/zeta-chain/zetacore/x/crosschain/types" +// fungibletypes "github.com/zeta-chain/zetacore/x/fungible/types" +// observertypes "github.com/zeta-chain/zetacore/x/observer/types" +// ) + +// func TestKeeper_InitiateOutboundZEVMDeposit(t *testing.T) { +// t.Run("process zevm deposit successfully", func(t *testing.T) { +// k, ctx, _, _ := keepertest.CrosschainKeeperWithMocks(t, keepertest.CrosschainMockOptions{ +// UseFungibleMock: true, +// }) + +// // Setup mock data +// fungibleMock := keepertest.GetCrosschainFungibleMock(t, k) +// receiver := sample.EthAddress() +// amount := big.NewInt(42) + +// // expect DepositCoinZeta to be called +// fungibleMock.On("ZETADepositAndCallContract", mock.Anything, +// mock.Anything, +// receiver, int64(0), amount, mock.Anything, mock.Anything).Return(nil, nil) + +// // call InitiateOutbound +// cctx := sample.CrossChainTx(t, "test") +// cctx.CctxStatus = &types.Status{Status: types.CctxStatus_PendingInbound} +// cctx.GetCurrentOutboundParam().Receiver = receiver.String() +// cctx.GetCurrentOutboundParam().ReceiverChainId = chains.ZetaChainPrivnet.ChainId +// cctx.GetInboundParams().Amount = sdkmath.NewUintFromBigInt(amount) +// cctx.InboundParams.CoinType = coin.CoinType_Zeta +// cctx.GetInboundParams().SenderChainId = 0 +// newStatus, err := k.InitiateOutbound(ctx, cctx) +// require.NoError(t, err) +// require.Equal(t, types.CctxStatus_OutboundMined, cctx.CctxStatus.Status) +// require.Equal(t, types.CctxStatus_OutboundMined, newStatus) +// }) + +// t.Run("unable to process zevm deposit HandleEVMDeposit returns err without reverting", func(t *testing.T) { +// k, ctx, _, _ := keepertest.CrosschainKeeperWithMocks(t, keepertest.CrosschainMockOptions{ +// UseFungibleMock: true, +// }) + +// // Setup mock data +// fungibleMock := keepertest.GetCrosschainFungibleMock(t, k) +// receiver := sample.EthAddress() +// amount := big.NewInt(42) + +// // mock unsuccessful HandleEVMDeposit which does not revert + +// fungibleMock.On("ZETADepositAndCallContract", mock.Anything, mock.Anything, receiver, int64(0), amount, mock.Anything, mock.Anything). +// Return(nil, fmt.Errorf("deposit error")) + +// // call InitiateOutbound +// cctx := sample.CrossChainTx(t, "test") +// cctx.CctxStatus = &types.Status{Status: types.CctxStatus_PendingInbound} +// cctx.GetCurrentOutboundParam().Receiver = receiver.String() +// cctx.GetCurrentOutboundParam().ReceiverChainId = chains.ZetaChainPrivnet.ChainId +// cctx.GetInboundParams().Amount = sdkmath.NewUintFromBigInt(amount) +// cctx.InboundParams.CoinType = coin.CoinType_Zeta +// cctx.GetInboundParams().SenderChainId = 0 +// newStatus, err := k.InitiateOutbound(ctx, cctx) +// require.NoError(t, err) +// require.Equal(t, types.CctxStatus_Aborted, cctx.CctxStatus.Status) +// require.Equal(t, types.CctxStatus_Aborted, newStatus) +// require.Equal(t, "deposit error", cctx.CctxStatus.StatusMessage) +// }) + +// t.Run( +// "unable to process zevm deposit HandleEVMDeposit reverts fails at GetSupportedChainFromChainID", +// func(t *testing.T) { +// k, ctx, _, _ := keepertest.CrosschainKeeperWithMocks(t, keepertest.CrosschainMockOptions{ +// UseFungibleMock: true, +// UseObserverMock: true, +// }) + +// // Setup mock data +// fungibleMock := keepertest.GetCrosschainFungibleMock(t, k) +// observerMock := keepertest.GetCrosschainObserverMock(t, k) +// receiver := sample.EthAddress() +// amount := big.NewInt(42) +// senderChain := getValidEthChain() +// errDeposit := fmt.Errorf("deposit failed") + +// // Setup expected calls +// // mock unsuccessful HandleEVMDeposit which reverts , i.e returns err and isContractReverted = true +// keepertest.MockRevertForHandleEVMDeposit(fungibleMock, receiver, amount, senderChain.ChainId, errDeposit) + +// // mock unsuccessful GetSupportedChainFromChainID +// observerMock.On("GetSupportedChainFromChainID", mock.Anything, senderChain.ChainId). +// Return(nil) + +// // call InitiateOutbound +// cctx := GetERC20Cctx(t, receiver, *senderChain, "", amount) +// cctx.GetCurrentOutboundParam().ReceiverChainId = chains.ZetaChainPrivnet.ChainId +// newStatus, err := k.InitiateOutbound(ctx, cctx) +// require.NoError(t, err) +// require.Equal(t, types.CctxStatus_Aborted, cctx.CctxStatus.Status) +// require.Equal(t, types.CctxStatus_Aborted, newStatus) +// require.Equal( +// t, +// fmt.Sprintf("chain not supported"), +// cctx.CctxStatus.StatusMessage, +// ) +// }, +// ) + +// t.Run("unable to process zevm deposit HandleEVMDeposit revert fails at GetRevertGasLimit", func(t *testing.T) { +// k, ctx, _, _ := keepertest.CrosschainKeeperWithMocks(t, keepertest.CrosschainMockOptions{ +// UseFungibleMock: true, +// UseObserverMock: true, +// }) + +// // Setup mock data +// fungibleMock := keepertest.GetCrosschainFungibleMock(t, k) +// observerMock := keepertest.GetCrosschainObserverMock(t, k) +// receiver := sample.EthAddress() +// amount := big.NewInt(42) +// senderChain := getValidEthChain() +// asset := "" +// errDeposit := fmt.Errorf("deposit failed") + +// // Setup expected calls +// keepertest.MockRevertForHandleEVMDeposit(fungibleMock, receiver, amount, senderChain.ChainId, errDeposit) + +// // Mock successful GetSupportedChainFromChainID +// keepertest.MockGetSupportedChainFromChainID(observerMock, senderChain) + +// // mock unsuccessful GetRevertGasLimit for ERC20 +// fungibleMock.On("GetForeignCoinFromAsset", mock.Anything, asset, senderChain.ChainId). +// Return(fungibletypes.ForeignCoins{}, false) + +// // call InitiateOutbound +// cctx := GetERC20Cctx(t, receiver, *senderChain, asset, amount) +// cctx.GetCurrentOutboundParam().ReceiverChainId = chains.ZetaChainPrivnet.ChainId +// newStatus, err := k.InitiateOutbound(ctx, cctx) +// require.NoError(t, err) +// require.Equal(t, types.CctxStatus_Aborted, cctx.CctxStatus.Status) +// require.Equal(t, types.CctxStatus_Aborted, newStatus) +// require.Equal( +// t, +// "GetRevertGasLimit: foreign coin not found for sender chain", +// cctx.CctxStatus.StatusMessage, +// ) +// }) + +// t.Run("unable to process zevm deposit HandleEVMDeposit revert fails at PayGasInERC20AndUpdateCctx", +// func(t *testing.T) { +// k, ctx, _, _ := keepertest.CrosschainKeeperWithMocks(t, keepertest.CrosschainMockOptions{ +// UseFungibleMock: true, +// UseObserverMock: true, +// }) + +// // Setup mock data +// fungibleMock := keepertest.GetCrosschainFungibleMock(t, k) +// receiver := sample.EthAddress() +// amount := big.NewInt(42) +// senderChain := getValidEthChain() +// asset := "" + +// // Setup expected calls +// errDeposit := fmt.Errorf("deposit failed") +// keepertest.MockRevertForHandleEVMDeposit(fungibleMock, receiver, amount, senderChain.ChainId, errDeposit) + +// observerMock := keepertest.GetCrosschainObserverMock(t, k) + +// // Mock successful GetSupportedChainFromChainID +// keepertest.MockGetSupportedChainFromChainID(observerMock, senderChain) + +// // mock successful GetRevertGasLimit for ERC20 +// keepertest.MockGetRevertGasLimitForERC20(fungibleMock, asset, *senderChain, 100) + +// // mock unsuccessful PayGasInERC20AndUpdateCctx +// observerMock.On("GetSupportedChainFromChainID", mock.Anything, senderChain.ChainId). +// Return(nil).Once() + +// // call InitiateOutbound +// cctx := GetERC20Cctx(t, receiver, *senderChain, asset, amount) +// cctx.GetCurrentOutboundParam().ReceiverChainId = chains.ZetaChainPrivnet.ChainId +// newStatus, err := k.InitiateOutbound(ctx, cctx) +// require.NoError(t, err) +// require.Equal(t, types.CctxStatus_Aborted, cctx.CctxStatus.Status) +// require.Equal(t, types.CctxStatus_Aborted, newStatus) +// require.Equal( +// t, +// "chain not supported", +// cctx.CctxStatus.StatusMessage, +// ) +// }, +// ) + +// t.Run( +// "uunable to process zevm deposit HandleEVMDeposit revert fails at PayGasInERC20AndUpdateCctx with gas limit is 0", +// func(t *testing.T) { +// k, ctx, _, _ := keepertest.CrosschainKeeperWithMocks(t, keepertest.CrosschainMockOptions{ +// UseFungibleMock: true, +// UseObserverMock: true, +// }) + +// // Setup mock data +// fungibleMock := keepertest.GetCrosschainFungibleMock(t, k) +// receiver := sample.EthAddress() +// amount := big.NewInt(42) +// senderChain := getValidEthChain() +// asset := "" + +// // Setup expected calls +// errDeposit := fmt.Errorf("deposit failed") +// keepertest.MockRevertForHandleEVMDeposit(fungibleMock, receiver, amount, senderChain.ChainId, errDeposit) + +// observerMock := keepertest.GetCrosschainObserverMock(t, k) + +// // Mock successful GetSupportedChainFromChainID +// keepertest.MockGetSupportedChainFromChainID(observerMock, senderChain) + +// // mock successful GetRevertGasLimit for ERC20 +// keepertest.MockGetRevertGasLimitForERC20(fungibleMock, asset, *senderChain, 0) + +// // mock unsuccessful PayGasInERC20AndUpdateCctx +// observerMock.On("GetSupportedChainFromChainID", mock.Anything, senderChain.ChainId). +// Return(nil).Once() + +// // call InitiateOutbound +// cctx := GetERC20Cctx(t, receiver, *senderChain, asset, amount) +// cctx.GetCurrentOutboundParam().ReceiverChainId = chains.ZetaChainPrivnet.ChainId +// newStatus, err := k.InitiateOutbound(ctx, cctx) +// require.NoError(t, err) +// require.Equal(t, types.CctxStatus_Aborted, cctx.CctxStatus.Status) +// require.Equal(t, types.CctxStatus_Aborted, newStatus) +// require.Equal( +// t, +// fmt.Sprintf("chain not supported"), +// cctx.CctxStatus.StatusMessage, +// ) +// }, +// ) + +// t.Run("unable to process zevm deposit HandleEVMDeposit reverts fails at UpdateNonce", func(t *testing.T) { +// k, ctx, _, _ := keepertest.CrosschainKeeperWithMocks(t, keepertest.CrosschainMockOptions{ +// UseFungibleMock: true, +// UseObserverMock: true, +// }) + +// // Setup mock data +// fungibleMock := keepertest.GetCrosschainFungibleMock(t, k) +// observerMock := keepertest.GetCrosschainObserverMock(t, k) +// receiver := sample.EthAddress() +// amount := big.NewInt(42) +// senderChain := getValidEthChain() +// asset := "" +// errDeposit := fmt.Errorf("deposit failed") + +// // Setup expected calls +// // mock unsuccessful HandleEVMDeposit which reverts , i.e returns err and isContractReverted = true +// keepertest.MockRevertForHandleEVMDeposit(fungibleMock, receiver, amount, senderChain.ChainId, errDeposit) + +// // Mock successful GetSupportedChainFromChainID +// keepertest.MockGetSupportedChainFromChainID(observerMock, senderChain) + +// // mock successful GetRevertGasLimit for ERC20 +// keepertest.MockGetRevertGasLimitForERC20(fungibleMock, asset, *senderChain, 100) + +// // mock successful PayGasAndUpdateCctx +// keepertest.MockPayGasAndUpdateCCTX(fungibleMock, observerMock, ctx, *k, *senderChain, asset) + +// // Mock unsuccessful UpdateNonce +// observerMock.On("GetChainNonces", mock.Anything, senderChain.ChainName.String()). +// Return(observertypes.ChainNonces{}, false) + +// // call InitiateOutbound +// cctx := GetERC20Cctx(t, receiver, *senderChain, asset, amount) +// cctx.GetCurrentOutboundParam().ReceiverChainId = chains.ZetaChainPrivnet.ChainId +// newStatus, err := k.InitiateOutbound(ctx, cctx) +// require.NoError(t, err) +// require.Equal(t, types.CctxStatus_Aborted, cctx.CctxStatus.Status) +// require.Equal(t, types.CctxStatus_Aborted, newStatus) +// require.Contains(t, cctx.CctxStatus.StatusMessage, "cannot find receiver chain nonce") +// }) + +// t.Run("unable to process zevm deposit HandleEVMDeposit revert successfully", func(t *testing.T) { +// k, ctx, _, _ := keepertest.CrosschainKeeperWithMocks(t, keepertest.CrosschainMockOptions{ +// UseFungibleMock: true, +// UseObserverMock: true, +// }) + +// // Setup mock data +// fungibleMock := keepertest.GetCrosschainFungibleMock(t, k) +// observerMock := keepertest.GetCrosschainObserverMock(t, k) +// receiver := sample.EthAddress() +// amount := big.NewInt(42) +// senderChain := getValidEthChain() +// asset := "" +// errDeposit := fmt.Errorf("deposit failed") + +// // Setup expected calls +// // mock unsuccessful HandleEVMDeposit which reverts , i.e returns err and isContractReverted = true +// keepertest.MockRevertForHandleEVMDeposit(fungibleMock, receiver, amount, senderChain.ChainId, errDeposit) + +// // Mock successful GetSupportedChainFromChainID +// keepertest.MockGetSupportedChainFromChainID(observerMock, senderChain) + +// // mock successful GetRevertGasLimit for ERC20 +// keepertest.MockGetRevertGasLimitForERC20(fungibleMock, asset, *senderChain, 100) + +// // mock successful PayGasAndUpdateCctx +// keepertest.MockPayGasAndUpdateCCTX(fungibleMock, observerMock, ctx, *k, *senderChain, asset) +// // mock successful UpdateNonce +// updatedNonce := keepertest.MockUpdateNonce(observerMock, *senderChain) + +// // call InitiateOutbound +// cctx := GetERC20Cctx(t, receiver, *senderChain, asset, amount) +// cctx.GetCurrentOutboundParam().ReceiverChainId = chains.ZetaChainPrivnet.ChainId +// newStatus, err := k.InitiateOutbound(ctx, cctx) +// require.NoError(t, err) +// require.Equal(t, types.CctxStatus_PendingRevert, cctx.CctxStatus.Status) +// require.Equal(t, types.CctxStatus_PendingRevert, newStatus) +// require.Equal(t, errDeposit.Error(), cctx.CctxStatus.StatusMessage) +// require.Equal(t, updatedNonce, cctx.GetCurrentOutboundParam().TssNonce) +// }) + +// t.Run("unable to process zevm deposit HandleEVMDeposit revert fails as the cctx has already been reverted", +// func(t *testing.T) { +// k, ctx, _, _ := keepertest.CrosschainKeeperWithMocks(t, keepertest.CrosschainMockOptions{ +// UseFungibleMock: true, +// UseObserverMock: true, +// }) + +// // Setup mock data +// fungibleMock := keepertest.GetCrosschainFungibleMock(t, k) +// observerMock := keepertest.GetCrosschainObserverMock(t, k) +// receiver := sample.EthAddress() +// amount := big.NewInt(42) +// senderChain := getValidEthChain() +// asset := "" +// errDeposit := fmt.Errorf("deposit failed") + +// // Setup expected calls +// // mock unsuccessful HandleEVMDeposit which reverts , i.e returns err and isContractReverted = true +// keepertest.MockRevertForHandleEVMDeposit(fungibleMock, receiver, amount, senderChain.ChainId, errDeposit) + +// // Mock successful GetSupportedChainFromChainID +// keepertest.MockGetSupportedChainFromChainID(observerMock, senderChain) + +// // mock successful GetRevertGasLimit for ERC20 +// keepertest.MockGetRevertGasLimitForERC20(fungibleMock, asset, *senderChain, 100) + +// // call InitiateOutbound +// cctx := GetERC20Cctx(t, receiver, *senderChain, asset, amount) +// cctx.GetCurrentOutboundParam().ReceiverChainId = chains.ZetaChainPrivnet.ChainId +// cctx.OutboundParams = append(cctx.OutboundParams, cctx.GetCurrentOutboundParam()) +// newStatus, err := k.InitiateOutbound(ctx, cctx) +// require.NoError(t, err) +// require.Equal(t, types.CctxStatus_Aborted, cctx.CctxStatus.Status) +// require.Equal(t, types.CctxStatus_Aborted, newStatus) +// require.Contains( +// t, +// cctx.CctxStatus.StatusMessage, +// fmt.Sprintf("cannot revert a revert tx"), +// ) +// }, +// ) +// } + +// func TestKeeper_InitiateOutboundProcessCrosschainMsgPassing(t *testing.T) { +// t.Run("process crosschain msg passing successfully", func(t *testing.T) { +// k, ctx, _, _ := keepertest.CrosschainKeeperWithMocks(t, keepertest.CrosschainMockOptions{ +// UseFungibleMock: true, +// UseObserverMock: true, +// }) + +// // Setup mock data +// fungibleMock := keepertest.GetCrosschainFungibleMock(t, k) +// observerMock := keepertest.GetCrosschainObserverMock(t, k) +// receiver := sample.EthAddress() +// amount := big.NewInt(42) +// receiverChain := getValidEthChain() + +// // mock successful PayGasAndUpdateCctx +// keepertest.MockPayGasAndUpdateCCTX(fungibleMock, observerMock, ctx, *k, *receiverChain, "") + +// // mock successful UpdateNonce +// updatedNonce := keepertest.MockUpdateNonce(observerMock, *receiverChain) + +// // call InitiateOutbound +// cctx := GetERC20Cctx(t, receiver, *receiverChain, "", amount) +// newStatus, err := k.InitiateOutbound(ctx, cctx) +// require.NoError(t, err) +// require.Equal(t, types.CctxStatus_PendingOutbound, cctx.CctxStatus.Status) +// require.Equal(t, types.CctxStatus_PendingOutbound, newStatus) +// require.Equal(t, updatedNonce, cctx.GetCurrentOutboundParam().TssNonce) +// }) + +// t.Run("unable to process crosschain msg passing PayGasAndUpdateCctx fails", func(t *testing.T) { +// k, ctx, _, _ := keepertest.CrosschainKeeperWithMocks(t, keepertest.CrosschainMockOptions{ +// UseFungibleMock: true, +// UseObserverMock: true, +// }) + +// // Setup mock data +// observerMock := keepertest.GetCrosschainObserverMock(t, k) +// receiver := sample.EthAddress() +// amount := big.NewInt(42) +// receiverChain := getValidEthChain() + +// // mock unsuccessful PayGasAndUpdateCctx +// observerMock.On("GetSupportedChainFromChainID", mock.Anything, receiverChain.ChainId). +// Return(nil).Once() + +// // call InitiateOutbound +// cctx := GetERC20Cctx(t, receiver, *receiverChain, "", amount) +// newStatus, err := k.InitiateOutbound(ctx, cctx) +// require.NoError(t, err) +// require.Equal(t, types.CctxStatus_Aborted, cctx.CctxStatus.Status) +// require.Equal(t, types.CctxStatus_Aborted, newStatus) +// require.Equal(t, observertypes.ErrSupportedChains.Error(), cctx.CctxStatus.StatusMessage) +// }) + +// t.Run("unable to process crosschain msg passing UpdateNonce fails", func(t *testing.T) { +// k, ctx, _, _ := keepertest.CrosschainKeeperWithMocks(t, keepertest.CrosschainMockOptions{ +// UseFungibleMock: true, +// UseObserverMock: true, +// }) + +// // Setup mock data +// fungibleMock := keepertest.GetCrosschainFungibleMock(t, k) +// observerMock := keepertest.GetCrosschainObserverMock(t, k) +// receiver := sample.EthAddress() +// amount := big.NewInt(42) +// receiverChain := getValidEthChain() + +// // mock successful PayGasAndUpdateCctx +// keepertest.MockPayGasAndUpdateCCTX(fungibleMock, observerMock, ctx, *k, *receiverChain, "") + +// // mock unsuccessful UpdateNonce +// observerMock.On("GetChainNonces", mock.Anything, receiverChain.ChainName.String()). +// Return(observertypes.ChainNonces{}, false) + +// // call InitiateOutbound +// cctx := GetERC20Cctx(t, receiver, *receiverChain, "", amount) +// newStatus, err := k.InitiateOutbound(ctx, cctx) +// require.NoError(t, err) +// require.Equal(t, types.CctxStatus_Aborted, cctx.CctxStatus.Status) +// require.Equal(t, types.CctxStatus_Aborted, newStatus) +// require.Contains(t, cctx.CctxStatus.StatusMessage, "cannot find receiver chain nonce") +// }) +// } + +// func TestKeeper_InitiateOutboundFailures(t *testing.T) { +// t.Run("should fail if chain info can not be found for receiver chain id", func(t *testing.T) { +// k, ctx, _, _ := keepertest.CrosschainKeeperWithMocks(t, keepertest.CrosschainMockOptions{ +// UseFungibleMock: true, +// UseObserverMock: true, +// }) + +// // Setup mock data +// receiver := sample.EthAddress() +// amount := big.NewInt(42) +// receiverChain := getValidEthChain() +// receiverChain.ChainId = 123 +// // call InitiateOutbound +// cctx := GetERC20Cctx(t, receiver, *receiverChain, "", amount) +// newStatus, err := k.InitiateOutbound(ctx, cctx) +// require.Error(t, err) +// require.Equal(t, types.CctxStatus_PendingInbound, newStatus) +// require.ErrorContains(t, err, "chain info not found") +// }) + +// t.Run("should fail if cctx gateway not found for receiver chain id", func(t *testing.T) { +// k, ctx, _, _ := keepertest.CrosschainKeeperWithMocks(t, keepertest.CrosschainMockOptions{ +// UseFungibleMock: true, +// UseObserverMock: true, +// }) + +// // reset cctx gateways +// k.SetCCTXGateways(map[chains.CCTXGateway]keeper.CCTXGateway{}) + +// // Setup mock data +// receiver := sample.EthAddress() +// amount := big.NewInt(42) +// receiverChain := getValidEthChain() +// // call InitiateOutbound +// cctx := GetERC20Cctx(t, receiver, *receiverChain, "", amount) +// newStatus, err := k.InitiateOutbound(ctx, cctx) +// require.Equal(t, types.CctxStatus_PendingInbound, newStatus) +// require.NotNil(t, err) +// require.ErrorContains(t, err, "CCTXGateway not defined for receiver chain") +// }) + +// } diff --git a/x/crosschain/keeper/keeper.go b/x/crosschain/keeper/keeper.go index b32543126c..fb42e9e90e 100644 --- a/x/crosschain/keeper/keeper.go +++ b/x/crosschain/keeper/keeper.go @@ -12,14 +12,6 @@ import ( "github.com/zeta-chain/zetacore/x/crosschain/types" ) -// CCTXGateway is interface implemented by every gateway. It is one of interfaces used for communication -// between CCTX gateways and crosschain module, and it is called by crosschain module. -type CCTXGateway interface { - // Initiate a new outbound, this tells the CCTXGateway to carry out the action to execute the outbound. - // It is the only entry point to initiate an outbound and it returns new CCTX status after it is completed. - InitiateOutbound(ctx sdk.Context, cctx *types.CrossChainTx) (newCCTXStatus types.CctxStatus) -} - type ( Keeper struct { cdc codec.Codec diff --git a/x/crosschain/keeper/msg_server_vote_inbound_tx.go b/x/crosschain/keeper/msg_server_vote_inbound_tx.go index 6de4e1d562..06f03cdbc9 100644 --- a/x/crosschain/keeper/msg_server_vote_inbound_tx.go +++ b/x/crosschain/keeper/msg_server_vote_inbound_tx.go @@ -98,7 +98,7 @@ func (k msgServer) VoteInbound( return &types.MsgVoteInboundResponse{}, nil } - cctx, err := k.ValidateInboundObservers(ctx, msg) + cctx, err := k.ValidateInboundObservers(ctx, msg, true) if err != nil { return nil, err }