-
Couldn't load subscription status.
- Fork 421
Include all channel route hints if no connected channels exist #1769
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
Include all channel route hints if no connected channels exist #1769
Conversation
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.
Generally looks good! Would be great with test coverage of this, since the current route hint filtering coverage in lightning_invoice::utils covers most of the current edge cases.
| L::Target: Logger, | ||
| { | ||
| let route_hints = filter_channels(channelmanager.list_usable_channels(), amt_msat); | ||
| let route_hints = filter_channels(channelmanager.list_channels(), amt_msat); |
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.
Would we like to do this for ChannelManager::get_phantom_route_hints as well so that create_phantom_invoice will include create hints with the same filtering if all participating nodes are disconnected or have just disconnected peers (although I guess this isn't really relevant for mobile clients), as phantom invoices are useless without any meaningful route hints?
rust-lightning/lightning/src/ln/channelmanager.rs
Line 5546 in 6772609
| channels: self.list_usable_channels(), |
If the case that all participating nodes have only private channels which are all currently !channel.is_usable, we'll currently push route hints containing only the participating node's id, but as there are no public channels to reach those participating nodes the route hint's will be useless, which seems kinda buggy.
That would make things a bit more complicated as we'd need filter_channels to only return usable channels if any of the phantom_route_hints in _create_phantom_invoice contains a channel with channel.is_usable.
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.
PhantomRouteHints are unlikely to be used in a mobile context. The related-utilities here are ChannelManager-less because these hints may be serialized and read for multiple nodes. We may even want to consider removing the peer connection requirement in get_phantom_route_hints.
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, right, interesting question. I think for now let's leave it as-is - in a server environment the "is channel connected" concept is usually "is peer not down doing maintenance or something", which we'd ideally like to avoid including those channels for.
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.
Ok as a follow-up/separate issue it might be worth it to at least add some validation and verify that the length of the channels vec in the PhantomRouteHints isn't 0? As it is now, we'll call filter_channels and add the phantom hop route hint that includes the participating node's id which can't be routed to in that case. Could also be worth ensuring that the final constructed invoice actually have some route hints depending on how the validation is implemented.
| return vec![] | ||
| } | ||
|
|
||
| if channel.inbound_capacity_msat >= min_inbound_capacity { |
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 still return an empty vec if any public channel exists (5 lines up), since the public channels may be offline now?
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, good question. I think not - we're really assuming that any channels that are disconnected now may be live by the time the sender goes to pay, so if we extend that assumption to public channels we dont need to include any hints. Further, I'd be concerned about accidentally nuking privacy if we did that.
|
Pushed some fixups, should actually compile now lol. |
Codecov ReportBase: 90.78% // Head: 90.89% // Increases project coverage by
Additional details and impacted files@@ Coverage Diff @@
## main #1769 +/- ##
==========================================
+ Coverage 90.78% 90.89% +0.10%
==========================================
Files 87 87
Lines 46969 48048 +1079
Branches 46969 48048 +1079
==========================================
+ Hits 42643 43673 +1030
- Misses 4326 4375 +49
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 at Codecov. |
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.
LGTM after squash
|
Squashed without changes. |
b4eb92c to
80354ac
Compare
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.
ACK 80354ac
654a724 to
b1b2256
Compare
|
Squash-force-pushed a change to use into_iter rather than drain: $ git diff-tree -U1 80354acb b1b225644
diff --git a/lightning-invoice/src/utils.rs b/lightning-invoice/src/utils.rs
index 71e7fbb26..48849263e 100644
--- a/lightning-invoice/src/utils.rs
+++ b/lightning-invoice/src/utils.rs
@@ -383,3 +383,3 @@ where
/// need to find the path by looking at the public channels instead
-fn filter_channels(mut channels: Vec<ChannelDetails>, min_inbound_capacity_msat: Option<u64>) -> Vec<RouteHint>{
+fn filter_channels(channels: Vec<ChannelDetails>, min_inbound_capacity_msat: Option<u64>) -> Vec<RouteHint>{
let mut filtered_channels: HashMap<PublicKey, ChannelDetails> = HashMap::new();
@@ -389,3 +389,3 @@ fn filter_channels(mut channels: Vec<ChannelDetails>, min_inbound_capacity_msat:
- for channel in channels.drain(..).filter(|chan| chan.is_channel_ready) {
+ for channel in channels.into_iter().filter(|chan| chan.is_channel_ready) {
if channel.get_inbound_payment_scid().is_none() || channel.counterparty.forwarding_info.is_none() { |
| .filter(|(_counterparty_id, channel)| { | ||
| min_capacity_channel_exists && channel.inbound_capacity_msat >= min_inbound_capacity || | ||
| !min_capacity_channel_exists | ||
| if min_capacity_channel_exists { | ||
| channel.inbound_capacity_msat >= min_inbound_capacity | ||
| } else { true } | ||
| }) | ||
| .filter(|(_counterparty_id, channel)| { | ||
| if online_channel_exists { | ||
| channel.is_usable | ||
| } else { true } | ||
| }) |
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... actually, this won't work. Consider two channels where one is connected but without enough capacity and the other is disconnected but with enough capacity. The first channel will not pass the first filter even though it would pass the second one. The second channel will pass the first filter but not the second one. So no channels will be included.
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.
Oof, good point. Pushed a fix for 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.
LGTM after squash
| channel.inbound_capacity_msat >= min_inbound_capacity | ||
| } else if online_channel_exists { | ||
| channel.is_usable | ||
| } else { true } |
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 there no limit to number of route hints we want to have?
lnd limits to 20.
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 probably should, but it depends a lot on the use-case. If the user is generating a QR code, 20 is way too many, if they're using it programatically in an API that may make sense. That seems like a separate concern from this PR, though, and we should do something about that in a followup. Can you open an issue and tag it, like, 0.1?
| !min_capacity_channel_exists | ||
| if online_min_capacity_channel_exists { | ||
| channel.inbound_capacity_msat >= min_inbound_capacity && channel.is_usable | ||
| } else if min_capacity_channel_exists && online_channel_exists { |
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.
acc. to this comment "// If there are some online channels and some min_capacity channels, but no
// online-and-min_capacity channels, just include the min capacity ones and ignore
// online-ness."
check should only be on "min_capacity_channel_exists", online_channel_exists seems redundant
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 the code matches the comment, and LLVM will trivially remove the redundant checks.
|
Squashed without further changes. |
f8d941b to
9e99577
Compare
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 good! Just one comment, that could be taken in a follow-up if you prefer to merge this.
Mobile clients often take a second or two before they reconnect to their peers as its not always clear immediately that connections have been killed (especially on iOS). This can cause us to spuriously fail to include route hints in our invoices if we're on mobile. The fix is simple, if we're selecting channels to include in route hints and we're not not connected to the peer for any of our channels, we should simply include the hints for all channels, even though we're disconencted. Fixes lightningdevkit#1768.
a534dcc
9e99577 to
a534dcc
Compare
|
Fixed the method docs, didn't bother to make them exact, the in-function comments leave that. $ git diff-tree -U1 9e99577d a534dcc5
diff --git a/lightning-invoice/src/utils.rs b/lightning-invoice/src/utils.rs
index 5b9e602c1..56f4fe879 100644
--- a/lightning-invoice/src/utils.rs
+++ b/lightning-invoice/src/utils.rs
@@ -379,6 +379,6 @@ where
/// * Always select the channel with the highest inbound capacity per counterparty node
-/// * Filter out channels with a lower inbound capacity than `min_inbound_capacity_msat`, if any
-/// channel with a higher or equal inbound capacity than `min_inbound_capacity_msat` exists
+/// * Prefer channels with capacity at least `min_inbound_capacity_msat` and where the channel
+/// `is_usable` (i.e. the peer is connected).
/// * If any public channel exists, the returned `RouteHint`s will be empty, and the sender will
-/// need to find the path by looking at the public channels instead
+/// need to find the path by looking at the public channels instead
fn filter_channels(channels: Vec<ChannelDetails>, min_inbound_capacity_msat: Option<u64>) -> Vec<RouteHint>{
$ |
|
Merged by @valentinewallace 👋😊 Thanks for the quick fix everyone! Will test this as soon as it lands in the react-native module. |
Mobile clients often take a second or two before they reconnect to their peers as its not always clear immediately that connections have been killed (especially on iOS). This can cause us to spuriously fail to include route hints in our invoices if we're on mobile.
The fix is simple, if we're selecting channels to include in route hints and we're not not connected to the peer for any of our channels, we should simply include the hints for all channels, even though we're disconencted.
Fixes #1768.