diff --git a/.gitignore b/.gitignore index 6a53c9be7b..080a8030b2 100644 --- a/.gitignore +++ b/.gitignore @@ -25,7 +25,6 @@ contrib/meta.pem.pub dispatch_payload.json .github/workflows/local-testing.yml - # Hardhat files cache localnet/artifacts @@ -53,4 +52,7 @@ contrib/localnet/blockscout/services/logs/* contrib/localnet/blockscout/services/redis-data/* contrib/localnet/blockscout/services/stats-db-data/* contrib/localnet/grafana/addresses.txt -contrib/localnet/addresses.txt \ No newline at end of file +contrib/localnet/addresses.txt + +# Config for e2e tests +e2e_conf* \ No newline at end of file diff --git a/Makefile b/Makefile index 2fe0da31cd..946d7a566b 100644 --- a/Makefile +++ b/Makefile @@ -183,7 +183,7 @@ generate: proto openapi specs typescript docs-zetacored ############################################################################### install-zetae2e: go.sum - @echo "--> Installing orchestrator" + @echo "--> Installing zetae2e" @go install -mod=readonly $(BUILD_FLAGS) ./cmd/zetae2e .PHONY: install-zetae2e diff --git a/changelog.md b/changelog.md index 4ad8ef044f..b7bb34d3dd 100644 --- a/changelog.md +++ b/changelog.md @@ -2,6 +2,25 @@ ## Unreleased +* `zetaclientd start` : 2 inputs required from stdin + +### Refactor + +* [1630](https://github.com/zeta-chain/node/pull/1630) added password prompts for hotkey and tss keyshare in zetaclient + Starting zetaclient now requires two passwords to be input; one for the hotkey and another for the tss key-share. + +### Fixes + +* [1690](https://github.com/zeta-chain/node/issues/1690) - double watched gas prices and fix btc scheduler +* [1687](https://github.com/zeta-chain/node/pull/1687) - only use EVM supported chains for gas stability pool +* [1692](https://github.com/zeta-chain/node/pull/1692) - fix get params query for emissions module + +### Tests + +* [1584](https://github.com/zeta-chain/node/pull/1584) - allow to run E2E tests on any networks + +## Version: v12.2.4 + ### Fixes * [1638](https://github.com/zeta-chain/node/issues/1638) - additional check to make sure external chain height always increases @@ -17,6 +36,7 @@ ## Version: v12.1.0 ### Tests + * [1577](https://github.com/zeta-chain/node/pull/1577) - add chain header tests in E2E tests and fix admin tests ### Features diff --git a/cmd/zetaclientd/keygen_tss.go b/cmd/zetaclientd/keygen_tss.go index 0b21f880ff..1c77086f59 100644 --- a/cmd/zetaclientd/keygen_tss.go +++ b/cmd/zetaclientd/keygen_tss.go @@ -27,7 +27,9 @@ func GenerateTss(logger zerolog.Logger, priKey secp256k1.PrivKey, ts *mc.TelemetryServer, tssHistoricalList []observertypes.TSS, - metrics *metrics.Metrics) (*mc.TSS, error) { + metrics *metrics.Metrics, + tssPassword string, + hotkeyPassword string) (*mc.TSS, error) { keygenLogger := logger.With().Str("module", "keygen").Logger() // Bitcoin chain ID is currently used for using the correct signature format @@ -47,6 +49,8 @@ func GenerateTss(logger zerolog.Logger, tssHistoricalList, metrics, bitcoinChainID, + tssPassword, + hotkeyPassword, ) if err != nil { keygenLogger.Error().Err(err).Msg("NewTSS error") diff --git a/cmd/zetaclientd/start.go b/cmd/zetaclientd/start.go index f0528fb9d1..483cab01ee 100644 --- a/cmd/zetaclientd/start.go +++ b/cmd/zetaclientd/start.go @@ -1,6 +1,7 @@ package main import ( + "bufio" "encoding/json" "fmt" "io" @@ -45,6 +46,12 @@ func start(_ *cobra.Command, _ []string) error { SetupConfigForTest() + //Prompt for Hotkey and TSS key-share passwords + hotkeyPass, tssKeyPass, err := promptPasswords() + if err != nil { + return err + } + //Load Config file given path cfg, err := config.Load(rootArgs.zetaCoreHome) if err != nil { @@ -77,7 +84,7 @@ func start(_ *cobra.Command, _ []string) error { // CreateZetaBridge: Zetabridge is used for all communication to zetacore , which this client connects to. // Zetacore accumulates votes , and provides a centralized source of truth for all clients - zetaBridge, err := CreateZetaBridge(cfg, telemetryServer) + zetaBridge, err := CreateZetaBridge(cfg, telemetryServer, hotkeyPass) if err != nil { panic(err) } @@ -120,7 +127,7 @@ func start(_ *cobra.Command, _ []string) error { // The bridgePk is private key for the Hotkey. The Hotkey is used to sign all inbound transactions // Each node processes a portion of the key stored in ~/.tss by default . Custom location can be specified in config file during init. // After generating the key , the address is set on the zetacore - bridgePk, err := zetaBridge.GetKeys().GetPrivateKey() + bridgePk, err := zetaBridge.GetKeys().GetPrivateKey(hotkeyPass) if err != nil { startLogger.Error().Err(err).Msg("zetabridge getPrivateKey error") } @@ -160,7 +167,7 @@ func start(_ *cobra.Command, _ []string) error { } telemetryServer.SetIPAddress(cfg.PublicIP) - tss, err := GenerateTss(masterLogger, cfg, zetaBridge, peers, priKey, telemetryServer, tssHistoricalList, metrics) + tss, err := GenerateTss(masterLogger, cfg, zetaBridge, peers, priKey, telemetryServer, tssHistoricalList, metrics, tssKeyPass, hotkeyPass) if err != nil { return err } @@ -309,3 +316,26 @@ func initPreParams(path string) { } } } + +// promptPasswords() This function will prompt for passwords which will be used to decrypt two key files: +// 1. HotKey +// 2. TSS key-share +func promptPasswords() (string, string, error) { + reader := bufio.NewReader(os.Stdin) + fmt.Print("HotKey Password: ") + hotKeyPass, err := reader.ReadString('\n') + if err != nil { + return "", "", err + } + fmt.Print("TSS Password: ") + TSSKeyPass, err := reader.ReadString('\n') + if err != nil { + return "", "", err + } + + if TSSKeyPass == "" { + return "", "", errors.New("tss password is required to start zetaclient") + } + + return hotKeyPass, TSSKeyPass, err +} diff --git a/cmd/zetaclientd/utils.go b/cmd/zetaclientd/utils.go index 9b17b1ef80..c1a4dfa069 100644 --- a/cmd/zetaclientd/utils.go +++ b/cmd/zetaclientd/utils.go @@ -15,7 +15,7 @@ func CreateAuthzSigner(granter string, grantee sdk.AccAddress) { zetaclient.SetupAuthZSignerList(granter, grantee) } -func CreateZetaBridge(cfg *config.Config, telemetry *zetaclient.TelemetryServer) (*zetaclient.ZetaCoreBridge, error) { +func CreateZetaBridge(cfg *config.Config, telemetry *zetaclient.TelemetryServer, hotkeyPassword string) (*zetaclient.ZetaCoreBridge, error) { hotKey := cfg.AuthzHotkey if cfg.HsmMode { hotKey = cfg.HsmHotKey @@ -23,7 +23,7 @@ func CreateZetaBridge(cfg *config.Config, telemetry *zetaclient.TelemetryServer) chainIP := cfg.ZetaCoreURL - kb, _, err := zetaclient.GetKeyringKeybase(cfg) + kb, _, err := zetaclient.GetKeyringKeybase(cfg, hotkeyPassword) if err != nil { return nil, err } @@ -33,7 +33,7 @@ func CreateZetaBridge(cfg *config.Config, telemetry *zetaclient.TelemetryServer) return nil, err } - k := zetaclient.NewKeysWithKeybase(kb, granterAddreess, cfg.AuthzHotkey) + k := zetaclient.NewKeysWithKeybase(kb, granterAddreess, cfg.AuthzHotkey, hotkeyPassword) bridge, err := zetaclient.NewZetaCoreBridge(k, chainIP, hotKey, cfg.ChainID, cfg.HsmMode, telemetry) if err != nil { diff --git a/cmd/zetae2e/balances.go b/cmd/zetae2e/balances.go new file mode 100644 index 0000000000..d1643dab53 --- /dev/null +++ b/cmd/zetae2e/balances.go @@ -0,0 +1,89 @@ +package main + +import ( + "context" + "errors" + + ethcommon "github.com/ethereum/go-ethereum/common" + "github.com/fatih/color" + "github.com/spf13/cobra" + "github.com/zeta-chain/zetacore/app" + zetae2econfig "github.com/zeta-chain/zetacore/cmd/zetae2e/config" + "github.com/zeta-chain/zetacore/contrib/localnet/orchestrator/smoketest/config" + "github.com/zeta-chain/zetacore/contrib/localnet/orchestrator/smoketest/runner" + "github.com/zeta-chain/zetacore/contrib/localnet/orchestrator/smoketest/utils" +) + +const flagSkipBTC = "skip-btc" + +// NewBalancesCmd returns the balances command +// which shows from the key and rpc, the balance of the account on different network +func NewBalancesCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "balances [config-file]", + Short: "Show account balances on networks for E2E tests", + RunE: runBalances, + Args: cobra.ExactArgs(1), + } + cmd.Flags().Bool( + flagSkipBTC, + false, + "skip the BTC network", + ) + return cmd +} + +func runBalances(cmd *cobra.Command, args []string) error { + // read the config file + conf, err := config.ReadConfig(args[0]) + if err != nil { + return err + } + + skipBTC, err := cmd.Flags().GetBool(flagSkipBTC) + if err != nil { + return err + } + + // initialize logger + logger := runner.NewLogger(false, color.FgHiCyan, "") + + // set config + app.SetConfig() + + // initialize context + ctx, cancel := context.WithCancel(context.Background()) + + // get EVM address from config + evmAddr := conf.Accounts.EVMAddress + if !ethcommon.IsHexAddress(evmAddr) { + cancel() + return errors.New("invalid EVM address") + } + + // initialize deployer runner with config + r, err := zetae2econfig.RunnerFromConfig( + ctx, + "e2e", + cancel, + conf, + ethcommon.HexToAddress(evmAddr), + conf.Accounts.EVMPrivKey, + utils.FungibleAdminName, // placeholder value, not used + FungibleAdminMnemonic, // placeholder value, not used + logger, + ) + if err != nil { + cancel() + return err + } + + balances, err := r.GetAccountBalances(skipBTC) + if err != nil { + cancel() + return err + } + r.PrintAccountBalances(balances) + + return nil +} diff --git a/cmd/zetae2e/bitcoin_address.go b/cmd/zetae2e/bitcoin_address.go new file mode 100644 index 0000000000..f1728e584b --- /dev/null +++ b/cmd/zetae2e/bitcoin_address.go @@ -0,0 +1,92 @@ +package main + +import ( + "context" + "errors" + + ethcommon "github.com/ethereum/go-ethereum/common" + "github.com/fatih/color" + "github.com/spf13/cobra" + "github.com/zeta-chain/zetacore/app" + zetae2econfig "github.com/zeta-chain/zetacore/cmd/zetae2e/config" + "github.com/zeta-chain/zetacore/contrib/localnet/orchestrator/smoketest/config" + "github.com/zeta-chain/zetacore/contrib/localnet/orchestrator/smoketest/runner" + "github.com/zeta-chain/zetacore/contrib/localnet/orchestrator/smoketest/utils" +) + +const flagPrivKey = "privkey" + +// NewBitcoinAddressCmd returns the bitcoin address command +// which shows from the used config file, the bitcoin address that can be used to receive funds for the E2E tests +func NewBitcoinAddressCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "bitcoin-address [config-file] ", + Short: "Show Bitcoin address to receive funds for E2E tests", + RunE: runBitcoinAddress, + Args: cobra.ExactArgs(1), + } + cmd.Flags().Bool( + flagPrivKey, + false, + "show the priv key in WIF format", + ) + return cmd +} + +func runBitcoinAddress(cmd *cobra.Command, args []string) error { + showPrivKey, err := cmd.Flags().GetBool(flagPrivKey) + if err != nil { + return err + } + + // read the config file + conf, err := config.ReadConfig(args[0]) + if err != nil { + return err + } + + // initialize logger + logger := runner.NewLogger(false, color.FgHiYellow, "") + + // set config + app.SetConfig() + + // initialize context + ctx, cancel := context.WithCancel(context.Background()) + + // get EVM address from config + evmAddr := conf.Accounts.EVMAddress + if !ethcommon.IsHexAddress(evmAddr) { + cancel() + return errors.New("invalid EVM address") + } + + // initialize deployer runner with config + r, err := zetae2econfig.RunnerFromConfig( + ctx, + "e2e", + cancel, + conf, + ethcommon.HexToAddress(evmAddr), + conf.Accounts.EVMPrivKey, + utils.FungibleAdminName, // placeholder value, not used + FungibleAdminMnemonic, // placeholder value, not used + logger, + ) + if err != nil { + cancel() + return err + } + + addr, privKey, err := r.GetBtcAddress() + if err != nil { + return err + } + + logger.Print("* BTC address: %s", addr) + if showPrivKey { + logger.Print("* BTC privkey: %s", privKey) + } + + return nil +} diff --git a/cmd/zetae2e/config/clients.go b/cmd/zetae2e/config/clients.go index e3b8fcebc2..99752648e4 100644 --- a/cmd/zetae2e/config/clients.go +++ b/cmd/zetae2e/config/clients.go @@ -2,6 +2,7 @@ package config import ( "context" + "fmt" "google.golang.org/grpc" @@ -33,19 +34,19 @@ func getClientsFromConfig(ctx context.Context, conf config.Config, evmPrivKey st ) { btcRPCClient, err := getBtcClient(conf.RPCs.Bitcoin) if err != nil { - return nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, err + return nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, fmt.Errorf("failed to get btc client: %w", err) } goerliClient, goerliAuth, err := getEVMClient(ctx, conf.RPCs.EVM, evmPrivKey) if err != nil { - return nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, err + return nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, fmt.Errorf("failed to get evm client: %w", err) } cctxClient, fungibleClient, authClient, bankClient, observerClient, err := getZetaClients(conf.RPCs.ZetaCoreGRPC) if err != nil { - return nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, err + return nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, fmt.Errorf("failed to get zeta clients: %w", err) } zevmClient, zevmAuth, err := getEVMClient(ctx, conf.RPCs.Zevm, evmPrivKey) if err != nil { - return nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, err + return nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, fmt.Errorf("failed to get zevm client: %w", err) } return btcRPCClient, goerliClient, @@ -61,14 +62,26 @@ func getClientsFromConfig(ctx context.Context, conf config.Config, evmPrivKey st } // getBtcClient get btc client -func getBtcClient(rpc string) (*rpcclient.Client, error) { +func getBtcClient(rpcConf config.BitcoinRPC) (*rpcclient.Client, error) { + var param string + switch rpcConf.Params { + case config.Regnet: + case config.Testnet3: + param = "testnet3" + case config.Mainnet: + param = "mainnet" + default: + return nil, fmt.Errorf("invalid bitcoin params %s", rpcConf.Params) + } + connCfg := &rpcclient.ConnConfig{ - Host: rpc, - User: "smoketest", - Pass: "123", - HTTPPostMode: true, - DisableTLS: true, - Params: "testnet3", + Host: rpcConf.Host, + User: rpcConf.User, + Pass: rpcConf.Pass, + HTTPPostMode: rpcConf.HTTPPostMode, + DisableTLS: rpcConf.DisableTLS, + Params: param, + //Endpoint: "/wallet/user", } return rpcclient.New(connCfg, nil) } diff --git a/cmd/zetae2e/config/config.go b/cmd/zetae2e/config/config.go index f307afa02e..d9a4f17edc 100644 --- a/cmd/zetae2e/config/config.go +++ b/cmd/zetae2e/config/config.go @@ -2,6 +2,7 @@ package config import ( "context" + "fmt" ethcommon "github.com/ethereum/go-ethereum/common" "github.com/zeta-chain/zetacore/contrib/localnet/orchestrator/smoketest/config" @@ -34,7 +35,7 @@ func RunnerFromConfig( zevmAuth, err := getClientsFromConfig(ctx, conf, evmUserPrivKey) if err != nil { - return nil, err + return nil, fmt.Errorf("failed to get clients from config: %w", err) } // initialize client to send messages to ZetaChain zetaTxServer, err := txserver.NewZetaTxServer( @@ -44,7 +45,7 @@ func RunnerFromConfig( conf.ZetaChainID, ) if err != nil { - return nil, err + return nil, fmt.Errorf("failed to initialize ZetaChain tx server: %w", err) } // initialize smoke test runner @@ -71,6 +72,16 @@ func RunnerFromConfig( // set contracts err = setContractsFromConfig(sm, conf) + if err != nil { + return nil, fmt.Errorf("failed to set contracts from config: %w", err) + } + + // set bitcoin params + chainParams, err := conf.RPCs.Bitcoin.Params.GetParams() + if err != nil { + return nil, fmt.Errorf("failed to get bitcoin params: %w", err) + } + sm.BitcoinParams = &chainParams return sm, err } diff --git a/cmd/zetae2e/config/contracts.go b/cmd/zetae2e/config/contracts.go index c49ce98f8b..0b80d44c4c 100644 --- a/cmd/zetae2e/config/contracts.go +++ b/cmd/zetae2e/config/contracts.go @@ -7,7 +7,9 @@ import ( "github.com/zeta-chain/protocol-contracts/pkg/contracts/evm/erc20custody.sol" zetaeth "github.com/zeta-chain/protocol-contracts/pkg/contracts/evm/zeta.eth.sol" zetaconnectoreth "github.com/zeta-chain/protocol-contracts/pkg/contracts/evm/zetaconnector.eth.sol" + "github.com/zeta-chain/protocol-contracts/pkg/contracts/zevm/connectorzevm.sol" "github.com/zeta-chain/protocol-contracts/pkg/contracts/zevm/systemcontract.sol" + "github.com/zeta-chain/protocol-contracts/pkg/contracts/zevm/wzeta.sol" "github.com/zeta-chain/protocol-contracts/pkg/contracts/zevm/zrc20.sol" "github.com/zeta-chain/protocol-contracts/pkg/uniswap/v2-core/contracts/uniswapv2factory.sol" uniswapv2router "github.com/zeta-chain/protocol-contracts/pkg/uniswap/v2-periphery/contracts/uniswapv2router02.sol" @@ -135,6 +137,26 @@ func setContractsFromConfig(r *runner.SmokeTestRunner, conf config.Config) error return err } } + if c := conf.Contracts.ZEVM.ConnectorZEVMAddr; c != "" { + if !ethcommon.IsHexAddress(c) { + return fmt.Errorf("invalid ConnectorZEVMAddr: %s", c) + } + r.ConnectorZEVMAddr = ethcommon.HexToAddress(c) + r.ConnectorZEVM, err = connectorzevm.NewZetaConnectorZEVM(r.ConnectorZEVMAddr, r.ZevmClient) + if err != nil { + return err + } + } + if c := conf.Contracts.ZEVM.WZetaAddr; c != "" { + if !ethcommon.IsHexAddress(c) { + return fmt.Errorf("invalid WZetaAddr: %s", c) + } + r.WZetaAddr = ethcommon.HexToAddress(c) + r.WZeta, err = wzeta.NewWETH9(r.WZetaAddr, r.ZevmClient) + if err != nil { + return err + } + } if c := conf.Contracts.ZEVM.ZEVMSwapAppAddr; c != "" { if !ethcommon.IsHexAddress(c) { return fmt.Errorf("invalid ZEVMSwapAppAddr: %s", c) diff --git a/cmd/zetae2e/config/example.yml b/cmd/zetae2e/config/example.yml new file mode 100644 index 0000000000..af38812323 --- /dev/null +++ b/cmd/zetae2e/config/example.yml @@ -0,0 +1,35 @@ +zeta_chain_id: "athens_7001-1" +accounts: + evm_address: "" + evm_priv_key: "" +rpcs: + zevm: ", generally using port 8545" + evm: ", generally using port 8545" + bitcoin: + host: "" + user: "" + pass: "" + http_post_mode: true + disable_tls: true + params: "" + zetacore_grpc: ", generally using port 9090" + zetacore_rpc: ", generally using port 26657" +contracts: + zevm: + system_contract: "0xEdf1c3275d13489aCdC6cD6eD246E72458B8795B" + eth_zrc20: "0x13A0c5930C028511Dc02665E7285134B6d11A5f4" + usdt_zrc20: "0x0cbe0dF132a6c6B4a2974Fa1b7Fb953CF0Cc798a" + btc_zrc20: "0x65a45c57636f9BcCeD4fe193A602008578BcA90b" + uniswap_factory: "0x9fd96203f7b22bCF72d9DCb40ff98302376cE09c" + uniswap_router: "0x2ca7d64A7EFE2D62A725E2B35Cf7230D6677FfEe" + evm: + zeta_eth: "0x0000c304d2934c00db1d51995b9f6996affd17c0" + connector_eth: "0x00005e3125aba53c5652f9f0ce1a4cf91d8b15ea" + custody: "0x000047f11c6e42293f433c82473532e869ce4ec5" + usdt: "0x07865c6e87b9f70255377e024ace6630c1eaa37f" +test_list: +# - "erc20_deposit" +# - "erc20_withdraw" +# - "eth_deposit" +# - "eth_withdraw" + diff --git a/cmd/zetae2e/init.go b/cmd/zetae2e/init.go index 66d7e4fd3f..eb112c98e2 100644 --- a/cmd/zetae2e/init.go +++ b/cmd/zetae2e/init.go @@ -23,7 +23,7 @@ func NewInitCmd() *cobra.Command { InitCmd.Flags().StringVar(&initConf.RPCs.ZetaCoreGRPC, "grpcURL", "zetacore0:9090", "--grpcURL zetacore0:9090") InitCmd.Flags().StringVar(&initConf.RPCs.ZetaCoreRPC, "rpcURL", "http://zetacore0:26657", "--rpcURL http://zetacore0:26657") InitCmd.Flags().StringVar(&initConf.RPCs.Zevm, "zevmURL", "http://zetacore0:8545", "--zevmURL http://zetacore0:8545") - InitCmd.Flags().StringVar(&initConf.RPCs.Bitcoin, "btcURL", "bitcoin:18443", "--grpcURL bitcoin:18443") + InitCmd.Flags().StringVar(&initConf.RPCs.Bitcoin.Host, "btcURL", "bitcoin:18443", "--grpcURL bitcoin:18443") InitCmd.Flags().StringVar(&initConf.ZetaChainID, "chainID", "athens_101-1", "--chainID athens_101-1") InitCmd.Flags().StringVar(&configFile, local.FlagConfigFile, "smoketest.config", "--cfg ./smoketest.config") diff --git a/cmd/zetae2e/list_tests.go b/cmd/zetae2e/list_tests.go new file mode 100644 index 0000000000..5e23da7090 --- /dev/null +++ b/cmd/zetae2e/list_tests.go @@ -0,0 +1,48 @@ +package main + +import ( + "fmt" + + "github.com/fatih/color" + "github.com/spf13/cobra" + "github.com/zeta-chain/zetacore/contrib/localnet/orchestrator/smoketest/runner" + "github.com/zeta-chain/zetacore/contrib/localnet/orchestrator/smoketest/smoketests" +) + +// NewListTestsCmd returns the list test cmd +// which list the available tests +func NewListTestsCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "list-tests", + Short: "List available tests", + RunE: runListTests, + Args: cobra.NoArgs, + } + return cmd +} + +func runListTests(_ *cobra.Command, _ []string) error { + logger := runner.NewLogger(false, color.FgHiGreen, "") + + logger.Print("Available tests:") + renderTests(logger, smoketests.AllSmokeTests) + + return nil +} + +func renderTests(logger *runner.Logger, tests []runner.SmokeTest) { + // Find the maximum length of the Name field + maxNameLength := 0 + for _, test := range tests { + if len(test.Name) > maxNameLength { + maxNameLength = len(test.Name) + } + } + + // Formatting and printing the table + formatString := fmt.Sprintf("%%-%ds | %%s", maxNameLength) + logger.Print(formatString, "Name", "Description") + for _, test := range tests { + logger.Print(formatString, test.Name, test.Description) + } +} diff --git a/cmd/zetae2e/local/bitcoin.go b/cmd/zetae2e/local/bitcoin.go index eaf700465e..908b02b13b 100644 --- a/cmd/zetae2e/local/bitcoin.go +++ b/cmd/zetae2e/local/bitcoin.go @@ -67,7 +67,7 @@ func bitcoinTestRoutine( if err := bitcoinRunner.RunSmokeTestsFromNames( smoketests.AllSmokeTests, smoketests.TestBitcoinWithdrawName, - smoketests.TestSendZetaOutBTCRevertName, + smoketests.TestZetaWithdrawBTCRevertName, smoketests.TestCrosschainSwapName, ); 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 66703121e8..ed82b80308 100644 --- a/cmd/zetae2e/local/erc20.go +++ b/cmd/zetae2e/local/erc20.go @@ -62,7 +62,7 @@ func erc20TestRoutine( // run erc20 test if err := erc20Runner.RunSmokeTestsFromNames( smoketests.AllSmokeTests, - smoketests.TestWithdrawERC20Name, + smoketests.TestERC20WithdrawName, smoketests.TestMultipleWithdrawsName, smoketests.TestERC20DepositAndCallRefundName, smoketests.TestZRC20SwapName, diff --git a/cmd/zetae2e/local/ethereum.go b/cmd/zetae2e/local/ethereum.go index 90d6a35dfb..2b4ffd7eb5 100644 --- a/cmd/zetae2e/local/ethereum.go +++ b/cmd/zetae2e/local/ethereum.go @@ -55,6 +55,7 @@ func ethereumTestRoutine( // to make it faster to catch up with the latest block header if err := ethereumRunner.RunSmokeTestsFromNames( smoketests.AllSmokeTests, + smoketests.TestEtherWithdrawName, smoketests.TestContextUpgradeName, smoketests.TestEtherDepositAndCallName, smoketests.TestDepositAndCallRefundName, diff --git a/cmd/zetae2e/local/local.go b/cmd/zetae2e/local/local.go index 92b16526f3..db82a23c85 100644 --- a/cmd/zetae2e/local/local.go +++ b/cmd/zetae2e/local/local.go @@ -195,7 +195,9 @@ func localE2ETest(cmd *cobra.Command, _ []string) { } // query and set the TSS - deployerRunner.SetTSSAddresses() + if err := deployerRunner.SetTSSAddresses(); err != nil { + panic(err) + } // setting up the networks if !skipSetup { diff --git a/cmd/zetae2e/local/zeta.go b/cmd/zetae2e/local/zeta.go index cc2bb6dd23..bc0f5a46bc 100644 --- a/cmd/zetae2e/local/zeta.go +++ b/cmd/zetae2e/local/zeta.go @@ -59,7 +59,7 @@ func zetaTestRoutine( // run zeta test if err := zetaRunner.RunSmokeTestsFromNames( smoketests.AllSmokeTests, - smoketests.TestSendZetaOutName, + smoketests.TestZetaWithdrawName, smoketests.TestMessagePassingName, smoketests.TestMessagePassingRevertFailName, smoketests.TestMessagePassingRevertSuccessName, diff --git a/cmd/zetae2e/readme.md b/cmd/zetae2e/readme.md new file mode 100644 index 0000000000..7ef33956e6 --- /dev/null +++ b/cmd/zetae2e/readme.md @@ -0,0 +1,190 @@ +# `zetae2e` + +### Basics + +`zetae2e` is a CLI tool allowing to quickly test ZetaChain functionality. + +`zetae2e` can communicate with ZetaChain, EVM and Bitcoin network to test and track the full workflow of a cross-chain transaction. + +### Getting Started + +`zetae2e` can be installed with the command: + +```go +make install-zetae2e + +zetae2e -h +``` + +### Config + +The command takes a config file as input containing RPC nodes to connect to, hotkey wallet information for interaction with networks, and addresses of the deployed contracts to be used. + +This is an example of config for interaction with Athens3: + +```go +zeta_chain_id: "athens_7001-1" +accounts: + evm_address: "" + evm_priv_key: "" +rpcs: + zevm: ", generally using port 8545" + evm: ", generally using port 8545" + bitcoin: + host: "" + user: "" + pass: "" + http_post_mode: true + disable_tls: true + params: "" + zetacore_grpc: ", generally using port 9090" + zetacore_rpc: ", generally using port 26657" +contracts: + zevm: + system_contract: "0xEdf1c3275d13489aCdC6cD6eD246E72458B8795B" + eth_zrc20: "0x13A0c5930C028511Dc02665E7285134B6d11A5f4" + usdt_zrc20: "0x0cbe0dF132a6c6B4a2974Fa1b7Fb953CF0Cc798a" + btc_zrc20: "0x65a45c57636f9BcCeD4fe193A602008578BcA90b" + uniswap_factory: "0x9fd96203f7b22bCF72d9DCb40ff98302376cE09c" + uniswap_router: "0x2ca7d64A7EFE2D62A725E2B35Cf7230D6677FfEe" + evm: + zeta_eth: "0x0000c304d2934c00db1d51995b9f6996affd17c0" + connector_eth: "0x00005e3125aba53c5652f9f0ce1a4cf91d8b15ea" + custody: "0x000047f11c6e42293f433c82473532e869ce4ec5" + usdt: "0x07865c6e87b9f70255377e024ace6630c1eaa37f" +test_list: +# - "erc20_deposit" +# - "erc20_withdraw" +# - "eth_deposit" +# - "eth_withdraw" +``` + +### Bitcoin setup +Interaction with the Bitcoin node will require setting up a specific node tracking the address. It can be set locally following the guide [Using Bitcoin Docker Image for Local Development](https://www.notion.so/Using-Bitcoin-Docker-Image-for-Local-Development-bf7e84c58f22431fb52f17a471997e1f?pvs=21) + +If an error occurs mention that wallets are not loaded. The following commands might need to be run in the Docker container: + +```go +docker exec -it bash + +bitcoin-cli -testnet -rpcuser=${bitcoin_username} -rpcpassword=${bitcoin_password} -named createwallet wallet_name=${WALLET_NAME} disable_private_keys=false load_on_startup=true +bitcoin-cli -testnet -rpcuser=${bitcoin_username} -rpcpassword=${bitcoin_password} importaddress "${WALLET_ADDRESS}" "${WALLET_NAME}" true +bitcoin-cli -testnet -rpcuser=${bitcoin_username} -rpcpassword=${bitcoin_password} importprivkey "your_private_key" "${WALLET_NAME}" false +``` + +### Commands + +Show the balances of the accounts used on the different networks: + +```go +zetae2e balances [config] +``` + +Show the Bitcoin address (the address is derived from the Ethereum private key, this address must therefore be found to perform the Bitcoin test): + +```go +zetae2e bitcoin-address [config] +``` + +The list of tests to be run can be found by running following command: + +```go +zetae2e list-tests +``` + +Run tests: + +```go +zetae2e run [config] --verbose +``` + +Since cctxs might take a longer time to be processed on live networks, it is highly recommended to use `--verbose` flag to see the current status of the cctx workflow. + +### Testing a gas ZRC20 from an EVM chain + +Testing a gas token requires the following values to be defined in the config: + +```go +zeta_chain_id +accounts: + evm_address + evm_priv_key +rpcs: + zevm + evm + zetacore_grpc + zetacore_rpc +contracts: + zevm: + system_contract + eth_zrc20 + uniswap_factory + uniswap_router + evm: + zeta_eth + connector_eth + custody: "0x000047f11c6e42293f433c82473532e869ce4ec5" +test_list: + - "eth_deposit" + - "eth_withdraw" +``` + +One of the tests can be commented out in case only a deposit or a withdrawal is to be tested. +Testing an ERC20 ZRC20 from an EVM chain + +Testing ZRC20 requires the same config as for the gas tokens, but must include the `usdt` field that contains the address of the ERC20 on the evm chain and `usdt_zrc20` on ZetaChain. + +It is currently named USDT because it was the defacto ERC20 tested in local tests, this field will be renamed into a more generic name in the future + +```go +zeta_chain_id +accounts: + evm_address + evm_priv_key +rpcs: + zevm + evm + zetacore_grpc + zetacore_rpc +contracts: + zevm: + usdt_zrc20 + evm: + usdt +test_list: + - "erc20_deposit" + - "erc20_withdraw" +``` + +### Testing a ZRC20 from a Bitcoin chain + +The following values must be set in the config in order to test Bitcoin functionality + +```go +zeta_chain_id +accounts: + evm_address + evm_priv_key +rpcs: + zevm + bitcoin: + host + user + pass + http_post_mode + disable_tls + params + zetacore_grpc + zetacore_rpc +contracts: + zevm: + system_contract + btc_zrc20 + uniswap_factory + uniswap_router +test_list: + - "bitcoin_deposit" + - "bitcoin_withdraw" +``` + +### TODO: message passing \ No newline at end of file diff --git a/cmd/zetae2e/root.go b/cmd/zetae2e/root.go index fb93caa131..ba82430a45 100644 --- a/cmd/zetae2e/root.go +++ b/cmd/zetae2e/root.go @@ -5,13 +5,25 @@ import ( "github.com/zeta-chain/zetacore/cmd/zetae2e/local" ) +var asciiArt = ` + _ ____ + _______| |_ __ _ ___|___ \ ___ +|_ / _ \ __/ _ |/ _ \ __) / _ \ + / / __/ || (_| | __// __/ __/ +/___\___|\__\__,_|\___|_____\___| +` + func NewRootCmd() *cobra.Command { cmd := &cobra.Command{ Use: "zetae2e", - Short: "E2E tests CLI", + Short: asciiArt, } cmd.AddCommand( NewRunCmd(), + NewBalancesCmd(), + NewBitcoinAddressCmd(), + NewListTestsCmd(), + NewShowTSSCmd(), local.NewLocalCmd(), NewStressTestCmd(), NewInitCmd(), diff --git a/cmd/zetae2e/run.go b/cmd/zetae2e/run.go index 9b1945d88e..6f98e78da6 100644 --- a/cmd/zetae2e/run.go +++ b/cmd/zetae2e/run.go @@ -52,7 +52,7 @@ func runE2ETest(cmd *cobra.Command, args []string) error { } // initialize logger - logger := runner.NewLogger(verbose, color.FgWhite, "e2e") + logger := runner.NewLogger(verbose, color.FgHiCyan, "e2e") // set config app.SetConfig() @@ -62,7 +62,7 @@ func runE2ETest(cmd *cobra.Command, args []string) error { // get EVM address from config evmAddr := conf.Accounts.EVMAddress - if ethcommon.IsHexAddress(evmAddr) { + if !ethcommon.IsHexAddress(evmAddr) { cancel() return errors.New("invalid EVM address") } @@ -75,8 +75,8 @@ func runE2ETest(cmd *cobra.Command, args []string) error { conf, ethcommon.HexToAddress(evmAddr), conf.Accounts.EVMPrivKey, - utils.FungibleAdminName, - FungibleAdminMnemonic, + utils.FungibleAdminName, // placeholder value, not used + FungibleAdminMnemonic, // placeholder value, not used logger, ) if err != nil { @@ -88,18 +88,42 @@ func runE2ETest(cmd *cobra.Command, args []string) error { logger.Print("starting tests") // fetch the TSS address - testRunner.SetTSSAddresses() + if err := testRunner.SetTSSAddresses(); err != nil { + return err + } + + // set timeout + testRunner.CctxTimeout = 60 * time.Minute + testRunner.ReceiptTimeout = 60 * time.Minute + + balancesBefore, err := testRunner.GetAccountBalances(true) + if err != nil { + cancel() + return err + } - // run tests - if err := testRunner.RunSmokeTestsFromNames( + //run tests + reports, err := testRunner.RunSmokeTestsFromNamesIntoReport( smoketests.AllSmokeTests, conf.TestList..., - ); err != nil { + ) + if err != nil { + cancel() + return err + } + + balancesAfter, err := testRunner.GetAccountBalances(true) + if err != nil { cancel() return err } + // Print tests completion info logger.Print("tests finished successfully in %s", time.Since(testStartTime).String()) + testRunner.Logger.SetColor(color.FgHiRed) + testRunner.PrintTotalDiff(runner.GetAccountBalancesDiff(balancesBefore, balancesAfter)) + testRunner.Logger.SetColor(color.FgHiGreen) + testRunner.PrintTestReports(reports) return nil } diff --git a/cmd/zetae2e/show_tss.go b/cmd/zetae2e/show_tss.go new file mode 100644 index 0000000000..84938396e3 --- /dev/null +++ b/cmd/zetae2e/show_tss.go @@ -0,0 +1,79 @@ +package main + +import ( + "context" + "errors" + + ethcommon "github.com/ethereum/go-ethereum/common" + "github.com/fatih/color" + "github.com/spf13/cobra" + "github.com/zeta-chain/zetacore/app" + zetae2econfig "github.com/zeta-chain/zetacore/cmd/zetae2e/config" + "github.com/zeta-chain/zetacore/contrib/localnet/orchestrator/smoketest/config" + "github.com/zeta-chain/zetacore/contrib/localnet/orchestrator/smoketest/runner" + "github.com/zeta-chain/zetacore/contrib/localnet/orchestrator/smoketest/utils" +) + +// NewShowTSSCmd returns the show TSS command +// which shows the TSS address in the network +func NewShowTSSCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "show-tss [config]", + Short: "Show address of the TSS", + RunE: runShowTSS, + Args: cobra.ExactArgs(1), + } + return cmd +} + +func runShowTSS(_ *cobra.Command, args []string) error { + // read the config file + conf, err := config.ReadConfig(args[0]) + if err != nil { + return err + } + + // initialize logger + logger := runner.NewLogger(true, color.FgHiCyan, "") + + // set config + app.SetConfig() + + // initialize context + ctx, cancel := context.WithCancel(context.Background()) + + // get EVM address from config + evmAddr := conf.Accounts.EVMAddress + if !ethcommon.IsHexAddress(evmAddr) { + cancel() + return errors.New("invalid EVM address") + } + + // initialize deployer runner with config + testRunner, err := zetae2econfig.RunnerFromConfig( + ctx, + "tss", + cancel, + conf, + ethcommon.HexToAddress(evmAddr), + conf.Accounts.EVMPrivKey, + utils.FungibleAdminName, // placeholder value, not used + FungibleAdminMnemonic, // placeholder value, not used + logger, + ) + if err != nil { + cancel() + return err + } + + // fetch the TSS address + if err := testRunner.SetTSSAddresses(); err != nil { + return err + } + + // print the TSS address + logger.Info("TSS EVM address: %s\n", testRunner.TSSAddress.Hex()) + logger.Info("TSS BTC address: %s\n", testRunner.BTCTSSAddress.EncodeAddress()) + + return nil +} diff --git a/cmd/zetae2e/stress.go b/cmd/zetae2e/stress.go index 26a2169b95..1ef3dc6410 100644 --- a/cmd/zetae2e/stress.go +++ b/cmd/zetae2e/stress.go @@ -152,7 +152,9 @@ func StressTest(cmd *cobra.Command, _ []string) { } // setup TSS addresses - smokeTest.SetTSSAddresses() + if err := smokeTest.SetTSSAddresses(); err != nil { + panic(err) + } smokeTest.SetupEVM(stressTestArgs.contractsDeployed) diff --git a/common/chain.go b/common/chain.go index cc3dde3722..303e3a238e 100644 --- a/common/chain.go +++ b/common/chain.go @@ -199,6 +199,19 @@ func GetBTCChainParams(chainID int64) (*chaincfg.Params, error) { } } +func GetBTCChainIDFromChainParams(params *chaincfg.Params) (int64, error) { + switch params.Name { + case chaincfg.RegressionNetParams.Name: + return 18444, nil + case chaincfg.TestNet3Params.Name: + return 18332, nil + case chaincfg.MainNetParams.Name: + return 8332, nil + default: + return 0, fmt.Errorf("error chain %s is not a Bitcoin chain", params.Name) + } +} + // InChainList checks whether the chain is in the chain list func (chain Chain) InChainList(chainList []*Chain) bool { return ChainIDInChainList(chain.ChainId, chainList) diff --git a/contrib/localnet/orchestrator/smoketest/cmd/smoketest/local/accounts.go b/contrib/localnet/orchestrator/smoketest/cmd/smoketest/local/accounts.go deleted file mode 100644 index f179e02859..0000000000 --- a/contrib/localnet/orchestrator/smoketest/cmd/smoketest/local/accounts.go +++ /dev/null @@ -1,37 +0,0 @@ -package local - -import ( - ethcommon "github.com/ethereum/go-ethereum/common" -) - -var ( - // DeployerAddress is the address of the account for deploying networks - DeployerAddress = ethcommon.HexToAddress("0xE5C5367B8224807Ac2207d350E60e1b6F27a7ecC") - DeployerPrivateKey = "d87baf7bf6dc560a252596678c12e41f7d1682837f05b29d411bc3f78ae2c263" // #nosec G101 - used for testing - - // UserERC20Address is the address of the account for testing ERC20 - UserERC20Address = ethcommon.HexToAddress("0x6F57D5E7c6DBb75e59F1524a3dE38Fc389ec5Fd6") - UserERC20PrivateKey = "fda3be1b1517bdf48615bdadacc1e6463d2865868dc8077d2cdcfa4709a16894" // #nosec G101 - used for testing - - // UserZetaTestAddress is the address of the account for testing Zeta - UserZetaTestAddress = ethcommon.HexToAddress("0x5cC2fBb200A929B372e3016F1925DcF988E081fd") - UserZetaTestPrivateKey = "729a6cdc5c925242e7df92fdeeb94dadbf2d0b9950d4db8f034ab27a3b114ba7" // #nosec G101 - used for testing - - // UserBitcoinAddress is the address of the account for testing Bitcoin - UserBitcoinAddress = ethcommon.HexToAddress("0x283d810090EdF4043E75247eAeBcE848806237fD") - UserBitcoinPrivateKey = "7bb523963ee2c78570fb6113d886a4184d42565e8847f1cb639f5f5e2ef5b37a" // #nosec G101 - used for testing - - // UserEtherAddress is the address of the account for testing Ether - UserEtherAddress = ethcommon.HexToAddress("0x8D47Db7390AC4D3D449Cc20D799ce4748F97619A") - UserEtherPrivateKey = "098e74a1c2261fa3c1b8cfca8ef2b4ff96c73ce36710d208d1f6535aef42545d" // #nosec G101 - used for testing - - // UserMiscAddress is the address of the account for miscellaneous tests - UserMiscAddress = ethcommon.HexToAddress("0x90126d02E41c9eB2a10cfc43aAb3BD3460523Cdf") - UserMiscPrivateKey = "853c0945b8035a501b1161df65a17a0a20fc848bda8975a8b4e9222cc6f84cd4" // #nosec G101 - used for testing - - // UserAdminAddress is the address of the account for testing admin function features - UserAdminAddress = ethcommon.HexToAddress("0xcC8487562AAc220ea4406196Ee902C7c076966af") - UserAdminPrivateKey = "95409f1f0e974871cc26ba98ffd31f613aa1287d40c0aea6a87475fc3521d083" // #nosec G101 - used for testing - - FungibleAdminMnemonic = "snow grace federal cupboard arrive fancy gym lady uniform rotate exercise either leave alien grass" // #nosec G101 - used for testing -) diff --git a/contrib/localnet/orchestrator/smoketest/cmd/smoketest/local/admin.go b/contrib/localnet/orchestrator/smoketest/cmd/smoketest/local/admin.go deleted file mode 100644 index bafc639033..0000000000 --- a/contrib/localnet/orchestrator/smoketest/cmd/smoketest/local/admin.go +++ /dev/null @@ -1,76 +0,0 @@ -package local - -import ( - "fmt" - "runtime" - "time" - - "github.com/fatih/color" - "github.com/zeta-chain/zetacore/contrib/localnet/orchestrator/smoketest/config" - "github.com/zeta-chain/zetacore/contrib/localnet/orchestrator/smoketest/runner" - "github.com/zeta-chain/zetacore/contrib/localnet/orchestrator/smoketest/smoketests" -) - -// adminTestRoutine runs admin functions smoke tests -func adminTestRoutine( - conf config.Config, - deployerRunner *runner.SmokeTestRunner, - verbose bool, -) func() error { - return func() (err error) { - // return an error on panic - // https://github.com/zeta-chain/node/issues/1500 - defer func() { - if r := recover(); r != nil { - // print stack trace - stack := make([]byte, 4096) - n := runtime.Stack(stack, false) - err = fmt.Errorf("admin panic: %v, stack trace %s", r, stack[:n]) - } - }() - - // initialize runner for erc20 advanced test - adminRunner, err := initTestRunner( - "admin", - conf, - deployerRunner, - UserAdminAddress, - UserAdminPrivateKey, - runner.NewLogger(verbose, color.FgGreen, "admin"), - ) - if err != nil { - return err - } - - adminRunner.Logger.Print("🏃 starting admin tests") - startTime := time.Now() - - // funding the account - txZetaSend := deployerRunner.SendZetaOnEvm(UserAdminAddress, 1000) - txUSDTSend := deployerRunner.SendUSDTOnEvm(UserAdminAddress, 1000) - adminRunner.WaitForTxReceiptOnEvm(txZetaSend) - adminRunner.WaitForTxReceiptOnEvm(txUSDTSend) - - // depositing the necessary tokens on ZetaChain - txZetaDeposit := adminRunner.DepositZeta() - txEtherDeposit := adminRunner.DepositEther(false) - txERC20Deposit := adminRunner.DepositERC20() - adminRunner.WaitForMinedCCTX(txZetaDeposit) - adminRunner.WaitForMinedCCTX(txEtherDeposit) - adminRunner.WaitForMinedCCTX(txERC20Deposit) - - // run erc20 advanced test - if err := adminRunner.RunSmokeTestsFromNames( - smoketests.AllSmokeTests, - smoketests.TestPauseZRC20Name, - smoketests.TestUpdateBytecodeName, - smoketests.TestDepositEtherLiquidityCapName, - ); err != nil { - return fmt.Errorf("admim tests failed: %v", err) - } - - adminRunner.Logger.Print("🍾 admin tests completed in %s", time.Since(startTime).String()) - - return err - } -} diff --git a/contrib/localnet/orchestrator/smoketest/cmd/smoketest/local/bitcoin.go b/contrib/localnet/orchestrator/smoketest/cmd/smoketest/local/bitcoin.go deleted file mode 100644 index f791e1c29b..0000000000 --- a/contrib/localnet/orchestrator/smoketest/cmd/smoketest/local/bitcoin.go +++ /dev/null @@ -1,82 +0,0 @@ -package local - -import ( - "fmt" - "runtime" - "time" - - "github.com/fatih/color" - "github.com/zeta-chain/zetacore/contrib/localnet/orchestrator/smoketest/config" - "github.com/zeta-chain/zetacore/contrib/localnet/orchestrator/smoketest/runner" - "github.com/zeta-chain/zetacore/contrib/localnet/orchestrator/smoketest/smoketests" -) - -// bitcoinTestRoutine runs Bitcoin related smoke tests -func bitcoinTestRoutine( - conf config.Config, - deployerRunner *runner.SmokeTestRunner, - verbose bool, -) func() error { - return func() (err error) { - // return an error on panic - // TODO: remove and instead return errors in the smoke tests - // https://github.com/zeta-chain/node/issues/1500 - defer func() { - if r := recover(); r != nil { - // print stack trace - stack := make([]byte, 4096) - n := runtime.Stack(stack, false) - err = fmt.Errorf("bitcoin panic: %v, stack trace %s", r, stack[:n]) - } - }() - - // initialize runner for bitcoin test - bitcoinRunner, err := initTestRunner( - "bitcoin", - conf, - deployerRunner, - UserBitcoinAddress, - UserBitcoinPrivateKey, - runner.NewLogger(verbose, color.FgYellow, "bitcoin"), - ) - if err != nil { - return err - } - - bitcoinRunner.Logger.Print("🏃 starting Bitcoin tests") - startTime := time.Now() - - // funding the account - txZetaSend := deployerRunner.SendZetaOnEvm(UserBitcoinAddress, 1000) - txUSDTSend := deployerRunner.SendUSDTOnEvm(UserBitcoinAddress, 1000) - - bitcoinRunner.WaitForTxReceiptOnEvm(txZetaSend) - bitcoinRunner.WaitForTxReceiptOnEvm(txUSDTSend) - - // depositing the necessary tokens on ZetaChain - txZetaDeposit := bitcoinRunner.DepositZeta() - txEtherDeposit := bitcoinRunner.DepositEther(false) - txERC20Deposit := bitcoinRunner.DepositERC20() - bitcoinRunner.SetupBitcoinAccount(true) - bitcoinRunner.DepositBTC(true) - bitcoinRunner.WaitForMinedCCTX(txZetaDeposit) - bitcoinRunner.WaitForMinedCCTX(txEtherDeposit) - bitcoinRunner.WaitForMinedCCTX(txERC20Deposit) - - // run bitcoin test - // Note: due to the extensive block generation in Bitcoin localnet, block header test is run first - // to make it faster to catch up with the latest block header - if err := bitcoinRunner.RunSmokeTestsFromNames( - smoketests.AllSmokeTests, - smoketests.TestBitcoinWithdrawName, - smoketests.TestSendZetaOutBTCRevertName, - smoketests.TestCrosschainSwapName, - ); err != nil { - return fmt.Errorf("bitcoin tests failed: %v", err) - } - - bitcoinRunner.Logger.Print("🍾 Bitcoin tests completed in %s", time.Since(startTime).String()) - - return err - } -} diff --git a/contrib/localnet/orchestrator/smoketest/cmd/smoketest/local/erc20.go b/contrib/localnet/orchestrator/smoketest/cmd/smoketest/local/erc20.go deleted file mode 100644 index 18449301ef..0000000000 --- a/contrib/localnet/orchestrator/smoketest/cmd/smoketest/local/erc20.go +++ /dev/null @@ -1,83 +0,0 @@ -package local - -import ( - "fmt" - "runtime" - "time" - - "github.com/fatih/color" - "github.com/zeta-chain/zetacore/contrib/localnet/orchestrator/smoketest/config" - "github.com/zeta-chain/zetacore/contrib/localnet/orchestrator/smoketest/runner" - "github.com/zeta-chain/zetacore/contrib/localnet/orchestrator/smoketest/smoketests" -) - -// erc20TestRoutine runs erc20 related smoke tests -func erc20TestRoutine( - conf config.Config, - deployerRunner *runner.SmokeTestRunner, - verbose bool, -) func() error { - return func() (err error) { - // return an error on panic - // TODO: remove and instead return errors in the smoke tests - // https://github.com/zeta-chain/node/issues/1500 - defer func() { - if r := recover(); r != nil { - // print stack trace - stack := make([]byte, 4096) - n := runtime.Stack(stack, false) - err = fmt.Errorf("erc20 panic: %v, stack trace %s", r, stack[:n]) - } - }() - - // initialize runner for erc20 test - erc20Runner, err := initTestRunner( - "erc20", - conf, - deployerRunner, - UserERC20Address, - UserERC20PrivateKey, - runner.NewLogger(verbose, color.FgGreen, "erc20"), - ) - if err != nil { - return err - } - - erc20Runner.Logger.Print("🏃 starting erc20 tests") - startTime := time.Now() - - // funding the account - txZetaSend := deployerRunner.SendZetaOnEvm(UserERC20Address, 1000) - txUSDTSend := deployerRunner.SendUSDTOnEvm(UserERC20Address, 10) - - erc20Runner.WaitForTxReceiptOnEvm(txZetaSend) - erc20Runner.WaitForTxReceiptOnEvm(txUSDTSend) - - // depositing the necessary tokens on ZetaChain - txZetaDeposit := erc20Runner.DepositZeta() - txEtherDeposit := erc20Runner.DepositEther(false) - txERC20Deposit := erc20Runner.DepositERC20() - erc20Runner.WaitForMinedCCTX(txZetaDeposit) - erc20Runner.WaitForMinedCCTX(txEtherDeposit) - erc20Runner.WaitForMinedCCTX(txERC20Deposit) - - //erc20Runner.SetupBitcoinAccount() - //erc20Runner.DepositBTC() - - // run erc20 test - if err := erc20Runner.RunSmokeTestsFromNames( - smoketests.AllSmokeTests, - smoketests.TestMultipleERC20DepositName, - smoketests.TestWithdrawERC20Name, - smoketests.TestMultipleWithdrawsName, - smoketests.TestERC20DepositAndCallRefundName, - smoketests.TestZRC20SwapName, - ); err != nil { - return fmt.Errorf("erc20 tests failed: %v", err) - } - - erc20Runner.Logger.Print("🍾 erc20 tests completed in %s", time.Since(startTime).String()) - - return err - } -} diff --git a/contrib/localnet/orchestrator/smoketest/cmd/smoketest/local/ethereum.go b/contrib/localnet/orchestrator/smoketest/cmd/smoketest/local/ethereum.go deleted file mode 100644 index 7f2eae6792..0000000000 --- a/contrib/localnet/orchestrator/smoketest/cmd/smoketest/local/ethereum.go +++ /dev/null @@ -1,75 +0,0 @@ -package local - -import ( - "fmt" - "runtime" - "time" - - "github.com/fatih/color" - "github.com/zeta-chain/zetacore/contrib/localnet/orchestrator/smoketest/config" - "github.com/zeta-chain/zetacore/contrib/localnet/orchestrator/smoketest/runner" - "github.com/zeta-chain/zetacore/contrib/localnet/orchestrator/smoketest/smoketests" -) - -// ethereumTestRoutine runs Ethereum related smoke tests -func ethereumTestRoutine( - conf config.Config, - deployerRunner *runner.SmokeTestRunner, - verbose bool, -) func() error { - return func() (err error) { - // return an error on panic - // TODO: remove and instead return errors in the smoke tests - // https://github.com/zeta-chain/node/issues/1500 - defer func() { - if r := recover(); r != nil { - // print stack trace - stack := make([]byte, 4096) - n := runtime.Stack(stack, false) - err = fmt.Errorf("ethereum panic: %v, stack trace %s", r, stack[:n]) - } - }() - - // initialize runner for ether test - ethereumRunner, err := initTestRunner( - "ether", - conf, - deployerRunner, - UserEtherAddress, - UserEtherPrivateKey, - runner.NewLogger(verbose, color.FgMagenta, "ether"), - ) - if err != nil { - return err - } - - ethereumRunner.Logger.Print("🏃 starting Ethereum tests") - startTime := time.Now() - - // funding the account - txZetaSend := deployerRunner.SendZetaOnEvm(UserEtherAddress, 1000) - ethereumRunner.WaitForTxReceiptOnEvm(txZetaSend) - - // depositing the necessary tokens on ZetaChain - txZetaDeposit := ethereumRunner.DepositZeta() - txEtherDeposit := ethereumRunner.DepositEther(true) - ethereumRunner.WaitForMinedCCTX(txZetaDeposit) - ethereumRunner.WaitForMinedCCTX(txEtherDeposit) - - // run ethereum test - // Note: due to the extensive block generation in Ethereum localnet, block header test is run first - // to make it faster to catch up with the latest block header - if err := ethereumRunner.RunSmokeTestsFromNames( - smoketests.AllSmokeTests, - smoketests.TestContextUpgradeName, - smoketests.TestEtherDepositAndCallName, - smoketests.TestDepositAndCallRefundName, - ); err != nil { - return fmt.Errorf("ethereum tests failed: %v", err) - } - - ethereumRunner.Logger.Print("🍾 Ethereum tests completed in %s", time.Since(startTime).String()) - - return err - } -} diff --git a/contrib/localnet/orchestrator/smoketest/cmd/smoketest/local/local.go b/contrib/localnet/orchestrator/smoketest/cmd/smoketest/local/local.go deleted file mode 100644 index 170e8a644c..0000000000 --- a/contrib/localnet/orchestrator/smoketest/cmd/smoketest/local/local.go +++ /dev/null @@ -1,184 +0,0 @@ -package local - -import ( - "context" - "os" - "time" - - "github.com/fatih/color" - "github.com/spf13/cobra" - "github.com/zeta-chain/zetacore/contrib/localnet/orchestrator/smoketest/runner" - "github.com/zeta-chain/zetacore/contrib/localnet/orchestrator/smoketest/utils" - "golang.org/x/sync/errgroup" -) - -const ( - flagContractsDeployed = "deployed" - flagWaitForHeight = "wait-for" - flagConfigFile = "config" - flagVerbose = "verbose" - flagTestAdmin = "test-admin" - flagTestCustom = "test-custom" - flagSkipRegular = "skip-regular" -) - -var ( - SmokeTestTimeout = 30 * time.Minute -) - -// NewLocalCmd returns the local command -// which runs the smoketest locally on the machine with localnet for each blockchain -func NewLocalCmd() *cobra.Command { - cmd := &cobra.Command{ - Use: "local", - Short: "Run Local Smoketest", - Run: localSmokeTest, - } - cmd.Flags().Bool( - flagContractsDeployed, - false, - "set to to true if running smoketest again with existing state", - ) - cmd.Flags().Int64( - flagWaitForHeight, - 0, - "block height for smoketest to begin, ex. --wait-for 100", - ) - cmd.Flags().String( - flagConfigFile, - "", - "config file to use for the smoketest", - ) - cmd.Flags().Bool( - flagVerbose, - false, - "set to true to enable verbose logging", - ) - cmd.Flags().Bool( - flagTestAdmin, - false, - "set to true to run admin tests", - ) - cmd.Flags().Bool( - flagTestCustom, - false, - "set to true to run custom tests", - ) - cmd.Flags().Bool( - flagSkipRegular, - false, - "set to true to skip regular tests", - ) - - return cmd -} - -func localSmokeTest(cmd *cobra.Command, _ []string) { - // fetch flags - waitForHeight, err := cmd.Flags().GetInt64(flagWaitForHeight) - if err != nil { - panic(err) - } - contractsDeployed, err := cmd.Flags().GetBool(flagContractsDeployed) - if err != nil { - panic(err) - } - verbose, err := cmd.Flags().GetBool(flagVerbose) - if err != nil { - panic(err) - } - logger := runner.NewLogger(verbose, color.FgWhite, "setup") - testAdmin, err := cmd.Flags().GetBool(flagTestAdmin) - if err != nil { - panic(err) - } - testCustom, err := cmd.Flags().GetBool(flagTestCustom) - if err != nil { - panic(err) - } - skipRegular, err := cmd.Flags().GetBool(flagSkipRegular) - if err != nil { - panic(err) - } - - testStartTime := time.Now() - logger.Print("starting smoke tests") - - // start timer - go func() { - time.Sleep(SmokeTestTimeout) - logger.Error("Smoke test timed out after %s", SmokeTestTimeout.String()) - os.Exit(1) - }() - - // initialize smoke tests config - conf, err := GetConfig(cmd) - if err != nil { - panic(err) - } - - // initialize context - ctx, cancel := context.WithCancel(context.Background()) - - // wait for a specific height on ZetaChain - if waitForHeight != 0 { - utils.WaitForBlockHeight(ctx, waitForHeight, conf.RPCs.ZetaCoreRPC, logger) - } - - // set account prefix to zeta - setCosmosConfig() - - // wait for Genesis - logger.Print("⏳ wait 70s for genesis") - time.Sleep(70 * time.Second) - - // initialize deployer runner with config - deployerRunner, err := runnerFromConfig( - ctx, - "deployer", - cancel, - conf, - DeployerAddress, - DeployerPrivateKey, - logger, - ) - if err != nil { - panic(err) - } - - // wait for keygen to be completed - waitKeygenHeight(ctx, deployerRunner.CctxClient, logger) - - // setting up the networks - logger.Print("⚙️ setting up networks") - startTime := time.Now() - deployerRunner.SetTSSAddresses() - deployerRunner.SetupEVM(contractsDeployed) - deployerRunner.SetZEVMContracts() - deployerRunner.MintUSDTOnEvm(10000) - logger.Print("✅ setup completed in %s", time.Since(startTime)) - - // run tests - var eg errgroup.Group - if !skipRegular { - eg.Go(erc20TestRoutine(conf, deployerRunner, verbose)) - eg.Go(zetaTestRoutine(conf, deployerRunner, verbose)) - eg.Go(bitcoinTestRoutine(conf, deployerRunner, verbose)) - eg.Go(ethereumTestRoutine(conf, deployerRunner, verbose)) - } - if testAdmin { - eg.Go(adminTestRoutine(conf, deployerRunner, verbose)) - } - if testCustom { - eg.Go(miscTestRoutine(conf, deployerRunner, verbose)) - } - - if err := eg.Wait(); err != nil { - deployerRunner.CtxCancel() - logger.Print("❌ %v", err) - logger.Print("❌ smoke tests failed after %s", time.Since(testStartTime).String()) - os.Exit(1) - } - - logger.Print("✅ smoke tests completed in %s", time.Since(testStartTime).String()) -} diff --git a/contrib/localnet/orchestrator/smoketest/cmd/smoketest/local/misc.go b/contrib/localnet/orchestrator/smoketest/cmd/smoketest/local/misc.go deleted file mode 100644 index a8f316c7a2..0000000000 --- a/contrib/localnet/orchestrator/smoketest/cmd/smoketest/local/misc.go +++ /dev/null @@ -1,70 +0,0 @@ -package local - -import ( - "fmt" - "runtime" - "time" - - "github.com/fatih/color" - "github.com/zeta-chain/zetacore/contrib/localnet/orchestrator/smoketest/config" - "github.com/zeta-chain/zetacore/contrib/localnet/orchestrator/smoketest/runner" - "github.com/zeta-chain/zetacore/contrib/localnet/orchestrator/smoketest/smoketests" -) - -// miscTestRoutine runs miscellaneous smoke tests -func miscTestRoutine( - conf config.Config, - deployerRunner *runner.SmokeTestRunner, - verbose bool, -) func() error { - return func() (err error) { - // return an error on panic - // TODO: remove and instead return errors in the smoke tests - // https://github.com/zeta-chain/node/issues/1500 - defer func() { - if r := recover(); r != nil { - // print stack trace - stack := make([]byte, 4096) - n := runtime.Stack(stack, false) - err = fmt.Errorf("misc panic: %v, stack trace %s", r, stack[:n]) - } - }() - - // initialize runner for misc test - miscRunner, err := initTestRunner( - "misc", - conf, - deployerRunner, - UserMiscAddress, - UserMiscPrivateKey, - runner.NewLogger(verbose, color.FgCyan, "misc"), - ) - if err != nil { - return err - } - - miscRunner.Logger.Print("🏃 starting miscellaneous tests") - startTime := time.Now() - - // funding the account - txZetaSend := deployerRunner.SendZetaOnEvm(UserMiscAddress, 1000) - miscRunner.WaitForTxReceiptOnEvm(txZetaSend) - - // depositing the necessary tokens on ZetaChain - txZetaDeposit := miscRunner.DepositZeta() - miscRunner.WaitForMinedCCTX(txZetaDeposit) - - // run misc test - if err := miscRunner.RunSmokeTestsFromNames( - smoketests.AllSmokeTests, - //smoketests.TestBlockHeadersName, - smoketests.TestMyTestName, - ); err != nil { - return fmt.Errorf("misc tests failed: %v", err) - } - - miscRunner.Logger.Print("🍾 miscellaneous tests completed in %s", time.Since(startTime).String()) - - return err - } -} diff --git a/contrib/localnet/orchestrator/smoketest/cmd/smoketest/local/utils.go b/contrib/localnet/orchestrator/smoketest/cmd/smoketest/local/utils.go deleted file mode 100644 index 3cc27582dc..0000000000 --- a/contrib/localnet/orchestrator/smoketest/cmd/smoketest/local/utils.go +++ /dev/null @@ -1,264 +0,0 @@ -package local - -import ( - "context" - "time" - - "github.com/btcsuite/btcd/rpcclient" - sdk "github.com/cosmos/cosmos-sdk/types" - authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" - banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" - "github.com/ethereum/go-ethereum/accounts/abi/bind" - ethcommon "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/crypto" - "github.com/ethereum/go-ethereum/ethclient" - "github.com/spf13/cobra" - "github.com/zeta-chain/zetacore/app" - "github.com/zeta-chain/zetacore/contrib/localnet/orchestrator/smoketest/config" - "github.com/zeta-chain/zetacore/contrib/localnet/orchestrator/smoketest/runner" - "github.com/zeta-chain/zetacore/contrib/localnet/orchestrator/smoketest/txserver" - "github.com/zeta-chain/zetacore/contrib/localnet/orchestrator/smoketest/utils" - crosschaintypes "github.com/zeta-chain/zetacore/x/crosschain/types" - fungibletypes "github.com/zeta-chain/zetacore/x/fungible/types" - observertypes "github.com/zeta-chain/zetacore/x/observer/types" - "google.golang.org/grpc" -) - -// GetConfig returns config from file from the command line flag -func GetConfig(cmd *cobra.Command) (config.Config, error) { - configFile, err := cmd.Flags().GetString(flagConfigFile) - if err != nil { - return config.Config{}, err - } - - // use default config if no config file is specified - if configFile == "" { - return config.DefaultConfig(), nil - } - - return config.ReadConfig(configFile) -} - -// setCosmosConfig set account prefix to zeta -func setCosmosConfig() { - cosmosConf := sdk.GetConfig() - cosmosConf.SetBech32PrefixForAccount(app.Bech32PrefixAccAddr, app.Bech32PrefixAccPub) - cosmosConf.Seal() -} - -// initTestRunner initializes a runner for smoke tests -// it creates a runner with an account and copy contracts from deployer runner -func initTestRunner( - name string, - conf config.Config, - deployerRunner *runner.SmokeTestRunner, - userAddress ethcommon.Address, - userPrivKey string, - logger *runner.Logger, -) (*runner.SmokeTestRunner, error) { - // initialize runner for smoke test - testRunner, err := runnerFromConfig( - deployerRunner.Ctx, - name, - deployerRunner.CtxCancel, - conf, - userAddress, - userPrivKey, - logger, - ) - if err != nil { - return nil, err - } - - // copy contracts from deployer runner - if err := testRunner.CopyAddressesFrom(deployerRunner); err != nil { - return nil, err - } - - return testRunner, nil -} - -// runnerFromConfig create test runner from config -func runnerFromConfig( - ctx context.Context, - name string, - ctxCancel context.CancelFunc, - conf config.Config, - userAddr ethcommon.Address, - userPrivKey string, - logger *runner.Logger, -) (*runner.SmokeTestRunner, error) { - // initialize clients - btcRPCClient, - goerliClient, - goerliAuth, - cctxClient, - fungibleClient, - authClient, - bankClient, - observerClient, - zevmClient, - zevmAuth, - err := getClientsFromConfig(ctx, conf, userPrivKey) - if err != nil { - return nil, err - } - // initialize client to send messages to ZetaChain - zetaTxServer, err := txserver.NewZetaTxServer( - conf.RPCs.ZetaCoreRPC, - []string{utils.FungibleAdminName}, - []string{FungibleAdminMnemonic}, - conf.ZetaChainID, - ) - if err != nil { - return nil, err - } - - // initialize smoke test runner - sm := runner.NewSmokeTestRunner( - ctx, - name, - ctxCancel, - userAddr, - userPrivKey, - FungibleAdminMnemonic, - goerliClient, - zevmClient, - cctxClient, - zetaTxServer, - fungibleClient, - authClient, - bankClient, - observerClient, - goerliAuth, - zevmAuth, - btcRPCClient, - logger, - ) - return sm, nil -} - -// getClientsFromConfig get clients from config -func getClientsFromConfig(ctx context.Context, conf config.Config, evmPrivKey string) ( - *rpcclient.Client, - *ethclient.Client, - *bind.TransactOpts, - crosschaintypes.QueryClient, - fungibletypes.QueryClient, - authtypes.QueryClient, - banktypes.QueryClient, - observertypes.QueryClient, - *ethclient.Client, - *bind.TransactOpts, - error, -) { - btcRPCClient, err := getBtcClient(conf.RPCs.Bitcoin) - if err != nil { - return nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, err - } - goerliClient, goerliAuth, err := getEVMClient(ctx, conf.RPCs.EVM, evmPrivKey) - if err != nil { - return nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, err - } - cctxClient, fungibleClient, authClient, bankClient, observerClient, err := getZetaClients(conf.RPCs.ZetaCoreGRPC) - if err != nil { - return nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, err - } - zevmClient, zevmAuth, err := getEVMClient(ctx, conf.RPCs.Zevm, evmPrivKey) - if err != nil { - return nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, err - } - return btcRPCClient, - goerliClient, - goerliAuth, - cctxClient, - fungibleClient, - authClient, - bankClient, - observerClient, - zevmClient, - zevmAuth, - nil -} - -// getBtcClient get btc client -func getBtcClient(rpc string) (*rpcclient.Client, error) { - connCfg := &rpcclient.ConnConfig{ - Host: rpc, - User: "smoketest", - Pass: "123", - HTTPPostMode: true, - DisableTLS: true, - Params: "testnet3", - } - return rpcclient.New(connCfg, nil) -} - -// getEVMClient get goerli client -func getEVMClient(ctx context.Context, rpc, privKey string) (*ethclient.Client, *bind.TransactOpts, error) { - evmClient, err := ethclient.Dial(rpc) - if err != nil { - return nil, nil, err - } - - chainid, err := evmClient.ChainID(ctx) - if err != nil { - return nil, nil, err - } - deployerPrivkey, err := crypto.HexToECDSA(privKey) - if err != nil { - return nil, nil, err - } - evmAuth, err := bind.NewKeyedTransactorWithChainID(deployerPrivkey, chainid) - if err != nil { - return nil, nil, err - } - - return evmClient, evmAuth, nil -} - -// getZetaClients get zeta clients -func getZetaClients(rpc string) ( - crosschaintypes.QueryClient, - fungibletypes.QueryClient, - authtypes.QueryClient, - banktypes.QueryClient, - observertypes.QueryClient, - error, -) { - grpcConn, err := grpc.Dial(rpc, grpc.WithInsecure()) - if err != nil { - return nil, nil, nil, nil, nil, err - } - - cctxClient := crosschaintypes.NewQueryClient(grpcConn) - fungibleClient := fungibletypes.NewQueryClient(grpcConn) - authClient := authtypes.NewQueryClient(grpcConn) - bankClient := banktypes.NewQueryClient(grpcConn) - observerClient := observertypes.NewQueryClient(grpcConn) - - return cctxClient, fungibleClient, authClient, bankClient, observerClient, nil -} - -// waitKeygenHeight waits for keygen height -func waitKeygenHeight( - ctx context.Context, - cctxClient crosschaintypes.QueryClient, - logger *runner.Logger, -) { - // wait for keygen to be completed. ~ height 30 - keygenHeight := int64(60) - logger.Print("⏳ wait height %v for keygen to be completed", keygenHeight) - for { - time.Sleep(2 * time.Second) - response, err := cctxClient.LastZetaHeight(ctx, &crosschaintypes.QueryLastZetaHeightRequest{}) - if err != nil { - logger.Error("cctxClient.LastZetaHeight error: %s", err) - continue - } - if response.Height >= keygenHeight { - break - } - logger.Info("Last ZetaHeight: %d", response.Height) - } -} diff --git a/contrib/localnet/orchestrator/smoketest/cmd/smoketest/local/zeta.go b/contrib/localnet/orchestrator/smoketest/cmd/smoketest/local/zeta.go deleted file mode 100644 index 6cefb8c615..0000000000 --- a/contrib/localnet/orchestrator/smoketest/cmd/smoketest/local/zeta.go +++ /dev/null @@ -1,74 +0,0 @@ -package local - -import ( - "fmt" - "runtime" - "time" - - "github.com/fatih/color" - "github.com/zeta-chain/zetacore/contrib/localnet/orchestrator/smoketest/config" - "github.com/zeta-chain/zetacore/contrib/localnet/orchestrator/smoketest/runner" - "github.com/zeta-chain/zetacore/contrib/localnet/orchestrator/smoketest/smoketests" -) - -// zetaTestRoutine runs Zeta transfer and message passing related smoke tests -func zetaTestRoutine( - conf config.Config, - deployerRunner *runner.SmokeTestRunner, - verbose bool, -) func() error { - return func() (err error) { - // return an error on panic - // TODO: remove and instead return errors in the smoke tests - // https://github.com/zeta-chain/node/issues/1500 - defer func() { - if r := recover(); r != nil { - // print stack trace - stack := make([]byte, 4096) - n := runtime.Stack(stack, false) - err = fmt.Errorf("zeta panic: %v, stack trace %s", r, stack[:n]) - } - }() - - // initialize runner for zeta test - zetaRunner, err := initTestRunner( - "zeta", - conf, - deployerRunner, - UserZetaTestAddress, - UserZetaTestPrivateKey, - runner.NewLogger(verbose, color.FgBlue, "zeta"), - ) - if err != nil { - return err - } - - zetaRunner.Logger.Print("🏃 starting Zeta tests") - startTime := time.Now() - - // funding the account - txZetaSend := deployerRunner.SendZetaOnEvm(UserZetaTestAddress, 1000) - zetaRunner.WaitForTxReceiptOnEvm(txZetaSend) - - // depositing the necessary tokens on ZetaChain - txZetaDeposit := zetaRunner.DepositZeta() - txEtherDeposit := zetaRunner.DepositEther(false) - zetaRunner.WaitForMinedCCTX(txZetaDeposit) - zetaRunner.WaitForMinedCCTX(txEtherDeposit) - - // run zeta test - if err := zetaRunner.RunSmokeTestsFromNames( - smoketests.AllSmokeTests, - smoketests.TestSendZetaOutName, - smoketests.TestMessagePassingName, - smoketests.TestMessagePassingRevertFailName, - smoketests.TestMessagePassingRevertSuccessName, - ); err != nil { - return fmt.Errorf("zeta tests failed: %v", err) - } - - zetaRunner.Logger.Print("🍾 Zeta tests completed in %s", time.Since(startTime).String()) - - return err - } -} diff --git a/contrib/localnet/orchestrator/smoketest/cmd/smoketest/main.go b/contrib/localnet/orchestrator/smoketest/cmd/smoketest/main.go deleted file mode 100644 index 7b8038709c..0000000000 --- a/contrib/localnet/orchestrator/smoketest/cmd/smoketest/main.go +++ /dev/null @@ -1,20 +0,0 @@ -package main - -import ( - "fmt" - "os" - - "github.com/fatih/color" -) - -func main() { - // enable color output - color.NoColor = false - - // initialize root command - rootCmd := NewRootCmd() - if err := rootCmd.Execute(); err != nil { - fmt.Println(err) - os.Exit(-1) - } -} diff --git a/contrib/localnet/orchestrator/smoketest/cmd/smoketest/root.go b/contrib/localnet/orchestrator/smoketest/cmd/smoketest/root.go deleted file mode 100644 index 512f235df5..0000000000 --- a/contrib/localnet/orchestrator/smoketest/cmd/smoketest/root.go +++ /dev/null @@ -1,16 +0,0 @@ -package main - -import ( - "github.com/spf13/cobra" - "github.com/zeta-chain/zetacore/contrib/localnet/orchestrator/smoketest/cmd/smoketest/local" -) - -func NewRootCmd() *cobra.Command { - cmd := &cobra.Command{ - Use: "smoketest", - Short: "Smoke Test CLI", - } - cmd.AddCommand(local.NewLocalCmd()) - - return cmd -} diff --git a/contrib/localnet/orchestrator/smoketest/config/config.go b/contrib/localnet/orchestrator/smoketest/config/config.go index 097bc58641..442ab6df63 100644 --- a/contrib/localnet/orchestrator/smoketest/config/config.go +++ b/contrib/localnet/orchestrator/smoketest/config/config.go @@ -2,8 +2,11 @@ package config import ( "errors" + "fmt" "os" + "github.com/btcsuite/btcd/chaincfg" + "gopkg.in/yaml.v2" ) @@ -24,11 +27,21 @@ type Accounts struct { // RPCs contains the configuration for the RPC endpoints type RPCs struct { - Zevm string `yaml:"zevm"` - EVM string `yaml:"evm"` - Bitcoin string `yaml:"bitcoin"` - ZetaCoreGRPC string `yaml:"zetacore_grpc"` - ZetaCoreRPC string `yaml:"zetacore_rpc"` + Zevm string `yaml:"zevm"` + EVM string `yaml:"evm"` + Bitcoin BitcoinRPC `yaml:"bitcoin"` + ZetaCoreGRPC string `yaml:"zetacore_grpc"` + ZetaCoreRPC string `yaml:"zetacore_rpc"` +} + +// BitcoinRPC contains the configuration for the Bitcoin RPC endpoint +type BitcoinRPC struct { + User string `yaml:"user"` + Pass string `yaml:"pass"` + Host string `yaml:"host"` + HTTPPostMode bool `yaml:"http_post_mode"` + DisableTLS bool `yaml:"disable_tls"` + Params BitcoinNetworkType `yaml:"params"` } // Contracts contains the addresses of predeployed contracts @@ -53,17 +66,27 @@ type ZEVM struct { BTCZRC20Addr string `yaml:"btc_zrc20"` UniswapFactoryAddr string `yaml:"uniswap_factory"` UniswapRouterAddr string `yaml:"uniswap_router"` + ConnectorZEVMAddr string `yaml:"connector_zevm"` + WZetaAddr string `yaml:"wzeta"` ZEVMSwapAppAddr string `yaml:"zevm_swap_app"` ContextAppAddr string `yaml:"context_app"` TestDappAddr string `yaml:"test_dapp"` } +// DefaultConfig returns the default config using values for localnet testing func DefaultConfig() Config { return Config{ RPCs: RPCs{ - Zevm: "http://zetacore0:8545", - EVM: "http://eth:8545", - Bitcoin: "bitcoin:18443", + Zevm: "http://zetacore0:8545", + EVM: "http://eth:8545", + Bitcoin: BitcoinRPC{ + Host: "bitcoin:18443", + User: "smoketest", + Pass: "123", + HTTPPostMode: true, + DisableTLS: true, + Params: Regnet, + }, ZetaCoreGRPC: "zetacore0:9090", ZetaCoreRPC: "http://zetacore0:26657", }, @@ -91,6 +114,10 @@ func ReadConfig(file string) (config Config, err error) { if err != nil { return Config{}, err } + if err := config.Validate(); err != nil { + return Config{}, err + } + return } @@ -110,3 +137,37 @@ func WriteConfig(file string, config Config) error { } return nil } + +// Validate validates the config +func (c Config) Validate() error { + if c.RPCs.Bitcoin.Params != Mainnet && + c.RPCs.Bitcoin.Params != Testnet3 && + c.RPCs.Bitcoin.Params != Regnet { + return errors.New("invalid bitcoin params") + } + return nil +} + +// BitcoinNetworkType is a custom type to represent allowed network types +type BitcoinNetworkType string + +// Enum values for BitcoinNetworkType +const ( + Mainnet BitcoinNetworkType = "mainnet" + Testnet3 BitcoinNetworkType = "testnet3" + Regnet BitcoinNetworkType = "regnet" +) + +// GetParams returns the chaincfg.Params for the BitcoinNetworkType +func (bnt BitcoinNetworkType) GetParams() (chaincfg.Params, error) { + switch bnt { + case Mainnet: + return chaincfg.MainNetParams, nil + case Testnet3: + return chaincfg.TestNet3Params, nil + case Regnet: + return chaincfg.RegressionNetParams, nil + default: + return chaincfg.Params{}, fmt.Errorf("invalid bitcoin params %s", bnt) + } +} diff --git a/contrib/localnet/orchestrator/smoketest/runner/balances.go b/contrib/localnet/orchestrator/smoketest/runner/balances.go new file mode 100644 index 0000000000..eb86122dc1 --- /dev/null +++ b/contrib/localnet/orchestrator/smoketest/runner/balances.go @@ -0,0 +1,195 @@ +package runner + +import ( + "fmt" + "math/big" + "strings" + + "github.com/btcsuite/btcutil" + "github.com/ethereum/go-ethereum/accounts/abi/bind" +) + +// AccountBalances is a struct that contains the balances of the accounts used in the smoke test +type AccountBalances struct { + ZetaETH *big.Int + ZetaZETA *big.Int + ZetaWZETA *big.Int + ZetaERC20 *big.Int + ZetaBTC *big.Int + EvmETH *big.Int + EvmZETA *big.Int + EvmERC20 *big.Int + BtcBTC string +} + +// AccountBalancesDiff is a struct that contains the difference in the balances of the accounts used in the smoke test +type AccountBalancesDiff struct { + ETH *big.Int + ZETA *big.Int + ERC20 *big.Int +} + +// GetAccountBalances returns the account balances of the accounts used in the smoke test +func (sm *SmokeTestRunner) GetAccountBalances(skipBTC bool) (AccountBalances, error) { + // zevm + zetaZeta, err := sm.ZevmClient.BalanceAt(sm.Ctx, sm.DeployerAddress, nil) + if err != nil { + return AccountBalances{}, err + } + zetaWZeta, err := sm.WZeta.BalanceOf(&bind.CallOpts{}, sm.DeployerAddress) + if err != nil { + return AccountBalances{}, err + } + zetaEth, err := sm.ETHZRC20.BalanceOf(&bind.CallOpts{}, sm.DeployerAddress) + if err != nil { + return AccountBalances{}, err + } + zetaErc20, err := sm.USDTZRC20.BalanceOf(&bind.CallOpts{}, sm.DeployerAddress) + if err != nil { + return AccountBalances{}, err + } + zetaBtc, err := sm.BTCZRC20.BalanceOf(&bind.CallOpts{}, sm.DeployerAddress) + if err != nil { + return AccountBalances{}, err + } + + // evm + evmEth, err := sm.GoerliClient.BalanceAt(sm.Ctx, sm.DeployerAddress, nil) + if err != nil { + return AccountBalances{}, err + } + evmZeta, err := sm.ZetaEth.BalanceOf(&bind.CallOpts{}, sm.DeployerAddress) + if err != nil { + return AccountBalances{}, err + } + evmErc20, err := sm.USDTERC20.BalanceOf(&bind.CallOpts{}, sm.DeployerAddress) + if err != nil { + return AccountBalances{}, err + } + + // bitcoin + var BtcBTC string + if !skipBTC { + if BtcBTC, err = sm.GetBitcoinBalance(); err != nil { + return AccountBalances{}, err + } + } + + return AccountBalances{ + ZetaETH: zetaEth, + ZetaZETA: zetaZeta, + ZetaWZETA: zetaWZeta, + ZetaERC20: zetaErc20, + ZetaBTC: zetaBtc, + EvmETH: evmEth, + EvmZETA: evmZeta, + EvmERC20: evmErc20, + BtcBTC: BtcBTC, + }, nil +} + +// GetBitcoinBalance returns the spendable BTC balance of the BTC address +func (sm *SmokeTestRunner) GetBitcoinBalance() (string, error) { + addr, _, err := sm.GetBtcAddress() + if err != nil { + return "", fmt.Errorf("failed to get BTC address: %w", err) + } + + address, err := btcutil.DecodeAddress(addr, sm.BitcoinParams) + if err != nil { + return "", fmt.Errorf("failed to decode BTC address: %w", err) + } + + unspentList, err := sm.BtcRPCClient.ListUnspentMinMaxAddresses(1, 9999999, []btcutil.Address{address}) + if err != nil { + return "", fmt.Errorf("failed to list unspent: %w", err) + } + + // calculate total amount + var totalAmount btcutil.Amount + for _, unspent := range unspentList { + if unspent.Spendable { + totalAmount += btcutil.Amount(unspent.Amount * 1e8) + } + } + + return totalAmount.String(), nil +} + +// PrintAccountBalances shows the account balances of the accounts used in the smoke test +// Note: USDT is mentioned as erc20 here because we want to show the balance of any erc20 contract +func (sm *SmokeTestRunner) PrintAccountBalances(balances AccountBalances) { + sm.Logger.Print(" ---💰 Account info %s ---", sm.DeployerAddress.Hex()) + + // zevm + sm.Logger.Print("ZetaChain:") + sm.Logger.Print("* ZETA balance: %s", balances.ZetaZETA.String()) + sm.Logger.Print("* WZETA balance: %s", balances.ZetaWZETA.String()) + sm.Logger.Print("* ETH balance: %s", balances.ZetaETH.String()) + sm.Logger.Print("* ERC20 balance: %s", balances.ZetaERC20.String()) + sm.Logger.Print("* BTC balance: %s", balances.ZetaBTC.String()) + + // evm + sm.Logger.Print("EVM:") + sm.Logger.Print("* ZETA balance: %s", balances.EvmZETA.String()) + sm.Logger.Print("* ETH balance: %s", balances.EvmETH.String()) + sm.Logger.Print("* ERC20 balance: %s", balances.EvmERC20.String()) + + // bitcoin + sm.Logger.Print("Bitcoin:") + sm.Logger.Print("* BTC balance: %s", balances.BtcBTC) + + return +} + +// PrintTotalDiff shows the difference in the account balances of the accounts used in the e2e test from two balances structs +func (sm *SmokeTestRunner) PrintTotalDiff(accoutBalancesDiff AccountBalancesDiff) { + sm.Logger.Print(" ---💰 Total gas spent ---") + + // show the value only if it is not zero + if accoutBalancesDiff.ZETA.Cmp(big.NewInt(0)) != 0 { + sm.Logger.Print("* ZETA spent: %s", accoutBalancesDiff.ZETA.String()) + } + if accoutBalancesDiff.ETH.Cmp(big.NewInt(0)) != 0 { + sm.Logger.Print("* ETH spent: %s", accoutBalancesDiff.ETH.String()) + } + if accoutBalancesDiff.ERC20.Cmp(big.NewInt(0)) != 0 { + sm.Logger.Print("* ERC20 spent: %s", accoutBalancesDiff.ERC20.String()) + } +} + +// GetAccountBalancesDiff returns the difference in the account balances of the accounts used in the smoke test +func GetAccountBalancesDiff(balancesBefore, balancesAfter AccountBalances) AccountBalancesDiff { + balancesBeforeZeta := big.NewInt(0).Add(balancesBefore.ZetaZETA, balancesBefore.EvmZETA) + balancesBeforeEth := big.NewInt(0).Add(balancesBefore.ZetaETH, balancesBefore.EvmETH) + balancesBeforeErc20 := big.NewInt(0).Add(balancesBefore.ZetaERC20, balancesBefore.EvmERC20) + + balancesAfterZeta := big.NewInt(0).Add(balancesAfter.ZetaZETA, balancesAfter.EvmZETA) + balancesAfterEth := big.NewInt(0).Add(balancesAfter.ZetaETH, balancesAfter.EvmETH) + balancesAfterErc20 := big.NewInt(0).Add(balancesAfter.ZetaERC20, balancesAfter.EvmERC20) + + diffZeta := big.NewInt(0).Sub(balancesBeforeZeta, balancesAfterZeta) + diffEth := big.NewInt(0).Sub(balancesBeforeEth, balancesAfterEth) + diffErc20 := big.NewInt(0).Sub(balancesBeforeErc20, balancesAfterErc20) + + return AccountBalancesDiff{ + ETH: diffEth, + ZETA: diffZeta, + ERC20: diffErc20, + } +} + +// formatBalances formats the AccountBalancesDiff into a one-liner string +func formatBalances(balances AccountBalancesDiff) string { + parts := []string{} + if balances.ETH != nil && balances.ETH.Cmp(big.NewInt(0)) > 0 { + parts = append(parts, fmt.Sprintf("ETH:%s", balances.ETH.String())) + } + if balances.ZETA != nil && balances.ZETA.Cmp(big.NewInt(0)) > 0 { + parts = append(parts, fmt.Sprintf("ZETA:%s", balances.ZETA.String())) + } + if balances.ERC20 != nil && balances.ERC20.Cmp(big.NewInt(0)) > 0 { + parts = append(parts, fmt.Sprintf("ERC20:%s", balances.ERC20.String())) + } + return strings.Join(parts, ",") +} diff --git a/contrib/localnet/orchestrator/smoketest/runner/bitcoin.go b/contrib/localnet/orchestrator/smoketest/runner/bitcoin.go index 9ee876a04e..b0ec145039 100644 --- a/contrib/localnet/orchestrator/smoketest/runner/bitcoin.go +++ b/contrib/localnet/orchestrator/smoketest/runner/bitcoin.go @@ -26,6 +26,44 @@ import ( var blockHeaderBTCTimeout = 5 * time.Minute +// DepositBTCWithAmount deposits BTC on ZetaChain with a specific amount +func (sm *SmokeTestRunner) DepositBTCWithAmount(amount float64) (txHash *chainhash.Hash) { + sm.Logger.Print("⏳ depositing BTC into ZEVM") + + // fetch utxos + utxos, err := sm.BtcRPCClient.ListUnspentMinMaxAddresses(1, 9999999, []btcutil.Address{sm.BTCDeployerAddress}) + if err != nil { + panic(err) + } + + spendableAmount := 0.0 + spendableUTXOs := 0 + for _, utxo := range utxos { + if utxo.Spendable { + spendableAmount += utxo.Amount + spendableUTXOs++ + } + } + + if spendableAmount < amount { + panic(fmt.Errorf("not enough spendable BTC to run the test; have %f", spendableAmount)) + } + + sm.Logger.Info("ListUnspent:") + sm.Logger.Info(" spendableAmount: %f", spendableAmount) + sm.Logger.Info(" spendableUTXOs: %d", spendableUTXOs) + sm.Logger.Info("Now sending two txs to TSS address...") + + amount = amount + zetaclient.BtcDepositorFeeMin + txHash, err = sm.SendToTSSFromDeployerToDeposit(sm.BTCTSSAddress, amount, utxos, sm.BtcRPCClient, sm.BTCDeployerAddress) + if err != nil { + panic(err) + } + sm.Logger.Info("send BTC to TSS txHash: %s", txHash.String()) + + return txHash +} + // DepositBTC deposits BTC on ZetaChain func (sm *SmokeTestRunner) DepositBTC(testHeader bool) { sm.Logger.Print("⏳ depositing BTC into ZEVM") @@ -127,7 +165,7 @@ func (sm *SmokeTestRunner) SendToTSSFromDeployerWithMemo( to btcutil.Address, amount float64, inputUTXOs []btcjson.ListUnspentResult, - btc *rpcclient.Client, + btcRPC *rpcclient.Client, memo []byte, btcDeployerAddress *btcutil.AddressWitnessPubKeyHash, ) (*chainhash.Hash, error) { @@ -156,8 +194,9 @@ func (sm *SmokeTestRunner) SendToTSSFromDeployerWithMemo( btcDeployerAddress: change, } - // create raw transaction - tx, err := btc.CreateRawTransaction(inputs, amountMap, nil) + // create raw + sm.Logger.Info("ADDRESS: %s, %s", btcDeployerAddress.EncodeAddress(), to.EncodeAddress()) + tx, err := btcRPC.CreateRawTransaction(inputs, amountMap, nil) if err != nil { panic(err) } @@ -200,38 +239,42 @@ func (sm *SmokeTestRunner) SendToTSSFromDeployerWithMemo( } } - stx, signed, err := btc.SignRawTransactionWithWallet2(tx, inputsForSign) + stx, signed, err := btcRPC.SignRawTransactionWithWallet2(tx, inputsForSign) if err != nil { panic(err) } if !signed { panic("btc transaction not signed") } - txid, err := btc.SendRawTransaction(stx, true) + txid, err := btcRPC.SendRawTransaction(stx, true) if err != nil { panic(err) } sm.Logger.Info("txid: %+v", txid) - _, err = btc.GenerateToAddress(6, btcDeployerAddress, nil) + _, err = btcRPC.GenerateToAddress(6, btcDeployerAddress, nil) if err != nil { panic(err) } - gtx, err := btc.GetTransaction(txid) + gtx, err := btcRPC.GetTransaction(txid) if err != nil { panic(err) } sm.Logger.Info("rawtx confirmation: %d", gtx.BlockIndex) - rawtx, err := btc.GetRawTransactionVerbose(txid) + rawtx, err := btcRPC.GetRawTransactionVerbose(txid) if err != nil { panic(err) } + btcChainID, err := common.GetBTCChainIDFromChainParams(sm.BitcoinParams) + if err != nil { + panic(err) + } events := zetaclient.FilterAndParseIncomingTx( []btcjson.TxRawResult{*rawtx}, 0, sm.BTCTSSAddress.EncodeAddress(), &log.Logger, - common.BtcRegtestChain().ChainId, + btcChainID, ) sm.Logger.Info("bitcoin intx events:") for _, event := range events { diff --git a/contrib/localnet/orchestrator/smoketest/runner/evm.go b/contrib/localnet/orchestrator/smoketest/runner/evm.go index f2bb0aaea5..cd922ed673 100644 --- a/contrib/localnet/orchestrator/smoketest/runner/evm.go +++ b/contrib/localnet/orchestrator/smoketest/runner/evm.go @@ -71,16 +71,13 @@ func (sm *SmokeTestRunner) SendUSDTOnEvm(address ethcommon.Address, amountUSDT i func (sm *SmokeTestRunner) DepositERC20() ethcommon.Hash { sm.Logger.Print("⏳ depositing ERC20 into ZEVM") - startTime := time.Now() - defer func() { - sm.Logger.Print("✅ ERC20 deposited in %s", time.Since(startTime)) - }() return sm.DepositERC20WithAmountAndMessage(big.NewInt(1e18), []byte{}) } func (sm *SmokeTestRunner) DepositERC20WithAmountAndMessage(amount *big.Int, msg []byte) ethcommon.Hash { - tx, err := sm.USDTERC20.Approve(sm.GoerliAuth, sm.ERC20CustodyAddr, amount) + // reset allowance, necessary for USDT + tx, err := sm.USDTERC20.Approve(sm.GoerliAuth, sm.ERC20CustodyAddr, big.NewInt(0)) if err != nil { panic(err) } @@ -90,7 +87,18 @@ func (sm *SmokeTestRunner) DepositERC20WithAmountAndMessage(amount *big.Int, msg } sm.Logger.Info("USDT Approve receipt tx hash: %s", tx.Hash().Hex()) + tx, err = sm.USDTERC20.Approve(sm.GoerliAuth, sm.ERC20CustodyAddr, amount) + if err != nil { + panic(err) + } + receipt = utils.MustWaitForTxReceipt(sm.Ctx, sm.GoerliClient, tx, sm.Logger, sm.ReceiptTimeout) + if receipt.Status == 0 { + panic("approve failed") + } + sm.Logger.Info("USDT Approve receipt tx hash: %s", tx.Hash().Hex()) + tx, err = sm.ERC20Custody.Deposit(sm.GoerliAuth, sm.DeployerAddress.Bytes(), sm.USDTERC20Addr, amount, msg) + sm.Logger.Print("TX: %v", tx) if err != nil { panic(err) } @@ -115,28 +123,24 @@ func (sm *SmokeTestRunner) DepositERC20WithAmountAndMessage(amount *big.Int, msg // DepositEther sends Ethers into ZEVM func (sm *SmokeTestRunner) DepositEther(testHeader bool) ethcommon.Hash { + return sm.DepositEtherWithAmount(testHeader, big.NewInt(1000000000000000000)) // in wei (1 eth) +} + +// DepositEtherWithAmount sends Ethers into ZEVM +func (sm *SmokeTestRunner) DepositEtherWithAmount(testHeader bool, amount *big.Int) ethcommon.Hash { sm.Logger.Print("⏳ depositing Ethers into ZEVM") - startTime := time.Now() - defer func() { - sm.Logger.Print("✅ Ethers deposited in %s", time.Since(startTime)) - }() - value := big.NewInt(1000000000000000000) // in wei (1 eth) - signedTx, err := sm.SendEther(sm.TSSAddress, value, nil) + signedTx, err := sm.SendEther(sm.TSSAddress, amount, nil) if err != nil { panic(err) } + sm.Logger.EVMTransaction(*signedTx, "send to TSS") - sm.Logger.Info("GOERLI tx sent: %s; to %s, nonce %d", signedTx.Hash().String(), signedTx.To().Hex(), signedTx.Nonce()) receipt := utils.MustWaitForTxReceipt(sm.Ctx, sm.GoerliClient, signedTx, sm.Logger, sm.ReceiptTimeout) if receipt.Status == 0 { panic("deposit failed") } - sm.Logger.Info("GOERLI tx receipt: %d", receipt.Status) - sm.Logger.Info(" tx hash: %s", receipt.TxHash.String()) - sm.Logger.Info(" to: %s", signedTx.To().String()) - sm.Logger.Info(" value: %d", signedTx.Value()) - sm.Logger.Info(" block num: %d", receipt.BlockNumber) + sm.Logger.EVMReceipt(*receipt, "send to TSS") // due to the high block throughput in localnet, ZetaClient might catch up slowly with the blocks // to optimize block header proof test, this test is directly executed here on the first deposit instead of having a separate test diff --git a/contrib/localnet/orchestrator/smoketest/runner/logger.go b/contrib/localnet/orchestrator/smoketest/runner/logger.go index 03b22f4671..3a9ed4827c 100644 --- a/contrib/localnet/orchestrator/smoketest/runner/logger.go +++ b/contrib/localnet/orchestrator/smoketest/runner/logger.go @@ -2,6 +2,11 @@ package runner import ( "fmt" + "sync" + + ethtypes "github.com/ethereum/go-ethereum/core/types" + "github.com/zeta-chain/protocol-contracts/pkg/contracts/zevm/zrc20.sol" + crosschaintypes "github.com/zeta-chain/zetacore/x/crosschain/types" "github.com/fatih/color" ) @@ -16,6 +21,7 @@ type Logger struct { verbose bool logger *color.Color prefix string + mu sync.Mutex } // NewLogger creates a new Logger @@ -32,15 +38,41 @@ func NewLogger(verbose bool, printColor color.Attribute, prefix string) *Logger } } +// SetColor sets the color of the logger +func (l *Logger) SetColor(printColor color.Attribute) { + l.logger = color.New(printColor) +} + +// Prefix returns the prefix of the logger +func (l *Logger) Prefix() string { + return l.getPrefixWithPadding() + loggerSeparator +} + // Print prints a message to the logger func (l *Logger) Print(message string, args ...interface{}) { + l.mu.Lock() + defer l.mu.Unlock() + text := fmt.Sprintf(message, args...) // #nosec G104 - we are not using user input l.logger.Printf(l.getPrefixWithPadding() + loggerSeparator + text + "\n") } +// PrintNoPrefix prints a message to the logger without the prefix +func (l *Logger) PrintNoPrefix(message string, args ...interface{}) { + l.mu.Lock() + defer l.mu.Unlock() + + text := fmt.Sprintf(message, args...) + // #nosec G104 - we are not using user input + l.logger.Printf(text + "\n") +} + // Info prints a message to the logger if verbose is true func (l *Logger) Info(message string, args ...interface{}) { + l.mu.Lock() + defer l.mu.Unlock() + if l.verbose { text := fmt.Sprintf(message, args...) // #nosec G104 - we are not using user input @@ -50,6 +82,9 @@ func (l *Logger) Info(message string, args ...interface{}) { // InfoLoud prints a message to the logger if verbose is true func (l *Logger) InfoLoud(message string, args ...interface{}) { + l.mu.Lock() + defer l.mu.Unlock() + if l.verbose { text := fmt.Sprintf(message, args...) // #nosec G104 - we are not using user input @@ -63,11 +98,107 @@ func (l *Logger) InfoLoud(message string, args ...interface{}) { // Error prints an error message to the logger func (l *Logger) Error(message string, args ...interface{}) { + l.mu.Lock() + defer l.mu.Unlock() + text := fmt.Sprintf(message, args...) // #nosec G104 - we are not using user input l.logger.Printf(l.getPrefixWithPadding() + loggerSeparator + "[ERROR]" + text + "\n") } +// CCTX prints a CCTX +func (l *Logger) CCTX(cctx crosschaintypes.CrossChainTx, name string) { + l.Info(" %s cross-chain transaction: %s", name, cctx.Index) + if cctx.CctxStatus != nil { + l.Info(" CctxStatus:") + l.Info(" Status: %s", cctx.CctxStatus.Status.String()) + if cctx.CctxStatus.StatusMessage != "" { + l.Info(" StatusMessage: %s", cctx.CctxStatus.StatusMessage) + } + } + if cctx.InboundTxParams != nil { + l.Info(" InboundTxParams:") + l.Info(" TxHash: %s", cctx.InboundTxParams.InboundTxObservedHash) + l.Info(" TxHeight: %d", cctx.InboundTxParams.InboundTxObservedExternalHeight) + l.Info(" BallotIndex: %s", cctx.InboundTxParams.InboundTxBallotIndex) + l.Info(" Amount: %s", cctx.InboundTxParams.Amount.String()) + l.Info(" CoinType: %s", cctx.InboundTxParams.CoinType.String()) + l.Info(" SenderChainID: %d", cctx.InboundTxParams.SenderChainId) + l.Info(" Origin: %s", cctx.InboundTxParams.TxOrigin) + if cctx.InboundTxParams.Sender != "" { + l.Info(" Sender: %s", cctx.InboundTxParams.Sender) + } + if cctx.InboundTxParams.Asset != "" { + l.Info(" Asset: %s", cctx.InboundTxParams.Asset) + } + } + if cctx.RelayedMessage != "" { + l.Info(" RelayedMessage: %s", cctx.RelayedMessage) + } + for i, outTxParam := range cctx.OutboundTxParams { + if i == 0 { + l.Info(" OutboundTxParams:") + } else { + l.Info(" RevertTxParams:") + } + l.Info(" TxHash: %s", outTxParam.OutboundTxHash) + l.Info(" TxHeight: %d", outTxParam.OutboundTxObservedExternalHeight) + l.Info(" BallotIndex: %s", outTxParam.OutboundTxBallotIndex) + l.Info(" TSSNonce: %d", outTxParam.OutboundTxTssNonce) + l.Info(" GasLimit: %d", outTxParam.OutboundTxGasLimit) + l.Info(" GasPrice: %s", outTxParam.OutboundTxGasPrice) + l.Info(" GasUsed: %d", outTxParam.OutboundTxGasUsed) + l.Info(" EffectiveGasPrice: %s", outTxParam.OutboundTxEffectiveGasPrice.String()) + l.Info(" EffectiveGasLimit: %d", outTxParam.OutboundTxEffectiveGasLimit) + l.Info(" Amount: %s", outTxParam.Amount.String()) + l.Info(" CoinType: %s", outTxParam.CoinType.String()) + l.Info(" Receiver: %s", outTxParam.Receiver) + l.Info(" ReceiverChainID: %d", outTxParam.ReceiverChainId) + } +} + +// EVMTransaction prints a transaction +func (l *Logger) EVMTransaction(tx ethtypes.Transaction, name string) { + l.Info(" %s EVM transaction: %s", name, tx.Hash().Hex()) + l.Info(" To: %s", tx.To().Hex()) + l.Info(" Value: %d", tx.Value()) + l.Info(" Gas: %d", tx.Gas()) + l.Info(" GasPrice: %d", tx.GasPrice()) +} + +// EVMReceipt prints a receipt +func (l *Logger) EVMReceipt(receipt ethtypes.Receipt, name string) { + l.Info(" %s EVM receipt: %s", name, receipt.TxHash.Hex()) + l.Info(" BlockNumber: %d", receipt.BlockNumber) + l.Info(" GasUsed: %d", receipt.GasUsed) + l.Info(" ContractAddress: %s", receipt.ContractAddress.Hex()) + l.Info(" Status: %d", receipt.Status) +} + +// ZRC20Withdrawal prints a ZRC20Withdrawal event +func (l *Logger) ZRC20Withdrawal( + contract interface { + ParseWithdrawal(ethtypes.Log) (*zrc20.ZRC20Withdrawal, error) + }, + receipt ethtypes.Receipt, + name string, +) { + for _, log := range receipt.Logs { + event, err := contract.ParseWithdrawal(*log) + if err != nil { + continue + } + l.Info( + " %s ZRC20Withdrawal: from %s, to %x, value %d, gasfee %d", + name, + event.From.Hex(), + event.To, + event.Value, + event.Gasfee, + ) + } +} + func (l *Logger) getPrefixWithPadding() string { // add padding to prefix prefix := l.prefix diff --git a/contrib/localnet/orchestrator/smoketest/runner/report.go b/contrib/localnet/orchestrator/smoketest/runner/report.go new file mode 100644 index 0000000000..1c8428a1a9 --- /dev/null +++ b/contrib/localnet/orchestrator/smoketest/runner/report.go @@ -0,0 +1,55 @@ +package runner + +import ( + "fmt" + "strings" + "text/tabwriter" + "time" +) + +// TestReport is a struct that contains the test report +type TestReport struct { + Name string + Success bool + Time time.Duration + GasSpent AccountBalancesDiff +} + +// TestReports is a slice of TestReport +type TestReports []TestReport + +// String returns the string representation of the test report as a table +// it uses text/tabwriter to format the table +func (tr TestReports) String(prefix string) (string, error) { + var b strings.Builder + writer := tabwriter.NewWriter(&b, 0, 4, 4, ' ', 0) + if _, err := fmt.Fprintln(writer, "Name\tSuccess\tTime\tSpent"); err != nil { + return "", err + } + + for _, report := range tr { + spent := formatBalances(report.GasSpent) + success := "✅" + if !report.Success { + success = "❌" + } + if _, err := fmt.Fprintf(writer, "%s%s\t%s\t%s\t%s\n", prefix, report.Name, success, report.Time, spent); err != nil { + return "", err + } + } + + if err := writer.Flush(); err != nil { + return "", err + } + return b.String(), nil +} + +// PrintTestReports prints the test reports +func (sm *SmokeTestRunner) PrintTestReports(tr TestReports) { + sm.Logger.Print(" ---📈 E2E Test Report ---") + table, err := tr.String("") + if err != nil { + sm.Logger.Print("Error rendering test report: %s", err) + } + sm.Logger.PrintNoPrefix(table) +} diff --git a/contrib/localnet/orchestrator/smoketest/runner/runner.go b/contrib/localnet/orchestrator/smoketest/runner/runner.go index 60698ac114..f968e2d9ec 100644 --- a/contrib/localnet/orchestrator/smoketest/runner/runner.go +++ b/contrib/localnet/orchestrator/smoketest/runner/runner.go @@ -7,6 +7,11 @@ import ( "sync" "time" + "github.com/zeta-chain/protocol-contracts/pkg/contracts/zevm/connectorzevm.sol" + "github.com/zeta-chain/protocol-contracts/pkg/contracts/zevm/wzeta.sol" + + "github.com/btcsuite/btcd/chaincfg" + "github.com/btcsuite/btcd/rpcclient" "github.com/btcsuite/btcutil" authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" @@ -80,25 +85,31 @@ type SmokeTestRunner struct { UniswapV2Factory *uniswapv2factory.UniswapV2Factory UniswapV2RouterAddr ethcommon.Address UniswapV2Router *uniswapv2router.UniswapV2Router02 - TestDAppAddr ethcommon.Address - ZEVMSwapAppAddr ethcommon.Address - ZEVMSwapApp *zevmswap.ZEVMSwapApp - ContextAppAddr ethcommon.Address - ContextApp *contextapp.ContextApp - SystemContractAddr ethcommon.Address - SystemContract *systemcontract.SystemContract + ConnectorZEVMAddr ethcommon.Address + ConnectorZEVM *connectorzevm.ZetaConnectorZEVM + WZetaAddr ethcommon.Address + WZeta *wzeta.WETH9 + + TestDAppAddr ethcommon.Address + ZEVMSwapAppAddr ethcommon.Address + ZEVMSwapApp *zevmswap.ZEVMSwapApp + ContextAppAddr ethcommon.Address + ContextApp *contextapp.ContextApp + SystemContractAddr ethcommon.Address + SystemContract *systemcontract.SystemContract // config CctxTimeout time.Duration ReceiptTimeout time.Duration // other - Name string - Ctx context.Context - CtxCancel context.CancelFunc - Logger *Logger - WG sync.WaitGroup - mutex sync.Mutex + Name string + Ctx context.Context + CtxCancel context.CancelFunc + Logger *Logger + WG sync.WaitGroup + BitcoinParams *chaincfg.Params + mutex sync.Mutex } func NewSmokeTestRunner( @@ -167,7 +178,7 @@ func (sm *SmokeTestRunner) RunSmokeTestsFromNames(smokeTests []SmokeTest, smokeT if !ok { return fmt.Errorf("smoke test %s not found", smokeTestName) } - if err := sm.RunSmokeTest(smokeTest); err != nil { + if err := sm.RunSmokeTest(smokeTest, true); err != nil { return err } } @@ -175,10 +186,62 @@ func (sm *SmokeTestRunner) RunSmokeTestsFromNames(smokeTests []SmokeTest, smokeT return nil } +// RunSmokeTestsFromNamesIntoReport runs a list of smoke tests by name in a list of smoke tests and returns a report +// The function doesn't return an error, it returns a report with the error +func (sm *SmokeTestRunner) RunSmokeTestsFromNamesIntoReport(smokeTests []SmokeTest, smokeTestNames ...string) (TestReports, error) { + // get all tests so we can return an error if a test is not found + tests := make([]SmokeTest, 0, len(smokeTestNames)) + for _, smokeTestName := range smokeTestNames { + smokeTest, ok := findSmokeTest(smokeTestName, smokeTests) + if !ok { + return nil, fmt.Errorf("smoke test %s not found", smokeTestName) + } + tests = append(tests, smokeTest) + } + + // go through all tests + reports := make(TestReports, 0, len(smokeTestNames)) + for _, test := range tests { + // get info before test + balancesBefore, err := sm.GetAccountBalances(true) + if err != nil { + return nil, err + } + timeBefore := time.Now() + + // run test + testErr := sm.RunSmokeTest(test, false) + if testErr != nil { + sm.Logger.Print("test %s failed: %s", test.Name, testErr.Error()) + } + + // wait 5 sec to make sure we get updated balances + time.Sleep(5 * time.Second) + + // get info after test + balancesAfter, err := sm.GetAccountBalances(true) + if err != nil { + return nil, err + } + timeAfter := time.Now() + + // create report + report := TestReport{ + Name: test.Name, + Success: testErr == nil, + Time: timeAfter.Sub(timeBefore), + GasSpent: GetAccountBalancesDiff(balancesBefore, balancesAfter), + } + reports = append(reports, report) + } + + return reports, nil +} + // RunSmokeTests runs a list of smoke tests func (sm *SmokeTestRunner) RunSmokeTests(smokeTests []SmokeTest) (err error) { for _, smokeTest := range smokeTests { - if err := sm.RunSmokeTest(smokeTest); err != nil { + if err := sm.RunSmokeTest(smokeTest, true); err != nil { return err } } @@ -186,7 +249,7 @@ func (sm *SmokeTestRunner) RunSmokeTests(smokeTests []SmokeTest) (err error) { } // RunSmokeTest runs a smoke test -func (sm *SmokeTestRunner) RunSmokeTest(smokeTestWithName SmokeTest) (err error) { +func (sm *SmokeTestRunner) RunSmokeTest(smokeTestWithName SmokeTest, checkAccounting bool) (err error) { // return an error on panic // https://github.com/zeta-chain/node/issues/1500 defer func() { @@ -205,8 +268,10 @@ func (sm *SmokeTestRunner) RunSmokeTest(smokeTestWithName SmokeTest) (err error) smokeTestWithName.SmokeTest(sm) //check supplies - if err := sm.CheckZRC20ReserveAndSupply(); err != nil { - return err + if checkAccounting { + if err := sm.CheckZRC20ReserveAndSupply(); err != nil { + return err + } } sm.Logger.Print("✅ completed in %s - %s", time.Since(startTime), smokeTestWithName.Description) @@ -240,6 +305,8 @@ func (sm *SmokeTestRunner) CopyAddressesFrom(other *SmokeTestRunner) (err error) sm.BTCZRC20Addr = other.BTCZRC20Addr sm.UniswapV2FactoryAddr = other.UniswapV2FactoryAddr sm.UniswapV2RouterAddr = other.UniswapV2RouterAddr + sm.ConnectorZEVMAddr = other.ConnectorZEVMAddr + sm.WZetaAddr = other.WZetaAddr sm.TestDAppAddr = other.TestDAppAddr sm.ZEVMSwapAppAddr = other.ZEVMSwapAppAddr sm.ContextAppAddr = other.ContextAppAddr @@ -282,6 +349,15 @@ func (sm *SmokeTestRunner) CopyAddressesFrom(other *SmokeTestRunner) (err error) if err != nil { return err } + sm.ConnectorZEVM, err = connectorzevm.NewZetaConnectorZEVM(sm.ConnectorZEVMAddr, sm.ZevmClient) + if err != nil { + return err + } + sm.WZeta, err = wzeta.NewWETH9(sm.WZetaAddr, sm.ZevmClient) + if err != nil { + return err + } + sm.ZEVMSwapApp, err = zevmswap.NewZEVMSwapApp(sm.ZEVMSwapAppAddr, sm.ZevmClient) if err != nil { return err @@ -319,6 +395,9 @@ func (sm *SmokeTestRunner) PrintContractAddresses() { sm.Logger.Print("BTCZRC20: %s", sm.BTCZRC20Addr.Hex()) sm.Logger.Print("UniswapFactory: %s", sm.UniswapV2FactoryAddr.Hex()) sm.Logger.Print("UniswapRouter: %s", sm.UniswapV2RouterAddr.Hex()) + sm.Logger.Print("ConnectorZEVM: %s", sm.ConnectorZEVMAddr.Hex()) + sm.Logger.Print("WZeta: %s", sm.WZetaAddr.Hex()) + sm.Logger.Print("ZEVMSwapApp: %s", sm.ZEVMSwapAppAddr.Hex()) sm.Logger.Print("ContextApp: %s", sm.ContextAppAddr.Hex()) sm.Logger.Print("TestDapp: %s", sm.TestDAppAddr.Hex()) diff --git a/contrib/localnet/orchestrator/smoketest/runner/setup_bitcoin.go b/contrib/localnet/orchestrator/smoketest/runner/setup_bitcoin.go index 1b4a1ce671..52dfc2549c 100644 --- a/contrib/localnet/orchestrator/smoketest/runner/setup_bitcoin.go +++ b/contrib/localnet/orchestrator/smoketest/runner/setup_bitcoin.go @@ -6,7 +6,6 @@ import ( "time" "github.com/btcsuite/btcd/btcec" - "github.com/btcsuite/btcd/chaincfg" "github.com/btcsuite/btcd/rpcclient" "github.com/btcsuite/btcutil" ) @@ -25,7 +24,7 @@ func (sm *SmokeTestRunner) SetupBitcoinAccount(initNetwork bool) { } } - sm.setBtcAddress() + sm.SetBtcAddress(sm.Name, true) if initNetwork { // import the TSS address @@ -47,29 +46,54 @@ func (sm *SmokeTestRunner) SetupBitcoinAccount(initNetwork bool) { } } -// setBtcAddress -func (sm *SmokeTestRunner) setBtcAddress() { +// GetBtcAddress returns the BTC address of the deployer from its EVM private key +func (sm *SmokeTestRunner) GetBtcAddress() (string, string, error) { skBytes, err := hex.DecodeString(sm.DeployerPrivateKey) if err != nil { - panic(err) + return "", "", err } - // TODO: support non regtest chain - // https://github.com/zeta-chain/node/issues/1482 sk, _ := btcec.PrivKeyFromBytes(btcec.S256(), skBytes) - privkeyWIF, err := btcutil.NewWIF(sk, &chaincfg.RegressionNetParams, true) + privkeyWIF, err := btcutil.NewWIF(sk, sm.BitcoinParams, true) + if err != nil { + return "", "", err + } + + address, err := btcutil.NewAddressWitnessPubKeyHash( + btcutil.Hash160(privkeyWIF.SerializePubKey()), + sm.BitcoinParams, + ) + if err != nil { + return "", "", err + } + + // return the string representation of the address + return address.EncodeAddress(), privkeyWIF.String(), nil +} + +// SetBtcAddress imports the deployer's private key into the Bitcoin node +func (sm *SmokeTestRunner) SetBtcAddress(name string, rescan bool) { + skBytes, err := hex.DecodeString(sm.DeployerPrivateKey) if err != nil { panic(err) } - err = sm.BtcRPCClient.ImportPrivKeyRescan(privkeyWIF, sm.Name, true) + sk, _ := btcec.PrivKeyFromBytes(btcec.S256(), skBytes) + privkeyWIF, err := btcutil.NewWIF(sk, sm.BitcoinParams, true) if err != nil { panic(err) } + if rescan { + err = sm.BtcRPCClient.ImportPrivKeyRescan(privkeyWIF, name, true) + if err != nil { + panic(err) + } + } + sm.BTCDeployerAddress, err = btcutil.NewAddressWitnessPubKeyHash( btcutil.Hash160(privkeyWIF.PrivKey.PubKey().SerializeCompressed()), - &chaincfg.RegressionNetParams, + sm.BitcoinParams, ) if err != nil { panic(err) diff --git a/contrib/localnet/orchestrator/smoketest/runner/setup_zeta.go b/contrib/localnet/orchestrator/smoketest/runner/setup_zeta.go index 4ce1ba196a..b40056f707 100644 --- a/contrib/localnet/orchestrator/smoketest/runner/setup_zeta.go +++ b/contrib/localnet/orchestrator/smoketest/runner/setup_zeta.go @@ -2,9 +2,11 @@ package runner import ( "math/big" - "strings" "time" + "github.com/zeta-chain/protocol-contracts/pkg/contracts/zevm/connectorzevm.sol" + "github.com/zeta-chain/protocol-contracts/pkg/contracts/zevm/wzeta.sol" + "github.com/btcsuite/btcutil" "github.com/ethereum/go-ethereum/accounts/abi/bind" ethcommon "github.com/ethereum/go-ethereum/common" @@ -16,29 +18,29 @@ import ( "github.com/zeta-chain/zetacore/contrib/localnet/orchestrator/smoketest/contracts/contextapp" "github.com/zeta-chain/zetacore/contrib/localnet/orchestrator/smoketest/contracts/zevmswap" "github.com/zeta-chain/zetacore/contrib/localnet/orchestrator/smoketest/utils" - crosschaintypes "github.com/zeta-chain/zetacore/x/crosschain/types" fungibletypes "github.com/zeta-chain/zetacore/x/fungible/types" observertypes "github.com/zeta-chain/zetacore/x/observer/types" ) // SetTSSAddresses set TSS addresses from information queried from ZetaChain -func (sm *SmokeTestRunner) SetTSSAddresses() { +func (sm *SmokeTestRunner) SetTSSAddresses() error { sm.Logger.Print("⚙️ setting up TSS address") - var err error + btcChainID, err := common.GetBTCChainIDFromChainParams(sm.BitcoinParams) + if err != nil { + return err + } + res := &observertypes.QueryGetTssAddressResponse{} - for { - res, err = sm.ObserverClient.GetTssAddress(sm.Ctx, &observertypes.QueryGetTssAddressRequest{}) + for i := 0; ; i++ { + res, err = sm.ObserverClient.GetTssAddress(sm.Ctx, &observertypes.QueryGetTssAddressRequest{ + BitcoinChainId: btcChainID, + }) if err != nil { - // if error contains unknown method GetTssAddress for service, we might be using an older version of the chain for upgrade test - // we query the TSS address with legacy method - if strings.Contains(err.Error(), "unknown method GetTssAddress for service") { - sm.SetTSSAddressesLegacy() - return + if i%10 == 0 { + sm.Logger.Info("ObserverClient.TSS error %s", err.Error()) + sm.Logger.Info("TSS not ready yet, waiting for TSS to be appear in zetacore network...") } - - sm.Logger.Info("ObserverClient.TSS error %s", err.Error()) - sm.Logger.Info("TSS not ready yet, waiting for TSS to be appear in zetacore network...") time.Sleep(1 * time.Second) continue } @@ -47,13 +49,15 @@ func (sm *SmokeTestRunner) SetTSSAddresses() { tssAddress := ethcommon.HexToAddress(res.Eth) - btcTSSAddress, err := common.DecodeBtcAddress(res.Btc, common.BtcRegtestChain().ChainId) + btcTSSAddress, err := btcutil.DecodeAddress(res.Btc, sm.BitcoinParams) if err != nil { panic(err) } sm.TSSAddress = tssAddress sm.BTCTSSAddress = btcTSSAddress + + return nil } // SetZEVMContracts set contracts for the ZEVM @@ -65,7 +69,7 @@ func (sm *SmokeTestRunner) SetZEVMContracts() { }() // deploy system contracts and ZRC20 contracts on ZetaChain - uniswapV2FactoryAddr, uniswapV2RouterAddr, usdtZRC20Addr, err := sm.ZetaTxServer.DeploySystemContractsAndZRC20( + uniswapV2FactoryAddr, uniswapV2RouterAddr, zevmConnectorAddr, wzetaAddr, usdtZRC20Addr, err := sm.ZetaTxServer.DeploySystemContractsAndZRC20( utils.FungibleAdminName, sm.USDTERC20Addr.Hex(), ) @@ -94,6 +98,20 @@ func (sm *SmokeTestRunner) SetZEVMContracts() { panic(err) } + // ZevmConnectorAddr + sm.ConnectorZEVMAddr = ethcommon.HexToAddress(zevmConnectorAddr) + sm.ConnectorZEVM, err = connectorzevm.NewZetaConnectorZEVM(sm.ConnectorZEVMAddr, sm.ZevmClient) + if err != nil { + panic(err) + } + + // WZetaAddr + sm.WZetaAddr = ethcommon.HexToAddress(wzetaAddr) + sm.WZeta, err = wzeta.NewWETH9(sm.WZetaAddr, sm.ZevmClient) + if err != nil { + panic(err) + } + // query system contract address from the chain systemContractRes, err := sm.FungibleClient.SystemContract( sm.Ctx, @@ -151,8 +169,6 @@ func (sm *SmokeTestRunner) SetZEVMContracts() { } func (sm *SmokeTestRunner) SetupETHZRC20() { - // TODO: support non testnet chain - // https://github.com/zeta-chain/node/issues/1482 ethZRC20Addr, err := sm.SystemContract.GasCoinZRC20ByChainId(&bind.CallOpts{}, big.NewInt(common.GoerliLocalnetChain().ChainId)) if err != nil { panic(err) @@ -169,8 +185,6 @@ func (sm *SmokeTestRunner) SetupETHZRC20() { } func (sm *SmokeTestRunner) SetupBTCZRC20() { - // TODO: support non testnet chain - // https://github.com/zeta-chain/node/issues/1482 BTCZRC20Addr, err := sm.SystemContract.GasCoinZRC20ByChainId(&bind.CallOpts{}, big.NewInt(common.BtcRegtestChain().ChainId)) if err != nil { panic(err) @@ -183,29 +197,3 @@ func (sm *SmokeTestRunner) SetupBTCZRC20() { } sm.BTCZRC20 = BTCZRC20 } - -// SetTSSAddressesLegacy set TSS addresses from information queried from ZetaChain using legacy TSS query -// TODO: remove this function after v12 once upgrade testing is no longer needed with v11 -func (sm *SmokeTestRunner) SetTSSAddressesLegacy() { - var err error - res := &crosschaintypes.QueryGetTssAddressResponse{} - for { - res, err = sm.CctxClient.GetTssAddress(sm.Ctx, &crosschaintypes.QueryGetTssAddressRequest{}) - if err != nil { - sm.Logger.Info("cctxClient.TSS (legacy) error %s", err.Error()) - sm.Logger.Info("TSS not ready yet, waiting for TSS to be appear in zetacore network...") - time.Sleep(1 * time.Second) - continue - } - break - } - - tssAddress := ethcommon.HexToAddress(res.Eth) - btcTSSAddress, err := btcutil.DecodeAddress(res.Btc, common.BitcoinRegnetParams) - if err != nil { - panic(err) - } - - sm.TSSAddress = tssAddress - sm.BTCTSSAddress = btcTSSAddress -} diff --git a/contrib/localnet/orchestrator/smoketest/runner/zeta.go b/contrib/localnet/orchestrator/smoketest/runner/zeta.go index bac7f37431..c2405b21de 100644 --- a/contrib/localnet/orchestrator/smoketest/runner/zeta.go +++ b/contrib/localnet/orchestrator/smoketest/runner/zeta.go @@ -3,12 +3,10 @@ package runner import ( "fmt" "math/big" - "time" ethcommon "github.com/ethereum/go-ethereum/common" ethtypes "github.com/ethereum/go-ethereum/core/types" zetaconnectoreth "github.com/zeta-chain/protocol-contracts/pkg/contracts/evm/zetaconnector.eth.sol" - "github.com/zeta-chain/zetacore/common" "github.com/zeta-chain/zetacore/contrib/localnet/orchestrator/smoketest/utils" "github.com/zeta-chain/zetacore/x/crosschain/types" ) @@ -49,29 +47,36 @@ func (sm *SmokeTestRunner) SendZetaOnEvm(address ethcommon.Address, zetaAmount i // DepositZeta deposits ZETA on ZetaChain from the ZETA smart contract on EVM func (sm *SmokeTestRunner) DepositZeta() ethcommon.Hash { - sm.Logger.Print("⏳ depositing ZETA into ZEVM") - startTime := time.Now() - defer func() { - sm.Logger.Print("✅ ZETA deposited in %s", time.Since(startTime)) - }() - amount := big.NewInt(1e18) amount = amount.Mul(amount, big.NewInt(100)) // 100 Zeta + + return sm.DepositZetaWithAmount(amount) +} + +// DepositZetaWithAmount deposits ZETA on ZetaChain from the ZETA smart contract on EVM with the specified amount +func (sm *SmokeTestRunner) DepositZetaWithAmount(amount *big.Int) ethcommon.Hash { tx, err := sm.ZetaEth.Approve(sm.GoerliAuth, sm.ConnectorEthAddr, amount) if err != nil { panic(err) } sm.Logger.Info("Approve tx hash: %s", tx.Hash().Hex()) + receipt := utils.MustWaitForTxReceipt(sm.Ctx, sm.GoerliClient, tx, sm.Logger, sm.ReceiptTimeout) + sm.Logger.EVMReceipt(*receipt, "approve") if receipt.Status != 1 { panic("approve tx failed") } - sm.Logger.Info("Approve tx receipt: status %d", receipt.Status) + + // query the chain ID using zevm client + zetaChainID, err := sm.ZevmClient.ChainID(sm.Ctx) + if err != nil { + panic(err) + } tx, err = sm.ConnectorEth.Send(sm.GoerliAuth, zetaconnectoreth.ZetaInterfacesSendInput{ // TODO: allow user to specify destination chain id // https://github.com/zeta-chain/node-private/issues/41 - DestinationChainId: big.NewInt(common.ZetaPrivnetChain().ChainId), + DestinationChainId: zetaChainID, DestinationAddress: sm.DeployerAddress.Bytes(), DestinationGasLimit: big.NewInt(250_000), Message: nil, @@ -81,13 +86,14 @@ func (sm *SmokeTestRunner) DepositZeta() ethcommon.Hash { if err != nil { panic(err) } - sm.Logger.Info("Send tx hash: %s", tx.Hash().Hex()) + receipt = utils.MustWaitForTxReceipt(sm.Ctx, sm.GoerliClient, tx, sm.Logger, sm.ReceiptTimeout) + sm.Logger.EVMReceipt(*receipt, "send") if receipt.Status != 1 { panic(fmt.Sprintf("expected tx receipt status to be 1; got %d", receipt.Status)) } - sm.Logger.Info("Send tx receipt: status %d", receipt.Status) + sm.Logger.Info(" Logs:") for _, log := range receipt.Logs { sentLog, err := sm.ConnectorEth.ParseZetaSent(*log) diff --git a/contrib/localnet/orchestrator/smoketest/smoketests/smoketests.go b/contrib/localnet/orchestrator/smoketest/smoketests/smoketests.go index 869293574a..92d4c97030 100644 --- a/contrib/localnet/orchestrator/smoketest/smoketests/smoketests.go +++ b/contrib/localnet/orchestrator/smoketest/smoketests/smoketests.go @@ -6,10 +6,9 @@ const ( TestContextUpgradeName = "context_upgrade" TestDepositAndCallRefundName = "deposit_and_call_refund" TestMultipleERC20DepositName = "erc20_multiple_deposit" - TestWithdrawERC20Name = "erc20_withdraw" TestMultipleWithdrawsName = "erc20_multiple_withdraw" - TestSendZetaOutName = "send_zeta_out" - TestSendZetaOutBTCRevertName = "send_zeta_out_btc_revert" // #nosec G101 - not a hardcoded password + TestZetaWithdrawName = "zeta_withdraw" + TestZetaWithdrawBTCRevertName = "zeta_withdraw_btc_revert" // #nosec G101 - not a hardcoded password TestMessagePassingName = "message_passing" TestZRC20SwapName = "zrc20_swap" TestBitcoinWithdrawName = "bitcoin_withdraw" @@ -23,8 +22,19 @@ const ( TestDepositEtherLiquidityCapName = "deposit_eth_liquidity_cap" TestMyTestName = "my_test" - TestERC20DepositName = "erc20_deposit" - TestEtherDepositName = "eth_deposit" + TestERC20WithdrawName = "erc20_withdraw" + TestERC20DepositName = "erc20_deposit" + TestEtherDepositName = "eth_deposit" + TestEtherWithdrawName = "eth_withdraw" + TestBitcoinDepositName = "bitcoin_deposit" + TestZetaDepositName = "zeta_deposit" + + TestDonationEtherName = "donation_ether" + + TestStressEtherWithdrawName = "stress_eth_withdraw" + TestStressBTCWithdrawName = "stress_btc_withdraw" + TestStressEtherDepositName = "stress_eth_deposit" + TestStressBTCDepositName = "stress_btc_deposit" ) // AllSmokeTests is an ordered list of all smoke tests @@ -45,24 +55,29 @@ var AllSmokeTests = []runner.SmokeTest{ TestMultipleERC20Deposit, }, { - TestWithdrawERC20Name, - "withdraw USDT ERC20 from ZEVM", - TestWithdrawERC20, + TestERC20WithdrawName, + "withdraw ERC20 from ZEVM", + TestERC20Withdraw, }, { TestMultipleWithdrawsName, - "withdraw USDT ERC20 from ZEVM in multiple deposits", + "withdraw ERC20 from ZEVM in multiple deposits", TestMultipleWithdraws, }, { - TestSendZetaOutName, - "sending ZETA from ZEVM to Ethereum", - TestSendZetaOut, + TestZetaWithdrawName, + "withdraw ZETA from ZEVM to Ethereum", + TestZetaWithdraw, }, { - TestSendZetaOutBTCRevertName, - "sending ZETA from ZEVM to Bitcoin; should revert when ", - TestSendZetaOutBTCRevert, + TestZetaDepositName, + "deposit ZETA from Ethereum to ZEVM", + TestZetaDeposit, + }, + { + TestZetaWithdrawBTCRevertName, + "sending ZETA from ZEVM to Bitcoin with a message that should revert cctxs", + TestZetaWithdrawBTCRevert, }, { TestMessagePassingName, @@ -101,7 +116,7 @@ var AllSmokeTests = []runner.SmokeTest{ }, { TestERC20DepositAndCallRefundName, - "deposit a non-gas ZRC20 into ZEVM and call a contract that reverts; should refund on ZetaChain if no liquidity pool, should refund on origin if liquidity pool", + "deposit a non-gas ZRC20 into ZEVM and call a contract that reverts", TestERC20DepositAndCallRefund, }, { @@ -134,4 +149,39 @@ var AllSmokeTests = []runner.SmokeTest{ "deposit Ether into ZEVM", TestEtherDeposit, }, + { + TestEtherWithdrawName, + "withdraw Ether from ZEVM", + TestEtherWithdraw, + }, + { + TestBitcoinDepositName, + "deposit Bitcoin into ZEVM", + TestBitcoinDeposit, + }, + { + TestDonationEtherName, + "donate Ether to the TSS", + TestDonationEther, + }, + { + TestStressEtherWithdrawName, + "stress test Ether withdrawal", + TestStressEtherWithdraw, + }, + { + TestStressBTCWithdrawName, + "stress test BTC withdrawal", + TestStressBTCWithdraw, + }, + { + TestStressEtherDepositName, + "stress test Ether deposit", + TestStressEtherDeposit, + }, + { + TestStressBTCDepositName, + "stress test BTC deposit", + TestStressBTCDeposit, + }, } diff --git a/contrib/localnet/orchestrator/smoketest/smoketests/test_bitcoin_deposit.go b/contrib/localnet/orchestrator/smoketest/smoketests/test_bitcoin_deposit.go new file mode 100644 index 0000000000..c16834725b --- /dev/null +++ b/contrib/localnet/orchestrator/smoketest/smoketests/test_bitcoin_deposit.go @@ -0,0 +1,27 @@ +package smoketests + +import ( + "fmt" + + "github.com/zeta-chain/zetacore/contrib/localnet/orchestrator/smoketest/runner" + "github.com/zeta-chain/zetacore/contrib/localnet/orchestrator/smoketest/utils" + crosschaintypes "github.com/zeta-chain/zetacore/x/crosschain/types" +) + +func TestBitcoinDeposit(sm *runner.SmokeTestRunner) { + + sm.SetBtcAddress(sm.Name, false) + + txHash := sm.DepositBTCWithAmount(0.001) + + // wait for the cctx to be mined + cctx := utils.WaitCctxMinedByInTxHash(sm.Ctx, txHash.String(), sm.CctxClient, sm.Logger, sm.CctxTimeout) + sm.Logger.CCTX(*cctx, "deposit") + if cctx.CctxStatus.Status != crosschaintypes.CctxStatus_OutboundMined { + panic(fmt.Sprintf( + "expected mined status; got %s, message: %s", + cctx.CctxStatus.Status.String(), + cctx.CctxStatus.StatusMessage), + ) + } +} diff --git a/contrib/localnet/orchestrator/smoketest/smoketests/test_bitcoin.go b/contrib/localnet/orchestrator/smoketest/smoketests/test_bitcoin_withdraw.go similarity index 100% rename from contrib/localnet/orchestrator/smoketest/smoketests/test_bitcoin.go rename to contrib/localnet/orchestrator/smoketest/smoketests/test_bitcoin_withdraw.go diff --git a/contrib/localnet/orchestrator/smoketest/smoketests/test_crosschain_swap.go b/contrib/localnet/orchestrator/smoketest/smoketests/test_crosschain_swap.go index 1a1ce1aa3c..803d5b6a7c 100644 --- a/contrib/localnet/orchestrator/smoketest/smoketests/test_crosschain_swap.go +++ b/contrib/localnet/orchestrator/smoketest/smoketests/test_crosschain_swap.go @@ -230,7 +230,7 @@ func TestCrosschainSwap(sm *runner.SmokeTestRunner) { sm.Logger.Info(" vout %d", vout.N) sm.Logger.Info(" value %f", vout.Value) sm.Logger.Info(" scriptPubKey %s", vout.ScriptPubKey.Hex) - sm.Logger.Info(" p2wpkh address: %s", utils.ScriptPKToAddress(vout.ScriptPubKey.Hex)) + sm.Logger.Info(" p2wpkh address: %s", utils.ScriptPKToAddress(vout.ScriptPubKey.Hex, sm.BitcoinParams)) } } diff --git a/contrib/localnet/orchestrator/smoketest/smoketests/test_donation.go b/contrib/localnet/orchestrator/smoketest/smoketests/test_donation.go new file mode 100644 index 0000000000..a81af67f3f --- /dev/null +++ b/contrib/localnet/orchestrator/smoketest/smoketests/test_donation.go @@ -0,0 +1,25 @@ +package smoketests + +import ( + "math/big" + + "github.com/zeta-chain/zetacore/contrib/localnet/orchestrator/smoketest/runner" + "github.com/zeta-chain/zetacore/contrib/localnet/orchestrator/smoketest/utils" + "github.com/zeta-chain/zetacore/zetaclient" +) + +// TestDonationEther tests donation of ether to the tss address +func TestDonationEther(sm *runner.SmokeTestRunner) { + txDonation, err := sm.SendEther(sm.TSSAddress, big.NewInt(100000000000000000), []byte(zetaclient.DonationMessage)) + if err != nil { + panic(err) + } + sm.Logger.EVMTransaction(*txDonation, "donation") + + // check contract deployment receipt + receipt := utils.MustWaitForTxReceipt(sm.Ctx, sm.GoerliClient, txDonation, sm.Logger, sm.ReceiptTimeout) + sm.Logger.EVMReceipt(*receipt, "donation") + if receipt.Status != 1 { + panic("donation tx failed") + } +} diff --git a/contrib/localnet/orchestrator/smoketest/smoketests/test_erc20_deposit.go b/contrib/localnet/orchestrator/smoketest/smoketests/test_erc20_deposit.go index d9439331e9..2c3a47eddf 100644 --- a/contrib/localnet/orchestrator/smoketest/smoketests/test_erc20_deposit.go +++ b/contrib/localnet/orchestrator/smoketest/smoketests/test_erc20_deposit.go @@ -1,81 +1,16 @@ package smoketests import ( - "fmt" "math/big" - "github.com/ethereum/go-ethereum/accounts/abi/bind" - ethcommon "github.com/ethereum/go-ethereum/common" "github.com/zeta-chain/zetacore/contrib/localnet/orchestrator/smoketest/runner" "github.com/zeta-chain/zetacore/contrib/localnet/orchestrator/smoketest/utils" - testcontract "github.com/zeta-chain/zetacore/testutil/contracts" ) func TestERC20Deposit(sm *runner.SmokeTestRunner) { - sm.DepositERC20() -} - -func TestMultipleERC20Deposit(sm *runner.SmokeTestRunner) { - initialBal, err := sm.USDTZRC20.BalanceOf(&bind.CallOpts{}, sm.DeployerAddress) - if err != nil { - panic(err) - } - txhash := MultipleDeposits(sm, big.NewInt(1e9), big.NewInt(3)) - cctxs := utils.WaitCctxsMinedByInTxHash(sm.Ctx, txhash.Hex(), sm.CctxClient, 3, sm.Logger, sm.CctxTimeout) - if len(cctxs) != 3 { - panic(fmt.Sprintf("cctxs length is not correct: %d", len(cctxs))) - } - - // check new balance is increased by 1e9 * 3 - bal, err := sm.USDTZRC20.BalanceOf(&bind.CallOpts{}, sm.DeployerAddress) - if err != nil { - panic(err) - } - diff := big.NewInt(0).Sub(bal, initialBal) - if diff.Int64() != 3e9 { - panic(fmt.Sprintf("balance difference is not correct: %d", diff.Int64())) - } -} - -func MultipleDeposits(sm *runner.SmokeTestRunner, amount, count *big.Int) ethcommon.Hash { - // deploy depositor - depositorAddr, _, depositor, err := testcontract.DeployDepositor(sm.GoerliAuth, sm.GoerliClient, sm.ERC20CustodyAddr) - if err != nil { - panic(err) - } - - fullAmount := big.NewInt(0).Mul(amount, count) - - // approve - tx, err := sm.USDTERC20.Approve(sm.GoerliAuth, depositorAddr, fullAmount) - if err != nil { - panic(err) - } - receipt := utils.MustWaitForTxReceipt(sm.Ctx, sm.GoerliClient, tx, sm.Logger, sm.ReceiptTimeout) - if receipt.Status == 0 { - panic("approve failed") - } - sm.Logger.Info("USDT Approve receipt tx hash: %s", tx.Hash().Hex()) - - // deposit - tx, err = depositor.RunDeposits(sm.GoerliAuth, sm.DeployerAddress.Bytes(), sm.USDTERC20Addr, amount, []byte{}, count) - if err != nil { - panic(err) - } - receipt = utils.MustWaitForTxReceipt(sm.Ctx, sm.GoerliClient, tx, sm.Logger, sm.ReceiptTimeout) - if receipt.Status == 0 { - panic("deposits failed") - } - sm.Logger.Info("Deposits receipt tx hash: %s", tx.Hash().Hex()) + hash := sm.DepositERC20WithAmountAndMessage(big.NewInt(100000), []byte{}) - for _, log := range receipt.Logs { - event, err := sm.ERC20Custody.ParseDeposited(*log) - if err != nil { - continue - } - sm.Logger.Info("Multiple deposit event: ") - sm.Logger.Info(" Amount: %d, ", event.Amount) - } - sm.Logger.Info("gas limit %d", sm.ZevmAuth.GasLimit) - return tx.Hash() + // wait for the cctx to be mined + cctx := utils.WaitCctxMinedByInTxHash(sm.Ctx, hash.Hex(), sm.CctxClient, sm.Logger, sm.CctxTimeout) + sm.Logger.CCTX(*cctx, "deposit") } diff --git a/contrib/localnet/orchestrator/smoketest/smoketests/test_erc20_multiple_deposits.go b/contrib/localnet/orchestrator/smoketest/smoketests/test_erc20_multiple_deposits.go new file mode 100644 index 0000000000..5af6a9f693 --- /dev/null +++ b/contrib/localnet/orchestrator/smoketest/smoketests/test_erc20_multiple_deposits.go @@ -0,0 +1,77 @@ +package smoketests + +import ( + "fmt" + "math/big" + + "github.com/ethereum/go-ethereum/accounts/abi/bind" + ethcommon "github.com/ethereum/go-ethereum/common" + "github.com/zeta-chain/zetacore/contrib/localnet/orchestrator/smoketest/runner" + "github.com/zeta-chain/zetacore/contrib/localnet/orchestrator/smoketest/utils" + testcontract "github.com/zeta-chain/zetacore/testutil/contracts" +) + +func TestMultipleERC20Deposit(sm *runner.SmokeTestRunner) { + initialBal, err := sm.USDTZRC20.BalanceOf(&bind.CallOpts{}, sm.DeployerAddress) + if err != nil { + panic(err) + } + txhash := MultipleDeposits(sm, big.NewInt(1e9), big.NewInt(3)) + cctxs := utils.WaitCctxsMinedByInTxHash(sm.Ctx, txhash.Hex(), sm.CctxClient, 3, sm.Logger, sm.CctxTimeout) + if len(cctxs) != 3 { + panic(fmt.Sprintf("cctxs length is not correct: %d", len(cctxs))) + } + + // check new balance is increased by 1e9 * 3 + bal, err := sm.USDTZRC20.BalanceOf(&bind.CallOpts{}, sm.DeployerAddress) + if err != nil { + panic(err) + } + diff := big.NewInt(0).Sub(bal, initialBal) + if diff.Int64() != 3e9 { + panic(fmt.Sprintf("balance difference is not correct: %d", diff.Int64())) + } +} + +func MultipleDeposits(sm *runner.SmokeTestRunner, amount, count *big.Int) ethcommon.Hash { + // deploy depositor + depositorAddr, _, depositor, err := testcontract.DeployDepositor(sm.GoerliAuth, sm.GoerliClient, sm.ERC20CustodyAddr) + if err != nil { + panic(err) + } + + fullAmount := big.NewInt(0).Mul(amount, count) + + // approve + tx, err := sm.USDTERC20.Approve(sm.GoerliAuth, depositorAddr, fullAmount) + if err != nil { + panic(err) + } + receipt := utils.MustWaitForTxReceipt(sm.Ctx, sm.GoerliClient, tx, sm.Logger, sm.ReceiptTimeout) + if receipt.Status == 0 { + panic("approve failed") + } + sm.Logger.Info("USDT Approve receipt tx hash: %s", tx.Hash().Hex()) + + // deposit + tx, err = depositor.RunDeposits(sm.GoerliAuth, sm.DeployerAddress.Bytes(), sm.USDTERC20Addr, amount, []byte{}, count) + if err != nil { + panic(err) + } + receipt = utils.MustWaitForTxReceipt(sm.Ctx, sm.GoerliClient, tx, sm.Logger, sm.ReceiptTimeout) + if receipt.Status == 0 { + panic("deposits failed") + } + sm.Logger.Info("Deposits receipt tx hash: %s", tx.Hash().Hex()) + + for _, log := range receipt.Logs { + event, err := sm.ERC20Custody.ParseDeposited(*log) + if err != nil { + continue + } + sm.Logger.Info("Multiple deposit event: ") + sm.Logger.Info(" Amount: %d, ", event.Amount) + } + sm.Logger.Info("gas limit %d", sm.ZevmAuth.GasLimit) + return tx.Hash() +} diff --git a/contrib/localnet/orchestrator/smoketest/smoketests/test_erc20_multiple_withdraws.go b/contrib/localnet/orchestrator/smoketest/smoketests/test_erc20_multiple_withdraws.go new file mode 100644 index 0000000000..60fa01e6b4 --- /dev/null +++ b/contrib/localnet/orchestrator/smoketest/smoketests/test_erc20_multiple_withdraws.go @@ -0,0 +1,78 @@ +package smoketests + +import ( + "fmt" + "math/big" + + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/zeta-chain/zetacore/contrib/localnet/orchestrator/smoketest/runner" + "github.com/zeta-chain/zetacore/contrib/localnet/orchestrator/smoketest/utils" + testcontract "github.com/zeta-chain/zetacore/testutil/contracts" +) + +func TestMultipleWithdraws(sm *runner.SmokeTestRunner) { + // deploy withdrawer + withdrawerAddr, _, withdrawer, err := testcontract.DeployWithdrawer(sm.ZevmAuth, sm.ZevmClient) + if err != nil { + panic(err) + } + + // approve + tx, err := sm.USDTZRC20.Approve(sm.ZevmAuth, withdrawerAddr, big.NewInt(1e18)) + if err != nil { + panic(err) + } + receipt := utils.MustWaitForTxReceipt(sm.Ctx, sm.ZevmClient, tx, sm.Logger, sm.ReceiptTimeout) + if receipt.Status == 0 { + panic("approve failed") + } + sm.Logger.Info("USDT ZRC20 approve receipt: status %d", receipt.Status) + + // approve gas token + tx, err = sm.ETHZRC20.Approve(sm.ZevmAuth, withdrawerAddr, big.NewInt(1e18)) + if err != nil { + panic(err) + } + receipt = utils.MustWaitForTxReceipt(sm.Ctx, sm.ZevmClient, tx, sm.Logger, sm.ReceiptTimeout) + if receipt.Status == 0 { + panic("approve gas token failed") + } + sm.Logger.Info("eth zrc20 approve receipt: status %d", receipt.Status) + + // check the balance + bal, err := sm.USDTZRC20.BalanceOf(&bind.CallOpts{}, sm.DeployerAddress) + if err != nil { + panic(err) + } + sm.Logger.Info("balance of deployer on USDT ZRC20: %d", bal) + + if bal.Int64() < 1000 { + panic("not enough USDT ZRC20 balance!") + } + + // withdraw + tx, err = withdrawer.RunWithdraws( + sm.ZevmAuth, + sm.DeployerAddress.Bytes(), + sm.USDTZRC20Addr, + big.NewInt(100), + big.NewInt(3), + ) + if err != nil { + panic(err) + } + receipt = utils.MustWaitForTxReceipt(sm.Ctx, sm.ZevmClient, tx, sm.Logger, sm.ReceiptTimeout) + if receipt.Status == 0 { + panic("withdraw failed") + } + + cctxs := utils.WaitCctxsMinedByInTxHash(sm.Ctx, tx.Hash().Hex(), sm.CctxClient, 3, sm.Logger, sm.CctxTimeout) + if len(cctxs) != 3 { + panic(fmt.Sprintf("cctxs length is not correct: %d", len(cctxs))) + } + + // verify the withdraw value + for _, cctx := range cctxs { + verifyTransferAmountFromCCTX(sm, cctx, 100) + } +} diff --git a/contrib/localnet/orchestrator/smoketest/smoketests/test_erc20_refund.go b/contrib/localnet/orchestrator/smoketest/smoketests/test_erc20_refund.go index 7834baacfa..dcfc86acc1 100644 --- a/contrib/localnet/orchestrator/smoketest/smoketests/test_erc20_refund.go +++ b/contrib/localnet/orchestrator/smoketest/smoketests/test_erc20_refund.go @@ -32,6 +32,7 @@ func TestERC20DepositAndCallRefund(sm *runner.SmokeTestRunner) { // There is no liquidity pool, therefore the cctx should abort cctx := utils.WaitCctxMinedByInTxHash(sm.Ctx, inTxHash, sm.CctxClient, sm.Logger, sm.CctxTimeout) + sm.Logger.CCTX(*cctx, "deposit") if cctx.CctxStatus.Status != types.CctxStatus_Aborted { panic(fmt.Sprintf("expected cctx status to be Aborted; got %s", cctx.CctxStatus.Status)) } diff --git a/contrib/localnet/orchestrator/smoketest/smoketests/test_erc20_withdraw.go b/contrib/localnet/orchestrator/smoketest/smoketests/test_erc20_withdraw.go index 4dad98d96a..793c718c12 100644 --- a/contrib/localnet/orchestrator/smoketest/smoketests/test_erc20_withdraw.go +++ b/contrib/localnet/orchestrator/smoketest/smoketests/test_erc20_withdraw.go @@ -1,18 +1,15 @@ package smoketests import ( - "fmt" "math/big" - "github.com/ethereum/go-ethereum/accounts/abi/bind" ethcommon "github.com/ethereum/go-ethereum/common" "github.com/zeta-chain/zetacore/contrib/localnet/orchestrator/smoketest/runner" "github.com/zeta-chain/zetacore/contrib/localnet/orchestrator/smoketest/utils" - testcontract "github.com/zeta-chain/zetacore/testutil/contracts" crosschaintypes "github.com/zeta-chain/zetacore/x/crosschain/types" ) -func TestWithdrawERC20(sm *runner.SmokeTestRunner) { +func TestERC20Withdraw(sm *runner.SmokeTestRunner) { // approve tx, err := sm.ETHZRC20.Approve(sm.ZevmAuth, sm.USDTZRC20Addr, big.NewInt(1e18)) if err != nil { @@ -25,7 +22,7 @@ func TestWithdrawERC20(sm *runner.SmokeTestRunner) { sm.Logger.Info("eth zrc20 approve receipt: status %d", receipt.Status) // withdraw - tx, err = sm.USDTZRC20.Withdraw(sm.ZevmAuth, sm.DeployerAddress.Bytes(), big.NewInt(100)) + tx, err = sm.USDTZRC20.Withdraw(sm.ZevmAuth, sm.DeployerAddress.Bytes(), big.NewInt(1000)) if err != nil { panic(err) } @@ -47,74 +44,7 @@ func TestWithdrawERC20(sm *runner.SmokeTestRunner) { // verify the withdraw value cctx := utils.WaitCctxMinedByInTxHash(sm.Ctx, receipt.TxHash.Hex(), sm.CctxClient, sm.Logger, sm.CctxTimeout) - verifyTransferAmountFromCCTX(sm, cctx, 100) -} - -func TestMultipleWithdraws(sm *runner.SmokeTestRunner) { - // deploy withdrawer - withdrawerAddr, _, withdrawer, err := testcontract.DeployWithdrawer(sm.ZevmAuth, sm.ZevmClient) - if err != nil { - panic(err) - } - - // approve - tx, err := sm.USDTZRC20.Approve(sm.ZevmAuth, withdrawerAddr, big.NewInt(1e18)) - if err != nil { - panic(err) - } - receipt := utils.MustWaitForTxReceipt(sm.Ctx, sm.ZevmClient, tx, sm.Logger, sm.ReceiptTimeout) - if receipt.Status == 0 { - panic("approve failed") - } - sm.Logger.Info("USDT ZRC20 approve receipt: status %d", receipt.Status) - - // approve gas token - tx, err = sm.ETHZRC20.Approve(sm.ZevmAuth, withdrawerAddr, big.NewInt(1e18)) - if err != nil { - panic(err) - } - receipt = utils.MustWaitForTxReceipt(sm.Ctx, sm.ZevmClient, tx, sm.Logger, sm.ReceiptTimeout) - if receipt.Status == 0 { - panic("approve gas token failed") - } - sm.Logger.Info("eth zrc20 approve receipt: status %d", receipt.Status) - - // check the balance - bal, err := sm.USDTZRC20.BalanceOf(&bind.CallOpts{}, sm.DeployerAddress) - if err != nil { - panic(err) - } - sm.Logger.Info("balance of deployer on USDT ZRC20: %d", bal) - - if bal.Int64() < 1000 { - panic("not enough USDT ZRC20 balance!") - } - - // withdraw - tx, err = withdrawer.RunWithdraws( - sm.ZevmAuth, - sm.DeployerAddress.Bytes(), - sm.USDTZRC20Addr, - big.NewInt(100), - big.NewInt(3), - ) - if err != nil { - panic(err) - } - receipt = utils.MustWaitForTxReceipt(sm.Ctx, sm.ZevmClient, tx, sm.Logger, sm.ReceiptTimeout) - if receipt.Status == 0 { - panic("withdraw failed") - } - - cctxs := utils.WaitCctxsMinedByInTxHash(sm.Ctx, tx.Hash().Hex(), sm.CctxClient, 3, sm.Logger, sm.CctxTimeout) - if len(cctxs) != 3 { - panic(fmt.Sprintf("cctxs length is not correct: %d", len(cctxs))) - } - - // verify the withdraw value - for _, cctx := range cctxs { - verifyTransferAmountFromCCTX(sm, cctx, 100) - } + verifyTransferAmountFromCCTX(sm, cctx, 1000) } // verifyTransferAmountFromCCTX verifies the transfer amount from the CCTX on Goerli diff --git a/contrib/localnet/orchestrator/smoketest/smoketests/test_deposit_eth.go b/contrib/localnet/orchestrator/smoketest/smoketests/test_eth_deposit.go similarity index 96% rename from contrib/localnet/orchestrator/smoketest/smoketests/test_deposit_eth.go rename to contrib/localnet/orchestrator/smoketest/smoketests/test_eth_deposit.go index 444009af35..44146e53cd 100644 --- a/contrib/localnet/orchestrator/smoketest/smoketests/test_deposit_eth.go +++ b/contrib/localnet/orchestrator/smoketest/smoketests/test_eth_deposit.go @@ -18,6 +18,15 @@ import ( fungibletypes "github.com/zeta-chain/zetacore/x/fungible/types" ) +// TestEtherDeposit tests deposit of ethers +func TestEtherDeposit(sm *runner.SmokeTestRunner) { + hash := sm.DepositEtherWithAmount(false, big.NewInt(10000000000000000)) // in wei (0.01 eth) + + // wait for the cctx to be mined + cctx := utils.WaitCctxMinedByInTxHash(sm.Ctx, hash.Hex(), sm.CctxClient, sm.Logger, sm.CctxTimeout) + sm.Logger.CCTX(*cctx, "deposit") +} + // TestEtherDepositAndCall tests deposit of ethers calling a example contract func TestEtherDepositAndCall(sm *runner.SmokeTestRunner) { sm.Logger.Info("Deploying example contract") diff --git a/contrib/localnet/orchestrator/smoketest/smoketests/test_eth_withdraw.go b/contrib/localnet/orchestrator/smoketest/smoketests/test_eth_withdraw.go new file mode 100644 index 0000000000..1629eede7e --- /dev/null +++ b/contrib/localnet/orchestrator/smoketest/smoketests/test_eth_withdraw.go @@ -0,0 +1,46 @@ +package smoketests + +import ( + "math/big" + + "github.com/zeta-chain/zetacore/contrib/localnet/orchestrator/smoketest/runner" + "github.com/zeta-chain/zetacore/contrib/localnet/orchestrator/smoketest/utils" + crosschaintypes "github.com/zeta-chain/zetacore/x/crosschain/types" +) + +// TestEtherWithdraw tests the withdraw of ether +func TestEtherWithdraw(sm *runner.SmokeTestRunner) { + // approve + tx, err := sm.ETHZRC20.Approve(sm.ZevmAuth, sm.ETHZRC20Addr, big.NewInt(1e18)) + if err != nil { + panic(err) + } + sm.Logger.EVMTransaction(*tx, "approve") + + receipt := utils.MustWaitForTxReceipt(sm.Ctx, sm.ZevmClient, tx, sm.Logger, sm.ReceiptTimeout) + if receipt.Status == 0 { + panic("approve failed") + } + sm.Logger.EVMReceipt(*receipt, "approve") + + // withdraw + tx, err = sm.ETHZRC20.Withdraw(sm.ZevmAuth, sm.DeployerAddress.Bytes(), big.NewInt(100000)) + if err != nil { + panic(err) + } + sm.Logger.EVMTransaction(*tx, "withdraw") + + receipt = utils.MustWaitForTxReceipt(sm.Ctx, sm.ZevmClient, tx, sm.Logger, sm.ReceiptTimeout) + if receipt.Status == 0 { + panic("withdraw failed") + } + sm.Logger.EVMReceipt(*receipt, "withdraw") + sm.Logger.ZRC20Withdrawal(sm.ETHZRC20, *receipt, "withdraw") + + // verify the withdraw value + cctx := utils.WaitCctxMinedByInTxHash(sm.Ctx, receipt.TxHash.Hex(), sm.CctxClient, sm.Logger, sm.CctxTimeout) + sm.Logger.CCTX(*cctx, "withdraw") + if cctx.CctxStatus.Status != crosschaintypes.CctxStatus_OutboundMined { + panic("cctx status is not outbound mined") + } +} diff --git a/contrib/localnet/orchestrator/smoketest/smoketests/test_ether_deposit.go b/contrib/localnet/orchestrator/smoketest/smoketests/test_ether_deposit.go deleted file mode 100644 index 4a600ffc84..0000000000 --- a/contrib/localnet/orchestrator/smoketest/smoketests/test_ether_deposit.go +++ /dev/null @@ -1,7 +0,0 @@ -package smoketests - -import "github.com/zeta-chain/zetacore/contrib/localnet/orchestrator/smoketest/runner" - -func TestEtherDeposit(sm *runner.SmokeTestRunner) { - sm.DepositEther(false) -} diff --git a/contrib/localnet/orchestrator/smoketest/smoketests/test_message_passing.go b/contrib/localnet/orchestrator/smoketest/smoketests/test_message_passing.go index 0ca5ae987b..ea6b5473bd 100644 --- a/contrib/localnet/orchestrator/smoketest/smoketests/test_message_passing.go +++ b/contrib/localnet/orchestrator/smoketest/smoketests/test_message_passing.go @@ -14,6 +14,11 @@ import ( ) func TestMessagePassing(sm *runner.SmokeTestRunner) { + chainID, err := sm.GoerliClient.ChainID(sm.Ctx) + if err != nil { + panic(err) + } + sm.Logger.Info("Approving ConnectorEth to spend deployer's ZetaEth") amount := big.NewInt(1e18) amount = amount.Mul(amount, big.NewInt(10)) // 10 Zeta @@ -31,7 +36,7 @@ func TestMessagePassing(sm *runner.SmokeTestRunner) { sm.Logger.Info("Approve tx receipt: %d", receipt.Status) sm.Logger.Info("Calling ConnectorEth.Send") tx, err = sm.ConnectorEth.Send(auth, zetaconnectoreth.ZetaInterfacesSendInput{ - DestinationChainId: big.NewInt(1337), // in dev mode, GOERLI has chainid 1337 + DestinationChainId: chainID, DestinationAddress: sm.DeployerAddress.Bytes(), DestinationGasLimit: big.NewInt(400_000), Message: nil, @@ -92,7 +97,10 @@ func TestMessagePassing(sm *runner.SmokeTestRunner) { } func TestMessagePassingRevertFail(sm *runner.SmokeTestRunner) { - sm.Logger.Info("Approving ConnectorEth to spend deployer's ZetaEth") + chainID, err := sm.GoerliClient.ChainID(sm.Ctx) + if err != nil { + panic(err) + } amount := big.NewInt(1e18) amount = amount.Mul(amount, big.NewInt(10)) // 10 Zeta @@ -109,7 +117,7 @@ func TestMessagePassingRevertFail(sm *runner.SmokeTestRunner) { sm.Logger.Info("Approve tx receipt: %d", receipt.Status) sm.Logger.Info("Calling ConnectorEth.Send") tx, err = sm.ConnectorEth.Send(auth, zetaconnectoreth.ZetaInterfacesSendInput{ - DestinationChainId: big.NewInt(1337), // in dev mode, GOERLI has chainid 1337 + DestinationChainId: chainID, DestinationAddress: sm.DeployerAddress.Bytes(), DestinationGasLimit: big.NewInt(400_000), Message: []byte("revert"), // non-empty message will cause revert, because the dest address is not a contract @@ -152,7 +160,10 @@ func TestMessagePassingRevertFail(sm *runner.SmokeTestRunner) { } func TestMessagePassingRevertSuccess(sm *runner.SmokeTestRunner) { - sm.Logger.Info("Approving TestDApp to spend deployer's ZetaEth") + chainID, err := sm.GoerliClient.ChainID(sm.Ctx) + if err != nil { + panic(err) + } amount := big.NewInt(1e18) amount = amount.Mul(amount, big.NewInt(10)) // 10 Zeta @@ -184,7 +195,7 @@ func TestMessagePassingRevertSuccess(sm *runner.SmokeTestRunner) { } sm.Logger.Info("$$$ Before: SUPPLY OF AZETA: %d", res2.Amount.Amount) - tx, err = testDApp.SendHelloWorld(auth, sm.TestDAppAddr, big.NewInt(1337), amount, true) + tx, err = testDApp.SendHelloWorld(auth, sm.TestDAppAddr, chainID, amount, true) if err != nil { panic(err) } diff --git a/contrib/localnet/orchestrator/smoketest/smoketests/test_stress_btc_deposit.go b/contrib/localnet/orchestrator/smoketest/smoketests/test_stress_btc_deposit.go new file mode 100644 index 0000000000..7c4e3d680c --- /dev/null +++ b/contrib/localnet/orchestrator/smoketest/smoketests/test_stress_btc_deposit.go @@ -0,0 +1,62 @@ +package smoketests + +import ( + "fmt" + "time" + + "github.com/btcsuite/btcd/chaincfg/chainhash" + + "github.com/zeta-chain/zetacore/contrib/localnet/orchestrator/smoketest/runner" + "github.com/zeta-chain/zetacore/contrib/localnet/orchestrator/smoketest/utils" + crosschaintypes "github.com/zeta-chain/zetacore/x/crosschain/types" + "golang.org/x/sync/errgroup" +) + +// TestStressBTCDeposit tests the stressing deposit of BTC +func TestStressBTCDeposit(sm *runner.SmokeTestRunner) { + // number of deposits to perform + numDeposits := 100 + + sm.SetBtcAddress(sm.Name, false) + + sm.Logger.Print("starting stress test of %d deposits", numDeposits) + + // create a wait group to wait for all the deposits to complete + var eg errgroup.Group + + // send the deposits + for i := 0; i < numDeposits; i++ { + i := i + txHash := sm.DepositBTCWithAmount(0.001) + sm.Logger.Print("index %d: starting deposit, tx hash: %s", i, txHash.String()) + + eg.Go(func() error { + return MonitorBTCDeposit(sm, txHash, i, time.Now()) + }) + } + + // wait for all the deposits to complete + if err := eg.Wait(); err != nil { + panic(err) + } + + sm.Logger.Print("all deposits completed") +} + +// MonitorBTCDeposit monitors the deposit of BTC, returns once the deposit is complete +func MonitorBTCDeposit(sm *runner.SmokeTestRunner, hash *chainhash.Hash, index int, startTime time.Time) error { + cctx := utils.WaitCctxMinedByInTxHash(sm.Ctx, hash.String(), sm.CctxClient, sm.Logger, sm.ReceiptTimeout) + if cctx.CctxStatus.Status != crosschaintypes.CctxStatus_OutboundMined { + return fmt.Errorf( + "index %d: deposit cctx failed with status %s, message %s, cctx index %s", + index, + cctx.CctxStatus.Status, + cctx.CctxStatus.StatusMessage, + cctx.Index, + ) + } + timeToComplete := time.Now().Sub(startTime) + sm.Logger.Print("index %d: deposit cctx success in %s", index, timeToComplete.String()) + + return nil +} diff --git a/contrib/localnet/orchestrator/smoketest/smoketests/test_stress_btc_withdraw.go b/contrib/localnet/orchestrator/smoketest/smoketests/test_stress_btc_withdraw.go new file mode 100644 index 0000000000..38b692a7e9 --- /dev/null +++ b/contrib/localnet/orchestrator/smoketest/smoketests/test_stress_btc_withdraw.go @@ -0,0 +1,73 @@ +package smoketests + +import ( + "fmt" + "math/big" + "time" + + "github.com/btcsuite/btcutil" + ethtypes "github.com/ethereum/go-ethereum/core/types" + "github.com/zeta-chain/zetacore/contrib/localnet/orchestrator/smoketest/runner" + "github.com/zeta-chain/zetacore/contrib/localnet/orchestrator/smoketest/utils" + crosschaintypes "github.com/zeta-chain/zetacore/x/crosschain/types" + "golang.org/x/sync/errgroup" +) + +// TestStressBTCWithdraw tests the stressing withdraw of btc +func TestStressBTCWithdraw(sm *runner.SmokeTestRunner) { + // number of withdraws to perform + numWithdraws := 100 + + sm.Logger.Print("starting stress test of %d withdraws", numWithdraws) + + // create a wait group to wait for all the withdraws to complete + var eg errgroup.Group + + // send the withdraws + for i := 0; i < numWithdraws; i++ { + i := i + tx, err := sm.BTCZRC20.Withdraw( + sm.ZevmAuth, + []byte(sm.BTCDeployerAddress.EncodeAddress()), + big.NewInt(0.01*btcutil.SatoshiPerBitcoin), + ) + if err != nil { + panic(err) + } + receipt := utils.MustWaitForTxReceipt(sm.Ctx, sm.ZevmClient, tx, sm.Logger, sm.ReceiptTimeout) + if receipt.Status == 0 { + //sm.Logger.Info("index %d: withdraw evm tx failed", index) + panic(fmt.Sprintf("index %d: withdraw btc tx %s failed", i, tx.Hash().Hex())) + } + sm.Logger.Print("index %d: starting withdraw, tx hash: %s", i, tx.Hash().Hex()) + + eg.Go(func() error { + return MonitorBTCWithdraw(sm, tx, i, time.Now()) + }) + } + + // wait for all the withdraws to complete + if err := eg.Wait(); err != nil { + panic(err) + } + + sm.Logger.Print("all withdraws completed") +} + +// MonitorBTCWithdraw monitors the withdraw of BTC, returns once the withdraw is complete +func MonitorBTCWithdraw(sm *runner.SmokeTestRunner, tx *ethtypes.Transaction, index int, startTime time.Time) error { + cctx := utils.WaitCctxMinedByInTxHash(sm.Ctx, tx.Hash().Hex(), sm.CctxClient, sm.Logger, sm.ReceiptTimeout) + if cctx.CctxStatus.Status != crosschaintypes.CctxStatus_OutboundMined { + return fmt.Errorf( + "index %d: withdraw cctx failed with status %s, message %s, cctx index %s", + index, + cctx.CctxStatus.Status, + cctx.CctxStatus.StatusMessage, + cctx.Index, + ) + } + timeToComplete := time.Now().Sub(startTime) + sm.Logger.Print("index %d: withdraw cctx success in %s", index, timeToComplete.String()) + + return nil +} diff --git a/contrib/localnet/orchestrator/smoketest/smoketests/test_stress_eth_deposit.go b/contrib/localnet/orchestrator/smoketest/smoketests/test_stress_eth_deposit.go new file mode 100644 index 0000000000..d12ba4efde --- /dev/null +++ b/contrib/localnet/orchestrator/smoketest/smoketests/test_stress_eth_deposit.go @@ -0,0 +1,60 @@ +package smoketests + +import ( + "fmt" + "math/big" + "time" + + ethcommon "github.com/ethereum/go-ethereum/common" + "github.com/zeta-chain/zetacore/contrib/localnet/orchestrator/smoketest/runner" + "github.com/zeta-chain/zetacore/contrib/localnet/orchestrator/smoketest/utils" + crosschaintypes "github.com/zeta-chain/zetacore/x/crosschain/types" + "golang.org/x/sync/errgroup" +) + +// TestStressEtherDeposit tests the stressing deposit of ether +func TestStressEtherDeposit(sm *runner.SmokeTestRunner) { + // number of deposits to perform + numDeposits := 100 + + sm.Logger.Print("starting stress test of %d deposits", numDeposits) + + // create a wait group to wait for all the deposits to complete + var eg errgroup.Group + + // send the deposits + for i := 0; i < numDeposits; i++ { + i := i + hash := sm.DepositERC20WithAmountAndMessage(big.NewInt(100000), []byte{}) + sm.Logger.Print("index %d: starting deposit, tx hash: %s", i, hash.Hex()) + + eg.Go(func() error { + return MonitorEtherDeposit(sm, hash, i, time.Now()) + }) + } + + // wait for all the deposits to complete + if err := eg.Wait(); err != nil { + panic(err) + } + + sm.Logger.Print("all deposits completed") +} + +// MonitorEtherDeposit monitors the deposit of ether, returns once the deposit is complete +func MonitorEtherDeposit(sm *runner.SmokeTestRunner, hash ethcommon.Hash, index int, startTime time.Time) error { + cctx := utils.WaitCctxMinedByInTxHash(sm.Ctx, hash.Hex(), sm.CctxClient, sm.Logger, sm.ReceiptTimeout) + if cctx.CctxStatus.Status != crosschaintypes.CctxStatus_OutboundMined { + return fmt.Errorf( + "index %d: deposit cctx failed with status %s, message %s, cctx index %s", + index, + cctx.CctxStatus.Status, + cctx.CctxStatus.StatusMessage, + cctx.Index, + ) + } + timeToComplete := time.Now().Sub(startTime) + sm.Logger.Print("index %d: deposit cctx success in %s", index, timeToComplete.String()) + + return nil +} diff --git a/contrib/localnet/orchestrator/smoketest/smoketests/test_stress_eth_withdraw.go b/contrib/localnet/orchestrator/smoketest/smoketests/test_stress_eth_withdraw.go new file mode 100644 index 0000000000..a8802a0f8f --- /dev/null +++ b/contrib/localnet/orchestrator/smoketest/smoketests/test_stress_eth_withdraw.go @@ -0,0 +1,69 @@ +package smoketests + +import ( + "fmt" + "math/big" + "time" + + "golang.org/x/sync/errgroup" + + ethtypes "github.com/ethereum/go-ethereum/core/types" + "github.com/zeta-chain/zetacore/contrib/localnet/orchestrator/smoketest/runner" + "github.com/zeta-chain/zetacore/contrib/localnet/orchestrator/smoketest/utils" + crosschaintypes "github.com/zeta-chain/zetacore/x/crosschain/types" +) + +// TestStressEtherWithdraw tests the stressing withdraw of ether +func TestStressEtherWithdraw(sm *runner.SmokeTestRunner) { + // number of withdraws to perform + numWithdraws := 100 + + sm.Logger.Print("starting stress test of %d withdraws", numWithdraws) + + // create a wait group to wait for all the withdraws to complete + var eg errgroup.Group + + // send the withdraws + for i := 0; i < numWithdraws; i++ { + i := i + tx, err := sm.ETHZRC20.Withdraw(sm.ZevmAuth, sm.DeployerAddress.Bytes(), big.NewInt(100000)) + if err != nil { + panic(err) + } + receipt := utils.MustWaitForTxReceipt(sm.Ctx, sm.ZevmClient, tx, sm.Logger, sm.ReceiptTimeout) + if receipt.Status == 0 { + //sm.Logger.Info("index %d: withdraw evm tx failed", index) + panic(fmt.Sprintf("index %d: withdraw evm tx %s failed", i, tx.Hash().Hex())) + } + sm.Logger.Print("index %d: starting withdraw, tx hash: %s", i, tx.Hash().Hex()) + + eg.Go(func() error { + return MonitorEtherWithdraw(sm, tx, i, time.Now()) + }) + } + + // wait for all the withdraws to complete + if err := eg.Wait(); err != nil { + panic(err) + } + + sm.Logger.Print("all withdraws completed") +} + +// MonitorEtherWithdraw monitors the withdraw of ether, returns once the withdraw is complete +func MonitorEtherWithdraw(sm *runner.SmokeTestRunner, tx *ethtypes.Transaction, index int, startTime time.Time) error { + cctx := utils.WaitCctxMinedByInTxHash(sm.Ctx, tx.Hash().Hex(), sm.CctxClient, sm.Logger, sm.ReceiptTimeout) + if cctx.CctxStatus.Status != crosschaintypes.CctxStatus_OutboundMined { + return fmt.Errorf( + "index %d: withdraw cctx failed with status %s, message %s, cctx index %s", + index, + cctx.CctxStatus.Status, + cctx.CctxStatus.StatusMessage, + cctx.Index, + ) + } + timeToComplete := time.Now().Sub(startTime) + sm.Logger.Print("index %d: withdraw cctx success in %s", index, timeToComplete.String()) + + return nil +} diff --git a/contrib/localnet/orchestrator/smoketest/smoketests/test_zeta_deposit.go b/contrib/localnet/orchestrator/smoketest/smoketests/test_zeta_deposit.go new file mode 100644 index 0000000000..886103b32f --- /dev/null +++ b/contrib/localnet/orchestrator/smoketest/smoketests/test_zeta_deposit.go @@ -0,0 +1,17 @@ +package smoketests + +import ( + "math/big" + + "github.com/zeta-chain/zetacore/contrib/localnet/orchestrator/smoketest/runner" + "github.com/zeta-chain/zetacore/contrib/localnet/orchestrator/smoketest/utils" +) + +func TestZetaDeposit(sm *runner.SmokeTestRunner) { + // Deposit 1 Zeta + hash := sm.DepositZetaWithAmount(big.NewInt(1e18)) + + // wait for the cctx to be mined + cctx := utils.WaitCctxMinedByInTxHash(sm.Ctx, hash.Hex(), sm.CctxClient, sm.Logger, sm.CctxTimeout) + sm.Logger.CCTX(*cctx, "deposit") +} diff --git a/contrib/localnet/orchestrator/smoketest/smoketests/test_zeta_in_and_out.go b/contrib/localnet/orchestrator/smoketest/smoketests/test_zeta_in_and_out.go deleted file mode 100644 index 7db514951a..0000000000 --- a/contrib/localnet/orchestrator/smoketest/smoketests/test_zeta_in_and_out.go +++ /dev/null @@ -1,172 +0,0 @@ -package smoketests - -import ( - "fmt" - "math/big" - - cctxtypes "github.com/zeta-chain/zetacore/x/crosschain/types" - - ethcommon "github.com/ethereum/go-ethereum/common" - connectorzevm "github.com/zeta-chain/protocol-contracts/pkg/contracts/zevm/connectorzevm.sol" - wzeta "github.com/zeta-chain/protocol-contracts/pkg/contracts/zevm/wzeta.sol" - "github.com/zeta-chain/zetacore/common" - "github.com/zeta-chain/zetacore/contrib/localnet/orchestrator/smoketest/runner" - "github.com/zeta-chain/zetacore/contrib/localnet/orchestrator/smoketest/utils" -) - -func TestSendZetaOut(sm *runner.SmokeTestRunner) { - zevmClient := sm.ZevmClient - cctxClient := sm.CctxClient - - ConnectorZEVMAddr := ethcommon.HexToAddress("0x239e96c8f17C85c30100AC26F635Ea15f23E9c67") - ConnectorZEVM, err := connectorzevm.NewZetaConnectorZEVM(ConnectorZEVMAddr, zevmClient) - if err != nil { - panic(err) - } - - wzetaAddr := ethcommon.HexToAddress("0x5F0b1a82749cb4E2278EC87F8BF6B618dC71a8bf") - wZeta, err := wzeta.NewWETH9(wzetaAddr, zevmClient) - if err != nil { - panic(err) - } - zchainid, err := zevmClient.ChainID(sm.Ctx) - if err != nil { - panic(err) - } - sm.Logger.Info("zevm chainid: %d", zchainid) - - // 10 Zeta - amount := big.NewInt(1e18) - amount = amount.Mul(amount, big.NewInt(10)) - - zauth := sm.ZevmAuth - zauth.Value = amount - tx, err := wZeta.Deposit(zauth) - if err != nil { - panic(err) - } - zauth.Value = big.NewInt(0) - sm.Logger.Info("Deposit tx hash: %s", tx.Hash().Hex()) - - receipt := utils.MustWaitForTxReceipt(sm.Ctx, zevmClient, tx, sm.Logger, sm.ReceiptTimeout) - sm.Logger.Info("Deposit tx receipt: status %d", receipt.Status) - - tx, err = wZeta.Approve(zauth, ConnectorZEVMAddr, amount) - if err != nil { - panic(err) - } - sm.Logger.Info("wzeta.approve tx hash: %s", tx.Hash().Hex()) - receipt = utils.MustWaitForTxReceipt(sm.Ctx, zevmClient, tx, sm.Logger, sm.ReceiptTimeout) - sm.Logger.Info("approve tx receipt: status %d", receipt.Status) - tx, err = ConnectorZEVM.Send(zauth, connectorzevm.ZetaInterfacesSendInput{ - DestinationChainId: big.NewInt(1337), - DestinationAddress: sm.DeployerAddress.Bytes(), - DestinationGasLimit: big.NewInt(400_000), - Message: nil, - ZetaValueAndGas: amount, - ZetaParams: nil, - }) - if err != nil { - panic(err) - } - sm.Logger.Info("send tx hash: %s", tx.Hash().Hex()) - receipt = utils.MustWaitForTxReceipt(sm.Ctx, zevmClient, tx, sm.Logger, sm.ReceiptTimeout) - sm.Logger.Info("send tx receipt: status %d", receipt.Status) - sm.Logger.Info(" Logs:") - for _, log := range receipt.Logs { - sentLog, err := ConnectorZEVM.ParseZetaSent(*log) - if err == nil { - sm.Logger.Info(" Dest Addr: %s", ethcommon.BytesToAddress(sentLog.DestinationAddress).Hex()) - sm.Logger.Info(" Dest Chain: %d", sentLog.DestinationChainId) - sm.Logger.Info(" Dest Gas: %d", sentLog.DestinationGasLimit) - sm.Logger.Info(" Zeta Value: %d", sentLog.ZetaValueAndGas) - } - } - sm.Logger.Info("waiting for cctx status to change to final...") - - cctx := utils.WaitCctxMinedByInTxHash(sm.Ctx, tx.Hash().Hex(), cctxClient, sm.Logger, sm.CctxTimeout) - if cctx.CctxStatus.Status != cctxtypes.CctxStatus_OutboundMined { - panic(fmt.Errorf( - "expected cctx status to be %s; got %s, message %s", - cctxtypes.CctxStatus_OutboundMined, - cctx.CctxStatus.Status.String(), - cctx.CctxStatus.StatusMessage, - )) - } - receipt, err = sm.GoerliClient.TransactionReceipt(sm.Ctx, ethcommon.HexToHash(cctx.GetCurrentOutTxParam().OutboundTxHash)) - if err != nil { - panic(err) - } - if receipt.Status != 1 { - panic(fmt.Errorf("tx failed")) - } - for _, log := range receipt.Logs { - event, err := sm.ConnectorEth.ParseZetaReceived(*log) - if err == nil { - sm.Logger.Info(" Dest Addr: %s", event.DestinationAddress.Hex()) - sm.Logger.Info(" sender addr: %x", event.ZetaTxSenderAddress) - sm.Logger.Info(" Zeta Value: %d", event.ZetaValue) - if event.ZetaValue.Cmp(amount) != -1 { - panic("wrong zeta value, gas should be paid in the amount") - } - } - } -} - -func TestSendZetaOutBTCRevert(sm *runner.SmokeTestRunner) { - zevmClient := sm.ZevmClient - - ConnectorZEVMAddr := ethcommon.HexToAddress("0x239e96c8f17C85c30100AC26F635Ea15f23E9c67") - ConnectorZEVM, err := connectorzevm.NewZetaConnectorZEVM(ConnectorZEVMAddr, zevmClient) - if err != nil { - panic(err) - } - - wzetaAddr := ethcommon.HexToAddress("0x5F0b1a82749cb4E2278EC87F8BF6B618dC71a8bf") - wZeta, err := wzeta.NewWETH9(wzetaAddr, zevmClient) - if err != nil { - panic(err) - } - zchainid, err := zevmClient.ChainID(sm.Ctx) - if err != nil { - panic(err) - } - sm.Logger.Info("zevm chainid: %d", zchainid) - - zauth := sm.ZevmAuth - zauth.Value = big.NewInt(1e18) - tx, err := wZeta.Deposit(zauth) - if err != nil { - panic(err) - } - zauth.Value = big.NewInt(0) - - sm.Logger.Info("Deposit tx hash: %s", tx.Hash().Hex()) - receipt := utils.MustWaitForTxReceipt(sm.Ctx, zevmClient, tx, sm.Logger, sm.ReceiptTimeout) - sm.Logger.Info("Deposit tx receipt: status %d", receipt.Status) - - tx, err = wZeta.Approve(zauth, ConnectorZEVMAddr, big.NewInt(1e18)) - if err != nil { - panic(err) - } - sm.Logger.Info("wzeta.approve tx hash: %s", tx.Hash().Hex()) - receipt = utils.MustWaitForTxReceipt(sm.Ctx, zevmClient, tx, sm.Logger, sm.ReceiptTimeout) - sm.Logger.Info("approve tx receipt: status %d", receipt.Status) - tx, err = ConnectorZEVM.Send(zauth, connectorzevm.ZetaInterfacesSendInput{ - DestinationChainId: big.NewInt(common.BtcRegtestChain().ChainId), - DestinationAddress: sm.DeployerAddress.Bytes(), - DestinationGasLimit: big.NewInt(400_000), - Message: nil, - ZetaValueAndGas: big.NewInt(1e17), - ZetaParams: nil, - }) - if err != nil { - panic(err) - } - sm.Logger.Info("send tx hash: %s", tx.Hash().Hex()) - receipt = utils.MustWaitForTxReceipt(sm.Ctx, zevmClient, tx, sm.Logger, sm.ReceiptTimeout) - sm.Logger.Info("send tx receipt: status %d", receipt.Status) - if receipt.Status != 0 { - panic("Was able to send ZETA to BTC") - } -} diff --git a/contrib/localnet/orchestrator/smoketest/smoketests/test_zeta_withdraw.go b/contrib/localnet/orchestrator/smoketest/smoketests/test_zeta_withdraw.go new file mode 100644 index 0000000000..8f9fa959ec --- /dev/null +++ b/contrib/localnet/orchestrator/smoketest/smoketests/test_zeta_withdraw.go @@ -0,0 +1,138 @@ +package smoketests + +import ( + "fmt" + "math/big" + + cctxtypes "github.com/zeta-chain/zetacore/x/crosschain/types" + + ethcommon "github.com/ethereum/go-ethereum/common" + connectorzevm "github.com/zeta-chain/protocol-contracts/pkg/contracts/zevm/connectorzevm.sol" + "github.com/zeta-chain/zetacore/common" + "github.com/zeta-chain/zetacore/contrib/localnet/orchestrator/smoketest/runner" + "github.com/zeta-chain/zetacore/contrib/localnet/orchestrator/smoketest/utils" +) + +func TestZetaWithdraw(sm *runner.SmokeTestRunner) { + amount := big.NewInt(0).Mul(big.NewInt(1e18), big.NewInt(10)) // 10 Zeta + + sm.ZevmAuth.Value = amount + tx, err := sm.WZeta.Deposit(sm.ZevmAuth) + if err != nil { + panic(err) + } + sm.ZevmAuth.Value = big.NewInt(0) + sm.Logger.Info("wzeta deposit tx hash: %s", tx.Hash().Hex()) + + receipt := utils.MustWaitForTxReceipt(sm.Ctx, sm.ZevmClient, tx, sm.Logger, sm.ReceiptTimeout) + sm.Logger.EVMReceipt(*receipt, "wzeta deposit") + if receipt.Status == 0 { + panic("deposit failed") + } + + chainID, err := sm.GoerliClient.ChainID(sm.Ctx) + if err != nil { + panic(err) + } + + tx, err = sm.WZeta.Approve(sm.ZevmAuth, sm.ConnectorZEVMAddr, amount) + if err != nil { + panic(err) + } + sm.Logger.Info("wzeta approve tx hash: %s", tx.Hash().Hex()) + + receipt = utils.MustWaitForTxReceipt(sm.Ctx, sm.ZevmClient, tx, sm.Logger, sm.ReceiptTimeout) + sm.Logger.EVMReceipt(*receipt, "wzeta approve") + if receipt.Status == 0 { + panic(fmt.Sprintf("approve failed, logs: %+v", receipt.Logs)) + } + + tx, err = sm.ConnectorZEVM.Send(sm.ZevmAuth, connectorzevm.ZetaInterfacesSendInput{ + DestinationChainId: chainID, + DestinationAddress: sm.DeployerAddress.Bytes(), + DestinationGasLimit: big.NewInt(400_000), + Message: nil, + ZetaValueAndGas: amount, + ZetaParams: nil, + }) + if err != nil { + panic(err) + } + sm.Logger.Info("send tx hash: %s", tx.Hash().Hex()) + receipt = utils.MustWaitForTxReceipt(sm.Ctx, sm.ZevmClient, tx, sm.Logger, sm.ReceiptTimeout) + sm.Logger.EVMReceipt(*receipt, "send") + if receipt.Status == 0 { + panic(fmt.Sprintf("send failed, logs: %+v", receipt.Logs)) + + } + + sm.Logger.Info(" Logs:") + for _, log := range receipt.Logs { + sentLog, err := sm.ConnectorZEVM.ParseZetaSent(*log) + if err == nil { + sm.Logger.Info(" Dest Addr: %s", ethcommon.BytesToAddress(sentLog.DestinationAddress).Hex()) + sm.Logger.Info(" Dest Chain: %d", sentLog.DestinationChainId) + sm.Logger.Info(" Dest Gas: %d", sentLog.DestinationGasLimit) + sm.Logger.Info(" Zeta Value: %d", sentLog.ZetaValueAndGas) + } + } + sm.Logger.Info("waiting for cctx status to change to final...") + + cctx := utils.WaitCctxMinedByInTxHash(sm.Ctx, tx.Hash().Hex(), sm.CctxClient, sm.Logger, sm.CctxTimeout) + sm.Logger.CCTX(*cctx, "zeta withdraw") + if cctx.CctxStatus.Status != cctxtypes.CctxStatus_OutboundMined { + panic(fmt.Errorf( + "expected cctx status to be %s; got %s, message %s", + cctxtypes.CctxStatus_OutboundMined, + cctx.CctxStatus.Status.String(), + cctx.CctxStatus.StatusMessage, + )) + } +} + +func TestZetaWithdrawBTCRevert(sm *runner.SmokeTestRunner) { + sm.ZevmAuth.Value = big.NewInt(1e18) // 1 Zeta + tx, err := sm.WZeta.Deposit(sm.ZevmAuth) + if err != nil { + panic(err) + } + sm.ZevmAuth.Value = big.NewInt(0) + sm.Logger.Info("Deposit tx hash: %s", tx.Hash().Hex()) + + receipt := utils.MustWaitForTxReceipt(sm.Ctx, sm.ZevmClient, tx, sm.Logger, sm.ReceiptTimeout) + sm.Logger.EVMReceipt(*receipt, "Deposit") + if receipt.Status != 1 { + panic("Deposit failed") + } + + tx, err = sm.WZeta.Approve(sm.ZevmAuth, sm.ConnectorZEVMAddr, big.NewInt(1e18)) + if err != nil { + panic(err) + } + sm.Logger.Info("wzeta.approve tx hash: %s", tx.Hash().Hex()) + + receipt = utils.MustWaitForTxReceipt(sm.Ctx, sm.ZevmClient, tx, sm.Logger, sm.ReceiptTimeout) + sm.Logger.EVMReceipt(*receipt, "Approve") + if receipt.Status != 1 { + panic("Approve failed") + } + + tx, err = sm.ConnectorZEVM.Send(sm.ZevmAuth, connectorzevm.ZetaInterfacesSendInput{ + DestinationChainId: big.NewInt(common.BtcRegtestChain().ChainId), + DestinationAddress: sm.DeployerAddress.Bytes(), + DestinationGasLimit: big.NewInt(400_000), + Message: nil, + ZetaValueAndGas: big.NewInt(1e17), + ZetaParams: nil, + }) + if err != nil { + panic(err) + } + sm.Logger.Info("send tx hash: %s", tx.Hash().Hex()) + + receipt = utils.MustWaitForTxReceipt(sm.Ctx, sm.ZevmClient, tx, sm.Logger, sm.ReceiptTimeout) + sm.Logger.EVMReceipt(*receipt, "send") + if receipt.Status != 0 { + panic("Was able to send ZETA to BTC") + } +} diff --git a/contrib/localnet/orchestrator/smoketest/txserver/zeta_tx_server.go b/contrib/localnet/orchestrator/smoketest/txserver/zeta_tx_server.go index f742641b38..d43cbd7fe6 100644 --- a/contrib/localnet/orchestrator/smoketest/txserver/zeta_tx_server.go +++ b/contrib/localnet/orchestrator/smoketest/txserver/zeta_tx_server.go @@ -168,42 +168,54 @@ func (zts ZetaTxServer) BroadcastTx(account string, msg sdktypes.Msg) (*sdktypes // DeploySystemContractsAndZRC20 deploys the system contracts and ZRC20 contracts // returns the addresses of uniswap factory, router and usdt zrc20 -func (zts ZetaTxServer) DeploySystemContractsAndZRC20(account, usdtERC20Addr string) (string, string, string, error) { +func (zts ZetaTxServer) DeploySystemContractsAndZRC20(account, usdtERC20Addr string) (string, string, string, string, string, error) { // retrieve account acc, err := zts.clientCtx.Keyring.Key(account) if err != nil { - return "", "", "", err + return "", "", "", "", "", err } addr, err := acc.GetAddress() if err != nil { - return "", "", "", err + return "", "", "", "", "", err } // deploy new system contracts res, err := zts.BroadcastTx(account, fungibletypes.NewMsgDeploySystemContracts(addr.String())) if err != nil { - return "", "", "", fmt.Errorf("failed to deploy system contracts: %s", err.Error()) + return "", "", "", "", "", fmt.Errorf("failed to deploy system contracts: %s", err.Error()) } systemContractAddress, err := fetchAttribute(res, "system_contract") if err != nil { - return "", "", "", fmt.Errorf("failed to fetch system contract address: %s; rawlog %s", err.Error(), res.RawLog) + return "", "", "", "", "", fmt.Errorf("failed to fetch system contract address: %s; rawlog %s", err.Error(), res.RawLog) } - // set system contract + // get system contract _, err = zts.BroadcastTx(account, fungibletypes.NewMsgUpdateSystemContract(addr.String(), systemContractAddress)) if err != nil { - return "", "", "", fmt.Errorf("failed to set system contract: %s", err.Error()) + return "", "", "", "", "", fmt.Errorf("failed to set system contract: %s", err.Error()) } - // set uniswap contract addresses + // get uniswap contract addresses uniswapV2FactoryAddr, err := fetchAttribute(res, "uniswap_v2_factory") if err != nil { - return "", "", "", fmt.Errorf("failed to fetch uniswap v2 factory address: %s", err.Error()) + return "", "", "", "", "", fmt.Errorf("failed to fetch uniswap v2 factory address: %s", err.Error()) } uniswapV2RouterAddr, err := fetchAttribute(res, "uniswap_v2_router") if err != nil { - return "", "", "", fmt.Errorf("failed to fetch uniswap v2 router address: %s", err.Error()) + return "", "", "", "", "", fmt.Errorf("failed to fetch uniswap v2 router address: %s", err.Error()) + } + + // get zevm connector address + zevmConnectorAddr, err := fetchAttribute(res, "connector_zevm") + if err != nil { + return "", "", "", "", "", fmt.Errorf("failed to fetch zevm connector address: %s, txResponse: %s", err.Error(), res.String()) + } + + // get wzeta address + wzetaAddr, err := fetchAttribute(res, "wzeta") + if err != nil { + return "", "", "", "", "", fmt.Errorf("failed to fetch wzeta address: %s, txResponse: %s", err.Error(), res.String()) } // deploy eth zrc20 @@ -218,7 +230,7 @@ func (zts ZetaTxServer) DeploySystemContractsAndZRC20(account, usdtERC20Addr str 100000, )) if err != nil { - return "", "", "", fmt.Errorf("failed to deploy eth zrc20: %s", err.Error()) + return "", "", "", "", "", fmt.Errorf("failed to deploy eth zrc20: %s", err.Error()) } // deploy btc zrc20 @@ -233,7 +245,7 @@ func (zts ZetaTxServer) DeploySystemContractsAndZRC20(account, usdtERC20Addr str 100000, )) if err != nil { - return "", "", "", fmt.Errorf("failed to deploy btc zrc20: %s", err.Error()) + return "", "", "", "", "", fmt.Errorf("failed to deploy btc zrc20: %s", err.Error()) } // deploy usdt zrc20 @@ -248,18 +260,18 @@ func (zts ZetaTxServer) DeploySystemContractsAndZRC20(account, usdtERC20Addr str 100000, )) if err != nil { - return "", "", "", fmt.Errorf("failed to deploy usdt zrc20: %s", err.Error()) + return "", "", "", "", "", fmt.Errorf("failed to deploy usdt zrc20: %s", err.Error()) } // fetch the usdt zrc20 contract address and remove the quotes usdtZRC20Addr, err := fetchAttribute(res, "Contract") if err != nil { - return "", "", "", fmt.Errorf("failed to fetch usdt zrc20 contract address: %s", err.Error()) + return "", "", "", "", "", fmt.Errorf("failed to fetch usdt zrc20 contract address: %s", err.Error()) } if !ethcommon.IsHexAddress(usdtZRC20Addr) { - return "", "", "", fmt.Errorf("invalid address in event: %s", usdtZRC20Addr) + return "", "", "", "", "", fmt.Errorf("invalid address in event: %s", usdtZRC20Addr) } - return uniswapV2FactoryAddr, uniswapV2RouterAddr, usdtZRC20Addr, nil + return uniswapV2FactoryAddr, uniswapV2RouterAddr, zevmConnectorAddr, wzetaAddr, usdtZRC20Addr, nil } // newCodec returns the codec for msg server diff --git a/contrib/localnet/orchestrator/smoketest/utils/evm.go b/contrib/localnet/orchestrator/smoketest/utils/evm.go index ec1273bcd4..d3f34f818c 100644 --- a/contrib/localnet/orchestrator/smoketest/utils/evm.go +++ b/contrib/localnet/orchestrator/smoketest/utils/evm.go @@ -48,13 +48,13 @@ func MustWaitForTxReceipt( } start := time.Now() - for { + for i := 0; ; i++ { if time.Since(start) > timeout { panic("waiting tx receipt timeout") } receipt, err := client.TransactionReceipt(ctx, tx.Hash()) if err != nil { - if !errors.Is(err, ethereum.NotFound) { + if !errors.Is(err, ethereum.NotFound) && i%10 == 0 { logger.Info("fetching tx receipt error: ", err.Error()) } time.Sleep(1 * time.Second) diff --git a/contrib/localnet/orchestrator/smoketest/utils/utils.go b/contrib/localnet/orchestrator/smoketest/utils/utils.go index c0ee15acde..bfa3d509b3 100644 --- a/contrib/localnet/orchestrator/smoketest/utils/utils.go +++ b/contrib/localnet/orchestrator/smoketest/utils/utils.go @@ -8,10 +8,10 @@ import ( ) // ScriptPKToAddress is a hex string for P2WPKH script -func ScriptPKToAddress(scriptPKHex string) string { +func ScriptPKToAddress(scriptPKHex string, params *chaincfg.Params) string { pkh, err := hex.DecodeString(scriptPKHex[4:]) if err == nil { - addr, err := btcutil.NewAddressWitnessPubKeyHash(pkh, &chaincfg.RegressionNetParams) + addr, err := btcutil.NewAddressWitnessPubKeyHash(pkh, params) if err == nil { return addr.EncodeAddress() } diff --git a/contrib/localnet/orchestrator/smoketest/utils/zetacore.go b/contrib/localnet/orchestrator/smoketest/utils/zetacore.go index 2ec8074354..6a491d1ebd 100644 --- a/contrib/localnet/orchestrator/smoketest/utils/zetacore.go +++ b/contrib/localnet/orchestrator/smoketest/utils/zetacore.go @@ -48,36 +48,45 @@ func WaitCctxsMinedByInTxHash( } // fetch cctxs by inTxHash - for { + for i := 0; ; i++ { time.Sleep(1 * time.Second) res, err := cctxClient.InTxHashToCctxData(ctx, &crosschaintypes.QueryInTxHashToCctxDataRequest{ InTxHash: inTxHash, }) if err != nil { - logger.Info("Error getting cctx by inTxHash: %s", err.Error()) + // prevent spamming logs + if i%10 == 0 { + logger.Info("Error getting cctx by inTxHash: %s", err.Error()) + } continue } if len(res.CrossChainTxs) < cctxsCount { - logger.Info( - "not enough cctxs found by inTxHash: %s, expected: %d, found: %d", - inTxHash, - cctxsCount, - len(res.CrossChainTxs), - ) + // prevent spamming logs + if i%10 == 0 { + logger.Info( + "not enough cctxs found by inTxHash: %s, expected: %d, found: %d", + inTxHash, + cctxsCount, + len(res.CrossChainTxs), + ) + } continue } cctxs := make([]*crosschaintypes.CrossChainTx, 0, len(res.CrossChainTxs)) allFound := true - for i, cctx := range res.CrossChainTxs { + for j, cctx := range res.CrossChainTxs { cctx := cctx if !IsTerminalStatus(cctx.CctxStatus.Status) { - logger.Info( - "waiting for cctx index %d to be mined by inTxHash: %s, cctx status: %s, message: %s", - i, - inTxHash, - cctx.CctxStatus.Status.String(), - cctx.CctxStatus.StatusMessage, - ) + // prevent spamming logs + if i%10 == 0 { + logger.Info( + "waiting for cctx index %d to be mined by inTxHash: %s, cctx status: %s, message: %s", + j, + inTxHash, + cctx.CctxStatus.Status.String(), + cctx.CctxStatus.StatusMessage, + ) + } allFound = false break } @@ -116,12 +125,16 @@ func WaitForBlockHeight( panic(err) } status := &coretypes.ResultStatus{} - for status.SyncInfo.LatestBlockHeight < height { + for i := 0; status.SyncInfo.LatestBlockHeight < height; i++ { status, err = rpc.Status(ctx) if err != nil { panic(err) } time.Sleep(1 * time.Second) - logger.Info("waiting for block: %d, current height: %d\n", height, status.SyncInfo.LatestBlockHeight) + + // prevent spamming logs + if i%10 == 0 { + logger.Info("waiting for block: %d, current height: %d\n", height, status.SyncInfo.LatestBlockHeight) + } } } diff --git a/contrib/localnet/scripts/gov-proposals-testing.sh b/contrib/localnet/scripts/gov-proposals-testing.sh index 6d7147536a..a984fa9250 100755 --- a/contrib/localnet/scripts/gov-proposals-testing.sh +++ b/contrib/localnet/scripts/gov-proposals-testing.sh @@ -7,17 +7,17 @@ WALLET_NAME=operator # Create a few short lived proposals for variety of testing zetacored tx gov submit-proposal proposals/proposal_for_failure.json --from $WALLET_NAME --keyring-backend test --chain-id athens_101-1 --fees 2000000000000000azeta --yes && sleep 12 -zetacored tx gov vote 1 VOTE_OPTION_NO --from $WALLET_NAME --keyring-backend test --chain-id athens_101-1 --fees 2000000000000000azeta --yes && sleep 12 +zetacored tx gov vote 1 VOTE_OPTION_NO --from $WALLET_NAME --keyring-backend test --chain-id athens_101-1 --fees 2000000000000000azeta --yes && sleep 12 zetacored tx gov submit-proposal proposals/proposal_for_success.json --from $WALLET_NAME --keyring-backend test --chain-id athens_101-1 --fees 2000000000000000azeta --yes && sleep 12 -zetacored tx gov vote 2 VOTE_OPTION_YES --from $WALLET_NAME --keyring-backend test --chain-id athens_101-1 --fees 2000000000000000azeta --yes && sleep 12 +zetacored tx gov vote 2 VOTE_OPTION_YES --from $WALLET_NAME --keyring-backend test --chain-id athens_101-1 --fees 2000000000000000azeta --yes && sleep 12 zetacored tx gov submit-proposal proposals/v100.0.0_proposal.json --from $WALLET_NAME --keyring-backend test --chain-id athens_101-1 --fees 2000000000000000azeta --yes && sleep 12 -zetacored tx gov vote 3 VOTE_OPTION_YES --from $WALLET_NAME --keyring-backend test --chain-id athens_101-1 --fees 2000000000000000azeta --yes && sleep 12 +zetacored tx gov vote 3 VOTE_OPTION_YES --from $WALLET_NAME --keyring-backend test --chain-id athens_101-1 --fees 2000000000000000azeta --yes && sleep 12 # Increase the length of the voting period to 1 week zetacored tx gov submit-proposal proposals/proposal_voting_period.json --from $WALLET_NAME --keyring-backend test --chain-id athens_101-1 --fees 2000000000000000azeta --yes && sleep 12 -zetacored tx gov vote 4 VOTE_OPTION_YES --from $WALLET_NAME --keyring-backend test --chain-id athens_101-1 --fees 2000000000000000azeta --yes && sleep 12 +zetacored tx gov vote 4 VOTE_OPTION_YES --from $WALLET_NAME --keyring-backend test --chain-id athens_101-1 --fees 2000000000000000azeta --yes && sleep 12 # Create a few long lived proposals for variety of testing @@ -25,14 +25,17 @@ echo "Sleeping for 3 minutes to allow the voting period to end and the voting pe sleep 180 zetacored tx gov submit-proposal proposals/proposal_voting_period.json --from $WALLET_NAME --keyring-backend test --chain-id athens_101-1 --fees 2000000000000000azeta --yes && sleep 12 -zetacored tx gov vote 5 VOTE_OPTION_YES --from $WALLET_NAME --keyring-backend test --chain-id athens_101-1 --fees 2000000000000000azeta --yes && sleep 12 +zetacored tx gov vote 5 VOTE_OPTION_YES --from $WALLET_NAME --keyring-backend test --chain-id athens_101-1 --fees 2000000000000000azeta --yes && sleep 12 zetacored tx gov submit-proposal proposals/v100.0.0_proposal.json --from $WALLET_NAME --keyring-backend test --chain-id athens_101-1 --fees 2000000000000000azeta --yes && sleep 12 -zetacored tx gov vote 6 VOTE_OPTION_YES --from $WALLET_NAME --keyring-backend test --chain-id athens_101-1 --fees 2000000000000000azeta --yes && sleep 12 +zetacored tx gov vote 6 VOTE_OPTION_YES --from $WALLET_NAME --keyring-backend test --chain-id athens_101-1 --fees 2000000000000000azeta --yes && sleep 12 zetacored tx gov submit-proposal proposals/proposal_for_deposit.json --from $WALLET_NAME --keyring-backend test --chain-id athens_101-1 --fees 2000000000000000azeta --yes && sleep 12 -zetacored tx gov vote 7 VOTE_OPTION_YES --from $WALLET_NAME --keyring-backend test --chain-id athens_101-1 --fees 2000000000000000azeta --yes && sleep 12 - - +zetacored tx gov vote 7 VOTE_OPTION_YES --from $WALLET_NAME --keyring-backend test --chain-id athens_101-1 --fees 2000000000000000azeta --yes && sleep 12 +# Consensus param test +#zetacored tx gov submit-legacy-proposal param-change proposals/proposal_for_consensus_params.json --from $WALLET_NAME --keyring-backend test --chain-id athens_101-1 --fees 2000000000000000azeta --yes && sleep 12 +#zetacored tx gov vote 1 VOTE_OPTION_YES --from $WALLET_NAME --keyring-backend test --chain-id athens_101-1 --fees 2000000000000000azeta --yes && sleep 12 +#zetacored tx gov submit-legacy-proposal param-change proposals/emissions_change.json --from $WALLET_NAME --keyring-backend test --chain-id athens_101-1 --gas 1000000 --yes && sleep 12 +#zetacored tx gov vote 1 VOTE_OPTION_YES --from $WALLET_NAME --keyring-backend test --chain-id athens_101-1 --fees 2000000000000000azeta --yes && sleep 12 diff --git a/contrib/localnet/scripts/password.file b/contrib/localnet/scripts/password.file new file mode 100644 index 0000000000..96b3814661 --- /dev/null +++ b/contrib/localnet/scripts/password.file @@ -0,0 +1,2 @@ +password +pass2 diff --git a/contrib/localnet/scripts/proposals/emissions_change.json b/contrib/localnet/scripts/proposals/emissions_change.json new file mode 100644 index 0000000000..5572e4e0a7 --- /dev/null +++ b/contrib/localnet/scripts/proposals/emissions_change.json @@ -0,0 +1,22 @@ +{ + "title": "Emissions Distribution", + "description": "Update emissions distribution", + "changes": [ + { + "subspace": "emissions", + "key": "ValidatorEmissionPercentage", + "value": "0.75" + }, + { + "subspace": "emissions", + "key": "ObserverEmissionPercentage", + "value": "0.125" + }, + { + "subspace": "emissions", + "key": "SignerEmissionPercentage", + "value": "0.125" + } + ], + "deposit": "10000000azeta" +} \ No newline at end of file diff --git a/contrib/localnet/scripts/proposals/proposal_for_consensus_params.json b/contrib/localnet/scripts/proposals/proposal_for_consensus_params.json new file mode 100644 index 0000000000..1e0aae79b5 --- /dev/null +++ b/contrib/localnet/scripts/proposals/proposal_for_consensus_params.json @@ -0,0 +1,15 @@ +{ + "title": "Change Max Gas Limit", + "description": "This proposal is to change the max gas limit per block.", + "changes": [ + { + "subspace": "baseapp", + "key": "BlockParams", + "value": { + "max_bytes": "22020096", + "max_gas": "500000000" + } + } + ], + "deposit": "100000000000000000azeta" +} \ No newline at end of file diff --git a/contrib/localnet/scripts/start-zetaclientd-background.sh b/contrib/localnet/scripts/start-zetaclientd-background.sh index 74ee897fdf..c41d13ff58 100644 --- a/contrib/localnet/scripts/start-zetaclientd-background.sh +++ b/contrib/localnet/scripts/start-zetaclientd-background.sh @@ -4,12 +4,19 @@ HOSTNAME=$(hostname) +# read HOTKEY_BACKEND env var for hotkey keyring backend and set default to test +BACKEND="test" +if [ "$HOTKEY_BACKEND" == "file" ]; then + BACKEND="file" +fi + + cp /root/preparams/PreParams_$HOSTNAME.json /root/preParams.json num=$(echo $HOSTNAME | tr -dc '0-9') node="zetacore$num" echo "Wait for zetacore to exchange genesis file" -sleep 30 +sleep 40 operator=$(cat $HOME/.zetacored/os.json | jq '.ObserverAddress' ) operatorAddress=$(echo "$operator" | tr -d '"') echo "operatorAddress: $operatorAddress" @@ -18,8 +25,8 @@ if [ $HOSTNAME == "zetaclient0" ] then rm ~/.tss/* MYIP=$(/sbin/ip -o -4 addr list eth0 | awk '{print $4}' | cut -d/ -f1) - zetaclientd init --zetacore-url zetacore0 --chain-id athens_101-1 --operator "$operatorAddress" --log-format=text --public-ip "$MYIP" - zetaclientd start > $HOME/zetaclient.log 2>&1 & + zetaclientd init --zetacore-url zetacore0 --chain-id athens_101-1 --operator "$operatorAddress" --log-format=text --public-ip "$MYIP" --keyring-backend "$BACKEND" + zetaclientd start < /root/password.file > $HOME/zetaclient.log 2>&1 & else num=$(echo $HOSTNAME | tr -dc '0-9') node="zetacore$num" @@ -30,8 +37,8 @@ else SEED=$(curl --retry 10 --retry-delay 5 --retry-connrefused -s zetaclient0:8123/p2p) done rm ~/.tss/* - zetaclientd init --peer /ip4/172.20.0.21/tcp/6668/p2p/"$SEED" --zetacore-url "$node" --chain-id athens_101-1 --operator "$operatorAddress" --log-format=text --public-ip "$MYIP" --log-level 0 - zetaclientd start > $HOME/zetaclient.log 2>&1 & + zetaclientd init --peer /ip4/172.20.0.21/tcp/6668/p2p/"$SEED" --zetacore-url "$node" --chain-id athens_101-1 --operator "$operatorAddress" --log-format=text --public-ip "$MYIP" --log-level 1 --keyring-backend "$BACKEND" + zetaclientd start < /root/password.file > $HOME/zetaclient.log 2>&1 & fi sleep 3 diff --git a/contrib/localnet/scripts/start-zetaclientd-genesis.sh b/contrib/localnet/scripts/start-zetaclientd-genesis.sh index 5f2cadceab..4716973fc8 100755 --- a/contrib/localnet/scripts/start-zetaclientd-genesis.sh +++ b/contrib/localnet/scripts/start-zetaclientd-genesis.sh @@ -25,7 +25,7 @@ then rm ~/.tss/* MYIP=$(/sbin/ip -o -4 addr list eth0 | awk '{print $4}' | cut -d/ -f1) zetaclientd init --zetacore-url zetacore0 --chain-id athens_101-1 --operator "$operatorAddress" --log-format=text --public-ip "$MYIP" --keyring-backend "$BACKEND" - zetaclientd start + zetaclientd start < /root/password.file else num=$(echo $HOSTNAME | tr -dc '0-9') node="zetacore$num" @@ -37,5 +37,5 @@ else done rm ~/.tss/* zetaclientd init --peer /ip4/172.20.0.21/tcp/6668/p2p/"$SEED" --zetacore-url "$node" --chain-id athens_101-1 --operator "$operatorAddress" --log-format=text --public-ip "$MYIP" --log-level 1 --keyring-backend "$BACKEND" - zetaclientd start + zetaclientd start < /root/password.file fi diff --git a/contrib/localnet/scripts/start-zetaclientd-p2p-diag.sh b/contrib/localnet/scripts/start-zetaclientd-p2p-diag.sh index a1fc3358f7..310e245878 100644 --- a/contrib/localnet/scripts/start-zetaclientd-p2p-diag.sh +++ b/contrib/localnet/scripts/start-zetaclientd-p2p-diag.sh @@ -17,7 +17,7 @@ then --pre-params ~/preParams.json --zetacore-url zetacore0 \ --chain-id athens_101-1 --dev --operator zeta1z46tdw75jvh4h39y3vu758ctv34rw5z9kmyhgz --log-level 0 --hotkey=val_grantee_observer \ --p2p-diagnostic - zetaclientd start + zetaclientd start < /root/password.file else num=$(echo $HOSTNAME | tr -dc '0-9') node="zetacore$num" @@ -29,5 +29,5 @@ else --pre-params ~/preParams.json --zetacore-url $node \ --chain-id athens_101-1 --dev --operator zeta1lz2fqwzjnk6qy48fgj753h48444fxtt7hekp52 --log-level 0 --hotkey=val_grantee_observer \ --p2p-diagnostic - zetaclientd start + zetaclientd start < /root/password.file fi diff --git a/go.mod b/go.mod index 8341c0bddf..95525856ce 100644 --- a/go.mod +++ b/go.mod @@ -338,6 +338,7 @@ replace ( // use cometbft github.com/tendermint/tendermint => github.com/cometbft/cometbft v0.34.28 github.com/tendermint/tm-db => github.com/BlockPILabs/cosmos-db v0.0.3 + github.com/zeta-chain/go-tss => github.com/zeta-chain/go-tss v0.1.1-0.20240115203400-a5b80e5da933 ) diff --git a/go.sum b/go.sum index be10cf47d8..5ebeddd613 100644 --- a/go.sum +++ b/go.sum @@ -1848,6 +1848,7 @@ github.com/ipfs/go-datastore v0.6.0/go.mod h1:rt5M3nNbSO/8q1t4LNkLyUwRs8HupMeN/8 github.com/ipfs/go-detect-race v0.0.1 h1:qX/xay2W3E4Q1U7d9lNs1sU9nvguX0a7319XbyQ6cOk= github.com/ipfs/go-detect-race v0.0.1/go.mod h1:8BNT7shDZPo99Q74BpGMK+4D8Mn4j46UU0LZ723meps= github.com/ipfs/go-ipfs-util v0.0.2 h1:59Sswnk1MFaiq+VcaknX7aYEyGyGDAA73ilhEK2POp8= +github.com/ipfs/go-ipfs-util v0.0.2/go.mod h1:CbPtkWJzjLdEcezDns2XYaehFVNXG9zrdrtMecczcsQ= github.com/ipfs/go-log v1.0.5 h1:2dOuUCB1Z7uoczMWgAyDck5JLb72zHzrMnGnCNNbvY8= github.com/ipfs/go-log v1.0.5/go.mod h1:j0b8ZoR+7+R99LD9jZ6+AJsrzkPbSXbZfGakb5JPtIo= github.com/ipfs/go-log/v2 v2.1.3/go.mod h1:/8d0SH3Su5Ooc31QlL1WysJhvyOTDCjcCZ9Axpmri6g= @@ -2760,6 +2761,7 @@ github.com/sourcegraph/go-diff v0.5.1/go.mod h1:j2dHj3m8aZgQO8lMTcTnBcXkRRRqi34c github.com/sourcegraph/go-diff v0.5.3/go.mod h1:v9JDtjCE4HHHCZGId75rg8gkKKa98RVjBcBGsVmMmak= github.com/sourcegraph/go-diff v0.6.1/go.mod h1:iBszgVvyxdc8SFZ7gm69go2KDdt3ag071iBaWPF6cjs= github.com/sourcegraph/syntaxhighlight v0.0.0-20170531221838-bd320f5d308e/go.mod h1:HuIsMU8RRBOtsCgI77wP899iHVBQpCmg4ErYMZB+2IA= +github.com/spacemonkeygo/spacelog v0.0.0-20180420211403-2296661a0572/go.mod h1:w0SWMsp6j9O/dk4/ZpIhL+3CkG8ofA2vuv7k+ltqUMc= github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI= github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= @@ -3035,6 +3037,8 @@ github.com/yvasiyarov/gorelic v0.0.0-20141212073537-a9bba5b9ab50/go.mod h1:NUSPS github.com/yvasiyarov/newrelic_platform_go v0.0.0-20140908184405-b21fdbd4370f/go.mod h1:GlGEuHIJweS1mbCqG+7vt2nvWLzLLnRHbXz5JKd/Qbg= github.com/zeta-chain/go-tss v0.1.1-0.20240103170132-35850edf5dbd h1:wv+VGLFX8IhPuoqAVQGAQjlEPWqYjowJgJVNReolJTM= github.com/zeta-chain/go-tss v0.1.1-0.20240103170132-35850edf5dbd/go.mod h1:+lJfk/qqt+oxXeVuJV+PzpUoxftUfoTRf2eF3qlbyFI= +github.com/zeta-chain/go-tss v0.1.1-0.20240115203400-a5b80e5da933 h1:cx6ZXVmV9LpkYRQER7+sTgu56wdmaU1U5VJcx3rsCwc= +github.com/zeta-chain/go-tss v0.1.1-0.20240115203400-a5b80e5da933/go.mod h1:+lJfk/qqt+oxXeVuJV+PzpUoxftUfoTRf2eF3qlbyFI= github.com/zeta-chain/keystone/keys v0.0.0-20231105174229-903bc9405da2 h1:gd2uE0X+ZbdFJ8DubxNqLbOVlCB12EgWdzSNRAR82tM= github.com/zeta-chain/keystone/keys v0.0.0-20231105174229-903bc9405da2/go.mod h1:x7Bkwbzt2W2lQfjOirnff0Dj+tykdbTG1FMJPVPZsvE= github.com/zeta-chain/protocol-contracts v1.0.2-athens3.0.20230816152528-db7d2bf9144b h1:aZRt5BtXdoDdyrUKwcv3B7mS30m/B854cjKjmnXBE5A= diff --git a/proto/crosschain/events.proto b/proto/crosschain/events.proto index 90fff428fb..6497fd147e 100644 --- a/proto/crosschain/events.proto +++ b/proto/crosschain/events.proto @@ -58,3 +58,9 @@ message EventOutboundSuccess { string new_status = 4; string value_received = 5; } + +message EventCCTXGasPriceIncreased { + string cctx_index = 1; + string gas_price_increase = 2; + string additional_fees = 3; +} diff --git a/server/config/config.go b/server/config/config.go index 0d2b433c71..20ddf48218 100644 --- a/server/config/config.go +++ b/server/config/config.go @@ -347,6 +347,7 @@ func GetConfig(v *viper.Viper) (Config, error) { BlockRangeCap: v.GetInt32("json-rpc.block-range-cap"), HTTPTimeout: v.GetDuration("json-rpc.http-timeout"), HTTPIdleTimeout: v.GetDuration("json-rpc.http-idle-timeout"), + AllowUnprotectedTxs: v.GetBool("json-rpc.allow-unprotected-txs"), MaxOpenConnections: v.GetInt("json-rpc.max-open-connections"), EnableIndexer: v.GetBool("json-rpc.enable-indexer"), MetricsAddress: v.GetString("json-rpc.metrics-address"), diff --git a/typescript/crosschain/events_pb.d.ts b/typescript/crosschain/events_pb.d.ts index 4bcfde46e6..285f476b30 100644 --- a/typescript/crosschain/events_pb.d.ts +++ b/typescript/crosschain/events_pb.d.ts @@ -291,3 +291,37 @@ export declare class EventOutboundSuccess extends Message static equals(a: EventOutboundSuccess | PlainMessage | undefined, b: EventOutboundSuccess | PlainMessage | undefined): boolean; } +/** + * @generated from message zetachain.zetacore.crosschain.EventCCTXGasPriceIncreased + */ +export declare class EventCCTXGasPriceIncreased extends Message { + /** + * @generated from field: string cctx_index = 1; + */ + cctxIndex: string; + + /** + * @generated from field: string gas_price_increase = 2; + */ + gasPriceIncrease: string; + + /** + * @generated from field: string additional_fees = 3; + */ + additionalFees: string; + + constructor(data?: PartialMessage); + + static readonly runtime: typeof proto3; + static readonly typeName = "zetachain.zetacore.crosschain.EventCCTXGasPriceIncreased"; + static readonly fields: FieldList; + + static fromBinary(bytes: Uint8Array, options?: Partial): EventCCTXGasPriceIncreased; + + static fromJson(jsonValue: JsonValue, options?: Partial): EventCCTXGasPriceIncreased; + + static fromJsonString(jsonString: string, options?: Partial): EventCCTXGasPriceIncreased; + + static equals(a: EventCCTXGasPriceIncreased | PlainMessage | undefined, b: EventCCTXGasPriceIncreased | PlainMessage | undefined): boolean; +} + diff --git a/x/crosschain/keeper/abci.go b/x/crosschain/keeper/abci.go index 78959be37c..dac6bcacc3 100644 --- a/x/crosschain/keeper/abci.go +++ b/x/crosschain/keeper/abci.go @@ -4,10 +4,11 @@ import ( "fmt" "time" + "github.com/zeta-chain/zetacore/common" + cosmoserrors "cosmossdk.io/errors" "cosmossdk.io/math" sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/zeta-chain/zetacore/common" "github.com/zeta-chain/zetacore/x/crosschain/types" observertypes "github.com/zeta-chain/zetacore/x/observer/types" ) @@ -17,8 +18,21 @@ const ( RemainingFeesToStabilityPoolPercent = 95 ) +// CheckAndUpdateCctxGasPriceFunc is a function type for checking and updating the gas price of a cctx +type CheckAndUpdateCctxGasPriceFunc func( + ctx sdk.Context, + k Keeper, + cctx types.CrossChainTx, + flags observertypes.GasPriceIncreaseFlags, +) (math.Uint, math.Uint, error) + // IterateAndUpdateCctxGasPrice iterates through all cctx and updates the gas price if pending for too long -func (k Keeper) IterateAndUpdateCctxGasPrice(ctx sdk.Context) error { +// The function returns the number of cctxs updated and the gas price increase flags used +func (k Keeper) IterateAndUpdateCctxGasPrice( + ctx sdk.Context, + chains []*common.Chain, + updateFunc CheckAndUpdateCctxGasPriceFunc, +) (int, observertypes.GasPriceIncreaseFlags) { // fetch the gas price increase flags or use default gasPriceIncreaseFlags := observertypes.DefaultGasPriceIncreaseFlags crosschainFlags, found := k.zetaObserverKeeper.GetCrosschainFlags(ctx) @@ -28,38 +42,66 @@ func (k Keeper) IterateAndUpdateCctxGasPrice(ctx sdk.Context) error { // skip if haven't reached epoch end if ctx.BlockHeight()%gasPriceIncreaseFlags.EpochLength != 0 { - return nil + return 0, gasPriceIncreaseFlags } - // iterate all chains' pending cctx - chains := common.DefaultChainsList() + cctxCount := 0 + +IterateChains: for _, chain := range chains { - res, err := k.CctxListPending(sdk.UnwrapSDKContext(ctx), &types.QueryListCctxPendingRequest{ - ChainId: chain.ChainId, - Limit: gasPriceIncreaseFlags.MaxPendingCctxs, - }) - if err != nil { - return err - } + // support only external evm chains + if common.IsEVMChain(chain.ChainId) && !common.IsZetaChain(chain.ChainId) { + res, err := k.CctxListPending(sdk.UnwrapSDKContext(ctx), &types.QueryListCctxPendingRequest{ + ChainId: chain.ChainId, + Limit: gasPriceIncreaseFlags.MaxPendingCctxs, + }) + if err != nil { + ctx.Logger().Info("GasStabilityPool: fetching pending cctx failed", + "chainID", chain.ChainId, + "err", err.Error(), + ) + continue IterateChains + } - // iterate through all pending cctx - for _, pendingCctx := range res.CrossChainTx { - if pendingCctx != nil { - _, _, err := k.CheckAndUpdateCctxGasPrice(ctx, *pendingCctx, gasPriceIncreaseFlags) - if err != nil { - return err + // iterate through all pending cctx + for _, pendingCctx := range res.CrossChainTx { + if pendingCctx != nil { + gasPriceIncrease, additionalFees, err := updateFunc(ctx, k, *pendingCctx, gasPriceIncreaseFlags) + if err != nil { + ctx.Logger().Info("GasStabilityPool: updating gas price for pending cctx failed", + "cctxIndex", pendingCctx.Index, + "err", err.Error(), + ) + continue IterateChains + } + if !gasPriceIncrease.IsNil() && !gasPriceIncrease.IsZero() { + // Emit typed event for gas price increase + if err := ctx.EventManager().EmitTypedEvent( + &types.EventCCTXGasPriceIncreased{ + CctxIndex: pendingCctx.Index, + GasPriceIncrease: gasPriceIncrease.String(), + AdditionalFees: additionalFees.String(), + }); err != nil { + ctx.Logger().Error( + "GasStabilityPool: failed to emit EventCCTXGasPriceIncreased", + "err", err.Error(), + ) + } + cctxCount++ + } } } } } - return nil + return cctxCount, gasPriceIncreaseFlags } // CheckAndUpdateCctxGasPrice checks if the retry interval is reached and updates the gas price if so // The function returns the gas price increase and the additional fees paid from the gas stability pool -func (k Keeper) CheckAndUpdateCctxGasPrice( +func CheckAndUpdateCctxGasPrice( ctx sdk.Context, + k Keeper, cctx types.CrossChainTx, flags observertypes.GasPriceIncreaseFlags, ) (math.Uint, math.Uint, error) { @@ -108,7 +150,7 @@ func (k Keeper) CheckAndUpdateCctxGasPrice( if err := k.fungibleKeeper.WithdrawFromGasStabilityPool(ctx, chainID, additionalFees.BigInt()); err != nil { return math.ZeroUint(), math.ZeroUint(), cosmoserrors.Wrap( types.ErrNotEnoughFunds, - fmt.Sprintf("cannot withdraw %s from gas stability pool", additionalFees.String()), + fmt.Sprintf("cannot withdraw %s from gas stability pool, error: %s", additionalFees.String(), err.Error()), ) } diff --git a/x/crosschain/keeper/abci_test.go b/x/crosschain/keeper/abci_test.go index 12263a3587..ea46e70dc4 100644 --- a/x/crosschain/keeper/abci_test.go +++ b/x/crosschain/keeper/abci_test.go @@ -6,13 +6,105 @@ import ( "time" "cosmossdk.io/math" + + sdk "github.com/cosmos/cosmos-sdk/types" "github.com/stretchr/testify/require" + "github.com/zeta-chain/zetacore/common" testkeeper "github.com/zeta-chain/zetacore/testutil/keeper" + "github.com/zeta-chain/zetacore/testutil/sample" + "github.com/zeta-chain/zetacore/x/crosschain/keeper" "github.com/zeta-chain/zetacore/x/crosschain/types" observertypes "github.com/zeta-chain/zetacore/x/observer/types" ) -func TestKeeper_CheckAndUpdateCctxGasPrice(t *testing.T) { +func TestKeeper_IterateAndUpdateCctxGasPrice(t *testing.T) { + k, ctx, _, zk := testkeeper.CrosschainKeeper(t) + + // updateFuncMap tracks the calls done with cctx index + updateFuncMap := make(map[string]struct{}) + + // failMap gives the cctx index that should fail + failMap := make(map[string]struct{}) + + // updateFunc mocks the update function and keep track of the calls done with cctx index + updateFunc := func( + ctx sdk.Context, + k keeper.Keeper, + cctx types.CrossChainTx, + flags observertypes.GasPriceIncreaseFlags, + ) (math.Uint, math.Uint, error) { + if _, ok := failMap[cctx.Index]; ok { + return math.NewUint(0), math.NewUint(0), errors.New("failed") + } + + updateFuncMap[cctx.Index] = struct{}{} + return math.NewUint(10), math.NewUint(10), nil + } + + // add some evm and non-evm chains + supportedChains := []*common.Chain{ + {ChainId: common.EthChain().ChainId}, + {ChainId: common.BtcMainnetChain().ChainId}, + {ChainId: common.BscMainnetChain().ChainId}, + {ChainId: common.ZetaChainMainnet().ChainId}, + } + + // set pending cctx + tss := sample.Tss() + zk.ObserverKeeper.SetTSS(ctx, tss) + createCctxWithNonceRange(t, ctx, *k, 10, 15, common.EthChain().ChainId, tss, zk) + createCctxWithNonceRange(t, ctx, *k, 20, 25, common.BtcMainnetChain().ChainId, tss, zk) + createCctxWithNonceRange(t, ctx, *k, 30, 35, common.BscMainnetChain().ChainId, tss, zk) + createCctxWithNonceRange(t, ctx, *k, 40, 45, common.ZetaChainMainnet().ChainId, tss, zk) + + // set a cctx where the update function should fail to test that the next cctx are not updated but the next chains are + failMap["1-12"] = struct{}{} + + // test that the default crosschain flags are used when not set and the epoch length is not reached + ctx = ctx.WithBlockHeight(observertypes.DefaultCrosschainFlags().GasPriceIncreaseFlags.EpochLength + 1) + + cctxCount, flags := k.IterateAndUpdateCctxGasPrice(ctx, supportedChains, updateFunc) + require.Equal(t, 0, cctxCount) + require.Equal(t, *observertypes.DefaultCrosschainFlags().GasPriceIncreaseFlags, flags) + + // test that custom crosschain flags are used when set and the epoch length is reached + customFlags := observertypes.GasPriceIncreaseFlags{ + EpochLength: 100, + RetryInterval: time.Minute * 10, + GasPriceIncreasePercent: 100, + GasPriceIncreaseMax: 200, + MaxPendingCctxs: 10, + } + crosschainFlags := sample.CrosschainFlags() + crosschainFlags.GasPriceIncreaseFlags = &customFlags + zk.ObserverKeeper.SetCrosschainFlags(ctx, *crosschainFlags) + + cctxCount, flags = k.IterateAndUpdateCctxGasPrice(ctx, supportedChains, updateFunc) + require.Equal(t, 0, cctxCount) + require.Equal(t, customFlags, flags) + + // test that cctx are iterated and updated when the epoch length is reached + + ctx = ctx.WithBlockHeight(observertypes.DefaultCrosschainFlags().GasPriceIncreaseFlags.EpochLength * 2) + cctxCount, flags = k.IterateAndUpdateCctxGasPrice(ctx, supportedChains, updateFunc) + + // 2 eth + 5 bsc = 7 + require.Equal(t, 7, cctxCount) + require.Equal(t, customFlags, flags) + + // check that the update function was called with the cctx index + require.Equal(t, 7, len(updateFuncMap)) + require.Contains(t, updateFuncMap, "1-10") + require.Contains(t, updateFuncMap, "1-11") + + require.Contains(t, updateFuncMap, "56-30") + require.Contains(t, updateFuncMap, "56-31") + require.Contains(t, updateFuncMap, "56-32") + require.Contains(t, updateFuncMap, "56-33") + require.Contains(t, updateFuncMap, "56-34") +} + +func TestCheckAndUpdateCctxGasPrice(t *testing.T) { sampleTimestamp := time.Now() retryIntervalReached := sampleTimestamp.Add(observertypes.DefaultGasPriceIncreaseFlags.RetryInterval + time.Second) retryIntervalNotReached := sampleTimestamp.Add(observertypes.DefaultGasPriceIncreaseFlags.RetryInterval - time.Second) @@ -282,7 +374,7 @@ func TestKeeper_CheckAndUpdateCctxGasPrice(t *testing.T) { } // check and update gas price - gasPriceIncrease, feesPaid, err := k.CheckAndUpdateCctxGasPrice(ctx, tc.cctx, tc.flags) + gasPriceIncrease, feesPaid, err := keeper.CheckAndUpdateCctxGasPrice(ctx, *k, tc.cctx, tc.flags) if tc.isError { require.Error(t, err) diff --git a/x/crosschain/keeper/grpc_query_cctx_test.go b/x/crosschain/keeper/grpc_query_cctx_test.go index cd5d1e3c14..c326f41a42 100644 --- a/x/crosschain/keeper/grpc_query_cctx_test.go +++ b/x/crosschain/keeper/grpc_query_cctx_test.go @@ -22,14 +22,14 @@ func createCctxWithNonceRange( t *testing.T, ctx sdk.Context, k keeper.Keeper, - low int, - high int, + lowPending int, + highPending int, chainID int64, tss observertypes.TSS, zk keepertest.ZetaKeepers, ) (cctxs []*types.CrossChainTx) { - for i := 0; i < low; i++ { - cctx := sample.CrossChainTx(t, fmt.Sprintf("%d", i)) + for i := 0; i < lowPending; i++ { + cctx := sample.CrossChainTx(t, fmt.Sprintf("%d-%d", chainID, i)) cctx.CctxStatus.Status = types.CctxStatus_OutboundMined cctx.InboundTxParams.SenderChainId = chainID k.SetCrossChainTx(ctx, *cctx) @@ -40,8 +40,8 @@ func createCctxWithNonceRange( Tss: tss.TssPubkey, }) } - for i := low; i < high; i++ { - cctx := sample.CrossChainTx(t, fmt.Sprintf("%d", i)) + for i := lowPending; i < highPending; i++ { + cctx := sample.CrossChainTx(t, fmt.Sprintf("%d-%d", chainID, i)) cctx.CctxStatus.Status = types.CctxStatus_PendingOutbound cctx.InboundTxParams.SenderChainId = chainID k.SetCrossChainTx(ctx, *cctx) @@ -55,8 +55,8 @@ func createCctxWithNonceRange( } zk.ObserverKeeper.SetPendingNonces(ctx, observertypes.PendingNonces{ ChainId: chainID, - NonceLow: int64(low), - NonceHigh: int64(high), + NonceLow: int64(lowPending), + NonceHigh: int64(highPending), Tss: tss.TssPubkey, }) @@ -135,12 +135,12 @@ func TestKeeper_CctxListPending(t *testing.T) { cctxs := createCctxWithNonceRange(t, ctx, *k, 1000, 2000, chainID, tss, zk) // set some cctxs as pending below nonce - cctx1, found := k.GetCrossChainTx(ctx, "940") + cctx1, found := k.GetCrossChainTx(ctx, "1337-940") require.True(t, found) cctx1.CctxStatus.Status = types.CctxStatus_PendingOutbound k.SetCrossChainTx(ctx, cctx1) - cctx2, found := k.GetCrossChainTx(ctx, "955") + cctx2, found := k.GetCrossChainTx(ctx, "1337-955") require.True(t, found) cctx2.CctxStatus.Status = types.CctxStatus_PendingOutbound k.SetCrossChainTx(ctx, cctx2) diff --git a/x/crosschain/keeper/msg_server_add_to_outtx_tracker_test.go b/x/crosschain/keeper/msg_server_add_to_outtx_tracker_test.go index 72820fcffc..c2ad9d6e7a 100644 --- a/x/crosschain/keeper/msg_server_add_to_outtx_tracker_test.go +++ b/x/crosschain/keeper/msg_server_add_to_outtx_tracker_test.go @@ -1,6 +1,3 @@ -//go:build TESTNET -// +build TESTNET - package keeper_test import ( @@ -283,7 +280,7 @@ func setupTssAndNonceToCctx(k *keeper.Keeper, ctx sdk.Context, chainId, nonce in k.GetObserverKeeper().SetTSS(ctx, observerTypes.TSS{ TssPubkey: tssPubKey, }) - k.SetPendingNonces(ctx, types.PendingNonces{ + k.GetObserverKeeper().SetPendingNonces(ctx, observertypes.PendingNonces{ Tss: tssPubKey, NonceLow: 0, NonceHigh: 1, @@ -297,7 +294,7 @@ func setupTssAndNonceToCctx(k *keeper.Keeper, ctx sdk.Context, chainId, nonce in }, } k.SetCrossChainTx(ctx, cctx) - k.SetNonceToCctx(ctx, types.NonceToCctx{ + k.GetObserverKeeper().SetNonceToCctx(ctx, observertypes.NonceToCctx{ ChainId: chainId, Nonce: nonce, CctxIndex: "0x123", diff --git a/x/crosschain/module.go b/x/crosschain/module.go index 9e2e4ec81b..421f92c895 100644 --- a/x/crosschain/module.go +++ b/x/crosschain/module.go @@ -188,10 +188,12 @@ func (AppModule) ConsensusVersion() uint64 { return 4 } // BeginBlock executes all ABCI BeginBlock logic respective to the crosschain module. func (am AppModule) BeginBlock(ctx sdk.Context, _ abci.RequestBeginBlock) { - err := am.keeper.IterateAndUpdateCctxGasPrice(ctx) - if err != nil { - ctx.Logger().Info("Error iterating and updating pending cctx gas price", "err", err.Error()) - } + // get all supported chains + supportedChains := am.keeper.GetObserverKeeper().GetSupportedChains(ctx) + + // iterate and update gas price for cctx that are pending for too long + // error is logged in the function + am.keeper.IterateAndUpdateCctxGasPrice(ctx, supportedChains, keeper.CheckAndUpdateCctxGasPrice) } // EndBlock executes all ABCI EndBlock logic respective to the crosschain module. It diff --git a/x/crosschain/types/events.pb.go b/x/crosschain/types/events.pb.go index 3fbc7f3fa3..68c05a18f0 100644 --- a/x/crosschain/types/events.pb.go +++ b/x/crosschain/types/events.pb.go @@ -509,55 +509,120 @@ func (m *EventOutboundSuccess) GetValueReceived() string { return "" } +type EventCCTXGasPriceIncreased struct { + CctxIndex string `protobuf:"bytes,1,opt,name=cctx_index,json=cctxIndex,proto3" json:"cctx_index,omitempty"` + GasPriceIncrease string `protobuf:"bytes,2,opt,name=gas_price_increase,json=gasPriceIncrease,proto3" json:"gas_price_increase,omitempty"` + AdditionalFees string `protobuf:"bytes,3,opt,name=additional_fees,json=additionalFees,proto3" json:"additional_fees,omitempty"` +} + +func (m *EventCCTXGasPriceIncreased) Reset() { *m = EventCCTXGasPriceIncreased{} } +func (m *EventCCTXGasPriceIncreased) String() string { return proto.CompactTextString(m) } +func (*EventCCTXGasPriceIncreased) ProtoMessage() {} +func (*EventCCTXGasPriceIncreased) Descriptor() ([]byte, []int) { + return fileDescriptor_7398db8b12b87b9e, []int{5} +} +func (m *EventCCTXGasPriceIncreased) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *EventCCTXGasPriceIncreased) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_EventCCTXGasPriceIncreased.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *EventCCTXGasPriceIncreased) XXX_Merge(src proto.Message) { + xxx_messageInfo_EventCCTXGasPriceIncreased.Merge(m, src) +} +func (m *EventCCTXGasPriceIncreased) XXX_Size() int { + return m.Size() +} +func (m *EventCCTXGasPriceIncreased) XXX_DiscardUnknown() { + xxx_messageInfo_EventCCTXGasPriceIncreased.DiscardUnknown(m) +} + +var xxx_messageInfo_EventCCTXGasPriceIncreased proto.InternalMessageInfo + +func (m *EventCCTXGasPriceIncreased) GetCctxIndex() string { + if m != nil { + return m.CctxIndex + } + return "" +} + +func (m *EventCCTXGasPriceIncreased) GetGasPriceIncrease() string { + if m != nil { + return m.GasPriceIncrease + } + return "" +} + +func (m *EventCCTXGasPriceIncreased) GetAdditionalFees() string { + if m != nil { + return m.AdditionalFees + } + return "" +} + func init() { proto.RegisterType((*EventInboundFinalized)(nil), "zetachain.zetacore.crosschain.EventInboundFinalized") proto.RegisterType((*EventZrcWithdrawCreated)(nil), "zetachain.zetacore.crosschain.EventZrcWithdrawCreated") proto.RegisterType((*EventZetaWithdrawCreated)(nil), "zetachain.zetacore.crosschain.EventZetaWithdrawCreated") proto.RegisterType((*EventOutboundFailure)(nil), "zetachain.zetacore.crosschain.EventOutboundFailure") proto.RegisterType((*EventOutboundSuccess)(nil), "zetachain.zetacore.crosschain.EventOutboundSuccess") + proto.RegisterType((*EventCCTXGasPriceIncreased)(nil), "zetachain.zetacore.crosschain.EventCCTXGasPriceIncreased") } func init() { proto.RegisterFile("crosschain/events.proto", fileDescriptor_7398db8b12b87b9e) } var fileDescriptor_7398db8b12b87b9e = []byte{ - // 586 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xd4, 0x54, 0xdd, 0x6a, 0xd4, 0x40, - 0x14, 0x6e, 0xda, 0xdd, 0xed, 0xee, 0xf4, 0x0f, 0x62, 0xb5, 0x63, 0xb1, 0xa1, 0x2e, 0xf8, 0x73, - 0xe3, 0x06, 0xf1, 0x0d, 0x5a, 0x94, 0x16, 0x91, 0x42, 0x5b, 0x11, 0x7a, 0x33, 0xcc, 0x26, 0x87, - 0x64, 0x30, 0x99, 0x59, 0x66, 0x26, 0xbb, 0xd9, 0x3e, 0x85, 0x2f, 0x22, 0xf8, 0x00, 0x3e, 0x80, - 0x97, 0xbd, 0xf0, 0xc2, 0x4b, 0xd9, 0x7d, 0x11, 0x99, 0x99, 0x44, 0xbb, 0xa9, 0xe8, 0x85, 0x28, - 0x78, 0x95, 0x73, 0xbe, 0x73, 0x72, 0xf2, 0xcd, 0xf7, 0x4d, 0x0e, 0xda, 0x89, 0xa4, 0x50, 0x2a, - 0x4a, 0x29, 0xe3, 0x21, 0x8c, 0x81, 0x6b, 0x35, 0x18, 0x49, 0xa1, 0x85, 0xbf, 0x77, 0x09, 0x9a, - 0x5a, 0x7c, 0x60, 0x23, 0x21, 0x61, 0xf0, 0xa3, 0x77, 0xf7, 0x56, 0x24, 0xf2, 0x5c, 0xf0, 0xd0, - 0x3d, 0xdc, 0x3b, 0xbb, 0xdb, 0x89, 0x48, 0x84, 0x0d, 0x43, 0x13, 0x39, 0xb4, 0xff, 0x79, 0x05, - 0xdd, 0x7e, 0x6e, 0x46, 0x1f, 0xf3, 0xa1, 0x28, 0x78, 0xfc, 0x82, 0x71, 0x9a, 0xb1, 0x4b, 0x88, - 0xfd, 0x7d, 0xb4, 0x9e, 0xab, 0x84, 0xe8, 0xe9, 0x08, 0x48, 0x21, 0x33, 0xec, 0xed, 0x7b, 0x8f, - 0x7b, 0xa7, 0x28, 0x57, 0xc9, 0xf9, 0x74, 0x04, 0xaf, 0x65, 0xe6, 0xef, 0x21, 0x14, 0x45, 0xba, - 0x24, 0x8c, 0xc7, 0x50, 0xe2, 0x65, 0x5b, 0xef, 0x19, 0xe4, 0xd8, 0x00, 0xfe, 0x1d, 0xd4, 0x51, - 0xc0, 0x63, 0x90, 0x78, 0xc5, 0x96, 0xaa, 0xcc, 0xbf, 0x8b, 0xba, 0xba, 0x24, 0x42, 0x26, 0x8c, - 0xe3, 0x96, 0xad, 0xac, 0xea, 0xf2, 0xc4, 0xa4, 0xfe, 0x36, 0x6a, 0x53, 0xa5, 0x40, 0xe3, 0xb6, - 0xc5, 0x5d, 0xe2, 0xdf, 0x43, 0x88, 0x71, 0xa2, 0x4b, 0x92, 0x52, 0x95, 0xe2, 0x8e, 0x2d, 0x75, - 0x19, 0x3f, 0x2f, 0x8f, 0xa8, 0x4a, 0xfd, 0x87, 0x68, 0x8b, 0x71, 0x32, 0xcc, 0x44, 0xf4, 0x96, - 0xa4, 0xc0, 0x92, 0x54, 0xe3, 0x55, 0xdb, 0xb2, 0xc1, 0xf8, 0x81, 0x41, 0x8f, 0x2c, 0xe8, 0xef, - 0xa2, 0xae, 0x84, 0x08, 0xd8, 0x18, 0x24, 0xee, 0xba, 0x19, 0x75, 0xee, 0x3f, 0x40, 0x9b, 0x75, - 0x4c, 0xac, 0x84, 0xb8, 0xe7, 0x46, 0xd4, 0xe8, 0xa1, 0x01, 0xcd, 0x89, 0x68, 0x2e, 0x0a, 0xae, - 0x31, 0x72, 0x27, 0x72, 0x99, 0xff, 0x08, 0x6d, 0x49, 0xc8, 0xe8, 0x14, 0x62, 0x92, 0x83, 0x52, - 0x34, 0x01, 0xbc, 0x66, 0x1b, 0x36, 0x2b, 0xf8, 0x95, 0x43, 0x8d, 0x62, 0x1c, 0x26, 0x44, 0x69, - 0xaa, 0x0b, 0x85, 0xd7, 0x9d, 0x62, 0x1c, 0x26, 0x67, 0x16, 0x30, 0x34, 0x5c, 0xe9, 0xfb, 0x98, - 0x0d, 0x47, 0xc3, 0xa1, 0xf5, 0x94, 0xfb, 0x68, 0xdd, 0x49, 0x59, 0x71, 0xdd, 0xb4, 0x4d, 0x6b, - 0x0e, 0xb3, 0x4c, 0xfb, 0xef, 0x97, 0xd1, 0x8e, 0xb5, 0xf5, 0x42, 0x46, 0x6f, 0x98, 0x4e, 0x63, - 0x49, 0x27, 0x87, 0x12, 0xa8, 0xfe, 0x9b, 0xc6, 0x36, 0x79, 0xb5, 0x6e, 0xf0, 0x6a, 0x58, 0xd9, - 0x6e, 0x58, 0x79, 0xdd, 0xa2, 0xce, 0x6f, 0x2d, 0x5a, 0xfd, 0xb5, 0x45, 0xdd, 0x05, 0x8b, 0x16, - 0x95, 0xef, 0x35, 0x94, 0xef, 0x7f, 0xf0, 0x10, 0x76, 0x7a, 0x81, 0xa6, 0xff, 0x4c, 0xb0, 0x45, - 0x35, 0x5a, 0x0d, 0x35, 0x16, 0x29, 0xb7, 0x9b, 0x94, 0x3f, 0x7a, 0x68, 0xdb, 0x52, 0x3e, 0x29, - 0xb4, 0xfb, 0x75, 0x29, 0xcb, 0x0a, 0x09, 0x7f, 0x4e, 0x77, 0x0f, 0x21, 0x91, 0xc5, 0xf5, 0x87, - 0x1d, 0xe5, 0x9e, 0xc8, 0xe2, 0xea, 0x96, 0x2e, 0xf2, 0x6a, 0xfd, 0xe4, 0x12, 0x8f, 0x69, 0x56, - 0x00, 0xa9, 0x8c, 0x89, 0x2b, 0xea, 0x1b, 0x16, 0x3d, 0xad, 0xc0, 0x9b, 0xf4, 0xcf, 0x8a, 0x28, - 0x02, 0xa5, 0xfe, 0x0f, 0xfa, 0x07, 0x2f, 0x3f, 0xcd, 0x02, 0xef, 0x6a, 0x16, 0x78, 0x5f, 0x67, - 0x81, 0xf7, 0x6e, 0x1e, 0x2c, 0x5d, 0xcd, 0x83, 0xa5, 0x2f, 0xf3, 0x60, 0xe9, 0xe2, 0x69, 0xc2, - 0x74, 0x5a, 0x0c, 0x07, 0x91, 0xc8, 0x43, 0xb3, 0x9c, 0x9f, 0xb8, 0xfd, 0x5d, 0xef, 0xe9, 0xb0, - 0x0c, 0xaf, 0x6d, 0x75, 0x73, 0x4a, 0x35, 0xec, 0xd8, 0x5d, 0xfc, 0xec, 0x5b, 0x00, 0x00, 0x00, - 0xff, 0xff, 0xee, 0x0c, 0x96, 0x59, 0xf0, 0x05, 0x00, 0x00, + // 654 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xd4, 0x95, 0xcf, 0x6e, 0x13, 0x3b, + 0x14, 0xc6, 0x3b, 0x6d, 0x92, 0x26, 0x6e, 0x9b, 0x5e, 0xcd, 0xed, 0xbd, 0xf5, 0x8d, 0x6e, 0xa3, + 0x52, 0x89, 0x3f, 0x0b, 0x48, 0x84, 0x78, 0x83, 0x46, 0x94, 0x56, 0x08, 0x15, 0xb5, 0x45, 0xa0, + 0x6e, 0x2c, 0xc7, 0x73, 0x98, 0xb1, 0x98, 0xb1, 0x23, 0xdb, 0xd3, 0x4e, 0xfb, 0x14, 0x88, 0xf7, + 0x40, 0xe2, 0x01, 0x78, 0x00, 0x96, 0x5d, 0xb0, 0x60, 0x89, 0xda, 0x17, 0x41, 0xb6, 0x67, 0x68, + 0x33, 0x45, 0xb0, 0x40, 0x20, 0xb1, 0x8a, 0xcf, 0x77, 0xec, 0x93, 0x9f, 0xbf, 0xe3, 0xe4, 0xa0, + 0x55, 0xa6, 0xa4, 0xd6, 0x2c, 0xa1, 0x5c, 0x0c, 0xe1, 0x08, 0x84, 0xd1, 0x83, 0x89, 0x92, 0x46, + 0x86, 0x6b, 0xa7, 0x60, 0xa8, 0xd3, 0x07, 0x6e, 0x25, 0x15, 0x0c, 0x2e, 0xf7, 0xf6, 0xfe, 0x66, + 0x32, 0xcb, 0xa4, 0x18, 0xfa, 0x0f, 0x7f, 0xa6, 0xb7, 0x12, 0xcb, 0x58, 0xba, 0xe5, 0xd0, 0xae, + 0xbc, 0xba, 0xf1, 0x71, 0x0e, 0xfd, 0xf3, 0xd0, 0x96, 0xde, 0x11, 0x63, 0x99, 0x8b, 0x68, 0x8b, + 0x0b, 0x9a, 0xf2, 0x53, 0x88, 0xc2, 0x75, 0xb4, 0x98, 0xe9, 0x98, 0x98, 0x93, 0x09, 0x90, 0x5c, + 0xa5, 0x38, 0x58, 0x0f, 0xee, 0x74, 0xf6, 0x50, 0xa6, 0xe3, 0x83, 0x93, 0x09, 0x3c, 0x53, 0x69, + 0xb8, 0x86, 0x10, 0x63, 0xa6, 0x20, 0x5c, 0x44, 0x50, 0xe0, 0x59, 0x97, 0xef, 0x58, 0x65, 0xc7, + 0x0a, 0xe1, 0xbf, 0xa8, 0xa5, 0x41, 0x44, 0xa0, 0xf0, 0x9c, 0x4b, 0x95, 0x51, 0xf8, 0x1f, 0x6a, + 0x9b, 0x82, 0x48, 0x15, 0x73, 0x81, 0x1b, 0x2e, 0x33, 0x6f, 0x8a, 0x5d, 0x1b, 0x86, 0x2b, 0xa8, + 0x49, 0xb5, 0x06, 0x83, 0x9b, 0x4e, 0xf7, 0x41, 0xf8, 0x3f, 0x42, 0x5c, 0x10, 0x53, 0x90, 0x84, + 0xea, 0x04, 0xb7, 0x5c, 0xaa, 0xcd, 0xc5, 0x41, 0xb1, 0x4d, 0x75, 0x12, 0xde, 0x42, 0xcb, 0x5c, + 0x90, 0x71, 0x2a, 0xd9, 0x2b, 0x92, 0x00, 0x8f, 0x13, 0x83, 0xe7, 0xdd, 0x96, 0x25, 0x2e, 0x36, + 0xad, 0xba, 0xed, 0xc4, 0xb0, 0x87, 0xda, 0x0a, 0x18, 0xf0, 0x23, 0x50, 0xb8, 0xed, 0x6b, 0x54, + 0x71, 0x78, 0x13, 0x75, 0xab, 0x35, 0x71, 0x16, 0xe2, 0x8e, 0x2f, 0x51, 0xa9, 0x23, 0x2b, 0xda, + 0x1b, 0xd1, 0x4c, 0xe6, 0xc2, 0x60, 0xe4, 0x6f, 0xe4, 0xa3, 0xf0, 0x36, 0x5a, 0x56, 0x90, 0xd2, + 0x13, 0x88, 0x48, 0x06, 0x5a, 0xd3, 0x18, 0xf0, 0x82, 0xdb, 0xd0, 0x2d, 0xe5, 0x27, 0x5e, 0xb5, + 0x8e, 0x09, 0x38, 0x26, 0xda, 0x50, 0x93, 0x6b, 0xbc, 0xe8, 0x1d, 0x13, 0x70, 0xbc, 0xef, 0x04, + 0x8b, 0xe1, 0x53, 0x5f, 0xcb, 0x2c, 0x79, 0x0c, 0xaf, 0x56, 0x55, 0x6e, 0xa0, 0x45, 0x6f, 0x65, + 0xc9, 0xda, 0x75, 0x9b, 0x16, 0xbc, 0xe6, 0x48, 0x37, 0xde, 0xce, 0xa2, 0x55, 0xd7, 0xd6, 0x43, + 0xc5, 0x9e, 0x73, 0x93, 0x44, 0x8a, 0x1e, 0x8f, 0x14, 0x50, 0xf3, 0x2b, 0x1b, 0x5b, 0xe7, 0x6a, + 0x5c, 0xe3, 0xaa, 0xb5, 0xb2, 0x59, 0x6b, 0xe5, 0xd5, 0x16, 0xb5, 0x7e, 0xd8, 0xa2, 0xf9, 0xef, + 0xb7, 0xa8, 0x3d, 0xd5, 0xa2, 0x69, 0xe7, 0x3b, 0x35, 0xe7, 0x37, 0xde, 0x05, 0x08, 0x7b, 0xbf, + 0xc0, 0xd0, 0xdf, 0x66, 0xd8, 0xb4, 0x1b, 0x8d, 0x9a, 0x1b, 0xd3, 0xc8, 0xcd, 0x3a, 0xf2, 0xfb, + 0x00, 0xad, 0x38, 0xe4, 0xdd, 0xdc, 0xf8, 0x9f, 0x2e, 0xe5, 0x69, 0xae, 0xe0, 0xe7, 0x71, 0xd7, + 0x10, 0x92, 0x69, 0x54, 0x7d, 0xb1, 0x47, 0xee, 0xc8, 0x34, 0x2a, 0x5f, 0xe9, 0x34, 0x57, 0xe3, + 0x1b, 0x8f, 0xf8, 0x88, 0xa6, 0x39, 0x90, 0xb2, 0x31, 0x51, 0x89, 0xbe, 0xe4, 0xd4, 0xbd, 0x52, + 0xbc, 0x8e, 0xbf, 0x9f, 0x33, 0x06, 0x5a, 0xff, 0x21, 0xf8, 0x6f, 0x02, 0xd4, 0x73, 0xf8, 0xa3, + 0xd1, 0xc1, 0x8b, 0x47, 0x54, 0x3f, 0x55, 0x9c, 0xc1, 0x8e, 0x60, 0x0a, 0xa8, 0x86, 0xa8, 0x86, + 0x18, 0xd4, 0x11, 0xef, 0xa2, 0x30, 0xa6, 0x9a, 0x4c, 0xec, 0x21, 0xc2, 0xcb, 0x53, 0xe5, 0x4d, + 0xfe, 0x8a, 0x6b, 0xd5, 0xec, 0xdf, 0x0b, 0x8d, 0x22, 0x6e, 0xb8, 0x14, 0x34, 0x25, 0x2f, 0x01, + 0xaa, 0x5b, 0x75, 0x2f, 0xe5, 0x2d, 0x00, 0xbd, 0xf9, 0xf8, 0xc3, 0x79, 0x3f, 0x38, 0x3b, 0xef, + 0x07, 0x9f, 0xcf, 0xfb, 0xc1, 0xeb, 0x8b, 0xfe, 0xcc, 0xd9, 0x45, 0x7f, 0xe6, 0xd3, 0x45, 0x7f, + 0xe6, 0xf0, 0x7e, 0xcc, 0x4d, 0x92, 0x8f, 0x07, 0x4c, 0x66, 0x43, 0x3b, 0x31, 0xee, 0xf9, 0xa1, + 0x52, 0x0d, 0x8f, 0x61, 0x31, 0xbc, 0x32, 0x6a, 0xac, 0xf5, 0x7a, 0xdc, 0x72, 0x03, 0xe2, 0xc1, + 0x97, 0x00, 0x00, 0x00, 0xff, 0xff, 0x45, 0x66, 0xf0, 0x4d, 0x85, 0x06, 0x00, 0x00, } func (m *EventInboundFinalized) Marshal() (dAtA []byte, err error) { @@ -941,6 +1006,50 @@ func (m *EventOutboundSuccess) MarshalToSizedBuffer(dAtA []byte) (int, error) { return len(dAtA) - i, nil } +func (m *EventCCTXGasPriceIncreased) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *EventCCTXGasPriceIncreased) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *EventCCTXGasPriceIncreased) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.AdditionalFees) > 0 { + i -= len(m.AdditionalFees) + copy(dAtA[i:], m.AdditionalFees) + i = encodeVarintEvents(dAtA, i, uint64(len(m.AdditionalFees))) + i-- + dAtA[i] = 0x1a + } + if len(m.GasPriceIncrease) > 0 { + i -= len(m.GasPriceIncrease) + copy(dAtA[i:], m.GasPriceIncrease) + i = encodeVarintEvents(dAtA, i, uint64(len(m.GasPriceIncrease))) + i-- + dAtA[i] = 0x12 + } + if len(m.CctxIndex) > 0 { + i -= len(m.CctxIndex) + copy(dAtA[i:], m.CctxIndex) + i = encodeVarintEvents(dAtA, i, uint64(len(m.CctxIndex))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + func encodeVarintEvents(dAtA []byte, offset int, v uint64) int { offset -= sovEvents(v) base := offset @@ -1149,6 +1258,27 @@ func (m *EventOutboundSuccess) Size() (n int) { return n } +func (m *EventCCTXGasPriceIncreased) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.CctxIndex) + if l > 0 { + n += 1 + l + sovEvents(uint64(l)) + } + l = len(m.GasPriceIncrease) + if l > 0 { + n += 1 + l + sovEvents(uint64(l)) + } + l = len(m.AdditionalFees) + if l > 0 { + n += 1 + l + sovEvents(uint64(l)) + } + return n +} + func sovEvents(x uint64) (n int) { return (math_bits.Len64(x|1) + 6) / 7 } @@ -2621,6 +2751,152 @@ func (m *EventOutboundSuccess) Unmarshal(dAtA []byte) error { } return nil } +func (m *EventCCTXGasPriceIncreased) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: EventCCTXGasPriceIncreased: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: EventCCTXGasPriceIncreased: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field CctxIndex", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthEvents + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthEvents + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.CctxIndex = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field GasPriceIncrease", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthEvents + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthEvents + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.GasPriceIncrease = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field AdditionalFees", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthEvents + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthEvents + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.AdditionalFees = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipEvents(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthEvents + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} func skipEvents(dAtA []byte) (n int, err error) { l := len(dAtA) iNdEx := 0 diff --git a/x/emissions/keeper/params.go b/x/emissions/keeper/params.go index 2e93eb1cde..17d506bbca 100644 --- a/x/emissions/keeper/params.go +++ b/x/emissions/keeper/params.go @@ -6,8 +6,9 @@ import ( ) // GetParams get all parameters as types.Params -func (k Keeper) GetParams(_ sdk.Context) types.Params { - return types.NewParams() +func (k Keeper) GetParams(ctx sdk.Context) (params types.Params) { + k.paramstore.GetParamSet(ctx, ¶ms) + return } // SetParams set the params diff --git a/x/emissions/keeper/params_test.go b/x/emissions/keeper/params_test.go new file mode 100644 index 0000000000..159e948a55 --- /dev/null +++ b/x/emissions/keeper/params_test.go @@ -0,0 +1,254 @@ +package keeper_test + +import ( + "testing" + + sdkmath "cosmossdk.io/math" + "github.com/stretchr/testify/assert" + keepertest "github.com/zeta-chain/zetacore/testutil/keeper" + emissionstypes "github.com/zeta-chain/zetacore/x/emissions/types" +) + +func TestKeeper_GetParams(t *testing.T) { + tests := []struct { + name string + params emissionstypes.Params + isPanic string + }{ + { + name: "Successfully set params", + params: emissionstypes.Params{ + MaxBondFactor: "1.25", + MinBondFactor: "0.75", + AvgBlockTime: "6.00", + TargetBondRatio: "00.67", + ValidatorEmissionPercentage: "00.50", + ObserverEmissionPercentage: "00.25", + TssSignerEmissionPercentage: "00.25", + DurationFactorConstant: "0.001877876953694702", + ObserverSlashAmount: sdkmath.NewInt(100000000000000000), + }, + isPanic: "", + }, + { + name: "negative observer slashed amount", + params: emissionstypes.Params{ + MaxBondFactor: "1.25", + MinBondFactor: "0.75", + AvgBlockTime: "6.00", + TargetBondRatio: "00.67", + ValidatorEmissionPercentage: "00.50", + ObserverEmissionPercentage: "00.25", + TssSignerEmissionPercentage: "00.25", + DurationFactorConstant: "0.001877876953694702", + ObserverSlashAmount: sdkmath.NewInt(-10), + }, + isPanic: "slash amount cannot be less than 0", + }, + { + name: "MaxBondFactor too high", + params: emissionstypes.Params{ + MaxBondFactor: "1.35", + MinBondFactor: "0.85", + AvgBlockTime: "6.00", + TargetBondRatio: "00.67", + ValidatorEmissionPercentage: "00.50", + ObserverEmissionPercentage: "00.25", + TssSignerEmissionPercentage: "00.25", + DurationFactorConstant: "0.001877876953694702", + ObserverSlashAmount: sdkmath.NewInt(100000000000000000), + }, + isPanic: "max bond factor cannot be higher that 0.25", + }, + { + name: "MinBondFactor too low", + params: emissionstypes.Params{ + MaxBondFactor: "1.25", + MinBondFactor: "0.35", + AvgBlockTime: "6.00", + TargetBondRatio: "00.67", + ValidatorEmissionPercentage: "00.50", + ObserverEmissionPercentage: "00.25", + TssSignerEmissionPercentage: "00.25", + DurationFactorConstant: "0.001877876953694702", + ObserverSlashAmount: sdkmath.NewInt(100000000000000000), + }, + isPanic: "min bond factor cannot be lower that 0.75", + }, + { + name: "invalid block time", + params: emissionstypes.Params{ + MaxBondFactor: "1.25", + MinBondFactor: "0.75", + AvgBlockTime: "invalidTime", + TargetBondRatio: "00.67", + ValidatorEmissionPercentage: "00.50", + ObserverEmissionPercentage: "00.25", + TssSignerEmissionPercentage: "00.25", + DurationFactorConstant: "0.001877876953694702", + ObserverSlashAmount: sdkmath.NewInt(100000000000000000), + }, + isPanic: "invalid block time", + }, + { + name: "invalid block time less than 0", + params: emissionstypes.Params{ + MaxBondFactor: "1.25", + MinBondFactor: "0.75", + AvgBlockTime: "-2", + TargetBondRatio: "00.67", + ValidatorEmissionPercentage: "00.50", + ObserverEmissionPercentage: "00.25", + TssSignerEmissionPercentage: "00.25", + DurationFactorConstant: "0.001877876953694702", + ObserverSlashAmount: sdkmath.NewInt(100000000000000000), + }, + isPanic: "block time cannot be less than or equal to 0", + }, + { + name: "bond ratio too high", + params: emissionstypes.Params{ + MaxBondFactor: "1.25", + MinBondFactor: "0.75", + AvgBlockTime: "6.00", + TargetBondRatio: "2.67", + ValidatorEmissionPercentage: "00.50", + ObserverEmissionPercentage: "00.25", + TssSignerEmissionPercentage: "00.25", + DurationFactorConstant: "0.001877876953694702", + ObserverSlashAmount: sdkmath.NewInt(100000000000000000), + }, + isPanic: "target bond ratio cannot be more than 100 percent", + }, + { + name: "bond ratio too low", + params: emissionstypes.Params{ + MaxBondFactor: "1.25", + MinBondFactor: "0.75", + AvgBlockTime: "6.00", + TargetBondRatio: "-1.00", + ValidatorEmissionPercentage: "00.50", + ObserverEmissionPercentage: "00.25", + TssSignerEmissionPercentage: "00.25", + DurationFactorConstant: "0.001877876953694702", + ObserverSlashAmount: sdkmath.NewInt(100000000000000000), + }, + isPanic: "target bond ratio cannot be less than 0 percent", + }, + { + name: "validator emission percentage too high", + params: emissionstypes.Params{ + MaxBondFactor: "1.25", + MinBondFactor: "0.75", + AvgBlockTime: "6.00", + TargetBondRatio: "00.67", + ValidatorEmissionPercentage: "1.50", + ObserverEmissionPercentage: "00.25", + TssSignerEmissionPercentage: "00.25", + DurationFactorConstant: "0.001877876953694702", + ObserverSlashAmount: sdkmath.NewInt(100000000000000000), + }, + isPanic: "validator emission percentage cannot be more than 100 percent", + }, + { + name: "validator emission percentage too low", + params: emissionstypes.Params{ + MaxBondFactor: "1.25", + MinBondFactor: "0.75", + AvgBlockTime: "6.00", + TargetBondRatio: "00.67", + ValidatorEmissionPercentage: "-1.50", + ObserverEmissionPercentage: "00.25", + TssSignerEmissionPercentage: "00.25", + DurationFactorConstant: "0.001877876953694702", + ObserverSlashAmount: sdkmath.NewInt(100000000000000000), + }, + isPanic: "validator emission percentage cannot be less than 0 percent", + }, + { + name: "observer percentage too low", + params: emissionstypes.Params{ + MaxBondFactor: "1.25", + MinBondFactor: "0.75", + AvgBlockTime: "6.00", + TargetBondRatio: "00.67", + ValidatorEmissionPercentage: "00.50", + ObserverEmissionPercentage: "-00.25", + TssSignerEmissionPercentage: "00.25", + DurationFactorConstant: "0.001877876953694702", + ObserverSlashAmount: sdkmath.NewInt(100000000000000000), + }, + isPanic: "observer emission percentage cannot be less than 0 percent", + }, + { + name: "observer percentage too high", + params: emissionstypes.Params{ + MaxBondFactor: "1.25", + MinBondFactor: "0.75", + AvgBlockTime: "6.00", + TargetBondRatio: "00.67", + ValidatorEmissionPercentage: "00.50", + ObserverEmissionPercentage: "150.25", + TssSignerEmissionPercentage: "00.25", + DurationFactorConstant: "0.001877876953694702", + ObserverSlashAmount: sdkmath.NewInt(100000000000000000), + }, + isPanic: "observer emission percentage cannot be more than 100 percent", + }, + { + name: "tss signer percentage too high", + params: emissionstypes.Params{ + MaxBondFactor: "1.25", + MinBondFactor: "0.75", + AvgBlockTime: "6.00", + TargetBondRatio: "00.67", + ValidatorEmissionPercentage: "00.50", + ObserverEmissionPercentage: "00.25", + TssSignerEmissionPercentage: "102.22", + DurationFactorConstant: "0.001877876953694702", + ObserverSlashAmount: sdkmath.NewInt(100000000000000000), + }, + isPanic: "tss emission percentage cannot be more than 100 percent", + }, + { + name: "tss signer percentage too loo", + params: emissionstypes.Params{ + MaxBondFactor: "1.25", + MinBondFactor: "0.75", + AvgBlockTime: "6.00", + TargetBondRatio: "00.67", + ValidatorEmissionPercentage: "00.50", + ObserverEmissionPercentage: "00.25", + TssSignerEmissionPercentage: "-102.22", + DurationFactorConstant: "0.001877876953694702", + ObserverSlashAmount: sdkmath.NewInt(100000000000000000), + }, + isPanic: "tss emission percentage cannot be less than 0 percent", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + k, ctx := keepertest.EmissionsKeeper(t) + defaultParams := k.GetParams(ctx) + assertPanic(t, func() { + k.SetParams(ctx, tt.params) + }, tt.isPanic) + + if tt.isPanic != "" { + assert.Equal(t, defaultParams, k.GetParams(ctx)) + } else { + assert.Equal(t, tt.params, k.GetParams(ctx)) + } + }) + } +} + +func assertPanic(t *testing.T, f func(), errorLog string) { + defer func() { + r := recover() + if r != nil { + assert.Contains(t, r, errorLog) + } + }() + f() +} diff --git a/x/emissions/types/keys.go b/x/emissions/types/keys.go index 4be2d268b4..9f1fa705fc 100644 --- a/x/emissions/types/keys.go +++ b/x/emissions/types/keys.go @@ -45,6 +45,7 @@ const ( ParamObserverEmissionPercentage = "ObserverEmissionPercentage" ParamTssSignerEmissionPercentage = "SignerEmissionPercentage" ParamDurationFactorConstant = "DurationFactorConstant" + ParamObserverSlashAmount = "ObserverSlashAmount" ) var ( diff --git a/x/emissions/types/params.go b/x/emissions/types/params.go index a61c4ecfea..aa896f77e8 100644 --- a/x/emissions/types/params.go +++ b/x/emissions/types/params.go @@ -2,6 +2,7 @@ package types import ( "fmt" + "strconv" sdkmath "cosmossdk.io/math" sdk "github.com/cosmos/cosmos-sdk/types" @@ -49,10 +50,11 @@ func (p *Params) ParamSetPairs() paramtypes.ParamSetPairs { paramtypes.NewParamSetPair(KeyPrefix(ParamMinBondFactor), &p.MinBondFactor, validateMinBondFactor), paramtypes.NewParamSetPair(KeyPrefix(ParamAvgBlockTime), &p.AvgBlockTime, validateAvgBlockTime), paramtypes.NewParamSetPair(KeyPrefix(ParamTargetBondRatio), &p.TargetBondRatio, validateTargetBondRatio), - paramtypes.NewParamSetPair(KeyPrefix(ParamValidatorEmissionPercentage), &p.ValidatorEmissionPercentage, validateValidatorEmissonPercentage), - paramtypes.NewParamSetPair(KeyPrefix(ParamObserverEmissionPercentage), &p.ObserverEmissionPercentage, validateObserverEmissonPercentage), + paramtypes.NewParamSetPair(KeyPrefix(ParamValidatorEmissionPercentage), &p.ValidatorEmissionPercentage, validateValidatorEmissionPercentage), + paramtypes.NewParamSetPair(KeyPrefix(ParamObserverEmissionPercentage), &p.ObserverEmissionPercentage, validateObserverEmissionPercentage), paramtypes.NewParamSetPair(KeyPrefix(ParamTssSignerEmissionPercentage), &p.TssSignerEmissionPercentage, validateTssEmissonPercentage), paramtypes.NewParamSetPair(KeyPrefix(ParamDurationFactorConstant), &p.DurationFactorConstant, validateDurationFactorConstant), + paramtypes.NewParamSetPair(KeyPrefix(ParamObserverSlashAmount), &p.ObserverSlashAmount, validateObserverSlashAmount), } } @@ -70,6 +72,16 @@ func (p Params) String() string { return string(out) } +func validateObserverSlashAmount(i interface{}) error { + v, ok := i.(sdkmath.Int) + if !ok { + return fmt.Errorf("invalid parameter type: %T", i) + } + if v.LT(sdk.ZeroInt()) { + return fmt.Errorf("slash amount cannot be less than 0") + } + return nil +} func validateDurationFactorConstant(i interface{}) error { _, ok := i.(string) if !ok { @@ -95,9 +107,6 @@ func validateMinBondFactor(i interface{}) error { if !ok { return fmt.Errorf("invalid parameter type: %T", i) } - if !ok { - return fmt.Errorf("invalid parameter type: %T", i) - } decMaxBond := sdk.MustNewDecFromStr(v) if decMaxBond.LT(sdk.MustNewDecFromStr("0.75")) { return fmt.Errorf("min bond factor cannot be lower that 0.75") @@ -106,10 +115,17 @@ func validateMinBondFactor(i interface{}) error { } func validateAvgBlockTime(i interface{}) error { - _, ok := i.(string) + v, ok := i.(string) if !ok { return fmt.Errorf("invalid parameter type: %T", i) } + blocktime, err := strconv.ParseFloat(v, 64) + if err != nil { + return fmt.Errorf("invalid block time: %T", i) + } + if blocktime <= 0 { + return fmt.Errorf("block time cannot be less than or equal to 0") + } return nil } @@ -119,7 +135,7 @@ func validateTargetBondRatio(i interface{}) error { return fmt.Errorf("invalid parameter type: %T", i) } decMaxBond := sdk.MustNewDecFromStr(v) - if decMaxBond.GT(sdk.MustNewDecFromStr("100.00")) { + if decMaxBond.GT(sdk.OneDec()) { return fmt.Errorf("target bond ratio cannot be more than 100 percent") } if decMaxBond.LT(sdk.ZeroDec()) { @@ -128,13 +144,13 @@ func validateTargetBondRatio(i interface{}) error { return nil } -func validateValidatorEmissonPercentage(i interface{}) error { +func validateValidatorEmissionPercentage(i interface{}) error { v, ok := i.(string) if !ok { return fmt.Errorf("invalid parameter type: %T", i) } dec := sdk.MustNewDecFromStr(v) - if dec.GT(sdk.MustNewDecFromStr("100.00")) { + if dec.GT(sdk.OneDec()) { return fmt.Errorf("validator emission percentage cannot be more than 100 percent") } if dec.LT(sdk.ZeroDec()) { @@ -143,13 +159,13 @@ func validateValidatorEmissonPercentage(i interface{}) error { return nil } -func validateObserverEmissonPercentage(i interface{}) error { +func validateObserverEmissionPercentage(i interface{}) error { v, ok := i.(string) if !ok { return fmt.Errorf("invalid parameter type: %T", i) } dec := sdk.MustNewDecFromStr(v) - if dec.GT(sdk.MustNewDecFromStr("100.00")) { + if dec.GT(sdk.OneDec()) { return fmt.Errorf("observer emission percentage cannot be more than 100 percent") } if dec.LT(sdk.ZeroDec()) { @@ -164,7 +180,7 @@ func validateTssEmissonPercentage(i interface{}) error { return fmt.Errorf("invalid parameter type: %T", i) } dec := sdk.MustNewDecFromStr(v) - if dec.GT(sdk.MustNewDecFromStr("100.00")) { + if dec.GT(sdk.OneDec()) { return fmt.Errorf("tss emission percentage cannot be more than 100 percent") } if dec.LT(sdk.ZeroDec()) { diff --git a/zetaclient/broadcast.go b/zetaclient/broadcast.go index a40fc04743..b46a9c6c8d 100644 --- a/zetaclient/broadcast.go +++ b/zetaclient/broadcast.go @@ -142,10 +142,7 @@ func (b *ZetaCoreBridge) GetContext() (client.Context, error) { } // if password is needed, set it as input - password, err := b.keys.GetHotkeyPassword() - if err != nil { - return ctx, err - } + password := b.keys.GetHotkeyPassword() if password != "" { ctx = ctx.WithInput(strings.NewReader(fmt.Sprintf("%[1]s\n%[1]s\n", password))) } diff --git a/zetaclient/keys.go b/zetaclient/keys.go index 6eae47850f..77f424eac7 100644 --- a/zetaclient/keys.go +++ b/zetaclient/keys.go @@ -2,7 +2,6 @@ package zetaclient import ( "bytes" - "errors" "fmt" "io" "os" @@ -20,22 +19,21 @@ import ( "github.com/zeta-chain/zetacore/zetaclient/config" ) -// HotkeyPasswordEnvVar is the environment variable used to retrieve the password for the hotkey -const HotkeyPasswordEnvVar = "HOTKEY_PASSWORD" - // Keys manages all the keys used by zeta client type Keys struct { signerName string kb ckeys.Keyring OperatorAddress sdk.AccAddress + hotkeyPassword string } // NewKeysWithKeybase create a new instance of Keys -func NewKeysWithKeybase(kb ckeys.Keyring, granterAddress sdk.AccAddress, granteeName string) *Keys { +func NewKeysWithKeybase(kb ckeys.Keyring, granterAddress sdk.AccAddress, granteeName string, hotkeyPassword string) *Keys { return &Keys{ signerName: granteeName, kb: kb, OperatorAddress: granterAddress, + hotkeyPassword: hotkeyPassword, } } @@ -44,7 +42,7 @@ func GetGranteeKeyName(signerName string) string { } // GetKeyringKeybase return keyring and key info -func GetKeyringKeybase(cfg *config.Config) (ckeys.Keyring, string, error) { +func GetKeyringKeybase(cfg *config.Config, hotkeyPassword string) (ckeys.Keyring, string, error) { granteeName := cfg.AuthzHotkey chainHomeFolder := cfg.ZetaCoreHome logger := log.Logger.With().Str("module", "GetKeyringKeybase").Logger() @@ -55,13 +53,9 @@ func GetKeyringKeybase(cfg *config.Config) (ckeys.Keyring, string, error) { // read password from env if using keyring backend file buf := bytes.NewBufferString("") if cfg.KeyringBackend == config.KeyringBackendFile { - password, err := getHotkeyPassword() - if err != nil { - return nil, "", err - } - buf.WriteString(password) + buf.WriteString(hotkeyPassword) buf.WriteByte('\n') // the library used by keyring is using ReadLine , which expect a new line - buf.WriteString(password) + buf.WriteString(hotkeyPassword) buf.WriteByte('\n') } @@ -137,12 +131,7 @@ func (k *Keys) GetAddress() sdk.AccAddress { } // GetPrivateKey return the private key -func (k *Keys) GetPrivateKey() (cryptotypes.PrivKey, error) { - password, err := k.GetHotkeyPassword() - if err != nil { - return nil, err - } - +func (k *Keys) GetPrivateKey(password string) (cryptotypes.PrivKey, error) { signer := GetGranteeKeyName(k.signerName) privKeyArmor, err := k.kb.ExportPrivKeyArmor(signer, password) if err != nil { @@ -160,13 +149,13 @@ func (k *Keys) GetKeybase() ckeys.Keyring { return k.kb } -func (k *Keys) GetPubKeySet() (common.PubKeySet, error) { +func (k *Keys) GetPubKeySet(password string) (common.PubKeySet, error) { pubkeySet := common.PubKeySet{ Secp256k1: "", Ed25519: "", } - pK, err := k.GetPrivateKey() + pK, err := k.GetPrivateKey(password) if err != nil { return pubkeySet, err } @@ -185,25 +174,9 @@ func (k *Keys) GetPubKeySet() (common.PubKeySet, error) { // GetHotkeyPassword returns the password to be used // returns empty if no password is needed -func (k *Keys) GetHotkeyPassword() (string, error) { +func (k *Keys) GetHotkeyPassword() string { if k.GetKeybase().Backend() == ckeys.BackendFile { - return getHotkeyPassword() - } - return "", nil -} - -// getHotkeyPassword retrieves the HOTKEY_PASSWORD environment variable -// and returns an error if it's not defined or shorter than 8 characters. -func getHotkeyPassword() (string, error) { - password := os.Getenv(HotkeyPasswordEnvVar) - - if password == "" { - return "", errors.New("HOTKEY_PASSWORD environment variable is not defined, use --keyring-backend-test to use the test keyring") + return k.hotkeyPassword } - - if len(password) < 8 { - return "", errors.New("HOTKEY_PASSWORD should be at least 8 characters long") - } - - return password, nil + return "" } diff --git a/zetaclient/keys_test.go b/zetaclient/keys_test.go index a2bd2fdcd5..74bb9b970b 100644 --- a/zetaclient/keys_test.go +++ b/zetaclient/keys_test.go @@ -80,7 +80,7 @@ func (ks *KeysSuite) TestGetKeyringKeybase(c *C) { AuthzHotkey: "bob", ZetaCoreHome: "/Users/test/.zetacored/", } - _, _, err := GetKeyringKeybase(cfg) + _, _, err := GetKeyringKeybase(cfg, "") c.Assert(err, NotNil) } @@ -101,15 +101,15 @@ func (ks *KeysSuite) TestNewKeys(c *C) { ZetaCoreHome: folder, } - k, _, err := GetKeyringKeybase(cfg) + k, _, err := GetKeyringKeybase(cfg, "") c.Assert(err, IsNil) c.Assert(k, NotNil) granter := cosmos.AccAddress(crypto.AddressHash([]byte("granter"))) - ki := NewKeysWithKeybase(k, granter, signerNameForTest) + ki := NewKeysWithKeybase(k, granter, signerNameForTest, "") kInfo := ki.GetSignerInfo() c.Assert(kInfo, NotNil) //c.Assert(kInfo.G, Equals, signerNameForTest) - priKey, err := ki.GetPrivateKey() + priKey, err := ki.GetPrivateKey("") c.Assert(err, IsNil) c.Assert(priKey, NotNil) c.Assert(priKey.Bytes(), HasLen, 32) diff --git a/zetaclient/tss_signer.go b/zetaclient/tss_signer.go index 2bb59a5550..c286622d42 100644 --- a/zetaclient/tss_signer.go +++ b/zetaclient/tss_signer.go @@ -87,8 +87,10 @@ func NewTSS( tssHistoricalList []observertypes.TSS, metrics *metrics.Metrics, bitcoinChainID int64, + tssPassword string, + hotkeyPassword string, ) (*TSS, error) { - server, err := SetupTSSServer(peer, privkey, preParams, cfg) + server, err := SetupTSSServer(peer, privkey, preParams, cfg, tssPassword) if err != nil { return nil, fmt.Errorf("SetupTSSServer error: %w", err) } @@ -105,7 +107,7 @@ func NewTSS( if err != nil { return nil, err } - _, pubkeyInBech32, err := GetKeyringKeybase(cfg) + _, pubkeyInBech32, err := GetKeyringKeybase(cfg, hotkeyPassword) if err != nil { return nil, err } @@ -122,7 +124,7 @@ func NewTSS( return &newTss, nil } -func SetupTSSServer(peer p2p.AddrList, privkey tmcrypto.PrivKey, preParams *keygen.LocalPreParams, cfg *config.Config) (*tss.TssServer, error) { +func SetupTSSServer(peer p2p.AddrList, privkey tmcrypto.PrivKey, preParams *keygen.LocalPreParams, cfg *config.Config, tssPassword string) (*tss.TssServer, error) { bootstrapPeers := peer log.Info().Msgf("Peers AddrList %v", bootstrapPeers) @@ -156,6 +158,7 @@ func SetupTSSServer(peer p2p.AddrList, privkey tmcrypto.PrivKey, preParams *keyg }, preParams, // use pre-generated pre-params if non-nil IP, // for docker test + tssPassword, ) if err != nil { log.Error().Err(err).Msg("NewTSS error") diff --git a/zetaclient/tx.go b/zetaclient/tx.go index 491b76d133..f422a3e026 100644 --- a/zetaclient/tx.go +++ b/zetaclient/tx.go @@ -51,6 +51,8 @@ func (b *ZetaCoreBridge) WrapMessageWithAuthz(msg sdk.Msg) (sdk.Msg, AuthZSigner } func (b *ZetaCoreBridge) PostGasPrice(chain common.Chain, gasPrice uint64, supply string, blockNum uint64) (string, error) { + // double the gas price to avoid gas price spike + gasPrice = gasPrice * 2 signerAddress := b.keys.GetOperatorAddress().String() msg := types.NewMsgGasPriceVoter(signerAddress, chain.ChainId, gasPrice, supply, blockNum) diff --git a/zetaclient/zetacore_observer.go b/zetaclient/zetacore_observer.go index 6fae898ece..dbce742826 100644 --- a/zetaclient/zetacore_observer.go +++ b/zetaclient/zetacore_observer.go @@ -342,7 +342,7 @@ func (co *CoreObserver) scheduleCctxBTC( } if included || confirmed { co.logger.ZetaChainWatcher.Info().Msgf("scheduleCctxBTC: outtx %s already included; do not schedule keysign", outTxID) - return + continue } // stop if the nonce being processed is higher than the pending nonce