github.com/Oyster-zx/tendermint@v0.34.24-fork/cmd/tendermint/commands/testnet.go (about) 1 package commands 2 3 import ( 4 "fmt" 5 "net" 6 "os" 7 "path/filepath" 8 "strings" 9 10 "github.com/spf13/cobra" 11 "github.com/spf13/viper" 12 13 cfg "github.com/tendermint/tendermint/config" 14 "github.com/tendermint/tendermint/libs/bytes" 15 tmrand "github.com/tendermint/tendermint/libs/rand" 16 "github.com/tendermint/tendermint/p2p" 17 "github.com/tendermint/tendermint/privval" 18 "github.com/tendermint/tendermint/types" 19 tmtime "github.com/tendermint/tendermint/types/time" 20 ) 21 22 var ( 23 nValidators int 24 nNonValidators int 25 initialHeight int64 26 configFile string 27 outputDir string 28 nodeDirPrefix string 29 30 populatePersistentPeers bool 31 hostnamePrefix string 32 hostnameSuffix string 33 startingIPAddress string 34 hostnames []string 35 p2pPort int 36 randomMonikers bool 37 ) 38 39 const ( 40 nodeDirPerm = 0755 41 ) 42 43 func init() { 44 TestnetFilesCmd.Flags().IntVar(&nValidators, "v", 4, 45 "number of validators to initialize the testnet with") 46 TestnetFilesCmd.Flags().StringVar(&configFile, "config", "", 47 "config file to use (note some options may be overwritten)") 48 TestnetFilesCmd.Flags().IntVar(&nNonValidators, "n", 0, 49 "number of non-validators to initialize the testnet with") 50 TestnetFilesCmd.Flags().StringVar(&outputDir, "o", "./mytestnet", 51 "directory to store initialization data for the testnet") 52 TestnetFilesCmd.Flags().StringVar(&nodeDirPrefix, "node-dir-prefix", "node", 53 "prefix the directory name for each node with (node results in node0, node1, ...)") 54 TestnetFilesCmd.Flags().Int64Var(&initialHeight, "initial-height", 0, 55 "initial height of the first block") 56 57 TestnetFilesCmd.Flags().BoolVar(&populatePersistentPeers, "populate-persistent-peers", true, 58 "update config of each node with the list of persistent peers build using either"+ 59 " hostname-prefix or"+ 60 " starting-ip-address") 61 TestnetFilesCmd.Flags().StringVar(&hostnamePrefix, "hostname-prefix", "node", 62 "hostname prefix (\"node\" results in persistent peers list ID0@node0:26656, ID1@node1:26656, ...)") 63 TestnetFilesCmd.Flags().StringVar(&hostnameSuffix, "hostname-suffix", "", 64 "hostname suffix ("+ 65 "\".xyz.com\""+ 66 " results in persistent peers list ID0@node0.xyz.com:26656, ID1@node1.xyz.com:26656, ...)") 67 TestnetFilesCmd.Flags().StringVar(&startingIPAddress, "starting-ip-address", "", 68 "starting IP address ("+ 69 "\"192.168.0.1\""+ 70 " results in persistent peers list ID0@192.168.0.1:26656, ID1@192.168.0.2:26656, ...)") 71 TestnetFilesCmd.Flags().StringArrayVar(&hostnames, "hostname", []string{}, 72 "manually override all hostnames of validators and non-validators (use --hostname multiple times for multiple hosts)") 73 TestnetFilesCmd.Flags().IntVar(&p2pPort, "p2p-port", 26656, 74 "P2P Port") 75 TestnetFilesCmd.Flags().BoolVar(&randomMonikers, "random-monikers", false, 76 "randomize the moniker for each generated node") 77 } 78 79 // TestnetFilesCmd allows initialisation of files for a Tendermint testnet. 80 var TestnetFilesCmd = &cobra.Command{ 81 Use: "testnet", 82 Short: "Initialize files for a Tendermint testnet", 83 Long: `testnet will create "v" + "n" number of directories and populate each with 84 necessary files (private validator, genesis, config, etc.). 85 86 Note, strict routability for addresses is turned off in the config file. 87 88 Optionally, it will fill in persistent_peers list in config file using either hostnames or IPs. 89 90 Example: 91 92 tendermint testnet --v 4 --o ./output --populate-persistent-peers --starting-ip-address 192.168.10.2 93 `, 94 RunE: testnetFiles, 95 } 96 97 func testnetFiles(cmd *cobra.Command, args []string) error { 98 if len(hostnames) > 0 && len(hostnames) != (nValidators+nNonValidators) { 99 return fmt.Errorf( 100 "testnet needs precisely %d hostnames (number of validators plus non-validators) if --hostname parameter is used", 101 nValidators+nNonValidators, 102 ) 103 } 104 105 config := cfg.DefaultConfig() 106 107 // overwrite default config if set and valid 108 if configFile != "" { 109 viper.SetConfigFile(configFile) 110 if err := viper.ReadInConfig(); err != nil { 111 return err 112 } 113 if err := viper.Unmarshal(config); err != nil { 114 return err 115 } 116 if err := config.ValidateBasic(); err != nil { 117 return err 118 } 119 } 120 121 genVals := make([]types.GenesisValidator, nValidators) 122 123 for i := 0; i < nValidators; i++ { 124 nodeDirName := fmt.Sprintf("%s%d", nodeDirPrefix, i) 125 nodeDir := filepath.Join(outputDir, nodeDirName) 126 config.SetRoot(nodeDir) 127 128 err := os.MkdirAll(filepath.Join(nodeDir, "config"), nodeDirPerm) 129 if err != nil { 130 _ = os.RemoveAll(outputDir) 131 return err 132 } 133 err = os.MkdirAll(filepath.Join(nodeDir, "data"), nodeDirPerm) 134 if err != nil { 135 _ = os.RemoveAll(outputDir) 136 return err 137 } 138 139 if err := initFilesWithConfig(config); err != nil { 140 return err 141 } 142 143 pvKeyFile := filepath.Join(nodeDir, config.BaseConfig.PrivValidatorKey) 144 pvStateFile := filepath.Join(nodeDir, config.BaseConfig.PrivValidatorState) 145 pv := privval.LoadFilePV(pvKeyFile, pvStateFile) 146 147 pubKey, err := pv.GetPubKey() 148 if err != nil { 149 return fmt.Errorf("can't get pubkey: %w", err) 150 } 151 genVals[i] = types.GenesisValidator{ 152 Address: pubKey.Address(), 153 PubKey: pubKey, 154 Power: 1, 155 Name: nodeDirName, 156 } 157 } 158 159 for i := 0; i < nNonValidators; i++ { 160 nodeDir := filepath.Join(outputDir, fmt.Sprintf("%s%d", nodeDirPrefix, i+nValidators)) 161 config.SetRoot(nodeDir) 162 163 err := os.MkdirAll(filepath.Join(nodeDir, "config"), nodeDirPerm) 164 if err != nil { 165 _ = os.RemoveAll(outputDir) 166 return err 167 } 168 169 err = os.MkdirAll(filepath.Join(nodeDir, "data"), nodeDirPerm) 170 if err != nil { 171 _ = os.RemoveAll(outputDir) 172 return err 173 } 174 175 if err := initFilesWithConfig(config); err != nil { 176 return err 177 } 178 } 179 180 // Generate genesis doc from generated validators 181 genDoc := &types.GenesisDoc{ 182 ChainID: "chain-" + tmrand.Str(6), 183 ConsensusParams: types.DefaultConsensusParams(), 184 GenesisTime: tmtime.Now(), 185 InitialHeight: initialHeight, 186 Validators: genVals, 187 } 188 189 // Write genesis file. 190 for i := 0; i < nValidators+nNonValidators; i++ { 191 nodeDir := filepath.Join(outputDir, fmt.Sprintf("%s%d", nodeDirPrefix, i)) 192 if err := genDoc.SaveAs(filepath.Join(nodeDir, config.BaseConfig.Genesis)); err != nil { 193 _ = os.RemoveAll(outputDir) 194 return err 195 } 196 } 197 198 // Gather persistent peer addresses. 199 var ( 200 persistentPeers string 201 err error 202 ) 203 if populatePersistentPeers { 204 persistentPeers, err = persistentPeersString(config) 205 if err != nil { 206 _ = os.RemoveAll(outputDir) 207 return err 208 } 209 } 210 211 // Overwrite default config. 212 for i := 0; i < nValidators+nNonValidators; i++ { 213 nodeDir := filepath.Join(outputDir, fmt.Sprintf("%s%d", nodeDirPrefix, i)) 214 config.SetRoot(nodeDir) 215 config.P2P.AddrBookStrict = false 216 config.P2P.AllowDuplicateIP = true 217 if populatePersistentPeers { 218 config.P2P.PersistentPeers = persistentPeers 219 } 220 config.Moniker = moniker(i) 221 222 cfg.WriteConfigFile(filepath.Join(nodeDir, "config", "config.toml"), config) 223 } 224 225 fmt.Printf("Successfully initialized %v node directories\n", nValidators+nNonValidators) 226 return nil 227 } 228 229 func hostnameOrIP(i int) string { 230 if len(hostnames) > 0 && i < len(hostnames) { 231 return hostnames[i] 232 } 233 if startingIPAddress == "" { 234 return fmt.Sprintf("%s%d%s", hostnamePrefix, i, hostnameSuffix) 235 } 236 ip := net.ParseIP(startingIPAddress) 237 ip = ip.To4() 238 if ip == nil { 239 fmt.Printf("%v: non ipv4 address\n", startingIPAddress) 240 os.Exit(1) 241 } 242 243 for j := 0; j < i; j++ { 244 ip[3]++ 245 } 246 return ip.String() 247 } 248 249 func persistentPeersString(config *cfg.Config) (string, error) { 250 persistentPeers := make([]string, nValidators+nNonValidators) 251 for i := 0; i < nValidators+nNonValidators; i++ { 252 nodeDir := filepath.Join(outputDir, fmt.Sprintf("%s%d", nodeDirPrefix, i)) 253 config.SetRoot(nodeDir) 254 nodeKey, err := p2p.LoadNodeKey(config.NodeKeyFile()) 255 if err != nil { 256 return "", err 257 } 258 persistentPeers[i] = p2p.IDAddressString(nodeKey.ID(), fmt.Sprintf("%s:%d", hostnameOrIP(i), p2pPort)) 259 } 260 return strings.Join(persistentPeers, ","), nil 261 } 262 263 func moniker(i int) string { 264 if randomMonikers { 265 return randomMoniker() 266 } 267 if len(hostnames) > 0 && i < len(hostnames) { 268 return hostnames[i] 269 } 270 if startingIPAddress == "" { 271 return fmt.Sprintf("%s%d%s", hostnamePrefix, i, hostnameSuffix) 272 } 273 return randomMoniker() 274 } 275 276 func randomMoniker() string { 277 return bytes.HexBytes(tmrand.Bytes(8)).String() 278 }