Repository navigation
fix(disputes): disputeFinalizesAt view, and an answer is one non-zero hash before the challenge deadline (D4, D5) - #53
Merged
Conversation
|
Important
This repository does not receive automatic reviews because it has fewer than 10 stars. ⚙️ Run configuration
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
…e at the effective challenge deadline is refused with the grace still to run (passes: the evidence), and a second answer, an answer at the challenge deadline and a zero answer are all accepted (red on the current code)
…he effective challenge deadline (the ruling's own cutoff): DisputeManager__ZeroResponse, __AlreadyResponded(orderHash), __ResponseWindowClosed(until) on EVM; ZeroResponse 27, AlreadyResponded 26, ResponseWindowClosed 25 on Soroban, relayed by the ad-manager as 84, 83, 82; Soroban record_response takes the escrow's paused seconds (the module cannot read back into its caller)
…lFinalizesAt's dispute twin: 0 off Disputed, else the effective challenge deadline plus the evidence grace when the co-signed payout was denied and the ruling is not MakerForfeit. It is the doors' own clock: on EVM finalizeCancel, finalizeDispute and both views read one _finalizesAt; on Soroban finalize_dispute and the view read one dispute_end. AdManager pays for the view by sharing the outcomeOf read and the ads[params.adId] lookup (EIP-170 margin 53 -> 135 bytes)
JoE11-y
force-pushed
the
fix/dispute-finalize-view-answer-rules
branch
from
October 5, 2026 13:48
38a54fc to
229e7d0
Compare
…(R1–R6) R1 (user decision): an answer after the arbiter has ruled is refused, AlreadyRuled, on both chains. The ruling stores its finalize time in the deadline slot, so without this an answer landed until then, of use only to a re-ruling (#454, tranche 3, untouched). Reproduced first on both chains. The module's AlreadyRuled is relayed 1:1 by both escrows now (ad-manager 85, order-portal 105) instead of collapsing to DisputeModuleRejected; core relay + the module's code-table test carry it. R2: Soroban cancel_finalizes_at and finalize_cancel read one cancel_end (the dispute_end shape); outcome_of uses deadline_at. R3: finalizeDispute passes the module and outcome it already read to _disputeEnd — one external read, not three. R4: _finalizesExactlyAt pins the refusal at at−1: TooEarly(at) inside the grace, DisputeNotResolved when the view equals the challenge deadline (the untyped expectRevert had hidden that shape). R5: both views' natspec — paused seconds accrue at unpause, re-read after one. R6: the order-portal relays the three answer faults 1:1 (102–104). EVM 2,048 non-invariant passed, fmt clean, AdManager 23,880 (696 spare). Soroban unit suites core 22 / dispute-manager 43 / ad-manager 56 / order-portal 26, fmt clean; integration 243 on freshly built WASM.
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.
Dispute fixes D4 and D5 from the relayer API audit, on both escrow implementations (EVM and Soroban). The relayer side is in a stacked monorepo PR.
D4: the relayer couldn't know when a dispute can be finalized.
finalizeDisputerefuses until the challenge deadline has passed and, when the co-signed payout was denied (a maker halt, or a key kill during the order) and the ruling isn'tMakerForfeit, until the evidence grace has passed too. On EVM two inputs to "denied" (the lock time and the registry epoch) sit in an internal mapping with no view, so the relayer showed the challenge deadline and a finalize sent then was refusedTooEarly.disputeFinalizesAt(params)(EVM) /dispute_finalizes_at(Soroban), the dispute twin ofcancelFinalizesAt: 0 unless disputed, else the effective challenge deadline, plus the grace when the payout was denied and the ruling isn'tMakerForfeit.finalizeCancel,finalizeDisputeand both views read one private_finalizesAt; on Sorobanfinalize_disputeand the view read onedispute_end.view - 1finalize reverts, at the view it succeeds; not denied, halted, key kill in the window,MakerForfeit, a pause, not disputed.D5: answers to a dispute had no rules.
recordResponseoverwrote a single slot and accepted an answer at any time while disputed, including a zero hash. Now, before the write:DisputeManager__ZeroResponse()DisputeManager__AlreadyResponded(orderHash)DisputeManager__ResponseWindowClosed(until)DisputeManager__AlreadyRuled(orderHash)record_responsenow takes the escrow's paused seconds, asoutcome_ofalready does (the module can't read back into its caller).How this was done: reproduced first on the current code, on both chains: a denied dispute finalized at the challenge deadline was refused
TooEarly(90001)(EVM) /TooEarly(Soroban), and a second, a late and a zero answer were all accepted. Each fix was reverted once to confirm its test goes red.Bytecode: the view alone put
AdManager231 bytes over the 24,576-byte limit. Two behaviour-neutral refactors (one helper for theoutcomeOfread, one for theads[params.adId]lookup) bring it to 24,441, 135 bytes under (it was 53 under before).DisputeManager7,979 → 8,095. No circuit change, so noVerifier.solregen.Checks:
forge fmt --checkclean; forge 1,983 passed (incl. the ffi proof tests against circuitsa1ee0f0), invariants 24, gas gates andOrderHashParitypass,error-coveragenames all 144 errors. Sorobancargo fmt --checkclean, unit tests green, all WASMs rebuilt, integration 242/242 incl. metering, parity steps pass. Local forge is 1.5.1; CI pins 1.7.1.Review pass 1 (2026-10-05), on the branch rebased onto main 4c28171 (ProofBridgeUtils in)
AlreadyRuled(row above). Reproduced first: with the guard removed the new test lands an answer after aMutualRefundruling on EVM and on Soroban. The module'sAlreadyRuledis now relayed 1:1 by both escrows (ad-manager 85, order-portal 105) instead of collapsing toDisputeModuleRejected; the core relay table and the module's code-table test carry it.cancel_finalizes_atandfinalize_cancelread onecancel_end, thedispute_endshape;outcome_ofusesdeadline_atinstead of its inline copy of the pause arithmetic.finalizeDisputepasses the module and outcome it already read to_disputeEnd; one external read, not three. (Bytecode: AdManager 23,880, 696 spare on the rebased branch.)_finalizesExactlyAtpins the refusal atat − 1:TooEarly(at)inside the grace,DisputeNotResolved(orderHash)when the view equals the challenge deadline. Typing it surfaced that second shape, which the untypedexpectReverthad hidden.unpause, re-read after one.For the monorepo PR (#548): one more rule row (
AlreadyRuled, Soroban 85) in the D5 docs and the frontend's Soroban error table; the order-portal codes 102–105 are unreachable and need no row.Checks (this pass): EVM 2,048 passed, non-invariant,
forge fmtclean; Soroban unit suites core 22 / dispute-manager 43 / ad-manager 56 / order-portal 26,cargo fmtclean; integration suite 243 passed on freshly built WASM (dispute-manager, ad-manager, order-portal rebuilt per package).