forked from cosmos/cosmos-sdk
-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(simapp): Testnet cmd for multi node on single host (cosmos#20075)
- Loading branch information
Showing
1 changed file
with
78 additions
and
25 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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 [email protected]:46656, [email protected]: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,21 +336,21 @@ 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 | ||
} | ||
valTokens := sdk.TokensFromConsensusPower(100, sdk.DefaultPowerReduction) | ||
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) | ||
|