Merge pull request #1208 from cosmos/greg/testnet-command-2
Testnet command and make localnet target
This commit is contained in:
commit
e4685ecba5
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
21
Makefile
21
Makefile
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
# Makefile for the "gaiadnode" docker image.
|
||||
|
||||
all:
|
||||
docker build --tag tendermint/gaiadnode gaiadnode
|
||||
|
||||
.PHONY: all
|
||||
|
|
@ -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.
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
}
|
247
server/init.go
247
server/init.go
|
@ -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
|
||||
|
|
|
@ -34,6 +34,10 @@ func TestGenTxCmd(t *testing.T) {
|
|||
// TODO
|
||||
}
|
||||
|
||||
func TestTestnetFilesCmd(t *testing.T) {
|
||||
// TODO
|
||||
}
|
||||
|
||||
func TestSimpleAppGenTx(t *testing.T) {
|
||||
// TODO
|
||||
}
|
||||
|
|
|
@ -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{
|
||||
|
|
|
@ -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
|
||||
}
|
|
@ -85,6 +85,7 @@ func AddCommands(
|
|||
|
||||
rootCmd.AddCommand(
|
||||
InitCmd(ctx, cdc, appInit),
|
||||
TestnetFilesCmd(ctx, cdc, appInit),
|
||||
StartCmd(ctx, appCreator),
|
||||
UnsafeResetAllCmd(ctx),
|
||||
client.LineBreak,
|
||||
|
|
Loading…
Reference in New Issue