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

Add Probing Documentation & Best Practices #283

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

Conversation

Beige-Coffee
Copy link
Contributor

This PR is a first-draft submission for Probing & ProbabilisticScorer documentation (including best practices). Once complete, it will close #274.

Looking forward to receiving feedback!

:
Add probing documentation draft
Copy link

netlify bot commented Oct 16, 2024

Deploy Preview for lightningdevkit ready!

Name Link
🔨 Latest commit dd056e6
🔍 Latest deploy log https://app.netlify.com/sites/lightningdevkit/deploys/672989a35e8ecf00089260f2
😎 Deploy Preview https://deploy-preview-283--lightningdevkit.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify site configuration.

@jkczyz jkczyz self-requested a review October 16, 2024 20:48
@ConorOkus ConorOkus self-requested a review October 18, 2024 22:46
docs/probing.md Outdated
# Probing and Path Finding

## The Challenge of Routing Payments on the Lightning Network
The Lightning Development Kit (LDK) provides you with the software tools to run a node on the Lightning Network - a collection of tens of thousands of nodes that, together, enable cheap and private Bitcoin transactions.
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
The Lightning Development Kit (LDK) provides you with the software tools to run a node on the Lightning Network - a collection of tens of thousands of nodes that, together, enable cheap and private Bitcoin transactions.
The Lightning Development Kit (LDK) provides you with the software tools to build a node on the Lightning node. The Lightning Network is a collection of tens of thousands of nodes that, together, enable cheap and private Bitcoin transactions.

docs/probing.md Outdated
1) **Missing Information**: Nodes may not know if a given channel's liquidity is prohibitively unbalanced.
2) **Path-Finding Optimization**: If multiple payment paths exist, we must decide which path is the best.

