Add FFOR receiver lifecycle and receipt recovery on 0.2.5 - #4
Draft
coreyphillips wants to merge 22 commits into
Draft
coreyphillips wants to merge 22 commits into
coreyphillips wants to merge 22 commits into
Conversation
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.
FFOR receiving needs the channel engine to retain real voucher claims while the receiver is offline. This draft ports the receiver lifecycle to rust-lightning v0.2.5 and adds one immutable, signed invoice assignment per supported epoch. It includes native persistence, witness acknowledgement and live-monitor checks before publication. It does not enable Bitkit's production provider or demonstrate an end-to-end offline payment.
Related to ldk-node #117 and ldk-node #118. Implementation and persistence contracts are documented in
FFOR.md.Receiver behavior
Invoice assignment and publication
The supported issuer uses a one-slot, non-hash-chained Variant D book. It derives amount, payment hash, receiver identity, settlement fees, inbound alias and CLTV from native ownership. The signed Init witness restriction must exactly match the registered and durably acknowledged witness set.
The invoice has one W -> S -> R route hint and no MPP support. Native verifies the public W-S announcement and directional update, signatures, distinct identities, canonical endpoint ordering, enabled state, amount bounds, fees and freshness. These signatures identify route terms; they do not prove current channel funding or liquidity. Private W-S channels without authenticated route evidence are not supported. BOLT11 hints remain advisory, with the signed witness restriction providing the honest settlement peer's admission guard.
Signing occurs outside locks. Native then rechecks the current phase, frozen pair, deadline, route binding and completed persistence before retaining the exact signed bytes. Requested expiry is capped using eight minutes per remaining block before admission closes, less the explicit safety margin. The actual watched monitor is locked through retention or final bounded publication, excluding pending off-chain persistence, known preimages, funding spends, pending funding changes and local commitment signing. The higher of manager and monitor height is used so delayed notifications cannot extend admission.
The storage getter returns exact bytes only after the latest native write completes. It supports historical recovery after expiry or close and does not grant display permission. The application must first durably confirm these bytes, the matching Pending payment and its protected confirmation marker. Final publication rechecks native and monitor state through a callback that performs only bounded in-memory publication. Retry, failed publication and restart cannot replace the assigned invoice. Custom Watch implementations default to refusing this operation until they implement the actual-monitor boundary. New issuance requires the std clock; no_std supports historical decoding.
Cooperative outcome journal
A new drain initializes a versioned journal with resolved and fulfilled bits per book slot, at most 483 slots. The stock revoke-and-ack removal of an inbound LocalRemoved HTLC records the exact owned voucher's slot at the same point it adds a fulfilled amount to the balance, with no new fallible step after the existing preflight. The journal is native accounting only, never derived from the signed settled bitmap or known preimages, and fulfilled is always a subset of resolved. A preimage learned after a signed failure protects the monitor but leaves the slot failed.
Restore validation requires exact bitmap lengths, zero padding, the subset rule and, per slot, either a resolved bit or the voucher still pending as a stock HTLC. The completion proof requires every slot resolved and every signed settled slot fulfilled, binds the journal into a distinct v2 digest domain, and carries it into the retained ClosedDrain under required-even field 14 with archive version 8. Legacy drains and archives keep the original domain, remain readable and never acquire invented outcomes; older readers reject journaled records instead of dropping them. The framing growth of a journaled Closed proof is charged inside the existing 512-byte terminal reservation.
ffor_receiver_voucher_outcomeis a historical, manager-instance-bound getter keyed by the current epoch context, one-based slot, exact payment hash and amount. It reports Fulfilled or Failed only from a fully finalized journal after the latest native write completed, including a fresh barrier after restore. Pending drains, force-closed epochs and legacy absence return no outcome. It changes no balances and emits no events. On-chain outcomes remain a separate unimplemented boundary.Repeated epochs on one channel
A new epoch on a channel with an existing registration is admitted only when the previous epoch is terminal without ambiguity: its book has an authenticated setup, no fence and no pending pre-init gate, and its drain is Closed with the retained completion hash or it is aborted without a drain; no owned inbound HTLC, holding-cell claim, pending finalized fulfill, in-flight or blocked monitor update, quiescence, splice or interactive session remains; and under the archive lock the archived record for the previous epoch is terminal with the same completion hash or abort reason, its runtime persistence requirement is complete, and archive capacity admits another record. The new book names its predecessor epoch under a required-even field. Restore requires the archived terminal predecessor record and rejects two unresolved records sharing a channel or funding identity; a book naming itself is rejected. Previous epochs stay readable by epoch id, including outcomes and invoices, and a witness receipt for a replaced epoch still imports into the original monitor without payment events. Reuse after a setup aborted before activation or a request the peer never accepted remains refused because the archive holds no durable abort evidence for those.
Recovery bounds
Native history is bounded to 64 records and 8 MiB, with at most 512 KiB per record and terminal transition capacity reserved before admission. Required serialized fields prevent old readers from dropping FFOR state. Immutable witness metadata uses schema 5; the fixed 373-byte acknowledgement field uses schema 6. Invoice assignment adds required field 12 and schema 7, bounding the signed string to 4096 bytes and its record to 8192 bytes. Local archive validation is not an authenticated storage envelope or rollback detector. The companion unpublished
lightning-fforcrate supplies shared canonical protocol and witness verification.Validation
Remaining work
Joining journaled outcomes to the application payment ledger, on-chain outcomes, production Node scheduling and bindings, private witness route support, settlement interoperability and process-stopped mobile payment tests remain incomplete. Reuse after a pre-activation abort still needs durable abort evidence in the archive. Application persistence remains a required part of the publication contract. Deployment margins and witness coverage need explicit policy. This PR remains a draft.