diff --git a/Makefile b/Makefile index b36ef61d4..9d4ad0a25 100644 --- a/Makefile +++ b/Makefile @@ -169,13 +169,13 @@ test_sim_gaia_nondeterminism: test_sim_gaia_fast: @echo "Running quick Gaia simulation. This may take several minutes..." - @go test ./cmd/gaia/app -run TestFullGaiaSimulation -SimulationEnabled=true -SimulationNumBlocks=400 -SimulationBlockSize=200 -SimulationCommit=true -SimulationSeed=9 -v -timeout 24h + @go test ./cmd/gaia/app -run TestFullGaiaSimulation -SimulationEnabled=true -SimulationNumBlocks=500 -SimulationBlockSize=200 -SimulationCommit=true -SimulationSeed=9 -v -timeout 24h test_sim_gaia_multi_seed: @echo "Running multi-seed Gaia simulation. This may take awhile!" - @bash scripts/multisim.sh 10 + @bash scripts/multisim.sh 25 -SIM_NUM_BLOCKS ?= 210 +SIM_NUM_BLOCKS ?= 500 SIM_BLOCK_SIZE ?= 200 SIM_COMMIT ?= true test_sim_gaia_benchmark: diff --git a/PENDING.md b/PENDING.md index 4d87301f0..4453ddf8e 100644 --- a/PENDING.md +++ b/PENDING.md @@ -18,6 +18,7 @@ FEATURES * Gaia REST API (`gaiacli advanced rest-server`) * Gaia CLI (`gaiacli`) + * [cli] [\#2569](https://github.com/cosmos/cosmos-sdk/pull/2569) Add commands to query validator unbondings and redelegations * Gaia @@ -35,6 +36,8 @@ IMPROVEMENTS * Gaia * SDK + - #2573 [x/distribution] add accum invariance + - #2610 [x/stake] Block redelegation to and from the same validator * Tendermint diff --git a/cmd/gaia/app/sim_test.go b/cmd/gaia/app/sim_test.go index 384afbf6d..9f94a82f7 100644 --- a/cmd/gaia/app/sim_test.go +++ b/cmd/gaia/app/sim_test.go @@ -4,6 +4,7 @@ import ( "encoding/json" "flag" "fmt" + "io/ioutil" "math/rand" "os" "testing" @@ -141,7 +142,7 @@ func BenchmarkFullGaiaSimulation(b *testing.B) { var logger log.Logger logger = log.NewNopLogger() var db dbm.DB - dir := os.TempDir() + dir, _ := ioutil.TempDir("", "goleveldb-gaia-sim") db, _ = dbm.NewGoLevelDB("Simulation", dir) defer func() { db.Close() @@ -183,7 +184,13 @@ func TestFullGaiaSimulation(t *testing.T) { } else { logger = log.NewNopLogger() } - db := dbm.NewMemDB() + var db dbm.DB + dir, _ := ioutil.TempDir("", "goleveldb-gaia-sim") + db, _ = dbm.NewGoLevelDB("Simulation", dir) + defer func() { + db.Close() + os.RemoveAll(dir) + }() app := NewGaiaApp(logger, db, nil) require.Equal(t, "GaiaApp", app.Name()) @@ -198,7 +205,11 @@ func TestFullGaiaSimulation(t *testing.T) { commit, ) if commit { - fmt.Println("Database Size", db.Stats()["database.size"]) + // for memdb: + // fmt.Println("Database Size", db.Stats()["database.size"]) + fmt.Println("GoLevelDB Stats") + fmt.Println(db.Stats()["leveldb.stats"]) + fmt.Println("GoLevelDB cached block size", db.Stats()["leveldb.cachedblock"]) } require.Nil(t, err) } diff --git a/cmd/gaia/cli_test/cli_test.go b/cmd/gaia/cli_test/cli_test.go index c375bcf4e..ad1549641 100644 --- a/cmd/gaia/cli_test/cli_test.go +++ b/cmd/gaia/cli_test/cli_test.go @@ -285,6 +285,12 @@ func TestGaiaCLICreateValidator(t *testing.T) { validator = executeGetValidator(t, fmt.Sprintf("gaiacli query validator %s --output=json %v", sdk.ValAddress(barAddr), flags)) require.Equal(t, "1.0000000000", validator.Tokens.String()) + validatorUbds := executeGetValidatorUnbondingDelegations(t, + fmt.Sprintf("gaiacli query unbonding-delegations-from %s --output=json %v", + sdk.ValAddress(barAddr), flags)) + require.Len(t, validatorUbds, 1) + require.Equal(t, "1", validatorUbds[0].Balance.Amount.String()) + params := executeGetParams(t, fmt.Sprintf("gaiacli query parameters --output=json %v", flags)) require.True(t, defaultParams.Equal(params)) @@ -693,6 +699,24 @@ func executeGetValidator(t *testing.T, cmdStr string) stake.Validator { return validator } +func executeGetValidatorUnbondingDelegations(t *testing.T, cmdStr string) []stake.UnbondingDelegation { + out, _ := tests.ExecuteT(t, cmdStr, "") + var ubds []stake.UnbondingDelegation + cdc := app.MakeCodec() + err := cdc.UnmarshalJSON([]byte(out), &ubds) + require.NoError(t, err, "out %v\n, err %v", out, err) + return ubds +} + +func executeGetValidatorRedelegations(t *testing.T, cmdStr string) []stake.Redelegation { + out, _ := tests.ExecuteT(t, cmdStr, "") + var reds []stake.Redelegation + cdc := app.MakeCodec() + err := cdc.UnmarshalJSON([]byte(out), &reds) + require.NoError(t, err, "out %v\n, err %v", out, err) + return reds +} + func executeGetPool(t *testing.T, cmdStr string) stake.Pool { out, _ := tests.ExecuteT(t, cmdStr, "") var pool stake.Pool diff --git a/cmd/gaia/cmd/gaiacli/main.go b/cmd/gaia/cmd/gaiacli/main.go index a2804706b..6f11e68a5 100644 --- a/cmd/gaia/cmd/gaiacli/main.go +++ b/cmd/gaia/cmd/gaiacli/main.go @@ -28,10 +28,11 @@ import ( ) const ( - storeAcc = "acc" - storeGov = "gov" - storeSlashing = "slashing" - storeStake = "stake" + storeAcc = "acc" + storeGov = "gov" + storeSlashing = "slashing" + storeStake = "stake" + queryRouteStake = "stake" ) // rootCmd is the entry point for this binary @@ -70,21 +71,23 @@ func main() { authcmd.GetAccountCmd(storeAcc, cdc, authcmd.GetAccountDecoder(cdc)), stakecmd.GetCmdQueryDelegation(storeStake, cdc), stakecmd.GetCmdQueryDelegations(storeStake, cdc), + stakecmd.GetCmdQueryUnbondingDelegation(storeStake, cdc), + stakecmd.GetCmdQueryUnbondingDelegations(storeStake, cdc), + stakecmd.GetCmdQueryRedelegation(storeStake, cdc), + stakecmd.GetCmdQueryRedelegations(storeStake, cdc), + stakecmd.GetCmdQueryValidator(storeStake, cdc), + stakecmd.GetCmdQueryValidators(storeStake, cdc), + stakecmd.GetCmdQueryValidatorUnbondingDelegations(queryRouteStake, cdc), + stakecmd.GetCmdQueryValidatorRedelegations(queryRouteStake, cdc), stakecmd.GetCmdQueryParams(storeStake, cdc), stakecmd.GetCmdQueryPool(storeStake, cdc), govcmd.GetCmdQueryProposal(storeGov, cdc), govcmd.GetCmdQueryProposals(storeGov, cdc), - govcmd.GetCmdQueryDeposit(storeGov, cdc), - govcmd.GetCmdQueryDeposits(storeGov, cdc), - stakecmd.GetCmdQueryRedelegation(storeStake, cdc), - stakecmd.GetCmdQueryRedelegations(storeStake, cdc), - slashingcmd.GetCmdQuerySigningInfo(storeSlashing, cdc), - stakecmd.GetCmdQueryUnbondingDelegation(storeStake, cdc), - stakecmd.GetCmdQueryUnbondingDelegations(storeStake, cdc), - stakecmd.GetCmdQueryValidator(storeStake, cdc), - stakecmd.GetCmdQueryValidators(storeStake, cdc), govcmd.GetCmdQueryVote(storeGov, cdc), govcmd.GetCmdQueryVotes(storeGov, cdc), + govcmd.GetCmdQueryDeposit(storeGov, cdc), + govcmd.GetCmdQueryDeposits(storeGov, cdc), + slashingcmd.GetCmdQuerySigningInfo(storeSlashing, cdc), )...) //Add query commands diff --git a/docs/sdk/clients.md b/docs/sdk/clients.md index e28a7f2b3..73d9a3b9a 100644 --- a/docs/sdk/clients.md +++ b/docs/sdk/clients.md @@ -285,7 +285,13 @@ Or if you want to check all your current unbonding-delegations with disctinct va gaiacli query unbonding-delegations ``` -You can also get previous unbonding-delegation(s) status by adding the `--height` flag. +Additionally, as you can get all the unbonding-delegations from a particular validator: + +```bash + gaiacli query unbonding-delegations-from +``` + +To get previous unbonding-delegation(s) status on past blocks, try adding the `--height` flag. #### Redelegate Tokens @@ -321,7 +327,13 @@ Or if you want to check all your current unbonding-delegations with disctinct va gaiacli query redelegations ``` -You can also get previous redelegation(s) status by adding the `--height` flag. +Additionally, as you can get all the outgoing redelegations from a particular validator: + +```bash + gaiacli query redelegations-from +``` + +To get previous redelegation(s) status on past blocks, try adding the `--height` flag. ### Governance diff --git a/examples/basecoin/cmd/basecli/main.go b/examples/basecoin/cmd/basecli/main.go index 94c5c6e01..1732e82eb 100644 --- a/examples/basecoin/cmd/basecli/main.go +++ b/examples/basecoin/cmd/basecli/main.go @@ -49,6 +49,8 @@ func main() { client.GetCommands( stakecmd.GetCmdQueryValidator("stake", cdc), stakecmd.GetCmdQueryValidators("stake", cdc), + stakecmd.GetCmdQueryValidatorUnbondingDelegations("stake", cdc), + stakecmd.GetCmdQueryValidatorRedelegations("stake", cdc), stakecmd.GetCmdQueryDelegation("stake", cdc), stakecmd.GetCmdQueryDelegations("stake", cdc), stakecmd.GetCmdQueryPool("stake", cdc), diff --git a/x/stake/client/cli/query.go b/x/stake/client/cli/query.go index ad8030c93..28d09df8e 100644 --- a/x/stake/client/cli/query.go +++ b/x/stake/client/cli/query.go @@ -114,6 +114,80 @@ func GetCmdQueryValidators(storeName string, cdc *codec.Codec) *cobra.Command { return cmd } +// GetCmdQueryValidatorUnbondingDelegations implements the query all unbonding delegatations from a validator command. +func GetCmdQueryValidatorUnbondingDelegations(queryRoute string, cdc *codec.Codec) *cobra.Command { + cmd := &cobra.Command{ + Use: "unbonding-delegations-from [operator-addr]", + Short: "Query all unbonding delegatations from a validator", + Args: cobra.ExactArgs(1), + RunE: func(cmd *cobra.Command, args []string) error { + valAddr, err := sdk.ValAddressFromBech32(args[0]) + if err != nil { + return err + } + + cliCtx := context.NewCLIContext().WithCodec(cdc) + params := stake.QueryValidatorParams{ + ValidatorAddr: valAddr, + } + + bz, err := cdc.MarshalJSON(params) + if err != nil { + return err + } + + res, err := cliCtx.QueryWithData( + fmt.Sprintf("custom/%s/validatorUnbondingDelegations", queryRoute), + bz) + if err != nil { + return err + } + + fmt.Println(string(res)) + return nil + }, + } + + return cmd +} + +// GetCmdQueryValidatorRedelegations implements the query all redelegatations from a validator command. +func GetCmdQueryValidatorRedelegations(queryRoute string, cdc *codec.Codec) *cobra.Command { + cmd := &cobra.Command{ + Use: "redelegations-from [operator-addr]", + Short: "Query all outgoing redelegatations from a validator", + Args: cobra.ExactArgs(1), + RunE: func(cmd *cobra.Command, args []string) error { + valAddr, err := sdk.ValAddressFromBech32(args[0]) + if err != nil { + return err + } + + cliCtx := context.NewCLIContext().WithCodec(cdc) + params := stake.QueryValidatorParams{ + ValidatorAddr: valAddr, + } + + bz, err := cdc.MarshalJSON(params) + if err != nil { + return err + } + + res, err := cliCtx.QueryWithData( + fmt.Sprintf("custom/%s/validatorRedelegations", queryRoute), + bz) + if err != nil { + return err + } + + fmt.Println(string(res)) + return nil + }, + } + + return cmd +} + // GetCmdQueryDelegation the query delegation command. func GetCmdQueryDelegation(storeName string, cdc *codec.Codec) *cobra.Command { cmd := &cobra.Command{ @@ -139,6 +213,7 @@ func GetCmdQueryDelegation(storeName string, cdc *codec.Codec) *cobra.Command { } // parse out the delegation + delegation, err := types.UnmarshalDelegation(cdc, key, res) if err != nil { return err @@ -292,7 +367,7 @@ func GetCmdQueryUnbondingDelegations(storeName string, cdc *codec.Codec) *cobra. return err } - // parse out the validators + // parse out the unbonding delegations var ubds []stake.UnbondingDelegation for _, kv := range resKVs { ubd := types.MustUnmarshalUBD(cdc, kv.Key, kv.Value) diff --git a/x/stake/keeper/delegation.go b/x/stake/keeper/delegation.go index a5d08d489..97acc2404 100644 --- a/x/stake/keeper/delegation.go +++ b/x/stake/keeper/delegation.go @@ -541,6 +541,10 @@ func (k Keeper) CompleteUnbonding(ctx sdk.Context, delAddr sdk.AccAddress, valAd func (k Keeper) BeginRedelegation(ctx sdk.Context, delAddr sdk.AccAddress, valSrcAddr, valDstAddr sdk.ValAddress, sharesAmount sdk.Dec) (types.Redelegation, sdk.Error) { + if bytes.Equal(valSrcAddr, valDstAddr) { + return types.Redelegation{}, types.ErrSelfRedelegation(k.Codespace()) + } + // check if there is already a redelgation in progress from src to dst // TODO quick fix, instead we should use an index, see https://github.com/cosmos/cosmos-sdk/issues/1402 _, found := k.GetRedelegation(ctx, delAddr, valSrcAddr, valDstAddr) diff --git a/x/stake/keeper/delegation_test.go b/x/stake/keeper/delegation_test.go index 24476ca3c..fcf2f4206 100644 --- a/x/stake/keeper/delegation_test.go +++ b/x/stake/keeper/delegation_test.go @@ -581,6 +581,32 @@ func TestRedelegation(t *testing.T) { require.Equal(t, 0, len(redelegations)) } +func TestRedelegateToSameValidator(t *testing.T) { + + ctx, _, keeper := CreateTestInput(t, false, 0) + pool := keeper.GetPool(ctx) + pool.LooseTokens = sdk.NewDec(30) + + // create a validator with a self-delegation + validator := types.NewValidator(addrVals[0], PKs[0], types.Description{}) + validator, pool, issuedShares := validator.AddTokensFromDel(pool, sdk.NewInt(10)) + require.Equal(t, int64(10), issuedShares.RoundInt64()) + keeper.SetPool(ctx, pool) + validator = TestingUpdateValidator(keeper, ctx, validator) + pool = keeper.GetPool(ctx) + val0AccAddr := sdk.AccAddress(addrVals[0].Bytes()) + selfDelegation := types.Delegation{ + DelegatorAddr: val0AccAddr, + ValidatorAddr: addrVals[0], + Shares: issuedShares, + } + keeper.SetDelegation(ctx, selfDelegation) + + _, err := keeper.BeginRedelegation(ctx, val0AccAddr, addrVals[0], addrVals[0], sdk.NewDec(5)) + require.Error(t, err) + +} + func TestRedelegateSelfDelegation(t *testing.T) { ctx, _, keeper := CreateTestInput(t, false, 0) diff --git a/x/stake/types/errors.go b/x/stake/types/errors.go index e8c85800f..d76eb39c1 100644 --- a/x/stake/types/errors.go +++ b/x/stake/types/errors.go @@ -155,6 +155,10 @@ func ErrNoRedelegation(codespace sdk.CodespaceType) sdk.Error { return sdk.NewError(codespace, CodeInvalidDelegation, "no redelegation found") } +func ErrSelfRedelegation(codespace sdk.CodespaceType) sdk.Error { + return sdk.NewError(codespace, CodeInvalidDelegation, "cannot redelegate to the same validator") +} + func ErrBadRedelegationDst(codespace sdk.CodespaceType) sdk.Error { return sdk.NewError(codespace, CodeInvalidDelegation, "redelegation validator not found") }