@ -2,60 +2,103 @@ package commands
import (
import (
"fmt"
"fmt"
"net"
"os"
"path/filepath"
"path/filepath"
"strings"
"time"
"time"
"github.com/spf13/cobra"
"github.com/spf13/cobra"
cfg "github.com/tendermint/tendermint/config"
cfg "github.com/tendermint/tendermint/config"
"github.com/tendermint/tendermint/p2p"
"github.com/tendermint/tendermint/types"
"github.com/tendermint/tendermint/types"
pvm "github.com/tendermint/tendermint/types/priv_validator"
pvm "github.com/tendermint/tendermint/types/priv_validator"
cmn "github.com/tendermint/tmlibs/common"
cmn "github.com/tendermint/tmlibs/common"
)
)
//flags
var (
var (
nValidators int
dataDir string
nValidators int
nNonValidators int
outputDir string
nodeDirPrefix string
populatePersistentPeers bool
hostnamePrefix string
startingIPAddress string
p2pPort int
)
const (
nodeDirPerm = 0755
)
)
func init ( ) {
func init ( ) {
TestnetFilesCmd . Flags ( ) . IntVar ( & nValidators , "n" , 4 ,
TestnetFilesCmd . Flags ( ) . IntVar ( & nValidators , "v " , 4 ,
"Number of validators to initialize the testnet with" )
"Number of validators to initialize the testnet with" )
TestnetFilesCmd . Flags ( ) . StringVar ( & dataDir , "dir" , "mytestnet" ,
TestnetFilesCmd . Flags ( ) . IntVar ( & nNonValidators , "n" , 0 ,
"Number of non-validators to initialize the testnet with" )
TestnetFilesCmd . Flags ( ) . StringVar ( & outputDir , "o" , "./mytestnet" ,
"Directory to store initialization data for the testnet" )
"Directory to store initialization data for the testnet" )
TestnetFilesCmd . Flags ( ) . StringVar ( & nodeDirPrefix , "node-dir-prefix" , "node" ,
"Prefix the directory name for each node with (node results in node0, node1, ...)" )
TestnetFilesCmd . Flags ( ) . BoolVar ( & populatePersistentPeers , "populate-persistent-peers" , true ,
"Update config of each node with the list of persistent peers build using either hostname-prefix or starting-ip-address" )
TestnetFilesCmd . Flags ( ) . StringVar ( & hostnamePrefix , "hostname-prefix" , "node" ,
"Hostname prefix (node results in persistent peers list ID0@node0:46656, ID1@node1:46656, ...)" )
TestnetFilesCmd . Flags ( ) . StringVar ( & startingIPAddress , "starting-ip-address" , "" ,
"Starting IP address (192.168.0.1 results in persistent peers list ID0@192.168.0.1:46656, ID1@192.168.0.2:46656, ...)" )
TestnetFilesCmd . Flags ( ) . IntVar ( & p2pPort , "p2p-port" , 46656 ,
"P2P Port" )
}
}
// TestnetFilesCmd allows initialisation of files for a
// Tendermint testnet.
// TestnetFilesCmd allows initialisation of files for a Tendermint testnet.
var TestnetFilesCmd = & cobra . Command {
var TestnetFilesCmd = & cobra . Command {
Use : "testnet" ,
Use : "testnet" ,
Short : "Initialize files for a Tendermint testnet" ,
Short : "Initialize files for a Tendermint testnet" ,
Run : testnetFiles ,
RunE : testnetFiles ,
}
}
func testnetFiles ( cmd * cobra . Command , args [ ] string ) {
func testnetFiles ( cmd * cobra . Command , args [ ] string ) error {
config := cfg . DefaultConfig ( )
genVals := make ( [ ] types . GenesisValidator , nValidators )
genVals := make ( [ ] types . GenesisValidator , nValidators )
defaultConfig := cfg . DefaultBaseConfig ( )
// Initialize core dir and priv_validator.json's
for i := 0 ; i < nValidators ; i ++ {
for i := 0 ; i < nValidators ; i ++ {
mach := cmn . Fmt ( "mach%d" , i )
err := initMachCoreDirectory ( dataDir , mach )
nodeDirName := cmn . Fmt ( "%s%d" , nodeDirPrefix , i )
nodeDir := filepath . Join ( outputDir , nodeDirName )
config . SetRoot ( nodeDir )
err := os . MkdirAll ( filepath . Join ( nodeDir , "config" ) , nodeDirPerm )
if err != nil {
if err != nil {
cmn . Exit ( err . Error ( ) )
_ = os . RemoveAll ( outputDir )
return err
}
}
// Read priv_validator.json to populate vals
pvFile := filepath . Join ( dataDir , mach , defaultConfig . PrivValidator )
initFilesWithConfig ( config )
pvFile := filepath . Join ( nodeDir , config . BaseConfig . PrivValidator )
pv := pvm . LoadFilePV ( pvFile )
pv := pvm . LoadFilePV ( pvFile )
genVals [ i ] = types . GenesisValidator {
genVals [ i ] = types . GenesisValidator {
PubKey : pv . GetPubKey ( ) ,
PubKey : pv . GetPubKey ( ) ,
Power : 1 ,
Power : 1 ,
Name : mach ,
Name : nodeDirName ,
}
}
}
}
for i := 0 ; i < nNonValidators ; i ++ {
nodeDir := filepath . Join ( outputDir , cmn . Fmt ( "%s%d" , nodeDirPrefix , i + nValidators ) )
config . SetRoot ( nodeDir )
err := os . MkdirAll ( filepath . Join ( nodeDir , "config" ) , nodeDirPerm )
if err != nil {
_ = os . RemoveAll ( outputDir )
return err
}
initFilesWithConfig ( config )
}
// Generate genesis doc from generated validators
// Generate genesis doc from generated validators
genDoc := & types . GenesisDoc {
genDoc := & types . GenesisDoc {
GenesisTime : time . Now ( ) ,
GenesisTime : time . Now ( ) ,
@ -64,36 +107,64 @@ func testnetFiles(cmd *cobra.Command, args []string) {
}
}
// Write genesis file.
// Write genesis file.
for i := 0 ; i < nValidators ; i ++ {
mach := cmn . Fmt ( "mach%d" , i )
if err := genDoc . SaveAs ( filepath . Join ( dataDir , mach , defaultConfig . Genesis ) ) ; err != nil {
panic ( err )
for i := 0 ; i < nValidators + nNonValidators ; i ++ {
nodeDir := filepath . Join ( outputDir , cmn . Fmt ( "%s%d" , nodeDirPrefix , i ) )
if err := genDoc . SaveAs ( filepath . Join ( nodeDir , config . BaseConfig . Genesis ) ) ; err != nil {
_ = os . RemoveAll ( outputDir )
return err
}
}
}
}
fmt . Println ( cmn . Fmt ( "Successfully initialized %v node directories" , nValidators ) )
if populatePersistentPeers {
err := populatePersistentPeersInConfigAndWriteIt ( config )
if err != nil {
_ = os . RemoveAll ( outputDir )
return err
}
}
fmt . Printf ( "Successfully initialized %v node directories\n" , nValidators + nNonValidators )
return nil
}
}
// Initialize per-machine core directory
func initMachCoreDirectory ( base , mach string ) error {
// Create priv_validator.json file if not present
defaultConfig := cfg . DefaultBaseConfig ( )
dir := filepath . Join ( base , mach )
pvPath := filepath . Join ( dir , defaultConfig . PrivValidator )
dir = filepath . Dir ( pvPath )
err := cmn . EnsureDir ( dir , 0700 )
if err != nil {
return err
func hostnameOrIP ( i int ) string {
if startingIPAddress != "" {
ip := net . ParseIP ( startingIPAddress )
ip = ip . To4 ( )
if ip == nil {
fmt . Printf ( "%v: non ipv4 address\n" , startingIPAddress )
os . Exit ( 1 )
}
ip = ip . Mask ( ip . DefaultMask ( ) )
for j := 0 ; j <= i ; j ++ {
ip [ 3 ] ++
}
return ip . String ( )
}
}
ensurePrivValidator ( pvPath )
return nil
return fmt . Sprintf ( "%s%d" , hostnamePrefix , i )
}
}
func ensurePrivValidator ( file string ) {
if cmn . FileExists ( file ) {
return
func populatePersistentPeersInConfigAndWriteIt ( config * cfg . Config ) error {
persistentPeers := make ( [ ] string , nValidators + nNonValidators )
for i := 0 ; i < nValidators + nNonValidators ; i ++ {
nodeKey , err := p2p . LoadNodeKey ( config . NodeKeyFile ( ) )
if err != nil {
return err
}
persistentPeers [ i ] = p2p . IDAddressString ( nodeKey . ID ( ) , fmt . Sprintf ( "%s:%d" , hostnameOrIP ( i ) , p2pPort ) )
}
}
pv := pvm . GenFilePV ( file )
pv . Save ( )
persistentPeersList := strings . Join ( persistentPeers , "," )
for i := 0 ; i < nValidators + nNonValidators ; i ++ {
nodeDir := filepath . Join ( outputDir , cmn . Fmt ( "%s%d" , nodeDirPrefix , i ) )
config . SetRoot ( nodeDir )
config . P2P . PersistentPeers = persistentPeersList
// overwrite default config
cfg . WriteConfigFile ( filepath . Join ( nodeDir , "config" , "config.toml" ) , config )
}
return nil
}
}