Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion src/liquidity/service/lsps2.rs
Original file line number Diff line number Diff line change
Expand Up @@ -448,7 +448,8 @@ where
let over_provisioning_msat = (amt_to_forward_msat
* service_config.channel_over_provisioning_ppm as u64)
/ 1_000_000;
let channel_amount_sats = (amt_to_forward_msat + over_provisioning_msat) / 1000;
let channel_amount_sats =
(amt_to_forward_msat + over_provisioning_msat).div_ceil(1000);
Comment on lines +451 to +452

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The div_ceil fix looks good, and I like that the funds check (L462) and create_channel use the same variable. One caveat: the channel isn't guaranteed to have enough usable capacity for the forward.

With channel_over_provisioning_ppm = 0, the channel is sized to exactly ceil(forward) sats and funded entirely by the LSP (push_msat = 0). The LSP still needs to account for the client's reserve (~1% by default), commitment fees, and anchor outputs, so the HTLC may not fit.

Could we either scope the PR description to fixing sub-sat truncation, or account for the reserve and fees explicitly and document the minimum sensible channel_over_provisioning_ppm?

let cur_anchor_reserve_sats =
total_anchor_channels_reserve_sats(&self.channel_manager, &self.config);
let spendable_amount_sats =
Expand Down
16 changes: 11 additions & 5 deletions tests/integration_tests_rust.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3837,11 +3837,18 @@ async fn unified_send_receive_bip21_uri() {

#[tokio::test(flavor = "multi_thread", worker_threads = 1)]
async fn lsps2_client_service_integration() {
do_lsps2_client_service_integration(true).await;
do_lsps2_client_service_integration(false).await;
do_lsps2_client_service_integration(true, 100_000_000).await;
do_lsps2_client_service_integration(false, 100_000_000).await;
}

async fn do_lsps2_client_service_integration(client_trusts_lsp: bool) {
#[tokio::test(flavor = "multi_thread", worker_threads = 1)]
async fn lsps2_jit_channel_rounds_fractional_msat_up() {
// A forward amount that is not a whole number of satoshis must result in a channel large
// enough to carry it, i.e., the channel amount needs to be rounded up, not down.
do_lsps2_client_service_integration(false, 100_000_100).await;
}

async fn do_lsps2_client_service_integration(client_trusts_lsp: bool, jit_amount_msat: u64) {
let (bitcoind, electrsd) = setup_bitcoind_and_electrsd();
let esplora_url = format!("http://{}", electrsd.esplora_url.as_ref().unwrap());

Expand Down Expand Up @@ -3918,7 +3925,6 @@ async fn do_lsps2_client_service_integration(client_trusts_lsp: bool) {

let invoice_description =
Bolt11InvoiceDescription::Direct(Description::new(String::from("asdf")).unwrap());
let jit_amount_msat = 100_000_000;

println!("Generating JIT invoice!");
let jit_invoice = client_node
Expand Down Expand Up @@ -3951,7 +3957,7 @@ async fn do_lsps2_client_service_integration(client_trusts_lsp: bool) {
let expected_channel_overprovisioning_msat =
(expected_received_amount_msat * channel_over_provisioning_ppm as u64) / 1_000_000;
let expected_channel_size_sat =
(expected_received_amount_msat + expected_channel_overprovisioning_msat) / 1000;
(expected_received_amount_msat + expected_channel_overprovisioning_msat).div_ceil(1000);
let channel_value_sats = client_node.list_channels().first().unwrap().channel_value_sats;
assert_eq!(channel_value_sats, expected_channel_size_sat);

Expand Down
Loading