Skip to content

feat(gov): allow variable governance proposal durations with dynamic quorum #24576

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 7 commits into
base: main
Choose a base branch
from

Conversation

DuongNguyen62025
Copy link
Contributor

@DuongNguyen62025 DuongNguyen62025 commented Apr 25, 2025

Description

Closes: #24535


Author Checklist

All items are required. Please add a note to the item if the item is not applicable and
please add links to any relevant follow up issues. Your PR will not be merged unless you satisfy
all of these items.

I have...

  • included the correct type prefix in the PR title, you can find examples of the prefixes below:
  • confirmed ! in the type prefix if API or client breaking change
  • targeted the correct branch (see PR Targeting)
  • provided a link to the relevant issue or specification
  • reviewed "Files changed" and left comments if necessary
  • included the necessary unit and integration tests
  • added a changelog entry to CHANGELOG.md
  • updated the relevant documentation or specification, including comments for documenting Go code
  • confirmed all CI checks have passed

Summary by CodeRabbit

  • New Features

    • Added support for specifying a custom proposal duration when submitting governance proposals.
    • Proposal duration now influences the required quorum and minimum deposit, with dynamic calculation based on the specified duration.
  • Bug Fixes

    • None.
  • Tests

    • Updated and added tests to cover dynamic threshold and minimum deposit calculations based on proposal duration.
    • Adjusted test cases and helper functions to support the new duration parameter.
  • Documentation

    • None.
  • Chores

    • Refactored internal logic and method signatures to accommodate the proposal duration feature.

Copy link

ironbird-prod bot commented Apr 25, 2025

Ironbird - launch a network To use Ironbird, you can use the following commands:
  • /ironbird start OR /ironbird start --load-test-config= - Launch a testnet with the specified chain and load test configuration.
  • /ironbird chains - List of chain images that ironbird can use to spin-up testnet
  • /ironbird loadtests - List of load test modes that ironbird can run against testnet
Custom Load Test Configuration You can provide a custom load test configuration using the `--load-test-config=` flag:
/ironbird start cosmos --load-test-config={
  "block_gas_limit_target": 0.75,
  "num_of_blocks": 50,
  "msgs": [
    {"weight": 0.3, "type": "MsgSend"},
    {"weight": 0.3, "type": "MsgMultiSend"},
	{"weight": 0.4, "type": "MsgArr", "ContainedType": "MsgSend", "NumMsgs": 3300}
  ]
}

Use /ironbird loadtests to see more examples.

Copy link
Contributor

coderabbitai bot commented Apr 25, 2025

📝 Walkthrough

Walkthrough

This change introduces support for variable governance proposal durations in the Cosmos SDK governance module. The protobuf definitions for proposals and proposal submission messages are extended with a new optional duration field. The codebase is updated to propagate this field through message constructors, keeper methods, and CLI utilities. A new method interpolates voting thresholds and minimum deposits based on the custom duration, allowing the required quorum and deposit to scale with the proposal's timeline. All relevant logic, validation, and test cases are updated to handle the new duration parameter, ensuring backward compatibility and correct integration throughout the governance workflow.

Changes

