Skip to content

fix: prevent asserter from disputing their own assertion - #117

Open
Kayce10 wants to merge 1 commit into
drydocs:mainfrom
Kayce10:fix/self-dispute
Open

fix: prevent asserter from disputing their own assertion#117
Kayce10 wants to merge 1 commit into
drydocs:mainfrom
Kayce10:fix/self-dispute

Conversation

@Kayce10

@Kayce10 Kayce10 commented Aug 25, 2026

Copy link
Copy Markdown

Summary

dispute (contracts/tholos/src/lib.rs) required disputer.require_auth() and checked the assertion was Pending and within the challenge window, but never checked disputer != assertion.asserter. An asserter could call dispute on their own pending assertion, consuming the assertion's one dispute slot and locking out any honest third party. Because resolve's winner is selected purely from assertion.asserter/assertion.disputer, when disputer == asserter the winner is the same address regardless of the resolver committee's actual vote, guaranteeing both bonds back and nullifying the bond-forfeiture deterrent.

Changes:

  • Added Error::SelfDispute = 22 to the Error enum with a doc comment explaining the invariant
  • Added a disputer == assertion.asserter check in dispute() after the auth check and NotPending/ChallengeWindowClosed guards, before any state is mutated or any bond is transferred
  • Added test_asserter_cannot_dispute_own_assertion which verifies SelfDispute is returned, the assertion remains Pending, the disputer field stays None, and the asserter's token balance is unchanged

Test plan

  • cargo fmt --check, cargo clippy --workspace --all-targets -- -D warnings, and cargo test pass locally
  • CONTRACT.md updated if the public interface changed
  • scripts/testnet-smoke.sh run against testnet, if this changes contract behavior in a way that affects the deployed flow
  • New test test_asserter_cannot_dispute_own_assertion added and passing; all 100 existing tests continue to pass

Closes #113

An asserter calling dispute() on their own pending assertion would consume
the one dispute slot and guarantee both bonds back regardless of the resolver
vote (since resolve() pays the winner and winner == asserter == disputer in
both branches), nullifying the bond-forfeiture deterrent.

Fix:
- Add Error::SelfDispute = 22 to the Error enum
- Add a disputer == assertion.asserter check in dispute() immediately after
  the NotPending and ChallengeWindowClosed guards, before any state is
  mutated or any bond is transferred

Test:
- Add test_asserter_cannot_dispute_own_assertion: verifies SelfDispute is
  returned, assertion remains Pending, disputer field stays None, and the
  asserter's token balance is unchanged after the rejected call

Closes drydocs#113
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.

[Bug] dispute doesn't prevent an asserter from disputing their own assertion

1 participant