diff --git a/Gopkg.lock b/Gopkg.lock index 3817fd238e9..c679450b9c0 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -452,7 +452,7 @@ version = "v0.11.0" [[projects]] - digest = "1:a69eebd15b05045ffdb10a984e001fadc5666f74383de3d2a9ee5862ee99cfdc" + digest = "1:f9c7a1f3ee087476f4883c33cc7c1bdbe56b9670b2fb27855ea2f386393272f5" name = "github.com/tendermint/tendermint" packages = [ "abci/client", @@ -518,8 +518,8 @@ "version", ] pruneopts = "UT" - revision = "0c9c3292c918617624f6f3fbcd95eceade18bcd5" - version = "v0.25.0" + revision = "90eda9bfb6e6daeed1c8015df41cb36772d91778" + version = "v0.25.1-rc0" [[projects]] digest = "1:7886f86064faff6f8d08a3eb0e8c773648ff5a2e27730831e2bfbf07467f6666" diff --git a/Gopkg.toml b/Gopkg.toml index 05140857dd4..0902bc41260 100644 --- a/Gopkg.toml +++ b/Gopkg.toml @@ -57,11 +57,11 @@ [[override]] name = "github.com/tendermint/tendermint" - version = "=0.25.0" + version = "=0.25.1-rc0" ## deps without releases: -[[constraint]] +[[override]] name = "golang.org/x/crypto" source = "https://github.com/tendermint/crypto" revision = "3764759f34a542a3aef74d6b02e35be7ab893bba" diff --git a/PENDING.md b/PENDING.md index 00d0ca3cc74..2dc3013de41 100644 --- a/PENDING.md +++ b/PENDING.md @@ -20,6 +20,7 @@ BREAKING CHANGES * [cli] [\#2190](https://github.com/cosmos/cosmos-sdk/issues/2190) `gaiacli init --gen-txs` is now `gaiacli init --with-txs` to reduce confusion * [cli] \#2073 --from can now be either an address or a key name * [cli] [\#1184](https://github.com/cosmos/cosmos-sdk/issues/1184) Subcommands reorganisation, see [\#2390](https://github.com/cosmos/cosmos-sdk/pull/2390) for a comprehensive list of changes. + * [cli] [\#2524](https://github.com/cosmos/cosmos-sdk/issues/2524) Add support offline mode to `gaiacli tx sign`. Lookups are not performed if the flag `--offline` is on. * Gaia * Make the transient store key use a distinct store key. [#2013](https://github.com/cosmos/cosmos-sdk/pull/2013) @@ -45,6 +46,12 @@ BREAKING CHANGES * [x/slashing] \#2430 Simulate more slashes, check if validator is jailed before jailing * [x/stake] \#2393 Removed `CompleteUnbonding` and `CompleteRedelegation` Msg types, and instead added unbonding/redelegation queues to endblocker * [x/stake] \#1673 Validators are no longer deleted until they can no longer possibly be slashed + * [\#1890](https://github.com/cosmos/cosmos-sdk/issues/1890) Start chain with initial state + sequence of transactions + * [cli] Rename `gaiad init gentx` to `gaiad gentx`. + * [cli] Add `--skip-genesis` flag to `gaiad init` to prevent `genesis.json` generation. + * Drop `GenesisTx` in favor of a signed `StdTx` with only one `MsgCreateValidator` message. + * [cli] Port `gaiad init` and `gaiad testnet` to work with `StdTx` genesis transactions. + * [cli] Add `--moniker` flag to `gaiad init` to override moniker when generating `genesis.json` - i.e. it takes effect when running with the `--with-txs` flag, it is ignored otherwise. * SDK * [core] \#2219 Update to Tendermint 0.24.0 @@ -115,6 +122,7 @@ FEATURES * [cli] \#2220 Add `gaiacli config` feature to interactively create CLI config files to reduce the number of required flags * [stake][cli] [\#1672](https://github.com/cosmos/cosmos-sdk/issues/1672) Introduced new commission flags for validator commands `create-validator` and `edit-validator`. + * [stake][cli] [\#1890](https://github.com/cosmos/cosmos-sdk/issues/1890) Add `--genesis-format` flag to `gaiacli tx create-validator` to produce transactions in genesis-friendly format. * Gaia * [cli] #2170 added ability to show the node's address via `gaiad tendermint show-address` diff --git a/client/lcd/lcd_test.go b/client/lcd/lcd_test.go index 7aea16d868c..c1c01877855 100644 --- a/client/lcd/lcd_test.go +++ b/client/lcd/lcd_test.go @@ -496,7 +496,7 @@ func TestValidatorsQuery(t *testing.T) { require.Equal(t, 1, len(operAddrs)) validators := getValidators(t, port) - require.Equal(t, len(validators), 1) + require.Equal(t, 1, len(validators), fmt.Sprintf("%+v", validators)) // make sure all the validators were found (order unknown because sorted by operator addr) foundVal := false diff --git a/client/lcd/test_helpers.go b/client/lcd/test_helpers.go index 3585023cdb3..ee7fdbdef5f 100644 --- a/client/lcd/test_helpers.go +++ b/client/lcd/test_helpers.go @@ -4,6 +4,8 @@ import ( "bytes" "encoding/json" "fmt" + "github.com/cosmos/cosmos-sdk/x/stake" + "github.com/tendermint/tendermint/crypto/secp256k1" "io/ioutil" "net" "net/http" @@ -14,7 +16,7 @@ import ( "testing" "github.com/cosmos/cosmos-sdk/client" - keys "github.com/cosmos/cosmos-sdk/client/keys" + "github.com/cosmos/cosmos-sdk/client/keys" gapp "github.com/cosmos/cosmos-sdk/cmd/gaia/app" "github.com/cosmos/cosmos-sdk/codec" crkeys "github.com/cosmos/cosmos-sdk/crypto/keys" @@ -26,6 +28,7 @@ import ( "github.com/spf13/viper" "github.com/stretchr/testify/require" + txbuilder "github.com/cosmos/cosmos-sdk/x/auth/client/txbuilder" abci "github.com/tendermint/tendermint/abci/types" tmcfg "github.com/tendermint/tendermint/config" "github.com/tendermint/tendermint/crypto" @@ -205,42 +208,43 @@ func InitializeTestLCD( genesisFile := config.GenesisFile() genDoc, err := tmtypes.GenesisDocFromFile(genesisFile) - require.NoError(t, err) - - // append initial (proposing) validator - genDoc.Validators[0] = tmtypes.GenesisValidator{ - PubKey: privVal.GetPubKey(), - Power: 100, // create enough power to enable 2/3 voting power - Name: "validator-1", - } + require.Nil(t, err) + genDoc.Validators = nil + genDoc.SaveAs(genesisFile) + genTxs := []json.RawMessage{} // append any additional (non-proposing) validators - for i := 1; i < nValidators; i++ { - genDoc.Validators = append(genDoc.Validators, - tmtypes.GenesisValidator{ - PubKey: ed25519.GenPrivKey().PubKey(), - Power: 1, - Name: fmt.Sprintf("validator-%d", i+1), - }, + for i := 0; i < nValidators; i++ { + operPrivKey := secp256k1.GenPrivKey() + operAddr := operPrivKey.PubKey().Address() + pubKey := privVal.PubKey + delegation := 100 + if i > 0 { + pubKey = ed25519.GenPrivKey().PubKey() + delegation = 1 + } + msg := stake.NewMsgCreateValidator( + sdk.ValAddress(operAddr), + pubKey, + sdk.NewCoin("steak", sdk.NewInt(int64(delegation))), + stake.Description{Moniker: fmt.Sprintf("validator-%d", i+1)}, + stake.NewCommissionMsg(sdk.ZeroDec(), sdk.ZeroDec(), sdk.ZeroDec()), ) - } - - var appGenTxs []json.RawMessage - - for _, gdValidator := range genDoc.Validators { - operAddr := ed25519.GenPrivKey().PubKey().Address() - pk := gdValidator.PubKey - - valConsPubKeys = append(valConsPubKeys, pk) + stdSignMsg := txbuilder.StdSignMsg{ + ChainID: genDoc.ChainID, + Msgs: []sdk.Msg{msg}, + } + sig, err := operPrivKey.Sign(stdSignMsg.Bytes()) + require.Nil(t, err) + tx := auth.NewStdTx([]sdk.Msg{msg}, auth.StdFee{}, []auth.StdSignature{{Signature: sig, PubKey: operPrivKey.PubKey()}}, "") + txBytes, err := cdc.MarshalJSON(tx) + require.Nil(t, err) + genTxs = append(genTxs, txBytes) + valConsPubKeys = append(valConsPubKeys, pubKey) valOperAddrs = append(valOperAddrs, sdk.ValAddress(operAddr)) - - appGenTx, _, _, err := gapp.GaiaAppGenTxNF(cdc, pk, sdk.AccAddress(operAddr), gdValidator.Name) - require.NoError(t, err) - - appGenTxs = append(appGenTxs, appGenTx) } - genesisState, err := gapp.NewTestGaiaAppGenState(cdc, appGenTxs[:], genDoc.Validators, valOperAddrs) + genesisState, err := gapp.GaiaAppGenState(cdc, genTxs) require.NoError(t, err) // add some tokens to init accounts diff --git a/client/utils/utils.go b/client/utils/utils.go index 364b9e692fb..f5cf04168c4 100644 --- a/client/utils/utils.go +++ b/client/utils/utils.go @@ -107,7 +107,8 @@ func PrintUnsignedStdTx(txBldr authtxb.TxBuilder, cliCtx context.CLIContext, msg // SignStdTx appends a signature to a StdTx and returns a copy of a it. If appendSig // is false, it replaces the signatures already attached with the new signature. -func SignStdTx(txBldr authtxb.TxBuilder, cliCtx context.CLIContext, name string, stdTx auth.StdTx, appendSig bool) (auth.StdTx, error) { +// Don't perform online validation or lookups if offline is true. +func SignStdTx(txBldr authtxb.TxBuilder, cliCtx context.CLIContext, name string, stdTx auth.StdTx, appendSig bool, offline bool) (auth.StdTx, error) { var signedStdTx auth.StdTx keybase, err := keys.GetKeyBase() @@ -122,10 +123,10 @@ func SignStdTx(txBldr authtxb.TxBuilder, cliCtx context.CLIContext, name string, // Check whether the address is a signer if !isTxSigner(sdk.AccAddress(addr), stdTx.GetSigners()) { - fmt.Fprintf(os.Stderr, "WARNING: The generated transaction's intended signer does not match the given signer: '%v'", name) + fmt.Fprintf(os.Stderr, "WARNING: The generated transaction's intended signer does not match the given signer: '%v'\n", name) } - if txBldr.AccountNumber == 0 { + if !offline && txBldr.AccountNumber == 0 { accNum, err := cliCtx.GetAccountNumber(addr) if err != nil { return signedStdTx, err @@ -133,7 +134,7 @@ func SignStdTx(txBldr authtxb.TxBuilder, cliCtx context.CLIContext, name string, txBldr = txBldr.WithAccountNumber(accNum) } - if txBldr.Sequence == 0 { + if !offline && txBldr.Sequence == 0 { accSeq, err := cliCtx.GetAccountSequence(addr) if err != nil { return signedStdTx, err diff --git a/cmd/gaia/app/app.go b/cmd/gaia/app/app.go index 2c72e3aaf93..6afeae7938f 100644 --- a/cmd/gaia/app/app.go +++ b/cmd/gaia/app/app.go @@ -2,15 +2,7 @@ package app import ( "encoding/json" - "io" - "os" - - abci "github.com/tendermint/tendermint/abci/types" - cmn "github.com/tendermint/tendermint/libs/common" - dbm "github.com/tendermint/tendermint/libs/db" - "github.com/tendermint/tendermint/libs/log" - tmtypes "github.com/tendermint/tendermint/types" - + "fmt" bam "github.com/cosmos/cosmos-sdk/baseapp" "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" @@ -21,10 +13,20 @@ import ( "github.com/cosmos/cosmos-sdk/x/params" "github.com/cosmos/cosmos-sdk/x/slashing" "github.com/cosmos/cosmos-sdk/x/stake" + abci "github.com/tendermint/tendermint/abci/types" + cmn "github.com/tendermint/tendermint/libs/common" + dbm "github.com/tendermint/tendermint/libs/db" + "github.com/tendermint/tendermint/libs/log" + tmtypes "github.com/tendermint/tendermint/types" + "io" + "os" + "sort" ) const ( appName = "GaiaApp" + // DefaultKeyPass contains the default key password for genesis transactions + DefaultKeyPass = "12345678" ) // default home directories for expected binaries @@ -238,6 +240,38 @@ func (app *GaiaApp) initChainer(ctx sdk.Context, req abci.RequestInitChain) abci panic(err) // TODO find a way to do this w/o panics } + if len(genesisState.GenTxs) > 0 { + for _, genTx := range genesisState.GenTxs { + var tx auth.StdTx + err = app.cdc.UnmarshalJSON(genTx, &tx) + if err != nil { + panic(err) + } + bz := app.cdc.MustMarshalBinary(tx) + res := app.BaseApp.DeliverTx(bz) + if !res.IsOK() { + panic(res.Log) + } + } + + validators = app.stakeKeeper.ApplyAndReturnValidatorSetUpdates(ctx) + } + app.slashingKeeper.AddValidators(ctx, validators) + + // sanity check + if len(req.Validators) > 0 { + if len(req.Validators) != len(validators) { + panic(fmt.Errorf("len(RequestInitChain.Validators) != len(validators) (%d != %d) ", len(req.Validators), len(validators))) + } + sort.Sort(abci.ValidatorUpdates(req.Validators)) + sort.Sort(abci.ValidatorUpdates(validators)) + for i, val := range validators { + if !val.Equal(req.Validators[i]) { + panic(fmt.Errorf("validators[%d] != req.Validators[%d] ", i, i)) + } + } + } + return abci.ResponseInitChain{ Validators: validators, } diff --git a/cmd/gaia/app/genesis.go b/cmd/gaia/app/genesis.go index 690d92502cc..9d52c9a5762 100644 --- a/cmd/gaia/app/genesis.go +++ b/cmd/gaia/app/genesis.go @@ -4,31 +4,27 @@ import ( "encoding/json" "errors" "fmt" + "io/ioutil" + "os" + "path/filepath" + "sort" + "strings" - "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/codec" "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/x/auth" distr "github.com/cosmos/cosmos-sdk/x/distribution" "github.com/cosmos/cosmos-sdk/x/gov" "github.com/cosmos/cosmos-sdk/x/slashing" "github.com/cosmos/cosmos-sdk/x/stake" - - "github.com/spf13/pflag" - - "github.com/tendermint/tendermint/crypto" tmtypes "github.com/tendermint/tendermint/types" ) -// DefaultKeyPass contains the default key password for genesis transactions -const DefaultKeyPass = "12345678" - var ( // bonded tokens given to genesis validators/accounts freeFermionVal = int64(100) - freeFermionsAcc = sdk.NewInt(50) + freeFermionsAcc = sdk.NewInt(150) ) // State to Unmarshal @@ -38,6 +34,7 @@ type GenesisState struct { DistrData distr.GenesisState `json:"distr"` GovData gov.GenesisState `json:"gov"` SlashingData slashing.GenesisState `json:"slashing"` + GenTxs []json.RawMessage `json:"gentxs"` } // GenesisAccount doesn't need pubkey or sequence @@ -70,97 +67,12 @@ func (ga *GenesisAccount) ToAccount() (acc *auth.BaseAccount) { // get app init parameters for server init command func GaiaAppInit() server.AppInit { - fsAppGenState := pflag.NewFlagSet("", pflag.ContinueOnError) - - fsAppGenTx := pflag.NewFlagSet("", pflag.ContinueOnError) - fsAppGenTx.String(server.FlagName, "", "validator moniker, required") - fsAppGenTx.String(server.FlagClientHome, DefaultCLIHome, - "home directory for the client, used for key generation") - fsAppGenTx.Bool(server.FlagOWK, false, "overwrite the accounts created") return server.AppInit{ - FlagsAppGenState: fsAppGenState, - FlagsAppGenTx: fsAppGenTx, - AppGenTx: GaiaAppGenTx, - AppGenState: GaiaAppGenStateJSON, + AppGenState: GaiaAppGenStateJSON, } } -// simple genesis tx -type GaiaGenTx struct { - Name string `json:"name"` - Address sdk.AccAddress `json:"address"` - PubKey string `json:"pub_key"` -} - -// GaiaAppGenTx generates a Gaia genesis transaction. -func GaiaAppGenTx( - cdc *codec.Codec, pk crypto.PubKey, genTxConfig config.GenTx, -) (appGenTx, cliPrint json.RawMessage, validator tmtypes.GenesisValidator, err error) { - if genTxConfig.Name == "" { - return nil, nil, tmtypes.GenesisValidator{}, errors.New("Must specify --name (validator moniker)") - } - - buf := client.BufferStdin() - prompt := fmt.Sprintf("Password for account '%s' (default %s):", genTxConfig.Name, DefaultKeyPass) - - keyPass, err := client.GetPassword(prompt, buf) - if err != nil && keyPass != "" { - // An error was returned that either failed to read the password from - // STDIN or the given password is not empty but failed to meet minimum - // length requirements. - return appGenTx, cliPrint, validator, err - } - - if keyPass == "" { - keyPass = DefaultKeyPass - } - - addr, secret, err := server.GenerateSaveCoinKey( - genTxConfig.CliRoot, - genTxConfig.Name, - keyPass, - genTxConfig.Overwrite, - ) - if err != nil { - return appGenTx, cliPrint, validator, err - } - - mm := map[string]string{"secret": secret} - bz, err := cdc.MarshalJSON(mm) - if err != nil { - return appGenTx, cliPrint, validator, err - } - - cliPrint = json.RawMessage(bz) - appGenTx, _, validator, err = GaiaAppGenTxNF(cdc, pk, addr, genTxConfig.Name) - - return appGenTx, cliPrint, validator, err -} - -// Generate a gaia genesis transaction without flags -func GaiaAppGenTxNF(cdc *codec.Codec, pk crypto.PubKey, addr sdk.AccAddress, name string) ( - appGenTx, cliPrint json.RawMessage, validator tmtypes.GenesisValidator, err error) { - - var bz []byte - gaiaGenTx := GaiaGenTx{ - Name: name, - Address: addr, - PubKey: sdk.MustBech32ifyConsPub(pk), - } - bz, err = codec.MarshalJSONIndent(cdc, gaiaGenTx) - if err != nil { - return - } - appGenTx = json.RawMessage(bz) - - validator = tmtypes.GenesisValidator{ - PubKey: pk, - Power: freeFermionVal, - } - return -} - // Create the core parameters for genesis initialization for gaia // note that the pubkey input is this machines pubkey func GaiaAppGenState(cdc *codec.Codec, appGenTxs []json.RawMessage) (genesisState GenesisState, err error) { @@ -171,27 +83,27 @@ func GaiaAppGenState(cdc *codec.Codec, appGenTxs []json.RawMessage) (genesisStat // start with the default staking genesis state stakeData := stake.DefaultGenesisState() - slashingData := slashing.DefaultGenesisState() // get genesis flag account information genaccs := make([]GenesisAccount, len(appGenTxs)) - for i, appGenTx := range appGenTxs { - var genTx GaiaGenTx - err = cdc.UnmarshalJSON(appGenTx, &genTx) + for i, genTx := range appGenTxs { + var tx auth.StdTx + err = cdc.UnmarshalJSON(genTx, &tx) if err != nil { return } + msgs := tx.GetMsgs() + if len(msgs) != 1 { + err = errors.New("must provide genesis StdTx with exactly 1 CreateValidator message") + return + } + msg := msgs[0].(stake.MsgCreateValidator) // create the genesis account, give'm few steaks and a buncha token with there name - genaccs[i] = genesisAccountFromGenTx(genTx) + genaccs[i] = genesisAccountFromMsgCreateValidator(msg, freeFermionsAcc) stakeData.Pool.LooseTokens = stakeData.Pool.LooseTokens.Add(sdk.NewDecFromInt(freeFermionsAcc)) // increase the supply - - // add the validator - if len(genTx.Name) > 0 { - stakeData = addValidatorToStakeData(genTx, stakeData) - } } // create the final app state @@ -201,41 +113,17 @@ func GaiaAppGenState(cdc *codec.Codec, appGenTxs []json.RawMessage) (genesisStat DistrData: distr.DefaultGenesisState(), GovData: gov.DefaultGenesisState(), SlashingData: slashingData, + GenTxs: appGenTxs, } return } -func addValidatorToStakeData(genTx GaiaGenTx, stakeData stake.GenesisState) stake.GenesisState { - desc := stake.NewDescription(genTx.Name, "", "", "") - validator := stake.NewValidator( - sdk.ValAddress(genTx.Address), sdk.MustGetConsPubKeyBech32(genTx.PubKey), desc, - ) - - stakeData.Pool.LooseTokens = stakeData.Pool.LooseTokens.Add(sdk.NewDec(freeFermionVal)) // increase the supply - - // add some new shares to the validator - var issuedDelShares sdk.Dec - validator, stakeData.Pool, issuedDelShares = validator.AddTokensFromDel(stakeData.Pool, sdk.NewInt(freeFermionVal)) - stakeData.Validators = append(stakeData.Validators, validator) - - // create the self-delegation from the issuedDelShares - delegation := stake.Delegation{ - DelegatorAddr: sdk.AccAddress(validator.OperatorAddr), - ValidatorAddr: validator.OperatorAddr, - Shares: issuedDelShares, - Height: 0, - } - - stakeData.Bonds = append(stakeData.Bonds, delegation) - return stakeData -} - -func genesisAccountFromGenTx(genTx GaiaGenTx) GenesisAccount { - accAuth := auth.NewBaseAccountWithAddress(genTx.Address) - accAuth.Coins = sdk.Coins{ - {genTx.Name + "Token", sdk.NewInt(1000)}, - {"steak", freeFermionsAcc}, +func genesisAccountFromMsgCreateValidator(msg stake.MsgCreateValidator, amount sdk.Int) GenesisAccount { + accAuth := auth.NewBaseAccountWithAddress(sdk.AccAddress(msg.ValidatorAddr)) + accAuth.Coins = []sdk.Coin{ + {msg.Description.Moniker + "Token", sdk.NewInt(1000)}, + {"steak", amount}, } return NewGenesisAccount(&accAuth) } @@ -249,11 +137,11 @@ func GaiaValidateGenesisState(genesisState GenesisState) (err error) { if err != nil { return } - err = stake.ValidateGenesis(genesisState.StakeData) - if err != nil { - return + // skip stakeData validation as genesis is created from txs + if len(genesisState.GenTxs) > 0 { + return nil } - return + return stake.ValidateGenesis(genesisState.StakeData) } // Ensures that there are no duplicate accounts in the genesis state, @@ -272,7 +160,6 @@ func validateGenesisStateAccounts(accs []GenesisAccount) (err error) { // GaiaAppGenState but with JSON func GaiaAppGenStateJSON(cdc *codec.Codec, appGenTxs []json.RawMessage) (appState json.RawMessage, err error) { - // create the final app state genesisState, err := GaiaAppGenState(cdc, appGenTxs) if err != nil { @@ -281,3 +168,75 @@ func GaiaAppGenStateJSON(cdc *codec.Codec, appGenTxs []json.RawMessage) (appStat appState, err = codec.MarshalJSONIndent(cdc, genesisState) return } + +// CollectStdTxs processes and validates application's genesis StdTxs and returns the list of validators, +// appGenTxs, and persistent peers required to generate genesis.json. +func CollectStdTxs(moniker string, genTxsDir string, cdc *codec.Codec) ( + validators []tmtypes.GenesisValidator, appGenTxs []auth.StdTx, persistentPeers string, err error) { + var fos []os.FileInfo + fos, err = ioutil.ReadDir(genTxsDir) + if err != nil { + return + } + + var addresses []string + for _, fo := range fos { + filename := filepath.Join(genTxsDir, fo.Name()) + if !fo.IsDir() && (filepath.Ext(filename) != ".json") { + continue + } + + // get the genStdTx + var jsonRawTx []byte + jsonRawTx, err = ioutil.ReadFile(filename) + if err != nil { + return + } + var genStdTx auth.StdTx + err = cdc.UnmarshalJSON(jsonRawTx, &genStdTx) + if err != nil { + return + } + appGenTxs = append(appGenTxs, genStdTx) + + nodeAddr := genStdTx.GetMemo() + if len(nodeAddr) == 0 { + err = fmt.Errorf("couldn't find node's address in %s", fo.Name()) + return + } + + msgs := genStdTx.GetMsgs() + if len(msgs) != 1 { + err = errors.New("each genesis transaction must provide a single genesis message") + return + } + + // TODO: this could be decoupled from stake.MsgCreateValidator + // TODO: and we likely want to do it for real world Gaia + msg := msgs[0].(stake.MsgCreateValidator) + validators = append(validators, tmtypes.GenesisValidator{ + PubKey: msg.PubKey, + Power: freeFermionVal, + Name: msg.Description.Moniker, + }) + + // exclude itself from persistent peers + if msg.Description.Moniker != moniker { + addresses = append(addresses, nodeAddr) + } + } + + sort.Strings(addresses) + persistentPeers = strings.Join(addresses, ",") + + return +} + +func NewDefaultGenesisAccount(addr sdk.AccAddress) GenesisAccount { + accAuth := auth.NewBaseAccountWithAddress(addr) + accAuth.Coins = []sdk.Coin{ + {"fooToken", sdk.NewInt(1000)}, + {"steak", freeFermionsAcc}, + } + return NewGenesisAccount(&accAuth) +} diff --git a/cmd/gaia/app/genesis_test.go b/cmd/gaia/app/genesis_test.go index 9cbd1f49fc7..1acc9f39314 100644 --- a/cmd/gaia/app/genesis_test.go +++ b/cmd/gaia/app/genesis_test.go @@ -25,25 +25,24 @@ var ( emptyPubkey crypto.PubKey ) -func makeGenesisState(genTxs []GaiaGenTx) GenesisState { +func makeGenesisState(t *testing.T, genTxs []auth.StdTx) GenesisState { // start with the default staking genesis state stakeData := stake.DefaultGenesisState() + genAccs := make([]GenesisAccount, len(genTxs)) - // get genesis flag account information - genaccs := make([]GenesisAccount, len(genTxs)) for i, genTx := range genTxs { - genaccs[i] = genesisAccountFromGenTx(genTx) - stakeData.Pool.LooseTokens = stakeData.Pool.LooseTokens.Add(sdk.NewDecFromInt(freeFermionsAcc)) // increase the supply + msgs := genTx.GetMsgs() + require.Equal(t, 1, len(msgs)) + msg := msgs[0].(stake.MsgCreateValidator) - // add the validator - if len(genTx.Name) > 0 { - stakeData = addValidatorToStakeData(genTx, stakeData) - } + // get genesis flag account information + genAccs[i] = genesisAccountFromMsgCreateValidator(msg, freeFermionsAcc) + stakeData.Pool.LooseTokens = stakeData.Pool.LooseTokens.Add(sdk.NewDecFromInt(freeFermionsAcc)) // increase the supply } // create the final app state return GenesisState{ - Accounts: genaccs, + Accounts: genAccs, StakeData: stakeData, GovData: gov.DefaultGenesisState(), } @@ -75,17 +74,23 @@ func TestGaiaAppGenState(t *testing.T) { // TODO correct: genesis account created, canididates created, pool token variance } +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("steak", 50), desc, comm) + return auth.NewStdTx([]sdk.Msg{msg}, auth.StdFee{}, nil, "") +} + func TestGaiaGenesisValidation(t *testing.T) { - genTxs := make([]GaiaGenTx, 2) - addr := pk1.Address() + genTxs := make([]auth.StdTx, 2) // Test duplicate accounts fails - genTxs[0] = GaiaGenTx{"", sdk.AccAddress(addr), ""} - genTxs[1] = GaiaGenTx{"", sdk.AccAddress(addr), ""} - genesisState := makeGenesisState(genTxs) + genTxs[0] = makeMsg("test-0", pk1) + genTxs[1] = makeMsg("test-1", pk1) + genesisState := makeGenesisState(t, genTxs) err := GaiaValidateGenesisState(genesisState) require.NotNil(t, err) // Test bonded + jailed validator fails - genesisState = makeGenesisState(genTxs[:1]) + genesisState = makeGenesisState(t, genTxs) val1 := stakeTypes.NewValidator(addr1, pk1, stakeTypes.Description{Moniker: "test #2"}) val1.Jailed = true val1.Status = sdk.Bonded @@ -94,7 +99,7 @@ func TestGaiaGenesisValidation(t *testing.T) { require.NotNil(t, err) // Test duplicate validator fails val1.Jailed = false - genesisState = makeGenesisState(genTxs[:1]) + genesisState = makeGenesisState(t, genTxs) val2 := stakeTypes.NewValidator(addr1, pk1, stakeTypes.Description{Moniker: "test #3"}) genesisState.StakeData.Validators = append(genesisState.StakeData.Validators, val1) genesisState.StakeData.Validators = append(genesisState.StakeData.Validators, val2) diff --git a/cmd/gaia/app/test_utils.go b/cmd/gaia/app/test_utils.go deleted file mode 100644 index 18946b3972a..00000000000 --- a/cmd/gaia/app/test_utils.go +++ /dev/null @@ -1,80 +0,0 @@ -package app - -import ( - "encoding/json" - "errors" - - "github.com/cosmos/cosmos-sdk/codec" - sdk "github.com/cosmos/cosmos-sdk/types" - distr "github.com/cosmos/cosmos-sdk/x/distribution" - "github.com/cosmos/cosmos-sdk/x/gov" - "github.com/cosmos/cosmos-sdk/x/slashing" - "github.com/cosmos/cosmos-sdk/x/stake" - tmtypes "github.com/tendermint/tendermint/types" -) - -// NewTestGaiaAppGenState creates the core parameters for a test genesis -// initialization given a set of genesis txs, TM validators and their respective -// operating addresses. -func NewTestGaiaAppGenState( - cdc *codec.Codec, appGenTxs []json.RawMessage, tmVals []tmtypes.GenesisValidator, valOperAddrs []sdk.ValAddress, -) (GenesisState, error) { - - switch { - case len(appGenTxs) == 0: - return GenesisState{}, errors.New("must provide at least genesis transaction") - case len(tmVals) != len(valOperAddrs): - return GenesisState{}, errors.New("number of TM validators does not match number of operator addresses") - } - - // start with the default staking genesis state - stakeData := stake.DefaultGenesisState() - - // get genesis account information - genAccs := make([]GenesisAccount, len(appGenTxs)) - for i, appGenTx := range appGenTxs { - - var genTx GaiaGenTx - if err := cdc.UnmarshalJSON(appGenTx, &genTx); err != nil { - return GenesisState{}, err - } - - stakeData.Pool.LooseTokens = stakeData.Pool.LooseTokens.Add(sdk.NewDecFromInt(freeFermionsAcc)) - - // create the genesis account for the given genesis tx - genAccs[i] = genesisAccountFromGenTx(genTx) - } - - for i, tmVal := range tmVals { - var issuedDelShares sdk.Dec - - // increase total supply by validator's power - power := sdk.NewInt(tmVal.Power) - stakeData.Pool.LooseTokens = stakeData.Pool.LooseTokens.Add(sdk.NewDecFromInt(power)) - - // add the validator - desc := stake.NewDescription(tmVal.Name, "", "", "") - validator := stake.NewValidator(valOperAddrs[i], tmVal.PubKey, desc) - - validator, stakeData.Pool, issuedDelShares = validator.AddTokensFromDel(stakeData.Pool, power) - stakeData.Validators = append(stakeData.Validators, validator) - - // create the self-delegation from the issuedDelShares - selfDel := stake.Delegation{ - DelegatorAddr: sdk.AccAddress(validator.OperatorAddr), - ValidatorAddr: validator.OperatorAddr, - Shares: issuedDelShares, - Height: 0, - } - - stakeData.Bonds = append(stakeData.Bonds, selfDel) - } - - return GenesisState{ - Accounts: genAccs, - StakeData: stakeData, - DistrData: distr.DefaultGenesisState(), - SlashingData: slashing.DefaultGenesisState(), - GovData: gov.DefaultGenesisState(), - }, nil -} diff --git a/cmd/gaia/cli_test/cli_test.go b/cmd/gaia/cli_test/cli_test.go index 04bc4c84a59..1639e143e5e 100644 --- a/cmd/gaia/cli_test/cli_test.go +++ b/cmd/gaia/cli_test/cli_test.go @@ -311,7 +311,7 @@ func TestGaiaCLISubmitProposal(t *testing.T) { fooAcc := executeGetAccount(t, fmt.Sprintf("gaiacli query account %s %v", fooAddr, flags)) require.Equal(t, int64(50), fooAcc.GetCoins().AmountOf("steak").Int64()) - proposalsQuery := tests.ExecuteT(t, fmt.Sprintf("gaiacli query proposals %v", flags), "") + proposalsQuery, _ := tests.ExecuteT(t, fmt.Sprintf("gaiacli query proposals %v", flags), "") require.Equal(t, "No matching proposals found", proposalsQuery) // submit a test proposal @@ -346,7 +346,7 @@ func TestGaiaCLISubmitProposal(t *testing.T) { require.Equal(t, int64(1), proposal1.GetProposalID()) require.Equal(t, gov.StatusDepositPeriod, proposal1.GetStatus()) - proposalsQuery = tests.ExecuteT(t, fmt.Sprintf("gaiacli query proposals %v", flags), "") + proposalsQuery, _ = tests.ExecuteT(t, fmt.Sprintf("gaiacli query proposals %v", flags), "") require.Equal(t, " 1 - Test", proposalsQuery) depositStr := fmt.Sprintf("gaiacli tx deposit %v", flags) @@ -400,10 +400,10 @@ func TestGaiaCLISubmitProposal(t *testing.T) { require.Equal(t, int64(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), "") + proposalsQuery, _ = tests.ExecuteT(t, fmt.Sprintf("gaiacli query proposals --status=DepositPeriod %v", flags), "") require.Equal(t, "No matching proposals found", proposalsQuery) - proposalsQuery = tests.ExecuteT(t, fmt.Sprintf("gaiacli query proposals --status=VotingPeriod %v", flags), "") + proposalsQuery, _ = tests.ExecuteT(t, fmt.Sprintf("gaiacli query proposals --status=VotingPeriod %v", flags), "") require.Equal(t, " 1 - Test", proposalsQuery) // submit a second test proposal @@ -417,7 +417,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 --latest=1 %v", flags), "") require.Equal(t, " 2 - Apples", proposalsQuery) } @@ -628,10 +628,10 @@ func executeWriteRetStdStreams(t *testing.T, cmdStr string, writes ...string) (b } func executeInit(t *testing.T, cmdStr string) (chainID string) { - out := tests.ExecuteT(t, cmdStr, app.DefaultKeyPass) + _, stderr := tests.ExecuteT(t, cmdStr, app.DefaultKeyPass) var initRes map[string]json.RawMessage - err := json.Unmarshal([]byte(out), &initRes) + err := json.Unmarshal([]byte(stderr), &initRes) require.NoError(t, err) err = json.Unmarshal(initRes["chain_id"], &chainID) @@ -641,7 +641,7 @@ func executeInit(t *testing.T, cmdStr string) (chainID string) { } func executeGetAddrPK(t *testing.T, cmdStr string) (sdk.AccAddress, crypto.PubKey) { - out := tests.ExecuteT(t, cmdStr, "") + out, _ := tests.ExecuteT(t, cmdStr, "") var ko keys.KeyOutput keys.UnmarshalJSON([]byte(out), &ko) @@ -655,7 +655,7 @@ func executeGetAddrPK(t *testing.T, cmdStr string) (sdk.AccAddress, crypto.PubKe } func executeGetAccount(t *testing.T, cmdStr string) auth.BaseAccount { - out := tests.ExecuteT(t, cmdStr, "") + out, _ := tests.ExecuteT(t, cmdStr, "") var initRes map[string]json.RawMessage err := json.Unmarshal([]byte(out), &initRes) require.NoError(t, err, "out %v, err %v", out, err) @@ -672,7 +672,7 @@ func executeGetAccount(t *testing.T, cmdStr string) auth.BaseAccount { // stake func executeGetValidator(t *testing.T, cmdStr string) stake.Validator { - out := tests.ExecuteT(t, cmdStr, "") + out, _ := tests.ExecuteT(t, cmdStr, "") var validator stake.Validator cdc := app.MakeCodec() err := cdc.UnmarshalJSON([]byte(out), &validator) @@ -681,7 +681,7 @@ func executeGetValidator(t *testing.T, cmdStr string) stake.Validator { } func executeGetPool(t *testing.T, cmdStr string) stake.Pool { - out := tests.ExecuteT(t, cmdStr, "") + out, _ := tests.ExecuteT(t, cmdStr, "") var pool stake.Pool cdc := app.MakeCodec() err := cdc.UnmarshalJSON([]byte(out), &pool) @@ -690,7 +690,7 @@ func executeGetPool(t *testing.T, cmdStr string) stake.Pool { } func executeGetParams(t *testing.T, cmdStr string) stake.Params { - out := tests.ExecuteT(t, cmdStr, "") + out, _ := tests.ExecuteT(t, cmdStr, "") var params stake.Params cdc := app.MakeCodec() err := cdc.UnmarshalJSON([]byte(out), ¶ms) @@ -702,7 +702,7 @@ func executeGetParams(t *testing.T, cmdStr string) stake.Params { // gov func executeGetProposal(t *testing.T, cmdStr string) gov.Proposal { - out := tests.ExecuteT(t, cmdStr, "") + out, _ := tests.ExecuteT(t, cmdStr, "") var proposal gov.Proposal cdc := app.MakeCodec() err := cdc.UnmarshalJSON([]byte(out), &proposal) @@ -711,7 +711,7 @@ func executeGetProposal(t *testing.T, cmdStr string) gov.Proposal { } func executeGetVote(t *testing.T, cmdStr string) gov.Vote { - out := tests.ExecuteT(t, cmdStr, "") + out, _ := tests.ExecuteT(t, cmdStr, "") var vote gov.Vote cdc := app.MakeCodec() err := cdc.UnmarshalJSON([]byte(out), &vote) @@ -720,7 +720,7 @@ func executeGetVote(t *testing.T, cmdStr string) gov.Vote { } func executeGetVotes(t *testing.T, cmdStr string) []gov.Vote { - out := tests.ExecuteT(t, cmdStr, "") + out, _ := tests.ExecuteT(t, cmdStr, "") var votes []gov.Vote cdc := app.MakeCodec() err := cdc.UnmarshalJSON([]byte(out), &votes) diff --git a/cmd/gaia/cmd/gaiad/main.go b/cmd/gaia/cmd/gaiad/main.go index adfb12d5735..1304b32f348 100644 --- a/cmd/gaia/cmd/gaiad/main.go +++ b/cmd/gaia/cmd/gaiad/main.go @@ -32,6 +32,7 @@ func main() { appInit := app.GaiaAppInit() rootCmd.AddCommand(gaiaInit.InitCmd(ctx, cdc, appInit)) rootCmd.AddCommand(gaiaInit.TestnetFilesCmd(ctx, cdc, appInit)) + rootCmd.AddCommand(gaiaInit.GenTxCmd(ctx, cdc)) server.AddCommands(ctx, cdc, rootCmd, appInit, newApp, exportAppStateAndTMValidators) diff --git a/cmd/gaia/init/gentx.go b/cmd/gaia/init/gentx.go new file mode 100644 index 00000000000..1b24a357688 --- /dev/null +++ b/cmd/gaia/init/gentx.go @@ -0,0 +1,120 @@ +package init + +import ( + "fmt" + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/cmd/gaia/app" + "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/server" + sdk "github.com/cosmos/cosmos-sdk/types" + authcmd "github.com/cosmos/cosmos-sdk/x/auth/client/cli" + "github.com/cosmos/cosmos-sdk/x/stake/client/cli" + "github.com/spf13/cobra" + "github.com/spf13/viper" + cfg "github.com/tendermint/tendermint/config" + "github.com/tendermint/tendermint/crypto" + tmcli "github.com/tendermint/tendermint/libs/cli" + "github.com/tendermint/tendermint/libs/common" + "io/ioutil" + "os" + "path/filepath" +) + +const ( + defaultAmount = "100steak" + defaultCommissionRate = "0.1" + defaultCommissionMaxRate = "0.2" + defaultCommissionMaxChangeRate = "0.01" +) + +// GenTxCmd builds the gaiad gentx command. +// nolint: errcheck +func GenTxCmd(ctx *server.Context, cdc *codec.Codec) *cobra.Command { + cmd := &cobra.Command{ + Use: "gentx", + Short: "Generate a genesis tx carrying a self delegation", + Long: fmt.Sprintf(`This command is an alias of the 'gaiad tx create-validator' command'. + +It creates a genesis piece carrying a self delegation with the +following delegation and commission default parameters: + + delegation amount: %s + commission rate: %s + commission max rate: %s + commission max change rate: %s +`, defaultAmount, defaultCommissionRate, defaultCommissionMaxRate, defaultCommissionMaxChangeRate), + RunE: func(cmd *cobra.Command, args []string) error { + + config := ctx.Config + config.SetRoot(viper.GetString(tmcli.HomeFlag)) + nodeID, valPubKey, err := InitializeNodeValidatorFiles(ctx.Config) + if err != nil { + return err + } + ip, err := server.ExternalIP() + if err != nil { + return err + } + + // Run gaiad tx create-validator + prepareFlagsForTxCreateValidator(config, nodeID, ip, valPubKey) + createValidatorCmd := cli.GetCmdCreateValidator(cdc) + + w, err := ioutil.TempFile("", "gentx") + if err != nil { + return err + } + unsignedGenTxFilename := w.Name() + defer os.Remove(unsignedGenTxFilename) + os.Stdout = w + if err = createValidatorCmd.RunE(nil, args); err != nil { + return err + } + w.Close() + + prepareFlagsForTxSign() + signCmd := authcmd.GetSignCommand(cdc, authcmd.GetAccountDecoder(cdc)) + if w, err = prepareOutputFile(config.RootDir, nodeID); err != nil { + return err + } + os.Stdout = w + return signCmd.RunE(nil, []string{unsignedGenTxFilename}) + }, + } + + cmd.Flags().String(flagClientHome, app.DefaultCLIHome, "client's home directory") + cmd.Flags().String(client.FlagChainID, "", "genesis file chain-id") + cmd.Flags().String(client.FlagName, "", "name of private key with which to sign the gentx") + cmd.MarkFlagRequired(client.FlagName) + return cmd +} + +func prepareFlagsForTxCreateValidator(config *cfg.Config, nodeID, ip string, valPubKey crypto.PubKey) { + viper.Set(tmcli.HomeFlag, viper.GetString(flagClientHome)) // --home + viper.Set(client.FlagFrom, viper.GetString(client.FlagName)) // --from + viper.Set(cli.FlagNodeID, nodeID) // --node-id + viper.Set(cli.FlagIP, ip) // --ip + viper.Set(cli.FlagPubKey, sdk.MustBech32ifyConsPub(valPubKey)) // --pubkey + viper.Set(cli.FlagAmount, defaultAmount) // --amount + viper.Set(cli.FlagCommissionRate, defaultCommissionRate) + viper.Set(cli.FlagCommissionMaxRate, defaultCommissionMaxRate) + viper.Set(cli.FlagCommissionMaxChangeRate, defaultCommissionMaxChangeRate) + viper.Set(cli.FlagGenesisFormat, true) // --genesis-format + viper.Set(cli.FlagMoniker, config.Moniker) // --moniker + if config.Moniker == "" { + viper.Set(cli.FlagMoniker, viper.GetString(client.FlagName)) + } +} + +func prepareFlagsForTxSign() { + viper.Set("offline", true) +} + +func prepareOutputFile(rootDir, nodeID string) (w *os.File, err error) { + writePath := filepath.Join(rootDir, "config", "gentx") + if err = common.EnsureDir(writePath, 0700); err != nil { + return + } + filename := filepath.Join(writePath, fmt.Sprintf("gentx-%v.json", nodeID)) + return os.Create(filename) +} diff --git a/cmd/gaia/init/init.go b/cmd/gaia/init/init.go index a04c1d2ae77..467ea3fc2e1 100644 --- a/cmd/gaia/init/init.go +++ b/cmd/gaia/init/init.go @@ -2,311 +2,258 @@ package init import ( "encoding/json" + "errors" "fmt" - "io/ioutil" + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/cmd/gaia/app" + "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/crypto" + "github.com/tendermint/tendermint/privval" "os" - "path" "path/filepath" - "sort" "github.com/cosmos/cosmos-sdk/codec" "github.com/cosmos/cosmos-sdk/server" - servercfg "github.com/cosmos/cosmos-sdk/server/config" + sdk "github.com/cosmos/cosmos-sdk/types" "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/privval" "github.com/tendermint/tendermint/types" ) -// get cmd to initialize all files for tendermint and application -func GenTxCmd(ctx *server.Context, cdc *codec.Codec, appInit server.AppInit) *cobra.Command { - cmd := &cobra.Command{ - Use: "gen-tx", - Short: "Create genesis transaction file (under [--home]/config/gentx/gentx-[nodeID].json)", - Args: cobra.NoArgs, - RunE: func(_ *cobra.Command, args []string) error { - config := ctx.Config - config.SetRoot(viper.GetString(cli.HomeFlag)) - - ip := viper.GetString(server.FlagIP) - if len(ip) == 0 { - eip, err := server.ExternalIP() - if err != nil { - return err - } - ip = eip - } +const ( + flagWithTxs = "with-txs" + flagOverwrite = "overwrite" + flagClientHome = "home-client" + flagOverwriteKey = "overwrite-key" + flagSkipGenesis = "skip-genesis" + flagMoniker = "moniker" +) - genTxConfig := servercfg.GenTx{ - viper.GetString(server.FlagName), - viper.GetString(server.FlagClientHome), - viper.GetBool(server.FlagOWK), - ip, - } - cliPrint, genTxFile, err := gentxWithConfig(cdc, appInit, config, genTxConfig) - if err != nil { - return err - } - toPrint := struct { - AppMessage json.RawMessage `json:"app_message"` - GenTxFile json.RawMessage `json:"gen_tx_file"` - }{ - cliPrint, - genTxFile, - } - out, err := codec.MarshalJSONIndent(cdc, toPrint) - if err != nil { - return err - } - fmt.Println(string(out)) - return nil - }, - } - cmd.Flags().String(server.FlagIP, "", "external facing IP to use if left blank IP will be retrieved from this machine") - cmd.Flags().AddFlagSet(appInit.FlagsAppGenTx) - return cmd +type initConfig struct { + ChainID string + GenTxsDir string + Name string + NodeID string + ClientHome string + WithTxs bool + Overwrite bool + OverwriteKey bool + ValPubKey crypto.PubKey } -// NOTE: This will update (write) the config file with -// updated name (moniker) for node. -func gentxWithConfig(cdc *codec.Codec, appInit server.AppInit, config *cfg.Config, genTxConfig servercfg.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 - } +type printInfo struct { + Moniker string `json:"moniker"` + ChainID string `json:"chain_id"` + NodeID string `json:"node_id"` + AppMessage json.RawMessage `json:"app_message"` +} - tx := server.GenesisTx{ - NodeID: nodeID, - IP: genTxConfig.IP, - Validator: validator, - AppGenTx: appGenTx, - } - bz, err := codec.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 = common.EnsureDir(writePath, 0700) +// nolint: errcheck +func displayInfo(cdc *codec.Codec, info printInfo) error { + out, err := codec.MarshalJSONIndent(cdc, info) if err != nil { - return - } - err = common.WriteFile(file, bz, 0644) - if err != nil { - return + return err } - - // Write updated config with moniker - config.Moniker = genTxConfig.Name - configFilePath := filepath.Join(config.RootDir, "config", "config.toml") - cfg.WriteConfigFile(configFilePath, config) - - return + fmt.Fprintf(os.Stderr, "%s\n", string(out)) + return nil } // get cmd to initialize all files for tendermint and application -// nolint: golint +// nolint func InitCmd(ctx *server.Context, cdc *codec.Codec, appInit server.AppInit) *cobra.Command { cmd := &cobra.Command{ Use: "init", - Short: "Initialize genesis config, priv-validator file, and p2p-node file", - Args: cobra.NoArgs, - RunE: func(_ *cobra.Command, _ []string) error { + Short: "Initialize private validator, p2p, genesis, and application configuration files", + Long: `Initialize validators's and node's configuration files. +Note that only node's configuration files will be written if the flag --skip-genesis is +enabled, and the genesis file will not be generated. +`, + Args: cobra.NoArgs, + RunE: func(_ *cobra.Command, _ []string) error { config := ctx.Config config.SetRoot(viper.GetString(cli.HomeFlag)) - initConfig := server.InitConfig{ - viper.GetString(server.FlagChainID), - viper.GetBool(server.FlagWithTxs), - filepath.Join(config.RootDir, "config", "gentx"), - viper.GetBool(server.FlagOverwrite), - } - chainID, nodeID, appMessage, err := initWithConfig(cdc, appInit, config, initConfig) + name := viper.GetString(client.FlagName) + chainID := viper.GetString(client.FlagChainID) + if chainID == "" { + chainID = fmt.Sprintf("test-chain-%v", common.RandStr(6)) + } + nodeID, valPubKey, err := InitializeNodeValidatorFiles(config) if err != nil { return err } - // print out some key information - toPrint := struct { - ChainID string `json:"chain_id"` - NodeID string `json:"node_id"` - AppMessage json.RawMessage `json:"app_message"` - }{ - chainID, - nodeID, - appMessage, + + if viper.GetString(flagMoniker) != "" { + config.Moniker = viper.GetString(flagMoniker) + } + if config.Moniker == "" && name != "" { + config.Moniker = name + } + toPrint := printInfo{ + ChainID: chainID, + Moniker: config.Moniker, + NodeID: nodeID, } - out, err := codec.MarshalJSONIndent(cdc, toPrint) + if viper.GetBool(flagSkipGenesis) { + cfg.WriteConfigFile(filepath.Join(config.RootDir, "config", "config.toml"), config) + return displayInfo(cdc, toPrint) + } + + initCfg := initConfig{ + ChainID: chainID, + GenTxsDir: filepath.Join(config.RootDir, "config", "gentx"), + Name: name, + NodeID: nodeID, + ClientHome: viper.GetString(flagClientHome), + WithTxs: viper.GetBool(flagWithTxs), + Overwrite: viper.GetBool(flagOverwrite), + OverwriteKey: viper.GetBool(flagOverwriteKey), + ValPubKey: valPubKey, + } + appMessage, err := initWithConfig(cdc, config, initCfg) + // print out some key information if err != nil { return err } - fmt.Println(string(out)) - return nil + + toPrint.AppMessage = appMessage + return displayInfo(cdc, toPrint) }, } - cmd.Flags().BoolP(server.FlagOverwrite, "o", false, "overwrite the genesis.json file") - cmd.Flags().String(server.FlagChainID, "", "genesis file chain-id, if left blank will be randomly created") - cmd.Flags().Bool(server.FlagWithTxs, false, "apply existing 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)) + + cmd.Flags().String(cli.HomeFlag, app.DefaultNodeHome, "node's home directory") + cmd.Flags().BoolP(flagOverwrite, "o", false, "overwrite the genesis.json file") + cmd.Flags().String(client.FlagChainID, "", "genesis file chain-id, if left blank will be randomly created") + cmd.Flags().Bool(flagWithTxs, false, "apply existing genesis transactions from [--home]/config/gentx/") + cmd.Flags().String(client.FlagName, "", "name of private key with which to sign the gentx") + cmd.Flags().String(flagMoniker, "", "overrides --name flag and set the validator's moniker to a different value; ignored if it runs without the --with-txs flag") + cmd.Flags().String(flagClientHome, app.DefaultCLIHome, "client's home directory") + cmd.Flags().Bool(flagOverwriteKey, false, "overwrite client's key") + cmd.Flags().Bool(flagSkipGenesis, false, "do not create genesis.json") return cmd } -func initWithConfig(cdc *codec.Codec, appInit server.AppInit, config *cfg.Config, initConfig server.InitConfig) ( - chainID string, nodeID string, appMessage json.RawMessage, err error) { +// 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()) - pubKey := readOrCreatePrivValidator(config) - - if initConfig.ChainID == "" { - initConfig.ChainID = fmt.Sprintf("test-chain-%v", common.RandStr(6)) - } - chainID = initConfig.ChainID + valPubKey = ReadOrCreatePrivValidator(config.PrivValidatorFile()) + return +} +func initWithConfig(cdc *codec.Codec, config *cfg.Config, initCfg initConfig) ( + appMessage json.RawMessage, err error) { genFile := config.GenesisFile() - if !initConfig.Overwrite && common.FileExists(genFile) { + if !initCfg.Overwrite && common.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 []types.GenesisValidator + // process genesis transactions, else create default genesis.json + var appGenTxs []auth.StdTx var persistentPeers string + var genTxs []json.RawMessage + var appState json.RawMessage + var jsonRawTx json.RawMessage + chainID := initCfg.ChainID - if initConfig.GenTxs { - validators, appGenTxs, persistentPeers, err = processGenTxs(initConfig.GenTxsDir, cdc) + if initCfg.WithTxs { + _, appGenTxs, persistentPeers, err = app.CollectStdTxs(config.Moniker, initCfg.GenTxsDir, cdc) if err != nil { return } + genTxs = make([]json.RawMessage, len(appGenTxs)) config.P2P.PersistentPeers = persistentPeers - configFilePath := filepath.Join(config.RootDir, "config", "config.toml") - cfg.WriteConfigFile(configFilePath, config) + for i, stdTx := range appGenTxs { + jsonRawTx, err = cdc.MarshalJSON(stdTx) + if err != nil { + return + } + genTxs[i] = jsonRawTx + } } else { - genTxConfig := servercfg.GenTx{ - viper.GetString(server.FlagName), - viper.GetString(server.FlagClientHome), - viper.GetBool(server.FlagOWK), - "127.0.0.1", + var ip, keyPass, secret string + var addr sdk.AccAddress + var signedTx auth.StdTx + + if initCfg.Name == "" { + err = errors.New("must specify validator's moniker (--name)") + return } - // Write updated config with moniker - config.Moniker = genTxConfig.Name - configFilePath := filepath.Join(config.RootDir, "config", "config.toml") - cfg.WriteConfigFile(configFilePath, config) - appGenTx, am, validator, err := appInit.AppGenTx(cdc, pubKey, genTxConfig) - appMessage = am + config.Moniker = initCfg.Name + ip, err = server.ExternalIP() if err != nil { - return "", "", nil, err + return } - validators = []types.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 *codec.Codec) ( - validators []types.GenesisValidator, appGenTxs []json.RawMessage, persistentPeers string, err error) { - - var fos []os.FileInfo - fos, err = ioutil.ReadDir(genTxsDir) - if err != nil { - return - } - - genTxs := make(map[string]server.GenesisTx) - var nodeIDs []string - for _, fo := range fos { - filename := path.Join(genTxsDir, fo.Name()) - if !fo.IsDir() && (path.Ext(filename) != ".json") { - continue + memo := fmt.Sprintf("%s@%s:26656", initCfg.NodeID, ip) + buf := client.BufferStdin() + prompt := fmt.Sprintf("Password for account %q (default: %q):", initCfg.Name, app.DefaultKeyPass) + keyPass, err = client.GetPassword(prompt, buf) + if err != nil && keyPass != "" { + // An error was returned that either failed to read the password from + // STDIN or the given password is not empty but failed to meet minimum + // length requirements. + return + } + if keyPass == "" { + keyPass = app.DefaultKeyPass } - // get the genTx - var bz []byte - bz, err = ioutil.ReadFile(filename) + addr, secret, err = server.GenerateSaveCoinKey(initCfg.ClientHome, initCfg.Name, keyPass, initCfg.OverwriteKey) if err != nil { return } - var genTx server.GenesisTx - err = cdc.UnmarshalJSON(bz, &genTx) + appMessage, err = json.Marshal(map[string]string{"secret": secret}) if err != nil { return } - genTxs[genTx.NodeID] = genTx - nodeIDs = append(nodeIDs, genTx.NodeID) + msg := stake.NewMsgCreateValidator( + sdk.ValAddress(addr), + initCfg.ValPubKey, + sdk.NewInt64Coin("steak", 100), + stake.NewDescription(config.Moniker, "", "", ""), + stake.NewCommissionMsg(sdk.ZeroDec(), sdk.ZeroDec(), sdk.ZeroDec()), + ) + txBldr := authtx.NewTxBuilderFromCLI().WithCodec(cdc).WithMemo(memo).WithChainID(chainID) + signedTx, err = txBldr.SignStdTx( + initCfg.Name, keyPass, auth.NewStdTx([]sdk.Msg{msg}, auth.StdFee{}, []auth.StdSignature{}, memo), false, + ) + if err != nil { + return + } + jsonRawTx, err = cdc.MarshalJSON(signedTx) + if err != nil { + return + } + genTxs = []json.RawMessage{jsonRawTx} } - sort.Strings(nodeIDs) - - for _, nodeID := range nodeIDs { - genTx := genTxs[nodeID] - - // combine some stuff - validators = append(validators, genTx.Validator) - appGenTxs = append(appGenTxs, genTx.AppGenTx) - - // Add a persistent peer - comma := "," - if len(persistentPeers) == 0 { - comma = "" - } - persistentPeers += fmt.Sprintf("%s%s@%s:26656", comma, genTx.NodeID, genTx.IP) + cfg.WriteConfigFile(filepath.Join(config.RootDir, "config", "config.toml"), config) + appState, err = app.GaiaAppGenStateJSON(cdc, genTxs) + if err != nil { + return } + err = WriteGenesisFile(genFile, chainID, nil, appState) return } -// read of create the private key file for this config -func readOrCreatePrivValidator(tmConfig *cfg.Config) crypto.PubKey { - // private validator - privValFile := tmConfig.PrivValidatorFile() - var privValidator *privval.FilePV - if common.FileExists(privValFile) { - privValidator = privval.LoadFilePV(privValFile) - } else { - privValidator = privval.GenFilePV(privValFile) - privValidator.Save() - } - return privValidator.GetPubKey() -} - -// writeGenesisFile creates and writes the genesis configuration to disk. An +// 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(cdc *codec.Codec, genesisFile, chainID string, validators []types.GenesisValidator, appState json.RawMessage) error { +func WriteGenesisFile(genesisFile, chainID string, validators []types.GenesisValidator, appState json.RawMessage) error { genDoc := types.GenesisDoc{ ChainID: chainID, Validators: validators, @@ -319,3 +266,16 @@ func writeGenesisFile(cdc *codec.Codec, genesisFile, chainID string, validators 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() +} diff --git a/cmd/gaia/init/init_test.go b/cmd/gaia/init/init_test.go index 3a7f0a358d4..48a5d9247c3 100644 --- a/cmd/gaia/init/init_test.go +++ b/cmd/gaia/init/init_test.go @@ -2,47 +2,64 @@ package init import ( "bytes" + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/cmd/gaia/app" + "github.com/tendermint/tendermint/libs/cli" "io" "io/ioutil" "os" "testing" "time" - "github.com/cosmos/cosmos-sdk/codec" "github.com/cosmos/cosmos-sdk/server" "github.com/cosmos/cosmos-sdk/server/mock" "github.com/stretchr/testify/require" abciServer "github.com/tendermint/tendermint/abci/server" tcmd "github.com/tendermint/tendermint/cmd/tendermint/commands" "github.com/tendermint/tendermint/libs/log" + + "github.com/spf13/viper" ) func TestInitCmd(t *testing.T) { defer server.SetupViper(t)() + defer setupClientHome(t)() logger := log.NewNopLogger() cfg, err := tcmd.ParseConfig() require.Nil(t, err) ctx := server.NewContext(cfg, logger) - cdc := codec.New() + cdc := app.MakeCodec() appInit := server.AppInit{ AppGenState: mock.AppGenState, - AppGenTx: mock.AppGenTx, } cmd := InitCmd(ctx, cdc, appInit) err = cmd.RunE(nil, nil) require.NoError(t, err) } +func setupClientHome(t *testing.T) func() { + clientDir, err := ioutil.TempDir("", "mock-sdk-cmd") + require.Nil(t, err) + viper.Set(flagClientHome, clientDir) + viper.Set(flagOverwriteKey, true) + return func() { + if err := os.RemoveAll(clientDir); err != nil { + // TODO: Handle with #870 + panic(err) + } + } +} + func TestEmptyState(t *testing.T) { defer server.SetupViper(t)() + defer setupClientHome(t)() logger := log.NewNopLogger() cfg, err := tcmd.ParseConfig() require.Nil(t, err) ctx := server.NewContext(cfg, logger) - cdc := codec.New() + cdc := app.MakeCodec() appInit := server.AppInit{ - AppGenTx: mock.AppGenTx, AppGenState: mock.AppGenStateEmpty, } cmd := InitCmd(ctx, cdc, appInit) @@ -80,15 +97,17 @@ func TestStartStandAlone(t *testing.T) { defer func() { os.RemoveAll(home) }() + viper.Set(cli.HomeFlag, home) + viper.Set(client.FlagName, "moniker") + defer setupClientHome(t)() logger := log.NewNopLogger() cfg, err := tcmd.ParseConfig() require.Nil(t, err) ctx := server.NewContext(cfg, logger) - cdc := codec.New() + cdc := app.MakeCodec() appInit := server.AppInit{ AppGenState: mock.AppGenState, - AppGenTx: mock.AppGenTx, } initCmd := InitCmd(ctx, cdc, appInit) err = initCmd.RunE(nil, nil) @@ -109,3 +128,19 @@ func TestStartStandAlone(t *testing.T) { svr.Stop() } } + +func TestInitNodeValidatorFiles(t *testing.T) { + home, err := ioutil.TempDir("", "mock-sdk-cmd") + require.Nil(t, err) + defer func() { + os.RemoveAll(home) + }() + viper.Set(cli.HomeFlag, home) + viper.Set(client.FlagName, "moniker") + cfg, err := tcmd.ParseConfig() + require.Nil(t, err) + nodeID, valPubKey, err := InitializeNodeValidatorFiles(cfg) + require.Nil(t, err) + require.NotEqual(t, "", nodeID) + require.NotEqual(t, 0, len(valPubKey.Bytes())) +} diff --git a/cmd/gaia/init/testnet.go b/cmd/gaia/init/testnet.go index 48b714f2de2..3002b83a0e2 100644 --- a/cmd/gaia/init/testnet.go +++ b/cmd/gaia/init/testnet.go @@ -1,20 +1,24 @@ package init import ( + "encoding/json" "fmt" - "github.com/cosmos/cosmos-sdk/server" + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/cmd/gaia/app" + "github.com/cosmos/cosmos-sdk/codec" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/auth" + authtx "github.com/cosmos/cosmos-sdk/x/auth/client/txbuilder" + "github.com/cosmos/cosmos-sdk/x/stake" "net" + "os" "path/filepath" + "github.com/cosmos/cosmos-sdk/server" "github.com/spf13/cobra" - - gc "github.com/cosmos/cosmos-sdk/server/config" - - "os" - - "github.com/cosmos/cosmos-sdk/codec" "github.com/spf13/viper" cfg "github.com/tendermint/tendermint/config" + "github.com/tendermint/tendermint/crypto" cmn "github.com/tendermint/tendermint/libs/common" ) @@ -46,8 +50,7 @@ Example: `, RunE: func(_ *cobra.Command, _ []string) error { config := ctx.Config - err := testnetWithConfig(config, cdc, appInit) - return err + return testnetWithConfig(config, cdc, appInit) }, } cmd.Flags().Int(nValidators, 4, @@ -70,6 +73,12 @@ func testnetWithConfig(config *cfg.Config, cdc *codec.Codec, appInit server.AppI outDir := viper.GetString(outputDir) numValidators := viper.GetInt(nValidators) + // 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 for i := 0; i < numValidators; i++ { nodeDirName := fmt.Sprintf("%s%d", viper.GetString(nodeDirPrefix), i) @@ -92,60 +101,101 @@ func testnetWithConfig(config *cfg.Config, cdc *codec.Codec, appInit server.AppI return err } + monikers = append(monikers, nodeDirName) config.Moniker = nodeDirName ip, err := getIP(i) 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) + + buf := client.BufferStdin() + prompt := fmt.Sprintf("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 + // STDIN or the given password is not empty but failed to meet minimum + // length requirements. return err } + if keyPass == "" { + keyPass = app.DefaultKeyPass + } - genTxConfig := gc.GenTx{ - nodeDirName, - clientDir, - true, - ip, + addr, secret, err := server.GenerateSaveCoinKey(clientDir, nodeDirName, keyPass, true) + if err != nil { + _ = 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 { + return err } - // Run `init gen-tx` and generate initial transactions - cliPrint, genTxFile, err := gentxWithConfig(cdc, appInit, config, genTxConfig) + msg := stake.NewMsgCreateValidator( + sdk.ValAddress(addr), + valPubKeys[i], + sdk.NewInt64Coin("steak", 100), + stake.NewDescription(nodeDirName, "", "", ""), + stake.NewCommissionMsg(sdk.ZeroDec(), sdk.ZeroDec(), sdk.ZeroDec()), + ) + 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) return err } - // Save private key seed words - name := fmt.Sprintf("%v.json", "key_seed") - err = writeFile(name, clientDir, cliPrint) + txBytes, err := cdc.MarshalJSON(signedTx) if err != nil { + _ = os.RemoveAll(outDir) return err } // Gather gentxs folder - name = fmt.Sprintf("%v.json", nodeDirName) - err = writeFile(name, gentxsDir, genTxFile) + err = writeFile(fmt.Sprintf("%v.json", nodeDirName), gentxsDir, txBytes) if err != nil { + _ = os.RemoveAll(outDir) return err } } - // Generate genesis.json and config.toml - chainID := "chain-" + cmn.RandStr(6) for i := 0; i < numValidators; i++ { nodeDirName := fmt.Sprintf("%s%d", viper.GetString(nodeDirPrefix), i) nodeDaemonHomeName := viper.GetString(nodeDaemonHome) nodeDir := filepath.Join(outDir, nodeDirName, nodeDaemonHomeName) gentxsDir := filepath.Join(outDir, "gentxs") - initConfig := server.InitConfig{ - chainID, - true, - gentxsDir, - true, - } + moniker := monikers[i] config.Moniker = nodeDirName config.SetRoot(nodeDir) + nodeID, valPubKey := nodeIDs[i], valPubKeys[i] // Run `init` and generate genesis.json and config.toml - _, _, _, err := initWithConfig(cdc, appInit, config, initConfig) - if err != nil { + initCfg := initConfig{ + ChainID: chainID, + GenTxsDir: gentxsDir, + Name: moniker, + WithTxs: true, + Overwrite: true, + OverwriteKey: false, + NodeID: nodeID, + ValPubKey: valPubKey, + } + if _, err := initWithConfig(cdc, config, initCfg); err != nil { return err } } diff --git a/docs/getting-started/join-testnet.md b/docs/getting-started/join-testnet.md index 66ec97cad5f..ce070c4fe96 100644 --- a/docs/getting-started/join-testnet.md +++ b/docs/getting-started/join-testnet.md @@ -15,7 +15,7 @@ These instructions are for setting up a brand new full node from scratch. First, initialize the node and create the necessary config files: ```bash -gaiad init --name +gaiad init --skip-genesis --name ``` ::: warning Note diff --git a/examples/basecoin/app/app.go b/examples/basecoin/app/app.go index 657cbcf0fdb..3d9ac81736c 100644 --- a/examples/basecoin/app/app.go +++ b/examples/basecoin/app/app.go @@ -2,6 +2,7 @@ package app import ( "encoding/json" + "os" bam "github.com/cosmos/cosmos-sdk/baseapp" "github.com/cosmos/cosmos-sdk/codec" @@ -21,6 +22,12 @@ const ( appName = "BasecoinApp" ) +// default home directories for expected binaries +var ( + DefaultCLIHome = os.ExpandEnv("$HOME/.basecli") + DefaultNodeHome = os.ExpandEnv("$HOME/.basecoind") +) + // BasecoinApp implements an extended ABCI application. It contains a BaseApp, // a codec for serialization, KVStore keys for multistore state management, and // various mappers and keepers to manage getting, setting, and serializing the diff --git a/examples/basecoin/cli_test/cli_test.go b/examples/basecoin/cli_test/cli_test.go index 3a33135e399..635b54c3c01 100644 --- a/examples/basecoin/cli_test/cli_test.go +++ b/examples/basecoin/cli_test/cli_test.go @@ -6,6 +6,7 @@ import ( "os" "testing" + "github.com/cosmos/cosmos-sdk/cmd/gaia/app" "github.com/cosmos/cosmos-sdk/server" "github.com/cosmos/cosmos-sdk/tests" "github.com/stretchr/testify/require" @@ -13,10 +14,11 @@ import ( var ( basecoindHome = "" + basecliHome = "" ) func init() { - basecoindHome = getTestingHomeDir() + basecoindHome, basecliHome = getTestingHomeDirs() } func TestInitStartSequence(t *testing.T) { @@ -32,8 +34,8 @@ func executeInit(t *testing.T) { chainID string initRes map[string]json.RawMessage ) - out := tests.ExecuteT(t, fmt.Sprintf("basecoind --home=%s init", basecoindHome), "") - err := json.Unmarshal([]byte(out), &initRes) + _, stderr := tests.ExecuteT(t, fmt.Sprintf("basecoind --home=%s --home-client=%s init --name=test", basecoindHome, basecliHome), app.DefaultKeyPass) + err := json.Unmarshal([]byte(stderr), &initRes) require.NoError(t, err) err = json.Unmarshal(initRes["chain_id"], &chainID) require.NoError(t, err) @@ -45,8 +47,9 @@ func executeStart(t *testing.T, servAddr, port string) { tests.WaitForTMStart(port) } -func getTestingHomeDir() string { +func getTestingHomeDirs() (string, string) { tmpDir := os.TempDir() basecoindHome := fmt.Sprintf("%s%s.test_basecoind", tmpDir, string(os.PathSeparator)) - return basecoindHome + basecliHome := fmt.Sprintf("%s%s.test_basecli", tmpDir, string(os.PathSeparator)) + return basecoindHome, basecliHome } diff --git a/examples/basecoin/cmd/basecli/main.go b/examples/basecoin/cmd/basecli/main.go index cbfae5fe0cb..94c5c6e0169 100644 --- a/examples/basecoin/cmd/basecli/main.go +++ b/examples/basecoin/cmd/basecli/main.go @@ -1,8 +1,6 @@ package main import ( - "os" - "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/client/keys" "github.com/cosmos/cosmos-sdk/client/lcd" @@ -86,7 +84,7 @@ func main() { ) // prepare and add flags - executor := cli.PrepareMainCmd(rootCmd, "BC", os.ExpandEnv("$HOME/.basecli")) + executor := cli.PrepareMainCmd(rootCmd, "BC", app.DefaultCLIHome) err := executor.Execute() if err != nil { // Note: Handle with #870 diff --git a/examples/basecoin/cmd/basecoind/main.go b/examples/basecoin/cmd/basecoind/main.go index 36219354449..ac79eaad06c 100644 --- a/examples/basecoin/cmd/basecoind/main.go +++ b/examples/basecoin/cmd/basecoind/main.go @@ -2,23 +2,32 @@ package main import ( "encoding/json" + "fmt" + "github.com/tendermint/tendermint/p2p" "io" "os" "github.com/cosmos/cosmos-sdk/baseapp" gaiaInit "github.com/cosmos/cosmos-sdk/cmd/gaia/init" + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/codec" "github.com/cosmos/cosmos-sdk/examples/basecoin/app" "github.com/cosmos/cosmos-sdk/server" "github.com/spf13/cobra" "github.com/spf13/viper" abci "github.com/tendermint/tendermint/abci/types" "github.com/tendermint/tendermint/libs/cli" + "github.com/tendermint/tendermint/libs/common" dbm "github.com/tendermint/tendermint/libs/db" "github.com/tendermint/tendermint/libs/log" tmtypes "github.com/tendermint/tendermint/types" ) +const ( + flagClientHome = "home-client" +) + func main() { cdc := app.MakeCodec() ctx := server.NewDefaultContext() @@ -30,7 +39,7 @@ func main() { } appInit := server.DefaultAppInit - rootCmd.AddCommand(gaiaInit.InitCmd(ctx, cdc, appInit)) + rootCmd.AddCommand(InitCmd(ctx, cdc, appInit)) rootCmd.AddCommand(gaiaInit.TestnetFilesCmd(ctx, cdc, appInit)) server.AddCommands(ctx, cdc, rootCmd, appInit, @@ -47,6 +56,69 @@ func main() { } } +// get cmd to initialize all files for tendermint and application +// nolint: errcheck +func InitCmd(ctx *server.Context, cdc *codec.Codec, appInit server.AppInit) *cobra.Command { + cmd := &cobra.Command{ + Use: "init", + Short: "Initialize genesis config, priv-validator file, and p2p-node file", + Args: cobra.NoArgs, + RunE: func(_ *cobra.Command, _ []string) error { + + config := ctx.Config + config.SetRoot(viper.GetString(cli.HomeFlag)) + chainID := viper.GetString(client.FlagChainID) + if chainID == "" { + chainID = fmt.Sprintf("test-chain-%v", common.RandStr(6)) + } + + nodeKey, err := p2p.LoadOrGenNodeKey(config.NodeKeyFile()) + if err != nil { + return err + } + nodeID := string(nodeKey.ID()) + + pk := gaiaInit.ReadOrCreatePrivValidator(config.PrivValidatorFile()) + genTx, appMessage, validator, err := server.SimpleAppGenTx(cdc, pk) + if err != nil { + return err + } + + appState, err := appInit.AppGenState(cdc, []json.RawMessage{genTx}) + if err != nil { + return err + } + appStateJSON, err := cdc.MarshalJSON(appState) + if err != nil { + return err + } + + toPrint := struct { + ChainID string `json:"chain_id"` + NodeID string `json:"noide_id"` + AppMessage json.RawMessage `json:"app_message"` + }{ + chainID, + nodeID, + appMessage, + } + out, err := codec.MarshalJSONIndent(cdc, toPrint) + if err != nil { + return err + } + fmt.Fprintf(os.Stderr, "%s\n", string(out)) + return gaiaInit.WriteGenesisFile(config.GenesisFile(), chainID, []tmtypes.GenesisValidator{validator}, appStateJSON) + }, + } + + cmd.Flags().String(cli.HomeFlag, app.DefaultNodeHome, "node's home directory") + cmd.Flags().String(flagClientHome, app.DefaultCLIHome, "client's home directory") + cmd.Flags().String(client.FlagChainID, "", "genesis file chain-id, if left blank will be randomly created") + cmd.Flags().String(client.FlagName, "", "validator's moniker") + cmd.MarkFlagRequired(client.FlagName) + return cmd +} + func newApp(logger log.Logger, db dbm.DB, storeTracer io.Writer) abci.Application { return app.NewBasecoinApp(logger, db, baseapp.SetPruning(viper.GetString("pruning"))) } diff --git a/examples/democoin/app/app.go b/examples/democoin/app/app.go index 0449f6251ed..97dcf4e3584 100644 --- a/examples/democoin/app/app.go +++ b/examples/democoin/app/app.go @@ -2,6 +2,7 @@ package app import ( "encoding/json" + "os" abci "github.com/tendermint/tendermint/abci/types" cmn "github.com/tendermint/tendermint/libs/common" @@ -27,6 +28,12 @@ const ( appName = "DemocoinApp" ) +// default home directories for expected binaries +var ( + DefaultCLIHome = os.ExpandEnv("$HOME/.democli") + DefaultNodeHome = os.ExpandEnv("$HOME/.democoind") +) + // Extended ABCI application type DemocoinApp struct { *bam.BaseApp diff --git a/examples/democoin/cli_test/cli_test.go b/examples/democoin/cli_test/cli_test.go index 2db2fff0964..8df97c987e8 100644 --- a/examples/democoin/cli_test/cli_test.go +++ b/examples/democoin/cli_test/cli_test.go @@ -3,6 +3,7 @@ package clitest import ( "encoding/json" "fmt" + "github.com/cosmos/cosmos-sdk/cmd/gaia/app" "os" "testing" @@ -13,10 +14,11 @@ import ( var ( democoindHome = "" + democliHome = "" ) func init() { - democoindHome = getTestingHomeDir() + democoindHome, democliHome = getTestingHomeDirs() } func TestInitStartSequence(t *testing.T) { @@ -32,8 +34,8 @@ func executeInit(t *testing.T) { chainID string initRes map[string]json.RawMessage ) - out := tests.ExecuteT(t, fmt.Sprintf("democoind --home=%s init", democoindHome), "") - err := json.Unmarshal([]byte(out), &initRes) + _, stderr := tests.ExecuteT(t, fmt.Sprintf("democoind --home=%s --home-client=%s init --name=test", democoindHome, democliHome), app.DefaultKeyPass) + err := json.Unmarshal([]byte(stderr), &initRes) require.NoError(t, err) err = json.Unmarshal(initRes["chain_id"], &chainID) require.NoError(t, err) @@ -45,8 +47,9 @@ func executeStart(t *testing.T, servAddr, port string) { tests.WaitForTMStart(port) } -func getTestingHomeDir() string { +func getTestingHomeDirs() (string, string) { tmpDir := os.TempDir() democoindHome := fmt.Sprintf("%s%s.test_democoind", tmpDir, string(os.PathSeparator)) - return democoindHome + democliHome := fmt.Sprintf("%s%s.test_democli", tmpDir, string(os.PathSeparator)) + return democoindHome, democliHome } diff --git a/examples/democoin/cmd/democli/main.go b/examples/democoin/cmd/democli/main.go index 43f86504eb2..08f13116871 100644 --- a/examples/democoin/cmd/democli/main.go +++ b/examples/democoin/cmd/democli/main.go @@ -1,8 +1,6 @@ package main import ( - "os" - "github.com/spf13/cobra" "github.com/tendermint/tendermint/libs/cli" @@ -91,7 +89,7 @@ func main() { ) // prepare and add flags - executor := cli.PrepareMainCmd(rootCmd, "BC", os.ExpandEnv("$HOME/.democli")) + executor := cli.PrepareMainCmd(rootCmd, "BC", app.DefaultCLIHome) err := executor.Execute() if err != nil { // handle with #870 diff --git a/examples/democoin/cmd/democoind/main.go b/examples/democoin/cmd/democoind/main.go index 7f8c7c54d17..8dfa2f95f62 100644 --- a/examples/democoin/cmd/democoind/main.go +++ b/examples/democoin/cmd/democoind/main.go @@ -2,6 +2,11 @@ package main import ( "encoding/json" + "fmt" + "github.com/cosmos/cosmos-sdk/client" + "github.com/spf13/viper" + "github.com/tendermint/tendermint/libs/common" + "github.com/tendermint/tendermint/p2p" "io" "os" @@ -19,10 +24,13 @@ import ( "github.com/cosmos/cosmos-sdk/server" ) +const ( + flagClientHome = "home-client" +) + // init parameters var CoolAppInit = server.AppInit{ AppGenState: CoolAppGenState, - AppGenTx: server.SimpleAppGenTx, } // coolGenAppParams sets up the app_state and appends the cool app state @@ -52,6 +60,69 @@ func CoolAppGenState(cdc *codec.Codec, appGenTxs []json.RawMessage) (appState js return } +// get cmd to initialize all files for tendermint and application +// nolint: errcheck +func InitCmd(ctx *server.Context, cdc *codec.Codec, appInit server.AppInit) *cobra.Command { + cmd := &cobra.Command{ + Use: "init", + Short: "Initialize genesis config, priv-validator file, and p2p-node file", + Args: cobra.NoArgs, + RunE: func(_ *cobra.Command, _ []string) error { + + config := ctx.Config + config.SetRoot(viper.GetString(cli.HomeFlag)) + chainID := viper.GetString(client.FlagChainID) + if chainID == "" { + chainID = fmt.Sprintf("test-chain-%v", common.RandStr(6)) + } + + nodeKey, err := p2p.LoadOrGenNodeKey(config.NodeKeyFile()) + if err != nil { + return err + } + nodeID := string(nodeKey.ID()) + + pk := gaiaInit.ReadOrCreatePrivValidator(config.PrivValidatorFile()) + genTx, appMessage, validator, err := server.SimpleAppGenTx(cdc, pk) + if err != nil { + return err + } + + appState, err := appInit.AppGenState(cdc, []json.RawMessage{genTx}) + if err != nil { + return err + } + appStateJSON, err := cdc.MarshalJSON(appState) + if err != nil { + return err + } + + toPrint := struct { + ChainID string `json:"chain_id"` + NodeID string `json:"noide_id"` + AppMessage json.RawMessage `json:"app_message"` + }{ + chainID, + nodeID, + appMessage, + } + out, err := codec.MarshalJSONIndent(cdc, toPrint) + if err != nil { + return err + } + fmt.Fprintf(os.Stderr, "%s\n", string(out)) + return gaiaInit.WriteGenesisFile(config.GenesisFile(), chainID, []tmtypes.GenesisValidator{validator}, appStateJSON) + }, + } + + cmd.Flags().String(cli.HomeFlag, app.DefaultNodeHome, "node's home directory") + cmd.Flags().String(flagClientHome, app.DefaultCLIHome, "client's home directory") + cmd.Flags().String(client.FlagChainID, "", "genesis file chain-id, if left blank will be randomly created") + cmd.Flags().String(client.FlagName, "", "validator's moniker") + cmd.MarkFlagRequired(client.FlagName) + return cmd +} + func newApp(logger log.Logger, db dbm.DB, _ io.Writer) abci.Application { return app.NewDemocoinApp(logger, db) } @@ -71,7 +142,7 @@ func main() { PersistentPreRunE: server.PersistentPreRunEFn(ctx), } - rootCmd.AddCommand(gaiaInit.InitCmd(ctx, cdc, CoolAppInit)) + rootCmd.AddCommand(InitCmd(ctx, cdc, CoolAppInit)) rootCmd.AddCommand(gaiaInit.TestnetFilesCmd(ctx, cdc, CoolAppInit)) server.AddCommands(ctx, cdc, rootCmd, CoolAppInit, diff --git a/server/config/config.go b/server/config/config.go index bd0d966e35b..239097b1373 100644 --- a/server/config/config.go +++ b/server/config/config.go @@ -35,16 +35,3 @@ func (c *Config) MinimumFees() sdk.Coins { // DefaultConfig returns server's default configuration. func DefaultConfig() *Config { return &Config{BaseConfig{MinFees: defaultMinimumFees}} } - -//_____________________________________________________________________ - -// 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 -} diff --git a/server/init.go b/server/init.go index 091ffa9484e..65a896e2ac1 100644 --- a/server/init.go +++ b/server/init.go @@ -2,111 +2,59 @@ package server import ( "encoding/json" + "errors" "fmt" "github.com/cosmos/cosmos-sdk/crypto/keys" - "github.com/pkg/errors" - "github.com/spf13/pflag" "github.com/tendermint/tendermint/crypto" - dbm "github.com/tendermint/tendermint/libs/db" - tmtypes "github.com/tendermint/tendermint/types" + "github.com/tendermint/tendermint/types" clkeys "github.com/cosmos/cosmos-sdk/client/keys" "github.com/cosmos/cosmos-sdk/codec" - serverconfig "github.com/cosmos/cosmos-sdk/server/config" sdk "github.com/cosmos/cosmos-sdk/types" + tmtypes "github.com/tendermint/tendermint/types" ) -//Parameter names, for init gen-tx command -var ( - FlagName = "name" - FlagClientHome = "home-client" - FlagOWK = "owk" -) - -//parameter names, init command -var ( - FlagOverwrite = "overwrite" - FlagWithTxs = "with-txs" - FlagIP = "ip" - FlagChainID = "chain-id" -) - -// genesis piece structure for creating combined genesis -type GenesisTx struct { - NodeID string `json:"node_id"` - IP string `json:"ip"` - Validator tmtypes.GenesisValidator `json:"validator"` - AppGenTx json.RawMessage `json:"app_gen_tx"` -} - -// Storage for init command input parameters -type InitConfig struct { - ChainID string - GenTxs bool - GenTxsDir string - Overwrite bool -} - -//________________________________________________________________________________________ - -//_____________________________________________________________________ - // Core functionality passed from the application to the server init command type AppInit struct { - - // flags required for application init functions - FlagsAppGenState *pflag.FlagSet - FlagsAppGenTx *pflag.FlagSet - - // create the application genesis tx - AppGenTx func(cdc *codec.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 // pubkey meant to represent the pubkey of the validator of this machine. - AppGenState func(cdc *codec.Codec, appGenTxs []json.RawMessage) (appState json.RawMessage, err error) + AppGenState func(cdc *codec.Codec, appGenTx []json.RawMessage) (appState json.RawMessage, err error) +} + +// SimpleGenTx is a simple genesis tx +type SimpleGenTx struct { + Addr sdk.AccAddress `json:"addr"` } //_____________________________________________________________________ // simple default application init var DefaultAppInit = AppInit{ - AppGenTx: SimpleAppGenTx, AppGenState: SimpleAppGenState, } -// simple genesis tx -type SimpleGenTx struct { - Addr sdk.AccAddress `json:"addr"` -} - // Generate a genesis transaction -func SimpleAppGenTx(cdc *codec.Codec, pk crypto.PubKey, genTxConfig serverconfig.GenTx) ( - appGenTx, cliPrint json.RawMessage, validator tmtypes.GenesisValidator, err error) { - +func SimpleAppGenTx(cdc *codec.Codec, pk crypto.PubKey) (appGenTx, cliPrint json.RawMessage, validator types.GenesisValidator, err error) { var addr sdk.AccAddress var secret string addr, secret, err = GenerateCoinKey() if err != nil { return } - var bz []byte - simpleGenTx := SimpleGenTx{addr} + simpleGenTx := SimpleGenTx{Addr: addr} bz, err = cdc.MarshalJSON(simpleGenTx) if err != nil { return } appGenTx = json.RawMessage(bz) - mm := map[string]string{"secret": secret} bz, err = cdc.MarshalJSON(mm) if err != nil { return } cliPrint = json.RawMessage(bz) - validator = tmtypes.GenesisValidator{ PubKey: pk, Power: 10, @@ -122,8 +70,8 @@ func SimpleAppGenState(cdc *codec.Codec, appGenTxs []json.RawMessage) (appState return } - var genTx SimpleGenTx - err = cdc.UnmarshalJSON(appGenTxs[0], &genTx) + var tx SimpleGenTx + err = cdc.UnmarshalJSON(appGenTxs[0], &tx) if err != nil { return } @@ -138,7 +86,7 @@ func SimpleAppGenState(cdc *codec.Codec, appGenTxs []json.RawMessage) (appState } ] }] -}`, genTx.Addr)) +}`, tx.Addr)) return } @@ -176,7 +124,8 @@ func GenerateSaveCoinKey(clientRoot, keyName, keyPass string, overwrite bool) (s if !overwrite { _, err := keybase.Get(keyName) if err == nil { - return sdk.AccAddress([]byte{}), "", errors.New("key already exists, overwrite is disabled") + return sdk.AccAddress([]byte{}), "", fmt.Errorf( + "key already exists, overwrite is disabled (clientRoot: %s)", clientRoot) } } diff --git a/server/mock/app.go b/server/mock/app.go index abdec6be508..18afa164ec6 100644 --- a/server/mock/app.go +++ b/server/mock/app.go @@ -6,14 +6,11 @@ import ( "path/filepath" abci "github.com/tendermint/tendermint/abci/types" - "github.com/tendermint/tendermint/crypto" dbm "github.com/tendermint/tendermint/libs/db" "github.com/tendermint/tendermint/libs/log" - tmtypes "github.com/tendermint/tendermint/types" bam "github.com/cosmos/cosmos-sdk/baseapp" "github.com/cosmos/cosmos-sdk/codec" - gc "github.com/cosmos/cosmos-sdk/server/config" sdk "github.com/cosmos/cosmos-sdk/types" ) @@ -126,14 +123,3 @@ func AppGenStateEmpty(_ *codec.Codec, _ []json.RawMessage) (appState json.RawMes appState = json.RawMessage(``) return } - -// Return a validator, not much else -func AppGenTx(_ *codec.Codec, pk crypto.PubKey, genTxConfig gc.GenTx) ( - appGenTx, cliPrint json.RawMessage, validator tmtypes.GenesisValidator, err error) { - - validator = tmtypes.GenesisValidator{ - PubKey: pk, - Power: 10, - } - return -} diff --git a/server/test_helpers.go b/server/test_helpers.go index 8de164e08ae..4347bad6c35 100644 --- a/server/test_helpers.go +++ b/server/test_helpers.go @@ -2,6 +2,7 @@ package server import ( "fmt" + "github.com/cosmos/cosmos-sdk/client" "io/ioutil" "net" "os" @@ -42,6 +43,7 @@ func SetupViper(t *testing.T) func() { rootDir, err := ioutil.TempDir("", "mock-sdk-cmd") require.Nil(t, err) viper.Set(cli.HomeFlag, rootDir) + viper.Set(client.FlagName, "moniker") return func() { err := os.RemoveAll(rootDir) if err != nil { diff --git a/tests/gobash.go b/tests/gobash.go index 6282f2fda4c..87d56a2974d 100644 --- a/tests/gobash.go +++ b/tests/gobash.go @@ -14,7 +14,7 @@ import ( // ExecuteT executes the command, pipes any input to STDIN and return STDOUT, // logging STDOUT/STDERR to t. // nolint: errcheck -func ExecuteT(t *testing.T, cmd, input string) (out string) { +func ExecuteT(t *testing.T, cmd, input string) (stdout, stderr string) { t.Log("Running", cmn.Cyan(cmd)) // split cmd to name and args @@ -50,8 +50,10 @@ func ExecuteT(t *testing.T, cmd, input string) (out string) { t.Log("Stderr:", cmn.Red(string(errbz))) } - out = strings.Trim(string(outbz), "\n") - return out + stdout = strings.Trim(string(outbz), "\n") + stderr = strings.Trim(string(errbz), "\n") + + return } // Execute the command, launch goroutines to log stdout/err to t. diff --git a/x/auth/ante.go b/x/auth/ante.go index b6f88025453..8a10a0239b2 100644 --- a/x/auth/ante.go +++ b/x/auth/ante.go @@ -4,7 +4,6 @@ import ( "bytes" "encoding/hex" "fmt" - sdk "github.com/cosmos/cosmos-sdk/types" "github.com/tendermint/tendermint/crypto" "github.com/tendermint/tendermint/crypto/ed25519" @@ -81,7 +80,7 @@ func NewAnteHandler(am AccountMapper, fck FeeCollectionKeeper) sdk.AnteHandler { if !res.IsOK() { return newCtx, res, true } - res = validateAccNumAndSequence(signerAccs, stdSigs) + res = validateAccNumAndSequence(ctx, signerAccs, stdSigs) if !res.IsOK() { return newCtx, res, true } @@ -149,17 +148,23 @@ func getSignerAccs(ctx sdk.Context, am AccountMapper, addrs []sdk.AccAddress) (a return } -func validateAccNumAndSequence(accs []Account, sigs []StdSignature) sdk.Result { +func validateAccNumAndSequence(ctx sdk.Context, accs []Account, sigs []StdSignature) sdk.Result { for i := 0; i < len(accs); i++ { - accnum := accs[i].GetAccountNumber() - seq := accs[i].GetSequence() + // On InitChain, make sure account number == 0 + if ctx.BlockHeight() == 0 && sigs[i].AccountNumber != 0 { + return sdk.ErrInvalidSequence( + fmt.Sprintf("Invalid account number for BlockHeight == 0. Got %d, expected 0", sigs[i].AccountNumber)).Result() + } + // Check account number. - if accnum != sigs[i].AccountNumber { + accnum := accs[i].GetAccountNumber() + if ctx.BlockHeight() != 0 && accnum != sigs[i].AccountNumber { return sdk.ErrInvalidSequence( fmt.Sprintf("Invalid account number. Got %d, expected %d", sigs[i].AccountNumber, accnum)).Result() } // Check sequence number. + seq := accs[i].GetSequence() if seq != sigs[i].Sequence { return sdk.ErrInvalidSequence( fmt.Sprintf("Invalid sequence. Got %d, expected %d", sigs[i].Sequence, seq)).Result() @@ -287,7 +292,7 @@ func ensureSufficientMempoolFees(ctx sdk.Context, stdTx StdTx) sdk.Result { func setGasMeter(simulate bool, ctx sdk.Context, stdTx StdTx) sdk.Context { // set the gas meter - if simulate { + if simulate || ctx.BlockHeight() == 0 { return ctx.WithGasMeter(sdk.NewInfiniteGasMeter()) } return ctx.WithGasMeter(sdk.NewGasMeter(stdTx.Fee.Gas)) diff --git a/x/auth/ante_test.go b/x/auth/ante_test.go index 2a289f317bf..bacb3013f90 100644 --- a/x/auth/ante_test.go +++ b/x/auth/ante_test.go @@ -169,6 +169,7 @@ func TestAnteHandlerAccountNumbers(t *testing.T) { feeCollector := NewFeeCollectionKeeper(cdc, capKey2) anteHandler := NewAnteHandler(mapper, feeCollector) ctx := sdk.NewContext(ms, abci.Header{ChainID: "mychainid"}, false, log.NewNopLogger()) + ctx = ctx.WithBlockHeight(1) // keys and addresses priv1, addr1 := privAndAddr() @@ -218,6 +219,66 @@ func TestAnteHandlerAccountNumbers(t *testing.T) { checkValidTx(t, anteHandler, ctx, tx, false) } +// Test logic around account number checking with many signers when BlockHeight is 0. +func TestAnteHandlerAccountNumbersAtBlockHeightZero(t *testing.T) { + // setup + ms, capKey, capKey2 := setupMultiStore() + cdc := codec.New() + RegisterBaseAccount(cdc) + mapper := NewAccountMapper(cdc, capKey, ProtoBaseAccount) + feeCollector := NewFeeCollectionKeeper(cdc, capKey2) + anteHandler := NewAnteHandler(mapper, feeCollector) + ctx := sdk.NewContext(ms, abci.Header{ChainID: "mychainid"}, false, log.NewNopLogger()) + ctx = ctx.WithBlockHeight(0) + + // keys and addresses + priv1, addr1 := privAndAddr() + priv2, addr2 := privAndAddr() + + // set the accounts + acc1 := mapper.NewAccountWithAddress(ctx, addr1) + acc1.SetCoins(newCoins()) + mapper.SetAccount(ctx, acc1) + acc2 := mapper.NewAccountWithAddress(ctx, addr2) + acc2.SetCoins(newCoins()) + mapper.SetAccount(ctx, acc2) + + // msg and signatures + var tx sdk.Tx + msg := newTestMsg(addr1) + fee := newStdFee() + + msgs := []sdk.Msg{msg} + + // test good tx from one signer + privs, accnums, seqs := []crypto.PrivKey{priv1}, []int64{0}, []int64{0} + tx = newTestTx(ctx, msgs, privs, accnums, seqs, fee) + checkValidTx(t, anteHandler, ctx, tx, false) + + // new tx from wrong account number + seqs = []int64{1} + tx = newTestTx(ctx, msgs, privs, []int64{1}, seqs, fee) + checkInvalidTx(t, anteHandler, ctx, tx, false, sdk.CodeInvalidSequence) + + // from correct account number + seqs = []int64{1} + tx = newTestTx(ctx, msgs, privs, []int64{0}, seqs, fee) + checkValidTx(t, anteHandler, ctx, tx, false) + + // new tx with another signer and incorrect account numbers + msg1 := newTestMsg(addr1, addr2) + msg2 := newTestMsg(addr2, addr1) + msgs = []sdk.Msg{msg1, msg2} + privs, accnums, seqs = []crypto.PrivKey{priv1, priv2}, []int64{1, 0}, []int64{2, 0} + tx = newTestTx(ctx, msgs, privs, accnums, seqs, fee) + checkInvalidTx(t, anteHandler, ctx, tx, false, sdk.CodeInvalidSequence) + + // correct account numbers + privs, accnums, seqs = []crypto.PrivKey{priv1, priv2}, []int64{0, 0}, []int64{2, 0} + tx = newTestTx(ctx, msgs, privs, accnums, seqs, fee) + checkValidTx(t, anteHandler, ctx, tx, false) +} + // Test logic around sequence checking with one signer and many signers. func TestAnteHandlerSequences(t *testing.T) { // setup @@ -228,6 +289,7 @@ func TestAnteHandlerSequences(t *testing.T) { feeCollector := NewFeeCollectionKeeper(cdc, capKey2) anteHandler := NewAnteHandler(mapper, feeCollector) ctx := sdk.NewContext(ms, abci.Header{ChainID: "mychainid"}, false, log.NewNopLogger()) + ctx = ctx.WithBlockHeight(1) // keys and addresses priv1, addr1 := privAndAddr() @@ -348,6 +410,7 @@ func TestAnteHandlerMemoGas(t *testing.T) { feeCollector := NewFeeCollectionKeeper(cdc, capKey2) anteHandler := NewAnteHandler(mapper, feeCollector) ctx := sdk.NewContext(ms, abci.Header{ChainID: "mychainid"}, false, log.NewNopLogger()) + ctx = ctx.WithBlockHeight(1) // keys and addresses priv1, addr1 := privAndAddr() @@ -391,6 +454,7 @@ func TestAnteHandlerMultiSigner(t *testing.T) { feeCollector := NewFeeCollectionKeeper(cdc, capKey2) anteHandler := NewAnteHandler(mapper, feeCollector) ctx := sdk.NewContext(ms, abci.Header{ChainID: "mychainid"}, false, log.NewNopLogger()) + ctx = ctx.WithBlockHeight(1) // keys and addresses priv1, addr1 := privAndAddr() @@ -442,6 +506,7 @@ func TestAnteHandlerBadSignBytes(t *testing.T) { feeCollector := NewFeeCollectionKeeper(cdc, capKey2) anteHandler := NewAnteHandler(mapper, feeCollector) ctx := sdk.NewContext(ms, abci.Header{ChainID: "mychainid"}, false, log.NewNopLogger()) + ctx = ctx.WithBlockHeight(1) // keys and addresses priv1, addr1 := privAndAddr() @@ -523,6 +588,7 @@ func TestAnteHandlerSetPubKey(t *testing.T) { feeCollector := NewFeeCollectionKeeper(cdc, capKey2) anteHandler := NewAnteHandler(mapper, feeCollector) ctx := sdk.NewContext(ms, abci.Header{ChainID: "mychainid"}, false, log.NewNopLogger()) + ctx = ctx.WithBlockHeight(1) // keys and addresses priv1, addr1 := privAndAddr() diff --git a/x/auth/client/cli/sign.go b/x/auth/client/cli/sign.go index 30704a50027..67c64dc2ad3 100644 --- a/x/auth/client/cli/sign.go +++ b/x/auth/client/cli/sign.go @@ -19,6 +19,7 @@ import ( const ( flagAppend = "append" flagPrintSigs = "print-sigs" + flagOffline = "offline" ) // GetSignCommand returns the sign command @@ -27,13 +28,18 @@ func GetSignCommand(codec *amino.Codec, decoder auth.AccountDecoder) *cobra.Comm Use: "sign ", Short: "Sign transactions generated offline", Long: `Sign transactions created with the --generate-only flag. -Read a transaction from , sign it, and print its JSON encoding.`, +Read a transaction from , sign it, and print its JSON encoding. + +The --offline flag makes sure that the client will not reach out to the local cache. +Thus account number or sequence number lookups will not be performed and it is +recommended to set such parameters manually.`, RunE: makeSignCmd(codec, decoder), Args: cobra.ExactArgs(1), } cmd.Flags().String(client.FlagName, "", "Name of private key with which to sign") cmd.Flags().Bool(flagAppend, true, "Append the signature to the existing ones. If disabled, old signatures would be overwritten") cmd.Flags().Bool(flagPrintSigs, false, "Print the addresses that must sign the transaction and those who have already signed it, then exit") + cmd.Flags().Bool(flagOffline, false, "Offline mode. Do not query local cache.") return cmd } @@ -53,7 +59,7 @@ func makeSignCmd(cdc *amino.Codec, decoder auth.AccountDecoder) func(cmd *cobra. cliCtx := context.NewCLIContext().WithCodec(cdc).WithAccountDecoder(decoder) txBldr := authtxb.NewTxBuilderFromCLI() - newTx, err := utils.SignStdTx(txBldr, cliCtx, name, stdTx, viper.GetBool(flagAppend)) + newTx, err := utils.SignStdTx(txBldr, cliCtx, name, stdTx, viper.GetBool(flagAppend), viper.GetBool(flagOffline)) if err != nil { return err } diff --git a/x/bank/app_test.go b/x/bank/app_test.go index 77991f94b78..7c320a9bd0e 100644 --- a/x/bank/app_test.go +++ b/x/bank/app_test.go @@ -210,7 +210,7 @@ func TestSengMsgMultipleInOut(t *testing.T) { testCases := []appTestCase{ { msgs: []sdk.Msg{sendMsg3}, - accNums: []int64{0, 2}, + accNums: []int64{0, 0}, accSeqs: []int64{0, 0}, expSimPass: true, expPass: true, @@ -258,7 +258,7 @@ func TestMsgSendDependent(t *testing.T) { }, { msgs: []sdk.Msg{sendMsg4}, - accNums: []int64{1}, + accNums: []int64{0}, accSeqs: []int64{0}, expSimPass: true, expPass: true, diff --git a/x/distribution/keeper/hooks.go b/x/distribution/keeper/hooks.go index 721a26db1de..d551cc3a3e3 100644 --- a/x/distribution/keeper/hooks.go +++ b/x/distribution/keeper/hooks.go @@ -14,9 +14,9 @@ func (k Keeper) onValidatorCreated(ctx sdk.Context, addr sdk.ValAddress) { vdi := types.ValidatorDistInfo{ OperatorAddr: addr, FeePoolWithdrawalHeight: height, - Pool: types.DecCoins{}, - PoolCommission: types.DecCoins{}, - DelAccum: types.NewTotalAccum(height), + Pool: types.DecCoins{}, + PoolCommission: types.DecCoins{}, + DelAccum: types.NewTotalAccum(height), } k.SetValidatorDistInfo(ctx, vdi) } diff --git a/x/mock/simulation/random_simulate_blocks.go b/x/mock/simulation/random_simulate_blocks.go index 0af6fbbac11..e52ca1068c6 100644 --- a/x/mock/simulation/random_simulate_blocks.go +++ b/x/mock/simulation/random_simulate_blocks.go @@ -82,7 +82,7 @@ func SimulateFromSeed(tb testing.TB, app *baseapp.BaseApp, // Initially this is the same as the initial validator set nextValidators := validators - header := abci.Header{Height: 0, Time: timestamp, ProposerAddress: randomProposer(r, validators)} + header := abci.Header{Height: 1, Time: timestamp, ProposerAddress: randomProposer(r, validators)} opCount := 0 // Setup code to catch SIGTERM's @@ -384,7 +384,7 @@ func RandomRequestBeginBlock(r *rand.Rand, validators map[string]mockValidator, time := header.Time vals := voteInfos if r.Float64() < pastEvidenceFraction { - height = int64(r.Intn(int(header.Height))) + height = int64(r.Intn(int(header.Height) - 1)) time = pastTimes[height] vals = pastVoteInfos[height] } diff --git a/x/stake/app_test.go b/x/stake/app_test.go index 4ab210f70a8..faafb6664f7 100644 --- a/x/stake/app_test.go +++ b/x/stake/app_test.go @@ -137,7 +137,7 @@ func TestStakeMsgs(t *testing.T) { addr1, sdk.ValAddress(addr2), priv2.PubKey(), bondCoin, description, commissionMsg, ) - mock.SignCheckDeliver(t, mApp.BaseApp, []sdk.Msg{createValidatorMsgOnBehalfOf}, []int64{0, 1}, []int64{1, 0}, true, true, priv1, priv2) + mock.SignCheckDeliver(t, mApp.BaseApp, []sdk.Msg{createValidatorMsgOnBehalfOf}, []int64{0, 0}, []int64{1, 0}, true, true, priv1, priv2) mock.CheckBalance(t, mApp, addr1, sdk.Coins{genCoin.Minus(bondCoin).Minus(bondCoin)}) mApp.BeginBlock(abci.RequestBeginBlock{}) @@ -161,13 +161,13 @@ func TestStakeMsgs(t *testing.T) { mock.CheckBalance(t, mApp, addr2, sdk.Coins{genCoin}) delegateMsg := NewMsgDelegate(addr2, sdk.ValAddress(addr1), bondCoin) - mock.SignCheckDeliver(t, mApp.BaseApp, []sdk.Msg{delegateMsg}, []int64{1}, []int64{1}, true, true, priv2) + mock.SignCheckDeliver(t, mApp.BaseApp, []sdk.Msg{delegateMsg}, []int64{0}, []int64{1}, true, true, priv2) mock.CheckBalance(t, mApp, addr2, sdk.Coins{genCoin.Minus(bondCoin)}) checkDelegation(t, mApp, keeper, addr2, sdk.ValAddress(addr1), true, sdk.NewDec(10)) // begin unbonding beginUnbondingMsg := NewMsgBeginUnbonding(addr2, sdk.ValAddress(addr1), sdk.NewDec(10)) - mock.SignCheckDeliver(t, mApp.BaseApp, []sdk.Msg{beginUnbondingMsg}, []int64{1}, []int64{2}, true, true, priv2) + mock.SignCheckDeliver(t, mApp.BaseApp, []sdk.Msg{beginUnbondingMsg}, []int64{0}, []int64{2}, true, true, priv2) // delegation should exist anymore checkDelegation(t, mApp, keeper, addr2, sdk.ValAddress(addr1), false, sdk.Dec{}) diff --git a/x/stake/client/cli/flags.go b/x/stake/client/cli/flags.go index bec76298f3f..29237b6ba82 100644 --- a/x/stake/client/cli/flags.go +++ b/x/stake/client/cli/flags.go @@ -25,6 +25,12 @@ const ( FlagCommissionRate = "commission-rate" FlagCommissionMaxRate = "commission-max-rate" FlagCommissionMaxChangeRate = "commission-max-change-rate" + + FlagGenesisFormat = "genesis-format" + FlagNodeID = "node-id" + FlagIP = "ip" + + FlagOutputDocument = "output-document" // inspired by wget -O ) // common flagsets to add to various functions diff --git a/x/stake/client/cli/tx.go b/x/stake/client/cli/tx.go index 9640c5c0506..e3f1060eeff 100644 --- a/x/stake/client/cli/tx.go +++ b/x/stake/client/cli/tx.go @@ -2,7 +2,6 @@ package cli import ( "fmt" - "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/client/context" "github.com/cosmos/cosmos-sdk/client/utils" @@ -87,7 +86,15 @@ func GetCmdCreateValidator(cdc *codec.Codec) *cobra.Command { ) } - if cliCtx.GenerateOnly { + if viper.GetBool(FlagGenesisFormat) { + ip := viper.GetString(FlagIP) + nodeID := viper.GetString(FlagNodeID) + if nodeID != "" && ip != "" { + txBldr = txBldr.WithMemo(fmt.Sprintf("%s@%s:26656", nodeID, ip)) + } + } + + if viper.GetBool(FlagGenesisFormat) || cliCtx.GenerateOnly { return utils.PrintUnsignedStdTx(txBldr, cliCtx, []sdk.Msg{msg}, true) } @@ -101,6 +108,10 @@ func GetCmdCreateValidator(cdc *codec.Codec) *cobra.Command { cmd.Flags().AddFlagSet(fsDescriptionCreate) cmd.Flags().AddFlagSet(fsCommissionCreate) cmd.Flags().AddFlagSet(fsDelegator) + cmd.Flags().Bool(FlagGenesisFormat, false, "Export the transaction in gen-tx format; it implies --generate-only") + cmd.Flags().String(FlagIP, "", fmt.Sprintf("Node's public IP. It takes effect only when used in combination with --%s", FlagGenesisFormat)) + cmd.Flags().String(FlagNodeID, "", "Node's ID") + cmd.MarkFlagRequired(client.FlagFrom) return cmd }