0g-chain/gaia/init/testnet.go

386 lines
9.9 KiB
Go
Raw Normal View History

2019-06-20 13:37:57 +00:00
package init
2019-06-07 11:59:19 +00:00
// DONTCOVER
import (
"encoding/json"
"fmt"
"net"
"os"
"path/filepath"
2019-06-20 13:37:57 +00:00
"github.com/cosmos/cosmos-sdk/client/keys"
"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/cmd/gaia/app"
"github.com/cosmos/cosmos-sdk/codec"
srvconfig "github.com/cosmos/cosmos-sdk/server/config"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/auth"
authtx "github.com/cosmos/cosmos-sdk/x/auth/client/txbuilder"
"github.com/cosmos/cosmos-sdk/x/staking"
2019-06-07 11:59:19 +00:00
"github.com/spf13/cobra"
"github.com/spf13/viper"
tmconfig "github.com/tendermint/tendermint/config"
"github.com/tendermint/tendermint/crypto"
cmn "github.com/tendermint/tendermint/libs/common"
"github.com/tendermint/tendermint/types"
tmtime "github.com/tendermint/tendermint/types/time"
"github.com/cosmos/cosmos-sdk/server"
)
var (
flagNodeDirPrefix = "node-dir-prefix"
flagNumValidators = "v"
flagOutputDir = "output-dir"
flagNodeDaemonHome = "node-daemon-home"
2019-06-20 13:37:57 +00:00
flagNodeCliHome = "node-cli-home"
2019-06-07 11:59:19 +00:00
flagStartingIPAddress = "starting-ip-address"
)
2019-06-20 13:37:57 +00:00
const nodeDirPerm = 0755
2019-06-07 11:59:19 +00:00
// get cmd to initialize all files for tendermint testnet and application
2019-06-20 13:37:57 +00:00
func TestnetFilesCmd(ctx *server.Context, cdc *codec.Codec) *cobra.Command {
2019-06-07 11:59:19 +00:00
cmd := &cobra.Command{
Use: "testnet",
2019-06-20 13:37:57 +00:00
Short: "Initialize files for a Gaiad testnet",
2019-06-07 11:59:19 +00:00
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:
2019-06-20 13:37:57 +00:00
gaiad testnet --v 4 --output-dir ./output --starting-ip-address 192.168.10.2
2019-06-07 11:59:19 +00:00
`,
RunE: func(_ *cobra.Command, _ []string) error {
config := ctx.Config
2019-06-20 13:37:57 +00:00
return initTestnet(config, cdc)
2019-06-07 11:59:19 +00:00
},
}
cmd.Flags().Int(flagNumValidators, 4,
2019-06-20 13:37:57 +00:00
"Number of validators to initialize the testnet with",
)
2019-06-07 11:59:19 +00:00
cmd.Flags().StringP(flagOutputDir, "o", "./mytestnet",
2019-06-20 13:37:57 +00:00
"Directory to store initialization data for the testnet",
)
2019-06-07 11:59:19 +00:00
cmd.Flags().String(flagNodeDirPrefix, "node",
2019-06-20 13:37:57 +00:00
"Prefix the directory name for each node with (node results in node0, node1, ...)",
)
cmd.Flags().String(flagNodeDaemonHome, "gaiad",
"Home directory of the node's daemon configuration",
)
cmd.Flags().String(flagNodeCliHome, "gaiacli",
"Home directory of the node's cli configuration",
)
2019-06-07 11:59:19 +00:00
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, ...)")
2019-06-20 13:37:57 +00:00
2019-06-07 11:59:19 +00:00
cmd.Flags().String(
2019-06-20 13:37:57 +00:00
client.FlagChainID, "", "genesis file chain-id, if left blank will be randomly created",
)
2019-06-07 11:59:19 +00:00
cmd.Flags().String(
server.FlagMinGasPrices, fmt.Sprintf("0.000006%s", sdk.DefaultBondDenom),
2019-06-20 13:37:57 +00:00
"Minimum gas prices to accept for transactions; All fees in a tx must meet this minimum (e.g. 0.01photino,0.001stake)",
)
2019-06-07 11:59:19 +00:00
return cmd
}
2019-06-20 13:37:57 +00:00
func initTestnet(config *tmconfig.Config, cdc *codec.Codec) error {
var chainID string
2019-06-07 11:59:19 +00:00
2019-06-20 13:37:57 +00:00
outDir := viper.GetString(flagOutputDir)
numValidators := viper.GetInt(flagNumValidators)
2019-06-07 11:59:19 +00:00
2019-06-20 13:37:57 +00:00
chainID = viper.GetString(client.FlagChainID)
2019-06-07 11:59:19 +00:00
if chainID == "" {
chainID = "chain-" + cmn.RandStr(6)
}
monikers := make([]string, numValidators)
nodeIDs := make([]string, numValidators)
valPubKeys := make([]crypto.PubKey, numValidators)
2019-06-20 13:37:57 +00:00
gaiaConfig := srvconfig.DefaultConfig()
gaiaConfig.MinGasPrices = viper.GetString(server.FlagMinGasPrices)
2019-06-07 11:59:19 +00:00
var (
2019-06-20 13:37:57 +00:00
accs []app.GenesisAccount
2019-06-07 11:59:19 +00:00
genFiles []string
)
// generate private keys, node IDs, and initial transactions
for i := 0; i < numValidators; i++ {
2019-06-20 13:37:57 +00:00
nodeDirName := fmt.Sprintf("%s%d", viper.GetString(flagNodeDirPrefix), i)
nodeDaemonHomeName := viper.GetString(flagNodeDaemonHome)
nodeCliHomeName := viper.GetString(flagNodeCliHome)
nodeDir := filepath.Join(outDir, nodeDirName, nodeDaemonHomeName)
clientDir := filepath.Join(outDir, nodeDirName, nodeCliHomeName)
gentxsDir := filepath.Join(outDir, "gentxs")
2019-06-07 11:59:19 +00:00
config.SetRoot(nodeDir)
err := os.MkdirAll(filepath.Join(nodeDir, "config"), nodeDirPerm)
if err != nil {
2019-06-20 13:37:57 +00:00
_ = os.RemoveAll(outDir)
2019-06-07 11:59:19 +00:00
return err
}
err = os.MkdirAll(clientDir, nodeDirPerm)
if err != nil {
2019-06-20 13:37:57 +00:00
_ = os.RemoveAll(outDir)
2019-06-07 11:59:19 +00:00
return err
}
monikers = append(monikers, nodeDirName)
config.Moniker = nodeDirName
2019-06-20 13:37:57 +00:00
ip, err := getIP(i, viper.GetString(flagStartingIPAddress))
2019-06-07 11:59:19 +00:00
if err != nil {
2019-06-20 13:37:57 +00:00
_ = os.RemoveAll(outDir)
2019-06-07 11:59:19 +00:00
return err
}
2019-06-20 13:37:57 +00:00
nodeIDs[i], valPubKeys[i], err = InitializeNodeValidatorFiles(config)
2019-06-07 11:59:19 +00:00
if err != nil {
2019-06-20 13:37:57 +00:00
_ = os.RemoveAll(outDir)
2019-06-07 11:59:19 +00:00
return err
}
memo := fmt.Sprintf("%s@%s:26656", nodeIDs[i], ip)
genFiles = append(genFiles, config.GenesisFile())
buf := client.BufferStdin()
prompt := fmt.Sprintf(
2019-06-20 13:37:57 +00:00
"Password for account '%s' (default %s):", nodeDirName, app.DefaultKeyPass,
2019-06-07 11:59:19 +00:00
)
keyPass, err := client.GetPassword(prompt, buf)
if err != nil && keyPass != "" {
// An error was returned that either failed to read the password from
// STDIN or the given password is not empty but failed to meet minimum
// length requirements.
return err
}
if keyPass == "" {
2019-06-20 13:37:57 +00:00
keyPass = app.DefaultKeyPass
2019-06-07 11:59:19 +00:00
}
addr, secret, err := server.GenerateSaveCoinKey(clientDir, nodeDirName, keyPass, true)
if err != nil {
2019-06-20 13:37:57 +00:00
_ = os.RemoveAll(outDir)
2019-06-07 11:59:19 +00:00
return err
}
info := map[string]string{"secret": secret}
cliPrint, err := json.Marshal(info)
if err != nil {
return err
}
// save private key seed words
err = writeFile(fmt.Sprintf("%v.json", "key_seed"), clientDir, cliPrint)
if err != nil {
return err
}
accTokens := sdk.TokensFromTendermintPower(1000)
accStakingTokens := sdk.TokensFromTendermintPower(500)
2019-06-20 13:37:57 +00:00
accs = append(accs, app.GenesisAccount{
2019-06-07 11:59:19 +00:00
Address: addr,
Coins: sdk.Coins{
sdk.NewCoin(fmt.Sprintf("%stoken", nodeDirName), accTokens),
sdk.NewCoin(sdk.DefaultBondDenom, accStakingTokens),
},
})
valTokens := sdk.TokensFromTendermintPower(100)
msg := staking.NewMsgCreateValidator(
sdk.ValAddress(addr),
valPubKeys[i],
sdk.NewCoin(sdk.DefaultBondDenom, valTokens),
staking.NewDescription(nodeDirName, "", "", ""),
2019-06-20 13:37:57 +00:00
staking.NewCommissionMsg(sdk.ZeroDec(), sdk.ZeroDec(), sdk.ZeroDec()),
2019-06-07 11:59:19 +00:00
sdk.OneInt(),
)
kb, err := keys.NewKeyBaseFromDir(clientDir)
if err != nil {
return err
}
tx := auth.NewStdTx([]sdk.Msg{msg}, auth.StdFee{}, []auth.StdSignature{}, memo)
2019-06-20 13:37:57 +00:00
txBldr := authtx.NewTxBuilderFromCLI().WithChainID(chainID).WithMemo(memo).WithKeybase(kb)
2019-06-07 11:59:19 +00:00
2019-06-20 13:37:57 +00:00
signedTx, err := txBldr.SignStdTx(nodeDirName, app.DefaultKeyPass, tx, false)
2019-06-07 11:59:19 +00:00
if err != nil {
2019-06-20 13:37:57 +00:00
_ = os.RemoveAll(outDir)
2019-06-07 11:59:19 +00:00
return err
}
txBytes, err := cdc.MarshalJSON(signedTx)
if err != nil {
2019-06-20 13:37:57 +00:00
_ = os.RemoveAll(outDir)
2019-06-07 11:59:19 +00:00
return err
}
// gather gentxs folder
err = writeFile(fmt.Sprintf("%v.json", nodeDirName), gentxsDir, txBytes)
if err != nil {
2019-06-20 13:37:57 +00:00
_ = os.RemoveAll(outDir)
2019-06-07 11:59:19 +00:00
return err
}
2019-06-20 13:37:57 +00:00
gaiaConfigFilePath := filepath.Join(nodeDir, "config/gaiad.toml")
srvconfig.WriteConfigFile(gaiaConfigFilePath, gaiaConfig)
2019-06-07 11:59:19 +00:00
}
2019-06-20 13:37:57 +00:00
if err := initGenFiles(cdc, chainID, accs, genFiles, numValidators); err != nil {
2019-06-07 11:59:19 +00:00
return err
}
err := collectGenFiles(
cdc, config, chainID, monikers, nodeIDs, valPubKeys, numValidators,
2019-06-20 13:37:57 +00:00
outDir, viper.GetString(flagNodeDirPrefix), viper.GetString(flagNodeDaemonHome),
2019-06-07 11:59:19 +00:00
)
if err != nil {
return err
}
fmt.Printf("Successfully initialized %d node directories\n", numValidators)
return nil
}
2019-06-20 13:37:57 +00:00
func initGenFiles(
cdc *codec.Codec, chainID string, accs []app.GenesisAccount,
genFiles []string, numValidators int,
) error {
2019-06-07 11:59:19 +00:00
2019-06-20 13:37:57 +00:00
appGenState := app.NewDefaultGenesisState()
appGenState.Accounts = accs
2019-06-07 11:59:19 +00:00
appGenStateJSON, err := codec.MarshalJSONIndent(cdc, appGenState)
if err != nil {
return err
}
genDoc := types.GenesisDoc{
ChainID: chainID,
AppState: appGenStateJSON,
Validators: nil,
}
// generate empty genesis files for each validator and save
for i := 0; i < numValidators; i++ {
if err := genDoc.SaveAs(genFiles[i]); err != nil {
return err
}
}
2019-06-20 13:37:57 +00:00
2019-06-07 11:59:19 +00:00
return nil
}
func collectGenFiles(
cdc *codec.Codec, config *tmconfig.Config, chainID string,
monikers, nodeIDs []string, valPubKeys []crypto.PubKey,
2019-06-20 13:37:57 +00:00
numValidators int, outDir, nodeDirPrefix, nodeDaemonHomeName string,
) error {
2019-06-07 11:59:19 +00:00
var appState json.RawMessage
genTime := tmtime.Now()
for i := 0; i < numValidators; i++ {
nodeDirName := fmt.Sprintf("%s%d", nodeDirPrefix, i)
2019-06-20 13:37:57 +00:00
nodeDir := filepath.Join(outDir, nodeDirName, nodeDaemonHomeName)
gentxsDir := filepath.Join(outDir, "gentxs")
2019-06-07 11:59:19 +00:00
moniker := monikers[i]
config.Moniker = nodeDirName
config.SetRoot(nodeDir)
nodeID, valPubKey := nodeIDs[i], valPubKeys[i]
2019-06-20 13:37:57 +00:00
initCfg := newInitConfig(chainID, gentxsDir, moniker, nodeID, valPubKey)
2019-06-07 11:59:19 +00:00
2019-06-20 13:37:57 +00:00
genDoc, err := LoadGenesisDoc(cdc, config.GenesisFile())
2019-06-07 11:59:19 +00:00
if err != nil {
return err
}
2019-06-20 13:37:57 +00:00
nodeAppState, err := genAppStateFromConfig(cdc, config, initCfg, genDoc)
2019-06-07 11:59:19 +00:00
if err != nil {
return err
}
if appState == nil {
// set the canonical application state (they should not differ)
appState = nodeAppState
}
genFile := config.GenesisFile()
// overwrite each validator's genesis file to have a canonical genesis time
2019-06-20 13:37:57 +00:00
err = ExportGenesisFileWithTime(genFile, chainID, nil, appState, genTime)
2019-06-07 11:59:19 +00:00
if err != nil {
return err
}
}
return nil
}
2019-06-20 13:37:57 +00:00
func getIP(i int, startingIPAddr string) (string, error) {
var (
ip string
err error
)
2019-06-07 11:59:19 +00:00
if len(startingIPAddr) == 0 {
ip, err = server.ExternalIP()
if err != nil {
return "", err
}
2019-06-20 13:37:57 +00:00
} else {
ip, err = calculateIP(startingIPAddr, i)
if err != nil {
return "", err
}
2019-06-07 11:59:19 +00:00
}
2019-06-20 13:37:57 +00:00
return ip, nil
2019-06-07 11:59:19 +00:00
}
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
}
2019-06-20 13:37:57 +00:00
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
}