From 10e6d61e729a61851fd483ea462e1ce0e2336edb Mon Sep 17 00:00:00 2001 From: Ayla Greystone Date: Mon, 9 Jun 2025 12:18:47 -0400 Subject: [PATCH 1/2] bLIP-0052: Add ongoing proportional fees --- blip-0052.md | 44 ++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 40 insertions(+), 4 deletions(-) diff --git a/blip-0052.md b/blip-0052.md index 46f266b..024d249 100644 --- a/blip-0052.md +++ b/blip-0052.md @@ -224,6 +224,7 @@ Example `lsps2.get_info` result: { "min_fee_msat": "546000", "proportional": 1200, + "ongoing_proportional": 1000, "valid_until": "2023-02-23T08:47:30.511Z", "min_lifetime": 1008, "max_client_to_self_delay": 2016, @@ -234,6 +235,7 @@ Example `lsps2.get_info` result: { "min_fee_msat": "1092000", "proportional": 2400, + "ongoing_proportional": 500, "valid_until": "2023-02-27T21:23:57.984Z", "min_lifetime": 1008, "max_client_to_self_delay": 2016, @@ -250,8 +252,8 @@ The LSP MAY return an empty array; in which case, the client currently cannot use JIT Channels with this LSP. An `opening_fee_params` object describes how much the LSP will charge for a -channel open, what payment sizes it will accept, and until when the described -parameters will be considered valid. +channel open, what payment sizes it will accept, ongoing fees for subsequent +payments, and until when the described parameters will be considered valid. An `opening_fee_params` object MUST have all of the following fields, and MUST NOT have any additional fields: @@ -267,6 +269,12 @@ and MUST NOT have any additional fields: `min_fee_msat` is paid instead of the proportional times payment size divided by 1 million. [][] +* `ongoing_proportional` is a parts-per-million number that describes how many + millisatoshis to charge for every 1 million millisatoshis of payment + size for each payment after the first payment that opened the channel. + This fee is charged on every forwarded payment through the channel after + the initial channel-opening payment. + [][] * `valid_until` is a datetime (as an ISO8601 string) up to which this specific `opening_fee_params` is valid, and also serves as the timeout for the JIT Channel flow, if this particular object is selected. @@ -294,8 +302,8 @@ and MUST NOT have any additional fields: to the client, not including the forwarding fees of nodes along the way. * `promise` is an arbitrary LSP-generated string that proves to the LSP that it has promised a specific `opening_fee_params` with the specific - `min_fee_msat`, `proportional`, `valid_until`, `min_lifetime`, - `max_client_to_self_delay`, `min_payment_size_msat`, and + `min_fee_msat`, `proportional`, `ongoing_proportional`, `valid_until`, + `min_lifetime`, `max_client_to_self_delay`, `min_payment_size_msat`, and `max_payment_size_msat`. > **Rationale** A "minimum" fee is used instead of an additive base fee as @@ -553,6 +561,7 @@ Example `lsps2.buy` request parameters: "opening_fee_params": { "min_fee_msat": "546000", "proportional": 1200, + "ongoing_proportional": 1000, "valid_until": "2023-02-23T08:47:30.511Z", "min_lifetime": 1008, "max_client_to_self_delay": 2016, @@ -1029,6 +1038,33 @@ selected. LSPs MUST consider this alias not just for forwarded payments, but also for onion messages. +#### Ongoing Proportional Fees + +For all payments forwarded through the channel after the initial +channel-opening payment, the LSP MUST charge an ongoing proportional +fee as specified in the `ongoing_proportional` field from the +`opening_fee_params` used to open the channel. + +The ongoing fee for each payment is computed as: + + ongoing_fee = ((payment_amount_msat * ongoing_proportional) + 999999) / 1000000 + +Where `payment_amount_msat` is the amount being forwarded to the client, +and `ongoing_proportional` is the value from the `opening_fee_params`. + +* All numbers MUST be computed using the same overflow detection rules + specified in the "Computing The `opening_fee`" section. +* The LSP MUST deduct this `ongoing_fee` from each forwarded payment. +* The LSP MUST include an `extra_fee` TLV (type 65537) in the + `update_add_htlc` message for each payment that has ongoing fees + deducted, with the value set to the computed `ongoing_fee`. +* The client MUST accept these fee deductions and validate that the + `extra_fee` matches the expected `ongoing_fee` calculation. + +This ongoing fee allows LSPs to distribute the cost of channel opening +across the lifetime of the channel rather than charging the full amount +upfront on the first payment. + []: ./blip-0050.md#link-lsps0msat []: ./blip-0050.md#link-lsps0ppm []: ./blip-0050.md#link-lsps0datetime From 6396f08db4f5040de0a24cb7a209bb2405985289 Mon Sep 17 00:00:00 2001 From: Ayla Greystone Date: Tue, 10 Jun 2025 08:54:24 -0400 Subject: [PATCH 2/2] Use backwards compat. flag instead of new param --- blip-0052.md | 47 ++++++++++++++++++++++++++--------------------- 1 file changed, 26 insertions(+), 21 deletions(-) diff --git a/blip-0052.md b/blip-0052.md index 024d249..8e4d983 100644 --- a/blip-0052.md +++ b/blip-0052.md @@ -224,7 +224,6 @@ Example `lsps2.get_info` result: { "min_fee_msat": "546000", "proportional": 1200, - "ongoing_proportional": 1000, "valid_until": "2023-02-23T08:47:30.511Z", "min_lifetime": 1008, "max_client_to_self_delay": 2016, @@ -235,7 +234,6 @@ Example `lsps2.get_info` result: { "min_fee_msat": "1092000", "proportional": 2400, - "ongoing_proportional": 500, "valid_until": "2023-02-27T21:23:57.984Z", "min_lifetime": 1008, "max_client_to_self_delay": 2016, @@ -243,7 +241,8 @@ Example `lsps2.get_info` result: "max_payment_size_msat": "1000000", "promise": "abcdefghijklmnopqrstuvwxyz" } - ] + ], + "ongoing_proportional": true, } ``` The `opening_fee_params_menu` is an array of `opening_fee_params` @@ -252,8 +251,8 @@ The LSP MAY return an empty array; in which case, the client currently cannot use JIT Channels with this LSP. An `opening_fee_params` object describes how much the LSP will charge for a -channel open, what payment sizes it will accept, ongoing fees for subsequent -payments, and until when the described parameters will be considered valid. +channel open, what payment sizes it will accept, and until when the described +parameters will be considered valid. An `opening_fee_params` object MUST have all of the following fields, and MUST NOT have any additional fields: @@ -269,12 +268,6 @@ and MUST NOT have any additional fields: `min_fee_msat` is paid instead of the proportional times payment size divided by 1 million. [][] -* `ongoing_proportional` is a parts-per-million number that describes how many - millisatoshis to charge for every 1 million millisatoshis of payment - size for each payment after the first payment that opened the channel. - This fee is charged on every forwarded payment through the channel after - the initial channel-opening payment. - [][] * `valid_until` is a datetime (as an ISO8601 string) up to which this specific `opening_fee_params` is valid, and also serves as the timeout for the JIT Channel flow, if this particular object is selected. @@ -302,8 +295,8 @@ and MUST NOT have any additional fields: to the client, not including the forwarding fees of nodes along the way. * `promise` is an arbitrary LSP-generated string that proves to the LSP that it has promised a specific `opening_fee_params` with the specific - `min_fee_msat`, `proportional`, `ongoing_proportional`, `valid_until`, - `min_lifetime`, `max_client_to_self_delay`, `min_payment_size_msat`, and + `min_fee_msat`, `proportional`, `valid_until`, `min_lifetime`, + `max_client_to_self_delay`, `min_payment_size_msat`, and `max_payment_size_msat`. > **Rationale** A "minimum" fee is used instead of an additive base fee as @@ -542,6 +535,10 @@ fn compute_opening_fee(payment_size_msat: u64, > overflow-detecting multiplication routine is optimized away > and replaced with a simple overflow-flag check. +The `ongoing_proportional` field is a boolean that indicates whether +the LSP will charge the agreed upon proportional fee on subsequent +payments after the initial channel open. + ### 2. Request JIT Channel The client constructs a request body for a `lsps2.buy` request, @@ -561,7 +558,6 @@ Example `lsps2.buy` request parameters: "opening_fee_params": { "min_fee_msat": "546000", "proportional": 1200, - "ongoing_proportional": 1000, "valid_until": "2023-02-23T08:47:30.511Z", "min_lifetime": 1008, "max_client_to_self_delay": 2016, @@ -569,7 +565,8 @@ Example `lsps2.buy` request parameters: "max_payment_size_msat": "1000000", "promise": "abcdefghijklmnopqrstuvwxyz" }, - "payment_size_msat": "42000" + "ongoing_proportional": true, + "payment_size_msat": "42000", } ``` @@ -582,6 +579,12 @@ prove that it previously promised the specified `opening_fee_params`. LSPs MUST check that the `opening_fee_params.valid_until` is not a past datetime. +`ongoing_proportional` is the boolean acquired from the previous step. +Clients MUST provide the same value from a result of a `lsps2.get_info` +call. +LSPs MUST check that the `ongoing_proportional` field matches the mode +of operation they intend to use. + `payment_size_msat` is an *optional* amount denominated in millisatoshis that the client wants to receive [][]: @@ -623,6 +626,8 @@ The following errors are specified for `lsps2.buy`: and the LSP hit an overflow error while calculating the `opening_fee`, **OR** the LSP has insufficient incoming liquidity from the public network to receive the `payment_size_msat`. +* `invalid_ongoing_proportional` (204) - the `ongoing_proportional` field + did not match the expected value. * [LSPS0.client_rejected_error][] (1) - The LSP rejected the client. If there were no errors, the LSP then provides a normal @@ -1040,17 +1045,17 @@ also for onion messages. #### Ongoing Proportional Fees -For all payments forwarded through the channel after the initial -channel-opening payment, the LSP MUST charge an ongoing proportional -fee as specified in the `ongoing_proportional` field from the -`opening_fee_params` used to open the channel. +If `ongoing_proportional` is true then for all payments forwarded through +the channel after the initial channel-opening payment, the LSP MUST charge +an ongoing proportional fee as specified in the `proportional` field from +the `opening_fee_params` used to open the channel. The ongoing fee for each payment is computed as: - ongoing_fee = ((payment_amount_msat * ongoing_proportional) + 999999) / 1000000 + ongoing_fee = ((payment_amount_msat * proportional) + 999999) / 1000000 Where `payment_amount_msat` is the amount being forwarded to the client, -and `ongoing_proportional` is the value from the `opening_fee_params`. +and `proportional` is the value from the `opening_fee_params`. * All numbers MUST be computed using the same overflow detection rules specified in the "Computing The `opening_fee`" section.