From 183aa9481d59d9e08f5ee4d4dc51458f2047bf53 Mon Sep 17 00:00:00 2001 From: GuillemGarciaDev Date: Tue, 19 Nov 2024 22:08:26 +0100 Subject: [PATCH 01/60] feat(e2e): e2e test suite poc --- e2e/manager/manager.go | 33 +++ e2e/manager/network/app.go | 29 +++ e2e/manager/network/config.go | 88 +++++++ e2e/manager/network/logger.go | 8 + e2e/manager/network/manager.go | 120 +++++++++ e2e/manager/network/network.go | 424 +++++++++++++++++++++++++++++++ e2e/manager/network/utils.go | 319 +++++++++++++++++++++++ e2e/manager/network/validator.go | 42 +++ e2e/modules/poa.go | 17 ++ e2e/suite.go | 40 +++ e2e/suite_test.go | 18 ++ 11 files changed, 1138 insertions(+) create mode 100644 e2e/manager/manager.go create mode 100644 e2e/manager/network/app.go create mode 100644 e2e/manager/network/config.go create mode 100644 e2e/manager/network/logger.go create mode 100644 e2e/manager/network/manager.go create mode 100644 e2e/manager/network/network.go create mode 100644 e2e/manager/network/utils.go create mode 100644 e2e/manager/network/validator.go create mode 100644 e2e/modules/poa.go create mode 100644 e2e/suite.go create mode 100644 e2e/suite_test.go diff --git a/e2e/manager/manager.go b/e2e/manager/manager.go new file mode 100644 index 0000000..32e213e --- /dev/null +++ b/e2e/manager/manager.go @@ -0,0 +1,33 @@ +package manager + +import ( + "time" + + "github.com/ethereum/go-ethereum/ethclient" + "github.com/evmos/evmos/v19/server/config" +) + +const ( + NetworkManager = "network" +) + +type Manager interface { + // Add here more module operations + // modules.PoaOps + + + // Block related methods + LatestHeight() (int64, error) + WaitForHeight(h int64) (int64, error) + WaitForHeightWithTimeout(h int64, t time.Duration) (int64, error) + WaitForNextBlock() error + MustWaitForNextBlock() + + // Manager related methods + Cleanup() + JSONRPCClient() *ethclient.Client + SetJSONRPCClient(client *ethclient.Client) + AppConfig() *config.Config +} + + diff --git a/e2e/manager/network/app.go b/e2e/manager/network/app.go new file mode 100644 index 0000000..b659959 --- /dev/null +++ b/e2e/manager/network/app.go @@ -0,0 +1,29 @@ +package network + +import ( + "cosmossdk.io/simapp/params" + dbm "github.com/cometbft/cometbft-db" + "github.com/cosmos/cosmos-sdk/baseapp" + servertypes "github.com/cosmos/cosmos-sdk/server/types" + pruningtypes "github.com/cosmos/cosmos-sdk/store/pruning/types" + simutils "github.com/cosmos/cosmos-sdk/testutil/sims" + "github.com/xrplevm/node/v3/app" +) + +// AppConstructor defines a function which accepts a network configuration and +// creates an ABCI Application to provide to Tendermint. +type AppConstructor = func(val Validator) servertypes.Application + +// NewAppConstructor returns a new Evmos AppConstructor +func NewAppConstructor(encodingCfg params.EncodingConfig, chainID string) AppConstructor { + return func(val Validator) servertypes.Application { + return app.New( + val.Ctx.Logger, dbm.NewMemDB(), nil, true, make(map[int64]bool), val.Ctx.Config.RootDir, 0, + encodingCfg, + simutils.NewAppOptionsWithFlagHome(val.Ctx.Config.RootDir), + baseapp.SetPruning(pruningtypes.NewPruningOptionsFromString(val.AppConfig.Pruning)), + baseapp.SetMinGasPrices(val.AppConfig.MinGasPrices), + baseapp.SetChainID(chainID), + ) + } +} \ No newline at end of file diff --git a/e2e/manager/network/config.go b/e2e/manager/network/config.go new file mode 100644 index 0000000..259fcc7 --- /dev/null +++ b/e2e/manager/network/config.go @@ -0,0 +1,88 @@ +package network + +import ( + "fmt" + "os" + "time" + + "cosmossdk.io/math" + "cosmossdk.io/simapp" + tmrand "github.com/cometbft/cometbft/libs/rand" + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/codec" + codectypes "github.com/cosmos/cosmos-sdk/codec/types" + "github.com/cosmos/cosmos-sdk/crypto/keyring" + pruningtypes "github.com/cosmos/cosmos-sdk/store/pruning/types" + sdk "github.com/cosmos/cosmos-sdk/types" + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" + "github.com/evmos/evmos/v19/crypto/hd" + "github.com/xrplevm/node/v3/app" +) + +// Config defines the necessary configuration used to bootstrap and start an +// in-process local testing network. +type Config struct { + KeyringOptions []keyring.Option // keyring configuration options + Codec codec.Codec + LegacyAmino *codec.LegacyAmino // TODO: Remove! + InterfaceRegistry codectypes.InterfaceRegistry + TxConfig client.TxConfig + AccountRetriever client.AccountRetriever + AppConstructor AppConstructor // the ABCI application constructor + GenesisState simapp.GenesisState // custom gensis state to provide + TimeoutCommit time.Duration // the consensus commitment timeout + AccountTokens math.Int // the amount of unique validator tokens (e.g. 1000node0) + StakingTokens math.Int // the amount of tokens each validator has available to stake + BondedTokens math.Int // the amount of tokens each validator stakes + SignedBlocksWindow int64 // slashing signed blocks window + NumValidators int // the total number of validators to create and bond + NumBondedValidators int // the total number of validators with bonded tokens to create + ChainID string // the network chain-id + BondDenom string // the staking bond denomination + TokenDenom string // the fees token denomination + UnBoundingTime time.Duration // the time to unbound and accept governance proposals + MinGasPrices string // the minimum gas prices each validator will accept + PruningStrategy string // the pruning strategy each validator will have + SigningAlgo string // signing algorithm for keys + RPCAddress string // RPC listen address (including port) + JSONRPCAddress string // JSON-RPC listen address (including port) + APIAddress string // REST API listen address (including port) + GRPCAddress string // GRPC server listen address (including port) + EnableTMLogging bool // enable Tendermint logging to STDOUT + CleanupDir bool // remove base temporary directory during cleanup + PrintMnemonic bool // print the mnemonic of first validator as log output for testing +} + +// DefaultConfig returns a sane default configuration suitable for nearly all +// testing requirements. +func DefaultConfig(numValidators int, numBondedValidators int, blockTime time.Duration, unbondingBlocks int64) Config { + encCfg := app.MakeEncodingConfig() + chainID := fmt.Sprintf("exrp_%d-1", tmrand.Int63n(9999999999999)+1) + return Config{ + Codec: encCfg.Codec, + TxConfig: encCfg.TxConfig, + LegacyAmino: encCfg.Amino, + InterfaceRegistry: encCfg.InterfaceRegistry, + AccountRetriever: authtypes.AccountRetriever{}, + AppConstructor: NewAppConstructor(encCfg, chainID), + GenesisState: app.ModuleBasics.DefaultGenesis(encCfg.Codec), + TimeoutCommit: blockTime, + ChainID: chainID, + NumValidators: numValidators, + NumBondedValidators: numBondedValidators, + BondDenom: "apoa", + MinGasPrices: fmt.Sprintf("0.000006%s", "apoa"), + AccountTokens: sdk.TokensFromConsensusPower(1000000000000000000, sdk.DefaultPowerReduction), + StakingTokens: sdk.TokensFromConsensusPower(1, sdk.DefaultPowerReduction), + BondedTokens: sdk.TokensFromConsensusPower(1, sdk.DefaultPowerReduction), + TokenDenom: "axrp", + UnBoundingTime: (time.Duration(unbondingBlocks) * blockTime) + time.Second, + PruningStrategy: pruningtypes.PruningOptionNothing, + CleanupDir: os.Getenv("TEST_CLEANUP_DIR") != "false", + SigningAlgo: string(hd.EthSecp256k1Type), + KeyringOptions: []keyring.Option{hd.EthSecp256k1Option()}, + PrintMnemonic: false, + EnableTMLogging: true, + SignedBlocksWindow: 2, + } +} \ No newline at end of file diff --git a/e2e/manager/network/logger.go b/e2e/manager/network/logger.go new file mode 100644 index 0000000..ce355ad --- /dev/null +++ b/e2e/manager/network/logger.go @@ -0,0 +1,8 @@ +package network + +// Logger is a network logger interface that exposes testnet-level Log() methods for an in-process testing network +// This is not to be confused with logging that may happen at an individual node or validator level +type Logger interface { + Log(args ...interface{}) + Logf(format string, args ...interface{}) +} \ No newline at end of file diff --git a/e2e/manager/network/manager.go b/e2e/manager/network/manager.go new file mode 100644 index 0000000..4adf1ef --- /dev/null +++ b/e2e/manager/network/manager.go @@ -0,0 +1,120 @@ +package network + +import ( + "context" + "errors" + "sync" + "testing" + "time" + + "github.com/ethereum/go-ethereum/ethclient" + "github.com/evmos/evmos/v19/server/config" + "github.com/stretchr/testify/require" +) + +// package-wide network lock to only allow one test network at a time +var lock = new(sync.Mutex) + +type NetworkManager struct { + + network *Network +} + +func NewManager(t *testing.T, numValidators int, numBondedValidators int, blockTime time.Duration, unbondingBlocks int64, nodeDir string) *NetworkManager { + cfg := DefaultConfig(numValidators, numBondedValidators, blockTime, unbondingBlocks) + + network, err := NewNetwork(t, nodeDir, cfg) + require.NoError(t, err) + + return &NetworkManager{network} +} + +func (m *NetworkManager) JSONRPCClient() *ethclient.Client { + return m.network.Validators[0].JSONRPCClient +} + +func (m *NetworkManager) SetJSONRPCClient(client *ethclient.Client) { + m.network.Validators[0].JSONRPCClient = client +} + +func (m *NetworkManager) Cleanup() { + m.network.Cleanup() +} + +// LatestHeight returns the latest height of the network or an error if the +// query fails or no validators exist. +func (m *NetworkManager) LatestHeight() (int64, error) { + if len(m.network.Validators) == 0 { + return 0, errors.New("no validators available") + } + + status, err := m.network.Validators[0].RPCClient.Status(context.Background()) + if err != nil { + return 0, err + } + + return status.SyncInfo.LatestBlockHeight, nil +} + +// WaitForHeight performs a blocking check where it waits for a block to be +// committed after a given block. If that height is not reached within a timeout, +// an error is returned. Regardless, the latest height queried is returned. +func (m *NetworkManager) WaitForHeight(h int64) (int64, error) { + return m.WaitForHeightWithTimeout(h, 10*time.Second) +} + +// WaitForHeightWithTimeout is the same as WaitForHeight except the caller can +// provide a custom timeout. +func (m *NetworkManager) WaitForHeightWithTimeout(h int64, t time.Duration) (int64, error) { + ticker := time.NewTicker(time.Second) + timeout := time.After(t) + + if len(m.network.Validators) == 0 { + return 0, errors.New("no validators available") + } + + var latestHeight int64 + val := m.network.Validators[0] + + for { + select { + case <-timeout: + ticker.Stop() + return latestHeight, errors.New("timeout exceeded waiting for block") + case <-ticker.C: + status, err := val.RPCClient.Status(context.Background()) + if err == nil && status != nil { + latestHeight = status.SyncInfo.LatestBlockHeight + if latestHeight >= h { + return latestHeight, nil + } + } + } + } +} + +// WaitForNextBlock waits for the next block to be committed, returning an error +// upon failure. +func (m *NetworkManager) WaitForNextBlock() error { + lastBlock, err := m.LatestHeight() + if err != nil { + return err + } + + _, err = m.WaitForHeight(lastBlock + 1) + if err != nil { + return err + } + + return err +} + +func (m *NetworkManager) MustWaitForNextBlock() { + if err := m.WaitForNextBlock(); err != nil { + panic(err) + } +} + +func (m *NetworkManager) AppConfig() *config.Config { + return m.network.Validators[0].AppConfig +} diff --git a/e2e/manager/network/network.go b/e2e/manager/network/network.go new file mode 100644 index 0000000..d695d4b --- /dev/null +++ b/e2e/manager/network/network.go @@ -0,0 +1,424 @@ +package network + +import ( + "bufio" + "context" + "encoding/json" + "fmt" + "net/url" + "os" + "path/filepath" + "strings" + "time" + + tmcfg "github.com/cometbft/cometbft/config" + tmflags "github.com/cometbft/cometbft/libs/cli/flags" + "github.com/cometbft/cometbft/libs/log" + "github.com/ethereum/go-ethereum/common" + + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/client/tx" + "github.com/cosmos/cosmos-sdk/crypto/keyring" + cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" + "github.com/cosmos/cosmos-sdk/server" + srvconfig "github.com/cosmos/cosmos-sdk/server/config" + "github.com/cosmos/cosmos-sdk/testutil" + 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/cosmos/cosmos-sdk/x/genutil" + stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" + + "github.com/evmos/evmos/v19/server/config" + evmostypes "github.com/evmos/evmos/v19/types" + evmtypes "github.com/evmos/evmos/v19/x/evm/types" +) + +// Network defines a local in-process testing network using SimApp. It can be +// configured to start any number of validators, each with its own RPC and API +// clients. Typically, this test network would be used in client and integration +// testing where user input is expected. +// +// Note, due to Tendermint constraints in regards to RPC functionality, there +// may only be one test network running at a time. Thus, any caller must be +// sure to Cleanup after testing is finished in order to allow other tests +// to create networks. In addition, only the first validator will have a valid +// RPC and API server/client. +type Network struct { + Logger Logger + BaseDir string + Validators []*Validator + + Config Config +} + +func NewNetwork(l Logger, baseDir string, cfg Config) (*Network, error) { + lock.Lock() + + if !evmostypes.IsValidChainID(cfg.ChainID) { + return nil, fmt.Errorf("invalid chain-id: %s", cfg.ChainID) + } + + network := &Network{ + Logger: l, + BaseDir: baseDir, + Validators: make([]*Validator, cfg.NumValidators), + Config: cfg, + } + + l.Logf("preparing test network with chain-id \"%s\"\n", cfg.ChainID) + + monikers := make([]string, cfg.NumValidators) + nodeIDs := make([]string, cfg.NumValidators) + valPubKeys := make([]cryptotypes.PubKey, cfg.NumValidators) + + var ( + genAccounts []authtypes.GenesisAccount + genBalances []banktypes.Balance + genFiles []string + ) + + buf := bufio.NewReader(os.Stdin) + + // generate private keys, node IDs, and initial transactions + for i := 0; i < cfg.NumValidators; i++ { + appCfg := config.DefaultConfig() + appCfg.Pruning = cfg.PruningStrategy + appCfg.MinGasPrices = cfg.MinGasPrices + appCfg.API.Enable = true + appCfg.API.Swagger = false + appCfg.Telemetry.Enabled = false + appCfg.Telemetry.GlobalLabels = [][]string{{"chain_id", cfg.ChainID}} + + ctx := server.NewDefaultContext() + tmCfg := ctx.Config + tmCfg.Consensus.TimeoutCommit = cfg.TimeoutCommit + + // Only allow the first validator to expose an RPC, API and gRPC + // server/client due to Tendermint in-process constraints. + apiAddr := "" + tmCfg.RPC.ListenAddress = "" + appCfg.GRPC.Enable = false + appCfg.GRPCWeb.Enable = false + appCfg.JSONRPC.Enable = false + apiListenAddr := "" + + if cfg.APIAddress != "" { + apiListenAddr = cfg.APIAddress + } else { + var err error + apiListenAddr, _, err = server.FreeTCPAddr() + if err != nil { + return nil, err + } + } + + appCfg.API.Address = apiListenAddr + apiURL, err := url.Parse(apiListenAddr) + if err != nil { + return nil, err + } + apiAddr = fmt.Sprintf("http://%s:%s", apiURL.Hostname(), apiURL.Port()) + + if cfg.RPCAddress != "" { + tmCfg.RPC.ListenAddress = cfg.RPCAddress + } else { + rpcAddr, _, err := server.FreeTCPAddr() + if err != nil { + return nil, err + } + tmCfg.RPC.ListenAddress = rpcAddr + } + + if cfg.GRPCAddress != "" { + appCfg.GRPC.Address = cfg.GRPCAddress + } else { + _, grpcPort, err := server.FreeTCPAddr() + if err != nil { + return nil, err + } + appCfg.GRPC.Address = fmt.Sprintf("0.0.0.0:%s", grpcPort) + } + appCfg.GRPC.Enable = false + + _, grpcWebPort, err := server.FreeTCPAddr() + if err != nil { + return nil, err + } + appCfg.GRPCWeb.Address = fmt.Sprintf("0.0.0.0:%s", grpcWebPort) + appCfg.GRPCWeb.Enable = false + + if cfg.JSONRPCAddress != "" { + appCfg.JSONRPC.Address = cfg.JSONRPCAddress + } else { + _, jsonRPCPort, err := server.FreeTCPAddr() + if err != nil { + return nil, err + } + appCfg.JSONRPC.Address = fmt.Sprintf("0.0.0.0:%s", jsonRPCPort) + } + appCfg.JSONRPC.Enable = false + appCfg.JSONRPC.API = config.GetAPINamespaces() + + logger := log.NewNopLogger() + if cfg.EnableTMLogging && i == 0 { + logger = log.NewTMLogger(log.NewSyncWriter(os.Stdout)) + logger, _ = tmflags.ParseLogLevel("info", logger, tmcfg.DefaultLogLevel) + } + + ctx.Logger = logger + + nodeDirName := fmt.Sprintf("node%d", i) + nodeDir := filepath.Join(network.BaseDir, nodeDirName, "evmosd") + clientDir := filepath.Join(network.BaseDir, nodeDirName, "evmoscli") + gentxsDir := filepath.Join(network.BaseDir, "gentxs") + + err = os.MkdirAll(filepath.Join(nodeDir, "config"), 0o750) + if err != nil { + return nil, err + } + + err = os.MkdirAll(clientDir, 0o750) + if err != nil { + return nil, err + } + + tmCfg.SetRoot(nodeDir) + tmCfg.Moniker = nodeDirName + monikers[i] = nodeDirName + + proxyAddr, _, err := server.FreeTCPAddr() + if err != nil { + return nil, err + } + tmCfg.ProxyApp = proxyAddr + + p2pAddr, _, err := server.FreeTCPAddr() + if err != nil { + return nil, err + } + tmCfg.P2P.ListenAddress = p2pAddr + tmCfg.P2P.AddrBookStrict = false + tmCfg.P2P.AllowDuplicateIP = true + + nodeID, pubKey, err := genutil.InitializeNodeValidatorFiles(tmCfg) + if err != nil { + return nil, err + } + nodeIDs[i] = nodeID + valPubKeys[i] = pubKey + + kb, err := keyring.New(sdk.KeyringServiceName(), keyring.BackendTest, clientDir, buf, cfg.Codec, cfg.KeyringOptions...) + if err != nil { + return nil, err + } + + keyringAlgos, _ := kb.SupportedAlgorithms() + algo, err := keyring.NewSigningAlgoFromString(cfg.SigningAlgo, keyringAlgos) + if err != nil { + return nil, err + } + + addr, secret, err := testutil.GenerateSaveCoinKey(kb, nodeDirName, "", true, algo) + if err != nil { + return nil, err + } + + // if PrintMnemonic is set to true, we print the first validator node's secret to the network's logger + // for debugging and manual testing + if cfg.PrintMnemonic && i == 0 { + printMnemonic(l, secret) + } + + info := map[string]string{"secret": secret} + infoBz, err := json.Marshal(info) + if err != nil { + return nil, err + } + + // save private key seed words + err = WriteFile(fmt.Sprintf("%v.json", "key_seed"), clientDir, infoBz) + if err != nil { + return nil, err + } + + balances := sdk.NewCoins( + sdk.NewCoin(cfg.TokenDenom, cfg.AccountTokens), + sdk.NewCoin(cfg.BondDenom, cfg.StakingTokens), + ) + + genFiles = append(genFiles, tmCfg.GenesisFile()) + genBalances = append(genBalances, banktypes.Balance{Address: addr.String(), Coins: balances.Sort()}) + genAccounts = append(genAccounts, &evmostypes.EthAccount{ + BaseAccount: authtypes.NewBaseAccount(addr, nil, 0, 0), + CodeHash: common.BytesToHash(evmtypes.EmptyCodeHash).Hex(), + }) + + commission, err := sdk.NewDecFromStr("0.5") + if err != nil { + return nil, err + } + + if i < cfg.NumBondedValidators { + + createValMsg, err := stakingtypes.NewMsgCreateValidator( + sdk.ValAddress(addr), + valPubKeys[i], + sdk.NewCoin(cfg.BondDenom, cfg.BondedTokens), + stakingtypes.NewDescription(nodeDirName, "", "", "", ""), + stakingtypes.NewCommissionRates(commission, sdk.OneDec(), sdk.OneDec()), + sdk.OneInt(), + ) + if err != nil { + return nil, err + } + + p2pURL, err := url.Parse(p2pAddr) + if err != nil { + return nil, err + } + + memo := fmt.Sprintf("%s@%s:%s", nodeIDs[i], p2pURL.Hostname(), p2pURL.Port()) + fee := sdk.NewCoins(sdk.NewCoin(cfg.BondDenom, sdk.NewInt(0))) + txBuilder := cfg.TxConfig.NewTxBuilder() + err = txBuilder.SetMsgs(createValMsg) + if err != nil { + return nil, err + } + txBuilder.SetFeeAmount(fee) // Arbitrary fee + txBuilder.SetGasLimit(1000000) // Need at least 100386 + txBuilder.SetMemo(memo) + + txFactory := tx.Factory{} + txFactory = txFactory. + WithChainID(cfg.ChainID). + WithMemo(memo). + WithKeybase(kb). + WithTxConfig(cfg.TxConfig) + + if err := tx.Sign(txFactory, nodeDirName, txBuilder, true); err != nil { + return nil, err + } + + txBz, err := cfg.TxConfig.TxJSONEncoder()(txBuilder.GetTx()) + if err != nil { + return nil, err + } + + if err := WriteFile(fmt.Sprintf("%v.json", nodeDirName), gentxsDir, txBz); err != nil { + return nil, err + } + } + + customAppTemplate, _ := config.AppConfig(cfg.TokenDenom) + srvconfig.SetConfigTemplate(customAppTemplate) + srvconfig.WriteConfigFile(filepath.Join(nodeDir, "config/app.toml"), appCfg) + + ctx.Viper.SetEnvKeyReplacer(strings.NewReplacer(".", "_", "-", "_")) + ctx.Viper.SetConfigFile(filepath.Join(nodeDir, "config/app.toml")) + err = ctx.Viper.ReadInConfig() + if err != nil { + return nil, err + } + + clientCtx := client.Context{}. + WithKeyringDir(clientDir). + WithKeyring(kb). + WithHomeDir(tmCfg.RootDir). + WithChainID(cfg.ChainID). + WithInterfaceRegistry(cfg.InterfaceRegistry). + WithCodec(cfg.Codec). + WithLegacyAmino(cfg.LegacyAmino). + WithTxConfig(cfg.TxConfig). + WithAccountRetriever(cfg.AccountRetriever) + + network.Validators[i] = &Validator{ + AppConfig: appCfg, + ClientCtx: clientCtx, + Ctx: ctx, + Dir: filepath.Join(network.BaseDir, nodeDirName), + NodeID: nodeID, + PubKey: pubKey, + Moniker: nodeDirName, + RPCAddress: tmCfg.RPC.ListenAddress, + P2PAddress: tmCfg.P2P.ListenAddress, + APIAddress: apiAddr, + Address: addr, + ValAddress: sdk.ValAddress(addr), + } + } + + err := initGenFiles(cfg, genAccounts, genBalances, genFiles) + if err != nil { + return nil, err + } + err = collectGenFiles(cfg, network.Validators, network.BaseDir) + if err != nil { + return nil, err + } + + l.Log("starting test network...") + for _, v := range network.Validators { + err := startInProcess(cfg, v) + if err != nil { + return nil, err + } + } + + l.Log("started test network") + + // Ensure we cleanup incase any test was abruptly halted (e.g. SIGINT) as any + // defer in a test would not be called. + server.TrapSignal(network.Cleanup) + + return network, nil +} + +func (n *Network) Cleanup() { + defer func() { + time.Sleep(10 * time.Second) + lock.Unlock() + n.Logger.Log("released test network lock") + }() + + n.Logger.Log("cleaning up test network...") + + for _, v := range n.Validators { + if v.TmNode != nil && v.TmNode.IsRunning() { + _ = v.TmNode.Stop() + } + + if v.api != nil { + _ = v.api.Close() + } + + if v.grpc != nil { + v.grpc.Stop() + if v.grpcWeb != nil { + _ = v.grpcWeb.Close() + } + } + + if v.jsonrpc != nil { + shutdownCtx, cancelFn := context.WithTimeout(context.Background(), 10*time.Second) + defer cancelFn() + + if err := v.jsonrpc.Shutdown(shutdownCtx); err != nil { + v.TmNode.Logger.Error("HTTP server shutdown produced a warning", "error", err.Error()) + } else { + v.TmNode.Logger.Info("HTTP server shut down, waiting 5 sec") + select { + case <-time.NewTicker(5 * time.Second).C: + case <-v.jsonrpcDone: + } + } + } + } + + if n.Config.CleanupDir { + time.Sleep(10 * time.Second) + _ = os.RemoveAll(n.BaseDir) + } + + n.Logger.Log("finished cleaning up test network") +} diff --git a/e2e/manager/network/utils.go b/e2e/manager/network/utils.go new file mode 100644 index 0000000..98a6a2d --- /dev/null +++ b/e2e/manager/network/utils.go @@ -0,0 +1,319 @@ +package network + +import ( + "encoding/json" + "fmt" + "path/filepath" + "strings" + "time" + + "github.com/cometbft/cometbft/libs/log" + sdk "github.com/cosmos/cosmos-sdk/types" + slashingtypes "github.com/cosmos/cosmos-sdk/x/slashing/types" + + tmos "github.com/cometbft/cometbft/libs/os" + "github.com/cometbft/cometbft/node" + "github.com/cometbft/cometbft/p2p" + pvm "github.com/cometbft/cometbft/privval" + "github.com/cometbft/cometbft/proxy" + "github.com/cometbft/cometbft/rpc/client/local" + "github.com/cometbft/cometbft/types" + tmtime "github.com/cometbft/cometbft/types/time" + "github.com/ethereum/go-ethereum/ethclient" + + "github.com/cosmos/cosmos-sdk/server/api" + servergrpc "github.com/cosmos/cosmos-sdk/server/grpc" + srvtypes "github.com/cosmos/cosmos-sdk/server/types" + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" + banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" + crisistypes "github.com/cosmos/cosmos-sdk/x/crisis/types" + "github.com/cosmos/cosmos-sdk/x/genutil" + genutiltypes "github.com/cosmos/cosmos-sdk/x/genutil/types" + govtypes "github.com/cosmos/cosmos-sdk/x/gov/types" + govv1 "github.com/cosmos/cosmos-sdk/x/gov/types/v1" + stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" + + "github.com/evmos/evmos/v19/server" + evmtypes "github.com/evmos/evmos/v19/x/evm/types" +) + +type FmtLogger struct { + log.Logger +} + +func startInProcess(cfg Config, val *Validator) error { + logger := val.Ctx.Logger + tmCfg := val.Ctx.Config + tmCfg.Instrumentation.Prometheus = false + + if err := val.AppConfig.ValidateBasic(); err != nil { + return err + } + + nodeKey, err := p2p.LoadOrGenNodeKey(tmCfg.NodeKeyFile()) + if err != nil { + return err + } + + app := cfg.AppConstructor(*val) + + genDocProvider := node.DefaultGenesisDocProviderFunc(tmCfg) + tmNode, err := node.NewNode( + tmCfg, + pvm.LoadOrGenFilePV(tmCfg.PrivValidatorKeyFile(), tmCfg.PrivValidatorStateFile()), + nodeKey, + proxy.NewLocalClientCreator(app), + genDocProvider, + node.DefaultDBProvider, + node.DefaultMetricsProvider(tmCfg.Instrumentation), + logger.With("module", val.Moniker), + ) + if err != nil { + return err + } + + if err := tmNode.Start(); err != nil { + return err + } + + val.TmNode = tmNode + + if val.RPCAddress != "" { + val.RPCClient = local.New(tmNode) + } + + // We'll need a RPC client if the validator exposes a gRPC or REST endpoint. + if val.APIAddress != "" || val.AppConfig.GRPC.Enable { + val.ClientCtx = val.ClientCtx. + WithClient(val.RPCClient) + + // Add the tx service in the gRPC router. + app.RegisterTxService(val.ClientCtx) + + // Add the tendermint queries service in the gRPC router. + app.RegisterTendermintService(val.ClientCtx) + } + + if val.AppConfig.API.Enable && val.APIAddress != "" { + apiSrv := api.New(val.ClientCtx, logger.With("module", "api-server")) + app.RegisterAPIRoutes(apiSrv, val.AppConfig.API) + + errCh := make(chan error) + + go func() { + if err := apiSrv.Start(val.AppConfig.Config); err != nil { + errCh <- err + } + }() + + select { + case err := <-errCh: + return err + case <-time.After(srvtypes.ServerStartTime): // assume server started successfully + } + + val.api = apiSrv + } + + if val.AppConfig.GRPC.Enable { + grpcSrv, err := servergrpc.StartGRPCServer(val.ClientCtx, app, val.AppConfig.GRPC) + if err != nil { + return err + } + + val.grpc = grpcSrv + + if val.AppConfig.GRPCWeb.Enable { + val.grpcWeb, err = servergrpc.StartGRPCWeb(grpcSrv, val.AppConfig.Config) + if err != nil { + return err + } + } + } + + if val.AppConfig.JSONRPC.Enable && val.AppConfig.JSONRPC.Address != "" { + if val.Ctx == nil || val.Ctx.Viper == nil { + return fmt.Errorf("validator %s context is nil", val.Moniker) + } + + tmEndpoint := "/websocket" + tmRPCAddr := val.RPCAddress + + val.jsonrpc, val.jsonrpcDone, err = server.StartJSONRPC(val.Ctx, val.ClientCtx, tmRPCAddr, tmEndpoint, val.AppConfig, nil) + if err != nil { + return err + } + + address := fmt.Sprintf("http://%s", val.AppConfig.JSONRPC.Address) + + val.JSONRPCClient, err = ethclient.Dial(address) + if err != nil { + return fmt.Errorf("failed to dial JSON-RPC at %s: %w", val.AppConfig.JSONRPC.Address, err) + } + } + + return nil +} + +func collectGenFiles(cfg Config, vals []*Validator, outputDir string) error { + genTime := tmtime.Now() + + for i := 0; i < cfg.NumValidators; i++ { + tmCfg := vals[i].Ctx.Config + + nodeDir := filepath.Join(outputDir, vals[i].Moniker, "evmosd") + gentxsDir := filepath.Join(outputDir, "gentxs") + + tmCfg.Moniker = vals[i].Moniker + tmCfg.SetRoot(nodeDir) + + initCfg := genutiltypes.NewInitConfig(cfg.ChainID, gentxsDir, vals[i].NodeID, vals[i].PubKey) + + genFile := tmCfg.GenesisFile() + genDoc, err := types.GenesisDocFromFile(genFile) + if err != nil { + return err + } + + appState, err := genutil.GenAppStateFromConfig(cfg.Codec, cfg.TxConfig, + tmCfg, initCfg, *genDoc, banktypes.GenesisBalancesIterator{}, genutiltypes.DefaultMessageValidator) + if err != nil { + return err + } + + // overwrite each validator's genesis file to have a canonical genesis time + if err := genutil.ExportGenesisFileWithTime(genFile, cfg.ChainID, nil, appState, genTime); err != nil { + return err + } + } + + return nil +} + +func initGenFiles(cfg Config, genAccounts []authtypes.GenesisAccount, genBalances []banktypes.Balance, genFiles []string) error { + // set the accounts in the genesis state + var authGenState authtypes.GenesisState + cfg.Codec.MustUnmarshalJSON(cfg.GenesisState[authtypes.ModuleName], &authGenState) + + accounts, err := authtypes.PackAccounts(genAccounts) + if err != nil { + return err + } + + authGenState.Accounts = append(authGenState.Accounts, accounts...) + cfg.GenesisState[authtypes.ModuleName] = cfg.Codec.MustMarshalJSON(&authGenState) + + // set the balances in the genesis state + var bankGenState banktypes.GenesisState + cfg.Codec.MustUnmarshalJSON(cfg.GenesisState[banktypes.ModuleName], &bankGenState) + + bankGenState.Balances = genBalances + cfg.GenesisState[banktypes.ModuleName] = cfg.Codec.MustMarshalJSON(&bankGenState) + + var stakingGenState stakingtypes.GenesisState + cfg.Codec.MustUnmarshalJSON(cfg.GenesisState[stakingtypes.ModuleName], &stakingGenState) + + stakingGenState.Params.BondDenom = cfg.BondDenom + stakingGenState.Params.UnbondingTime = cfg.UnBoundingTime + cfg.GenesisState[stakingtypes.ModuleName] = cfg.Codec.MustMarshalJSON(&stakingGenState) + + var govGenState govv1.GenesisState + cfg.Codec.MustUnmarshalJSON(cfg.GenesisState[govtypes.ModuleName], &govGenState) + + govGenState.Params.MinDeposit[0].Denom = cfg.TokenDenom + govGenState.Params.VotingPeriod = &cfg.UnBoundingTime + cfg.GenesisState[govtypes.ModuleName] = cfg.Codec.MustMarshalJSON(&govGenState) + + var crisisGenState crisistypes.GenesisState + cfg.Codec.MustUnmarshalJSON(cfg.GenesisState[crisistypes.ModuleName], &crisisGenState) + + crisisGenState.ConstantFee.Denom = cfg.BondDenom + cfg.GenesisState[crisistypes.ModuleName] = cfg.Codec.MustMarshalJSON(&crisisGenState) + + var evmGenState evmtypes.GenesisState + cfg.Codec.MustUnmarshalJSON(cfg.GenesisState[evmtypes.ModuleName], &evmGenState) + + evmGenState.Params.EvmDenom = cfg.TokenDenom + cfg.GenesisState[evmtypes.ModuleName] = cfg.Codec.MustMarshalJSON(&evmGenState) + + var slashingGenState slashingtypes.GenesisState + cfg.Codec.MustUnmarshalJSON(cfg.GenesisState[slashingtypes.ModuleName], &slashingGenState) + + slashingGenState.Params.SignedBlocksWindow = cfg.SignedBlocksWindow + slashingGenState.Params.SlashFractionDowntime = sdk.ZeroDec() + cfg.GenesisState[slashingtypes.ModuleName] = cfg.Codec.MustMarshalJSON(&slashingGenState) + + appGenStateJSON, err := json.MarshalIndent(cfg.GenesisState, "", " ") + if err != nil { + return err + } + + genDoc := types.GenesisDoc{ + ChainID: cfg.ChainID, + AppState: appGenStateJSON, + Validators: nil, + } + + // generate empty genesis files for each validator and save + for i := 0; i < cfg.NumValidators; i++ { + if err := genDoc.SaveAs(genFiles[i]); err != nil { + return err + } + } + + return nil +} + +func WriteFile(name string, dir string, contents []byte) error { + file := filepath.Join(dir, name) + + err := tmos.EnsureDir(dir, 0o755) + if err != nil { + return err + } + + return tmos.WriteFile(file, contents, 0o644) +} + + +// printMnemonic prints a provided mnemonic seed phrase on a network logger +// for debugging and manual testing +func printMnemonic(l Logger, secret string) { + lines := []string{ + "THIS MNEMONIC IS FOR TESTING PURPOSES ONLY", + "DO NOT USE IN PRODUCTION", + "", + strings.Join(strings.Fields(secret)[0:8], " "), + strings.Join(strings.Fields(secret)[8:16], " "), + strings.Join(strings.Fields(secret)[16:24], " "), + } + + lineLengths := make([]int, len(lines)) + for i, line := range lines { + lineLengths[i] = len(line) + } + + maxLineLength := 0 + for _, lineLen := range lineLengths { + if lineLen > maxLineLength { + maxLineLength = lineLen + } + } + + l.Log("\n") + l.Log(strings.Repeat("+", maxLineLength+8)) + for _, line := range lines { + l.Logf("++ %s ++\n", centerText(line, maxLineLength)) + } + l.Log(strings.Repeat("+", maxLineLength+8)) + l.Log("\n") +} + +// centerText centers text across a fixed width, filling either side with whitespace buffers +func centerText(text string, width int) string { + textLen := len(text) + leftBuffer := strings.Repeat(" ", (width-textLen)/2) + rightBuffer := strings.Repeat(" ", (width-textLen)/2+(width-textLen)%2) + + return fmt.Sprintf("%s%s%s", leftBuffer, text, rightBuffer) +} diff --git a/e2e/manager/network/validator.go b/e2e/manager/network/validator.go new file mode 100644 index 0000000..387ad89 --- /dev/null +++ b/e2e/manager/network/validator.go @@ -0,0 +1,42 @@ +package network + +import ( + "net/http" + + "github.com/cometbft/cometbft/node" + cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" + "github.com/ethereum/go-ethereum/ethclient" + "google.golang.org/grpc" + + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/server" + + tmclient "github.com/cometbft/cometbft/rpc/client" + "github.com/cosmos/cosmos-sdk/server/api" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/evmos/evmos/v19/server/config" +) + +type Validator struct { + AppConfig *config.Config + ClientCtx client.Context + Ctx *server.Context + Dir string + NodeID string + PubKey cryptotypes.PubKey + Moniker string + APIAddress string + RPCAddress string + P2PAddress string + Address sdk.AccAddress + ValAddress sdk.ValAddress + RPCClient tmclient.Client + JSONRPCClient *ethclient.Client + + TmNode *node.Node + api *api.Server + grpc *grpc.Server + grpcWeb *http.Server + jsonrpc *http.Server + jsonrpcDone chan struct{} +} \ No newline at end of file diff --git a/e2e/modules/poa.go b/e2e/modules/poa.go new file mode 100644 index 0000000..a62643f --- /dev/null +++ b/e2e/modules/poa.go @@ -0,0 +1,17 @@ +package modules + +import ( + "github.com/xrplevm/node/v3/testutil/network" + + cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" + sdk "github.com/cosmos/cosmos-sdk/types" + govtypesv1 "github.com/cosmos/cosmos-sdk/x/gov/types/v1" +) + +type PoaOps interface { + ChangeValidator(action int, address sdk.AccAddress, pubKey cryptotypes.PubKey, validators []*network.Validator, waitStatus govtypesv1.ProposalStatus) + BondTokens(validator *network.Validator, tokens sdk.Int) + UnbondTokens(validator *network.Validator, tokens sdk.Int, wait bool) string + Delegate(validator *network.Validator, tokens sdk.Int) string + Redelegate(src *network.Validator, dst *network.Validator, tokens sdk.Int) string +} diff --git a/e2e/suite.go b/e2e/suite.go new file mode 100644 index 0000000..039bd87 --- /dev/null +++ b/e2e/suite.go @@ -0,0 +1,40 @@ +package e2e + +import ( + "fmt" + "time" + + "github.com/ethereum/go-ethereum/ethclient" + "github.com/stretchr/testify/suite" + "github.com/xrplevm/node/v3/e2e/manager" +) + +type End2EndSuite struct { + suite.Suite + + manager manager.Manager +} + + +func (s *End2EndSuite) SetManager(manager manager.Manager) { + s.T().Log("setting up test suite manager") + + s.manager = manager + s.Require().NotNil(s.manager) + + // Load state + if s.manager.JSONRPCClient() == nil { + address := fmt.Sprintf("http://%s", s.manager.AppConfig().JSONRPC.Address) + client, err := ethclient.Dial(address) + s.Require().NoError(err) + s.manager.SetJSONRPCClient(client) + } + + s.T().Log("test suite manager setup successfully") +} + +func (s *End2EndSuite) TearDownTest() { + s.T().Log("tearing down test suite") + s.manager.Cleanup() + time.Sleep(5 * time.Second) +} diff --git a/e2e/suite_test.go b/e2e/suite_test.go new file mode 100644 index 0000000..b040fd0 --- /dev/null +++ b/e2e/suite_test.go @@ -0,0 +1,18 @@ +package e2e + +import ( + "testing" + "time" + + "github.com/stretchr/testify/suite" + "github.com/xrplevm/node/v3/e2e/manager/network" +) + +func (s *End2EndSuite) SetupSuite() { + networkManager := network.NewManager(s.T(), 5, 4, 3*time.Second, 5, s.T().TempDir()) + s.SetManager(networkManager) +} + +func Test_Suite(t *testing.T) { + suite.Run(t, new(End2EndSuite)) +} \ No newline at end of file From f324c9ddc3360dbe8b6902be5cb81894f6b72f5e Mon Sep 17 00:00:00 2001 From: GuillemGarciaDev Date: Wed, 20 Nov 2024 15:37:07 +0100 Subject: [PATCH 02/60] fix(e2e): delete legacy e2e tests --- e2e/manager/manager.go | 33 --- e2e/manager/network/app.go | 29 --- e2e/manager/network/config.go | 88 ------- e2e/manager/network/logger.go | 8 - e2e/manager/network/manager.go | 120 --------- e2e/manager/network/network.go | 424 ------------------------------- e2e/manager/network/utils.go | 319 ----------------------- e2e/manager/network/validator.go | 42 --- e2e/modules/poa.go | 17 -- e2e/suite.go | 40 --- e2e/suite_test.go | 18 -- 11 files changed, 1138 deletions(-) delete mode 100644 e2e/manager/manager.go delete mode 100644 e2e/manager/network/app.go delete mode 100644 e2e/manager/network/config.go delete mode 100644 e2e/manager/network/logger.go delete mode 100644 e2e/manager/network/manager.go delete mode 100644 e2e/manager/network/network.go delete mode 100644 e2e/manager/network/utils.go delete mode 100644 e2e/manager/network/validator.go delete mode 100644 e2e/modules/poa.go delete mode 100644 e2e/suite.go delete mode 100644 e2e/suite_test.go diff --git a/e2e/manager/manager.go b/e2e/manager/manager.go deleted file mode 100644 index 32e213e..0000000 --- a/e2e/manager/manager.go +++ /dev/null @@ -1,33 +0,0 @@ -package manager - -import ( - "time" - - "github.com/ethereum/go-ethereum/ethclient" - "github.com/evmos/evmos/v19/server/config" -) - -const ( - NetworkManager = "network" -) - -type Manager interface { - // Add here more module operations - // modules.PoaOps - - - // Block related methods - LatestHeight() (int64, error) - WaitForHeight(h int64) (int64, error) - WaitForHeightWithTimeout(h int64, t time.Duration) (int64, error) - WaitForNextBlock() error - MustWaitForNextBlock() - - // Manager related methods - Cleanup() - JSONRPCClient() *ethclient.Client - SetJSONRPCClient(client *ethclient.Client) - AppConfig() *config.Config -} - - diff --git a/e2e/manager/network/app.go b/e2e/manager/network/app.go deleted file mode 100644 index b659959..0000000 --- a/e2e/manager/network/app.go +++ /dev/null @@ -1,29 +0,0 @@ -package network - -import ( - "cosmossdk.io/simapp/params" - dbm "github.com/cometbft/cometbft-db" - "github.com/cosmos/cosmos-sdk/baseapp" - servertypes "github.com/cosmos/cosmos-sdk/server/types" - pruningtypes "github.com/cosmos/cosmos-sdk/store/pruning/types" - simutils "github.com/cosmos/cosmos-sdk/testutil/sims" - "github.com/xrplevm/node/v3/app" -) - -// AppConstructor defines a function which accepts a network configuration and -// creates an ABCI Application to provide to Tendermint. -type AppConstructor = func(val Validator) servertypes.Application - -// NewAppConstructor returns a new Evmos AppConstructor -func NewAppConstructor(encodingCfg params.EncodingConfig, chainID string) AppConstructor { - return func(val Validator) servertypes.Application { - return app.New( - val.Ctx.Logger, dbm.NewMemDB(), nil, true, make(map[int64]bool), val.Ctx.Config.RootDir, 0, - encodingCfg, - simutils.NewAppOptionsWithFlagHome(val.Ctx.Config.RootDir), - baseapp.SetPruning(pruningtypes.NewPruningOptionsFromString(val.AppConfig.Pruning)), - baseapp.SetMinGasPrices(val.AppConfig.MinGasPrices), - baseapp.SetChainID(chainID), - ) - } -} \ No newline at end of file diff --git a/e2e/manager/network/config.go b/e2e/manager/network/config.go deleted file mode 100644 index 259fcc7..0000000 --- a/e2e/manager/network/config.go +++ /dev/null @@ -1,88 +0,0 @@ -package network - -import ( - "fmt" - "os" - "time" - - "cosmossdk.io/math" - "cosmossdk.io/simapp" - tmrand "github.com/cometbft/cometbft/libs/rand" - "github.com/cosmos/cosmos-sdk/client" - "github.com/cosmos/cosmos-sdk/codec" - codectypes "github.com/cosmos/cosmos-sdk/codec/types" - "github.com/cosmos/cosmos-sdk/crypto/keyring" - pruningtypes "github.com/cosmos/cosmos-sdk/store/pruning/types" - sdk "github.com/cosmos/cosmos-sdk/types" - authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" - "github.com/evmos/evmos/v19/crypto/hd" - "github.com/xrplevm/node/v3/app" -) - -// Config defines the necessary configuration used to bootstrap and start an -// in-process local testing network. -type Config struct { - KeyringOptions []keyring.Option // keyring configuration options - Codec codec.Codec - LegacyAmino *codec.LegacyAmino // TODO: Remove! - InterfaceRegistry codectypes.InterfaceRegistry - TxConfig client.TxConfig - AccountRetriever client.AccountRetriever - AppConstructor AppConstructor // the ABCI application constructor - GenesisState simapp.GenesisState // custom gensis state to provide - TimeoutCommit time.Duration // the consensus commitment timeout - AccountTokens math.Int // the amount of unique validator tokens (e.g. 1000node0) - StakingTokens math.Int // the amount of tokens each validator has available to stake - BondedTokens math.Int // the amount of tokens each validator stakes - SignedBlocksWindow int64 // slashing signed blocks window - NumValidators int // the total number of validators to create and bond - NumBondedValidators int // the total number of validators with bonded tokens to create - ChainID string // the network chain-id - BondDenom string // the staking bond denomination - TokenDenom string // the fees token denomination - UnBoundingTime time.Duration // the time to unbound and accept governance proposals - MinGasPrices string // the minimum gas prices each validator will accept - PruningStrategy string // the pruning strategy each validator will have - SigningAlgo string // signing algorithm for keys - RPCAddress string // RPC listen address (including port) - JSONRPCAddress string // JSON-RPC listen address (including port) - APIAddress string // REST API listen address (including port) - GRPCAddress string // GRPC server listen address (including port) - EnableTMLogging bool // enable Tendermint logging to STDOUT - CleanupDir bool // remove base temporary directory during cleanup - PrintMnemonic bool // print the mnemonic of first validator as log output for testing -} - -// DefaultConfig returns a sane default configuration suitable for nearly all -// testing requirements. -func DefaultConfig(numValidators int, numBondedValidators int, blockTime time.Duration, unbondingBlocks int64) Config { - encCfg := app.MakeEncodingConfig() - chainID := fmt.Sprintf("exrp_%d-1", tmrand.Int63n(9999999999999)+1) - return Config{ - Codec: encCfg.Codec, - TxConfig: encCfg.TxConfig, - LegacyAmino: encCfg.Amino, - InterfaceRegistry: encCfg.InterfaceRegistry, - AccountRetriever: authtypes.AccountRetriever{}, - AppConstructor: NewAppConstructor(encCfg, chainID), - GenesisState: app.ModuleBasics.DefaultGenesis(encCfg.Codec), - TimeoutCommit: blockTime, - ChainID: chainID, - NumValidators: numValidators, - NumBondedValidators: numBondedValidators, - BondDenom: "apoa", - MinGasPrices: fmt.Sprintf("0.000006%s", "apoa"), - AccountTokens: sdk.TokensFromConsensusPower(1000000000000000000, sdk.DefaultPowerReduction), - StakingTokens: sdk.TokensFromConsensusPower(1, sdk.DefaultPowerReduction), - BondedTokens: sdk.TokensFromConsensusPower(1, sdk.DefaultPowerReduction), - TokenDenom: "axrp", - UnBoundingTime: (time.Duration(unbondingBlocks) * blockTime) + time.Second, - PruningStrategy: pruningtypes.PruningOptionNothing, - CleanupDir: os.Getenv("TEST_CLEANUP_DIR") != "false", - SigningAlgo: string(hd.EthSecp256k1Type), - KeyringOptions: []keyring.Option{hd.EthSecp256k1Option()}, - PrintMnemonic: false, - EnableTMLogging: true, - SignedBlocksWindow: 2, - } -} \ No newline at end of file diff --git a/e2e/manager/network/logger.go b/e2e/manager/network/logger.go deleted file mode 100644 index ce355ad..0000000 --- a/e2e/manager/network/logger.go +++ /dev/null @@ -1,8 +0,0 @@ -package network - -// Logger is a network logger interface that exposes testnet-level Log() methods for an in-process testing network -// This is not to be confused with logging that may happen at an individual node or validator level -type Logger interface { - Log(args ...interface{}) - Logf(format string, args ...interface{}) -} \ No newline at end of file diff --git a/e2e/manager/network/manager.go b/e2e/manager/network/manager.go deleted file mode 100644 index 4adf1ef..0000000 --- a/e2e/manager/network/manager.go +++ /dev/null @@ -1,120 +0,0 @@ -package network - -import ( - "context" - "errors" - "sync" - "testing" - "time" - - "github.com/ethereum/go-ethereum/ethclient" - "github.com/evmos/evmos/v19/server/config" - "github.com/stretchr/testify/require" -) - -// package-wide network lock to only allow one test network at a time -var lock = new(sync.Mutex) - -type NetworkManager struct { - - network *Network -} - -func NewManager(t *testing.T, numValidators int, numBondedValidators int, blockTime time.Duration, unbondingBlocks int64, nodeDir string) *NetworkManager { - cfg := DefaultConfig(numValidators, numBondedValidators, blockTime, unbondingBlocks) - - network, err := NewNetwork(t, nodeDir, cfg) - require.NoError(t, err) - - return &NetworkManager{network} -} - -func (m *NetworkManager) JSONRPCClient() *ethclient.Client { - return m.network.Validators[0].JSONRPCClient -} - -func (m *NetworkManager) SetJSONRPCClient(client *ethclient.Client) { - m.network.Validators[0].JSONRPCClient = client -} - -func (m *NetworkManager) Cleanup() { - m.network.Cleanup() -} - -// LatestHeight returns the latest height of the network or an error if the -// query fails or no validators exist. -func (m *NetworkManager) LatestHeight() (int64, error) { - if len(m.network.Validators) == 0 { - return 0, errors.New("no validators available") - } - - status, err := m.network.Validators[0].RPCClient.Status(context.Background()) - if err != nil { - return 0, err - } - - return status.SyncInfo.LatestBlockHeight, nil -} - -// WaitForHeight performs a blocking check where it waits for a block to be -// committed after a given block. If that height is not reached within a timeout, -// an error is returned. Regardless, the latest height queried is returned. -func (m *NetworkManager) WaitForHeight(h int64) (int64, error) { - return m.WaitForHeightWithTimeout(h, 10*time.Second) -} - -// WaitForHeightWithTimeout is the same as WaitForHeight except the caller can -// provide a custom timeout. -func (m *NetworkManager) WaitForHeightWithTimeout(h int64, t time.Duration) (int64, error) { - ticker := time.NewTicker(time.Second) - timeout := time.After(t) - - if len(m.network.Validators) == 0 { - return 0, errors.New("no validators available") - } - - var latestHeight int64 - val := m.network.Validators[0] - - for { - select { - case <-timeout: - ticker.Stop() - return latestHeight, errors.New("timeout exceeded waiting for block") - case <-ticker.C: - status, err := val.RPCClient.Status(context.Background()) - if err == nil && status != nil { - latestHeight = status.SyncInfo.LatestBlockHeight - if latestHeight >= h { - return latestHeight, nil - } - } - } - } -} - -// WaitForNextBlock waits for the next block to be committed, returning an error -// upon failure. -func (m *NetworkManager) WaitForNextBlock() error { - lastBlock, err := m.LatestHeight() - if err != nil { - return err - } - - _, err = m.WaitForHeight(lastBlock + 1) - if err != nil { - return err - } - - return err -} - -func (m *NetworkManager) MustWaitForNextBlock() { - if err := m.WaitForNextBlock(); err != nil { - panic(err) - } -} - -func (m *NetworkManager) AppConfig() *config.Config { - return m.network.Validators[0].AppConfig -} diff --git a/e2e/manager/network/network.go b/e2e/manager/network/network.go deleted file mode 100644 index d695d4b..0000000 --- a/e2e/manager/network/network.go +++ /dev/null @@ -1,424 +0,0 @@ -package network - -import ( - "bufio" - "context" - "encoding/json" - "fmt" - "net/url" - "os" - "path/filepath" - "strings" - "time" - - tmcfg "github.com/cometbft/cometbft/config" - tmflags "github.com/cometbft/cometbft/libs/cli/flags" - "github.com/cometbft/cometbft/libs/log" - "github.com/ethereum/go-ethereum/common" - - "github.com/cosmos/cosmos-sdk/client" - "github.com/cosmos/cosmos-sdk/client/tx" - "github.com/cosmos/cosmos-sdk/crypto/keyring" - cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" - "github.com/cosmos/cosmos-sdk/server" - srvconfig "github.com/cosmos/cosmos-sdk/server/config" - "github.com/cosmos/cosmos-sdk/testutil" - 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/cosmos/cosmos-sdk/x/genutil" - stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" - - "github.com/evmos/evmos/v19/server/config" - evmostypes "github.com/evmos/evmos/v19/types" - evmtypes "github.com/evmos/evmos/v19/x/evm/types" -) - -// Network defines a local in-process testing network using SimApp. It can be -// configured to start any number of validators, each with its own RPC and API -// clients. Typically, this test network would be used in client and integration -// testing where user input is expected. -// -// Note, due to Tendermint constraints in regards to RPC functionality, there -// may only be one test network running at a time. Thus, any caller must be -// sure to Cleanup after testing is finished in order to allow other tests -// to create networks. In addition, only the first validator will have a valid -// RPC and API server/client. -type Network struct { - Logger Logger - BaseDir string - Validators []*Validator - - Config Config -} - -func NewNetwork(l Logger, baseDir string, cfg Config) (*Network, error) { - lock.Lock() - - if !evmostypes.IsValidChainID(cfg.ChainID) { - return nil, fmt.Errorf("invalid chain-id: %s", cfg.ChainID) - } - - network := &Network{ - Logger: l, - BaseDir: baseDir, - Validators: make([]*Validator, cfg.NumValidators), - Config: cfg, - } - - l.Logf("preparing test network with chain-id \"%s\"\n", cfg.ChainID) - - monikers := make([]string, cfg.NumValidators) - nodeIDs := make([]string, cfg.NumValidators) - valPubKeys := make([]cryptotypes.PubKey, cfg.NumValidators) - - var ( - genAccounts []authtypes.GenesisAccount - genBalances []banktypes.Balance - genFiles []string - ) - - buf := bufio.NewReader(os.Stdin) - - // generate private keys, node IDs, and initial transactions - for i := 0; i < cfg.NumValidators; i++ { - appCfg := config.DefaultConfig() - appCfg.Pruning = cfg.PruningStrategy - appCfg.MinGasPrices = cfg.MinGasPrices - appCfg.API.Enable = true - appCfg.API.Swagger = false - appCfg.Telemetry.Enabled = false - appCfg.Telemetry.GlobalLabels = [][]string{{"chain_id", cfg.ChainID}} - - ctx := server.NewDefaultContext() - tmCfg := ctx.Config - tmCfg.Consensus.TimeoutCommit = cfg.TimeoutCommit - - // Only allow the first validator to expose an RPC, API and gRPC - // server/client due to Tendermint in-process constraints. - apiAddr := "" - tmCfg.RPC.ListenAddress = "" - appCfg.GRPC.Enable = false - appCfg.GRPCWeb.Enable = false - appCfg.JSONRPC.Enable = false - apiListenAddr := "" - - if cfg.APIAddress != "" { - apiListenAddr = cfg.APIAddress - } else { - var err error - apiListenAddr, _, err = server.FreeTCPAddr() - if err != nil { - return nil, err - } - } - - appCfg.API.Address = apiListenAddr - apiURL, err := url.Parse(apiListenAddr) - if err != nil { - return nil, err - } - apiAddr = fmt.Sprintf("http://%s:%s", apiURL.Hostname(), apiURL.Port()) - - if cfg.RPCAddress != "" { - tmCfg.RPC.ListenAddress = cfg.RPCAddress - } else { - rpcAddr, _, err := server.FreeTCPAddr() - if err != nil { - return nil, err - } - tmCfg.RPC.ListenAddress = rpcAddr - } - - if cfg.GRPCAddress != "" { - appCfg.GRPC.Address = cfg.GRPCAddress - } else { - _, grpcPort, err := server.FreeTCPAddr() - if err != nil { - return nil, err - } - appCfg.GRPC.Address = fmt.Sprintf("0.0.0.0:%s", grpcPort) - } - appCfg.GRPC.Enable = false - - _, grpcWebPort, err := server.FreeTCPAddr() - if err != nil { - return nil, err - } - appCfg.GRPCWeb.Address = fmt.Sprintf("0.0.0.0:%s", grpcWebPort) - appCfg.GRPCWeb.Enable = false - - if cfg.JSONRPCAddress != "" { - appCfg.JSONRPC.Address = cfg.JSONRPCAddress - } else { - _, jsonRPCPort, err := server.FreeTCPAddr() - if err != nil { - return nil, err - } - appCfg.JSONRPC.Address = fmt.Sprintf("0.0.0.0:%s", jsonRPCPort) - } - appCfg.JSONRPC.Enable = false - appCfg.JSONRPC.API = config.GetAPINamespaces() - - logger := log.NewNopLogger() - if cfg.EnableTMLogging && i == 0 { - logger = log.NewTMLogger(log.NewSyncWriter(os.Stdout)) - logger, _ = tmflags.ParseLogLevel("info", logger, tmcfg.DefaultLogLevel) - } - - ctx.Logger = logger - - nodeDirName := fmt.Sprintf("node%d", i) - nodeDir := filepath.Join(network.BaseDir, nodeDirName, "evmosd") - clientDir := filepath.Join(network.BaseDir, nodeDirName, "evmoscli") - gentxsDir := filepath.Join(network.BaseDir, "gentxs") - - err = os.MkdirAll(filepath.Join(nodeDir, "config"), 0o750) - if err != nil { - return nil, err - } - - err = os.MkdirAll(clientDir, 0o750) - if err != nil { - return nil, err - } - - tmCfg.SetRoot(nodeDir) - tmCfg.Moniker = nodeDirName - monikers[i] = nodeDirName - - proxyAddr, _, err := server.FreeTCPAddr() - if err != nil { - return nil, err - } - tmCfg.ProxyApp = proxyAddr - - p2pAddr, _, err := server.FreeTCPAddr() - if err != nil { - return nil, err - } - tmCfg.P2P.ListenAddress = p2pAddr - tmCfg.P2P.AddrBookStrict = false - tmCfg.P2P.AllowDuplicateIP = true - - nodeID, pubKey, err := genutil.InitializeNodeValidatorFiles(tmCfg) - if err != nil { - return nil, err - } - nodeIDs[i] = nodeID - valPubKeys[i] = pubKey - - kb, err := keyring.New(sdk.KeyringServiceName(), keyring.BackendTest, clientDir, buf, cfg.Codec, cfg.KeyringOptions...) - if err != nil { - return nil, err - } - - keyringAlgos, _ := kb.SupportedAlgorithms() - algo, err := keyring.NewSigningAlgoFromString(cfg.SigningAlgo, keyringAlgos) - if err != nil { - return nil, err - } - - addr, secret, err := testutil.GenerateSaveCoinKey(kb, nodeDirName, "", true, algo) - if err != nil { - return nil, err - } - - // if PrintMnemonic is set to true, we print the first validator node's secret to the network's logger - // for debugging and manual testing - if cfg.PrintMnemonic && i == 0 { - printMnemonic(l, secret) - } - - info := map[string]string{"secret": secret} - infoBz, err := json.Marshal(info) - if err != nil { - return nil, err - } - - // save private key seed words - err = WriteFile(fmt.Sprintf("%v.json", "key_seed"), clientDir, infoBz) - if err != nil { - return nil, err - } - - balances := sdk.NewCoins( - sdk.NewCoin(cfg.TokenDenom, cfg.AccountTokens), - sdk.NewCoin(cfg.BondDenom, cfg.StakingTokens), - ) - - genFiles = append(genFiles, tmCfg.GenesisFile()) - genBalances = append(genBalances, banktypes.Balance{Address: addr.String(), Coins: balances.Sort()}) - genAccounts = append(genAccounts, &evmostypes.EthAccount{ - BaseAccount: authtypes.NewBaseAccount(addr, nil, 0, 0), - CodeHash: common.BytesToHash(evmtypes.EmptyCodeHash).Hex(), - }) - - commission, err := sdk.NewDecFromStr("0.5") - if err != nil { - return nil, err - } - - if i < cfg.NumBondedValidators { - - createValMsg, err := stakingtypes.NewMsgCreateValidator( - sdk.ValAddress(addr), - valPubKeys[i], - sdk.NewCoin(cfg.BondDenom, cfg.BondedTokens), - stakingtypes.NewDescription(nodeDirName, "", "", "", ""), - stakingtypes.NewCommissionRates(commission, sdk.OneDec(), sdk.OneDec()), - sdk.OneInt(), - ) - if err != nil { - return nil, err - } - - p2pURL, err := url.Parse(p2pAddr) - if err != nil { - return nil, err - } - - memo := fmt.Sprintf("%s@%s:%s", nodeIDs[i], p2pURL.Hostname(), p2pURL.Port()) - fee := sdk.NewCoins(sdk.NewCoin(cfg.BondDenom, sdk.NewInt(0))) - txBuilder := cfg.TxConfig.NewTxBuilder() - err = txBuilder.SetMsgs(createValMsg) - if err != nil { - return nil, err - } - txBuilder.SetFeeAmount(fee) // Arbitrary fee - txBuilder.SetGasLimit(1000000) // Need at least 100386 - txBuilder.SetMemo(memo) - - txFactory := tx.Factory{} - txFactory = txFactory. - WithChainID(cfg.ChainID). - WithMemo(memo). - WithKeybase(kb). - WithTxConfig(cfg.TxConfig) - - if err := tx.Sign(txFactory, nodeDirName, txBuilder, true); err != nil { - return nil, err - } - - txBz, err := cfg.TxConfig.TxJSONEncoder()(txBuilder.GetTx()) - if err != nil { - return nil, err - } - - if err := WriteFile(fmt.Sprintf("%v.json", nodeDirName), gentxsDir, txBz); err != nil { - return nil, err - } - } - - customAppTemplate, _ := config.AppConfig(cfg.TokenDenom) - srvconfig.SetConfigTemplate(customAppTemplate) - srvconfig.WriteConfigFile(filepath.Join(nodeDir, "config/app.toml"), appCfg) - - ctx.Viper.SetEnvKeyReplacer(strings.NewReplacer(".", "_", "-", "_")) - ctx.Viper.SetConfigFile(filepath.Join(nodeDir, "config/app.toml")) - err = ctx.Viper.ReadInConfig() - if err != nil { - return nil, err - } - - clientCtx := client.Context{}. - WithKeyringDir(clientDir). - WithKeyring(kb). - WithHomeDir(tmCfg.RootDir). - WithChainID(cfg.ChainID). - WithInterfaceRegistry(cfg.InterfaceRegistry). - WithCodec(cfg.Codec). - WithLegacyAmino(cfg.LegacyAmino). - WithTxConfig(cfg.TxConfig). - WithAccountRetriever(cfg.AccountRetriever) - - network.Validators[i] = &Validator{ - AppConfig: appCfg, - ClientCtx: clientCtx, - Ctx: ctx, - Dir: filepath.Join(network.BaseDir, nodeDirName), - NodeID: nodeID, - PubKey: pubKey, - Moniker: nodeDirName, - RPCAddress: tmCfg.RPC.ListenAddress, - P2PAddress: tmCfg.P2P.ListenAddress, - APIAddress: apiAddr, - Address: addr, - ValAddress: sdk.ValAddress(addr), - } - } - - err := initGenFiles(cfg, genAccounts, genBalances, genFiles) - if err != nil { - return nil, err - } - err = collectGenFiles(cfg, network.Validators, network.BaseDir) - if err != nil { - return nil, err - } - - l.Log("starting test network...") - for _, v := range network.Validators { - err := startInProcess(cfg, v) - if err != nil { - return nil, err - } - } - - l.Log("started test network") - - // Ensure we cleanup incase any test was abruptly halted (e.g. SIGINT) as any - // defer in a test would not be called. - server.TrapSignal(network.Cleanup) - - return network, nil -} - -func (n *Network) Cleanup() { - defer func() { - time.Sleep(10 * time.Second) - lock.Unlock() - n.Logger.Log("released test network lock") - }() - - n.Logger.Log("cleaning up test network...") - - for _, v := range n.Validators { - if v.TmNode != nil && v.TmNode.IsRunning() { - _ = v.TmNode.Stop() - } - - if v.api != nil { - _ = v.api.Close() - } - - if v.grpc != nil { - v.grpc.Stop() - if v.grpcWeb != nil { - _ = v.grpcWeb.Close() - } - } - - if v.jsonrpc != nil { - shutdownCtx, cancelFn := context.WithTimeout(context.Background(), 10*time.Second) - defer cancelFn() - - if err := v.jsonrpc.Shutdown(shutdownCtx); err != nil { - v.TmNode.Logger.Error("HTTP server shutdown produced a warning", "error", err.Error()) - } else { - v.TmNode.Logger.Info("HTTP server shut down, waiting 5 sec") - select { - case <-time.NewTicker(5 * time.Second).C: - case <-v.jsonrpcDone: - } - } - } - } - - if n.Config.CleanupDir { - time.Sleep(10 * time.Second) - _ = os.RemoveAll(n.BaseDir) - } - - n.Logger.Log("finished cleaning up test network") -} diff --git a/e2e/manager/network/utils.go b/e2e/manager/network/utils.go deleted file mode 100644 index 98a6a2d..0000000 --- a/e2e/manager/network/utils.go +++ /dev/null @@ -1,319 +0,0 @@ -package network - -import ( - "encoding/json" - "fmt" - "path/filepath" - "strings" - "time" - - "github.com/cometbft/cometbft/libs/log" - sdk "github.com/cosmos/cosmos-sdk/types" - slashingtypes "github.com/cosmos/cosmos-sdk/x/slashing/types" - - tmos "github.com/cometbft/cometbft/libs/os" - "github.com/cometbft/cometbft/node" - "github.com/cometbft/cometbft/p2p" - pvm "github.com/cometbft/cometbft/privval" - "github.com/cometbft/cometbft/proxy" - "github.com/cometbft/cometbft/rpc/client/local" - "github.com/cometbft/cometbft/types" - tmtime "github.com/cometbft/cometbft/types/time" - "github.com/ethereum/go-ethereum/ethclient" - - "github.com/cosmos/cosmos-sdk/server/api" - servergrpc "github.com/cosmos/cosmos-sdk/server/grpc" - srvtypes "github.com/cosmos/cosmos-sdk/server/types" - authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" - banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" - crisistypes "github.com/cosmos/cosmos-sdk/x/crisis/types" - "github.com/cosmos/cosmos-sdk/x/genutil" - genutiltypes "github.com/cosmos/cosmos-sdk/x/genutil/types" - govtypes "github.com/cosmos/cosmos-sdk/x/gov/types" - govv1 "github.com/cosmos/cosmos-sdk/x/gov/types/v1" - stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" - - "github.com/evmos/evmos/v19/server" - evmtypes "github.com/evmos/evmos/v19/x/evm/types" -) - -type FmtLogger struct { - log.Logger -} - -func startInProcess(cfg Config, val *Validator) error { - logger := val.Ctx.Logger - tmCfg := val.Ctx.Config - tmCfg.Instrumentation.Prometheus = false - - if err := val.AppConfig.ValidateBasic(); err != nil { - return err - } - - nodeKey, err := p2p.LoadOrGenNodeKey(tmCfg.NodeKeyFile()) - if err != nil { - return err - } - - app := cfg.AppConstructor(*val) - - genDocProvider := node.DefaultGenesisDocProviderFunc(tmCfg) - tmNode, err := node.NewNode( - tmCfg, - pvm.LoadOrGenFilePV(tmCfg.PrivValidatorKeyFile(), tmCfg.PrivValidatorStateFile()), - nodeKey, - proxy.NewLocalClientCreator(app), - genDocProvider, - node.DefaultDBProvider, - node.DefaultMetricsProvider(tmCfg.Instrumentation), - logger.With("module", val.Moniker), - ) - if err != nil { - return err - } - - if err := tmNode.Start(); err != nil { - return err - } - - val.TmNode = tmNode - - if val.RPCAddress != "" { - val.RPCClient = local.New(tmNode) - } - - // We'll need a RPC client if the validator exposes a gRPC or REST endpoint. - if val.APIAddress != "" || val.AppConfig.GRPC.Enable { - val.ClientCtx = val.ClientCtx. - WithClient(val.RPCClient) - - // Add the tx service in the gRPC router. - app.RegisterTxService(val.ClientCtx) - - // Add the tendermint queries service in the gRPC router. - app.RegisterTendermintService(val.ClientCtx) - } - - if val.AppConfig.API.Enable && val.APIAddress != "" { - apiSrv := api.New(val.ClientCtx, logger.With("module", "api-server")) - app.RegisterAPIRoutes(apiSrv, val.AppConfig.API) - - errCh := make(chan error) - - go func() { - if err := apiSrv.Start(val.AppConfig.Config); err != nil { - errCh <- err - } - }() - - select { - case err := <-errCh: - return err - case <-time.After(srvtypes.ServerStartTime): // assume server started successfully - } - - val.api = apiSrv - } - - if val.AppConfig.GRPC.Enable { - grpcSrv, err := servergrpc.StartGRPCServer(val.ClientCtx, app, val.AppConfig.GRPC) - if err != nil { - return err - } - - val.grpc = grpcSrv - - if val.AppConfig.GRPCWeb.Enable { - val.grpcWeb, err = servergrpc.StartGRPCWeb(grpcSrv, val.AppConfig.Config) - if err != nil { - return err - } - } - } - - if val.AppConfig.JSONRPC.Enable && val.AppConfig.JSONRPC.Address != "" { - if val.Ctx == nil || val.Ctx.Viper == nil { - return fmt.Errorf("validator %s context is nil", val.Moniker) - } - - tmEndpoint := "/websocket" - tmRPCAddr := val.RPCAddress - - val.jsonrpc, val.jsonrpcDone, err = server.StartJSONRPC(val.Ctx, val.ClientCtx, tmRPCAddr, tmEndpoint, val.AppConfig, nil) - if err != nil { - return err - } - - address := fmt.Sprintf("http://%s", val.AppConfig.JSONRPC.Address) - - val.JSONRPCClient, err = ethclient.Dial(address) - if err != nil { - return fmt.Errorf("failed to dial JSON-RPC at %s: %w", val.AppConfig.JSONRPC.Address, err) - } - } - - return nil -} - -func collectGenFiles(cfg Config, vals []*Validator, outputDir string) error { - genTime := tmtime.Now() - - for i := 0; i < cfg.NumValidators; i++ { - tmCfg := vals[i].Ctx.Config - - nodeDir := filepath.Join(outputDir, vals[i].Moniker, "evmosd") - gentxsDir := filepath.Join(outputDir, "gentxs") - - tmCfg.Moniker = vals[i].Moniker - tmCfg.SetRoot(nodeDir) - - initCfg := genutiltypes.NewInitConfig(cfg.ChainID, gentxsDir, vals[i].NodeID, vals[i].PubKey) - - genFile := tmCfg.GenesisFile() - genDoc, err := types.GenesisDocFromFile(genFile) - if err != nil { - return err - } - - appState, err := genutil.GenAppStateFromConfig(cfg.Codec, cfg.TxConfig, - tmCfg, initCfg, *genDoc, banktypes.GenesisBalancesIterator{}, genutiltypes.DefaultMessageValidator) - if err != nil { - return err - } - - // overwrite each validator's genesis file to have a canonical genesis time - if err := genutil.ExportGenesisFileWithTime(genFile, cfg.ChainID, nil, appState, genTime); err != nil { - return err - } - } - - return nil -} - -func initGenFiles(cfg Config, genAccounts []authtypes.GenesisAccount, genBalances []banktypes.Balance, genFiles []string) error { - // set the accounts in the genesis state - var authGenState authtypes.GenesisState - cfg.Codec.MustUnmarshalJSON(cfg.GenesisState[authtypes.ModuleName], &authGenState) - - accounts, err := authtypes.PackAccounts(genAccounts) - if err != nil { - return err - } - - authGenState.Accounts = append(authGenState.Accounts, accounts...) - cfg.GenesisState[authtypes.ModuleName] = cfg.Codec.MustMarshalJSON(&authGenState) - - // set the balances in the genesis state - var bankGenState banktypes.GenesisState - cfg.Codec.MustUnmarshalJSON(cfg.GenesisState[banktypes.ModuleName], &bankGenState) - - bankGenState.Balances = genBalances - cfg.GenesisState[banktypes.ModuleName] = cfg.Codec.MustMarshalJSON(&bankGenState) - - var stakingGenState stakingtypes.GenesisState - cfg.Codec.MustUnmarshalJSON(cfg.GenesisState[stakingtypes.ModuleName], &stakingGenState) - - stakingGenState.Params.BondDenom = cfg.BondDenom - stakingGenState.Params.UnbondingTime = cfg.UnBoundingTime - cfg.GenesisState[stakingtypes.ModuleName] = cfg.Codec.MustMarshalJSON(&stakingGenState) - - var govGenState govv1.GenesisState - cfg.Codec.MustUnmarshalJSON(cfg.GenesisState[govtypes.ModuleName], &govGenState) - - govGenState.Params.MinDeposit[0].Denom = cfg.TokenDenom - govGenState.Params.VotingPeriod = &cfg.UnBoundingTime - cfg.GenesisState[govtypes.ModuleName] = cfg.Codec.MustMarshalJSON(&govGenState) - - var crisisGenState crisistypes.GenesisState - cfg.Codec.MustUnmarshalJSON(cfg.GenesisState[crisistypes.ModuleName], &crisisGenState) - - crisisGenState.ConstantFee.Denom = cfg.BondDenom - cfg.GenesisState[crisistypes.ModuleName] = cfg.Codec.MustMarshalJSON(&crisisGenState) - - var evmGenState evmtypes.GenesisState - cfg.Codec.MustUnmarshalJSON(cfg.GenesisState[evmtypes.ModuleName], &evmGenState) - - evmGenState.Params.EvmDenom = cfg.TokenDenom - cfg.GenesisState[evmtypes.ModuleName] = cfg.Codec.MustMarshalJSON(&evmGenState) - - var slashingGenState slashingtypes.GenesisState - cfg.Codec.MustUnmarshalJSON(cfg.GenesisState[slashingtypes.ModuleName], &slashingGenState) - - slashingGenState.Params.SignedBlocksWindow = cfg.SignedBlocksWindow - slashingGenState.Params.SlashFractionDowntime = sdk.ZeroDec() - cfg.GenesisState[slashingtypes.ModuleName] = cfg.Codec.MustMarshalJSON(&slashingGenState) - - appGenStateJSON, err := json.MarshalIndent(cfg.GenesisState, "", " ") - if err != nil { - return err - } - - genDoc := types.GenesisDoc{ - ChainID: cfg.ChainID, - AppState: appGenStateJSON, - Validators: nil, - } - - // generate empty genesis files for each validator and save - for i := 0; i < cfg.NumValidators; i++ { - if err := genDoc.SaveAs(genFiles[i]); err != nil { - return err - } - } - - return nil -} - -func WriteFile(name string, dir string, contents []byte) error { - file := filepath.Join(dir, name) - - err := tmos.EnsureDir(dir, 0o755) - if err != nil { - return err - } - - return tmos.WriteFile(file, contents, 0o644) -} - - -// printMnemonic prints a provided mnemonic seed phrase on a network logger -// for debugging and manual testing -func printMnemonic(l Logger, secret string) { - lines := []string{ - "THIS MNEMONIC IS FOR TESTING PURPOSES ONLY", - "DO NOT USE IN PRODUCTION", - "", - strings.Join(strings.Fields(secret)[0:8], " "), - strings.Join(strings.Fields(secret)[8:16], " "), - strings.Join(strings.Fields(secret)[16:24], " "), - } - - lineLengths := make([]int, len(lines)) - for i, line := range lines { - lineLengths[i] = len(line) - } - - maxLineLength := 0 - for _, lineLen := range lineLengths { - if lineLen > maxLineLength { - maxLineLength = lineLen - } - } - - l.Log("\n") - l.Log(strings.Repeat("+", maxLineLength+8)) - for _, line := range lines { - l.Logf("++ %s ++\n", centerText(line, maxLineLength)) - } - l.Log(strings.Repeat("+", maxLineLength+8)) - l.Log("\n") -} - -// centerText centers text across a fixed width, filling either side with whitespace buffers -func centerText(text string, width int) string { - textLen := len(text) - leftBuffer := strings.Repeat(" ", (width-textLen)/2) - rightBuffer := strings.Repeat(" ", (width-textLen)/2+(width-textLen)%2) - - return fmt.Sprintf("%s%s%s", leftBuffer, text, rightBuffer) -} diff --git a/e2e/manager/network/validator.go b/e2e/manager/network/validator.go deleted file mode 100644 index 387ad89..0000000 --- a/e2e/manager/network/validator.go +++ /dev/null @@ -1,42 +0,0 @@ -package network - -import ( - "net/http" - - "github.com/cometbft/cometbft/node" - cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" - "github.com/ethereum/go-ethereum/ethclient" - "google.golang.org/grpc" - - "github.com/cosmos/cosmos-sdk/client" - "github.com/cosmos/cosmos-sdk/server" - - tmclient "github.com/cometbft/cometbft/rpc/client" - "github.com/cosmos/cosmos-sdk/server/api" - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/evmos/evmos/v19/server/config" -) - -type Validator struct { - AppConfig *config.Config - ClientCtx client.Context - Ctx *server.Context - Dir string - NodeID string - PubKey cryptotypes.PubKey - Moniker string - APIAddress string - RPCAddress string - P2PAddress string - Address sdk.AccAddress - ValAddress sdk.ValAddress - RPCClient tmclient.Client - JSONRPCClient *ethclient.Client - - TmNode *node.Node - api *api.Server - grpc *grpc.Server - grpcWeb *http.Server - jsonrpc *http.Server - jsonrpcDone chan struct{} -} \ No newline at end of file diff --git a/e2e/modules/poa.go b/e2e/modules/poa.go deleted file mode 100644 index a62643f..0000000 --- a/e2e/modules/poa.go +++ /dev/null @@ -1,17 +0,0 @@ -package modules - -import ( - "github.com/xrplevm/node/v3/testutil/network" - - cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" - sdk "github.com/cosmos/cosmos-sdk/types" - govtypesv1 "github.com/cosmos/cosmos-sdk/x/gov/types/v1" -) - -type PoaOps interface { - ChangeValidator(action int, address sdk.AccAddress, pubKey cryptotypes.PubKey, validators []*network.Validator, waitStatus govtypesv1.ProposalStatus) - BondTokens(validator *network.Validator, tokens sdk.Int) - UnbondTokens(validator *network.Validator, tokens sdk.Int, wait bool) string - Delegate(validator *network.Validator, tokens sdk.Int) string - Redelegate(src *network.Validator, dst *network.Validator, tokens sdk.Int) string -} diff --git a/e2e/suite.go b/e2e/suite.go deleted file mode 100644 index 039bd87..0000000 --- a/e2e/suite.go +++ /dev/null @@ -1,40 +0,0 @@ -package e2e - -import ( - "fmt" - "time" - - "github.com/ethereum/go-ethereum/ethclient" - "github.com/stretchr/testify/suite" - "github.com/xrplevm/node/v3/e2e/manager" -) - -type End2EndSuite struct { - suite.Suite - - manager manager.Manager -} - - -func (s *End2EndSuite) SetManager(manager manager.Manager) { - s.T().Log("setting up test suite manager") - - s.manager = manager - s.Require().NotNil(s.manager) - - // Load state - if s.manager.JSONRPCClient() == nil { - address := fmt.Sprintf("http://%s", s.manager.AppConfig().JSONRPC.Address) - client, err := ethclient.Dial(address) - s.Require().NoError(err) - s.manager.SetJSONRPCClient(client) - } - - s.T().Log("test suite manager setup successfully") -} - -func (s *End2EndSuite) TearDownTest() { - s.T().Log("tearing down test suite") - s.manager.Cleanup() - time.Sleep(5 * time.Second) -} diff --git a/e2e/suite_test.go b/e2e/suite_test.go deleted file mode 100644 index b040fd0..0000000 --- a/e2e/suite_test.go +++ /dev/null @@ -1,18 +0,0 @@ -package e2e - -import ( - "testing" - "time" - - "github.com/stretchr/testify/suite" - "github.com/xrplevm/node/v3/e2e/manager/network" -) - -func (s *End2EndSuite) SetupSuite() { - networkManager := network.NewManager(s.T(), 5, 4, 3*time.Second, 5, s.T().TempDir()) - s.SetManager(networkManager) -} - -func Test_Suite(t *testing.T) { - suite.Run(t, new(End2EndSuite)) -} \ No newline at end of file From 48a00d304524a884201d15adcd077f703d22ddbe Mon Sep 17 00:00:00 2001 From: GuillemGarciaDev Date: Wed, 20 Nov 2024 15:38:43 +0100 Subject: [PATCH 03/60] feat(test/integration): import integration test utils --- app/app.go | 1 + tests/upgrade/network.go | 19 + tests/upgrade/suite.go | 36 ++ tests/upgrade/suite_test.go | 18 + .../integration/common/network/network.go | 53 ++ testutil/integration/exrp/network/abci.go | 100 +++ .../exrp/network/chain_id_modifiers.go | 121 ++++ testutil/integration/exrp/network/clients.go | 102 ++++ testutil/integration/exrp/network/config.go | 155 +++++ .../integration/exrp/network/config_test.go | 51 ++ testutil/integration/exrp/network/ibc.go | 28 + testutil/integration/exrp/network/network.go | 345 +++++++++++ testutil/integration/exrp/network/setup.go | 574 ++++++++++++++++++ .../integration/exrp/network/unit_network.go | 56 ++ 14 files changed, 1659 insertions(+) create mode 100644 tests/upgrade/network.go create mode 100644 tests/upgrade/suite.go create mode 100644 tests/upgrade/suite_test.go create mode 100644 testutil/integration/common/network/network.go create mode 100644 testutil/integration/exrp/network/abci.go create mode 100644 testutil/integration/exrp/network/chain_id_modifiers.go create mode 100644 testutil/integration/exrp/network/clients.go create mode 100644 testutil/integration/exrp/network/config.go create mode 100644 testutil/integration/exrp/network/config_test.go create mode 100644 testutil/integration/exrp/network/ibc.go create mode 100644 testutil/integration/exrp/network/network.go create mode 100644 testutil/integration/exrp/network/setup.go create mode 100644 testutil/integration/exrp/network/unit_network.go diff --git a/app/app.go b/app/app.go index 2166114..4cecbad 100644 --- a/app/app.go +++ b/app/app.go @@ -137,6 +137,7 @@ import ( "github.com/evmos/evmos/v20/x/feemarket" feemarketkeeper "github.com/evmos/evmos/v20/x/feemarket/keeper" feemarkettypes "github.com/evmos/evmos/v20/x/feemarket/types" + // Overriders "github.com/evmos/evmos/v20/x/ibc/transfer" ibctransferkeeper "github.com/evmos/evmos/v20/x/ibc/transfer/keeper" diff --git a/tests/upgrade/network.go b/tests/upgrade/network.go new file mode 100644 index 0000000..d47dcfc --- /dev/null +++ b/tests/upgrade/network.go @@ -0,0 +1,19 @@ +package testupgrade + +import ( + commonnetwork "github.com/xrplevm/node/v4/testutil/integration/common/network" + exrpnetwork "github.com/xrplevm/node/v4/testutil/integration/exrp/network" +) + +var _ commonnetwork.Network = (*UpgradeTestNetwork)(nil) + +type UpgradeTestNetwork struct { + exrpnetwork.IntegrationNetwork +} + +func NewUpgradeTestNetwork(opts ...exrpnetwork.ConfigOption) *UpgradeTestNetwork { + network := exrpnetwork.New(opts...) + return &UpgradeTestNetwork{ + IntegrationNetwork: *network, + } +} diff --git a/tests/upgrade/suite.go b/tests/upgrade/suite.go new file mode 100644 index 0000000..90d0e7b --- /dev/null +++ b/tests/upgrade/suite.go @@ -0,0 +1,36 @@ +package testupgrade + +import ( + "encoding/json" + "os" + + "github.com/stretchr/testify/suite" + exrpnetwork "github.com/xrplevm/node/v4/testutil/integration/exrp/network" +) + +type UpgradeTestSuite struct { + suite.Suite + + network *UpgradeTestNetwork +} + +func (s *UpgradeTestSuite) SetupTest() { + // READ APP STATE FILE + + genesisBytes, err := os.ReadFile("../../exported-state.json") + s.Require().NoError(err) + + var genesisState exrpnetwork.CustomGenesisState + + err = json.Unmarshal(genesisBytes, &genesisState) + s.Require().NoError(err) + + appState := genesisState["app_state"].(map[string]interface{}) + + s.network = NewUpgradeTestNetwork( + // LOAD APP STATE FROM FILE + exrpnetwork.WithCustomGenesis(appState), + ) + + s.Require().NotNil(s.network) +} \ No newline at end of file diff --git a/tests/upgrade/suite_test.go b/tests/upgrade/suite_test.go new file mode 100644 index 0000000..70f9a0b --- /dev/null +++ b/tests/upgrade/suite_test.go @@ -0,0 +1,18 @@ +package testupgrade + +import ( + "testing" + + "github.com/stretchr/testify/suite" +) + +func TestUpgradeTestSuite(t *testing.T) { + suite.Run(t, new(UpgradeTestSuite)) +} + +func (s *UpgradeTestSuite) TestUpgrade() { + + denom := s.network.GetDenom() + s.Require().NotEmpty(denom) + s.Require().Equal(denom, "uxrp") +} \ No newline at end of file diff --git a/testutil/integration/common/network/network.go b/testutil/integration/common/network/network.go new file mode 100644 index 0000000..7998444 --- /dev/null +++ b/testutil/integration/common/network/network.go @@ -0,0 +1,53 @@ +// Copyright Tharsis Labs Ltd.(Evmos) +// SPDX-License-Identifier:ENCL-1.0(https://github.com/evmos/evmos/blob/main/LICENSE) + +package commonnetwork + +import ( + "testing" + "time" + + abcitypes "github.com/cometbft/cometbft/abci/types" + sdktypes "github.com/cosmos/cosmos-sdk/types" + sdktestutil "github.com/cosmos/cosmos-sdk/types/module/testutil" + txtypes "github.com/cosmos/cosmos-sdk/types/tx" + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" + "github.com/cosmos/cosmos-sdk/x/authz" + banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" + distrtypes "github.com/cosmos/cosmos-sdk/x/distribution/types" + stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" + ibctesting "github.com/cosmos/ibc-go/v8/testing" +) + +// Network is the interface that wraps the common methods to interact with integration test network. +// +// It was designed to avoid users to access module's keepers directly and force integration tests +// to be closer to the real user's behavior. +type Network interface { + GetContext() sdktypes.Context + GetChainID() string + GetDenom() string + GetOtherDenoms() []string + GetValidators() []stakingtypes.Validator + + NextBlock() error + NextBlockAfter(duration time.Duration) error + NextBlockWithTxs(txBytes ...[]byte) (*abcitypes.ResponseFinalizeBlock, error) + + // Clients + GetAuthClient() authtypes.QueryClient + GetAuthzClient() authz.QueryClient + GetBankClient() banktypes.QueryClient + GetStakingClient() stakingtypes.QueryClient + GetDistrClient() distrtypes.QueryClient + + BroadcastTxSync(txBytes []byte) (abcitypes.ExecTxResult, error) + Simulate(txBytes []byte) (*txtypes.SimulateResponse, error) + CheckTx(txBytes []byte) (*abcitypes.ResponseCheckTx, error) + + // GetIBCChain returns the IBC test chain. + // NOTE: this is only used for testing IBC related functionality. + // The idea is to deprecate this eventually. + GetIBCChain(t *testing.T, coord *ibctesting.Coordinator) *ibctesting.TestChain + GetEncodingConfig() sdktestutil.TestEncodingConfig +} diff --git a/testutil/integration/exrp/network/abci.go b/testutil/integration/exrp/network/abci.go new file mode 100644 index 0000000..0da0fa0 --- /dev/null +++ b/testutil/integration/exrp/network/abci.go @@ -0,0 +1,100 @@ +// Copyright Tharsis Labs Ltd.(Evmos) +// SPDX-License-Identifier:ENCL-1.0(https://github.com/evmos/evmos/blob/main/LICENSE) +package exrpnetwork + +import ( + "time" + + storetypes "cosmossdk.io/store/types" + abcitypes "github.com/cometbft/cometbft/abci/types" + cmtproto "github.com/cometbft/cometbft/proto/tendermint/types" + cmttypes "github.com/cometbft/cometbft/types" +) + +// NextBlock is a private helper function that runs the EndBlocker logic, commits the changes, +// updates the header and runs the BeginBlocker +func (n *IntegrationNetwork) NextBlock() error { + return n.NextBlockAfter(time.Second) +} + +// NextBlockAfter is a private helper function that runs the FinalizeBlock logic, updates the context and +// commits the changes to have a block time after the given duration. +func (n *IntegrationNetwork) NextBlockAfter(duration time.Duration) error { + _, err := n.finalizeBlockAndCommit(duration) + return err +} + +// NextBlockWithTxs is a helper function that runs the FinalizeBlock logic +// with the provided tx bytes, updates the context and +// commits the changes to have a block time after the given duration. +func (n *IntegrationNetwork) NextBlockWithTxs(txBytes ...[]byte) (*abcitypes.ResponseFinalizeBlock, error) { + return n.finalizeBlockAndCommit(time.Second, txBytes...) +} + +// finalizeBlockAndCommit is a private helper function that runs the FinalizeBlock logic +// with the provided txBytes, updates the context and +// commits the changes to have a block time after the given duration. +func (n *IntegrationNetwork) finalizeBlockAndCommit(duration time.Duration, txBytes ...[]byte) (*abcitypes.ResponseFinalizeBlock, error) { + header := n.ctx.BlockHeader() + // Update block header and BeginBlock + header.Height++ + header.AppHash = n.app.LastCommitID().Hash + // Calculate new block time after duration + newBlockTime := header.Time.Add(duration) + header.Time = newBlockTime + + // FinalizeBlock to run endBlock, deliverTx & beginBlock logic + req := buildFinalizeBlockReq(header, n.valSet.Validators, txBytes...) + + res, err := n.app.FinalizeBlock(req) + if err != nil { + return nil, err + } + + newCtx := n.app.BaseApp.NewContextLegacy(false, header) + + // Update context header + newCtx = newCtx.WithMinGasPrices(n.ctx.MinGasPrices()) + newCtx = newCtx.WithKVGasConfig(n.ctx.KVGasConfig()) + newCtx = newCtx.WithTransientKVGasConfig(n.ctx.TransientKVGasConfig()) + newCtx = newCtx.WithConsensusParams(n.ctx.ConsensusParams()) + // This might have to be changed with time if we want to test gas limits + newCtx = newCtx.WithBlockGasMeter(storetypes.NewInfiniteGasMeter()) + newCtx = newCtx.WithVoteInfos(req.DecidedLastCommit.GetVotes()) + n.ctx = newCtx + + // commit changes + _, err = n.app.Commit() + + return res, err +} + +// buildFinalizeBlockReq is a helper function to build +// properly the FinalizeBlock request +func buildFinalizeBlockReq(header cmtproto.Header, validators []*cmttypes.Validator, txs ...[]byte) *abcitypes.RequestFinalizeBlock { + // add validator's commit info to allocate corresponding tokens to validators + ci := getCommitInfo(validators) + return &abcitypes.RequestFinalizeBlock{ + Height: header.Height, + DecidedLastCommit: ci, + Hash: header.AppHash, + NextValidatorsHash: header.ValidatorsHash, + ProposerAddress: header.ProposerAddress, + Time: header.Time, + Txs: txs, + } +} + +func getCommitInfo(validators []*cmttypes.Validator) abcitypes.CommitInfo { + voteInfos := make([]abcitypes.VoteInfo, len(validators)) + for i, val := range validators { + voteInfos[i] = abcitypes.VoteInfo{ + Validator: abcitypes.Validator{ + Address: val.Address, + Power: val.VotingPower, + }, + BlockIdFlag: cmtproto.BlockIDFlagCommit, + } + } + return abcitypes.CommitInfo{Votes: voteInfos} +} diff --git a/testutil/integration/exrp/network/chain_id_modifiers.go b/testutil/integration/exrp/network/chain_id_modifiers.go new file mode 100644 index 0000000..f36ecf3 --- /dev/null +++ b/testutil/integration/exrp/network/chain_id_modifiers.go @@ -0,0 +1,121 @@ +// Copyright Tharsis Labs Ltd.(Evmos) +// SPDX-License-Identifier:ENCL-1.0(https://github.com/evmos/evmos/blob/main/LICENSE) +// +// This files contains handler for the testing suite that has to be run to +// modify the chain configuration depending on the chainID + +package exrpnetwork + +import ( + banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" + "github.com/evmos/evmos/v20/utils" + erc20types "github.com/evmos/evmos/v20/x/erc20/types" +) + +// updateErc20GenesisStateForChainID modify the default genesis state for the +// bank module of the testing suite depending on the chainID. +func updateBankGenesisStateForChainID(chainID string, bankGenesisState banktypes.GenesisState) banktypes.GenesisState { + metadata := generateBankGenesisMetadata(chainID) + bankGenesisState.DenomMetadata = []banktypes.Metadata{metadata} + + return bankGenesisState +} + +// generateBankGenesisMetadata generates the metadata +// for the Evm coin depending on the chainID. +func generateBankGenesisMetadata(chainID string) banktypes.Metadata { + if utils.IsTestnet(chainID) { + return banktypes.Metadata{ + Description: "The native EVM, governance and staking token of the Evmos testnet", + Base: "atevmos", + DenomUnits: []*banktypes.DenomUnit{ + { + Denom: "atevmos", + Exponent: 0, + }, + { + Denom: "tevmos", + Exponent: 18, + }, + }, + Name: "tEvmos", + Symbol: "tEVMOS", + Display: "tevmos", + } + } + + return banktypes.Metadata{ + Description: "The native EVM, governance and staking token of the Evmos mainnet", + Base: "aevmos", + DenomUnits: []*banktypes.DenomUnit{ + { + Denom: "aevmos", + Exponent: 0, + }, + { + Denom: "evmos", + Exponent: 18, + }, + }, + Name: "Evmos", + Symbol: "EVMOS", + Display: "evmos", + } +} + +// updateErc20GenesisStateForChainID modify the default genesis state for the +// erc20 module on the testing suite depending on the chainID. +func updateErc20GenesisStateForChainID(chainID string, erc20GenesisState erc20types.GenesisState) erc20types.GenesisState { + if !utils.IsTestnet(chainID) { + return erc20GenesisState + } + + erc20GenesisState.Params = updateErc20Params(chainID, erc20GenesisState.Params) + erc20GenesisState.TokenPairs = updateErc20TokenPairs(chainID, erc20GenesisState.TokenPairs) + + return erc20GenesisState +} + +// updateErc20Params modifies the erc20 module params to use the correct +// WEVMOS contract depending on ChainID +func updateErc20Params(chainID string, params erc20types.Params) erc20types.Params { + // mainnetAddress := erc20types.GetWEVMOSContractHex(utils.MainnetChainID) + // testnetAddress := erc20types.GetWEVMOSContractHex(chainID) + + nativePrecompiles := make([]string, len(params.NativePrecompiles)) + for i, nativePrecompile := range params.NativePrecompiles { + // if nativePrecompile == mainnetAddress { + // nativePrecompiles[i] = testnetAddress + // } else { + // nativePrecompiles[i] = nativePrecompile + // } + nativePrecompiles[i] = nativePrecompile + } + params.NativePrecompiles = nativePrecompiles + return params +} + +// updateErc20TokenPairs modifies the erc20 token pairs to use the correct +// WEVMOS depending on ChainID +func updateErc20TokenPairs(chainID string, tokenPairs []erc20types.TokenPair) []erc20types.TokenPair { + // testnetAddress := erc20types.GetWEVMOSContractHex(chainID) + // coinInfo := evmtypes.ChainsCoinInfo[utils.MainnetChainID] + + // mainnetAddress := erc20types.GetWEVMOSContractHex(utils.MainnetChainID) + + updatedTokenPairs := make([]erc20types.TokenPair, len(tokenPairs)) + for i, tokenPair := range tokenPairs { + // if tokenPair.Erc20Address == mainnetAddress { + // updatedTokenPairs[i] = erc20types.TokenPair{ + // Erc20Address: testnetAddress, + // Denom: coinInfo.Denom, + // Enabled: tokenPair.Enabled, + // ContractOwner: tokenPair.ContractOwner, + // } + // } else { + // updatedTokenPairs[i] = tokenPair + // } + updatedTokenPairs[i] = tokenPair + } + return updatedTokenPairs +} diff --git a/testutil/integration/exrp/network/clients.go b/testutil/integration/exrp/network/clients.go new file mode 100644 index 0000000..2c98220 --- /dev/null +++ b/testutil/integration/exrp/network/clients.go @@ -0,0 +1,102 @@ +// Copyright Tharsis Labs Ltd.(Evmos) +// SPDX-License-Identifier:ENCL-1.0(https://github.com/evmos/evmos/blob/main/LICENSE) +package exrpnetwork + +import ( + "github.com/cosmos/cosmos-sdk/baseapp" + sdktypes "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/module/testutil" + authkeeper "github.com/cosmos/cosmos-sdk/x/auth/keeper" + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" + "github.com/cosmos/cosmos-sdk/x/authz" + banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" + distrkeeper "github.com/cosmos/cosmos-sdk/x/distribution/keeper" + distrtypes "github.com/cosmos/cosmos-sdk/x/distribution/types" + govkeeper "github.com/cosmos/cosmos-sdk/x/gov/keeper" + govtypes "github.com/cosmos/cosmos-sdk/x/gov/types/v1" + stakingkeeper "github.com/cosmos/cosmos-sdk/x/staking/keeper" + stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" + erc20types "github.com/evmos/evmos/v20/x/erc20/types" + evmtypes "github.com/evmos/evmos/v20/x/evm/types" + feemarkettypes "github.com/evmos/evmos/v20/x/feemarket/types" +) + +func getQueryHelper(ctx sdktypes.Context, encCfg testutil.TestEncodingConfig) *baseapp.QueryServiceTestHelper { + interfaceRegistry := encCfg.InterfaceRegistry + // This is needed so that state changes are not committed in precompiles + // simulations. + cacheCtx, _ := ctx.CacheContext() + return baseapp.NewQueryServerTestHelper(cacheCtx, interfaceRegistry) +} + +func (n *IntegrationNetwork) GetERC20Client() erc20types.QueryClient { + queryHelper := getQueryHelper(n.GetContext(), n.GetEncodingConfig()) + erc20types.RegisterQueryServer(queryHelper, n.app.Erc20Keeper) + return erc20types.NewQueryClient(queryHelper) +} + +func (n *IntegrationNetwork) GetEvmClient() evmtypes.QueryClient { + queryHelper := getQueryHelper(n.GetContext(), n.GetEncodingConfig()) + evmtypes.RegisterQueryServer(queryHelper, n.app.EvmKeeper) + return evmtypes.NewQueryClient(queryHelper) +} + +func (n *IntegrationNetwork) GetGovClient() govtypes.QueryClient { + queryHelper := getQueryHelper(n.GetContext(), n.GetEncodingConfig()) + govtypes.RegisterQueryServer(queryHelper, govkeeper.NewQueryServer(&n.app.GovKeeper)) + return govtypes.NewQueryClient(queryHelper) +} + +func (n *IntegrationNetwork) GetBankClient() banktypes.QueryClient { + queryHelper := getQueryHelper(n.GetContext(), n.GetEncodingConfig()) + banktypes.RegisterQueryServer(queryHelper, n.app.BankKeeper) + return banktypes.NewQueryClient(queryHelper) +} + +func (n *IntegrationNetwork) GetFeeMarketClient() feemarkettypes.QueryClient { + queryHelper := getQueryHelper(n.GetContext(), n.GetEncodingConfig()) + feemarkettypes.RegisterQueryServer(queryHelper, n.app.FeeMarketKeeper) + return feemarkettypes.NewQueryClient(queryHelper) +} + +// func (n *IntegrationNetwork) GetInflationClient() infltypes.QueryClient { +// queryHelper := getQueryHelper(n.GetContext(), n.GetEncodingConfig()) +// infltypes.RegisterQueryServer(queryHelper, n.app.InflationKeeper) +// return infltypes.NewQueryClient(queryHelper) +// } + +func (n *IntegrationNetwork) GetAuthClient() authtypes.QueryClient { + queryHelper := getQueryHelper(n.GetContext(), n.GetEncodingConfig()) + authtypes.RegisterQueryServer(queryHelper, authkeeper.NewQueryServer(n.app.AccountKeeper)) + return authtypes.NewQueryClient(queryHelper) +} + +func (n *IntegrationNetwork) GetAuthzClient() authz.QueryClient { + queryHelper := getQueryHelper(n.GetContext(), n.GetEncodingConfig()) + authz.RegisterQueryServer(queryHelper, n.app.AuthzKeeper) + return authz.NewQueryClient(queryHelper) +} + +func (n *IntegrationNetwork) GetStakingClient() stakingtypes.QueryClient { + queryHelper := getQueryHelper(n.GetContext(), n.GetEncodingConfig()) + stakingtypes.RegisterQueryServer(queryHelper, stakingkeeper.Querier{Keeper: n.app.StakingKeeper.Keeper}) + return stakingtypes.NewQueryClient(queryHelper) +} + +func (n *IntegrationNetwork) GetDistrClient() distrtypes.QueryClient { + queryHelper := getQueryHelper(n.GetContext(), n.GetEncodingConfig()) + distrtypes.RegisterQueryServer(queryHelper, distrkeeper.Querier{Keeper: n.app.DistrKeeper}) + return distrtypes.NewQueryClient(queryHelper) +} + +// func (n *IntegrationNetwork) GetEpochsClient() epochstypes.QueryClient { +// queryHelper := getQueryHelper(n.GetContext(), n.GetEncodingConfig()) +// epochstypes.RegisterQueryServer(queryHelper, n.app.EpochsKeeper) +// return epochstypes.NewQueryClient(queryHelper) +// } + +// func (n *IntegrationNetwork) GetVestingClient() vestingtypes.QueryClient { +// queryHelper := getQueryHelper(n.GetContext(), n.GetEncodingConfig()) +// vestingtypes.RegisterQueryServer(queryHelper, n.app.VestingKeeper) +// return vestingtypes.NewQueryClient(queryHelper) +// } diff --git a/testutil/integration/exrp/network/config.go b/testutil/integration/exrp/network/config.go new file mode 100644 index 0000000..f1cc2f0 --- /dev/null +++ b/testutil/integration/exrp/network/config.go @@ -0,0 +1,155 @@ +// Copyright Tharsis Labs Ltd.(Evmos) +// SPDX-License-Identifier:ENCL-1.0(https://github.com/evmos/evmos/blob/main/LICENSE) + +package exrpnetwork + +import ( + "fmt" + "math/big" + + "github.com/cosmos/cosmos-sdk/baseapp" + sdktypes "github.com/cosmos/cosmos-sdk/types" + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" + banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" + stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" + testtx "github.com/evmos/evmos/v20/testutil/tx" + evmostypes "github.com/evmos/evmos/v20/types" + "github.com/evmos/evmos/v20/utils" + "github.com/xrplevm/node/v4/app" +) + +// Config defines the configuration for a chain. +// It allows for customization of the network to adjust to +// testing needs. +type Config struct { + chainID string + eip155ChainID *big.Int + amountOfValidators int + preFundedAccounts []sdktypes.AccAddress + balances []banktypes.Balance + denom string + customGenesisState CustomGenesisState + otherCoinDenom []string + operatorsAddrs []sdktypes.AccAddress + customBaseAppOpts []func(*baseapp.BaseApp) +} + +type CustomGenesisState map[string]interface{} + +// DefaultConfig returns the default configuration for a chain. +func DefaultConfig() Config { + account, _ := testtx.NewAccAddressAndKey() + return Config{ + chainID: utils.MainnetChainID + "-1", + eip155ChainID: big.NewInt(9001), + amountOfValidators: 3, + // Only one account besides the validators + preFundedAccounts: []sdktypes.AccAddress{account}, + // NOTE: Per default, the balances are left empty, and the pre-funded accounts are used. + balances: nil, + denom: app.BaseDenom, + customGenesisState: nil, + } +} + +// getGenAccountsAndBalances takes the network configuration and returns the used +// genesis accounts and balances. +// +// NOTE: If the balances are set, the pre-funded accounts are ignored. +func getGenAccountsAndBalances(cfg Config, validators []stakingtypes.Validator) (genAccounts []authtypes.GenesisAccount, balances []banktypes.Balance) { + if len(cfg.balances) > 0 { + balances = cfg.balances + accounts := getAccAddrsFromBalances(balances) + genAccounts = createGenesisAccounts(accounts) + } else { + genAccounts = createGenesisAccounts(cfg.preFundedAccounts) + balances = createBalances(cfg.preFundedAccounts, append(cfg.otherCoinDenom, cfg.denom)) + } + + // append validators to genesis accounts and balances + valAccs := make([]sdktypes.AccAddress, len(validators)) + for i, v := range validators { + valAddr, err := sdktypes.ValAddressFromBech32(v.OperatorAddress) + if err != nil { + panic(fmt.Sprintf("failed to derive validator address from %q: %s", v.OperatorAddress, err.Error())) + } + valAccs[i] = sdktypes.AccAddress(valAddr.Bytes()) + } + genAccounts = append(genAccounts, createGenesisAccounts(valAccs)...) + + return +} + +// ConfigOption defines a function that can modify the NetworkConfig. +// The purpose of this is to force to be declarative when the default configuration +// requires to be changed. +type ConfigOption func(*Config) + +// WithChainID sets a custom chainID for the network. It panics if the chainID is invalid. +func WithChainID(chainID string) ConfigOption { + chainIDNum, err := evmostypes.ParseChainID(chainID) + if err != nil { + panic(err) + } + return func(cfg *Config) { + cfg.chainID = chainID + cfg.eip155ChainID = chainIDNum + } +} + +// WithAmountOfValidators sets the amount of validators for the network. +func WithAmountOfValidators(amount int) ConfigOption { + return func(cfg *Config) { + cfg.amountOfValidators = amount + } +} + +// WithPreFundedAccounts sets the pre-funded accounts for the network. +func WithPreFundedAccounts(accounts ...sdktypes.AccAddress) ConfigOption { + return func(cfg *Config) { + cfg.preFundedAccounts = accounts + } +} + +// WithBalances sets the specific balances for the pre-funded accounts, that +// are being set up for the network. +func WithBalances(balances ...banktypes.Balance) ConfigOption { + return func(cfg *Config) { + cfg.balances = append(cfg.balances, balances...) + } +} + +// WithDenom sets the denom for the network. +func WithDenom(denom string) ConfigOption { + return func(cfg *Config) { + cfg.denom = denom + } +} + +// WithCustomGenesis sets the custom genesis of the network for specific modules. +func WithCustomGenesis(customGenesis CustomGenesisState) ConfigOption { + return func(cfg *Config) { + cfg.customGenesisState = customGenesis + } +} + +// WithOtherDenoms sets other possible coin denominations for the network. +func WithOtherDenoms(otherDenoms []string) ConfigOption { + return func(cfg *Config) { + cfg.otherCoinDenom = otherDenoms + } +} + +// WithValidatorOperators overwrites the used operator address for the network instantiation. +func WithValidatorOperators(keys []sdktypes.AccAddress) ConfigOption { + return func(cfg *Config) { + cfg.operatorsAddrs = keys + } +} + +// WithCustomBaseAppOpts sets custom base app options for the network. +func WithCustomBaseAppOpts(opts ...func(*baseapp.BaseApp)) ConfigOption { + return func(cfg *Config) { + cfg.customBaseAppOpts = opts + } +} diff --git a/testutil/integration/exrp/network/config_test.go b/testutil/integration/exrp/network/config_test.go new file mode 100644 index 0000000..480acaf --- /dev/null +++ b/testutil/integration/exrp/network/config_test.go @@ -0,0 +1,51 @@ +// Copyright Tharsis Labs Ltd.(Evmos) +// SPDX-License-Identifier:ENCL-1.0(https://github.com/evmos/evmos/blob/main/LICENSE) + +package exrpnetwork_test + +import ( + "testing" + + sdk "github.com/cosmos/cosmos-sdk/types" + banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" + grpchandler "github.com/evmos/evmos/v20/testutil/integration/evmos/grpc" + testkeyring "github.com/evmos/evmos/v20/testutil/integration/evmos/keyring" + "github.com/evmos/evmos/v20/testutil/integration/evmos/network" + "github.com/stretchr/testify/require" + "github.com/xrplevm/node/v4/app" +) + +func TestWithBalances(t *testing.T) { + key1Balance := sdk.NewCoins(sdk.NewInt64Coin(app.BaseDenom, 1e18)) + key2Balance := sdk.NewCoins( + sdk.NewInt64Coin(app.BaseDenom, 2e18), + sdk.NewInt64Coin("other", 3e18), + ) + + // Create a new network with 2 pre-funded accounts + keyring := testkeyring.New(2) + balances := []banktypes.Balance{ + { + Address: keyring.GetAccAddr(0).String(), + Coins: key1Balance, + }, + { + Address: keyring.GetAccAddr(1).String(), + Coins: key2Balance, + }, + } + nw := network.New( + network.WithBalances(balances...), + ) + handler := grpchandler.NewIntegrationHandler(nw) + + req, err := handler.GetAllBalances(keyring.GetAccAddr(0)) + require.NoError(t, err, "error getting balances") + require.Len(t, req.Balances, 1, "wrong number of balances") + require.Equal(t, balances[0].Coins, req.Balances, "wrong balances") + + req, err = handler.GetAllBalances(keyring.GetAccAddr(1)) + require.NoError(t, err, "error getting balances") + require.Len(t, req.Balances, 2, "wrong number of balances") + require.Equal(t, balances[1].Coins, req.Balances, "wrong balances") +} diff --git a/testutil/integration/exrp/network/ibc.go b/testutil/integration/exrp/network/ibc.go new file mode 100644 index 0000000..3c221cc --- /dev/null +++ b/testutil/integration/exrp/network/ibc.go @@ -0,0 +1,28 @@ +// Copyright Tharsis Labs Ltd.(Evmos) +// SPDX-License-Identifier:ENCL-1.0(https://github.com/evmos/evmos/blob/main/LICENSE) +package exrpnetwork + +import ( + "testing" + + ibctesting "github.com/cosmos/ibc-go/v8/testing" +) + +// GetIBCChain returns a TestChain instance for the given network. +// Note: the sender accounts are not populated. Do not use this accounts to send transactions during tests. +// The keyring should be used instead. +func (n *IntegrationNetwork) GetIBCChain(t *testing.T, coord *ibctesting.Coordinator) *ibctesting.TestChain { + return &ibctesting.TestChain{ + TB: t, + Coordinator: coord, + ChainID: n.GetChainID(), + App: n.app, + CurrentHeader: n.ctx.BlockHeader(), + QueryServer: n.app.GetIBCKeeper(), + TxConfig: n.app.GetTxConfig(), + Codec: n.app.AppCodec(), + Vals: n.valSet, + NextVals: n.valSet, + Signers: n.valSigners, + } +} diff --git a/testutil/integration/exrp/network/network.go b/testutil/integration/exrp/network/network.go new file mode 100644 index 0000000..14dc958 --- /dev/null +++ b/testutil/integration/exrp/network/network.go @@ -0,0 +1,345 @@ +// Copyright Tharsis Labs Ltd.(Evmos) +// SPDX-License-Identifier:ENCL-1.0(https://github.com/evmos/evmos/blob/main/LICENSE) + +package exrpnetwork + +import ( + "fmt" + "math" + "math/big" + "time" + + sdkmath "cosmossdk.io/math" + + "github.com/evmos/evmos/v20/types" + "github.com/xrplevm/node/v4/app" + + abcitypes "github.com/cometbft/cometbft/abci/types" + cmtjson "github.com/cometbft/cometbft/libs/json" + cmtproto "github.com/cometbft/cometbft/proto/tendermint/types" + tmversion "github.com/cometbft/cometbft/proto/tendermint/version" + cmttypes "github.com/cometbft/cometbft/types" + "github.com/cometbft/cometbft/version" + sdktypes "github.com/cosmos/cosmos-sdk/types" + sdktestutil "github.com/cosmos/cosmos-sdk/types/module/testutil" + txtypes "github.com/cosmos/cosmos-sdk/types/tx" + govtypes "github.com/cosmos/cosmos-sdk/x/gov/types/v1" + stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" + commonnetwork "github.com/evmos/evmos/v20/testutil/integration/common/network" + erc20types "github.com/evmos/evmos/v20/x/erc20/types" + evmtypes "github.com/evmos/evmos/v20/x/evm/types" + feemarkettypes "github.com/evmos/evmos/v20/x/feemarket/types" +) + +// Network is the interface that wraps the methods to interact with integration test network. +// +// It was designed to avoid users to access module's keepers directly and force integration tests +// to be closer to the real user's behavior. +type Network interface { + commonnetwork.Network + + GetEIP155ChainID() *big.Int + // GetEVMChainConfig() *gethparams.ChainConfig + + // Clients + GetERC20Client() erc20types.QueryClient + GetEvmClient() evmtypes.QueryClient + GetGovClient() govtypes.QueryClient + // GetInflationClient() infltypes.QueryClient + GetFeeMarketClient() feemarkettypes.QueryClient + // GetVestingClient() vestingtypes.QueryClient +} + +var _ Network = (*IntegrationNetwork)(nil) + +// IntegrationNetwork is the implementation of the Network interface for integration tests. +type IntegrationNetwork struct { + cfg Config + ctx sdktypes.Context + validators []stakingtypes.Validator + app *app.App + + // This is only needed for IBC chain testing setup + valSet *cmttypes.ValidatorSet + valSigners map[string]cmttypes.PrivValidator +} + +// New configures and initializes a new integration Network instance with +// the given configuration options. If no configuration options are provided +// it uses the default configuration. +// +// It panics if an error occurs. +func New(opts ...ConfigOption) *IntegrationNetwork { + cfg := DefaultConfig() + // Modify the default config with the given options + for _, opt := range opts { + opt(&cfg) + } + + ctx := sdktypes.Context{} + network := &IntegrationNetwork{ + cfg: cfg, + ctx: ctx, + validators: []stakingtypes.Validator{}, + } + + err := network.configureAndInitChain() + if err != nil { + panic(err) + } + return network +} + +var ( + // DefaultBondedAmount is the amount of tokens that each validator will have initially bonded + DefaultBondedAmount = sdktypes.TokensFromConsensusPower(1, types.PowerReduction) + // PrefundedAccountInitialBalance is the amount of tokens that each prefunded account has at genesis + PrefundedAccountInitialBalance, _ = sdkmath.NewIntFromString("100_000_000_000_000_000_000_000") // 100k +) + +// configureAndInitChain initializes the network with the given configuration. +// It creates the genesis state and starts the network. +func (n *IntegrationNetwork) configureAndInitChain() error { + // // Create validator set with the amount of validators specified in the config + // // with the default power of 1. + // valSet, valSigners := createValidatorSetAndSigners(n.cfg.amountOfValidators) + // totalBonded := DefaultBondedAmount.Mul(sdkmath.NewInt(int64(n.cfg.amountOfValidators))) + + // // Build staking type validators and delegations + validators, err := createStakingValidators(valSet.Validators, DefaultBondedAmount, n.cfg.operatorsAddrs) + // if err != nil { + // return err + // } + + // // Create genesis accounts and funded balances based on the config + // genAccounts, fundedAccountBalances := getGenAccountsAndBalances(n.cfg, validators) + + // fundedAccountBalances = addBondedModuleAccountToFundedBalances( + // fundedAccountBalances, + // sdktypes.NewCoin(n.cfg.denom, totalBonded), + // ) + + // delegations := createDelegations(validators, genAccounts[0].GetAddress()) + + // // Create a new EvmosApp with the following params + // evmosApp := createExrpApp(n.cfg.chainID, n.cfg.customBaseAppOpts...) + + // stakingParams := StakingCustomGenesisState{ + // denom: n.cfg.denom, + // validators: validators, + // delegations: delegations, + // } + // govParams := GovCustomGenesisState{ + // denom: n.cfg.denom, + // } + + // totalSupply := calculateTotalSupply(fundedAccountBalances) + // bankParams := BankCustomGenesisState{ + // totalSupply: totalSupply, + // balances: fundedAccountBalances, + // } + + // // Get the corresponding slashing info and missed block info + // // for the created validators + // slashingParams, err := getValidatorsSlashingGen(validators, evmosApp.StakingKeeper) + // if err != nil { + // return err + // } + + // // Configure Genesis state + // genesisState := newDefaultGenesisState( + // evmosApp, + // defaultGenesisParams{ + // genAccounts: genAccounts, + // staking: stakingParams, + // bank: bankParams, + // slashing: slashingParams, + // gov: govParams, + // }, + // ) + + // // modify genesis state if there're any custom genesis state + // // for specific modules + // genesisState, err = customizeGenesis(evmosApp, n.cfg.customGenesisState, genesisState) + // if err != nil { + // return err + // } + + // ============================================================ + + evmosApp := createExrpApp(n.cfg.chainID, n.cfg.customBaseAppOpts...) + genesisState := n.cfg.customGenesisState + + // Init chain + stateBytes, err := cmtjson.MarshalIndent(genesisState, "", " ") + if err != nil { + return err + } + + // Consensus module does not have a genesis state on the app, + // but can customize the consensus parameters of the chain on initialization + consensusParams := &cmtproto.ConsensusParams{} + // if gen, ok := n.cfg.customGenesisState[consensustypes.ModuleName]; ok { + // consensusParams, ok = gen.(*cmtproto.ConsensusParams) + // if !ok { + // return fmt.Errorf("invalid type for consensus parameters. Expected: cmtproto.ConsensusParams, got %T", gen) + // } + // } + + now := time.Now().UTC() + if _, err := evmosApp.InitChain( + &abcitypes.RequestInitChain{ + Time: now, + ChainId: n.cfg.chainID, + Validators: []abcitypes.ValidatorUpdate{}, + ConsensusParams: consensusParams, + AppStateBytes: stateBytes, + }, + ); err != nil { + return err + } + + header := cmtproto.Header{ + ChainID: n.cfg.chainID, + Height: evmosApp.LastBlockHeight() + 1, + AppHash: evmosApp.LastCommitID().Hash, + Time: now, + ValidatorsHash: valSet.Hash(), + NextValidatorsHash: valSet.Hash(), + ProposerAddress: valSet.Proposer.Address, + Version: tmversion.Consensus{ + Block: version.BlockProtocol, + }, + } + + req := buildFinalizeBlockReq(header, valSet.Validators) + if _, err := evmosApp.FinalizeBlock(req); err != nil { + return err + } + + // TODO - this might not be the best way to initilize the context + n.ctx = evmosApp.BaseApp.NewContextLegacy(false, header) + + // Commit genesis changes + if _, err := evmosApp.Commit(); err != nil { + return err + } + + // Set networks global parameters + var blockMaxGas uint64 = math.MaxUint64 + if consensusParams.Block != nil && consensusParams.Block.MaxGas > 0 { + blockMaxGas = uint64(consensusParams.Block.MaxGas) //nolint:gosec // G115 + } + + n.app = evmosApp + n.ctx = n.ctx.WithConsensusParams(*consensusParams) + n.ctx = n.ctx.WithBlockGasMeter(types.NewInfiniteGasMeterWithLimit(blockMaxGas)) + + n.validators = validators + n.valSet = valSet + n.valSigners = valSigners + + return nil +} + +// GetContext returns the network's context +func (n *IntegrationNetwork) GetContext() sdktypes.Context { + return n.ctx +} + +// WithIsCheckTxCtx switches the network's checkTx property +func (n *IntegrationNetwork) WithIsCheckTxCtx(isCheckTx bool) sdktypes.Context { + n.ctx = n.ctx.WithIsCheckTx(isCheckTx) + return n.ctx +} + +// GetChainID returns the network's chainID +func (n *IntegrationNetwork) GetChainID() string { + return n.cfg.chainID +} + +// GetEIP155ChainID returns the network EIp-155 chainID number +func (n *IntegrationNetwork) GetEIP155ChainID() *big.Int { + return n.cfg.eip155ChainID +} + +// // GetChainConfig returns the network's chain config +// func (n *IntegrationNetwork) GetEVMChainConfig() *gethparams.ChainConfig { +// return evmtypes.GetEthChainConfig() +// } + +// GetDenom returns the network's denom +func (n *IntegrationNetwork) GetDenom() string { + return n.cfg.denom +} + +// GetOtherDenoms returns network's other supported denoms +func (n *IntegrationNetwork) GetOtherDenoms() []string { + return n.cfg.otherCoinDenom +} + +// GetValidators returns the network's validators +func (n *IntegrationNetwork) GetValidators() []stakingtypes.Validator { + return n.validators +} + +// GetOtherDenoms returns network's other supported denoms +func (n *IntegrationNetwork) GetEncodingConfig() sdktestutil.TestEncodingConfig { + return sdktestutil.TestEncodingConfig{ + InterfaceRegistry: n.app.InterfaceRegistry(), + Codec: n.app.AppCodec(), + TxConfig: n.app.GetTxConfig(), + Amino: n.app.LegacyAmino(), + } +} + +// BroadcastTxSync broadcasts the given txBytes to the network and returns the response. +// TODO - this should be change to gRPC +func (n *IntegrationNetwork) BroadcastTxSync(txBytes []byte) (abcitypes.ExecTxResult, error) { + header := n.ctx.BlockHeader() + // Update block header and BeginBlock + header.Height++ + header.AppHash = n.app.LastCommitID().Hash + // Calculate new block time after duration + newBlockTime := header.Time.Add(time.Second) + header.Time = newBlockTime + + req := buildFinalizeBlockReq(header, n.valSet.Validators, txBytes) + + // dont include the DecidedLastCommit because we're not committing the changes + // here, is just for broadcasting the tx. To persist the changes, use the + // NextBlock or NextBlockAfter functions + req.DecidedLastCommit = abcitypes.CommitInfo{} + + blockRes, err := n.app.BaseApp.FinalizeBlock(req) + if err != nil { + return abcitypes.ExecTxResult{}, err + } + if len(blockRes.TxResults) != 1 { + return abcitypes.ExecTxResult{}, fmt.Errorf("unexpected number of tx results. Expected 1, got: %d", len(blockRes.TxResults)) + } + return *blockRes.TxResults[0], nil +} + +// Simulate simulates the given txBytes to the network and returns the simulated response. +// TODO - this should be change to gRPC +func (n *IntegrationNetwork) Simulate(txBytes []byte) (*txtypes.SimulateResponse, error) { + gas, result, err := n.app.BaseApp.Simulate(txBytes) + if err != nil { + return nil, err + } + return &txtypes.SimulateResponse{ + GasInfo: &gas, + Result: result, + }, nil +} + +// CheckTx calls the BaseApp's CheckTx method with the given txBytes to the network and returns the response. +func (n *IntegrationNetwork) CheckTx(txBytes []byte) (*abcitypes.ResponseCheckTx, error) { + req := &abcitypes.RequestCheckTx{Tx: txBytes} + res, err := n.app.BaseApp.CheckTx(req) + if err != nil { + return nil, err + } + return res, nil +} diff --git a/testutil/integration/exrp/network/setup.go b/testutil/integration/exrp/network/setup.go new file mode 100644 index 0000000..2e4ad94 --- /dev/null +++ b/testutil/integration/exrp/network/setup.go @@ -0,0 +1,574 @@ +// Copyright Tharsis Labs Ltd.(Evmos) +// SPDX-License-Identifier:ENCL-1.0(https://github.com/evmos/evmos/blob/main/LICENSE) + +package exrpnetwork + +import ( + "fmt" + "slices" + "time" + + "github.com/cosmos/cosmos-sdk/baseapp" + "github.com/cosmos/cosmos-sdk/testutil/mock" + "github.com/cosmos/gogoproto/proto" + "github.com/xrplevm/node/v4/app" + + "cosmossdk.io/log" + sdkmath "cosmossdk.io/math" + evidencetypes "cosmossdk.io/x/evidence/types" + "cosmossdk.io/x/feegrant" + upgradetypes "cosmossdk.io/x/upgrade/types" + cmttypes "github.com/cometbft/cometbft/types" + dbm "github.com/cosmos/cosmos-db" + codectypes "github.com/cosmos/cosmos-sdk/codec/types" + cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec" + simutils "github.com/cosmos/cosmos-sdk/testutil/sims" + sdktypes "github.com/cosmos/cosmos-sdk/types" + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" + authztypes "github.com/cosmos/cosmos-sdk/x/authz" + banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" + consensustypes "github.com/cosmos/cosmos-sdk/x/consensus/types" + crisistypes "github.com/cosmos/cosmos-sdk/x/crisis/types" + distrtypes "github.com/cosmos/cosmos-sdk/x/distribution/types" + genutiltypes "github.com/cosmos/cosmos-sdk/x/genutil/types" + govtypes "github.com/cosmos/cosmos-sdk/x/gov/types" + govtypesv1 "github.com/cosmos/cosmos-sdk/x/gov/types/v1" + "github.com/cosmos/cosmos-sdk/x/params/types/proposal" + slashingtypes "github.com/cosmos/cosmos-sdk/x/slashing/types" + stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" + capabilitytypes "github.com/cosmos/ibc-go/modules/capability/types" + evmostypes "github.com/evmos/evmos/v20/types" + epochstypes "github.com/evmos/evmos/v20/x/epochs/types" + erc20types "github.com/evmos/evmos/v20/x/erc20/types" + evmtypes "github.com/evmos/evmos/v20/x/evm/types" + feemarkettypes "github.com/evmos/evmos/v20/x/feemarket/types" + infltypes "github.com/evmos/evmos/v20/x/inflation/v1/types" + vestingtypes "github.com/evmos/evmos/v20/x/vesting/types" + poatypes "github.com/xrplevm/node/v4/x/poa/types" +) + +// genSetupFn is the type for the module genesis setup functions +type genSetupFn func(evmosApp *app.App, genesisState evmostypes.GenesisState, customGenesis interface{}) (evmostypes.GenesisState, error) + +// defaultGenesisParams contains the params that are needed to +// setup the default genesis for the testing setup +type defaultGenesisParams struct { + genAccounts []authtypes.GenesisAccount + staking StakingCustomGenesisState + slashing SlashingCustomGenesisState + bank BankCustomGenesisState + gov GovCustomGenesisState +} + +// genesisSetupFunctions contains the available genesis setup functions +// that can be used to customize the network genesis +var genesisSetupFunctions = map[string]genSetupFn{ + evmtypes.ModuleName: func(_ *app.App, genesisState evmostypes.GenesisState, _ interface{}) (evmostypes.GenesisState, error) { + return genesisState, nil + }, + // erc20types.ModuleName: genStateSetter[*erc20types.GenesisState](erc20types.ModuleName), + erc20types.ModuleName: func(_ *app.App, genesisState evmostypes.GenesisState, _ interface{}) (evmostypes.GenesisState, error) { + // no-op. Consensus does not have a genesis state on the application + // but the params are used on it + // (e.g. block max gas, max bytes). + // This is handled accordingly on chain and context initialization + return genesisState, nil + }, + // govtypes.ModuleName: genStateSetter[*govtypesv1.GenesisState](govtypes.ModuleName), + infltypes.ModuleName: func(_ *app.App, genesisState evmostypes.GenesisState, _ interface{}) (evmostypes.GenesisState, error) { + return genesisState, nil + }, + feemarkettypes.ModuleName: func(_ *app.App, genesisState evmostypes.GenesisState, _ interface{}) (evmostypes.GenesisState, error) { + // no-op. Consensus does not have a genesis state on the application + // but the params are used on it + // (e.g. block max gas, max bytes). + // This is handled accordingly on chain and context initialization + return genesisState, nil + }, + distrtypes.ModuleName: func(_ *app.App, genesisState evmostypes.GenesisState, _ interface{}) (evmostypes.GenesisState, error) { + return genesisState, nil + }, + slashingtypes.ModuleName: func(_ *app.App, genesisState evmostypes.GenesisState, _ interface{}) (evmostypes.GenesisState, error) { + return genesisState, nil + }, + authztypes.ModuleName: func(_ *app.App, genesisState evmostypes.GenesisState, _ interface{}) (evmostypes.GenesisState, error) { + return genesisState, nil + }, + banktypes.ModuleName: func(_ *app.App, genesisState evmostypes.GenesisState, _ interface{}) (evmostypes.GenesisState, error) { + return genesisState, nil + }, + // authtypes.ModuleName: setAuthGenesisState, + authtypes.ModuleName: func(_ *app.App, genesisState evmostypes.GenesisState, _ interface{}) (evmostypes.GenesisState, error) { + return genesisState, nil + }, + epochstypes.ModuleName: func(_ *app.App, genesisState evmostypes.GenesisState, _ interface{}) (evmostypes.GenesisState, error) { + return genesisState, nil + }, + consensustypes.ModuleName: func(_ *app.App, genesisState evmostypes.GenesisState, _ interface{}) (evmostypes.GenesisState, error) { + // no-op. Consensus does not have a genesis state on the application + // but the params are used on it + // (e.g. block max gas, max bytes). + // This is handled accordingly on chain and context initialization + return genesisState, nil + }, + // capabilitytypes.ModuleName: genStateSetter[*capabilitytypes.GenesisState](capabilitytypes.ModuleName), + capabilitytypes.ModuleName: func(_ *app.App, genesisState evmostypes.GenesisState, _ interface{}) (evmostypes.GenesisState, error) { + // no-op. Consensus does not have a genesis state on the application + // but the params are used on it + // (e.g. block max gas, max bytes). + // This is handled accordingly on chain and context initialization + return genesisState, nil + }, + stakingtypes.ModuleName: func(_ *app.App, genesisState evmostypes.GenesisState, _ interface{}) (evmostypes.GenesisState, error) { + return genesisState, nil + }, + evidencetypes.ModuleName: func(_ *app.App, genesisState evmostypes.GenesisState, _ interface{}) (evmostypes.GenesisState, error) { + return genesisState, nil + }, + proposal.ModuleName: func(_ *app.App, genesisState evmostypes.GenesisState, customGenesis interface{}) (evmostypes.GenesisState, error) { + return genesisState, nil + }, + poatypes.ModuleName: func(_ *app.App, genesisState evmostypes.GenesisState, _ interface{}) (evmostypes.GenesisState, error) { + return genesisState, nil + }, + govtypes.ModuleName: func(_ *app.App, genesisState evmostypes.GenesisState, _ interface{}) (evmostypes.GenesisState, error) { + // no-op. Consensus does not have a genesis state on the application + // but the params are used on it + // (e.g. block max gas, max bytes). + // This is handled accordingly on chain and context initialization + return genesisState, nil + }, + upgradetypes.ModuleName: func(_ *app.App, genesisState evmostypes.GenesisState, _ interface{}) (evmostypes.GenesisState, error) { + return genesisState, nil + }, + feegrant.ModuleName: func(_ *app.App, genesisState evmostypes.GenesisState, _ interface{}) (evmostypes.GenesisState, error) { + return genesisState, nil + }, + "ibc": func(_ *app.App, genesisState evmostypes.GenesisState, _ interface{}) (evmostypes.GenesisState, error) { + return genesisState, nil + }, + genutiltypes.ModuleName: func(_ *app.App, genesisState evmostypes.GenesisState, _ interface{}) (evmostypes.GenesisState, error) { + return genesisState, nil + }, + vestingtypes.ModuleName: func(_ *app.App, genesisState evmostypes.GenesisState, _ interface{}) (evmostypes.GenesisState, error) { + return genesisState, nil + }, + crisistypes.ModuleName: func(_ *app.App, genesisState evmostypes.GenesisState, _ interface{}) (evmostypes.GenesisState, error) { + return genesisState, nil + }, + "transfer": func(_ *app.App, genesisState evmostypes.GenesisState, _ interface{}) (evmostypes.GenesisState, error) { + return genesisState, nil + }, +} + +// genStateSetter is a generic function to set module-specific genesis state +func genStateSetter[T proto.Message](moduleName string) genSetupFn { + return func(evmosApp *app.App, genesisState evmostypes.GenesisState, customGenesis interface{}) (evmostypes.GenesisState, error) { + moduleGenesis, ok := customGenesis.(T) + if !ok { + return nil, fmt.Errorf("invalid type %T for %s module genesis state", customGenesis, moduleName) + } + + genesisState[moduleName] = evmosApp.AppCodec().MustMarshalJSON(moduleGenesis) + return genesisState, nil + } +} + +// createValidatorSetAndSigners creates validator set with the amount of validators specified +// with the default power of 1. +func createValidatorSetAndSigners(numberOfValidators int) (*cmttypes.ValidatorSet, map[string]cmttypes.PrivValidator) { + // Create validator set + tmValidators := make([]*cmttypes.Validator, 0, numberOfValidators) + signers := make(map[string]cmttypes.PrivValidator, numberOfValidators) + + for i := 0; i < numberOfValidators; i++ { + privVal := mock.NewPV() + pubKey, _ := privVal.GetPubKey() + validator := cmttypes.NewValidator(pubKey, 1) + tmValidators = append(tmValidators, validator) + signers[pubKey.Address().String()] = privVal + } + + return cmttypes.NewValidatorSet(tmValidators), signers +} + +// createGenesisAccounts returns a slice of genesis accounts from the given +// account addresses. +func createGenesisAccounts(accounts []sdktypes.AccAddress) []authtypes.GenesisAccount { + numberOfAccounts := len(accounts) + genAccounts := make([]authtypes.GenesisAccount, 0, numberOfAccounts) + for _, acc := range accounts { + genAccounts = append(genAccounts, authtypes.NewBaseAccount( + acc, nil, 0, 0), + ) + } + return genAccounts +} + +// getAccAddrsFromBalances returns a slice of genesis accounts from the +// given balances. +func getAccAddrsFromBalances(balances []banktypes.Balance) []sdktypes.AccAddress { + numberOfBalances := len(balances) + genAccounts := make([]sdktypes.AccAddress, 0, numberOfBalances) + for _, balance := range balances { + genAccounts = append(genAccounts, sdktypes.AccAddress(balance.Address)) + } + return genAccounts +} + +// createBalances creates balances for the given accounts and coin +func createBalances(accounts []sdktypes.AccAddress, denoms []string) []banktypes.Balance { + slices.Sort(denoms) + numberOfAccounts := len(accounts) + coins := make([]sdktypes.Coin, len(denoms)) + for i, denom := range denoms { + coins[i] = sdktypes.NewCoin(denom, PrefundedAccountInitialBalance) + } + fundedAccountBalances := make([]banktypes.Balance, 0, numberOfAccounts) + for _, acc := range accounts { + balance := banktypes.Balance{ + Address: acc.String(), + Coins: coins, + } + + fundedAccountBalances = append(fundedAccountBalances, balance) + } + return fundedAccountBalances +} + +// createExrpApp creates an exrp app +func createExrpApp(chainID string, customBaseAppOptions ...func(*baseapp.BaseApp)) *app.App { + // Create evmos app + db := dbm.NewMemDB() + logger := log.NewNopLogger() + loadLatest := true + skipUpgradeHeights := map[int64]bool{} + homePath := app.DefaultNodeHome + invCheckPeriod := uint(5) + appOptions := simutils.NewAppOptionsWithFlagHome(app.DefaultNodeHome) + baseAppOptions := append(customBaseAppOptions, baseapp.SetChainID(chainID)) //nolint:gocritic + + return app.New( + logger, + db, + nil, + loadLatest, + skipUpgradeHeights, + homePath, + invCheckPeriod, + appOptions, + baseAppOptions..., + ) +} + +// createStakingValidator creates a staking validator from the given tm validator and bonded +func createStakingValidator(val *cmttypes.Validator, bondedAmt sdkmath.Int, operatorAddr *sdktypes.AccAddress) (stakingtypes.Validator, error) { + pk, err := cryptocodec.FromTmPubKeyInterface(val.PubKey) //nolint:staticcheck + if err != nil { + return stakingtypes.Validator{}, err + } + + pkAny, err := codectypes.NewAnyWithValue(pk) + if err != nil { + return stakingtypes.Validator{}, err + } + + opAddr := sdktypes.ValAddress(val.Address).String() + if operatorAddr != nil { + opAddr = sdktypes.ValAddress(operatorAddr.Bytes()).String() + } + + // Default to 5% commission + commission := stakingtypes.NewCommission(sdkmath.LegacyNewDecWithPrec(5, 2), sdkmath.LegacyNewDecWithPrec(2, 1), sdkmath.LegacyNewDecWithPrec(5, 2)) + validator := stakingtypes.Validator{ + OperatorAddress: opAddr, + ConsensusPubkey: pkAny, + Jailed: false, + Status: stakingtypes.Bonded, + Tokens: bondedAmt, + DelegatorShares: sdkmath.LegacyOneDec(), + Description: stakingtypes.Description{}, + UnbondingHeight: int64(0), + UnbondingTime: time.Unix(0, 0).UTC(), + Commission: commission, + MinSelfDelegation: sdkmath.ZeroInt(), + } + return validator, nil +} + +// createStakingValidators creates staking validators from the given tm validators and bonded +// amounts +func createStakingValidators(tmValidators []*cmttypes.Validator, bondedAmt sdkmath.Int, operatorsAddresses []sdktypes.AccAddress) ([]stakingtypes.Validator, error) { + if len(operatorsAddresses) == 0 { + return createStakingValidatorsWithRandomOperator(tmValidators, bondedAmt) + } + return createStakingValidatorsWithSpecificOperator(tmValidators, bondedAmt, operatorsAddresses) +} + +// createStakingValidatorsWithRandomOperator creates staking validators with non-specified operator addresses. +func createStakingValidatorsWithRandomOperator(tmValidators []*cmttypes.Validator, bondedAmt sdkmath.Int) ([]stakingtypes.Validator, error) { + amountOfValidators := len(tmValidators) + stakingValidators := make([]stakingtypes.Validator, 0, amountOfValidators) + for _, val := range tmValidators { + validator, err := createStakingValidator(val, bondedAmt, nil) + if err != nil { + return nil, err + } + stakingValidators = append(stakingValidators, validator) + } + return stakingValidators, nil +} + +// createStakingValidatorsWithSpecificOperator creates staking validators with the given operator addresses. +func createStakingValidatorsWithSpecificOperator(tmValidators []*cmttypes.Validator, bondedAmt sdkmath.Int, operatorsAddresses []sdktypes.AccAddress) ([]stakingtypes.Validator, error) { + amountOfValidators := len(tmValidators) + stakingValidators := make([]stakingtypes.Validator, 0, amountOfValidators) + operatorsCount := len(operatorsAddresses) + if operatorsCount != amountOfValidators { + panic(fmt.Sprintf("provided %d validator operator keys but need %d!", operatorsCount, amountOfValidators)) + } + for i, val := range tmValidators { + validator, err := createStakingValidator(val, bondedAmt, &operatorsAddresses[i]) + if err != nil { + return nil, err + } + stakingValidators = append(stakingValidators, validator) + } + return stakingValidators, nil +} + +// createDelegations creates delegations for the given validators and account +func createDelegations(validators []stakingtypes.Validator, fromAccount sdktypes.AccAddress) []stakingtypes.Delegation { + amountOfValidators := len(validators) + delegations := make([]stakingtypes.Delegation, 0, amountOfValidators) + for _, val := range validators { + delegation := stakingtypes.NewDelegation(fromAccount.String(), val.OperatorAddress, sdkmath.LegacyOneDec()) + delegations = append(delegations, delegation) + } + return delegations +} + +// getValidatorsSlashingGen creates the validators signingInfos and missedBlocks +// records necessary for the slashing module genesis +func getValidatorsSlashingGen(validators []stakingtypes.Validator, sk slashingtypes.StakingKeeper) (SlashingCustomGenesisState, error) { + valCount := len(validators) + signInfo := make([]slashingtypes.SigningInfo, valCount) + missedBlocks := make([]slashingtypes.ValidatorMissedBlocks, valCount) + for i, val := range validators { + consAddrBz, err := val.GetConsAddr() + if err != nil { + return SlashingCustomGenesisState{}, err + } + consAddr, err := sk.ConsensusAddressCodec().BytesToString(consAddrBz) + if err != nil { + return SlashingCustomGenesisState{}, err + } + signInfo[i] = slashingtypes.SigningInfo{ + Address: consAddr, + ValidatorSigningInfo: slashingtypes.ValidatorSigningInfo{ + Address: consAddr, + }, + } + missedBlocks[i] = slashingtypes.ValidatorMissedBlocks{ + Address: consAddr, + } + } + return SlashingCustomGenesisState{ + signingInfo: signInfo, + missedBlocks: missedBlocks, + }, nil +} + +// StakingCustomGenesisState defines the staking genesis state +type StakingCustomGenesisState struct { + denom string + + validators []stakingtypes.Validator + delegations []stakingtypes.Delegation +} + +// setDefaultStakingGenesisState sets the default staking genesis state +func setDefaultStakingGenesisState(evmosApp *app.App, genesisState evmostypes.GenesisState, overwriteParams StakingCustomGenesisState) evmostypes.GenesisState { + // Set staking params + stakingParams := stakingtypes.DefaultParams() + stakingParams.BondDenom = overwriteParams.denom + + stakingGenesis := stakingtypes.NewGenesisState( + stakingParams, + overwriteParams.validators, + overwriteParams.delegations, + ) + genesisState[stakingtypes.ModuleName] = evmosApp.AppCodec().MustMarshalJSON(stakingGenesis) + return genesisState +} + +type BankCustomGenesisState struct { + totalSupply sdktypes.Coins + balances []banktypes.Balance +} + +// setDefaultBankGenesisState sets the default bank genesis state +func setDefaultBankGenesisState(evmosApp *app.App, genesisState evmostypes.GenesisState, overwriteParams BankCustomGenesisState) evmostypes.GenesisState { + bankGenesis := banktypes.NewGenesisState( + banktypes.DefaultGenesisState().Params, + overwriteParams.balances, + overwriteParams.totalSupply, + []banktypes.Metadata{}, + []banktypes.SendEnabled{}, + ) + updatedBankGen := updateBankGenesisStateForChainID(evmosApp.ChainID(), *bankGenesis) + genesisState[banktypes.ModuleName] = evmosApp.AppCodec().MustMarshalJSON(&updatedBankGen) + return genesisState +} + +// SlashingCustomGenesisState defines the corresponding +// validators signing info and missed blocks for the genesis state +type SlashingCustomGenesisState struct { + signingInfo []slashingtypes.SigningInfo + missedBlocks []slashingtypes.ValidatorMissedBlocks +} + +// setDefaultSlashingGenesisState sets the default slashing genesis state +func setDefaultSlashingGenesisState(evmosApp *app.App, genesisState evmostypes.GenesisState, overwriteParams SlashingCustomGenesisState) evmostypes.GenesisState { + slashingGen := slashingtypes.DefaultGenesisState() + slashingGen.SigningInfos = overwriteParams.signingInfo + slashingGen.MissedBlocks = overwriteParams.missedBlocks + + genesisState[slashingtypes.ModuleName] = evmosApp.AppCodec().MustMarshalJSON(slashingGen) + return genesisState +} + +// setBankGenesisState updates the bank genesis state with custom genesis state +func setBankGenesisState(evmosApp *app.App, genesisState evmostypes.GenesisState, customGenesis interface{}) (evmostypes.GenesisState, error) { + customGen, ok := customGenesis.(*banktypes.GenesisState) + if !ok { + return nil, fmt.Errorf("invalid type %T for bank module genesis state", customGenesis) + } + + bankGen := &banktypes.GenesisState{} + evmosApp.AppCodec().MustUnmarshalJSON(genesisState[banktypes.ModuleName], bankGen) + + if len(customGen.Balances) > 0 { + coins := sdktypes.NewCoins() + bankGen.Balances = append(bankGen.Balances, customGen.Balances...) + for _, b := range customGen.Balances { + coins = append(coins, b.Coins...) + } + bankGen.Supply = bankGen.Supply.Add(coins...) + } + if len(customGen.DenomMetadata) > 0 { + bankGen.DenomMetadata = append(bankGen.DenomMetadata, customGen.DenomMetadata...) + } + + if len(customGen.SendEnabled) > 0 { + bankGen.SendEnabled = append(bankGen.SendEnabled, customGen.SendEnabled...) + } + + bankGen.Params = customGen.Params + + genesisState[banktypes.ModuleName] = evmosApp.AppCodec().MustMarshalJSON(bankGen) + return genesisState, nil +} + +// calculateTotalSupply calculates the total supply from the given balances +func calculateTotalSupply(fundedAccountsBalances []banktypes.Balance) sdktypes.Coins { + totalSupply := sdktypes.NewCoins() + for _, balance := range fundedAccountsBalances { + totalSupply = totalSupply.Add(balance.Coins...) + } + return totalSupply +} + +// addBondedModuleAccountToFundedBalances adds bonded amount to bonded pool module account and include it on funded accounts +func addBondedModuleAccountToFundedBalances( + fundedAccountsBalances []banktypes.Balance, + totalBonded sdktypes.Coin, +) []banktypes.Balance { + return append(fundedAccountsBalances, banktypes.Balance{ + Address: authtypes.NewModuleAddress(stakingtypes.BondedPoolName).String(), + Coins: sdktypes.Coins{totalBonded}, + }) +} + +// setDefaultAuthGenesisState sets the default auth genesis state +func setDefaultAuthGenesisState(evmosApp *app.App, genesisState evmostypes.GenesisState, genAccs []authtypes.GenesisAccount) evmostypes.GenesisState { + defaultAuthGen := authtypes.NewGenesisState(authtypes.DefaultParams(), genAccs) + genesisState[authtypes.ModuleName] = evmosApp.AppCodec().MustMarshalJSON(defaultAuthGen) + return genesisState +} + +// setAuthGenesisState updates the bank genesis state with custom genesis state +func setAuthGenesisState(evmosApp *app.App, genesisState evmostypes.GenesisState, customGenesis interface{}) (evmostypes.GenesisState, error) { + customGen, ok := customGenesis.(*authtypes.GenesisState) + if !ok { + return nil, fmt.Errorf("invalid type %T for auth module genesis state", customGenesis) + } + + authGen := &authtypes.GenesisState{} + evmosApp.AppCodec().MustUnmarshalJSON(genesisState[authtypes.ModuleName], authGen) + + if len(customGen.Accounts) > 0 { + authGen.Accounts = append(authGen.Accounts, customGen.Accounts...) + } + + authGen.Params = customGen.Params + + genesisState[authtypes.ModuleName] = evmosApp.AppCodec().MustMarshalJSON(authGen) + return genesisState, nil +} + +// GovCustomGenesisState defines the gov genesis state +type GovCustomGenesisState struct { + denom string +} + +// setDefaultGovGenesisState sets the default gov genesis state +func setDefaultGovGenesisState(evmosApp *app.App, genesisState evmostypes.GenesisState, overwriteParams GovCustomGenesisState) evmostypes.GenesisState { + govGen := govtypesv1.DefaultGenesisState() + updatedParams := govGen.Params + minDepositAmt := sdkmath.NewInt(1e18) + updatedParams.MinDeposit = sdktypes.NewCoins(sdktypes.NewCoin(overwriteParams.denom, minDepositAmt)) + updatedParams.ExpeditedMinDeposit = sdktypes.NewCoins(sdktypes.NewCoin(overwriteParams.denom, minDepositAmt)) + govGen.Params = updatedParams + genesisState[govtypes.ModuleName] = evmosApp.AppCodec().MustMarshalJSON(govGen) + return genesisState +} + +func setDefaultErc20GenesisState(evmosApp *app.App, genesisState evmostypes.GenesisState) evmostypes.GenesisState { + erc20Gen := erc20types.DefaultGenesisState() + updatedErc20Gen := updateErc20GenesisStateForChainID(evmosApp.ChainID(), *erc20Gen) + genesisState[erc20types.ModuleName] = evmosApp.AppCodec().MustMarshalJSON(&updatedErc20Gen) + return genesisState +} + +// defaultAuthGenesisState sets the default genesis state +// for the testing setup +func newDefaultGenesisState(evmosApp *app.App, params defaultGenesisParams) evmostypes.GenesisState { + genesisState := evmosApp.DefaultGenesis() + + genesisState = setDefaultAuthGenesisState(evmosApp, genesisState, params.genAccounts) + genesisState = setDefaultStakingGenesisState(evmosApp, genesisState, params.staking) + genesisState = setDefaultBankGenesisState(evmosApp, genesisState, params.bank) + genesisState = setDefaultGovGenesisState(evmosApp, genesisState, params.gov) + genesisState = setDefaultSlashingGenesisState(evmosApp, genesisState, params.slashing) + genesisState = setDefaultErc20GenesisState(evmosApp, genesisState) + + return genesisState +} + +// customizeGenesis modifies genesis state if there're any custom genesis state +// for specific modules +func customizeGenesis(evmosApp *app.App, customGen CustomGenesisState, genesisState evmostypes.GenesisState) (evmostypes.GenesisState, error) { + var err error + for mod, modGenState := range customGen { + if fn, found := genesisSetupFunctions[mod]; found { + genesisState, err = fn(evmosApp, genesisState, modGenState) + if err != nil { + return genesisState, err + } + } else { + panic(fmt.Sprintf("module %s not found in genesis setup functions", mod)) + } + } + return genesisState, err +} diff --git a/testutil/integration/exrp/network/unit_network.go b/testutil/integration/exrp/network/unit_network.go new file mode 100644 index 0000000..1413a57 --- /dev/null +++ b/testutil/integration/exrp/network/unit_network.go @@ -0,0 +1,56 @@ +// Copyright Tharsis Labs Ltd.(Evmos) +// SPDX-License-Identifier:ENCL-1.0(https://github.com/evmos/evmos/blob/main/LICENSE) +package exrpnetwork + +import ( + sdktypes "github.com/cosmos/cosmos-sdk/types" + "github.com/ethereum/go-ethereum/common" + "github.com/evmos/evmos/v20/x/evm/statedb" + inflationtypes "github.com/evmos/evmos/v20/x/inflation/v1/types" + "github.com/xrplevm/node/v4/app" +) + +// UnitTestNetwork is the implementation of the Network interface for unit tests. +// It embeds the IntegrationNetwork struct to reuse its methods and +// makes the App public for easier testing. +type UnitTestNetwork struct { + IntegrationNetwork + App *app.App +} + +var _ Network = (*UnitTestNetwork)(nil) + +// NewUnitTestNetwork configures and initializes a new Evmos Network instance with +// the given configuration options. If no configuration options are provided +// it uses the default configuration. +// +// It panics if an error occurs. +// Note: Only uses for Unit Tests +func NewUnitTestNetwork(opts ...ConfigOption) *UnitTestNetwork { + network := New(opts...) + return &UnitTestNetwork{ + IntegrationNetwork: *network, + App: network.app, + } +} + +// GetStateDB returns the state database for the current block. +func (n *UnitTestNetwork) GetStateDB() *statedb.StateDB { + headerHash := n.GetContext().HeaderHash() + return statedb.New( + n.GetContext(), + n.App.EvmKeeper, + statedb.NewEmptyTxConfig(common.BytesToHash(headerHash)), + ) +} + +// FundAccount funds the given account with the given amount of coins. +func (n *UnitTestNetwork) FundAccount(addr sdktypes.AccAddress, coins sdktypes.Coins) error { + ctx := n.GetContext() + + if err := n.app.BankKeeper.MintCoins(ctx, inflationtypes.ModuleName, coins); err != nil { + return err + } + + return n.app.BankKeeper.SendCoinsFromModuleToAccount(ctx, inflationtypes.ModuleName, addr, coins) +} From 40ab899610560960dfad2accc3d39122290100be Mon Sep 17 00:00:00 2001 From: GuillemGarciaDev Date: Fri, 22 Nov 2024 17:57:50 +0100 Subject: [PATCH 04/60] feat(tests): working on upgrade test suite --- tests/upgrade/suite.go | 15 +- tests/upgrade/suite_test.go | 3 +- .../exrp/network/chain_id_modifiers.go | 121 ---- testutil/integration/exrp/network/config.go | 59 +- .../integration/exrp/network/config_test.go | 1 + testutil/integration/exrp/network/network.go | 156 +++-- testutil/integration/exrp/network/setup.go | 552 ++---------------- 7 files changed, 131 insertions(+), 776 deletions(-) delete mode 100644 testutil/integration/exrp/network/chain_id_modifiers.go diff --git a/tests/upgrade/suite.go b/tests/upgrade/suite.go index 90d0e7b..70ae3b6 100644 --- a/tests/upgrade/suite.go +++ b/tests/upgrade/suite.go @@ -1,9 +1,6 @@ package testupgrade import ( - "encoding/json" - "os" - "github.com/stretchr/testify/suite" exrpnetwork "github.com/xrplevm/node/v4/testutil/integration/exrp/network" ) @@ -17,19 +14,9 @@ type UpgradeTestSuite struct { func (s *UpgradeTestSuite) SetupTest() { // READ APP STATE FILE - genesisBytes, err := os.ReadFile("../../exported-state.json") - s.Require().NoError(err) - - var genesisState exrpnetwork.CustomGenesisState - - err = json.Unmarshal(genesisBytes, &genesisState) - s.Require().NoError(err) - - appState := genesisState["app_state"].(map[string]interface{}) - s.network = NewUpgradeTestNetwork( // LOAD APP STATE FROM FILE - exrpnetwork.WithCustomGenesis(appState), + exrpnetwork.WithGenesisFile("exported-state.json"), ) s.Require().NotNil(s.network) diff --git a/tests/upgrade/suite_test.go b/tests/upgrade/suite_test.go index 70f9a0b..14a5b13 100644 --- a/tests/upgrade/suite_test.go +++ b/tests/upgrade/suite_test.go @@ -4,6 +4,7 @@ import ( "testing" "github.com/stretchr/testify/suite" + "github.com/xrplevm/node/v4/app" ) func TestUpgradeTestSuite(t *testing.T) { @@ -14,5 +15,5 @@ func (s *UpgradeTestSuite) TestUpgrade() { denom := s.network.GetDenom() s.Require().NotEmpty(denom) - s.Require().Equal(denom, "uxrp") + s.Require().Equal(denom, app.BaseDenom) } \ No newline at end of file diff --git a/testutil/integration/exrp/network/chain_id_modifiers.go b/testutil/integration/exrp/network/chain_id_modifiers.go deleted file mode 100644 index f36ecf3..0000000 --- a/testutil/integration/exrp/network/chain_id_modifiers.go +++ /dev/null @@ -1,121 +0,0 @@ -// Copyright Tharsis Labs Ltd.(Evmos) -// SPDX-License-Identifier:ENCL-1.0(https://github.com/evmos/evmos/blob/main/LICENSE) -// -// This files contains handler for the testing suite that has to be run to -// modify the chain configuration depending on the chainID - -package exrpnetwork - -import ( - banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" - "github.com/evmos/evmos/v20/utils" - erc20types "github.com/evmos/evmos/v20/x/erc20/types" -) - -// updateErc20GenesisStateForChainID modify the default genesis state for the -// bank module of the testing suite depending on the chainID. -func updateBankGenesisStateForChainID(chainID string, bankGenesisState banktypes.GenesisState) banktypes.GenesisState { - metadata := generateBankGenesisMetadata(chainID) - bankGenesisState.DenomMetadata = []banktypes.Metadata{metadata} - - return bankGenesisState -} - -// generateBankGenesisMetadata generates the metadata -// for the Evm coin depending on the chainID. -func generateBankGenesisMetadata(chainID string) banktypes.Metadata { - if utils.IsTestnet(chainID) { - return banktypes.Metadata{ - Description: "The native EVM, governance and staking token of the Evmos testnet", - Base: "atevmos", - DenomUnits: []*banktypes.DenomUnit{ - { - Denom: "atevmos", - Exponent: 0, - }, - { - Denom: "tevmos", - Exponent: 18, - }, - }, - Name: "tEvmos", - Symbol: "tEVMOS", - Display: "tevmos", - } - } - - return banktypes.Metadata{ - Description: "The native EVM, governance and staking token of the Evmos mainnet", - Base: "aevmos", - DenomUnits: []*banktypes.DenomUnit{ - { - Denom: "aevmos", - Exponent: 0, - }, - { - Denom: "evmos", - Exponent: 18, - }, - }, - Name: "Evmos", - Symbol: "EVMOS", - Display: "evmos", - } -} - -// updateErc20GenesisStateForChainID modify the default genesis state for the -// erc20 module on the testing suite depending on the chainID. -func updateErc20GenesisStateForChainID(chainID string, erc20GenesisState erc20types.GenesisState) erc20types.GenesisState { - if !utils.IsTestnet(chainID) { - return erc20GenesisState - } - - erc20GenesisState.Params = updateErc20Params(chainID, erc20GenesisState.Params) - erc20GenesisState.TokenPairs = updateErc20TokenPairs(chainID, erc20GenesisState.TokenPairs) - - return erc20GenesisState -} - -// updateErc20Params modifies the erc20 module params to use the correct -// WEVMOS contract depending on ChainID -func updateErc20Params(chainID string, params erc20types.Params) erc20types.Params { - // mainnetAddress := erc20types.GetWEVMOSContractHex(utils.MainnetChainID) - // testnetAddress := erc20types.GetWEVMOSContractHex(chainID) - - nativePrecompiles := make([]string, len(params.NativePrecompiles)) - for i, nativePrecompile := range params.NativePrecompiles { - // if nativePrecompile == mainnetAddress { - // nativePrecompiles[i] = testnetAddress - // } else { - // nativePrecompiles[i] = nativePrecompile - // } - nativePrecompiles[i] = nativePrecompile - } - params.NativePrecompiles = nativePrecompiles - return params -} - -// updateErc20TokenPairs modifies the erc20 token pairs to use the correct -// WEVMOS depending on ChainID -func updateErc20TokenPairs(chainID string, tokenPairs []erc20types.TokenPair) []erc20types.TokenPair { - // testnetAddress := erc20types.GetWEVMOSContractHex(chainID) - // coinInfo := evmtypes.ChainsCoinInfo[utils.MainnetChainID] - - // mainnetAddress := erc20types.GetWEVMOSContractHex(utils.MainnetChainID) - - updatedTokenPairs := make([]erc20types.TokenPair, len(tokenPairs)) - for i, tokenPair := range tokenPairs { - // if tokenPair.Erc20Address == mainnetAddress { - // updatedTokenPairs[i] = erc20types.TokenPair{ - // Erc20Address: testnetAddress, - // Denom: coinInfo.Denom, - // Enabled: tokenPair.Enabled, - // ContractOwner: tokenPair.ContractOwner, - // } - // } else { - // updatedTokenPairs[i] = tokenPair - // } - updatedTokenPairs[i] = tokenPair - } - return updatedTokenPairs -} diff --git a/testutil/integration/exrp/network/config.go b/testutil/integration/exrp/network/config.go index f1cc2f0..19aa024 100644 --- a/testutil/integration/exrp/network/config.go +++ b/testutil/integration/exrp/network/config.go @@ -4,20 +4,20 @@ package exrpnetwork import ( - "fmt" "math/big" + "os" "github.com/cosmos/cosmos-sdk/baseapp" sdktypes "github.com/cosmos/cosmos-sdk/types" - authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" - stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" - testtx "github.com/evmos/evmos/v20/testutil/tx" evmostypes "github.com/evmos/evmos/v20/types" - "github.com/evmos/evmos/v20/utils" "github.com/xrplevm/node/v4/app" ) +const ( + ChainID = "exrp_1440002-1" +) + // Config defines the configuration for a chain. // It allows for customization of the network to adjust to // testing needs. @@ -29,6 +29,7 @@ type Config struct { balances []banktypes.Balance denom string customGenesisState CustomGenesisState + genesisBytes []byte otherCoinDenom []string operatorsAddrs []sdktypes.AccAddress customBaseAppOpts []func(*baseapp.BaseApp) @@ -38,48 +39,16 @@ type CustomGenesisState map[string]interface{} // DefaultConfig returns the default configuration for a chain. func DefaultConfig() Config { - account, _ := testtx.NewAccAddressAndKey() return Config{ - chainID: utils.MainnetChainID + "-1", - eip155ChainID: big.NewInt(9001), - amountOfValidators: 3, - // Only one account besides the validators - preFundedAccounts: []sdktypes.AccAddress{account}, - // NOTE: Per default, the balances are left empty, and the pre-funded accounts are used. + chainID: ChainID, + eip155ChainID: big.NewInt(1440002), balances: nil, + // MODIFIED denom: app.BaseDenom, customGenesisState: nil, } } -// getGenAccountsAndBalances takes the network configuration and returns the used -// genesis accounts and balances. -// -// NOTE: If the balances are set, the pre-funded accounts are ignored. -func getGenAccountsAndBalances(cfg Config, validators []stakingtypes.Validator) (genAccounts []authtypes.GenesisAccount, balances []banktypes.Balance) { - if len(cfg.balances) > 0 { - balances = cfg.balances - accounts := getAccAddrsFromBalances(balances) - genAccounts = createGenesisAccounts(accounts) - } else { - genAccounts = createGenesisAccounts(cfg.preFundedAccounts) - balances = createBalances(cfg.preFundedAccounts, append(cfg.otherCoinDenom, cfg.denom)) - } - - // append validators to genesis accounts and balances - valAccs := make([]sdktypes.AccAddress, len(validators)) - for i, v := range validators { - valAddr, err := sdktypes.ValAddressFromBech32(v.OperatorAddress) - if err != nil { - panic(fmt.Sprintf("failed to derive validator address from %q: %s", v.OperatorAddress, err.Error())) - } - valAccs[i] = sdktypes.AccAddress(valAddr.Bytes()) - } - genAccounts = append(genAccounts, createGenesisAccounts(valAccs)...) - - return -} - // ConfigOption defines a function that can modify the NetworkConfig. // The purpose of this is to force to be declarative when the default configuration // requires to be changed. @@ -153,3 +122,13 @@ func WithCustomBaseAppOpts(opts ...func(*baseapp.BaseApp)) ConfigOption { cfg.customBaseAppOpts = opts } } + +func WithGenesisFile(genesisFile string) ConfigOption { + return func(cfg *Config) { + genesisBytes, err := os.ReadFile(genesisFile) + if err != nil { + panic(err) + } + cfg.genesisBytes = genesisBytes + } +} diff --git a/testutil/integration/exrp/network/config_test.go b/testutil/integration/exrp/network/config_test.go index 480acaf..0c5c897 100644 --- a/testutil/integration/exrp/network/config_test.go +++ b/testutil/integration/exrp/network/config_test.go @@ -16,6 +16,7 @@ import ( ) func TestWithBalances(t *testing.T) { + // MODIFIED key1Balance := sdk.NewCoins(sdk.NewInt64Coin(app.BaseDenom, 1e18)) key2Balance := sdk.NewCoins( sdk.NewInt64Coin(app.BaseDenom, 2e18), diff --git a/testutil/integration/exrp/network/network.go b/testutil/integration/exrp/network/network.go index 14dc958..5d4e748 100644 --- a/testutil/integration/exrp/network/network.go +++ b/testutil/integration/exrp/network/network.go @@ -4,6 +4,7 @@ package exrpnetwork import ( + "encoding/json" "fmt" "math" "math/big" @@ -11,7 +12,6 @@ import ( sdkmath "cosmossdk.io/math" - "github.com/evmos/evmos/v20/types" "github.com/xrplevm/node/v4/app" abcitypes "github.com/cometbft/cometbft/abci/types" @@ -20,12 +20,14 @@ import ( tmversion "github.com/cometbft/cometbft/proto/tendermint/version" cmttypes "github.com/cometbft/cometbft/types" "github.com/cometbft/cometbft/version" + "github.com/cosmos/cosmos-sdk/testutil/mock" sdktypes "github.com/cosmos/cosmos-sdk/types" sdktestutil "github.com/cosmos/cosmos-sdk/types/module/testutil" txtypes "github.com/cosmos/cosmos-sdk/types/tx" govtypes "github.com/cosmos/cosmos-sdk/x/gov/types/v1" stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" commonnetwork "github.com/evmos/evmos/v20/testutil/integration/common/network" + "github.com/evmos/evmos/v20/types" erc20types "github.com/evmos/evmos/v20/x/erc20/types" evmtypes "github.com/evmos/evmos/v20/x/evm/types" feemarkettypes "github.com/evmos/evmos/v20/x/feemarket/types" @@ -39,6 +41,7 @@ type Network interface { commonnetwork.Network GetEIP155ChainID() *big.Int + // MODIFIED // GetEVMChainConfig() *gethparams.ChainConfig // Clients @@ -57,7 +60,7 @@ type IntegrationNetwork struct { cfg Config ctx sdktypes.Context validators []stakingtypes.Validator - app *app.App + app *app.App // This is only needed for IBC chain testing setup valSet *cmttypes.ValidatorSet @@ -97,97 +100,69 @@ var ( PrefundedAccountInitialBalance, _ = sdkmath.NewIntFromString("100_000_000_000_000_000_000_000") // 100k ) +func getValidatorsAndSignersFromCustomGenesisState(stakingState stakingtypes.GenesisState) (*cmttypes.ValidatorSet, map[string]cmttypes.PrivValidator, error) { + genesisValidators := stakingState.Validators + validators := make([]stakingtypes.Validator, 0, len(genesisValidators)) + + tmValidators := make([]*cmttypes.Validator, 0, len(validators)) + valSigners := make(map[string]cmttypes.PrivValidator, len(validators)) + + for i := 0; i < len(validators); i++ { + privVal := mock.NewPV() + pubKey, _ := privVal.GetPubKey() + validator := cmttypes.NewValidator(pubKey, 1) + tmValidators = append(tmValidators, validator) + valSigners[pubKey.Address().String()] = privVal + } + + return cmttypes.NewValidatorSet(tmValidators), valSigners, nil +} + // configureAndInitChain initializes the network with the given configuration. // It creates the genesis state and starts the network. func (n *IntegrationNetwork) configureAndInitChain() error { - // // Create validator set with the amount of validators specified in the config - // // with the default power of 1. - // valSet, valSigners := createValidatorSetAndSigners(n.cfg.amountOfValidators) - // totalBonded := DefaultBondedAmount.Mul(sdkmath.NewInt(int64(n.cfg.amountOfValidators))) - - // // Build staking type validators and delegations - validators, err := createStakingValidators(valSet.Validators, DefaultBondedAmount, n.cfg.operatorsAddrs) - // if err != nil { - // return err - // } - - // // Create genesis accounts and funded balances based on the config - // genAccounts, fundedAccountBalances := getGenAccountsAndBalances(n.cfg, validators) - - // fundedAccountBalances = addBondedModuleAccountToFundedBalances( - // fundedAccountBalances, - // sdktypes.NewCoin(n.cfg.denom, totalBonded), - // ) - - // delegations := createDelegations(validators, genAccounts[0].GetAddress()) - - // // Create a new EvmosApp with the following params - // evmosApp := createExrpApp(n.cfg.chainID, n.cfg.customBaseAppOpts...) - - // stakingParams := StakingCustomGenesisState{ - // denom: n.cfg.denom, - // validators: validators, - // delegations: delegations, - // } - // govParams := GovCustomGenesisState{ - // denom: n.cfg.denom, - // } - - // totalSupply := calculateTotalSupply(fundedAccountBalances) - // bankParams := BankCustomGenesisState{ - // totalSupply: totalSupply, - // balances: fundedAccountBalances, - // } - - // // Get the corresponding slashing info and missed block info - // // for the created validators - // slashingParams, err := getValidatorsSlashingGen(validators, evmosApp.StakingKeeper) - // if err != nil { - // return err - // } - - // // Configure Genesis state - // genesisState := newDefaultGenesisState( - // evmosApp, - // defaultGenesisParams{ - // genAccounts: genAccounts, - // staking: stakingParams, - // bank: bankParams, - // slashing: slashingParams, - // gov: govParams, - // }, - // ) - - // // modify genesis state if there're any custom genesis state - // // for specific modules - // genesisState, err = customizeGenesis(evmosApp, n.cfg.customGenesisState, genesisState) - // if err != nil { - // return err - // } - - // ============================================================ - - evmosApp := createExrpApp(n.cfg.chainID, n.cfg.customBaseAppOpts...) - genesisState := n.cfg.customGenesisState - - // Init chain + // Create a new EvmosApp with the following params + exrpApp := createExrpApp(n.cfg.chainID, n.cfg.customBaseAppOpts...) + + var genesisState app.GenesisState + err := json.Unmarshal(n.cfg.genesisBytes, &genesisState) + if err != nil { + return fmt.Errorf("error unmarshalling genesis state: %w", err) + } + stateBytes, err := cmtjson.MarshalIndent(genesisState, "", " ") if err != nil { - return err + return fmt.Errorf("error marshalling genesis state bytes: %w", err) + } + + var appState map[string]json.RawMessage + err = json.Unmarshal(genesisState["app_state"], &appState) + if err != nil { + return fmt.Errorf("error unmarshalling app state: %w", err) + } + + var stakingState stakingtypes.GenesisState + err = exrpApp.AppCodec().UnmarshalJSON(appState["staking"], &stakingState) + if err != nil { + return fmt.Errorf("error unmarshalling staking state: %w", err) + } + + valSet, valSigners, err := getValidatorsAndSignersFromCustomGenesisState(stakingState) + if err != nil { + return fmt.Errorf("error getting validators and signers from custom genesis state: %w", err) } // Consensus module does not have a genesis state on the app, // but can customize the consensus parameters of the chain on initialization - consensusParams := &cmtproto.ConsensusParams{} - // if gen, ok := n.cfg.customGenesisState[consensustypes.ModuleName]; ok { - // consensusParams, ok = gen.(*cmtproto.ConsensusParams) - // if !ok { - // return fmt.Errorf("invalid type for consensus parameters. Expected: cmtproto.ConsensusParams, got %T", gen) - // } - // } + + fmt.Println("consensus_params", genesisState["consensus_params"]) + + var consensusParams *cmtproto.ConsensusParams + _ = json.Unmarshal(genesisState["consensus_params"], &consensusParams) + now := time.Now().UTC() - if _, err := evmosApp.InitChain( + if _, err := exrpApp.InitChain( &abcitypes.RequestInitChain{ Time: now, ChainId: n.cfg.chainID, @@ -201,8 +176,8 @@ func (n *IntegrationNetwork) configureAndInitChain() error { header := cmtproto.Header{ ChainID: n.cfg.chainID, - Height: evmosApp.LastBlockHeight() + 1, - AppHash: evmosApp.LastCommitID().Hash, + Height: exrpApp.LastBlockHeight() + 1, + AppHash: exrpApp.LastCommitID().Hash, Time: now, ValidatorsHash: valSet.Hash(), NextValidatorsHash: valSet.Hash(), @@ -213,15 +188,15 @@ func (n *IntegrationNetwork) configureAndInitChain() error { } req := buildFinalizeBlockReq(header, valSet.Validators) - if _, err := evmosApp.FinalizeBlock(req); err != nil { + if _, err := exrpApp.FinalizeBlock(req); err != nil { return err } // TODO - this might not be the best way to initilize the context - n.ctx = evmosApp.BaseApp.NewContextLegacy(false, header) + n.ctx = exrpApp.BaseApp.NewContextLegacy(false, header) // Commit genesis changes - if _, err := evmosApp.Commit(); err != nil { + if _, err := exrpApp.Commit(); err != nil { return err } @@ -231,11 +206,11 @@ func (n *IntegrationNetwork) configureAndInitChain() error { blockMaxGas = uint64(consensusParams.Block.MaxGas) //nolint:gosec // G115 } - n.app = evmosApp + n.app = exrpApp n.ctx = n.ctx.WithConsensusParams(*consensusParams) n.ctx = n.ctx.WithBlockGasMeter(types.NewInfiniteGasMeterWithLimit(blockMaxGas)) - n.validators = validators + n.validators = stakingState.Validators n.valSet = valSet n.valSigners = valSigners @@ -263,7 +238,8 @@ func (n *IntegrationNetwork) GetEIP155ChainID() *big.Int { return n.cfg.eip155ChainID } -// // GetChainConfig returns the network's chain config +// MODIFIED +// GetChainConfig returns the network's chain config // func (n *IntegrationNetwork) GetEVMChainConfig() *gethparams.ChainConfig { // return evmtypes.GetEthChainConfig() // } diff --git a/testutil/integration/exrp/network/setup.go b/testutil/integration/exrp/network/setup.go index 2e4ad94..dc4f716 100644 --- a/testutil/integration/exrp/network/setup.go +++ b/testutil/integration/exrp/network/setup.go @@ -5,238 +5,82 @@ package exrpnetwork import ( "fmt" - "slices" - "time" + + "github.com/xrplevm/node/v4/app" "github.com/cosmos/cosmos-sdk/baseapp" - "github.com/cosmos/cosmos-sdk/testutil/mock" "github.com/cosmos/gogoproto/proto" - "github.com/xrplevm/node/v4/app" "cosmossdk.io/log" - sdkmath "cosmossdk.io/math" - evidencetypes "cosmossdk.io/x/evidence/types" - "cosmossdk.io/x/feegrant" - upgradetypes "cosmossdk.io/x/upgrade/types" - cmttypes "github.com/cometbft/cometbft/types" dbm "github.com/cosmos/cosmos-db" - codectypes "github.com/cosmos/cosmos-sdk/codec/types" - cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec" simutils "github.com/cosmos/cosmos-sdk/testutil/sims" - sdktypes "github.com/cosmos/cosmos-sdk/types" + erc20types "github.com/evmos/evmos/v20/x/erc20/types" + evmtypes "github.com/evmos/evmos/v20/x/evm/types" + feemarkettypes "github.com/evmos/evmos/v20/x/feemarket/types" + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" - authztypes "github.com/cosmos/cosmos-sdk/x/authz" banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" - consensustypes "github.com/cosmos/cosmos-sdk/x/consensus/types" - crisistypes "github.com/cosmos/cosmos-sdk/x/crisis/types" distrtypes "github.com/cosmos/cosmos-sdk/x/distribution/types" genutiltypes "github.com/cosmos/cosmos-sdk/x/genutil/types" govtypes "github.com/cosmos/cosmos-sdk/x/gov/types" govtypesv1 "github.com/cosmos/cosmos-sdk/x/gov/types/v1" - "github.com/cosmos/cosmos-sdk/x/params/types/proposal" - slashingtypes "github.com/cosmos/cosmos-sdk/x/slashing/types" - stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" capabilitytypes "github.com/cosmos/ibc-go/modules/capability/types" - evmostypes "github.com/evmos/evmos/v20/types" - epochstypes "github.com/evmos/evmos/v20/x/epochs/types" - erc20types "github.com/evmos/evmos/v20/x/erc20/types" - evmtypes "github.com/evmos/evmos/v20/x/evm/types" - feemarkettypes "github.com/evmos/evmos/v20/x/feemarket/types" - infltypes "github.com/evmos/evmos/v20/x/inflation/v1/types" - vestingtypes "github.com/evmos/evmos/v20/x/vesting/types" - poatypes "github.com/xrplevm/node/v4/x/poa/types" ) // genSetupFn is the type for the module genesis setup functions -type genSetupFn func(evmosApp *app.App, genesisState evmostypes.GenesisState, customGenesis interface{}) (evmostypes.GenesisState, error) +type genSetupFn func(evmosApp *app.App, genesisState app.GenesisState, customGenesis interface{}) (app.GenesisState, error) -// defaultGenesisParams contains the params that are needed to -// setup the default genesis for the testing setup -type defaultGenesisParams struct { - genAccounts []authtypes.GenesisAccount - staking StakingCustomGenesisState - slashing SlashingCustomGenesisState - bank BankCustomGenesisState - gov GovCustomGenesisState -} -// genesisSetupFunctions contains the available genesis setup functions -// that can be used to customize the network genesis var genesisSetupFunctions = map[string]genSetupFn{ - evmtypes.ModuleName: func(_ *app.App, genesisState evmostypes.GenesisState, _ interface{}) (evmostypes.GenesisState, error) { - return genesisState, nil - }, - // erc20types.ModuleName: genStateSetter[*erc20types.GenesisState](erc20types.ModuleName), - erc20types.ModuleName: func(_ *app.App, genesisState evmostypes.GenesisState, _ interface{}) (evmostypes.GenesisState, error) { - // no-op. Consensus does not have a genesis state on the application - // but the params are used on it - // (e.g. block max gas, max bytes). - // This is handled accordingly on chain and context initialization - return genesisState, nil - }, - // govtypes.ModuleName: genStateSetter[*govtypesv1.GenesisState](govtypes.ModuleName), - infltypes.ModuleName: func(_ *app.App, genesisState evmostypes.GenesisState, _ interface{}) (evmostypes.GenesisState, error) { - return genesisState, nil - }, - feemarkettypes.ModuleName: func(_ *app.App, genesisState evmostypes.GenesisState, _ interface{}) (evmostypes.GenesisState, error) { - // no-op. Consensus does not have a genesis state on the application - // but the params are used on it - // (e.g. block max gas, max bytes). - // This is handled accordingly on chain and context initialization - return genesisState, nil - }, - distrtypes.ModuleName: func(_ *app.App, genesisState evmostypes.GenesisState, _ interface{}) (evmostypes.GenesisState, error) { - return genesisState, nil - }, - slashingtypes.ModuleName: func(_ *app.App, genesisState evmostypes.GenesisState, _ interface{}) (evmostypes.GenesisState, error) { - return genesisState, nil - }, - authztypes.ModuleName: func(_ *app.App, genesisState evmostypes.GenesisState, _ interface{}) (evmostypes.GenesisState, error) { - return genesisState, nil - }, - banktypes.ModuleName: func(_ *app.App, genesisState evmostypes.GenesisState, _ interface{}) (evmostypes.GenesisState, error) { - return genesisState, nil - }, - // authtypes.ModuleName: setAuthGenesisState, - authtypes.ModuleName: func(_ *app.App, genesisState evmostypes.GenesisState, _ interface{}) (evmostypes.GenesisState, error) { - return genesisState, nil - }, - epochstypes.ModuleName: func(_ *app.App, genesisState evmostypes.GenesisState, _ interface{}) (evmostypes.GenesisState, error) { - return genesisState, nil - }, - consensustypes.ModuleName: func(_ *app.App, genesisState evmostypes.GenesisState, _ interface{}) (evmostypes.GenesisState, error) { - // no-op. Consensus does not have a genesis state on the application - // but the params are used on it - // (e.g. block max gas, max bytes). - // This is handled accordingly on chain and context initialization - return genesisState, nil - }, - // capabilitytypes.ModuleName: genStateSetter[*capabilitytypes.GenesisState](capabilitytypes.ModuleName), - capabilitytypes.ModuleName: func(_ *app.App, genesisState evmostypes.GenesisState, _ interface{}) (evmostypes.GenesisState, error) { - // no-op. Consensus does not have a genesis state on the application - // but the params are used on it - // (e.g. block max gas, max bytes). - // This is handled accordingly on chain and context initialization - return genesisState, nil - }, - stakingtypes.ModuleName: func(_ *app.App, genesisState evmostypes.GenesisState, _ interface{}) (evmostypes.GenesisState, error) { - return genesisState, nil - }, - evidencetypes.ModuleName: func(_ *app.App, genesisState evmostypes.GenesisState, _ interface{}) (evmostypes.GenesisState, error) { - return genesisState, nil - }, - proposal.ModuleName: func(_ *app.App, genesisState evmostypes.GenesisState, customGenesis interface{}) (evmostypes.GenesisState, error) { - return genesisState, nil - }, - poatypes.ModuleName: func(_ *app.App, genesisState evmostypes.GenesisState, _ interface{}) (evmostypes.GenesisState, error) { - return genesisState, nil - }, - govtypes.ModuleName: func(_ *app.App, genesisState evmostypes.GenesisState, _ interface{}) (evmostypes.GenesisState, error) { - // no-op. Consensus does not have a genesis state on the application - // but the params are used on it - // (e.g. block max gas, max bytes). - // This is handled accordingly on chain and context initialization - return genesisState, nil - }, - upgradetypes.ModuleName: func(_ *app.App, genesisState evmostypes.GenesisState, _ interface{}) (evmostypes.GenesisState, error) { - return genesisState, nil - }, - feegrant.ModuleName: func(_ *app.App, genesisState evmostypes.GenesisState, _ interface{}) (evmostypes.GenesisState, error) { - return genesisState, nil - }, - "ibc": func(_ *app.App, genesisState evmostypes.GenesisState, _ interface{}) (evmostypes.GenesisState, error) { - return genesisState, nil - }, - genutiltypes.ModuleName: func(_ *app.App, genesisState evmostypes.GenesisState, _ interface{}) (evmostypes.GenesisState, error) { - return genesisState, nil - }, - vestingtypes.ModuleName: func(_ *app.App, genesisState evmostypes.GenesisState, _ interface{}) (evmostypes.GenesisState, error) { - return genesisState, nil - }, - crisistypes.ModuleName: func(_ *app.App, genesisState evmostypes.GenesisState, _ interface{}) (evmostypes.GenesisState, error) { - return genesisState, nil - }, - "transfer": func(_ *app.App, genesisState evmostypes.GenesisState, _ interface{}) (evmostypes.GenesisState, error) { - return genesisState, nil - }, + evmtypes.ModuleName: genStateSetter[*evmtypes.GenesisState](evmtypes.ModuleName), + erc20types.ModuleName: genStateSetter[*erc20types.GenesisState](erc20types.ModuleName), + govtypes.ModuleName: genStateSetter[*govtypesv1.GenesisState](govtypes.ModuleName), + feemarkettypes.ModuleName: genStateSetter[*feemarkettypes.GenesisState](feemarkettypes.ModuleName), + distrtypes.ModuleName: genStateSetter[*distrtypes.GenesisState](distrtypes.ModuleName), + banktypes.ModuleName: genStateSetter[*banktypes.GenesisState](banktypes.ModuleName), + authtypes.ModuleName: genStateSetter[*authtypes.GenesisState](authtypes.ModuleName), + capabilitytypes.ModuleName: genStateSetter[*capabilitytypes.GenesisState](capabilitytypes.ModuleName), + genutiltypes.ModuleName: genStateSetter[*genutiltypes.GenesisState](genutiltypes.ModuleName), } // genStateSetter is a generic function to set module-specific genesis state func genStateSetter[T proto.Message](moduleName string) genSetupFn { - return func(evmosApp *app.App, genesisState evmostypes.GenesisState, customGenesis interface{}) (evmostypes.GenesisState, error) { - moduleGenesis, ok := customGenesis.(T) - if !ok { - return nil, fmt.Errorf("invalid type %T for %s module genesis state", customGenesis, moduleName) + return func(exrpApp *app.App, genesisState app.GenesisState, customGenesis interface{}) (app.GenesisState, error) { + var customGen T + err := exrpApp.AppCodec().UnmarshalJSON(genesisState[moduleName], customGen) + + if err != nil { + return nil, fmt.Errorf("error unmarshalling %s module genesis state: %w", moduleName, err) } + // moduleGenesis, ok := customGenesis.(T) + // if !ok { + // return nil, fmt.Errorf("invalid type %T for %s module genesis state", customGenesis, moduleName) + // } - genesisState[moduleName] = evmosApp.AppCodec().MustMarshalJSON(moduleGenesis) + genesisState[moduleName] = exrpApp.AppCodec().MustMarshalJSON(customGen) return genesisState, nil } } -// createValidatorSetAndSigners creates validator set with the amount of validators specified -// with the default power of 1. -func createValidatorSetAndSigners(numberOfValidators int) (*cmttypes.ValidatorSet, map[string]cmttypes.PrivValidator) { - // Create validator set - tmValidators := make([]*cmttypes.Validator, 0, numberOfValidators) - signers := make(map[string]cmttypes.PrivValidator, numberOfValidators) - - for i := 0; i < numberOfValidators; i++ { - privVal := mock.NewPV() - pubKey, _ := privVal.GetPubKey() - validator := cmttypes.NewValidator(pubKey, 1) - tmValidators = append(tmValidators, validator) - signers[pubKey.Address().String()] = privVal - } - - return cmttypes.NewValidatorSet(tmValidators), signers -} - -// createGenesisAccounts returns a slice of genesis accounts from the given -// account addresses. -func createGenesisAccounts(accounts []sdktypes.AccAddress) []authtypes.GenesisAccount { - numberOfAccounts := len(accounts) - genAccounts := make([]authtypes.GenesisAccount, 0, numberOfAccounts) - for _, acc := range accounts { - genAccounts = append(genAccounts, authtypes.NewBaseAccount( - acc, nil, 0, 0), - ) - } - return genAccounts -} - -// getAccAddrsFromBalances returns a slice of genesis accounts from the -// given balances. -func getAccAddrsFromBalances(balances []banktypes.Balance) []sdktypes.AccAddress { - numberOfBalances := len(balances) - genAccounts := make([]sdktypes.AccAddress, 0, numberOfBalances) - for _, balance := range balances { - genAccounts = append(genAccounts, sdktypes.AccAddress(balance.Address)) - } - return genAccounts -} - -// createBalances creates balances for the given accounts and coin -func createBalances(accounts []sdktypes.AccAddress, denoms []string) []banktypes.Balance { - slices.Sort(denoms) - numberOfAccounts := len(accounts) - coins := make([]sdktypes.Coin, len(denoms)) - for i, denom := range denoms { - coins[i] = sdktypes.NewCoin(denom, PrefundedAccountInitialBalance) - } - fundedAccountBalances := make([]banktypes.Balance, 0, numberOfAccounts) - for _, acc := range accounts { - balance := banktypes.Balance{ - Address: acc.String(), - Coins: coins, +// customizeGenesis modifies genesis state if there're any custom genesis state +// for specific modules +func customizeGenesis(exrpApp *app.App, customGen CustomGenesisState, genesisState app.GenesisState) (app.GenesisState, error) { + var err error + for mod, modGenState := range customGen { + if fn, found := genesisSetupFunctions[mod]; found { + genesisState, err = fn(exrpApp, genesisState, modGenState) + if err != nil { + fmt.Println("error", err) + return genesisState, err + } + } else { + panic(fmt.Sprintf("module %s not found in genesis setup functions", mod)) } - - fundedAccountBalances = append(fundedAccountBalances, balance) } - return fundedAccountBalances + return genesisState, err } -// createExrpApp creates an exrp app +// createExrpApp creates an evmos app func createExrpApp(chainID string, customBaseAppOptions ...func(*baseapp.BaseApp)) *app.App { // Create evmos app db := dbm.NewMemDB() @@ -260,315 +104,3 @@ func createExrpApp(chainID string, customBaseAppOptions ...func(*baseapp.BaseApp baseAppOptions..., ) } - -// createStakingValidator creates a staking validator from the given tm validator and bonded -func createStakingValidator(val *cmttypes.Validator, bondedAmt sdkmath.Int, operatorAddr *sdktypes.AccAddress) (stakingtypes.Validator, error) { - pk, err := cryptocodec.FromTmPubKeyInterface(val.PubKey) //nolint:staticcheck - if err != nil { - return stakingtypes.Validator{}, err - } - - pkAny, err := codectypes.NewAnyWithValue(pk) - if err != nil { - return stakingtypes.Validator{}, err - } - - opAddr := sdktypes.ValAddress(val.Address).String() - if operatorAddr != nil { - opAddr = sdktypes.ValAddress(operatorAddr.Bytes()).String() - } - - // Default to 5% commission - commission := stakingtypes.NewCommission(sdkmath.LegacyNewDecWithPrec(5, 2), sdkmath.LegacyNewDecWithPrec(2, 1), sdkmath.LegacyNewDecWithPrec(5, 2)) - validator := stakingtypes.Validator{ - OperatorAddress: opAddr, - ConsensusPubkey: pkAny, - Jailed: false, - Status: stakingtypes.Bonded, - Tokens: bondedAmt, - DelegatorShares: sdkmath.LegacyOneDec(), - Description: stakingtypes.Description{}, - UnbondingHeight: int64(0), - UnbondingTime: time.Unix(0, 0).UTC(), - Commission: commission, - MinSelfDelegation: sdkmath.ZeroInt(), - } - return validator, nil -} - -// createStakingValidators creates staking validators from the given tm validators and bonded -// amounts -func createStakingValidators(tmValidators []*cmttypes.Validator, bondedAmt sdkmath.Int, operatorsAddresses []sdktypes.AccAddress) ([]stakingtypes.Validator, error) { - if len(operatorsAddresses) == 0 { - return createStakingValidatorsWithRandomOperator(tmValidators, bondedAmt) - } - return createStakingValidatorsWithSpecificOperator(tmValidators, bondedAmt, operatorsAddresses) -} - -// createStakingValidatorsWithRandomOperator creates staking validators with non-specified operator addresses. -func createStakingValidatorsWithRandomOperator(tmValidators []*cmttypes.Validator, bondedAmt sdkmath.Int) ([]stakingtypes.Validator, error) { - amountOfValidators := len(tmValidators) - stakingValidators := make([]stakingtypes.Validator, 0, amountOfValidators) - for _, val := range tmValidators { - validator, err := createStakingValidator(val, bondedAmt, nil) - if err != nil { - return nil, err - } - stakingValidators = append(stakingValidators, validator) - } - return stakingValidators, nil -} - -// createStakingValidatorsWithSpecificOperator creates staking validators with the given operator addresses. -func createStakingValidatorsWithSpecificOperator(tmValidators []*cmttypes.Validator, bondedAmt sdkmath.Int, operatorsAddresses []sdktypes.AccAddress) ([]stakingtypes.Validator, error) { - amountOfValidators := len(tmValidators) - stakingValidators := make([]stakingtypes.Validator, 0, amountOfValidators) - operatorsCount := len(operatorsAddresses) - if operatorsCount != amountOfValidators { - panic(fmt.Sprintf("provided %d validator operator keys but need %d!", operatorsCount, amountOfValidators)) - } - for i, val := range tmValidators { - validator, err := createStakingValidator(val, bondedAmt, &operatorsAddresses[i]) - if err != nil { - return nil, err - } - stakingValidators = append(stakingValidators, validator) - } - return stakingValidators, nil -} - -// createDelegations creates delegations for the given validators and account -func createDelegations(validators []stakingtypes.Validator, fromAccount sdktypes.AccAddress) []stakingtypes.Delegation { - amountOfValidators := len(validators) - delegations := make([]stakingtypes.Delegation, 0, amountOfValidators) - for _, val := range validators { - delegation := stakingtypes.NewDelegation(fromAccount.String(), val.OperatorAddress, sdkmath.LegacyOneDec()) - delegations = append(delegations, delegation) - } - return delegations -} - -// getValidatorsSlashingGen creates the validators signingInfos and missedBlocks -// records necessary for the slashing module genesis -func getValidatorsSlashingGen(validators []stakingtypes.Validator, sk slashingtypes.StakingKeeper) (SlashingCustomGenesisState, error) { - valCount := len(validators) - signInfo := make([]slashingtypes.SigningInfo, valCount) - missedBlocks := make([]slashingtypes.ValidatorMissedBlocks, valCount) - for i, val := range validators { - consAddrBz, err := val.GetConsAddr() - if err != nil { - return SlashingCustomGenesisState{}, err - } - consAddr, err := sk.ConsensusAddressCodec().BytesToString(consAddrBz) - if err != nil { - return SlashingCustomGenesisState{}, err - } - signInfo[i] = slashingtypes.SigningInfo{ - Address: consAddr, - ValidatorSigningInfo: slashingtypes.ValidatorSigningInfo{ - Address: consAddr, - }, - } - missedBlocks[i] = slashingtypes.ValidatorMissedBlocks{ - Address: consAddr, - } - } - return SlashingCustomGenesisState{ - signingInfo: signInfo, - missedBlocks: missedBlocks, - }, nil -} - -// StakingCustomGenesisState defines the staking genesis state -type StakingCustomGenesisState struct { - denom string - - validators []stakingtypes.Validator - delegations []stakingtypes.Delegation -} - -// setDefaultStakingGenesisState sets the default staking genesis state -func setDefaultStakingGenesisState(evmosApp *app.App, genesisState evmostypes.GenesisState, overwriteParams StakingCustomGenesisState) evmostypes.GenesisState { - // Set staking params - stakingParams := stakingtypes.DefaultParams() - stakingParams.BondDenom = overwriteParams.denom - - stakingGenesis := stakingtypes.NewGenesisState( - stakingParams, - overwriteParams.validators, - overwriteParams.delegations, - ) - genesisState[stakingtypes.ModuleName] = evmosApp.AppCodec().MustMarshalJSON(stakingGenesis) - return genesisState -} - -type BankCustomGenesisState struct { - totalSupply sdktypes.Coins - balances []banktypes.Balance -} - -// setDefaultBankGenesisState sets the default bank genesis state -func setDefaultBankGenesisState(evmosApp *app.App, genesisState evmostypes.GenesisState, overwriteParams BankCustomGenesisState) evmostypes.GenesisState { - bankGenesis := banktypes.NewGenesisState( - banktypes.DefaultGenesisState().Params, - overwriteParams.balances, - overwriteParams.totalSupply, - []banktypes.Metadata{}, - []banktypes.SendEnabled{}, - ) - updatedBankGen := updateBankGenesisStateForChainID(evmosApp.ChainID(), *bankGenesis) - genesisState[banktypes.ModuleName] = evmosApp.AppCodec().MustMarshalJSON(&updatedBankGen) - return genesisState -} - -// SlashingCustomGenesisState defines the corresponding -// validators signing info and missed blocks for the genesis state -type SlashingCustomGenesisState struct { - signingInfo []slashingtypes.SigningInfo - missedBlocks []slashingtypes.ValidatorMissedBlocks -} - -// setDefaultSlashingGenesisState sets the default slashing genesis state -func setDefaultSlashingGenesisState(evmosApp *app.App, genesisState evmostypes.GenesisState, overwriteParams SlashingCustomGenesisState) evmostypes.GenesisState { - slashingGen := slashingtypes.DefaultGenesisState() - slashingGen.SigningInfos = overwriteParams.signingInfo - slashingGen.MissedBlocks = overwriteParams.missedBlocks - - genesisState[slashingtypes.ModuleName] = evmosApp.AppCodec().MustMarshalJSON(slashingGen) - return genesisState -} - -// setBankGenesisState updates the bank genesis state with custom genesis state -func setBankGenesisState(evmosApp *app.App, genesisState evmostypes.GenesisState, customGenesis interface{}) (evmostypes.GenesisState, error) { - customGen, ok := customGenesis.(*banktypes.GenesisState) - if !ok { - return nil, fmt.Errorf("invalid type %T for bank module genesis state", customGenesis) - } - - bankGen := &banktypes.GenesisState{} - evmosApp.AppCodec().MustUnmarshalJSON(genesisState[banktypes.ModuleName], bankGen) - - if len(customGen.Balances) > 0 { - coins := sdktypes.NewCoins() - bankGen.Balances = append(bankGen.Balances, customGen.Balances...) - for _, b := range customGen.Balances { - coins = append(coins, b.Coins...) - } - bankGen.Supply = bankGen.Supply.Add(coins...) - } - if len(customGen.DenomMetadata) > 0 { - bankGen.DenomMetadata = append(bankGen.DenomMetadata, customGen.DenomMetadata...) - } - - if len(customGen.SendEnabled) > 0 { - bankGen.SendEnabled = append(bankGen.SendEnabled, customGen.SendEnabled...) - } - - bankGen.Params = customGen.Params - - genesisState[banktypes.ModuleName] = evmosApp.AppCodec().MustMarshalJSON(bankGen) - return genesisState, nil -} - -// calculateTotalSupply calculates the total supply from the given balances -func calculateTotalSupply(fundedAccountsBalances []banktypes.Balance) sdktypes.Coins { - totalSupply := sdktypes.NewCoins() - for _, balance := range fundedAccountsBalances { - totalSupply = totalSupply.Add(balance.Coins...) - } - return totalSupply -} - -// addBondedModuleAccountToFundedBalances adds bonded amount to bonded pool module account and include it on funded accounts -func addBondedModuleAccountToFundedBalances( - fundedAccountsBalances []banktypes.Balance, - totalBonded sdktypes.Coin, -) []banktypes.Balance { - return append(fundedAccountsBalances, banktypes.Balance{ - Address: authtypes.NewModuleAddress(stakingtypes.BondedPoolName).String(), - Coins: sdktypes.Coins{totalBonded}, - }) -} - -// setDefaultAuthGenesisState sets the default auth genesis state -func setDefaultAuthGenesisState(evmosApp *app.App, genesisState evmostypes.GenesisState, genAccs []authtypes.GenesisAccount) evmostypes.GenesisState { - defaultAuthGen := authtypes.NewGenesisState(authtypes.DefaultParams(), genAccs) - genesisState[authtypes.ModuleName] = evmosApp.AppCodec().MustMarshalJSON(defaultAuthGen) - return genesisState -} - -// setAuthGenesisState updates the bank genesis state with custom genesis state -func setAuthGenesisState(evmosApp *app.App, genesisState evmostypes.GenesisState, customGenesis interface{}) (evmostypes.GenesisState, error) { - customGen, ok := customGenesis.(*authtypes.GenesisState) - if !ok { - return nil, fmt.Errorf("invalid type %T for auth module genesis state", customGenesis) - } - - authGen := &authtypes.GenesisState{} - evmosApp.AppCodec().MustUnmarshalJSON(genesisState[authtypes.ModuleName], authGen) - - if len(customGen.Accounts) > 0 { - authGen.Accounts = append(authGen.Accounts, customGen.Accounts...) - } - - authGen.Params = customGen.Params - - genesisState[authtypes.ModuleName] = evmosApp.AppCodec().MustMarshalJSON(authGen) - return genesisState, nil -} - -// GovCustomGenesisState defines the gov genesis state -type GovCustomGenesisState struct { - denom string -} - -// setDefaultGovGenesisState sets the default gov genesis state -func setDefaultGovGenesisState(evmosApp *app.App, genesisState evmostypes.GenesisState, overwriteParams GovCustomGenesisState) evmostypes.GenesisState { - govGen := govtypesv1.DefaultGenesisState() - updatedParams := govGen.Params - minDepositAmt := sdkmath.NewInt(1e18) - updatedParams.MinDeposit = sdktypes.NewCoins(sdktypes.NewCoin(overwriteParams.denom, minDepositAmt)) - updatedParams.ExpeditedMinDeposit = sdktypes.NewCoins(sdktypes.NewCoin(overwriteParams.denom, minDepositAmt)) - govGen.Params = updatedParams - genesisState[govtypes.ModuleName] = evmosApp.AppCodec().MustMarshalJSON(govGen) - return genesisState -} - -func setDefaultErc20GenesisState(evmosApp *app.App, genesisState evmostypes.GenesisState) evmostypes.GenesisState { - erc20Gen := erc20types.DefaultGenesisState() - updatedErc20Gen := updateErc20GenesisStateForChainID(evmosApp.ChainID(), *erc20Gen) - genesisState[erc20types.ModuleName] = evmosApp.AppCodec().MustMarshalJSON(&updatedErc20Gen) - return genesisState -} - -// defaultAuthGenesisState sets the default genesis state -// for the testing setup -func newDefaultGenesisState(evmosApp *app.App, params defaultGenesisParams) evmostypes.GenesisState { - genesisState := evmosApp.DefaultGenesis() - - genesisState = setDefaultAuthGenesisState(evmosApp, genesisState, params.genAccounts) - genesisState = setDefaultStakingGenesisState(evmosApp, genesisState, params.staking) - genesisState = setDefaultBankGenesisState(evmosApp, genesisState, params.bank) - genesisState = setDefaultGovGenesisState(evmosApp, genesisState, params.gov) - genesisState = setDefaultSlashingGenesisState(evmosApp, genesisState, params.slashing) - genesisState = setDefaultErc20GenesisState(evmosApp, genesisState) - - return genesisState -} - -// customizeGenesis modifies genesis state if there're any custom genesis state -// for specific modules -func customizeGenesis(evmosApp *app.App, customGen CustomGenesisState, genesisState evmostypes.GenesisState) (evmostypes.GenesisState, error) { - var err error - for mod, modGenState := range customGen { - if fn, found := genesisSetupFunctions[mod]; found { - genesisState, err = fn(evmosApp, genesisState, modGenState) - if err != nil { - return genesisState, err - } - } else { - panic(fmt.Sprintf("module %s not found in genesis setup functions", mod)) - } - } - return genesisState, err -} From c8c15613bb73a2e8e5b0f24340644b9aff46ca40 Mon Sep 17 00:00:00 2001 From: GuillemGarciaDev Date: Mon, 2 Dec 2024 10:36:22 +0100 Subject: [PATCH 05/60] feat(tests, testutil): upgrade testsuite loads chain state successfully --- Makefile | 4 + tests/upgrade/README.md | 14 + tests/upgrade/network.go | 5 + tests/upgrade/suite.go | 22 +- tests/upgrade/suite_test.go | 13 +- tests/upgrade/upgrade-state.json | 656 ++++++++++++++++++ testutil/integration/exrp/common/config.go | 25 + .../exrp/{network => upgrade}/abci.go | 10 +- .../exrp/{network => upgrade}/clients.go | 38 +- .../exrp/{network => upgrade}/config.go | 3 +- .../exrp/{network => upgrade}/config_test.go | 2 +- .../exrp/{network => upgrade}/ibc.go | 4 +- .../exrp/{network => upgrade}/network.go | 127 ++-- .../exrp/{network => upgrade}/setup.go | 20 +- .../exrp/{network => upgrade}/unit_network.go | 6 +- 15 files changed, 841 insertions(+), 108 deletions(-) create mode 100644 tests/upgrade/README.md create mode 100644 tests/upgrade/upgrade-state.json create mode 100644 testutil/integration/exrp/common/config.go rename testutil/integration/exrp/{network => upgrade}/abci.go (88%) rename testutil/integration/exrp/{network => upgrade}/clients.go (68%) rename testutil/integration/exrp/{network => upgrade}/config.go (97%) rename testutil/integration/exrp/{network => upgrade}/config_test.go (98%) rename testutil/integration/exrp/{network => upgrade}/ibc.go (85%) rename testutil/integration/exrp/{network => upgrade}/network.go (69%) rename testutil/integration/exrp/{network => upgrade}/setup.go (92%) rename testutil/integration/exrp/{network => upgrade}/unit_network.go (95%) diff --git a/Makefile b/Makefile index 1fa6cd7..8773fe2 100644 --- a/Makefile +++ b/Makefile @@ -125,6 +125,10 @@ mocks: @echo "--> Generating mocks" @./scripts/mockgen.sh +test-upgrade: + @echo "--> Running upgrade testsuite" + @go test -mod=readonly ./tests/upgrade + test-poa: @echo "--> Running POA tests" @go test $(EXCLUDED_POA_PACKAGES) diff --git a/tests/upgrade/README.md b/tests/upgrade/README.md new file mode 100644 index 0000000..0a05141 --- /dev/null +++ b/tests/upgrade/README.md @@ -0,0 +1,14 @@ +# Upgrade testsuite + +## Download exported state + +```bash + +``` + +## Setup + +Set the `UPGRADE_STATE_FILE` environment variable to the path to the exported state file. +```bash +UPGRADE_STATE_FILE="path/to/exported-state.json" +``` \ No newline at end of file diff --git a/tests/upgrade/network.go b/tests/upgrade/network.go index d47dcfc..ba79f2f 100644 --- a/tests/upgrade/network.go +++ b/tests/upgrade/network.go @@ -2,6 +2,7 @@ package testupgrade import ( commonnetwork "github.com/xrplevm/node/v4/testutil/integration/common/network" + exrpcommon "github.com/xrplevm/node/v4/testutil/integration/exrp/common" exrpnetwork "github.com/xrplevm/node/v4/testutil/integration/exrp/network" ) @@ -17,3 +18,7 @@ func NewUpgradeTestNetwork(opts ...exrpnetwork.ConfigOption) *UpgradeTestNetwork IntegrationNetwork: *network, } } + +func (n *UpgradeTestNetwork) SetupSdkConfig() { + exrpcommon.SetupSdkConfig() +} diff --git a/tests/upgrade/suite.go b/tests/upgrade/suite.go index 70ae3b6..2089d11 100644 --- a/tests/upgrade/suite.go +++ b/tests/upgrade/suite.go @@ -1,10 +1,15 @@ package testupgrade import ( + "os" + + sdk "github.com/cosmos/cosmos-sdk/types" "github.com/stretchr/testify/suite" exrpnetwork "github.com/xrplevm/node/v4/testutil/integration/exrp/network" ) +const defaultStateFile = "upgrade-state.json" + type UpgradeTestSuite struct { suite.Suite @@ -12,12 +17,23 @@ type UpgradeTestSuite struct { } func (s *UpgradeTestSuite) SetupTest() { - // READ APP STATE FILE + // Get the state file from the environment variable, or use the default one + stateFile := os.Getenv("UPGRADE_STATE_FILE") + if stateFile == "" { + stateFile = defaultStateFile + } + s.Require().NotEmpty(stateFile) + + // Setup the SDK config + s.network.SetupSdkConfig() + + s.Require().Equal(sdk.GetConfig().GetBech32AccountAddrPrefix(), "ethm") + // Create the network s.network = NewUpgradeTestNetwork( - // LOAD APP STATE FROM FILE - exrpnetwork.WithGenesisFile("exported-state.json"), + exrpnetwork.WithGenesisFile(stateFile), ) + // Check that the network was created successfully s.Require().NotNil(s.network) } \ No newline at end of file diff --git a/tests/upgrade/suite_test.go b/tests/upgrade/suite_test.go index 14a5b13..5294058 100644 --- a/tests/upgrade/suite_test.go +++ b/tests/upgrade/suite_test.go @@ -1,8 +1,10 @@ package testupgrade import ( + "fmt" "testing" + banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" "github.com/stretchr/testify/suite" "github.com/xrplevm/node/v4/app" ) @@ -12,8 +14,15 @@ func TestUpgradeTestSuite(t *testing.T) { } func (s *UpgradeTestSuite) TestUpgrade() { - denom := s.network.GetDenom() s.Require().NotEmpty(denom) s.Require().Equal(denom, app.BaseDenom) -} \ No newline at end of file + + balances, err := s.network.GetBankClient().AllBalances(s.network.GetContext(), &banktypes.QueryAllBalancesRequest{ + Address: "ethm1fl48vsnmsdzcv85q5d2q4z5ajdha8yu3w48d64", + }) + + fmt.Println("balances", balances) + s.Require().NoError(err) + fmt.Println(balances) +} diff --git a/tests/upgrade/upgrade-state.json b/tests/upgrade/upgrade-state.json new file mode 100644 index 0000000..af21608 --- /dev/null +++ b/tests/upgrade/upgrade-state.json @@ -0,0 +1,656 @@ +{ + "app_name": "exrpd", + "app_version": "v4.0.0-7-g40ab899", + "genesis_time": "2024-11-27T17:14:04.871328Z", + "chain_id": "exrp_1440002-1", + "initial_height": 8, + "app_hash": null, + "app_state": { + "auth": { + "params": { + "max_memo_characters": "256", + "tx_sig_limit": "7", + "tx_size_cost_per_byte": "10", + "sig_verify_cost_ed25519": "590", + "sig_verify_cost_secp256k1": "1000" + }, + "accounts": [ + { + "@type": "/cosmos.auth.v1beta1.ModuleAccount", + "base_account": { + "address": "ethm1glht96kr2rseywuvhhay894qw7ekuc4q32ac2y", + "pub_key": null, + "account_number": "7", + "sequence": "0" + }, + "name": "erc20", + "permissions": [ + "minter", + "burner" + ] + }, + { + "@type": "/cosmos.auth.v1beta1.ModuleAccount", + "base_account": { + "address": "ethm1fl48vsnmsdzcv85q5d2q4z5ajdha8yu3w48d64", + "pub_key": null, + "account_number": "3", + "sequence": "0" + }, + "name": "bonded_tokens_pool", + "permissions": [ + "burner", + "staking" + ] + }, + { + "@type": "/cosmos.auth.v1beta1.ModuleAccount", + "base_account": { + "address": "ethm1tygms3xhhs3yv487phx3dw4a95jn7t7l64muvp", + "pub_key": null, + "account_number": "4", + "sequence": "0" + }, + "name": "not_bonded_tokens_pool", + "permissions": [ + "burner", + "staking" + ] + }, + { + "@type": "/cosmos.auth.v1beta1.BaseAccount", + "address": "ethm1dakgyqjulg29m5fmv992g2y66m9g2mjn6hahwg", + "pub_key": { + "@type": "/ethermint.crypto.v1.ethsecp256k1.PubKey", + "key": "AyzS/NPje8oX+4+4uWP9f3060duAFXh3MpNWELtTxPz1" + }, + "account_number": "0", + "sequence": "1" + }, + { + "@type": "/cosmos.auth.v1beta1.ModuleAccount", + "base_account": { + "address": "ethm1wztruvatpslu6ngetc65272cshcnzsxgphqyau", + "pub_key": null, + "account_number": "8", + "sequence": "0" + }, + "name": "poa", + "permissions": [ + "minter", + "burner" + ] + }, + { + "@type": "/cosmos.auth.v1beta1.ModuleAccount", + "base_account": { + "address": "ethm10d07y265gmmuvt4z0w9aw880jnsr700jpva843", + "pub_key": null, + "account_number": "5", + "sequence": "0" + }, + "name": "gov", + "permissions": [ + "burner" + ] + }, + { + "@type": "/cosmos.auth.v1beta1.ModuleAccount", + "base_account": { + "address": "ethm1jv65s3grqf6v6jl3dp4t6c9t9rk99cd8u3272a", + "pub_key": null, + "account_number": "2", + "sequence": "0" + }, + "name": "distribution", + "permissions": [] + }, + { + "@type": "/ethermint.types.v1.EthAccount", + "base_account": { + "address": "ethm16j2fvexdsfnq4t5ehmwqxjsda29qh4gh75fmg2", + "pub_key": null, + "account_number": "6", + "sequence": "0" + }, + "code_hash": "0x7b477c761b4d0469f03f27ba58d0a7eacbfdd62b69b82c6c683ae5f81c67fe80" + }, + { + "@type": "/cosmos.auth.v1beta1.ModuleAccount", + "base_account": { + "address": "ethm17xpfvakm2amg962yls6f84z3kell8c5lthdzgl", + "pub_key": null, + "account_number": "1", + "sequence": "0" + }, + "name": "fee_collector", + "permissions": [] + } + ] + }, + "authz": { + "authorization": [] + }, + "bank": { + "params": { + "send_enabled": [], + "default_send_enabled": true + }, + "balances": [ + { + "address": "ethm1fl48vsnmsdzcv85q5d2q4z5ajdha8yu3w48d64", + "coins": [ + { + "denom": "apoa", + "amount": "1000000" + } + ] + }, + { + "address": "ethm1dakgyqjulg29m5fmv992g2y66m9g2mjn6hahwg", + "coins": [ + { + "denom": "token", + "amount": "1000000000000000000000000000" + } + ] + } + ], + "supply": [ + { + "denom": "apoa", + "amount": "1000000" + }, + { + "denom": "token", + "amount": "1000000000000000000000000000" + } + ], + "denom_metadata": [], + "send_enabled": [] + }, + "capability": { + "index": "3", + "owners": [ + { + "index": "1", + "index_owners": { + "owners": [ + { + "module": "ibc", + "name": "ports/transfer" + }, + { + "module": "transfer", + "name": "ports/transfer" + } + ] + } + }, + { + "index": "2", + "index_owners": { + "owners": [ + { + "module": "ibc", + "name": "ports/icahost" + }, + { + "module": "icahost", + "name": "ports/icahost" + } + ] + } + } + ] + }, + "crisis": { + "constant_fee": { + "denom": "token", + "amount": "1000" + } + }, + "distribution": { + "params": { + "community_tax": "0.020000000000000000", + "base_proposer_reward": "0.000000000000000000", + "bonus_proposer_reward": "0.000000000000000000", + "withdraw_addr_enabled": true + }, + "fee_pool": { + "community_pool": [] + }, + "delegator_withdraw_infos": [], + "previous_proposer": "ethmvalcons123s7tvluhrla7tpj5mwysxqlcgwwzwavxenru7", + "outstanding_rewards": [ + { + "validator_address": "ethmvaloper1dakgyqjulg29m5fmv992g2y66m9g2mjn48hmk4", + "outstanding_rewards": [] + } + ], + "validator_accumulated_commissions": [ + { + "validator_address": "ethmvaloper1dakgyqjulg29m5fmv992g2y66m9g2mjn48hmk4", + "accumulated": { + "commission": [] + } + } + ], + "validator_historical_rewards": [ + { + "validator_address": "ethmvaloper1dakgyqjulg29m5fmv992g2y66m9g2mjn48hmk4", + "period": "1", + "rewards": { + "cumulative_reward_ratio": [], + "reference_count": 2 + } + } + ], + "validator_current_rewards": [ + { + "validator_address": "ethmvaloper1dakgyqjulg29m5fmv992g2y66m9g2mjn48hmk4", + "rewards": { + "rewards": [], + "period": "2" + } + } + ], + "delegator_starting_infos": [ + { + "delegator_address": "ethm1dakgyqjulg29m5fmv992g2y66m9g2mjn6hahwg", + "validator_address": "ethmvaloper1dakgyqjulg29m5fmv992g2y66m9g2mjn48hmk4", + "starting_info": { + "previous_period": "1", + "stake": "1000000.000000000000000000", + "height": "0" + } + } + ], + "validator_slash_events": [] + }, + "erc20": { + "params": { + "enable_erc20": true, + "native_precompiles": [ + "0xD4949664cD82660AaE99bEdc034a0deA8A0bd517" + ], + "dynamic_precompiles": [] + }, + "token_pairs": [ + { + "erc20_address": "0xD4949664cD82660AaE99bEdc034a0deA8A0bd517", + "denom": "token", + "enabled": true, + "contract_owner": "OWNER_MODULE", + "owner_address": "ethm1zrxl239wa6ad5xge3gs68rt98227xgnjq0xyw2" + } + ] + }, + "evidence": { + "evidence": [] + }, + "evm": { + "accounts": [ + { + "address": "0xD4949664cD82660AaE99bEdc034a0deA8A0bd517", + "code": "608060405234801561001057600080fd5b50600436106101da5760003560e01c80635c975abb11610104578063a217fddf116100a2578063d539139311610071578063d53913931461057d578063d547741f1461059b578063dd62ed3e146105b7578063e63ab1e9146105e7576101da565b8063a217fddf146104cf578063a457c2d7146104ed578063a9059cbb1461051d578063ca15c8731461054d576101da565b80638456cb59116100de5780638456cb59146104475780639010d07c1461045157806391d148541461048157806395d89b41146104b1576101da565b80635c975abb146103dd57806370a08231146103fb57806379cc67901461042b576101da565b8063282c51f31161017c578063395093511161014b578063395093511461036b5780633f4ba83a1461039b57806340c10f19146103a557806342966c68146103c1576101da565b8063282c51f3146102f75780632f2ff15d14610315578063313ce5671461033157806336568abe1461034f576101da565b806318160ddd116101b857806318160ddd1461025d5780631cf2c7e21461027b57806323b872dd14610297578063248a9ca3146102c7576101da565b806301ffc9a7146101df57806306fdde031461020f578063095ea7b31461022d575b600080fd5b6101f960048036038101906101f49190612216565b610605565b604051610206919061225e565b60405180910390f35b61021761067f565b6040516102249190612312565b60405180910390f35b610247600480360381019061024291906123c8565b610711565b604051610254919061225e565b60405180910390f35b61026561072f565b6040516102729190612417565b60405180910390f35b610295600480360381019061029091906123c8565b610739565b005b6102b160048036038101906102ac9190612432565b6107b7565b6040516102be919061225e565b60405180910390f35b6102e160048036038101906102dc91906124bb565b6108af565b6040516102ee91906124f7565b60405180910390f35b6102ff6108ce565b60405161030c91906124f7565b60405180910390f35b61032f600480360381019061032a9190612512565b6108f2565b005b61033961091b565b604051610346919061256e565b60405180910390f35b61036960048036038101906103649190612512565b610932565b005b610385600480360381019061038091906123c8565b6109b5565b604051610392919061225e565b60405180910390f35b6103a3610a61565b005b6103bf60048036038101906103ba91906123c8565b610adb565b005b6103db60048036038101906103d69190612589565b610b59565b005b6103e5610b6d565b6040516103f2919061225e565b60405180910390f35b610415600480360381019061041091906125b6565b610b84565b6040516104229190612417565b60405180910390f35b610445600480360381019061044091906123c8565b610bcd565b005b61044f610c48565b005b61046b600480360381019061046691906125e3565b610cc2565b6040516104789190612632565b60405180910390f35b61049b60048036038101906104969190612512565b610cf1565b6040516104a8919061225e565b60405180910390f35b6104b9610d5b565b6040516104c69190612312565b60405180910390f35b6104d7610ded565b6040516104e491906124f7565b60405180910390f35b610507600480360381019061050291906123c8565b610df4565b604051610514919061225e565b60405180910390f35b610537600480360381019061053291906123c8565b610edf565b604051610544919061225e565b60405180910390f35b610567600480360381019061056291906124bb565b610efd565b6040516105749190612417565b60405180910390f35b610585610f21565b60405161059291906124f7565b60405180910390f35b6105b560048036038101906105b09190612512565b610f45565b005b6105d160048036038101906105cc919061264d565b610f6e565b6040516105de9190612417565b60405180910390f35b6105ef610ff5565b6040516105fc91906124f7565b60405180910390f35b60007f5a05180f000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19161480610678575061067782611129565b5b9050919050565b60606005805461068e906126bc565b80601f01602080910402602001604051908101604052809291908181526020018280546106ba906126bc565b80156107075780601f106106dc57610100808354040283529160200191610707565b820191906000526020600020905b8154815290600101906020018083116106ea57829003601f168201915b5050505050905090565b600061072561071e6111a3565b84846111ab565b6001905092915050565b6000600454905090565b61076a7f3c11d16cbaffd01df69ce1c404f6340ee057498f5f00246190ea54220576a8486107656111a3565b610cf1565b6107a9576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016107a090612760565b60405180910390fd5b6107b38282611376565b5050565b60006107c484848461154f565b6000600360008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600061080f6111a3565b73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205490508281101561088f576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610886906127f2565b60405180910390fd5b6108a38561089b6111a3565b8584036111ab565b60019150509392505050565b6000806000838152602001908152602001600020600101549050919050565b7f3c11d16cbaffd01df69ce1c404f6340ee057498f5f00246190ea54220576a84881565b6108fb826108af565b61090c816109076111a3565b6117d3565b6109168383611870565b505050565b6000600760019054906101000a900460ff16905090565b61093a6111a3565b73ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16146109a7576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161099e90612884565b60405180910390fd5b6109b182826118a4565b5050565b6000610a576109c26111a3565b8484600360006109d06111a3565b73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008873ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054610a5291906128d3565b6111ab565b6001905092915050565b610a927f65d7a28e3265b37a6474929f336521b332c1681b933f6cb9f3376673440d862a610a8d6111a3565b610cf1565b610ad1576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610ac89061299b565b60405180910390fd5b610ad96118d8565b565b610b0c7f9f2df0fed2c77648de5860a4cc508cd0818c85b8b8a1ab4ceeef8d981c8956a6610b076111a3565b610cf1565b610b4b576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610b4290612a2d565b60405180910390fd5b610b55828261197a565b5050565b610b6a610b646111a3565b82611376565b50565b6000600760009054906101000a900460ff16905090565b6000600260008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020549050919050565b6000610be083610bdb6111a3565b610f6e565b905081811015610c25576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610c1c90612abf565b60405180910390fd5b610c3983610c316111a3565b8484036111ab565b610c438383611376565b505050565b610c797f65d7a28e3265b37a6474929f336521b332c1681b933f6cb9f3376673440d862a610c746111a3565b610cf1565b610cb8576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610caf90612b51565b60405180910390fd5b610cc0611adb565b565b6000610ce98260016000868152602001908152602001600020611b7e90919063ffffffff16565b905092915050565b600080600084815260200190815260200160002060000160008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff16905092915050565b606060068054610d6a906126bc565b80601f0160208091040260200160405190810160405280929190818152602001828054610d96906126bc565b8015610de35780601f10610db857610100808354040283529160200191610de3565b820191906000526020600020905b815481529060010190602001808311610dc657829003601f168201915b5050505050905090565b6000801b81565b60008060036000610e036111a3565b73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054905082811015610ec0576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610eb790612be3565b60405180910390fd5b610ed4610ecb6111a3565b858584036111ab565b600191505092915050565b6000610ef3610eec6111a3565b848461154f565b6001905092915050565b6000610f1a60016000848152602001908152602001600020611b98565b9050919050565b7f9f2df0fed2c77648de5860a4cc508cd0818c85b8b8a1ab4ceeef8d981c8956a681565b610f4e826108af565b610f5f81610f5a6111a3565b6117d3565b610f6983836118a4565b505050565b6000600360008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054905092915050565b7f65d7a28e3265b37a6474929f336521b332c1681b933f6cb9f3376673440d862a81565b6110238282610cf1565b6110f557600160008084815260200190815260200160002060000160008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff02191690831515021790555061109a6111a3565b73ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16837f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d60405160405180910390a45b5050565b6000611121836000018373ffffffffffffffffffffffffffffffffffffffff1660001b611bad565b905092915050565b60007f7965db0b000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916148061119c575061119b82611c1d565b5b9050919050565b600033905090565b600073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff16141561121b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161121290612c75565b60405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff16141561128b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161128290612d07565b60405180910390fd5b80600360008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508173ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925836040516113699190612417565b60405180910390a3505050565b600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff1614156113e6576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016113dd90612d99565b60405180910390fd5b6113f282600083611c87565b6000600260008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054905081811015611479576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161147090612e2b565b60405180910390fd5b818103600260008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000208190555081600460008282546114d19190612e4b565b92505081905550600073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef846040516115369190612417565b60405180910390a361154a83600084611c97565b505050565b600073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff1614156115bf576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016115b690612ef1565b60405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff16141561162f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161162690612f83565b60405180910390fd5b61163a838383611c87565b6000600260008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020549050818110156116c1576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016116b890613015565b60405180910390fd5b818103600260008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000208190555081600260008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825461175691906128d3565b925050819055508273ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef846040516117ba9190612417565b60405180910390a36117cd848484611c97565b50505050565b6117dd8282610cf1565b61186c576118028173ffffffffffffffffffffffffffffffffffffffff166014611c9c565b6118108360001c6020611c9c565b604051602001611821929190613109565b6040516020818303038152906040526040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016118639190612312565b60405180910390fd5b5050565b61187a8282611019565b61189f81600160008581526020019081526020016000206110f990919063ffffffff16565b505050565b6118ae8282611ed8565b6118d38160016000858152602001908152602001600020611fb990919063ffffffff16565b505050565b6118e0610b6d565b61191f576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016119169061318f565b60405180910390fd5b6000600760006101000a81548160ff0219169083151502179055507f5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa6119636111a3565b6040516119709190612632565b60405180910390a1565b600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff1614156119ea576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016119e1906131fb565b60405180910390fd5b6119f660008383611c87565b8060046000828254611a0891906128d3565b9250508190555080600260008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000828254611a5e91906128d3565b925050819055508173ffffffffffffffffffffffffffffffffffffffff16600073ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef83604051611ac39190612417565b60405180910390a3611ad760008383611c97565b5050565b611ae3610b6d565b15611b23576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611b1a90613267565b60405180910390fd5b6001600760006101000a81548160ff0219169083151502179055507f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a258611b676111a3565b604051611b749190612632565b60405180910390a1565b6000611b8d8360000183611fe9565b60001c905092915050565b6000611ba682600001612014565b9050919050565b6000611bb98383612025565b611c12578260000182908060018154018082558091505060019003906000526020600020016000909190919091505582600001805490508360010160008481526020019081526020016000208190555060019050611c17565b600090505b92915050565b60007f01ffc9a7000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916149050919050565b611c92838383612048565b505050565b505050565b606060006002836002611caf9190613287565b611cb991906128d3565b67ffffffffffffffff811115611cd257611cd16132e1565b5b6040519080825280601f01601f191660200182016040528015611d045781602001600182028036833780820191505090505b5090507f300000000000000000000000000000000000000000000000000000000000000081600081518110611d3c57611d3b613310565b5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a9053507f780000000000000000000000000000000000000000000000000000000000000081600181518110611da057611d9f613310565b5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a90535060006001846002611de09190613287565b611dea91906128d3565b90505b6001811115611e8a577f3031323334353637383961626364656600000000000000000000000000000000600f861660108110611e2c57611e2b613310565b5b1a60f81b828281518110611e4357611e42613310565b5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a905350600485901c945080611e839061333f565b9050611ded565b5060008414611ece576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611ec5906133b5565b60405180910390fd5b8091505092915050565b611ee28282610cf1565b15611fb557600080600084815260200190815260200160002060000160008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff021916908315150217905550611f5a6111a3565b73ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16837ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b60405160405180910390a45b5050565b6000611fe1836000018373ffffffffffffffffffffffffffffffffffffffff1660001b6120a0565b905092915050565b600082600001828154811061200157612000613310565b5b9060005260206000200154905092915050565b600081600001805490509050919050565b600080836001016000848152602001908152602001600020541415905092915050565b6120538383836121b4565b61205b610b6d565b1561209b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161209290613447565b60405180910390fd5b505050565b600080836001016000848152602001908152602001600020549050600081146121a85760006001826120d29190612e4b565b90506000600186600001805490506120ea9190612e4b565b905081811461215957600086600001828154811061210b5761210a613310565b5b906000526020600020015490508087600001848154811061212f5761212e613310565b5b90600052602060002001819055508387600101600083815260200190815260200160002081905550505b8560000180548061216d5761216c613467565b5b6001900381819060005260206000200160009055905585600101600086815260200190815260200160002060009055600193505050506121ae565b60009150505b92915050565b505050565b600080fd5b60007fffffffff0000000000000000000000000000000000000000000000000000000082169050919050565b6121f3816121be565b81146121fe57600080fd5b50565b600081359050612210816121ea565b92915050565b60006020828403121561222c5761222b6121b9565b5b600061223a84828501612201565b91505092915050565b60008115159050919050565b61225881612243565b82525050565b6000602082019050612273600083018461224f565b92915050565b600081519050919050565b600082825260208201905092915050565b60005b838110156122b3578082015181840152602081019050612298565b838111156122c2576000848401525b50505050565b6000601f19601f8301169050919050565b60006122e482612279565b6122ee8185612284565b93506122fe818560208601612295565b612307816122c8565b840191505092915050565b6000602082019050818103600083015261232c81846122d9565b905092915050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b600061235f82612334565b9050919050565b61236f81612354565b811461237a57600080fd5b50565b60008135905061238c81612366565b92915050565b6000819050919050565b6123a581612392565b81146123b057600080fd5b50565b6000813590506123c28161239c565b92915050565b600080604083850312156123df576123de6121b9565b5b60006123ed8582860161237d565b92505060206123fe858286016123b3565b9150509250929050565b61241181612392565b82525050565b600060208201905061242c6000830184612408565b92915050565b60008060006060848603121561244b5761244a6121b9565b5b60006124598682870161237d565b935050602061246a8682870161237d565b925050604061247b868287016123b3565b9150509250925092565b6000819050919050565b61249881612485565b81146124a357600080fd5b50565b6000813590506124b58161248f565b92915050565b6000602082840312156124d1576124d06121b9565b5b60006124df848285016124a6565b91505092915050565b6124f181612485565b82525050565b600060208201905061250c60008301846124e8565b92915050565b60008060408385031215612529576125286121b9565b5b6000612537858286016124a6565b92505060206125488582860161237d565b9150509250929050565b600060ff82169050919050565b61256881612552565b82525050565b6000602082019050612583600083018461255f565b92915050565b60006020828403121561259f5761259e6121b9565b5b60006125ad848285016123b3565b91505092915050565b6000602082840312156125cc576125cb6121b9565b5b60006125da8482850161237d565b91505092915050565b600080604083850312156125fa576125f96121b9565b5b6000612608858286016124a6565b9250506020612619858286016123b3565b9150509250929050565b61262c81612354565b82525050565b60006020820190506126476000830184612623565b92915050565b60008060408385031215612664576126636121b9565b5b60006126728582860161237d565b92505060206126838582860161237d565b9150509250929050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b600060028204905060018216806126d457607f821691505b602082108114156126e8576126e761268d565b5b50919050565b7f45524332304d696e7465724275726e6572446563696d616c733a206d7573742060008201527f68617665206275726e657220726f6c6520746f206275726e0000000000000000602082015250565b600061274a603883612284565b9150612755826126ee565b604082019050919050565b600060208201905081810360008301526127798161273d565b9050919050565b7f45524332303a207472616e7366657220616d6f756e742065786365656473206160008201527f6c6c6f77616e6365000000000000000000000000000000000000000000000000602082015250565b60006127dc602883612284565b91506127e782612780565b604082019050919050565b6000602082019050818103600083015261280b816127cf565b9050919050565b7f416363657373436f6e74726f6c3a2063616e206f6e6c792072656e6f756e636560008201527f20726f6c657320666f722073656c660000000000000000000000000000000000602082015250565b600061286e602f83612284565b915061287982612812565b604082019050919050565b6000602082019050818103600083015261289d81612861565b9050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b60006128de82612392565b91506128e983612392565b9250827fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0382111561291e5761291d6128a4565b5b828201905092915050565b7f45524332304d696e7465724275726e6572446563696d616c733a206d7573742060008201527f686176652070617573657220726f6c6520746f20756e70617573650000000000602082015250565b6000612985603b83612284565b915061299082612929565b604082019050919050565b600060208201905081810360008301526129b481612978565b9050919050565b7f45524332304d696e7465724275726e6572446563696d616c733a206d7573742060008201527f68617665206d696e74657220726f6c6520746f206d696e740000000000000000602082015250565b6000612a17603883612284565b9150612a22826129bb565b604082019050919050565b60006020820190508181036000830152612a4681612a0a565b9050919050565b7f45524332303a206275726e20616d6f756e74206578636565647320616c6c6f7760008201527f616e636500000000000000000000000000000000000000000000000000000000602082015250565b6000612aa9602483612284565b9150612ab482612a4d565b604082019050919050565b60006020820190508181036000830152612ad881612a9c565b9050919050565b7f45524332304d696e7465724275726e6572446563696d616c733a206d7573742060008201527f686176652070617573657220726f6c6520746f20706175736500000000000000602082015250565b6000612b3b603983612284565b9150612b4682612adf565b604082019050919050565b60006020820190508181036000830152612b6a81612b2e565b9050919050565b7f45524332303a2064656372656173656420616c6c6f77616e63652062656c6f7760008201527f207a65726f000000000000000000000000000000000000000000000000000000602082015250565b6000612bcd602583612284565b9150612bd882612b71565b604082019050919050565b60006020820190508181036000830152612bfc81612bc0565b9050919050565b7f45524332303a20617070726f76652066726f6d20746865207a65726f2061646460008201527f7265737300000000000000000000000000000000000000000000000000000000602082015250565b6000612c5f602483612284565b9150612c6a82612c03565b604082019050919050565b60006020820190508181036000830152612c8e81612c52565b9050919050565b7f45524332303a20617070726f766520746f20746865207a65726f20616464726560008201527f7373000000000000000000000000000000000000000000000000000000000000602082015250565b6000612cf1602283612284565b9150612cfc82612c95565b604082019050919050565b60006020820190508181036000830152612d2081612ce4565b9050919050565b7f45524332303a206275726e2066726f6d20746865207a65726f2061646472657360008201527f7300000000000000000000000000000000000000000000000000000000000000602082015250565b6000612d83602183612284565b9150612d8e82612d27565b604082019050919050565b60006020820190508181036000830152612db281612d76565b9050919050565b7f45524332303a206275726e20616d6f756e7420657863656564732062616c616e60008201527f6365000000000000000000000000000000000000000000000000000000000000602082015250565b6000612e15602283612284565b9150612e2082612db9565b604082019050919050565b60006020820190508181036000830152612e4481612e08565b9050919050565b6000612e5682612392565b9150612e6183612392565b925082821015612e7457612e736128a4565b5b828203905092915050565b7f45524332303a207472616e736665722066726f6d20746865207a65726f20616460008201527f6472657373000000000000000000000000000000000000000000000000000000602082015250565b6000612edb602583612284565b9150612ee682612e7f565b604082019050919050565b60006020820190508181036000830152612f0a81612ece565b9050919050565b7f45524332303a207472616e7366657220746f20746865207a65726f206164647260008201527f6573730000000000000000000000000000000000000000000000000000000000602082015250565b6000612f6d602383612284565b9150612f7882612f11565b604082019050919050565b60006020820190508181036000830152612f9c81612f60565b9050919050565b7f45524332303a207472616e7366657220616d6f756e742065786365656473206260008201527f616c616e63650000000000000000000000000000000000000000000000000000602082015250565b6000612fff602683612284565b915061300a82612fa3565b604082019050919050565b6000602082019050818103600083015261302e81612ff2565b9050919050565b600081905092915050565b7f416363657373436f6e74726f6c3a206163636f756e7420000000000000000000600082015250565b6000613076601783613035565b915061308182613040565b601782019050919050565b600061309782612279565b6130a18185613035565b93506130b1818560208601612295565b80840191505092915050565b7f206973206d697373696e6720726f6c6520000000000000000000000000000000600082015250565b60006130f3601183613035565b91506130fe826130bd565b601182019050919050565b600061311482613069565b9150613120828561308c565b915061312b826130e6565b9150613137828461308c565b91508190509392505050565b7f5061757361626c653a206e6f7420706175736564000000000000000000000000600082015250565b6000613179601483612284565b915061318482613143565b602082019050919050565b600060208201905081810360008301526131a88161316c565b9050919050565b7f45524332303a206d696e7420746f20746865207a65726f206164647265737300600082015250565b60006131e5601f83612284565b91506131f0826131af565b602082019050919050565b60006020820190508181036000830152613214816131d8565b9050919050565b7f5061757361626c653a2070617573656400000000000000000000000000000000600082015250565b6000613251601083612284565b915061325c8261321b565b602082019050919050565b6000602082019050818103600083015261328081613244565b9050919050565b600061329282612392565b915061329d83612392565b9250817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff04831182151516156132d6576132d56128a4565b5b828202905092915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b600061334a82612392565b9150600082141561335e5761335d6128a4565b5b600182039050919050565b7f537472696e67733a20686578206c656e67746820696e73756666696369656e74600082015250565b600061339f602083612284565b91506133aa82613369565b602082019050919050565b600060208201905081810360008301526133ce81613392565b9050919050565b7f45524332305061757361626c653a20746f6b656e207472616e7366657220776860008201527f696c652070617573656400000000000000000000000000000000000000000000602082015250565b6000613431602a83612284565b915061343c826133d5565b604082019050919050565b6000602082019050818103600083015261346081613424565b9050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603160045260246000fdfea2646970667358221220c3d4a4231a6c94cfb03623ea4b77df2c9ccfa487132bebf43620219e3dc2f4cf64736f6c63430008090033", + "storage": [] + } + ], + "params": { + "evm_denom": "token", + "extra_eips": [ + "ethereum_3855" + ], + "chain_config": { + "homestead_block": "0", + "dao_fork_block": "0", + "dao_fork_support": true, + "eip150_block": "0", + "eip150_hash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "eip155_block": "0", + "eip158_block": "0", + "byzantium_block": "0", + "constantinople_block": "0", + "petersburg_block": "0", + "istanbul_block": "0", + "muir_glacier_block": "0", + "berlin_block": "0", + "london_block": "0", + "arrow_glacier_block": "0", + "gray_glacier_block": "0", + "merge_netsplit_block": "0", + "shanghai_block": "0", + "cancun_block": "0" + }, + "allow_unprotected_txs": true, + "evm_channels": [ + "channel-10", + "channel-31", + "channel-83" + ], + "access_control": { + "create": { + "access_type": "ACCESS_TYPE_PERMISSIONLESS", + "access_control_list": [] + }, + "call": { + "access_type": "ACCESS_TYPE_PERMISSIONLESS", + "access_control_list": [] + } + }, + "active_static_precompiles": [ + "0x0000000000000000000000000000000000000100", + "0x0000000000000000000000000000000000000400", + "0x0000000000000000000000000000000000000800", + "0x0000000000000000000000000000000000000801", + "0x0000000000000000000000000000000000000802", + "0x0000000000000000000000000000000000000803", + "0x0000000000000000000000000000000000000804", + "0x0000000000000000000000000000000000000805" + ] + } + }, + "feegrant": { + "allowances": [] + }, + "feemarket": { + "params": { + "no_base_fee": false, + "base_fee_change_denominator": 8, + "elasticity_multiplier": 2, + "enable_height": "0", + "base_fee": "0", + "min_gas_price": "0.000000000000000000", + "min_gas_multiplier": "0.500000000000000000" + }, + "block_gas": "0" + }, + "genutil": { + "gen_txs": [] + }, + "gov": { + "starting_proposal_id": "1", + "deposits": [], + "votes": [], + "proposals": [], + "deposit_params": null, + "voting_params": null, + "tally_params": null, + "params": { + "min_deposit": [ + { + "denom": "token", + "amount": "1" + } + ], + "max_deposit_period": "172800s", + "voting_period": "10s", + "quorum": "0.334000000000000000", + "threshold": "0.500000000000000000", + "veto_threshold": "0.334000000000000000", + "min_initial_deposit_ratio": "0.000000000000000000", + "proposal_cancel_ratio": "0.500000000000000000", + "proposal_cancel_dest": "", + "expedited_voting_period": "5s", + "expedited_threshold": "0.667000000000000000", + "expedited_min_deposit": [ + { + "denom": "stake", + "amount": "50000000" + } + ], + "burn_vote_quorum": false, + "burn_proposal_deposit_prevote": false, + "burn_vote_veto": true, + "min_deposit_ratio": "0.010000000000000000" + }, + "constitution": "" + }, + "ibc": { + "client_genesis": { + "clients": [ + { + "client_id": "09-localhost", + "client_state": { + "@type": "/ibc.lightclients.localhost.v2.ClientState", + "latest_height": { + "revision_number": "1", + "revision_height": "7" + } + } + } + ], + "clients_consensus": [], + "clients_metadata": [], + "params": { + "allowed_clients": [ + "*" + ] + }, + "create_localhost": false, + "next_client_sequence": "0" + }, + "connection_genesis": { + "connections": [ + { + "id": "connection-localhost", + "client_id": "09-localhost", + "versions": [ + { + "identifier": "1", + "features": [ + "ORDER_ORDERED", + "ORDER_UNORDERED" + ] + } + ], + "state": "STATE_OPEN", + "counterparty": { + "client_id": "09-localhost", + "connection_id": "connection-localhost", + "prefix": { + "key_prefix": "aWJj" + } + }, + "delay_period": "0" + } + ], + "client_connection_paths": [], + "next_connection_sequence": "0", + "params": { + "max_expected_time_per_block": "30000000000" + } + }, + "channel_genesis": { + "channels": [], + "acknowledgements": [], + "commitments": [], + "receipts": [], + "send_sequences": [], + "recv_sequences": [], + "ack_sequences": [], + "next_channel_sequence": "0", + "params": { + "upgrade_timeout": { + "height": { + "revision_number": "0", + "revision_height": "0" + }, + "timestamp": "600000000000" + } + } + } + }, + "interchainaccounts": { + "controller_genesis_state": { + "active_channels": [], + "interchain_accounts": [], + "ports": [], + "params": { + "controller_enabled": true + } + }, + "host_genesis_state": { + "active_channels": [], + "interchain_accounts": [], + "port": "icahost", + "params": { + "host_enabled": true, + "allow_messages": [ + "*" + ] + } + } + }, + "poa": { + "params": {} + }, + "ratelimit": { + "params": {}, + "rate_limits": [], + "whitelisted_address_pairs": [], + "blacklisted_denoms": [], + "pending_send_packet_sequence_numbers": [], + "hour_epoch": { + "epoch_number": "17", + "duration": "3600s", + "epoch_start_time": "2024-11-27T17:00:00Z", + "epoch_start_height": "0" + } + }, + "slashing": { + "params": { + "signed_blocks_window": "100", + "min_signed_per_window": "0.500000000000000000", + "downtime_jail_duration": "600s", + "slash_fraction_double_sign": "0.000000000000000000", + "slash_fraction_downtime": "0.000000000000000000" + }, + "signing_infos": [ + { + "address": "ethmvalcons123s7tvluhrla7tpj5mwysxqlcgwwzwavxenru7", + "validator_signing_info": { + "address": "ethmvalcons123s7tvluhrla7tpj5mwysxqlcgwwzwavxenru7", + "start_height": "0", + "index_offset": "6", + "jailed_until": "1970-01-01T00:00:00Z", + "tombstoned": false, + "missed_blocks_counter": "0" + } + } + ], + "missed_blocks": [ + { + "address": "ethmvalcons123s7tvluhrla7tpj5mwysxqlcgwwzwavxenru7", + "missed_blocks": [] + } + ] + }, + "staking": { + "params": { + "unbonding_time": "60s", + "max_validators": 100, + "max_entries": 7, + "historical_entries": 10000, + "bond_denom": "apoa", + "min_commission_rate": "0.000000000000000000" + }, + "last_total_power": "1", + "last_validator_powers": [ + { + "address": "ethmvaloper1dakgyqjulg29m5fmv992g2y66m9g2mjn48hmk4", + "power": "1" + } + ], + "validators": [ + { + "operator_address": "ethmvaloper1dakgyqjulg29m5fmv992g2y66m9g2mjn48hmk4", + "consensus_pubkey": { + "@type": "/cosmos.crypto.ed25519.PubKey", + "key": "x39TSSHGypcFLoD91nkVBQTiRT/KcVnnvyJrnXn0Bps=" + }, + "jailed": false, + "status": "BOND_STATUS_BONDED", + "tokens": "1000000", + "delegator_shares": "1000000.000000000000000000", + "description": { + "moniker": "localnet", + "identity": "", + "website": "", + "security_contact": "", + "details": "" + }, + "unbonding_height": "0", + "unbonding_time": "1970-01-01T00:00:00Z", + "commission": { + "commission_rates": { + "rate": "0.100000000000000000", + "max_rate": "0.200000000000000000", + "max_change_rate": "0.010000000000000000" + }, + "update_time": "2024-11-27T17:14:04.871328Z" + }, + "min_self_delegation": "1", + "unbonding_on_hold_ref_count": "0", + "unbonding_ids": [] + } + ], + "delegations": [ + { + "delegator_address": "ethm1dakgyqjulg29m5fmv992g2y66m9g2mjn6hahwg", + "validator_address": "ethmvaloper1dakgyqjulg29m5fmv992g2y66m9g2mjn48hmk4", + "shares": "1000000.000000000000000000" + } + ], + "unbonding_delegations": [], + "redelegations": [], + "exported": true + }, + "transfer": { + "port_id": "transfer", + "denom_traces": [], + "params": { + "send_enabled": true, + "receive_enabled": true + }, + "total_escrowed": [] + }, + "upgrade": {} + }, + "consensus": { + "validators": [ + { + "address": "5461E5B3FCB8FFDF2C32A6DC48181FC21CE13BAC", + "pub_key": { + "type": "tendermint/PubKeyEd25519", + "value": "x39TSSHGypcFLoD91nkVBQTiRT/KcVnnvyJrnXn0Bps=" + }, + "power": "1", + "name": "localnet" + } + ], + "params": { + "block": { + "max_bytes": 22020096, + "max_gas": 10500000 + }, + "evidence": { + "max_age_num_blocks": 100000, + "max_age_duration": 172800000000000, + "max_bytes": 1048576 + }, + "validator": { + "pub_key_types": [ + "ed25519" + ] + }, + "version": { + "app": 0 + }, + "abci": { + "vote_extensions_enable_height": 0 + } + } + } +} \ No newline at end of file diff --git a/testutil/integration/exrp/common/config.go b/testutil/integration/exrp/common/config.go new file mode 100644 index 0000000..20af9bd --- /dev/null +++ b/testutil/integration/exrp/common/config.go @@ -0,0 +1,25 @@ +package exrpcommon + +import sdk "github.com/cosmos/cosmos-sdk/types" + +const ( + accountAddressPrefix = "ethm" + bip44CoinType = 60 +) + +func SetupSdkConfig() { + accountPubKeyPrefix := accountAddressPrefix + "pub" + validatorAddressPrefix := accountAddressPrefix + "valoper" + validatorPubKeyPrefix := accountAddressPrefix + "valoperpub" + consNodeAddressPrefix := accountAddressPrefix + "valcons" + consNodePubKeyPrefix := accountAddressPrefix + "valconspub" + + // Set config + config := sdk.GetConfig() + config.SetBech32PrefixForAccount(accountAddressPrefix, accountPubKeyPrefix) + config.SetBech32PrefixForValidator(validatorAddressPrefix, validatorPubKeyPrefix) + config.SetBech32PrefixForConsensusNode(consNodeAddressPrefix, consNodePubKeyPrefix) + config.SetCoinType(bip44CoinType) + config.SetPurpose(sdk.Purpose) // Shared + config.Seal() +} \ No newline at end of file diff --git a/testutil/integration/exrp/network/abci.go b/testutil/integration/exrp/upgrade/abci.go similarity index 88% rename from testutil/integration/exrp/network/abci.go rename to testutil/integration/exrp/upgrade/abci.go index 0da0fa0..0b8bf7d 100644 --- a/testutil/integration/exrp/network/abci.go +++ b/testutil/integration/exrp/upgrade/abci.go @@ -1,6 +1,6 @@ // Copyright Tharsis Labs Ltd.(Evmos) // SPDX-License-Identifier:ENCL-1.0(https://github.com/evmos/evmos/blob/main/LICENSE) -package exrpnetwork +package exrpupgrade import ( "time" @@ -13,13 +13,13 @@ import ( // NextBlock is a private helper function that runs the EndBlocker logic, commits the changes, // updates the header and runs the BeginBlocker -func (n *IntegrationNetwork) NextBlock() error { +func (n *UpgradeIntegrationNetwork) NextBlock() error { return n.NextBlockAfter(time.Second) } // NextBlockAfter is a private helper function that runs the FinalizeBlock logic, updates the context and // commits the changes to have a block time after the given duration. -func (n *IntegrationNetwork) NextBlockAfter(duration time.Duration) error { +func (n *UpgradeIntegrationNetwork) NextBlockAfter(duration time.Duration) error { _, err := n.finalizeBlockAndCommit(duration) return err } @@ -27,14 +27,14 @@ func (n *IntegrationNetwork) NextBlockAfter(duration time.Duration) error { // NextBlockWithTxs is a helper function that runs the FinalizeBlock logic // with the provided tx bytes, updates the context and // commits the changes to have a block time after the given duration. -func (n *IntegrationNetwork) NextBlockWithTxs(txBytes ...[]byte) (*abcitypes.ResponseFinalizeBlock, error) { +func (n *UpgradeIntegrationNetwork) NextBlockWithTxs(txBytes ...[]byte) (*abcitypes.ResponseFinalizeBlock, error) { return n.finalizeBlockAndCommit(time.Second, txBytes...) } // finalizeBlockAndCommit is a private helper function that runs the FinalizeBlock logic // with the provided txBytes, updates the context and // commits the changes to have a block time after the given duration. -func (n *IntegrationNetwork) finalizeBlockAndCommit(duration time.Duration, txBytes ...[]byte) (*abcitypes.ResponseFinalizeBlock, error) { +func (n *UpgradeIntegrationNetwork) finalizeBlockAndCommit(duration time.Duration, txBytes ...[]byte) (*abcitypes.ResponseFinalizeBlock, error) { header := n.ctx.BlockHeader() // Update block header and BeginBlock header.Height++ diff --git a/testutil/integration/exrp/network/clients.go b/testutil/integration/exrp/upgrade/clients.go similarity index 68% rename from testutil/integration/exrp/network/clients.go rename to testutil/integration/exrp/upgrade/clients.go index 2c98220..7cf9bce 100644 --- a/testutil/integration/exrp/network/clients.go +++ b/testutil/integration/exrp/upgrade/clients.go @@ -1,6 +1,6 @@ // Copyright Tharsis Labs Ltd.(Evmos) // SPDX-License-Identifier:ENCL-1.0(https://github.com/evmos/evmos/blob/main/LICENSE) -package exrpnetwork +package exrpupgrade import ( "github.com/cosmos/cosmos-sdk/baseapp" @@ -29,74 +29,56 @@ func getQueryHelper(ctx sdktypes.Context, encCfg testutil.TestEncodingConfig) *b return baseapp.NewQueryServerTestHelper(cacheCtx, interfaceRegistry) } -func (n *IntegrationNetwork) GetERC20Client() erc20types.QueryClient { +func (n *UpgradeIntegrationNetwork) GetERC20Client() erc20types.QueryClient { queryHelper := getQueryHelper(n.GetContext(), n.GetEncodingConfig()) erc20types.RegisterQueryServer(queryHelper, n.app.Erc20Keeper) return erc20types.NewQueryClient(queryHelper) } -func (n *IntegrationNetwork) GetEvmClient() evmtypes.QueryClient { +func (n *UpgradeIntegrationNetwork) GetEvmClient() evmtypes.QueryClient { queryHelper := getQueryHelper(n.GetContext(), n.GetEncodingConfig()) evmtypes.RegisterQueryServer(queryHelper, n.app.EvmKeeper) return evmtypes.NewQueryClient(queryHelper) } -func (n *IntegrationNetwork) GetGovClient() govtypes.QueryClient { +func (n *UpgradeIntegrationNetwork) GetGovClient() govtypes.QueryClient { queryHelper := getQueryHelper(n.GetContext(), n.GetEncodingConfig()) govtypes.RegisterQueryServer(queryHelper, govkeeper.NewQueryServer(&n.app.GovKeeper)) return govtypes.NewQueryClient(queryHelper) } -func (n *IntegrationNetwork) GetBankClient() banktypes.QueryClient { +func (n *UpgradeIntegrationNetwork) GetBankClient() banktypes.QueryClient { queryHelper := getQueryHelper(n.GetContext(), n.GetEncodingConfig()) banktypes.RegisterQueryServer(queryHelper, n.app.BankKeeper) return banktypes.NewQueryClient(queryHelper) } -func (n *IntegrationNetwork) GetFeeMarketClient() feemarkettypes.QueryClient { +func (n *UpgradeIntegrationNetwork) GetFeeMarketClient() feemarkettypes.QueryClient { queryHelper := getQueryHelper(n.GetContext(), n.GetEncodingConfig()) feemarkettypes.RegisterQueryServer(queryHelper, n.app.FeeMarketKeeper) return feemarkettypes.NewQueryClient(queryHelper) } -// func (n *IntegrationNetwork) GetInflationClient() infltypes.QueryClient { -// queryHelper := getQueryHelper(n.GetContext(), n.GetEncodingConfig()) -// infltypes.RegisterQueryServer(queryHelper, n.app.InflationKeeper) -// return infltypes.NewQueryClient(queryHelper) -// } - -func (n *IntegrationNetwork) GetAuthClient() authtypes.QueryClient { +func (n *UpgradeIntegrationNetwork) GetAuthClient() authtypes.QueryClient { queryHelper := getQueryHelper(n.GetContext(), n.GetEncodingConfig()) authtypes.RegisterQueryServer(queryHelper, authkeeper.NewQueryServer(n.app.AccountKeeper)) return authtypes.NewQueryClient(queryHelper) } -func (n *IntegrationNetwork) GetAuthzClient() authz.QueryClient { +func (n *UpgradeIntegrationNetwork) GetAuthzClient() authz.QueryClient { queryHelper := getQueryHelper(n.GetContext(), n.GetEncodingConfig()) authz.RegisterQueryServer(queryHelper, n.app.AuthzKeeper) return authz.NewQueryClient(queryHelper) } -func (n *IntegrationNetwork) GetStakingClient() stakingtypes.QueryClient { +func (n *UpgradeIntegrationNetwork) GetStakingClient() stakingtypes.QueryClient { queryHelper := getQueryHelper(n.GetContext(), n.GetEncodingConfig()) stakingtypes.RegisterQueryServer(queryHelper, stakingkeeper.Querier{Keeper: n.app.StakingKeeper.Keeper}) return stakingtypes.NewQueryClient(queryHelper) } -func (n *IntegrationNetwork) GetDistrClient() distrtypes.QueryClient { +func (n *UpgradeIntegrationNetwork) GetDistrClient() distrtypes.QueryClient { queryHelper := getQueryHelper(n.GetContext(), n.GetEncodingConfig()) distrtypes.RegisterQueryServer(queryHelper, distrkeeper.Querier{Keeper: n.app.DistrKeeper}) return distrtypes.NewQueryClient(queryHelper) } - -// func (n *IntegrationNetwork) GetEpochsClient() epochstypes.QueryClient { -// queryHelper := getQueryHelper(n.GetContext(), n.GetEncodingConfig()) -// epochstypes.RegisterQueryServer(queryHelper, n.app.EpochsKeeper) -// return epochstypes.NewQueryClient(queryHelper) -// } - -// func (n *IntegrationNetwork) GetVestingClient() vestingtypes.QueryClient { -// queryHelper := getQueryHelper(n.GetContext(), n.GetEncodingConfig()) -// vestingtypes.RegisterQueryServer(queryHelper, n.app.VestingKeeper) -// return vestingtypes.NewQueryClient(queryHelper) -// } diff --git a/testutil/integration/exrp/network/config.go b/testutil/integration/exrp/upgrade/config.go similarity index 97% rename from testutil/integration/exrp/network/config.go rename to testutil/integration/exrp/upgrade/config.go index 19aa024..8c7d12c 100644 --- a/testutil/integration/exrp/network/config.go +++ b/testutil/integration/exrp/upgrade/config.go @@ -1,7 +1,7 @@ // Copyright Tharsis Labs Ltd.(Evmos) // SPDX-License-Identifier:ENCL-1.0(https://github.com/evmos/evmos/blob/main/LICENSE) -package exrpnetwork +package exrpupgrade import ( "math/big" @@ -123,6 +123,7 @@ func WithCustomBaseAppOpts(opts ...func(*baseapp.BaseApp)) ConfigOption { } } +// WithGenesisFile sets the genesis file for the network. func WithGenesisFile(genesisFile string) ConfigOption { return func(cfg *Config) { genesisBytes, err := os.ReadFile(genesisFile) diff --git a/testutil/integration/exrp/network/config_test.go b/testutil/integration/exrp/upgrade/config_test.go similarity index 98% rename from testutil/integration/exrp/network/config_test.go rename to testutil/integration/exrp/upgrade/config_test.go index 0c5c897..ecbcfe5 100644 --- a/testutil/integration/exrp/network/config_test.go +++ b/testutil/integration/exrp/upgrade/config_test.go @@ -1,7 +1,7 @@ // Copyright Tharsis Labs Ltd.(Evmos) // SPDX-License-Identifier:ENCL-1.0(https://github.com/evmos/evmos/blob/main/LICENSE) -package exrpnetwork_test +package exrpupgrade import ( "testing" diff --git a/testutil/integration/exrp/network/ibc.go b/testutil/integration/exrp/upgrade/ibc.go similarity index 85% rename from testutil/integration/exrp/network/ibc.go rename to testutil/integration/exrp/upgrade/ibc.go index 3c221cc..49be088 100644 --- a/testutil/integration/exrp/network/ibc.go +++ b/testutil/integration/exrp/upgrade/ibc.go @@ -1,6 +1,6 @@ // Copyright Tharsis Labs Ltd.(Evmos) // SPDX-License-Identifier:ENCL-1.0(https://github.com/evmos/evmos/blob/main/LICENSE) -package exrpnetwork +package exrpupgrade import ( "testing" @@ -11,7 +11,7 @@ import ( // GetIBCChain returns a TestChain instance for the given network. // Note: the sender accounts are not populated. Do not use this accounts to send transactions during tests. // The keyring should be used instead. -func (n *IntegrationNetwork) GetIBCChain(t *testing.T, coord *ibctesting.Coordinator) *ibctesting.TestChain { +func (n *UpgradeIntegrationNetwork) GetIBCChain(t *testing.T, coord *ibctesting.Coordinator) *ibctesting.TestChain { return &ibctesting.TestChain{ TB: t, Coordinator: coord, diff --git a/testutil/integration/exrp/network/network.go b/testutil/integration/exrp/upgrade/network.go similarity index 69% rename from testutil/integration/exrp/network/network.go rename to testutil/integration/exrp/upgrade/network.go index 5d4e748..e212e6f 100644 --- a/testutil/integration/exrp/network/network.go +++ b/testutil/integration/exrp/upgrade/network.go @@ -1,7 +1,7 @@ // Copyright Tharsis Labs Ltd.(Evmos) // SPDX-License-Identifier:ENCL-1.0(https://github.com/evmos/evmos/blob/main/LICENSE) -package exrpnetwork +package exrpupgrade import ( "encoding/json" @@ -10,17 +10,14 @@ import ( "math/big" "time" - sdkmath "cosmossdk.io/math" - "github.com/xrplevm/node/v4/app" abcitypes "github.com/cometbft/cometbft/abci/types" - cmtjson "github.com/cometbft/cometbft/libs/json" + ed25519 "github.com/cometbft/cometbft/crypto/ed25519" cmtproto "github.com/cometbft/cometbft/proto/tendermint/types" tmversion "github.com/cometbft/cometbft/proto/tendermint/version" cmttypes "github.com/cometbft/cometbft/types" "github.com/cometbft/cometbft/version" - "github.com/cosmos/cosmos-sdk/testutil/mock" sdktypes "github.com/cosmos/cosmos-sdk/types" sdktestutil "github.com/cosmos/cosmos-sdk/types/module/testutil" txtypes "github.com/cosmos/cosmos-sdk/types/tx" @@ -41,22 +38,18 @@ type Network interface { commonnetwork.Network GetEIP155ChainID() *big.Int - // MODIFIED - // GetEVMChainConfig() *gethparams.ChainConfig // Clients GetERC20Client() erc20types.QueryClient GetEvmClient() evmtypes.QueryClient GetGovClient() govtypes.QueryClient - // GetInflationClient() infltypes.QueryClient GetFeeMarketClient() feemarkettypes.QueryClient - // GetVestingClient() vestingtypes.QueryClient } -var _ Network = (*IntegrationNetwork)(nil) +var _ Network = (*UpgradeIntegrationNetwork)(nil) -// IntegrationNetwork is the implementation of the Network interface for integration tests. -type IntegrationNetwork struct { +// UpgradeIntegrationNetwork is the implementation of the Network interface for integration tests. +type UpgradeIntegrationNetwork struct { cfg Config ctx sdktypes.Context validators []stakingtypes.Validator @@ -72,7 +65,7 @@ type IntegrationNetwork struct { // it uses the default configuration. // // It panics if an error occurs. -func New(opts ...ConfigOption) *IntegrationNetwork { +func New(opts ...ConfigOption) *UpgradeIntegrationNetwork { cfg := DefaultConfig() // Modify the default config with the given options for _, opt := range opts { @@ -80,47 +73,59 @@ func New(opts ...ConfigOption) *IntegrationNetwork { } ctx := sdktypes.Context{} - network := &IntegrationNetwork{ + network := &UpgradeIntegrationNetwork{ cfg: cfg, ctx: ctx, validators: []stakingtypes.Validator{}, } - err := network.configureAndInitChain() + var err error + if cfg.genesisBytes == nil { + // err = network.configureAndInitDefaultChain() + } else { + err = network.configureAndInitChainFromGenesisBytes() + } if err != nil { panic(err) } return network } -var ( - // DefaultBondedAmount is the amount of tokens that each validator will have initially bonded - DefaultBondedAmount = sdktypes.TokensFromConsensusPower(1, types.PowerReduction) - // PrefundedAccountInitialBalance is the amount of tokens that each prefunded account has at genesis - PrefundedAccountInitialBalance, _ = sdkmath.NewIntFromString("100_000_000_000_000_000_000_000") // 100k -) - -func getValidatorsAndSignersFromCustomGenesisState(stakingState stakingtypes.GenesisState) (*cmttypes.ValidatorSet, map[string]cmttypes.PrivValidator, error) { +func getValidatorsAndSignersFromCustomGenesisState( + stakingState stakingtypes.GenesisState, +) ( + *cmttypes.ValidatorSet, + map[string]cmttypes.PrivValidator, + []abcitypes.ValidatorUpdate, error, +) { genesisValidators := stakingState.Validators - validators := make([]stakingtypes.Validator, 0, len(genesisValidators)) - tmValidators := make([]*cmttypes.Validator, 0, len(validators)) - valSigners := make(map[string]cmttypes.PrivValidator, len(validators)) + tmValidators := make([]*cmttypes.Validator, 0, len(genesisValidators)) + validatorsUpdates := make([]abcitypes.ValidatorUpdate, 0, len(genesisValidators)) + valSigners := make(map[string]cmttypes.PrivValidator, len(genesisValidators)) - for i := 0; i < len(validators); i++ { - privVal := mock.NewPV() - pubKey, _ := privVal.GetPubKey() - validator := cmttypes.NewValidator(pubKey, 1) + // For each validator, we need to get the pubkey and create a new validator + for _, val := range genesisValidators { + pb, err := val.CmtConsPublicKey() + if err != nil { + return nil, nil, nil, err + } + pubKey := ed25519.PubKey(pb.GetEd25519()) + + validator := cmttypes.NewValidator(pubKey, 10000000) tmValidators = append(tmValidators, validator) - valSigners[pubKey.Address().String()] = privVal + validatorsUpdates = append(validatorsUpdates, abcitypes.ValidatorUpdate{ + PubKey: pb, + Power: val.GetConsensusPower(val.Tokens), + }) } - return cmttypes.NewValidatorSet(tmValidators), valSigners, nil + return cmttypes.NewValidatorSet(tmValidators), valSigners, validatorsUpdates, nil } // configureAndInitChain initializes the network with the given configuration. // It creates the genesis state and starts the network. -func (n *IntegrationNetwork) configureAndInitChain() error { +func (n *UpgradeIntegrationNetwork) configureAndInitChainFromGenesisBytes() error { // Create a new EvmosApp with the following params exrpApp := createExrpApp(n.cfg.chainID, n.cfg.customBaseAppOpts...) @@ -130,9 +135,9 @@ func (n *IntegrationNetwork) configureAndInitChain() error { return fmt.Errorf("error unmarshalling genesis state: %w", err) } - stateBytes, err := cmtjson.MarshalIndent(genesisState, "", " ") - if err != nil { - return fmt.Errorf("error marshalling genesis state bytes: %w", err) + stateBytes, ok := genesisState["app_state"] + if !ok { + return fmt.Errorf("app_state not found in genesis state") } var appState map[string]json.RawMessage @@ -147,7 +152,7 @@ func (n *IntegrationNetwork) configureAndInitChain() error { return fmt.Errorf("error unmarshalling staking state: %w", err) } - valSet, valSigners, err := getValidatorsAndSignersFromCustomGenesisState(stakingState) + valSet, valSigners, _, err := getValidatorsAndSignersFromCustomGenesisState(stakingState) if err != nil { return fmt.Errorf("error getting validators and signers from custom genesis state: %w", err) } @@ -155,11 +160,22 @@ func (n *IntegrationNetwork) configureAndInitChain() error { // Consensus module does not have a genesis state on the app, // but can customize the consensus parameters of the chain on initialization - fmt.Println("consensus_params", genesisState["consensus_params"]) + var consensusState map[string]json.RawMessage + err = json.Unmarshal(genesisState["consensus"], &consensusState) + if err != nil { + return fmt.Errorf("error unmarshalling consensus state: %w", err) + } var consensusParams *cmtproto.ConsensusParams - _ = json.Unmarshal(genesisState["consensus_params"], &consensusParams) - + err = json.Unmarshal(consensusState["params"], &consensusParams) + if err != nil { + return fmt.Errorf("error unmarshalling consensus params: %w", err) + } + + var initialHeight int64 + if err := json.Unmarshal(genesisState["initial_height"], &initialHeight); err != nil { + return fmt.Errorf("initial_height is not an int64") + } now := time.Now().UTC() if _, err := exrpApp.InitChain( @@ -169,6 +185,7 @@ func (n *IntegrationNetwork) configureAndInitChain() error { Validators: []abcitypes.ValidatorUpdate{}, ConsensusParams: consensusParams, AppStateBytes: stateBytes, + InitialHeight: initialHeight, }, ); err != nil { return err @@ -176,7 +193,7 @@ func (n *IntegrationNetwork) configureAndInitChain() error { header := cmtproto.Header{ ChainID: n.cfg.chainID, - Height: exrpApp.LastBlockHeight() + 1, + Height: initialHeight, AppHash: exrpApp.LastCommitID().Hash, Time: now, ValidatorsHash: valSet.Hash(), @@ -218,49 +235,43 @@ func (n *IntegrationNetwork) configureAndInitChain() error { } // GetContext returns the network's context -func (n *IntegrationNetwork) GetContext() sdktypes.Context { +func (n *UpgradeIntegrationNetwork) GetContext() sdktypes.Context { return n.ctx } // WithIsCheckTxCtx switches the network's checkTx property -func (n *IntegrationNetwork) WithIsCheckTxCtx(isCheckTx bool) sdktypes.Context { +func (n *UpgradeIntegrationNetwork) WithIsCheckTxCtx(isCheckTx bool) sdktypes.Context { n.ctx = n.ctx.WithIsCheckTx(isCheckTx) return n.ctx } // GetChainID returns the network's chainID -func (n *IntegrationNetwork) GetChainID() string { +func (n *UpgradeIntegrationNetwork) GetChainID() string { return n.cfg.chainID } // GetEIP155ChainID returns the network EIp-155 chainID number -func (n *IntegrationNetwork) GetEIP155ChainID() *big.Int { +func (n *UpgradeIntegrationNetwork) GetEIP155ChainID() *big.Int { return n.cfg.eip155ChainID } -// MODIFIED -// GetChainConfig returns the network's chain config -// func (n *IntegrationNetwork) GetEVMChainConfig() *gethparams.ChainConfig { -// return evmtypes.GetEthChainConfig() -// } - // GetDenom returns the network's denom -func (n *IntegrationNetwork) GetDenom() string { +func (n *UpgradeIntegrationNetwork) GetDenom() string { return n.cfg.denom } // GetOtherDenoms returns network's other supported denoms -func (n *IntegrationNetwork) GetOtherDenoms() []string { +func (n *UpgradeIntegrationNetwork) GetOtherDenoms() []string { return n.cfg.otherCoinDenom } // GetValidators returns the network's validators -func (n *IntegrationNetwork) GetValidators() []stakingtypes.Validator { +func (n *UpgradeIntegrationNetwork) GetValidators() []stakingtypes.Validator { return n.validators } // GetOtherDenoms returns network's other supported denoms -func (n *IntegrationNetwork) GetEncodingConfig() sdktestutil.TestEncodingConfig { +func (n *UpgradeIntegrationNetwork) GetEncodingConfig() sdktestutil.TestEncodingConfig { return sdktestutil.TestEncodingConfig{ InterfaceRegistry: n.app.InterfaceRegistry(), Codec: n.app.AppCodec(), @@ -271,7 +282,7 @@ func (n *IntegrationNetwork) GetEncodingConfig() sdktestutil.TestEncodingConfig // BroadcastTxSync broadcasts the given txBytes to the network and returns the response. // TODO - this should be change to gRPC -func (n *IntegrationNetwork) BroadcastTxSync(txBytes []byte) (abcitypes.ExecTxResult, error) { +func (n *UpgradeIntegrationNetwork) BroadcastTxSync(txBytes []byte) (abcitypes.ExecTxResult, error) { header := n.ctx.BlockHeader() // Update block header and BeginBlock header.Height++ @@ -299,7 +310,7 @@ func (n *IntegrationNetwork) BroadcastTxSync(txBytes []byte) (abcitypes.ExecTxRe // Simulate simulates the given txBytes to the network and returns the simulated response. // TODO - this should be change to gRPC -func (n *IntegrationNetwork) Simulate(txBytes []byte) (*txtypes.SimulateResponse, error) { +func (n *UpgradeIntegrationNetwork) Simulate(txBytes []byte) (*txtypes.SimulateResponse, error) { gas, result, err := n.app.BaseApp.Simulate(txBytes) if err != nil { return nil, err @@ -311,7 +322,7 @@ func (n *IntegrationNetwork) Simulate(txBytes []byte) (*txtypes.SimulateResponse } // CheckTx calls the BaseApp's CheckTx method with the given txBytes to the network and returns the response. -func (n *IntegrationNetwork) CheckTx(txBytes []byte) (*abcitypes.ResponseCheckTx, error) { +func (n *UpgradeIntegrationNetwork) CheckTx(txBytes []byte) (*abcitypes.ResponseCheckTx, error) { req := &abcitypes.RequestCheckTx{Tx: txBytes} res, err := n.app.BaseApp.CheckTx(req) if err != nil { diff --git a/testutil/integration/exrp/network/setup.go b/testutil/integration/exrp/upgrade/setup.go similarity index 92% rename from testutil/integration/exrp/network/setup.go rename to testutil/integration/exrp/upgrade/setup.go index dc4f716..1ab365a 100644 --- a/testutil/integration/exrp/network/setup.go +++ b/testutil/integration/exrp/upgrade/setup.go @@ -1,10 +1,11 @@ // Copyright Tharsis Labs Ltd.(Evmos) // SPDX-License-Identifier:ENCL-1.0(https://github.com/evmos/evmos/blob/main/LICENSE) -package exrpnetwork +package exrpupgrade import ( "fmt" + "os" "github.com/xrplevm/node/v4/app" @@ -27,6 +28,15 @@ import ( capabilitytypes "github.com/cosmos/ibc-go/modules/capability/types" ) +func MustGetIntegrationTestNodeHome() string { + wd, err := os.Getwd() + if err != nil { + panic(err) + } + + return wd + "/../../" +} + // genSetupFn is the type for the module genesis setup functions type genSetupFn func(evmosApp *app.App, genesisState app.GenesisState, customGenesis interface{}) (app.GenesisState, error) @@ -70,7 +80,6 @@ func customizeGenesis(exrpApp *app.App, customGen CustomGenesisState, genesisSta if fn, found := genesisSetupFunctions[mod]; found { genesisState, err = fn(exrpApp, genesisState, modGenState) if err != nil { - fmt.Println("error", err) return genesisState, err } } else { @@ -80,16 +89,17 @@ func customizeGenesis(exrpApp *app.App, customGen CustomGenesisState, genesisSta return genesisState, err } -// createExrpApp creates an evmos app +// createExrpApp creates an exrp app func createExrpApp(chainID string, customBaseAppOptions ...func(*baseapp.BaseApp)) *app.App { + testNodeHome := MustGetIntegrationTestNodeHome() // Create evmos app db := dbm.NewMemDB() logger := log.NewNopLogger() loadLatest := true skipUpgradeHeights := map[int64]bool{} - homePath := app.DefaultNodeHome + homePath := testNodeHome invCheckPeriod := uint(5) - appOptions := simutils.NewAppOptionsWithFlagHome(app.DefaultNodeHome) + appOptions := simutils.NewAppOptionsWithFlagHome(homePath) baseAppOptions := append(customBaseAppOptions, baseapp.SetChainID(chainID)) //nolint:gocritic return app.New( diff --git a/testutil/integration/exrp/network/unit_network.go b/testutil/integration/exrp/upgrade/unit_network.go similarity index 95% rename from testutil/integration/exrp/network/unit_network.go rename to testutil/integration/exrp/upgrade/unit_network.go index 1413a57..89422d3 100644 --- a/testutil/integration/exrp/network/unit_network.go +++ b/testutil/integration/exrp/upgrade/unit_network.go @@ -1,6 +1,6 @@ // Copyright Tharsis Labs Ltd.(Evmos) // SPDX-License-Identifier:ENCL-1.0(https://github.com/evmos/evmos/blob/main/LICENSE) -package exrpnetwork +package exrpupgrade import ( sdktypes "github.com/cosmos/cosmos-sdk/types" @@ -14,7 +14,7 @@ import ( // It embeds the IntegrationNetwork struct to reuse its methods and // makes the App public for easier testing. type UnitTestNetwork struct { - IntegrationNetwork + UpgradeIntegrationNetwork App *app.App } @@ -29,7 +29,7 @@ var _ Network = (*UnitTestNetwork)(nil) func NewUnitTestNetwork(opts ...ConfigOption) *UnitTestNetwork { network := New(opts...) return &UnitTestNetwork{ - IntegrationNetwork: *network, + UpgradeIntegrationNetwork: *network, App: network.app, } } From 063f4f2a1ac9c78f28a3e73f18e1265a30c91eac Mon Sep 17 00:00:00 2001 From: GuillemGarciaDev Date: Tue, 3 Dec 2024 16:38:38 +0100 Subject: [PATCH 06/60] refactor(tests, testutil): network composition --- tests/upgrade/network.go | 10 +- tests/upgrade/suite.go | 8 +- tests/upgrade/suite_test.go | 6 +- .../integration/common/network/network.go | 14 +- testutil/integration/exrp/common/abci.go | 38 +++++ .../exrp/{upgrade => common}/clients.go | 59 +++++--- testutil/integration/exrp/common/config.go | 132 +++++++++++++++--- testutil/integration/exrp/common/network.go | 42 ++++++ .../exrp/{upgrade => common}/setup.go | 94 ++++++++----- testutil/integration/exrp/upgrade/abci.go | 35 +---- testutil/integration/exrp/upgrade/config.go | 116 ++------------- testutil/integration/exrp/upgrade/keepers.go | 51 +++++++ testutil/integration/exrp/upgrade/network.go | 43 +++--- .../integration/exrp/upgrade/unit_network.go | 14 +- 14 files changed, 403 insertions(+), 259 deletions(-) create mode 100644 testutil/integration/exrp/common/abci.go rename testutil/integration/exrp/{upgrade => common}/clients.go (61%) create mode 100644 testutil/integration/exrp/common/network.go rename testutil/integration/exrp/{upgrade => common}/setup.go (62%) create mode 100644 testutil/integration/exrp/upgrade/keepers.go diff --git a/tests/upgrade/network.go b/tests/upgrade/network.go index ba79f2f..947eca5 100644 --- a/tests/upgrade/network.go +++ b/tests/upgrade/network.go @@ -3,19 +3,19 @@ package testupgrade import ( commonnetwork "github.com/xrplevm/node/v4/testutil/integration/common/network" exrpcommon "github.com/xrplevm/node/v4/testutil/integration/exrp/common" - exrpnetwork "github.com/xrplevm/node/v4/testutil/integration/exrp/network" + upgradenetwork "github.com/xrplevm/node/v4/testutil/integration/exrp/upgrade" ) var _ commonnetwork.Network = (*UpgradeTestNetwork)(nil) type UpgradeTestNetwork struct { - exrpnetwork.IntegrationNetwork + upgradenetwork.UpgradeIntegrationNetwork } -func NewUpgradeTestNetwork(opts ...exrpnetwork.ConfigOption) *UpgradeTestNetwork { - network := exrpnetwork.New(opts...) +func NewUpgradeTestNetwork(opts ...upgradenetwork.UpgradeConfigOption) *UpgradeTestNetwork { + network := upgradenetwork.New(opts...) return &UpgradeTestNetwork{ - IntegrationNetwork: *network, + UpgradeIntegrationNetwork: *network, } } diff --git a/tests/upgrade/suite.go b/tests/upgrade/suite.go index 2089d11..9da680f 100644 --- a/tests/upgrade/suite.go +++ b/tests/upgrade/suite.go @@ -5,7 +5,7 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" "github.com/stretchr/testify/suite" - exrpnetwork "github.com/xrplevm/node/v4/testutil/integration/exrp/network" + upgradenetwork "github.com/xrplevm/node/v4/testutil/integration/exrp/upgrade" ) const defaultStateFile = "upgrade-state.json" @@ -16,6 +16,10 @@ type UpgradeTestSuite struct { network *UpgradeTestNetwork } +func (s *UpgradeTestSuite) Network() *UpgradeTestNetwork { + return s.network +} + func (s *UpgradeTestSuite) SetupTest() { // Get the state file from the environment variable, or use the default one stateFile := os.Getenv("UPGRADE_STATE_FILE") @@ -31,7 +35,7 @@ func (s *UpgradeTestSuite) SetupTest() { // Create the network s.network = NewUpgradeTestNetwork( - exrpnetwork.WithGenesisFile(stateFile), + upgradenetwork.WithGenesisFile(stateFile), ) // Check that the network was created successfully diff --git a/tests/upgrade/suite_test.go b/tests/upgrade/suite_test.go index 5294058..e326a9a 100644 --- a/tests/upgrade/suite_test.go +++ b/tests/upgrade/suite_test.go @@ -7,6 +7,7 @@ import ( banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" "github.com/stretchr/testify/suite" "github.com/xrplevm/node/v4/app" + exrpcommon "github.com/xrplevm/node/v4/testutil/integration/exrp/common" ) func TestUpgradeTestSuite(t *testing.T) { @@ -18,11 +19,14 @@ func (s *UpgradeTestSuite) TestUpgrade() { s.Require().NotEmpty(denom) s.Require().Equal(denom, app.BaseDenom) - balances, err := s.network.GetBankClient().AllBalances(s.network.GetContext(), &banktypes.QueryAllBalancesRequest{ + balances, err := exrpcommon.GetBankClient(s.Network()).AllBalances(s.network.GetContext(), &banktypes.QueryAllBalancesRequest{ Address: "ethm1fl48vsnmsdzcv85q5d2q4z5ajdha8yu3w48d64", }) fmt.Println("balances", balances) s.Require().NoError(err) fmt.Println(balances) + + err = s.network.NextBlock() + s.Require().NoError(err) } diff --git a/testutil/integration/common/network/network.go b/testutil/integration/common/network/network.go index 7998444..39108ba 100644 --- a/testutil/integration/common/network/network.go +++ b/testutil/integration/common/network/network.go @@ -11,10 +11,6 @@ import ( sdktypes "github.com/cosmos/cosmos-sdk/types" sdktestutil "github.com/cosmos/cosmos-sdk/types/module/testutil" txtypes "github.com/cosmos/cosmos-sdk/types/tx" - authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" - "github.com/cosmos/cosmos-sdk/x/authz" - banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" - distrtypes "github.com/cosmos/cosmos-sdk/x/distribution/types" stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" ibctesting "github.com/cosmos/ibc-go/v8/testing" ) @@ -35,11 +31,11 @@ type Network interface { NextBlockWithTxs(txBytes ...[]byte) (*abcitypes.ResponseFinalizeBlock, error) // Clients - GetAuthClient() authtypes.QueryClient - GetAuthzClient() authz.QueryClient - GetBankClient() banktypes.QueryClient - GetStakingClient() stakingtypes.QueryClient - GetDistrClient() distrtypes.QueryClient + // GetAuthClient() authtypes.QueryClient + // GetAuthzClient() authz.QueryClient + // GetBankClient() banktypes.QueryClient + // GetStakingClient() stakingtypes.QueryClient + // GetDistrClient() distrtypes.QueryClient BroadcastTxSync(txBytes []byte) (abcitypes.ExecTxResult, error) Simulate(txBytes []byte) (*txtypes.SimulateResponse, error) diff --git a/testutil/integration/exrp/common/abci.go b/testutil/integration/exrp/common/abci.go new file mode 100644 index 0000000..b943b7e --- /dev/null +++ b/testutil/integration/exrp/common/abci.go @@ -0,0 +1,38 @@ +package exrpcommon + +import ( + abcitypes "github.com/cometbft/cometbft/abci/types" + cmtproto "github.com/cometbft/cometbft/proto/tendermint/types" + cmttypes "github.com/cometbft/cometbft/types" +) + +// buildFinalizeBlockReq is a helper function to build +// properly the FinalizeBlock request +func BuildFinalizeBlockReq(header cmtproto.Header, validators []*cmttypes.Validator, txs ...[]byte) *abcitypes.RequestFinalizeBlock { + // add validator's commit info to allocate corresponding tokens to validators + ci := GetCommitInfo(validators) + return &abcitypes.RequestFinalizeBlock{ + Height: header.Height, + DecidedLastCommit: ci, + Hash: header.AppHash, + NextValidatorsHash: header.ValidatorsHash, + ProposerAddress: header.ProposerAddress, + Time: header.Time, + Txs: txs, + } +} + +func GetCommitInfo(validators []*cmttypes.Validator) abcitypes.CommitInfo { + voteInfos := make([]abcitypes.VoteInfo, len(validators)) + for i, val := range validators { + voteInfos[i] = abcitypes.VoteInfo{ + Validator: abcitypes.Validator{ + Address: val.Address, + Power: val.VotingPower, + }, + BlockIdFlag: cmtproto.BlockIDFlagCommit, + } + } + return abcitypes.CommitInfo{Votes: voteInfos} +} + diff --git a/testutil/integration/exrp/upgrade/clients.go b/testutil/integration/exrp/common/clients.go similarity index 61% rename from testutil/integration/exrp/upgrade/clients.go rename to testutil/integration/exrp/common/clients.go index 7cf9bce..c2cdc9e 100644 --- a/testutil/integration/exrp/upgrade/clients.go +++ b/testutil/integration/exrp/common/clients.go @@ -1,6 +1,6 @@ // Copyright Tharsis Labs Ltd.(Evmos) // SPDX-License-Identifier:ENCL-1.0(https://github.com/evmos/evmos/blob/main/LICENSE) -package exrpupgrade +package exrpcommon import ( "github.com/cosmos/cosmos-sdk/baseapp" @@ -9,6 +9,8 @@ import ( authkeeper "github.com/cosmos/cosmos-sdk/x/auth/keeper" authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" "github.com/cosmos/cosmos-sdk/x/authz" + authzkeeper "github.com/cosmos/cosmos-sdk/x/authz/keeper" + bankkeeper "github.com/cosmos/cosmos-sdk/x/bank/keeper" banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" distrkeeper "github.com/cosmos/cosmos-sdk/x/distribution/keeper" distrtypes "github.com/cosmos/cosmos-sdk/x/distribution/types" @@ -16,11 +18,30 @@ import ( govtypes "github.com/cosmos/cosmos-sdk/x/gov/types/v1" stakingkeeper "github.com/cosmos/cosmos-sdk/x/staking/keeper" stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" + + erc20keeper "github.com/evmos/evmos/v20/x/erc20/keeper" erc20types "github.com/evmos/evmos/v20/x/erc20/types" + evmkeeper "github.com/evmos/evmos/v20/x/evm/keeper" evmtypes "github.com/evmos/evmos/v20/x/evm/types" + feemarketkeeper "github.com/evmos/evmos/v20/x/feemarket/keeper" feemarkettypes "github.com/evmos/evmos/v20/x/feemarket/types" ) +type NetworkKeepers interface { + GetContext() sdktypes.Context + GetEncodingConfig() testutil.TestEncodingConfig + + ERC20Keeper() erc20keeper.Keeper + EvmKeeper() evmkeeper.Keeper + GovKeeper() *govkeeper.Keeper + BankKeeper() bankkeeper.Keeper + StakingKeeper() *stakingkeeper.Keeper + DistrKeeper() distrkeeper.Keeper + AccountKeeper() authkeeper.AccountKeeper + AuthzKeeper() authzkeeper.Keeper + FeeMarketKeeper() feemarketkeeper.Keeper +} + func getQueryHelper(ctx sdktypes.Context, encCfg testutil.TestEncodingConfig) *baseapp.QueryServiceTestHelper { interfaceRegistry := encCfg.InterfaceRegistry // This is needed so that state changes are not committed in precompiles @@ -29,56 +50,56 @@ func getQueryHelper(ctx sdktypes.Context, encCfg testutil.TestEncodingConfig) *b return baseapp.NewQueryServerTestHelper(cacheCtx, interfaceRegistry) } -func (n *UpgradeIntegrationNetwork) GetERC20Client() erc20types.QueryClient { +func GetERC20Client(n NetworkKeepers) erc20types.QueryClient { queryHelper := getQueryHelper(n.GetContext(), n.GetEncodingConfig()) - erc20types.RegisterQueryServer(queryHelper, n.app.Erc20Keeper) + erc20types.RegisterQueryServer(queryHelper, n.ERC20Keeper()) return erc20types.NewQueryClient(queryHelper) } -func (n *UpgradeIntegrationNetwork) GetEvmClient() evmtypes.QueryClient { +func GetEvmClient(n NetworkKeepers) evmtypes.QueryClient { queryHelper := getQueryHelper(n.GetContext(), n.GetEncodingConfig()) - evmtypes.RegisterQueryServer(queryHelper, n.app.EvmKeeper) + evmtypes.RegisterQueryServer(queryHelper, n.EvmKeeper()) return evmtypes.NewQueryClient(queryHelper) } -func (n *UpgradeIntegrationNetwork) GetGovClient() govtypes.QueryClient { +func GetGovClient(n NetworkKeepers) govtypes.QueryClient { queryHelper := getQueryHelper(n.GetContext(), n.GetEncodingConfig()) - govtypes.RegisterQueryServer(queryHelper, govkeeper.NewQueryServer(&n.app.GovKeeper)) + govtypes.RegisterQueryServer(queryHelper, govkeeper.NewQueryServer(n.GovKeeper())) return govtypes.NewQueryClient(queryHelper) } -func (n *UpgradeIntegrationNetwork) GetBankClient() banktypes.QueryClient { +func GetBankClient(n NetworkKeepers) banktypes.QueryClient { queryHelper := getQueryHelper(n.GetContext(), n.GetEncodingConfig()) - banktypes.RegisterQueryServer(queryHelper, n.app.BankKeeper) + banktypes.RegisterQueryServer(queryHelper, n.BankKeeper()) return banktypes.NewQueryClient(queryHelper) } -func (n *UpgradeIntegrationNetwork) GetFeeMarketClient() feemarkettypes.QueryClient { +func GetFeeMarketClient(n NetworkKeepers) feemarkettypes.QueryClient { queryHelper := getQueryHelper(n.GetContext(), n.GetEncodingConfig()) - feemarkettypes.RegisterQueryServer(queryHelper, n.app.FeeMarketKeeper) + feemarkettypes.RegisterQueryServer(queryHelper, n.FeeMarketKeeper()) return feemarkettypes.NewQueryClient(queryHelper) } -func (n *UpgradeIntegrationNetwork) GetAuthClient() authtypes.QueryClient { +func GetAuthClient(n NetworkKeepers) authtypes.QueryClient { queryHelper := getQueryHelper(n.GetContext(), n.GetEncodingConfig()) - authtypes.RegisterQueryServer(queryHelper, authkeeper.NewQueryServer(n.app.AccountKeeper)) + authtypes.RegisterQueryServer(queryHelper, authkeeper.NewQueryServer(n.AccountKeeper())) return authtypes.NewQueryClient(queryHelper) } -func (n *UpgradeIntegrationNetwork) GetAuthzClient() authz.QueryClient { +func GetAuthzClient(n NetworkKeepers) authz.QueryClient { queryHelper := getQueryHelper(n.GetContext(), n.GetEncodingConfig()) - authz.RegisterQueryServer(queryHelper, n.app.AuthzKeeper) + authz.RegisterQueryServer(queryHelper, n.AuthzKeeper()) return authz.NewQueryClient(queryHelper) } -func (n *UpgradeIntegrationNetwork) GetStakingClient() stakingtypes.QueryClient { +func GetStakingClient(n NetworkKeepers) stakingtypes.QueryClient { queryHelper := getQueryHelper(n.GetContext(), n.GetEncodingConfig()) - stakingtypes.RegisterQueryServer(queryHelper, stakingkeeper.Querier{Keeper: n.app.StakingKeeper.Keeper}) + stakingtypes.RegisterQueryServer(queryHelper, stakingkeeper.Querier{Keeper: n.StakingKeeper()}) return stakingtypes.NewQueryClient(queryHelper) } -func (n *UpgradeIntegrationNetwork) GetDistrClient() distrtypes.QueryClient { +func GetDistrClient(n NetworkKeepers) distrtypes.QueryClient { queryHelper := getQueryHelper(n.GetContext(), n.GetEncodingConfig()) - distrtypes.RegisterQueryServer(queryHelper, distrkeeper.Querier{Keeper: n.app.DistrKeeper}) + distrtypes.RegisterQueryServer(queryHelper, distrkeeper.Querier{Keeper: n.DistrKeeper()}) return distrtypes.NewQueryClient(queryHelper) } diff --git a/testutil/integration/exrp/common/config.go b/testutil/integration/exrp/common/config.go index 20af9bd..e3b78a5 100644 --- a/testutil/integration/exrp/common/config.go +++ b/testutil/integration/exrp/common/config.go @@ -1,25 +1,123 @@ package exrpcommon -import sdk "github.com/cosmos/cosmos-sdk/types" +import ( + "math/big" + + "github.com/cosmos/cosmos-sdk/baseapp" + sdktypes "github.com/cosmos/cosmos-sdk/types" + banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" + evmostypes "github.com/evmos/evmos/v20/types" + "github.com/xrplevm/node/v4/app" +) const ( accountAddressPrefix = "ethm" bip44CoinType = 60 + + ChainID = "exrp_1440002-1" ) -func SetupSdkConfig() { - accountPubKeyPrefix := accountAddressPrefix + "pub" - validatorAddressPrefix := accountAddressPrefix + "valoper" - validatorPubKeyPrefix := accountAddressPrefix + "valoperpub" - consNodeAddressPrefix := accountAddressPrefix + "valcons" - consNodePubKeyPrefix := accountAddressPrefix + "valconspub" - - // Set config - config := sdk.GetConfig() - config.SetBech32PrefixForAccount(accountAddressPrefix, accountPubKeyPrefix) - config.SetBech32PrefixForValidator(validatorAddressPrefix, validatorPubKeyPrefix) - config.SetBech32PrefixForConsensusNode(consNodeAddressPrefix, consNodePubKeyPrefix) - config.SetCoinType(bip44CoinType) - config.SetPurpose(sdk.Purpose) // Shared - config.Seal() -} \ No newline at end of file +// Config defines the configuration for a chain. +// It allows for customization of the network to adjust to +// testing needs. +type Config struct { + ChainID string + Eip155ChainID *big.Int + AmountOfValidators int + PreFundedAccounts []sdktypes.AccAddress + Balances []banktypes.Balance + Denom string + CustomGenesisState CustomGenesisState + GenesisBytes []byte + OtherCoinDenom []string + OperatorsAddrs []sdktypes.AccAddress + CustomBaseAppOpts []func(*baseapp.BaseApp) +} + +type CustomGenesisState map[string]interface{} + +// DefaultConfig returns the default configuration for a chain. +func DefaultConfig() Config { + return Config{ + ChainID: ChainID, + Eip155ChainID: big.NewInt(1440002), + Balances: nil, + // MODIFIED + Denom: app.BaseDenom, + CustomGenesisState: nil, + } +} + +// ConfigOption defines a function that can modify the NetworkConfig. +// The purpose of this is to force to be declarative when the default configuration +// requires to be changed. +type ConfigOption func(*Config) + +// WithChainID sets a custom chainID for the network. It panics if the chainID is invalid. +func WithChainID(chainID string) ConfigOption { + chainIDNum, err := evmostypes.ParseChainID(chainID) + if err != nil { + panic(err) + } + return func(cfg *Config) { + cfg.ChainID = chainID + cfg.Eip155ChainID = chainIDNum + } +} + +// WithAmountOfValidators sets the amount of validators for the network. +func WithAmountOfValidators(amount int) ConfigOption { + return func(cfg *Config) { + cfg.AmountOfValidators = amount + } +} + +// WithPreFundedAccounts sets the pre-funded accounts for the network. +func WithPreFundedAccounts(accounts ...sdktypes.AccAddress) ConfigOption { + return func(cfg *Config) { + cfg.PreFundedAccounts = accounts + } +} + +// WithBalances sets the specific balances for the pre-funded accounts, that +// are being set up for the network. +func WithBalances(balances ...banktypes.Balance) ConfigOption { + return func(cfg *Config) { + cfg.Balances = append(cfg.Balances, balances...) + } +} + +// WithDenom sets the denom for the network. +func WithDenom(denom string) ConfigOption { + return func(cfg *Config) { + cfg.Denom = denom + } +} + +// WithCustomGenesis sets the custom genesis of the network for specific modules. +func WithCustomGenesis(customGenesis CustomGenesisState) ConfigOption { + return func(cfg *Config) { + cfg.CustomGenesisState = customGenesis + } +} + +// WithOtherDenoms sets other possible coin denominations for the network. +func WithOtherDenoms(otherDenoms []string) ConfigOption { + return func(cfg *Config) { + cfg.OtherCoinDenom = otherDenoms + } +} + +// WithValidatorOperators overwrites the used operator address for the network instantiation. +func WithValidatorOperators(keys []sdktypes.AccAddress) ConfigOption { + return func(cfg *Config) { + cfg.OperatorsAddrs = keys + } +} + +// WithCustomBaseAppOpts sets custom base app options for the network. +func WithCustomBaseAppOpts(opts ...func(*baseapp.BaseApp)) ConfigOption { + return func(cfg *Config) { + cfg.CustomBaseAppOpts = opts + } +} diff --git a/testutil/integration/exrp/common/network.go b/testutil/integration/exrp/common/network.go new file mode 100644 index 0000000..00b5fe4 --- /dev/null +++ b/testutil/integration/exrp/common/network.go @@ -0,0 +1,42 @@ +package exrpcommon + +import ( + "testing" + "time" + + sdktypes "github.com/cosmos/cosmos-sdk/types" + ibctesting "github.com/cosmos/ibc-go/v8/testing" + + abcitypes "github.com/cometbft/cometbft/abci/types" + sdktestutil "github.com/cosmos/cosmos-sdk/types/module/testutil" + txtypes "github.com/cosmos/cosmos-sdk/types/tx" + stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" +) + +type Network interface { + // Keepers + NetworkKeepers + + // Clients + BroadcastTxSync(txBytes []byte) (abcitypes.ExecTxResult, error) + Simulate(txBytes []byte) (*txtypes.SimulateResponse, error) + CheckTx(txBytes []byte) (*abcitypes.ResponseCheckTx, error) + + // GetIBCChain returns the IBC test chain. + // NOTE: this is only used for testing IBC related functionality. + // The idea is to deprecate this eventually. + GetIBCChain(t *testing.T, coord *ibctesting.Coordinator) *ibctesting.TestChain + GetEncodingConfig() sdktestutil.TestEncodingConfig + + // Getters + GetContext() sdktypes.Context + GetChainID() string + GetDenom() string + GetOtherDenoms() []string + GetValidators() []stakingtypes.Validator + + // ABCI + NextBlock() error + NextBlockAfter(duration time.Duration) error + NextBlockWithTxs(txBytes ...[]byte) (*abcitypes.ResponseFinalizeBlock, error) +} \ No newline at end of file diff --git a/testutil/integration/exrp/upgrade/setup.go b/testutil/integration/exrp/common/setup.go similarity index 62% rename from testutil/integration/exrp/upgrade/setup.go rename to testutil/integration/exrp/common/setup.go index 1ab365a..a1371db 100644 --- a/testutil/integration/exrp/upgrade/setup.go +++ b/testutil/integration/exrp/common/setup.go @@ -1,60 +1,57 @@ -// Copyright Tharsis Labs Ltd.(Evmos) -// SPDX-License-Identifier:ENCL-1.0(https://github.com/evmos/evmos/blob/main/LICENSE) - -package exrpupgrade +package exrpcommon import ( - "fmt" "os" - "github.com/xrplevm/node/v4/app" - + "cosmossdk.io/log" "github.com/cosmos/cosmos-sdk/baseapp" - "github.com/cosmos/gogoproto/proto" + sdktypes "github.com/cosmos/cosmos-sdk/types" + + "fmt" - "cosmossdk.io/log" dbm "github.com/cosmos/cosmos-db" simutils "github.com/cosmos/cosmos-sdk/testutil/sims" + "github.com/cosmos/gogoproto/proto" + "github.com/xrplevm/node/v4/app" + erc20types "github.com/evmos/evmos/v20/x/erc20/types" + evmtypes "github.com/evmos/evmos/v20/x/evm/types" + feemarkettypes "github.com/evmos/evmos/v20/x/feemarket/types" authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" + banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" + distrtypes "github.com/cosmos/cosmos-sdk/x/distribution/types" + genutiltypes "github.com/cosmos/cosmos-sdk/x/genutil/types" + govtypes "github.com/cosmos/cosmos-sdk/x/gov/types" + govtypesv1 "github.com/cosmos/cosmos-sdk/x/gov/types/v1" + capabilitytypes "github.com/cosmos/ibc-go/modules/capability/types" ) -func MustGetIntegrationTestNodeHome() string { - wd, err := os.Getwd() - if err != nil { - panic(err) - } - - return wd + "/../../" -} - // genSetupFn is the type for the module genesis setup functions -type genSetupFn func(evmosApp *app.App, genesisState app.GenesisState, customGenesis interface{}) (app.GenesisState, error) - +type genSetupFn func(exrpApp *app.App, genesisState app.GenesisState, customGenesis interface{}) (app.GenesisState, error) var genesisSetupFunctions = map[string]genSetupFn{ - evmtypes.ModuleName: genStateSetter[*evmtypes.GenesisState](evmtypes.ModuleName), - erc20types.ModuleName: genStateSetter[*erc20types.GenesisState](erc20types.ModuleName), - govtypes.ModuleName: genStateSetter[*govtypesv1.GenesisState](govtypes.ModuleName), - feemarkettypes.ModuleName: genStateSetter[*feemarkettypes.GenesisState](feemarkettypes.ModuleName), - distrtypes.ModuleName: genStateSetter[*distrtypes.GenesisState](distrtypes.ModuleName), - banktypes.ModuleName: genStateSetter[*banktypes.GenesisState](banktypes.ModuleName), - authtypes.ModuleName: genStateSetter[*authtypes.GenesisState](authtypes.ModuleName), - capabilitytypes.ModuleName: genStateSetter[*capabilitytypes.GenesisState](capabilitytypes.ModuleName), - genutiltypes.ModuleName: genStateSetter[*genutiltypes.GenesisState](genutiltypes.ModuleName), + evmtypes.ModuleName: GenStateSetter[*evmtypes.GenesisState](evmtypes.ModuleName), + erc20types.ModuleName: GenStateSetter[*erc20types.GenesisState](erc20types.ModuleName), + govtypes.ModuleName: GenStateSetter[*govtypesv1.GenesisState](govtypes.ModuleName), + feemarkettypes.ModuleName: GenStateSetter[*feemarkettypes.GenesisState](feemarkettypes.ModuleName), + distrtypes.ModuleName: GenStateSetter[*distrtypes.GenesisState](distrtypes.ModuleName), + banktypes.ModuleName: GenStateSetter[*banktypes.GenesisState](banktypes.ModuleName), + authtypes.ModuleName: GenStateSetter[*authtypes.GenesisState](authtypes.ModuleName), + capabilitytypes.ModuleName: GenStateSetter[*capabilitytypes.GenesisState](capabilitytypes.ModuleName), + genutiltypes.ModuleName: GenStateSetter[*genutiltypes.GenesisState](genutiltypes.ModuleName), } -// genStateSetter is a generic function to set module-specific genesis state -func genStateSetter[T proto.Message](moduleName string) genSetupFn { +// GenStateSetter is a generic function to set module-specific genesis state +func GenStateSetter[T proto.Message](moduleName string) genSetupFn { return func(exrpApp *app.App, genesisState app.GenesisState, customGenesis interface{}) (app.GenesisState, error) { var customGen T err := exrpApp.AppCodec().UnmarshalJSON(genesisState[moduleName], customGen) @@ -72,9 +69,9 @@ func genStateSetter[T proto.Message](moduleName string) genSetupFn { } } -// customizeGenesis modifies genesis state if there're any custom genesis state +// CustomizeGenesis modifies genesis state if there're any custom genesis state // for specific modules -func customizeGenesis(exrpApp *app.App, customGen CustomGenesisState, genesisState app.GenesisState) (app.GenesisState, error) { +func CustomizeGenesis(exrpApp *app.App, customGen CustomGenesisState, genesisState app.GenesisState) (app.GenesisState, error) { var err error for mod, modGenState := range customGen { if fn, found := genesisSetupFunctions[mod]; found { @@ -89,10 +86,37 @@ func customizeGenesis(exrpApp *app.App, customGen CustomGenesisState, genesisSta return genesisState, err } + +func SetupSdkConfig() { + accountPubKeyPrefix := accountAddressPrefix + "pub" + validatorAddressPrefix := accountAddressPrefix + "valoper" + validatorPubKeyPrefix := accountAddressPrefix + "valoperpub" + consNodeAddressPrefix := accountAddressPrefix + "valcons" + consNodePubKeyPrefix := accountAddressPrefix + "valconspub" + + // Set config + config := sdktypes.GetConfig() + config.SetBech32PrefixForAccount(accountAddressPrefix, accountPubKeyPrefix) + config.SetBech32PrefixForValidator(validatorAddressPrefix, validatorPubKeyPrefix) + config.SetBech32PrefixForConsensusNode(consNodeAddressPrefix, consNodePubKeyPrefix) + config.SetCoinType(bip44CoinType) + config.SetPurpose(sdktypes.Purpose) // Shared + config.Seal() +} + +func MustGetIntegrationTestNodeHome() string { + wd, err := os.Getwd() + if err != nil { + panic(err) + } + + return wd + "/../../" +} + // createExrpApp creates an exrp app -func createExrpApp(chainID string, customBaseAppOptions ...func(*baseapp.BaseApp)) *app.App { +func CreateExrpApp(chainID string, customBaseAppOptions ...func(*baseapp.BaseApp)) *app.App { testNodeHome := MustGetIntegrationTestNodeHome() - // Create evmos app + // Create exrp app db := dbm.NewMemDB() logger := log.NewNopLogger() loadLatest := true diff --git a/testutil/integration/exrp/upgrade/abci.go b/testutil/integration/exrp/upgrade/abci.go index 0b8bf7d..5c5805d 100644 --- a/testutil/integration/exrp/upgrade/abci.go +++ b/testutil/integration/exrp/upgrade/abci.go @@ -7,8 +7,7 @@ import ( storetypes "cosmossdk.io/store/types" abcitypes "github.com/cometbft/cometbft/abci/types" - cmtproto "github.com/cometbft/cometbft/proto/tendermint/types" - cmttypes "github.com/cometbft/cometbft/types" + exrpcommon "github.com/xrplevm/node/v4/testutil/integration/exrp/common" ) // NextBlock is a private helper function that runs the EndBlocker logic, commits the changes, @@ -44,7 +43,7 @@ func (n *UpgradeIntegrationNetwork) finalizeBlockAndCommit(duration time.Duratio header.Time = newBlockTime // FinalizeBlock to run endBlock, deliverTx & beginBlock logic - req := buildFinalizeBlockReq(header, n.valSet.Validators, txBytes...) + req := exrpcommon.BuildFinalizeBlockReq(header, n.valSet.Validators, txBytes...) res, err := n.app.FinalizeBlock(req) if err != nil { @@ -68,33 +67,3 @@ func (n *UpgradeIntegrationNetwork) finalizeBlockAndCommit(duration time.Duratio return res, err } - -// buildFinalizeBlockReq is a helper function to build -// properly the FinalizeBlock request -func buildFinalizeBlockReq(header cmtproto.Header, validators []*cmttypes.Validator, txs ...[]byte) *abcitypes.RequestFinalizeBlock { - // add validator's commit info to allocate corresponding tokens to validators - ci := getCommitInfo(validators) - return &abcitypes.RequestFinalizeBlock{ - Height: header.Height, - DecidedLastCommit: ci, - Hash: header.AppHash, - NextValidatorsHash: header.ValidatorsHash, - ProposerAddress: header.ProposerAddress, - Time: header.Time, - Txs: txs, - } -} - -func getCommitInfo(validators []*cmttypes.Validator) abcitypes.CommitInfo { - voteInfos := make([]abcitypes.VoteInfo, len(validators)) - for i, val := range validators { - voteInfos[i] = abcitypes.VoteInfo{ - Validator: abcitypes.Validator{ - Address: val.Address, - Power: val.VotingPower, - }, - BlockIdFlag: cmtproto.BlockIDFlagCommit, - } - } - return abcitypes.CommitInfo{Votes: voteInfos} -} diff --git a/testutil/integration/exrp/upgrade/config.go b/testutil/integration/exrp/upgrade/config.go index 8c7d12c..a55c3b8 100644 --- a/testutil/integration/exrp/upgrade/config.go +++ b/testutil/integration/exrp/upgrade/config.go @@ -4,14 +4,9 @@ package exrpupgrade import ( - "math/big" "os" - "github.com/cosmos/cosmos-sdk/baseapp" - sdktypes "github.com/cosmos/cosmos-sdk/types" - banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" - evmostypes "github.com/evmos/evmos/v20/types" - "github.com/xrplevm/node/v4/app" + exrpcommon "github.com/xrplevm/node/v4/testutil/integration/exrp/common" ) const ( @@ -21,115 +16,26 @@ const ( // Config defines the configuration for a chain. // It allows for customization of the network to adjust to // testing needs. -type Config struct { - chainID string - eip155ChainID *big.Int - amountOfValidators int - preFundedAccounts []sdktypes.AccAddress - balances []banktypes.Balance - denom string - customGenesisState CustomGenesisState - genesisBytes []byte - otherCoinDenom []string - operatorsAddrs []sdktypes.AccAddress - customBaseAppOpts []func(*baseapp.BaseApp) -} - -type CustomGenesisState map[string]interface{} - -// DefaultConfig returns the default configuration for a chain. -func DefaultConfig() Config { - return Config{ - chainID: ChainID, - eip155ChainID: big.NewInt(1440002), - balances: nil, - // MODIFIED - denom: app.BaseDenom, - customGenesisState: nil, - } -} - -// ConfigOption defines a function that can modify the NetworkConfig. -// The purpose of this is to force to be declarative when the default configuration -// requires to be changed. -type ConfigOption func(*Config) +type UpgradeConfig exrpcommon.Config -// WithChainID sets a custom chainID for the network. It panics if the chainID is invalid. -func WithChainID(chainID string) ConfigOption { - chainIDNum, err := evmostypes.ParseChainID(chainID) - if err != nil { - panic(err) - } - return func(cfg *Config) { - cfg.chainID = chainID - cfg.eip155ChainID = chainIDNum - } +func DefaultUpgradeConfig() UpgradeConfig { + return UpgradeConfig(exrpcommon.DefaultConfig()) } -// WithAmountOfValidators sets the amount of validators for the network. -func WithAmountOfValidators(amount int) ConfigOption { - return func(cfg *Config) { - cfg.amountOfValidators = amount - } -} - -// WithPreFundedAccounts sets the pre-funded accounts for the network. -func WithPreFundedAccounts(accounts ...sdktypes.AccAddress) ConfigOption { - return func(cfg *Config) { - cfg.preFundedAccounts = accounts - } -} - -// WithBalances sets the specific balances for the pre-funded accounts, that -// are being set up for the network. -func WithBalances(balances ...banktypes.Balance) ConfigOption { - return func(cfg *Config) { - cfg.balances = append(cfg.balances, balances...) - } +func (cfg UpgradeConfig) Config() *exrpcommon.Config { + rootCfg := exrpcommon.Config(cfg) + return &rootCfg } -// WithDenom sets the denom for the network. -func WithDenom(denom string) ConfigOption { - return func(cfg *Config) { - cfg.denom = denom - } -} - -// WithCustomGenesis sets the custom genesis of the network for specific modules. -func WithCustomGenesis(customGenesis CustomGenesisState) ConfigOption { - return func(cfg *Config) { - cfg.customGenesisState = customGenesis - } -} - -// WithOtherDenoms sets other possible coin denominations for the network. -func WithOtherDenoms(otherDenoms []string) ConfigOption { - return func(cfg *Config) { - cfg.otherCoinDenom = otherDenoms - } -} - -// WithValidatorOperators overwrites the used operator address for the network instantiation. -func WithValidatorOperators(keys []sdktypes.AccAddress) ConfigOption { - return func(cfg *Config) { - cfg.operatorsAddrs = keys - } -} - -// WithCustomBaseAppOpts sets custom base app options for the network. -func WithCustomBaseAppOpts(opts ...func(*baseapp.BaseApp)) ConfigOption { - return func(cfg *Config) { - cfg.customBaseAppOpts = opts - } -} +type UpgradeConfigOption func(cfg *UpgradeConfig) // WithGenesisFile sets the genesis file for the network. -func WithGenesisFile(genesisFile string) ConfigOption { - return func(cfg *Config) { +func WithGenesisFile(genesisFile string) UpgradeConfigOption { + return func(cfg *UpgradeConfig) { genesisBytes, err := os.ReadFile(genesisFile) if err != nil { panic(err) } - cfg.genesisBytes = genesisBytes + cfg.GenesisBytes = genesisBytes } } diff --git a/testutil/integration/exrp/upgrade/keepers.go b/testutil/integration/exrp/upgrade/keepers.go new file mode 100644 index 0000000..b2a37cf --- /dev/null +++ b/testutil/integration/exrp/upgrade/keepers.go @@ -0,0 +1,51 @@ +package exrpupgrade + +import ( + authkeeper "github.com/cosmos/cosmos-sdk/x/auth/keeper" + authzkeeper "github.com/cosmos/cosmos-sdk/x/authz/keeper" + bankkeeper "github.com/cosmos/cosmos-sdk/x/bank/keeper" + distrkeeper "github.com/cosmos/cosmos-sdk/x/distribution/keeper" + govkeeper "github.com/cosmos/cosmos-sdk/x/gov/keeper" + stakingkeeper "github.com/cosmos/cosmos-sdk/x/staking/keeper" + + erc20keeper "github.com/evmos/evmos/v20/x/erc20/keeper" + evmkeeper "github.com/evmos/evmos/v20/x/evm/keeper" + feemarketkeeper "github.com/evmos/evmos/v20/x/feemarket/keeper" +) + + +func (n *UpgradeIntegrationNetwork) BankKeeper() bankkeeper.Keeper { + return n.app.BankKeeper +} + +func (n *UpgradeIntegrationNetwork) ERC20Keeper() erc20keeper.Keeper { + return n.app.Erc20Keeper +} + +func (n *UpgradeIntegrationNetwork) EvmKeeper() evmkeeper.Keeper { + return *n.app.EvmKeeper +} + +func (n *UpgradeIntegrationNetwork) GovKeeper() *govkeeper.Keeper { + return &n.app.GovKeeper +} + +func (n *UpgradeIntegrationNetwork) StakingKeeper() *stakingkeeper.Keeper { + return n.app.StakingKeeper.Keeper +} + +func (n *UpgradeIntegrationNetwork) DistrKeeper() distrkeeper.Keeper { + return n.app.DistrKeeper +} + +func (n *UpgradeIntegrationNetwork) AccountKeeper() authkeeper.AccountKeeper { + return n.app.AccountKeeper +} + +func (n *UpgradeIntegrationNetwork) AuthzKeeper() authzkeeper.Keeper { + return n.app.AuthzKeeper +} + +func (n *UpgradeIntegrationNetwork) FeeMarketKeeper() feemarketkeeper.Keeper { + return n.app.FeeMarketKeeper +} diff --git a/testutil/integration/exrp/upgrade/network.go b/testutil/integration/exrp/upgrade/network.go index e212e6f..09e115d 100644 --- a/testutil/integration/exrp/upgrade/network.go +++ b/testutil/integration/exrp/upgrade/network.go @@ -11,6 +11,7 @@ import ( "time" "github.com/xrplevm/node/v4/app" + exrpcommon "github.com/xrplevm/node/v4/testutil/integration/exrp/common" abcitypes "github.com/cometbft/cometbft/abci/types" ed25519 "github.com/cometbft/cometbft/crypto/ed25519" @@ -21,13 +22,9 @@ import ( sdktypes "github.com/cosmos/cosmos-sdk/types" sdktestutil "github.com/cosmos/cosmos-sdk/types/module/testutil" txtypes "github.com/cosmos/cosmos-sdk/types/tx" - govtypes "github.com/cosmos/cosmos-sdk/x/gov/types/v1" stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" - commonnetwork "github.com/evmos/evmos/v20/testutil/integration/common/network" + "github.com/evmos/evmos/v20/types" - erc20types "github.com/evmos/evmos/v20/x/erc20/types" - evmtypes "github.com/evmos/evmos/v20/x/evm/types" - feemarkettypes "github.com/evmos/evmos/v20/x/feemarket/types" ) // Network is the interface that wraps the methods to interact with integration test network. @@ -35,22 +32,16 @@ import ( // It was designed to avoid users to access module's keepers directly and force integration tests // to be closer to the real user's behavior. type Network interface { - commonnetwork.Network + exrpcommon.Network GetEIP155ChainID() *big.Int - - // Clients - GetERC20Client() erc20types.QueryClient - GetEvmClient() evmtypes.QueryClient - GetGovClient() govtypes.QueryClient - GetFeeMarketClient() feemarkettypes.QueryClient } var _ Network = (*UpgradeIntegrationNetwork)(nil) // UpgradeIntegrationNetwork is the implementation of the Network interface for integration tests. type UpgradeIntegrationNetwork struct { - cfg Config + cfg UpgradeConfig ctx sdktypes.Context validators []stakingtypes.Validator app *app.App @@ -65,8 +56,8 @@ type UpgradeIntegrationNetwork struct { // it uses the default configuration. // // It panics if an error occurs. -func New(opts ...ConfigOption) *UpgradeIntegrationNetwork { - cfg := DefaultConfig() +func New(opts ...UpgradeConfigOption) *UpgradeIntegrationNetwork { + cfg := DefaultUpgradeConfig() // Modify the default config with the given options for _, opt := range opts { opt(&cfg) @@ -80,7 +71,7 @@ func New(opts ...ConfigOption) *UpgradeIntegrationNetwork { } var err error - if cfg.genesisBytes == nil { + if cfg.GenesisBytes == nil { // err = network.configureAndInitDefaultChain() } else { err = network.configureAndInitChainFromGenesisBytes() @@ -127,10 +118,10 @@ func getValidatorsAndSignersFromCustomGenesisState( // It creates the genesis state and starts the network. func (n *UpgradeIntegrationNetwork) configureAndInitChainFromGenesisBytes() error { // Create a new EvmosApp with the following params - exrpApp := createExrpApp(n.cfg.chainID, n.cfg.customBaseAppOpts...) + exrpApp := exrpcommon.CreateExrpApp(n.cfg.ChainID, n.cfg.CustomBaseAppOpts...) var genesisState app.GenesisState - err := json.Unmarshal(n.cfg.genesisBytes, &genesisState) + err := json.Unmarshal(n.cfg.GenesisBytes, &genesisState) if err != nil { return fmt.Errorf("error unmarshalling genesis state: %w", err) } @@ -181,7 +172,7 @@ func (n *UpgradeIntegrationNetwork) configureAndInitChainFromGenesisBytes() erro if _, err := exrpApp.InitChain( &abcitypes.RequestInitChain{ Time: now, - ChainId: n.cfg.chainID, + ChainId: n.cfg.ChainID, Validators: []abcitypes.ValidatorUpdate{}, ConsensusParams: consensusParams, AppStateBytes: stateBytes, @@ -192,7 +183,7 @@ func (n *UpgradeIntegrationNetwork) configureAndInitChainFromGenesisBytes() erro } header := cmtproto.Header{ - ChainID: n.cfg.chainID, + ChainID: n.cfg.ChainID, Height: initialHeight, AppHash: exrpApp.LastCommitID().Hash, Time: now, @@ -204,7 +195,7 @@ func (n *UpgradeIntegrationNetwork) configureAndInitChainFromGenesisBytes() erro }, } - req := buildFinalizeBlockReq(header, valSet.Validators) + req := exrpcommon.BuildFinalizeBlockReq(header, valSet.Validators) if _, err := exrpApp.FinalizeBlock(req); err != nil { return err } @@ -247,22 +238,22 @@ func (n *UpgradeIntegrationNetwork) WithIsCheckTxCtx(isCheckTx bool) sdktypes.Co // GetChainID returns the network's chainID func (n *UpgradeIntegrationNetwork) GetChainID() string { - return n.cfg.chainID + return n.cfg.ChainID } // GetEIP155ChainID returns the network EIp-155 chainID number func (n *UpgradeIntegrationNetwork) GetEIP155ChainID() *big.Int { - return n.cfg.eip155ChainID + return n.cfg.Eip155ChainID } // GetDenom returns the network's denom func (n *UpgradeIntegrationNetwork) GetDenom() string { - return n.cfg.denom + return n.cfg.Denom } // GetOtherDenoms returns network's other supported denoms func (n *UpgradeIntegrationNetwork) GetOtherDenoms() []string { - return n.cfg.otherCoinDenom + return n.cfg.OtherCoinDenom } // GetValidators returns the network's validators @@ -291,7 +282,7 @@ func (n *UpgradeIntegrationNetwork) BroadcastTxSync(txBytes []byte) (abcitypes.E newBlockTime := header.Time.Add(time.Second) header.Time = newBlockTime - req := buildFinalizeBlockReq(header, n.valSet.Validators, txBytes) + req := exrpcommon.BuildFinalizeBlockReq(header, n.valSet.Validators, txBytes) // dont include the DecidedLastCommit because we're not committing the changes // here, is just for broadcasting the tx. To persist the changes, use the diff --git a/testutil/integration/exrp/upgrade/unit_network.go b/testutil/integration/exrp/upgrade/unit_network.go index 89422d3..376a14c 100644 --- a/testutil/integration/exrp/upgrade/unit_network.go +++ b/testutil/integration/exrp/upgrade/unit_network.go @@ -10,15 +10,15 @@ import ( "github.com/xrplevm/node/v4/app" ) -// UnitTestNetwork is the implementation of the Network interface for unit tests. +// UnitTestUpgradeNetwork is the implementation of the Network interface for unit tests. // It embeds the IntegrationNetwork struct to reuse its methods and // makes the App public for easier testing. -type UnitTestNetwork struct { +type UnitTestUpgradeNetwork struct { UpgradeIntegrationNetwork App *app.App } -var _ Network = (*UnitTestNetwork)(nil) +var _ Network = (*UnitTestUpgradeNetwork)(nil) // NewUnitTestNetwork configures and initializes a new Evmos Network instance with // the given configuration options. If no configuration options are provided @@ -26,16 +26,16 @@ var _ Network = (*UnitTestNetwork)(nil) // // It panics if an error occurs. // Note: Only uses for Unit Tests -func NewUnitTestNetwork(opts ...ConfigOption) *UnitTestNetwork { +func NewUnitTestUpgradeNetwork(opts ...UpgradeConfigOption) *UnitTestUpgradeNetwork { network := New(opts...) - return &UnitTestNetwork{ + return &UnitTestUpgradeNetwork{ UpgradeIntegrationNetwork: *network, App: network.app, } } // GetStateDB returns the state database for the current block. -func (n *UnitTestNetwork) GetStateDB() *statedb.StateDB { +func (n *UnitTestUpgradeNetwork) GetStateDB() *statedb.StateDB { headerHash := n.GetContext().HeaderHash() return statedb.New( n.GetContext(), @@ -45,7 +45,7 @@ func (n *UnitTestNetwork) GetStateDB() *statedb.StateDB { } // FundAccount funds the given account with the given amount of coins. -func (n *UnitTestNetwork) FundAccount(addr sdktypes.AccAddress, coins sdktypes.Coins) error { +func (n *UnitTestUpgradeNetwork) FundAccount(addr sdktypes.AccAddress, coins sdktypes.Coins) error { ctx := n.GetContext() if err := n.app.BankKeeper.MintCoins(ctx, inflationtypes.ModuleName, coins); err != nil { From 6d0e8112b703cc72b16ab06be2e6579a7249f670 Mon Sep 17 00:00:00 2001 From: GuillemGarciaDev Date: Wed, 4 Dec 2024 09:33:47 +0100 Subject: [PATCH 07/60] feat(testutil): add keyring testutils --- .../integration/common/keyring/keyring.go | 119 ++++++++++++++++++ 1 file changed, 119 insertions(+) create mode 100644 testutil/integration/common/keyring/keyring.go diff --git a/testutil/integration/common/keyring/keyring.go b/testutil/integration/common/keyring/keyring.go new file mode 100644 index 0000000..013e499 --- /dev/null +++ b/testutil/integration/common/keyring/keyring.go @@ -0,0 +1,119 @@ +// Copyright Tharsis Labs Ltd.(Evmos) +// SPDX-License-Identifier:ENCL-1.0(https://github.com/evmos/evmos/blob/main/LICENSE) +package keyring + +import ( + "fmt" + + "github.com/ethereum/go-ethereum/common" + + cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" + sdktypes "github.com/cosmos/cosmos-sdk/types" + utiltx "github.com/evmos/evmos/v20/testutil/tx" +) + +type Key struct { + Addr common.Address + AccAddr sdktypes.AccAddress + Priv cryptotypes.PrivKey +} + +func NewKey() Key { + addr, privKey := utiltx.NewAddrKey() + return Key{ + Addr: addr, + AccAddr: sdktypes.AccAddress(addr.Bytes()), + Priv: privKey, + } +} + +type Keyring interface { + // GetPrivKey returns the private key of the account at the given keyring index. + GetPrivKey(index int) cryptotypes.PrivKey + // GetAddr returns the address of the account at the given keyring index. + GetAddr(index int) common.Address + // GetAccAddr returns the SDK address of the account at the given keyring index. + GetAccAddr(index int) sdktypes.AccAddress + // GetAllAccAddrs returns all the SDK addresses of the accounts in the keyring. + GetAllAccAddrs() []sdktypes.AccAddress + // GetKey returns the key at the given keyring index + GetKey(index int) Key + // GetKeys returns all the keys + GetKeys() []Key + + // AddKey adds a new account to the keyring + AddKey() int + + // Sign signs message with the specified account. + Sign(index int, msg []byte) ([]byte, error) +} + +// IntegrationKeyring is a keyring designed for integration tests. +type IntegrationKeyring struct { + keys []Key +} + +var _ Keyring = (*IntegrationKeyring)(nil) + +// New returns a new keyring with nAccs accounts. +func New(nAccs int) Keyring { + accs := make([]Key, 0, nAccs) + for i := 0; i < nAccs; i++ { + acc := NewKey() + accs = append(accs, acc) + } + return &IntegrationKeyring{ + keys: accs, + } +} + +// GetPrivKey returns the private key of the specified account. +func (kr *IntegrationKeyring) GetPrivKey(index int) cryptotypes.PrivKey { + return kr.keys[index].Priv +} + +// GetAddr returns the address of the specified account. +func (kr *IntegrationKeyring) GetAddr(index int) common.Address { + return kr.keys[index].Addr +} + +// GetAccAddr returns the sdk address of the specified account. +func (kr *IntegrationKeyring) GetAccAddr(index int) sdktypes.AccAddress { + return kr.keys[index].AccAddr +} + +// GetAllAccAddrs returns all the sdk addresses of the accounts in the keyring. +func (kr *IntegrationKeyring) GetAllAccAddrs() []sdktypes.AccAddress { + accs := make([]sdktypes.AccAddress, 0, len(kr.keys)) + for _, key := range kr.keys { + accs = append(accs, key.AccAddr) + } + return accs +} + +// GetKey returns the key specified by index +func (kr *IntegrationKeyring) GetKey(index int) Key { + return kr.keys[index] +} + +// GetKey returns the key specified by index +func (kr *IntegrationKeyring) GetKeys() []Key { + return kr.keys +} + +// AddKey adds a new account to the keyring. It returns the index for the key +func (kr *IntegrationKeyring) AddKey() int { + acc := NewKey() + index := len(kr.keys) + kr.keys = append(kr.keys, acc) + return index +} + +// Sign signs message with the specified key. +func (kr *IntegrationKeyring) Sign(index int, msg []byte) ([]byte, error) { + privKey := kr.GetPrivKey(index) + if privKey == nil { + return nil, fmt.Errorf("no private key for account %d", index) + } + return privKey.Sign(msg) +} From 73082f516ab34d9a99813addbc7a52508e977bac Mon Sep 17 00:00:00 2001 From: GuillemGarciaDev Date: Wed, 4 Dec 2024 09:34:06 +0100 Subject: [PATCH 08/60] feat(testutil): adding default network integration testutil --- .../integration/exrp/integration/clients.go | 105 ++++ .../integration/exrp/integration/config.go | 154 ++++++ .../exrp/integration/config_test.go | 51 ++ testutil/integration/exrp/integration/ibc.go | 28 + .../integration/exrp/integration/network.go | 345 ++++++++++++ .../integration/exrp/integration/setup.go | 493 ++++++++++++++++++ .../exrp/integration/unit_network.go | 56 ++ testutil/integration/exrp/upgrade/network.go | 8 +- 8 files changed, 1235 insertions(+), 5 deletions(-) create mode 100644 testutil/integration/exrp/integration/clients.go create mode 100644 testutil/integration/exrp/integration/config.go create mode 100644 testutil/integration/exrp/integration/config_test.go create mode 100644 testutil/integration/exrp/integration/ibc.go create mode 100644 testutil/integration/exrp/integration/network.go create mode 100644 testutil/integration/exrp/integration/setup.go create mode 100644 testutil/integration/exrp/integration/unit_network.go diff --git a/testutil/integration/exrp/integration/clients.go b/testutil/integration/exrp/integration/clients.go new file mode 100644 index 0000000..685905c --- /dev/null +++ b/testutil/integration/exrp/integration/clients.go @@ -0,0 +1,105 @@ +// Copyright Tharsis Labs Ltd.(Evmos) +// SPDX-License-Identifier:ENCL-1.0(https://github.com/evmos/evmos/blob/main/LICENSE) +package exrpintegration + +import ( + "github.com/cosmos/cosmos-sdk/baseapp" + sdktypes "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/module/testutil" + authkeeper "github.com/cosmos/cosmos-sdk/x/auth/keeper" + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" + "github.com/cosmos/cosmos-sdk/x/authz" + banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" + distrkeeper "github.com/cosmos/cosmos-sdk/x/distribution/keeper" + distrtypes "github.com/cosmos/cosmos-sdk/x/distribution/types" + govkeeper "github.com/cosmos/cosmos-sdk/x/gov/keeper" + govtypes "github.com/cosmos/cosmos-sdk/x/gov/types/v1" + stakingkeeper "github.com/cosmos/cosmos-sdk/x/staking/keeper" + stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" + epochstypes "github.com/evmos/evmos/v20/x/epochs/types" + erc20types "github.com/evmos/evmos/v20/x/erc20/types" + evmtypes "github.com/evmos/evmos/v20/x/evm/types" + feemarkettypes "github.com/evmos/evmos/v20/x/feemarket/types" + infltypes "github.com/evmos/evmos/v20/x/inflation/v1/types" + vestingtypes "github.com/evmos/evmos/v20/x/vesting/types" +) + +func getQueryHelper(ctx sdktypes.Context, encCfg testutil.TestEncodingConfig) *baseapp.QueryServiceTestHelper { + interfaceRegistry := encCfg.InterfaceRegistry + // This is needed so that state changes are not committed in precompiles + // simulations. + cacheCtx, _ := ctx.CacheContext() + return baseapp.NewQueryServerTestHelper(cacheCtx, interfaceRegistry) +} + +func (n *IntegrationNetwork) GetERC20Client() erc20types.QueryClient { + queryHelper := getQueryHelper(n.GetContext(), n.GetEncodingConfig()) + erc20types.RegisterQueryServer(queryHelper, n.app.Erc20Keeper) + return erc20types.NewQueryClient(queryHelper) +} + +func (n *IntegrationNetwork) GetEvmClient() evmtypes.QueryClient { + queryHelper := getQueryHelper(n.GetContext(), n.GetEncodingConfig()) + evmtypes.RegisterQueryServer(queryHelper, n.app.EvmKeeper) + return evmtypes.NewQueryClient(queryHelper) +} + +func (n *IntegrationNetwork) GetGovClient() govtypes.QueryClient { + queryHelper := getQueryHelper(n.GetContext(), n.GetEncodingConfig()) + govtypes.RegisterQueryServer(queryHelper, govkeeper.NewQueryServer(&n.app.GovKeeper)) + return govtypes.NewQueryClient(queryHelper) +} + +func (n *IntegrationNetwork) GetBankClient() banktypes.QueryClient { + queryHelper := getQueryHelper(n.GetContext(), n.GetEncodingConfig()) + banktypes.RegisterQueryServer(queryHelper, n.app.BankKeeper) + return banktypes.NewQueryClient(queryHelper) +} + +func (n *IntegrationNetwork) GetFeeMarketClient() feemarkettypes.QueryClient { + queryHelper := getQueryHelper(n.GetContext(), n.GetEncodingConfig()) + feemarkettypes.RegisterQueryServer(queryHelper, n.app.FeeMarketKeeper) + return feemarkettypes.NewQueryClient(queryHelper) +} + +func (n *IntegrationNetwork) GetInflationClient() infltypes.QueryClient { + queryHelper := getQueryHelper(n.GetContext(), n.GetEncodingConfig()) + infltypes.RegisterQueryServer(queryHelper, n.app.InflationKeeper) + return infltypes.NewQueryClient(queryHelper) +} + +func (n *IntegrationNetwork) GetAuthClient() authtypes.QueryClient { + queryHelper := getQueryHelper(n.GetContext(), n.GetEncodingConfig()) + authtypes.RegisterQueryServer(queryHelper, authkeeper.NewQueryServer(n.app.AccountKeeper)) + return authtypes.NewQueryClient(queryHelper) +} + +func (n *IntegrationNetwork) GetAuthzClient() authz.QueryClient { + queryHelper := getQueryHelper(n.GetContext(), n.GetEncodingConfig()) + authz.RegisterQueryServer(queryHelper, n.app.AuthzKeeper) + return authz.NewQueryClient(queryHelper) +} + +func (n *IntegrationNetwork) GetStakingClient() stakingtypes.QueryClient { + queryHelper := getQueryHelper(n.GetContext(), n.GetEncodingConfig()) + stakingtypes.RegisterQueryServer(queryHelper, stakingkeeper.Querier{Keeper: n.app.StakingKeeper.Keeper}) + return stakingtypes.NewQueryClient(queryHelper) +} + +func (n *IntegrationNetwork) GetDistrClient() distrtypes.QueryClient { + queryHelper := getQueryHelper(n.GetContext(), n.GetEncodingConfig()) + distrtypes.RegisterQueryServer(queryHelper, distrkeeper.Querier{Keeper: n.app.DistrKeeper}) + return distrtypes.NewQueryClient(queryHelper) +} + +func (n *IntegrationNetwork) GetEpochsClient() epochstypes.QueryClient { + queryHelper := getQueryHelper(n.GetContext(), n.GetEncodingConfig()) + epochstypes.RegisterQueryServer(queryHelper, n.app.EpochsKeeper) + return epochstypes.NewQueryClient(queryHelper) +} + +func (n *IntegrationNetwork) GetVestingClient() vestingtypes.QueryClient { + queryHelper := getQueryHelper(n.GetContext(), n.GetEncodingConfig()) + vestingtypes.RegisterQueryServer(queryHelper, n.app.VestingKeeper) + return vestingtypes.NewQueryClient(queryHelper) +} diff --git a/testutil/integration/exrp/integration/config.go b/testutil/integration/exrp/integration/config.go new file mode 100644 index 0000000..ccfd9d5 --- /dev/null +++ b/testutil/integration/exrp/integration/config.go @@ -0,0 +1,154 @@ +// Copyright Tharsis Labs Ltd.(Evmos) +// SPDX-License-Identifier:ENCL-1.0(https://github.com/evmos/evmos/blob/main/LICENSE) + +package exrpintegration + +import ( + "fmt" + "math/big" + + "github.com/cosmos/cosmos-sdk/baseapp" + sdktypes "github.com/cosmos/cosmos-sdk/types" + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" + banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" + stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" + testtx "github.com/evmos/evmos/v20/testutil/tx" + evmostypes "github.com/evmos/evmos/v20/types" + "github.com/evmos/evmos/v20/utils" +) + +// Config defines the configuration for a chain. +// It allows for customization of the network to adjust to +// testing needs. +type Config struct { + chainID string + eip155ChainID *big.Int + amountOfValidators int + preFundedAccounts []sdktypes.AccAddress + balances []banktypes.Balance + denom string + customGenesisState CustomGenesisState + otherCoinDenom []string + operatorsAddrs []sdktypes.AccAddress + customBaseAppOpts []func(*baseapp.BaseApp) +} + +type CustomGenesisState map[string]interface{} + +// DefaultConfig returns the default configuration for a chain. +func DefaultConfig() Config { + account, _ := testtx.NewAccAddressAndKey() + return Config{ + chainID: utils.MainnetChainID + "-1", + eip155ChainID: big.NewInt(9001), + amountOfValidators: 3, + // Only one account besides the validators + preFundedAccounts: []sdktypes.AccAddress{account}, + // NOTE: Per default, the balances are left empty, and the pre-funded accounts are used. + balances: nil, + denom: utils.BaseDenom, + customGenesisState: nil, + } +} + +// getGenAccountsAndBalances takes the network configuration and returns the used +// genesis accounts and balances. +// +// NOTE: If the balances are set, the pre-funded accounts are ignored. +func getGenAccountsAndBalances(cfg Config, validators []stakingtypes.Validator) (genAccounts []authtypes.GenesisAccount, balances []banktypes.Balance) { + if len(cfg.balances) > 0 { + balances = cfg.balances + accounts := getAccAddrsFromBalances(balances) + genAccounts = createGenesisAccounts(accounts) + } else { + genAccounts = createGenesisAccounts(cfg.preFundedAccounts) + balances = createBalances(cfg.preFundedAccounts, append(cfg.otherCoinDenom, cfg.denom)) + } + + // append validators to genesis accounts and balances + valAccs := make([]sdktypes.AccAddress, len(validators)) + for i, v := range validators { + valAddr, err := sdktypes.ValAddressFromBech32(v.OperatorAddress) + if err != nil { + panic(fmt.Sprintf("failed to derive validator address from %q: %s", v.OperatorAddress, err.Error())) + } + valAccs[i] = sdktypes.AccAddress(valAddr.Bytes()) + } + genAccounts = append(genAccounts, createGenesisAccounts(valAccs)...) + + return +} + +// ConfigOption defines a function that can modify the NetworkConfig. +// The purpose of this is to force to be declarative when the default configuration +// requires to be changed. +type ConfigOption func(*Config) + +// WithChainID sets a custom chainID for the network. It panics if the chainID is invalid. +func WithChainID(chainID string) ConfigOption { + chainIDNum, err := evmostypes.ParseChainID(chainID) + if err != nil { + panic(err) + } + return func(cfg *Config) { + cfg.chainID = chainID + cfg.eip155ChainID = chainIDNum + } +} + +// WithAmountOfValidators sets the amount of validators for the network. +func WithAmountOfValidators(amount int) ConfigOption { + return func(cfg *Config) { + cfg.amountOfValidators = amount + } +} + +// WithPreFundedAccounts sets the pre-funded accounts for the network. +func WithPreFundedAccounts(accounts ...sdktypes.AccAddress) ConfigOption { + return func(cfg *Config) { + cfg.preFundedAccounts = accounts + } +} + +// WithBalances sets the specific balances for the pre-funded accounts, that +// are being set up for the network. +func WithBalances(balances ...banktypes.Balance) ConfigOption { + return func(cfg *Config) { + cfg.balances = append(cfg.balances, balances...) + } +} + +// WithDenom sets the denom for the network. +func WithDenom(denom string) ConfigOption { + return func(cfg *Config) { + cfg.denom = denom + } +} + +// WithCustomGenesis sets the custom genesis of the network for specific modules. +func WithCustomGenesis(customGenesis CustomGenesisState) ConfigOption { + return func(cfg *Config) { + cfg.customGenesisState = customGenesis + } +} + +// WithOtherDenoms sets other possible coin denominations for the network. +func WithOtherDenoms(otherDenoms []string) ConfigOption { + return func(cfg *Config) { + cfg.otherCoinDenom = otherDenoms + } +} + +// WithValidatorOperators overwrites the used operator address for the network instantiation. +func WithValidatorOperators(keys []sdktypes.AccAddress) ConfigOption { + return func(cfg *Config) { + cfg.operatorsAddrs = keys + } +} + +// WithCustomBaseAppOpts sets custom base app options for the network. +func WithCustomBaseAppOpts(opts ...func(*baseapp.BaseApp)) ConfigOption { + return func(cfg *Config) { + cfg.customBaseAppOpts = opts + } +} diff --git a/testutil/integration/exrp/integration/config_test.go b/testutil/integration/exrp/integration/config_test.go new file mode 100644 index 0000000..fddf107 --- /dev/null +++ b/testutil/integration/exrp/integration/config_test.go @@ -0,0 +1,51 @@ +// Copyright Tharsis Labs Ltd.(Evmos) +// SPDX-License-Identifier:ENCL-1.0(https://github.com/evmos/evmos/blob/main/LICENSE) + +package exrpintegration + +import ( + "testing" + + sdk "github.com/cosmos/cosmos-sdk/types" + banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" + grpchandler "github.com/evmos/evmos/v20/testutil/integration/evmos/grpc" + testkeyring "github.com/evmos/evmos/v20/testutil/integration/evmos/keyring" + "github.com/evmos/evmos/v20/testutil/integration/evmos/network" + "github.com/evmos/evmos/v20/utils" + "github.com/stretchr/testify/require" +) + +func TestWithBalances(t *testing.T) { + key1Balance := sdk.NewCoins(sdk.NewInt64Coin(utils.BaseDenom, 1e18)) + key2Balance := sdk.NewCoins( + sdk.NewInt64Coin(utils.BaseDenom, 2e18), + sdk.NewInt64Coin("other", 3e18), + ) + + // Create a new network with 2 pre-funded accounts + keyring := testkeyring.New(2) + balances := []banktypes.Balance{ + { + Address: keyring.GetAccAddr(0).String(), + Coins: key1Balance, + }, + { + Address: keyring.GetAccAddr(1).String(), + Coins: key2Balance, + }, + } + nw := network.New( + network.WithBalances(balances...), + ) + handler := grpchandler.NewIntegrationHandler(nw) + + req, err := handler.GetAllBalances(keyring.GetAccAddr(0)) + require.NoError(t, err, "error getting balances") + require.Len(t, req.Balances, 1, "wrong number of balances") + require.Equal(t, balances[0].Coins, req.Balances, "wrong balances") + + req, err = handler.GetAllBalances(keyring.GetAccAddr(1)) + require.NoError(t, err, "error getting balances") + require.Len(t, req.Balances, 2, "wrong number of balances") + require.Equal(t, balances[1].Coins, req.Balances, "wrong balances") +} diff --git a/testutil/integration/exrp/integration/ibc.go b/testutil/integration/exrp/integration/ibc.go new file mode 100644 index 0000000..7d7a316 --- /dev/null +++ b/testutil/integration/exrp/integration/ibc.go @@ -0,0 +1,28 @@ +// Copyright Tharsis Labs Ltd.(Evmos) +// SPDX-License-Identifier:ENCL-1.0(https://github.com/evmos/evmos/blob/main/LICENSE) +package exrpintegration + +import ( + "testing" + + ibctesting "github.com/cosmos/ibc-go/v8/testing" +) + +// GetIBCChain returns a TestChain instance for the given network. +// Note: the sender accounts are not populated. Do not use this accounts to send transactions during tests. +// The keyring should be used instead. +func (n *IntegrationNetwork) GetIBCChain(t *testing.T, coord *ibctesting.Coordinator) *ibctesting.TestChain { + return &ibctesting.TestChain{ + TB: t, + Coordinator: coord, + ChainID: n.GetChainID(), + App: n.app, + CurrentHeader: n.ctx.BlockHeader(), + QueryServer: n.app.GetIBCKeeper(), + TxConfig: n.app.GetTxConfig(), + Codec: n.app.AppCodec(), + Vals: n.valSet, + NextVals: n.valSet, + Signers: n.valSigners, + } +} diff --git a/testutil/integration/exrp/integration/network.go b/testutil/integration/exrp/integration/network.go new file mode 100644 index 0000000..ba93592 --- /dev/null +++ b/testutil/integration/exrp/integration/network.go @@ -0,0 +1,345 @@ +// Copyright Tharsis Labs Ltd.(Evmos) +// SPDX-License-Identifier:ENCL-1.0(https://github.com/evmos/evmos/blob/main/LICENSE) + +package exrpintegration + +import ( + "fmt" + "math" + "math/big" + "time" + + sdkmath "cosmossdk.io/math" + + gethparams "github.com/ethereum/go-ethereum/params" + "github.com/xrplevm/node/v4/app" + "github.com/xrplevm/node/v4/types" + + abcitypes "github.com/cometbft/cometbft/abci/types" + cmtjson "github.com/cometbft/cometbft/libs/json" + cmtproto "github.com/cometbft/cometbft/proto/tendermint/types" + tmversion "github.com/cometbft/cometbft/proto/tendermint/version" + cmttypes "github.com/cometbft/cometbft/types" + "github.com/cometbft/cometbft/version" + sdktypes "github.com/cosmos/cosmos-sdk/types" + sdktestutil "github.com/cosmos/cosmos-sdk/types/module/testutil" + txtypes "github.com/cosmos/cosmos-sdk/types/tx" + consensustypes "github.com/cosmos/cosmos-sdk/x/consensus/types" + govtypes "github.com/cosmos/cosmos-sdk/x/gov/types/v1" + stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" + commonnetwork "github.com/evmos/evmos/v20/testutil/integration/common/network" + erc20types "github.com/evmos/evmos/v20/x/erc20/types" + evmtypes "github.com/evmos/evmos/v20/x/evm/types" + feemarkettypes "github.com/evmos/evmos/v20/x/feemarket/types" + infltypes "github.com/evmos/evmos/v20/x/inflation/v1/types" + vestingtypes "github.com/evmos/evmos/v20/x/vesting/types" +) + +// Network is the interface that wraps the methods to interact with integration test network. +// +// It was designed to avoid users to access module's keepers directly and force integration tests +// to be closer to the real user's behavior. +type Network interface { + commonnetwork.Network + + GetEIP155ChainID() *big.Int + GetEVMChainConfig() *gethparams.ChainConfig + + // Clients + GetERC20Client() erc20types.QueryClient + GetEvmClient() evmtypes.QueryClient + GetGovClient() govtypes.QueryClient + GetInflationClient() infltypes.QueryClient + GetFeeMarketClient() feemarkettypes.QueryClient + GetVestingClient() vestingtypes.QueryClient +} + +var _ Network = (*IntegrationNetwork)(nil) + +// IntegrationNetwork is the implementation of the Network interface for integration tests. +type IntegrationNetwork struct { + cfg Config + ctx sdktypes.Context + validators []stakingtypes.Validator + app *app.App + + // This is only needed for IBC chain testing setup + valSet *cmttypes.ValidatorSet + valSigners map[string]cmttypes.PrivValidator +} + +// New configures and initializes a new integration Network instance with +// the given configuration options. If no configuration options are provided +// it uses the default configuration. +// +// It panics if an error occurs. +func New(opts ...ConfigOption) *IntegrationNetwork { + cfg := DefaultConfig() + // Modify the default config with the given options + for _, opt := range opts { + opt(&cfg) + } + + ctx := sdktypes.Context{} + network := &IntegrationNetwork{ + cfg: cfg, + ctx: ctx, + validators: []stakingtypes.Validator{}, + } + + err := network.configureAndInitChain() + if err != nil { + panic(err) + } + return network +} + +var ( + // DefaultBondedAmount is the amount of tokens that each validator will have initially bonded + DefaultBondedAmount = sdktypes.TokensFromConsensusPower(1, types.PowerReduction) + // PrefundedAccountInitialBalance is the amount of tokens that each prefunded account has at genesis + PrefundedAccountInitialBalance, _ = sdkmath.NewIntFromString("100000000000000000000000") // 100k +) + +// configureAndInitChain initializes the network with the given configuration. +// It creates the genesis state and starts the network. +func (n *IntegrationNetwork) configureAndInitChain() error { + // Create validator set with the amount of validators specified in the config + // with the default power of 1. + valSet, valSigners := createValidatorSetAndSigners(n.cfg.amountOfValidators) + totalBonded := DefaultBondedAmount.Mul(sdkmath.NewInt(int64(n.cfg.amountOfValidators))) + + // Build staking type validators and delegations + validators, err := createStakingValidators(valSet.Validators, DefaultBondedAmount, n.cfg.operatorsAddrs) + if err != nil { + return err + } + + // Create genesis accounts and funded balances based on the config + genAccounts, fundedAccountBalances := getGenAccountsAndBalances(n.cfg, validators) + + fundedAccountBalances = addBondedModuleAccountToFundedBalances( + fundedAccountBalances, + sdktypes.NewCoin(n.cfg.denom, totalBonded), + ) + + delegations := createDelegations(validators, genAccounts[0].GetAddress()) + + // Create a new EvmosApp with the following params + evmosApp := createEvmosApp(n.cfg.chainID, n.cfg.customBaseAppOpts...) + + stakingParams := StakingCustomGenesisState{ + denom: n.cfg.denom, + validators: validators, + delegations: delegations, + } + govParams := GovCustomGenesisState{ + denom: n.cfg.denom, + } + + totalSupply := calculateTotalSupply(fundedAccountBalances) + bankParams := BankCustomGenesisState{ + totalSupply: totalSupply, + balances: fundedAccountBalances, + } + + // Get the corresponding slashing info and missed block info + // for the created validators + slashingParams, err := getValidatorsSlashingGen(validators, evmosApp.StakingKeeper) + if err != nil { + return err + } + + // Configure Genesis state + genesisState := newDefaultGenesisState( + evmosApp, + defaultGenesisParams{ + genAccounts: genAccounts, + staking: stakingParams, + bank: bankParams, + slashing: slashingParams, + gov: govParams, + }, + ) + + // modify genesis state if there're any custom genesis state + // for specific modules + genesisState, err = customizeGenesis(evmosApp, n.cfg.customGenesisState, genesisState) + if err != nil { + return err + } + + // Init chain + stateBytes, err := cmtjson.MarshalIndent(genesisState, "", " ") + if err != nil { + return err + } + + // Consensus module does not have a genesis state on the app, + // but can customize the consensus parameters of the chain on initialization + consensusParams := app.DefaultConsensusParams + if gen, ok := n.cfg.customGenesisState[consensustypes.ModuleName]; ok { + consensusParams, ok = gen.(*cmtproto.ConsensusParams) + if !ok { + return fmt.Errorf("invalid type for consensus parameters. Expected: cmtproto.ConsensusParams, got %T", gen) + } + } + + now := time.Now().UTC() + if _, err := evmosApp.InitChain( + &abcitypes.RequestInitChain{ + Time: now, + ChainId: n.cfg.chainID, + Validators: []abcitypes.ValidatorUpdate{}, + ConsensusParams: consensusParams, + AppStateBytes: stateBytes, + }, + ); err != nil { + return err + } + + header := cmtproto.Header{ + ChainID: n.cfg.chainID, + Height: evmosApp.LastBlockHeight() + 1, + AppHash: evmosApp.LastCommitID().Hash, + Time: now, + ValidatorsHash: valSet.Hash(), + NextValidatorsHash: valSet.Hash(), + ProposerAddress: valSet.Proposer.Address, + Version: tmversion.Consensus{ + Block: version.BlockProtocol, + }, + } + + req := buildFinalizeBlockReq(header, valSet.Validators) + if _, err := evmosApp.FinalizeBlock(req); err != nil { + return err + } + + // TODO - this might not be the best way to initilize the context + n.ctx = evmosApp.BaseApp.NewContextLegacy(false, header) + + // Commit genesis changes + if _, err := evmosApp.Commit(); err != nil { + return err + } + + // Set networks global parameters + var blockMaxGas uint64 = math.MaxUint64 + if consensusParams.Block != nil && consensusParams.Block.MaxGas > 0 { + blockMaxGas = uint64(consensusParams.Block.MaxGas) //nolint:gosec // G115 + } + + n.app = evmosApp + n.ctx = n.ctx.WithConsensusParams(*consensusParams) + n.ctx = n.ctx.WithBlockGasMeter(types.NewInfiniteGasMeterWithLimit(blockMaxGas)) + + n.validators = validators + n.valSet = valSet + n.valSigners = valSigners + + return nil +} + +// GetContext returns the network's context +func (n *IntegrationNetwork) GetContext() sdktypes.Context { + return n.ctx +} + +// WithIsCheckTxCtx switches the network's checkTx property +func (n *IntegrationNetwork) WithIsCheckTxCtx(isCheckTx bool) sdktypes.Context { + n.ctx = n.ctx.WithIsCheckTx(isCheckTx) + return n.ctx +} + +// GetChainID returns the network's chainID +func (n *IntegrationNetwork) GetChainID() string { + return n.cfg.chainID +} + +// GetEIP155ChainID returns the network EIp-155 chainID number +func (n *IntegrationNetwork) GetEIP155ChainID() *big.Int { + return n.cfg.eip155ChainID +} + +// GetChainConfig returns the network's chain config +func (n *IntegrationNetwork) GetEVMChainConfig() *gethparams.ChainConfig { + params := n.app.EvmKeeper.GetParams(n.ctx) + return params.ChainConfig.EthereumConfig(n.cfg.eip155ChainID) +} + +// GetDenom returns the network's denom +func (n *IntegrationNetwork) GetDenom() string { + return n.cfg.denom +} + +// GetOtherDenoms returns network's other supported denoms +func (n *IntegrationNetwork) GetOtherDenoms() []string { + return n.cfg.otherCoinDenom +} + +// GetValidators returns the network's validators +func (n *IntegrationNetwork) GetValidators() []stakingtypes.Validator { + return n.validators +} + +// GetOtherDenoms returns network's other supported denoms +func (n *IntegrationNetwork) GetEncodingConfig() sdktestutil.TestEncodingConfig { + return sdktestutil.TestEncodingConfig{ + InterfaceRegistry: n.app.InterfaceRegistry(), + Codec: n.app.AppCodec(), + TxConfig: n.app.GetTxConfig(), + Amino: n.app.LegacyAmino(), + } +} + +// BroadcastTxSync broadcasts the given txBytes to the network and returns the response. +// TODO - this should be change to gRPC +func (n *IntegrationNetwork) BroadcastTxSync(txBytes []byte) (abcitypes.ExecTxResult, error) { + header := n.ctx.BlockHeader() + // Update block header and BeginBlock + header.Height++ + header.AppHash = n.app.LastCommitID().Hash + // Calculate new block time after duration + newBlockTime := header.Time.Add(time.Second) + header.Time = newBlockTime + + req := buildFinalizeBlockReq(header, n.valSet.Validators, txBytes) + + // dont include the DecidedLastCommit because we're not committing the changes + // here, is just for broadcasting the tx. To persist the changes, use the + // NextBlock or NextBlockAfter functions + req.DecidedLastCommit = abcitypes.CommitInfo{} + + blockRes, err := n.app.BaseApp.FinalizeBlock(req) + if err != nil { + return abcitypes.ExecTxResult{}, err + } + if len(blockRes.TxResults) != 1 { + return abcitypes.ExecTxResult{}, fmt.Errorf("unexpected number of tx results. Expected 1, got: %d", len(blockRes.TxResults)) + } + return *blockRes.TxResults[0], nil +} + +// Simulate simulates the given txBytes to the network and returns the simulated response. +// TODO - this should be change to gRPC +func (n *IntegrationNetwork) Simulate(txBytes []byte) (*txtypes.SimulateResponse, error) { + gas, result, err := n.app.BaseApp.Simulate(txBytes) + if err != nil { + return nil, err + } + return &txtypes.SimulateResponse{ + GasInfo: &gas, + Result: result, + }, nil +} + +// CheckTx calls the BaseApp's CheckTx method with the given txBytes to the network and returns the response. +func (n *IntegrationNetwork) CheckTx(txBytes []byte) (*abcitypes.ResponseCheckTx, error) { + req := &abcitypes.RequestCheckTx{Tx: txBytes} + res, err := n.app.BaseApp.CheckTx(req) + if err != nil { + return nil, err + } + return res, nil +} diff --git a/testutil/integration/exrp/integration/setup.go b/testutil/integration/exrp/integration/setup.go new file mode 100644 index 0000000..d31acde --- /dev/null +++ b/testutil/integration/exrp/integration/setup.go @@ -0,0 +1,493 @@ +// Copyright Tharsis Labs Ltd.(Evmos) +// SPDX-License-Identifier:ENCL-1.0(https://github.com/evmos/evmos/blob/main/LICENSE) + +package exrpintegration + +import ( + "fmt" + "slices" + "time" + + "github.com/evmos/evmos/v20/app" + + "github.com/cosmos/cosmos-sdk/baseapp" + "github.com/cosmos/cosmos-sdk/testutil/mock" + "github.com/cosmos/gogoproto/proto" + + capabilitytypes "github.com/cosmos/ibc-go/modules/capability/types" + + "cosmossdk.io/log" + sdkmath "cosmossdk.io/math" + cmttypes "github.com/cometbft/cometbft/types" + dbm "github.com/cosmos/cosmos-db" + codectypes "github.com/cosmos/cosmos-sdk/codec/types" + cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec" + simutils "github.com/cosmos/cosmos-sdk/testutil/sims" + sdktypes "github.com/cosmos/cosmos-sdk/types" + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" + banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" + consensustypes "github.com/cosmos/cosmos-sdk/x/consensus/types" + distrtypes "github.com/cosmos/cosmos-sdk/x/distribution/types" + govtypes "github.com/cosmos/cosmos-sdk/x/gov/types" + govtypesv1 "github.com/cosmos/cosmos-sdk/x/gov/types/v1" + slashingtypes "github.com/cosmos/cosmos-sdk/x/slashing/types" + stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" + "github.com/ethereum/go-ethereum/crypto" + + evmostypes "github.com/evmos/evmos/v20/types" + epochstypes "github.com/evmos/evmos/v20/x/epochs/types" + erc20types "github.com/evmos/evmos/v20/x/erc20/types" + feemarkettypes "github.com/evmos/evmos/v20/x/feemarket/types" + infltypes "github.com/evmos/evmos/v20/x/inflation/v1/types" + + evmtypes "github.com/evmos/evmos/v20/x/evm/types" +) + +// genSetupFn is the type for the module genesis setup functions +type genSetupFn func(evmosApp *app.Evmos, genesisState evmostypes.GenesisState, customGenesis interface{}) (evmostypes.GenesisState, error) + +// defaultGenesisParams contains the params that are needed to +// setup the default genesis for the testing setup +type defaultGenesisParams struct { + genAccounts []authtypes.GenesisAccount + staking StakingCustomGenesisState + slashing SlashingCustomGenesisState + bank BankCustomGenesisState + gov GovCustomGenesisState +} + +// genesisSetupFunctions contains the available genesis setup functions +// that can be used to customize the network genesis +var genesisSetupFunctions = map[string]genSetupFn{ + evmtypes.ModuleName: genStateSetter[*evmtypes.GenesisState](evmtypes.ModuleName), + erc20types.ModuleName: genStateSetter[*erc20types.GenesisState](erc20types.ModuleName), + govtypes.ModuleName: genStateSetter[*govtypesv1.GenesisState](govtypes.ModuleName), + infltypes.ModuleName: genStateSetter[*infltypes.GenesisState](infltypes.ModuleName), + feemarkettypes.ModuleName: genStateSetter[*feemarkettypes.GenesisState](feemarkettypes.ModuleName), + distrtypes.ModuleName: genStateSetter[*distrtypes.GenesisState](distrtypes.ModuleName), + banktypes.ModuleName: setBankGenesisState, + authtypes.ModuleName: setAuthGenesisState, + epochstypes.ModuleName: genStateSetter[*epochstypes.GenesisState](epochstypes.ModuleName), + consensustypes.ModuleName: func(_ *app.Evmos, genesisState evmostypes.GenesisState, _ interface{}) (evmostypes.GenesisState, error) { + // no-op. Consensus does not have a genesis state on the application + // but the params are used on it + // (e.g. block max gas, max bytes). + // This is handled accordingly on chain and context initialization + return genesisState, nil + }, + capabilitytypes.ModuleName: genStateSetter[*capabilitytypes.GenesisState](capabilitytypes.ModuleName), +} + +// genStateSetter is a generic function to set module-specific genesis state +func genStateSetter[T proto.Message](moduleName string) genSetupFn { + return func(evmosApp *app.Evmos, genesisState evmostypes.GenesisState, customGenesis interface{}) (evmostypes.GenesisState, error) { + moduleGenesis, ok := customGenesis.(T) + if !ok { + return nil, fmt.Errorf("invalid type %T for %s module genesis state", customGenesis, moduleName) + } + + genesisState[moduleName] = evmosApp.AppCodec().MustMarshalJSON(moduleGenesis) + return genesisState, nil + } +} + +// createValidatorSetAndSigners creates validator set with the amount of validators specified +// with the default power of 1. +func createValidatorSetAndSigners(numberOfValidators int) (*cmttypes.ValidatorSet, map[string]cmttypes.PrivValidator) { + // Create validator set + tmValidators := make([]*cmttypes.Validator, 0, numberOfValidators) + signers := make(map[string]cmttypes.PrivValidator, numberOfValidators) + + for i := 0; i < numberOfValidators; i++ { + privVal := mock.NewPV() + pubKey, _ := privVal.GetPubKey() + validator := cmttypes.NewValidator(pubKey, 1) + tmValidators = append(tmValidators, validator) + signers[pubKey.Address().String()] = privVal + } + + return cmttypes.NewValidatorSet(tmValidators), signers +} + +// createGenesisAccounts returns a slice of genesis accounts from the given +// account addresses. +func createGenesisAccounts(accounts []sdktypes.AccAddress) []authtypes.GenesisAccount { + numberOfAccounts := len(accounts) + genAccounts := make([]authtypes.GenesisAccount, 0, numberOfAccounts) + emptyCodeHash := crypto.Keccak256Hash(nil).String() + for _, acc := range accounts { + baseAcc := authtypes.NewBaseAccount(acc, nil, 0, 0) + ethAcc := &evmostypes.EthAccount{ + BaseAccount: baseAcc, + CodeHash: emptyCodeHash, + } + genAccounts = append(genAccounts, ethAcc) + } + return genAccounts +} + +// getAccAddrsFromBalances returns a slice of genesis accounts from the +// given balances. +func getAccAddrsFromBalances(balances []banktypes.Balance) []sdktypes.AccAddress { + numberOfBalances := len(balances) + genAccounts := make([]sdktypes.AccAddress, 0, numberOfBalances) + for _, balance := range balances { + genAccounts = append(genAccounts, sdktypes.AccAddress(balance.Address)) + } + return genAccounts +} + +// createBalances creates balances for the given accounts and coin +func createBalances(accounts []sdktypes.AccAddress, denoms []string) []banktypes.Balance { + slices.Sort(denoms) + numberOfAccounts := len(accounts) + coins := make([]sdktypes.Coin, len(denoms)) + for i, denom := range denoms { + coins[i] = sdktypes.NewCoin(denom, PrefundedAccountInitialBalance) + } + fundedAccountBalances := make([]banktypes.Balance, 0, numberOfAccounts) + for _, acc := range accounts { + balance := banktypes.Balance{ + Address: acc.String(), + Coins: coins, + } + + fundedAccountBalances = append(fundedAccountBalances, balance) + } + return fundedAccountBalances +} + +// createEvmosApp creates an evmos app +func createEvmosApp(chainID string, customBaseAppOptions ...func(*baseapp.BaseApp)) *app.Evmos { + // Create evmos app + db := dbm.NewMemDB() + logger := log.NewNopLogger() + loadLatest := true + skipUpgradeHeights := map[int64]bool{} + homePath := app.DefaultNodeHome + invCheckPeriod := uint(5) + appOptions := simutils.NewAppOptionsWithFlagHome(app.DefaultNodeHome) + baseAppOptions := append(customBaseAppOptions, baseapp.SetChainID(chainID)) //nolint:gocritic + + return app.NewEvmos( + logger, + db, + nil, + loadLatest, + skipUpgradeHeights, + homePath, + invCheckPeriod, + appOptions, + baseAppOptions..., + ) +} + +// createStakingValidator creates a staking validator from the given tm validator and bonded +func createStakingValidator(val *cmttypes.Validator, bondedAmt sdkmath.Int, operatorAddr *sdktypes.AccAddress) (stakingtypes.Validator, error) { + pk, err := cryptocodec.FromTmPubKeyInterface(val.PubKey) //nolint:staticcheck + if err != nil { + return stakingtypes.Validator{}, err + } + + pkAny, err := codectypes.NewAnyWithValue(pk) + if err != nil { + return stakingtypes.Validator{}, err + } + + opAddr := sdktypes.ValAddress(val.Address).String() + if operatorAddr != nil { + opAddr = sdktypes.ValAddress(operatorAddr.Bytes()).String() + } + + // Default to 5% commission + commission := stakingtypes.NewCommission(sdkmath.LegacyNewDecWithPrec(5, 2), sdkmath.LegacyNewDecWithPrec(2, 1), sdkmath.LegacyNewDecWithPrec(5, 2)) + validator := stakingtypes.Validator{ + OperatorAddress: opAddr, + ConsensusPubkey: pkAny, + Jailed: false, + Status: stakingtypes.Bonded, + Tokens: bondedAmt, + DelegatorShares: sdkmath.LegacyOneDec(), + Description: stakingtypes.Description{}, + UnbondingHeight: int64(0), + UnbondingTime: time.Unix(0, 0).UTC(), + Commission: commission, + MinSelfDelegation: sdkmath.ZeroInt(), + } + return validator, nil +} + +// createStakingValidators creates staking validators from the given tm validators and bonded +// amounts +func createStakingValidators(tmValidators []*cmttypes.Validator, bondedAmt sdkmath.Int, operatorsAddresses []sdktypes.AccAddress) ([]stakingtypes.Validator, error) { + if len(operatorsAddresses) == 0 { + return createStakingValidatorsWithRandomOperator(tmValidators, bondedAmt) + } + return createStakingValidatorsWithSpecificOperator(tmValidators, bondedAmt, operatorsAddresses) +} + +// createStakingValidatorsWithRandomOperator creates staking validators with non-specified operator addresses. +func createStakingValidatorsWithRandomOperator(tmValidators []*cmttypes.Validator, bondedAmt sdkmath.Int) ([]stakingtypes.Validator, error) { + amountOfValidators := len(tmValidators) + stakingValidators := make([]stakingtypes.Validator, 0, amountOfValidators) + for _, val := range tmValidators { + validator, err := createStakingValidator(val, bondedAmt, nil) + if err != nil { + return nil, err + } + stakingValidators = append(stakingValidators, validator) + } + return stakingValidators, nil +} + +// createStakingValidatorsWithSpecificOperator creates staking validators with the given operator addresses. +func createStakingValidatorsWithSpecificOperator(tmValidators []*cmttypes.Validator, bondedAmt sdkmath.Int, operatorsAddresses []sdktypes.AccAddress) ([]stakingtypes.Validator, error) { + amountOfValidators := len(tmValidators) + stakingValidators := make([]stakingtypes.Validator, 0, amountOfValidators) + operatorsCount := len(operatorsAddresses) + if operatorsCount != amountOfValidators { + panic(fmt.Sprintf("provided %d validator operator keys but need %d!", operatorsCount, amountOfValidators)) + } + for i, val := range tmValidators { + validator, err := createStakingValidator(val, bondedAmt, &operatorsAddresses[i]) + if err != nil { + return nil, err + } + stakingValidators = append(stakingValidators, validator) + } + return stakingValidators, nil +} + +// createDelegations creates delegations for the given validators and account +func createDelegations(validators []stakingtypes.Validator, fromAccount sdktypes.AccAddress) []stakingtypes.Delegation { + amountOfValidators := len(validators) + delegations := make([]stakingtypes.Delegation, 0, amountOfValidators) + for _, val := range validators { + delegation := stakingtypes.NewDelegation(fromAccount.String(), val.OperatorAddress, sdkmath.LegacyOneDec()) + delegations = append(delegations, delegation) + } + return delegations +} + +// getValidatorsSlashingGen creates the validators signingInfos and missedBlocks +// records necessary for the slashing module genesis +func getValidatorsSlashingGen(validators []stakingtypes.Validator, sk slashingtypes.StakingKeeper) (SlashingCustomGenesisState, error) { + valCount := len(validators) + signInfo := make([]slashingtypes.SigningInfo, valCount) + missedBlocks := make([]slashingtypes.ValidatorMissedBlocks, valCount) + for i, val := range validators { + consAddrBz, err := val.GetConsAddr() + if err != nil { + return SlashingCustomGenesisState{}, err + } + consAddr, err := sk.ConsensusAddressCodec().BytesToString(consAddrBz) + if err != nil { + return SlashingCustomGenesisState{}, err + } + signInfo[i] = slashingtypes.SigningInfo{ + Address: consAddr, + ValidatorSigningInfo: slashingtypes.ValidatorSigningInfo{ + Address: consAddr, + }, + } + missedBlocks[i] = slashingtypes.ValidatorMissedBlocks{ + Address: consAddr, + } + } + return SlashingCustomGenesisState{ + signingInfo: signInfo, + missedBlocks: missedBlocks, + }, nil +} + +// StakingCustomGenesisState defines the staking genesis state +type StakingCustomGenesisState struct { + denom string + + validators []stakingtypes.Validator + delegations []stakingtypes.Delegation +} + +// setDefaultStakingGenesisState sets the default staking genesis state +func setDefaultStakingGenesisState(evmosApp *app.Evmos, genesisState evmostypes.GenesisState, overwriteParams StakingCustomGenesisState) evmostypes.GenesisState { + // Set staking params + stakingParams := stakingtypes.DefaultParams() + stakingParams.BondDenom = overwriteParams.denom + + stakingGenesis := stakingtypes.NewGenesisState( + stakingParams, + overwriteParams.validators, + overwriteParams.delegations, + ) + genesisState[stakingtypes.ModuleName] = evmosApp.AppCodec().MustMarshalJSON(stakingGenesis) + return genesisState +} + +type BankCustomGenesisState struct { + totalSupply sdktypes.Coins + balances []banktypes.Balance +} + +// setDefaultBankGenesisState sets the default bank genesis state +func setDefaultBankGenesisState(evmosApp *app.Evmos, genesisState evmostypes.GenesisState, overwriteParams BankCustomGenesisState) evmostypes.GenesisState { + bankGenesis := banktypes.NewGenesisState( + banktypes.DefaultGenesisState().Params, + overwriteParams.balances, + overwriteParams.totalSupply, + []banktypes.Metadata{}, + []banktypes.SendEnabled{}, + ) + genesisState[banktypes.ModuleName] = evmosApp.AppCodec().MustMarshalJSON(bankGenesis) + return genesisState +} + +// SlashingCustomGenesisState defines the corresponding +// validators signing info and missed blocks for the genesis state +type SlashingCustomGenesisState struct { + signingInfo []slashingtypes.SigningInfo + missedBlocks []slashingtypes.ValidatorMissedBlocks +} + +// setDefaultSlashingGenesisState sets the default slashing genesis state +func setDefaultSlashingGenesisState(evmosApp *app.Evmos, genesisState evmostypes.GenesisState, overwriteParams SlashingCustomGenesisState) evmostypes.GenesisState { + slashingGen := slashingtypes.DefaultGenesisState() + slashingGen.SigningInfos = overwriteParams.signingInfo + slashingGen.MissedBlocks = overwriteParams.missedBlocks + + genesisState[slashingtypes.ModuleName] = evmosApp.AppCodec().MustMarshalJSON(slashingGen) + return genesisState +} + +// setBankGenesisState updates the bank genesis state with custom genesis state +func setBankGenesisState(evmosApp *app.Evmos, genesisState evmostypes.GenesisState, customGenesis interface{}) (evmostypes.GenesisState, error) { + customGen, ok := customGenesis.(*banktypes.GenesisState) + if !ok { + return nil, fmt.Errorf("invalid type %T for bank module genesis state", customGenesis) + } + + bankGen := &banktypes.GenesisState{} + evmosApp.AppCodec().MustUnmarshalJSON(genesisState[banktypes.ModuleName], bankGen) + + if len(customGen.Balances) > 0 { + coins := sdktypes.NewCoins() + bankGen.Balances = append(bankGen.Balances, customGen.Balances...) + for _, b := range customGen.Balances { + coins = append(coins, b.Coins...) + } + bankGen.Supply = bankGen.Supply.Add(coins...) + } + if len(customGen.DenomMetadata) > 0 { + bankGen.DenomMetadata = append(bankGen.DenomMetadata, customGen.DenomMetadata...) + } + + if len(customGen.SendEnabled) > 0 { + bankGen.SendEnabled = append(bankGen.SendEnabled, customGen.SendEnabled...) + } + + bankGen.Params = customGen.Params + + genesisState[banktypes.ModuleName] = evmosApp.AppCodec().MustMarshalJSON(bankGen) + return genesisState, nil +} + +// calculateTotalSupply calculates the total supply from the given balances +func calculateTotalSupply(fundedAccountsBalances []banktypes.Balance) sdktypes.Coins { + totalSupply := sdktypes.NewCoins() + for _, balance := range fundedAccountsBalances { + totalSupply = totalSupply.Add(balance.Coins...) + } + return totalSupply +} + +// addBondedModuleAccountToFundedBalances adds bonded amount to bonded pool module account and include it on funded accounts +func addBondedModuleAccountToFundedBalances( + fundedAccountsBalances []banktypes.Balance, + totalBonded sdktypes.Coin, +) []banktypes.Balance { + return append(fundedAccountsBalances, banktypes.Balance{ + Address: authtypes.NewModuleAddress(stakingtypes.BondedPoolName).String(), + Coins: sdktypes.Coins{totalBonded}, + }) +} + +// setDefaultAuthGenesisState sets the default auth genesis state +func setDefaultAuthGenesisState(evmosApp *app.Evmos, genesisState evmostypes.GenesisState, genAccs []authtypes.GenesisAccount) evmostypes.GenesisState { + defaultAuthGen := authtypes.NewGenesisState(authtypes.DefaultParams(), genAccs) + genesisState[authtypes.ModuleName] = evmosApp.AppCodec().MustMarshalJSON(defaultAuthGen) + return genesisState +} + +// setAuthGenesisState updates the bank genesis state with custom genesis state +func setAuthGenesisState(evmosApp *app.Evmos, genesisState evmostypes.GenesisState, customGenesis interface{}) (evmostypes.GenesisState, error) { + customGen, ok := customGenesis.(*authtypes.GenesisState) + if !ok { + return nil, fmt.Errorf("invalid type %T for auth module genesis state", customGenesis) + } + + authGen := &authtypes.GenesisState{} + evmosApp.AppCodec().MustUnmarshalJSON(genesisState[authtypes.ModuleName], authGen) + + if len(customGen.Accounts) > 0 { + authGen.Accounts = append(authGen.Accounts, customGen.Accounts...) + } + + authGen.Params = customGen.Params + + genesisState[authtypes.ModuleName] = evmosApp.AppCodec().MustMarshalJSON(authGen) + return genesisState, nil +} + +// GovCustomGenesisState defines the gov genesis state +type GovCustomGenesisState struct { + denom string +} + +// setDefaultGovGenesisState sets the default gov genesis state +func setDefaultGovGenesisState(evmosApp *app.Evmos, genesisState evmostypes.GenesisState, overwriteParams GovCustomGenesisState) evmostypes.GenesisState { + govGen := govtypesv1.DefaultGenesisState() + updatedParams := govGen.Params + minDepositAmt := sdkmath.NewInt(1e18) + updatedParams.MinDeposit = sdktypes.NewCoins(sdktypes.NewCoin(overwriteParams.denom, minDepositAmt)) + updatedParams.ExpeditedMinDeposit = sdktypes.NewCoins(sdktypes.NewCoin(overwriteParams.denom, minDepositAmt.MulRaw(2))) + govGen.Params = updatedParams + genesisState[govtypes.ModuleName] = evmosApp.AppCodec().MustMarshalJSON(govGen) + return genesisState +} + +func setDefaultErc20GenesisState(evmosApp *app.Evmos, genesisState evmostypes.GenesisState) evmostypes.GenesisState { + erc20Gen := erc20types.DefaultGenesisState() + genesisState[erc20types.ModuleName] = evmosApp.AppCodec().MustMarshalJSON(erc20Gen) + return genesisState +} + +// defaultAuthGenesisState sets the default genesis state +// for the testing setup +func newDefaultGenesisState(evmosApp *app.Evmos, params defaultGenesisParams) evmostypes.GenesisState { + genesisState := evmosApp.DefaultGenesis() + + genesisState = setDefaultAuthGenesisState(evmosApp, genesisState, params.genAccounts) + genesisState = setDefaultStakingGenesisState(evmosApp, genesisState, params.staking) + genesisState = setDefaultBankGenesisState(evmosApp, genesisState, params.bank) + genesisState = setDefaultGovGenesisState(evmosApp, genesisState, params.gov) + genesisState = setDefaultSlashingGenesisState(evmosApp, genesisState, params.slashing) + genesisState = setDefaultErc20GenesisState(evmosApp, genesisState) + + return genesisState +} + +// customizeGenesis modifies genesis state if there're any custom genesis state +// for specific modules +func customizeGenesis(evmosApp *app.Evmos, customGen CustomGenesisState, genesisState evmostypes.GenesisState) (evmostypes.GenesisState, error) { + var err error + for mod, modGenState := range customGen { + if fn, found := genesisSetupFunctions[mod]; found { + genesisState, err = fn(evmosApp, genesisState, modGenState) + if err != nil { + return genesisState, err + } + } else { + panic(fmt.Sprintf("module %s not found in genesis setup functions", mod)) + } + } + return genesisState, err +} diff --git a/testutil/integration/exrp/integration/unit_network.go b/testutil/integration/exrp/integration/unit_network.go new file mode 100644 index 0000000..720d17e --- /dev/null +++ b/testutil/integration/exrp/integration/unit_network.go @@ -0,0 +1,56 @@ +// Copyright Tharsis Labs Ltd.(Evmos) +// SPDX-License-Identifier:ENCL-1.0(https://github.com/evmos/evmos/blob/main/LICENSE) +package exrpintegration + +import ( + sdktypes "github.com/cosmos/cosmos-sdk/types" + "github.com/ethereum/go-ethereum/common" + "github.com/evmos/evmos/v20/x/evm/statedb" + inflationtypes "github.com/evmos/evmos/v20/x/inflation/v1/types" + "github.com/xrplevm/node/v4/app" +) + +// UnitTestNetwork is the implementation of the Network interface for unit tests. +// It embeds the IntegrationNetwork struct to reuse its methods and +// makes the App public for easier testing. +type UnitTestNetwork struct { + IntegrationNetwork + App *app.App +} + +var _ Network = (*UnitTestNetwork)(nil) + +// NewUnitTestNetwork configures and initializes a new Evmos Network instance with +// the given configuration options. If no configuration options are provided +// it uses the default configuration. +// +// It panics if an error occurs. +// Note: Only uses for Unit Tests +func NewUnitTestNetwork(opts ...ConfigOption) *UnitTestNetwork { + network := New(opts...) + return &UnitTestNetwork{ + IntegrationNetwork: *network, + App: network.app, + } +} + +// GetStateDB returns the state database for the current block. +func (n *UnitTestNetwork) GetStateDB() *statedb.StateDB { + headerHash := n.GetContext().HeaderHash() + return statedb.New( + n.GetContext(), + n.App.EvmKeeper, + statedb.NewEmptyTxConfig(common.BytesToHash(headerHash)), + ) +} + +// FundAccount funds the given account with the given amount of coins. +func (n *UnitTestNetwork) FundAccount(addr sdktypes.AccAddress, coins sdktypes.Coins) error { + ctx := n.GetContext() + + if err := n.app.BankKeeper.MintCoins(ctx, inflationtypes.ModuleName, coins); err != nil { + return err + } + + return n.app.BankKeeper.SendCoinsFromModuleToAccount(ctx, inflationtypes.ModuleName, addr, coins) +} diff --git a/testutil/integration/exrp/upgrade/network.go b/testutil/integration/exrp/upgrade/network.go index 09e115d..ac243c6 100644 --- a/testutil/integration/exrp/upgrade/network.go +++ b/testutil/integration/exrp/upgrade/network.go @@ -70,12 +70,10 @@ func New(opts ...UpgradeConfigOption) *UpgradeIntegrationNetwork { validators: []stakingtypes.Validator{}, } - var err error if cfg.GenesisBytes == nil { - // err = network.configureAndInitDefaultChain() - } else { - err = network.configureAndInitChainFromGenesisBytes() + panic("GenesisBytes is nil") } + err := network.configureAndInitChain() if err != nil { panic(err) } @@ -116,7 +114,7 @@ func getValidatorsAndSignersFromCustomGenesisState( // configureAndInitChain initializes the network with the given configuration. // It creates the genesis state and starts the network. -func (n *UpgradeIntegrationNetwork) configureAndInitChainFromGenesisBytes() error { +func (n *UpgradeIntegrationNetwork) configureAndInitChain() error { // Create a new EvmosApp with the following params exrpApp := exrpcommon.CreateExrpApp(n.cfg.ChainID, n.cfg.CustomBaseAppOpts...) From 2174526292e821f455ae19e43913810086e9ed4a Mon Sep 17 00:00:00 2001 From: GuillemGarciaDev Date: Tue, 7 Jan 2025 11:05:39 +0100 Subject: [PATCH 09/60] fix(testutil): exrp common package --- testutil/integration/exrp/common/abci.go | 1 - testutil/integration/exrp/common/config.go | 22 +++--- testutil/integration/exrp/common/consensus.go | 27 +++++++ testutil/integration/exrp/common/network.go | 2 +- testutil/integration/exrp/common/setup.go | 73 +++++++------------ 5 files changed, 65 insertions(+), 60 deletions(-) create mode 100644 testutil/integration/exrp/common/consensus.go diff --git a/testutil/integration/exrp/common/abci.go b/testutil/integration/exrp/common/abci.go index b943b7e..d4f1f49 100644 --- a/testutil/integration/exrp/common/abci.go +++ b/testutil/integration/exrp/common/abci.go @@ -35,4 +35,3 @@ func GetCommitInfo(validators []*cmttypes.Validator) abcitypes.CommitInfo { } return abcitypes.CommitInfo{Votes: voteInfos} } - diff --git a/testutil/integration/exrp/common/config.go b/testutil/integration/exrp/common/config.go index e3b78a5..fbf6431 100644 --- a/testutil/integration/exrp/common/config.go +++ b/testutil/integration/exrp/common/config.go @@ -7,14 +7,12 @@ import ( sdktypes "github.com/cosmos/cosmos-sdk/types" banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" evmostypes "github.com/evmos/evmos/v20/types" - "github.com/xrplevm/node/v4/app" + "github.com/xrplevm/node/v5/app" ) const ( - accountAddressPrefix = "ethm" - bip44CoinType = 60 - - ChainID = "exrp_1440002-1" + bip44CoinType = 60 + ChainID = "exrp_1440002-1" ) // Config defines the configuration for a chain. @@ -22,7 +20,7 @@ const ( // testing needs. type Config struct { ChainID string - Eip155ChainID *big.Int + EIP155ChainID *big.Int AmountOfValidators int PreFundedAccounts []sdktypes.AccAddress Balances []banktypes.Balance @@ -39,12 +37,10 @@ type CustomGenesisState map[string]interface{} // DefaultConfig returns the default configuration for a chain. func DefaultConfig() Config { return Config{ - ChainID: ChainID, - Eip155ChainID: big.NewInt(1440002), - Balances: nil, - // MODIFIED - Denom: app.BaseDenom, - CustomGenesisState: nil, + ChainID: ChainID, + EIP155ChainID: big.NewInt(1440002), + Balances: nil, + Denom: app.BaseDenom, } } @@ -61,7 +57,7 @@ func WithChainID(chainID string) ConfigOption { } return func(cfg *Config) { cfg.ChainID = chainID - cfg.Eip155ChainID = chainIDNum + cfg.EIP155ChainID = chainIDNum } } diff --git a/testutil/integration/exrp/common/consensus.go b/testutil/integration/exrp/common/consensus.go new file mode 100644 index 0000000..1965e2d --- /dev/null +++ b/testutil/integration/exrp/common/consensus.go @@ -0,0 +1,27 @@ +package exrpcommon + +import ( + "time" + + cmtproto "github.com/cometbft/cometbft/proto/tendermint/types" + cmttypes "github.com/cometbft/cometbft/types" +) + +// DefaultConsensusParams defines the default Tendermint consensus params used in +// Evmos testing. +var DefaultConsensusParams = &cmtproto.ConsensusParams{ + Block: &cmtproto.BlockParams{ + MaxBytes: 200000, + MaxGas: -1, // no limit + }, + Evidence: &cmtproto.EvidenceParams{ + MaxAgeNumBlocks: 302400, + MaxAgeDuration: 504 * time.Hour, // 3 weeks is the max duration + MaxBytes: 10000, + }, + Validator: &cmtproto.ValidatorParams{ + PubKeyTypes: []string{ + cmttypes.ABCIPubKeyTypeEd25519, + }, + }, +} diff --git a/testutil/integration/exrp/common/network.go b/testutil/integration/exrp/common/network.go index 00b5fe4..44ef8ea 100644 --- a/testutil/integration/exrp/common/network.go +++ b/testutil/integration/exrp/common/network.go @@ -39,4 +39,4 @@ type Network interface { NextBlock() error NextBlockAfter(duration time.Duration) error NextBlockWithTxs(txBytes ...[]byte) (*abcitypes.ResponseFinalizeBlock, error) -} \ No newline at end of file +} diff --git a/testutil/integration/exrp/common/setup.go b/testutil/integration/exrp/common/setup.go index a1371db..38bf227 100644 --- a/testutil/integration/exrp/common/setup.go +++ b/testutil/integration/exrp/common/setup.go @@ -1,70 +1,54 @@ package exrpcommon import ( + "fmt" "os" "cosmossdk.io/log" "github.com/cosmos/cosmos-sdk/baseapp" sdktypes "github.com/cosmos/cosmos-sdk/types" - "fmt" - dbm "github.com/cosmos/cosmos-db" simutils "github.com/cosmos/cosmos-sdk/testutil/sims" "github.com/cosmos/gogoproto/proto" - "github.com/xrplevm/node/v4/app" - - erc20types "github.com/evmos/evmos/v20/x/erc20/types" - - evmtypes "github.com/evmos/evmos/v20/x/evm/types" - - feemarkettypes "github.com/evmos/evmos/v20/x/feemarket/types" + "github.com/xrplevm/node/v5/app" authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" - banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" - distrtypes "github.com/cosmos/cosmos-sdk/x/distribution/types" - genutiltypes "github.com/cosmos/cosmos-sdk/x/genutil/types" - govtypes "github.com/cosmos/cosmos-sdk/x/gov/types" - govtypesv1 "github.com/cosmos/cosmos-sdk/x/gov/types/v1" - capabilitytypes "github.com/cosmos/ibc-go/modules/capability/types" + erc20types "github.com/evmos/evmos/v20/x/erc20/types" + evmtypes "github.com/evmos/evmos/v20/x/evm/types" + feemarkettypes "github.com/evmos/evmos/v20/x/feemarket/types" ) -// genSetupFn is the type for the module genesis setup functions -type genSetupFn func(exrpApp *app.App, genesisState app.GenesisState, customGenesis interface{}) (app.GenesisState, error) - -var genesisSetupFunctions = map[string]genSetupFn{ - evmtypes.ModuleName: GenStateSetter[*evmtypes.GenesisState](evmtypes.ModuleName), - erc20types.ModuleName: GenStateSetter[*erc20types.GenesisState](erc20types.ModuleName), - govtypes.ModuleName: GenStateSetter[*govtypesv1.GenesisState](govtypes.ModuleName), - feemarkettypes.ModuleName: GenStateSetter[*feemarkettypes.GenesisState](feemarkettypes.ModuleName), - distrtypes.ModuleName: GenStateSetter[*distrtypes.GenesisState](distrtypes.ModuleName), - banktypes.ModuleName: GenStateSetter[*banktypes.GenesisState](banktypes.ModuleName), - authtypes.ModuleName: GenStateSetter[*authtypes.GenesisState](authtypes.ModuleName), +// GenSetupFn is the type for the module genesis setup functions +type GenSetupFn func(exrpApp *app.App, genesisState app.GenesisState, customGenesis interface{}) (app.GenesisState, error) + +var genesisSetupFunctions = map[string]GenSetupFn{ + evmtypes.ModuleName: GenStateSetter[*evmtypes.GenesisState](evmtypes.ModuleName), + erc20types.ModuleName: GenStateSetter[*erc20types.GenesisState](erc20types.ModuleName), + govtypes.ModuleName: GenStateSetter[*govtypesv1.GenesisState](govtypes.ModuleName), + feemarkettypes.ModuleName: GenStateSetter[*feemarkettypes.GenesisState](feemarkettypes.ModuleName), + distrtypes.ModuleName: GenStateSetter[*distrtypes.GenesisState](distrtypes.ModuleName), + banktypes.ModuleName: GenStateSetter[*banktypes.GenesisState](banktypes.ModuleName), + authtypes.ModuleName: GenStateSetter[*authtypes.GenesisState](authtypes.ModuleName), capabilitytypes.ModuleName: GenStateSetter[*capabilitytypes.GenesisState](capabilitytypes.ModuleName), - genutiltypes.ModuleName: GenStateSetter[*genutiltypes.GenesisState](genutiltypes.ModuleName), + genutiltypes.ModuleName: GenStateSetter[*genutiltypes.GenesisState](genutiltypes.ModuleName), } // GenStateSetter is a generic function to set module-specific genesis state -func GenStateSetter[T proto.Message](moduleName string) genSetupFn { +func GenStateSetter[T proto.Message](moduleName string) GenSetupFn { return func(exrpApp *app.App, genesisState app.GenesisState, customGenesis interface{}) (app.GenesisState, error) { - var customGen T - err := exrpApp.AppCodec().UnmarshalJSON(genesisState[moduleName], customGen) - - if err != nil { - return nil, fmt.Errorf("error unmarshalling %s module genesis state: %w", moduleName, err) + moduleGenesis, ok := customGenesis.(T) + if !ok { + return nil, fmt.Errorf("invalid type %T for %s module genesis state", customGenesis, moduleName) } - // moduleGenesis, ok := customGenesis.(T) - // if !ok { - // return nil, fmt.Errorf("invalid type %T for %s module genesis state", customGenesis, moduleName) - // } - genesisState[moduleName] = exrpApp.AppCodec().MustMarshalJSON(customGen) + genesisState[moduleName] = exrpApp.AppCodec().MustMarshalJSON(moduleGenesis) return genesisState, nil } } @@ -86,17 +70,16 @@ func CustomizeGenesis(exrpApp *app.App, customGen CustomGenesisState, genesisSta return genesisState, err } - func SetupSdkConfig() { - accountPubKeyPrefix := accountAddressPrefix + "pub" - validatorAddressPrefix := accountAddressPrefix + "valoper" - validatorPubKeyPrefix := accountAddressPrefix + "valoperpub" - consNodeAddressPrefix := accountAddressPrefix + "valcons" - consNodePubKeyPrefix := accountAddressPrefix + "valconspub" + accountPubKeyPrefix := app.AccountAddressPrefix + "pub" + validatorAddressPrefix := app.AccountAddressPrefix + "valoper" + validatorPubKeyPrefix := app.AccountAddressPrefix + "valoperpub" + consNodeAddressPrefix := app.AccountAddressPrefix + "valcons" + consNodePubKeyPrefix := app.AccountAddressPrefix + "valconspub" // Set config config := sdktypes.GetConfig() - config.SetBech32PrefixForAccount(accountAddressPrefix, accountPubKeyPrefix) + config.SetBech32PrefixForAccount(app.AccountAddressPrefix, accountPubKeyPrefix) config.SetBech32PrefixForValidator(validatorAddressPrefix, validatorPubKeyPrefix) config.SetBech32PrefixForConsensusNode(consNodeAddressPrefix, consNodePubKeyPrefix) config.SetCoinType(bip44CoinType) From 16e6cc9b99bddfbcd95488c4d1ce25e653f4670d Mon Sep 17 00:00:00 2001 From: GuillemGarciaDev Date: Tue, 7 Jan 2025 11:07:59 +0100 Subject: [PATCH 10/60] refactor(testutil): exrp upgrade config --- testutil/integration/exrp/upgrade/abci.go | 2 +- testutil/integration/exrp/upgrade/config.go | 26 ++-------- .../integration/exrp/upgrade/config_test.go | 52 ------------------- testutil/integration/exrp/upgrade/keepers.go | 1 - testutil/integration/exrp/upgrade/network.go | 10 ++-- .../integration/exrp/upgrade/unit_network.go | 7 +-- 6 files changed, 15 insertions(+), 83 deletions(-) delete mode 100644 testutil/integration/exrp/upgrade/config_test.go diff --git a/testutil/integration/exrp/upgrade/abci.go b/testutil/integration/exrp/upgrade/abci.go index 5c5805d..9d07808 100644 --- a/testutil/integration/exrp/upgrade/abci.go +++ b/testutil/integration/exrp/upgrade/abci.go @@ -7,7 +7,7 @@ import ( storetypes "cosmossdk.io/store/types" abcitypes "github.com/cometbft/cometbft/abci/types" - exrpcommon "github.com/xrplevm/node/v4/testutil/integration/exrp/common" + exrpcommon "github.com/xrplevm/node/v5/testutil/integration/exrp/common" ) // NextBlock is a private helper function that runs the EndBlocker logic, commits the changes, diff --git a/testutil/integration/exrp/upgrade/config.go b/testutil/integration/exrp/upgrade/config.go index a55c3b8..d05ddff 100644 --- a/testutil/integration/exrp/upgrade/config.go +++ b/testutil/integration/exrp/upgrade/config.go @@ -6,32 +6,16 @@ package exrpupgrade import ( "os" - exrpcommon "github.com/xrplevm/node/v4/testutil/integration/exrp/common" + exrpcommon "github.com/xrplevm/node/v5/testutil/integration/exrp/common" ) -const ( - ChainID = "exrp_1440002-1" -) - -// Config defines the configuration for a chain. -// It allows for customization of the network to adjust to -// testing needs. -type UpgradeConfig exrpcommon.Config - -func DefaultUpgradeConfig() UpgradeConfig { - return UpgradeConfig(exrpcommon.DefaultConfig()) +func DefaultUpgradeConfig() exrpcommon.Config { + return exrpcommon.DefaultConfig() } -func (cfg UpgradeConfig) Config() *exrpcommon.Config { - rootCfg := exrpcommon.Config(cfg) - return &rootCfg -} - -type UpgradeConfigOption func(cfg *UpgradeConfig) - // WithGenesisFile sets the genesis file for the network. -func WithGenesisFile(genesisFile string) UpgradeConfigOption { - return func(cfg *UpgradeConfig) { +func WithGenesisFile(genesisFile string) exrpcommon.ConfigOption { + return func(cfg *exrpcommon.Config) { genesisBytes, err := os.ReadFile(genesisFile) if err != nil { panic(err) diff --git a/testutil/integration/exrp/upgrade/config_test.go b/testutil/integration/exrp/upgrade/config_test.go deleted file mode 100644 index ecbcfe5..0000000 --- a/testutil/integration/exrp/upgrade/config_test.go +++ /dev/null @@ -1,52 +0,0 @@ -// Copyright Tharsis Labs Ltd.(Evmos) -// SPDX-License-Identifier:ENCL-1.0(https://github.com/evmos/evmos/blob/main/LICENSE) - -package exrpupgrade - -import ( - "testing" - - sdk "github.com/cosmos/cosmos-sdk/types" - banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" - grpchandler "github.com/evmos/evmos/v20/testutil/integration/evmos/grpc" - testkeyring "github.com/evmos/evmos/v20/testutil/integration/evmos/keyring" - "github.com/evmos/evmos/v20/testutil/integration/evmos/network" - "github.com/stretchr/testify/require" - "github.com/xrplevm/node/v4/app" -) - -func TestWithBalances(t *testing.T) { - // MODIFIED - key1Balance := sdk.NewCoins(sdk.NewInt64Coin(app.BaseDenom, 1e18)) - key2Balance := sdk.NewCoins( - sdk.NewInt64Coin(app.BaseDenom, 2e18), - sdk.NewInt64Coin("other", 3e18), - ) - - // Create a new network with 2 pre-funded accounts - keyring := testkeyring.New(2) - balances := []banktypes.Balance{ - { - Address: keyring.GetAccAddr(0).String(), - Coins: key1Balance, - }, - { - Address: keyring.GetAccAddr(1).String(), - Coins: key2Balance, - }, - } - nw := network.New( - network.WithBalances(balances...), - ) - handler := grpchandler.NewIntegrationHandler(nw) - - req, err := handler.GetAllBalances(keyring.GetAccAddr(0)) - require.NoError(t, err, "error getting balances") - require.Len(t, req.Balances, 1, "wrong number of balances") - require.Equal(t, balances[0].Coins, req.Balances, "wrong balances") - - req, err = handler.GetAllBalances(keyring.GetAccAddr(1)) - require.NoError(t, err, "error getting balances") - require.Len(t, req.Balances, 2, "wrong number of balances") - require.Equal(t, balances[1].Coins, req.Balances, "wrong balances") -} diff --git a/testutil/integration/exrp/upgrade/keepers.go b/testutil/integration/exrp/upgrade/keepers.go index b2a37cf..84dc8fb 100644 --- a/testutil/integration/exrp/upgrade/keepers.go +++ b/testutil/integration/exrp/upgrade/keepers.go @@ -13,7 +13,6 @@ import ( feemarketkeeper "github.com/evmos/evmos/v20/x/feemarket/keeper" ) - func (n *UpgradeIntegrationNetwork) BankKeeper() bankkeeper.Keeper { return n.app.BankKeeper } diff --git a/testutil/integration/exrp/upgrade/network.go b/testutil/integration/exrp/upgrade/network.go index ac243c6..4d31bae 100644 --- a/testutil/integration/exrp/upgrade/network.go +++ b/testutil/integration/exrp/upgrade/network.go @@ -10,8 +10,8 @@ import ( "math/big" "time" - "github.com/xrplevm/node/v4/app" - exrpcommon "github.com/xrplevm/node/v4/testutil/integration/exrp/common" + "github.com/xrplevm/node/v5/app" + exrpcommon "github.com/xrplevm/node/v5/testutil/integration/exrp/common" abcitypes "github.com/cometbft/cometbft/abci/types" ed25519 "github.com/cometbft/cometbft/crypto/ed25519" @@ -41,7 +41,7 @@ var _ Network = (*UpgradeIntegrationNetwork)(nil) // UpgradeIntegrationNetwork is the implementation of the Network interface for integration tests. type UpgradeIntegrationNetwork struct { - cfg UpgradeConfig + cfg exrpcommon.Config ctx sdktypes.Context validators []stakingtypes.Validator app *app.App @@ -56,7 +56,7 @@ type UpgradeIntegrationNetwork struct { // it uses the default configuration. // // It panics if an error occurs. -func New(opts ...UpgradeConfigOption) *UpgradeIntegrationNetwork { +func New(opts ...exrpcommon.ConfigOption) *UpgradeIntegrationNetwork { cfg := DefaultUpgradeConfig() // Modify the default config with the given options for _, opt := range opts { @@ -241,7 +241,7 @@ func (n *UpgradeIntegrationNetwork) GetChainID() string { // GetEIP155ChainID returns the network EIp-155 chainID number func (n *UpgradeIntegrationNetwork) GetEIP155ChainID() *big.Int { - return n.cfg.Eip155ChainID + return n.cfg.EIP155ChainID } // GetDenom returns the network's denom diff --git a/testutil/integration/exrp/upgrade/unit_network.go b/testutil/integration/exrp/upgrade/unit_network.go index 376a14c..0de1185 100644 --- a/testutil/integration/exrp/upgrade/unit_network.go +++ b/testutil/integration/exrp/upgrade/unit_network.go @@ -7,7 +7,8 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/evmos/evmos/v20/x/evm/statedb" inflationtypes "github.com/evmos/evmos/v20/x/inflation/v1/types" - "github.com/xrplevm/node/v4/app" + "github.com/xrplevm/node/v5/app" + exrpcommon "github.com/xrplevm/node/v5/testutil/integration/exrp/common" ) // UnitTestUpgradeNetwork is the implementation of the Network interface for unit tests. @@ -26,11 +27,11 @@ var _ Network = (*UnitTestUpgradeNetwork)(nil) // // It panics if an error occurs. // Note: Only uses for Unit Tests -func NewUnitTestUpgradeNetwork(opts ...UpgradeConfigOption) *UnitTestUpgradeNetwork { +func NewUnitTestUpgradeNetwork(opts ...exrpcommon.ConfigOption) *UnitTestUpgradeNetwork { network := New(opts...) return &UnitTestUpgradeNetwork{ UpgradeIntegrationNetwork: *network, - App: network.app, + App: network.app, } } From 2748e5622e521c600931290a615a4ff5144134b2 Mon Sep 17 00:00:00 2001 From: GuillemGarciaDev Date: Tue, 7 Jan 2025 11:08:30 +0100 Subject: [PATCH 11/60] feat(testutil): new exrp integration network testutil --- testutil/integration/exrp/integration/abci.go | 69 ++++++++++ .../integration/exrp/integration/clients.go | 105 --------------- .../integration/exrp/integration/config.go | 126 ++---------------- .../exrp/integration/config_test.go | 51 ------- .../integration/exrp/integration/keepers.go | 50 +++++++ .../integration/exrp/integration/network.go | 90 ++++++------- .../integration/exrp/integration/setup.go | 109 ++++++--------- .../exrp/integration/unit_network.go | 17 +-- 8 files changed, 222 insertions(+), 395 deletions(-) create mode 100644 testutil/integration/exrp/integration/abci.go delete mode 100644 testutil/integration/exrp/integration/clients.go delete mode 100644 testutil/integration/exrp/integration/config_test.go create mode 100644 testutil/integration/exrp/integration/keepers.go diff --git a/testutil/integration/exrp/integration/abci.go b/testutil/integration/exrp/integration/abci.go new file mode 100644 index 0000000..177aafe --- /dev/null +++ b/testutil/integration/exrp/integration/abci.go @@ -0,0 +1,69 @@ +// Copyright Tharsis Labs Ltd.(Evmos) +// SPDX-License-Identifier:ENCL-1.0(https://github.com/evmos/evmos/blob/main/LICENSE) +package exrpintegration + +import ( + "time" + + storetypes "cosmossdk.io/store/types" + abcitypes "github.com/cometbft/cometbft/abci/types" + exrpcommon "github.com/xrplevm/node/v5/testutil/integration/exrp/common" +) + +// NextBlock is a private helper function that runs the EndBlocker logic, commits the changes, +// updates the header and runs the BeginBlocker +func (n *IntegrationNetwork) NextBlock() error { + return n.NextBlockAfter(time.Second) +} + +// NextBlockAfter is a private helper function that runs the FinalizeBlock logic, updates the context and +// commits the changes to have a block time after the given duration. +func (n *IntegrationNetwork) NextBlockAfter(duration time.Duration) error { + _, err := n.finalizeBlockAndCommit(duration) + return err +} + +// NextBlockWithTxs is a helper function that runs the FinalizeBlock logic +// with the provided tx bytes, updates the context and +// commits the changes to have a block time after the given duration. +func (n *IntegrationNetwork) NextBlockWithTxs(txBytes ...[]byte) (*abcitypes.ResponseFinalizeBlock, error) { + return n.finalizeBlockAndCommit(time.Second, txBytes...) +} + +// finalizeBlockAndCommit is a private helper function that runs the FinalizeBlock logic +// with the provided txBytes, updates the context and +// commits the changes to have a block time after the given duration. +func (n *IntegrationNetwork) finalizeBlockAndCommit(duration time.Duration, txBytes ...[]byte) (*abcitypes.ResponseFinalizeBlock, error) { + header := n.ctx.BlockHeader() + // Update block header and BeginBlock + header.Height++ + header.AppHash = n.app.LastCommitID().Hash + // Calculate new block time after duration + newBlockTime := header.Time.Add(duration) + header.Time = newBlockTime + + // FinalizeBlock to run endBlock, deliverTx & beginBlock logic + req := exrpcommon.BuildFinalizeBlockReq(header, n.valSet.Validators, txBytes...) + + res, err := n.app.FinalizeBlock(req) + if err != nil { + return nil, err + } + + newCtx := n.app.BaseApp.NewContextLegacy(false, header) + + // Update context header + newCtx = newCtx.WithMinGasPrices(n.ctx.MinGasPrices()) + newCtx = newCtx.WithKVGasConfig(n.ctx.KVGasConfig()) + newCtx = newCtx.WithTransientKVGasConfig(n.ctx.TransientKVGasConfig()) + newCtx = newCtx.WithConsensusParams(n.ctx.ConsensusParams()) + // This might have to be changed with time if we want to test gas limits + newCtx = newCtx.WithBlockGasMeter(storetypes.NewInfiniteGasMeter()) + newCtx = newCtx.WithVoteInfos(req.DecidedLastCommit.GetVotes()) + n.ctx = newCtx + + // commit changes + _, err = n.app.Commit() + + return res, err +} diff --git a/testutil/integration/exrp/integration/clients.go b/testutil/integration/exrp/integration/clients.go deleted file mode 100644 index 685905c..0000000 --- a/testutil/integration/exrp/integration/clients.go +++ /dev/null @@ -1,105 +0,0 @@ -// Copyright Tharsis Labs Ltd.(Evmos) -// SPDX-License-Identifier:ENCL-1.0(https://github.com/evmos/evmos/blob/main/LICENSE) -package exrpintegration - -import ( - "github.com/cosmos/cosmos-sdk/baseapp" - sdktypes "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/types/module/testutil" - authkeeper "github.com/cosmos/cosmos-sdk/x/auth/keeper" - authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" - "github.com/cosmos/cosmos-sdk/x/authz" - banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" - distrkeeper "github.com/cosmos/cosmos-sdk/x/distribution/keeper" - distrtypes "github.com/cosmos/cosmos-sdk/x/distribution/types" - govkeeper "github.com/cosmos/cosmos-sdk/x/gov/keeper" - govtypes "github.com/cosmos/cosmos-sdk/x/gov/types/v1" - stakingkeeper "github.com/cosmos/cosmos-sdk/x/staking/keeper" - stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" - epochstypes "github.com/evmos/evmos/v20/x/epochs/types" - erc20types "github.com/evmos/evmos/v20/x/erc20/types" - evmtypes "github.com/evmos/evmos/v20/x/evm/types" - feemarkettypes "github.com/evmos/evmos/v20/x/feemarket/types" - infltypes "github.com/evmos/evmos/v20/x/inflation/v1/types" - vestingtypes "github.com/evmos/evmos/v20/x/vesting/types" -) - -func getQueryHelper(ctx sdktypes.Context, encCfg testutil.TestEncodingConfig) *baseapp.QueryServiceTestHelper { - interfaceRegistry := encCfg.InterfaceRegistry - // This is needed so that state changes are not committed in precompiles - // simulations. - cacheCtx, _ := ctx.CacheContext() - return baseapp.NewQueryServerTestHelper(cacheCtx, interfaceRegistry) -} - -func (n *IntegrationNetwork) GetERC20Client() erc20types.QueryClient { - queryHelper := getQueryHelper(n.GetContext(), n.GetEncodingConfig()) - erc20types.RegisterQueryServer(queryHelper, n.app.Erc20Keeper) - return erc20types.NewQueryClient(queryHelper) -} - -func (n *IntegrationNetwork) GetEvmClient() evmtypes.QueryClient { - queryHelper := getQueryHelper(n.GetContext(), n.GetEncodingConfig()) - evmtypes.RegisterQueryServer(queryHelper, n.app.EvmKeeper) - return evmtypes.NewQueryClient(queryHelper) -} - -func (n *IntegrationNetwork) GetGovClient() govtypes.QueryClient { - queryHelper := getQueryHelper(n.GetContext(), n.GetEncodingConfig()) - govtypes.RegisterQueryServer(queryHelper, govkeeper.NewQueryServer(&n.app.GovKeeper)) - return govtypes.NewQueryClient(queryHelper) -} - -func (n *IntegrationNetwork) GetBankClient() banktypes.QueryClient { - queryHelper := getQueryHelper(n.GetContext(), n.GetEncodingConfig()) - banktypes.RegisterQueryServer(queryHelper, n.app.BankKeeper) - return banktypes.NewQueryClient(queryHelper) -} - -func (n *IntegrationNetwork) GetFeeMarketClient() feemarkettypes.QueryClient { - queryHelper := getQueryHelper(n.GetContext(), n.GetEncodingConfig()) - feemarkettypes.RegisterQueryServer(queryHelper, n.app.FeeMarketKeeper) - return feemarkettypes.NewQueryClient(queryHelper) -} - -func (n *IntegrationNetwork) GetInflationClient() infltypes.QueryClient { - queryHelper := getQueryHelper(n.GetContext(), n.GetEncodingConfig()) - infltypes.RegisterQueryServer(queryHelper, n.app.InflationKeeper) - return infltypes.NewQueryClient(queryHelper) -} - -func (n *IntegrationNetwork) GetAuthClient() authtypes.QueryClient { - queryHelper := getQueryHelper(n.GetContext(), n.GetEncodingConfig()) - authtypes.RegisterQueryServer(queryHelper, authkeeper.NewQueryServer(n.app.AccountKeeper)) - return authtypes.NewQueryClient(queryHelper) -} - -func (n *IntegrationNetwork) GetAuthzClient() authz.QueryClient { - queryHelper := getQueryHelper(n.GetContext(), n.GetEncodingConfig()) - authz.RegisterQueryServer(queryHelper, n.app.AuthzKeeper) - return authz.NewQueryClient(queryHelper) -} - -func (n *IntegrationNetwork) GetStakingClient() stakingtypes.QueryClient { - queryHelper := getQueryHelper(n.GetContext(), n.GetEncodingConfig()) - stakingtypes.RegisterQueryServer(queryHelper, stakingkeeper.Querier{Keeper: n.app.StakingKeeper.Keeper}) - return stakingtypes.NewQueryClient(queryHelper) -} - -func (n *IntegrationNetwork) GetDistrClient() distrtypes.QueryClient { - queryHelper := getQueryHelper(n.GetContext(), n.GetEncodingConfig()) - distrtypes.RegisterQueryServer(queryHelper, distrkeeper.Querier{Keeper: n.app.DistrKeeper}) - return distrtypes.NewQueryClient(queryHelper) -} - -func (n *IntegrationNetwork) GetEpochsClient() epochstypes.QueryClient { - queryHelper := getQueryHelper(n.GetContext(), n.GetEncodingConfig()) - epochstypes.RegisterQueryServer(queryHelper, n.app.EpochsKeeper) - return epochstypes.NewQueryClient(queryHelper) -} - -func (n *IntegrationNetwork) GetVestingClient() vestingtypes.QueryClient { - queryHelper := getQueryHelper(n.GetContext(), n.GetEncodingConfig()) - vestingtypes.RegisterQueryServer(queryHelper, n.app.VestingKeeper) - return vestingtypes.NewQueryClient(queryHelper) -} diff --git a/testutil/integration/exrp/integration/config.go b/testutil/integration/exrp/integration/config.go index ccfd9d5..95d79e0 100644 --- a/testutil/integration/exrp/integration/config.go +++ b/testutil/integration/exrp/integration/config.go @@ -5,64 +5,36 @@ package exrpintegration import ( "fmt" - "math/big" - "github.com/cosmos/cosmos-sdk/baseapp" sdktypes "github.com/cosmos/cosmos-sdk/types" authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" testtx "github.com/evmos/evmos/v20/testutil/tx" - evmostypes "github.com/evmos/evmos/v20/types" - "github.com/evmos/evmos/v20/utils" + exrpcommon "github.com/xrplevm/node/v5/testutil/integration/exrp/common" ) -// Config defines the configuration for a chain. -// It allows for customization of the network to adjust to -// testing needs. -type Config struct { - chainID string - eip155ChainID *big.Int - amountOfValidators int - preFundedAccounts []sdktypes.AccAddress - balances []banktypes.Balance - denom string - customGenesisState CustomGenesisState - otherCoinDenom []string - operatorsAddrs []sdktypes.AccAddress - customBaseAppOpts []func(*baseapp.BaseApp) -} - -type CustomGenesisState map[string]interface{} - -// DefaultConfig returns the default configuration for a chain. -func DefaultConfig() Config { +// DefaultIntegrationConfig returns the default configuration for a chain. +func DefaultIntegrationConfig() exrpcommon.Config { account, _ := testtx.NewAccAddressAndKey() - return Config{ - chainID: utils.MainnetChainID + "-1", - eip155ChainID: big.NewInt(9001), - amountOfValidators: 3, - // Only one account besides the validators - preFundedAccounts: []sdktypes.AccAddress{account}, - // NOTE: Per default, the balances are left empty, and the pre-funded accounts are used. - balances: nil, - denom: utils.BaseDenom, - customGenesisState: nil, - } + config := exrpcommon.DefaultConfig() + config.AmountOfValidators = 3 + config.PreFundedAccounts = []sdktypes.AccAddress{account} + return config } // getGenAccountsAndBalances takes the network configuration and returns the used // genesis accounts and balances. // // NOTE: If the balances are set, the pre-funded accounts are ignored. -func getGenAccountsAndBalances(cfg Config, validators []stakingtypes.Validator) (genAccounts []authtypes.GenesisAccount, balances []banktypes.Balance) { - if len(cfg.balances) > 0 { - balances = cfg.balances +func getGenAccountsAndBalances(cfg exrpcommon.Config, validators []stakingtypes.Validator) (genAccounts []authtypes.GenesisAccount, balances []banktypes.Balance) { + if len(cfg.Balances) > 0 { + balances = cfg.Balances accounts := getAccAddrsFromBalances(balances) genAccounts = createGenesisAccounts(accounts) } else { - genAccounts = createGenesisAccounts(cfg.preFundedAccounts) - balances = createBalances(cfg.preFundedAccounts, append(cfg.otherCoinDenom, cfg.denom)) + genAccounts = createGenesisAccounts(cfg.PreFundedAccounts) + balances = createBalances(cfg.PreFundedAccounts, append(cfg.OtherCoinDenom, cfg.Denom)) } // append validators to genesis accounts and balances @@ -78,77 +50,3 @@ func getGenAccountsAndBalances(cfg Config, validators []stakingtypes.Validator) return } - -// ConfigOption defines a function that can modify the NetworkConfig. -// The purpose of this is to force to be declarative when the default configuration -// requires to be changed. -type ConfigOption func(*Config) - -// WithChainID sets a custom chainID for the network. It panics if the chainID is invalid. -func WithChainID(chainID string) ConfigOption { - chainIDNum, err := evmostypes.ParseChainID(chainID) - if err != nil { - panic(err) - } - return func(cfg *Config) { - cfg.chainID = chainID - cfg.eip155ChainID = chainIDNum - } -} - -// WithAmountOfValidators sets the amount of validators for the network. -func WithAmountOfValidators(amount int) ConfigOption { - return func(cfg *Config) { - cfg.amountOfValidators = amount - } -} - -// WithPreFundedAccounts sets the pre-funded accounts for the network. -func WithPreFundedAccounts(accounts ...sdktypes.AccAddress) ConfigOption { - return func(cfg *Config) { - cfg.preFundedAccounts = accounts - } -} - -// WithBalances sets the specific balances for the pre-funded accounts, that -// are being set up for the network. -func WithBalances(balances ...banktypes.Balance) ConfigOption { - return func(cfg *Config) { - cfg.balances = append(cfg.balances, balances...) - } -} - -// WithDenom sets the denom for the network. -func WithDenom(denom string) ConfigOption { - return func(cfg *Config) { - cfg.denom = denom - } -} - -// WithCustomGenesis sets the custom genesis of the network for specific modules. -func WithCustomGenesis(customGenesis CustomGenesisState) ConfigOption { - return func(cfg *Config) { - cfg.customGenesisState = customGenesis - } -} - -// WithOtherDenoms sets other possible coin denominations for the network. -func WithOtherDenoms(otherDenoms []string) ConfigOption { - return func(cfg *Config) { - cfg.otherCoinDenom = otherDenoms - } -} - -// WithValidatorOperators overwrites the used operator address for the network instantiation. -func WithValidatorOperators(keys []sdktypes.AccAddress) ConfigOption { - return func(cfg *Config) { - cfg.operatorsAddrs = keys - } -} - -// WithCustomBaseAppOpts sets custom base app options for the network. -func WithCustomBaseAppOpts(opts ...func(*baseapp.BaseApp)) ConfigOption { - return func(cfg *Config) { - cfg.customBaseAppOpts = opts - } -} diff --git a/testutil/integration/exrp/integration/config_test.go b/testutil/integration/exrp/integration/config_test.go deleted file mode 100644 index fddf107..0000000 --- a/testutil/integration/exrp/integration/config_test.go +++ /dev/null @@ -1,51 +0,0 @@ -// Copyright Tharsis Labs Ltd.(Evmos) -// SPDX-License-Identifier:ENCL-1.0(https://github.com/evmos/evmos/blob/main/LICENSE) - -package exrpintegration - -import ( - "testing" - - sdk "github.com/cosmos/cosmos-sdk/types" - banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" - grpchandler "github.com/evmos/evmos/v20/testutil/integration/evmos/grpc" - testkeyring "github.com/evmos/evmos/v20/testutil/integration/evmos/keyring" - "github.com/evmos/evmos/v20/testutil/integration/evmos/network" - "github.com/evmos/evmos/v20/utils" - "github.com/stretchr/testify/require" -) - -func TestWithBalances(t *testing.T) { - key1Balance := sdk.NewCoins(sdk.NewInt64Coin(utils.BaseDenom, 1e18)) - key2Balance := sdk.NewCoins( - sdk.NewInt64Coin(utils.BaseDenom, 2e18), - sdk.NewInt64Coin("other", 3e18), - ) - - // Create a new network with 2 pre-funded accounts - keyring := testkeyring.New(2) - balances := []banktypes.Balance{ - { - Address: keyring.GetAccAddr(0).String(), - Coins: key1Balance, - }, - { - Address: keyring.GetAccAddr(1).String(), - Coins: key2Balance, - }, - } - nw := network.New( - network.WithBalances(balances...), - ) - handler := grpchandler.NewIntegrationHandler(nw) - - req, err := handler.GetAllBalances(keyring.GetAccAddr(0)) - require.NoError(t, err, "error getting balances") - require.Len(t, req.Balances, 1, "wrong number of balances") - require.Equal(t, balances[0].Coins, req.Balances, "wrong balances") - - req, err = handler.GetAllBalances(keyring.GetAccAddr(1)) - require.NoError(t, err, "error getting balances") - require.Len(t, req.Balances, 2, "wrong number of balances") - require.Equal(t, balances[1].Coins, req.Balances, "wrong balances") -} diff --git a/testutil/integration/exrp/integration/keepers.go b/testutil/integration/exrp/integration/keepers.go new file mode 100644 index 0000000..61e258a --- /dev/null +++ b/testutil/integration/exrp/integration/keepers.go @@ -0,0 +1,50 @@ +package exrpintegration + +import ( + authkeeper "github.com/cosmos/cosmos-sdk/x/auth/keeper" + authzkeeper "github.com/cosmos/cosmos-sdk/x/authz/keeper" + bankkeeper "github.com/cosmos/cosmos-sdk/x/bank/keeper" + distrkeeper "github.com/cosmos/cosmos-sdk/x/distribution/keeper" + govkeeper "github.com/cosmos/cosmos-sdk/x/gov/keeper" + stakingkeeper "github.com/cosmos/cosmos-sdk/x/staking/keeper" + + erc20keeper "github.com/evmos/evmos/v20/x/erc20/keeper" + evmkeeper "github.com/evmos/evmos/v20/x/evm/keeper" + feemarketkeeper "github.com/evmos/evmos/v20/x/feemarket/keeper" +) + +func (n *IntegrationNetwork) BankKeeper() bankkeeper.Keeper { + return n.app.BankKeeper +} + +func (n *IntegrationNetwork) ERC20Keeper() erc20keeper.Keeper { + return n.app.Erc20Keeper +} + +func (n *IntegrationNetwork) EvmKeeper() evmkeeper.Keeper { + return *n.app.EvmKeeper +} + +func (n *IntegrationNetwork) GovKeeper() *govkeeper.Keeper { + return &n.app.GovKeeper +} + +func (n *IntegrationNetwork) StakingKeeper() *stakingkeeper.Keeper { + return n.app.StakingKeeper.Keeper +} + +func (n *IntegrationNetwork) DistrKeeper() distrkeeper.Keeper { + return n.app.DistrKeeper +} + +func (n *IntegrationNetwork) AccountKeeper() authkeeper.AccountKeeper { + return n.app.AccountKeeper +} + +func (n *IntegrationNetwork) AuthzKeeper() authzkeeper.Keeper { + return n.app.AuthzKeeper +} + +func (n *IntegrationNetwork) FeeMarketKeeper() feemarketkeeper.Keeper { + return n.app.FeeMarketKeeper +} diff --git a/testutil/integration/exrp/integration/network.go b/testutil/integration/exrp/integration/network.go index ba93592..56d7480 100644 --- a/testutil/integration/exrp/integration/network.go +++ b/testutil/integration/exrp/integration/network.go @@ -12,8 +12,9 @@ import ( sdkmath "cosmossdk.io/math" gethparams "github.com/ethereum/go-ethereum/params" - "github.com/xrplevm/node/v4/app" - "github.com/xrplevm/node/v4/types" + "github.com/xrplevm/node/v5/app" + + "github.com/evmos/evmos/v20/types" abcitypes "github.com/cometbft/cometbft/abci/types" cmtjson "github.com/cometbft/cometbft/libs/json" @@ -25,14 +26,8 @@ import ( sdktestutil "github.com/cosmos/cosmos-sdk/types/module/testutil" txtypes "github.com/cosmos/cosmos-sdk/types/tx" consensustypes "github.com/cosmos/cosmos-sdk/x/consensus/types" - govtypes "github.com/cosmos/cosmos-sdk/x/gov/types/v1" stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" - commonnetwork "github.com/evmos/evmos/v20/testutil/integration/common/network" - erc20types "github.com/evmos/evmos/v20/x/erc20/types" - evmtypes "github.com/evmos/evmos/v20/x/evm/types" - feemarkettypes "github.com/evmos/evmos/v20/x/feemarket/types" - infltypes "github.com/evmos/evmos/v20/x/inflation/v1/types" - vestingtypes "github.com/evmos/evmos/v20/x/vesting/types" + exrpcommon "github.com/xrplevm/node/v5/testutil/integration/exrp/common" ) // Network is the interface that wraps the methods to interact with integration test network. @@ -40,25 +35,16 @@ import ( // It was designed to avoid users to access module's keepers directly and force integration tests // to be closer to the real user's behavior. type Network interface { - commonnetwork.Network + exrpcommon.Network GetEIP155ChainID() *big.Int - GetEVMChainConfig() *gethparams.ChainConfig - - // Clients - GetERC20Client() erc20types.QueryClient - GetEvmClient() evmtypes.QueryClient - GetGovClient() govtypes.QueryClient - GetInflationClient() infltypes.QueryClient - GetFeeMarketClient() feemarkettypes.QueryClient - GetVestingClient() vestingtypes.QueryClient } var _ Network = (*IntegrationNetwork)(nil) // IntegrationNetwork is the implementation of the Network interface for integration tests. type IntegrationNetwork struct { - cfg Config + cfg exrpcommon.Config ctx sdktypes.Context validators []stakingtypes.Validator app *app.App @@ -73,8 +59,8 @@ type IntegrationNetwork struct { // it uses the default configuration. // // It panics if an error occurs. -func New(opts ...ConfigOption) *IntegrationNetwork { - cfg := DefaultConfig() +func New(opts ...exrpcommon.ConfigOption) *IntegrationNetwork { + cfg := DefaultIntegrationConfig() // Modify the default config with the given options for _, opt := range opts { opt(&cfg) @@ -96,7 +82,7 @@ func New(opts ...ConfigOption) *IntegrationNetwork { var ( // DefaultBondedAmount is the amount of tokens that each validator will have initially bonded - DefaultBondedAmount = sdktypes.TokensFromConsensusPower(1, types.PowerReduction) + DefaultBondedAmount = sdktypes.TokensFromConsensusPower(1, sdktypes.DefaultPowerReduction) // PrefundedAccountInitialBalance is the amount of tokens that each prefunded account has at genesis PrefundedAccountInitialBalance, _ = sdkmath.NewIntFromString("100000000000000000000000") // 100k ) @@ -106,11 +92,11 @@ var ( func (n *IntegrationNetwork) configureAndInitChain() error { // Create validator set with the amount of validators specified in the config // with the default power of 1. - valSet, valSigners := createValidatorSetAndSigners(n.cfg.amountOfValidators) - totalBonded := DefaultBondedAmount.Mul(sdkmath.NewInt(int64(n.cfg.amountOfValidators))) + valSet, valSigners := createValidatorSetAndSigners(n.cfg.AmountOfValidators) + totalBonded := DefaultBondedAmount.Mul(sdkmath.NewInt(int64(n.cfg.AmountOfValidators))) // Build staking type validators and delegations - validators, err := createStakingValidators(valSet.Validators, DefaultBondedAmount, n.cfg.operatorsAddrs) + validators, err := createStakingValidators(valSet.Validators, DefaultBondedAmount, n.cfg.OperatorsAddrs) if err != nil { return err } @@ -120,21 +106,21 @@ func (n *IntegrationNetwork) configureAndInitChain() error { fundedAccountBalances = addBondedModuleAccountToFundedBalances( fundedAccountBalances, - sdktypes.NewCoin(n.cfg.denom, totalBonded), + sdktypes.NewCoin(n.cfg.Denom, totalBonded), ) - delegations := createDelegations(validators, genAccounts[0].GetAddress()) + delegations := createDelegations(validators) // Create a new EvmosApp with the following params - evmosApp := createEvmosApp(n.cfg.chainID, n.cfg.customBaseAppOpts...) + exrpApp := exrpcommon.CreateExrpApp(n.cfg.ChainID, n.cfg.CustomBaseAppOpts...) stakingParams := StakingCustomGenesisState{ - denom: n.cfg.denom, + denom: n.cfg.Denom, validators: validators, delegations: delegations, } govParams := GovCustomGenesisState{ - denom: n.cfg.denom, + denom: n.cfg.Denom, } totalSupply := calculateTotalSupply(fundedAccountBalances) @@ -145,14 +131,14 @@ func (n *IntegrationNetwork) configureAndInitChain() error { // Get the corresponding slashing info and missed block info // for the created validators - slashingParams, err := getValidatorsSlashingGen(validators, evmosApp.StakingKeeper) + slashingParams, err := getValidatorsSlashingGen(validators, exrpApp.StakingKeeper) if err != nil { return err } // Configure Genesis state genesisState := newDefaultGenesisState( - evmosApp, + exrpApp, defaultGenesisParams{ genAccounts: genAccounts, staking: stakingParams, @@ -164,7 +150,7 @@ func (n *IntegrationNetwork) configureAndInitChain() error { // modify genesis state if there're any custom genesis state // for specific modules - genesisState, err = customizeGenesis(evmosApp, n.cfg.customGenesisState, genesisState) + genesisState, err = customizeGenesis(exrpApp, n.cfg.CustomGenesisState, genesisState) if err != nil { return err } @@ -177,8 +163,8 @@ func (n *IntegrationNetwork) configureAndInitChain() error { // Consensus module does not have a genesis state on the app, // but can customize the consensus parameters of the chain on initialization - consensusParams := app.DefaultConsensusParams - if gen, ok := n.cfg.customGenesisState[consensustypes.ModuleName]; ok { + consensusParams := exrpcommon.DefaultConsensusParams + if gen, ok := n.cfg.CustomGenesisState[consensustypes.ModuleName]; ok { consensusParams, ok = gen.(*cmtproto.ConsensusParams) if !ok { return fmt.Errorf("invalid type for consensus parameters. Expected: cmtproto.ConsensusParams, got %T", gen) @@ -186,10 +172,10 @@ func (n *IntegrationNetwork) configureAndInitChain() error { } now := time.Now().UTC() - if _, err := evmosApp.InitChain( + if _, err := exrpApp.InitChain( &abcitypes.RequestInitChain{ Time: now, - ChainId: n.cfg.chainID, + ChainId: n.cfg.ChainID, Validators: []abcitypes.ValidatorUpdate{}, ConsensusParams: consensusParams, AppStateBytes: stateBytes, @@ -199,9 +185,9 @@ func (n *IntegrationNetwork) configureAndInitChain() error { } header := cmtproto.Header{ - ChainID: n.cfg.chainID, - Height: evmosApp.LastBlockHeight() + 1, - AppHash: evmosApp.LastCommitID().Hash, + ChainID: n.cfg.ChainID, + Height: exrpApp.LastBlockHeight() + 1, + AppHash: exrpApp.LastCommitID().Hash, Time: now, ValidatorsHash: valSet.Hash(), NextValidatorsHash: valSet.Hash(), @@ -211,16 +197,16 @@ func (n *IntegrationNetwork) configureAndInitChain() error { }, } - req := buildFinalizeBlockReq(header, valSet.Validators) - if _, err := evmosApp.FinalizeBlock(req); err != nil { + req := exrpcommon.BuildFinalizeBlockReq(header, valSet.Validators) + if _, err := exrpApp.FinalizeBlock(req); err != nil { return err } // TODO - this might not be the best way to initilize the context - n.ctx = evmosApp.BaseApp.NewContextLegacy(false, header) + n.ctx = exrpApp.BaseApp.NewContextLegacy(false, header) // Commit genesis changes - if _, err := evmosApp.Commit(); err != nil { + if _, err := exrpApp.Commit(); err != nil { return err } @@ -230,7 +216,7 @@ func (n *IntegrationNetwork) configureAndInitChain() error { blockMaxGas = uint64(consensusParams.Block.MaxGas) //nolint:gosec // G115 } - n.app = evmosApp + n.app = exrpApp n.ctx = n.ctx.WithConsensusParams(*consensusParams) n.ctx = n.ctx.WithBlockGasMeter(types.NewInfiniteGasMeterWithLimit(blockMaxGas)) @@ -254,28 +240,28 @@ func (n *IntegrationNetwork) WithIsCheckTxCtx(isCheckTx bool) sdktypes.Context { // GetChainID returns the network's chainID func (n *IntegrationNetwork) GetChainID() string { - return n.cfg.chainID + return n.cfg.ChainID } // GetEIP155ChainID returns the network EIp-155 chainID number func (n *IntegrationNetwork) GetEIP155ChainID() *big.Int { - return n.cfg.eip155ChainID + return n.cfg.EIP155ChainID } // GetChainConfig returns the network's chain config func (n *IntegrationNetwork) GetEVMChainConfig() *gethparams.ChainConfig { params := n.app.EvmKeeper.GetParams(n.ctx) - return params.ChainConfig.EthereumConfig(n.cfg.eip155ChainID) + return params.ChainConfig.EthereumConfig(n.cfg.EIP155ChainID) } // GetDenom returns the network's denom func (n *IntegrationNetwork) GetDenom() string { - return n.cfg.denom + return n.cfg.Denom } // GetOtherDenoms returns network's other supported denoms func (n *IntegrationNetwork) GetOtherDenoms() []string { - return n.cfg.otherCoinDenom + return n.cfg.OtherCoinDenom } // GetValidators returns the network's validators @@ -304,7 +290,7 @@ func (n *IntegrationNetwork) BroadcastTxSync(txBytes []byte) (abcitypes.ExecTxRe newBlockTime := header.Time.Add(time.Second) header.Time = newBlockTime - req := buildFinalizeBlockReq(header, n.valSet.Validators, txBytes) + req := exrpcommon.BuildFinalizeBlockReq(header, n.valSet.Validators, txBytes) // dont include the DecidedLastCommit because we're not committing the changes // here, is just for broadcasting the tx. To persist the changes, use the diff --git a/testutil/integration/exrp/integration/setup.go b/testutil/integration/exrp/integration/setup.go index d31acde..c684b0f 100644 --- a/testutil/integration/exrp/integration/setup.go +++ b/testutil/integration/exrp/integration/setup.go @@ -8,21 +8,15 @@ import ( "slices" "time" - "github.com/evmos/evmos/v20/app" - - "github.com/cosmos/cosmos-sdk/baseapp" "github.com/cosmos/cosmos-sdk/testutil/mock" "github.com/cosmos/gogoproto/proto" capabilitytypes "github.com/cosmos/ibc-go/modules/capability/types" - "cosmossdk.io/log" sdkmath "cosmossdk.io/math" cmttypes "github.com/cometbft/cometbft/types" - dbm "github.com/cosmos/cosmos-db" codectypes "github.com/cosmos/cosmos-sdk/codec/types" cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec" - simutils "github.com/cosmos/cosmos-sdk/testutil/sims" sdktypes "github.com/cosmos/cosmos-sdk/types" authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" @@ -41,10 +35,13 @@ import ( infltypes "github.com/evmos/evmos/v20/x/inflation/v1/types" evmtypes "github.com/evmos/evmos/v20/x/evm/types" + exrpcommon "github.com/xrplevm/node/v5/testutil/integration/exrp/common" + + "github.com/xrplevm/node/v5/app" ) // genSetupFn is the type for the module genesis setup functions -type genSetupFn func(evmosApp *app.Evmos, genesisState evmostypes.GenesisState, customGenesis interface{}) (evmostypes.GenesisState, error) +type genSetupFn func(app *app.App, genesisState evmostypes.GenesisState, customGenesis interface{}) (evmostypes.GenesisState, error) // defaultGenesisParams contains the params that are needed to // setup the default genesis for the testing setup @@ -68,7 +65,7 @@ var genesisSetupFunctions = map[string]genSetupFn{ banktypes.ModuleName: setBankGenesisState, authtypes.ModuleName: setAuthGenesisState, epochstypes.ModuleName: genStateSetter[*epochstypes.GenesisState](epochstypes.ModuleName), - consensustypes.ModuleName: func(_ *app.Evmos, genesisState evmostypes.GenesisState, _ interface{}) (evmostypes.GenesisState, error) { + consensustypes.ModuleName: func(_ *app.App, genesisState evmostypes.GenesisState, _ interface{}) (evmostypes.GenesisState, error) { // no-op. Consensus does not have a genesis state on the application // but the params are used on it // (e.g. block max gas, max bytes). @@ -80,13 +77,13 @@ var genesisSetupFunctions = map[string]genSetupFn{ // genStateSetter is a generic function to set module-specific genesis state func genStateSetter[T proto.Message](moduleName string) genSetupFn { - return func(evmosApp *app.Evmos, genesisState evmostypes.GenesisState, customGenesis interface{}) (evmostypes.GenesisState, error) { + return func(app *app.App, genesisState evmostypes.GenesisState, customGenesis interface{}) (evmostypes.GenesisState, error) { moduleGenesis, ok := customGenesis.(T) if !ok { return nil, fmt.Errorf("invalid type %T for %s module genesis state", customGenesis, moduleName) } - genesisState[moduleName] = evmosApp.AppCodec().MustMarshalJSON(moduleGenesis) + genesisState[moduleName] = app.AppCodec().MustMarshalJSON(moduleGenesis) return genesisState, nil } } @@ -157,31 +154,6 @@ func createBalances(accounts []sdktypes.AccAddress, denoms []string) []banktypes return fundedAccountBalances } -// createEvmosApp creates an evmos app -func createEvmosApp(chainID string, customBaseAppOptions ...func(*baseapp.BaseApp)) *app.Evmos { - // Create evmos app - db := dbm.NewMemDB() - logger := log.NewNopLogger() - loadLatest := true - skipUpgradeHeights := map[int64]bool{} - homePath := app.DefaultNodeHome - invCheckPeriod := uint(5) - appOptions := simutils.NewAppOptionsWithFlagHome(app.DefaultNodeHome) - baseAppOptions := append(customBaseAppOptions, baseapp.SetChainID(chainID)) //nolint:gocritic - - return app.NewEvmos( - logger, - db, - nil, - loadLatest, - skipUpgradeHeights, - homePath, - invCheckPeriod, - appOptions, - baseAppOptions..., - ) -} - // createStakingValidator creates a staking validator from the given tm validator and bonded func createStakingValidator(val *cmttypes.Validator, bondedAmt sdkmath.Int, operatorAddr *sdktypes.AccAddress) (stakingtypes.Validator, error) { pk, err := cryptocodec.FromTmPubKeyInterface(val.PubKey) //nolint:staticcheck @@ -259,11 +231,15 @@ func createStakingValidatorsWithSpecificOperator(tmValidators []*cmttypes.Valida } // createDelegations creates delegations for the given validators and account -func createDelegations(validators []stakingtypes.Validator, fromAccount sdktypes.AccAddress) []stakingtypes.Delegation { +func createDelegations(validators []stakingtypes.Validator) []stakingtypes.Delegation { amountOfValidators := len(validators) delegations := make([]stakingtypes.Delegation, 0, amountOfValidators) for _, val := range validators { - delegation := stakingtypes.NewDelegation(fromAccount.String(), val.OperatorAddress, sdkmath.LegacyOneDec()) + valAddr, err := sdktypes.ValAddressFromBech32(val.OperatorAddress) + if err != nil { + panic(err) + } + delegation := stakingtypes.NewDelegation(sdktypes.AccAddress(valAddr).String(), val.OperatorAddress, sdkmath.LegacyOneDec()) delegations = append(delegations, delegation) } return delegations @@ -309,7 +285,7 @@ type StakingCustomGenesisState struct { } // setDefaultStakingGenesisState sets the default staking genesis state -func setDefaultStakingGenesisState(evmosApp *app.Evmos, genesisState evmostypes.GenesisState, overwriteParams StakingCustomGenesisState) evmostypes.GenesisState { +func setDefaultStakingGenesisState(app *app.App, genesisState evmostypes.GenesisState, overwriteParams StakingCustomGenesisState) evmostypes.GenesisState { // Set staking params stakingParams := stakingtypes.DefaultParams() stakingParams.BondDenom = overwriteParams.denom @@ -319,7 +295,7 @@ func setDefaultStakingGenesisState(evmosApp *app.Evmos, genesisState evmostypes. overwriteParams.validators, overwriteParams.delegations, ) - genesisState[stakingtypes.ModuleName] = evmosApp.AppCodec().MustMarshalJSON(stakingGenesis) + genesisState[stakingtypes.ModuleName] = app.AppCodec().MustMarshalJSON(stakingGenesis) return genesisState } @@ -329,7 +305,7 @@ type BankCustomGenesisState struct { } // setDefaultBankGenesisState sets the default bank genesis state -func setDefaultBankGenesisState(evmosApp *app.Evmos, genesisState evmostypes.GenesisState, overwriteParams BankCustomGenesisState) evmostypes.GenesisState { +func setDefaultBankGenesisState(app *app.App, genesisState evmostypes.GenesisState, overwriteParams BankCustomGenesisState) evmostypes.GenesisState { bankGenesis := banktypes.NewGenesisState( banktypes.DefaultGenesisState().Params, overwriteParams.balances, @@ -337,7 +313,7 @@ func setDefaultBankGenesisState(evmosApp *app.Evmos, genesisState evmostypes.Gen []banktypes.Metadata{}, []banktypes.SendEnabled{}, ) - genesisState[banktypes.ModuleName] = evmosApp.AppCodec().MustMarshalJSON(bankGenesis) + genesisState[banktypes.ModuleName] = app.AppCodec().MustMarshalJSON(bankGenesis) return genesisState } @@ -349,24 +325,27 @@ type SlashingCustomGenesisState struct { } // setDefaultSlashingGenesisState sets the default slashing genesis state -func setDefaultSlashingGenesisState(evmosApp *app.Evmos, genesisState evmostypes.GenesisState, overwriteParams SlashingCustomGenesisState) evmostypes.GenesisState { +func setDefaultSlashingGenesisState(app *app.App, genesisState evmostypes.GenesisState, overwriteParams SlashingCustomGenesisState) evmostypes.GenesisState { slashingGen := slashingtypes.DefaultGenesisState() slashingGen.SigningInfos = overwriteParams.signingInfo slashingGen.MissedBlocks = overwriteParams.missedBlocks - genesisState[slashingtypes.ModuleName] = evmosApp.AppCodec().MustMarshalJSON(slashingGen) + slashingGen.Params.SlashFractionDoubleSign = sdkmath.LegacyZeroDec() + slashingGen.Params.SlashFractionDowntime = sdkmath.LegacyZeroDec() + + genesisState[slashingtypes.ModuleName] = app.AppCodec().MustMarshalJSON(slashingGen) return genesisState } // setBankGenesisState updates the bank genesis state with custom genesis state -func setBankGenesisState(evmosApp *app.Evmos, genesisState evmostypes.GenesisState, customGenesis interface{}) (evmostypes.GenesisState, error) { +func setBankGenesisState(app *app.App, genesisState evmostypes.GenesisState, customGenesis interface{}) (evmostypes.GenesisState, error) { customGen, ok := customGenesis.(*banktypes.GenesisState) if !ok { return nil, fmt.Errorf("invalid type %T for bank module genesis state", customGenesis) } bankGen := &banktypes.GenesisState{} - evmosApp.AppCodec().MustUnmarshalJSON(genesisState[banktypes.ModuleName], bankGen) + app.AppCodec().MustUnmarshalJSON(genesisState[banktypes.ModuleName], bankGen) if len(customGen.Balances) > 0 { coins := sdktypes.NewCoins() @@ -386,7 +365,7 @@ func setBankGenesisState(evmosApp *app.Evmos, genesisState evmostypes.GenesisSta bankGen.Params = customGen.Params - genesisState[banktypes.ModuleName] = evmosApp.AppCodec().MustMarshalJSON(bankGen) + genesisState[banktypes.ModuleName] = app.AppCodec().MustMarshalJSON(bankGen) return genesisState, nil } @@ -411,21 +390,21 @@ func addBondedModuleAccountToFundedBalances( } // setDefaultAuthGenesisState sets the default auth genesis state -func setDefaultAuthGenesisState(evmosApp *app.Evmos, genesisState evmostypes.GenesisState, genAccs []authtypes.GenesisAccount) evmostypes.GenesisState { +func setDefaultAuthGenesisState(app *app.App, genesisState evmostypes.GenesisState, genAccs []authtypes.GenesisAccount) evmostypes.GenesisState { defaultAuthGen := authtypes.NewGenesisState(authtypes.DefaultParams(), genAccs) - genesisState[authtypes.ModuleName] = evmosApp.AppCodec().MustMarshalJSON(defaultAuthGen) + genesisState[authtypes.ModuleName] = app.AppCodec().MustMarshalJSON(defaultAuthGen) return genesisState } // setAuthGenesisState updates the bank genesis state with custom genesis state -func setAuthGenesisState(evmosApp *app.Evmos, genesisState evmostypes.GenesisState, customGenesis interface{}) (evmostypes.GenesisState, error) { +func setAuthGenesisState(app *app.App, genesisState evmostypes.GenesisState, customGenesis interface{}) (evmostypes.GenesisState, error) { customGen, ok := customGenesis.(*authtypes.GenesisState) if !ok { return nil, fmt.Errorf("invalid type %T for auth module genesis state", customGenesis) } authGen := &authtypes.GenesisState{} - evmosApp.AppCodec().MustUnmarshalJSON(genesisState[authtypes.ModuleName], authGen) + app.AppCodec().MustUnmarshalJSON(genesisState[authtypes.ModuleName], authGen) if len(customGen.Accounts) > 0 { authGen.Accounts = append(authGen.Accounts, customGen.Accounts...) @@ -433,7 +412,7 @@ func setAuthGenesisState(evmosApp *app.Evmos, genesisState evmostypes.GenesisSta authGen.Params = customGen.Params - genesisState[authtypes.ModuleName] = evmosApp.AppCodec().MustMarshalJSON(authGen) + genesisState[authtypes.ModuleName] = app.AppCodec().MustMarshalJSON(authGen) return genesisState, nil } @@ -443,45 +422,45 @@ type GovCustomGenesisState struct { } // setDefaultGovGenesisState sets the default gov genesis state -func setDefaultGovGenesisState(evmosApp *app.Evmos, genesisState evmostypes.GenesisState, overwriteParams GovCustomGenesisState) evmostypes.GenesisState { +func setDefaultGovGenesisState(app *app.App, genesisState evmostypes.GenesisState, overwriteParams GovCustomGenesisState) evmostypes.GenesisState { govGen := govtypesv1.DefaultGenesisState() updatedParams := govGen.Params minDepositAmt := sdkmath.NewInt(1e18) updatedParams.MinDeposit = sdktypes.NewCoins(sdktypes.NewCoin(overwriteParams.denom, minDepositAmt)) updatedParams.ExpeditedMinDeposit = sdktypes.NewCoins(sdktypes.NewCoin(overwriteParams.denom, minDepositAmt.MulRaw(2))) govGen.Params = updatedParams - genesisState[govtypes.ModuleName] = evmosApp.AppCodec().MustMarshalJSON(govGen) + genesisState[govtypes.ModuleName] = app.AppCodec().MustMarshalJSON(govGen) return genesisState } -func setDefaultErc20GenesisState(evmosApp *app.Evmos, genesisState evmostypes.GenesisState) evmostypes.GenesisState { +func setDefaultErc20GenesisState(app *app.App, genesisState evmostypes.GenesisState) evmostypes.GenesisState { erc20Gen := erc20types.DefaultGenesisState() - genesisState[erc20types.ModuleName] = evmosApp.AppCodec().MustMarshalJSON(erc20Gen) + genesisState[erc20types.ModuleName] = app.AppCodec().MustMarshalJSON(erc20Gen) return genesisState } // defaultAuthGenesisState sets the default genesis state // for the testing setup -func newDefaultGenesisState(evmosApp *app.Evmos, params defaultGenesisParams) evmostypes.GenesisState { - genesisState := evmosApp.DefaultGenesis() +func newDefaultGenesisState(app *app.App, params defaultGenesisParams) evmostypes.GenesisState { + genesisState := app.DefaultGenesis() - genesisState = setDefaultAuthGenesisState(evmosApp, genesisState, params.genAccounts) - genesisState = setDefaultStakingGenesisState(evmosApp, genesisState, params.staking) - genesisState = setDefaultBankGenesisState(evmosApp, genesisState, params.bank) - genesisState = setDefaultGovGenesisState(evmosApp, genesisState, params.gov) - genesisState = setDefaultSlashingGenesisState(evmosApp, genesisState, params.slashing) - genesisState = setDefaultErc20GenesisState(evmosApp, genesisState) + genesisState = setDefaultAuthGenesisState(app, genesisState, params.genAccounts) + genesisState = setDefaultStakingGenesisState(app, genesisState, params.staking) + genesisState = setDefaultBankGenesisState(app, genesisState, params.bank) + genesisState = setDefaultGovGenesisState(app, genesisState, params.gov) + genesisState = setDefaultSlashingGenesisState(app, genesisState, params.slashing) + genesisState = setDefaultErc20GenesisState(app, genesisState) return genesisState } // customizeGenesis modifies genesis state if there're any custom genesis state // for specific modules -func customizeGenesis(evmosApp *app.Evmos, customGen CustomGenesisState, genesisState evmostypes.GenesisState) (evmostypes.GenesisState, error) { +func customizeGenesis(app *app.App, customGen exrpcommon.CustomGenesisState, genesisState evmostypes.GenesisState) (evmostypes.GenesisState, error) { var err error for mod, modGenState := range customGen { if fn, found := genesisSetupFunctions[mod]; found { - genesisState, err = fn(evmosApp, genesisState, modGenState) + genesisState, err = fn(app, genesisState, modGenState) if err != nil { return genesisState, err } diff --git a/testutil/integration/exrp/integration/unit_network.go b/testutil/integration/exrp/integration/unit_network.go index 720d17e..893b26e 100644 --- a/testutil/integration/exrp/integration/unit_network.go +++ b/testutil/integration/exrp/integration/unit_network.go @@ -7,18 +7,19 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/evmos/evmos/v20/x/evm/statedb" inflationtypes "github.com/evmos/evmos/v20/x/inflation/v1/types" - "github.com/xrplevm/node/v4/app" + "github.com/xrplevm/node/v5/app" + exrpcommon "github.com/xrplevm/node/v5/testutil/integration/exrp/common" ) -// UnitTestNetwork is the implementation of the Network interface for unit tests. +// UnitTestIntegrationNetwork is the implementation of the Network interface for unit tests. // It embeds the IntegrationNetwork struct to reuse its methods and // makes the App public for easier testing. -type UnitTestNetwork struct { +type UnitTestIntegrationNetwork struct { IntegrationNetwork App *app.App } -var _ Network = (*UnitTestNetwork)(nil) +var _ Network = (*UnitTestIntegrationNetwork)(nil) // NewUnitTestNetwork configures and initializes a new Evmos Network instance with // the given configuration options. If no configuration options are provided @@ -26,16 +27,16 @@ var _ Network = (*UnitTestNetwork)(nil) // // It panics if an error occurs. // Note: Only uses for Unit Tests -func NewUnitTestNetwork(opts ...ConfigOption) *UnitTestNetwork { +func NewUnitTestNetwork(opts ...exrpcommon.ConfigOption) *UnitTestIntegrationNetwork { network := New(opts...) - return &UnitTestNetwork{ + return &UnitTestIntegrationNetwork{ IntegrationNetwork: *network, App: network.app, } } // GetStateDB returns the state database for the current block. -func (n *UnitTestNetwork) GetStateDB() *statedb.StateDB { +func (n *UnitTestIntegrationNetwork) GetStateDB() *statedb.StateDB { headerHash := n.GetContext().HeaderHash() return statedb.New( n.GetContext(), @@ -45,7 +46,7 @@ func (n *UnitTestNetwork) GetStateDB() *statedb.StateDB { } // FundAccount funds the given account with the given amount of coins. -func (n *UnitTestNetwork) FundAccount(addr sdktypes.AccAddress, coins sdktypes.Coins) error { +func (n *UnitTestIntegrationNetwork) FundAccount(addr sdktypes.AccAddress, coins sdktypes.Coins) error { ctx := n.GetContext() if err := n.app.BankKeeper.MintCoins(ctx, inflationtypes.ModuleName, coins); err != nil { From 18872b7fea89452444909b5c6a118541e3e75d4f Mon Sep 17 00:00:00 2001 From: GuillemGarciaDev Date: Tue, 7 Jan 2025 11:09:08 +0100 Subject: [PATCH 12/60] refactor(tests): update network config + module clients --- tests/upgrade/network.go | 53 ++++++++++++++++++++++++++++++++++--- tests/upgrade/suite.go | 6 ++--- tests/upgrade/suite_test.go | 16 ++++++----- 3 files changed, 62 insertions(+), 13 deletions(-) diff --git a/tests/upgrade/network.go b/tests/upgrade/network.go index 947eca5..9f1bb92 100644 --- a/tests/upgrade/network.go +++ b/tests/upgrade/network.go @@ -1,9 +1,18 @@ package testupgrade import ( - commonnetwork "github.com/xrplevm/node/v4/testutil/integration/common/network" - exrpcommon "github.com/xrplevm/node/v4/testutil/integration/exrp/common" - upgradenetwork "github.com/xrplevm/node/v4/testutil/integration/exrp/upgrade" + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" + "github.com/cosmos/cosmos-sdk/x/authz" + banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" + distrtypes "github.com/cosmos/cosmos-sdk/x/distribution/types" + govtypes "github.com/cosmos/cosmos-sdk/x/gov/types/v1" + stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" + erc20types "github.com/evmos/evmos/v20/x/erc20/types" + evmtypes "github.com/evmos/evmos/v20/x/evm/types" + feemarkettypes "github.com/evmos/evmos/v20/x/feemarket/types" + commonnetwork "github.com/xrplevm/node/v5/testutil/integration/common/network" + exrpcommon "github.com/xrplevm/node/v5/testutil/integration/exrp/common" + upgradenetwork "github.com/xrplevm/node/v5/testutil/integration/exrp/upgrade" ) var _ commonnetwork.Network = (*UpgradeTestNetwork)(nil) @@ -12,7 +21,7 @@ type UpgradeTestNetwork struct { upgradenetwork.UpgradeIntegrationNetwork } -func NewUpgradeTestNetwork(opts ...upgradenetwork.UpgradeConfigOption) *UpgradeTestNetwork { +func NewUpgradeTestNetwork(opts ...exrpcommon.ConfigOption) *UpgradeTestNetwork { network := upgradenetwork.New(opts...) return &UpgradeTestNetwork{ UpgradeIntegrationNetwork: *network, @@ -22,3 +31,39 @@ func NewUpgradeTestNetwork(opts ...upgradenetwork.UpgradeConfigOption) *UpgradeT func (n *UpgradeTestNetwork) SetupSdkConfig() { exrpcommon.SetupSdkConfig() } + +func (n *UpgradeTestNetwork) ERC20Client() erc20types.QueryClient { + return exrpcommon.GetERC20Client(n) +} + +func (n *UpgradeTestNetwork) EvmClient() evmtypes.QueryClient { + return exrpcommon.GetEvmClient(n) +} + +func (n *UpgradeTestNetwork) GovClient() govtypes.QueryClient { + return exrpcommon.GetGovClient(n) +} + +func (n *UpgradeTestNetwork) BankClient() banktypes.QueryClient { + return exrpcommon.GetBankClient(n) +} + +func (n *UpgradeTestNetwork) FeeMarketClient() feemarkettypes.QueryClient { + return exrpcommon.GetFeeMarketClient(n) +} + +func (n *UpgradeTestNetwork) AuthClient() authtypes.QueryClient { + return exrpcommon.GetAuthClient(n) +} + +func (n *UpgradeTestNetwork) AuthzClient() authz.QueryClient { + return exrpcommon.GetAuthzClient(n) +} + +func (n *UpgradeTestNetwork) StakingClient() stakingtypes.QueryClient { + return exrpcommon.GetStakingClient(n) +} + +func (n *UpgradeTestNetwork) DistrClient() distrtypes.QueryClient { + return exrpcommon.GetDistrClient(n) +} diff --git a/tests/upgrade/suite.go b/tests/upgrade/suite.go index 9da680f..b95347c 100644 --- a/tests/upgrade/suite.go +++ b/tests/upgrade/suite.go @@ -5,7 +5,7 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" "github.com/stretchr/testify/suite" - upgradenetwork "github.com/xrplevm/node/v4/testutil/integration/exrp/upgrade" + exrpupgrade "github.com/xrplevm/node/v5/testutil/integration/exrp/upgrade" ) const defaultStateFile = "upgrade-state.json" @@ -35,9 +35,9 @@ func (s *UpgradeTestSuite) SetupTest() { // Create the network s.network = NewUpgradeTestNetwork( - upgradenetwork.WithGenesisFile(stateFile), + exrpupgrade.WithGenesisFile(stateFile), ) // Check that the network was created successfully s.Require().NotNil(s.network) -} \ No newline at end of file +} diff --git a/tests/upgrade/suite_test.go b/tests/upgrade/suite_test.go index e326a9a..d46f384 100644 --- a/tests/upgrade/suite_test.go +++ b/tests/upgrade/suite_test.go @@ -1,13 +1,12 @@ package testupgrade import ( - "fmt" "testing" banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" + stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" "github.com/stretchr/testify/suite" - "github.com/xrplevm/node/v4/app" - exrpcommon "github.com/xrplevm/node/v4/testutil/integration/exrp/common" + "github.com/xrplevm/node/v5/app" ) func TestUpgradeTestSuite(t *testing.T) { @@ -19,14 +18,19 @@ func (s *UpgradeTestSuite) TestUpgrade() { s.Require().NotEmpty(denom) s.Require().Equal(denom, app.BaseDenom) - balances, err := exrpcommon.GetBankClient(s.Network()).AllBalances(s.network.GetContext(), &banktypes.QueryAllBalancesRequest{ + balances, err := s.Network().BankClient().AllBalances(s.network.GetContext(), &banktypes.QueryAllBalancesRequest{ Address: "ethm1fl48vsnmsdzcv85q5d2q4z5ajdha8yu3w48d64", }) - fmt.Println("balances", balances) + s.T().Log("balances", balances) s.Require().NoError(err) - fmt.Println(balances) err = s.network.NextBlock() s.Require().NoError(err) + + res, err := s.Network().StakingClient().Validators(s.network.GetContext(), &stakingtypes.QueryValidatorsRequest{}) + s.Require().NoError(err) + + s.T().Log("validators", len(res.Validators)) + s.Require().Equal(len(res.Validators), 1) } From 92fbbe424552c9669224977536837b632882ba62 Mon Sep 17 00:00:00 2001 From: GuillemGarciaDev Date: Tue, 7 Jan 2025 11:09:26 +0100 Subject: [PATCH 13/60] feat(tests): add integration test suite --- tests/integration/network.go | 69 +++++++++++++++++++++++++++++++++ tests/integration/suite.go | 30 ++++++++++++++ tests/integration/suite_test.go | 37 ++++++++++++++++++ 3 files changed, 136 insertions(+) create mode 100644 tests/integration/network.go create mode 100644 tests/integration/suite.go create mode 100644 tests/integration/suite_test.go diff --git a/tests/integration/network.go b/tests/integration/network.go new file mode 100644 index 0000000..1c2c259 --- /dev/null +++ b/tests/integration/network.go @@ -0,0 +1,69 @@ +package integration + +import ( + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" + "github.com/cosmos/cosmos-sdk/x/authz" + banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" + distrtypes "github.com/cosmos/cosmos-sdk/x/distribution/types" + govtypes "github.com/cosmos/cosmos-sdk/x/gov/types/v1" + stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" + erc20types "github.com/evmos/evmos/v20/x/erc20/types" + evmtypes "github.com/evmos/evmos/v20/x/evm/types" + feemarkettypes "github.com/evmos/evmos/v20/x/feemarket/types" + commonnetwork "github.com/xrplevm/node/v5/testutil/integration/common/network" + exrpcommon "github.com/xrplevm/node/v5/testutil/integration/exrp/common" + exrpintegration "github.com/xrplevm/node/v5/testutil/integration/exrp/integration" +) + +var _ commonnetwork.Network = (*Network)(nil) + +type Network struct { + exrpintegration.IntegrationNetwork +} + +func NewIntegrationNetwork(opts ...exrpcommon.ConfigOption) *Network { + network := exrpintegration.New(opts...) + return &Network{ + IntegrationNetwork: *network, + } +} + +func (n *Network) SetupSdkConfig() { + exrpcommon.SetupSdkConfig() +} + +func (n *Network) ERC20Client() erc20types.QueryClient { + return exrpcommon.GetERC20Client(n) +} + +func (n *Network) EvmClient() evmtypes.QueryClient { + return exrpcommon.GetEvmClient(n) +} + +func (n *Network) GovClient() govtypes.QueryClient { + return exrpcommon.GetGovClient(n) +} + +func (n *Network) BankClient() banktypes.QueryClient { + return exrpcommon.GetBankClient(n) +} + +func (n *Network) FeeMarketClient() feemarkettypes.QueryClient { + return exrpcommon.GetFeeMarketClient(n) +} + +func (n *Network) AuthClient() authtypes.QueryClient { + return exrpcommon.GetAuthClient(n) +} + +func (n *Network) AuthzClient() authz.QueryClient { + return exrpcommon.GetAuthzClient(n) +} + +func (n *Network) StakingClient() stakingtypes.QueryClient { + return exrpcommon.GetStakingClient(n) +} + +func (n *Network) DistrClient() distrtypes.QueryClient { + return exrpcommon.GetDistrClient(n) +} diff --git a/tests/integration/suite.go b/tests/integration/suite.go new file mode 100644 index 0000000..fcf3256 --- /dev/null +++ b/tests/integration/suite.go @@ -0,0 +1,30 @@ +package integration + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/stretchr/testify/suite" + exrpcommon "github.com/xrplevm/node/v5/testutil/integration/exrp/common" +) + +type TestSuite struct { + suite.Suite + + network *Network +} + +func (s *TestSuite) Network() *Network { + return s.network +} + +func (s *TestSuite) SetupTest() { + // Setup the SDK config + s.network.SetupSdkConfig() + + s.Require().Equal(sdk.GetConfig().GetBech32AccountAddrPrefix(), "ethm") + + // Check that the network was created successfully + s.network = NewIntegrationNetwork( + exrpcommon.WithAmountOfValidators(5), + ) + s.Require().NotNil(s.network) +} diff --git a/tests/integration/suite_test.go b/tests/integration/suite_test.go new file mode 100644 index 0000000..e740f3d --- /dev/null +++ b/tests/integration/suite_test.go @@ -0,0 +1,37 @@ +package integration + +import ( + "testing" + + banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" + stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" + "github.com/stretchr/testify/suite" + "github.com/xrplevm/node/v5/app" +) + +func TestIntegrationTestSuite(t *testing.T) { + suite.Run(t, new(TestSuite)) +} + +func (s *TestSuite) TestIntegration() { + denom := s.Network().GetDenom() + s.Require().NotEmpty(denom) + s.Require().Equal(app.BaseDenom, denom) + + balances, err := s.Network().BankClient().AllBalances(s.network.GetContext(), &banktypes.QueryAllBalancesRequest{ + Address: "ethm1fl48vsnmsdzcv85q5d2q4z5ajdha8yu3w48d64", + }) + + s.T().Log("balances", balances) + s.Require().NoError(err) + + err = s.network.NextBlock() + s.Require().NoError(err) + + res, err := s.Network().StakingClient().Validators(s.network.GetContext(), &stakingtypes.QueryValidatorsRequest{}) + s.Require().NoError(err) + + s.T().Log("validators", len(res.Validators)) + + s.Require().Equal(len(res.Validators), len(s.network.GetValidators())) +} From 8cf568e3de7773617a0eef4ac0db1a49f0ae8f82 Mon Sep 17 00:00:00 2001 From: GuillemGarciaDev Date: Tue, 7 Jan 2025 11:09:35 +0100 Subject: [PATCH 14/60] chore: add test-integration make rule --- Makefile | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 8773fe2..47e47a8 100644 --- a/Makefile +++ b/Makefile @@ -127,7 +127,11 @@ mocks: test-upgrade: @echo "--> Running upgrade testsuite" - @go test -mod=readonly ./tests/upgrade + @go test -mod=readonly -v ./tests/upgrade + +test-integration: + @echo "--> Running integration testsuite" + @go test -mod=readonly -v ./tests/integration test-poa: @echo "--> Running POA tests" From 5a825f0a76594ad0512a584e588a44a3816c533a Mon Sep 17 00:00:00 2001 From: GuillemGarciaDev Date: Tue, 7 Jan 2025 11:12:09 +0100 Subject: [PATCH 15/60] chore: add integration tests to Dockerfile --- Dockerfile | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Dockerfile b/Dockerfile index a34ed2e..9aff88a 100644 --- a/Dockerfile +++ b/Dockerfile @@ -18,6 +18,9 @@ FROM base AS integration RUN make lint # Unit tests RUN make test-poa +# Integration tests +RUN make test-integration +RUN make test-upgrade # Simulation tests RUN make test-sim-benchmark-simulation RUN make test-sim-full-app-fast From 088bae453671ab295bd5fddac27a4194692d1029 Mon Sep 17 00:00:00 2001 From: GuillemGarciaDev Date: Tue, 7 Jan 2025 13:30:00 +0100 Subject: [PATCH 16/60] feat: sketch remove validator test --- tests/integration/poa_test.go | 40 +++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 tests/integration/poa_test.go diff --git a/tests/integration/poa_test.go b/tests/integration/poa_test.go new file mode 100644 index 0000000..772ca71 --- /dev/null +++ b/tests/integration/poa_test.go @@ -0,0 +1,40 @@ +package integration + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +func (s *TestSuite) TestRemoveValidator(t *testing.T) { + tt := []struct { + name string + valAddress string + expectedError error + }{ + { + name: "remove unexisting validator - validator without balance", + }, + { + name: "remove unexisting validator - validator with balance", + }, + } + + for _, tc := range tt { + s.Run(tc.name, func() { + // Submit MsgRemoveValidator proposal + err := s.Network().PoaKeeper().ExecuteRemoveValidator( + s.Network().GetContext(), + tc.valAddress, + ) + + if tc.expectedError != nil { + require.Error(t, err) + require.ErrorIs(t, err, tc.expectedError) + } else { + require.NoError(t, err) + } + }) + } +} + From 0180109508d85b45eb53f89a870baa34efbb3286 Mon Sep 17 00:00:00 2001 From: GuillemGarciaDev Date: Tue, 7 Jan 2025 13:30:26 +0100 Subject: [PATCH 17/60] feat(testutil): add poa client and keeper --- tests/integration/network.go | 5 +++++ tests/upgrade/network.go | 5 +++++ testutil/integration/exrp/common/clients.go | 9 +++++++++ testutil/integration/exrp/integration/keepers.go | 5 +++++ testutil/integration/exrp/upgrade/keepers.go | 5 +++++ x/poa/keeper/keeper.go | 10 ++++++++++ 6 files changed, 39 insertions(+) diff --git a/tests/integration/network.go b/tests/integration/network.go index 1c2c259..38029d6 100644 --- a/tests/integration/network.go +++ b/tests/integration/network.go @@ -13,6 +13,7 @@ import ( commonnetwork "github.com/xrplevm/node/v5/testutil/integration/common/network" exrpcommon "github.com/xrplevm/node/v5/testutil/integration/exrp/common" exrpintegration "github.com/xrplevm/node/v5/testutil/integration/exrp/integration" + poatypes "github.com/xrplevm/node/v5/x/poa/types" ) var _ commonnetwork.Network = (*Network)(nil) @@ -67,3 +68,7 @@ func (n *Network) StakingClient() stakingtypes.QueryClient { func (n *Network) DistrClient() distrtypes.QueryClient { return exrpcommon.GetDistrClient(n) } + +func (n *Network) PoaClient() poatypes.QueryClient { + return exrpcommon.GetPoaClient(n) +} diff --git a/tests/upgrade/network.go b/tests/upgrade/network.go index 9f1bb92..3273327 100644 --- a/tests/upgrade/network.go +++ b/tests/upgrade/network.go @@ -13,6 +13,7 @@ import ( commonnetwork "github.com/xrplevm/node/v5/testutil/integration/common/network" exrpcommon "github.com/xrplevm/node/v5/testutil/integration/exrp/common" upgradenetwork "github.com/xrplevm/node/v5/testutil/integration/exrp/upgrade" + poatypes "github.com/xrplevm/node/v5/x/poa/types" ) var _ commonnetwork.Network = (*UpgradeTestNetwork)(nil) @@ -67,3 +68,7 @@ func (n *UpgradeTestNetwork) StakingClient() stakingtypes.QueryClient { func (n *UpgradeTestNetwork) DistrClient() distrtypes.QueryClient { return exrpcommon.GetDistrClient(n) } + +func (n *UpgradeTestNetwork) PoaClient() poatypes.QueryClient { + return exrpcommon.GetPoaClient(n) +} diff --git a/testutil/integration/exrp/common/clients.go b/testutil/integration/exrp/common/clients.go index c2cdc9e..9c546d2 100644 --- a/testutil/integration/exrp/common/clients.go +++ b/testutil/integration/exrp/common/clients.go @@ -25,6 +25,8 @@ import ( evmtypes "github.com/evmos/evmos/v20/x/evm/types" feemarketkeeper "github.com/evmos/evmos/v20/x/feemarket/keeper" feemarkettypes "github.com/evmos/evmos/v20/x/feemarket/types" + poakeeper "github.com/xrplevm/node/v5/x/poa/keeper" + poatypes "github.com/xrplevm/node/v5/x/poa/types" ) type NetworkKeepers interface { @@ -40,6 +42,7 @@ type NetworkKeepers interface { AccountKeeper() authkeeper.AccountKeeper AuthzKeeper() authzkeeper.Keeper FeeMarketKeeper() feemarketkeeper.Keeper + PoaKeeper() poakeeper.Keeper } func getQueryHelper(ctx sdktypes.Context, encCfg testutil.TestEncodingConfig) *baseapp.QueryServiceTestHelper { @@ -103,3 +106,9 @@ func GetDistrClient(n NetworkKeepers) distrtypes.QueryClient { distrtypes.RegisterQueryServer(queryHelper, distrkeeper.Querier{Keeper: n.DistrKeeper()}) return distrtypes.NewQueryClient(queryHelper) } + +func GetPoaClient(n NetworkKeepers) poatypes.QueryClient { + queryHelper := getQueryHelper(n.GetContext(), n.GetEncodingConfig()) + poatypes.RegisterQueryServer(queryHelper, poakeeper.Querier{Keeper: n.PoaKeeper()}) + return poatypes.NewQueryClient(queryHelper) +} diff --git a/testutil/integration/exrp/integration/keepers.go b/testutil/integration/exrp/integration/keepers.go index 61e258a..42b5244 100644 --- a/testutil/integration/exrp/integration/keepers.go +++ b/testutil/integration/exrp/integration/keepers.go @@ -11,6 +11,7 @@ import ( erc20keeper "github.com/evmos/evmos/v20/x/erc20/keeper" evmkeeper "github.com/evmos/evmos/v20/x/evm/keeper" feemarketkeeper "github.com/evmos/evmos/v20/x/feemarket/keeper" + poakeeper "github.com/xrplevm/node/v5/x/poa/keeper" ) func (n *IntegrationNetwork) BankKeeper() bankkeeper.Keeper { @@ -48,3 +49,7 @@ func (n *IntegrationNetwork) AuthzKeeper() authzkeeper.Keeper { func (n *IntegrationNetwork) FeeMarketKeeper() feemarketkeeper.Keeper { return n.app.FeeMarketKeeper } + +func (n *IntegrationNetwork) PoaKeeper() poakeeper.Keeper { + return n.app.PoaKeeper +} diff --git a/testutil/integration/exrp/upgrade/keepers.go b/testutil/integration/exrp/upgrade/keepers.go index 84dc8fb..dc4cc6c 100644 --- a/testutil/integration/exrp/upgrade/keepers.go +++ b/testutil/integration/exrp/upgrade/keepers.go @@ -11,6 +11,7 @@ import ( erc20keeper "github.com/evmos/evmos/v20/x/erc20/keeper" evmkeeper "github.com/evmos/evmos/v20/x/evm/keeper" feemarketkeeper "github.com/evmos/evmos/v20/x/feemarket/keeper" + poakeeper "github.com/xrplevm/node/v5/x/poa/keeper" ) func (n *UpgradeIntegrationNetwork) BankKeeper() bankkeeper.Keeper { @@ -48,3 +49,7 @@ func (n *UpgradeIntegrationNetwork) AuthzKeeper() authzkeeper.Keeper { func (n *UpgradeIntegrationNetwork) FeeMarketKeeper() feemarketkeeper.Keeper { return n.app.FeeMarketKeeper } + +func (n *UpgradeIntegrationNetwork) PoaKeeper() poakeeper.Keeper { + return n.app.PoaKeeper +} diff --git a/x/poa/keeper/keeper.go b/x/poa/keeper/keeper.go index 3606605..93c9f66 100644 --- a/x/poa/keeper/keeper.go +++ b/x/poa/keeper/keeper.go @@ -20,6 +20,16 @@ import ( "github.com/xrplevm/node/v5/x/poa/types" ) +var _ types.QueryServer = Querier{} + +type Querier struct { + Keeper +} + +func NewQuerier(keeper Keeper) Querier { + return Querier{Keeper: keeper} +} + type ( Keeper struct { cdc codec.Codec From e35d24c31ed3d3848d8fb16a2390850dd7f51d42 Mon Sep 17 00:00:00 2001 From: GuillemGarciaDev Date: Wed, 8 Jan 2025 12:49:35 +0100 Subject: [PATCH 18/60] feat(tests): updated fund accounts for remove validator tests --- tests/integration/poa_test.go | 114 +++++++++++++++++++++++++++++++--- 1 file changed, 104 insertions(+), 10 deletions(-) diff --git a/tests/integration/poa_test.go b/tests/integration/poa_test.go index 772ca71..598db73 100644 --- a/tests/integration/poa_test.go +++ b/tests/integration/poa_test.go @@ -1,38 +1,132 @@ package integration import ( - "testing" + "math/rand" + "time" + sdkmath "cosmossdk.io/math" + sdktypes "github.com/cosmos/cosmos-sdk/types" + simtypes "github.com/cosmos/cosmos-sdk/types/simulation" + banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" "github.com/stretchr/testify/require" + poatypes "github.com/xrplevm/node/v5/x/poa/types" ) -func (s *TestSuite) TestRemoveValidator(t *testing.T) { +func (s *TestSuite) TestRemoveValidator() { + // Generate a random account + randomAccs := simtypes.RandomAccounts(rand.New(rand.NewSource(time.Now().UnixNano())), 1) + randomAcc := randomAccs[0] + + // Validators + validators := s.Network().GetValidators() + require.NotZero(s.T(), len(validators)) + + valAddr, err := sdktypes.ValAddressFromBech32(validators[0].OperatorAddress) + require.NoError(s.T(), err) + valAccAddr := sdktypes.AccAddress(valAddr) + tt := []struct { name string valAddress string expectedError error + beforeRun func() + afterRun func() }{ { - name: "remove unexisting validator - validator without balance", + name: "remove unexisting validator - random address - no balance", + valAddress: randomAcc.Address.String(), + expectedError: poatypes.ErrAddressHasNoTokens, + beforeRun: func() { + balance, err := s.Network().GetBankClient().Balance( + s.Network().GetContext(), + &banktypes.QueryBalanceRequest{ + Address: randomAcc.Address.String(), + Denom: s.Network().GetDenom(), + }, + ) + require.NoError(s.T(), err) + require.Equal(s.T(), balance.Balance.Amount, sdkmath.NewInt(0)) + }, }, { - name: "remove unexisting validator - validator with balance", + name: "remove unexisting validator - random address - with balance", + valAddress: randomAcc.Address.String(), + expectedError: poatypes.ErrAddressHasNoTokens, + beforeRun: func() { + coins := sdktypes.NewCoin( + s.Network().GetDenom(), + sdkmath.NewInt(100000000000), + ) + + err = s.factory.FundAccount( + s.keyring.GetKey(0), + randomAcc.Address, + sdktypes.NewCoins( + coins, + ), + ) + require.NoError(s.T(), err) + require.NoError(s.T(), s.Network().NextBlock()) + + balance, err := s.Network().GetBankClient().Balance( + s.Network().GetContext(), + &banktypes.QueryBalanceRequest{ + Address: randomAcc.Address.String(), + Denom: s.Network().GetDenom(), + }, + ) + require.NoError(s.T(), err) + require.Equal(s.T(), coins.Amount.BigInt(), balance.Balance.Amount.BigInt()) + }, + afterRun: func() { + balance, err := s.Network().GetBankClient().Balance( + s.Network().GetContext(), + &banktypes.QueryBalanceRequest{ + Address: randomAcc.Address.String(), + Denom: s.Network().GetDenom(), + }, + ) + require.NoError(s.T(), err) + require.Equal(s.T(), balance.Balance.Amount, sdkmath.NewInt(0)) + }, + }, + { + name: "remove existing validator - with unbonding delegations - with tokens", + valAddress: valAccAddr.String(), + afterRun: func() { + balance, err := s.Network().GetBankClient().Balance( + s.Network().GetContext(), + &banktypes.QueryBalanceRequest{ + Address: randomAcc.Address.String(), + Denom: s.Network().GetDenom(), + }, + ) + require.NoError(s.T(), err) + require.Equal(s.T(), balance.Balance.Amount, sdkmath.NewInt(0)) + }, }, } for _, tc := range tt { s.Run(tc.name, func() { - // Submit MsgRemoveValidator proposal - err := s.Network().PoaKeeper().ExecuteRemoveValidator( + if tc.beforeRun != nil { + tc.beforeRun() + } + + err = s.Network().PoaKeeper().ExecuteRemoveValidator( s.Network().GetContext(), tc.valAddress, ) - if tc.expectedError != nil { - require.Error(t, err) - require.ErrorIs(t, err, tc.expectedError) + if tc.afterRun != nil { + tc.afterRun() + } + + if tc.expectedError != nil && err != nil { + require.Error(s.T(), err) + require.ErrorIs(s.T(), err, tc.expectedError) } else { - require.NoError(t, err) + require.NoError(s.T(), err) } }) } From c110eb5b4d7b65c63f8e2512c59446397cd88902 Mon Sep 17 00:00:00 2001 From: GuillemGarciaDev Date: Wed, 8 Jan 2025 12:50:42 +0100 Subject: [PATCH 19/60] refactor(testutil): add `grpc`,`factory` and `keyring`utils --- testutil/integration/common/factory/base.go | 118 +++++++++++++++++ .../common/factory/distribution.go | 87 +++++++++++++ .../integration/common/factory/factory.go | 48 +++++++ testutil/integration/common/factory/fund.go | 51 ++++++++ testutil/integration/common/factory/helper.go | 119 ++++++++++++++++++ testutil/integration/common/factory/sign.go | 58 +++++++++ .../integration/common/factory/staking.go | 88 +++++++++++++ testutil/integration/common/factory/types.go | 24 ++++ testutil/integration/common/grpc/account.go | 28 +++++ testutil/integration/common/grpc/authz.go | 117 +++++++++++++++++ testutil/integration/common/grpc/bank.go | 40 ++++++ .../integration/common/grpc/distribution.go | 56 +++++++++ testutil/integration/common/grpc/feemarket.go | 13 ++ testutil/integration/common/grpc/grpc.go | 71 +++++++++++ testutil/integration/common/grpc/staking.go | 68 ++++++++++ .../integration/common/network/network.go | 18 ++- .../integration/exrp/integration/network.go | 5 +- 17 files changed, 1001 insertions(+), 8 deletions(-) create mode 100644 testutil/integration/common/factory/base.go create mode 100644 testutil/integration/common/factory/distribution.go create mode 100644 testutil/integration/common/factory/factory.go create mode 100644 testutil/integration/common/factory/fund.go create mode 100644 testutil/integration/common/factory/helper.go create mode 100644 testutil/integration/common/factory/sign.go create mode 100644 testutil/integration/common/factory/staking.go create mode 100644 testutil/integration/common/factory/types.go create mode 100644 testutil/integration/common/grpc/account.go create mode 100644 testutil/integration/common/grpc/authz.go create mode 100644 testutil/integration/common/grpc/bank.go create mode 100644 testutil/integration/common/grpc/distribution.go create mode 100644 testutil/integration/common/grpc/feemarket.go create mode 100644 testutil/integration/common/grpc/grpc.go create mode 100644 testutil/integration/common/grpc/staking.go diff --git a/testutil/integration/common/factory/base.go b/testutil/integration/common/factory/base.go new file mode 100644 index 0000000..f77a22a --- /dev/null +++ b/testutil/integration/common/factory/base.go @@ -0,0 +1,118 @@ +// Copyright Tharsis Labs Ltd.(Evmos) +// SPDX-License-Identifier:ENCL-1.0(https://github.com/evmos/evmos/blob/main/LICENSE) +package factory + +import ( + "fmt" + + errorsmod "cosmossdk.io/errors" + abcitypes "github.com/cometbft/cometbft/abci/types" + "github.com/cosmos/cosmos-sdk/client" + cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" + sdktypes "github.com/cosmos/cosmos-sdk/types" + testutiltypes "github.com/cosmos/cosmos-sdk/types/module/testutil" + authsigning "github.com/cosmos/cosmos-sdk/x/auth/signing" + "github.com/xrplevm/node/v5/testutil/integration/common/grpc" + "github.com/xrplevm/node/v5/testutil/integration/common/network" +) + +// BaseTxFactory is the interface that wraps the common methods to build and broadcast transactions +// within cosmos chains +type BaseTxFactory interface { + // BuildCosmosTx builds a Cosmos tx with the provided private key and txArgs + BuildCosmosTx(privKey cryptotypes.PrivKey, txArgs CosmosTxArgs) (authsigning.Tx, error) + // SignCosmosTx signs a Cosmos transaction with the provided + // private key and tx builder + SignCosmosTx(privKey cryptotypes.PrivKey, txBuilder client.TxBuilder) error + // ExecuteCosmosTx builds, signs and broadcasts a Cosmos tx with the provided private key and txArgs + ExecuteCosmosTx(privKey cryptotypes.PrivKey, txArgs CosmosTxArgs) (abcitypes.ExecTxResult, error) + // EncodeTx encodes the provided transaction + EncodeTx(tx sdktypes.Tx) ([]byte, error) + // CommitCosmosTx creates, signs and commits a cosmos tx + // (produces a block with the specified transaction) + CommitCosmosTx(privKey cryptotypes.PrivKey, txArgs CosmosTxArgs) (abcitypes.ExecTxResult, error) +} + +// baseTxFactory is the struct of the basic tx factory +// to build and broadcast transactions. +// This is to simulate the behavior of a real user. +type baseTxFactory struct { + grpcHandler grpc.Handler + network network.Network + ec testutiltypes.TestEncodingConfig +} + +// newBaseTxFactory instantiates a new baseTxFactory +func newBaseTxFactory( + network network.Network, + grpcHandler grpc.Handler, +) BaseTxFactory { + return &baseTxFactory{ + grpcHandler: grpcHandler, + network: network, + ec: network.GetEncodingConfig(), + } +} + +func (tf *baseTxFactory) BuildCosmosTx(privKey cryptotypes.PrivKey, txArgs CosmosTxArgs) (authsigning.Tx, error) { + txBuilder, err := tf.buildTx(privKey, txArgs) + if err != nil { + return nil, errorsmod.Wrap(err, "failed to build tx") + } + return txBuilder.GetTx(), nil +} + +// ExecuteCosmosTx creates, signs and broadcasts a Cosmos transaction +func (tf *baseTxFactory) ExecuteCosmosTx(privKey cryptotypes.PrivKey, txArgs CosmosTxArgs) (abcitypes.ExecTxResult, error) { + signedTx, err := tf.BuildCosmosTx(privKey, txArgs) + if err != nil { + return abcitypes.ExecTxResult{}, errorsmod.Wrap(err, "failed to build tx") + } + + txBytes, err := tf.EncodeTx(signedTx) + if err != nil { + return abcitypes.ExecTxResult{}, errorsmod.Wrap(err, "failed to encode tx") + } + + return tf.network.BroadcastTxSync(txBytes) +} + +// CommitCosmosTx creates and signs a Cosmos transaction, and then includes it in +// a block and commits the state changes on the chain +func (tf *baseTxFactory) CommitCosmosTx(privKey cryptotypes.PrivKey, txArgs CosmosTxArgs) (abcitypes.ExecTxResult, error) { + signedTx, err := tf.BuildCosmosTx(privKey, txArgs) + if err != nil { + return abcitypes.ExecTxResult{}, errorsmod.Wrap(err, "failed to build tx") + } + + txBytes, err := tf.EncodeTx(signedTx) + if err != nil { + return abcitypes.ExecTxResult{}, errorsmod.Wrap(err, "failed to encode tx") + } + + blockRes, err := tf.network.NextBlockWithTxs(txBytes) + if err != nil { + return abcitypes.ExecTxResult{}, errorsmod.Wrap(err, "failed to include the tx in a block") + } + txResCount := len(blockRes.TxResults) + if txResCount != 1 { + return abcitypes.ExecTxResult{}, fmt.Errorf("expected to receive only one tx result, but got %d", txResCount) + } + return *blockRes.TxResults[0], nil +} + +// SignCosmosTx is a helper function that signs a Cosmos transaction +// with the provided private key and transaction builder +func (tf *baseTxFactory) SignCosmosTx(privKey cryptotypes.PrivKey, txBuilder client.TxBuilder) error { + txConfig := tf.ec.TxConfig + signMode, err := authsigning.APISignModeToInternal(txConfig.SignModeHandler().DefaultMode()) + if err != nil { + return errorsmod.Wrap(err, "invalid sign mode") + } + signerData, err := tf.setSignatures(privKey, txBuilder, signMode) + if err != nil { + return errorsmod.Wrap(err, "failed to set tx signatures") + } + + return tf.signWithPrivKey(privKey, txBuilder, signerData, signMode) +} diff --git a/testutil/integration/common/factory/distribution.go b/testutil/integration/common/factory/distribution.go new file mode 100644 index 0000000..ed530a6 --- /dev/null +++ b/testutil/integration/common/factory/distribution.go @@ -0,0 +1,87 @@ +// Copyright Tharsis Labs Ltd.(Evmos) +// SPDX-License-Identifier:ENCL-1.0(https://github.com/evmos/evmos/blob/main/LICENSE) +package factory + +import ( + "fmt" + + cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" + sdk "github.com/cosmos/cosmos-sdk/types" + distrtypes "github.com/cosmos/cosmos-sdk/x/distribution/types" +) + +type DistributionTxFactory interface { + // SetWithdrawAddress is a method to create and broadcast a MsgSetWithdrawAddress + SetWithdrawAddress(delegatorPriv cryptotypes.PrivKey, withdrawerAddr sdk.AccAddress) error + // WithdrawDelegationRewards is a method to create and broadcast a MsgWithdrawDelegationRewards + WithdrawDelegationRewards(delegatorPriv cryptotypes.PrivKey, validatorAddr string) error + // WithdrawValidatorCommission is a method to create and broadcast a MsgWithdrawValidatorCommission + WithdrawValidatorCommission(validatorPriv cryptotypes.PrivKey) error +} + +type distributionTxFactory struct { + BaseTxFactory +} + +func newDistrTxFactory(bf BaseTxFactory) DistributionTxFactory { + return &distributionTxFactory{bf} +} + +func (tf *distributionTxFactory) SetWithdrawAddress(delegatorPriv cryptotypes.PrivKey, withdrawerAddr sdk.AccAddress) error { + delegatorAccAddr := sdk.AccAddress(delegatorPriv.PubKey().Address()) + + msg := distrtypes.NewMsgSetWithdrawAddress( + delegatorAccAddr, + withdrawerAddr, + ) + + resp, err := tf.ExecuteCosmosTx(delegatorPriv, CosmosTxArgs{ + Msgs: []sdk.Msg{msg}, + }) + + if resp.Code != 0 { + err = fmt.Errorf("received error code %d on SetWithdrawAddress transaction. Logs: %s", resp.Code, resp.Log) + } + + return err +} + +// WithdrawDelegationRewards will withdraw any unclaimed staking rewards for the delegator associated with +// the given private key from the validator. +// The validator address should be in the format `evmosvaloper1...`. +func (tf *distributionTxFactory) WithdrawDelegationRewards(delegatorPriv cryptotypes.PrivKey, validatorAddr string) error { + delegatorAccAddr := sdk.AccAddress(delegatorPriv.PubKey().Address()) + + msg := distrtypes.NewMsgWithdrawDelegatorReward( + delegatorAccAddr.String(), + validatorAddr, + ) + + resp, err := tf.ExecuteCosmosTx(delegatorPriv, CosmosTxArgs{ + Msgs: []sdk.Msg{msg}, + }) + + if resp.Code != 0 { + err = fmt.Errorf("received error code %d on WithdrawDelegationRewards transaction. Logs: %s", resp.Code, resp.Log) + } + + return err +} + +func (tf *distributionTxFactory) WithdrawValidatorCommission(validatorPriv cryptotypes.PrivKey) error { + validatorAddr := sdk.ValAddress(validatorPriv.PubKey().Address()) + + msg := distrtypes.NewMsgWithdrawValidatorCommission( + validatorAddr.String(), + ) + + resp, err := tf.ExecuteCosmosTx(validatorPriv, CosmosTxArgs{ + Msgs: []sdk.Msg{msg}, + }) + + if resp.Code != 0 { + err = fmt.Errorf("received error code %d on WithdrawValidatorCommission transaction. Logs: %s", resp.Code, resp.Log) + } + + return err +} diff --git a/testutil/integration/common/factory/factory.go b/testutil/integration/common/factory/factory.go new file mode 100644 index 0000000..f56e130 --- /dev/null +++ b/testutil/integration/common/factory/factory.go @@ -0,0 +1,48 @@ +// Copyright Tharsis Labs Ltd.(Evmos) +// SPDX-License-Identifier:ENCL-1.0(https://github.com/evmos/evmos/blob/main/LICENSE) + +package factory + +import ( + "github.com/xrplevm/node/v5/testutil/integration/common/grpc" + "github.com/xrplevm/node/v5/testutil/integration/common/network" +) + +const ( + GasAdjustment = float64(1.7) +) + +// CoreTxFactory is the interface that wraps the methods +// to build and broadcast cosmos transactions, and also +// includes module-specific transactions +type CoreTxFactory interface { + BaseTxFactory + DistributionTxFactory + StakingTxFactory + FundTxFactory +} + +var _ CoreTxFactory = (*IntegrationTxFactory)(nil) + +// IntegrationTxFactory is a helper struct to build and broadcast transactions +// to the network on integration tests. This is to simulate the behavior of a real user. +type IntegrationTxFactory struct { + BaseTxFactory + DistributionTxFactory + StakingTxFactory + FundTxFactory +} + +// New creates a new IntegrationTxFactory instance +func New( + network network.Network, + grpcHandler grpc.Handler, +) CoreTxFactory { + bf := newBaseTxFactory(network, grpcHandler) + return &IntegrationTxFactory{ + bf, + newDistrTxFactory(bf), + newStakingTxFactory(bf), + newFundTxFactory(bf), + } +} diff --git a/testutil/integration/common/factory/fund.go b/testutil/integration/common/factory/fund.go new file mode 100644 index 0000000..1928b37 --- /dev/null +++ b/testutil/integration/common/factory/fund.go @@ -0,0 +1,51 @@ +// Copyright Tharsis Labs Ltd.(Evmos) +// SPDX-License-Identifier:ENCL-1.0(https://github.com/evmos/evmos/blob/main/LICENSE) + +package factory + +import ( + "fmt" + + sdktypes "github.com/cosmos/cosmos-sdk/types" + banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" + "github.com/xrplevm/node/v5/testutil/integration/common/keyring" +) + +// FundTxFactory is the interface that wraps the common methods to fund accounts +// via a bank send transaction +type FundTxFactory interface { + // FundAccount funds the given account with the given amount. + FundAccount(sender keyring.Key, receiver sdktypes.AccAddress, amount sdktypes.Coins) error +} + +// baseTxFactory is the struct of the basic tx factory +// to build and broadcast transactions. +// This is to simulate the behavior of a real user. +type fundTxFactory struct { + BaseTxFactory +} + +// newBaseTxFactory instantiates a new baseTxFactory +func newFundTxFactory(bf BaseTxFactory) FundTxFactory { + return &fundTxFactory{bf} +} + +// FundAccount funds the given account with the given amount of coins. +func (tf *fundTxFactory) FundAccount(sender keyring.Key, receiver sdktypes.AccAddress, coins sdktypes.Coins) error { + bankmsg := banktypes.NewMsgSend( + sender.AccAddr, + receiver, + coins, + ) + txArgs := CosmosTxArgs{Msgs: []sdktypes.Msg{bankmsg}} + txRes, err := tf.ExecuteCosmosTx(sender.Priv, txArgs) + if err != nil { + return err + } + + if txRes.Code != 0 { + return fmt.Errorf("transaction returned non-zero code %d", txRes.Code) + } + + return nil +} diff --git a/testutil/integration/common/factory/helper.go b/testutil/integration/common/factory/helper.go new file mode 100644 index 0000000..6950bd0 --- /dev/null +++ b/testutil/integration/common/factory/helper.go @@ -0,0 +1,119 @@ +// Copyright Tharsis Labs Ltd.(Evmos) +// SPDX-License-Identifier:ENCL-1.0(https://github.com/evmos/evmos/blob/main/LICENSE) + +package factory + +import ( + "math/big" + + errorsmod "cosmossdk.io/errors" + sdkmath "cosmossdk.io/math" + "github.com/cosmos/cosmos-sdk/client" + cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" + sdktypes "github.com/cosmos/cosmos-sdk/types" + authsigning "github.com/cosmos/cosmos-sdk/x/auth/signing" +) + +// EncodeTx encodes the tx using the txConfig's encoder. +func (tf *baseTxFactory) EncodeTx(tx sdktypes.Tx) ([]byte, error) { + txConfig := tf.ec.TxConfig + txBytes, err := txConfig.TxEncoder()(tx) + if err != nil { + return nil, errorsmod.Wrap(err, "failed to encode tx") + } + return txBytes, nil +} + +// buildTx builds a tx with the provided private key and txArgs +func (tf *baseTxFactory) buildTx(privKey cryptotypes.PrivKey, txArgs CosmosTxArgs) (client.TxBuilder, error) { + txConfig := tf.ec.TxConfig + txBuilder := txConfig.NewTxBuilder() + + if err := txBuilder.SetMsgs(txArgs.Msgs...); err != nil { + return nil, errorsmod.Wrap(err, "failed to set tx msgs") + } + + if txArgs.FeeGranter != nil { + txBuilder.SetFeeGranter(txArgs.FeeGranter) + } + + senderAddress := sdktypes.AccAddress(privKey.PubKey().Address().Bytes()) + + if txArgs.FeeGranter != nil { + txBuilder.SetFeeGranter(txArgs.FeeGranter) + } + + txBuilder.SetFeePayer(senderAddress) + + // need to sign the tx to simulate the tx to get the gas estimation + signMode, err := authsigning.APISignModeToInternal(txConfig.SignModeHandler().DefaultMode()) + if err != nil { + return nil, errorsmod.Wrap(err, "invalid sign mode") + } + signerData, err := tf.setSignatures(privKey, txBuilder, signMode) + if err != nil { + return nil, errorsmod.Wrap(err, "failed to set tx signatures") + } + + gasLimit, err := tf.estimateGas(txArgs, txBuilder) + if err != nil { + return nil, errorsmod.Wrap(err, "failed to estimate gas") + } + txBuilder.SetGasLimit(gasLimit) + + fees := txArgs.Fees + if fees.IsZero() { + fees, err = tf.calculateFees(txArgs.GasPrice, gasLimit) + if err != nil { + return nil, errorsmod.Wrap(err, "failed to calculate fees") + } + } + txBuilder.SetFeeAmount(fees) + + if err := tf.signWithPrivKey(privKey, txBuilder, signerData, signMode); err != nil { + return nil, errorsmod.Wrap(err, "failed to sign Cosmos Tx") + } + + return txBuilder, nil +} + +// calculateFees calculates the fees for the transaction. +func (tf *baseTxFactory) calculateFees(gasPrice *sdkmath.Int, gasLimit uint64) (sdktypes.Coins, error) { + denom := tf.network.GetDenom() + var fees sdktypes.Coins + if gasPrice != nil { + fees = sdktypes.Coins{{Denom: denom, Amount: gasPrice.MulRaw(int64(gasLimit))}} //#nosec G115 + } else { + resp, err := tf.grpcHandler.GetBaseFee() + if err != nil { + return sdktypes.Coins{}, errorsmod.Wrap(err, "failed to get base fee") + } + price := resp.BaseFee + fees = sdktypes.Coins{{Denom: denom, Amount: price.MulRaw(int64(gasLimit))}} //#nosec G115 + } + return fees, nil +} + +// estimateGas estimates the gas needed for the transaction. +func (tf *baseTxFactory) estimateGas(txArgs CosmosTxArgs, txBuilder client.TxBuilder) (uint64, error) { + txConfig := tf.ec.TxConfig + simulateBytes, err := txConfig.TxEncoder()(txBuilder.GetTx()) + if err != nil { + return 0, errorsmod.Wrap(err, "failed to encode tx") + } + + var gasLimit uint64 + if txArgs.Gas == nil { + simulateRes, err := tf.network.Simulate(simulateBytes) + if err != nil { + return 0, errorsmod.Wrap(err, "failed to simulate tx") + } + + gasAdj := new(big.Float).SetFloat64(GasAdjustment) + gasUsed := new(big.Float).SetUint64(simulateRes.GasInfo.GasUsed) + gasLimit, _ = gasAdj.Mul(gasAdj, gasUsed).Uint64() + } else { + gasLimit = *txArgs.Gas + } + return gasLimit, nil +} diff --git a/testutil/integration/common/factory/sign.go b/testutil/integration/common/factory/sign.go new file mode 100644 index 0000000..6686dfe --- /dev/null +++ b/testutil/integration/common/factory/sign.go @@ -0,0 +1,58 @@ +// Copyright Tharsis Labs Ltd.(Evmos) +// SPDX-License-Identifier:ENCL-1.0(https://github.com/evmos/evmos/blob/main/LICENSE) + +package factory + +import ( + "context" + + errorsmod "cosmossdk.io/errors" + "github.com/cosmos/cosmos-sdk/client" + cosmostx "github.com/cosmos/cosmos-sdk/client/tx" + cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" + sdktypes "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/tx/signing" + authsigning "github.com/cosmos/cosmos-sdk/x/auth/signing" +) + +// setSignatures is a helper function that sets the signature for +// the transaction in the tx builder. It returns the signerData to be used +// when signing the transaction (e.g. when calling signWithPrivKey) +func (tf *baseTxFactory) setSignatures(privKey cryptotypes.PrivKey, txBuilder client.TxBuilder, signMode signing.SignMode) (signerData authsigning.SignerData, err error) { + senderAddress := sdktypes.AccAddress(privKey.PubKey().Address().Bytes()) + account, err := tf.grpcHandler.GetAccount(senderAddress.String()) + if err != nil { + return signerData, err + } + sequence := account.GetSequence() + signerData = authsigning.SignerData{ + ChainID: tf.network.GetChainID(), + AccountNumber: account.GetAccountNumber(), + Sequence: sequence, + Address: senderAddress.String(), + PubKey: privKey.PubKey(), + } + + sigsV2 := signing.SignatureV2{ + PubKey: privKey.PubKey(), + Data: &signing.SingleSignatureData{ + SignMode: signMode, + Signature: nil, + }, + Sequence: sequence, + } + + return signerData, txBuilder.SetSignatures(sigsV2) +} + +// signWithPrivKey is a helper function that signs a transaction +// with the provided private key +func (tf *baseTxFactory) signWithPrivKey(privKey cryptotypes.PrivKey, txBuilder client.TxBuilder, signerData authsigning.SignerData, signMode signing.SignMode) error { + txConfig := tf.ec.TxConfig + signature, err := cosmostx.SignWithPrivKey(context.TODO(), signMode, signerData, txBuilder, privKey, txConfig, signerData.Sequence) + if err != nil { + return errorsmod.Wrap(err, "failed to sign tx") + } + + return txBuilder.SetSignatures(signature) +} diff --git a/testutil/integration/common/factory/staking.go b/testutil/integration/common/factory/staking.go new file mode 100644 index 0000000..72799a4 --- /dev/null +++ b/testutil/integration/common/factory/staking.go @@ -0,0 +1,88 @@ +// Copyright Tharsis Labs Ltd.(Evmos) +// SPDX-License-Identifier:ENCL-1.0(https://github.com/evmos/evmos/blob/main/LICENSE) + +package factory + +import ( + "fmt" + + "cosmossdk.io/math" + cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" + sdk "github.com/cosmos/cosmos-sdk/types" + stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" +) + +type StakingTxFactory interface { + // Delegate is a method to create and execute a MsgDelegate paying always the same fee amount + // The tx is included in a block and committed in the chain state + Delegate(delegatorPriv cryptotypes.PrivKey, validatorAddr string, amount sdk.Coin) error + // CreateValidator is a method to create and broadcast a MsgCreateValidator + CreateValidator(operatorPriv cryptotypes.PrivKey, pubKey cryptotypes.PubKey, selfDelegation sdk.Coin, description stakingtypes.Description, commission stakingtypes.CommissionRates, minSelfDelegation math.Int) error +} + +type stakingTxFactory struct { + BaseTxFactory +} + +func newStakingTxFactory(bf BaseTxFactory) StakingTxFactory { + return &stakingTxFactory{bf} +} + +// Delegate on behalf of the account associated with the given private key. +// The defined amount will delegated to the specified validator. +// The validator address should be in the format `evmosvaloper1...`. +func (tf *stakingTxFactory) Delegate(delegatorPriv cryptotypes.PrivKey, validatorAddr string, amount sdk.Coin) error { + delegatorAccAddr := sdk.AccAddress(delegatorPriv.PubKey().Address()) + + msgDelegate := stakingtypes.NewMsgDelegate( + delegatorAccAddr.String(), + validatorAddr, + amount, + ) + + // set gas and gas prices to pay the same fees + // every time this function is called + feesToPay := math.NewInt(1e16) + gas := uint64(400_000) + gasPrice := feesToPay.QuoRaw(int64(gas)) //#nosec G115 -- gas will not exceed int64 + + res, err := tf.CommitCosmosTx(delegatorPriv, CosmosTxArgs{ + Msgs: []sdk.Msg{msgDelegate}, + Gas: &gas, + GasPrice: &gasPrice, + }) + + if res.IsErr() { + return fmt.Errorf("tx result with code %d. Logs: %s", res.Code, res.Log) + } + + return err +} + +// CreateValidator executes the transaction to create a validator +// with the parameters specified +func (tf *stakingTxFactory) CreateValidator(operatorPriv cryptotypes.PrivKey, pubKey cryptotypes.PubKey, selfDelegation sdk.Coin, description stakingtypes.Description, commission stakingtypes.CommissionRates, minSelfDelegation math.Int) error { + operatorAccAddr := sdk.ValAddress(operatorPriv.PubKey().Address()) + + msgCreateValidator, err := stakingtypes.NewMsgCreateValidator( + operatorAccAddr.String(), + pubKey, + selfDelegation, + description, + commission, + minSelfDelegation, + ) + if err != nil { + return err + } + + resp, err := tf.ExecuteCosmosTx(operatorPriv, CosmosTxArgs{ + Msgs: []sdk.Msg{msgCreateValidator}, + }) + + if resp.Code != 0 { + err = fmt.Errorf("received error code %d on CreateValidator transaction. Logs: %s", resp.Code, resp.Log) + } + + return err +} diff --git a/testutil/integration/common/factory/types.go b/testutil/integration/common/factory/types.go new file mode 100644 index 0000000..52b6861 --- /dev/null +++ b/testutil/integration/common/factory/types.go @@ -0,0 +1,24 @@ +// Copyright Tharsis Labs Ltd.(Evmos) +// SPDX-License-Identifier:ENCL-1.0(https://github.com/evmos/evmos/blob/main/LICENSE) +package factory + +import ( + sdkmath "cosmossdk.io/math" + sdktypes "github.com/cosmos/cosmos-sdk/types" +) + +// CosmosTxArgs contains the params to create a cosmos tx +type CosmosTxArgs struct { + // ChainID is the chain's id in cosmos format, e.g. 'evmos_9000-1' + ChainID string + // Gas to be used on the tx + Gas *uint64 + // GasPrice to use on tx + GasPrice *sdkmath.Int + // Fees is the fee to be used on the tx (amount and denom) + Fees sdktypes.Coins + // FeeGranter is the account address of the fee granter + FeeGranter sdktypes.AccAddress + // Msgs slice of messages to include on the tx + Msgs []sdktypes.Msg +} diff --git a/testutil/integration/common/grpc/account.go b/testutil/integration/common/grpc/account.go new file mode 100644 index 0000000..b53cce0 --- /dev/null +++ b/testutil/integration/common/grpc/account.go @@ -0,0 +1,28 @@ +// Copyright Tharsis Labs Ltd.(Evmos) +// SPDX-License-Identifier:ENCL-1.0(https://github.com/evmos/evmos/blob/main/LICENSE) +package grpc + +import ( + "context" + + sdk "github.com/cosmos/cosmos-sdk/types" + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" +) + +// GetAccount returns the account for the given address. +func (gqh *IntegrationHandler) GetAccount(address string) (sdk.AccountI, error) { + authClient := gqh.network.GetAuthClient() + res, err := authClient.Account(context.Background(), &authtypes.QueryAccountRequest{ + Address: address, + }) + if err != nil { + return nil, err + } + + encodingCgf := gqh.network.GetEncodingConfig() + var acc sdk.AccountI + if err = encodingCgf.InterfaceRegistry.UnpackAny(res.Account, &acc); err != nil { + return nil, err + } + return acc, nil +} diff --git a/testutil/integration/common/grpc/authz.go b/testutil/integration/common/grpc/authz.go new file mode 100644 index 0000000..bd60044 --- /dev/null +++ b/testutil/integration/common/grpc/authz.go @@ -0,0 +1,117 @@ +// Copyright Tharsis Labs Ltd.(Evmos) +// SPDX-License-Identifier:ENCL-1.0(https://github.com/evmos/evmos/blob/main/LICENSE) + +package grpc + +import ( + "context" + + "github.com/cosmos/cosmos-sdk/x/authz" +) + +// GetGrants returns the grants for the given grantee and granter combination. +// +// NOTE: To extract the concrete authorizations, use the GetAuthorizations method. +func (gqh *IntegrationHandler) GetGrants(grantee, granter string) ([]*authz.Grant, error) { + authzClient := gqh.network.GetAuthzClient() + res, err := authzClient.Grants(context.Background(), &authz.QueryGrantsRequest{ + Grantee: grantee, + Granter: granter, + }) + if err != nil { + return nil, err + } + + return res.Grants, nil +} + +// GetGrantsByGrantee returns the grants for the given grantee. +// +// NOTE: To extract the concrete authorizations, use the GetAuthorizationsByGrantee method. +func (gqh *IntegrationHandler) GetGrantsByGrantee(grantee string) ([]*authz.GrantAuthorization, error) { + authzClient := gqh.network.GetAuthzClient() + res, err := authzClient.GranteeGrants(context.Background(), &authz.QueryGranteeGrantsRequest{ + Grantee: grantee, + }) + if err != nil { + return nil, err + } + + return res.Grants, nil +} + +// GetGrantsByGranter returns the grants for the given granter. +// +// NOTE: To extract the concrete authorizations, use the GetAuthorizationsByGranter method. +func (gqh *IntegrationHandler) GetGrantsByGranter(granter string) ([]*authz.GrantAuthorization, error) { + authzClient := gqh.network.GetAuthzClient() + res, err := authzClient.GranterGrants(context.Background(), &authz.QueryGranterGrantsRequest{ + Granter: granter, + }) + if err != nil { + return nil, err + } + + return res.Grants, nil +} + +// GetAuthorizations returns the concrete authorizations for the given grantee and granter combination. +func (gqh *IntegrationHandler) GetAuthorizations(grantee, granter string) ([]authz.Authorization, error) { + encodingCfg := gqh.network.GetEncodingConfig() + + grants, err := gqh.GetGrants(grantee, granter) + if err != nil { + return nil, err + } + + auths := make([]authz.Authorization, 0, len(grants)) + for _, grant := range grants { + var auth authz.Authorization + err := encodingCfg.InterfaceRegistry.UnpackAny(grant.Authorization, &auth) + if err != nil { + return nil, err + } + + auths = append(auths, auth) + } + + return auths, nil +} + +// GetAuthorizationsByGrantee returns the concrete authorizations for the given grantee. +func (gqh *IntegrationHandler) GetAuthorizationsByGrantee(grantee string) ([]authz.Authorization, error) { + grants, err := gqh.GetGrantsByGrantee(grantee) + if err != nil { + return nil, err + } + + return gqh.unpackGrantAuthzs(grants) +} + +// GetAuthorizationsByGranter returns the concrete authorizations for the given granter. +func (gqh *IntegrationHandler) GetAuthorizationsByGranter(granter string) ([]authz.Authorization, error) { + grants, err := gqh.GetGrantsByGranter(granter) + if err != nil { + return nil, err + } + + return gqh.unpackGrantAuthzs(grants) +} + +// unpackGrantAuthzs unpacks the given grant authorization. +func (gqh *IntegrationHandler) unpackGrantAuthzs(grantAuthzs []*authz.GrantAuthorization) ([]authz.Authorization, error) { + encodingCfg := gqh.network.GetEncodingConfig() + + auths := make([]authz.Authorization, 0, len(grantAuthzs)) + for _, grantAuthz := range grantAuthzs { + var auth authz.Authorization + err := encodingCfg.InterfaceRegistry.UnpackAny(grantAuthz.Authorization, &auth) + if err != nil { + return nil, err + } + + auths = append(auths, auth) + } + + return auths, nil +} diff --git a/testutil/integration/common/grpc/bank.go b/testutil/integration/common/grpc/bank.go new file mode 100644 index 0000000..2d6675e --- /dev/null +++ b/testutil/integration/common/grpc/bank.go @@ -0,0 +1,40 @@ +// Copyright Tharsis Labs Ltd.(Evmos) +// SPDX-License-Identifier:ENCL-1.0(https://github.com/evmos/evmos/blob/main/LICENSE) + +package grpc + +import ( + "context" + + sdktypes "github.com/cosmos/cosmos-sdk/types" + banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" +) + +// GetBalance returns the balance for the given address and denom. +func (gqh *IntegrationHandler) GetBalance(address sdktypes.AccAddress, denom string) (*banktypes.QueryBalanceResponse, error) { + bankClient := gqh.network.GetBankClient() + return bankClient.Balance(context.Background(), &banktypes.QueryBalanceRequest{ + Address: address.String(), + Denom: denom, + }) +} + +// GetAllBalances returns all the balances for the given address. +func (gqh *IntegrationHandler) GetAllBalances(address sdktypes.AccAddress) (*banktypes.QueryAllBalancesResponse, error) { + bankClient := gqh.network.GetBankClient() + return bankClient.AllBalances(context.Background(), &banktypes.QueryAllBalancesRequest{ + Address: address.String(), + }) +} + +// GetTotalSupply returns all the balances for the given address. +func (gqh *IntegrationHandler) GetTotalSupply() (*banktypes.QueryTotalSupplyResponse, error) { + bankClient := gqh.network.GetBankClient() + return bankClient.TotalSupply(context.Background(), &banktypes.QueryTotalSupplyRequest{}) +} + +// GetSpendableBalance returns the spendable balance for the given denomination. +func (gqh *IntegrationHandler) GetSpendableBalance(address sdktypes.AccAddress, denom string) (*banktypes.QuerySpendableBalanceByDenomResponse, error) { + bankClient := gqh.network.GetBankClient() + return bankClient.SpendableBalanceByDenom(context.Background(), &banktypes.QuerySpendableBalanceByDenomRequest{Address: address.String(), Denom: denom}) +} diff --git a/testutil/integration/common/grpc/distribution.go b/testutil/integration/common/grpc/distribution.go new file mode 100644 index 0000000..2e89b41 --- /dev/null +++ b/testutil/integration/common/grpc/distribution.go @@ -0,0 +1,56 @@ +// Copyright Tharsis Labs Ltd.(Evmos) +// SPDX-License-Identifier:ENCL-1.0(https://github.com/evmos/evmos/blob/main/LICENSE) +package grpc + +import ( + "context" + + distrtypes "github.com/cosmos/cosmos-sdk/x/distribution/types" +) + +// GetDelegationTotalRewards returns the total delegation rewards for the given delegator. +func (gqh *IntegrationHandler) GetDelegationTotalRewards(delegatorAddress string) (*distrtypes.QueryDelegationTotalRewardsResponse, error) { + distrClient := gqh.network.GetDistrClient() + return distrClient.DelegationTotalRewards(context.Background(), &distrtypes.QueryDelegationTotalRewardsRequest{ + DelegatorAddress: delegatorAddress, + }) +} + +// GetDelegationRewards returns the delegation rewards for the given delegator and validator. +func (gqh *IntegrationHandler) GetDelegationRewards(delegatorAddress string, validatorAddress string) (*distrtypes.QueryDelegationRewardsResponse, error) { + distrClient := gqh.network.GetDistrClient() + return distrClient.DelegationRewards(context.Background(), &distrtypes.QueryDelegationRewardsRequest{ + DelegatorAddress: delegatorAddress, + ValidatorAddress: validatorAddress, + }) +} + +// GetDelegatorWithdrawAddr returns the withdraw address the given delegator. +func (gqh *IntegrationHandler) GetDelegatorWithdrawAddr(delegatorAddress string) (*distrtypes.QueryDelegatorWithdrawAddressResponse, error) { + distrClient := gqh.network.GetDistrClient() + return distrClient.DelegatorWithdrawAddress(context.Background(), &distrtypes.QueryDelegatorWithdrawAddressRequest{ + DelegatorAddress: delegatorAddress, + }) +} + +// GetValidatorCommission returns the commission for the given validator. +func (gqh *IntegrationHandler) GetValidatorCommission(validatorAddress string) (*distrtypes.QueryValidatorCommissionResponse, error) { + distrClient := gqh.network.GetDistrClient() + return distrClient.ValidatorCommission(context.Background(), &distrtypes.QueryValidatorCommissionRequest{ + ValidatorAddress: validatorAddress, + }) +} + +// GetValidatorOutstandingRewards returns the delegation rewards for the given delegator and validator. +func (gqh *IntegrationHandler) GetValidatorOutstandingRewards(validatorAddress string) (*distrtypes.QueryValidatorOutstandingRewardsResponse, error) { + distrClient := gqh.network.GetDistrClient() + return distrClient.ValidatorOutstandingRewards(context.Background(), &distrtypes.QueryValidatorOutstandingRewardsRequest{ + ValidatorAddress: validatorAddress, + }) +} + +// GetCommunityPool queries the community pool coins. +func (gqh *IntegrationHandler) GetCommunityPool() (*distrtypes.QueryCommunityPoolResponse, error) { + distrClient := gqh.network.GetDistrClient() + return distrClient.CommunityPool(context.Background(), &distrtypes.QueryCommunityPoolRequest{}) +} diff --git a/testutil/integration/common/grpc/feemarket.go b/testutil/integration/common/grpc/feemarket.go new file mode 100644 index 0000000..0d87c6e --- /dev/null +++ b/testutil/integration/common/grpc/feemarket.go @@ -0,0 +1,13 @@ +package grpc + +import ( + "context" + + feemarkettypes "github.com/evmos/evmos/v20/x/feemarket/types" +) + +// GetBaseFee returns the base fee from the feemarket module. +func (gqh *IntegrationHandler) GetBaseFee() (*feemarkettypes.QueryBaseFeeResponse, error) { + feeMarketClient := gqh.network.GetFeeMarketClient() + return feeMarketClient.BaseFee(context.Background(), &feemarkettypes.QueryBaseFeeRequest{}) +} \ No newline at end of file diff --git a/testutil/integration/common/grpc/grpc.go b/testutil/integration/common/grpc/grpc.go new file mode 100644 index 0000000..ecf0932 --- /dev/null +++ b/testutil/integration/common/grpc/grpc.go @@ -0,0 +1,71 @@ +// Copyright Tharsis Labs Ltd.(Evmos) +// SPDX-License-Identifier:ENCL-1.0(https://github.com/evmos/evmos/blob/main/LICENSE) + +package grpc + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/authz" + banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" + distrtypes "github.com/cosmos/cosmos-sdk/x/distribution/types" + stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" + feemarkettypes "github.com/evmos/evmos/v20/x/feemarket/types" + network "github.com/xrplevm/node/v5/testutil/integration/common/network" +) + +// Handler is an interface that defines the common methods that are used to query +// the network's modules via gRPC. +type Handler interface { + // Account methods + GetAccount(address string) (sdk.AccountI, error) + + // Authz methods + GetAuthorizations(grantee, granter string) ([]authz.Authorization, error) + GetAuthorizationsByGrantee(grantee string) ([]authz.Authorization, error) + GetAuthorizationsByGranter(granter string) ([]authz.Authorization, error) + GetGrants(grantee, granter string) ([]*authz.Grant, error) + GetGrantsByGrantee(grantee string) ([]*authz.GrantAuthorization, error) + GetGrantsByGranter(granter string) ([]*authz.GrantAuthorization, error) + + // Bank methods + GetBalance(address sdk.AccAddress, denom string) (*banktypes.QueryBalanceResponse, error) + GetSpendableBalance(address sdk.AccAddress, denom string) (*banktypes.QuerySpendableBalanceByDenomResponse, error) + GetAllBalances(address sdk.AccAddress) (*banktypes.QueryAllBalancesResponse, error) + GetTotalSupply() (*banktypes.QueryTotalSupplyResponse, error) + + // Staking methods + GetDelegation(delegatorAddress string, validatorAddress string) (*stakingtypes.QueryDelegationResponse, error) + GetDelegatorDelegations(delegatorAddress string) (*stakingtypes.QueryDelegatorDelegationsResponse, error) + GetValidatorDelegations(validatorAddress string) (*stakingtypes.QueryValidatorDelegationsResponse, error) + GetRedelegations(delegatorAddress, srcValidator, dstValidator string) (*stakingtypes.QueryRedelegationsResponse, error) + GetValidatorUnbondingDelegations(validatorAddress string) (*stakingtypes.QueryValidatorUnbondingDelegationsResponse, error) + GetDelegatorUnbondingDelegations(delegatorAddress string) (*stakingtypes.QueryDelegatorUnbondingDelegationsResponse, error) + + // Distribution methods + GetDelegationTotalRewards(delegatorAddress string) (*distrtypes.QueryDelegationTotalRewardsResponse, error) + GetDelegationRewards(delegatorAddress string, validatorAddress string) (*distrtypes.QueryDelegationRewardsResponse, error) + GetDelegatorWithdrawAddr(delegatorAddress string) (*distrtypes.QueryDelegatorWithdrawAddressResponse, error) + GetValidatorCommission(validatorAddress string) (*distrtypes.QueryValidatorCommissionResponse, error) + GetValidatorOutstandingRewards(validatorAddress string) (*distrtypes.QueryValidatorOutstandingRewardsResponse, error) + GetCommunityPool() (*distrtypes.QueryCommunityPoolResponse, error) + GetBondedValidators() (*stakingtypes.QueryValidatorsResponse, error) + + // FeeMarket methods + GetBaseFee() (*feemarkettypes.QueryBaseFeeResponse, error) +} + +var _ Handler = (*IntegrationHandler)(nil) + +// IntegrationHandler is a helper struct to query the network's modules +// via gRPC. This is to simulate the behavior of a real user and avoid querying +// the modules directly. +type IntegrationHandler struct { + network network.Network +} + +// NewIntegrationHandler creates a new IntegrationHandler instance. +func NewIntegrationHandler(network network.Network) *IntegrationHandler { + return &IntegrationHandler{ + network: network, + } +} diff --git a/testutil/integration/common/grpc/staking.go b/testutil/integration/common/grpc/staking.go new file mode 100644 index 0000000..71cec10 --- /dev/null +++ b/testutil/integration/common/grpc/staking.go @@ -0,0 +1,68 @@ +// Copyright Tharsis Labs Ltd.(Evmos) +// SPDX-License-Identifier:ENCL-1.0(https://github.com/evmos/evmos/blob/main/LICENSE) +package grpc + +import ( + "context" + + stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" +) + +// GetDelegation returns the delegation for the given delegator and validator addresses. +func (gqh *IntegrationHandler) GetDelegation(delegatorAddress string, validatorAddress string) (*stakingtypes.QueryDelegationResponse, error) { + stakingClient := gqh.network.GetStakingClient() + return stakingClient.Delegation(context.Background(), &stakingtypes.QueryDelegationRequest{ + DelegatorAddr: delegatorAddress, + ValidatorAddr: validatorAddress, + }) +} + +// GetValidatorDelegations returns the delegations to a given validator. +func (gqh *IntegrationHandler) GetValidatorDelegations(validatorAddress string) (*stakingtypes.QueryValidatorDelegationsResponse, error) { + stakingClient := gqh.network.GetStakingClient() + return stakingClient.ValidatorDelegations(context.Background(), &stakingtypes.QueryValidatorDelegationsRequest{ + ValidatorAddr: validatorAddress, + }) +} + +// GetDelegatorDelegations returns the delegations to a given delegator. +func (gqh *IntegrationHandler) GetDelegatorDelegations(delegatorAddress string) (*stakingtypes.QueryDelegatorDelegationsResponse, error) { + stakingClient := gqh.network.GetStakingClient() + return stakingClient.DelegatorDelegations(context.Background(), &stakingtypes.QueryDelegatorDelegationsRequest{ + DelegatorAddr: delegatorAddress, + }) +} + +// GetRedelegations returns the redelegations to a given delegator and validators. +func (gqh *IntegrationHandler) GetRedelegations(delegatorAddress, srcValidator, dstValidator string) (*stakingtypes.QueryRedelegationsResponse, error) { + stakingClient := gqh.network.GetStakingClient() + return stakingClient.Redelegations(context.Background(), &stakingtypes.QueryRedelegationsRequest{ + DelegatorAddr: delegatorAddress, + SrcValidatorAddr: srcValidator, + DstValidatorAddr: dstValidator, + }) +} + +// GetValidatorUnbondingDelegations returns the unbonding delegations to a given validator. +func (gqh *IntegrationHandler) GetValidatorUnbondingDelegations(validatorAddress string) (*stakingtypes.QueryValidatorUnbondingDelegationsResponse, error) { + stakingClient := gqh.network.GetStakingClient() + return stakingClient.ValidatorUnbondingDelegations(context.Background(), &stakingtypes.QueryValidatorUnbondingDelegationsRequest{ + ValidatorAddr: validatorAddress, + }) +} + +// GetDelegatorUnbondingDelegations returns all the unbonding delegations for given delegator. +func (gqh *IntegrationHandler) GetDelegatorUnbondingDelegations(delegatorAddress string) (*stakingtypes.QueryDelegatorUnbondingDelegationsResponse, error) { + stakingClient := gqh.network.GetStakingClient() + return stakingClient.DelegatorUnbondingDelegations(context.Background(), &stakingtypes.QueryDelegatorUnbondingDelegationsRequest{ + DelegatorAddr: delegatorAddress, + }) +} + +// GetValidators returns the list of all bonded validators. +func (gqh *IntegrationHandler) GetBondedValidators() (*stakingtypes.QueryValidatorsResponse, error) { + stakingClient := gqh.network.GetStakingClient() + return stakingClient.Validators(context.Background(), &stakingtypes.QueryValidatorsRequest{ + Status: stakingtypes.BondStatusBonded, + }) +} diff --git a/testutil/integration/common/network/network.go b/testutil/integration/common/network/network.go index 39108ba..57a5666 100644 --- a/testutil/integration/common/network/network.go +++ b/testutil/integration/common/network/network.go @@ -1,7 +1,7 @@ // Copyright Tharsis Labs Ltd.(Evmos) // SPDX-License-Identifier:ENCL-1.0(https://github.com/evmos/evmos/blob/main/LICENSE) -package commonnetwork +package network import ( "testing" @@ -11,8 +11,13 @@ import ( sdktypes "github.com/cosmos/cosmos-sdk/types" sdktestutil "github.com/cosmos/cosmos-sdk/types/module/testutil" txtypes "github.com/cosmos/cosmos-sdk/types/tx" + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" + authz "github.com/cosmos/cosmos-sdk/x/authz" + banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" + distrtypes "github.com/cosmos/cosmos-sdk/x/distribution/types" stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" ibctesting "github.com/cosmos/ibc-go/v8/testing" + feemarkettypes "github.com/evmos/evmos/v20/x/feemarket/types" ) // Network is the interface that wraps the common methods to interact with integration test network. @@ -31,11 +36,12 @@ type Network interface { NextBlockWithTxs(txBytes ...[]byte) (*abcitypes.ResponseFinalizeBlock, error) // Clients - // GetAuthClient() authtypes.QueryClient - // GetAuthzClient() authz.QueryClient - // GetBankClient() banktypes.QueryClient - // GetStakingClient() stakingtypes.QueryClient - // GetDistrClient() distrtypes.QueryClient + GetAuthClient() authtypes.QueryClient + GetAuthzClient() authz.QueryClient + GetBankClient() banktypes.QueryClient + GetStakingClient() stakingtypes.QueryClient + GetDistrClient() distrtypes.QueryClient + GetFeeMarketClient() feemarkettypes.QueryClient BroadcastTxSync(txBytes []byte) (abcitypes.ExecTxResult, error) Simulate(txBytes []byte) (*txtypes.SimulateResponse, error) diff --git a/testutil/integration/exrp/integration/network.go b/testutil/integration/exrp/integration/network.go index 56d7480..fcc14d1 100644 --- a/testutil/integration/exrp/integration/network.go +++ b/testutil/integration/exrp/integration/network.go @@ -291,12 +291,12 @@ func (n *IntegrationNetwork) BroadcastTxSync(txBytes []byte) (abcitypes.ExecTxRe header.Time = newBlockTime req := exrpcommon.BuildFinalizeBlockReq(header, n.valSet.Validators, txBytes) - + // dont include the DecidedLastCommit because we're not committing the changes // here, is just for broadcasting the tx. To persist the changes, use the // NextBlock or NextBlockAfter functions req.DecidedLastCommit = abcitypes.CommitInfo{} - + blockRes, err := n.app.BaseApp.FinalizeBlock(req) if err != nil { return abcitypes.ExecTxResult{}, err @@ -329,3 +329,4 @@ func (n *IntegrationNetwork) CheckTx(txBytes []byte) (*abcitypes.ResponseCheckTx } return res, nil } + From dbd53da8a7fc88b9c6edc1fedb176b9ea886e4d6 Mon Sep 17 00:00:00 2001 From: GuillemGarciaDev Date: Wed, 8 Jan 2025 12:51:01 +0100 Subject: [PATCH 20/60] fix(tests): update `integration` and `upgrade` test suites --- tests/integration/network.go | 20 ++++++++++---------- tests/integration/suite.go | 29 ++++++++++++++++++++++++++--- tests/integration/suite_test.go | 26 -------------------------- tests/upgrade/network.go | 20 ++++++++++---------- tests/upgrade/suite_test.go | 4 ++-- 5 files changed, 48 insertions(+), 51 deletions(-) diff --git a/tests/integration/network.go b/tests/integration/network.go index 38029d6..746e8a7 100644 --- a/tests/integration/network.go +++ b/tests/integration/network.go @@ -33,42 +33,42 @@ func (n *Network) SetupSdkConfig() { exrpcommon.SetupSdkConfig() } -func (n *Network) ERC20Client() erc20types.QueryClient { +func (n *Network) GetERC20Client() erc20types.QueryClient { return exrpcommon.GetERC20Client(n) } -func (n *Network) EvmClient() evmtypes.QueryClient { +func (n *Network) GetEvmClient() evmtypes.QueryClient { return exrpcommon.GetEvmClient(n) } -func (n *Network) GovClient() govtypes.QueryClient { +func (n *Network) GetGovClient() govtypes.QueryClient { return exrpcommon.GetGovClient(n) } -func (n *Network) BankClient() banktypes.QueryClient { +func (n *Network) GetBankClient() banktypes.QueryClient { return exrpcommon.GetBankClient(n) } -func (n *Network) FeeMarketClient() feemarkettypes.QueryClient { +func (n *Network) GetFeeMarketClient() feemarkettypes.QueryClient { return exrpcommon.GetFeeMarketClient(n) } -func (n *Network) AuthClient() authtypes.QueryClient { +func (n *Network) GetAuthClient() authtypes.QueryClient { return exrpcommon.GetAuthClient(n) } -func (n *Network) AuthzClient() authz.QueryClient { +func (n *Network) GetAuthzClient() authz.QueryClient { return exrpcommon.GetAuthzClient(n) } -func (n *Network) StakingClient() stakingtypes.QueryClient { +func (n *Network) GetStakingClient() stakingtypes.QueryClient { return exrpcommon.GetStakingClient(n) } -func (n *Network) DistrClient() distrtypes.QueryClient { +func (n *Network) GetDistrClient() distrtypes.QueryClient { return exrpcommon.GetDistrClient(n) } -func (n *Network) PoaClient() poatypes.QueryClient { +func (n *Network) GetPoaClient() poatypes.QueryClient { return exrpcommon.GetPoaClient(n) } diff --git a/tests/integration/suite.go b/tests/integration/suite.go index fcf3256..2f14862 100644 --- a/tests/integration/suite.go +++ b/tests/integration/suite.go @@ -2,7 +2,12 @@ package integration import ( sdk "github.com/cosmos/cosmos-sdk/types" + evmtypes "github.com/evmos/evmos/v20/x/evm/types" "github.com/stretchr/testify/suite" + "github.com/xrplevm/node/v5/app" + factory "github.com/xrplevm/node/v5/testutil/integration/common/factory" + "github.com/xrplevm/node/v5/testutil/integration/common/grpc" + "github.com/xrplevm/node/v5/testutil/integration/common/keyring" exrpcommon "github.com/xrplevm/node/v5/testutil/integration/exrp/common" ) @@ -10,6 +15,9 @@ type TestSuite struct { suite.Suite network *Network + keyring keyring.Keyring + factory factory.CoreTxFactory + grpcHandler grpc.Handler } func (s *TestSuite) Network() *Network { @@ -17,14 +25,29 @@ func (s *TestSuite) Network() *Network { } func (s *TestSuite) SetupTest() { - // Setup the SDK config s.network.SetupSdkConfig() - s.Require().Equal(sdk.GetConfig().GetBech32AccountAddrPrefix(), "ethm") - // Check that the network was created successfully + kr := keyring.New(3) + + customGenesis := exrpcommon.CustomGenesisState{} + + evmGen := evmtypes.DefaultGenesisState() + + evmGen.Params.EvmDenom = app.BaseDenom + + customGenesis[evmtypes.ModuleName] = evmGen + s.network = NewIntegrationNetwork( + exrpcommon.WithPreFundedAccounts(kr.GetAllAccAddrs()...), exrpcommon.WithAmountOfValidators(5), + exrpcommon.WithCustomGenesis(customGenesis), ) s.Require().NotNil(s.network) + + grpcHandler := grpc.NewIntegrationHandler(s.network) + + s.factory = factory.New(s.network, grpcHandler) + s.keyring = kr + s.grpcHandler = grpcHandler } diff --git a/tests/integration/suite_test.go b/tests/integration/suite_test.go index e740f3d..fc13aa6 100644 --- a/tests/integration/suite_test.go +++ b/tests/integration/suite_test.go @@ -3,35 +3,9 @@ package integration import ( "testing" - banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" - stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" "github.com/stretchr/testify/suite" - "github.com/xrplevm/node/v5/app" ) func TestIntegrationTestSuite(t *testing.T) { suite.Run(t, new(TestSuite)) } - -func (s *TestSuite) TestIntegration() { - denom := s.Network().GetDenom() - s.Require().NotEmpty(denom) - s.Require().Equal(app.BaseDenom, denom) - - balances, err := s.Network().BankClient().AllBalances(s.network.GetContext(), &banktypes.QueryAllBalancesRequest{ - Address: "ethm1fl48vsnmsdzcv85q5d2q4z5ajdha8yu3w48d64", - }) - - s.T().Log("balances", balances) - s.Require().NoError(err) - - err = s.network.NextBlock() - s.Require().NoError(err) - - res, err := s.Network().StakingClient().Validators(s.network.GetContext(), &stakingtypes.QueryValidatorsRequest{}) - s.Require().NoError(err) - - s.T().Log("validators", len(res.Validators)) - - s.Require().Equal(len(res.Validators), len(s.network.GetValidators())) -} diff --git a/tests/upgrade/network.go b/tests/upgrade/network.go index 3273327..a71a2d1 100644 --- a/tests/upgrade/network.go +++ b/tests/upgrade/network.go @@ -33,42 +33,42 @@ func (n *UpgradeTestNetwork) SetupSdkConfig() { exrpcommon.SetupSdkConfig() } -func (n *UpgradeTestNetwork) ERC20Client() erc20types.QueryClient { +func (n *UpgradeTestNetwork) GetERC20Client() erc20types.QueryClient { return exrpcommon.GetERC20Client(n) } -func (n *UpgradeTestNetwork) EvmClient() evmtypes.QueryClient { +func (n *UpgradeTestNetwork) GetEvmClient() evmtypes.QueryClient { return exrpcommon.GetEvmClient(n) } -func (n *UpgradeTestNetwork) GovClient() govtypes.QueryClient { +func (n *UpgradeTestNetwork) GetGovClient() govtypes.QueryClient { return exrpcommon.GetGovClient(n) } -func (n *UpgradeTestNetwork) BankClient() banktypes.QueryClient { +func (n *UpgradeTestNetwork) GetBankClient() banktypes.QueryClient { return exrpcommon.GetBankClient(n) } -func (n *UpgradeTestNetwork) FeeMarketClient() feemarkettypes.QueryClient { +func (n *UpgradeTestNetwork) GetFeeMarketClient() feemarkettypes.QueryClient { return exrpcommon.GetFeeMarketClient(n) } -func (n *UpgradeTestNetwork) AuthClient() authtypes.QueryClient { +func (n *UpgradeTestNetwork) GetAuthClient() authtypes.QueryClient { return exrpcommon.GetAuthClient(n) } -func (n *UpgradeTestNetwork) AuthzClient() authz.QueryClient { +func (n *UpgradeTestNetwork) GetAuthzClient() authz.QueryClient { return exrpcommon.GetAuthzClient(n) } -func (n *UpgradeTestNetwork) StakingClient() stakingtypes.QueryClient { +func (n *UpgradeTestNetwork) GetStakingClient() stakingtypes.QueryClient { return exrpcommon.GetStakingClient(n) } -func (n *UpgradeTestNetwork) DistrClient() distrtypes.QueryClient { +func (n *UpgradeTestNetwork) GetDistrClient() distrtypes.QueryClient { return exrpcommon.GetDistrClient(n) } -func (n *UpgradeTestNetwork) PoaClient() poatypes.QueryClient { +func (n *UpgradeTestNetwork) GetPoaClient() poatypes.QueryClient { return exrpcommon.GetPoaClient(n) } diff --git a/tests/upgrade/suite_test.go b/tests/upgrade/suite_test.go index d46f384..4de919c 100644 --- a/tests/upgrade/suite_test.go +++ b/tests/upgrade/suite_test.go @@ -18,7 +18,7 @@ func (s *UpgradeTestSuite) TestUpgrade() { s.Require().NotEmpty(denom) s.Require().Equal(denom, app.BaseDenom) - balances, err := s.Network().BankClient().AllBalances(s.network.GetContext(), &banktypes.QueryAllBalancesRequest{ + balances, err := s.Network().GetBankClient().AllBalances(s.network.GetContext(), &banktypes.QueryAllBalancesRequest{ Address: "ethm1fl48vsnmsdzcv85q5d2q4z5ajdha8yu3w48d64", }) @@ -28,7 +28,7 @@ func (s *UpgradeTestSuite) TestUpgrade() { err = s.network.NextBlock() s.Require().NoError(err) - res, err := s.Network().StakingClient().Validators(s.network.GetContext(), &stakingtypes.QueryValidatorsRequest{}) + res, err := s.Network().GetStakingClient().Validators(s.network.GetContext(), &stakingtypes.QueryValidatorsRequest{}) s.Require().NoError(err) s.T().Log("validators", len(res.Validators)) From eb38fba44bb92dc3b43263dd70dc5584ff544d29 Mon Sep 17 00:00:00 2001 From: GuillemGarciaDev Date: Wed, 8 Jan 2025 12:51:29 +0100 Subject: [PATCH 21/60] fix: linting --- tests/integration/suite.go | 6 +++--- testutil/integration/common/grpc/feemarket.go | 2 +- testutil/integration/exrp/integration/network.go | 5 ++--- 3 files changed, 6 insertions(+), 7 deletions(-) diff --git a/tests/integration/suite.go b/tests/integration/suite.go index 2f14862..848e610 100644 --- a/tests/integration/suite.go +++ b/tests/integration/suite.go @@ -14,9 +14,9 @@ import ( type TestSuite struct { suite.Suite - network *Network - keyring keyring.Keyring - factory factory.CoreTxFactory + network *Network + keyring keyring.Keyring + factory factory.CoreTxFactory grpcHandler grpc.Handler } diff --git a/testutil/integration/common/grpc/feemarket.go b/testutil/integration/common/grpc/feemarket.go index 0d87c6e..7fa9ee3 100644 --- a/testutil/integration/common/grpc/feemarket.go +++ b/testutil/integration/common/grpc/feemarket.go @@ -10,4 +10,4 @@ import ( func (gqh *IntegrationHandler) GetBaseFee() (*feemarkettypes.QueryBaseFeeResponse, error) { feeMarketClient := gqh.network.GetFeeMarketClient() return feeMarketClient.BaseFee(context.Background(), &feemarkettypes.QueryBaseFeeRequest{}) -} \ No newline at end of file +} diff --git a/testutil/integration/exrp/integration/network.go b/testutil/integration/exrp/integration/network.go index fcc14d1..56d7480 100644 --- a/testutil/integration/exrp/integration/network.go +++ b/testutil/integration/exrp/integration/network.go @@ -291,12 +291,12 @@ func (n *IntegrationNetwork) BroadcastTxSync(txBytes []byte) (abcitypes.ExecTxRe header.Time = newBlockTime req := exrpcommon.BuildFinalizeBlockReq(header, n.valSet.Validators, txBytes) - + // dont include the DecidedLastCommit because we're not committing the changes // here, is just for broadcasting the tx. To persist the changes, use the // NextBlock or NextBlockAfter functions req.DecidedLastCommit = abcitypes.CommitInfo{} - + blockRes, err := n.app.BaseApp.FinalizeBlock(req) if err != nil { return abcitypes.ExecTxResult{}, err @@ -329,4 +329,3 @@ func (n *IntegrationNetwork) CheckTx(txBytes []byte) (*abcitypes.ResponseCheckTx } return res, nil } - From bdd6458ea139b69ee5f7cc1479a2c42a7c4db177 Mon Sep 17 00:00:00 2001 From: GuillemGarciaDev Date: Thu, 9 Jan 2025 17:13:55 +0100 Subject: [PATCH 22/60] feat(testutil): add `slashing` keeper to integration network --- tests/integration/network.go | 5 +++++ testutil/integration/exrp/common/clients.go | 9 +++++++++ testutil/integration/exrp/integration/keepers.go | 5 +++++ testutil/integration/exrp/upgrade/keepers.go | 5 +++++ 4 files changed, 24 insertions(+) diff --git a/tests/integration/network.go b/tests/integration/network.go index 746e8a7..0287ac4 100644 --- a/tests/integration/network.go +++ b/tests/integration/network.go @@ -6,6 +6,7 @@ import ( banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" distrtypes "github.com/cosmos/cosmos-sdk/x/distribution/types" govtypes "github.com/cosmos/cosmos-sdk/x/gov/types/v1" + slashingtypes "github.com/cosmos/cosmos-sdk/x/slashing/types" stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" erc20types "github.com/evmos/evmos/v20/x/erc20/types" evmtypes "github.com/evmos/evmos/v20/x/evm/types" @@ -65,6 +66,10 @@ func (n *Network) GetStakingClient() stakingtypes.QueryClient { return exrpcommon.GetStakingClient(n) } +func (n *Network) GetSlashingClient() slashingtypes.QueryClient { + return exrpcommon.GetSlashingClient(n) +} + func (n *Network) GetDistrClient() distrtypes.QueryClient { return exrpcommon.GetDistrClient(n) } diff --git a/testutil/integration/exrp/common/clients.go b/testutil/integration/exrp/common/clients.go index 9c546d2..d1e2086 100644 --- a/testutil/integration/exrp/common/clients.go +++ b/testutil/integration/exrp/common/clients.go @@ -16,6 +16,8 @@ import ( distrtypes "github.com/cosmos/cosmos-sdk/x/distribution/types" govkeeper "github.com/cosmos/cosmos-sdk/x/gov/keeper" govtypes "github.com/cosmos/cosmos-sdk/x/gov/types/v1" + slashingkeeper "github.com/cosmos/cosmos-sdk/x/slashing/keeper" + slashingtypes "github.com/cosmos/cosmos-sdk/x/slashing/types" stakingkeeper "github.com/cosmos/cosmos-sdk/x/staking/keeper" stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" @@ -38,6 +40,7 @@ type NetworkKeepers interface { GovKeeper() *govkeeper.Keeper BankKeeper() bankkeeper.Keeper StakingKeeper() *stakingkeeper.Keeper + SlashingKeeper() slashingkeeper.Keeper DistrKeeper() distrkeeper.Keeper AccountKeeper() authkeeper.AccountKeeper AuthzKeeper() authzkeeper.Keeper @@ -101,6 +104,12 @@ func GetStakingClient(n NetworkKeepers) stakingtypes.QueryClient { return stakingtypes.NewQueryClient(queryHelper) } +func GetSlashingClient(n NetworkKeepers) slashingtypes.QueryClient { + queryHelper := getQueryHelper(n.GetContext(), n.GetEncodingConfig()) + slashingtypes.RegisterQueryServer(queryHelper, slashingkeeper.Querier{Keeper: n.SlashingKeeper()}) + return slashingtypes.NewQueryClient(queryHelper) +} + func GetDistrClient(n NetworkKeepers) distrtypes.QueryClient { queryHelper := getQueryHelper(n.GetContext(), n.GetEncodingConfig()) distrtypes.RegisterQueryServer(queryHelper, distrkeeper.Querier{Keeper: n.DistrKeeper()}) diff --git a/testutil/integration/exrp/integration/keepers.go b/testutil/integration/exrp/integration/keepers.go index 42b5244..2cab201 100644 --- a/testutil/integration/exrp/integration/keepers.go +++ b/testutil/integration/exrp/integration/keepers.go @@ -6,6 +6,7 @@ import ( bankkeeper "github.com/cosmos/cosmos-sdk/x/bank/keeper" distrkeeper "github.com/cosmos/cosmos-sdk/x/distribution/keeper" govkeeper "github.com/cosmos/cosmos-sdk/x/gov/keeper" + slashingkeeper "github.com/cosmos/cosmos-sdk/x/slashing/keeper" stakingkeeper "github.com/cosmos/cosmos-sdk/x/staking/keeper" erc20keeper "github.com/evmos/evmos/v20/x/erc20/keeper" @@ -34,6 +35,10 @@ func (n *IntegrationNetwork) StakingKeeper() *stakingkeeper.Keeper { return n.app.StakingKeeper.Keeper } +func (n *IntegrationNetwork) SlashingKeeper() slashingkeeper.Keeper { + return n.app.SlashingKeeper +} + func (n *IntegrationNetwork) DistrKeeper() distrkeeper.Keeper { return n.app.DistrKeeper } diff --git a/testutil/integration/exrp/upgrade/keepers.go b/testutil/integration/exrp/upgrade/keepers.go index dc4cc6c..3bfd973 100644 --- a/testutil/integration/exrp/upgrade/keepers.go +++ b/testutil/integration/exrp/upgrade/keepers.go @@ -6,6 +6,7 @@ import ( bankkeeper "github.com/cosmos/cosmos-sdk/x/bank/keeper" distrkeeper "github.com/cosmos/cosmos-sdk/x/distribution/keeper" govkeeper "github.com/cosmos/cosmos-sdk/x/gov/keeper" + slashingkeeper "github.com/cosmos/cosmos-sdk/x/slashing/keeper" stakingkeeper "github.com/cosmos/cosmos-sdk/x/staking/keeper" erc20keeper "github.com/evmos/evmos/v20/x/erc20/keeper" @@ -34,6 +35,10 @@ func (n *UpgradeIntegrationNetwork) StakingKeeper() *stakingkeeper.Keeper { return n.app.StakingKeeper.Keeper } +func (n *UpgradeIntegrationNetwork) SlashingKeeper() slashingkeeper.Keeper { + return n.app.SlashingKeeper +} + func (n *UpgradeIntegrationNetwork) DistrKeeper() distrkeeper.Keeper { return n.app.DistrKeeper } From ffc4e8ab805f4834d7cd0c9fb460fcb52dd684e9 Mon Sep 17 00:00:00 2001 From: GuillemGarciaDev Date: Thu, 9 Jan 2025 17:14:23 +0100 Subject: [PATCH 23/60] feat(testutil): add `BondDenom` config --- testutil/integration/exrp/common/config.go | 8 ++++++++ testutil/integration/exrp/common/network.go | 1 + testutil/integration/exrp/integration/network.go | 5 +++++ testutil/integration/exrp/upgrade/network.go | 5 +++++ 4 files changed, 19 insertions(+) diff --git a/testutil/integration/exrp/common/config.go b/testutil/integration/exrp/common/config.go index fbf6431..f49e17f 100644 --- a/testutil/integration/exrp/common/config.go +++ b/testutil/integration/exrp/common/config.go @@ -24,6 +24,7 @@ type Config struct { AmountOfValidators int PreFundedAccounts []sdktypes.AccAddress Balances []banktypes.Balance + BondDenom string Denom string CustomGenesisState CustomGenesisState GenesisBytes []byte @@ -83,6 +84,13 @@ func WithBalances(balances ...banktypes.Balance) ConfigOption { } } +// WithBondDenom sets the bond denom for the network. +func WithBondDenom(denom string) ConfigOption { + return func(cfg *Config) { + cfg.BondDenom = denom + } +} + // WithDenom sets the denom for the network. func WithDenom(denom string) ConfigOption { return func(cfg *Config) { diff --git a/testutil/integration/exrp/common/network.go b/testutil/integration/exrp/common/network.go index 44ef8ea..75ad0a3 100644 --- a/testutil/integration/exrp/common/network.go +++ b/testutil/integration/exrp/common/network.go @@ -31,6 +31,7 @@ type Network interface { // Getters GetContext() sdktypes.Context GetChainID() string + GetBondDenom() string GetDenom() string GetOtherDenoms() []string GetValidators() []stakingtypes.Validator diff --git a/testutil/integration/exrp/integration/network.go b/testutil/integration/exrp/integration/network.go index 56d7480..9338de2 100644 --- a/testutil/integration/exrp/integration/network.go +++ b/testutil/integration/exrp/integration/network.go @@ -259,6 +259,11 @@ func (n *IntegrationNetwork) GetDenom() string { return n.cfg.Denom } +// GetBondDenom returns the network's bond denom +func (n *IntegrationNetwork) GetBondDenom() string { + return n.cfg.BondDenom +} + // GetOtherDenoms returns network's other supported denoms func (n *IntegrationNetwork) GetOtherDenoms() []string { return n.cfg.OtherCoinDenom diff --git a/testutil/integration/exrp/upgrade/network.go b/testutil/integration/exrp/upgrade/network.go index 4d31bae..f21390c 100644 --- a/testutil/integration/exrp/upgrade/network.go +++ b/testutil/integration/exrp/upgrade/network.go @@ -249,6 +249,11 @@ func (n *UpgradeIntegrationNetwork) GetDenom() string { return n.cfg.Denom } +// GetBondDenom returns the network's bond denom +func (n *UpgradeIntegrationNetwork) GetBondDenom() string { + return n.cfg.BondDenom +} + // GetOtherDenoms returns network's other supported denoms func (n *UpgradeIntegrationNetwork) GetOtherDenoms() []string { return n.cfg.OtherCoinDenom From c6a75125aa5d939414a7129793c86d62e06af872 Mon Sep 17 00:00:00 2001 From: GuillemGarciaDev Date: Thu, 9 Jan 2025 17:14:53 +0100 Subject: [PATCH 24/60] feat(tests): add `Jail` and `Tombstone` remove validator tests --- tests/integration/poa_test.go | 312 ++++++++++++++++++++++++++++++---- tests/integration/suite.go | 6 +- 2 files changed, 288 insertions(+), 30 deletions(-) diff --git a/tests/integration/poa_test.go b/tests/integration/poa_test.go index 598db73..c8dac4d 100644 --- a/tests/integration/poa_test.go +++ b/tests/integration/poa_test.go @@ -8,49 +8,44 @@ import ( sdktypes "github.com/cosmos/cosmos-sdk/types" simtypes "github.com/cosmos/cosmos-sdk/types/simulation" banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" + stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" "github.com/stretchr/testify/require" poatypes "github.com/xrplevm/node/v5/x/poa/types" ) -func (s *TestSuite) TestRemoveValidator() { +func (s *TestSuite) TestRemoveValidator_UnexistingValidator() { // Generate a random account randomAccs := simtypes.RandomAccounts(rand.New(rand.NewSource(time.Now().UnixNano())), 1) randomAcc := randomAccs[0] - // Validators - validators := s.Network().GetValidators() - require.NotZero(s.T(), len(validators)) - - valAddr, err := sdktypes.ValAddressFromBech32(validators[0].OperatorAddress) - require.NoError(s.T(), err) - valAccAddr := sdktypes.AccAddress(valAddr) - tt := []struct { - name string - valAddress string + name string + valAddress string expectedError error - beforeRun func() - afterRun func() + beforeRun func() + afterRun func() }{ { - name: "remove unexisting validator - random address - no balance", - valAddress: randomAcc.Address.String(), + name: "remove unexisting validator - random address - no balance", + valAddress: randomAcc.Address.String(), expectedError: poatypes.ErrAddressHasNoTokens, beforeRun: func() { balance, err := s.Network().GetBankClient().Balance( s.Network().GetContext(), &banktypes.QueryBalanceRequest{ Address: randomAcc.Address.String(), - Denom: s.Network().GetDenom(), + Denom: s.Network().GetBondDenom(), }, ) require.NoError(s.T(), err) - require.Equal(s.T(), balance.Balance.Amount, sdkmath.NewInt(0)) + + // Check account has no balance + require.True(s.T(), balance.Balance.Amount.IsZero()) }, }, { - name: "remove unexisting validator - random address - with balance", - valAddress: randomAcc.Address.String(), + name: "remove unexisting validator - random address - with balance", + valAddress: randomAcc.Address.String(), expectedError: poatypes.ErrAddressHasNoTokens, beforeRun: func() { coins := sdktypes.NewCoin( @@ -58,7 +53,7 @@ func (s *TestSuite) TestRemoveValidator() { sdkmath.NewInt(100000000000), ) - err = s.factory.FundAccount( + err := s.factory.FundAccount( s.keyring.GetKey(0), randomAcc.Address, sdktypes.NewCoins( @@ -72,37 +67,118 @@ func (s *TestSuite) TestRemoveValidator() { s.Network().GetContext(), &banktypes.QueryBalanceRequest{ Address: randomAcc.Address.String(), - Denom: s.Network().GetDenom(), + Denom: s.Network().GetBondDenom(), }, ) require.NoError(s.T(), err) - require.Equal(s.T(), coins.Amount.BigInt(), balance.Balance.Amount.BigInt()) + + // Check account has been funded + require.True(s.T(), balance.Balance.Amount.IsZero()) }, afterRun: func() { balance, err := s.Network().GetBankClient().Balance( s.Network().GetContext(), &banktypes.QueryBalanceRequest{ Address: randomAcc.Address.String(), - Denom: s.Network().GetDenom(), + Denom: s.Network().GetBondDenom(), }, ) require.NoError(s.T(), err) - require.Equal(s.T(), balance.Balance.Amount, sdkmath.NewInt(0)) + + // Check account has been emptied + require.True(s.T(), balance.Balance.Amount.IsZero()) }, }, + } + + for _, tc := range tt { + s.Run(tc.name, func() { + if tc.beforeRun != nil { + tc.beforeRun() + } + + err := s.Network().PoaKeeper().ExecuteRemoveValidator( + s.Network().GetContext(), + tc.valAddress, + ) + + if tc.expectedError != nil && err != nil { + require.Error(s.T(), err) + require.ErrorIs(s.T(), err, tc.expectedError) + } else { + require.NoError(s.T(), err) + } + + if tc.afterRun != nil { + tc.afterRun() + } + }) + } +} + +func (s *TestSuite) TestRemoveValidator_ExistingValidator_StatusBonded() { + // Validators + validators := s.Network().GetValidators() + require.NotZero(s.T(), len(validators)) + + valAddr, err := sdktypes.ValAddressFromBech32(validators[0].OperatorAddress) + require.NoError(s.T(), err) + valAccAddr := sdktypes.AccAddress(valAddr) + + tt := []struct { + name string + valAddress string + expectedError error + beforeRun func() + afterRun func() + }{ { - name: "remove existing validator - with unbonding delegations - with tokens", - valAddress: valAccAddr.String(), + name: "remove existing validator - status bonded", + valAddress: valAccAddr.String(), + beforeRun: func() { + resVal, err := s.Network().GetStakingClient().Validator( + s.Network().GetContext(), + &stakingtypes.QueryValidatorRequest{ + ValidatorAddr: valAddr.String(), + }, + ) + require.NoError(s.T(), err) + + // Check if the validator is bonded + require.Equal(s.T(), resVal.Validator.Status, stakingtypes.Bonded) + + // Check if the validator has delegator shares + require.False(s.T(), resVal.Validator.DelegatorShares.IsZero()) + + // Check if the validator has tokens + require.NotZero(s.T(), resVal.Validator.Tokens) + }, afterRun: func() { balance, err := s.Network().GetBankClient().Balance( s.Network().GetContext(), &banktypes.QueryBalanceRequest{ - Address: randomAcc.Address.String(), - Denom: s.Network().GetDenom(), + Address: valAccAddr.String(), + Denom: s.Network().GetDenom(), + }, + ) + require.NoError(s.T(), err) + + // Check account has been emptied + require.True(s.T(), balance.Balance.Amount.IsZero()) + + resVal, err := s.Network().GetStakingClient().Validator( + s.Network().GetContext(), + &stakingtypes.QueryValidatorRequest{ + ValidatorAddr: valAddr.String(), }, ) require.NoError(s.T(), err) - require.Equal(s.T(), balance.Balance.Amount, sdkmath.NewInt(0)) + + // Check if the validator has delegator shares + require.True(s.T(), resVal.Validator.DelegatorShares.IsZero()) + + // Check if the validator has no tokens + require.True(s.T(), resVal.Validator.Tokens.IsZero()) }, }, } @@ -118,9 +194,101 @@ func (s *TestSuite) TestRemoveValidator() { tc.valAddress, ) + if tc.expectedError != nil && err != nil { + require.Error(s.T(), err) + require.ErrorIs(s.T(), err, tc.expectedError) + } else { + require.NoError(s.T(), err) + } + if tc.afterRun != nil { tc.afterRun() } + }) + } +} + +func (s *TestSuite) TestRemoveValidator_ExistingValidator_StatusJailed() { + // Validators + validators := s.Network().GetValidators() + require.NotZero(s.T(), len(validators)) + + validator := validators[1] + valAddr, err := sdktypes.ValAddressFromBech32(validator.OperatorAddress) + require.NoError(s.T(), err) + valAccAddr := sdktypes.AccAddress(valAddr) + valConsAddr := sdktypes.ConsAddress(valAddr) + + tt := []struct { + name string + valAddress string + expectedError error + beforeRun func() + afterRun func() + }{ + { + name: "remove existing validator - jailed", + valAddress: valAccAddr.String(), + beforeRun: func() { + // Jail validator + err := s.Network().StakingKeeper().Jail( + s.Network().GetContext(), + valConsAddr, + ) + require.NoError(s.T(), err) + + resVal, err := s.Network().GetStakingClient().Validator( + s.Network().GetContext(), + &stakingtypes.QueryValidatorRequest{ + ValidatorAddr: valAddr.String(), + }, + ) + require.NoError(s.T(), err) + + // Check if the validator is jailed + require.Equal(s.T(), resVal.Validator.Jailed, true) + }, + afterRun: func() { + // Check if the validator is jailed + balance, err := s.Network().GetBankClient().Balance( + s.Network().GetContext(), + &banktypes.QueryBalanceRequest{ + Address: valAccAddr.String(), + Denom: s.Network().GetBondDenom(), + }, + ) + require.NoError(s.T(), err) + + // Check account has been emptied + require.True(s.T(), balance.Balance.Amount.IsZero()) + + resVal, err := s.Network().GetStakingClient().Validator( + s.Network().GetContext(), + &stakingtypes.QueryValidatorRequest{ + ValidatorAddr: valAddr.String(), + }, + ) + require.NoError(s.T(), err) + + // Check if the validator has delegator shares + require.True(s.T(), resVal.Validator.DelegatorShares.IsZero()) + + // Check if the validator has no tokens + require.True(s.T(), resVal.Validator.Tokens.IsZero()) + }, + }, + } + + for _, tc := range tt { + s.Run(tc.name, func() { + if tc.beforeRun != nil { + tc.beforeRun() + } + + err = s.Network().PoaKeeper().ExecuteRemoveValidator( + s.Network().GetContext(), + tc.valAddress, + ) if tc.expectedError != nil && err != nil { require.Error(s.T(), err) @@ -128,7 +296,93 @@ func (s *TestSuite) TestRemoveValidator() { } else { require.NoError(s.T(), err) } + + if tc.afterRun != nil { + tc.afterRun() + } }) } } +func (s *TestSuite) TestRemoveValidator_ExistingValidator_StatusTombstoned() { + // Validators + validators := s.Network().GetValidators() + require.NotZero(s.T(), len(validators)) + + validator := validators[1] + valAddr, err := sdktypes.ValAddressFromBech32(validator.OperatorAddress) + require.NoError(s.T(), err) + valAccAddr := sdktypes.AccAddress(valAddr) + valConsAddr := sdktypes.ConsAddress(valAddr) + + tt := []struct { + name string + valAddress string + expectedError error + beforeRun func() + afterRun func() + }{ + { + name: "remove existing validator - tombstoned", + valAddress: valAccAddr.String(), + beforeRun: func() { + err := s.Network().SlashingKeeper().Tombstone( + s.Network().GetContext(), + valConsAddr, + ) + require.NoError(s.T(), err) + }, + afterRun: func() { + balance, err := s.Network().GetBankClient().Balance( + s.Network().GetContext(), + &banktypes.QueryBalanceRequest{ + Address: valAccAddr.String(), + Denom: s.Network().GetBondDenom(), + }, + ) + require.NoError(s.T(), err) + + // Check account has been emptied + require.True(s.T(), balance.Balance.Amount.IsZero()) + + resVal, err := s.Network().GetStakingClient().Validator( + s.Network().GetContext(), + &stakingtypes.QueryValidatorRequest{ + ValidatorAddr: valAddr.String(), + }, + ) + require.NoError(s.T(), err) + + // Check if the validator has delegator shares + require.True(s.T(), resVal.Validator.DelegatorShares.IsZero()) + + // Check if the validator has no tokens + require.True(s.T(), resVal.Validator.Tokens.IsZero()) + }, + }, + } + + for _, tc := range tt { + s.Run(tc.name, func() { + if tc.beforeRun != nil { + tc.beforeRun() + } + + err = s.Network().PoaKeeper().ExecuteRemoveValidator( + s.Network().GetContext(), + tc.valAddress, + ) + + if tc.expectedError != nil && err != nil { + require.Error(s.T(), err) + require.ErrorIs(s.T(), err, tc.expectedError) + } else { + require.NoError(s.T(), err) + } + + if tc.afterRun != nil { + tc.afterRun() + } + }) + } +} \ No newline at end of file diff --git a/tests/integration/suite.go b/tests/integration/suite.go index 848e610..ddfc6fc 100644 --- a/tests/integration/suite.go +++ b/tests/integration/suite.go @@ -24,9 +24,12 @@ func (s *TestSuite) Network() *Network { return s.network } -func (s *TestSuite) SetupTest() { +func (s *TestSuite) SetupSuite() { s.network.SetupSdkConfig() s.Require().Equal(sdk.GetConfig().GetBech32AccountAddrPrefix(), "ethm") +} + +func (s *TestSuite) SetupTest() { // Check that the network was created successfully kr := keyring.New(3) @@ -42,6 +45,7 @@ func (s *TestSuite) SetupTest() { exrpcommon.WithPreFundedAccounts(kr.GetAllAccAddrs()...), exrpcommon.WithAmountOfValidators(5), exrpcommon.WithCustomGenesis(customGenesis), + exrpcommon.WithBondDenom("apoa"), ) s.Require().NotNil(s.network) From 68907f2babb26ebfb70ecdf45b87c5f160abebf8 Mon Sep 17 00:00:00 2001 From: GuillemGarciaDev Date: Thu, 9 Jan 2025 17:16:47 +0100 Subject: [PATCH 25/60] fix(tests): jail validator on tombstone remove validator test --- tests/integration/poa_test.go | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/tests/integration/poa_test.go b/tests/integration/poa_test.go index c8dac4d..90827ab 100644 --- a/tests/integration/poa_test.go +++ b/tests/integration/poa_test.go @@ -208,7 +208,7 @@ func (s *TestSuite) TestRemoveValidator_ExistingValidator_StatusBonded() { } } -func (s *TestSuite) TestRemoveValidator_ExistingValidator_StatusJailed() { +func (s *TestSuite) TestRemoveValidator_ExistingValidator_Jailed() { // Validators validators := s.Network().GetValidators() require.NotZero(s.T(), len(validators)) @@ -304,7 +304,7 @@ func (s *TestSuite) TestRemoveValidator_ExistingValidator_StatusJailed() { } } -func (s *TestSuite) TestRemoveValidator_ExistingValidator_StatusTombstoned() { +func (s *TestSuite) TestRemoveValidator_ExistingValidator_Tombstoned() { // Validators validators := s.Network().GetValidators() require.NotZero(s.T(), len(validators)) @@ -326,7 +326,25 @@ func (s *TestSuite) TestRemoveValidator_ExistingValidator_StatusTombstoned() { name: "remove existing validator - tombstoned", valAddress: valAccAddr.String(), beforeRun: func() { - err := s.Network().SlashingKeeper().Tombstone( + // Jail validator + err := s.Network().StakingKeeper().Jail( + s.Network().GetContext(), + valConsAddr, + ) + require.NoError(s.T(), err) + + resVal, err := s.Network().GetStakingClient().Validator( + s.Network().GetContext(), + &stakingtypes.QueryValidatorRequest{ + ValidatorAddr: valAddr.String(), + }, + ) + require.NoError(s.T(), err) + + // Check if the validator is jailed + require.Equal(s.T(), resVal.Validator.Jailed, true) + + err = s.Network().SlashingKeeper().Tombstone( s.Network().GetContext(), valConsAddr, ) From 8942b3098175619c31835a06b545f25271e1d0a2 Mon Sep 17 00:00:00 2001 From: GuillemGarciaDev Date: Fri, 10 Jan 2025 15:00:18 +0100 Subject: [PATCH 26/60] feat(testutil): added new abci calls for NextBloc logic --- testutil/integration/exrp/common/abci.go | 9 +++-- testutil/integration/exrp/integration/abci.go | 37 +++++++++++++++++-- .../integration/exrp/integration/network.go | 21 +++++++++-- testutil/integration/exrp/upgrade/network.go | 4 +- 4 files changed, 57 insertions(+), 14 deletions(-) diff --git a/testutil/integration/exrp/common/abci.go b/testutil/integration/exrp/common/abci.go index d4f1f49..b17f6f5 100644 --- a/testutil/integration/exrp/common/abci.go +++ b/testutil/integration/exrp/common/abci.go @@ -8,10 +8,11 @@ import ( // buildFinalizeBlockReq is a helper function to build // properly the FinalizeBlock request -func BuildFinalizeBlockReq(header cmtproto.Header, validators []*cmttypes.Validator, txs ...[]byte) *abcitypes.RequestFinalizeBlock { +func BuildFinalizeBlockReq(header cmtproto.Header, validators []*cmttypes.Validator, validatorFlags []cmtproto.BlockIDFlag, misbehaviors []abcitypes.Misbehavior, txs ...[]byte) *abcitypes.RequestFinalizeBlock { // add validator's commit info to allocate corresponding tokens to validators - ci := GetCommitInfo(validators) + ci := GetCommitInfo(validators, validatorFlags) return &abcitypes.RequestFinalizeBlock{ + Misbehavior: misbehaviors, Height: header.Height, DecidedLastCommit: ci, Hash: header.AppHash, @@ -22,7 +23,7 @@ func BuildFinalizeBlockReq(header cmtproto.Header, validators []*cmttypes.Valida } } -func GetCommitInfo(validators []*cmttypes.Validator) abcitypes.CommitInfo { +func GetCommitInfo(validators []*cmttypes.Validator, validatorFlags []cmtproto.BlockIDFlag) abcitypes.CommitInfo { voteInfos := make([]abcitypes.VoteInfo, len(validators)) for i, val := range validators { voteInfos[i] = abcitypes.VoteInfo{ @@ -30,7 +31,7 @@ func GetCommitInfo(validators []*cmttypes.Validator) abcitypes.CommitInfo { Address: val.Address, Power: val.VotingPower, }, - BlockIdFlag: cmtproto.BlockIDFlagCommit, + BlockIdFlag: validatorFlags[i], } } return abcitypes.CommitInfo{Votes: voteInfos} diff --git a/testutil/integration/exrp/integration/abci.go b/testutil/integration/exrp/integration/abci.go index 177aafe..a4f3fb7 100644 --- a/testutil/integration/exrp/integration/abci.go +++ b/testutil/integration/exrp/integration/abci.go @@ -7,6 +7,7 @@ import ( storetypes "cosmossdk.io/store/types" abcitypes "github.com/cometbft/cometbft/abci/types" + cmtproto "github.com/cometbft/cometbft/proto/tendermint/types" exrpcommon "github.com/xrplevm/node/v5/testutil/integration/exrp/common" ) @@ -19,7 +20,7 @@ func (n *IntegrationNetwork) NextBlock() error { // NextBlockAfter is a private helper function that runs the FinalizeBlock logic, updates the context and // commits the changes to have a block time after the given duration. func (n *IntegrationNetwork) NextBlockAfter(duration time.Duration) error { - _, err := n.finalizeBlockAndCommit(duration) + _, err := n.finalizeBlockAndCommit(duration, nil, nil) return err } @@ -27,13 +28,34 @@ func (n *IntegrationNetwork) NextBlockAfter(duration time.Duration) error { // with the provided tx bytes, updates the context and // commits the changes to have a block time after the given duration. func (n *IntegrationNetwork) NextBlockWithTxs(txBytes ...[]byte) (*abcitypes.ResponseFinalizeBlock, error) { - return n.finalizeBlockAndCommit(time.Second, txBytes...) + return n.finalizeBlockAndCommit(time.Second, n.valFlags, nil, txBytes...) +} + +// NextNBlocksWithValidatorFlags is a helper function that runs the FinalizeBlock logic +// with the provided validator flags, updates the context and +// commits the changes to have a block time after the given duration. +func (n *IntegrationNetwork) NextNBlocksWithValidatorFlags(blocks int64, validatorFlags []cmtproto.BlockIDFlag) error { + for i := int64(0); i < blocks; i++ { + _, err := n.finalizeBlockAndCommit(time.Second, validatorFlags, nil) + if err != nil { + return err + } + } + return nil +} + +// NextBlockWithMisBehaviors is a helper function that runs the FinalizeBlock logic +// with the provided misbehaviors, updates the context and +// commits the changes to have a block time after the given duration. +func (n *IntegrationNetwork) NextBlockWithMisBehaviors(misbehaviors []abcitypes.Misbehavior) error { + _, err := n.finalizeBlockAndCommit(time.Second, n.valFlags, misbehaviors) + return err } // finalizeBlockAndCommit is a private helper function that runs the FinalizeBlock logic // with the provided txBytes, updates the context and // commits the changes to have a block time after the given duration. -func (n *IntegrationNetwork) finalizeBlockAndCommit(duration time.Duration, txBytes ...[]byte) (*abcitypes.ResponseFinalizeBlock, error) { +func (n *IntegrationNetwork) finalizeBlockAndCommit(duration time.Duration, vFlags []cmtproto.BlockIDFlag, misbehaviors []abcitypes.Misbehavior, txBytes ...[]byte) (*abcitypes.ResponseFinalizeBlock, error) { header := n.ctx.BlockHeader() // Update block header and BeginBlock header.Height++ @@ -42,8 +64,15 @@ func (n *IntegrationNetwork) finalizeBlockAndCommit(duration time.Duration, txBy newBlockTime := header.Time.Add(duration) header.Time = newBlockTime + var validatorFlags []cmtproto.BlockIDFlag + if len(vFlags) > 0 { + validatorFlags = vFlags + } else { + validatorFlags = n.valFlags + } + // FinalizeBlock to run endBlock, deliverTx & beginBlock logic - req := exrpcommon.BuildFinalizeBlockReq(header, n.valSet.Validators, txBytes...) + req := exrpcommon.BuildFinalizeBlockReq(header, n.valSet.Validators, validatorFlags, misbehaviors, txBytes...) res, err := n.app.FinalizeBlock(req) if err != nil { diff --git a/testutil/integration/exrp/integration/network.go b/testutil/integration/exrp/integration/network.go index 9338de2..22c7e3f 100644 --- a/testutil/integration/exrp/integration/network.go +++ b/testutil/integration/exrp/integration/network.go @@ -38,6 +38,7 @@ type Network interface { exrpcommon.Network GetEIP155ChainID() *big.Int + GetValidatorSet() *cmttypes.ValidatorSet } var _ Network = (*IntegrationNetwork)(nil) @@ -51,6 +52,7 @@ type IntegrationNetwork struct { // This is only needed for IBC chain testing setup valSet *cmttypes.ValidatorSet + valFlags []cmtproto.BlockIDFlag valSigners map[string]cmttypes.PrivValidator } @@ -95,6 +97,11 @@ func (n *IntegrationNetwork) configureAndInitChain() error { valSet, valSigners := createValidatorSetAndSigners(n.cfg.AmountOfValidators) totalBonded := DefaultBondedAmount.Mul(sdkmath.NewInt(int64(n.cfg.AmountOfValidators))) + valFlags := make([]cmtproto.BlockIDFlag, len(valSet.Validators)) + for i := range valSet.Validators { + valFlags[i] = cmtproto.BlockIDFlagCommit + } + // Build staking type validators and delegations validators, err := createStakingValidators(valSet.Validators, DefaultBondedAmount, n.cfg.OperatorsAddrs) if err != nil { @@ -106,7 +113,7 @@ func (n *IntegrationNetwork) configureAndInitChain() error { fundedAccountBalances = addBondedModuleAccountToFundedBalances( fundedAccountBalances, - sdktypes.NewCoin(n.cfg.Denom, totalBonded), + sdktypes.NewCoin(n.cfg.BondDenom, totalBonded), ) delegations := createDelegations(validators) @@ -115,7 +122,7 @@ func (n *IntegrationNetwork) configureAndInitChain() error { exrpApp := exrpcommon.CreateExrpApp(n.cfg.ChainID, n.cfg.CustomBaseAppOpts...) stakingParams := StakingCustomGenesisState{ - denom: n.cfg.Denom, + denom: n.cfg.BondDenom, validators: validators, delegations: delegations, } @@ -197,7 +204,7 @@ func (n *IntegrationNetwork) configureAndInitChain() error { }, } - req := exrpcommon.BuildFinalizeBlockReq(header, valSet.Validators) + req := exrpcommon.BuildFinalizeBlockReq(header, valSet.Validators, valFlags, nil) if _, err := exrpApp.FinalizeBlock(req); err != nil { return err } @@ -222,6 +229,7 @@ func (n *IntegrationNetwork) configureAndInitChain() error { n.validators = validators n.valSet = valSet + n.valFlags = valFlags n.valSigners = valSigners return nil @@ -248,6 +256,11 @@ func (n *IntegrationNetwork) GetEIP155ChainID() *big.Int { return n.cfg.EIP155ChainID } +// GetValidatorSet returns the network's validator set +func (n *IntegrationNetwork) GetValidatorSet() *cmttypes.ValidatorSet { + return n.valSet +} + // GetChainConfig returns the network's chain config func (n *IntegrationNetwork) GetEVMChainConfig() *gethparams.ChainConfig { params := n.app.EvmKeeper.GetParams(n.ctx) @@ -295,7 +308,7 @@ func (n *IntegrationNetwork) BroadcastTxSync(txBytes []byte) (abcitypes.ExecTxRe newBlockTime := header.Time.Add(time.Second) header.Time = newBlockTime - req := exrpcommon.BuildFinalizeBlockReq(header, n.valSet.Validators, txBytes) + req := exrpcommon.BuildFinalizeBlockReq(header, n.valSet.Validators, n.valFlags, nil, txBytes) // dont include the DecidedLastCommit because we're not committing the changes // here, is just for broadcasting the tx. To persist the changes, use the diff --git a/testutil/integration/exrp/upgrade/network.go b/testutil/integration/exrp/upgrade/network.go index f21390c..e1ba3ff 100644 --- a/testutil/integration/exrp/upgrade/network.go +++ b/testutil/integration/exrp/upgrade/network.go @@ -193,7 +193,7 @@ func (n *UpgradeIntegrationNetwork) configureAndInitChain() error { }, } - req := exrpcommon.BuildFinalizeBlockReq(header, valSet.Validators) + req := exrpcommon.BuildFinalizeBlockReq(header, valSet.Validators, nil, nil) if _, err := exrpApp.FinalizeBlock(req); err != nil { return err } @@ -285,7 +285,7 @@ func (n *UpgradeIntegrationNetwork) BroadcastTxSync(txBytes []byte) (abcitypes.E newBlockTime := header.Time.Add(time.Second) header.Time = newBlockTime - req := exrpcommon.BuildFinalizeBlockReq(header, n.valSet.Validators, txBytes) + req := exrpcommon.BuildFinalizeBlockReq(header, n.valSet.Validators, nil, nil, txBytes) // dont include the DecidedLastCommit because we're not committing the changes // here, is just for broadcasting the tx. To persist the changes, use the From 467ac6af637f297fbaed924e4f7287b6ae8fc871 Mon Sep 17 00:00:00 2001 From: GuillemGarciaDev Date: Fri, 10 Jan 2025 15:00:48 +0100 Subject: [PATCH 27/60] fix(testutil): missing BuildFinalizeBlockReq param --- testutil/integration/exrp/upgrade/abci.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testutil/integration/exrp/upgrade/abci.go b/testutil/integration/exrp/upgrade/abci.go index 9d07808..a58060a 100644 --- a/testutil/integration/exrp/upgrade/abci.go +++ b/testutil/integration/exrp/upgrade/abci.go @@ -43,7 +43,7 @@ func (n *UpgradeIntegrationNetwork) finalizeBlockAndCommit(duration time.Duratio header.Time = newBlockTime // FinalizeBlock to run endBlock, deliverTx & beginBlock logic - req := exrpcommon.BuildFinalizeBlockReq(header, n.valSet.Validators, txBytes...) + req := exrpcommon.BuildFinalizeBlockReq(header, n.valSet.Validators, nil, nil, txBytes...) res, err := n.app.FinalizeBlock(req) if err != nil { From 2e2f975165abcdf10b4c7bceeb789dc8a41064fc Mon Sep 17 00:00:00 2001 From: GuillemGarciaDev Date: Fri, 10 Jan 2025 15:01:12 +0100 Subject: [PATCH 28/60] feat(tests): add jailed, tombstoned, unbonded and unbonding status integration remove validator tests --- tests/integration/poa_test.go | 297 ++++++++++++++++++++++++++++++++-- 1 file changed, 281 insertions(+), 16 deletions(-) diff --git a/tests/integration/poa_test.go b/tests/integration/poa_test.go index 90827ab..4013747 100644 --- a/tests/integration/poa_test.go +++ b/tests/integration/poa_test.go @@ -5,14 +5,92 @@ import ( "time" sdkmath "cosmossdk.io/math" + abcitypes "github.com/cometbft/cometbft/abci/types" + cmtproto "github.com/cometbft/cometbft/proto/tendermint/types" + types1 "github.com/cosmos/cosmos-sdk/codec/types" sdktypes "github.com/cosmos/cosmos-sdk/types" simtypes "github.com/cosmos/cosmos-sdk/types/simulation" + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" + govtypes "github.com/cosmos/cosmos-sdk/x/gov/types" + slashingtypes "github.com/cosmos/cosmos-sdk/x/slashing/types" stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" "github.com/stretchr/testify/require" poatypes "github.com/xrplevm/node/v5/x/poa/types" ) +func (s *TestSuite) TestAddValidator_UnexistingValidator() { + validator := s.Network().GetValidators()[0] + valAddr, err := sdktypes.ValAddressFromBech32(validator.OperatorAddress) + require.NoError(s.T(), err) + + // Generate a random account + randomAccs := simtypes.RandomAccounts(rand.New(rand.NewSource(time.Now().UnixNano())), 1) + randomAcc := randomAccs[0] + + tt := []struct { + name string + valAddress string + expectedError error + beforeRun func() + afterRun func() + }{ + { + name: "add unexisting validator - random address - no balance", + valAddress: randomAcc.Address.String(), + afterRun: func() { + + require.NoError(s.T(), s.Network().NextBlock()) + + resVal, err := s.Network().GetStakingClient().Validator( + s.Network().GetContext(), + &stakingtypes.QueryValidatorRequest{ + ValidatorAddr: valAddr.String(), + }, + ) + require.NoError(s.T(), err) + + // Check if the validator is unbonding + require.Equal(s.T(), resVal.Validator.Status, stakingtypes.Bonded) + }, + }, + + } + + for _, tc := range tt { + s.Run(tc.name, func() { + if tc.beforeRun != nil { + tc.beforeRun() + } + + msgPubKey, _ := types1.NewAnyWithValue(randomAcc.ConsKey.PubKey()) + + err := s.Network().PoaKeeper().ExecuteAddValidator( + s.Network().GetContext(), + &poatypes.MsgAddValidator{ + ValidatorAddress: randomAcc.Address.String(), + Authority: authtypes.NewModuleAddress(govtypes.ModuleName).String(), + Pubkey: msgPubKey, + Description: stakingtypes.Description{ + Moniker: "test", + }, + }, + ) + + if tc.expectedError != nil && err != nil { + require.Error(s.T(), err) + require.ErrorIs(s.T(), err, tc.expectedError) + } else { + require.NoError(s.T(), err) + } + + if tc.afterRun != nil { + tc.afterRun() + } + }) + } +} + func (s *TestSuite) TestRemoveValidator_UnexistingValidator() { // Generate a random account randomAccs := simtypes.RandomAccounts(rand.New(rand.NewSource(time.Now().UnixNano())), 1) @@ -217,7 +295,6 @@ func (s *TestSuite) TestRemoveValidator_ExistingValidator_Jailed() { valAddr, err := sdktypes.ValAddressFromBech32(validator.OperatorAddress) require.NoError(s.T(), err) valAccAddr := sdktypes.AccAddress(valAddr) - valConsAddr := sdktypes.ConsAddress(valAddr) tt := []struct { name string @@ -230,12 +307,17 @@ func (s *TestSuite) TestRemoveValidator_ExistingValidator_Jailed() { name: "remove existing validator - jailed", valAddress: valAccAddr.String(), beforeRun: func() { - // Jail validator - err := s.Network().StakingKeeper().Jail( - s.Network().GetContext(), - valConsAddr, - ) - require.NoError(s.T(), err) + // Force jail validator + valSet := s.Network().GetValidatorSet() + // Exclude validator at index 1 from validator set + require.Equal(s.T(), sdktypes.ValAddress(valSet.Validators[1].Address).String(), valAddr.String()) + vf := make([]cmtproto.BlockIDFlag, len(valSet.Validators)) + for i := range valSet.Validators { + vf[i] = cmtproto.BlockIDFlagCommit + } + vf[1] = cmtproto.BlockIDFlagAbsent + + require.NoError(s.T(), s.Network().NextNBlocksWithValidatorFlags(slashingtypes.DefaultSignedBlocksWindow+10, vf)) resVal, err := s.Network().GetStakingClient().Validator( s.Network().GetContext(), @@ -247,6 +329,9 @@ func (s *TestSuite) TestRemoveValidator_ExistingValidator_Jailed() { // Check if the validator is jailed require.Equal(s.T(), resVal.Validator.Jailed, true) + + // Check if the validator is unbonding + require.Equal(s.T(), resVal.Validator.Status, stakingtypes.Unbonding) }, afterRun: func() { // Check if the validator is jailed @@ -326,12 +411,19 @@ func (s *TestSuite) TestRemoveValidator_ExistingValidator_Tombstoned() { name: "remove existing validator - tombstoned", valAddress: valAccAddr.String(), beforeRun: func() { - // Jail validator - err := s.Network().StakingKeeper().Jail( - s.Network().GetContext(), - valConsAddr, - ) - require.NoError(s.T(), err) + // Force validator to be tombstoned + require.NoError(s.T(), s.Network().NextBlockWithMisBehaviors( + []abcitypes.Misbehavior{ + { + Type: abcitypes.MisbehaviorType_DUPLICATE_VOTE, + Validator: abcitypes.Validator{ + Address: valAddr.Bytes(), + }, + Height: s.Network().GetContext().BlockHeight(), + TotalVotingPower: s.Network().GetValidatorSet().TotalVotingPower(), + }, + }, + )) resVal, err := s.Network().GetStakingClient().Validator( s.Network().GetContext(), @@ -344,11 +436,19 @@ func (s *TestSuite) TestRemoveValidator_ExistingValidator_Tombstoned() { // Check if the validator is jailed require.Equal(s.T(), resVal.Validator.Jailed, true) - err = s.Network().SlashingKeeper().Tombstone( + // Check if the validator is unbonding + require.Equal(s.T(), resVal.Validator.Status, stakingtypes.Unbonding) + + info, err := s.Network().GetSlashingClient().SigningInfo( s.Network().GetContext(), - valConsAddr, + &slashingtypes.QuerySigningInfoRequest{ + ConsAddress: valConsAddr.String(), + }, ) require.NoError(s.T(), err) + + // Check if the validator is tombstoned + require.Equal(s.T(), info.ValSigningInfo.Tombstoned, true) }, afterRun: func() { balance, err := s.Network().GetBankClient().Balance( @@ -403,4 +503,169 @@ func (s *TestSuite) TestRemoveValidator_ExistingValidator_Tombstoned() { } }) } -} \ No newline at end of file +} + +func (s *TestSuite) TestRemoveValidator_ExistingValidator_StatusUnbonded() { + + // Validators + validators := s.Network().GetValidators() + require.NotZero(s.T(), len(validators)) + + valAddr, err := sdktypes.ValAddressFromBech32(validators[1].OperatorAddress) + require.NoError(s.T(), err) + valAccAddr := sdktypes.AccAddress(valAddr) + + tt := []struct { + name string + valAddress string + expectedError error + beforeRun func() + afterRun func() + }{ + { + name: "remove existing validator - status unbonded", + valAddress: valAccAddr.String(), + expectedError: poatypes.ErrAddressHasNoTokens, + beforeRun: func() { + // Force jail validator + valSet := s.Network().GetValidatorSet() + // Exclude validator at index 1 from validator set + require.Equal(s.T(), sdktypes.ValAddress(valSet.Validators[1].Address).String(), valAddr.String()) + vf := make([]cmtproto.BlockIDFlag, len(valSet.Validators)) + for i := range valSet.Validators { + vf[i] = cmtproto.BlockIDFlagCommit + } + vf[1] = cmtproto.BlockIDFlagAbsent + + require.NoError(s.T(), s.Network().NextNBlocksWithValidatorFlags(slashingtypes.DefaultSignedBlocksWindow+10, vf)) + + resVal, err := s.Network().GetStakingClient().Validator( + s.Network().GetContext(), + &stakingtypes.QueryValidatorRequest{ + ValidatorAddr: valAddr.String(), + }, + ) + require.NoError(s.T(), err) + + // Check if the validator is jailed + require.Equal(s.T(), resVal.Validator.Jailed, true) + + // Check if the validator is unbonding + require.Equal(s.T(), resVal.Validator.Status, stakingtypes.Unbonding) + + require.NoError(s.T(), s.Network().NextBlockAfter(stakingtypes.DefaultUnbondingTime)) + require.NoError(s.T(), s.Network().NextBlock()) + + resVal, err = s.Network().GetStakingClient().Validator( + s.Network().GetContext(), + &stakingtypes.QueryValidatorRequest{ + ValidatorAddr: valAddr.String(), + }, + ) + require.NoError(s.T(), err) + + // Check if the validator is unbonded + require.Equal(s.T(), resVal.Validator.Status, stakingtypes.Unbonded) + }, + }, + } + + for _, tc := range tt { + s.Run(tc.name, func() { + if tc.beforeRun != nil { + tc.beforeRun() + } + + err = s.Network().PoaKeeper().ExecuteRemoveValidator( + s.Network().GetContext(), + tc.valAddress, + ) + + if tc.expectedError != nil && err != nil { + require.Error(s.T(), err) + require.ErrorIs(s.T(), err, tc.expectedError) + } else { + require.NoError(s.T(), err) + } + + if tc.afterRun != nil { + tc.afterRun() + } + }) + } +} + +func (s *TestSuite) TestRemoveValidator_ExistingValidator_StatusUnbonding() { + // Validators + validators := s.Network().GetValidators() + require.NotZero(s.T(), len(validators)) + + valAddr, err := sdktypes.ValAddressFromBech32(validators[1].OperatorAddress) + require.NoError(s.T(), err) + valAccAddr := sdktypes.AccAddress(valAddr) + + tt := []struct { + name string + valAddress string + expectedError error + beforeRun func() + afterRun func() + }{ + { + name: "remove existing validator - status unbonded", + valAddress: valAccAddr.String(), + expectedError: poatypes.ErrAddressHasNoTokens, + beforeRun: func() { + // Force jail validator + valSet := s.Network().GetValidatorSet() + // Exclude validator at index 1 from validator set + require.Equal(s.T(), sdktypes.ValAddress(valSet.Validators[1].Address).String(), valAddr.String()) + vf := make([]cmtproto.BlockIDFlag, len(valSet.Validators)) + for i := range valSet.Validators { + vf[i] = cmtproto.BlockIDFlagCommit + } + vf[1] = cmtproto.BlockIDFlagAbsent + + require.NoError(s.T(), s.Network().NextNBlocksWithValidatorFlags(slashingtypes.DefaultSignedBlocksWindow+10, vf)) + + resVal, err := s.Network().GetStakingClient().Validator( + s.Network().GetContext(), + &stakingtypes.QueryValidatorRequest{ + ValidatorAddr: valAddr.String(), + }, + ) + require.NoError(s.T(), err) + + // Check if the validator is jailed + require.Equal(s.T(), resVal.Validator.Jailed, true) + + // Check if the validator is unbonding + require.Equal(s.T(), resVal.Validator.Status, stakingtypes.Unbonding) + }, + }, + } + + for _, tc := range tt { + s.Run(tc.name, func() { + if tc.beforeRun != nil { + tc.beforeRun() + } + + err = s.Network().PoaKeeper().ExecuteRemoveValidator( + s.Network().GetContext(), + tc.valAddress, + ) + + if tc.expectedError != nil && err != nil { + require.Error(s.T(), err) + require.ErrorIs(s.T(), err, tc.expectedError) + } else { + require.NoError(s.T(), err) + } + + if tc.afterRun != nil { + tc.afterRun() + } + }) + } +} From 6ec2d999e80ea8a4548d6db30a56f2624c75d4a0 Mon Sep 17 00:00:00 2001 From: GuillemGarciaDev Date: Fri, 10 Jan 2025 15:02:15 +0100 Subject: [PATCH 29/60] fix(tests): tests linting --- tests/integration/poa_test.go | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/tests/integration/poa_test.go b/tests/integration/poa_test.go index 4013747..29828a2 100644 --- a/tests/integration/poa_test.go +++ b/tests/integration/poa_test.go @@ -36,8 +36,8 @@ func (s *TestSuite) TestAddValidator_UnexistingValidator() { afterRun func() }{ { - name: "add unexisting validator - random address - no balance", - valAddress: randomAcc.Address.String(), + name: "add unexisting validator - random address - no balance", + valAddress: randomAcc.Address.String(), afterRun: func() { require.NoError(s.T(), s.Network().NextBlock()) @@ -54,7 +54,6 @@ func (s *TestSuite) TestAddValidator_UnexistingValidator() { require.Equal(s.T(), resVal.Validator.Status, stakingtypes.Bonded) }, }, - } for _, tc := range tt { @@ -69,8 +68,8 @@ func (s *TestSuite) TestAddValidator_UnexistingValidator() { s.Network().GetContext(), &poatypes.MsgAddValidator{ ValidatorAddress: randomAcc.Address.String(), - Authority: authtypes.NewModuleAddress(govtypes.ModuleName).String(), - Pubkey: msgPubKey, + Authority: authtypes.NewModuleAddress(govtypes.ModuleName).String(), + Pubkey: msgPubKey, Description: stakingtypes.Description{ Moniker: "test", }, @@ -211,8 +210,8 @@ func (s *TestSuite) TestRemoveValidator_ExistingValidator_StatusBonded() { afterRun func() }{ { - name: "remove existing validator - status bonded", - valAddress: valAccAddr.String(), + name: "remove existing validator - status bonded", + valAddress: valAccAddr.String(), beforeRun: func() { resVal, err := s.Network().GetStakingClient().Validator( s.Network().GetContext(), @@ -304,8 +303,8 @@ func (s *TestSuite) TestRemoveValidator_ExistingValidator_Jailed() { afterRun func() }{ { - name: "remove existing validator - jailed", - valAddress: valAccAddr.String(), + name: "remove existing validator - jailed", + valAddress: valAccAddr.String(), beforeRun: func() { // Force jail validator valSet := s.Network().GetValidatorSet() @@ -408,8 +407,8 @@ func (s *TestSuite) TestRemoveValidator_ExistingValidator_Tombstoned() { afterRun func() }{ { - name: "remove existing validator - tombstoned", - valAddress: valAccAddr.String(), + name: "remove existing validator - tombstoned", + valAddress: valAccAddr.String(), beforeRun: func() { // Force validator to be tombstoned require.NoError(s.T(), s.Network().NextBlockWithMisBehaviors( @@ -419,7 +418,7 @@ func (s *TestSuite) TestRemoveValidator_ExistingValidator_Tombstoned() { Validator: abcitypes.Validator{ Address: valAddr.Bytes(), }, - Height: s.Network().GetContext().BlockHeight(), + Height: s.Network().GetContext().BlockHeight(), TotalVotingPower: s.Network().GetValidatorSet().TotalVotingPower(), }, }, From 7ff8362439c4f8b6852a7a3ec406713fd6cf2cf0 Mon Sep 17 00:00:00 2001 From: GuillemGarciaDev Date: Fri, 10 Jan 2025 16:30:57 +0100 Subject: [PATCH 30/60] feat(tests): add add existing validator with bonded status test --- tests/integration/poa_test.go | 64 +++++++++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) diff --git a/tests/integration/poa_test.go b/tests/integration/poa_test.go index 29828a2..2f0fcee 100644 --- a/tests/integration/poa_test.go +++ b/tests/integration/poa_test.go @@ -90,6 +90,70 @@ func (s *TestSuite) TestAddValidator_UnexistingValidator() { } } +func (s *TestSuite) TestAddValidator_ExistingValidator_StatusBonded() { + validator := s.Network().GetValidators()[0] + valAddr, err := sdktypes.ValAddressFromBech32(validator.OperatorAddress) + require.NoError(s.T(), err) + valAccAddr := sdktypes.AccAddress(valAddr) + + tt := []struct { + name string + valAddress string + expectedError error + beforeRun func() + afterRun func() + }{ + { + name: "add existing validator - status bonded", + valAddress: valAddr.String(), + expectedError: poatypes.ErrAddressHasBondedTokens, + beforeRun: func() { + resVal, err := s.Network().GetStakingClient().Validator( + s.Network().GetContext(), + &stakingtypes.QueryValidatorRequest{ + ValidatorAddr: valAddr.String(), + }, + ) + require.NoError(s.T(), err) + + // Check if the validator is bonded + require.Equal(s.T(), resVal.Validator.Status, stakingtypes.Bonded) + }, + }, + } + + for _, tc := range tt { + s.Run(tc.name, func() { + if tc.beforeRun != nil { + tc.beforeRun() + } + + err := s.Network().PoaKeeper().ExecuteAddValidator( + s.Network().GetContext(), + &poatypes.MsgAddValidator{ + ValidatorAddress: valAccAddr.String(), + Authority: authtypes.NewModuleAddress(govtypes.ModuleName).String(), + Pubkey: validator.ConsensusPubkey, + Description: stakingtypes.Description{ + Moniker: "test", + }, + }, + ) + + if tc.expectedError != nil && err != nil { + require.Error(s.T(), err) + require.ErrorIs(s.T(), err, tc.expectedError) + } else { + require.NoError(s.T(), err) + } + + if tc.afterRun != nil { + tc.afterRun() + } + }) + } +} + func (s *TestSuite) TestRemoveValidator_UnexistingValidator() { // Generate a random account randomAccs := simtypes.RandomAccounts(rand.New(rand.NewSource(time.Now().UnixNano())), 1) From 3df4b72db6b59de648cb6f546785e7cc6427f10c Mon Sep 17 00:00:00 2001 From: GuillemGarciaDev Date: Mon, 13 Jan 2025 09:42:47 +0100 Subject: [PATCH 31/60] feat(tests): add missing `MsgAddValidator` tests --- tests/integration/poa_test.go | 274 ++++++++++++++++++++++++++++++++++ 1 file changed, 274 insertions(+) diff --git a/tests/integration/poa_test.go b/tests/integration/poa_test.go index 2f0fcee..363172e 100644 --- a/tests/integration/poa_test.go +++ b/tests/integration/poa_test.go @@ -19,6 +19,8 @@ import ( poatypes "github.com/xrplevm/node/v5/x/poa/types" ) +// AddValidator tests + func (s *TestSuite) TestAddValidator_UnexistingValidator() { validator := s.Network().GetValidators()[0] valAddr, err := sdktypes.ValAddressFromBech32(validator.OperatorAddress) @@ -90,6 +92,72 @@ func (s *TestSuite) TestAddValidator_UnexistingValidator() { } } +func (s *TestSuite) TestAddValidator_InvalidMsgAddValidator() { + // Generate a random account + randomAccs := simtypes.RandomAccounts(rand.New(rand.NewSource(time.Now().UnixNano())), 1) + randomAcc := randomAccs[0] + randomAccPubKey, _ := types1.NewAnyWithValue(randomAcc.ConsKey.PubKey()) + + validator := s.Network().GetValidators()[1] + valAddr, err := sdktypes.ValAddressFromBech32(validator.OperatorAddress) + require.NoError(s.T(), err) + valAccAddr := sdktypes.AccAddress(valAddr) + + tt := []struct { + name string + valAddress string + valPubKey *types1.Any + expectedError error + beforeRun func() + afterRun func() + }{ + { + name: "add validator - already used pubkey", + valAddress: randomAcc.Address.String(), + valPubKey: validator.ConsensusPubkey, + expectedError: stakingtypes.ErrValidatorPubKeyExists, + }, + { + name: "add validator - already used validator address", + valAddress: valAccAddr.String(), + valPubKey: randomAccPubKey, + expectedError: poatypes.ErrAddressHasBankTokens, + }, + } + + + for _, tc := range tt { + s.Run(tc.name, func() { + if tc.beforeRun != nil { + tc.beforeRun() + } + + err := s.Network().PoaKeeper().ExecuteAddValidator( + s.Network().GetContext(), + &poatypes.MsgAddValidator{ + ValidatorAddress: randomAcc.Address.String(), + Authority: authtypes.NewModuleAddress(govtypes.ModuleName).String(), + Pubkey: validator.ConsensusPubkey, + Description: stakingtypes.Description{ + Moniker: "test", + }, + }, + ) + + if tc.expectedError != nil && err != nil { + require.Error(s.T(), err) + require.ErrorIs(s.T(), err, tc.expectedError) + } else { + require.NoError(s.T(), err) + } + + if tc.afterRun != nil { + tc.afterRun() + } + }) + } +} + func (s *TestSuite) TestAddValidator_ExistingValidator_StatusBonded() { validator := s.Network().GetValidators()[0] valAddr, err := sdktypes.ValAddressFromBech32(validator.OperatorAddress) @@ -154,6 +222,212 @@ func (s *TestSuite) TestAddValidator_ExistingValidator_StatusBonded() { } } +func (s *TestSuite) TestAddValidator_ExistingValidator_Jailed() { + validator := s.Network().GetValidators()[1] + valAddr, err := sdktypes.ValAddressFromBech32(validator.OperatorAddress) + require.NoError(s.T(), err) + valAccAddr := sdktypes.AccAddress(valAddr) + + tt := []struct { + name string + valAddress string + expectedError error + beforeRun func() + afterRun func() + }{ + { + name: "add existing validator - status jailed", + valAddress: valAccAddr.String(), + expectedError: poatypes.ErrAddressHasBondedTokens, + beforeRun: func() { + // Force jail validator + valSet := s.Network().GetValidatorSet() + // Exclude validator at index 1 from validator set + require.Equal(s.T(), sdktypes.ValAddress(valSet.Validators[1].Address).String(), valAddr.String()) + vf := make([]cmtproto.BlockIDFlag, len(valSet.Validators)) + for i := range valSet.Validators { + vf[i] = cmtproto.BlockIDFlagCommit + } + vf[1] = cmtproto.BlockIDFlagAbsent + + require.NoError(s.T(), s.Network().NextNBlocksWithValidatorFlags(slashingtypes.DefaultSignedBlocksWindow+10, vf)) + + resVal, err := s.Network().GetStakingClient().Validator( + s.Network().GetContext(), + &stakingtypes.QueryValidatorRequest{ + ValidatorAddr: valAddr.String(), + }, + ) + require.NoError(s.T(), err) + + // Check if the validator is jailed + require.Equal(s.T(), resVal.Validator.Jailed, true) + + // Check if the validator is unbonding + require.Equal(s.T(), resVal.Validator.Status, stakingtypes.Unbonding) + }, + afterRun: func() { + // Check if the validator is still jailed + resVal, err := s.Network().GetStakingClient().Validator( + s.Network().GetContext(), + &stakingtypes.QueryValidatorRequest{ + ValidatorAddr: valAddr.String(), + }, + ) + require.NoError(s.T(), err) + + // Check if the validator is jailed + require.Equal(s.T(), resVal.Validator.Jailed, true) + + // Check if the validator is unbonding + require.Equal(s.T(), resVal.Validator.Status, stakingtypes.Unbonding) + }, + }, + } + + for _, tc := range tt { + s.Run(tc.name, func() { + if tc.beforeRun != nil { + tc.beforeRun() + } + + err := s.Network().PoaKeeper().ExecuteAddValidator( + s.Network().GetContext(), + &poatypes.MsgAddValidator{ + ValidatorAddress: valAccAddr.String(), + Authority: authtypes.NewModuleAddress(govtypes.ModuleName).String(), + Pubkey: validator.ConsensusPubkey, + Description: stakingtypes.Description{ + Moniker: "test", + }, + }, + ) + + if tc.expectedError != nil && err != nil { + require.Error(s.T(), err) + require.ErrorIs(s.T(), err, tc.expectedError) + } else { + require.NoError(s.T(), err) + } + + if tc.afterRun != nil { + tc.afterRun() + } + }) + } +} + +func (s *TestSuite) TestAddValidator_ExistingValidator_Tombstoned() { + validator := s.Network().GetValidators()[1] + valAddr, err := sdktypes.ValAddressFromBech32(validator.OperatorAddress) + require.NoError(s.T(), err) + valAccAddr := sdktypes.AccAddress(valAddr) + valConsAddr := sdktypes.ConsAddress(valAddr) + + tt := []struct { + name string + valAddress string + expectedError error + beforeRun func() + afterRun func() + }{ + { + name: "add existing validator - status tombstoned", + valAddress: valAccAddr.String(), + expectedError: poatypes.ErrAddressHasBondedTokens, + beforeRun: func() { + // Force validator to be tombstoned + require.NoError(s.T(), s.Network().NextBlockWithMisBehaviors( + []abcitypes.Misbehavior{ + { + Type: abcitypes.MisbehaviorType_DUPLICATE_VOTE, + Validator: abcitypes.Validator{ + Address: valAddr.Bytes(), + }, + Height: s.Network().GetContext().BlockHeight(), + TotalVotingPower: s.Network().GetValidatorSet().TotalVotingPower(), + }, + }, + )) + + resVal, err := s.Network().GetStakingClient().Validator( + s.Network().GetContext(), + &stakingtypes.QueryValidatorRequest{ + ValidatorAddr: valAddr.String(), + }, + ) + require.NoError(s.T(), err) + + // Check if the validator is jailed + require.Equal(s.T(), resVal.Validator.Jailed, true) + + // Check if the validator is unbonding + require.Equal(s.T(), resVal.Validator.Status, stakingtypes.Unbonding) + + info, err := s.Network().GetSlashingClient().SigningInfo( + s.Network().GetContext(), + &slashingtypes.QuerySigningInfoRequest{ + ConsAddress: valConsAddr.String(), + }, + ) + require.NoError(s.T(), err) + + // Check if the validator is tombstoned + require.Equal(s.T(), info.ValSigningInfo.Tombstoned, true) + }, + afterRun: func() { + // Check if the validator is still jailed + resVal, err := s.Network().GetStakingClient().Validator( + s.Network().GetContext(), + &stakingtypes.QueryValidatorRequest{ + ValidatorAddr: valAddr.String(), + }, + ) + require.NoError(s.T(), err) + + // Check if the validator is jailed + require.Equal(s.T(), resVal.Validator.Jailed, true) + + // Check if the validator is unbonding + require.Equal(s.T(), resVal.Validator.Status, stakingtypes.Unbonding) + }, + }, + } + + for _, tc := range tt { + s.Run(tc.name, func() { + if tc.beforeRun != nil { + tc.beforeRun() + } + + err := s.Network().PoaKeeper().ExecuteAddValidator( + s.Network().GetContext(), + &poatypes.MsgAddValidator{ + ValidatorAddress: valAccAddr.String(), + Authority: authtypes.NewModuleAddress(govtypes.ModuleName).String(), + Pubkey: validator.ConsensusPubkey, + Description: stakingtypes.Description{ + Moniker: "test", + }, + }, + ) + + if tc.expectedError != nil && err != nil { + require.Error(s.T(), err) + require.ErrorIs(s.T(), err, tc.expectedError) + } else { + require.NoError(s.T(), err) + } + + if tc.afterRun != nil { + tc.afterRun() + } + }) + } +} + +// RemoveValidator tests + func (s *TestSuite) TestRemoveValidator_UnexistingValidator() { // Generate a random account randomAccs := simtypes.RandomAccounts(rand.New(rand.NewSource(time.Now().UnixNano())), 1) From e6fbb79a28c7f07c5cd5f130397a15aa6bf022cf Mon Sep 17 00:00:00 2001 From: GuillemGarciaDev Date: Mon, 13 Jan 2025 09:44:05 +0100 Subject: [PATCH 32/60] fix(tests): move integration `MsgAddValidator` tests to corresponding branch --- tests/integration/poa_test.go | 138 +--------------------------------- 1 file changed, 1 insertion(+), 137 deletions(-) diff --git a/tests/integration/poa_test.go b/tests/integration/poa_test.go index 2f0fcee..8ce51a5 100644 --- a/tests/integration/poa_test.go +++ b/tests/integration/poa_test.go @@ -7,152 +7,16 @@ import ( sdkmath "cosmossdk.io/math" abcitypes "github.com/cometbft/cometbft/abci/types" cmtproto "github.com/cometbft/cometbft/proto/tendermint/types" - types1 "github.com/cosmos/cosmos-sdk/codec/types" sdktypes "github.com/cosmos/cosmos-sdk/types" simtypes "github.com/cosmos/cosmos-sdk/types/simulation" - authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" - govtypes "github.com/cosmos/cosmos-sdk/x/gov/types" slashingtypes "github.com/cosmos/cosmos-sdk/x/slashing/types" stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" "github.com/stretchr/testify/require" poatypes "github.com/xrplevm/node/v5/x/poa/types" ) -func (s *TestSuite) TestAddValidator_UnexistingValidator() { - validator := s.Network().GetValidators()[0] - valAddr, err := sdktypes.ValAddressFromBech32(validator.OperatorAddress) - require.NoError(s.T(), err) - - // Generate a random account - randomAccs := simtypes.RandomAccounts(rand.New(rand.NewSource(time.Now().UnixNano())), 1) - randomAcc := randomAccs[0] - - tt := []struct { - name string - valAddress string - expectedError error - beforeRun func() - afterRun func() - }{ - { - name: "add unexisting validator - random address - no balance", - valAddress: randomAcc.Address.String(), - afterRun: func() { - - require.NoError(s.T(), s.Network().NextBlock()) - - resVal, err := s.Network().GetStakingClient().Validator( - s.Network().GetContext(), - &stakingtypes.QueryValidatorRequest{ - ValidatorAddr: valAddr.String(), - }, - ) - require.NoError(s.T(), err) - - // Check if the validator is unbonding - require.Equal(s.T(), resVal.Validator.Status, stakingtypes.Bonded) - }, - }, - } - - for _, tc := range tt { - s.Run(tc.name, func() { - if tc.beforeRun != nil { - tc.beforeRun() - } - - msgPubKey, _ := types1.NewAnyWithValue(randomAcc.ConsKey.PubKey()) - - err := s.Network().PoaKeeper().ExecuteAddValidator( - s.Network().GetContext(), - &poatypes.MsgAddValidator{ - ValidatorAddress: randomAcc.Address.String(), - Authority: authtypes.NewModuleAddress(govtypes.ModuleName).String(), - Pubkey: msgPubKey, - Description: stakingtypes.Description{ - Moniker: "test", - }, - }, - ) - - if tc.expectedError != nil && err != nil { - require.Error(s.T(), err) - require.ErrorIs(s.T(), err, tc.expectedError) - } else { - require.NoError(s.T(), err) - } - - if tc.afterRun != nil { - tc.afterRun() - } - }) - } -} - -func (s *TestSuite) TestAddValidator_ExistingValidator_StatusBonded() { - validator := s.Network().GetValidators()[0] - valAddr, err := sdktypes.ValAddressFromBech32(validator.OperatorAddress) - require.NoError(s.T(), err) - valAccAddr := sdktypes.AccAddress(valAddr) - - tt := []struct { - name string - valAddress string - expectedError error - beforeRun func() - afterRun func() - }{ - { - name: "add existing validator - status bonded", - valAddress: valAddr.String(), - expectedError: poatypes.ErrAddressHasBondedTokens, - beforeRun: func() { - resVal, err := s.Network().GetStakingClient().Validator( - s.Network().GetContext(), - &stakingtypes.QueryValidatorRequest{ - ValidatorAddr: valAddr.String(), - }, - ) - require.NoError(s.T(), err) - - // Check if the validator is bonded - require.Equal(s.T(), resVal.Validator.Status, stakingtypes.Bonded) - }, - }, - } - - for _, tc := range tt { - s.Run(tc.name, func() { - if tc.beforeRun != nil { - tc.beforeRun() - } - - err := s.Network().PoaKeeper().ExecuteAddValidator( - s.Network().GetContext(), - &poatypes.MsgAddValidator{ - ValidatorAddress: valAccAddr.String(), - Authority: authtypes.NewModuleAddress(govtypes.ModuleName).String(), - Pubkey: validator.ConsensusPubkey, - Description: stakingtypes.Description{ - Moniker: "test", - }, - }, - ) - - if tc.expectedError != nil && err != nil { - require.Error(s.T(), err) - require.ErrorIs(s.T(), err, tc.expectedError) - } else { - require.NoError(s.T(), err) - } - - if tc.afterRun != nil { - tc.afterRun() - } - }) - } -} +// RemoveValidator tests func (s *TestSuite) TestRemoveValidator_UnexistingValidator() { // Generate a random account From 0a901f9deab0e7e23862234178f26e06afdd362f Mon Sep 17 00:00:00 2001 From: GuillemGarciaDev Date: Mon, 13 Jan 2025 09:47:53 +0100 Subject: [PATCH 33/60] fix(tests): linting --- tests/integration/poa_test.go | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/integration/poa_test.go b/tests/integration/poa_test.go index 8ce51a5..6e48bc1 100644 --- a/tests/integration/poa_test.go +++ b/tests/integration/poa_test.go @@ -433,7 +433,6 @@ func (s *TestSuite) TestRemoveValidator_ExistingValidator_Tombstoned() { } func (s *TestSuite) TestRemoveValidator_ExistingValidator_StatusUnbonded() { - // Validators validators := s.Network().GetValidators() require.NotZero(s.T(), len(validators)) From fb4aa93c56b5c849de85279e5de20430b44e7ea5 Mon Sep 17 00:00:00 2001 From: GuillemGarciaDev Date: Mon, 13 Jan 2025 10:08:52 +0100 Subject: [PATCH 34/60] fix(tests): add valIndex to tests --- tests/integration/poa_test.go | 51 ++++++++++++++++++++--------------- 1 file changed, 30 insertions(+), 21 deletions(-) diff --git a/tests/integration/poa_test.go b/tests/integration/poa_test.go index 363172e..e9b5fe6 100644 --- a/tests/integration/poa_test.go +++ b/tests/integration/poa_test.go @@ -38,7 +38,7 @@ func (s *TestSuite) TestAddValidator_UnexistingValidator() { afterRun func() }{ { - name: "add unexisting validator - random address - no balance", + name: "add unexisting validator - random address", valAddress: randomAcc.Address.String(), afterRun: func() { @@ -98,7 +98,7 @@ func (s *TestSuite) TestAddValidator_InvalidMsgAddValidator() { randomAcc := randomAccs[0] randomAccPubKey, _ := types1.NewAnyWithValue(randomAcc.ConsKey.PubKey()) - validator := s.Network().GetValidators()[1] + validator := s.Network().GetValidators()[0] valAddr, err := sdktypes.ValAddressFromBech32(validator.OperatorAddress) require.NoError(s.T(), err) valAccAddr := sdktypes.AccAddress(valAddr) @@ -223,7 +223,8 @@ func (s *TestSuite) TestAddValidator_ExistingValidator_StatusBonded() { } func (s *TestSuite) TestAddValidator_ExistingValidator_Jailed() { - validator := s.Network().GetValidators()[1] + valIndex := 1 + validator := s.Network().GetValidators()[valIndex] valAddr, err := sdktypes.ValAddressFromBech32(validator.OperatorAddress) require.NoError(s.T(), err) valAccAddr := sdktypes.AccAddress(valAddr) @@ -243,12 +244,12 @@ func (s *TestSuite) TestAddValidator_ExistingValidator_Jailed() { // Force jail validator valSet := s.Network().GetValidatorSet() // Exclude validator at index 1 from validator set - require.Equal(s.T(), sdktypes.ValAddress(valSet.Validators[1].Address).String(), valAddr.String()) + require.Equal(s.T(), sdktypes.ValAddress(valSet.Validators[valIndex].Address).String(), valAddr.String()) vf := make([]cmtproto.BlockIDFlag, len(valSet.Validators)) for i := range valSet.Validators { vf[i] = cmtproto.BlockIDFlagCommit } - vf[1] = cmtproto.BlockIDFlagAbsent + vf[valIndex] = cmtproto.BlockIDFlagAbsent require.NoError(s.T(), s.Network().NextNBlocksWithValidatorFlags(slashingtypes.DefaultSignedBlocksWindow+10, vf)) @@ -318,7 +319,8 @@ func (s *TestSuite) TestAddValidator_ExistingValidator_Jailed() { } func (s *TestSuite) TestAddValidator_ExistingValidator_Tombstoned() { - validator := s.Network().GetValidators()[1] + valIndex := 1 + validator := s.Network().GetValidators()[valIndex] valAddr, err := sdktypes.ValAddressFromBech32(validator.OperatorAddress) require.NoError(s.T(), err) valAccAddr := sdktypes.AccAddress(valAddr) @@ -433,6 +435,12 @@ func (s *TestSuite) TestRemoveValidator_UnexistingValidator() { randomAccs := simtypes.RandomAccounts(rand.New(rand.NewSource(time.Now().UnixNano())), 1) randomAcc := randomAccs[0] + // Default amount of tokens to fund the account with + coins := sdktypes.NewCoin( + s.Network().GetDenom(), + sdkmath.NewInt(100000000000), + ) + tt := []struct { name string valAddress string @@ -463,11 +471,6 @@ func (s *TestSuite) TestRemoveValidator_UnexistingValidator() { valAddress: randomAcc.Address.String(), expectedError: poatypes.ErrAddressHasNoTokens, beforeRun: func() { - coins := sdktypes.NewCoin( - s.Network().GetDenom(), - sdkmath.NewInt(100000000000), - ) - err := s.factory.FundAccount( s.keyring.GetKey(0), randomAcc.Address, @@ -628,7 +631,8 @@ func (s *TestSuite) TestRemoveValidator_ExistingValidator_Jailed() { validators := s.Network().GetValidators() require.NotZero(s.T(), len(validators)) - validator := validators[1] + valIndex := 1 + validator := validators[valIndex] valAddr, err := sdktypes.ValAddressFromBech32(validator.OperatorAddress) require.NoError(s.T(), err) valAccAddr := sdktypes.AccAddress(valAddr) @@ -647,12 +651,12 @@ func (s *TestSuite) TestRemoveValidator_ExistingValidator_Jailed() { // Force jail validator valSet := s.Network().GetValidatorSet() // Exclude validator at index 1 from validator set - require.Equal(s.T(), sdktypes.ValAddress(valSet.Validators[1].Address).String(), valAddr.String()) + require.Equal(s.T(), sdktypes.ValAddress(valSet.Validators[valIndex].Address).String(), valAddr.String()) vf := make([]cmtproto.BlockIDFlag, len(valSet.Validators)) for i := range valSet.Validators { vf[i] = cmtproto.BlockIDFlagCommit } - vf[1] = cmtproto.BlockIDFlagAbsent + vf[valIndex] = cmtproto.BlockIDFlagAbsent require.NoError(s.T(), s.Network().NextNBlocksWithValidatorFlags(slashingtypes.DefaultSignedBlocksWindow+10, vf)) @@ -731,7 +735,8 @@ func (s *TestSuite) TestRemoveValidator_ExistingValidator_Tombstoned() { validators := s.Network().GetValidators() require.NotZero(s.T(), len(validators)) - validator := validators[1] + valIndex := 1 + validator := validators[valIndex] valAddr, err := sdktypes.ValAddressFromBech32(validator.OperatorAddress) require.NoError(s.T(), err) valAccAddr := sdktypes.AccAddress(valAddr) @@ -848,7 +853,9 @@ func (s *TestSuite) TestRemoveValidator_ExistingValidator_StatusUnbonded() { validators := s.Network().GetValidators() require.NotZero(s.T(), len(validators)) - valAddr, err := sdktypes.ValAddressFromBech32(validators[1].OperatorAddress) + valIndex := 1 + validator := validators[valIndex] + valAddr, err := sdktypes.ValAddressFromBech32(validator.OperatorAddress) require.NoError(s.T(), err) valAccAddr := sdktypes.AccAddress(valAddr) @@ -867,12 +874,12 @@ func (s *TestSuite) TestRemoveValidator_ExistingValidator_StatusUnbonded() { // Force jail validator valSet := s.Network().GetValidatorSet() // Exclude validator at index 1 from validator set - require.Equal(s.T(), sdktypes.ValAddress(valSet.Validators[1].Address).String(), valAddr.String()) + require.Equal(s.T(), sdktypes.ValAddress(valSet.Validators[valIndex].Address).String(), valAddr.String()) vf := make([]cmtproto.BlockIDFlag, len(valSet.Validators)) for i := range valSet.Validators { vf[i] = cmtproto.BlockIDFlagCommit } - vf[1] = cmtproto.BlockIDFlagAbsent + vf[valIndex] = cmtproto.BlockIDFlagAbsent require.NoError(s.T(), s.Network().NextNBlocksWithValidatorFlags(slashingtypes.DefaultSignedBlocksWindow+10, vf)) @@ -937,7 +944,9 @@ func (s *TestSuite) TestRemoveValidator_ExistingValidator_StatusUnbonding() { validators := s.Network().GetValidators() require.NotZero(s.T(), len(validators)) - valAddr, err := sdktypes.ValAddressFromBech32(validators[1].OperatorAddress) + valIndex := 1 + validator := validators[valIndex] + valAddr, err := sdktypes.ValAddressFromBech32(validator.OperatorAddress) require.NoError(s.T(), err) valAccAddr := sdktypes.AccAddress(valAddr) @@ -956,12 +965,12 @@ func (s *TestSuite) TestRemoveValidator_ExistingValidator_StatusUnbonding() { // Force jail validator valSet := s.Network().GetValidatorSet() // Exclude validator at index 1 from validator set - require.Equal(s.T(), sdktypes.ValAddress(valSet.Validators[1].Address).String(), valAddr.String()) + require.Equal(s.T(), sdktypes.ValAddress(valSet.Validators[valIndex].Address).String(), valAddr.String()) vf := make([]cmtproto.BlockIDFlag, len(valSet.Validators)) for i := range valSet.Validators { vf[i] = cmtproto.BlockIDFlagCommit } - vf[1] = cmtproto.BlockIDFlagAbsent + vf[valIndex] = cmtproto.BlockIDFlagAbsent require.NoError(s.T(), s.Network().NextNBlocksWithValidatorFlags(slashingtypes.DefaultSignedBlocksWindow+10, vf)) From d9c7d212083e9de451bd410c88cfd8bac063badc Mon Sep 17 00:00:00 2001 From: GuillemGarciaDev Date: Tue, 14 Jan 2025 13:39:39 +0100 Subject: [PATCH 35/60] fix(x/poa): `RemoveValidator` with propert amount of delegator shares --- x/poa/keeper/keeper.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x/poa/keeper/keeper.go b/x/poa/keeper/keeper.go index 93c9f66..296fcaf 100644 --- a/x/poa/keeper/keeper.go +++ b/x/poa/keeper/keeper.go @@ -288,7 +288,7 @@ func (k Keeper) ExecuteRemoveValidator(ctx sdk.Context, validatorAddress string) } // Unbond self-delegation so the validator is removed after being unbonded - _, err = k.sk.Unbond(ctx, sdk.AccAddress(valAddress), valAddress, math.LegacyOneDec()) + _, err = k.sk.Unbond(ctx, sdk.AccAddress(valAddress), valAddress, changedVal.DelegatorShares) if err != nil { return err } From f031f1de68425660e859558e9199885196e67158 Mon Sep 17 00:00:00 2001 From: GuillemGarciaDev Date: Tue, 14 Jan 2025 13:40:58 +0100 Subject: [PATCH 36/60] feat(tests): remove validator integration tests --- tests/integration/poa_test.go | 385 +++++++++++----------------------- 1 file changed, 127 insertions(+), 258 deletions(-) diff --git a/tests/integration/poa_test.go b/tests/integration/poa_test.go index 6e48bc1..5f1f022 100644 --- a/tests/integration/poa_test.go +++ b/tests/integration/poa_test.go @@ -1,18 +1,20 @@ package integration import ( + "fmt" "math/rand" "time" - sdkmath "cosmossdk.io/math" abcitypes "github.com/cometbft/cometbft/abci/types" cmtproto "github.com/cometbft/cometbft/proto/tendermint/types" sdktypes "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/address" simtypes "github.com/cosmos/cosmos-sdk/types/simulation" - banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" + govtypes "github.com/cosmos/cosmos-sdk/x/gov/types/v1" slashingtypes "github.com/cosmos/cosmos-sdk/x/slashing/types" stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" "github.com/stretchr/testify/require" + "github.com/xrplevm/node/v5/testutil/integration/exrp/utils" poatypes "github.com/xrplevm/node/v5/x/poa/types" ) @@ -20,9 +22,11 @@ import ( func (s *TestSuite) TestRemoveValidator_UnexistingValidator() { // Generate a random account - randomAccs := simtypes.RandomAccounts(rand.New(rand.NewSource(time.Now().UnixNano())), 1) + randomAccs := simtypes.RandomAccounts(rand.New(rand.NewSource(time.Now().UnixNano())), 1) //nolint:gosec randomAcc := randomAccs[0] + randomValAddr := sdktypes.ValAddress(randomAcc.Address.Bytes()) + tt := []struct { name string valAddress string @@ -30,68 +34,31 @@ func (s *TestSuite) TestRemoveValidator_UnexistingValidator() { beforeRun func() afterRun func() }{ - { - name: "remove unexisting validator - random address - no balance", - valAddress: randomAcc.Address.String(), - expectedError: poatypes.ErrAddressHasNoTokens, - beforeRun: func() { - balance, err := s.Network().GetBankClient().Balance( - s.Network().GetContext(), - &banktypes.QueryBalanceRequest{ - Address: randomAcc.Address.String(), - Denom: s.Network().GetBondDenom(), - }, - ) - require.NoError(s.T(), err) - - // Check account has no balance - require.True(s.T(), balance.Balance.Amount.IsZero()) - }, - }, { name: "remove unexisting validator - random address - with balance", valAddress: randomAcc.Address.String(), expectedError: poatypes.ErrAddressHasNoTokens, beforeRun: func() { - coins := sdktypes.NewCoin( - s.Network().GetDenom(), - sdkmath.NewInt(100000000000), - ) - - err := s.factory.FundAccount( - s.keyring.GetKey(0), - randomAcc.Address, - sdktypes.NewCoins( - coins, - ), - ) - require.NoError(s.T(), err) - require.NoError(s.T(), s.Network().NextBlock()) - - balance, err := s.Network().GetBankClient().Balance( + _, err := s.Network().GetStakingClient().Validator( s.Network().GetContext(), - &banktypes.QueryBalanceRequest{ - Address: randomAcc.Address.String(), - Denom: s.Network().GetBondDenom(), + &stakingtypes.QueryValidatorRequest{ + ValidatorAddr: randomValAddr.String(), }, ) - require.NoError(s.T(), err) - // Check account has been funded - require.True(s.T(), balance.Balance.Amount.IsZero()) + // Check if the validator does not exist + require.Contains(s.T(), err.Error(), fmt.Sprintf("validator %s not found", randomValAddr.String())) }, afterRun: func() { - balance, err := s.Network().GetBankClient().Balance( + _, err := s.Network().GetStakingClient().Validator( s.Network().GetContext(), - &banktypes.QueryBalanceRequest{ - Address: randomAcc.Address.String(), - Denom: s.Network().GetBondDenom(), + &stakingtypes.QueryValidatorRequest{ + ValidatorAddr: randomValAddr.String(), }, ) - require.NoError(s.T(), err) - // Check account has been emptied - require.True(s.T(), balance.Balance.Amount.IsZero()) + // Check if the validator is not found + require.Contains(s.T(), err.Error(), fmt.Sprintf("validator %s not found", randomValAddr.String())) }, }, } @@ -102,11 +69,18 @@ func (s *TestSuite) TestRemoveValidator_UnexistingValidator() { tc.beforeRun() } - err := s.Network().PoaKeeper().ExecuteRemoveValidator( - s.Network().GetContext(), + authority := sdktypes.AccAddress(address.Module("gov")) + msg := poatypes.NewMsgRemoveValidator( + authority.String(), tc.valAddress, ) + proposal, err := utils.SubmitAndAwaitProposalResolution(s.factory, s.Network(), s.keyring.GetKeys(), "test", msg) + require.NoError(s.T(), err) + + require.Equal(s.T(), govtypes.ProposalStatus_PROPOSAL_STATUS_FAILED, proposal.Status) + require.Equal(s.T(), tc.expectedError.Error(), proposal.FailedReason) + if tc.expectedError != nil && err != nil { require.Error(s.T(), err) require.ErrorIs(s.T(), err, tc.expectedError) @@ -126,7 +100,8 @@ func (s *TestSuite) TestRemoveValidator_ExistingValidator_StatusBonded() { validators := s.Network().GetValidators() require.NotZero(s.T(), len(validators)) - valAddr, err := sdktypes.ValAddressFromBech32(validators[0].OperatorAddress) + validator := validators[0] + valAddr, err := sdktypes.ValAddressFromBech32(validator.OperatorAddress) require.NoError(s.T(), err) valAccAddr := sdktypes.AccAddress(valAddr) @@ -153,25 +128,31 @@ func (s *TestSuite) TestRemoveValidator_ExistingValidator_StatusBonded() { require.Equal(s.T(), resVal.Validator.Status, stakingtypes.Bonded) // Check if the validator has delegator shares - require.False(s.T(), resVal.Validator.DelegatorShares.IsZero()) + require.Equal(s.T(), sdktypes.DefaultPowerReduction.ToLegacyDec(), resVal.Validator.DelegatorShares) // Check if the validator has tokens require.NotZero(s.T(), resVal.Validator.Tokens) }, afterRun: func() { - balance, err := s.Network().GetBankClient().Balance( + resVal, err := s.Network().GetStakingClient().Validator( s.Network().GetContext(), - &banktypes.QueryBalanceRequest{ - Address: valAccAddr.String(), - Denom: s.Network().GetDenom(), + &stakingtypes.QueryValidatorRequest{ + ValidatorAddr: valAddr.String(), }, ) require.NoError(s.T(), err) - // Check account has been emptied - require.True(s.T(), balance.Balance.Amount.IsZero()) + // Check if the validator has delegator shares + require.True(s.T(), + resVal.Validator.DelegatorShares.IsZero(), + "delegator shares should be zero, got %s", + resVal.Validator.DelegatorShares, + ) - resVal, err := s.Network().GetStakingClient().Validator( + // Check if the validator has no tokens + require.True(s.T(), resVal.Validator.Tokens.IsZero()) + + resVal, err = s.Network().GetStakingClient().Validator( s.Network().GetContext(), &stakingtypes.QueryValidatorRequest{ ValidatorAddr: valAddr.String(), @@ -179,26 +160,40 @@ func (s *TestSuite) TestRemoveValidator_ExistingValidator_StatusBonded() { ) require.NoError(s.T(), err) - // Check if the validator has delegator shares - require.True(s.T(), resVal.Validator.DelegatorShares.IsZero()) + // Check if the validator is unbonded + require.Equal(s.T(), resVal.Validator.Status, stakingtypes.Unbonding) - // Check if the validator has no tokens - require.True(s.T(), resVal.Validator.Tokens.IsZero()) + require.NoError(s.T(), s.Network().NextBlockAfter(stakingtypes.DefaultUnbondingTime)) + + _, err = s.Network().GetStakingClient().Validator( + s.Network().GetContext(), + &stakingtypes.QueryValidatorRequest{ + ValidatorAddr: valAddr.String(), + }, + ) + require.Contains(s.T(), err.Error(), fmt.Sprintf("validator %s not found", valAddr.String())) }, }, } + //nolint:dupl for _, tc := range tt { s.Run(tc.name, func() { if tc.beforeRun != nil { tc.beforeRun() } - err = s.Network().PoaKeeper().ExecuteRemoveValidator( - s.Network().GetContext(), - tc.valAddress, + authority := sdktypes.AccAddress(address.Module("gov")) + msg := poatypes.NewMsgRemoveValidator( + authority.String(), + valAccAddr.String(), ) + proposal, err := utils.SubmitAndAwaitProposalResolution(s.factory, s.Network(), s.keyring.GetKeys(), "test", msg) + require.NoError(s.T(), err) + + require.Equal(s.T(), govtypes.ProposalStatus_PROPOSAL_STATUS_PASSED, proposal.Status) + if tc.expectedError != nil && err != nil { require.Error(s.T(), err) require.ErrorIs(s.T(), err, tc.expectedError) @@ -236,15 +231,17 @@ func (s *TestSuite) TestRemoveValidator_ExistingValidator_Jailed() { beforeRun: func() { // Force jail validator valSet := s.Network().GetValidatorSet() - // Exclude validator at index 1 from validator set - require.Equal(s.T(), sdktypes.ValAddress(valSet.Validators[1].Address).String(), valAddr.String()) - vf := make([]cmtproto.BlockIDFlag, len(valSet.Validators)) - for i := range valSet.Validators { - vf[i] = cmtproto.BlockIDFlagCommit - } - vf[1] = cmtproto.BlockIDFlagAbsent - require.NoError(s.T(), s.Network().NextNBlocksWithValidatorFlags(slashingtypes.DefaultSignedBlocksWindow+10, vf)) + require.NoError( + s.T(), + s.Network().NextNBlocksWithValidatorFlags( + slashingtypes.DefaultSignedBlocksWindow, + utils.NewValidatorFlags( + len(valSet.Validators), + utils.NewValidatorFlagOverride(1, cmtproto.BlockIDFlagAbsent), + ), + ), + ) resVal, err := s.Network().GetStakingClient().Validator( s.Network().GetContext(), @@ -261,19 +258,6 @@ func (s *TestSuite) TestRemoveValidator_ExistingValidator_Jailed() { require.Equal(s.T(), resVal.Validator.Status, stakingtypes.Unbonding) }, afterRun: func() { - // Check if the validator is jailed - balance, err := s.Network().GetBankClient().Balance( - s.Network().GetContext(), - &banktypes.QueryBalanceRequest{ - Address: valAccAddr.String(), - Denom: s.Network().GetBondDenom(), - }, - ) - require.NoError(s.T(), err) - - // Check account has been emptied - require.True(s.T(), balance.Balance.Amount.IsZero()) - resVal, err := s.Network().GetStakingClient().Validator( s.Network().GetContext(), &stakingtypes.QueryValidatorRequest{ @@ -287,21 +271,42 @@ func (s *TestSuite) TestRemoveValidator_ExistingValidator_Jailed() { // Check if the validator has no tokens require.True(s.T(), resVal.Validator.Tokens.IsZero()) + + // Check if the validator is unbonding + require.Equal(s.T(), resVal.Validator.Status, stakingtypes.Unbonding) + + require.NoError(s.T(), s.Network().NextBlockAfter(stakingtypes.DefaultUnbondingTime)) + + _, err = s.Network().GetStakingClient().Validator( + s.Network().GetContext(), + &stakingtypes.QueryValidatorRequest{ + ValidatorAddr: valAddr.String(), + }, + ) + + require.Contains(s.T(), err.Error(), fmt.Sprintf("validator %s not found", valAddr.String())) }, }, } + //nolint:dupl for _, tc := range tt { s.Run(tc.name, func() { if tc.beforeRun != nil { tc.beforeRun() } - err = s.Network().PoaKeeper().ExecuteRemoveValidator( - s.Network().GetContext(), - tc.valAddress, + authority := sdktypes.AccAddress(address.Module("gov")) + msg := poatypes.NewMsgRemoveValidator( + authority.String(), + valAccAddr.String(), ) + proposal, err := utils.SubmitAndAwaitProposalResolution(s.factory, s.Network(), s.keyring.GetKeys(), "test", msg) + require.NoError(s.T(), err) + + require.Equal(s.T(), govtypes.ProposalStatus_PROPOSAL_STATUS_PASSED, proposal.Status) + if tc.expectedError != nil && err != nil { require.Error(s.T(), err) require.ErrorIs(s.T(), err, tc.expectedError) @@ -317,15 +322,21 @@ func (s *TestSuite) TestRemoveValidator_ExistingValidator_Jailed() { } func (s *TestSuite) TestRemoveValidator_ExistingValidator_Tombstoned() { - // Validators + valIndex := 1 + + // CometBFT validators + valSet := s.Network().GetValidatorSet() + cmtValAddr := sdktypes.AccAddress(valSet.Validators[valIndex].Address.Bytes()) + cmtValConsAddr := sdktypes.ConsAddress(valSet.Validators[valIndex].Address.Bytes()) + + // Cosmos validators validators := s.Network().GetValidators() require.NotZero(s.T(), len(validators)) - validator := validators[1] + validator := validators[valIndex] valAddr, err := sdktypes.ValAddressFromBech32(validator.OperatorAddress) require.NoError(s.T(), err) valAccAddr := sdktypes.AccAddress(valAddr) - valConsAddr := sdktypes.ConsAddress(valAddr) tt := []struct { name string @@ -344,7 +355,7 @@ func (s *TestSuite) TestRemoveValidator_ExistingValidator_Tombstoned() { { Type: abcitypes.MisbehaviorType_DUPLICATE_VOTE, Validator: abcitypes.Validator{ - Address: valAddr.Bytes(), + Address: cmtValAddr, }, Height: s.Network().GetContext().BlockHeight(), TotalVotingPower: s.Network().GetValidatorSet().TotalVotingPower(), @@ -369,7 +380,7 @@ func (s *TestSuite) TestRemoveValidator_ExistingValidator_Tombstoned() { info, err := s.Network().GetSlashingClient().SigningInfo( s.Network().GetContext(), &slashingtypes.QuerySigningInfoRequest{ - ConsAddress: valConsAddr.String(), + ConsAddress: cmtValConsAddr.String(), }, ) require.NoError(s.T(), err) @@ -378,18 +389,6 @@ func (s *TestSuite) TestRemoveValidator_ExistingValidator_Tombstoned() { require.Equal(s.T(), info.ValSigningInfo.Tombstoned, true) }, afterRun: func() { - balance, err := s.Network().GetBankClient().Balance( - s.Network().GetContext(), - &banktypes.QueryBalanceRequest{ - Address: valAccAddr.String(), - Denom: s.Network().GetBondDenom(), - }, - ) - require.NoError(s.T(), err) - - // Check account has been emptied - require.True(s.T(), balance.Balance.Amount.IsZero()) - resVal, err := s.Network().GetStakingClient().Validator( s.Network().GetContext(), &stakingtypes.QueryValidatorRequest{ @@ -403,185 +402,55 @@ func (s *TestSuite) TestRemoveValidator_ExistingValidator_Tombstoned() { // Check if the validator has no tokens require.True(s.T(), resVal.Validator.Tokens.IsZero()) - }, - }, - } - - for _, tc := range tt { - s.Run(tc.name, func() { - if tc.beforeRun != nil { - tc.beforeRun() - } - - err = s.Network().PoaKeeper().ExecuteRemoveValidator( - s.Network().GetContext(), - tc.valAddress, - ) - - if tc.expectedError != nil && err != nil { - require.Error(s.T(), err) - require.ErrorIs(s.T(), err, tc.expectedError) - } else { - require.NoError(s.T(), err) - } - - if tc.afterRun != nil { - tc.afterRun() - } - }) - } -} - -func (s *TestSuite) TestRemoveValidator_ExistingValidator_StatusUnbonded() { - // Validators - validators := s.Network().GetValidators() - require.NotZero(s.T(), len(validators)) - - valAddr, err := sdktypes.ValAddressFromBech32(validators[1].OperatorAddress) - require.NoError(s.T(), err) - valAccAddr := sdktypes.AccAddress(valAddr) - - tt := []struct { - name string - valAddress string - expectedError error - beforeRun func() - afterRun func() - }{ - { - name: "remove existing validator - status unbonded", - valAddress: valAccAddr.String(), - expectedError: poatypes.ErrAddressHasNoTokens, - beforeRun: func() { - // Force jail validator - valSet := s.Network().GetValidatorSet() - // Exclude validator at index 1 from validator set - require.Equal(s.T(), sdktypes.ValAddress(valSet.Validators[1].Address).String(), valAddr.String()) - vf := make([]cmtproto.BlockIDFlag, len(valSet.Validators)) - for i := range valSet.Validators { - vf[i] = cmtproto.BlockIDFlagCommit - } - vf[1] = cmtproto.BlockIDFlagAbsent - - require.NoError(s.T(), s.Network().NextNBlocksWithValidatorFlags(slashingtypes.DefaultSignedBlocksWindow+10, vf)) - - resVal, err := s.Network().GetStakingClient().Validator( - s.Network().GetContext(), - &stakingtypes.QueryValidatorRequest{ - ValidatorAddr: valAddr.String(), - }, - ) - require.NoError(s.T(), err) - - // Check if the validator is jailed - require.Equal(s.T(), resVal.Validator.Jailed, true) // Check if the validator is unbonding require.Equal(s.T(), resVal.Validator.Status, stakingtypes.Unbonding) + // Await unbonding time to pass require.NoError(s.T(), s.Network().NextBlockAfter(stakingtypes.DefaultUnbondingTime)) - require.NoError(s.T(), s.Network().NextBlock()) - resVal, err = s.Network().GetStakingClient().Validator( + _, err = s.Network().GetStakingClient().Validator( s.Network().GetContext(), &stakingtypes.QueryValidatorRequest{ ValidatorAddr: valAddr.String(), }, ) - require.NoError(s.T(), err) - // Check if the validator is unbonded - require.Equal(s.T(), resVal.Validator.Status, stakingtypes.Unbonded) - }, - }, - } + // Check if the validator is not found + require.Contains(s.T(), err.Error(), fmt.Sprintf("validator %s not found", valAddr.String())) - for _, tc := range tt { - s.Run(tc.name, func() { - if tc.beforeRun != nil { - tc.beforeRun() - } - - err = s.Network().PoaKeeper().ExecuteRemoveValidator( - s.Network().GetContext(), - tc.valAddress, - ) - - if tc.expectedError != nil && err != nil { - require.Error(s.T(), err) - require.ErrorIs(s.T(), err, tc.expectedError) - } else { - require.NoError(s.T(), err) - } - - if tc.afterRun != nil { - tc.afterRun() - } - }) - } -} - -func (s *TestSuite) TestRemoveValidator_ExistingValidator_StatusUnbonding() { - // Validators - validators := s.Network().GetValidators() - require.NotZero(s.T(), len(validators)) - - valAddr, err := sdktypes.ValAddressFromBech32(validators[1].OperatorAddress) - require.NoError(s.T(), err) - valAccAddr := sdktypes.AccAddress(valAddr) - - tt := []struct { - name string - valAddress string - expectedError error - beforeRun func() - afterRun func() - }{ - { - name: "remove existing validator - status unbonded", - valAddress: valAccAddr.String(), - expectedError: poatypes.ErrAddressHasNoTokens, - beforeRun: func() { - // Force jail validator - valSet := s.Network().GetValidatorSet() - // Exclude validator at index 1 from validator set - require.Equal(s.T(), sdktypes.ValAddress(valSet.Validators[1].Address).String(), valAddr.String()) - vf := make([]cmtproto.BlockIDFlag, len(valSet.Validators)) - for i := range valSet.Validators { - vf[i] = cmtproto.BlockIDFlagCommit - } - vf[1] = cmtproto.BlockIDFlagAbsent - - require.NoError(s.T(), s.Network().NextNBlocksWithValidatorFlags(slashingtypes.DefaultSignedBlocksWindow+10, vf)) - - resVal, err := s.Network().GetStakingClient().Validator( + info, err := s.Network().GetSlashingClient().SigningInfo( s.Network().GetContext(), - &stakingtypes.QueryValidatorRequest{ - ValidatorAddr: valAddr.String(), + &slashingtypes.QuerySigningInfoRequest{ + ConsAddress: cmtValConsAddr.String(), }, ) require.NoError(s.T(), err) - // Check if the validator is jailed - require.Equal(s.T(), resVal.Validator.Jailed, true) - - // Check if the validator is unbonding - require.Equal(s.T(), resVal.Validator.Status, stakingtypes.Unbonding) + // Check if the validator is tombstoned + require.True(s.T(), info.ValSigningInfo.Tombstoned) }, }, } + //nolint:dupl for _, tc := range tt { s.Run(tc.name, func() { if tc.beforeRun != nil { tc.beforeRun() } - err = s.Network().PoaKeeper().ExecuteRemoveValidator( - s.Network().GetContext(), - tc.valAddress, + authority := sdktypes.AccAddress(address.Module("gov")) + msg := poatypes.NewMsgRemoveValidator( + authority.String(), + valAccAddr.String(), ) + proposal, err := utils.SubmitAndAwaitProposalResolution(s.factory, s.Network(), s.keyring.GetKeys(), "test", msg) + require.NoError(s.T(), err) + + require.Equal(s.T(), govtypes.ProposalStatus_PROPOSAL_STATUS_PASSED, proposal.Status) + if tc.expectedError != nil && err != nil { require.Error(s.T(), err) require.ErrorIs(s.T(), err, tc.expectedError) From ea081830ccb01a58d0a47846fd07679ea98eccf1 Mon Sep 17 00:00:00 2001 From: GuillemGarciaDev Date: Tue, 14 Jan 2025 13:43:55 +0100 Subject: [PATCH 37/60] feat(testutil): add `abci` and `gov` testutils --- testutil/integration/exrp/utils/abci.go | 30 ++++ testutil/integration/exrp/utils/gov.go | 224 ++++++++++++++++++++++++ 2 files changed, 254 insertions(+) create mode 100644 testutil/integration/exrp/utils/abci.go create mode 100644 testutil/integration/exrp/utils/gov.go diff --git a/testutil/integration/exrp/utils/abci.go b/testutil/integration/exrp/utils/abci.go new file mode 100644 index 0000000..f0219ff --- /dev/null +++ b/testutil/integration/exrp/utils/abci.go @@ -0,0 +1,30 @@ +package utils + +import ( + cmtproto "github.com/cometbft/cometbft/proto/tendermint/types" +) + +type ValidatorFlagOverride struct { + Index int + Flag cmtproto.BlockIDFlag +} + +func NewValidatorFlagOverride(index int, flag cmtproto.BlockIDFlag) ValidatorFlagOverride { + return ValidatorFlagOverride{ + Index: index, + Flag: flag, + } +} + +func NewValidatorFlags(n int, overrides ...ValidatorFlagOverride) []cmtproto.BlockIDFlag { + flags := make([]cmtproto.BlockIDFlag, n) + for i := range flags { + flags[i] = cmtproto.BlockIDFlagCommit + } + + for _, override := range overrides { + flags[override.Index] = override.Flag + } + + return flags +} diff --git a/testutil/integration/exrp/utils/gov.go b/testutil/integration/exrp/utils/gov.go new file mode 100644 index 0000000..9340ce6 --- /dev/null +++ b/testutil/integration/exrp/utils/gov.go @@ -0,0 +1,224 @@ +// Copyright Tharsis Labs Ltd.(Evmos) +// SPDX-License-Identifier:ENCL-1.0(https://github.com/evmos/evmos/blob/main/LICENSE) + +package utils + +import ( + "errors" + "fmt" + "strconv" + + errorsmod "cosmossdk.io/errors" + "cosmossdk.io/math" + abcitypes "github.com/cometbft/cometbft/abci/types" + cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" + sdk "github.com/cosmos/cosmos-sdk/types" + govtypes "github.com/cosmos/cosmos-sdk/x/gov/types" + govv1 "github.com/cosmos/cosmos-sdk/x/gov/types/v1" + govv1beta1 "github.com/cosmos/cosmos-sdk/x/gov/types/v1beta1" + commonfactory "github.com/xrplevm/node/v5/testutil/integration/common/factory" + "github.com/xrplevm/node/v5/testutil/integration/common/keyring" + "github.com/xrplevm/node/v5/testutil/integration/common/network" +) + +// SubmitProposal is a helper function to submit a governance proposal and +// return the proposal ID. +func SubmitProposal(tf commonfactory.BaseTxFactory, network network.Network, proposerPriv cryptotypes.PrivKey, title string, msgs ...sdk.Msg) (uint64, error) { + proposerAccAddr := sdk.AccAddress(proposerPriv.PubKey().Address()).String() + proposal, err := govv1.NewMsgSubmitProposal( + msgs, + sdk.NewCoins(sdk.NewCoin(network.GetDenom(), network.GetMinDepositAmt())), + proposerAccAddr, + "", + title, + title, + false, + ) + if err != nil { + return 0, err + } + + txArgs := commonfactory.CosmosTxArgs{ + Msgs: []sdk.Msg{proposal}, + } + + return submitProposal(tf, network, proposerPriv, txArgs) +} + +// SubmitLegacyProposal is a helper function to submit a governance proposal and +// return the proposal ID. +func SubmitLegacyProposal(tf commonfactory.BaseTxFactory, network network.Network, proposerPriv cryptotypes.PrivKey, proposal govv1beta1.Content) (uint64, error) { + proposerAccAddr := sdk.AccAddress(proposerPriv.PubKey().Address()) + + msgSubmitProposal, err := govv1beta1.NewMsgSubmitProposal( + proposal, + sdk.NewCoins(sdk.NewCoin(network.GetDenom(), math.NewInt(1e18))), + proposerAccAddr, + ) + if err != nil { + return 0, err + } + + txArgs := commonfactory.CosmosTxArgs{ + Msgs: []sdk.Msg{msgSubmitProposal}, + } + + return submitProposal(tf, network, proposerPriv, txArgs) +} + +// VoteOnProposal is a helper function to vote on a governance proposal given the private key of the voter and +// the option to vote. +func VoteOnProposal(tf commonfactory.BaseTxFactory, voterPriv cryptotypes.PrivKey, proposalID uint64, option govv1.VoteOption) (abcitypes.ExecTxResult, error) { + voterAccAddr := sdk.AccAddress(voterPriv.PubKey().Address()) + + msgVote := govv1.NewMsgVote( + voterAccAddr, + proposalID, + option, + "", + ) + + res, err := tf.CommitCosmosTx(voterPriv, commonfactory.CosmosTxArgs{ + Msgs: []sdk.Msg{msgVote}, + }) + + return res, err +} + +// ApproveProposal is a helper function to vote 'yes' +// for it and wait till it passes. +func ApproveProposal(tf commonfactory.BaseTxFactory, network network.Network, proposerPriv cryptotypes.PrivKey, proposalID uint64) error { + // Vote on proposal + if _, err := VoteOnProposal(tf, proposerPriv, proposalID, govv1.OptionYes); err != nil { + return errorsmod.Wrap(err, "failed to vote on proposal") + } + + if err := waitVotingPeriod(network); err != nil { + return errorsmod.Wrap(err, "failed to wait for voting period to pass") + } + + return checkProposalStatus(network, proposalID, govv1.StatusPassed) +} + +func SubmitAndAwaitProposalResolution(tf commonfactory.BaseTxFactory, network network.Network, keys []keyring.Key, title string, msgs ...sdk.Msg) (*govv1.Proposal, error) { + proposalID, err := SubmitProposal(tf, network, keys[0].Priv, title, msgs...) + if err != nil { + return nil, err + } + + for _, key := range keys { + _, err := VoteOnProposal(tf, key.Priv, proposalID, govv1.OptionYes) + if err != nil { + return nil, err + } + } + + if err := waitVotingPeriod(network); err != nil { + return nil, errorsmod.Wrap(err, "failed to wait for voting period to pass") + } + + return retrieveResolvedProposal(network, proposalID) +} + +func retrieveResolvedProposal(network network.Network, proposalID uint64) (*govv1.Proposal, error) { + gq := network.GetGovClient() + proposalRes, err := gq.Proposal(network.GetContext(), &govv1.QueryProposalRequest{ProposalId: proposalID}) + if err != nil { + return nil, err + } + return proposalRes.Proposal, nil +} + +// getProposalIDFromEvents returns the proposal ID from the events in +// the ResponseDeliverTx. +func getProposalIDFromEvents(events []abcitypes.Event) (uint64, error) { + var ( + err error + found bool + proposalID uint64 + ) + + for _, event := range events { + if event.Type != govtypes.EventTypeProposalDeposit { + continue + } + + for _, attr := range event.Attributes { + if attr.Key != govtypes.AttributeKeyProposalID { + continue + } + + proposalID, err = strconv.ParseUint(attr.Value, 10, 64) + if err != nil { + return 0, errorsmod.Wrap(err, "failed to parse proposal ID") + } + + found = true + break + } + + if found { + break + } + } + + if !found { + return 0, errors.New("proposal deposit not found") + } + + return proposalID, nil +} + +func submitProposal(tf commonfactory.BaseTxFactory, network network.Network, proposerPriv cryptotypes.PrivKey, txArgs commonfactory.CosmosTxArgs) (uint64, error) { + res, err := tf.CommitCosmosTx(proposerPriv, txArgs) + if err != nil { + return 0, err + } + + proposalID, err := getProposalIDFromEvents(res.Events) + if err != nil { + return 0, errorsmod.Wrap(err, "failed to get proposal ID from events") + } + + err = network.NextBlock() + if err != nil { + return 0, errorsmod.Wrap(err, "failed to commit block after proposal") + } + + if err := checkProposalStatus(network, proposalID, govv1.StatusVotingPeriod); err != nil { + return 0, errorsmod.Wrap(err, "error while checking proposal") + } + + return proposalID, nil +} + +// waitVotingPeriod is a helper function that waits for the current voting period +// defined in the gov module params to pass +func waitVotingPeriod(n network.Network) error { + gq := n.GetGovClient() + params, err := gq.Params(n.GetContext(), &govv1.QueryParamsRequest{ParamsType: "voting"}) + if err != nil { + return errorsmod.Wrap(err, "failed to query voting params") + } + + err = n.NextBlockAfter(*params.Params.VotingPeriod) // commit after voting period is over + if err != nil { + return errorsmod.Wrap(err, "failed to commit block after voting period ends") + } + + return n.NextBlock() +} + +// checkProposalStatus is a helper function to check for a specific proposal status +func checkProposalStatus(n network.Network, proposalID uint64, expStatus govv1.ProposalStatus) error { + gq := n.GetGovClient() + proposalRes, err := gq.Proposal(n.GetContext(), &govv1.QueryProposalRequest{ProposalId: proposalID}) + if err != nil { + return errorsmod.Wrap(err, "failed to query proposal") + } + + if proposalRes.Proposal.Status != expStatus { + return fmt.Errorf("proposal status different than expected. Expected %s; got: %s", expStatus.String(), proposalRes.Proposal.Status.String()) + } + return nil +} From 4843e6bc0cec8ef2e870ef542142074c62be606c Mon Sep 17 00:00:00 2001 From: GuillemGarciaDev Date: Tue, 14 Jan 2025 13:44:48 +0100 Subject: [PATCH 38/60] feat(testutil): add new config options --- tests/integration/suite.go | 6 +++- testutil/integration/common/grpc/gov.go | 28 +++++++++++++++++++ .../integration/common/network/network.go | 5 +++- testutil/integration/exrp/common/config.go | 24 ++++++++++++++++ .../integration/exrp/integration/network.go | 18 +++++++++++- .../integration/exrp/integration/setup.go | 13 +++++---- testutil/integration/exrp/upgrade/network.go | 6 ++++ 7 files changed, 91 insertions(+), 9 deletions(-) create mode 100644 testutil/integration/common/grpc/gov.go diff --git a/tests/integration/suite.go b/tests/integration/suite.go index 848e610..e80088f 100644 --- a/tests/integration/suite.go +++ b/tests/integration/suite.go @@ -1,6 +1,7 @@ package integration import ( + sdkmath "cosmossdk.io/math" sdk "github.com/cosmos/cosmos-sdk/types" evmtypes "github.com/evmos/evmos/v20/x/evm/types" "github.com/stretchr/testify/suite" @@ -28,7 +29,7 @@ func (s *TestSuite) SetupTest() { s.network.SetupSdkConfig() s.Require().Equal(sdk.GetConfig().GetBech32AccountAddrPrefix(), "ethm") // Check that the network was created successfully - kr := keyring.New(3) + kr := keyring.New(5) customGenesis := exrpcommon.CustomGenesisState{} @@ -42,6 +43,9 @@ func (s *TestSuite) SetupTest() { exrpcommon.WithPreFundedAccounts(kr.GetAllAccAddrs()...), exrpcommon.WithAmountOfValidators(5), exrpcommon.WithCustomGenesis(customGenesis), + exrpcommon.WithBondDenom("apoa"), + exrpcommon.WithMinDepositAmt(sdkmath.NewInt(1)), + exrpcommon.WithValidatorOperators(kr.GetAllAccAddrs()), ) s.Require().NotNil(s.network) diff --git a/testutil/integration/common/grpc/gov.go b/testutil/integration/common/grpc/gov.go new file mode 100644 index 0000000..e916c7d --- /dev/null +++ b/testutil/integration/common/grpc/gov.go @@ -0,0 +1,28 @@ +// Copyright Tharsis Labs Ltd.(Evmos) +// SPDX-License-Identifier:ENCL-1.0(https://github.com/evmos/evmos/blob/main/LICENSE) + +package grpc + +import ( + "fmt" + "slices" + + govtypes "github.com/cosmos/cosmos-sdk/x/gov/types/v1" +) + +// GetGovParams returns the gov params from the gov module. +func (gqh *IntegrationHandler) GetGovParams(paramsType string) (*govtypes.QueryParamsResponse, error) { + possibleTypes := []string{"deposit", "tallying", "voting"} + if !slices.Contains(possibleTypes, paramsType) { + return nil, fmt.Errorf("invalid params type: %s\npossible types: %s", paramsType, possibleTypes) + } + + govClient := gqh.network.GetGovClient() + return govClient.Params(gqh.network.GetContext(), &govtypes.QueryParamsRequest{ParamsType: paramsType}) +} + +// GetProposal returns the proposal from the gov module. +func (gqh *IntegrationHandler) GetProposal(proposalID uint64) (*govtypes.QueryProposalResponse, error) { + govClient := gqh.network.GetGovClient() + return govClient.Proposal(gqh.network.GetContext(), &govtypes.QueryProposalRequest{ProposalId: proposalID}) +} diff --git a/testutil/integration/common/network/network.go b/testutil/integration/common/network/network.go index 57a5666..bb98e18 100644 --- a/testutil/integration/common/network/network.go +++ b/testutil/integration/common/network/network.go @@ -7,6 +7,7 @@ import ( "testing" "time" + sdkmath "cosmossdk.io/math" abcitypes "github.com/cometbft/cometbft/abci/types" sdktypes "github.com/cosmos/cosmos-sdk/types" sdktestutil "github.com/cosmos/cosmos-sdk/types/module/testutil" @@ -15,6 +16,7 @@ import ( authz "github.com/cosmos/cosmos-sdk/x/authz" banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" distrtypes "github.com/cosmos/cosmos-sdk/x/distribution/types" + govtypes "github.com/cosmos/cosmos-sdk/x/gov/types/v1" stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" ibctesting "github.com/cosmos/ibc-go/v8/testing" feemarkettypes "github.com/evmos/evmos/v20/x/feemarket/types" @@ -30,7 +32,7 @@ type Network interface { GetDenom() string GetOtherDenoms() []string GetValidators() []stakingtypes.Validator - + GetMinDepositAmt() sdkmath.Int NextBlock() error NextBlockAfter(duration time.Duration) error NextBlockWithTxs(txBytes ...[]byte) (*abcitypes.ResponseFinalizeBlock, error) @@ -42,6 +44,7 @@ type Network interface { GetStakingClient() stakingtypes.QueryClient GetDistrClient() distrtypes.QueryClient GetFeeMarketClient() feemarkettypes.QueryClient + GetGovClient() govtypes.QueryClient BroadcastTxSync(txBytes []byte) (abcitypes.ExecTxResult, error) Simulate(txBytes []byte) (*txtypes.SimulateResponse, error) diff --git a/testutil/integration/exrp/common/config.go b/testutil/integration/exrp/common/config.go index fbf6431..87a9d28 100644 --- a/testutil/integration/exrp/common/config.go +++ b/testutil/integration/exrp/common/config.go @@ -3,6 +3,7 @@ package exrpcommon import ( "math/big" + sdkmath "cosmossdk.io/math" "github.com/cosmos/cosmos-sdk/baseapp" sdktypes "github.com/cosmos/cosmos-sdk/types" banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" @@ -24,12 +25,15 @@ type Config struct { AmountOfValidators int PreFundedAccounts []sdktypes.AccAddress Balances []banktypes.Balance + BondDenom string Denom string CustomGenesisState CustomGenesisState GenesisBytes []byte OtherCoinDenom []string OperatorsAddrs []sdktypes.AccAddress CustomBaseAppOpts []func(*baseapp.BaseApp) + MinDepositAmt sdkmath.Int + Quorum string } type CustomGenesisState map[string]interface{} @@ -90,6 +94,13 @@ func WithDenom(denom string) ConfigOption { } } +// WithBondDenom sets the bond denom for the network. +func WithBondDenom(denom string) ConfigOption { + return func(cfg *Config) { + cfg.BondDenom = denom + } +} + // WithCustomGenesis sets the custom genesis of the network for specific modules. func WithCustomGenesis(customGenesis CustomGenesisState) ConfigOption { return func(cfg *Config) { @@ -117,3 +128,16 @@ func WithCustomBaseAppOpts(opts ...func(*baseapp.BaseApp)) ConfigOption { cfg.CustomBaseAppOpts = opts } } + +// WithMinDepositAmt sets the min deposit amount for the network. +func WithMinDepositAmt(minDepositAmt sdkmath.Int) ConfigOption { + return func(cfg *Config) { + cfg.MinDepositAmt = minDepositAmt + } +} + +func WithQuorum(quorum string) ConfigOption { + return func(cfg *Config) { + cfg.Quorum = quorum + } +} diff --git a/testutil/integration/exrp/integration/network.go b/testutil/integration/exrp/integration/network.go index 56d7480..4be5d1e 100644 --- a/testutil/integration/exrp/integration/network.go +++ b/testutil/integration/exrp/integration/network.go @@ -120,7 +120,8 @@ func (n *IntegrationNetwork) configureAndInitChain() error { delegations: delegations, } govParams := GovCustomGenesisState{ - denom: n.cfg.Denom, + denom: n.cfg.Denom, + minDepositAmt: n.cfg.MinDepositAmt, } totalSupply := calculateTotalSupply(fundedAccountBalances) @@ -248,6 +249,21 @@ func (n *IntegrationNetwork) GetEIP155ChainID() *big.Int { return n.cfg.EIP155ChainID } +// GetValidatorSet returns the network's validator set +func (n *IntegrationNetwork) GetValidatorSet() *cmttypes.ValidatorSet { + return n.valSet +} + +// GetValidatorSigners returns the network's validator signers +func (n *IntegrationNetwork) GetValidatorSigners() map[string]cmttypes.PrivValidator { + return n.valSigners +} + +// GetMinDepositAmt returns the network's min deposit amount +func (n *IntegrationNetwork) GetMinDepositAmt() sdkmath.Int { + return n.cfg.MinDepositAmt +} + // GetChainConfig returns the network's chain config func (n *IntegrationNetwork) GetEVMChainConfig() *gethparams.ChainConfig { params := n.app.EvmKeeper.GetParams(n.ctx) diff --git a/testutil/integration/exrp/integration/setup.go b/testutil/integration/exrp/integration/setup.go index c684b0f..c5b637b 100644 --- a/testutil/integration/exrp/integration/setup.go +++ b/testutil/integration/exrp/integration/setup.go @@ -8,7 +8,6 @@ import ( "slices" "time" - "github.com/cosmos/cosmos-sdk/testutil/mock" "github.com/cosmos/gogoproto/proto" capabilitytypes "github.com/cosmos/ibc-go/modules/capability/types" @@ -17,6 +16,7 @@ import ( cmttypes "github.com/cometbft/cometbft/types" codectypes "github.com/cosmos/cosmos-sdk/codec/types" cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec" + "github.com/cosmos/cosmos-sdk/testutil/mock" sdktypes "github.com/cosmos/cosmos-sdk/types" authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" @@ -156,7 +156,7 @@ func createBalances(accounts []sdktypes.AccAddress, denoms []string) []banktypes // createStakingValidator creates a staking validator from the given tm validator and bonded func createStakingValidator(val *cmttypes.Validator, bondedAmt sdkmath.Int, operatorAddr *sdktypes.AccAddress) (stakingtypes.Validator, error) { - pk, err := cryptocodec.FromTmPubKeyInterface(val.PubKey) //nolint:staticcheck + pk, err := cryptocodec.FromCmtPubKeyInterface(val.PubKey) if err != nil { return stakingtypes.Validator{}, err } @@ -179,7 +179,7 @@ func createStakingValidator(val *cmttypes.Validator, bondedAmt sdkmath.Int, oper Jailed: false, Status: stakingtypes.Bonded, Tokens: bondedAmt, - DelegatorShares: sdkmath.LegacyOneDec(), + DelegatorShares: sdktypes.DefaultPowerReduction.ToLegacyDec(), Description: stakingtypes.Description{}, UnbondingHeight: int64(0), UnbondingTime: time.Unix(0, 0).UTC(), @@ -239,7 +239,7 @@ func createDelegations(validators []stakingtypes.Validator) []stakingtypes.Deleg if err != nil { panic(err) } - delegation := stakingtypes.NewDelegation(sdktypes.AccAddress(valAddr).String(), val.OperatorAddress, sdkmath.LegacyOneDec()) + delegation := stakingtypes.NewDelegation(sdktypes.AccAddress(valAddr).String(), val.OperatorAddress, sdktypes.DefaultPowerReduction.ToLegacyDec()) delegations = append(delegations, delegation) } return delegations @@ -418,14 +418,15 @@ func setAuthGenesisState(app *app.App, genesisState evmostypes.GenesisState, cus // GovCustomGenesisState defines the gov genesis state type GovCustomGenesisState struct { - denom string + denom string + minDepositAmt sdkmath.Int } // setDefaultGovGenesisState sets the default gov genesis state func setDefaultGovGenesisState(app *app.App, genesisState evmostypes.GenesisState, overwriteParams GovCustomGenesisState) evmostypes.GenesisState { govGen := govtypesv1.DefaultGenesisState() updatedParams := govGen.Params - minDepositAmt := sdkmath.NewInt(1e18) + minDepositAmt := overwriteParams.minDepositAmt updatedParams.MinDeposit = sdktypes.NewCoins(sdktypes.NewCoin(overwriteParams.denom, minDepositAmt)) updatedParams.ExpeditedMinDeposit = sdktypes.NewCoins(sdktypes.NewCoin(overwriteParams.denom, minDepositAmt.MulRaw(2))) govGen.Params = updatedParams diff --git a/testutil/integration/exrp/upgrade/network.go b/testutil/integration/exrp/upgrade/network.go index 4d31bae..0f53c0e 100644 --- a/testutil/integration/exrp/upgrade/network.go +++ b/testutil/integration/exrp/upgrade/network.go @@ -10,6 +10,7 @@ import ( "math/big" "time" + sdkmath "cosmossdk.io/math" "github.com/xrplevm/node/v5/app" exrpcommon "github.com/xrplevm/node/v5/testutil/integration/exrp/common" @@ -259,6 +260,11 @@ func (n *UpgradeIntegrationNetwork) GetValidators() []stakingtypes.Validator { return n.validators } +// GetMinDepositAmt returns the network's min deposit amount +func (n *UpgradeIntegrationNetwork) GetMinDepositAmt() sdkmath.Int { + return n.cfg.MinDepositAmt +} + // GetOtherDenoms returns network's other supported denoms func (n *UpgradeIntegrationNetwork) GetEncodingConfig() sdktestutil.TestEncodingConfig { return sdktestutil.TestEncodingConfig{ From 3a5b7c13397992c29cdf02c0975fabb3928f85ac Mon Sep 17 00:00:00 2001 From: GuillemGarciaDev Date: Tue, 14 Jan 2025 14:31:30 +0100 Subject: [PATCH 39/60] refactor(testutil): dedicated abci block api for each integration network --- testutil/integration/exrp/common/abci.go | 38 ------------------- testutil/integration/exrp/integration/abci.go | 35 ++++++++++++++++- .../integration/exrp/integration/network.go | 4 +- testutil/integration/exrp/upgrade/abci.go | 36 +++++++++++++++++- testutil/integration/exrp/upgrade/network.go | 4 +- 5 files changed, 71 insertions(+), 46 deletions(-) delete mode 100644 testutil/integration/exrp/common/abci.go diff --git a/testutil/integration/exrp/common/abci.go b/testutil/integration/exrp/common/abci.go deleted file mode 100644 index b17f6f5..0000000 --- a/testutil/integration/exrp/common/abci.go +++ /dev/null @@ -1,38 +0,0 @@ -package exrpcommon - -import ( - abcitypes "github.com/cometbft/cometbft/abci/types" - cmtproto "github.com/cometbft/cometbft/proto/tendermint/types" - cmttypes "github.com/cometbft/cometbft/types" -) - -// buildFinalizeBlockReq is a helper function to build -// properly the FinalizeBlock request -func BuildFinalizeBlockReq(header cmtproto.Header, validators []*cmttypes.Validator, validatorFlags []cmtproto.BlockIDFlag, misbehaviors []abcitypes.Misbehavior, txs ...[]byte) *abcitypes.RequestFinalizeBlock { - // add validator's commit info to allocate corresponding tokens to validators - ci := GetCommitInfo(validators, validatorFlags) - return &abcitypes.RequestFinalizeBlock{ - Misbehavior: misbehaviors, - Height: header.Height, - DecidedLastCommit: ci, - Hash: header.AppHash, - NextValidatorsHash: header.ValidatorsHash, - ProposerAddress: header.ProposerAddress, - Time: header.Time, - Txs: txs, - } -} - -func GetCommitInfo(validators []*cmttypes.Validator, validatorFlags []cmtproto.BlockIDFlag) abcitypes.CommitInfo { - voteInfos := make([]abcitypes.VoteInfo, len(validators)) - for i, val := range validators { - voteInfos[i] = abcitypes.VoteInfo{ - Validator: abcitypes.Validator{ - Address: val.Address, - Power: val.VotingPower, - }, - BlockIdFlag: validatorFlags[i], - } - } - return abcitypes.CommitInfo{Votes: voteInfos} -} diff --git a/testutil/integration/exrp/integration/abci.go b/testutil/integration/exrp/integration/abci.go index a4f3fb7..d7328c9 100644 --- a/testutil/integration/exrp/integration/abci.go +++ b/testutil/integration/exrp/integration/abci.go @@ -8,7 +8,7 @@ import ( storetypes "cosmossdk.io/store/types" abcitypes "github.com/cometbft/cometbft/abci/types" cmtproto "github.com/cometbft/cometbft/proto/tendermint/types" - exrpcommon "github.com/xrplevm/node/v5/testutil/integration/exrp/common" + cmttypes "github.com/cometbft/cometbft/types" ) // NextBlock is a private helper function that runs the EndBlocker logic, commits the changes, @@ -72,7 +72,7 @@ func (n *IntegrationNetwork) finalizeBlockAndCommit(duration time.Duration, vFla } // FinalizeBlock to run endBlock, deliverTx & beginBlock logic - req := exrpcommon.BuildFinalizeBlockReq(header, n.valSet.Validators, validatorFlags, misbehaviors, txBytes...) + req := BuildFinalizeBlockReq(header, n.valSet.Validators, validatorFlags, misbehaviors, txBytes...) res, err := n.app.FinalizeBlock(req) if err != nil { @@ -96,3 +96,34 @@ func (n *IntegrationNetwork) finalizeBlockAndCommit(duration time.Duration, vFla return res, err } + +// buildFinalizeBlockReq is a helper function to build +// properly the FinalizeBlock request +func BuildFinalizeBlockReq(header cmtproto.Header, validators []*cmttypes.Validator, validatorFlags []cmtproto.BlockIDFlag, misbehaviors []abcitypes.Misbehavior, txs ...[]byte) *abcitypes.RequestFinalizeBlock { + // add validator's commit info to allocate corresponding tokens to validators + ci := GetCommitInfo(validators, validatorFlags) + return &abcitypes.RequestFinalizeBlock{ + Misbehavior: misbehaviors, + Height: header.Height, + DecidedLastCommit: ci, + Hash: header.AppHash, + NextValidatorsHash: header.ValidatorsHash, + ProposerAddress: header.ProposerAddress, + Time: header.Time, + Txs: txs, + } +} + +func GetCommitInfo(validators []*cmttypes.Validator, validatorFlags []cmtproto.BlockIDFlag) abcitypes.CommitInfo { + voteInfos := make([]abcitypes.VoteInfo, len(validators)) + for i, val := range validators { + voteInfos[i] = abcitypes.VoteInfo{ + Validator: abcitypes.Validator{ + Address: val.Address, + Power: val.VotingPower, + }, + BlockIdFlag: validatorFlags[i], + } + } + return abcitypes.CommitInfo{Votes: voteInfos} +} diff --git a/testutil/integration/exrp/integration/network.go b/testutil/integration/exrp/integration/network.go index 0b53c38..85f6793 100644 --- a/testutil/integration/exrp/integration/network.go +++ b/testutil/integration/exrp/integration/network.go @@ -205,7 +205,7 @@ func (n *IntegrationNetwork) configureAndInitChain() error { }, } - req := exrpcommon.BuildFinalizeBlockReq(header, valSet.Validators, valFlags, nil) + req := BuildFinalizeBlockReq(header, valSet.Validators, valFlags, nil) if _, err := exrpApp.FinalizeBlock(req); err != nil { return err } @@ -319,7 +319,7 @@ func (n *IntegrationNetwork) BroadcastTxSync(txBytes []byte) (abcitypes.ExecTxRe newBlockTime := header.Time.Add(time.Second) header.Time = newBlockTime - req := exrpcommon.BuildFinalizeBlockReq(header, n.valSet.Validators, n.valFlags, nil, txBytes) + req := BuildFinalizeBlockReq(header, n.valSet.Validators, n.valFlags, nil, txBytes) // dont include the DecidedLastCommit because we're not committing the changes // here, is just for broadcasting the tx. To persist the changes, use the diff --git a/testutil/integration/exrp/upgrade/abci.go b/testutil/integration/exrp/upgrade/abci.go index a58060a..b3a82c9 100644 --- a/testutil/integration/exrp/upgrade/abci.go +++ b/testutil/integration/exrp/upgrade/abci.go @@ -7,7 +7,8 @@ import ( storetypes "cosmossdk.io/store/types" abcitypes "github.com/cometbft/cometbft/abci/types" - exrpcommon "github.com/xrplevm/node/v5/testutil/integration/exrp/common" + cmtproto "github.com/cometbft/cometbft/proto/tendermint/types" + cmttypes "github.com/cometbft/cometbft/types" ) // NextBlock is a private helper function that runs the EndBlocker logic, commits the changes, @@ -43,7 +44,7 @@ func (n *UpgradeIntegrationNetwork) finalizeBlockAndCommit(duration time.Duratio header.Time = newBlockTime // FinalizeBlock to run endBlock, deliverTx & beginBlock logic - req := exrpcommon.BuildFinalizeBlockReq(header, n.valSet.Validators, nil, nil, txBytes...) + req := BuildFinalizeBlockReq(header, n.valSet.Validators, txBytes...) res, err := n.app.FinalizeBlock(req) if err != nil { @@ -67,3 +68,34 @@ func (n *UpgradeIntegrationNetwork) finalizeBlockAndCommit(duration time.Duratio return res, err } + +// buildFinalizeBlockReq is a helper function to build +// properly the FinalizeBlock request +func BuildFinalizeBlockReq(header cmtproto.Header, validators []*cmttypes.Validator, txs ...[]byte) *abcitypes.RequestFinalizeBlock { + // add validator's commit info to allocate corresponding tokens to validators + ci := GetCommitInfo(validators) + return &abcitypes.RequestFinalizeBlock{ + Misbehavior: nil, + Height: header.Height, + DecidedLastCommit: ci, + Hash: header.AppHash, + NextValidatorsHash: header.ValidatorsHash, + ProposerAddress: header.ProposerAddress, + Time: header.Time, + Txs: txs, + } +} + +func GetCommitInfo(validators []*cmttypes.Validator) abcitypes.CommitInfo { + voteInfos := make([]abcitypes.VoteInfo, len(validators)) + for i, val := range validators { + voteInfos[i] = abcitypes.VoteInfo{ + Validator: abcitypes.Validator{ + Address: val.Address, + Power: val.VotingPower, + }, + BlockIdFlag: cmtproto.BlockIDFlagCommit, + } + } + return abcitypes.CommitInfo{Votes: voteInfos} +} diff --git a/testutil/integration/exrp/upgrade/network.go b/testutil/integration/exrp/upgrade/network.go index caff823..3a64756 100644 --- a/testutil/integration/exrp/upgrade/network.go +++ b/testutil/integration/exrp/upgrade/network.go @@ -194,7 +194,7 @@ func (n *UpgradeIntegrationNetwork) configureAndInitChain() error { }, } - req := exrpcommon.BuildFinalizeBlockReq(header, valSet.Validators, nil, nil) + req := BuildFinalizeBlockReq(header, valSet.Validators, nil, nil) if _, err := exrpApp.FinalizeBlock(req); err != nil { return err } @@ -291,7 +291,7 @@ func (n *UpgradeIntegrationNetwork) BroadcastTxSync(txBytes []byte) (abcitypes.E newBlockTime := header.Time.Add(time.Second) header.Time = newBlockTime - req := exrpcommon.BuildFinalizeBlockReq(header, n.valSet.Validators, nil, nil, txBytes) + req := BuildFinalizeBlockReq(header, n.valSet.Validators, txBytes) // dont include the DecidedLastCommit because we're not committing the changes // here, is just for broadcasting the tx. To persist the changes, use the From 41b998986580e5d86ed38b588743cd262e239ed2 Mon Sep 17 00:00:00 2001 From: GuillemGarciaDev Date: Tue, 14 Jan 2025 15:07:55 +0100 Subject: [PATCH 40/60] refactor(tests): `MsgAddValidator` integration tests --- tests/integration/poa_test.go | 231 ++++++++++++++++++++++------------ 1 file changed, 148 insertions(+), 83 deletions(-) diff --git a/tests/integration/poa_test.go b/tests/integration/poa_test.go index 87713a1..7c3e98f 100644 --- a/tests/integration/poa_test.go +++ b/tests/integration/poa_test.go @@ -7,10 +7,11 @@ import ( abcitypes "github.com/cometbft/cometbft/abci/types" cmtproto "github.com/cometbft/cometbft/proto/tendermint/types" + cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" sdktypes "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/types/address" simtypes "github.com/cosmos/cosmos-sdk/types/simulation" - govtypes "github.com/cosmos/cosmos-sdk/x/gov/types/v1" + govv1 "github.com/cosmos/cosmos-sdk/x/gov/types/v1" slashingtypes "github.com/cosmos/cosmos-sdk/x/slashing/types" stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" "github.com/stretchr/testify/require" @@ -21,38 +22,42 @@ import ( // AddValidator tests func (s *TestSuite) TestAddValidator_UnexistingValidator() { - validator := s.Network().GetValidators()[0] - valAddr, err := sdktypes.ValAddressFromBech32(validator.OperatorAddress) - require.NoError(s.T(), err) - // Generate a random account - randomAccs := simtypes.RandomAccounts(rand.New(rand.NewSource(time.Now().UnixNano())), 1) + randomAccs := simtypes.RandomAccounts(rand.New(rand.NewSource(time.Now().UnixNano())), 1) //nolint:gosec randomAcc := randomAccs[0] + randomValAddr := sdktypes.ValAddress(randomAcc.Address.Bytes()) tt := []struct { name string valAddress string + valPubKey cryptotypes.PubKey expectedError error beforeRun func() afterRun func() }{ { - name: "add unexisting validator - random address", + name: "add unexisting validator - random address", valAddress: randomAcc.Address.String(), + valPubKey: randomAcc.ConsKey.PubKey(), afterRun: func() { - require.NoError(s.T(), s.Network().NextBlock()) resVal, err := s.Network().GetStakingClient().Validator( s.Network().GetContext(), &stakingtypes.QueryValidatorRequest{ - ValidatorAddr: valAddr.String(), + ValidatorAddr: randomValAddr.String(), }, ) require.NoError(s.T(), err) - // Check if the validator is unbonding + // Check if the validator is bonded require.Equal(s.T(), resVal.Validator.Status, stakingtypes.Bonded) + + // Check if the validator has the default amount of tokens + require.Equal(s.T(), sdktypes.DefaultPowerReduction, resVal.Validator.Tokens) + + // Check if the validator has the default delegator shares + require.Equal(s.T(), sdktypes.DefaultPowerReduction.ToLegacyDec(), resVal.Validator.DelegatorShares) }, }, } @@ -66,8 +71,8 @@ func (s *TestSuite) TestAddValidator_UnexistingValidator() { authority := sdktypes.AccAddress(address.Module("gov")) msg, err := poatypes.NewMsgAddValidator( authority.String(), - randomAcc.Address.String(), - randomAcc.ConsKey.PubKey(), + tc.valAddress, + tc.valPubKey, stakingtypes.Description{ Moniker: "test", }, @@ -77,8 +82,7 @@ func (s *TestSuite) TestAddValidator_UnexistingValidator() { proposal, err := utils.SubmitAndAwaitProposalResolution(s.factory, s.Network(), s.keyring.GetKeys(), "test", msg) require.NoError(s.T(), err) - require.Equal(s.T(), govtypes.ProposalStatus_PROPOSAL_STATUS_PASSED, proposal.Status) - + require.Equal(s.T(), govv1.ProposalStatus_PROPOSAL_STATUS_PASSED, proposal.Status) if tc.expectedError != nil && err != nil { require.Error(s.T(), err) @@ -96,9 +100,8 @@ func (s *TestSuite) TestAddValidator_UnexistingValidator() { func (s *TestSuite) TestAddValidator_InvalidMsgAddValidator() { // Generate a random account - randomAccs := simtypes.RandomAccounts(rand.New(rand.NewSource(time.Now().UnixNano())), 1) + randomAccs := simtypes.RandomAccounts(rand.New(rand.NewSource(time.Now().UnixNano())), 1) //nolint:gosec randomAcc := randomAccs[0] - randomAccPubKey, _ := types1.NewAnyWithValue(randomAcc.ConsKey.PubKey()) validator := s.Network().GetValidators()[0] valAddr, err := sdktypes.ValAddressFromBech32(validator.OperatorAddress) @@ -108,43 +111,58 @@ func (s *TestSuite) TestAddValidator_InvalidMsgAddValidator() { tt := []struct { name string valAddress string - valPubKey *types1.Any + valPubKey cryptotypes.PubKey expectedError error beforeRun func() afterRun func() }{ { - name: "add validator - already used pubkey", - valAddress: randomAcc.Address.String(), - valPubKey: validator.ConsensusPubkey, + name: "add validator - already used pubkey", + valAddress: randomAcc.Address.String(), + valPubKey: validator.ConsensusPubkey.GetCachedValue().(cryptotypes.PubKey), expectedError: stakingtypes.ErrValidatorPubKeyExists, }, { - name: "add validator - already used validator address", - valAddress: valAccAddr.String(), - valPubKey: randomAccPubKey, - expectedError: poatypes.ErrAddressHasBankTokens, + name: "add validator - already used validator address", + valAddress: valAccAddr.String(), + valPubKey: randomAcc.ConsKey.PubKey(), + expectedError: poatypes.ErrAddressHasBondedTokens, + beforeRun: func() { + // Check if the validator exists + _, err := s.Network().GetStakingClient().Validator( + s.Network().GetContext(), + &stakingtypes.QueryValidatorRequest{ + ValidatorAddr: valAddr.String(), + }, + ) + require.NoError(s.T(), err) + }, }, } - + //nolint:dupl for _, tc := range tt { s.Run(tc.name, func() { if tc.beforeRun != nil { tc.beforeRun() } - err := s.Network().PoaKeeper().ExecuteAddValidator( - s.Network().GetContext(), - &poatypes.MsgAddValidator{ - ValidatorAddress: randomAcc.Address.String(), - Authority: authtypes.NewModuleAddress(govtypes.ModuleName).String(), - Pubkey: validator.ConsensusPubkey, - Description: stakingtypes.Description{ - Moniker: "test", - }, + authority := sdktypes.AccAddress(address.Module("gov")) + msg, err := poatypes.NewMsgAddValidator( + authority.String(), + tc.valAddress, + tc.valPubKey, + stakingtypes.Description{ + Moniker: "test", }, ) + require.NoError(s.T(), err) + + proposal, err := utils.SubmitAndAwaitProposalResolution(s.factory, s.Network(), s.keyring.GetKeys(), "test", msg) + require.NoError(s.T(), err) + + require.Equal(s.T(), govv1.ProposalStatus_PROPOSAL_STATUS_FAILED, proposal.Status) + require.Contains(s.T(), proposal.FailedReason, tc.expectedError.Error()) if tc.expectedError != nil && err != nil { require.Error(s.T(), err) @@ -169,13 +187,15 @@ func (s *TestSuite) TestAddValidator_ExistingValidator_StatusBonded() { tt := []struct { name string valAddress string + valPubKey cryptotypes.PubKey expectedError error beforeRun func() afterRun func() }{ { name: "add existing validator - status bonded", - valAddress: valAddr.String(), + valAddress: valAccAddr.String(), + valPubKey: validator.ConsensusPubkey.GetCachedValue().(cryptotypes.PubKey), expectedError: poatypes.ErrAddressHasBondedTokens, beforeRun: func() { resVal, err := s.Network().GetStakingClient().Validator( @@ -189,26 +209,44 @@ func (s *TestSuite) TestAddValidator_ExistingValidator_StatusBonded() { // Check if the validator is bonded require.Equal(s.T(), resVal.Validator.Status, stakingtypes.Bonded) }, + afterRun: func() { + resVal, err := s.Network().GetStakingClient().Validator( + s.Network().GetContext(), + &stakingtypes.QueryValidatorRequest{ + ValidatorAddr: valAddr.String(), + }, + ) + require.NoError(s.T(), err) + + // Check if the validator is still bonded + require.Equal(s.T(), resVal.Validator.Status, stakingtypes.Bonded) + }, }, } + //nolint:dupl for _, tc := range tt { s.Run(tc.name, func() { if tc.beforeRun != nil { tc.beforeRun() } - err := s.Network().PoaKeeper().ExecuteAddValidator( - s.Network().GetContext(), - &poatypes.MsgAddValidator{ - ValidatorAddress: valAccAddr.String(), - Authority: authtypes.NewModuleAddress(govtypes.ModuleName).String(), - Pubkey: validator.ConsensusPubkey, - Description: stakingtypes.Description{ - Moniker: "test", - }, + authority := sdktypes.AccAddress(address.Module("gov")) + msg, err := poatypes.NewMsgAddValidator( + authority.String(), + tc.valAddress, + tc.valPubKey, + stakingtypes.Description{ + Moniker: "test", }, ) + require.NoError(s.T(), err) + + proposal, err := utils.SubmitAndAwaitProposalResolution(s.factory, s.Network(), s.keyring.GetKeys(), "test", msg) + require.NoError(s.T(), err) + + require.Equal(s.T(), govv1.ProposalStatus_PROPOSAL_STATUS_FAILED, proposal.Status) + require.Contains(s.T(), proposal.FailedReason, tc.expectedError.Error()) if tc.expectedError != nil && err != nil { require.Error(s.T(), err) @@ -234,26 +272,30 @@ func (s *TestSuite) TestAddValidator_ExistingValidator_Jailed() { tt := []struct { name string valAddress string + valPubKey cryptotypes.PubKey expectedError error beforeRun func() afterRun func() }{ { - name: "add existing validator - status jailed", - valAddress: valAccAddr.String(), + name: "add existing validator - status jailed", + valAddress: valAccAddr.String(), + valPubKey: validator.ConsensusPubkey.GetCachedValue().(cryptotypes.PubKey), expectedError: poatypes.ErrAddressHasBondedTokens, beforeRun: func() { // Force jail validator valSet := s.Network().GetValidatorSet() - // Exclude validator at index 1 from validator set - require.Equal(s.T(), sdktypes.ValAddress(valSet.Validators[valIndex].Address).String(), valAddr.String()) - vf := make([]cmtproto.BlockIDFlag, len(valSet.Validators)) - for i := range valSet.Validators { - vf[i] = cmtproto.BlockIDFlagCommit - } - vf[valIndex] = cmtproto.BlockIDFlagAbsent - require.NoError(s.T(), s.Network().NextNBlocksWithValidatorFlags(slashingtypes.DefaultSignedBlocksWindow+10, vf)) + require.NoError( + s.T(), + s.Network().NextNBlocksWithValidatorFlags( + slashingtypes.DefaultSignedBlocksWindow, + utils.NewValidatorFlags( + len(valSet.Validators), + utils.NewValidatorFlagOverride(valIndex, cmtproto.BlockIDFlagAbsent), + ), + ), + ) resVal, err := s.Network().GetStakingClient().Validator( s.Network().GetContext(), @@ -288,23 +330,29 @@ func (s *TestSuite) TestAddValidator_ExistingValidator_Jailed() { }, } + //nolint:dupl for _, tc := range tt { s.Run(tc.name, func() { if tc.beforeRun != nil { tc.beforeRun() } - err := s.Network().PoaKeeper().ExecuteAddValidator( - s.Network().GetContext(), - &poatypes.MsgAddValidator{ - ValidatorAddress: valAccAddr.String(), - Authority: authtypes.NewModuleAddress(govtypes.ModuleName).String(), - Pubkey: validator.ConsensusPubkey, - Description: stakingtypes.Description{ - Moniker: "test", - }, + authority := sdktypes.AccAddress(address.Module("gov")) + msg, err := poatypes.NewMsgAddValidator( + authority.String(), + tc.valAddress, + tc.valPubKey, + stakingtypes.Description{ + Moniker: "test", }, ) + require.NoError(s.T(), err) + + proposal, err := utils.SubmitAndAwaitProposalResolution(s.factory, s.Network(), s.keyring.GetKeys(), "test", msg) + require.NoError(s.T(), err) + + require.Equal(s.T(), govv1.ProposalStatus_PROPOSAL_STATUS_FAILED, proposal.Status) + require.Contains(s.T(), proposal.FailedReason, tc.expectedError.Error()) if tc.expectedError != nil && err != nil { require.Error(s.T(), err) @@ -322,22 +370,33 @@ func (s *TestSuite) TestAddValidator_ExistingValidator_Jailed() { func (s *TestSuite) TestAddValidator_ExistingValidator_Tombstoned() { valIndex := 1 - validator := s.Network().GetValidators()[valIndex] + + // CometBFT validators + valSet := s.Network().GetValidatorSet() + cmtValAddr := sdktypes.AccAddress(valSet.Validators[valIndex].Address.Bytes()) + cmtValConsAddr := sdktypes.ConsAddress(valSet.Validators[valIndex].Address.Bytes()) + + // Cosmos validators + validators := s.Network().GetValidators() + require.NotZero(s.T(), len(validators)) + + validator := validators[valIndex] valAddr, err := sdktypes.ValAddressFromBech32(validator.OperatorAddress) require.NoError(s.T(), err) valAccAddr := sdktypes.AccAddress(valAddr) - valConsAddr := sdktypes.ConsAddress(valAddr) tt := []struct { name string valAddress string + valPubKey cryptotypes.PubKey expectedError error beforeRun func() afterRun func() }{ { - name: "add existing validator - status tombstoned", - valAddress: valAccAddr.String(), + name: "add existing validator - status tombstoned", + valAddress: valAccAddr.String(), + valPubKey: validator.ConsensusPubkey.GetCachedValue().(cryptotypes.PubKey), expectedError: poatypes.ErrAddressHasBondedTokens, beforeRun: func() { // Force validator to be tombstoned @@ -346,7 +405,7 @@ func (s *TestSuite) TestAddValidator_ExistingValidator_Tombstoned() { { Type: abcitypes.MisbehaviorType_DUPLICATE_VOTE, Validator: abcitypes.Validator{ - Address: valAddr.Bytes(), + Address: cmtValAddr.Bytes(), }, Height: s.Network().GetContext().BlockHeight(), TotalVotingPower: s.Network().GetValidatorSet().TotalVotingPower(), @@ -371,7 +430,7 @@ func (s *TestSuite) TestAddValidator_ExistingValidator_Tombstoned() { info, err := s.Network().GetSlashingClient().SigningInfo( s.Network().GetContext(), &slashingtypes.QuerySigningInfoRequest{ - ConsAddress: valConsAddr.String(), + ConsAddress: cmtValConsAddr.String(), }, ) require.NoError(s.T(), err) @@ -398,23 +457,29 @@ func (s *TestSuite) TestAddValidator_ExistingValidator_Tombstoned() { }, } + //nolint:dupl for _, tc := range tt { s.Run(tc.name, func() { if tc.beforeRun != nil { tc.beforeRun() } - err := s.Network().PoaKeeper().ExecuteAddValidator( - s.Network().GetContext(), - &poatypes.MsgAddValidator{ - ValidatorAddress: valAccAddr.String(), - Authority: authtypes.NewModuleAddress(govtypes.ModuleName).String(), - Pubkey: validator.ConsensusPubkey, - Description: stakingtypes.Description{ - Moniker: "test", - }, + authority := sdktypes.AccAddress(address.Module("gov")) + msg, err := poatypes.NewMsgAddValidator( + authority.String(), + tc.valAddress, + tc.valPubKey, + stakingtypes.Description{ + Moniker: "test", }, ) + require.NoError(s.T(), err) + + proposal, err := utils.SubmitAndAwaitProposalResolution(s.factory, s.Network(), s.keyring.GetKeys(), "test", msg) + require.NoError(s.T(), err) + + require.Equal(s.T(), govv1.ProposalStatus_PROPOSAL_STATUS_FAILED, proposal.Status) + require.Contains(s.T(), proposal.FailedReason, tc.expectedError.Error()) if tc.expectedError != nil && err != nil { require.Error(s.T(), err) @@ -490,7 +555,7 @@ func (s *TestSuite) TestRemoveValidator_UnexistingValidator() { proposal, err := utils.SubmitAndAwaitProposalResolution(s.factory, s.Network(), s.keyring.GetKeys(), "test", msg) require.NoError(s.T(), err) - require.Equal(s.T(), govtypes.ProposalStatus_PROPOSAL_STATUS_FAILED, proposal.Status) + require.Equal(s.T(), govv1.ProposalStatus_PROPOSAL_STATUS_FAILED, proposal.Status) require.Equal(s.T(), tc.expectedError.Error(), proposal.FailedReason) if tc.expectedError != nil && err != nil { @@ -604,7 +669,7 @@ func (s *TestSuite) TestRemoveValidator_ExistingValidator_StatusBonded() { proposal, err := utils.SubmitAndAwaitProposalResolution(s.factory, s.Network(), s.keyring.GetKeys(), "test", msg) require.NoError(s.T(), err) - require.Equal(s.T(), govtypes.ProposalStatus_PROPOSAL_STATUS_PASSED, proposal.Status) + require.Equal(s.T(), govv1.ProposalStatus_PROPOSAL_STATUS_PASSED, proposal.Status) if tc.expectedError != nil && err != nil { require.Error(s.T(), err) @@ -651,7 +716,7 @@ func (s *TestSuite) TestRemoveValidator_ExistingValidator_Jailed() { slashingtypes.DefaultSignedBlocksWindow, utils.NewValidatorFlags( len(valSet.Validators), - utils.NewValidatorFlagOverride(1, cmtproto.BlockIDFlagAbsent), + utils.NewValidatorFlagOverride(valIndex, cmtproto.BlockIDFlagAbsent), ), ), ) @@ -718,7 +783,7 @@ func (s *TestSuite) TestRemoveValidator_ExistingValidator_Jailed() { proposal, err := utils.SubmitAndAwaitProposalResolution(s.factory, s.Network(), s.keyring.GetKeys(), "test", msg) require.NoError(s.T(), err) - require.Equal(s.T(), govtypes.ProposalStatus_PROPOSAL_STATUS_PASSED, proposal.Status) + require.Equal(s.T(), govv1.ProposalStatus_PROPOSAL_STATUS_PASSED, proposal.Status) if tc.expectedError != nil && err != nil { require.Error(s.T(), err) @@ -862,7 +927,7 @@ func (s *TestSuite) TestRemoveValidator_ExistingValidator_Tombstoned() { proposal, err := utils.SubmitAndAwaitProposalResolution(s.factory, s.Network(), s.keyring.GetKeys(), "test", msg) require.NoError(s.T(), err) - require.Equal(s.T(), govtypes.ProposalStatus_PROPOSAL_STATUS_PASSED, proposal.Status) + require.Equal(s.T(), govv1.ProposalStatus_PROPOSAL_STATUS_PASSED, proposal.Status) if tc.expectedError != nil && err != nil { require.Error(s.T(), err) From ec86cc02707f11798145b8f4705d6cd4976711d4 Mon Sep 17 00:00:00 2001 From: GuillemGarciaDev Date: Tue, 14 Jan 2025 16:39:35 +0100 Subject: [PATCH 41/60] chore: add `coverage-poa` makefile rule --- .gitignore | 1 + Makefile | 10 ++++++++++ 2 files changed, 11 insertions(+) diff --git a/.gitignore b/.gitignore index f137666..cd05c1e 100644 --- a/.gitignore +++ b/.gitignore @@ -8,5 +8,6 @@ release/ .exrpd/ +*.out bin/ \ No newline at end of file diff --git a/Makefile b/Makefile index 47e47a8..cbc23a7 100644 --- a/Makefile +++ b/Makefile @@ -148,6 +148,16 @@ test-sim-full-app-fast: @cd ${CURDIR}/app && go test -mod=readonly -run TestFullAppSimulation \ -Enabled=true -NumBlocks=100 -BlockSize=200 -Commit=true -Period=5 -Params=${CURDIR}/tests/sim/params.json -v -timeout 24h +############################################################################### +### Coverage ### +############################################################################### + +coverage-poa: + @echo "--> Running POA coverage" + @go test $(EXCLUDED_POA_PACKAGES) -coverprofile=coverage_poa.out > /dev/null + @go tool cover -func=coverage_poa.out + + ############################################################################### ### Protobuf ### ############################################################################### From f7235a48207eb4f82ae7d75a7dc51728d0b81448 Mon Sep 17 00:00:00 2001 From: GuillemGarciaDev Date: Wed, 15 Jan 2025 09:39:06 +0100 Subject: [PATCH 42/60] feat(poa): unit tests --- Makefile | 4 + x/poa/ante/poa_test.go | 52 ++++++++--- x/poa/keeper/hooks_test.go | 153 +++++++++++++++++++++++++++++--- x/poa/keeper/invariants_test.go | 78 ++++++++-------- 4 files changed, 227 insertions(+), 60 deletions(-) diff --git a/Makefile b/Makefile index cbc23a7..cd6b9c7 100644 --- a/Makefile +++ b/Makefile @@ -157,6 +157,10 @@ coverage-poa: @go test $(EXCLUDED_POA_PACKAGES) -coverprofile=coverage_poa.out > /dev/null @go tool cover -func=coverage_poa.out +coverage-integration: + @echo "--> Running integration coverage" + @go test ./tests/integration -mod=readonly -coverprofile=coverage_integration.out > /dev/null + @go tool cover -func=coverage_integration.out ############################################################################### ### Protobuf ### diff --git a/x/poa/ante/poa_test.go b/x/poa/ante/poa_test.go index 7b6cfde..1574f93 100644 --- a/x/poa/ante/poa_test.go +++ b/x/poa/ante/poa_test.go @@ -1,6 +1,7 @@ package ante import ( + "errors" "testing" "time" @@ -9,6 +10,7 @@ import ( tmproto "github.com/cometbft/cometbft/proto/tendermint/types" sdktestutil "github.com/cosmos/cosmos-sdk/testutil" sdk "github.com/cosmos/cosmos-sdk/types" + stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" "github.com/golang/mock/gomock" "github.com/stretchr/testify/require" "github.com/xrplevm/node/v5/x/poa/testutil" @@ -16,7 +18,7 @@ import ( ) func setupPoaDecorator(t *testing.T) ( - *PoaDecorator, + PoaDecorator, sdk.Context, ) { key := storetypes.NewKVStoreKey(types.StoreKey) @@ -24,20 +26,48 @@ func setupPoaDecorator(t *testing.T) ( testCtx := sdktestutil.DefaultContextWithDB(t, key, tsKey) ctx := testCtx.Ctx.WithBlockHeader(tmproto.Header{Time: time.Now()}) - return &PoaDecorator{}, ctx + return NewPoaDecorator(), ctx } func TestPoaDecorator_AnteHandle(t *testing.T) { - pd, ctx := setupPoaDecorator(t) + tt := []struct { + name string + msgs []sdk.Msg + expectedError error + }{ + { + name: "should return error - tx not allowed", + msgs: []sdk.Msg{ + &stakingtypes.MsgUndelegate{}, + &stakingtypes.MsgBeginRedelegate{}, + }, + expectedError: errors.New("tx type not allowed"), + }, + { + name: "should not return error", + msgs: []sdk.Msg{ + &stakingtypes.MsgDelegate{}, + }, + }, + } - ctrl := gomock.NewController(t) - txMock := testutil.NewMockTx(ctrl) - txMock.EXPECT().GetMsgs().Return([]sdk.Msg{}).AnyTimes() + for _, tc := range tt { + pd, ctx := setupPoaDecorator(t) - mockNext := func(ctx sdk.Context, _ sdk.Tx, _ bool) (sdk.Context, error) { - return ctx, nil - } + ctrl := gomock.NewController(t) + txMock := testutil.NewMockTx(ctrl) + txMock.EXPECT().GetMsgs().Return(tc.msgs).AnyTimes() - _, err := pd.AnteHandle(ctx, txMock, false, mockNext) - require.NoError(t, err) + mockNext := func(ctx sdk.Context, _ sdk.Tx, _ bool) (sdk.Context, error) { + return ctx, nil + } + + _, err := pd.AnteHandle(ctx, txMock, false, mockNext) + if tc.expectedError != nil { + require.Error(t, err) + require.Equal(t, tc.expectedError, err) + } else { + require.NoError(t, err) + } + } } diff --git a/x/poa/keeper/hooks_test.go b/x/poa/keeper/hooks_test.go index 5aeaa25..a515957 100644 --- a/x/poa/keeper/hooks_test.go +++ b/x/poa/keeper/hooks_test.go @@ -10,7 +10,7 @@ import ( "github.com/xrplevm/node/v5/x/poa/testutil" ) -func TestPoA_Hooks(t *testing.T) { +func TestHooks_BeforeDelegationCreated(t *testing.T) { keeper, ctx := setupPoaKeeper( t, func(_ sdk.Context, _ *testutil.MockStakingKeeper) {}, @@ -20,26 +20,155 @@ func TestPoA_Hooks(t *testing.T) { hooks := keeper.Hooks() - err := hooks.BeforeDelegationCreated(ctx, sdk.AccAddress("ethm1a0pd5cyew47pvgf7rd7axxy3humv9ev0nnkprp"), sdk.ValAddress("ethm1a0pd5cyew47pvgf7rd7axxy3humv9ev0nnkprp")) + // Test delegating to self (should succeed) + addr := sdk.AccAddress("ethm1a0pd5cyew47pvgf7rd7axxy3humv9ev0nnkprp") + valAddr := sdk.ValAddress(addr) + err := hooks.BeforeDelegationCreated(ctx, addr, valAddr) require.NoError(t, err) - err = hooks.AfterValidatorBonded(ctx, sdk.ConsAddress(""), sdk.ValAddress("")) + + // Test delegating to other (should fail) + otherValAddr := sdk.ValAddress("ethm1other47pvgf7rd7axxy3humv9ev0nnkprp") + err = hooks.BeforeDelegationCreated(ctx, addr, otherValAddr) + require.Error(t, err) + require.Contains(t, err.Error(), "delegation to other accounts is not allowed") +} + +func TestHooks_AfterValidatorBonded(t *testing.T) { + keeper, ctx := setupPoaKeeper( + t, + func(_ sdk.Context, _ *testutil.MockStakingKeeper) {}, + func(_ sdk.Context, _ *testutil.MockBankKeeper) {}, + func(_ sdk.Context, _ *testutil.MockSlashingKeeper) {}, + ) + + hooks := keeper.Hooks() + + err := hooks.AfterValidatorBonded(ctx, sdk.ConsAddress("test"), sdk.ValAddress("test")) require.NoError(t, err) - err = hooks.AfterValidatorRemoved(ctx, sdk.ConsAddress(""), sdk.ValAddress("")) +} + +func TestHooks_AfterValidatorRemoved(t *testing.T) { + keeper, ctx := setupPoaKeeper( + t, + func(_ sdk.Context, _ *testutil.MockStakingKeeper) {}, + func(_ sdk.Context, _ *testutil.MockBankKeeper) {}, + func(_ sdk.Context, _ *testutil.MockSlashingKeeper) {}, + ) + + hooks := keeper.Hooks() + + err := hooks.AfterValidatorRemoved(ctx, sdk.ConsAddress("test"), sdk.ValAddress("test")) require.NoError(t, err) - err = hooks.AfterValidatorCreated(ctx, sdk.ValAddress("")) +} + +func TestHooks_AfterValidatorCreated(t *testing.T) { + keeper, ctx := setupPoaKeeper( + t, + func(_ sdk.Context, _ *testutil.MockStakingKeeper) {}, + func(_ sdk.Context, _ *testutil.MockBankKeeper) {}, + func(_ sdk.Context, _ *testutil.MockSlashingKeeper) {}, + ) + + hooks := keeper.Hooks() + + err := hooks.AfterValidatorCreated(ctx, sdk.ValAddress("test")) require.NoError(t, err) - err = hooks.AfterValidatorBeginUnbonding(ctx, sdk.ConsAddress(""), sdk.ValAddress("")) +} + +func TestHooks_AfterValidatorBeginUnbonding(t *testing.T) { + keeper, ctx := setupPoaKeeper( + t, + func(_ sdk.Context, _ *testutil.MockStakingKeeper) {}, + func(_ sdk.Context, _ *testutil.MockBankKeeper) {}, + func(_ sdk.Context, _ *testutil.MockSlashingKeeper) {}, + ) + + hooks := keeper.Hooks() + + err := hooks.AfterValidatorBeginUnbonding(ctx, sdk.ConsAddress("test"), sdk.ValAddress("test")) require.NoError(t, err) - err = hooks.BeforeValidatorModified(ctx, sdk.ValAddress("")) +} + +func TestHooks_BeforeValidatorModified(t *testing.T) { + keeper, ctx := setupPoaKeeper( + t, + func(_ sdk.Context, _ *testutil.MockStakingKeeper) {}, + func(_ sdk.Context, _ *testutil.MockBankKeeper) {}, + func(_ sdk.Context, _ *testutil.MockSlashingKeeper) {}, + ) + + hooks := keeper.Hooks() + + err := hooks.BeforeValidatorModified(ctx, sdk.ValAddress("test")) require.NoError(t, err) - err = hooks.BeforeDelegationSharesModified(ctx, sdk.AccAddress(""), sdk.ValAddress("")) +} + +func TestHooks_BeforeDelegationSharesModified(t *testing.T) { + keeper, ctx := setupPoaKeeper( + t, + func(_ sdk.Context, _ *testutil.MockStakingKeeper) {}, + func(_ sdk.Context, _ *testutil.MockBankKeeper) {}, + func(_ sdk.Context, _ *testutil.MockSlashingKeeper) {}, + ) + + hooks := keeper.Hooks() + + err := hooks.BeforeDelegationSharesModified(ctx, sdk.AccAddress("test"), sdk.ValAddress("test")) require.NoError(t, err) - err = hooks.BeforeDelegationRemoved(ctx, sdk.AccAddress(""), sdk.ValAddress("")) +} + +func TestHooks_BeforeDelegationRemoved(t *testing.T) { + keeper, ctx := setupPoaKeeper( + t, + func(_ sdk.Context, _ *testutil.MockStakingKeeper) {}, + func(_ sdk.Context, _ *testutil.MockBankKeeper) {}, + func(_ sdk.Context, _ *testutil.MockSlashingKeeper) {}, + ) + + hooks := keeper.Hooks() + + err := hooks.BeforeDelegationRemoved(ctx, sdk.AccAddress("test"), sdk.ValAddress("test")) require.NoError(t, err) - err = hooks.AfterDelegationModified(ctx, sdk.AccAddress(""), sdk.ValAddress("")) +} + +func TestHooks_AfterDelegationModified(t *testing.T) { + keeper, ctx := setupPoaKeeper( + t, + func(_ sdk.Context, _ *testutil.MockStakingKeeper) {}, + func(_ sdk.Context, _ *testutil.MockBankKeeper) {}, + func(_ sdk.Context, _ *testutil.MockSlashingKeeper) {}, + ) + + hooks := keeper.Hooks() + + err := hooks.AfterDelegationModified(ctx, sdk.AccAddress("test"), sdk.ValAddress("test")) require.NoError(t, err) - err = hooks.AfterUnbondingInitiated(ctx, 0) +} + +func TestHooks_AfterUnbondingInitiated(t *testing.T) { + keeper, ctx := setupPoaKeeper( + t, + func(_ sdk.Context, _ *testutil.MockStakingKeeper) {}, + func(_ sdk.Context, _ *testutil.MockBankKeeper) {}, + func(_ sdk.Context, _ *testutil.MockSlashingKeeper) {}, + ) + + hooks := keeper.Hooks() + + err := hooks.AfterUnbondingInitiated(ctx, 123) require.NoError(t, err) - err = hooks.BeforeValidatorSlashed(ctx, sdk.ValAddress(""), math.LegacyNewDec(0)) +} + +func TestHooks_BeforeValidatorSlashed(t *testing.T) { + keeper, ctx := setupPoaKeeper( + t, + func(_ sdk.Context, _ *testutil.MockStakingKeeper) {}, + func(_ sdk.Context, _ *testutil.MockBankKeeper) {}, + func(_ sdk.Context, _ *testutil.MockSlashingKeeper) {}, + ) + + hooks := keeper.Hooks() + + err := hooks.BeforeValidatorSlashed(ctx, sdk.ValAddress("test"), math.LegacyNewDec(100)) require.NoError(t, err) } diff --git a/x/poa/keeper/invariants_test.go b/x/poa/keeper/invariants_test.go index 7f33524..6fe18f9 100644 --- a/x/poa/keeper/invariants_test.go +++ b/x/poa/keeper/invariants_test.go @@ -12,47 +12,51 @@ import ( ) func TestStakingPowerInvariant_Valid(t *testing.T) { - poaKeeper, ctx := setupPoaKeeper( - t, - func(ctx sdk.Context, stakingKeeper *testutil.MockStakingKeeper) { - stakingKeeper.EXPECT().GetAllValidators(ctx).Return([]stakingtypes.Validator{ - { - Tokens: sdk.DefaultPowerReduction, - }, - { - Tokens: math.ZeroInt(), - }, - }, nil) + tt := []struct { + name string + broken bool + validators func() ([]stakingtypes.Validator, error) + }{ + { + name: "should pass - all validators have the same staking power", + broken: false, + validators: func() ([]stakingtypes.Validator, error) { + return []stakingtypes.Validator{ + { + Tokens: sdk.DefaultPowerReduction, + }, + }, nil + }, }, - func(sdk.Context, *testutil.MockBankKeeper) {}, - func(sdk.Context, *testutil.MockSlashingKeeper) {}, - ) - - invariant := StakingPowerInvariant(*poaKeeper) - msg, broken := invariant(ctx) - require.False(t, broken, msg) -} + { + name: "should fail - one validator has excessive staking power", + broken: true, + validators: func() ([]stakingtypes.Validator, error) { + return []stakingtypes.Validator{ + { + Tokens: sdk.DefaultPowerReduction.Add(math.OneInt()), + }, + }, nil + }, + }, + } -func TestStakingPowerInvariant_Invalid(t *testing.T) { - poaKeeper, ctx := setupPoaKeeper( - t, - func(ctx sdk.Context, stakingKeeper *testutil.MockStakingKeeper) { - stakingKeeper.EXPECT().GetAllValidators(ctx).Return([]stakingtypes.Validator{ - { - Tokens: sdk.DefaultPowerReduction, + for _, tc := range tt { + t.Run(tc.name, func(t *testing.T) { + poaKeeper, ctx := setupPoaKeeper( + t, + func(ctx sdk.Context, stakingKeeper *testutil.MockStakingKeeper) { + stakingKeeper.EXPECT().GetAllValidators(ctx).Return(tc.validators()) }, - { - Tokens: sdk.DefaultPowerReduction.Add(math.OneInt()), - }, - }, nil) - }, - func(_ sdk.Context, _ *testutil.MockBankKeeper) {}, - func(_ sdk.Context, _ *testutil.MockSlashingKeeper) {}, - ) + func(sdk.Context, *testutil.MockBankKeeper) {}, + func(sdk.Context, *testutil.MockSlashingKeeper) {}, + ) - invariant := StakingPowerInvariant(*poaKeeper) - msg, broken := invariant(ctx) - require.True(t, broken, msg) + invariant := StakingPowerInvariant(*poaKeeper) + _, broken := invariant(ctx) + require.Equal(t, broken, tc.broken) + }) + } } func TestSelfDelegationInvariant_Valid(t *testing.T) { From 9a6b41863ffc0467a92e8c1aa4ab94c80e45eace Mon Sep 17 00:00:00 2001 From: GuillemGarciaDev Date: Wed, 15 Jan 2025 14:32:06 +0100 Subject: [PATCH 43/60] fix(x/poa): refactor `ExecuteAddValidator`and `ExecuteRemoveValidator` unit tests --- x/poa/keeper/keeper_test.go | 653 ++++++++++++++++++++++++++++++++++-- 1 file changed, 634 insertions(+), 19 deletions(-) diff --git a/x/poa/keeper/keeper_test.go b/x/poa/keeper/keeper_test.go index b517285..4489fd9 100644 --- a/x/poa/keeper/keeper_test.go +++ b/x/poa/keeper/keeper_test.go @@ -1,6 +1,7 @@ package keeper import ( + "errors" "testing" "cosmossdk.io/math" @@ -50,32 +51,646 @@ func poaKeeperTestSetup(t *testing.T) (*Keeper, sdk.Context) { } // Define here Keeper methods to be unit tested -func TestPoAKeeper_ExecuteAddValidator(t *testing.T) { - keeper, ctx := poaKeeperTestSetup(t) +func TestKeeper_ExecuteAddValidator(t *testing.T) { ctrl := gomock.NewController(t) pubKey := testutil.NewMockPubKey(ctrl) msgPubKey, _ := types1.NewAnyWithValue(pubKey) - msg := &types.MsgAddValidator{ - Authority: keeper.GetAuthority(), - ValidatorAddress: "ethm1a0pd5cyew47pvgf7rd7axxy3humv9ev0nnkprp", - Description: stakingtypes.Description{ - Moniker: "test", - Identity: "test", - Website: "test", - SecurityContact: "test", - Details: "test", - }, - Pubkey: msgPubKey, + tt := []struct{ + name string + validatorAddress string + pubKey *types1.Any + stakingMocks func(ctx sdk.Context, stakingKeeper *testutil.MockStakingKeeper) + bankMocks func(ctx sdk.Context, bankKeeper *testutil.MockBankKeeper) + slashingMocks func(ctx sdk.Context, slashingKeeper *testutil.MockSlashingKeeper) + expectedError error + }{ + { + name: "should fail - invalid validator address", + validatorAddress: "invalidnaddress", + expectedError: errors.New("decoding bech32 failed"), + stakingMocks: func(ctx sdk.Context, stakingKeeper *testutil.MockStakingKeeper) {}, + bankMocks: func(ctx sdk.Context, bankKeeper *testutil.MockBankKeeper) {}, + slashingMocks: func(ctx sdk.Context, slashingKeeper *testutil.MockSlashingKeeper) {}, + }, + { + name: "should fail - staking keeper returns error on GetParams", + validatorAddress: "ethm1a0pd5cyew47pvgf7rd7axxy3humv9ev0nnkprp", + expectedError: errors.New("staking params error"), + stakingMocks: func(ctx sdk.Context, stakingKeeper *testutil.MockStakingKeeper) { + stakingKeeper.EXPECT().GetParams(ctx).Return(stakingtypes.Params{}, errors.New("staking params error")) + }, + bankMocks: func(ctx sdk.Context, bankKeeper *testutil.MockBankKeeper) {}, + slashingMocks: func(ctx sdk.Context, slashingKeeper *testutil.MockSlashingKeeper) {}, + }, + { + name: "should fail - validator has bonded tokens", + validatorAddress: "ethm1a0pd5cyew47pvgf7rd7axxy3humv9ev0nnkprp", + expectedError: types.ErrAddressHasBankTokens, + stakingMocks: func(ctx sdk.Context, stakingKeeper *testutil.MockStakingKeeper) { + stakingKeeper.EXPECT().GetParams(ctx).Return(stakingtypes.Params{ + BondDenom: "BND", + }, nil) + }, + bankMocks: func(ctx sdk.Context, bankKeeper *testutil.MockBankKeeper) { + bankKeeper.EXPECT().GetBalance(ctx, gomock.Any(), gomock.Any()).Return(sdk.Coin{ + Denom: "BND", + Amount: sdk.DefaultPowerReduction, + }) + }, + slashingMocks: func(ctx sdk.Context, slashingKeeper *testutil.MockSlashingKeeper) {}, + }, + { + name: "should fail - staking keeper returns error on GetValidator", + validatorAddress: "ethm1a0pd5cyew47pvgf7rd7axxy3humv9ev0nnkprp", + expectedError: errors.New("staking validator error"), + stakingMocks: func(ctx sdk.Context, stakingKeeper *testutil.MockStakingKeeper) { + stakingKeeper.EXPECT().GetParams(ctx).Return(stakingtypes.Params{ + BondDenom: "BND", + }, nil) + stakingKeeper.EXPECT().GetValidator(ctx, gomock.Any()).Return(stakingtypes.Validator{}, errors.New("staking validator error")) + }, + bankMocks: func(ctx sdk.Context, bankKeeper *testutil.MockBankKeeper) { + bankKeeper.EXPECT().GetBalance(ctx, gomock.Any(), gomock.Any()).Return(sdk.Coin{ + Denom: "BND", + Amount: math.NewInt(0), + }) + }, + slashingMocks: func(ctx sdk.Context, slashingKeeper *testutil.MockSlashingKeeper) {}, + }, + { + name: "should fail - staking keeper returns validator with tokens", + validatorAddress: "ethm1a0pd5cyew47pvgf7rd7axxy3humv9ev0nnkprp", + expectedError: types.ErrAddressHasBondedTokens, + stakingMocks: func(ctx sdk.Context, stakingKeeper *testutil.MockStakingKeeper) { + stakingKeeper.EXPECT().GetParams(ctx).Return(stakingtypes.Params{ + BondDenom: "BND", + }, nil) + stakingKeeper.EXPECT().GetValidator(ctx, gomock.Any()).Return(stakingtypes.Validator{Tokens: sdk.DefaultPowerReduction}, nil) + }, + bankMocks: func(ctx sdk.Context, bankKeeper *testutil.MockBankKeeper) { + bankKeeper.EXPECT().GetBalance(ctx, gomock.Any(), gomock.Any()).Return(sdk.Coin{ + Denom: "BND", + Amount: math.NewInt(0), + }) + }, + slashingMocks: func(ctx sdk.Context, slashingKeeper *testutil.MockSlashingKeeper) {}, + }, + { + name: "should fail - staking keeper returns error on GetAllDelegatorDelegations", + validatorAddress: "ethm1a0pd5cyew47pvgf7rd7axxy3humv9ev0nnkprp", + expectedError: errors.New("staking delegations error"), + stakingMocks: func(ctx sdk.Context, stakingKeeper *testutil.MockStakingKeeper) { + stakingKeeper.EXPECT().GetParams(ctx).Return(stakingtypes.Params{ + BondDenom: "BND", + }, nil) + stakingKeeper.EXPECT().GetValidator(ctx, gomock.Any()).Return(stakingtypes.Validator{Tokens: math.NewInt(0)}, nil) + stakingKeeper.EXPECT().GetAllDelegatorDelegations(ctx, gomock.Any()).Return([]stakingtypes.Delegation{}, errors.New("staking delegations error")) + }, + bankMocks: func(ctx sdk.Context, bankKeeper *testutil.MockBankKeeper) { + bankKeeper.EXPECT().GetBalance(ctx, gomock.Any(), gomock.Any()).Return(sdk.Coin{ + Denom: "BND", + Amount: math.NewInt(0), + }) + }, + slashingMocks: func(ctx sdk.Context, slashingKeeper *testutil.MockSlashingKeeper) {}, + }, + { + name: "should fail - delegations are greater than 0 with invalid delegation validator address", + validatorAddress: "ethm1a0pd5cyew47pvgf7rd7axxy3humv9ev0nnkprp", + expectedError: errors.New("decoding bech32 failed"), + stakingMocks: func(ctx sdk.Context, stakingKeeper *testutil.MockStakingKeeper) { + stakingKeeper.EXPECT().GetParams(ctx).Return(stakingtypes.Params{ + BondDenom: "BND", + }, nil) + stakingKeeper.EXPECT().GetValidator(ctx, gomock.Any()).Return(stakingtypes.Validator{Tokens: math.NewInt(0)}, nil) + stakingKeeper.EXPECT().GetAllDelegatorDelegations(ctx, gomock.Any()).Return([]stakingtypes.Delegation{ + { + ValidatorAddress: "invalidvalidatoraddress", + Shares: sdk.DefaultPowerReduction.ToLegacyDec(), + }, + }, nil) + }, + bankMocks: func(ctx sdk.Context, bankKeeper *testutil.MockBankKeeper) { + bankKeeper.EXPECT().GetBalance(ctx, gomock.Any(), gomock.Any()).Return(sdk.Coin{ + Denom: "BND", + Amount: math.NewInt(0), + }) + }, + slashingMocks: func(ctx sdk.Context, slashingKeeper *testutil.MockSlashingKeeper) {}, + }, + // { + // name: "should fail - delegations are greater than 0 with error on GetValidator call", + // validatorAddress: "ethm1a0pd5cyew47pvgf7rd7axxy3humv9ev0nnkprp", + // expectedError: errors.New("staking validator error"), + // stakingMocks: func(ctx sdk.Context, stakingKeeper *testutil.MockStakingKeeper) { + // valAddr, _ := sdk.ValAddressFromBech32("ethm1a0pd5cyew47pvgf7rd7axxy3humv9ev0nnkprp") + // delAddr, _ := sdk.ValAddressFromBech32("ethm1dakgyqjulg29m5fmv992g2y66m9g2mjn6hahwg") + + // stakingKeeper.EXPECT().GetParams(ctx).Return(stakingtypes.Params{ + // BondDenom: "BND", + // }, nil) + // stakingKeeper.EXPECT().GetValidator(ctx, valAddr).Return(stakingtypes.Validator{Tokens: math.NewInt(0)}, nil) + // stakingKeeper.EXPECT().GetValidator(ctx, delAddr).Return(stakingtypes.Validator{}, errors.New("staking validator error")) + // stakingKeeper.EXPECT().GetAllDelegatorDelegations(ctx, gomock.Any()).Return([]stakingtypes.Delegation{ + // { + // ValidatorAddress: "ethm1a0pd5cyew47pvgf7rd7axxy3humv9ev0nnkprp", + // Shares: sdk.DefaultPowerReduction.ToLegacyDec(), + // }, + // }, nil) + // }, + // bankMocks: func(ctx sdk.Context, bankKeeper *testutil.MockBankKeeper) { + // bankKeeper.EXPECT().GetBalance(ctx, gomock.Any(), gomock.Any()).Return(sdk.Coin{ + // Denom: "BND", + // Amount: math.NewInt(0), + // }) + // }, + // slashingMocks: func(ctx sdk.Context, slashingKeeper *testutil.MockSlashingKeeper) {}, + // }, + { + name: "should fail - GetUnbondingDelegationsFromValidator returns error", + validatorAddress: "ethm1a0pd5cyew47pvgf7rd7axxy3humv9ev0nnkprp", + expectedError: errors.New("staking unbonding delegations error"), + stakingMocks: func(ctx sdk.Context, stakingKeeper *testutil.MockStakingKeeper) { + stakingKeeper.EXPECT().GetParams(ctx).Return(stakingtypes.Params{ + BondDenom: "BND", + }, nil) + stakingKeeper.EXPECT().GetValidator(ctx, gomock.Any()).Return(stakingtypes.Validator{Tokens: math.NewInt(0)}, nil) + stakingKeeper.EXPECT().GetAllDelegatorDelegations(ctx, gomock.Any()).Return([]stakingtypes.Delegation{}, nil) + stakingKeeper.EXPECT().GetUnbondingDelegationsFromValidator(ctx, gomock.Any()).Return([]stakingtypes.UnbondingDelegation{}, errors.New("staking unbonding delegations error")) + }, + bankMocks: func(ctx sdk.Context, bankKeeper *testutil.MockBankKeeper) { + bankKeeper.EXPECT().GetBalance(ctx, gomock.Any(), gomock.Any()).Return(sdk.Coin{ + Denom: "BND", + Amount: math.NewInt(0), + }) + }, + slashingMocks: func(ctx sdk.Context, slashingKeeper *testutil.MockSlashingKeeper) {}, + }, + { + name: "should fail - unbonding delegations balances are greater than 0", + validatorAddress: "ethm1a0pd5cyew47pvgf7rd7axxy3humv9ev0nnkprp", + expectedError: types.ErrAddressHasUnbondingTokens, + stakingMocks: func(ctx sdk.Context, stakingKeeper *testutil.MockStakingKeeper) { + stakingKeeper.EXPECT().GetParams(ctx).Return(stakingtypes.Params{ + BondDenom: "BND", + }, nil) + stakingKeeper.EXPECT().GetValidator(ctx, gomock.Any()).Return(stakingtypes.Validator{Tokens: math.NewInt(0)}, nil) + stakingKeeper.EXPECT().GetAllDelegatorDelegations(ctx, gomock.Any()).Return([]stakingtypes.Delegation{}, nil) + stakingKeeper.EXPECT().GetUnbondingDelegationsFromValidator(ctx, gomock.Any()).Return([]stakingtypes.UnbondingDelegation{ + { + Entries: []stakingtypes.UnbondingDelegationEntry{ + { + Balance: sdk.DefaultPowerReduction, + }, + }, + }, + }, nil) + }, + bankMocks: func(ctx sdk.Context, bankKeeper *testutil.MockBankKeeper) { + bankKeeper.EXPECT().GetBalance(ctx, gomock.Any(), gomock.Any()).Return(sdk.Coin{ + Denom: "BND", + Amount: math.NewInt(0), + }) + }, + slashingMocks: func(ctx sdk.Context, slashingKeeper *testutil.MockSlashingKeeper) {}, + }, + { + name: "should fail - bank keeper MintCoins returns error", + validatorAddress: "ethm1a0pd5cyew47pvgf7rd7axxy3humv9ev0nnkprp", + expectedError: errors.New("bank mint coins error"), + stakingMocks: func(ctx sdk.Context, stakingKeeper *testutil.MockStakingKeeper) { + stakingKeeper.EXPECT().GetParams(ctx).Return(stakingtypes.Params{ + BondDenom: "BND", + }, nil) + stakingKeeper.EXPECT().GetValidator(ctx, gomock.Any()).Return(stakingtypes.Validator{Tokens: math.NewInt(0)}, nil) + stakingKeeper.EXPECT().GetAllDelegatorDelegations(ctx, gomock.Any()).Return([]stakingtypes.Delegation{}, nil) + stakingKeeper.EXPECT().GetUnbondingDelegationsFromValidator(ctx, gomock.Any()).Return([]stakingtypes.UnbondingDelegation{}, nil) + }, + bankMocks: func(ctx sdk.Context, bankKeeper *testutil.MockBankKeeper) { + bankKeeper.EXPECT().GetBalance(ctx, gomock.Any(), gomock.Any()).Return(sdk.Coin{ + Denom: "BND", + Amount: math.NewInt(0), + }) + bankKeeper.EXPECT().MintCoins(ctx, gomock.Any(), gomock.Any()).Return(errors.New("bank mint coins error")) + }, + slashingMocks: func(ctx sdk.Context, slashingKeeper *testutil.MockSlashingKeeper) {}, + }, + { + name: "should fail - bank keeper SendCoinsFromModuleToAccount returns error", + validatorAddress: "ethm1a0pd5cyew47pvgf7rd7axxy3humv9ev0nnkprp", + expectedError: errors.New("bank send coins from module to account error"), + stakingMocks: func(ctx sdk.Context, stakingKeeper *testutil.MockStakingKeeper) { + stakingKeeper.EXPECT().GetParams(ctx).Return(stakingtypes.Params{ + BondDenom: "BND", + }, nil) + stakingKeeper.EXPECT().GetValidator(ctx, gomock.Any()).Return(stakingtypes.Validator{Tokens: math.NewInt(0)}, nil) + stakingKeeper.EXPECT().GetAllDelegatorDelegations(ctx, gomock.Any()).Return([]stakingtypes.Delegation{}, nil) + stakingKeeper.EXPECT().GetUnbondingDelegationsFromValidator(ctx, gomock.Any()).Return([]stakingtypes.UnbondingDelegation{}, nil) + }, + bankMocks: func(ctx sdk.Context, bankKeeper *testutil.MockBankKeeper) { + bankKeeper.EXPECT().GetBalance(ctx, gomock.Any(), gomock.Any()).Return(sdk.Coin{ + Denom: "BND", + Amount: math.NewInt(0), + }) + bankKeeper.EXPECT().MintCoins(ctx, gomock.Any(), gomock.Any()).Return(nil) + bankKeeper.EXPECT().SendCoinsFromModuleToAccount(ctx, gomock.Any(), gomock.Any(), gomock.Any()).Return(errors.New("bank send coins from module to account error")) + }, + slashingMocks: func(ctx sdk.Context, slashingKeeper *testutil.MockSlashingKeeper) {}, + }, + { + name: "should pass - MsgAddValidator", + validatorAddress: "ethm1a0pd5cyew47pvgf7rd7axxy3humv9ev0nnkprp", + pubKey: msgPubKey, + stakingMocks: func(ctx sdk.Context, stakingKeeper *testutil.MockStakingKeeper) { + stakingKeeper.EXPECT().GetParams(ctx).Return(stakingtypes.Params{ + BondDenom: "BND", + }, nil) + stakingKeeper.EXPECT().GetValidator(ctx, gomock.Any()).Return(stakingtypes.Validator{Tokens: math.NewInt(0)}, nil) + stakingKeeper.EXPECT().GetAllDelegatorDelegations(ctx, gomock.Any()).Return([]stakingtypes.Delegation{}, nil) + stakingKeeper.EXPECT().GetUnbondingDelegationsFromValidator(ctx, gomock.Any()).Return([]stakingtypes.UnbondingDelegation{}, nil) + }, + bankMocks: func(ctx sdk.Context, bankKeeper *testutil.MockBankKeeper) { + bankKeeper.EXPECT().GetBalance(ctx, gomock.Any(), gomock.Any()).Return(sdk.Coin{ + Denom: "BND", + Amount: math.NewInt(0), + }) + bankKeeper.EXPECT().MintCoins(ctx, gomock.Any(), gomock.Any()).Return(nil) + bankKeeper.EXPECT().SendCoinsFromModuleToAccount(ctx, gomock.Any(), gomock.Any(), gomock.Any()).Return(nil) + }, + slashingMocks: func(ctx sdk.Context, slashingKeeper *testutil.MockSlashingKeeper) {}, + }, } - err := keeper.ExecuteAddValidator(ctx, msg) - require.NoError(t, err) + for _, tc := range tt { + t.Run(tc.name, func(t *testing.T) { + keeper, ctx := setupPoaKeeper(t, tc.stakingMocks, tc.bankMocks, tc.slashingMocks) + + msg := &types.MsgAddValidator{ + Authority: keeper.GetAuthority(), + ValidatorAddress: tc.validatorAddress, + Description: stakingtypes.Description{ + Moniker: "test", + Identity: "test", + Website: "test", + SecurityContact: "test", + Details: "test", + }, + Pubkey: tc.pubKey, + } + + err := keeper.ExecuteAddValidator(ctx, msg) + if tc.expectedError != nil { + require.Contains(t, err.Error(), tc.expectedError.Error()) + } else { + require.NoError(t, err) + } + }) + } } -func TestPoAKeeper_ExecuteRemoveValidator(t *testing.T) { - keeper, ctx := poaKeeperTestSetup(t) +func TestKeeper_ExecuteRemoveValidator(t *testing.T) { + ctrl := gomock.NewController(t) - err := keeper.ExecuteRemoveValidator(ctx, "ethm1a0pd5cyew47pvgf7rd7axxy3humv9ev0nnkprp") - require.NoError(t, err) + tt := []struct{ + name string + validatorAddress string + stakingMocks func(ctx sdk.Context, stakingKeeper *testutil.MockStakingKeeper) + bankMocks func(ctx sdk.Context, bankKeeper *testutil.MockBankKeeper) + slashingMocks func(ctx sdk.Context, slashingKeeper *testutil.MockSlashingKeeper) + expectedError error + }{ + { + name: "should fail - invalid validator address", + validatorAddress: "invalidnaddress", + expectedError: errors.New("decoding bech32 failed"), + stakingMocks: func(ctx sdk.Context, stakingKeeper *testutil.MockStakingKeeper) {}, + bankMocks: func(ctx sdk.Context, bankKeeper *testutil.MockBankKeeper) {}, + slashingMocks: func(ctx sdk.Context, slashingKeeper *testutil.MockSlashingKeeper) {}, + }, + { + name: "should fail - staking keeper returns error on GetParams", + validatorAddress: "ethm1a0pd5cyew47pvgf7rd7axxy3humv9ev0nnkprp", + expectedError: errors.New("staking params error"), + stakingMocks: func(ctx sdk.Context, stakingKeeper *testutil.MockStakingKeeper) { + stakingKeeper.EXPECT().GetParams(ctx).Return(stakingtypes.Params{}, errors.New("staking params error")) + }, + bankMocks: func(ctx sdk.Context, bankKeeper *testutil.MockBankKeeper) {}, + slashingMocks: func(ctx sdk.Context, slashingKeeper *testutil.MockSlashingKeeper) {}, + }, + { + name: "should fail - bank keeper returns error on SendCoinsFromAccountToModule when validator has balance", + validatorAddress: "ethm1a0pd5cyew47pvgf7rd7axxy3humv9ev0nnkprp", + expectedError: errors.New("bank send coins from account to module error"), + stakingMocks: func(ctx sdk.Context, stakingKeeper *testutil.MockStakingKeeper) { + stakingKeeper.EXPECT().GetParams(ctx).Return(stakingtypes.Params{ + BondDenom: "BND", + }, nil) + }, + bankMocks: func(ctx sdk.Context, bankKeeper *testutil.MockBankKeeper) { + bankKeeper.EXPECT().GetBalance(ctx, gomock.Any(), gomock.Any()).Return(sdk.Coin{ + Denom: "BND", + Amount: sdk.DefaultPowerReduction, + }) + bankKeeper.EXPECT().SendCoinsFromAccountToModule(ctx, gomock.Any(), gomock.Any(), gomock.Any()).Return(errors.New("bank send coins from account to module error")) + }, + slashingMocks: func(ctx sdk.Context, slashingKeeper *testutil.MockSlashingKeeper) {}, + }, + { + name: "should fail - bank keeper returns error on BurnCoins when validator has balance", + validatorAddress: "ethm1a0pd5cyew47pvgf7rd7axxy3humv9ev0nnkprp", + expectedError: errors.New("bank burn coins error"), + stakingMocks: func(ctx sdk.Context, stakingKeeper *testutil.MockStakingKeeper) { + stakingKeeper.EXPECT().GetParams(ctx).Return(stakingtypes.Params{ + BondDenom: "BND", + }, nil) + }, + bankMocks: func(ctx sdk.Context, bankKeeper *testutil.MockBankKeeper) { + bankKeeper.EXPECT().GetBalance(ctx, gomock.Any(), gomock.Any()).Return(sdk.Coin{ + Denom: "BND", + Amount: sdk.DefaultPowerReduction, + }) + bankKeeper.EXPECT().SendCoinsFromAccountToModule(ctx, gomock.Any(), gomock.Any(), gomock.Any()).Return(nil) + bankKeeper.EXPECT().BurnCoins(ctx, gomock.Any(), gomock.Any()).Return(errors.New("bank burn coins error")) + }, + slashingMocks: func(ctx sdk.Context, slashingKeeper *testutil.MockSlashingKeeper) {}, + }, + { + name: "should fail - staking keeper returns error on GetValidator with balance", + validatorAddress: "ethm1a0pd5cyew47pvgf7rd7axxy3humv9ev0nnkprp", + stakingMocks: func(ctx sdk.Context, stakingKeeper *testutil.MockStakingKeeper) { + stakingKeeper.EXPECT().GetParams(ctx).Return(stakingtypes.Params{ + BondDenom: "BND", + }, nil) + stakingKeeper.EXPECT().GetValidator(ctx, gomock.Any()).Return(stakingtypes.Validator{ + }, errors.New("staking keeper get validator error")) + }, + bankMocks: func(ctx sdk.Context, bankKeeper *testutil.MockBankKeeper) { + bankKeeper.EXPECT().GetBalance(ctx, gomock.Any(), gomock.Any()).Return(sdk.Coin{ + Denom: "BND", + Amount: sdk.DefaultPowerReduction, + }) + bankKeeper.EXPECT().SendCoinsFromAccountToModule(ctx, gomock.Any(), gomock.Any(), gomock.Any()).Return(nil) + bankKeeper.EXPECT().BurnCoins(ctx, gomock.Any(), gomock.Any()).Return(nil) + }, + slashingMocks: func(ctx sdk.Context, slashingKeeper *testutil.MockSlashingKeeper) {}, + }, + { + name: "should fail - staking keeper returns error on GetValidator with no balance", + validatorAddress: "ethm1a0pd5cyew47pvgf7rd7axxy3humv9ev0nnkprp", + expectedError: types.ErrAddressHasNoTokens, + stakingMocks: func(ctx sdk.Context, stakingKeeper *testutil.MockStakingKeeper) { + stakingKeeper.EXPECT().GetParams(ctx).Return(stakingtypes.Params{ + BondDenom: "BND", + }, nil) + stakingKeeper.EXPECT().GetValidator(ctx, gomock.Any()).Return(stakingtypes.Validator{ + }, errors.New("staking keeper get validator error")) + }, + bankMocks: func(ctx sdk.Context, bankKeeper *testutil.MockBankKeeper) { + bankKeeper.EXPECT().GetBalance(ctx, gomock.Any(), gomock.Any()).Return(sdk.Coin{ + Denom: "BND", + Amount: math.NewInt(0), + }) + }, + slashingMocks: func(ctx sdk.Context, slashingKeeper *testutil.MockSlashingKeeper) {}, + }, + { + name: "should fail - staking keeper returns error on call GetUnbondingDelegationsFromValidator", + validatorAddress: "ethm1a0pd5cyew47pvgf7rd7axxy3humv9ev0nnkprp", + expectedError: errors.New("staking keeper get unbonding delegations from validator error"), + stakingMocks: func(ctx sdk.Context, stakingKeeper *testutil.MockStakingKeeper) { + stakingKeeper.EXPECT().GetParams(ctx).Return(stakingtypes.Params{ + BondDenom: "BND", + }, nil) + stakingKeeper.EXPECT().GetValidator(ctx, gomock.Any()).Return(stakingtypes.Validator{},nil) + stakingKeeper.EXPECT().GetUnbondingDelegationsFromValidator(ctx, gomock.Any()).Return( + []stakingtypes.UnbondingDelegation{}, errors.New("staking keeper get unbonding delegations from validator error")) + hooks := testutil.NewMockStakingHooks(ctrl) + hooks.EXPECT().BeforeValidatorModified(ctx, gomock.Any()).Return(errors.New("staking keeper hooks error")) + stakingKeeper.EXPECT().Hooks().Return(hooks) + }, + bankMocks: func(ctx sdk.Context, bankKeeper *testutil.MockBankKeeper) { + bankKeeper.EXPECT().GetBalance(ctx, gomock.Any(), gomock.Any()).Return(sdk.Coin{ + Denom: "BND", + Amount: math.NewInt(0), + }) + }, + slashingMocks: func(ctx sdk.Context, slashingKeeper *testutil.MockSlashingKeeper) {}, + }, + { + name: "should fail - staking keeper returns error on call SlashUnbondingDelegation", + validatorAddress: "ethm1a0pd5cyew47pvgf7rd7axxy3humv9ev0nnkprp", + expectedError: errors.New("staking keeper slash unbonding delegation error"), + stakingMocks: func(ctx sdk.Context, stakingKeeper *testutil.MockStakingKeeper) { + stakingKeeper.EXPECT().GetParams(ctx).Return(stakingtypes.Params{ + BondDenom: "BND", + }, nil) + stakingKeeper.EXPECT().GetValidator(ctx, gomock.Any()).Return(stakingtypes.Validator{},nil) + stakingKeeper.EXPECT().GetUnbondingDelegationsFromValidator(ctx, gomock.Any()).Return( + []stakingtypes.UnbondingDelegation{ + { + ValidatorAddress: "ethm1a0pd5cyew47pvgf7rd7axxy3humv9ev0nnkprp", + }, + }, nil) + + hooks := testutil.NewMockStakingHooks(ctrl) + hooks.EXPECT().BeforeValidatorModified(ctx, gomock.Any()).Return(nil) + stakingKeeper.EXPECT().Hooks().Return(hooks) + + stakingKeeper.EXPECT().SlashUnbondingDelegation(ctx, gomock.Any(), gomock.Any(), gomock.Any()).Return( + math.NewInt(0), errors.New("staking keeper slash unbonding delegation error")) + }, + bankMocks: func(ctx sdk.Context, bankKeeper *testutil.MockBankKeeper) { + bankKeeper.EXPECT().GetBalance(ctx, gomock.Any(), gomock.Any()).Return(sdk.Coin{ + Denom: "BND", + Amount: math.NewInt(0), + }) + }, + slashingMocks: func(ctx sdk.Context, slashingKeeper *testutil.MockSlashingKeeper) {}, + }, + { + name: "should fail - staking keeper returns error on RemoveValidatorTokens call", + validatorAddress: "ethm1a0pd5cyew47pvgf7rd7axxy3humv9ev0nnkprp", + expectedError: errors.New("staking keeper remove validator tokens error"), + stakingMocks: func(ctx sdk.Context, stakingKeeper *testutil.MockStakingKeeper) { + stakingKeeper.EXPECT().GetParams(ctx).Return(stakingtypes.Params{ + BondDenom: "BND", + }, nil) + stakingKeeper.EXPECT().GetValidator(ctx, gomock.Any()).Return(stakingtypes.Validator{ + Tokens: sdk.DefaultPowerReduction, + },nil) + stakingKeeper.EXPECT().GetUnbondingDelegationsFromValidator(ctx, gomock.Any()).Return( + []stakingtypes.UnbondingDelegation{}, nil, + ) + + hooks := testutil.NewMockStakingHooks(ctrl) + hooks.EXPECT().BeforeValidatorModified(ctx, gomock.Any()).Return(nil) + hooks.EXPECT().BeforeValidatorSlashed(ctx, gomock.Any(),gomock.Any()).Return(errors.New("staking keeper hook error")) + stakingKeeper.EXPECT().Hooks().Return(hooks).AnyTimes() + + stakingKeeper.EXPECT().RemoveValidatorTokens(ctx, gomock.Any(), gomock.Any()).Return( + stakingtypes.Validator{}, errors.New("staking keeper remove validator tokens error"), + ) + }, + bankMocks: func(ctx sdk.Context, bankKeeper *testutil.MockBankKeeper) { + bankKeeper.EXPECT().GetBalance(ctx, gomock.Any(), gomock.Any()).Return(sdk.Coin{ + Denom: "BND", + Amount: math.NewInt(0), + }) + }, + slashingMocks: func(ctx sdk.Context, slashingKeeper *testutil.MockSlashingKeeper) {}, + }, + { + name: "should fail - bank keeper returns error on call BurnCoins for status bonded", + validatorAddress: "ethm1a0pd5cyew47pvgf7rd7axxy3humv9ev0nnkprp", + expectedError: errors.New("bank keeper burn coins error"), + stakingMocks: func(ctx sdk.Context, stakingKeeper *testutil.MockStakingKeeper) { + stakingKeeper.EXPECT().GetParams(ctx).Return(stakingtypes.Params{ + BondDenom: "BND", + }, nil) + stakingKeeper.EXPECT().GetValidator(ctx, gomock.Any()).Return(stakingtypes.Validator{ + Tokens: math.NewInt(0), + },nil) + stakingKeeper.EXPECT().GetUnbondingDelegationsFromValidator(ctx, gomock.Any()).Return( + []stakingtypes.UnbondingDelegation{}, nil, + ) + + hooks := testutil.NewMockStakingHooks(ctrl) + hooks.EXPECT().BeforeValidatorModified(ctx, gomock.Any()).Return(nil) + stakingKeeper.EXPECT().Hooks().Return(hooks).AnyTimes() + + stakingKeeper.EXPECT().RemoveValidatorTokens(ctx, gomock.Any(), gomock.Any()).Return( + stakingtypes.Validator{ + Status: stakingtypes.Bonded, + }, nil, + ) + }, + bankMocks: func(ctx sdk.Context, bankKeeper *testutil.MockBankKeeper) { + bankKeeper.EXPECT().GetBalance(ctx, gomock.Any(), gomock.Any()).Return(sdk.Coin{ + Denom: "BND", + Amount: math.NewInt(0), + }) + bankKeeper.EXPECT().BurnCoins(ctx, gomock.Any(), gomock.Any()).Return(errors.New("bank keeper burn coins error")) + }, + slashingMocks: func(ctx sdk.Context, slashingKeeper *testutil.MockSlashingKeeper) {}, + }, + { + name: "should fail - bank keeper returns error on call BurnCoins for status unbonding/unbonded", + validatorAddress: "ethm1a0pd5cyew47pvgf7rd7axxy3humv9ev0nnkprp", + expectedError: errors.New("bank keeper burn coins error"), + stakingMocks: func(ctx sdk.Context, stakingKeeper *testutil.MockStakingKeeper) { + stakingKeeper.EXPECT().GetParams(ctx).Return(stakingtypes.Params{ + BondDenom: "BND", + }, nil) + stakingKeeper.EXPECT().GetValidator(ctx, gomock.Any()).Return(stakingtypes.Validator{ + Tokens: math.NewInt(0), + },nil) + stakingKeeper.EXPECT().GetUnbondingDelegationsFromValidator(ctx, gomock.Any()).Return( + []stakingtypes.UnbondingDelegation{}, nil, + ) + + hooks := testutil.NewMockStakingHooks(ctrl) + hooks.EXPECT().BeforeValidatorModified(ctx, gomock.Any()).Return(nil) + stakingKeeper.EXPECT().Hooks().Return(hooks).AnyTimes() + + stakingKeeper.EXPECT().RemoveValidatorTokens(ctx, gomock.Any(), gomock.Any()).Return( + stakingtypes.Validator{ + Status: stakingtypes.Unbonding, + }, nil, + ) + }, + bankMocks: func(ctx sdk.Context, bankKeeper *testutil.MockBankKeeper) { + bankKeeper.EXPECT().GetBalance(ctx, gomock.Any(), gomock.Any()).Return(sdk.Coin{ + Denom: "BND", + Amount: math.NewInt(0), + }) + bankKeeper.EXPECT().BurnCoins(ctx, gomock.Any(), gomock.Any()).Return(errors.New("bank keeper burn coins error")) + }, + slashingMocks: func(ctx sdk.Context, slashingKeeper *testutil.MockSlashingKeeper) {}, + }, + { + name: "should fail - bank keeper returns error for invalid validator status", + validatorAddress: "ethm1a0pd5cyew47pvgf7rd7axxy3humv9ev0nnkprp", + expectedError: types.ErrInvalidValidatorStatus, + stakingMocks: func(ctx sdk.Context, stakingKeeper *testutil.MockStakingKeeper) { + stakingKeeper.EXPECT().GetParams(ctx).Return(stakingtypes.Params{ + BondDenom: "BND", + }, nil) + stakingKeeper.EXPECT().GetValidator(ctx, gomock.Any()).Return(stakingtypes.Validator{ + Tokens: math.NewInt(0), + },nil) + stakingKeeper.EXPECT().GetUnbondingDelegationsFromValidator(ctx, gomock.Any()).Return( + []stakingtypes.UnbondingDelegation{}, nil, + ) + + hooks := testutil.NewMockStakingHooks(ctrl) + hooks.EXPECT().BeforeValidatorModified(ctx, gomock.Any()).Return(nil) + stakingKeeper.EXPECT().Hooks().Return(hooks).AnyTimes() + + stakingKeeper.EXPECT().RemoveValidatorTokens(ctx, gomock.Any(), gomock.Any()).Return( + stakingtypes.Validator{ + Status: stakingtypes.Unspecified, + }, nil, + ) + }, + bankMocks: func(ctx sdk.Context, bankKeeper *testutil.MockBankKeeper) { + bankKeeper.EXPECT().GetBalance(ctx, gomock.Any(), gomock.Any()).Return(sdk.Coin{ + Denom: "BND", + Amount: math.NewInt(0), + }) + }, + slashingMocks: func(ctx sdk.Context, slashingKeeper *testutil.MockSlashingKeeper) {}, + }, + { + name: "should fail - staking keeper returns error on call Unbond", + validatorAddress: "ethm1a0pd5cyew47pvgf7rd7axxy3humv9ev0nnkprp", + expectedError: errors.New("staking keeper unbond error"), + stakingMocks: func(ctx sdk.Context, stakingKeeper *testutil.MockStakingKeeper) { + stakingKeeper.EXPECT().GetParams(ctx).Return(stakingtypes.Params{ + BondDenom: "BND", + }, nil) + stakingKeeper.EXPECT().GetValidator(ctx, gomock.Any()).Return(stakingtypes.Validator{ + Tokens: math.NewInt(0), + },nil) + stakingKeeper.EXPECT().GetUnbondingDelegationsFromValidator(ctx, gomock.Any()).Return( + []stakingtypes.UnbondingDelegation{}, nil, + ) + + hooks := testutil.NewMockStakingHooks(ctrl) + hooks.EXPECT().BeforeValidatorModified(ctx, gomock.Any()).Return(nil) + stakingKeeper.EXPECT().Hooks().Return(hooks).AnyTimes() + + stakingKeeper.EXPECT().RemoveValidatorTokens(ctx, gomock.Any(), gomock.Any()).Return( + stakingtypes.Validator{ + Status: stakingtypes.Bonded, + }, nil, + ) + + stakingKeeper.EXPECT().Unbond(ctx, gomock.Any(), gomock.Any(), gomock.Any()).Return( + math.NewInt(0), errors.New("staking keeper unbond error"), + ) + }, + bankMocks: func(ctx sdk.Context, bankKeeper *testutil.MockBankKeeper) { + bankKeeper.EXPECT().GetBalance(ctx, gomock.Any(), gomock.Any()).Return(sdk.Coin{ + Denom: "BND", + Amount: math.NewInt(0), + }) + bankKeeper.EXPECT().BurnCoins(ctx, gomock.Any(), gomock.Any()).Return(nil) + }, + slashingMocks: func(ctx sdk.Context, slashingKeeper *testutil.MockSlashingKeeper) {}, + }, + } + + for _, tc := range tt { + t.Run(tc.name, func(t *testing.T) { + keeper, ctx := setupPoaKeeper(t, tc.stakingMocks, tc.bankMocks, tc.slashingMocks) + + err := keeper.ExecuteRemoveValidator(ctx, tc.validatorAddress) + if tc.expectedError != nil { + require.Contains(t, err.Error(), tc.expectedError.Error()) + } else { + require.NoError(t, err) + } + }) + } } From 63f5ec8706387ae92d403c61615311209c0f757b Mon Sep 17 00:00:00 2001 From: GuillemGarciaDev Date: Wed, 15 Jan 2025 14:32:19 +0100 Subject: [PATCH 44/60] fix(x/poa): msg server unit tests --- x/poa/keeper/msg_server_add_validator_test.go | 61 +++++++++++++++---- .../msg_server_remove_validator_test.go | 47 ++++++++++++-- 2 files changed, 91 insertions(+), 17 deletions(-) diff --git a/x/poa/keeper/msg_server_add_validator_test.go b/x/poa/keeper/msg_server_add_validator_test.go index 1b0d3c8..b388b2e 100644 --- a/x/poa/keeper/msg_server_add_validator_test.go +++ b/x/poa/keeper/msg_server_add_validator_test.go @@ -1,9 +1,11 @@ package keeper import ( + "errors" "testing" types1 "github.com/cosmos/cosmos-sdk/codec/types" + govtypes "github.com/cosmos/cosmos-sdk/x/gov/types" stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" "github.com/golang/mock/gomock" "github.com/stretchr/testify/require" @@ -19,19 +21,54 @@ func TestMsgServer_AddValidator(t *testing.T) { msgPubKey, _ := types1.NewAnyWithValue(pubKey) msgServer := NewMsgServerImpl(*poaKeeper) - msg := &types.MsgAddValidator{ - Authority: poaKeeper.GetAuthority(), - ValidatorAddress: "ethm1a0pd5cyew47pvgf7rd7axxy3humv9ev0nnkprp", - Description: stakingtypes.Description{ - Moniker: "test", - Identity: "test", - Website: "test", - SecurityContact: "test", - Details: "test", + tt := []struct{ + name string + authority string + validatorAddress string + expectedErr error + }{ + { + name: "should fail - invalid authority address", + authority: "invalidauthority", + validatorAddress: "ethm1a0pd5cyew47pvgf7rd7axxy3humv9ev0nnkprp", + expectedErr: govtypes.ErrInvalidSigner, }, - Pubkey: msgPubKey, + { + name: "should fail - invalid validator address", + authority: poaKeeper.GetAuthority(), + validatorAddress: "invalidvalidatoraddress", + expectedErr: errors.New("decoding bech32 failed"), + }, + { + name: "should pass", + authority: poaKeeper.GetAuthority(), + validatorAddress: "ethm1a0pd5cyew47pvgf7rd7axxy3humv9ev0nnkprp", + }, + } + + for _, tc := range tt { + t.Run(tc.name, func(t *testing.T) { + msg := &types.MsgAddValidator{ + Authority: tc.authority, + ValidatorAddress: tc.validatorAddress, + Description: stakingtypes.Description{ + Moniker: "test", + Identity: "test", + Website: "test", + SecurityContact: "test", + Details: "test", + }, + Pubkey: msgPubKey, + } + + _, err := msgServer.AddValidator(ctx, msg) + if tc.expectedErr != nil { + require.Error(t, err) + require.Contains(t, err.Error(), tc.expectedErr.Error()) + } else { + require.NoError(t, err) + } + }) } - _, err := msgServer.AddValidator(ctx, msg) - require.NoError(t, err) } diff --git a/x/poa/keeper/msg_server_remove_validator_test.go b/x/poa/keeper/msg_server_remove_validator_test.go index 2031c13..8561e9a 100644 --- a/x/poa/keeper/msg_server_remove_validator_test.go +++ b/x/poa/keeper/msg_server_remove_validator_test.go @@ -1,8 +1,10 @@ package keeper import ( + "errors" "testing" + govtypes "github.com/cosmos/cosmos-sdk/x/gov/types" "github.com/stretchr/testify/require" "github.com/xrplevm/node/v5/x/poa/types" ) @@ -12,11 +14,46 @@ func TestMsgServer_RemoveValidator(t *testing.T) { msgServer := NewMsgServerImpl(*poaKeeper) - msg := &types.MsgRemoveValidator{ - Authority: poaKeeper.GetAuthority(), - ValidatorAddress: "ethm1a0pd5cyew47pvgf7rd7axxy3humv9ev0nnkprp", + tt := []struct{ + name string + authority string + validatorAddress string + expectedErr error + }{ + { + name: "should fail - invalid authority address", + authority: "invalidauthority", + validatorAddress: "ethm1a0pd5cyew47pvgf7rd7axxy3humv9ev0nnkprp", + expectedErr: govtypes.ErrInvalidSigner, + }, + { + name: "should fail - invalid validator address", + authority: poaKeeper.GetAuthority(), + validatorAddress: "invalidvalidatoraddress", + expectedErr: errors.New("decoding bech32 failed"), + }, + { + name: "should pass", + authority: poaKeeper.GetAuthority(), + validatorAddress: "ethm1a0pd5cyew47pvgf7rd7axxy3humv9ev0nnkprp", + }, + } + + for _, tc := range tt { + t.Run(tc.name, func(t *testing.T) { + msg := &types.MsgRemoveValidator{ + Authority: tc.authority, + ValidatorAddress: tc.validatorAddress, + } + + _, err := msgServer.RemoveValidator(ctx, msg) + if tc.expectedErr != nil { + require.Error(t, err) + require.Contains(t, err.Error(), tc.expectedErr.Error()) + } else { + require.NoError(t, err) + } + }) } - _, err := msgServer.RemoveValidator(ctx, msg) - require.NoError(t, err) } From a2f620e393326655b93232b7e83fbdfc0496fe1d Mon Sep 17 00:00:00 2001 From: GuillemGarciaDev Date: Wed, 15 Jan 2025 14:32:34 +0100 Subject: [PATCH 45/60] chore: include html files on gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index cd05c1e..65e16b7 100644 --- a/.gitignore +++ b/.gitignore @@ -9,5 +9,6 @@ release/ .exrpd/ *.out +*.html bin/ \ No newline at end of file From e444cd2409220098817dc433ec3a5cf75f66fa6b Mon Sep 17 00:00:00 2001 From: GuillemGarciaDev Date: Wed, 15 Jan 2025 14:49:40 +0100 Subject: [PATCH 46/60] fix(x/poa): fix linting --- x/poa/ante/poa_test.go | 4 +- x/poa/keeper/invariants_test.go | 8 +- x/poa/keeper/keeper_test.go | 205 +++++++++--------- x/poa/keeper/msg_server_add_validator_test.go | 25 +-- .../msg_server_remove_validator_test.go | 25 +-- 5 files changed, 127 insertions(+), 140 deletions(-) diff --git a/x/poa/ante/poa_test.go b/x/poa/ante/poa_test.go index 1574f93..21b0c1c 100644 --- a/x/poa/ante/poa_test.go +++ b/x/poa/ante/poa_test.go @@ -31,8 +31,8 @@ func setupPoaDecorator(t *testing.T) ( func TestPoaDecorator_AnteHandle(t *testing.T) { tt := []struct { - name string - msgs []sdk.Msg + name string + msgs []sdk.Msg expectedError error }{ { diff --git a/x/poa/keeper/invariants_test.go b/x/poa/keeper/invariants_test.go index 6fe18f9..f8b7fc1 100644 --- a/x/poa/keeper/invariants_test.go +++ b/x/poa/keeper/invariants_test.go @@ -13,12 +13,12 @@ import ( func TestStakingPowerInvariant_Valid(t *testing.T) { tt := []struct { - name string - broken bool + name string + broken bool validators func() ([]stakingtypes.Validator, error) }{ { - name: "should pass - all validators have the same staking power", + name: "should pass - all validators have the same staking power", broken: false, validators: func() ([]stakingtypes.Validator, error) { return []stakingtypes.Validator{ @@ -29,7 +29,7 @@ func TestStakingPowerInvariant_Valid(t *testing.T) { }, }, { - name: "should fail - one validator has excessive staking power", + name: "should fail - one validator has excessive staking power", broken: true, validators: func() ([]stakingtypes.Validator, error) { return []stakingtypes.Validator{ diff --git a/x/poa/keeper/keeper_test.go b/x/poa/keeper/keeper_test.go index 4489fd9..13b08ab 100644 --- a/x/poa/keeper/keeper_test.go +++ b/x/poa/keeper/keeper_test.go @@ -56,37 +56,37 @@ func TestKeeper_ExecuteAddValidator(t *testing.T) { pubKey := testutil.NewMockPubKey(ctrl) msgPubKey, _ := types1.NewAnyWithValue(pubKey) - tt := []struct{ - name string + tt := []struct { + name string validatorAddress string - pubKey *types1.Any - stakingMocks func(ctx sdk.Context, stakingKeeper *testutil.MockStakingKeeper) - bankMocks func(ctx sdk.Context, bankKeeper *testutil.MockBankKeeper) - slashingMocks func(ctx sdk.Context, slashingKeeper *testutil.MockSlashingKeeper) - expectedError error + pubKey *types1.Any + stakingMocks func(ctx sdk.Context, stakingKeeper *testutil.MockStakingKeeper) + bankMocks func(ctx sdk.Context, bankKeeper *testutil.MockBankKeeper) + slashingMocks func(ctx sdk.Context, slashingKeeper *testutil.MockSlashingKeeper) + expectedError error }{ { - name: "should fail - invalid validator address", + name: "should fail - invalid validator address", validatorAddress: "invalidnaddress", - expectedError: errors.New("decoding bech32 failed"), - stakingMocks: func(ctx sdk.Context, stakingKeeper *testutil.MockStakingKeeper) {}, - bankMocks: func(ctx sdk.Context, bankKeeper *testutil.MockBankKeeper) {}, - slashingMocks: func(ctx sdk.Context, slashingKeeper *testutil.MockSlashingKeeper) {}, + expectedError: errors.New("decoding bech32 failed"), + stakingMocks: func(_ sdk.Context, _ *testutil.MockStakingKeeper) {}, + bankMocks: func(_ sdk.Context, _ *testutil.MockBankKeeper) {}, + slashingMocks: func(_ sdk.Context, _ *testutil.MockSlashingKeeper) {}, }, { - name: "should fail - staking keeper returns error on GetParams", + name: "should fail - staking keeper returns error on GetParams", validatorAddress: "ethm1a0pd5cyew47pvgf7rd7axxy3humv9ev0nnkprp", - expectedError: errors.New("staking params error"), + expectedError: errors.New("staking params error"), stakingMocks: func(ctx sdk.Context, stakingKeeper *testutil.MockStakingKeeper) { stakingKeeper.EXPECT().GetParams(ctx).Return(stakingtypes.Params{}, errors.New("staking params error")) }, - bankMocks: func(ctx sdk.Context, bankKeeper *testutil.MockBankKeeper) {}, - slashingMocks: func(ctx sdk.Context, slashingKeeper *testutil.MockSlashingKeeper) {}, + bankMocks: func(_ sdk.Context, _ *testutil.MockBankKeeper) {}, + slashingMocks: func(_ sdk.Context, _ *testutil.MockSlashingKeeper) {}, }, { - name: "should fail - validator has bonded tokens", + name: "should fail - validator has bonded tokens", validatorAddress: "ethm1a0pd5cyew47pvgf7rd7axxy3humv9ev0nnkprp", - expectedError: types.ErrAddressHasBankTokens, + expectedError: types.ErrAddressHasBankTokens, stakingMocks: func(ctx sdk.Context, stakingKeeper *testutil.MockStakingKeeper) { stakingKeeper.EXPECT().GetParams(ctx).Return(stakingtypes.Params{ BondDenom: "BND", @@ -98,12 +98,12 @@ func TestKeeper_ExecuteAddValidator(t *testing.T) { Amount: sdk.DefaultPowerReduction, }) }, - slashingMocks: func(ctx sdk.Context, slashingKeeper *testutil.MockSlashingKeeper) {}, + slashingMocks: func(_ sdk.Context, _ *testutil.MockSlashingKeeper) {}, }, { - name: "should fail - staking keeper returns error on GetValidator", + name: "should fail - staking keeper returns error on GetValidator", validatorAddress: "ethm1a0pd5cyew47pvgf7rd7axxy3humv9ev0nnkprp", - expectedError: errors.New("staking validator error"), + expectedError: errors.New("staking validator error"), stakingMocks: func(ctx sdk.Context, stakingKeeper *testutil.MockStakingKeeper) { stakingKeeper.EXPECT().GetParams(ctx).Return(stakingtypes.Params{ BondDenom: "BND", @@ -116,12 +116,12 @@ func TestKeeper_ExecuteAddValidator(t *testing.T) { Amount: math.NewInt(0), }) }, - slashingMocks: func(ctx sdk.Context, slashingKeeper *testutil.MockSlashingKeeper) {}, + slashingMocks: func(_ sdk.Context, _ *testutil.MockSlashingKeeper) {}, }, { - name: "should fail - staking keeper returns validator with tokens", + name: "should fail - staking keeper returns validator with tokens", validatorAddress: "ethm1a0pd5cyew47pvgf7rd7axxy3humv9ev0nnkprp", - expectedError: types.ErrAddressHasBondedTokens, + expectedError: types.ErrAddressHasBondedTokens, stakingMocks: func(ctx sdk.Context, stakingKeeper *testutil.MockStakingKeeper) { stakingKeeper.EXPECT().GetParams(ctx).Return(stakingtypes.Params{ BondDenom: "BND", @@ -134,12 +134,12 @@ func TestKeeper_ExecuteAddValidator(t *testing.T) { Amount: math.NewInt(0), }) }, - slashingMocks: func(ctx sdk.Context, slashingKeeper *testutil.MockSlashingKeeper) {}, + slashingMocks: func(_ sdk.Context, _ *testutil.MockSlashingKeeper) {}, }, { - name: "should fail - staking keeper returns error on GetAllDelegatorDelegations", + name: "should fail - staking keeper returns error on GetAllDelegatorDelegations", validatorAddress: "ethm1a0pd5cyew47pvgf7rd7axxy3humv9ev0nnkprp", - expectedError: errors.New("staking delegations error"), + expectedError: errors.New("staking delegations error"), stakingMocks: func(ctx sdk.Context, stakingKeeper *testutil.MockStakingKeeper) { stakingKeeper.EXPECT().GetParams(ctx).Return(stakingtypes.Params{ BondDenom: "BND", @@ -153,12 +153,12 @@ func TestKeeper_ExecuteAddValidator(t *testing.T) { Amount: math.NewInt(0), }) }, - slashingMocks: func(ctx sdk.Context, slashingKeeper *testutil.MockSlashingKeeper) {}, + slashingMocks: func(_ sdk.Context, _ *testutil.MockSlashingKeeper) {}, }, { - name: "should fail - delegations are greater than 0 with invalid delegation validator address", + name: "should fail - delegations are greater than 0 with invalid delegation validator address", validatorAddress: "ethm1a0pd5cyew47pvgf7rd7axxy3humv9ev0nnkprp", - expectedError: errors.New("decoding bech32 failed"), + expectedError: errors.New("decoding bech32 failed"), stakingMocks: func(ctx sdk.Context, stakingKeeper *testutil.MockStakingKeeper) { stakingKeeper.EXPECT().GetParams(ctx).Return(stakingtypes.Params{ BondDenom: "BND", @@ -167,7 +167,7 @@ func TestKeeper_ExecuteAddValidator(t *testing.T) { stakingKeeper.EXPECT().GetAllDelegatorDelegations(ctx, gomock.Any()).Return([]stakingtypes.Delegation{ { ValidatorAddress: "invalidvalidatoraddress", - Shares: sdk.DefaultPowerReduction.ToLegacyDec(), + Shares: sdk.DefaultPowerReduction.ToLegacyDec(), }, }, nil) }, @@ -177,7 +177,7 @@ func TestKeeper_ExecuteAddValidator(t *testing.T) { Amount: math.NewInt(0), }) }, - slashingMocks: func(ctx sdk.Context, slashingKeeper *testutil.MockSlashingKeeper) {}, + slashingMocks: func(_ sdk.Context, _ *testutil.MockSlashingKeeper) {}, }, // { // name: "should fail - delegations are greater than 0 with error on GetValidator call", @@ -208,9 +208,9 @@ func TestKeeper_ExecuteAddValidator(t *testing.T) { // slashingMocks: func(ctx sdk.Context, slashingKeeper *testutil.MockSlashingKeeper) {}, // }, { - name: "should fail - GetUnbondingDelegationsFromValidator returns error", + name: "should fail - GetUnbondingDelegationsFromValidator returns error", validatorAddress: "ethm1a0pd5cyew47pvgf7rd7axxy3humv9ev0nnkprp", - expectedError: errors.New("staking unbonding delegations error"), + expectedError: errors.New("staking unbonding delegations error"), stakingMocks: func(ctx sdk.Context, stakingKeeper *testutil.MockStakingKeeper) { stakingKeeper.EXPECT().GetParams(ctx).Return(stakingtypes.Params{ BondDenom: "BND", @@ -225,12 +225,12 @@ func TestKeeper_ExecuteAddValidator(t *testing.T) { Amount: math.NewInt(0), }) }, - slashingMocks: func(ctx sdk.Context, slashingKeeper *testutil.MockSlashingKeeper) {}, + slashingMocks: func(_ sdk.Context, _ *testutil.MockSlashingKeeper) {}, }, { - name: "should fail - unbonding delegations balances are greater than 0", + name: "should fail - unbonding delegations balances are greater than 0", validatorAddress: "ethm1a0pd5cyew47pvgf7rd7axxy3humv9ev0nnkprp", - expectedError: types.ErrAddressHasUnbondingTokens, + expectedError: types.ErrAddressHasUnbondingTokens, stakingMocks: func(ctx sdk.Context, stakingKeeper *testutil.MockStakingKeeper) { stakingKeeper.EXPECT().GetParams(ctx).Return(stakingtypes.Params{ BondDenom: "BND", @@ -253,12 +253,12 @@ func TestKeeper_ExecuteAddValidator(t *testing.T) { Amount: math.NewInt(0), }) }, - slashingMocks: func(ctx sdk.Context, slashingKeeper *testutil.MockSlashingKeeper) {}, + slashingMocks: func(_ sdk.Context, _ *testutil.MockSlashingKeeper) {}, }, { - name: "should fail - bank keeper MintCoins returns error", + name: "should fail - bank keeper MintCoins returns error", validatorAddress: "ethm1a0pd5cyew47pvgf7rd7axxy3humv9ev0nnkprp", - expectedError: errors.New("bank mint coins error"), + expectedError: errors.New("bank mint coins error"), stakingMocks: func(ctx sdk.Context, stakingKeeper *testutil.MockStakingKeeper) { stakingKeeper.EXPECT().GetParams(ctx).Return(stakingtypes.Params{ BondDenom: "BND", @@ -274,12 +274,12 @@ func TestKeeper_ExecuteAddValidator(t *testing.T) { }) bankKeeper.EXPECT().MintCoins(ctx, gomock.Any(), gomock.Any()).Return(errors.New("bank mint coins error")) }, - slashingMocks: func(ctx sdk.Context, slashingKeeper *testutil.MockSlashingKeeper) {}, + slashingMocks: func(_ sdk.Context, _ *testutil.MockSlashingKeeper) {}, }, { - name: "should fail - bank keeper SendCoinsFromModuleToAccount returns error", + name: "should fail - bank keeper SendCoinsFromModuleToAccount returns error", validatorAddress: "ethm1a0pd5cyew47pvgf7rd7axxy3humv9ev0nnkprp", - expectedError: errors.New("bank send coins from module to account error"), + expectedError: errors.New("bank send coins from module to account error"), stakingMocks: func(ctx sdk.Context, stakingKeeper *testutil.MockStakingKeeper) { stakingKeeper.EXPECT().GetParams(ctx).Return(stakingtypes.Params{ BondDenom: "BND", @@ -296,12 +296,12 @@ func TestKeeper_ExecuteAddValidator(t *testing.T) { bankKeeper.EXPECT().MintCoins(ctx, gomock.Any(), gomock.Any()).Return(nil) bankKeeper.EXPECT().SendCoinsFromModuleToAccount(ctx, gomock.Any(), gomock.Any(), gomock.Any()).Return(errors.New("bank send coins from module to account error")) }, - slashingMocks: func(ctx sdk.Context, slashingKeeper *testutil.MockSlashingKeeper) {}, + slashingMocks: func(_ sdk.Context, _ *testutil.MockSlashingKeeper) {}, }, { - name: "should pass - MsgAddValidator", + name: "should pass - MsgAddValidator", validatorAddress: "ethm1a0pd5cyew47pvgf7rd7axxy3humv9ev0nnkprp", - pubKey: msgPubKey, + pubKey: msgPubKey, stakingMocks: func(ctx sdk.Context, stakingKeeper *testutil.MockStakingKeeper) { stakingKeeper.EXPECT().GetParams(ctx).Return(stakingtypes.Params{ BondDenom: "BND", @@ -318,7 +318,7 @@ func TestKeeper_ExecuteAddValidator(t *testing.T) { bankKeeper.EXPECT().MintCoins(ctx, gomock.Any(), gomock.Any()).Return(nil) bankKeeper.EXPECT().SendCoinsFromModuleToAccount(ctx, gomock.Any(), gomock.Any(), gomock.Any()).Return(nil) }, - slashingMocks: func(ctx sdk.Context, slashingKeeper *testutil.MockSlashingKeeper) {}, + slashingMocks: func(_ sdk.Context, _ *testutil.MockSlashingKeeper) {}, }, } @@ -352,36 +352,33 @@ func TestKeeper_ExecuteAddValidator(t *testing.T) { func TestKeeper_ExecuteRemoveValidator(t *testing.T) { ctrl := gomock.NewController(t) - tt := []struct{ - name string + tt := []struct { + name string validatorAddress string - stakingMocks func(ctx sdk.Context, stakingKeeper *testutil.MockStakingKeeper) - bankMocks func(ctx sdk.Context, bankKeeper *testutil.MockBankKeeper) - slashingMocks func(ctx sdk.Context, slashingKeeper *testutil.MockSlashingKeeper) - expectedError error + stakingMocks func(ctx sdk.Context, stakingKeeper *testutil.MockStakingKeeper) + bankMocks func(ctx sdk.Context, bankKeeper *testutil.MockBankKeeper) + expectedError error }{ { - name: "should fail - invalid validator address", + name: "should fail - invalid validator address", validatorAddress: "invalidnaddress", - expectedError: errors.New("decoding bech32 failed"), - stakingMocks: func(ctx sdk.Context, stakingKeeper *testutil.MockStakingKeeper) {}, - bankMocks: func(ctx sdk.Context, bankKeeper *testutil.MockBankKeeper) {}, - slashingMocks: func(ctx sdk.Context, slashingKeeper *testutil.MockSlashingKeeper) {}, + expectedError: errors.New("decoding bech32 failed"), + stakingMocks: func(_ sdk.Context, _ *testutil.MockStakingKeeper) {}, + bankMocks: func(_ sdk.Context, _ *testutil.MockBankKeeper) {}, }, { - name: "should fail - staking keeper returns error on GetParams", + name: "should fail - staking keeper returns error on GetParams", validatorAddress: "ethm1a0pd5cyew47pvgf7rd7axxy3humv9ev0nnkprp", - expectedError: errors.New("staking params error"), + expectedError: errors.New("staking params error"), stakingMocks: func(ctx sdk.Context, stakingKeeper *testutil.MockStakingKeeper) { stakingKeeper.EXPECT().GetParams(ctx).Return(stakingtypes.Params{}, errors.New("staking params error")) }, - bankMocks: func(ctx sdk.Context, bankKeeper *testutil.MockBankKeeper) {}, - slashingMocks: func(ctx sdk.Context, slashingKeeper *testutil.MockSlashingKeeper) {}, + bankMocks: func(_ sdk.Context, _ *testutil.MockBankKeeper) {}, }, { - name: "should fail - bank keeper returns error on SendCoinsFromAccountToModule when validator has balance", + name: "should fail - bank keeper returns error on SendCoinsFromAccountToModule when validator has balance", validatorAddress: "ethm1a0pd5cyew47pvgf7rd7axxy3humv9ev0nnkprp", - expectedError: errors.New("bank send coins from account to module error"), + expectedError: errors.New("bank send coins from account to module error"), stakingMocks: func(ctx sdk.Context, stakingKeeper *testutil.MockStakingKeeper) { stakingKeeper.EXPECT().GetParams(ctx).Return(stakingtypes.Params{ BondDenom: "BND", @@ -394,12 +391,11 @@ func TestKeeper_ExecuteRemoveValidator(t *testing.T) { }) bankKeeper.EXPECT().SendCoinsFromAccountToModule(ctx, gomock.Any(), gomock.Any(), gomock.Any()).Return(errors.New("bank send coins from account to module error")) }, - slashingMocks: func(ctx sdk.Context, slashingKeeper *testutil.MockSlashingKeeper) {}, }, { - name: "should fail - bank keeper returns error on BurnCoins when validator has balance", + name: "should fail - bank keeper returns error on BurnCoins when validator has balance", validatorAddress: "ethm1a0pd5cyew47pvgf7rd7axxy3humv9ev0nnkprp", - expectedError: errors.New("bank burn coins error"), + expectedError: errors.New("bank burn coins error"), stakingMocks: func(ctx sdk.Context, stakingKeeper *testutil.MockStakingKeeper) { stakingKeeper.EXPECT().GetParams(ctx).Return(stakingtypes.Params{ BondDenom: "BND", @@ -413,17 +409,15 @@ func TestKeeper_ExecuteRemoveValidator(t *testing.T) { bankKeeper.EXPECT().SendCoinsFromAccountToModule(ctx, gomock.Any(), gomock.Any(), gomock.Any()).Return(nil) bankKeeper.EXPECT().BurnCoins(ctx, gomock.Any(), gomock.Any()).Return(errors.New("bank burn coins error")) }, - slashingMocks: func(ctx sdk.Context, slashingKeeper *testutil.MockSlashingKeeper) {}, }, { - name: "should fail - staking keeper returns error on GetValidator with balance", + name: "should fail - staking keeper returns error on GetValidator with balance", validatorAddress: "ethm1a0pd5cyew47pvgf7rd7axxy3humv9ev0nnkprp", stakingMocks: func(ctx sdk.Context, stakingKeeper *testutil.MockStakingKeeper) { stakingKeeper.EXPECT().GetParams(ctx).Return(stakingtypes.Params{ BondDenom: "BND", }, nil) - stakingKeeper.EXPECT().GetValidator(ctx, gomock.Any()).Return(stakingtypes.Validator{ - }, errors.New("staking keeper get validator error")) + stakingKeeper.EXPECT().GetValidator(ctx, gomock.Any()).Return(stakingtypes.Validator{}, errors.New("staking keeper get validator error")) }, bankMocks: func(ctx sdk.Context, bankKeeper *testutil.MockBankKeeper) { bankKeeper.EXPECT().GetBalance(ctx, gomock.Any(), gomock.Any()).Return(sdk.Coin{ @@ -433,18 +427,16 @@ func TestKeeper_ExecuteRemoveValidator(t *testing.T) { bankKeeper.EXPECT().SendCoinsFromAccountToModule(ctx, gomock.Any(), gomock.Any(), gomock.Any()).Return(nil) bankKeeper.EXPECT().BurnCoins(ctx, gomock.Any(), gomock.Any()).Return(nil) }, - slashingMocks: func(ctx sdk.Context, slashingKeeper *testutil.MockSlashingKeeper) {}, }, { - name: "should fail - staking keeper returns error on GetValidator with no balance", + name: "should fail - staking keeper returns error on GetValidator with no balance", validatorAddress: "ethm1a0pd5cyew47pvgf7rd7axxy3humv9ev0nnkprp", - expectedError: types.ErrAddressHasNoTokens, + expectedError: types.ErrAddressHasNoTokens, stakingMocks: func(ctx sdk.Context, stakingKeeper *testutil.MockStakingKeeper) { stakingKeeper.EXPECT().GetParams(ctx).Return(stakingtypes.Params{ BondDenom: "BND", }, nil) - stakingKeeper.EXPECT().GetValidator(ctx, gomock.Any()).Return(stakingtypes.Validator{ - }, errors.New("staking keeper get validator error")) + stakingKeeper.EXPECT().GetValidator(ctx, gomock.Any()).Return(stakingtypes.Validator{}, errors.New("staking keeper get validator error")) }, bankMocks: func(ctx sdk.Context, bankKeeper *testutil.MockBankKeeper) { bankKeeper.EXPECT().GetBalance(ctx, gomock.Any(), gomock.Any()).Return(sdk.Coin{ @@ -452,17 +444,16 @@ func TestKeeper_ExecuteRemoveValidator(t *testing.T) { Amount: math.NewInt(0), }) }, - slashingMocks: func(ctx sdk.Context, slashingKeeper *testutil.MockSlashingKeeper) {}, }, { - name: "should fail - staking keeper returns error on call GetUnbondingDelegationsFromValidator", + name: "should fail - staking keeper returns error on call GetUnbondingDelegationsFromValidator", validatorAddress: "ethm1a0pd5cyew47pvgf7rd7axxy3humv9ev0nnkprp", - expectedError: errors.New("staking keeper get unbonding delegations from validator error"), + expectedError: errors.New("staking keeper get unbonding delegations from validator error"), stakingMocks: func(ctx sdk.Context, stakingKeeper *testutil.MockStakingKeeper) { stakingKeeper.EXPECT().GetParams(ctx).Return(stakingtypes.Params{ BondDenom: "BND", }, nil) - stakingKeeper.EXPECT().GetValidator(ctx, gomock.Any()).Return(stakingtypes.Validator{},nil) + stakingKeeper.EXPECT().GetValidator(ctx, gomock.Any()).Return(stakingtypes.Validator{}, nil) stakingKeeper.EXPECT().GetUnbondingDelegationsFromValidator(ctx, gomock.Any()).Return( []stakingtypes.UnbondingDelegation{}, errors.New("staking keeper get unbonding delegations from validator error")) hooks := testutil.NewMockStakingHooks(ctrl) @@ -475,21 +466,20 @@ func TestKeeper_ExecuteRemoveValidator(t *testing.T) { Amount: math.NewInt(0), }) }, - slashingMocks: func(ctx sdk.Context, slashingKeeper *testutil.MockSlashingKeeper) {}, }, { - name: "should fail - staking keeper returns error on call SlashUnbondingDelegation", + name: "should fail - staking keeper returns error on call SlashUnbondingDelegation", validatorAddress: "ethm1a0pd5cyew47pvgf7rd7axxy3humv9ev0nnkprp", - expectedError: errors.New("staking keeper slash unbonding delegation error"), + expectedError: errors.New("staking keeper slash unbonding delegation error"), stakingMocks: func(ctx sdk.Context, stakingKeeper *testutil.MockStakingKeeper) { stakingKeeper.EXPECT().GetParams(ctx).Return(stakingtypes.Params{ BondDenom: "BND", }, nil) - stakingKeeper.EXPECT().GetValidator(ctx, gomock.Any()).Return(stakingtypes.Validator{},nil) + stakingKeeper.EXPECT().GetValidator(ctx, gomock.Any()).Return(stakingtypes.Validator{}, nil) stakingKeeper.EXPECT().GetUnbondingDelegationsFromValidator(ctx, gomock.Any()).Return( []stakingtypes.UnbondingDelegation{ { - ValidatorAddress: "ethm1a0pd5cyew47pvgf7rd7axxy3humv9ev0nnkprp", + ValidatorAddress: "ethm1a0pd5cyew47pvgf7rd7axxy3humv9ev0nnkprp", }, }, nil) @@ -506,26 +496,25 @@ func TestKeeper_ExecuteRemoveValidator(t *testing.T) { Amount: math.NewInt(0), }) }, - slashingMocks: func(ctx sdk.Context, slashingKeeper *testutil.MockSlashingKeeper) {}, }, { - name: "should fail - staking keeper returns error on RemoveValidatorTokens call", + name: "should fail - staking keeper returns error on RemoveValidatorTokens call", validatorAddress: "ethm1a0pd5cyew47pvgf7rd7axxy3humv9ev0nnkprp", - expectedError: errors.New("staking keeper remove validator tokens error"), + expectedError: errors.New("staking keeper remove validator tokens error"), stakingMocks: func(ctx sdk.Context, stakingKeeper *testutil.MockStakingKeeper) { stakingKeeper.EXPECT().GetParams(ctx).Return(stakingtypes.Params{ BondDenom: "BND", }, nil) stakingKeeper.EXPECT().GetValidator(ctx, gomock.Any()).Return(stakingtypes.Validator{ Tokens: sdk.DefaultPowerReduction, - },nil) + }, nil) stakingKeeper.EXPECT().GetUnbondingDelegationsFromValidator(ctx, gomock.Any()).Return( []stakingtypes.UnbondingDelegation{}, nil, ) hooks := testutil.NewMockStakingHooks(ctrl) hooks.EXPECT().BeforeValidatorModified(ctx, gomock.Any()).Return(nil) - hooks.EXPECT().BeforeValidatorSlashed(ctx, gomock.Any(),gomock.Any()).Return(errors.New("staking keeper hook error")) + hooks.EXPECT().BeforeValidatorSlashed(ctx, gomock.Any(), gomock.Any()).Return(errors.New("staking keeper hook error")) stakingKeeper.EXPECT().Hooks().Return(hooks).AnyTimes() stakingKeeper.EXPECT().RemoveValidatorTokens(ctx, gomock.Any(), gomock.Any()).Return( @@ -538,19 +527,20 @@ func TestKeeper_ExecuteRemoveValidator(t *testing.T) { Amount: math.NewInt(0), }) }, - slashingMocks: func(ctx sdk.Context, slashingKeeper *testutil.MockSlashingKeeper) {}, }, + //nolint:dupl { - name: "should fail - bank keeper returns error on call BurnCoins for status bonded", + name: "should fail - bank keeper returns error on call BurnCoins for status bonded", validatorAddress: "ethm1a0pd5cyew47pvgf7rd7axxy3humv9ev0nnkprp", - expectedError: errors.New("bank keeper burn coins error"), + expectedError: errors.New("bank keeper burn coins error"), + //nolint:dupl stakingMocks: func(ctx sdk.Context, stakingKeeper *testutil.MockStakingKeeper) { stakingKeeper.EXPECT().GetParams(ctx).Return(stakingtypes.Params{ BondDenom: "BND", }, nil) stakingKeeper.EXPECT().GetValidator(ctx, gomock.Any()).Return(stakingtypes.Validator{ Tokens: math.NewInt(0), - },nil) + }, nil) stakingKeeper.EXPECT().GetUnbondingDelegationsFromValidator(ctx, gomock.Any()).Return( []stakingtypes.UnbondingDelegation{}, nil, ) @@ -572,19 +562,20 @@ func TestKeeper_ExecuteRemoveValidator(t *testing.T) { }) bankKeeper.EXPECT().BurnCoins(ctx, gomock.Any(), gomock.Any()).Return(errors.New("bank keeper burn coins error")) }, - slashingMocks: func(ctx sdk.Context, slashingKeeper *testutil.MockSlashingKeeper) {}, }, + //nolint:dupl { - name: "should fail - bank keeper returns error on call BurnCoins for status unbonding/unbonded", + name: "should fail - bank keeper returns error on call BurnCoins for status unbonding/unbonded", validatorAddress: "ethm1a0pd5cyew47pvgf7rd7axxy3humv9ev0nnkprp", - expectedError: errors.New("bank keeper burn coins error"), + expectedError: errors.New("bank keeper burn coins error"), + //nolint:dupl stakingMocks: func(ctx sdk.Context, stakingKeeper *testutil.MockStakingKeeper) { stakingKeeper.EXPECT().GetParams(ctx).Return(stakingtypes.Params{ BondDenom: "BND", }, nil) stakingKeeper.EXPECT().GetValidator(ctx, gomock.Any()).Return(stakingtypes.Validator{ Tokens: math.NewInt(0), - },nil) + }, nil) stakingKeeper.EXPECT().GetUnbondingDelegationsFromValidator(ctx, gomock.Any()).Return( []stakingtypes.UnbondingDelegation{}, nil, ) @@ -606,19 +597,19 @@ func TestKeeper_ExecuteRemoveValidator(t *testing.T) { }) bankKeeper.EXPECT().BurnCoins(ctx, gomock.Any(), gomock.Any()).Return(errors.New("bank keeper burn coins error")) }, - slashingMocks: func(ctx sdk.Context, slashingKeeper *testutil.MockSlashingKeeper) {}, }, { - name: "should fail - bank keeper returns error for invalid validator status", + name: "should fail - bank keeper returns error for invalid validator status", validatorAddress: "ethm1a0pd5cyew47pvgf7rd7axxy3humv9ev0nnkprp", - expectedError: types.ErrInvalidValidatorStatus, + expectedError: types.ErrInvalidValidatorStatus, + //nolint:dupl stakingMocks: func(ctx sdk.Context, stakingKeeper *testutil.MockStakingKeeper) { stakingKeeper.EXPECT().GetParams(ctx).Return(stakingtypes.Params{ BondDenom: "BND", }, nil) stakingKeeper.EXPECT().GetValidator(ctx, gomock.Any()).Return(stakingtypes.Validator{ Tokens: math.NewInt(0), - },nil) + }, nil) stakingKeeper.EXPECT().GetUnbondingDelegationsFromValidator(ctx, gomock.Any()).Return( []stakingtypes.UnbondingDelegation{}, nil, ) @@ -639,19 +630,18 @@ func TestKeeper_ExecuteRemoveValidator(t *testing.T) { Amount: math.NewInt(0), }) }, - slashingMocks: func(ctx sdk.Context, slashingKeeper *testutil.MockSlashingKeeper) {}, }, { - name: "should fail - staking keeper returns error on call Unbond", + name: "should fail - staking keeper returns error on call Unbond", validatorAddress: "ethm1a0pd5cyew47pvgf7rd7axxy3humv9ev0nnkprp", - expectedError: errors.New("staking keeper unbond error"), + expectedError: errors.New("staking keeper unbond error"), stakingMocks: func(ctx sdk.Context, stakingKeeper *testutil.MockStakingKeeper) { stakingKeeper.EXPECT().GetParams(ctx).Return(stakingtypes.Params{ BondDenom: "BND", }, nil) stakingKeeper.EXPECT().GetValidator(ctx, gomock.Any()).Return(stakingtypes.Validator{ Tokens: math.NewInt(0), - },nil) + }, nil) stakingKeeper.EXPECT().GetUnbondingDelegationsFromValidator(ctx, gomock.Any()).Return( []stakingtypes.UnbondingDelegation{}, nil, ) @@ -677,13 +667,12 @@ func TestKeeper_ExecuteRemoveValidator(t *testing.T) { }) bankKeeper.EXPECT().BurnCoins(ctx, gomock.Any(), gomock.Any()).Return(nil) }, - slashingMocks: func(ctx sdk.Context, slashingKeeper *testutil.MockSlashingKeeper) {}, }, } for _, tc := range tt { t.Run(tc.name, func(t *testing.T) { - keeper, ctx := setupPoaKeeper(t, tc.stakingMocks, tc.bankMocks, tc.slashingMocks) + keeper, ctx := setupPoaKeeper(t, tc.stakingMocks, tc.bankMocks, func(_ sdk.Context, _ *testutil.MockSlashingKeeper) {}) err := keeper.ExecuteRemoveValidator(ctx, tc.validatorAddress) if tc.expectedError != nil { diff --git a/x/poa/keeper/msg_server_add_validator_test.go b/x/poa/keeper/msg_server_add_validator_test.go index b388b2e..b3c8a84 100644 --- a/x/poa/keeper/msg_server_add_validator_test.go +++ b/x/poa/keeper/msg_server_add_validator_test.go @@ -21,27 +21,27 @@ func TestMsgServer_AddValidator(t *testing.T) { msgPubKey, _ := types1.NewAnyWithValue(pubKey) msgServer := NewMsgServerImpl(*poaKeeper) - tt := []struct{ - name string - authority string + tt := []struct { + name string + authority string validatorAddress string - expectedErr error + expectedErr error }{ { - name: "should fail - invalid authority address", - authority: "invalidauthority", + name: "should fail - invalid authority address", + authority: "invalidauthority", validatorAddress: "ethm1a0pd5cyew47pvgf7rd7axxy3humv9ev0nnkprp", - expectedErr: govtypes.ErrInvalidSigner, + expectedErr: govtypes.ErrInvalidSigner, }, { - name: "should fail - invalid validator address", - authority: poaKeeper.GetAuthority(), + name: "should fail - invalid validator address", + authority: poaKeeper.GetAuthority(), validatorAddress: "invalidvalidatoraddress", - expectedErr: errors.New("decoding bech32 failed"), + expectedErr: errors.New("decoding bech32 failed"), }, { - name: "should pass", - authority: poaKeeper.GetAuthority(), + name: "should pass", + authority: poaKeeper.GetAuthority(), validatorAddress: "ethm1a0pd5cyew47pvgf7rd7axxy3humv9ev0nnkprp", }, } @@ -70,5 +70,4 @@ func TestMsgServer_AddValidator(t *testing.T) { } }) } - } diff --git a/x/poa/keeper/msg_server_remove_validator_test.go b/x/poa/keeper/msg_server_remove_validator_test.go index 8561e9a..3064485 100644 --- a/x/poa/keeper/msg_server_remove_validator_test.go +++ b/x/poa/keeper/msg_server_remove_validator_test.go @@ -14,27 +14,27 @@ func TestMsgServer_RemoveValidator(t *testing.T) { msgServer := NewMsgServerImpl(*poaKeeper) - tt := []struct{ - name string - authority string + tt := []struct { + name string + authority string validatorAddress string - expectedErr error + expectedErr error }{ { - name: "should fail - invalid authority address", - authority: "invalidauthority", + name: "should fail - invalid authority address", + authority: "invalidauthority", validatorAddress: "ethm1a0pd5cyew47pvgf7rd7axxy3humv9ev0nnkprp", - expectedErr: govtypes.ErrInvalidSigner, + expectedErr: govtypes.ErrInvalidSigner, }, { - name: "should fail - invalid validator address", - authority: poaKeeper.GetAuthority(), + name: "should fail - invalid validator address", + authority: poaKeeper.GetAuthority(), validatorAddress: "invalidvalidatoraddress", - expectedErr: errors.New("decoding bech32 failed"), + expectedErr: errors.New("decoding bech32 failed"), }, { - name: "should pass", - authority: poaKeeper.GetAuthority(), + name: "should pass", + authority: poaKeeper.GetAuthority(), validatorAddress: "ethm1a0pd5cyew47pvgf7rd7axxy3humv9ev0nnkprp", }, } @@ -55,5 +55,4 @@ func TestMsgServer_RemoveValidator(t *testing.T) { } }) } - } From d6ca29677d81ac483330c69ce958d94f9f44823f Mon Sep 17 00:00:00 2001 From: GuillemGarciaDev Date: Wed, 15 Jan 2025 17:04:39 +0100 Subject: [PATCH 47/60] fix(x/poa): `ExecuteAddValidator` missing test cases --- x/poa/keeper/keeper_test.go | 109 +++++++++++++++++++++++++++--------- 1 file changed, 81 insertions(+), 28 deletions(-) diff --git a/x/poa/keeper/keeper_test.go b/x/poa/keeper/keeper_test.go index 13b08ab..7327b91 100644 --- a/x/poa/keeper/keeper_test.go +++ b/x/poa/keeper/keeper_test.go @@ -179,34 +179,58 @@ func TestKeeper_ExecuteAddValidator(t *testing.T) { }, slashingMocks: func(_ sdk.Context, _ *testutil.MockSlashingKeeper) {}, }, - // { - // name: "should fail - delegations are greater than 0 with error on GetValidator call", - // validatorAddress: "ethm1a0pd5cyew47pvgf7rd7axxy3humv9ev0nnkprp", - // expectedError: errors.New("staking validator error"), - // stakingMocks: func(ctx sdk.Context, stakingKeeper *testutil.MockStakingKeeper) { - // valAddr, _ := sdk.ValAddressFromBech32("ethm1a0pd5cyew47pvgf7rd7axxy3humv9ev0nnkprp") - // delAddr, _ := sdk.ValAddressFromBech32("ethm1dakgyqjulg29m5fmv992g2y66m9g2mjn6hahwg") - - // stakingKeeper.EXPECT().GetParams(ctx).Return(stakingtypes.Params{ - // BondDenom: "BND", - // }, nil) - // stakingKeeper.EXPECT().GetValidator(ctx, valAddr).Return(stakingtypes.Validator{Tokens: math.NewInt(0)}, nil) - // stakingKeeper.EXPECT().GetValidator(ctx, delAddr).Return(stakingtypes.Validator{}, errors.New("staking validator error")) - // stakingKeeper.EXPECT().GetAllDelegatorDelegations(ctx, gomock.Any()).Return([]stakingtypes.Delegation{ - // { - // ValidatorAddress: "ethm1a0pd5cyew47pvgf7rd7axxy3humv9ev0nnkprp", - // Shares: sdk.DefaultPowerReduction.ToLegacyDec(), - // }, - // }, nil) - // }, - // bankMocks: func(ctx sdk.Context, bankKeeper *testutil.MockBankKeeper) { - // bankKeeper.EXPECT().GetBalance(ctx, gomock.Any(), gomock.Any()).Return(sdk.Coin{ - // Denom: "BND", - // Amount: math.NewInt(0), - // }) - // }, - // slashingMocks: func(ctx sdk.Context, slashingKeeper *testutil.MockSlashingKeeper) {}, - // }, + { + name: "should fail - delegations are greater than 0 with error on GetValidator call", + validatorAddress: "ethm1a0pd5cyew47pvgf7rd7axxy3humv9ev0nnkprp", + expectedError: errors.New("staking validator error"), + stakingMocks: func(ctx sdk.Context, stakingKeeper *testutil.MockStakingKeeper) { + stakingKeeper.EXPECT().GetParams(ctx).Return(stakingtypes.Params{ + BondDenom: "BND", + }, nil) + stakingKeeper.EXPECT().GetValidator(ctx, gomock.Any()).Return(stakingtypes.Validator{Tokens: math.NewInt(0)}, nil).Times(1) + stakingKeeper.EXPECT().GetValidator(ctx, gomock.Any()).Return(stakingtypes.Validator{Tokens: math.NewInt(0)}, errors.New("staking validator error")).Times(1) + stakingKeeper.EXPECT().GetAllDelegatorDelegations(ctx, gomock.Any()).Return([]stakingtypes.Delegation{ + { + ValidatorAddress: "ethmvaloper1a0pd5cyew47pvgf7rd7axxy3humv9ev0urudmu", + DelegatorAddress: "ethm1a0pd5cyew47pvgf7rd7axxy3humv9ev0nnkprp", + Shares: sdk.DefaultPowerReduction.ToLegacyDec(), + }, + }, nil) + }, + bankMocks: func(ctx sdk.Context, bankKeeper *testutil.MockBankKeeper) { + bankKeeper.EXPECT().GetBalance(ctx, gomock.Any(), gomock.Any()).Return(sdk.Coin{ + Denom: "BND", + Amount: math.NewInt(0), + }) + }, + slashingMocks: func(ctx sdk.Context, slashingKeeper *testutil.MockSlashingKeeper) {}, + }, + { + name: "should fail - delegations are greater than 0 with delegated tokens", + validatorAddress: "ethm1a0pd5cyew47pvgf7rd7axxy3humv9ev0nnkprp", + expectedError: types.ErrAddressHasDelegatedTokens, + stakingMocks: func(ctx sdk.Context, stakingKeeper *testutil.MockStakingKeeper) { + stakingKeeper.EXPECT().GetParams(ctx).Return(stakingtypes.Params{ + BondDenom: "BND", + }, nil) + stakingKeeper.EXPECT().GetValidator(ctx, gomock.Any()).Return(stakingtypes.Validator{Tokens: math.NewInt(0)}, nil).Times(1) + stakingKeeper.EXPECT().GetValidator(ctx, gomock.Any()).Return(stakingtypes.Validator{Tokens: sdk.DefaultPowerReduction}, nil).Times(1) + stakingKeeper.EXPECT().GetAllDelegatorDelegations(ctx, gomock.Any()).Return([]stakingtypes.Delegation{ + { + ValidatorAddress: "ethmvaloper1a0pd5cyew47pvgf7rd7axxy3humv9ev0urudmu", + DelegatorAddress: "ethm1a0pd5cyew47pvgf7rd7axxy3humv9ev0nnkprp", + Shares: sdk.DefaultPowerReduction.ToLegacyDec(), + }, + }, nil) + }, + bankMocks: func(ctx sdk.Context, bankKeeper *testutil.MockBankKeeper) { + bankKeeper.EXPECT().GetBalance(ctx, gomock.Any(), gomock.Any()).Return(sdk.Coin{ + Denom: "BND", + Amount: math.NewInt(0), + }) + }, + slashingMocks: func(ctx sdk.Context, slashingKeeper *testutil.MockSlashingKeeper) {}, + }, { name: "should fail - GetUnbondingDelegationsFromValidator returns error", validatorAddress: "ethm1a0pd5cyew47pvgf7rd7axxy3humv9ev0nnkprp", @@ -320,6 +344,35 @@ func TestKeeper_ExecuteAddValidator(t *testing.T) { }, slashingMocks: func(_ sdk.Context, _ *testutil.MockSlashingKeeper) {}, }, + { + name: "should pass - validator not found when iterating over delegator delegations", + validatorAddress: "ethm1a0pd5cyew47pvgf7rd7axxy3humv9ev0nnkprp", + pubKey: msgPubKey, + stakingMocks: func(ctx sdk.Context, stakingKeeper *testutil.MockStakingKeeper) { + stakingKeeper.EXPECT().GetParams(ctx).Return(stakingtypes.Params{ + BondDenom: "BND", + }, nil) + stakingKeeper.EXPECT().GetValidator(ctx, gomock.Any()).Return(stakingtypes.Validator{Tokens: math.NewInt(0)}, nil).Times(1) + stakingKeeper.EXPECT().GetValidator(ctx, gomock.Any()).Return(stakingtypes.Validator{Tokens: math.NewInt(0)}, stakingtypes.ErrNoValidatorFound).Times(1) + stakingKeeper.EXPECT().GetAllDelegatorDelegations(ctx, gomock.Any()).Return([]stakingtypes.Delegation{ + { + ValidatorAddress: "ethmvaloper1a0pd5cyew47pvgf7rd7axxy3humv9ev0urudmu", + DelegatorAddress: "ethm1a0pd5cyew47pvgf7rd7axxy3humv9ev0nnkprp", + Shares: sdk.DefaultPowerReduction.ToLegacyDec(), + }, + }, nil) + stakingKeeper.EXPECT().GetUnbondingDelegationsFromValidator(ctx, gomock.Any()).Return([]stakingtypes.UnbondingDelegation{}, nil) + }, + bankMocks: func(ctx sdk.Context, bankKeeper *testutil.MockBankKeeper) { + bankKeeper.EXPECT().GetBalance(ctx, gomock.Any(), gomock.Any()).Return(sdk.Coin{ + Denom: "BND", + Amount: math.NewInt(0), + }) + bankKeeper.EXPECT().MintCoins(ctx, gomock.Any(), gomock.Any()).Return(nil) + bankKeeper.EXPECT().SendCoinsFromModuleToAccount(ctx, gomock.Any(), gomock.Any(), gomock.Any()).Return(nil) + }, + slashingMocks: func(ctx sdk.Context, slashingKeeper *testutil.MockSlashingKeeper) {}, + }, } for _, tc := range tt { From 53103972e9d890f4b029e26440e441733389d603 Mon Sep 17 00:00:00 2001 From: GuillemGarciaDev Date: Wed, 15 Jan 2025 17:09:29 +0100 Subject: [PATCH 48/60] fix(poa): test linting --- x/poa/keeper/keeper_test.go | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/x/poa/keeper/keeper_test.go b/x/poa/keeper/keeper_test.go index 7327b91..bdcffe6 100644 --- a/x/poa/keeper/keeper_test.go +++ b/x/poa/keeper/keeper_test.go @@ -180,9 +180,9 @@ func TestKeeper_ExecuteAddValidator(t *testing.T) { slashingMocks: func(_ sdk.Context, _ *testutil.MockSlashingKeeper) {}, }, { - name: "should fail - delegations are greater than 0 with error on GetValidator call", + name: "should fail - delegations are greater than 0 with error on GetValidator call", validatorAddress: "ethm1a0pd5cyew47pvgf7rd7axxy3humv9ev0nnkprp", - expectedError: errors.New("staking validator error"), + expectedError: errors.New("staking validator error"), stakingMocks: func(ctx sdk.Context, stakingKeeper *testutil.MockStakingKeeper) { stakingKeeper.EXPECT().GetParams(ctx).Return(stakingtypes.Params{ BondDenom: "BND", @@ -193,7 +193,7 @@ func TestKeeper_ExecuteAddValidator(t *testing.T) { { ValidatorAddress: "ethmvaloper1a0pd5cyew47pvgf7rd7axxy3humv9ev0urudmu", DelegatorAddress: "ethm1a0pd5cyew47pvgf7rd7axxy3humv9ev0nnkprp", - Shares: sdk.DefaultPowerReduction.ToLegacyDec(), + Shares: sdk.DefaultPowerReduction.ToLegacyDec(), }, }, nil) }, @@ -203,10 +203,10 @@ func TestKeeper_ExecuteAddValidator(t *testing.T) { Amount: math.NewInt(0), }) }, - slashingMocks: func(ctx sdk.Context, slashingKeeper *testutil.MockSlashingKeeper) {}, + slashingMocks: func(_ sdk.Context, _ *testutil.MockSlashingKeeper) {}, }, { - name: "should fail - delegations are greater than 0 with delegated tokens", + name: "should fail - delegations are greater than 0 with delegated tokens", validatorAddress: "ethm1a0pd5cyew47pvgf7rd7axxy3humv9ev0nnkprp", expectedError: types.ErrAddressHasDelegatedTokens, stakingMocks: func(ctx sdk.Context, stakingKeeper *testutil.MockStakingKeeper) { @@ -219,7 +219,7 @@ func TestKeeper_ExecuteAddValidator(t *testing.T) { { ValidatorAddress: "ethmvaloper1a0pd5cyew47pvgf7rd7axxy3humv9ev0urudmu", DelegatorAddress: "ethm1a0pd5cyew47pvgf7rd7axxy3humv9ev0nnkprp", - Shares: sdk.DefaultPowerReduction.ToLegacyDec(), + Shares: sdk.DefaultPowerReduction.ToLegacyDec(), }, }, nil) }, @@ -229,7 +229,7 @@ func TestKeeper_ExecuteAddValidator(t *testing.T) { Amount: math.NewInt(0), }) }, - slashingMocks: func(ctx sdk.Context, slashingKeeper *testutil.MockSlashingKeeper) {}, + slashingMocks: func(_ sdk.Context, _ *testutil.MockSlashingKeeper) {}, }, { name: "should fail - GetUnbondingDelegationsFromValidator returns error", @@ -345,7 +345,7 @@ func TestKeeper_ExecuteAddValidator(t *testing.T) { slashingMocks: func(_ sdk.Context, _ *testutil.MockSlashingKeeper) {}, }, { - name: "should pass - validator not found when iterating over delegator delegations", + name: "should pass - validator not found when iterating over delegator delegations", validatorAddress: "ethm1a0pd5cyew47pvgf7rd7axxy3humv9ev0nnkprp", pubKey: msgPubKey, stakingMocks: func(ctx sdk.Context, stakingKeeper *testutil.MockStakingKeeper) { @@ -358,7 +358,7 @@ func TestKeeper_ExecuteAddValidator(t *testing.T) { { ValidatorAddress: "ethmvaloper1a0pd5cyew47pvgf7rd7axxy3humv9ev0urudmu", DelegatorAddress: "ethm1a0pd5cyew47pvgf7rd7axxy3humv9ev0nnkprp", - Shares: sdk.DefaultPowerReduction.ToLegacyDec(), + Shares: sdk.DefaultPowerReduction.ToLegacyDec(), }, }, nil) stakingKeeper.EXPECT().GetUnbondingDelegationsFromValidator(ctx, gomock.Any()).Return([]stakingtypes.UnbondingDelegation{}, nil) @@ -371,7 +371,7 @@ func TestKeeper_ExecuteAddValidator(t *testing.T) { bankKeeper.EXPECT().MintCoins(ctx, gomock.Any(), gomock.Any()).Return(nil) bankKeeper.EXPECT().SendCoinsFromModuleToAccount(ctx, gomock.Any(), gomock.Any(), gomock.Any()).Return(nil) }, - slashingMocks: func(ctx sdk.Context, slashingKeeper *testutil.MockSlashingKeeper) {}, + slashingMocks: func(_ sdk.Context, _ *testutil.MockSlashingKeeper) {}, }, } From a44d73697ee674a0846ff4afa937d7a0ce3ad2b5 Mon Sep 17 00:00:00 2001 From: GuillemGarciaDev Date: Fri, 17 Jan 2025 09:35:32 +0100 Subject: [PATCH 49/60] chore: unit test coverage workflow --- .github/workflows/coverage.yml | 13 +++++++ .testcoverage.yml | 63 ++++++++++++++++++++++++++++++++++ Makefile | 6 ++++ 3 files changed, 82 insertions(+) create mode 100644 .github/workflows/coverage.yml create mode 100644 .testcoverage.yml diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml new file mode 100644 index 0000000..d757069 --- /dev/null +++ b/.github/workflows/coverage.yml @@ -0,0 +1,13 @@ +name: Coverage +runs-on: ubuntu-latest +steps: + - uses: actions/checkout@v3 + - uses: actions/setup-go@v3 + + - name: Generate coverage report for unit tests + run: make coverage-unit + + - name: Check coverage for unit tests + uses: vladopajic/go-test-coverage@v2 + with: + config: ./.testcoverage.yml \ No newline at end of file diff --git a/.testcoverage.yml b/.testcoverage.yml new file mode 100644 index 0000000..d5fdbce --- /dev/null +++ b/.testcoverage.yml @@ -0,0 +1,63 @@ +# (mandatory) +# Path to coverage profile file (output of `go test -coverprofile` command). +# +# For cases where there are many coverage profiles, such as when running +# unit tests and integration tests separately, you can combine all those +# profiles into one. In this case, the profile should have a comma-separated list +# of profile files, e.g., 'cover_unit.out,cover_integration.out'. +profile: cover.out + +# (optional; but recommended to set) +# When specified reported file paths will not contain local prefix in the output. +local-prefix: "github.com/xrplevm/node" + +# Holds coverage thresholds percentages, values should be in range [0-100]. +threshold: + # (optional; default 0) + # Minimum coverage percentage required for individual files. + file: 70 + + # (optional; default 0) + # Minimum coverage percentage required for each package. + package: 70 + + # (optional; default 0) + # Minimum overall project coverage percentage required. + total: 70 + +# Holds regexp rules which will override thresholds for matched files or packages +# using their paths. +# +# First rule from this list that matches file or package is going to apply +# new threshold to it. If project has multiple rules that match same path, +# override rules should be listed in order from specific to more general rules. +# override: + # Increase coverage threshold to 100% for `foo` package + # (default is 80, as configured above in this example). + # - path: ^pkg/lib/foo$ + # threshold: 100 + +# Holds regexp rules which will exclude matched files or packages +# from coverage statistics. +exclude: + # Exclude files or packages matching their paths + paths: + - cmd + - docs + - app + - tools + - tests + - "**/test_*.go" + - "**/*.pb.go" + - "**/*.pb.gw.go" + - "x/**/module.go" + - "x/**/testutil" + +# File name of go-test-coverage breakdown file, which can be used to +# analyze coverage difference. +breakdown-file-name: '' + +diff: + # File name of go-test-coverage breakdown file which will be used to + # report coverage difference. + base-breakdown-file-name: '' \ No newline at end of file diff --git a/Makefile b/Makefile index cd6b9c7..c15620f 100644 --- a/Makefile +++ b/Makefile @@ -118,6 +118,7 @@ lint-fix: ### Testing ### ############################################################################### EXCLUDED_POA_PACKAGES=$(shell go list ./x/poa/... | grep -v /x/poa/testutil | grep -v /x/poa/client | grep -v /x/poa/simulation | grep -v /x/poa/types) +EXCLUDED_UNIT_PACKAGES=$(shell go list ./... | grep -v tests | grep -v testutil | grep -v tools | grep -v app | grep -v docs | grep -v cmd | grep -v /x/poa/testutil | grep -v /x/poa/client | grep -v /x/poa/simulation | grep -v /x/poa/types) mocks: @echo "--> Installing mockgen" @@ -152,6 +153,11 @@ test-sim-full-app-fast: ### Coverage ### ############################################################################### +coverage-unit: + @echo "--> Running unit coverage" + @go test $(EXCLUDED_UNIT_PACKAGES) -coverprofile=coverage_unit.out > /dev/null + @go tool cover -func=coverage_unit.out + coverage-poa: @echo "--> Running POA coverage" @go test $(EXCLUDED_POA_PACKAGES) -coverprofile=coverage_poa.out > /dev/null From 61c5ee2f0271cc4daa0de5184b2afb4d6e4f5de1 Mon Sep 17 00:00:00 2001 From: GuillemGarciaDev Date: Fri, 17 Jan 2025 09:37:47 +0100 Subject: [PATCH 50/60] fix: coverage workflow to run on every pull-request --- .github/workflows/coverage.yml | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml index d757069..742a4fd 100644 --- a/.github/workflows/coverage.yml +++ b/.github/workflows/coverage.yml @@ -1,8 +1,15 @@ name: Coverage -runs-on: ubuntu-latest -steps: - - uses: actions/checkout@v3 - - uses: actions/setup-go@v3 + +on: + pull_request: + types: [opened, synchronize] + +jobs: + unit: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: actions/setup-go@v3 - name: Generate coverage report for unit tests run: make coverage-unit From a77bfed3d72c4cfe7122d8ba7354c256da7596ed Mon Sep 17 00:00:00 2001 From: GuillemGarciaDev Date: Fri, 17 Jan 2025 09:39:36 +0100 Subject: [PATCH 51/60] fix: move coverage worflow to pull-request.yml --- .github/workflows/coverage.yml | 20 -------------------- .github/workflows/pull-request.yml | 13 +++++++++++++ 2 files changed, 13 insertions(+), 20 deletions(-) delete mode 100644 .github/workflows/coverage.yml diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml deleted file mode 100644 index 742a4fd..0000000 --- a/.github/workflows/coverage.yml +++ /dev/null @@ -1,20 +0,0 @@ -name: Coverage - -on: - pull_request: - types: [opened, synchronize] - -jobs: - unit: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - - uses: actions/setup-go@v3 - - - name: Generate coverage report for unit tests - run: make coverage-unit - - - name: Check coverage for unit tests - uses: vladopajic/go-test-coverage@v2 - with: - config: ./.testcoverage.yml \ No newline at end of file diff --git a/.github/workflows/pull-request.yml b/.github/workflows/pull-request.yml index f048909..7b035d5 100644 --- a/.github/workflows/pull-request.yml +++ b/.github/workflows/pull-request.yml @@ -10,6 +10,19 @@ concurrency: cancel-in-progress: true jobs: + unit: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: actions/setup-go@v3 + + - name: Generate coverage report for unit tests + run: make coverage-unit + + - name: Check coverage for unit tests + uses: vladopajic/go-test-coverage@v2 + with: + config: ./.testcoverage.yml integration: runs-on: ubuntu-latest steps: From f8eacf52ec1b1eb95bd442e6a817892ae76cd41c Mon Sep 17 00:00:00 2001 From: GuillemGarciaDev Date: Fri, 17 Jan 2025 09:55:52 +0100 Subject: [PATCH 52/60] chore: fix coverage-unit workflow --- .github/workflows/pull-request.yml | 13 ------------- .github/workflows/unit-coverage.yml | 22 ++++++++++++++++++++++ .testcoverage.yml | 2 +- 3 files changed, 23 insertions(+), 14 deletions(-) create mode 100644 .github/workflows/unit-coverage.yml diff --git a/.github/workflows/pull-request.yml b/.github/workflows/pull-request.yml index 7b035d5..f048909 100644 --- a/.github/workflows/pull-request.yml +++ b/.github/workflows/pull-request.yml @@ -10,19 +10,6 @@ concurrency: cancel-in-progress: true jobs: - unit: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - - uses: actions/setup-go@v3 - - - name: Generate coverage report for unit tests - run: make coverage-unit - - - name: Check coverage for unit tests - uses: vladopajic/go-test-coverage@v2 - with: - config: ./.testcoverage.yml integration: runs-on: ubuntu-latest steps: diff --git a/.github/workflows/unit-coverage.yml b/.github/workflows/unit-coverage.yml new file mode 100644 index 0000000..783eca8 --- /dev/null +++ b/.github/workflows/unit-coverage.yml @@ -0,0 +1,22 @@ +name: XRPL-GO Coverage +on: + push: + branches: [ main ] + pull_request: + branches: [ '**' ] +jobs: + build: + name: Test Coverage + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: actions/setup-go@v3 + + - name: Generate unit test coverage + run: make coverage-unit + + - name: Check unit test coverage + uses: vladopajic/go-test-coverage@v2 + with: + # Configure action using config file (option 1) + config: ./.testcoverage.yml diff --git a/.testcoverage.yml b/.testcoverage.yml index d5fdbce..0304f19 100644 --- a/.testcoverage.yml +++ b/.testcoverage.yml @@ -5,7 +5,7 @@ # unit tests and integration tests separately, you can combine all those # profiles into one. In this case, the profile should have a comma-separated list # of profile files, e.g., 'cover_unit.out,cover_integration.out'. -profile: cover.out +profile: coverage_unit.out # (optional; but recommended to set) # When specified reported file paths will not contain local prefix in the output. From 6260b3e63c7f3e1ae4f153e867398a6c936b378b Mon Sep 17 00:00:00 2001 From: GuillemGarciaDev Date: Fri, 17 Jan 2025 10:08:31 +0100 Subject: [PATCH 53/60] fix: test coverage config --- .github/workflows/unit-coverage.yml | 4 ++-- .testcoverage.yml | 1 - 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/.github/workflows/unit-coverage.yml b/.github/workflows/unit-coverage.yml index 783eca8..cb7f555 100644 --- a/.github/workflows/unit-coverage.yml +++ b/.github/workflows/unit-coverage.yml @@ -1,4 +1,4 @@ -name: XRPL-GO Coverage +name: Coverage on: push: branches: [ main ] @@ -6,7 +6,7 @@ on: branches: [ '**' ] jobs: build: - name: Test Coverage + name: Unit test runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 diff --git a/.testcoverage.yml b/.testcoverage.yml index 0304f19..69d7b1d 100644 --- a/.testcoverage.yml +++ b/.testcoverage.yml @@ -47,7 +47,6 @@ exclude: - app - tools - tests - - "**/test_*.go" - "**/*.pb.go" - "**/*.pb.gw.go" - "x/**/module.go" From d09b8accaa0797845469ff8eb6f8d8088dfbc56d Mon Sep 17 00:00:00 2001 From: GuillemGarciaDev Date: Fri, 17 Jan 2025 10:13:25 +0100 Subject: [PATCH 54/60] fix: coverage config exclude paths --- .testcoverage.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.testcoverage.yml b/.testcoverage.yml index 69d7b1d..fb5d184 100644 --- a/.testcoverage.yml +++ b/.testcoverage.yml @@ -47,10 +47,10 @@ exclude: - app - tools - tests - - "**/*.pb.go" - - "**/*.pb.gw.go" - - "x/**/module.go" - - "x/**/testutil" + - \.pb\.go$ + - \.pb\.gw\.go$ + - \module\.go$ + - \testutil # File name of go-test-coverage breakdown file, which can be used to # analyze coverage difference. From 8573792e16d8a13ec9a5a3a5d59a0e0d53f19e7b Mon Sep 17 00:00:00 2001 From: GuillemGarciaDev Date: Fri, 17 Jan 2025 10:16:23 +0100 Subject: [PATCH 55/60] fix: coverage unit --- .testcoverage.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.testcoverage.yml b/.testcoverage.yml index fb5d184..3613be6 100644 --- a/.testcoverage.yml +++ b/.testcoverage.yml @@ -49,8 +49,8 @@ exclude: - tests - \.pb\.go$ - \.pb\.gw\.go$ - - \module\.go$ - - \testutil + - module\.go$ + - \/testutil\/ # File name of go-test-coverage breakdown file, which can be used to # analyze coverage difference. From c179183b4234af09c49fefc428967948766b8332 Mon Sep 17 00:00:00 2001 From: GuillemGarciaDev Date: Fri, 17 Jan 2025 12:01:29 +0100 Subject: [PATCH 56/60] feat: add `client` rule to linter --- .testcoverage.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.testcoverage.yml b/.testcoverage.yml index 3613be6..715b23e 100644 --- a/.testcoverage.yml +++ b/.testcoverage.yml @@ -49,8 +49,9 @@ exclude: - tests - \.pb\.go$ - \.pb\.gw\.go$ - - module\.go$ + - module.go$ - \/testutil\/ + - \/client\/ # File name of go-test-coverage breakdown file, which can be used to # analyze coverage difference. From f52bb86d3b3463cedbb6c1597c187997c94fc5f2 Mon Sep 17 00:00:00 2001 From: GuillemGarciaDev Date: Fri, 17 Jan 2025 12:07:16 +0100 Subject: [PATCH 57/60] fix: total coverage threshold --- .testcoverage.yml | 8 -------- 1 file changed, 8 deletions(-) diff --git a/.testcoverage.yml b/.testcoverage.yml index 715b23e..5434e75 100644 --- a/.testcoverage.yml +++ b/.testcoverage.yml @@ -13,14 +13,6 @@ local-prefix: "github.com/xrplevm/node" # Holds coverage thresholds percentages, values should be in range [0-100]. threshold: - # (optional; default 0) - # Minimum coverage percentage required for individual files. - file: 70 - - # (optional; default 0) - # Minimum coverage percentage required for each package. - package: 70 - # (optional; default 0) # Minimum overall project coverage percentage required. total: 70 From a660a075fc4d220a52d09cad125a378a65adfd50 Mon Sep 17 00:00:00 2001 From: GuillemGarciaDev Date: Tue, 21 Jan 2025 15:24:23 +0100 Subject: [PATCH 58/60] fix(tests, testutil): update version on `tests` and `testutil` packages --- tests/integration/network.go | 8 ++++---- tests/integration/poa_test.go | 4 ++-- tests/integration/suite.go | 10 +++++----- tests/upgrade/network.go | 8 ++++---- tests/upgrade/suite.go | 2 +- tests/upgrade/suite_test.go | 2 +- testutil/integration/common/factory/base.go | 4 ++-- testutil/integration/common/factory/factory.go | 4 ++-- testutil/integration/common/factory/fund.go | 2 +- testutil/integration/common/grpc/grpc.go | 2 +- testutil/integration/exrp/common/clients.go | 4 ++-- testutil/integration/exrp/common/config.go | 2 +- testutil/integration/exrp/common/setup.go | 2 +- testutil/integration/exrp/integration/config.go | 2 +- testutil/integration/exrp/integration/keepers.go | 2 +- testutil/integration/exrp/integration/network.go | 4 ++-- testutil/integration/exrp/integration/setup.go | 4 ++-- testutil/integration/exrp/integration/unit_network.go | 4 ++-- testutil/integration/exrp/upgrade/config.go | 2 +- testutil/integration/exrp/upgrade/keepers.go | 2 +- testutil/integration/exrp/upgrade/network.go | 4 ++-- testutil/integration/exrp/upgrade/unit_network.go | 4 ++-- testutil/integration/exrp/utils/gov.go | 6 +++--- 23 files changed, 44 insertions(+), 44 deletions(-) diff --git a/tests/integration/network.go b/tests/integration/network.go index 0287ac4..c71a799 100644 --- a/tests/integration/network.go +++ b/tests/integration/network.go @@ -11,10 +11,10 @@ import ( erc20types "github.com/evmos/evmos/v20/x/erc20/types" evmtypes "github.com/evmos/evmos/v20/x/evm/types" feemarkettypes "github.com/evmos/evmos/v20/x/feemarket/types" - commonnetwork "github.com/xrplevm/node/v5/testutil/integration/common/network" - exrpcommon "github.com/xrplevm/node/v5/testutil/integration/exrp/common" - exrpintegration "github.com/xrplevm/node/v5/testutil/integration/exrp/integration" - poatypes "github.com/xrplevm/node/v5/x/poa/types" + commonnetwork "github.com/xrplevm/node/v6/testutil/integration/common/network" + exrpcommon "github.com/xrplevm/node/v6/testutil/integration/exrp/common" + exrpintegration "github.com/xrplevm/node/v6/testutil/integration/exrp/integration" + poatypes "github.com/xrplevm/node/v6/x/poa/types" ) var _ commonnetwork.Network = (*Network)(nil) diff --git a/tests/integration/poa_test.go b/tests/integration/poa_test.go index 7c3e98f..c0f7906 100644 --- a/tests/integration/poa_test.go +++ b/tests/integration/poa_test.go @@ -15,8 +15,8 @@ import ( slashingtypes "github.com/cosmos/cosmos-sdk/x/slashing/types" stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" "github.com/stretchr/testify/require" - "github.com/xrplevm/node/v5/testutil/integration/exrp/utils" - poatypes "github.com/xrplevm/node/v5/x/poa/types" + "github.com/xrplevm/node/v6/testutil/integration/exrp/utils" + poatypes "github.com/xrplevm/node/v6/x/poa/types" ) // AddValidator tests diff --git a/tests/integration/suite.go b/tests/integration/suite.go index 6d921e0..fec241f 100644 --- a/tests/integration/suite.go +++ b/tests/integration/suite.go @@ -5,11 +5,11 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" evmtypes "github.com/evmos/evmos/v20/x/evm/types" "github.com/stretchr/testify/suite" - "github.com/xrplevm/node/v5/app" - factory "github.com/xrplevm/node/v5/testutil/integration/common/factory" - "github.com/xrplevm/node/v5/testutil/integration/common/grpc" - "github.com/xrplevm/node/v5/testutil/integration/common/keyring" - exrpcommon "github.com/xrplevm/node/v5/testutil/integration/exrp/common" + "github.com/xrplevm/node/v6/app" + factory "github.com/xrplevm/node/v6/testutil/integration/common/factory" + "github.com/xrplevm/node/v6/testutil/integration/common/grpc" + "github.com/xrplevm/node/v6/testutil/integration/common/keyring" + exrpcommon "github.com/xrplevm/node/v6/testutil/integration/exrp/common" ) type TestSuite struct { diff --git a/tests/upgrade/network.go b/tests/upgrade/network.go index a71a2d1..960cbab 100644 --- a/tests/upgrade/network.go +++ b/tests/upgrade/network.go @@ -10,10 +10,10 @@ import ( erc20types "github.com/evmos/evmos/v20/x/erc20/types" evmtypes "github.com/evmos/evmos/v20/x/evm/types" feemarkettypes "github.com/evmos/evmos/v20/x/feemarket/types" - commonnetwork "github.com/xrplevm/node/v5/testutil/integration/common/network" - exrpcommon "github.com/xrplevm/node/v5/testutil/integration/exrp/common" - upgradenetwork "github.com/xrplevm/node/v5/testutil/integration/exrp/upgrade" - poatypes "github.com/xrplevm/node/v5/x/poa/types" + commonnetwork "github.com/xrplevm/node/v6/testutil/integration/common/network" + exrpcommon "github.com/xrplevm/node/v6/testutil/integration/exrp/common" + upgradenetwork "github.com/xrplevm/node/v6/testutil/integration/exrp/upgrade" + poatypes "github.com/xrplevm/node/v6/x/poa/types" ) var _ commonnetwork.Network = (*UpgradeTestNetwork)(nil) diff --git a/tests/upgrade/suite.go b/tests/upgrade/suite.go index b95347c..ec76fc4 100644 --- a/tests/upgrade/suite.go +++ b/tests/upgrade/suite.go @@ -5,7 +5,7 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" "github.com/stretchr/testify/suite" - exrpupgrade "github.com/xrplevm/node/v5/testutil/integration/exrp/upgrade" + exrpupgrade "github.com/xrplevm/node/v6/testutil/integration/exrp/upgrade" ) const defaultStateFile = "upgrade-state.json" diff --git a/tests/upgrade/suite_test.go b/tests/upgrade/suite_test.go index 4de919c..a5513a2 100644 --- a/tests/upgrade/suite_test.go +++ b/tests/upgrade/suite_test.go @@ -6,7 +6,7 @@ import ( banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" "github.com/stretchr/testify/suite" - "github.com/xrplevm/node/v5/app" + "github.com/xrplevm/node/v6/app" ) func TestUpgradeTestSuite(t *testing.T) { diff --git a/testutil/integration/common/factory/base.go b/testutil/integration/common/factory/base.go index f77a22a..7239e67 100644 --- a/testutil/integration/common/factory/base.go +++ b/testutil/integration/common/factory/base.go @@ -12,8 +12,8 @@ import ( sdktypes "github.com/cosmos/cosmos-sdk/types" testutiltypes "github.com/cosmos/cosmos-sdk/types/module/testutil" authsigning "github.com/cosmos/cosmos-sdk/x/auth/signing" - "github.com/xrplevm/node/v5/testutil/integration/common/grpc" - "github.com/xrplevm/node/v5/testutil/integration/common/network" + "github.com/xrplevm/node/v6/testutil/integration/common/grpc" + "github.com/xrplevm/node/v6/testutil/integration/common/network" ) // BaseTxFactory is the interface that wraps the common methods to build and broadcast transactions diff --git a/testutil/integration/common/factory/factory.go b/testutil/integration/common/factory/factory.go index f56e130..c07d19f 100644 --- a/testutil/integration/common/factory/factory.go +++ b/testutil/integration/common/factory/factory.go @@ -4,8 +4,8 @@ package factory import ( - "github.com/xrplevm/node/v5/testutil/integration/common/grpc" - "github.com/xrplevm/node/v5/testutil/integration/common/network" + "github.com/xrplevm/node/v6/testutil/integration/common/grpc" + "github.com/xrplevm/node/v6/testutil/integration/common/network" ) const ( diff --git a/testutil/integration/common/factory/fund.go b/testutil/integration/common/factory/fund.go index 1928b37..b7aa778 100644 --- a/testutil/integration/common/factory/fund.go +++ b/testutil/integration/common/factory/fund.go @@ -8,7 +8,7 @@ import ( sdktypes "github.com/cosmos/cosmos-sdk/types" banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" - "github.com/xrplevm/node/v5/testutil/integration/common/keyring" + "github.com/xrplevm/node/v6/testutil/integration/common/keyring" ) // FundTxFactory is the interface that wraps the common methods to fund accounts diff --git a/testutil/integration/common/grpc/grpc.go b/testutil/integration/common/grpc/grpc.go index ecf0932..84423ec 100644 --- a/testutil/integration/common/grpc/grpc.go +++ b/testutil/integration/common/grpc/grpc.go @@ -10,7 +10,7 @@ import ( distrtypes "github.com/cosmos/cosmos-sdk/x/distribution/types" stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" feemarkettypes "github.com/evmos/evmos/v20/x/feemarket/types" - network "github.com/xrplevm/node/v5/testutil/integration/common/network" + network "github.com/xrplevm/node/v6/testutil/integration/common/network" ) // Handler is an interface that defines the common methods that are used to query diff --git a/testutil/integration/exrp/common/clients.go b/testutil/integration/exrp/common/clients.go index d1e2086..f10d2db 100644 --- a/testutil/integration/exrp/common/clients.go +++ b/testutil/integration/exrp/common/clients.go @@ -27,8 +27,8 @@ import ( evmtypes "github.com/evmos/evmos/v20/x/evm/types" feemarketkeeper "github.com/evmos/evmos/v20/x/feemarket/keeper" feemarkettypes "github.com/evmos/evmos/v20/x/feemarket/types" - poakeeper "github.com/xrplevm/node/v5/x/poa/keeper" - poatypes "github.com/xrplevm/node/v5/x/poa/types" + poakeeper "github.com/xrplevm/node/v6/x/poa/keeper" + poatypes "github.com/xrplevm/node/v6/x/poa/types" ) type NetworkKeepers interface { diff --git a/testutil/integration/exrp/common/config.go b/testutil/integration/exrp/common/config.go index 4a2ae7f..952d149 100644 --- a/testutil/integration/exrp/common/config.go +++ b/testutil/integration/exrp/common/config.go @@ -8,7 +8,7 @@ import ( sdktypes "github.com/cosmos/cosmos-sdk/types" banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" evmostypes "github.com/evmos/evmos/v20/types" - "github.com/xrplevm/node/v5/app" + "github.com/xrplevm/node/v6/app" ) const ( diff --git a/testutil/integration/exrp/common/setup.go b/testutil/integration/exrp/common/setup.go index 38bf227..fbb0d65 100644 --- a/testutil/integration/exrp/common/setup.go +++ b/testutil/integration/exrp/common/setup.go @@ -11,7 +11,7 @@ import ( dbm "github.com/cosmos/cosmos-db" simutils "github.com/cosmos/cosmos-sdk/testutil/sims" "github.com/cosmos/gogoproto/proto" - "github.com/xrplevm/node/v5/app" + "github.com/xrplevm/node/v6/app" authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" diff --git a/testutil/integration/exrp/integration/config.go b/testutil/integration/exrp/integration/config.go index 95d79e0..fb2b49b 100644 --- a/testutil/integration/exrp/integration/config.go +++ b/testutil/integration/exrp/integration/config.go @@ -11,7 +11,7 @@ import ( banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" testtx "github.com/evmos/evmos/v20/testutil/tx" - exrpcommon "github.com/xrplevm/node/v5/testutil/integration/exrp/common" + exrpcommon "github.com/xrplevm/node/v6/testutil/integration/exrp/common" ) // DefaultIntegrationConfig returns the default configuration for a chain. diff --git a/testutil/integration/exrp/integration/keepers.go b/testutil/integration/exrp/integration/keepers.go index 2cab201..067b6ff 100644 --- a/testutil/integration/exrp/integration/keepers.go +++ b/testutil/integration/exrp/integration/keepers.go @@ -12,7 +12,7 @@ import ( erc20keeper "github.com/evmos/evmos/v20/x/erc20/keeper" evmkeeper "github.com/evmos/evmos/v20/x/evm/keeper" feemarketkeeper "github.com/evmos/evmos/v20/x/feemarket/keeper" - poakeeper "github.com/xrplevm/node/v5/x/poa/keeper" + poakeeper "github.com/xrplevm/node/v6/x/poa/keeper" ) func (n *IntegrationNetwork) BankKeeper() bankkeeper.Keeper { diff --git a/testutil/integration/exrp/integration/network.go b/testutil/integration/exrp/integration/network.go index 85f6793..4470371 100644 --- a/testutil/integration/exrp/integration/network.go +++ b/testutil/integration/exrp/integration/network.go @@ -12,7 +12,7 @@ import ( sdkmath "cosmossdk.io/math" gethparams "github.com/ethereum/go-ethereum/params" - "github.com/xrplevm/node/v5/app" + "github.com/xrplevm/node/v6/app" "github.com/evmos/evmos/v20/types" @@ -27,7 +27,7 @@ import ( txtypes "github.com/cosmos/cosmos-sdk/types/tx" consensustypes "github.com/cosmos/cosmos-sdk/x/consensus/types" stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" - exrpcommon "github.com/xrplevm/node/v5/testutil/integration/exrp/common" + exrpcommon "github.com/xrplevm/node/v6/testutil/integration/exrp/common" ) // Network is the interface that wraps the methods to interact with integration test network. diff --git a/testutil/integration/exrp/integration/setup.go b/testutil/integration/exrp/integration/setup.go index c5b637b..c38417a 100644 --- a/testutil/integration/exrp/integration/setup.go +++ b/testutil/integration/exrp/integration/setup.go @@ -35,9 +35,9 @@ import ( infltypes "github.com/evmos/evmos/v20/x/inflation/v1/types" evmtypes "github.com/evmos/evmos/v20/x/evm/types" - exrpcommon "github.com/xrplevm/node/v5/testutil/integration/exrp/common" + exrpcommon "github.com/xrplevm/node/v6/testutil/integration/exrp/common" - "github.com/xrplevm/node/v5/app" + "github.com/xrplevm/node/v6/app" ) // genSetupFn is the type for the module genesis setup functions diff --git a/testutil/integration/exrp/integration/unit_network.go b/testutil/integration/exrp/integration/unit_network.go index 893b26e..74af9a1 100644 --- a/testutil/integration/exrp/integration/unit_network.go +++ b/testutil/integration/exrp/integration/unit_network.go @@ -7,8 +7,8 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/evmos/evmos/v20/x/evm/statedb" inflationtypes "github.com/evmos/evmos/v20/x/inflation/v1/types" - "github.com/xrplevm/node/v5/app" - exrpcommon "github.com/xrplevm/node/v5/testutil/integration/exrp/common" + "github.com/xrplevm/node/v6/app" + exrpcommon "github.com/xrplevm/node/v6/testutil/integration/exrp/common" ) // UnitTestIntegrationNetwork is the implementation of the Network interface for unit tests. diff --git a/testutil/integration/exrp/upgrade/config.go b/testutil/integration/exrp/upgrade/config.go index d05ddff..524423f 100644 --- a/testutil/integration/exrp/upgrade/config.go +++ b/testutil/integration/exrp/upgrade/config.go @@ -6,7 +6,7 @@ package exrpupgrade import ( "os" - exrpcommon "github.com/xrplevm/node/v5/testutil/integration/exrp/common" + exrpcommon "github.com/xrplevm/node/v6/testutil/integration/exrp/common" ) func DefaultUpgradeConfig() exrpcommon.Config { diff --git a/testutil/integration/exrp/upgrade/keepers.go b/testutil/integration/exrp/upgrade/keepers.go index 3bfd973..bf0d139 100644 --- a/testutil/integration/exrp/upgrade/keepers.go +++ b/testutil/integration/exrp/upgrade/keepers.go @@ -12,7 +12,7 @@ import ( erc20keeper "github.com/evmos/evmos/v20/x/erc20/keeper" evmkeeper "github.com/evmos/evmos/v20/x/evm/keeper" feemarketkeeper "github.com/evmos/evmos/v20/x/feemarket/keeper" - poakeeper "github.com/xrplevm/node/v5/x/poa/keeper" + poakeeper "github.com/xrplevm/node/v6/x/poa/keeper" ) func (n *UpgradeIntegrationNetwork) BankKeeper() bankkeeper.Keeper { diff --git a/testutil/integration/exrp/upgrade/network.go b/testutil/integration/exrp/upgrade/network.go index 3a64756..329f2c4 100644 --- a/testutil/integration/exrp/upgrade/network.go +++ b/testutil/integration/exrp/upgrade/network.go @@ -11,8 +11,8 @@ import ( "time" sdkmath "cosmossdk.io/math" - "github.com/xrplevm/node/v5/app" - exrpcommon "github.com/xrplevm/node/v5/testutil/integration/exrp/common" + "github.com/xrplevm/node/v6/app" + exrpcommon "github.com/xrplevm/node/v6/testutil/integration/exrp/common" abcitypes "github.com/cometbft/cometbft/abci/types" ed25519 "github.com/cometbft/cometbft/crypto/ed25519" diff --git a/testutil/integration/exrp/upgrade/unit_network.go b/testutil/integration/exrp/upgrade/unit_network.go index 0de1185..042a7a2 100644 --- a/testutil/integration/exrp/upgrade/unit_network.go +++ b/testutil/integration/exrp/upgrade/unit_network.go @@ -7,8 +7,8 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/evmos/evmos/v20/x/evm/statedb" inflationtypes "github.com/evmos/evmos/v20/x/inflation/v1/types" - "github.com/xrplevm/node/v5/app" - exrpcommon "github.com/xrplevm/node/v5/testutil/integration/exrp/common" + "github.com/xrplevm/node/v6/app" + exrpcommon "github.com/xrplevm/node/v6/testutil/integration/exrp/common" ) // UnitTestUpgradeNetwork is the implementation of the Network interface for unit tests. diff --git a/testutil/integration/exrp/utils/gov.go b/testutil/integration/exrp/utils/gov.go index 9340ce6..248ba28 100644 --- a/testutil/integration/exrp/utils/gov.go +++ b/testutil/integration/exrp/utils/gov.go @@ -16,9 +16,9 @@ import ( govtypes "github.com/cosmos/cosmos-sdk/x/gov/types" govv1 "github.com/cosmos/cosmos-sdk/x/gov/types/v1" govv1beta1 "github.com/cosmos/cosmos-sdk/x/gov/types/v1beta1" - commonfactory "github.com/xrplevm/node/v5/testutil/integration/common/factory" - "github.com/xrplevm/node/v5/testutil/integration/common/keyring" - "github.com/xrplevm/node/v5/testutil/integration/common/network" + commonfactory "github.com/xrplevm/node/v6/testutil/integration/common/factory" + "github.com/xrplevm/node/v6/testutil/integration/common/keyring" + "github.com/xrplevm/node/v6/testutil/integration/common/network" ) // SubmitProposal is a helper function to submit a governance proposal and From 401e25bd1f656f7187fd7c01dc440898b58eec7b Mon Sep 17 00:00:00 2001 From: AdriaCarrera Date: Tue, 18 Mar 2025 18:38:50 +0100 Subject: [PATCH 59/60] fix(tests): adapt main changes --- tests/integration/poa_test.go | 118 ++++++++-- tests/integration/suite.go | 1 + testutil/integration/exrp/common/config.go | 8 + .../integration/exrp/integration/network.go | 7 +- .../integration/exrp/integration/setup.go | 4 +- x/poa/ante/poa_test.go | 4 +- x/poa/keeper/keeper_test.go | 211 ++++++------------ .../msg_server_remove_validator_test.go | 4 +- 8 files changed, 198 insertions(+), 159 deletions(-) diff --git a/tests/integration/poa_test.go b/tests/integration/poa_test.go index c0f7906..16d5a75 100644 --- a/tests/integration/poa_test.go +++ b/tests/integration/poa_test.go @@ -495,14 +495,109 @@ func (s *TestSuite) TestAddValidator_ExistingValidator_Tombstoned() { } } +func (s *TestSuite) TestAddValidator_MaximumValidators() { + // Generate a random account + randomAccs := simtypes.RandomAccounts(rand.New(rand.NewSource(time.Now().UnixNano())), 1) //nolint:gosec + randomAcc := randomAccs[0] + randomValAddr := sdktypes.ValAddress(randomAcc.Address.Bytes()) + + tt := []struct { + name string + valAddress string + valPubKey cryptotypes.PubKey + expectedError error + beforeRun func() + afterRun func() + }{ + { + name: "add validator - maximum validators reached", + valAddress: randomAcc.Address.String(), + valPubKey: randomAcc.PubKey, + expectedError: poatypes.ErrMaxValidatorsReached, + beforeRun: func() { + resVal, err := s.Network().GetStakingClient().Params( + s.Network().GetContext(), + &stakingtypes.QueryParamsRequest{}, + ) + s.Require().NoError(err) + amountOfValidators := uint32(5) + maxValidators := resVal.Params.MaxValidators + authority := sdktypes.AccAddress(address.Module("gov")).String() + + for i := uint32(0); i < maxValidators-amountOfValidators; i++ { + randomValidator := simtypes.RandomAccounts(rand.New(rand.NewSource(time.Now().UnixNano())), 1) //nolint:gosec + randomValidatorAcc := randomValidator[0] + msg, err := poatypes.NewMsgAddValidator( + authority, + randomValidatorAcc.Address.String(), + randomValidatorAcc.ConsKey.PubKey(), + stakingtypes.Description{ + Moniker: "test", + }, + ) + proposal, err := utils.SubmitAndAwaitProposalResolution(s.factory, s.Network(), s.keyring.GetKeys(), "test", msg) + require.NoError(s.T(), err) + require.Equal(s.T(), govv1.ProposalStatus_PROPOSAL_STATUS_PASSED, proposal.Status) + } + }, + afterRun: func() { + // Check validator not added + _, err := s.Network().GetStakingClient().Validator( + s.Network().GetContext(), + &stakingtypes.QueryValidatorRequest{ + ValidatorAddr: randomValAddr.String(), + }, + ) + require.Error(s.T(), err) + }, + }, + } + + //nolint:dupl + for _, tc := range tt { + s.Run(tc.name, func() { + if tc.beforeRun != nil { + tc.beforeRun() + } + + authority := sdktypes.AccAddress(address.Module("gov")) + msg, err := poatypes.NewMsgAddValidator( + authority.String(), + tc.valAddress, + tc.valPubKey, + stakingtypes.Description{ + Moniker: "test", + }, + ) + require.NoError(s.T(), err) + + proposal, err := utils.SubmitAndAwaitProposalResolution(s.factory, s.Network(), s.keyring.GetKeys(), "test", msg) + require.NoError(s.T(), err) + + require.Equal(s.T(), govv1.ProposalStatus_PROPOSAL_STATUS_FAILED, proposal.Status) + require.Contains(s.T(), proposal.FailedReason, tc.expectedError.Error()) + + if tc.expectedError != nil && err != nil { + require.Error(s.T(), err) + require.ErrorIs(s.T(), err, tc.expectedError) + } else { + require.NoError(s.T(), err) + } + + if tc.afterRun != nil { + tc.afterRun() + } + }) + } +} + // RemoveValidator tests func (s *TestSuite) TestRemoveValidator_UnexistingValidator() { // Generate a random account randomAccs := simtypes.RandomAccounts(rand.New(rand.NewSource(time.Now().UnixNano())), 1) //nolint:gosec - randomAcc := randomAccs[0] - randomValAddr := sdktypes.ValAddress(randomAcc.Address.Bytes()) + randomValAddr := sdktypes.ValAddress(randomAccs[0].Address.Bytes()) tt := []struct { name string @@ -513,8 +608,8 @@ func (s *TestSuite) TestRemoveValidator_UnexistingValidator() { }{ { name: "remove unexisting validator - random address - with balance", - valAddress: randomAcc.Address.String(), - expectedError: poatypes.ErrAddressHasNoTokens, + valAddress: randomValAddr.String(), + expectedError: poatypes.ErrAddressIsNotAValidator, beforeRun: func() { _, err := s.Network().GetStakingClient().Validator( s.Network().GetContext(), @@ -580,7 +675,6 @@ func (s *TestSuite) TestRemoveValidator_ExistingValidator_StatusBonded() { validator := validators[0] valAddr, err := sdktypes.ValAddressFromBech32(validator.OperatorAddress) require.NoError(s.T(), err) - valAccAddr := sdktypes.AccAddress(valAddr) tt := []struct { name string @@ -591,7 +685,7 @@ func (s *TestSuite) TestRemoveValidator_ExistingValidator_StatusBonded() { }{ { name: "remove existing validator - status bonded", - valAddress: valAccAddr.String(), + valAddress: valAddr.String(), beforeRun: func() { resVal, err := s.Network().GetStakingClient().Validator( s.Network().GetContext(), @@ -663,7 +757,7 @@ func (s *TestSuite) TestRemoveValidator_ExistingValidator_StatusBonded() { authority := sdktypes.AccAddress(address.Module("gov")) msg := poatypes.NewMsgRemoveValidator( authority.String(), - valAccAddr.String(), + valAddr.String(), ) proposal, err := utils.SubmitAndAwaitProposalResolution(s.factory, s.Network(), s.keyring.GetKeys(), "test", msg) @@ -694,7 +788,6 @@ func (s *TestSuite) TestRemoveValidator_ExistingValidator_Jailed() { validator := validators[valIndex] valAddr, err := sdktypes.ValAddressFromBech32(validator.OperatorAddress) require.NoError(s.T(), err) - valAccAddr := sdktypes.AccAddress(valAddr) tt := []struct { name string @@ -705,7 +798,7 @@ func (s *TestSuite) TestRemoveValidator_ExistingValidator_Jailed() { }{ { name: "remove existing validator - jailed", - valAddress: valAccAddr.String(), + valAddress: valAddr.String(), beforeRun: func() { // Force jail validator valSet := s.Network().GetValidatorSet() @@ -777,7 +870,7 @@ func (s *TestSuite) TestRemoveValidator_ExistingValidator_Jailed() { authority := sdktypes.AccAddress(address.Module("gov")) msg := poatypes.NewMsgRemoveValidator( authority.String(), - valAccAddr.String(), + valAddr.String(), ) proposal, err := utils.SubmitAndAwaitProposalResolution(s.factory, s.Network(), s.keyring.GetKeys(), "test", msg) @@ -814,7 +907,6 @@ func (s *TestSuite) TestRemoveValidator_ExistingValidator_Tombstoned() { validator := validators[valIndex] valAddr, err := sdktypes.ValAddressFromBech32(validator.OperatorAddress) require.NoError(s.T(), err) - valAccAddr := sdktypes.AccAddress(valAddr) tt := []struct { name string @@ -825,7 +917,7 @@ func (s *TestSuite) TestRemoveValidator_ExistingValidator_Tombstoned() { }{ { name: "remove existing validator - tombstoned", - valAddress: valAccAddr.String(), + valAddress: valAddr.String(), beforeRun: func() { // Force validator to be tombstoned require.NoError(s.T(), s.Network().NextBlockWithMisBehaviors( @@ -921,7 +1013,7 @@ func (s *TestSuite) TestRemoveValidator_ExistingValidator_Tombstoned() { authority := sdktypes.AccAddress(address.Module("gov")) msg := poatypes.NewMsgRemoveValidator( authority.String(), - valAccAddr.String(), + valAddr.String(), ) proposal, err := utils.SubmitAndAwaitProposalResolution(s.factory, s.Network(), s.keyring.GetKeys(), "test", msg) diff --git a/tests/integration/suite.go b/tests/integration/suite.go index fec241f..662727d 100644 --- a/tests/integration/suite.go +++ b/tests/integration/suite.go @@ -47,6 +47,7 @@ func (s *TestSuite) SetupTest() { exrpcommon.WithAmountOfValidators(5), exrpcommon.WithCustomGenesis(customGenesis), exrpcommon.WithBondDenom("apoa"), + exrpcommon.WithMaxValidators(7), exrpcommon.WithMinDepositAmt(sdkmath.NewInt(1)), exrpcommon.WithValidatorOperators(kr.GetAllAccAddrs()), ) diff --git a/testutil/integration/exrp/common/config.go b/testutil/integration/exrp/common/config.go index 952d149..3e3527c 100644 --- a/testutil/integration/exrp/common/config.go +++ b/testutil/integration/exrp/common/config.go @@ -26,6 +26,7 @@ type Config struct { PreFundedAccounts []sdktypes.AccAddress Balances []banktypes.Balance BondDenom string + MaxValidators uint32 Denom string CustomGenesisState CustomGenesisState GenesisBytes []byte @@ -94,6 +95,13 @@ func WithBondDenom(denom string) ConfigOption { } } +// WithMaxValidators sets the max validators for the network. +func WithMaxValidators(maxValidators uint32) ConfigOption { + return func(cfg *Config) { + cfg.MaxValidators = maxValidators + } +} + // WithDenom sets the denom for the network. func WithDenom(denom string) ConfigOption { return func(cfg *Config) { diff --git a/testutil/integration/exrp/integration/network.go b/testutil/integration/exrp/integration/network.go index 4470371..1ebbe35 100644 --- a/testutil/integration/exrp/integration/network.go +++ b/testutil/integration/exrp/integration/network.go @@ -122,9 +122,10 @@ func (n *IntegrationNetwork) configureAndInitChain() error { exrpApp := exrpcommon.CreateExrpApp(n.cfg.ChainID, n.cfg.CustomBaseAppOpts...) stakingParams := StakingCustomGenesisState{ - denom: n.cfg.BondDenom, - validators: validators, - delegations: delegations, + denom: n.cfg.BondDenom, + maxValidators: n.cfg.MaxValidators, + validators: validators, + delegations: delegations, } govParams := GovCustomGenesisState{ denom: n.cfg.Denom, diff --git a/testutil/integration/exrp/integration/setup.go b/testutil/integration/exrp/integration/setup.go index c38417a..630c68d 100644 --- a/testutil/integration/exrp/integration/setup.go +++ b/testutil/integration/exrp/integration/setup.go @@ -278,7 +278,8 @@ func getValidatorsSlashingGen(validators []stakingtypes.Validator, sk slashingty // StakingCustomGenesisState defines the staking genesis state type StakingCustomGenesisState struct { - denom string + denom string + maxValidators uint32 validators []stakingtypes.Validator delegations []stakingtypes.Delegation @@ -289,6 +290,7 @@ func setDefaultStakingGenesisState(app *app.App, genesisState evmostypes.Genesis // Set staking params stakingParams := stakingtypes.DefaultParams() stakingParams.BondDenom = overwriteParams.denom + stakingParams.MaxValidators = overwriteParams.maxValidators stakingGenesis := stakingtypes.NewGenesisState( stakingParams, diff --git a/x/poa/ante/poa_test.go b/x/poa/ante/poa_test.go index d45f56c..3d0acd1 100644 --- a/x/poa/ante/poa_test.go +++ b/x/poa/ante/poa_test.go @@ -40,13 +40,15 @@ func TestPoaDecorator_AnteHandle(t *testing.T) { msgs: []sdk.Msg{ &stakingtypes.MsgUndelegate{}, &stakingtypes.MsgBeginRedelegate{}, + &stakingtypes.MsgDelegate{}, + &stakingtypes.MsgCancelUnbondingDelegation{}, }, expectedError: errors.New("tx type not allowed"), }, { name: "should not return error", msgs: []sdk.Msg{ - &stakingtypes.MsgDelegate{}, + &stakingtypes.MsgEditValidator{}, }, }, } diff --git a/x/poa/keeper/keeper_test.go b/x/poa/keeper/keeper_test.go index 2224f8f..c250435 100644 --- a/x/poa/keeper/keeper_test.go +++ b/x/poa/keeper/keeper_test.go @@ -62,7 +62,6 @@ func TestKeeper_ExecuteAddValidator(t *testing.T) { pubKey *types1.Any stakingMocks func(ctx sdk.Context, stakingKeeper *testutil.MockStakingKeeper) bankMocks func(ctx sdk.Context, bankKeeper *testutil.MockBankKeeper) - slashingMocks func(ctx sdk.Context, slashingKeeper *testutil.MockSlashingKeeper) expectedError error }{ { @@ -71,7 +70,6 @@ func TestKeeper_ExecuteAddValidator(t *testing.T) { expectedError: errors.New("decoding bech32 failed"), stakingMocks: func(_ sdk.Context, _ *testutil.MockStakingKeeper) {}, bankMocks: func(_ sdk.Context, _ *testutil.MockBankKeeper) {}, - slashingMocks: func(_ sdk.Context, _ *testutil.MockSlashingKeeper) {}, }, { name: "should fail - staking keeper returns error on GetParams", @@ -80,8 +78,20 @@ func TestKeeper_ExecuteAddValidator(t *testing.T) { stakingMocks: func(ctx sdk.Context, stakingKeeper *testutil.MockStakingKeeper) { stakingKeeper.EXPECT().GetParams(ctx).Return(stakingtypes.Params{}, errors.New("staking params error")) }, - bankMocks: func(_ sdk.Context, _ *testutil.MockBankKeeper) {}, - slashingMocks: func(_ sdk.Context, _ *testutil.MockSlashingKeeper) {}, + bankMocks: func(_ sdk.Context, _ *testutil.MockBankKeeper) {}, + }, + { + name: "should fail - maximum validators reached", + validatorAddress: "ethm1a0pd5cyew47pvgf7rd7axxy3humv9ev0nnkprp", + expectedError: types.ErrMaxValidatorsReached, + stakingMocks: func(ctx sdk.Context, stakingKeeper *testutil.MockStakingKeeper) { + stakingKeeper.EXPECT().GetParams(ctx).Return(stakingtypes.Params{ + BondDenom: "BND", + MaxValidators: 1, + }, nil) + stakingKeeper.EXPECT().GetAllValidators(ctx).Return([]stakingtypes.Validator{{}}, nil) + }, + bankMocks: func(ctx sdk.Context, bankKeeper *testutil.MockBankKeeper) {}, }, { name: "should fail - validator has bonded tokens", @@ -89,8 +99,10 @@ func TestKeeper_ExecuteAddValidator(t *testing.T) { expectedError: types.ErrAddressHasBankTokens, stakingMocks: func(ctx sdk.Context, stakingKeeper *testutil.MockStakingKeeper) { stakingKeeper.EXPECT().GetParams(ctx).Return(stakingtypes.Params{ - BondDenom: "BND", + BondDenom: "BND", + MaxValidators: 2, }, nil) + stakingKeeper.EXPECT().GetAllValidators(ctx).Return([]stakingtypes.Validator{}, nil) }, bankMocks: func(ctx sdk.Context, bankKeeper *testutil.MockBankKeeper) { bankKeeper.EXPECT().GetBalance(ctx, gomock.Any(), gomock.Any()).Return(sdk.Coin{ @@ -98,7 +110,6 @@ func TestKeeper_ExecuteAddValidator(t *testing.T) { Amount: sdk.DefaultPowerReduction, }) }, - slashingMocks: func(_ sdk.Context, _ *testutil.MockSlashingKeeper) {}, }, { name: "should fail - staking keeper returns error on GetValidator", @@ -106,8 +117,10 @@ func TestKeeper_ExecuteAddValidator(t *testing.T) { expectedError: errors.New("staking validator error"), stakingMocks: func(ctx sdk.Context, stakingKeeper *testutil.MockStakingKeeper) { stakingKeeper.EXPECT().GetParams(ctx).Return(stakingtypes.Params{ - BondDenom: "BND", + BondDenom: "BND", + MaxValidators: 2, }, nil) + stakingKeeper.EXPECT().GetAllValidators(ctx).Return([]stakingtypes.Validator{}, nil) stakingKeeper.EXPECT().GetValidator(ctx, gomock.Any()).Return(stakingtypes.Validator{}, errors.New("staking validator error")) }, bankMocks: func(ctx sdk.Context, bankKeeper *testutil.MockBankKeeper) { @@ -116,7 +129,6 @@ func TestKeeper_ExecuteAddValidator(t *testing.T) { Amount: math.NewInt(0), }) }, - slashingMocks: func(_ sdk.Context, _ *testutil.MockSlashingKeeper) {}, }, { name: "should fail - staking keeper returns validator with tokens", @@ -124,8 +136,10 @@ func TestKeeper_ExecuteAddValidator(t *testing.T) { expectedError: types.ErrAddressHasBondedTokens, stakingMocks: func(ctx sdk.Context, stakingKeeper *testutil.MockStakingKeeper) { stakingKeeper.EXPECT().GetParams(ctx).Return(stakingtypes.Params{ - BondDenom: "BND", + BondDenom: "BND", + MaxValidators: 2, }, nil) + stakingKeeper.EXPECT().GetAllValidators(ctx).Return([]stakingtypes.Validator{}, nil) stakingKeeper.EXPECT().GetValidator(ctx, gomock.Any()).Return(stakingtypes.Validator{Tokens: sdk.DefaultPowerReduction}, nil) }, bankMocks: func(ctx sdk.Context, bankKeeper *testutil.MockBankKeeper) { @@ -134,7 +148,6 @@ func TestKeeper_ExecuteAddValidator(t *testing.T) { Amount: math.NewInt(0), }) }, - slashingMocks: func(_ sdk.Context, _ *testutil.MockSlashingKeeper) {}, }, { name: "should fail - staking keeper returns error on GetAllDelegatorDelegations", @@ -142,8 +155,10 @@ func TestKeeper_ExecuteAddValidator(t *testing.T) { expectedError: errors.New("staking delegations error"), stakingMocks: func(ctx sdk.Context, stakingKeeper *testutil.MockStakingKeeper) { stakingKeeper.EXPECT().GetParams(ctx).Return(stakingtypes.Params{ - BondDenom: "BND", + BondDenom: "BND", + MaxValidators: 2, }, nil) + stakingKeeper.EXPECT().GetAllValidators(ctx).Return([]stakingtypes.Validator{}, nil) stakingKeeper.EXPECT().GetValidator(ctx, gomock.Any()).Return(stakingtypes.Validator{Tokens: math.NewInt(0)}, nil) stakingKeeper.EXPECT().GetAllDelegatorDelegations(ctx, gomock.Any()).Return([]stakingtypes.Delegation{}, errors.New("staking delegations error")) }, @@ -153,7 +168,6 @@ func TestKeeper_ExecuteAddValidator(t *testing.T) { Amount: math.NewInt(0), }) }, - slashingMocks: func(_ sdk.Context, _ *testutil.MockSlashingKeeper) {}, }, { name: "should fail - delegations are greater than 0 with invalid delegation validator address", @@ -161,8 +175,10 @@ func TestKeeper_ExecuteAddValidator(t *testing.T) { expectedError: errors.New("decoding bech32 failed"), stakingMocks: func(ctx sdk.Context, stakingKeeper *testutil.MockStakingKeeper) { stakingKeeper.EXPECT().GetParams(ctx).Return(stakingtypes.Params{ - BondDenom: "BND", + BondDenom: "BND", + MaxValidators: 2, }, nil) + stakingKeeper.EXPECT().GetAllValidators(ctx).Return([]stakingtypes.Validator{}, nil) stakingKeeper.EXPECT().GetValidator(ctx, gomock.Any()).Return(stakingtypes.Validator{Tokens: math.NewInt(0)}, nil) stakingKeeper.EXPECT().GetAllDelegatorDelegations(ctx, gomock.Any()).Return([]stakingtypes.Delegation{ { @@ -177,7 +193,6 @@ func TestKeeper_ExecuteAddValidator(t *testing.T) { Amount: math.NewInt(0), }) }, - slashingMocks: func(_ sdk.Context, _ *testutil.MockSlashingKeeper) {}, }, { name: "should fail - delegations are greater than 0 with error on GetValidator call", @@ -185,8 +200,10 @@ func TestKeeper_ExecuteAddValidator(t *testing.T) { expectedError: errors.New("staking validator error"), stakingMocks: func(ctx sdk.Context, stakingKeeper *testutil.MockStakingKeeper) { stakingKeeper.EXPECT().GetParams(ctx).Return(stakingtypes.Params{ - BondDenom: "BND", + BondDenom: "BND", + MaxValidators: 2, }, nil) + stakingKeeper.EXPECT().GetAllValidators(ctx).Return([]stakingtypes.Validator{}, nil) stakingKeeper.EXPECT().GetValidator(ctx, gomock.Any()).Return(stakingtypes.Validator{Tokens: math.NewInt(0)}, nil).Times(1) stakingKeeper.EXPECT().GetValidator(ctx, gomock.Any()).Return(stakingtypes.Validator{Tokens: math.NewInt(0)}, errors.New("staking validator error")).Times(1) stakingKeeper.EXPECT().GetAllDelegatorDelegations(ctx, gomock.Any()).Return([]stakingtypes.Delegation{ @@ -203,7 +220,6 @@ func TestKeeper_ExecuteAddValidator(t *testing.T) { Amount: math.NewInt(0), }) }, - slashingMocks: func(_ sdk.Context, _ *testutil.MockSlashingKeeper) {}, }, { name: "should fail - delegations are greater than 0 with delegated tokens", @@ -211,8 +227,10 @@ func TestKeeper_ExecuteAddValidator(t *testing.T) { expectedError: types.ErrAddressHasDelegatedTokens, stakingMocks: func(ctx sdk.Context, stakingKeeper *testutil.MockStakingKeeper) { stakingKeeper.EXPECT().GetParams(ctx).Return(stakingtypes.Params{ - BondDenom: "BND", + BondDenom: "BND", + MaxValidators: 2, }, nil) + stakingKeeper.EXPECT().GetAllValidators(ctx).Return([]stakingtypes.Validator{}, nil) stakingKeeper.EXPECT().GetValidator(ctx, gomock.Any()).Return(stakingtypes.Validator{Tokens: math.NewInt(0)}, nil).Times(1) stakingKeeper.EXPECT().GetValidator(ctx, gomock.Any()).Return(stakingtypes.Validator{Tokens: sdk.DefaultPowerReduction}, nil).Times(1) stakingKeeper.EXPECT().GetAllDelegatorDelegations(ctx, gomock.Any()).Return([]stakingtypes.Delegation{ @@ -229,7 +247,6 @@ func TestKeeper_ExecuteAddValidator(t *testing.T) { Amount: math.NewInt(0), }) }, - slashingMocks: func(_ sdk.Context, _ *testutil.MockSlashingKeeper) {}, }, { name: "should fail - GetUnbondingDelegationsFromValidator returns error", @@ -237,8 +254,10 @@ func TestKeeper_ExecuteAddValidator(t *testing.T) { expectedError: errors.New("staking unbonding delegations error"), stakingMocks: func(ctx sdk.Context, stakingKeeper *testutil.MockStakingKeeper) { stakingKeeper.EXPECT().GetParams(ctx).Return(stakingtypes.Params{ - BondDenom: "BND", + BondDenom: "BND", + MaxValidators: 2, }, nil) + stakingKeeper.EXPECT().GetAllValidators(ctx).Return([]stakingtypes.Validator{}, nil) stakingKeeper.EXPECT().GetValidator(ctx, gomock.Any()).Return(stakingtypes.Validator{Tokens: math.NewInt(0)}, nil) stakingKeeper.EXPECT().GetAllDelegatorDelegations(ctx, gomock.Any()).Return([]stakingtypes.Delegation{}, nil) stakingKeeper.EXPECT().GetUnbondingDelegationsFromValidator(ctx, gomock.Any()).Return([]stakingtypes.UnbondingDelegation{}, errors.New("staking unbonding delegations error")) @@ -249,7 +268,6 @@ func TestKeeper_ExecuteAddValidator(t *testing.T) { Amount: math.NewInt(0), }) }, - slashingMocks: func(_ sdk.Context, _ *testutil.MockSlashingKeeper) {}, }, { name: "should fail - unbonding delegations balances are greater than 0", @@ -257,8 +275,10 @@ func TestKeeper_ExecuteAddValidator(t *testing.T) { expectedError: types.ErrAddressHasUnbondingTokens, stakingMocks: func(ctx sdk.Context, stakingKeeper *testutil.MockStakingKeeper) { stakingKeeper.EXPECT().GetParams(ctx).Return(stakingtypes.Params{ - BondDenom: "BND", + BondDenom: "BND", + MaxValidators: 2, }, nil) + stakingKeeper.EXPECT().GetAllValidators(ctx).Return([]stakingtypes.Validator{}, nil) stakingKeeper.EXPECT().GetValidator(ctx, gomock.Any()).Return(stakingtypes.Validator{Tokens: math.NewInt(0)}, nil) stakingKeeper.EXPECT().GetAllDelegatorDelegations(ctx, gomock.Any()).Return([]stakingtypes.Delegation{}, nil) stakingKeeper.EXPECT().GetUnbondingDelegationsFromValidator(ctx, gomock.Any()).Return([]stakingtypes.UnbondingDelegation{ @@ -277,7 +297,6 @@ func TestKeeper_ExecuteAddValidator(t *testing.T) { Amount: math.NewInt(0), }) }, - slashingMocks: func(_ sdk.Context, _ *testutil.MockSlashingKeeper) {}, }, { name: "should fail - bank keeper MintCoins returns error", @@ -285,8 +304,10 @@ func TestKeeper_ExecuteAddValidator(t *testing.T) { expectedError: errors.New("bank mint coins error"), stakingMocks: func(ctx sdk.Context, stakingKeeper *testutil.MockStakingKeeper) { stakingKeeper.EXPECT().GetParams(ctx).Return(stakingtypes.Params{ - BondDenom: "BND", + BondDenom: "BND", + MaxValidators: 2, }, nil) + stakingKeeper.EXPECT().GetAllValidators(ctx).Return([]stakingtypes.Validator{}, nil) stakingKeeper.EXPECT().GetValidator(ctx, gomock.Any()).Return(stakingtypes.Validator{Tokens: math.NewInt(0)}, nil) stakingKeeper.EXPECT().GetAllDelegatorDelegations(ctx, gomock.Any()).Return([]stakingtypes.Delegation{}, nil) stakingKeeper.EXPECT().GetUnbondingDelegationsFromValidator(ctx, gomock.Any()).Return([]stakingtypes.UnbondingDelegation{}, nil) @@ -298,7 +319,6 @@ func TestKeeper_ExecuteAddValidator(t *testing.T) { }) bankKeeper.EXPECT().MintCoins(ctx, gomock.Any(), gomock.Any()).Return(errors.New("bank mint coins error")) }, - slashingMocks: func(_ sdk.Context, _ *testutil.MockSlashingKeeper) {}, }, { name: "should fail - bank keeper SendCoinsFromModuleToAccount returns error", @@ -306,8 +326,10 @@ func TestKeeper_ExecuteAddValidator(t *testing.T) { expectedError: errors.New("bank send coins from module to account error"), stakingMocks: func(ctx sdk.Context, stakingKeeper *testutil.MockStakingKeeper) { stakingKeeper.EXPECT().GetParams(ctx).Return(stakingtypes.Params{ - BondDenom: "BND", + BondDenom: "BND", + MaxValidators: 2, }, nil) + stakingKeeper.EXPECT().GetAllValidators(ctx).Return([]stakingtypes.Validator{}, nil) stakingKeeper.EXPECT().GetValidator(ctx, gomock.Any()).Return(stakingtypes.Validator{Tokens: math.NewInt(0)}, nil) stakingKeeper.EXPECT().GetAllDelegatorDelegations(ctx, gomock.Any()).Return([]stakingtypes.Delegation{}, nil) stakingKeeper.EXPECT().GetUnbondingDelegationsFromValidator(ctx, gomock.Any()).Return([]stakingtypes.UnbondingDelegation{}, nil) @@ -320,7 +342,6 @@ func TestKeeper_ExecuteAddValidator(t *testing.T) { bankKeeper.EXPECT().MintCoins(ctx, gomock.Any(), gomock.Any()).Return(nil) bankKeeper.EXPECT().SendCoinsFromModuleToAccount(ctx, gomock.Any(), gomock.Any(), gomock.Any()).Return(errors.New("bank send coins from module to account error")) }, - slashingMocks: func(_ sdk.Context, _ *testutil.MockSlashingKeeper) {}, }, { name: "should pass - MsgAddValidator", @@ -328,8 +349,10 @@ func TestKeeper_ExecuteAddValidator(t *testing.T) { pubKey: msgPubKey, stakingMocks: func(ctx sdk.Context, stakingKeeper *testutil.MockStakingKeeper) { stakingKeeper.EXPECT().GetParams(ctx).Return(stakingtypes.Params{ - BondDenom: "BND", + BondDenom: "BND", + MaxValidators: 2, }, nil) + stakingKeeper.EXPECT().GetAllValidators(ctx).Return([]stakingtypes.Validator{}, nil) stakingKeeper.EXPECT().GetValidator(ctx, gomock.Any()).Return(stakingtypes.Validator{Tokens: math.NewInt(0)}, nil) stakingKeeper.EXPECT().GetAllDelegatorDelegations(ctx, gomock.Any()).Return([]stakingtypes.Delegation{}, nil) stakingKeeper.EXPECT().GetUnbondingDelegationsFromValidator(ctx, gomock.Any()).Return([]stakingtypes.UnbondingDelegation{}, nil) @@ -342,7 +365,6 @@ func TestKeeper_ExecuteAddValidator(t *testing.T) { bankKeeper.EXPECT().MintCoins(ctx, gomock.Any(), gomock.Any()).Return(nil) bankKeeper.EXPECT().SendCoinsFromModuleToAccount(ctx, gomock.Any(), gomock.Any(), gomock.Any()).Return(nil) }, - slashingMocks: func(_ sdk.Context, _ *testutil.MockSlashingKeeper) {}, }, { name: "should pass - validator not found when iterating over delegator delegations", @@ -350,8 +372,10 @@ func TestKeeper_ExecuteAddValidator(t *testing.T) { pubKey: msgPubKey, stakingMocks: func(ctx sdk.Context, stakingKeeper *testutil.MockStakingKeeper) { stakingKeeper.EXPECT().GetParams(ctx).Return(stakingtypes.Params{ - BondDenom: "BND", + BondDenom: "BND", + MaxValidators: 2, }, nil) + stakingKeeper.EXPECT().GetAllValidators(ctx).Return([]stakingtypes.Validator{}, nil) stakingKeeper.EXPECT().GetValidator(ctx, gomock.Any()).Return(stakingtypes.Validator{Tokens: math.NewInt(0)}, nil).Times(1) stakingKeeper.EXPECT().GetValidator(ctx, gomock.Any()).Return(stakingtypes.Validator{Tokens: math.NewInt(0)}, stakingtypes.ErrNoValidatorFound).Times(1) stakingKeeper.EXPECT().GetAllDelegatorDelegations(ctx, gomock.Any()).Return([]stakingtypes.Delegation{ @@ -371,13 +395,12 @@ func TestKeeper_ExecuteAddValidator(t *testing.T) { bankKeeper.EXPECT().MintCoins(ctx, gomock.Any(), gomock.Any()).Return(nil) bankKeeper.EXPECT().SendCoinsFromModuleToAccount(ctx, gomock.Any(), gomock.Any(), gomock.Any()).Return(nil) }, - slashingMocks: func(_ sdk.Context, _ *testutil.MockSlashingKeeper) {}, }, } for _, tc := range tt { t.Run(tc.name, func(t *testing.T) { - keeper, ctx := setupPoaKeeper(t, tc.stakingMocks, tc.bankMocks, tc.slashingMocks) + keeper, ctx := setupPoaKeeper(t, tc.stakingMocks, tc.bankMocks) msg := &types.MsgAddValidator{ Authority: keeper.GetAuthority(), @@ -421,7 +444,7 @@ func TestKeeper_ExecuteRemoveValidator(t *testing.T) { }, { name: "should fail - staking keeper returns error on GetParams", - validatorAddress: "ethm1a0pd5cyew47pvgf7rd7axxy3humv9ev0nnkprp", + validatorAddress: "ethmvaloper1a0pd5cyew47pvgf7rd7axxy3humv9ev0urudmu", expectedError: errors.New("staking params error"), stakingMocks: func(ctx sdk.Context, stakingKeeper *testutil.MockStakingKeeper) { stakingKeeper.EXPECT().GetParams(ctx).Return(stakingtypes.Params{}, errors.New("staking params error")) @@ -429,78 +452,20 @@ func TestKeeper_ExecuteRemoveValidator(t *testing.T) { bankMocks: func(_ sdk.Context, _ *testutil.MockBankKeeper) {}, }, { - name: "should fail - bank keeper returns error on SendCoinsFromAccountToModule when validator has balance", - validatorAddress: "ethm1a0pd5cyew47pvgf7rd7axxy3humv9ev0nnkprp", - expectedError: errors.New("bank send coins from account to module error"), - stakingMocks: func(ctx sdk.Context, stakingKeeper *testutil.MockStakingKeeper) { - stakingKeeper.EXPECT().GetParams(ctx).Return(stakingtypes.Params{ - BondDenom: "BND", - }, nil) - }, - bankMocks: func(ctx sdk.Context, bankKeeper *testutil.MockBankKeeper) { - bankKeeper.EXPECT().GetBalance(ctx, gomock.Any(), gomock.Any()).Return(sdk.Coin{ - Denom: "BND", - Amount: sdk.DefaultPowerReduction, - }) - bankKeeper.EXPECT().SendCoinsFromAccountToModule(ctx, gomock.Any(), gomock.Any(), gomock.Any()).Return(errors.New("bank send coins from account to module error")) - }, - }, - { - name: "should fail - bank keeper returns error on BurnCoins when validator has balance", - validatorAddress: "ethm1a0pd5cyew47pvgf7rd7axxy3humv9ev0nnkprp", - expectedError: errors.New("bank burn coins error"), - stakingMocks: func(ctx sdk.Context, stakingKeeper *testutil.MockStakingKeeper) { - stakingKeeper.EXPECT().GetParams(ctx).Return(stakingtypes.Params{ - BondDenom: "BND", - }, nil) - }, - bankMocks: func(ctx sdk.Context, bankKeeper *testutil.MockBankKeeper) { - bankKeeper.EXPECT().GetBalance(ctx, gomock.Any(), gomock.Any()).Return(sdk.Coin{ - Denom: "BND", - Amount: sdk.DefaultPowerReduction, - }) - bankKeeper.EXPECT().SendCoinsFromAccountToModule(ctx, gomock.Any(), gomock.Any(), gomock.Any()).Return(nil) - bankKeeper.EXPECT().BurnCoins(ctx, gomock.Any(), gomock.Any()).Return(errors.New("bank burn coins error")) - }, - }, - { - name: "should fail - staking keeper returns error on GetValidator with balance", - validatorAddress: "ethm1a0pd5cyew47pvgf7rd7axxy3humv9ev0nnkprp", - stakingMocks: func(ctx sdk.Context, stakingKeeper *testutil.MockStakingKeeper) { - stakingKeeper.EXPECT().GetParams(ctx).Return(stakingtypes.Params{ - BondDenom: "BND", - }, nil) - stakingKeeper.EXPECT().GetValidator(ctx, gomock.Any()).Return(stakingtypes.Validator{}, errors.New("staking keeper get validator error")) - }, - bankMocks: func(ctx sdk.Context, bankKeeper *testutil.MockBankKeeper) { - bankKeeper.EXPECT().GetBalance(ctx, gomock.Any(), gomock.Any()).Return(sdk.Coin{ - Denom: "BND", - Amount: sdk.DefaultPowerReduction, - }) - bankKeeper.EXPECT().SendCoinsFromAccountToModule(ctx, gomock.Any(), gomock.Any(), gomock.Any()).Return(nil) - bankKeeper.EXPECT().BurnCoins(ctx, gomock.Any(), gomock.Any()).Return(nil) - }, - }, - { - name: "should fail - staking keeper returns error on GetValidator with no balance", - validatorAddress: "ethm1a0pd5cyew47pvgf7rd7axxy3humv9ev0nnkprp", - expectedError: types.ErrAddressHasNoTokens, + name: "should fail - staking keeper returns error on GetValidator", + expectedError: types.ErrAddressIsNotAValidator, + validatorAddress: "ethmvaloper1a0pd5cyew47pvgf7rd7axxy3humv9ev0urudmu", stakingMocks: func(ctx sdk.Context, stakingKeeper *testutil.MockStakingKeeper) { stakingKeeper.EXPECT().GetParams(ctx).Return(stakingtypes.Params{ BondDenom: "BND", }, nil) stakingKeeper.EXPECT().GetValidator(ctx, gomock.Any()).Return(stakingtypes.Validator{}, errors.New("staking keeper get validator error")) }, - bankMocks: func(ctx sdk.Context, bankKeeper *testutil.MockBankKeeper) { - bankKeeper.EXPECT().GetBalance(ctx, gomock.Any(), gomock.Any()).Return(sdk.Coin{ - Denom: "BND", - Amount: math.NewInt(0), - }) - }, + bankMocks: func(ctx sdk.Context, bankKeeper *testutil.MockBankKeeper) {}, }, { name: "should fail - staking keeper returns error on call GetUnbondingDelegationsFromValidator", - validatorAddress: "ethm1a0pd5cyew47pvgf7rd7axxy3humv9ev0nnkprp", + validatorAddress: "ethmvaloper1a0pd5cyew47pvgf7rd7axxy3humv9ev0urudmu", expectedError: errors.New("staking keeper get unbonding delegations from validator error"), stakingMocks: func(ctx sdk.Context, stakingKeeper *testutil.MockStakingKeeper) { stakingKeeper.EXPECT().GetParams(ctx).Return(stakingtypes.Params{ @@ -513,16 +478,11 @@ func TestKeeper_ExecuteRemoveValidator(t *testing.T) { hooks.EXPECT().BeforeValidatorModified(ctx, gomock.Any()).Return(errors.New("staking keeper hooks error")) stakingKeeper.EXPECT().Hooks().Return(hooks) }, - bankMocks: func(ctx sdk.Context, bankKeeper *testutil.MockBankKeeper) { - bankKeeper.EXPECT().GetBalance(ctx, gomock.Any(), gomock.Any()).Return(sdk.Coin{ - Denom: "BND", - Amount: math.NewInt(0), - }) - }, + bankMocks: func(ctx sdk.Context, bankKeeper *testutil.MockBankKeeper) {}, }, { name: "should fail - staking keeper returns error on call SlashUnbondingDelegation", - validatorAddress: "ethm1a0pd5cyew47pvgf7rd7axxy3humv9ev0nnkprp", + validatorAddress: "ethmvaloper1a0pd5cyew47pvgf7rd7axxy3humv9ev0urudmu", expectedError: errors.New("staking keeper slash unbonding delegation error"), stakingMocks: func(ctx sdk.Context, stakingKeeper *testutil.MockStakingKeeper) { stakingKeeper.EXPECT().GetParams(ctx).Return(stakingtypes.Params{ @@ -532,7 +492,7 @@ func TestKeeper_ExecuteRemoveValidator(t *testing.T) { stakingKeeper.EXPECT().GetUnbondingDelegationsFromValidator(ctx, gomock.Any()).Return( []stakingtypes.UnbondingDelegation{ { - ValidatorAddress: "ethm1a0pd5cyew47pvgf7rd7axxy3humv9ev0nnkprp", + ValidatorAddress: "ethmvaloper1a0pd5cyew47pvgf7rd7axxy3humv9ev0urudmu", }, }, nil) @@ -543,16 +503,11 @@ func TestKeeper_ExecuteRemoveValidator(t *testing.T) { stakingKeeper.EXPECT().SlashUnbondingDelegation(ctx, gomock.Any(), gomock.Any(), gomock.Any()).Return( math.NewInt(0), errors.New("staking keeper slash unbonding delegation error")) }, - bankMocks: func(ctx sdk.Context, bankKeeper *testutil.MockBankKeeper) { - bankKeeper.EXPECT().GetBalance(ctx, gomock.Any(), gomock.Any()).Return(sdk.Coin{ - Denom: "BND", - Amount: math.NewInt(0), - }) - }, + bankMocks: func(ctx sdk.Context, bankKeeper *testutil.MockBankKeeper) {}, }, { name: "should fail - staking keeper returns error on RemoveValidatorTokens call", - validatorAddress: "ethm1a0pd5cyew47pvgf7rd7axxy3humv9ev0nnkprp", + validatorAddress: "ethmvaloper1a0pd5cyew47pvgf7rd7axxy3humv9ev0urudmu", expectedError: errors.New("staking keeper remove validator tokens error"), stakingMocks: func(ctx sdk.Context, stakingKeeper *testutil.MockStakingKeeper) { stakingKeeper.EXPECT().GetParams(ctx).Return(stakingtypes.Params{ @@ -574,17 +529,12 @@ func TestKeeper_ExecuteRemoveValidator(t *testing.T) { stakingtypes.Validator{}, errors.New("staking keeper remove validator tokens error"), ) }, - bankMocks: func(ctx sdk.Context, bankKeeper *testutil.MockBankKeeper) { - bankKeeper.EXPECT().GetBalance(ctx, gomock.Any(), gomock.Any()).Return(sdk.Coin{ - Denom: "BND", - Amount: math.NewInt(0), - }) - }, + bankMocks: func(ctx sdk.Context, bankKeeper *testutil.MockBankKeeper) {}, }, //nolint:dupl { name: "should fail - bank keeper returns error on call BurnCoins for status bonded", - validatorAddress: "ethm1a0pd5cyew47pvgf7rd7axxy3humv9ev0nnkprp", + validatorAddress: "ethmvaloper1a0pd5cyew47pvgf7rd7axxy3humv9ev0urudmu", expectedError: errors.New("bank keeper burn coins error"), //nolint:dupl stakingMocks: func(ctx sdk.Context, stakingKeeper *testutil.MockStakingKeeper) { @@ -609,17 +559,13 @@ func TestKeeper_ExecuteRemoveValidator(t *testing.T) { ) }, bankMocks: func(ctx sdk.Context, bankKeeper *testutil.MockBankKeeper) { - bankKeeper.EXPECT().GetBalance(ctx, gomock.Any(), gomock.Any()).Return(sdk.Coin{ - Denom: "BND", - Amount: math.NewInt(0), - }) bankKeeper.EXPECT().BurnCoins(ctx, gomock.Any(), gomock.Any()).Return(errors.New("bank keeper burn coins error")) }, }, //nolint:dupl { name: "should fail - bank keeper returns error on call BurnCoins for status unbonding/unbonded", - validatorAddress: "ethm1a0pd5cyew47pvgf7rd7axxy3humv9ev0nnkprp", + validatorAddress: "ethmvaloper1a0pd5cyew47pvgf7rd7axxy3humv9ev0urudmu", expectedError: errors.New("bank keeper burn coins error"), //nolint:dupl stakingMocks: func(ctx sdk.Context, stakingKeeper *testutil.MockStakingKeeper) { @@ -644,16 +590,12 @@ func TestKeeper_ExecuteRemoveValidator(t *testing.T) { ) }, bankMocks: func(ctx sdk.Context, bankKeeper *testutil.MockBankKeeper) { - bankKeeper.EXPECT().GetBalance(ctx, gomock.Any(), gomock.Any()).Return(sdk.Coin{ - Denom: "BND", - Amount: math.NewInt(0), - }) bankKeeper.EXPECT().BurnCoins(ctx, gomock.Any(), gomock.Any()).Return(errors.New("bank keeper burn coins error")) }, }, { name: "should fail - bank keeper returns error for invalid validator status", - validatorAddress: "ethm1a0pd5cyew47pvgf7rd7axxy3humv9ev0nnkprp", + validatorAddress: "ethmvaloper1a0pd5cyew47pvgf7rd7axxy3humv9ev0urudmu", expectedError: types.ErrInvalidValidatorStatus, //nolint:dupl stakingMocks: func(ctx sdk.Context, stakingKeeper *testutil.MockStakingKeeper) { @@ -677,16 +619,11 @@ func TestKeeper_ExecuteRemoveValidator(t *testing.T) { }, nil, ) }, - bankMocks: func(ctx sdk.Context, bankKeeper *testutil.MockBankKeeper) { - bankKeeper.EXPECT().GetBalance(ctx, gomock.Any(), gomock.Any()).Return(sdk.Coin{ - Denom: "BND", - Amount: math.NewInt(0), - }) - }, + bankMocks: func(ctx sdk.Context, bankKeeper *testutil.MockBankKeeper) {}, }, { name: "should fail - staking keeper returns error on call Unbond", - validatorAddress: "ethm1a0pd5cyew47pvgf7rd7axxy3humv9ev0nnkprp", + validatorAddress: "ethmvaloper1a0pd5cyew47pvgf7rd7axxy3humv9ev0urudmu", expectedError: errors.New("staking keeper unbond error"), stakingMocks: func(ctx sdk.Context, stakingKeeper *testutil.MockStakingKeeper) { stakingKeeper.EXPECT().GetParams(ctx).Return(stakingtypes.Params{ @@ -714,10 +651,6 @@ func TestKeeper_ExecuteRemoveValidator(t *testing.T) { ) }, bankMocks: func(ctx sdk.Context, bankKeeper *testutil.MockBankKeeper) { - bankKeeper.EXPECT().GetBalance(ctx, gomock.Any(), gomock.Any()).Return(sdk.Coin{ - Denom: "BND", - Amount: math.NewInt(0), - }) bankKeeper.EXPECT().BurnCoins(ctx, gomock.Any(), gomock.Any()).Return(nil) }, }, @@ -725,7 +658,7 @@ func TestKeeper_ExecuteRemoveValidator(t *testing.T) { for _, tc := range tt { t.Run(tc.name, func(t *testing.T) { - keeper, ctx := setupPoaKeeper(t, tc.stakingMocks, tc.bankMocks, func(_ sdk.Context, _ *testutil.MockSlashingKeeper) {}) + keeper, ctx := setupPoaKeeper(t, tc.stakingMocks, tc.bankMocks) err := keeper.ExecuteRemoveValidator(ctx, tc.validatorAddress) if tc.expectedError != nil { diff --git a/x/poa/keeper/msg_server_remove_validator_test.go b/x/poa/keeper/msg_server_remove_validator_test.go index 7ee1943..32d0b5b 100644 --- a/x/poa/keeper/msg_server_remove_validator_test.go +++ b/x/poa/keeper/msg_server_remove_validator_test.go @@ -23,7 +23,7 @@ func TestMsgServer_RemoveValidator(t *testing.T) { { name: "should fail - invalid authority address", authority: "invalidauthority", - validatorAddress: "ethm1a0pd5cyew47pvgf7rd7axxy3humv9ev0nnkprp", + validatorAddress: "ethmvaloper1a0pd5cyew47pvgf7rd7axxy3humv9ev0urudmu", expectedErr: govtypes.ErrInvalidSigner, }, { @@ -35,7 +35,7 @@ func TestMsgServer_RemoveValidator(t *testing.T) { { name: "should pass", authority: poaKeeper.GetAuthority(), - validatorAddress: "ethm1a0pd5cyew47pvgf7rd7axxy3humv9ev0nnkprp", + validatorAddress: "ethmvaloper1a0pd5cyew47pvgf7rd7axxy3humv9ev0urudmu", }, } From 9108862521a7b55a98d656b908682f4502e467b6 Mon Sep 17 00:00:00 2001 From: AdriaCarrera Date: Tue, 18 Mar 2025 18:42:02 +0100 Subject: [PATCH 60/60] fix(tests): adapt main changes --- tests/integration/poa_test.go | 1 + x/poa/keeper/keeper_test.go | 12 ++++++------ 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/tests/integration/poa_test.go b/tests/integration/poa_test.go index 16d5a75..0c8f8c2 100644 --- a/tests/integration/poa_test.go +++ b/tests/integration/poa_test.go @@ -535,6 +535,7 @@ func (s *TestSuite) TestAddValidator_MaximumValidators() { Moniker: "test", }, ) + require.NoError(s.T(), err) proposal, err := utils.SubmitAndAwaitProposalResolution(s.factory, s.Network(), s.keyring.GetKeys(), "test", msg) require.NoError(s.T(), err) require.Equal(s.T(), govv1.ProposalStatus_PROPOSAL_STATUS_PASSED, proposal.Status) diff --git a/x/poa/keeper/keeper_test.go b/x/poa/keeper/keeper_test.go index c250435..4858da6 100644 --- a/x/poa/keeper/keeper_test.go +++ b/x/poa/keeper/keeper_test.go @@ -91,7 +91,7 @@ func TestKeeper_ExecuteAddValidator(t *testing.T) { }, nil) stakingKeeper.EXPECT().GetAllValidators(ctx).Return([]stakingtypes.Validator{{}}, nil) }, - bankMocks: func(ctx sdk.Context, bankKeeper *testutil.MockBankKeeper) {}, + bankMocks: func(_ sdk.Context, _ *testutil.MockBankKeeper) {}, }, { name: "should fail - validator has bonded tokens", @@ -461,7 +461,7 @@ func TestKeeper_ExecuteRemoveValidator(t *testing.T) { }, nil) stakingKeeper.EXPECT().GetValidator(ctx, gomock.Any()).Return(stakingtypes.Validator{}, errors.New("staking keeper get validator error")) }, - bankMocks: func(ctx sdk.Context, bankKeeper *testutil.MockBankKeeper) {}, + bankMocks: func(_ sdk.Context, _ *testutil.MockBankKeeper) {}, }, { name: "should fail - staking keeper returns error on call GetUnbondingDelegationsFromValidator", @@ -478,7 +478,7 @@ func TestKeeper_ExecuteRemoveValidator(t *testing.T) { hooks.EXPECT().BeforeValidatorModified(ctx, gomock.Any()).Return(errors.New("staking keeper hooks error")) stakingKeeper.EXPECT().Hooks().Return(hooks) }, - bankMocks: func(ctx sdk.Context, bankKeeper *testutil.MockBankKeeper) {}, + bankMocks: func(_ sdk.Context, _ *testutil.MockBankKeeper) {}, }, { name: "should fail - staking keeper returns error on call SlashUnbondingDelegation", @@ -503,7 +503,7 @@ func TestKeeper_ExecuteRemoveValidator(t *testing.T) { stakingKeeper.EXPECT().SlashUnbondingDelegation(ctx, gomock.Any(), gomock.Any(), gomock.Any()).Return( math.NewInt(0), errors.New("staking keeper slash unbonding delegation error")) }, - bankMocks: func(ctx sdk.Context, bankKeeper *testutil.MockBankKeeper) {}, + bankMocks: func(_ sdk.Context, _ *testutil.MockBankKeeper) {}, }, { name: "should fail - staking keeper returns error on RemoveValidatorTokens call", @@ -529,7 +529,7 @@ func TestKeeper_ExecuteRemoveValidator(t *testing.T) { stakingtypes.Validator{}, errors.New("staking keeper remove validator tokens error"), ) }, - bankMocks: func(ctx sdk.Context, bankKeeper *testutil.MockBankKeeper) {}, + bankMocks: func(_ sdk.Context, _ *testutil.MockBankKeeper) {}, }, //nolint:dupl { @@ -619,7 +619,7 @@ func TestKeeper_ExecuteRemoveValidator(t *testing.T) { }, nil, ) }, - bankMocks: func(ctx sdk.Context, bankKeeper *testutil.MockBankKeeper) {}, + bankMocks: func(_ sdk.Context, _ *testutil.MockBankKeeper) {}, }, { name: "should fail - staking keeper returns error on call Unbond",