188 lines
4.5 KiB
Go
188 lines
4.5 KiB
Go
package server
|
|
|
|
import (
|
|
"fmt"
|
|
"net"
|
|
"path/filepath"
|
|
|
|
"github.com/spf13/cobra"
|
|
|
|
gc "github.com/cosmos/cosmos-sdk/server/config"
|
|
|
|
"os"
|
|
|
|
"github.com/cosmos/cosmos-sdk/wire"
|
|
"github.com/spf13/viper"
|
|
cfg "github.com/tendermint/tendermint/config"
|
|
cmn "github.com/tendermint/tendermint/libs/common"
|
|
)
|
|
|
|
var (
|
|
nodeDirPrefix = "node-dir-prefix"
|
|
nValidators = "v"
|
|
outputDir = "output-dir"
|
|
|
|
startingIPAddress = "starting-ip-address"
|
|
)
|
|
|
|
const nodeDirPerm = 0755
|
|
|
|
// get cmd to initialize all files for tendermint testnet and application
|
|
func TestnetFilesCmd(ctx *Context, cdc *wire.Codec, appInit AppInit) *cobra.Command {
|
|
cmd := &cobra.Command{
|
|
Use: "testnet",
|
|
Short: "Initialize files for a Gaiad testnet",
|
|
Long: `testnet will create "v" number of directories and populate each with
|
|
necessary files (private validator, genesis, config, etc.).
|
|
|
|
Note, strict routability for addresses is turned off in the config file.
|
|
|
|
Example:
|
|
|
|
gaiad testnet --v 4 --output-dir ./output --starting-ip-address 192.168.10.2
|
|
`,
|
|
RunE: func(_ *cobra.Command, _ []string) error {
|
|
config := ctx.Config
|
|
err := testnetWithConfig(config, cdc, appInit)
|
|
return err
|
|
},
|
|
}
|
|
cmd.Flags().Int(nValidators, 4,
|
|
"Number of validators to initialize the testnet with")
|
|
cmd.Flags().StringP(outputDir, "o", "./mytestnet",
|
|
"Directory to store initialization data for the testnet")
|
|
cmd.Flags().String(nodeDirPrefix, "node",
|
|
"Prefix the directory name for each node with (node results in node0, node1, ...)")
|
|
|
|
cmd.Flags().String(startingIPAddress, "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, ...)")
|
|
return cmd
|
|
}
|
|
|
|
func testnetWithConfig(config *cfg.Config, cdc *wire.Codec, appInit AppInit) error {
|
|
outDir := viper.GetString(outputDir)
|
|
numValidators := viper.GetInt(nValidators)
|
|
|
|
// Generate private key, node ID, initial transaction
|
|
for i := 0; i < numValidators; i++ {
|
|
nodeDirName := fmt.Sprintf("%s%d", viper.GetString(nodeDirPrefix), i)
|
|
nodeDir := filepath.Join(outDir, nodeDirName, "gaiad")
|
|
clientDir := filepath.Join(outDir, nodeDirName, "gaiacli")
|
|
gentxsDir := filepath.Join(outDir, "gentxs")
|
|
config.SetRoot(nodeDir)
|
|
|
|
err := os.MkdirAll(filepath.Join(nodeDir, "config"), nodeDirPerm)
|
|
if err != nil {
|
|
_ = os.RemoveAll(outDir)
|
|
return err
|
|
}
|
|
|
|
err = os.MkdirAll(clientDir, nodeDirPerm)
|
|
if err != nil {
|
|
_ = os.RemoveAll(outDir)
|
|
return err
|
|
}
|
|
|
|
config.Moniker = nodeDirName
|
|
ip, err := getIP(i)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
genTxConfig := gc.GenTx{
|
|
nodeDirName,
|
|
clientDir,
|
|
true,
|
|
ip,
|
|
}
|
|
|
|
// Run `init gen-tx` and generate initial transactions
|
|
cliPrint, genTxFile, err := gentxWithConfig(cdc, appInit, config, genTxConfig)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// Save private key seed words
|
|
name := fmt.Sprintf("%v.json", "key_seed")
|
|
err = writeFile(name, clientDir, cliPrint)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// Gather gentxs folder
|
|
name = fmt.Sprintf("%v.json", nodeDirName)
|
|
err = writeFile(name, gentxsDir, genTxFile)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
// Generate genesis.json and config.toml
|
|
chainID := "chain-" + cmn.RandStr(6)
|
|
for i := 0; i < numValidators; i++ {
|
|
|
|
nodeDirName := fmt.Sprintf("%s%d", viper.GetString(nodeDirPrefix), i)
|
|
nodeDir := filepath.Join(outDir, nodeDirName, "gaiad")
|
|
gentxsDir := filepath.Join(outDir, "gentxs")
|
|
initConfig := InitConfig{
|
|
chainID,
|
|
true,
|
|
gentxsDir,
|
|
true,
|
|
}
|
|
config.Moniker = nodeDirName
|
|
config.SetRoot(nodeDir)
|
|
|
|
// Run `init` and generate genesis.json and config.toml
|
|
_, _, _, err := initWithConfig(cdc, appInit, config, initConfig)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
fmt.Printf("Successfully initialized %v node directories\n", viper.GetInt(nValidators))
|
|
return nil
|
|
}
|
|
|
|
func getIP(i int) (ip string, err error) {
|
|
ip = viper.GetString(startingIPAddress)
|
|
if len(ip) == 0 {
|
|
ip, err = externalIP()
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
} else {
|
|
ip, err = calculateIP(ip, i)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
}
|
|
return ip, nil
|
|
}
|
|
|
|
func writeFile(name string, dir string, contents []byte) error {
|
|
writePath := filepath.Join(dir)
|
|
file := filepath.Join(writePath, name)
|
|
err := cmn.EnsureDir(writePath, 0700)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
err = cmn.WriteFile(file, contents, 0600)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func calculateIP(ip string, i int) (string, error) {
|
|
ipv4 := net.ParseIP(ip).To4()
|
|
if ipv4 == nil {
|
|
return "", fmt.Errorf("%v: non ipv4 address", ip)
|
|
}
|
|
|
|
for j := 0; j < i; j++ {
|
|
ipv4[3]++
|
|
}
|
|
return ipv4.String(), nil
|
|
}
|