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 numberDiff line numberDiff 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 DownExpand 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 numberDiff line numberDiff 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 numberDiff line numberDiff 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 DownExpand 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 DownExpand 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 DownExpand 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 DownExpand 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 DownExpand 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 DownExpand 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 DownExpand 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 DownExpand 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 DownExpand 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 DownExpand 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 DownExpand 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 DownExpand 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 DownExpand 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 DownExpand 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 DownExpand 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 numberDiff line numberDiff 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
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
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 numberDiff line numberDiff 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 DownExpand 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 numberDiff line numberDiff 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 numberDiff line numberDiff 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 DownExpand 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 DownExpand 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 DownExpand 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 DownExpand 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 DownExpand 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 DownExpand 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 DownExpand 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 DownExpand 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 DownExpand 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 DownExpand 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 DownExpand 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 DownExpand 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 DownExpand 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 DownExpand 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 DownExpand 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 numberDiff line numberDiff 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
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
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 numberDiff line numberDiff 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 DownExpand 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 numberDiff line numberDiff 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 numberDiff line numberDiff 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 DownExpand 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 DownExpand 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 DownExpand 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 DownExpand 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 DownExpand 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 DownExpand 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 DownExpand 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 DownExpand 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 DownExpand 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 DownExpand 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 DownExpand 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 DownExpand 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 DownExpand 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 DownExpand 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 DownExpand 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 numberDiff line numberDiff 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
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
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 numberDiff line numberDiff 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 DownExpand 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 numberDiff line numberDiff 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 numberDiff line numberDiff 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 DownExpand 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 DownExpand 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 DownExpand 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 DownExpand 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 DownExpand 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 DownExpand 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 DownExpand 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 DownExpand 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 DownExpand 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 DownExpand 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 DownExpand 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 DownExpand 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 DownExpand 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 DownExpand 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 DownExpand 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 numberDiff line numberDiff 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
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
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 numberDiff line numberDiff 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 DownExpand 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 numberDiff line numberDiff 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 numberDiff line numberDiff 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 DownExpand 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 DownExpand 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 DownExpand 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 DownExpand 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 DownExpand 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 DownExpand 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 DownExpand 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 DownExpand 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 DownExpand 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 DownExpand 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 DownExpand 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 DownExpand 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 DownExpand 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 DownExpand 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 DownExpand 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 numberDiff line numberDiff 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
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
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 numberDiff line numberDiff 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 DownExpand 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 numberDiff line numberDiff 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 numberDiff line numberDiff 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 DownExpand 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 DownExpand 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 DownExpand 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 DownExpand 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 DownExpand 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 DownExpand 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 DownExpand 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 DownExpand 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 DownExpand 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 DownExpand 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 DownExpand 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 DownExpand 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 DownExpand 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 DownExpand 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 DownExpand 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 numberDiff line numberDiff 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
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
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 numberDiff line numberDiff 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 DownExpand 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 numberDiff line numberDiff 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 numberDiff line numberDiff 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 DownExpand 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 DownExpand 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 DownExpand 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 DownExpand 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 DownExpand 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 DownExpand 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 DownExpand 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 DownExpand 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 DownExpand 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 DownExpand 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 DownExpand 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 DownExpand 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 DownExpand 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 DownExpand 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 DownExpand 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 numberDiff line numberDiff 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
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
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 numberDiff line numberDiff 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 DownExpand 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 numberDiff line numberDiff 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 numberDiff line numberDiff 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 DownExpand 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 DownExpand 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 DownExpand 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 DownExpand 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 DownExpand 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 DownExpand 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 DownExpand 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 DownExpand 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 DownExpand 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 DownExpand 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 DownExpand 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 DownExpand 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 DownExpand 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 DownExpand 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 DownExpand 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 numberDiff line numberDiff 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