Skip to content

Conversation

tnull
Copy link
Contributor

@tnull tnull commented Sep 5, 2025

This is the second PR in a series of PRs adding persistence to lightning-liquidity (see #4058). As this is already >1000LoC, I now decided to put this up as an intermediary step instead of adding everything in one go.

In this PR we add the serialization logic for for the LSPS2 and LSPS5 service handlers as well as for the event queue. We also have LiquidityManager take a KVStore towards which it persists the respetive peer states keyed by the counterparty's node id. LiquidityManager::new now also deserializes any previously-persisted state from that given KVStore. Note that so far we don't actually persist anything, as wiring up BackgroundProcessor to drive persistence will be part of the next PR (which will also make further optimizations, such as only persisting when needed, and persisting some imporant things in-line).

This also adds a bunch of boilerplate to account for both KVStore and KVStoreSync variants, following the approach we previously took with OutputSweeper etc.

cc @martinsaposnic

@tnull tnull requested a review from TheBlueMatt September 5, 2025 14:31
@ldk-reviews-bot
Copy link

ldk-reviews-bot commented Sep 5, 2025

👋 Thanks for assigning @TheBlueMatt as a reviewer!
I'll wait for their review and will help manage the review process.
Once they submit their review, I'll check if a second reviewer would be helpful.

@tnull tnull force-pushed the 2025-01-liquidity-persistence branch 2 times, most recently from 124211d to 26f3ce3 Compare September 5, 2025 14:41
@tnull tnull self-assigned this Sep 5, 2025
@tnull tnull added the weekly goal Someone wants to land this this week label Sep 5, 2025
@tnull tnull added this to the 0.2 milestone Sep 5, 2025
@tnull tnull moved this to Goal: Merge in Weekly Goals Sep 5, 2025
@tnull tnull force-pushed the 2025-01-liquidity-persistence branch 4 times, most recently from a98dff6 to d630c4e Compare September 5, 2025 14:58
@@ -248,7 +252,8 @@ where
/// [`LiquidityClientConfig`] and [`LiquidityServiceConfig`].
pub fn new_with_custom_time_provider(
entropy_source: ES, node_signer: NS, channel_manager: CM, chain_source: Option<C>,
chain_params: Option<ChainParameters>, service_config: Option<LiquidityServiceConfig>,
chain_params: Option<ChainParameters>, kv_store: Arc<dyn KVStore + Send + Sync>,
Copy link
Contributor

Choose a reason for hiding this comment

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

why is this dyn vs parameterizing the manager with the KVStore?

Copy link
Contributor Author

@tnull tnull Sep 5, 2025

Choose a reason for hiding this comment

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

why is this dyn vs parameterizing the manager with the KVStore?

Because dealing with the generics in the API that needs to support both KVStore and KVStoreSync is very cumbersome to impossible without rerwriting things even more fundamentally (I initially tried going the generics way).

For instance, we'd then also need to parametrize LSPS2ServiceHandler/LSPS5ServiceHandler with a KVStore, which would be part of the LiquidityManager API through LiquidityManager::lsps2_service_handler/lsps5_service_handler, in turn making wrapping it for LiquidityManagerSync ~impossible without having both generics on LiquidityManagerSync.

Copy link
Contributor

Choose a reason for hiding this comment

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

Because dealing with the generics in the API that needs to support both KVStore and KVStoreSync is very cumbersome

sorry for the dumb / high level question: why do we need to support both KVStore AND KVStoreSync?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Because we sync and async versions of the background processor, and the sync one only supports KVStoreSync.

There is also process_events_async_with_kv_store_sync now, which is the async variant that's still using a sync KVStore (which is what we're currently using in LDK Node still, though we want to drop that eventually once we got around to writing async wrappers for all remaining sync variants).

Copy link

codecov bot commented Sep 5, 2025

Codecov Report

❌ Patch coverage is 45.02924% with 282 lines in your changes missing coverage. Please review.
✅ Project coverage is 88.59%. Comparing base (bf87832) to head (dd43edc).
⚠️ Report is 24 commits behind head on main.

Files with missing lines Patch % Lines
lightning-liquidity/src/manager.rs 63.77% 68 Missing and 3 partials ⚠️
lightning-liquidity/src/persist.rs 47.12% 41 Missing and 5 partials ⚠️
lightning-liquidity/src/lsps2/service.rs 23.07% 38 Missing and 2 partials ⚠️
lightning-liquidity/src/lsps5/service.rs 20.51% 31 Missing ⚠️
lightning-liquidity/src/events/event_queue.rs 14.70% 29 Missing ⚠️
lightning-liquidity/src/events/mod.rs 0.00% 28 Missing ⚠️
lightning-liquidity/src/lsps5/msgs.rs 0.00% 16 Missing ⚠️
lightning-liquidity/src/lsps0/ser.rs 53.57% 10 Missing and 3 partials ⚠️
lightning-liquidity/src/lsps5/url_utils.rs 33.33% 8 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #4059      +/-   ##
==========================================
- Coverage   88.76%   88.59%   -0.17%     
==========================================
  Files         176      178       +2     
  Lines      129345   129876     +531     
  Branches   129345   129876     +531     
==========================================
+ Hits       114812   115064     +252     
- Misses      11925    12192     +267     
- Partials     2608     2620      +12     
Flag Coverage Δ
fuzzing 21.96% <13.20%> (-0.05%) ⬇️
tests 88.42% <44.05%> (-0.18%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

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

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

We add `KVStore` to `LiquidityManager`, which will be used in the next
commits. We also add a `LiquidityManagerSync` wrapper that wraps a the
`LiquidityManager` interface which will soon become async due to usage
of the async `KVStore`.
We add simple `persist` call to `LSPS2ServiceHandler` that sequentially
persist all the peer states under a key that encodes their node id.
We add simple `persist` call to `LSPS5ServiceHandler` that sequentially
persist all the peer states under a key that encodes their node id.
We add simple `persist` call to `EventQueue` that persists it under a
`event_queue` key.
.. this is likely only temporary necessary as we can drop our own
`dummy_waker` implementation once we bump MSRV.
@tnull tnull force-pushed the 2025-01-liquidity-persistence branch from d630c4e to 70118e7 Compare September 5, 2025 15:15
for fut in futures {
let res = fut.await;
if res.is_err() {
ret = res;
Copy link
Contributor

Choose a reason for hiding this comment

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

this only returns the last error found, it overwrites the others

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes, this is correct. I think we could alternatively abort on the first error, but I wanted to at least attempt them.

In the future (when looking into parallelizing persistence) we could use something like MultiResultFuturePoller to poll all futures in parallel and get all results. However, at some point we'll have to actually deal with the error values, and in the background processor that will likely mean logging and ignoring the persistence failure anyways. So not sure if it would make a whole lot of a difference to bubble up more than only the last-found error.

We read any previously-persisted state upon construction of
`LiquidityManager`.
We read any previously-persisted state upon construction of
`LiquidityManager`.
We read any previously-persisted state upon construction of
`LiquidityManager`.
@tnull tnull force-pushed the 2025-01-liquidity-persistence branch from 70118e7 to dd43edc Compare September 5, 2025 15:28
@martinsaposnic
Copy link
Contributor

this all LGTM.

I have a small concern: maybe I’m being a little paranoid, but read_lsps2_service_peer_states and read_lsps5_service_peer_states pull every entry from the KVStore into memory with no limit. That could lead to unbounded state, exhausting memory and crash. Maybe we can add a limit on how many entries we load into memory to protect against this dos?

not sure how realistic this is though. maybe an attacker could have access to or share the same storage with the victim, and they could dump effectively infinite data onto disk. in this scenario, probably the victim would be vulnerable to other attacks too, but still..

@tnull
Copy link
Contributor Author

tnull commented Sep 5, 2025

I have a small concern: maybe I’m being a little paranoid, but read_lsps2_service_peer_states and read_lsps5_service_peer_states pull every entry from the KVStore into memory with no limit. That could lead to unbounded state, exhausting memory and crash. Maybe we can add a limit on how many entries we load into memory to protect against this dos?

Reading state from disk (currently) happens on startup only, so crashing wouldn't be the worst thing, we would simply fail to start up properly. Some even argue that we need to panic if we hit any IO errors at this point to escalate to an operator. We could add some safeguard/upper bound, but I'm honestly not sure what it would protect against.

not sure how realistic this is though. maybe an attacker could have access to or share the same storage with the victim, and they could dump effectively infinite data onto disk. in this scenario, probably the victim would be vulnerable to other attacks too, but still..

Heh, well, if we assume the attacker has write access to our KVStore, we're very very screwed either way. Crashing could be the favorable outcome then, actually.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
weekly goal Someone wants to land this this week
Projects
Status: Goal: Merge
Development

Successfully merging this pull request may close these issues.

3 participants