From 9d2d3687ad5bb7ab1fb60114e3f5c952d749bc5f Mon Sep 17 00:00:00 2001 From: Jeffrey Czyz Date: Wed, 20 Aug 2025 11:04:51 -0500 Subject: [PATCH 1/6] Re-phrase splice_channel error message --- lightning/src/ln/channel.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lightning/src/ln/channel.rs b/lightning/src/ln/channel.rs index 115d68acc14..fe2a1508b17 100644 --- a/lightning/src/ln/channel.rs +++ b/lightning/src/ln/channel.rs @@ -10574,7 +10574,7 @@ where if self.holder_commitment_point.current_point().is_none() { return Err(APIError::APIMisuseError { err: format!( - "Channel {} cannot be spliced, commitment point needs to be advanced once", + "Channel {} cannot be spliced until a payment is routed", self.context.channel_id(), ), }); From 8ef76e9c2a1c6401c5400d6a17718559c64ccdcd Mon Sep 17 00:00:00 2001 From: Jeffrey Czyz Date: Wed, 20 Aug 2025 11:07:10 -0500 Subject: [PATCH 2/6] Use WarnAndDisconnect to fail a splice When the current holder commitment point is unavailable for a channel, we can't splice the channel. Make sure to disconnect so that the channel is no longer quiescent. Otherwise, it cannot be used for payments. --- lightning/src/ln/channel.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lightning/src/ln/channel.rs b/lightning/src/ln/channel.rs index fe2a1508b17..4d8c9d083cb 100644 --- a/lightning/src/ln/channel.rs +++ b/lightning/src/ln/channel.rs @@ -10682,7 +10682,7 @@ where // TODO(splicing): Add check that we are the quiescence acceptor if self.holder_commitment_point.current_point().is_none() { - return Err(ChannelError::Warn(format!( + return Err(ChannelError::WarnAndDisconnect(format!( "Channel {} commitment point needs to be advanced once before spliced", self.context.channel_id(), ))); From fa9cb2e16fc62ad0c516c5b524de2a7977cc52d5 Mon Sep 17 00:00:00 2001 From: Jeffrey Czyz Date: Wed, 20 Aug 2025 11:15:49 -0500 Subject: [PATCH 3/6] Fix comment regarding holder commitment point --- lightning/src/ln/channel.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lightning/src/ln/channel.rs b/lightning/src/ln/channel.rs index 4d8c9d083cb..032881059c6 100644 --- a/lightning/src/ln/channel.rs +++ b/lightning/src/ln/channel.rs @@ -13835,7 +13835,7 @@ where } // If we're restoring this channel for the first time after an upgrade, then we require that the - // signer be available so that we can immediately populate the current commitment point. Channel + // signer be available so that we can immediately populate the next commitment point. Channel // restoration will fail if this is not possible. let holder_commitment_point = match (holder_commitment_point_next_opt, holder_commitment_point_pending_next_opt) { From 7a5a2a9c0c6dda65c27f136e9cb8a815ada9b757 Mon Sep 17 00:00:00 2001 From: Jeffrey Czyz Date: Wed, 20 Aug 2025 11:49:39 -0500 Subject: [PATCH 4/6] Set HolderCommitmentPoint::current_point on read When introducing HolderCommitmentPoint::current_point, the value was mistakenly not set when read except in the legacy case where the next point needed to be fetched. But in that case, it would have been read as None given it is a new field. --- lightning/src/ln/channel.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lightning/src/ln/channel.rs b/lightning/src/ln/channel.rs index 032881059c6..a30b3d58631 100644 --- a/lightning/src/ln/channel.rs +++ b/lightning/src/ln/channel.rs @@ -13841,7 +13841,7 @@ where match (holder_commitment_point_next_opt, holder_commitment_point_pending_next_opt) { (Some(next_point), pending_next_point) => HolderCommitmentPoint { next_transaction_number: holder_commitment_next_transaction_number, - current_point: None, + current_point: holder_commitment_point_current_opt, next_point, pending_next_point, }, From 3ae8c4a63a4ae74a54e7a8efbd1c787c3dc929de Mon Sep 17 00:00:00 2001 From: Jeffrey Czyz Date: Wed, 20 Aug 2025 12:28:05 -0500 Subject: [PATCH 5/6] Fetch HolderCommitmentPoint::current_point on read When reading HolderCommitmentPoint, attempt to fetch the current point if it wasn't serialized. This allows channels to be spliced without first needing to have the HolderCommitmentPoint advanced. Don't fail if it can't be fetch synchronously as the channel can still be spliced once it is advanced. --- lightning/src/ln/channel.rs | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/lightning/src/ln/channel.rs b/lightning/src/ln/channel.rs index a30b3d58631..f3ffd1c37a2 100644 --- a/lightning/src/ln/channel.rs +++ b/lightning/src/ln/channel.rs @@ -13837,11 +13837,26 @@ where // If we're restoring this channel for the first time after an upgrade, then we require that the // signer be available so that we can immediately populate the next commitment point. Channel // restoration will fail if this is not possible. - let holder_commitment_point = + let holder_commitment_point = { + let current_point = holder_commitment_point_current_opt.or_else(|| { + if holder_commitment_next_transaction_number == INITIAL_COMMITMENT_NUMBER { + None + } else { + // If the current point is not available then splicing can't be initiated + // until the next point is advanced and becomes the current point. + holder_signer + .get_per_commitment_point( + holder_commitment_next_transaction_number + 1, + &secp_ctx, + ) + .ok() + } + }); + match (holder_commitment_point_next_opt, holder_commitment_point_pending_next_opt) { (Some(next_point), pending_next_point) => HolderCommitmentPoint { next_transaction_number: holder_commitment_next_transaction_number, - current_point: holder_commitment_point_current_opt, + current_point, next_point, pending_next_point, }, @@ -13861,12 +13876,13 @@ where ); HolderCommitmentPoint { next_transaction_number: holder_commitment_next_transaction_number, - current_point: holder_commitment_point_current_opt, + current_point, next_point, pending_next_point: Some(pending_next_point), } }, - }; + } + }; Ok(FundedChannel { funding: FundingScope { From 1f8a7d78980e7bdd356f86de2e7ff96ae2ea7540 Mon Sep 17 00:00:00 2001 From: Jeffrey Czyz Date: Wed, 20 Aug 2025 17:28:54 -0500 Subject: [PATCH 6/6] Return ChannelError instead of calling expect --- lightning/src/ln/channel.rs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/lightning/src/ln/channel.rs b/lightning/src/ln/channel.rs index f3ffd1c37a2..29e778127c4 100644 --- a/lightning/src/ln/channel.rs +++ b/lightning/src/ln/channel.rs @@ -6984,10 +6984,12 @@ where .and_then(|funding_negotiation| funding_negotiation.as_funding()) .expect("Funding must exist for negotiated pending splice"); let transaction_number = self.holder_commitment_point.current_transaction_number(); - let commitment_point = self - .holder_commitment_point - .current_point() - .expect("current should be set after receiving the initial commitment_signed"); + let commitment_point = self.holder_commitment_point.current_point().ok_or_else(|| { + debug_assert!(false); + ChannelError::close( + "current_point should be set for channels initiating splicing".to_owned(), + ) + })?; let (holder_commitment_tx, _) = self.context.validate_commitment_signed( pending_splice_funding, transaction_number,