File(s) Change Summary
proto/cosmos/gov/v1/gov.proto, proto/cosmos/gov/v1/tx.proto Added optional duration field (type: google.protobuf.Duration) to Proposal and MsgSubmitProposal messages.
x/gov/types/v1/params.go, x/gov/types/v1/params_test.go Added CalculateThresholdAndMinDeposit method to interpolate threshold and deposit based on duration; added corresponding unit tests.
x/gov/types/v1/proposal.go, x/gov/types/v1/msgs.go Updated NewProposal and NewMsgSubmitProposal constructors to accept and set the new duration parameter.
x/gov/keeper/proposal.go, x/gov/keeper/msg_server.go, x/gov/keeper/deposit.go Updated keeper methods to accept and utilize the duration parameter for proposal creation, validation, and voting period calculation.
x/gov/keeper/tally.go Modified tallying logic to use interpolated threshold for expedited proposals with a custom duration.
x/gov/client/cli/util.go, x/gov/client/cli/tx.go Extended CLI proposal struct and submission logic to support parsing and passing proposal duration.
x/bank/app_test.go, x/gov/keeper/.go, x/gov/keeper/.test.go, x/gov/simulation/*.go, x/gov/types/v1/msgs_test.go, x/gov/types/v1/proposals_test.go, x/gov/migrations/v4/store_test.go Updated all affected test code and simulation logic to pass the new duration argument (typically as nil) to constructors and keeper methods.

Sequence Diagram(s)

sequenceDiagram
    participant CLI/User
    participant CLI
    participant MsgSubmitProposal
    participant Keeper
    participant Params

    CLI/User->>CLI: Submit proposal with duration
    CLI->>MsgSubmitProposal: Construct MsgSubmitProposal(duration)
    MsgSubmitProposal->>Keeper: SubmitProposal(..., duration)
    Keeper->>Params: CalculateThresholdAndMinDeposit(duration)
    Params-->>Keeper: threshold, minDeposit
    Keeper->>Keeper: Validate initial deposit (uses minDeposit)
    Keeper->>Keeper: Set proposal voting period (uses duration)
    Keeper-->>CLI/User: Proposal created with custom duration/quorum
Loading

Assessment against linked issues

Objective Addressed Explanation
Allow governance proposals to specify a custom duration field (issue #24535)
Use proposal duration to interpolate required quorum and minimum deposit (issue #24535)
Update all relevant message types, constructors, and keeper logic for duration support (#24535)
Ensure tests and CLI tooling handle the new duration parameter (issue #24535)
✨ Finishing Touches
  • 📝 Generate Docstrings

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

‼️ IMPORTANT
Auto-reply has been disabled for this repository in the CodeRabbit settings. The CodeRabbit bot will not respond to your replies unless it is explicitly tagged.

  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🧹 Nitpick comments (8)
x/gov/types/v1/proposal.go (1)

26-26: Function signature updated with duration parameter, needs documentation.

The NewProposal function signature has been updated to include a new duration parameter, which is part of the variable governance proposal durations feature. However, the function's documentation comment doesn't explain this new parameter or its purpose.

Consider updating the function documentation at line 25 to explain the purpose and usage of the duration parameter.

-// NewProposal creates a new Proposal instance
+// NewProposal creates a new Proposal instance
+// duration is an optional parameter that can be used to set a custom voting period duration,
+// which will influence the required quorum for proposal passage
x/gov/types/v1/params.go (1)

10-10: Inconsistent import alias for the same package.

The import for github.com/cosmos/cosmos-sdk/types now has two different aliases in use - sdk on line 9 and types on line 10. This could lead to confusion and inconsistency in the codebase.

Choose one alias consistently throughout the file:

-import (
-	"fmt"
-	"time"
-
-	sdkmath "cosmossdk.io/math"
-
-	sdk "github.com/cosmos/cosmos-sdk/types"
-	types "github.com/cosmos/cosmos-sdk/types"
-)
+import (
+	"fmt"
+	"time"
+
+	sdkmath "cosmossdk.io/math"
+
+	sdk "github.com/cosmos/cosmos-sdk/types"
+)
x/gov/keeper/deposit_test.go (2)

56-57: Add test coverage for variable duration proposals.

While the test has been updated to support the new function signature, there's no explicit test case for the variable duration functionality. Consider adding test cases that verify the behavior with custom durations to ensure the feature works as expected.

-proposal, err := govKeeper.SubmitProposal(ctx, tp, "", "title", "summary", TestAddrs[0], tc.expedited, nil)
+
+// Test with nil duration (default behavior)
+proposal, err := govKeeper.SubmitProposal(ctx, tp, "", "title", "summary", TestAddrs[0], tc.expedited, nil)
+require.NoError(t, err)
+proposalID := proposal.Id
+
+// Test with custom duration
+customDuration := 72 * time.Hour
+customDurationProposal, err := govKeeper.SubmitProposal(ctx, tp, "", "title with custom duration", "summary", 
+    TestAddrs[0], tc.expedited, &customDuration)
+require.NoError(t, err)
+
+// Verify custom duration is set correctly
+if tc.expedited {
+    require.NotNil(t, customDurationProposal.Duration)
+    require.Equal(t, customDuration, *customDurationProposal.Duration)
+}

146-147: Add a test case with non-nil duration for expedited proposals.

The current test only checks the functionality with nil duration. Consider adding a test case where expedited is true and a custom duration is provided to ensure the correct interaction between expedited proposals and custom durations.

x/gov/keeper/proposal.go (2)

19-19: Document the purpose of the new duration parameter in the function comment.

The function comment should be updated to explain the purpose and behavior of the new duration parameter, particularly its interaction with expedited proposals.

 // SubmitProposal creates a new proposal given an array of messages
-func (k Keeper) SubmitProposal(ctx context.Context, messages []sdk.Msg, metadata, title, summary string, proposer sdk.AccAddress, expedited bool, duration *time.Duration) (v1.Proposal, error) {
+// SubmitProposal creates a new proposal given an array of messages
+// If duration is provided for expedited proposals, it overrides the default expedited voting period.
+// For non-expedited proposals, the duration parameter is ignored.
+func (k Keeper) SubmitProposal(ctx context.Context, messages []sdk.Msg, metadata, title, summary string, proposer sdk.AccAddress, expedited bool, duration *time.Duration) (v1.Proposal, error) {

252-259: Consider supporting variable durations for non-expedited proposals as well.

The current implementation only supports custom durations for expedited proposals. Consider extending this functionality to non-expedited proposals as well for consistency and flexibility.

 if proposal.Expedited {
     if proposal.Duration != nil {
         votingPeriod = proposal.Duration
     } else {
         votingPeriod = params.ExpeditedVotingPeriod
     }
 } else {
-    votingPeriod = params.VotingPeriod
+    if proposal.Duration != nil {
+        votingPeriod = proposal.Duration
+    } else {
+        votingPeriod = params.VotingPeriod
+    }
 }
x/gov/client/cli/util.go (2)

92-92: Update the comment to document the Duration field.

Add a comment line documenting the purpose and format of the Duration field for better developer experience.

 	Expedited bool              `json:"expedited"`
-	Duration  Duration          `json:"duration"`
+	// Duration is an optional custom voting period for the proposal (e.g. "72h", "10m", "1h30m")
+	Duration  Duration          `json:"duration"`

206-209: Add support for JSON marshaling.

Implement the json.Marshaler interface as well to ensure the Duration type can be properly serialized back to JSON if needed.

 func (d Duration) ToDuration() *time.Duration {
 	duration := time.Duration(d)
 	return &duration
 }
+
+// MarshalJSON implements the json.Marshaler interface
+func (d Duration) MarshalJSON() ([]byte, error) {
+	return json.Marshal(time.Duration(d).String())
+}
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 4846e80 and 1c4b60a.

⛔ Files ignored due to path filters (4)
  • api/cosmos/gov/v1/gov.pulsar.go is excluded by !**/*.pulsar.go
  • api/cosmos/gov/v1/tx.pulsar.go is excluded by !**/*.pulsar.go
  • x/gov/types/v1/gov.pb.go is excluded by !**/*.pb.go, !**/*.pb.go
  • x/gov/types/v1/tx.pb.go is excluded by !**/*.pb.go, !**/*.pb.go
📒 Files selected for processing (29)
  • proto/cosmos/gov/v1/gov.proto (1 hunks)
  • proto/cosmos/gov/v1/tx.proto (2 hunks)
  • x/bank/app_test.go (1 hunks)
  • x/gov/abci_test.go (10 hunks)
  • x/gov/client/cli/tx.go (1 hunks)
  • x/gov/client/cli/util.go (3 hunks)
  • x/gov/keeper/deposit.go (3 hunks)
  • x/gov/keeper/deposit_test.go (4 hunks)
  • x/gov/keeper/export_test.go (1 hunks)
  • x/gov/keeper/grpc_query_test.go (13 hunks)
  • x/gov/keeper/hooks_test.go (2 hunks)
  • x/gov/keeper/keeper_test.go (2 hunks)
  • x/gov/keeper/msg_server.go (2 hunks)
  • x/gov/keeper/msg_server_test.go (33 hunks)
  • x/gov/keeper/proposal.go (3 hunks)
  • x/gov/keeper/proposal_test.go (6 hunks)
  • x/gov/keeper/tally.go (1 hunks)
  • x/gov/keeper/tally_test.go (2 hunks)
  • x/gov/keeper/vote_test.go (1 hunks)
  • x/gov/migrations/v4/store_test.go (1 hunks)
  • x/gov/simulation/msg_factory.go (1 hunks)
  • x/gov/simulation/operations.go (1 hunks)
  • x/gov/simulation/operations_test.go (4 hunks)
  • x/gov/types/v1/msgs.go (3 hunks)
  • x/gov/types/v1/msgs_test.go (1 hunks)
  • x/gov/types/v1/params.go (2 hunks)
  • x/gov/types/v1/params_test.go (1 hunks)
  • x/gov/types/v1/proposal.go (2 hunks)
  • x/gov/types/v1/proposals_test.go (3 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (7)
x/gov/client/cli/tx.go (2)
x/gov/types/v1/msgs.go (1)
  • NewMsgSubmitProposal (18-43)
x/gov/client/cli/util.go (1)
  • Duration (191-191)
x/gov/types/v1/params.go (4)
x/gov/client/cli/util.go (1)
  • Duration (191-191)
x/gov/simulation/genesis.go (6)
  • VotingPeriod (22-22)
  • ExpeditedVotingPeriod (23-23)
  • ExpeditedThreshold (26-26)
  • ExpeditedMinDeposit (19-19)
  • Threshold (25-25)
  • MinDeposit (18-18)
math/legacy_dec.go (2)
  • LegacyMustNewDecFromStr (209-215)
  • LegacyNewDecFromInt (130-132)
types/coin.go (1)
  • NewCoin (19-30)
x/gov/types/v1/params_test.go (3)
x/gov/types/v1/params.go (1)
  • DefaultParams (88-107)
types/coin.go (2)
  • NewCoins (194-201)
  • NewCoin (19-30)
math/int.go (1)
  • NewInt (119-121)
x/gov/keeper/msg_server_test.go (2)
x/gov/types/v1/msgs.go (1)
  • NewMsgSubmitProposal (18-43)
x/gov/migrations/v4/store_test.go (1)
  • TestProposal (31-31)
x/gov/keeper/deposit.go (1)
x/gov/keeper/keeper.go (1)
  • Keeper (21-62)
x/gov/keeper/msg_server.go (1)
x/gov/client/cli/util.go (1)
  • Duration (191-191)
x/gov/types/v1/msgs.go (1)
x/gov/client/cli/util.go (1)
  • Duration (191-191)
⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: Summary
🔇 Additional comments (36)
x/gov/simulation/msg_factory.go (1)

161-170: Added support for new duration parameter in proposal submission.

The call to v1.NewMsgSubmitProposal has been updated to include a new nil parameter for the optional proposal duration. This is consistent with the PR's goal of implementing variable governance proposal durations.

x/gov/keeper/vote_test.go (1)

22-24: Updated test call with duration parameter.

The SubmitProposal call has been updated to match the new signature that supports variable durations (passed as nil here to maintain default behavior).

x/bank/app_test.go (1)

382-393: Updated governance proposal construction with duration parameter.

The NewMsgSubmitProposal call has been updated to include the new optional duration parameter as nil. This change properly implements the variable proposal duration feature in test code.

x/gov/migrations/v4/store_test.go (2)

80-82: Updated proposal creation with duration parameter.

The NewProposal call has been updated to include the new optional duration parameter as nil for this test proposal.


87-88: Updated second test proposal creation with duration parameter.

Similar to the first proposal, this one has also been updated to include the new optional duration parameter, maintaining consistency throughout the test code.

x/gov/keeper/tally_test.go (2)

25-25: Updated SubmitProposal call with nil duration parameter

The function call has been updated to accommodate the new duration parameter in the SubmitProposal signature. Passing nil here maintains backward compatibility, using the default duration behavior for this test case.


69-73: Updated SubmitProposal calls with nil duration parameter

Both SubmitProposal calls have been updated to match the new method signature by adding a nil duration parameter, preserving the existing test behavior while supporting the new variable duration feature.

x/gov/keeper/tally.go (1)

197-202: Added support for dynamic threshold calculation based on proposal duration

The implementation correctly handles both cases:

  1. When a custom duration is specified, it dynamically calculates the threshold using CalculateThresholdAndMinDeposit
  2. When no duration is provided, it falls back to GetExpeditedThreshold

This change integrates well with the existing code structure while adding support for variable governance proposal durations.

proto/cosmos/gov/v1/gov.proto (1)

104-107: Added duration field to Proposal message

The new field enables variable governance proposal durations with proper type and options:

  • Uses the standard Google protobuf Duration type
  • Includes appropriate gogoproto options for standard duration handling
  • Clearly documented purpose for quorum calculation
  • Field is properly optional, maintaining backward compatibility

This protobuf change aligns with the broader implementation of variable proposal durations throughout the governance module.

x/gov/keeper/hooks_test.go (2)

76-76: Updated SubmitProposal call with nil duration parameter

The function call has been updated to match the new method signature by adding the nil duration parameter, maintaining backward compatibility for the test.


88-88: Updated SubmitProposal call with nil duration parameter

Similar to the previous change, this function call was updated to support the new signature while preserving the existing test behavior.

x/gov/types/v1/msgs_test.go (1)

70-70: Duration parameter added to NewMsgSubmitProposal calls

The test now correctly passes the new duration parameter (as nil) to the v1.NewMsgSubmitProposal function, maintaining compatibility with the updated function signature that supports variable governance proposal durations.

x/gov/simulation/operations.go (1)

249-249: Duration parameter added to simulation code

Added support for the new duration parameter (passed as nil) in the simulated proposal submission function. This change properly aligns the simulation code with the updated NewMsgSubmitProposal signature.

x/gov/client/cli/tx.go (1)

143-143: CLI support for custom proposal durations

The CLI command now properly handles the custom duration parameter by passing proposal.Duration.ToDuration() to the NewMsgSubmitProposal function. This enables users to specify variable durations when submitting governance proposals through the CLI.

x/gov/types/v1/proposals_test.go (1)

40-40: Duration parameter added to test proposal creation

Updated all test cases to include the new duration parameter (as nil) in calls to v1.NewProposal, maintaining compatibility with the updated constructor signature that supports variable proposal durations.

Also applies to: 49-49, 53-53, 74-74

x/gov/types/v1/proposal.go (1)

46-46: LGTM!

Correctly assigning the duration parameter to the Proposal struct's Duration field.

x/gov/keeper/keeper_test.go (2)

93-104: LGTM!

All calls to govKeeper.SubmitProposal have been correctly updated to include nil as the last parameter, aligning with the new method signature that accepts a duration parameter.


119-120: LGTM!

The call to govKeeper.SubmitProposal has been properly updated to include nil as the last parameter for the duration.

proto/cosmos/gov/v1/tx.proto (1)

12-12: LGTM!

Properly added import for the Duration protobuf type to support the new duration field.

x/gov/keeper/export_test.go (1)

13-13: LGTM!

Correctly updated the call to k.validateInitialDeposit to include nil as the last parameter for the duration, aligning with the updated method signature.

x/gov/keeper/proposal_test.go (1)

32-32: Updated function calls to match new signature with duration parameter.

All calls to suite.govKeeper.SubmitProposal have been updated to include a nil duration parameter, matching the new method signature that supports variable governance proposal durations.

Also applies to: 59-59, 80-80, 110-110, 171-171, 181-181, 185-185, 190-190, 798-798

x/gov/types/v1/msgs.go (2)

4-5: Added time package import for duration support.

This import is needed to support the new *time.Duration parameter.


23-24: Added duration parameter to support variable governance proposal durations.

The NewMsgSubmitProposal constructor now accepts an optional duration parameter, which is a key part of the implementation for variable governance proposal durations with dynamic quorum. This parameter allows proposals to have custom voting periods rather than fixed ones.

Also applies to: 32-33

x/gov/keeper/msg_server_test.go (1)

55-56: Updated function calls to include nil duration parameter.

All test cases calling v1.NewMsgSubmitProposal have been updated to include a nil duration parameter, maintaining existing test behavior while supporting the new variable duration feature.

Also applies to: 71-72, 87-88, 103-104, 119-120, 135-136, 151-152, 167-168, 183-184, 199-200, 215-216, 231-232, 247-248, 263-264, 279-280, 319-320, 369-370, 421-422, 468-469, 513-514, 567-568, 689-690, 734-735, 798-799, 814-815, 1025-1026, 1072-1073, 1161-1162, 1312-1313, 1357-1358, 1415-1416

x/gov/abci_test.go (3)

34-35: Added nil duration parameter to NewProposal calls.

Updated v1.NewProposal constructor calls to include the new duration parameter as nil, which preserves existing behavior in tests while supporting the variable duration feature.

Also applies to: 62-63


108-109: Updated proposal submission calls to include nil duration parameter.

All calls to v1.NewMsgSubmitProposal and suite.GovKeeper.SubmitProposal now include a nil duration parameter, ensuring consistent function signatures across the codebase while maintaining existing test behavior.

Also applies to: 161-162, 185-186, 233-234, 296-297, 396-397, 454-455, 551-552


1-724:

✅ Verification successful

Verify integration with dynamic quorum calculations.

The PR aims to implement variable governance proposal durations with dynamic quorum, but the quorum calculation logic isn't visible in this file. Ensure that the duration parameter is properly used to adjust quorum requirements in the relevant parts of the codebase.


🏁 Script executed:

#!/bin/bash
# Search for code that uses the duration parameter to calculate quorum
echo "Searching for dynamic quorum calculation based on duration..."
rg -A 10 "CalculateThresholdAndMinDeposit|interpolate.*quorum|quorum.*duration" --type go

Length of output: 3310


Dynamic quorum calculation integration verified

The CalculateThresholdAndMinDeposit helper is invoked in both the tally and deposit keeper logic to adjust quorum and minimum deposit based on each proposal’s Duration:

  • In x/gov/keeper/tally.go, we call
    thresholdStr, _ = params.CalculateThresholdAndMinDeposit(*proposal.Duration)
    before converting to a Dec for the vote threshold.
  • In x/gov/keeper/deposit.go, we retrieve
    _, minDepositCoins = params.CalculateThresholdAndMinDeposit(*duration)
    to enforce the correct minimum deposit.

Additionally, x/gov/types/v1/params_test.go includes comprehensive tests for CalculateThresholdAndMinDeposit.

No further changes are needed here.

x/gov/keeper/grpc_query_test.go (1)

61-61: SubmitProposal method signature updated to support variable durations.

All calls to suite.govKeeper.SubmitProposal() have been updated with an additional nil parameter to match the new method signature that now includes an optional *time.Duration parameter. This change correctly maintains the existing behavior while supporting the new variable governance proposal durations feature.

Also applies to: 142-142, 158-158, 219-219, 416-416, 497-497, 611-611, 717-717, 821-821, 1108-1108, 1209-1209, 1299-1299, 1394-1394

x/gov/simulation/operations_test.go (1)

234-234: NewProposal constructor updated to include optional duration parameter.

The v1.NewProposal() constructor calls have been updated with a nil duration parameter to support the variable governance proposal durations feature. This correctly adapts the test code to the new constructor signature while maintaining the previous behavior.

Also applies to: 282-282, 331-331, 378-378

x/gov/keeper/deposit.go (1)

286-306: Added support for dynamic minimum deposit based on proposal duration.

The validateInitialDeposit function has been enhanced to consider custom proposal durations when validating initial deposits. For expedited proposals, it now:

  • Uses the standard expedited minimum deposit when no duration is specified
  • Calculates a dynamic minimum deposit based on the provided duration using the new interpolation method

This implementation correctly supports the variable governance proposal durations feature by allowing minimum deposit requirements to scale with the proposal's timeline.

x/gov/types/v1/params.go (1)

226-263: Core implementation for dynamic governance parameters based on proposal duration.

The new CalculateThresholdAndMinDeposit method is a well-designed solution for implementing variable governance proposal durations. Key points:

  1. It properly normalizes the proposal duration between expedited and normal voting periods
  2. It uses linear interpolation to calculate appropriate threshold and deposit values based on duration
  3. It handles edge cases appropriately by clamping values and providing fallback behavior

This implementation allows governance parameters to scale proportionally with proposal durations, enabling more flexible governance processes.

Two minor suggestions for future improvement:

  1. Consider adding explicit validation that normalCoins and expeditedCoins have the same length and matching denominations
  2. Add comments documenting the behavior when duration is outside the normal range
x/gov/keeper/msg_server.go (1)

325-325: Ensure nil duration propagation through legacy message server.

The addition of nil for the duration in the legacy message server is appropriate for backward compatibility. This ensures that legacy proposal submissions will continue to work with the new duration parameter architecture.

x/gov/keeper/deposit_test.go (2)

223-224: LGTM!

Changes appropriately adapt the test to the new function signature.


412-414: LGTM!

Changes appropriately adapt the test to the new function signature.

x/gov/keeper/proposal.go (1)

102-102: LGTM!

The change correctly passes the duration parameter to the NewProposal constructor.

x/gov/types/v1/params_test.go (1)

14-83: Well-designed test cases for the dynamic threshold and deposit calculation

This test thoroughly validates the CalculateThresholdAndMinDeposit functionality, covering a comprehensive range of duration scenarios including edge cases and intermediate values. The linear interpolation between expedited and normal governance parameters is well-tested with precise expected values.

The test cases appropriately verify:

  • Default duration (48h) parameters
  • Expedited duration (24h) parameters
  • Durations less than expedited (using expedited values)
  • Durations greater than default (using default values)
  • Multiple intermediate durations with correctly interpolated values

The calculations for the interpolated thresholds and minimum deposits are mathematically sound, maintaining proper precision for financial values.

return nil, err
}

if err := k.validateDepositDenom(ctx, params, initialDeposit); err != nil {
return nil, err
}

proposal, err := k.Keeper.SubmitProposal(ctx, proposalMsgs, msg.Metadata, msg.Title, msg.Summary, proposer, msg.Expedited)
proposal, err := k.Keeper.SubmitProposal(ctx, proposalMsgs, msg.Metadata, msg.Title, msg.Summary, proposer, msg.Expedited, msg.Duration)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

Validate the function signature updates are applied consistently across the codebase.

The addition of the duration parameter to the SubmitProposal function is a significant change that must be consistently applied to all callers of this method.


🏁 Script executed:

#!/bin/bash
# Check all SubmitProposal function calls to verify they have been updated
rg "SubmitProposal\([^)]*\)" --type go | grep -v "duration"

Length of output: 17121


Ensure the SubmitProposal signature update is applied across all callers

I ran a search for SubmitProposal( calls missing the duration argument and found numerous unupdated usages. All calls to the gov keeper (and its MsgServer) must now pass the new duration parameter.

Key locations needing updates:

  • x/gov/abci_test.go
  • x/gov/keeper/deposit_test.go
  • x/gov/keeper/grpc_query_test.go
  • x/gov/keeper/msg_server_test.go
  • x/gov/keeper/tally_test.go
  • x/gov/keeper/vote_test.go
  • x/gov/keeper/proposal_test.go
  • x/gov/keeper/keeper_test.go
  • x/gov/client/cli/util.go
  • x/gov/client/cli/tx.go
  • x/gov/simulation/operations.go & operations_test.go
  • tests/e2e/gov/tx.go
  • tests/integration/gov/keeper/tally_test.go, grpc_query_test.go, genesis_test.go

Each invocation should match:

SubmitProposal(
  ctx,
  msgs,
  metadata,
  title,
  summary,
  proposer,
  expedited,
  duration,
)

After updating, please re-run your grep to confirm no missing instances.

@@ -76,15 +76,15 @@ func (k msgServer) SubmitProposal(goCtx context.Context, msg *v1.MsgSubmitPropos
return nil, fmt.Errorf("failed to get governance parameters: %w", err)
}

if err := k.validateInitialDeposit(ctx, params, initialDeposit, msg.Expedited); err != nil {
if err := k.validateInitialDeposit(ctx, params, initialDeposit, msg.Expedited, msg.Duration); err != nil {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

Ensure parameter validation for the new duration parameter.

The Duration parameter is now added to the validateInitialDeposit call, but there seems to be no validation to ensure that if provided, the duration is reasonable (not too short or too long). Consider adding validation logic elsewhere in the code to prevent potential abuse of this feature.


🏁 Script executed:

#!/bin/bash
# Searching for duration validation code in other files
rg -A 5 "func.*validateInitialDeposit.*duration \*time\.Duration" --type go

Length of output: 608


🏁 Script executed:

#!/bin/bash
# Print the full body of validateInitialDeposit to check for duration validation
rg -n "func (k Keeper) validateInitialDeposit" -A200 x/gov/keeper/deposit.go

Length of output: 78


🏁 Script executed:

#!/bin/bash
# Verify files in the keeper directory
echo "=== x/gov/keeper contents ==="
ls -R x/gov/keeper

# Search for the full definition of validateInitialDeposit across the repo
echo "=== validateInitialDeposit definition ==="
rg -n "func (k Keeper) validateInitialDeposit" -A200 --type go

Length of output: 527


🏁 Script executed:

#!/bin/bash
# Locate the definition of validateInitialDeposit, accounting for pointer receiver
rg -n "func \(.*Keeper\) validateInitialDeposit" -A20 -B5 x/gov/keeper/deposit.go
rg -n "validateInitialDeposit" -A5 -B2 x/gov/keeper/deposit.go

Length of output: 1870


🏁 Script executed:

#!/bin/bash
# Locate CalculateThresholdAndMinDeposit and its implementation to check for duration validation
rg -nH "func .*CalculateThresholdAndMinDeposit" -A200 -t go

# Search for any Params fields related to deposit periods to identify valid duration bounds
rg -nH "MinDepositPeriod" -A5 -t go
rg -nH "MaxDepositPeriod" -A5 -t go

Length of output: 23515


Add explicit duration bounds validation in validateInitialDeposit

Although CalculateThresholdAndMinDeposit clamps out-of-range durations, we should still validate that the user-supplied duration lies within the configured expedited and normal voting periods. Without this, an attacker could pass a zero or negative duration (bypassing expedited deposits) or an absurdly large duration (skewing deposit calculations).

Please add, at the top of validateInitialDeposit (in x/gov/keeper/deposit.go), something like:

 func (k Keeper) validateInitialDeposit(
     ctx context.Context,
     params v1.Params,
     initialDeposit sdk.Coins,
     expedited bool,
     duration *time.Duration,
 ) error {
+   // Ensure provided duration is within [ExpeditedVotingPeriod, VotingPeriod]
+   if duration != nil {
+     if *duration < params.ExpeditedVotingPeriod || *duration > params.VotingPeriod {
+       return fmt.Errorf(
+         "duration %s must be between expedited voting period (%s) and voting period (%s)",
+         duration.String(), params.ExpeditedVotingPeriod.String(), params.VotingPeriod.String(),
+       )
+     }
+   }
     // existing coin validation…

• File: x/gov/keeper/deposit.go
• Function: validateInitialDeposit

Committable suggestion skipped: line range outside the PR's diff.

Comment on lines +191 to +209
type Duration time.Duration

func (d *Duration) UnmarshalJSON(b []byte) error {
var s string
if err := json.Unmarshal(b, &s); err != nil {
return err
}
duration, err := time.ParseDuration(s)
if err != nil {
return err
}
*d = Duration(duration)
return nil
}

func (d Duration) ToDuration() *time.Duration {
duration := time.Duration(d)
return &duration
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Add validation for reasonable duration values.

The code currently allows any valid duration string to be parsed, but there's no validation to ensure the duration is reasonable for governance proposals (e.g., not too short or too long). Consider adding validation logic to prevent potential misuse.

 func (d *Duration) UnmarshalJSON(b []byte) error {
 	var s string
 	if err := json.Unmarshal(b, &s); err != nil {
 		return err
 	}
 	duration, err := time.ParseDuration(s)
 	if err != nil {
 		return err
 	}
+	// Add validation to ensure the duration is reasonable
+	minDuration := 1 * time.Hour    // Example: 1 hour minimum
+	maxDuration := 30 * 24 * time.Hour // Example: 30 days maximum
+	if duration < minDuration {
+		return fmt.Errorf("duration is too short, minimum allowed is %s", minDuration)
+	}
+	if duration > maxDuration {
+		return fmt.Errorf("duration is too long, maximum allowed is %s", maxDuration)
+	}
 	*d = Duration(duration)
 	return nil
 }

Note: The specific minimum and maximum values should be determined based on your governance requirements. Consider aligning with the default governance parameters or defining these values as constants.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
type Duration time.Duration
func (d *Duration) UnmarshalJSON(b []byte) error {
var s string
if err := json.Unmarshal(b, &s); err != nil {
return err
}
duration, err := time.ParseDuration(s)
if err != nil {
return err
}
*d = Duration(duration)
return nil
}
func (d Duration) ToDuration() *time.Duration {
duration := time.Duration(d)
return &duration
}
func (d *Duration) UnmarshalJSON(b []byte) error {
var s string
if err := json.Unmarshal(b, &s); err != nil {
return err
}
duration, err := time.ParseDuration(s)
if err != nil {
return err
}
// Add validation to ensure the duration is reasonable
minDuration := 1 * time.Hour // Example: 1 hour minimum
maxDuration := 30 * 24 * time.Hour // Example: 30 days maximum
if duration < minDuration {
return fmt.Errorf("duration is too short, minimum allowed is %s", minDuration)
}
if duration > maxDuration {
return fmt.Errorf("duration is too long, maximum allowed is %s", maxDuration)
}
*d = Duration(duration)
return nil
}

@aljo242 aljo242 added the v54 label Apr 25, 2025
@aljo242 aljo242 requested a review from aaronc April 29, 2025 13:45
@aljo242
Copy link
Contributor

aljo242 commented Apr 29, 2025

@aaronc assigning you for review for this:

  • is the function we are giving simple and extendable enough?
  • does this introduce any bugs such as bypassing quorum / too-quick voting periods
  • is this a backwards compatible change (just adding logic, not modifying existing logic)

Copy link
Member

@aaronc aaronc left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for working on this @DuongNguyen62025 !

If I understand correctly, this allows for specifying a duration between the regular and expedited voting periods and scales it linearly. Is that right?

We need to document the algorithm and conditions thoroughly. Initially, I suggest adding this documentation to the new duration field in MsgSubmitProposal in the form of a specification for how this field will be used. Later we will need to add additional user facing docs.

Assuming we are fine with the specified algorithm, there are some issues to address in terms of calculating the target threshold deterministically using LegacyDec instead of float64.

@@ -185,3 +187,23 @@ func ReadGovPropCmdFlags(proposer string, flagSet *pflag.FlagSet) (*govv1.MsgSub
func ReadGovPropFlags(clientCtx client.Context, flagSet *pflag.FlagSet) (*govv1.MsgSubmitProposal, error) {
return ReadGovPropCmdFlags(clientCtx.GetFromAddress().String(), flagSet)
}

type Duration time.Duration
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Proto3 json already has custom formatting for durations. The issue is that we're using encoding/json instead of the proto codec. We should just use proto json instead in parseSubmitProposal IMHO

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't quite understand what you mean. protojson.Unmarshal() is used for proto.Message and json.Unmarshal is used for any. So if using protojson I think we will need to change the proposal(add ProtoReflect method)

I have created 3 buttons with simapp for submitting proposal and voting, so tested it after adding duration field

@aaronc
Copy link
Member

aaronc commented Apr 29, 2025

@aaronc assigning you for review for this:

  • is the function we are giving simple and extendable enough?

  • does this introduce any bugs such as bypassing quorum / too-quick voting periods

  • is this a backwards compatible change (just adding logic, not modifying existing logic)

Just seeing this @aljo242. Already left my initial review. Trying to first make sure that we get clarification on the specified behavior. It does appear backwards compatible.

@DuongNguyen62025
Copy link
Contributor Author

DuongNguyen62025 commented May 2, 2025

Thanks for working on this @DuongNguyen62025 !

If I understand correctly, this allows for specifying a duration between the regular and expedited voting periods and scales it linearly. Is that right?

That's right, this is backwards compatible, a period of time that is not too fast or too slow that the proposer desires

// based on a desired proposal duration, interpolating between the normal and expedited values.
func (p Params) CalculateThresholdAndMinDeposit(duration time.Duration) (threshold string, minDeposit []types.Coin) {
// Ensure voting period ranges make sense
rate := (duration.Seconds() - p.ExpeditedVotingPeriod.Seconds()) / (p.VotingPeriod.Seconds() - p.ExpeditedVotingPeriod.Seconds())

Check notice

Code scanning / CodeQL

Floating point arithmetic Note

Floating point arithmetic operations are not associative and a possible source of non-determinism
// based on a desired proposal duration, interpolating between the normal and expedited values.
func (p Params) CalculateThresholdAndMinDeposit(duration time.Duration) (threshold string, minDeposit []types.Coin) {
// Ensure voting period ranges make sense
rate := (duration.Seconds() - p.ExpeditedVotingPeriod.Seconds()) / (p.VotingPeriod.Seconds() - p.ExpeditedVotingPeriod.Seconds())

Check notice

Code scanning / CodeQL

Floating point arithmetic Note

Floating point arithmetic operations are not associative and a possible source of non-determinism
// based on a desired proposal duration, interpolating between the normal and expedited values.
func (p Params) CalculateThresholdAndMinDeposit(duration time.Duration) (threshold string, minDeposit []types.Coin) {
// Ensure voting period ranges make sense
rate := (duration.Seconds() - p.ExpeditedVotingPeriod.Seconds()) / (p.VotingPeriod.Seconds() - p.ExpeditedVotingPeriod.Seconds())

Check notice

Code scanning / CodeQL

Floating point arithmetic Note

Floating point arithmetic operations are not associative and a possible source of non-determinism
@aljo242 aljo242 requested a review from Eric-Warehime May 2, 2025 17:07
Copy link
Contributor

@Eric-Warehime Eric-Warehime left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Logic mostly looks good in terms of how this will be working. I assume that the dynamic amounts will be hard to reason about when constructing a gov prop, so I think it would be nice to include some client/CLI functions to allow simulation to show you what the calculated values will be before you submit a prop.

@DuongNguyen62025
Copy link
Contributor Author

DuongNguyen62025 commented May 5, 2025

Logic mostly looks good in terms of how this will be working. I assume that the dynamic amounts will be hard to reason about when constructing a gov prop, so I think it would be nice to include some client/CLI functions to allow simulation to show you what the calculated values will be before you submit a prop.

I can add a query to preview Threshold and MinDeposit from suggested duration. What do you think?

type QueryThresholdAndMinDepositFromDurationRequest struct {
	Duration time.Duration `protobuf:"varint,1,opt,name=duration,json=duration,proto3" json:"duration,omitempty"`
}
type QueryThresholdAndMinDepositFromDurationResponse struct {
	MinDeposit []types.Coin `protobuf:"bytes,1,rep,name=min_deposit,json=minDeposit,proto3" json:"min_deposit"`
        Threshold string `protobuf:"bytes,2,opt,name=threshold,proto3" json:"threshold,omitempty"`
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Allow for variable governance proposal times
4 participants