diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index f7e2d0fd6..36b9f97f2 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -5,13 +5,14 @@ v If a checkbox is n/a - please still include it but + a little note why ☺ > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > --> - [ ] Linked to github-issue with discussion and accepted design OR link to spec that describes this work. -- [ ] Updated all relevant documentation (`docs/`) -- [ ] Updated all relevant code comments - [ ] Wrote tests -- [ ] Added entries in `PENDING.md` that include links to the relevant issue or PR that most accurately describes the change. -- [ ] Updated `cmd/gaia` and `examples/` -___________________________________ +- [ ] Updated relevant documentation (`docs/`) +- [ ] Added entries in `PENDING.md` with issue # +- [ ] rereviewed `Files changed` in the github PR explorer + +______ + For Admin Use: -- [ ] Added appropriate labels to PR (ex. wip, ready-for-review, docs) -- [ ] Reviewers Assigned -- [ ] Squashed all commits, uses message "Merge pull request #XYZ: [title]" ([coding standards](https://github.com/tendermint/coding/blob/master/README.md#merging-a-pr)) +- Added appropriate labels to PR (ex. wip, ready-for-review, docs) +- Reviewers Assigned +- Squashed all commits, uses message "Merge pull request #XYZ: [title]" ([coding standards](https://github.com/tendermint/coding/blob/master/README.md#merging-a-pr)) diff --git a/Gopkg.lock b/Gopkg.lock index 38830c49e..73a2ff60f 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -490,6 +490,14 @@ revision = "013b9cef642f875634c614019ab13b17570778ad" version = "v0.23.0" +[[projects]] + digest = "1:bf6d9a827ea3cad964c2f863302e4f6823170d0b5ed16f72cf1184a7c615067e" + name = "github.com/tendermint/tmlibs" + packages = ["cli"] + pruneopts = "UT" + revision = "49596e0a1f48866603813df843c9409fc19805c6" + version = "v0.9.0" + [[projects]] digest = "1:4dcb0dd65feecb068ce23a234d1a07c7868a1e39f52a6defcae0bb371d03abf6" name = "github.com/zondax/ledger-goclient" @@ -499,7 +507,7 @@ [[projects]] branch = "master" - digest = "1:7a71fffde456d746c52f9cd09c50b034533a3180fb1f6320abb149f2ccc579e5" + digest = "1:27507554c6d4f060d8d700c31c624a43d3a92baa634e178ddc044bdf7d13b44a" name = "golang.org/x/crypto" packages = [ "blowfish", @@ -518,7 +526,7 @@ "salsa20/salsa", ] pruneopts = "UT" - revision = "aabede6cba87e37f413b3e60ebfc214f8eeca1b0" + revision = "614d502a4dac94afa3a6ce146bd1736da82514c6" [[projects]] digest = "1:d36f55a999540d29b6ea3c2ea29d71c76b1d9853fdcd3e5c5cb4836f2ba118f1" @@ -538,14 +546,14 @@ [[projects]] branch = "master" - digest = "1:ead82e3e398388679f3ad77633a087ac31a47a6be59ae20841e1d1b3a3fbbd22" + digest = "1:a0e12bc26f317c0e2d497baf767285e1790e526e8dd46553c5a67fcbc8692157" name = "golang.org/x/sys" packages = [ "cpu", "unix", ] pruneopts = "UT" - revision = "1a700e749ce29638d0bbcb531cce1094ea096bd3" + revision = "3b58ed4ad3395d483fc92d5d14123ce2c3581fec" [[projects]] digest = "1:a2ab62866c75542dd18d2b069fec854577a20211d7c0ea6ae746072a1dccdd18" @@ -666,6 +674,7 @@ "github.com/tendermint/tendermint/rpc/lib/server", "github.com/tendermint/tendermint/types", "github.com/tendermint/tendermint/version", + "github.com/tendermint/tmlibs/cli", "github.com/zondax/ledger-goclient", "golang.org/x/crypto/blowfish", "golang.org/x/crypto/ripemd160", diff --git a/PENDING.md b/PENDING.md index 5e8405270..bc0a861fb 100644 --- a/PENDING.md +++ b/PENDING.md @@ -8,12 +8,15 @@ BREAKING CHANGES * Gaia CLI (`gaiacli`) * [x/stake] Validator.Owner renamed to Validator.Operator * [cli] unsafe_reset_all, show_validator, and show_node_id have been renamed to unsafe-reset-all, show-validator, and show-node-id + * [cli] \#1983 --print-response now defaults to true in commands that create and send a transaction + * [cli] \#1983 you can now pass --pubkey or --address to gaiacli keys show to return a plaintext representation of the key's address or public key for use with other commands * [cli] \#2061 changed proposalID in governance REST endpoints to proposal-id * [cli] \#2014 `gaiacli advanced` no longer exists - to access `ibc`, `rest-server`, and `validator-set` commands use `gaiacli ibc`, `gaiacli rest-server`, and `gaiacli tendermint`, respectively * Gaia * Make the transient store key use a distinct store key. [#2013](https://github.com/cosmos/cosmos-sdk/pull/2013) * [x/stake] \#1901 Validator type's Owner field renamed to Operator; Validator's GetOwner() renamed accordingly to comply with the SDK's Validator interface. + * [x/stake, x/slashing] [#1305](https://github.com/cosmos/cosmos-sdk/issues/1305) - Rename "revoked" to "jailed" * SDK * [core] \#1807 Switch from use of rational to decimal @@ -29,10 +32,12 @@ FEATURES * Gaia CLI (`gaiacli`) * [cli] Cmds to query staking pool and params + * [gov][cli] #2062 added `--proposal` flag to `submit-proposal` that allows a JSON file containing a proposal to be passed in * Gaia * SDK + * [querier] added custom querier functionality, so ABCI query requests can be handled by keepers * Tendermint @@ -66,6 +71,7 @@ BUG FIXES * Gaia * SDK - * \#1988 Make us compile on OpenBSD (disable ledger) [#1988] (https://github.com/cosmos/cosmos-sdk/issues/1988) + * \#1988 Make us compile on OpenBSD (disable ledger) [#1988] (https://github.com/cosmos/cosmos-sdk/issues/1988) + * \#2105 Fix DB Iterator leak, which may leak a go routine. * Tendermint diff --git a/baseapp/baseapp.go b/baseapp/baseapp.go index cf63f1f4d..365d7b31f 100644 --- a/baseapp/baseapp.go +++ b/baseapp/baseapp.go @@ -41,13 +41,14 @@ const ( // BaseApp reflects the ABCI application implementation. type BaseApp struct { // initialized on creation - Logger log.Logger - name string // application name from abci.Info - db dbm.DB // common DB backend - cms sdk.CommitMultiStore // Main (uncached) state - router Router // handle any kind of message - codespacer *sdk.Codespacer // handle module codespacing - txDecoder sdk.TxDecoder // unmarshal []byte into sdk.Tx + Logger log.Logger + name string // application name from abci.Info + db dbm.DB // common DB backend + cms sdk.CommitMultiStore // Main (uncached) state + router Router // handle any kind of message + queryRouter QueryRouter // router for redirecting query calls + codespacer *sdk.Codespacer // handle module codespacing + txDecoder sdk.TxDecoder // unmarshal []byte into sdk.Tx anteHandler sdk.AnteHandler // ante handler for fee and auth @@ -84,13 +85,14 @@ var _ abci.Application = (*BaseApp)(nil) // Accepts variable number of option functions, which act on the BaseApp to set configuration choices func NewBaseApp(name string, logger log.Logger, db dbm.DB, txDecoder sdk.TxDecoder, options ...func(*BaseApp)) *BaseApp { app := &BaseApp{ - Logger: logger, - name: name, - db: db, - cms: store.NewCommitMultiStore(db), - router: NewRouter(), - codespacer: sdk.NewCodespacer(), - txDecoder: txDecoder, + Logger: logger, + name: name, + db: db, + cms: store.NewCommitMultiStore(db), + router: NewRouter(), + queryRouter: NewQueryRouter(), + codespacer: sdk.NewCodespacer(), + txDecoder: txDecoder, } // Register the undefined & root codespaces, which should not be used by @@ -266,6 +268,7 @@ func (app *BaseApp) FilterPeerByPubKey(info string) abci.ResponseQuery { return abci.ResponseQuery{} } +// Splits a string path using the delimter '/'. i.e. "this/is/funny" becomes []string{"this", "is", "funny"} func splitPath(requestPath string) (path []string) { path = strings.Split(requestPath, "/") // first element is empty string @@ -291,6 +294,8 @@ func (app *BaseApp) Query(req abci.RequestQuery) (res abci.ResponseQuery) { return handleQueryStore(app, path, req) case "p2p": return handleQueryP2P(app, path, req) + case "custom": + return handleQueryCustom(app, path, req) } msg := "unknown query path" @@ -362,6 +367,33 @@ func handleQueryP2P(app *BaseApp, path []string, req abci.RequestQuery) (res abc return sdk.ErrUnknownRequest(msg).QueryResult() } +func handleQueryCustom(app *BaseApp, path []string, req abci.RequestQuery) (res abci.ResponseQuery) { + // path[0] should be "custom" because "/custom" prefix is required for keeper queries. + // the queryRouter routes using path[1]. For example, in the path "custom/gov/proposal", queryRouter routes using "gov" + if path[1] == "" { + sdk.ErrUnknownRequest("No route for custom query specified").QueryResult() + } + querier := app.queryRouter.Route(path[1]) + if querier == nil { + sdk.ErrUnknownRequest(fmt.Sprintf("no custom querier found for route %s", path[1])).QueryResult() + } + + ctx := sdk.NewContext(app.cms.CacheMultiStore(), app.checkState.ctx.BlockHeader(), true, app.Logger) + // Passes the rest of the path as an argument to the querier. + // For example, in the path "custom/gov/proposal/test", the gov querier gets []string{"proposal", "test"} as the path + resBytes, err := querier(ctx, path[2:], req) + if err != nil { + return abci.ResponseQuery{ + Code: uint32(err.ABCICode()), + Log: err.ABCILog(), + } + } + return abci.ResponseQuery{ + Code: uint32(sdk.ABCICodeOK), + Value: resBytes, + } +} + // BeginBlock implements the ABCI application interface. func (app *BaseApp) BeginBlock(req abci.RequestBeginBlock) (res abci.ResponseBeginBlock) { if app.cms.TracingEnabled() { diff --git a/baseapp/queryrouter.go b/baseapp/queryrouter.go new file mode 100644 index 000000000..23cfad072 --- /dev/null +++ b/baseapp/queryrouter.go @@ -0,0 +1,41 @@ +package baseapp + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" +) + +// QueryRouter provides queryables for each query path. +type QueryRouter interface { + AddRoute(r string, h sdk.Querier) (rtr QueryRouter) + Route(path string) (h sdk.Querier) +} + +type queryrouter struct { + routes map[string]sdk.Querier +} + +// nolint +// NewRouter - create new router +// TODO either make Function unexported or make return type (router) Exported +func NewQueryRouter() *queryrouter { + return &queryrouter{ + routes: map[string]sdk.Querier{}, + } +} + +// AddRoute - Adds an sdk.Querier to the route provided. Panics on duplicate +func (rtr *queryrouter) AddRoute(r string, q sdk.Querier) QueryRouter { + if !isAlphaNumeric(r) { + panic("route expressions can only contain alphanumeric characters") + } + if rtr.routes[r] != nil { + panic("route has already been initialized") + } + rtr.routes[r] = q + return rtr +} + +// Returns the sdk.Querier for a certain route path +func (rtr *queryrouter) Route(path string) (h sdk.Querier) { + return rtr.routes[path] +} diff --git a/baseapp/setters.go b/baseapp/setters.go index 86a647d32..a8b1591a7 100644 --- a/baseapp/setters.go +++ b/baseapp/setters.go @@ -74,6 +74,9 @@ func (app *BaseApp) Router() Router { } return app.router } +func (app *BaseApp) QueryRouter() QueryRouter { + return app.queryRouter +} func (app *BaseApp) Seal() { app.sealed = true } func (app *BaseApp) IsSealed() bool { return app.sealed } func (app *BaseApp) enforceSeal() { diff --git a/client/context/query.go b/client/context/query.go index 081f723b5..68676f741 100644 --- a/client/context/query.go +++ b/client/context/query.go @@ -31,6 +31,11 @@ func (ctx CLIContext) Query(path string) (res []byte, err error) { return ctx.query(path, nil) } +// Query information about the connected node with a data payload +func (ctx CLIContext) QueryWithData(path string, data []byte) (res []byte, err error) { + return ctx.query(path, data) +} + // QueryStore performs a query from a Tendermint node with the provided key and // store name. func (ctx CLIContext) QueryStore(key cmn.HexBytes, storeName string) (res []byte, err error) { diff --git a/client/flags.go b/client/flags.go index 8616f9e78..b02078905 100644 --- a/client/flags.go +++ b/client/flags.go @@ -52,7 +52,7 @@ func PostCommands(cmds ...*cobra.Command) []*cobra.Command { c.Flags().Int64(FlagGas, 200000, "gas limit to set per-transaction") c.Flags().Bool(FlagAsync, false, "broadcast transactions asynchronously") c.Flags().Bool(FlagJson, false, "return output in json format") - c.Flags().Bool(FlagPrintResponse, false, "return tx response (only works with async = false)") + c.Flags().Bool(FlagPrintResponse, true, "return tx response (only works with async = false)") } return cmds } diff --git a/client/keys/show.go b/client/keys/show.go index 873c45a4b..e9d692ece 100644 --- a/client/keys/show.go +++ b/client/keys/show.go @@ -4,10 +4,20 @@ import ( "encoding/json" "net/http" - keys "github.com/cosmos/cosmos-sdk/crypto/keys" + "github.com/cosmos/cosmos-sdk/crypto/keys" "github.com/gorilla/mux" + "github.com/pkg/errors" "github.com/spf13/cobra" + "github.com/spf13/viper" + "github.com/tendermint/tmlibs/cli" +) + +const ( + // FlagAddress is the flag for the user's address on the command line. + FlagAddress = "address" + // FlagPublicKey represents the user's public key on the command line. + FlagPublicKey = "pubkey" ) var showKeysCmd = &cobra.Command{ @@ -18,13 +28,38 @@ var showKeysCmd = &cobra.Command{ RunE: func(cmd *cobra.Command, args []string) error { name := args[0] info, err := getKey(name) - if err == nil { - printInfo(info) + if err != nil { + return err } - return err + + showAddress := viper.GetBool(FlagAddress) + showPublicKey := viper.GetBool(FlagPublicKey) + outputSet := cmd.Flag(cli.OutputFlag).Changed + if showAddress && showPublicKey { + return errors.New("cannot use both --address and --pubkey at once") + } + if outputSet && (showAddress || showPublicKey) { + return errors.New("cannot use --output with --address or --pubkey") + } + if showAddress { + printKeyAddress(info) + return nil + } + if showPublicKey { + printPubKey(info) + return nil + } + + printInfo(info) + return nil }, } +func init() { + showKeysCmd.Flags().Bool(FlagAddress, false, "output the address only (overrides --output)") + showKeysCmd.Flags().Bool(FlagPublicKey, false, "output the public key only (overrides --output)") +} + func getKey(name string) (keys.Info, error) { kb, err := GetKeyBase() if err != nil { diff --git a/client/keys/utils.go b/client/keys/utils.go index 907f9eda8..aa1b4bed9 100644 --- a/client/keys/utils.go +++ b/client/keys/utils.go @@ -6,7 +6,7 @@ import ( "github.com/spf13/viper" - keys "github.com/cosmos/cosmos-sdk/crypto/keys" + "github.com/cosmos/cosmos-sdk/crypto/keys" "github.com/tendermint/tendermint/libs/cli" dbm "github.com/tendermint/tendermint/libs/db" @@ -173,3 +173,19 @@ func printInfos(infos []keys.Info) { func printKeyOutput(ko KeyOutput) { fmt.Printf("%s\t%s\t%s\t%s\n", ko.Name, ko.Type, ko.Address, ko.PubKey) } + +func printKeyAddress(info keys.Info) { + ko, err := Bech32KeyOutput(info) + if err != nil { + panic(err) + } + fmt.Println(ko.Address.String()) +} + +func printPubKey(info keys.Info) { + ko, err := Bech32KeyOutput(info) + if err != nil { + panic(err) + } + fmt.Println(ko.PubKey) +} diff --git a/client/lcd/lcd_test.go b/client/lcd/lcd_test.go index fbbf6a6cf..2e7ab0af7 100644 --- a/client/lcd/lcd_test.go +++ b/client/lcd/lcd_test.go @@ -595,7 +595,7 @@ func TestVote(t *testing.T) { require.Equal(t, gov.OptionYes, vote.Option) } -func TestUnrevoke(t *testing.T) { +func TestUnjail(t *testing.T) { _, password := "test", "1234567890" addr, _ := CreateAddr(t, "test", password, GetKeyBase(t)) cleanup, pks, port := InitializeTestLCD(t, 1, []sdk.AccAddress{addr}) diff --git a/client/tx/search.go b/client/tx/search.go index adad29d7d..06b3c0972 100644 --- a/client/tx/search.go +++ b/client/tx/search.go @@ -27,7 +27,20 @@ const ( func SearchTxCmd(cdc *wire.Codec) *cobra.Command { cmd := &cobra.Command{ Use: "txs", - Short: "Search for all transactions that match the given tags", + Short: "Search for all transactions that match the given tags.", + Long: strings.TrimSpace(` +Search for transactions that match the given tags. By default, transactions must match ALL tags +passed to the --tags option. To match any transaction, use the --any option. + +For example: + +$ gaiacli tendermint txs --tag test1,test2 + +will match any transaction tagged with both test1,test2. To match a transaction tagged with either +test1 or test2, use: + +$ gaiacli tendermint txs --tag test1,test2 --any +`), RunE: func(cmd *cobra.Command, args []string) error { tags := viper.GetStringSlice(flagTags) @@ -52,7 +65,7 @@ func SearchTxCmd(cdc *wire.Codec) *cobra.Command { // TODO: change this to false once proofs built in cmd.Flags().Bool(client.FlagTrustNode, true, "Don't verify proofs for responses") - cmd.Flags().StringSlice(flagTags, nil, "Tags that must match (may provide multiple)") + cmd.Flags().StringSlice(flagTags, nil, "Comma-separated list of tags that must match") cmd.Flags().Bool(flagAny, false, "Return transactions that match ANY tag, rather than ALL") return cmd } diff --git a/cmd/gaia/app/app.go b/cmd/gaia/app/app.go index a96efa6b1..4ce6b2806 100644 --- a/cmd/gaia/app/app.go +++ b/cmd/gaia/app/app.go @@ -105,6 +105,9 @@ func NewGaiaApp(logger log.Logger, db dbm.DB, traceStore io.Writer, baseAppOptio AddRoute("slashing", slashing.NewHandler(app.slashingKeeper)). AddRoute("gov", gov.NewHandler(app.govKeeper)) + app.QueryRouter(). + AddRoute("gov", gov.NewQuerier(app.govKeeper)) + // initialize BaseApp app.SetInitChainer(app.initChainer) app.SetBeginBlocker(app.BeginBlocker) @@ -185,7 +188,7 @@ func (app *GaiaApp) initChainer(ctx sdk.Context, req abci.RequestInitChain) abci // load the address to pubkey map slashing.InitGenesis(ctx, app.slashingKeeper, genesisState.StakeData) - gov.InitGenesis(ctx, app.govKeeper, gov.DefaultGenesisState()) + gov.InitGenesis(ctx, app.govKeeper, genesisState.GovData) return abci.ResponseInitChain{ Validators: validators, @@ -208,6 +211,7 @@ func (app *GaiaApp) ExportAppStateAndValidators() (appState json.RawMessage, val genState := GenesisState{ Accounts: accounts, StakeData: stake.WriteGenesis(ctx, app.stakeKeeper), + GovData: gov.WriteGenesis(ctx, app.govKeeper), } appState, err = wire.MarshalJSONIndent(app.cdc, genState) if err != nil { diff --git a/cmd/gaia/app/genesis.go b/cmd/gaia/app/genesis.go index 0399d6652..24b160a51 100644 --- a/cmd/gaia/app/genesis.go +++ b/cmd/gaia/app/genesis.go @@ -11,6 +11,7 @@ import ( 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/gov" "github.com/cosmos/cosmos-sdk/x/stake" "github.com/spf13/pflag" @@ -32,6 +33,7 @@ var ( type GenesisState struct { Accounts []GenesisAccount `json:"accounts"` StakeData stake.GenesisState `json:"stake"` + GovData gov.GenesisState `json:"gov"` } // GenesisAccount doesn't need pubkey or sequence @@ -216,6 +218,7 @@ func GaiaAppGenState(cdc *wire.Codec, appGenTxs []json.RawMessage) (genesisState genesisState = GenesisState{ Accounts: genaccs, StakeData: stakeData, + GovData: gov.DefaultGenesisState(), } return } diff --git a/cmd/gaia/app/sim_test.go b/cmd/gaia/app/sim_test.go index 051508e1a..5fa75a931 100644 --- a/cmd/gaia/app/sim_test.go +++ b/cmd/gaia/app/sim_test.go @@ -98,7 +98,7 @@ func testAndRunTxs(app *GaiaApp) []simulation.TestAndRunTx { stakesim.SimulateMsgCompleteUnbonding(app.stakeKeeper), stakesim.SimulateMsgBeginRedelegate(app.accountMapper, app.stakeKeeper), stakesim.SimulateMsgCompleteRedelegate(app.stakeKeeper), - slashingsim.SimulateMsgUnrevoke(app.slashingKeeper), + slashingsim.SimulateMsgUnjail(app.slashingKeeper), } } diff --git a/cmd/gaia/cmd/gaiacli/main.go b/cmd/gaia/cmd/gaiacli/main.go index 25c670bdd..df0fd3c11 100644 --- a/cmd/gaia/cmd/gaiacli/main.go +++ b/cmd/gaia/cmd/gaiacli/main.go @@ -95,7 +95,7 @@ func main() { stakecmd.GetCmdDelegate(cdc), stakecmd.GetCmdUnbond("stake", cdc), stakecmd.GetCmdRedelegate("stake", cdc), - slashingcmd.GetCmdUnrevoke(cdc), + slashingcmd.GetCmdUnjail(cdc), )...) rootCmd.AddCommand( stakeCmd, diff --git a/docs/light/api.md b/docs/light/api.md index 4507c9eb8..6c5e8aa5f 100644 --- a/docs/light/api.md +++ b/docs/light/api.md @@ -802,7 +802,7 @@ The GovernanceAPI exposes all functionality needed for casting votes on plain te ## ICS23 - SlashingAPI -The SlashingAPI exposes all functionalities needed to slash (*i.e* penalize) validators and delegators in Proof-of-Stake. The penalization is a fine of the staking coin and jail time, defined by governance parameters. During the jail period, the penalized validator is `Revoked`. +The SlashingAPI exposes all functionalities needed to slash (*i.e* penalize) validators and delegators in Proof-of-Stake. The penalization is a fine of the staking coin and jail time, defined by governance parameters. During the jail period, the penalized validator is "jailed". ### GET /slashing/validator/{validatorAddr}/signing-info diff --git a/docs/sdk/core/app1.md b/docs/sdk/core/app1.md index 54121d05b..a2978ffb0 100644 --- a/docs/sdk/core/app1.md +++ b/docs/sdk/core/app1.md @@ -469,7 +469,7 @@ Tendermint consensus engine. It would be initialized by a Genesis file, and it would be driven by blocks of transactions committed by the underlying Tendermint consensus. We'll talk more about ABCI and how this all works a bit later, but feel free to check the -[specification](https://github.com/tendermint/tendermint/blob/master/docs/abci-spec.md). +[specification](https://github.com/tendermint/tendermint/blob/master/docs/app-dev/abci-spec.md). We'll also see how to connect our app to a complete suite of components for running and using a live blockchain application. diff --git a/docs/sdk/core/app4.md b/docs/sdk/core/app4.md index 0724e5e7a..b6596ef0c 100644 --- a/docs/sdk/core/app4.md +++ b/docs/sdk/core/app4.md @@ -16,7 +16,7 @@ here we will introduce the other ABCI requests sent by Tendermint, and how we can use them to build more advanced applications. For a more complete depiction of the ABCI and how its used, see [the -specification](https://github.com/tendermint/tendermint/blob/master/docs/abci-spec.md) +specification](https://github.com/tendermint/tendermint/blob/master/docs/app-dev/abci-spec.md) ## InitChain diff --git a/docs/sdk/modules.md b/docs/sdk/modules.md index b9249cf3e..7df8fb2bb 100644 --- a/docs/sdk/modules.md +++ b/docs/sdk/modules.md @@ -6,7 +6,7 @@ The Cosmos SDK has all the necessary pre-built modules to add functionality on top of a `BaseApp`, which is the template to build a blockchain dApp in Cosmos. In this context, a `module` is a fundamental unit in the Cosmos SDK. -Each module is an extension of the `BaseApp`'s functionalities that defines transactions, handles application state and manages the state transition logic. Each module also contains handlers for messages and transactions, as well as REST and CLI for secure user interactions. +Each module is an extension of the `BaseApp`'s functionalities that defines transactions, handles application state and manages the state transition logic. Each module also contains handlers for messages and transactions, queriers for handling query requests, as well as REST and CLI for secure user interactions. Some of the most important modules in the SDK are: diff --git a/docs/sdk/sdk-by-examples/simple-governance/submit-proposal.md b/docs/sdk/sdk-by-examples/simple-governance/submit-proposal.md index bb9eb289f..57571c151 100644 --- a/docs/sdk/sdk-by-examples/simple-governance/submit-proposal.md +++ b/docs/sdk/sdk-by-examples/simple-governance/submit-proposal.md @@ -6,6 +6,23 @@ Uuse the CLI to create a new proposal: simplegovcli propose --title="Voting Period update" --description="Should we change the proposal voting period to 3 weeks?" --deposit=300Atoms ``` +Or, via a json file: + +```bash +simplegovcli propose --proposal="path/to/proposal.json" +``` + +Where proposal.json contains: + +```json +{ + "title": "Voting Period Update", + "description": "Should we change the proposal voting period to 3 weeks?", + "type": "Text", + "deposit": "300Atoms" +} +``` + Get the details of your newly created proposal: ```bash diff --git a/docs/spec/auth/vesting.md b/docs/spec/auth/vesting.md new file mode 100644 index 000000000..c5c25ecae --- /dev/null +++ b/docs/spec/auth/vesting.md @@ -0,0 +1,159 @@ +## Vesting + +### Intro and Requirements + +This paper specifies vesting account implementation for the Cosmos Hub. +The requirements for this vesting account is that it should be initialized during genesis with +a starting balance X coins and a vesting endtime T. The owner of this account should be able to delegate to validators +and vote with locked coins, however they cannot send locked coins to other accounts until those coins have been unlocked. +The vesting account should also be able to spend any coins it receives from other users. +Thus, the bank module's `MsgSend` handler should error if a vesting account is trying to send an amount that exceeds their +unlocked coin amount. + +### Implementation + +##### Vesting Account implementation + +NOTE: `Now = ctx.BlockHeader().Time` + +```go +type VestingAccount interface { + Account + AssertIsVestingAccount() // existence implies that account is vesting. + + // Calculates amount of coins that can be sent to other accounts given the current time + SendableCoins(sdk.Context) sdk.Coins +} + +// Implements Vesting Account +// Continuously vests by unlocking coins linearly with respect to time +type ContinuousVestingAccount struct { + BaseAccount + OriginalVestingCoins sdk.Coins // Coins in account on Initialization + ReceivedCoins sdk.Coins // Coins received from other accounts + SentCoins sdk.Coins // Coins sent to other accounts + + // StartTime and EndTime used to calculate how much of OriginalCoins is unlocked at any given point + StartTime time.Time + EndTime time.Time +} + +// Uses time in context to calculate total unlocked coins +SendableCoins(vacc ContinuousVestingAccount, ctx sdk.Context) sdk.Coins: + + // Coins unlocked by vesting schedule + unlockedCoins := ReceivedCoins - SentCoins + OriginalVestingCoins * (Now - StartTime) / (EndTime - StartTime) + + // Must still check for currentCoins constraint since some unlocked coins may have been delegated. + currentCoins := vacc.BaseAccount.GetCoins() + + // min will return sdk.Coins with each denom having the minimum amount from unlockedCoins and currentCoins + return min(unlockedCoins, currentCoins) + +``` + +The `VestingAccount` interface is used to assert that an account is a vesting account like so: + +```go +vacc, ok := acc.(VestingAccount); ok +``` + +as well as to calculate the SendableCoins at any given moment. + +The `ContinuousVestingAccount` struct implements the Vesting account interface. It uses `OriginalVestingCoins`, `ReceivedCoins`, +`SentCoins`, `StartTime`, and `EndTime` to calculate how many coins are sendable at any given point. +Since the vesting restrictions need to be implemented on a per-module basis, the `ContinuousVestingAccount` implements +the `Account` interface exactly like `BaseAccount`. Thus, `ContinuousVestingAccount.GetCoins()` will return the total of +both locked coins and unlocked coins currently in the account. Delegated coins are deducted from `Account.GetCoins()`, but do not count against unlocked coins because they are still at stake and will be reinstated (partially if slashed) after waiting the full unbonding period. + +##### Changes to Keepers/Handler + +Since a vesting account should be capable of doing everything but sending with its locked coins, the restriction should be +handled at the `bank.Keeper` level. Specifically in methods that are explicitly used for sending like +`sendCoins` and `inputOutputCoins`. These methods must check that an account is a vesting account using the check described above. + +```go +if acc is VestingAccount and Now < vestingAccount.EndTime: + // Check if amount is less than currently allowed sendable coins + if msg.Amount > vestingAccount.SendableCoins(ctx) then fail + else: + vestingAccount.SentCoins += msg.Amount + +else: + // Account has fully vested, treat like regular account + if msg.Amount > account.GetCoins() then fail + +// All checks passed, send the coins +SendCoins(inputs, outputs) + +``` + +Coins that are sent to a vesting account after initialization by users sending them coins should be spendable +immediately after receiving them. Thus, handlers (like staking or bank) that send coins that a vesting account did not +originally own should increment `ReceivedCoins` by the amount sent. +Unlocked coins that are sent to other accounts will increment the vesting account's `SentCoins` attribute. + +CONTRACT: Handlers SHOULD NOT update `ReceivedCoins` if they were originally sent from the vesting account. For example, if a vesting account unbonds from a validator, their tokens should be added back to account but staking handlers SHOULD NOT update `ReceivedCoins`. +However when a user sends coins to vesting account, then `ReceivedCoins` SHOULD be incremented. + +### Initializing at Genesis + +To initialize both vesting accounts and base accounts, the `GenesisAccount` struct will include an EndTime. Accounts meant to be +BaseAccounts will have `EndTime = 0`. The `initChainer` method will parse the GenesisAccount into BaseAccounts and VestingAccounts +as appropriate. + +```go +type GenesisAccount struct { + Address sdk.AccAddress `json:"address"` + GenesisCoins sdk.Coins `json:"coins"` + EndTime int64 `json:"lock"` +} + +initChainer: + for gacc in GenesisAccounts: + baseAccount := BaseAccount{ + Address: gacc.Address, + Coins: gacc.GenesisCoins, + } + if gacc.EndTime != 0: + vestingAccount := ContinuouslyVestingAccount{ + BaseAccount: baseAccount, + OriginalVestingCoins: gacc.GenesisCoins, + StartTime: RequestInitChain.Time, + EndTime: gacc.EndTime, + } + AddAccountToState(vestingAccount) + else: + AddAccountToState(baseAccount) + +``` + +### Formulas + +`OriginalVestingCoins`: Amount of coins in account at Genesis + +`CurrentCoins`: Coins currently in the baseaccount (both locked and unlocked: `vestingAccount.GetCoins`) + +`ReceivedCoins`: Coins received from other accounts (always unlocked) + +`LockedCoins`: Coins that are currently locked + +`Delegated`: Coins that have been delegated (no longer in account; may be locked or unlocked) + +`Sent`: Coins sent to other accounts (MUST be unlocked) + +Maximum amount of coins vesting schedule allows to be sent: + +`ReceivedCoins - SentCoins + OriginalVestingCoins * (Now - StartTime) / (EndTime - StartTime)` + +`ReceivedCoins - SentCoins + OriginalVestingCoins - LockedCoins` + +Coins currently in Account: + +`CurrentCoins = OriginalVestingCoins + ReceivedCoins - Delegated - Sent` + +`CurrentCoins = vestingAccount.GetCoins()` + +**Maximum amount of coins spendable right now:** + +`min( ReceivedCoins - SentCoins + OriginalVestingCoins - LockedCoins, CurrentCoins )` diff --git a/docs/spec/slashing/state.md b/docs/spec/slashing/state.md index 1df9d5022..8bbb22c76 100644 --- a/docs/spec/slashing/state.md +++ b/docs/spec/slashing/state.md @@ -47,5 +47,5 @@ type ValidatorSigningInfo struct { Where: * `StartHeight` is set to the height that the candidate became an active validator (with non-zero voting power). * `IndexOffset` is incremented each time the candidate was a bonded validator in a block (and may have signed a precommit or not). -* `JailedUntil` is set whenever the candidate is revoked due to downtime +* `JailedUntil` is set whenever the candidate is jailed due to downtime * `SignedBlocksCounter` is a counter kept to avoid unnecessary array reads. `SignedBlocksBitArray.Sum() == SignedBlocksCounter` always. diff --git a/docs/spec/staking/end_block.md b/docs/spec/staking/end_block.md index 0eb557fec..c2fe143ba 100644 --- a/docs/spec/staking/end_block.md +++ b/docs/spec/staking/end_block.md @@ -4,7 +4,7 @@ The Tendermint validator set may be updated by state transitions that run at the end of every block. The Tendermint validator set may be changed by -validators either being revoked due to inactivity/unexpected behaviour (covered +validators either being jailed due to inactivity/unexpected behaviour (covered in slashing) or changed in validator power. Determining which validator set changes must be made occurs during staking transactions (and slashing transactions) - during end-block the already accounted changes are applied and diff --git a/docs/spec/staking/state.md b/docs/spec/staking/state.md index 46f18ed78..58bd67cd5 100644 --- a/docs/spec/staking/state.md +++ b/docs/spec/staking/state.md @@ -71,7 +71,7 @@ validator. ```golang type Validator struct { ConsensusPubKey crypto.PubKey // Tendermint consensus pubkey of validator - Revoked bool // has the validator been revoked? + Jailed bool // has the validator been jailed? Status sdk.BondStatus // validator status (bonded/unbonding/unbonded) Tokens sdk.Dec // delegated tokens (incl. self-delegation) diff --git a/docs/validators/validator-setup.md b/docs/validators/validator-setup.md index e56c3c691..6fa4fefd2 100644 --- a/docs/validators/validator-setup.md +++ b/docs/validators/validator-setup.md @@ -72,12 +72,12 @@ gaiacli stake signing-information \ --chain-id= ``` -### Unrevoke Validator +### Unjail Validator -When a validator is `Revoked` for downtime, you must submit an `Unrevoke` transaction in order to be able to get block proposer rewards again (depends on the zone fee distribution). +When a validator is "jailed" for downtime, you must submit an `Unjail` transaction in order to be able to get block proposer rewards again (depends on the zone fee distribution). ```bash -gaiacli stake unrevoke \ +gaiacli stake unjail \ --from= \ --chain-id= --validator= \ @@ -113,11 +113,11 @@ gaiad start Wait for your full node to catch up to the latest block. Next, run the following command. Note that `` is the address of your validator account, and `` is the name of the validator account. You can find this info by running `gaiacli keys list`. ```bash -gaiacli stake unrevoke --chain-id= --name= +gaiacli stake unjail --chain-id= --name= ``` ::: danger Warning -If you don't wait for `gaiad` to sync before running `unrevoke`, you will receive an error message telling you your validator is still jailed. +If you don't wait for `gaiad` to sync before running `unjail`, you will receive an error message telling you your validator is still jailed. ::: Lastly, check your validator again to see if your voting power is back. diff --git a/examples/basecoin/cmd/basecli/main.go b/examples/basecoin/cmd/basecli/main.go index 15bfd8035..23183c87a 100644 --- a/examples/basecoin/cmd/basecli/main.go +++ b/examples/basecoin/cmd/basecli/main.go @@ -72,7 +72,7 @@ func main() { stakecmd.GetCmdDelegate(cdc), stakecmd.GetCmdUnbond("stake", cdc), stakecmd.GetCmdRedelegate("stake", cdc), - slashingcmd.GetCmdUnrevoke(cdc), + slashingcmd.GetCmdUnjail(cdc), )...) // add proxy, version and key info diff --git a/examples/democoin/mock/validator.go b/examples/democoin/mock/validator.go index f76f14803..bb936097c 100644 --- a/examples/democoin/mock/validator.go +++ b/examples/democoin/mock/validator.go @@ -44,7 +44,7 @@ func (v Validator) GetDelegatorShares() sdk.Dec { } // Implements sdk.Validator -func (v Validator) GetRevoked() bool { +func (v Validator) GetJailed() bool { return false } @@ -127,11 +127,11 @@ func (vs *ValidatorSet) Slash(ctx sdk.Context, pubkey crypto.PubKey, height int6 } // Implements sdk.ValidatorSet -func (vs *ValidatorSet) Revoke(ctx sdk.Context, pubkey crypto.PubKey) { +func (vs *ValidatorSet) Jail(ctx sdk.Context, pubkey crypto.PubKey) { panic("not implemented") } // Implements sdk.ValidatorSet -func (vs *ValidatorSet) Unrevoke(ctx sdk.Context, pubkey crypto.PubKey) { +func (vs *ValidatorSet) Unjail(ctx sdk.Context, pubkey crypto.PubKey) { panic("not implemented") } diff --git a/examples/democoin/x/assoc/validator_set.go b/examples/democoin/x/assoc/validator_set.go index 03ce506ea..ad89aab19 100644 --- a/examples/democoin/x/assoc/validator_set.go +++ b/examples/democoin/x/assoc/validator_set.go @@ -93,6 +93,7 @@ func (valset ValidatorSet) Dissociate(ctx sdk.Context, base sdk.AccAddress, asso func (valset ValidatorSet) Associations(ctx sdk.Context, base sdk.AccAddress) (res []sdk.AccAddress) { res = make([]sdk.AccAddress, valset.maxAssoc) iter := sdk.KVStorePrefixIterator(valset.store, GetAssocPrefix(base)) + defer iter.Close() i := 0 for ; iter.Valid(); iter.Next() { key := iter.Key() diff --git a/examples/democoin/x/oracle/handler.go b/examples/democoin/x/oracle/handler.go index 3c78fc566..1aaefc7e1 100644 --- a/examples/democoin/x/oracle/handler.go +++ b/examples/democoin/x/oracle/handler.go @@ -28,6 +28,7 @@ func (keeper Keeper) update(ctx sdk.Context, val sdk.Validator, valset sdk.Valid prefix := GetSignPrefix(p, keeper.cdc) store := ctx.KVStore(keeper.key) iter := sdk.KVStorePrefixIterator(store, prefix) + defer iter.Close() for ; iter.Valid(); iter.Next() { if valset.Validator(ctx, iter.Value()) != nil { store.Delete(iter.Key()) diff --git a/server/tm_cmds.go b/server/tm_cmds.go index f852b4029..c2395e399 100644 --- a/server/tm_cmds.go +++ b/server/tm_cmds.go @@ -69,7 +69,7 @@ func ShowValidatorCmd(ctx *Context) *cobra.Command { func UnsafeResetAllCmd(ctx *Context) *cobra.Command { return &cobra.Command{ Use: "unsafe-reset-all", - Short: "Reset blockchain database, priv_validator.json file, and the logger", + Short: "Resets the blockchain database, removes address book files, and resets priv_validator.json to the genesis state", RunE: func(cmd *cobra.Command, args []string) error { cfg := ctx.Config tcmd.ResetAll(cfg.DBDir(), cfg.P2P.AddrBookFile(), cfg.PrivValidatorFile(), ctx.Logger) diff --git a/store/iavlstore_test.go b/store/iavlstore_test.go index d081b4576..38d85c658 100644 --- a/store/iavlstore_test.go +++ b/store/iavlstore_test.go @@ -168,6 +168,7 @@ func TestIAVLSubspaceIterator(t *testing.T) { require.EqualValues(t, value, expectedKey) i++ } + iter.Close() require.Equal(t, len(expected), i) iter = sdk.KVStorePrefixIterator(iavlStore, []byte{byte(55), byte(255), byte(255)}) @@ -183,6 +184,7 @@ func TestIAVLSubspaceIterator(t *testing.T) { require.EqualValues(t, value, []byte("test4")) i++ } + iter.Close() require.Equal(t, len(expected), i) iter = sdk.KVStorePrefixIterator(iavlStore, []byte{byte(255), byte(255)}) @@ -198,6 +200,7 @@ func TestIAVLSubspaceIterator(t *testing.T) { require.EqualValues(t, value, []byte("test4")) i++ } + iter.Close() require.Equal(t, len(expected), i) } diff --git a/types/account.go b/types/account.go index 92e2988f2..00076b529 100644 --- a/types/account.go +++ b/types/account.go @@ -1,6 +1,7 @@ package types import ( + "bytes" "encoding/hex" "encoding/json" "errors" @@ -108,6 +109,23 @@ func (bz AccAddress) Format(s fmt.State, verb rune) { } } +// Returns boolean for whether two AccAddresses are Equal +func (bz AccAddress) Equals(bz2 AccAddress) bool { + if bz.Empty() && bz2.Empty() { + return true + } + return bytes.Compare(bz.Bytes(), bz2.Bytes()) == 0 +} + +// Returns boolean for whether an AccAddress is empty +func (bz AccAddress) Empty() bool { + if bz == nil { + return true + } + bz2 := AccAddress{} + return bytes.Compare(bz.Bytes(), bz2.Bytes()) == 0 +} + //__________________________________________________________ // AccAddress a wrapper around bytes meant to represent a validator address @@ -192,6 +210,23 @@ func (bz ValAddress) Format(s fmt.State, verb rune) { } } +// Returns boolean for whether two ValAddresses are Equal +func (bz ValAddress) Equals(bz2 ValAddress) bool { + if bz.Empty() && bz2.Empty() { + return true + } + return bytes.Compare(bz.Bytes(), bz2.Bytes()) == 0 +} + +// Returns boolean for whether an AccAddress is empty +func (bz ValAddress) Empty() bool { + if bz == nil { + return true + } + bz2 := ValAddress{} + return bytes.Compare(bz.Bytes(), bz2.Bytes()) == 0 +} + // Bech32ifyAccPub takes AccountPubKey and returns the bech32 encoded string func Bech32ifyAccPub(pub crypto.PubKey) (string, error) { return bech32.ConvertAndEncode(Bech32PrefixAccPub, pub.Bytes()) diff --git a/types/queryable.go b/types/queryable.go new file mode 100644 index 000000000..9223332bc --- /dev/null +++ b/types/queryable.go @@ -0,0 +1,6 @@ +package types + +import abci "github.com/tendermint/tendermint/abci/types" + +// Type for querier functions on keepers to implement to handle custom queries +type Querier = func(ctx Context, path []string, req abci.RequestQuery) (res []byte, err Error) diff --git a/types/stake.go b/types/stake.go index 69efed2c9..d87227725 100644 --- a/types/stake.go +++ b/types/stake.go @@ -37,7 +37,7 @@ func (b BondStatus) Equal(b2 BondStatus) bool { // validator for a delegated proof of stake system type Validator interface { - GetRevoked() bool // whether the validator is revoked + GetJailed() bool // whether the validator is jailed GetMoniker() string // moniker of the validator GetStatus() BondStatus // status of the validator GetOperator() AccAddress // owner AccAddress to receive/return validators coins @@ -73,8 +73,8 @@ type ValidatorSet interface { // slash the validator and delegators of the validator, specifying offence height, offence power, and slash fraction Slash(Context, crypto.PubKey, int64, int64, Dec) - Revoke(Context, crypto.PubKey) // revoke a validator - Unrevoke(Context, crypto.PubKey) // unrevoke a validator + Jail(Context, crypto.PubKey) // jail a validator + Unjail(Context, crypto.PubKey) // unjail a validator } //_______________________________________________________________________________ diff --git a/x/auth/mapper.go b/x/auth/mapper.go index 244527af3..c8bf94a21 100644 --- a/x/auth/mapper.go +++ b/x/auth/mapper.go @@ -87,6 +87,7 @@ func (am AccountMapper) SetAccount(ctx sdk.Context, acc Account) { func (am AccountMapper) IterateAccounts(ctx sdk.Context, process func(Account) (stop bool)) { store := ctx.KVStore(am.key) iter := sdk.KVStorePrefixIterator(store, []byte("account:")) + defer iter.Close() for { if !iter.Valid() { return diff --git a/x/gov/client/cli/tx.go b/x/gov/client/cli/tx.go index d19de9f07..2a7e71aac 100644 --- a/x/gov/client/cli/tx.go +++ b/x/gov/client/cli/tx.go @@ -12,9 +12,12 @@ import ( authctx "github.com/cosmos/cosmos-sdk/x/auth/client/context" "github.com/cosmos/cosmos-sdk/x/gov" + "encoding/json" "github.com/pkg/errors" "github.com/spf13/cobra" "github.com/spf13/viper" + "io/ioutil" + "strings" ) const ( @@ -28,18 +31,51 @@ const ( flagDepositer = "depositer" flagStatus = "status" flagLatestProposalIDs = "latest" + flagProposal = "proposal" ) +type proposal struct { + Title string + Description string + Type string + Deposit string +} + +var proposalFlags = []string{ + flagTitle, + flagDescription, + flagProposalType, + flagDeposit, +} + // GetCmdSubmitProposal implements submitting a proposal transaction command. func GetCmdSubmitProposal(cdc *wire.Codec) *cobra.Command { cmd := &cobra.Command{ Use: "submit-proposal", Short: "Submit a proposal along with an initial deposit", + Long: strings.TrimSpace(` +Submit a proposal along with an initial deposit. Proposal title, description, type and deposit can be given directly or through a proposal JSON file. For example: + +$ gaiacli gov submit-proposal --proposal="path/to/proposal.json" + +where proposal.json contains: + +{ + "title": "Test Proposal", + "description": "My awesome proposal", + "type": "Text", + "deposit": "1000test" +} + +is equivalent to + +$ gaiacli gov submit-proposal --title="Test Proposal" --description="My awesome proposal" --type="Text" --deposit="1000test" +`), RunE: func(cmd *cobra.Command, args []string) error { - title := viper.GetString(flagTitle) - description := viper.GetString(flagDescription) - strProposalType := viper.GetString(flagProposalType) - initialDeposit := viper.GetString(flagDeposit) + proposal, err := parseSubmitProposalFlags() + if err != nil { + return err + } txCtx := authctx.NewTxContextFromCLI().WithCodec(cdc) cliCtx := context.NewCLIContext(). @@ -52,17 +88,17 @@ func GetCmdSubmitProposal(cdc *wire.Codec) *cobra.Command { return err } - amount, err := sdk.ParseCoins(initialDeposit) + amount, err := sdk.ParseCoins(proposal.Deposit) if err != nil { return err } - proposalType, err := gov.ProposalTypeFromString(strProposalType) + proposalType, err := gov.ProposalTypeFromString(proposal.Type) if err != nil { return err } - msg := gov.NewMsgSubmitProposal(title, description, proposalType, fromAddr, amount) + msg := gov.NewMsgSubmitProposal(proposal.Title, proposal.Description, proposalType, fromAddr, amount) err = msg.ValidateBasic() if err != nil { @@ -80,10 +116,42 @@ func GetCmdSubmitProposal(cdc *wire.Codec) *cobra.Command { cmd.Flags().String(flagDescription, "", "description of proposal") cmd.Flags().String(flagProposalType, "", "proposalType of proposal") cmd.Flags().String(flagDeposit, "", "deposit of proposal") + cmd.Flags().String(flagProposal, "", "proposal file path (if this path is given, other proposal flags are ignored)") return cmd } +func parseSubmitProposalFlags() (*proposal, error) { + proposal := &proposal{} + proposalFile := viper.GetString(flagProposal) + + if proposalFile == "" { + proposal.Title = viper.GetString(flagTitle) + proposal.Description = viper.GetString(flagDescription) + proposal.Type = viper.GetString(flagProposalType) + proposal.Deposit = viper.GetString(flagDeposit) + return proposal, nil + } + + for _, flag := range proposalFlags { + if viper.GetString(flag) != "" { + return nil, fmt.Errorf("--%s flag provided alongside --proposal, which is a noop", flag) + } + } + + contents, err := ioutil.ReadFile(proposalFile) + if err != nil { + return nil, err + } + + err = json.Unmarshal(contents, proposal) + if err != nil { + return nil, err + } + + return proposal, nil +} + // GetCmdDeposit implements depositing tokens for an active proposal. func GetCmdDeposit(cdc *wire.Codec) *cobra.Command { cmd := &cobra.Command{ diff --git a/x/gov/client/cli/tx_test.go b/x/gov/client/cli/tx_test.go new file mode 100644 index 000000000..e3aed05ff --- /dev/null +++ b/x/gov/client/cli/tx_test.go @@ -0,0 +1,70 @@ +package cli + +import ( + "github.com/spf13/viper" + "github.com/stretchr/testify/require" + "io/ioutil" + "testing" +) + +func TestParseSubmitProposalFlags(t *testing.T) { + okJSON, err := ioutil.TempFile("", "proposal") + require.Nil(t, err, "unexpected error") + okJSON.WriteString(` +{ + "title": "Test Proposal", + "description": "My awesome proposal", + "type": "Text", + "deposit": "1000test" +} +`) + + badJSON, err := ioutil.TempFile("", "proposal") + require.Nil(t, err, "unexpected error") + badJSON.WriteString("bad json") + + // nonexistent json + viper.Set(flagProposal, "fileDoesNotExist") + _, err = parseSubmitProposalFlags() + require.Error(t, err) + + // invalid json + viper.Set(flagProposal, badJSON.Name()) + _, err = parseSubmitProposalFlags() + require.Error(t, err) + + // ok json + viper.Set(flagProposal, okJSON.Name()) + proposal1, err := parseSubmitProposalFlags() + require.Nil(t, err, "unexpected error") + require.Equal(t, "Test Proposal", proposal1.Title) + require.Equal(t, "My awesome proposal", proposal1.Description) + require.Equal(t, "Text", proposal1.Type) + require.Equal(t, "1000test", proposal1.Deposit) + + // flags that can't be used with --proposal + for _, incompatibleFlag := range proposalFlags { + viper.Set(incompatibleFlag, "some value") + _, err := parseSubmitProposalFlags() + require.Error(t, err) + viper.Set(incompatibleFlag, "") + } + + // no --proposal, only flags + viper.Set(flagProposal, "") + viper.Set(flagTitle, proposal1.Title) + viper.Set(flagDescription, proposal1.Description) + viper.Set(flagProposalType, proposal1.Type) + viper.Set(flagDeposit, proposal1.Deposit) + proposal2, err := parseSubmitProposalFlags() + require.Nil(t, err, "unexpected error") + require.Equal(t, proposal1.Title, proposal2.Title) + require.Equal(t, proposal1.Description, proposal2.Description) + require.Equal(t, proposal1.Type, proposal2.Type) + require.Equal(t, proposal1.Deposit, proposal2.Deposit) + + err = okJSON.Close() + require.Nil(t, err, "unexpected error") + err = badJSON.Close() + require.Nil(t, err, "unexpected error") +} diff --git a/x/gov/client/rest/rest.go b/x/gov/client/rest/rest.go index 22283a652..5cdc7bda2 100644 --- a/x/gov/client/rest/rest.go +++ b/x/gov/client/rest/rest.go @@ -3,7 +3,6 @@ package rest import ( "fmt" "net/http" - "strconv" "github.com/cosmos/cosmos-sdk/client/context" sdk "github.com/cosmos/cosmos-sdk/types" @@ -21,6 +20,7 @@ const ( RestDepositer = "depositer" RestVoter = "voter" RestProposalStatus = "status" + RestNumLatest = "latest" storeName = "gov" ) @@ -97,16 +97,13 @@ func depositHandlerFn(cdc *wire.Codec, cliCtx context.CLIContext) http.HandlerFu return } - proposalID, err := strconv.ParseInt(strProposalID, 10, 64) - if err != nil { - err := errors.Errorf("proposalID [%d] is not positive", proposalID) - w.Write([]byte(err.Error())) - + proposalID, ok := parseInt64OrReturnBadRequest(strProposalID, w) + if !ok { return } var req depositReq - err = buildReq(w, r, cdc, &req) + err := buildReq(w, r, cdc, &req) if err != nil { return } @@ -139,15 +136,13 @@ func voteHandlerFn(cdc *wire.Codec, cliCtx context.CLIContext) http.HandlerFunc return } - proposalID, err := strconv.ParseInt(strProposalID, 10, 64) - if err != nil { - err := errors.Errorf("proposalID [%d] is not positive", proposalID) - w.Write([]byte(err.Error())) + proposalID, ok := parseInt64OrReturnBadRequest(strProposalID, w) + if !ok { return } var req voteReq - err = buildReq(w, r, cdc, &req) + err := buildReq(w, r, cdc, &req) if err != nil { return } @@ -180,36 +175,33 @@ func queryProposalHandlerFn(cdc *wire.Codec) http.HandlerFunc { return } - proposalID, err := strconv.ParseInt(strProposalID, 10, 64) - if err != nil { - err := errors.Errorf("proposalID [%d] is not positive", proposalID) - w.Write([]byte(err.Error())) - + proposalID, ok := parseInt64OrReturnBadRequest(strProposalID, w) + if !ok { return } cliCtx := context.NewCLIContext().WithCodec(cdc) - res, err := cliCtx.QueryStore(gov.KeyProposal(proposalID), storeName) - if err != nil || len(res) == 0 { - err := errors.Errorf("proposalID [%d] does not exist", proposalID) - w.Write([]byte(err.Error())) - - return + params := gov.QueryProposalParams{ + ProposalID: proposalID, } - var proposal gov.Proposal - cdc.MustUnmarshalBinary(res, &proposal) - - output, err := wire.MarshalJSONIndent(cdc, proposal) + bz, err := cdc.MarshalJSON(params) if err != nil { w.WriteHeader(http.StatusBadRequest) w.Write([]byte(err.Error())) + return + } + + res, err := cliCtx.QueryWithData("custom/gov/proposal", bz) + if err != nil { + w.WriteHeader(http.StatusInternalServerError) + w.Write([]byte(err.Error())) return } - w.Write(output) + w.Write(res) } } @@ -227,12 +219,8 @@ func queryDepositHandlerFn(cdc *wire.Codec) http.HandlerFunc { return } - proposalID, err := strconv.ParseInt(strProposalID, 10, 64) - if err != nil { - w.WriteHeader(http.StatusBadRequest) - err := errors.Errorf("proposalID [%d] is not positive", proposalID) - w.Write([]byte(err.Error())) - + proposalID, ok := parseInt64OrReturnBadRequest(strProposalID, w) + if !ok { return } @@ -255,36 +243,43 @@ func queryDepositHandlerFn(cdc *wire.Codec) http.HandlerFunc { cliCtx := context.NewCLIContext().WithCodec(cdc) - res, err := cliCtx.QueryStore(gov.KeyDeposit(proposalID, depositerAddr), storeName) - if err != nil || len(res) == 0 { - res, err := cliCtx.QueryStore(gov.KeyProposal(proposalID), storeName) - if err != nil || len(res) == 0 { - w.WriteHeader(http.StatusNotFound) - err := errors.Errorf("proposalID [%d] does not exist", proposalID) - w.Write([]byte(err.Error())) + params := gov.QueryDepositParams{ + ProposalID: proposalID, + Depositer: depositerAddr, + } - return - } + bz, err := cdc.MarshalJSON(params) + if err != nil { + w.WriteHeader(http.StatusBadRequest) + w.Write([]byte(err.Error())) + return + } - w.WriteHeader(http.StatusNotFound) - err = errors.Errorf("depositer [%s] did not deposit on proposalID [%d]", bechDepositerAddr, proposalID) + res, err := cliCtx.QueryWithData("custom/gov/deposit", bz) + if err != nil { + w.WriteHeader(http.StatusInternalServerError) w.Write([]byte(err.Error())) return } var deposit gov.Deposit - cdc.MustUnmarshalBinary(res, &deposit) - - output, err := wire.MarshalJSONIndent(cdc, deposit) - if err != nil { - w.WriteHeader(http.StatusBadRequest) + cdc.UnmarshalJSON(res, &deposit) + if deposit.Empty() { + res, err := cliCtx.QueryWithData("custom/gov/proposal", cdc.MustMarshalBinary(gov.QueryProposalParams{params.ProposalID})) + if err != nil || len(res) == 0 { + w.WriteHeader(http.StatusNotFound) + err := errors.Errorf("proposalID [%d] does not exist", proposalID) + w.Write([]byte(err.Error())) + return + } + w.WriteHeader(http.StatusNotFound) + err = errors.Errorf("depositer [%s] did not deposit on proposalID [%d]", bechDepositerAddr, proposalID) w.Write([]byte(err.Error())) - return } - w.Write(output) + w.Write(res) } } @@ -301,12 +296,8 @@ func queryVoteHandlerFn(cdc *wire.Codec) http.HandlerFunc { return } - proposalID, err := strconv.ParseInt(strProposalID, 10, 64) - if err != nil { - w.WriteHeader(http.StatusBadRequest) - err := errors.Errorf("proposalID [%s] is not positive", proposalID) - w.Write([]byte(err.Error())) - + proposalID, ok := parseInt64OrReturnBadRequest(strProposalID, w) + if !ok { return } @@ -328,36 +319,47 @@ func queryVoteHandlerFn(cdc *wire.Codec) http.HandlerFunc { cliCtx := context.NewCLIContext().WithCodec(cdc) - res, err := cliCtx.QueryStore(gov.KeyVote(proposalID, voterAddr), storeName) - if err != nil || len(res) == 0 { - res, err := cliCtx.QueryStore(gov.KeyProposal(proposalID), storeName) - if err != nil || len(res) == 0 { - w.WriteHeader(http.StatusNotFound) - err := errors.Errorf("proposalID [%d] does not exist", proposalID) - w.Write([]byte(err.Error())) + params := gov.QueryVoteParams{ + Voter: voterAddr, + ProposalID: proposalID, + } + bz, err := cdc.MarshalJSON(params) + if err != nil { + w.WriteHeader(http.StatusBadRequest) + w.Write([]byte(err.Error())) + return + } - return - } - - w.WriteHeader(http.StatusNotFound) - err = errors.Errorf("voter [%s] did not vote on proposalID [%d]", bechVoterAddr, proposalID) + res, err := cliCtx.QueryWithData("custom/gov/vote", bz) + if err != nil { + w.WriteHeader(http.StatusInternalServerError) w.Write([]byte(err.Error())) return } var vote gov.Vote - cdc.MustUnmarshalBinary(res, &vote) - - output, err := wire.MarshalJSONIndent(cdc, vote) - if err != nil { - w.WriteHeader(http.StatusBadRequest) + cdc.UnmarshalJSON(res, &vote) + if vote.Empty() { + bz, err := cdc.MarshalJSON(gov.QueryProposalParams{params.ProposalID}) + if err != nil { + w.WriteHeader(http.StatusBadRequest) + w.Write([]byte(err.Error())) + return + } + res, err := cliCtx.QueryWithData("custom/gov/proposal", bz) + if err != nil || len(res) == 0 { + w.WriteHeader(http.StatusNotFound) + err := errors.Errorf("proposalID [%d] does not exist", proposalID) + w.Write([]byte(err.Error())) + return + } + w.WriteHeader(http.StatusNotFound) + err = errors.Errorf("voter [%s] did not deposit on proposalID [%d]", bechVoterAddr, proposalID) w.Write([]byte(err.Error())) - return } - - w.Write(output) + w.Write(res) } } @@ -376,59 +378,31 @@ func queryVotesOnProposalHandlerFn(cdc *wire.Codec) http.HandlerFunc { return } - proposalID, err := strconv.ParseInt(strProposalID, 10, 64) - if err != nil { - w.WriteHeader(http.StatusBadRequest) - err := errors.Errorf("proposalID [%s] is not positive", proposalID) - w.Write([]byte(err.Error())) - + proposalID, ok := parseInt64OrReturnBadRequest(strProposalID, w) + if !ok { return } cliCtx := context.NewCLIContext().WithCodec(cdc) - res, err := cliCtx.QueryStore(gov.KeyProposal(proposalID), storeName) - if err != nil || len(res) == 0 { - err := errors.Errorf("proposalID [%d] does not exist", proposalID) - w.Write([]byte(err.Error())) - - return + params := gov.QueryVotesParams{ + ProposalID: proposalID, } - - var proposal gov.Proposal - cdc.MustUnmarshalBinary(res, &proposal) - - if proposal.GetStatus() != gov.StatusVotingPeriod { - err := errors.Errorf("proposal is not in Voting Period", proposalID) - w.Write([]byte(err.Error())) - - return - } - - res2, err := cliCtx.QuerySubspace(gov.KeyVotesSubspace(proposalID), storeName) - if err != nil { - err = errors.New("ProposalID doesn't exist") - w.Write([]byte(err.Error())) - return - } - - var votes []gov.Vote - - for i := 0; i < len(res2); i++ { - var vote gov.Vote - cdc.MustUnmarshalBinary(res2[i].Value, &vote) - votes = append(votes, vote) - } - - output, err := wire.MarshalJSONIndent(cdc, votes) + bz, err := cdc.MarshalJSON(params) if err != nil { w.WriteHeader(http.StatusBadRequest) w.Write([]byte(err.Error())) - return } - w.Write(output) + res, err := cliCtx.QueryWithData("custom/gov/votes", bz) + if err != nil { + w.WriteHeader(http.StatusInternalServerError) + w.Write([]byte(err.Error())) + return + } + + w.Write(res) } } @@ -439,24 +413,23 @@ func queryProposalsWithParameterFn(cdc *wire.Codec) http.HandlerFunc { bechVoterAddr := r.URL.Query().Get(RestVoter) bechDepositerAddr := r.URL.Query().Get(RestDepositer) strProposalStatus := r.URL.Query().Get(RestProposalStatus) + strNumLatest := r.URL.Query().Get(RestNumLatest) - var err error - var voterAddr sdk.AccAddress - var depositerAddr sdk.AccAddress - var proposalStatus gov.ProposalStatus + params := gov.QueryProposalsParams{} if len(bechVoterAddr) != 0 { - voterAddr, err = sdk.AccAddressFromBech32(bechVoterAddr) + voterAddr, err := sdk.AccAddressFromBech32(bechVoterAddr) if err != nil { w.WriteHeader(http.StatusBadRequest) err := errors.Errorf("'%s' needs to be bech32 encoded", RestVoter) w.Write([]byte(err.Error())) return } + params.Voter = voterAddr } if len(bechDepositerAddr) != 0 { - depositerAddr, err = sdk.AccAddressFromBech32(bechDepositerAddr) + depositerAddr, err := sdk.AccAddressFromBech32(bechDepositerAddr) if err != nil { w.WriteHeader(http.StatusBadRequest) err := errors.Errorf("'%s' needs to be bech32 encoded", RestDepositer) @@ -464,10 +437,11 @@ func queryProposalsWithParameterFn(cdc *wire.Codec) http.HandlerFunc { return } + params.Depositer = depositerAddr } if len(strProposalStatus) != 0 { - proposalStatus, err = gov.ProposalStatusFromString(strProposalStatus) + proposalStatus, err := gov.ProposalStatusFromString(strProposalStatus) if err != nil { w.WriteHeader(http.StatusBadRequest) err := errors.Errorf("'%s' is not a valid Proposal Status", strProposalStatus) @@ -475,63 +449,33 @@ func queryProposalsWithParameterFn(cdc *wire.Codec) http.HandlerFunc { return } + params.ProposalStatus = proposalStatus + } + if len(strNumLatest) != 0 { + numLatest, ok := parseInt64OrReturnBadRequest(strNumLatest, w) + if !ok { + return + } + params.NumLatestProposals = numLatest + } + + bz, err := cdc.MarshalJSON(params) + if err != nil { + w.WriteHeader(http.StatusBadRequest) + w.Write([]byte(err.Error())) + return } cliCtx := context.NewCLIContext().WithCodec(cdc) - res, err := cliCtx.QueryStore(gov.KeyNextProposalID, storeName) + res, err := cliCtx.QueryWithData("custom/gov/proposals", bz) if err != nil { - err = errors.New("no proposals exist yet and proposalID has not been set") + w.WriteHeader(http.StatusInternalServerError) w.Write([]byte(err.Error())) return } - var maxProposalID int64 - cdc.MustUnmarshalBinary(res, &maxProposalID) - - matchingProposals := []gov.Proposal{} - - for proposalID := int64(0); proposalID < maxProposalID; proposalID++ { - if voterAddr != nil { - res, err = cliCtx.QueryStore(gov.KeyVote(proposalID, voterAddr), storeName) - if err != nil || len(res) == 0 { - continue - } - } - - if depositerAddr != nil { - res, err = cliCtx.QueryStore(gov.KeyDeposit(proposalID, depositerAddr), storeName) - if err != nil || len(res) == 0 { - continue - } - } - - res, err = cliCtx.QueryStore(gov.KeyProposal(proposalID), storeName) - if err != nil || len(res) == 0 { - continue - } - - var proposal gov.Proposal - cdc.MustUnmarshalBinary(res, &proposal) - - if len(strProposalStatus) != 0 { - if proposal.GetStatus() != proposalStatus { - continue - } - } - - matchingProposals = append(matchingProposals, proposal) - } - - output, err := wire.MarshalJSONIndent(cdc, matchingProposals) - if err != nil { - w.WriteHeader(http.StatusBadRequest) - w.Write([]byte(err.Error())) - - return - } - - w.Write(output) + w.Write(res) } } diff --git a/x/gov/client/rest/util.go b/x/gov/client/rest/util.go index 58d96b591..e6aa83cc4 100644 --- a/x/gov/client/rest/util.go +++ b/x/gov/client/rest/util.go @@ -1,8 +1,10 @@ package rest import ( + "fmt" "io/ioutil" "net/http" + "strconv" "github.com/cosmos/cosmos-sdk/client/context" sdk "github.com/cosmos/cosmos-sdk/types" @@ -100,3 +102,15 @@ func signAndBuild(w http.ResponseWriter, cliCtx context.CLIContext, baseReq base w.Write(output) } + +func parseInt64OrReturnBadRequest(s string, w http.ResponseWriter) (n int64, ok bool) { + var err error + n, err = strconv.ParseInt(s, 10, 64) + if err != nil { + w.WriteHeader(http.StatusBadRequest) + err := fmt.Errorf("'%s' is not a valid int64", s) + w.Write([]byte(err.Error())) + return 0, false + } + return n, true +} diff --git a/x/gov/depositsvotes.go b/x/gov/depositsvotes.go index 19ed97f69..b5c65931f 100644 --- a/x/gov/depositsvotes.go +++ b/x/gov/depositsvotes.go @@ -15,6 +15,17 @@ type Vote struct { Option VoteOption `json:"option"` // option from OptionSet chosen by the voter } +// Returns whether 2 votes are equal +func (voteA Vote) Equals(voteB Vote) bool { + return voteA.Voter.Equals(voteB.Voter) && voteA.ProposalID == voteB.ProposalID && voteA.Option == voteB.Option +} + +// Returns whether a vote is empty +func (voteA Vote) Empty() bool { + voteB := Vote{} + return voteA.Equals(voteB) +} + // Deposit type Deposit struct { Depositer sdk.AccAddress `json:"depositer"` // Address of the depositer @@ -22,6 +33,17 @@ type Deposit struct { Amount sdk.Coins `json:"amount"` // Deposit amount } +// Returns whether 2 deposits are equal +func (depositA Deposit) Equals(depositB Deposit) bool { + return depositA.Depositer.Equals(depositB.Depositer) && depositA.ProposalID == depositB.ProposalID && depositA.Amount.IsEqual(depositB.Amount) +} + +// Returns whether a deposit is empty +func (depositA Deposit) Empty() bool { + depositB := Deposit{} + return depositA.Equals(depositB) +} + // Type that represents VoteOption as a byte type VoteOption byte diff --git a/x/gov/keeper.go b/x/gov/keeper.go index 8a23ad248..e8dbbc4a7 100644 --- a/x/gov/keeper.go +++ b/x/gov/keeper.go @@ -108,6 +108,52 @@ func (keeper Keeper) DeleteProposal(ctx sdk.Context, proposal Proposal) { store.Delete(KeyProposal(proposal.GetProposalID())) } +// nolint: gocyclo +// Get Proposal from store by ProposalID +func (keeper Keeper) GetProposalsFiltered(ctx sdk.Context, voterAddr sdk.AccAddress, depositerAddr sdk.AccAddress, status ProposalStatus, numLatest int64) []Proposal { + + maxProposalID, err := keeper.peekCurrentProposalID(ctx) + if err != nil { + return nil + } + + matchingProposals := []Proposal{} + + if numLatest <= 0 { + numLatest = maxProposalID + } + + for proposalID := maxProposalID - numLatest; proposalID < maxProposalID; proposalID++ { + if voterAddr != nil && len(voterAddr) != 0 { + _, found := keeper.GetVote(ctx, proposalID, voterAddr) + if !found { + continue + } + } + + if depositerAddr != nil && len(depositerAddr) != 0 { + _, found := keeper.GetDeposit(ctx, proposalID, depositerAddr) + if !found { + continue + } + } + + proposal := keeper.GetProposal(ctx, proposalID) + if proposal == nil { + continue + } + + if validProposalStatus(status) { + if proposal.GetStatus() != status { + continue + } + } + + matchingProposals = append(matchingProposals, proposal) + } + return matchingProposals +} + func (keeper Keeper) setInitialProposalID(ctx sdk.Context, proposalID int64) sdk.Error { store := ctx.KVStore(keeper.storeKey) bz := store.Get(KeyNextProposalID) @@ -121,16 +167,15 @@ func (keeper Keeper) setInitialProposalID(ctx sdk.Context, proposalID int64) sdk // Get the last used proposal ID func (keeper Keeper) GetLastProposalID(ctx sdk.Context) (proposalID int64) { - store := ctx.KVStore(keeper.storeKey) - bz := store.Get(KeyNextProposalID) - if bz == nil { + proposalID, err := keeper.peekCurrentProposalID(ctx) + if err != nil { return 0 } - keeper.cdc.MustUnmarshalBinary(bz, &proposalID) proposalID-- return } +// Gets the next available ProposalID and increments it func (keeper Keeper) getNewProposalID(ctx sdk.Context) (proposalID int64, err sdk.Error) { store := ctx.KVStore(keeper.storeKey) bz := store.Get(KeyNextProposalID) @@ -143,6 +188,17 @@ func (keeper Keeper) getNewProposalID(ctx sdk.Context) (proposalID int64, err sd return proposalID, nil } +// Peeks the next available ProposalID without incrementing it +func (keeper Keeper) peekCurrentProposalID(ctx sdk.Context) (proposalID int64, err sdk.Error) { + store := ctx.KVStore(keeper.storeKey) + bz := store.Get(KeyNextProposalID) + if bz == nil { + return -1, ErrInvalidGenesis(keeper.codespace, "InitialProposalID never set") + } + keeper.cdc.MustUnmarshalBinary(bz, &proposalID) + return proposalID, nil +} + func (keeper Keeper) activateVotingPeriod(ctx sdk.Context, proposal Proposal) { proposal.SetVotingStartBlock(ctx.BlockHeight()) proposal.SetStatus(StatusVotingPeriod) diff --git a/x/gov/keeper_test.go b/x/gov/keeper_test.go index 442caee90..a61292b93 100644 --- a/x/gov/keeper_test.go +++ b/x/gov/keeper_test.go @@ -130,6 +130,7 @@ func TestDeposits(t *testing.T) { require.Equal(t, fourSteak, deposit.Amount) depositsIterator.Next() require.False(t, depositsIterator.Valid()) + depositsIterator.Close() // Test Refund Deposits deposit, found = keeper.GetDeposit(ctx, proposalID, addrs[1]) @@ -196,6 +197,7 @@ func TestVotes(t *testing.T) { require.Equal(t, OptionNoWithVeto, vote.Option) votesIterator.Next() require.False(t, votesIterator.Valid()) + votesIterator.Close() } func TestProposalQueues(t *testing.T) { diff --git a/x/gov/proposals.go b/x/gov/proposals.go index f05dabd08..c52ab1e87 100644 --- a/x/gov/proposals.go +++ b/x/gov/proposals.go @@ -110,6 +110,7 @@ type ProposalKind byte //nolint const ( + ProposalTypeNil ProposalKind = 0x00 ProposalTypeText ProposalKind = 0x01 ProposalTypeParameterChange ProposalKind = 0x02 ProposalTypeSoftwareUpgrade ProposalKind = 0x03 @@ -203,6 +204,7 @@ type ProposalStatus byte //nolint const ( + StatusNil ProposalStatus = 0x00 StatusDepositPeriod ProposalStatus = 0x01 StatusVotingPeriod ProposalStatus = 0x02 StatusPassed ProposalStatus = 0x03 @@ -220,6 +222,8 @@ func ProposalStatusFromString(str string) (ProposalStatus, error) { return StatusPassed, nil case "Rejected": return StatusRejected, nil + case "": + return StatusNil, nil default: return ProposalStatus(0xff), errors.Errorf("'%s' is not a valid proposal status", str) } diff --git a/x/gov/queryable.go b/x/gov/queryable.go new file mode 100644 index 000000000..e64d506d1 --- /dev/null +++ b/x/gov/queryable.go @@ -0,0 +1,213 @@ +package gov + +import ( + "fmt" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/wire" + abci "github.com/tendermint/tendermint/abci/types" +) + +func NewQuerier(keeper Keeper) sdk.Querier { + return func(ctx sdk.Context, path []string, req abci.RequestQuery) (res []byte, err sdk.Error) { + switch path[0] { + case "proposal": + return queryProposal(ctx, path[1:], req, keeper) + case "deposit": + return queryDeposit(ctx, path[1:], req, keeper) + case "vote": + return queryVote(ctx, path[1:], req, keeper) + case "deposits": + return queryDeposits(ctx, path[1:], req, keeper) + case "votes": + return queryVotes(ctx, path[1:], req, keeper) + case "proposals": + return queryProposals(ctx, path[1:], req, keeper) + case "tally": + return queryTally(ctx, path[1:], req, keeper) + default: + return nil, sdk.ErrUnknownRequest("unknown gov query endpoint") + } + } +} + +// Params for query 'custom/gov/proposal' +type QueryProposalParams struct { + ProposalID int64 +} + +func queryProposal(ctx sdk.Context, path []string, req abci.RequestQuery, keeper Keeper) (res []byte, err sdk.Error) { + var params QueryProposalParams + err2 := keeper.cdc.UnmarshalJSON(req.Data, ¶ms) + if err2 != nil { + return []byte{}, sdk.ErrUnknownRequest(fmt.Sprintf("incorrectly formatted request data - %s", err2.Error())) + } + + proposal := keeper.GetProposal(ctx, params.ProposalID) + if proposal == nil { + return []byte{}, ErrUnknownProposal(DefaultCodespace, params.ProposalID) + } + + bz, err2 := wire.MarshalJSONIndent(keeper.cdc, proposal) + if err2 != nil { + panic("could not marshal result to JSON") + } + return bz, nil +} + +// Params for query 'custom/gov/deposit' +type QueryDepositParams struct { + ProposalID int64 + Depositer sdk.AccAddress +} + +func queryDeposit(ctx sdk.Context, path []string, req abci.RequestQuery, keeper Keeper) (res []byte, err sdk.Error) { + var params QueryDepositParams + err2 := keeper.cdc.UnmarshalJSON(req.Data, ¶ms) + if err2 != nil { + return []byte{}, sdk.ErrUnknownRequest(fmt.Sprintf("incorrectly formatted request data - %s", err2.Error())) + } + + deposit, _ := keeper.GetDeposit(ctx, params.ProposalID, params.Depositer) + bz, err2 := wire.MarshalJSONIndent(keeper.cdc, deposit) + if err2 != nil { + panic("could not marshal result to JSON") + } + return bz, nil +} + +// Params for query 'custom/gov/vote' +type QueryVoteParams struct { + ProposalID int64 + Voter sdk.AccAddress +} + +func queryVote(ctx sdk.Context, path []string, req abci.RequestQuery, keeper Keeper) (res []byte, err sdk.Error) { + var params QueryVoteParams + err2 := keeper.cdc.UnmarshalJSON(req.Data, ¶ms) + if err2 != nil { + return []byte{}, sdk.ErrUnknownRequest(fmt.Sprintf("incorrectly formatted request data - %s", err2.Error())) + } + + vote, _ := keeper.GetVote(ctx, params.ProposalID, params.Voter) + bz, err2 := wire.MarshalJSONIndent(keeper.cdc, vote) + if err2 != nil { + panic("could not marshal result to JSON") + } + return bz, nil +} + +// Params for query 'custom/gov/deposits' +type QueryDepositsParams struct { + ProposalID int64 +} + +func queryDeposits(ctx sdk.Context, path []string, req abci.RequestQuery, keeper Keeper) (res []byte, err sdk.Error) { + var params QueryDepositParams + err2 := keeper.cdc.UnmarshalJSON(req.Data, ¶ms) + if err2 != nil { + return []byte{}, sdk.ErrUnknownRequest(fmt.Sprintf("incorrectly formatted request data - %s", err2.Error())) + } + + var deposits []Deposit + depositsIterator := keeper.GetDeposits(ctx, params.ProposalID) + for ; depositsIterator.Valid(); depositsIterator.Next() { + deposit := Deposit{} + keeper.cdc.MustUnmarshalBinary(depositsIterator.Value(), &deposit) + deposits = append(deposits, deposit) + } + + bz, err2 := wire.MarshalJSONIndent(keeper.cdc, deposits) + if err2 != nil { + panic("could not marshal result to JSON") + } + return bz, nil +} + +// Params for query 'custom/gov/votes' +type QueryVotesParams struct { + ProposalID int64 +} + +func queryVotes(ctx sdk.Context, path []string, req abci.RequestQuery, keeper Keeper) (res []byte, err sdk.Error) { + var params QueryVotesParams + err2 := keeper.cdc.UnmarshalJSON(req.Data, ¶ms) + + if err2 != nil { + return []byte{}, sdk.ErrUnknownRequest(fmt.Sprintf("incorrectly formatted request data - %s", err2.Error())) + } + + var votes []Vote + votesIterator := keeper.GetVotes(ctx, params.ProposalID) + for ; votesIterator.Valid(); votesIterator.Next() { + vote := Vote{} + keeper.cdc.MustUnmarshalBinary(votesIterator.Value(), &vote) + votes = append(votes, vote) + } + + bz, err2 := wire.MarshalJSONIndent(keeper.cdc, votes) + if err2 != nil { + panic("could not marshal result to JSON") + } + return bz, nil +} + +// Params for query 'custom/gov/proposals' +type QueryProposalsParams struct { + Voter sdk.AccAddress + Depositer sdk.AccAddress + ProposalStatus ProposalStatus + NumLatestProposals int64 +} + +func queryProposals(ctx sdk.Context, path []string, req abci.RequestQuery, keeper Keeper) (res []byte, err sdk.Error) { + var params QueryProposalsParams + err2 := keeper.cdc.UnmarshalJSON(req.Data, ¶ms) + if err2 != nil { + return []byte{}, sdk.ErrUnknownRequest(fmt.Sprintf("incorrectly formatted request data - %s", err2.Error())) + } + + proposals := keeper.GetProposalsFiltered(ctx, params.Voter, params.Depositer, params.ProposalStatus, params.NumLatestProposals) + + bz, err2 := wire.MarshalJSONIndent(keeper.cdc, proposals) + if err2 != nil { + panic("could not marshal result to JSON") + } + return bz, nil +} + +// Params for query 'custom/gov/tally' +type QueryTallyParams struct { + ProposalID int64 +} + +func queryTally(ctx sdk.Context, path []string, req abci.RequestQuery, keeper Keeper) (res []byte, err sdk.Error) { + // TODO: Dependant on #1914 + + var proposalID int64 + err2 := keeper.cdc.UnmarshalJSON(req.Data, proposalID) + if err2 != nil { + return []byte{}, sdk.ErrUnknownRequest(fmt.Sprintf("incorrectly formatted request data - %s", err2.Error())) + } + + proposal := keeper.GetProposal(ctx, proposalID) + if proposal == nil { + return []byte{}, ErrUnknownProposal(DefaultCodespace, proposalID) + } + + var tallyResult TallyResult + + if proposal.GetStatus() == StatusDepositPeriod { + tallyResult = EmptyTallyResult() + } else if proposal.GetStatus() == StatusPassed || proposal.GetStatus() == StatusRejected { + tallyResult = proposal.GetTallyResult() + } else { + _, tallyResult, _ = tally(ctx, keeper, proposal) + } + + bz, err2 := wire.MarshalJSONIndent(keeper.cdc, tallyResult) + if err2 != nil { + panic("could not marshal result to JSON") + } + return bz, nil +} diff --git a/x/gov/tally.go b/x/gov/tally.go index d8d4c7870..cfb113ae3 100644 --- a/x/gov/tally.go +++ b/x/gov/tally.go @@ -36,6 +36,7 @@ func tally(ctx sdk.Context, keeper Keeper, proposal Proposal) (passes bool, tall // iterate over all the votes votesIterator := keeper.GetVotes(ctx, proposal.GetProposalID()) + defer votesIterator.Close() for ; votesIterator.Valid(); votesIterator.Next() { vote := &Vote{} keeper.cdc.MustUnmarshalBinary(votesIterator.Value(), vote) @@ -64,7 +65,6 @@ func tally(ctx sdk.Context, keeper Keeper, proposal Proposal) (passes bool, tall keeper.deleteVote(ctx, vote.ProposalID, vote.Voter) } - votesIterator.Close() // Iterate over the validators again to tally their voting power and see who didn't vote nonVoting = []sdk.AccAddress{} diff --git a/x/gov/tally_test.go b/x/gov/tally_test.go index 730885266..157f59440 100644 --- a/x/gov/tally_test.go +++ b/x/gov/tally_test.go @@ -354,7 +354,7 @@ func TestTallyDelgatorMultipleInherit(t *testing.T) { require.False(t, tallyResults.Equals(EmptyTallyResult())) } -func TestTallyRevokedValidator(t *testing.T) { +func TestTallyJailedValidator(t *testing.T) { mapp, keeper, sk, addrs, _, _ := getMockApp(t, 10) mapp.BeginBlock(abci.RequestBeginBlock{}) ctx := mapp.BaseApp.NewContext(false, abci.Header{}) @@ -368,7 +368,7 @@ func TestTallyRevokedValidator(t *testing.T) { val2, found := sk.GetValidator(ctx, addrs[1]) require.True(t, found) - sk.Revoke(ctx, val2.PubKey) + sk.Jail(ctx, val2.PubKey) proposal := keeper.NewTextProposal(ctx, "Test", "description", ProposalTypeText) proposalID := proposal.GetProposalID() diff --git a/x/slashing/app_test.go b/x/slashing/app_test.go index e018c43e8..86d350a84 100644 --- a/x/slashing/app_test.go +++ b/x/slashing/app_test.go @@ -110,12 +110,12 @@ func TestSlashingMsgs(t *testing.T) { require.Equal(t, addr1, validator.Operator) require.Equal(t, sdk.Bonded, validator.Status) require.True(sdk.DecEq(t, sdk.NewDec(10), validator.BondedTokens())) - unrevokeMsg := MsgUnrevoke{ValidatorAddr: sdk.AccAddress(validator.PubKey.Address())} + unjailMsg := MsgUnjail{ValidatorAddr: sdk.AccAddress(validator.PubKey.Address())} // no signing info yet checkValidatorSigningInfo(t, mapp, keeper, sdk.ValAddress(addr1), false) - // unrevoke should fail with unknown validator - res := mock.SignCheckDeliver(t, mapp.BaseApp, []sdk.Msg{unrevokeMsg}, []int64{0}, []int64{1}, false, priv1) - require.Equal(t, sdk.ToABCICode(DefaultCodespace, CodeValidatorNotRevoked), res.Code) + // unjail should fail with unknown validator + res := mock.SignCheckDeliver(t, mapp.BaseApp, []sdk.Msg{unjailMsg}, []int64{0}, []int64{1}, false, priv1) + require.Equal(t, sdk.ToABCICode(DefaultCodespace, CodeValidatorNotJailed), res.Code) } diff --git a/x/slashing/client/cli/tx.go b/x/slashing/client/cli/tx.go index 5085f5aac..5831ef310 100644 --- a/x/slashing/client/cli/tx.go +++ b/x/slashing/client/cli/tx.go @@ -14,12 +14,12 @@ import ( "github.com/spf13/cobra" ) -// GetCmdUnrevoke implements the create unrevoke validator command. -func GetCmdUnrevoke(cdc *wire.Codec) *cobra.Command { +// GetCmdUnjail implements the create unjail validator command. +func GetCmdUnjail(cdc *wire.Codec) *cobra.Command { cmd := &cobra.Command{ - Use: "unrevoke", + Use: "unjail", Args: cobra.ExactArgs(0), - Short: "unrevoke validator previously revoked for downtime", + Short: "unjail validator previously jailed for downtime", RunE: func(cmd *cobra.Command, args []string) error { txCtx := authctx.NewTxContextFromCLI().WithCodec(cdc) cliCtx := context.NewCLIContext(). @@ -32,7 +32,7 @@ func GetCmdUnrevoke(cdc *wire.Codec) *cobra.Command { return err } - msg := slashing.NewMsgUnrevoke(validatorAddr) + msg := slashing.NewMsgUnjail(validatorAddr) return utils.SendTx(txCtx, cliCtx, []sdk.Msg{msg}) }, diff --git a/x/slashing/client/rest/tx.go b/x/slashing/client/rest/tx.go index 2ecec51ea..6e45230b0 100644 --- a/x/slashing/client/rest/tx.go +++ b/x/slashing/client/rest/tx.go @@ -19,13 +19,13 @@ import ( func registerTxRoutes(cliCtx context.CLIContext, r *mux.Router, cdc *wire.Codec, kb keys.Keybase) { r.HandleFunc( - "/slashing/unrevoke", - unrevokeRequestHandlerFn(cdc, kb, cliCtx), + "/slashing/unjail", + unjailRequestHandlerFn(cdc, kb, cliCtx), ).Methods("POST") } -// Unrevoke TX body -type UnrevokeBody struct { +// Unjail TX body +type UnjailBody struct { LocalAccountName string `json:"name"` Password string `json:"password"` ChainID string `json:"chain_id"` @@ -35,9 +35,9 @@ type UnrevokeBody struct { ValidatorAddr string `json:"validator_addr"` } -func unrevokeRequestHandlerFn(cdc *wire.Codec, kb keys.Keybase, cliCtx context.CLIContext) http.HandlerFunc { +func unjailRequestHandlerFn(cdc *wire.Codec, kb keys.Keybase, cliCtx context.CLIContext) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { - var m UnrevokeBody + var m UnjailBody body, err := ioutil.ReadAll(r.Body) if err != nil { w.WriteHeader(http.StatusBadRequest) @@ -79,7 +79,7 @@ func unrevokeRequestHandlerFn(cdc *wire.Codec, kb keys.Keybase, cliCtx context.C Gas: m.Gas, } - msg := slashing.NewMsgUnrevoke(validatorAddr) + msg := slashing.NewMsgUnjail(validatorAddr) txBytes, err := txCtx.BuildAndSign(m.LocalAccountName, m.Password, []sdk.Msg{msg}) if err != nil { diff --git a/x/slashing/errors.go b/x/slashing/errors.go index 3139c1662..4573d5e14 100644 --- a/x/slashing/errors.go +++ b/x/slashing/errors.go @@ -12,9 +12,9 @@ const ( // Default slashing codespace DefaultCodespace sdk.CodespaceType = 10 - CodeInvalidValidator CodeType = 101 - CodeValidatorJailed CodeType = 102 - CodeValidatorNotRevoked CodeType = 103 + CodeInvalidValidator CodeType = 101 + CodeValidatorJailed CodeType = 102 + CodeValidatorNotJailed CodeType = 103 ) func ErrNoValidatorForAddress(codespace sdk.CodespaceType) sdk.Error { @@ -24,8 +24,8 @@ func ErrBadValidatorAddr(codespace sdk.CodespaceType) sdk.Error { return sdk.NewError(codespace, CodeInvalidValidator, "validator does not exist for that address") } func ErrValidatorJailed(codespace sdk.CodespaceType) sdk.Error { - return sdk.NewError(codespace, CodeValidatorJailed, "validator jailed, cannot yet be unrevoked") + return sdk.NewError(codespace, CodeValidatorJailed, "validator still jailed, cannot yet be unjailed") } -func ErrValidatorNotRevoked(codespace sdk.CodespaceType) sdk.Error { - return sdk.NewError(codespace, CodeValidatorNotRevoked, "validator not revoked, cannot be unrevoked") +func ErrValidatorNotJailed(codespace sdk.CodespaceType) sdk.Error { + return sdk.NewError(codespace, CodeValidatorNotJailed, "validator not jailed, cannot be unjailed") } diff --git a/x/slashing/handler.go b/x/slashing/handler.go index 0cb64ab40..d79ea73c2 100644 --- a/x/slashing/handler.go +++ b/x/slashing/handler.go @@ -8,17 +8,17 @@ 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) { - case MsgUnrevoke: - return handleMsgUnrevoke(ctx, msg, k) + case MsgUnjail: + return handleMsgUnjail(ctx, msg, k) default: return sdk.ErrTxDecode("invalid message parse in staking module").Result() } } } -// Validators must submit a transaction to unrevoke itself after -// having been revoked (and thus unbonded) for downtime -func handleMsgUnrevoke(ctx sdk.Context, msg MsgUnrevoke, k Keeper) sdk.Result { +// Validators must submit a transaction to unjail itself after +// having been jailed (and thus unbonded) for downtime +func handleMsgUnjail(ctx sdk.Context, msg MsgUnjail, k Keeper) sdk.Result { // Validator must exist validator := k.validatorSet.Validator(ctx, msg.ValidatorAddr) @@ -26,8 +26,8 @@ func handleMsgUnrevoke(ctx sdk.Context, msg MsgUnrevoke, k Keeper) sdk.Result { return ErrNoValidatorForAddress(k.codespace).Result() } - if !validator.GetRevoked() { - return ErrValidatorNotRevoked(k.codespace).Result() + if !validator.GetJailed() { + return ErrValidatorNotJailed(k.codespace).Result() } addr := sdk.ValAddress(validator.GetPubKey().Address()) @@ -38,19 +38,19 @@ func handleMsgUnrevoke(ctx sdk.Context, msg MsgUnrevoke, k Keeper) sdk.Result { return ErrNoValidatorForAddress(k.codespace).Result() } - // Cannot be unrevoked until out of jail + // Cannot be unjailed until out of jail if ctx.BlockHeader().Time.Before(info.JailedUntil) { return ErrValidatorJailed(k.codespace).Result() } - // Update the starting height (so the validator can't be immediately revoked again) + // Update the starting height (so the validator can't be immediately jailed again) info.StartHeight = ctx.BlockHeight() k.setValidatorSigningInfo(ctx, addr, info) - // Unrevoke the validator - k.validatorSet.Unrevoke(ctx, validator.GetPubKey()) + // Unjail the validator + k.validatorSet.Unjail(ctx, validator.GetPubKey()) - tags := sdk.NewTags("action", []byte("unrevoke"), "validator", []byte(msg.ValidatorAddr.String())) + tags := sdk.NewTags("action", []byte("unjail"), "validator", []byte(msg.ValidatorAddr.String())) return sdk.Result{ Tags: tags, diff --git a/x/slashing/handler_test.go b/x/slashing/handler_test.go index 41cb5d199..4408d3c09 100644 --- a/x/slashing/handler_test.go +++ b/x/slashing/handler_test.go @@ -9,7 +9,7 @@ import ( "github.com/cosmos/cosmos-sdk/x/stake" ) -func TestCannotUnrevokeUnlessRevoked(t *testing.T) { +func TestCannotUnjailUnlessJailed(t *testing.T) { // initial setup ctx, ck, sk, _, keeper := createTestInput(t) slh := NewHandler(keeper) @@ -22,8 +22,8 @@ func TestCannotUnrevokeUnlessRevoked(t *testing.T) { require.Equal(t, ck.GetCoins(ctx, addr), sdk.Coins{{sk.GetParams(ctx).BondDenom, initCoins.Sub(amt)}}) require.True(t, sdk.NewDecFromInt(amt).Equal(sk.Validator(ctx, addr).GetPower())) - // assert non-revoked validator can't be unrevoked - got = slh(ctx, NewMsgUnrevoke(addr)) - require.False(t, got.IsOK(), "allowed unrevoke of non-revoked validator") - require.Equal(t, sdk.ToABCICode(DefaultCodespace, CodeValidatorNotRevoked), got.Code) + // assert non-jailed validator can't be unjailed + got = slh(ctx, NewMsgUnjail(addr)) + require.False(t, got.IsOK(), "allowed unjail of non-jailed validator") + require.Equal(t, sdk.ToABCICode(DefaultCodespace, CodeValidatorNotJailed), got.Code) } diff --git a/x/slashing/keeper.go b/x/slashing/keeper.go index 373423b2d..6d8e47cbe 100644 --- a/x/slashing/keeper.go +++ b/x/slashing/keeper.go @@ -59,10 +59,10 @@ func (k Keeper) handleDoubleSign(ctx sdk.Context, addr crypto.Address, infractio // Slash validator k.validatorSet.Slash(ctx, pubkey, infractionHeight, power, k.SlashFractionDoubleSign(ctx)) - // Revoke validator - k.validatorSet.Revoke(ctx, pubkey) - // Jail validator + k.validatorSet.Jail(ctx, pubkey) + + // Set validator jail duration signInfo, found := k.getValidatorSigningInfo(ctx, address) if !found { panic(fmt.Sprintf("Expected signing info for validator %s but not found", address)) @@ -113,16 +113,16 @@ func (k Keeper) handleValidatorSignature(ctx sdk.Context, addr crypto.Address, p minHeight := signInfo.StartHeight + k.SignedBlocksWindow(ctx) if height > minHeight && signInfo.SignedBlocksCounter < k.MinSignedPerWindow(ctx) { validator := k.validatorSet.ValidatorByPubKey(ctx, pubkey) - if validator != nil && !validator.GetRevoked() { - // Downtime confirmed, slash, revoke, and jail the validator + if validator != nil && !validator.GetJailed() { + // Downtime confirmed: slash and jail the validator logger.Info(fmt.Sprintf("Validator %s past min height of %d and below signed blocks threshold of %d", pubkey.Address(), minHeight, k.MinSignedPerWindow(ctx))) k.validatorSet.Slash(ctx, pubkey, height, power, k.SlashFractionDowntime(ctx)) - k.validatorSet.Revoke(ctx, pubkey) + k.validatorSet.Jail(ctx, pubkey) signInfo.JailedUntil = ctx.BlockHeader().Time.Add(k.DowntimeUnbondDuration(ctx)) } else { - // Validator was (a) not found or (b) already revoked, don't slash - logger.Info(fmt.Sprintf("Validator %s would have been slashed for downtime, but was either not found in store or already revoked", + // Validator was (a) not found or (b) already jailed, don't slash + logger.Info(fmt.Sprintf("Validator %s would have been slashed for downtime, but was either not found in store or already jailed", pubkey.Address())) } } diff --git a/x/slashing/keeper_test.go b/x/slashing/keeper_test.go index 5ebfec21a..808c82014 100644 --- a/x/slashing/keeper_test.go +++ b/x/slashing/keeper_test.go @@ -39,10 +39,10 @@ func TestHandleDoubleSign(t *testing.T) { // double sign less than max age keeper.handleDoubleSign(ctx, val.Address(), 0, time.Unix(0, 0), amtInt) - // should be revoked - require.True(t, sk.Validator(ctx, addr).GetRevoked()) - // unrevoke to measure power - sk.Unrevoke(ctx, val) + // should be jailed + require.True(t, sk.Validator(ctx, addr).GetJailed()) + // unjail to measure power + sk.Unjail(ctx, val) // power should be reduced require.Equal(t, sdk.NewDecFromInt(amt).Mul(sdk.NewDec(19).Quo(sdk.NewDec(20))), sk.Validator(ctx, addr).GetPower()) ctx = ctx.WithBlockHeader(abci.Header{Time: time.Unix(1, 0).Add(keeper.MaxEvidenceAge(ctx))}) @@ -112,17 +112,17 @@ func TestHandleAbsentValidator(t *testing.T) { require.Equal(t, int64(0), info.StartHeight) require.Equal(t, keeper.SignedBlocksWindow(ctx)-keeper.MinSignedPerWindow(ctx)-1, info.SignedBlocksCounter) - // validator should have been revoked + // validator should have been jailed validator, _ = sk.GetValidatorByPubKey(ctx, val) require.Equal(t, sdk.Unbonded, validator.GetStatus()) // unrevocation should fail prior to jail expiration - got = slh(ctx, NewMsgUnrevoke(addr)) + got = slh(ctx, NewMsgUnjail(addr)) require.False(t, got.IsOK()) // unrevocation should succeed after jail expiration ctx = ctx.WithBlockHeader(abci.Header{Time: time.Unix(1, 0).Add(keeper.DowntimeUnbondDuration(ctx))}) - got = slh(ctx, NewMsgUnrevoke(addr)) + got = slh(ctx, NewMsgUnjail(addr)) require.True(t, got.IsOK()) // validator should be rebonded now @@ -140,7 +140,7 @@ func TestHandleAbsentValidator(t *testing.T) { require.Equal(t, height, info.StartHeight) require.Equal(t, keeper.SignedBlocksWindow(ctx)-keeper.MinSignedPerWindow(ctx)-1, info.SignedBlocksCounter) - // validator should not be immediately revoked again + // validator should not be immediately jailed again height++ ctx = ctx.WithBlockHeight(height) keeper.handleValidatorSignature(ctx, val.Address(), amtInt, false) @@ -154,7 +154,7 @@ func TestHandleAbsentValidator(t *testing.T) { keeper.handleValidatorSignature(ctx, val.Address(), amtInt, false) } - // validator should be revoked again after 500 unsigned blocks + // validator should be jailed again after 500 unsigned blocks nextHeight = height + keeper.MinSignedPerWindow(ctx) + 1 for ; height <= nextHeight; height++ { ctx = ctx.WithBlockHeight(height) @@ -166,7 +166,7 @@ func TestHandleAbsentValidator(t *testing.T) { // Test a new validator entering the validator set // Ensure that SigningInfo.StartHeight is set correctly -// and that they are not immediately revoked +// and that they are not immediately jailed func TestHandleNewValidator(t *testing.T) { // initial setup ctx, ck, sk, _, keeper := createTestInput(t) @@ -194,16 +194,16 @@ func TestHandleNewValidator(t *testing.T) { require.Equal(t, int64(1), info.SignedBlocksCounter) require.Equal(t, time.Unix(0, 0).UTC(), info.JailedUntil) - // validator should be bonded still, should not have been revoked or slashed + // validator should be bonded still, should not have been jailed or slashed validator, _ := sk.GetValidatorByPubKey(ctx, val) require.Equal(t, sdk.Bonded, validator.GetStatus()) pool := sk.GetPool(ctx) require.Equal(t, int64(100), pool.BondedTokens.RoundInt64()) } -// Test a revoked validator being "down" twice +// Test a jailed validator being "down" twice // Ensure that they're only slashed once -func TestHandleAlreadyRevoked(t *testing.T) { +func TestHandleAlreadyJailed(t *testing.T) { // initial setup ctx, _, sk, _, keeper := createTestInput(t) @@ -228,7 +228,7 @@ func TestHandleAlreadyRevoked(t *testing.T) { keeper.handleValidatorSignature(ctx, val.Address(), amtInt, false) } - // validator should have been revoked and slashed + // validator should have been jailed and slashed validator, _ := sk.GetValidatorByPubKey(ctx, val) require.Equal(t, sdk.Unbonded, validator.GetStatus()) diff --git a/x/slashing/msg.go b/x/slashing/msg.go index 4b1483dce..e22eca7bd 100644 --- a/x/slashing/msg.go +++ b/x/slashing/msg.go @@ -11,25 +11,25 @@ var cdc = wire.NewCodec() const MsgType = "slashing" // verify interface at compile time -var _ sdk.Msg = &MsgUnrevoke{} +var _ sdk.Msg = &MsgUnjail{} -// MsgUnrevoke - struct for unrevoking revoked validator -type MsgUnrevoke struct { +// MsgUnjail - struct for unjailing jailed validator +type MsgUnjail struct { ValidatorAddr sdk.AccAddress `json:"address"` // address of the validator owner } -func NewMsgUnrevoke(validatorAddr sdk.AccAddress) MsgUnrevoke { - return MsgUnrevoke{ +func NewMsgUnjail(validatorAddr sdk.AccAddress) MsgUnjail { + return MsgUnjail{ ValidatorAddr: validatorAddr, } } //nolint -func (msg MsgUnrevoke) Type() string { return MsgType } -func (msg MsgUnrevoke) GetSigners() []sdk.AccAddress { return []sdk.AccAddress{msg.ValidatorAddr} } +func (msg MsgUnjail) Type() string { return MsgType } +func (msg MsgUnjail) GetSigners() []sdk.AccAddress { return []sdk.AccAddress{msg.ValidatorAddr} } // get the bytes for the message signer to sign on -func (msg MsgUnrevoke) GetSignBytes() []byte { +func (msg MsgUnjail) GetSignBytes() []byte { b, err := cdc.MarshalJSON(msg) if err != nil { panic(err) @@ -38,7 +38,7 @@ func (msg MsgUnrevoke) GetSignBytes() []byte { } // quick validity check -func (msg MsgUnrevoke) ValidateBasic() sdk.Error { +func (msg MsgUnjail) ValidateBasic() sdk.Error { if msg.ValidatorAddr == nil { return ErrBadValidatorAddr(DefaultCodespace) } diff --git a/x/slashing/msg_test.go b/x/slashing/msg_test.go index 287eb6c5c..ef2e673c8 100644 --- a/x/slashing/msg_test.go +++ b/x/slashing/msg_test.go @@ -8,9 +8,9 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" ) -func TestMsgUnrevokeGetSignBytes(t *testing.T) { +func TestMsgUnjailGetSignBytes(t *testing.T) { addr := sdk.AccAddress("abcd") - msg := NewMsgUnrevoke(addr) + msg := NewMsgUnjail(addr) bytes := msg.GetSignBytes() require.Equal(t, string(bytes), `{"address":"cosmosaccaddr1v93xxeqhyqz5v"}`) } diff --git a/x/slashing/signing_info.go b/x/slashing/signing_info.go index a21917e79..25a83e833 100644 --- a/x/slashing/signing_info.go +++ b/x/slashing/signing_info.go @@ -60,9 +60,9 @@ func NewValidatorSigningInfo(startHeight int64, indexOffset int64, jailedUntil t // Signing info for a validator type ValidatorSigningInfo struct { - StartHeight int64 `json:"start_height"` // height at which validator was first a candidate OR was unrevoked + StartHeight int64 `json:"start_height"` // height at which validator was first a candidate OR was unjailed IndexOffset int64 `json:"index_offset"` // index offset into signed block bit array - JailedUntil time.Time `json:"jailed_until"` // timestamp validator cannot be unrevoked until + JailedUntil time.Time `json:"jailed_until"` // timestamp validator cannot be unjailed until SignedBlocksCounter int64 `json:"signed_blocks_counter"` // signed blocks counter (to avoid scanning the array every time) } diff --git a/x/slashing/simulation/msgs.go b/x/slashing/simulation/msgs.go index b6a093674..8ef24493b 100644 --- a/x/slashing/simulation/msgs.go +++ b/x/slashing/simulation/msgs.go @@ -15,20 +15,20 @@ import ( "github.com/cosmos/cosmos-sdk/x/slashing" ) -// SimulateMsgUnrevoke -func SimulateMsgUnrevoke(k slashing.Keeper) simulation.TestAndRunTx { +// SimulateMsgUnjail +func SimulateMsgUnjail(k slashing.Keeper) simulation.TestAndRunTx { return func(t *testing.T, r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, keys []crypto.PrivKey, log string, event func(string)) (action string, err sdk.Error) { key := simulation.RandomKey(r, keys) address := sdk.AccAddress(key.PubKey().Address()) - msg := slashing.NewMsgUnrevoke(address) + msg := slashing.NewMsgUnjail(address) require.Nil(t, msg.ValidateBasic(), "expected msg to pass ValidateBasic: %s", msg.GetSignBytes()) ctx, write := ctx.CacheContext() result := slashing.NewHandler(k)(ctx, msg) if result.IsOK() { write() } - event(fmt.Sprintf("slashing/MsgUnrevoke/%v", result.IsOK())) - action = fmt.Sprintf("TestMsgUnrevoke: ok %v, msg %s", result.IsOK(), msg.GetSignBytes()) + event(fmt.Sprintf("slashing/MsgUnjail/%v", result.IsOK())) + action = fmt.Sprintf("TestMsgUnjail: ok %v, msg %s", result.IsOK(), msg.GetSignBytes()) return action, nil } } diff --git a/x/slashing/tick_test.go b/x/slashing/tick_test.go index b230f9c94..78d269549 100644 --- a/x/slashing/tick_test.go +++ b/x/slashing/tick_test.go @@ -77,7 +77,7 @@ func TestBeginBlocker(t *testing.T) { BeginBlocker(ctx, req, keeper) } - // validator should be revoked + // validator should be jailed validator, found := sk.GetValidatorByPubKey(ctx, pk) require.True(t, found) require.Equal(t, sdk.Unbonded, validator.GetStatus()) diff --git a/x/slashing/wire.go b/x/slashing/wire.go index 465a06587..208acda06 100644 --- a/x/slashing/wire.go +++ b/x/slashing/wire.go @@ -6,7 +6,7 @@ import ( // Register concrete types on wire codec func RegisterWire(cdc *wire.Codec) { - cdc.RegisterConcrete(MsgUnrevoke{}, "cosmos-sdk/MsgUnrevoke", nil) + cdc.RegisterConcrete(MsgUnjail{}, "cosmos-sdk/MsgUnjail", nil) } var cdcEmpty = wire.NewCodec() diff --git a/x/stake/handler.go b/x/stake/handler.go index 8f7475de9..c8be6a835 100644 --- a/x/stake/handler.go +++ b/x/stake/handler.go @@ -136,8 +136,8 @@ func handleMsgDelegate(ctx sdk.Context, msg types.MsgDelegate, k keeper.Keeper) if msg.Delegation.Denom != k.GetParams(ctx).BondDenom { return ErrBadDenom(k.Codespace()).Result() } - if validator.Revoked == true { - return ErrValidatorRevoked(k.Codespace()).Result() + if validator.Jailed { + return ErrValidatorJailed(k.Codespace()).Result() } _, err := k.Delegate(ctx, msg.DelegatorAddr, msg.Delegation, validator, true) if err != nil { diff --git a/x/stake/handler_test.go b/x/stake/handler_test.go index 9ff72090e..6ab54e48c 100644 --- a/x/stake/handler_test.go +++ b/x/stake/handler_test.go @@ -80,9 +80,9 @@ func TestValidatorByPowerIndex(t *testing.T) { got = handleMsgCreateValidator(ctx, msgCreateValidator, keeper) require.True(t, got.IsOK(), "expected create-validator to be ok, got %v", got) - // slash and revoke the first validator + // slash and jail the first validator keeper.Slash(ctx, keep.PKs[0], 0, initBond, sdk.NewDecWithPrec(5, 1)) - keeper.Revoke(ctx, keep.PKs[0]) + keeper.Jail(ctx, keep.PKs[0]) validator, found = keeper.GetValidator(ctx, validatorAddr) require.True(t, found) require.Equal(t, sdk.Unbonded, validator.Status) // ensure is unbonded @@ -387,7 +387,7 @@ func TestMultipleMsgCreateValidator(t *testing.T) { require.Equal(t, balanceExpd, balanceGot, "expected account to have %d, got %d", balanceExpd, balanceGot) } - // unbond them all by revoking delegation + // unbond them all by removing delegation for i, validatorAddr := range validatorAddrs { _, found := keeper.GetValidator(ctx, validatorAddr) require.True(t, found) @@ -449,7 +449,7 @@ func TestMultipleMsgDelegate(t *testing.T) { } } -func TestRevokeValidator(t *testing.T) { +func TestJailValidator(t *testing.T) { ctx, _, keeper := keep.CreateTestInput(t, false, 1000) validatorAddr, delegatorAddr := keep.Addrs[0], keep.Addrs[1] _ = setInstantUnbondPeriod(keeper, ctx) @@ -476,9 +476,9 @@ func TestRevokeValidator(t *testing.T) { validator, found := keeper.GetValidator(ctx, validatorAddr) require.True(t, found) - require.True(t, validator.Revoked, "%v", validator) + require.True(t, validator.Jailed, "%v", validator) - // test that this address cannot yet be bonded too because is revoked + // test that this address cannot yet be bonded too because is jailed got = handleMsgDelegate(ctx, msgDelegate, keeper) require.False(t, got.IsOK(), "expected error, got %v", got) diff --git a/x/stake/keeper/delegation.go b/x/stake/keeper/delegation.go index 4256df4b5..2bad79c20 100644 --- a/x/stake/keeper/delegation.go +++ b/x/stake/keeper/delegation.go @@ -283,9 +283,9 @@ func (k Keeper) unbond(ctx sdk.Context, delegatorAddr, validatorAddr sdk.AccAddr if delegation.Shares.IsZero() { // if the delegation is the operator of the validator then - // trigger a revoke validator - if bytes.Equal(delegation.DelegatorAddr, validator.Operator) && validator.Revoked == false { - validator.Revoked = true + // trigger a jail validator + if bytes.Equal(delegation.DelegatorAddr, validator.Operator) && validator.Jailed == false { + validator.Jailed = true } k.RemoveDelegation(ctx, delegation) } else { diff --git a/x/stake/keeper/key.go b/x/stake/keeper/key.go index 502ec2654..958de62ed 100644 --- a/x/stake/keeper/key.go +++ b/x/stake/keeper/key.go @@ -73,11 +73,11 @@ func getValidatorPowerRank(validator types.Validator, pool types.Pool) []byte { potentialPower := validator.Tokens powerBytes := []byte(potentialPower.ToLeftPadded(maxDigitsForAccount)) // power big-endian (more powerful validators first) - revokedBytes := make([]byte, 1) - if validator.Revoked { - revokedBytes[0] = byte(0x00) + jailedBytes := make([]byte, 1) + if validator.Jailed { + jailedBytes[0] = byte(0x00) } else { - revokedBytes[0] = byte(0x01) + jailedBytes[0] = byte(0x01) } // heightBytes and counterBytes represent strings like powerBytes does @@ -88,7 +88,7 @@ func getValidatorPowerRank(validator types.Validator, pool types.Pool) []byte { return append(append(append(append( ValidatorsByPowerIndexKey, - revokedBytes...), + jailedBytes...), powerBytes...), heightBytes...), counterBytes...) diff --git a/x/stake/keeper/slash.go b/x/stake/keeper/slash.go index 471886c26..aa6fe974d 100644 --- a/x/stake/keeper/slash.go +++ b/x/stake/keeper/slash.go @@ -115,31 +115,31 @@ func (k Keeper) Slash(ctx sdk.Context, pubkey crypto.PubKey, infractionHeight in return } -// revoke a validator -func (k Keeper) Revoke(ctx sdk.Context, pubkey crypto.PubKey) { - k.setRevoked(ctx, pubkey, true) +// jail a validator +func (k Keeper) Jail(ctx sdk.Context, pubkey crypto.PubKey) { + k.setJailed(ctx, pubkey, true) logger := ctx.Logger().With("module", "x/stake") - logger.Info(fmt.Sprintf("Validator %s revoked", pubkey.Address())) + logger.Info(fmt.Sprintf("Validator %s jailed", pubkey.Address())) // TODO Return event(s), blocked on https://github.com/tendermint/tendermint/pull/1803 return } -// unrevoke a validator -func (k Keeper) Unrevoke(ctx sdk.Context, pubkey crypto.PubKey) { - k.setRevoked(ctx, pubkey, false) +// unjail a validator +func (k Keeper) Unjail(ctx sdk.Context, pubkey crypto.PubKey) { + k.setJailed(ctx, pubkey, false) logger := ctx.Logger().With("module", "x/stake") - logger.Info(fmt.Sprintf("Validator %s unrevoked", pubkey.Address())) + logger.Info(fmt.Sprintf("Validator %s unjailed", pubkey.Address())) // TODO Return event(s), blocked on https://github.com/tendermint/tendermint/pull/1803 return } -// set the revoked flag on a validator -func (k Keeper) setRevoked(ctx sdk.Context, pubkey crypto.PubKey, revoked bool) { +// set the jailed flag on a validator +func (k Keeper) setJailed(ctx sdk.Context, pubkey crypto.PubKey, jailed bool) { validator, found := k.GetValidatorByPubKey(ctx, pubkey) if !found { - panic(fmt.Errorf("Validator with pubkey %s not found, cannot set revoked to %v", pubkey, revoked)) + panic(fmt.Errorf("Validator with pubkey %s not found, cannot set jailed to %v", pubkey, jailed)) } - validator.Revoked = revoked + validator.Jailed = jailed k.UpdateValidator(ctx, validator) // update validator, possibly unbonding or bonding it return } diff --git a/x/stake/keeper/slash_test.go b/x/stake/keeper/slash_test.go index 444858f29..156f0602e 100644 --- a/x/stake/keeper/slash_test.go +++ b/x/stake/keeper/slash_test.go @@ -34,7 +34,7 @@ func setupHelper(t *testing.T, amt int64) (sdk.Context, Keeper, types.Params) { return ctx, keeper, params } -// tests Revoke, Unrevoke +// tests Jail, Unjail func TestRevocation(t *testing.T) { // setup ctx, keeper, _ := setupHelper(t, 10) @@ -44,19 +44,19 @@ func TestRevocation(t *testing.T) { // initial state val, found := keeper.GetValidator(ctx, addr) require.True(t, found) - require.False(t, val.GetRevoked()) + require.False(t, val.GetJailed()) - // test revoke - keeper.Revoke(ctx, pk) + // test jail + keeper.Jail(ctx, pk) val, found = keeper.GetValidator(ctx, addr) require.True(t, found) - require.True(t, val.GetRevoked()) + require.True(t, val.GetJailed()) - // test unrevoke - keeper.Unrevoke(ctx, pk) + // test unjail + keeper.Unjail(ctx, pk) val, found = keeper.GetValidator(ctx, addr) require.True(t, found) - require.False(t, val.GetRevoked()) + require.False(t, val.GetJailed()) } diff --git a/x/stake/keeper/validator.go b/x/stake/keeper/validator.go index c828cc38e..e933a6d23 100644 --- a/x/stake/keeper/validator.go +++ b/x/stake/keeper/validator.go @@ -205,7 +205,7 @@ func (k Keeper) UpdateValidator(ctx sdk.Context, validator types.Validator) type pool := k.GetPool(ctx) oldValidator, oldFound := k.GetValidator(ctx, validator.Operator) - validator = k.updateForRevoking(ctx, oldFound, oldValidator, validator) + validator = k.updateForJailing(ctx, oldFound, oldValidator, validator) powerIncreasing := k.getPowerIncreasing(ctx, oldFound, oldValidator, validator) validator.BondHeight, validator.BondIntraTxCounter = k.bondIncrement(ctx, oldFound, oldValidator, validator) valPower := k.updateValidatorPower(ctx, oldFound, oldValidator, validator, pool) @@ -216,7 +216,7 @@ func (k Keeper) UpdateValidator(ctx sdk.Context, validator types.Validator) type // perform the following: // a) update Tendermint // b) check if the cliff validator needs to be updated - case powerIncreasing && !validator.Revoked && + case powerIncreasing && !validator.Jailed && (oldFound && oldValidator.Status == sdk.Bonded): bz := k.cdc.MustMarshalBinary(validator.ABCIValidator()) @@ -293,8 +293,8 @@ func (k Keeper) updateCliffValidator(ctx sdk.Context, affectedVal types.Validato panic(fmt.Sprintf("validator record not found for address: %v\n", ownerAddr)) } - if currVal.Status != sdk.Bonded || currVal.Revoked { - panic(fmt.Sprintf("unexpected revoked or unbonded validator for address: %s\n", ownerAddr)) + if currVal.Status != sdk.Bonded || currVal.Jailed { + panic(fmt.Sprintf("unexpected jailed or unbonded validator for address: %s\n", ownerAddr)) } newCliffVal = currVal @@ -319,11 +319,11 @@ func (k Keeper) updateCliffValidator(ctx sdk.Context, affectedVal types.Validato } } -func (k Keeper) updateForRevoking(ctx sdk.Context, oldFound bool, oldValidator, newValidator types.Validator) types.Validator { - if newValidator.Revoked && oldFound && oldValidator.Status == sdk.Bonded { +func (k Keeper) updateForJailing(ctx sdk.Context, oldFound bool, oldValidator, newValidator types.Validator) types.Validator { + if newValidator.Jailed && oldFound && oldValidator.Status == sdk.Bonded { newValidator = k.unbondValidator(ctx, newValidator) - // need to also clear the cliff validator spot because the revoke has + // need to also clear the cliff validator spot because the jail has // opened up a new spot which will be filled when // updateValidatorsBonded is called k.clearCliffValidator(ctx) @@ -417,7 +417,7 @@ func (k Keeper) UpdateBondedValidators( } // increment bondedValidatorsCount / get the validator to bond - if !validator.Revoked { + if !validator.Jailed { if validator.Status != sdk.Bonded { validatorToBond = validator if newValidatorBonded { @@ -529,11 +529,11 @@ func (k Keeper) UpdateBondedValidatorsFull(ctx sdk.Context) { validator = k.bondValidator(ctx, validator) } - if validator.Revoked { + if validator.Jailed { // we should no longer consider jailed validators as they are ranked // lower than any non-jailed/bonded validators if validator.Status == sdk.Bonded { - panic(fmt.Sprintf("revoked validator cannot be bonded for address: %s\n", ownerAddr)) + panic(fmt.Sprintf("jailed validator cannot be bonded for address: %s\n", ownerAddr)) } break diff --git a/x/stake/stake.go b/x/stake/stake.go index c5185433d..2782957ce 100644 --- a/x/stake/stake.go +++ b/x/stake/stake.go @@ -94,7 +94,7 @@ var ( ErrNoValidatorFound = types.ErrNoValidatorFound ErrValidatorOwnerExists = types.ErrValidatorOwnerExists ErrValidatorPubKeyExists = types.ErrValidatorPubKeyExists - ErrValidatorRevoked = types.ErrValidatorRevoked + ErrValidatorJailed = types.ErrValidatorJailed ErrBadRemoveValidator = types.ErrBadRemoveValidator ErrDescriptionLength = types.ErrDescriptionLength ErrCommissionNegative = types.ErrCommissionNegative diff --git a/x/stake/types/errors.go b/x/stake/types/errors.go index d44ed411c..0c1d9192d 100644 --- a/x/stake/types/errors.go +++ b/x/stake/types/errors.go @@ -44,8 +44,8 @@ func ErrValidatorPubKeyExists(codespace sdk.CodespaceType) sdk.Error { return sdk.NewError(codespace, CodeInvalidValidator, "validator already exist for this pubkey, must use new validator pubkey") } -func ErrValidatorRevoked(codespace sdk.CodespaceType) sdk.Error { - return sdk.NewError(codespace, CodeInvalidValidator, "validator for this address is currently revoked") +func ErrValidatorJailed(codespace sdk.CodespaceType) sdk.Error { + return sdk.NewError(codespace, CodeInvalidValidator, "validator for this address is currently jailed") } func ErrBadRemoveValidator(codespace sdk.CodespaceType) sdk.Error { diff --git a/x/stake/types/validator.go b/x/stake/types/validator.go index 4b2cf46a6..f8404b596 100644 --- a/x/stake/types/validator.go +++ b/x/stake/types/validator.go @@ -22,7 +22,7 @@ import ( type Validator struct { Operator sdk.AccAddress `json:"operator"` // sender of BondTx - UnbondTx returns here PubKey crypto.PubKey `json:"pub_key"` // pubkey of validator - Revoked bool `json:"revoked"` // has the validator been revoked from bonded status? + Jailed bool `json:"jailed"` // has the validator been jailed from bonded status? Status sdk.BondStatus `json:"status"` // validator status (bonded/unbonding/unbonded) Tokens sdk.Dec `json:"tokens"` // delegated tokens (incl. self-delegation) @@ -47,7 +47,7 @@ func NewValidator(operator sdk.AccAddress, pubKey crypto.PubKey, description Des return Validator{ Operator: operator, PubKey: pubKey, - Revoked: false, + Jailed: false, Status: sdk.Unbonded, Tokens: sdk.ZeroDec(), DelegatorShares: sdk.ZeroDec(), @@ -66,7 +66,7 @@ func NewValidator(operator sdk.AccAddress, pubKey crypto.PubKey, description Des // what's kept in the store value type validatorValue struct { PubKey crypto.PubKey - Revoked bool + Jailed bool Status sdk.BondStatus Tokens sdk.Dec DelegatorShares sdk.Dec @@ -85,7 +85,7 @@ type validatorValue struct { func MustMarshalValidator(cdc *wire.Codec, validator Validator) []byte { val := validatorValue{ PubKey: validator.PubKey, - Revoked: validator.Revoked, + Jailed: validator.Jailed, Status: validator.Status, Tokens: validator.Tokens, DelegatorShares: validator.DelegatorShares, @@ -127,7 +127,7 @@ func UnmarshalValidator(cdc *wire.Codec, operatorAddr, value []byte) (validator return Validator{ Operator: operatorAddr, PubKey: storeValue.PubKey, - Revoked: storeValue.Revoked, + Jailed: storeValue.Jailed, Tokens: storeValue.Tokens, Status: storeValue.Status, DelegatorShares: storeValue.DelegatorShares, @@ -155,7 +155,7 @@ func (v Validator) HumanReadableString() (string, error) { resp := "Validator \n" resp += fmt.Sprintf("Operator: %s\n", v.Operator) resp += fmt.Sprintf("Validator: %s\n", bechVal) - resp += fmt.Sprintf("Revoked: %v\n", v.Revoked) + resp += fmt.Sprintf("Jailed: %v\n", v.Jailed) resp += fmt.Sprintf("Status: %s\n", sdk.BondStatusToString(v.Status)) resp += fmt.Sprintf("Tokens: %s\n", v.Tokens.String()) resp += fmt.Sprintf("Delegator Shares: %s\n", v.DelegatorShares.String()) @@ -177,7 +177,7 @@ func (v Validator) HumanReadableString() (string, error) { type BechValidator struct { Operator sdk.AccAddress `json:"operator"` // in bech32 PubKey string `json:"pub_key"` // in bech32 - Revoked bool `json:"revoked"` // has the validator been revoked from bonded status? + Jailed bool `json:"jailed"` // has the validator been jailed from bonded status? Status sdk.BondStatus `json:"status"` // validator status (bonded/unbonding/unbonded) Tokens sdk.Dec `json:"tokens"` // delegated tokens (incl. self-delegation) @@ -207,7 +207,7 @@ func (v Validator) Bech32Validator() (BechValidator, error) { return BechValidator{ Operator: v.Operator, PubKey: bechValPubkey, - Revoked: v.Revoked, + Jailed: v.Jailed, Status: v.Status, Tokens: v.Tokens, @@ -429,7 +429,7 @@ func (v Validator) BondedTokens() sdk.Dec { var _ sdk.Validator = Validator{} // nolint - for sdk.Validator -func (v Validator) GetRevoked() bool { return v.Revoked } +func (v Validator) GetJailed() bool { return v.Jailed } func (v Validator) GetMoniker() string { return v.Description.Moniker } func (v Validator) GetStatus() sdk.BondStatus { return v.Status } func (v Validator) GetOperator() sdk.AccAddress { return v.Operator }