From 54c7b224799ae7afe8634380c4b9f22bd7eba624 Mon Sep 17 00:00:00 2001 From: lumtis Date: Mon, 11 Mar 2024 09:39:48 +0100 Subject: [PATCH] add network report --- cmd/zetae2e/local/local.go | 13 +++ e2e/runner/e2etest.go | 105 +++++++++++++++++++ e2e/runner/report.go | 75 +++++++++++++- e2e/runner/run.go | 94 +++++++++++++++++ e2e/runner/runner.go | 200 +------------------------------------ e2e/runner/setup_zeta.go | 7 +- 6 files changed, 296 insertions(+), 198 deletions(-) create mode 100644 e2e/runner/e2etest.go create mode 100644 e2e/runner/run.go diff --git a/cmd/zetae2e/local/local.go b/cmd/zetae2e/local/local.go index b7aff01155..a1382b716e 100644 --- a/cmd/zetae2e/local/local.go +++ b/cmd/zetae2e/local/local.go @@ -224,4 +224,17 @@ func localE2ETest(cmd *cobra.Command, _ []string) { } logger.Print("✅ e2e tests completed in %s", time.Since(testStartTime).String()) + + // print and validate report + networkReport, err := deployerRunner.GenerateNetworkReport() + if err != nil { + logger.Print("❌ failed to generate network report %v", err) + } + deployerRunner.PrintNetworkReport(networkReport) + if err := networkReport.Validate(); err != nil { + logger.Print("❌ network report validation failed %v", err) + os.Exit(1) + } + + os.Exit(0) } diff --git a/e2e/runner/e2etest.go b/e2e/runner/e2etest.go new file mode 100644 index 0000000000..499d45144d --- /dev/null +++ b/e2e/runner/e2etest.go @@ -0,0 +1,105 @@ +package runner + +import "fmt" + +// E2ETestFunc is a function representing a E2E test +// It takes a E2ERunner as an argument +type E2ETestFunc func(*E2ERunner, []string) + +// E2ETest represents a E2E test with a name, args, description and test func +type E2ETest struct { + Name string + Description string + Args []string + ArgsDefinition []ArgDefinition + E2ETest E2ETestFunc +} + +// NewE2ETest creates a new instance of E2ETest with specified parameters. +func NewE2ETest(name, description string, argsDefinition []ArgDefinition, e2eTestFunc E2ETestFunc) E2ETest { + return E2ETest{ + Name: name, + Description: description, + ArgsDefinition: argsDefinition, + E2ETest: e2eTestFunc, + Args: []string{}, + } +} + +// ArgDefinition defines a structure for holding an argument's description along with it's default value. +type ArgDefinition struct { + Description string + DefaultValue string +} + +// DefaultArgs extracts and returns array of default arguments from the ArgsDefinition. +func (e E2ETest) DefaultArgs() []string { + defaultArgs := make([]string, len(e.ArgsDefinition)) + for i, spec := range e.ArgsDefinition { + defaultArgs[i] = spec.DefaultValue + } + return defaultArgs +} + +// ArgsDescription returns a string representing the arguments description in a readable format. +func (e E2ETest) ArgsDescription() string { + argsDescription := "" + for _, def := range e.ArgsDefinition { + argDesc := fmt.Sprintf("%s (%s)", def.Description, def.DefaultValue) + if argsDescription != "" { + argsDescription += ", " + } + argsDescription += argDesc + } + return argsDescription +} + +// E2ETestRunConfig defines the basic configuration for initiating an E2E test, including its name and optional runtime arguments. +type E2ETestRunConfig struct { + Name string + Args []string +} + +// GetE2ETestsToRunByName prepares a list of E2ETests to run based on given test names without arguments +func (runner *E2ERunner) GetE2ETestsToRunByName(availableTests []E2ETest, testNames ...string) ([]E2ETest, error) { + tests := []E2ETestRunConfig{} + for _, testName := range testNames { + tests = append(tests, E2ETestRunConfig{ + Name: testName, + Args: []string{}, + }) + } + return runner.GetE2ETestsToRunByConfig(availableTests, tests) +} + +// GetE2ETestsToRunByConfig prepares a list of E2ETests to run based on provided test names and their corresponding arguments +func (runner *E2ERunner) GetE2ETestsToRunByConfig(availableTests []E2ETest, testConfigs []E2ETestRunConfig) ([]E2ETest, error) { + tests := []E2ETest{} + for _, testSpec := range testConfigs { + e2eTest, found := findE2ETestByName(availableTests, testSpec.Name) + if !found { + return nil, fmt.Errorf("e2e test %s not found", testSpec.Name) + } + e2eTestToRun := NewE2ETest( + e2eTest.Name, + e2eTest.Description, + e2eTest.ArgsDefinition, + e2eTest.E2ETest, + ) + // update e2e test args + e2eTestToRun.Args = testSpec.Args + tests = append(tests, e2eTestToRun) + } + + return tests, nil +} + +// findE2ETest finds a e2e test by name +func findE2ETestByName(e2eTests []E2ETest, e2eTestName string) (E2ETest, bool) { + for _, test := range e2eTests { + if test.Name == e2eTestName { + return test, true + } + } + return E2ETest{}, false +} diff --git a/e2e/runner/report.go b/e2e/runner/report.go index a502c442dd..e81243f774 100644 --- a/e2e/runner/report.go +++ b/e2e/runner/report.go @@ -5,9 +5,16 @@ import ( "strings" "text/tabwriter" "time" + + sdkmath "cosmossdk.io/math" + banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" + "github.com/zeta-chain/zetacore/cmd/zetacored/config" + "github.com/zeta-chain/zetacore/e2e/txserver" + crosschaintypes "github.com/zeta-chain/zetacore/x/crosschain/types" ) -// TestReport is a struct that contains the test report +// TestReport is a struct that contains the report for a specific e2e test +// It can be generated with the RunE2ETestsIntoReport method type TestReport struct { Name string Success bool @@ -53,3 +60,69 @@ func (runner *E2ERunner) PrintTestReports(tr TestReports) { } runner.Logger.PrintNoPrefix(table) } + +// NetworkReport is a struct that contains the report for the network used after running e2e tests +// This report has been initialized to check the emissions pool balance and if the pool is decreasing +// TODO: add more complete data and validation to the network +type NetworkReport struct { + EmissionsPoolBalance sdkmath.Int + Height uint64 + CctxCount int +} + +// Validate validates the network report +// This method is used to validate the network after running e2e tests +// It checks the emissions pool balance and if the pool is decreasing +func (nr NetworkReport) Validate() error { + if nr.EmissionsPoolBalance.GTE(sdkmath.NewIntFromBigInt(EmissionsPoolFunding)) { + return fmt.Errorf( + "emissions pool balance is not decreasing, expected less than %s, got %s", + EmissionsPoolFunding, + nr.EmissionsPoolBalance, + ) + } + return nil +} + +// GenerateNetworkReport generates a report for the network used after running e2e tests +func (runner *E2ERunner) GenerateNetworkReport() (NetworkReport, error) { + // get the emissions pool balance + balanceRes, err := runner.BankClient.Balance(runner.Ctx, &banktypes.QueryBalanceRequest{ + Address: txserver.EmissionsPoolAddress, + Denom: config.BaseDenom, + }) + if err != nil { + return NetworkReport{}, err + } + emissionsPoolBalance := balanceRes.Balance + + // fetch the height and number of cctxs, this gives a better idea on the activity of the network + + // get the block height + blockRes, err := runner.ZevmClient.BlockNumber(runner.Ctx) + if err != nil { + return NetworkReport{}, err + } + + // get the number of cctxs + cctxsRes, err := runner.CctxClient.CctxAll(runner.Ctx, &crosschaintypes.QueryAllCctxRequest{}) + if err != nil { + return NetworkReport{}, err + } + cctxCount := len(cctxsRes.CrossChainTx) + + return NetworkReport{ + EmissionsPoolBalance: emissionsPoolBalance.Amount, + Height: blockRes, + CctxCount: cctxCount, + }, nil +} + +// PrintNetworkReport prints the network report +func (runner *E2ERunner) PrintNetworkReport(nr NetworkReport) { + runner.Logger.Print(" ---📈 Network Report ---") + runner.Logger.Print("Block Height: %d", nr.Height) + runner.Logger.Print("CCTX Processed: %d", nr.CctxCount) + runner.Logger.Print("Emissions Pool Balance: %sZETA", nr.EmissionsPoolBalance.Quo(sdkmath.NewIntFromUint64(1e18))) + +} diff --git a/e2e/runner/run.go b/e2e/runner/run.go new file mode 100644 index 0000000000..f24ff9d9f8 --- /dev/null +++ b/e2e/runner/run.go @@ -0,0 +1,94 @@ +package runner + +import ( + "fmt" + "runtime" + "time" +) + +// RunE2ETests runs a list of e2e tests +func (runner *E2ERunner) RunE2ETests(e2eTests []E2ETest) (err error) { + for _, e2eTest := range e2eTests { + if err := runner.RunE2ETest(e2eTest, true); err != nil { + return err + } + } + return nil +} + +// RunE2ETest runs a e2e test +func (runner *E2ERunner) RunE2ETest(e2eTest E2ETest, checkAccounting bool) (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("%s failed: %v, stack trace %s", e2eTest.Name, r, stack[:n]) + } + }() + + startTime := time.Now() + runner.Logger.Print("⏳running - %s", e2eTest.Description) + + // run e2e test, if args are not provided, use default args + args := e2eTest.Args + if len(args) == 0 { + args = e2eTest.DefaultArgs() + } + e2eTest.E2ETest(runner, args) + + //check supplies + if checkAccounting { + if err := runner.CheckZRC20ReserveAndSupply(); err != nil { + return err + } + } + + runner.Logger.Print("✅ completed in %s - %s", time.Since(startTime), e2eTest.Description) + + return err +} + +// RunE2ETestsIntoReport runs a list of e2e tests by name in a list of e2e tests and returns a report +// The function doesn't return an error, it returns a report with the error +func (runner *E2ERunner) RunE2ETestsIntoReport(e2eTests []E2ETest) (TestReports, error) { + // go through all tests + reports := make(TestReports, 0, len(e2eTests)) + for _, test := range e2eTests { + // get info before test + balancesBefore, err := runner.GetAccountBalances(true) + if err != nil { + return nil, err + } + timeBefore := time.Now() + + // run test + testErr := runner.RunE2ETest(test, false) + if testErr != nil { + runner.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 := runner.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 +} diff --git a/e2e/runner/runner.go b/e2e/runner/runner.go index 26339acccc..28bfc90d09 100644 --- a/e2e/runner/runner.go +++ b/e2e/runner/runner.go @@ -2,16 +2,9 @@ package runner import ( "context" - "fmt" - "runtime" "sync" "time" - "github.com/zeta-chain/zetacore/e2e/contracts/contextapp" - "github.com/zeta-chain/zetacore/e2e/contracts/erc20" - "github.com/zeta-chain/zetacore/e2e/contracts/zevmswap" - "github.com/zeta-chain/zetacore/e2e/txserver" - "github.com/btcsuite/btcd/chaincfg" "github.com/btcsuite/btcd/rpcclient" "github.com/btcsuite/btcutil" @@ -29,6 +22,10 @@ import ( "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" + "github.com/zeta-chain/zetacore/e2e/contracts/contextapp" + "github.com/zeta-chain/zetacore/e2e/contracts/erc20" + "github.com/zeta-chain/zetacore/e2e/contracts/zevmswap" + "github.com/zeta-chain/zetacore/e2e/txserver" 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" @@ -159,195 +156,6 @@ func NewE2ERunner( } } -// E2ETestFunc is a function representing a E2E test -// It takes a E2ERunner as an argument -type E2ETestFunc func(*E2ERunner, []string) - -// E2ETest represents a E2E test with a name, args, description and test func -type E2ETest struct { - Name string - Description string - Args []string - ArgsDefinition []ArgDefinition - E2ETest E2ETestFunc -} - -// NewE2ETest creates a new instance of E2ETest with specified parameters. -func NewE2ETest(name, description string, argsDefinition []ArgDefinition, e2eTestFunc E2ETestFunc) E2ETest { - return E2ETest{ - Name: name, - Description: description, - ArgsDefinition: argsDefinition, - E2ETest: e2eTestFunc, - Args: []string{}, - } -} - -// ArgDefinition defines a structure for holding an argument's description along with it's default value. -type ArgDefinition struct { - Description string - DefaultValue string -} - -// DefaultArgs extracts and returns array of default arguments from the ArgsDefinition. -func (e E2ETest) DefaultArgs() []string { - defaultArgs := make([]string, len(e.ArgsDefinition)) - for i, spec := range e.ArgsDefinition { - defaultArgs[i] = spec.DefaultValue - } - return defaultArgs -} - -// ArgsDescription returns a string representing the arguments description in a readable format. -func (e E2ETest) ArgsDescription() string { - argsDescription := "" - for _, def := range e.ArgsDefinition { - argDesc := fmt.Sprintf("%s (%s)", def.Description, def.DefaultValue) - if argsDescription != "" { - argsDescription += ", " - } - argsDescription += argDesc - } - return argsDescription -} - -// E2ETestRunConfig defines the basic configuration for initiating an E2E test, including its name and optional runtime arguments. -type E2ETestRunConfig struct { - Name string - Args []string -} - -// GetE2ETestsToRunByName prepares a list of E2ETests to run based on given test names without arguments -func (runner *E2ERunner) GetE2ETestsToRunByName(availableTests []E2ETest, testNames ...string) ([]E2ETest, error) { - tests := []E2ETestRunConfig{} - for _, testName := range testNames { - tests = append(tests, E2ETestRunConfig{ - Name: testName, - Args: []string{}, - }) - } - return runner.GetE2ETestsToRunByConfig(availableTests, tests) -} - -// GetE2ETestsToRunByConfig prepares a list of E2ETests to run based on provided test names and their corresponding arguments -func (runner *E2ERunner) GetE2ETestsToRunByConfig(availableTests []E2ETest, testConfigs []E2ETestRunConfig) ([]E2ETest, error) { - tests := []E2ETest{} - for _, testSpec := range testConfigs { - e2eTest, found := findE2ETestByName(availableTests, testSpec.Name) - if !found { - return nil, fmt.Errorf("e2e test %s not found", testSpec.Name) - } - e2eTestToRun := NewE2ETest( - e2eTest.Name, - e2eTest.Description, - e2eTest.ArgsDefinition, - e2eTest.E2ETest, - ) - // update e2e test args - e2eTestToRun.Args = testSpec.Args - tests = append(tests, e2eTestToRun) - } - - return tests, nil -} - -// RunE2ETests runs a list of e2e tests -func (runner *E2ERunner) RunE2ETests(e2eTests []E2ETest) (err error) { - for _, e2eTest := range e2eTests { - if err := runner.RunE2ETest(e2eTest, true); err != nil { - return err - } - } - return nil -} - -// RunE2ETestsFromNamesIntoReport runs a list of e2e tests by name in a list of e2e tests and returns a report -// The function doesn't return an error, it returns a report with the error -func (runner *E2ERunner) RunE2ETestsIntoReport(e2eTests []E2ETest) (TestReports, error) { - // go through all tests - reports := make(TestReports, 0, len(e2eTests)) - for _, test := range e2eTests { - // get info before test - balancesBefore, err := runner.GetAccountBalances(true) - if err != nil { - return nil, err - } - timeBefore := time.Now() - - // run test - testErr := runner.RunE2ETest(test, false) - if testErr != nil { - runner.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 := runner.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 -} - -// RunE2ETest runs a e2e test -func (runner *E2ERunner) RunE2ETest(e2eTest E2ETest, checkAccounting bool) (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("%s failed: %v, stack trace %s", e2eTest.Name, r, stack[:n]) - } - }() - - startTime := time.Now() - runner.Logger.Print("⏳running - %s", e2eTest.Description) - - // run e2e test, if args are not provided, use default args - args := e2eTest.Args - if len(args) == 0 { - args = e2eTest.DefaultArgs() - } - e2eTest.E2ETest(runner, args) - - //check supplies - if checkAccounting { - if err := runner.CheckZRC20ReserveAndSupply(); err != nil { - return err - } - } - - runner.Logger.Print("✅ completed in %s - %s", time.Since(startTime), e2eTest.Description) - - return err -} - -// findE2ETest finds a e2e test by name -func findE2ETestByName(e2eTests []E2ETest, e2eTestName string) (E2ETest, bool) { - for _, test := range e2eTests { - if test.Name == e2eTestName { - return test, true - } - } - return E2ETest{}, false -} - // CopyAddressesFrom copies addresses from another E2ETestRunner that initialized the contracts func (runner *E2ERunner) CopyAddressesFrom(other *E2ERunner) (err error) { // copy TSS address diff --git a/e2e/runner/setup_zeta.go b/e2e/runner/setup_zeta.go index 71718a8e3f..02ec540023 100644 --- a/e2e/runner/setup_zeta.go +++ b/e2e/runner/setup_zeta.go @@ -22,6 +22,10 @@ import ( observertypes "github.com/zeta-chain/zetacore/x/observer/types" ) +// EmissionsPoolFunding represents the amount of ZETA to fund the emissions pool with +// This is the same value as used originally on mainnet (20M ZETA) +var EmissionsPoolFunding = big.NewInt(0).Mul(big.NewInt(1e18), big.NewInt(2e7)) + // SetTSSAddresses set TSS addresses from information queried from ZetaChain func (runner *E2ERunner) SetTSSAddresses() error { runner.Logger.Print("⚙️ setting up TSS address") @@ -203,5 +207,6 @@ func (runner *E2ERunner) SetupBTCZRC20() { // FundEmissionsPool funds the emissions pool on ZetaChain with the same value as used originally on mainnet (20M ZETA) func (runner *E2ERunner) FundEmissionsPool() error { runner.Logger.Print("⚙️ funding the emissions pool on ZetaChain with 20M ZETA (%s)", txserver.EmissionsPoolAddress) - return runner.ZetaTxServer.FundEmissionsPool(e2eutils.FungibleAdminName, "20000000000000000000000000") + + return runner.ZetaTxServer.FundEmissionsPool(e2eutils.FungibleAdminName, EmissionsPoolFunding.String()) }