diff --git a/simapp/simd/cmd/testnet.go b/simapp/simd/cmd/testnet.go index 957cc455e5..8068d765b0 100644 --- a/simapp/simd/cmd/testnet.go +++ b/simapp/simd/cmd/testnet.go @@ -7,6 +7,7 @@ import ( "net" "os" "path/filepath" + "time" cmtconfig "github.com/cometbft/cometbft/config" cmttime "github.com/cometbft/cometbft/types/time" @@ -32,6 +33,7 @@ import ( "github.com/cosmos/cosmos-sdk/testutil/network" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/types/module" + "github.com/cosmos/cosmos-sdk/version" "github.com/cosmos/cosmos-sdk/x/genutil" genutiltypes "github.com/cosmos/cosmos-sdk/x/genutil/types" ) @@ -48,6 +50,9 @@ var ( flagRPCAddress = "rpc.address" flagAPIAddress = "api.address" flagPrintMnemonic = "print-mnemonic" + flagStakingDenom = "staking-denom" + flagCommitTimeout = "commit-timeout" + flagSingleHost = "single-host" ) type initArgs struct { @@ -61,6 +66,8 @@ type initArgs struct { outputDir string startingIPAddress string listenIPAddress string + singleMachine bool + bondTokenDenom string } type startArgs struct { @@ -74,6 +81,7 @@ type startArgs struct { outputDir string printMnemonic bool rpcAddress string + timeoutCommit time.Duration } func addTestnetFlagsToCmd(cmd *cobra.Command) { @@ -115,7 +123,7 @@ func testnetInitFilesCmd(mm *module.Manager, genBalIterator banktypes.GenesisBal cmd := &cobra.Command{ Use: "init-files", Short: "Initialize config directories & files for a multi-validator testnet running locally via separate processes (e.g. Docker Compose or similar)", - Long: `init-files will setup one directory per validator and populate each with + Long: fmt.Sprintf(`init-files will setup one directory per validator and populate each with necessary files (private validator, genesis, config, etc.) for running validator nodes. Booting up a network with these validator folders is intended to be used with Docker Compose, @@ -124,8 +132,8 @@ or a similar setup where each node has a manually configurable IP address. Note, strict routability for addresses is turned off in the config file. Example: - simd testnet init-files -n 4 --output-dir ./.testnets --starting-ip-address 192.168.10.2 - `, + %s testnet init-files --validator-count 4 --output-dir ./.testnets --starting-ip-address 192.168.10.2 + `, version.AppName), RunE: func(cmd *cobra.Command, _ []string) error { clientCtx, err := client.GetClientQueryContext(cmd) if err != nil { @@ -146,6 +154,12 @@ Example: args.listenIPAddress, _ = cmd.Flags().GetString(flagListenIPAddress) args.numValidators, _ = cmd.Flags().GetInt(flagNumValidators) args.algo, _ = cmd.Flags().GetString(flags.FlagKeyType) + args.bondTokenDenom, _ = cmd.Flags().GetString(flagStakingDenom) + args.singleMachine, _ = cmd.Flags().GetBool(flagSingleHost) + config.Consensus.TimeoutCommit, err = cmd.Flags().GetDuration(flagCommitTimeout) + if err != nil { + return err + } return initTestnetFiles(clientCtx, cmd, config, mm, genBalIterator, args) }, @@ -157,6 +171,9 @@ Example: cmd.Flags().String(flagStartingIPAddress, "192.168.0.1", "Starting IP address (192.168.0.1 results in persistent peers list ID0@192.168.0.1:46656, ID1@192.168.0.2:46656, ...)") cmd.Flags().String(flagListenIPAddress, "127.0.0.1", "TCP or UNIX socket IP address for the RPC server to listen on") cmd.Flags().String(flags.FlagKeyringBackend, flags.DefaultKeyringBackend, "Select keyring's backend (os|file|test)") + cmd.Flags().Duration(flagCommitTimeout, 5*time.Second, "Time to wait after a block commit before starting on the new height") + cmd.Flags().Bool(flagSingleHost, false, "Cluster runs on a single host machine with different ports") + cmd.Flags().String(flagStakingDenom, sdk.DefaultBondDenom, "Default staking token denominator") return cmd } @@ -166,14 +183,14 @@ func testnetStartCmd() *cobra.Command { cmd := &cobra.Command{ Use: "start", Short: "Launch an in-process multi-validator testnet", - Long: `testnet will launch an in-process multi-validator testnet, + Long: fmt.Sprintf(`testnet will launch an in-process multi-validator testnet, and generate a directory for each validator populated with necessary configuration files (private validator, genesis, config, etc.). Example: - simd testnet -n 4 --output-dir ./.testnets - `, - RunE: func(cmd *cobra.Command, _ []string) error { + %s testnet --validator-count4 --output-dir ./.testnets + `, version.AppName), + RunE: func(cmd *cobra.Command, _ []string) (err error) { args := startArgs{} args.outputDir, _ = cmd.Flags().GetString(flagOutputDir) args.chainID, _ = cmd.Flags().GetString(flags.FlagChainID) @@ -216,39 +233,65 @@ func initTestnetFiles( nodeIDs := make([]string, args.numValidators) valPubKeys := make([]cryptotypes.PubKey, args.numValidators) - simappConfig := srvconfig.DefaultConfig() - simappConfig.MinGasPrices = args.minGasPrices - simappConfig.API.Enable = true - simappConfig.Telemetry.Enabled = true - simappConfig.Telemetry.PrometheusRetentionTime = 60 - simappConfig.Telemetry.EnableHostnameLabel = false - simappConfig.Telemetry.GlobalLabels = [][]string{{"chain_id", args.chainID}} + appConfig := srvconfig.DefaultConfig() + appConfig.MinGasPrices = args.minGasPrices + appConfig.API.Enable = true + appConfig.Telemetry.Enabled = true + appConfig.Telemetry.PrometheusRetentionTime = 60 + appConfig.Telemetry.EnableHostnameLabel = false + appConfig.Telemetry.GlobalLabels = [][]string{{"chain_id", args.chainID}} var ( genAccounts []authtypes.GenesisAccount genBalances []banktypes.Balance genFiles []string ) + const ( + rpcPort = 26657 + apiPort = 1317 + grpcPort = 9090 + ) + p2pPortStart := 26656 inBuf := bufio.NewReader(cmd.InOrStdin()) // generate private keys, node IDs, and initial transactions for i := 0; i < args.numValidators; i++ { + var portOffset int + if args.singleMachine { + portOffset = i + p2pPortStart = 16656 // use different start point to not conflict with rpc port + nodeConfig.P2P.AddrBookStrict = false + nodeConfig.P2P.PexReactor = false + nodeConfig.P2P.AllowDuplicateIP = true + appConfig.API.Address = fmt.Sprintf("tcp://127.0.0.1:%d", apiPort+portOffset) + appConfig.GRPC.Address = fmt.Sprintf("127.0.0.1:%d", grpcPort+portOffset) + appConfig.GRPCWeb.Enable = true + } + nodeDirName := fmt.Sprintf("%s%d", args.nodeDirPrefix, i) nodeDir := filepath.Join(args.outputDir, nodeDirName, args.nodeDaemonHome) gentxsDir := filepath.Join(args.outputDir, "gentxs") nodeConfig.SetRoot(nodeDir) nodeConfig.Moniker = nodeDirName - nodeConfig.RPC.ListenAddress = fmt.Sprintf("tcp://%s:26657", args.listenIPAddress) + nodeConfig.RPC.ListenAddress = fmt.Sprintf("tcp://%s:%d", args.listenIPAddress, rpcPort+portOffset) + if err := os.MkdirAll(filepath.Join(nodeDir, "config"), nodeDirPerm); err != nil { _ = os.RemoveAll(args.outputDir) return err } - - ip, err := getIP(i, args.startingIPAddress) - if err != nil { - _ = os.RemoveAll(args.outputDir) - return err + var ( + err error + ip string + ) + if args.singleMachine { + ip = "127.0.0.1" + } else { + ip, err = getIP(i, args.startingIPAddress) + if err != nil { + _ = os.RemoveAll(args.outputDir) + return err + } } nodeIDs[i], valPubKeys[i], err = genutil.InitializeNodeValidatorFiles(nodeConfig) @@ -257,7 +300,7 @@ func initTestnetFiles( return err } - memo := fmt.Sprintf("%s@%s:26656", nodeIDs[i], ip) + memo := fmt.Sprintf("%s@%s:%d", nodeIDs[i], ip, p2pPortStart+portOffset) genFiles = append(genFiles, nodeConfig.GenesisFile()) kb, err := keyring.New(sdk.KeyringServiceName(), args.keyringBackend, nodeDir, inBuf, clientCtx.Codec) @@ -293,13 +336,13 @@ func initTestnetFiles( accStakingTokens := sdk.TokensFromConsensusPower(500, sdk.DefaultPowerReduction) coins := sdk.Coins{ sdk.NewCoin("testtoken", accTokens), - sdk.NewCoin(sdk.DefaultBondDenom, accStakingTokens), + sdk.NewCoin(args.bondTokenDenom, accStakingTokens), } genBalances = append(genBalances, banktypes.Balance{Address: addr.String(), Coins: coins.Sort()}) genAccounts = append(genAccounts, authtypes.NewBaseAccount(addr, nil, 0, 0)) - valStr, err := clientCtx.ValidatorAddressCodec.BytesToString(sdk.ValAddress(addr)) + valStr, err := clientCtx.ValidatorAddressCodec.BytesToString(addr) if err != nil { return err } @@ -307,7 +350,7 @@ func initTestnetFiles( createValMsg, err := stakingtypes.NewMsgCreateValidator( valStr, valPubKeys[i], - sdk.NewCoin(sdk.DefaultBondDenom, valTokens), + sdk.NewCoin(args.bondTokenDenom, valTokens), stakingtypes.NewDescription(nodeDirName, "", "", "", ""), stakingtypes.NewCommissionRates(math.LegacyOneDec(), math.LegacyOneDec(), math.LegacyOneDec()), math.OneInt(), @@ -347,7 +390,7 @@ func initTestnetFiles( return err } - if err := srvconfig.WriteConfigFile(filepath.Join(nodeDir, "config", "app.toml"), simappConfig); err != nil { + if err := srvconfig.WriteConfigFile(filepath.Join(nodeDir, "config", "app.toml"), appConfig); err != nil { return err } } @@ -359,6 +402,7 @@ func initTestnetFiles( err := collectGenFiles( clientCtx, nodeConfig, args.chainID, nodeIDs, valPubKeys, args.numValidators, args.outputDir, args.nodeDirPrefix, args.nodeDaemonHome, genBalIterator, + rpcPort, p2pPortStart, args.singleMachine, ) if err != nil { return err @@ -419,11 +463,19 @@ func collectGenFiles( clientCtx client.Context, nodeConfig *cmtconfig.Config, chainID string, nodeIDs []string, valPubKeys []cryptotypes.PubKey, numValidators int, outputDir, nodeDirPrefix, nodeDaemonHome string, genBalIterator banktypes.GenesisBalancesIterator, + rpcPortStart, p2pPortStart int, + singleMachine bool, ) error { var appState json.RawMessage genTime := cmttime.Now() for i := 0; i < numValidators; i++ { + if singleMachine { + portOffset := i + nodeConfig.RPC.ListenAddress = fmt.Sprintf("tcp://127.0.0.1:%d", rpcPortStart+portOffset) + nodeConfig.P2P.ListenAddress = fmt.Sprintf("tcp://127.0.0.1:%d", p2pPortStart+portOffset) + } + nodeDirName := fmt.Sprintf("%s%d", nodeDirPrefix, i) nodeDir := filepath.Join(outputDir, nodeDirName, nodeDaemonHome) gentxsDir := filepath.Join(outputDir, "gentxs") @@ -516,6 +568,7 @@ func startTestnet(cmd *cobra.Command, args startArgs) error { networkConfig.APIAddress = args.apiAddress networkConfig.GRPCAddress = args.grpcAddress networkConfig.PrintMnemonic = args.printMnemonic + networkConfig.TimeoutCommit = args.timeoutCommit networkLogger := network.NewCLILogger(cmd) baseDir := fmt.Sprintf("%s/%s", args.outputDir, networkConfig.ChainID)