From e6287e26bdff17d2aa3895e4492d76fc3a6ca0b0 Mon Sep 17 00:00:00 2001 From: skosito Date: Fri, 21 Jun 2024 03:30:43 +0100 Subject: [PATCH] refactor: cctx validate inbound (#2340) --- app/app.go | 9 --- changelog.md | 3 +- testutil/keeper/crosschain.go | 8 -- x/crosschain/keeper/cctx_gateway_observers.go | 54 ++++++++----- x/crosschain/keeper/cctx_gateway_zevm.go | 15 ++-- x/crosschain/keeper/cctx_gateways.go | 29 +++++++ .../cctx_orchestrator_validate_inbound.go | 50 +++++++++++++ .../cctx_orchestrator_validate_outbound.go | 2 +- x/crosschain/keeper/cctx_utils.go | 4 +- x/crosschain/keeper/cctx_utils_test.go | 14 ++-- x/crosschain/keeper/evm_deposit.go | 4 +- x/crosschain/keeper/evm_hooks.go | 75 +++++-------------- x/crosschain/keeper/evm_hooks_test.go | 56 ++++++-------- x/crosschain/keeper/initiate_outbound.go | 25 +++++-- x/crosschain/keeper/initiate_outbound_test.go | 54 ++++--------- x/crosschain/keeper/keeper.go | 20 +---- .../keeper/msg_server_migrate_tss_funds.go | 2 +- .../keeper/msg_server_vote_inbound_tx.go | 23 ++---- .../keeper/msg_server_vote_inbound_tx_test.go | 6 +- .../keeper/msg_server_whitelist_erc20.go | 2 +- x/crosschain/types/cctx.go | 2 +- 21 files changed, 229 insertions(+), 228 deletions(-) create mode 100644 x/crosschain/keeper/cctx_gateways.go create mode 100644 x/crosschain/keeper/cctx_orchestrator_validate_inbound.go diff --git a/app/app.go b/app/app.go index 243a0e7665..0688ca75db 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,14 +597,6 @@ 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) - // 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/changelog.md b/changelog.md index fb156220c4..583acd57c9 100644 --- a/changelog.md +++ b/changelog.md @@ -47,8 +47,9 @@ * [2269](https://github.com/zeta-chain/node/pull/2269) - refactor MsgUpdateCrosschainFlags into MsgEnableCCTX, MsgDisableCCTX and MsgUpdateGasPriceIncreaseFlags * [2306](https://github.com/zeta-chain/node/pull/2306) - refactor zetaclient outbound transaction signing logic * [2296](https://github.com/zeta-chain/node/pull/2296) - move `testdata` package to `testutil` to organize test-related utilities -* [2344](https://github.com/zeta-chain/node/pull/2344) - group common data of EVM/Bitcoin signer and observer using base structs * [2317](https://github.com/zeta-chain/node/pull/2317) - add ValidateOutbound method for cctx orchestrator +* [2340](https://github.com/zeta-chain/node/pull/2340) - add ValidateInbound method for cctx orchestrator +* [2344](https://github.com/zeta-chain/node/pull/2344) - group common data of EVM/Bitcoin signer and observer using base structs ### Tests diff --git a/testutil/keeper/crosschain.go b/testutil/keeper/crosschain.go index 11fda8128f..aec1ac7838 100644 --- a/testutil/keeper/crosschain.go +++ b/testutil/keeper/crosschain.go @@ -174,16 +174,8 @@ func CrosschainKeeperWithMocks( lightclientKeeper, ) - cctxGateways := map[chains.CCTXGateway]keeper.CCTXGateway{ - chains.CCTXGateway_observers: keeper.NewCCTXGatewayObservers(*k), - chains.CCTXGateway_zevm: keeper.NewCCTXGatewayZEVM(*k), - } - - k.SetCCTXGateways(cctxGateways) - // 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 - var ibcCrosschainKeeperTmp types.IBCCrosschainKeeper = initIBCCrosschainKeeper( cdc, db, diff --git a/x/crosschain/keeper/cctx_gateway_observers.go b/x/crosschain/keeper/cctx_gateway_observers.go index e9603b3903..7155e61cd0 100644 --- a/x/crosschain/keeper/cctx_gateway_observers.go +++ b/x/crosschain/keeper/cctx_gateway_observers.go @@ -1,8 +1,11 @@ package keeper import ( + "fmt" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/zeta-chain/zetacore/pkg/chains" "github.com/zeta-chain/zetacore/x/crosschain/types" ) @@ -32,28 +35,45 @@ InitiateOutbound updates the store so observers can use the PendingCCTX query: */ func (c CCTXGatewayObservers) InitiateOutbound( ctx sdk.Context, - cctx *types.CrossChainTx, -) (newCCTXStatus types.CctxStatus) { + config InitiateOutboundConfig, +) (newCCTXStatus types.CctxStatus, err error) { tmpCtx, commit := ctx.CacheContext() - outboundReceiverChainID := cctx.GetCurrentOutboundParam().ReceiverChainId - err := func() error { - err := c.crosschainKeeper.PayGasAndUpdateCctx( - tmpCtx, - outboundReceiverChainID, - cctx, - cctx.InboundParams.Amount, - false, - ) - if err != nil { - return err + outboundReceiverChainID := config.CCTX.GetCurrentOutboundParam().ReceiverChainId + // TODO (https://github.com/zeta-chain/node/issues/1010): workaround for this bug + noEthereumTxEvent := false + if chains.IsZetaChain(config.CCTX.InboundParams.SenderChainId) { + noEthereumTxEvent = true + } + + err = func() error { + // If ShouldPayGas flag is set during ValidateInbound PayGasAndUpdateCctx should be called + // which will set GasPrice and Amount. Otherwise, use median gas price and InboundParams amount. + if config.ShouldPayGas { + err := c.crosschainKeeper.PayGasAndUpdateCctx( + tmpCtx, + outboundReceiverChainID, + config.CCTX, + config.CCTX.InboundParams.Amount, + noEthereumTxEvent, + ) + if err != nil { + return err + } + } else { + gasPrice, found := c.crosschainKeeper.GetMedianGasPriceInUint(ctx, config.CCTX.GetCurrentOutboundParam().ReceiverChainId) + if !found { + return fmt.Errorf("gasprice not found for %d", config.CCTX.GetCurrentOutboundParam().ReceiverChainId) + } + config.CCTX.GetCurrentOutboundParam().GasPrice = gasPrice.String() + config.CCTX.GetCurrentOutboundParam().Amount = config.CCTX.InboundParams.Amount } - return c.crosschainKeeper.UpdateNonce(tmpCtx, outboundReceiverChainID, cctx) + return c.crosschainKeeper.SetObserverOutboundInfo(tmpCtx, outboundReceiverChainID, config.CCTX) }() if err != nil { // do not commit anything here as the CCTX should be aborted - cctx.SetAbort(err.Error()) - return types.CctxStatus_Aborted + config.CCTX.SetAbort(err.Error()) + return types.CctxStatus_Aborted, err } commit() - return types.CctxStatus_PendingOutbound + return types.CctxStatus_PendingOutbound, nil } diff --git a/x/crosschain/keeper/cctx_gateway_zevm.go b/x/crosschain/keeper/cctx_gateway_zevm.go index c6cadf7f8f..3a6f9a8135 100644 --- a/x/crosschain/keeper/cctx_gateway_zevm.go +++ b/x/crosschain/keeper/cctx_gateway_zevm.go @@ -19,20 +19,23 @@ 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, err error) { 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()) - return types.CctxStatus_Aborted + config.CCTX.SetAbort(err.Error()) + return types.CctxStatus_Aborted, err } - newCCTXStatus = c.crosschainKeeper.ValidateOutboundZEVM(ctx, cctx, err, isContractReverted) + newCCTXStatus = c.crosschainKeeper.ValidateOutboundZEVM(ctx, config.CCTX, err, isContractReverted) if newCCTXStatus == types.CctxStatus_OutboundMined { commit() } - return newCCTXStatus + return newCCTXStatus, nil } diff --git a/x/crosschain/keeper/cctx_gateways.go b/x/crosschain/keeper/cctx_gateways.go new file mode 100644 index 0000000000..9f8e79c0d9 --- /dev/null +++ b/x/crosschain/keeper/cctx_gateways.go @@ -0,0 +1,29 @@ +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, err error) +} + +var cctxGateways map[chains.CCTXGateway]CCTXGateway + +// ResolveCCTXGateway respolves cctx gateway implementation based on provided cctx gateway +func ResolveCCTXGateway(c chains.CCTXGateway, keeper Keeper) (CCTXGateway, bool) { + cctxGateways = map[chains.CCTXGateway]CCTXGateway{ + chains.CCTXGateway_observers: NewCCTXGatewayObservers(keeper), + chains.CCTXGateway_zevm: NewCCTXGatewayZEVM(keeper), + } + + 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 new file mode 100644 index 0000000000..4bb90ec915 --- /dev/null +++ b/x/crosschain/keeper/cctx_orchestrator_validate_inbound.go @@ -0,0 +1,50 @@ +package keeper + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + + "github.com/zeta-chain/zetacore/x/crosschain/types" + observertypes "github.com/zeta-chain/zetacore/x/observer/types" +) + +// ValidateInbound is the only entry-point to create new CCTX (eg. when observers voting is done or new inbound event is detected). +// It creates new CCTX object and calls InitiateOutbound method. +func (k Keeper) ValidateInbound( + ctx sdk.Context, + msg *types.MsgVoteInbound, + shouldPayGas bool, +) (*types.CrossChainTx, error) { + tss, tssFound := k.zetaObserverKeeper.GetTSS(ctx) + if !tssFound { + return nil, types.ErrCannotFindTSSKeys + } + + // Do not process if inbound is disabled + if !k.zetaObserverKeeper.IsInboundEnabled(ctx) { + return nil, observertypes.ErrInboundDisabled + } + + // create a new CCTX from the inbound message. The status of the new CCTX is set to PendingInbound. + cctx, err := types.NewCCTX(ctx, *msg, tss.TssPubkey) + if err != nil { + return nil, err + } + + // 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, InitiateOutboundConfig{ + CCTX: &cctx, + ShouldPayGas: shouldPayGas, + }) + if err != nil { + return nil, err + } + + inCctxIndex, ok := ctx.Value(InCCTXIndexKey).(string) + if ok { + cctx.InboundParams.ObservedHash = inCctxIndex + } + k.SetCctxAndNonceToCctxAndInboundHashToCctx(ctx, cctx) + + return &cctx, nil +} diff --git a/x/crosschain/keeper/cctx_orchestrator_validate_outbound.go b/x/crosschain/keeper/cctx_orchestrator_validate_outbound.go index 16b61a3a0a..77989b88fe 100644 --- a/x/crosschain/keeper/cctx_orchestrator_validate_outbound.go +++ b/x/crosschain/keeper/cctx_orchestrator_validate_outbound.go @@ -189,7 +189,7 @@ func (k Keeper) validateFailedOutbound( if err != nil { return err } - err = k.UpdateNonce(ctx, cctx.InboundParams.SenderChainId, cctx) + err = k.SetObserverOutboundInfo(ctx, cctx.InboundParams.SenderChainId, cctx) if err != nil { return err } diff --git a/x/crosschain/keeper/cctx_utils.go b/x/crosschain/keeper/cctx_utils.go index f85b243c1c..3d3cf31cd5 100644 --- a/x/crosschain/keeper/cctx_utils.go +++ b/x/crosschain/keeper/cctx_utils.go @@ -16,9 +16,9 @@ import ( zetaObserverTypes "github.com/zeta-chain/zetacore/x/observer/types" ) -// UpdateNonce sets the CCTX outbound nonce to the next nonce, and updates the nonce of blockchain state. +// SetObserverOutboundInfo sets the CCTX outbound nonce to the next available nonce for the TSS address, and updates the nonce of blockchain state. // It also updates the PendingNonces that is used to track the unfulfilled outbound txs. -func (k Keeper) UpdateNonce(ctx sdk.Context, receiveChainID int64, cctx *types.CrossChainTx) error { +func (k Keeper) SetObserverOutboundInfo(ctx sdk.Context, receiveChainID int64, cctx *types.CrossChainTx) error { chain := k.GetObserverKeeper().GetSupportedChainFromChainID(ctx, receiveChainID) if chain == nil { return zetaObserverTypes.ErrSupportedChains diff --git a/x/crosschain/keeper/cctx_utils_test.go b/x/crosschain/keeper/cctx_utils_test.go index edeefabfa1..798fcdd8c4 100644 --- a/x/crosschain/keeper/cctx_utils_test.go +++ b/x/crosschain/keeper/cctx_utils_test.go @@ -226,7 +226,7 @@ func Test_IsPending(t *testing.T) { } } -func TestKeeper_UpdateNonce(t *testing.T) { +func TestKeeper_SetObserverOutboundInfo(t *testing.T) { t.Run("should error if supported chain is nil", func(t *testing.T) { k, ctx, _, _ := keepertest.CrosschainKeeperWithMocks(t, keepertest.CrosschainMockOptions{ UseObserverMock: true, @@ -236,7 +236,7 @@ func TestKeeper_UpdateNonce(t *testing.T) { // mock failed GetSupportedChainFromChainID keepertest.MockFailedGetSupportedChainFromChainID(observerMock, nil) - err := k.UpdateNonce(ctx, 5, nil) + err := k.SetObserverOutboundInfo(ctx, 5, nil) require.Error(t, err) }) @@ -262,7 +262,7 @@ func TestKeeper_UpdateNonce(t *testing.T) { {Amount: sdkmath.NewUint(1)}, }, } - err := k.UpdateNonce(ctx, 5, &cctx) + err := k.SetObserverOutboundInfo(ctx, 5, &cctx) require.Error(t, err) }) @@ -291,7 +291,7 @@ func TestKeeper_UpdateNonce(t *testing.T) { {Amount: sdkmath.NewUint(1)}, }, } - err := k.UpdateNonce(ctx, 5, &cctx) + err := k.SetObserverOutboundInfo(ctx, 5, &cctx) require.Error(t, err) require.Equal(t, uint64(100), cctx.GetCurrentOutboundParam().TssNonce) }) @@ -324,7 +324,7 @@ func TestKeeper_UpdateNonce(t *testing.T) { {Amount: sdkmath.NewUint(1)}, }, } - err := k.UpdateNonce(ctx, 5, &cctx) + err := k.SetObserverOutboundInfo(ctx, 5, &cctx) require.Error(t, err) }) @@ -358,7 +358,7 @@ func TestKeeper_UpdateNonce(t *testing.T) { {Amount: sdkmath.NewUint(1)}, }, } - err := k.UpdateNonce(ctx, 5, &cctx) + err := k.SetObserverOutboundInfo(ctx, 5, &cctx) require.Error(t, err) }) @@ -395,7 +395,7 @@ func TestKeeper_UpdateNonce(t *testing.T) { {Amount: sdkmath.NewUint(1)}, }, } - err := k.UpdateNonce(ctx, 5, &cctx) + err := k.SetObserverOutboundInfo(ctx, 5, &cctx) require.NoError(t, err) }) } diff --git a/x/crosschain/keeper/evm_deposit.go b/x/crosschain/keeper/evm_deposit.go index 927e9f5eb1..858c709f18 100644 --- a/x/crosschain/keeper/evm_deposit.go +++ b/x/crosschain/keeper/evm_deposit.go @@ -18,6 +18,8 @@ import ( fungibletypes "github.com/zeta-chain/zetacore/x/fungible/types" ) +const InCCTXIndexKey = "inCctxIndex" + // HandleEVMDeposit handles a deposit from an inbound tx // returns (isContractReverted, err) // (true, non-nil) means CallEVM() reverted @@ -102,7 +104,7 @@ func (k Keeper) HandleEVMDeposit(ctx sdk.Context, cctx *types.CrossChainTx) (boo if !evmTxResponse.Failed() && contractCall { logs := evmtypes.LogsToEthereum(evmTxResponse.Logs) if len(logs) > 0 { - ctx = ctx.WithValue("inCctxIndex", cctx.Index) + ctx = ctx.WithValue(InCCTXIndexKey, cctx.Index) txOrigin := cctx.InboundParams.TxOrigin if txOrigin == "" { txOrigin = inboundSender diff --git a/x/crosschain/keeper/evm_hooks.go b/x/crosschain/keeper/evm_hooks.go index 863bdd991b..dbc2d2dbb1 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,7 @@ func (k Keeper) ProcessLogs( if connectorZEVMAddr == (ethcommon.Address{}) { return fmt.Errorf("connectorZEVM address is empty") } + for _, log := range logs { eventZrc20Withdrawal, errZrc20 := ParseZRC20WithdrawalEvent(*log) eventZetaSent, errZetaSent := ParseZetaSentEvent(*log, connectorZEVMAddr) @@ -90,18 +92,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 @@ -119,13 +109,13 @@ func (k Keeper) ProcessLogs( } // If the event is valid, we will process it and create a new CCTX // If the process fails, we will return an error and roll back the transaction - if err := k.ProcessZRC20WithdrawalEvent(ctx, eventZrc20Withdrawal, emittingContract, txOrigin, tss); err != nil { + if err := k.ProcessZRC20WithdrawalEvent(ctx, eventZrc20Withdrawal, emittingContract, txOrigin); err != nil { return err } } // if eventZetaSent is not nil we will try to validate it and see if it can be processed if eventZetaSent != nil { - if err := k.ProcessZetaSentEvent(ctx, eventZetaSent, emittingContract, txOrigin, tss); err != nil { + if err := k.ProcessZetaSentEvent(ctx, eventZetaSent, emittingContract, txOrigin); err != nil { return err } } @@ -140,9 +130,7 @@ func (k Keeper) ProcessZRC20WithdrawalEvent( event *zrc20.ZRC20Withdrawal, emittingContract ethcommon.Address, txOrigin string, - tss observertypes.TSS, ) error { - ctx.Logger().Info(fmt.Sprintf("ZRC20 withdrawal to %s amount %d", hex.EncodeToString(event.To), event.Value)) foreignCoin, found := k.fungibleKeeper.GetForeignCoins(ctx, event.Raw.Address.Hex()) if !found { @@ -188,22 +176,18 @@ 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.ValidateInbound(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 - gasprice, found := k.GetGasPrice(ctx, receiverChain.ChainId) - if !found { - return fmt.Errorf("gasprice not found for %s", receiverChain) + + if cctx.CctxStatus.Status == types.CctxStatus_Aborted { + return errors.New("cctx aborted") } - 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 nil } func (k Keeper) ProcessZetaSentEvent( @@ -211,7 +195,6 @@ func (k Keeper) ProcessZetaSentEvent( event *connectorzevm.ZetaConnectorZEVMZetaSent, emittingContract ethcommon.Address, txOrigin string, - tss observertypes.TSS, ) error { ctx.Logger().Info(fmt.Sprintf( "Zeta withdrawal to %s amount %d to chain with chainId %d", @@ -267,40 +250,16 @@ 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.ValidateInbound(ctx, msg, true) if err != nil { - return fmt.Errorf("ProcessZetaSentEvent: failed to initialize cctx: %s", err.Error()) - } - 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()) - } - - EmitZetaWithdrawCreated(ctx, cctx) - return k.ProcessCCTX(ctx, cctx, receiverChain) -} - -func (k Keeper) ProcessCCTX(ctx sdk.Context, cctx types.CrossChainTx, receiverChain *chains.Chain) error { - inCctxIndex, ok := ctx.Value("inCctxIndex").(string) - if ok { - cctx.InboundParams.ObservedHash = inCctxIndex + return err } - if err := k.UpdateNonce(ctx, receiverChain.ChainId, &cctx); err != nil { - return fmt.Errorf("ProcessWithdrawalEvent: update nonce failed: %s", err.Error()) + if cctx.CctxStatus.Status == types.CctxStatus_Aborted { + return errors.New("cctx aborted") } - k.SetCctxAndNonceToCctxAndInboundHashToCctx(ctx, cctx) - ctx.Logger().Debug("ProcessCCTX successful \n") + EmitZetaWithdrawCreated(ctx, *cctx) return nil } diff --git a/x/crosschain/keeper/evm_hooks_test.go b/x/crosschain/keeper/evm_hooks_test.go index 80f8c3b28b..bc67f34a99 100644 --- a/x/crosschain/keeper/evm_hooks_test.go +++ b/x/crosschain/keeper/evm_hooks_test.go @@ -211,9 +211,8 @@ func TestKeeper_ProcessZRC20WithdrawalEvent(t *testing.T) { event.Raw.Address = zrc20 emittingContract := sample.EthAddress() txOrigin := sample.EthAddress() - tss := sample.Tss() - err = k.ProcessZRC20WithdrawalEvent(ctx, event, emittingContract, txOrigin.Hex(), tss) + err = k.ProcessZRC20WithdrawalEvent(ctx, event, emittingContract, txOrigin.Hex()) require.NoError(t, err) cctxList := k.GetAllCrossChainTx(ctx) require.Len(t, cctxList, 1) @@ -237,9 +236,8 @@ func TestKeeper_ProcessZRC20WithdrawalEvent(t *testing.T) { event.Raw.Address = zrc20 emittingContract := sample.EthAddress() txOrigin := sample.EthAddress() - tss := sample.Tss() - err = k.ProcessZRC20WithdrawalEvent(ctx, event, emittingContract, txOrigin.Hex(), tss) + err = k.ProcessZRC20WithdrawalEvent(ctx, event, emittingContract, txOrigin.Hex()) require.NoError(t, err) cctxList := k.GetAllCrossChainTx(ctx) require.Len(t, cctxList, 1) @@ -262,9 +260,8 @@ func TestKeeper_ProcessZRC20WithdrawalEvent(t *testing.T) { setupGasCoin(t, ctx, zk.FungibleKeeper, sdkk.EvmKeeper, chainID, "ethereum", "ETH") emittingContract := sample.EthAddress() txOrigin := sample.EthAddress() - tss := sample.Tss() - err = k.ProcessZRC20WithdrawalEvent(ctx, event, emittingContract, txOrigin.Hex(), tss) + err = k.ProcessZRC20WithdrawalEvent(ctx, event, emittingContract, txOrigin.Hex()) require.ErrorContains(t, err, "cannot find foreign coin with emittingContract address") require.Empty(t, k.GetAllCrossChainTx(ctx)) }) @@ -283,9 +280,8 @@ func TestKeeper_ProcessZRC20WithdrawalEvent(t *testing.T) { event.Raw.Address = zrc20 emittingContract := sample.EthAddress() txOrigin := sample.EthAddress() - tss := sample.Tss() - err = k.ProcessZRC20WithdrawalEvent(ctx, event, emittingContract, txOrigin.Hex(), tss) + err = k.ProcessZRC20WithdrawalEvent(ctx, event, emittingContract, txOrigin.Hex()) require.ErrorContains(t, err, "chain not supported") require.Empty(t, k.GetAllCrossChainTx(ctx)) }) @@ -305,10 +301,10 @@ func TestKeeper_ProcessZRC20WithdrawalEvent(t *testing.T) { event.Raw.Address = zrc20 emittingContract := sample.EthAddress() txOrigin := sample.EthAddress() - tss := sample.Tss() + ctx = ctx.WithChainID("test_21-1") - err = k.ProcessZRC20WithdrawalEvent(ctx, event, emittingContract, txOrigin.Hex(), tss) + err = k.ProcessZRC20WithdrawalEvent(ctx, event, emittingContract, txOrigin.Hex()) require.ErrorContains(t, err, "failed to convert chainID: chain 21 not found") require.Empty(t, k.GetAllCrossChainTx(ctx)) }) @@ -329,9 +325,8 @@ func TestKeeper_ProcessZRC20WithdrawalEvent(t *testing.T) { event.To = ethcommon.Address{}.Bytes() emittingContract := sample.EthAddress() txOrigin := sample.EthAddress() - tss := sample.Tss() - err = k.ProcessZRC20WithdrawalEvent(ctx, event, emittingContract, txOrigin.Hex(), tss) + err = k.ProcessZRC20WithdrawalEvent(ctx, event, emittingContract, txOrigin.Hex()) require.ErrorContains(t, err, "cannot encode address") require.Empty(t, k.GetAllCrossChainTx(ctx)) }) @@ -354,13 +349,13 @@ func TestKeeper_ProcessZRC20WithdrawalEvent(t *testing.T) { event.Raw.Address = zrc20 emittingContract := sample.EthAddress() txOrigin := sample.EthAddress() - tss := sample.Tss() + fc, _ := zk.FungibleKeeper.GetForeignCoins(ctx, zrc20.Hex()) fungibleMock.On("GetForeignCoins", mock.Anything, mock.Anything).Return(fc, true) fungibleMock.On("QueryGasLimit", mock.Anything, mock.Anything). Return(big.NewInt(0), fmt.Errorf("error querying gas limit")) - err = k.ProcessZRC20WithdrawalEvent(ctx, event, emittingContract, txOrigin.Hex(), tss) + err = k.ProcessZRC20WithdrawalEvent(ctx, event, emittingContract, txOrigin.Hex()) require.ErrorContains(t, err, "error querying gas limit") require.Empty(t, k.GetAllCrossChainTx(ctx)) }) @@ -381,9 +376,8 @@ func TestKeeper_ProcessZRC20WithdrawalEvent(t *testing.T) { event.Raw.Address = zrc20 emittingContract := sample.EthAddress() txOrigin := sample.EthAddress() - tss := sample.Tss() - err = k.ProcessZRC20WithdrawalEvent(ctx, event, emittingContract, txOrigin.Hex(), tss) + err = k.ProcessZRC20WithdrawalEvent(ctx, event, emittingContract, txOrigin.Hex()) require.ErrorContains(t, err, "gasprice not found") require.Empty(t, k.GetAllCrossChainTx(ctx)) }) @@ -408,10 +402,9 @@ func TestKeeper_ProcessZRC20WithdrawalEvent(t *testing.T) { event.Raw.Address = zrc20 emittingContract := sample.EthAddress() txOrigin := sample.EthAddress() - tss := sample.Tss() - err = k.ProcessZRC20WithdrawalEvent(ctx, event, emittingContract, txOrigin.Hex(), tss) - require.ErrorContains(t, err, "ProcessWithdrawalEvent: update nonce failed") + err = k.ProcessZRC20WithdrawalEvent(ctx, event, emittingContract, txOrigin.Hex()) + require.ErrorContains(t, err, "nonce mismatch") require.Empty(t, k.GetAllCrossChainTx(ctx)) }) } @@ -489,9 +482,8 @@ func TestKeeper_ProcessZetaSentEvent(t *testing.T) { require.NoError(t, err) emittingContract := sample.EthAddress() txOrigin := sample.EthAddress() - tss := sample.Tss() - err = k.ProcessZetaSentEvent(ctx, event, emittingContract, txOrigin.Hex(), tss) + err = k.ProcessZetaSentEvent(ctx, event, emittingContract, txOrigin.Hex()) require.NoError(t, err) cctxList := k.GetAllCrossChainTx(ctx) require.Len(t, cctxList, 1) @@ -526,9 +518,8 @@ func TestKeeper_ProcessZetaSentEvent(t *testing.T) { require.NoError(t, err) emittingContract := sample.EthAddress() txOrigin := sample.EthAddress() - tss := sample.Tss() - err = k.ProcessZetaSentEvent(ctx, event, emittingContract, txOrigin.Hex(), tss) + err = k.ProcessZetaSentEvent(ctx, event, emittingContract, txOrigin.Hex()) require.ErrorContains(t, err, "ProcessZetaSentEvent: failed to burn coins from fungible") }) @@ -557,8 +548,8 @@ func TestKeeper_ProcessZetaSentEvent(t *testing.T) { require.NoError(t, err) emittingContract := sample.EthAddress() txOrigin := sample.EthAddress() - tss := sample.Tss() - err = k.ProcessZetaSentEvent(ctx, event, emittingContract, txOrigin.Hex(), tss) + + err = k.ProcessZetaSentEvent(ctx, event, emittingContract, txOrigin.Hex()) require.ErrorContains(t, err, "chain not supported") }) @@ -589,9 +580,9 @@ func TestKeeper_ProcessZetaSentEvent(t *testing.T) { require.NoError(t, err) emittingContract := sample.EthAddress() txOrigin := sample.EthAddress() - tss := sample.Tss() + ctx = ctx.WithChainID("test-21-1") - err = k.ProcessZetaSentEvent(ctx, event, emittingContract, txOrigin.Hex(), tss) + err = k.ProcessZetaSentEvent(ctx, event, emittingContract, txOrigin.Hex()) require.ErrorContains(t, err, "ProcessZetaSentEvent: failed to convert chainID") }) @@ -619,10 +610,9 @@ func TestKeeper_ProcessZetaSentEvent(t *testing.T) { require.NoError(t, err) emittingContract := sample.EthAddress() txOrigin := sample.EthAddress() - tss := sample.Tss() - err = k.ProcessZetaSentEvent(ctx, event, emittingContract, txOrigin.Hex(), tss) - require.ErrorContains(t, err, "ProcessWithdrawalEvent: pay gas failed") + err = k.ProcessZetaSentEvent(ctx, event, emittingContract, txOrigin.Hex()) + require.ErrorContains(t, err, "gas coin contract invalid address") }) t.Run("unable to process ZetaSentEvent if process cctx fails", func(t *testing.T) { @@ -658,9 +648,9 @@ func TestKeeper_ProcessZetaSentEvent(t *testing.T) { require.NoError(t, err) emittingContract := sample.EthAddress() txOrigin := sample.EthAddress() - tss := sample.Tss() - err = k.ProcessZetaSentEvent(ctx, event, emittingContract, txOrigin.Hex(), tss) - require.ErrorContains(t, err, "ProcessWithdrawalEvent: update nonce failed") + + err = k.ProcessZetaSentEvent(ctx, event, emittingContract, txOrigin.Hex()) + require.ErrorContains(t, err, "nonce mismatch") }) } diff --git a/x/crosschain/keeper/initiate_outbound.go b/x/crosschain/keeper/initiate_outbound.go index dc3fcf4c24..954db18778 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 (https://github.com/zeta-chain/node/issues/2345): this is just a tmp solution because some flows require gas payment and others don't. +// TBD during implementation of issue above if info can be passed to CCTX constructor somehow. +// and not initialize CCTX using MsgVoteInbound and instead use something like (InboundParams, OutboundParams). +// Also check if msg.Digest can be replaced to calculate index +type InitiateOutboundConfig struct { + CCTX *types.CrossChainTx + ShouldPayGas 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, k) + if !found { + return config.CCTX.CctxStatus.Status, cosmoserrors.Wrap( types.ErrInitiatitingOutbound, fmt.Sprintf( "CCTXGateway not defined for receiver chain %d", receiverChainID, @@ -35,6 +44,6 @@ func (k Keeper) InitiateOutbound(ctx sdk.Context, cctx *types.CrossChainTx) (typ ) } - cctx.SetPendingOutbound("") - return cctxGateway.InitiateOutbound(ctx, cctx), nil + config.CCTX.SetPendingOutbound("") + return cctxGateway.InitiateOutbound(ctx, config) } diff --git a/x/crosschain/keeper/initiate_outbound_test.go b/x/crosschain/keeper/initiate_outbound_test.go index 25032e4a09..78175a76a8 100644 --- a/x/crosschain/keeper/initiate_outbound_test.go +++ b/x/crosschain/keeper/initiate_outbound_test.go @@ -43,7 +43,7 @@ func TestKeeper_InitiateOutboundZEVMDeposit(t *testing.T) { cctx.GetInboundParams().Amount = sdkmath.NewUintFromBigInt(amount) cctx.InboundParams.CoinType = coin.CoinType_Zeta cctx.GetInboundParams().SenderChainId = 0 - newStatus, err := k.InitiateOutbound(ctx, cctx) + newStatus, err := k.InitiateOutbound(ctx, keeper.InitiateOutboundConfig{CCTX: cctx, ShouldPayGas: true}) require.NoError(t, err) require.Equal(t, types.CctxStatus_OutboundMined, cctx.CctxStatus.Status) require.Equal(t, types.CctxStatus_OutboundMined, newStatus) @@ -72,8 +72,8 @@ func TestKeeper_InitiateOutboundZEVMDeposit(t *testing.T) { 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) + newStatus, err := k.InitiateOutbound(ctx, keeper.InitiateOutboundConfig{CCTX: cctx, ShouldPayGas: true}) + require.ErrorContains(t, err, "deposit error") require.Equal(t, types.CctxStatus_Aborted, cctx.CctxStatus.Status) require.Equal(t, types.CctxStatus_Aborted, newStatus) require.Equal(t, "deposit error", cctx.CctxStatus.StatusMessage) @@ -105,7 +105,7 @@ func TestKeeper_InitiateOutboundZEVMDeposit(t *testing.T) { // call InitiateOutbound cctx := GetERC20Cctx(t, receiver, *senderChain, "", amount) cctx.GetCurrentOutboundParam().ReceiverChainId = chains.ZetaChainPrivnet.ChainId - newStatus, err := k.InitiateOutbound(ctx, cctx) + newStatus, err := k.InitiateOutbound(ctx, keeper.InitiateOutboundConfig{CCTX: cctx, ShouldPayGas: true}) require.NoError(t, err) require.Equal(t, types.CctxStatus_Aborted, cctx.CctxStatus.Status) require.Equal(t, types.CctxStatus_Aborted, newStatus) @@ -145,7 +145,7 @@ func TestKeeper_InitiateOutboundZEVMDeposit(t *testing.T) { // call InitiateOutbound cctx := GetERC20Cctx(t, receiver, *senderChain, asset, amount) cctx.GetCurrentOutboundParam().ReceiverChainId = chains.ZetaChainPrivnet.ChainId - newStatus, err := k.InitiateOutbound(ctx, cctx) + newStatus, err := k.InitiateOutbound(ctx, keeper.InitiateOutboundConfig{CCTX: cctx, ShouldPayGas: true}) require.NoError(t, err) require.Equal(t, types.CctxStatus_Aborted, cctx.CctxStatus.Status) require.Equal(t, types.CctxStatus_Aborted, newStatus) @@ -188,7 +188,7 @@ func TestKeeper_InitiateOutboundZEVMDeposit(t *testing.T) { // call InitiateOutbound cctx := GetERC20Cctx(t, receiver, *senderChain, asset, amount) cctx.GetCurrentOutboundParam().ReceiverChainId = chains.ZetaChainPrivnet.ChainId - newStatus, err := k.InitiateOutbound(ctx, cctx) + newStatus, err := k.InitiateOutbound(ctx, keeper.InitiateOutboundConfig{CCTX: cctx, ShouldPayGas: true}) require.NoError(t, err) require.Equal(t, types.CctxStatus_Aborted, cctx.CctxStatus.Status) require.Equal(t, types.CctxStatus_Aborted, newStatus) @@ -233,7 +233,7 @@ func TestKeeper_InitiateOutboundZEVMDeposit(t *testing.T) { // call InitiateOutbound cctx := GetERC20Cctx(t, receiver, *senderChain, asset, amount) cctx.GetCurrentOutboundParam().ReceiverChainId = chains.ZetaChainPrivnet.ChainId - newStatus, err := k.InitiateOutbound(ctx, cctx) + newStatus, err := k.InitiateOutbound(ctx, keeper.InitiateOutboundConfig{CCTX: cctx, ShouldPayGas: true}) require.NoError(t, err) require.Equal(t, types.CctxStatus_Aborted, cctx.CctxStatus.Status) require.Equal(t, types.CctxStatus_Aborted, newStatus) @@ -280,7 +280,7 @@ func TestKeeper_InitiateOutboundZEVMDeposit(t *testing.T) { // call InitiateOutbound cctx := GetERC20Cctx(t, receiver, *senderChain, asset, amount) cctx.GetCurrentOutboundParam().ReceiverChainId = chains.ZetaChainPrivnet.ChainId - newStatus, err := k.InitiateOutbound(ctx, cctx) + newStatus, err := k.InitiateOutbound(ctx, keeper.InitiateOutboundConfig{CCTX: cctx, ShouldPayGas: true}) require.NoError(t, err) require.Equal(t, types.CctxStatus_Aborted, cctx.CctxStatus.Status) require.Equal(t, types.CctxStatus_Aborted, newStatus) @@ -320,7 +320,7 @@ func TestKeeper_InitiateOutboundZEVMDeposit(t *testing.T) { // call InitiateOutbound cctx := GetERC20Cctx(t, receiver, *senderChain, asset, amount) cctx.GetCurrentOutboundParam().ReceiverChainId = chains.ZetaChainPrivnet.ChainId - newStatus, err := k.InitiateOutbound(ctx, cctx) + newStatus, err := k.InitiateOutbound(ctx, keeper.InitiateOutboundConfig{CCTX: cctx, ShouldPayGas: true}) require.NoError(t, err) require.Equal(t, types.CctxStatus_PendingRevert, cctx.CctxStatus.Status) require.Equal(t, types.CctxStatus_PendingRevert, newStatus) @@ -358,7 +358,7 @@ func TestKeeper_InitiateOutboundZEVMDeposit(t *testing.T) { 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) + newStatus, err := k.InitiateOutbound(ctx, keeper.InitiateOutboundConfig{CCTX: cctx, ShouldPayGas: true}) require.NoError(t, err) require.Equal(t, types.CctxStatus_Aborted, cctx.CctxStatus.Status) require.Equal(t, types.CctxStatus_Aborted, newStatus) @@ -393,7 +393,7 @@ func TestKeeper_InitiateOutboundProcessCrosschainMsgPassing(t *testing.T) { // call InitiateOutbound cctx := GetERC20Cctx(t, receiver, *receiverChain, "", amount) - newStatus, err := k.InitiateOutbound(ctx, cctx) + newStatus, err := k.InitiateOutbound(ctx, keeper.InitiateOutboundConfig{CCTX: cctx, ShouldPayGas: true}) require.NoError(t, err) require.Equal(t, types.CctxStatus_PendingOutbound, cctx.CctxStatus.Status) require.Equal(t, types.CctxStatus_PendingOutbound, newStatus) @@ -417,8 +417,8 @@ func TestKeeper_InitiateOutboundProcessCrosschainMsgPassing(t *testing.T) { // call InitiateOutbound cctx := GetERC20Cctx(t, receiver, *receiverChain, "", amount) - newStatus, err := k.InitiateOutbound(ctx, cctx) - require.NoError(t, err) + newStatus, err := k.InitiateOutbound(ctx, keeper.InitiateOutboundConfig{CCTX: cctx, ShouldPayGas: true}) + require.ErrorIs(t, err, observertypes.ErrSupportedChains) 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) @@ -446,8 +446,8 @@ func TestKeeper_InitiateOutboundProcessCrosschainMsgPassing(t *testing.T) { // call InitiateOutbound cctx := GetERC20Cctx(t, receiver, *receiverChain, "", amount) - newStatus, err := k.InitiateOutbound(ctx, cctx) - require.NoError(t, err) + newStatus, err := k.InitiateOutbound(ctx, keeper.InitiateOutboundConfig{CCTX: cctx, ShouldPayGas: true}) + require.ErrorContains(t, err, "cannot find receiver chain nonce") 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") @@ -468,31 +468,9 @@ func TestKeeper_InitiateOutboundFailures(t *testing.T) { receiverChain.ChainId = 123 // call InitiateOutbound cctx := GetERC20Cctx(t, receiver, *receiverChain, "", amount) - newStatus, err := k.InitiateOutbound(ctx, cctx) + newStatus, err := k.InitiateOutbound(ctx, keeper.InitiateOutboundConfig{CCTX: cctx, ShouldPayGas: true}) 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..fc483689b3 100644 --- a/x/crosschain/keeper/keeper.go +++ b/x/crosschain/keeper/keeper.go @@ -8,24 +8,14 @@ import ( storetypes "github.com/cosmos/cosmos-sdk/store/types" 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, cctx *types.CrossChainTx) (newCCTXStatus types.CctxStatus) -} - type ( Keeper struct { - cdc codec.Codec - storeKey storetypes.StoreKey - memKey storetypes.StoreKey - cctxGateways map[chains.CCTXGateway]CCTXGateway + cdc codec.Codec + storeKey storetypes.StoreKey + memKey storetypes.StoreKey stakingKeeper types.StakingKeeper authKeeper types.AccountKeeper @@ -110,10 +100,6 @@ func (k *Keeper) SetIBCCrosschainKeeper(ibcCrosschainKeeper types.IBCCrosschainK k.ibcCrosschainKeeper = ibcCrosschainKeeper } -func (k *Keeper) SetCCTXGateways(cctxGateways map[chains.CCTXGateway]CCTXGateway) { - k.cctxGateways = cctxGateways -} - func (k Keeper) GetStoreKey() storetypes.StoreKey { return k.storeKey } diff --git a/x/crosschain/keeper/msg_server_migrate_tss_funds.go b/x/crosschain/keeper/msg_server_migrate_tss_funds.go index 1bff3b8443..05f218162f 100644 --- a/x/crosschain/keeper/msg_server_migrate_tss_funds.go +++ b/x/crosschain/keeper/msg_server_migrate_tss_funds.go @@ -200,7 +200,7 @@ func (k Keeper) MigrateTSSFundsForChain( return errorsmod.Wrap(types.ErrReceiverIsEmpty, fmt.Sprintf("chain %d is not supported", chainID)) } - err := k.UpdateNonce(ctx, chainID, &cctx) + err := k.SetObserverOutboundInfo(ctx, chainID, &cctx) if err != nil { return err } diff --git a/x/crosschain/keeper/msg_server_vote_inbound_tx.go b/x/crosschain/keeper/msg_server_vote_inbound_tx.go index b118d2bc43..ecf87ef9fd 100644 --- a/x/crosschain/keeper/msg_server_vote_inbound_tx.go +++ b/x/crosschain/keeper/msg_server_vote_inbound_tx.go @@ -97,27 +97,18 @@ func (k msgServer) VoteInbound( if !finalized { return &types.MsgVoteInboundResponse{}, nil } - tss, tssFound := k.zetaObserverKeeper.GetTSS(ctx) - if !tssFound { - return nil, types.ErrCannotFindTSSKeys - } - // create a new CCTX from the inbound message.The status of the new CCTX is set to PendingInbound. - cctx, err := types.NewCCTX(ctx, *msg, tss.TssPubkey) - if err != nil { - return nil, err - } - // 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) + + cctx, err := k.ValidateInbound(ctx, msg, true) if err != nil { return nil, err } + // Save the inbound CCTX to the store. This is called irrespective of the status of the CCTX or the outcome of the process function. - k.SaveInbound(ctx, &cctx, msg.EventIndex) + k.SaveObservedInboundInformation(ctx, cctx, msg.EventIndex) return &types.MsgVoteInboundResponse{}, nil } -/* SaveInbound saves the inbound CCTX to the store.It does the following: +/* SaveObservedInboundInformation saves the inbound CCTX to the store.It does the following: - Emits an event for the finalized inbound CCTX. - Adds the inbound CCTX to the finalized inbound CCTX store.This is done to prevent double spending, using the same inbound tx hash and event index. - Updates the CCTX with the finalized height and finalization status. @@ -125,7 +116,7 @@ func (k msgServer) VoteInbound( - Sets the CCTX and nonce to the CCTX and inbound transaction hash to CCTX store. */ -func (k Keeper) SaveInbound(ctx sdk.Context, cctx *types.CrossChainTx, eventIndex uint64) { +func (k Keeper) SaveObservedInboundInformation(ctx sdk.Context, cctx *types.CrossChainTx, eventIndex uint64) { EmitEventInboundFinalized(ctx, cctx) k.AddFinalizedInbound(ctx, cctx.GetInboundParams().ObservedHash, @@ -135,5 +126,5 @@ func (k Keeper) SaveInbound(ctx sdk.Context, cctx *types.CrossChainTx, eventInde cctx.InboundParams.FinalizedZetaHeight = uint64(ctx.BlockHeight()) cctx.InboundParams.TxFinalizationStatus = types.TxFinalizationStatus_Executed k.RemoveInboundTrackerIfExists(ctx, cctx.InboundParams.SenderChainId, cctx.InboundParams.ObservedHash) - k.SetCctxAndNonceToCctxAndInboundHashToCctx(ctx, *cctx) + k.SetCrossChainTx(ctx, *cctx) } diff --git a/x/crosschain/keeper/msg_server_vote_inbound_tx_test.go b/x/crosschain/keeper/msg_server_vote_inbound_tx_test.go index 332a47f270..64a2153611 100644 --- a/x/crosschain/keeper/msg_server_vote_inbound_tx_test.go +++ b/x/crosschain/keeper/msg_server_vote_inbound_tx_test.go @@ -300,7 +300,7 @@ func TestStatus_ChangeStatus(t *testing.T) { } } -func TestKeeper_SaveInbound(t *testing.T) { +func TestKeeper_SaveObservedInboundInformation(t *testing.T) { t.Run("should save the cctx", func(t *testing.T) { k, ctx, _, zk := keepertest.CrosschainKeeper(t) zk.ObserverKeeper.SetTSS(ctx, sample.Tss()) @@ -309,7 +309,7 @@ func TestKeeper_SaveInbound(t *testing.T) { senderChain := getValidEthChain() cctx := GetERC20Cctx(t, receiver, *senderChain, "", amount) eventIndex := sample.Uint64InRange(1, 100) - k.SaveInbound(ctx, cctx, eventIndex) + k.SaveObservedInboundInformation(ctx, cctx, eventIndex) require.Equal(t, types.TxFinalizationStatus_Executed, cctx.InboundParams.TxFinalizationStatus) require.True( t, @@ -340,7 +340,7 @@ func TestKeeper_SaveInbound(t *testing.T) { eventIndex := sample.Uint64InRange(1, 100) zk.ObserverKeeper.SetTSS(ctx, sample.Tss()) - k.SaveInbound(ctx, cctx, eventIndex) + k.SaveObservedInboundInformation(ctx, cctx, eventIndex) require.Equal(t, types.TxFinalizationStatus_Executed, cctx.InboundParams.TxFinalizationStatus) require.True( t, diff --git a/x/crosschain/keeper/msg_server_whitelist_erc20.go b/x/crosschain/keeper/msg_server_whitelist_erc20.go index c8fbd1248b..04ceb4a1a4 100644 --- a/x/crosschain/keeper/msg_server_whitelist_erc20.go +++ b/x/crosschain/keeper/msg_server_whitelist_erc20.go @@ -162,7 +162,7 @@ func (k msgServer) WhitelistERC20( }, }, } - err = k.UpdateNonce(ctx, msg.ChainId, &cctx) + err = k.SetObserverOutboundInfo(ctx, msg.ChainId, &cctx) if err != nil { return nil, err } diff --git a/x/crosschain/types/cctx.go b/x/crosschain/types/cctx.go index f99fbd6cbf..a4080cc968 100644 --- a/x/crosschain/types/cctx.go +++ b/x/crosschain/types/cctx.go @@ -168,7 +168,7 @@ func GetCctxIndexFromBytes(sendHash [32]byte) string { return fmt.Sprintf("0x%s", hex.EncodeToString(sendHash[:])) } -// NewCCTX creates a new CCTX.From a MsgVoteInbound message and a TSS pubkey. +// NewCCTX creates a new CCTX from a MsgVoteInbound message and a TSS pubkey. // It also validates the created cctx func NewCCTX(ctx sdk.Context, msg MsgVoteInbound, tssPubkey string) (CrossChainTx, error) { index := msg.Digest()