-
Notifications
You must be signed in to change notification settings - Fork 407
Support manual signaling of channel readiness #2109
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
Support manual signaling of channel readiness #2109
Conversation
1f68897
to
c8ae2e7
Compare
Codecov ReportPatch coverage:
📣 This organization is not using Codecov’s GitHub App Integration. We recommend you install it so Codecov can continue to function properly for your repositories. Learn more Additional details and impacted files@@ Coverage Diff @@
## main #2109 +/- ##
==========================================
+ Coverage 91.15% 91.44% +0.29%
==========================================
Files 101 101
Lines 48866 50865 +1999
Branches 48866 50865 +1999
==========================================
+ Hits 44544 46514 +1970
- Misses 4322 4351 +29
... and 2 files with indirect coverage changes Help us with your feedback. Take ten seconds to tell us how you rate us. Have a feature suggestion? Share it here. ☔ View full report in Codecov by Sentry. |
lightning/src/ln/channelmanager.rs
Outdated
if let Some(real_scid) = $channel.get_short_channel_id() { | ||
let scid_insert = short_to_chan_info.insert(real_scid, ($channel.get_counterparty_node_id(), $channel.channel_id())); | ||
assert!(scid_insert.is_none() || scid_insert.unwrap() == ($channel.get_counterparty_node_id(), $channel.channel_id()), | ||
if $self.default_configuration.manually_signal_channel_ready { |
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.
Channels may have a different config from ChannelManager::default_configuration
so we should handle that, but it seems like we only track it at the time we create the channel.
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.
Right, I think this maybe belongs in the ChannelHandshakeConfig
or so?
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 ok, yeah that probably makes more sense. This does mean though that I'll have to store the manually_signal_channel_ready
value in the Channel
itself, right? Seems like the ChannelHandshakeConfig
isn't actually saved anywhere and is just used to set some values during Channel
creation, right?
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 the ChannelHandshakeConfig
is ephemeral. We'll definitely need to persist that we need to manually signal since we can restart while the channel still needs confirmations.
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.
Hmm, so the other option would be to only do this for inbound channels, then we can keep looking at the default config?
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.
well at least for our use case we only care about inbound channels. I'm not sure what other use cases there might be for this feature other than waiting to see the funding transaction used by your counterparty.
feels like any potential use case for outbound channels could be implemented during the handling of FundingGenerationReady and delaying giving the tx back to ldk then.
I guess in theory(?) there's some use case where you want to do some check after receiving funding_signed. I don't know, happy to put the config in either place.
I think it doesn't really change the implementation details either way afaict since we will need (at least one) flag in Channel to monitor readiness anyway.
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.
I think it doesn't really change the implementation details either way afaict since we will need (at least one) flag in Channel to monitor readiness anyway.
Yea, I guess absent a good reason not to we should support it both directions and just put it in the handshake and store it.
let best_block_height = self.best_block.read().unwrap().height(); | ||
let channel = channel.get_mut(); | ||
match channel.check_get_channel_ready(best_block_height) { | ||
Some(channel_ready) => send_channel_ready!(self, pending_msg_events, channel, channel_ready), |
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.
Isn't this just going to queue another PendingChannelReady
event when manually_signal_channel_ready
is set? We need to actually force it through this time.
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 whoops, my bad. Makes sense to pull the conditional logic out of the macro and then the fn on ChannelManager the user uses can just call the macro to send it.
/// [`ChannelManager::force_close_without_broadcasting_txn`]: crate::ln::channelmanager::ChannelManager::force_close_without_broadcasting_txn | ||
/// [`UserConfig::manually_signal_channel_ready`]: crate::util::config::UserConfig::manually_signal_channel_ready | ||
/// [`msgs::ChannelReady`]: crate::ln::msgs::ChannelReady | ||
PendingChannelReady { |
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.
SignalChannelReady
or MarkChannelReady
?
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.
That sounds like the event itself marks the channel ready? I kinda like pending, tbh.
/// and the `counterparty_node_id` parameter is the id of the peer the channel is with. | ||
/// | ||
/// [`Event::PendingChannelReady`]: events::Event::PendingChannelReady | ||
pub fn signal_channel_readiness(&self, channel_id: &[u8; 32], counterparty_node_id: &PublicKey) -> Result<(), APIError> { |
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.
Let's add a test for this.
assert!(scid_insert.is_none() || scid_insert.unwrap() == ($channel.get_counterparty_node_id(), $channel.channel_id()), | ||
if $self.default_configuration.manually_signal_channel_ready { | ||
let mut pending_events = $self.pending_events.lock().unwrap(); | ||
pending_events.push(events::Event::PendingChannelReady { |
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.
The send_channel_ready
macro doesn't seem like the proper place for this. At this point, we're ready to send out ChannelReady
to our counterparty, and if we already received theirs, then we'll actually mark the channel as ready internally (see check_get_channel_ready
) and even receive a ChannelReady
event immediately after. This may result in a HTLC being routed through the channel, even though we haven't signaled it ready ourselves. If we want to continue using check_get_channel_ready
for the manual signal, we'll need to make sure it only returns Some(ChannelReady)
once signal_channel_readiness
is called.
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, I didn't look carefully enough at check_get_channel_ready
and didn't realize it can update channel state.
So the options are to update check_get_channel_ready
so that like you said, it only returns the msg if manual is enabled and user has already signaled. This implies adding state to a channel to track whether or not user has signaled already.
Is an alternative to update check_get_channel_ready
so that it never returns Some(msg) if manual signaling is enabled and then write new logic in the signal_channel_readiness
to make sure it's safe to send and then builds and sends the message?
The latter approach would avoid adding additional state and is the way I'd lean going if you think it's okay?
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.
Is an alternative to update check_get_channel_ready so that it never returns Some(msg) if manual signaling is enabled and then write new logic in the signal_channel_readiness to make sure it's safe to send and then builds and sends the message?
This wouldn't allow the ChannelReady
message to be retransmitted to the counterparty upon a reconnection and ChannelReestablish
, unless another PendingChannelReady
event is queued for the consumer to signal. For your use case, it seems you only care about the first ChannelReady
that gets sent, excluding any further ones.
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.
Hm true, I was thinking it would just continue to fire Pending as needed, requiring the user to continually perform whatever checks they need before sending.
I guess if we wanted to support only one approval from user then there's no way around adding state to the Channel
so it knows whether or not it's received approval already
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.
I guess we can solve both problems at once and just add a requires_manual_readiness_signal
to Channel
that gets set to the new config at creation and then when they manually signal we can flip it to 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.
Hm true, I was thinking it would just continue to fire Pending as needed, requiring the user to continually perform whatever checks they need before sending.
We could, but I don't immediately see the use case for it. We already do something similar with our ChannelReady
event – we only queue it once.
I guess we can solve both problems at once and just add a requires_manual_readiness_signal to Channel that gets set to the new config at creation and then when they manually signal we can flip it to false.
Yeah that should work. Depending on how/when the event is queued, we may need another bit to make sure it's only queued once.
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.
Just realized we also potentially send ChannelReady as part of channel reestablishment and monitor update workflows. Seems like everywhere we might return Some(msg::ChannelReady) we would need to make sure it's been signaled already.
The issue then becomes where do we actually queue the PendingChannelReady event. I guess the normal path is part of do_chain_event
workflow? Though maybe the first time can also be triggered after funding_created and a node restart via monitor update and 0-conf channel?
I think I need some help figuring out best place to queue it.
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.
Yea, I think we shouldn't concern ourselves with monitors or peer connectivity for generating the event itself - we should generate the event in the 0conf-signed/block-connection path (which all ends up in check_get_channel_ready
I think). In those cases, we will want to not set the OurChannelReady
/move to the ChannelReady
state until the user tells us we're ready to fly.
We'll want to avoid sending the actual ready message if there's a monitor update pending or the peer is disconnected.
We'll also want to generate a fresh event on restart if we see that we're ready to fly (via check_get_channel_ready
) but we're blocked on the user. That may be as simple as...doing nothing (but storing a "we generated the event" flag in memory only), as presumably there will (eventually) be another block connected on restart and we'll generate the event then.
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.
Only some minor nits, but @wpaulino raised some good points in his review.
lightning/src/util/events.rs
Outdated
/// [`ChannelManager::force_close_without_broadcasting_txn`]: crate::ln::channelmanager::ChannelManager::force_close_without_broadcasting_txn | ||
counterparty_node_id: PublicKey, | ||
/// The txid of the funding transaction. | ||
funding_txid: Txid, |
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.
nit; we should expose the funding outpoint, rather than txid.
/// [`ChannelManager::force_close_without_broadcasting_txn`]: crate::ln::channelmanager::ChannelManager::force_close_without_broadcasting_txn | ||
/// [`UserConfig::manually_signal_channel_ready`]: crate::util::config::UserConfig::manually_signal_channel_ready | ||
/// [`msgs::ChannelReady`]: crate::ln::msgs::ChannelReady | ||
PendingChannelReady { |
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.
That sounds like the event itself marks the channel ready? I kinda like pending, tbh.
lightning/src/ln/channelmanager.rs
Outdated
if let Some(real_scid) = $channel.get_short_channel_id() { | ||
let scid_insert = short_to_chan_info.insert(real_scid, ($channel.get_counterparty_node_id(), $channel.channel_id())); | ||
assert!(scid_insert.is_none() || scid_insert.unwrap() == ($channel.get_counterparty_node_id(), $channel.channel_id()), | ||
if $self.default_configuration.manually_signal_channel_ready { |
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.
Right, I think this maybe belongs in the ChannelHandshakeConfig
or so?
Added a note in the description that this Fixes #2106 |
c8ae2e7
to
d529d7b
Compare
Is this ready for another look yet @johncantrell97? |
not yet, sorry. moving to another PR actually as I think this approach was too far off what we want. |
Closing this in favor of #2137 |
First pass at implementing #2106
A lot of the naming here is likely not great. Please make recommendations for
Event::PendingChannelReady
,signal_channel_readiness
, andmanually_signal_channel_ready
.I wasn't sure if it was a great idea to make
check_get_channel_ready
public but it has all the logic I need to implementsignal_channel_readiness
.I ended up adding the logic for conditionally sending the
ChannelReady
message inside of thesend_channel_ready
macro which might not be great but it was the simplest place to put it to guarantee it would re-trigger the event should the user have the feature enabled.Fixes #2106