Skip to content

Introduce interactive signing state flags for funded states. #3637

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 5 commits into
base: main
Choose a base branch
from

Conversation

dunxen
Copy link
Contributor

@dunxen dunxen commented Mar 3, 2025

This PR includes some deferred follow-ups extracted from #3423 and introduces new state flags to track interactive signing along with persistence of the minimum information needed from a signing session to reconstruct it.

A top-level state flag was avoided so that this work is compatible with splicing as well as V2 channel establishment (dual-funding).

@ldk-reviews-bot
Copy link

ldk-reviews-bot commented Mar 3, 2025

👋 Thanks for assigning @wpaulino as a reviewer!
I'll wait for their review and will help manage the review process.
Once they submit their review, I'll check if a second reviewer would be helpful.

@dunxen dunxen marked this pull request as draft March 3, 2025 18:50
@dunxen dunxen force-pushed the 2025-02-interactivesigningstate branch 2 times, most recently from 4c6b6ab to c1f430a Compare March 4, 2025 09:02
@dunxen dunxen changed the title DRAFT: Introduce interactive signing state flags for funded states. Introduce interactive signing state flags for funded states. Mar 4, 2025
@dunxen dunxen marked this pull request as ready for review March 4, 2025 09:03
@dunxen dunxen force-pushed the 2025-02-interactivesigningstate branch from c1f430a to e89ba58 Compare March 4, 2025 11:09
Copy link
Collaborator

@TheBlueMatt TheBlueMatt left a comment

Choose a reason for hiding this comment

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

Did you want to include test coverage for restarts here?

@dunxen
Copy link
Contributor Author

dunxen commented Mar 4, 2025

Did you want to include test coverage for restarts here?

Not yet. Tracked in #3636. Will need to be able to contribute inputs first to test a useful order of message exchange + restart.

@dunxen dunxen force-pushed the 2025-02-interactivesigningstate branch from e89ba58 to 3b2ac55 Compare March 5, 2025 10:05
Copy link

codecov bot commented Mar 5, 2025

Codecov Report

Attention: Patch coverage is 64.55026% with 67 lines in your changes missing coverage. Please review.

Project coverage is 90.01%. Comparing base (89f5217) to head (fb00cb6).
Report is 19 commits behind head on main.

Files with missing lines Patch % Lines
lightning/src/ln/channel.rs 59.55% 49 Missing and 6 partials ⚠️
lightning/src/ln/interactivetxs.rs 46.66% 8 Missing ⚠️
lightning/src/ln/channelmanager.rs 90.90% 1 Missing and 1 partial ⚠️
lightning/src/ln/dual_funding_tests.rs 87.50% 1 Missing ⚠️
lightning/src/util/ser.rs 85.71% 0 Missing and 1 partial ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #3637      +/-   ##
==========================================
+ Coverage   89.37%   90.01%   +0.63%     
==========================================
  Files         157      157              
  Lines      124095   129376    +5281     
  Branches   124095   129376    +5281     
==========================================
+ Hits       110915   116452    +5537     
+ Misses      10469    10187     -282     
- Partials     2711     2737      +26     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@dunxen dunxen force-pushed the 2025-02-interactivesigningstate branch from 3b2ac55 to 5110ecc Compare March 5, 2025 14:20
@wpaulino
Copy link
Contributor

wpaulino commented Mar 5, 2025

@dunxen re-request when this is ready for review again, feel free to squash as well

@dunxen dunxen force-pushed the 2025-02-interactivesigningstate branch from 5110ecc to 1d96044 Compare March 6, 2025 12:43
@dunxen dunxen requested review from TheBlueMatt and wpaulino March 6, 2025 12:44
@ldk-reviews-bot
Copy link

🔔 1st Reminder

Hey @TheBlueMatt @jkczyz @wpaulino! This PR has been waiting for your review.
Please take a look when you have a chance. If you're unable to review, please let us know so we can find another reviewer.

