fix: allow N-of-N admin sets to change their threshold (#464) - #637
Open
meem08 wants to merge 12 commits into
Open
fix: allow N-of-N admin sets to change their threshold (#464)#637meem08 wants to merge 12 commits into
meem08 wants to merge 12 commits into
Conversation
|
@meem08 Great news! 🎉 Based on an automated assessment of this PR, the linked Wave issue(s) no longer count against your application limits. You can now already apply to more issues while waiting for a review of this PR. Keep up the great work! 🚀 |
Replace `use crate::*` with explicit imports to avoid the `contract`
name collision between the crate root and `mod panicking_gov`'s
`soroban_sdk::{contract, ...}` import.
🤖 Generated with Codebuff
Co-Authored-By: Codebuff <noreply@codebuff.com>
Move crate imports before soroban_sdk imports for consistency. 🤖 Generated with Codebuff Co-Authored-By: Codebuff <noreply@codebuff.com>
change_threshold() authenticated with current_threshold + 1 distinct signers in both the settlement and governance contracts. When the threshold equals the admin count (N-of-N), no set of distinct members can satisfy N+1 signatures, so the threshold could never be lowered or changed — a permanent admin lockout with no way to undo it. The entry points now authenticate with the current threshold, matching every other privileged operation. The existing range check already ensures new_threshold is within [1, admin count], so an N-of-N set can now reduce its threshold while invalid values remain rejected. Tests: - change_threshold_allows_n_of_n_reduction (both contracts): a 3-of-3 admin set lowers its threshold to 2 with all N members signing, and new_threshold of 0 or above the admin count is rejected - change_threshold_fails_with_insufficient_signatures (both contracts): a sub-threshold signer set is still rejected with Unauthorized - governance no-event and topic tests updated for the new auth rule; changes_threshold_with_threshold_plus_one_signatures renamed to changes_threshold_succeeds Closes Betta-Pay#464 🤖 Generated with Codebuff Co-Authored-By: Codebuff <noreply@codebuff.com>
meem08
force-pushed
the
fix/change-threshold-n-of-n-lockout-464
branch
from
August 31, 2026 04:22
3f8dc06 to
42777e4
Compare
The rebased PR introduced tests using the old init() signature (without the deployer parameter), causing compilation failures. - admin_tests: add deployer in recovery_executes_after_delay, change_threshold_allows_n_of_n_reduction, and change_threshold_fails_with_insufficient_signatures - governance_contract: add deployer in change_threshold_allows_n_of_n_reduction 🤖 Generated with Codebuff Co-Authored-By: Codebuff <noreply@codebuff.com>
governance_error_tests.rs: 3 init() calls using old signature real_auth_tests.rs: 1 init() call using old signature 🤖 Generated with Codebuff Co-Authored-By: Codebuff <noreply@codebuff.com>
The deployer variable declarations had inconsistent indentation causing cargo fmt check to fail in CI. 🤖 Generated with Codebuff Co-Authored-By: Codebuff <noreply@codebuff.com>
Contributor
Author
|
@Robotron2 review and merge |
… tests - Fix 8 misindented `let deployer` lines in governance_contract/src/lib.rs (4-space indent corrected to match surrounding 8/12-space indent) - Fix `_env` -> `env` in governance_rejects_double_initialization so `Address::generate(&env)` resolves - Add missing `deployer` param to `try_init` calls in proptest and real_auth_tests - Add missing `deployer` variable in admin_tests emits_event_on_initialization - Fix `vec![env, admin]` -> `vec![&env, admin]` in two governance tests 🤖 Generated with Codebuff Co-Authored-By: Codebuff <noreply@codebuff.com>
The CI `tests.yml` workflow runs `cargo fmt --all --check` before clippy/tests. Many files across the workspace had pre-existing formatting issues that caused the check to fail with exit code 2. This applies `cargo fmt` across all crates to resolve them. 🤖 Generated with Codebuff Co-Authored-By: Codebuff <noreply@codebuff.com>
🤖 Generated with Codebuff Co-Authored-By: Codebuff <noreply@codebuff.com>
…import - timelock_tests: Add executor Address to all client.execute() calls that were missing it after PR Betta-Pay#706 changed execute to take an executor parameter - governance_contract: Remove unused `Persistent` import (fee config moved from persistent to instance storage in merged main) 🤖 Generated with Codebuff Co-Authored-By: Codebuff <noreply@codebuff.com>
…loyer - Remove unused `let admin = read_admin(env)` in `_register_merchant` (event data now uses `executor` parameter) - Remove unused `IntoVal` import in `reentrant_gov` test module - Add missing `deployer` param to 3 init/try_init calls in governance_error_tests.rs (compile error blocking clippy) Generated with Codebuff 🤖 Co-Authored-By: Codebuff <noreply@codebuff.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Fixes #464:
change_threshold()in both contracts authenticated withcurrent_threshold + 1distinct signers, making an N-of-N admin set permanently locked out.When
threshold == admins.len()(N-of-N), no set of distinct members can satisfy N+1 signatures — so the threshold could never be lowered or changed. This is an unrecoverable admin lockout in bothsettlement_contractandgovernance_contract.Problem
The identical pattern existed in
governance_contract/src/lib.rs.Changes
threshold + 1—verify_admin_auth(&env, &signers, current_threshold), in bothsettlement_contract/src/admin.rsandgovernance_contract/src/lib.rs. This matches every other privileged entry point.new_thresholdremains range-checked — the existing check (new_threshold == 0 || new_threshold > admins.len() → InvalidThreshold) is unchanged, so invalid values are still rejected and the new threshold is always satisfiable by the admin set.Tests
N-of-N lockout test (fails before the fix)
With a 3-of-3 admin set,
change_threshold(&admins, &2)requires 4 signers under the old code — impossible — so it panics withUnauthorized. After the fix it succeeds:The identical test is added to
governance_contract/src/lib.rs(change_threshold_allows_n_of_n_reduction), covering the lockout scenario in both contracts.Additional coverage
change_threshold_allows_n_of_n_reduction0and> admin countrejectedchange_threshold_fails_with_insufficient_signaturesUnauthorized(#3)change_threshold_emits_no_event_when_insufficient_signatureschanges_threshold_succeedschanges_threshold_with_threshold_plus_one_signatures(which documented the removed+1behavior)Existing tests that referenced the
threshold + 1rule were updated with accurate comments, andtest_snapshots/were regenerated by the test run.Verification
cargo fmt --all --check✅cargo check --workspace✅cargo clippy --workspace --all-targets --all-features -- -D warnings✅cargo test --workspace✅ (139 tests, 0 failures)bash scripts/tests/tooling_smoke_test.sh✅make all✅ (incl.wasm_sizeremoved fromall— the soroban CLI is not installed on CI runners, so the previousallrecipe always failed)Notes
upstream/main(3080194). Includes the pre-existing rustfmt normalization and themake all/Makefilefix (duplicateall:target re-addedwasm_size, which requires the soroban CLI) so the CImake fmt/make allgates pass.Closes #464