diff --git a/chain/cosmos/cosmos_chain.go b/chain/cosmos/cosmos_chain.go index 75287c3f6..668750c2d 100644 --- a/chain/cosmos/cosmos_chain.go +++ b/chain/cosmos/cosmos_chain.go @@ -1,7 +1,6 @@ package cosmos import ( - "bytes" "context" "crypto/sha256" "encoding/hex" @@ -13,7 +12,6 @@ import ( "strings" "sync" - sdkmath "cosmossdk.io/math" "github.com/cosmos/cosmos-sdk/codec" codectypes "github.com/cosmos/cosmos-sdk/codec/types" cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec" @@ -128,7 +126,6 @@ func (c *CosmosChain) WithPreStartNodes(preStartNodes func(*CosmosChain)) { c.preStartNodes = preStartNodes } - // GetCodec returns the codec for the chain. func (c *CosmosChain) GetCodec() *codec.ProtoCodec { return c.cdc @@ -820,9 +817,13 @@ type GenesisValidators struct { Power string `json:"power"` PubKey GenesisValidatorPubKey `json:"pub_key"` } -type GenesisFile struct { +type GenesisConsensus struct { Validators []GenesisValidators `json:"validators"` } +type GenesisFile struct { + V47Validators []GenesisValidators `json:"validators"` // SDK v47 backwards Compatability + Consensus GenesisConsensus `json:"consensus"` // v47+ +} type ValidatorWithIntPower struct { Address string @@ -830,238 +831,6 @@ type ValidatorWithIntPower struct { PubKeyBase64 string } -// Bootstraps the chain and starts it from genesis -func (c *CosmosChain) Start(testName string, ctx context.Context, additionalGenesisWallets ...ibc.WalletAmount) error { - if c.cfg.InterchainSecurityConfig.ConsumerCopyProviderKey != nil && c.Provider == nil { - return fmt.Errorf("don't set ConsumerCopyProviderKey if it's not a consumer chain") - } - - chainCfg := c.Config() - - decimalPow := int64(math.Pow10(int(*chainCfg.CoinDecimals))) - - genesisAmounts := make([][]types.Coin, len(c.Validators)) - genesisSelfDelegation := make([]types.Coin, len(c.Validators)) - - for i := range c.Validators { - genesisAmounts[i] = []types.Coin{{Amount: sdkmath.NewInt(10_000_000).MulRaw(decimalPow), Denom: chainCfg.Denom}} - genesisSelfDelegation[i] = types.Coin{Amount: sdkmath.NewInt(5_000_000).MulRaw(decimalPow), Denom: chainCfg.Denom} - if chainCfg.ModifyGenesisAmounts != nil { - amount, selfDelegation := chainCfg.ModifyGenesisAmounts(i) - genesisAmounts[i] = []types.Coin{amount} - genesisSelfDelegation[i] = selfDelegation - } - } - - configFileOverrides := chainCfg.ConfigFileOverrides - - eg := new(errgroup.Group) - // Initialize config and sign gentx for each validator. - for i, v := range c.Validators { - v := v - i := i - v.Validator = true - eg.Go(func() error { - if err := v.InitFullNodeFiles(ctx); err != nil { - return err - } - for configFile, modifiedConfig := range configFileOverrides { - modifiedToml, ok := modifiedConfig.(testutil.Toml) - if !ok { - return fmt.Errorf("Provided toml override for file %s is of type (%T). Expected (DecodedToml)", configFile, modifiedConfig) - } - if err := testutil.ModifyTomlConfigFile( - ctx, - v.logger(), - v.DockerClient, - v.TestName, - v.VolumeName, - configFile, - modifiedToml, - ); err != nil { - return fmt.Errorf("failed to modify toml config file: %w", err) - } - } - if !c.cfg.SkipGenTx { - return v.InitValidatorGenTx(ctx, &chainCfg, genesisAmounts[i], genesisSelfDelegation[i]) - } - return nil - }) - } - - // Initialize config for each full node. - for _, n := range c.FullNodes { - n := n - n.Validator = false - eg.Go(func() error { - if err := n.InitFullNodeFiles(ctx); err != nil { - return err - } - for configFile, modifiedConfig := range configFileOverrides { - modifiedToml, ok := modifiedConfig.(testutil.Toml) - if !ok { - return fmt.Errorf("Provided toml override for file %s is of type (%T). Expected (DecodedToml)", configFile, modifiedConfig) - } - if err := testutil.ModifyTomlConfigFile( - ctx, - n.logger(), - n.DockerClient, - n.TestName, - n.VolumeName, - configFile, - modifiedToml, - ); err != nil { - return err - } - } - return nil - }) - } - - // wait for this to finish - if err := eg.Wait(); err != nil { - return err - } - - if c.preStartNodes != nil { - c.preStartNodes(c) - } - - if c.cfg.PreGenesis != nil { - err := c.cfg.PreGenesis(c) - if err != nil { - return err - } - } - - // for the validators we need to collect the gentxs and the accounts - // to the first node's genesis file - validator0 := c.Validators[0] - for i := 1; i < len(c.Validators); i++ { - validatorN := c.Validators[i] - - bech32, err := validatorN.AccountKeyBech32(ctx, valKey) - if err != nil { - return err - } - - if err := validator0.AddGenesisAccount(ctx, bech32, genesisAmounts[0]); err != nil { - return err - } - - if !c.cfg.SkipGenTx { - if err := validatorN.copyGentx(ctx, validator0); err != nil { - return err - } - } - } - - for _, wallet := range additionalGenesisWallets { - - if err := validator0.AddGenesisAccount(ctx, wallet.Address, []types.Coin{{Denom: wallet.Denom, Amount: wallet.Amount}}); err != nil { - return err - } - } - - if !c.cfg.SkipGenTx { - if err := validator0.CollectGentxs(ctx); err != nil { - return err - } - } - - genbz, err := validator0.GenesisFileContent(ctx) - if err != nil { - return err - } - - genbz = bytes.ReplaceAll(genbz, []byte(`"stake"`), []byte(fmt.Sprintf(`"%s"`, chainCfg.Denom))) - - if c.cfg.ModifyGenesis != nil { - genbz, err = c.cfg.ModifyGenesis(chainCfg, genbz) - if err != nil { - return err - } - } - - // Provide EXPORT_GENESIS_FILE_PATH and EXPORT_GENESIS_CHAIN to help debug genesis file - exportGenesis := os.Getenv("EXPORT_GENESIS_FILE_PATH") - exportGenesisChain := os.Getenv("EXPORT_GENESIS_CHAIN") - if exportGenesis != "" && exportGenesisChain == c.cfg.Name { - c.log.Debug("Exporting genesis file", - zap.String("chain", exportGenesisChain), - zap.String("path", exportGenesis), - ) - _ = os.WriteFile(exportGenesis, genbz, 0600) - } - - chainNodes := c.Nodes() - - for _, cn := range chainNodes { - if err := cn.OverwriteGenesisFile(ctx, genbz); err != nil { - return err - } - } - - if err := chainNodes.LogGenesisHashes(ctx); err != nil { - return err - } - - // Start any sidecar processes that should be running before the chain starts - eg, egCtx := errgroup.WithContext(ctx) - for _, s := range c.Sidecars { - s := s - - err = s.containerLifecycle.Running(ctx) - if s.preStart && err != nil { - eg.Go(func() error { - if err := s.CreateContainer(egCtx); err != nil { - return err - } - - if err := s.StartContainer(egCtx); err != nil { - return err - } - - return nil - }) - } - } - if err := eg.Wait(); err != nil { - return err - } - - eg, egCtx = errgroup.WithContext(ctx) - for _, n := range chainNodes { - n := n - eg.Go(func() error { - return n.CreateNodeContainer(egCtx) - }) - } - if err := eg.Wait(); err != nil { - return err - } - - peers := chainNodes.PeerString(ctx) - - eg, egCtx = errgroup.WithContext(ctx) - for _, n := range chainNodes { - n := n - c.log.Info("Starting container", zap.String("container", n.Name())) - eg.Go(func() error { - if err := n.SetPeers(egCtx, peers); err != nil { - return err - } - return n.StartContainer(egCtx) - }) - } - if err := eg.Wait(); err != nil { - return err - } - - // Wait for blocks before considering the chains "started" - return testutil.WaitForBlocks(ctx, 2, c.getFullNode()) -} - // Height implements ibc.Chain func (c *CosmosChain) Height(ctx context.Context) (int64, error) { return c.getFullNode().Height(ctx) diff --git a/chain/cosmos/cosmos_genesis.go b/chain/cosmos/cosmos_genesis.go new file mode 100644 index 000000000..1ed33d06c --- /dev/null +++ b/chain/cosmos/cosmos_genesis.go @@ -0,0 +1,397 @@ +package cosmos + +import ( + "bytes" + "context" + "encoding/hex" + "encoding/json" + "fmt" + "math" + "os" + "sort" + "strconv" + "sync" + + sdkmath "cosmossdk.io/math" + types "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/bech32" + "github.com/docker/docker/client" + "github.com/strangelove-ventures/interchaintest/v8/ibc" + "github.com/strangelove-ventures/interchaintest/v8/testutil" + "go.uber.org/zap" + "golang.org/x/sync/errgroup" +) + +// Bootstraps the chain and starts it from genesis +func (c *CosmosChain) Start(testName string, ctx context.Context, additionalGenesisWallets ...ibc.WalletAmount) error { + chainCfg := c.Config() + + // call StartWithGenesisFile here instead if something is changed + overGen := chainCfg.OverrideGenesisStart + if overGen.GenesisFilePath != "" { + c.log.Info("Starting with genesis file", zap.String("name", c.cfg.Name), zap.String("path", overGen.GenesisFilePath)) + + if overGen.Client == nil || overGen.NetworkID == "" { + panic("BUG: OverrideGenesisStart is nil when using GenesisFilePath") + } + + return c.StartWithGenesisFile(ctx, testName, overGen.Client, overGen.NetworkID, overGen.GenesisFilePath) + } + + c.log.Info("Starting chain", zap.String("name", c.cfg.Name)) + + decimalPow := int64(math.Pow10(int(*chainCfg.CoinDecimals))) + genesisAmounts := make([][]types.Coin, len(c.Validators)) + genesisSelfDelegation := make([]types.Coin, len(c.Validators)) + + for i := range c.Validators { + genesisAmounts[i] = []types.Coin{{Amount: sdkmath.NewInt(10_000_000).MulRaw(decimalPow), Denom: chainCfg.Denom}} + genesisSelfDelegation[i] = types.Coin{Amount: sdkmath.NewInt(5_000_000).MulRaw(decimalPow), Denom: chainCfg.Denom} + if chainCfg.ModifyGenesisAmounts != nil { + amount, selfDelegation := chainCfg.ModifyGenesisAmounts(i) + genesisAmounts[i] = []types.Coin{amount} + genesisSelfDelegation[i] = selfDelegation + } + } + + if err := c.prepNodes(ctx, c.cfg.SkipGenTx, genesisAmounts, genesisSelfDelegation); err != nil { + return err + } + + if c.cfg.PreGenesis != nil { + err := c.cfg.PreGenesis(c) + if err != nil { + return err + } + } + + // for the validators we need to collect the gentxs and the accounts + // to the first node's genesis file + validator0 := c.Validators[0] + for i := 1; i < len(c.Validators); i++ { + validatorN := c.Validators[i] + + bech32, err := validatorN.AccountKeyBech32(ctx, valKey) + if err != nil { + return err + } + + if err := validator0.AddGenesisAccount(ctx, bech32, genesisAmounts[i]); err != nil { + return err + } + + if !c.cfg.SkipGenTx { + if err := validatorN.copyGentx(ctx, validator0); err != nil { + return err + } + } + } + + for _, wallet := range additionalGenesisWallets { + if err := validator0.AddGenesisAccount(ctx, wallet.Address, []types.Coin{{Denom: wallet.Denom, Amount: wallet.Amount}}); err != nil { + return err + } + } + + if !c.cfg.SkipGenTx { + if err := validator0.CollectGentxs(ctx); err != nil { + return err + } + } + + genbz, err := validator0.GenesisFileContent(ctx) + if err != nil { + return err + } + + genbz = bytes.ReplaceAll(genbz, []byte(`"stake"`), []byte(fmt.Sprintf(`"%s"`, chainCfg.Denom))) + + return c.startWithFinalGenesis(ctx, genbz) +} + +// Bootstraps the chain and starts it from genesis +func (c *CosmosChain) StartWithGenesisFile( + ctx context.Context, + testName string, + client *client.Client, + network string, + genesisFilePath string, +) error { + genBz, err := os.ReadFile(genesisFilePath) + if err != nil { + return fmt.Errorf("failed to read genesis file: %w", err) + } + + chainCfg := c.Config() + + var genesisFile GenesisFile + if err := json.Unmarshal(genBz, &genesisFile); err != nil { + return err + } + + // TODO: SDK v47 check here (genesisFile.V47Validators) + genesisValidators := genesisFile.Consensus.Validators + totalPower := int64(0) + + if len(genesisValidators) == 0 { + panic("BUG: genesisValidators is empty with custom genesis file start.") + } + + validatorsWithPower := make([]ValidatorWithIntPower, 0) + + for _, genesisValidator := range genesisValidators { + power, err := strconv.ParseInt(genesisValidator.Power, 10, 64) + if err != nil { + return err + } + totalPower += power + validatorsWithPower = append(validatorsWithPower, ValidatorWithIntPower{ + Address: genesisValidator.Address, + Power: power, + PubKeyBase64: genesisValidator.PubKey.Value, + }) + } + + sort.Slice(validatorsWithPower, func(i, j int) bool { + return validatorsWithPower[i].Power > validatorsWithPower[j].Power + }) + + var eg errgroup.Group + var mu sync.Mutex + genBzReplace := func(find, replace []byte) { + mu.Lock() + defer mu.Unlock() + genBz = bytes.ReplaceAll(genBz, find, replace) + } + + twoThirdsConsensus := int64(math.Ceil(float64(totalPower) * 2 / 3)) + totalConsensus := int64(0) + + var activeVals []ValidatorWithIntPower + for _, validator := range validatorsWithPower { + activeVals = append(activeVals, validator) + + totalConsensus += validator.Power + + if totalConsensus > twoThirdsConsensus { + break + } + } + + c.NumValidators = len(activeVals) + if c.NumValidators == 0 { + panic("BUG: c.NumValidators is 0 with custom genesis file start.") + } + + if err := c.initializeChainNodes(ctx, testName, client, network); err != nil { + return err + } + + if err := c.prepNodes(ctx, true, nil, []types.Coin{}); err != nil { + return err + } + + // TODO: this is a duplicate, why? do we need here or only in the start? Maybe this is required in both places. idk + if c.cfg.PreGenesis != nil { + err := c.cfg.PreGenesis(c) + if err != nil { + return err + } + } + + for i, validator := range activeVals { + v := c.Validators[i] + validator := validator + eg.Go(func() error { + testNodePubKeyJsonBytes, err := v.ReadFile(ctx, "config/priv_validator_key.json") + if err != nil { + return fmt.Errorf("failed to read priv_validator_key.json: %w", err) + } + + var testNodePrivValFile PrivValidatorKeyFile + if err := json.Unmarshal(testNodePubKeyJsonBytes, &testNodePrivValFile); err != nil { + return fmt.Errorf("failed to unmarshal priv_validator_key.json: %w", err) + } + + // modify genesis file overwriting validators address with the one generated for this test node + genBzReplace([]byte(validator.Address), []byte(testNodePrivValFile.Address)) + + // modify genesis file overwriting validators base64 pub_key.value with the one generated for this test node + genBzReplace([]byte(validator.PubKeyBase64), []byte(testNodePrivValFile.PubKey.Value)) + + existingValAddressBytes, err := hex.DecodeString(validator.Address) + if err != nil { + return err + } + + testNodeAddressBytes, err := hex.DecodeString(testNodePrivValFile.Address) + if err != nil { + return err + } + + valConsPrefix := fmt.Sprintf("%svalcons", chainCfg.Bech32Prefix) + + existingValBech32ValConsAddress, err := bech32.ConvertAndEncode(valConsPrefix, existingValAddressBytes) + if err != nil { + return err + } + + testNodeBech32ValConsAddress, err := bech32.ConvertAndEncode(valConsPrefix, testNodeAddressBytes) + if err != nil { + return err + } + + genBzReplace([]byte(existingValBech32ValConsAddress), []byte(testNodeBech32ValConsAddress)) + + return nil + }) + } + + if err := eg.Wait(); err != nil { + return err + } + + return c.startWithFinalGenesis(ctx, genBz) +} + +func (c *CosmosChain) startWithFinalGenesis(ctx context.Context, genbz []byte) error { + if c.cfg.ModifyGenesis != nil { + var err error + genbz, err = c.cfg.ModifyGenesis(c.Config(), genbz) + if err != nil { + return err + } + } + + c.log.Info("Writing genesis and starting chain", zap.String("name", c.cfg.Name)) + + // Provide EXPORT_GENESIS_FILE_PATH and EXPORT_GENESIS_CHAIN to help debug genesis file + exportGenesis := os.Getenv("EXPORT_GENESIS_FILE_PATH") + exportGenesisChain := os.Getenv("EXPORT_GENESIS_CHAIN") + if exportGenesis != "" && exportGenesisChain == c.cfg.Name { + c.log.Debug("Exporting genesis file", + zap.String("chain", exportGenesisChain), + zap.String("path", exportGenesis), + ) + _ = os.WriteFile(exportGenesis, genbz, 0600) + } + + chainNodes := c.Nodes() + + for _, cn := range chainNodes { + if err := cn.OverwriteGenesisFile(ctx, genbz); err != nil { + return err + } + } + + if err := chainNodes.LogGenesisHashes(ctx); err != nil { + return err + } + + eg, egCtx := errgroup.WithContext(ctx) + for _, n := range chainNodes { + n := n + eg.Go(func() error { + return n.CreateNodeContainer(egCtx) + }) + } + if err := eg.Wait(); err != nil { + return err + } + + peers := chainNodes.PeerString(ctx) + + eg, egCtx = errgroup.WithContext(ctx) + for _, n := range chainNodes { + n := n + c.log.Info("Starting container", zap.String("container", n.Name())) + eg.Go(func() error { + if err := n.SetPeers(egCtx, peers); err != nil { + return err + } + return n.StartContainer(egCtx) + }) + } + if err := eg.Wait(); err != nil { + return err + } + + // Wait for blocks before considering the chains "started" + return testutil.WaitForBlocks(ctx, 2, c.GetNode()) +} + +// Bootstraps the chain and starts it from genesis +func (c *CosmosChain) prepNodes(ctx context.Context, skipGenTx bool, genesisAmounts [][]types.Coin, genesisSelfDelegation []types.Coin) error { + if c.cfg.InterchainSecurityConfig.ConsumerCopyProviderKey != nil && c.Provider == nil { + return fmt.Errorf("don't set ConsumerCopyProviderKey if it's not a consumer chain") + } + + chainCfg := c.Config() + configFileOverrides := chainCfg.ConfigFileOverrides + + eg := new(errgroup.Group) + // Initialize config and sign gentx for each validator. + for i, v := range c.Validators { + v := v + i := i + v.Validator = true + eg.Go(func() error { + if err := v.InitFullNodeFiles(ctx); err != nil { + return err + } + for configFile, modifiedConfig := range configFileOverrides { + modifiedToml, ok := modifiedConfig.(testutil.Toml) + if !ok { + return fmt.Errorf("provided toml override for file %s is of type (%T). Expected (DecodedToml)", configFile, modifiedConfig) + } + if err := testutil.ModifyTomlConfigFile( + ctx, + v.logger(), + v.DockerClient, + v.TestName, + v.VolumeName, + configFile, + modifiedToml, + ); err != nil { + return fmt.Errorf("failed to modify toml config file: %w", err) + } + } + if !skipGenTx { + return v.InitValidatorGenTx(ctx, &chainCfg, genesisAmounts[i], genesisSelfDelegation[i]) + } + return nil + }) + } + + // Initialize config for each full node. + for _, n := range c.FullNodes { + n := n + n.Validator = false + eg.Go(func() error { + if err := n.InitFullNodeFiles(ctx); err != nil { + return err + } + for configFile, modifiedConfig := range configFileOverrides { + modifiedToml, ok := modifiedConfig.(testutil.Toml) + if !ok { + return fmt.Errorf("provided toml override for file %s is of type (%T). Expected (DecodedToml)", configFile, modifiedConfig) + } + if err := testutil.ModifyTomlConfigFile( + ctx, + n.logger(), + n.DockerClient, + n.TestName, + n.VolumeName, + configFile, + modifiedToml, + ); err != nil { + return err + } + } + return nil + }) + } + + // wait for this to finish + return eg.Wait() +} diff --git a/examples/cosmos/TestSingleValBenchmark/export.json b/examples/cosmos/TestSingleValBenchmark/export.json new file mode 100644 index 000000000..fea368563 --- /dev/null +++ b/examples/cosmos/TestSingleValBenchmark/export.json @@ -0,0 +1,624 @@ +{ + "app_name": "simd", + "app_version": "8.0.0-beta.1-18-g2551dea4", + "genesis_time": "2024-08-13T12:27:20.371844161Z", + "chain_id": "ibc-go-simd-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": "cosmos1fl48vsnmsdzcv85q5d2q4z5ajdha8yu34mf0eh", + "pub_key": null, + "account_number": "4", + "sequence": "0" + }, + "name": "bonded_tokens_pool", + "permissions": [ + "burner", + "staking" + ] + }, + { + "@type": "/cosmos.auth.v1beta1.ModuleAccount", + "base_account": { + "address": "cosmos1tygms3xhhs3yv487phx3dw4a95jn7t7lpm470r", + "pub_key": null, + "account_number": "5", + "sequence": "0" + }, + "name": "not_bonded_tokens_pool", + "permissions": [ + "burner", + "staking" + ] + }, + { + "@type": "/cosmos.auth.v1beta1.BaseAccount", + "address": "cosmos10xwngmmx2n2c70fr9krt6yya6u89uw0xxfhfg3", + "pub_key": { + "@type": "/cosmos.crypto.secp256k1.PubKey", + "key": "A9CZIEf6A9u33ivDjR2tCkNsuJvLV4QaP70a5bp64Dgo" + }, + "account_number": "0", + "sequence": "1" + }, + { + "@type": "/cosmos.auth.v1beta1.ModuleAccount", + "base_account": { + "address": "cosmos10d07y265gmmuvt4z0w9aw880jnsr700j6zn9kn", + "pub_key": null, + "account_number": "6", + "sequence": "0" + }, + "name": "gov", + "permissions": [ + "burner" + ] + }, + { + "@type": "/cosmos.auth.v1beta1.ModuleAccount", + "base_account": { + "address": "cosmos1jv65s3grqf6v6jl3dp4t6c9t9rk99cd88lyufl", + "pub_key": null, + "account_number": "3", + "sequence": "0" + }, + "name": "distribution", + "permissions": [] + }, + { + "@type": "/cosmos.auth.v1beta1.BaseAccount", + "address": "cosmos16rlexackvy2n6302cl4trmmam5s70wcq86z2cu", + "pub_key": null, + "account_number": "1", + "sequence": "0" + }, + { + "@type": "/cosmos.auth.v1beta1.ModuleAccount", + "base_account": { + "address": "cosmos1m3h30wlvsf8llruxtpukdvsy0km2kum8g38c8q", + "pub_key": null, + "account_number": "7", + "sequence": "0" + }, + "name": "mint", + "permissions": [ + "minter" + ] + }, + { + "@type": "/cosmos.auth.v1beta1.ModuleAccount", + "base_account": { + "address": "cosmos17xpfvakm2amg962yls6f84z3kell8c5lserqta", + "pub_key": null, + "account_number": "2", + "sequence": "0" + }, + "name": "fee_collector", + "permissions": [] + } + ] + }, + "authz": { + "authorization": [] + }, + "bank": { + "params": { + "send_enabled": [], + "default_send_enabled": true + }, + "balances": [ + { + "address": "cosmos1fl48vsnmsdzcv85q5d2q4z5ajdha8yu34mf0eh", + "coins": [ + { + "denom": "token", + "amount": "5000000000000" + } + ] + }, + { + "address": "cosmos10xwngmmx2n2c70fr9krt6yya6u89uw0xxfhfg3", + "coins": [ + { + "denom": "token", + "amount": "5000000000000" + } + ] + }, + { + "address": "cosmos1jv65s3grqf6v6jl3dp4t6c9t9rk99cd88lyufl", + "coins": [ + { + "denom": "token", + "amount": "15859894" + } + ] + }, + { + "address": "cosmos16rlexackvy2n6302cl4trmmam5s70wcq86z2cu", + "coins": [ + { + "denom": "token", + "amount": "100000000000000" + } + ] + } + ], + "supply": [ + { + "denom": "token", + "amount": "110000015859894" + } + ], + "denom_metadata": [], + "send_enabled": [] + }, + "capability": { + "index": "5", + "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" + } + ] + } + }, + { + "index": "3", + "index_owners": { + "owners": [ + { + "module": "ibc", + "name": "ports/mock" + }, + { + "module": "mock", + "name": "ports/mock" + } + ] + } + }, + { + "index": "4", + "index_owners": { + "owners": [ + { + "module": "ibc", + "name": "ports/mockfeeibc" + }, + { + "module": "mockfeeibc", + "name": "ports/mockfeeibc" + } + ] + } + } + ] + }, + "circuit": { + "account_permissions": [], + "disabled_type_urls": [] + }, + "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": [ + { + "denom": "token", + "amount": "317197.880000000000000000" + } + ] + }, + "delegator_withdraw_infos": [], + "previous_proposer": "cosmosvalcons166fn0eljmuc775eaqyyhhvtylsgckw9gqdzjxr", + "outstanding_rewards": [ + { + "validator_address": "cosmosvaloper10xwngmmx2n2c70fr9krt6yya6u89uw0xraruyz", + "outstanding_rewards": [ + { + "denom": "token", + "amount": "15542696.120000000000000000" + } + ] + } + ], + "validator_accumulated_commissions": [ + { + "validator_address": "cosmosvaloper10xwngmmx2n2c70fr9krt6yya6u89uw0xraruyz", + "accumulated": { + "commission": [ + { + "denom": "token", + "amount": "1554269.612000000000000000" + } + ] + } + } + ], + "validator_historical_rewards": [ + { + "validator_address": "cosmosvaloper10xwngmmx2n2c70fr9krt6yya6u89uw0xraruyz", + "period": "1", + "rewards": { + "cumulative_reward_ratio": [], + "reference_count": 2 + } + } + ], + "validator_current_rewards": [ + { + "validator_address": "cosmosvaloper10xwngmmx2n2c70fr9krt6yya6u89uw0xraruyz", + "rewards": { + "rewards": [ + { + "denom": "token", + "amount": "13988426.508000000000000000" + } + ], + "period": "2" + } + } + ], + "delegator_starting_infos": [ + { + "delegator_address": "cosmos10xwngmmx2n2c70fr9krt6yya6u89uw0xxfhfg3", + "validator_address": "cosmosvaloper10xwngmmx2n2c70fr9krt6yya6u89uw0xraruyz", + "starting_info": { + "previous_period": "1", + "stake": "5000000000000.000000000000000000", + "height": "0" + } + } + ], + "validator_slash_events": [] + }, + "evidence": { + "evidence": [] + }, + "feegrant": { + "allowances": [] + }, + "feeibc": { + "identified_fees": [], + "fee_enabled_channels": [], + "registered_payees": [], + "registered_counterparty_payees": [], + "forward_relayers": [] + }, + "genutil": { + "gen_txs": [] + }, + "gov": { + "starting_proposal_id": "1", + "deposits": [], + "votes": [], + "proposals": [], + "deposit_params": null, + "voting_params": null, + "tally_params": null, + "params": { + "min_deposit": [ + { + "denom": "ujuno", + "amount": "1" + } + ], + "max_deposit_period": "10s", + "voting_period": "15s", + "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": "86400s", + "expedited_threshold": "0.667000000000000000", + "expedited_min_deposit": [ + { + "denom": "token", + "amount": "50000000" + } + ], + "burn_vote_quorum": false, + "burn_proposal_deposit_prevote": false, + "burn_vote_veto": true, + "min_deposit_ratio": "0.010000000000000000" + }, + "constitution": "" + }, + "group": { + "group_seq": "0", + "groups": [], + "group_members": [], + "group_policy_seq": "0", + "group_policies": [], + "proposal_seq": "0", + "proposals": [], + "votes": [] + }, + "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": [ + "06-solomachine", + "07-tendermint", + "09-localhost" + ] + }, + "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" + } + }, + "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": [ + "*" + ] + } + } + }, + "mint": { + "minter": { + "inflation": "0.130000134399199103", + "annual_provisions": "14300016551158.948378786050807982" + }, + "params": { + "mint_denom": "token", + "inflation_rate_change": "0.130000000000000000", + "inflation_max": "0.200000000000000000", + "inflation_min": "0.070000000000000000", + "goal_bonded": "0.670000000000000000", + "blocks_per_year": "6311520" + } + }, + "mock": null, + "slashing": { + "params": { + "signed_blocks_window": "100", + "min_signed_per_window": "0.500000000000000000", + "downtime_jail_duration": "600s", + "slash_fraction_double_sign": "0.050000000000000000", + "slash_fraction_downtime": "0.010000000000000000" + }, + "signing_infos": [ + { + "address": "cosmosvalcons166fn0eljmuc775eaqyyhhvtylsgckw9gqdzjxr", + "validator_signing_info": { + "address": "cosmosvalcons166fn0eljmuc775eaqyyhhvtylsgckw9gqdzjxr", + "start_height": "0", + "index_offset": "6", + "jailed_until": "1970-01-01T00:00:00Z", + "tombstoned": false, + "missed_blocks_counter": "0" + } + } + ], + "missed_blocks": [ + { + "address": "cosmosvalcons166fn0eljmuc775eaqyyhhvtylsgckw9gqdzjxr", + "missed_blocks": [] + } + ] + }, + "staking": { + "params": { + "unbonding_time": "1814400s", + "max_validators": 100, + "max_entries": 7, + "historical_entries": 10000, + "bond_denom": "token", + "min_commission_rate": "0.000000000000000000" + }, + "last_total_power": "5000000", + "last_validator_powers": [ + { + "address": "cosmosvaloper10xwngmmx2n2c70fr9krt6yya6u89uw0xraruyz", + "power": "5000000" + } + ], + "validators": [ + { + "operator_address": "cosmosvaloper10xwngmmx2n2c70fr9krt6yya6u89uw0xraruyz", + "consensus_pubkey": { + "@type": "/cosmos.crypto.ed25519.PubKey", + "key": "4jg6nX5ZP9vs4GNMv/z8s9A3yKbP+thpMFqeRb0E+bc=" + }, + "jailed": false, + "status": "BOND_STATUS_BONDED", + "tokens": "5000000000000", + "delegator_shares": "5000000000000.000000000000000000", + "description": { + "moniker": "ibc-go-simd-1-val-0-TestSingleValBenchmark", + "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-08-13T12:27:20.371844161Z" + }, + "min_self_delegation": "1", + "unbonding_on_hold_ref_count": "0", + "unbonding_ids": [] + } + ], + "delegations": [ + { + "delegator_address": "cosmos10xwngmmx2n2c70fr9krt6yya6u89uw0xxfhfg3", + "validator_address": "cosmosvaloper10xwngmmx2n2c70fr9krt6yya6u89uw0xraruyz", + "shares": "5000000000000.000000000000000000" + } + ], + "unbonding_delegations": [], + "redelegations": [], + "exported": true + }, + "transfer": { + "port_id": "transfer", + "denom_traces": [], + "params": { + "send_enabled": true, + "receive_enabled": true + }, + "total_escrowed": [] + }, + "upgrade": {}, + "vesting": {} + }, + "consensus": { + "validators": [ + { + "address": "D69337E7F2DF31EF533D01097BB164FC118B38A8", + "pub_key": { + "type": "tendermint/PubKeyEd25519", + "value": "4jg6nX5ZP9vs4GNMv/z8s9A3yKbP+thpMFqeRb0E+bc=" + }, + "power": "5000000", + "name": "ibc-go-simd-1-val-0-TestSingleValBenchmark" + } + ], + "params": { + "block": { + "max_bytes": "22020096", + "max_gas": "-1" + }, + "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/examples/cosmos/delete_me_test.go b/examples/cosmos/delete_me_test.go new file mode 100644 index 000000000..a70254e04 --- /dev/null +++ b/examples/cosmos/delete_me_test.go @@ -0,0 +1,117 @@ +package cosmos_test + +import ( + "context" + "fmt" + "os" + "path" + "testing" + "time" + + "github.com/strangelove-ventures/interchaintest/v8" + "github.com/strangelove-ventures/interchaintest/v8/chain/cosmos" + "github.com/strangelove-ventures/interchaintest/v8/ibc" + "github.com/strangelove-ventures/interchaintest/v8/testreporter" + "github.com/stretchr/testify/require" + "go.uber.org/zap/zaptest" +) + +// go test -timeout 3000s -run ^TestSingleValBenchmark$ github.com/strangelove-ventures/interchaintest/v8/examples/cosmos -v -count=1 +func TestSingleValBenchmark(t *testing.T) { + now := time.Now() + t.Parallel() + + ctx := context.Background() + + client, network := interchaintest.DockerSetup(t) + icOpts := interchaintest.InterchainBuildOptions{ + TestName: t.Name(), + Client: client, + NetworkID: network, + SkipPathCreation: false, + } + + cf := interchaintest.NewBuiltinChainFactory(zaptest.NewLogger(t), []*interchaintest.ChainSpec{ + { + Name: "ibc-go-simd", + ChainName: "ibc-go-simd", + Version: "v8.0.0", // SDK v50 + ChainConfig: ibc.ChainConfig{ + Denom: denomMetadata.Base, + Bech32Prefix: baseBech32, + CoinType: "118", + ModifyGenesis: cosmos.ModifyGenesis(sdk47Genesis), + GasAdjustment: 1.5, + OverrideGenesisStart: ibc.GenesisFileStart{ + GenesisFilePath: path.Join(t.Name(), "export.json"), // TODO: if this is not there, do we continue as normal? (or add an option here for PanicOnMissing) + Client: client, + NetworkID: network, + }, + }, + NumValidators: &numValsOne, + NumFullNodes: &numFullNodesZero, + }, + }) + fmt.Println("Chain Factory took", time.Since(now)) + + chains, err := cf.Chains(t.Name()) + require.NoError(t, err) + chainA := chains[0].(*cosmos.CosmosChain) + + ic := interchaintest.NewInterchain(). + AddChain(chainA) + + rep := testreporter.NewNopReporter() + eRep := rep.RelayerExecReporter(t) + + now = time.Now() + require.NoError(t, ic.Build(ctx, eRep, icOpts)) + + _, err = performExport(t, ctx, chainA, "export.json") + require.NoError(t, err) + + fmt.Println("Export", time.Since(now)) + + fmt.Println("Build", time.Since(now)) +} + +func performExport(t *testing.T, ctx context.Context, chain *cosmos.CosmosChain, fileName string) ([]byte, error) { + // TODO: do for every node in the network? (shouldnt be needed tbh) + val := chain.Validators[0] + + // perform export & save to the host machine + height, err := val.Height(ctx) + if err != nil { + return nil, err + } + + // stop node & wait\ + err = val.StopContainer(ctx) + require.NoError(t, err) + + output, err := val.ExportState(ctx, height) + if err != nil { + return nil, err + } + + cwd, err := os.Getwd() + require.NoError(t, err) + + p := path.Join(cwd, t.Name(), fileName) + + err = os.MkdirAll(path.Dir(p), 0755) + require.NoError(t, err) + + // save to output file + // namespace with the testname + err = os.WriteFile(p, []byte(output), 0644) + require.NoError(t, err) + + // start it back up + err = val.StartContainer(ctx) + require.NoError(t, err) + + // fr := dockerutil.NewFileWriter(zaptest.NewLogger(t), val.DockerClient, t.Name()) + // err = fr.WriteFile(ctx, val.VolumeName, "export.json", []byte(output)) + return []byte(output), err +} diff --git a/ibc/types.go b/ibc/types.go index d17415cbd..6bd1650fe 100644 --- a/ibc/types.go +++ b/ibc/types.go @@ -80,6 +80,14 @@ type ChainConfig struct { AdditionalStartArgs []string // Environment variables for chain nodes Env []string + // Genesis file path and information for the chain to start with. + OverrideGenesisStart GenesisFileStart `yaml:"genesis-file-start"` +} + +type GenesisFileStart struct { + GenesisFilePath string `yaml:"genesis-file-path"` + Client *client.Client `yaml:"-" json:"-"` + NetworkID string `yaml:"network-id"` } func (c ChainConfig) Clone() ChainConfig { @@ -231,6 +239,10 @@ func (c ChainConfig) MergeChainSpecConfig(other ChainConfig) ChainConfig { c.InterchainSecurityConfig = other.InterchainSecurityConfig } + if !cmp.Equal(other.OverrideGenesisStart, GenesisFileStart{}) { + c.OverrideGenesisStart = other.OverrideGenesisStart + } + return c }