Uwrit: enforce UWREQ lifecycle expiry with bounded epoch-driven pruning#533
Open
heifner wants to merge 1 commit into
Open
Uwrit: enforce UWREQ lifecycle expiry with bounded epoch-driven pruning#533heifner wants to merge 1 commit into
heifner wants to merge 1 commit into
Conversation
…ng (SEC-129 / WSA-223) expires_at_epoch was recorded on terminal UWREQ rows but never read, and no code path erased a row; PENDING requests had no deadline at all, so consensus state grew monotonically and an abandoned race pinned the user's funds in custody indefinitely. - New byexpire secondary index + pruneuwreqs(max_rows): a bounded sweep inlined from sysio.epoch::advance (after chklocks, before buildenv so expiry SWAP_REVERTs ride the same epoch's envelopes). PENDING rows past uwconfig.uwreq_pending_timeout_epochs are expired -- refund/revert via the reject_and_refund machinery, terminal status EXPIRED (first producer of that proto value) -- and terminal rows past uwconfig.uwreq_retention_epochs are erased. Never throws past the auth gate (epoch or self, like chklocks/drainfwq); a backlog drains across epochs. - Deadline stamping: createuwreq/drainfwq stamp creation_epoch + pending timeout; try_select_winner zeroes the deadline (the wall-clock lock window owns CONFIRMED rows -- chklocks always terminalizes them); terminal transitions stamp the retention window. Zero means "no deadline, never swept" (dclaim's zero-sentinel predicate). - Payload compaction: non-winner UIC blobs clear at CONFIRMED; the inbound attestation copy and every stored UIC byte blob clear at terminal transitions -- retention keeps compact audit metadata only. - Row-growth rails: per-leg UIC byte cap, candidate cap, source_tx_id / depositor / attestation-data caps at ingestion, and a projected pack_size whole-row guard before growing modifies -- all fail-closed (log + skip), never check(), per the never-throw dispatch contract. - setconfig grows uwreq_pending_timeout_epochs + uwreq_retention_epochs (defaults 10/10; positive, ceiling-capped against deadline-stamp wrap). Tests: 13 new cases. Unit: sweep auth/no-op/zero-budget + knob validation/round-trip. Dispatch-suite integration driving real epoch advances (the fixture now deploys sysio.system and satisfies the emissions gate, per the emissions_tests recipe): pre-deadline advances leave rows untouched; PENDING -> EXPIRED + compaction -> retention -> erased; from-WIRE expiry refunds the full escrow fee-exempt; the COMPLETED happy path ages out to steady state; 33 due rows against the 32-row budget prove the bound; oversized-UIC and candidate-cap rails hold. Full contracts_unit_test suite green (520 cases). Change-Id: Ifce803b6bfea1e7fa2668cd745d702b7845413c7
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
The underwriter contract declared a ten-epoch retention period and stamped
expires_at_epochon terminal UWREQ rows, but nothing ever read the field and no code path erased a row — and PENDING requests carried no deadline at all. Consensus state grew monotonically during normal operation, an abandoned race pinned the user's funds in custody indefinitely, and unbounded per-row payloads (full inbound attestation copy, verbatim per-candidate UIC blobs) meant every latermodify— all of which run inside the never-throw evalcons/advance dispatch surfaces — rewrote an ever-larger row. Addresses SEC-129 / WSA-223.byexpiresecondary index +pruneuwreqs(max_rows): a bounded sweep inlined fromsysio.epoch::advance(afterchklocks, beforebuildenvso expiry SWAP_REVERTs ride the same epoch's envelopes). PENDING rows pastuwconfig.uwreq_pending_timeout_epochsare expired — refund/revert via thereject_and_refundmachinery, terminal statusUNDERWRITE_REQUEST_STATUS_EXPIRED(first producer of that proto value) — and terminal rows pastuwconfig.uwreq_retention_epochsare erased. Auth mirrorschklocks/drainfwq(epoch or self; not permissionless — the sweep emits refunds); a backlog drains across epochs.createuwreq/drainfwqstampcreation_epoch + pending_timeout;try_select_winnerzeroes the deadline (the wall-clock lock window owns CONFIRMED rows —chklocksalways terminalizes them); terminal transitions stamp the retention window. Zero means "no deadline, never swept". Nothing is scheduled; the sweep evaluates the field lazily when the epoch machinery fires it.source_tx_id/depositor/attestation-data caps at ingestion, and a projectedpack_sizewhole-row guard before growing modifies — all fail-closed (log + skip), nevercheck(), per the never-throw dispatch contract.setconfiggrowsuwreq_pending_timeout_epochs+uwreq_retention_epochs(defaults 10/10; positive, ceiling-capped against deadline-stamp wrap).ABI delta: new action
pruneuwreqs, extendedsetconfig/uw_config, and the newuwreqssecondary index. Pre-launch dispensation applies to theuw_configsingleton shape and to pre-upgrade rows lackingbyexpireindex entries — every environment rebuilds from genesis.Validation
contracts_unit_testsuite green (520 cases). The dispatch-suite integration tests drive real epoch advances (the fixture now deployssysio.systemand satisfies the emissions gate, per theemissions_testsrecipe) covering: pre-deadline advances leave rows untouched; PENDING → EXPIRED + compaction → retention → erased; from-WIRE expiry refunds the full escrow fee-exempt; the COMPLETED happy path ages out to steady state; 33 due rows against the 32-row budget prove the bound; the oversized-UIC and candidate-cap rails hold.swap-non-native-tokensfailure was an outpost-side USDT provisioning revert, second-in-block behind a concurrent provisioning tx) + control run 29948649459 (same branch combination, that flow green — demonstrating the race is run-local and pre-existing; ticket to follow). Includes the 35-minute emissions soak: hundreds of advances with the inline sweep, no stall.Companions: Wire-Network/wire-libraries-ts#53 (types regen) + Wire-Network/wire-tools-ts#38 (harness config); the gate was dispatched with all three branch overrides.