Merge pull request #1208 from cosmos/greg/testnet-command-2

Testnet command and make localnet target
This commit is contained in:
Rigel 2018-06-15 12:32:27 -07:00 committed by GitHub
commit e4685ecba5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 598 additions and 129 deletions

2
.gitignore vendored
View File

@ -3,6 +3,7 @@
*.swp
*.swo
.vscode
.idea
# Build
vendor
@ -15,6 +16,7 @@ docs/_build
examples/basecoin/app/data
baseapp/data/*
client/lcd/keys/*
mytestnet
# Testing
coverage.txt

View File

@ -2,7 +2,7 @@
## 0.20.0
*TBD*
*TBD*
BREAKING CHANGES
* Change default ports from 466xx to 266xx
@ -24,6 +24,8 @@ IMPROVEMENTS
* [tests] Application module tests now use a mock application
* [gaiacli] Fix error message when account isn't found when running gaiacli account
* [lcd] refactored to eliminate use of global variables, and interdependent tests
* [tests] Added testnet command to gaiad
* [tests] Added localnet targets to Makefile
* [x/stake] More stake tests added to test ByPower index
FIXES

View File

@ -1,7 +1,7 @@
PACKAGES=$(shell go list ./... | grep -v '/vendor/')
PACKAGES_NOCLITEST=$(shell go list ./... | grep -v '/vendor/' | grep -v github.com/cosmos/cosmos-sdk/cmd/gaia/cli_test)
COMMIT_HASH := $(shell git rev-parse --short HEAD)
BUILD_FLAGS = -ldflags "-X github.com/cosmos/cosmos-sdk/version.GitCommit=${COMMIT_HASH}"
BUILD_FLAGS = -tags netgo -ldflags "-X github.com/cosmos/cosmos-sdk/version.GitCommit=${COMMIT_HASH}"
all: check_tools get_vendor_deps install install_examples test_lint test
@ -133,12 +133,27 @@ devdoc_update:
########################################
### Remote validator nodes using terraform and ansible
### Local validator nodes using docker and docker-compose
# Build linux binary
build-linux:
GOOS=linux GOARCH=amd64 $(MAKE) build
build-docker-gaiadnode:
$(MAKE) -C networks/local
# Run a 4-node testnet locally
localnet-start: localnet-stop
@if ! [ -f build/node0/gaiad/config/genesis.json ]; then docker run --rm -v $(CURDIR)/build:/gaiad:Z tendermint/gaiadnode testnet --v 4 --o . --starting-ip-address 192.168.10.2 ; fi
docker-compose up
# Stop testnet
localnet-stop:
docker-compose down
########################################
### Remote validator nodes using terraform and ansible
TESTNET_NAME?=remotenet
SERVERS?=4
BINARY=$(CURDIR)/build/gaiad
@ -160,4 +175,4 @@ remotenet-status:
# To avoid unintended conflicts with file names, always add to .PHONY
# unless there is a reason not to.
# https://www.gnu.org/software/make/manual/html_node/Phony-Targets.html
.PHONY: build build_examples install install_examples install_debug dist check_tools get_tools get_vendor_deps draw_deps test test_cli test_unit test_cover test_lint benchmark devdoc_init devdoc devdoc_save devdoc_update remotenet-start remotenet-stop remotenet-status
.PHONY: build build_examples install install_examples install_debug dist check_tools get_tools get_vendor_deps draw_deps test test_cli test_unit test_cover test_lint benchmark devdoc_init devdoc devdoc_save devdoc_update build-linux build-docker-gaiadnode localnet-start localnet-stop remotenet-start remotenet-stop remotenet-status

View File

@ -3,19 +3,24 @@ package app
import (
"encoding/json"
"errors"
"github.com/spf13/pflag"
"github.com/spf13/viper"
crypto "github.com/tendermint/go-crypto"
tmtypes "github.com/tendermint/tendermint/types"
"github.com/cosmos/cosmos-sdk/server"
"github.com/cosmos/cosmos-sdk/server/config"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/wire"
"github.com/cosmos/cosmos-sdk/x/auth"
"github.com/cosmos/cosmos-sdk/x/stake"
)
var (
// bonded tokens given to genesis validators/accounts
freeTokenVal = int64(100)
freeTokensAcc = int64(50)
)
// State to Unmarshal
type GenesisState struct {
Accounts []GenesisAccount `json:"accounts"`
@ -50,25 +55,15 @@ func (ga *GenesisAccount) ToAccount() (acc *auth.BaseAccount) {
}
}
var (
flagName = "name"
flagClientHome = "home-client"
flagOWK = "owk"
// bonded tokens given to genesis validators/accounts
freeFermionVal = int64(100)
freeFermionsAcc = int64(50)
)
// get app init parameters for server init command
func GaiaAppInit() server.AppInit {
fsAppGenState := pflag.NewFlagSet("", pflag.ContinueOnError)
fsAppGenTx := pflag.NewFlagSet("", pflag.ContinueOnError)
fsAppGenTx.String(flagName, "", "validator moniker, required")
fsAppGenTx.String(flagClientHome, DefaultCLIHome,
fsAppGenTx.String(server.FlagName, "", "validator moniker, required")
fsAppGenTx.String(server.FlagClientHome, DefaultCLIHome,
"home directory for the client, used for key generation")
fsAppGenTx.Bool(flagOWK, false, "overwrite the accounts created")
fsAppGenTx.Bool(server.FlagOWK, false, "overwrite the accounts created")
return server.AppInit{
FlagsAppGenState: fsAppGenState,
@ -86,18 +81,15 @@ type GaiaGenTx struct {
}
// Generate a gaia genesis transaction with flags
func GaiaAppGenTx(cdc *wire.Codec, pk crypto.PubKey) (
func GaiaAppGenTx(cdc *wire.Codec, pk crypto.PubKey, genTxConfig config.GenTx) (
appGenTx, cliPrint json.RawMessage, validator tmtypes.GenesisValidator, err error) {
clientRoot := viper.GetString(flagClientHome)
overwrite := viper.GetBool(flagOWK)
name := viper.GetString(flagName)
if name == "" {
return nil, nil, tmtypes.GenesisValidator{}, errors.New("must specify --name (validator moniker)")
if genTxConfig.Name == "" {
return nil, nil, tmtypes.GenesisValidator{}, errors.New("Must specify --name (validator moniker)")
}
var addr sdk.Address
var secret string
addr, secret, err = server.GenerateSaveCoinKey(clientRoot, name, "1234567890", overwrite)
addr, secret, err = server.GenerateSaveCoinKey(genTxConfig.CliRoot, genTxConfig.Name, "1234567890", genTxConfig.Overwrite)
if err != nil {
return
}
@ -107,8 +99,9 @@ func GaiaAppGenTx(cdc *wire.Codec, pk crypto.PubKey) (
if err != nil {
return
}
cliPrint = json.RawMessage(bz)
appGenTx,_,validator,err = GaiaAppGenTxNF(cdc, pk, addr, name, overwrite)
appGenTx, _, validator, err = GaiaAppGenTxNF(cdc, pk, addr, genTxConfig.Name, genTxConfig.Overwrite)
return
}
@ -130,7 +123,7 @@ func GaiaAppGenTxNF(cdc *wire.Codec, pk crypto.PubKey, addr sdk.Address, name st
validator = tmtypes.GenesisValidator{
PubKey: pk,
Power: freeFermionVal,
Power: freeTokenVal,
}
return
}
@ -161,21 +154,21 @@ func GaiaAppGenState(cdc *wire.Codec, appGenTxs []json.RawMessage) (genesisState
accAuth := auth.NewBaseAccountWithAddress(genTx.Address)
accAuth.Coins = sdk.Coins{
{genTx.Name + "Token", 1000},
{"steak", freeFermionsAcc},
{"steak", freeTokensAcc},
}
acc := NewGenesisAccount(&accAuth)
genaccs[i] = acc
stakeData.Pool.LooseUnbondedTokens += freeFermionsAcc // increase the supply
stakeData.Pool.LooseUnbondedTokens += freeTokensAcc // increase the supply
// add the validator
if len(genTx.Name) > 0 {
desc := stake.NewDescription(genTx.Name, "", "", "")
validator := stake.NewValidator(genTx.Address, genTx.PubKey, desc)
validator.PoolShares = stake.NewBondedShares(sdk.NewRat(freeFermionVal))
validator.PoolShares = stake.NewBondedShares(sdk.NewRat(freeTokenVal))
stakeData.Validators = append(stakeData.Validators, validator)
// pool logic
stakeData.Pool.BondedTokens += freeFermionVal
stakeData.Pool.BondedTokens += freeTokenVal
stakeData.Pool.BondedShares = sdk.NewRat(stakeData.Pool.BondedTokens)
}
}

68
docker-compose.yml Normal file
View File

@ -0,0 +1,68 @@
version: '3'
services:
gaiadnode0:
container_name: gaiadnode0
image: "tendermint/gaiadnode"
ports:
- "26656-26657:26656-26657"
environment:
- ID=0
- LOG=$${LOG:-gaiad.log}
volumes:
- ./build:/gaiad:Z
networks:
localnet:
ipv4_address: 192.168.10.2
gaiadnode1:
container_name: gaiadnode1
image: "tendermint/gaiadnode"
ports:
- "26659-26660:26656-26657"
environment:
- ID=1
- LOG=$${LOG:-gaiad.log}
volumes:
- ./build:/gaiad:Z
networks:
localnet:
ipv4_address: 192.168.10.3
gaiadnode2:
container_name: gaiadnode2
image: "tendermint/gaiadnode"
environment:
- ID=2
- LOG=$${LOG:-gaiad.log}
ports:
- "26661-26662:26656-26657"
volumes:
- ./build:/gaiad:Z
networks:
localnet:
ipv4_address: 192.168.10.4
gaiadnode3:
container_name: gaiadnode3
image: "tendermint/gaiadnode"
environment:
- ID=3
- LOG=$${LOG:-gaiad.log}
ports:
- "26663-26664:26656-26657"
volumes:
- ./build:/gaiad:Z
networks:
localnet:
ipv4_address: 192.168.10.5
networks:
localnet:
driver: bridge
ipam:
driver: default
config:
-
subnet: 192.168.10.0/16

7
networks/local/Makefile Normal file
View File

@ -0,0 +1,7 @@
# Makefile for the "gaiadnode" docker image.
all:
docker build --tag tendermint/gaiadnode gaiadnode
.PHONY: all

79
networks/local/README.md Normal file
View File

@ -0,0 +1,79 @@
# Local Cluster with Docker Compose
## Requirements
- [Install gaia](/docs/index.rst)
- [Install docker](https://docs.docker.com/engine/installation/)
- [Install docker-compose](https://docs.docker.com/compose/install/)
## Build
Build the `gaiad` binary and the `tendermint/gaiadnode` docker image.
Note the binary will be mounted into the container so it can be updated without
rebuilding the image.
```
cd $GOPATH/src/github.com/cosmos/cosmos-sdk
# Build the linux binary in ./build
make build-linux
# Build tendermint/gaiadnode image
make build-docker-gaiadnode
```
## Run a testnet
To start a 4 node testnet run:
```
make localnet-start
```
The nodes bind their RPC servers to ports 46657, 46660, 46662, and 46664 on the host.
This file creates a 4-node network using the gaiadnode image.
The nodes of the network expose their P2P and RPC endpoints to the host machine on ports 46656-46657, 46659-46660, 46661-46662, and 46663-46664 respectively.
To update the binary, just rebuild it and restart the nodes:
```
make build-linux
make localnet-stop
make localnet-start
```
## Configuration
The `make localnet-start` creates files for a 4-node testnet in `./build` by calling the `gaiad testnet` command.
The `./build` directory is mounted to the `/gaiad` mount point to attach the binary and config files to the container.
For instance, to create a single node testnet:
```
cd $GOPATH/src/github.com/cosmos/cosmos-sdk
# Clear the build folder
rm -rf ./build
# Build binary
make build-linux
# Create configuration
docker run -v `pwd`/build:/gaiad tendermint/gaiadnode testnet --o . --v 1
#Run the node
docker run -v `pwd`/build:/gaiad tendermint/gaiadnode
```
## Logging
Log is saved under the attached volume, in the `gaiad.log` file and written on the screen.
## Special binaries
If you have multiple binaries with different names, you can specify which one to run with the BINARY environment variable. The path of the binary is relative to the attached volume.

View File

@ -0,0 +1,16 @@
FROM alpine:3.7
MAINTAINER Greg Szabo <greg@tendermint.com>
RUN apk update && \
apk upgrade && \
apk --no-cache add curl jq file
VOLUME [ /gaiad ]
WORKDIR /gaiad
EXPOSE 46656 46657
ENTRYPOINT ["/usr/bin/wrapper.sh"]
CMD ["start"]
STOPSIGNAL SIGTERM
COPY wrapper.sh /usr/bin/wrapper.sh

View File

@ -0,0 +1,35 @@
#!/usr/bin/env sh
##
## Input parameters
##
BINARY=/gaiad/${BINARY:-gaiad}
ID=${ID:-0}
LOG=${LOG:-gaiad.log}
##
## Assert linux binary
##
if ! [ -f "${BINARY}" ]; then
echo "The binary $(basename "${BINARY}") cannot be found. Please add the binary to the shared folder. Please use the BINARY environment variable if the name of the binary is not 'gaiad' E.g.: -e BINARY=gaiad_my_test_version"
exit 1
fi
BINARY_CHECK="$(file "$BINARY" | grep 'ELF 64-bit LSB executable, x86-64')"
if [ -z "${BINARY_CHECK}" ]; then
echo "Binary needs to be OS linux, ARCH amd64"
exit 1
fi
##
## Run binary with all parameters
##
export GAIADHOME="/gaiad/node${ID}/gaiad"
if [ -d "`dirname ${GAIADHOME}/${LOG}`" ]; then
"$BINARY" --home "$GAIADHOME" "$@" | tee "${GAIADHOME}/${LOG}"
else
"$BINARY" --home "$GAIADHOME" "$@"
fi
chmod 777 -R /gaiad

14
server/config/config.go Normal file
View File

@ -0,0 +1,14 @@
package config
//_____________________________________________________________________
// Configuration structure for command functions that share configuration.
// For example: init, init gen-tx and testnet commands need similar input and run the same code
// Storage for init gen-tx command input parameters
type GenTx struct {
Name string
CliRoot string
Overwrite bool
IP string
}

View File

@ -19,17 +19,33 @@ import (
"github.com/tendermint/go-crypto/keys/words"
cfg "github.com/tendermint/tendermint/config"
"github.com/tendermint/tendermint/p2p"
tmtypes "github.com/tendermint/tendermint/types"
pvm "github.com/tendermint/tendermint/privval"
tmtypes "github.com/tendermint/tendermint/types"
tmcli "github.com/tendermint/tmlibs/cli"
cmn "github.com/tendermint/tmlibs/common"
dbm "github.com/tendermint/tmlibs/db"
clkeys "github.com/cosmos/cosmos-sdk/client/keys"
serverconfig "github.com/cosmos/cosmos-sdk/server/config"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/wire"
)
//Parameter names, for init gen-tx command
var (
FlagName = "name"
FlagClientHome = "home-client"
FlagOWK = "owk"
)
//parameter names, init command
var (
FlagOverwrite = "overwrite"
FlagGenTxs = "gen-txs"
FlagIP = "ip"
FlagChainID = "chain-id"
)
// genesis piece structure for creating combined genesis
type GenesisTx struct {
NodeID string `json:"node_id"`
@ -38,12 +54,13 @@ type GenesisTx struct {
AppGenTx json.RawMessage `json:"app_gen_tx"`
}
var (
flagOverwrite = "overwrite"
flagGenTxs = "gen-txs"
flagIP = "ip"
flagChainID = "chain-id"
)
// Storage for init command input parameters
type InitConfig struct {
ChainID string
GenTxs bool
GenTxsDir string
Overwrite bool
}
// get cmd to initialize all files for tendermint and application
func GenTxCmd(ctx *Context, cdc *wire.Codec, appInit AppInit) *cobra.Command {
@ -54,50 +71,27 @@ func GenTxCmd(ctx *Context, cdc *wire.Codec, appInit AppInit) *cobra.Command {
RunE: func(_ *cobra.Command, args []string) error {
config := ctx.Config
nodeKey, err := p2p.LoadOrGenNodeKey(config.NodeKeyFile())
if err != nil {
return err
}
nodeID := string(nodeKey.ID())
pubKey := readOrCreatePrivValidator(config)
config.SetRoot(viper.GetString(tmcli.HomeFlag))
appGenTx, cliPrint, validator, err := appInit.AppGenTx(cdc, pubKey)
if err != nil {
return err
}
ip := viper.GetString(flagIP)
ip := viper.GetString(FlagIP)
if len(ip) == 0 {
ip, err = externalIP()
eip, err := externalIP()
if err != nil {
return err
}
ip = eip
}
tx := GenesisTx{
NodeID: nodeID,
IP: ip,
Validator: validator,
AppGenTx: appGenTx,
genTxConfig := serverconfig.GenTx{
viper.GetString(FlagName),
viper.GetString(FlagClientHome),
viper.GetBool(FlagOWK),
ip,
}
bz, err := wire.MarshalJSONIndent(cdc, tx)
cliPrint, genTxFile, err := gentxWithConfig(ctx, cdc, appInit, config, genTxConfig)
if err != nil {
return err
}
genTxFile := json.RawMessage(bz)
name := fmt.Sprintf("gentx-%v.json", nodeID)
writePath := filepath.Join(viper.GetString(tmcli.HomeFlag), "config", "gentx")
file := filepath.Join(writePath, name)
err = cmn.EnsureDir(writePath, 0700)
if err != nil {
return err
}
err = cmn.WriteFile(file, bz, 0644)
if err != nil {
return err
}
// print out some key information
toPrint := struct {
AppMessage json.RawMessage `json:"app_message"`
GenTxFile json.RawMessage `json:"gen_tx_file"`
@ -113,11 +107,51 @@ func GenTxCmd(ctx *Context, cdc *wire.Codec, appInit AppInit) *cobra.Command {
return nil
},
}
cmd.Flags().String(flagIP, "", "external facing IP to use if left blank IP will be retrieved from this machine")
cmd.Flags().String(FlagIP, "", "external facing IP to use if left blank IP will be retrieved from this machine")
cmd.Flags().AddFlagSet(appInit.FlagsAppGenTx)
return cmd
}
func gentxWithConfig(ctx *Context, cdc *wire.Codec, appInit AppInit, config *cfg.Config, genTxConfig serverconfig.GenTx) (
cliPrint json.RawMessage, genTxFile json.RawMessage, err error) {
nodeKey, err := p2p.LoadOrGenNodeKey(config.NodeKeyFile())
if err != nil {
return
}
nodeID := string(nodeKey.ID())
pubKey := readOrCreatePrivValidator(config)
appGenTx, cliPrint, validator, err := appInit.AppGenTx(cdc, pubKey, genTxConfig)
if err != nil {
return
}
tx := GenesisTx{
NodeID: nodeID,
IP: genTxConfig.IP,
Validator: validator,
AppGenTx: appGenTx,
}
bz, err := wire.MarshalJSONIndent(cdc, tx)
if err != nil {
return
}
genTxFile = json.RawMessage(bz)
name := fmt.Sprintf("gentx-%v.json", nodeID)
writePath := filepath.Join(config.RootDir, "config", "gentx")
file := filepath.Join(writePath, name)
err = cmn.EnsureDir(writePath, 0700)
if err != nil {
return
}
err = cmn.WriteFile(file, bz, 0644)
if err != nil {
return
}
return
}
// get cmd to initialize all files for tendermint and application
func InitCmd(ctx *Context, cdc *wire.Codec, appInit AppInit) *cobra.Command {
cmd := &cobra.Command{
@ -127,58 +161,18 @@ func InitCmd(ctx *Context, cdc *wire.Codec, appInit AppInit) *cobra.Command {
RunE: func(_ *cobra.Command, _ []string) error {
config := ctx.Config
nodeKey, err := p2p.LoadOrGenNodeKey(config.NodeKeyFile())
config.SetRoot(viper.GetString(tmcli.HomeFlag))
initConfig := InitConfig{
viper.GetString(FlagChainID),
viper.GetBool(FlagGenTxs),
filepath.Join(config.RootDir, "config", "gentx"),
viper.GetBool(FlagOverwrite),
}
chainID, nodeID, appMessage, err := initWithConfig(ctx, cdc, appInit, config, initConfig)
if err != nil {
return err
}
nodeID := string(nodeKey.ID())
pubKey := readOrCreatePrivValidator(config)
chainID := viper.GetString(flagChainID)
if chainID == "" {
chainID = cmn.Fmt("test-chain-%v", cmn.RandStr(6))
}
genFile := config.GenesisFile()
if !viper.GetBool(flagOverwrite) && cmn.FileExists(genFile) {
return fmt.Errorf("genesis.json file already exists: %v", genFile)
}
// process genesis transactions, or otherwise create one for defaults
var appMessage json.RawMessage
var appGenTxs []json.RawMessage
var validators []tmtypes.GenesisValidator
var persistentPeers string
if viper.GetBool(flagGenTxs) {
genTxsDir := filepath.Join(viper.GetString(tmcli.HomeFlag), "config", "gentx")
validators, appGenTxs, persistentPeers, err = processGenTxs(genTxsDir, cdc, appInit)
if err != nil {
return err
}
config.P2P.PersistentPeers = persistentPeers
configFilePath := filepath.Join(viper.GetString(tmcli.HomeFlag), "config", "config.toml")
cfg.WriteConfigFile(configFilePath, config)
} else {
appGenTx, am, validator, err := appInit.AppGenTx(cdc, pubKey)
appMessage = am
if err != nil {
return err
}
validators = []tmtypes.GenesisValidator{validator}
appGenTxs = []json.RawMessage{appGenTx}
}
appState, err := appInit.AppGenState(cdc, appGenTxs)
if err != nil {
return err
}
err = writeGenesisFile(cdc, genFile, chainID, validators, appState)
if err != nil {
return err
}
// print out some key information
toPrint := struct {
ChainID string `json:"chain_id"`
@ -194,19 +188,80 @@ func InitCmd(ctx *Context, cdc *wire.Codec, appInit AppInit) *cobra.Command {
return err
}
fmt.Println(string(out))
return nil
},
}
cmd.Flags().BoolP(flagOverwrite, "o", false, "overwrite the genesis.json file")
cmd.Flags().String(flagChainID, "", "genesis file chain-id, if left blank will be randomly created")
cmd.Flags().Bool(flagGenTxs, false, "apply genesis transactions from [--home]/config/gentx/")
cmd.Flags().BoolP(FlagOverwrite, "o", false, "overwrite the genesis.json file")
cmd.Flags().String(FlagChainID, "", "genesis file chain-id, if left blank will be randomly created")
cmd.Flags().Bool(FlagGenTxs, false, "apply genesis transactions from [--home]/config/gentx/")
cmd.Flags().AddFlagSet(appInit.FlagsAppGenState)
cmd.Flags().AddFlagSet(appInit.FlagsAppGenTx) // need to add this flagset for when no GenTx's provided
cmd.AddCommand(GenTxCmd(ctx, cdc, appInit))
return cmd
}
func initWithConfig(ctx *Context, cdc *wire.Codec, appInit AppInit, config *cfg.Config, initConfig InitConfig) (
chainID string, nodeID string, appMessage json.RawMessage, err error) {
nodeKey, err := p2p.LoadOrGenNodeKey(config.NodeKeyFile())
if err != nil {
return
}
nodeID = string(nodeKey.ID())
pubKey := readOrCreatePrivValidator(config)
if initConfig.ChainID == "" {
initConfig.ChainID = fmt.Sprintf("test-chain-%v", cmn.RandStr(6))
}
chainID = initConfig.ChainID
genFile := config.GenesisFile()
if !initConfig.Overwrite && cmn.FileExists(genFile) {
err = fmt.Errorf("genesis.json file already exists: %v", genFile)
return
}
// process genesis transactions, or otherwise create one for defaults
var appGenTxs []json.RawMessage
var validators []tmtypes.GenesisValidator
var persistentPeers string
if initConfig.GenTxs {
validators, appGenTxs, persistentPeers, err = processGenTxs(initConfig.GenTxsDir, cdc, appInit)
if err != nil {
return
}
config.P2P.PersistentPeers = persistentPeers
configFilePath := filepath.Join(config.RootDir, "config", "config.toml")
cfg.WriteConfigFile(configFilePath, config)
} else {
genTxConfig := serverconfig.GenTx{
viper.GetString(FlagName),
viper.GetString(FlagClientHome),
viper.GetBool(FlagOWK),
"127.0.0.1",
}
appGenTx, am, validator, err := appInit.AppGenTx(cdc, pubKey, genTxConfig)
appMessage = am
if err != nil {
return "", "", nil, err
}
validators = []tmtypes.GenesisValidator{validator}
appGenTxs = []json.RawMessage{appGenTx}
}
appState, err := appInit.AppGenState(cdc, appGenTxs)
if err != nil {
return
}
err = writeGenesisFile(cdc, genFile, initConfig.ChainID, validators, appState)
if err != nil {
return
}
return
}
// append a genesis-piece
func processGenTxs(genTxsDir string, cdc *wire.Codec, appInit AppInit) (
validators []tmtypes.GenesisValidator, appGenTxs []json.RawMessage, persistentPeers string, err error) {
@ -315,7 +370,7 @@ type AppInit struct {
FlagsAppGenTx *pflag.FlagSet
// create the application genesis tx
AppGenTx func(cdc *wire.Codec, pk crypto.PubKey) (
AppGenTx func(cdc *wire.Codec, pk crypto.PubKey, genTxConfig serverconfig.GenTx) (
appGenTx, cliPrint json.RawMessage, validator tmtypes.GenesisValidator, err error)
// AppGenState creates the core parameters initialization. It takes in a
@ -337,7 +392,7 @@ type SimpleGenTx struct {
}
// Generate a genesis transaction
func SimpleAppGenTx(cdc *wire.Codec, pk crypto.PubKey) (
func SimpleAppGenTx(cdc *wire.Codec, pk crypto.PubKey, genTxConfig serverconfig.GenTx) (
appGenTx, cliPrint json.RawMessage, validator tmtypes.GenesisValidator, err error) {
var addr sdk.Address

View File

@ -34,6 +34,10 @@ func TestGenTxCmd(t *testing.T) {
// TODO
}
func TestTestnetFilesCmd(t *testing.T) {
// TODO
}
func TestSimpleAppGenTx(t *testing.T) {
// TODO
}

View File

@ -12,6 +12,7 @@ import (
"github.com/tendermint/tmlibs/log"
bam "github.com/cosmos/cosmos-sdk/baseapp"
gc "github.com/cosmos/cosmos-sdk/server/config"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/wire"
)
@ -124,7 +125,7 @@ func AppGenState(_ *wire.Codec, _ []json.RawMessage) (appState json.RawMessage,
}
// Return a validator, not much else
func AppGenTx(_ *wire.Codec, pk crypto.PubKey) (
func AppGenTx(_ *wire.Codec, pk crypto.PubKey, genTxConfig gc.GenTx) (
appGenTx, cliPrint json.RawMessage, validator tmtypes.GenesisValidator, err error) {
validator = tmtypes.GenesisValidator{

177
server/testnet.go Normal file
View File

@ -0,0 +1,177 @@
package server
import (
"fmt"
"net"
"path/filepath"
"github.com/spf13/cobra"
gc "github.com/cosmos/cosmos-sdk/server/config"
"github.com/cosmos/cosmos-sdk/wire"
"github.com/spf13/viper"
cfg "github.com/tendermint/tendermint/config"
cmn "github.com/tendermint/tmlibs/common"
"os"
)
var (
nodeDirPrefix = "node-dir-prefix"
nValidators = "v"
outputDir = "o"
startingIPAddress = "starting-ip-address"
)
const nodeDirPerm = 0755
// get cmd to initialize all files for tendermint testnet and application
func TestnetFilesCmd(ctx *Context, cdc *wire.Codec, appInit AppInit) *cobra.Command {
cmd := &cobra.Command{
Use: "testnet",
Short: "Initialize files for a Gaiad 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:
gaiad testnet --v 4 --output-dir ./output --starting-ip-address 192.168.10.2
`,
RunE: func(_ *cobra.Command, _ []string) error {
config := ctx.Config
err := testnetWithConfig(config, ctx, cdc, appInit)
return err
},
}
cmd.Flags().Int(nValidators, 4,
"Number of validators to initialize the testnet with")
cmd.Flags().String(outputDir, "./mytestnet",
"Directory to store initialization data for the testnet")
cmd.Flags().String(nodeDirPrefix, "node",
"Prefix the directory name for each node with (node results in node0, node1, ...)")
cmd.Flags().String(startingIPAddress, "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, ...)")
return cmd
}
func testnetWithConfig(config *cfg.Config, ctx *Context, cdc *wire.Codec, appInit AppInit) error {
outDir := viper.GetString(outputDir)
// Generate private key, node ID, initial transaction
for i := 0; i < viper.GetInt(nValidators); i++ {
nodeDirName := fmt.Sprintf("%s%d", viper.GetString(nodeDirPrefix), i)
nodeDir := filepath.Join(outDir, nodeDirName, "gaiad")
clientDir := filepath.Join(outDir, nodeDirName, "gaiacli")
gentxsDir := filepath.Join(outDir, "gentxs")
config.SetRoot(nodeDir)
err := os.MkdirAll(filepath.Join(nodeDir, "config"), nodeDirPerm)
if err != nil {
_ = os.RemoveAll(outDir)
return err
}
err = os.MkdirAll(clientDir, nodeDirPerm)
if err != nil {
_ = os.RemoveAll(outDir)
return err
}
config.Moniker = nodeDirName
ip := viper.GetString(startingIPAddress)
if len(ip) == 0 {
ip, err = externalIP()
if err != nil {
return err
}
} else {
ip, err = calculateIP(ip, i)
if err != nil {
return err
}
}
genTxConfig := gc.GenTx{
nodeDirName,
clientDir,
true,
ip,
}
// Run `init gen-tx` and generate initial transactions
cliPrint, genTxFile, err := gentxWithConfig(ctx, cdc, appInit, config, genTxConfig)
if err != nil {
return err
}
// Save private key seed words
name := fmt.Sprintf("%v.json", "key_seed")
writePath := filepath.Join(clientDir)
file := filepath.Join(writePath, name)
err = cmn.EnsureDir(writePath, 0700)
if err != nil {
return err
}
err = cmn.WriteFile(file, cliPrint, 0600)
if err != nil {
return err
}
// Gather gentxs folder
name = fmt.Sprintf("%v.json", nodeDirName)
writePath = filepath.Join(gentxsDir)
file = filepath.Join(writePath, name)
err = cmn.EnsureDir(writePath, 0700)
if err != nil {
return err
}
err = cmn.WriteFile(file, genTxFile, 0644)
if err != nil {
return err
}
}
// Generate genesis.json and config.toml
chainID := "chain-" + cmn.RandStr(6)
for i := 0; i < viper.GetInt(nValidators); i++ {
nodeDirName := fmt.Sprintf("%s%d", viper.GetString(nodeDirPrefix), i)
nodeDir := filepath.Join(outDir, nodeDirName, "gaiad")
gentxsDir := filepath.Join(outDir, "gentxs")
initConfig := InitConfig{
chainID,
true,
gentxsDir,
true,
}
config.Moniker = nodeDirName
config.SetRoot(nodeDir)
// Run `init` and generate genesis.json and config.toml
_, _, _, err := initWithConfig(ctx, cdc, appInit, config, initConfig)
if err != nil {
return err
}
}
fmt.Printf("Successfully initialized %v node directories\n", viper.GetInt(nValidators))
return nil
}
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
}

View File

@ -85,6 +85,7 @@ func AddCommands(
rootCmd.AddCommand(
InitCmd(ctx, cdc, appInit),
TestnetFilesCmd(ctx, cdc, appInit),
StartCmd(ctx, appCreator),
UnsafeResetAllCmd(ctx),
client.LineBreak,