diff --git a/app/ante/ante.go b/app/ante/ante.go index 7cbd8adebd..8c4187bd36 100644 --- a/app/ante/ante.go +++ b/app/ante/ante.go @@ -172,6 +172,7 @@ func IsSystemTx(tx sdk.Tx, isAuthorizedSigner func(string) bool) bool { *crosschaintypes.MsgVoteOnObservedInboundTx, *crosschaintypes.MsgVoteOnObservedOutboundTx, *crosschaintypes.MsgAddToOutTxTracker, + *crosschaintypes.MsgAddToInTxTracker, *observertypes.MsgVoteBlockHeader, *observertypes.MsgVoteTSS, *observertypes.MsgAddBlameVote: diff --git a/app/ante/ante_test.go b/app/ante/ante_test.go index 790930d175..75220e14a7 100644 --- a/app/ante/ante_test.go +++ b/app/ante/ante_test.go @@ -36,6 +36,7 @@ func TestIsSystemTx(t *testing.T) { // *cctxtypes.MsgVoteOnObservedInboundTx, // *cctxtypes.MsgVoteOnObservedOutboundTx, // *cctxtypes.MsgAddToOutTxTracker, + // *cctxtypes.MsgAddToInTxTracker, // *observertypes.MsgVoteBlockHeader, // *observertypes.MsgVoteTSS, // *observertypes.MsgAddBlameVote: @@ -168,6 +169,24 @@ func TestIsSystemTx(t *testing.T) { true, }, + { + "MsgAddToInTxTracker", + buildTxFromMsg(&crosschaintypes.MsgAddToInTxTracker{ + Creator: sample.AccAddress(), + }), + isAuthorized, + + true, + }, + { + "MsgExec{MsgAddToInTxTracker}", + buildAuthzTxFromMsg(&crosschaintypes.MsgAddToInTxTracker{ + Creator: sample.AccAddress(), + }), + isAuthorized, + + true, + }, { "MsgVoteTSS", buildTxFromMsg(&observertypes.MsgVoteTSS{ diff --git a/changelog.md b/changelog.md index c707512165..da439f86e0 100644 --- a/changelog.md +++ b/changelog.md @@ -30,6 +30,7 @@ ### Tests * [2047](https://github.com/zeta-chain/node/pull/2047) - fix liquidity cap advanced test +* [2184](https://github.com/zeta-chain/node/pull/2184) - add tx priority checks to e2e tests ### Fixes diff --git a/cmd/zetae2e/local/config.go b/cmd/zetae2e/local/config.go new file mode 100644 index 0000000000..46a8953dc1 --- /dev/null +++ b/cmd/zetae2e/local/config.go @@ -0,0 +1,37 @@ +package local + +import ( + "path/filepath" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/spf13/cobra" + "github.com/zeta-chain/zetacore/app" + "github.com/zeta-chain/zetacore/e2e/config" +) + +// 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 + } + + configFile, err = filepath.Abs(configFile) + if err != nil { + return config.Config{}, err + } + + return config.ReadConfig(configFile) +} + +// setCosmosConfig set account prefix to zeta +func setCosmosConfig() { + cosmosConf := sdk.GetConfig() + cosmosConf.SetBech32PrefixForAccount(app.Bech32PrefixAccAddr, app.Bech32PrefixAccPub) + cosmosConf.Seal() +} diff --git a/cmd/zetae2e/local/local.go b/cmd/zetae2e/local/local.go index ba745377fa..8c05a903bd 100644 --- a/cmd/zetae2e/local/local.go +++ b/cmd/zetae2e/local/local.go @@ -14,6 +14,7 @@ import ( "github.com/zeta-chain/zetacore/e2e/runner" "github.com/zeta-chain/zetacore/e2e/utils" "github.com/zeta-chain/zetacore/pkg/chains" + crosschaintypes "github.com/zeta-chain/zetacore/x/crosschain/types" "golang.org/x/sync/errgroup" ) @@ -331,8 +332,23 @@ func localE2ETest(cmd *cobra.Command, _ []string) { eg.Go(miscTestRoutine(conf, deployerRunner, verbose, e2etests.TestMyTestName)) } + // while tests are executed, monitor blocks in parallel to check if system txs are on top and they have biggest priority + txPriorityErrCh := make(chan error, 1) + ctx, monitorPriorityCancel := context.WithCancel(context.Background()) + go monitorTxPriorityInBlocks(ctx, conf, txPriorityErrCh) + if err := eg.Wait(); err != nil { deployerRunner.CtxCancel() + monitorPriorityCancel() + logger.Print("❌ %v", err) + logger.Print("❌ e2e tests failed after %s", time.Since(testStartTime).String()) + os.Exit(1) + } + + // if all tests pass, cancel txs priority monitoring and check if tx priority is not correct in some blocks + logger.Print("⏳ e2e tests passed, checking tx priority") + monitorPriorityCancel() + if err := <-txPriorityErrCh; err != nil { logger.Print("❌ %v", err) logger.Print("❌ e2e tests failed after %s", time.Since(testStartTime).String()) os.Exit(1) @@ -353,3 +369,26 @@ func localE2ETest(cmd *cobra.Command, _ []string) { os.Exit(0) } + +// waitKeygenHeight waits for keygen height +func waitKeygenHeight( + ctx context.Context, + cctxClient crosschaintypes.QueryClient, + logger *runner.Logger, +) { + // wait for keygen to be completed + 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/cmd/zetae2e/local/monitor_priority_txs.go b/cmd/zetae2e/local/monitor_priority_txs.go new file mode 100644 index 0000000000..458be05eab --- /dev/null +++ b/cmd/zetae2e/local/monitor_priority_txs.go @@ -0,0 +1,113 @@ +package local + +import ( + "context" + "errors" + "strings" + "time" + + "github.com/cometbft/cometbft/abci/types" + rpchttp "github.com/cometbft/cometbft/rpc/client/http" + coretypes "github.com/cometbft/cometbft/rpc/core/types" + "github.com/zeta-chain/zetacore/e2e/config" +) + +// monitorTxPriorityInBlocks checks for transaction priorities in blocks and reports errors +func monitorTxPriorityInBlocks(ctx context.Context, conf config.Config, errCh chan error) { + rpcClient, err := rpchttp.New(conf.RPCs.ZetaCoreRPC, "/websocket") + if err != nil { + errCh <- err + return + } + + ticker := time.NewTicker(2 * time.Second) + defer ticker.Stop() + + for { + select { + case <-ctx.Done(): + errCh <- nil + case <-ticker.C: + processBlockTxs(ctx, rpcClient, errCh) + } + } +} + +// processBlockTxs fetches the latest block and evaluates transaction priorities +func processBlockTxs(ctx context.Context, rpc *rpchttp.HTTP, errCh chan error) { + block, err := rpc.Block(ctx, nil) + if err != nil { + errCh <- err + return + } + + nonSystemTxFound := false + for _, tx := range block.Block.Txs { + txResult, err := rpc.Tx(ctx, tx.Hash(), false) + if err != nil { + continue + } + processTx(txResult, &nonSystemTxFound, errCh) + } +} + +// processTx handles the processing of each transaction and its events +func processTx(txResult *coretypes.ResultTx, nonSystemTxFound *bool, errCh chan error) { + for _, event := range txResult.TxResult.Events { + for _, attr := range event.Attributes { + // skip attrs with empty value + if attr.Value == "\"\"" { + continue + } + + // skip internal events with msg_type_url key, because they are not representing sdk msgs + if strings.Contains(attr.Value, ".internal.") { + continue + } + switch attr.Key { + // if attr key is msg_type_url, check if it's one of system txs, otherwise mark it as non system tx + case "msg_type_url": + if isMsgTypeURLSystemTx(attr) { + // a non system tx has been found in the block before a system tx + if *nonSystemTxFound { + errCh <- errors.New("wrong tx priority, system tx not on top") + return + } + } else { + *nonSystemTxFound = true + } + // if attr key is action, check if tx is ethermint non system tx and if it is, mark it + case "action": + if isActionNonSystemTx(attr) { + *nonSystemTxFound = true + } + } + } + } +} + +func isMsgTypeURLSystemTx(attr types.EventAttribute) bool { + // type urls in attr.Value are in double quotes, so it needs to be formatted like this + systemTxsMsgTypeUrls := []string{ + "\"/zetachain.zetacore.crosschain.MsgVoteOnObservedOutboundTx\"", + "\"/zetachain.zetacore.crosschain.MsgVoteOnObservedInboundTx\"", + "\"/zetachain.zetacore.crosschain.MsgVoteGasPrice\"", + "\"/zetachain.zetacore.crosschain.MsgAddToOutTxTracker\"", + "\"/zetachain.zetacore.crosschain.MsgAddToInTxTracker\"", + "\"/zetachain.zetacore.observer.MsgVoteBlockHeader\"", + "\"/zetachain.zetacore.observer.MsgVoteTSS\"", + "\"/zetachain.zetacore.observer.MsgAddBlameVote\"", + } + + for _, url := range systemTxsMsgTypeUrls { + if url == attr.Value { + return true + } + } + + return false +} + +func isActionNonSystemTx(attr types.EventAttribute) bool { + return attr.Value == "/ethermint.evm.v1.MsgEthereumTx" +} diff --git a/cmd/zetae2e/local/performance.go b/cmd/zetae2e/local/performance.go index 0c15e799db..3f01d80b6c 100644 --- a/cmd/zetae2e/local/performance.go +++ b/cmd/zetae2e/local/performance.go @@ -59,7 +59,7 @@ func ethereumDepositPerformanceRoutine( } if err := r.RunE2ETests(tests); err != nil { - return fmt.Errorf("misc tests failed: %v", err) + return fmt.Errorf("ethereum deposit performance test failed: %v", err) } r.Logger.Print("🍾 Ethereum deposit performance test completed in %s", time.Since(startTime).String()) @@ -117,7 +117,7 @@ func ethereumWithdrawPerformanceRoutine( } if err := r.RunE2ETests(tests); err != nil { - return fmt.Errorf("misc tests failed: %v", err) + return fmt.Errorf("ethereum withdraw performance test failed: %v", err) } r.Logger.Print("🍾 Ethereum withdraw performance test completed in %s", time.Since(startTime).String()) diff --git a/cmd/zetae2e/local/test_runner.go b/cmd/zetae2e/local/test_runner.go new file mode 100644 index 0000000000..30d759a09f --- /dev/null +++ b/cmd/zetae2e/local/test_runner.go @@ -0,0 +1,43 @@ +package local + +import ( + ethcommon "github.com/ethereum/go-ethereum/common" + zetae2econfig "github.com/zeta-chain/zetacore/cmd/zetae2e/config" + "github.com/zeta-chain/zetacore/e2e/config" + "github.com/zeta-chain/zetacore/e2e/runner" + "github.com/zeta-chain/zetacore/e2e/utils" +) + +// initTestRunner initializes a runner form tests +// it creates a runner with an account and copy contracts from deployer runner +func initTestRunner( + name string, + conf config.Config, + deployerRunner *runner.E2ERunner, + userAddress ethcommon.Address, + userPrivKey string, + logger *runner.Logger, +) (*runner.E2ERunner, error) { + // initialize runner for test + testRunner, err := zetae2econfig.RunnerFromConfig( + deployerRunner.Ctx, + name, + deployerRunner.CtxCancel, + conf, + userAddress, + userPrivKey, + utils.FungibleAdminName, + FungibleAdminMnemonic, + 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 +} diff --git a/cmd/zetae2e/local/utils.go b/cmd/zetae2e/local/utils.go deleted file mode 100644 index f59782183c..0000000000 --- a/cmd/zetae2e/local/utils.go +++ /dev/null @@ -1,102 +0,0 @@ -package local - -import ( - "context" - - "path/filepath" - "time" - - sdk "github.com/cosmos/cosmos-sdk/types" - ethcommon "github.com/ethereum/go-ethereum/common" - "github.com/spf13/cobra" - "github.com/zeta-chain/zetacore/app" - zetae2econfig "github.com/zeta-chain/zetacore/cmd/zetae2e/config" - "github.com/zeta-chain/zetacore/e2e/config" - "github.com/zeta-chain/zetacore/e2e/runner" - "github.com/zeta-chain/zetacore/e2e/utils" - crosschaintypes "github.com/zeta-chain/zetacore/x/crosschain/types" -) - -// 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 - } - - configFile, err = filepath.Abs(configFile) - if err != nil { - return config.Config{}, err - } - - 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 form tests -// it creates a runner with an account and copy contracts from deployer runner -func initTestRunner( - name string, - conf config.Config, - deployerRunner *runner.E2ERunner, - userAddress ethcommon.Address, - userPrivKey string, - logger *runner.Logger, -) (*runner.E2ERunner, error) { - // initialize runner for test - testRunner, err := zetae2econfig.RunnerFromConfig( - deployerRunner.Ctx, - name, - deployerRunner.CtxCancel, - conf, - userAddress, - userPrivKey, - utils.FungibleAdminName, - FungibleAdminMnemonic, - 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 -} - -// waitKeygenHeight waits for keygen height -func waitKeygenHeight( - ctx context.Context, - cctxClient crosschaintypes.QueryClient, - logger *runner.Logger, -) { - // wait for keygen to be completed - 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) - } -}