lightningdevkit / rust-lightning

A highly modular Bitcoin Lightning library written in Rust. It's rust-lightning, not Rusty's Lightning!
Other
1.16k stars 365 forks source link

Exploring the Integration of Dummy Hops into Blinded Paths #3252

Open shaavan opened 2 months ago

shaavan commented 2 months ago

This issue is a brainstorming session on why and how we should incorporate an interface for adding dummy hops to blinded paths.

Why Dummy Hops?

Dummy hops function as a privacy enhancement for network nodes. Currently, the MessageRouter can only construct one-hop or two-hop blinded paths. Although this limit could be increased in the future, it is still constrained by the number of actual peers connected to the node.

Introducing the ability to create blinded paths with dummy hops offers several key improvements:

  1. Increased Path Length: The length of the blinded path itself increases, expanding the potential end-points of the blinded path and making de-anonymization more difficult.
  2. Enhanced Utility for Less-Connected Nodes: The benefits of a longer blinded path are not limited by the number of actual peers connected to the node, making this feature valuable even for nodes that are not well-connected.
  3. Obfuscation of the Final Node: The final "true" node is obscured, meaning any of the nodes in the path could potentially be the recipient, adding another layer of privacy.
<><> - **Technical Analysis**: In more technical terms, consider a blinded path that is `n` hops long, where each peer is connected to `k` other peers. - **Traditional Case**: The potential number of final nodes is `k^(n-1)`. - **Dummy Hops Case**: The potential number of final nodes becomes `k^0 + k^1 + ... + k^(n-1) = (k^n - 1)/(k - 1)`. For example, with `k=3` and `n=4`: - Traditional case: 27 searchable nodes. - Dummy hops case: 40 searchable nodes. ### Interface Design After discussions with @jkczyz, we identified two key decision points for implementing dummy hops: 1. **Responsibility**: Should the `MessageRouter` or `ChannelManager` be responsible for adding dummy hops? 2. **Parameter Design**: Should users specify the number of dummy hops directly, or should they define the total number of hops in the blinded path? ### Responsibility: `MessageRouter` vs `ChannelManager` Dummy hops can be interspersed with real hops to achieve the desired path length. This responsibility can be handled by either the `MessageRouter` or the `ChannelManager`. - **`MessageRouter` Approach**: - The `MessageRouter` works on a best-effort basis and can be limited in its capabilities. For example, the current `DefaultMessageRouter` implementation can return a blinded path with at most two hops. - With this approach, if we request a longer path than it can find, it can pad the remaining path length with dummy hops. - The main advantage here is easier integration with the existing codebase, keeping the responsibility of creating blinded paths cleanly within the `MessageRouter`. - However, this method might introduce ambiguity, as we won’t know how many hops are real versus dummy. - Despite this, given that blinded paths with dummy hops offer similar privacy benefits to those with real hops, this approach could be a valid initial step. - **`ChannelManager` Approach**: - Alternatively, the `MessageRouter` could return the "Node for Path" instead of a complete `BlindedPath`, and then the `ChannelManager` would handle padding the path with dummy hops. - This offers more control and transparency over dummy hop addition, but it also increases the complexity of the interface and shifts the burden of blinded path creation onto the `ChannelManager`. - This approach requires a careful trade-off analysis to determine if the added control justifies the increased complexity. **Trade-off Analysis**: With the current codebase, the `ChannelManager` approach would require significant modifications, which might not justify the added control over dummy hops. A simpler middle-ground solution could be to enable the `MessageRouter` to back-propagate information about how many dummy nodes it has appended. Here’s an incomplete branch that explores the `ChannelManager` approach: [[dummy_hops_cm branch](https://github.com/shaavan/rust-lightning/commits/dummy_hops_cm)](https://github.com/shaavan/rust-lightning/commits/dummy_hops_cm). 1. The processes of filtering out potential forward nodes and creating blinded paths using them are closely interlinked. For example, many of the variables used in the first part of code are reutilize in the second part. Separating the process would require either recalculation, reallocation, or passing the variable as a parameter to the new function. 2. Some variables and functions in the blinded path creation part of the code require access to the `network_graph` (e.g., `recipient_announced` and `path.use_compact_introduction_node(&network_graph)`). 3. Since the network graph is not directly accessible to the `ChannelManager`, this would lead to a complicated interface design where the `ChannelManager` and `MessageRouter` become unnecessarily coupled. ### Parameter Design: Total Path Length vs. Number of Dummy Hops The design of the interface for adding dummy hops can take two primary forms: allowing users to specify either the total length of the blinded path or the number of dummy hops. ### Option 1: Specify Total Path Length In this approach, users define the desired total length of the blinded path. This method offers several benefits: - **Consistency**: - Users receive exactly what they ask for—a blinded path of a specific length. This is particularly advantageous in scenarios where consistency of path length is critical, such as in QR code generation for payments, where the maximum path length is constrained by the size of the QR code. ### Option 2: Specify the Number of Dummy Hops Alternatively, the interface could allow users to directly specify the number of dummy hops to be added to the path. This approach offers distinct advantages: - **Granular Control**: - Users have precise control over the number of dummy hops, allowing for tailored privacy configurations. However, this approach also comes with significant disadvantages: - **Inconsistent Path Lengths**: - Allowing users to specify only the number of dummy hops can lead to variability in the total path length. This inconsistency might complicate scenarios where a fixed path length is required, such as in QR codes for offers and refunds, where space is a constraint. - **Increased Complexity**: - Users might find this approach more complex, as they need to understand the implications of adding specific numbers of dummy hops. After discussions with @jkczyz, we are leaning toward the first approach (specifying total path length), but we wanted to document our thoughts and keep the discussion open for further input. ### Test Branch I’ve created a [test branch](https://github.com/shaavan/rust-lightning/commits/dummy_hops) to experiment with integrating dummy hops as a parameter during blinded path creation in the `MessageRouter`. This branch is rebased over two key PRs: #3177 (which enables padding with dummy hops) and #3246 (which introduces the BlindedPathParameter setup for the new `hops` parameter). The branch offers a foundational implementation, providing a clear visualization of how dummy hops could be incorporated. ### Conclusion This issue is still in its early stages, and we welcome any feedback or additional ideas for refinement. Thank you, everyone, for your thoughts and suggestions!
TheBlueMatt commented 1 month ago

In general I prefer that we let the MessageRouter decide the kind of blinded paths it wants the ChannelManager to use. This is for a few reasons, (a) it is configurable without needing knobs - the MessageRouter interface is already an interface so users can override it and do whatever they want with it, (b) it seems like the right separation of concerns - the ChannelManager doesn't have a NetworkGraph and is responsible for channel and payment operations, its not responsible for building blinded paths that's the MessageRouter's job.

Instead ChannelManager should give the MessageRouter the context it has - the expiry time of the path, what its being used for, etc (maybe just as a single boolean for if the path is to be included in something that fits in a QR code, maybe more details, I dunno) and let the MessageRouter return a fully-formed BlindedMessagePath for us.