diff --git a/CHANGELOG.md b/CHANGELOG.md index 076129dbc1..539c997355 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,10 @@ FEATURES: * MountStoreWithDB without providing a custom store works. * Repo is now lint compliant / GoMetaLinter with tendermint-lint integrated into CI * Better key output, pubkey go-amino hex bytes now output by default +* gaiad init overhaul + * Create genesis transactions with `gaiad init gen-tx` + * New genesis account keys are automatically added to the client keybase (introduce `--client-home` flag) + * Initialize with genesis txs using `--gen-txs` flag BREAKING CHANGES @@ -25,6 +29,7 @@ BREAKING CHANGES * Type as a prefix naming convention applied (ex. BondMsg -> MsgBond) * Removed redundancy in names (ex. stake.StakeKeeper -> stake.Keeper) * Removed SealedAccountMapper +* gaiad init now requires use of `--name` flag BUG FIXES * Gaia now uses stake, ported from github.com/cosmos/gaia @@ -74,7 +79,7 @@ BREAKING CHANGES to allow mounting multiple stores with their own DB until they can share one * [x/staking] Renamed to `simplestake` * [builder] Functions don't take `passphrase` as argument -* [server] GenAppState returns generated seed and address +* [server] GenAppParams returns generated seed and address * [basecoind] `init` command outputs JSON of everything necessary for testnet * [basecoind] `basecoin.db -> data/basecoin.db` * [basecli] `data/keys.db -> keys/keys.db` diff --git a/Makefile b/Makefile index 4381eea82f..855d963570 100644 --- a/Makefile +++ b/Makefile @@ -85,6 +85,9 @@ godocs: test: test_unit # test_cli +test_nocli: + go test `go list ./... | grep -v github.com/cosmos/cosmos-sdk/cmd/gaia/cli_test` + # Must be run in each package seperately for the visualization # Added here for easy reference # coverage: diff --git a/client/keys/utils.go b/client/keys/utils.go index f86389a896..013aa1848b 100644 --- a/client/keys/utils.go +++ b/client/keys/utils.go @@ -24,8 +24,13 @@ var keybase keys.Keybase // initialize a keybase based on the configuration func GetKeyBase() (keys.Keybase, error) { + rootDir := viper.GetString(cli.HomeFlag) + return GetKeyBaseFromDir(rootDir) +} + +// initialize a keybase based on the configuration +func GetKeyBaseFromDir(rootDir string) (keys.Keybase, error) { if keybase == nil { - rootDir := viper.GetString(cli.HomeFlag) db, err := dbm.NewGoLevelDB(KeyDBName, filepath.Join(rootDir, "keys")) if err != nil { return nil, err diff --git a/client/lcd/helpers.go b/client/lcd/helpers.go index a64d44dfa6..367e1a53de 100644 --- a/client/lcd/helpers.go +++ b/client/lcd/helpers.go @@ -7,33 +7,14 @@ import ( "os" "path/filepath" "strings" - "time" cmn "github.com/tendermint/tmlibs/common" cfg "github.com/tendermint/tendermint/config" - ctypes "github.com/tendermint/tendermint/rpc/core/types" - rpcclient "github.com/tendermint/tendermint/rpc/lib/client" ) var globalConfig *cfg.Config -func waitForRPC() { - laddr := GetConfig().RPC.ListenAddress - fmt.Println("LADDR", laddr) - client := rpcclient.NewJSONRPCClient(laddr) - ctypes.RegisterAmino(client.Codec()) - result := new(ctypes.ResultStatus) - for { - _, err := client.Call("status", map[string]interface{}{}, result) - if err == nil { - return - } - fmt.Println("error", err) - time.Sleep(time.Millisecond) - } -} - // f**ing long, but unique for each test func makePathname() string { // get path diff --git a/client/lcd/lcd_test.go b/client/lcd/lcd_test.go index 0b5c6b064b..66a8a4085f 100644 --- a/client/lcd/lcd_test.go +++ b/client/lcd/lcd_test.go @@ -10,7 +10,6 @@ import ( "os" "regexp" "testing" - "time" "github.com/spf13/viper" "github.com/stretchr/testify/assert" @@ -34,6 +33,7 @@ import ( keys "github.com/cosmos/cosmos-sdk/client/keys" bapp "github.com/cosmos/cosmos-sdk/examples/basecoin/app" btypes "github.com/cosmos/cosmos-sdk/examples/basecoin/types" + tests "github.com/cosmos/cosmos-sdk/tests" sdk "github.com/cosmos/cosmos-sdk/types" ) @@ -157,7 +157,7 @@ func TestNodeStatus(t *testing.T) { func TestBlock(t *testing.T) { - waitForHeight(2) + tests.WaitForHeight(2, port) var resultBlock ctypes.ResultBlock @@ -224,7 +224,7 @@ func TestCoinSend(t *testing.T) { // create TX receiveAddr, resultTx := doSend(t, port, seed) - waitForHeight(resultTx.Height + 1) + tests.WaitForHeight(resultTx.Height+1, port) // check if tx was commited assert.Equal(t, uint32(0), resultTx.CheckTx.Code) @@ -253,7 +253,7 @@ func TestIBCTransfer(t *testing.T) { // create TX resultTx := doIBCTransfer(t, port, seed) - waitForHeight(resultTx.Height + 1) + tests.WaitForHeight(resultTx.Height+1, port) // check if tx was commited assert.Equal(t, uint32(0), resultTx.CheckTx.Code) @@ -286,7 +286,7 @@ func TestTxs(t *testing.T) { // create TX _, resultTx := doSend(t, port, seed) - waitForHeight(resultTx.Height + 1) + tests.WaitForHeight(resultTx.Height+1, port) // check if tx is findable res, body := request(t, port, "GET", fmt.Sprintf("/txs/%s", resultTx.Hash), nil) @@ -380,7 +380,7 @@ func startTMAndLCD() (*nm.Node, net.Listener, error) { return nil, nil, err } - waitForStart() + tests.WaitForStart(port) return node, lcd, nil } @@ -407,7 +407,7 @@ func startTM(cfg *tmcfg.Config, logger log.Logger, genDoc *tmtypes.GenesisDoc, p } // wait for rpc - waitForRPC() + tests.WaitForRPC(GetConfig().RPC.ListenAddress) logger.Info("Tendermint running!") return n, err @@ -490,71 +490,3 @@ func doIBCTransfer(t *testing.T, port, seed string) (resultTx ctypes.ResultBroad return resultTx } - -func waitForHeight(height int64) { - for { - var resultBlock ctypes.ResultBlock - - url := fmt.Sprintf("http://localhost:%v%v", port, "/blocks/latest") - res, err := http.Get(url) - if err != nil { - panic(err) - } - - body, err := ioutil.ReadAll(res.Body) - if err != nil { - panic(err) - } - res.Body.Close() - - err = cdc.UnmarshalJSON([]byte(body), &resultBlock) - if err != nil { - fmt.Println("RES", res) - fmt.Println("BODY", string(body)) - panic(err) - } - - if resultBlock.Block.Height >= height { - return - } - time.Sleep(time.Millisecond * 100) - } -} - -// wait for 2 blocks -func waitForStart() { - waitHeight := int64(2) - for { - time.Sleep(time.Second) - - url := fmt.Sprintf("http://localhost:%v%v", port, "/blocks/latest") - res, err := http.Get(url) - if err != nil { - panic(err) - } - - // waiting for server to start ... - if res.StatusCode != http.StatusOK { - res.Body.Close() - continue - } - - body, err := ioutil.ReadAll(res.Body) - if err != nil { - panic(err) - } - res.Body.Close() - - resultBlock := new(ctypes.ResultBlock) - err = cdc.UnmarshalJSON([]byte(body), &resultBlock) - if err != nil { - fmt.Println("RES", res) - fmt.Println("BODY", string(body)) - panic(err) - } - - if resultBlock.Block.Height >= waitHeight { - return - } - } -} diff --git a/client/tx/query.go b/client/tx/query.go index 0583f94f5b..2078b78831 100644 --- a/client/tx/query.go +++ b/client/tx/query.go @@ -8,7 +8,6 @@ import ( "strconv" "github.com/gorilla/mux" - "github.com/pkg/errors" "github.com/spf13/cobra" "github.com/spf13/viper" abci "github.com/tendermint/abci/types" @@ -25,10 +24,8 @@ func QueryTxCmd(cdc *wire.Codec) *cobra.Command { cmd := &cobra.Command{ Use: "tx [hash]", Short: "Matches this txhash over all committed blocks", + Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { - if len(args) != 1 || len(args[0]) == 0 { - return errors.New("You must provide a tx hash") - } // find the key to look up the account hashHexStr := args[0] diff --git a/cmd/gaia/app/app.go b/cmd/gaia/app/app.go index fd981a3b5c..9ece80ea7f 100644 --- a/cmd/gaia/app/app.go +++ b/cmd/gaia/app/app.go @@ -1,7 +1,7 @@ package app import ( - "encoding/json" + "os" abci "github.com/tendermint/abci/types" cmn "github.com/tendermint/tmlibs/common" @@ -21,6 +21,12 @@ const ( appName = "GaiaApp" ) +// default home directories for expected binaries +var ( + DefaultCLIHome = os.ExpandEnv("$HOME/.gaiacli") + DefaultNodeHome = os.ExpandEnv("$HOME/.gaiad") +) + // Extended ABCI application type GaiaApp struct { *bam.BaseApp @@ -130,7 +136,7 @@ func (app *GaiaApp) initChainer(ctx sdk.Context, req abci.RequestInitChain) abci stateJSON := req.AppStateBytes genesisState := new(GenesisState) - err := json.Unmarshal(stateJSON, genesisState) + err := app.cdc.UnmarshalJSON(stateJSON, genesisState) if err != nil { panic(err) // TODO https://github.com/cosmos/cosmos-sdk/issues/468 // return sdk.ErrGenesisParse("").TraceCause(err, "") @@ -147,54 +153,3 @@ func (app *GaiaApp) initChainer(ctx sdk.Context, req abci.RequestInitChain) abci return abci.ResponseInitChain{} } - -//__________________________________________________________ - -// State to Unmarshal -type GenesisState struct { - Accounts []GenesisAccount `json:"accounts"` - StakeData stake.GenesisState `json:"stake"` -} - -// GenesisAccount doesn't need pubkey or sequence -type GenesisAccount struct { - Address sdk.Address `json:"address"` - Coins sdk.Coins `json:"coins"` -} - -func NewGenesisAccount(acc *auth.BaseAccount) GenesisAccount { - return GenesisAccount{ - Address: acc.Address, - Coins: acc.Coins, - } -} - -// convert GenesisAccount to GaiaAccount -func (ga *GenesisAccount) ToAccount() (acc *auth.BaseAccount) { - return &auth.BaseAccount{ - Address: ga.Address, - Coins: ga.Coins.Sort(), - } -} - -// DefaultGenAppState expects two args: an account address -// and a coin denomination, and gives lots of coins to that address. -func DefaultGenAppState(args []string, addr sdk.Address, coinDenom string) (json.RawMessage, error) { - - accAuth := auth.NewBaseAccountWithAddress(addr) - accAuth.Coins = sdk.Coins{{"fermion", 100000}} - acc := NewGenesisAccount(&accAuth) - genaccs := []GenesisAccount{acc} - - genesisState := GenesisState{ - Accounts: genaccs, - StakeData: stake.GetDefaultGenesisState(), - } - - stateBytes, err := json.MarshalIndent(genesisState, "", "\t") - if err != nil { - return nil, err - } - - return stateBytes, nil -} diff --git a/cmd/gaia/app/genesis.go b/cmd/gaia/app/genesis.go new file mode 100644 index 0000000000..513430ec9a --- /dev/null +++ b/cmd/gaia/app/genesis.go @@ -0,0 +1,173 @@ +package app + +import ( + "encoding/json" + "errors" + + "github.com/spf13/pflag" + "github.com/spf13/viper" + crypto "github.com/tendermint/go-crypto" + tmtypes "github.com/tendermint/tendermint/types" + + "github.com/cosmos/cosmos-sdk/server" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/wire" + "github.com/cosmos/cosmos-sdk/x/auth" + "github.com/cosmos/cosmos-sdk/x/stake" +) + +// State to Unmarshal +type GenesisState struct { + Accounts []GenesisAccount `json:"accounts"` + StakeData stake.GenesisState `json:"stake"` +} + +// GenesisAccount doesn't need pubkey or sequence +type GenesisAccount struct { + Address sdk.Address `json:"address"` + Coins sdk.Coins `json:"coins"` +} + +func NewGenesisAccount(acc *auth.BaseAccount) GenesisAccount { + return GenesisAccount{ + Address: acc.Address, + Coins: acc.Coins, + } +} + +// convert GenesisAccount to auth.BaseAccount +func (ga *GenesisAccount) ToAccount() (acc *auth.BaseAccount) { + return &auth.BaseAccount{ + Address: ga.Address, + Coins: ga.Coins.Sort(), + } +} + +var ( + flagName = "name" + flagClientHome = "home-client" + flagOWK = "owk" + + // bonded tokens given to genesis validators/accounts + freeFermionVal = int64(100) + freeFermionsAcc = int64(50) +) + +// get app init parameters for server init command +func GaiaAppInit() server.AppInit { + fsAppGenState := pflag.NewFlagSet("", pflag.ContinueOnError) + + fsAppGenTx := pflag.NewFlagSet("", pflag.ContinueOnError) + fsAppGenTx.String(flagName, "", "validator moniker, if left blank, do not add validator") + fsAppGenTx.String(flagClientHome, DefaultCLIHome, "home directory for the client, used for key generation") + fsAppGenTx.Bool(flagOWK, false, "overwrite the for the accounts created") + + return server.AppInit{ + FlagsAppGenState: fsAppGenState, + FlagsAppGenTx: fsAppGenTx, + AppGenTx: GaiaAppGenTx, + AppGenState: GaiaAppGenState, + } +} + +// simple genesis tx +type GaiaGenTx struct { + Name string `json:"name"` + Address sdk.Address `json:"address"` + PubKey crypto.PubKey `json:"pub_key"` +} + +// Generate a gaia genesis transaction +func GaiaAppGenTx(cdc *wire.Codec, pk crypto.PubKey) ( + appGenTx, cliPrint json.RawMessage, validator tmtypes.GenesisValidator, err error) { + + var addr sdk.Address + var secret string + clientRoot := viper.GetString(flagClientHome) + overwrite := viper.GetBool(flagOWK) + name := viper.GetString(flagName) + addr, secret, err = server.GenerateSaveCoinKey(clientRoot, name, "1234567890", overwrite) + if err != nil { + return + } + + var bz []byte + gaiaGenTx := GaiaGenTx{ + Name: name, + Address: addr, + PubKey: pk, + } + bz, err = wire.MarshalJSONIndent(cdc, gaiaGenTx) + 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: freeFermionVal, + } + return +} + +// Create the core parameters for genesis initialization for gaia +// note that the pubkey input is this machines pubkey +func GaiaAppGenState(cdc *wire.Codec, appGenTxs []json.RawMessage) (appState json.RawMessage, err error) { + + if len(appGenTxs) == 0 { + err = errors.New("must provide at least genesis transaction") + return + } + + // start with the default staking genesis state + stakeData := stake.GetDefaultGenesisState() + + // get genesis flag account information + genaccs := make([]GenesisAccount, len(appGenTxs)) + for i, appGenTx := range appGenTxs { + + var genTx GaiaGenTx + err = cdc.UnmarshalJSON(appGenTx, &genTx) + if err != nil { + return + } + + // create the genesis account, give'm few fermions and a buncha token with there name + accAuth := auth.NewBaseAccountWithAddress(genTx.Address) + accAuth.Coins = sdk.Coins{ + {genTx.Name + "Token", 1000}, + {"fermion", freeFermionsAcc}, + } + acc := NewGenesisAccount(&accAuth) + genaccs[i] = acc + stakeData.Pool.TotalSupply += freeFermionsAcc // increase the supply + + // add the validator + if len(genTx.Name) > 0 { + desc := stake.NewDescription(genTx.Name, "", "", "") + candidate := stake.NewCandidate(genTx.Address, genTx.PubKey, desc) + candidate.Assets = sdk.NewRat(freeFermionVal) + stakeData.Candidates = append(stakeData.Candidates, candidate) + + // pool logic + stakeData.Pool.TotalSupply += freeFermionVal + stakeData.Pool.BondedPool += freeFermionVal + stakeData.Pool.BondedShares = sdk.NewRat(stakeData.Pool.BondedPool) + } + } + + // create the final app state + genesisState := GenesisState{ + Accounts: genaccs, + StakeData: stakeData, + } + appState, err = wire.MarshalJSONIndent(cdc, genesisState) + return +} diff --git a/cmd/gaia/app/genesis_test.go b/cmd/gaia/app/genesis_test.go new file mode 100644 index 0000000000..03ff46e50d --- /dev/null +++ b/cmd/gaia/app/genesis_test.go @@ -0,0 +1,36 @@ +package app + +import ( + "testing" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/auth" + "github.com/stretchr/testify/assert" + crypto "github.com/tendermint/go-crypto" +) + +func TestToAccount(t *testing.T) { + priv := crypto.GenPrivKeyEd25519() + addr := sdk.Address(priv.PubKey().Address()) + authAcc := auth.NewBaseAccountWithAddress(addr) + genAcc := NewGenesisAccount(&authAcc) + assert.Equal(t, authAcc, *genAcc.ToAccount()) +} + +func TestGaiaAppGenTx(t *testing.T) { + cdc := MakeCodec() + _ = cdc + + //TODO test that key overwrite flags work / no overwrites if set off + //TODO test validator created has provided pubkey + //TODO test the account created has the correct pubkey +} + +func TestGaiaAppGenState(t *testing.T) { + cdc := MakeCodec() + _ = cdc + + // TODO test must provide at least genesis transaction + // TODO test with both one and two genesis transactions: + // TODO correct: genesis account created, canididates created, pool token variance +} diff --git a/cmd/gaia/cli_test/cli_test.go b/cmd/gaia/cli_test/cli_test.go index 827c7116b4..01f2e15631 100644 --- a/cmd/gaia/cli_test/cli_test.go +++ b/cmd/gaia/cli_test/cli_test.go @@ -3,7 +3,6 @@ package clitest import ( "encoding/json" "fmt" - "strings" "testing" "time" @@ -14,17 +13,19 @@ import ( "github.com/cosmos/cosmos-sdk/cmd/gaia/app" "github.com/cosmos/cosmos-sdk/server" "github.com/cosmos/cosmos-sdk/tests" + "github.com/cosmos/cosmos-sdk/wire" "github.com/cosmos/cosmos-sdk/x/auth" "github.com/cosmos/cosmos-sdk/x/stake" ) func TestGaiaCLISend(t *testing.T) { - tests.ExecuteT(t, "gaiad unsafe_reset_all", 1) + tests.ExecuteT(t, "gaiad unsafe_reset_all") pass := "1234567890" executeWrite(t, "gaiacli keys delete foo", pass) executeWrite(t, "gaiacli keys delete bar", pass) - masterKey, chainID := executeInit(t, "gaiad init") + chainID := executeInit(t, "gaiad init -o --name=foo") + executeWrite(t, "gaiacli keys add bar", pass) // get a free port, also setup some common flags servAddr := server.FreeTCPAddr(t) @@ -34,14 +35,11 @@ func TestGaiaCLISend(t *testing.T) { cmd, _, _ := tests.GoExecuteT(t, fmt.Sprintf("gaiad start --rpc.laddr=%v", servAddr)) defer cmd.Process.Kill() - executeWrite(t, "gaiacli keys add foo --recover", pass, masterKey) - executeWrite(t, "gaiacli keys add bar", pass) - fooAddr, _ := executeGetAddrPK(t, "gaiacli keys show foo --output=json") barAddr, _ := executeGetAddrPK(t, "gaiacli keys show bar --output=json") fooAcc := executeGetAccount(t, fmt.Sprintf("gaiacli account %v %v", fooAddr, flags)) - assert.Equal(t, int64(100000), fooAcc.GetCoins().AmountOf("fermion")) + assert.Equal(t, int64(50), fooAcc.GetCoins().AmountOf("fermion")) executeWrite(t, fmt.Sprintf("gaiacli send %v --amount=10fermion --to=%v --name=foo", flags, barAddr), pass) time.Sleep(time.Second * 3) // waiting for some blocks to pass @@ -49,15 +47,17 @@ func TestGaiaCLISend(t *testing.T) { barAcc := executeGetAccount(t, fmt.Sprintf("gaiacli account %v %v", barAddr, flags)) assert.Equal(t, int64(10), barAcc.GetCoins().AmountOf("fermion")) fooAcc = executeGetAccount(t, fmt.Sprintf("gaiacli account %v %v", fooAddr, flags)) - assert.Equal(t, int64(99990), fooAcc.GetCoins().AmountOf("fermion")) + assert.Equal(t, int64(40), fooAcc.GetCoins().AmountOf("fermion")) } func TestGaiaCLIDeclareCandidacy(t *testing.T) { - tests.ExecuteT(t, "gaiad unsafe_reset_all", 1) + tests.ExecuteT(t, "gaiad unsafe_reset_all") pass := "1234567890" executeWrite(t, "gaiacli keys delete foo", pass) - masterKey, chainID := executeInit(t, "gaiad init") + executeWrite(t, "gaiacli keys delete bar", pass) + chainID := executeInit(t, "gaiad init -o --name=foo") + executeWrite(t, "gaiacli keys add bar", pass) // get a free port, also setup some common flags servAddr := server.FreeTCPAddr(t) @@ -67,42 +67,48 @@ func TestGaiaCLIDeclareCandidacy(t *testing.T) { cmd, _, _ := tests.GoExecuteT(t, fmt.Sprintf("gaiad start --rpc.laddr=%v", servAddr)) defer cmd.Process.Kill() - executeWrite(t, "gaiacli keys add foo --recover", pass, masterKey) - fooAddr, fooPubKey := executeGetAddrPK(t, "gaiacli keys show foo --output=json") + fooAddr, _ := executeGetAddrPK(t, "gaiacli keys show foo --output=json") + barAddr, barPubKey := executeGetAddrPK(t, "gaiacli keys show bar --output=json") + + executeWrite(t, fmt.Sprintf("gaiacli send %v --amount=10fermion --to=%v --name=foo", flags, barAddr), pass) + time.Sleep(time.Second * 3) // waiting for some blocks to pass + fooAcc := executeGetAccount(t, fmt.Sprintf("gaiacli account %v %v", fooAddr, flags)) - assert.Equal(t, int64(100000), fooAcc.GetCoins().AmountOf("fermion")) + assert.Equal(t, int64(40), fooAcc.GetCoins().AmountOf("fermion")) + barAcc := executeGetAccount(t, fmt.Sprintf("gaiacli account %v %v", barAddr, flags)) + assert.Equal(t, int64(10), barAcc.GetCoins().AmountOf("fermion")) // declare candidacy declStr := fmt.Sprintf("gaiacli declare-candidacy %v", flags) - declStr += fmt.Sprintf(" --name=%v", "foo") - declStr += fmt.Sprintf(" --address-candidate=%v", fooAddr) - declStr += fmt.Sprintf(" --pubkey=%v", fooPubKey) + declStr += fmt.Sprintf(" --name=%v", "bar") + declStr += fmt.Sprintf(" --address-candidate=%v", barAddr) + declStr += fmt.Sprintf(" --pubkey=%v", barPubKey) declStr += fmt.Sprintf(" --amount=%v", "3fermion") - declStr += fmt.Sprintf(" --moniker=%v", "foo-vally") + declStr += fmt.Sprintf(" --moniker=%v", "bar-vally") fmt.Printf("debug declStr: %v\n", declStr) executeWrite(t, declStr, pass) - time.Sleep(time.Second * 3) // waiting for some blocks to pass - fooAcc = executeGetAccount(t, fmt.Sprintf("gaiacli account %v %v", fooAddr, flags)) - assert.Equal(t, int64(99997), fooAcc.GetCoins().AmountOf("fermion")) - candidate := executeGetCandidate(t, fmt.Sprintf("gaiacli candidate %v --address-candidate=%v", flags, fooAddr)) - assert.Equal(t, candidate.Address.String(), fooAddr) + time.Sleep(time.Second) // waiting for some blocks to pass + barAcc = executeGetAccount(t, fmt.Sprintf("gaiacli account %v %v", barAddr, flags)) + assert.Equal(t, int64(7), barAcc.GetCoins().AmountOf("fermion")) + candidate := executeGetCandidate(t, fmt.Sprintf("gaiacli candidate %v --address-candidate=%v", flags, barAddr)) + assert.Equal(t, candidate.Address.String(), barAddr) assert.Equal(t, int64(3), candidate.Assets.Evaluate()) // TODO timeout issues if not connected to the internet // unbond a single share - unbondStr := fmt.Sprintf("gaiacli unbond %v", flags) - unbondStr += fmt.Sprintf(" --name=%v", "foo") - unbondStr += fmt.Sprintf(" --address-candidate=%v", fooAddr) - unbondStr += fmt.Sprintf(" --address-delegator=%v", fooAddr) - unbondStr += fmt.Sprintf(" --shares=%v", "1") - unbondStr += fmt.Sprintf(" --sequence=%v", "1") - fmt.Printf("debug unbondStr: %v\n", unbondStr) - executeWrite(t, unbondStr, pass) - time.Sleep(time.Second * 3) // waiting for some blocks to pass - fooAcc = executeGetAccount(t, fmt.Sprintf("gaiacli account %v %v", fooAddr, flags)) - assert.Equal(t, int64(99998), fooAcc.GetCoins().AmountOf("fermion")) - candidate = executeGetCandidate(t, fmt.Sprintf("gaiacli candidate %v --address-candidate=%v", flags, fooAddr)) - assert.Equal(t, int64(2), candidate.Assets.Evaluate()) + //unbondStr := fmt.Sprintf("gaiacli unbond %v", flags) + //unbondStr += fmt.Sprintf(" --name=%v", "bar") + //unbondStr += fmt.Sprintf(" --address-candidate=%v", barAddr) + //unbondStr += fmt.Sprintf(" --address-delegator=%v", barAddr) + //unbondStr += fmt.Sprintf(" --shares=%v", "1") + //unbondStr += fmt.Sprintf(" --sequence=%v", "1") + //fmt.Printf("debug unbondStr: %v\n", unbondStr) + //executeWrite(t, unbondStr, pass) + //time.Sleep(time.Second * 3) // waiting for some blocks to pass + //barAcc = executeGetAccount(t, fmt.Sprintf("gaiacli account %v %v", barAddr, flags)) + //assert.Equal(t, int64(99998), barAcc.GetCoins().AmountOf("fermion")) + //candidate = executeGetCandidate(t, fmt.Sprintf("gaiacli candidate %v --address-candidate=%v", flags, barAddr)) + //assert.Equal(t, int64(2), candidate.Assets.Evaluate()) } func executeWrite(t *testing.T, cmdStr string, writes ...string) { @@ -112,6 +118,7 @@ func executeWrite(t *testing.T, cmdStr string, writes ...string) { _, err := wc.Write([]byte(write + "\n")) require.NoError(t, err) } + fmt.Printf("debug waiting cmdStr: %v\n", cmdStr) cmd.Wait() } @@ -122,6 +129,7 @@ func executeWritePrint(t *testing.T, cmdStr string, writes ...string) { _, err := wc.Write([]byte(write + "\n")) require.NoError(t, err) } + fmt.Printf("debug waiting cmdStr: %v\n", cmdStr) cmd.Wait() bz := make([]byte, 100000) @@ -129,41 +137,42 @@ func executeWritePrint(t *testing.T, cmdStr string, writes ...string) { fmt.Printf("debug read: %v\n", string(bz)) } -func executeInit(t *testing.T, cmdStr string) (masterKey, chainID string) { - out := tests.ExecuteT(t, cmdStr, 1) - outCut := "{" + strings.SplitN(out, "{", 2)[1] // weird I'm sorry +func executeInit(t *testing.T, cmdStr string) (chainID string) { + out := tests.ExecuteT(t, cmdStr) var initRes map[string]json.RawMessage - err := json.Unmarshal([]byte(outCut), &initRes) - require.NoError(t, err) - err = json.Unmarshal(initRes["secret"], &masterKey) + err := json.Unmarshal([]byte(out), &initRes) require.NoError(t, err) + err = json.Unmarshal(initRes["chain_id"], &chainID) require.NoError(t, err) + return } func executeGetAddrPK(t *testing.T, cmdStr string) (addr, pubKey string) { - out := tests.ExecuteT(t, cmdStr, 2) + out := tests.ExecuteT(t, cmdStr) var ko keys.KeyOutput keys.UnmarshalJSON([]byte(out), &ko) return ko.Address, ko.PubKey } func executeGetAccount(t *testing.T, cmdStr string) auth.BaseAccount { - out := tests.ExecuteT(t, cmdStr, 2) + 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) value := initRes["value"] var acc auth.BaseAccount - _ = json.Unmarshal(value, &acc) //XXX pubkey can't be decoded go amino issue + cdc := wire.NewCodec() + wire.RegisterCrypto(cdc) + err = cdc.UnmarshalJSON(value, &acc) require.NoError(t, err, "value %v, err %v", string(value), err) return acc } func executeGetCandidate(t *testing.T, cmdStr string) stake.Candidate { - out := tests.ExecuteT(t, cmdStr, 2) + out := tests.ExecuteT(t, cmdStr) var candidate stake.Candidate cdc := app.MakeCodec() err := cdc.UnmarshalJSON([]byte(out), &candidate) diff --git a/cmd/gaia/cmd/gaiacli/main.go b/cmd/gaia/cmd/gaiacli/main.go index 8efb9f4dd8..ab0a98c695 100644 --- a/cmd/gaia/cmd/gaiacli/main.go +++ b/cmd/gaia/cmd/gaiacli/main.go @@ -1,8 +1,6 @@ package main import ( - "os" - "github.com/spf13/cobra" "github.com/tendermint/tmlibs/cli" @@ -73,6 +71,6 @@ func main() { ) // prepare and add flags - executor := cli.PrepareMainCmd(rootCmd, "GA", os.ExpandEnv("$HOME/.gaiacli")) + executor := cli.PrepareMainCmd(rootCmd, "GA", app.DefaultCLIHome) executor.Execute() } diff --git a/cmd/gaia/cmd/gaiad/main.go b/cmd/gaia/cmd/gaiad/main.go index 199a061524..0417671a2f 100644 --- a/cmd/gaia/cmd/gaiad/main.go +++ b/cmd/gaia/cmd/gaiad/main.go @@ -1,7 +1,6 @@ package main import ( - "os" "path/filepath" "github.com/spf13/cobra" @@ -15,15 +14,21 @@ import ( "github.com/cosmos/cosmos-sdk/server" ) -// rootCmd is the entry point for this binary -var ( - context = server.NewDefaultContext() - rootCmd = &cobra.Command{ +func main() { + cdc := app.MakeCodec() + ctx := server.NewDefaultContext() + rootCmd := &cobra.Command{ Use: "gaiad", Short: "Gaia Daemon (server)", - PersistentPreRunE: server.PersistentPreRunEFn(context), + PersistentPreRunE: server.PersistentPreRunEFn(ctx), } -) + + server.AddCommands(ctx, cdc, rootCmd, app.GaiaAppInit(), generateApp) + + // prepare and add flags + executor := cli.PrepareBaseCmd(rootCmd, "GA", app.DefaultNodeHome) + executor.Execute() +} func generateApp(rootDir string, logger log.Logger) (abci.Application, error) { dataDir := filepath.Join(rootDir, "data") @@ -34,12 +39,3 @@ func generateApp(rootDir string, logger log.Logger) (abci.Application, error) { bapp := app.NewGaiaApp(logger, db) return bapp, nil } - -func main() { - server.AddCommands(rootCmd, app.DefaultGenAppState, generateApp, context) - - // prepare and add flags - rootDir := os.ExpandEnv("$HOME/.gaiad") - executor := cli.PrepareBaseCmd(rootCmd, "GA", rootDir) - executor.Execute() -} diff --git a/examples/basecoin/app/app.go b/examples/basecoin/app/app.go index b02f216695..ead9c78e33 100644 --- a/examples/basecoin/app/app.go +++ b/examples/basecoin/app/app.go @@ -1,8 +1,6 @@ package app import ( - "encoding/json" - abci "github.com/tendermint/abci/types" cmn "github.com/tendermint/tmlibs/common" dbm "github.com/tendermint/tmlibs/db" @@ -133,7 +131,7 @@ func (app *BasecoinApp) initChainer(ctx sdk.Context, req abci.RequestInitChain) stateJSON := req.AppStateBytes genesisState := new(types.GenesisState) - err := json.Unmarshal(stateJSON, genesisState) + err := app.cdc.UnmarshalJSON(stateJSON, genesisState) if err != nil { panic(err) // TODO https://github.com/cosmos/cosmos-sdk/issues/468 // return sdk.ErrGenesisParse("").TraceCause(err, "") diff --git a/examples/basecoin/cmd/basecoind/main.go b/examples/basecoin/cmd/basecoind/main.go index 37eb7d58f4..8d14821c6a 100644 --- a/examples/basecoin/cmd/basecoind/main.go +++ b/examples/basecoin/cmd/basecoind/main.go @@ -15,15 +15,23 @@ import ( "github.com/cosmos/cosmos-sdk/server" ) -// rootCmd is the entry point for this binary -var ( - context = server.NewDefaultContext() - rootCmd = &cobra.Command{ +func main() { + cdc := app.MakeCodec() + ctx := server.NewDefaultContext() + + rootCmd := &cobra.Command{ Use: "basecoind", Short: "Basecoin Daemon (server)", - PersistentPreRunE: server.PersistentPreRunEFn(context), + PersistentPreRunE: server.PersistentPreRunEFn(ctx), } -) + + server.AddCommands(ctx, cdc, rootCmd, server.DefaultAppInit, generateApp) + + // prepare and add flags + rootDir := os.ExpandEnv("$HOME/.basecoind") + executor := cli.PrepareBaseCmd(rootCmd, "BC", rootDir) + executor.Execute() +} func generateApp(rootDir string, logger log.Logger) (abci.Application, error) { dataDir := filepath.Join(rootDir, "data") @@ -34,12 +42,3 @@ func generateApp(rootDir string, logger log.Logger) (abci.Application, error) { bapp := app.NewBasecoinApp(logger, db) return bapp, nil } - -func main() { - server.AddCommands(rootCmd, server.DefaultGenAppState, generateApp, context) - - // prepare and add flags - rootDir := os.ExpandEnv("$HOME/.basecoind") - executor := cli.PrepareBaseCmd(rootCmd, "BC", rootDir) - executor.Execute() -} diff --git a/examples/democoin/app/app.go b/examples/democoin/app/app.go index b70f51b5cc..8266b2b9b9 100644 --- a/examples/democoin/app/app.go +++ b/examples/democoin/app/app.go @@ -1,8 +1,6 @@ package app import ( - "encoding/json" - abci "github.com/tendermint/abci/types" cmn "github.com/tendermint/tmlibs/common" dbm "github.com/tendermint/tmlibs/db" @@ -147,7 +145,7 @@ func (app *DemocoinApp) initChainerFn(coolKeeper cool.Keeper, powKeeper pow.Keep stateJSON := req.AppStateBytes genesisState := new(types.GenesisState) - err := json.Unmarshal(stateJSON, genesisState) + err := app.cdc.UnmarshalJSON(stateJSON, genesisState) if err != nil { panic(err) // TODO https://github.com/cosmos/cosmos-sdk/issues/468 // return sdk.ErrGenesisParse("").TraceCause(err, "") diff --git a/examples/democoin/cmd/democoind/main.go b/examples/democoin/cmd/democoind/main.go index 8f8bb5a905..afca80bce3 100644 --- a/examples/democoin/cmd/democoind/main.go +++ b/examples/democoin/cmd/democoind/main.go @@ -14,36 +14,27 @@ import ( "github.com/cosmos/cosmos-sdk/examples/democoin/app" "github.com/cosmos/cosmos-sdk/server" - sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/wire" ) -// rootCmd is the entry point for this binary -var ( - context = server.NewDefaultContext() - rootCmd = &cobra.Command{ - Use: "democoind", - Short: "Democoin Daemon (server)", - PersistentPreRunE: server.PersistentPreRunEFn(context), - } -) +// init parameters +var CoolAppInit = server.AppInit{ + AppGenState: CoolAppGenState, + AppGenTx: server.SimpleAppGenTx, +} -// defaultAppState sets up the app_state for the -// default genesis file -func defaultAppState(args []string, addr sdk.Address, coinDenom string) (json.RawMessage, error) { - baseJSON, err := server.DefaultGenAppState(args, addr, coinDenom) +// coolGenAppParams sets up the app_state and appends the cool app state +func CoolAppGenState(cdc *wire.Codec, appGenTxs []json.RawMessage) (appState json.RawMessage, err error) { + appState, err = server.SimpleAppGenState(cdc, appGenTxs) if err != nil { - return nil, err + return } - var jsonMap map[string]json.RawMessage - err = json.Unmarshal(baseJSON, &jsonMap) - if err != nil { - return nil, err - } - jsonMap["cool"] = json.RawMessage(`{ + key := "cool" + value := json.RawMessage(`{ "trend": "ice-cold" }`) - bz, err := json.Marshal(jsonMap) - return json.RawMessage(bz), err + appState, err = server.AppendJSON(cdc, appState, key, value) + return } func generateApp(rootDir string, logger log.Logger) (abci.Application, error) { @@ -56,7 +47,16 @@ func generateApp(rootDir string, logger log.Logger) (abci.Application, error) { } func main() { - server.AddCommands(rootCmd, defaultAppState, generateApp, context) + cdc := app.MakeCodec() + ctx := server.NewDefaultContext() + + rootCmd := &cobra.Command{ + Use: "democoind", + Short: "Democoin Daemon (server)", + PersistentPreRunE: server.PersistentPreRunEFn(ctx), + } + + server.AddCommands(ctx, cdc, rootCmd, CoolAppInit, generateApp) // prepare and add flags rootDir := os.ExpandEnv("$HOME/.democoind") diff --git a/mock/app.go b/mock/app.go index 631cc3c314..84f762db0e 100644 --- a/mock/app.go +++ b/mock/app.go @@ -6,16 +6,18 @@ import ( "path/filepath" abci "github.com/tendermint/abci/types" + crypto "github.com/tendermint/go-crypto" + tmtypes "github.com/tendermint/tendermint/types" dbm "github.com/tendermint/tmlibs/db" "github.com/tendermint/tmlibs/log" bam "github.com/cosmos/cosmos-sdk/baseapp" sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/wire" ) -// NewApp creates a simple mock kvstore app for testing. -// It should work similar to a real app. -// Make sure rootDir is empty before running the test, +// NewApp creates a simple mock kvstore app for testing. It should work +// similar to a real app. Make sure rootDir is empty before running the test, // in order to guarantee consistent results func NewApp(rootDir string, logger log.Logger) (abci.Application, error) { db, err := dbm.NewGoLevelDB("mock", filepath.Join(rootDir, "data")) @@ -103,11 +105,10 @@ func InitChainer(key sdk.StoreKey) func(sdk.Context, abci.RequestInitChain) abci } } -// GenInitOptions can be passed into InitCmd, -// returns a static string of a few key-values that can be parsed -// by InitChainer -func GenInitOptions(args []string, addr sdk.Address, coinDenom string) (json.RawMessage, error) { - opts := []byte(`{ +// AppGenState can be passed into InitCmd, returns a static string of a few +// key-values that can be parsed by InitChainer +func AppGenState(_ *wire.Codec, _ []json.RawMessage) (appState json.RawMessage, err error) { + appState = json.RawMessage(`{ "values": [ { "key": "hello", @@ -119,5 +120,16 @@ func GenInitOptions(args []string, addr sdk.Address, coinDenom string) (json.Raw } ] }`) - return opts, nil + return +} + +// Return a validator, not much else +func AppGenTx(_ *wire.Codec, pk crypto.PubKey) ( + appGenTx, cliPrint json.RawMessage, validator tmtypes.GenesisValidator, err error) { + + validator = tmtypes.GenesisValidator{ + PubKey: pk, + Power: 10, + } + return } diff --git a/mock/app_test.go b/mock/app_test.go index 18449631c6..7c84f9a1d1 100644 --- a/mock/app_test.go +++ b/mock/app_test.go @@ -21,9 +21,13 @@ func TestInitApp(t *testing.T) { require.NoError(t, err) // initialize it future-way - opts, err := GenInitOptions(nil, nil, "") + appState, err := AppGenState(nil, nil) require.NoError(t, err) - req := abci.RequestInitChain{AppStateBytes: opts} + + //TODO test validators in the init chain? + req := abci.RequestInitChain{ + AppStateBytes: appState, + } app.InitChain(req) app.Commit() diff --git a/server/init.go b/server/init.go index a68566cdf0..af735f0927 100644 --- a/server/init.go +++ b/server/init.go @@ -4,218 +4,388 @@ import ( "encoding/json" "fmt" "io/ioutil" + "os" + "path" + "path/filepath" - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/wire" + "github.com/pkg/errors" "github.com/spf13/cobra" + "github.com/spf13/pflag" + "github.com/spf13/viper" + crypto "github.com/tendermint/go-crypto" "github.com/tendermint/go-crypto/keys" "github.com/tendermint/go-crypto/keys/words" cfg "github.com/tendermint/tendermint/config" "github.com/tendermint/tendermint/p2p" tmtypes "github.com/tendermint/tendermint/types" pvm "github.com/tendermint/tendermint/types/priv_validator" + tmcli "github.com/tendermint/tmlibs/cli" cmn "github.com/tendermint/tmlibs/common" dbm "github.com/tendermint/tmlibs/db" + + clkeys "github.com/cosmos/cosmos-sdk/client/keys" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/wire" ) -// testnetInformation contains the info necessary -// to setup a testnet including this account and validator. -type testnetInformation struct { - Secret string `json:"secret"` - - ChainID string `json:"chain_id"` - Account string `json:"account"` +// genesis piece structure for creating combined genesis +type GenesisTx struct { + NodeID string `json:"node_id"` + IP string `json:"ip"` Validator tmtypes.GenesisValidator `json:"validator"` - NodeID p2p.ID `json:"node_id"` + AppGenTx json.RawMessage `json:"app_gen_tx"` } -type initCmd struct { - genAppState GenAppState - context *Context -} +var ( + flagOverwrite = "overwrite" + flagGenTxs = "gen-txs" + flagIP = "ip" + flagChainID = "chain-id" +) -// InitCmd will initialize all files for tendermint, -// along with proper app_state. -// The application can pass in a function to generate -// proper state. And may want to use GenerateCoinKey -// to create default account(s). -func InitCmd(gen GenAppState, ctx *Context) *cobra.Command { - cmd := initCmd{ - genAppState: gen, - context: ctx, +// get cmd to initialize all files for tendermint and application +func GenTxCmd(ctx *Context, cdc *wire.Codec, appInit AppInit) *cobra.Command { + cmd := &cobra.Command{ + Use: "gen-tx", + Short: "Create genesis transaction file (under [--home]/gentx-[nodeID].json)", + Args: cobra.NoArgs, + RunE: func(_ *cobra.Command, args []string) error { + + config := ctx.Config + nodeKey, err := p2p.LoadOrGenNodeKey(config.NodeKeyFile()) + if err != nil { + return err + } + nodeID := string(nodeKey.ID()) + pubKey := readOrCreatePrivValidator(config) + + appGenTx, cliPrint, validator, err := appInit.AppGenTx(cdc, pubKey) + if err != nil { + return err + } + + ip := viper.GetString(flagIP) + if len(ip) == 0 { + ip, err = externalIP() + if err != nil { + return err + } + } + + tx := GenesisTx{ + NodeID: nodeID, + IP: ip, + Validator: validator, + AppGenTx: appGenTx, + } + bz, err := wire.MarshalJSONIndent(cdc, tx) + if err != nil { + return err + } + genTxFile := json.RawMessage(bz) + name := fmt.Sprintf("gentx-%v.json", nodeID) + file := filepath.Join(viper.GetString(tmcli.HomeFlag), name) + err = cmn.WriteFile(file, bz, 0644) + if err != nil { + return err + } + + // print out some key information + toPrint := struct { + AppMessage json.RawMessage `json:"app_message"` + GenTxFile json.RawMessage `json:"gen_tx_file"` + }{ + cliPrint, + genTxFile, + } + out, err := wire.MarshalJSONIndent(cdc, toPrint) + if err != nil { + return err + } + fmt.Println(string(out)) + return nil + }, } - cobraCmd := cobra.Command{ + cmd.Flags().String(flagIP, "", "external facing IP to use if left blank IP will be retrieved from this machine") + cmd.Flags().AddFlagSet(appInit.FlagsAppGenTx) + return cmd +} + +// get cmd to initialize all files for tendermint and application +func InitCmd(ctx *Context, cdc *wire.Codec, appInit AppInit) *cobra.Command { + cmd := &cobra.Command{ Use: "init", - Short: "Initialize genesis files", - RunE: cmd.run, + Short: "Initialize genesis config, priv-validator file, and p2p-node file", + Args: cobra.NoArgs, + RunE: func(_ *cobra.Command, _ []string) error { + + config := ctx.Config + nodeKey, err := p2p.LoadOrGenNodeKey(config.NodeKeyFile()) + if err != nil { + return err + } + nodeID := string(nodeKey.ID()) + pubKey := readOrCreatePrivValidator(config) + + chainID := viper.GetString(flagChainID) + if chainID == "" { + chainID = cmn.Fmt("test-chain-%v", cmn.RandStr(6)) + } + + genFile := config.GenesisFile() + if !viper.GetBool(flagOverwrite) && cmn.FileExists(genFile) { + return fmt.Errorf("genesis.json file already exists: %v", genFile) + } + + // process genesis transactions, or otherwise create one for defaults + var appMessage json.RawMessage + var appGenTxs []json.RawMessage + var validators []tmtypes.GenesisValidator + var persistentPeers string + + genTxsDir := viper.GetString(flagGenTxs) + if genTxsDir != "" { + validators, appGenTxs, persistentPeers, err = processGenTxs(genTxsDir, cdc, appInit) + if err != nil { + return err + } + config.P2P.PersistentPeers = persistentPeers + configFilePath := filepath.Join(viper.GetString(tmcli.HomeFlag), "config", "config.toml") //TODO this is annoying should be easier to get + cfg.WriteConfigFile(configFilePath, config) + } else { + appGenTx, am, validator, err := appInit.AppGenTx(cdc, pubKey) + appMessage = am + if err != nil { + return err + } + validators = []tmtypes.GenesisValidator{validator} + appGenTxs = []json.RawMessage{appGenTx} + } + + appState, err := appInit.AppGenState(cdc, appGenTxs) + if err != nil { + return err + } + + err = writeGenesisFile(cdc, genFile, chainID, validators, appState) + if err != nil { + return err + } + + // print out some key information + toPrint := struct { + ChainID string `json:"chain_id"` + NodeID string `json:"node_id"` + AppMessage json.RawMessage `json:"app_message"` + }{ + chainID, + nodeID, + appMessage, + } + out, err := wire.MarshalJSONIndent(cdc, toPrint) + if err != nil { + return err + } + fmt.Println(string(out)) + + return nil + }, } - return &cobraCmd + cmd.Flags().BoolP(flagOverwrite, "o", false, "overwrite the genesis.json file") + cmd.Flags().String(flagChainID, "", "genesis file chain-id, if left blank will be randomly created") + cmd.Flags().String(flagGenTxs, "", "directory containing the genesis transactions") + cmd.Flags().AddFlagSet(appInit.FlagsAppGenState) + cmd.Flags().AddFlagSet(appInit.FlagsAppGenTx) // need to add this flagset for when no GenTx's provided + cmd.AddCommand(GenTxCmd(ctx, cdc, appInit)) + return cmd } -func (c initCmd) run(cmd *cobra.Command, args []string) error { - // Store testnet information as we go - var testnetInfo testnetInformation +// append a genesis-piece +func processGenTxs(genTxsDir string, cdc *wire.Codec, appInit AppInit) ( + validators []tmtypes.GenesisValidator, appGenTxs []json.RawMessage, persistentPeers string, err error) { - // Run the basic tendermint initialization, - // set up a default genesis with no app_options - config := c.context.Config - err := c.initTendermintFiles(config, &testnetInfo) + // XXX sort the files by contents just incase people renamed their files + var fos []os.FileInfo + fos, err = ioutil.ReadDir(genTxsDir) if err != nil { - return err + return + } + for _, fo := range fos { + filename := path.Join(genTxsDir, fo.Name()) + if !fo.IsDir() && (path.Ext(filename) != ".json") { + return + } + + // get the genTx + var bz []byte + bz, err = ioutil.ReadFile(filename) + if err != nil { + return + } + var genTx GenesisTx + err = cdc.UnmarshalJSON(bz, &genTx) + if err != nil { + return + } + + // 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:46656", comma, genTx.NodeID, genTx.IP) } - // no app_options, leave like tendermint - if c.genAppState == nil { - return nil - } - - // generate secret and address - addr, secret, err := GenerateCoinKey() - if err != nil { - return err - } - - var defaultDenom = "mycoin" - - // Now, we want to add the custom app_state - appState, err := c.genAppState(args, addr, defaultDenom) - if err != nil { - return err - } - - testnetInfo.Secret = secret - testnetInfo.Account = addr.String() - - // And add them to the genesis file - genFile := config.GenesisFile() - if err := addGenesisState(genFile, appState); err != nil { - return err - } - - nodeKey, err := p2p.LoadOrGenNodeKey(config.NodeKeyFile()) - if err != nil { - return err - } - testnetInfo.NodeID = nodeKey.ID() - out, err := wire.MarshalJSONIndent(cdc, testnetInfo) - if err != nil { - return err - } - fmt.Println(string(out)) - return nil + return } -// This was copied from tendermint/cmd/tendermint/commands/init.go -// so we could pass in the config and the logger. -func (c initCmd) initTendermintFiles(config *cfg.Config, info *testnetInformation) error { +//________________________________________________________________________________________ + +// read of create the private key file for this config +func readOrCreatePrivValidator(tmConfig *cfg.Config) crypto.PubKey { // private validator - privValFile := config.PrivValidatorFile() + privValFile := tmConfig.PrivValidatorFile() var privValidator *pvm.FilePV if cmn.FileExists(privValFile) { privValidator = pvm.LoadFilePV(privValFile) - c.context.Logger.Info("Found private validator", "path", privValFile) } else { privValidator = pvm.GenFilePV(privValFile) privValidator.Save() - c.context.Logger.Info("Generated private validator", "path", privValFile) } - - // genesis file - genFile := config.GenesisFile() - if cmn.FileExists(genFile) { - c.context.Logger.Info("Found genesis file", "path", genFile) - } else { - genDoc := tmtypes.GenesisDoc{ - ChainID: cmn.Fmt("test-chain-%v", cmn.RandStr(6)), - } - genDoc.Validators = []tmtypes.GenesisValidator{{ - PubKey: privValidator.GetPubKey(), - Power: 10, - }} - - if err := genDoc.SaveAs(genFile); err != nil { - return err - } - c.context.Logger.Info("Generated genesis file", "path", genFile) - } - - // reload the config file and find our validator info - loadedDoc, err := tmtypes.GenesisDocFromFile(genFile) - if err != nil { - return err - } - for _, validator := range loadedDoc.Validators { - if validator.PubKey == privValidator.GetPubKey() { - info.Validator = validator - } - } - info.ChainID = loadedDoc.ChainID - - return nil + return privValidator.GetPubKey() } -//------------------------------------------------------------------- - -// GenAppState takes the command line args, as well -// as an address and coin denomination. -// It returns a default app_state to be included in -// in the genesis file. -// This is application-specific -type GenAppState func(args []string, addr sdk.Address, coinDenom string) (json.RawMessage, error) - -// DefaultGenAppState expects two args: an account address -// and a coin denomination, and gives lots of coins to that address. -func DefaultGenAppState(args []string, addr sdk.Address, coinDenom string) (json.RawMessage, error) { - opts := fmt.Sprintf(`{ - "accounts": [{ - "address": "%s", - "coins": [ - { - "denom": "%s", - "amount": 9007199254740992 - } - ] - }] - }`, addr.String(), coinDenom) - return json.RawMessage(opts), nil +// create the genesis file +func writeGenesisFile(cdc *wire.Codec, genesisFile, chainID string, validators []tmtypes.GenesisValidator, appState json.RawMessage) error { + genDoc := tmtypes.GenesisDoc{ + ChainID: chainID, + Validators: validators, + } + if err := genDoc.ValidateAndComplete(); err != nil { + return err + } + if err := genDoc.SaveAs(genesisFile); err != nil { + return err + } + return addAppStateToGenesis(cdc, genesisFile, appState) } -//------------------------------------------------------------------- - -// GenesisDoc involves some tendermint-specific structures we don't -// want to parse, so we just grab it into a raw object format, -// so we can add one line. -type GenesisDoc map[string]json.RawMessage - -func addGenesisState(filename string, appState json.RawMessage) error { - bz, err := ioutil.ReadFile(filename) +// Add one line to the genesis file +func addAppStateToGenesis(cdc *wire.Codec, genesisConfigPath string, appState json.RawMessage) error { + bz, err := ioutil.ReadFile(genesisConfigPath) if err != nil { return err } - - var doc GenesisDoc - err = cdc.UnmarshalJSON(bz, &doc) + out, err := AppendJSON(cdc, bz, "app_state", appState) if err != nil { return err } - - doc["app_state"] = appState - out, err := wire.MarshalJSONIndent(cdc, doc) - if err != nil { - return err - } - - return ioutil.WriteFile(filename, out, 0600) + return ioutil.WriteFile(genesisConfigPath, out, 0600) } -//------------------------------------------------------------------- +//_____________________________________________________________________ -// GenerateCoinKey returns the address of a public key, -// along with the secret phrase to recover the private key. -// You can give coins to this address and return the recovery -// phrase to the user to access them. +// 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 *wire.Codec, pk crypto.PubKey) ( + 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 *wire.Codec, appGenTxs []json.RawMessage) (appState json.RawMessage, err error) +} + +//_____________________________________________________________________ + +// simple default application init +var DefaultAppInit = AppInit{ + AppGenTx: SimpleAppGenTx, + AppGenState: SimpleAppGenState, +} + +// simple genesis tx +type SimpleGenTx struct { + Addr sdk.Address `json:"addr"` +} + +// Generate a genesis transaction +func SimpleAppGenTx(cdc *wire.Codec, pk crypto.PubKey) ( + appGenTx, cliPrint json.RawMessage, validator tmtypes.GenesisValidator, err error) { + + var addr sdk.Address + var secret string + addr, secret, err = GenerateCoinKey() + if err != nil { + return + } + + var bz []byte + simpleGenTx := SimpleGenTx{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, + } + return +} + +// create the genesis app state +func SimpleAppGenState(cdc *wire.Codec, appGenTxs []json.RawMessage) (appState json.RawMessage, err error) { + + if len(appGenTxs) != 1 { + err = errors.New("must provide a single genesis transaction") + return + } + + var genTx SimpleGenTx + err = cdc.UnmarshalJSON(appGenTxs[0], &genTx) + if err != nil { + return + } + + appState = json.RawMessage(fmt.Sprintf(`{ + "accounts": [{ + "address": "%s", + "coins": [ + { + "denom": "mycoin", + "amount": 9007199254740992 + } + ] + }] +}`, genTx.Addr.String())) + return +} + +//___________________________________________________________________________________________ + +// GenerateCoinKey returns the address of a public key, along with the secret +// phrase to recover the private key. func GenerateCoinKey() (sdk.Address, string, error) { + // construct an in-memory key store codec, err := words.LoadCodec("english") if err != nil { @@ -231,7 +401,33 @@ func GenerateCoinKey() (sdk.Address, string, error) { if err != nil { return nil, "", err } - + addr := info.PubKey.Address() + return addr, secret, nil +} + +// GenerateSaveCoinKey returns the address of a public key, along with the secret +// phrase to recover the private key. +func GenerateSaveCoinKey(clientRoot, keyName, keyPass string, overwrite bool) (sdk.Address, string, error) { + + // get the keystore from the client + keybase, err := clkeys.GetKeyBaseFromDir(clientRoot) + if err != nil { + return nil, "", err + } + + // ensure no overwrite + if !overwrite { + _, err := keybase.Get(keyName) + if err == nil { + return nil, "", errors.New("key already exists, overwrite is disabled") + } + } + + // generate a private key, with recovery phrase + info, secret, err := keybase.Create(keyName, keyPass, keys.AlgoEd25519) + if err != nil { + return nil, "", err + } addr := info.PubKey.Address() return addr, secret, nil } diff --git a/server/init_test.go b/server/init_test.go index 19e6695193..eca5295052 100644 --- a/server/init_test.go +++ b/server/init_test.go @@ -8,17 +8,36 @@ import ( "github.com/tendermint/tmlibs/log" "github.com/cosmos/cosmos-sdk/mock" + "github.com/cosmos/cosmos-sdk/wire" tcmd "github.com/tendermint/tendermint/cmd/tendermint/commands" ) -func TestInit(t *testing.T) { +// TODO update +func TestInitCmd(t *testing.T) { defer setupViper(t)() logger := log.NewNopLogger() cfg, err := tcmd.ParseConfig() require.Nil(t, err) ctx := NewContext(cfg, logger) - cmd := InitCmd(mock.GenInitOptions, ctx) + cdc := wire.NewCodec() + appInit := AppInit{ + AppGenState: mock.AppGenState, + AppGenTx: mock.AppGenTx, + } + cmd := InitCmd(ctx, cdc, appInit) err = cmd.RunE(nil, nil) require.NoError(t, err) } + +func TestGenTxCmd(t *testing.T) { + // TODO +} + +func TestSimpleAppGenTx(t *testing.T) { + // TODO +} + +func TestSimpleAppGenState(t *testing.T) { + // TODO +} diff --git a/server/start.go b/server/start.go index a4ff9852e5..c9a38abdff 100644 --- a/server/start.go +++ b/server/start.go @@ -27,45 +27,34 @@ type AppCreator func(string, log.Logger) (abci.Application, error) // StartCmd runs the service passed in, either // stand-alone, or in-process with tendermint -func StartCmd(app AppCreator, ctx *Context) *cobra.Command { - start := startCmd{ - appCreator: app, - context: ctx, - } +func StartCmd(ctx *Context, appCreator AppCreator) *cobra.Command { cmd := &cobra.Command{ Use: "start", Short: "Run the full node", - RunE: start.run, + RunE: func(cmd *cobra.Command, args []string) error { + if !viper.GetBool(flagWithTendermint) { + ctx.Logger.Info("Starting ABCI without Tendermint") + return startStandAlone(ctx, appCreator) + } + ctx.Logger.Info("Starting ABCI with Tendermint") + return startInProcess(ctx, appCreator) + }, } + // basic flags for abci app cmd.Flags().Bool(flagWithTendermint, true, "run abci app embedded in-process with tendermint") cmd.Flags().String(flagAddress, "tcp://0.0.0.0:46658", "Listen address") - // AddNodeFlags adds support for all - // tendermint-specific command line options + // AddNodeFlags adds support for all tendermint-specific command line options tcmd.AddNodeFlags(cmd) return cmd } -type startCmd struct { - appCreator AppCreator - context *Context -} - -func (s startCmd) run(cmd *cobra.Command, args []string) error { - if !viper.GetBool(flagWithTendermint) { - s.context.Logger.Info("Starting ABCI without Tendermint") - return s.startStandAlone() - } - s.context.Logger.Info("Starting ABCI with Tendermint") - return s.startInProcess() -} - -func (s startCmd) startStandAlone() error { +func startStandAlone(ctx *Context, appCreator AppCreator) error { // Generate the app in the proper dir addr := viper.GetString(flagAddress) home := viper.GetString("home") - app, err := s.appCreator(home, s.context.Logger) + app, err := appCreator(home, ctx.Logger) if err != nil { return err } @@ -74,7 +63,7 @@ func (s startCmd) startStandAlone() error { if err != nil { return errors.Errorf("Error creating listener: %v\n", err) } - svr.SetLogger(s.context.Logger.With("module", "abci-server")) + svr.SetLogger(ctx.Logger.With("module", "abci-server")) svr.Start() // Wait forever @@ -85,10 +74,10 @@ func (s startCmd) startStandAlone() error { return nil } -func (s startCmd) startInProcess() error { - cfg := s.context.Config +func startInProcess(ctx *Context, appCreator AppCreator) error { + cfg := ctx.Config home := cfg.RootDir - app, err := s.appCreator(home, s.context.Logger) + app, err := appCreator(home, ctx.Logger) if err != nil { return err } @@ -99,7 +88,7 @@ func (s startCmd) startInProcess() error { proxy.NewLocalClientCreator(app), node.DefaultGenesisDocProviderFunc(cfg), node.DefaultDBProvider, - s.context.Logger.With("module", "node")) + ctx.Logger.With("module", "node")) if err != nil { return err } diff --git a/server/start_test.go b/server/start_test.go index ec6c886b11..3bf2eac7e4 100644 --- a/server/start_test.go +++ b/server/start_test.go @@ -10,6 +10,7 @@ import ( "github.com/stretchr/testify/require" "github.com/cosmos/cosmos-sdk/mock" + "github.com/cosmos/cosmos-sdk/wire" "github.com/tendermint/abci/server" tcmd "github.com/tendermint/tendermint/cmd/tendermint/commands" "github.com/tendermint/tmlibs/log" @@ -25,7 +26,12 @@ func TestStartStandAlone(t *testing.T) { cfg, err := tcmd.ParseConfig() require.Nil(t, err) ctx := NewContext(cfg, logger) - initCmd := InitCmd(mock.GenInitOptions, ctx) + cdc := wire.NewCodec() + appInit := AppInit{ + AppGenState: mock.AppGenState, + AppGenTx: mock.AppGenTx, + } + initCmd := InitCmd(ctx, cdc, appInit) err = initCmd.RunE(nil, nil) require.NoError(t, err) @@ -51,13 +57,18 @@ func TestStartWithTendermint(t *testing.T) { cfg, err := tcmd.ParseConfig() require.Nil(t, err) ctx := NewContext(cfg, logger) - initCmd := InitCmd(mock.GenInitOptions, ctx) + cdc := wire.NewCodec() + appInit := AppInit{ + AppGenState: mock.AppGenState, + AppGenTx: mock.AppGenTx, + } + initCmd := InitCmd(ctx, cdc, appInit) err = initCmd.RunE(nil, nil) require.NoError(t, err) // set up app and start up viper.Set(flagWithTendermint, true) - startCmd := StartCmd(mock.NewApp, ctx) + startCmd := StartCmd(ctx, mock.NewApp) startCmd.Flags().Set(flagAddress, FreeTCPAddr(t)) // set to a new free address timeout := time.Duration(5) * time.Second diff --git a/server/test_helpers.go b/server/test_helpers.go index f226ba1b1f..382c778787 100644 --- a/server/test_helpers.go +++ b/server/test_helpers.go @@ -8,13 +8,10 @@ import ( "testing" "time" - "github.com/cosmos/cosmos-sdk/mock" "github.com/spf13/cobra" "github.com/spf13/viper" "github.com/stretchr/testify/require" - tcmd "github.com/tendermint/tendermint/cmd/tendermint/commands" "github.com/tendermint/tmlibs/cli" - "github.com/tendermint/tmlibs/log" ) // Get a free address for a test tendermint server @@ -40,30 +37,6 @@ func setupViper(t *testing.T) func() { } } -// Begin the server pass up the channel to close -// NOTE pass up the channel so it can be closed at the end of the process -func StartServer(t *testing.T) chan error { - defer setupViper(t)() - - cfg, err := tcmd.ParseConfig() - require.Nil(t, err) - - // init server - ctx := NewContext(cfg, log.NewNopLogger()) - initCmd := InitCmd(mock.GenInitOptions, ctx) - err = initCmd.RunE(nil, nil) - require.NoError(t, err) - - // start server - viper.Set(flagWithTendermint, true) - startCmd := StartCmd(mock.NewApp, ctx) - startCmd.Flags().Set(flagAddress, FreeTCPAddr(t)) // set to a new free address - startCmd.Flags().Set("rpc.laddr", FreeTCPAddr(t)) // set to a new free address - timeout := time.Duration(3) * time.Second - - return RunOrTimeout(startCmd, timeout, t) -} - // Run or Timout RunE of command passed in func RunOrTimeout(cmd *cobra.Command, timeout time.Duration, t *testing.T) chan error { done := make(chan error) diff --git a/server/tm_cmds.go b/server/tm_cmds.go index 73dca6651e..d581ca5f76 100644 --- a/server/tm_cmds.go +++ b/server/tm_cmds.go @@ -5,6 +5,7 @@ import ( "fmt" "strings" + "github.com/cosmos/cosmos-sdk/wire" "github.com/spf13/cobra" "github.com/spf13/viper" @@ -35,7 +36,7 @@ func ShowValidatorCmd(ctx *Context) *cobra.Command { flagJSON := "json" cmd := cobra.Command{ Use: "show_validator", - Short: "Show this node's validator info", + Short: "Show this node's tendermint validator info", RunE: func(cmd *cobra.Command, args []string) error { cfg := ctx.Config @@ -43,6 +44,9 @@ func ShowValidatorCmd(ctx *Context) *cobra.Command { pubKey := privValidator.PubKey if viper.GetBool(flagJSON) { + + cdc := wire.NewCodec() + wire.RegisterCrypto(cdc) pubKeyJSONBytes, err := cdc.MarshalJSON(pubKey) if err != nil { return err @@ -63,7 +67,7 @@ func ShowValidatorCmd(ctx *Context) *cobra.Command { func UnsafeResetAllCmd(ctx *Context) *cobra.Command { return &cobra.Command{ Use: "unsafe_reset_all", - Short: "Reset all blockchain data", + Short: "Reset blockchain database, priv_validator.json file, and the logger", RunE: func(cmd *cobra.Command, args []string) error { cfg := ctx.Config tcmd.ResetAll(cfg.DBDir(), cfg.PrivValidatorFile(), ctx.Logger) diff --git a/server/util.go b/server/util.go index cf37ec5cc4..ed91f30489 100644 --- a/server/util.go +++ b/server/util.go @@ -1,12 +1,16 @@ package server import ( + "encoding/json" + "net" "os" + "github.com/pkg/errors" "github.com/spf13/cobra" "github.com/spf13/viper" "github.com/cosmos/cosmos-sdk/version" + "github.com/cosmos/cosmos-sdk/wire" tcmd "github.com/tendermint/tendermint/cmd/tendermint/commands" cfg "github.com/tendermint/tendermint/config" "github.com/tendermint/tmlibs/cli" @@ -31,7 +35,7 @@ func NewContext(config *cfg.Config, logger log.Logger) *Context { return &Context{config, logger} } -//-------------------------------------------------------------------- +//___________________________________________________________________________________ // PersistentPreRunEFn returns a PersistentPreRunE function for cobra // that initailizes the passed in context with a properly configured @@ -62,18 +66,71 @@ func PersistentPreRunEFn(context *Context) func(*cobra.Command, []string) error // add server commands func AddCommands( - rootCmd *cobra.Command, - appState GenAppState, appCreator AppCreator, - context *Context) { + ctx *Context, cdc *wire.Codec, + rootCmd *cobra.Command, appInit AppInit, + appCreator AppCreator) { - rootCmd.PersistentFlags().String("log_level", context.Config.LogLevel, "Log level") + rootCmd.PersistentFlags().String("log_level", ctx.Config.LogLevel, "Log level") rootCmd.AddCommand( - InitCmd(appState, context), - StartCmd(appCreator, context), - UnsafeResetAllCmd(context), - ShowNodeIDCmd(context), - ShowValidatorCmd(context), + InitCmd(ctx, cdc, appInit), + StartCmd(ctx, appCreator), + UnsafeResetAllCmd(ctx), + ShowNodeIDCmd(ctx), + ShowValidatorCmd(ctx), version.VersionCmd, ) } + +//___________________________________________________________________________________ + +// append a new json field to existing json message +func AppendJSON(cdc *wire.Codec, baseJSON []byte, key string, value json.RawMessage) (appended []byte, err error) { + var jsonMap map[string]json.RawMessage + err = cdc.UnmarshalJSON(baseJSON, &jsonMap) + if err != nil { + return nil, err + } + jsonMap[key] = value + bz, err := wire.MarshalJSONIndent(cdc, jsonMap) + return json.RawMessage(bz), err +} + +// https://stackoverflow.com/questions/23558425/how-do-i-get-the-local-ip-address-in-go +// TODO there must be a better way to get external IP +func externalIP() (string, error) { + ifaces, err := net.Interfaces() + if err != nil { + return "", err + } + for _, iface := range ifaces { + if iface.Flags&net.FlagUp == 0 { + continue // interface down + } + if iface.Flags&net.FlagLoopback != 0 { + continue // loopback interface + } + addrs, err := iface.Addrs() + if err != nil { + return "", err + } + for _, addr := range addrs { + var ip net.IP + switch v := addr.(type) { + case *net.IPNet: + ip = v.IP + case *net.IPAddr: + ip = v.IP + } + if ip == nil || ip.IsLoopback() { + continue + } + ip = ip.To4() + if ip == nil { + continue // not an ipv4 address + } + return ip.String(), nil + } + } + return "", errors.New("are you connected to the network?") +} diff --git a/server/util_test.go b/server/util_test.go new file mode 100644 index 0000000000..13f8ad5dbc --- /dev/null +++ b/server/util_test.go @@ -0,0 +1,41 @@ +package server + +import ( + "encoding/json" + "testing" + + "github.com/cosmos/cosmos-sdk/wire" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestAppendJSON(t *testing.T) { + cdc := wire.NewCodec() + + foo := map[string]string{"foo": "foofoo"} + bar := map[string]string{"barInner": "barbar"} + + // create raw messages + bz, err := cdc.MarshalJSON(foo) + require.NoError(t, err) + fooRaw := json.RawMessage(bz) + + bz, err = cdc.MarshalJSON(bar) + require.NoError(t, err) + barRaw := json.RawMessage(bz) + + // make the append + appBz, err := AppendJSON(cdc, fooRaw, "barOuter", barRaw) + require.NoError(t, err) + + // test the append + var appended map[string]json.RawMessage + err = cdc.UnmarshalJSON(appBz, &appended) + require.NoError(t, err) + + var resBar map[string]string + err = cdc.UnmarshalJSON(appended["barOuter"], &resBar) + require.NoError(t, err) + + assert.Equal(t, bar, resBar, "appended: %v", appended) +} diff --git a/server/wire.go b/server/wire.go deleted file mode 100644 index 261e7cfe1e..0000000000 --- a/server/wire.go +++ /dev/null @@ -1,12 +0,0 @@ -package server - -import ( - "github.com/cosmos/cosmos-sdk/wire" -) - -var cdc *wire.Codec - -func init() { - cdc = wire.NewCodec() - wire.RegisterCrypto(cdc) -} diff --git a/tests/gobash.go b/tests/gobash.go index c106c1f3f2..baa84f417f 100644 --- a/tests/gobash.go +++ b/tests/gobash.go @@ -1,7 +1,6 @@ package tests import ( - "fmt" "io" "os/exec" "strings" @@ -27,13 +26,11 @@ func getCmd(t *testing.T, command string) *exec.Cmd { } // Execute the command, return standard output and error, try a few times if requested -func ExecuteT(t *testing.T, command string, trials int) (out string) { +func ExecuteT(t *testing.T, command string) (out string) { cmd := getCmd(t, command) bz, err := cmd.CombinedOutput() - if err != nil && trials > 1 { - fmt.Printf("trial %v, retrying: %v\n", trials, command) - time.Sleep(time.Second * 10) - return ExecuteT(t, command, trials-1) + if err != nil { + panic(err) } require.NoError(t, err, string(bz)) out = strings.Trim(string(bz), "\n") //trim any new lines @@ -48,7 +45,7 @@ func GoExecuteT(t *testing.T, command string) (cmd *exec.Cmd, pipeIn io.WriteClo require.NoError(t, err) pipeOut, err = cmd.StdoutPipe() require.NoError(t, err) - go cmd.Start() + cmd.Start() time.Sleep(time.Second) return cmd, pipeIn, pipeOut } diff --git a/tests/util.go b/tests/util.go new file mode 100644 index 0000000000..a6f026f242 --- /dev/null +++ b/tests/util.go @@ -0,0 +1,107 @@ +package tests + +import ( + "fmt" + "io/ioutil" + "net/http" + "time" + + amino "github.com/tendermint/go-amino" + ctypes "github.com/tendermint/tendermint/rpc/core/types" + rpcclient "github.com/tendermint/tendermint/rpc/lib/client" +) + +// TODO: these functions just print to Stdout. +// consider using the logger. + +// Uses localhost +func WaitForHeight(height int64, port string) { + for { + var resultBlock ctypes.ResultBlock + + url := fmt.Sprintf("http://localhost:%v%v", port, "/blocks/latest") + res, err := http.Get(url) + if err != nil { + panic(err) + } + + body, err := ioutil.ReadAll(res.Body) + if err != nil { + panic(err) + } + res.Body.Close() + + err = cdc.UnmarshalJSON([]byte(body), &resultBlock) + if err != nil { + fmt.Println("RES", res) + fmt.Println("BODY", string(body)) + panic(err) + } + + if resultBlock.Block.Height >= height { + return + } + time.Sleep(time.Millisecond * 100) + } +} + +// wait for 2 blocks. +// uses localhost +func WaitForStart(port string) { + waitHeight := int64(2) + for { + time.Sleep(time.Second) + + url := fmt.Sprintf("http://localhost:%v%v", port, "/blocks/latest") + res, err := http.Get(url) + if err != nil { + panic(err) + } + + // waiting for server to start ... + if res.StatusCode != http.StatusOK { + res.Body.Close() + continue + } + + body, err := ioutil.ReadAll(res.Body) + if err != nil { + panic(err) + } + res.Body.Close() + + resultBlock := new(ctypes.ResultBlock) + err = cdc.UnmarshalJSON([]byte(body), &resultBlock) + if err != nil { + fmt.Println("RES", res) + fmt.Println("BODY", string(body)) + panic(err) + } + + if resultBlock.Block.Height >= waitHeight { + return + } + } +} + +// Wait for the RPC server to respond to /status +func WaitForRPC(laddr string) { + fmt.Println("LADDR", laddr) + client := rpcclient.NewJSONRPCClient(laddr) + ctypes.RegisterAmino(client.Codec()) + result := new(ctypes.ResultStatus) + for { + _, err := client.Call("status", map[string]interface{}{}, result) + if err == nil { + return + } + fmt.Printf("Waiting for RPC server to start on %s:%v\n", laddr, err) + time.Sleep(time.Millisecond) + } +} + +var cdc = amino.NewCodec() + +func init() { + ctypes.RegisterAmino(cdc) +} diff --git a/x/auth/client/cli/account.go b/x/auth/client/cli/account.go index c6c8c6c548..b45cb12ddf 100644 --- a/x/auth/client/cli/account.go +++ b/x/auth/client/cli/account.go @@ -4,7 +4,6 @@ import ( "encoding/hex" "fmt" - "github.com/pkg/errors" "github.com/spf13/cobra" "github.com/cosmos/cosmos-sdk/client/context" @@ -32,56 +31,40 @@ func GetAccountDecoder(cdc *wire.Codec) sdk.AccountDecoder { // GetAccountCmd returns a query account that will display the // state of the account at a given address func GetAccountCmd(storeName string, cdc *wire.Codec, decoder sdk.AccountDecoder) *cobra.Command { - cmdr := commander{ - storeName, - cdc, - decoder, - } return &cobra.Command{ - Use: "account
", + Use: "account [address]", Short: "Query account balance", - RunE: cmdr.getAccountCmd, + Args: cobra.ExactArgs(1), + RunE: func(cmd *cobra.Command, args []string) error { + + // find the key to look up the account + addr := args[0] + bz, err := hex.DecodeString(addr) + if err != nil { + return err + } + key := sdk.Address(bz) + + // perform query + ctx := context.NewCoreContextFromViper() + res, err := ctx.Query(key, storeName) + if err != nil { + return err + } + + // decode the value + account, err := decoder(res) + if err != nil { + return err + } + + // print out whole account + output, err := wire.MarshalJSONIndent(cdc, account) + if err != nil { + return err + } + fmt.Println(string(output)) + return nil + }, } } - -type commander struct { - storeName string - cdc *wire.Codec - decoder sdk.AccountDecoder -} - -func (c commander) getAccountCmd(cmd *cobra.Command, args []string) error { - if len(args) != 1 || len(args[0]) == 0 { - return errors.New("You must provide an account name") - } - - // find the key to look up the account - addr := args[0] - bz, err := hex.DecodeString(addr) - if err != nil { - return err - } - key := sdk.Address(bz) - - ctx := context.NewCoreContextFromViper() - - res, err := ctx.Query(key, c.storeName) - if err != nil { - return err - } - - // decode the value - account, err := c.decoder(res) - if err != nil { - return err - } - - // print out whole account - output, err := wire.MarshalJSONIndent(c.cdc, account) - if err != nil { - return err - } - fmt.Println(string(output)) - - return nil -} diff --git a/x/stake/handler.go b/x/stake/handler.go index d9f718fe58..e397203857 100644 --- a/x/stake/handler.go +++ b/x/stake/handler.go @@ -52,6 +52,9 @@ func NewEndBlocker(k Keeper) sdk.EndBlocker { func InitGenesis(ctx sdk.Context, k Keeper, data GenesisState) { k.setPool(ctx, data.Pool) k.setParams(ctx, data.Params) + for _, candidate := range data.Candidates { + k.setCandidate(ctx, candidate) + } } //_____________________________________________________________________ diff --git a/x/stake/types.go b/x/stake/types.go index 8f9e87cbb0..634b51186b 100644 --- a/x/stake/types.go +++ b/x/stake/types.go @@ -9,8 +9,9 @@ import ( // GenesisState - all staking state that must be provided at genesis type GenesisState struct { - Pool Pool `json:"pool"` - Params Params `json:"params"` + Pool Pool `json:"pool"` + Params Params `json:"params"` + Candidates []Candidate `json:"candidates"` } //_________________________________________________________________________