parent
d184121ea0
commit
d1614ebb7e
@ -20,11 +20,17 @@ BREAKING CHANGES
|
||||
FEATURES
|
||||
|
||||
* Gaia REST API (`gaiacli advanced rest-server`)
|
||||
* [gov] [\#2479](https://github.com/cosmos/cosmos-sdk/issues/2479) Added governance parameter
|
||||
query REST endpoints.
|
||||
|
||||
* Gaia CLI (`gaiacli`)
|
||||
* [stake][cli] [\#2027] Add CLI query command for getting all delegations to a specific validator.
|
||||
* [gov][cli] [\#2479](https://github.com/cosmos/cosmos-sdk/issues/2479) Added governance
|
||||
parameter query commands.
|
||||
* [stake][cli] [\#2027] Add CLI query command for getting all delegations to a specific validator.
|
||||
|
||||
* Gaia
|
||||
* [x/gov] [#2479](https://github.com/cosmos/cosmos-sdk/issues/2479) Implemented querier
|
||||
for getting governance parameters.
|
||||
|
||||
* SDK
|
||||
* [simulator] \#2682 MsgEditValidator now looks at the validator's max rate, thus it now succeeds a significant portion of the time
|
||||
|
||||
@ -735,26 +735,33 @@ func TestProposalsQuery(t *testing.T) {
|
||||
cleanup, _, _, port := InitializeTestLCD(t, 1, []sdk.AccAddress{addrs[0], addrs[1]})
|
||||
defer cleanup()
|
||||
|
||||
depositParam := getDepositParam(t, port)
|
||||
halfMinDeposit := depositParam.MinDeposit.AmountOf(stakeTypes.DefaultBondDenom).Int64() / 2
|
||||
getVotingParam(t, port)
|
||||
getTallyingParam(t, port)
|
||||
|
||||
// Addr1 proposes (and deposits) proposals #1 and #2
|
||||
resultTx := doSubmitProposal(t, port, seeds[0], names[0], passwords[0], addrs[0], 5)
|
||||
resultTx := doSubmitProposal(t, port, seeds[0], names[0], passwords[0], addrs[0], halfMinDeposit)
|
||||
var proposalID1 uint64
|
||||
cdc.MustUnmarshalBinaryLengthPrefixed(resultTx.DeliverTx.GetData(), &proposalID1)
|
||||
tests.WaitForHeight(resultTx.Height+1, port)
|
||||
resultTx = doSubmitProposal(t, port, seeds[0], names[0], passwords[0], addrs[0], 5)
|
||||
|
||||
resultTx = doSubmitProposal(t, port, seeds[0], names[0], passwords[0], addrs[0], halfMinDeposit)
|
||||
var proposalID2 uint64
|
||||
cdc.MustUnmarshalBinaryLengthPrefixed(resultTx.DeliverTx.GetData(), &proposalID2)
|
||||
tests.WaitForHeight(resultTx.Height+1, port)
|
||||
|
||||
// Addr2 proposes (and deposits) proposals #3
|
||||
resultTx = doSubmitProposal(t, port, seeds[1], names[1], passwords[1], addrs[1], 5)
|
||||
resultTx = doSubmitProposal(t, port, seeds[1], names[1], passwords[1], addrs[1], halfMinDeposit)
|
||||
var proposalID3 uint64
|
||||
cdc.MustUnmarshalBinaryLengthPrefixed(resultTx.DeliverTx.GetData(), &proposalID3)
|
||||
tests.WaitForHeight(resultTx.Height+1, port)
|
||||
|
||||
// Addr2 deposits on proposals #2 & #3
|
||||
resultTx = doDeposit(t, port, seeds[1], names[1], passwords[1], addrs[1], proposalID2, 5)
|
||||
resultTx = doDeposit(t, port, seeds[1], names[1], passwords[1], addrs[1], proposalID2, halfMinDeposit)
|
||||
tests.WaitForHeight(resultTx.Height+1, port)
|
||||
resultTx = doDeposit(t, port, seeds[1], names[1], passwords[1], addrs[1], proposalID3, 5)
|
||||
|
||||
resultTx = doDeposit(t, port, seeds[1], names[1], passwords[1], addrs[1], proposalID3, halfMinDeposit)
|
||||
tests.WaitForHeight(resultTx.Height+1, port)
|
||||
|
||||
// check deposits match proposal and individual deposits
|
||||
@ -1246,6 +1253,36 @@ func getValidatorRedelegations(t *testing.T, port string, validatorAddr sdk.ValA
|
||||
|
||||
// ============= Governance Module ================
|
||||
|
||||
func getDepositParam(t *testing.T, port string) gov.DepositParams {
|
||||
res, body := Request(t, port, "GET", "/gov/parameters/deposit", nil)
|
||||
require.Equal(t, http.StatusOK, res.StatusCode, body)
|
||||
|
||||
var depositParams gov.DepositParams
|
||||
err := cdc.UnmarshalJSON([]byte(body), &depositParams)
|
||||
require.Nil(t, err)
|
||||
return depositParams
|
||||
}
|
||||
|
||||
func getVotingParam(t *testing.T, port string) gov.VotingParams {
|
||||
res, body := Request(t, port, "GET", "/gov/parameters/voting", nil)
|
||||
require.Equal(t, http.StatusOK, res.StatusCode, body)
|
||||
|
||||
var votingParams gov.VotingParams
|
||||
err := cdc.UnmarshalJSON([]byte(body), &votingParams)
|
||||
require.Nil(t, err)
|
||||
return votingParams
|
||||
}
|
||||
|
||||
func getTallyingParam(t *testing.T, port string) gov.TallyParams {
|
||||
res, body := Request(t, port, "GET", "/gov/parameters/tallying", nil)
|
||||
require.Equal(t, http.StatusOK, res.StatusCode, body)
|
||||
|
||||
var tallyParams gov.TallyParams
|
||||
err := cdc.UnmarshalJSON([]byte(body), &tallyParams)
|
||||
require.Nil(t, err)
|
||||
return tallyParams
|
||||
}
|
||||
|
||||
func getProposal(t *testing.T, port string, proposalID uint64) gov.Proposal {
|
||||
res, body := Request(t, port, "GET", fmt.Sprintf("/gov/proposals/%d", proposalID), nil)
|
||||
require.Equal(t, http.StatusOK, res.StatusCode, body)
|
||||
|
||||
@ -1451,7 +1451,7 @@ paths:
|
||||
/gov/proposals/{proposalId}/votes/{voter}:
|
||||
get:
|
||||
summary: Query vote
|
||||
description: Query vote information by proposalId and voter address
|
||||
description: Query vote information by proposal Id and voter address
|
||||
produces:
|
||||
- application/json
|
||||
tags:
|
||||
@ -1478,6 +1478,83 @@ paths:
|
||||
description: Found no vote
|
||||
500:
|
||||
description: Internal Server Error
|
||||
/gov/parameters/deposit:
|
||||
get:
|
||||
summary: Query governance deposit parameters
|
||||
description: Query governance deposit parameters. The max_deposit_period units are in nanoseconds.
|
||||
produces:
|
||||
- application/json
|
||||
tags:
|
||||
- ICS22
|
||||
responses:
|
||||
200:
|
||||
description: OK
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
min_deposit:
|
||||
type: array
|
||||
items:
|
||||
$ref: "#/definitions/Coin"
|
||||
max_deposit_period:
|
||||
type: string
|
||||
example: "86400000000000"
|
||||
400:
|
||||
description: <other_path> is not a valid query request path
|
||||
404:
|
||||
description: Found no deposit parameters
|
||||
500:
|
||||
description: Internal Server Error
|
||||
/gov/parameters/tallying:
|
||||
get:
|
||||
summary: Query governance tally parameters
|
||||
description: Query governance tally parameters
|
||||
produces:
|
||||
- application/json
|
||||
tags:
|
||||
- ICS22
|
||||
responses:
|
||||
200:
|
||||
description: OK
|
||||
schema:
|
||||
properties:
|
||||
threshold:
|
||||
type: string
|
||||
example: "0.5000000000"
|
||||
veto:
|
||||
type: string
|
||||
example: "0.3340000000"
|
||||
governance_penalty:
|
||||
type: string
|
||||
example: "0.0100000000"
|
||||
400:
|
||||
description: <other_path> is not a valid query request path
|
||||
404:
|
||||
description: Found no tally parameters
|
||||
500:
|
||||
description: Internal Server Error
|
||||
/gov/parameters/voting:
|
||||
get:
|
||||
summary: Query governance voting parameters
|
||||
description: Query governance voting parameters. The voting_period units are in nanoseconds.
|
||||
produces:
|
||||
- application/json
|
||||
tags:
|
||||
- ICS22
|
||||
responses:
|
||||
200:
|
||||
description: OK
|
||||
schema:
|
||||
properties:
|
||||
voting_period:
|
||||
type: string
|
||||
example: "86400000000000"
|
||||
400:
|
||||
description: <other_path> is not a valid query request path
|
||||
404:
|
||||
description: Found no voting parameters
|
||||
500:
|
||||
description: Internal Server Error
|
||||
|
||||
definitions:
|
||||
CheckTxResult:
|
||||
|
||||
@ -4,9 +4,7 @@ import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/cosmos/cosmos-sdk/x/stake"
|
||||
stakeTypes "github.com/cosmos/cosmos-sdk/x/stake/types"
|
||||
"github.com/tendermint/tendermint/crypto/secp256k1"
|
||||
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"net/http"
|
||||
@ -16,6 +14,9 @@ import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
stakeTypes "github.com/cosmos/cosmos-sdk/x/stake/types"
|
||||
"github.com/tendermint/tendermint/crypto/secp256k1"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/client"
|
||||
"github.com/cosmos/cosmos-sdk/client/keys"
|
||||
gapp "github.com/cosmos/cosmos-sdk/cmd/gaia/app"
|
||||
@ -25,6 +26,7 @@ import (
|
||||
"github.com/cosmos/cosmos-sdk/tests"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/cosmos/cosmos-sdk/x/auth"
|
||||
"github.com/cosmos/cosmos-sdk/x/stake"
|
||||
|
||||
"github.com/spf13/viper"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
@ -317,6 +317,10 @@ func TestGaiaCLISubmitProposal(t *testing.T) {
|
||||
tests.WaitForTMStart(port)
|
||||
tests.WaitForNextNBlocksTM(2, port)
|
||||
|
||||
executeGetDepositParam(t, fmt.Sprintf("gaiacli query gov param deposit %v", flags))
|
||||
executeGetVotingParam(t, fmt.Sprintf("gaiacli query gov param voting %v", flags))
|
||||
executeGetTallyingParam(t, fmt.Sprintf("gaiacli query gov param tallying %v", flags))
|
||||
|
||||
fooAddr, _ := executeGetAddrPK(t, fmt.Sprintf("gaiacli keys show foo --output=json --home=%s", gaiacliHome))
|
||||
|
||||
fooAcc := executeGetAccount(t, fmt.Sprintf("gaiacli query account %s %v", fooAddr, flags))
|
||||
@ -784,6 +788,33 @@ func executeGetParams(t *testing.T, cmdStr string) stake.Params {
|
||||
//___________________________________________________________________________________
|
||||
// gov
|
||||
|
||||
func executeGetDepositParam(t *testing.T, cmdStr string) gov.DepositParams {
|
||||
out, _ := tests.ExecuteT(t, cmdStr, "")
|
||||
var depositParam gov.DepositParams
|
||||
cdc := app.MakeCodec()
|
||||
err := cdc.UnmarshalJSON([]byte(out), &depositParam)
|
||||
require.NoError(t, err, "out %v\n, err %v", out, err)
|
||||
return depositParam
|
||||
}
|
||||
|
||||
func executeGetVotingParam(t *testing.T, cmdStr string) gov.VotingParams {
|
||||
out, _ := tests.ExecuteT(t, cmdStr, "")
|
||||
var votingParam gov.VotingParams
|
||||
cdc := app.MakeCodec()
|
||||
err := cdc.UnmarshalJSON([]byte(out), &votingParam)
|
||||
require.NoError(t, err, "out %v\n, err %v", out, err)
|
||||
return votingParam
|
||||
}
|
||||
|
||||
func executeGetTallyingParam(t *testing.T, cmdStr string) gov.TallyParams {
|
||||
out, _ := tests.ExecuteT(t, cmdStr, "")
|
||||
var tallyingParam gov.TallyParams
|
||||
cdc := app.MakeCodec()
|
||||
err := cdc.UnmarshalJSON([]byte(out), &tallyingParam)
|
||||
require.NoError(t, err, "out %v\n, err %v", out, err)
|
||||
return tallyingParam
|
||||
}
|
||||
|
||||
func executeGetProposal(t *testing.T, cmdStr string) gov.Proposal {
|
||||
out, _ := tests.ExecuteT(t, cmdStr, "")
|
||||
var proposal gov.Proposal
|
||||
|
||||
@ -53,6 +53,7 @@ func queryCmd(cdc *amino.Codec) *cobra.Command {
|
||||
govcmd.GetCmdQueryProposals(storeGov, cdc),
|
||||
govcmd.GetCmdQueryVote(storeGov, cdc),
|
||||
govcmd.GetCmdQueryVotes(storeGov, cdc),
|
||||
govcmd.GetCmdQueryParams(storeGov, cdc),
|
||||
govcmd.GetCmdQueryDeposit(storeGov, cdc),
|
||||
govcmd.GetCmdQueryDeposits(storeGov, cdc))...)
|
||||
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
|
||||
## Abstract
|
||||
|
||||
This paper specifies the Governance module of the Cosmos-SDK, which was first described in the [Cosmos Whitepaper](https://cosmos.network/about/whitepaper) in June 2016.
|
||||
This paper specifies the Governance module of the Cosmos-SDK, which was first described in the [Cosmos Whitepaper](https://cosmos.network/about/whitepaper) in June 2016.
|
||||
|
||||
The module enables Cosmos-SDK based blockchain to support an on-chain governance system. In this system, holders of the native staking token of the chain can vote on proposals on a 1 token 1 vote basis. Next is a list of features the module currently supports:
|
||||
|
||||
@ -24,7 +24,7 @@ The following specification uses *Atom* as the native staking token. The module
|
||||
1. **[Design overview](overview.md)**
|
||||
2. **Implementation**
|
||||
1. **[State](state.md)**
|
||||
1. Procedures
|
||||
1. Parameters
|
||||
2. Proposals
|
||||
3. Proposal Processing Queue
|
||||
2. **[Transactions](transactions.md)**
|
||||
|
||||
@ -2,36 +2,35 @@
|
||||
|
||||
## State
|
||||
|
||||
### Procedures and base types
|
||||
|
||||
`Procedures` define the rule according to which votes are run. There can only
|
||||
be one active procedure at any given time. If governance wants to change a
|
||||
procedure, either to modify a value or add/remove a parameter, a new procedure
|
||||
has to be created and the previous one rendered inactive.
|
||||
### Parameters and base types
|
||||
|
||||
`Parameters` define the rules according to which votes are run. There can only
|
||||
be one active parameter set at any given time. If governance wants to change a
|
||||
parameter set, either to modify a value or add/remove a parameter field, a new
|
||||
parameter set has to be created and the previous one rendered inactive.
|
||||
|
||||
```go
|
||||
type DepositProcedure struct {
|
||||
MinDeposit sdk.Coins // Minimum deposit for a proposal to enter voting period.
|
||||
MaxDepositPeriod time.Time // Maximum period for Atom holders to deposit on a proposal. Initial value: 2 months
|
||||
type DepositParams struct {
|
||||
MinDeposit sdk.Coins // Minimum deposit for a proposal to enter voting period.
|
||||
MaxDepositPeriod time.Time // Maximum period for Atom holders to deposit on a proposal. Initial value: 2 months
|
||||
}
|
||||
```
|
||||
|
||||
```go
|
||||
type VotingProcedure struct {
|
||||
VotingPeriod time.Time // Length of the voting period. Initial value: 2 weeks
|
||||
type VotingParams struct {
|
||||
VotingPeriod time.Time // Length of the voting period. Initial value: 2 weeks
|
||||
}
|
||||
```
|
||||
|
||||
```go
|
||||
type TallyingProcedure struct {
|
||||
Threshold sdk.Dec // Minimum propotion of Yes votes for proposal to pass. Initial value: 0.5
|
||||
Veto sdk.Dec // Minimum proportion of Veto votes to Total votes ratio for proposal to be vetoed. Initial value: 1/3
|
||||
GovernancePenalty sdk.Dec // Penalty if validator does not vote
|
||||
type TallyParams struct {
|
||||
Threshold sdk.Dec // Minimum proportion of Yes votes for proposal to pass. Initial value: 0.5
|
||||
Veto sdk.Dec // Minimum proportion of Veto votes to Total votes ratio for proposal to be vetoed. Initial value: 1/3
|
||||
GovernancePenalty sdk.Dec // Penalty if validator does not vote
|
||||
}
|
||||
```
|
||||
|
||||
Procedures are stored in a global `GlobalParams` KVStore.
|
||||
Parameters are stored in a global `GlobalParams` KVStore.
|
||||
|
||||
Additionally, we introduce some basic types:
|
||||
|
||||
@ -61,7 +60,7 @@ const (
|
||||
ProposalStatusActive = 0x2 // MinDeposit is reachhed, participants can vote
|
||||
ProposalStatusAccepted = 0x3 // Proposal has been accepted
|
||||
ProposalStatusRejected = 0x4 // Proposal has been rejected
|
||||
ProposalStatusClosed. = 0x5 // Proposal never reached MinDeposit
|
||||
ProposalStatusClosed = 0x5 // Proposal never reached MinDeposit
|
||||
)
|
||||
```
|
||||
|
||||
@ -76,7 +75,7 @@ const (
|
||||
|
||||
### ValidatorGovInfo
|
||||
|
||||
This type is used in a temp map when tallying
|
||||
This type is used in a temp map when tallying
|
||||
|
||||
```go
|
||||
type ValidatorGovInfo struct {
|
||||
@ -87,7 +86,7 @@ This type is used in a temp map when tallying
|
||||
|
||||
### Proposals
|
||||
|
||||
`Proposals` are an item to be voted on.
|
||||
`Proposals` are an item to be voted on.
|
||||
|
||||
```go
|
||||
type Proposal struct {
|
||||
@ -99,7 +98,7 @@ type Proposal struct {
|
||||
SubmitTime time.Time // Time of the block where TxGovSubmitProposal was included
|
||||
DepositEndTime time.Time // Time that the DepositPeriod of a proposal would expire
|
||||
Submitter sdk.AccAddress // Address of the submitter
|
||||
|
||||
|
||||
VotingStartTime time.Time // Time of the block where MinDeposit was reached. time.Time{} if MinDeposit is not reached
|
||||
VotingEndTime time.Time // Time of the block that the VotingPeriod for a proposal will end.
|
||||
CurrentStatus ProposalStatus // Current status of the proposal
|
||||
@ -135,7 +134,7 @@ For pseudocode purposes, here are the two function we will use to read or write
|
||||
### Proposal Processing Queue
|
||||
|
||||
**Store:**
|
||||
* `ProposalProcessingQueue`: A queue `queue[proposalID]` containing all the
|
||||
* `ProposalProcessingQueue`: A queue `queue[proposalID]` containing all the
|
||||
`ProposalIDs` of proposals that reached `MinDeposit`. Each `EndBlock`, all the proposals
|
||||
that have reached the end of their voting period are processed.
|
||||
To process a finished proposal, the application tallies the votes, compute the votes of
|
||||
@ -145,8 +144,8 @@ For pseudocode purposes, here are the two function we will use to read or write
|
||||
And the pseudocode for the `ProposalProcessingQueue`:
|
||||
|
||||
```go
|
||||
in EndBlock do
|
||||
|
||||
in EndBlock do
|
||||
|
||||
for finishedProposalID in GetAllFinishedProposalIDs(block.Time)
|
||||
proposal = load(Governance, <proposalID|'proposal'>) // proposal is a const key
|
||||
|
||||
@ -171,7 +170,7 @@ And the pseudocode for the `ProposalProcessingQueue`:
|
||||
if (isVal)
|
||||
tmpValMap(voterAddress).Vote = vote
|
||||
|
||||
tallyingProcedure = load(GlobalParams, 'TallyingProcedure')
|
||||
tallyingParam = load(GlobalParams, 'TallyingParam')
|
||||
|
||||
// Update tally if validator voted they voted
|
||||
for each validator in validators
|
||||
@ -182,14 +181,14 @@ And the pseudocode for the `ProposalProcessingQueue`:
|
||||
|
||||
// Check if proposal is accepted or rejected
|
||||
totalNonAbstain := proposal.YesVotes + proposal.NoVotes + proposal.NoWithVetoVotes
|
||||
if (proposal.Votes.YesVotes/totalNonAbstain > tallyingProcedure.Threshold AND proposal.Votes.NoWithVetoVotes/totalNonAbstain < tallyingProcedure.Veto)
|
||||
if (proposal.Votes.YesVotes/totalNonAbstain > tallyingParam.Threshold AND proposal.Votes.NoWithVetoVotes/totalNonAbstain < tallyingParam.Veto)
|
||||
// proposal was accepted at the end of the voting period
|
||||
// refund deposits (non-voters already punished)
|
||||
proposal.CurrentStatus = ProposalStatusAccepted
|
||||
for each (amount, depositer) in proposal.Deposits
|
||||
depositer.AtomBalance += amount
|
||||
|
||||
else
|
||||
else
|
||||
// proposal was rejected
|
||||
proposal.CurrentStatus = ProposalStatusRejected
|
||||
|
||||
|
||||
@ -4,7 +4,7 @@
|
||||
|
||||
### Proposal Submission
|
||||
|
||||
Proposals can be submitted by any Atom holder via a `TxGovSubmitProposal`
|
||||
Proposals can be submitted by any Atom holder via a `TxGovSubmitProposal`
|
||||
transaction.
|
||||
|
||||
```go
|
||||
@ -24,7 +24,7 @@ type TxGovSubmitProposal struct {
|
||||
* If `MinDeposit` is reached:
|
||||
* Push `proposalID` in `ProposalProcessingQueue`
|
||||
|
||||
A `TxGovSubmitProposal` transaction can be handled according to the following
|
||||
A `TxGovSubmitProposal` transaction can be handled according to the following
|
||||
pseudocode.
|
||||
|
||||
```go
|
||||
@ -32,31 +32,31 @@ pseudocode.
|
||||
// Check if TxGovSubmitProposal is valid. If it is, create proposal //
|
||||
|
||||
upon receiving txGovSubmitProposal from sender do
|
||||
|
||||
if !correctlyFormatted(txGovSubmitProposal)
|
||||
|
||||
if !correctlyFormatted(txGovSubmitProposal)
|
||||
// check if proposal is correctly formatted. Includes fee payment.
|
||||
throw
|
||||
|
||||
initialDeposit = txGovSubmitProposal.InitialDeposit
|
||||
if (initialDeposit.Atoms <= 0) OR (sender.AtomBalance < initialDeposit.Atoms)
|
||||
|
||||
initialDeposit = txGovSubmitProposal.InitialDeposit
|
||||
if (initialDeposit.Atoms <= 0) OR (sender.AtomBalance < initialDeposit.Atoms)
|
||||
// InitialDeposit is negative or null OR sender has insufficient funds
|
||||
throw
|
||||
|
||||
if (txGovSubmitProposal.Type != ProposalTypePlainText) OR (txGovSubmitProposal.Type != ProposalTypeSoftwareUpgrade)
|
||||
|
||||
|
||||
sender.AtomBalance -= initialDeposit.Atoms
|
||||
|
||||
depositProcedure = load(GlobalParams, 'DepositProcedure')
|
||||
|
||||
depositParam = load(GlobalParams, 'DepositParam')
|
||||
|
||||
proposalID = generate new proposalID
|
||||
proposal = NewProposal()
|
||||
|
||||
|
||||
proposal.Title = txGovSubmitProposal.Title
|
||||
proposal.Description = txGovSubmitProposal.Description
|
||||
proposal.Type = txGovSubmitProposal.Type
|
||||
proposal.TotalDeposit = initialDeposit
|
||||
proposal.SubmitTime = <CurrentTime>
|
||||
proposal.DepositEndTime = <CurrentTime>.Add(depositProcedure.MaxDepositPeriod)
|
||||
proposal.DepositEndTime = <CurrentTime>.Add(depositParam.MaxDepositPeriod)
|
||||
proposal.Deposits.append({initialDeposit, sender})
|
||||
proposal.Submitter = sender
|
||||
proposal.YesVotes = 0
|
||||
@ -64,15 +64,15 @@ upon receiving txGovSubmitProposal from sender do
|
||||
proposal.NoWithVetoVotes = 0
|
||||
proposal.AbstainVotes = 0
|
||||
proposal.CurrentStatus = ProposalStatusOpen
|
||||
|
||||
|
||||
store(Proposals, <proposalID|'proposal'>, proposal) // Store proposal in Proposals mapping
|
||||
return proposalID
|
||||
```
|
||||
|
||||
### Deposit
|
||||
|
||||
Once a proposal is submitted, if
|
||||
`Proposal.TotalDeposit < ActiveProcedure.MinDeposit`, Atom holders can send
|
||||
Once a proposal is submitted, if
|
||||
`Proposal.TotalDeposit < ActiveParam.MinDeposit`, Atom holders can send
|
||||
`TxGovDeposit` transactions to increase the proposal's deposit.
|
||||
|
||||
```go
|
||||
@ -89,7 +89,7 @@ type TxGovDeposit struct {
|
||||
* If `MinDeposit` is reached:
|
||||
* Push `proposalID` in `ProposalProcessingQueueEnd`
|
||||
|
||||
A `TxGovDeposit` transaction has to go through a number of checks to be valid.
|
||||
A `TxGovDeposit` transaction has to go through a number of checks to be valid.
|
||||
These checks are outlined in the following pseudocode.
|
||||
|
||||
```go
|
||||
@ -98,27 +98,27 @@ These checks are outlined in the following pseudocode.
|
||||
|
||||
upon receiving txGovDeposit from sender do
|
||||
// check if proposal is correctly formatted. Includes fee payment.
|
||||
|
||||
if !correctlyFormatted(txGovDeposit)
|
||||
|
||||
if !correctlyFormatted(txGovDeposit)
|
||||
throw
|
||||
|
||||
|
||||
proposal = load(Proposals, <txGovDeposit.ProposalID|'proposal'>) // proposal is a const key, proposalID is variable
|
||||
|
||||
if (proposal == nil)
|
||||
if (proposal == nil)
|
||||
// There is no proposal for this proposalID
|
||||
throw
|
||||
|
||||
|
||||
if (txGovDeposit.Deposit.Atoms <= 0) OR (sender.AtomBalance < txGovDeposit.Deposit.Atoms) OR (proposal.CurrentStatus != ProposalStatusOpen)
|
||||
|
||||
// deposit is negative or null
|
||||
// deposit is negative or null
|
||||
// OR sender has insufficient funds
|
||||
// OR proposal is not open for deposit anymore
|
||||
|
||||
throw
|
||||
|
||||
depositProcedure = load(GlobalParams, 'DepositProcedure')
|
||||
depositParam = load(GlobalParams, 'DepositParam')
|
||||
|
||||
if (CurrentBlock >= proposal.SubmitBlock + depositProcedure.MaxDepositPeriod)
|
||||
if (CurrentBlock >= proposal.SubmitBlock + depositParam.MaxDepositPeriod)
|
||||
proposal.CurrentStatus = ProposalStatusClosed
|
||||
|
||||
else
|
||||
@ -127,21 +127,21 @@ upon receiving txGovDeposit from sender do
|
||||
|
||||
proposal.Deposits.append({txGovVote.Deposit, sender})
|
||||
proposal.TotalDeposit.Plus(txGovDeposit.Deposit)
|
||||
|
||||
if (proposal.TotalDeposit >= depositProcedure.MinDeposit)
|
||||
|
||||
if (proposal.TotalDeposit >= depositParam.MinDeposit)
|
||||
// MinDeposit is reached, vote opens
|
||||
|
||||
|
||||
proposal.VotingStartBlock = CurrentBlock
|
||||
proposal.CurrentStatus = ProposalStatusActive
|
||||
ProposalProcessingQueue.push(txGovDeposit.ProposalID)
|
||||
ProposalProcessingQueue.push(txGovDeposit.ProposalID)
|
||||
|
||||
store(Proposals, <txGovVote.ProposalID|'proposal'>, proposal)
|
||||
```
|
||||
|
||||
### Vote
|
||||
|
||||
Once `ActiveProcedure.MinDeposit` is reached, voting period starts. From there,
|
||||
bonded Atom holders are able to send `TxGovVote` transactions to cast their
|
||||
Once `ActiveParam.MinDeposit` is reached, voting period starts. From there,
|
||||
bonded Atom holders are able to send `TxGovVote` transactions to cast their
|
||||
vote on the proposal.
|
||||
|
||||
```go
|
||||
@ -157,25 +157,25 @@ vote on the proposal.
|
||||
*Note: Gas cost for this message has to take into account the future tallying of the vote in EndBlocker*
|
||||
|
||||
|
||||
Next is a pseudocode proposal of the way `TxGovVote` transactions are
|
||||
Next is a pseudocode proposal of the way `TxGovVote` transactions are
|
||||
handled:
|
||||
|
||||
```go
|
||||
// PSEUDOCODE //
|
||||
// Check if TxGovVote is valid. If it is, count vote//
|
||||
|
||||
|
||||
upon receiving txGovVote from sender do
|
||||
// check if proposal is correctly formatted. Includes fee payment.
|
||||
|
||||
if !correctlyFormatted(txGovDeposit)
|
||||
// check if proposal is correctly formatted. Includes fee payment.
|
||||
|
||||
if !correctlyFormatted(txGovDeposit)
|
||||
throw
|
||||
|
||||
|
||||
proposal = load(Proposals, <txGovDeposit.ProposalID|'proposal'>)
|
||||
|
||||
if (proposal == nil)
|
||||
if (proposal == nil)
|
||||
// There is no proposal for this proposalID
|
||||
throw
|
||||
|
||||
|
||||
|
||||
if (proposal.CurrentStatus == ProposalStatusActive)
|
||||
|
||||
|
||||
@ -3,10 +3,11 @@ package clitest
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/cosmos/cosmos-sdk/cmd/gaia/app"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/cmd/gaia/app"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/server"
|
||||
"github.com/cosmos/cosmos-sdk/tests"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
@ -249,6 +249,30 @@ func GetCmdVote(cdc *codec.Codec) *cobra.Command {
|
||||
return cmd
|
||||
}
|
||||
|
||||
// GetCmdQueryProposal implements the query proposal command.
|
||||
func GetCmdQueryParams(queryRoute string, cdc *codec.Codec) *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "param [param-type]",
|
||||
Short: "Query the parameters (voting|tallying|deposit) of the governance process",
|
||||
Args: cobra.ExactArgs(1),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
paramType := args[0]
|
||||
|
||||
cliCtx := context.NewCLIContext().WithCodec(cdc)
|
||||
|
||||
res, err := cliCtx.QueryWithData(fmt.Sprintf("custom/%s/params/%s", queryRoute, paramType), nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Println(string(res))
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
// GetCmdQueryProposal implements the query proposal command.
|
||||
func GetCmdQueryProposal(queryRoute string, cdc *codec.Codec) *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
|
||||
@ -18,7 +18,8 @@ import (
|
||||
// REST Variable names
|
||||
// nolint
|
||||
const (
|
||||
RestProposalID = "proposalId"
|
||||
RestParamsType = "type"
|
||||
RestProposalID = "proposal-id"
|
||||
RestDepositer = "depositer"
|
||||
RestVoter = "voter"
|
||||
RestProposalStatus = "status"
|
||||
@ -32,6 +33,11 @@ func RegisterRoutes(cliCtx context.CLIContext, r *mux.Router, cdc *codec.Codec)
|
||||
r.HandleFunc(fmt.Sprintf("/gov/proposals/{%s}/deposits", RestProposalID), depositHandlerFn(cdc, cliCtx)).Methods("POST")
|
||||
r.HandleFunc(fmt.Sprintf("/gov/proposals/{%s}/votes", RestProposalID), voteHandlerFn(cdc, cliCtx)).Methods("POST")
|
||||
|
||||
r.HandleFunc(
|
||||
fmt.Sprintf("/gov/parameters/{%s}", RestParamsType),
|
||||
queryParamsHandlerFn(cdc, cliCtx),
|
||||
).Methods("GET")
|
||||
|
||||
r.HandleFunc("/gov/proposals", queryProposalsWithParameterFn(cdc, cliCtx)).Methods("GET")
|
||||
r.HandleFunc(fmt.Sprintf("/gov/proposals/{%s}", RestProposalID), queryProposalHandlerFn(cdc, cliCtx)).Methods("GET")
|
||||
r.HandleFunc(fmt.Sprintf("/gov/proposals/{%s}/deposits", RestProposalID), queryDepositsHandlerFn(cdc, cliCtx)).Methods("GET")
|
||||
@ -177,6 +183,21 @@ func voteHandlerFn(cdc *codec.Codec, cliCtx context.CLIContext) http.HandlerFunc
|
||||
}
|
||||
}
|
||||
|
||||
func queryParamsHandlerFn(cdc *codec.Codec, cliCtx context.CLIContext) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
vars := mux.Vars(r)
|
||||
paramType := vars[RestParamsType]
|
||||
|
||||
res, err := cliCtx.QueryWithData(fmt.Sprintf("custom/gov/%s/%s", gov.QueryParams, paramType), nil)
|
||||
if err != nil {
|
||||
utils.WriteErrorResponse(w, http.StatusNotFound, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
utils.PostProcessResponse(w, cdc, res, cliCtx.Indent)
|
||||
}
|
||||
}
|
||||
|
||||
func queryProposalHandlerFn(cdc *codec.Codec, cliCtx context.CLIContext) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
vars := mux.Vars(r)
|
||||
|
||||
@ -250,7 +250,7 @@ func (keeper Keeper) GetDepositParams(ctx sdk.Context) DepositParams {
|
||||
return depositParams
|
||||
}
|
||||
|
||||
// Returns the current Voting Procedure from the global param store
|
||||
// Returns the current VotingParams from the global param store
|
||||
// nolint: errcheck
|
||||
func (keeper Keeper) GetVotingParams(ctx sdk.Context) VotingParams {
|
||||
var votingParams VotingParams
|
||||
@ -258,7 +258,7 @@ func (keeper Keeper) GetVotingParams(ctx sdk.Context) VotingParams {
|
||||
return votingParams
|
||||
}
|
||||
|
||||
// Returns the current Tallying Procedure from the global param store
|
||||
// Returns the current TallyParam from the global param store
|
||||
// nolint: errcheck
|
||||
func (keeper Keeper) GetTallyParams(ctx sdk.Context) TallyParams {
|
||||
var tallyParams TallyParams
|
||||
|
||||
@ -6,20 +6,20 @@ import (
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
)
|
||||
|
||||
// Procedure 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
|
||||
}
|
||||
|
||||
// Procedure around Tallying votes in governance
|
||||
// Param around Tallying votes in governance
|
||||
type TallyParams struct {
|
||||
Threshold sdk.Dec `json:"threshold"` // Minimum propotion of Yes votes for proposal to pass. Initial value: 0.5
|
||||
Veto sdk.Dec `json:"veto"` // Minimum value of Veto votes to Total votes ratio for proposal to be vetoed. Initial value: 1/3
|
||||
GovernancePenalty sdk.Dec `json:"governance_penalty"` // Penalty if validator does not vote
|
||||
}
|
||||
|
||||
// Procedure around Voting in governance
|
||||
// Param around Voting in governance
|
||||
type VotingParams struct {
|
||||
VotingPeriod time.Duration `json:"voting_period"` // Length of the voting period.
|
||||
}
|
||||
|
||||
@ -1,6 +1,8 @@
|
||||
package gov
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/codec"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
abci "github.com/tendermint/tendermint/abci/types"
|
||||
@ -8,6 +10,7 @@ import (
|
||||
|
||||
// query endpoints supported by the governance Querier
|
||||
const (
|
||||
QueryParams = "params"
|
||||
QueryProposals = "proposals"
|
||||
QueryProposal = "proposal"
|
||||
QueryDeposits = "deposits"
|
||||
@ -15,11 +18,17 @@ const (
|
||||
QueryVotes = "votes"
|
||||
QueryVote = "vote"
|
||||
QueryTally = "tally"
|
||||
|
||||
ParamDeposit = "deposit"
|
||||
ParamVoting = "voting"
|
||||
ParamTallying = "tallying"
|
||||
)
|
||||
|
||||
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 QueryParams:
|
||||
return queryParams(ctx, path[1:], req, keeper)
|
||||
case QueryProposals:
|
||||
return queryProposals(ctx, path[1:], req, keeper)
|
||||
case QueryProposal:
|
||||
@ -40,6 +49,31 @@ func NewQuerier(keeper Keeper) sdk.Querier {
|
||||
}
|
||||
}
|
||||
|
||||
func queryParams(ctx sdk.Context, path []string, req abci.RequestQuery, keeper Keeper) (res []byte, err sdk.Error) {
|
||||
switch path[0] {
|
||||
case ParamDeposit:
|
||||
bz, err2 := codec.MarshalJSONIndent(keeper.cdc, keeper.GetDepositParams(ctx))
|
||||
if err2 != nil {
|
||||
return nil, sdk.ErrInternal(sdk.AppendMsgToErr("could not marshal result to JSON", err2.Error()))
|
||||
}
|
||||
return bz, nil
|
||||
case ParamVoting:
|
||||
bz, err2 := codec.MarshalJSONIndent(keeper.cdc, keeper.GetVotingParams(ctx))
|
||||
if err2 != nil {
|
||||
return nil, sdk.ErrInternal(sdk.AppendMsgToErr("could not marshal result to JSON", err2.Error()))
|
||||
}
|
||||
return bz, nil
|
||||
case ParamTallying:
|
||||
bz, err2 := codec.MarshalJSONIndent(keeper.cdc, keeper.GetTallyParams(ctx))
|
||||
if err2 != nil {
|
||||
return nil, sdk.ErrInternal(sdk.AppendMsgToErr("could not marshal result to JSON", err2.Error()))
|
||||
}
|
||||
return bz, nil
|
||||
default:
|
||||
return res, sdk.ErrUnknownRequest(fmt.Sprintf("%s is not a valid query request path", req.Path))
|
||||
}
|
||||
}
|
||||
|
||||
// Params for query 'custom/gov/proposal'
|
||||
type QueryProposalParams struct {
|
||||
ProposalID uint64
|
||||
306
x/gov/querier_test.go
Normal file
306
x/gov/querier_test.go
Normal file
@ -0,0 +1,306 @@
|
||||
package gov
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/codec"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/stretchr/testify/require"
|
||||
abci "github.com/tendermint/tendermint/abci/types"
|
||||
)
|
||||
|
||||
func getQueriedParams(t *testing.T, ctx sdk.Context, cdc *codec.Codec, querier sdk.Querier) (DepositParams, VotingParams, TallyParams) {
|
||||
query := abci.RequestQuery{
|
||||
Path: strings.Join([]string{"custom", "gov", QueryParams, ParamDeposit}, "/"),
|
||||
Data: []byte{},
|
||||
}
|
||||
|
||||
bz, err := querier(ctx, []string{QueryParams, ParamDeposit}, query)
|
||||
require.Nil(t, err)
|
||||
require.NotNil(t, bz)
|
||||
|
||||
var depositParams DepositParams
|
||||
err2 := cdc.UnmarshalJSON(bz, &depositParams)
|
||||
require.Nil(t, err2)
|
||||
|
||||
query = abci.RequestQuery{
|
||||
Path: strings.Join([]string{"custom", "gov", QueryParams, ParamVoting}, "/"),
|
||||
Data: []byte{},
|
||||
}
|
||||
|
||||
bz, err = querier(ctx, []string{QueryParams, ParamVoting}, query)
|
||||
require.Nil(t, err)
|
||||
require.NotNil(t, bz)
|
||||
|
||||
var votingParams VotingParams
|
||||
err2 = cdc.UnmarshalJSON(bz, &votingParams)
|
||||
require.Nil(t, err2)
|
||||
|
||||
query = abci.RequestQuery{
|
||||
Path: strings.Join([]string{"custom", "gov", QueryParams, ParamTallying}, "/"),
|
||||
Data: []byte{},
|
||||
}
|
||||
|
||||
bz, err = querier(ctx, []string{QueryParams, ParamTallying}, query)
|
||||
require.Nil(t, err)
|
||||
require.NotNil(t, bz)
|
||||
|
||||
var tallyParams TallyParams
|
||||
err2 = cdc.UnmarshalJSON(bz, &tallyParams)
|
||||
require.Nil(t, err2)
|
||||
|
||||
return depositParams, votingParams, tallyParams
|
||||
}
|
||||
|
||||
func getQueriedProposal(t *testing.T, ctx sdk.Context, cdc *codec.Codec, querier sdk.Querier, proposalID uint64) Proposal {
|
||||
query := abci.RequestQuery{
|
||||
Path: strings.Join([]string{"custom", "gov", QueryProposal}, "/"),
|
||||
Data: cdc.MustMarshalJSON(QueryProposalParams{
|
||||
ProposalID: proposalID,
|
||||
}),
|
||||
}
|
||||
|
||||
bz, err := querier(ctx, []string{QueryProposal}, query)
|
||||
require.Nil(t, err)
|
||||
require.NotNil(t, bz)
|
||||
|
||||
var proposal Proposal
|
||||
err2 := cdc.UnmarshalJSON(bz, proposal)
|
||||
require.Nil(t, err2)
|
||||
return proposal
|
||||
}
|
||||
|
||||
func getQueriedProposals(t *testing.T, ctx sdk.Context, cdc *codec.Codec, querier sdk.Querier, depositer, voter sdk.AccAddress, status ProposalStatus, limit uint64) []Proposal {
|
||||
query := abci.RequestQuery{
|
||||
Path: strings.Join([]string{"custom", "gov", QueryProposals}, "/"),
|
||||
Data: cdc.MustMarshalJSON(QueryProposalsParams{
|
||||
Voter: voter,
|
||||
Depositer: depositer,
|
||||
ProposalStatus: status,
|
||||
Limit: limit,
|
||||
}),
|
||||
}
|
||||
|
||||
bz, err := querier(ctx, []string{QueryProposal}, query)
|
||||
require.Nil(t, err)
|
||||
require.NotNil(t, bz)
|
||||
|
||||
var proposals []Proposal
|
||||
err2 := cdc.UnmarshalJSON(bz, proposals)
|
||||
require.Nil(t, err2)
|
||||
return proposals
|
||||
}
|
||||
|
||||
func getQueriedDeposit(t *testing.T, ctx sdk.Context, cdc *codec.Codec, querier sdk.Querier, proposalID uint64, depositer sdk.AccAddress) Deposit {
|
||||
query := abci.RequestQuery{
|
||||
Path: strings.Join([]string{"custom", "gov", QueryDeposit}, "/"),
|
||||
Data: cdc.MustMarshalJSON(QueryDepositParams{
|
||||
ProposalID: proposalID,
|
||||
Depositer: depositer,
|
||||
}),
|
||||
}
|
||||
|
||||
bz, err := querier(ctx, []string{QueryDeposits}, query)
|
||||
require.Nil(t, err)
|
||||
require.NotNil(t, bz)
|
||||
|
||||
var deposit Deposit
|
||||
err2 := cdc.UnmarshalJSON(bz, deposit)
|
||||
require.Nil(t, err2)
|
||||
return deposit
|
||||
}
|
||||
|
||||
func getQueriedDeposits(t *testing.T, ctx sdk.Context, cdc *codec.Codec, querier sdk.Querier, proposalID uint64) []Deposit {
|
||||
query := abci.RequestQuery{
|
||||
Path: strings.Join([]string{"custom", "gov", QueryDeposits}, "/"),
|
||||
Data: cdc.MustMarshalJSON(QueryDepositsParams{
|
||||
ProposalID: proposalID,
|
||||
}),
|
||||
}
|
||||
|
||||
bz, err := querier(ctx, []string{QueryDeposits}, query)
|
||||
require.Nil(t, err)
|
||||
require.NotNil(t, bz)
|
||||
|
||||
var deposits []Deposit
|
||||
err2 := cdc.UnmarshalJSON(bz, &deposits)
|
||||
require.Nil(t, err2)
|
||||
return deposits
|
||||
}
|
||||
|
||||
func getQueriedVote(t *testing.T, ctx sdk.Context, cdc *codec.Codec, querier sdk.Querier, proposalID uint64, voter sdk.AccAddress) Vote {
|
||||
query := abci.RequestQuery{
|
||||
Path: strings.Join([]string{"custom", "gov", QueryVote}, "/"),
|
||||
Data: cdc.MustMarshalJSON(QueryVoteParams{
|
||||
ProposalID: proposalID,
|
||||
Voter: voter,
|
||||
}),
|
||||
}
|
||||
|
||||
bz, err := querier(ctx, []string{QueryVote}, query)
|
||||
require.Nil(t, err)
|
||||
require.NotNil(t, bz)
|
||||
|
||||
var vote Vote
|
||||
err2 := cdc.UnmarshalJSON(bz, &vote)
|
||||
require.Nil(t, err2)
|
||||
return vote
|
||||
}
|
||||
|
||||
func getQueriedVotes(t *testing.T, ctx sdk.Context, cdc *codec.Codec, querier sdk.Querier, proposalID uint64) []Vote {
|
||||
query := abci.RequestQuery{
|
||||
Path: strings.Join([]string{"custom", "gov", QueryVote}, "/"),
|
||||
Data: cdc.MustMarshalJSON(QueryVotesParams{
|
||||
ProposalID: proposalID,
|
||||
}),
|
||||
}
|
||||
|
||||
bz, err := querier(ctx, []string{QueryVotes}, query)
|
||||
require.Nil(t, err)
|
||||
require.NotNil(t, bz)
|
||||
|
||||
var votes []Vote
|
||||
err2 := cdc.UnmarshalJSON(bz, &votes)
|
||||
require.Nil(t, err2)
|
||||
return votes
|
||||
}
|
||||
|
||||
func getQueriedTally(t *testing.T, ctx sdk.Context, cdc *codec.Codec, querier sdk.Querier, proposalID uint64) TallyResult {
|
||||
query := abci.RequestQuery{
|
||||
Path: strings.Join([]string{"custom", "gov", QueryTally}, "/"),
|
||||
Data: cdc.MustMarshalJSON(QueryTallyParams{
|
||||
ProposalID: proposalID,
|
||||
}),
|
||||
}
|
||||
|
||||
bz, err := querier(ctx, []string{QueryTally}, query)
|
||||
require.Nil(t, err)
|
||||
require.NotNil(t, bz)
|
||||
|
||||
var tally TallyResult
|
||||
err2 := cdc.UnmarshalJSON(bz, &tally)
|
||||
require.Nil(t, err2)
|
||||
return tally
|
||||
}
|
||||
|
||||
func testQueryParams(t *testing.T) {
|
||||
cdc := codec.New()
|
||||
mapp, keeper, _, _, _, _ := getMockApp(t, 1000)
|
||||
querier := NewQuerier(keeper)
|
||||
ctx := mapp.NewContext(false, abci.Header{})
|
||||
|
||||
getQueriedParams(t, ctx, cdc, querier)
|
||||
}
|
||||
|
||||
func testQueries(t *testing.T) {
|
||||
cdc := codec.New()
|
||||
mapp, keeper, _, addrs, _, _ := getMockApp(t, 1000)
|
||||
querier := NewQuerier(keeper)
|
||||
handler := NewHandler(keeper)
|
||||
ctx := mapp.NewContext(false, abci.Header{})
|
||||
|
||||
depositParams, _, _ := getQueriedParams(t, ctx, cdc, querier)
|
||||
|
||||
// addrs[0] proposes (and deposits) proposals #1 and #2
|
||||
res := handler(ctx, NewMsgSubmitProposal("title", "description", ProposalTypeText, addrs[0], sdk.Coins{sdk.NewInt64Coin("dummycoin", 1)}))
|
||||
var proposalID1 uint64
|
||||
cdc.MustUnmarshalBinaryLengthPrefixed(res.Data, &proposalID1)
|
||||
|
||||
res = handler(ctx, NewMsgSubmitProposal("title", "description", ProposalTypeText, addrs[0], sdk.Coins{sdk.NewInt64Coin("dummycoin", 1)}))
|
||||
var proposalID2 uint64
|
||||
cdc.MustUnmarshalBinaryLengthPrefixed(res.Data, &proposalID2)
|
||||
|
||||
// addrs[1] proposes (and deposits) proposals #3
|
||||
res = handler(ctx, NewMsgSubmitProposal("title", "description", ProposalTypeText, addrs[1], sdk.Coins{sdk.NewInt64Coin("dummycoin", 1)}))
|
||||
var proposalID3 uint64
|
||||
cdc.MustUnmarshalBinaryLengthPrefixed(res.Data, &proposalID3)
|
||||
|
||||
// addrs[1] deposits on proposals #2 & #3
|
||||
res = handler(ctx, NewMsgDeposit(addrs[1], proposalID2, depositParams.MinDeposit))
|
||||
res = handler(ctx, NewMsgDeposit(addrs[1], proposalID3, depositParams.MinDeposit))
|
||||
|
||||
// check deposits on proposal1 match individual deposits
|
||||
deposits := getQueriedDeposits(t, ctx, cdc, querier, proposalID1)
|
||||
require.Len(t, deposits, 1)
|
||||
deposit := getQueriedDeposit(t, ctx, cdc, querier, proposalID1, addrs[0])
|
||||
require.Equal(t, deposit, deposits[0])
|
||||
|
||||
// check deposits on proposal2 match individual deposits
|
||||
deposits = getQueriedDeposits(t, ctx, cdc, querier, proposalID2)
|
||||
require.Len(t, deposits, 2)
|
||||
deposit = getQueriedDeposit(t, ctx, cdc, querier, proposalID2, addrs[0])
|
||||
require.True(t, deposit.Equals(deposits[0]))
|
||||
deposit = getQueriedDeposit(t, ctx, cdc, querier, proposalID2, addrs[1])
|
||||
require.True(t, deposit.Equals(deposits[1]))
|
||||
|
||||
// check deposits on proposal3 match individual deposits
|
||||
deposits = getQueriedDeposits(t, ctx, cdc, querier, proposalID3)
|
||||
require.Len(t, deposits, 1)
|
||||
deposit = getQueriedDeposit(t, ctx, cdc, querier, proposalID3, addrs[1])
|
||||
require.Equal(t, deposit, deposits[0])
|
||||
|
||||
// Only proposal #1 should be in Deposit Period
|
||||
proposals := getQueriedProposals(t, ctx, cdc, querier, nil, nil, StatusDepositPeriod, 0)
|
||||
require.Len(t, proposals, 1)
|
||||
require.Equal(t, proposalID1, proposals[0].GetProposalID())
|
||||
// Only proposals #2 and #3 should be in Voting Period
|
||||
proposals = getQueriedProposals(t, ctx, cdc, querier, nil, nil, StatusVotingPeriod, 0)
|
||||
require.Len(t, proposals, 2)
|
||||
require.Equal(t, proposalID2, proposals[0].GetProposalID())
|
||||
require.Equal(t, proposalID3, proposals[1].GetProposalID())
|
||||
|
||||
// Addrs[0] votes on proposals #2 & #3
|
||||
handler(ctx, NewMsgVote(addrs[0], proposalID2, OptionYes))
|
||||
handler(ctx, NewMsgVote(addrs[0], proposalID3, OptionYes))
|
||||
|
||||
// Addrs[1] votes on proposal #3
|
||||
handler(ctx, NewMsgVote(addrs[1], proposalID3, OptionYes))
|
||||
|
||||
// Test query voted by addrs[0]
|
||||
proposals = getQueriedProposals(t, ctx, cdc, querier, nil, addrs[0], StatusNil, 0)
|
||||
require.Equal(t, proposalID2, (proposals[0]).GetProposalID())
|
||||
require.Equal(t, proposalID3, (proposals[1]).GetProposalID())
|
||||
|
||||
// Test query votes on Proposal 2
|
||||
votes := getQueriedVotes(t, ctx, cdc, querier, proposalID2)
|
||||
require.Len(t, votes, 1)
|
||||
require.Equal(t, addrs[0], votes[0].Voter)
|
||||
vote := getQueriedVote(t, ctx, cdc, querier, proposalID2, addrs[0])
|
||||
require.Equal(t, vote, votes[0])
|
||||
|
||||
// Test query votes on Proposal 3
|
||||
votes = getQueriedVotes(t, ctx, cdc, querier, proposalID3)
|
||||
require.Len(t, votes, 2)
|
||||
require.True(t, addrs[0].String() == votes[0].Voter.String())
|
||||
require.True(t, addrs[1].String() == votes[0].Voter.String())
|
||||
|
||||
// Test proposals queries with filters
|
||||
|
||||
// Test query all proposals
|
||||
proposals = getQueriedProposals(t, ctx, cdc, querier, nil, nil, StatusNil, 0)
|
||||
require.Equal(t, proposalID1, (proposals[0]).GetProposalID())
|
||||
require.Equal(t, proposalID2, (proposals[1]).GetProposalID())
|
||||
require.Equal(t, proposalID3, (proposals[2]).GetProposalID())
|
||||
|
||||
// Test query voted by addrs[1]
|
||||
proposals = getQueriedProposals(t, ctx, cdc, querier, nil, addrs[1], StatusNil, 0)
|
||||
require.Equal(t, proposalID3, (proposals[0]).GetProposalID())
|
||||
|
||||
// Test query deposited by addrs[0]
|
||||
proposals = getQueriedProposals(t, ctx, cdc, querier, addrs[0], nil, StatusNil, 0)
|
||||
require.Equal(t, proposalID1, (proposals[0]).GetProposalID())
|
||||
|
||||
// Test query deposited by addr2
|
||||
proposals = getQueriedProposals(t, ctx, cdc, querier, addrs[1], nil, StatusNil, 0)
|
||||
require.Equal(t, proposalID2, (proposals[0]).GetProposalID())
|
||||
require.Equal(t, proposalID3, (proposals[1]).GetProposalID())
|
||||
|
||||
// Test query voted AND deposited by addr1
|
||||
proposals = getQueriedProposals(t, ctx, cdc, querier, addrs[0], addrs[0], StatusNil, 0)
|
||||
require.Equal(t, proposalID2, (proposals[0]).GetProposalID())
|
||||
|
||||
// Test Tally Query
|
||||
tally := getQueriedTally(t, ctx, cdc, querier, proposalID2)
|
||||
require.True(t, !tally.Equals(EmptyTallyResult()))
|
||||
}
|
||||
@ -39,6 +39,7 @@ func getMockApp(t *testing.T, numGenAccs int) (*mock.App, Keeper, stake.Keeper,
|
||||
keeper := NewKeeper(mapp.Cdc, keyGov, pk, pk.Subspace("testgov"), ck, sk, DefaultCodespace)
|
||||
|
||||
mapp.Router().AddRoute("gov", NewHandler(keeper))
|
||||
mapp.QueryRouter().AddRoute("gov", NewQuerier(keeper))
|
||||
|
||||
mapp.SetEndBlocker(getEndBlocker(keeper))
|
||||
mapp.SetInitChainer(getInitChainer(mapp, keeper, sk))
|
||||
|
||||
@ -1,9 +1,11 @@
|
||||
package mock
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"os"
|
||||
"sort"
|
||||
|
||||
bam "github.com/cosmos/cosmos-sdk/baseapp"
|
||||
"github.com/cosmos/cosmos-sdk/codec"
|
||||
@ -109,23 +111,64 @@ func (app *App) InitChainer(ctx sdk.Context, _ abci.RequestInitChain) abci.Respo
|
||||
return abci.ResponseInitChain{}
|
||||
}
|
||||
|
||||
// Type that combines an Address with the privKey and pubKey to that address
|
||||
type AddrKeys struct {
|
||||
Address sdk.AccAddress
|
||||
PubKey crypto.PubKey
|
||||
PrivKey crypto.PrivKey
|
||||
}
|
||||
|
||||
// implement `Interface` in sort package.
|
||||
type AddrKeysSlice []AddrKeys
|
||||
|
||||
func (b AddrKeysSlice) Len() int {
|
||||
return len(b)
|
||||
}
|
||||
|
||||
// Sorts lexographically by Address
|
||||
func (b AddrKeysSlice) Less(i, j int) bool {
|
||||
// bytes package already implements Comparable for []byte.
|
||||
switch bytes.Compare(b[i].Address.Bytes(), b[j].Address.Bytes()) {
|
||||
case -1:
|
||||
return true
|
||||
case 0, 1:
|
||||
return false
|
||||
default:
|
||||
panic("not fail-able with `bytes.Comparable` bounded [-1, 1].")
|
||||
}
|
||||
}
|
||||
|
||||
func (b AddrKeysSlice) Swap(i, j int) {
|
||||
b[j], b[i] = b[i], b[j]
|
||||
}
|
||||
|
||||
// CreateGenAccounts generates genesis accounts loaded with coins, and returns
|
||||
// their addresses, pubkeys, and privkeys.
|
||||
func CreateGenAccounts(numAccs int, genCoins sdk.Coins) (genAccs []auth.Account, addrs []sdk.AccAddress, pubKeys []crypto.PubKey, privKeys []crypto.PrivKey) {
|
||||
addrKeysSlice := AddrKeysSlice{}
|
||||
|
||||
for i := 0; i < numAccs; i++ {
|
||||
privKey := ed25519.GenPrivKey()
|
||||
pubKey := privKey.PubKey()
|
||||
addr := sdk.AccAddress(pubKey.Address())
|
||||
|
||||
genAcc := &auth.BaseAccount{
|
||||
addrKeysSlice = append(addrKeysSlice, AddrKeys{
|
||||
Address: addr,
|
||||
Coins: genCoins,
|
||||
}
|
||||
PubKey: pubKey,
|
||||
PrivKey: privKey,
|
||||
})
|
||||
}
|
||||
|
||||
genAccs = append(genAccs, genAcc)
|
||||
privKeys = append(privKeys, privKey)
|
||||
pubKeys = append(pubKeys, pubKey)
|
||||
addrs = append(addrs, addr)
|
||||
sort.Sort(addrKeysSlice)
|
||||
|
||||
for i := range addrKeysSlice {
|
||||
addrs = append(addrs, addrKeysSlice[i].Address)
|
||||
pubKeys = append(pubKeys, addrKeysSlice[i].PubKey)
|
||||
privKeys = append(privKeys, addrKeysSlice[i].PrivKey)
|
||||
genAccs = append(genAccs, &auth.BaseAccount{
|
||||
Address: addrKeysSlice[i].Address,
|
||||
Coins: genCoins,
|
||||
})
|
||||
}
|
||||
|
||||
return
|
||||
|
||||
Loading…
Reference in New Issue
Block a user