To help address both of the above challenges, LDK provides a ```ProbabilisticScorer``` struct. This structure holds information about our node's current view of the Lightning Network graph, including an estimate of available liquidity within each channel. It also records historical liquidity observations, which are updated each time our node either fails or succeeds at making a payment (or probe). It's important to note that our node can have multiple ```ProbabilisticScorer``` in use simultaneously. Furthermore, this can actually be quite beneficial, as it allows our node to leverage multiple perspectives of the Lightning Network graph, helping to ensure we don't become too reliant on the same paths when exploring potential payment paths.
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
To help address both of the above challenges, LDK provides a ```ProbabilisticScorer``` struct. This structure holds information about our node's current view of the Lightning Network graph, including an estimate of available liquidity within each channel. It also records historical liquidity observations, which are updated each time our node either fails or succeeds at making a payment (or probe). It's important to note that our node can have multiple ```ProbabilisticScorer``` in use simultaneously. Furthermore, this can actually be quite beneficial, as it allows our node to leverage multiple perspectives of the Lightning Network graph, helping to ensure we don't become too reliant on the same paths when exploring potential payment paths.
To help address both of the above challenges, LDK provides a [```ProbabilisticScorer```](https://docs.rs/lightning/latest/lightning/routing/scoring/struct.ProbabilisticScorer.html) struct. This structure holds information about our node's current view of the Lightning Network graph, including an estimate of available liquidity within each channel. It also records historical liquidity observations, which are updated each time our node either fails or succeeds at making a payment (or probe). It's important to note that our node can have multiple ```ProbabilisticScorer``` in use simultaneously. Furthermore, this can actually be quite beneficial, as it allows our node to leverage multiple perspectives of the Lightning Network graph, helping to ensure we don't become too reliant on the same paths when exploring potential payment paths.


Before digging into best practices for probing, it's important to note that popular Lightning Network implementations such as Core Lightning (CLN), Lightning Network Daemon (LND), and Lightning Development Kit (LDK), have different internal representations of the Lightning Network. Therefore, it's not possible to simply "feed" CLN, LND, or LDK the same scoring file or information when setting up path-finding functionality.

### General Architecture Approaches
Copy link
Contributor

Choose a reason for hiding this comment

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

@jkczyz does this check out? So probing isn't something we would do on a mobile phone?

Copy link
Collaborator

Choose a reason for hiding this comment

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

Yeah, the wallet's vendor would periodically provide an updated scorer for the wallet to use. A mobile wallet would need to be active to send / handle probes. However, a wallet could send a JIT probe before making a payment to help ensure it will make it to the destination. And it could update it's local scorer with recent payment success / failure results, though that information would tend to decay before the next payment, if payments are infrequent.

@ConorOkus
Copy link
Contributor

Thanks @Beige-Coffee. Wondering if we should at least point to RGS https://lightningdevkit.org/blog/announcing-rapid-gossip-sync/ as way to get a view of the network graph, it's important for mobile users.

Copy link
Collaborator

@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.

Need to go through the example still but looks good so far.

docs/probing.md Outdated
## The Challenge of Routing Payments on the Lightning Network
The Lightning Development Kit (LDK) provides you with the software tools to run a node on the Lightning Network - a collection of tens of thousands of nodes that, together, enable cheap and private Bitcoin transactions.

The challenge is that your node is just one among tens of thousands. While each node has its own internal representation of the Lightning Network Graph, the information within this graph is incomplete to optimally route payments. This is largely because, when nodes announce new (or updated) channels to the network, they do not specify how the capacity is distributed between the two channel parties, making it difficult to know for certain if a given channel is a viable option when routing payments. To further complicate things, your node may not be entirely certain that a given channel is online when routing a payment, further increasing the risk of payment failure.
Copy link
Collaborator

Choose a reason for hiding this comment

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

Not only how the capacity is distributed at time of announcement, but also over time as payments are made.

docs/probing.md Outdated
## The Challenge of Routing Payments on the Lightning Network
The Lightning Development Kit (LDK) provides you with the software tools to run a node on the Lightning Network - a collection of tens of thousands of nodes that, together, enable cheap and private Bitcoin transactions.

The challenge is that your node is just one among tens of thousands. While each node has its own internal representation of the Lightning Network Graph, the information within this graph is incomplete to optimally route payments. This is largely because, when nodes announce new (or updated) channels to the network, they do not specify how the capacity is distributed between the two channel parties, making it difficult to know for certain if a given channel is a viable option when routing payments. To further complicate things, your node may not be entirely certain that a given channel is online when routing a payment, further increasing the risk of payment failure.
Copy link
Collaborator

Choose a reason for hiding this comment

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

s/Graph/graph

docs/probing.md Outdated

Probing best practices are discussed in more detail in [**Best Practices**](#best-practices), however, for now, it's sufficient to note that LDK will update its view of the graph each time real and test payments either succeed or fail. Successes and failures are communicated to our LDK node via the [ScoreUpdate](https://docs.rs/lightning/latest/lightning/routing/scoring/trait.ScoreUpdate.html) trait. These successes and failures serve as historical data points and influence our node's current estimate of available liquidity for each channel.

Given that the Lightning Network graph is constantly changing as payments flow throughout the ecosystem, our LDK node must account for the passage of time and assume that channel balances will change even if our node has not probed or otherwise interacted with a channel in a while. To accomplish this, LDK provides ```ProbabilisticScoringDecayParameters```. These parameters configure how each channel’s available liquidity estimates are updated over time. For example, one ```ProbabilisticScoringDecayParameters```configuration is ```liquidity_offset_half_life```. This configuration determines how frequently a channel's upper bound and lower bound available liquidity estimations are updated. For example, after the set time passes (default 6 hours), the channel’s lower bound estimate is cut in half, and the upper bound moves halfway to the channel’s total capacity. For example, if a channel has a 1 million sat capacity, and our node currently estimates its lower bound to be 200,000 sats and an upper bound to be 600,000 sats, then, after ```liquidity_offset_half_life```, it will be updated to 100,000 sats and 800,000 sats. While the ```ProbabilisticScoringDecayParameters``` can be customized, default parameters are provided. You can read more about the ```ProbabilisticScoringDecayParameters``` [here](https://docs.rs/lightning/latest/lightning/routing/scoring/struct.ProbabilisticScoringDecayParameters.html).
Copy link
Collaborator

@jkczyz jkczyz Oct 30, 2024

Choose a reason for hiding this comment

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

More of a preference, but consider re-wording the last sentence and linking from the word "docs", for instance, instead of "here".

docs/probing.md Outdated
Given that the Lightning Network graph is constantly changing as payments flow throughout the ecosystem, our LDK node must account for the passage of time and assume that channel balances will change even if our node has not probed or otherwise interacted with a channel in a while. To accomplish this, LDK provides ```ProbabilisticScoringDecayParameters```. These parameters configure how each channel’s available liquidity estimates are updated over time. For example, one ```ProbabilisticScoringDecayParameters```configuration is ```liquidity_offset_half_life```. This configuration determines how frequently a channel's upper bound and lower bound available liquidity estimations are updated. For example, after the set time passes (default 6 hours), the channel’s lower bound estimate is cut in half, and the upper bound moves halfway to the channel’s total capacity. For example, if a channel has a 1 million sat capacity, and our node currently estimates its lower bound to be 200,000 sats and an upper bound to be 600,000 sats, then, after ```liquidity_offset_half_life```, it will be updated to 100,000 sats and 800,000 sats. While the ```ProbabilisticScoringDecayParameters``` can be customized, default parameters are provided. You can read more about the ```ProbabilisticScoringDecayParameters``` [here](https://docs.rs/lightning/latest/lightning/routing/scoring/struct.ProbabilisticScoringDecayParameters.html).

### Finding the Optimal Path
The ```ProbabilisticScorer``` assists in finding the optimal path by providing our node with a perspective of the Lightning Network graph and the estimated available liquidity for each channel. To assist in finding the optimal payment route, LDK provides ```ProbabilisticScoringFeeParameters```. These fee parameters impact how our node optimizes its routing decisions. Broadly speaking, ```ProbabilisticScoringFeeParameters``` contains various parameters that help select paths with desirable properties (ex: fewer hops, reliable, private, low fees, etc.). For example, one setting is ```liquidity_penalty_amount_multiplier_msat```. This configuration defines a multiplier that is used in conjunction with the total amount flowing over the given channel and our node's estimated probability of successfully routing the payment through the channel. This configuration will give a larger penalty to channels that have a low probability of success, and that penalty will grow larger as the payment amount increases.
Copy link
Collaborator

Choose a reason for hiding this comment

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

For liquidity_penalty_amount_multiplier_msat, this was recently changed such that the success probability is based on the total amount but the multiplier is used with the payment amount. See lightningdevkit/rust-lightning#3356.

docs/probing.md Outdated
- ```decay_params```: This field holds the decay parameters (```ProbabilisticScoringDecayParameters```) for the scorer. As mentioned above, these are customizable, but LDK also provides defaults. You can learn more about them [here](https://docs.rs/lightning/latest/lightning/routing/scoring/struct.ProbabilisticScoringDecayParameters.html).
- ```network_graph```: This field holds a reference to the network graph.
- ```logger```: This field holds a reference to the logger.
- ```channel_liquidities```: This field holds a ```HashMap```, which maps **short channel IDs** to ```ChannelLiquidity``` objects. These objects include liquidity estimates, historical data for the respective channels, and timestamps indicating when the liquidity estimates were last updated.
Copy link
Collaborator

Choose a reason for hiding this comment

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

Given channel_liquidities is part of the internal representation, we probably don't need to mention it , unlike the other fields which are passed as parameters.


Before digging into best practices for probing, it's important to note that popular Lightning Network implementations such as Core Lightning (CLN), Lightning Network Daemon (LND), and Lightning Development Kit (LDK), have different internal representations of the Lightning Network. Therefore, it's not possible to simply "feed" CLN, LND, or LDK the same scoring file or information when setting up path-finding functionality.

### General Architecture Approaches
Copy link
Collaborator

Choose a reason for hiding this comment

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

Yeah, the wallet's vendor would periodically provide an updated scorer for the wallet to use. A mobile wallet would need to be active to send / handle probes. However, a wallet could send a JIT probe before making a payment to help ensure it will make it to the destination. And it could update it's local scorer with recent payment success / failure results, though that information would tend to decay before the next payment, if payments are infrequent.

docs/probing.md Outdated
1) **Missing Information**: Nodes may not know if a given channel's liquidity is prohibitively unbalanced.
2) **Path-Finding Optimization**: If multiple payment paths exist, we must decide which path is the best.

To help address both of the above challenges, LDK provides a ```ProbabilisticScorer``` struct. This structure holds information about our node's current view of the Lightning Network graph, including an estimate of available liquidity within each channel. It also records historical liquidity observations, which are updated each time our node either fails or succeeds at making a payment (or probe). It's important to note that our node can have multiple ```ProbabilisticScorer``` in use simultaneously. Furthermore, this can actually be quite beneficial, as it allows our node to leverage multiple perspectives of the Lightning Network graph, helping to ensure we don't become too reliant on the same paths when exploring potential payment paths.
Copy link
Collaborator

Choose a reason for hiding this comment

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

I think we can drop the part about multiple scorers here. It's more an implementation detail of how to configure probing.

docs/probing.md Outdated
2) **Leverage Your Routing History**: If you record a history of payments forwarded by your node, you can leverage that by identifying large or well-ranked nodes that are 1 or 2 hops beyond frequent next-hops from your node. The goal here is to attempt to identify heavily used paths for payments that route through your node. Of course, you do not know the final destination of each payment, which is why you will need to use some heuristics (ex: channel capacity, node rank, etc.) to estimate which paths are heavily used.
3) **Probe Large Nodes**: As mentioned above, it can be beneficial to probe large nodes on the network, such as ACINQ. This is because large nodes can often be the final destination for payments, so probing these nodes will help your LDK node understand which paths are viable for varying payment amounts. It's also worth noting that since many channels on the Lightning Network are connected to large nodes, you will likely succeed in probing large nodes via the "Random Probing" approach.
4) **Use Multiple ```ProbabilisticScorer```**: Your node can leverage multiple ```ProbabilisticScorer``` at once. Remember, each scorer has its own internal liquidity estimates and payment history for each channel it knows about. Therefore, it may be beneficial to use a new scorer, which has not been updated, to select a route and then update a different scorer. This approach would help ensure that your node doesn't keep probing the same paths, as your node's updated scorer will likely begin to identify and prefer certain paths once it's sufficiently updated.
5) **Varry The Payment Amount**: Remember, a central part of the path-finding algorithm involves generating penalties for each channel so that your node can select the best path. A key variable when calculating the penalty for each channel is the payment amount. You will likely get different paths for the same source --> destination payment, depending on the amount. Therefore, it's important to varry the payment amount when setting up your application to probe the network. You can read more about the ```ProbabilisticScoringFeeParameters``` [here](https://docs.rs/lightning/latest/lightning/routing/scoring/struct.ProbabilisticScoringFeeParameters.html).
Copy link
Collaborator

