diff --git a/proto/atomone/gov/v1/gov.proto b/proto/atomone/gov/v1/gov.proto index b50e2ed9..4b3c8171 100644 --- a/proto/atomone/gov/v1/gov.proto +++ b/proto/atomone/gov/v1/gov.proto @@ -346,4 +346,7 @@ message Params { MinDepositThrottler min_deposit_throttler = 23; MinInitialDepositThrottler min_initial_deposit_throttler = 24; + + // Minimum proportion of No Votes for a proposal deposit to be burnt. + string burn_deposit_no_threshold = 25 [(cosmos_proto.scalar) = "cosmos.Dec"]; } diff --git a/tests/e2e/e2e_gov_test.go b/tests/e2e/e2e_gov_test.go index d14096fd..868bb50d 100644 --- a/tests/e2e/e2e_gov_test.go +++ b/tests/e2e/e2e_gov_test.go @@ -132,6 +132,8 @@ func (s *IntegrationTestSuite) testGovCommunityPoolSpend() { sendAmount := sdk.NewInt64Coin(uatoneDenom, 10_000_000) // 10atone s.writeGovCommunitySpendProposal(s.chainA, sendAmount, recipient) + beforeSenderBalance, err := getSpecificBalance(chainAAPIEndpoint, sender, uatoneDenom) + s.Require().NoError(err) beforeRecipientBalance, err := getSpecificBalance(chainAAPIEndpoint, recipient, uatoneDenom) s.Require().NoError(err) @@ -140,8 +142,20 @@ func (s *IntegrationTestSuite) testGovCommunityPoolSpend() { submitGovFlags := []string{configFile(proposalCommunitySpendFilename)} depositGovFlags := []string{strconv.Itoa(proposalCounter), depositAmount.String()} voteGovFlags := []string{strconv.Itoa(proposalCounter), "yes"} - s.submitGovProposal(chainAAPIEndpoint, sender, proposalCounter, "CommunityPoolSpend", submitGovFlags, depositGovFlags, voteGovFlags, "vote") + s.submitGovProposal(chainAAPIEndpoint, sender, proposalCounter, "CommunityPoolSpend", submitGovFlags, depositGovFlags, voteGovFlags, "vote", govtypesv1beta1.StatusPassed) + + // Check that sender is refunded with the proposal deposit + s.Require().Eventually( + func() bool { + afterSenderBalance, err := getSpecificBalance(chainAAPIEndpoint, sender, uatoneDenom) + s.Require().NoError(err) + return afterSenderBalance.IsEqual(beforeSenderBalance) + }, + 10*time.Second, + time.Second, + ) + // Check that recipient received the community pool spend s.Require().Eventually( func() bool { afterRecipientBalance, err := getSpecificBalance(chainAAPIEndpoint, recipient, uatoneDenom) @@ -153,6 +167,53 @@ func (s *IntegrationTestSuite) testGovCommunityPoolSpend() { time.Second, ) }) + s.Run("community pool spend with number of no votes exceeds threshold", func() { + s.fundCommunityPool() + chainAAPIEndpoint := fmt.Sprintf("http://%s", s.valResources[s.chainA.id][0].GetHostPort("1317/tcp")) + senderAddress, _ := s.chainA.validators[0].keyInfo.GetAddress() + sender := senderAddress.String() + recipientAddress, _ := s.chainA.validators[1].keyInfo.GetAddress() + recipient := recipientAddress.String() + sendAmount := sdk.NewInt64Coin(uatoneDenom, 10_000_000) // 10atone + s.writeGovCommunitySpendProposal(s.chainA, sendAmount, recipient) + + beforeSenderBalance, err := getSpecificBalance(chainAAPIEndpoint, sender, uatoneDenom) + s.Require().NoError(err) + beforeRecipientBalance, err := getSpecificBalance(chainAAPIEndpoint, recipient, uatoneDenom) + s.Require().NoError(err) + + // Gov tests may be run in arbitrary order, each test must increment proposalCounter to have the correct proposal id to submit and query + proposalCounter++ + submitGovFlags := []string{configFile(proposalCommunitySpendFilename)} + depositGovFlags := []string{strconv.Itoa(proposalCounter), depositAmount.String()} + voteGovFlags := []string{strconv.Itoa(proposalCounter), "no"} + s.submitGovProposal(chainAAPIEndpoint, sender, proposalCounter, "CommunityPoolSpend", submitGovFlags, depositGovFlags, voteGovFlags, "vote", govtypesv1beta1.StatusRejected) + + // Check that sender is not refunded with the proposal deposit + s.Require().Eventually( + func() bool { + afterSenderBalance, err := getSpecificBalance(chainAAPIEndpoint, sender, uatoneDenom) + s.Require().NoError(err) + + return afterSenderBalance.Add(depositAmount).Add(initialDepositAmount). + IsEqual(beforeSenderBalance) + }, + 10*time.Second, + time.Second, + ) + // Check that recipient didnt receive the community pool spend since the + // proposal was rejected + s.Require().Eventually( + func() bool { + afterRecipientBalance, err := getSpecificBalance(chainAAPIEndpoint, recipient, uatoneDenom) + s.Require().NoError(err) + + return afterRecipientBalance.IsEqual(beforeRecipientBalance) + }, + 10*time.Second, + time.Second, + ) + }) } // testGovParamChange tests passing a param change proposal. @@ -173,7 +234,7 @@ func (s *IntegrationTestSuite) testGovParamChange() { submitGovFlags := []string{configFile(proposalParamChangeFilename)} depositGovFlags := []string{strconv.Itoa(proposalCounter), depositAmount.String()} voteGovFlags := []string{strconv.Itoa(proposalCounter), "yes"} - s.submitGovProposal(chainAAPIEndpoint, sender, proposalCounter, "cosmos.staking.v1beta1.MsgUpdateParams", submitGovFlags, depositGovFlags, voteGovFlags, "vote") + s.submitGovProposal(chainAAPIEndpoint, sender, proposalCounter, "cosmos.staking.v1beta1.MsgUpdateParams", submitGovFlags, depositGovFlags, voteGovFlags, "vote", govtypesv1beta1.StatusPassed) newParams := s.queryStakingParams(chainAAPIEndpoint) s.Assert().NotEqual(oldMaxValidator, newParams.Params.MaxValidators) @@ -196,7 +257,7 @@ func (s *IntegrationTestSuite) testGovParamChange() { submitGovFlags := []string{configFile(proposalParamChangeFilename)} depositGovFlags := []string{strconv.Itoa(proposalCounter), depositAmount.String()} voteGovFlags := []string{strconv.Itoa(proposalCounter), "yes"} - s.submitGovProposal(chainAAPIEndpoint, sender, proposalCounter, "atomone.photon.v1.MsgUpdateParams", submitGovFlags, depositGovFlags, voteGovFlags, "vote") + s.submitGovProposal(chainAAPIEndpoint, sender, proposalCounter, "atomone.photon.v1.MsgUpdateParams", submitGovFlags, depositGovFlags, voteGovFlags, "vote", govtypesv1beta1.StatusPassed) newParams := s.queryPhotonParams(chainAAPIEndpoint) s.Assert().True(newParams.Params.MintDisabled, "expected photon param mint disabled to be true") @@ -207,7 +268,7 @@ func (s *IntegrationTestSuite) testGovParamChange() { depositGovFlags = []string{strconv.Itoa(proposalCounter), depositAmount.String()} voteGovFlags = []string{strconv.Itoa(proposalCounter), "yes"} s.writePhotonParamChangeProposal(s.chainA, params.Params) - s.submitGovProposal(chainAAPIEndpoint, sender, proposalCounter, "atomone.photon.v1.MsgUpdateParams", submitGovFlags, depositGovFlags, voteGovFlags, "vote") + s.submitGovProposal(chainAAPIEndpoint, sender, proposalCounter, "atomone.photon.v1.MsgUpdateParams", submitGovFlags, depositGovFlags, voteGovFlags, "vote", govtypesv1beta1.StatusPassed) newParams = s.queryPhotonParams(chainAAPIEndpoint) s.Require().False(newParams.Params.MintDisabled, "expected photon param mint disabled to be false") @@ -229,7 +290,7 @@ func (s *IntegrationTestSuite) testGovConstitutionAmendment() { submitGovFlags := []string{configFile(proposalConstitutionAmendmentFilename)} depositGovFlags := []string{strconv.Itoa(proposalCounter), depositAmount.String()} voteGovFlags := []string{strconv.Itoa(proposalCounter), "yes"} - s.submitGovProposal(chainAAPIEndpoint, sender, proposalCounter, "gov/MsgSubmitProposal", submitGovFlags, depositGovFlags, voteGovFlags, "vote") + s.submitGovProposal(chainAAPIEndpoint, sender, proposalCounter, "gov/MsgSubmitProposal", submitGovFlags, depositGovFlags, voteGovFlags, "vote", govtypesv1beta1.StatusPassed) s.Require().Eventually( func() bool { @@ -260,14 +321,14 @@ func (s *IntegrationTestSuite) submitLegacyGovProposal(chainAAPIEndpoint, sender // Instead, the deposit is added to the "deposit" field of the proposal JSON (usually stored as a file) // you can use `atomoned tx gov draft-proposal` to create a proposal file that you can use // min initial deposit of 100uatone is required in e2e tests, otherwise the proposal would be dropped -func (s *IntegrationTestSuite) submitGovProposal(chainAAPIEndpoint, sender string, proposalID int, proposalType string, submitFlags []string, depositFlags []string, voteFlags []string, voteCommand string) { +func (s *IntegrationTestSuite) submitGovProposal(chainAAPIEndpoint, sender string, proposalID int, proposalType string, submitFlags []string, depositFlags []string, voteFlags []string, voteCommand string, expectedStatusAfterVote govtypesv1beta1.ProposalStatus) { s.T().Logf("Submitting Gov Proposal: %s", proposalType) sflags := submitFlags s.submitGovCommand(chainAAPIEndpoint, sender, proposalID, "submit-proposal", sflags, govtypesv1beta1.StatusDepositPeriod) s.T().Logf("Depositing Gov Proposal: %s", proposalType) s.submitGovCommand(chainAAPIEndpoint, sender, proposalID, "deposit", depositFlags, govtypesv1beta1.StatusVotingPeriod) s.T().Logf("Voting Gov Proposal: %s", proposalType) - s.submitGovCommand(chainAAPIEndpoint, sender, proposalID, voteCommand, voteFlags, govtypesv1beta1.StatusPassed) + s.submitGovCommand(chainAAPIEndpoint, sender, proposalID, voteCommand, voteFlags, expectedStatusAfterVote) } func (s *IntegrationTestSuite) verifyChainHaltedAtUpgradeHeight(c *chain, valIdx int, upgradeHeight int64) { diff --git a/tests/e2e/genesis_test.go b/tests/e2e/genesis_test.go index 78def00b..951512ff 100644 --- a/tests/e2e/genesis_test.go +++ b/tests/e2e/genesis_test.go @@ -202,6 +202,7 @@ func modifyGenesis(path, moniker, amountStr string, addrAll []sdk.AccAddress, de govv1.DefaultTargetActiveProposals, sdk.NewCoins(sdk.NewCoin(denom, initialDepositAmount.Amount)), govv1.DefaultMinInitialDepositUpdatePeriod, govv1.DefaultMinInitialDepositSensitivityTargetDistance, govv1.DefaultMinInitialDepositIncreaseRatio.String(), govv1.DefaultMinInitialDepositDecreaseRatio.String(), govv1.DefaultTargetProposalsInDepositPeriod, + govv1.DefaultBurnDepositNoThreshold.String(), ), ) govGenState.Constitution = "This is a test constitution" diff --git a/x/gov/README.md b/x/gov/README.md index c89007ea..2492b50f 100644 --- a/x/gov/README.md +++ b/x/gov/README.md @@ -41,18 +41,18 @@ staking token of the chain. * [Proposal submission](#proposal-submission) * [Deposit](#deposit) * [Vote](#vote) - * [Software Upgrade](#software-upgrade) + * [Quorum](#quorum) * [State](#state) * [Proposals](#proposals) * [Parameters and base types](#parameters-and-base-types) * [Deposit](#deposit-1) - * [ValidatorGovInfo](#validatorgovinfo) - * [Stores](#stores) +* [Stores](#stores) * [Proposal Processing Queue](#proposal-processing-queue) * [Legacy Proposal](#legacy-proposal) * [Quorum Checks and Voting Period Extension](#quorum-checks-and-voting-period-extension) * [Constitution](#constitution) * [Law and Constitution Amendment Proposals](#law-and-constitution-amendment-proposals) + * [Last Min Deposit and Last Min Initial Deposit](#last-min-deposit-and-last-min-initial-deposit) * [Messages](#messages) * [Proposal Submission](#proposal-submission-1) * [Deposit](#deposit-2) @@ -61,6 +61,8 @@ staking token of the chain. * [EndBlocker](#endblocker) * [Handlers](#handlers) * [Parameters](#parameters) + * [MinDepositThrottler (dynamic MinDeposit)](#mindepositthrottler-dynamic-mindeposit) + * [MinInitialDepositThrottler (dynamic MinInitialDeposit)](#mininitialdepositthrottler-dynamic-mininitialdeposit) * [Client](#client) * [CLI](#cli) * [gRPC](#grpc) @@ -199,6 +201,11 @@ The initial option set includes the following options: `Abstain` option allows voters to signal that they do not intend to vote in favor or against the proposal but accept the result of the vote. +At the end of the voting period, if the percentage of `No` votes (excluding +`Abstain` votes) is greater than a specific threshold (see [Burnable +Params](#burnable-params) section), then the proposal is considered as SPAM and +its deposit is burned. + #### Weighted Votes [ADR-037](https://github.com/cosmos/cosmos-sdk/blob/main/docs/architecture/adr-037-gov-split-vote.md) @@ -270,6 +277,9 @@ have to sign governance transactions with the sensitive CometBFT PrivKey. There are three parameters that define if the deposit of a proposal should be burned or returned to the depositors. +* `BurnDepositNoThreshold` burns the proposal deposit at the end of the voting + period if the percentage of `No` votes (excluding `Abstain` votes) exceeds + the threshold. * `BurnVoteQuorum` burns the proposal deposit if the proposal deposit if the vote does not reach quorum. * `BurnProposalDepositPrevote` burns the proposal deposit if it does not enter the voting phase. diff --git a/x/gov/abci.go b/x/gov/abci.go index 091e1f64..6cda55a0 100644 --- a/x/gov/abci.go +++ b/x/gov/abci.go @@ -69,10 +69,7 @@ func EndBlocker(ctx sdk.Context, keeper *keeper.Keeper) { // remove from queue keeper.RemoveFromQuorumCheckQueue(ctx, proposal.Id, endTime) // check if proposal passed quorum - quorum, err := keeper.HasReachedQuorum(ctx, proposal) - if err != nil { - return false - } + quorum := keeper.HasReachedQuorum(ctx, proposal) logMsg := "proposal did not pass quorum after timeout, but was removed from quorum check queue" tagValue := types.AttributeValueProposalQuorumNotMet diff --git a/x/gov/genesis.go b/x/gov/genesis.go index d14a8378..5eca7f01 100644 --- a/x/gov/genesis.go +++ b/x/gov/genesis.go @@ -48,11 +48,7 @@ func InitGenesis(ctx sdk.Context, ak types.AccountKeeper, bk types.BankKeeper, k quorumCheckEntry := v1.NewQuorumCheckQueueEntry(quorumTimeoutTime, data.Params.QuorumCheckCount) quorum := false if ctx.BlockTime().After(quorumTimeoutTime) { - var err error - quorum, err = k.HasReachedQuorum(ctx, *proposal) - if err != nil { - panic(err) - } + quorum = k.HasReachedQuorum(ctx, *proposal) if !quorum { // since we don't export the state of the quorum check queue, we can't know how many checks were actually // done. However, in order to trigger a vote time extension, it is enough to have QuorumChecksDone > 0 to diff --git a/x/gov/keeper/msg_server_test.go b/x/gov/keeper/msg_server_test.go index beb76ee3..64f1c46c 100644 --- a/x/gov/keeper/msg_server_test.go +++ b/x/gov/keeper/msg_server_test.go @@ -924,6 +924,20 @@ func (suite *KeeperTestSuite) TestMsgUpdateParams() { expErr: true, expErrMsg: "quorum too large", }, + { + name: "empty threshold", + input: func() *v1.MsgUpdateParams { + params1 := params + params1.Threshold = "" + + return &v1.MsgUpdateParams{ + Authority: authority, + Params: params1, + } + }, + expErr: true, + expErrMsg: "invalid threshold string: decimal string cannot be empty", + }, { name: "invalid threshold", input: func() *v1.MsgUpdateParams { @@ -994,6 +1008,20 @@ func (suite *KeeperTestSuite) TestMsgUpdateParams() { expErr: true, expErrMsg: "constitution amendment quorum too large", }, + { + name: "empty constitution amendment threshold", + input: func() *v1.MsgUpdateParams { + params1 := params + params1.ConstitutionAmendmentThreshold = "" + + return &v1.MsgUpdateParams{ + Authority: authority, + Params: params1, + } + }, + expErr: true, + expErrMsg: "invalid constitution amendment threshold string: decimal string cannot be empty", + }, { name: "negative constitution amendment threshold", input: func() *v1.MsgUpdateParams { @@ -1205,6 +1233,97 @@ func (suite *KeeperTestSuite) TestMsgUpdateParams() { } }, }, + { + name: "empty burnDepositNoThreshold", + input: func() *v1.MsgUpdateParams { + params1 := params + params1.BurnDepositNoThreshold = "" + + return &v1.MsgUpdateParams{ + Authority: authority, + Params: params1, + } + }, + expErr: true, + expErrMsg: "invalid burnDepositNoThreshold string: decimal string cannot be empty", + }, + { + name: "invalid burnDepositNoThreshold", + input: func() *v1.MsgUpdateParams { + params1 := params + params1.BurnDepositNoThreshold = "abc" + + return &v1.MsgUpdateParams{ + Authority: authority, + Params: params1, + } + }, + expErr: true, + expErrMsg: "invalid burnDepositNoThreshold string", + }, + { + name: "burnDepositNoThreshold <= 1 - amendmentThreshold", + input: func() *v1.MsgUpdateParams { + params1 := params + params1.LawThreshold = "0.8" + params1.ConstitutionAmendmentThreshold = "0.8" + params1.BurnDepositNoThreshold = "0.199" + + return &v1.MsgUpdateParams{ + Authority: authority, + Params: params1, + } + }, + expErr: true, + expErrMsg: "burnDepositNoThreshold cannot be lower than 1-amendmentThreshold", + }, + { + name: "burnDepositNoThreshold <= 1 - lawThreshold", + input: func() *v1.MsgUpdateParams { + params1 := params + params1.ConstitutionAmendmentThreshold = "0.9" + params1.LawThreshold = "0.8" + params1.BurnDepositNoThreshold = "0.199" + + return &v1.MsgUpdateParams{ + Authority: authority, + Params: params1, + } + }, + expErr: true, + expErrMsg: "burnDepositNoThreshold cannot be lower than 1-lawThreshold", + }, + { + name: "burnDepositNoThreshold <= 1 - threshold", + input: func() *v1.MsgUpdateParams { + params1 := params + params1.ConstitutionAmendmentThreshold = "0.8" + params1.LawThreshold = "0.8" + params1.Threshold = "0.6" + params1.BurnDepositNoThreshold = "0.399" + + return &v1.MsgUpdateParams{ + Authority: authority, + Params: params1, + } + }, + expErr: true, + expErrMsg: "burnDepositNoThreshold cannot be lower than 1-threshold", + }, + { + name: "burnDepositNoThreshold > 1", + input: func() *v1.MsgUpdateParams { + params1 := params + params1.BurnDepositNoThreshold = "2" + + return &v1.MsgUpdateParams{ + Authority: authority, + Params: params1, + } + }, + expErr: true, + expErrMsg: "burnDepositNoThreshold too large", + }, } for _, tc := range testCases { diff --git a/x/gov/keeper/tally.go b/x/gov/keeper/tally.go index 7b65fbaf..0a896a8d 100644 --- a/x/gov/keeper/tally.go +++ b/x/gov/keeper/tally.go @@ -1,8 +1,6 @@ package keeper import ( - "fmt" - "cosmossdk.io/math" sdk "github.com/cosmos/cosmos-sdk/types" @@ -29,34 +27,45 @@ func (keeper Keeper) Tally(ctx sdk.Context, proposal v1.Proposal) (passes bool, // If there is not enough quorum of votes, the proposal fails percentVoting := totalVotingPower.Quo(math.LegacyNewDecFromInt(totalBonded)) - quorum, threshold, err := keeper.getQuorumAndThreshold(ctx, proposal) - if err != nil { - return false, false, tallyResults - } + quorum, threshold := keeper.getQuorumAndThreshold(ctx, proposal) if percentVoting.LT(quorum) { return false, params.BurnVoteQuorum, tallyResults } + // Compute non-abstaining voting power, aka active voting power + activeVotingPower := totalVotingPower.Sub(results[v1.OptionAbstain]) + // If no one votes (everyone abstains), proposal fails - if totalVotingPower.Sub(results[v1.OptionAbstain]).Equal(math.LegacyZeroDec()) { + if activeVotingPower.IsZero() { return false, false, tallyResults } - if results[v1.OptionYes].Quo(totalVotingPower.Sub(results[v1.OptionAbstain])).GT(threshold) { + // If more than `threshold` of non-abstaining voters vote Yes, proposal passes. + yesPercent := results[v1.OptionYes].Quo(activeVotingPower) + if yesPercent.GT(threshold) { return true, false, tallyResults } - // If more than 1/2 of non-abstaining voters vote No, proposal fails + // If more than `burnDepositNoThreshold` of non-abstaining voters vote No, + // proposal is rejected and deposit is burned. + burnDepositNoThreshold := sdk.MustNewDecFromStr(params.BurnDepositNoThreshold) + noPercent := results[v1.OptionNo].Quo(activeVotingPower) + if noPercent.GT(burnDepositNoThreshold) { + return false, true, tallyResults + } + + // If less than `burnDepositNoThreshold` of non-abstaining voters vote No, + // proposal is rejected but deposit is not burned. return false, false, tallyResults } // HasReachedQuorum returns whether or not a proposal has reached quorum // this is just a stripped down version of the Tally function above -func (keeper Keeper) HasReachedQuorum(ctx sdk.Context, proposal v1.Proposal) (quorumPassed bool, err error) { +func (keeper Keeper) HasReachedQuorum(ctx sdk.Context, proposal v1.Proposal) bool { // If there is no staked coins, the proposal has not reached quorum totalBonded := keeper.sk.TotalBondedTokens(ctx) if totalBonded.IsZero() { - return false, nil + return false } /* DISABLED on AtomOne - no possible increase of computation speed by @@ -86,11 +95,8 @@ func (keeper Keeper) HasReachedQuorum(ctx sdk.Context, proposal v1.Proposal) (qu // check and return whether or not the proposal has reached quorum percentVoting := totalVotingPower.Quo(math.LegacyNewDecFromInt(totalBonded)) - quorum, _, err := keeper.getQuorumAndThreshold(ctx, proposal) - if err != nil { - return false, err - } - return percentVoting.GTE(quorum), nil + quorum, _ := keeper.getQuorumAndThreshold(ctx, proposal) + return percentVoting.GTE(quorum) } // getBondedValidatorsByAddress fetches all the bonded validators and return @@ -173,16 +179,10 @@ func (keeper Keeper) tallyVotes( // getQuorumAndThreshold iterates over the proposal's messages to returns the // appropriate quorum and threshold. -func (keeper Keeper) getQuorumAndThreshold(ctx sdk.Context, proposal v1.Proposal) (sdk.Dec, sdk.Dec, error) { +func (keeper Keeper) getQuorumAndThreshold(ctx sdk.Context, proposal v1.Proposal) (sdk.Dec, sdk.Dec) { params := keeper.GetParams(ctx) - quorum, err := sdk.NewDecFromStr(params.Quorum) - if err != nil { - return sdk.Dec{}, sdk.Dec{}, fmt.Errorf("parsing params.Quorum: %w", err) - } - threshold, err := sdk.NewDecFromStr(params.Threshold) - if err != nil { - return sdk.Dec{}, sdk.Dec{}, fmt.Errorf("parsing params.Threshold: %w", err) - } + quorum := sdk.MustNewDecFromStr(params.Quorum) + threshold := sdk.MustNewDecFromStr(params.Threshold) // Check if a proposal message is an ExecLegacyContent message if len(proposal.Messages) > 0 { @@ -193,32 +193,20 @@ func (keeper Keeper) getQuorumAndThreshold(ctx sdk.Context, proposal v1.Proposal // quorum and threshold accordingly switch sdkMsg.(type) { case *v1.MsgProposeConstitutionAmendment: - q, err := sdk.NewDecFromStr(params.ConstitutionAmendmentQuorum) - if err != nil { - return sdk.Dec{}, sdk.Dec{}, fmt.Errorf("parsing params.ConstitutionAmendmentQuorum: %w", err) - } + q := sdk.MustNewDecFromStr(params.ConstitutionAmendmentQuorum) if quorum.LT(q) { quorum = q } - t, err := sdk.NewDecFromStr(params.ConstitutionAmendmentThreshold) - if err != nil { - return sdk.Dec{}, sdk.Dec{}, fmt.Errorf("parsing params.ConstitutionAmendmentThreshold: %w", err) - } + t := sdk.MustNewDecFromStr(params.ConstitutionAmendmentThreshold) if threshold.LT(t) { threshold = t } case *v1.MsgProposeLaw: - q, err := sdk.NewDecFromStr(params.LawQuorum) - if err != nil { - return sdk.Dec{}, sdk.Dec{}, fmt.Errorf("parsing params.LawQuorum: %w", err) - } + q := sdk.MustNewDecFromStr(params.LawQuorum) if quorum.LT(q) { quorum = q } - t, err := sdk.NewDecFromStr(params.LawThreshold) - if err != nil { - return sdk.Dec{}, sdk.Dec{}, fmt.Errorf("parsing params.LawThreshold: %w", err) - } + t := sdk.MustNewDecFromStr(params.LawThreshold) if threshold.LT(t) { threshold = t } @@ -226,5 +214,5 @@ func (keeper Keeper) getQuorumAndThreshold(ctx sdk.Context, proposal v1.Proposal } } } - return quorum, threshold, nil + return quorum, threshold } diff --git a/x/gov/keeper/tally_test.go b/x/gov/keeper/tally_test.go index d1deed9d..db9f3672 100644 --- a/x/gov/keeper/tally_test.go +++ b/x/gov/keeper/tally_test.go @@ -271,24 +271,29 @@ func TestTally(t *testing.T) { }, }, { - name: "quorum reached with yes<=.667: prop fails", + name: "quorum reached with yes<=.667: prop rejected", setup: func(s *tallyFixture) { s.validatorVote(s.valAddrs[0], v1.VoteOption_VOTE_OPTION_YES) s.validatorVote(s.valAddrs[1], v1.VoteOption_VOTE_OPTION_YES) - s.validatorVote(s.valAddrs[2], v1.VoteOption_VOTE_OPTION_NO) - s.validatorVote(s.valAddrs[3], v1.VoteOption_VOTE_OPTION_NO) + s.validatorVote(s.valAddrs[2], v1.VoteOption_VOTE_OPTION_YES) + s.validatorVote(s.valAddrs[3], v1.VoteOption_VOTE_OPTION_YES) + s.validatorVote(s.valAddrs[4], v1.VoteOption_VOTE_OPTION_NO) + s.validatorVote(s.valAddrs[5], v1.VoteOption_VOTE_OPTION_NO) + s.validatorVote(s.valAddrs[6], v1.VoteOption_VOTE_OPTION_ABSTAIN) + s.validatorVote(s.valAddrs[7], v1.VoteOption_VOTE_OPTION_ABSTAIN) + s.validatorVote(s.valAddrs[8], v1.VoteOption_VOTE_OPTION_ABSTAIN) }, proposalMsgs: TestProposal, expectedPass: false, expectedBurn: false, expectedTally: v1.TallyResult{ - YesCount: "2", - AbstainCount: "0", + YesCount: "4", + AbstainCount: "3", NoCount: "2", }, }, { - name: "quorum reached with yes>.667: prop succeeds", + name: "quorum reached with yes>.667: prop passes", setup: func(s *tallyFixture) { s.validatorVote(s.valAddrs[0], v1.VoteOption_VOTE_OPTION_YES) s.validatorVote(s.valAddrs[1], v1.VoteOption_VOTE_OPTION_YES) @@ -296,18 +301,43 @@ func TestTally(t *testing.T) { s.validatorVote(s.valAddrs[3], v1.VoteOption_VOTE_OPTION_YES) s.validatorVote(s.valAddrs[4], v1.VoteOption_VOTE_OPTION_YES) s.validatorVote(s.valAddrs[5], v1.VoteOption_VOTE_OPTION_NO) + s.validatorVote(s.valAddrs[6], v1.VoteOption_VOTE_OPTION_ABSTAIN) + s.validatorVote(s.valAddrs[7], v1.VoteOption_VOTE_OPTION_ABSTAIN) + s.validatorVote(s.valAddrs[8], v1.VoteOption_VOTE_OPTION_ABSTAIN) }, proposalMsgs: TestProposal, expectedPass: true, expectedBurn: false, expectedTally: v1.TallyResult{ YesCount: "5", - AbstainCount: "0", + AbstainCount: "3", NoCount: "1", }, }, { - name: "quorum reached thanks to abstain, yes>.667: prop succeeds", + name: "quorum reached with no>.7: prop rejected and deposit burned", + setup: func(s *tallyFixture) { + s.validatorVote(s.valAddrs[0], v1.VoteOption_VOTE_OPTION_YES) + s.validatorVote(s.valAddrs[1], v1.VoteOption_VOTE_OPTION_NO) + s.validatorVote(s.valAddrs[2], v1.VoteOption_VOTE_OPTION_NO) + s.validatorVote(s.valAddrs[3], v1.VoteOption_VOTE_OPTION_NO) + s.validatorVote(s.valAddrs[4], v1.VoteOption_VOTE_OPTION_NO) + s.validatorVote(s.valAddrs[5], v1.VoteOption_VOTE_OPTION_NO) + s.validatorVote(s.valAddrs[6], v1.VoteOption_VOTE_OPTION_ABSTAIN) + s.validatorVote(s.valAddrs[7], v1.VoteOption_VOTE_OPTION_ABSTAIN) + s.validatorVote(s.valAddrs[8], v1.VoteOption_VOTE_OPTION_ABSTAIN) + }, + proposalMsgs: TestProposal, + expectedPass: false, + expectedBurn: true, + expectedTally: v1.TallyResult{ + YesCount: "1", + AbstainCount: "3", + NoCount: "5", + }, + }, + { + name: "quorum reached thanks to abstain, yes>.667: prop passes", setup: func(s *tallyFixture) { s.validatorVote(s.valAddrs[0], v1.VoteOption_VOTE_OPTION_YES) s.validatorVote(s.valAddrs[1], v1.VoteOption_VOTE_OPTION_YES) @@ -326,7 +356,7 @@ func TestTally(t *testing.T) { }, }, { - name: "amendment quorum not reached: prop fails", + name: "amendment quorum not reached: prop rejected", setup: func(s *tallyFixture) { s.validatorVote(s.valAddrs[0], v1.VoteOption_VOTE_OPTION_YES) s.validatorVote(s.valAddrs[1], v1.VoteOption_VOTE_OPTION_YES) @@ -341,7 +371,7 @@ func TestTally(t *testing.T) { }, }, { - name: "amendment quorum reached and threshold not reached: prop fails", + name: "amendment quorum reached and threshold not reached: prop rejected", setup: func(s *tallyFixture) { s.validatorVote(s.valAddrs[0], v1.VoteOption_VOTE_OPTION_YES) s.validatorVote(s.valAddrs[1], v1.VoteOption_VOTE_OPTION_YES) @@ -384,7 +414,7 @@ func TestTally(t *testing.T) { }, }, { - name: "law quorum not reached: prop fails", + name: "law quorum not reached: prop rejected", setup: func(s *tallyFixture) { s.validatorVote(s.valAddrs[0], v1.VoteOption_VOTE_OPTION_YES) s.validatorVote(s.valAddrs[2], v1.VoteOption_VOTE_OPTION_NO) @@ -399,7 +429,7 @@ func TestTally(t *testing.T) { }, }, { - name: "law quorum reached and threshold not reached: prop fails", + name: "law quorum reached and threshold not reached: prop rejected", setup: func(s *tallyFixture) { s.validatorVote(s.valAddrs[0], v1.VoteOption_VOTE_OPTION_YES) s.validatorVote(s.valAddrs[1], v1.VoteOption_VOTE_OPTION_YES) @@ -572,9 +602,8 @@ func TestHasReachedQuorum(t *testing.T) { suite := newTallyFixture(t, ctx, proposal, valAddrs, delAddrs, govKeeper, mocks) tt.setup(suite) - quorum, err := govKeeper.HasReachedQuorum(ctx, proposal) + quorum := govKeeper.HasReachedQuorum(ctx, proposal) - require.NoError(t, err) assert.Equal(t, tt.expectedQuorum, quorum) if tt.expectedQuorum { // Assert votes are still here after HasReachedQuorum diff --git a/x/gov/migrations/v5/store.go b/x/gov/migrations/v5/store.go index 3ce8d29f..034371a9 100644 --- a/x/gov/migrations/v5/store.go +++ b/x/gov/migrations/v5/store.go @@ -11,6 +11,7 @@ import ( var ParamsKey = []byte{0x30} // Addition of the dynamic-deposit parameters. +// Addition of the burnDepositNoThreshold parameter. func MigrateStore(ctx sdk.Context, storeKey storetypes.StoreKey, cdc codec.BinaryCodec) error { store := ctx.KVStore(storeKey) paramsBz := store.Get(ParamsKey) @@ -21,6 +22,7 @@ func MigrateStore(ctx sdk.Context, storeKey storetypes.StoreKey, cdc codec.Binar defaultParams := govv1.DefaultParams() params.MinDepositThrottler = defaultParams.MinDepositThrottler params.MinInitialDepositThrottler = defaultParams.MinInitialDepositThrottler + params.BurnDepositNoThreshold = defaultParams.BurnDepositNoThreshold bz, err := cdc.Marshal(¶ms) if err != nil { diff --git a/x/gov/migrations/v5/store_test.go b/x/gov/migrations/v5/store_test.go index fd571688..e37b6cbe 100644 --- a/x/gov/migrations/v5/store_test.go +++ b/x/gov/migrations/v5/store_test.go @@ -27,6 +27,7 @@ func TestMigrateStore(t *testing.T) { require.NotNil(t, params) require.Nil(t, params.MinDepositThrottler) require.Nil(t, params.MinInitialDepositThrottler) + require.Equal(t, "", params.BurnDepositNoThreshold) // Run migrations. err := v5.MigrateStore(ctx, govKey, cdc) @@ -38,4 +39,5 @@ func TestMigrateStore(t *testing.T) { require.NotNil(t, params) require.Equal(t, govv1.DefaultParams().MinDepositThrottler, params.MinDepositThrottler) require.Equal(t, govv1.DefaultParams().MinInitialDepositThrottler, params.MinInitialDepositThrottler) + require.Equal(t, govv1.DefaultParams().BurnDepositNoThreshold, params.BurnDepositNoThreshold) } diff --git a/x/gov/simulation/genesis.go b/x/gov/simulation/genesis.go index 809fe372..913a2103 100644 --- a/x/gov/simulation/genesis.go +++ b/x/gov/simulation/genesis.go @@ -48,6 +48,7 @@ const ( QuorumTimeout = "quorum_timeout" MaxVotingPeriodExtension = "max_voting_period_extension" QuorumCheckCount = "quorum_check_count" + BurnDepositNoThreshold = "burn_deposit_no_threshold" ) // GenDepositParamsDepositPeriod returns randomized DepositParamsDepositPeriod @@ -85,13 +86,13 @@ func GenMinDepositRatio(r *rand.Rand) math.LegacyDec { return math.LegacyMustNewDecFromStr("0.01") } -// GenTallyParamsQuorum returns randomized TallyParamsQuorum +// GenTallyParamsQuorum returns randomized TallyParamsConstitutionQuorum func GenTallyParamsConstitutionalQuorum(r *rand.Rand, minDec sdk.Dec) math.LegacyDec { min := int(minDec.Mul(sdk.NewDec(1000)).RoundInt64()) return sdk.NewDecWithPrec(int64(simulation.RandIntBetween(r, min, 600)), 3) } -// GenTallyParamsThreshold returns randomized TallyParamsThreshold +// GenTallyParamsThreshold returns randomized TallyParamsConstitutionalThreshold func GenTallyParamsConstitutionalThreshold(r *rand.Rand, minDec sdk.Dec) math.LegacyDec { min := int(minDec.Mul(sdk.NewDec(1000)).RoundInt64()) return sdk.NewDecWithPrec(int64(simulation.RandIntBetween(r, min, 950)), 3) @@ -152,6 +153,11 @@ func GenDepositParamsMinInitialDepositTargetProposals(r *rand.Rand) uint64 { return uint64(simulation.RandIntBetween(r, 1, 100)) } +// GenBurnDepositNoThreshold returns a randomized BurnDepositNoThreshold between 0.5 and 0.95 +func GenBurnDepositNoThreshold(r *rand.Rand) math.LegacyDec { + return sdk.NewDecWithPrec(int64(simulation.RandIntBetween(r, 500, 950)), 3) +} + // RandomizedGenState generates a random GenesisState for gov func RandomizedGenState(simState *module.SimulationState) { startingProposalID := uint64(simState.Rand.Intn(100)) @@ -324,6 +330,12 @@ func RandomizedGenState(simState *module.SimulationState) { }, ) + var burnDepositNoThreshold sdk.Dec + simState.AppParams.GetOrGenerate( + simState.Cdc, BurnDepositNoThreshold, &burnDepositNoThreshold, simState.Rand, + func(r *rand.Rand) { burnDepositNoThreshold = GenBurnDepositNoThreshold(r) }, + ) + govGenesis := v1.NewGenesisState( startingProposalID, v1.NewParams(depositPeriod, votingPeriod, quorum.String(), threshold.String(), amendmentsQuorum.String(), @@ -333,7 +345,9 @@ func RandomizedGenState(simState *module.SimulationState) { minDepositSensitivityTargetDistance, minDepositIncreaseRatio.String(), minDepositDecreaseRatio.String(), targetActiveProposals, minInitialDepositFloor, minInitialDepositUpdatePeriod, minInitialDepositSensitivityTargetDistance, minInitialDepositIncreaseRatio.String(), - minInitialDepositDecreaseRatio.String(), minInitialDepositTargetProposals), + minInitialDepositDecreaseRatio.String(), minInitialDepositTargetProposals, + burnDepositNoThreshold.String(), + ), ) bz, err := json.MarshalIndent(&govGenesis, "", " ") diff --git a/x/gov/simulation/genesis_test.go b/x/gov/simulation/genesis_test.go index d9c0c6b7..0345f2d5 100644 --- a/x/gov/simulation/genesis_test.go +++ b/x/gov/simulation/genesis_test.go @@ -46,12 +46,13 @@ func TestRandomizedGenState(t *testing.T) { simState.Cdc.MustUnmarshalJSON(simState.GenState[types.ModuleName], &govGenesis) const ( - tallyQuorum = "0.294000000000000000" - tallyThreshold = "0.611000000000000000" - amendmentQuorum = "0.568000000000000000" - amendmentThreshold = "0.933000000000000000" - lawQuorum = "0.540000000000000000" - lawThreshold = "0.931000000000000000" + tallyQuorum = "0.294000000000000000" + tallyThreshold = "0.611000000000000000" + amendmentQuorum = "0.568000000000000000" + amendmentThreshold = "0.933000000000000000" + lawQuorum = "0.540000000000000000" + lawThreshold = "0.931000000000000000" + burnDepositNoThreshold = "0.758000000000000000" ) var ( @@ -72,6 +73,7 @@ func TestRandomizedGenState(t *testing.T) { require.Equal(t, "26h19m52s", govGenesis.Params.QuorumTimeout.String()) require.Equal(t, "120h29m51s", govGenesis.Params.MaxVotingPeriodExtension.String()) require.Equal(t, uint64(17), govGenesis.Params.QuorumCheckCount) + require.Equal(t, burnDepositNoThreshold, govGenesis.Params.BurnDepositNoThreshold) require.Equal(t, uint64(0x28), govGenesis.StartingProposalId) require.Equal(t, []*v1.Deposit{}, govGenesis.Deposits) require.Equal(t, []*v1.Vote{}, govGenesis.Votes) diff --git a/x/gov/types/v1/gov.pb.go b/x/gov/types/v1/gov.pb.go index 723329dc..0643cd67 100644 --- a/x/gov/types/v1/gov.pb.go +++ b/x/gov/types/v1/gov.pb.go @@ -1101,6 +1101,8 @@ type Params struct { QuorumCheckCount uint64 `protobuf:"varint,22,opt,name=quorum_check_count,json=quorumCheckCount,proto3" json:"quorum_check_count,omitempty"` MinDepositThrottler *MinDepositThrottler `protobuf:"bytes,23,opt,name=min_deposit_throttler,json=minDepositThrottler,proto3" json:"min_deposit_throttler,omitempty"` MinInitialDepositThrottler *MinInitialDepositThrottler `protobuf:"bytes,24,opt,name=min_initial_deposit_throttler,json=minInitialDepositThrottler,proto3" json:"min_initial_deposit_throttler,omitempty"` + // Minimum proportion of No Votes for a proposal deposit to be burnt. + BurnDepositNoThreshold string `protobuf:"bytes,25,opt,name=burn_deposit_no_threshold,json=burnDepositNoThreshold,proto3" json:"burn_deposit_no_threshold,omitempty"` } func (m *Params) Reset() { *m = Params{} } @@ -1264,6 +1266,13 @@ func (m *Params) GetMinInitialDepositThrottler() *MinInitialDepositThrottler { return nil } +func (m *Params) GetBurnDepositNoThreshold() string { + if m != nil { + return m.BurnDepositNoThreshold + } + return "" +} + func init() { proto.RegisterEnum("atomone.gov.v1.VoteOption", VoteOption_name, VoteOption_value) proto.RegisterEnum("atomone.gov.v1.ProposalStatus", ProposalStatus_name, ProposalStatus_value) @@ -1285,114 +1294,115 @@ func init() { func init() { proto.RegisterFile("atomone/gov/v1/gov.proto", fileDescriptor_ecf0f9950ff6986c) } var fileDescriptor_ecf0f9950ff6986c = []byte{ - // 1698 bytes of a gzipped FileDescriptorProto + // 1722 bytes of a gzipped FileDescriptorProto 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xd4, 0x58, 0xcd, 0x6f, 0x1b, 0xc7, 0x15, 0xd7, 0x8a, 0x1f, 0x92, 0x1e, 0x45, 0x6a, 0x35, 0x92, 0xed, 0x15, 0x15, 0x51, 0x2e, 0x5b, 0x04, 0x8e, 0x6b, 0x91, 0x95, 0x9d, 0xfa, 0x10, 0x04, 0x05, 0x28, 0x91, 0x76, 0xe9, 0xda, 0x22, 0xb3, 0x64, 0x94, 0xa6, 0x87, 0x2e, 0x86, 0xdc, 0x31, 0xb5, 0x08, 0x77, 0x87, 0xd9, 0x99, 0xa5, - 0xc5, 0x6b, 0x4e, 0x3d, 0xe6, 0x58, 0xf4, 0xd4, 0x63, 0x8f, 0x3d, 0x04, 0xe8, 0x1f, 0x50, 0x14, - 0xc8, 0xa9, 0x08, 0x72, 0x6a, 0x2f, 0x6e, 0x21, 0x1f, 0x0a, 0xe4, 0xde, 0x7b, 0xb1, 0x33, 0xb3, - 0xfc, 0xd2, 0x0a, 0xa2, 0x8c, 0x16, 0x68, 0x2e, 0x09, 0x77, 0xde, 0xef, 0xf7, 0xde, 0x9b, 0xf7, - 0x39, 0x16, 0x18, 0x98, 0x53, 0x97, 0x7a, 0xa4, 0xdc, 0xa3, 0xc3, 0xf2, 0xf0, 0x30, 0xfc, 0x5f, - 0x69, 0xe0, 0x53, 0x4e, 0x51, 0x4e, 0x49, 0x4a, 0xe1, 0xd1, 0xf0, 0x30, 0x5f, 0xe8, 0x52, 0xe6, - 0x52, 0x56, 0xee, 0x60, 0x46, 0xca, 0xc3, 0xc3, 0x0e, 0xe1, 0xf8, 0xb0, 0xdc, 0xa5, 0x8e, 0x27, - 0xf1, 0xf9, 0xed, 0x1e, 0xed, 0x51, 0xf1, 0xb3, 0x1c, 0xfe, 0x52, 0xa7, 0xfb, 0x3d, 0x4a, 0x7b, - 0x7d, 0x52, 0x16, 0x5f, 0x9d, 0xe0, 0x65, 0x99, 0x3b, 0x2e, 0x61, 0x1c, 0xbb, 0x03, 0x05, 0xd8, - 0x99, 0x07, 0x60, 0x6f, 0xa4, 0x44, 0x85, 0x79, 0x91, 0x1d, 0xf8, 0x98, 0x3b, 0x34, 0xb2, 0xb8, - 0x23, 0x3d, 0xb2, 0xa4, 0x51, 0xf9, 0xa1, 0x44, 0x9b, 0xd8, 0x75, 0x3c, 0x5a, 0x16, 0xff, 0x95, - 0x47, 0xc5, 0x01, 0xa0, 0x4f, 0x88, 0xd3, 0x3b, 0xe3, 0xc4, 0x3e, 0xa5, 0x9c, 0x34, 0x06, 0xa1, - 0x26, 0xf4, 0x10, 0xd2, 0x54, 0xfc, 0x32, 0xb4, 0xbb, 0xda, 0xbd, 0xdc, 0xc3, 0x7c, 0x69, 0xf6, - 0xda, 0xa5, 0x09, 0xd6, 0x54, 0x48, 0xf4, 0x2e, 0xa4, 0x5f, 0x09, 0x4d, 0xc6, 0xf2, 0x5d, 0xed, - 0xde, 0xda, 0x51, 0xee, 0xdb, 0xaf, 0x0e, 0x40, 0x99, 0xaf, 0x92, 0xae, 0xa9, 0xa4, 0xc5, 0xdf, - 0x6b, 0xb0, 0x52, 0x25, 0x03, 0xca, 0x1c, 0x8e, 0xf6, 0x21, 0x33, 0xf0, 0xe9, 0x80, 0x32, 0xdc, - 0xb7, 0x1c, 0x5b, 0x18, 0x4b, 0x9a, 0x10, 0x1d, 0xd5, 0x6d, 0xf4, 0x18, 0xd6, 0x6c, 0x89, 0xa5, - 0xbe, 0xd2, 0x6b, 0x7c, 0xfb, 0xd5, 0xc1, 0xb6, 0xd2, 0x5b, 0xb1, 0x6d, 0x9f, 0x30, 0xd6, 0xe2, - 0xbe, 0xe3, 0xf5, 0xcc, 0x09, 0x14, 0x7d, 0x08, 0x69, 0xec, 0xd2, 0xc0, 0xe3, 0x46, 0xe2, 0x6e, - 0xe2, 0x5e, 0xe6, 0xe1, 0x4e, 0x49, 0x31, 0xc2, 0x3c, 0x95, 0x54, 0x9e, 0x4a, 0xc7, 0xd4, 0xf1, - 0x8e, 0xd6, 0xbe, 0x7e, 0xbd, 0xbf, 0xf4, 0x87, 0x7f, 0xfd, 0xf1, 0xbe, 0x66, 0x2a, 0x4e, 0xf1, - 0x0b, 0x0d, 0x72, 0xcf, 0x31, 0xe3, 0x2f, 0x1c, 0x2f, 0xf2, 0xf4, 0x03, 0x48, 0x0d, 0x71, 0x3f, - 0x20, 0x86, 0x76, 0x03, 0x7d, 0x92, 0x82, 0xde, 0x87, 0x64, 0x98, 0x5f, 0xe1, 0x7f, 0xe6, 0x61, - 0xbe, 0x24, 0x13, 0x58, 0x8a, 0x12, 0x58, 0x6a, 0x47, 0xc9, 0x3f, 0x4a, 0x7e, 0xf9, 0x8f, 0x7d, - 0xcd, 0x14, 0xe8, 0xe2, 0x9f, 0x53, 0xb0, 0xda, 0x54, 0x91, 0x40, 0x39, 0x58, 0x1e, 0xc7, 0x67, - 0xd9, 0xb1, 0xd1, 0x4f, 0x60, 0xd5, 0x25, 0x8c, 0xe1, 0x1e, 0x61, 0xc6, 0xb2, 0xf0, 0x68, 0xfb, - 0x92, 0xda, 0x8a, 0x37, 0x32, 0xc7, 0x28, 0xf4, 0x18, 0xd2, 0x8c, 0x63, 0x1e, 0x30, 0x23, 0x21, - 0x52, 0x5a, 0x98, 0x4f, 0x69, 0x64, 0xab, 0x25, 0x50, 0xa6, 0x42, 0xa3, 0x3a, 0xa0, 0x97, 0x8e, - 0x87, 0xfb, 0x16, 0xc7, 0xfd, 0xfe, 0xc8, 0xf2, 0x09, 0x0b, 0xfa, 0xdc, 0x48, 0x8a, 0xab, 0xec, - 0xce, 0xeb, 0x68, 0x87, 0x18, 0x53, 0x40, 0x4c, 0x5d, 0xd0, 0xa6, 0x4e, 0x50, 0x05, 0x32, 0x2c, - 0xe8, 0xb8, 0x0e, 0xb7, 0x44, 0x38, 0x52, 0x0b, 0x86, 0x03, 0x24, 0x29, 0x3c, 0x46, 0xcf, 0x40, - 0x57, 0x49, 0xb6, 0x88, 0x67, 0x4b, 0x3d, 0xe9, 0x05, 0xf5, 0xe4, 0x14, 0xb3, 0xe6, 0xd9, 0x42, - 0x57, 0x1d, 0xb2, 0x9c, 0x72, 0xdc, 0xb7, 0xd4, 0xb9, 0xb1, 0x72, 0x83, 0xd4, 0xae, 0x0b, 0x6a, - 0x54, 0x1d, 0xcf, 0x61, 0x73, 0x48, 0xb9, 0xe3, 0xf5, 0x2c, 0xc6, 0xb1, 0xaf, 0xee, 0xb7, 0xba, - 0xa0, 0x5f, 0x1b, 0x92, 0xda, 0x0a, 0x99, 0xc2, 0xb1, 0x9f, 0x83, 0x3a, 0x9a, 0xdc, 0x71, 0x6d, - 0x41, 0x5d, 0x59, 0x49, 0x8c, 0xae, 0x98, 0x0f, 0xcb, 0x84, 0x63, 0x1b, 0x73, 0x6c, 0x40, 0xd8, - 0x3d, 0xe6, 0xf8, 0x1b, 0x6d, 0x43, 0x8a, 0x3b, 0xbc, 0x4f, 0x8c, 0x8c, 0x10, 0xc8, 0x0f, 0x64, - 0xc0, 0x0a, 0x0b, 0x5c, 0x17, 0xfb, 0x23, 0x63, 0x5d, 0x9c, 0x47, 0x9f, 0xe8, 0x7d, 0x58, 0x95, - 0x8d, 0x49, 0x7c, 0x23, 0x7b, 0x4d, 0x27, 0x8e, 0x91, 0xc5, 0xdf, 0x69, 0x90, 0x99, 0xae, 0x81, - 0x1f, 0xc3, 0xda, 0x88, 0x30, 0xab, 0x2b, 0x7a, 0x53, 0xbb, 0x34, 0x28, 0xea, 0x1e, 0x37, 0x57, - 0x47, 0x84, 0x1d, 0x87, 0x72, 0xf4, 0x08, 0xb2, 0xb8, 0xc3, 0x38, 0x76, 0x3c, 0x45, 0x58, 0x8e, - 0x25, 0xac, 0x2b, 0x90, 0x24, 0xbd, 0x07, 0xab, 0x1e, 0x55, 0xf8, 0x44, 0x2c, 0x7e, 0xc5, 0xa3, - 0x02, 0x5a, 0xfc, 0x93, 0x06, 0xc9, 0x70, 0x92, 0x5d, 0x3f, 0x87, 0x4a, 0x90, 0x1a, 0x52, 0x4e, - 0xae, 0x9f, 0x41, 0x12, 0x86, 0x3e, 0x84, 0x15, 0x39, 0x16, 0x99, 0x91, 0x14, 0x55, 0x55, 0x9c, - 0x6f, 0x95, 0xcb, 0x53, 0xd7, 0x8c, 0x28, 0x33, 0x69, 0x4b, 0xcd, 0xa6, 0xed, 0x59, 0x72, 0x35, - 0xa1, 0x27, 0x8b, 0x7f, 0xd1, 0xe0, 0xd6, 0x47, 0x01, 0xf5, 0x03, 0xf7, 0xf8, 0x8c, 0x74, 0x3f, - 0xfb, 0x28, 0x20, 0x01, 0xa9, 0x79, 0xdc, 0x1f, 0xa1, 0x26, 0x6c, 0x7d, 0x2e, 0x04, 0xa2, 0x70, - 0x68, 0xa0, 0x8a, 0x51, 0x5b, 0xb0, 0x80, 0x36, 0x25, 0xb9, 0x2d, 0xb9, 0xa2, 0x88, 0x1e, 0x00, - 0x52, 0x1a, 0xbb, 0xa1, 0xad, 0xa9, 0x54, 0x24, 0x4d, 0xfd, 0xf3, 0x89, 0x13, 0x32, 0xfc, 0x73, - 0x68, 0x66, 0xd9, 0xd4, 0x23, 0x22, 0x11, 0xb3, 0x68, 0x56, 0xa5, 0x1e, 0x29, 0xfe, 0x5d, 0x83, - 0xac, 0x6a, 0xa2, 0x26, 0xf6, 0xb1, 0xcb, 0xd0, 0xa7, 0x90, 0x71, 0x1d, 0x6f, 0xdc, 0x93, 0xd7, - 0x8e, 0xdb, 0xbd, 0xb0, 0x27, 0xbf, 0x7b, 0xbd, 0x7f, 0x6b, 0x8a, 0xf5, 0x80, 0xba, 0x0e, 0x27, - 0xee, 0x80, 0x8f, 0x4c, 0x70, 0x27, 0x33, 0xdc, 0x05, 0xe4, 0xe2, 0xf3, 0x08, 0x64, 0x0d, 0x88, - 0xef, 0x50, 0x5b, 0x4d, 0xe5, 0x9d, 0x4b, 0x91, 0xa9, 0xaa, 0xb5, 0x7a, 0xf4, 0xa3, 0xef, 0x5e, - 0xef, 0xbf, 0x73, 0x99, 0x38, 0x31, 0xf2, 0xdb, 0x30, 0x70, 0xba, 0x8b, 0xcf, 0xa3, 0x9b, 0x08, - 0x79, 0xb1, 0x0d, 0xeb, 0xa7, 0xa2, 0x1b, 0xd5, 0xcd, 0xaa, 0xa0, 0xba, 0x33, 0xb2, 0xac, 0x5d, - 0x67, 0x39, 0x29, 0x34, 0xaf, 0x4b, 0x96, 0xd2, 0xfa, 0xef, 0x65, 0xd5, 0x50, 0x4a, 0xeb, 0xbb, - 0x90, 0x96, 0x51, 0x8d, 0xe9, 0x26, 0xb1, 0x76, 0xa5, 0x14, 0x3d, 0x80, 0x35, 0x7e, 0xe6, 0x13, - 0x76, 0x46, 0xfb, 0xf6, 0x15, 0x1b, 0x7a, 0x02, 0x40, 0x26, 0xec, 0x75, 0xa9, 0xc7, 0xb8, 0xc3, - 0x83, 0xd0, 0x13, 0x0b, 0xbb, 0xc4, 0xb3, 0x5d, 0xe2, 0x71, 0x4b, 0x19, 0x4b, 0xc4, 0x6a, 0xd8, - 0x9d, 0x26, 0x55, 0x22, 0x8e, 0x2c, 0x54, 0xf4, 0x4b, 0xb8, 0x7b, 0x85, 0xce, 0x89, 0x63, 0xc9, - 0x58, 0xb5, 0x85, 0x58, 0xb5, 0xed, 0xb1, 0xb7, 0x07, 0x00, 0x7d, 0xfc, 0x2a, 0x72, 0x2d, 0x15, - 0x7f, 0xb9, 0x3e, 0x7e, 0xa5, 0x1c, 0x79, 0x04, 0xd9, 0x10, 0x3e, 0xb1, 0x9a, 0x8e, 0x65, 0xac, - 0xf7, 0xf1, 0xab, 0xb1, 0x8d, 0xe2, 0x6f, 0x12, 0xb0, 0x35, 0x79, 0x0f, 0xb4, 0xcf, 0x7c, 0xca, - 0x79, 0x9f, 0xf8, 0xa8, 0x06, 0x99, 0x97, 0x7d, 0x4a, 0x7d, 0xeb, 0xe6, 0xcf, 0x03, 0x10, 0xc4, - 0x53, 0xf1, 0x46, 0xa8, 0x42, 0x36, 0x18, 0xd8, 0x98, 0x93, 0x85, 0xcb, 0x52, 0x15, 0x87, 0x64, - 0xc9, 0xe2, 0x40, 0x8f, 0xe1, 0x0e, 0xc7, 0x7e, 0x8f, 0x70, 0x0b, 0x77, 0xb9, 0x33, 0x24, 0x56, - 0x34, 0xc2, 0x98, 0xea, 0xc0, 0x5b, 0x52, 0x5c, 0x11, 0xd2, 0x68, 0xe3, 0x33, 0xf4, 0x53, 0xc8, - 0x39, 0x5e, 0xd7, 0x27, 0x98, 0x11, 0x4b, 0xa8, 0xbf, 0x22, 0x11, 0xd9, 0x08, 0x65, 0x86, 0xa0, - 0x90, 0x66, 0x93, 0x19, 0x5a, 0x7c, 0xec, 0xb3, 0x11, 0x4a, 0xd2, 0x7e, 0x06, 0xbb, 0x8c, 0x78, - 0xcc, 0xe1, 0xce, 0xd0, 0xe1, 0x23, 0x4b, 0x79, 0x6c, 0x3b, 0x8c, 0x63, 0xaf, 0x2b, 0xf7, 0x79, - 0xd2, 0xdc, 0x99, 0x82, 0xb4, 0x05, 0xa2, 0xaa, 0x00, 0xc5, 0x2f, 0x12, 0x90, 0x7f, 0xe1, 0x78, - 0x75, 0xcf, 0xe1, 0xce, 0x78, 0x07, 0xff, 0x9f, 0x66, 0xe4, 0x3d, 0xd0, 0xd5, 0xfd, 0xe6, 0x53, - 0xb1, 0x21, 0xcf, 0xbf, 0xaf, 0x49, 0xb8, 0x58, 0x83, 0xb4, 0x1a, 0x41, 0x4f, 0x6f, 0x38, 0xb2, - 0x33, 0xe3, 0x80, 0x1b, 0xda, 0xcc, 0x80, 0x7e, 0xf1, 0x76, 0x03, 0x3a, 0x19, 0x3f, 0x80, 0x2f, - 0x0f, 0xdc, 0xc4, 0x5b, 0x0c, 0xdc, 0xa9, 0x01, 0x9b, 0x5c, 0x7c, 0xc0, 0xa6, 0xae, 0x1b, 0xb0, - 0xbf, 0x80, 0x9d, 0x30, 0x66, 0x8e, 0xac, 0xe1, 0xf1, 0x95, 0x65, 0x02, 0x57, 0x04, 0x5b, 0x9f, - 0x65, 0x1b, 0x9a, 0x79, 0xdb, 0x9d, 0xaf, 0x7a, 0x99, 0xcb, 0x7b, 0xa0, 0x77, 0x02, 0xdf, 0xb3, - 0xc2, 0xb7, 0x47, 0x34, 0x05, 0xc3, 0x27, 0xda, 0xaa, 0x99, 0x0b, 0xcf, 0xc3, 0x27, 0x86, 0x1a, - 0x7d, 0x15, 0xd8, 0x13, 0xc8, 0xf1, 0x6b, 0x67, 0x1c, 0x6b, 0x9f, 0x84, 0x6c, 0x23, 0x27, 0x68, - 0xf9, 0x10, 0x14, 0x55, 0x66, 0x14, 0x54, 0x89, 0x40, 0x1f, 0xc0, 0xe6, 0x54, 0xb6, 0x95, 0xc7, - 0x1b, 0xb1, 0xf7, 0xdd, 0x98, 0xe4, 0x56, 0x3a, 0x7a, 0xed, 0x5a, 0xd1, 0xff, 0x37, 0x6b, 0x65, - 0xf3, 0xbf, 0xb0, 0x56, 0xd0, 0x8d, 0xd7, 0xca, 0xd6, 0xf5, 0x6b, 0x05, 0x3d, 0x81, 0xdc, 0xec, - 0x73, 0xcd, 0xd8, 0x5e, 0xac, 0x48, 0xb3, 0x33, 0x0f, 0x35, 0xf4, 0x6b, 0xd8, 0x0d, 0x5b, 0x67, - 0xa6, 0xde, 0x2d, 0x72, 0xce, 0xc3, 0xfe, 0xa5, 0x9e, 0x71, 0x6b, 0x31, 0xa5, 0x86, 0x8b, 0xcf, - 0x4f, 0xa7, 0x8a, 0xbf, 0x16, 0x29, 0xb8, 0xe2, 0x11, 0x78, 0xfb, 0x8a, 0x47, 0xe0, 0x27, 0x30, - 0xfd, 0x1c, 0x0b, 0x43, 0x22, 0x67, 0xb3, 0x71, 0x47, 0xf8, 0xf1, 0xc3, 0xf9, 0xc7, 0x70, 0xcc, - 0x62, 0x35, 0xb7, 0xdc, 0x98, 0x6d, 0xeb, 0xc2, 0x5e, 0x5c, 0xdb, 0x4c, 0x0c, 0x18, 0xc2, 0xc0, - 0xfd, 0x18, 0x03, 0x57, 0xac, 0x0b, 0x33, 0xef, 0x5e, 0x29, 0xbb, 0xff, 0x19, 0xc0, 0xd4, 0x5f, - 0x45, 0x76, 0xe1, 0xce, 0x69, 0xa3, 0x5d, 0xb3, 0x1a, 0xcd, 0x76, 0xbd, 0x71, 0x62, 0x7d, 0x7c, - 0xd2, 0x6a, 0xd6, 0x8e, 0xeb, 0x4f, 0xea, 0xb5, 0xaa, 0xbe, 0x84, 0xb6, 0x60, 0x63, 0x5a, 0xf8, - 0x69, 0xad, 0xa5, 0x6b, 0xe8, 0x0e, 0x6c, 0x4d, 0x1f, 0x56, 0x8e, 0x5a, 0xed, 0x4a, 0xfd, 0x44, - 0x5f, 0x46, 0x08, 0x72, 0xd3, 0x82, 0x93, 0x86, 0x9e, 0xb8, 0xff, 0x57, 0x0d, 0x72, 0xb3, 0xff, - 0x08, 0x47, 0xfb, 0xb0, 0xdb, 0x34, 0x1b, 0xcd, 0x46, 0xab, 0xf2, 0xdc, 0x6a, 0xb5, 0x2b, 0xed, - 0x8f, 0x5b, 0x73, 0x56, 0x8b, 0x50, 0x98, 0x07, 0x54, 0x6b, 0xcd, 0x46, 0xab, 0xde, 0xb6, 0x9a, - 0x35, 0xb3, 0xde, 0xa8, 0xea, 0x1a, 0xfa, 0x01, 0xec, 0xcd, 0x63, 0x4e, 0x1b, 0xed, 0xfa, 0xc9, - 0xd3, 0x08, 0xb2, 0x8c, 0xf2, 0x70, 0x7b, 0x1e, 0xd2, 0xac, 0xb4, 0x5a, 0xb5, 0xaa, 0x9e, 0x40, - 0xef, 0x80, 0x31, 0x2f, 0x33, 0x6b, 0xcf, 0x6a, 0xc7, 0xed, 0x5a, 0x55, 0x4f, 0xc6, 0x31, 0x9f, - 0x54, 0xea, 0xcf, 0x6b, 0x55, 0x3d, 0x75, 0xf4, 0xf4, 0xeb, 0x8b, 0x82, 0xf6, 0xcd, 0x45, 0x41, - 0xfb, 0xe7, 0x45, 0x41, 0xfb, 0xf2, 0x4d, 0x61, 0xe9, 0x9b, 0x37, 0x85, 0xa5, 0xbf, 0xbd, 0x29, - 0x2c, 0xfd, 0xea, 0xa0, 0xe7, 0xf0, 0xb3, 0xa0, 0x53, 0xea, 0x52, 0xb7, 0xac, 0x32, 0x75, 0x70, - 0x16, 0x74, 0xa2, 0xdf, 0xe5, 0x73, 0xf1, 0x87, 0x37, 0x3e, 0x1a, 0x10, 0x56, 0x1e, 0x1e, 0x76, - 0xd2, 0xa2, 0x5e, 0x1f, 0xfd, 0x27, 0x00, 0x00, 0xff, 0xff, 0x62, 0x1f, 0xc8, 0x86, 0x97, 0x13, - 0x00, 0x00, + 0xc4, 0x6b, 0x4e, 0x3d, 0xe6, 0x58, 0xf4, 0xd4, 0x63, 0x2f, 0x05, 0x7a, 0x08, 0xd0, 0x3f, 0xa0, + 0x28, 0x90, 0x53, 0x11, 0xe4, 0xd4, 0x5e, 0xdc, 0xc2, 0x3e, 0x14, 0xc8, 0xbd, 0xf7, 0x62, 0x67, + 0x66, 0xf9, 0xa5, 0x15, 0x44, 0x19, 0x2d, 0xd0, 0x5e, 0x12, 0xee, 0xbc, 0xdf, 0xef, 0xbd, 0x37, + 0xef, 0x73, 0x2c, 0x30, 0x30, 0xa7, 0x2e, 0xf5, 0x48, 0xb9, 0x47, 0x87, 0xe5, 0xe1, 0x61, 0xf8, + 0xbf, 0xd2, 0xc0, 0xa7, 0x9c, 0xa2, 0x9c, 0x92, 0x94, 0xc2, 0xa3, 0xe1, 0x61, 0xbe, 0xd0, 0xa5, + 0xcc, 0xa5, 0xac, 0xdc, 0xc1, 0x8c, 0x94, 0x87, 0x87, 0x1d, 0xc2, 0xf1, 0x61, 0xb9, 0x4b, 0x1d, + 0x4f, 0xe2, 0xf3, 0xdb, 0x3d, 0xda, 0xa3, 0xe2, 0x67, 0x39, 0xfc, 0xa5, 0x4e, 0xf7, 0x7b, 0x94, + 0xf6, 0xfa, 0xa4, 0x2c, 0xbe, 0x3a, 0xc1, 0xcb, 0x32, 0x77, 0x5c, 0xc2, 0x38, 0x76, 0x07, 0x0a, + 0xb0, 0x33, 0x0f, 0xc0, 0xde, 0x48, 0x89, 0x0a, 0xf3, 0x22, 0x3b, 0xf0, 0x31, 0x77, 0x68, 0x64, + 0x71, 0x47, 0x7a, 0x64, 0x49, 0xa3, 0xf2, 0x43, 0x89, 0x36, 0xb1, 0xeb, 0x78, 0xb4, 0x2c, 0xfe, + 0x2b, 0x8f, 0x8a, 0x03, 0x40, 0x9f, 0x10, 0xa7, 0x77, 0xc6, 0x89, 0x7d, 0x4a, 0x39, 0x69, 0x0c, + 0x42, 0x4d, 0xe8, 0x21, 0xa4, 0xa9, 0xf8, 0x65, 0x68, 0x77, 0xb5, 0x7b, 0xb9, 0x87, 0xf9, 0xd2, + 0xec, 0xb5, 0x4b, 0x13, 0xac, 0xa9, 0x90, 0xe8, 0x5d, 0x48, 0x9f, 0x0b, 0x4d, 0xc6, 0xf2, 0x5d, + 0xed, 0xde, 0xda, 0x51, 0xee, 0xdb, 0xaf, 0x0e, 0x40, 0x99, 0xaf, 0x92, 0xae, 0xa9, 0xa4, 0xc5, + 0xdf, 0x6a, 0xb0, 0x52, 0x25, 0x03, 0xca, 0x1c, 0x8e, 0xf6, 0x21, 0x33, 0xf0, 0xe9, 0x80, 0x32, + 0xdc, 0xb7, 0x1c, 0x5b, 0x18, 0x4b, 0x9a, 0x10, 0x1d, 0xd5, 0x6d, 0xf4, 0x18, 0xd6, 0x6c, 0x89, + 0xa5, 0xbe, 0xd2, 0x6b, 0x7c, 0xfb, 0xd5, 0xc1, 0xb6, 0xd2, 0x5b, 0xb1, 0x6d, 0x9f, 0x30, 0xd6, + 0xe2, 0xbe, 0xe3, 0xf5, 0xcc, 0x09, 0x14, 0x7d, 0x08, 0x69, 0xec, 0xd2, 0xc0, 0xe3, 0x46, 0xe2, + 0x6e, 0xe2, 0x5e, 0xe6, 0xe1, 0x4e, 0x49, 0x31, 0xc2, 0x3c, 0x95, 0x54, 0x9e, 0x4a, 0xc7, 0xd4, + 0xf1, 0x8e, 0xd6, 0xbe, 0x7e, 0xb5, 0xbf, 0xf4, 0xbb, 0x7f, 0xfe, 0xe1, 0xbe, 0x66, 0x2a, 0x4e, + 0xf1, 0x0b, 0x0d, 0x72, 0xcf, 0x31, 0xe3, 0x2f, 0x1c, 0x2f, 0xf2, 0xf4, 0x03, 0x48, 0x0d, 0x71, + 0x3f, 0x20, 0x86, 0x76, 0x03, 0x7d, 0x92, 0x82, 0xde, 0x87, 0x64, 0x98, 0x5f, 0xe1, 0x7f, 0xe6, + 0x61, 0xbe, 0x24, 0x13, 0x58, 0x8a, 0x12, 0x58, 0x6a, 0x47, 0xc9, 0x3f, 0x4a, 0x7e, 0xf9, 0xf7, + 0x7d, 0xcd, 0x14, 0xe8, 0xe2, 0x9f, 0x52, 0xb0, 0xda, 0x54, 0x91, 0x40, 0x39, 0x58, 0x1e, 0xc7, + 0x67, 0xd9, 0xb1, 0xd1, 0x8f, 0x60, 0xd5, 0x25, 0x8c, 0xe1, 0x1e, 0x61, 0xc6, 0xb2, 0xf0, 0x68, + 0xfb, 0x92, 0xda, 0x8a, 0x37, 0x32, 0xc7, 0x28, 0xf4, 0x18, 0xd2, 0x8c, 0x63, 0x1e, 0x30, 0x23, + 0x21, 0x52, 0x5a, 0x98, 0x4f, 0x69, 0x64, 0xab, 0x25, 0x50, 0xa6, 0x42, 0xa3, 0x3a, 0xa0, 0x97, + 0x8e, 0x87, 0xfb, 0x16, 0xc7, 0xfd, 0xfe, 0xc8, 0xf2, 0x09, 0x0b, 0xfa, 0xdc, 0x48, 0x8a, 0xab, + 0xec, 0xce, 0xeb, 0x68, 0x87, 0x18, 0x53, 0x40, 0x4c, 0x5d, 0xd0, 0xa6, 0x4e, 0x50, 0x05, 0x32, + 0x2c, 0xe8, 0xb8, 0x0e, 0xb7, 0x44, 0x38, 0x52, 0x0b, 0x86, 0x03, 0x24, 0x29, 0x3c, 0x46, 0xcf, + 0x40, 0x57, 0x49, 0xb6, 0x88, 0x67, 0x4b, 0x3d, 0xe9, 0x05, 0xf5, 0xe4, 0x14, 0xb3, 0xe6, 0xd9, + 0x42, 0x57, 0x1d, 0xb2, 0x9c, 0x72, 0xdc, 0xb7, 0xd4, 0xb9, 0xb1, 0x72, 0x83, 0xd4, 0xae, 0x0b, + 0x6a, 0x54, 0x1d, 0xcf, 0x61, 0x73, 0x48, 0xb9, 0xe3, 0xf5, 0x2c, 0xc6, 0xb1, 0xaf, 0xee, 0xb7, + 0xba, 0xa0, 0x5f, 0x1b, 0x92, 0xda, 0x0a, 0x99, 0xc2, 0xb1, 0x9f, 0x82, 0x3a, 0x9a, 0xdc, 0x71, + 0x6d, 0x41, 0x5d, 0x59, 0x49, 0x8c, 0xae, 0x98, 0x0f, 0xcb, 0x84, 0x63, 0x1b, 0x73, 0x6c, 0x40, + 0xd8, 0x3d, 0xe6, 0xf8, 0x1b, 0x6d, 0x43, 0x8a, 0x3b, 0xbc, 0x4f, 0x8c, 0x8c, 0x10, 0xc8, 0x0f, + 0x64, 0xc0, 0x0a, 0x0b, 0x5c, 0x17, 0xfb, 0x23, 0x63, 0x5d, 0x9c, 0x47, 0x9f, 0xe8, 0x7d, 0x58, + 0x95, 0x8d, 0x49, 0x7c, 0x23, 0x7b, 0x4d, 0x27, 0x8e, 0x91, 0xc5, 0xdf, 0x68, 0x90, 0x99, 0xae, + 0x81, 0x1f, 0xc2, 0xda, 0x88, 0x30, 0xab, 0x2b, 0x7a, 0x53, 0xbb, 0x34, 0x28, 0xea, 0x1e, 0x37, + 0x57, 0x47, 0x84, 0x1d, 0x87, 0x72, 0xf4, 0x08, 0xb2, 0xb8, 0xc3, 0x38, 0x76, 0x3c, 0x45, 0x58, + 0x8e, 0x25, 0xac, 0x2b, 0x90, 0x24, 0xbd, 0x07, 0xab, 0x1e, 0x55, 0xf8, 0x44, 0x2c, 0x7e, 0xc5, + 0xa3, 0x02, 0x5a, 0xfc, 0xa3, 0x06, 0xc9, 0x70, 0x92, 0x5d, 0x3f, 0x87, 0x4a, 0x90, 0x1a, 0x52, + 0x4e, 0xae, 0x9f, 0x41, 0x12, 0x86, 0x3e, 0x84, 0x15, 0x39, 0x16, 0x99, 0x91, 0x14, 0x55, 0x55, + 0x9c, 0x6f, 0x95, 0xcb, 0x53, 0xd7, 0x8c, 0x28, 0x33, 0x69, 0x4b, 0xcd, 0xa6, 0xed, 0x59, 0x72, + 0x35, 0xa1, 0x27, 0x8b, 0x7f, 0xd6, 0xe0, 0xd6, 0x47, 0x01, 0xf5, 0x03, 0xf7, 0xf8, 0x8c, 0x74, + 0x3f, 0xfb, 0x28, 0x20, 0x01, 0xa9, 0x79, 0xdc, 0x1f, 0xa1, 0x26, 0x6c, 0x7d, 0x2e, 0x04, 0xa2, + 0x70, 0x68, 0xa0, 0x8a, 0x51, 0x5b, 0xb0, 0x80, 0x36, 0x25, 0xb9, 0x2d, 0xb9, 0xa2, 0x88, 0x1e, + 0x00, 0x52, 0x1a, 0xbb, 0xa1, 0xad, 0xa9, 0x54, 0x24, 0x4d, 0xfd, 0xf3, 0x89, 0x13, 0x32, 0xfc, + 0x73, 0x68, 0x66, 0xd9, 0xd4, 0x23, 0x22, 0x11, 0xb3, 0x68, 0x56, 0xa5, 0x1e, 0x29, 0xfe, 0x4d, + 0x83, 0xac, 0x6a, 0xa2, 0x26, 0xf6, 0xb1, 0xcb, 0xd0, 0xa7, 0x90, 0x71, 0x1d, 0x6f, 0xdc, 0x93, + 0xd7, 0x8e, 0xdb, 0xbd, 0xb0, 0x27, 0xbf, 0x7b, 0xb5, 0x7f, 0x6b, 0x8a, 0xf5, 0x80, 0xba, 0x0e, + 0x27, 0xee, 0x80, 0x8f, 0x4c, 0x70, 0x27, 0x33, 0xdc, 0x05, 0xe4, 0xe2, 0x8b, 0x08, 0x64, 0x0d, + 0x88, 0xef, 0x50, 0x5b, 0x4d, 0xe5, 0x9d, 0x4b, 0x91, 0xa9, 0xaa, 0xb5, 0x7a, 0xf4, 0x83, 0xef, + 0x5e, 0xed, 0xbf, 0x73, 0x99, 0x38, 0x31, 0xf2, 0xeb, 0x30, 0x70, 0xba, 0x8b, 0x2f, 0xa2, 0x9b, + 0x08, 0x79, 0xb1, 0x0d, 0xeb, 0xa7, 0xa2, 0x1b, 0xd5, 0xcd, 0xaa, 0xa0, 0xba, 0x33, 0xb2, 0xac, + 0x5d, 0x67, 0x39, 0x29, 0x34, 0xaf, 0x4b, 0x96, 0xd2, 0xfa, 0xaf, 0x65, 0xd5, 0x50, 0x4a, 0xeb, + 0xbb, 0x90, 0x96, 0x51, 0x8d, 0xe9, 0x26, 0xb1, 0x76, 0xa5, 0x14, 0x3d, 0x80, 0x35, 0x7e, 0xe6, + 0x13, 0x76, 0x46, 0xfb, 0xf6, 0x15, 0x1b, 0x7a, 0x02, 0x40, 0x26, 0xec, 0x75, 0xa9, 0xc7, 0xb8, + 0xc3, 0x83, 0xd0, 0x13, 0x0b, 0xbb, 0xc4, 0xb3, 0x5d, 0xe2, 0x71, 0x4b, 0x19, 0x4b, 0xc4, 0x6a, + 0xd8, 0x9d, 0x26, 0x55, 0x22, 0x8e, 0x2c, 0x54, 0xf4, 0x73, 0xb8, 0x7b, 0x85, 0xce, 0x89, 0x63, + 0xc9, 0x58, 0xb5, 0x85, 0x58, 0xb5, 0xed, 0xb1, 0xb7, 0x07, 0x00, 0x7d, 0x7c, 0x1e, 0xb9, 0x96, + 0x8a, 0xbf, 0x5c, 0x1f, 0x9f, 0x2b, 0x47, 0x1e, 0x41, 0x36, 0x84, 0x4f, 0xac, 0xa6, 0x63, 0x19, + 0xeb, 0x7d, 0x7c, 0x3e, 0xb6, 0x51, 0xfc, 0x55, 0x02, 0xb6, 0x26, 0xef, 0x81, 0xf6, 0x99, 0x4f, + 0x39, 0xef, 0x13, 0x1f, 0xd5, 0x20, 0xf3, 0xb2, 0x4f, 0xa9, 0x6f, 0xdd, 0xfc, 0x79, 0x00, 0x82, + 0x78, 0x2a, 0xde, 0x08, 0x55, 0xc8, 0x06, 0x03, 0x1b, 0x73, 0xb2, 0x70, 0x59, 0xaa, 0xe2, 0x90, + 0x2c, 0x59, 0x1c, 0xe8, 0x31, 0xdc, 0xe1, 0xd8, 0xef, 0x11, 0x6e, 0xe1, 0x2e, 0x77, 0x86, 0xc4, + 0x8a, 0x46, 0x18, 0x53, 0x1d, 0x78, 0x4b, 0x8a, 0x2b, 0x42, 0x1a, 0x6d, 0x7c, 0x86, 0x7e, 0x0c, + 0x39, 0xc7, 0xeb, 0xfa, 0x04, 0x33, 0x62, 0x09, 0xf5, 0x57, 0x24, 0x22, 0x1b, 0xa1, 0xcc, 0x10, + 0x14, 0xd2, 0x6c, 0x32, 0x43, 0x8b, 0x8f, 0x7d, 0x36, 0x42, 0x49, 0xda, 0x4f, 0x60, 0x97, 0x11, + 0x8f, 0x39, 0xdc, 0x19, 0x3a, 0x7c, 0x64, 0x29, 0x8f, 0x6d, 0x87, 0x71, 0xec, 0x75, 0xe5, 0x3e, + 0x4f, 0x9a, 0x3b, 0x53, 0x90, 0xb6, 0x40, 0x54, 0x15, 0xa0, 0xf8, 0x45, 0x02, 0xf2, 0x2f, 0x1c, + 0xaf, 0xee, 0x39, 0xdc, 0x19, 0xef, 0xe0, 0xff, 0xd1, 0x8c, 0xbc, 0x07, 0xba, 0xba, 0xdf, 0x7c, + 0x2a, 0x36, 0xe4, 0xf9, 0xff, 0x6b, 0x12, 0x7e, 0x0f, 0x90, 0x56, 0x23, 0xe8, 0xe9, 0x0d, 0x47, + 0x76, 0x66, 0x1c, 0x70, 0x43, 0x9b, 0x19, 0xd0, 0x2f, 0xde, 0x6e, 0x40, 0x27, 0xe3, 0x07, 0xf0, + 0xe5, 0x81, 0x9b, 0x78, 0x8b, 0x81, 0x3b, 0x35, 0x60, 0x93, 0x8b, 0x0f, 0xd8, 0xd4, 0x75, 0x03, + 0xf6, 0x67, 0xb0, 0x13, 0xc6, 0xcc, 0x91, 0x35, 0x3c, 0xbe, 0xb2, 0x4c, 0xe0, 0x8a, 0x60, 0xeb, + 0xb3, 0x6c, 0x43, 0x33, 0x6f, 0xbb, 0xf3, 0x55, 0x2f, 0x73, 0x79, 0x0f, 0xf4, 0x4e, 0xe0, 0x7b, + 0x56, 0xf8, 0xf6, 0x88, 0xa6, 0x60, 0xf8, 0x44, 0x5b, 0x35, 0x73, 0xe1, 0x79, 0xf8, 0xc4, 0x50, + 0xa3, 0xaf, 0x02, 0x7b, 0x02, 0x39, 0x7e, 0xed, 0x8c, 0x63, 0xed, 0x93, 0x90, 0x6d, 0xe4, 0x04, + 0x2d, 0x1f, 0x82, 0xa2, 0xca, 0x8c, 0x82, 0x2a, 0x11, 0xe8, 0x03, 0xd8, 0x9c, 0xca, 0xb6, 0xf2, + 0x78, 0x23, 0xf6, 0xbe, 0x1b, 0x93, 0xdc, 0x4a, 0x47, 0xaf, 0x5d, 0x2b, 0xfa, 0x7f, 0x67, 0xad, + 0x6c, 0xfe, 0x07, 0xd6, 0x0a, 0xba, 0xf1, 0x5a, 0xd9, 0xba, 0x7e, 0xad, 0xa0, 0x27, 0x90, 0x9b, + 0x7d, 0xae, 0x19, 0xdb, 0x8b, 0x15, 0x69, 0x76, 0xe6, 0xa1, 0x86, 0x7e, 0x09, 0xbb, 0x61, 0xeb, + 0xcc, 0xd4, 0xbb, 0x45, 0x2e, 0x78, 0xd8, 0xbf, 0xd4, 0x33, 0x6e, 0x2d, 0xa6, 0xd4, 0x70, 0xf1, + 0xc5, 0xe9, 0x54, 0xf1, 0xd7, 0x22, 0x05, 0x57, 0x3c, 0x02, 0x6f, 0x5f, 0xf1, 0x08, 0xfc, 0x04, + 0xa6, 0x9f, 0x63, 0x61, 0x48, 0xe4, 0x6c, 0x36, 0xee, 0x08, 0x3f, 0xbe, 0x3f, 0xff, 0x18, 0x8e, + 0x59, 0xac, 0xe6, 0x96, 0x1b, 0xb3, 0x6d, 0x5d, 0xd8, 0x8b, 0x6b, 0x9b, 0x89, 0x01, 0x43, 0x18, + 0xb8, 0x1f, 0x63, 0xe0, 0x8a, 0x75, 0x61, 0xe6, 0xdd, 0xab, 0x57, 0x49, 0x1d, 0x76, 0x44, 0xbb, + 0x44, 0x76, 0x3c, 0x3a, 0x95, 0xde, 0x9d, 0xd8, 0xf4, 0xde, 0x0e, 0x09, 0x4a, 0xd1, 0x09, 0x1d, + 0x27, 0xfa, 0xfe, 0x67, 0x00, 0x53, 0x7f, 0x60, 0xd9, 0x85, 0x3b, 0xa7, 0x8d, 0x76, 0xcd, 0x6a, + 0x34, 0xdb, 0xf5, 0xc6, 0x89, 0xf5, 0xf1, 0x49, 0xab, 0x59, 0x3b, 0xae, 0x3f, 0xa9, 0xd7, 0xaa, + 0xfa, 0x12, 0xda, 0x82, 0x8d, 0x69, 0xe1, 0xa7, 0xb5, 0x96, 0xae, 0xa1, 0x3b, 0xb0, 0x35, 0x7d, + 0x58, 0x39, 0x6a, 0xb5, 0x2b, 0xf5, 0x13, 0x7d, 0x19, 0x21, 0xc8, 0x4d, 0x0b, 0x4e, 0x1a, 0x7a, + 0xe2, 0xfe, 0x5f, 0x34, 0xc8, 0xcd, 0xfe, 0x7b, 0x1e, 0xed, 0xc3, 0x6e, 0xd3, 0x6c, 0x34, 0x1b, + 0xad, 0xca, 0x73, 0xab, 0xd5, 0xae, 0xb4, 0x3f, 0x6e, 0xcd, 0x59, 0x2d, 0x42, 0x61, 0x1e, 0x50, + 0xad, 0x35, 0x1b, 0xad, 0x7a, 0xdb, 0x6a, 0xd6, 0xcc, 0x7a, 0xa3, 0xaa, 0x6b, 0xe8, 0x7b, 0xb0, + 0x37, 0x8f, 0x39, 0x6d, 0xb4, 0xeb, 0x27, 0x4f, 0x23, 0xc8, 0x32, 0xca, 0xc3, 0xed, 0x79, 0x48, + 0xb3, 0xd2, 0x6a, 0xd5, 0xaa, 0x7a, 0x02, 0xbd, 0x03, 0xc6, 0xbc, 0xcc, 0xac, 0x3d, 0xab, 0x1d, + 0xb7, 0x6b, 0x55, 0x3d, 0x19, 0xc7, 0x7c, 0x52, 0xa9, 0x3f, 0xaf, 0x55, 0xf5, 0xd4, 0xd1, 0xd3, + 0xaf, 0x5f, 0x17, 0xb4, 0x6f, 0x5e, 0x17, 0xb4, 0x7f, 0xbc, 0x2e, 0x68, 0x5f, 0xbe, 0x29, 0x2c, + 0x7d, 0xf3, 0xa6, 0xb0, 0xf4, 0xd7, 0x37, 0x85, 0xa5, 0x5f, 0x1c, 0xf4, 0x1c, 0x7e, 0x16, 0x74, + 0x4a, 0x5d, 0xea, 0x96, 0x55, 0xd2, 0x0f, 0xce, 0x82, 0x4e, 0xf4, 0xbb, 0x7c, 0x21, 0xfe, 0x86, + 0xc7, 0x47, 0x03, 0xc2, 0xca, 0xc3, 0xc3, 0x4e, 0x5a, 0x94, 0xfe, 0xa3, 0x7f, 0x07, 0x00, 0x00, + 0xff, 0xff, 0xb5, 0x70, 0xbf, 0x41, 0xe2, 0x13, 0x00, 0x00, } func (m *WeightedVoteOption) Marshal() (dAtA []byte, err error) { @@ -2117,6 +2127,15 @@ func (m *Params) MarshalToSizedBuffer(dAtA []byte) (int, error) { _ = i var l int _ = l + if len(m.BurnDepositNoThreshold) > 0 { + i -= len(m.BurnDepositNoThreshold) + copy(dAtA[i:], m.BurnDepositNoThreshold) + i = encodeVarintGov(dAtA, i, uint64(len(m.BurnDepositNoThreshold))) + i-- + dAtA[i] = 0x1 + i-- + dAtA[i] = 0xca + } if m.MinInitialDepositThrottler != nil { { size, err := m.MinInitialDepositThrottler.MarshalToSizedBuffer(dAtA[:i]) @@ -2702,6 +2721,10 @@ func (m *Params) Size() (n int) { l = m.MinInitialDepositThrottler.Size() n += 2 + l + sovGov(uint64(l)) } + l = len(m.BurnDepositNoThreshold) + if l > 0 { + n += 2 + l + sovGov(uint64(l)) + } return n } @@ -5454,6 +5477,38 @@ func (m *Params) Unmarshal(dAtA []byte) error { return err } iNdEx = postIndex + case 25: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field BurnDepositNoThreshold", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGov + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthGov + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthGov + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.BurnDepositNoThreshold = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipGov(dAtA[iNdEx:]) diff --git a/x/gov/types/v1/params.go b/x/gov/types/v1/params.go index 4db865d2..3cbe6980 100644 --- a/x/gov/types/v1/params.go +++ b/x/gov/types/v1/params.go @@ -57,6 +57,7 @@ var ( DefaultMinInitialDepositIncreaseRatio = sdk.NewDecWithPrec(1, 2) DefaultMinInitialDepositDecreaseRatio = sdk.NewDecWithPrec(5, 3) DefaultTargetProposalsInDepositPeriod uint64 = 5 + DefaultBurnDepositNoThreshold = sdk.NewDecWithPrec(80, 2) ) // Deprecated: NewDepositParams creates a new DepositParams object @@ -94,6 +95,7 @@ func NewParams( minDepositIncreaseRatio, minDepositDecreaseRatio string, targetActiveProposals uint64, minInitialDepositFloor sdk.Coins, minInitialDepositUpdatePeriod time.Duration, minInitialDepositSensitivityTargetDistance uint64, minInitialDepositIncreaseRatio, minInitialDepositDecreaseRatio string, targetProposalsInDepositPeriod uint64, + burnDepositNoThreshold string, ) Params { return Params{ // MinDeposit: minDeposit, // Deprecated in favor of dynamic min deposit @@ -128,6 +130,7 @@ func NewParams( DecreaseRatio: minInitialDepositDecreaseRatio, TargetProposals: targetProposalsInDepositPeriod, }, + BurnDepositNoThreshold: burnDepositNoThreshold, } } @@ -162,6 +165,7 @@ func DefaultParams() Params { DefaultMinInitialDepositIncreaseRatio.String(), DefaultMinInitialDepositDecreaseRatio.String(), DefaultTargetProposalsInDepositPeriod, + DefaultBurnDepositNoThreshold.String(), ) } @@ -419,5 +423,22 @@ func (p Params) ValidateBasic() error { return fmt.Errorf("minimum initial deposit decrease ratio too large: %s", minInitialDepositDecreaseRatio) } + burnDepositNoThreshold, err := sdk.NewDecFromStr(p.BurnDepositNoThreshold) + if err != nil { + return fmt.Errorf("invalid burnDepositNoThreshold string: %w", err) + } + if burnDepositNoThreshold.LT(math.LegacyOneDec().Sub(amendmentThreshold)) { + return fmt.Errorf("burnDepositNoThreshold cannot be lower than 1-amendmentThreshold") + } + if burnDepositNoThreshold.LT(math.LegacyOneDec().Sub(lawThreshold)) { + return fmt.Errorf("burnDepositNoThreshold cannot be lower than 1-lawThreshold") + } + if burnDepositNoThreshold.LT(math.LegacyOneDec().Sub(threshold)) { + return fmt.Errorf("burnDepositNoThreshold cannot be lower than 1-threshold") + } + if burnDepositNoThreshold.GT(math.LegacyOneDec()) { + return fmt.Errorf("burnDepositNoThreshold too large: %s", burnDepositNoThreshold) + } + return nil }