diff --git a/.circleci/config.yml b/.circleci/config.yml index 6754ebc05..47cb5661d 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -31,7 +31,7 @@ jobs: name: binaries command: | export PATH="$GOBIN:$PATH" - make build + make install - persist_to_workspace: root: /tmp/workspace paths: @@ -59,7 +59,9 @@ jobs: - run: name: Run tests command: | - for pkg in $(go list github.com/cosmos/cosmos-sdk/... | grep -v /vendor/ | circleci tests split --split-by=timings); do + export PATH="$GOBIN:$PATH" + make install + for pkg in $(go list github.com/cosmos/cosmos-sdk/... | grep -v /vendor/ | grep -v github.com/cosmos/cosmos-sdk/cmd/gaia/cli_test | circleci tests split --split-by=timings); do id=$(basename "$pkg") go test -timeout 5m -race -coverprofile=/tmp/workspace/profiles/$id.out -covermode=atomic "$pkg" diff --git a/CHANGELOG.md b/CHANGELOG.md index 873824c48..d31ce6cbb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,17 +7,19 @@ FEATURES: * Add CacheContext * Add auto sequencing to client * Add FeeHandler to ante handler +* Gaia stake commands include, DeclareCandidacy, EditCandidacy, Delegate, Unbond +* MountStoreWithDB without providing a custom store works. BREAKING CHANGES * Remove go-wire, use go-amino +* Gaia simple-staking bond and unbond functions replaced * [store] Add `SubspaceIterator` and `ReverseSubspaceIterator` to `KVStore` interface * [basecoin] NewBasecoinApp takes a `dbm.DB` and uses namespaced DBs for substores * All module keepers now require a codespace, see basecoin or democoin for usage BUG FIXES - -* MountStoreWithDB without providing a custom store works. +* Gaia now uses stake, ported from github.com/cosmos/gaia ## 0.14.1 (April 9, 2018) diff --git a/Makefile b/Makefile index b5177621a..daa0361fc 100644 --- a/Makefile +++ b/Makefile @@ -7,7 +7,7 @@ all: check_tools get_vendor_deps build build_examples test ######################################## ### CI -ci: get_tools get_vendor_deps build test_cover +ci: get_tools get_vendor_deps install test_cover ######################################## ### Build @@ -15,11 +15,11 @@ ci: get_tools get_vendor_deps build test_cover # This can be unified later, here for easy demos build: ifeq ($(OS),Windows_NT) - go build $(BUILD_FLAGS) -o build/gaiad.exe ./cmd/gaiad - go build $(BUILD_FLAGS) -o build/gaiacli.exe ./cmd/gaiacli + go build $(BUILD_FLAGS) -o build/gaiad.exe ./cmd/gaia/cmd/gaiad + go build $(BUILD_FLAGS) -o build/gaiacli.exe ./cmd/gaia/cmd/gaiacli else - go build $(BUILD_FLAGS) -o build/gaiad ./cmd/gaiad - go build $(BUILD_FLAGS) -o build/gaiacli ./cmd/gaiacli + go build $(BUILD_FLAGS) -o build/gaiad ./cmd/gaia/cmd/gaiad + go build $(BUILD_FLAGS) -o build/gaiacli ./cmd/gaia/cmd/gaiacli endif build_examples: @@ -36,8 +36,8 @@ else endif install: - go install $(BUILD_FLAGS) ./cmd/gaiad - go install $(BUILD_FLAGS) ./cmd/gaiacli + go install $(BUILD_FLAGS) ./cmd/gaia/cmd/gaiad + go install $(BUILD_FLAGS) ./cmd/gaia/cmd/gaiacli install_examples: go install $(BUILD_FLAGS) ./examples/basecoin/cmd/basecoind diff --git a/baseapp/baseapp.go b/baseapp/baseapp.go index 115748ca5..3eebb1eb4 100644 --- a/baseapp/baseapp.go +++ b/baseapp/baseapp.go @@ -322,9 +322,8 @@ func (app *BaseApp) DeliverTx(txBytes []byte) (res abci.ResponseDeliverTx) { if result.IsOK() { app.valUpdates = append(app.valUpdates, result.ValidatorUpdates...) } else { - // Even though the Code is not OK, there will be some side - // effects, like those caused by fee deductions or sequence - // incrementations. + // Even though the Result.Code is not OK, there are still effects, + // namely fee deductions and sequence incrementing. } // Tell the blockchain engine (i.e. Tendermint). @@ -453,7 +452,7 @@ func (app *BaseApp) Commit() (res abci.ResponseCommit) { // Use the header from this latest block. app.setCheckState(header) - // Emtpy the Deliver state + // Empty the Deliver state app.deliverState = nil return abci.ResponseCommit{ diff --git a/client/keys/wire.go b/client/keys/wire.go index 225e60ae7..a163f995a 100644 --- a/client/keys/wire.go +++ b/client/keys/wire.go @@ -11,6 +11,12 @@ func init() { wire.RegisterCrypto(cdc) } +// marshal keys func MarshalJSON(o interface{}) ([]byte, error) { return cdc.MarshalJSON(o) } + +// unmarshal json +func UnmarshalJSON(bz []byte, ptr interface{}) error { + return cdc.UnmarshalJSON(bz, ptr) +} diff --git a/cmd/gaia/app/app.go b/cmd/gaia/app/app.go new file mode 100644 index 000000000..57a5d672c --- /dev/null +++ b/cmd/gaia/app/app.go @@ -0,0 +1,200 @@ +package app + +import ( + "encoding/json" + + abci "github.com/tendermint/abci/types" + cmn "github.com/tendermint/tmlibs/common" + 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" + "github.com/cosmos/cosmos-sdk/x/auth" + "github.com/cosmos/cosmos-sdk/x/bank" + "github.com/cosmos/cosmos-sdk/x/ibc" + "github.com/cosmos/cosmos-sdk/x/stake" +) + +const ( + appName = "GaiaApp" +) + +// Extended ABCI application +type GaiaApp struct { + *bam.BaseApp + cdc *wire.Codec + + // keys to access the substores + capKeyMainStore *sdk.KVStoreKey + capKeyAccountStore *sdk.KVStoreKey + capKeyIBCStore *sdk.KVStoreKey + capKeyStakeStore *sdk.KVStoreKey + + // Manage getting and setting accounts + accountMapper sdk.AccountMapper + coinKeeper bank.CoinKeeper + ibcMapper ibc.IBCMapper + stakeKeeper stake.Keeper + + // Handle fees + feeHandler sdk.FeeHandler +} + +func NewGaiaApp(logger log.Logger, db dbm.DB) *GaiaApp { + // create your application object + var app = &GaiaApp{ + BaseApp: bam.NewBaseApp(appName, logger, db), + cdc: MakeCodec(), + capKeyMainStore: sdk.NewKVStoreKey("main"), + capKeyAccountStore: sdk.NewKVStoreKey("acc"), + capKeyIBCStore: sdk.NewKVStoreKey("ibc"), + capKeyStakeStore: sdk.NewKVStoreKey("stake"), + } + + // define the accountMapper + app.accountMapper = auth.NewAccountMapper( + app.cdc, + app.capKeyMainStore, // target store + &auth.BaseAccount{}, // prototype + ).Seal() + + // add handlers + app.coinKeeper = bank.NewCoinKeeper(app.accountMapper) + app.ibcMapper = ibc.NewIBCMapper(app.cdc, app.capKeyIBCStore, app.RegisterCodespace(ibc.DefaultCodespace)) + app.stakeKeeper = stake.NewKeeper(app.cdc, app.capKeyStakeStore, app.coinKeeper, app.RegisterCodespace(stake.DefaultCodespace)) + + app.Router(). + AddRoute("bank", bank.NewHandler(app.coinKeeper)). + AddRoute("ibc", ibc.NewHandler(app.ibcMapper, app.coinKeeper)). + AddRoute("stake", stake.NewHandler(app.stakeKeeper)) + + // Define the feeHandler. + app.feeHandler = auth.BurnFeeHandler + + // initialize BaseApp + app.SetTxDecoder(app.txDecoder) + app.SetInitChainer(app.initChainer) + app.SetEndBlocker(stake.NewEndBlocker(app.stakeKeeper)) + app.MountStoresIAVL(app.capKeyMainStore, app.capKeyAccountStore, app.capKeyIBCStore, app.capKeyStakeStore) + app.SetAnteHandler(auth.NewAnteHandler(app.accountMapper, app.feeHandler)) + err := app.LoadLatestVersion(app.capKeyMainStore) + if err != nil { + cmn.Exit(err.Error()) + } + + return app +} + +// custom tx codec +func MakeCodec() *wire.Codec { + var cdc = wire.NewCodec() + + // Register Msgs + cdc.RegisterInterface((*sdk.Msg)(nil), nil) + + ibc.RegisterWire(cdc) + bank.RegisterWire(cdc) + stake.RegisterWire(cdc) + + // Register AppAccount + cdc.RegisterInterface((*sdk.Account)(nil), nil) + cdc.RegisterConcrete(&auth.BaseAccount{}, "gaia/Account", nil) + + // Register crypto. + wire.RegisterCrypto(cdc) + + return cdc +} + +// custom logic for transaction decoding +func (app *GaiaApp) txDecoder(txBytes []byte) (sdk.Tx, sdk.Error) { + var tx = sdk.StdTx{} + + if len(txBytes) == 0 { + return nil, sdk.ErrTxDecode("txBytes are empty") + } + + // StdTx.Msg is an interface. The concrete types + // are registered by MakeTxCodec + err := app.cdc.UnmarshalBinary(txBytes, &tx) + if err != nil { + return nil, sdk.ErrTxDecode("").Trace(err.Error()) + } + return tx, nil +} + +// custom logic for gaia initialization +func (app *GaiaApp) initChainer(ctx sdk.Context, req abci.RequestInitChain) abci.ResponseInitChain { + stateJSON := req.AppStateBytes + + genesisState := new(GenesisState) + err := json.Unmarshal(stateJSON, genesisState) + if err != nil { + panic(err) // TODO https://github.com/cosmos/cosmos-sdk/issues/468 + // return sdk.ErrGenesisParse("").TraceCause(err, "") + } + + // load the accounts + for _, gacc := range genesisState.Accounts { + acc := gacc.ToAccount() + app.accountMapper.SetAccount(ctx, acc) + } + + // load the initial stake information + stake.InitGenesis(ctx, app.stakeKeeper, genesisState.StakeData) + + 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/app_test.go b/cmd/gaia/app/app_test.go new file mode 100644 index 000000000..b3350b503 --- /dev/null +++ b/cmd/gaia/app/app_test.go @@ -0,0 +1,518 @@ +package app + +import ( + "encoding/json" + "fmt" + "os" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/auth" + "github.com/cosmos/cosmos-sdk/x/bank" + "github.com/cosmos/cosmos-sdk/x/ibc" + "github.com/cosmos/cosmos-sdk/x/stake" + + abci "github.com/tendermint/abci/types" + crypto "github.com/tendermint/go-crypto" + dbm "github.com/tendermint/tmlibs/db" + "github.com/tendermint/tmlibs/log" +) + +// Construct some global addrs and txs for tests. +var ( + chainID = "" // TODO + + accName = "foobart" + + priv1 = crypto.GenPrivKeyEd25519() + addr1 = priv1.PubKey().Address() + priv2 = crypto.GenPrivKeyEd25519() + addr2 = priv2.PubKey().Address() + addr3 = crypto.GenPrivKeyEd25519().PubKey().Address() + priv4 = crypto.GenPrivKeyEd25519() + addr4 = priv4.PubKey().Address() + coins = sdk.Coins{{"foocoin", 10}} + halfCoins = sdk.Coins{{"foocoin", 5}} + manyCoins = sdk.Coins{{"foocoin", 1}, {"barcoin", 1}} + fee = sdk.StdFee{ + sdk.Coins{{"foocoin", 0}}, + 0, + } + + sendMsg1 = bank.SendMsg{ + Inputs: []bank.Input{bank.NewInput(addr1, coins)}, + Outputs: []bank.Output{bank.NewOutput(addr2, coins)}, + } + + sendMsg2 = bank.SendMsg{ + Inputs: []bank.Input{bank.NewInput(addr1, coins)}, + Outputs: []bank.Output{ + bank.NewOutput(addr2, halfCoins), + bank.NewOutput(addr3, halfCoins), + }, + } + + sendMsg3 = bank.SendMsg{ + Inputs: []bank.Input{ + bank.NewInput(addr1, coins), + bank.NewInput(addr4, coins), + }, + Outputs: []bank.Output{ + bank.NewOutput(addr2, coins), + bank.NewOutput(addr3, coins), + }, + } + + sendMsg4 = bank.SendMsg{ + Inputs: []bank.Input{ + bank.NewInput(addr2, coins), + }, + Outputs: []bank.Output{ + bank.NewOutput(addr1, coins), + }, + } + + sendMsg5 = bank.SendMsg{ + Inputs: []bank.Input{ + bank.NewInput(addr1, manyCoins), + }, + Outputs: []bank.Output{ + bank.NewOutput(addr2, manyCoins), + }, + } +) + +func loggerAndDB() (log.Logger, dbm.DB) { + logger := log.NewTMLogger(log.NewSyncWriter(os.Stdout)).With("module", "sdk/app") + db := dbm.NewMemDB() + return logger, db +} + +func newGaiaApp() *GaiaApp { + logger, db := loggerAndDB() + return NewGaiaApp(logger, db) +} + +func setGenesis(gapp *GaiaApp, accs ...*auth.BaseAccount) error { + genaccs := make([]GenesisAccount, len(accs)) + for i, acc := range accs { + genaccs[i] = NewGenesisAccount(acc) + } + + genesisState := GenesisState{ + Accounts: genaccs, + StakeData: stake.GetDefaultGenesisState(), + } + + stateBytes, err := json.MarshalIndent(genesisState, "", "\t") + if err != nil { + return err + } + + // Initialize the chain + vals := []abci.Validator{} + gapp.InitChain(abci.RequestInitChain{vals, stateBytes}) + gapp.Commit() + + return nil +} + +//_______________________________________________________________________ + +func TestMsgs(t *testing.T) { + gapp := newGaiaApp() + require.Nil(t, setGenesis(gapp)) + + msgs := []struct { + msg sdk.Msg + }{ + {sendMsg1}, + } + + for i, m := range msgs { + // Run a CheckDeliver + SignCheckDeliver(t, gapp, m.msg, []int64{int64(i)}, false, priv1) + } +} + +func setGenesisAccounts(gapp *GaiaApp, accs ...*auth.BaseAccount) error { + genaccs := make([]GenesisAccount, len(accs)) + for i, acc := range accs { + genaccs[i] = NewGenesisAccount(acc) + } + + genesisState := GenesisState{ + Accounts: genaccs, + StakeData: stake.GetDefaultGenesisState(), + } + + stateBytes, err := json.MarshalIndent(genesisState, "", "\t") + if err != nil { + return err + } + + // Initialize the chain + vals := []abci.Validator{} + gapp.InitChain(abci.RequestInitChain{vals, stateBytes}) + gapp.Commit() + + return nil +} + +func TestGenesis(t *testing.T) { + logger, dbs := loggerAndDB() + gapp := NewGaiaApp(logger, dbs) + + // Construct some genesis bytes to reflect GaiaAccount + pk := crypto.GenPrivKeyEd25519().PubKey() + addr := pk.Address() + coins, err := sdk.ParseCoins("77foocoin,99barcoin") + require.Nil(t, err) + baseAcc := &auth.BaseAccount{ + Address: addr, + Coins: coins, + } + + err = setGenesis(gapp, baseAcc) + assert.Nil(t, err) + + // A checkTx context + ctx := gapp.BaseApp.NewContext(true, abci.Header{}) + res1 := gapp.accountMapper.GetAccount(ctx, baseAcc.Address) + assert.Equal(t, baseAcc, res1) + + // reload app and ensure the account is still there + gapp = NewGaiaApp(logger, dbs) + ctx = gapp.BaseApp.NewContext(true, abci.Header{}) + res1 = gapp.accountMapper.GetAccount(ctx, baseAcc.Address) + assert.Equal(t, baseAcc, res1) +} + +func TestSendMsgWithAccounts(t *testing.T) { + gapp := newGaiaApp() + + // Construct some genesis bytes to reflect GaiaAccount + // Give 77 foocoin to the first key + coins, err := sdk.ParseCoins("77foocoin") + require.Nil(t, err) + baseAcc := &auth.BaseAccount{ + Address: addr1, + Coins: coins, + } + + // Construct genesis state + err = setGenesis(gapp, baseAcc) + require.Nil(t, err) + + // A checkTx context (true) + ctxCheck := gapp.BaseApp.NewContext(true, abci.Header{}) + res1 := gapp.accountMapper.GetAccount(ctxCheck, addr1) + assert.Equal(t, baseAcc, res1.(*auth.BaseAccount)) + + // Run a CheckDeliver + SignCheckDeliver(t, gapp, sendMsg1, []int64{0}, true, priv1) + + // Check balances + CheckBalance(t, gapp, addr1, "67foocoin") + CheckBalance(t, gapp, addr2, "10foocoin") + + // Delivering again should cause replay error + SignCheckDeliver(t, gapp, sendMsg1, []int64{0}, false, priv1) + + // bumping the txnonce number without resigning should be an auth error + tx := genTx(sendMsg1, []int64{0}, priv1) + tx.Signatures[0].Sequence = 1 + res := gapp.Deliver(tx) + + assert.Equal(t, sdk.ToABCICode(sdk.CodespaceRoot, sdk.CodeUnauthorized), res.Code, res.Log) + + // resigning the tx with the bumped sequence should work + SignCheckDeliver(t, gapp, sendMsg1, []int64{1}, true, priv1) +} + +func TestSendMsgMultipleOut(t *testing.T) { + gapp := newGaiaApp() + + genCoins, err := sdk.ParseCoins("42foocoin") + require.Nil(t, err) + + acc1 := &auth.BaseAccount{ + Address: addr1, + Coins: genCoins, + } + + acc2 := &auth.BaseAccount{ + Address: addr2, + Coins: genCoins, + } + + err = setGenesis(gapp, acc1, acc2) + require.Nil(t, err) + + // Simulate a Block + SignCheckDeliver(t, gapp, sendMsg2, []int64{0}, true, priv1) + + // Check balances + CheckBalance(t, gapp, addr1, "32foocoin") + CheckBalance(t, gapp, addr2, "47foocoin") + CheckBalance(t, gapp, addr3, "5foocoin") +} + +func TestSengMsgMultipleInOut(t *testing.T) { + gapp := newGaiaApp() + + genCoins, err := sdk.ParseCoins("42foocoin") + require.Nil(t, err) + + acc1 := &auth.BaseAccount{ + Address: addr1, + Coins: genCoins, + } + acc2 := &auth.BaseAccount{ + Address: addr2, + Coins: genCoins, + } + acc4 := &auth.BaseAccount{ + Address: addr4, + Coins: genCoins, + } + + err = setGenesis(gapp, acc1, acc2, acc4) + assert.Nil(t, err) + + // CheckDeliver + SignCheckDeliver(t, gapp, sendMsg3, []int64{0, 0}, true, priv1, priv4) + + // Check balances + CheckBalance(t, gapp, addr1, "32foocoin") + CheckBalance(t, gapp, addr4, "32foocoin") + CheckBalance(t, gapp, addr2, "52foocoin") + CheckBalance(t, gapp, addr3, "10foocoin") +} + +func TestSendMsgDependent(t *testing.T) { + gapp := newGaiaApp() + + genCoins, err := sdk.ParseCoins("42foocoin") + require.Nil(t, err) + + acc1 := &auth.BaseAccount{ + Address: addr1, + Coins: genCoins, + } + + err = setGenesis(gapp, acc1) + require.Nil(t, err) + + // CheckDeliver + SignCheckDeliver(t, gapp, sendMsg1, []int64{0}, true, priv1) + + // Check balances + CheckBalance(t, gapp, addr1, "32foocoin") + CheckBalance(t, gapp, addr2, "10foocoin") + + // Simulate a Block + SignCheckDeliver(t, gapp, sendMsg4, []int64{0}, true, priv2) + + // Check balances + CheckBalance(t, gapp, addr1, "42foocoin") +} + +func TestIBCMsgs(t *testing.T) { + gapp := newGaiaApp() + + sourceChain := "source-chain" + destChain := "dest-chain" + + baseAcc := &auth.BaseAccount{ + Address: addr1, + Coins: coins, + } + + err := setGenesis(gapp, baseAcc) + require.Nil(t, err) + + // A checkTx context (true) + ctxCheck := gapp.BaseApp.NewContext(true, abci.Header{}) + res1 := gapp.accountMapper.GetAccount(ctxCheck, addr1) + assert.Equal(t, baseAcc, res1) + + packet := ibc.IBCPacket{ + SrcAddr: addr1, + DestAddr: addr1, + Coins: coins, + SrcChain: sourceChain, + DestChain: destChain, + } + + transferMsg := ibc.IBCTransferMsg{ + IBCPacket: packet, + } + + receiveMsg := ibc.IBCReceiveMsg{ + IBCPacket: packet, + Relayer: addr1, + Sequence: 0, + } + + SignCheckDeliver(t, gapp, transferMsg, []int64{0}, true, priv1) + CheckBalance(t, gapp, addr1, "") + SignCheckDeliver(t, gapp, transferMsg, []int64{1}, false, priv1) + SignCheckDeliver(t, gapp, receiveMsg, []int64{2}, true, priv1) + CheckBalance(t, gapp, addr1, "10foocoin") + SignCheckDeliver(t, gapp, receiveMsg, []int64{3}, false, priv1) +} + +func TestStakeMsgs(t *testing.T) { + gapp := newGaiaApp() + + genCoins, err := sdk.ParseCoins("42fermion") + require.Nil(t, err) + bondCoin, err := sdk.ParseCoin("10fermion") + require.Nil(t, err) + + acc1 := &auth.BaseAccount{ + Address: addr1, + Coins: genCoins, + } + acc2 := &auth.BaseAccount{ + Address: addr2, + Coins: genCoins, + } + + err = setGenesis(gapp, acc1, acc2) + require.Nil(t, err) + + // A checkTx context (true) + ctxCheck := gapp.BaseApp.NewContext(true, abci.Header{}) + res1 := gapp.accountMapper.GetAccount(ctxCheck, addr1) + res2 := gapp.accountMapper.GetAccount(ctxCheck, addr2) + require.Equal(t, acc1, res1) + require.Equal(t, acc2, res2) + + // Declare Candidacy + + description := stake.NewDescription("foo_moniker", "", "", "") + declareCandidacyMsg := stake.NewMsgDeclareCandidacy( + addr1, priv1.PubKey(), bondCoin, description, + ) + SignCheckDeliver(t, gapp, declareCandidacyMsg, []int64{0}, true, priv1) + + ctxDeliver := gapp.BaseApp.NewContext(false, abci.Header{}) + res1 = gapp.accountMapper.GetAccount(ctxDeliver, addr1) + require.Equal(t, genCoins.Minus(sdk.Coins{bondCoin}), res1.GetCoins()) + candidate, found := gapp.stakeKeeper.GetCandidate(ctxDeliver, addr1) + require.True(t, found) + require.Equal(t, candidate.Address, addr1) + + // Edit Candidacy + + description = stake.NewDescription("bar_moniker", "", "", "") + editCandidacyMsg := stake.NewMsgEditCandidacy( + addr1, description, + ) + SignDeliver(t, gapp, editCandidacyMsg, []int64{1}, true, priv1) + + candidate, found = gapp.stakeKeeper.GetCandidate(ctxDeliver, addr1) + require.True(t, found) + require.Equal(t, candidate.Description, description) + + // Delegate + + delegateMsg := stake.NewMsgDelegate( + addr2, addr1, bondCoin, + ) + SignDeliver(t, gapp, delegateMsg, []int64{0}, true, priv2) + + ctxDeliver = gapp.BaseApp.NewContext(false, abci.Header{}) + res2 = gapp.accountMapper.GetAccount(ctxDeliver, addr2) + require.Equal(t, genCoins.Minus(sdk.Coins{bondCoin}), res2.GetCoins()) + bond, found := gapp.stakeKeeper.GetDelegatorBond(ctxDeliver, addr2, addr1) + require.True(t, found) + require.Equal(t, bond.DelegatorAddr, addr2) + + // Unbond + + unbondMsg := stake.NewMsgUnbond( + addr2, addr1, "MAX", + ) + SignDeliver(t, gapp, unbondMsg, []int64{1}, true, priv2) + + ctxDeliver = gapp.BaseApp.NewContext(false, abci.Header{}) + res2 = gapp.accountMapper.GetAccount(ctxDeliver, addr2) + require.Equal(t, genCoins, res2.GetCoins()) + _, found = gapp.stakeKeeper.GetDelegatorBond(ctxDeliver, addr2, addr1) + require.False(t, found) +} + +//____________________________________________________________________________________ + +func CheckBalance(t *testing.T, gapp *GaiaApp, addr sdk.Address, balExpected string) { + ctxDeliver := gapp.BaseApp.NewContext(false, abci.Header{}) + res2 := gapp.accountMapper.GetAccount(ctxDeliver, addr) + assert.Equal(t, balExpected, fmt.Sprintf("%v", res2.GetCoins())) +} + +func genTx(msg sdk.Msg, seq []int64, priv ...crypto.PrivKeyEd25519) sdk.StdTx { + sigs := make([]sdk.StdSignature, len(priv)) + for i, p := range priv { + sigs[i] = sdk.StdSignature{ + PubKey: p.PubKey(), + Signature: p.Sign(sdk.StdSignBytes(chainID, seq, fee, msg)), + Sequence: seq[i], + } + } + + return sdk.NewStdTx(msg, fee, sigs) + +} + +func SignCheckDeliver(t *testing.T, gapp *GaiaApp, msg sdk.Msg, seq []int64, expPass bool, priv ...crypto.PrivKeyEd25519) { + + // Sign the tx + tx := genTx(msg, seq, priv...) + + // Run a Check + res := gapp.Check(tx) + if expPass { + require.Equal(t, sdk.ABCICodeOK, res.Code, res.Log) + } else { + require.NotEqual(t, sdk.ABCICodeOK, res.Code, res.Log) + } + + // Simulate a Block + gapp.BeginBlock(abci.RequestBeginBlock{}) + res = gapp.Deliver(tx) + if expPass { + require.Equal(t, sdk.ABCICodeOK, res.Code, res.Log) + } else { + require.NotEqual(t, sdk.ABCICodeOK, res.Code, res.Log) + } + gapp.EndBlock(abci.RequestEndBlock{}) + + // XXX fix code or add explaination as to why using commit breaks a bunch of these tests + //gapp.Commit() +} + +// XXX the only reason we are using Sign Deliver here is because the tests +// break on check tx the second time you use SignCheckDeliver in a test because +// the checktx state has not been updated likely because commit is not being +// called! +func SignDeliver(t *testing.T, gapp *GaiaApp, msg sdk.Msg, seq []int64, expPass bool, priv ...crypto.PrivKeyEd25519) { + + // Sign the tx + tx := genTx(msg, seq, priv...) + + // Simulate a Block + gapp.BeginBlock(abci.RequestBeginBlock{}) + res := gapp.Deliver(tx) + if expPass { + require.Equal(t, sdk.ABCICodeOK, res.Code, res.Log) + } else { + require.NotEqual(t, sdk.ABCICodeOK, res.Code, res.Log) + } + gapp.EndBlock(abci.RequestEndBlock{}) +} diff --git a/cmd/gaia/cli_test/cli_test.go b/cmd/gaia/cli_test/cli_test.go new file mode 100644 index 000000000..0d9a89578 --- /dev/null +++ b/cmd/gaia/cli_test/cli_test.go @@ -0,0 +1,184 @@ +package clitest + +import ( + "encoding/hex" + "encoding/json" + "fmt" + "strings" + "testing" + "time" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/cosmos/cosmos-sdk/client/keys" + "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/x/auth" + "github.com/cosmos/cosmos-sdk/x/stake" + crypto "github.com/tendermint/go-crypto" + crkeys "github.com/tendermint/go-crypto/keys" +) + +func TestGaiaCLISend(t *testing.T) { + + tests.ExecuteT(t, "gaiad unsafe_reset_all", 1) + pass := "1234567890" + executeWrite(t, "gaiacli keys delete foo", pass) + executeWrite(t, "gaiacli keys delete bar", pass) + masterKey, chainID := executeInit(t, "gaiad init") + + // get a free port, also setup some common flags + servAddr := server.FreeTCPAddr(t) + flags := fmt.Sprintf("--node=%v --chain-id=%v", servAddr, chainID) + + // start gaiad server + 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, _ := executeGetAddr(t, "gaiacli keys show foo --output=json") + barAddr, _ := executeGetAddr(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")) + + 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 + + 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")) +} + +func TestGaiaCLIDeclareCandidacy(t *testing.T) { + + tests.ExecuteT(t, "gaiad unsafe_reset_all", 1) + pass := "1234567890" + executeWrite(t, "gaiacli keys delete foo", pass) + masterKey, chainID := executeInit(t, "gaiad init") + + // get a free port, also setup some common flags + servAddr := server.FreeTCPAddr(t) + flags := fmt.Sprintf("--node=%v --chain-id=%v", servAddr, chainID) + + // start gaiad server + 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 := executeGetAddr(t, "gaiacli keys show foo --output=json") + fooAcc := executeGetAccount(t, fmt.Sprintf("gaiacli account %v %v", fooAddr, flags)) + assert.Equal(t, int64(100000), fooAcc.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(" --amount=%v", "3fermion") + declStr += fmt.Sprintf(" --moniker=%v", "foo-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) + assert.Equal(t, int64(3), candidate.Assets.Evaluate()) + + // TODO figure out why this times out with connection refused errors in go-bash + // 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()) +} + +func executeWrite(t *testing.T, cmdStr string, writes ...string) { + cmd, wc, _ := tests.GoExecuteT(t, cmdStr) + + for _, write := range writes { + _, err := wc.Write([]byte(write + "\n")) + require.NoError(t, err) + } + cmd.Wait() +} + +func executeWritePrint(t *testing.T, cmdStr string, writes ...string) { + cmd, wc, rc := tests.GoExecuteT(t, cmdStr) + + for _, write := range writes { + _, err := wc.Write([]byte(write + "\n")) + require.NoError(t, err) + } + cmd.Wait() + + bz := make([]byte, 100000) + rc.Read(bz) + 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 + + var initRes map[string]json.RawMessage + err := json.Unmarshal([]byte(outCut), &initRes) + require.NoError(t, err) + err = json.Unmarshal(initRes["secret"], &masterKey) + require.NoError(t, err) + err = json.Unmarshal(initRes["chain_id"], &chainID) + require.NoError(t, err) + return +} + +func executeGetAddr(t *testing.T, cmdStr string) (addr, pubKey string) { + out := tests.ExecuteT(t, cmdStr, 2) + var info crkeys.Info + keys.UnmarshalJSON([]byte(out), &info) + pubKey = hex.EncodeToString(info.PubKey.(crypto.PubKeyEd25519).Bytes()) + + // TODO this is really wierd, also error that not 64 characters! + pubKey = strings.TrimLeft(pubKey, "1624de6220") + pubKey = fmt.Sprintf("%064v", pubKey) + + fmt.Printf("debug pubKey: %v\n", pubKey) + addr = info.PubKey.Address().String() + fmt.Printf("debug addr: %v\n", addr) + return +} + +func executeGetAccount(t *testing.T, cmdStr string) auth.BaseAccount { + out := tests.ExecuteT(t, cmdStr, 2) + 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 + 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) + var candidate stake.Candidate + cdc := app.MakeCodec() + err := cdc.UnmarshalJSON([]byte(out), &candidate) + require.NoError(t, err, "out %v, err %v", out, err) + return candidate +} diff --git a/cmd/gaiacli/main.go b/cmd/gaia/cmd/gaiacli/main.go similarity index 70% rename from cmd/gaiacli/main.go rename to cmd/gaia/cmd/gaiacli/main.go index 1c71d7829..1e5528308 100644 --- a/cmd/gaiacli/main.go +++ b/cmd/gaia/cmd/gaiacli/main.go @@ -17,14 +17,11 @@ import ( authcmd "github.com/cosmos/cosmos-sdk/x/auth/commands" bankcmd "github.com/cosmos/cosmos-sdk/x/bank/commands" ibccmd "github.com/cosmos/cosmos-sdk/x/ibc/commands" - simplestakingcmd "github.com/cosmos/cosmos-sdk/x/simplestake/commands" + stakecmd "github.com/cosmos/cosmos-sdk/x/stake/commands" - "github.com/cosmos/cosmos-sdk/examples/basecoin/app" - "github.com/cosmos/cosmos-sdk/examples/basecoin/types" + "github.com/cosmos/cosmos-sdk/cmd/gaia/app" ) -// TODO: distinguish from basecli - // rootCmd is the entry point for this binary var ( rootCmd = &cobra.Command{ @@ -34,10 +31,7 @@ var ( ) func main() { - // disable sorting cobra.EnableCommandSorting = false - - // get the codec cdc := app.MakeCodec() // TODO: setup keybase, viper object, etc. to be passed into @@ -53,24 +47,21 @@ func main() { // add query/post commands (custom to binary) rootCmd.AddCommand( client.GetCommands( - authcmd.GetAccountCmd("main", cdc, types.GetAccountDecoder(cdc)), + authcmd.GetAccountCmd("main", cdc, authcmd.GetAccountDecoder(cdc)), + stakecmd.GetCmdQueryCandidate("stake", cdc), + //stakecmd.GetCmdQueryCandidates("stake", cdc), + stakecmd.GetCmdQueryDelegatorBond("stake", cdc), + //stakecmd.GetCmdQueryDelegatorBonds("stake", cdc), )...) rootCmd.AddCommand( client.PostCommands( bankcmd.SendTxCmd(cdc), - )...) - rootCmd.AddCommand( - client.PostCommands( ibccmd.IBCTransferCmd(cdc), - )...) - rootCmd.AddCommand( - client.PostCommands( ibccmd.IBCRelayCmd(cdc), - simplestakingcmd.BondTxCmd(cdc), - )...) - rootCmd.AddCommand( - client.PostCommands( - simplestakingcmd.UnbondTxCmd(cdc), + stakecmd.GetCmdDeclareCandidacy(cdc), + stakecmd.GetCmdEditCandidacy(cdc), + stakecmd.GetCmdDelegate(cdc), + stakecmd.GetCmdUnbond(cdc), )...) // add proxy, version and key info @@ -83,6 +74,6 @@ func main() { ) // prepare and add flags - executor := cli.PrepareMainCmd(rootCmd, "BC", os.ExpandEnv("$HOME/.gaiacli")) + executor := cli.PrepareMainCmd(rootCmd, "GA", os.ExpandEnv("$HOME/.gaiacli")) executor.Execute() } diff --git a/cmd/gaiad/main.go b/cmd/gaia/cmd/gaiad/main.go similarity index 74% rename from cmd/gaiad/main.go rename to cmd/gaia/cmd/gaiad/main.go index 9ebf196db..199a06152 100644 --- a/cmd/gaiad/main.go +++ b/cmd/gaia/cmd/gaiad/main.go @@ -11,7 +11,7 @@ import ( dbm "github.com/tendermint/tmlibs/db" "github.com/tendermint/tmlibs/log" - "github.com/cosmos/cosmos-sdk/examples/basecoin/app" + "github.com/cosmos/cosmos-sdk/cmd/gaia/app" "github.com/cosmos/cosmos-sdk/server" ) @@ -25,21 +25,21 @@ var ( } ) -// TODO: distinguish from basecoin func generateApp(rootDir string, logger log.Logger) (abci.Application, error) { dataDir := filepath.Join(rootDir, "data") db, err := dbm.NewGoLevelDB("gaia", dataDir) if err != nil { return nil, err } - bapp := app.NewBasecoinApp(logger, db) + bapp := app.NewGaiaApp(logger, db) return bapp, nil } func main() { - server.AddCommands(rootCmd, server.DefaultGenAppState, generateApp, context) + server.AddCommands(rootCmd, app.DefaultGenAppState, generateApp, context) // prepare and add flags - executor := cli.PrepareBaseCmd(rootCmd, "GA", os.ExpandEnv("$HOME/.gaiad")) + rootDir := os.ExpandEnv("$HOME/.gaiad") + executor := cli.PrepareBaseCmd(rootCmd, "GA", rootDir) executor.Execute() } diff --git a/server/init.go b/server/init.go index 8c82d2796..f2578cb19 100644 --- a/server/init.go +++ b/server/init.go @@ -70,7 +70,7 @@ func (c initCmd) run(cmd *cobra.Command, args []string) error { return nil } - // generate secrete and address + // generate secret and address addr, secret, err := GenerateCoinKey() if err != nil { return err diff --git a/tests/gobash.go b/tests/gobash.go new file mode 100644 index 000000000..c106c1f3f --- /dev/null +++ b/tests/gobash.go @@ -0,0 +1,54 @@ +package tests + +import ( + "fmt" + "io" + "os/exec" + "strings" + "testing" + "time" + + "github.com/stretchr/testify/require" +) + +func getCmd(t *testing.T, command string) *exec.Cmd { + + //split command into command and args + split := strings.Split(command, " ") + require.True(t, len(split) > 0, "no command provided") + + var cmd *exec.Cmd + if len(split) == 1 { + cmd = exec.Command(split[0]) + } else { + cmd = exec.Command(split[0], split[1:]...) + } + return 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) { + 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) + } + require.NoError(t, err, string(bz)) + out = strings.Trim(string(bz), "\n") //trim any new lines + time.Sleep(time.Second) + return out +} + +// Asynchronously execute the command, return standard output and error +func GoExecuteT(t *testing.T, command string) (cmd *exec.Cmd, pipeIn io.WriteCloser, pipeOut io.ReadCloser) { + cmd = getCmd(t, command) + pipeIn, err := cmd.StdinPipe() + require.NoError(t, err) + pipeOut, err = cmd.StdoutPipe() + require.NoError(t, err) + go cmd.Start() + time.Sleep(time.Second) + return cmd, pipeIn, pipeOut +} diff --git a/types/result.go b/types/result.go index 7b0c1a593..f4f7454e2 100644 --- a/types/result.go +++ b/types/result.go @@ -20,7 +20,7 @@ type Result struct { // GasWanted is the maximum units of work we allow this tx to perform. GasWanted int64 - // GasUsed is the amount of gas actually consumed. NOTE: not used. + // GasUsed is the amount of gas actually consumed. NOTE: unimplemented GasUsed int64 // Tx fee amount and denom. diff --git a/x/bank/wire.go b/x/bank/wire.go index 846103a52..c53f06564 100644 --- a/x/bank/wire.go +++ b/x/bank/wire.go @@ -6,7 +6,6 @@ import ( // Register concrete types on wire codec func RegisterWire(cdc *wire.Codec) { - // TODO include option to always include prefix bytes. - //cdc.RegisterConcrete(SendMsg{}, "github.com/cosmos/cosmos-sdk/bank/SendMsg", nil) - //cdc.RegisterConcrete(IssueMsg{}, "github.com/cosmos/cosmos-sdk/bank/IssueMsg", nil) + cdc.RegisterConcrete(SendMsg{}, "cosmos-sdk/Send", nil) + cdc.RegisterConcrete(IssueMsg{}, "cosmos-sdk/Issue", nil) } diff --git a/x/ibc/wire.go b/x/ibc/wire.go index 91e6d88bb..f5644acc5 100644 --- a/x/ibc/wire.go +++ b/x/ibc/wire.go @@ -6,6 +6,6 @@ import ( // Register concrete types on wire codec func RegisterWire(cdc *wire.Codec) { - //cdc.RegisterConcrete(IBCTransferMsg{}, "github.com/cosmos/cosmos-sdk/x/ibc/IBCTransferMsg", nil) - //cdc.RegisterConcrete(IBCReceiveMsg{}, "github.com/cosmos/cosmos-sdk/x/ibc/IBCReceiveMsg", nil) + cdc.RegisterConcrete(IBCTransferMsg{}, "cosmos-sdk/IBCTransferMsg", nil) + cdc.RegisterConcrete(IBCReceiveMsg{}, "cosmos-sdk/IBCReceiveMsg", nil) } diff --git a/x/stake/commands/flags.go b/x/stake/commands/flags.go new file mode 100644 index 000000000..e5b97d62c --- /dev/null +++ b/x/stake/commands/flags.go @@ -0,0 +1,41 @@ +package commands + +import ( + flag "github.com/spf13/pflag" +) + +// nolint +const ( + FlagAddressDelegator = "address-delegator" + FlagAddressCandidate = "address-candidate" + FlagPubKey = "pubkey" + FlagAmount = "amount" + FlagShares = "shares" + + FlagMoniker = "moniker" + FlagIdentity = "keybase-sig" + FlagWebsite = "website" + FlagDetails = "details" +) + +// common flagsets to add to various functions +var ( + fsPk = flag.NewFlagSet("", flag.ContinueOnError) + fsAmount = flag.NewFlagSet("", flag.ContinueOnError) + fsShares = flag.NewFlagSet("", flag.ContinueOnError) + fsDescription = flag.NewFlagSet("", flag.ContinueOnError) + fsCandidate = flag.NewFlagSet("", flag.ContinueOnError) + fsDelegator = flag.NewFlagSet("", flag.ContinueOnError) +) + +func init() { + fsPk.String(FlagPubKey, "", "PubKey of the validator-candidate") + fsAmount.String(FlagAmount, "1fermion", "Amount of coins to bond") + fsShares.String(FlagShares, "", "Amount of shares to unbond, either in decimal or keyword MAX (ex. 1.23456789, 99, MAX)") + fsDescription.String(FlagMoniker, "", "validator-candidate name") + fsDescription.String(FlagIdentity, "", "optional keybase signature") + fsDescription.String(FlagWebsite, "", "optional website") + fsDescription.String(FlagDetails, "", "optional details") + fsCandidate.String(FlagAddressCandidate, "", "hex address of the validator/candidate") + fsDelegator.String(FlagAddressDelegator, "", "hex address of the delegator") +} diff --git a/x/stake/commands/query.go b/x/stake/commands/query.go index 8cbb877ce..3bc2cffa7 100644 --- a/x/stake/commands/query.go +++ b/x/stake/commands/query.go @@ -5,7 +5,6 @@ import ( "fmt" "github.com/spf13/cobra" - flag "github.com/spf13/pflag" "github.com/spf13/viper" crypto "github.com/tendermint/go-crypto" @@ -16,76 +15,55 @@ import ( "github.com/cosmos/cosmos-sdk/x/stake" ) -// XXX remove dependancy -func PrefixedKey(app string, key []byte) []byte { - prefix := append([]byte(app), byte(0)) - return append(prefix, key...) -} +//// create command to query for all candidates +//func GetCmdQueryCandidates(storeName string, cdc *wire.Codec) *cobra.Command { +//cmd := &cobra.Command{ +//Use: "candidates", +//Short: "Query for the set of validator-candidates pubkeys", +//RunE: func(cmd *cobra.Command, args []string) error { -//nolint -var ( - fsValAddr = flag.NewFlagSet("", flag.ContinueOnError) - fsDelAddr = flag.NewFlagSet("", flag.ContinueOnError) - FlagValidatorAddr = "address" - FlagDelegatorAddr = "delegator-address" -) +//key := stake.CandidatesKey -func init() { - //Add Flags - fsValAddr.String(FlagValidatorAddr, "", "Address of the validator/candidate") - fsDelAddr.String(FlagDelegatorAddr, "", "Delegator hex address") +//ctx := context.NewCoreContextFromViper() +//res, err := ctx.Query(key, storeName) +//if err != nil { +//return err +//} -} +//// parse out the candidates +//candidates := new(stake.Candidates) +//err = cdc.UnmarshalBinary(res, candidates) +//if err != nil { +//return err +//} +//output, err := wire.MarshalJSONIndent(cdc, candidates) +//if err != nil { +//return err +//} +//fmt.Println(string(output)) +//return nil -// create command to query for all candidates -func GetCmdQueryCandidates(cdc *wire.Codec, storeName string) *cobra.Command { - cmd := &cobra.Command{ - Use: "candidates", - Short: "Query for the set of validator-candidates pubkeys", - RunE: func(cmd *cobra.Command, args []string) error { +//// TODO output with proofs / machine parseable etc. +//}, +//} - key := PrefixedKey(stake.MsgType, stake.CandidatesKey) - - ctx := context.NewCoreContextFromViper() - res, err := ctx.Query(key, storeName) - if err != nil { - return err - } - - // parse out the candidates - candidates := new(stake.Candidates) - err = cdc.UnmarshalJSON(res, candidates) - if err != nil { - return err - } - output, err := wire.MarshalJSONIndent(cdc, candidates) - if err != nil { - return err - } - fmt.Println(string(output)) - return nil - - // TODO output with proofs / machine parseable etc. - }, - } - - cmd.Flags().AddFlagSet(fsDelAddr) - return cmd -} +//cmd.Flags().AddFlagSet(fsDelegator) +//return cmd +//} // get the command to query a candidate -func GetCmdQueryCandidate(cdc *wire.Codec, storeName string) *cobra.Command { +func GetCmdQueryCandidate(storeName string, cdc *wire.Codec) *cobra.Command { cmd := &cobra.Command{ Use: "candidate", Short: "Query a validator-candidate account", RunE: func(cmd *cobra.Command, args []string) error { - addr, err := sdk.GetAddress(viper.GetString(FlagValidatorAddr)) + addr, err := sdk.GetAddress(viper.GetString(FlagAddressCandidate)) if err != nil { return err } - key := PrefixedKey(stake.MsgType, stake.GetCandidateKey(addr)) + key := stake.GetCandidateKey(addr) ctx := context.NewCoreContextFromViper() @@ -111,29 +89,29 @@ func GetCmdQueryCandidate(cdc *wire.Codec, storeName string) *cobra.Command { }, } - cmd.Flags().AddFlagSet(fsValAddr) + cmd.Flags().AddFlagSet(fsCandidate) return cmd } // get the command to query a single delegator bond -func GetCmdQueryDelegatorBond(cdc *wire.Codec, storeName string) *cobra.Command { +func GetCmdQueryDelegatorBond(storeName string, cdc *wire.Codec) *cobra.Command { cmd := &cobra.Command{ Use: "delegator-bond", Short: "Query a delegators bond based on address and candidate pubkey", RunE: func(cmd *cobra.Command, args []string) error { - addr, err := sdk.GetAddress(viper.GetString(FlagValidatorAddr)) + addr, err := sdk.GetAddress(viper.GetString(FlagAddressCandidate)) if err != nil { return err } - bz, err := hex.DecodeString(viper.GetString(FlagDelegatorAddr)) + bz, err := hex.DecodeString(viper.GetString(FlagAddressDelegator)) if err != nil { return err } delegator := crypto.Address(bz) - key := PrefixedKey(stake.MsgType, stake.GetDelegatorBondKey(delegator, addr, cdc)) + key := stake.GetDelegatorBondKey(delegator, addr, cdc) ctx := context.NewCoreContextFromViper() @@ -143,7 +121,7 @@ func GetCmdQueryDelegatorBond(cdc *wire.Codec, storeName string) *cobra.Command } // parse out the bond - var bond stake.DelegatorBond + bond := new(stake.DelegatorBond) err = cdc.UnmarshalBinary(res, bond) if err != nil { return err @@ -159,49 +137,49 @@ func GetCmdQueryDelegatorBond(cdc *wire.Codec, storeName string) *cobra.Command }, } - cmd.Flags().AddFlagSet(fsValAddr) - cmd.Flags().AddFlagSet(fsDelAddr) + cmd.Flags().AddFlagSet(fsCandidate) + cmd.Flags().AddFlagSet(fsDelegator) return cmd } -// get the command to query all the candidates bonded to a delegator -func GetCmdQueryDelegatorBonds(cdc *wire.Codec, storeName string) *cobra.Command { - cmd := &cobra.Command{ - Use: "delegator-candidates", - Short: "Query all delegators candidates' pubkeys based on address", - RunE: func(cmd *cobra.Command, args []string) error { +//// get the command to query all the candidates bonded to a delegator +//func GetCmdQueryDelegatorBonds(storeName string, cdc *wire.Codec) *cobra.Command { +//cmd := &cobra.Command{ +//Use: "delegator-candidates", +//Short: "Query all delegators bond's candidate-addresses based on delegator-address", +//RunE: func(cmd *cobra.Command, args []string) error { - bz, err := hex.DecodeString(viper.GetString(FlagDelegatorAddr)) - if err != nil { - return err - } - delegator := crypto.Address(bz) +//bz, err := hex.DecodeString(viper.GetString(FlagAddressDelegator)) +//if err != nil { +//return err +//} +//delegator := crypto.Address(bz) - key := PrefixedKey(stake.MsgType, stake.GetDelegatorBondsKey(delegator, cdc)) +//key := stake.GetDelegatorBondsKey(delegator, cdc) - ctx := context.NewCoreContextFromViper() +//ctx := context.NewCoreContextFromViper() - res, err := ctx.Query(key, storeName) - if err != nil { - return err - } +//res, err := ctx.Query(key, storeName) +//if err != nil { +//return err +//} - // parse out the candidates list - var candidates []crypto.PubKey - err = cdc.UnmarshalBinary(res, candidates) - if err != nil { - return err - } - output, err := wire.MarshalJSONIndent(cdc, candidates) - if err != nil { - return err - } - fmt.Println(string(output)) - return nil +//// parse out the candidates list +//var candidates []crypto.PubKey +//err = cdc.UnmarshalBinary(res, candidates) +//if err != nil { +//return err +//} +//output, err := wire.MarshalJSONIndent(cdc, candidates) +//if err != nil { +//return err +//} +//fmt.Println(string(output)) +//return nil - // TODO output with proofs / machine parseable etc. - }, - } - cmd.Flags().AddFlagSet(fsDelAddr) - return cmd -} +//// TODO output with proofs / machine parseable etc. +//}, +//} +//cmd.Flags().AddFlagSet(fsDelegator) +//return cmd +//} diff --git a/x/stake/commands/tx.go b/x/stake/commands/tx.go index 5cc9747b6..4a1983743 100644 --- a/x/stake/commands/tx.go +++ b/x/stake/commands/tx.go @@ -5,12 +5,10 @@ import ( "fmt" "github.com/spf13/cobra" - flag "github.com/spf13/pflag" "github.com/spf13/viper" crypto "github.com/tendermint/go-crypto" - "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/client/context" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/wire" @@ -18,51 +16,6 @@ import ( "github.com/cosmos/cosmos-sdk/x/stake" ) -// nolint -const ( - FlagAddressDelegator = "addressD" - FlagAddressCandidate = "addressC" - FlagPubKey = "pubkey" - FlagAmount = "amount" - FlagShares = "shares" - - FlagMoniker = "moniker" - FlagIdentity = "keybase-sig" - FlagWebsite = "website" - FlagDetails = "details" -) - -// common flagsets to add to various functions -var ( - fsPk = flag.NewFlagSet("", flag.ContinueOnError) - fsAmount = flag.NewFlagSet("", flag.ContinueOnError) - fsShares = flag.NewFlagSet("", flag.ContinueOnError) - fsCandidate = flag.NewFlagSet("", flag.ContinueOnError) - fsDelegator = flag.NewFlagSet("", flag.ContinueOnError) -) - -func init() { - fsPk.String(FlagPubKey, "", "PubKey of the validator-candidate") - fsAmount.String(FlagAmount, "1fermion", "Amount of coins to bond") - fsShares.String(FlagShares, "", "Amount of shares to unbond, either in decimal or keyword MAX (ex. 1.23456789, 99, MAX)") - fsCandidate.String(FlagMoniker, "", "validator-candidate name") - fsCandidate.String(FlagIdentity, "", "optional keybase signature") - fsCandidate.String(FlagWebsite, "", "optional website") - fsCandidate.String(FlagAddressCandidate, "", "hex address of the validator/candidate") - fsDelegator.String(FlagAddressDelegator, "", "hex address of the delegator") -} - -//TODO refactor to common functionality -func getNamePassword() (name, passphrase string, err error) { - name = viper.GetString(client.FlagName) - buf := client.BufferStdin() - prompt := fmt.Sprintf("Password to sign with '%s':", name) - passphrase, err = client.GetPassword(prompt, buf) - return -} - -//_________________________________________________________________________________________ - // create declare candidacy command func GetCmdDeclareCandidacy(cdc *wire.Codec) *cobra.Command { cmd := &cobra.Command{ @@ -113,6 +66,7 @@ func GetCmdDeclareCandidacy(cdc *wire.Codec) *cobra.Command { cmd.Flags().AddFlagSet(fsPk) cmd.Flags().AddFlagSet(fsAmount) + cmd.Flags().AddFlagSet(fsDescription) cmd.Flags().AddFlagSet(fsCandidate) return cmd } @@ -155,7 +109,7 @@ func GetCmdEditCandidacy(cdc *wire.Codec) *cobra.Command { }, } - cmd.Flags().AddFlagSet(fsPk) + cmd.Flags().AddFlagSet(fsDescription) cmd.Flags().AddFlagSet(fsCandidate) return cmd } @@ -198,9 +152,9 @@ func GetCmdDelegate(cdc *wire.Codec) *cobra.Command { }, } - cmd.Flags().AddFlagSet(fsPk) cmd.Flags().AddFlagSet(fsAmount) cmd.Flags().AddFlagSet(fsDelegator) + cmd.Flags().AddFlagSet(fsCandidate) return cmd } @@ -252,9 +206,9 @@ func GetCmdUnbond(cdc *wire.Codec) *cobra.Command { }, } - cmd.Flags().AddFlagSet(fsPk) cmd.Flags().AddFlagSet(fsShares) cmd.Flags().AddFlagSet(fsDelegator) + cmd.Flags().AddFlagSet(fsCandidate) return cmd } @@ -269,9 +223,11 @@ func GetPubKey(pubKeyStr string) (pk crypto.PubKey, err error) { return } if len(pubKeyStr) != 64 { //if len(pkBytes) != 32 { - err = fmt.Errorf("pubkey must be Ed25519 hex encoded string which is 64 characters long") + err = fmt.Errorf("pubkey must be Ed25519 hex encoded string which is 64 characters, this pubkey is %v characters", len(pubKeyStr)) return } + + // TODO: bech32 ... var pkBytes []byte pkBytes, err = hex.DecodeString(pubKeyStr) if err != nil { diff --git a/x/stake/handler.go b/x/stake/handler.go index 5f80ecb37..84eeca3c4 100644 --- a/x/stake/handler.go +++ b/x/stake/handler.go @@ -4,7 +4,6 @@ import ( "bytes" sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/x/bank" abci "github.com/tendermint/abci/types" ) @@ -18,7 +17,7 @@ const ( //_______________________________________________________________________ -func NewHandler(k Keeper, ck bank.CoinKeeper) sdk.Handler { +func NewHandler(k Keeper) sdk.Handler { return func(ctx sdk.Context, msg sdk.Msg) sdk.Result { // NOTE msg already has validate basic run switch msg := msg.(type) { @@ -36,7 +35,7 @@ func NewHandler(k Keeper, ck bank.CoinKeeper) sdk.Handler { } } -//_______________________________________________ +//_____________________________________________________________________ // NewEndBlocker generates sdk.EndBlocker // Performs tick functionality @@ -49,6 +48,14 @@ func NewEndBlocker(k Keeper) sdk.EndBlocker { //_____________________________________________________________________ +// InitGenesis - store genesis parameters +func InitGenesis(ctx sdk.Context, k Keeper, data GenesisState) { + k.setPool(ctx, data.Pool) + k.setParams(ctx, data.Params) +} + +//_____________________________________________________________________ + // These functions assume everything has been authenticated, // now we just perform action and save @@ -92,9 +99,6 @@ func handleMsgEditCandidacy(ctx sdk.Context, msg MsgEditCandidacy, k Keeper) sdk GasUsed: GasEditCandidacy, } } - if candidate.Status == Unbonded { //candidate has been withdrawn - return ErrBondNotNominated(k.codespace).Result() - } // XXX move to types // replace all editable fields (clients should autofill existing values) @@ -136,7 +140,7 @@ func delegate(ctx sdk.Context, k Keeper, delegatorAddr sdk.Address, bondAmt sdk.Coin, candidate Candidate) sdk.Error { // Get or create the delegator bond - bond, found := k.getDelegatorBond(ctx, delegatorAddr, candidate.Address) + bond, found := k.GetDelegatorBond(ctx, delegatorAddr, candidate.Address) if !found { bond = DelegatorBond{ DelegatorAddr: delegatorAddr, @@ -163,7 +167,7 @@ func delegate(ctx sdk.Context, k Keeper, delegatorAddr sdk.Address, func handleMsgUnbond(ctx sdk.Context, msg MsgUnbond, k Keeper) sdk.Result { // check if bond has any shares in it unbond - bond, found := k.getDelegatorBond(ctx, msg.DelegatorAddr, msg.CandidateAddr) + bond, found := k.GetDelegatorBond(ctx, msg.DelegatorAddr, msg.CandidateAddr) if !found { return ErrNoDelegatorForAddress(k.codespace).Result() } @@ -171,11 +175,7 @@ func handleMsgUnbond(ctx sdk.Context, msg MsgUnbond, k Keeper) sdk.Result { return ErrInsufficientFunds(k.codespace).Result() } - // test getting rational number from decimal provided - shares, err := sdk.NewRatFromDecimal(msg.Shares) - if err != nil { - return err.Result() - } + var shares sdk.Rat // test that there are enough shares to unbond if msg.Shares == "MAX" { @@ -183,6 +183,11 @@ func handleMsgUnbond(ctx sdk.Context, msg MsgUnbond, k Keeper) sdk.Result { return ErrNotEnoughBondShares(k.codespace, msg.Shares).Result() } } else { + var err sdk.Error + shares, err = sdk.NewRatFromDecimal(msg.Shares) + if err != nil { + return err.Result() + } if bond.Shares.LT(shares) { return ErrNotEnoughBondShares(k.codespace, msg.Shares).Result() } @@ -253,40 +258,3 @@ func handleMsgUnbond(ctx sdk.Context, msg MsgUnbond, k Keeper) sdk.Result { k.setPool(ctx, p) return sdk.Result{} } - -// TODO use or remove -//// Perform all the actions required to bond tokens to a delegator bond from their account -//func BondCoins(ctx sdk.Context, k Keeper, bond DelegatorBond, -//candidate Candidate, amount sdk.Coin) (DelegatorBond, Candidate, Pool, sdk.Error) { - -//pool := k.GetPool(ctx) -//_, err := k.coinKeeper.SubtractCoins(ctx, bond.DelegatorAddr, sdk.Coins{amount}) -//if err != nil { -//return bond, candidate, pool, err -//} -//pool, candidate, newShares := pool.candidateAddTokens(candidate, amount.Amount) -//bond.Shares = bond.Shares.Add(newShares) -//return bond, candidate, pool, nil -//} -//// Perform all the actions required to bond tokens to a delegator bond from their account -//func UnbondCoins(ctx sdk.Context, k Keeper, bond DelegatorBond, -//candidate Candidate, shares sdk.Rat) (DelegatorBond, Candidate, Pool, sdk.Error) { - -//pool := k.GetPool(ctx) - -//// subtract bond tokens from delegator bond -//if bond.Shares.LT(shares) { -//errMsg := fmt.Sprintf("cannot unbond %v shares, only have %v shares available", shares, bond.Shares) -//return bond, candidate, pool, sdk.ErrInsufficientFunds(errMsg) -//} -//bond.Shares = bond.Shares.Sub(shares) - -//pool, candidate, returnAmount := p.candidateRemoveShares(candidate, shares) -//returnCoins := sdk.Coins{{k.GetParams(ctx).BondDenom, returnAmount}} - -//_, err := k.coinKeeper.AddCoins(ctx, candidate.Address, returnCoins) -//if err != nil { -//return err -//} -//return bond, candidate, pool, nil -//} diff --git a/x/stake/handler_test.go b/x/stake/handler_test.go index 1f0bc6415..538c664e9 100644 --- a/x/stake/handler_test.go +++ b/x/stake/handler_test.go @@ -83,7 +83,7 @@ func TestIncrementsMsgDelegate(t *testing.T) { //Check that the accounts and the bond account have the appropriate values candidate, found := keeper.GetCandidate(ctx, candidateAddr) require.True(t, found) - bond, found := keeper.getDelegatorBond(ctx, delegatorAddr, candidateAddr) + bond, found := keeper.GetDelegatorBond(ctx, delegatorAddr, candidateAddr) require.True(t, found) expBond := int64(i+1) * bondAmount @@ -139,7 +139,7 @@ func TestIncrementsMsgUnbond(t *testing.T) { //Check that the accounts and the bond account have the appropriate values candidate, found = keeper.GetCandidate(ctx, candidateAddr) require.True(t, found) - bond, found := keeper.getDelegatorBond(ctx, delegatorAddr, candidateAddr) + bond, found := keeper.GetDelegatorBond(ctx, delegatorAddr, candidateAddr) require.True(t, found) expBond := initBond - int64(i+1)*unbondShares @@ -254,7 +254,7 @@ func TestMultipleMsgDelegate(t *testing.T) { require.True(t, got.IsOK(), "expected msg %d to be ok, got %v", i, got) //Check that the account is bonded - bond, found := keeper.getDelegatorBond(ctx, delegatorAddr, candidateAddr) + bond, found := keeper.GetDelegatorBond(ctx, delegatorAddr, candidateAddr) require.True(t, found) require.NotNil(t, bond, "expected delegatee bond %d to exist", bond) } @@ -266,7 +266,7 @@ func TestMultipleMsgDelegate(t *testing.T) { require.True(t, got.IsOK(), "expected msg %d to be ok, got %v", i, got) //Check that the account is unbonded - _, found := keeper.getDelegatorBond(ctx, delegatorAddr, candidateAddr) + _, found := keeper.GetDelegatorBond(ctx, delegatorAddr, candidateAddr) require.False(t, found) } } diff --git a/x/stake/keeper.go b/x/stake/keeper.go index ae3dfb7c7..751b84017 100644 --- a/x/stake/keeper.go +++ b/x/stake/keeper.go @@ -2,7 +2,6 @@ package stake import ( "bytes" - "encoding/json" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/wire" @@ -24,7 +23,7 @@ type Keeper struct { codespace sdk.CodespaceType } -func NewKeeper(ctx sdk.Context, cdc *wire.Codec, key sdk.StoreKey, ck bank.CoinKeeper, codespace sdk.CodespaceType) Keeper { +func NewKeeper(cdc *wire.Codec, key sdk.StoreKey, ck bank.CoinKeeper, codespace sdk.CodespaceType) Keeper { keeper := Keeper{ storeKey: key, cdc: cdc, @@ -34,17 +33,6 @@ func NewKeeper(ctx sdk.Context, cdc *wire.Codec, key sdk.StoreKey, ck bank.CoinK return keeper } -// InitGenesis - store genesis parameters -func (k Keeper) InitGenesis(ctx sdk.Context, data json.RawMessage) error { - var state GenesisState - if err := json.Unmarshal(data, &state); err != nil { - return err - } - k.setPool(ctx, state.Pool) - k.setParams(ctx, state.Params) - return nil -} - //_________________________________________________________________________ // get a single candidate @@ -309,7 +297,8 @@ func (k Keeper) clearAccUpdateValidators(ctx sdk.Context) { //_____________________________________________________________________ -func (k Keeper) getDelegatorBond(ctx sdk.Context, +// load a delegator bong +func (k Keeper) GetDelegatorBond(ctx sdk.Context, delegatorAddr, candidateAddr sdk.Address) (bond DelegatorBond, found bool) { store := ctx.KVStore(k.storeKey) @@ -326,7 +315,7 @@ func (k Keeper) getDelegatorBond(ctx sdk.Context, } // load all bonds of a delegator -func (k Keeper) getDelegatorBonds(ctx sdk.Context, delegator sdk.Address, maxRetrieve int16) (bonds []DelegatorBond) { +func (k Keeper) GetDelegatorBonds(ctx sdk.Context, delegator sdk.Address, maxRetrieve int16) (bonds []DelegatorBond) { store := ctx.KVStore(k.storeKey) delegatorPrefixKey := GetDelegatorBondsKey(delegator, k.cdc) iterator := store.Iterator(subspace(delegatorPrefixKey)) //smallest to largest diff --git a/x/stake/keeper_test.go b/x/stake/keeper_test.go index 088bc5e30..e01df1aae 100644 --- a/x/stake/keeper_test.go +++ b/x/stake/keeper_test.go @@ -2,11 +2,9 @@ package stake import ( "bytes" - "encoding/json" "testing" sdk "github.com/cosmos/cosmos-sdk/types" - crypto "github.com/tendermint/go-crypto" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -128,19 +126,19 @@ func TestBond(t *testing.T) { } // check the empty keeper first - _, found := keeper.getDelegatorBond(ctx, addrDels[0], addrVals[0]) + _, found := keeper.GetDelegatorBond(ctx, addrDels[0], addrVals[0]) assert.False(t, found) // set and retrieve a record keeper.setDelegatorBond(ctx, bond1to1) - resBond, found := keeper.getDelegatorBond(ctx, addrDels[0], addrVals[0]) + resBond, found := keeper.GetDelegatorBond(ctx, addrDels[0], addrVals[0]) assert.True(t, found) assert.True(t, bondsEqual(bond1to1, resBond)) // modify a records, save, and retrieve bond1to1.Shares = sdk.NewRat(99) keeper.setDelegatorBond(ctx, bond1to1) - resBond, found = keeper.getDelegatorBond(ctx, addrDels[0], addrVals[0]) + resBond, found = keeper.GetDelegatorBond(ctx, addrDels[0], addrVals[0]) assert.True(t, found) assert.True(t, bondsEqual(bond1to1, resBond)) @@ -159,16 +157,16 @@ func TestBond(t *testing.T) { keeper.setDelegatorBond(ctx, bond2to3) // test all bond retrieve capabilities - resBonds := keeper.getDelegatorBonds(ctx, addrDels[0], 5) + resBonds := keeper.GetDelegatorBonds(ctx, addrDels[0], 5) require.Equal(t, 3, len(resBonds)) assert.True(t, bondsEqual(bond1to1, resBonds[0])) assert.True(t, bondsEqual(bond1to2, resBonds[1])) assert.True(t, bondsEqual(bond1to3, resBonds[2])) - resBonds = keeper.getDelegatorBonds(ctx, addrDels[0], 3) + resBonds = keeper.GetDelegatorBonds(ctx, addrDels[0], 3) require.Equal(t, 3, len(resBonds)) - resBonds = keeper.getDelegatorBonds(ctx, addrDels[0], 2) + resBonds = keeper.GetDelegatorBonds(ctx, addrDels[0], 2) require.Equal(t, 2, len(resBonds)) - resBonds = keeper.getDelegatorBonds(ctx, addrDels[1], 5) + resBonds = keeper.GetDelegatorBonds(ctx, addrDels[1], 5) require.Equal(t, 3, len(resBonds)) assert.True(t, bondsEqual(bond2to1, resBonds[0])) assert.True(t, bondsEqual(bond2to2, resBonds[1])) @@ -176,9 +174,9 @@ func TestBond(t *testing.T) { // delete a record keeper.removeDelegatorBond(ctx, bond2to3) - _, found = keeper.getDelegatorBond(ctx, addrDels[1], addrVals[2]) + _, found = keeper.GetDelegatorBond(ctx, addrDels[1], addrVals[2]) assert.False(t, found) - resBonds = keeper.getDelegatorBonds(ctx, addrDels[1], 5) + resBonds = keeper.GetDelegatorBonds(ctx, addrDels[1], 5) require.Equal(t, 2, len(resBonds)) assert.True(t, bondsEqual(bond2to1, resBonds[0])) assert.True(t, bondsEqual(bond2to2, resBonds[1])) @@ -186,11 +184,11 @@ func TestBond(t *testing.T) { // delete all the records from delegator 2 keeper.removeDelegatorBond(ctx, bond2to1) keeper.removeDelegatorBond(ctx, bond2to2) - _, found = keeper.getDelegatorBond(ctx, addrDels[1], addrVals[0]) + _, found = keeper.GetDelegatorBond(ctx, addrDels[1], addrVals[0]) assert.False(t, found) - _, found = keeper.getDelegatorBond(ctx, addrDels[1], addrVals[1]) + _, found = keeper.GetDelegatorBond(ctx, addrDels[1], addrVals[1]) assert.False(t, found) - resBonds = keeper.getDelegatorBonds(ctx, addrDels[1], 5) + resBonds = keeper.GetDelegatorBonds(ctx, addrDels[1], 5) require.Equal(t, 0, len(resBonds)) } @@ -316,15 +314,6 @@ func TestGetAccUpdateValidators(t *testing.T) { } } - // to compare pubkeys between abci pubkey and crypto.PubKey - wirePK := func(pk crypto.PubKey) []byte { - pkBytes, err := keeper.cdc.MarshalBinary(pk) - if err != nil { - panic(err) - } - return pkBytes - } - // test from nothing to something // candidate set: {} -> {c1, c3} // validator set: {} -> {c1, c3} @@ -479,7 +468,7 @@ func TestGetAccUpdateValidators(t *testing.T) { acc = keeper.getAccUpdateValidators(ctx) require.Equal(t, 2, len(acc), "%v", acc) - assert.Equal(t, wirePK(candidatesIn[0].PubKey), acc[0].PubKey) + assert.Equal(t, candidatesIn[0].PubKey.Bytes(), acc[0].PubKey) assert.Equal(t, int64(0), acc[0].Power) assert.Equal(t, vals[0].abciValidator(keeper.cdc), acc[1]) @@ -504,10 +493,10 @@ func TestGetAccUpdateValidators(t *testing.T) { require.Equal(t, 0, len(candidates)) acc = keeper.getAccUpdateValidators(ctx) require.Equal(t, 4, len(acc)) - assert.Equal(t, wirePK(candidatesIn[1].PubKey), acc[0].PubKey) - assert.Equal(t, wirePK(candidatesIn[2].PubKey), acc[1].PubKey) - assert.Equal(t, wirePK(candidatesIn[3].PubKey), acc[2].PubKey) - assert.Equal(t, wirePK(candidatesIn[4].PubKey), acc[3].PubKey) + assert.Equal(t, candidatesIn[1].PubKey.Bytes(), acc[0].PubKey) + assert.Equal(t, candidatesIn[2].PubKey.Bytes(), acc[1].PubKey) + assert.Equal(t, candidatesIn[3].PubKey.Bytes(), acc[2].PubKey) + assert.Equal(t, candidatesIn[4].PubKey.Bytes(), acc[3].PubKey) assert.Equal(t, int64(0), acc[0].Power) assert.Equal(t, int64(0), acc[1].Power) assert.Equal(t, int64(0), acc[2].Power) @@ -584,31 +573,3 @@ func TestPool(t *testing.T) { resPool = keeper.GetPool(ctx) assert.Equal(t, expPool, resPool) } - -func TestInitGenesis(t *testing.T) { - ctx, _, keeper := createTestInput(t, false, 0) - jsonStr := `{ - "params": { - "inflation_rate_change": {"num": 13, "denom": 100}, - "inflation_max": {"num": 20, "denom": 100}, - "inflation_min": {"num": 7, "denom": 100}, - "goal_bonded": {"num": 67, "denom": 100}, - "max_validators": 100, - "bond_denom": "fermion" - }, - "pool": { - "total_supply": 0, - "bonded_shares": {"num": 0, "denom": 1}, - "unbonded_shares": {"num": 0, "denom": 1}, - "bonded_pool": 0, - "unbonded_pool": 0, - "inflation_last_time": 0, - "inflation": {"num": 7, "denom": 100} - } -}` - encoded := json.RawMessage(jsonStr) - err := keeper.InitGenesis(ctx, encoded) - require.Nil(t, err) - require.Equal(t, keeper.GetPool(ctx), initialPool()) - require.Equal(t, keeper.GetParams(ctx), defaultParams()) -} diff --git a/x/stake/test_common.go b/x/stake/test_common.go index 6470abbc7..1de86c912 100644 --- a/x/stake/test_common.go +++ b/x/stake/test_common.go @@ -75,6 +75,14 @@ func initialPool() Pool { } } +// get raw genesis raw message for testing +func GetDefaultGenesisState() GenesisState { + return GenesisState{ + Pool: initialPool(), + Params: defaultParams(), + } +} + // XXX reference the common declaration of this function func subspace(prefix []byte) (start, end []byte) { end = make([]byte, len(prefix)) @@ -132,7 +140,7 @@ func createTestInput(t *testing.T, isCheckTx bool, initCoins int64) (sdk.Context &auth.BaseAccount{}, // prototype ).Seal() ck := bank.NewCoinKeeper(accountMapper) - keeper := NewKeeper(ctx, cdc, keyStake, ck, DefaultCodespace) + keeper := NewKeeper(cdc, keyStake, ck, DefaultCodespace) keeper.setPool(ctx, initialPool()) keeper.setParams(ctx, defaultParams()) diff --git a/x/stake/types.go b/x/stake/types.go index 1154f7962..9f7d97ae5 100644 --- a/x/stake/types.go +++ b/x/stake/types.go @@ -7,6 +7,14 @@ import ( crypto "github.com/tendermint/go-crypto" ) +// GenesisState - all staking state that must be provided at genesis +type GenesisState struct { + Pool Pool `json:"pool"` + Params Params `json:"params"` +} + +//_________________________________________________________________________ + // Params defines the high level settings for staking type Params struct { InflationRateChange sdk.Rat `json:"inflation_rate_change"` // maximum annual change in inflation rate @@ -31,13 +39,7 @@ type Pool struct { Inflation sdk.Rat `json:"inflation"` // current annual inflation rate } -// GenesisState - all staking state that must be provided at genesis -type GenesisState struct { - Pool Pool `json:"pool"` - Params Params `json:"params"` -} - -//_______________________________________________________________________________________________________ +//_________________________________________________________________________ // CandidateStatus - status of a validator-candidate type CandidateStatus byte @@ -65,6 +67,9 @@ type Candidate struct { Description Description `json:"description"` // Description terms for the candidate } +// Candidates - list of Candidates +type Candidates []Candidate + // NewCandidate - initialize a new candidate func NewCandidate(address sdk.Address, pubKey crypto.PubKey, description Description) Candidate { return Candidate{ @@ -126,12 +131,8 @@ type Validator struct { // abci validator from stake validator type func (v Validator) abciValidator(cdc *wire.Codec) abci.Validator { - pkBytes, err := cdc.MarshalBinary(v.PubKey) - if err != nil { - panic(err) - } return abci.Validator{ - PubKey: pkBytes, + PubKey: v.PubKey.Bytes(), Power: v.Power.Evaluate(), } } @@ -139,29 +140,20 @@ func (v Validator) abciValidator(cdc *wire.Codec) abci.Validator { // abci validator from stake validator type // with zero power used for validator updates func (v Validator) abciValidatorZero(cdc *wire.Codec) abci.Validator { - pkBytes, err := cdc.MarshalBinary(v.PubKey) - if err != nil { - panic(err) - } return abci.Validator{ - PubKey: pkBytes, + PubKey: v.PubKey.Bytes(), Power: 0, } } //_________________________________________________________________________ -// Candidates - list of Candidates -type Candidates []Candidate - -//_________________________________________________________________________ - // DelegatorBond represents the bond with tokens held by an account. It is // owned by one delegator, and is associated with the voting power of one // pubKey. // TODO better way of managing space type DelegatorBond struct { - DelegatorAddr sdk.Address `json:"delegatoraddr"` + DelegatorAddr sdk.Address `json:"delegator_addr"` CandidateAddr sdk.Address `json:"candidate_addr"` Shares sdk.Rat `json:"shares"` } diff --git a/x/stake/wire.go b/x/stake/wire.go index 4516f89f2..e99c621fa 100644 --- a/x/stake/wire.go +++ b/x/stake/wire.go @@ -4,9 +4,10 @@ import ( "github.com/cosmos/cosmos-sdk/wire" ) -// XXX complete +// Register concrete types on wire codec func RegisterWire(cdc *wire.Codec) { - // TODO include option to always include prefix bytes. - //cdc.RegisterConcrete(SendMsg{}, "cosmos-sdk/SendMsg", nil) - //cdc.RegisterConcrete(IssueMsg{}, "cosmos-sdk/IssueMsg", nil) + cdc.RegisterConcrete(MsgDeclareCandidacy{}, "cosmos-sdk/MsgDeclareCandidacy", nil) + cdc.RegisterConcrete(MsgEditCandidacy{}, "cosmos-sdk/MsgEditCandidacy", nil) + cdc.RegisterConcrete(MsgDelegate{}, "cosmos-sdk/MsgDelegate", nil) + cdc.RegisterConcrete(MsgUnbond{}, "cosmos-sdk/MsgUnbond", nil) }