@dunxen dunxen added weekly goal Someone wants to land this this week Dual-funding labels Mar 6, 2025
@TheBlueMatt TheBlueMatt removed their request for review March 6, 2025 23:01
@TheBlueMatt
Copy link
Collaborator

Taking myself off since @wpaulino and @jkczyz are on this one. Aside from my first comment I don't have any more high-level feedback.

@dunxen dunxen force-pushed the 2025-02-interactivesigningstate branch from 1d96044 to 55e5f6f Compare March 11, 2025 11:42
@dunxen dunxen force-pushed the 2025-02-interactivesigningstate branch from 692d59b to 9ca7d03 Compare May 5, 2025 10:27
@ldk-reviews-bot
Copy link

🔔 3rd Reminder

Hey @wpaulino! This PR has been waiting for your review.
Please take a look when you have a chance. If you're unable to review, please let us know so we can find another reviewer.

@dunxen dunxen force-pushed the 2025-02-interactivesigningstate branch from 9ca7d03 to b3c4f18 Compare May 6, 2025 07:55
@dunxen
Copy link
Contributor Author

dunxen commented May 6, 2025

Changes:

git diff-tree -U1 9ca7d03 b3c4f18
diff --git a/lightning/src/ln/channel.rs b/lightning/src/ln/channel.rs
index 1eeca4aee..5111a5060 100644
--- a/lightning/src/ln/channel.rs
+++ b/lightning/src/ln/channel.rs
@@ -6600,6 +6600,5 @@ impl<SP: Deref> FundedChannel<SP> where
        {
-               if !matches!(
-                       self.context.channel_state,
-                       ChannelState::FundingNegotiated(flags) if flags.is_interactive_signing() || !flags.is_their_tx_signatures_sent()
-               ) {
+               if self.interactive_tx_signing_session.is_none() ||
+                       matches!(self.context.channel_state, ChannelState::FundingNegotiated(flags) if flags.is_their_tx_signatures_sent())
+               {
                        return Err(ChannelError::Ignore("Ignoring tx_signatures received outside of interactive signing".to_owned()));

@dunxen dunxen requested review from wpaulino and jkczyz May 6, 2025 08:10
@dunxen dunxen force-pushed the 2025-02-interactivesigningstate branch from b3c4f18 to fb00cb6 Compare May 7, 2025 06:17
@dunxen
Copy link
Contributor Author

dunxen commented May 7, 2025

Changes:

git diff-tree -U1 b3c4f18 fb00cb6
diff --git a/lightning/src/ln/channel.rs b/lightning/src/ln/channel.rs
index 5111a5060..66305c042 100644
--- a/lightning/src/ln/channel.rs
+++ b/lightning/src/ln/channel.rs
@@ -6600,4 +6600,4 @@ impl<SP: Deref> FundedChannel<SP> where
        {
-               if self.interactive_tx_signing_session.is_none() ||
-                       matches!(self.context.channel_state, ChannelState::FundingNegotiated(flags) if flags.is_their_tx_signatures_sent())
+               if !self.context.channel_state.is_interactive_signing()
+                       || self.context.channel_state.is_their_tx_signatures_sent()
                {

@dunxen dunxen requested a review from wpaulino May 7, 2025 06:21
@ldk-reviews-bot
Copy link

🔔 1st Reminder

Hey @jkczyz @wpaulino! This PR has been waiting for your review.
Please take a look when you have a chance. If you're unable to review, please let us know so we can find another reviewer.

1 similar comment
@ldk-reviews-bot
Copy link

🔔 1st Reminder

Hey @jkczyz @wpaulino! This PR has been waiting for your review.
Please take a look when you have a chance. If you're unable to review, please let us know so we can find another reviewer.

@ldk-reviews-bot
Copy link

🔔 2nd Reminder

Hey @jkczyz @wpaulino! This PR has been waiting for your review.
Please take a look when you have a chance. If you're unable to review, please let us know so we can find another reviewer.

1 similar comment
@ldk-reviews-bot
Copy link

🔔 2nd Reminder

Hey @jkczyz @wpaulino! This PR has been waiting for your review.
Please take a look when you have a chance. If you're unable to review, please let us know so we can find another reviewer.

@ldk-reviews-bot
Copy link

🔔 3rd Reminder

Hey @jkczyz @wpaulino! This PR has been waiting for your review.
Please take a look when you have a chance. If you're unable to review, please let us know so we can find another reviewer.

@dunxen dunxen force-pushed the 2025-02-interactivesigningstate branch from fb00cb6 to 3c0b810 Compare May 12, 2025 10:50
@jkczyz
Copy link
Contributor

jkczyz commented May 12, 2025

@dunxen Was the latest push just a rebase?

@dunxen
Copy link
Contributor Author

dunxen commented May 12, 2025

@dunxen Was the latest push just a rebase?

Oh yip.

Sorry, I didn't put a diff-tree showing that.

).into_bytes() }))
}
} else {
return Err(ChannelError::Warn("No active signing session. The associated funding transaction may have already been broadcast.".into()));
Copy link
Contributor

Choose a reason for hiding this comment

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

Seems like we could just send TxAbort anyway and not close the channel?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Seems fine!

if !self.context.channel_state.is_interactive_signing()
|| self.context.channel_state.is_their_tx_signatures_sent()
{
return Err(ChannelError::Ignore("Ignoring tx_signatures received outside of interactive signing".to_owned()));
}

if let Some(ref mut signing_session) = self.interactive_tx_signing_session {
Copy link
Contributor

Choose a reason for hiding this comment

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

We may need to close the channel if they haven't sent commitment_signed first?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yeah just to be safe I'll add the check, but we await initial monitor persist anyway before sending our tx_signatures. I think it would be a risk for a possible manual broadcast, but I haven't checked.

@@ -5880,7 +5933,7 @@ impl<SP: Deref> FundedChannel<SP> where
) -> Result<ChannelMonitor<<SP::Target as SignerProvider>::EcdsaSigner>, ChannelError>
where L::Target: Logger
{
if !matches!(self.context.channel_state, ChannelState::FundingNegotiated) {
if !matches!(self.context.channel_state, ChannelState::FundingNegotiated(flags) if flags.is_interactive_signing()) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Maybe also check for them not having already sent tx_signatures?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Makes sense. Will add. I'll ensure there's coverage for the cases in tests in #3735.

@ldk-reviews-bot
Copy link

🔔 4th Reminder

Hey @jkczyz! This PR has been waiting for your review.
Please take a look when you have a chance. If you're unable to review, please let us know so we can find another reviewer.

dunxen added 5 commits May 14, 2025 13:09
Instead of having an explicit `ChannelContext::next_funding_txid` to set
and read, we can get this value on the fly when it is appropriate to do
so.
This follows the the specification closely in branching without being
too verbose, so that it should be easy to follow the logic.

See: https://github.com/lightning/bolts/blob/aa5207a/02-peer-protocol.md?plain=1#L2520-L2531
This intoduces the INTERACTIVE_SIGNING, THEIR_TX_SIGNATURES_SENT, and
OUR_TX_SIGNATURES_SENT funded state flags.

A top-level state flag for INTERACTIVE_SIGNING was avoided so that this
work is compatible with splicing as well as V2 channel establishment
(dual-funding).

This commit also ensures that `ChannelPending` is only emitted after
peers exchange `tx_signatures`.
We fully persist `InteractiveTxSigningSession` as it provides the full
context of the constructed transaction which is still needed for signing.
When this config field is enabled, the dual_fund feature bit will be
set which determines support when receiving `open_channel2` messages.
@dunxen dunxen force-pushed the 2025-02-interactivesigningstate branch from 3c0b810 to 74139d0 Compare May 14, 2025 12:37
Copy link
Contributor Author

@dunxen dunxen left a comment

Choose a reason for hiding this comment

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

Changes:

git diff-tree -U1 3c0b810 74139d0
diff --git a/lightning/src/ln/channel.rs b/lightning/src/ln/channel.rs
index 66305c042..e6e9c8f3b 100644
--- a/lightning/src/ln/channel.rs
+++ b/lightning/src/ln/channel.rs
@@ -5942,6 +5942,6 @@ impl<SP: Deref> FundedChannel<SP> where
        {
-               if !matches!(self.context.channel_state, ChannelState::FundingNegotiated(flags) if flags.is_interactive_signing()) {
+               if !matches!(self.context.channel_state, ChannelState::FundingNegotiated(flags) if flags.is_interactive_signing() && !flags.is_their_tx_signatures_sent()) {
                        return Err(ChannelError::Close(
                                (
-                                       "Received initial commitment_signed before funding transaction constructed!".to_owned(),
+                                       "Received initial commitment_signed before funding transaction constructed or after peer's tx_signatures received!".to_owned(),
                                        ClosureReason::HolderForceClosed { broadcasted_latest_txn: Some(false) },
@@ -6615,2 +6615,13 @@ impl<SP: Deref> FundedChannel<SP> where

+                       // We need to close the channel if our peer hasn't sent their commitment signed already.
+                       // Technically we'd wait on having an initial monitor persisted, so we shouldn't be broadcasting
+                       // the transaction, but this may risk losing funds for a manual broadcast if we continue.
+                       if !signing_session.has_received_commitment_signed() {
+                               return Err(ChannelError::Close(
+                                       (
+                                               "Received tx_signatures before initial commitment_signed".to_string(),
+                                               ClosureReason::HolderForceClosed { broadcasted_latest_txn: Some(false) },
+                                       )));
+                       }
+
                        if msg.witnesses.len() != signing_session.remote_inputs_count() {
@@ -7458,3 +7469,9 @@ impl<SP: Deref> FundedChannel<SP> where
                                } else {
-                                       return Err(ChannelError::close("Counterparty set `next_funding_txid` at incorrect state".into()));
+                                       // We'll just send a `tx_abort` here if we don't have a signing session for this channel
+                                       // on reestablish and tell our peer to just forget about it.
+                                       // Our peer is doing something strange, but it doesn't warrant closing the channel.
+                                       (None, None, Some(msgs::TxAbort {
+                                               channel_id: self.context.channel_id(),
+                                               data:
+                                                       "No active signing session. The associated funding transaction may have already been broadcast.".as_bytes().to_vec() }))
                                }

).into_bytes() }))
}
} else {
return Err(ChannelError::Warn("No active signing session. The associated funding transaction may have already been broadcast.".into()));
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Seems fine!

@@ -5880,7 +5933,7 @@ impl<SP: Deref> FundedChannel<SP> where
) -> Result<ChannelMonitor<<SP::Target as SignerProvider>::EcdsaSigner>, ChannelError>
where L::Target: Logger
{
if !matches!(self.context.channel_state, ChannelState::FundingNegotiated) {
if !matches!(self.context.channel_state, ChannelState::FundingNegotiated(flags) if flags.is_interactive_signing()) {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Makes sense. Will add. I'll ensure there's coverage for the cases in tests in #3735.

if !self.context.channel_state.is_interactive_signing()
|| self.context.channel_state.is_their_tx_signatures_sent()
{
return Err(ChannelError::Ignore("Ignoring tx_signatures received outside of interactive signing".to_owned()));
}

if let Some(ref mut signing_session) = self.interactive_tx_signing_session {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yeah just to be safe I'll add the check, but we await initial monitor persist anyway before sending our tx_signatures. I think it would be a risk for a possible manual broadcast, but I haven't checked.

@dunxen dunxen requested a review from wpaulino May 14, 2025 12:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Dual-funding weekly goal Someone wants to land this this week
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants