Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
0f4db0d
Let `LiquidityManager` take a `KVStore` and add `Sync` wrapper
tnull Aug 13, 2025
53ebf78
Implement serialization for LSPS2 state types
tnull Jan 9, 2025
ede5898
Implement serialization for LSPS5 state types
tnull Aug 12, 2025
29e4aeb
Add symlink for `async_poll.rs`
tnull Sep 4, 2025
bf6d408
Add `LSPS2ServiceHandler` persistence
tnull Aug 13, 2025
d0760a2
Add `LSPS5ServiceHandler` persistence
tnull Sep 3, 2025
88a3cf5
Add `EventQueue` persistence
tnull Sep 5, 2025
651d4df
Read persisted LSPS2 service state in `LiquidityManager::new`
tnull Sep 11, 2025
aceb3c1
Read persisted LSPS5 service state in `LiquidityManager::new`
tnull Sep 4, 2025
6a01d75
Read persisted event queue state in `LiquidityManager::new`
tnull Sep 5, 2025
5fb21bd
Have background processor task drive `LiquidityManger` persistence
tnull Sep 11, 2025
179420c
Skip `EventQueue` persistence if unnecessary
tnull Sep 15, 2025
631d3f0
Skip `LSPS2ServiceHandler` persistence if unnecessary
tnull Sep 16, 2025
01a94e6
Add `LSPS2ServiceHandlerSync` wrapper
tnull Sep 16, 2025
a72bbee
Add inline persistence with async `LSPS2ServiceHandler` API methods
tnull Sep 16, 2025
8652305
Skip `LSPS5ServiceHandler` persistence if unnecessary
tnull Sep 16, 2025
2983178
Add test asserting LSPS2 service state is persisted across restarts
tnull Sep 18, 2025
52aa286
Add test asserting LSPS5 service state is persisted across restarts
tnull Sep 18, 2025
f70a902
Remove pruned LSPS2/LSPS5 peer state entries from the `KVStore`
tnull Sep 19, 2025
454f0e6
Mention `user_channel_id` uniqueness in `invoice_params_generated` docs
tnull Sep 23, 2025
adc8a87
Log `PublicKey`s as `Display`, not `Debug`
tnull Sep 24, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions fuzz/src/lsps_message.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ use lightning::util::test_utils::{
};

use lightning_liquidity::lsps0::ser::LSPS_MESSAGE_TYPE_ID;
use lightning_liquidity::LiquidityManager;
use lightning_liquidity::LiquidityManagerSync;

use core::time::Duration;

Expand Down Expand Up @@ -77,15 +77,16 @@ pub fn do_test(data: &[u8]) {
genesis_block.header.time,
));