Choose a reason for hiding this comment

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

s/Varry/Vary
(two places)

docs/probing.md Outdated
```

#### Setting Up Our Probing Logic
For this example, we'll build a simple prober that fetches a random node from our ```NetworkGraph``` and attempts to send that node a random payment - up to 500,000,000 milli-satoshis. We'll start by creating a function that fetches a random node from our ```NetworkGraph```. If we cannot find a node, the function will return, and we will not probe any nodes. If we're able to identify a node to probe, then we'll select a random payment amount of up to 500,000,000 milli-satoshis. As a reminder, 1 satoshi = 1,000 milli-satoshis. Finally, we'll get the public key for the node we selected and call ```send_probe```, which is defined below.
Copy link
Collaborator

Choose a reason for hiding this comment

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

Can drop the first "up to 500,000,000 milli-satoshis". Feels saying it twice is redundant.

Comment on lines +150 to +151
// Generate a random amount for the probe - up to 500,000,000 milli-satoshis.
let amt = ::rand::random::<u64>() % 500_000_000;
Copy link
Collaborator

Choose a reason for hiding this comment

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

It might be worth noting somewhere how a more advance prober might choose an amount. For instance, it could try to hone in on the exact liquidity balance by choosing an amount that falls between the min and max liquidity estimate of a channel. Probing below / above the the min / max wouldn't yield any useful information. Although, changing the amount could result in a different path being chosen. To chose an exact path, they could use lightning::routing::router::build_route_from_hops. https://docs.rs/lightning/latest/lightning/routing/router/fn.build_route_from_hops.html

docs/probing.md Outdated

LDK leverages an event-driven architecture, allowing for asynchronous result notification. Therefore, to update our scorer over time, we'll want to inform our application that it should be on the lookout for payment or probing-related events, as these are what indicate to our node that a given route has succeeded or failed. The specific events that we should be watching for and handling are ```Event::PaymentPathFailed```, ```Event::PaymentPathSuccessful```, ```Event::ProbeSuccessful```, and ```Event::ProbeFailed```. There are many other event types. For extensive documentation of LDK events, please see [here](https://docs.rs/lightning/latest/lightning/events/enum.Event.html).

To handle the above events and other tasks that either can or should run in the background, LDK provides the ```process_events_async``` function within the ```lightning-background-processor``` crate. When instantiated, this starts an asynchronous process of handling lightning network node operations, such as maintaining our node's view of the network graph, updating the scorer, and more.
Copy link
Collaborator

Choose a reason for hiding this comment

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

Let's link to the crate docs.

docs/probing.md Outdated
let probing_cm = Arc::clone(&channel_manager);
let probing_graph = Arc::clone(&network_graph);
let probing_logger = Arc::clone(&logger);
let probing_scorer = Arc::clone(&scorer);
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 use a fresh scorer here rather than the same one used for updates, IIUC.

@Beige-Coffee
Copy link
Contributor Author

Thanks, @jkczyz and @ConorOkus for the comments! I'll incorporate both of your feedback over the next few days and let everyone know when it's updated 🫡

Update probing docs per feedback from Spiral team in PR.
@Beige-Coffee
Copy link
Contributor Author

@jkczyz @ConorOkus

I’ve reviewed both of your comments and updated the documentation accordingly. For clarity, I've listed my updates below. FYI, I did not list any simple changes I made (ex: updating or deleting a sentence, adding a link, fixing a typo, etc.).

  1. Added “A Note For Mobile Developers” section to “Best Practices” which briefly describes Rapid Gossip Sync and links to the LDK RGS blog.
  2. Added a new bullet point to the “Vary The Payment Amount” section in “Best Practices” to discuss how the user can select between the min/max payment and specify the route so that they can better hone their channel balance estimates.
  3. Added a fresh scorer to the probing code so that we’re not using the same one we’re updating. This is now consistent with recommendations within the documentation itself.

Happy to continue iterating, if there is more we want to add/change!

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.

Add best practices for probing
3 participants