Some checks failed
CodeQL / Analyze (push) Waiting to run
golangci-lint / lint (push) Waiting to run
Tests / Code Coverage / build (amd64) (push) Waiting to run
Tests / Code Coverage / build (arm64) (push) Waiting to run
Tests / Code Coverage / unit-tests (map[additional-args:-tags="test_e2e" name:e2e path:./e2e]) (push) Waiting to run
Tests / Code Coverage / unit-tests (map[name:08-wasm path:./modules/light-clients/08-wasm]) (push) Waiting to run
Tests / Code Coverage / unit-tests (map[name:ibc-go path:.]) (push) Waiting to run
Docker Build & Push Simapp (main) / docker-build (push) Has been cancelled
1098 lines
34 KiB
Go
1098 lines
34 KiB
Go
package testsuite
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/json"
|
|
"errors"
|
|
"fmt"
|
|
"os"
|
|
"path"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/cosmos/interchaintest/v10"
|
|
"github.com/cosmos/interchaintest/v10/ibc"
|
|
interchaintestutil "github.com/cosmos/interchaintest/v10/testutil"
|
|
"gopkg.in/yaml.v2"
|
|
|
|
"git.cw.tr/mukan-network/mukan-sdk/codec"
|
|
sdk "git.cw.tr/mukan-network/mukan-sdk/types"
|
|
"git.cw.tr/mukan-network/mukan-sdk/types/module/testutil"
|
|
genutiltypes "git.cw.tr/mukan-network/mukan-sdk/x/genutil/types"
|
|
govtypes "git.cw.tr/mukan-network/mukan-sdk/x/gov/types"
|
|
govv1 "git.cw.tr/mukan-network/mukan-sdk/x/gov/types/v1"
|
|
govv1beta1 "git.cw.tr/mukan-network/mukan-sdk/x/gov/types/v1beta1"
|
|
|
|
cmtjson "git.cw.tr/mukan-network/mukan-consensus/libs/json"
|
|
|
|
"github.com/cosmos/ibc-go/e2e/internal/directories"
|
|
"github.com/cosmos/ibc-go/e2e/relayer"
|
|
"github.com/cosmos/ibc-go/e2e/semverutil"
|
|
"github.com/cosmos/ibc-go/e2e/testvalues"
|
|
wasmtypes "github.com/cosmos/ibc-go/modules/light-clients/08-wasm/v10/types"
|
|
clienttypes "git.cw.tr/mukan-network/mukan-ibc/modules/core/02-client/types"
|
|
ibcexported "git.cw.tr/mukan-network/mukan-ibc/modules/core/exported"
|
|
ibctypes "git.cw.tr/mukan-network/mukan-ibc/modules/core/types"
|
|
)
|
|
|
|
const (
|
|
// ChainImageEnv specifies the image that the chains will use. If left unspecified, it will
|
|
// default to being determined based on the specified binary. E.g. ghcr.io/cosmos/ibc-go-simd
|
|
ChainImageEnv = "CHAIN_IMAGE"
|
|
// ChainATagEnv specifies the tag that Chain A will use.
|
|
ChainATagEnv = "CHAIN_A_TAG"
|
|
// ChainBTagEnv specifies the tag that Chain B will use. If unspecified
|
|
// the value will default to the same value as Chain A.
|
|
ChainBTagEnv = "CHAIN_B_TAG"
|
|
// ChainCTagEnv specifies the tag that Chain C will use.
|
|
// the value will default to the same value as Chain A.
|
|
ChainCTagEnv = "CHAIN_C_TAG"
|
|
// ChainDTagEnv specifies the tag that Chain D will use. If unspecified
|
|
// the value will default to the same value as Chain A.
|
|
ChainDTagEnv = "CHAIN_D_TAG"
|
|
// RelayerIDEnv specifies the ID of the relayer to use.
|
|
RelayerIDEnv = "RELAYER_ID"
|
|
// ChainBinaryEnv binary is the binary that will be used for both chains.
|
|
ChainBinaryEnv = "CHAIN_BINARY"
|
|
// ChainUpgradePlanEnv specifies the upgrade plan name
|
|
ChainUpgradePlanEnv = "CHAIN_UPGRADE_PLAN"
|
|
// E2EConfigFilePathEnv allows you to specify a custom path for the config file to be used. It can be relative
|
|
// or absolute.
|
|
E2EConfigFilePathEnv = "E2E_CONFIG_PATH"
|
|
// KeepContainersEnv instructs interchaintest to not delete the containers after a test has run.
|
|
// this ensures that chain containers are not deleted after a test suite is run if other tests
|
|
// depend on those chains.
|
|
KeepContainersEnv = "KEEP_CONTAINERS"
|
|
|
|
// defaultBinary is the default binary that will be used by the chains.
|
|
defaultBinary = "simd"
|
|
// defaultRlyTag is the tag that will be used if no relayer tag is specified.
|
|
// all images are here https://github.com/cosmos/relayer/pkgs/container/relayer/versions
|
|
defaultRlyTag = "latest"
|
|
|
|
// defaultHermesTag is the tag that will be used if no relayer tag is specified for hermes.
|
|
defaultHermesTag = "1.10.4"
|
|
// defaultChainTag is the tag that will be used for the chains if none is specified.
|
|
defaultChainTag = "main"
|
|
// defaultConfigFileName is the default filename for the config file that can be used to configure
|
|
// e2e tests. See sample.config.yaml or sample.config.extended.yaml as an example for what this should look like.
|
|
defaultConfigFileName = ".ibc-go-e2e-config.yaml"
|
|
// defaultCIConfigFileName is the default filename for the config file that should be used for CI.
|
|
defaultCIConfigFileName = "ci-e2e-config.yaml"
|
|
)
|
|
|
|
// defaultChainNames contains the default name for chainA, chainB, ChainC and ChainD.
|
|
var defaultChainNames = []string{"simapp-a", "simapp-b", "simapp-c", "simapp-d"}
|
|
|
|
func getChainImage(binary string) string {
|
|
if binary == "" {
|
|
binary = defaultBinary
|
|
}
|
|
return fmt.Sprintf("ghcr.io/cosmos/ibc-go-%s", binary)
|
|
}
|
|
|
|
// TestConfig holds configuration used throughout the different e2e tests.
|
|
type TestConfig struct {
|
|
// ChainConfigs holds configuration values related to the chains used in the tests.
|
|
ChainConfigs []ChainConfig `yaml:"chains"`
|
|
// RelayerConfigs holds all known relayer configurations that can be used in the tests.
|
|
RelayerConfigs []relayer.Config `yaml:"relayers"`
|
|
// ActiveRelayer specifies the relayer that will be used. It must match the ID of one of the entries in RelayerConfigs.
|
|
ActiveRelayer string `yaml:"activeRelayer"`
|
|
// CometBFTConfig holds values for configuring CometBFT.
|
|
CometBFTConfig CometBFTConfig `yaml:"cometbft"`
|
|
// DebugConfig holds configuration for miscellaneous options.
|
|
DebugConfig DebugConfig `yaml:"debug"`
|
|
// UpgradePlanName specifies which upgrade plan to use. It must match a plan name for an entry in the
|
|
// list of UpgradeConfigs.
|
|
UpgradePlanName string `yaml:"upgradePlanName"`
|
|
// UpgradeConfigs provides a list of all possible upgrades.
|
|
UpgradeConfigs []UpgradeConfig `yaml:"upgrades"`
|
|
}
|
|
|
|
// Validate validates the test configuration is valid for use within the tests.
|
|
// this should be called before using the configuration.
|
|
func (tc TestConfig) Validate() error {
|
|
if err := tc.validateChains(); err != nil {
|
|
return fmt.Errorf("invalid chain configuration: %w", err)
|
|
}
|
|
|
|
if err := tc.validateRelayers(); err != nil {
|
|
return fmt.Errorf("invalid relayer configuration: %w", err)
|
|
}
|
|
|
|
if err := tc.validateGenesisDebugConfig(); err != nil {
|
|
return fmt.Errorf("invalid Genesis debug configuration: %w", err)
|
|
}
|
|
|
|
if err := tc.validateUpgradeConfig(); err != nil {
|
|
return fmt.Errorf("invalid upgrade configuration: %w", err)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// validateChains validates the chain configurations.
|
|
func (tc TestConfig) validateChains() error {
|
|
for _, cfg := range tc.ChainConfigs {
|
|
if cfg.Binary == "" {
|
|
return fmt.Errorf("chain config missing binary: %+v", cfg)
|
|
}
|
|
if cfg.Image == "" {
|
|
return fmt.Errorf("chain config missing image: %+v", cfg)
|
|
}
|
|
if cfg.Tag == "" {
|
|
return fmt.Errorf("chain config missing tag: %+v", cfg)
|
|
}
|
|
|
|
if cfg.NumValidators == 0 && cfg.NumFullNodes == 0 {
|
|
return fmt.Errorf("chain config missing number of validators or full nodes: %+v", cfg)
|
|
}
|
|
}
|
|
|
|
// clienttypes.ParseChainID is used to determine revision heights. If the chainIDs are not in the expected format,
|
|
// tests can fail with timeout errors.
|
|
if clienttypes.ParseChainID(tc.GetChainAID()) != clienttypes.ParseChainID(tc.GetChainBID()) {
|
|
return fmt.Errorf("ensure both chainIDs are in the format {chainID}-{revision} and have the same revision. Got: chainA: %s, chainB: %s", tc.GetChainAID(), tc.GetChainBID())
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// validateRelayers validates relayer configuration.
|
|
func (tc TestConfig) validateRelayers() error {
|
|
if len(tc.RelayerConfigs) < 1 {
|
|
return errors.New("no relayer configurations specified")
|
|
}
|
|
|
|
for _, r := range tc.RelayerConfigs {
|
|
if r.ID == "" {
|
|
return fmt.Errorf("relayer config missing ID: %+v", r)
|
|
}
|
|
if r.Image == "" {
|
|
return fmt.Errorf("relayer config missing image: %+v", r)
|
|
}
|
|
if r.Tag == "" {
|
|
return fmt.Errorf("relayer config missing tag: %+v", r)
|
|
}
|
|
}
|
|
|
|
if tc.GetActiveRelayerConfig() == nil {
|
|
return fmt.Errorf("active relayer %s not found in relayer configs: %+v", tc.ActiveRelayer, tc.RelayerConfigs)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// GetUpgradeConfig returns the upgrade configuration for the current test configuration.
|
|
func (tc TestConfig) GetUpgradeConfig() UpgradeConfig {
|
|
for _, upgrade := range tc.UpgradeConfigs {
|
|
if upgrade.PlanName == tc.UpgradePlanName {
|
|
return upgrade
|
|
}
|
|
}
|
|
panic("upgrade plan not found in upgrade configs, this test config should not have passed validation")
|
|
}
|
|
|
|
// GetChainIndex returns the index of the chain with the given name, if it
|
|
// exists.
|
|
func (tc TestConfig) GetChainIndex(name string) (int, error) {
|
|
for i := range tc.ChainConfigs {
|
|
chainName := tc.GetChainName(i)
|
|
if chainName == name {
|
|
return i, nil
|
|
}
|
|
}
|
|
return -1, fmt.Errorf("chain %s not found in chain configs", name)
|
|
}
|
|
|
|
// validateGenesisDebugConfig validates configuration of Genesis debug options/
|
|
func (tc TestConfig) validateGenesisDebugConfig() error {
|
|
cfg := tc.DebugConfig.GenesisDebug
|
|
if !cfg.DumpGenesisDebugInfo {
|
|
return nil
|
|
}
|
|
|
|
// Verify that the provided chain exists in our config
|
|
_, err := tc.GetChainIndex(tc.GetGenesisChainName())
|
|
|
|
return err
|
|
}
|
|
|
|
// validateUpgradeConfig ensures the upgrade configuration is valid.
|
|
func (tc TestConfig) validateUpgradeConfig() error {
|
|
if strings.TrimSpace(tc.UpgradePlanName) == "" {
|
|
return nil
|
|
}
|
|
|
|
// the upgrade plan name specified must match one of the upgrade plans in the upgrade configs.
|
|
foundPlan := false
|
|
for _, upgrade := range tc.UpgradeConfigs {
|
|
if strings.TrimSpace(upgrade.Tag) == "" {
|
|
return fmt.Errorf("upgrade config missing tag: %+v", upgrade)
|
|
}
|
|
|
|
if strings.TrimSpace(upgrade.PlanName) == "" {
|
|
return fmt.Errorf("upgrade config missing plan name: %+v", upgrade)
|
|
}
|
|
|
|
if upgrade.PlanName == tc.UpgradePlanName {
|
|
foundPlan = true
|
|
}
|
|
}
|
|
|
|
if foundPlan {
|
|
return nil
|
|
}
|
|
|
|
return fmt.Errorf("upgrade plan %s not found in upgrade configs: %+v", tc.UpgradePlanName, tc.UpgradeConfigs)
|
|
}
|
|
|
|
// GetActiveRelayerConfig returns the currently specified relayer config.
|
|
func (tc TestConfig) GetActiveRelayerConfig() *relayer.Config {
|
|
for _, r := range tc.RelayerConfigs {
|
|
if r.ID == tc.ActiveRelayer {
|
|
return &r
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// GetChainNumValidators returns the number of validators for the specific chain index.
|
|
// default 1
|
|
func (tc TestConfig) GetChainNumValidators(idx int) int {
|
|
if tc.ChainConfigs[idx].NumValidators > 0 {
|
|
return tc.ChainConfigs[idx].NumValidators
|
|
}
|
|
return 1
|
|
}
|
|
|
|
// GetChainNumFullNodes returns the number of full nodes for the specific chain index.
|
|
// default 0
|
|
func (tc TestConfig) GetChainNumFullNodes(idx int) int {
|
|
if tc.ChainConfigs[idx].NumFullNodes > 0 {
|
|
return tc.ChainConfigs[idx].NumFullNodes
|
|
}
|
|
return 0
|
|
}
|
|
|
|
// GetChainID returns the chain-id for i. Assumes indicies are correct.
|
|
func (tc TestConfig) GetChainID(i int) string {
|
|
if tc.ChainConfigs[i].ChainID != "" {
|
|
return tc.ChainConfigs[i].ChainID
|
|
}
|
|
return fmt.Sprintf("chain%c-1", 'A'+i)
|
|
}
|
|
|
|
// GetChainAID returns the chain-id for chain A.
|
|
// NOTE: the default return value will ensure that ParseChainID will return 1 as the revision number.
|
|
func (tc TestConfig) GetChainAID() string {
|
|
if tc.ChainConfigs[0].ChainID != "" {
|
|
return tc.ChainConfigs[0].ChainID
|
|
}
|
|
return "chainA-1"
|
|
}
|
|
|
|
// GetChainBID returns the chain-id for chain B.
|
|
// NOTE: the default return value will ensure that ParseChainID will return 1 as the revision number.
|
|
func (tc TestConfig) GetChainBID() string {
|
|
if tc.ChainConfigs[1].ChainID != "" {
|
|
return tc.ChainConfigs[1].ChainID
|
|
}
|
|
return "chainB-1"
|
|
}
|
|
|
|
// GetChainCID returns the chain-id for chain C.
|
|
// NOTE: the default return value will ensure that ParseChainID will return 1 as the revision number.
|
|
func (tc TestConfig) GetChainCID() string {
|
|
if tc.ChainConfigs[2].ChainID != "" {
|
|
return tc.ChainConfigs[2].ChainID
|
|
}
|
|
return "chainC-1"
|
|
}
|
|
|
|
// GetChainDID returns the chain-id for chain D.
|
|
// NOTE: the default return value will ensure that ParseChainID will return 1 as the revision number.
|
|
func (tc TestConfig) GetChainDID() string {
|
|
if tc.ChainConfigs[3].ChainID != "" {
|
|
return tc.ChainConfigs[3].ChainID
|
|
}
|
|
return "chainD-1"
|
|
}
|
|
|
|
// GetChainName returns the name of the chain given an index.
|
|
func (tc TestConfig) GetChainName(idx int) string {
|
|
// Assumes that only valid indices are provided. We do the same in several other places.
|
|
chainName := tc.ChainConfigs[idx].Name
|
|
if chainName == "" {
|
|
chainName = defaultChainNames[idx]
|
|
}
|
|
return chainName
|
|
}
|
|
|
|
// GetGenesisChainName returns the name of the chain for which to dump Genesis files.
|
|
// If no chain is provided, it uses the default one (chainA).
|
|
func (tc TestConfig) GetGenesisChainName() string {
|
|
name := tc.DebugConfig.GenesisDebug.ChainName
|
|
if name == "" {
|
|
return tc.GetChainName(0)
|
|
}
|
|
return name
|
|
}
|
|
|
|
// UpgradeConfig holds values relevant to upgrade tests.
|
|
type UpgradeConfig struct {
|
|
PlanName string `yaml:"planName"`
|
|
Tag string `yaml:"tag"`
|
|
}
|
|
|
|
// ChainConfig holds information about an individual chain used in the tests.
|
|
type ChainConfig struct {
|
|
ChainID string `yaml:"chainId"`
|
|
Name string `yaml:"name"`
|
|
Image string `yaml:"image"`
|
|
Tag string `yaml:"tag"`
|
|
Binary string `yaml:"binary"`
|
|
NumValidators int `yaml:"numValidators"`
|
|
NumFullNodes int `yaml:"numFullNodes"`
|
|
}
|
|
|
|
type CometBFTConfig struct {
|
|
LogLevel string `yaml:"logLevel"`
|
|
}
|
|
|
|
type GenesisDebugConfig struct {
|
|
// DumpGenesisDebugInfo enables the output of Genesis debug files.
|
|
DumpGenesisDebugInfo bool `yaml:"dumpGenesisDebugInfo"`
|
|
|
|
// ExportFilePath specifies which path to export Genesis debug files to.
|
|
ExportFilePath string `yaml:"filePath"`
|
|
|
|
// ChainName represent which chain to get Genesis debug info for.
|
|
ChainName string `yaml:"chainName"`
|
|
}
|
|
|
|
type DebugConfig struct {
|
|
// DumpLogs forces the logs to be collected before removing test containers.
|
|
DumpLogs bool `yaml:"dumpLogs"`
|
|
|
|
// GenesisDebug contains debug information specific to Genesis.
|
|
GenesisDebug GenesisDebugConfig `yaml:"genesis"`
|
|
|
|
// KeepContainers specifies if the containers should be kept after the test suite is done.
|
|
// NOTE: when running a full test suite, this value should be set to true in order to preserve
|
|
// shared resources.
|
|
KeepContainers bool `yaml:"keepContainers"`
|
|
}
|
|
|
|
// LoadConfig attempts to load a test configuration from the default file path.
|
|
// if any environment variables are specified, they will take precedence over the individual configuration
|
|
// options.
|
|
func LoadConfig() TestConfig {
|
|
tc := getConfig()
|
|
if err := tc.Validate(); err != nil {
|
|
panic(err)
|
|
}
|
|
return tc
|
|
}
|
|
|
|
// getConfig returns the TestConfig with any environment variable overrides.
|
|
func getConfig() TestConfig {
|
|
fileTc, foundFile := fromFile()
|
|
if !foundFile {
|
|
return fromEnv()
|
|
}
|
|
|
|
testCfg := applyEnvironmentVariableOverrides(fileTc)
|
|
|
|
// If tags for chain C and D are not present in the file, also not set in the CI, fallback to A
|
|
if testCfg.ChainConfigs[2].Tag == "" {
|
|
testCfg.ChainConfigs[2].Tag = testCfg.ChainConfigs[0].Tag
|
|
}
|
|
if testCfg.ChainConfigs[3].Tag == "" {
|
|
testCfg.ChainConfigs[3].Tag = testCfg.ChainConfigs[0].Tag
|
|
}
|
|
return testCfg
|
|
}
|
|
|
|
// fromFile returns a TestConfig from a json file and a boolean indicating if the file was found.
|
|
func fromFile() (TestConfig, bool) {
|
|
var tc TestConfig
|
|
bz, err := os.ReadFile(getConfigFilePath())
|
|
if err != nil {
|
|
return TestConfig{}, false
|
|
}
|
|
|
|
if err := yaml.Unmarshal(bz, &tc); err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
return populateDefaults(tc), true
|
|
}
|
|
|
|
// populateDefaults populates default values for the test config if
|
|
// certain required fields are not specified.
|
|
func populateDefaults(tc TestConfig) TestConfig {
|
|
chainIDs := []string{
|
|
"chainA-1",
|
|
"chainB-1",
|
|
"chainC-1",
|
|
"chainD-1",
|
|
}
|
|
|
|
for i := range tc.ChainConfigs {
|
|
if tc.ChainConfigs[i].ChainID == "" {
|
|
tc.ChainConfigs[i].ChainID = chainIDs[i]
|
|
}
|
|
if tc.ChainConfigs[i].Binary == "" {
|
|
tc.ChainConfigs[i].Binary = defaultBinary
|
|
}
|
|
if tc.ChainConfigs[i].Image == "" {
|
|
tc.ChainConfigs[i].Image = getChainImage(tc.ChainConfigs[i].Binary)
|
|
}
|
|
if tc.ChainConfigs[i].NumValidators == 0 {
|
|
tc.ChainConfigs[i].NumValidators = 1
|
|
}
|
|
|
|
// If tag not given for chain C and D, set to chain A' tag
|
|
if tc.ChainConfigs[i].Tag == "" && i != 0 {
|
|
tc.ChainConfigs[i].Tag = tc.ChainConfigs[0].Tag
|
|
}
|
|
}
|
|
|
|
if tc.ActiveRelayer == "" {
|
|
tc.ActiveRelayer = relayer.Hermes
|
|
}
|
|
|
|
if tc.RelayerConfigs == nil {
|
|
tc.RelayerConfigs = []relayer.Config{
|
|
getDefaultRlyRelayerConfig(),
|
|
getDefaultHermesRelayerConfig(),
|
|
}
|
|
}
|
|
|
|
if tc.CometBFTConfig.LogLevel == "" {
|
|
tc.CometBFTConfig.LogLevel = "info"
|
|
}
|
|
|
|
return tc
|
|
}
|
|
|
|
// applyEnvironmentVariableOverrides applies all environment variable changes to the config
|
|
// loaded from a file.
|
|
func applyEnvironmentVariableOverrides(fromFile TestConfig) TestConfig {
|
|
envTc := fromEnv()
|
|
|
|
if os.Getenv(ChainATagEnv) != "" {
|
|
fromFile.ChainConfigs[0].Tag = envTc.ChainConfigs[0].Tag
|
|
}
|
|
|
|
if os.Getenv(ChainBTagEnv) != "" {
|
|
fromFile.ChainConfigs[1].Tag = envTc.ChainConfigs[1].Tag
|
|
}
|
|
|
|
if os.Getenv(ChainCTagEnv) != "" {
|
|
fromFile.ChainConfigs[2].Tag = envTc.ChainConfigs[2].Tag
|
|
}
|
|
|
|
if os.Getenv(ChainDTagEnv) != "" {
|
|
fromFile.ChainConfigs[3].Tag = envTc.ChainConfigs[3].Tag
|
|
}
|
|
|
|
if os.Getenv(ChainBinaryEnv) != "" {
|
|
for i := range fromFile.ChainConfigs {
|
|
fromFile.ChainConfigs[i].Binary = envTc.ChainConfigs[i].Binary
|
|
}
|
|
}
|
|
|
|
if os.Getenv(ChainImageEnv) != "" {
|
|
for i := range fromFile.ChainConfigs {
|
|
fromFile.ChainConfigs[i].Image = envTc.ChainConfigs[i].Image
|
|
}
|
|
}
|
|
|
|
if os.Getenv(RelayerIDEnv) != "" {
|
|
fromFile.ActiveRelayer = envTc.ActiveRelayer
|
|
}
|
|
|
|
if os.Getenv(ChainUpgradePlanEnv) != "" {
|
|
fromFile.UpgradePlanName = envTc.UpgradePlanName
|
|
}
|
|
|
|
if isEnvTrue(KeepContainersEnv) {
|
|
fromFile.DebugConfig.KeepContainers = true
|
|
}
|
|
|
|
return fromFile
|
|
}
|
|
|
|
// fromEnv returns a TestConfig constructed from environment variables.
|
|
func fromEnv() TestConfig {
|
|
return TestConfig{
|
|
ChainConfigs: getChainConfigsFromEnv(),
|
|
UpgradePlanName: os.Getenv(ChainUpgradePlanEnv),
|
|
ActiveRelayer: os.Getenv(RelayerIDEnv),
|
|
CometBFTConfig: CometBFTConfig{LogLevel: "info"},
|
|
}
|
|
}
|
|
|
|
// getChainConfigsFromEnv returns the chain configs from environment variables.
|
|
func getChainConfigsFromEnv() []ChainConfig {
|
|
chainBinary, ok := os.LookupEnv(ChainBinaryEnv)
|
|
if !ok {
|
|
chainBinary = defaultBinary
|
|
}
|
|
|
|
chainATag, ok := os.LookupEnv(ChainATagEnv)
|
|
if !ok {
|
|
chainATag = defaultChainTag
|
|
}
|
|
|
|
chainBTag, ok := os.LookupEnv(ChainBTagEnv)
|
|
if !ok {
|
|
chainBTag = chainATag
|
|
}
|
|
|
|
chainCTag, ok := os.LookupEnv(ChainCTagEnv)
|
|
if !ok {
|
|
chainCTag = chainATag
|
|
}
|
|
|
|
chainDTag, ok := os.LookupEnv(ChainDTagEnv)
|
|
if !ok {
|
|
chainDTag = chainATag
|
|
}
|
|
|
|
chainAImage := getChainImage(chainBinary)
|
|
specifiedChainImage, ok := os.LookupEnv(ChainImageEnv)
|
|
if ok {
|
|
chainAImage = specifiedChainImage
|
|
}
|
|
|
|
numValidators := 4
|
|
numFullNodes := 1
|
|
|
|
chainBImage := chainAImage
|
|
chainCImage := chainAImage
|
|
chainDImage := chainAImage
|
|
|
|
return []ChainConfig{
|
|
{
|
|
Image: chainAImage,
|
|
Tag: chainATag,
|
|
Binary: chainBinary,
|
|
NumValidators: numValidators,
|
|
NumFullNodes: numFullNodes,
|
|
},
|
|
{
|
|
Image: chainBImage,
|
|
Tag: chainBTag,
|
|
Binary: chainBinary,
|
|
NumValidators: numValidators,
|
|
NumFullNodes: numFullNodes,
|
|
},
|
|
{
|
|
Image: chainCImage,
|
|
Tag: chainCTag,
|
|
Binary: chainBinary,
|
|
NumValidators: numValidators,
|
|
NumFullNodes: numFullNodes,
|
|
},
|
|
{
|
|
Image: chainDImage,
|
|
Tag: chainDTag,
|
|
Binary: chainBinary,
|
|
NumValidators: numValidators,
|
|
NumFullNodes: numFullNodes,
|
|
},
|
|
}
|
|
}
|
|
|
|
// getConfigFilePath returns the absolute path where the e2e config file should be.
|
|
func getConfigFilePath() string {
|
|
if specifiedConfigPath := os.Getenv(E2EConfigFilePathEnv); specifiedConfigPath != "" {
|
|
if path.IsAbs(specifiedConfigPath) {
|
|
return specifiedConfigPath
|
|
}
|
|
|
|
e2eDir, err := directories.E2E()
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
return path.Join(e2eDir, specifiedConfigPath)
|
|
}
|
|
|
|
if IsCI() {
|
|
if err := os.Setenv(E2EConfigFilePathEnv, defaultCIConfigFileName); err != nil {
|
|
panic(err)
|
|
}
|
|
return getConfigFilePath()
|
|
}
|
|
|
|
// running locally.
|
|
homeDir, err := os.UserHomeDir()
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
return path.Join(homeDir, defaultConfigFileName)
|
|
}
|
|
|
|
// TODO: remove in https://github.com/cosmos/ibc-go/issues/4697
|
|
// getDefaultHermesRelayerConfig returns the default config for the hermes relayer.
|
|
func getDefaultHermesRelayerConfig() relayer.Config {
|
|
return relayer.Config{
|
|
Tag: defaultHermesTag,
|
|
ID: relayer.Hermes,
|
|
Image: relayer.HermesRelayerRepository,
|
|
}
|
|
}
|
|
|
|
// TODO: remove in https://github.com/cosmos/ibc-go/issues/4697
|
|
// getDefaultRlyRelayerConfig returns the default config for the golang relayer.
|
|
func getDefaultRlyRelayerConfig() relayer.Config {
|
|
return relayer.Config{
|
|
Tag: defaultRlyTag,
|
|
ID: relayer.Rly,
|
|
Image: relayer.RlyRelayerRepository,
|
|
}
|
|
}
|
|
|
|
func GetChainATag() string {
|
|
return LoadConfig().ChainConfigs[0].Tag
|
|
}
|
|
|
|
func GetChainBTag() string {
|
|
if chainBTag := LoadConfig().ChainConfigs[1].Tag; chainBTag != "" {
|
|
return chainBTag
|
|
}
|
|
return GetChainATag()
|
|
}
|
|
|
|
// IsCI returns true if the tests are running in CI, false is returned
|
|
// if the tests are running locally.
|
|
// Note: github actions passes a CI env value of true by default to all runners.
|
|
func IsCI() bool {
|
|
return isEnvTrue("CI")
|
|
}
|
|
|
|
// IsFork returns true if the tests are running in fork mode, false is returned otherwise.
|
|
func IsFork() bool {
|
|
return isEnvTrue("FORK")
|
|
}
|
|
|
|
// IsRunSuite returns true if the tests are running in suite mode, false is returned otherwise.
|
|
func IsRunSuite() bool {
|
|
return isEnvTrue("RUN_SUITE")
|
|
}
|
|
|
|
func isEnvTrue(env string) bool {
|
|
return strings.ToLower(os.Getenv(env)) == "true"
|
|
}
|
|
|
|
// ChainOptions stores chain configurations for the chains that will be
|
|
// created for the tests. They can be modified by passing ChainOptionConfiguration
|
|
// to E2ETestSuite.GetChains.
|
|
type ChainOptions struct {
|
|
ChainSpecs []*interchaintest.ChainSpec
|
|
SkipPathCreation bool
|
|
RelayerCount int
|
|
}
|
|
|
|
// ChainOptionConfiguration enables arbitrary configuration of ChainOptions.
|
|
type ChainOptionConfiguration func(options *ChainOptions)
|
|
|
|
// DefaultChainOptions returns the default configuration for the chains.
|
|
// These options can be configured by passing configuration functions to E2ETestSuite.GetChains.
|
|
func DefaultChainOptions(chainCount int) (ChainOptions, error) {
|
|
tc := LoadConfig()
|
|
|
|
if len(tc.ChainConfigs) < chainCount {
|
|
return ChainOptions{}, fmt.Errorf("file has %d configs. want %d configs", len(tc.ChainConfigs), chainCount)
|
|
}
|
|
|
|
specs := make([]*interchaintest.ChainSpec, 0, chainCount)
|
|
for i := range chainCount {
|
|
denom := fmt.Sprintf("atom%c", 'a'+i)
|
|
chainName := tc.GetChainName(i)
|
|
chainID := tc.GetChainID(i)
|
|
cfg := newDefaultSimappConfig(tc.ChainConfigs[0], chainName, chainID, denom, tc.CometBFTConfig)
|
|
validators, fullNodes := getValidatorsAndFullNodes(i)
|
|
|
|
spec := &interchaintest.ChainSpec{
|
|
ChainConfig: cfg,
|
|
NumFullNodes: &fullNodes,
|
|
NumValidators: &validators,
|
|
}
|
|
specs = append(specs, spec)
|
|
}
|
|
|
|
// if running a single test, only one relayer is needed.
|
|
numRelayers := 1
|
|
if IsRunSuite() {
|
|
// Arbitrary number; if interchaintest supports dynamic relayer creation during tests,
|
|
// this can be reduced or simplified.
|
|
numRelayers = 10
|
|
}
|
|
|
|
return ChainOptions{
|
|
ChainSpecs: specs,
|
|
RelayerCount: numRelayers,
|
|
}, nil
|
|
}
|
|
|
|
// newDefaultSimappConfig creates an ibc configuration for simd.
|
|
func newDefaultSimappConfig(cc ChainConfig, name, chainID, denom string, cometCfg CometBFTConfig) ibc.ChainConfig {
|
|
configFileOverrides := make(map[string]any)
|
|
tmTomlOverrides := make(interchaintestutil.Toml)
|
|
|
|
tmTomlOverrides["log_level"] = cometCfg.LogLevel // change to debug in the e2e test config to increase cometbft logging.
|
|
configFileOverrides["config/config.toml"] = tmTomlOverrides
|
|
|
|
return ibc.ChainConfig{
|
|
Type: "cosmos",
|
|
Name: name,
|
|
ChainID: chainID,
|
|
Images: []ibc.DockerImage{
|
|
{
|
|
Repository: cc.Image,
|
|
Version: cc.Tag,
|
|
UIDGID: "1000:1000",
|
|
},
|
|
},
|
|
Bin: cc.Binary,
|
|
Bech32Prefix: "cosmos",
|
|
CoinType: fmt.Sprint(sdk.CoinType),
|
|
Denom: denom,
|
|
EncodingConfig: SDKEncodingConfig(),
|
|
GasPrices: fmt.Sprintf("0.00%s", denom),
|
|
GasAdjustment: 1.3,
|
|
TrustingPeriod: "508h",
|
|
NoHostMount: false,
|
|
ModifyGenesis: getGenesisModificationFunction(cc),
|
|
ConfigFileOverrides: configFileOverrides,
|
|
}
|
|
}
|
|
|
|
// getGenesisModificationFunction returns a genesis modification function that handles the GenesisState type
|
|
// correctly depending on if the govv1beta1 gov module is used or if govv1 is being used.
|
|
func getGenesisModificationFunction(cc ChainConfig) func(ibc.ChainConfig, []byte) ([]byte, error) {
|
|
binary := cc.Binary
|
|
version := cc.Tag
|
|
|
|
simdSupportsGovV1Genesis := binary == defaultBinary && testvalues.GovGenesisFeatureReleases.IsSupported(version)
|
|
|
|
// TODO: Remove after we drop v7 support (this is only needed right now because of v6 -> v7 upgrade tests)
|
|
if simdSupportsGovV1Genesis {
|
|
return defaultGovv1ModifyGenesis(version)
|
|
}
|
|
|
|
return defaultGovv1Beta1ModifyGenesis(version)
|
|
}
|
|
|
|
// defaultGovv1ModifyGenesis will only modify governance params to ensure the voting period and minimum deposit
|
|
// are functional for e2e testing purposes.
|
|
func defaultGovv1ModifyGenesis(version string) func(ibc.ChainConfig, []byte) ([]byte, error) {
|
|
stdlibJSONMarshalling := semverutil.FeatureReleases{MajorVersion: "v8"}
|
|
return func(chainConfig ibc.ChainConfig, genbz []byte) ([]byte, error) {
|
|
appGenesis, err := genutiltypes.AppGenesisFromReader(bytes.NewReader(genbz))
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to unmarshal genesis bytes into genesis doc: %w", err)
|
|
}
|
|
|
|
var appState genutiltypes.AppMap
|
|
if err := json.Unmarshal(appGenesis.AppState, &appState); err != nil {
|
|
return nil, fmt.Errorf("failed to unmarshal genesis bytes into app state: %w", err)
|
|
}
|
|
|
|
govGenBz, err := modifyGovV1AppState(chainConfig, appState[govtypes.ModuleName])
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
appState[govtypes.ModuleName] = govGenBz
|
|
|
|
if !testvalues.AllowAllClientsWildcardFeatureReleases.IsSupported(version) {
|
|
ibcGenBz, err := modifyClientGenesisAppState(appState[ibcexported.ModuleName])
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
appState[ibcexported.ModuleName] = ibcGenBz
|
|
}
|
|
|
|
if !testvalues.ChannelParamsFeatureReleases.IsSupported(version) {
|
|
ibcGenBz, err := modifyChannelGenesisAppState(appState[ibcexported.ModuleName])
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
appState[ibcexported.ModuleName] = ibcGenBz
|
|
}
|
|
|
|
if !testvalues.ChannelsV2FeatureReleases.IsSupported(version) {
|
|
ibcGenBz, err := modifyChannelV2GenesisAppState(appState[ibcexported.ModuleName])
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
appState[ibcexported.ModuleName] = ibcGenBz
|
|
}
|
|
|
|
if !testvalues.ClientV2FeatureReleases.IsSupported(version) {
|
|
ibcGenBz, err := modifyClientV2GenesisAppState(appState[ibcexported.ModuleName])
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
appState[ibcexported.ModuleName] = ibcGenBz
|
|
}
|
|
|
|
appGenesis.AppState, err = json.Marshal(appState)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// in older version < v8, tmjson marshal must be used.
|
|
// regular json marshalling must be used for v8 and above as the
|
|
// sdk is de-coupled from comet.
|
|
marshalIndentFn := cmtjson.MarshalIndent
|
|
if stdlibJSONMarshalling.IsSupported(version) {
|
|
marshalIndentFn = json.MarshalIndent
|
|
}
|
|
|
|
bz, err := marshalIndentFn(appGenesis, "", " ")
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return bz, nil
|
|
}
|
|
}
|
|
|
|
// defaultGovv1Beta1ModifyGenesis will only modify governance params to ensure the voting period and minimum deposit
|
|
// // are functional for e2e testing purposes.
|
|
func defaultGovv1Beta1ModifyGenesis(version string) func(ibc.ChainConfig, []byte) ([]byte, error) {
|
|
const appStateKey = "app_state"
|
|
return func(chainConfig ibc.ChainConfig, genbz []byte) ([]byte, error) {
|
|
genesisDocMap := map[string]any{}
|
|
err := json.Unmarshal(genbz, &genesisDocMap)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to unmarshal genesis bytes into genesis doc: %w", err)
|
|
}
|
|
|
|
appStateMap, ok := genesisDocMap[appStateKey].(map[string]any)
|
|
if !ok {
|
|
return nil, errors.New("failed to extract to app_state")
|
|
}
|
|
|
|
govModuleBytes, err := json.Marshal(appStateMap[govtypes.ModuleName])
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to extract gov genesis bytes: %s", err)
|
|
}
|
|
|
|
govModuleGenesisBytes, err := modifyGovv1Beta1AppState(chainConfig, govModuleBytes)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
govModuleGenesisMap := map[string]any{}
|
|
err = json.Unmarshal(govModuleGenesisBytes, &govModuleGenesisMap)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to unmarshal gov genesis bytes into map: %w", err)
|
|
}
|
|
|
|
if !testvalues.AllowAllClientsWildcardFeatureReleases.IsSupported(version) {
|
|
ibcModuleBytes, err := json.Marshal(appStateMap[ibcexported.ModuleName])
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to extract ibc genesis bytes: %s", err)
|
|
}
|
|
|
|
ibcGenesisBytes, err := modifyClientGenesisAppState(ibcModuleBytes)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
ibcModuleGenesisMap := map[string]any{}
|
|
err = json.Unmarshal(ibcGenesisBytes, &ibcModuleGenesisMap)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to unmarshal gov genesis bytes into map: %w", err)
|
|
}
|
|
appStateMap[ibcexported.ModuleName] = ibcModuleGenesisMap
|
|
}
|
|
|
|
if !testvalues.ChannelParamsFeatureReleases.IsSupported(version) {
|
|
ibcModuleBytes, err := json.Marshal(appStateMap[ibcexported.ModuleName])
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to extract ibc genesis bytes: %s", err)
|
|
}
|
|
|
|
ibcGenesisBytes, err := modifyChannelGenesisAppState(ibcModuleBytes)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
ibcModuleGenesisMap := map[string]any{}
|
|
err = json.Unmarshal(ibcGenesisBytes, &ibcModuleGenesisMap)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to unmarshal gov genesis bytes into map: %w", err)
|
|
}
|
|
appStateMap[ibcexported.ModuleName] = ibcModuleGenesisMap
|
|
|
|
}
|
|
|
|
if !testvalues.ChannelsV2FeatureReleases.IsSupported(version) {
|
|
ibcModuleBytes, err := json.Marshal(appStateMap[ibcexported.ModuleName])
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to extract ibc genesis bytes: %s", err)
|
|
}
|
|
|
|
ibcGenesisBytes, err := modifyChannelV2GenesisAppState(ibcModuleBytes)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
ibcModuleGenesisMap := map[string]any{}
|
|
err = json.Unmarshal(ibcGenesisBytes, &ibcModuleGenesisMap)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to unmarshal gov genesis bytes into map: %w", err)
|
|
}
|
|
appStateMap[ibcexported.ModuleName] = ibcModuleGenesisMap
|
|
}
|
|
|
|
if !testvalues.ClientV2FeatureReleases.IsSupported(version) {
|
|
ibcModuleBytes, err := json.Marshal(appStateMap[ibcexported.ModuleName])
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to extract ibc genesis bytes: %s", err)
|
|
}
|
|
|
|
ibcGenesisBytes, err := modifyClientV2GenesisAppState(ibcModuleBytes)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
ibcModuleGenesisMap := map[string]any{}
|
|
err = json.Unmarshal(ibcGenesisBytes, &ibcModuleGenesisMap)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to unmarshal gov genesis bytes into map: %w", err)
|
|
}
|
|
appStateMap[ibcexported.ModuleName] = ibcModuleGenesisMap
|
|
}
|
|
|
|
appStateMap[govtypes.ModuleName] = govModuleGenesisMap
|
|
genesisDocMap[appStateKey] = appStateMap
|
|
|
|
finalGenesisDocBytes, err := json.MarshalIndent(genesisDocMap, "", " ")
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return finalGenesisDocBytes, nil
|
|
}
|
|
}
|
|
|
|
// modifyGovV1AppState takes the existing gov app state and marshals it to a govv1 GenesisState.
|
|
func modifyGovV1AppState(chainConfig ibc.ChainConfig, govAppState []byte) ([]byte, error) {
|
|
cfg := testutil.MakeTestEncodingConfig()
|
|
|
|
cdc := codec.NewProtoCodec(cfg.InterfaceRegistry)
|
|
govv1.RegisterInterfaces(cfg.InterfaceRegistry)
|
|
|
|
govGenesisState := &govv1.GenesisState{}
|
|
|
|
if err := cdc.UnmarshalJSON(govAppState, govGenesisState); err != nil {
|
|
return nil, fmt.Errorf("failed to unmarshal genesis bytes into gov genesis state: %w", err)
|
|
}
|
|
|
|
if govGenesisState.Params == nil {
|
|
govGenesisState.Params = &govv1.Params{}
|
|
}
|
|
|
|
govGenesisState.Params.MinDeposit = sdk.NewCoins(sdk.NewCoin(chainConfig.Denom, govv1beta1.DefaultMinDepositTokens))
|
|
maxDep := time.Second * 10
|
|
govGenesisState.Params.MaxDepositPeriod = &maxDep
|
|
vp := testvalues.VotingPeriod
|
|
govGenesisState.Params.VotingPeriod = &vp
|
|
|
|
govGenBz := MustProtoMarshalJSON(govGenesisState)
|
|
|
|
return govGenBz, nil
|
|
}
|
|
|
|
// modifyGovv1Beta1AppState takes the existing gov app state and marshals it to a govv1beta1 GenesisState.
|
|
func modifyGovv1Beta1AppState(chainConfig ibc.ChainConfig, govAppState []byte) ([]byte, error) {
|
|
cfg := testutil.MakeTestEncodingConfig()
|
|
|
|
cdc := codec.NewProtoCodec(cfg.InterfaceRegistry)
|
|
govv1beta1.RegisterInterfaces(cfg.InterfaceRegistry)
|
|
|
|
govGenesisState := &govv1beta1.GenesisState{}
|
|
if err := cdc.UnmarshalJSON(govAppState, govGenesisState); err != nil {
|
|
return nil, fmt.Errorf("failed to unmarshal genesis bytes into govv1beta1 genesis state: %w", err)
|
|
}
|
|
|
|
govGenesisState.DepositParams.MinDeposit = sdk.NewCoins(sdk.NewCoin(chainConfig.Denom, govv1beta1.DefaultMinDepositTokens))
|
|
govGenesisState.VotingParams.VotingPeriod = testvalues.VotingPeriod
|
|
|
|
govGenBz, err := cdc.MarshalJSON(govGenesisState)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to marshal gov genesis state: %w", err)
|
|
}
|
|
|
|
return govGenBz, nil
|
|
}
|
|
|
|
// modifyClientGenesisAppState takes the existing ibc app state and marshals it to an ibc GenesisState.
|
|
func modifyClientGenesisAppState(ibcAppState []byte) ([]byte, error) {
|
|
cfg := testutil.MakeTestEncodingConfig()
|
|
|
|
cdc := codec.NewProtoCodec(cfg.InterfaceRegistry)
|
|
clienttypes.RegisterInterfaces(cfg.InterfaceRegistry)
|
|
|
|
ibcGenesisState := &ibctypes.GenesisState{}
|
|
if err := cdc.UnmarshalJSON(ibcAppState, ibcGenesisState); err != nil {
|
|
return nil, fmt.Errorf("failed to unmarshal genesis bytes into client genesis state: %w", err)
|
|
}
|
|
|
|
ibcGenesisState.ClientGenesis.Params.AllowedClients = append(ibcGenesisState.ClientGenesis.Params.AllowedClients, wasmtypes.Wasm)
|
|
ibcGenBz, err := cdc.MarshalJSON(ibcGenesisState)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to marshal gov genesis state: %w", err)
|
|
}
|
|
|
|
return ibcGenBz, nil
|
|
}
|
|
|
|
// modifyChannelGenesisAppState takes the existing ibc app state, unmarshals it to a map and removes the `params` entry from ibc channel genesis.
|
|
// It marshals and returns the ibc GenesisState JSON map as bytes.
|
|
func modifyChannelGenesisAppState(ibcAppState []byte) ([]byte, error) {
|
|
var ibcGenesisMap map[string]any
|
|
if err := json.Unmarshal(ibcAppState, &ibcGenesisMap); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
var channelGenesis map[string]any
|
|
// be ashamed, be very ashamed
|
|
channelGenesis, ok := ibcGenesisMap["channel_genesis"].(map[string]any)
|
|
if !ok {
|
|
return nil, fmt.Errorf("can't convert IBC genesis map entry into type %T", &channelGenesis)
|
|
}
|
|
delete(channelGenesis, "params")
|
|
|
|
return json.Marshal(ibcGenesisMap)
|
|
}
|
|
|
|
func modifyChannelV2GenesisAppState(ibcAppState []byte) ([]byte, error) {
|
|
var ibcGenesisMap map[string]any
|
|
if err := json.Unmarshal(ibcAppState, &ibcGenesisMap); err != nil {
|
|
return nil, err
|
|
}
|
|
delete(ibcGenesisMap, "channel_v2_genesis")
|
|
|
|
return json.Marshal(ibcGenesisMap)
|
|
}
|
|
|
|
func modifyClientV2GenesisAppState(ibcAppState []byte) ([]byte, error) {
|
|
var ibcGenesisMap map[string]any
|
|
if err := json.Unmarshal(ibcAppState, &ibcGenesisMap); err != nil {
|
|
return nil, err
|
|
}
|
|
delete(ibcGenesisMap, "client_v2_genesis")
|
|
|
|
return json.Marshal(ibcGenesisMap)
|
|
}
|