Skip to content

Governance signer set can change mid-vote: add_signer/remove_signer alter quorum math on active proposals #1024

Description

@nanaf6203-bit

Governance signer set can change mid-vote: add_signer/remove_signer alter quorum math on active proposals

Labels / Complexity: Contract · Rust · Medium Complexity — Medium

Problem

contracts/governance/src/lib.rs add_signer (line 711) and remove_signer (line 734) are admin-only and can be called at any time — including while proposals are Active. Proposal threshold is snapshotted at creation (line 272: threshold: self.threshold), but the rejection math in vote reads the live signer set: let total_signers = self.signers.len() as u32; let remaining = total_signers.saturating_sub(total_votes); if proposal.votes_for.saturating_add(remaining) < proposal.threshold (lines ~502-507). Removing a signer mid-vote shrinks total_signers, making the "rejection is certain" branch fire on a proposal that could otherwise still pass; adding a signer mid-vote does the opposite and can keep a doomed proposal alive. Either way, the outcome depends on when admin happened to change the roster, and the same live signers.len() is used by get_analytics/get_proposal_participation, so analytics shift after the fact.

Root cause

contracts/governance/src/lib.rs vote (line ~502): let total_signers = self.signers.len() as u32; reads the live set instead of the proposal's snapshot, while add_signer/remove_signer (lines 711-768) are unrestricted by proposal state.

Why this is architecturally hard

  1. Snapshot vs. live is a design decision. Options: snapshot total_signers into the proposal at creation (requires a storage change and migration for existing proposals), or restrict roster changes while proposals are active (blocks legitimate admin actions). The contributor must pick and document.
  2. All signer-count consumers must agree. vote, get_analytics, and get_proposal_participation each read signers.len(); the fix must keep them consistent or explicitly define why they diverge.

Acceptance criteria

  • A test proves: removing a signer while a proposal is active does not change that proposal's pass/reject outcome (per the chosen policy).
  • The chosen policy (snapshot or freeze) is documented.
  • cargo test -p propchain-governance passes.

Out of scope

Redesigning quorum thresholds.

Getting started

Files: contracts/governance/src/lib.rs (lines 502-507, 711-768). Command: cargo test -p propchain-governance.

Metadata

Metadata

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions