feat(flow-underwriter-slashing): the bonded Tier-1 challenge flow (WIRE-297) - #56
Merged
Conversation
The regenerated SysioContractTypes (WIRE-297) makes lock_entry.challenge_id a required field — the underwriter-challenge hold marker, 0 when unheld. The zero-defaults lockRow fixture now supplies it, exactly the drift the regen chain exists to catch. The flow package lands separately. Change-Id: I3c0cfc3af42cc6a860f14da4edf69ffd00cefaf8
…RE-297) Two identical ETH→SOL underwritten swaps produce two CONFIRMED commitments by the same winner, then the flow exercises both challenge verdicts on the live pipeline — ordering is load-bearing (both swaps confirm before any challenge; the upheld verdict slashes the cluster's only underwriter): - ChallengeRejected (commitment B) runs first: openuwchal escrows the bond (asserted to the unit against the challenger's balance), three Tier-1 ballots REJECT_FORFEIT, chkuwchal resolves REJECTED_FORFEIT — the bond lands exactly on the wrongly-challenged underwriter, both locks persist with their holds cleared, the operator stays ACTIVE, the uwreq stays CONFIRMED. - ChallengeUpheld (commitment A) runs last: three UPHOLD ballots, chkuwchal resolves UPHELD — the operator flips SLASHED, both locks sweep through the deferred-slash branch, the uwreq finalizes COMPLETED, the bond refunds the challenger to its pre-filing balance, and sysio.chalg ends with zero WIRE custody. The electorate is three roa::forcereg'd Tier-1 voters + the bootstrap's one Tier-1 owner (N=4, Q=3); openuwchal asserts the snapshotted quorum stays reachable by the flow's voters so a bootstrap-roster change fails loudly instead of deadlocking the vote. The swap substrate mirrors flow-swap-with-underwriting's Phase A; commitments are distinguished by excluding already-captured uwreq ids (direction alone is ambiguous with two same-direction swaps). Change-Id: I7c7fabdeb5d19448c8c33de7419d92833b3932c0
liqsol-core's clean-room OPP outpost (wire-solana 6e2fc70a) names the funding token account `authority_ata`; the SPL reserve provisioning still passed `adminAta`, so Anchor's account resolver failed the transaction build with "Account `authorityAta` not provided" and the SOL outpost deploy died before submitting anything. Surfaced by any cluster bootstrap against a wire-solana tree built after the clean-room commit. Change-Id: Ie22a3df710439d2dd13643b130cb013f6b33f5f5
The uwchals verdict/ballot/reason cells ride the ABI `enums` extension,
so get_table_rows serializes them as NAME spellings ("REJECTED_FORFEIT"),
not numbers — `Number(challenge.verdict)` was NaN forever and the verdict
poll timed out 420s AFTER the chain had already resolved the tally and
paid the forfeit inline (verified from the chkuwchal trace's inline
sysio.chalg → winner transfer of the exact bond). awaitVerdict now matches
via matchesProtoEnum, which covers the numeric, numeric-string, and
spelling forms.
Change-Id: Ic377d82b7644789bc0288e6b801b105c490ac859
…thority" Reverts 714b68f. The fix matched a STALE local wire-solana tree: liqsol-core named the account `authority_ata` only in the Jul-9 → Jul-29 window; the GlobalConfig admin gating (ef70e254, in next via the Aug-5 develop merge #407) renamed it `admin_ata`, which is what master's `adminAta` already targets. The e2e gate (run 31127327308) failed all 14 flows on "Account `adminAta` not provided" with the fix in place — current next wants master's spelling. Local clusters must build wire-solana@next (post-#407) instead. Change-Id: I3b131ded7fdd56bc43b0a32ac89bf67b1ae36bad
Contributor
Author
✅ e2e gate GREEN — all 14 flows passedRun 31132787179 (
|
…mbond sysio.chalg::chkuwchal now credits a claimable balance instead of transferring the bond (wire-sysio #549 review round 2): the tally can run inline under the epoch tick, where sysio.token::transfer's require_recipient(to) would run the recipient's own code and let it abort epoch advancement. Each verdict is therefore asserted twice. First that resolution moved no WIRE — the bondcredits row carries the whole bond and both balances are unchanged, which is the property the pull exists to buy. Then a claimbond step pulls it, and a second verify asserts delivery: the forfeit lands on the wrongly-challenged underwriter exactly, the refund makes the challenger whole. The zero-custody assertion moves to the end, where it now means both bonds resolved AND claimed. planClaimbond takes either a literal account (the challenger) or the commitment whose winner is owed the forfeit, resolved from ctx.outputs at run time; its runner asserts the credit exists before the pull, that the payout equals the credit, and that the row is erased. readBondCredit reads sysio.chalg::bondcredits.
jglanz
approved these changes
Aug 10, 2026
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.
The live-cluster validation of the underwriter challenge system (Wire-Network/wire-sysio#549 + Wire-Network/wire-libraries-ts#61): a new
flow-underwriter-slashingpackage plus one harness fix the validation surfaced.The flow
Two identical ETH→SOL underwritten swaps produce two CONFIRMED commitments by the same winner, then both challenge verdicts run on the real pipeline — ordering is load-bearing (both swaps confirm before any challenge; the upheld verdict slashes the cluster's only underwriter):
openuwchalescrows the bond (asserted to the unit against the challenger's balance), three Tier-1 ballots REJECT_FORFEIT,chkuwchalresolves REJECTED_FORFEIT — the bond lands exactly on the wrongly-challenged underwriter, both locks persist with their holds cleared, the operator stays ACTIVE, the uwreq stays CONFIRMED.sysio.chalgends with zero WIRE custody.The electorate is three
roa::forcereg'd Tier-1 voters + the bootstrap's one Tier-1 owner (N=4, Q=3);openuwchalasserts the snapshotted quorum stays reachable by the flow's voters so a bootstrap-roster change fails loudly instead of deadlocking the vote. Commitments are distinguished by excluding already-captured uwreq ids (direction alone is ambiguous with two same-direction swaps).Validated green: 154/154 Report steps on a live local cluster.
Harness fix the validation surfaced
fix(flow-underwriter-slashing): verdict matched by proto spelling — the uwchals verdict/ballot/reason cells ride the ABIenumsextension, soget_table_rowsserializes them as NAME spellings ("REJECTED_FORFEIT"); the poll now usesmatchesProtoEnum(numeric, numeric-string, and spelling forms). Attempt-one of the live run proved the chain resolves + pays the forfeit inline while aNumber()comparison stays blind forever.(The branch briefly carried a
create_reserve_spl_authorityaccount rename in the SOL bootstrapper, reverted ind0229287: liqsol-core named that accountauthority_ataonly in a Jul-9→Jul-29 window that a stale local wire-solana build happened to capture; currentnext(post-#407 GlobalConfig admin gating) usesadmin_ata, which master's harness already targets.)Merge order: Wire-Network/wire-sysio#549 → Wire-Network/wire-libraries-ts#61 → this. The e2e gate must run with all three
BRANCH_*=feat/underwriter-challengeoverrides.