From 8a3de0e2225362b5818cca6378c4af5ee1f9049b Mon Sep 17 00:00:00 2001 From: Tanmay Date: Tue, 23 Jan 2024 01:02:03 -0500 Subject: [PATCH 1/6] add evm amount calculation for tss migration --- common/gas_limits.go | 7 + .../keeper/msg_server_migrate_tss_funds.go | 18 ++ .../msg_server_migrate_tss_funds_test.go | 30 +++- x/crosschain/types/errors.go | 2 + x/crosschain/types/keys.go | 2 + zetaclient/evm_signer.go | 154 +++++++++--------- 6 files changed, 130 insertions(+), 83 deletions(-) create mode 100644 common/gas_limits.go diff --git a/common/gas_limits.go b/common/gas_limits.go new file mode 100644 index 0000000000..167537d5e5 --- /dev/null +++ b/common/gas_limits.go @@ -0,0 +1,7 @@ +package common + +const ( + EVMSend = 21000 + // Todo Move gas limits from zeta-client to this file + // https://github.com/zeta-chain/node/issues/1606 +) diff --git a/x/crosschain/keeper/msg_server_migrate_tss_funds.go b/x/crosschain/keeper/msg_server_migrate_tss_funds.go index 081e2d6490..7fb8293a0c 100644 --- a/x/crosschain/keeper/msg_server_migrate_tss_funds.go +++ b/x/crosschain/keeper/msg_server_migrate_tss_funds.go @@ -116,6 +116,24 @@ func (k Keeper) MigrateTSSFundsForChain(ctx sdk.Context, chainID int64, amount s } cctx.InboundTxParams.Sender = ethAddressOld.String() cctx.GetCurrentOutTxParam().Receiver = ethAddressNew.String() + // Tss migration is a send transaction, so the gas limit is set to 21000 + cctx.GetCurrentOutTxParam().OutboundTxGasLimit = common.EVMSend + // Multiple current gas price with standard multiplier to add some buffer + multiplier, err := sdk.NewDecFromStr(types.TssMigrationGasMultiplierEVM) + if err != nil { + return err + } + gasPrice, err := sdk.NewDecFromStr(medianGasPrice.String()) + if err != nil { + return err + } + newGasPrice := gasPrice.Mul(multiplier) + cctx.GetCurrentOutTxParam().OutboundTxGasPrice = newGasPrice.TruncateInt().String() + evmFee := sdkmath.NewUint(cctx.GetCurrentOutTxParam().OutboundTxGasLimit).Mul(medianGasPrice) + if evmFee.GT(amount) { + return errorsmod.Wrap(types.ErrInsufficientFundsTssMigration, fmt.Sprintf("insufficient funds to pay for gas fee, amount: %s, gas fee: %s, chainid: %d", amount.String(), evmFee.String(), chainID)) + } + cctx.GetCurrentOutTxParam().Amount = amount.Sub(evmFee) } // Set the sender and receiver addresses for Bitcoin chain if common.IsBitcoinChain(chainID) { diff --git a/x/crosschain/keeper/msg_server_migrate_tss_funds_test.go b/x/crosschain/keeper/msg_server_migrate_tss_funds_test.go index 436a6b69c9..ff396515e9 100644 --- a/x/crosschain/keeper/msg_server_migrate_tss_funds_test.go +++ b/x/crosschain/keeper/msg_server_migrate_tss_funds_test.go @@ -22,7 +22,7 @@ func TestMsgServer_MigrateTssFunds(t *testing.T) { setAdminPolicies(ctx, zk, admin) msgServer := keeper.NewMsgServerImpl(*k) chain := getValidEthChain(t) - amount := sdkmath.NewUint(100) + amount := sdkmath.NewUintFromString("10000000000000000000") indexString, _ := setupTssMigrationParams(zk, k, ctx, *chain, amount, true, true) _, err := msgServer.MigrateTssFunds(ctx, &crosschaintypes.MsgMigrateTssFunds{ Creator: admin, @@ -35,20 +35,38 @@ func TestMsgServer_MigrateTssFunds(t *testing.T) { _, found := k.GetCrossChainTx(ctx, index) assert.True(t, found) }) + t.Run("not enough funds in tss address for migration", func(t *testing.T) { + k, ctx, _, zk := keepertest.CrosschainKeeper(t) + admin := sample.AccAddress() + setAdminPolicies(ctx, zk, admin) + msgServer := keeper.NewMsgServerImpl(*k) + chain := getValidEthChain(t) + amount := sdkmath.NewUintFromString("100") + indexString, _ := setupTssMigrationParams(zk, k, ctx, *chain, amount, true, true) + _, err := msgServer.MigrateTssFunds(ctx, &crosschaintypes.MsgMigrateTssFunds{ + Creator: admin, + ChainId: chain.ChainId, + Amount: amount, + }) + assert.ErrorContains(t, err, crosschaintypes.ErrCannotMigrateTssFunds.Error()) + hash := crypto.Keccak256Hash([]byte(indexString)) + index := hash.Hex() + _, found := k.GetCrossChainTx(ctx, index) + assert.False(t, found) + }) t.Run("unable to migrate funds if new TSS is not created ", func(t *testing.T) { k, ctx, _, zk := keepertest.CrosschainKeeper(t) admin := sample.AccAddress() setAdminPolicies(ctx, zk, admin) msgServer := keeper.NewMsgServerImpl(*k) chain := getValidEthChain(t) - amount := sdkmath.NewUint(100) + amount := sdkmath.NewUintFromString("10000000000000000000") indexString, _ := setupTssMigrationParams(zk, k, ctx, *chain, amount, false, true) _, err := msgServer.MigrateTssFunds(ctx, &crosschaintypes.MsgMigrateTssFunds{ Creator: admin, ChainId: chain.ChainId, Amount: amount, }) - assert.ErrorIs(t, err, crosschaintypes.ErrCannotMigrateTssFunds) assert.ErrorContains(t, err, "no new tss address has been generated") hash := crypto.Keccak256Hash([]byte(indexString)) index := hash.Hex() @@ -61,7 +79,7 @@ func TestMsgServer_MigrateTssFunds(t *testing.T) { setAdminPolicies(ctx, zk, admin) msgServer := keeper.NewMsgServerImpl(*k) chain := getValidEthChain(t) - amount := sdkmath.NewUint(100) + amount := sdkmath.NewUintFromString("10000000000000000000") indexString, tssPubkey := setupTssMigrationParams(zk, k, ctx, *chain, amount, true, true) k.GetObserverKeeper().SetPendingNonces(ctx, observertypes.PendingNonces{ NonceLow: 1, @@ -87,7 +105,7 @@ func TestMsgServer_MigrateTssFunds(t *testing.T) { setAdminPolicies(ctx, zk, admin) msgServer := keeper.NewMsgServerImpl(*k) chain := getValidEthChain(t) - amount := sdkmath.NewUint(100) + amount := sdkmath.NewUintFromString("10000000000000000000") indexString, tssPubkey := setupTssMigrationParams(zk, k, ctx, *chain, amount, true, true) k.GetObserverKeeper().SetPendingNonces(ctx, observertypes.PendingNonces{ NonceLow: 1, @@ -123,7 +141,7 @@ func TestMsgServer_MigrateTssFunds(t *testing.T) { setAdminPolicies(ctx, zk, admin) msgServer := keeper.NewMsgServerImpl(*k) chain := getValidEthChain(t) - amount := sdkmath.NewUint(100) + amount := sdkmath.NewUintFromString("10000000000000000000") indexString, _ := setupTssMigrationParams(zk, k, ctx, *chain, amount, false, false) currentTss, found := k.GetObserverKeeper().GetTSS(ctx) assert.True(t, found) diff --git a/x/crosschain/types/errors.go b/x/crosschain/types/errors.go index 8dc589e878..12bbd4ad84 100644 --- a/x/crosschain/types/errors.go +++ b/x/crosschain/types/errors.go @@ -43,4 +43,6 @@ var ( ErrReceiverIsEmpty = errorsmod.Register(ModuleName, 1142, "receiver is empty") ErrUnsupportedStatus = errorsmod.Register(ModuleName, 1143, "unsupported status") ErrObservedTxAlreadyFinalized = errorsmod.Register(ModuleName, 1144, "observed tx already finalized") + + ErrInsufficientFundsTssMigration = errorsmod.Register(ModuleName, 1145, "insufficient funds for TSS migration") ) diff --git a/x/crosschain/types/keys.go b/x/crosschain/types/keys.go index 8777d8df0c..dbec6d5c9f 100644 --- a/x/crosschain/types/keys.go +++ b/x/crosschain/types/keys.go @@ -25,6 +25,8 @@ const ( MemStoreKey = "mem_metacore" ProtocolFee = 2000000000000000000 + + TssMigrationGasMultiplierEVM = "2.5" ) func GetProtocolFee() sdk.Uint { diff --git a/zetaclient/evm_signer.go b/zetaclient/evm_signer.go index c961a627e0..70f8816246 100644 --- a/zetaclient/evm_signer.go +++ b/zetaclient/evm_signer.go @@ -294,7 +294,7 @@ func (signer *EVMSigner) SignCommandTx( return tx, nil } if cmd == common.CmdMigrateTssFunds { - tx := ethtypes.NewTransaction(outboundParams.OutboundTxTssNonce, to, outboundParams.Amount.BigInt(), 21000, gasPrice, nil) + tx := ethtypes.NewTransaction(outboundParams.OutboundTxTssNonce, to, outboundParams.Amount.BigInt(), outboundParams.OutboundTxGasLimit, gasPrice, nil) hashBytes := signer.ethSigner.Hash(tx).Bytes() sig, err := signer.tssSigner.Sign(hashBytes, height, outboundParams.OutboundTxTssNonce, signer.chain, "") if err != nil { @@ -318,7 +318,7 @@ func (signer *EVMSigner) SignCommandTx( } func (signer *EVMSigner) TryProcessOutTx( - send *types.CrossChainTx, + crossChainTx *types.CrossChainTx, outTxMan *OutTxProcessorManager, outTxID string, chainclient ChainClient, @@ -327,10 +327,10 @@ func (signer *EVMSigner) TryProcessOutTx( ) { logger := signer.logger.With(). Str("outTxID", outTxID). - Str("SendHash", send.Index). + Str("SendHash", crossChainTx.Index). Logger() logger.Info().Msgf("start processing outTxID %s", outTxID) - logger.Info().Msgf("EVM Chain TryProcessOutTx: %s, value %d to %s", send.Index, send.GetCurrentOutTxParam().Amount.BigInt(), send.GetCurrentOutTxParam().Receiver) + logger.Info().Msgf("EVM Chain TryProcessOutTx: %s, value %d to %s", crossChainTx.Index, crossChainTx.GetCurrentOutTxParam().Amount.BigInt(), crossChainTx.GetCurrentOutTxParam().Receiver) defer func() { outTxMan.EndTryProcess(outTxID) @@ -339,23 +339,23 @@ func (signer *EVMSigner) TryProcessOutTx( var to ethcommon.Address var toChain *common.Chain - if send.CctxStatus.Status == types.CctxStatus_PendingRevert { - to = ethcommon.HexToAddress(send.InboundTxParams.Sender) - toChain = common.GetChainFromChainID(send.InboundTxParams.SenderChainId) + if crossChainTx.CctxStatus.Status == types.CctxStatus_PendingRevert { + to = ethcommon.HexToAddress(crossChainTx.InboundTxParams.Sender) + toChain = common.GetChainFromChainID(crossChainTx.InboundTxParams.SenderChainId) if toChain == nil { - logger.Error().Msgf("Unknown chain: %d", send.InboundTxParams.SenderChainId) + logger.Error().Msgf("Unknown chain: %d", crossChainTx.InboundTxParams.SenderChainId) return } logger.Info().Msgf("Abort: reverting inbound") - } else if send.CctxStatus.Status == types.CctxStatus_PendingOutbound { - to = ethcommon.HexToAddress(send.GetCurrentOutTxParam().Receiver) - toChain = common.GetChainFromChainID(send.GetCurrentOutTxParam().ReceiverChainId) + } else if crossChainTx.CctxStatus.Status == types.CctxStatus_PendingOutbound { + to = ethcommon.HexToAddress(crossChainTx.GetCurrentOutTxParam().Receiver) + toChain = common.GetChainFromChainID(crossChainTx.GetCurrentOutTxParam().ReceiverChainId) if toChain == nil { - logger.Error().Msgf("Unknown chain: %d", send.GetCurrentOutTxParam().ReceiverChainId) + logger.Error().Msgf("Unknown chain: %d", crossChainTx.GetCurrentOutTxParam().ReceiverChainId) return } } else { - logger.Info().Msgf("Transaction doesn't need to be processed status: %d", send.CctxStatus.Status) + logger.Info().Msgf("Transaction doesn't need to be processed status: %d", crossChainTx.CctxStatus.Status) return } evmClient, ok := chainclient.(*EVMChainClient) @@ -365,8 +365,8 @@ func (signer *EVMSigner) TryProcessOutTx( } // Early return if the cctx is already processed - nonce := send.GetCurrentOutTxParam().OutboundTxTssNonce - included, confirmed, err := evmClient.IsSendOutTxProcessed(send.Index, nonce, send.GetCurrentOutTxParam().CoinType, logger) + nonce := crossChainTx.GetCurrentOutTxParam().OutboundTxTssNonce + included, confirmed, err := evmClient.IsSendOutTxProcessed(crossChainTx.Index, nonce, crossChainTx.GetCurrentOutTxParam().CoinType, logger) if err != nil { logger.Error().Err(err).Msg("IsSendOutTxProcessed failed") } @@ -376,27 +376,27 @@ func (signer *EVMSigner) TryProcessOutTx( } var message []byte - if send.GetCurrentOutTxParam().CoinType != common.CoinType_Cmd { - message, err = base64.StdEncoding.DecodeString(send.RelayedMessage) + if crossChainTx.GetCurrentOutTxParam().CoinType != common.CoinType_Cmd { + message, err = base64.StdEncoding.DecodeString(crossChainTx.RelayedMessage) if err != nil { - logger.Err(err).Msgf("decode CCTX.Message %s error", send.RelayedMessage) + logger.Err(err).Msgf("decode CCTX.Message %s error", crossChainTx.RelayedMessage) } } - gasLimit := send.GetCurrentOutTxParam().OutboundTxGasLimit + gasLimit := crossChainTx.GetCurrentOutTxParam().OutboundTxGasLimit if gasLimit < 100_000 { gasLimit = 100_000 - logger.Warn().Msgf("gasLimit %d is too low; set to %d", send.GetCurrentOutTxParam().OutboundTxGasLimit, gasLimit) + logger.Warn().Msgf("gasLimit %d is too low; set to %d", crossChainTx.GetCurrentOutTxParam().OutboundTxGasLimit, gasLimit) } if gasLimit > 1_000_000 { gasLimit = 1_000_000 - logger.Warn().Msgf("gasLimit %d is too high; set to %d", send.GetCurrentOutTxParam().OutboundTxGasLimit, gasLimit) + logger.Warn().Msgf("gasLimit %d is too high; set to %d", crossChainTx.GetCurrentOutTxParam().OutboundTxGasLimit, gasLimit) } - logger.Info().Msgf("chain %s minting %d to %s, nonce %d, finalized zeta bn %d", toChain, send.InboundTxParams.Amount, to.Hex(), nonce, send.InboundTxParams.InboundTxFinalizedZetaHeight) - sendHash, err := hex.DecodeString(send.Index[2:]) // remove the leading 0x + logger.Info().Msgf("chain %s minting %d to %s, nonce %d, finalized zeta bn %d", toChain, crossChainTx.InboundTxParams.Amount, to.Hex(), nonce, crossChainTx.InboundTxParams.InboundTxFinalizedZetaHeight) + sendHash, err := hex.DecodeString(crossChainTx.Index[2:]) // remove the leading 0x if err != nil || len(sendHash) != 32 { - logger.Error().Err(err).Msgf("decode CCTX %s error", send.Index) + logger.Error().Err(err).Msgf("decode CCTX %s error", crossChainTx.Index) return } var sendhash [32]byte @@ -408,7 +408,7 @@ func (signer *EVMSigner) TryProcessOutTx( // The code below is a fix for https://github.com/zeta-chain/node/issues/1085 // doesn't close directly the issue because we should determine if we want to keep using SuggestGasPrice if no OutboundTxGasPrice // we should possibly remove it completely and return an error if no OutboundTxGasPrice is provided because it means no fee is processed on ZetaChain - specified, ok := new(big.Int).SetString(send.GetCurrentOutTxParam().OutboundTxGasPrice, 10) + specified, ok := new(big.Int).SetString(crossChainTx.GetCurrentOutTxParam().OutboundTxGasPrice, 10) if !ok { if common.IsEthereumChain(toChain.ChainId) { suggested, err := signer.client.SuggestGasPrice(context.Background()) @@ -418,7 +418,7 @@ func (signer *EVMSigner) TryProcessOutTx( } gasprice = roundUpToNearestGwei(suggested) } else { - logger.Error().Err(err).Msgf("cannot convert gas price %s ", send.GetCurrentOutTxParam().OutboundTxGasPrice) + logger.Error().Err(err).Msgf("cannot convert gas price %s ", crossChainTx.GetCurrentOutTxParam().OutboundTxGasPrice) return } } else { @@ -444,138 +444,138 @@ func (signer *EVMSigner) TryProcessOutTx( var tx *ethtypes.Transaction - if send.GetCurrentOutTxParam().CoinType == common.CoinType_Cmd { // admin command - to := ethcommon.HexToAddress(send.GetCurrentOutTxParam().Receiver) + if crossChainTx.GetCurrentOutTxParam().CoinType == common.CoinType_Cmd { // admin command + to := ethcommon.HexToAddress(crossChainTx.GetCurrentOutTxParam().Receiver) if to == (ethcommon.Address{}) { - logger.Error().Msgf("invalid receiver %s", send.GetCurrentOutTxParam().Receiver) + logger.Error().Msgf("invalid receiver %s", crossChainTx.GetCurrentOutTxParam().Receiver) return } - msg := strings.Split(send.RelayedMessage, ":") + msg := strings.Split(crossChainTx.RelayedMessage, ":") if len(msg) != 2 { logger.Error().Msgf("invalid message %s", msg) return } - tx, err = signer.SignCommandTx(msg[0], msg[1], to, send.GetCurrentOutTxParam(), gasLimit, gasprice, height) - } else if send.InboundTxParams.SenderChainId == zetaBridge.ZetaChain().ChainId && send.CctxStatus.Status == types.CctxStatus_PendingOutbound && flags.IsOutboundEnabled { - if send.GetCurrentOutTxParam().CoinType == common.CoinType_Gas { - logger.Info().Msgf("SignWithdrawTx: %d => %s, nonce %d, gasprice %d", send.InboundTxParams.SenderChainId, toChain, send.GetCurrentOutTxParam().OutboundTxTssNonce, gasprice) + tx, err = signer.SignCommandTx(msg[0], msg[1], to, crossChainTx.GetCurrentOutTxParam(), gasLimit, gasprice, height) + } else if crossChainTx.InboundTxParams.SenderChainId == zetaBridge.ZetaChain().ChainId && crossChainTx.CctxStatus.Status == types.CctxStatus_PendingOutbound && flags.IsOutboundEnabled { + if crossChainTx.GetCurrentOutTxParam().CoinType == common.CoinType_Gas { + logger.Info().Msgf("SignWithdrawTx: %d => %s, nonce %d, gasprice %d", crossChainTx.InboundTxParams.SenderChainId, toChain, crossChainTx.GetCurrentOutTxParam().OutboundTxTssNonce, gasprice) tx, err = signer.SignWithdrawTx( to, - send.GetCurrentOutTxParam().Amount.BigInt(), - send.GetCurrentOutTxParam().OutboundTxTssNonce, + crossChainTx.GetCurrentOutTxParam().Amount.BigInt(), + crossChainTx.GetCurrentOutTxParam().OutboundTxTssNonce, gasprice, height, ) } - if send.GetCurrentOutTxParam().CoinType == common.CoinType_ERC20 { - asset := ethcommon.HexToAddress(send.InboundTxParams.Asset) - logger.Info().Msgf("SignERC20WithdrawTx: %d => %s, nonce %d, gasprice %d", send.InboundTxParams.SenderChainId, toChain, send.GetCurrentOutTxParam().OutboundTxTssNonce, gasprice) + if crossChainTx.GetCurrentOutTxParam().CoinType == common.CoinType_ERC20 { + asset := ethcommon.HexToAddress(crossChainTx.InboundTxParams.Asset) + logger.Info().Msgf("SignERC20WithdrawTx: %d => %s, nonce %d, gasprice %d", crossChainTx.InboundTxParams.SenderChainId, toChain, crossChainTx.GetCurrentOutTxParam().OutboundTxTssNonce, gasprice) tx, err = signer.SignERC20WithdrawTx( to, asset, - send.GetCurrentOutTxParam().Amount.BigInt(), + crossChainTx.GetCurrentOutTxParam().Amount.BigInt(), gasLimit, - send.GetCurrentOutTxParam().OutboundTxTssNonce, + crossChainTx.GetCurrentOutTxParam().OutboundTxTssNonce, gasprice, height, ) } - if send.GetCurrentOutTxParam().CoinType == common.CoinType_Zeta { - logger.Info().Msgf("SignOutboundTx: %d => %s, nonce %d, gasprice %d", send.InboundTxParams.SenderChainId, toChain, send.GetCurrentOutTxParam().OutboundTxTssNonce, gasprice) + if crossChainTx.GetCurrentOutTxParam().CoinType == common.CoinType_Zeta { + logger.Info().Msgf("SignOutboundTx: %d => %s, nonce %d, gasprice %d", crossChainTx.InboundTxParams.SenderChainId, toChain, crossChainTx.GetCurrentOutTxParam().OutboundTxTssNonce, gasprice) tx, err = signer.SignOutboundTx( - ethcommon.HexToAddress(send.InboundTxParams.Sender), - big.NewInt(send.InboundTxParams.SenderChainId), + ethcommon.HexToAddress(crossChainTx.InboundTxParams.Sender), + big.NewInt(crossChainTx.InboundTxParams.SenderChainId), to, - send.GetCurrentOutTxParam().Amount.BigInt(), + crossChainTx.GetCurrentOutTxParam().Amount.BigInt(), gasLimit, message, sendhash, - send.GetCurrentOutTxParam().OutboundTxTssNonce, + crossChainTx.GetCurrentOutTxParam().OutboundTxTssNonce, gasprice, height, ) } - } else if send.CctxStatus.Status == types.CctxStatus_PendingRevert && send.OutboundTxParams[0].ReceiverChainId == zetaBridge.ZetaChain().ChainId { - if send.GetCurrentOutTxParam().CoinType == common.CoinType_Gas { - logger.Info().Msgf("SignWithdrawTx: %d => %s, nonce %d, gasprice %d", send.InboundTxParams.SenderChainId, toChain, send.GetCurrentOutTxParam().OutboundTxTssNonce, gasprice) + } else if crossChainTx.CctxStatus.Status == types.CctxStatus_PendingRevert && crossChainTx.OutboundTxParams[0].ReceiverChainId == zetaBridge.ZetaChain().ChainId { + if crossChainTx.GetCurrentOutTxParam().CoinType == common.CoinType_Gas { + logger.Info().Msgf("SignWithdrawTx: %d => %s, nonce %d, gasprice %d", crossChainTx.InboundTxParams.SenderChainId, toChain, crossChainTx.GetCurrentOutTxParam().OutboundTxTssNonce, gasprice) tx, err = signer.SignWithdrawTx( to, - send.GetCurrentOutTxParam().Amount.BigInt(), - send.GetCurrentOutTxParam().OutboundTxTssNonce, + crossChainTx.GetCurrentOutTxParam().Amount.BigInt(), + crossChainTx.GetCurrentOutTxParam().OutboundTxTssNonce, gasprice, height, ) } - if send.GetCurrentOutTxParam().CoinType == common.CoinType_ERC20 { - asset := ethcommon.HexToAddress(send.InboundTxParams.Asset) - logger.Info().Msgf("SignERC20WithdrawTx: %d => %s, nonce %d, gasprice %d", send.InboundTxParams.SenderChainId, toChain, send.GetCurrentOutTxParam().OutboundTxTssNonce, gasprice) + if crossChainTx.GetCurrentOutTxParam().CoinType == common.CoinType_ERC20 { + asset := ethcommon.HexToAddress(crossChainTx.InboundTxParams.Asset) + logger.Info().Msgf("SignERC20WithdrawTx: %d => %s, nonce %d, gasprice %d", crossChainTx.InboundTxParams.SenderChainId, toChain, crossChainTx.GetCurrentOutTxParam().OutboundTxTssNonce, gasprice) tx, err = signer.SignERC20WithdrawTx( to, asset, - send.GetCurrentOutTxParam().Amount.BigInt(), + crossChainTx.GetCurrentOutTxParam().Amount.BigInt(), gasLimit, - send.GetCurrentOutTxParam().OutboundTxTssNonce, + crossChainTx.GetCurrentOutTxParam().OutboundTxTssNonce, gasprice, height, ) } - } else if send.CctxStatus.Status == types.CctxStatus_PendingRevert { - logger.Info().Msgf("SignRevertTx: %d => %s, nonce %d, gasprice %d", send.InboundTxParams.SenderChainId, toChain, send.GetCurrentOutTxParam().OutboundTxTssNonce, gasprice) + } else if crossChainTx.CctxStatus.Status == types.CctxStatus_PendingRevert { + logger.Info().Msgf("SignRevertTx: %d => %s, nonce %d, gasprice %d", crossChainTx.InboundTxParams.SenderChainId, toChain, crossChainTx.GetCurrentOutTxParam().OutboundTxTssNonce, gasprice) tx, err = signer.SignRevertTx( - ethcommon.HexToAddress(send.InboundTxParams.Sender), - big.NewInt(send.OutboundTxParams[0].ReceiverChainId), + ethcommon.HexToAddress(crossChainTx.InboundTxParams.Sender), + big.NewInt(crossChainTx.OutboundTxParams[0].ReceiverChainId), to.Bytes(), - big.NewInt(send.GetCurrentOutTxParam().ReceiverChainId), - send.GetCurrentOutTxParam().Amount.BigInt(), + big.NewInt(crossChainTx.GetCurrentOutTxParam().ReceiverChainId), + crossChainTx.GetCurrentOutTxParam().Amount.BigInt(), gasLimit, message, sendhash, - send.GetCurrentOutTxParam().OutboundTxTssNonce, + crossChainTx.GetCurrentOutTxParam().OutboundTxTssNonce, gasprice, height, ) - } else if send.CctxStatus.Status == types.CctxStatus_PendingOutbound { - logger.Info().Msgf("SignOutboundTx: %d => %s, nonce %d, gasprice %d", send.InboundTxParams.SenderChainId, toChain, send.GetCurrentOutTxParam().OutboundTxTssNonce, gasprice) + } else if crossChainTx.CctxStatus.Status == types.CctxStatus_PendingOutbound { + logger.Info().Msgf("SignOutboundTx: %d => %s, nonce %d, gasprice %d", crossChainTx.InboundTxParams.SenderChainId, toChain, crossChainTx.GetCurrentOutTxParam().OutboundTxTssNonce, gasprice) tx, err = signer.SignOutboundTx( - ethcommon.HexToAddress(send.InboundTxParams.Sender), - big.NewInt(send.InboundTxParams.SenderChainId), + ethcommon.HexToAddress(crossChainTx.InboundTxParams.Sender), + big.NewInt(crossChainTx.InboundTxParams.SenderChainId), to, - send.GetCurrentOutTxParam().Amount.BigInt(), + crossChainTx.GetCurrentOutTxParam().Amount.BigInt(), gasLimit, message, sendhash, - send.GetCurrentOutTxParam().OutboundTxTssNonce, + crossChainTx.GetCurrentOutTxParam().OutboundTxTssNonce, gasprice, height, ) } if err != nil { - logger.Warn().Err(err).Msgf("signer SignOutbound error: nonce %d chain %d", send.GetCurrentOutTxParam().OutboundTxTssNonce, send.GetCurrentOutTxParam().ReceiverChainId) + logger.Warn().Err(err).Msgf("signer SignOutbound error: nonce %d chain %d", crossChainTx.GetCurrentOutTxParam().OutboundTxTssNonce, crossChainTx.GetCurrentOutTxParam().ReceiverChainId) return } - logger.Info().Msgf("Key-sign success: %d => %s, nonce %d", send.InboundTxParams.SenderChainId, toChain, send.GetCurrentOutTxParam().OutboundTxTssNonce) + logger.Info().Msgf("Key-sign success: %d => %s, nonce %d", crossChainTx.InboundTxParams.SenderChainId, toChain, crossChainTx.GetCurrentOutTxParam().OutboundTxTssNonce) _, err = zetaBridge.GetObserverList() if err != nil { - logger.Warn().Err(err).Msgf("unable to get observer list: chain %d observation %s", send.GetCurrentOutTxParam().OutboundTxTssNonce, observertypes.ObservationType_OutBoundTx.String()) + logger.Warn().Err(err).Msgf("unable to get observer list: chain %d observation %s", crossChainTx.GetCurrentOutTxParam().OutboundTxTssNonce, observertypes.ObservationType_OutBoundTx.String()) } if tx != nil { outTxHash := tx.Hash().Hex() - logger.Info().Msgf("on chain %s nonce %d, outTxHash %s signer %s", signer.chain, send.GetCurrentOutTxParam().OutboundTxTssNonce, outTxHash, myID) + logger.Info().Msgf("on chain %s nonce %d, outTxHash %s signer %s", signer.chain, crossChainTx.GetCurrentOutTxParam().OutboundTxTssNonce, outTxHash, myID) //if len(signers) == 0 || myid == signers[send.OutboundTxParams.Broadcaster] || myid == signers[int(send.OutboundTxParams.Broadcaster+1)%len(signers)] { backOff := 1000 * time.Millisecond // retry loop: 1s, 2s, 4s, 8s, 16s in case of RPC error for i := 0; i < 5; i++ { - logger.Info().Msgf("broadcasting tx %s to chain %s: nonce %d, retry %d", outTxHash, toChain, send.GetCurrentOutTxParam().OutboundTxTssNonce, i) + logger.Info().Msgf("broadcasting tx %s to chain %s: nonce %d, retry %d", outTxHash, toChain, crossChainTx.GetCurrentOutTxParam().OutboundTxTssNonce, i) // #nosec G404 randomness is not a security issue here time.Sleep(time.Duration(rand.Intn(1500)) * time.Millisecond) // FIXME: use backoff err := signer.Broadcast(tx) if err != nil { log.Warn().Err(err).Msgf("OutTx Broadcast error") - retry, report := HandleBroadcastError(err, strconv.FormatUint(send.GetCurrentOutTxParam().OutboundTxTssNonce, 10), toChain.String(), outTxHash) + retry, report := HandleBroadcastError(err, strconv.FormatUint(crossChainTx.GetCurrentOutTxParam().OutboundTxTssNonce, 10), toChain.String(), outTxHash) if report { signer.reportToOutTxTracker(zetaBridge, toChain.ChainId, tx.Nonce(), outTxHash, logger) } @@ -585,7 +585,7 @@ func (signer *EVMSigner) TryProcessOutTx( backOff *= 2 continue } - logger.Info().Msgf("Broadcast success: nonce %d to chain %s outTxHash %s", send.GetCurrentOutTxParam().OutboundTxTssNonce, toChain, outTxHash) + logger.Info().Msgf("Broadcast success: nonce %d to chain %s outTxHash %s", crossChainTx.GetCurrentOutTxParam().OutboundTxTssNonce, toChain, outTxHash) signer.reportToOutTxTracker(zetaBridge, toChain.ChainId, tx.Nonce(), outTxHash, logger) break // successful broadcast; no need to retry } From 1d4cb5ec8a0929319c982c6e12b87722efcb1532 Mon Sep 17 00:00:00 2001 From: Tanmay Date: Tue, 23 Jan 2024 01:06:51 -0500 Subject: [PATCH 2/6] add changelog entry --- changelog.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/changelog.md b/changelog.md index c6396ae86f..14d75989e5 100644 --- a/changelog.md +++ b/changelog.md @@ -22,6 +22,9 @@ * [1585](https://github.com/zeta-chain/node/pull/1585) - Updated release instructions +### Refactoring +* [1619](https://github.com/zeta-chain/node/pull/1619) - Add evm fee calculation to tss migration of evm chains + ## Version: v12.0.0 ### Breaking Changes From d0db72c6fe8ceaa7d317f8a26020d90e87864b08 Mon Sep 17 00:00:00 2001 From: Tanmay Date: Tue, 23 Jan 2024 14:27:15 -0500 Subject: [PATCH 3/6] add test for multiplier --- .../msg_server_migrate_tss_funds_test.go | 27 ++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/x/crosschain/keeper/msg_server_migrate_tss_funds_test.go b/x/crosschain/keeper/msg_server_migrate_tss_funds_test.go index ff396515e9..675d27ee64 100644 --- a/x/crosschain/keeper/msg_server_migrate_tss_funds_test.go +++ b/x/crosschain/keeper/msg_server_migrate_tss_funds_test.go @@ -15,6 +15,31 @@ import ( observertypes "github.com/zeta-chain/zetacore/x/observer/types" ) +func TestKeeper_MigrateTSSFundsForChain(t *testing.T) { + t.Run("test gas price multiplier", func(t *testing.T) { + k, ctx, _, zk := keepertest.CrosschainKeeper(t) + admin := sample.AccAddress() + setAdminPolicies(ctx, zk, admin) + msgServer := keeper.NewMsgServerImpl(*k) + chain := getValidEthChain(t) + amount := sdkmath.NewUintFromString("10000000000000000000") + indexString, _ := setupTssMigrationParams(zk, k, ctx, *chain, amount, true, true) + gp, found := k.GetMedianGasPriceInUint(ctx, chain.ChainId) + assert.True(t, found) + _, err := msgServer.MigrateTssFunds(ctx, &crosschaintypes.MsgMigrateTssFunds{ + Creator: admin, + ChainId: chain.ChainId, + Amount: amount, + }) + assert.NoError(t, err) + hash := crypto.Keccak256Hash([]byte(indexString)) + index := hash.Hex() + _, found = k.GetCrossChainTx(ctx, index) + assert.True(t, found) + assert.Equal(t, gp.MulUint64(crosschaintypes.TssMigrationGasMultiplierEVM), k.GetGasPrice(ctx, chain.ChainId).Prices[1]) + + }) +} func TestMsgServer_MigrateTssFunds(t *testing.T) { t.Run("successfully create tss migration cctx", func(t *testing.T) { k, ctx, _, zk := keepertest.CrosschainKeeper(t) @@ -210,7 +235,7 @@ func setupTssMigrationParams( ChainId: chain.ChainId, Signers: nil, BlockNums: nil, - Prices: []uint64{1, 1, 1}, + Prices: []uint64{100000, 100000, 100000}, MedianIndex: 1, }) k.GetObserverKeeper().SetChainNonces(ctx, observertypes.ChainNonces{ From 4c92c62dec3cc26730e95204fc330a0cc4d30a1a Mon Sep 17 00:00:00 2001 From: Tanmay Date: Thu, 25 Jan 2024 16:35:18 -0500 Subject: [PATCH 4/6] Update common/gas_limits.go Co-authored-by: Lucas Bertrand --- common/gas_limits.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/gas_limits.go b/common/gas_limits.go index 167537d5e5..0e85ea9f1c 100644 --- a/common/gas_limits.go +++ b/common/gas_limits.go @@ -2,6 +2,6 @@ package common const ( EVMSend = 21000 - // Todo Move gas limits from zeta-client to this file + // TODO: Move gas limits from zeta-client to this file // https://github.com/zeta-chain/node/issues/1606 ) From 1a24f7336d587480ed91c3bb65541a2fdecc01ae Mon Sep 17 00:00:00 2001 From: Tanmay Date: Thu, 25 Jan 2024 17:19:16 -0500 Subject: [PATCH 5/6] add additional checks for unit tests --- common/gas_limits.go | 19 +++++++ .../keeper/msg_server_migrate_tss_funds.go | 14 ++--- .../msg_server_migrate_tss_funds_test.go | 53 ++++++++++--------- x/crosschain/types/keys.go | 2 +- 4 files changed, 52 insertions(+), 36 deletions(-) diff --git a/common/gas_limits.go b/common/gas_limits.go index 0e85ea9f1c..bbd1f0a6c5 100644 --- a/common/gas_limits.go +++ b/common/gas_limits.go @@ -1,7 +1,26 @@ package common +import ( + sdkmath "cosmossdk.io/math" + sdk "github.com/cosmos/cosmos-sdk/types" +) + const ( + // EVMSend is the gas limit required to transfer tokens on an EVM based chain EVMSend = 21000 // TODO: Move gas limits from zeta-client to this file // https://github.com/zeta-chain/node/issues/1606 ) + +// MultiplyGasPrice multiplies the median gas price by the given multiplier and returns the truncated value +func MultiplyGasPrice(medianGasPrice sdkmath.Uint, multiplierString string) (sdkmath.Uint, error) { + multiplier, err := sdk.NewDecFromStr(multiplierString) + if err != nil { + return sdkmath.ZeroUint(), err + } + gasPrice, err := sdk.NewDecFromStr(medianGasPrice.String()) + if err != nil { + return sdkmath.ZeroUint(), err + } + return sdkmath.NewUintFromString(gasPrice.Mul(multiplier).TruncateInt().String()), nil +} diff --git a/x/crosschain/keeper/msg_server_migrate_tss_funds.go b/x/crosschain/keeper/msg_server_migrate_tss_funds.go index 7fb8293a0c..44ab8ee1dc 100644 --- a/x/crosschain/keeper/msg_server_migrate_tss_funds.go +++ b/x/crosschain/keeper/msg_server_migrate_tss_funds.go @@ -119,17 +119,9 @@ func (k Keeper) MigrateTSSFundsForChain(ctx sdk.Context, chainID int64, amount s // Tss migration is a send transaction, so the gas limit is set to 21000 cctx.GetCurrentOutTxParam().OutboundTxGasLimit = common.EVMSend // Multiple current gas price with standard multiplier to add some buffer - multiplier, err := sdk.NewDecFromStr(types.TssMigrationGasMultiplierEVM) - if err != nil { - return err - } - gasPrice, err := sdk.NewDecFromStr(medianGasPrice.String()) - if err != nil { - return err - } - newGasPrice := gasPrice.Mul(multiplier) - cctx.GetCurrentOutTxParam().OutboundTxGasPrice = newGasPrice.TruncateInt().String() - evmFee := sdkmath.NewUint(cctx.GetCurrentOutTxParam().OutboundTxGasLimit).Mul(medianGasPrice) + multipliedGasPrice, err := common.MultiplyGasPrice(medianGasPrice, types.TssMigrationGasMultiplierEVM) + cctx.GetCurrentOutTxParam().OutboundTxGasPrice = multipliedGasPrice.String() + evmFee := sdkmath.NewUint(cctx.GetCurrentOutTxParam().OutboundTxGasLimit).Mul(multipliedGasPrice) if evmFee.GT(amount) { return errorsmod.Wrap(types.ErrInsufficientFundsTssMigration, fmt.Sprintf("insufficient funds to pay for gas fee, amount: %s, gas fee: %s, chainid: %d", amount.String(), evmFee.String(), chainID)) } diff --git a/x/crosschain/keeper/msg_server_migrate_tss_funds_test.go b/x/crosschain/keeper/msg_server_migrate_tss_funds_test.go index 675d27ee64..4d909abdf0 100644 --- a/x/crosschain/keeper/msg_server_migrate_tss_funds_test.go +++ b/x/crosschain/keeper/msg_server_migrate_tss_funds_test.go @@ -6,7 +6,7 @@ import ( sdkmath "cosmossdk.io/math" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/ethereum/go-ethereum/crypto" - "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" "github.com/zeta-chain/zetacore/common" keepertest "github.com/zeta-chain/zetacore/testutil/keeper" "github.com/zeta-chain/zetacore/testutil/sample" @@ -25,18 +25,20 @@ func TestKeeper_MigrateTSSFundsForChain(t *testing.T) { amount := sdkmath.NewUintFromString("10000000000000000000") indexString, _ := setupTssMigrationParams(zk, k, ctx, *chain, amount, true, true) gp, found := k.GetMedianGasPriceInUint(ctx, chain.ChainId) - assert.True(t, found) + require.True(t, found) _, err := msgServer.MigrateTssFunds(ctx, &crosschaintypes.MsgMigrateTssFunds{ Creator: admin, ChainId: chain.ChainId, Amount: amount, }) - assert.NoError(t, err) + require.NoError(t, err) hash := crypto.Keccak256Hash([]byte(indexString)) index := hash.Hex() - _, found = k.GetCrossChainTx(ctx, index) - assert.True(t, found) - assert.Equal(t, gp.MulUint64(crosschaintypes.TssMigrationGasMultiplierEVM), k.GetGasPrice(ctx, chain.ChainId).Prices[1]) + cctx, found := k.GetCrossChainTx(ctx, index) + require.True(t, found) + multipliedValue, err := common.MultiplyGasPrice(gp, crosschaintypes.TssMigrationGasMultiplierEVM) + require.NoError(t, err) + require.Equal(t, multipliedValue.String(), cctx.GetCurrentOutTxParam().OutboundTxGasPrice) }) } @@ -54,11 +56,14 @@ func TestMsgServer_MigrateTssFunds(t *testing.T) { ChainId: chain.ChainId, Amount: amount, }) - assert.NoError(t, err) + require.NoError(t, err) hash := crypto.Keccak256Hash([]byte(indexString)) index := hash.Hex() - _, found := k.GetCrossChainTx(ctx, index) - assert.True(t, found) + cctx, found := k.GetCrossChainTx(ctx, index) + require.True(t, found) + feeCalculated := sdk.NewUint(cctx.GetCurrentOutTxParam().OutboundTxGasLimit). + Mul(sdkmath.NewUintFromString(cctx.GetCurrentOutTxParam().OutboundTxGasPrice)) + require.Equal(t, cctx.GetCurrentOutTxParam().Amount.String(), amount.Sub(feeCalculated).String()) }) t.Run("not enough funds in tss address for migration", func(t *testing.T) { k, ctx, _, zk := keepertest.CrosschainKeeper(t) @@ -73,11 +78,11 @@ func TestMsgServer_MigrateTssFunds(t *testing.T) { ChainId: chain.ChainId, Amount: amount, }) - assert.ErrorContains(t, err, crosschaintypes.ErrCannotMigrateTssFunds.Error()) + require.ErrorContains(t, err, crosschaintypes.ErrCannotMigrateTssFunds.Error()) hash := crypto.Keccak256Hash([]byte(indexString)) index := hash.Hex() _, found := k.GetCrossChainTx(ctx, index) - assert.False(t, found) + require.False(t, found) }) t.Run("unable to migrate funds if new TSS is not created ", func(t *testing.T) { k, ctx, _, zk := keepertest.CrosschainKeeper(t) @@ -92,11 +97,11 @@ func TestMsgServer_MigrateTssFunds(t *testing.T) { ChainId: chain.ChainId, Amount: amount, }) - assert.ErrorContains(t, err, "no new tss address has been generated") + require.ErrorContains(t, err, "no new tss address has been generated") hash := crypto.Keccak256Hash([]byte(indexString)) index := hash.Hex() _, found := k.GetCrossChainTx(ctx, index) - assert.False(t, found) + require.False(t, found) }) t.Run("unable to migrate funds when nonce low does not match nonce high", func(t *testing.T) { k, ctx, _, zk := keepertest.CrosschainKeeper(t) @@ -117,12 +122,12 @@ func TestMsgServer_MigrateTssFunds(t *testing.T) { ChainId: chain.ChainId, Amount: amount, }) - assert.ErrorIs(t, err, crosschaintypes.ErrCannotMigrateTssFunds) - assert.ErrorContains(t, err, "cannot migrate funds when there are pending nonces") + require.ErrorIs(t, err, crosschaintypes.ErrCannotMigrateTssFunds) + require.ErrorContains(t, err, "cannot migrate funds when there are pending nonces") hash := crypto.Keccak256Hash([]byte(indexString)) index := hash.Hex() _, found := k.GetCrossChainTx(ctx, index) - assert.False(t, found) + require.False(t, found) }) t.Run("unable to migrate funds when a pending cctx is presnt in migration info", func(t *testing.T) { k, ctx, _, zk := keepertest.CrosschainKeeper(t) @@ -150,14 +155,14 @@ func TestMsgServer_MigrateTssFunds(t *testing.T) { ChainId: chain.ChainId, Amount: amount, }) - assert.ErrorIs(t, err, crosschaintypes.ErrCannotMigrateTssFunds) - assert.ErrorContains(t, err, "cannot migrate funds while there are pending migrations") + require.ErrorIs(t, err, crosschaintypes.ErrCannotMigrateTssFunds) + require.ErrorContains(t, err, "cannot migrate funds while there are pending migrations") hash := crypto.Keccak256Hash([]byte(indexString)) index := hash.Hex() _, found := k.GetCrossChainTx(ctx, index) - assert.False(t, found) + require.False(t, found) _, found = k.GetCrossChainTx(ctx, existingCctx.Index) - assert.True(t, found) + require.True(t, found) }) t.Run("unable to migrate funds if current TSS is not present in TSSHistory and no new TSS has been generated", func(t *testing.T) { @@ -169,7 +174,7 @@ func TestMsgServer_MigrateTssFunds(t *testing.T) { amount := sdkmath.NewUintFromString("10000000000000000000") indexString, _ := setupTssMigrationParams(zk, k, ctx, *chain, amount, false, false) currentTss, found := k.GetObserverKeeper().GetTSS(ctx) - assert.True(t, found) + require.True(t, found) newTss := sample.Tss() newTss.FinalizedZetaHeight = currentTss.FinalizedZetaHeight - 10 newTss.KeyGenZetaHeight = currentTss.KeyGenZetaHeight - 10 @@ -179,12 +184,12 @@ func TestMsgServer_MigrateTssFunds(t *testing.T) { ChainId: chain.ChainId, Amount: amount, }) - assert.ErrorIs(t, err, crosschaintypes.ErrCannotMigrateTssFunds) - assert.ErrorContains(t, err, "current tss is the latest") + require.ErrorIs(t, err, crosschaintypes.ErrCannotMigrateTssFunds) + require.ErrorContains(t, err, "current tss is the latest") hash := crypto.Keccak256Hash([]byte(indexString)) index := hash.Hex() _, found = k.GetCrossChainTx(ctx, index) - assert.False(t, found) + require.False(t, found) }) } func setupTssMigrationParams( diff --git a/x/crosschain/types/keys.go b/x/crosschain/types/keys.go index dbec6d5c9f..4adac4d73f 100644 --- a/x/crosschain/types/keys.go +++ b/x/crosschain/types/keys.go @@ -25,7 +25,7 @@ const ( MemStoreKey = "mem_metacore" ProtocolFee = 2000000000000000000 - + //TssMigrationGasMultiplierEVM is multiplied to the median gas price to get the gas price for the tss migration . This is done to avoid the tss migration tx getting stuck in the mempool TssMigrationGasMultiplierEVM = "2.5" ) From ec6b0d8eb62f0f456266e7dc6dd7668ea0612f75 Mon Sep 17 00:00:00 2001 From: Tanmay Date: Thu, 25 Jan 2024 17:41:21 -0500 Subject: [PATCH 6/6] rename crosschainTX to cctx --- .../keeper/msg_server_migrate_tss_funds.go | 3 + zetaclient/evm_signer.go | 152 +++++++++--------- 2 files changed, 79 insertions(+), 76 deletions(-) diff --git a/x/crosschain/keeper/msg_server_migrate_tss_funds.go b/x/crosschain/keeper/msg_server_migrate_tss_funds.go index 44ab8ee1dc..a552d0feb4 100644 --- a/x/crosschain/keeper/msg_server_migrate_tss_funds.go +++ b/x/crosschain/keeper/msg_server_migrate_tss_funds.go @@ -120,6 +120,9 @@ func (k Keeper) MigrateTSSFundsForChain(ctx sdk.Context, chainID int64, amount s cctx.GetCurrentOutTxParam().OutboundTxGasLimit = common.EVMSend // Multiple current gas price with standard multiplier to add some buffer multipliedGasPrice, err := common.MultiplyGasPrice(medianGasPrice, types.TssMigrationGasMultiplierEVM) + if err != nil { + return err + } cctx.GetCurrentOutTxParam().OutboundTxGasPrice = multipliedGasPrice.String() evmFee := sdkmath.NewUint(cctx.GetCurrentOutTxParam().OutboundTxGasLimit).Mul(multipliedGasPrice) if evmFee.GT(amount) { diff --git a/zetaclient/evm_signer.go b/zetaclient/evm_signer.go index 70f8816246..204ff75cc7 100644 --- a/zetaclient/evm_signer.go +++ b/zetaclient/evm_signer.go @@ -318,7 +318,7 @@ func (signer *EVMSigner) SignCommandTx( } func (signer *EVMSigner) TryProcessOutTx( - crossChainTx *types.CrossChainTx, + cctx *types.CrossChainTx, outTxMan *OutTxProcessorManager, outTxID string, chainclient ChainClient, @@ -327,10 +327,10 @@ func (signer *EVMSigner) TryProcessOutTx( ) { logger := signer.logger.With(). Str("outTxID", outTxID). - Str("SendHash", crossChainTx.Index). + Str("SendHash", cctx.Index). Logger() logger.Info().Msgf("start processing outTxID %s", outTxID) - logger.Info().Msgf("EVM Chain TryProcessOutTx: %s, value %d to %s", crossChainTx.Index, crossChainTx.GetCurrentOutTxParam().Amount.BigInt(), crossChainTx.GetCurrentOutTxParam().Receiver) + logger.Info().Msgf("EVM Chain TryProcessOutTx: %s, value %d to %s", cctx.Index, cctx.GetCurrentOutTxParam().Amount.BigInt(), cctx.GetCurrentOutTxParam().Receiver) defer func() { outTxMan.EndTryProcess(outTxID) @@ -339,23 +339,23 @@ func (signer *EVMSigner) TryProcessOutTx( var to ethcommon.Address var toChain *common.Chain - if crossChainTx.CctxStatus.Status == types.CctxStatus_PendingRevert { - to = ethcommon.HexToAddress(crossChainTx.InboundTxParams.Sender) - toChain = common.GetChainFromChainID(crossChainTx.InboundTxParams.SenderChainId) + if cctx.CctxStatus.Status == types.CctxStatus_PendingRevert { + to = ethcommon.HexToAddress(cctx.InboundTxParams.Sender) + toChain = common.GetChainFromChainID(cctx.InboundTxParams.SenderChainId) if toChain == nil { - logger.Error().Msgf("Unknown chain: %d", crossChainTx.InboundTxParams.SenderChainId) + logger.Error().Msgf("Unknown chain: %d", cctx.InboundTxParams.SenderChainId) return } logger.Info().Msgf("Abort: reverting inbound") - } else if crossChainTx.CctxStatus.Status == types.CctxStatus_PendingOutbound { - to = ethcommon.HexToAddress(crossChainTx.GetCurrentOutTxParam().Receiver) - toChain = common.GetChainFromChainID(crossChainTx.GetCurrentOutTxParam().ReceiverChainId) + } else if cctx.CctxStatus.Status == types.CctxStatus_PendingOutbound { + to = ethcommon.HexToAddress(cctx.GetCurrentOutTxParam().Receiver) + toChain = common.GetChainFromChainID(cctx.GetCurrentOutTxParam().ReceiverChainId) if toChain == nil { - logger.Error().Msgf("Unknown chain: %d", crossChainTx.GetCurrentOutTxParam().ReceiverChainId) + logger.Error().Msgf("Unknown chain: %d", cctx.GetCurrentOutTxParam().ReceiverChainId) return } } else { - logger.Info().Msgf("Transaction doesn't need to be processed status: %d", crossChainTx.CctxStatus.Status) + logger.Info().Msgf("Transaction doesn't need to be processed status: %d", cctx.CctxStatus.Status) return } evmClient, ok := chainclient.(*EVMChainClient) @@ -365,8 +365,8 @@ func (signer *EVMSigner) TryProcessOutTx( } // Early return if the cctx is already processed - nonce := crossChainTx.GetCurrentOutTxParam().OutboundTxTssNonce - included, confirmed, err := evmClient.IsSendOutTxProcessed(crossChainTx.Index, nonce, crossChainTx.GetCurrentOutTxParam().CoinType, logger) + nonce := cctx.GetCurrentOutTxParam().OutboundTxTssNonce + included, confirmed, err := evmClient.IsSendOutTxProcessed(cctx.Index, nonce, cctx.GetCurrentOutTxParam().CoinType, logger) if err != nil { logger.Error().Err(err).Msg("IsSendOutTxProcessed failed") } @@ -376,27 +376,27 @@ func (signer *EVMSigner) TryProcessOutTx( } var message []byte - if crossChainTx.GetCurrentOutTxParam().CoinType != common.CoinType_Cmd { - message, err = base64.StdEncoding.DecodeString(crossChainTx.RelayedMessage) + if cctx.GetCurrentOutTxParam().CoinType != common.CoinType_Cmd { + message, err = base64.StdEncoding.DecodeString(cctx.RelayedMessage) if err != nil { - logger.Err(err).Msgf("decode CCTX.Message %s error", crossChainTx.RelayedMessage) + logger.Err(err).Msgf("decode CCTX.Message %s error", cctx.RelayedMessage) } } - gasLimit := crossChainTx.GetCurrentOutTxParam().OutboundTxGasLimit + gasLimit := cctx.GetCurrentOutTxParam().OutboundTxGasLimit if gasLimit < 100_000 { gasLimit = 100_000 - logger.Warn().Msgf("gasLimit %d is too low; set to %d", crossChainTx.GetCurrentOutTxParam().OutboundTxGasLimit, gasLimit) + logger.Warn().Msgf("gasLimit %d is too low; set to %d", cctx.GetCurrentOutTxParam().OutboundTxGasLimit, gasLimit) } if gasLimit > 1_000_000 { gasLimit = 1_000_000 - logger.Warn().Msgf("gasLimit %d is too high; set to %d", crossChainTx.GetCurrentOutTxParam().OutboundTxGasLimit, gasLimit) + logger.Warn().Msgf("gasLimit %d is too high; set to %d", cctx.GetCurrentOutTxParam().OutboundTxGasLimit, gasLimit) } - logger.Info().Msgf("chain %s minting %d to %s, nonce %d, finalized zeta bn %d", toChain, crossChainTx.InboundTxParams.Amount, to.Hex(), nonce, crossChainTx.InboundTxParams.InboundTxFinalizedZetaHeight) - sendHash, err := hex.DecodeString(crossChainTx.Index[2:]) // remove the leading 0x + logger.Info().Msgf("chain %s minting %d to %s, nonce %d, finalized zeta bn %d", toChain, cctx.InboundTxParams.Amount, to.Hex(), nonce, cctx.InboundTxParams.InboundTxFinalizedZetaHeight) + sendHash, err := hex.DecodeString(cctx.Index[2:]) // remove the leading 0x if err != nil || len(sendHash) != 32 { - logger.Error().Err(err).Msgf("decode CCTX %s error", crossChainTx.Index) + logger.Error().Err(err).Msgf("decode CCTX %s error", cctx.Index) return } var sendhash [32]byte @@ -408,7 +408,7 @@ func (signer *EVMSigner) TryProcessOutTx( // The code below is a fix for https://github.com/zeta-chain/node/issues/1085 // doesn't close directly the issue because we should determine if we want to keep using SuggestGasPrice if no OutboundTxGasPrice // we should possibly remove it completely and return an error if no OutboundTxGasPrice is provided because it means no fee is processed on ZetaChain - specified, ok := new(big.Int).SetString(crossChainTx.GetCurrentOutTxParam().OutboundTxGasPrice, 10) + specified, ok := new(big.Int).SetString(cctx.GetCurrentOutTxParam().OutboundTxGasPrice, 10) if !ok { if common.IsEthereumChain(toChain.ChainId) { suggested, err := signer.client.SuggestGasPrice(context.Background()) @@ -418,7 +418,7 @@ func (signer *EVMSigner) TryProcessOutTx( } gasprice = roundUpToNearestGwei(suggested) } else { - logger.Error().Err(err).Msgf("cannot convert gas price %s ", crossChainTx.GetCurrentOutTxParam().OutboundTxGasPrice) + logger.Error().Err(err).Msgf("cannot convert gas price %s ", cctx.GetCurrentOutTxParam().OutboundTxGasPrice) return } } else { @@ -444,138 +444,138 @@ func (signer *EVMSigner) TryProcessOutTx( var tx *ethtypes.Transaction - if crossChainTx.GetCurrentOutTxParam().CoinType == common.CoinType_Cmd { // admin command - to := ethcommon.HexToAddress(crossChainTx.GetCurrentOutTxParam().Receiver) + if cctx.GetCurrentOutTxParam().CoinType == common.CoinType_Cmd { // admin command + to := ethcommon.HexToAddress(cctx.GetCurrentOutTxParam().Receiver) if to == (ethcommon.Address{}) { - logger.Error().Msgf("invalid receiver %s", crossChainTx.GetCurrentOutTxParam().Receiver) + logger.Error().Msgf("invalid receiver %s", cctx.GetCurrentOutTxParam().Receiver) return } - msg := strings.Split(crossChainTx.RelayedMessage, ":") + msg := strings.Split(cctx.RelayedMessage, ":") if len(msg) != 2 { logger.Error().Msgf("invalid message %s", msg) return } - tx, err = signer.SignCommandTx(msg[0], msg[1], to, crossChainTx.GetCurrentOutTxParam(), gasLimit, gasprice, height) - } else if crossChainTx.InboundTxParams.SenderChainId == zetaBridge.ZetaChain().ChainId && crossChainTx.CctxStatus.Status == types.CctxStatus_PendingOutbound && flags.IsOutboundEnabled { - if crossChainTx.GetCurrentOutTxParam().CoinType == common.CoinType_Gas { - logger.Info().Msgf("SignWithdrawTx: %d => %s, nonce %d, gasprice %d", crossChainTx.InboundTxParams.SenderChainId, toChain, crossChainTx.GetCurrentOutTxParam().OutboundTxTssNonce, gasprice) + tx, err = signer.SignCommandTx(msg[0], msg[1], to, cctx.GetCurrentOutTxParam(), gasLimit, gasprice, height) + } else if cctx.InboundTxParams.SenderChainId == zetaBridge.ZetaChain().ChainId && cctx.CctxStatus.Status == types.CctxStatus_PendingOutbound && flags.IsOutboundEnabled { + if cctx.GetCurrentOutTxParam().CoinType == common.CoinType_Gas { + logger.Info().Msgf("SignWithdrawTx: %d => %s, nonce %d, gasprice %d", cctx.InboundTxParams.SenderChainId, toChain, cctx.GetCurrentOutTxParam().OutboundTxTssNonce, gasprice) tx, err = signer.SignWithdrawTx( to, - crossChainTx.GetCurrentOutTxParam().Amount.BigInt(), - crossChainTx.GetCurrentOutTxParam().OutboundTxTssNonce, + cctx.GetCurrentOutTxParam().Amount.BigInt(), + cctx.GetCurrentOutTxParam().OutboundTxTssNonce, gasprice, height, ) } - if crossChainTx.GetCurrentOutTxParam().CoinType == common.CoinType_ERC20 { - asset := ethcommon.HexToAddress(crossChainTx.InboundTxParams.Asset) - logger.Info().Msgf("SignERC20WithdrawTx: %d => %s, nonce %d, gasprice %d", crossChainTx.InboundTxParams.SenderChainId, toChain, crossChainTx.GetCurrentOutTxParam().OutboundTxTssNonce, gasprice) + if cctx.GetCurrentOutTxParam().CoinType == common.CoinType_ERC20 { + asset := ethcommon.HexToAddress(cctx.InboundTxParams.Asset) + logger.Info().Msgf("SignERC20WithdrawTx: %d => %s, nonce %d, gasprice %d", cctx.InboundTxParams.SenderChainId, toChain, cctx.GetCurrentOutTxParam().OutboundTxTssNonce, gasprice) tx, err = signer.SignERC20WithdrawTx( to, asset, - crossChainTx.GetCurrentOutTxParam().Amount.BigInt(), + cctx.GetCurrentOutTxParam().Amount.BigInt(), gasLimit, - crossChainTx.GetCurrentOutTxParam().OutboundTxTssNonce, + cctx.GetCurrentOutTxParam().OutboundTxTssNonce, gasprice, height, ) } - if crossChainTx.GetCurrentOutTxParam().CoinType == common.CoinType_Zeta { - logger.Info().Msgf("SignOutboundTx: %d => %s, nonce %d, gasprice %d", crossChainTx.InboundTxParams.SenderChainId, toChain, crossChainTx.GetCurrentOutTxParam().OutboundTxTssNonce, gasprice) + if cctx.GetCurrentOutTxParam().CoinType == common.CoinType_Zeta { + logger.Info().Msgf("SignOutboundTx: %d => %s, nonce %d, gasprice %d", cctx.InboundTxParams.SenderChainId, toChain, cctx.GetCurrentOutTxParam().OutboundTxTssNonce, gasprice) tx, err = signer.SignOutboundTx( - ethcommon.HexToAddress(crossChainTx.InboundTxParams.Sender), - big.NewInt(crossChainTx.InboundTxParams.SenderChainId), + ethcommon.HexToAddress(cctx.InboundTxParams.Sender), + big.NewInt(cctx.InboundTxParams.SenderChainId), to, - crossChainTx.GetCurrentOutTxParam().Amount.BigInt(), + cctx.GetCurrentOutTxParam().Amount.BigInt(), gasLimit, message, sendhash, - crossChainTx.GetCurrentOutTxParam().OutboundTxTssNonce, + cctx.GetCurrentOutTxParam().OutboundTxTssNonce, gasprice, height, ) } - } else if crossChainTx.CctxStatus.Status == types.CctxStatus_PendingRevert && crossChainTx.OutboundTxParams[0].ReceiverChainId == zetaBridge.ZetaChain().ChainId { - if crossChainTx.GetCurrentOutTxParam().CoinType == common.CoinType_Gas { - logger.Info().Msgf("SignWithdrawTx: %d => %s, nonce %d, gasprice %d", crossChainTx.InboundTxParams.SenderChainId, toChain, crossChainTx.GetCurrentOutTxParam().OutboundTxTssNonce, gasprice) + } else if cctx.CctxStatus.Status == types.CctxStatus_PendingRevert && cctx.OutboundTxParams[0].ReceiverChainId == zetaBridge.ZetaChain().ChainId { + if cctx.GetCurrentOutTxParam().CoinType == common.CoinType_Gas { + logger.Info().Msgf("SignWithdrawTx: %d => %s, nonce %d, gasprice %d", cctx.InboundTxParams.SenderChainId, toChain, cctx.GetCurrentOutTxParam().OutboundTxTssNonce, gasprice) tx, err = signer.SignWithdrawTx( to, - crossChainTx.GetCurrentOutTxParam().Amount.BigInt(), - crossChainTx.GetCurrentOutTxParam().OutboundTxTssNonce, + cctx.GetCurrentOutTxParam().Amount.BigInt(), + cctx.GetCurrentOutTxParam().OutboundTxTssNonce, gasprice, height, ) } - if crossChainTx.GetCurrentOutTxParam().CoinType == common.CoinType_ERC20 { - asset := ethcommon.HexToAddress(crossChainTx.InboundTxParams.Asset) - logger.Info().Msgf("SignERC20WithdrawTx: %d => %s, nonce %d, gasprice %d", crossChainTx.InboundTxParams.SenderChainId, toChain, crossChainTx.GetCurrentOutTxParam().OutboundTxTssNonce, gasprice) + if cctx.GetCurrentOutTxParam().CoinType == common.CoinType_ERC20 { + asset := ethcommon.HexToAddress(cctx.InboundTxParams.Asset) + logger.Info().Msgf("SignERC20WithdrawTx: %d => %s, nonce %d, gasprice %d", cctx.InboundTxParams.SenderChainId, toChain, cctx.GetCurrentOutTxParam().OutboundTxTssNonce, gasprice) tx, err = signer.SignERC20WithdrawTx( to, asset, - crossChainTx.GetCurrentOutTxParam().Amount.BigInt(), + cctx.GetCurrentOutTxParam().Amount.BigInt(), gasLimit, - crossChainTx.GetCurrentOutTxParam().OutboundTxTssNonce, + cctx.GetCurrentOutTxParam().OutboundTxTssNonce, gasprice, height, ) } - } else if crossChainTx.CctxStatus.Status == types.CctxStatus_PendingRevert { - logger.Info().Msgf("SignRevertTx: %d => %s, nonce %d, gasprice %d", crossChainTx.InboundTxParams.SenderChainId, toChain, crossChainTx.GetCurrentOutTxParam().OutboundTxTssNonce, gasprice) + } else if cctx.CctxStatus.Status == types.CctxStatus_PendingRevert { + logger.Info().Msgf("SignRevertTx: %d => %s, nonce %d, gasprice %d", cctx.InboundTxParams.SenderChainId, toChain, cctx.GetCurrentOutTxParam().OutboundTxTssNonce, gasprice) tx, err = signer.SignRevertTx( - ethcommon.HexToAddress(crossChainTx.InboundTxParams.Sender), - big.NewInt(crossChainTx.OutboundTxParams[0].ReceiverChainId), + ethcommon.HexToAddress(cctx.InboundTxParams.Sender), + big.NewInt(cctx.OutboundTxParams[0].ReceiverChainId), to.Bytes(), - big.NewInt(crossChainTx.GetCurrentOutTxParam().ReceiverChainId), - crossChainTx.GetCurrentOutTxParam().Amount.BigInt(), + big.NewInt(cctx.GetCurrentOutTxParam().ReceiverChainId), + cctx.GetCurrentOutTxParam().Amount.BigInt(), gasLimit, message, sendhash, - crossChainTx.GetCurrentOutTxParam().OutboundTxTssNonce, + cctx.GetCurrentOutTxParam().OutboundTxTssNonce, gasprice, height, ) - } else if crossChainTx.CctxStatus.Status == types.CctxStatus_PendingOutbound { - logger.Info().Msgf("SignOutboundTx: %d => %s, nonce %d, gasprice %d", crossChainTx.InboundTxParams.SenderChainId, toChain, crossChainTx.GetCurrentOutTxParam().OutboundTxTssNonce, gasprice) + } else if cctx.CctxStatus.Status == types.CctxStatus_PendingOutbound { + logger.Info().Msgf("SignOutboundTx: %d => %s, nonce %d, gasprice %d", cctx.InboundTxParams.SenderChainId, toChain, cctx.GetCurrentOutTxParam().OutboundTxTssNonce, gasprice) tx, err = signer.SignOutboundTx( - ethcommon.HexToAddress(crossChainTx.InboundTxParams.Sender), - big.NewInt(crossChainTx.InboundTxParams.SenderChainId), + ethcommon.HexToAddress(cctx.InboundTxParams.Sender), + big.NewInt(cctx.InboundTxParams.SenderChainId), to, - crossChainTx.GetCurrentOutTxParam().Amount.BigInt(), + cctx.GetCurrentOutTxParam().Amount.BigInt(), gasLimit, message, sendhash, - crossChainTx.GetCurrentOutTxParam().OutboundTxTssNonce, + cctx.GetCurrentOutTxParam().OutboundTxTssNonce, gasprice, height, ) } if err != nil { - logger.Warn().Err(err).Msgf("signer SignOutbound error: nonce %d chain %d", crossChainTx.GetCurrentOutTxParam().OutboundTxTssNonce, crossChainTx.GetCurrentOutTxParam().ReceiverChainId) + logger.Warn().Err(err).Msgf("signer SignOutbound error: nonce %d chain %d", cctx.GetCurrentOutTxParam().OutboundTxTssNonce, cctx.GetCurrentOutTxParam().ReceiverChainId) return } - logger.Info().Msgf("Key-sign success: %d => %s, nonce %d", crossChainTx.InboundTxParams.SenderChainId, toChain, crossChainTx.GetCurrentOutTxParam().OutboundTxTssNonce) + logger.Info().Msgf("Key-sign success: %d => %s, nonce %d", cctx.InboundTxParams.SenderChainId, toChain, cctx.GetCurrentOutTxParam().OutboundTxTssNonce) _, err = zetaBridge.GetObserverList() if err != nil { - logger.Warn().Err(err).Msgf("unable to get observer list: chain %d observation %s", crossChainTx.GetCurrentOutTxParam().OutboundTxTssNonce, observertypes.ObservationType_OutBoundTx.String()) + logger.Warn().Err(err).Msgf("unable to get observer list: chain %d observation %s", cctx.GetCurrentOutTxParam().OutboundTxTssNonce, observertypes.ObservationType_OutBoundTx.String()) } if tx != nil { outTxHash := tx.Hash().Hex() - logger.Info().Msgf("on chain %s nonce %d, outTxHash %s signer %s", signer.chain, crossChainTx.GetCurrentOutTxParam().OutboundTxTssNonce, outTxHash, myID) + logger.Info().Msgf("on chain %s nonce %d, outTxHash %s signer %s", signer.chain, cctx.GetCurrentOutTxParam().OutboundTxTssNonce, outTxHash, myID) //if len(signers) == 0 || myid == signers[send.OutboundTxParams.Broadcaster] || myid == signers[int(send.OutboundTxParams.Broadcaster+1)%len(signers)] { backOff := 1000 * time.Millisecond // retry loop: 1s, 2s, 4s, 8s, 16s in case of RPC error for i := 0; i < 5; i++ { - logger.Info().Msgf("broadcasting tx %s to chain %s: nonce %d, retry %d", outTxHash, toChain, crossChainTx.GetCurrentOutTxParam().OutboundTxTssNonce, i) + logger.Info().Msgf("broadcasting tx %s to chain %s: nonce %d, retry %d", outTxHash, toChain, cctx.GetCurrentOutTxParam().OutboundTxTssNonce, i) // #nosec G404 randomness is not a security issue here time.Sleep(time.Duration(rand.Intn(1500)) * time.Millisecond) // FIXME: use backoff err := signer.Broadcast(tx) if err != nil { log.Warn().Err(err).Msgf("OutTx Broadcast error") - retry, report := HandleBroadcastError(err, strconv.FormatUint(crossChainTx.GetCurrentOutTxParam().OutboundTxTssNonce, 10), toChain.String(), outTxHash) + retry, report := HandleBroadcastError(err, strconv.FormatUint(cctx.GetCurrentOutTxParam().OutboundTxTssNonce, 10), toChain.String(), outTxHash) if report { signer.reportToOutTxTracker(zetaBridge, toChain.ChainId, tx.Nonce(), outTxHash, logger) } @@ -585,7 +585,7 @@ func (signer *EVMSigner) TryProcessOutTx( backOff *= 2 continue } - logger.Info().Msgf("Broadcast success: nonce %d to chain %s outTxHash %s", crossChainTx.GetCurrentOutTxParam().OutboundTxTssNonce, toChain, outTxHash) + logger.Info().Msgf("Broadcast success: nonce %d to chain %s outTxHash %s", cctx.GetCurrentOutTxParam().OutboundTxTssNonce, toChain, outTxHash) signer.reportToOutTxTracker(zetaBridge, toChain.ChainId, tx.Nonce(), outTxHash, logger) break // successful broadcast; no need to retry }