From add699a5adf05e68880de1bd156baeb5ae62fdda Mon Sep 17 00:00:00 2001 From: dripsmvcp <138900956+dripsmvcp@users.noreply.github.com> Date: Sat, 25 Apr 2026 01:20:35 +0900 Subject: [PATCH] fix(ink): exclude removed validators from vote tally (#181) record_vote now filters the stored voter list against the current validator set before returning the count, so votes cast by a validator who is later removed stop contributing to quorum. Fixes the scenario in issue #181 where removing a compromised validator lowered the quorum denominator but left their stale votes counting, shortening the real vote threshold on every in-flight round they had touched. The stored voter list is deliberately kept as-is so the AlreadyVoted check still catches duplicate votes from the same address across re-additions; only the tally is filtered. --- smart-contracts/ink/lib.rs | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/smart-contracts/ink/lib.rs b/smart-contracts/ink/lib.rs index 7103a851..530c9b05 100644 --- a/smart-contracts/ink/lib.rs +++ b/smart-contracts/ink/lib.rs @@ -158,16 +158,20 @@ mod allways_swap_manager { id } - /// Record a vote on a request. Returns the new vote count. + /// Record a vote on a request. Returns the effective vote count — + /// votes from accounts no longer in the validator set are retained in + /// storage (for AlreadyVoted idempotency) but excluded from the tally, + /// so a removed validator's stale vote can't coast a round to quorum + /// under the post-removal (smaller) threshold. fn record_vote(&mut self, request_id: u64, caller: AccountId) -> Result { let mut voters = self.request_voters.get(request_id).unwrap_or_default(); if voters.contains(&caller) { return Err(Error::AlreadyVoted); } voters.push(caller); - let count = u32::try_from(voters.len()).unwrap_or(u32::MAX); self.request_voters.insert(request_id, &voters); - Ok(count) + let effective = voters.iter().filter(|v| self.validators.contains(v)).count(); + Ok(u32::try_from(effective).unwrap_or(u32::MAX)) } /// Return the active request ID for (miner, req_type), or clear it and