diff --git a/CHANGELOG.md b/CHANGELOG.md index fb28675939da..a88cf406df83 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,127 @@ # Changelog +## 0.31.0 + +BREAKING CHANGES + +* Gaia REST API (`gaiacli advanced rest-server`) + * [\#3284](https://github.com/cosmos/cosmos-sdk/issues/3284) Rename the `name` + field to `from` in the `base_req` body. + * [\#3485](https://github.com/cosmos/cosmos-sdk/pull/3485) Error responses are now JSON objects. + * [\#3477][distribution] endpoint changed "all_delegation_rewards" -> "delegator_total_rewards" + +* Gaia CLI (`gaiacli`) + - [#3399](https://github.com/cosmos/cosmos-sdk/pull/3399) Add `gaiad validate-genesis` command to facilitate checking of genesis files + - [\#1894](https://github.com/cosmos/cosmos-sdk/issues/1894) `version` prints out short info by default. Add `--long` flag. Proper handling of `--format` flag introduced. + - [\#3465](https://github.com/cosmos/cosmos-sdk/issues/3465) `gaiacli rest-server` switched back to insecure mode by default: + - `--insecure` flag is removed. + - `--tls` is now used to enable secure layer. + - [\#3451](https://github.com/cosmos/cosmos-sdk/pull/3451) `gaiacli` now returns transactions in plain text including tags. + - [\#3497](https://github.com/cosmos/cosmos-sdk/issues/3497) `gaiad init` now takes moniker as required arguments, not as parameter. + * [\#3501](https://github.com/cosmos/cosmos-sdk/issues/3501) Change validator + address Bech32 encoding to consensus address in `tendermint-validator-set`. + +* Gaia + * [\#3457](https://github.com/cosmos/cosmos-sdk/issues/3457) Changed governance tally validatorGovInfo to use sdk.Int power instead of sdk.Dec + * [\#3495](https://github.com/cosmos/cosmos-sdk/issues/3495) Added Validator Minimum Self Delegation + * Reintroduce OR semantics for tx fees + +* SDK + * [\#2513](https://github.com/cosmos/cosmos-sdk/issues/2513) Tendermint updates are adjusted by 10^-6 relative to staking tokens, + * [\#3487](https://github.com/cosmos/cosmos-sdk/pull/3487) Move HTTP/REST utilities out of client/utils into a new dedicated client/rest package. + * [\#3490](https://github.com/cosmos/cosmos-sdk/issues/3490) ReadRESTReq() returns bool to avoid callers to write error responses twice. + * [\#3502](https://github.com/cosmos/cosmos-sdk/pull/3502) Fixes issue when comparing genesis states + * [\#3514](https://github.com/cosmos/cosmos-sdk/pull/3514) Various clean ups: + - Replace all GetKeyBase\* functions family in favor of NewKeyBaseFromDir and NewKeyBaseFromHomeFlag. + - Remove Get prefix from all TxBuilder's getters. + * [\#3522](https://github.com/cosmos/cosmos-sdk/pull/3522) Get rid of double negatives: Coins.IsNotNegative() -> Coins.IsAnyNegative(). + * [\#3561](https://github.com/cosmos/cosmos-sdk/issues/3561) Don't unnecessarily store denominations in staking + + +FEATURES + +* Gaia REST API + * [\#2358](https://github.com/cosmos/cosmos-sdk/issues/2358) Add distribution module REST interface + +* Gaia CLI (`gaiacli`) + * [\#3429](https://github.com/cosmos/cosmos-sdk/issues/3429) Support querying + for all delegator distribution rewards. + * [\#3449](https://github.com/cosmos/cosmos-sdk/issues/3449) Proof verification now works with absence proofs + * [\#3484](https://github.com/cosmos/cosmos-sdk/issues/3484) Add support + vesting accounts to the add-genesis-account command. + +* Gaia + - [\#3397](https://github.com/cosmos/cosmos-sdk/pull/3397) Implement genesis file sanitization to avoid failures at chain init. + * [\#3428](https://github.com/cosmos/cosmos-sdk/issues/3428) Run the simulation from a particular genesis state loaded from a file + +* SDK + * [\#3270](https://github.com/cosmos/cosmos-sdk/issues/3270) [x/staking] limit number of ongoing unbonding delegations /redelegations per pair/trio + * [\#3477][distribution] new query endpoint "delegator_validators" + * [\#3514](https://github.com/cosmos/cosmos-sdk/pull/3514) Provided a lazy loading implementation of Keybase that locks the underlying + storage only for the time needed to perform the required operation. Also added Keybase reference to TxBuilder struct. + * [types] [\#2580](https://github.com/cosmos/cosmos-sdk/issues/2580) Addresses now Bech32 empty addresses to an empty string + + +IMPROVEMENTS + +* Gaia REST API + * [\#3284](https://github.com/cosmos/cosmos-sdk/issues/3284) Update Gaia Lite + REST service to support the following: + * Automatic account number and sequence population when fields are omitted + * Generate only functionality no longer requires access to a local Keybase + * `from` field in the `base_req` body can be a Keybase name or account address + * [\#3423](https://github.com/cosmos/cosmos-sdk/issues/3423) Allow simulation + (auto gas) to work with generate only. + * [\#3514](https://github.com/cosmos/cosmos-sdk/pull/3514) REST server calls to keybase does not lock the underlying storage anymore. + * [\#3523](https://github.com/cosmos/cosmos-sdk/pull/3523) Added `/tx/encode` endpoint to serialize a JSON tx to base64-encoded Amino. + +* Gaia CLI (`gaiacli`) + * [\#3476](https://github.com/cosmos/cosmos-sdk/issues/3476) New `withdraw-all-rewards` command to withdraw all delegations rewards for delegators. + * [\#3497](https://github.com/cosmos/cosmos-sdk/issues/3497) `gaiad gentx` supports `--ip` and `--node-id` flags to override defaults. + * [\#3518](https://github.com/cosmos/cosmos-sdk/issues/3518) Fix flow in + `keys add` to show the mnemonic by default. + * [\#3517](https://github.com/cosmos/cosmos-sdk/pull/3517) Increased test coverage + * [\#3523](https://github.com/cosmos/cosmos-sdk/pull/3523) Added `tx encode` command to serialize a JSON tx to base64-encoded Amino. + +* Gaia + * [\#3418](https://github.com/cosmos/cosmos-sdk/issues/3418) Add vesting account + genesis validation checks to `GaiaValidateGenesisState`. + * [\#3420](https://github.com/cosmos/cosmos-sdk/issues/3420) Added maximum length to governance proposal descriptions and titles + * [\#3256](https://github.com/cosmos/cosmos-sdk/issues/3256) Add gas consumption + for tx size in the ante handler. + * [\#3454](https://github.com/cosmos/cosmos-sdk/pull/3454) Add `--jail-whitelist` to `gaiad export` to enable testing of complex exports + * [\#3424](https://github.com/cosmos/cosmos-sdk/issues/3424) Allow generation of gentxs with empty memo field. + * [\#3507](https://github.com/cosmos/cosmos-sdk/issues/3507) General cleanup, removal of unnecessary struct fields, undelegation bugfix, and comment clarification in x/staking and x/slashing + +* SDK + * [\#2605] x/params add subkey accessing + * [\#2986](https://github.com/cosmos/cosmos-sdk/pull/2986) Store Refactor + * [\#3435](https://github.com/cosmos/cosmos-sdk/issues/3435) Test that store implementations do not allow nil values + * [\#2509](https://github.com/cosmos/cosmos-sdk/issues/2509) Sanitize all usage of Dec.RoundInt64() + * [\#556](https://github.com/cosmos/cosmos-sdk/issues/556) Increase `BaseApp` + test coverage. + * [\#3357](https://github.com/cosmos/cosmos-sdk/issues/3357) develop state-transitions.md for staking spec, missing states added to `state.md` + * [\#3552](https://github.com/cosmos/cosmos-sdk/pull/3552) Validate bit length when + deserializing `Int` types. + + +BUG FIXES + +* Gaia CLI (`gaiacli`) + - [\#3417](https://github.com/cosmos/cosmos-sdk/pull/3417) Fix `q slashing signing-info` panic by ensuring safety of user input and properly returning not found error + - [\#3345](https://github.com/cosmos/cosmos-sdk/issues/3345) Upgrade ledger-cosmos-go dependency to v0.9.3 to pull + https://github.com/ZondaX/ledger-cosmos-go/commit/ed9aa39ce8df31bad1448c72d3d226bf2cb1a8d1 in order to fix a derivation path issue that causes `gaiacli keys add --recover` + to malfunction. + - [\#3419](https://github.com/cosmos/cosmos-sdk/pull/3419) Fix `q distr slashes` panic + - [\#3453](https://github.com/cosmos/cosmos-sdk/pull/3453) The `rest-server` command didn't respect persistent flags such as `--chain-id` and `--trust-node` if they were + passed on the command line. + - [\#3441](https://github.com/cosmos/cosmos-sdk/pull/3431) Improved resource management and connection handling (ledger devices). Fixes issue with DER vs BER signatures. + +* Gaia + * [\#3486](https://github.com/cosmos/cosmos-sdk/pull/3486) Use AmountOf in + vesting accounts instead of zipping/aligning denominations. + + ## 0.30.0 BREAKING CHANGES diff --git a/PENDING.md b/PENDING.md index 73f3e1771255..164614789df8 100644 --- a/PENDING.md +++ b/PENDING.md @@ -2,65 +2,25 @@ BREAKING CHANGES -* Gaia REST API (`gaiacli advanced rest-server`) - * [\#3284](https://github.com/cosmos/cosmos-sdk/issues/3284) Rename the `name` - field to `from` in the `base_req` body. - * [\#3485](https://github.com/cosmos/cosmos-sdk/pull/3485) Error responses are now JSON objects. - * [\#3477][distribution] endpoint changed "all_delegation_rewards" -> "delegator_total_rewards" - -* Gaia CLI (`gaiacli`) - - [#3399](https://github.com/cosmos/cosmos-sdk/pull/3399) Add `gaiad validate-genesis` command to facilitate checking of genesis files - - [\#1894](https://github.com/cosmos/cosmos-sdk/issues/1894) `version` prints out short info by default. Add `--long` flag. Proper handling of `--format` flag introduced. - - [\#3465](https://github.com/cosmos/cosmos-sdk/issues/3465) `gaiacli rest-server` switched back to insecure mode by default: - - `--insecure` flag is removed. - - `--tls` is now used to enable secure layer. - - [\#3451](https://github.com/cosmos/cosmos-sdk/pull/3451) `gaiacli` now returns transactions in plain text including tags. - - [\#3497](https://github.com/cosmos/cosmos-sdk/issues/3497) `gaiad init` now takes moniker as required arguments, not as parameter. - * [\#3501](https://github.com/cosmos/cosmos-sdk/issues/3501) Change validator - address Bech32 encoding to consensus address in `tendermint-validator-set`. +* Gaia REST API + +* Gaia CLI * Gaia - * [\#3457](https://github.com/cosmos/cosmos-sdk/issues/3457) Changed governance tally validatorGovInfo to use sdk.Int power instead of sdk.Dec - * [\#3495](https://github.com/cosmos/cosmos-sdk/issues/3495) Added Validator Minimum Self Delegation - * Reintroduce OR semantics for tx fees * SDK - * \#2513 Tendermint updates are adjusted by 10^-6 relative to staking tokens, - * [\#3487](https://github.com/cosmos/cosmos-sdk/pull/3487) Move HTTP/REST utilities out of client/utils into a new dedicated client/rest package. - * [\#3490](https://github.com/cosmos/cosmos-sdk/issues/3490) ReadRESTReq() returns bool to avoid callers to write error responses twice. - * [\#3502](https://github.com/cosmos/cosmos-sdk/pull/3502) Fixes issue when comparing genesis states - * [\#3514](https://github.com/cosmos/cosmos-sdk/pull/3514) Various clean ups: - - Replace all GetKeyBase\* functions family in favor of NewKeyBaseFromDir and NewKeyBaseFromHomeFlag. - - Remove Get prefix from all TxBuilder's getters. - * [\#3522](https://github.com/cosmos/cosmos-sdk/pull/3522) Get rid of double negatives: Coins.IsNotNegative() -> Coins.IsAnyNegative(). - * \#3561 Don't unnecessarily store denominations in staking - * Tendermint - * [\#3563](https://github.com/cosmos/cosmos-sdk/pull/3563) Upgrade to TM v0.30.0-rc0 FEATURES * Gaia REST API - * [\#2358](https://github.com/cosmos/cosmos-sdk/issues/2358) Add distribution module REST interface -* Gaia CLI (`gaiacli`) - * [\#3429](https://github.com/cosmos/cosmos-sdk/issues/3429) Support querying - for all delegator distribution rewards. - * \#3449 Proof verification now works with absence proofs - * [\#3484](https://github.com/cosmos/cosmos-sdk/issues/3484) Add support - vesting accounts to the add-genesis-account command. +* Gaia CLI * Gaia - - [\#3397](https://github.com/cosmos/cosmos-sdk/pull/3397) Implement genesis file sanitization to avoid failures at chain init. - * \#3428 Run the simulation from a particular genesis state loaded from a file * SDK - * \#3270 [x/staking] limit number of ongoing unbonding delegations /redelegations per pair/trio - * [\#3477][distribution] new query endpoint "delegator_validators" - * [\#3514](https://github.com/cosmos/cosmos-sdk/pull/3514) Provided a lazy loading implementation of Keybase that locks the underlying - storage only for the time needed to perform the required operation. Also added Keybase reference to TxBuilder struct. - * [types] [\#2580](https://github.com/cosmos/cosmos-sdk/issues/2580) Addresses now Bech32 empty addresses to an empty string * Tendermint @@ -68,44 +28,12 @@ FEATURES IMPROVEMENTS * Gaia REST API - * [\#3284](https://github.com/cosmos/cosmos-sdk/issues/3284) Update Gaia Lite - REST service to support the following: - * Automatic account number and sequence population when fields are omitted - * Generate only functionality no longer requires access to a local Keybase - * `from` field in the `base_req` body can be a Keybase name or account address - * [\#3423](https://github.com/cosmos/cosmos-sdk/issues/3423) Allow simulation - (auto gas) to work with generate only. - * [\#3514](https://github.com/cosmos/cosmos-sdk/pull/3514) REST server calls to keybase does not lock the underlying storage anymore. - * [\#3523](https://github.com/cosmos/cosmos-sdk/pull/3523) Added `/tx/encode` endpoint to serialize a JSON tx to base64-encoded Amino. - -* Gaia CLI (`gaiacli`) - * [\#3476](https://github.com/cosmos/cosmos-sdk/issues/3476) New `withdraw-all-rewards` command to withdraw all delegations rewards for delegators. - * [\#3497](https://github.com/cosmos/cosmos-sdk/issues/3497) `gaiad gentx` supports `--ip` and `--node-id` flags to override defaults. - * [\#3518](https://github.com/cosmos/cosmos-sdk/issues/3518) Fix flow in - `keys add` to show the mnemonic by default. - * [\#3517](https://github.com/cosmos/cosmos-sdk/pull/3517) Increased test coverage - * [\#3523](https://github.com/cosmos/cosmos-sdk/pull/3523) Added `tx encode` command to serialize a JSON tx to base64-encoded Amino. + +* Gaia CLI * Gaia - * [\#3418](https://github.com/cosmos/cosmos-sdk/issues/3418) Add vesting account - genesis validation checks to `GaiaValidateGenesisState`. - * [\#3420](https://github.com/cosmos/cosmos-sdk/issues/3420) Added maximum length to governance proposal descriptions and titles - * [\#3256](https://github.com/cosmos/cosmos-sdk/issues/3256) Add gas consumption - for tx size in the ante handler. - * [\#3454](https://github.com/cosmos/cosmos-sdk/pull/3454) Add `--jail-whitelist` to `gaiad export` to enable testing of complex exports - * [\#3424](https://github.com/cosmos/cosmos-sdk/issues/3424) Allow generation of gentxs with empty memo field. - * \#3507 General cleanup, removal of unnecessary struct fields, undelegation bugfix, and comment clarification in x/staking and x/slashing * SDK - * [\#2605] x/params add subkey accessing - * [\#2986](https://github.com/cosmos/cosmos-sdk/pull/2986) Store Refactor - * \#3435 Test that store implementations do not allow nil values - * \#2509 Sanitize all usage of Dec.RoundInt64() - * [\#556](https://github.com/cosmos/cosmos-sdk/issues/556) Increase `BaseApp` - test coverage. - * \#3357 develop state-transitions.md for staking spec, missing states added to `state.md` - * [\#3552](https://github.com/cosmos/cosmos-sdk/pull/3552) Validate bit length when - deserializing `Int` types. * Tendermint @@ -114,20 +42,10 @@ BUG FIXES * Gaia REST API -* Gaia CLI (`gaiacli`) - - [\#3417](https://github.com/cosmos/cosmos-sdk/pull/3417) Fix `q slashing signing-info` panic by ensuring safety of user input and properly returning not found error - - [\#3345](https://github.com/cosmos/cosmos-sdk/issues/3345) Upgrade ledger-cosmos-go dependency to v0.9.3 to pull - https://github.com/ZondaX/ledger-cosmos-go/commit/ed9aa39ce8df31bad1448c72d3d226bf2cb1a8d1 in order to fix a derivation path issue that causes `gaiacli keys add --recover` - to malfunction. - - [\#3419](https://github.com/cosmos/cosmos-sdk/pull/3419) Fix `q distr slashes` panic - - [\#3453](https://github.com/cosmos/cosmos-sdk/pull/3453) The `rest-server` command didn't respect persistent flags such as `--chain-id` and `--trust-node` if they were - passed on the command line. - - [\#3441](https://github.com/cosmos/cosmos-sdk/pull/3431) Improved resource management and connection handling (ledger devices). Fixes issue with DER vs BER signatures. +* Gaia CLI * Gaia - * [\#3486](https://github.com/cosmos/cosmos-sdk/pull/3486) Use AmountOf in - vesting accounts instead of zipping/aligning denominations. * SDK -* Tendermint +* Tendermint \ No newline at end of file diff --git a/client/lcd/lcd_test.go b/client/lcd/lcd_test.go index 5a4ef35d4dd2..56dd65d3e4fc 100644 --- a/client/lcd/lcd_test.go +++ b/client/lcd/lcd_test.go @@ -870,7 +870,7 @@ func TestVote(t *testing.T) { require.Equal(t, gov.OptionYes, vote.Option) tally := getTally(t, port, proposalID) - require.Equal(t, sdk.ZeroDec(), tally.Yes, "tally should be 0 as the address is not bonded") + require.Equal(t, sdk.ZeroInt(), tally.Yes, "tally should be 0 as the address is not bonded") // create bond TX delTokens := staking.TokensFromTendermintPower(60) @@ -885,7 +885,7 @@ func TestVote(t *testing.T) { expectedBalance = coins[0] tally = getTally(t, port, proposalID) - require.Equal(t, sdk.NewDecFromInt(delTokens), tally.Yes, "tally should be equal to the amount delegated") + require.Equal(t, delTokens, tally.Yes, "tally should be equal to the amount delegated") // change vote option resultTx = doVote(t, port, seed, name1, pw, addr, proposalID, "No", fees) @@ -897,8 +897,8 @@ func TestVote(t *testing.T) { require.Equal(t, expectedBalance.Amount, acc.GetCoins().AmountOf(staking.DefaultBondDenom)) tally = getTally(t, port, proposalID) - require.Equal(t, sdk.ZeroDec(), tally.Yes, "tally should be 0 the user changed the option") - require.Equal(t, sdk.NewDecFromInt(delTokens), tally.No, "tally should be equal to the amount delegated") + require.Equal(t, sdk.ZeroInt(), tally.Yes, "tally should be 0 the user changed the option") + require.Equal(t, delTokens, tally.No, "tally should be equal to the amount delegated") } func TestUnjail(t *testing.T) { diff --git a/x/gov/client/module_client.go b/x/gov/client/module_client.go index f66546ba92c2..9f4b29e12df6 100644 --- a/x/gov/client/module_client.go +++ b/x/gov/client/module_client.go @@ -5,6 +5,7 @@ import ( amino "github.com/tendermint/go-amino" "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/x/gov" govCli "github.com/cosmos/cosmos-sdk/x/gov/client/cli" ) @@ -22,7 +23,7 @@ func NewModuleClient(storeKey string, cdc *amino.Codec) ModuleClient { func (mc ModuleClient) GetQueryCmd() *cobra.Command { // Group gov queries under a subcommand govQueryCmd := &cobra.Command{ - Use: "gov", + Use: gov.ModuleName, Short: "Querying commands for the governance module", } @@ -44,7 +45,7 @@ func (mc ModuleClient) GetQueryCmd() *cobra.Command { // GetTxCmd returns the transaction commands for this module func (mc ModuleClient) GetTxCmd() *cobra.Command { govTxCmd := &cobra.Command{ - Use: "gov", + Use: gov.ModuleName, Short: "Governance transactions subcommands", } diff --git a/x/gov/client/rest/rest.go b/x/gov/client/rest/rest.go index abb3b115b179..d530054052f6 100644 --- a/x/gov/client/rest/rest.go +++ b/x/gov/client/rest/rest.go @@ -56,10 +56,10 @@ func RegisterRoutes(cliCtx context.CLIContext, r *mux.Router, cdc *codec.Codec) type postProposalReq struct { BaseReq rest.BaseReq `json:"base_req"` - Title string `json:"title"` // Title of the proposal - Description string `json:"description"` // Description of the proposal - ProposalType string `json:"proposal_type"` // Type of proposal. Initial set {PlainTextProposal, SoftwareUpgradeProposal} - Proposer sdk.AccAddress `json:"proposer"` // Address of the proposer + Title string `json:"title"` // Title of the proposal + Description string `json:"description"` // Description of the proposal + ProposalType string `json:"proposal_type"` // Type of proposal. Initial set {PlainTextProposal, SoftwareUpgradeProposal} + Proposer sdk.AccAddress `json:"proposer"` // Address of the proposer InitialDeposit sdk.Coins `json:"initial_deposit"` // Coins to add to the proposal's deposit } @@ -71,8 +71,8 @@ type depositReq struct { type voteReq struct { BaseReq rest.BaseReq `json:"base_req"` - Voter sdk.AccAddress `json:"voter"` // address of the voter - Option string `json:"option"` // option from OptionSet chosen by the voter + Voter sdk.AccAddress `json:"voter"` // address of the voter + Option string `json:"option"` // option from OptionSet chosen by the voter } func postProposalHandlerFn(cdc *codec.Codec, cliCtx context.CLIContext) http.HandlerFunc { diff --git a/x/gov/depositsvotes.go b/x/gov/depositsvotes.go index ce70150dd3e9..059d0a6bcb3e 100644 --- a/x/gov/depositsvotes.go +++ b/x/gov/depositsvotes.go @@ -55,6 +55,9 @@ func (d Deposit) String() string { type Deposits []Deposit func (d Deposits) String() string { + if len(d) == 0 { + return "[]" + } out := fmt.Sprintf("Deposits for Proposal %d:", d[0].ProposalID) for _, dep := range d { out += fmt.Sprintf("\n %s: %s", dep.Depositor, dep.Amount) diff --git a/x/gov/endblocker.go b/x/gov/endblocker.go new file mode 100644 index 000000000000..294a0e44f7f7 --- /dev/null +++ b/x/gov/endblocker.go @@ -0,0 +1,76 @@ +package gov + +import ( + "fmt" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/gov/tags" +) + +// Called every block, process inflation, update validator set +func EndBlocker(ctx sdk.Context, keeper Keeper) sdk.Tags { + logger := ctx.Logger().With("module", "x/gov") + resTags := sdk.NewTags() + + inactiveIterator := keeper.InactiveProposalQueueIterator(ctx, ctx.BlockHeader().Time) + defer inactiveIterator.Close() + for ; inactiveIterator.Valid(); inactiveIterator.Next() { + var proposalID uint64 + + keeper.cdc.MustUnmarshalBinaryLengthPrefixed(inactiveIterator.Value(), &proposalID) + inactiveProposal := keeper.GetProposal(ctx, proposalID) + + keeper.DeleteProposal(ctx, proposalID) + keeper.DeleteDeposits(ctx, proposalID) // delete any associated deposits (burned) + + resTags = resTags.AppendTag(tags.ProposalID, fmt.Sprintf("%d", proposalID)) + resTags = resTags.AppendTag(tags.ProposalResult, tags.ActionProposalDropped) + + logger.Info( + fmt.Sprintf("proposal %d (%s) didn't meet minimum deposit of %s (had only %s); deleted", + inactiveProposal.GetProposalID(), + inactiveProposal.GetTitle(), + keeper.GetDepositParams(ctx).MinDeposit, + inactiveProposal.GetTotalDeposit(), + ), + ) + } + + // fetch active proposals whose voting periods have ended (are passed the block time) + activeIterator := keeper.ActiveProposalQueueIterator(ctx, ctx.BlockHeader().Time) + defer activeIterator.Close() + for ; activeIterator.Valid(); activeIterator.Next() { + var proposalID uint64 + + keeper.cdc.MustUnmarshalBinaryLengthPrefixed(activeIterator.Value(), &proposalID) + activeProposal := keeper.GetProposal(ctx, proposalID) + passes, tallyResults := tally(ctx, keeper, activeProposal) + + var tagValue string + if passes { + keeper.RefundDeposits(ctx, activeProposal.GetProposalID()) + activeProposal.SetStatus(StatusPassed) + tagValue = tags.ActionProposalPassed + } else { + keeper.DeleteDeposits(ctx, activeProposal.GetProposalID()) + activeProposal.SetStatus(StatusRejected) + tagValue = tags.ActionProposalRejected + } + + activeProposal.SetFinalTallyResult(tallyResults) + keeper.SetProposal(ctx, activeProposal) + keeper.RemoveFromActiveProposalQueue(ctx, activeProposal.GetVotingEndTime(), activeProposal.GetProposalID()) + + logger.Info( + fmt.Sprintf( + "proposal %d (%s) tallied; passed: %v", + activeProposal.GetProposalID(), activeProposal.GetTitle(), passes, + ), + ) + + resTags = resTags.AppendTag(tags.ProposalID, fmt.Sprintf("%d", proposalID)) + resTags = resTags.AppendTag(tags.ProposalResult, tagValue) + } + + return resTags +} diff --git a/x/gov/errors.go b/x/gov/errors.go index 0bf904d339f3..e1acaf53b5b6 100644 --- a/x/gov/errors.go +++ b/x/gov/errors.go @@ -8,7 +8,7 @@ import ( ) const ( - DefaultCodespace sdk.CodespaceType = "GOV" + DefaultCodespace sdk.CodespaceType = ModuleName CodeUnknownProposal sdk.CodeType = 1 CodeInactiveProposal sdk.CodeType = 2 @@ -23,7 +23,6 @@ const ( CodeInvalidProposalStatus sdk.CodeType = 11 ) -//---------------------------------------- // Error constructors func ErrUnknownProposal(codespace sdk.CodespaceType, proposalID uint64) sdk.Error { diff --git a/x/gov/genesis.go b/x/gov/genesis.go index 36c699e19df5..8e4c31ac581c 100644 --- a/x/gov/genesis.go +++ b/x/gov/genesis.go @@ -1,6 +1,7 @@ package gov import ( + "bytes" "fmt" "time" @@ -8,6 +9,11 @@ import ( "github.com/cosmos/cosmos-sdk/x/staking" ) +const ( + // Default period for deposits & voting + DefaultPeriod time.Duration = 86400 * 2 * time.Second // 2 days +) + // GenesisState - all staking state that must be provided at genesis type GenesisState struct { StartingProposalID uint64 `json:"starting_proposal_id"` @@ -47,10 +53,10 @@ func DefaultGenesisState() GenesisState { StartingProposalID: 1, DepositParams: DepositParams{ MinDeposit: sdk.Coins{sdk.NewCoin(staking.DefaultBondDenom, minDepositTokens)}, - MaxDepositPeriod: time.Duration(172800) * time.Second, + MaxDepositPeriod: DefaultPeriod, }, VotingParams: VotingParams{ - VotingPeriod: time.Duration(172800) * time.Second, + VotingPeriod: DefaultPeriod, }, TallyParams: TallyParams{ Quorum: sdk.NewDecWithPrec(334, 3), @@ -63,48 +69,9 @@ func DefaultGenesisState() GenesisState { // Checks whether 2 GenesisState structs are equivalent. func (data GenesisState) Equal(data2 GenesisState) bool { - if data.StartingProposalID != data2.StartingProposalID || - !data.DepositParams.Equal(data2.DepositParams) || - data.VotingParams != data2.VotingParams || - data.TallyParams != data2.TallyParams { - return false - } - - if len(data.Deposits) != len(data2.Deposits) { - return false - } - for i := range data.Deposits { - deposit1 := data.Deposits[i] - deposit2 := data2.Deposits[i] - if deposit1.ProposalID != deposit2.ProposalID || - !deposit1.Deposit.Equals(deposit2.Deposit) { - return false - } - } - - if len(data.Votes) != len(data2.Votes) { - return false - } - for i := range data.Votes { - vote1 := data.Votes[i] - vote2 := data2.Votes[i] - if vote1.ProposalID != vote2.ProposalID || - !vote1.Vote.Equals(vote2.Vote) { - return false - } - } - - if len(data.Proposals) != len(data2.Proposals) { - return false - } - for i := range data.Proposals { - if !ProposalEqual(data.Proposals[i], data2.Proposals[i]) { - return false - } - } - - return true - + b1 := msgCdc.MustMarshalBinaryBare(data) + b2 := msgCdc.MustMarshalBinaryBare(data2) + return bytes.Equal(b1, b2) } // Returns if a GenesisState is empty or has data in it @@ -113,7 +80,7 @@ func (data GenesisState) IsEmpty() bool { return data.Equal(emptyGenState) } -// ValidateGenesis TODO https://github.com/cosmos/cosmos-sdk/issues/3007 +// ValidateGenesis func ValidateGenesis(data GenesisState) error { threshold := data.TallyParams.Threshold if threshold.IsNegative() || threshold.GT(sdk.OneDec()) { diff --git a/x/gov/handler.go b/x/gov/handler.go index aee576dc244b..133ea541b020 100644 --- a/x/gov/handler.go +++ b/x/gov/handler.go @@ -83,72 +83,3 @@ func handleMsgVote(ctx sdk.Context, keeper Keeper, msg MsgVote) sdk.Result { ), } } - -// Called every block, process inflation, update validator set -func EndBlocker(ctx sdk.Context, keeper Keeper) sdk.Tags { - logger := ctx.Logger().With("module", "x/gov") - resTags := sdk.NewTags() - - inactiveIterator := keeper.InactiveProposalQueueIterator(ctx, ctx.BlockHeader().Time) - for ; inactiveIterator.Valid(); inactiveIterator.Next() { - var proposalID uint64 - - keeper.cdc.MustUnmarshalBinaryLengthPrefixed(inactiveIterator.Value(), &proposalID) - inactiveProposal := keeper.GetProposal(ctx, proposalID) - - keeper.DeleteProposal(ctx, proposalID) - keeper.DeleteDeposits(ctx, proposalID) // delete any associated deposits (burned) - - resTags = resTags.AppendTag(tags.ProposalID, fmt.Sprintf("%d", proposalID)) - resTags = resTags.AppendTag(tags.ProposalResult, tags.ActionProposalDropped) - - logger.Info( - fmt.Sprintf("proposal %d (%s) didn't meet minimum deposit of %s (had only %s); deleted", - inactiveProposal.GetProposalID(), - inactiveProposal.GetTitle(), - keeper.GetDepositParams(ctx).MinDeposit, - inactiveProposal.GetTotalDeposit(), - ), - ) - } - - inactiveIterator.Close() - - activeIterator := keeper.ActiveProposalQueueIterator(ctx, ctx.BlockHeader().Time) - for ; activeIterator.Valid(); activeIterator.Next() { - var proposalID uint64 - - keeper.cdc.MustUnmarshalBinaryLengthPrefixed(activeIterator.Value(), &proposalID) - activeProposal := keeper.GetProposal(ctx, proposalID) - passes, tallyResults := tally(ctx, keeper, activeProposal) - - var tagValue string - if passes { - keeper.RefundDeposits(ctx, activeProposal.GetProposalID()) - activeProposal.SetStatus(StatusPassed) - tagValue = tags.ActionProposalPassed - } else { - keeper.DeleteDeposits(ctx, activeProposal.GetProposalID()) - activeProposal.SetStatus(StatusRejected) - tagValue = tags.ActionProposalRejected - } - - activeProposal.SetFinalTallyResult(tallyResults) - keeper.SetProposal(ctx, activeProposal) - keeper.RemoveFromActiveProposalQueue(ctx, activeProposal.GetVotingEndTime(), activeProposal.GetProposalID()) - - logger.Info( - fmt.Sprintf( - "proposal %d (%s) tallied; passed: %v", - activeProposal.GetProposalID(), activeProposal.GetTitle(), passes, - ), - ) - - resTags = resTags.AppendTag(tags.ProposalID, fmt.Sprintf("%d", proposalID)) - resTags = resTags.AppendTag(tags.ProposalResult, tagValue) - } - - activeIterator.Close() - - return resTags -} diff --git a/x/gov/keeper.go b/x/gov/keeper.go index 28b4832f3fb9..3a3cdcb814e7 100644 --- a/x/gov/keeper.go +++ b/x/gov/keeper.go @@ -12,17 +12,20 @@ import ( ) const ( + // ModuleKey is the name of the module + ModuleName = "gov" + // StoreKey is the store key string for gov - StoreKey = "gov" + StoreKey = ModuleName // RouterKey is the message route for gov - RouterKey = "gov" + RouterKey = ModuleName // QuerierRoute is the querier route for gov - QuerierRoute = "gov" + QuerierRoute = ModuleName // Parameter store default namestore - DefaultParamspace = "gov" + DefaultParamspace = ModuleName ) // Parameter store key @@ -31,6 +34,7 @@ var ( ParamStoreKeyVotingParams = []byte("votingparams") ParamStoreKeyTallyParams = []byte("tallyparams") + // TODO: Find another way to implement this without using accounts, or find a cleaner way to implement it using accounts. DepositedCoinsAccAddr = sdk.AccAddress(crypto.AddressHash([]byte("govDepositedCoins"))) BurnedDepositCoinsAccAddr = sdk.AccAddress(crypto.AddressHash([]byte("govBurnedDepositCoins"))) ) @@ -89,7 +93,6 @@ func NewKeeper(cdc *codec.Codec, key sdk.StoreKey, paramsKeeper params.Keeper, p } } -// ===================================================== // Proposals // Creates a NewProposal @@ -124,10 +127,8 @@ func (keeper Keeper) GetProposal(ctx sdk.Context, proposalID uint64) Proposal { if bz == nil { return nil } - var proposal Proposal keeper.cdc.MustUnmarshalBinaryLengthPrefixed(bz, &proposal) - return proposal } @@ -148,6 +149,10 @@ func (keeper Keeper) DeleteProposal(ctx sdk.Context, proposalID uint64) { } // Get Proposal from store by ProposalID +// voterAddr will filter proposals by whether or not that address has voted on them +// depositorAddr will filter proposals by whether or not that address has deposited to them +// status will filter proposals by status +// numLatest will fetch a specified number of the most recent proposals, or 0 for all proposals func (keeper Keeper) GetProposalsFiltered(ctx sdk.Context, voterAddr sdk.AccAddress, depositorAddr sdk.AccAddress, status ProposalStatus, numLatest uint64) []Proposal { maxProposalID, err := keeper.peekCurrentProposalID(ctx) @@ -192,6 +197,7 @@ func (keeper Keeper) GetProposalsFiltered(ctx sdk.Context, voterAddr sdk.AccAddr return matchingProposals } +// Set the initial proposal ID func (keeper Keeper) setInitialProposalID(ctx sdk.Context, proposalID uint64) sdk.Error { store := ctx.KVStore(keeper.storeKey) bz := store.Get(KeyNextProposalID) @@ -248,11 +254,9 @@ func (keeper Keeper) activateVotingPeriod(ctx sdk.Context, proposal Proposal) { keeper.InsertActiveProposalQueue(ctx, proposal.GetVotingEndTime(), proposal.GetProposalID()) } -// ===================================================== // Params // Returns the current DepositParams from the global param store -// nolint: errcheck func (keeper Keeper) GetDepositParams(ctx sdk.Context) DepositParams { var depositParams DepositParams keeper.paramSpace.Get(ctx, ParamStoreKeyDepositParams, &depositParams) @@ -260,7 +264,6 @@ func (keeper Keeper) GetDepositParams(ctx sdk.Context) DepositParams { } // Returns the current VotingParams from the global param store -// nolint: errcheck func (keeper Keeper) GetVotingParams(ctx sdk.Context) VotingParams { var votingParams VotingParams keeper.paramSpace.Get(ctx, ParamStoreKeyVotingParams, &votingParams) @@ -268,29 +271,24 @@ func (keeper Keeper) GetVotingParams(ctx sdk.Context) VotingParams { } // Returns the current TallyParam from the global param store -// nolint: errcheck func (keeper Keeper) GetTallyParams(ctx sdk.Context) TallyParams { var tallyParams TallyParams keeper.paramSpace.Get(ctx, ParamStoreKeyTallyParams, &tallyParams) return tallyParams } -// nolint: errcheck func (keeper Keeper) setDepositParams(ctx sdk.Context, depositParams DepositParams) { keeper.paramSpace.Set(ctx, ParamStoreKeyDepositParams, &depositParams) } -// nolint: errcheck func (keeper Keeper) setVotingParams(ctx sdk.Context, votingParams VotingParams) { keeper.paramSpace.Set(ctx, ParamStoreKeyVotingParams, &votingParams) } -// nolint: errcheck func (keeper Keeper) setTallyParams(ctx sdk.Context, tallyParams TallyParams) { keeper.paramSpace.Set(ctx, ParamStoreKeyTallyParams, &tallyParams) } -// ===================================================== // Votes // Adds a vote on a specific proposal @@ -346,7 +344,6 @@ func (keeper Keeper) deleteVote(ctx sdk.Context, proposalID uint64, voterAddr sd store.Delete(KeyVote(proposalID, voterAddr)) } -// ===================================================== // Deposits // Gets the deposit of a specific depositor on a specific proposal @@ -382,17 +379,17 @@ func (keeper Keeper) AddDeposit(ctx sdk.Context, proposalID uint64, depositorAdd } // Send coins from depositor's account to DepositedCoinsAccAddr account + // TODO: Don't use an account for this purpose; it's clumsy and prone to misuse. _, err := keeper.ck.SendCoins(ctx, depositorAddr, DepositedCoinsAccAddr, depositAmount) if err != nil { return err, false } - // Update Proposal + // Update proposal proposal.SetTotalDeposit(proposal.GetTotalDeposit().Plus(depositAmount)) keeper.SetProposal(ctx, proposal) - // Check if deposit tipped proposal into voting period - // Active voting period if so + // Check if deposit has provided sufficient total funds to transition the proposal into the voting period activatedVotingPeriod := false if proposal.GetStatus() == StatusDepositPeriod && proposal.GetTotalDeposit().IsAllGTE(keeper.GetDepositParams(ctx).MinDeposit) { keeper.activateVotingPeriod(ctx, proposal) @@ -412,13 +409,13 @@ func (keeper Keeper) AddDeposit(ctx sdk.Context, proposalID uint64, depositorAdd return nil, activatedVotingPeriod } -// Gets all the deposits on a specific proposal +// Gets all the deposits on a specific proposal as an sdk.Iterator func (keeper Keeper) GetDeposits(ctx sdk.Context, proposalID uint64) sdk.Iterator { store := ctx.KVStore(keeper.storeKey) return sdk.KVStorePrefixIterator(store, KeyDepositsSubspace(proposalID)) } -// Returns and deletes all the deposits on a specific proposal +// Refunds and deletes all the deposits on a specific proposal func (keeper Keeper) RefundDeposits(ctx sdk.Context, proposalID uint64) { store := ctx.KVStore(keeper.storeKey) depositsIterator := keeper.GetDeposits(ctx, proposalID) @@ -445,6 +442,7 @@ func (keeper Keeper) DeleteDeposits(ctx sdk.Context, proposalID uint64) { deposit := &Deposit{} keeper.cdc.MustUnmarshalBinaryLengthPrefixed(depositsIterator.Value(), deposit) + // TODO: Find a way to do this without using accounts. _, err := keeper.ck.SendCoins(ctx, DepositedCoinsAccAddr, BurnedDepositCoinsAccAddr, deposit.Amount) if err != nil { panic("should not happen") @@ -454,7 +452,6 @@ func (keeper Keeper) DeleteDeposits(ctx sdk.Context, proposalID uint64) { } } -// ===================================================== // ProposalQueues // Returns an iterator for all the proposals in the Active Queue that expire by endTime diff --git a/x/gov/keeper_keys.go b/x/gov/keeper_keys.go index b1a5d7761e26..690379566b1d 100644 --- a/x/gov/keeper_keys.go +++ b/x/gov/keeper_keys.go @@ -8,8 +8,6 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" ) -// TODO remove some of these prefixes once have working multistore - // Key for getting a the next available proposalID from the store var ( KeyDelimiter = []byte(":") diff --git a/x/gov/msgs.go b/x/gov/msgs.go index 78ad6f5ad7bb..a6e26a97853f 100644 --- a/x/gov/msgs.go +++ b/x/gov/msgs.go @@ -18,7 +18,6 @@ const ( var _, _, _ sdk.Msg = MsgSubmitProposal{}, MsgDeposit{}, MsgVote{} -//----------------------------------------------------------- // MsgSubmitProposal type MsgSubmitProposal struct { Title string `json:"title"` // Title of the proposal @@ -28,7 +27,7 @@ type MsgSubmitProposal struct { InitialDeposit sdk.Coins `json:"initial_deposit"` // Initial deposit paid by sender. Must be strictly positive. } -func NewMsgSubmitProposal(title string, description string, proposalType ProposalKind, proposer sdk.AccAddress, initialDeposit sdk.Coins) MsgSubmitProposal { +func NewMsgSubmitProposal(title, description string, proposalType ProposalKind, proposer sdk.AccAddress, initialDeposit sdk.Coins) MsgSubmitProposal { return MsgSubmitProposal{ Title: title, Description: description, @@ -75,11 +74,6 @@ func (msg MsgSubmitProposal) String() string { return fmt.Sprintf("MsgSubmitProposal{%s, %s, %s, %v}", msg.Title, msg.Description, msg.ProposalType, msg.InitialDeposit) } -// Implements Msg. -func (msg MsgSubmitProposal) Get(key interface{}) (value interface{}) { - return nil -} - // Implements Msg. func (msg MsgSubmitProposal) GetSignBytes() []byte { bz := msgCdc.MustMarshalJSON(msg) @@ -91,7 +85,6 @@ func (msg MsgSubmitProposal) GetSigners() []sdk.AccAddress { return []sdk.AccAddress{msg.Proposer} } -//----------------------------------------------------------- // MsgDeposit type MsgDeposit struct { ProposalID uint64 `json:"proposal_id"` // ID of the proposal @@ -114,7 +107,7 @@ func (msg MsgDeposit) Type() string { return TypeMsgDeposit } // Implements Msg. func (msg MsgDeposit) ValidateBasic() sdk.Error { - if len(msg.Depositor) == 0 { + if msg.Depositor.Empty() { return sdk.ErrInvalidAddress(msg.Depositor.String()) } if !msg.Amount.IsValid() { @@ -133,11 +126,6 @@ func (msg MsgDeposit) String() string { return fmt.Sprintf("MsgDeposit{%s=>%v: %v}", msg.Depositor, msg.ProposalID, msg.Amount) } -// Implements Msg. -func (msg MsgDeposit) Get(key interface{}) (value interface{}) { - return nil -} - // Implements Msg. func (msg MsgDeposit) GetSignBytes() []byte { bz := msgCdc.MustMarshalJSON(msg) @@ -149,7 +137,6 @@ func (msg MsgDeposit) GetSigners() []sdk.AccAddress { return []sdk.AccAddress{msg.Depositor} } -//----------------------------------------------------------- // MsgVote type MsgVote struct { ProposalID uint64 `json:"proposal_id"` // ID of the proposal @@ -188,11 +175,6 @@ func (msg MsgVote) String() string { return fmt.Sprintf("MsgVote{%v - %s}", msg.ProposalID, msg.Option) } -// Implements Msg. -func (msg MsgVote) Get(key interface{}) (value interface{}) { - return nil -} - // Implements Msg. func (msg MsgVote) GetSignBytes() []byte { bz := msgCdc.MustMarshalJSON(msg) diff --git a/x/gov/params.go b/x/gov/params.go index 15692041a96b..54601c7a62d3 100644 --- a/x/gov/params.go +++ b/x/gov/params.go @@ -7,7 +7,7 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" ) -// Param around Deposits for governance +// Param around deposits for governance type DepositParams struct { MinDeposit sdk.Coins `json:"min_deposit"` // Minimum deposit for a proposal to enter voting period. MaxDepositPeriod time.Duration `json:"max_deposit_period"` // Maximum period for Atom holders to deposit on a proposal. Initial value: 2 months diff --git a/x/gov/proposals.go b/x/gov/proposals.go index ad9bb852d2b4..49095fb1a3b6 100644 --- a/x/gov/proposals.go +++ b/x/gov/proposals.go @@ -9,7 +9,6 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" ) -//----------------------------------------------------------- // Proposal interface type Proposal interface { GetProposalID() uint64 @@ -79,7 +78,6 @@ func ProposalEqual(proposalA Proposal, proposalB Proposal) bool { return false } -//----------------------------------------------------------- // Text Proposals type TextProposal struct { ProposalID uint64 `json:"proposal_id"` // ID of the proposal @@ -147,11 +145,9 @@ func (tp TextProposal) String() string { tp.TotalDeposit, tp.VotingStartTime, tp.VotingEndTime) } -//----------------------------------------------------------- // ProposalQueue type ProposalQueue []uint64 -//----------------------------------------------------------- // ProposalKind // Type that represents Proposal Type as a byte @@ -165,7 +161,7 @@ const ( ProposalTypeSoftwareUpgrade ProposalKind = 0x03 ) -// String to proposalType byte. Returns ff if invalid. +// String to proposalType byte. Returns 0xff if invalid. func ProposalTypeFromString(str string) (ProposalKind, error) { switch str { case "Text": @@ -247,7 +243,6 @@ func (pt ProposalKind) Format(s fmt.State, verb rune) { } } -//----------------------------------------------------------- // ProposalStatus // Type that represents Proposal Status as a byte @@ -351,16 +346,15 @@ func (status ProposalStatus) Format(s fmt.State, verb rune) { } } -//----------------------------------------------------------- // Tally Results type TallyResult struct { - Yes sdk.Dec `json:"yes"` - Abstain sdk.Dec `json:"abstain"` - No sdk.Dec `json:"no"` - NoWithVeto sdk.Dec `json:"no_with_veto"` + Yes sdk.Int `json:"yes"` + Abstain sdk.Int `json:"abstain"` + No sdk.Int `json:"no"` + NoWithVeto sdk.Int `json:"no_with_veto"` } -func NewTallyResult(yes, abstain, no, noWithVeto sdk.Dec) TallyResult { +func NewTallyResult(yes, abstain, no, noWithVeto sdk.Int) TallyResult { return TallyResult{ Yes: yes, Abstain: abstain, @@ -371,20 +365,20 @@ func NewTallyResult(yes, abstain, no, noWithVeto sdk.Dec) TallyResult { func NewTallyResultFromMap(results map[VoteOption]sdk.Dec) TallyResult { return TallyResult{ - Yes: results[OptionYes], - Abstain: results[OptionAbstain], - No: results[OptionNo], - NoWithVeto: results[OptionNoWithVeto], + Yes: results[OptionYes].TruncateInt(), + Abstain: results[OptionAbstain].TruncateInt(), + No: results[OptionNo].TruncateInt(), + NoWithVeto: results[OptionNoWithVeto].TruncateInt(), } } // checks if two proposals are equal func EmptyTallyResult() TallyResult { return TallyResult{ - Yes: sdk.ZeroDec(), - Abstain: sdk.ZeroDec(), - No: sdk.ZeroDec(), - NoWithVeto: sdk.ZeroDec(), + Yes: sdk.ZeroInt(), + Abstain: sdk.ZeroInt(), + No: sdk.ZeroInt(), + NoWithVeto: sdk.ZeroInt(), } } diff --git a/x/gov/tally.go b/x/gov/tally.go index d6cac26f50b4..cc7df2412ffa 100644 --- a/x/gov/tally.go +++ b/x/gov/tally.go @@ -6,25 +6,26 @@ import ( // validatorGovInfo used for tallying type validatorGovInfo struct { - Address sdk.ValAddress // address of the validator operator - BondedTokens sdk.Int // Power of a Validator - DelegatorShares sdk.Dec // Total outstanding delegator shares - Minus sdk.Dec // Minus of validator, used to compute validator's voting power - Vote VoteOption // Vote of the validator + Address sdk.ValAddress // address of the validator operator + BondedTokens sdk.Int // Power of a Validator + DelegatorShares sdk.Dec // Total outstanding delegator shares + DelegatorDeductions sdk.Dec // Delegator deductions from validator's delegators voting independently + Vote VoteOption // Vote of the validator } func newValidatorGovInfo(address sdk.ValAddress, bondedTokens sdk.Int, delegatorShares, - minus sdk.Dec, vote VoteOption) validatorGovInfo { + delegatorDeductions sdk.Dec, vote VoteOption) validatorGovInfo { return validatorGovInfo{ - Address: address, - BondedTokens: bondedTokens, - DelegatorShares: delegatorShares, - Minus: minus, - Vote: vote, + Address: address, + BondedTokens: bondedTokens, + DelegatorShares: delegatorShares, + DelegatorDeductions: delegatorDeductions, + Vote: vote, } } +// TODO: Break into several smaller functions for clarity func tally(ctx sdk.Context, keeper Keeper, proposal Proposal) (passes bool, tallyResults TallyResult) { results := make(map[VoteOption]sdk.Dec) results[OptionYes] = sdk.ZeroDec() @@ -35,6 +36,7 @@ func tally(ctx sdk.Context, keeper Keeper, proposal Proposal) (passes bool, tall totalVotingPower := sdk.ZeroDec() currValidators := make(map[string]validatorGovInfo) + // fetch all the bonded validators, insert them into currValidators keeper.vs.IterateBondedValidatorsByPower(ctx, func(index int64, validator sdk.Validator) (stop bool) { currValidators[validator.GetOperator().String()] = newValidatorGovInfo( validator.GetOperator(), @@ -60,12 +62,12 @@ func tally(ctx sdk.Context, keeper Keeper, proposal Proposal) (passes bool, tall val.Vote = vote.Option currValidators[valAddrStr] = val } else { - + // iterate over all delegations from voter, deduct from any delegated-to validators keeper.ds.IterateDelegations(ctx, vote.Voter, func(index int64, delegation sdk.Delegation) (stop bool) { valAddrStr := delegation.GetValidatorAddr().String() if val, ok := currValidators[valAddrStr]; ok { - val.Minus = val.Minus.Add(delegation.GetShares()) + val.DelegatorDeductions = val.DelegatorDeductions.Add(delegation.GetShares()) currValidators[valAddrStr] = val delegatorShare := delegation.GetShares().Quo(val.DelegatorShares) @@ -88,9 +90,9 @@ func tally(ctx sdk.Context, keeper Keeper, proposal Proposal) (passes bool, tall continue } - sharesAfterMinus := val.DelegatorShares.Sub(val.Minus) - percentAfterMinus := sharesAfterMinus.Quo(val.DelegatorShares) - votingPower := percentAfterMinus.MulInt(val.BondedTokens) + sharesAfterDeductions := val.DelegatorShares.Sub(val.DelegatorDeductions) + fractionAfterDeductions := sharesAfterDeductions.Quo(val.DelegatorShares) + votingPower := fractionAfterDeductions.MulInt(val.BondedTokens) results[val.Vote] = results[val.Vote].Add(votingPower) totalVotingPower = totalVotingPower.Add(votingPower) @@ -99,6 +101,7 @@ func tally(ctx sdk.Context, keeper Keeper, proposal Proposal) (passes bool, tall tallyParams := keeper.GetTallyParams(ctx) tallyResults = NewTallyResultFromMap(results) + // TODO: Upgrade the spec to cover all of these cases & remove pseudocode. // If there is no staked coins, the proposal fails if keeper.vs.TotalPower(ctx).IsZero() { return false, tallyResults