2020-07-07 08:40:46 -07:00
package cmd
2020-06-12 09:23:08 -07:00
// DONTCOVER
import (
"bufio"
"encoding/json"
"fmt"
"net"
"os"
"path/filepath"
"github.com/spf13/cobra"
tmconfig "github.com/tendermint/tendermint/config"
tmos "github.com/tendermint/tendermint/libs/os"
tmrand "github.com/tendermint/tendermint/libs/rand"
"github.com/tendermint/tendermint/types"
tmtime "github.com/tendermint/tendermint/types/time"
2020-07-07 08:40:46 -07:00
"github.com/cosmos/cosmos-sdk/client"
2020-06-12 09:23:08 -07:00
"github.com/cosmos/cosmos-sdk/client/flags"
2020-08-04 09:51:27 -07:00
"github.com/cosmos/cosmos-sdk/client/tx"
2020-08-12 01:34:10 -07:00
"github.com/cosmos/cosmos-sdk/crypto/hd"
2020-06-12 09:23:08 -07:00
"github.com/cosmos/cosmos-sdk/crypto/keyring"
2020-11-09 08:01:43 -08:00
cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types"
2020-06-12 09:23:08 -07:00
"github.com/cosmos/cosmos-sdk/server"
srvconfig "github.com/cosmos/cosmos-sdk/server/config"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/types/module"
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
2020-06-14 16:06:16 -07:00
banktypes "github.com/cosmos/cosmos-sdk/x/bank/types"
2020-06-12 09:23:08 -07:00
"github.com/cosmos/cosmos-sdk/x/genutil"
2020-06-13 00:41:45 -07:00
genutiltypes "github.com/cosmos/cosmos-sdk/x/genutil/types"
2020-06-12 09:23:08 -07:00
stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types"
)
var (
flagNodeDirPrefix = "node-dir-prefix"
flagNumValidators = "v"
flagOutputDir = "output-dir"
flagNodeDaemonHome = "node-daemon-home"
flagStartingIPAddress = "starting-ip-address"
)
// get cmd to initialize all files for tendermint testnet and application
2020-07-07 08:40:46 -07:00
func testnetCmd ( mbm module . BasicManager , genBalIterator banktypes . GenesisBalancesIterator ) * cobra . Command {
2020-06-12 09:23:08 -07:00
cmd := & cobra . Command {
Use : "testnet" ,
Short : "Initialize files for a simapp 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 :
simd testnet -- v 4 -- output - dir . / output -- starting - ip - address 192.168 .10 .2
` ,
RunE : func ( cmd * cobra . Command , _ [ ] string ) error {
2020-07-07 08:40:46 -07:00
clientCtx := client . GetClientContextFromCmd ( cmd )
serverCtx := server . GetServerContextFromCmd ( cmd )
config := serverCtx . Config
2020-06-12 09:23:08 -07:00
2020-07-05 09:56:17 -07:00
outputDir , _ := cmd . Flags ( ) . GetString ( flagOutputDir )
keyringBackend , _ := cmd . Flags ( ) . GetString ( flags . FlagKeyringBackend )
chainID , _ := cmd . Flags ( ) . GetString ( flags . FlagChainID )
minGasPrices , _ := cmd . Flags ( ) . GetString ( server . FlagMinGasPrices )
nodeDirPrefix , _ := cmd . Flags ( ) . GetString ( flagNodeDirPrefix )
nodeDaemonHome , _ := cmd . Flags ( ) . GetString ( flagNodeDaemonHome )
startingIPAddress , _ := cmd . Flags ( ) . GetString ( flagStartingIPAddress )
numValidators , _ := cmd . Flags ( ) . GetInt ( flagNumValidators )
2020-08-12 01:34:10 -07:00
algo , _ := cmd . Flags ( ) . GetString ( flags . FlagKeyAlgorithm )
2020-06-12 09:23:08 -07:00
return InitTestnet (
2020-08-04 09:51:27 -07:00
clientCtx , cmd , config , mbm , genBalIterator , outputDir , chainID , minGasPrices ,
2020-09-12 07:03:20 -07:00
nodeDirPrefix , nodeDaemonHome , startingIPAddress , keyringBackend , algo , numValidators ,
2020-06-12 09:23:08 -07:00
)
} ,
}
2020-07-05 09:56:17 -07:00
cmd . Flags ( ) . Int ( flagNumValidators , 4 , "Number of validators to initialize the testnet with" )
cmd . Flags ( ) . StringP ( flagOutputDir , "o" , "./mytestnet" , "Directory to store initialization data for the testnet" )
cmd . Flags ( ) . String ( flagNodeDirPrefix , "node" , "Prefix the directory name for each node with (node results in node0, node1, ...)" )
cmd . Flags ( ) . String ( flagNodeDaemonHome , "simd" , "Home directory of the node's daemon configuration" )
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 ( flags . FlagChainID , "" , "genesis file chain-id, if left blank will be randomly created" )
cmd . Flags ( ) . String ( server . FlagMinGasPrices , fmt . Sprintf ( "0.000006%s" , sdk . DefaultBondDenom ) , "Minimum gas prices to accept for transactions; All fees in a tx must meet this minimum (e.g. 0.01photino,0.001stake)" )
2020-06-12 09:23:08 -07:00
cmd . Flags ( ) . String ( flags . FlagKeyringBackend , flags . DefaultKeyringBackend , "Select keyring's backend (os|file|test)" )
2020-08-12 01:34:10 -07:00
cmd . Flags ( ) . String ( flags . FlagKeyAlgorithm , string ( hd . Secp256k1Type ) , "Key signing algorithm to generate keys for" )
2020-06-12 09:23:08 -07:00
return cmd
}
const nodeDirPerm = 0755
// Initialize the testnet
func InitTestnet (
2020-08-12 01:34:10 -07:00
clientCtx client . Context ,
cmd * cobra . Command ,
nodeConfig * tmconfig . Config ,
mbm module . BasicManager ,
genBalIterator banktypes . GenesisBalancesIterator ,
outputDir ,
chainID ,
minGasPrices ,
nodeDirPrefix ,
nodeDaemonHome ,
startingIPAddress ,
keyringBackend ,
algoStr string ,
numValidators int ,
2020-06-12 09:23:08 -07:00
) error {
if chainID == "" {
chainID = "chain-" + tmrand . NewRand ( ) . Str ( 6 )
}
nodeIDs := make ( [ ] string , numValidators )
2020-11-09 08:01:43 -08:00
valPubKeys := make ( [ ] cryptotypes . PubKey , numValidators )
2020-06-12 09:23:08 -07:00
simappConfig := srvconfig . DefaultConfig ( )
simappConfig . MinGasPrices = minGasPrices
2020-06-15 10:39:09 -07:00
simappConfig . API . Enable = true
2020-06-16 08:11:02 -07:00
simappConfig . Telemetry . Enabled = true
simappConfig . Telemetry . PrometheusRetentionTime = 60
simappConfig . Telemetry . EnableHostnameLabel = false
2020-06-18 11:12:44 -07:00
simappConfig . Telemetry . GlobalLabels = [ ] [ ] string { { "chain_id" , chainID } }
2020-06-12 09:23:08 -07:00
var (
genAccounts [ ] authtypes . GenesisAccount
2020-06-14 16:06:16 -07:00
genBalances [ ] banktypes . Balance
2020-06-12 09:23:08 -07:00
genFiles [ ] string
)
inBuf := bufio . NewReader ( cmd . InOrStdin ( ) )
// generate private keys, node IDs, and initial transactions
for i := 0 ; i < numValidators ; i ++ {
nodeDirName := fmt . Sprintf ( "%s%d" , nodeDirPrefix , i )
nodeDir := filepath . Join ( outputDir , nodeDirName , nodeDaemonHome )
gentxsDir := filepath . Join ( outputDir , "gentxs" )
2020-07-25 01:10:04 -07:00
nodeConfig . SetRoot ( nodeDir )
nodeConfig . RPC . ListenAddress = "tcp://0.0.0.0:26657"
2020-06-12 09:23:08 -07:00
if err := os . MkdirAll ( filepath . Join ( nodeDir , "config" ) , nodeDirPerm ) ; err != nil {
_ = os . RemoveAll ( outputDir )
return err
}
2020-07-25 01:10:04 -07:00
nodeConfig . Moniker = nodeDirName
2020-06-12 09:23:08 -07:00
ip , err := getIP ( i , startingIPAddress )
if err != nil {
_ = os . RemoveAll ( outputDir )
return err
}
2020-07-25 01:10:04 -07:00
nodeIDs [ i ] , valPubKeys [ i ] , err = genutil . InitializeNodeValidatorFiles ( nodeConfig )
2020-06-12 09:23:08 -07:00
if err != nil {
_ = os . RemoveAll ( outputDir )
return err
}
memo := fmt . Sprintf ( "%s@%s:26656" , nodeIDs [ i ] , ip )
2020-07-25 01:10:04 -07:00
genFiles = append ( genFiles , nodeConfig . GenesisFile ( ) )
2020-06-12 09:23:08 -07:00
2020-09-12 07:03:20 -07:00
kb , err := keyring . New ( sdk . KeyringServiceName ( ) , keyringBackend , nodeDir , inBuf )
2020-06-12 09:23:08 -07:00
if err != nil {
return err
}
2020-08-12 01:34:10 -07:00
keyringAlgos , _ := kb . SupportedAlgorithms ( )
algo , err := keyring . NewSigningAlgoFromString ( algoStr , keyringAlgos )
if err != nil {
return err
}
addr , secret , err := server . GenerateSaveCoinKey ( kb , nodeDirName , true , algo )
2020-06-12 09:23:08 -07:00
if err != nil {
_ = os . RemoveAll ( outputDir )
return err
}
info := map [ string ] string { "secret" : secret }
cliPrint , err := json . Marshal ( info )
if err != nil {
return err
}
// save private key seed words
2020-09-12 07:03:20 -07:00
if err := writeFile ( fmt . Sprintf ( "%v.json" , "key_seed" ) , nodeDir , cliPrint ) ; err != nil {
2020-06-12 09:23:08 -07:00
return err
}
accTokens := sdk . TokensFromConsensusPower ( 1000 )
accStakingTokens := sdk . TokensFromConsensusPower ( 500 )
coins := sdk . Coins {
sdk . NewCoin ( fmt . Sprintf ( "%stoken" , nodeDirName ) , accTokens ) ,
sdk . NewCoin ( sdk . DefaultBondDenom , accStakingTokens ) ,
}
2020-09-25 03:25:37 -07:00
genBalances = append ( genBalances , banktypes . Balance { Address : addr . String ( ) , Coins : coins . Sort ( ) } )
2020-06-17 11:42:27 -07:00
genAccounts = append ( genAccounts , authtypes . NewBaseAccount ( addr , nil , 0 , 0 ) )
2020-06-12 09:23:08 -07:00
valTokens := sdk . TokensFromConsensusPower ( 100 )
2020-10-19 06:04:55 -07:00
createValMsg , err := stakingtypes . NewMsgCreateValidator (
2020-06-12 09:23:08 -07:00
sdk . ValAddress ( addr ) ,
valPubKeys [ i ] ,
sdk . NewCoin ( sdk . DefaultBondDenom , valTokens ) ,
stakingtypes . NewDescription ( nodeDirName , "" , "" , "" , "" ) ,
stakingtypes . NewCommissionRates ( sdk . OneDec ( ) , sdk . OneDec ( ) , sdk . OneDec ( ) ) ,
sdk . OneInt ( ) ,
)
2020-10-19 06:04:55 -07:00
if err != nil {
return err
}
2020-06-12 09:23:08 -07:00
2020-08-04 09:51:27 -07:00
txBuilder := clientCtx . TxConfig . NewTxBuilder ( )
if err := txBuilder . SetMsgs ( createValMsg ) ; err != nil {
return err
2020-07-10 02:05:35 -07:00
}
2020-07-17 13:20:45 -07:00
2020-08-04 09:51:27 -07:00
txBuilder . SetMemo ( memo )
2020-06-12 09:23:08 -07:00
2020-08-04 09:51:27 -07:00
txFactory := tx . Factory { }
txFactory = txFactory .
WithChainID ( chainID ) .
WithMemo ( memo ) .
WithKeybase ( kb ) .
WithTxConfig ( clientCtx . TxConfig )
if err := tx . Sign ( txFactory , nodeDirName , txBuilder ) ; err != nil {
2020-06-12 09:23:08 -07:00
return err
}
2020-08-04 09:51:27 -07:00
txBz , err := clientCtx . TxConfig . TxJSONEncoder ( ) ( txBuilder . GetTx ( ) )
2020-06-12 09:23:08 -07:00
if err != nil {
return err
}
2020-08-04 09:51:27 -07:00
if err := writeFile ( fmt . Sprintf ( "%v.json" , nodeDirName ) , gentxsDir , txBz ) ; err != nil {
2020-06-12 09:23:08 -07:00
return err
}
srvconfig . WriteConfigFile ( filepath . Join ( nodeDir , "config/app.toml" ) , simappConfig )
}
2020-08-04 09:51:27 -07:00
if err := initGenFiles ( clientCtx , mbm , chainID , genAccounts , genBalances , genFiles , numValidators ) ; err != nil {
2020-06-12 09:23:08 -07:00
return err
}
err := collectGenFiles (
2020-08-04 09:51:27 -07:00
clientCtx , nodeConfig , chainID , nodeIDs , valPubKeys , numValidators ,
2020-06-12 09:23:08 -07:00
outputDir , nodeDirPrefix , nodeDaemonHome , genBalIterator ,
)
if err != nil {
return err
}
cmd . PrintErrf ( "Successfully initialized %d node directories\n" , numValidators )
return nil
}
func initGenFiles (
2020-08-04 09:51:27 -07:00
clientCtx client . Context , mbm module . BasicManager , chainID string ,
2020-06-14 16:06:16 -07:00
genAccounts [ ] authtypes . GenesisAccount , genBalances [ ] banktypes . Balance ,
2020-06-12 09:23:08 -07:00
genFiles [ ] string , numValidators int ,
) error {
2020-08-04 09:51:27 -07:00
appGenState := mbm . DefaultGenesis ( clientCtx . JSONMarshaler )
2020-06-12 09:23:08 -07:00
// set the accounts in the genesis state
2020-06-17 11:42:27 -07:00
var authGenState authtypes . GenesisState
2020-08-04 09:51:27 -07:00
clientCtx . JSONMarshaler . MustUnmarshalJSON ( appGenState [ authtypes . ModuleName ] , & authGenState )
2020-06-12 09:23:08 -07:00
2020-07-29 09:00:15 -07:00
accounts , err := authtypes . PackAccounts ( genAccounts )
if err != nil {
return err
}
authGenState . Accounts = accounts
2020-08-17 08:23:37 -07:00
appGenState [ authtypes . ModuleName ] = clientCtx . JSONMarshaler . MustMarshalJSON ( & authGenState )
2020-06-12 09:23:08 -07:00
// set the balances in the genesis state
2020-06-14 16:06:16 -07:00
var bankGenState banktypes . GenesisState
2020-08-04 09:51:27 -07:00
clientCtx . JSONMarshaler . MustUnmarshalJSON ( appGenState [ banktypes . ModuleName ] , & bankGenState )
2020-06-12 09:23:08 -07:00
bankGenState . Balances = genBalances
2020-08-17 08:23:37 -07:00
appGenState [ banktypes . ModuleName ] = clientCtx . JSONMarshaler . MustMarshalJSON ( & bankGenState )
2020-06-12 09:23:08 -07:00
2020-08-17 08:23:37 -07:00
appGenStateJSON , err := json . MarshalIndent ( appGenState , "" , " " )
2020-06-12 09:23:08 -07:00
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
}
}
return nil
}
func collectGenFiles (
2020-08-04 09:51:27 -07:00
clientCtx client . Context , nodeConfig * tmconfig . Config , chainID string ,
2020-11-09 08:01:43 -08:00
nodeIDs [ ] string , valPubKeys [ ] cryptotypes . PubKey , numValidators int ,
2020-08-04 09:51:27 -07:00
outputDir , nodeDirPrefix , nodeDaemonHome string , genBalIterator banktypes . GenesisBalancesIterator ,
2020-06-12 09:23:08 -07:00
) error {
var appState json . RawMessage
genTime := tmtime . Now ( )
for i := 0 ; i < numValidators ; i ++ {
nodeDirName := fmt . Sprintf ( "%s%d" , nodeDirPrefix , i )
nodeDir := filepath . Join ( outputDir , nodeDirName , nodeDaemonHome )
gentxsDir := filepath . Join ( outputDir , "gentxs" )
2020-07-25 01:10:04 -07:00
nodeConfig . Moniker = nodeDirName
2020-06-12 09:23:08 -07:00
2020-07-25 01:10:04 -07:00
nodeConfig . SetRoot ( nodeDir )
2020-06-12 09:23:08 -07:00
nodeID , valPubKey := nodeIDs [ i ] , valPubKeys [ i ]
2020-07-06 14:48:54 -07:00
initCfg := genutiltypes . NewInitConfig ( chainID , gentxsDir , nodeID , valPubKey )
2020-06-12 09:23:08 -07:00
2020-07-25 01:10:04 -07:00
genDoc , err := types . GenesisDocFromFile ( nodeConfig . GenesisFile ( ) )
2020-06-12 09:23:08 -07:00
if err != nil {
return err
}
2020-08-04 09:51:27 -07:00
nodeAppState , err := genutil . GenAppStateFromConfig ( clientCtx . JSONMarshaler , clientCtx . TxConfig , nodeConfig , initCfg , * genDoc , genBalIterator )
2020-06-12 09:23:08 -07:00
if err != nil {
return err
}
if appState == nil {
// set the canonical application state (they should not differ)
appState = nodeAppState
}
2020-07-25 01:10:04 -07:00
genFile := nodeConfig . GenesisFile ( )
2020-06-12 09:23:08 -07:00
// overwrite each validator's genesis file to have a canonical genesis time
if err := genutil . ExportGenesisFileWithTime ( genFile , chainID , nil , appState , genTime ) ; err != nil {
return err
}
}
return nil
}
func getIP ( i int , startingIPAddr string ) ( ip string , err error ) {
if len ( startingIPAddr ) == 0 {
ip , err = server . ExternalIP ( )
if err != nil {
return "" , err
}
return ip , nil
}
return calculateIP ( startingIPAddr , i )
}
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
}
func writeFile ( name string , dir string , contents [ ] byte ) error {
writePath := filepath . Join ( dir )
file := filepath . Join ( writePath , name )
err := tmos . EnsureDir ( writePath , 0755 )
if err != nil {
return err
}
err = tmos . WriteFile ( file , contents , 0644 )
if err != nil {
return err
}
return nil
}