feat: add LSPS7 (Channel Lease Extensions) client handler#4450
feat: add LSPS7 (Channel Lease Extensions) client handler#4450kaloudis wants to merge 34 commits intolightningdevkit:mainfrom
Conversation
We add the first LSPS1 integration test. This is based on the unfinished work in lightningdevkit#3864, but rebased to account for the new ways we now do integration test setup.
.. for which we got warnings
We previously considered tracking payment confirmations as part of the handler. However, we can considerably simplify our logic if we stick with the current approach of having the LSPs track the payment status and update us when prompted through events.
Now that we don't do on-chain tracking in LSPS1, we can drop quite a few `LiquidityManager` parameters and generics, which were only added in anticipation of tracking on-chain state. Signed-off-by: Elias Rohrer <dev@tnull.de>
We move the `PeerState` related types to a new module. In the following commits we'll bit-by-bit drop the `pub(super)`s introduced here, asserting better separation of state and logic going forward.
.. we will re-add a proper state machine in a later commit, but for now we can just drop all of this half-baked logic that doesn't actually do anything.
.. requiring less access to internals
Previously, we'd directly access the internal `outbound_` map of `PeerState`. Here we refactor the code to avoid this. Note this also highlighted a bug in that we currently don't actually update/persist the order state in `update_order_state`. We don't fix this here, but just improve isolation for now, as all state update behavior will be reworked later.
We introduce two new methods on `PeerState` to avoid direct access to the internal `pending_requests` map.
The `OutboundChannel` construct simply wrapped `ChannelOrder` which we can now simply use directly.
We here remember and update the order state and channel details in `ChannelOrder`
Since we by now have the `TimeProvider` trait, we might as well use it in `LSPS1ServiceHandler` instead of requiring the user to provide a `created_at` manually. Signed-off-by: Elias Rohrer <dev@tnull.de>
In the future we might want to inline the fields in `LSPS1ServiceConfig` (especially once some are added that we'd want to always/never set for the user), but for now we just make the `supported_options` field in `LSPS1ServiceConfig` required, avoiding some dangerous `unwrap`s.
Previously, we'd use an event to have the user check the order status and then call back in. As we already track the order status, we here change that to a model where we respond immediately based on our state and have the user/LSP update that state whenever it detects a change (e.g., a received payment, reorg, etc.). In the next commmit we will add/modify the corresponding API methods to do so.
We add the serializations for all types that will be persisted as part of the `PeerState`.
We follow the model already employed in LSPS2/LSPS5 and implement state pruning and persistence for `LSPS1ServiceHandler` state. Signed-off-by: Elias Rohrer <dev@tnull.de>
.. we read the persisted state in `LiquidityManager::new` Signed-off-by: Elias Rohrer <dev@tnull.de>
Co-authored by Claude AI
As per spec, we check that the user provides at least one payment detail *and* that they don't provide onchain payment details if `refund_onchain_address` is unset.
.. as there's no need to do so.
We add a method that allows the LSP to signal to the client the token they used was invalid. We use the `102` error code as proposed in lightning/blips#68.
We test the just-added API. Co-authored by Claude AI
This refactors `ChannelOrder` to use an internal state machine enum `ChannelOrderState` that: - Encapsulates state-specific data in variants (e.g., `channel_info` only available in `CompletedAndChannelOpened`) - Provides type-safe state transitions - Replaces the generic `update_order_status` API with specific transition methods: `order_payment_received`, `order_channel_opened`, and `order_failed_and_refunded` The state machine has four states: - `ExpectingPayment`: Initial state, awaiting payment - `OrderPaid`: Payment received, awaiting channel open - `CompletedAndChannelOpened`: Terminal state with channel info - `FailedAndRefunded`: Terminal state for failed/refunded orders Co-Authored-By: HAL 9000 Signed-off-by: Elias Rohrer <dev@tnull.de>
Add two new integration tests to cover the new public API methods: - `lsps1_order_state_transitions`: Tests the full flow of `order_payment_received` followed by `order_channel_opened`, verifying that payment states are updated correctly and channel info is returned after the channel is opened. - `lsps1_order_failed_and_refunded`: Tests the `order_failed_and_refunded` method, verifying that payment states are set to Refunded. Co-Authored-By: HAL 9000
Add `lsps1_expired_orders_are_pruned_and_not_persisted` test that verifies: - Orders with expired payment details (expires_at in the past) are accessible before persist() is called - After persist() is called, expired orders in ExpectingPayment state are pruned and no longer accessible - Pruned orders are not recovered after restart, confirming that the pruning also removes the persisted state Co-Authored-By: HAL 9000
The bLIP-51 specification defines a `HOLD` intermediate payment state: - `EXPECT_PAYMENT` -> `HOLD` -> `PAID` (success path) - `EXPECT_PAYMENT` -> `REFUNDED` (failure before payment) - `HOLD` -> `REFUNDED` (failure after payment received) This commit adds the `Hold` variant to `LSPS1PaymentState` and updates the state machine transitions: - `payment_received()` now sets payment state to `Hold` (not `Paid`) - `channel_opened()` transitions payment state from `Hold` to `Paid` - Tests updated to verify the correct state at each transition This allows LSPs to properly communicate when a payment has been received but the channel has not yet been opened (e.g., Lightning HTLC held, or on-chain tx detected but channel funding not published). Co-Authored-By: HAL 9000
Turns out this was another variant we didn't actually use anywhere. So we're dropping it.
We previously had no way to reject requests in case the LSP requires onchain payment while the client not providing `refund_onchain_address`. Here we add a method allowing to do so.
Add per-peer and global rate limiting to `LSPS1ServiceHandler` to prevent resource exhaustion, mirroring the existing LSPS2 pattern. Introduce `MAX_PENDING_REQUESTS_PER_PEER` (10), `MAX_TOTAL_PENDING_REQUESTS` (1000), and `MAX_TOTAL_PEERS` (100000) constants and enforce them in `handle_create_order_request`. Rejected requests receive a `CreateOrderError` with `LSPS0_CLIENT_REJECTED_ERROR_CODE`. A `total_pending_requests` atomic counter tracks the global count, and a `verify_pending_request_counter` debug assertion ensures it stays in sync. Co-Authored-By: HAL 9000
Signed-off-by: Elias Rohrer <dev@tnull.de>
|
👋 Thanks for assigning @tnull as a reviewer! |
There was a problem hiding this comment.
Oh wow, will need to re-read LSPS7 to refresh my understanding, in particular if there are still things left that to address on the spec level to allow integration with the other specs and splicing.
For now CI seems unhappy and you might need to account for some interface changes. Although, if you rebase it might make sense to intermittently rebase on #4282 as this is large related change that is somewhat close to landing (i.e., will likely introduce further conflicts when it lands).
|
👋 The first review has been submitted! Do you think this PR is ready for a second reviewer? If so, click here to assign a second reviewer. |
d53e44f to
43df0f2
Compare
Implement bLIP-57 / LSPS7 client-side support in lightning-liquidity. LSPS7 allows extending the lifetime of LSP-purchased channels. Three JSON-RPC methods: - lsps7.get_extendable_channels: list channels eligible for extension - lsps7.create_order: create extension order with payment info - lsps7.get_order: poll order status Reuses LSPS1PaymentInfo for the payment object as specified. Client-only scope (no service handler).
43df0f2 to
37f66f3
Compare
|
@tnull rebased and CI issues addressed |
Implement bLIP-57 / LSPS7 client-side support in lightning-liquidity. LSPS7 allows extending the lifetime of LSP-purchased channels.
Three JSON-RPC methods:
lsps7.get_extendable_channels: list channels eligible for extensionlsps7.create_order: create extension order with payment infolsps7.get_order: fetch order statusReuses LSPS1PaymentInfo for the payment object as specified. Client-only scope (no service handler).
The full spec can be found here: lightning/blips#57
This has been tested end-to-end by extending channels through the ZEUS LSP.