-
Notifications
You must be signed in to change notification settings - Fork 419
Wipe splice state upon failed interactive funding construction #4120
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
base: main
Are you sure you want to change the base?
Conversation
👋 Thanks for assigning @jkczyz as a reviewer! |
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #4120 +/- ##
==========================================
- Coverage 88.72% 88.65% -0.07%
==========================================
Files 177 180 +3
Lines 133404 135086 +1682
Branches 133404 135086 +1682
==========================================
+ Hits 118365 119767 +1402
- Misses 12325 12557 +232
- Partials 2714 2762 +48
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
signing_session.holder_tx_signatures().is_some() | ||
|| signing_session.has_received_tx_signatures() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does it matter if we received their signatures if we haven't sent ours? Or is it because if they sent theirs then they would not have reset their state?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You're supposed to remember the channel as soon as one side has sent tx_signatures
, because you can't be sure the other side didn't just sign and broadcast without sending their own.
👋 The first review has been submitted! Do you think this PR is ready for a second reviewer? If so, click here to assign a second reviewer. |
lightning/src/ln/channel.rs
Outdated
.pending_splice | ||
.as_mut() | ||
.and_then(|pending_splice| pending_splice.funding_negotiation.take()); | ||
if funded_channel.should_reset_pending_splice_funding_negotiation() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks like we aren't calling fail_interactive_tx_negotiation
when failing to handle a splice_ack
. #4077 updates fail_interactive_tx_negotiation
to take a NegotiationError
, which it uses to construct a SpliceFundingFailed
struct.
For FundingNegotiation::ConstructingTransaction
, the NegotiationError
is formed by copying the inputs from the InteractiveTxConstructor
. For FundingNegotiation::AwaitingAck
when handling splice_ack
, we'd need to do something similar?
Either way we are cloning the inputs just to later take
the FundingNegotiation
here. Maybe that is ok for now? Any thoughts on a better way of doing this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmmm... so we are also already take
'ing the FundingNegotiation
in one place in splice_ack
handling (when FundingNegotiation::into_interactive_tx_constructor
fails), but not earlier when validating the splice_ack
message.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We shouldn't need to call fail_interactive_tx_negotiation
whenever we fail handling a message by sending a warning and disconnecting.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, so failing to process a splice_ack
would just disconnect, and the peer could re-send it after reconnecting. Do we eventually timeout quiescence somewhere?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Quiescence is also implicitly terminated upon disconnection, but we do have a timeout enforced at should_disconnect_peer_awaiting_response
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh, so we'd need to produce a SpliceFailed
event in some other way? I'm thinking when we are in FundingNegotiation::AwaitingAck
and fail processing the splice_ack
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah sounds like that logic might need to live in the disconnection handler, since we'll need to emit an event anyway if a peer disconnects mid-splice negotiation for whatever reason.
e418d45
to
92c7ff4
Compare
92c7ff4
to
c95b678
Compare
An interactive funding construction can be considered failed upon a disconnect or a `tx_abort` message. So far, we've consumed the `InteractiveTxConstructor` in the latter case, but not the former. Additionally, we may have splice-specific state that needs to be consumed as well to allow us to negotiate another splice later on. This commit ensures that we properly consume all splice and interactive funding state whenever possible upon a disconnect or `tx_abort`. The interactive funding state is safe to consume as long as we have either yet to reach `AwaitingSignatures`, or we have but `tx_signatures` has not been sent/received. In all of these cases, we also make sure to clear the quiescent state flag such that we're able to resume processing updates on the channel. The splice state is safe to consume as long as we don't have a pending `FundingNegotiation::AwaitingSignatures` with a `tx_signatures` sent/received and we don't have any negotiated candidates. Note that until splice RBF is supported, it is not currently possible to have any negotiated candidates with a pending interactive funding transaction.
c95b678
to
a5dcdc1
Compare
signing_session.holder_tx_signatures().is_some() | ||
|| signing_session.has_received_tx_signatures() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Shouldn't this be negated? If either side has exchanged signatures then we don't want to reset?
funded_channel.context.channel_state.clear_quiescent(); | ||
funded_channel | ||
.pending_splice | ||
.as_mut() | ||
.and_then(|pending_splice| pending_splice.funding_negotiation.take()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we also clear interactive_tx_signing_session
if in FundingNegotiation::AwaitingSignatures
and we haven't sent/received signatures?
} | ||
} | ||
|
||
if self.should_reset_pending_splice_funding_negotiation().unwrap_or(false) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why use unwrap_or(false)
here but fail_interactive_tx_negotiation
uses true
?
if self.pending_splice.is_none() { | ||
// If we were in quiescence but a splice was never negotiated, we shouldn't be | ||
// quiescent anymore upon reconnecting. | ||
self.context.channel_state.clear_quiescent(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Don't we always want to clear quiescence upon disconnect?
self.pending_splice | ||
.as_mut() | ||
.and_then(|pending_splice| pending_splice.funding_negotiation.take()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
FWIW, I have this in a different function in #4077 since I need to funding_negotiation
to produce a SpliceFailed
, but I should be able to move it there.
if funded_channel.should_reset_pending_splice_state() { | ||
funded_channel.pending_splice.take(); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
should_reset_pending_splice_state
calls should_reset_pending_splice_funding_negotiation
again, but the latter is partly determined based on pending_splice.funding_negotiation
, which is taken above. Everything seems to check out for the second call based on how unwrap_or
is used, but that feels kinda fragile.
Wondering if instead of using two different methods, we could do something like:
fn maybe_reset_splice_funding_negotation(&mut self) -> Option<SpliceFundingFailed> {
// ...
}
This would clear pending_splice.funding_negotiation
, pending_splice
, and interactive_tx_signing_session
, as appropriate and convert them to the SpliceFundingFailed
needed to produce a SpliceFailed
event.
An interactive funding construction can be considered failed upon a disconnect or a
tx_abort
message. So far, we've consumed theInteractiveTxConstructor
in the latter case, but not the former. Additionally, we may have splice-specific state that needs to be consumed as well to allow us to negotiate another splice later on.This commit ensures that we properly consume all splice and interactive funding state whenever possible upon a disconnect or
tx_abort
.The interactive funding state is safe to consume as long as we have either yet to reach
AwaitingSignatures
, or we have buttx_signatures
has not been sent/received.The splice state is safe to consume as long as we don't have a pending
FundingNegotiation::AwaitingSignatures
with atx_signatures
sent/received and we don't have any negotiated candidates. Note that until splice RBF is supported, it is not currently possible to have any negotiated candidates with a pending interactive funding transaction.