Merge branch 'develop' into sunny/get_all_delegations_validator_querier

This commit is contained in:
Christopher Goes
2018-11-09 15:28:03 +01:00
committed by GitHub
86 changed files with 1667 additions and 837 deletions
+20 -12
View File
@@ -210,9 +210,6 @@ func (app *GaiaApp) EndBlocker(ctx sdk.Context, req abci.RequestEndBlock) abci.R
tags := gov.EndBlocker(ctx, app.govKeeper)
validatorUpdates := stake.EndBlocker(ctx, app.stakeKeeper)
// Add these new validators to the addr -> pubkey map.
app.slashingKeeper.AddValidators(ctx, validatorUpdates)
return abci.ResponseEndBlock{
ValidatorUpdates: validatorUpdates,
Tags: tags,
@@ -231,6 +228,10 @@ func (app *GaiaApp) initChainer(ctx sdk.Context, req abci.RequestInitChain) abci
// return sdk.ErrGenesisParse("").TraceCause(err, "")
}
// sort by account number to maintain consistency
sort.Slice(genesisState.Accounts, func(i, j int) bool {
return genesisState.Accounts[i].AccountNumber < genesisState.Accounts[j].AccountNumber
})
// load the accounts
for _, gacc := range genesisState.Accounts {
acc := gacc.ToAccount()
@@ -244,7 +245,8 @@ func (app *GaiaApp) initChainer(ctx sdk.Context, req abci.RequestInitChain) abci
panic(err) // TODO find a way to do this w/o panics
}
// load the address to pubkey map
// initialize module-specific stores
auth.InitGenesis(ctx, app.feeCollectionKeeper, genesisState.AuthData)
slashing.InitGenesis(ctx, app.slashingKeeper, genesisState.SlashingData, genesisState.StakeData)
gov.InitGenesis(ctx, app.govKeeper, genesisState.GovData)
mint.InitGenesis(ctx, app.mintKeeper, genesisState.MintData)
@@ -270,7 +272,6 @@ func (app *GaiaApp) initChainer(ctx sdk.Context, req abci.RequestInitChain) abci
validators = app.stakeKeeper.ApplyAndReturnValidatorSetUpdates(ctx)
}
app.slashingKeeper.AddValidators(ctx, validators)
// sanity check
if len(req.Validators) > 0 {
@@ -306,11 +307,12 @@ func (app *GaiaApp) ExportAppStateAndValidators() (appState json.RawMessage, val
app.accountKeeper.IterateAccounts(ctx, appendAccount)
genState := NewGenesisState(
accounts,
stake.WriteGenesis(ctx, app.stakeKeeper),
mint.WriteGenesis(ctx, app.mintKeeper),
distr.WriteGenesis(ctx, app.distrKeeper),
gov.WriteGenesis(ctx, app.govKeeper),
slashing.GenesisState{}, // TODO create write methods
auth.ExportGenesis(ctx, app.feeCollectionKeeper),
stake.ExportGenesis(ctx, app.stakeKeeper),
mint.ExportGenesis(ctx, app.mintKeeper),
distr.ExportGenesis(ctx, app.distrKeeper),
gov.ExportGenesis(ctx, app.govKeeper),
slashing.ExportGenesis(ctx, app.slashingKeeper),
)
appState, err = codec.MarshalJSONIndent(app.cdc, genState)
if err != nil {
@@ -337,12 +339,15 @@ var _ sdk.StakingHooks = Hooks{}
// nolint
func (h Hooks) OnValidatorCreated(ctx sdk.Context, valAddr sdk.ValAddress) {
h.dh.OnValidatorCreated(ctx, valAddr)
h.sh.OnValidatorCreated(ctx, valAddr)
}
func (h Hooks) OnValidatorModified(ctx sdk.Context, valAddr sdk.ValAddress) {
h.dh.OnValidatorModified(ctx, valAddr)
h.sh.OnValidatorModified(ctx, valAddr)
}
func (h Hooks) OnValidatorRemoved(ctx sdk.Context, valAddr sdk.ValAddress) {
h.dh.OnValidatorRemoved(ctx, valAddr)
func (h Hooks) OnValidatorRemoved(ctx sdk.Context, consAddr sdk.ConsAddress, valAddr sdk.ValAddress) {
h.dh.OnValidatorRemoved(ctx, consAddr, valAddr)
h.sh.OnValidatorRemoved(ctx, consAddr, valAddr)
}
func (h Hooks) OnValidatorBonded(ctx sdk.Context, consAddr sdk.ConsAddress, valAddr sdk.ValAddress) {
h.dh.OnValidatorBonded(ctx, consAddr, valAddr)
@@ -358,10 +363,13 @@ func (h Hooks) OnValidatorBeginUnbonding(ctx sdk.Context, consAddr sdk.ConsAddre
}
func (h Hooks) OnDelegationCreated(ctx sdk.Context, delAddr sdk.AccAddress, valAddr sdk.ValAddress) {
h.dh.OnDelegationCreated(ctx, delAddr, valAddr)
h.sh.OnDelegationCreated(ctx, delAddr, valAddr)
}
func (h Hooks) OnDelegationSharesModified(ctx sdk.Context, delAddr sdk.AccAddress, valAddr sdk.ValAddress) {
h.dh.OnDelegationSharesModified(ctx, delAddr, valAddr)
h.sh.OnDelegationSharesModified(ctx, delAddr, valAddr)
}
func (h Hooks) OnDelegationRemoved(ctx sdk.Context, delAddr sdk.AccAddress, valAddr sdk.ValAddress) {
h.dh.OnDelegationRemoved(ctx, delAddr, valAddr)
h.sh.OnDelegationRemoved(ctx, delAddr, valAddr)
}
+20 -10
View File
@@ -32,6 +32,7 @@ var (
// State to Unmarshal
type GenesisState struct {
Accounts []GenesisAccount `json:"accounts"`
AuthData auth.GenesisState `json:"auth"`
StakeData stake.GenesisState `json:"stake"`
MintData mint.GenesisState `json:"mint"`
DistrData distr.GenesisState `json:"distr"`
@@ -40,11 +41,12 @@ type GenesisState struct {
GenTxs []json.RawMessage `json:"gentxs"`
}
func NewGenesisState(accounts []GenesisAccount, stakeData stake.GenesisState, mintData mint.GenesisState,
func NewGenesisState(accounts []GenesisAccount, authData auth.GenesisState, stakeData stake.GenesisState, mintData mint.GenesisState,
distrData distr.GenesisState, govData gov.GenesisState, slashingData slashing.GenesisState) GenesisState {
return GenesisState{
Accounts: accounts,
AuthData: authData,
StakeData: stakeData,
MintData: mintData,
DistrData: distrData,
@@ -53,31 +55,39 @@ func NewGenesisState(accounts []GenesisAccount, stakeData stake.GenesisState, mi
}
}
// GenesisAccount doesn't need pubkey or sequence
// nolint
type GenesisAccount struct {
Address sdk.AccAddress `json:"address"`
Coins sdk.Coins `json:"coins"`
Address sdk.AccAddress `json:"address"`
Coins sdk.Coins `json:"coins"`
Sequence int64 `json:"sequence_number"`
AccountNumber int64 `json:"account_number"`
}
func NewGenesisAccount(acc *auth.BaseAccount) GenesisAccount {
return GenesisAccount{
Address: acc.Address,
Coins: acc.Coins,
Address: acc.Address,
Coins: acc.Coins,
AccountNumber: acc.AccountNumber,
Sequence: acc.Sequence,
}
}
func NewGenesisAccountI(acc auth.Account) GenesisAccount {
return GenesisAccount{
Address: acc.GetAddress(),
Coins: acc.GetCoins(),
Address: acc.GetAddress(),
Coins: acc.GetCoins(),
AccountNumber: acc.GetAccountNumber(),
Sequence: acc.GetSequence(),
}
}
// convert GenesisAccount to auth.BaseAccount
func (ga *GenesisAccount) ToAccount() (acc *auth.BaseAccount) {
return &auth.BaseAccount{
Address: ga.Address,
Coins: ga.Coins.Sort(),
Address: ga.Address,
Coins: ga.Coins.Sort(),
AccountNumber: ga.AccountNumber,
Sequence: ga.Sequence,
}
}
+6 -3
View File
@@ -2,9 +2,10 @@ package app
import (
"encoding/json"
"testing"
"github.com/tendermint/tendermint/crypto/secp256k1"
tmtypes "github.com/tendermint/tendermint/types"
"testing"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/auth"
@@ -91,8 +92,10 @@ func TestGaiaAppGenState(t *testing.T) {
func makeMsg(name string, pk crypto.PubKey) auth.StdTx {
desc := stake.NewDescription(name, "", "", "")
comm := stakeTypes.CommissionMsg{}
msg := stake.NewMsgCreateValidator(sdk.ValAddress(pk.Address()), pk, sdk.NewInt64Coin(bondDenom,
50), desc, comm)
msg := stake.NewMsgCreateValidator(
sdk.ValAddress(pk.Address()), pk,
sdk.NewInt64Coin(bondDenom, 50), desc, comm,
)
return auth.NewStdTx([]sdk.Msg{msg}, auth.StdFee{}, nil, "")
}
+103 -5
View File
@@ -12,6 +12,7 @@ import (
"github.com/stretchr/testify/require"
abci "github.com/tendermint/tendermint/abci/types"
dbm "github.com/tendermint/tendermint/libs/db"
"github.com/tendermint/tendermint/libs/log"
@@ -70,15 +71,15 @@ func appStateFn(r *rand.Rand, accs []simulation.Account) json.RawMessage {
// Random genesis states
govGenesis := gov.GenesisState{
StartingProposalID: int64(r.Intn(100)),
DepositProcedure: gov.DepositProcedure{
StartingProposalID: uint64(r.Intn(100)),
DepositParams: gov.DepositParams{
MinDeposit: sdk.Coins{sdk.NewInt64Coin("steak", int64(r.Intn(1e3)))},
MaxDepositPeriod: time.Duration(r.Intn(2*172800)) * time.Second,
},
VotingProcedure: gov.VotingProcedure{
VotingParams: gov.VotingParams{
VotingPeriod: time.Duration(r.Intn(2*172800)) * time.Second,
},
TallyingProcedure: gov.TallyingProcedure{
TallyParams: gov.TallyParams{
Threshold: sdk.NewDecWithPrec(5, 1),
Veto: sdk.NewDecWithPrec(334, 3),
GovernancePenalty: sdk.NewDecWithPrec(1, 2),
@@ -166,7 +167,7 @@ func testAndRunTxs(app *GaiaApp) []simulation.WeightedOperation {
{50, distrsim.SimulateMsgWithdrawDelegatorReward(app.accountKeeper, app.distrKeeper)},
{50, distrsim.SimulateMsgWithdrawValidatorRewardsAll(app.accountKeeper, app.distrKeeper)},
{5, govsim.SimulateSubmittingVotingAndSlashingForProposal(app.govKeeper, app.stakeKeeper)},
{100, govsim.SimulateMsgDeposit(app.govKeeper, app.stakeKeeper)},
{100, govsim.SimulateMsgDeposit(app.govKeeper)},
{100, stakesim.SimulateMsgCreateValidator(app.accountKeeper, app.stakeKeeper)},
{5, stakesim.SimulateMsgEditValidator(app.stakeKeeper)},
{100, stakesim.SimulateMsgDelegate(app.accountKeeper, app.stakeKeeper)},
@@ -266,6 +267,103 @@ func TestFullGaiaSimulation(t *testing.T) {
require.Nil(t, err)
}
func TestGaiaImportExport(t *testing.T) {
if !enabled {
t.Skip("Skipping Gaia import/export simulation")
}
// Setup Gaia application
var logger log.Logger
if verbose {
logger = log.TestingLogger()
} else {
logger = log.NewNopLogger()
}
var db dbm.DB
dir, _ := ioutil.TempDir("", "goleveldb-gaia-sim")
db, _ = dbm.NewGoLevelDB("Simulation", dir)
defer func() {
db.Close()
os.RemoveAll(dir)
}()
app := NewGaiaApp(logger, db, nil)
require.Equal(t, "GaiaApp", app.Name())
// Run randomized simulation
err := simulation.SimulateFromSeed(
t, app.BaseApp, appStateFn, seed,
testAndRunTxs(app),
[]simulation.RandSetup{},
invariants(app),
numBlocks,
blockSize,
commit,
)
if commit {
// for memdb:
// fmt.Println("Database Size", db.Stats()["database.size"])
fmt.Println("GoLevelDB Stats")
fmt.Println(db.Stats()["leveldb.stats"])
fmt.Println("GoLevelDB cached block size", db.Stats()["leveldb.cachedblock"])
}
require.Nil(t, err)
fmt.Printf("Exporting genesis...\n")
appState, _, err := app.ExportAppStateAndValidators()
if err != nil {
panic(err)
}
fmt.Printf("Importing genesis...\n")
newDir, _ := ioutil.TempDir("", "goleveldb-gaia-sim-2")
newDB, _ := dbm.NewGoLevelDB("Simulation-2", dir)
defer func() {
newDB.Close()
os.RemoveAll(newDir)
}()
newApp := NewGaiaApp(log.NewNopLogger(), newDB, nil)
require.Equal(t, "GaiaApp", newApp.Name())
request := abci.RequestInitChain{
AppStateBytes: appState,
}
newApp.InitChain(request)
newApp.Commit()
fmt.Printf("Comparing stores...\n")
ctxA := app.NewContext(true, abci.Header{})
ctxB := newApp.NewContext(true, abci.Header{})
type StoreKeysPrefixes struct {
A sdk.StoreKey
B sdk.StoreKey
Prefixes [][]byte
}
storeKeysPrefixes := []StoreKeysPrefixes{
{app.keyMain, newApp.keyMain, [][]byte{}},
{app.keyAccount, newApp.keyAccount, [][]byte{}},
{app.keyStake, newApp.keyStake, [][]byte{stake.UnbondingQueueKey, stake.RedelegationQueueKey, stake.ValidatorQueueKey}}, // ordering may change but it doesn't matter
{app.keySlashing, newApp.keySlashing, [][]byte{}},
{app.keyMint, newApp.keyMint, [][]byte{}},
{app.keyDistr, newApp.keyDistr, [][]byte{}},
{app.keyFeeCollection, newApp.keyFeeCollection, [][]byte{}},
{app.keyParams, newApp.keyParams, [][]byte{}},
{app.keyGov, newApp.keyGov, [][]byte{}},
}
for _, storeKeysPrefix := range storeKeysPrefixes {
storeKeyA := storeKeysPrefix.A
storeKeyB := storeKeysPrefix.B
prefixes := storeKeysPrefix.Prefixes
storeA := ctxA.KVStore(storeKeyA)
storeB := ctxB.KVStore(storeKeyB)
kvA, kvB, count, equal := sdk.DiffKVStores(storeA, storeB, prefixes)
fmt.Printf("Compared %d key/value pairs between %s and %s\n", count, storeKeyA, storeKeyB)
require.True(t, equal, "unequal stores: %s / %s:\nstore A %s (%X) => %s (%X)\nstore B %s (%X) => %s (%X)",
storeKeyA, storeKeyB, kvA.Key, kvA.Key, kvA.Value, kvA.Value, kvB.Key, kvB.Key, kvB.Value, kvB.Value)
}
}
// TODO: Make another test for the fuzzer itself, which just has noOp txs
// and doesn't depend on gaia
func TestAppStateDeterminism(t *testing.T) {
+7 -6
View File
@@ -5,13 +5,14 @@ package clitest
import (
"encoding/json"
"fmt"
"github.com/tendermint/tendermint/types"
"io/ioutil"
"os"
"path"
"path/filepath"
"testing"
"github.com/tendermint/tendermint/types"
"github.com/stretchr/testify/require"
abci "github.com/tendermint/tendermint/abci/types"
@@ -352,7 +353,7 @@ func TestGaiaCLISubmitProposal(t *testing.T) {
require.Equal(t, int64(45), fooAcc.GetCoins().AmountOf("steak").Int64())
proposal1 := executeGetProposal(t, fmt.Sprintf("gaiacli query proposal --proposal-id=1 --output=json %v", flags))
require.Equal(t, int64(1), proposal1.GetProposalID())
require.Equal(t, uint64(1), proposal1.GetProposalID())
require.Equal(t, gov.StatusDepositPeriod, proposal1.GetStatus())
proposalsQuery, _ = tests.ExecuteT(t, fmt.Sprintf("gaiacli query proposals %v", flags), "")
@@ -395,7 +396,7 @@ func TestGaiaCLISubmitProposal(t *testing.T) {
fooAcc = executeGetAccount(t, fmt.Sprintf("gaiacli query account %s %v", fooAddr, flags))
require.Equal(t, int64(35), fooAcc.GetCoins().AmountOf("steak").Int64())
proposal1 = executeGetProposal(t, fmt.Sprintf("gaiacli query proposal --proposal-id=1 --output=json %v", flags))
require.Equal(t, int64(1), proposal1.GetProposalID())
require.Equal(t, uint64(1), proposal1.GetProposalID())
require.Equal(t, gov.StatusVotingPeriod, proposal1.GetStatus())
voteStr := fmt.Sprintf("gaiacli tx vote %v", flags)
@@ -417,12 +418,12 @@ func TestGaiaCLISubmitProposal(t *testing.T) {
tests.WaitForNextNBlocksTM(2, port)
vote := executeGetVote(t, fmt.Sprintf("gaiacli query vote --proposal-id=1 --voter=%s --output=json %v", fooAddr, flags))
require.Equal(t, int64(1), vote.ProposalID)
require.Equal(t, uint64(1), vote.ProposalID)
require.Equal(t, gov.OptionYes, vote.Option)
votes := executeGetVotes(t, fmt.Sprintf("gaiacli query votes --proposal-id=1 --output=json %v", flags))
require.Len(t, votes, 1)
require.Equal(t, int64(1), votes[0].ProposalID)
require.Equal(t, uint64(1), votes[0].ProposalID)
require.Equal(t, gov.OptionYes, votes[0].Option)
proposalsQuery, _ = tests.ExecuteT(t, fmt.Sprintf("gaiacli query proposals --status=DepositPeriod %v", flags), "")
@@ -442,7 +443,7 @@ func TestGaiaCLISubmitProposal(t *testing.T) {
executeWrite(t, spStr, app.DefaultKeyPass)
tests.WaitForNextNBlocksTM(2, port)
proposalsQuery, _ = tests.ExecuteT(t, fmt.Sprintf("gaiacli query proposals --latest=1 %v", flags), "")
proposalsQuery, _ = tests.ExecuteT(t, fmt.Sprintf("gaiacli query proposals --limit=1 %v", flags), "")
require.Equal(t, " 2 - Apples", proposalsQuery)
}
+20 -21
View File
@@ -2,7 +2,6 @@ package init
import (
"encoding/json"
"io/ioutil"
"path/filepath"
"github.com/cosmos/cosmos-sdk/client"
@@ -12,7 +11,6 @@ import (
"github.com/cosmos/cosmos-sdk/x/auth"
"github.com/spf13/cobra"
"github.com/spf13/viper"
"github.com/tendermint/go-amino"
cfg "github.com/tendermint/tendermint/config"
"github.com/tendermint/tendermint/crypto"
"github.com/tendermint/tendermint/libs/cli"
@@ -35,12 +33,13 @@ func CollectGenTxsCmd(ctx *server.Context, cdc *codec.Codec) *cobra.Command {
RunE: func(_ *cobra.Command, _ []string) error {
config := ctx.Config
config.SetRoot(viper.GetString(cli.HomeFlag))
name := viper.GetString(client.FlagName)
nodeID, valPubKey, err := InitializeNodeValidatorFiles(config)
if err != nil {
return err
}
genDoc, err := loadGenesisDoc(cdc, config.GenesisFile())
if err != nil {
return err
@@ -59,12 +58,14 @@ func CollectGenTxsCmd(ctx *server.Context, cdc *codec.Codec) *cobra.Command {
NodeID: nodeID,
ValPubKey: valPubKey,
}
appMessage, err := genAppStateFromConfig(cdc, config, initCfg, genDoc)
if err != nil {
return err
}
toPrint.AppMessage = appMessage
// print out some key information
return displayInfo(cdc, toPrint)
},
@@ -74,23 +75,29 @@ func CollectGenTxsCmd(ctx *server.Context, cdc *codec.Codec) *cobra.Command {
return cmd
}
func genAppStateFromConfig(cdc *codec.Codec, config *cfg.Config, initCfg initConfig,
genDoc types.GenesisDoc) (appState json.RawMessage, err error) {
func genAppStateFromConfig(
cdc *codec.Codec, config *cfg.Config, initCfg initConfig, genDoc types.GenesisDoc,
) (appState json.RawMessage, err error) {
genFile := config.GenesisFile()
// process genesis transactions, else create default genesis.json
var appGenTxs []auth.StdTx
var persistentPeers string
var genTxs []json.RawMessage
var jsonRawTx json.RawMessage
var (
appGenTxs []auth.StdTx
persistentPeers string
genTxs []json.RawMessage
jsonRawTx json.RawMessage
)
// process genesis transactions, else create default genesis.json
appGenTxs, persistentPeers, err = app.CollectStdTxs(
cdc, config.Moniker, initCfg.GenTxsDir, genDoc)
cdc, config.Moniker, initCfg.GenTxsDir, genDoc,
)
if err != nil {
return
}
genTxs = make([]json.RawMessage, len(appGenTxs))
config.P2P.PersistentPeers = persistentPeers
for i, stdTx := range appGenTxs {
jsonRawTx, err = cdc.MarshalJSON(stdTx)
if err != nil {
@@ -100,20 +107,12 @@ func genAppStateFromConfig(cdc *codec.Codec, config *cfg.Config, initCfg initCon
}
cfg.WriteConfigFile(filepath.Join(config.RootDir, "config", "config.toml"), config)
appState, err = app.GaiaAppGenStateJSON(cdc, genDoc, genTxs)
if err != nil {
return
}
err = WriteGenesisFile(genFile, initCfg.ChainID, nil, appState)
return
}
func loadGenesisDoc(cdc *amino.Codec, genFile string) (genDoc types.GenesisDoc, err error) {
genContents, err := ioutil.ReadFile(genFile)
if err != nil {
return
}
err = cdc.UnmarshalJSON(genContents, &genDoc)
err = ExportGenesisFile(genFile, initCfg.ChainID, nil, appState)
return
}
+1 -56
View File
@@ -3,7 +3,6 @@ package init
import (
"encoding/json"
"fmt"
"github.com/tendermint/tendermint/privval"
"os"
"path/filepath"
@@ -14,11 +13,8 @@ import (
"github.com/spf13/cobra"
"github.com/spf13/viper"
cfg "github.com/tendermint/tendermint/config"
"github.com/tendermint/tendermint/crypto"
"github.com/tendermint/tendermint/libs/cli"
"github.com/tendermint/tendermint/libs/common"
"github.com/tendermint/tendermint/p2p"
"github.com/tendermint/tendermint/types"
)
const (
@@ -74,7 +70,7 @@ func InitCmd(ctx *server.Context, cdc *codec.Codec, appInit server.AppInit) *cob
viper.GetBool(flagOverwrite)); err != nil {
return err
}
if err = WriteGenesisFile(genFile, chainID, nil, appState); err != nil {
if err = ExportGenesisFile(genFile, chainID, nil, appState); err != nil {
return err
}
@@ -97,54 +93,3 @@ func InitCmd(ctx *server.Context, cdc *codec.Codec, appInit server.AppInit) *cob
cmd.Flags().String(flagMoniker, "", "set the validator's moniker")
return cmd
}
// InitializeNodeValidatorFiles creates private validator and p2p configuration files.
func InitializeNodeValidatorFiles(config *cfg.Config) (nodeID string, valPubKey crypto.PubKey, err error) {
nodeKey, err := p2p.LoadOrGenNodeKey(config.NodeKeyFile())
if err != nil {
return
}
nodeID = string(nodeKey.ID())
valPubKey = ReadOrCreatePrivValidator(config.PrivValidatorFile())
return
}
// WriteGenesisFile creates and writes the genesis configuration to disk. An
// error is returned if building or writing the configuration to file fails.
// nolint: unparam
func WriteGenesisFile(genesisFile, chainID string, validators []types.GenesisValidator, appState json.RawMessage) error {
genDoc := types.GenesisDoc{
ChainID: chainID,
Validators: validators,
AppState: appState,
}
if err := genDoc.ValidateAndComplete(); err != nil {
return err
}
return genDoc.SaveAs(genesisFile)
}
// 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()
}
func initializeEmptyGenesis(cdc *codec.Codec, genFile string, chainID string,
overwrite bool) (appState json.RawMessage, err error) {
if !overwrite && common.FileExists(genFile) {
err = fmt.Errorf("genesis.json file already exists: %v", genFile)
return
}
return codec.MarshalJSONIndent(cdc, app.NewDefaultGenesisState())
}
+126 -51
View File
@@ -3,6 +3,10 @@ package init
import (
"encoding/json"
"fmt"
"net"
"os"
"path/filepath"
"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/cmd/gaia/app"
"github.com/cosmos/cosmos-sdk/codec"
@@ -10,10 +14,6 @@ import (
"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/types"
"net"
"os"
"path/filepath"
"github.com/cosmos/cosmos-sdk/server"
"github.com/spf13/cobra"
@@ -21,16 +21,17 @@ import (
cfg "github.com/tendermint/tendermint/config"
"github.com/tendermint/tendermint/crypto"
cmn "github.com/tendermint/tendermint/libs/common"
"github.com/tendermint/tendermint/types"
tmtime "github.com/tendermint/tendermint/types/time"
)
var (
nodeDirPrefix = "node-dir-prefix"
nValidators = "v"
outputDir = "output-dir"
nodeDaemonHome = "node-daemon-home"
nodeCliHome = "node-cli-home"
startingIPAddress = "starting-ip-address"
flagNodeDirPrefix = "node-dir-prefix"
flagNumValidators = "v"
flagOutputDir = "output-dir"
flagNodeDaemonHome = "node-daemon-home"
flagNodeCliHome = "node-cli-home"
flagStartingIPAddress = "starting-ip-address"
)
const nodeDirPerm = 0755
@@ -48,50 +49,59 @@ necessary files (private validator, genesis, config, etc.).
Note, strict routability for addresses is turned off in the config file.
Example:
gaiad testnet --v 4 --o ./output --starting-ip-address 192.168.10.2
gaiad testnet --v 4 --output-dir ./output --starting-ip-address 192.168.10.2
`,
RunE: func(_ *cobra.Command, _ []string) error {
config := ctx.Config
return testnetWithConfig(config, cdc)
return initTestnet(config, cdc)
},
}
cmd.Flags().Int(nValidators, 4,
"Number of validators to initialize the testnet with")
cmd.Flags().StringP(outputDir, "o", "./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(nodeDaemonHome, "gaiad",
"Home directory of the node's daemon configuration")
cmd.Flags().String(nodeCliHome, "gaiacli",
"Home directory of the node's cli configuration")
cmd.Flags().String(startingIPAddress, "192.168.0.1",
cmd.Flags().Int(flagNumValidators, 4,
"Number of validators to initialize the testnet with",
)
cmd.Flags().StringP(flagOutputDir, "o", "./mytestnet",
"Directory to store initialization data for the testnet",
)
cmd.Flags().String(flagNodeDirPrefix, "node",
"Prefix the directory name for each node with (node results in node0, node1, ...)",
)
cmd.Flags().String(flagNodeDaemonHome, "gaiad",
"Home directory of the node's daemon configuration",
)
cmd.Flags().String(flagNodeCliHome, "gaiacli",
"Home directory of the node's cli configuration",
)
cmd.Flags().String(flagStartingIPAddress, "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, cdc *codec.Codec) error {
outDir := viper.GetString(outputDir)
numValidators := viper.GetInt(nValidators)
func initTestnet(config *cfg.Config, cdc *codec.Codec) error {
outDir := viper.GetString(flagOutputDir)
numValidators := viper.GetInt(flagNumValidators)
// Generate genesis.json and config.toml
chainID := "chain-" + cmn.RandStr(6)
monikers := make([]string, numValidators)
nodeIDs := make([]string, numValidators)
valPubKeys := make([]crypto.PubKey, numValidators)
// Generate private key, node ID, initial transaction
var accs []app.GenesisAccount
var genFiles []string
var (
accs []app.GenesisAccount
genFiles []string
)
// generate private keys, node IDs, and initial transactions
for i := 0; i < numValidators; i++ {
nodeDirName := fmt.Sprintf("%s%d", viper.GetString(nodeDirPrefix), i)
nodeDaemonHomeName := viper.GetString(nodeDaemonHome)
nodeCliHomeName := viper.GetString(nodeCliHome)
nodeDirName := fmt.Sprintf("%s%d", viper.GetString(flagNodeDirPrefix), i)
nodeDaemonHomeName := viper.GetString(flagNodeDaemonHome)
nodeCliHomeName := viper.GetString(flagNodeCliHome)
nodeDir := filepath.Join(outDir, nodeDirName, nodeDaemonHomeName)
clientDir := filepath.Join(outDir, nodeDirName, nodeCliHomeName)
gentxsDir := filepath.Join(outDir, "gentxs")
config.SetRoot(nodeDir)
err := os.MkdirAll(filepath.Join(nodeDir, "config"), nodeDirPerm)
@@ -108,24 +118,27 @@ func testnetWithConfig(config *cfg.Config, cdc *codec.Codec) error {
monikers = append(monikers, nodeDirName)
config.Moniker = nodeDirName
ip, err := getIP(i)
ip, err := getIP(i, viper.GetString(flagStartingIPAddress))
if err != nil {
_ = os.RemoveAll(outDir)
return err
}
nodeIDs[i], valPubKeys[i], err = InitializeNodeValidatorFiles(config)
if err != nil {
_ = os.RemoveAll(outDir)
return err
}
memo := fmt.Sprintf("%s@%s:26656", nodeIDs[i], ip)
// write genesis
memo := fmt.Sprintf("%s@%s:26656", nodeIDs[i], ip)
genFiles = append(genFiles, config.GenesisFile())
buf := client.BufferStdin()
prompt := fmt.Sprintf(
"Password for account '%s' (default %s):", nodeDirName, app.DefaultKeyPass)
"Password for account '%s' (default %s):", nodeDirName, app.DefaultKeyPass,
)
keyPass, err := client.GetPassword(prompt, buf)
if err != nil && keyPass != "" {
// An error was returned that either failed to read the password from
@@ -133,6 +146,7 @@ func testnetWithConfig(config *cfg.Config, cdc *codec.Codec) error {
// length requirements.
return err
}
if keyPass == "" {
keyPass = app.DefaultKeyPass
}
@@ -142,11 +156,14 @@ func testnetWithConfig(config *cfg.Config, cdc *codec.Codec) error {
_ = os.RemoveAll(outDir)
return err
}
info := map[string]string{"secret": secret}
cliPrint, err := json.Marshal(info)
if err != nil {
return err
}
// save private key seed words
err = writeFile(fmt.Sprintf("%v.json", "key_seed"), clientDir, cliPrint)
if err != nil {
@@ -170,6 +187,7 @@ func testnetWithConfig(config *cfg.Config, cdc *codec.Codec) error {
)
tx := auth.NewStdTx([]sdk.Msg{msg}, auth.StdFee{}, []auth.StdSignature{}, memo)
txBldr := authtx.NewTxBuilderFromCLI().WithChainID(chainID).WithMemo(memo)
signedTx, err := txBldr.SignStdTx(nodeDirName, app.DefaultKeyPass, tx, false)
if err != nil {
_ = os.RemoveAll(outDir)
@@ -182,7 +200,7 @@ func testnetWithConfig(config *cfg.Config, cdc *codec.Codec) error {
return err
}
// Gather gentxs folder
// gather gentxs folder
err = writeFile(fmt.Sprintf("%v.json", nodeDirName), gentxsDir, txBytes)
if err != nil {
_ = os.RemoveAll(outDir)
@@ -190,37 +208,70 @@ func testnetWithConfig(config *cfg.Config, cdc *codec.Codec) error {
}
}
// Generate empty genesis.json
if err := initGenFiles(cdc, chainID, accs, genFiles, numValidators); err != nil {
return err
}
err := collectGenFiles(
cdc, config, chainID, monikers, nodeIDs, valPubKeys, numValidators,
outDir, viper.GetString(flagNodeDirPrefix), viper.GetString(flagNodeDaemonHome),
)
if err != nil {
return err
}
fmt.Printf("Successfully initialized %d node directories\n", numValidators)
return nil
}
func initGenFiles(
cdc *codec.Codec, chainID string, accs []app.GenesisAccount,
genFiles []string, numValidators int,
) error {
appGenState := app.NewDefaultGenesisState()
appGenState.Accounts = accs
appGenStateJSON, err := codec.MarshalJSONIndent(cdc, appGenState)
if err != nil {
return err
}
genDoc := types.GenesisDoc{
ChainID: chainID,
AppState: appGenStateJSON,
Validators: nil,
}
// Save all genesis.json files
// generate empty genesis files for each validator and save
for i := 0; i < numValidators; i++ {
if err := genDoc.SaveAs(genFiles[i]); err != nil {
return err
}
}
return nil
}
func collectGenFiles(
cdc *codec.Codec, config *cfg.Config, chainID string,
monikers, nodeIDs []string, valPubKeys []crypto.PubKey,
numValidators int, outDir, nodeDirPrefix, nodeDaemonHomeName string,
) error {
var appState json.RawMessage
genTime := tmtime.Now()
for i := 0; i < numValidators; i++ {
nodeDirName := fmt.Sprintf("%s%d", viper.GetString(nodeDirPrefix), i)
nodeDaemonHomeName := viper.GetString(nodeDaemonHome)
nodeDirName := fmt.Sprintf("%s%d", nodeDirPrefix, i)
nodeDir := filepath.Join(outDir, nodeDirName, nodeDaemonHomeName)
gentxsDir := filepath.Join(outDir, "gentxs")
moniker := monikers[i]
config.Moniker = nodeDirName
config.SetRoot(nodeDir)
nodeID, valPubKey := nodeIDs[i], valPubKeys[i]
// Run `init` and generate genesis.json and config.toml
initCfg := initConfig{
ChainID: chainID,
GenTxsDir: gentxsDir,
@@ -228,46 +279,69 @@ func testnetWithConfig(config *cfg.Config, cdc *codec.Codec) error {
NodeID: nodeID,
ValPubKey: valPubKey,
}
genDoc, err := loadGenesisDoc(cdc, config.GenesisFile())
if err != nil {
return err
}
if _, err := genAppStateFromConfig(cdc, config, initCfg, genDoc); err != nil {
nodeAppState, err := genAppStateFromConfig(cdc, config, initCfg, genDoc)
if err != nil {
return err
}
if appState == nil {
// set the canonical application state (they should not differ)
appState = nodeAppState
}
genFile := config.GenesisFile()
// overwrite each validator's genesis file to have a canonical genesis time
err = ExportGenesisFileWithTime(genFile, chainID, nil, appState, genTime)
if err != nil {
return err
}
}
fmt.Printf("Successfully initialized %v node directories\n", viper.GetInt(nValidators))
return nil
}
func getIP(i int) (ip string, err error) {
ip = viper.GetString(startingIPAddress)
if len(ip) == 0 {
func getIP(i int, startingIPAddr string) (string, error) {
var (
ip string
err error
)
if len(startingIPAddr) == 0 {
ip, err = server.ExternalIP()
if err != nil {
return "", err
}
} else {
ip, err = calculateIP(ip, i)
ip, err = calculateIP(startingIPAddr, i)
if err != nil {
return "", err
}
}
return ip, nil
}
func writeFile(name string, dir string, contents []byte) error {
writePath := filepath.Join(dir)
file := filepath.Join(writePath, name)
err := cmn.EnsureDir(writePath, 0700)
if err != nil {
return err
}
err = cmn.WriteFile(file, contents, 0600)
if err != nil {
return err
}
return nil
}
@@ -280,5 +354,6 @@ func calculateIP(ip string, i int) (string, error) {
for j := 0; j < i; j++ {
ipv4[3]++
}
return ipv4.String(), nil
}
+112
View File
@@ -0,0 +1,112 @@
package init
import (
"encoding/json"
"fmt"
"io/ioutil"
"time"
"github.com/cosmos/cosmos-sdk/cmd/gaia/app"
"github.com/cosmos/cosmos-sdk/codec"
amino "github.com/tendermint/go-amino"
cfg "github.com/tendermint/tendermint/config"
"github.com/tendermint/tendermint/crypto"
"github.com/tendermint/tendermint/libs/common"
"github.com/tendermint/tendermint/p2p"
"github.com/tendermint/tendermint/privval"
"github.com/tendermint/tendermint/types"
)
// ExportGenesisFile creates and writes the genesis configuration to disk. An
// error is returned if building or writing the configuration to file fails.
func ExportGenesisFile(
genFile, chainID string, validators []types.GenesisValidator, appState json.RawMessage,
) error {
genDoc := types.GenesisDoc{
ChainID: chainID,
Validators: validators,
AppState: appState,
}
if err := genDoc.ValidateAndComplete(); err != nil {
return err
}
return genDoc.SaveAs(genFile)
}
// ExportGenesisFileWithTime creates and writes the genesis configuration to disk.
// An error is returned if building or writing the configuration to file fails.
func ExportGenesisFileWithTime(
genFile, chainID string, validators []types.GenesisValidator,
appState json.RawMessage, genTime time.Time,
) error {
genDoc := types.GenesisDoc{
GenesisTime: genTime,
ChainID: chainID,
Validators: validators,
AppState: appState,
}
if err := genDoc.ValidateAndComplete(); err != nil {
return err
}
return genDoc.SaveAs(genFile)
}
// read of create the private key file for this config
func ReadOrCreatePrivValidator(privValFile string) crypto.PubKey {
var privValidator *privval.FilePV
if common.FileExists(privValFile) {
privValidator = privval.LoadFilePV(privValFile)
} else {
privValidator = privval.GenFilePV(privValFile)
privValidator.Save()
}
return privValidator.GetPubKey()
}
// InitializeNodeValidatorFiles creates private validator and p2p configuration files.
func InitializeNodeValidatorFiles(
config *cfg.Config) (nodeID string, valPubKey crypto.PubKey, err error,
) {
nodeKey, err := p2p.LoadOrGenNodeKey(config.NodeKeyFile())
if err != nil {
return nodeID, valPubKey, err
}
nodeID = string(nodeKey.ID())
valPubKey = ReadOrCreatePrivValidator(config.PrivValidatorFile())
return nodeID, valPubKey, nil
}
func loadGenesisDoc(cdc *amino.Codec, genFile string) (genDoc types.GenesisDoc, err error) {
genContents, err := ioutil.ReadFile(genFile)
if err != nil {
return genDoc, err
}
if err := cdc.UnmarshalJSON(genContents, &genDoc); err != nil {
return genDoc, err
}
return genDoc, err
}
func initializeEmptyGenesis(
cdc *codec.Codec, genFile, chainID string, overwrite bool,
) (appState json.RawMessage, err error) {
if !overwrite && common.FileExists(genFile) {
return nil, fmt.Errorf("genesis.json file already exists: %v", genFile)
}
return codec.MarshalJSONIndent(cdc, app.NewDefaultGenesisState())
}