From abb98e9511b2627e10f0d6d61c526242fc93cbbe Mon Sep 17 00:00:00 2001 From: Charlie Chen Date: Wed, 21 Feb 2024 13:21:37 -0600 Subject: [PATCH 01/13] initiated feature of zetaclient-banned-address --- cmd/zetaclientd/main.go | 47 +++++++++- cmd/zetaclientd/start.go | 18 ++-- cmd/zetaclientd/utils.go | 22 ++--- common/utils.go | 23 +++++ x/crosschain/keeper/evm_deposit.go | 25 +----- zetaclient/bitcoin/bitcoin_client.go | 80 ++++++++++------- zetaclient/bitcoin/bitcoin_client_rpc_test.go | 3 +- zetaclient/bitcoin/bitcoin_client_test.go | 8 +- zetaclient/bitcoin/bitcoin_signer.go | 39 +++++--- zetaclient/bitcoin/bitcoin_signer_test.go | 4 +- zetaclient/bitcoin/inbound_tracker.go | 3 + zetaclient/common/utils.go | 26 ++++++ zetaclient/common/utils_test.go | 47 ++++++++++ zetaclient/config/config.go | 19 ++++ zetaclient/config/types.go | 20 +++++ zetaclient/evm/evm_client.go | 52 ++++++++--- zetaclient/evm/evm_signer.go | 40 +++++---- zetaclient/evm/inbounds.go | 55 ++++++++++-- zetaclient/evm/inbounds_test.go | 88 +++++++++++++++++++ zetaclient/interfaces/interfaces.go | 4 +- .../btc/chain_8332_block_trimmed_828440.json} | 0 ...hain_8332_mempool.space_block_828440.json} | 0 zetaclient/testdata/cctx/cctx_1_6270.json | 34 +++++++ ...ffc40ca898e134525c42c2ae3cbc5725f9d76.json | 60 +++++++++++++ zetaclient/testutils/utils.go | 18 +++- zetaclient/zetacore_observer.go | 4 +- 26 files changed, 606 insertions(+), 133 deletions(-) create mode 100644 zetaclient/common/utils.go create mode 100644 zetaclient/common/utils_test.go create mode 100644 zetaclient/evm/inbounds_test.go rename zetaclient/{bitcoin/testdata/bitcoin_block_trimmed_828440.json => testdata/btc/chain_8332_block_trimmed_828440.json} (100%) rename zetaclient/{bitcoin/testdata/mempool.space_block_828440.json => testdata/btc/chain_8332_mempool.space_block_828440.json} (100%) create mode 100644 zetaclient/testdata/cctx/cctx_1_6270.json create mode 100644 zetaclient/testdata/evm/chain_1_receipt_ZetaSent_0xf3935200c80f98502d5edc7e871ffc40ca898e134525c42c2ae3cbc5725f9d76.json diff --git a/cmd/zetaclientd/main.go b/cmd/zetaclientd/main.go index a51fb40d82..5fea730b1e 100644 --- a/cmd/zetaclientd/main.go +++ b/cmd/zetaclientd/main.go @@ -1,10 +1,15 @@ package main import ( + "path/filepath" + + "github.com/rs/zerolog/log" + ecdsakeygen "github.com/binance-chain/tss-lib/ecdsa/keygen" "github.com/cosmos/cosmos-sdk/server" svrcmd "github.com/cosmos/cosmos-sdk/server/cmd" "github.com/rs/zerolog" + clientcommon "github.com/zeta-chain/zetacore/zetaclient/common" "github.com/zeta-chain/zetacore/zetaclient/config" "github.com/zeta-chain/zetacore/cmd" @@ -20,6 +25,10 @@ import ( "github.com/zeta-chain/zetacore/app" ) +const ( + ComplianceLogFile = "compliance.log" +) + var ( preParams *ecdsakeygen.LocalPreParams ) @@ -51,19 +60,53 @@ func SetupConfigForTest() { } -func InitLogger(cfg *config.Config) zerolog.Logger { +func InitLogger(cfg *config.Config) (clientcommon.ClientLogger, error) { + // open compliance log file + file, err := OpenComplianceLogFile(cfg) + if err != nil { + return clientcommon.DefaultLoggers(), err + } + var logger zerolog.Logger + var loggerCompliance zerolog.Logger switch cfg.LogFormat { case "json": logger = zerolog.New(os.Stdout).Level(zerolog.Level(cfg.LogLevel)).With().Timestamp().Logger() + loggerCompliance = zerolog.New(file).Level(zerolog.Level(cfg.LogLevel)).With().Timestamp().Logger() case "text": logger = zerolog.New(zerolog.ConsoleWriter{Out: os.Stdout, TimeFormat: time.RFC3339}).Level(zerolog.Level(cfg.LogLevel)).With().Timestamp().Logger() + loggerCompliance = zerolog.New(file).Level(zerolog.Level(cfg.LogLevel)).With().Timestamp().Logger() default: logger = zerolog.New(zerolog.ConsoleWriter{Out: os.Stdout, TimeFormat: time.RFC3339}) + loggerCompliance = zerolog.New(file).With().Timestamp().Logger() } if cfg.LogSampler { logger = logger.Sample(&zerolog.BasicSampler{N: 5}) } - return logger + log.Logger = logger // set global logger + + return clientcommon.ClientLogger{ + Std: log.Logger, + Compliance: loggerCompliance, + }, nil +} + +func OpenComplianceLogFile(cfg *config.Config) (*os.File, error) { + // use zetacore home as default + logPath := cfg.ZetaCoreHome + if cfg.ComplianceConfig != nil && cfg.ComplianceConfig.LogPath != "" { + logPath = cfg.ComplianceConfig.LogPath + } + + // clean file name + name := filepath.Join(logPath, ComplianceLogFile) + name, err := filepath.Abs(name) + if err != nil { + return nil, err + } + name = filepath.Clean(name) + + // open (or create) compliance log file + return os.OpenFile(name, os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0644) } diff --git a/cmd/zetaclientd/start.go b/cmd/zetaclientd/start.go index e22d86b356..3225e80160 100644 --- a/cmd/zetaclientd/start.go +++ b/cmd/zetaclientd/start.go @@ -57,7 +57,12 @@ func start(_ *cobra.Command, _ []string) error { if err != nil { return err } - log.Logger = InitLogger(cfg) + loggers, err := InitLogger(cfg) + if err != nil { + log.Error().Err(err).Msg("InitLogger failed") + return err + } + //Wait until zetacore has started if len(cfg.Peer) != 0 { err := validatePeer(cfg.Peer) @@ -67,9 +72,10 @@ func start(_ *cobra.Command, _ []string) error { } } - masterLogger := log.Logger + masterLogger := loggers.Std startLogger := masterLogger.With().Str("module", "startup").Logger() + // Wait until zetacore is up waitForZetaCore(cfg, startLogger) startLogger.Info().Msgf("ZetaCore is ready , Trying to connect to %s", cfg.Peer) @@ -218,8 +224,8 @@ func start(_ *cobra.Command, _ []string) error { } } - // CreateSignerMap: This creates a map of all signers for each chain . Each signer is responsible for signing transactions for a particular chain - signerMap, err := CreateSignerMap(tss, masterLogger, cfg, telemetryServer) + // CreateSignerMap: This creates a map of all signers for each chain. Each signer is responsible for signing transactions for a particular chain + signerMap, err := CreateSignerMap(tss, loggers, cfg, telemetryServer) if err != nil { log.Error().Err(err).Msg("CreateSignerMap") return err @@ -232,8 +238,8 @@ func start(_ *cobra.Command, _ []string) error { } dbpath := filepath.Join(userDir, ".zetaclient/chainobserver") - // CreateChainClientMap : This creates a map of all chain clients . Each chain client is responsible for listening to events on the chain and processing them - chainClientMap, err := CreateChainClientMap(zetaBridge, tss, dbpath, metrics, masterLogger, cfg, telemetryServer) + // CreateChainClientMap : This creates a map of all chain clients. Each chain client is responsible for listening to events on the chain and processing them + chainClientMap, err := CreateChainClientMap(zetaBridge, tss, dbpath, metrics, loggers, cfg, telemetryServer) if err != nil { startLogger.Err(err).Msg("CreateSignerMap") return err diff --git a/cmd/zetaclientd/utils.go b/cmd/zetaclientd/utils.go index 87c009c11b..c520440162 100644 --- a/cmd/zetaclientd/utils.go +++ b/cmd/zetaclientd/utils.go @@ -3,11 +3,11 @@ package main import ( sdk "github.com/cosmos/cosmos-sdk/types" ethcommon "github.com/ethereum/go-ethereum/common" - "github.com/rs/zerolog" "github.com/zeta-chain/zetacore/common" "github.com/zeta-chain/zetacore/common/cosmos" "github.com/zeta-chain/zetacore/zetaclient/authz" "github.com/zeta-chain/zetacore/zetaclient/bitcoin" + clientcommon "github.com/zeta-chain/zetacore/zetaclient/common" "github.com/zeta-chain/zetacore/zetaclient/config" "github.com/zeta-chain/zetacore/zetaclient/interfaces" "github.com/zeta-chain/zetacore/zetaclient/keys" @@ -51,7 +51,7 @@ func CreateZetaBridge(cfg *config.Config, telemetry *metrics.TelemetryServer, ho func CreateSignerMap( tss interfaces.TSSSigner, - logger zerolog.Logger, + loggers clientcommon.ClientLogger, cfg *config.Config, ts *metrics.TelemetryServer, ) (map[common.Chain]interfaces.ChainSigner, error) { @@ -63,9 +63,9 @@ func CreateSignerMap( } mpiAddress := ethcommon.HexToAddress(evmConfig.ChainParams.ConnectorContractAddress) erc20CustodyAddress := ethcommon.HexToAddress(evmConfig.ChainParams.Erc20CustodyContractAddress) - signer, err := evm.NewEVMSigner(evmConfig.Chain, evmConfig.Endpoint, tss, config.GetConnectorABI(), config.GetERC20CustodyABI(), mpiAddress, erc20CustodyAddress, logger, ts) + signer, err := evm.NewEVMSigner(evmConfig.Chain, evmConfig.Endpoint, tss, config.GetConnectorABI(), config.GetERC20CustodyABI(), mpiAddress, erc20CustodyAddress, loggers, ts) if err != nil { - logger.Error().Err(err).Msgf("NewEVMSigner error for chain %s", evmConfig.Chain.String()) + loggers.Std.Error().Err(err).Msgf("NewEVMSigner error for chain %s", evmConfig.Chain.String()) continue } signerMap[evmConfig.Chain] = signer @@ -73,9 +73,9 @@ func CreateSignerMap( // BTC signer btcChain, btcConfig, enabled := cfg.GetBTCConfig() if enabled { - signer, err := bitcoin.NewBTCSigner(btcConfig, tss, logger, ts) + signer, err := bitcoin.NewBTCSigner(btcConfig, tss, loggers, ts) if err != nil { - logger.Error().Err(err).Msgf("NewBTCSigner error for chain %s", btcChain.String()) + loggers.Std.Error().Err(err).Msgf("NewBTCSigner error for chain %s", btcChain.String()) } else { signerMap[btcChain] = signer } @@ -89,7 +89,7 @@ func CreateChainClientMap( tss interfaces.TSSSigner, dbpath string, metrics *metrics.Metrics, - logger zerolog.Logger, + loggers clientcommon.ClientLogger, cfg *config.Config, ts *metrics.TelemetryServer, ) (map[common.Chain]interfaces.ChainClient, error) { @@ -99,9 +99,9 @@ func CreateChainClientMap( if evmConfig.Chain.IsZetaChain() { continue } - co, err := evm.NewEVMChainClient(bridge, tss, dbpath, metrics, logger, cfg, *evmConfig, ts) + co, err := evm.NewEVMChainClient(bridge, tss, dbpath, metrics, loggers, cfg, *evmConfig, ts) if err != nil { - logger.Error().Err(err).Msgf("NewEVMChainClient error for chain %s", evmConfig.Chain.String()) + loggers.Std.Error().Err(err).Msgf("NewEVMChainClient error for chain %s", evmConfig.Chain.String()) continue } clientMap[evmConfig.Chain] = co @@ -109,9 +109,9 @@ func CreateChainClientMap( // BTC client btcChain, btcConfig, enabled := cfg.GetBTCConfig() if enabled { - co, err := bitcoin.NewBitcoinClient(btcChain, bridge, tss, dbpath, metrics, logger, btcConfig, ts) + co, err := bitcoin.NewBitcoinClient(btcChain, bridge, tss, dbpath, metrics, loggers, btcConfig, ts) if err != nil { - logger.Error().Err(err).Msgf("NewBitcoinClient error for chain %s", btcChain.String()) + loggers.Std.Error().Err(err).Msgf("NewBitcoinClient error for chain %s", btcChain.String()) } else { clientMap[btcChain] = co diff --git a/common/utils.go b/common/utils.go index 773c709210..62c4e27ad5 100644 --- a/common/utils.go +++ b/common/utils.go @@ -41,3 +41,26 @@ func StringToHash(chainID int64, hash string) ([]byte, error) { } return nil, fmt.Errorf("cannot convert hash to bytes for chain %d", chainID) } + +// ParseAddressAndData parses the message string into an address and data +// message is hex encoded byte array +// [ contractAddress calldata ] +// [ 20B, variable] +func ParseAddressAndData(message string) (ethcommon.Address, []byte, error) { + if len(message) == 0 { + return ethcommon.Address{}, nil, nil + } + + data, err := hex.DecodeString(message) + if err != nil { + return ethcommon.Address{}, nil, fmt.Errorf("message should be a hex encoded string: " + err.Error()) + } + + if len(data) < 20 { + return ethcommon.Address{}, data, nil + } + + address := ethcommon.BytesToAddress(data[:20]) + data = data[20:] + return address, data, nil +} diff --git a/x/crosschain/keeper/evm_deposit.go b/x/crosschain/keeper/evm_deposit.go index c33adc8420..50d8277f15 100644 --- a/x/crosschain/keeper/evm_deposit.go +++ b/x/crosschain/keeper/evm_deposit.go @@ -43,7 +43,7 @@ func (k Keeper) HandleEVMDeposit( } } else { // cointype is Gas or ERC20; then it could be a ZRC20 deposit/depositAndCall cctx. - parsedAddress, data, err := parseAddressAndData(msg.Message) + parsedAddress, data, err := common.ParseAddressAndData(msg.Message) if err != nil { return false, errors.Wrap(types.ErrUnableToParseAddress, err.Error()) } @@ -110,26 +110,3 @@ func errShouldRevertCctx(err error) bool { errors.Is(err, fungibletypes.ErrCallNonContract) || errors.Is(err, fungibletypes.ErrPausedZRC20) } - -// parseAddressAndData parses the message string into an address and data -// message is hex encoded byte array -// [ contractAddress calldata ] -// [ 20B, variable] -func parseAddressAndData(message string) (ethcommon.Address, []byte, error) { - if len(message) == 0 { - return ethcommon.Address{}, nil, nil - } - - data, err := hex.DecodeString(message) - if err != nil { - return ethcommon.Address{}, nil, fmt.Errorf("message should be a hex encoded string: " + err.Error()) - } - - if len(data) < 20 { - return ethcommon.Address{}, data, nil - } - - address := ethcommon.BytesToAddress(data[:20]) - data = data[20:] - return address, data, nil -} diff --git a/zetaclient/bitcoin/bitcoin_client.go b/zetaclient/bitcoin/bitcoin_client.go index b0e409b756..0bb741a0e1 100644 --- a/zetaclient/bitcoin/bitcoin_client.go +++ b/zetaclient/bitcoin/bitcoin_client.go @@ -29,6 +29,7 @@ import ( "github.com/zeta-chain/zetacore/common" "github.com/zeta-chain/zetacore/x/crosschain/types" observertypes "github.com/zeta-chain/zetacore/x/observer/types" + clientcommon "github.com/zeta-chain/zetacore/zetaclient/common" "github.com/zeta-chain/zetacore/zetaclient/config" metricsPkg "github.com/zeta-chain/zetacore/zetaclient/metrics" clienttypes "github.com/zeta-chain/zetacore/zetaclient/types" @@ -49,6 +50,7 @@ type BTCLog struct { ObserveOutTx zerolog.Logger WatchUTXOS zerolog.Logger WatchGasPrice zerolog.Logger + Compliance zerolog.Logger } // BTCChainClient represents a chain configuration for Bitcoin @@ -137,7 +139,7 @@ func NewBitcoinClient( tss interfaces.TSSSigner, dbpath string, metrics *metricsPkg.Metrics, - logger zerolog.Logger, + loggers clientcommon.ClientLogger, btcCfg config.BTCConfig, ts *metricsPkg.TelemetryServer, ) (*BTCChainClient, error) { @@ -153,13 +155,14 @@ func NewBitcoinClient( } ob.netParams = netParams ob.Mu = &sync.Mutex{} - chainLogger := logger.With().Str("chain", chain.ChainName.String()).Logger() + chainLogger := loggers.Std.With().Str("chain", chain.ChainName.String()).Logger() ob.logger = BTCLog{ ChainLogger: chainLogger, WatchInTx: chainLogger.With().Str("module", "WatchInTx").Logger(), ObserveOutTx: chainLogger.With().Str("module", "observeOutTx").Logger(), WatchUTXOS: chainLogger.With().Str("module", "WatchUTXOS").Logger(), WatchGasPrice: chainLogger.With().Str("module", "WatchGasPrice").Logger(), + Compliance: loggers.Compliance, } ob.zetaClient = bridge @@ -459,13 +462,15 @@ func (ob *BTCChainClient) observeInTx() error { // post inbound vote message to zetacore for _, inTx := range inTxs { msg := ob.GetInboundVoteMessageFromBtcEvent(inTx) - zetaHash, ballot, err := ob.zetaClient.PostVoteInbound(zetabridge.PostVoteInboundGasLimit, zetabridge.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: PostVoteInbound zeta tx hash: %s inTx %s ballot %s fee %v", - zetaHash, inTx.TxHash, ballot, depositorFee) + if msg != nil { + zetaHash, ballot, err := ob.zetaClient.PostVoteInbound(zetabridge.PostVoteInboundGasLimit, zetabridge.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: PostVoteInbound zeta tx hash: %s inTx %s ballot %s fee %v", + zetaHash, inTx.TxHash, ballot, depositorFee) + } } } } @@ -494,7 +499,12 @@ func (ob *BTCChainClient) ConfirmationsThreshold(amount *big.Int) int64 { } // IsSendOutTxProcessed returns isIncluded(or inMempool), isConfirmed, Error -func (ob *BTCChainClient) IsSendOutTxProcessed(sendHash string, nonce uint64, _ common.CoinType, logger zerolog.Logger) (bool, bool, error) { +func (ob *BTCChainClient) IsSendOutTxProcessed(cctx *types.CrossChainTx, logger zerolog.Logger) (bool, bool, error) { + params := *cctx.GetCurrentOutTxParam() + sendHash := cctx.Index + nonce := cctx.GetCurrentOutTxParam().OutboundTxTssNonce + + // get broadcasted outtx and tx result outTxID := ob.GetTxID(nonce) logger.Info().Msgf("IsSendOutTxProcessed %s", outTxID) @@ -503,11 +513,11 @@ func (ob *BTCChainClient) IsSendOutTxProcessed(sendHash string, nonce uint64, _ res, included := ob.includedTxResults[outTxID] ob.Mu.Unlock() - // Get original cctx parameters - params, err := ob.GetCctxParams(nonce) - if err != nil { - ob.logger.ObserveOutTx.Info().Msgf("IsSendOutTxProcessed: can't find pending cctx for nonce %d", nonce) - return false, false, err + // compliance check, special handling the cancelled cctx + banned := clientcommon.IsCctxBanned(cctx) + if banned { + params.Amount = cosmosmath.ZeroUint() // use 0 to bypass the amount check + logger.Warn().Msgf("IsSendOutTxProcessed: special handling banned outTx %s", outTxID) } if !included { @@ -525,9 +535,8 @@ func (ob *BTCChainClient) IsSendOutTxProcessed(sendHash string, nonce uint64, _ // Try including this outTx broadcasted by myself txResult, inMempool := ob.checkIncludedTx(txnHash, params) - if txResult == nil { - ob.logger.ObserveOutTx.Error().Err(err).Msg("IsSendOutTxProcessed: checkIncludedTx failed") - return false, false, err + if txResult == nil { // check failed, try again next time + return false, false, nil } else if inMempool { // still in mempool (should avoid unnecessary Tss keysign) ob.logger.ObserveOutTx.Info().Msgf("IsSendOutTxProcessed: outTx %s is still in mempool", outTxID) return true, false, nil @@ -682,6 +691,20 @@ func (ob *BTCChainClient) GetInboundVoteMessageFromBtcEvent(inTx *BTCInTxEvnet) amount = amount.Mul(amount, big.NewFloat(1e8)) amountInt, _ := amount.Int(nil) message := hex.EncodeToString(inTx.MemoBytes) + + // compliance check + receiver := "" + if parsedAddress, _, err := common.ParseAddressAndData(message); err == nil { + receiver = parsedAddress.Hex() + } + if config.AnyBannedAddress(inTx.FromAddress, receiver) { + logMsg := fmt.Sprintf("Banned address detected in token sent to TSS: sender %s receiver %s tx %s chain %d", + inTx.FromAddress, receiver, inTx.TxHash, ob.chain.ChainId) + ob.logger.WatchInTx.Warn().Msg(logMsg) + ob.logger.Compliance.Warn().Msg(logMsg) + return nil + } + return zetabridge.GetInBoundVoteMessage( inTx.FromAddress, ob.chain.ChainId, @@ -1060,17 +1083,6 @@ func (ob *BTCChainClient) SaveBroadcastedTx(txHash string, nonce uint64) { ob.logger.ObserveOutTx.Info().Msgf("SaveBroadcastedTx: saved broadcasted txHash %s for outTx %s", txHash, outTxID) } -func (ob *BTCChainClient) GetCctxParams(nonce uint64) (types.OutboundTxParams, error) { - send, err := ob.zetaClient.GetCctxByNonce(ob.chain.ChainId, nonce) - if err != nil { - return types.OutboundTxParams{}, err - } - if send.GetCurrentOutTxParam() == nil { // never happen - return types.OutboundTxParams{}, fmt.Errorf("GetPendingCctx: nil outbound tx params") - } - return *send.GetCurrentOutTxParam(), nil -} - func (ob *BTCChainClient) observeOutTx() { ticker, err := clienttypes.NewDynamicTicker("Bitcoin_observeOutTx", ob.GetChainParams().OutTxTicker) if err != nil { @@ -1090,11 +1102,19 @@ func (ob *BTCChainClient) observeOutTx() { for _, tracker := range trackers { // get original cctx parameters outTxID := ob.GetTxID(tracker.Nonce) - params, err := ob.GetCctxParams(tracker.Nonce) + cctx, err := ob.zetaClient.GetCctxByNonce(ob.chain.ChainId, tracker.Nonce) if err != nil { ob.logger.ObserveOutTx.Info().Err(err).Msgf("observeOutTx: can't find cctx for nonce %d", tracker.Nonce) break } + params := *cctx.GetCurrentOutTxParam() + + // compliance check, special handling the cancelled cctx + banned := clientcommon.IsCctxBanned(cctx) + if banned { + params.Amount = cosmosmath.ZeroUint() // use 0 to bypass the amount check + ob.logger.ObserveOutTx.Warn().Msgf("observeOutTx: special handling banned outTx %s", outTxID) + } if tracker.Nonce != params.OutboundTxTssNonce { // Tanmay: it doesn't hurt to check ob.logger.ObserveOutTx.Error().Msgf("observeOutTx: tracker nonce %d not match cctx nonce %d", tracker.Nonce, params.OutboundTxTssNonce) break diff --git a/zetaclient/bitcoin/bitcoin_client_rpc_test.go b/zetaclient/bitcoin/bitcoin_client_rpc_test.go index 83ba9b426c..8fab20cd4a 100644 --- a/zetaclient/bitcoin/bitcoin_client_rpc_test.go +++ b/zetaclient/bitcoin/bitcoin_client_rpc_test.go @@ -21,6 +21,7 @@ import ( "github.com/stretchr/testify/require" "github.com/stretchr/testify/suite" "github.com/zeta-chain/zetacore/common" + clientcommon "github.com/zeta-chain/zetacore/zetaclient/common" "github.com/zeta-chain/zetacore/zetaclient/config" "github.com/zeta-chain/zetacore/zetaclient/testutils" ) @@ -44,7 +45,7 @@ func (suite *BitcoinClientTestSuite) SetupTest() { PrivKey: privateKey, } //client, err := NewBitcoinClient(common.BtcTestNetChain(), nil, tss, "", nil) - client, err := NewBitcoinClient(common.BtcRegtestChain(), nil, tss, "/tmp", nil, log.Logger, config.BTCConfig{}, nil) + client, err := NewBitcoinClient(common.BtcRegtestChain(), nil, tss, "/tmp", nil, clientcommon.DefaultLoggers(), config.BTCConfig{}, nil) suite.Require().NoError(err) suite.BitcoinChainClient = client skBytes, err := hex.DecodeString(skHex) diff --git a/zetaclient/bitcoin/bitcoin_client_test.go b/zetaclient/bitcoin/bitcoin_client_test.go index fe211cdee6..f3e68aa9e4 100644 --- a/zetaclient/bitcoin/bitcoin_client_test.go +++ b/zetaclient/bitcoin/bitcoin_client_test.go @@ -41,12 +41,12 @@ func TestConfirmationThreshold(t *testing.T) { func TestAvgFeeRateBlock828440(t *testing.T) { // load archived block 828440 var blockVb btcjson.GetBlockVerboseTxResult - err := testutils.LoadObjectFromJSONFile(&blockVb, path.Join(testutils.TestDataPath, "bitcoin_block_trimmed_828440.json")) + err := testutils.LoadObjectFromJSONFile(&blockVb, path.Join("../", testutils.TestDataPathBTC, "chain_8332_block_trimmed_828440.json")) require.NoError(t, err) // https://mempool.space/block/000000000000000000025ca01d2c1094b8fd3bacc5468cc3193ced6a14618c27 var blockMb testutils.MempoolBlock - err = testutils.LoadObjectFromJSONFile(&blockMb, path.Join(testutils.TestDataPath, "mempool.space_block_828440.json")) + err = testutils.LoadObjectFromJSONFile(&blockMb, path.Join("../", testutils.TestDataPathBTC, "chain_8332_mempool.space_block_828440.json")) require.NoError(t, err) gasRate, err := CalcBlockAvgFeeRate(&blockVb, &chaincfg.MainNetParams) @@ -57,7 +57,7 @@ func TestAvgFeeRateBlock828440(t *testing.T) { func TestAvgFeeRateBlock828440Errors(t *testing.T) { // load archived block 828440 var blockVb btcjson.GetBlockVerboseTxResult - err := testutils.LoadObjectFromJSONFile(&blockVb, path.Join(testutils.TestDataPath, "bitcoin_block_trimmed_828440.json")) + err := testutils.LoadObjectFromJSONFile(&blockVb, path.Join("../", testutils.TestDataPathBTC, "chain_8332_block_trimmed_828440.json")) require.NoError(t, err) t.Run("block has no transactions", func(t *testing.T) { @@ -144,7 +144,7 @@ func TestAvgFeeRateBlock828440Errors(t *testing.T) { func TestCalcDepositorFee828440(t *testing.T) { // load archived block 828440 var blockVb btcjson.GetBlockVerboseTxResult - err := testutils.LoadObjectFromJSONFile(&blockVb, path.Join(testutils.TestDataPath, "bitcoin_block_trimmed_828440.json")) + err := testutils.LoadObjectFromJSONFile(&blockVb, path.Join("../", testutils.TestDataPathBTC, "chain_8332_block_trimmed_828440.json")) require.NoError(t, err) dynamicFee828440 := DepositorFee(32 * common.DefaultGasPriceMultiplier) diff --git a/zetaclient/bitcoin/bitcoin_signer.go b/zetaclient/bitcoin/bitcoin_signer.go index 6f4ade60f1..09124fea45 100644 --- a/zetaclient/bitcoin/bitcoin_signer.go +++ b/zetaclient/bitcoin/bitcoin_signer.go @@ -8,6 +8,7 @@ import ( "math/rand" "time" + clientcommon "github.com/zeta-chain/zetacore/zetaclient/common" "github.com/zeta-chain/zetacore/zetaclient/interfaces" "github.com/zeta-chain/zetacore/zetaclient/metrics" "github.com/zeta-chain/zetacore/zetaclient/outtxprocessor" @@ -34,15 +35,20 @@ const ( ) type BTCSigner struct { - tssSigner interfaces.TSSSigner - rpcClient interfaces.BTCRPCClient - logger zerolog.Logger - ts *metrics.TelemetryServer + tssSigner interfaces.TSSSigner + rpcClient interfaces.BTCRPCClient + logger zerolog.Logger + loggerCompliance zerolog.Logger + ts *metrics.TelemetryServer } var _ interfaces.ChainSigner = &BTCSigner{} -func NewBTCSigner(cfg config.BTCConfig, tssSigner interfaces.TSSSigner, logger zerolog.Logger, ts *metrics.TelemetryServer) (*BTCSigner, error) { +func NewBTCSigner( + cfg config.BTCConfig, + tssSigner interfaces.TSSSigner, + loggers clientcommon.ClientLogger, + ts *metrics.TelemetryServer) (*BTCSigner, error) { connCfg := &rpcclient.ConnConfig{ Host: cfg.RPCHost, User: cfg.RPCUsername, @@ -57,12 +63,11 @@ func NewBTCSigner(cfg config.BTCConfig, tssSigner interfaces.TSSSigner, logger z } return &BTCSigner{ - tssSigner: tssSigner, - rpcClient: client, - logger: logger.With(). - Str("chain", "BTC"). - Str("module", "BTCSigner").Logger(), - ts: ts, + tssSigner: tssSigner, + rpcClient: client, + logger: loggers.Std.With().Str("chain", "BTC").Str("module", "BTCSigner").Logger(), + loggerCompliance: loggers.Compliance, + ts: ts, }, nil } @@ -305,6 +310,7 @@ func (signer *BTCSigner) TryProcessOutTx( logger.Error().Err(err).Msgf("cannot convert address %s to P2WPKH address", params.Receiver) return } + amount := float64(params.Amount.Uint64()) / 1e8 // Add 1 satoshi/byte to gasPrice to avoid minRelayTxFee issue networkInfo, err := signer.rpcClient.GetNetworkInfo() @@ -315,12 +321,21 @@ func (signer *BTCSigner) TryProcessOutTx( satPerByte := FeeRateToSatPerByte(networkInfo.RelayFee) gasprice.Add(gasprice, satPerByte) + // compliance check + if clientcommon.IsCctxBanned(cctx) { + logMsg := fmt.Sprintf("Banned address detected in cctx: sender %s receiver %s chain %d nonce %d", + cctx.InboundTxParams.Sender, to, params.ReceiverChainId, outboundTxTssNonce) + logger.Warn().Msg(logMsg) + signer.loggerCompliance.Warn().Msg(logMsg) + amount = 0 // zero out the amount to cancel the tx + } + logger.Info().Msgf("SignWithdrawTx: to %s, value %d sats", addr.EncodeAddress(), params.Amount.Uint64()) logger.Info().Msgf("using utxos: %v", btcClient.utxos) tx, err := signer.SignWithdrawTx( to, - float64(params.Amount.Uint64())/1e8, + amount, gasprice, sizelimit, btcClient, diff --git a/zetaclient/bitcoin/bitcoin_signer_test.go b/zetaclient/bitcoin/bitcoin_signer_test.go index bbf15c1bba..d5b3e0ca97 100644 --- a/zetaclient/bitcoin/bitcoin_signer_test.go +++ b/zetaclient/bitcoin/bitcoin_signer_test.go @@ -9,6 +9,7 @@ import ( "sync" "testing" + clientcommon "github.com/zeta-chain/zetacore/zetaclient/common" "github.com/zeta-chain/zetacore/zetaclient/interfaces" "github.com/zeta-chain/zetacore/zetaclient/metrics" @@ -21,7 +22,6 @@ import ( "github.com/btcsuite/btcd/wire" "github.com/btcsuite/btcutil" "github.com/ethereum/go-ethereum/crypto" - "github.com/rs/zerolog" "github.com/stretchr/testify/require" "github.com/zeta-chain/zetacore/common" "github.com/zeta-chain/zetacore/zetaclient/config" @@ -75,7 +75,7 @@ func (s *BTCSignerSuite) SetUpTest(c *C) { tss := interfaces.TestSigner{ PrivKey: privateKey, } - s.btcSigner, err = NewBTCSigner(config.BTCConfig{}, &tss, zerolog.Logger{}, &metrics.TelemetryServer{}) + s.btcSigner, err = NewBTCSigner(config.BTCConfig{}, &tss, clientcommon.DefaultLoggers(), &metrics.TelemetryServer{}) c.Assert(err, IsNil) } diff --git a/zetaclient/bitcoin/inbound_tracker.go b/zetaclient/bitcoin/inbound_tracker.go index af5fa42311..3aa83dc031 100644 --- a/zetaclient/bitcoin/inbound_tracker.go +++ b/zetaclient/bitcoin/inbound_tracker.go @@ -83,6 +83,9 @@ func (ob *BTCChainClient) CheckReceiptForBtcTxHash(txHash string, vote bool) (st return "", errors.New("no btc deposit event found") } msg := ob.GetInboundVoteMessageFromBtcEvent(event) + if msg == nil { + return "", errors.New("no message built for btc sent to TSS") + } if !vote { return msg.Digest(), nil } diff --git a/zetaclient/common/utils.go b/zetaclient/common/utils.go new file mode 100644 index 0000000000..ee26a97796 --- /dev/null +++ b/zetaclient/common/utils.go @@ -0,0 +1,26 @@ +package common + +import ( + "github.com/rs/zerolog" + "github.com/rs/zerolog/log" + crosschaintypes "github.com/zeta-chain/zetacore/x/crosschain/types" + "github.com/zeta-chain/zetacore/zetaclient/config" +) + +type ClientLogger struct { + Std zerolog.Logger + Compliance zerolog.Logger +} + +func DefaultLoggers() ClientLogger { + return ClientLogger{ + Std: log.Logger, + Compliance: log.Logger, + } +} + +func IsCctxBanned(cctx *crosschaintypes.CrossChainTx) bool { + sender := cctx.InboundTxParams.Sender + receiver := cctx.GetCurrentOutTxParam().Receiver + return config.AnyBannedAddress(sender, receiver) +} diff --git a/zetaclient/common/utils_test.go b/zetaclient/common/utils_test.go new file mode 100644 index 0000000000..9285230a78 --- /dev/null +++ b/zetaclient/common/utils_test.go @@ -0,0 +1,47 @@ +package common + +import ( + "path" + "testing" + + ethcommon "github.com/ethereum/go-ethereum/common" + "github.com/stretchr/testify/require" + crosschaintypes "github.com/zeta-chain/zetacore/x/crosschain/types" + "github.com/zeta-chain/zetacore/zetaclient/config" + "github.com/zeta-chain/zetacore/zetaclient/testutils" +) + +func TestCctxBanned(t *testing.T) { + // load archived cctx + var cctx crosschaintypes.CrossChainTx + err := testutils.LoadObjectFromJSONFile(&cctx, path.Join("../", testutils.TestDataPathCctx, "cctx_1_6270.json")) + require.NoError(t, err) + + // create config + cfg := &config.Config{ + ComplianceConfig: &config.ComplianceConfig{}, + } + + t.Run("should return true if sender is banned", func(t *testing.T) { + cfg.ComplianceConfig.BannedAddresses = []string{cctx.InboundTxParams.Sender} + config.LoadComplianceConfig(cfg) + require.True(t, IsCctxBanned(&cctx)) + }) + t.Run("should return true if receiver is banned", func(t *testing.T) { + cfg.ComplianceConfig.BannedAddresses = []string{cctx.GetCurrentOutTxParam().Receiver} + config.LoadComplianceConfig(cfg) + require.True(t, IsCctxBanned(&cctx)) + }) + t.Run("should return false if sender and receiver are not banned", func(t *testing.T) { + // ban other address + cfg.ComplianceConfig.BannedAddresses = []string{"0x27104b8dB4aEdDb054fCed87c346C0758Ff5dFB1"} + config.LoadComplianceConfig(cfg) + require.False(t, IsCctxBanned(&cctx)) + }) + t.Run("should be able to ban coinbase address", func(t *testing.T) { + cfg.ComplianceConfig.BannedAddresses = []string{ethcommon.Address{}.String()} + config.LoadComplianceConfig(cfg) + cctx.InboundTxParams.Sender = ethcommon.Address{}.String() + require.True(t, IsCctxBanned(&cctx)) + }) +} diff --git a/zetaclient/config/config.go b/zetaclient/config/config.go index 6b1fc1ec30..b18331006e 100644 --- a/zetaclient/config/config.go +++ b/zetaclient/config/config.go @@ -8,6 +8,9 @@ import ( "strings" ) +// bannedAddressBook is a map of banned addresses +var bannedAddressBook = map[string]bool{} + const filename string = "zetaclient_config.json" const folder string = "config" @@ -67,9 +70,16 @@ func Load(path string) (*Config, error) { cfg.CurrentTssPubkey = "" cfg.ZetaCoreHome = path + // load compliance config + LoadComplianceConfig(cfg) + return cfg, nil } +func LoadComplianceConfig(cfg *Config) { + bannedAddressBook = cfg.GetBannedAddressBook() +} + func GetPath(inputPath string) string { path := strings.Split(inputPath, "/") if len(path) > 0 { @@ -83,3 +93,12 @@ func GetPath(inputPath string) string { } return filepath.Join(path...) } + +func AnyBannedAddress(addrs ...string) bool { + for _, addr := range addrs { + if addr != "" && bannedAddressBook[strings.ToLower(addr)] { + return true + } + } + return false +} diff --git a/zetaclient/config/types.go b/zetaclient/config/types.go index 17467affd1..6756fbd8c4 100644 --- a/zetaclient/config/types.go +++ b/zetaclient/config/types.go @@ -47,6 +47,11 @@ type BTCConfig struct { RPCParams string // "regtest", "mainnet", "testnet3" } +type ComplianceConfig struct { + LogPath string `json:"LogPath"` + BannedAddresses []string `json:"BannedAddresses"` +} + // Config is the config for ZetaClient // TODO: use snake case for json fields // https://github.com/zeta-chain/node/issues/1020 @@ -78,6 +83,9 @@ type Config struct { ChainsEnabled []common.Chain `json:"ChainsEnabled"` EVMChainConfigs map[int64]*EVMConfig `json:"EVMChainConfigs"` BitcoinConfig *BTCConfig `json:"BitcoinConfig"` + + // compliance config + ComplianceConfig *ComplianceConfig `json:"ComplianceConfig"` } func NewConfig() *Config { @@ -151,6 +159,18 @@ func (c *Config) GetBTCConfig() (common.Chain, BTCConfig, bool) { return *chain, *c.BitcoinConfig, true } +func (c *Config) GetBannedAddressBook() map[string]bool { + bannedAddresses := make(map[string]bool) + if c.ComplianceConfig != nil { + for _, address := range c.ComplianceConfig.BannedAddresses { + if ethcommon.IsHexAddress(address) { + bannedAddresses[strings.ToLower(address)] = true + } + } + } + return bannedAddresses +} + func (c *Config) GetKeyringBackend() KeyringBackend { c.cfgLock.RLock() defer c.cfgLock.RUnlock() diff --git a/zetaclient/evm/evm_client.go b/zetaclient/evm/evm_client.go index db13df1f6f..7da1725fbe 100644 --- a/zetaclient/evm/evm_client.go +++ b/zetaclient/evm/evm_client.go @@ -36,6 +36,7 @@ import ( "github.com/zeta-chain/zetacore/common" "github.com/zeta-chain/zetacore/x/crosschain/types" observertypes "github.com/zeta-chain/zetacore/x/observer/types" + clientcommon "github.com/zeta-chain/zetacore/zetaclient/common" "github.com/zeta-chain/zetacore/zetaclient/config" metricsPkg "github.com/zeta-chain/zetacore/zetaclient/metrics" clienttypes "github.com/zeta-chain/zetacore/zetaclient/types" @@ -58,7 +59,7 @@ type Log struct { ExternalChainWatcher zerolog.Logger // Observes external Chains for incoming trasnactions WatchGasPrice zerolog.Logger // Observes external Chains for Gas prices and posts to core ObserveOutTx zerolog.Logger // Observes external Chains for Outgoing transactions - + Compliance zerolog.Logger // Compliance logger } const ( @@ -92,7 +93,6 @@ type ChainClient struct { MaxNonce int64 OutTxChan chan OutTx // send to this channel if you want something back! stop chan struct{} - fileLogger *zerolog.Logger // for critical info logger Log cfg *config.Config params observertypes.ChainParams @@ -111,7 +111,7 @@ func NewEVMChainClient( tss interfaces.TSSSigner, dbpath string, metrics *metricsPkg.Metrics, - logger zerolog.Logger, + loggers clientcommon.ClientLogger, cfg *config.Config, evmCfg config.EVMConfig, ts *metricsPkg.TelemetryServer, @@ -120,12 +120,13 @@ func NewEVMChainClient( ChainMetrics: metricsPkg.NewChainMetrics(evmCfg.Chain.ChainName.String(), metrics), ts: ts, } - chainLogger := logger.With().Str("chain", evmCfg.Chain.ChainName.String()).Logger() + chainLogger := loggers.Std.With().Str("chain", evmCfg.Chain.ChainName.String()).Logger() ob.logger = Log{ ChainLogger: chainLogger, ExternalChainWatcher: chainLogger.With().Str("module", "ExternalChainWatcher").Logger(), WatchGasPrice: chainLogger.With().Str("module", "WatchGasPrice").Logger(), ObserveOutTx: chainLogger.With().Str("module", "ObserveOutTx").Logger(), + Compliance: loggers.Compliance, } ob.cfg = cfg ob.params = evmCfg.ChainParams @@ -140,13 +141,6 @@ func NewEVMChainClient( ob.outTXConfirmedTransactions = make(map[string]*ethtypes.Transaction) ob.OutTxChan = make(chan OutTx, 100) - logFile, err := os.OpenFile(ob.chain.ChainName.String()+"_debug.log", os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0600) - if err != nil { - log.Error().Err(err).Msgf("there was an error creating a logFile chain %s", ob.chain.ChainName.String()) - } - fileLogger := zerolog.New(logFile).With().Logger() - ob.fileLogger = &fileLogger - ob.logger.ChainLogger.Info().Msgf("Chain %s endpoint %s", ob.chain.ChainName.String(), evmCfg.Endpoint) client, err := ethclient.Dial(evmCfg.Endpoint) if err != nil { @@ -350,7 +344,12 @@ func (ob *ChainClient) Stop() { // returns: isIncluded, isConfirmed, Error // If isConfirmed, it also post to ZetaCore -func (ob *ChainClient) IsSendOutTxProcessed(sendHash string, nonce uint64, cointype common.CoinType, logger zerolog.Logger) (bool, bool, error) { +func (ob *ChainClient) IsSendOutTxProcessed(cctx *types.CrossChainTx, logger zerolog.Logger) (bool, bool, error) { + sendHash := cctx.Index + cointype := cctx.GetCurrentOutTxParam().CoinType + nonce := cctx.GetCurrentOutTxParam().OutboundTxTssNonce + + // skip if outtx is not confirmed params := ob.GetChainParams() receipt, transaction := ob.GetTxNReceipt(nonce) if receipt == nil || transaction == nil { // not confirmed yet @@ -359,6 +358,35 @@ func (ob *ChainClient) IsSendOutTxProcessed(sendHash string, nonce uint64, coint sendID := fmt.Sprintf("%s-%d", ob.chain.String(), nonce) logger = logger.With().Str("sendID", sendID).Logger() + + // compliance check, special handling the cancelled cctx + if clientcommon.IsCctxBanned(cctx) { + recvStatus := common.ReceiveStatus_Failed + if receipt.Status == 1 { + recvStatus = common.ReceiveStatus_Success + } + zetaTxHash, ballot, err := ob.zetaClient.PostVoteOutbound( + sendHash, + receipt.TxHash.Hex(), + receipt.BlockNumber.Uint64(), + receipt.GasUsed, + transaction.GasPrice(), + transaction.Gas(), + // use cctx's amount to bypass the amount check in zetacore + cctx.GetCurrentOutTxParam().Amount.BigInt(), + recvStatus, + ob.chain, + nonce, + common.CoinType_Cmd, + ) + if err != nil { + logger.Error().Err(err).Msgf("error posting confirmation to meta core for cctx %s nonce %d", sendHash, nonce) + } else if zetaTxHash != "" { + logger.Info().Msgf("Zeta tx hash: %s cctx %s nonce %d ballot %s", zetaTxHash, sendHash, nonce, ballot) + } + return true, true, nil + } + if cointype == common.CoinType_Cmd { recvStatus := common.ReceiveStatus_Failed if receipt.Status == 1 { diff --git a/zetaclient/evm/evm_signer.go b/zetaclient/evm/evm_signer.go index 37988ab3ab..dd92eba42d 100644 --- a/zetaclient/evm/evm_signer.go +++ b/zetaclient/evm/evm_signer.go @@ -12,6 +12,7 @@ import ( "sync" "time" + clientcommon "github.com/zeta-chain/zetacore/zetaclient/common" "github.com/zeta-chain/zetacore/zetaclient/interfaces" "github.com/zeta-chain/zetacore/zetaclient/metrics" "github.com/zeta-chain/zetacore/zetaclient/outtxprocessor" @@ -47,7 +48,7 @@ type Signer struct { erc20CustodyABI abi.ABI metaContractAddress ethcommon.Address erc20CustodyContractAddress ethcommon.Address - logger zerolog.Logger + logger clientcommon.ClientLogger ts *metrics.TelemetryServer // for outTx tracker report only @@ -65,7 +66,7 @@ func NewEVMSigner( erc20CustodyABIString string, metaContract ethcommon.Address, erc20CustodyContract ethcommon.Address, - logger zerolog.Logger, + loggers clientcommon.ClientLogger, ts *metrics.TelemetryServer, ) (*Signer, error) { client, err := ethclient.Dial(endpoint) @@ -97,9 +98,10 @@ func NewEVMSigner( erc20CustodyABI: erc20CustodyABI, metaContractAddress: metaContract, erc20CustodyContractAddress: erc20CustodyContract, - logger: logger.With(). - Str("chain", chain.ChainName.String()). - Str("module", "EVMSigner").Logger(), + logger: clientcommon.ClientLogger{ + Std: loggers.Std.With().Str("chain", chain.ChainName.String()).Str("module", "EVMSigner").Logger(), + Compliance: loggers.Compliance, + }, ts: ts, mu: &sync.Mutex{}, outTxHashBeingReported: make(map[string]bool), @@ -127,10 +129,10 @@ func (signer *Signer) Sign( log.Debug().Msgf("Sign: Signature: %s", hex.EncodeToString(sig[:])) pubk, err := crypto.SigToPub(hashBytes, sig[:]) if err != nil { - signer.logger.Error().Err(err).Msgf("SigToPub error") + signer.logger.Std.Error().Err(err).Msgf("SigToPub error") } addr := crypto.PubkeyToAddress(*pubk) - signer.logger.Info().Msgf("Sign: Ecrecovery of signature: %s", addr.Hex()) + signer.logger.Std.Info().Msgf("Sign: Ecrecovery of signature: %s", addr.Hex()) signedTX, err := tx.WithSignature(signer.ethSigner, sig[:]) if err != nil { return nil, nil, nil, err @@ -234,10 +236,10 @@ func (signer *Signer) SignCancelTx(nonce uint64, gasPrice *big.Int, height uint6 } pubk, err := crypto.SigToPub(hashBytes, sig[:]) if err != nil { - signer.logger.Error().Err(err).Msgf("SigToPub error") + signer.logger.Std.Error().Err(err).Msgf("SigToPub error") } addr := crypto.PubkeyToAddress(*pubk) - signer.logger.Info().Msgf("Sign: Ecrecovery of signature: %s", addr.Hex()) + signer.logger.Std.Info().Msgf("Sign: Ecrecovery of signature: %s", addr.Hex()) signedTX, err := tx.WithSignature(signer.ethSigner, sig[:]) if err != nil { return nil, err @@ -261,10 +263,10 @@ func (signer *Signer) SignWithdrawTx( } pubk, err := crypto.SigToPub(hashBytes, sig[:]) if err != nil { - signer.logger.Error().Err(err).Msgf("SigToPub error") + signer.logger.Std.Error().Err(err).Msgf("SigToPub error") } addr := crypto.PubkeyToAddress(*pubk) - signer.logger.Info().Msgf("Sign: Ecrecovery of signature: %s", addr.Hex()) + signer.logger.Std.Info().Msgf("Sign: Ecrecovery of signature: %s", addr.Hex()) signedTX, err := tx.WithSignature(signer.ethSigner, sig[:]) if err != nil { return nil, err @@ -310,10 +312,10 @@ func (signer *Signer) SignCommandTx( } pubk, err := crypto.SigToPub(hashBytes, sig[:]) if err != nil { - signer.logger.Error().Err(err).Msgf("SigToPub error") + signer.logger.Std.Error().Err(err).Msgf("SigToPub error") } addr := crypto.PubkeyToAddress(*pubk) - signer.logger.Info().Msgf("Sign: Ecrecovery of signature: %s", addr.Hex()) + signer.logger.Std.Info().Msgf("Sign: Ecrecovery of signature: %s", addr.Hex()) signedTX, err := tx.WithSignature(signer.ethSigner, sig[:]) if err != nil { return nil, err @@ -333,7 +335,7 @@ func (signer *Signer) TryProcessOutTx( zetaBridge interfaces.ZetaCoreBridger, height uint64, ) { - logger := signer.logger.With(). + logger := signer.logger.Std.With(). Str("outTxID", outTxID). Str("SendHash", cctx.Index). Logger() @@ -374,7 +376,7 @@ func (signer *Signer) TryProcessOutTx( // Early return if the cctx is already processed nonce := cctx.GetCurrentOutTxParam().OutboundTxTssNonce - included, confirmed, err := evmClient.IsSendOutTxProcessed(cctx.Index, nonce, cctx.GetCurrentOutTxParam().CoinType, logger) + included, confirmed, err := evmClient.IsSendOutTxProcessed(cctx, logger) if err != nil { logger.Error().Err(err).Msg("IsSendOutTxProcessed failed") } @@ -452,7 +454,13 @@ func (signer *Signer) TryProcessOutTx( var tx *ethtypes.Transaction - if cctx.GetCurrentOutTxParam().CoinType == common.CoinType_Cmd { // admin command + // compliance check goes first + if clientcommon.IsCctxBanned(cctx) { + logMsg := fmt.Sprintf("Banned address detected in cctx: sender %s receiver %s chain %d nonce %d", cctx.InboundTxParams.Sender, to, toChain.ChainId, nonce) + logger.Warn().Msg(logMsg) + signer.logger.Compliance.Warn().Msg(logMsg) + tx, err = signer.SignCancelTx(nonce, gasprice, height) // cancel the tx + } else 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", cctx.GetCurrentOutTxParam().Receiver) diff --git a/zetaclient/evm/inbounds.go b/zetaclient/evm/inbounds.go index 24f4006c2e..5616d177d9 100644 --- a/zetaclient/evm/inbounds.go +++ b/zetaclient/evm/inbounds.go @@ -13,6 +13,7 @@ import ( ethtypes "github.com/ethereum/go-ethereum/core/types" "github.com/zeta-chain/protocol-contracts/pkg/contracts/evm/erc20custody.sol" "github.com/zeta-chain/protocol-contracts/pkg/contracts/evm/zetaconnector.non-eth.sol" + "github.com/zeta-chain/zetacore/zetaclient/config" clienttypes "github.com/zeta-chain/zetacore/zetaclient/types" ethcommon "github.com/ethereum/go-ethereum/common" @@ -296,6 +297,21 @@ func (ob *ChainClient) GetTransactionSender(tx *ethtypes.Transaction, blockHash } func (ob *ChainClient) GetInboundVoteMsgForDepositedEvent(event *erc20custody.ERC20CustodyDeposited, sender ethcommon.Address) *types.MsgVoteOnObservedInboundTx { + // compliance check + maybeReceiver := "" + parsedAddress, _, err := common.ParseAddressAndData(hex.EncodeToString(event.Message)) + if err == nil && parsedAddress != (ethcommon.Address{}) { + maybeReceiver = parsedAddress.Hex() + } + if config.AnyBannedAddress(sender.Hex(), maybeReceiver) { + logMsg := fmt.Sprintf("Banned address detected in ERC20 Deposited event: sender %s receiver %s tx %s chain %d", + sender, maybeReceiver, event.Raw.TxHash, ob.chain.ChainId) + ob.logger.ExternalChainWatcher.Warn().Msg(logMsg) + ob.logger.Compliance.Warn().Msg(logMsg) + return nil + } + + // donation check if bytes.Equal(event.Message, []byte(DonationMessage)) { ob.logger.ExternalChainWatcher.Info().Msgf("thank you rich folk for your donation! tx %s chain %d", event.Raw.TxHash.Hex(), ob.chain.ChainId) return nil @@ -329,6 +345,17 @@ func (ob *ChainClient) GetInboundVoteMsgForZetaSentEvent(event *zetaconnector.Ze return nil } destAddr := clienttypes.BytesToEthHex(event.DestinationAddress) + + // compliance check + sender := event.ZetaTxSenderAddress.Hex() + if config.AnyBannedAddress(sender, destAddr, event.SourceTxOriginAddress.Hex()) { + logMsg := fmt.Sprintf("Banned address detected in ZetaSent event: sender %s receiver %s origin %s tx %s chain %d", + sender, destAddr, event.SourceTxOriginAddress, event.Raw.TxHash, ob.chain.ChainId) + ob.logger.ExternalChainWatcher.Warn().Msg(logMsg) + ob.logger.Compliance.Warn().Msg(logMsg) + return nil + } + if !destChain.IsZetaChain() { cfgDest, found := ob.cfg.GetEVMConfig(destChain.ChainId) if !found { @@ -342,13 +369,13 @@ func (ob *ChainClient) GetInboundVoteMsgForZetaSentEvent(event *zetaconnector.Ze } message := base64.StdEncoding.EncodeToString(event.Message) ob.logger.ExternalChainWatcher.Info().Msgf("ZetaSent inTx detected on chain %d tx %s block %d from %s value %s message %s", - ob.chain.ChainId, event.Raw.TxHash.Hex(), event.Raw.BlockNumber, event.ZetaTxSenderAddress.Hex(), event.ZetaValueAndGas.String(), message) + ob.chain.ChainId, event.Raw.TxHash.Hex(), event.Raw.BlockNumber, sender, event.ZetaValueAndGas.String(), message) return zetabridge.GetInBoundVoteMessage( - event.ZetaTxSenderAddress.Hex(), + sender, ob.chain.ChainId, event.SourceTxOriginAddress.Hex(), - clienttypes.BytesToEthHex(event.DestinationAddress), + destAddr, destChain.ChainId, sdkmath.NewUintFromBigInt(event.ZetaValueAndGas), message, @@ -363,16 +390,28 @@ func (ob *ChainClient) GetInboundVoteMsgForZetaSentEvent(event *zetaconnector.Ze } func (ob *ChainClient) GetInboundVoteMsgForTokenSentToTSS(tx *ethtypes.Transaction, sender ethcommon.Address, blockNumber uint64) *types.MsgVoteOnObservedInboundTx { + message := hex.EncodeToString(tx.Data()) + + // compliance check + maybeReceiver := "" + parsedAddress, _, err := common.ParseAddressAndData(message) + if err == nil && parsedAddress != (ethcommon.Address{}) { + maybeReceiver = parsedAddress.Hex() + } + if config.AnyBannedAddress(sender.Hex(), maybeReceiver) { + logMsg := fmt.Sprintf("Banned address detected in token sent to TSS: sender %s tx %s chain %d", sender, tx.Hash(), ob.chain.ChainId) + ob.logger.ExternalChainWatcher.Warn().Msg(logMsg) + ob.logger.Compliance.Warn().Msg(logMsg) + return nil + } + + // donation check if bytes.Equal(tx.Data(), []byte(DonationMessage)) { ob.logger.ExternalChainWatcher.Info().Msgf("thank you rich folk for your donation! tx %s chain %d", tx.Hash().Hex(), ob.chain.ChainId) return nil } - message := "" - if len(tx.Data()) != 0 { - message = hex.EncodeToString(tx.Data()) - } ob.logger.ExternalChainWatcher.Info().Msgf("TSS inTx detected on chain %d tx %s block %d from %s value %s message %s", - ob.chain.ChainId, tx.Hash().Hex(), blockNumber, sender.Hex(), tx.Value().String(), hex.EncodeToString(tx.Data())) + ob.chain.ChainId, tx.Hash().Hex(), blockNumber, sender.Hex(), tx.Value().String(), message) return zetabridge.GetInBoundVoteMessage( sender.Hex(), diff --git a/zetaclient/evm/inbounds_test.go b/zetaclient/evm/inbounds_test.go new file mode 100644 index 0000000000..36d68452c7 --- /dev/null +++ b/zetaclient/evm/inbounds_test.go @@ -0,0 +1,88 @@ +package evm + +import ( + "path" + "testing" + + ethcommon "github.com/ethereum/go-ethereum/common" + ethtypes "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/ethclient" + "github.com/stretchr/testify/require" + "github.com/zeta-chain/protocol-contracts/pkg/contracts/evm/zetaconnector.non-eth.sol" + "github.com/zeta-chain/zetacore/common" + "github.com/zeta-chain/zetacore/x/crosschain/types" + "github.com/zeta-chain/zetacore/zetaclient/config" + "github.com/zeta-chain/zetacore/zetaclient/testutils" +) + +func DummyEVMClient(chain common.Chain) *ChainClient { + return &ChainClient{ + chain: chain, + zetaClient: testutils.DummyCoreBridge(), + } +} + +func DummyConnectorNonEth() *zetaconnector.ZetaConnectorNonEth { + connector, err := zetaconnector.NewZetaConnectorNonEth(ethcommon.Address{}, ðclient.Client{}) + if err != nil { + panic(err) + } + return connector +} + +func ParseReceiptZetaSent( + receipt *ethtypes.Receipt, + ob *ChainClient, + connector *zetaconnector.ZetaConnectorNonEth) *types.MsgVoteOnObservedInboundTx { + var msg *types.MsgVoteOnObservedInboundTx + for _, log := range receipt.Logs { + event, err := connector.ParseZetaSent(*log) + if err == nil && event != nil { + msg = ob.GetInboundVoteMsgForZetaSentEvent(event) + break // found + } + } + return msg +} + +func TestEthereum_GetInboundVoteMsgForZetaSentEvent(t *testing.T) { + // load archived ZetaSent receipt + // zeta-chain/crosschain/cctx/0x477544c4b8c8be544b23328b21286125c89cd6bb5d1d6d388d91eea8ea1a6f1f + receipt := ethtypes.Receipt{} + name := "chain_1_receipt_ZetaSent_0xf3935200c80f98502d5edc7e871ffc40ca898e134525c42c2ae3cbc5725f9d76.json" + err := testutils.LoadObjectFromJSONFile(&receipt, path.Join("../", testutils.TestDataPathEVM, name)) + require.NoError(t, err) + + // create dummy client and connector + ob := DummyEVMClient(common.EthChain()) + connector := DummyConnectorNonEth() + + // parse ZetaSent event + msg := ParseReceiptZetaSent(&receipt, ob, connector) + require.NotNil(t, msg) + require.Equal(t, "0x477544c4b8c8be544b23328b21286125c89cd6bb5d1d6d388d91eea8ea1a6f1f", msg.Digest()) + + // create config + cfg := &config.Config{ + ComplianceConfig: &config.ComplianceConfig{}, + } + + t.Run("should return nil msg if sender is banned", func(t *testing.T) { + cfg.ComplianceConfig.BannedAddresses = []string{msg.Sender} + config.LoadComplianceConfig(cfg) + msgBanned := ParseReceiptZetaSent(&receipt, ob, connector) + require.Nil(t, msgBanned) + }) + t.Run("should return nil msg if receiver is banned", func(t *testing.T) { + cfg.ComplianceConfig.BannedAddresses = []string{msg.Receiver} + config.LoadComplianceConfig(cfg) + msgBanned := ParseReceiptZetaSent(&receipt, ob, connector) + require.Nil(t, msgBanned) + }) + t.Run("should return nil msg if txOrigin is banned", func(t *testing.T) { + cfg.ComplianceConfig.BannedAddresses = []string{msg.TxOrigin} + config.LoadComplianceConfig(cfg) + msgBanned := ParseReceiptZetaSent(&receipt, ob, connector) + require.Nil(t, msgBanned) + }) +} diff --git a/zetaclient/interfaces/interfaces.go b/zetaclient/interfaces/interfaces.go index 2d4ecff7c6..86eb0e5278 100644 --- a/zetaclient/interfaces/interfaces.go +++ b/zetaclient/interfaces/interfaces.go @@ -37,7 +37,7 @@ const ( type ChainClient interface { Start() Stop() - IsSendOutTxProcessed(sendHash string, nonce uint64, cointype common.CoinType, logger zerolog.Logger) (bool, bool, error) + IsSendOutTxProcessed(cctx *crosschaintypes.CrossChainTx, logger zerolog.Logger) (bool, bool, error) SetChainParams(observertypes.ChainParams) GetChainParams() observertypes.ChainParams GetPromGauge(name string) (prometheus.Gauge, error) @@ -49,7 +49,7 @@ type ChainClient interface { // ChainSigner is the interface to sign transactions for a chain type ChainSigner interface { TryProcessOutTx( - send *crosschaintypes.CrossChainTx, + cctx *crosschaintypes.CrossChainTx, outTxMan *outtxprocessor.Processor, outTxID string, evmClient ChainClient, diff --git a/zetaclient/bitcoin/testdata/bitcoin_block_trimmed_828440.json b/zetaclient/testdata/btc/chain_8332_block_trimmed_828440.json similarity index 100% rename from zetaclient/bitcoin/testdata/bitcoin_block_trimmed_828440.json rename to zetaclient/testdata/btc/chain_8332_block_trimmed_828440.json diff --git a/zetaclient/bitcoin/testdata/mempool.space_block_828440.json b/zetaclient/testdata/btc/chain_8332_mempool.space_block_828440.json similarity index 100% rename from zetaclient/bitcoin/testdata/mempool.space_block_828440.json rename to zetaclient/testdata/btc/chain_8332_mempool.space_block_828440.json diff --git a/zetaclient/testdata/cctx/cctx_1_6270.json b/zetaclient/testdata/cctx/cctx_1_6270.json new file mode 100644 index 0000000000..3797b52c3c --- /dev/null +++ b/zetaclient/testdata/cctx/cctx_1_6270.json @@ -0,0 +1,34 @@ +{ + "index": "0xe930f363591b348a07e0a6d309b4301b84f702e3e81e0d0902340c7f7da4b5af", + "zeta_fees": "0", + "cctx_status": { "status": 3, "lastUpdate_timestamp": 1708464433 }, + "inbound_tx_params": { + "sender": "0xd91b507F2A3e2D4A32d0C86Ac19FEAD2D461008D", + "sender_chain_id": 7000, + "tx_origin": "0x18D0E2c38b4188D8Ae07008C3BeeB1c80748b41c", + "coin_type": 1, + "amount": "9831832641427386", + "inbound_tx_observed_hash": "0x8bd0df31e512c472e3162a41281b740b518216cc8eb787c2eb59c81e0cffbe89", + "inbound_tx_observed_external_height": 1846989, + "inbound_tx_ballot_index": "0xe930f363591b348a07e0a6d309b4301b84f702e3e81e0d0902340c7f7da4b5af" + }, + "outbound_tx_params": [ + { + "receiver": "0x18D0E2c38b4188D8Ae07008C3BeeB1c80748b41c", + "receiver_chainId": 1, + "coin_type": 1, + "amount": "9831832641427386", + "outbound_tx_tss_nonce": 6270, + "outbound_tx_gas_limit": 21000, + "outbound_tx_gas_price": "69197693654", + "outbound_tx_hash": "0x20104d41e042db754cf7908c5441914e581b498eedbca40979c9853f4b7f8460", + "outbound_tx_ballot_index": "0x346a1d00a4d26a2065fe1dc7d5af59a49ad6a8af25853ae2ec976c07349f48c1", + "outbound_tx_observed_external_height": 19271550, + "outbound_tx_gas_used": 21000, + "outbound_tx_effective_gas_price": "69197693654", + "outbound_tx_effective_gas_limit": 21000, + "tss_pubkey": "zetapub1addwnpepqtadxdyt037h86z60nl98t6zk56mw5zpnm79tsmvspln3hgt5phdc79kvfc", + "tx_finalization_status": 2 + } + ] +} diff --git a/zetaclient/testdata/evm/chain_1_receipt_ZetaSent_0xf3935200c80f98502d5edc7e871ffc40ca898e134525c42c2ae3cbc5725f9d76.json b/zetaclient/testdata/evm/chain_1_receipt_ZetaSent_0xf3935200c80f98502d5edc7e871ffc40ca898e134525c42c2ae3cbc5725f9d76.json new file mode 100644 index 0000000000..77db2b356f --- /dev/null +++ b/zetaclient/testdata/evm/chain_1_receipt_ZetaSent_0xf3935200c80f98502d5edc7e871ffc40ca898e134525c42c2ae3cbc5725f9d76.json @@ -0,0 +1,60 @@ +{ + "type": "0x2", + "root": "0x", + "status": "0x1", + "cumulativeGasUsed": "0xd980e6", + "logsBloom": "0x00000000000000000000000000000000001802000000000000000000080000000000000000000000000000000000000008000000000000000004000000200000000000000000000000000008000000000000000000000000000000000080080000000000100000000000040010000000000000000000000000000010000000000000000000000100000000000000000200000000000000000000000000000000020000000000000000000000000000000000000000000002000000000000000000000002000000000000000000000080000000000000000000200000000000000010100000000000000000000000000000000000000000000000002000000000", + "logs": [ + { + "address": "0xf091867ec603a6628ed83d274e835539d82e9cc8", + "topics": [ + "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef", + "0x0000000000000000000000002f993766e8e1ef9288b1f33f6aa244911a0a77a7", + "0x000000000000000000000000000007cf399229b2f5a4d043f20e90c9c98b7c6a" + ], + "data": "0x000000000000000000000000000000000000000000000001158e460913d00000", + "blockNumber": "0x12617e6", + "transactionHash": "0xf3935200c80f98502d5edc7e871ffc40ca898e134525c42c2ae3cbc5725f9d76", + "transactionIndex": "0xbd", + "blockHash": "0x68afbd4ae4a74a74e6ce0dd85f208a5422f453f19ef20b36443eb1bba2ba77fa", + "logIndex": "0x121", + "removed": false + }, + { + "address": "0xf091867ec603a6628ed83d274e835539d82e9cc8", + "topics": [ + "0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925", + "0x0000000000000000000000002f993766e8e1ef9288b1f33f6aa244911a0a77a7", + "0x000000000000000000000000000007cf399229b2f5a4d043f20e90c9c98b7c6a" + ], + "data": "0x00000000000000000000000000000000000000000001a783220f53d22e300000", + "blockNumber": "0x12617e6", + "transactionHash": "0xf3935200c80f98502d5edc7e871ffc40ca898e134525c42c2ae3cbc5725f9d76", + "transactionIndex": "0xbd", + "blockHash": "0x68afbd4ae4a74a74e6ce0dd85f208a5422f453f19ef20b36443eb1bba2ba77fa", + "logIndex": "0x122", + "removed": false + }, + { + "address": "0x000007cf399229b2f5a4d043f20e90c9c98b7c6a", + "topics": [ + "0x7ec1c94701e09b1652f3e1d307e60c4b9ebf99aff8c2079fd1d8c585e031c4e4", + "0x0000000000000000000000002f993766e8e1ef9288b1f33f6aa244911a0a77a7", + "0x0000000000000000000000000000000000000000000000000000000000001b58" + ], + "data": "0x0000000000000000000000002f993766e8e1ef9288b1f33f6aa244911a0a77a700000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000001158e460913d0000000000000000000000000000000000000000000000000000000000000000186a00000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000000142f993766e8e1ef9288b1f33f6aa244911a0a77a700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "blockNumber": "0x12617e6", + "transactionHash": "0xf3935200c80f98502d5edc7e871ffc40ca898e134525c42c2ae3cbc5725f9d76", + "transactionIndex": "0xbd", + "blockHash": "0x68afbd4ae4a74a74e6ce0dd85f208a5422f453f19ef20b36443eb1bba2ba77fa", + "logIndex": "0x123", + "removed": false + } + ], + "transactionHash": "0xf3935200c80f98502d5edc7e871ffc40ca898e134525c42c2ae3cbc5725f9d76", + "contractAddress": "0x0000000000000000000000000000000000000000", + "gasUsed": "0xd956", + "blockHash": "0x68afbd4ae4a74a74e6ce0dd85f208a5422f453f19ef20b36443eb1bba2ba77fa", + "blockNumber": "0x12617e6", + "transactionIndex": "0xbd" +} diff --git a/zetaclient/testutils/utils.go b/zetaclient/testutils/utils.go index 80d4bb00e7..e33bd4cef8 100644 --- a/zetaclient/testutils/utils.go +++ b/zetaclient/testutils/utils.go @@ -6,10 +6,15 @@ import ( "path/filepath" "github.com/btcsuite/btcd/btcjson" + "github.com/cosmos/cosmos-sdk/types" + "github.com/zeta-chain/zetacore/zetaclient/keys" + "github.com/zeta-chain/zetacore/zetaclient/zetabridge" ) const ( - TestDataPath = "testdata" + TestDataPathEVM = "testdata/evm" + TestDataPathBTC = "testdata/btc" + TestDataPathCctx = "testdata/cctx" ) // SaveObjectToJSONFile saves an object to a file in JSON format @@ -50,3 +55,14 @@ func SaveBTCBlockTrimTx(blockVb *btcjson.GetBlockVerboseTxResult, filename strin } return SaveObjectToJSONFile(blockVb, filename) } + +func DummyCoreBridge() *zetabridge.ZetaCoreBridge { + bridge, _ := zetabridge.NewZetaCoreBridge( + &keys.Keys{OperatorAddress: types.AccAddress{}}, + "127.0.0.1", + "", + "zetachain_7000-1", + false, + nil) + return bridge +} diff --git a/zetaclient/zetacore_observer.go b/zetaclient/zetacore_observer.go index d37e2967cf..e8cec47d74 100644 --- a/zetaclient/zetacore_observer.go +++ b/zetaclient/zetacore_observer.go @@ -257,7 +257,7 @@ func (co *CoreObserver) scheduleCctxEVM( } // try confirming the outtx - included, _, err := ob.IsSendOutTxProcessed(cctx.Index, params.OutboundTxTssNonce, params.CoinType, co.logger.ZetaChainWatcher) + included, _, err := ob.IsSendOutTxProcessed(cctx, co.logger.ZetaChainWatcher) if err != nil { co.logger.ZetaChainWatcher.Error().Err(err).Msgf("scheduleCctxEVM: IsSendOutTxProcessed faild for chain %d nonce %d", chainID, nonce) continue @@ -337,7 +337,7 @@ func (co *CoreObserver) scheduleCctxBTC( continue } // try confirming the outtx - included, confirmed, err := btcClient.IsSendOutTxProcessed(cctx.Index, nonce, params.CoinType, co.logger.ZetaChainWatcher) + included, confirmed, err := btcClient.IsSendOutTxProcessed(cctx, co.logger.ZetaChainWatcher) if err != nil { co.logger.ZetaChainWatcher.Error().Err(err).Msgf("scheduleCctxBTC: IsSendOutTxProcessed faild for chain %d nonce %d", chainID, nonce) continue From 508669436c5cbb70184175f9f1204414cf91a3ea Mon Sep 17 00:00:00 2001 From: Charlie Chen Date: Wed, 21 Feb 2024 22:31:37 -0600 Subject: [PATCH 02/13] fix some issues in e2e tests --- changelog.md | 1 + cmd/zetaclientd/init.go | 2 + zetaclient/bitcoin/bitcoin_client.go | 130 +++++++++++++++++---------- zetaclient/bitcoin/bitcoin_signer.go | 19 ++-- zetaclient/bitcoin/utils.go | 7 +- zetaclient/config/types.go | 2 +- zetaclient/evm/inbounds.go | 2 +- zetaclient/testutils/utils.go | 15 +++- 8 files changed, 116 insertions(+), 62 deletions(-) diff --git a/changelog.md b/changelog.md index f2fe3e0815..6f012cedf2 100644 --- a/changelog.md +++ b/changelog.md @@ -15,6 +15,7 @@ ### Features *[1728] (https://github.com/zeta-chain/node/pull/1728) - allow aborted transactions to be refunded by minting tokens to zEvm. +*[1789] (https://github.com/zeta-chain/node/pull/1789) - block cross-chain transactions that involved banned addresses ### Refactor * [1766](https://github.com/zeta-chain/node/pull/1766) - Refactors the `PostTxProcessing` EVM hook functionality to deal with invalid withdraw events diff --git a/cmd/zetaclientd/init.go b/cmd/zetaclientd/init.go index 3ae36cf66b..4af5a437b3 100644 --- a/cmd/zetaclientd/init.go +++ b/cmd/zetaclientd/init.go @@ -4,6 +4,7 @@ import ( "github.com/rs/zerolog" "github.com/spf13/cobra" "github.com/zeta-chain/zetacore/zetaclient/config" + "github.com/zeta-chain/zetacore/zetaclient/testutils" ) var InitCmd = &cobra.Command{ @@ -95,6 +96,7 @@ func Initialize(_ *cobra.Command, _ []string) error { configData.KeyringBackend = config.KeyringBackend(initArgs.KeyringBackend) configData.HsmMode = initArgs.HsmMode configData.HsmHotKey = initArgs.HsmHotKey + configData.ComplianceConfig = testutils.ComplianceConfigTest() //Save config file return config.Save(&configData, rootArgs.zetaCoreHome) diff --git a/zetaclient/bitcoin/bitcoin_client.go b/zetaclient/bitcoin/bitcoin_client.go index 0bb741a0e1..4938609014 100644 --- a/zetaclient/bitcoin/bitcoin_client.go +++ b/zetaclient/bitcoin/bitcoin_client.go @@ -513,13 +513,6 @@ func (ob *BTCChainClient) IsSendOutTxProcessed(cctx *types.CrossChainTx, logger res, included := ob.includedTxResults[outTxID] ob.Mu.Unlock() - // compliance check, special handling the cancelled cctx - banned := clientcommon.IsCctxBanned(cctx) - if banned { - params.Amount = cosmosmath.ZeroUint() // use 0 to bypass the amount check - logger.Warn().Msgf("IsSendOutTxProcessed: special handling banned outTx %s", outTxID) - } - if !included { if !broadcasted { return false, false, nil @@ -534,7 +527,7 @@ func (ob *BTCChainClient) IsSendOutTxProcessed(cctx *types.CrossChainTx, logger } // Try including this outTx broadcasted by myself - txResult, inMempool := ob.checkIncludedTx(txnHash, params) + txResult, inMempool := ob.checkIncludedTx(cctx, txnHash) if txResult == nil { // check failed, try again next time return false, false, nil } else if inMempool { // still in mempool (should avoid unnecessary Tss keysign) @@ -1107,16 +1100,9 @@ func (ob *BTCChainClient) observeOutTx() { ob.logger.ObserveOutTx.Info().Err(err).Msgf("observeOutTx: can't find cctx for nonce %d", tracker.Nonce) break } - params := *cctx.GetCurrentOutTxParam() - - // compliance check, special handling the cancelled cctx - banned := clientcommon.IsCctxBanned(cctx) - if banned { - params.Amount = cosmosmath.ZeroUint() // use 0 to bypass the amount check - ob.logger.ObserveOutTx.Warn().Msgf("observeOutTx: special handling banned outTx %s", outTxID) - } - if tracker.Nonce != params.OutboundTxTssNonce { // Tanmay: it doesn't hurt to check - ob.logger.ObserveOutTx.Error().Msgf("observeOutTx: tracker nonce %d not match cctx nonce %d", tracker.Nonce, params.OutboundTxTssNonce) + nonce := cctx.GetCurrentOutTxParam().OutboundTxTssNonce + if tracker.Nonce != nonce { // Tanmay: it doesn't hurt to check + ob.logger.ObserveOutTx.Error().Msgf("observeOutTx: tracker nonce %d not match cctx nonce %d", tracker.Nonce, nonce) break } if len(tracker.HashList) > 1 { @@ -1127,7 +1113,7 @@ func (ob *BTCChainClient) observeOutTx() { txCount := 0 var txResult *btcjson.GetTransactionResult for _, txHash := range tracker.HashList { - result, inMempool := ob.checkIncludedTx(txHash.TxHash, params) + result, inMempool := ob.checkIncludedTx(cctx, txHash.TxHash) if result != nil && !inMempool { // included txCount++ txResult = result @@ -1155,8 +1141,8 @@ func (ob *BTCChainClient) observeOutTx() { // checkIncludedTx checks if a txHash is included and returns (txResult, inMempool) // Note: if txResult is nil, then inMempool flag should be ignored. -func (ob *BTCChainClient) checkIncludedTx(txHash string, params types.OutboundTxParams) (*btcjson.GetTransactionResult, bool) { - outTxID := ob.GetTxID(params.OutboundTxTssNonce) +func (ob *BTCChainClient) checkIncludedTx(cctx *types.CrossChainTx, txHash string) (*btcjson.GetTransactionResult, bool) { + outTxID := ob.GetTxID(cctx.GetCurrentOutTxParam().OutboundTxTssNonce) hash, getTxResult, err := ob.GetTxResultByHash(txHash) if err != nil { ob.logger.ObserveOutTx.Error().Err(err).Msgf("checkIncludedTx: error GetTxResultByHash: %s", txHash) @@ -1167,7 +1153,7 @@ func (ob *BTCChainClient) checkIncludedTx(txHash string, params types.OutboundTx return nil, false } if getTxResult.Confirmations >= 0 { // check included tx only - err = ob.checkTssOutTxResult(hash, getTxResult, params, params.OutboundTxTssNonce) + err = ob.checkTssOutTxResult(cctx, hash, getTxResult) if err != nil { ob.logger.ObserveOutTx.Error().Err(err).Msgf("checkIncludedTx: error verify bitcoin outTx %s outTxID %s", txHash, outTxID) return nil, false @@ -1228,7 +1214,9 @@ func (ob *BTCChainClient) removeIncludedTx(nonce uint64) { // - check if all inputs are segwit && TSS inputs // // Returns: true if outTx passes basic checks. -func (ob *BTCChainClient) checkTssOutTxResult(hash *chainhash.Hash, res *btcjson.GetTransactionResult, params types.OutboundTxParams, nonce uint64) error { +func (ob *BTCChainClient) checkTssOutTxResult(cctx *types.CrossChainTx, hash *chainhash.Hash, res *btcjson.GetTransactionResult) error { + params := cctx.GetCurrentOutTxParam() + nonce := params.OutboundTxTssNonce rawResult, err := ob.getRawTxResult(hash, res) if err != nil { return errors.Wrapf(err, "checkTssOutTxResult: error GetRawTxResultByHash %s", hash.String()) @@ -1237,7 +1225,13 @@ func (ob *BTCChainClient) checkTssOutTxResult(hash *chainhash.Hash, res *btcjson if err != nil { return errors.Wrapf(err, "checkTssOutTxResult: invalid TSS Vin in outTx %s nonce %d", hash, nonce) } - err = ob.checkTSSVout(rawResult.Vout, params, nonce) + + // differentiate between normal and banned cctx + if clientcommon.IsCctxBanned(cctx) { + err = ob.checkTSSVoutCancelled(params, rawResult.Vout) + } else { + err = ob.checkTSSVout(params, rawResult.Vout) + } if err != nil { return errors.Wrapf(err, "checkTssOutTxResult: invalid TSS Vout in outTx %s nonce %d", hash, nonce) } @@ -1316,41 +1310,50 @@ func (ob *BTCChainClient) checkTSSVin(vins []btcjson.Vin, nonce uint64) error { return nil } +// DecodeP2WPKHVout decodes receiver and amount from P2WPKH output +func (ob *BTCChainClient) DecodeP2WPKHVout(vout btcjson.Vout) (string, int64, error) { + amount, err := GetSatoshis(vout.Value) + if err != nil { + return "", 0, errors.Wrap(err, "error getting satoshis") + } + // decode P2WPKH scriptPubKey + scriptPubKey := vout.ScriptPubKey.Hex + decodedScriptPubKey, err := hex.DecodeString(scriptPubKey) + if err != nil { + return "", 0, errors.Wrapf(err, "error decoding scriptPubKey %s", scriptPubKey) + } + if len(decodedScriptPubKey) != 22 { // P2WPKH script + return "", 0, fmt.Errorf("unsupported scriptPubKey: %s", scriptPubKey) + } + witnessVersion := decodedScriptPubKey[0] + witnessProgram := decodedScriptPubKey[2:] + if witnessVersion != 0 { + return "", 0, fmt.Errorf("unsupported witness in scriptPubKey %s", scriptPubKey) + } + recvAddress, err := ob.chain.BTCAddressFromWitnessProgram(witnessProgram) + if err != nil { + return "", 0, errors.Wrapf(err, "error getting receiver from witness program %s", witnessProgram) + } + return recvAddress, amount, nil +} + // checkTSSVout vout is valid if: // - The first output is the nonce-mark // - The second output is the correct payment to recipient // - The third output is the change to TSS (optional) -func (ob *BTCChainClient) checkTSSVout(vouts []btcjson.Vout, params types.OutboundTxParams, nonce uint64) error { +func (ob *BTCChainClient) checkTSSVout(params *types.OutboundTxParams, vouts []btcjson.Vout) error { // vouts: [nonce-mark, payment to recipient, change to TSS (optional)] if !(len(vouts) == 2 || len(vouts) == 3) { return fmt.Errorf("checkTSSVout: invalid number of vouts: %d", len(vouts)) } + nonce := params.OutboundTxTssNonce tssAddress := ob.Tss.BTCAddress() for _, vout := range vouts { - amount, err := GetSatoshis(vout.Value) - if err != nil { - return errors.Wrap(err, "checkTSSVout: error getting satoshis") - } - // decode P2WPKH scriptPubKey - scriptPubKey := vout.ScriptPubKey.Hex - decodedScriptPubKey, err := hex.DecodeString(scriptPubKey) - if err != nil { - return errors.Wrapf(err, "checkTSSVout: error decoding scriptPubKey %s", scriptPubKey) - } - if len(decodedScriptPubKey) != 22 { // P2WPKH script - return fmt.Errorf("checkTSSVout: unsupported scriptPubKey: %s", scriptPubKey) - } - witnessVersion := decodedScriptPubKey[0] - witnessProgram := decodedScriptPubKey[2:] - if witnessVersion != 0 { - return fmt.Errorf("checkTSSVout: unsupported witness in scriptPubKey %s", scriptPubKey) - } - recvAddress, err := ob.chain.BTCAddressFromWitnessProgram(witnessProgram) + recvAddress, amount, err := ob.DecodeP2WPKHVout(vout) if err != nil { - return errors.Wrapf(err, "checkTSSVout: error getting receiver from witness program %s", witnessProgram) + return errors.Wrap(err, "checkTSSVout: error decoding P2WPKH vout") } - // 1st vout: nonce-mark if vout.N == 0 { if recvAddress != tssAddress { @@ -1380,6 +1383,41 @@ func (ob *BTCChainClient) checkTSSVout(vouts []btcjson.Vout, params types.Outbou return nil } +// checkTSSVoutCancelled vout is valid if: +// - The first output is the nonce-mark +// - The second output is the change to TSS (optional) +func (ob *BTCChainClient) checkTSSVoutCancelled(params *types.OutboundTxParams, vouts []btcjson.Vout) error { + // vouts: [nonce-mark, change to TSS (optional)] + if !(len(vouts) == 1 || len(vouts) == 2) { + return fmt.Errorf("checkTSSVoutCancelled: invalid number of vouts: %d", len(vouts)) + } + + nonce := params.OutboundTxTssNonce + tssAddress := ob.Tss.BTCAddress() + for _, vout := range vouts { + recvAddress, amount, err := ob.DecodeP2WPKHVout(vout) + if err != nil { + return errors.Wrap(err, "checkTSSVoutCancelled: error decoding P2WPKH vout") + } + // 1st vout: nonce-mark + if vout.N == 0 { + if recvAddress != tssAddress { + return fmt.Errorf("checkTSSVoutCancelled: nonce-mark address %s not match TSS address %s", recvAddress, tssAddress) + } + if amount != common.NonceMarkAmount(nonce) { + return fmt.Errorf("checkTSSVoutCancelled: nonce-mark amount %d not match nonce-mark amount %d", amount, common.NonceMarkAmount(nonce)) + } + } + // 2nd vout: change to TSS (optional) + if vout.N == 2 { + if recvAddress != tssAddress { + return fmt.Errorf("checkTSSVoutCancelled: change address %s not match TSS address %s", recvAddress, tssAddress) + } + } + } + return nil +} + func (ob *BTCChainClient) BuildBroadcastedTxMap() error { var broadcastedTransactions []clienttypes.OutTxHashSQLType if err := ob.db.Find(&broadcastedTransactions).Error; err != nil { diff --git a/zetaclient/bitcoin/bitcoin_signer.go b/zetaclient/bitcoin/bitcoin_signer.go index 09124fea45..7fbb5b621d 100644 --- a/zetaclient/bitcoin/bitcoin_signer.go +++ b/zetaclient/bitcoin/bitcoin_signer.go @@ -81,6 +81,7 @@ func (signer *BTCSigner) SignWithdrawTx( height uint64, nonce uint64, chain *common.Chain, + cancelTx bool, ) (*wire.MsgTx, error) { estimateFee := float64(gasPrice.Uint64()*outTxBytesMax) / 1e8 nonceMark := common.NonceMarkAmount(nonce) @@ -160,12 +161,14 @@ func (signer *BTCSigner) SignWithdrawTx( tx.AddTxOut(txOut1) // 2nd output: the payment to the recipient - pkScript, err := PayToWitnessPubKeyHashScript(to.WitnessProgram()) - if err != nil { - return nil, err + if !cancelTx { + pkScript, err := PayToWitnessPubKeyHashScript(to.WitnessProgram()) + if err != nil { + return nil, err + } + txOut2 := wire.NewTxOut(amountSatoshis, pkScript) + tx.AddTxOut(txOut2) } - txOut2 := wire.NewTxOut(amountSatoshis, pkScript) - tx.AddTxOut(txOut2) // 3rd output: the remaining btc to TSS self if remainingSats > 0 { @@ -322,12 +325,13 @@ func (signer *BTCSigner) TryProcessOutTx( gasprice.Add(gasprice, satPerByte) // compliance check - if clientcommon.IsCctxBanned(cctx) { + cancelTx := clientcommon.IsCctxBanned(cctx) + if cancelTx { logMsg := fmt.Sprintf("Banned address detected in cctx: sender %s receiver %s chain %d nonce %d", cctx.InboundTxParams.Sender, to, params.ReceiverChainId, outboundTxTssNonce) logger.Warn().Msg(logMsg) signer.loggerCompliance.Warn().Msg(logMsg) - amount = 0 // zero out the amount to cancel the tx + amount = 0.0 // zero out the amount to cancel the tx } logger.Info().Msgf("SignWithdrawTx: to %s, value %d sats", addr.EncodeAddress(), params.Amount.Uint64()) @@ -342,6 +346,7 @@ func (signer *BTCSigner) TryProcessOutTx( height, outboundTxTssNonce, &btcClient.chain, + cancelTx, ) if err != nil { logger.Warn().Err(err).Msgf("SignOutboundTx error: nonce %d chain %d", outboundTxTssNonce, params.ReceiverChainId) diff --git a/zetaclient/bitcoin/utils.go b/zetaclient/bitcoin/utils.go index 2b90e21280..e6acae0763 100644 --- a/zetaclient/bitcoin/utils.go +++ b/zetaclient/bitcoin/utils.go @@ -20,7 +20,6 @@ import ( ) const ( - satoshiPerBitcoin = 1e8 bytesPerKB = 1000 bytesEmptyTx = 10 // an empty tx is about 10 bytes bytesPerInput = 41 // each input is about 41 bytes @@ -60,7 +59,7 @@ func PrettyPrintStruct(val interface{}) (string, error) { // FeeRateToSatPerByte converts a fee rate in BTC/KB to sat/byte. func FeeRateToSatPerByte(rate float64) *big.Int { // #nosec G701 always in range - satPerKB := new(big.Int).SetInt64(int64(rate * satoshiPerBitcoin)) + satPerKB := new(big.Int).SetInt64(int64(rate * btcutil.SatoshiPerBitcoin)) return new(big.Int).Div(satPerKB, big.NewInt(bytesPerKB)) } @@ -102,7 +101,7 @@ func SegWitTxSizeWithdrawer() uint64 { // DepositorFee calculates the depositor fee in BTC for a given sat/byte fee rate // Note: the depositor fee is charged in order to cover the cost of spending the deposited UTXO in the future func DepositorFee(satPerByte int64) float64 { - return float64(satPerByte) * float64(BtcOutTxBytesDepositor) / satoshiPerBitcoin + return float64(satPerByte) * float64(BtcOutTxBytesDepositor) / btcutil.SatoshiPerBitcoin } // CalcBlockAvgFeeRate calculates the average gas rate (in sat/vByte) for a given block @@ -209,7 +208,7 @@ func GetSatoshis(btc float64) (int64, error) { case btc < 0.0: return 0, errors.New("cannot be less than zero") } - return round(btc * satoshiPerBitcoin), nil + return round(btc * btcutil.SatoshiPerBitcoin), nil } func round(f float64) int64 { diff --git a/zetaclient/config/types.go b/zetaclient/config/types.go index 6756fbd8c4..e936f7dcb2 100644 --- a/zetaclient/config/types.go +++ b/zetaclient/config/types.go @@ -163,7 +163,7 @@ func (c *Config) GetBannedAddressBook() map[string]bool { bannedAddresses := make(map[string]bool) if c.ComplianceConfig != nil { for _, address := range c.ComplianceConfig.BannedAddresses { - if ethcommon.IsHexAddress(address) { + if address != "" { bannedAddresses[strings.ToLower(address)] = true } } diff --git a/zetaclient/evm/inbounds.go b/zetaclient/evm/inbounds.go index 5616d177d9..8264e0e3dd 100644 --- a/zetaclient/evm/inbounds.go +++ b/zetaclient/evm/inbounds.go @@ -303,7 +303,7 @@ func (ob *ChainClient) GetInboundVoteMsgForDepositedEvent(event *erc20custody.ER if err == nil && parsedAddress != (ethcommon.Address{}) { maybeReceiver = parsedAddress.Hex() } - if config.AnyBannedAddress(sender.Hex(), maybeReceiver) { + if config.AnyBannedAddress(sender.Hex(), clienttypes.BytesToEthHex(event.Recipient), maybeReceiver) { logMsg := fmt.Sprintf("Banned address detected in ERC20 Deposited event: sender %s receiver %s tx %s chain %d", sender, maybeReceiver, event.Raw.TxHash, ob.chain.ChainId) ob.logger.ExternalChainWatcher.Warn().Msg(logMsg) diff --git a/zetaclient/testutils/utils.go b/zetaclient/testutils/utils.go index e33bd4cef8..2c80b38b35 100644 --- a/zetaclient/testutils/utils.go +++ b/zetaclient/testutils/utils.go @@ -7,14 +7,17 @@ import ( "github.com/btcsuite/btcd/btcjson" "github.com/cosmos/cosmos-sdk/types" + "github.com/zeta-chain/zetacore/zetaclient/config" "github.com/zeta-chain/zetacore/zetaclient/keys" "github.com/zeta-chain/zetacore/zetaclient/zetabridge" ) const ( - TestDataPathEVM = "testdata/evm" - TestDataPathBTC = "testdata/btc" - TestDataPathCctx = "testdata/cctx" + TestDataPathEVM = "testdata/evm" + TestDataPathBTC = "testdata/btc" + TestDataPathCctx = "testdata/cctx" + BannedEVMAddressTest = "0x8a81Ba8eCF2c418CAe624be726F505332DF119C6" + BannedBtcAddressTest = "bcrt1qzp4gt6fc7zkds09kfzaf9ln9c5rvrzxmy6qmpp" ) // SaveObjectToJSONFile saves an object to a file in JSON format @@ -66,3 +69,9 @@ func DummyCoreBridge() *zetabridge.ZetaCoreBridge { nil) return bridge } + +func ComplianceConfigTest() *config.ComplianceConfig { + return &config.ComplianceConfig{ + BannedAddresses: []string{BannedEVMAddressTest, BannedBtcAddressTest}, + } +} From dfabbd16b557395f0df6e188c6fac9f3384c254e Mon Sep 17 00:00:00 2001 From: Charlie Chen Date: Wed, 21 Feb 2024 22:34:19 -0600 Subject: [PATCH 03/13] added e2e tests for banned addresses --- cmd/zetae2e/local/bitcoin.go | 1 + cmd/zetae2e/local/erc20.go | 1 + cmd/zetae2e/local/ethereum.go | 2 + cmd/zetae2e/local/utils.go | 2 +- cmd/zetae2e/local/zeta.go | 1 + e2e/e2etests/e2etests.go | 18 ++++++--- e2e/e2etests/test_bitcoin_withdraw.go | 54 ++++++++++++++++++------- e2e/e2etests/test_crosschain_swap.go | 2 +- e2e/e2etests/test_erc20_deposit.go | 7 +++- e2e/e2etests/test_erc20_refund.go | 2 +- e2e/e2etests/test_eth_withdraw.go | 43 ++++++++++++++++++++ e2e/e2etests/test_stress_eth_deposit.go | 2 +- e2e/e2etests/test_zeta_deposit.go | 7 +++- e2e/runner/evm.go | 6 +-- e2e/runner/zeta.go | 6 +-- 15 files changed, 122 insertions(+), 32 deletions(-) diff --git a/cmd/zetae2e/local/bitcoin.go b/cmd/zetae2e/local/bitcoin.go index 2cd39f9c53..f82963285a 100644 --- a/cmd/zetae2e/local/bitcoin.go +++ b/cmd/zetae2e/local/bitcoin.go @@ -66,6 +66,7 @@ func bitcoinTestRoutine( // to make it faster to catch up with the latest block header if err := bitcoinRunner.RunE2ETestsFromNames( e2etests.AllE2ETests, + e2etests.TestBitcoinDepositName, e2etests.TestBitcoinWithdrawName, e2etests.TestZetaWithdrawBTCRevertName, e2etests.TestCrosschainSwapName, diff --git a/cmd/zetae2e/local/erc20.go b/cmd/zetae2e/local/erc20.go index c3dbc3f354..2ae8041a67 100644 --- a/cmd/zetae2e/local/erc20.go +++ b/cmd/zetae2e/local/erc20.go @@ -62,6 +62,7 @@ func erc20TestRoutine( // run erc20 test if err := erc20Runner.RunE2ETestsFromNames( e2etests.AllE2ETests, + e2etests.TestERC20DepositName, e2etests.TestERC20WithdrawName, e2etests.TestMultipleWithdrawsName, e2etests.TestERC20DepositAndCallRefundName, diff --git a/cmd/zetae2e/local/ethereum.go b/cmd/zetae2e/local/ethereum.go index 31e0e33b37..3f3e4d3cf7 100644 --- a/cmd/zetae2e/local/ethereum.go +++ b/cmd/zetae2e/local/ethereum.go @@ -55,7 +55,9 @@ func ethereumTestRoutine( // to make it faster to catch up with the latest block header if err := ethereumRunner.RunE2ETestsFromNames( e2etests.AllE2ETests, + e2etests.TestEtherDepositName, e2etests.TestEtherWithdrawName, + e2etests.TestEtherWithdrawBannedName, e2etests.TestContextUpgradeName, e2etests.TestEtherDepositAndCallName, e2etests.TestDepositAndCallRefundName, diff --git a/cmd/zetae2e/local/utils.go b/cmd/zetae2e/local/utils.go index f59782183c..c24acbe171 100644 --- a/cmd/zetae2e/local/utils.go +++ b/cmd/zetae2e/local/utils.go @@ -85,7 +85,7 @@ func waitKeygenHeight( logger *runner.Logger, ) { // wait for keygen to be completed - keygenHeight := int64(60) + keygenHeight := int64(55) logger.Print("⏳ wait height %v for keygen to be completed", keygenHeight) for { time.Sleep(2 * time.Second) diff --git a/cmd/zetae2e/local/zeta.go b/cmd/zetae2e/local/zeta.go index ac7243f7fb..e45a6215fd 100644 --- a/cmd/zetae2e/local/zeta.go +++ b/cmd/zetae2e/local/zeta.go @@ -59,6 +59,7 @@ func zetaTestRoutine( // run zeta test if err := zetaRunner.RunE2ETestsFromNames( e2etests.AllE2ETests, + e2etests.TestZetaDepositName, e2etests.TestZetaWithdrawName, e2etests.TestMessagePassingName, e2etests.TestMessagePassingRevertFailName, diff --git a/e2e/e2etests/e2etests.go b/e2e/e2etests/e2etests.go index ef6e1cbb74..76cd9e0c98 100644 --- a/e2e/e2etests/e2etests.go +++ b/e2e/e2etests/e2etests.go @@ -26,12 +26,13 @@ const ( TestDepositEtherLiquidityCapName = "deposit_eth_liquidity_cap" TestMyTestName = "my_test" - TestERC20WithdrawName = "erc20_withdraw" - TestERC20DepositName = "erc20_deposit" - TestEtherDepositName = "eth_deposit" - TestEtherWithdrawName = "eth_withdraw" - TestBitcoinDepositName = "bitcoin_deposit" - TestZetaDepositName = "zeta_deposit" + TestERC20WithdrawName = "erc20_withdraw" + TestERC20DepositName = "erc20_deposit" + TestEtherDepositName = "eth_deposit" + TestEtherWithdrawName = "eth_withdraw" + TestEtherWithdrawBannedName = "eth_withdraw_banned" + TestBitcoinDepositName = "bitcoin_deposit" + TestZetaDepositName = "zeta_deposit" TestDonationEtherName = "donation_ether" @@ -158,6 +159,11 @@ var AllE2ETests = []runner.E2ETest{ "withdraw Ether from ZEVM", TestEtherWithdraw, }, + { + TestEtherWithdrawBannedName, + "withdraw Ether from ZEVM to banned address", + TestEtherWithdrawBanned, + }, { TestBitcoinDepositName, "deposit Bitcoin into ZEVM", diff --git a/e2e/e2etests/test_bitcoin_withdraw.go b/e2e/e2etests/test_bitcoin_withdraw.go index f70a4653d2..43d5078e8b 100644 --- a/e2e/e2etests/test_bitcoin_withdraw.go +++ b/e2e/e2etests/test_bitcoin_withdraw.go @@ -4,22 +4,31 @@ import ( "fmt" "math/big" + "github.com/btcsuite/btcd/btcjson" "github.com/btcsuite/btcd/chaincfg/chainhash" "github.com/btcsuite/btcutil" + "github.com/zeta-chain/zetacore/common" "github.com/zeta-chain/zetacore/e2e/runner" "github.com/zeta-chain/zetacore/e2e/utils" + "github.com/zeta-chain/zetacore/zetaclient/testutils" ) func TestBitcoinWithdraw(r *runner.E2ERunner) { - // withdraw 0.1 BTC from ZRC20 to BTC address - // first, approve the ZRC20 contract to spend 1 BTC from the deployer address + // start mining blocks + stop := r.MineBlocks() + + // withdraw 0.01 BTC from ZRC20 to BTC address WithdrawBitcoin(r) -} -func WithdrawBitcoin(r *runner.E2ERunner) { - amount := big.NewInt(0.1 * btcutil.SatoshiPerBitcoin) + // withdraw 0.01 BTC from ZRC20 to BTC banned address + WithdrawBitcoinBanned(r) + + // stop mining + stop <- struct{}{} +} - // approve the ZRC20 contract to spend 1 BTC from the deployer address +func withdrawBTCZRC20(r *runner.E2ERunner, to btcutil.Address, amount *big.Int) *btcjson.TxRawResult { + // approve the ZRC20 contract to spend 'amount' of BTC from the deployer address tx, err := r.BTCZRC20.Approve(r.ZevmAuth, r.BTCZRC20Addr, big.NewInt(amount.Int64()*2)) // approve more to cover withdraw fee if err != nil { panic(err) @@ -29,11 +38,8 @@ func WithdrawBitcoin(r *runner.E2ERunner) { panic(fmt.Errorf("approve receipt status is not 1")) } - // mine blocks - stop := r.MineBlocks() - - // withdraw 0.1 BTC from ZRC20 to BTC address - tx, err = r.BTCZRC20.Withdraw(r.ZevmAuth, []byte(r.BTCDeployerAddress.EncodeAddress()), amount) + // withdraw 'amount' of BTC from ZRC20 to BTC address + tx, err = r.BTCZRC20.Withdraw(r.ZevmAuth, []byte(to.EncodeAddress()), amount) if err != nil { panic(err) } @@ -43,7 +49,7 @@ func WithdrawBitcoin(r *runner.E2ERunner) { } // mine 10 blocks to confirm the withdraw tx - _, err = r.BtcRPCClient.GenerateToAddress(10, r.BTCDeployerAddress, nil) + _, err = r.BtcRPCClient.GenerateToAddress(10, to, nil) if err != nil { panic(err) } @@ -73,8 +79,28 @@ func WithdrawBitcoin(r *runner.E2ERunner) { r.Logger.Info(" ScriptPubKey: %s", txOut.ScriptPubKey.Hex) } - // stop mining - stop <- struct{}{} + return rawTx +} + +func WithdrawBitcoin(r *runner.E2ERunner) { + amount := big.NewInt(0.01 * btcutil.SatoshiPerBitcoin) + withdrawBTCZRC20(r, r.BTCDeployerAddress, amount) +} + +func WithdrawBitcoinBanned(r *runner.E2ERunner) { + amount := big.NewInt(0.01 * btcutil.SatoshiPerBitcoin) + + // use banned BTC P2WPKH address + addressBanned, err := common.DecodeBtcAddress(testutils.BannedBtcAddressTest, common.BtcRegtestChain().ChainId) + if err != nil { + panic(err) + } + + // the cctx should be cancelled + rawTx := withdrawBTCZRC20(r, addressBanned, amount) + if len(rawTx.Vout) != 2 { + panic(fmt.Errorf("BTC cancelled outtx rawTx.Vout should have 2 outputs")) + } } // WithdrawBitcoinMultipleTimes ... diff --git a/e2e/e2etests/test_crosschain_swap.go b/e2e/e2etests/test_crosschain_swap.go index 5772d185b9..e492a75908 100644 --- a/e2e/e2etests/test_crosschain_swap.go +++ b/e2e/e2etests/test_crosschain_swap.go @@ -89,7 +89,7 @@ func TestCrosschainSwap(r *runner.E2ERunner) { r.Logger.Info("***** First test: USDT -> BTC") // Should deposit USDT for swap, swap for BTC and withdraw BTC - txHash := r.DepositERC20WithAmountAndMessage(big.NewInt(8e7), msg) + txHash := r.DepositERC20WithAmountAndMessage(r.DeployerAddress, big.NewInt(8e7), msg) cctx1 := utils.WaitCctxMinedByInTxHash(r.Ctx, txHash.Hex(), r.CctxClient, r.Logger, r.CctxTimeout) // check the cctx status diff --git a/e2e/e2etests/test_erc20_deposit.go b/e2e/e2etests/test_erc20_deposit.go index bd91ac72d6..3f45987fd9 100644 --- a/e2e/e2etests/test_erc20_deposit.go +++ b/e2e/e2etests/test_erc20_deposit.go @@ -3,14 +3,19 @@ package e2etests import ( "math/big" + ethcommon "github.com/ethereum/go-ethereum/common" "github.com/zeta-chain/zetacore/e2e/runner" "github.com/zeta-chain/zetacore/e2e/utils" + "github.com/zeta-chain/zetacore/zetaclient/testutils" ) func TestERC20Deposit(r *runner.E2ERunner) { - hash := r.DepositERC20WithAmountAndMessage(big.NewInt(100000), []byte{}) + hash := r.DepositERC20WithAmountAndMessage(r.DeployerAddress, big.NewInt(100000), []byte{}) // wait for the cctx to be mined cctx := utils.WaitCctxMinedByInTxHash(r.Ctx, hash.Hex(), r.CctxClient, r.Logger, r.CctxTimeout) r.Logger.CCTX(*cctx, "deposit") + + // deposit ERC20 to banned address + r.DepositERC20WithAmountAndMessage(ethcommon.HexToAddress(testutils.BannedEVMAddressTest), big.NewInt(100000), []byte{}) } diff --git a/e2e/e2etests/test_erc20_refund.go b/e2e/e2etests/test_erc20_refund.go index 252c76162d..d4fec2b59e 100644 --- a/e2e/e2etests/test_erc20_refund.go +++ b/e2e/e2etests/test_erc20_refund.go @@ -137,7 +137,7 @@ func TestERC20DepositAndCallRefund(r *runner.E2ERunner) { func createZetaERC20LiquidityPool(r *runner.E2ERunner) error { amount := big.NewInt(1e10) - txHash := r.DepositERC20WithAmountAndMessage(amount, []byte{}) + txHash := r.DepositERC20WithAmountAndMessage(r.DeployerAddress, amount, []byte{}) utils.WaitCctxMinedByInTxHash(r.Ctx, txHash.Hex(), r.CctxClient, r.Logger, r.CctxTimeout) tx, err := r.USDTZRC20.Approve(r.ZevmAuth, r.UniswapV2RouterAddr, big.NewInt(1e10)) diff --git a/e2e/e2etests/test_eth_withdraw.go b/e2e/e2etests/test_eth_withdraw.go index 1f6cd59897..bb5c423336 100644 --- a/e2e/e2etests/test_eth_withdraw.go +++ b/e2e/e2etests/test_eth_withdraw.go @@ -3,9 +3,11 @@ package e2etests import ( "math/big" + ethcommon "github.com/ethereum/go-ethereum/common" "github.com/zeta-chain/zetacore/e2e/runner" "github.com/zeta-chain/zetacore/e2e/utils" crosschaintypes "github.com/zeta-chain/zetacore/x/crosschain/types" + "github.com/zeta-chain/zetacore/zetaclient/testutils" ) // TestEtherWithdraw tests the withdraw of ether @@ -44,3 +46,44 @@ func TestEtherWithdraw(r *runner.E2ERunner) { panic("cctx status is not outbound mined") } } + +// TestEtherWithdrawBanned tests the withdrawal to a banned receiver address +func TestEtherWithdrawBanned(r *runner.E2ERunner) { + // approve + tx, err := r.ETHZRC20.Approve(r.ZevmAuth, r.ETHZRC20Addr, big.NewInt(1e18)) + if err != nil { + panic(err) + } + r.Logger.EVMTransaction(*tx, "approve") + + receipt := utils.MustWaitForTxReceipt(r.Ctx, r.ZevmClient, tx, r.Logger, r.ReceiptTimeout) + if receipt.Status == 0 { + panic("approve failed") + } + r.Logger.EVMReceipt(*receipt, "approve") + + // withdraw + bannedAddress := ethcommon.HexToAddress(testutils.BannedEVMAddressTest) + tx, err = r.ETHZRC20.Withdraw(r.ZevmAuth, bannedAddress.Bytes(), big.NewInt(100000)) + if err != nil { + panic(err) + } + r.Logger.EVMTransaction(*tx, "withdraw to banned address") + + receipt = utils.MustWaitForTxReceipt(r.Ctx, r.ZevmClient, tx, r.Logger, r.ReceiptTimeout) + if receipt.Status == 0 { + panic("withdraw failed") + } + r.Logger.EVMReceipt(*receipt, "withdraw") + r.Logger.ZRC20Withdrawal(r.ETHZRC20, *receipt, "withdraw") + + // verify the withdraw value + cctx := utils.WaitCctxMinedByInTxHash(r.Ctx, receipt.TxHash.Hex(), r.CctxClient, r.Logger, r.CctxTimeout) + r.Logger.CCTX(*cctx, "withdraw") + if cctx.CctxStatus.Status != crosschaintypes.CctxStatus_OutboundMined { + panic("cctx status is not outbound mined") + } + + // the cctx should be cancelled with zero value + verifyTransferAmountFromCCTX(r, cctx, 0) +} diff --git a/e2e/e2etests/test_stress_eth_deposit.go b/e2e/e2etests/test_stress_eth_deposit.go index ea5f3919f5..b568b460d5 100644 --- a/e2e/e2etests/test_stress_eth_deposit.go +++ b/e2e/e2etests/test_stress_eth_deposit.go @@ -25,7 +25,7 @@ func TestStressEtherDeposit(r *runner.E2ERunner) { // send the deposits for i := 0; i < numDeposits; i++ { i := i - hash := r.DepositERC20WithAmountAndMessage(big.NewInt(100000), []byte{}) + hash := r.DepositERC20WithAmountAndMessage(r.DeployerAddress, big.NewInt(100000), []byte{}) r.Logger.Print("index %d: starting deposit, tx hash: %s", i, hash.Hex()) eg.Go(func() error { diff --git a/e2e/e2etests/test_zeta_deposit.go b/e2e/e2etests/test_zeta_deposit.go index ac58399a21..658e5dfcbe 100644 --- a/e2e/e2etests/test_zeta_deposit.go +++ b/e2e/e2etests/test_zeta_deposit.go @@ -3,15 +3,20 @@ package e2etests import ( "math/big" + ethcommon "github.com/ethereum/go-ethereum/common" "github.com/zeta-chain/zetacore/e2e/runner" "github.com/zeta-chain/zetacore/e2e/utils" + "github.com/zeta-chain/zetacore/zetaclient/testutils" ) func TestZetaDeposit(r *runner.E2ERunner) { // Deposit 1 Zeta - hash := r.DepositZetaWithAmount(big.NewInt(1e18)) + hash := r.DepositZetaWithAmount(r.DeployerAddress, big.NewInt(1e18)) // wait for the cctx to be mined cctx := utils.WaitCctxMinedByInTxHash(r.Ctx, hash.Hex(), r.CctxClient, r.Logger, r.CctxTimeout) r.Logger.CCTX(*cctx, "deposit") + + // Deposit 1 Zeta to banned address + r.DepositZetaWithAmount(ethcommon.HexToAddress(testutils.BannedEVMAddressTest), big.NewInt(1e18)) } diff --git a/e2e/runner/evm.go b/e2e/runner/evm.go index 75eaf75c2b..47035e61f3 100644 --- a/e2e/runner/evm.go +++ b/e2e/runner/evm.go @@ -72,10 +72,10 @@ func (runner *E2ERunner) SendUSDTOnEvm(address ethcommon.Address, amountUSDT int func (runner *E2ERunner) DepositERC20() ethcommon.Hash { runner.Logger.Print("⏳ depositing ERC20 into ZEVM") - return runner.DepositERC20WithAmountAndMessage(big.NewInt(1e18), []byte{}) + return runner.DepositERC20WithAmountAndMessage(runner.DeployerAddress, big.NewInt(1e18), []byte{}) } -func (runner *E2ERunner) DepositERC20WithAmountAndMessage(amount *big.Int, msg []byte) ethcommon.Hash { +func (runner *E2ERunner) DepositERC20WithAmountAndMessage(to ethcommon.Address, amount *big.Int, msg []byte) ethcommon.Hash { // reset allowance, necessary for USDT tx, err := runner.USDTERC20.Approve(runner.GoerliAuth, runner.ERC20CustodyAddr, big.NewInt(0)) if err != nil { @@ -97,7 +97,7 @@ func (runner *E2ERunner) DepositERC20WithAmountAndMessage(amount *big.Int, msg [ } runner.Logger.Info("USDT Approve receipt tx hash: %s", tx.Hash().Hex()) - tx, err = runner.ERC20Custody.Deposit(runner.GoerliAuth, runner.DeployerAddress.Bytes(), runner.USDTERC20Addr, amount, msg) + tx, err = runner.ERC20Custody.Deposit(runner.GoerliAuth, to.Bytes(), runner.USDTERC20Addr, amount, msg) runner.Logger.Print("TX: %v", tx) if err != nil { panic(err) diff --git a/e2e/runner/zeta.go b/e2e/runner/zeta.go index b5df8145d9..e4bbf99ccc 100644 --- a/e2e/runner/zeta.go +++ b/e2e/runner/zeta.go @@ -51,11 +51,11 @@ func (runner *E2ERunner) DepositZeta() ethcommon.Hash { amount := big.NewInt(1e18) amount = amount.Mul(amount, big.NewInt(100)) // 100 Zeta - return runner.DepositZetaWithAmount(amount) + return runner.DepositZetaWithAmount(runner.DeployerAddress, amount) } // DepositZetaWithAmount deposits ZETA on ZetaChain from the ZETA smart contract on EVM with the specified amount -func (runner *E2ERunner) DepositZetaWithAmount(amount *big.Int) ethcommon.Hash { +func (runner *E2ERunner) DepositZetaWithAmount(to ethcommon.Address, amount *big.Int) ethcommon.Hash { tx, err := runner.ZetaEth.Approve(runner.GoerliAuth, runner.ConnectorEthAddr, amount) if err != nil { panic(err) @@ -78,7 +78,7 @@ func (runner *E2ERunner) DepositZetaWithAmount(amount *big.Int) ethcommon.Hash { // TODO: allow user to specify destination chain id // https://github.com/zeta-chain/node-private/issues/41 DestinationChainId: zetaChainID, - DestinationAddress: runner.DeployerAddress.Bytes(), + DestinationAddress: to.Bytes(), DestinationGasLimit: big.NewInt(250_000), Message: nil, ZetaValueAndGas: amount, From d7f7c826dca501150f7f6e29bf2250f9c225033e Mon Sep 17 00:00:00 2001 From: Charlie Chen Date: Wed, 21 Feb 2024 22:39:53 -0600 Subject: [PATCH 04/13] fix gosec issues --- cmd/zetaclientd/main.go | 2 +- zetaclient/testutils/utils.go | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/cmd/zetaclientd/main.go b/cmd/zetaclientd/main.go index 5fea730b1e..767b1fd78e 100644 --- a/cmd/zetaclientd/main.go +++ b/cmd/zetaclientd/main.go @@ -108,5 +108,5 @@ func OpenComplianceLogFile(cfg *config.Config) (*os.File, error) { name = filepath.Clean(name) // open (or create) compliance log file - return os.OpenFile(name, os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0644) + return os.OpenFile(name, os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0600) } diff --git a/zetaclient/testutils/utils.go b/zetaclient/testutils/utils.go index 2c80b38b35..e154e0467c 100644 --- a/zetaclient/testutils/utils.go +++ b/zetaclient/testutils/utils.go @@ -60,13 +60,16 @@ func SaveBTCBlockTrimTx(blockVb *btcjson.GetBlockVerboseTxResult, filename strin } func DummyCoreBridge() *zetabridge.ZetaCoreBridge { - bridge, _ := zetabridge.NewZetaCoreBridge( + bridge, err := zetabridge.NewZetaCoreBridge( &keys.Keys{OperatorAddress: types.AccAddress{}}, "127.0.0.1", "", "zetachain_7000-1", false, nil) + if err != nil { + panic(err) + } return bridge } From cf72c1bfc036951b792b8d3d7b327fbb7b53cc32 Mon Sep 17 00:00:00 2001 From: Charlie Chen Date: Thu, 22 Feb 2024 23:35:31 -0600 Subject: [PATCH 05/13] some unit tests, comments and refactor --- changelog.md | 2 +- zetaclient/bitcoin/bitcoin_client.go | 72 +++----- zetaclient/bitcoin/bitcoin_client_test.go | 168 +++++++++++++++++- zetaclient/bitcoin/bitcoin_signer.go | 4 +- zetaclient/bitcoin/utils.go | 27 +++ zetaclient/bitcoin/utils_test.go | 165 ++++++++--------- zetaclient/common/utils.go | 5 +- zetaclient/common/utils_test.go | 34 ++-- zetaclient/config/config.go | 12 +- zetaclient/config/types.go | 16 +- zetaclient/evm/evm_client.go | 2 +- zetaclient/evm/evm_client_test.go | 94 ++++++++++ zetaclient/evm/evm_signer.go | 4 +- zetaclient/evm/inbounds.go | 12 +- zetaclient/evm/inbounds_test.go | 36 ++-- ...n => block_mempool.space_8332_828440.json} | 0 ...40.json => block_trimmed_8332_828440.json} | 0 .../btc/outtx_8332_148_raw_result.json | 111 ++++++++++++ zetaclient/testdata/cctx/cctx_8332_148.json | 32 ++++ zetaclient/testutils/constant.go | 9 + zetaclient/testutils/mock.go | 72 ++++++++ zetaclient/testutils/utils.go | 29 +-- 22 files changed, 685 insertions(+), 221 deletions(-) rename zetaclient/testdata/btc/{chain_8332_mempool.space_block_828440.json => block_mempool.space_8332_828440.json} (100%) rename zetaclient/testdata/btc/{chain_8332_block_trimmed_828440.json => block_trimmed_8332_828440.json} (100%) create mode 100644 zetaclient/testdata/btc/outtx_8332_148_raw_result.json create mode 100644 zetaclient/testdata/cctx/cctx_8332_148.json create mode 100644 zetaclient/testutils/constant.go create mode 100644 zetaclient/testutils/mock.go diff --git a/changelog.md b/changelog.md index ab37885c0c..8b4f72204e 100644 --- a/changelog.md +++ b/changelog.md @@ -13,6 +13,7 @@ ### Features * [1698](https://github.com/zeta-chain/node/issues/1698) - bitcoin dynamic depositor fee +* [1789](https://github.com/zeta-chain/node/issues/1789) - block cross-chain transactions that involve restricted addresses ### Docs @@ -21,7 +22,6 @@ ### Features *[1728] (https://github.com/zeta-chain/node/pull/1728) - allow aborted transactions to be refunded by minting tokens to zEvm. -*[1789] (https://github.com/zeta-chain/node/pull/1789) - block cross-chain transactions that involved banned addresses ### Refactor * [1766](https://github.com/zeta-chain/node/pull/1766) - Refactors the `PostTxProcessing` EVM hook functionality to deal with invalid withdraw events diff --git a/zetaclient/bitcoin/bitcoin_client.go b/zetaclient/bitcoin/bitcoin_client.go index 4938609014..ad2e7a0ca9 100644 --- a/zetaclient/bitcoin/bitcoin_client.go +++ b/zetaclient/bitcoin/bitcoin_client.go @@ -23,6 +23,7 @@ import ( "github.com/btcsuite/btcd/rpcclient" "github.com/btcsuite/btcd/wire" "github.com/btcsuite/btcutil" + ethcommon "github.com/ethereum/go-ethereum/common" lru "github.com/hashicorp/golang-lru" "github.com/pkg/errors" "github.com/rs/zerolog" @@ -686,15 +687,7 @@ func (ob *BTCChainClient) GetInboundVoteMessageFromBtcEvent(inTx *BTCInTxEvnet) message := hex.EncodeToString(inTx.MemoBytes) // compliance check - receiver := "" - if parsedAddress, _, err := common.ParseAddressAndData(message); err == nil { - receiver = parsedAddress.Hex() - } - if config.AnyBannedAddress(inTx.FromAddress, receiver) { - logMsg := fmt.Sprintf("Banned address detected in token sent to TSS: sender %s receiver %s tx %s chain %d", - inTx.FromAddress, receiver, inTx.TxHash, ob.chain.ChainId) - ob.logger.WatchInTx.Warn().Msg(logMsg) - ob.logger.Compliance.Warn().Msg(logMsg) + if ob.IsInTxRestricted(inTx) { return nil } @@ -716,6 +709,23 @@ func (ob *BTCChainClient) GetInboundVoteMessageFromBtcEvent(inTx *BTCInTxEvnet) ) } +// IsInTxRestricted returns true if the inTx contains restricted addresses +func (ob *BTCChainClient) IsInTxRestricted(inTx *BTCInTxEvnet) bool { + receiver := "" + parsedAddress, _, err := common.ParseAddressAndData(hex.EncodeToString(inTx.MemoBytes)) + if err == nil && parsedAddress != (ethcommon.Address{}) { + receiver = parsedAddress.Hex() + } + if config.ContainRestrictedAddress(inTx.FromAddress, receiver) { + logMsg := fmt.Sprintf("Restricted address detected in token sent to TSS: sender %s receiver %s tx %s chain %d", + inTx.FromAddress, receiver, inTx.TxHash, ob.chain.ChainId) + ob.logger.WatchInTx.Warn().Msg(logMsg) + ob.logger.Compliance.Warn().Msg(logMsg) + return true + } + return false +} + func GetBtcEvent( tx btcjson.TxRawResult, targetAddress string, @@ -1226,14 +1236,17 @@ func (ob *BTCChainClient) checkTssOutTxResult(cctx *types.CrossChainTx, hash *ch return errors.Wrapf(err, "checkTssOutTxResult: invalid TSS Vin in outTx %s nonce %d", hash, nonce) } - // differentiate between normal and banned cctx - if clientcommon.IsCctxBanned(cctx) { + // differentiate between normal and restricted cctx + if clientcommon.IsCctxRestricted(cctx) { err = ob.checkTSSVoutCancelled(params, rawResult.Vout) + if err != nil { + return errors.Wrapf(err, "checkTssOutTxResult: invalid TSS Vout in cancelled outTx %s nonce %d", hash, nonce) + } } else { err = ob.checkTSSVout(params, rawResult.Vout) - } - if err != nil { - return errors.Wrapf(err, "checkTssOutTxResult: invalid TSS Vout in outTx %s nonce %d", hash, nonce) + if err != nil { + return errors.Wrapf(err, "checkTssOutTxResult: invalid TSS Vout in outTx %s nonce %d", hash, nonce) + } } return nil } @@ -1310,33 +1323,6 @@ func (ob *BTCChainClient) checkTSSVin(vins []btcjson.Vin, nonce uint64) error { return nil } -// DecodeP2WPKHVout decodes receiver and amount from P2WPKH output -func (ob *BTCChainClient) DecodeP2WPKHVout(vout btcjson.Vout) (string, int64, error) { - amount, err := GetSatoshis(vout.Value) - if err != nil { - return "", 0, errors.Wrap(err, "error getting satoshis") - } - // decode P2WPKH scriptPubKey - scriptPubKey := vout.ScriptPubKey.Hex - decodedScriptPubKey, err := hex.DecodeString(scriptPubKey) - if err != nil { - return "", 0, errors.Wrapf(err, "error decoding scriptPubKey %s", scriptPubKey) - } - if len(decodedScriptPubKey) != 22 { // P2WPKH script - return "", 0, fmt.Errorf("unsupported scriptPubKey: %s", scriptPubKey) - } - witnessVersion := decodedScriptPubKey[0] - witnessProgram := decodedScriptPubKey[2:] - if witnessVersion != 0 { - return "", 0, fmt.Errorf("unsupported witness in scriptPubKey %s", scriptPubKey) - } - recvAddress, err := ob.chain.BTCAddressFromWitnessProgram(witnessProgram) - if err != nil { - return "", 0, errors.Wrapf(err, "error getting receiver from witness program %s", witnessProgram) - } - return recvAddress, amount, nil -} - // checkTSSVout vout is valid if: // - The first output is the nonce-mark // - The second output is the correct payment to recipient @@ -1350,7 +1336,7 @@ func (ob *BTCChainClient) checkTSSVout(params *types.OutboundTxParams, vouts []b nonce := params.OutboundTxTssNonce tssAddress := ob.Tss.BTCAddress() for _, vout := range vouts { - recvAddress, amount, err := ob.DecodeP2WPKHVout(vout) + recvAddress, amount, err := DecodeP2WPKHVout(vout, ob.chain) if err != nil { return errors.Wrap(err, "checkTSSVout: error decoding P2WPKH vout") } @@ -1395,7 +1381,7 @@ func (ob *BTCChainClient) checkTSSVoutCancelled(params *types.OutboundTxParams, nonce := params.OutboundTxTssNonce tssAddress := ob.Tss.BTCAddress() for _, vout := range vouts { - recvAddress, amount, err := ob.DecodeP2WPKHVout(vout) + recvAddress, amount, err := DecodeP2WPKHVout(vout, ob.chain) if err != nil { return errors.Wrap(err, "checkTSSVoutCancelled: error decoding P2WPKH vout") } diff --git a/zetaclient/bitcoin/bitcoin_client_test.go b/zetaclient/bitcoin/bitcoin_client_test.go index f3e68aa9e4..b9a29db816 100644 --- a/zetaclient/bitcoin/bitcoin_client_test.go +++ b/zetaclient/bitcoin/bitcoin_client_test.go @@ -16,10 +16,31 @@ import ( "github.com/rs/zerolog/log" "github.com/stretchr/testify/require" "github.com/zeta-chain/zetacore/common" + crosschaintypes "github.com/zeta-chain/zetacore/x/crosschain/types" observertypes "github.com/zeta-chain/zetacore/x/observer/types" "github.com/zeta-chain/zetacore/zetaclient/testutils" ) +func MockBTCClientMainnet() *BTCChainClient { + return &BTCChainClient{ + chain: common.BtcMainnetChain(), + zetaClient: testutils.MockCoreBridge(), + Tss: testutils.NewMockTSSMainnet(), + } +} + +// LoadTxRawResultNCctx loads archived outtx raw result and corresponding cctx +func LoadTxRawResultNCctx(t *testing.T, fileTxResult string, fileCctx string) (btcjson.TxRawResult, *crosschaintypes.CrossChainTx) { + var rawResult btcjson.TxRawResult + err := testutils.LoadObjectFromJSONFile(&rawResult, path.Join("../", testutils.TestDataPathBTC, "outtx_8332_148_raw_result.json")) + require.NoError(t, err) + + var cctx crosschaintypes.CrossChainTx + err = testutils.LoadObjectFromJSONFile(&cctx, path.Join("../", testutils.TestDataPathCctx, "cctx_8332_148.json")) + require.NoError(t, err) + return rawResult, &cctx +} + func TestConfirmationThreshold(t *testing.T) { client := &BTCChainClient{Mu: &sync.Mutex{}} t.Run("should return confirmations in chain param", func(t *testing.T) { @@ -41,12 +62,12 @@ func TestConfirmationThreshold(t *testing.T) { func TestAvgFeeRateBlock828440(t *testing.T) { // load archived block 828440 var blockVb btcjson.GetBlockVerboseTxResult - err := testutils.LoadObjectFromJSONFile(&blockVb, path.Join("../", testutils.TestDataPathBTC, "chain_8332_block_trimmed_828440.json")) + err := testutils.LoadObjectFromJSONFile(&blockVb, path.Join("../", testutils.TestDataPathBTC, "block_trimmed_8332_828440.json")) require.NoError(t, err) // https://mempool.space/block/000000000000000000025ca01d2c1094b8fd3bacc5468cc3193ced6a14618c27 var blockMb testutils.MempoolBlock - err = testutils.LoadObjectFromJSONFile(&blockMb, path.Join("../", testutils.TestDataPathBTC, "chain_8332_mempool.space_block_828440.json")) + err = testutils.LoadObjectFromJSONFile(&blockMb, path.Join("../", testutils.TestDataPathBTC, "block_mempool.space_8332_828440.json")) require.NoError(t, err) gasRate, err := CalcBlockAvgFeeRate(&blockVb, &chaincfg.MainNetParams) @@ -57,7 +78,7 @@ func TestAvgFeeRateBlock828440(t *testing.T) { func TestAvgFeeRateBlock828440Errors(t *testing.T) { // load archived block 828440 var blockVb btcjson.GetBlockVerboseTxResult - err := testutils.LoadObjectFromJSONFile(&blockVb, path.Join("../", testutils.TestDataPathBTC, "chain_8332_block_trimmed_828440.json")) + err := testutils.LoadObjectFromJSONFile(&blockVb, path.Join("../", testutils.TestDataPathBTC, "block_trimmed_8332_828440.json")) require.NoError(t, err) t.Run("block has no transactions", func(t *testing.T) { @@ -144,7 +165,7 @@ func TestAvgFeeRateBlock828440Errors(t *testing.T) { func TestCalcDepositorFee828440(t *testing.T) { // load archived block 828440 var blockVb btcjson.GetBlockVerboseTxResult - err := testutils.LoadObjectFromJSONFile(&blockVb, path.Join("../", testutils.TestDataPathBTC, "chain_8332_block_trimmed_828440.json")) + err := testutils.LoadObjectFromJSONFile(&blockVb, path.Join("../", testutils.TestDataPathBTC, "block_trimmed_8332_828440.json")) require.NoError(t, err) dynamicFee828440 := DepositorFee(32 * common.DefaultGasPriceMultiplier) @@ -168,3 +189,142 @@ func TestCalcDepositorFee828440(t *testing.T) { require.NotEqual(t, DefaultDepositorFee, fee) require.Equal(t, dynamicFee828440, fee) } + +func TestCheckTSSVout(t *testing.T) { + // the archived outtx raw result file and cctx file + // https://blockstream.info/tx/030cd813443f7b70cc6d8a544d320c6d8465e4528fc0f3410b599dc0b26753a0 + fileCctx := path.Join("../", testutils.TestDataPathCctx, "cctx_8332_148.json") + fileTxResult := path.Join("../", testutils.TestDataPathBTC, "outtx_8332_148_raw_result.json") + + // create mainnet mock client + btcClient := MockBTCClientMainnet() + + t.Run("valid TSS vout should pass", func(t *testing.T) { + rawResult, cctx := LoadTxRawResultNCctx(t, fileTxResult, fileCctx) + params := cctx.GetCurrentOutTxParam() + err := btcClient.checkTSSVout(params, rawResult.Vout) + require.NoError(t, err) + }) + t.Run("should fail if vout length < 2 or > 3", func(t *testing.T) { + _, cctx := LoadTxRawResultNCctx(t, fileTxResult, fileCctx) + params := cctx.GetCurrentOutTxParam() + + err := btcClient.checkTSSVout(params, []btcjson.Vout{{}}) + require.ErrorContains(t, err, "invalid number of vouts") + + err = btcClient.checkTSSVout(params, []btcjson.Vout{{}, {}, {}, {}}) + require.ErrorContains(t, err, "invalid number of vouts") + }) + t.Run("should fail if vout 0 is not to the TSS address", func(t *testing.T) { + rawResult, cctx := LoadTxRawResultNCctx(t, fileTxResult, fileCctx) + params := cctx.GetCurrentOutTxParam() + + // not TSS address, bc1qh297vdt8xq6df5xae9z8gzd4jsu9a392mp0dus + rawResult.Vout[0].ScriptPubKey.Hex = "0014ba8be635673034d4d0ddc9447409b594385ec4aa" + err := btcClient.checkTSSVout(params, rawResult.Vout) + require.ErrorContains(t, err, "not match TSS address") + }) + t.Run("should fail if vout 0 not match nonce mark", func(t *testing.T) { + rawResult, cctx := LoadTxRawResultNCctx(t, fileTxResult, fileCctx) + params := cctx.GetCurrentOutTxParam() + + // not match nonce mark + rawResult.Vout[0].Value = 0.00000147 + err := btcClient.checkTSSVout(params, rawResult.Vout) + require.ErrorContains(t, err, "not match nonce-mark amount") + }) + t.Run("should fail if vout 1 is not to the receiver address", func(t *testing.T) { + rawResult, cctx := LoadTxRawResultNCctx(t, fileTxResult, fileCctx) + params := cctx.GetCurrentOutTxParam() + + // not receiver address, bc1qh297vdt8xq6df5xae9z8gzd4jsu9a392mp0dus + rawResult.Vout[1].ScriptPubKey.Hex = "0014ba8be635673034d4d0ddc9447409b594385ec4aa" + err := btcClient.checkTSSVout(params, rawResult.Vout) + require.ErrorContains(t, err, "not match params receiver") + }) + t.Run("should fail if vout 1 not match payment amount", func(t *testing.T) { + rawResult, cctx := LoadTxRawResultNCctx(t, fileTxResult, fileCctx) + params := cctx.GetCurrentOutTxParam() + + // not match payment amount + rawResult.Vout[1].Value = 0.00011000 + err := btcClient.checkTSSVout(params, rawResult.Vout) + require.ErrorContains(t, err, "not match params amount") + }) + t.Run("should fail if vout 2 is not to the TSS address", func(t *testing.T) { + rawResult, cctx := LoadTxRawResultNCctx(t, fileTxResult, fileCctx) + params := cctx.GetCurrentOutTxParam() + + // not TSS address, bc1qh297vdt8xq6df5xae9z8gzd4jsu9a392mp0dus + rawResult.Vout[2].ScriptPubKey.Hex = "0014ba8be635673034d4d0ddc9447409b594385ec4aa" + err := btcClient.checkTSSVout(params, rawResult.Vout) + require.ErrorContains(t, err, "not match TSS address") + }) +} + +func TestCheckTSSVoutCancelled(t *testing.T) { + // the archived outtx raw result file and cctx file + // https://blockstream.info/tx/030cd813443f7b70cc6d8a544d320c6d8465e4528fc0f3410b599dc0b26753a0 + fileCctx := path.Join("../", testutils.TestDataPathCctx, "cctx_8332_148.json") + fileTxResult := path.Join("../", testutils.TestDataPathBTC, "outtx_8332_148_raw_result.json") + + // create mainnet mock client + btcClient := MockBTCClientMainnet() + + t.Run("valid TSS vout should pass", func(t *testing.T) { + // remove change vout to simulate cancelled tx + rawResult, cctx := LoadTxRawResultNCctx(t, fileTxResult, fileCctx) + rawResult.Vout[1] = rawResult.Vout[2] + rawResult.Vout = rawResult.Vout[:2] + params := cctx.GetCurrentOutTxParam() + + err := btcClient.checkTSSVoutCancelled(params, rawResult.Vout) + require.NoError(t, err) + }) + t.Run("should fail if vout length < 1 or > 2", func(t *testing.T) { + _, cctx := LoadTxRawResultNCctx(t, fileTxResult, fileCctx) + params := cctx.GetCurrentOutTxParam() + + err := btcClient.checkTSSVoutCancelled(params, []btcjson.Vout{}) + require.ErrorContains(t, err, "invalid number of vouts") + + err = btcClient.checkTSSVoutCancelled(params, []btcjson.Vout{{}, {}, {}}) + require.ErrorContains(t, err, "invalid number of vouts") + }) + t.Run("should fail if vout 0 is not to the TSS address", func(t *testing.T) { + // remove change vout to simulate cancelled tx + rawResult, cctx := LoadTxRawResultNCctx(t, fileTxResult, fileCctx) + rawResult.Vout[1] = rawResult.Vout[2] + rawResult.Vout = rawResult.Vout[:2] + params := cctx.GetCurrentOutTxParam() + + // not TSS address, bc1qh297vdt8xq6df5xae9z8gzd4jsu9a392mp0dus + rawResult.Vout[0].ScriptPubKey.Hex = "0014ba8be635673034d4d0ddc9447409b594385ec4aa" + err := btcClient.checkTSSVoutCancelled(params, rawResult.Vout) + require.ErrorContains(t, err, "not match TSS address") + }) + t.Run("should fail if vout 0 not match nonce mark", func(t *testing.T) { + // remove change vout to simulate cancelled tx + rawResult, cctx := LoadTxRawResultNCctx(t, fileTxResult, fileCctx) + rawResult.Vout[1] = rawResult.Vout[2] + rawResult.Vout = rawResult.Vout[:2] + params := cctx.GetCurrentOutTxParam() + + // not match nonce mark + rawResult.Vout[0].Value = 0.00000147 + err := btcClient.checkTSSVoutCancelled(params, rawResult.Vout) + require.ErrorContains(t, err, "not match nonce-mark amount") + }) + t.Run("should fail if vout 1 is not to the TSS address", func(t *testing.T) { + // remove change vout to simulate cancelled tx + rawResult, cctx := LoadTxRawResultNCctx(t, fileTxResult, fileCctx) + rawResult.Vout[1] = rawResult.Vout[2] + rawResult.Vout = rawResult.Vout[:2] + params := cctx.GetCurrentOutTxParam() + + // not TSS address, bc1qh297vdt8xq6df5xae9z8gzd4jsu9a392mp0dus + rawResult.Vout[1].ScriptPubKey.Hex = "0014ba8be635673034d4d0ddc9447409b594385ec4aa" + err := btcClient.checkTSSVoutCancelled(params, rawResult.Vout) + require.ErrorContains(t, err, "not match TSS address") + }) +} diff --git a/zetaclient/bitcoin/bitcoin_signer.go b/zetaclient/bitcoin/bitcoin_signer.go index 7fbb5b621d..e3fb1c59fd 100644 --- a/zetaclient/bitcoin/bitcoin_signer.go +++ b/zetaclient/bitcoin/bitcoin_signer.go @@ -325,9 +325,9 @@ func (signer *BTCSigner) TryProcessOutTx( gasprice.Add(gasprice, satPerByte) // compliance check - cancelTx := clientcommon.IsCctxBanned(cctx) + cancelTx := clientcommon.IsCctxRestricted(cctx) if cancelTx { - logMsg := fmt.Sprintf("Banned address detected in cctx: sender %s receiver %s chain %d nonce %d", + logMsg := fmt.Sprintf("Restricted address detected in cctx: sender %s receiver %s chain %d nonce %d", cctx.InboundTxParams.Sender, to, params.ReceiverChainId, outboundTxTssNonce) logger.Warn().Msg(logMsg) signer.loggerCompliance.Warn().Msg(logMsg) diff --git a/zetaclient/bitcoin/utils.go b/zetaclient/bitcoin/utils.go index e6acae0763..b11c1a7056 100644 --- a/zetaclient/bitcoin/utils.go +++ b/zetaclient/bitcoin/utils.go @@ -223,3 +223,30 @@ func round(f float64) int64 { func PayToWitnessPubKeyHashScript(pubKeyHash []byte) ([]byte, error) { return txscript.NewScriptBuilder().AddOp(txscript.OP_0).AddData(pubKeyHash).Script() } + +// DecodeP2WPKHVout decodes receiver and amount from P2WPKH output +func DecodeP2WPKHVout(vout btcjson.Vout, chain common.Chain) (string, int64, error) { + amount, err := GetSatoshis(vout.Value) + if err != nil { + return "", 0, errors.Wrap(err, "error getting satoshis") + } + // decode P2WPKH scriptPubKey + scriptPubKey := vout.ScriptPubKey.Hex + decodedScriptPubKey, err := hex.DecodeString(scriptPubKey) + if err != nil { + return "", 0, errors.Wrapf(err, "error decoding scriptPubKey %s", scriptPubKey) + } + if len(decodedScriptPubKey) != 22 { // P2WPKH script + return "", 0, fmt.Errorf("unsupported scriptPubKey: %s", scriptPubKey) + } + witnessVersion := decodedScriptPubKey[0] + witnessProgram := decodedScriptPubKey[2:] + if witnessVersion != 0 { + return "", 0, fmt.Errorf("unsupported witness in scriptPubKey %s", scriptPubKey) + } + recvAddress, err := chain.BTCAddressFromWitnessProgram(witnessProgram) + if err != nil { + return "", 0, errors.Wrapf(err, "error getting receiver from witness program %s", witnessProgram) + } + return recvAddress, amount, nil +} diff --git a/zetaclient/bitcoin/utils_test.go b/zetaclient/bitcoin/utils_test.go index ab6692e111..56c7e0d967 100644 --- a/zetaclient/bitcoin/utils_test.go +++ b/zetaclient/bitcoin/utils_test.go @@ -1,104 +1,83 @@ package bitcoin import ( - "fmt" + "path" "testing" - "github.com/zeta-chain/zetacore/zetaclient/evm" - - ethcommon "github.com/ethereum/go-ethereum/common" - ethtypes "github.com/ethereum/go-ethereum/core/types" + "github.com/btcsuite/btcd/btcjson" "github.com/stretchr/testify/require" + "github.com/zeta-chain/zetacore/common" + "github.com/zeta-chain/zetacore/zetaclient/testutils" ) -func TestCheckEvmTxLog(t *testing.T) { - // test data - connectorAddr := ethcommon.HexToAddress("0x00005e3125aba53c5652f9f0ce1a4cf91d8b15ea") - txHash := "0xb252c9e77feafdeeae25cc1f037a16c4b50fa03c494754b99a7339d816c79626" - topics := []ethcommon.Hash{ - // https://goerli.etherscan.io/tx/0xb252c9e77feafdeeae25cc1f037a16c4b50fa03c494754b99a7339d816c79626#eventlog - ethcommon.HexToHash("0x7ec1c94701e09b1652f3e1d307e60c4b9ebf99aff8c2079fd1d8c585e031c4e4"), - ethcommon.HexToHash("0x00000000000000000000000023856df5d563bd893fc7df864102d8bbfe7fc487"), - ethcommon.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000061"), - } +func TestDecodeP2WPKHVout(t *testing.T) { + // load archived outtx raw result + // https://blockstream.info/tx/030cd813443f7b70cc6d8a544d320c6d8465e4528fc0f3410b599dc0b26753a0 + var rawResult btcjson.TxRawResult + err := testutils.LoadObjectFromJSONFile(&rawResult, path.Join("../", testutils.TestDataPathBTC, "outtx_8332_148_raw_result.json")) + require.NoError(t, err) + require.Len(t, rawResult.Vout, 3) + + // it's a mainnet outtx + chain := common.BtcMainnetChain() + nonce := uint64(148) + + // decode vout 0, nonce mark 148 + receiver, amount, err := DecodeP2WPKHVout(rawResult.Vout[0], chain) + require.NoError(t, err) + require.Equal(t, testutils.TSSAddressBTCMainnet, receiver) + require.Equal(t, common.NonceMarkAmount(nonce), amount) + + // decode vout 1, payment 0.00012000 BTC + receiver, amount, err = DecodeP2WPKHVout(rawResult.Vout[1], chain) + require.NoError(t, err) + require.Equal(t, "bc1qpsdlklfcmlcfgm77c43x65ddtrt7n0z57hsyjp", receiver) + require.Equal(t, int64(12000), amount) + + // decode vout 2, change 0.39041489 BTC + receiver, amount, err = DecodeP2WPKHVout(rawResult.Vout[2], chain) + require.NoError(t, err) + require.Equal(t, testutils.TSSAddressBTCMainnet, receiver) + require.Equal(t, int64(39041489), amount) +} + +func TestDecodeP2WPKHVoutErrors(t *testing.T) { + // load archived outtx raw result + // https://blockstream.info/tx/030cd813443f7b70cc6d8a544d320c6d8465e4528fc0f3410b599dc0b26753a0 + var rawResult btcjson.TxRawResult + err := testutils.LoadObjectFromJSONFile(&rawResult, path.Join("../", testutils.TestDataPathBTC, "outtx_8332_148_raw_result.json")) + require.NoError(t, err) - tests := []struct { - name string - vLog *ethtypes.Log - fail bool - }{ - { - name: "chain reorganization", - vLog: ðtypes.Log{ - Removed: true, - Address: connectorAddr, - TxHash: ethcommon.HexToHash(txHash), - Topics: topics, - }, - fail: true, - }, - { - name: "emitter address mismatch", - vLog: ðtypes.Log{ - Removed: false, - Address: ethcommon.HexToAddress("0x184ba627DB853244c9f17f3Cb4378cB8B39bf147"), - TxHash: ethcommon.HexToHash(txHash), - Topics: topics, - }, - fail: true, - }, - { - name: "tx hash mismatch", - vLog: ðtypes.Log{ - Removed: false, - Address: connectorAddr, - TxHash: ethcommon.HexToHash("0x781c018d604af4dad0fe5e3cea4ad9fb949a996d8cd0cd04a92cadd7f08c05f2"), - Topics: topics, - }, - fail: true, - }, - { - name: "topics mismatch", - vLog: ðtypes.Log{ - Removed: false, - Address: connectorAddr, - TxHash: ethcommon.HexToHash(txHash), - Topics: []ethcommon.Hash{ - // https://goerli.etherscan.io/tx/0xb252c9e77feafdeeae25cc1f037a16c4b50fa03c494754b99a7339d816c79626#eventlog - ethcommon.HexToHash("0x7ec1c94701e09b1652f3e1d307e60c4b9ebf99aff8c2079fd1d8c585e031c4e4"), - ethcommon.HexToHash("0x00000000000000000000000023856df5d563bd893fc7df864102d8bbfe7fc487"), - }, - }, - fail: true, - }, - { - name: "should pass", - vLog: ðtypes.Log{ - Removed: false, - Address: connectorAddr, - TxHash: ethcommon.HexToHash(txHash), - Topics: topics, - }, - fail: false, - }, - } + chain := common.BtcMainnetChain() - evmClient := evm.ChainClient{} - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - fmt.Printf("check test: %s\n", tt.name) - err := evmClient.CheckEvmTxLog( - tt.vLog, - connectorAddr, - "0xb252c9e77feafdeeae25cc1f037a16c4b50fa03c494754b99a7339d816c79626", - evm.TopicsZetaSent, - ) - if tt.fail { - require.Error(t, err) - return - } else { - require.NoError(t, err) - } - }) - } + t.Run("should return error on invalid amount", func(t *testing.T) { + invalidVout := rawResult.Vout[0] + invalidVout.Value = -0.5 // negative amount, should not happen + _, _, err := DecodeP2WPKHVout(invalidVout, chain) + require.Error(t, err) + require.ErrorContains(t, err, "error getting satoshis") + }) + t.Run("should return error on invalid script", func(t *testing.T) { + invalidVout := rawResult.Vout[0] + invalidVout.ScriptPubKey.Hex = "invalid script" + _, _, err := DecodeP2WPKHVout(invalidVout, chain) + require.Error(t, err) + require.ErrorContains(t, err, "error decoding scriptPubKey") + }) + t.Run("should return error on unsupported script", func(t *testing.T) { + invalidVout := rawResult.Vout[0] + // can use any invalid script, https://blockstream.info/tx/e95c6ff206103716129c8e3aa8def1427782af3490589d1ea35ccf0122adbc25 (P2SH) + invalidVout.ScriptPubKey.Hex = "a91413b2388e6532653a4b369b7e4ed130f7b81626cc87" + _, _, err := DecodeP2WPKHVout(invalidVout, chain) + require.Error(t, err) + require.ErrorContains(t, err, "unsupported scriptPubKey") + }) + t.Run("should return error on unsupported witness version", func(t *testing.T) { + invalidVout := rawResult.Vout[0] + // use a fake witness version 1, even if version 0 is the only witness version defined in BIP141 + invalidVout.ScriptPubKey.Hex = "01140c1bfb7d38dff0946fdec5626d51ad58d7e9bc54" + _, _, err := DecodeP2WPKHVout(invalidVout, chain) + require.Error(t, err) + require.ErrorContains(t, err, "unsupported witness in scriptPubKey") + }) } diff --git a/zetaclient/common/utils.go b/zetaclient/common/utils.go index ee26a97796..415592a5f2 100644 --- a/zetaclient/common/utils.go +++ b/zetaclient/common/utils.go @@ -19,8 +19,9 @@ func DefaultLoggers() ClientLogger { } } -func IsCctxBanned(cctx *crosschaintypes.CrossChainTx) bool { +// IsCctxRestricted returns true if the cctx involves restricted addresses +func IsCctxRestricted(cctx *crosschaintypes.CrossChainTx) bool { sender := cctx.InboundTxParams.Sender receiver := cctx.GetCurrentOutTxParam().Receiver - return config.AnyBannedAddress(sender, receiver) + return config.ContainRestrictedAddress(sender, receiver) } diff --git a/zetaclient/common/utils_test.go b/zetaclient/common/utils_test.go index 9285230a78..41b3f0c4cf 100644 --- a/zetaclient/common/utils_test.go +++ b/zetaclient/common/utils_test.go @@ -11,7 +11,7 @@ import ( "github.com/zeta-chain/zetacore/zetaclient/testutils" ) -func TestCctxBanned(t *testing.T) { +func TestCctxRestricted(t *testing.T) { // load archived cctx var cctx crosschaintypes.CrossChainTx err := testutils.LoadObjectFromJSONFile(&cctx, path.Join("../", testutils.TestDataPathCctx, "cctx_1_6270.json")) @@ -22,26 +22,32 @@ func TestCctxBanned(t *testing.T) { ComplianceConfig: &config.ComplianceConfig{}, } - t.Run("should return true if sender is banned", func(t *testing.T) { - cfg.ComplianceConfig.BannedAddresses = []string{cctx.InboundTxParams.Sender} + t.Run("should return true if sender is restricted", func(t *testing.T) { + cfg.ComplianceConfig.RestrictedAddresses = []string{cctx.InboundTxParams.Sender} config.LoadComplianceConfig(cfg) - require.True(t, IsCctxBanned(&cctx)) + require.True(t, IsCctxRestricted(&cctx)) }) - t.Run("should return true if receiver is banned", func(t *testing.T) { - cfg.ComplianceConfig.BannedAddresses = []string{cctx.GetCurrentOutTxParam().Receiver} + t.Run("should return true if receiver is restricted", func(t *testing.T) { + cfg.ComplianceConfig.RestrictedAddresses = []string{cctx.GetCurrentOutTxParam().Receiver} config.LoadComplianceConfig(cfg) - require.True(t, IsCctxBanned(&cctx)) + require.True(t, IsCctxRestricted(&cctx)) }) - t.Run("should return false if sender and receiver are not banned", func(t *testing.T) { - // ban other address - cfg.ComplianceConfig.BannedAddresses = []string{"0x27104b8dB4aEdDb054fCed87c346C0758Ff5dFB1"} + t.Run("should return false if sender and receiver are not restricted", func(t *testing.T) { + // restrict other address + cfg.ComplianceConfig.RestrictedAddresses = []string{"0x27104b8dB4aEdDb054fCed87c346C0758Ff5dFB1"} config.LoadComplianceConfig(cfg) - require.False(t, IsCctxBanned(&cctx)) + require.False(t, IsCctxRestricted(&cctx)) }) - t.Run("should be able to ban coinbase address", func(t *testing.T) { - cfg.ComplianceConfig.BannedAddresses = []string{ethcommon.Address{}.String()} + t.Run("should be able to restrict coinbase address", func(t *testing.T) { + cfg.ComplianceConfig.RestrictedAddresses = []string{ethcommon.Address{}.String()} config.LoadComplianceConfig(cfg) cctx.InboundTxParams.Sender = ethcommon.Address{}.String() - require.True(t, IsCctxBanned(&cctx)) + require.True(t, IsCctxRestricted(&cctx)) + }) + t.Run("should ignore empty address", func(t *testing.T) { + cfg.ComplianceConfig.RestrictedAddresses = []string{""} + config.LoadComplianceConfig(cfg) + cctx.InboundTxParams.Sender = "" + require.False(t, IsCctxRestricted(&cctx)) }) } diff --git a/zetaclient/config/config.go b/zetaclient/config/config.go index b18331006e..78c2d6ae9a 100644 --- a/zetaclient/config/config.go +++ b/zetaclient/config/config.go @@ -8,8 +8,8 @@ import ( "strings" ) -// bannedAddressBook is a map of banned addresses -var bannedAddressBook = map[string]bool{} +// restrictedAddressBook is a map of restricted addresses +var restrictedAddressBook = map[string]bool{} const filename string = "zetaclient_config.json" const folder string = "config" @@ -77,7 +77,7 @@ func Load(path string) (*Config, error) { } func LoadComplianceConfig(cfg *Config) { - bannedAddressBook = cfg.GetBannedAddressBook() + restrictedAddressBook = cfg.GetRestrictedAddressBook() } func GetPath(inputPath string) string { @@ -94,9 +94,11 @@ func GetPath(inputPath string) string { return filepath.Join(path...) } -func AnyBannedAddress(addrs ...string) bool { +// ContainRestrictedAddress returns true if any one of the addresses is restricted +// Note: the addrs can contains both ETH and BTC addresses +func ContainRestrictedAddress(addrs ...string) bool { for _, addr := range addrs { - if addr != "" && bannedAddressBook[strings.ToLower(addr)] { + if addr != "" && restrictedAddressBook[strings.ToLower(addr)] { return true } } diff --git a/zetaclient/config/types.go b/zetaclient/config/types.go index e936f7dcb2..e2a9ccc1c2 100644 --- a/zetaclient/config/types.go +++ b/zetaclient/config/types.go @@ -48,8 +48,8 @@ type BTCConfig struct { } type ComplianceConfig struct { - LogPath string `json:"LogPath"` - BannedAddresses []string `json:"BannedAddresses"` + LogPath string `json:"LogPath"` + RestrictedAddresses []string `json:"RestrictedAddresses"` } // Config is the config for ZetaClient @@ -159,16 +159,18 @@ func (c *Config) GetBTCConfig() (common.Chain, BTCConfig, bool) { return *chain, *c.BitcoinConfig, true } -func (c *Config) GetBannedAddressBook() map[string]bool { - bannedAddresses := make(map[string]bool) +// GetRestrictedAddressBook returns a map of restricted addresses +// Note: the restricted address book contains both ETH and BTC addresses +func (c *Config) GetRestrictedAddressBook() map[string]bool { + restrictedAddresses := make(map[string]bool) if c.ComplianceConfig != nil { - for _, address := range c.ComplianceConfig.BannedAddresses { + for _, address := range c.ComplianceConfig.RestrictedAddresses { if address != "" { - bannedAddresses[strings.ToLower(address)] = true + restrictedAddresses[strings.ToLower(address)] = true } } } - return bannedAddresses + return restrictedAddresses } func (c *Config) GetKeyringBackend() KeyringBackend { diff --git a/zetaclient/evm/evm_client.go b/zetaclient/evm/evm_client.go index 7da1725fbe..8e6a17a37c 100644 --- a/zetaclient/evm/evm_client.go +++ b/zetaclient/evm/evm_client.go @@ -360,7 +360,7 @@ func (ob *ChainClient) IsSendOutTxProcessed(cctx *types.CrossChainTx, logger zer logger = logger.With().Str("sendID", sendID).Logger() // compliance check, special handling the cancelled cctx - if clientcommon.IsCctxBanned(cctx) { + if clientcommon.IsCctxRestricted(cctx) { recvStatus := common.ReceiveStatus_Failed if receipt.Status == 1 { recvStatus = common.ReceiveStatus_Success diff --git a/zetaclient/evm/evm_client_test.go b/zetaclient/evm/evm_client_test.go index 730cba83f9..fdcee8c837 100644 --- a/zetaclient/evm/evm_client_test.go +++ b/zetaclient/evm/evm_client_test.go @@ -1,9 +1,11 @@ package evm import ( + "fmt" "math/big" "testing" + ethcommon "github.com/ethereum/go-ethereum/common" ethtypes "github.com/ethereum/go-ethereum/core/types" lru "github.com/hashicorp/golang-lru" "github.com/stretchr/testify/require" @@ -35,3 +37,95 @@ func TestEVMBlockCache(t *testing.T) { // delete the block should not panic ob.RemoveCachedBlock(uint64(blockNumber)) } + +func TestCheckEvmTxLog(t *testing.T) { + // test data + connectorAddr := ethcommon.HexToAddress("0x00005e3125aba53c5652f9f0ce1a4cf91d8b15ea") + txHash := "0xb252c9e77feafdeeae25cc1f037a16c4b50fa03c494754b99a7339d816c79626" + topics := []ethcommon.Hash{ + // https://goerli.etherscan.io/tx/0xb252c9e77feafdeeae25cc1f037a16c4b50fa03c494754b99a7339d816c79626#eventlog + ethcommon.HexToHash("0x7ec1c94701e09b1652f3e1d307e60c4b9ebf99aff8c2079fd1d8c585e031c4e4"), + ethcommon.HexToHash("0x00000000000000000000000023856df5d563bd893fc7df864102d8bbfe7fc487"), + ethcommon.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000061"), + } + + tests := []struct { + name string + vLog *ethtypes.Log + fail bool + }{ + { + name: "chain reorganization", + vLog: ðtypes.Log{ + Removed: true, + Address: connectorAddr, + TxHash: ethcommon.HexToHash(txHash), + Topics: topics, + }, + fail: true, + }, + { + name: "emitter address mismatch", + vLog: ðtypes.Log{ + Removed: false, + Address: ethcommon.HexToAddress("0x184ba627DB853244c9f17f3Cb4378cB8B39bf147"), + TxHash: ethcommon.HexToHash(txHash), + Topics: topics, + }, + fail: true, + }, + { + name: "tx hash mismatch", + vLog: ðtypes.Log{ + Removed: false, + Address: connectorAddr, + TxHash: ethcommon.HexToHash("0x781c018d604af4dad0fe5e3cea4ad9fb949a996d8cd0cd04a92cadd7f08c05f2"), + Topics: topics, + }, + fail: true, + }, + { + name: "topics mismatch", + vLog: ðtypes.Log{ + Removed: false, + Address: connectorAddr, + TxHash: ethcommon.HexToHash(txHash), + Topics: []ethcommon.Hash{ + // https://goerli.etherscan.io/tx/0xb252c9e77feafdeeae25cc1f037a16c4b50fa03c494754b99a7339d816c79626#eventlog + ethcommon.HexToHash("0x7ec1c94701e09b1652f3e1d307e60c4b9ebf99aff8c2079fd1d8c585e031c4e4"), + ethcommon.HexToHash("0x00000000000000000000000023856df5d563bd893fc7df864102d8bbfe7fc487"), + }, + }, + fail: true, + }, + { + name: "should pass", + vLog: ðtypes.Log{ + Removed: false, + Address: connectorAddr, + TxHash: ethcommon.HexToHash(txHash), + Topics: topics, + }, + fail: false, + }, + } + + evmClient := ChainClient{} + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + fmt.Printf("check test: %s\n", tt.name) + err := evmClient.CheckEvmTxLog( + tt.vLog, + connectorAddr, + "0xb252c9e77feafdeeae25cc1f037a16c4b50fa03c494754b99a7339d816c79626", + TopicsZetaSent, + ) + if tt.fail { + require.Error(t, err) + return + } else { + require.NoError(t, err) + } + }) + } +} diff --git a/zetaclient/evm/evm_signer.go b/zetaclient/evm/evm_signer.go index dd92eba42d..3ae2af5b2f 100644 --- a/zetaclient/evm/evm_signer.go +++ b/zetaclient/evm/evm_signer.go @@ -455,8 +455,8 @@ func (signer *Signer) TryProcessOutTx( var tx *ethtypes.Transaction // compliance check goes first - if clientcommon.IsCctxBanned(cctx) { - logMsg := fmt.Sprintf("Banned address detected in cctx: sender %s receiver %s chain %d nonce %d", cctx.InboundTxParams.Sender, to, toChain.ChainId, nonce) + if clientcommon.IsCctxRestricted(cctx) { + logMsg := fmt.Sprintf("Restricted address detected in cctx: sender %s receiver %s chain %d nonce %d", cctx.InboundTxParams.Sender, to, toChain.ChainId, nonce) logger.Warn().Msg(logMsg) signer.logger.Compliance.Warn().Msg(logMsg) tx, err = signer.SignCancelTx(nonce, gasprice, height) // cancel the tx diff --git a/zetaclient/evm/inbounds.go b/zetaclient/evm/inbounds.go index 8264e0e3dd..2bca262ea5 100644 --- a/zetaclient/evm/inbounds.go +++ b/zetaclient/evm/inbounds.go @@ -303,8 +303,8 @@ func (ob *ChainClient) GetInboundVoteMsgForDepositedEvent(event *erc20custody.ER if err == nil && parsedAddress != (ethcommon.Address{}) { maybeReceiver = parsedAddress.Hex() } - if config.AnyBannedAddress(sender.Hex(), clienttypes.BytesToEthHex(event.Recipient), maybeReceiver) { - logMsg := fmt.Sprintf("Banned address detected in ERC20 Deposited event: sender %s receiver %s tx %s chain %d", + if config.ContainRestrictedAddress(sender.Hex(), clienttypes.BytesToEthHex(event.Recipient), maybeReceiver) { + logMsg := fmt.Sprintf("Restricted address detected in ERC20 Deposited event: sender %s receiver %s tx %s chain %d", sender, maybeReceiver, event.Raw.TxHash, ob.chain.ChainId) ob.logger.ExternalChainWatcher.Warn().Msg(logMsg) ob.logger.Compliance.Warn().Msg(logMsg) @@ -348,8 +348,8 @@ func (ob *ChainClient) GetInboundVoteMsgForZetaSentEvent(event *zetaconnector.Ze // compliance check sender := event.ZetaTxSenderAddress.Hex() - if config.AnyBannedAddress(sender, destAddr, event.SourceTxOriginAddress.Hex()) { - logMsg := fmt.Sprintf("Banned address detected in ZetaSent event: sender %s receiver %s origin %s tx %s chain %d", + if config.ContainRestrictedAddress(sender, destAddr, event.SourceTxOriginAddress.Hex()) { + logMsg := fmt.Sprintf("Restricted address detected in ZetaSent event: sender %s receiver %s origin %s tx %s chain %d", sender, destAddr, event.SourceTxOriginAddress, event.Raw.TxHash, ob.chain.ChainId) ob.logger.ExternalChainWatcher.Warn().Msg(logMsg) ob.logger.Compliance.Warn().Msg(logMsg) @@ -398,8 +398,8 @@ func (ob *ChainClient) GetInboundVoteMsgForTokenSentToTSS(tx *ethtypes.Transacti if err == nil && parsedAddress != (ethcommon.Address{}) { maybeReceiver = parsedAddress.Hex() } - if config.AnyBannedAddress(sender.Hex(), maybeReceiver) { - logMsg := fmt.Sprintf("Banned address detected in token sent to TSS: sender %s tx %s chain %d", sender, tx.Hash(), ob.chain.ChainId) + if config.ContainRestrictedAddress(sender.Hex(), maybeReceiver) { + logMsg := fmt.Sprintf("Restricted address detected in token sent to TSS: sender %s tx %s chain %d", sender, tx.Hash(), ob.chain.ChainId) ob.logger.ExternalChainWatcher.Warn().Msg(logMsg) ob.logger.Compliance.Warn().Msg(logMsg) return nil diff --git a/zetaclient/evm/inbounds_test.go b/zetaclient/evm/inbounds_test.go index 36d68452c7..84b24dd200 100644 --- a/zetaclient/evm/inbounds_test.go +++ b/zetaclient/evm/inbounds_test.go @@ -15,14 +15,14 @@ import ( "github.com/zeta-chain/zetacore/zetaclient/testutils" ) -func DummyEVMClient(chain common.Chain) *ChainClient { +func MockEVMClient(chain common.Chain) *ChainClient { return &ChainClient{ chain: chain, - zetaClient: testutils.DummyCoreBridge(), + zetaClient: testutils.MockCoreBridge(), } } -func DummyConnectorNonEth() *zetaconnector.ZetaConnectorNonEth { +func MockConnectorNonEth() *zetaconnector.ZetaConnectorNonEth { connector, err := zetaconnector.NewZetaConnectorNonEth(ethcommon.Address{}, ðclient.Client{}) if err != nil { panic(err) @@ -53,9 +53,9 @@ func TestEthereum_GetInboundVoteMsgForZetaSentEvent(t *testing.T) { err := testutils.LoadObjectFromJSONFile(&receipt, path.Join("../", testutils.TestDataPathEVM, name)) require.NoError(t, err) - // create dummy client and connector - ob := DummyEVMClient(common.EthChain()) - connector := DummyConnectorNonEth() + // create mock client and connector + ob := MockEVMClient(common.EthChain()) + connector := MockConnectorNonEth() // parse ZetaSent event msg := ParseReceiptZetaSent(&receipt, ob, connector) @@ -67,22 +67,22 @@ func TestEthereum_GetInboundVoteMsgForZetaSentEvent(t *testing.T) { ComplianceConfig: &config.ComplianceConfig{}, } - t.Run("should return nil msg if sender is banned", func(t *testing.T) { - cfg.ComplianceConfig.BannedAddresses = []string{msg.Sender} + t.Run("should return nil msg if sender is restricted", func(t *testing.T) { + cfg.ComplianceConfig.RestrictedAddresses = []string{msg.Sender} config.LoadComplianceConfig(cfg) - msgBanned := ParseReceiptZetaSent(&receipt, ob, connector) - require.Nil(t, msgBanned) + msgRestricted := ParseReceiptZetaSent(&receipt, ob, connector) + require.Nil(t, msgRestricted) }) - t.Run("should return nil msg if receiver is banned", func(t *testing.T) { - cfg.ComplianceConfig.BannedAddresses = []string{msg.Receiver} + t.Run("should return nil msg if receiver is restricted", func(t *testing.T) { + cfg.ComplianceConfig.RestrictedAddresses = []string{msg.Receiver} config.LoadComplianceConfig(cfg) - msgBanned := ParseReceiptZetaSent(&receipt, ob, connector) - require.Nil(t, msgBanned) + msgRestricted := ParseReceiptZetaSent(&receipt, ob, connector) + require.Nil(t, msgRestricted) }) - t.Run("should return nil msg if txOrigin is banned", func(t *testing.T) { - cfg.ComplianceConfig.BannedAddresses = []string{msg.TxOrigin} + t.Run("should return nil msg if txOrigin is restricted", func(t *testing.T) { + cfg.ComplianceConfig.RestrictedAddresses = []string{msg.TxOrigin} config.LoadComplianceConfig(cfg) - msgBanned := ParseReceiptZetaSent(&receipt, ob, connector) - require.Nil(t, msgBanned) + msgRestricted := ParseReceiptZetaSent(&receipt, ob, connector) + require.Nil(t, msgRestricted) }) } diff --git a/zetaclient/testdata/btc/chain_8332_mempool.space_block_828440.json b/zetaclient/testdata/btc/block_mempool.space_8332_828440.json similarity index 100% rename from zetaclient/testdata/btc/chain_8332_mempool.space_block_828440.json rename to zetaclient/testdata/btc/block_mempool.space_8332_828440.json diff --git a/zetaclient/testdata/btc/chain_8332_block_trimmed_828440.json b/zetaclient/testdata/btc/block_trimmed_8332_828440.json similarity index 100% rename from zetaclient/testdata/btc/chain_8332_block_trimmed_828440.json rename to zetaclient/testdata/btc/block_trimmed_8332_828440.json diff --git a/zetaclient/testdata/btc/outtx_8332_148_raw_result.json b/zetaclient/testdata/btc/outtx_8332_148_raw_result.json new file mode 100644 index 0000000000..01aff1a4b7 --- /dev/null +++ b/zetaclient/testdata/btc/outtx_8332_148_raw_result.json @@ -0,0 +1,111 @@ +{ + "hex": "0100000000010792fe0c144838ef41b22aa655771547acca8ee913efafb8b3eb8cbd182a30caef0000000000ffffffffefceb531b2f8db989f7f7cfba458a434e313aa84ea7070713e391d0ceb05c03d0000000000ffffffffd7e5f6f569fa1b52bdab189ca836e8c1d4409c934d34ee821bb2f325a8acc3740000000000ffffffffbfc5c2e4988acfa851d68880b4869621c3be2132c5993cab4a1f580eef4c26870000000000ffffffff7400bc3a0f71a60a4241c8ffc2dbe860a8791d34e14a62963df03d973349f25a0000000000ffffffff92fe0c144838ef41b22aa655771547acca8ee913efafb8b3eb8cbd182a30caef0200000000ffffffff30a5ad76ee984c7e2da0b44e2b7153f4885201cfba5f3ed1b226c08a935557b80000000000ffffffff036408000000000000160014daaae0d3de9d8fdee31661e61aea828b59be7864e02e0000000000001600140c1bfb7d38dff0946fdec5626d51ad58d7e9bc54d1b9530200000000160014daaae0d3de9d8fdee31661e61aea828b59be786402483045022100febd70794057e61b83e2aac09302d89910e3b79a031b084b4f148c5529a189c602207c60a3834eeb7dd989c108c735bc17e0d53ce77a016e8c0890dbc1e5dc573b6c012102fad3348b7c7d73e85a7cfe53af42b535b750419efc55c36c807f38dd0ba06edc02483045022100e2a7db33480b4623c191be348525f48d2c6c7fd8c7a976ab87d556a3d9f1bf0c022060c51f38542f13c332c114aaaa2093728343a95506930d5c810bddde4a312153012102fad3348b7c7d73e85a7cfe53af42b535b750419efc55c36c807f38dd0ba06edc0247304402203cebf1856c0af2c5c2d8ece435418e999e700507ea647bb5cf109949b337d57302206753f01b7c81b731046b18aff7589e8471524aefe6349a346870bc09ca1de8d7012102fad3348b7c7d73e85a7cfe53af42b535b750419efc55c36c807f38dd0ba06edc02473044022053358333d728affa4a95e23f66599f66c9401e4f802bbb13f014acfae28217680220793c58356267a7c0052889a7da83d37eb01306277496b3e044e4a5b4abdc4461012102fad3348b7c7d73e85a7cfe53af42b535b750419efc55c36c807f38dd0ba06edc02473044022021552ae9009183979b534dedb251d555aacaa68f140c016ebfb1e6787a35c5e00220309df76bd511124352c3955f92d0db1cbab6504fe5649b0585b0c7400403570b012102fad3348b7c7d73e85a7cfe53af42b535b750419efc55c36c807f38dd0ba06edc02483045022100fabcbdbccc0d2468af60743fa817159fdd09a3f97193f119780ee95ab4014c9202205f728cb2dea3a9272651d09c23822649b38c160d0cc222613cb75db1bcde5e18012102fad3348b7c7d73e85a7cfe53af42b535b750419efc55c36c807f38dd0ba06edc024830450221008535da95650bcd786495f3fdbf19103fc0d6408d002d60212bb61fbc7bf6894502202ba5e5e32989ac3ef293f556196c2633e6e73750e3253ed474e366c5649ac1dd012102fad3348b7c7d73e85a7cfe53af42b535b750419efc55c36c807f38dd0ba06edc00000000", + "txid": "030cd813443f7b70cc6d8a544d320c6d8465e4528fc0f3410b599dc0b26753a0", + "hash": "a9ae1d437cc910eeaf82b566102db68a9180c21f5f23f4fcff386c59c45359c1", + "size": 1145, + "vsize": 579, + "weight": 2315, + "version": 1, + "locktime": 0, + "vin": [ + { + "txid": "efca302a18bd8cebb3b8afef13e98ecaac47157755a62ab241ef3848140cfe92", + "vout": 0, + "scriptSig": { "asm": "", "hex": "" }, + "txinwitness": [ + "3045022100febd70794057e61b83e2aac09302d89910e3b79a031b084b4f148c5529a189c602207c60a3834eeb7dd989c108c735bc17e0d53ce77a016e8c0890dbc1e5dc573b6c01", + "02fad3348b7c7d73e85a7cfe53af42b535b750419efc55c36c807f38dd0ba06edc" + ], + "sequence": 4294967295 + }, + { + "txid": "3dc005eb0c1d393e717070ea84aa13e334a458a4fb7c7f9f98dbf8b231b5ceef", + "vout": 0, + "scriptSig": { "asm": "", "hex": "" }, + "txinwitness": [ + "3045022100e2a7db33480b4623c191be348525f48d2c6c7fd8c7a976ab87d556a3d9f1bf0c022060c51f38542f13c332c114aaaa2093728343a95506930d5c810bddde4a31215301", + "02fad3348b7c7d73e85a7cfe53af42b535b750419efc55c36c807f38dd0ba06edc" + ], + "sequence": 4294967295 + }, + { + "txid": "74c3aca825f3b21b82ee344d939c40d4c1e836a89c18abbd521bfa69f5f6e5d7", + "vout": 0, + "scriptSig": { "asm": "", "hex": "" }, + "txinwitness": [ + "304402203cebf1856c0af2c5c2d8ece435418e999e700507ea647bb5cf109949b337d57302206753f01b7c81b731046b18aff7589e8471524aefe6349a346870bc09ca1de8d701", + "02fad3348b7c7d73e85a7cfe53af42b535b750419efc55c36c807f38dd0ba06edc" + ], + "sequence": 4294967295 + }, + { + "txid": "87264cef0e581f4aab3c99c53221bec3219686b48088d651a8cf8a98e4c2c5bf", + "vout": 0, + "scriptSig": { "asm": "", "hex": "" }, + "txinwitness": [ + "3044022053358333d728affa4a95e23f66599f66c9401e4f802bbb13f014acfae28217680220793c58356267a7c0052889a7da83d37eb01306277496b3e044e4a5b4abdc446101", + "02fad3348b7c7d73e85a7cfe53af42b535b750419efc55c36c807f38dd0ba06edc" + ], + "sequence": 4294967295 + }, + { + "txid": "5af24933973df03d96624ae1341d79a860e8dbc2ffc841420aa6710f3abc0074", + "vout": 0, + "scriptSig": { "asm": "", "hex": "" }, + "txinwitness": [ + "3044022021552ae9009183979b534dedb251d555aacaa68f140c016ebfb1e6787a35c5e00220309df76bd511124352c3955f92d0db1cbab6504fe5649b0585b0c7400403570b01", + "02fad3348b7c7d73e85a7cfe53af42b535b750419efc55c36c807f38dd0ba06edc" + ], + "sequence": 4294967295 + }, + { + "txid": "efca302a18bd8cebb3b8afef13e98ecaac47157755a62ab241ef3848140cfe92", + "vout": 2, + "scriptSig": { "asm": "", "hex": "" }, + "txinwitness": [ + "3045022100fabcbdbccc0d2468af60743fa817159fdd09a3f97193f119780ee95ab4014c9202205f728cb2dea3a9272651d09c23822649b38c160d0cc222613cb75db1bcde5e1801", + "02fad3348b7c7d73e85a7cfe53af42b535b750419efc55c36c807f38dd0ba06edc" + ], + "sequence": 4294967295 + }, + { + "txid": "b85755938ac026b2d13e5fbacf015288f453712b4eb4a02d7e4c98ee76ada530", + "vout": 0, + "scriptSig": { "asm": "", "hex": "" }, + "txinwitness": [ + "30450221008535da95650bcd786495f3fdbf19103fc0d6408d002d60212bb61fbc7bf6894502202ba5e5e32989ac3ef293f556196c2633e6e73750e3253ed474e366c5649ac1dd01", + "02fad3348b7c7d73e85a7cfe53af42b535b750419efc55c36c807f38dd0ba06edc" + ], + "sequence": 4294967295 + } + ], + "vout": [ + { + "value": 0.00002148, + "n": 0, + "scriptPubKey": { + "asm": "0 daaae0d3de9d8fdee31661e61aea828b59be7864", + "hex": "0014daaae0d3de9d8fdee31661e61aea828b59be7864", + "type": "witness_v0_keyhash" + } + }, + { + "value": 0.00012, + "n": 1, + "scriptPubKey": { + "asm": "0 0c1bfb7d38dff0946fdec5626d51ad58d7e9bc54", + "hex": "00140c1bfb7d38dff0946fdec5626d51ad58d7e9bc54", + "type": "witness_v0_keyhash" + } + }, + { + "value": 0.39041489, + "n": 2, + "scriptPubKey": { + "asm": "0 daaae0d3de9d8fdee31661e61aea828b59be7864", + "hex": "0014daaae0d3de9d8fdee31661e61aea828b59be7864", + "type": "witness_v0_keyhash" + } + } + ] +} diff --git a/zetaclient/testdata/cctx/cctx_8332_148.json b/zetaclient/testdata/cctx/cctx_8332_148.json new file mode 100644 index 0000000000..4564858f58 --- /dev/null +++ b/zetaclient/testdata/cctx/cctx_8332_148.json @@ -0,0 +1,32 @@ +{ + "index": "0xb3f5f3cf2ed2e0c3fa64c8297c9e50fbc07351fb2d26d8eae4cfbbd45e47a524", + "zeta_fees": "0", + "cctx_status": { "status": 3, "lastUpdate_timestamp": 1708608895 }, + "inbound_tx_params": { + "sender": "0x13A0c5930C028511Dc02665E7285134B6d11A5f4", + "sender_chain_id": 7000, + "tx_origin": "0xe99174F08e1186134830f8511De06bd010978533", + "coin_type": 1, + "amount": "12000", + "inbound_tx_observed_hash": "0x06455013319acb1b027461134853c77b003d8eab162b1f37673da5ad8a50b74f", + "inbound_tx_observed_external_height": 1870408, + "inbound_tx_ballot_index": "0xb3f5f3cf2ed2e0c3fa64c8297c9e50fbc07351fb2d26d8eae4cfbbd45e47a524" + }, + "outbound_tx_params": [ + { + "receiver": "bc1qpsdlklfcmlcfgm77c43x65ddtrt7n0z57hsyjp", + "receiver_chainId": 8332, + "coin_type": 1, + "amount": "12000", + "outbound_tx_tss_nonce": 148, + "outbound_tx_gas_limit": 254, + "outbound_tx_gas_price": "46", + "outbound_tx_hash": "030cd813443f7b70cc6d8a544d320c6d8465e4528fc0f3410b599dc0b26753a0", + "outbound_tx_ballot_index": "0x43845693f799b7a5e84dcf11321ae681ec018d709ecc919773968018f93e21c1", + "outbound_tx_observed_external_height": 150, + "outbound_tx_effective_gas_price": "0", + "tss_pubkey": "zetapub1addwnpepqtadxdyt037h86z60nl98t6zk56mw5zpnm79tsmvspln3hgt5phdc79kvfc", + "tx_finalization_status": 2 + } + ] +} diff --git a/zetaclient/testutils/constant.go b/zetaclient/testutils/constant.go new file mode 100644 index 0000000000..f767f4905a --- /dev/null +++ b/zetaclient/testutils/constant.go @@ -0,0 +1,9 @@ +package testutils + +const ( + TSSAddressEVMMainnet = "0x70e967acFcC17c3941E87562161406d41676FD83" + TSSAddressBTCMainnet = "bc1qm24wp577nk8aacckv8np465z3dvmu7ry45el6y" + + TSSAddressEVMAthens3 = "0x8531a5aB847ff5B22D855633C25ED1DA3255247e" + TSSAddressBTCAthens3 = "tb1qy9pqmk2pd9sv63g27jt8r657wy0d9ueeh0nqur" +) diff --git a/zetaclient/testutils/mock.go b/zetaclient/testutils/mock.go new file mode 100644 index 0000000000..92c0f6d1b2 --- /dev/null +++ b/zetaclient/testutils/mock.go @@ -0,0 +1,72 @@ +package testutils + +import ( + "github.com/btcsuite/btcutil" + "github.com/cosmos/cosmos-sdk/types" + ethcommon "github.com/ethereum/go-ethereum/common" + "github.com/zeta-chain/zetacore/common" + "github.com/zeta-chain/zetacore/zetaclient/interfaces" + "github.com/zeta-chain/zetacore/zetaclient/keys" + "github.com/zeta-chain/zetacore/zetaclient/zetabridge" +) + +var _ interfaces.TSSSigner = (*MockTSS)(nil) + +// MockTSS is a mock of TSS signer for testing +type MockTSS struct { + evmAddress string + btcAddress string +} + +func NewMockTSS(evmAddress string, btcAddress string) *MockTSS { + return &MockTSS{ + evmAddress: evmAddress, + btcAddress: btcAddress, + } +} + +func NewMockTSSMainnet() *MockTSS { + return NewMockTSS(TSSAddressEVMMainnet, TSSAddressBTCMainnet) +} + +func NewMockTSSAthens3() *MockTSS { + return NewMockTSS(TSSAddressEVMAthens3, TSSAddressBTCAthens3) +} + +func (s *MockTSS) Sign(_ []byte, _ uint64, _ uint64, _ *common.Chain, _ string) ([65]byte, error) { + return [65]byte{}, nil +} + +func (s *MockTSS) Pubkey() []byte { + return []byte{} +} + +func (s *MockTSS) EVMAddress() ethcommon.Address { + return ethcommon.HexToAddress(s.evmAddress) +} + +func (s *MockTSS) BTCAddress() string { + return s.btcAddress +} + +func (s *MockTSS) BTCAddressWitnessPubkeyHash() *btcutil.AddressWitnessPubKeyHash { + return nil +} + +func (s *MockTSS) PubKeyCompressedBytes() []byte { + return []byte{} +} + +func MockCoreBridge() *zetabridge.ZetaCoreBridge { + bridge, err := zetabridge.NewZetaCoreBridge( + &keys.Keys{OperatorAddress: types.AccAddress{}}, + "127.0.0.1", + "", + "zetachain_7000-1", + false, + nil) + if err != nil { + panic(err) + } + return bridge +} diff --git a/zetaclient/testutils/utils.go b/zetaclient/testutils/utils.go index e154e0467c..4f00f10bfd 100644 --- a/zetaclient/testutils/utils.go +++ b/zetaclient/testutils/utils.go @@ -6,18 +6,15 @@ import ( "path/filepath" "github.com/btcsuite/btcd/btcjson" - "github.com/cosmos/cosmos-sdk/types" "github.com/zeta-chain/zetacore/zetaclient/config" - "github.com/zeta-chain/zetacore/zetaclient/keys" - "github.com/zeta-chain/zetacore/zetaclient/zetabridge" ) const ( - TestDataPathEVM = "testdata/evm" - TestDataPathBTC = "testdata/btc" - TestDataPathCctx = "testdata/cctx" - BannedEVMAddressTest = "0x8a81Ba8eCF2c418CAe624be726F505332DF119C6" - BannedBtcAddressTest = "bcrt1qzp4gt6fc7zkds09kfzaf9ln9c5rvrzxmy6qmpp" + TestDataPathEVM = "testdata/evm" + TestDataPathBTC = "testdata/btc" + TestDataPathCctx = "testdata/cctx" + RestrictedEVMAddressTest = "0x8a81Ba8eCF2c418CAe624be726F505332DF119C6" + RestrictedBtcAddressTest = "bcrt1qzp4gt6fc7zkds09kfzaf9ln9c5rvrzxmy6qmpp" ) // SaveObjectToJSONFile saves an object to a file in JSON format @@ -59,22 +56,8 @@ func SaveBTCBlockTrimTx(blockVb *btcjson.GetBlockVerboseTxResult, filename strin return SaveObjectToJSONFile(blockVb, filename) } -func DummyCoreBridge() *zetabridge.ZetaCoreBridge { - bridge, err := zetabridge.NewZetaCoreBridge( - &keys.Keys{OperatorAddress: types.AccAddress{}}, - "127.0.0.1", - "", - "zetachain_7000-1", - false, - nil) - if err != nil { - panic(err) - } - return bridge -} - func ComplianceConfigTest() *config.ComplianceConfig { return &config.ComplianceConfig{ - BannedAddresses: []string{BannedEVMAddressTest, BannedBtcAddressTest}, + RestrictedAddresses: []string{RestrictedEVMAddressTest, RestrictedBtcAddressTest}, } } From 48d29bc1b4a3c30dd6f619b87a520585090e4a61 Mon Sep 17 00:00:00 2001 From: Charlie Chen Date: Fri, 23 Feb 2024 00:24:39 -0600 Subject: [PATCH 06/13] unified log prints for restricted address detection --- zetaclient/bitcoin/bitcoin_client.go | 4 ++-- zetaclient/bitcoin/bitcoin_signer.go | 4 ++-- zetaclient/evm/evm_signer.go | 3 ++- zetaclient/evm/inbounds.go | 8 ++++---- 4 files changed, 10 insertions(+), 9 deletions(-) diff --git a/zetaclient/bitcoin/bitcoin_client.go b/zetaclient/bitcoin/bitcoin_client.go index ad2e7a0ca9..eb5d9251f7 100644 --- a/zetaclient/bitcoin/bitcoin_client.go +++ b/zetaclient/bitcoin/bitcoin_client.go @@ -717,8 +717,8 @@ func (ob *BTCChainClient) IsInTxRestricted(inTx *BTCInTxEvnet) bool { receiver = parsedAddress.Hex() } if config.ContainRestrictedAddress(inTx.FromAddress, receiver) { - logMsg := fmt.Sprintf("Restricted address detected in token sent to TSS: sender %s receiver %s tx %s chain %d", - inTx.FromAddress, receiver, inTx.TxHash, ob.chain.ChainId) + logMsg := fmt.Sprintf("Restricted address detected, token: BTC sender: %s receiver: %s intx: %s", + inTx.FromAddress, receiver, inTx.TxHash) ob.logger.WatchInTx.Warn().Msg(logMsg) ob.logger.Compliance.Warn().Msg(logMsg) return true diff --git a/zetaclient/bitcoin/bitcoin_signer.go b/zetaclient/bitcoin/bitcoin_signer.go index e3fb1c59fd..f2e4be41f5 100644 --- a/zetaclient/bitcoin/bitcoin_signer.go +++ b/zetaclient/bitcoin/bitcoin_signer.go @@ -327,8 +327,8 @@ func (signer *BTCSigner) TryProcessOutTx( // compliance check cancelTx := clientcommon.IsCctxRestricted(cctx) if cancelTx { - logMsg := fmt.Sprintf("Restricted address detected in cctx: sender %s receiver %s chain %d nonce %d", - cctx.InboundTxParams.Sender, to, params.ReceiverChainId, outboundTxTssNonce) + logMsg := fmt.Sprintf("Restricted address detected, token: BTC sender: %s receiver: %s cctx: %s", + cctx.InboundTxParams.Sender, to, cctx.Index) logger.Warn().Msg(logMsg) signer.loggerCompliance.Warn().Msg(logMsg) amount = 0.0 // zero out the amount to cancel the tx diff --git a/zetaclient/evm/evm_signer.go b/zetaclient/evm/evm_signer.go index 3ae2af5b2f..5c070f8a6f 100644 --- a/zetaclient/evm/evm_signer.go +++ b/zetaclient/evm/evm_signer.go @@ -456,7 +456,8 @@ func (signer *Signer) TryProcessOutTx( // compliance check goes first if clientcommon.IsCctxRestricted(cctx) { - logMsg := fmt.Sprintf("Restricted address detected in cctx: sender %s receiver %s chain %d nonce %d", cctx.InboundTxParams.Sender, to, toChain.ChainId, nonce) + logMsg := fmt.Sprintf("Restricted address detected, token: %s sender: %s receiver: %s cctx: %s", + cctx.GetCurrentOutTxParam().CoinType, cctx.InboundTxParams.Sender, to, cctx.Index) logger.Warn().Msg(logMsg) signer.logger.Compliance.Warn().Msg(logMsg) tx, err = signer.SignCancelTx(nonce, gasprice, height) // cancel the tx diff --git a/zetaclient/evm/inbounds.go b/zetaclient/evm/inbounds.go index 2bca262ea5..415d1fa4d4 100644 --- a/zetaclient/evm/inbounds.go +++ b/zetaclient/evm/inbounds.go @@ -304,8 +304,8 @@ func (ob *ChainClient) GetInboundVoteMsgForDepositedEvent(event *erc20custody.ER maybeReceiver = parsedAddress.Hex() } if config.ContainRestrictedAddress(sender.Hex(), clienttypes.BytesToEthHex(event.Recipient), maybeReceiver) { - logMsg := fmt.Sprintf("Restricted address detected in ERC20 Deposited event: sender %s receiver %s tx %s chain %d", - sender, maybeReceiver, event.Raw.TxHash, ob.chain.ChainId) + logMsg := fmt.Sprintf("Restricted address detected, token: ERC20 sender: %s receiver: %s intx: %s chain: %d", + sender, clienttypes.BytesToEthHex(event.Recipient), event.Raw.TxHash, ob.chain.ChainId) ob.logger.ExternalChainWatcher.Warn().Msg(logMsg) ob.logger.Compliance.Warn().Msg(logMsg) return nil @@ -349,7 +349,7 @@ func (ob *ChainClient) GetInboundVoteMsgForZetaSentEvent(event *zetaconnector.Ze // compliance check sender := event.ZetaTxSenderAddress.Hex() if config.ContainRestrictedAddress(sender, destAddr, event.SourceTxOriginAddress.Hex()) { - logMsg := fmt.Sprintf("Restricted address detected in ZetaSent event: sender %s receiver %s origin %s tx %s chain %d", + logMsg := fmt.Sprintf("Restricted address detected, token: Zeta sender: %s receiver: %s origin: %s intx: %s chain: %d", sender, destAddr, event.SourceTxOriginAddress, event.Raw.TxHash, ob.chain.ChainId) ob.logger.ExternalChainWatcher.Warn().Msg(logMsg) ob.logger.Compliance.Warn().Msg(logMsg) @@ -399,7 +399,7 @@ func (ob *ChainClient) GetInboundVoteMsgForTokenSentToTSS(tx *ethtypes.Transacti maybeReceiver = parsedAddress.Hex() } if config.ContainRestrictedAddress(sender.Hex(), maybeReceiver) { - logMsg := fmt.Sprintf("Restricted address detected in token sent to TSS: sender %s tx %s chain %d", sender, tx.Hash(), ob.chain.ChainId) + logMsg := fmt.Sprintf("Restricted address detected, token: Gas sender: %s intx: %s chain: %d", sender, tx.Hash(), ob.chain.ChainId) ob.logger.ExternalChainWatcher.Warn().Msg(logMsg) ob.logger.Compliance.Warn().Msg(logMsg) return nil From 73ccf559b2050c2dd9ab75479dc497719eadf1c2 Mon Sep 17 00:00:00 2001 From: Charlie Chen Date: Fri, 23 Feb 2024 00:31:10 -0600 Subject: [PATCH 07/13] renamed tests and variables --- cmd/zetae2e/local/ethereum.go | 2 +- e2e/e2etests/e2etests.go | 20 ++++++++++---------- e2e/e2etests/test_bitcoin_withdraw.go | 12 ++++++------ e2e/e2etests/test_erc20_deposit.go | 4 ++-- e2e/e2etests/test_eth_withdraw.go | 10 +++++----- e2e/e2etests/test_zeta_deposit.go | 4 ++-- 6 files changed, 26 insertions(+), 26 deletions(-) diff --git a/cmd/zetae2e/local/ethereum.go b/cmd/zetae2e/local/ethereum.go index 3f3e4d3cf7..e153fe90fa 100644 --- a/cmd/zetae2e/local/ethereum.go +++ b/cmd/zetae2e/local/ethereum.go @@ -57,7 +57,7 @@ func ethereumTestRoutine( e2etests.AllE2ETests, e2etests.TestEtherDepositName, e2etests.TestEtherWithdrawName, - e2etests.TestEtherWithdrawBannedName, + e2etests.TestEtherWithdrawRestrictedName, e2etests.TestContextUpgradeName, e2etests.TestEtherDepositAndCallName, e2etests.TestDepositAndCallRefundName, diff --git a/e2e/e2etests/e2etests.go b/e2e/e2etests/e2etests.go index 76cd9e0c98..dd68abdc29 100644 --- a/e2e/e2etests/e2etests.go +++ b/e2e/e2etests/e2etests.go @@ -26,13 +26,13 @@ const ( TestDepositEtherLiquidityCapName = "deposit_eth_liquidity_cap" TestMyTestName = "my_test" - TestERC20WithdrawName = "erc20_withdraw" - TestERC20DepositName = "erc20_deposit" - TestEtherDepositName = "eth_deposit" - TestEtherWithdrawName = "eth_withdraw" - TestEtherWithdrawBannedName = "eth_withdraw_banned" - TestBitcoinDepositName = "bitcoin_deposit" - TestZetaDepositName = "zeta_deposit" + TestERC20WithdrawName = "erc20_withdraw" + TestERC20DepositName = "erc20_deposit" + TestEtherDepositName = "eth_deposit" + TestEtherWithdrawName = "eth_withdraw" + TestEtherWithdrawRestrictedName = "eth_withdraw_restricted" + TestBitcoinDepositName = "bitcoin_deposit" + TestZetaDepositName = "zeta_deposit" TestDonationEtherName = "donation_ether" @@ -160,9 +160,9 @@ var AllE2ETests = []runner.E2ETest{ TestEtherWithdraw, }, { - TestEtherWithdrawBannedName, - "withdraw Ether from ZEVM to banned address", - TestEtherWithdrawBanned, + TestEtherWithdrawRestrictedName, + "withdraw Ether from ZEVM to restricted address", + TestEtherWithdrawRestricted, }, { TestBitcoinDepositName, diff --git a/e2e/e2etests/test_bitcoin_withdraw.go b/e2e/e2etests/test_bitcoin_withdraw.go index 43d5078e8b..065adf63d8 100644 --- a/e2e/e2etests/test_bitcoin_withdraw.go +++ b/e2e/e2etests/test_bitcoin_withdraw.go @@ -20,8 +20,8 @@ func TestBitcoinWithdraw(r *runner.E2ERunner) { // withdraw 0.01 BTC from ZRC20 to BTC address WithdrawBitcoin(r) - // withdraw 0.01 BTC from ZRC20 to BTC banned address - WithdrawBitcoinBanned(r) + // withdraw 0.01 BTC from ZRC20 to BTC restricted address + WithdrawBitcoinRestricted(r) // stop mining stop <- struct{}{} @@ -87,17 +87,17 @@ func WithdrawBitcoin(r *runner.E2ERunner) { withdrawBTCZRC20(r, r.BTCDeployerAddress, amount) } -func WithdrawBitcoinBanned(r *runner.E2ERunner) { +func WithdrawBitcoinRestricted(r *runner.E2ERunner) { amount := big.NewInt(0.01 * btcutil.SatoshiPerBitcoin) - // use banned BTC P2WPKH address - addressBanned, err := common.DecodeBtcAddress(testutils.BannedBtcAddressTest, common.BtcRegtestChain().ChainId) + // use restricted BTC P2WPKH address + addressRestricted, err := common.DecodeBtcAddress(testutils.RestrictedBtcAddressTest, common.BtcRegtestChain().ChainId) if err != nil { panic(err) } // the cctx should be cancelled - rawTx := withdrawBTCZRC20(r, addressBanned, amount) + rawTx := withdrawBTCZRC20(r, addressRestricted, amount) if len(rawTx.Vout) != 2 { panic(fmt.Errorf("BTC cancelled outtx rawTx.Vout should have 2 outputs")) } diff --git a/e2e/e2etests/test_erc20_deposit.go b/e2e/e2etests/test_erc20_deposit.go index 3f45987fd9..af1bb6266a 100644 --- a/e2e/e2etests/test_erc20_deposit.go +++ b/e2e/e2etests/test_erc20_deposit.go @@ -16,6 +16,6 @@ func TestERC20Deposit(r *runner.E2ERunner) { cctx := utils.WaitCctxMinedByInTxHash(r.Ctx, hash.Hex(), r.CctxClient, r.Logger, r.CctxTimeout) r.Logger.CCTX(*cctx, "deposit") - // deposit ERC20 to banned address - r.DepositERC20WithAmountAndMessage(ethcommon.HexToAddress(testutils.BannedEVMAddressTest), big.NewInt(100000), []byte{}) + // deposit ERC20 to restricted address + r.DepositERC20WithAmountAndMessage(ethcommon.HexToAddress(testutils.RestrictedEVMAddressTest), big.NewInt(100000), []byte{}) } diff --git a/e2e/e2etests/test_eth_withdraw.go b/e2e/e2etests/test_eth_withdraw.go index bb5c423336..fbc866fcce 100644 --- a/e2e/e2etests/test_eth_withdraw.go +++ b/e2e/e2etests/test_eth_withdraw.go @@ -47,8 +47,8 @@ func TestEtherWithdraw(r *runner.E2ERunner) { } } -// TestEtherWithdrawBanned tests the withdrawal to a banned receiver address -func TestEtherWithdrawBanned(r *runner.E2ERunner) { +// TestEtherWithdrawRestricted tests the withdrawal to a restricted receiver address +func TestEtherWithdrawRestricted(r *runner.E2ERunner) { // approve tx, err := r.ETHZRC20.Approve(r.ZevmAuth, r.ETHZRC20Addr, big.NewInt(1e18)) if err != nil { @@ -63,12 +63,12 @@ func TestEtherWithdrawBanned(r *runner.E2ERunner) { r.Logger.EVMReceipt(*receipt, "approve") // withdraw - bannedAddress := ethcommon.HexToAddress(testutils.BannedEVMAddressTest) - tx, err = r.ETHZRC20.Withdraw(r.ZevmAuth, bannedAddress.Bytes(), big.NewInt(100000)) + restrictedAddress := ethcommon.HexToAddress(testutils.RestrictedEVMAddressTest) + tx, err = r.ETHZRC20.Withdraw(r.ZevmAuth, restrictedAddress.Bytes(), big.NewInt(100000)) if err != nil { panic(err) } - r.Logger.EVMTransaction(*tx, "withdraw to banned address") + r.Logger.EVMTransaction(*tx, "withdraw to restricted address") receipt = utils.MustWaitForTxReceipt(r.Ctx, r.ZevmClient, tx, r.Logger, r.ReceiptTimeout) if receipt.Status == 0 { diff --git a/e2e/e2etests/test_zeta_deposit.go b/e2e/e2etests/test_zeta_deposit.go index 658e5dfcbe..7b079d2953 100644 --- a/e2e/e2etests/test_zeta_deposit.go +++ b/e2e/e2etests/test_zeta_deposit.go @@ -17,6 +17,6 @@ func TestZetaDeposit(r *runner.E2ERunner) { cctx := utils.WaitCctxMinedByInTxHash(r.Ctx, hash.Hex(), r.CctxClient, r.Logger, r.CctxTimeout) r.Logger.CCTX(*cctx, "deposit") - // Deposit 1 Zeta to banned address - r.DepositZetaWithAmount(ethcommon.HexToAddress(testutils.BannedEVMAddressTest), big.NewInt(1e18)) + // Deposit 1 Zeta to restricted address + r.DepositZetaWithAmount(ethcommon.HexToAddress(testutils.RestrictedEVMAddressTest), big.NewInt(1e18)) } From 2142d35d064ce15f3a44a7d605f0efbcae0d13af Mon Sep 17 00:00:00 2001 From: Charlie Chen Date: Fri, 23 Feb 2024 00:48:16 -0600 Subject: [PATCH 08/13] move restricted address to Unreleased section --- changelog.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/changelog.md b/changelog.md index 8b4f72204e..dec688b9bf 100644 --- a/changelog.md +++ b/changelog.md @@ -2,6 +2,10 @@ ## Unreleased +### Features + +* [1789](https://github.com/zeta-chain/node/issues/1789) - block cross-chain transactions that involve restricted addresses + ### Tests * [1767](https://github.com/zeta-chain/node/pull/1767) - add unit tests for emissions module begin blocker @@ -13,7 +17,6 @@ ### Features * [1698](https://github.com/zeta-chain/node/issues/1698) - bitcoin dynamic depositor fee -* [1789](https://github.com/zeta-chain/node/issues/1789) - block cross-chain transactions that involve restricted addresses ### Docs From eaae25bb6f48d59f7a09f47a9e6019db0911f9ce Mon Sep 17 00:00:00 2001 From: Charlie Chen Date: Fri, 23 Feb 2024 00:49:38 -0600 Subject: [PATCH 09/13] move restricted address to Unreleased section --- changelog.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/changelog.md b/changelog.md index 8b4f72204e..dec688b9bf 100644 --- a/changelog.md +++ b/changelog.md @@ -2,6 +2,10 @@ ## Unreleased +### Features + +* [1789](https://github.com/zeta-chain/node/issues/1789) - block cross-chain transactions that involve restricted addresses + ### Tests * [1767](https://github.com/zeta-chain/node/pull/1767) - add unit tests for emissions module begin blocker @@ -13,7 +17,6 @@ ### Features * [1698](https://github.com/zeta-chain/node/issues/1698) - bitcoin dynamic depositor fee -* [1789](https://github.com/zeta-chain/node/issues/1789) - block cross-chain transactions that involve restricted addresses ### Docs From fc770c3fc65868818caad45e3f0ffa1cac8d1bf4 Mon Sep 17 00:00:00 2001 From: Charlie Chen Date: Fri, 23 Feb 2024 12:54:26 -0600 Subject: [PATCH 10/13] improved compliance log prints --- zetaclient/bitcoin/bitcoin_client.go | 7 +++---- zetaclient/bitcoin/bitcoin_signer.go | 7 +++---- zetaclient/common/utils.go | 6 ++++++ zetaclient/evm/evm_signer.go | 7 +++---- zetaclient/evm/inbounds.go | 21 ++++++++++----------- 5 files changed, 25 insertions(+), 23 deletions(-) diff --git a/zetaclient/bitcoin/bitcoin_client.go b/zetaclient/bitcoin/bitcoin_client.go index eb5d9251f7..f48246cff8 100644 --- a/zetaclient/bitcoin/bitcoin_client.go +++ b/zetaclient/bitcoin/bitcoin_client.go @@ -717,10 +717,9 @@ func (ob *BTCChainClient) IsInTxRestricted(inTx *BTCInTxEvnet) bool { receiver = parsedAddress.Hex() } if config.ContainRestrictedAddress(inTx.FromAddress, receiver) { - logMsg := fmt.Sprintf("Restricted address detected, token: BTC sender: %s receiver: %s intx: %s", - inTx.FromAddress, receiver, inTx.TxHash) - ob.logger.WatchInTx.Warn().Msg(logMsg) - ob.logger.Compliance.Warn().Msg(logMsg) + logMsg := fmt.Sprintf("Restricted address detected in intx %s", inTx.TxHash) + clientcommon.PrintComplianceLog(ob.logger.WatchInTx, inTx.FromAddress, receiver, "BTC", logMsg) + clientcommon.PrintComplianceLog(ob.logger.Compliance, inTx.FromAddress, receiver, "BTC", logMsg) return true } return false diff --git a/zetaclient/bitcoin/bitcoin_signer.go b/zetaclient/bitcoin/bitcoin_signer.go index f2e4be41f5..291133e51e 100644 --- a/zetaclient/bitcoin/bitcoin_signer.go +++ b/zetaclient/bitcoin/bitcoin_signer.go @@ -327,10 +327,9 @@ func (signer *BTCSigner) TryProcessOutTx( // compliance check cancelTx := clientcommon.IsCctxRestricted(cctx) if cancelTx { - logMsg := fmt.Sprintf("Restricted address detected, token: BTC sender: %s receiver: %s cctx: %s", - cctx.InboundTxParams.Sender, to, cctx.Index) - logger.Warn().Msg(logMsg) - signer.loggerCompliance.Warn().Msg(logMsg) + logMsg := fmt.Sprintf("Restricted address detected in cctx %s", cctx.Index) + clientcommon.PrintComplianceLog(logger, cctx.InboundTxParams.Sender, params.Receiver, "BTC", logMsg) + clientcommon.PrintComplianceLog(signer.loggerCompliance, cctx.InboundTxParams.Sender, params.Receiver, "BTC", logMsg) amount = 0.0 // zero out the amount to cancel the tx } diff --git a/zetaclient/common/utils.go b/zetaclient/common/utils.go index 415592a5f2..48220fd5e4 100644 --- a/zetaclient/common/utils.go +++ b/zetaclient/common/utils.go @@ -25,3 +25,9 @@ func IsCctxRestricted(cctx *crosschaintypes.CrossChainTx) bool { receiver := cctx.GetCurrentOutTxParam().Receiver return config.ContainRestrictedAddress(sender, receiver) } + +// PrintComplianceLog prints compliance log with fields [sender, receiver, token] +func PrintComplianceLog(logger zerolog.Logger, sender, receiver, token, msg string) { + logWithFields := logger.With().Str("sender", sender).Str("receiver", receiver).Str("token", token).Logger() + logWithFields.Warn().Msg(msg) +} diff --git a/zetaclient/evm/evm_signer.go b/zetaclient/evm/evm_signer.go index 5c070f8a6f..ffa709ec97 100644 --- a/zetaclient/evm/evm_signer.go +++ b/zetaclient/evm/evm_signer.go @@ -456,10 +456,9 @@ func (signer *Signer) TryProcessOutTx( // compliance check goes first if clientcommon.IsCctxRestricted(cctx) { - logMsg := fmt.Sprintf("Restricted address detected, token: %s sender: %s receiver: %s cctx: %s", - cctx.GetCurrentOutTxParam().CoinType, cctx.InboundTxParams.Sender, to, cctx.Index) - logger.Warn().Msg(logMsg) - signer.logger.Compliance.Warn().Msg(logMsg) + logMsg := fmt.Sprintf("Restricted address detected in cctx %s", cctx.Index) + clientcommon.PrintComplianceLog(logger, cctx.InboundTxParams.Sender, to.Hex(), cctx.GetCurrentOutTxParam().CoinType.String(), logMsg) + clientcommon.PrintComplianceLog(signer.logger.Compliance, cctx.InboundTxParams.Sender, to.Hex(), cctx.GetCurrentOutTxParam().CoinType.String(), logMsg) tx, err = signer.SignCancelTx(nonce, gasprice, height) // cancel the tx } else if cctx.GetCurrentOutTxParam().CoinType == common.CoinType_Cmd { // admin command to := ethcommon.HexToAddress(cctx.GetCurrentOutTxParam().Receiver) diff --git a/zetaclient/evm/inbounds.go b/zetaclient/evm/inbounds.go index 415d1fa4d4..8652bf2232 100644 --- a/zetaclient/evm/inbounds.go +++ b/zetaclient/evm/inbounds.go @@ -13,6 +13,7 @@ import ( ethtypes "github.com/ethereum/go-ethereum/core/types" "github.com/zeta-chain/protocol-contracts/pkg/contracts/evm/erc20custody.sol" "github.com/zeta-chain/protocol-contracts/pkg/contracts/evm/zetaconnector.non-eth.sol" + clientcommon "github.com/zeta-chain/zetacore/zetaclient/common" "github.com/zeta-chain/zetacore/zetaclient/config" clienttypes "github.com/zeta-chain/zetacore/zetaclient/types" @@ -304,10 +305,9 @@ func (ob *ChainClient) GetInboundVoteMsgForDepositedEvent(event *erc20custody.ER maybeReceiver = parsedAddress.Hex() } if config.ContainRestrictedAddress(sender.Hex(), clienttypes.BytesToEthHex(event.Recipient), maybeReceiver) { - logMsg := fmt.Sprintf("Restricted address detected, token: ERC20 sender: %s receiver: %s intx: %s chain: %d", - sender, clienttypes.BytesToEthHex(event.Recipient), event.Raw.TxHash, ob.chain.ChainId) - ob.logger.ExternalChainWatcher.Warn().Msg(logMsg) - ob.logger.Compliance.Warn().Msg(logMsg) + logMsg := fmt.Sprintf("Restricted address detected in intx %s", event.Raw.TxHash) + clientcommon.PrintComplianceLog(ob.logger.ExternalChainWatcher, sender.Hex(), clienttypes.BytesToEthHex(event.Recipient), "ERC20", logMsg) + clientcommon.PrintComplianceLog(ob.logger.Compliance, sender.Hex(), clienttypes.BytesToEthHex(event.Recipient), "ERC20", logMsg) return nil } @@ -349,10 +349,9 @@ func (ob *ChainClient) GetInboundVoteMsgForZetaSentEvent(event *zetaconnector.Ze // compliance check sender := event.ZetaTxSenderAddress.Hex() if config.ContainRestrictedAddress(sender, destAddr, event.SourceTxOriginAddress.Hex()) { - logMsg := fmt.Sprintf("Restricted address detected, token: Zeta sender: %s receiver: %s origin: %s intx: %s chain: %d", - sender, destAddr, event.SourceTxOriginAddress, event.Raw.TxHash, ob.chain.ChainId) - ob.logger.ExternalChainWatcher.Warn().Msg(logMsg) - ob.logger.Compliance.Warn().Msg(logMsg) + logMsg := fmt.Sprintf("Restricted address detected in intx %s", event.Raw.TxHash) + clientcommon.PrintComplianceLog(ob.logger.ExternalChainWatcher, sender, destAddr, "Zeta", logMsg) + clientcommon.PrintComplianceLog(ob.logger.Compliance, sender, destAddr, "Zeta", logMsg) return nil } @@ -399,9 +398,9 @@ func (ob *ChainClient) GetInboundVoteMsgForTokenSentToTSS(tx *ethtypes.Transacti maybeReceiver = parsedAddress.Hex() } if config.ContainRestrictedAddress(sender.Hex(), maybeReceiver) { - logMsg := fmt.Sprintf("Restricted address detected, token: Gas sender: %s intx: %s chain: %d", sender, tx.Hash(), ob.chain.ChainId) - ob.logger.ExternalChainWatcher.Warn().Msg(logMsg) - ob.logger.Compliance.Warn().Msg(logMsg) + logMsg := fmt.Sprintf("Restricted address detected in intx %s", tx.Hash()) + clientcommon.PrintComplianceLog(ob.logger.ExternalChainWatcher, sender.Hex(), sender.Hex(), "Gas", logMsg) + clientcommon.PrintComplianceLog(ob.logger.Compliance, sender.Hex(), sender.Hex(), "Gas", logMsg) return nil } From 7c4bebe80d93b1474ca1913ddd24694e9de9c1d1 Mon Sep 17 00:00:00 2001 From: Charlie Chen Date: Thu, 29 Feb 2024 23:55:02 -0600 Subject: [PATCH 11/13] removed unnecessary tests and made bitcoin withdrawal to restricted address a separate test --- cmd/zetae2e/local/bitcoin.go | 2 +- cmd/zetae2e/local/erc20.go | 1 - cmd/zetae2e/local/ethereum.go | 1 - e2e/e2etests/e2etests.go | 6 ++++++ e2e/e2etests/test_bitcoin_withdraw.go | 14 ++++++++------ 5 files changed, 15 insertions(+), 9 deletions(-) diff --git a/cmd/zetae2e/local/bitcoin.go b/cmd/zetae2e/local/bitcoin.go index f82963285a..f5606eba20 100644 --- a/cmd/zetae2e/local/bitcoin.go +++ b/cmd/zetae2e/local/bitcoin.go @@ -66,8 +66,8 @@ func bitcoinTestRoutine( // to make it faster to catch up with the latest block header if err := bitcoinRunner.RunE2ETestsFromNames( e2etests.AllE2ETests, - e2etests.TestBitcoinDepositName, e2etests.TestBitcoinWithdrawName, + e2etests.TestBitcoinWithdrawRestrictedName, e2etests.TestZetaWithdrawBTCRevertName, e2etests.TestCrosschainSwapName, ); err != nil { diff --git a/cmd/zetae2e/local/erc20.go b/cmd/zetae2e/local/erc20.go index 2ae8041a67..c3dbc3f354 100644 --- a/cmd/zetae2e/local/erc20.go +++ b/cmd/zetae2e/local/erc20.go @@ -62,7 +62,6 @@ func erc20TestRoutine( // run erc20 test if err := erc20Runner.RunE2ETestsFromNames( e2etests.AllE2ETests, - e2etests.TestERC20DepositName, e2etests.TestERC20WithdrawName, e2etests.TestMultipleWithdrawsName, e2etests.TestERC20DepositAndCallRefundName, diff --git a/cmd/zetae2e/local/ethereum.go b/cmd/zetae2e/local/ethereum.go index e153fe90fa..eb615a315a 100644 --- a/cmd/zetae2e/local/ethereum.go +++ b/cmd/zetae2e/local/ethereum.go @@ -55,7 +55,6 @@ func ethereumTestRoutine( // to make it faster to catch up with the latest block header if err := ethereumRunner.RunE2ETestsFromNames( e2etests.AllE2ETests, - e2etests.TestEtherDepositName, e2etests.TestEtherWithdrawName, e2etests.TestEtherWithdrawRestrictedName, e2etests.TestContextUpgradeName, diff --git a/e2e/e2etests/e2etests.go b/e2e/e2etests/e2etests.go index dd68abdc29..6d9492bcba 100644 --- a/e2e/e2etests/e2etests.go +++ b/e2e/e2etests/e2etests.go @@ -16,6 +16,7 @@ const ( TestMessagePassingName = "message_passing" TestZRC20SwapName = "zrc20_swap" TestBitcoinWithdrawName = "bitcoin_withdraw" + TestBitcoinWithdrawRestrictedName = "bitcoin_withdraw_restricted" TestCrosschainSwapName = "crosschain_swap" TestMessagePassingRevertFailName = "message_passing_revert_fail" TestMessagePassingRevertSuccessName = "message_passing_revert_success" @@ -99,6 +100,11 @@ var AllE2ETests = []runner.E2ETest{ "withdraw BTC from ZEVM", TestBitcoinWithdraw, }, + { + TestBitcoinWithdrawRestrictedName, + "withdraw Bitcoin from ZEVM to restricted address", + TestBitcoinWithdrawRestricted, + }, { TestCrosschainSwapName, "testing Bitcoin ERC20 cross-chain swap", diff --git a/e2e/e2etests/test_bitcoin_withdraw.go b/e2e/e2etests/test_bitcoin_withdraw.go index 065adf63d8..6f302c1acf 100644 --- a/e2e/e2etests/test_bitcoin_withdraw.go +++ b/e2e/e2etests/test_bitcoin_withdraw.go @@ -14,17 +14,13 @@ import ( ) func TestBitcoinWithdraw(r *runner.E2ERunner) { - // start mining blocks - stop := r.MineBlocks() - // withdraw 0.01 BTC from ZRC20 to BTC address WithdrawBitcoin(r) +} +func TestBitcoinWithdrawRestricted(r *runner.E2ERunner) { // withdraw 0.01 BTC from ZRC20 to BTC restricted address WithdrawBitcoinRestricted(r) - - // stop mining - stop <- struct{}{} } func withdrawBTCZRC20(r *runner.E2ERunner, to btcutil.Address, amount *big.Int) *btcjson.TxRawResult { @@ -38,6 +34,9 @@ func withdrawBTCZRC20(r *runner.E2ERunner, to btcutil.Address, amount *big.Int) panic(fmt.Errorf("approve receipt status is not 1")) } + // mine blocks + stop := r.MineBlocks() + // withdraw 'amount' of BTC from ZRC20 to BTC address tx, err = r.BTCZRC20.Withdraw(r.ZevmAuth, []byte(to.EncodeAddress()), amount) if err != nil { @@ -79,6 +78,9 @@ func withdrawBTCZRC20(r *runner.E2ERunner, to btcutil.Address, amount *big.Int) r.Logger.Info(" ScriptPubKey: %s", txOut.ScriptPubKey.Hex) } + // stop mining + stop <- struct{}{} + return rawTx } From ce021e113520dbab64e1d27e5512c968b2dcefff Mon Sep 17 00:00:00 2001 From: Charlie Chen Date: Fri, 1 Mar 2024 11:11:44 -0600 Subject: [PATCH 12/13] made OFAC tests separate and moved them to bottom of list --- cmd/zetae2e/local/bitcoin.go | 2 +- cmd/zetae2e/local/erc20.go | 1 + cmd/zetae2e/local/ethereum.go | 2 +- cmd/zetae2e/local/utils.go | 2 +- cmd/zetae2e/local/zeta.go | 2 +- e2e/e2etests/e2etests.go | 32 ++++++++++++++++++++---------- e2e/e2etests/test_erc20_deposit.go | 2 ++ e2e/e2etests/test_zeta_deposit.go | 2 ++ 8 files changed, 31 insertions(+), 14 deletions(-) diff --git a/cmd/zetae2e/local/bitcoin.go b/cmd/zetae2e/local/bitcoin.go index f5606eba20..3dd4fe3c13 100644 --- a/cmd/zetae2e/local/bitcoin.go +++ b/cmd/zetae2e/local/bitcoin.go @@ -67,9 +67,9 @@ func bitcoinTestRoutine( if err := bitcoinRunner.RunE2ETestsFromNames( e2etests.AllE2ETests, e2etests.TestBitcoinWithdrawName, - e2etests.TestBitcoinWithdrawRestrictedName, e2etests.TestZetaWithdrawBTCRevertName, e2etests.TestCrosschainSwapName, + e2etests.TestBitcoinWithdrawRestrictedName, ); err != nil { return fmt.Errorf("bitcoin tests failed: %v", err) } diff --git a/cmd/zetae2e/local/erc20.go b/cmd/zetae2e/local/erc20.go index c3dbc3f354..2b1b266f83 100644 --- a/cmd/zetae2e/local/erc20.go +++ b/cmd/zetae2e/local/erc20.go @@ -66,6 +66,7 @@ func erc20TestRoutine( e2etests.TestMultipleWithdrawsName, e2etests.TestERC20DepositAndCallRefundName, e2etests.TestZRC20SwapName, + e2etests.TestERC20DepositRestrictedName, ); err != nil { return fmt.Errorf("erc20 tests failed: %v", err) } diff --git a/cmd/zetae2e/local/ethereum.go b/cmd/zetae2e/local/ethereum.go index eb615a315a..8925a8fc51 100644 --- a/cmd/zetae2e/local/ethereum.go +++ b/cmd/zetae2e/local/ethereum.go @@ -56,10 +56,10 @@ func ethereumTestRoutine( if err := ethereumRunner.RunE2ETestsFromNames( e2etests.AllE2ETests, e2etests.TestEtherWithdrawName, - e2etests.TestEtherWithdrawRestrictedName, e2etests.TestContextUpgradeName, e2etests.TestEtherDepositAndCallName, e2etests.TestDepositAndCallRefundName, + e2etests.TestEtherWithdrawRestrictedName, ); err != nil { return fmt.Errorf("ethereum tests failed: %v", err) } diff --git a/cmd/zetae2e/local/utils.go b/cmd/zetae2e/local/utils.go index c24acbe171..f59782183c 100644 --- a/cmd/zetae2e/local/utils.go +++ b/cmd/zetae2e/local/utils.go @@ -85,7 +85,7 @@ func waitKeygenHeight( logger *runner.Logger, ) { // wait for keygen to be completed - keygenHeight := int64(55) + keygenHeight := int64(60) logger.Print("⏳ wait height %v for keygen to be completed", keygenHeight) for { time.Sleep(2 * time.Second) diff --git a/cmd/zetae2e/local/zeta.go b/cmd/zetae2e/local/zeta.go index e45a6215fd..ff62f41c2e 100644 --- a/cmd/zetae2e/local/zeta.go +++ b/cmd/zetae2e/local/zeta.go @@ -59,11 +59,11 @@ func zetaTestRoutine( // run zeta test if err := zetaRunner.RunE2ETestsFromNames( e2etests.AllE2ETests, - e2etests.TestZetaDepositName, e2etests.TestZetaWithdrawName, e2etests.TestMessagePassingName, e2etests.TestMessagePassingRevertFailName, e2etests.TestMessagePassingRevertSuccessName, + e2etests.TestZetaDepositRestrictedName, ); err != nil { return fmt.Errorf("zeta tests failed: %v", err) } diff --git a/e2e/e2etests/e2etests.go b/e2e/e2etests/e2etests.go index 6d9492bcba..ec83befd72 100644 --- a/e2e/e2etests/e2etests.go +++ b/e2e/e2etests/e2etests.go @@ -29,11 +29,13 @@ const ( TestERC20WithdrawName = "erc20_withdraw" TestERC20DepositName = "erc20_deposit" + TestERC20DepositRestrictedName = "erc20_deposit_restricted" TestEtherDepositName = "eth_deposit" TestEtherWithdrawName = "eth_withdraw" TestEtherWithdrawRestrictedName = "eth_withdraw_restricted" TestBitcoinDepositName = "bitcoin_deposit" TestZetaDepositName = "zeta_deposit" + TestZetaDepositRestrictedName = "zeta_deposit_restricted" TestDonationEtherName = "donation_ether" @@ -100,11 +102,6 @@ var AllE2ETests = []runner.E2ETest{ "withdraw BTC from ZEVM", TestBitcoinWithdraw, }, - { - TestBitcoinWithdrawRestrictedName, - "withdraw Bitcoin from ZEVM to restricted address", - TestBitcoinWithdrawRestricted, - }, { TestCrosschainSwapName, "testing Bitcoin ERC20 cross-chain swap", @@ -165,11 +162,6 @@ var AllE2ETests = []runner.E2ETest{ "withdraw Ether from ZEVM", TestEtherWithdraw, }, - { - TestEtherWithdrawRestrictedName, - "withdraw Ether from ZEVM to restricted address", - TestEtherWithdrawRestricted, - }, { TestBitcoinDepositName, "deposit Bitcoin into ZEVM", @@ -200,4 +192,24 @@ var AllE2ETests = []runner.E2ETest{ "stress test BTC deposit", TestStressBTCDeposit, }, + { + TestZetaDepositRestrictedName, + "deposit ZETA from Ethereum to ZEVM restricted address", + TestZetaDepositRestricted, + }, + { + TestERC20DepositRestrictedName, + "deposit ERC20 into ZEVM restricted address", + TestERC20DepositRestricted, + }, + { + TestEtherWithdrawRestrictedName, + "withdraw Ether from ZEVM to restricted address", + TestEtherWithdrawRestricted, + }, + { + TestBitcoinWithdrawRestrictedName, + "withdraw Bitcoin from ZEVM to restricted address", + TestBitcoinWithdrawRestricted, + }, } diff --git a/e2e/e2etests/test_erc20_deposit.go b/e2e/e2etests/test_erc20_deposit.go index af1bb6266a..6efd066141 100644 --- a/e2e/e2etests/test_erc20_deposit.go +++ b/e2e/e2etests/test_erc20_deposit.go @@ -15,7 +15,9 @@ func TestERC20Deposit(r *runner.E2ERunner) { // wait for the cctx to be mined cctx := utils.WaitCctxMinedByInTxHash(r.Ctx, hash.Hex(), r.CctxClient, r.Logger, r.CctxTimeout) r.Logger.CCTX(*cctx, "deposit") +} +func TestERC20DepositRestricted(r *runner.E2ERunner) { // deposit ERC20 to restricted address r.DepositERC20WithAmountAndMessage(ethcommon.HexToAddress(testutils.RestrictedEVMAddressTest), big.NewInt(100000), []byte{}) } diff --git a/e2e/e2etests/test_zeta_deposit.go b/e2e/e2etests/test_zeta_deposit.go index 7b079d2953..71c10328e3 100644 --- a/e2e/e2etests/test_zeta_deposit.go +++ b/e2e/e2etests/test_zeta_deposit.go @@ -16,7 +16,9 @@ func TestZetaDeposit(r *runner.E2ERunner) { // wait for the cctx to be mined cctx := utils.WaitCctxMinedByInTxHash(r.Ctx, hash.Hex(), r.CctxClient, r.Logger, r.CctxTimeout) r.Logger.CCTX(*cctx, "deposit") +} +func TestZetaDepositRestricted(r *runner.E2ERunner) { // Deposit 1 Zeta to restricted address r.DepositZetaWithAmount(ethcommon.HexToAddress(testutils.RestrictedEVMAddressTest), big.NewInt(1e18)) } From 116fd213cb15fc200c77f30d75fd759cdd1b1559 Mon Sep 17 00:00:00 2001 From: Charlie Chen Date: Fri, 1 Mar 2024 13:19:23 -0600 Subject: [PATCH 13/13] fix nosec false positive --- e2e/e2etests/e2etests.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/e2e/e2etests/e2etests.go b/e2e/e2etests/e2etests.go index ec83befd72..c23f5030d9 100644 --- a/e2e/e2etests/e2etests.go +++ b/e2e/e2etests/e2etests.go @@ -27,8 +27,9 @@ const ( TestDepositEtherLiquidityCapName = "deposit_eth_liquidity_cap" TestMyTestName = "my_test" - TestERC20WithdrawName = "erc20_withdraw" - TestERC20DepositName = "erc20_deposit" + TestERC20WithdrawName = "erc20_withdraw" + TestERC20DepositName = "erc20_deposit" + // #nosec G101: Potential hardcoded credentials (gosec), not a credential TestERC20DepositRestrictedName = "erc20_deposit_restricted" TestEtherDepositName = "eth_deposit" TestEtherWithdrawName = "eth_withdraw"