F0b: index cambiatus.es::expire closes as status 'expired' (land before contract deploy) - #64
Merged
Merged
Conversation
contracts #47 added expire(order_ref) — the permissionless backstop that refunds the buyer once a deposit outlives ESCROW_EXPIRY_SECONDS — and the indexer never heard of it, leaving the replica row 'open' forever after the money moved (permanent only_in_db drift, order never settles). expire routes through the same closeDeposit path as release/refund, so the replay guards (status = 'open' scoped, created_block bound) apply unchanged. It closes as its own status, 'expired', not 'refunded': the counterparty going silent and the backstop firing must stay distinguishable from the seller refunding, for the P3.5 escalation work. closed_by records the buyer for an expired close, taken from the row being closed rather than the payload: expire is permissionless (no require_auth, so the authorization array can even be empty) and whoever fires it gains nothing — the contract pays the buyer regardless, so the buyer is the one account every expire provably pays. setminimum is deliberately NOT indexed: it writes the mindeposit config table (configuration, not money movement) — noted at the updater registration for whoever builds the P3.2/P3.3 buy flow.
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.
F0b (1/3) — Index
cambiatus.es::expirecloses as statusexpired🔴 Land this before the hardened escrow contract deploys. contracts #47 added
expire(closes a deposit, erases the row, pays the buyer) and the indexer doesn't know it exists. Unindexed, the firstexpireleaves the replica rowopenforever and the hourly reconciler reports permanentonly_in_dbdrift — the exact divergence the reconciliation packet exists to detect, created by construction.Companion PRs: backend
fix/escrow-expired-status(status validation + payout reconciliation) and contractsfix/escrow-docs-offchain(DEPLOYMENT.md).Changes
src/updaters.js— registerscambiatus.es::expirebetweenrefundandsweep, with a comment recording thatsetminimumis deliberately not indexed (configuration, not money movement; the reconciler reads nothing from it; the P3.2/P3.3 buy flow reads themindepositrow from chain when validating amounts).src/updaters/escrow.js— newexpire()updater routes through the existingcloseDepositwith status'expired', so the replay guards (status = 'open'scope,created_block <= action blockbound) apply unchanged — same UPDATE, verified.expiredis distinct fromrefundedon purpose: the P3.5 escalation work needs to tell "seller refunded" from "counterparty went silent and the backstop fired".closed_byrecords the buyer (from the row being closed, viaCOALESCE($5, buyer_id)with NULL passed forexpired), not the action's actor.expireis permissionless —payload.authorizationcan be empty from a programmatic client, soauthorization[0].actorwould crash; and the actor says nothing about where the money went — the contract always pays the buyer, which is what reconciliation needs.Acceptance (local chain,
make node-fresh && make build && make bootstrapon contracts master — deploys the hardened escrow)Deposit
order_ref=777001(carol→dave, arbiter alice, 10 TST); short-expiry wasm (ESCROW_EXPIRY_SECONDS=3, the test-suite shape) deployed;expirefired by third partyeve; production wasm restored (code hash matches checked-inescrow.wasmbefore and after):Chain confirms: deposit erased, carol refunded 10 TST, inline transfer
cambiatus.es → carol 10 TST.Replay (safe rewind:
UPDATE _index_state SET block_number=1996 … WHERE id=1+DELETE FROM _processed_actions WHERE global_seq=2064, then reindex):The deleted ledger row forced the replay through
closeDeposititself, and thestatus='open'+created_blockbound made it a no-op — the two-layer guard holds for the new status path.yarn format(StandardJS) clean.