Skip to content

[Governance 3/4] Introduce pallet-referenda#2807

Draft
l0r1s wants to merge 1 commit into
governance-signed-votingfrom
governance-referenda
Draft

[Governance 3/4] Introduce pallet-referenda#2807
l0r1s wants to merge 1 commit into
governance-signed-votingfrom
governance-referenda

Conversation

@l0r1s

@l0r1s l0r1s commented Jun 29, 2026

Copy link
Copy Markdown
Collaborator

WIP

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

AI review — see the sticky summary comment for the verdict and the inline comments below for specific findings.

Comment on lines +509 to +529
let now = T::BlockNumberProvider::current_block_number();
let index = ReferendumCount::<T>::get();
ReferendumCount::<T>::put(index.saturating_add(1));
ActiveCount::<T>::put(active.saturating_add(1));
ActivePerProposer::<T>::insert(&proposer, active_per_proposer.saturating_add(1));

let proposal = match &track_info.decision_strategy {
DecisionStrategy::PassOrFail {
decision_period, ..
} => {
let when = now.saturating_add(*decision_period);
Self::set_alarm(index, when)?;
let bounded_call = T::Preimages::bound(*call)?;
Proposal::Action(bounded_call)
}
DecisionStrategy::Adjustable { initial_delay, .. } => {
let when = now.saturating_add(*initial_delay);
Self::schedule_enactment(index, DispatchTime::At(when), call)?;
Proposal::Review
}
};

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

[HIGH] submit leaks queue slots when setup fails

ReferendumCount, ActiveCount, and ActivePerProposer are mutated before fallible setup (set_alarm, Preimages::bound, and schedule_enactment). FRAME does not automatically roll back earlier storage writes when an extrinsic returns DispatchError, so an authorized proposer can repeatedly trigger one of these failures and leave no ReferendumStatusFor entry for kill to clear. That permanently consumes MaxQueued / per-proposer capacity and can block governance submissions. Perform all fallible setup first, then mutate the queue counters only after the referendum can be fully recorded; if an already-bounded call must be abandoned because scheduling fails, drop that preimage handle before returning.

Suggested change
let now = T::BlockNumberProvider::current_block_number();
let index = ReferendumCount::<T>::get();
ReferendumCount::<T>::put(index.saturating_add(1));
ActiveCount::<T>::put(active.saturating_add(1));
ActivePerProposer::<T>::insert(&proposer, active_per_proposer.saturating_add(1));
let proposal = match &track_info.decision_strategy {
DecisionStrategy::PassOrFail {
decision_period, ..
} => {
let when = now.saturating_add(*decision_period);
Self::set_alarm(index, when)?;
let bounded_call = T::Preimages::bound(*call)?;
Proposal::Action(bounded_call)
}
DecisionStrategy::Adjustable { initial_delay, .. } => {
let when = now.saturating_add(*initial_delay);
Self::schedule_enactment(index, DispatchTime::At(when), call)?;
Proposal::Review
}
};
let now = T::BlockNumberProvider::current_block_number();
let index = ReferendumCount::<T>::get();
let proposal = match &track_info.decision_strategy {
DecisionStrategy::PassOrFail {
decision_period, ..
} => {
let bounded_call = T::Preimages::bound(*call)?;
let when = now.saturating_add(*decision_period);
if let Err(err) = Self::set_alarm(index, when) {
T::Preimages::drop(&bounded_call);
return Err(err);
}
Proposal::Action(bounded_call)
}
DecisionStrategy::Adjustable { initial_delay, .. } => {
let when = now.saturating_add(*initial_delay);
Self::schedule_enactment(index, DispatchTime::At(when), call)?;
Proposal::Review
}
};
ReferendumCount::<T>::put(index.saturating_add(1));
ActiveCount::<T>::put(active.saturating_add(1));
ActivePerProposer::<T>::insert(&proposer, active_per_proposer.saturating_add(1));

@github-actions

Copy link
Copy Markdown
Contributor

🛡️ AI Review — Skeptic (security review)

VERDICT: VULNERABLE

BASELINE scrutiny: l0r1s is an established contributor with write permission; no Gittensor allowlist hit found. Branch is governance-referenda -> governance-signed-voting.

No .github/ai-review/* or .github/copilot-instructions.md changes were present. The new dependency is a local workspace pallet, not an external supply-chain addition.

Findings

Sev File Finding
HIGH pallets/referenda/src/lib.rs:529 submit leaks queue slots when setup fails inline

Conclusion

The PR introduces a governance queue DoS in submit: failed setup can permanently consume active referendum capacity without creating a referendum that can be killed. This blocks a SAFE verdict until the storage mutation order is fixed.


# 🔍 AI Review — Auditor (domain review) has not yet run on this PR.

@github-actions

Copy link
Copy Markdown
Contributor

🔄 AI review updated — Skeptic: VULNERABLE

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant