Governance: tech-collective-only (hardened)#604
Conversation
… tech collective Replace the public/token-weighted community governance lane with a minimal, upgrades-only governance pallet, keep the tech-collective referenda lane as a transitional fallback, and fix verified audit findings in the retained code. New: - pallet-upgrade-gov (index 22): M-of-N member approval + timelock that authorizes runtime upgrades via frame_system::authorize_upgrade only. Fixed Action enum (no arbitrary dispatch), internal membership/threshold/delay management, on_initialize timelock. Seeded at genesis with the same trusted set as the tech collective. 11 unit tests. Removed (community lane): - pallet-conviction-voting (deleted). - Community Referenda instance (idx 10) and ConvictionVoting (idx 12); indices kept vacant. CommunityTracksInfo removed. - Dead community-lane integration tests (engine.rs, logic.rs). Retained as transitional fallback: TechCollective + TechReferenda, to be removed once upgrades via pallet-upgrade-gov are proven on-chain. Audit fixes in retained code: - #91267: tech-collective Add/RemoveOrigin now EnsureRootWithSuccess (Root-only, i.e. a passed referendum) instead of any single member. - #91248: removed dead Root branch in RootOrMemberForTechReferendaOrigin. - #91272: referenda submission deposit now refundable for Rejected/TimedOut. - #91165: preimage per-byte deposit corrected (was 1000x too low). spec_version 132 -> 133.
Alternative to the pallet-upgrade-gov approach (#603): keep the tech-collective referenda lane as the sole governance mechanism and harden it, rather than adding a new minimal upgrade pallet. Builds on the shared base (community lane removed, #91267/#91248/#91272/#91165 fixed). Removed: - pallet-upgrade-gov (crate, runtime wiring, config, genesis seeding). The tech lane authorizes runtime upgrades and other Root calls directly. Tech-lane audit fixes (local referenda fork + runtime config): - #91247/#91270 (high): TechCollectiveTracksInfo::track_for now accepts only a Root proposal origin. Previously any Signed(_) mapped to track 0, so a passed referendum could dispatch as an arbitrary account (impersonation) or route Root-level dispatch through the single low-threshold track. All legitimate submissions already use a Root proposal origin. - #91271 (high): ready_for_deciding honors the bounded-queue insertion result instead of unconditionally setting in_queue = true, preventing ghost-queued referenda (skipped by timeout yet absent from TrackQueue). - #91210 (high): schedule_enactment and set_alarm log a loud error on scheduler failure instead of failing silently (debug_assert-only in release). - #91213 (low): cancel/kill only release a deciding slot when the referendum actually held one, preventing DecidingCount corruption (relevant at max_deciding = 1). Parameter hardening: - TechReferenda prepare_period 20 blocks (4 min) -> 2 hours of advance notice. - UndecidingTimeout comment corrected (value is 45 days, not 90). Moot via the community-lane removal already on this branch: - #91141 (abstentions satisfy approval), #91166 (self-vote weight): both were pallet-conviction-voting issues; that pallet is deleted. - #91193 (unbounded member exchange): exchange_member is NeverEnsureOrigin. - #91265 (removed members' votes): non-issue (upstream ranked-collective behavior; membership changes are Root-gated and rare). Tests: pallet-referenda 39/39; runtime governance 23 passed/1 ignored; native + WASM build and runtime-benchmarks compile clean.
|
I diffed our forks against upstream master and the latest published crates (pulled the real source for Version contextOur forks are ~3 majors behind: Finding-by-finding: upstream vs our fixA. Fixes we made in the kept lane (referenda)
B. Config-level findings (not a pallet-version thing)
C. Moot for us (community lane deleted)
D. Audit "false positive" — and upstream agrees with you
E. Not addressed by us / not separately diffed
Verdict
One thing to double-check#91272 is our only real divergence from upstream. Upstream deliberately keeps the submission deposit non-refundable on Recommended follow-up (separate from these PRs): rebase the forks onto 48.0.0 to pick up unrelated upstream fixes/security patches, then re-apply our 4 patches on top — since the fixes we need aren't upstream-released anyway. Want me to (a) write this up as a doc next to PR #604, (b) pull the exact upstream diff snippets for #91271/#91210/#91213 into the PR description, or (c) scope the 45→48 rebase? |
Summary
Alternative to #603. Instead of introducing a new
pallet-upgrade-gov, this PR keeps the tech-collective referenda lane as the sole governance mechanism and hardens it. Both PRs share the same base — the permissionless, token-weighted community lane is removed (pallet-conviction-votingdeleted, communityReferendainstance dropped) — so they compare directly:pallet-upgrade-gov(M-of-N + timelock) and keeps TechReferenda as a fallback.pallet-upgrade-goventirely and relies on the (now hardened) TechCollective + TechReferenda lane, which already provides the full referendum lifecycle (thresholds, confirm window, vote-changing, cancel/kill).Net change vs
main: +342 / −5339 — strictly less code than #603.Removed
pallet-upgrade-govcrate + runtime wiring, config, and genesis seeding.pallet-conviction-voting, communityReferenda/ConvictionVoting, and their tests.Tech-lane audit fixes
TechCollectiveTracksInfo::track_foraccepts only a Root proposal origin. Previously anySigned(_)mapped to track 0, letting a passed referendum dispatch as an arbitrary account (impersonation) or route Root dispatch through the single low-threshold track. All legitimate submissions already use a Root proposal origin.ready_for_decidinghonors the bounded-queue insertion result instead of always settingin_queue = true→ no more ghost-queued referenda (skipped by timeout yet absent fromTrackQueue).schedule_enactment/set_alarmlog a loud error on scheduler failure instead of failing silently in release (wasdebug_assert!-only).cancel/killrelease a deciding slot only when the referendum actually held one → noDecidingCountcorruption (matters atmax_deciding = 1).Already fixed on the shared base: #91272, #91267, #91248, #91165.
Parameter hardening
prepare_period: 20 blocks (4 min) → 2 hours advance notice before deciding.UndecidingTimeoutcomment corrected (value is 45 days, not 90).CancelOrigin = Rootandmax_deciding = 1retained (serialized upgrade lane); the real-time defense against a malicious referendum is nay votes within the 24h confirm window — seedocs/TECH_COLLECTIVE_GOVERNANCE_TUNING.md.Assessed as not-applicable / non-issue
pallet-conviction-votingissues — that pallet is deleted; the tech lane uses ranked-collective's aye/nay head-count tally (no abstain).exchange_memberisNeverEnsureOrigin(disabled).Test plan
cargo test -p pallet-referenda— 39/39cargo build -p quantus-runtime(native + WASM)cargo test -p quantus-runtime --features fast-governance— 23 passed / 1 ignoredcargo check -p quantus-runtime --features runtime-benchmarks