Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
f19c1ed
LSPS1: Add initial integration test
martinsaposnic Jun 16, 2025
ce7ff1d
Cleanup unused code
tnull Nov 16, 2025
810f026
Drop `chain_source` from `LSPS1ServiceHandler`
tnull Dec 9, 2025
601bdbf
Drop `Listen`/`Confirm`/etc from `LiquidityManager`
tnull Dec 9, 2025
7a3e7c9
Move `PeerState` and related types to `peer_state.rs` module
tnull Nov 16, 2025
114a588
Drop bogus channel state handling
tnull Nov 16, 2025
4ed2173
Replace `insert_outbound_channel` with `PeerState::new_order`
tnull Nov 16, 2025
f5adabf
Use `PeerState::{get_order, has_active_requests}` instead of map
tnull Nov 16, 2025
536a45c
Use `PeerState::{register,remove}_request` instead of map access
tnull Nov 16, 2025
d96c0c3
Drop `OutboundCRChannel`
tnull Nov 16, 2025
8ec2fa5
Actually remember the order state in `ChannelOrder`
tnull Nov 16, 2025
cc587a5
`LSPS1ServiceHandler`: Use `TimeProvider` when creating new orders
tnull Dec 10, 2025
24b64bd
Require `supported_options` in `LSPS1ServiceConfig`
tnull Dec 10, 2025
169fe92
Remove pending request in `update_order_status`
tnull Dec 10, 2025
396528b
Minor cleanups and typo fixes
tnull Dec 10, 2025
03fc43d
Respond to `GetOrder` requests from our saved state
tnull Dec 11, 2025
300e33a
Add serialization logic for LSPS1 `PeerState` types
tnull Dec 11, 2025
6cce16b
Implement `LSPS1ServiceHandler` persistence and state pruning
tnull Dec 11, 2025
8ce495f
Read persisted LSPS1ServiceHandler state on startup
tnull Dec 12, 2025
da6e3af
Add test case asserting `LSPS1ServiceState` is persisted across restarts
tnull Dec 12, 2025
3e3e8e3
Add some checks on provided payment details
tnull Dec 12, 2025
8e72922
Don't hold write lock in `LSPS{1,2}ServiceHandler::peer_disconnected`
tnull Dec 12, 2025
2ef14e0
Add `invalid_token_provided` API method
tnull Dec 12, 2025
6d38350
Add test case for `invalid_token_provided` flow
tnull Dec 12, 2025
4205c68
Drop `lsps1_service` cfg flag
tnull Dec 12, 2025
267f12a
Bump `lightning-liquidity` version number
tnull Dec 12, 2025
fb519ab
Fix clippy lints
tnull Dec 12, 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
2 changes: 0 additions & 2 deletions ci/ci-tests.sh
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,4 @@ RUSTFLAGS="--cfg=taproot" cargo test --verbose --color always -p lightning
[ "$CI_MINIMIZE_DISK_USAGE" != "" ] && cargo clean
RUSTFLAGS="--cfg=simple_close" cargo test --verbose --color always -p lightning
[ "$CI_MINIMIZE_DISK_USAGE" != "" ] && cargo clean
RUSTFLAGS="--cfg=lsps1_service" cargo test --verbose --color always -p lightning-liquidity
[ "$CI_MINIMIZE_DISK_USAGE" != "" ] && cargo clean
RUSTFLAGS="--cfg=peer_storage" cargo test --verbose --color always -p lightning
2 changes: 0 additions & 2 deletions fuzz/src/lsps_message.rs
Original file line number Diff line number Diff line change
Expand Up @@ -81,8 +81,6 @@ pub fn do_test(data: &[u8]) {
Arc::clone(&keys_manager),
Arc::clone(&keys_manager),
Arc::clone(&manager),
None::<Arc<dyn Filter + Send + Sync>>,
None,
kv_store,
Arc::clone(&tx_broadcaster),
None,
Expand Down
4 changes: 2 additions & 2 deletions lightning-background-processor/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -26,14 +26,14 @@ bitcoin_hashes = { version = "0.14.0", default-features = false }
bitcoin-io = { version = "0.1.2", default-features = false }
lightning = { version = "0.3.0", path = "../lightning", default-features = false }
lightning-rapid-gossip-sync = { version = "0.2.0", path = "../lightning-rapid-gossip-sync", default-features = false }
lightning-liquidity = { version = "0.2.0", path = "../lightning-liquidity", default-features = false }
lightning-liquidity = { version = "0.3.0", path = "../lightning-liquidity", default-features = false }
possiblyrandom = { version = "0.2", path = "../possiblyrandom", default-features = false }

[dev-dependencies]
tokio = { version = "1.35", features = [ "macros", "rt", "rt-multi-thread", "sync", "time" ] }
lightning = { version = "0.3.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-liquidity = { version = "0.3.0", path = "../lightning-liquidity", default-features = false, features = ["_test_utils"] }
lightning-persister = { version = "0.2.0", path = "../lightning-persister" }

[lints]
Expand Down
29 changes: 11 additions & 18 deletions lightning-background-processor/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -467,8 +467,6 @@ pub const NO_LIQUIDITY_MANAGER: Option<
NS = &(dyn lightning::sign::NodeSigner + Send + Sync),
AChannelManager = DynChannelManager,
CM = &DynChannelManager,
Filter = dyn chain::Filter + Send + Sync,
C = &(dyn chain::Filter + Send + Sync),
KVStore = DummyKVStore,
K = &DummyKVStore,
TimeProvider = dyn lightning_liquidity::utils::time::TimeProvider + Send + Sync,
Expand All @@ -494,8 +492,6 @@ pub const NO_LIQUIDITY_MANAGER_SYNC: Option<
NS = &(dyn lightning::sign::NodeSigner + Send + Sync),
AChannelManager = DynChannelManager,
CM = &DynChannelManager,
Filter = dyn chain::Filter + Send + Sync,
C = &(dyn chain::Filter + Send + Sync),
KVStoreSync = dyn lightning::util::persist::KVStoreSync + Send + Sync,
KS = &(dyn lightning::util::persist::KVStoreSync + Send + Sync),
TimeProvider = dyn lightning_liquidity::utils::time::TimeProvider + Send + Sync,
Expand Down Expand Up @@ -823,7 +819,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<Store>, Arc<DefaultTimeProvider>, Arc<B>>;
/// # type LiquidityManager<B, F, FE> = lightning_liquidity::LiquidityManager<Arc<lightning::sign::KeysManager>, Arc<lightning::sign::KeysManager>, Arc<ChannelManager<B, F, FE>>, Arc<Store>, Arc<DefaultTimeProvider>, Arc<B>>;
/// # type Scorer = RwLock<lightning::routing::scoring::ProbabilisticScorer<Arc<NetworkGraph>, Arc<Logger>>>;
/// # type PeerManager<B, F, FE, UL> = lightning::ln::peer_handler::SimpleArcPeerManager<SocketDescriptor, ChainMonitor<B, F, FE>, B, FE, Arc<UL>, Logger, F, StoreSync>;
/// # type OutputSweeper<B, D, FE, F, O> = lightning::util::sweep::OutputSweeper<Arc<B>, Arc<D>, Arc<FE>, Arc<F>, Arc<Store>, Arc<Logger>, Arc<O>>;
Expand Down Expand Up @@ -1872,7 +1868,7 @@ mod tests {
use core::sync::atomic::{AtomicBool, Ordering};
use lightning::chain::channelmonitor::ANTI_REORG_DELAY;
use lightning::chain::transaction::OutPoint;
use lightning::chain::{chainmonitor, BestBlock, Confirm, Filter};
use lightning::chain::{chainmonitor, BestBlock, Confirm};
use lightning::events::{Event, PathFailure, ReplayEvent};
use lightning::ln::channelmanager;
use lightning::ln::channelmanager::{
Expand Down Expand Up @@ -2008,7 +2004,6 @@ mod tests {
Arc<KeysManager>,
Arc<KeysManager>,
Arc<ChannelManager>,
Arc<dyn Filter + Sync + Send>,
Arc<Persister>,
DefaultTimeProvider,
Arc<test_utils::TestBroadcaster>,
Expand Down Expand Up @@ -2465,8 +2460,6 @@ mod tests {
Arc::clone(&keys_manager),
Arc::clone(&keys_manager),
Arc::clone(&manager),
None,
None,
Arc::clone(&kv_store),
Arc::clone(&tx_broadcaster),
None,
Expand Down Expand Up @@ -2843,10 +2836,10 @@ mod tests {
let kv_store = KVStoreSyncWrapper(kv_store_sync);

// Yes, you can unsafe { turn off the borrow checker }
let lm_async: &'static LiquidityManager<_, _, _, _, _, _, _> = unsafe {
let lm_async: &'static LiquidityManager<_, _, _, _, _, _> = unsafe {
&*(nodes[0].liquidity_manager.get_lm_async()
as *const LiquidityManager<_, _, _, _, _, _, _>)
as &'static LiquidityManager<_, _, _, _, _, _, _>
as *const LiquidityManager<_, _, _, _, _, _>)
as &'static LiquidityManager<_, _, _, _, _, _>
};
let sweeper_async: &'static OutputSweeper<_, _, _, _, _, _, _> = unsafe {
&*(nodes[0].sweeper.sweeper_async() as *const OutputSweeper<_, _, _, _, _, _, _>)
Expand Down Expand Up @@ -3362,10 +3355,10 @@ mod tests {
let kv_store = KVStoreSyncWrapper(kv_store_sync);

// Yes, you can unsafe { turn off the borrow checker }
let lm_async: &'static LiquidityManager<_, _, _, _, _, _, _> = unsafe {
let lm_async: &'static LiquidityManager<_, _, _, _, _, _> = unsafe {
&*(nodes[0].liquidity_manager.get_lm_async()
as *const LiquidityManager<_, _, _, _, _, _, _>)
as &'static LiquidityManager<_, _, _, _, _, _, _>
as *const LiquidityManager<_, _, _, _, _, _>)
as &'static LiquidityManager<_, _, _, _, _, _>
};
let sweeper_async: &'static OutputSweeper<_, _, _, _, _, _, _> = unsafe {
&*(nodes[0].sweeper.sweeper_async() as *const OutputSweeper<_, _, _, _, _, _, _>)
Expand Down Expand Up @@ -3589,10 +3582,10 @@ mod tests {
let (exit_sender, exit_receiver) = tokio::sync::watch::channel(());

// Yes, you can unsafe { turn off the borrow checker }
let lm_async: &'static LiquidityManager<_, _, _, _, _, _, _> = unsafe {
let lm_async: &'static LiquidityManager<_, _, _, _, _, _> = unsafe {
&*(nodes[0].liquidity_manager.get_lm_async()
as *const LiquidityManager<_, _, _, _, _, _, _>)
as &'static LiquidityManager<_, _, _, _, _, _, _>
as *const LiquidityManager<_, _, _, _, _, _>)
as &'static LiquidityManager<_, _, _, _, _, _>
};
let sweeper_async: &'static OutputSweeper<_, _, _, _, _, _, _> = unsafe {
&*(nodes[0].sweeper.sweeper_async() as *const OutputSweeper<_, _, _, _, _, _, _>)
Expand Down
3 changes: 1 addition & 2 deletions lightning-liquidity/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "lightning-liquidity"
version = "0.2.0+git"
version = "0.3.0+git"
authors = ["John Cantrell <johncantrell97@gmail.com>", "Elias Rohrer <dev@tnull.de>"]
homepage = "https://lightningdevkit.org/"
license = "MIT OR Apache-2.0"
Expand Down Expand Up @@ -47,7 +47,6 @@ parking_lot = { version = "0.12", default-features = false }
level = "forbid"
# When adding a new cfg attribute, ensure that it is added to this list.
check-cfg = [
"cfg(lsps1_service)",
"cfg(c_bindings)",
"cfg(backtrace)",
"cfg(ldk_bench)",
Expand Down
2 changes: 0 additions & 2 deletions lightning-liquidity/src/events/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,6 @@ pub enum LiquidityEvent {
/// An LSPS1 (Channel Request) client event.
LSPS1Client(lsps1::event::LSPS1ClientEvent),
/// An LSPS1 (Channel Request) server event.
#[cfg(lsps1_service)]
LSPS1Service(lsps1::event::LSPS1ServiceEvent),
/// An LSPS2 (JIT Channel) client event.
LSPS2Client(lsps2::event::LSPS2ClientEvent),
Expand All @@ -57,7 +56,6 @@ impl From<lsps1::event::LSPS1ClientEvent> for LiquidityEvent {
}
}

#[cfg(lsps1_service)]
impl From<lsps1::event::LSPS1ServiceEvent> for LiquidityEvent {
fn from(event: lsps1::event::LSPS1ServiceEvent) -> Self {
Self::LSPS1Service(event)
Expand Down
21 changes: 0 additions & 21 deletions lightning-liquidity/src/lsps1/event.rs
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,6 @@ pub enum LSPS1ClientEvent {
}

/// An event which an LSPS1 server should take some action in response to.
#[cfg(lsps1_service)]
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum LSPS1ServiceEvent {
/// A client has selected the parameters to use from the supported options of the LSP
Expand All @@ -165,26 +164,6 @@ pub enum LSPS1ServiceEvent {
/// The order requested by the client.
order: LSPS1OrderParams,
},
/// A request from client to check the status of the payment.
///
/// An event to poll for checking payment status either onchain or lightning.
///
/// You must call [`LSPS1ServiceHandler::update_order_status`] to update the client
/// regarding the status of the payment and order.
///
/// **Note: ** This event will *not* be persisted across restarts.
///
/// [`LSPS1ServiceHandler::update_order_status`]: crate::lsps1::service::LSPS1ServiceHandler::update_order_status
CheckPaymentConfirmation {
/// An identifier that must be passed to [`LSPS1ServiceHandler::update_order_status`].
///
/// [`LSPS1ServiceHandler::update_order_status`]: crate::lsps1::service::LSPS1ServiceHandler::update_order_status
request_id: LSPSRequestId,
/// The node id of the client making the information request.
counterparty_node_id: PublicKey,
/// The order id of order with pending payment.
order_id: LSPS1OrderId,
},
/// If error is encountered, refund the amount if paid by the client.
///
/// **Note: ** This event will *not* be persisted across restarts.
Expand Down
2 changes: 1 addition & 1 deletion lightning-liquidity/src/lsps1/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,5 @@
pub mod client;
pub mod event;
pub mod msgs;
#[cfg(lsps1_service)]
pub(crate) mod peer_state;
pub mod service;
84 changes: 82 additions & 2 deletions lightning-liquidity/src/lsps1/msgs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,9 @@ use crate::lsps0::ser::{
};

use bitcoin::{Address, FeeRate, OutPoint};

use lightning::offers::offer::Offer;
use lightning::util::ser::{Readable, Writeable};
use lightning::{impl_writeable_tlv_based, impl_writeable_tlv_based_enum};
use lightning_invoice::Bolt11Invoice;

use serde::{Deserialize, Serialize};
Expand All @@ -30,13 +31,31 @@ pub(crate) const LSPS1_CREATE_ORDER_METHOD_NAME: &str = "lsps1.create_order";
pub(crate) const LSPS1_GET_ORDER_METHOD_NAME: &str = "lsps1.get_order";

pub(crate) const _LSPS1_CREATE_ORDER_REQUEST_INVALID_PARAMS_ERROR_CODE: i32 = -32602;
#[cfg(lsps1_service)]
pub(crate) const LSPS1_CREATE_ORDER_REQUEST_ORDER_MISMATCH_ERROR_CODE: i32 = 100;
pub(crate) const LSPS1_GET_ORDER_REQUEST_ORDER_NOT_FOUND_ERROR_CODE: i32 = 101;
pub(crate) const LSPS1_CREATE_ORDER_REQUEST_UNRECOGNIZED_OR_STALE_TOKEN_ERROR_CODE: i32 = 102;

/// The identifier of an order.
#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize, Hash)]
pub struct LSPS1OrderId(pub String);

impl Writeable for LSPS1OrderId {
fn write<W: lightning::util::ser::Writer>(
&self, writer: &mut W,
) -> Result<(), lightning::io::Error> {
self.0.write(writer)
}
}

impl Readable for LSPS1OrderId {
fn read<R: bitcoin::io::Read>(
reader: &mut R,
) -> Result<Self, lightning::ln::msgs::DecodeError> {
let inner = Readable::read(reader)?;
Ok(Self(inner))
}
}

/// A request made to an LSP to retrieve the supported options.
///
/// Please refer to the [bLIP-51 / LSPS1
Expand Down Expand Up @@ -126,6 +145,16 @@ pub struct LSPS1OrderParams {
pub announce_channel: bool,
}

impl_writeable_tlv_based!(LSPS1OrderParams, {
(0, lsp_balance_sat, required),
(2, client_balance_sat, required),
(4, required_channel_confirmations, required),
(6, funding_confirms_within_blocks, required),
(8, channel_expiry_blocks, required),
(10, token, option),
(12, announce_channel, required),
});

/// A response to a [`LSPS1CreateOrderRequest`].
#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
pub struct LSPS1CreateOrderResponse {
Expand Down Expand Up @@ -156,6 +185,12 @@ pub enum LSPS1OrderState {
Failed,
}

impl_writeable_tlv_based_enum!(LSPS1OrderState,
(0, Created) => {},
(2, Completed) => {},
(4, Failed) => {}
);

/// Details regarding how to pay for an order.
#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
pub struct LSPS1PaymentInfo {
Expand All @@ -167,6 +202,12 @@ pub struct LSPS1PaymentInfo {
pub onchain: Option<LSPS1OnchainPaymentInfo>,
}

impl_writeable_tlv_based!(LSPS1PaymentInfo, {
(0, bolt11, option),
(2, bolt12, option),
(4, onchain, option),
});

/// A Lightning payment using BOLT 11.
#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
pub struct LSPS1Bolt11PaymentInfo {
Expand All @@ -184,6 +225,14 @@ pub struct LSPS1Bolt11PaymentInfo {
pub invoice: Bolt11Invoice,
}

impl_writeable_tlv_based!(LSPS1Bolt11PaymentInfo, {
(0, state, required),
(2, expires_at, required),
(4, fee_total_sat, required),
(6, order_total_sat, required),
(8, invoice, required),
});

/// A Lightning payment using BOLT 12.
#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
pub struct LSPS1Bolt12PaymentInfo {
Expand All @@ -202,6 +251,14 @@ pub struct LSPS1Bolt12PaymentInfo {
pub offer: Offer,
}

impl_writeable_tlv_based!(LSPS1Bolt12PaymentInfo, {
(0, state, required),
(2, expires_at, required),
(4, fee_total_sat, required),
(6, order_total_sat, required),
(8, offer, required),
});

/// An onchain payment.
#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
pub struct LSPS1OnchainPaymentInfo {
Expand Down Expand Up @@ -233,6 +290,17 @@ pub struct LSPS1OnchainPaymentInfo {
pub refund_onchain_address: Option<Address>,
}

impl_writeable_tlv_based!(LSPS1OnchainPaymentInfo, {
(0, state, required),
(2, expires_at, required),
(4, fee_total_sat, required),
(6, order_total_sat, required),
(8, address, required),
(10, min_onchain_payment_confirmations, option),
(12, min_fee_for_0conf, required),
(14, refund_onchain_address, option),
});

/// The state of a payment.
///
/// *Note*: Previously, the spec also knew a `CANCELLED` state for BOLT11 payments, which has since
Expand All @@ -249,6 +317,12 @@ pub enum LSPS1PaymentState {
Refunded,
}

impl_writeable_tlv_based_enum!(LSPS1PaymentState,
(0, ExpectPayment) => {},
(2, Paid) => {},
(4, Refunded) => {}
);

/// Details regarding a detected on-chain payment.
#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
pub struct LSPS1OnchainPayment {
Expand All @@ -272,6 +346,12 @@ pub struct LSPS1ChannelInfo {
pub expires_at: LSPSDateTime,
}

impl_writeable_tlv_based!(LSPS1ChannelInfo, {
(0, funded_at, required),
(2, funding_outpoint, required),
(4, expires_at, required),
});

/// A request made to an LSP to retrieve information about an previously made order.
///
/// Please refer to the [bLIP-51 / LSPS1
Expand Down
Loading
Loading