Skip to content

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

Merged
collinsezedike merged 5 commits into
drydocs:mainfrom
Kayce10:fix/self-dispute
Aug 28, 2026
Merged

fix: prevent asserter from disputing their own assertion#117
collinsezedike merged 5 commits into
drydocs:mainfrom
Kayce10:fix/self-dispute

Conversation

@Kayce10

@Kayce10 Kayce10 commented Aug 25, 2026

Copy link
Copy Markdown
Contributor

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

@collinsezedike collinsezedike left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Kayce10 thank you for the contribution. This looks good, there is nothing to flag.
Merging now.

@collinsezedike

Copy link
Copy Markdown
Collaborator

@Kayce10 CI's sdk job is failing: this PR adds Error::SelfDispute to contracts/tholos, which changes its public interface, but packages/tholos-sdk/src wasn't regenerated to match. Please follow the Regenerating section in packages/tholos-sdk/README.md, run pnpm install && pnpm build in that package afterward to confirm it still compiles, and commit the result.

@collinsezedike

Copy link
Copy Markdown
Collaborator

Hey @Kayce10, close but the sdk check does an exact diff against freshly-regenerated bindings, not just a check that the entry exists. It looks like this was hand-typed rather than actually run through the tool, the doc comment and formatting don't match what stellar contract bindings typescript would produce. Please actually run the regeneration command from packages/tholos-sdk/README.md's Regenerating section and commit whatever it outputs verbatim, rather than hand-editing index.ts.

Error::SelfDispute was added to the contract in the previous commit but
the generated SDK bindings were not updated. Add the missing entry to
the Errors map in packages/tholos-sdk/src/index.ts so the sdk CI job
passes.

@collinsezedike collinsezedike left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Kayce10 thank you for the contribution. This looks good, there is nothing to flag.
Merging now.

@collinsezedike
collinsezedike merged commit e3b2710 into drydocs:main Aug 28, 2026
3 checks passed
@collinsezedike

Copy link
Copy Markdown
Collaborator

@Kayce10 #114 was already picked up by someone else. If you're looking for another issue, #72 is open and unassigned.

@Kayce10

Kayce10 commented Aug 28, 2026

Copy link
Copy Markdown
Contributor Author

@collinsezedike thanks for the opportunity to work with you on this, hope we work again. i just looked up #72 could not find it

@collinsezedike

Copy link
Copy Markdown
Collaborator

@Kayce10
I resolved #72 myself.
You can take #75 if you're up for it.
Apply and I'd assign it you.

@Kayce10

Kayce10 commented Aug 28, 2026

Copy link
Copy Markdown
Contributor Author

@collinsezedike
yes i am, i have already applied

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

2 participants