-
Notifications
You must be signed in to change notification settings - Fork 11
contracts: bound chklocks' sweep (epoch-stall risk) + materialize the per-bucket locked total #563
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 6 commits
2084159
262a599
548790a
0ff2bdc
6b31e07
f3e23c2
0902f94
55dd7e9
2dd6e1b
bc881dd
20e9ed6
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -12,8 +12,11 @@ rate every swap is charged. | |
| against their WIRE account permissions, re-runs the LP variance check, | ||
| re-checks available collateral, then picks a winner. | ||
| - Writes one collateral lock per required leg and holds it for the full | ||
| wall-clock challenge window. **Locks are never released by delivery** — only | ||
| `chklocks` sweeps them once `expires_at_ms` passes. | ||
| wall-clock challenge window. **Locks are never released by delivery** — on the | ||
| healthy path only `chklocks` sweeps them, once `expires_at_ms` passes. An | ||
| UPHELD underwriter-fault challenge ends the window early instead: `sweeplocks` | ||
| erases that commitment's locks where they stand, which can be well before | ||
| `expires_at_ms`. | ||
| - Settles the winning swap against `sysio.reserv` and queues the outbound | ||
| `SWAP_REMIT`. | ||
| - Escrows and drains swap-from-WIRE requests (`fwqueue`), charging a revert fee | ||
|
|
@@ -64,7 +67,8 @@ chain deregistered) refund in full. | |
| |-------|----------|-------------| | ||
| | `uwconfig` | `uw_config` | Singleton: `fee_bps`, `collateral_lock_duration_ms`, `min_fromwire_amount`, `fromwire_revert_fee_bps`, `uwreq_pending_timeout_epochs`, `uwreq_retention_epochs` | | ||
| | `uwreqs` | `uw_request_t` | One row per swap intent — race state in `commits_by`, `winner`, lifecycle status, mirrored `variance_tolerance_bps`. Retained for `uwreq_retention_epochs` after ANY terminal transition — `COMPLETED` (after `chklocks` sweeps the final collateral lock; the reserve settlement itself already happened at winner selection, which is what made the row CONFIRMED), `REJECTED` (immediate failure via `reject_and_refund`), or `EXPIRED` (pending timeout, same path) — then erased by `pruneuwreqs` | | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [P3] Include
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fixed in 0902f94. You were right, and I swept for the claim rather than fixing only this row, and there were four: the README's Comments and markdown only — no Fuller write-up: #563 (comment) |
||
| | `locks` | `lock_entry` | Flat per-leg lock vector consulted by `sysio.opreg::available()`. The `byexpire` secondary index lets `chklocks` sweep expired locks in one pass | | ||
| | `locks` | `lock_entry` | Flat per-leg lock vector consulted by `sysio.opreg::available()`. The `byexpire` secondary index lets `chklocks` sweep expired locks oldest-first, up to its per-epoch budget | | ||
| | `locksums` | `lock_sum` | Materialized Σ `lock_entry.amount` per `(underwriter, chain_code, token_code)` bucket — the "locked" half of `sysio.opreg::available()`, read O(1) instead of scanning `locks`. Written by exactly three paths, all in this contract: `try_select_winner` ADDS (on a win), `chklocks` DECREMENTS (healthy release at expiry), and `sweeplocks` DECREMENTS (erasing a commitment's held locks on an UPHELD challenge). A bucket's row is erased once its total reaches zero, so an absent row reads as zero. Any new erase path must decrement too — the rollup is authoritative for `available()`, so a bucket left positive after its last row is gone suppresses that collateral permanently | | ||
| | `fwqueue` | `fromwire_q` | Escrowed swap-from-WIRE requests awaiting drain. `byepoch` secondary index | | ||
| | `uwcounters` | `uw_counters` | Monotonic id allocators (uwreq ids, lock ids) | | ||
|
|
||
|
|
@@ -77,8 +81,11 @@ chain deregistered) refund in full. | |
| | `rcrdcommit` | `sysio.msgch` | Record an underwriter's per-leg `UNDERWRITE_INTENT_COMMIT` bytes; resolves the race once both legs are present | | ||
| | `swapfromwire` | `user` | Escrow WIRE and enqueue a swap-FROM-WIRE request | | ||
| | `drainfwq` | `sysio.epoch` or self | Drain the from-WIRE queue: settle what prices, revert the rest (charging the revert fee on caller-fault causes) | | ||
| | `chklocks` | `sysio.epoch` or self | Sweep collateral locks whose wall-clock window has expired | | ||
| | `chklocks` | `sysio.epoch` or self | Sweep collateral locks whose wall-clock window has expired, oldest-first, EXAMINING at most `max_rows` rows per call (`advance` passes `MAX_LOCK_RELEASE_PER_EPOCH`) — the budget counts rows examined rather than locks released, so held locks and the challenge pokes they generate are bounded by it too; an oversized expiry burst drains across later epochs rather than aborting `advance` | | ||
| | `pruneuwreqs` | `sysio.epoch` or self | Expire timed-out PENDING uwreqs and erase terminal rows past their retention window | | ||
| | `holdlocks` | `sysio.chalg` | Mark a commitment's winning locks as held by an OPEN underwriter-fault challenge (WIRE-297); held locks are skipped by `chklocks` instead of released | | ||
| | `freelocks` | `sysio.chalg` | Clear the hold after a REJECTED or LAPSED challenge, so the next `chklocks` releases the locks normally | | ||
| | `sweeplocks` | `sysio.chalg` | Erase a commitment's held locks after an UPHELD challenge — the underwriter is already SLASHED, so each `releaselock` takes its deferred-slash branch. Decrements `locksums` like `chklocks` does | | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [P3] Qualify the early upheld-challenge sweep. This new row documents that
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fixed in f3e23c2. The Responsibility summary now marks the While there I found the Fuller write-up: #563 (comment) |
||
| | `sumlocks` | read-only | Sum an underwriter's active locks for a `(chain, token)` bucket — the lock half of `sysio.opreg::available()` | | ||
|
|
||
| ## Dependencies | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[P3] Keep this on the three-path invariant. This says “two sites,” but the rollup is mutated at three:
try_select_winneradds,chklocksdecrements, andsweeplocksdecrements after an upheld challenge. That third path was the source of the stale-cache bug fixed in this PR, so leaving the old count here undercuts the invariant documented inlock_sum. Please update this to say three sites and name them. The adjacentlocks_tcomment insysio.uwrit.hppalso still saysopreg::available()scansbyunderwriter; it should identifylocksas the authority andlocksumsas the O(1) read cache.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed in bc881dd.
You are right on both, and the second one is the more useful catch: the
locks_tcomment was still describing the scan thatlocksumsexists to replace, so the file taught the old model at the point a reader is most likely to look it up.sum_locks_inline's comment now enumerates all three with direction, and states why the count matters at this site specifically — it is the cross-contract reader, it trusts the rollup completely, and a bucket left positive after its last row is gone suppresses that collateral permanently.locks_tnow nameslocksas the AUTHORITY andlocksumsas the O(1) read cacheavailable()reads, with the reason the old scan does not scale: locks are held for the full wall-clock challenge window and are never released by delivery, so a bucket's live count is (settlement rate × lock duration).I swept for the claim rather than fixing only the two sites you named, and three more had
available()reading the lock rows:lock_entrydoc, which called the triple the indexing surfaceavailable()uses and pointed cross-contract atlocks_trather thanlocksums_tlocksrow ("consulted bysysio.opreg::available()") and its integration note — both contradicting thelocksumsrow one line below, which already said the read is O(1) instead of a scanTwo adjacent bits of staleness turned up while there. The header bullet described the composite as one of "two secondary indexes",
byuwckandbyunderwriter— neither exists; the composite is deliberately not a table-managed index (thelocks_tcomment says so directly), and the uint64 indexes arebyuw,byuwreqandbyexpire. Andlock_entrysaid its rows are erased byrelease, an action this contract does not have — the erase sites arechklocksandsweeplocks, which is also where the twosub_locked_totalcalls live.Comments and markdown only.
sysio.uwrit.wasm,sysio.opreg.wasmand both.abifiles rebuild byte-identical, so this commit carries no artifact.