feat(simapp): Testnet cmd for multi node on single host (#20075)
This commit is contained in:
parent
0110632ed7
commit
a9344ab02e
@ -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)
|
||||
|
||||
Loading…
Reference in New Issue
Block a user