Skip to content

feat(multisig): implement signer rotation and threshold updates with approval invalidation (#197) - #321

Open
Dimank92 wants to merge 1 commit into
Zenith-options:mainfrom
Dimank92:feat/issue-197-signer-rotation-and-threshold-updates
Open

Dimank92 wants to merge 1 commit into
Zenith-options:mainfrom
Dimank92:feat/issue-197-signer-rotation-and-threshold-updates

Conversation

@Dimank92

Copy link
Copy Markdown

Summary

Resolves #197 by implementing admin-gated signer rotation (add_signer, add_signer_weighted, remove_signer, remove_signer_and_set_threshold) and threshold update (set_threshold) entrypoints with O(1) approval invalidation via monotonic signer epoch bumping.

Scope & Architecture Implementation

  1. Self-Administered Admin Entrypoints:

    • add_signer(caller, signer) & add_signer_weighted(caller, signer, weight): Adds a new signer with validation (1 <= total_weight, bounded by MAX_SIGNERS, rejects duplicate signers).
    • remove_signer(caller, signer): Removes an existing signer. Rejects removal with Error::InvalidThreshold if the remaining count or total weight falls below the threshold.
    • set_threshold(caller, threshold): Updates approval threshold. Validates 1 <= threshold <= signer_count and threshold <= total_weight, updating RotationThreshold invariant.
    • remove_signer_and_set_threshold(caller, signer, new_threshold): Atomically lowers threshold and removes a signer in a single step so operations requiring simultaneously lower threshold succeed without bricking.
    • Requires caller == env.current_contract_address() && caller.require_auth(). Direct calls by single signers outside execute are rejected with Error::DirectAdminCall (Code 34).
  2. Self-Call Proposal Dispatch in execute:

    • Added execute_self_call to handle proposals targeting current_contract_address(), bypassing Soroban's re-entrancy restriction while enforcing full proposal verification and quorum threshold.
  3. Approval Invalidation via Signer Epoch Bumping:

    • Every signer rotation or threshold modification increments DataKey::SignerEpoch.
    • Because approvals_key is keyed by DataKey::Approvals(epoch, action_id), bumping SignerEpoch invalidates all outstanding approvals in O(1) time across both persistent and temporary storage.
    • Emits signers_rotated with the new epoch, resulting signer set, threshold, and rotation threshold.
  4. Testing & Verification:

    • Added 6 comprehensive unit tests covering:
      • rotation_happy_path_via_proposal: End-to-end add_signer -> remove_signer -> set_threshold via approved proposals.
      • epoch_bump_invalidates_in_flight_pending_approvals: Verifies that approvals cast in an earlier epoch do not carry over and cannot execute after a rotation.
      • threshold_bounds_rejection: Rejects 0 and threshold > count.
      • denial_of_configuration_change_below_threshold: Rejects execution of configuration changes without threshold approvals (NotYetApproved).
      • removing_signer_dropping_count_below_threshold_rejected_unless_threshold_lowered: Ensures removal that drops count below threshold reverts unless threshold is atomically lowered.
      • direct_admin_calls_by_individual_signers_rejected: Direct invocations reject with Error::DirectAdminCall.
    • All 94 multisig tests + event conformance tests pass (94 passed; 0 failed).

Payout Wallet (Base / Polygon / EVM): 0xC345C07229307d4f1c7935aD2cAe48e390215808

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.

Implement multisig Signer Rotation and Threshold Updates with Approval Invalidation

1 participant