2018-10-10 15:45:41 -07:00
package init
import (
"encoding/json"
2018-10-19 11:00:27 -07:00
"errors"
2018-10-10 15:45:41 -07:00
"fmt"
2018-10-19 11:00:27 -07:00
"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/cmd/gaia/app"
"github.com/cosmos/cosmos-sdk/x/auth"
authtx "github.com/cosmos/cosmos-sdk/x/auth/client/txbuilder"
"github.com/cosmos/cosmos-sdk/x/stake"
"github.com/tendermint/tendermint/crypto"
"github.com/tendermint/tendermint/privval"
2018-10-10 15:45:41 -07:00
"os"
"path/filepath"
"github.com/cosmos/cosmos-sdk/codec"
"github.com/cosmos/cosmos-sdk/server"
2018-10-19 11:00:27 -07:00
sdk "github.com/cosmos/cosmos-sdk/types"
2018-10-10 15:45:41 -07:00
"github.com/spf13/cobra"
"github.com/spf13/viper"
cfg "github.com/tendermint/tendermint/config"
"github.com/tendermint/tendermint/libs/cli"
"github.com/tendermint/tendermint/libs/common"
"github.com/tendermint/tendermint/p2p"
"github.com/tendermint/tendermint/types"
)
2018-10-19 11:00:27 -07:00
const (
flagWithTxs = "with-txs"
flagOverwrite = "overwrite"
flagClientHome = "home-client"
flagOverwriteKey = "overwrite-key"
flagSkipGenesis = "skip-genesis"
flagMoniker = "moniker"
)
2018-10-10 15:45:41 -07:00
2018-10-19 11:00:27 -07:00
type initConfig struct {
ChainID string
GenTxsDir string
Name string
NodeID string
ClientHome string
WithTxs bool
Overwrite bool
OverwriteKey bool
ValPubKey crypto . PubKey
2018-10-10 15:45:41 -07:00
}
2018-10-19 11:00:27 -07:00
type printInfo struct {
Moniker string ` json:"moniker" `
ChainID string ` json:"chain_id" `
NodeID string ` json:"node_id" `
AppMessage json . RawMessage ` json:"app_message" `
}
2018-10-10 15:45:41 -07:00
2018-10-19 11:00:27 -07:00
// nolint: errcheck
func displayInfo ( cdc * codec . Codec , info printInfo ) error {
out , err := codec . MarshalJSONIndent ( cdc , info )
2018-10-10 15:45:41 -07:00
if err != nil {
2018-10-19 11:00:27 -07:00
return err
2018-10-10 15:45:41 -07:00
}
2018-10-19 11:00:27 -07:00
fmt . Fprintf ( os . Stderr , "%s\n" , string ( out ) )
return nil
2018-10-10 15:45:41 -07:00
}
// get cmd to initialize all files for tendermint and application
2018-10-19 11:00:27 -07:00
// nolint
2018-10-10 15:45:41 -07:00
func InitCmd ( ctx * server . Context , cdc * codec . Codec , appInit server . AppInit ) * cobra . Command {
cmd := & cobra . Command {
Use : "init" ,
2018-10-19 11:00:27 -07:00
Short : "Initialize private validator, p2p, genesis, and application configuration files" ,
Long : ` Initialize validators ' s and node ' s configuration files .
2018-10-10 15:45:41 -07:00
2018-10-19 11:00:27 -07:00
Note that only node ' s configuration files will be written if the flag -- skip - genesis is
enabled , and the genesis file will not be generated .
` ,
Args : cobra . NoArgs ,
RunE : func ( _ * cobra . Command , _ [ ] string ) error {
2018-10-10 15:45:41 -07:00
config := ctx . Config
config . SetRoot ( viper . GetString ( cli . HomeFlag ) )
2018-10-19 11:00:27 -07:00
name := viper . GetString ( client . FlagName )
chainID := viper . GetString ( client . FlagChainID )
if chainID == "" {
chainID = fmt . Sprintf ( "test-chain-%v" , common . RandStr ( 6 ) )
}
nodeID , valPubKey , err := InitializeNodeValidatorFiles ( config )
2018-10-10 15:45:41 -07:00
if err != nil {
return err
}
2018-10-19 11:00:27 -07:00
if viper . GetString ( flagMoniker ) != "" {
config . Moniker = viper . GetString ( flagMoniker )
}
if config . Moniker == "" && name != "" {
config . Moniker = name
}
toPrint := printInfo {
ChainID : chainID ,
Moniker : config . Moniker ,
NodeID : nodeID ,
2018-10-10 15:45:41 -07:00
}
2018-10-19 11:00:27 -07:00
if viper . GetBool ( flagSkipGenesis ) {
cfg . WriteConfigFile ( filepath . Join ( config . RootDir , "config" , "config.toml" ) , config )
return displayInfo ( cdc , toPrint )
}
initCfg := initConfig {
ChainID : chainID ,
GenTxsDir : filepath . Join ( config . RootDir , "config" , "gentx" ) ,
Name : name ,
NodeID : nodeID ,
ClientHome : viper . GetString ( flagClientHome ) ,
WithTxs : viper . GetBool ( flagWithTxs ) ,
Overwrite : viper . GetBool ( flagOverwrite ) ,
OverwriteKey : viper . GetBool ( flagOverwriteKey ) ,
ValPubKey : valPubKey ,
}
appMessage , err := initWithConfig ( cdc , config , initCfg )
// print out some key information
2018-10-10 15:45:41 -07:00
if err != nil {
return err
}
2018-10-19 11:00:27 -07:00
toPrint . AppMessage = appMessage
return displayInfo ( cdc , toPrint )
2018-10-10 15:45:41 -07:00
} ,
}
2018-10-19 11:00:27 -07:00
cmd . Flags ( ) . String ( cli . HomeFlag , app . DefaultNodeHome , "node's home directory" )
cmd . Flags ( ) . BoolP ( flagOverwrite , "o" , false , "overwrite the genesis.json file" )
cmd . Flags ( ) . String ( client . FlagChainID , "" , "genesis file chain-id, if left blank will be randomly created" )
cmd . Flags ( ) . Bool ( flagWithTxs , false , "apply existing genesis transactions from [--home]/config/gentx/" )
cmd . Flags ( ) . String ( client . FlagName , "" , "name of private key with which to sign the gentx" )
cmd . Flags ( ) . String ( flagMoniker , "" , "overrides --name flag and set the validator's moniker to a different value; ignored if it runs without the --with-txs flag" )
cmd . Flags ( ) . String ( flagClientHome , app . DefaultCLIHome , "client's home directory" )
cmd . Flags ( ) . Bool ( flagOverwriteKey , false , "overwrite client's key" )
cmd . Flags ( ) . Bool ( flagSkipGenesis , false , "do not create genesis.json" )
2018-10-10 15:45:41 -07:00
return cmd
}
2018-10-19 11:00:27 -07:00
// InitializeNodeValidatorFiles creates private validator and p2p configuration files.
func InitializeNodeValidatorFiles ( config * cfg . Config ) ( nodeID string , valPubKey crypto . PubKey , err error ) {
2018-10-10 15:45:41 -07:00
nodeKey , err := p2p . LoadOrGenNodeKey ( config . NodeKeyFile ( ) )
if err != nil {
return
}
nodeID = string ( nodeKey . ID ( ) )
2018-10-19 11:00:27 -07:00
valPubKey = ReadOrCreatePrivValidator ( config . PrivValidatorFile ( ) )
return
}
2018-10-10 15:45:41 -07:00
2018-10-19 11:00:27 -07:00
func initWithConfig ( cdc * codec . Codec , config * cfg . Config , initCfg initConfig ) (
appMessage json . RawMessage , err error ) {
2018-10-10 15:45:41 -07:00
genFile := config . GenesisFile ( )
2018-10-19 11:00:27 -07:00
if ! initCfg . Overwrite && common . FileExists ( genFile ) {
2018-10-10 15:45:41 -07:00
err = fmt . Errorf ( "genesis.json file already exists: %v" , genFile )
return
}
2018-10-19 11:00:27 -07:00
// process genesis transactions, else create default genesis.json
var appGenTxs [ ] auth . StdTx
2018-10-10 15:45:41 -07:00
var persistentPeers string
2018-10-19 11:00:27 -07:00
var genTxs [ ] json . RawMessage
var appState json . RawMessage
var jsonRawTx json . RawMessage
chainID := initCfg . ChainID
2018-10-10 15:45:41 -07:00
2018-10-19 11:00:27 -07:00
if initCfg . WithTxs {
_ , appGenTxs , persistentPeers , err = app . CollectStdTxs ( config . Moniker , initCfg . GenTxsDir , cdc )
2018-10-10 15:45:41 -07:00
if err != nil {
return
}
2018-10-19 11:00:27 -07:00
genTxs = make ( [ ] json . RawMessage , len ( appGenTxs ) )
2018-10-10 15:45:41 -07:00
config . P2P . PersistentPeers = persistentPeers
2018-10-19 11:00:27 -07:00
for i , stdTx := range appGenTxs {
jsonRawTx , err = cdc . MarshalJSON ( stdTx )
if err != nil {
return
}
genTxs [ i ] = jsonRawTx
}
2018-10-10 15:45:41 -07:00
} else {
2018-10-19 11:00:27 -07:00
var ip , keyPass , secret string
var addr sdk . AccAddress
var signedTx auth . StdTx
if initCfg . Name == "" {
err = errors . New ( "must specify validator's moniker (--name)" )
return
2018-10-10 15:45:41 -07:00
}
2018-10-19 11:00:27 -07:00
config . Moniker = initCfg . Name
ip , err = server . ExternalIP ( )
2018-10-10 15:45:41 -07:00
if err != nil {
2018-10-19 11:00:27 -07:00
return
2018-10-10 15:45:41 -07:00
}
2018-10-19 11:00:27 -07:00
memo := fmt . Sprintf ( "%s@%s:26656" , initCfg . NodeID , ip )
buf := client . BufferStdin ( )
prompt := fmt . Sprintf ( "Password for account %q (default: %q):" , initCfg . Name , app . DefaultKeyPass )
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
}
if keyPass == "" {
keyPass = app . DefaultKeyPass
2018-10-10 15:45:41 -07:00
}
2018-10-19 11:00:27 -07:00
addr , secret , err = server . GenerateSaveCoinKey ( initCfg . ClientHome , initCfg . Name , keyPass , initCfg . OverwriteKey )
2018-10-10 15:45:41 -07:00
if err != nil {
return
}
2018-10-19 11:00:27 -07:00
appMessage , err = json . Marshal ( map [ string ] string { "secret" : secret } )
2018-10-10 15:45:41 -07:00
if err != nil {
return
}
2018-10-19 11:00:27 -07:00
msg := stake . NewMsgCreateValidator (
sdk . ValAddress ( addr ) ,
initCfg . ValPubKey ,
sdk . NewInt64Coin ( "steak" , 100 ) ,
stake . NewDescription ( config . Moniker , "" , "" , "" ) ,
stake . NewCommissionMsg ( sdk . ZeroDec ( ) , sdk . ZeroDec ( ) , sdk . ZeroDec ( ) ) ,
)
txBldr := authtx . NewTxBuilderFromCLI ( ) . WithCodec ( cdc ) . WithMemo ( memo ) . WithChainID ( chainID )
signedTx , err = txBldr . SignStdTx (
initCfg . Name , keyPass , auth . NewStdTx ( [ ] sdk . Msg { msg } , auth . StdFee { } , [ ] auth . StdSignature { } , memo ) , false ,
)
if err != nil {
return
}
jsonRawTx , err = cdc . MarshalJSON ( signedTx )
if err != nil {
return
}
genTxs = [ ] json . RawMessage { jsonRawTx }
2018-10-10 15:45:41 -07:00
}
2018-10-19 11:00:27 -07:00
cfg . WriteConfigFile ( filepath . Join ( config . RootDir , "config" , "config.toml" ) , config )
appState , err = app . GaiaAppGenStateJSON ( cdc , genTxs )
if err != nil {
return
2018-10-10 15:45:41 -07:00
}
2018-10-19 11:00:27 -07:00
err = WriteGenesisFile ( genFile , chainID , nil , appState )
2018-10-10 15:45:41 -07:00
return
}
2018-10-19 11:00:27 -07:00
// WriteGenesisFile creates and writes the genesis configuration to disk. An
2018-10-10 15:45:41 -07:00
// error is returned if building or writing the configuration to file fails.
// nolint: unparam
2018-10-19 11:00:27 -07:00
func WriteGenesisFile ( genesisFile , chainID string , validators [ ] types . GenesisValidator , appState json . RawMessage ) error {
2018-10-10 15:45:41 -07:00
genDoc := types . GenesisDoc {
ChainID : chainID ,
Validators : validators ,
AppState : appState ,
}
if err := genDoc . ValidateAndComplete ( ) ; err != nil {
return err
}
return genDoc . SaveAs ( genesisFile )
}
2018-10-19 11:00:27 -07:00
// read of create the private key file for this config
func ReadOrCreatePrivValidator ( privValFile string ) crypto . PubKey {
// private validator
var privValidator * privval . FilePV
if common . FileExists ( privValFile ) {
privValidator = privval . LoadFilePV ( privValFile )
} else {
privValidator = privval . GenFilePV ( privValFile )
privValidator . Save ( )
}
return privValidator . GetPubKey ( )
}