Skip to content
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

Compact blinded path creation #3011

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

Conversation

jkczyz
Copy link
Contributor

@jkczyz jkczyz commented Apr 22, 2024

Add support for creating a compact BlindedPath, which consists of:

  • using an SCID instead of a node id in BlindedHop::encrypted_payload
  • using IntroductionNode::DirectedShortChannelId

The first is accomplished by specifying SCIDs when calling BlindedPath::new_for_message using a new message::ForwardNode struct. The second is through calling BlindedPath::use_compact_introduction_node. Both are called by DefaultMessageRouter.

@codecov-commenter
Copy link

codecov-commenter commented Apr 22, 2024

Codecov Report

Attention: Patch coverage is 92.19858% with 11 lines in your changes are missing coverage. Please review.

Project coverage is 89.83%. Comparing base (da7a916) to head (7d85abd).

Files Patch % Lines
lightning/src/ln/offers_tests.rs 81.25% 0 Missing and 6 partials ⚠️
lightning/src/blinded_path/mod.rs 86.20% 3 Missing and 1 partial ⚠️
lightning/src/util/test_utils.rs 50.00% 1 Missing ⚠️

❗ Your organization needs to install the Codecov GitHub app to enable full functionality.

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #3011      +/-   ##
==========================================
+ Coverage   89.79%   89.83%   +0.03%     
==========================================
  Files         116      116              
  Lines       96466    96560      +94     
  Branches    96466    96560      +94     
==========================================
+ Hits        86619    86741     +122     
+ Misses       7290     7255      -35     
- Partials     2557     2564       +7     

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

Copy link
Contributor

@valentinewallace valentinewallace left a comment

Choose a reason for hiding this comment

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

Basically LGTM

lightning/src/onion_message/messenger.rs Outdated Show resolved Hide resolved

/// Attempts to a use a compact representation for the [`IntroductionNode`] by using a directed
/// short channel id from a channel in `network_graph` leading to the introduction node.
pub fn compact_introduction_node(&mut self, network_graph: &ReadOnlyNetworkGraph) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Pedantic but the convention is for setters to use the set_ prefix: https://github.com/rust-lang/rfcs/blob/master/text/0344-conventions-galore.md#gettersetter-apis and I think it would read a bit cleaner in this case.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Hmmm... this is not a setter in the normal sense, though, as we don't pass a value to set a field with. Nor is the field private, which would necessitate a setter. It also may not update the field if a value cannot be found in the network graph. Happy to go with a better name. Just not sure if a set_ prefix is appropriate here.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Still, it does seem a bit strange that we're mutating the blinded path with a fn name that sounds like it could just as well be a straight getter.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ok, added a use_ prefix. I can see how the docs make it less clear that compact was a verb in the name.

T: secp256k1::Signing + secp256k1::Verification
>(
&self, recipient: PublicKey, peers: Vec<PublicKey>, secp_ctx: &Secp256k1<T>,
&self, recipient: PublicKey, peers: Vec<PublicKey>, scid_lookup: &SL,
Copy link
Collaborator

Choose a reason for hiding this comment

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

Rather than passing a list of our peers and a trait so that create_blinded_paths can make a callback to the caller to ask for an SCID for a given peer, shouldn't we just pass in the peers as a ForwardNode or (PublicKey, u64)?

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, come to think of it that is probably cleaner. That would also allow the caller to use the compact hop representation only when desired. (e.g., in offer/refund but not in reply paths).

@jkczyz jkczyz force-pushed the 2024-04-compact-blinded-path-creation branch from 084d070 to a74e84f Compare May 9, 2024 22:55
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.

Looks like this needs a rebase.

node_id: *node_id,
short_channel_id: peer.channel_by_id
.iter()
.find(|(_, channel)| channel.context().is_usable())
Copy link
Collaborator

Choose a reason for hiding this comment

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

Do we want to sort by oldest or biggest or something?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good idea! Oldest is probably best as this is only for onion messages, so we just want to make sure the channel announcement has been propagated in the gossip.

@jkczyz jkczyz force-pushed the 2024-04-compact-blinded-path-creation branch 2 times, most recently from 5d08b1f to f3726a7 Compare May 13, 2024 21:48
@jkczyz
Copy link
Contributor Author

jkczyz commented May 13, 2024

Rebased

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.

Basically LGTM, but IMO we really should make this optional somehow. It could happen in a followup if we want, cause we should also change how many hops we include based on if an offfer is long-lived.


/// Attempts to a use a compact representation for the [`IntroductionNode`] by using a directed
/// short channel id from a channel in `network_graph` leading to the introduction node.
pub fn compact_introduction_node(&mut self, network_graph: &ReadOnlyNetworkGraph) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Still, it does seem a bit strange that we're mutating the blinded path with a fn name that sounds like it could just as well be a straight getter.

@@ -213,6 +217,30 @@ impl BlindedPath {
},
}
}

/// Attempts to a use a compact representation for the [`IntroductionNode`] by using a directed
/// short channel id from a channel in `network_graph` leading to the introduction node.
Copy link
Collaborator

Choose a reason for hiding this comment

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

We should probably have some kind of discussion of how this makes paths shorter but if a channel closes will invalidate it.