let liquidity_manager = Arc::new(LiquidityManager::new(
let liquidity_manager = Arc::new(LiquidityManagerSync::new(
Arc::clone(&keys_manager),
Arc::clone(&keys_manager),
Arc::clone(&manager),
None::<Arc<dyn Filter + Send + Sync>>,
None,
kv_store,
None,
None,
));
).unwrap());
let mut reader = data;
if let Ok(Some(msg)) = liquidity_manager.read(LSPS_MESSAGE_TYPE_ID, &mut reader) {
let secp = Secp256k1::signing_only();
Expand Down
1 change: 1 addition & 0 deletions lightning-background-processor/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ possiblyrandom = { version = "0.2", path = "../possiblyrandom", default-features
tokio = { version = "1.35", features = [ "macros", "rt", "rt-multi-thread", "sync", "time" ] }
lightning = { version = "0.2.0", path = "../lightning", features = ["_test_utils"] }
lightning-invoice = { version = "0.34.0", path = "../lightning-invoice" }
lightning-liquidity = { version = "0.2.0", path = "../lightning-liquidity", default-features = false, features = ["_test_utils"] }
lightning-persister = { version = "0.2.0", path = "../lightning-persister" }

[lints]
Expand Down
158 changes: 110 additions & 48 deletions lightning-background-processor/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,8 @@ use lightning::util::wakers::Sleeper;
use lightning_rapid_gossip_sync::RapidGossipSync;

use lightning_liquidity::ALiquidityManager;
#[cfg(feature = "std")]
use lightning_liquidity::ALiquidityManagerSync;

use core::ops::Deref;
use core::time::Duration;
Expand Down Expand Up @@ -424,6 +426,31 @@ pub const NO_LIQUIDITY_MANAGER: Option<
CM = &DynChannelManager,
Filter = dyn chain::Filter,
C = &dyn chain::Filter,
KVStore = dyn lightning::util::persist::KVStore,
K = &dyn lightning::util::persist::KVStore,
TimeProvider = dyn lightning_liquidity::utils::time::TimeProvider,
TP = &dyn lightning_liquidity::utils::time::TimeProvider,
> + Send
+ Sync,
>,
> = None;

/// When initializing a background processor without a liquidity manager, this can be used to avoid
/// specifying a concrete `LiquidityManagerSync` type.
#[cfg(all(not(c_bindings), feature = "std"))]
pub const NO_LIQUIDITY_MANAGER_SYNC: Option<
Arc<
dyn ALiquidityManagerSync<
EntropySource = dyn EntropySource,
ES = &dyn EntropySource,
NodeSigner = dyn lightning::sign::NodeSigner,
NS = &dyn lightning::sign::NodeSigner,
AChannelManager = DynChannelManager,
CM = &DynChannelManager,
Filter = dyn chain::Filter,
C = &dyn chain::Filter,
KVStoreSync = dyn lightning::util::persist::KVStoreSync,
KS = &dyn lightning::util::persist::KVStoreSync,
TimeProvider = dyn lightning_liquidity::utils::time::TimeProvider,
TP = &dyn lightning_liquidity::utils::time::TimeProvider,
> + Send
Expand Down Expand Up @@ -544,45 +571,49 @@ pub(crate) mod futures_util {
unsafe { Waker::from_raw(RawWaker::new(core::ptr::null(), &DUMMY_WAKER_VTABLE)) }
}

enum JoinerResult<E, F: Future<Output = Result<(), E>> + Unpin> {
enum JoinerResult<ERR, F: Future<Output = Result<(), ERR>> + Unpin> {
Pending(Option<F>),
Ready(Result<(), E>),
Ready(Result<(), ERR>),
}

pub(crate) struct Joiner<
E,
A: Future<Output = Result<(), E>> + Unpin,
B: Future<Output = Result<(), E>> + Unpin,
C: Future<Output = Result<(), E>> + Unpin,
D: Future<Output = Result<(), E>> + Unpin,
ERR,
A: Future<Output = Result<(), ERR>> + Unpin,
B: Future<Output = Result<(), ERR>> + Unpin,
C: Future<Output = Result<(), ERR>> + Unpin,
D: Future<Output = Result<(), ERR>> + Unpin,
E: Future<Output = Result<(), ERR>> + Unpin,
> {
a: JoinerResult<E, A>,
b: JoinerResult<E, B>,
c: JoinerResult<E, C>,
d: JoinerResult<E, D>,
a: JoinerResult<ERR, A>,
b: JoinerResult<ERR, B>,
c: JoinerResult<ERR, C>,
d: JoinerResult<ERR, D>,
e: JoinerResult<ERR, E>,
}

impl<
E,
A: Future<Output = Result<(), E>> + Unpin,
B: Future<Output = Result<(), E>> + Unpin,
C: Future<Output = Result<(), E>> + Unpin,
D: Future<Output = Result<(), E>> + Unpin,
> Joiner<E, A, B, C, D>
ERR,
A: Future<Output = Result<(), ERR>> + Unpin,
B: Future<Output = Result<(), ERR>> + Unpin,
C: Future<Output = Result<(), ERR>> + Unpin,
D: Future<Output = Result<(), ERR>> + Unpin,
E: Future<Output = Result<(), ERR>> + Unpin,
> Joiner<ERR, A, B, C, D, E>
{
pub(crate) fn new() -> Self {
Self {
a: JoinerResult::Pending(None),
b: JoinerResult::Pending(None),
c: JoinerResult::Pending(None),
d: JoinerResult::Pending(None),
e: JoinerResult::Pending(None),
}
}

pub(crate) fn set_a(&mut self, fut: A) {
self.a = JoinerResult::Pending(Some(fut));
}
pub(crate) fn set_a_res(&mut self, res: Result<(), E>) {
pub(crate) fn set_a_res(&mut self, res: Result<(), ERR>) {
self.a = JoinerResult::Ready(res);
}
pub(crate) fn set_b(&mut self, fut: B) {
Expand All @@ -594,19 +625,23 @@ pub(crate) mod futures_util {
pub(crate) fn set_d(&mut self, fut: D) {
self.d = JoinerResult::Pending(Some(fut));
}
pub(crate) fn set_e(&mut self, fut: E) {
self.e = JoinerResult::Pending(Some(fut));
}
}

impl<
E,
A: Future<Output = Result<(), E>> + Unpin,
B: Future<Output = Result<(), E>> + Unpin,
C: Future<Output = Result<(), E>> + Unpin,
D: Future<Output = Result<(), E>> + Unpin,
> Future for Joiner<E, A, B, C, D>
ERR,
A: Future<Output = Result<(), ERR>> + Unpin,
B: Future<Output = Result<(), ERR>> + Unpin,
C: Future<Output = Result<(), ERR>> + Unpin,
D: Future<Output = Result<(), ERR>> + Unpin,
E: Future<Output = Result<(), ERR>> + Unpin,
> Future for Joiner<ERR, A, B, C, D, E>
where
Joiner<E, A, B, C, D>: Unpin,
Joiner<ERR, A, B, C, D, E>: Unpin,
{
type Output = [Result<(), E>; 4];
type Output = [Result<(), ERR>; 5];
fn poll(mut self: Pin<&mut Self>, ctx: &mut core::task::Context<'_>) -> Poll<Self::Output> {
let mut all_complete = true;
macro_rules! handle {
Expand All @@ -615,7 +650,7 @@ pub(crate) mod futures_util {
JoinerResult::Pending(None) => {
self.$val = JoinerResult::Ready(Ok(()));
},
JoinerResult::<E, _>::Pending(Some(ref mut val)) => {
JoinerResult::<ERR, _>::Pending(Some(ref mut val)) => {
match Pin::new(val).poll(ctx) {
Poll::Ready(res) => {
self.$val = JoinerResult::Ready(res);
Expand All @@ -633,9 +668,10 @@ pub(crate) mod futures_util {
handle!(b);
handle!(c);
handle!(d);
handle!(e);

if all_complete {
let mut res = [Ok(()), Ok(()), Ok(()), Ok(())];
let mut res = [Ok(()), Ok(()), Ok(()), Ok(()), Ok(())];
if let JoinerResult::Ready(ref mut val) = &mut self.a {
core::mem::swap(&mut res[0], val);
}
Expand All @@ -648,6 +684,9 @@ pub(crate) mod futures_util {
if let JoinerResult::Ready(ref mut val) = &mut self.d {
core::mem::swap(&mut res[3], val);
}
if let JoinerResult::Ready(ref mut val) = &mut self.e {
core::mem::swap(&mut res[4], val);
}
Poll::Ready(res)
} else {
Poll::Pending
Expand Down Expand Up @@ -731,7 +770,7 @@ use futures_util::{dummy_waker, Joiner, OptionalSelector, Selector, SelectorOutp
/// # type P2PGossipSync<UL> = lightning::routing::gossip::P2PGossipSync<Arc<NetworkGraph>, Arc<UL>, Arc<Logger>>;
/// # type ChannelManager<B, F, FE> = lightning::ln::channelmanager::SimpleArcChannelManager<ChainMonitor<B, F, FE>, B, FE, Logger>;
/// # type OnionMessenger<B, F, FE> = lightning::onion_message::messenger::OnionMessenger<Arc<lightning::sign::KeysManager>, Arc<lightning::sign::KeysManager>, Arc<Logger>, Arc<ChannelManager<B, F, FE>>, Arc<lightning::onion_message::messenger::DefaultMessageRouter<Arc<NetworkGraph>, Arc<Logger>, Arc<lightning::sign::KeysManager>>>, Arc<ChannelManager<B, F, FE>>, lightning::ln::peer_handler::IgnoringMessageHandler, lightning::ln::peer_handler::IgnoringMessageHandler, lightning::ln::peer_handler::IgnoringMessageHandler>;
/// # type LiquidityManager<B, F, FE> = lightning_liquidity::LiquidityManager<Arc<lightning::sign::KeysManager>, Arc<lightning::sign::KeysManager>, Arc<ChannelManager<B, F, FE>>, Arc<F>, Arc<DefaultTimeProvider>>;
/// # type LiquidityManager<B, F, FE> = lightning_liquidity::LiquidityManager<Arc<lightning::sign::KeysManager>, Arc<lightning::sign::KeysManager>, Arc<ChannelManager<B, F, FE>>, Arc<F>, Arc<Store>, Arc<DefaultTimeProvider>>;
/// # type Scorer = RwLock<lightning::routing::scoring::ProbabilisticScorer<Arc<NetworkGraph>, Arc<Logger>>>;
/// # type PeerManager<B, F, FE, UL> = lightning::ln::peer_handler::SimpleArcPeerManager<SocketDescriptor, ChainMonitor<B, F, FE>, B, FE, Arc<UL>, Logger, F, StoreSync>;
/// # type OutputSweeper<B, D, FE, F, O> = lightning::util::sweep::OutputSweeper<Arc<B>, Arc<D>, Arc<FE>, Arc<F>, Arc<Store>, Arc<Logger>, Arc<O>>;
Expand Down Expand Up @@ -976,7 +1015,7 @@ where
OptionalSelector { optional_future: None }
};
let lm_fut = if let Some(lm) = liquidity_manager.as_ref() {
let fut = lm.get_lm().get_pending_msgs_future();
let fut = lm.get_lm().get_pending_msgs_or_needs_persist_future();
OptionalSelector { optional_future: Some(fut) }
} else {
OptionalSelector { optional_future: None }
Expand Down Expand Up @@ -1179,6 +1218,17 @@ where
None => {},
}

if let Some(liquidity_manager) = liquidity_manager.as_ref() {
log_trace!(logger, "Persisting LiquidityManager...");
let fut = async {
liquidity_manager.get_lm().persist().await.map_err(|e| {
log_error!(logger, "Persisting LiquidityManager failed: {}", e);
e
})
};
futures.set_e(Box::pin(fut));
}

// Run persistence tasks in parallel and exit if any of them returns an error.
for res in futures.await {
res?;
Expand Down Expand Up @@ -1450,7 +1500,7 @@ impl BackgroundProcessor {
CM::Target: AChannelManager,
OM::Target: AOnionMessenger,
PM::Target: APeerManager,
LM::Target: ALiquidityManager,
LM::Target: ALiquidityManagerSync,
D::Target: ChangeDestinationSourceSync,
O::Target: 'static + OutputSpender,
K::Target: 'static + KVStoreSync,
Expand Down Expand Up @@ -1535,7 +1585,7 @@ impl BackgroundProcessor {
&channel_manager.get_cm().get_event_or_persistence_needed_future(),
&chain_monitor.get_update_future(),
&om.get_om().get_update_future(),
&lm.get_lm().get_pending_msgs_future(),
&lm.get_lm().get_pending_msgs_or_needs_persist_future(),
),
(Some(om), None) => Sleeper::from_three_futures(
&channel_manager.get_cm().get_event_or_persistence_needed_future(),
Expand All @@ -1545,7 +1595,7 @@ impl BackgroundProcessor {
(None, Some(lm)) => Sleeper::from_three_futures(
&channel_manager.get_cm().get_event_or_persistence_needed_future(),
&chain_monitor.get_update_future(),
&lm.get_lm().get_pending_msgs_future(),
&lm.get_lm().get_pending_msgs_or_needs_persist_future(),
),
(None, None) => Sleeper::from_two_futures(
&channel_manager.get_cm().get_event_or_persistence_needed_future(),
Expand Down Expand Up @@ -1579,6 +1629,13 @@ impl BackgroundProcessor {
log_trace!(logger, "Done persisting ChannelManager.");
}

if let Some(liquidity_manager) = liquidity_manager.as_ref() {
log_trace!(logger, "Persisting LiquidityManager...");
let _ = liquidity_manager.get_lm().persist().map_err(|e| {
log_error!(logger, "Persisting LiquidityManager failed: {}", e);
});
}

// Note that we want to run a graph prune once not long after startup before
// falling back to our usual hourly prunes. This avoids short-lived clients never
// pruning their network graph. We run once 60 seconds after startup before
Expand Down Expand Up @@ -1793,7 +1850,7 @@ mod tests {
use lightning::util::test_utils;
use lightning::{get_event, get_event_msg};
use lightning_liquidity::utils::time::DefaultTimeProvider;
use lightning_liquidity::LiquidityManager;
use lightning_liquidity::{ALiquidityManagerSync, LiquidityManagerSync};
use lightning_persister::fs_store::FilesystemStore;
use lightning_rapid_gossip_sync::RapidGossipSync;
use std::collections::VecDeque;
Expand Down Expand Up @@ -1890,11 +1947,12 @@ mod tests {
IgnoringMessageHandler,
>;

type LM = LiquidityManager<
type LM = LiquidityManagerSync<
Arc<KeysManager>,
Arc<KeysManager>,
Arc<ChannelManager>,
Arc<dyn Filter + Sync + Send>,
Arc<Persister>,
Arc<DefaultTimeProvider>,
>;

Expand Down Expand Up @@ -2342,15 +2400,19 @@ mod tests {
Arc::clone(&logger),
Arc::clone(&keys_manager),
));
let liquidity_manager = Arc::new(LiquidityManager::new(
Arc::clone(&keys_manager),
Arc::clone(&keys_manager),
Arc::clone(&manager),
None,
None,
None,
None,
));
let liquidity_manager = Arc::new(
LiquidityManagerSync::new(
Arc::clone(&keys_manager),
Arc::clone(&keys_manager),
Arc::clone(&manager),
None,
None,
Arc::clone(&kv_store),
None,
None,
)
.unwrap(),
);
let node = Node {
node: manager,
p2p_gossip_sync,
Expand Down Expand Up @@ -2727,7 +2789,7 @@ mod tests {
Some(Arc::clone(&nodes[0].messenger)),
nodes[0].rapid_gossip_sync(),
Arc::clone(&nodes[0].peer_manager),
Some(Arc::clone(&nodes[0].liquidity_manager)),
Some(nodes[0].liquidity_manager.get_lm_async()),
Some(nodes[0].sweeper.sweeper_async()),
Arc::clone(&nodes[0].logger),
Some(Arc::clone(&nodes[0].scorer)),
Expand Down Expand Up @@ -3236,7 +3298,7 @@ mod tests {
Some(Arc::clone(&nodes[0].messenger)),
nodes[0].rapid_gossip_sync(),
Arc::clone(&nodes[0].peer_manager),
Some(Arc::clone(&nodes[0].liquidity_manager)),
Some(nodes[0].liquidity_manager.get_lm_async()),
Some(nodes[0].sweeper.sweeper_async()),
Arc::clone(&nodes[0].logger),
Some(Arc::clone(&nodes[0].scorer)),
Expand Down Expand Up @@ -3451,7 +3513,7 @@ mod tests {
Some(Arc::clone(&nodes[0].messenger)),
nodes[0].no_gossip_sync(),
Arc::clone(&nodes[0].peer_manager),
Some(Arc::clone(&nodes[0].liquidity_manager)),
Some(nodes[0].liquidity_manager.get_lm_async()),
Some(nodes[0].sweeper.sweeper_async()),
Arc::clone(&nodes[0].logger),
Some(Arc::clone(&nodes[0].scorer)),
Expand Down Expand Up @@ -3500,7 +3562,7 @@ mod tests {
crate::NO_ONION_MESSENGER,
nodes[0].no_gossip_sync(),
Arc::clone(&nodes[0].peer_manager),
crate::NO_LIQUIDITY_MANAGER,
crate::NO_LIQUIDITY_MANAGER_SYNC,
Some(Arc::clone(&nodes[0].sweeper)),
Arc::clone(&nodes[0].logger),
Some(Arc::clone(&nodes[0].scorer)),
Expand Down
2 changes: 2 additions & 0 deletions lightning-liquidity/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,13 @@ default = ["std", "time"]
std = ["lightning/std"]
time = ["std"]
backtrace = ["dep:backtrace"]
_test_utils = []

[dependencies]
lightning = { version = "0.2.0", path = "../lightning", default-features = false }
lightning-types = { version = "0.3.0", path = "../lightning-types", default-features = false }
lightning-invoice = { version = "0.34.0", path = "../lightning-invoice", default-features = false, features = ["serde"] }
lightning-macros = { version = "0.2", path = "../lightning-macros" }

bitcoin = { version = "0.32.2", default-features = false, features = ["serde"] }

Expand Down
Loading
Loading