Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
287609a
Let `LiquidityManager` take a `KVStore` and add `Sync` wrapper
tnull Aug 13, 2025
c8ff1ab
Implement serialization for LSPS2 state types
tnull Jan 9, 2025
133bebe
Implement serialization for LSPS5 state types
tnull Aug 12, 2025
ca0ba23
Implement serialization for event queue types
tnull Sep 5, 2025
5b771d7
f Always write *something* for each event to account for `MaybeReadable`
tnull Sep 17, 2025
3990578
Add symlink for `async_poll.rs`
tnull Sep 4, 2025
d648463
Add `LSPS2ServiceHandler` persistence
tnull Aug 13, 2025
590dcd6
f Hold lock while creating write future
tnull Sep 17, 2025
cc6203d
Add `LSPS5ServiceHandler` persistence
tnull Sep 3, 2025
56ee60a
f Hold lock while creating write future
tnull Sep 17, 2025
e290a88
Add `EventQueue` persistence
tnull Sep 5, 2025
f34d7df
f Move `Readable`/`Writeable` from `LiquidityEvent` to the ser wrappers
tnull Sep 18, 2025
f06f39e
f Add comment on `OpenChannel` idempotency
tnull Sep 19, 2025
639dee1
Read persisted LSPS2 service state in `LiquidityManager::new`
tnull Sep 11, 2025
46df4b8
Read persisted LSPS5 service state in `LiquidityManager::new`
tnull Sep 4, 2025
c38006e
Read persisted event queue state in `LiquidityManager::new`
tnull Sep 5, 2025
917f036
Have background processor task drive `LiquidityManger` persistence
tnull Sep 11, 2025
7bac177
f Parallelize persistence in BP
tnull Sep 17, 2025
f88cca3
Skip `EventQueue` persistence if unnecessary
tnull Sep 15, 2025
7ba3843
f Hold lock while creating write future
tnull Sep 17, 2025
f05ce96
Skip `LSPS2ServiceHandler` persistence if unnecessary
tnull Sep 16, 2025
e947e2b
f Just continue on `persist_peer_state` race
tnull Sep 17, 2025
d7d8a03
Add `LSPS2ServiceHandlerSync` wrapper
tnull Sep 16, 2025
b7be9ab
f Use lifetimes in `Sync` wrapper
tnull Sep 17, 2025
b50fa9a
f Also drop inner `Arc`
tnull Sep 17, 2025
b9378d9
Add inline persistence with async `LSPS2ServiceHandler` API methods
tnull Sep 16, 2025
cb74d6a
f Allow for clippy
tnull Sep 17, 2025
05f534e
Skip `LSPS5ServiceHandler` persistence if unnecessary
tnull Sep 16, 2025
0f98b80
f Just continue on `persist_peer_state` race
tnull Sep 17, 2025
66aafee
Add test asserting LSPS2 service state is persisted across restarts
tnull Sep 18, 2025
87a2987
Add test asserting LSPS5 service state is persisted across restarts
tnull Sep 18, 2025
5fb2ae3
Remove pruned LSPS2/LSPS5 peer state entries from the `KVStore`
tnull Sep 19, 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
131 changes: 84 additions & 47 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 @@ -444,45 +446,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 @@ -494,19 +500,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 @@ -515,7 +525,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 @@ -533,9 +543,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 @@ -548,6 +559,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 @@ -631,7 +645,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 @@ -876,7 +890,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 @@ -1079,6 +1093,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 @@ -1350,7 +1375,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 @@ -1435,7 +1460,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 @@ -1445,7 +1470,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 @@ -1479,6 +1504,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 @@ -1693,7 +1725,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 @@ -1790,11 +1822,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 @@ -2242,15 +2275,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 @@ -2627,7 +2664,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 @@ -3136,7 +3173,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 @@ -3351,7 +3388,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
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