Skip to content

feat: add governance fee withdraw cancel race test - #475

Open
Stanley-Owoh wants to merge 1 commit into
TevaLabs:mainfrom
Stanley-Owoh:add-governance-fee-withdraw-cancel-race-tests-401
Open

feat: add governance fee withdraw cancel race test#475
Stanley-Owoh wants to merge 1 commit into
TevaLabs:mainfrom
Stanley-Owoh:add-governance-fee-withdraw-cancel-race-tests-401

Conversation

@Stanley-Owoh

Copy link
Copy Markdown
Contributor

Security: governance 2-of-N fee withdraw + cancel races

Summary

Closes #401

Hardened the dual-approval governance mechanism against fee-withdraw reentrancy,
cancel/execute races, and pause-policy bypasses. execute now follows proper
CEI ordering (marking a proposal Executed before performing any external
interaction) and respects the protocol pause state for non-emergency actions,
while keeping emergency pause/unpause always callable.

What changed

contracts/src/governance.rs

  • CEI ordering in execute: The proposal is now marked Executed (persisted)
    before the protected action payload (e.g. WithdrawProtocolFee) runs. This
    makes it impossible to double-execute a proposal or cancel it once its effects
    have begun. Soroban's atomic revert still rolls the write back if an action
    fails, so approvals aren't burned and can be retried.
  • Pause rules respected on execute: Non-emergency actions
    (WithdrawProtocolFee, SetProtocolFeeBps, SetTreasuryAddress, SetAdmin,
    SetOracle) are now gated by _ensure_not_paused — blocked while the protocol
    is FullyPaused, matching the AdminConfig policy matrix used by the direct
    admin entrypoints. PauseProtocol / UnpauseProtocol remain executable while
    paused so emergency response is never blocked.

contracts/src/tests/governance.rs

Added security tests covering every acceptance criterion:

  • test_double_execute_impossible — second execute of an executed proposal fails
  • test_cancel_after_approve_blocks_execute_and_second_approve — cancelling an
    approved proposal blocks execution and re-approval; no side effects applied
  • test_expired_proposal_rejected_on_execute — execution after the expiry ledger
    is rejected; no side effects applied
  • test_pause_rules_respected_on_execute — fee withdraw / config mutation are
    blocked while paused; unpause still succeeds
  • test_pause_proposal_remains_executable_while_paused — pause is idempotent /
    always allowed
  • test_governance_fee_withdraw_success_applies_cei — treasury debited and
    recipient credited exactly once; no double-drain on a second execute

contracts/src/tests/mod.rs

Registered the previously-orphaned mod governance; (it was declared in lib.rs
but missing from the test module list), so the governance tests now compile and
run.

Acceptance criteria

  • Double-execute impossibleexecute transitions the proposal to Executed
    before effects; a second call hits the state guard and is rejected.
  • Cancel after approve cannot execute — cancelling an approved proposal flips
    it to Cancelled, which both execute and approve reject.
  • Expired proposal rejected — execution/approval past expires_at_ledger
    returns ProposalExpired with no side effects.
  • Pause rules respected on execute — non-pause actions are blocked by
    FullyPaused, while pause/unpause remain available for emergency control.

Note on pre-existing build state

The branch does not compile in its checked-out state due to unrelated,
pre-existing failures (left untouched per scope):

  • errors.rs is missing variants referenced by access_control.rs/settlement.rs
    (AccessDenied, OracleHeartbeatUnhealthy, DisputeWindowExpired,
    ClaimLocked).
  • The test build additionally fails in types.rs (MarketSnapshot) and across
    many other test modules.

None of these touch the governance module or its tests. The governance changes
were validated to compile; the full test suite cannot be executed until the
unrelated breakage is resolved.

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.

Security: governance 2-of-N fee withdraw + cancel races

1 participant