feat(payments): answer priced invocations in explicit-gating sessions with payment-required errors - #112
Draft
harsh04044 wants to merge 4 commits into
Draft
Conversation
…authorization store
…am's drop-cleanup
… with payment-required errors
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.
Part of #100
Stacked on #111 (the transparent middleware); diff base
d8eae4a, three commits. Retargets or rebases once #111 merges, same as that PR did over its own open parents.The second lifecycle where money moves: in an
explicit_gatingsession a priced invocation is answered immediately and dropped instead of held. First sighting gets-32042 Payment Requiredwith exactly one payment option through the targeted sender; a detached task verifies the payment and banks a single-use grant keyed by canonical invocation identity; a repeat during verification gets-32043 Payment Pending; a later retry with the same method and params claims the grant atomically and forwards the client's original message untouched,_metaincluded. A pricing-callback rejection answers-32000with nodatakey; a waiver forwards free. No registration and no client retry loop yet: those are the next two PRs.What's here
create_explicit_gating_middleware+ExplicitGatingMiddlewareParams(store injected, options shared with the transparent middleware), with 29 in-file tests driving the real seam chainAuthorizationStore::claim_or_set_pending+ClaimOrPending: the grant check-and-pop and the pending check-and-set in one critical section; the six shipped ops and their tests byte-identicalMockRelayPool(stateless first exchange pinned tag-for-tag with the original inner id, encrypted cycle, oversized re-inject, shutdown mid-verify, both middlewares registered partitioning by mode) and 4 conformance tests pinning the full error objects through the middleware's own builders[[test]]Cargo entry, per-PR CHANGELOG. No new dependency, no FFI change. 592 insertions across 9 tracked files plus the two new filesNote
ts runs
claim()thentrySetPending()back to back and gets the pair atomic free from run-to-completion. rs cannot: the detached verify grants from another OS thread, and the literal two-call port has a window where a settlement pops the pending between the calls, so the server mints a second invoice while a paid grant sits unclaimed. The composed op removes that interleaving; every observable outcome is one ts also produces. The race is measured, not argued: a two-thread barrier probe trips the two-call port roughly ten times per 100k contended rounds and the composed op never.Things you'll probably ask about
idthe client's inner request id when ts sends the event id? ts rewrites the inner id before its middleware runs and publishes the object as-is; the spec's examples show the request's own id, which is what rs emits. Interop holds because the ts client correlates byetag and restores the id before reading it. Filed upstream: ts's bytes break any client that correlates errors by JSON-RPC id.ttl: 0option treated as absent for the grant horizon? ts mints a grant that expires at birth while the invoice stays payable. ts's own verification-timeout rule already readsttl <= 0as absent; rs applies that sibling rule to the grant. Also filed upstream. The offer still advertises the option'sttlverbatim.-32042clears pending and spawns no verify, so nothing blocks a retry nothing was offered for; a failed-32043leaves pending alone, since it belongs to the live original cycle; the-32000arm cleared before sending. Mirrors ts's try/catch boundaries, and unifying them breaks one direction or the other; each arm has a watched-to-die mutation.send_response's pop-then-read on the transparent duplicate-drop path and degrades response mirroring on a paid response. A returned route proves no responder owns the event; every returned-nothing case has its own reclaimer.Heads up
Verification failure, timeout and pending loss are silent; the next matching invocation gets a fresh offer with a new invoice. A verbatim replay is indistinguishable from a retry by design and can burn a paid grant inside its window. Paying two live offers for one identity leaves one grant (a re-grant overwrites); upstream question. A failed first-offer publish costs the session its one-shot discovery and disclosure latches (sender contract). The store is in-memory and single-process.