From 719c320bb8a07875ae6732da08f487bb8fe19ed6 Mon Sep 17 00:00:00 2001 From: Evan Kaloudis Date: Mon, 13 Jan 2025 23:47:49 -0500 Subject: [PATCH 1/2] bLIP-57: Channel Lease Extensions (LSPS7) --- blip-0057.md | 241 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 241 insertions(+) create mode 100644 blip-0057.md diff --git a/blip-0057.md b/blip-0057.md new file mode 100644 index 0000000..25d863c --- /dev/null +++ b/blip-0057.md @@ -0,0 +1,241 @@ +``` +bLIP: 57 +Title: LSPS7: Channel Lease Extensions +Status: Draft +Author: Evan Kaloudis +Created: 2025-01-10 +License: MIT +``` + +# Channel Lease Extensions (LSPS7) + +LSPS7 requires [LSPS0](./blip-0050.md) and therefore [BOLT8](https://github.com/lightning/bolts/blob/master/08-transport.md) as a transport layer. The payment object structure is taken from [LSPS1][]. + +## Motivation + +The goal of this specification is to provide a standardized LSP API for wallets to extend the lifetime of a channel purchased from an LSP. + +## Copyright + +This bLIP is licensed under the MIT license. + +## Order Flow Overview + +* Client calls `lsps7.get_extendable_channels` to get the LSP's options. +* Client calls `lsps7.create_order` to create an order. +* Client pays the order either on-chain or off-chain. +* LSP extends the expiry time of the channel as soon as the payment is confirmed. + + +## API + +### 1. lsps7.get_extendable_channels + +| JSON-RPC Method | lsps7.get_extendable_channels | +|---------------- |----------- | +| Idempotent | Yes | + + +`lsps7.get_extendable_channels` is the entrypoint for each client using the API. It lists all options in a dictionary. + +The client SHOULD call `lsps7.get_extendable_channels` first. + +**Request** No parameters needed. + +**Response** + +```JSON +{ + "extendable_channels": [ + { + "original_order": { + "id": "3c2ef2b-195f-0695-b72ef-3929b9138e04", + "service": "LSPS1" + }, + "extension_order_ids": [ + "142b3ef8-2eca-6135-7f74-7d1afda8b835", + "39afg2a3-7cf7-f3b6-056e-43f0d5b61f3f" + ], + "short_channel_id": "871428x964x0", + "max_channel_extension_expiry_blocks": 300, + "expiration_block": 839230 + } + ] +} +``` + +`extendable_channels` is an array of channels the LSP will allow you to extend the expiry of. The structure of the `extendable_channel` object can be found in section [3. Extendable Channel](#3-extendable-channel). + + +**Errors** No additional errors are defined for this method. + +### 2. lsps7.create_order + +| JSON-RPC Method | lsps7.create_order | +|-------------------- |------------------- | +| Idempotent | No | + + +The request is constructed depending on the client's needs. + +**Request** + +```json +{ + "short_channel_id": "871428x964x0", + "channel_extension_expiry_blocks": 144, + "token": "", + "refund_onchain_address": "bc1qvmsy0f3yyes6z9jvddk8xqwznndmdwapvrc0xrmhd3vqj5rhdrrq6hz49h" +} +``` + +- `short_channel_id` <[LSPS0.scid][]> Short Channel Identifier (SCID) of the channel to extend. +- `channel_extension_expiry_blocks ` How long to extend the lease of the channel in block time. + - MUST be 1 or greater. + - MUST be below or equal to `max_channel_extension_expiry_blocks` returned from the `get_extendable_channels` call. +- `token ` Field for arbitrary data like a coupon code or a authentication token. + - Client MAY omit this field. +- `refund_onchain_address ` Address where the LSP will send the funds if the order fails. + - Client MAY omit this field. + +**Response** + +```json +{ + "order_id": "bb4b5d0a-8334-49d8-9463-90a6d413af7c", + "channel_extension_expiry_blocks": 144, + "new_channel_expiry_block": 839374, + "token": "", + "created_at": "2012-04-23T18:25:43.511Z", + "order_state": "CREATED", + "payment": { + "bolt11": { + "state": "EXPECT_PAYMENT", + "expires_at": "2015-01-25T19:29:44.612Z", + "fee_total_sat": "8888", + "order_total_sat": "2008888", + "invoice" : "lnbc252u1p3aht9ysp580g4633gd2x9lc5al0wd8wx0mpn9748jeyz46kqjrpxn52uhfpjqpp5qgf67tcqmuqehzgjm8mzya90h73deafvr4m5705l5u5l4r05l8cqdpud3h8ymm4w3jhytnpwpczqmt0de6xsmre2pkxzm3qydmkzdjrdev9s7zhgfaqxqyjw5qcqpjrzjqt6xptnd85lpqnu2lefq4cx070v5cdwzh2xlvmdgnu7gqp4zvkus5zapryqqx9qqqyqqqqqqqqqqqcsq9q9qyysgqen77vu8xqjelum24hgjpgfdgfgx4q0nehhalcmuggt32japhjuksq9jv6eksjfnppm4hrzsgyxt8y8xacxut9qv3fpyetz8t7tsymygq8yzn05" + }, + "onchain": { + "state": "EXPECT_PAYMENT", + "expires_at": "2015-01-25T19:29:44.612Z", + "fee_total_sat": "9999", + "order_total_sat": "2009999", + "address" : "bc1p5uvtaxzkjwvey2tfy49k5vtqfpjmrgm09cvs88ezyy8h2zv7jhas9tu4yr", + "min_fee_for_0conf": 253, + "min_onchain_payment_confirmations": 0, + "refund_onchain_address": "bc1qvmsy0f3yyes6z9jvddk8xqwznndmdwapvrc0xrmhd3vqj5rhdrrq6hz49h" + } + }, + "channel": { + "original_order": { + "id": "3c2ef2b-195f-0695-b72ef-3929b9138e04", + "service": "LSPS1" + }, + "extension_order_ids": [ + "142b3ef8-2eca-6135-7f74-7d1afda8b835", + "39afg2a3-7cf7-f3b6-056e-43f0d5b61f3f" + ], + "short_channel_id": "871428x964x0", + "max_channel_extension_expiry_blocks": 300, + "expiration_block": 839230 + }, +} +``` + +- `order_id ` Id of this specific order. + - MUST be unique. + - MUST be at most 64 characters long. + - SHOULD be a valid [UUID version 4](https://en.wikipedia.org/wiki/Universally_unique_identifier#Version_4_(random)) (aka random UUID). +- `funding_confirms_within_blocks ` Mirrored from the request. +- `token ` Mirrored from the request. + - MUST be an empty string if the token was not provided. +- `created_at` <[LSPS0.datetime][]> Datetime when the order was created. +- `order_state ` Current state of the order. + - `CREATED` Order has been created. Default value. + - `COMPLETED` LSP has published funding transaction. + - `FAILED` Order failed. +- `payment ` Contains everything about payments, see [4. Payment](#4-payment). +- `channel ` Contains information about the channel, see [3. Extendable Channel](#3-extendable-channel). + + +**Client** +- SHOULD validate that `channel.expiration_block` + `channel_extension_expiry_blocks` = `new_channel_expiry_block`. +- SHOULD validate the `fee_total_sat` is reasonable. +- MAY abort the flow here. + +**Errors** + +| Code | Message | Data | Description | +| ---- | ------- | ----------- | ---- | +| -32602 | Invalid params | {"property": %invalid_property%, "message": %human_message% } | Invalid method parameter(s). | +| 001 | Client rejected | {"message": %human_message% } | [LSPS0.client_rejected_error][] | +| 100 | Option mismatch | {"property": %option_mismatch_property%, "message": %human_message% } | The order doesnt match the options defined in `lsps7.get_extendable_channels` channel object. | + + +- LSP MUST validate the order against the options defined in the `lsps7.get_extendable_channels` channel object. LSP MUST return an `100` error in case of a mismatch. + - `%option_mismatch_property%` MUST be one of the fields in the `lsps7.get_extendable_channels` channel object. + - Example: `{ "property": "max_channel_extension_expiry_blocks" }`. + +- LSP MUST validate the request fields. LSP MUST return a `-32602` error in case of an invalid request field. + - `%invalid_property%` MUST be one of the fields in the request body. MUST use `.` to separate nested fields. + - Example: `{ "property": "channel_extension_expiry_blocks", "message": "Not an integer" }`. + +- LSP MUST validate the `token` field and return an error if the token is invalid. + +> **Rationale `token` validation** The client should be informed if the token is invalid. Ignoring the invalid token and creating an order without the potentially discount or other side effect is not good UX. Ignoring the invalid token will also NOT prevent anybody bruteforcing the token because the client will still detect if the LSP has given a discount. + +> **Rationale Client rejected** LSPs can reject a client for example for misbehaviour. LSPs can reject a node on two levels: Prevent a peer connection OR disable order creation. Preventing a peer connection might not work in case you still want to allow other functions to keep working, for example an existing channel. + + +### 2.1 lsps7.get_order + +| JSON-RPC Method | lsps7.get_order | +|---------------- |---------------- | +| Idempotent | Yes | + +The client MAY check the current status of the order at any point. + +**Request** + +```json +{ + "order_id": "bb4b5d0a-8334-49d8-9463-90a6d413af7c" +} +``` + +**Response** is the same as defined in `lsps7.create_order`. + +**Errors** + +| Code | Message | Data | Description | +| ------ | --------- | ------- | ----------------------------------------------------- | +| 101 | Not found | {} | Order with the requested order_id has not been found. | + + +### 3. Extendable channel + +The `extendable_channel` object returned in `lsps7.get_extendable_channels`, `lsps7.create_order`, and `lsps7.get_order`, has the following properties: + +- `original_order ` An *optional* property for the original order for the channel lease, including the `id ` to identify the order and the `service ` the purchase occured on. +- `extension_order_ids ` An *optional* list of order IDs for each time the channel lease has been extended. +- `short_channel_id` <[LSPS0.scid][]> Short Channel Identifier (SCID) of the channel. +- `max_channel_extension_expiry_blocks ` The maximum number of blocks a channel can be leased for. +- `expiration_block ` The block height at which the channel lease will expiry. May be marked as 0 if the user opened the channel to the LSP, and the LSP has no obligation to keep open. + - MUST be 0 or greater. + +> **Rationale `original_order`** The client MAY want to look up the original order for reference. + +> **Rationale `extension_order_ids`** The client MAY want to look up any one of the previous extensions orders for reference. + +### 4. Payment + +The `payment` object returned by `lsps7.create_order` and `lsps7.get_order`, mirrors the formatting of [LSPS1.payment][]. + + +[LSPS0.onchain_address]: ./blip-0050.md#link-lsps0onchain_address +[LSPS0.datetime]: ./blip-0050.md#link-lsps0datetime\ +[LSPS0.scid]: ./blip-0050.md#link-lsps0scid +[LSPS0.client_rejected_error]: ./blip-0050.md#link-lsps0client_rejected_error +[LSPS1]: ./blip-0051.md +[LSPS1.payment]: ./blip-0051.md#3-payment \ No newline at end of file From 2bde89e82cc2d4cd3b1bdae6ee1c082cf8881cae Mon Sep 17 00:00:00 2001 From: Evan Kaloudis Date: Sat, 26 Sep 2026 14:34:41 -0400 Subject: [PATCH 2/2] bLIP-57: address review feedback Add BOLT12 to the payment example and fix order_total_sat, which for an extension equals fee_total_sat. Add the 102 unrecognized or stale token error and only require token validation when a token is given. Fix the COMPLETED order state, which described a funding transaction. Define which SCID identifies a channel, allow LSPS2 channels by omitting original_order, and clarify extension_order_ids, max_channel_extension_expiry_blocks and original_order.service. Add payment rules requiring the LSP to persist the new expiry before releasing the preimage, and failing the HTLC as the refund. Add channel extension and splicing sections. Fix a trailing comma in the create_order example and a broken LSPS0.datetime link. --- blip-0057.md | 114 ++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 95 insertions(+), 19 deletions(-) diff --git a/blip-0057.md b/blip-0057.md index 25d863c..3ba3b54 100644 --- a/blip-0057.md +++ b/blip-0057.md @@ -15,6 +15,8 @@ LSPS7 requires [LSPS0](./blip-0050.md) and therefore [BOLT8](https://github.com/ The goal of this specification is to provide a standardized LSP API for wallets to extend the lifetime of a channel purchased from an LSP. +LSPS7 applies to any channel between the client and the LSP for which the LSP has committed to keep the channel open until a certain block height. This includes channels purchased via [LSPS1][], channels opened via [LSPS2][], and channels opened by other means. The lease is a property of the channel as a whole. See [6. Splicing](#6-splicing). + ## Copyright This bLIP is licensed under the MIT license. @@ -66,6 +68,9 @@ The client SHOULD call `lsps7.get_extendable_channels` first. `extendable_channels` is an array of channels the LSP will allow you to extend the expiry of. The structure of the `extendable_channel` object can be found in section [3. Extendable Channel](#3-extendable-channel). +**LSP** +- MUST only return channels between the client and the LSP. + **Errors** No additional errors are defined for this method. @@ -90,6 +95,7 @@ The request is constructed depending on the client's needs. ``` - `short_channel_id` <[LSPS0.scid][]> Short Channel Identifier (SCID) of the channel to extend. + - MUST be the `short_channel_id` of one of the channels returned by the most recent `lsps7.get_extendable_channels` call. - `channel_extension_expiry_blocks ` How long to extend the lease of the channel in block time. - MUST be 1 or greater. - MUST be below or equal to `max_channel_extension_expiry_blocks` returned from the `get_extendable_channels` call. @@ -113,14 +119,21 @@ The request is constructed depending on the client's needs. "state": "EXPECT_PAYMENT", "expires_at": "2015-01-25T19:29:44.612Z", "fee_total_sat": "8888", - "order_total_sat": "2008888", + "order_total_sat": "8888", "invoice" : "lnbc252u1p3aht9ysp580g4633gd2x9lc5al0wd8wx0mpn9748jeyz46kqjrpxn52uhfpjqpp5qgf67tcqmuqehzgjm8mzya90h73deafvr4m5705l5u5l4r05l8cqdpud3h8ymm4w3jhytnpwpczqmt0de6xsmre2pkxzm3qydmkzdjrdev9s7zhgfaqxqyjw5qcqpjrzjqt6xptnd85lpqnu2lefq4cx070v5cdwzh2xlvmdgnu7gqp4zvkus5zapryqqx9qqqyqqqqqqqqqqqcsq9q9qyysgqen77vu8xqjelum24hgjpgfdgfgx4q0nehhalcmuggt32japhjuksq9jv6eksjfnppm4hrzsgyxt8y8xacxut9qv3fpyetz8t7tsymygq8yzn05" }, + "bolt12": { + "state" : "EXPECT_PAYMENT", + "expires_at": "2015-01-25T19:29:44.612Z", + "fee_total_sat": "8888", + "order_total_sat": "8888", + "offer": "lno1qgsqvgnwgcg35z6ee2h3yczraddm72xrfua9uve2rlrm9deu7xyfzrcgqvp3pwq2q3shxerxzzfqyxdxxkjqxfpt9tz94zj79m4n99kwjddq92uqryuwsu4nt0t3lthfq02jzqla96dtkf4rew8edxw0p85swe89wd8ldekdl5j262n76qyl2qgzajm08clzr74z6ssy0qlvvp9f5kvrxf27yz4pcy99jge69kxu8ttsqt8gw8jsk5397zvvdf4lfd52paf73thcg6xf57xmvtdrwny5mn2r4jw2d5jzalqrq537mmt6u9qpqytzzql6zemrme07jqqwtza76lldcj9wgc0ccd4d2w584cdcx6szsuupvy" + }, "onchain": { "state": "EXPECT_PAYMENT", "expires_at": "2015-01-25T19:29:44.612Z", "fee_total_sat": "9999", - "order_total_sat": "2009999", + "order_total_sat": "9999", "address" : "bc1p5uvtaxzkjwvey2tfy49k5vtqfpjmrgm09cvs88ezyy8h2zv7jhas9tu4yr", "min_fee_for_0conf": 253, "min_onchain_payment_confirmations": 0, @@ -139,7 +152,7 @@ The request is constructed depending on the client's needs. "short_channel_id": "871428x964x0", "max_channel_extension_expiry_blocks": 300, "expiration_block": 839230 - }, + } } ``` @@ -147,13 +160,15 @@ The request is constructed depending on the client's needs. - MUST be unique. - MUST be at most 64 characters long. - SHOULD be a valid [UUID version 4](https://en.wikipedia.org/wiki/Universally_unique_identifier#Version_4_(random)) (aka random UUID). -- `funding_confirms_within_blocks ` Mirrored from the request. +- `channel_extension_expiry_blocks ` Mirrored from the request. +- `new_channel_expiry_block ` The block height at which the channel lease will expire once this order is completed. + - MUST be `channel.expiration_block` + `channel_extension_expiry_blocks`, where `channel.expiration_block` is the value at the time the order was created. - `token ` Mirrored from the request. - MUST be an empty string if the token was not provided. - `created_at` <[LSPS0.datetime][]> Datetime when the order was created. - `order_state ` Current state of the order. - `CREATED` Order has been created. Default value. - - `COMPLETED` LSP has published funding transaction. + - `COMPLETED` LSP has extended the channel lease. `channel.expiration_block` is equal to `new_channel_expiry_block`. - `FAILED` Order failed. - `payment ` Contains everything about payments, see [4. Payment](#4-payment). - `channel ` Contains information about the channel, see [3. Extendable Channel](#3-extendable-channel). @@ -166,11 +181,12 @@ The request is constructed depending on the client's needs. **Errors** -| Code | Message | Data | Description | -| ---- | ------- | ----------- | ---- | -| -32602 | Invalid params | {"property": %invalid_property%, "message": %human_message% } | Invalid method parameter(s). | -| 001 | Client rejected | {"message": %human_message% } | [LSPS0.client_rejected_error][] | -| 100 | Option mismatch | {"property": %option_mismatch_property%, "message": %human_message% } | The order doesnt match the options defined in `lsps7.get_extendable_channels` channel object. | +| Code | Message | Data | Description | +| ---- | ------- | ----------- | ---- | +| -32602 | Invalid params | {"property": %invalid_property%, "message": %human_message% } | Invalid method parameter(s). | +| 001 | Client rejected | {"message": %human_message% } | [LSPS0.client_rejected_error][] | +| 100 | Option mismatch | {"property": %option_mismatch_property%, "message": %human_message% } | The order doesnt match the options defined in `lsps7.get_extendable_channels` channel object. | +| 102 | Unrecognized or stale token | {} | The provided token was unrecognized or stale. | - LSP MUST validate the order against the options defined in the `lsps7.get_extendable_channels` channel object. LSP MUST return an `100` error in case of a mismatch. @@ -180,8 +196,9 @@ The request is constructed depending on the client's needs. - LSP MUST validate the request fields. LSP MUST return a `-32602` error in case of an invalid request field. - `%invalid_property%` MUST be one of the fields in the request body. MUST use `.` to separate nested fields. - Example: `{ "property": "channel_extension_expiry_blocks", "message": "Not an integer" }`. + - LSP MUST return a `-32602` error with `%invalid_property%` set to `short_channel_id` if `short_channel_id` does not identify a channel the LSP would return in `lsps7.get_extendable_channels`. -- LSP MUST validate the `token` field and return an error if the token is invalid. +- If the client provided a `token`, LSP MUST validate it and return a `102` error if the token is invalid. > **Rationale `token` validation** The client should be informed if the token is invalid. Ignoring the invalid token and creating an order without the potentially discount or other side effect is not good UX. Ignoring the invalid token will also NOT prevent anybody bruteforcing the token because the client will still detect if the LSP has given a discount. @@ -217,25 +234,84 @@ The client MAY check the current status of the order at any point. The `extendable_channel` object returned in `lsps7.get_extendable_channels`, `lsps7.create_order`, and `lsps7.get_order`, has the following properties: -- `original_order ` An *optional* property for the original order for the channel lease, including the `id ` to identify the order and the `service ` the purchase occured on. -- `extension_order_ids ` An *optional* list of order IDs for each time the channel lease has been extended. +- `original_order ` An *optional* property for the order through which the channel was originally purchased. + - `id ` The id of that order, as returned by the service it was purchased through. + - `service ` The service the channel was purchased through. MUST be the uppercase name of the LSPS spec, for example `LSPS1`. + - MUST be omitted if the channel was not opened through an order that has an id, for example channels opened via [LSPS2][] or channels opened by the client. +- `extension_order_ids ` An *optional* list of the `order_id`s of previous `lsps7.create_order` orders for this channel, oldest first. + - MUST only contain orders with `order_state` `COMPLETED`. + - MUST NOT contain the id of `original_order`. + - MUST be omitted or empty if the channel lease has not been extended before. - `short_channel_id` <[LSPS0.scid][]> Short Channel Identifier (SCID) of the channel. -- `max_channel_extension_expiry_blocks ` The maximum number of blocks a channel can be leased for. -- `expiration_block ` The block height at which the channel lease will expiry. May be marked as 0 if the user opened the channel to the LSP, and the LSP has no obligation to keep open. + - MUST be the SCID of the channel's current funding transaction. After a splice, this is the SCID of the splice transaction. + - MAY be an SCID alias if the funding transaction is not yet confirmed. + - MUST NOT be an intercept SCID such as the [LSPS2][] `jit_channel_scid`. +- `max_channel_extension_expiry_blocks ` The maximum number of blocks a single `lsps7.create_order` order MAY extend the lease by. +- `expiration_block ` The block height at which the channel lease will expire. May be marked as 0 if the user opened the channel to the LSP, and the LSP has no obligation to keep open. - MUST be 0 or greater. + - For channels opened via [LSPS2][], which does not define a lease duration, this is set by LSP policy. -> **Rationale `original_order`** The client MAY want to look up the original order for reference. +> **Rationale `original_order`** The client MAY want to look up the original order for reference. [LSPS2][] does not assign order ids, so there is nothing to reference for those channels. The channel is identified by `short_channel_id` alone. > **Rationale `extension_order_ids`** The client MAY want to look up any one of the previous extensions orders for reference. +> **Rationale `short_channel_id`** The client learns the SCID from `lsps7.get_extendable_channels` rather than choosing it, so client and LSP never disagree about which of a channel's identifiers to use. + ### 4. Payment -The `payment` object returned by `lsps7.create_order` and `lsps7.get_order`, mirrors the formatting of [LSPS1.payment][]. +The `payment` object returned by `lsps7.create_order` and `lsps7.get_order`, mirrors the formatting of [LSPS1.payment][], including the `bolt11`, `bolt12`, and `onchain` payment options, with the following changes: + +- `fee_total_sat` <[LSPS0.sat][]> The total fee the LSP will charge to extend the channel lease. +- `order_total_sat` <[LSPS0.sat][]> What the client needs to pay in total to extend the channel lease. + - MUST be equal to `fee_total_sat`. + +**LSP** + +For `bolt11` and `bolt12` payments: +- MUST NOT release the preimage before the new `expiration_block` has been persisted. +- MUST change the `payment.state` to `HOLD` when the payment arrived. +- If the channel lease has been extended successfully: + - MUST release the preimage and therefore complete the payment. + - MUST set the `payment.state` to `PAID`. +- If the lease could not be extended, the order expired, or shortly before the payment times out: + - MUST fail the payment. Failing the payment is the refund. + - MUST set the `payment.state` to `REFUNDED`. + +For `onchain` payments: +- If the lease could not be extended, MUST refund the client to `refund_onchain_address`. + +> **Rationale** Persisting the new expiry before releasing the preimage makes the payment and the extension atomic from the client's point of view. Extending a lease is a bookkeeping change on the LSP side, so the payment is expected to stay in `HOLD` only briefly. + +### 5. Channel Extension + +The LSP MUST extend the channel lease when: +- `payment.state` is `HOLD` (lightning) or `PAID` (on-chain). + +In case the extension succeeds: +- MUST set `channel.expiration_block` to `new_channel_expiry_block`. +- MUST set `order_state` to `COMPLETED`. +- MUST add `order_id` to `extension_order_ids`. + +In case the extension fails: +- MUST set `order_state` to `FAILED`. +- MUST issue a refund as described in [4. Payment](#4-payment). + +### 6. Splicing + +The lease applies to the channel as a whole, not to a particular portion of its capacity or balance. + +**LSP** +- MUST NOT change `expiration_block` as a result of a splice. +- MUST continue to return the channel in `lsps7.get_extendable_channels` after a splice, with `short_channel_id` set to the SCID of the new funding transaction. + +> **Rationale** Tracking separate expiries for liquidity added at different times would make it unclear which part of the balance is leased until when. An LSP that wants to charge for additional liquidity spliced into a leased channel can do so in the order that adds the liquidity. [LSPS0.onchain_address]: ./blip-0050.md#link-lsps0onchain_address -[LSPS0.datetime]: ./blip-0050.md#link-lsps0datetime\ +[LSPS0.datetime]: ./blip-0050.md#link-lsps0datetime [LSPS0.scid]: ./blip-0050.md#link-lsps0scid +[LSPS0.sat]: ./blip-0050.md#link-lsps0sat [LSPS0.client_rejected_error]: ./blip-0050.md#link-lsps0client_rejected_error [LSPS1]: ./blip-0051.md -[LSPS1.payment]: ./blip-0051.md#3-payment \ No newline at end of file +[LSPS1.payment]: ./blip-0051.md#3-payment +[LSPS2]: ./blip-0052.md