if let Some((scid, channel_info)) = node_info
.channels
.iter()
.find_map(|scid| network_graph.channel(*scid).map(|info| (*scid, info)))
Copy link
Collaborator

Choose a reason for hiding this comment

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

Similar here, should we sort by size/age?

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, good catch. Using the block height from the scid as it seems the timestamp in the update is really a counter specific to the channel.

.filter(|(_, peer)| peer.latest_features.supports_onion_messages())
.map(|(node_id, peer)| ForwardNode {
node_id: *node_id,
short_channel_id: peer.channel_by_id
Copy link
Collaborator

Choose a reason for hiding this comment

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

Should we make setting this optional somehow? I feel like if I'm building a super long-term offer I may have a different preference from something being scanned right now.

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... arguably we shouldn't bother with it for reply paths, either. Just not sure exactly how we want to convey it through the MessageRouter trait. Currently, the caller makes the decision for the penultimate hop using ForwardNode, but when adding more hops the MessageRouter makes the decision since it needs a NetworkGraph to find more hops. Similarly for the introduction node.

So right now it's partly an implementation concern given you need a NetworkGraph. I guess we can just add a bool parameter and it say it is best effort? Any other ideas?

Copy link
Contributor

Choose a reason for hiding this comment

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

I see what you mean... if there's an obvious "default behavior" that stands out, we could have a separate method, e.g. create_long_term_blinded_paths, or have a Config struct. That way we could also have a config setting for compact offers vs offers that don't need to be QR-scanned, or privacy-oriented offers that want longer blinded paths.

@@ -450,7 +455,12 @@ where
Err(())
}
},
}?;
for path in &mut paths {
path.compact_introduction_node(&network_graph);
Copy link
Collaborator

Choose a reason for hiding this comment

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

Similar here, can we make this optional on a per-offer/message basis?

@jkczyz jkczyz force-pushed the 2024-04-compact-blinded-path-creation branch from f3726a7 to b295e98 Compare May 14, 2024 17:17
Copy link
Contributor Author

@jkczyz jkczyz left a comment

Choose a reason for hiding this comment

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

Oops, forgot to publish these comments.


/// Attempts to a use a compact representation for the [`IntroductionNode`] by using a directed
/// short channel id from a channel in `network_graph` leading to the introduction node.
pub fn compact_introduction_node(&mut self, network_graph: &ReadOnlyNetworkGraph) {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ok, added a use_ prefix. I can see how the docs make it less clear that compact was a verb in the name.

if let Some((scid, channel_info)) = node_info
.channels
.iter()
.find_map(|scid| network_graph.channel(*scid).map(|info| (*scid, info)))
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, good catch. Using the block height from the scid as it seems the timestamp in the update is really a counter specific to the channel.

.filter(|(_, peer)| peer.latest_features.supports_onion_messages())
.map(|(node_id, peer)| ForwardNode {
node_id: *node_id,
short_channel_id: peer.channel_by_id
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... arguably we shouldn't bother with it for reply paths, either. Just not sure exactly how we want to convey it through the MessageRouter trait. Currently, the caller makes the decision for the penultimate hop using ForwardNode, but when adding more hops the MessageRouter makes the decision since it needs a NetworkGraph to find more hops. Similarly for the introduction node.

So right now it's partly an implementation concern given you need a NetworkGraph. I guess we can just add a bool parameter and it say it is best effort? Any other ideas?

Copy link
Contributor

@valentinewallace valentinewallace left a comment

Choose a reason for hiding this comment

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

LGTM pending maybe making the compact encoding optional. Looks like it needs another rebase as well.

lightning/src/blinded_path/mod.rs Outdated Show resolved Hide resolved
@jkczyz jkczyz force-pushed the 2024-04-compact-blinded-path-creation branch from b295e98 to 2dd24ed Compare May 14, 2024 22:42
jkczyz added 11 commits May 14, 2024 17:48
When sending an onion message to a blinded path, the short channel id
between hops isn't need in each hop's encrypted_payload since it is not
a payment. However, using the short channel id instead of the node id
gives a more compact representation. Update BlindedPath::new_for_message
to allow for this.
Instead of passing Vec<PublicKey> to MessageRouter::crate_blinded_path,
pass Vec<ForwardNode>. This way callers can include a short_channel_id
for a more compact BlindedPath encoding.
Add a method to BlindedPath that given a network graph will compact the
IntroductionNode as the DirectedShortChannelId variant. Call this method
from DefaultMessageRouter so that Offer paths use the compact
representation (along with reply paths). This leaves payment paths in
Bolt12Invoice using the NodeId variant, as the compact representation
isn't as useful there.

WIP: Compact introduction node
@jkczyz jkczyz force-pushed the 2024-04-compact-blinded-path-creation branch from 2dd24ed to 7d85abd Compare May 14, 2024 22:48
@jkczyz
Copy link
Contributor Author

jkczyz commented May 14, 2024

LGTM pending maybe making the compact encoding optional. Looks like it needs another rebase as well.

Rebased now. Yeah, maybe best to do that in a follow-up as it may be a little more involved. Let me know if you have any thoughts on #3011 (comment).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants