Assessment reports>All in Bits>Threat Model>Component: Dynamic quorum (x/gov)

Component: Dynamic quorum (x/gov)

Description

This module continuously tunes the minimum voter-participation threshold (quorum) required for proposals to pass, so governance remains possible even when participation fluctuates. It does so by maintaining an exponential moving average (EMA) of participation for general, constitution-amendment, and law proposals. It then computes the next quorum as quorum = min_quorum + (max_quorum − min_quorum) × participationEMA.

The result is used during tallying and exposed through gRPC/CLI queries. This is how it works.

  1. State keys. These include KeyParticipationEMA, KeyConstitutionAmendmentParticipationEMA, and KeyLawParticipationEMA.

  2. Update flow. After every vote tally, UpdateParticipationEMA is called, and it applies the new quorum requirements.

  3. Quorum ranges. These are stored in Params.QuorumRange, ConstitutionAmendmentQuorumRange, and LawQuorumRange as {Min, Max} strings (sdk.Dec 0--1).

  4. Genesis/upgrade. The V3 upgrade handler seeds all EMAs to 12% and copies default quorum ranges into params.

Invariants

  • 0 ≤ participationEMA ≤ 1.

  • 0 ≤ minQuorum ≤ maxQuorum ≤ 1.

  • minQuorum ≤ computedQuorum ≤ maxQuorum for each bucket.

  • For every proposal tally, the matching EMA is updated exactly once.

Test coverage

Covered

  • Get/set each EMA key.

  • computeQuorum correctness across custom (min, max, EMA) triples.

  • UpdateParticipationEMA path for no-kind, law, constitution, and mixed proposals.

  • Genesis/upgrade seeding of params and EMA.

  • gRPC query returns expected quorum values.

Not covered

  • Negative tests for malformed params (min > max, out-of-range values).

  • Overflow/precision edge cases on large EMAs.

  • Governance param-change integration tests (end-to-end vote → new quorum).

Attack surface

  • Governance param changes. An attacker can propose params with

    • min_quorum > max_quorum → inverted curve.

    • values outside [0,1] → runtime panics or impossible quorums.

    • an extremely wide range → quorum swings to 0% or 100%.

  • Direct state manipulation via migrations/scripts could set EMA outside [0,1]; quorum then overflows bounds.

Zellic © 2025Back to top ↑