diff --git a/changelog.md b/changelog.md index 004fa82933..f2ab4302eb 100644 --- a/changelog.md +++ b/changelog.md @@ -8,7 +8,8 @@ * [1535](https://github.com/zeta-chain/node/issues/1535) - Avoid voting on wrong ballots due to false blockNumber in EVM tx receipt * [1588](https://github.com/zeta-chain/node/pull/1588) - fix chain params comparison logic -* [1592](https://github.com/zeta-chain/node/issues/1592) - Check inbound tracker tx hash against Tss address and some refactor on inTx observation +* [1591](https://github.com/zeta-chain/node/pull/1591) - support lower gas limit for voting on inbound and outbound transactions +* [1602](https://github.com/zeta-chain/node/issues/1602) - Check inbound tracker tx hash against Tss address and some refactor on inTx observation ## Version: v12.0.0 @@ -43,7 +44,10 @@ Getting the correct TSS address for Bitcoin now requires proviidng the Bitcoin c ### Fixes +<<<<<<< HEAD * [1576](https://github.com/zeta-chain/node/pull/1576) - Fix zetaclient crash due to out of bound integer conversion and log prints. +======= +>>>>>>> 6de5bc85c8b7c298e285602b57a4d2f8ff2d21cd * [1575](https://github.com/zeta-chain/node/issues/1575) - Skip unsupported chain parameters by IsSupported flag * [1554](https://github.com/zeta-chain/node/pull/1554) - Screen out unconfirmed UTXOs that are not created by TSS itself * [1560](https://github.com/zeta-chain/node/issues/1560) - Zetaclient post evm-chain outtx hashes only when receipt is available diff --git a/contrib/localnet/scripts/start-zetaclientd-genesis.sh b/contrib/localnet/scripts/start-zetaclientd-genesis.sh index 7c23cd0673..5f2cadceab 100755 --- a/contrib/localnet/scripts/start-zetaclientd-genesis.sh +++ b/contrib/localnet/scripts/start-zetaclientd-genesis.sh @@ -36,6 +36,6 @@ else SEED=$(curl --retry 10 --retry-delay 5 --retry-connrefused -s zetaclient0:8123/p2p) done rm ~/.tss/* - zetaclientd init --peer /ip4/172.20.0.21/tcp/6668/p2p/"$SEED" --zetacore-url "$node" --chain-id athens_101-1 --operator "$operatorAddress" --log-format=text --public-ip "$MYIP" --log-level 0 --keyring-backend "$BACKEND" + zetaclientd init --peer /ip4/172.20.0.21/tcp/6668/p2p/"$SEED" --zetacore-url "$node" --chain-id athens_101-1 --operator "$operatorAddress" --log-format=text --public-ip "$MYIP" --log-level 1 --keyring-backend "$BACKEND" zetaclientd start fi diff --git a/contrib/localnet/scripts/start-zetaclientd.sh b/contrib/localnet/scripts/start-zetaclientd.sh index 14de0fada6..eecb8b8e5d 100644 --- a/contrib/localnet/scripts/start-zetaclientd.sh +++ b/contrib/localnet/scripts/start-zetaclientd.sh @@ -23,6 +23,6 @@ else zetaclientd init \ --peer /ip4/172.20.0.21/tcp/6668/p2p/$SEED \ --pre-params ~/preParams.json --zetacore-url $node \ - --chain-id athens_101-1 --operator zeta1lz2fqwzjnk6qy48fgj753h48444fxtt7hekp52 --log-level 0 --hotkey=val_grantee_observer + --chain-id athens_101-1 --operator zeta1lz2fqwzjnk6qy48fgj753h48444fxtt7hekp52 --log-level 1 --hotkey=val_grantee_observer zetaclientd start fi diff --git a/zetaclient/bitcoin_client.go b/zetaclient/bitcoin_client.go index a82e3a44cb..b0fa1aae64 100644 --- a/zetaclient/bitcoin_client.go +++ b/zetaclient/bitcoin_client.go @@ -385,12 +385,12 @@ func (ob *BitcoinChainClient) observeInTx() error { // post inbound vote message to zetacore for _, inTx := range inTxs { msg := ob.GetInboundVoteMessageFromBtcEvent(inTx) - zetaHash, ballot, err := ob.zetaClient.PostSend(PostSendEVMGasLimit, msg) + zetaHash, ballot, err := ob.zetaClient.PostVoteInbound(PostVoteInboundGasLimit, PostVoteInboundExecutionGasLimit, msg) if err != nil { ob.logger.WatchInTx.Error().Err(err).Msgf("observeInTxBTC: error posting to zeta core for tx %s", inTx.TxHash) return err // we have to re-scan this block next time } else if zetaHash != "" { - ob.logger.WatchInTx.Info().Msgf("observeInTxBTC: BTC deposit detected and reported: PostSend zeta tx: %s ballot %s", zetaHash, ballot) + ob.logger.WatchInTx.Info().Msgf("observeInTxBTC: BTC deposit detected and reported: PostVoteInbound zeta tx: %s ballot %s", zetaHash, ballot) } } @@ -470,7 +470,7 @@ func (ob *BitcoinChainClient) IsSendOutTxProcessed(sendHash string, nonce uint64 } logger.Debug().Msgf("Bitcoin outTx confirmed: txid %s, amount %s\n", res.TxID, amountInSat.String()) - zetaHash, ballot, err := ob.zetaClient.PostReceiveConfirmation( + zetaHash, ballot, err := ob.zetaClient.PostVoteOutbound( sendHash, res.TxID, // #nosec G701 always positive diff --git a/zetaclient/broadcast.go b/zetaclient/broadcast.go index f015f2df85..c4fee8c1c9 100644 --- a/zetaclient/broadcast.go +++ b/zetaclient/broadcast.go @@ -53,7 +53,6 @@ func (b *ZetaCoreBridge) Broadcast(gaslimit uint64, authzWrappedMsg sdktypes.Msg b.seqNumber[authzSigner.KeyType] = seqNumber } } - //b.logger.Info().Uint64("account_number", b.accountNumber).Uint64("sequence_number", b.seqNumber).Msg("account info") flags := flag.NewFlagSet("zetacore", 0) @@ -69,12 +68,13 @@ func (b *ZetaCoreBridge) Broadcast(gaslimit uint64, authzWrappedMsg sdktypes.Msg if err != nil { return "", err } + builder.SetGasLimit(gaslimit) + // #nosec G701 always in range fee := sdktypes.NewCoins(sdktypes.NewCoin(config.BaseDenom, cosmos.NewInt(int64(gaslimit)).Mul(cosmos.NewInt(baseGasPrice)))) builder.SetFeeAmount(fee) - //fmt.Printf("signing from name: %s\n", ctx.GetFromName()) err = b.SignTx(factory, ctx.GetFromName(), builder, true, ctx.TxConfig) if err != nil { return "", err @@ -90,6 +90,7 @@ func (b *ZetaCoreBridge) Broadcast(gaslimit uint64, authzWrappedMsg sdktypes.Msg b.logger.Error().Err(err).Msgf("fail to broadcast tx %s", err.Error()) return "", err } + // Code will be the tendermint ABICode , it start at 1 , so if it is an error , code will not be zero if commit.Code > 0 { if commit.Code == 32 { @@ -114,11 +115,8 @@ func (b *ZetaCoreBridge) Broadcast(gaslimit uint64, authzWrappedMsg sdktypes.Msg } return commit.TxHash, fmt.Errorf("fail to broadcast to zetachain,code:%d, log:%s", commit.Code, commit.RawLog) } - //b.logger.Debug().Msgf("Received a TxHash of %v from the metachain, Code %d, log %s", commit.TxHash, commit.Code, commit.Logs) // increment seqNum - //seq := b.seqNumber[authzSigner.KeyType] - //atomic.AddUint64(&seq, 1) b.seqNumber[authzSigner.KeyType] = b.seqNumber[authzSigner.KeyType] + 1 return commit.TxHash, nil diff --git a/zetaclient/evm_client.go b/zetaclient/evm_client.go index ba51d28244..65c7f7c13a 100644 --- a/zetaclient/evm_client.go +++ b/zetaclient/evm_client.go @@ -317,7 +317,7 @@ func (ob *EVMChainClient) IsSendOutTxProcessed(sendHash string, nonce uint64, co if receipt.Status == 1 { recvStatus = common.ReceiveStatus_Success } - zetaTxHash, ballot, err := ob.zetaClient.PostReceiveConfirmation( + zetaTxHash, ballot, err := ob.zetaClient.PostVoteOutbound( sendHash, receipt.TxHash.Hex(), receipt.BlockNumber.Uint64(), @@ -339,7 +339,7 @@ func (ob *EVMChainClient) IsSendOutTxProcessed(sendHash string, nonce uint64, co } else if cointype == common.CoinType_Gas { // the outbound is a regular Ether/BNB/Matic transfer; no need to check events if receipt.Status == 1 { - zetaTxHash, ballot, err := ob.zetaClient.PostReceiveConfirmation( + zetaTxHash, ballot, err := ob.zetaClient.PostVoteOutbound( sendHash, receipt.TxHash.Hex(), receipt.BlockNumber.Uint64(), @@ -360,7 +360,7 @@ func (ob *EVMChainClient) IsSendOutTxProcessed(sendHash string, nonce uint64, co return true, true, nil } else if receipt.Status == 0 { // the same as below events flow logger.Info().Msgf("Found (failed tx) sendHash %s on chain %s txhash %s", sendHash, ob.chain.String(), receipt.TxHash.Hex()) - zetaTxHash, ballot, err := ob.zetaClient.PostReceiveConfirmation( + zetaTxHash, ballot, err := ob.zetaClient.PostVoteOutbound( sendHash, receipt.TxHash.Hex(), receipt.BlockNumber.Uint64(), @@ -374,7 +374,7 @@ func (ob *EVMChainClient) IsSendOutTxProcessed(sendHash string, nonce uint64, co common.CoinType_Gas, ) if err != nil { - logger.Error().Err(err).Msgf("PostReceiveConfirmation error in WatchTxHashWithTimeout; zeta tx hash %s cctx %s nonce %d", zetaTxHash, sendHash, nonce) + logger.Error().Err(err).Msgf("PostVoteOutbound error in WatchTxHashWithTimeout; zeta tx hash %s cctx %s nonce %d", zetaTxHash, sendHash, nonce) } else if zetaTxHash != "" { logger.Info().Msgf("Zeta tx hash: %s cctx %s nonce %d ballot %s", zetaTxHash, sendHash, nonce, ballot) } @@ -404,7 +404,7 @@ func (ob *EVMChainClient) IsSendOutTxProcessed(sendHash string, nonce uint64, co sendhash := vLog.Topics[3].Hex() //var rxAddress string = ethcommon.HexToAddress(vLog.Topics[1].Hex()).Hex() mMint := receivedLog.ZetaValue - zetaTxHash, ballot, err := ob.zetaClient.PostReceiveConfirmation( + zetaTxHash, ballot, err := ob.zetaClient.PostVoteOutbound( sendhash, vLog.TxHash.Hex(), vLog.BlockNumber, @@ -441,7 +441,7 @@ func (ob *EVMChainClient) IsSendOutTxProcessed(sendHash string, nonce uint64, co } sendhash := vLog.Topics[2].Hex() mMint := revertedLog.RemainingZetaValue - zetaTxHash, ballot, err := ob.zetaClient.PostReceiveConfirmation( + zetaTxHash, ballot, err := ob.zetaClient.PostVoteOutbound( sendhash, vLog.TxHash.Hex(), vLog.BlockNumber, @@ -469,7 +469,7 @@ func (ob *EVMChainClient) IsSendOutTxProcessed(sendHash string, nonce uint64, co } else if receipt.Status == 0 { //FIXME: check nonce here by getTransaction RPC logger.Info().Msgf("Found (failed tx) sendHash %s on chain %s txhash %s", sendHash, ob.chain.String(), receipt.TxHash.Hex()) - zetaTxHash, ballot, err := ob.zetaClient.PostReceiveConfirmation( + zetaTxHash, ballot, err := ob.zetaClient.PostVoteOutbound( sendHash, receipt.TxHash.Hex(), receipt.BlockNumber.Uint64(), @@ -509,7 +509,7 @@ func (ob *EVMChainClient) IsSendOutTxProcessed(sendHash string, nonce uint64, co } if confHeight <= ob.GetLastBlockHeight() { logger.Info().Msg("Confirmed! Sending PostConfirmation to zetacore...") - zetaTxHash, ballot, err := ob.zetaClient.PostReceiveConfirmation( + zetaTxHash, ballot, err := ob.zetaClient.PostVoteOutbound( sendHash, vLog.TxHash.Hex(), vLog.BlockNumber, @@ -536,7 +536,7 @@ func (ob *EVMChainClient) IsSendOutTxProcessed(sendHash string, nonce uint64, co } } else { logger.Info().Msgf("Found (failed tx) sendHash %s on chain %s txhash %s", sendHash, ob.chain.String(), receipt.TxHash.Hex()) - zetaTxHash, ballot, err := ob.zetaClient.PostReceiveConfirmation( + zetaTxHash, ballot, err := ob.zetaClient.PostVoteOutbound( sendHash, receipt.TxHash.Hex(), receipt.BlockNumber.Uint64(), @@ -550,7 +550,7 @@ func (ob *EVMChainClient) IsSendOutTxProcessed(sendHash string, nonce uint64, co common.CoinType_ERC20, ) if err != nil { - logger.Error().Err(err).Msgf("PostReceiveConfirmation error in WatchTxHashWithTimeout; zeta tx hash %s", zetaTxHash) + logger.Error().Err(err).Msgf("PostVoteOutbound error in WatchTxHashWithTimeout; zeta tx hash %s", zetaTxHash) } else if zetaTxHash != "" { logger.Info().Msgf("Zeta tx hash: %s cctx %s nonce %d ballot %s", zetaTxHash, sendHash, nonce, ballot) } @@ -983,7 +983,7 @@ func (ob *EVMChainClient) observeZetaSent(startBlock, toBlock uint64) uint64 { if msg == nil { continue } - zetaHash, ballot, err := ob.zetaClient.PostSend(PostSendNonEVMGasLimit, msg) + zetaHash, ballot, err := ob.zetaClient.PostVoteInbound(PostVoteInboundGasLimit, PostVoteInboundMessagePassingExecutionGasLimit, msg) if err != nil { ob.logger.ExternalChainWatcher.Error().Err(err).Msgf( "observeZetaSent: error posting event to zeta core for tx %s at height %d for chain %d", @@ -991,7 +991,7 @@ func (ob *EVMChainClient) observeZetaSent(startBlock, toBlock uint64) uint64 { return beingScanned - 1 // we have to re-scan from this block next time } else if zetaHash != "" { ob.logger.ExternalChainWatcher.Info().Msgf( - "observeZetaSent: event detected in tx %s at height %d for chain %d, PostSend zeta tx: %s ballot %s", + "observeZetaSent: event detected in tx %s at height %d for chain %d, PostVoteInbound zeta tx: %s ballot %s", event.Raw.TxHash.Hex(), event.Raw.BlockNumber, ob.chain.ChainId, zetaHash, ballot) } } @@ -1073,7 +1073,7 @@ func (ob *EVMChainClient) observeERC20Deposited(startBlock, toBlock uint64) uint if msg == nil { continue } - zetaHash, ballot, err := ob.zetaClient.PostSend(PostSendEVMGasLimit, msg) + zetaHash, ballot, err := ob.zetaClient.PostVoteInbound(PostVoteInboundGasLimit, PostVoteInboundExecutionGasLimit, msg) if err != nil { ob.logger.ExternalChainWatcher.Error().Err(err).Msgf( "observeERC20Deposited: error posting event to zeta core for tx %s at height %d for chain %d", @@ -1081,7 +1081,7 @@ func (ob *EVMChainClient) observeERC20Deposited(startBlock, toBlock uint64) uint return beingScanned - 1 // we have to re-scan from this block next time } else if zetaHash != "" { ob.logger.ExternalChainWatcher.Info().Msgf( - "observeERC20Deposited: event detected in tx %s at height %d for chain %d, PostSend zeta tx: %s ballot %s", + "observeERC20Deposited: event detected in tx %s at height %d for chain %d, PostVoteInbound zeta tx: %s ballot %s", event.Raw.TxHash.Hex(), event.Raw.BlockNumber, ob.chain.ChainId, zetaHash, ballot) } } @@ -1145,14 +1145,14 @@ func (ob *EVMChainClient) observeTssRecvd(startBlock, toBlock uint64, flags obse if msg == nil { continue } - zetaHash, ballot, err := ob.zetaClient.PostSend(PostSendEVMGasLimit, msg) + zetaHash, ballot, err := ob.zetaClient.PostVoteInbound(PostVoteInboundGasLimit, PostVoteInboundExecutionGasLimit, msg) if err != nil { ob.logger.ExternalChainWatcher.Error().Err(err).Msgf( "observeTssRecvd: error posting to zeta core for tx %s at height %d for chain %d", tx.Hash().Hex(), bn, ob.chain.ChainId) return bn - 1 // we have to re-scan this block next time } else if zetaHash != "" { ob.logger.ExternalChainWatcher.Info().Msgf( - "observeTssRecvd: gas asset deposit detected in tx %s at height %d for chain %d, PostSend zeta tx: %s ballot %s", + "observeTssRecvd: gas asset deposit detected in tx %s at height %d for chain %d, PostVoteInbound zeta tx: %s ballot %s", tx.Hash().Hex(), bn, ob.chain.ChainId, zetaHash, ballot) } } diff --git a/zetaclient/inbound_tracker.go b/zetaclient/inbound_tracker.go index 08d749a878..fa9ca10b68 100644 --- a/zetaclient/inbound_tracker.go +++ b/zetaclient/inbound_tracker.go @@ -112,12 +112,12 @@ func (ob *BitcoinChainClient) CheckReceiptForBtcTxHash(txHash string, vote bool) if !vote { return msg.Digest(), nil } - zetaHash, ballot, err := ob.zetaClient.PostSend(PostSendEVMGasLimit, msg) + zetaHash, ballot, err := ob.zetaClient.PostVoteInbound(PostVoteInboundGasLimit, PostVoteInboundExecutionGasLimit, msg) if err != nil { ob.logger.WatchInTx.Error().Err(err).Msg("error posting to zeta core") return "", err } else if ballot == "" { - ob.logger.WatchInTx.Info().Msgf("BTC deposit detected and reported: PostSend zeta tx: %s ballot %s", zetaHash, ballot) + ob.logger.WatchInTx.Info().Msgf("BTC deposit detected and reported: PostVoteInbound zeta tx: %s ballot %s", zetaHash, ballot) } return msg.Digest(), nil } @@ -193,12 +193,12 @@ func (ob *EVMChainClient) CheckReceiptForCoinTypeZeta(txHash string, vote bool) return msg.Digest(), nil } - zetaHash, ballot, err := ob.zetaClient.PostSend(PostSendNonEVMGasLimit, msg) + zetaHash, ballot, err := ob.zetaClient.PostVoteInbound(PostVoteInboundGasLimit, PostVoteInboundMessagePassingExecutionGasLimit, msg) if err != nil { ob.logger.ExternalChainWatcher.Error().Err(err).Msg("error posting to zeta core") return "", err } else if zetaHash != "" { - ob.logger.ExternalChainWatcher.Info().Msgf("ZetaSent event detected and reported: PostSend zeta tx: %s ballot %s", zetaHash, ballot) + ob.logger.ExternalChainWatcher.Info().Msgf("ZetaSent event detected and reported: PostVoteInbound zeta tx: %s ballot %s", zetaHash, ballot) } return msg.Digest(), nil @@ -253,12 +253,12 @@ func (ob *EVMChainClient) CheckReceiptForCoinTypeERC20(txHash string, vote bool) return msg.Digest(), nil } - zetaHash, ballot, err := ob.zetaClient.PostSend(PostSendEVMGasLimit, msg) + zetaHash, ballot, err := ob.zetaClient.PostVoteInbound(PostVoteInboundGasLimit, PostVoteInboundExecutionGasLimit, msg) if err != nil { ob.logger.ExternalChainWatcher.Error().Err(err).Msg("error posting to zeta core") return "", err } else if zetaHash != "" { - ob.logger.ExternalChainWatcher.Info().Msgf("ERC20 Deposit event detected and reported: PostSend zeta tx: %s ballot %s", zetaHash, ballot) + ob.logger.ExternalChainWatcher.Info().Msgf("ERC20 Deposit event detected and reported: PostVoteInbound zeta tx: %s ballot %s", zetaHash, ballot) } return msg.Digest(), nil @@ -313,12 +313,12 @@ func (ob *EVMChainClient) CheckReceiptForCoinTypeGas(txHash string, vote bool) ( return msg.Digest(), nil } - zetaHash, ballot, err := ob.zetaClient.PostSend(PostSendEVMGasLimit, msg) + zetaHash, ballot, err := ob.zetaClient.PostVoteInbound(PostVoteInboundGasLimit, PostVoteInboundExecutionGasLimit, msg) if err != nil { ob.logger.ExternalChainWatcher.Error().Err(err).Msg("error posting to zeta core") return "", err } else if zetaHash != "" { - ob.logger.ExternalChainWatcher.Info().Msgf("Gas deposit detected and reported: PostSend zeta tx: %s ballot %s", zetaHash, ballot) + ob.logger.ExternalChainWatcher.Info().Msgf("Gas deposit detected and reported: PostVoteInbound zeta tx: %s ballot %s", zetaHash, ballot) } return msg.Digest(), nil diff --git a/zetaclient/interfaces.go b/zetaclient/interfaces.go index fe2d0824c1..6ca41f59fd 100644 --- a/zetaclient/interfaces.go +++ b/zetaclient/interfaces.go @@ -48,8 +48,8 @@ type ChainSigner interface { // ZetaCoreBridger is the interface to interact with ZetaCore type ZetaCoreBridger interface { - PostSend(zetaGasLimit uint64, msg *crosschaintypes.MsgVoteOnObservedInboundTx) (string, string, error) - PostReceiveConfirmation( + PostVoteInbound(gasLimit, retryGasLimit uint64, msg *crosschaintypes.MsgVoteOnObservedInboundTx) (string, string, error) + PostVoteOutbound( sendHash string, outTxHash string, outBlockHeight uint64, diff --git a/zetaclient/tx.go b/zetaclient/tx.go index 38a02052ed..15847c3f80 100644 --- a/zetaclient/tx.go +++ b/zetaclient/tx.go @@ -2,15 +2,10 @@ package zetaclient import ( "fmt" - "math/big" - "strings" "time" - "cosmossdk.io/math" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/authz" - "github.com/pkg/errors" - "github.com/zeta-chain/go-tss/blame" "github.com/zeta-chain/zetacore/common" "github.com/zeta-chain/zetacore/x/crosschain/types" @@ -19,55 +14,27 @@ import ( ) const ( - PostGasPriceGasLimit = 1_500_000 + // DefaultGasLimit is the default gas limit used for broadcasting txs + DefaultGasLimit = 200_000 + + // PostGasPriceGasLimit is the gas limit for voting new gas price + PostGasPriceGasLimit = 1_500_000 + + // AddTxHashToOutTxTrackerGasLimit is the gas limit for adding tx hash to out tx tracker AddTxHashToOutTxTrackerGasLimit = 200_000 - PostNonceGasLimit = 200_000 - PostSendEVMGasLimit = 4_000_000 // likely emit a lot of logs, so costly - PostSendNonEVMGasLimit = 1_000_000 - PostReceiveConfirmationGasLimit = 400_000 - PostBlameDataGasLimit = 200_000 - DefaultGasLimit = 200_000 - PostProveOutboundTxGasLimit = 400_000 - DefaultRetryCount = 5 - ExtendedRetryCount = 15 - DefaultRetryInterval = 5 -) -// GetInBoundVoteMessage returns a new MsgVoteOnObservedInboundTx -func GetInBoundVoteMessage( - sender string, - senderChain int64, - txOrigin string, - receiver string, - receiverChain int64, - amount math.Uint, - message string, - inTxHash string, - inBlockHeight uint64, - gasLimit uint64, - coinType common.CoinType, - asset string, - signerAddress string, - eventIndex uint, -) *types.MsgVoteOnObservedInboundTx { - msg := types.NewMsgVoteOnObservedInboundTx( - signerAddress, - sender, - senderChain, - txOrigin, - receiver, - receiverChain, - amount, - message, - inTxHash, - inBlockHeight, - gasLimit, - coinType, - asset, - eventIndex, - ) - return msg -} + // PostBlameDataGasLimit is the gas limit for voting on blames + PostBlameDataGasLimit = 200_000 + + // DefaultRetryCount is the number of retries for broadcasting a tx + DefaultRetryCount = 5 + + // ExtendedRetryCount is an extended number of retries for broadcasting a tx, used in keygen operations + ExtendedRetryCount = 15 + + // DefaultRetryInterval is the interval between retries in seconds + DefaultRetryInterval = 5 +) func (b *ZetaCoreBridge) WrapMessageWithAuthz(msg sdk.Msg) (sdk.Msg, AuthZSigner, error) { msgURL := sdk.MsgTypeURL(msg) @@ -126,100 +93,6 @@ func (b *ZetaCoreBridge) AddTxHashToOutTxTracker( return zetaTxHash, nil } -func (b *ZetaCoreBridge) PostSend(zetaGasLimit uint64, msg *types.MsgVoteOnObservedInboundTx) (string, string, error) { - authzMsg, authzSigner, err := b.WrapMessageWithAuthz(msg) - if err != nil { - return "", "", err - } - - // don't post send if has already voted before - ballotIndex := msg.Digest() - hasVoted, err := b.HasVoted(ballotIndex, msg.Creator) - if err != nil { - return "", ballotIndex, errors.Wrapf(err, "PostSend: unable to check if already voted for ballot %s voter %s", ballotIndex, msg.Creator) - } - if hasVoted { - return "", ballotIndex, nil - } - - for i := 0; i < DefaultRetryCount; i++ { - zetaTxHash, err := b.Broadcast(zetaGasLimit, authzMsg, authzSigner) - if err == nil { - // monitor the result of the transaction - go b.MonitorTxResult(zetaTxHash, true) - - return zetaTxHash, ballotIndex, nil - } - b.logger.Debug().Err(err).Msgf("PostSend broadcast fail | Retry count : %d", i+1) - time.Sleep(DefaultRetryInterval * time.Second) - } - return "", ballotIndex, fmt.Errorf("post send failed after %d retries", DefaultRetryInterval) -} - -func (b *ZetaCoreBridge) PostReceiveConfirmation( - sendHash string, - outTxHash string, - outBlockHeight uint64, - outTxGasUsed uint64, - outTxEffectiveGasPrice *big.Int, - outTxEffectiveGasLimit uint64, - amount *big.Int, - status common.ReceiveStatus, - chain common.Chain, - nonce uint64, - coinType common.CoinType, -) (string, string, error) { - signerAddress := b.keys.GetOperatorAddress().String() - msg := types.NewMsgVoteOnObservedOutboundTx( - signerAddress, - sendHash, - outTxHash, - outBlockHeight, - outTxGasUsed, - math.NewIntFromBigInt(outTxEffectiveGasPrice), - outTxEffectiveGasLimit, - math.NewUintFromBigInt(amount), - status, - chain.ChainId, - nonce, - coinType, - ) - - authzMsg, authzSigner, err := b.WrapMessageWithAuthz(msg) - if err != nil { - return "", "", err - } - - // don't post confirmation if has already voted before - ballotIndex := msg.Digest() - hasVoted, err := b.HasVoted(ballotIndex, msg.Creator) - if err != nil { - return "", ballotIndex, errors.Wrapf(err, "PostReceiveConfirmation: unable to check if already voted for ballot %s voter %s", ballotIndex, msg.Creator) - } - if hasVoted { - return "", ballotIndex, nil - } - - // FIXME: remove this gas limit stuff; in the special ante handler with no gas limit, add - // NewMsgReceiveConfirmation to it. - var gasLimit uint64 = PostReceiveConfirmationGasLimit - if status == common.ReceiveStatus_Failed { - gasLimit = PostSendEVMGasLimit - } - for i := 0; i < DefaultRetryCount; i++ { - zetaTxHash, err := b.Broadcast(gasLimit, authzMsg, authzSigner) - if err == nil { - // monitor the result of the transaction - go b.MonitorTxResult(zetaTxHash, true) - - return zetaTxHash, ballotIndex, nil - } - b.logger.Debug().Err(err).Msgf("PostReceive broadcast fail | Retry count : %d", i+1) - time.Sleep(DefaultRetryInterval * time.Second) - } - return "", ballotIndex, fmt.Errorf("post receive failed after %d retries", DefaultRetryCount) -} - func (b *ZetaCoreBridge) SetTSS(tssPubkey string, keyGenZetaHeight int64, status common.ReceiveStatus) (string, error) { signerAddress := b.keys.GetOperatorAddress().String() msg := types.NewMsgCreateTSSVoter(signerAddress, tssPubkey, keyGenZetaHeight, status) @@ -308,40 +181,3 @@ func (b *ZetaCoreBridge) PostAddBlockHeader(chainID int64, blockHash []byte, hei } return "", fmt.Errorf("post add block header failed after %d retries", DefaultRetryCount) } - -// MonitorTxResult monitors the result of a tx (used for inbound and outbound vote txs) -func (b *ZetaCoreBridge) MonitorTxResult(zetaTxHash string, isInbound bool) { - var lastErr error - ticker := 5 * time.Second - retry := 10 - prefix := "MonitorOutboundTxResult" - if isInbound { - prefix = "MonitorInboundTxResult" - } - - for i := 0; i < retry; i++ { - time.Sleep(ticker) - txResult, err := b.QueryTxResult(zetaTxHash) - if err == nil { - // the inbound vote tx shouldn't fail to execute - if strings.Contains(txResult.RawLog, "failed to execute message") { - b.logger.Error().Msgf( - "%s: failed to execute vote, txHash: %s, txResult %s", - prefix, - zetaTxHash, - txResult.String(), - ) - } - return - } - lastErr = err - } - - b.logger.Error().Err(lastErr).Msgf( - "%s: unable to query tx result for txHash %s, err %s", - prefix, - zetaTxHash, - lastErr.Error(), - ) - return -} diff --git a/zetaclient/tx_vote_inbound.go b/zetaclient/tx_vote_inbound.go new file mode 100644 index 0000000000..1e5807474f --- /dev/null +++ b/zetaclient/tx_vote_inbound.go @@ -0,0 +1,153 @@ +package zetaclient + +import ( + "fmt" + "strings" + "time" + + "cosmossdk.io/math" + "github.com/zeta-chain/zetacore/common" + + "github.com/pkg/errors" + + "github.com/zeta-chain/zetacore/x/crosschain/types" +) + +const ( + // PostVoteInboundGasLimit is the gas limit for voting on observed inbound tx + PostVoteInboundGasLimit = 400_000 + + // PostVoteInboundExecutionGasLimit is the gas limit for voting on observed inbound tx and executing it + PostVoteInboundExecutionGasLimit = 4_000_000 + + // PostVoteInboundMessagePassingExecutionGasLimit is the gas limit for voting on, and executing ,observed inbound tx related to message passing (coin_type == zeta) + PostVoteInboundMessagePassingExecutionGasLimit = 1_000_000 + + // MonitorVoteInboundTxResultInterval is the interval between retries for monitoring tx result in seconds + MonitorVoteInboundTxResultInterval = 5 + + // MonitorVoteInboundTxResultRetryCount is the number of retries to fetch monitoring tx result + MonitorVoteInboundTxResultRetryCount = 20 +) + +// GetInBoundVoteMessage returns a new MsgVoteOnObservedInboundTx +func GetInBoundVoteMessage( + sender string, + senderChain int64, + txOrigin string, + receiver string, + receiverChain int64, + amount math.Uint, + message string, + inTxHash string, + inBlockHeight uint64, + gasLimit uint64, + coinType common.CoinType, + asset string, + signerAddress string, + eventIndex uint, +) *types.MsgVoteOnObservedInboundTx { + msg := types.NewMsgVoteOnObservedInboundTx( + signerAddress, + sender, + senderChain, + txOrigin, + receiver, + receiverChain, + amount, + message, + inTxHash, + inBlockHeight, + gasLimit, + coinType, + asset, + eventIndex, + ) + return msg +} + +// PostVoteInbound posts a vote on an observed inbound tx +// retryGasLimit is the gas limit used to resend the tx if it fails because of insufficient gas +// it is used when the ballot is finalized and the inbound tx needs to be processed +func (b *ZetaCoreBridge) PostVoteInbound(gasLimit, retryGasLimit uint64, msg *types.MsgVoteOnObservedInboundTx) (string, string, error) { + authzMsg, authzSigner, err := b.WrapMessageWithAuthz(msg) + if err != nil { + return "", "", err + } + + // don't post send if has already voted before + ballotIndex := msg.Digest() + hasVoted, err := b.HasVoted(ballotIndex, msg.Creator) + if err != nil { + return "", ballotIndex, errors.Wrapf(err, "PostVoteInbound: unable to check if already voted for ballot %s voter %s", ballotIndex, msg.Creator) + } + if hasVoted { + return "", ballotIndex, nil + } + + for i := 0; i < DefaultRetryCount; i++ { + zetaTxHash, err := b.Broadcast(gasLimit, authzMsg, authzSigner) + if err == nil { + // monitor the result of the transaction and resend if necessary + go b.MonitorVoteInboundTxResult(zetaTxHash, retryGasLimit, msg) + + return zetaTxHash, ballotIndex, nil + } + b.logger.Debug().Err(err).Msgf("PostVoteInbound broadcast fail | Retry count : %d", i+1) + time.Sleep(DefaultRetryInterval * time.Second) + } + return "", ballotIndex, fmt.Errorf("post send failed after %d retries", DefaultRetryInterval) +} + +// MonitorVoteInboundTxResult monitors the result of a vote inbound tx +// retryGasLimit is the gas limit used to resend the tx if it fails because of insufficient gas +// if retryGasLimit is 0, the tx is not resent +func (b *ZetaCoreBridge) MonitorVoteInboundTxResult(zetaTxHash string, retryGasLimit uint64, msg *types.MsgVoteOnObservedInboundTx) { + var lastErr error + + for i := 0; i < MonitorVoteInboundTxResultRetryCount; i++ { + time.Sleep(MonitorVoteInboundTxResultInterval * time.Second) + + // query tx result from ZetaChain + txResult, err := b.QueryTxResult(zetaTxHash) + + if err == nil { + if strings.Contains(txResult.RawLog, "failed to execute message") { + // the inbound vote tx shouldn't fail to execute + // this shouldn't happen + b.logger.Error().Msgf( + "MonitorInboundTxResult: failed to execute vote, txHash: %s, log %s", zetaTxHash, txResult.RawLog, + ) + } else if strings.Contains(txResult.RawLog, "out of gas") { + // if the tx fails with an out of gas error, resend the tx with more gas if retryGasLimit > 0 + b.logger.Debug().Msgf( + "MonitorInboundTxResult: out of gas, txHash: %s, log %s", zetaTxHash, txResult.RawLog, + ) + if retryGasLimit > 0 { + // new retryGasLimit set to 0 to prevent reentering this function + _, _, err := b.PostVoteInbound(retryGasLimit, 0, msg) + if err != nil { + b.logger.Error().Err(err).Msgf( + "MonitorInboundTxResult: failed to resend tx, txHash: %s, log %s", zetaTxHash, txResult.RawLog, + ) + } else { + b.logger.Info().Msgf( + "MonitorInboundTxResult: successfully resent tx, txHash: %s, log %s", zetaTxHash, txResult.RawLog, + ) + } + } + } else { + b.logger.Debug().Msgf( + "MonitorInboundTxResult: successful txHash %s, log %s", zetaTxHash, txResult.RawLog, + ) + } + return + } + lastErr = err + } + + b.logger.Error().Err(lastErr).Msgf( + "MonitorInboundTxResult: unable to query tx result for txHash %s, err %s", zetaTxHash, lastErr.Error(), + ) + return +} diff --git a/zetaclient/tx_vote_outbound.go b/zetaclient/tx_vote_outbound.go new file mode 100644 index 0000000000..bcf79c69f6 --- /dev/null +++ b/zetaclient/tx_vote_outbound.go @@ -0,0 +1,154 @@ +package zetaclient + +import ( + "fmt" + "math/big" + "strings" + "time" + + "cosmossdk.io/math" + "github.com/pkg/errors" + "github.com/zeta-chain/zetacore/common" + "github.com/zeta-chain/zetacore/x/crosschain/types" +) + +const ( + // PostVoteOutboundGasLimit is the gas limit for voting on observed outbound tx + PostVoteOutboundGasLimit = 400_000 + + // PostVoteOutboundRevertGasLimit is the gas limit for voting on observed outbound tx for revert (when outbound fails) + // The value needs to be higher because reverting implies interacting with the EVM to perform swaps for the gas token + PostVoteOutboundRevertGasLimit = 1_500_000 + + // MonitorVoteOutboundTxResultInterval is the interval between retries for monitoring tx result in seconds + MonitorVoteOutboundTxResultInterval = 5 + + // MonitorVoteOutboundTxResultRetryCount is the number of retries to fetch monitoring tx result + MonitorVoteOutboundTxResultRetryCount = 20 +) + +// PostVoteOutbound posts a vote on an observed outbound tx +func (b *ZetaCoreBridge) PostVoteOutbound( + sendHash string, + outTxHash string, + outBlockHeight uint64, + outTxGasUsed uint64, + outTxEffectiveGasPrice *big.Int, + outTxEffectiveGasLimit uint64, + amount *big.Int, + status common.ReceiveStatus, + chain common.Chain, + nonce uint64, + coinType common.CoinType, +) (string, string, error) { + signerAddress := b.keys.GetOperatorAddress().String() + msg := types.NewMsgVoteOnObservedOutboundTx( + signerAddress, + sendHash, + outTxHash, + outBlockHeight, + outTxGasUsed, + math.NewIntFromBigInt(outTxEffectiveGasPrice), + outTxEffectiveGasLimit, + math.NewUintFromBigInt(amount), + status, + chain.ChainId, + nonce, + coinType, + ) + + // when an outbound fails and a revert is required, the gas limit needs to be higher + // this is because the revert tx needs to interact with the EVM to perform swaps for the gas token + // the higher gas limit is only necessary when the vote is finalized and the outbound is processed + // therefore we use a retryGasLimit with a higher value to resend the tx if it fails (when the vote is finalized) + retryGasLimit := uint64(0) + if msg.Status == common.ReceiveStatus_Failed { + retryGasLimit = PostVoteOutboundRevertGasLimit + } + + return b.PostVoteOutboundFromMsg(PostVoteOutboundGasLimit, retryGasLimit, msg) +} + +// PostVoteOutboundFromMsg posts a vote on an observed outbound tx from a MsgVoteOnObservedOutboundTx +func (b *ZetaCoreBridge) PostVoteOutboundFromMsg(gasLimit, retryGasLimit uint64, msg *types.MsgVoteOnObservedOutboundTx) (string, string, error) { + authzMsg, authzSigner, err := b.WrapMessageWithAuthz(msg) + if err != nil { + return "", "", err + } + + // don't post confirmation if has already voted before + ballotIndex := msg.Digest() + hasVoted, err := b.HasVoted(ballotIndex, msg.Creator) + if err != nil { + return "", ballotIndex, errors.Wrapf(err, "PostVoteOutbound: unable to check if already voted for ballot %s voter %s", ballotIndex, msg.Creator) + } + if hasVoted { + return "", ballotIndex, nil + } + for i := 0; i < DefaultRetryCount; i++ { + zetaTxHash, err := b.Broadcast(gasLimit, authzMsg, authzSigner) + if err == nil { + // monitor the result of the transaction and resend if necessary + go b.MonitorVoteOutboundTxResult(zetaTxHash, retryGasLimit, msg) + + return zetaTxHash, ballotIndex, nil + } + b.logger.Debug().Err(err).Msgf("PostVoteOutbound broadcast fail | Retry count : %d", i+1) + time.Sleep(DefaultRetryInterval * time.Second) + } + return "", ballotIndex, fmt.Errorf("post receive failed after %d retries", DefaultRetryCount) +} + +// MonitorVoteOutboundTxResult monitors the result of a vote outbound tx +// retryGasLimit is the gas limit used to resend the tx if it fails because of insufficient gas +// if retryGasLimit is 0, the tx is not resent +func (b *ZetaCoreBridge) MonitorVoteOutboundTxResult(zetaTxHash string, retryGasLimit uint64, msg *types.MsgVoteOnObservedOutboundTx) { + var lastErr error + + for i := 0; i < MonitorVoteOutboundTxResultRetryCount; i++ { + time.Sleep(MonitorVoteOutboundTxResultInterval * time.Second) + + // query tx result from ZetaChain + txResult, err := b.QueryTxResult(zetaTxHash) + + if err == nil { + if strings.Contains(txResult.RawLog, "failed to execute message") { + // the inbound vote tx shouldn't fail to execute + // this shouldn't happen + b.logger.Error().Msgf( + "MonitorVoteOutboundTxResult: failed to execute vote, txHash: %s, log %s", zetaTxHash, txResult.RawLog, + ) + } else if strings.Contains(txResult.RawLog, "out of gas") { + // if the tx fails with an out of gas error, resend the tx with more gas if retryGasLimit > 0 + b.logger.Debug().Msgf( + "MonitorVoteOutboundTxResult: out of gas, txHash: %s, log %s", zetaTxHash, txResult.RawLog, + ) + if retryGasLimit > 0 { + // new retryGasLimit set to 0 to prevent reentering this function + _, _, err := b.PostVoteOutboundFromMsg(retryGasLimit, 0, msg) + + if err != nil { + b.logger.Error().Err(err).Msgf( + "MonitorVoteOutboundTxResult: failed to resend tx, txHash: %s, log %s", zetaTxHash, txResult.RawLog, + ) + } else { + b.logger.Info().Msgf( + "MonitorVoteOutboundTxResult: successfully resent tx, txHash: %s, log %s", zetaTxHash, txResult.RawLog, + ) + } + } + } else { + b.logger.Debug().Msgf( + "MonitorVoteOutboundTxResult: successful txHash %s, log %s", zetaTxHash, txResult.RawLog, + ) + } + return + } + lastErr = err + } + + b.logger.Error().Err(lastErr).Msgf( + "MonitorVoteOutboundTxResult: unable to query tx result for txHash %s, err %s", zetaTxHash, lastErr.Error(), + ) + return +} diff --git a/zetaclient/voter_test.go b/zetaclient/voter_test.go index 5df01de806..853c17eea9 100644 --- a/zetaclient/voter_test.go +++ b/zetaclient/voter_test.go @@ -85,20 +85,20 @@ func (s *VoterSuite) SetUpTest(c *C) { func (s *VoterSuite) TestSendVoter(c *C) { b1 := s.bridge1 b2 := s.bridge2 - metaHash, err := b1.PostSend("0xfrom", "Ethereum", "0xfrom", "0xto", "BSC", "123456", "23245", "little message", + metaHash, err := b1.PostVoteInbound("0xfrom", "Ethereum", "0xfrom", "0xto", "BSC", "123456", "23245", "little message", "0xtxhash", 123123, "0xtoken") c.Assert(err, IsNil) - log.Info().Msgf("PostSend metaHash %s", metaHash) + log.Info().Msgf("PostVoteInbound metaHash %s", metaHash) // wait for the next block timer1 := time.NewTimer(2 * time.Second) <-timer1.C - metaHash, err = b2.PostSend("0xfrom", "Ethereum", "0xfrom", "0xto", "BSC", "123456", "23245", "little message", + metaHash, err = b2.PostVoteInbound("0xfrom", "Ethereum", "0xfrom", "0xto", "BSC", "123456", "23245", "little message", "0xtxhash", 123123, "0xtoken") c.Assert(err, IsNil) - log.Info().Msgf("Second PostSend metaHash %s", metaHash) + log.Info().Msgf("Second PostVoteInbound metaHash %s", metaHash) // wait for the next block timer2 := time.NewTimer(2 * time.Second) @@ -111,13 +111,13 @@ func (s *VoterSuite) TestSendVoter(c *C) { send := sends[0] - metaHash, err = b1.PostReceiveConfirmation(send.Index, "0xoutHash", 2123, "23245") + metaHash, err = b1.PostVoteOutbound(send.Index, "0xoutHash", 2123, "23245") c.Assert(err, IsNil) timer3 := time.NewTimer(2 * time.Second) <-timer3.C - metaHash, err = b2.PostReceiveConfirmation(send.Index, "0xoutHash", 2123, "23245") + metaHash, err = b2.PostVoteOutbound(send.Index, "0xoutHash", 2123, "23245") c.Assert(err, IsNil) receives, err := b2.GetAllReceive() diff --git a/zetaclient/zetacore_observer_test.go b/zetaclient/zetacore_observer_test.go index afa8114764..919450d1cb 100644 --- a/zetaclient/zetacore_observer_test.go +++ b/zetaclient/zetacore_observer_test.go @@ -117,18 +117,18 @@ func (s *COSuite) SetUpTest(c *C) { func (s *COSuite) TestSendFlow(c *C) { b1 := s.bridge1 b2 := s.bridge2 - metaHash, err := b1.PostSend(TEST_SENDER, "Ethereum", TEST_SENDER, TEST_RECEIVER, "BSC", "1337", "0", "treat or trick", + metaHash, err := b1.PostVoteInbound(TEST_SENDER, "Ethereum", TEST_SENDER, TEST_RECEIVER, "BSC", "1337", "0", "treat or trick", "0xtxhash", 123123, "0xtoken") c.Assert(err, IsNil) - c.Logf("PostSend metaHash %s", metaHash) + c.Logf("PostVoteInbound metaHash %s", metaHash) timer1 := time.NewTimer(2 * time.Second) <-timer1.C - metaHash, err = b2.PostSend(TEST_SENDER, "Ethereum", TEST_SENDER, TEST_RECEIVER, "BSC", "1337", "0", "treat or trick", + metaHash, err = b2.PostVoteInbound(TEST_SENDER, "Ethereum", TEST_SENDER, TEST_RECEIVER, "BSC", "1337", "0", "treat or trick", "0xtxhash", 123123, "0xtoken") c.Assert(err, IsNil) - c.Logf("Second PostSend metaHash %s", metaHash) + c.Logf("Second PostVoteInbound metaHash %s", metaHash) timer2 := time.NewTimer(2 * time.Second) <-timer2.C