feat: implement v2 weighted-majority outcome resolution - #86
Merged
Conversation
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
Implements #68: weighted-majority outcome resolution for
tholos-v2, the piece that turns revealed vote weight into a decidedterminal_cause/final_outcome.StrictMajorityFor/StrictMajorityAgainst) the moment its weight exceeds half of the frozen eligible totalW, checked viaside_weight > W - side_weightrather than division, so an oddWdoesn't round the wrong way. Locking doesn't move the assertion out ofReveal: other positions can keep revealing afterward to prove entitlement for settlement, matching V2_RESOLUTION.md's state diagram (OutcomeLockedis folded into the existingRevealphase rather than adding a new one).Resolvedoncerevealed_weightcatches up withW, orreveal_deadlinepasses, whichever happens first. If neither side ever reached a majority by then,terminal_causedefaults toOptimisticTimeoutand the originally asserted outcome stands, even in a lopsided-but-incomplete reveal (e.g. one side reveals 49% against with 50% of weight never showing up at all) or an exact tie at full reveal.resolve_outcomeentrypoint. It's needed becausereveal()itself can never be the one to close things out pastreveal_deadline(a call returningErrreverts all of its state changes in Soroban, so arevealattempt made after the deadline can't leave a partial close behind), and because a dispute that drew zero third-party registrations has nobody who could ever callrevealsuccessfully at all (the asserter/disputer's fixed positions are already marked revealed automatically whenRevealopens).resolve_outcomerequires no signature: it only applies a deterministic rule to already-committed weight and elapsed time, moving no funds.Settlement (who actually gets paid, and how much) stays out of scope here per the issue:
terminal_causeis enough to know the outcome, the payout math is #69.Test plan
cargo fmt --check,cargo clippy --workspace --all-targets -- -D warnings, andcargo testpass locallycargo build -p tholos --target wasm32v1-none --releaseandcargo build --workspace --target wasm32v1-none --releasepass locallymdbook build docspasses locallyCONTRACT.mdupdated if the public interface changed — not applicable,tholos-v2isn't documented there yet (tracked separately)scripts/testnet-smoke.shrun against testnet — not run; this only touchestholos-v2, which isn't deployed yetresolve_outcome's three failure modes and its idempotency, and the lopsided-but-incomplete-reveal optimistic-timeout case from V2_RESOLUTION.md. Two pre-existing reveal tests needed a second never-revealing voter added to keep exercising their original single-position scenario now that a fully-revealed dispute closes automatically.