diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 148e5c4..33f5375 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -37,8 +37,6 @@ jobs: run: | cargo doc --release cargo doc --no-default-features --features no-std - RUSTFLAGS="--cfg lsps1" RUSTDOCFLAGS="--cfg lsps1" cargo doc --release - RUSTFLAGS="--cfg lsps1" RUSTDOCFLAGS="--cfg lsps1" cargo doc --no-default-features --features no-std - name: Build on Rust ${{ matrix.toolchain }} run: cargo build --verbose --color always - name: Check formatting @@ -50,8 +48,6 @@ jobs: - name: Test on Rust ${{ matrix.toolchain }} run: | cargo test - RUSTFLAGS="--cfg lsps1" cargo test - name: Test on Rust ${{ matrix.toolchain }} with no-std support run: | cargo test --no-default-features --features no-std - RUSTFLAGS="--cfg lsps1" cargo test --no-default-features --features no-std diff --git a/Cargo.toml b/Cargo.toml index 9b273f7..e38b42c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,9 +19,13 @@ std = ["lightning/std", "bitcoin/std", "lightning-invoice/std"] no-std = ["hashbrown", "lightning/no-std"] [dependencies] -lightning = { version = "0.0.125", default-features = false } +lightning = { version = "0.0.125", default-features = false, features = ["max_level_trace"] } lightning-types = { version = "0.1", default-features = false } lightning-invoice = { version = "0.32.0", default-features = false, features = ["serde"] } +#lightning = { path = "../rust-lightning/lightning", default-features = false, features = ["max_level_trace"] } +#lightning-types = { path = "../rust-lightning/lightning-types", default-features = false } +#lightning-invoice = { path = "../rust-lightning/lightning-invoice", default-features = false, features = ["serde"] } + bitcoin = { version = "0.32.2", default-features = false, features = ["serde"] } hashbrown = { version = "0.8", optional = true } @@ -33,6 +37,10 @@ serde_json = "1.0" lightning = { version = "0.0.125", default-features = false, features = ["_test_utils"] } lightning-persister = { version = "0.0.125", default-features = false } lightning-background-processor = { version = "0.0.125", default-features = false, features = ["std"] } +# lightning = { path = "../rust-lightning/lightning", default-features = false, features = ["max_level_trace", "_test_utils"] } +# lightning-persister = { path = "../rust-lightning/lightning-persister", default-features = false } +# lightning-background-processor = { path = "../rust-lightning/lightning-background-processor", default-features = false, features = ["std"] } + proptest = "1.0.0" tokio = { version = "1.35", default-features = false, features = [ "rt-multi-thread", "time", "sync", "macros" ] } @@ -40,5 +48,5 @@ tokio = { version = "1.35", default-features = false, features = [ "rt-multi-thr level = "forbid" # When adding a new cfg attribute, ensure that it is added to this list. check-cfg = [ - "cfg(lsps1)", + "cfg(lsps1_service)", ] diff --git a/src/events.rs b/src/events.rs index efa6d55..e71afdc 100644 --- a/src/events.rs +++ b/src/events.rs @@ -16,7 +16,6 @@ //! [`LiquidityManager::get_and_clear_pending_events`]: crate::LiquidityManager::get_and_clear_pending_events use crate::lsps0; -#[cfg(lsps1)] use crate::lsps1; use crate::lsps2; use crate::prelude::{Vec, VecDeque}; @@ -98,10 +97,9 @@ pub enum Event { /// An LSPS0 client event. LSPS0Client(lsps0::event::LSPS0ClientEvent), /// An LSPS1 (Channel Request) client event. - #[cfg(lsps1)] LSPS1Client(lsps1::event::LSPS1ClientEvent), /// An LSPS1 (Channel Request) server event. - #[cfg(lsps1)] + #[cfg(lsps1_service)] LSPS1Service(lsps1::event::LSPS1ServiceEvent), /// An LSPS2 (JIT Channel) client event. LSPS2Client(lsps2::event::LSPS2ClientEvent), diff --git a/src/lib.rs b/src/lib.rs index 8bf3189..b284f3d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -41,9 +41,6 @@ mod prelude { pub mod events; pub mod lsps0; -#[cfg(lsps1)] -// TODO: disallow warnings once the implementation is finished -#[allow(warnings)] pub mod lsps1; pub mod lsps2; mod manager; diff --git a/src/lsps0/msgs.rs b/src/lsps0/msgs.rs index e1ea2a1..2366aa6 100644 --- a/src/lsps0/msgs.rs +++ b/src/lsps0/msgs.rs @@ -76,7 +76,6 @@ impl TryFrom for LSPS0Message { match message { LSPSMessage::Invalid(_) => Err(()), LSPSMessage::LSPS0(message) => Ok(message), - #[cfg(lsps1)] LSPSMessage::LSPS1(_) => Err(()), LSPSMessage::LSPS2(_) => Err(()), } diff --git a/src/lsps0/ser.rs b/src/lsps0/ser.rs index 03eee1c..c3a8908 100644 --- a/src/lsps0/ser.rs +++ b/src/lsps0/ser.rs @@ -7,7 +7,6 @@ use crate::lsps0::msgs::{ LSPS0_LISTPROTOCOLS_METHOD_NAME, }; -#[cfg(lsps1)] use crate::lsps1::msgs::{ LSPS1Message, LSPS1Request, LSPS1Response, LSPS1_CREATE_ORDER_METHOD_NAME, LSPS1_GET_INFO_METHOD_NAME, LSPS1_GET_ORDER_METHOD_NAME, @@ -47,11 +46,8 @@ pub(crate) const _LSPS0_CLIENT_REJECTED_ERROR_CODE: i32 = 001; #[derive(Copy, Clone, Debug, PartialEq, Eq)] pub(crate) enum LSPSMethod { LSPS0ListProtocols, - #[cfg(lsps1)] LSPS1GetInfo, - #[cfg(lsps1)] LSPS1GetOrder, - #[cfg(lsps1)] LSPS1CreateOrder, LSPS2GetInfo, LSPS2Buy, @@ -62,11 +58,8 @@ impl FromStr for LSPSMethod { fn from_str(s: &str) -> Result { match s { LSPS0_LISTPROTOCOLS_METHOD_NAME => Ok(Self::LSPS0ListProtocols), - #[cfg(lsps1)] LSPS1_GET_INFO_METHOD_NAME => Ok(Self::LSPS1GetInfo), - #[cfg(lsps1)] LSPS1_CREATE_ORDER_METHOD_NAME => Ok(Self::LSPS1CreateOrder), - #[cfg(lsps1)] LSPS1_GET_ORDER_METHOD_NAME => Ok(Self::LSPS1GetOrder), LSPS2_GET_INFO_METHOD_NAME => Ok(Self::LSPS2GetInfo), LSPS2_BUY_METHOD_NAME => Ok(Self::LSPS2Buy), @@ -79,11 +72,8 @@ impl Display for LSPSMethod { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let s = match self { Self::LSPS0ListProtocols => LSPS0_LISTPROTOCOLS_METHOD_NAME, - #[cfg(lsps1)] Self::LSPS1GetInfo => LSPS1_GET_INFO_METHOD_NAME, - #[cfg(lsps1)] Self::LSPS1CreateOrder => LSPS1_CREATE_ORDER_METHOD_NAME, - #[cfg(lsps1)] Self::LSPS1GetOrder => LSPS1_GET_ORDER_METHOD_NAME, Self::LSPS2GetInfo => LSPS2_GET_INFO_METHOD_NAME, Self::LSPS2Buy => LSPS2_BUY_METHOD_NAME, @@ -100,7 +90,6 @@ impl From<&LSPS0Request> for LSPSMethod { } } -#[cfg(lsps1)] impl From<&LSPS1Request> for LSPSMethod { fn from(value: &LSPS1Request) -> Self { match value { @@ -216,7 +205,6 @@ pub enum LSPSMessage { /// An LSPS0 message. LSPS0(LSPS0Message), /// An LSPS1 message. - #[cfg(lsps1)] LSPS1(LSPS1Message), /// An LSPS2 message. LSPS2(LSPS2Message), @@ -241,7 +229,6 @@ impl LSPSMessage { LSPSMessage::LSPS0(LSPS0Message::Request(request_id, request)) => { Some((RequestId(request_id.0.clone()), request.into())) }, - #[cfg(lsps1)] LSPSMessage::LSPS1(LSPS1Message::Request(request_id, request)) => { Some((RequestId(request_id.0.clone()), request.into())) }, @@ -287,7 +274,6 @@ impl Serialize for LSPSMessage { }, } }, - #[cfg(lsps1)] LSPSMessage::LSPS1(LSPS1Message::Request(request_id, request)) => { jsonrpc_object.serialize_field(JSONRPC_ID_FIELD_KEY, &request_id.0)?; jsonrpc_object @@ -305,7 +291,6 @@ impl Serialize for LSPSMessage { }, } }, - #[cfg(lsps1)] LSPSMessage::LSPS1(LSPS1Message::Response(request_id, response)) => { jsonrpc_object.serialize_field(JSONRPC_ID_FIELD_KEY, &request_id.0)?; @@ -442,7 +427,6 @@ impl<'de, 'a> Visitor<'de> for LSPSMessageVisitor<'a> { id, LSPS0Request::ListProtocols(ListProtocolsRequest {}), ))), - #[cfg(lsps1)] LSPSMethod::LSPS1GetInfo => { let request = serde_json::from_value(params.unwrap_or(json!({}))) .map_err(de::Error::custom)?; @@ -451,7 +435,6 @@ impl<'de, 'a> Visitor<'de> for LSPSMessageVisitor<'a> { LSPS1Request::GetInfo(request), ))) }, - #[cfg(lsps1)] LSPSMethod::LSPS1CreateOrder => { let request = serde_json::from_value(params.unwrap_or(json!({}))) .map_err(de::Error::custom)?; @@ -460,7 +443,6 @@ impl<'de, 'a> Visitor<'de> for LSPSMessageVisitor<'a> { LSPS1Request::CreateOrder(request), ))) }, - #[cfg(lsps1)] LSPSMethod::LSPS1GetOrder => { let request = serde_json::from_value(params.unwrap_or(json!({}))) .map_err(de::Error::custom)?; @@ -502,7 +484,6 @@ impl<'de, 'a> Visitor<'de> for LSPSMessageVisitor<'a> { Err(de::Error::custom("Received invalid JSON-RPC object: one of method, result, or error required")) } }, - #[cfg(lsps1)] LSPSMethod::LSPS1GetInfo => { if let Some(error) = error { Ok(LSPSMessage::LSPS1(LSPS1Message::Response( @@ -520,7 +501,6 @@ impl<'de, 'a> Visitor<'de> for LSPSMessageVisitor<'a> { Err(de::Error::custom("Received invalid JSON-RPC object: one of method, result, or error required")) } }, - #[cfg(lsps1)] LSPSMethod::LSPS1CreateOrder => { if let Some(error) = error { Ok(LSPSMessage::LSPS1(LSPS1Message::Response( @@ -538,7 +518,6 @@ impl<'de, 'a> Visitor<'de> for LSPSMessageVisitor<'a> { Err(de::Error::custom("Received invalid JSON-RPC object: one of method, result, or error required")) } }, - #[cfg(lsps1)] LSPSMethod::LSPS1GetOrder => { if let Some(error) = error { Ok(LSPSMessage::LSPS1(LSPS1Message::Response( @@ -654,7 +633,63 @@ pub(crate) mod string_amount_option { } } -#[cfg(lsps1)] +pub(crate) mod unchecked_address { + use crate::prelude::{String, ToString}; + use bitcoin::Address; + use core::str::FromStr; + use serde::de::Unexpected; + use serde::{Deserialize, Deserializer, Serializer}; + + pub(crate) fn serialize(x: &Address, s: S) -> Result + where + S: Serializer, + { + s.serialize_str(&x.to_string()) + } + + pub(crate) fn deserialize<'de, D>(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + let buf = String::deserialize(deserializer)?; + + let parsed_addr = Address::from_str(&buf).map_err(|_| { + serde::de::Error::invalid_value(Unexpected::Str(&buf), &"invalid address string") + })?; + Ok(parsed_addr.assume_checked()) + } +} + +pub(crate) mod unchecked_address_option { + use crate::prelude::{String, ToString}; + use bitcoin::Address; + use core::str::FromStr; + use serde::de::Unexpected; + use serde::{Deserialize, Deserializer, Serialize, Serializer}; + + pub(crate) fn serialize(x: &Option
, s: S) -> Result + where + S: Serializer, + { + let v = x.as_ref().map(|v| v.to_string()); + Option::::serialize(&v, s) + } + + pub(crate) fn deserialize<'de, D>(deserializer: D) -> Result, D::Error> + where + D: Deserializer<'de>, + { + if let Some(buf) = Option::::deserialize(deserializer)? { + let val = Address::from_str(&buf).map_err(|_| { + serde::de::Error::invalid_value(Unexpected::Str(&buf), &"invalid address string") + })?; + Ok(Some(val.assume_checked())) + } else { + Ok(None) + } + } +} + pub(crate) mod u32_fee_rate { use bitcoin::FeeRate; use serde::{Deserialize, Deserializer, Serializer}; diff --git a/src/lsps1/client.rs b/src/lsps1/client.rs index ec9f9c4..9ccfa52 100644 --- a/src/lsps1/client.rs +++ b/src/lsps1/client.rs @@ -12,24 +12,22 @@ use super::event::LSPS1ClientEvent; use super::msgs::{ CreateOrderRequest, CreateOrderResponse, GetInfoRequest, GetInfoResponse, GetOrderRequest, - LSPS1Message, LSPS1Request, LSPS1Response, OptionsSupported, OrderId, OrderParams, + LSPS1Message, LSPS1Request, LSPS1Response, OrderId, OrderParameters, }; -use super::utils::is_valid; use crate::message_queue::MessageQueue; use crate::events::{Event, EventQueue}; use crate::lsps0::ser::{ProtocolMessageHandler, RequestId, ResponseError}; -use crate::prelude::{HashMap, String, ToString, Vec}; +use crate::prelude::{HashMap, HashSet}; use crate::sync::{Arc, Mutex, RwLock}; -use lightning::chain::Filter; -use lightning::ln::channelmanager::AChannelManager; use lightning::ln::msgs::{ErrorAction, LightningError}; use lightning::sign::EntropySource; use lightning::util::errors::APIError; use lightning::util::logger::Level; use bitcoin::secp256k1::PublicKey; +use bitcoin::Address; use core::ops::Deref; @@ -40,209 +38,53 @@ pub struct LSPS1ClientConfig { pub max_channel_fees_msat: Option, } -struct ChannelStateError(String); - -impl From for LightningError { - fn from(value: ChannelStateError) -> Self { - LightningError { err: value.0, action: ErrorAction::IgnoreAndLog(Level::Info) } - } -} - -#[derive(PartialEq, Debug)] -enum InboundRequestState { - InfoRequested, - OptionsSupport { options_supported: OptionsSupported }, - OrderRequested { order: OrderParams }, - PendingPayment { order_id: OrderId }, - AwaitingConfirmation { user_channel_id: u128, order_id: OrderId }, -} - -impl InboundRequestState { - fn info_received(&self, options: OptionsSupported) -> Result { - match self { - InboundRequestState::InfoRequested => { - Ok(InboundRequestState::OptionsSupport { options_supported: options }) - }, - state => Err(ChannelStateError(format!( - "Received unexpected get_info response. Channel was in state: {:?}", - state - ))), - } - } - - fn order_requested(&self, order: OrderParams) -> Result { - match self { - InboundRequestState::OptionsSupport { options_supported } => { - if is_valid(&order, options_supported) { - Ok(InboundRequestState::OrderRequested { order }) - } else { - return Err(ChannelStateError(format!( - "The order created does not match options supported by LSP. Options Supported by LSP are {:?}. The order created was {:?}", - options_supported, order - ))); - } - }, - state => Err(ChannelStateError(format!( - "Received create order request for wrong channel. Channel was in state: {:?}", - state - ))), - } - } - - fn order_received( - &self, response_order: &OrderParams, order_id: OrderId, - ) -> Result { - match self { - InboundRequestState::OrderRequested { order } => { - if response_order == order { - Ok(InboundRequestState::PendingPayment { order_id }) - } else { - Err(ChannelStateError(format!( - "Received order is different from created order. The order created was : {:?}. Order Received from LSP is : {:?}", - order, response_order - ))) - } - }, - state => Err(ChannelStateError(format!( - "Received unexpected create order response. Channel was in state: {:?}", - state - ))), - } - } - - fn pay_for_channel(&self, user_channel_id: u128) -> Result { - match self { - InboundRequestState::PendingPayment { order_id } => { - Ok(InboundRequestState::AwaitingConfirmation { - user_channel_id, - order_id: order_id.clone(), - }) - }, - state => Err(ChannelStateError(format!( - "Received unexpected response. Channel was in state: {:?}", - state - ))), - } - } -} - -struct InboundCRChannel { - user_channel_id: u128, - state: InboundRequestState, -} - -impl InboundCRChannel { - fn new(user_channel_id: u128) -> Self { - Self { user_channel_id, state: InboundRequestState::InfoRequested } - } - - fn info_received(&mut self, options: OptionsSupported) -> Result<(), LightningError> { - self.state = self.state.info_received(options)?; - - match self.state { - InboundRequestState::OptionsSupport { .. } => Ok(()), - _ => Err(LightningError { - action: ErrorAction::IgnoreAndLog(Level::Error), - err: "impossible state transition".to_string(), - }), - } - } - - fn order_requested(&mut self, order: OrderParams) -> Result<(), LightningError> { - self.state = self.state.order_requested(order)?; - - match self.state { - InboundRequestState::OrderRequested { .. } => Ok(()), - _ => { - return Err(LightningError { - action: ErrorAction::IgnoreAndLog(Level::Error), - err: "impossible state transition".to_string(), - }); - }, - } - } - - fn order_received( - &mut self, order: &OrderParams, order_id: OrderId, - ) -> Result<(), LightningError> { - self.state = self.state.order_received(order, order_id)?; - Ok(()) - } - - fn pay_for_channel(&mut self, user_channel_id: u128) -> Result<(), LightningError> { - self.state = self.state.pay_for_channel(user_channel_id)?; - Ok(()) - } -} - #[derive(Default)] struct PeerState { - inbound_channels_by_id: HashMap, - request_to_cid: HashMap, - pending_requests: HashMap, -} - -impl PeerState { - fn insert_inbound_channel(&mut self, user_channel_id: u128, channel: InboundCRChannel) { - self.inbound_channels_by_id.insert(user_channel_id, channel); - } - - fn insert_request(&mut self, request_id: RequestId, user_channel_id: u128) { - self.request_to_cid.insert(request_id, user_channel_id); - } - - fn remove_inbound_channel(&mut self, user_channel_id: u128) { - self.inbound_channels_by_id.remove(&user_channel_id); - } + pending_get_info_requests: HashSet, + pending_create_order_requests: HashSet, + pending_get_order_requests: HashSet, } /// The main object allowing to send and receive LSPS1 messages. -pub struct LSPS1ClientHandler +pub struct LSPS1ClientHandler where ES::Target: EntropySource, - CM::Target: AChannelManager, - C::Target: Filter, { entropy_source: ES, - channel_manager: CM, - chain_source: Option, pending_messages: Arc, pending_events: Arc, per_peer_state: RwLock>>, - config: LSPS1ClientConfig, + _config: LSPS1ClientConfig, } -impl LSPS1ClientHandler +impl LSPS1ClientHandler where ES::Target: EntropySource, - CM::Target: AChannelManager, - C::Target: Filter, - ES::Target: EntropySource, { /// Constructs an `LSPS1ClientHandler`. pub(crate) fn new( entropy_source: ES, pending_messages: Arc, pending_events: Arc, - channel_manager: CM, chain_source: Option, config: LSPS1ClientConfig, + config: LSPS1ClientConfig, ) -> Self { Self { entropy_source, - channel_manager, - chain_source, pending_messages, pending_events, per_peer_state: RwLock::new(HashMap::new()), - config, + _config: config, } } - /// Retrieve information from the LSP regarding the options supported. + /// Request the supported options from the LSP. /// - /// `counterparty_node_id` is the node_id of the LSP you would like to use. + /// The user will receive the LSP's response via an [`SupportedOptionsReady`] event. /// - /// `user_channel_id` is the id used to uniquely identify the channel with counterparty node. - pub fn send_get_info_request(&self, counterparty_node_id: PublicKey, user_channel_id: u128) { - let channel = InboundCRChannel::new(user_channel_id); - + /// `counterparty_node_id` is the `node_id` of the LSP you would like to use. + /// + /// Returns the used [`RequestId`], which will be returned via [`SupportedOptionsReady`]. + /// + /// [`SupportedOptionsReady`]: crate::lsps1::event::LSPS1ClientEvent::SupportedOptionsReady + pub fn request_supported_options(&self, counterparty_node_id: PublicKey) -> RequestId { let request_id = crate::utils::generate_request_id(&self.entropy_source); { let mut outer_state_lock = self.per_peer_state.write().unwrap(); @@ -250,14 +92,13 @@ where .entry(counterparty_node_id) .or_insert(Mutex::new(PeerState::default())); let mut peer_state_lock = inner_state_lock.lock().unwrap(); - peer_state_lock.insert_inbound_channel(user_channel_id, channel); - - peer_state_lock.insert_request(request_id.clone(), user_channel_id); + peer_state_lock.pending_get_info_requests.insert(request_id.clone()); } let request = LSPS1Request::GetInfo(GetInfoRequest {}); - let msg = LSPS1Message::Request(request_id, request).into(); + let msg = LSPS1Message::Request(request_id.clone(), request).into(); self.pending_messages.enqueue(&counterparty_node_id, msg); + request_id } fn handle_get_info_response( @@ -269,51 +110,33 @@ where Some(inner_state_lock) => { let mut peer_state_lock = inner_state_lock.lock().unwrap(); - let user_channel_id = - peer_state_lock.request_to_cid.remove(&request_id).ok_or(LightningError { + if !peer_state_lock.pending_get_info_requests.remove(&request_id) { + return Err(LightningError { err: format!( "Received get_info response for an unknown request: {:?}", request_id ), - action: ErrorAction::IgnoreAndLog(Level::Info), - })?; + action: ErrorAction::IgnoreAndLog(Level::Debug), + }); + } - let inbound_channel = peer_state_lock - .inbound_channels_by_id - .get_mut(&user_channel_id) - .ok_or(LightningError { - err: format!( - "Received get_info response for an unknown channel: {:?}", - user_channel_id - ), - action: ErrorAction::IgnoreAndLog(Level::Info), - })?; - - match inbound_channel.info_received(result.options.clone()) { - Ok(()) => (), - Err(e) => { - peer_state_lock.remove_inbound_channel(user_channel_id); - return Err(e); + self.pending_events.enqueue(Event::LSPS1Client( + LSPS1ClientEvent::SupportedOptionsReady { + counterparty_node_id: *counterparty_node_id, + supported_options: result.options, + request_id, }, - }; - - self.pending_events.enqueue(Event::LSPS1Client(LSPS1ClientEvent::GetInfoResponse { - user_channel_id, - counterparty_node_id: *counterparty_node_id, - options_supported: result.options, - })) - }, - None => { - return Err(LightningError { - err: format!( - "Received get_info response from unknown peer: {:?}", - counterparty_node_id - ), - action: ErrorAction::IgnoreAndLog(Level::Info), - }) + )); + Ok(()) }, + None => Err(LightningError { + err: format!( + "Received get_info response from unknown peer: {:?}", + counterparty_node_id + ), + action: ErrorAction::IgnoreAndLog(Level::Debug), + }), } - Ok(()) } fn handle_get_info_error( @@ -324,91 +147,72 @@ where Some(inner_state_lock) => { let mut peer_state_lock = inner_state_lock.lock().unwrap(); - let user_channel_id = - peer_state_lock.request_to_cid.remove(&request_id).ok_or(LightningError { + if !peer_state_lock.pending_get_info_requests.remove(&request_id) { + return Err(LightningError { err: format!( - "Received GetInfo error for an unknown request: {:?}", + "Received get_info error for an unknown request: {:?}", request_id ), action: ErrorAction::IgnoreAndLog(Level::Debug), - })?; + }); + } - let inbound_channel = peer_state_lock - .inbound_channels_by_id - .get_mut(&user_channel_id) - .ok_or(LightningError { - err: format!( - "Received GetInfo error for an unknown channel: {:?}", - user_channel_id - ), - action: ErrorAction::IgnoreAndLog(Level::Info), - })?; - Ok(()) + self.pending_events.enqueue(Event::LSPS1Client( + LSPS1ClientEvent::SupportedOptionsRequestFailed { + request_id: request_id.clone(), + counterparty_node_id: *counterparty_node_id, + error: error.clone(), + }, + )); + + Err(LightningError { + err: format!( + "Received get_info error response for request {:?}: {:?}", + request_id, error + ), + action: ErrorAction::IgnoreAndLog(Level::Error), + }) }, None => { - return Err(LightningError { err: format!("Received error response for a create order request from an unknown counterparty ({:?})",counterparty_node_id), action: ErrorAction::IgnoreAndLog(Level::Info)}); + return Err(LightningError { + err: format!( + "Received get_info error response from an unknown counterparty ({:?})", + counterparty_node_id + ), + action: ErrorAction::IgnoreAndLog(Level::Debug), + }); }, } } /// Places an order with the connected LSP given its `counterparty_node_id`. - /// The client agrees to paying channel fees according to the provided parameters. - /// - /// Should be called in response to receiving a [`LSPS1ClientEvent::GetInfoResponse`] event. /// - /// [`LSPS1ClientEvent::GetInfoResponse`]: crate::lsps1::event::LSPS1ClientEvent::GetInfoResponse - pub fn place_order( - &self, user_channel_id: u128, counterparty_node_id: &PublicKey, order: OrderParams, - ) -> Result<(), APIError> { - let (result, request_msg) = { - let outer_state_lock = self.per_peer_state.write().unwrap(); - - match outer_state_lock.get(counterparty_node_id) { - Some(inner_state_lock) => { - let mut peer_state_lock = inner_state_lock.lock().unwrap(); - - let inbound_channel = peer_state_lock - .inbound_channels_by_id - .get_mut(&user_channel_id) - .ok_or(APIError::APIMisuseError { - err: format!( - "Channel with user_channel_id {} not found", - user_channel_id - ), - })?; - - match inbound_channel.order_requested(order.clone()) { - Ok(()) => (), - Err(e) => { - peer_state_lock.remove_inbound_channel(user_channel_id); - return Err(APIError::APIMisuseError { err: e.err }); - }, - }; - - let request_id = crate::utils::generate_request_id(&self.entropy_source); - let request = LSPS1Request::CreateOrder(CreateOrderRequest { order }); - let msg = LSPS1Message::Request(request_id.clone(), request).into(); - peer_state_lock.insert_request(request_id, user_channel_id); - - (Ok(()), Some(msg)) - }, - None => ( - Err(APIError::APIMisuseError { - err: format!( - "No existing state with counterparty {}", - counterparty_node_id - ), - }), - None, - ), - } + /// The client agrees to paying channel fees according to the provided parameters. + pub fn create_order( + &self, counterparty_node_id: &PublicKey, order: OrderParameters, + refund_onchain_address: Option
, + ) -> RequestId { + let (request_id, request_msg) = { + let mut outer_state_lock = self.per_peer_state.write().unwrap(); + let inner_state_lock = outer_state_lock + .entry(*counterparty_node_id) + .or_insert(Mutex::new(PeerState::default())); + let mut peer_state_lock = inner_state_lock.lock().unwrap(); + + let request_id = crate::utils::generate_request_id(&self.entropy_source); + let request = + LSPS1Request::CreateOrder(CreateOrderRequest { order, refund_onchain_address }); + let msg = LSPS1Message::Request(request_id.clone(), request).into(); + peer_state_lock.pending_create_order_requests.insert(request_id.clone()); + + (request_id, Some(msg)) }; if let Some(msg) = request_msg { self.pending_messages.enqueue(&counterparty_node_id, msg); } - result + request_id } fn handle_create_order_response( @@ -420,55 +224,24 @@ where Some(inner_state_lock) => { let mut peer_state_lock = inner_state_lock.lock().unwrap(); - let user_channel_id = - peer_state_lock.request_to_cid.remove(&request_id).ok_or(LightningError { + if !peer_state_lock.pending_create_order_requests.remove(&request_id) { + return Err(LightningError { err: format!( "Received create_order response for an unknown request: {:?}", request_id ), - action: ErrorAction::IgnoreAndLog(Level::Info), - })?; - - let inbound_channel = peer_state_lock - .inbound_channels_by_id - .get_mut(&user_channel_id) - .ok_or(LightningError { - err: format!( - "Received create_order response for an unknown channel: {:?}", - user_channel_id - ), - action: ErrorAction::IgnoreAndLog(Level::Info), - })?; - - if let Err(e) = - inbound_channel.order_received(&response.order, response.order_id.clone()) - { - peer_state_lock.remove_inbound_channel(user_channel_id); - return Err(e); - } - - let total_fees = response.payment.fee_total_sat + response.order.client_balance_sat; - let max_channel_fees_msat = self.config.max_channel_fees_msat.unwrap_or(u64::MAX); - - if total_fees == response.payment.order_total_sat - && total_fees < max_channel_fees_msat - { - self.pending_events.enqueue(Event::LSPS1Client( - LSPS1ClientEvent::DisplayOrder { - user_channel_id, - counterparty_node_id: *counterparty_node_id, - order: response.order, - payment: response.payment, - channel: response.channel, - }, - )); - } else { - peer_state_lock.remove_inbound_channel(user_channel_id); - return Err(LightningError { - err: format!("Fees are too high : {:?}", total_fees), - action: ErrorAction::IgnoreAndLog(Level::Info), + action: ErrorAction::IgnoreAndLog(Level::Debug), }); } + + self.pending_events.enqueue(Event::LSPS1Client(LSPS1ClientEvent::OrderCreated { + request_id, + counterparty_node_id: *counterparty_node_id, + order_id: response.order_id, + order: response.order, + payment: response.payment, + channel: response.channel, + })); }, None => { return Err(LightningError { @@ -476,7 +249,7 @@ where "Received create_order response from unknown peer: {}", counterparty_node_id ), - action: ErrorAction::IgnoreAndLog(Level::Info), + action: ErrorAction::IgnoreAndLog(Level::Debug), }) }, } @@ -492,120 +265,102 @@ where Some(inner_state_lock) => { let mut peer_state_lock = inner_state_lock.lock().unwrap(); - let user_channel_id = - peer_state_lock.request_to_cid.remove(&request_id).ok_or(LightningError { + if !peer_state_lock.pending_create_order_requests.remove(&request_id) { + return Err(LightningError { err: format!( "Received create order error for an unknown request: {:?}", request_id ), - action: ErrorAction::IgnoreAndLog(Level::Info), - })?; + action: ErrorAction::IgnoreAndLog(Level::Debug), + }); + } - let inbound_channel = peer_state_lock - .inbound_channels_by_id - .get_mut(&user_channel_id) - .ok_or(LightningError { - err: format!( - "Received create order error for an unknown channel: {:?}", - user_channel_id - ), - action: ErrorAction::IgnoreAndLog(Level::Info), - })?; - Ok(()) + self.pending_events.enqueue(Event::LSPS1Client( + LSPS1ClientEvent::OrderRequestFailed { + request_id: request_id.clone(), + counterparty_node_id: *counterparty_node_id, + error: error.clone(), + }, + )); + + Err(LightningError { + err: format!( + "Received create_order error response for request {:?}: {:?}", + request_id, error + ), + action: ErrorAction::IgnoreAndLog(Level::Error), + }) }, None => { - return Err(LightningError { err: format!("Received error response for a create order request from an unknown counterparty ({:?})",counterparty_node_id), action: ErrorAction::IgnoreAndLog(Level::Info)}); + return Err(LightningError { + err: format!( + "Received error response for a create order request from an unknown counterparty ({:?})", + counterparty_node_id + ), + action: ErrorAction::IgnoreAndLog(Level::Debug), + }); }, } } /// Queries the status of a pending payment, i.e., whether a payment has been received by the LSP. /// - /// Should be called in response to receiving a [`LSPS1ClientEvent::DisplayOrder`] event. + /// Upon success an [`LSPS1ClientEvent::OrderStatus`] event will be emitted. /// - /// [`LSPS1ClientEvent::DisplayOrder`]: crate::lsps1::event::LSPS1ClientEvent::DisplayOrder + /// [`LSPS1ClientEvent::OrderStatus`]: crate::lsps1::event::LSPS1ClientEvent::OrderStatus pub fn check_order_status( - &self, counterparty_node_id: &PublicKey, order_id: OrderId, user_channel_id: u128, - ) -> Result<(), APIError> { - let (result, request_msg) = { - let outer_state_lock = self.per_peer_state.write().unwrap(); - match outer_state_lock.get(&counterparty_node_id) { - Some(inner_state_lock) => { - let mut peer_state_lock = inner_state_lock.lock().unwrap(); - - if let Some(inbound_channel) = - peer_state_lock.inbound_channels_by_id.get_mut(&user_channel_id) - { - if let Err(e) = inbound_channel.pay_for_channel(user_channel_id) { - peer_state_lock.remove_inbound_channel(user_channel_id); - return Err(APIError::APIMisuseError { err: e.err }); - } - - let request_id = crate::utils::generate_request_id(&self.entropy_source); - peer_state_lock.insert_request(request_id.clone(), user_channel_id); - - let request = - LSPS1Request::GetOrder(GetOrderRequest { order_id: order_id.clone() }); - let msg = LSPS1Message::Request(request_id, request).into(); - (Ok(()), Some(msg)) - } else { - ( - Err(APIError::APIMisuseError { - err: format!( - "Channel with user_channel_id {} not found", - user_channel_id - ), - }), - None, - ) - } - }, - None => ( - Err(APIError::APIMisuseError { - err: format!( - "No existing state with counterparty {}", - counterparty_node_id - ), - }), - None, - ), - } + &self, counterparty_node_id: &PublicKey, order_id: OrderId, + ) -> RequestId { + let (request_id, request_msg) = { + let mut outer_state_lock = self.per_peer_state.write().unwrap(); + let inner_state_lock = outer_state_lock + .entry(*counterparty_node_id) + .or_insert(Mutex::new(PeerState::default())); + let mut peer_state_lock = inner_state_lock.lock().unwrap(); + + let request_id = crate::utils::generate_request_id(&self.entropy_source); + peer_state_lock.pending_get_order_requests.insert(request_id.clone()); + + let request = LSPS1Request::GetOrder(GetOrderRequest { order_id: order_id.clone() }); + let msg = LSPS1Message::Request(request_id.clone(), request).into(); + + (request_id, Some(msg)) }; if let Some(msg) = request_msg { self.pending_messages.enqueue(&counterparty_node_id, msg); } - result + request_id } fn handle_get_order_response( - &self, request_id: RequestId, counterparty_node_id: &PublicKey, params: CreateOrderResponse, + &self, request_id: RequestId, counterparty_node_id: &PublicKey, + response: CreateOrderResponse, ) -> Result<(), LightningError> { let outer_state_lock = self.per_peer_state.read().unwrap(); match outer_state_lock.get(&counterparty_node_id) { Some(inner_state_lock) => { let mut peer_state_lock = inner_state_lock.lock().unwrap(); - let user_channel_id = - peer_state_lock.request_to_cid.remove(&request_id).ok_or(LightningError { + if !peer_state_lock.pending_get_order_requests.remove(&request_id) { + return Err(LightningError { err: format!( "Received get_order response for an unknown request: {:?}", request_id ), - action: ErrorAction::IgnoreAndLog(Level::Info), - })?; + action: ErrorAction::IgnoreAndLog(Level::Debug), + }); + } - let inbound_channel = peer_state_lock - .inbound_channels_by_id - .get_mut(&user_channel_id) - .ok_or(LightningError { - err: format!( - "Received get_order response for an unknown channel: {:?}", - user_channel_id - ), - action: ErrorAction::IgnoreAndLog(Level::Info), - })?; + self.pending_events.enqueue(Event::LSPS1Client(LSPS1ClientEvent::OrderStatus { + request_id, + counterparty_node_id: *counterparty_node_id, + order_id: response.order_id, + order: response.order, + payment: response.payment, + channel: response.channel, + })); }, None => { return Err(LightningError { @@ -613,7 +368,7 @@ where "Received get_order response from unknown peer: {}", counterparty_node_id ), - action: ErrorAction::IgnoreAndLog(Level::Info), + action: ErrorAction::IgnoreAndLog(Level::Debug), }) }, } @@ -622,47 +377,55 @@ where } fn handle_get_order_error( - &self, request_id: RequestId, counterparty_node_id: &PublicKey, params: ResponseError, + &self, request_id: RequestId, counterparty_node_id: &PublicKey, error: ResponseError, ) -> Result<(), LightningError> { let outer_state_lock = self.per_peer_state.read().unwrap(); match outer_state_lock.get(&counterparty_node_id) { Some(inner_state_lock) => { let mut peer_state_lock = inner_state_lock.lock().unwrap(); - let user_channel_id = - peer_state_lock.request_to_cid.remove(&request_id).ok_or(LightningError { + if !peer_state_lock.pending_get_order_requests.remove(&request_id) { + return Err(LightningError { err: format!( - "Received get_order error for an unknown request: {:?}", + "Received get order error for an unknown request: {:?}", request_id ), - action: ErrorAction::IgnoreAndLog(Level::Info), - })?; + action: ErrorAction::IgnoreAndLog(Level::Debug), + }); + } - let _inbound_channel = peer_state_lock - .inbound_channels_by_id - .get_mut(&user_channel_id) - .ok_or(LightningError { - err: format!( - "Received get_order error for an unknown channel: {:?}", - user_channel_id - ), - action: ErrorAction::IgnoreAndLog(Level::Info), - })?; - Ok(()) + self.pending_events.enqueue(Event::LSPS1Client( + LSPS1ClientEvent::OrderRequestFailed { + request_id: request_id.clone(), + counterparty_node_id: *counterparty_node_id, + error: error.clone(), + }, + )); + + Err(LightningError { + err: format!( + "Received get_order error response for request {:?}: {:?}", + request_id, error + ), + action: ErrorAction::IgnoreAndLog(Level::Error), + }) }, None => { - return Err(LightningError { err: format!("Received get_order response for a create order request from an unknown counterparty ({:?})",counterparty_node_id), action: ErrorAction::IgnoreAndLog(Level::Info)}); + return Err(LightningError { + err: format!( + "Received error response for a get order request from an unknown counterparty ({:?})", + counterparty_node_id + ), + action: ErrorAction::IgnoreAndLog(Level::Debug), + }); }, } } } -impl ProtocolMessageHandler - for LSPS1ClientHandler +impl ProtocolMessageHandler for LSPS1ClientHandler where ES::Target: EntropySource, - CM::Target: AChannelManager, - C::Target: Filter, { type ProtocolMessage = LSPS1Message; const PROTOCOL_NUMBER: Option = Some(1); @@ -696,7 +459,13 @@ where false, "Client handler received LSPS1 request message. This should never happen." ); - Err(LightningError { err: format!("Client handler received LSPS1 request message from node {:?}. This should never happen.", counterparty_node_id), action: ErrorAction::IgnoreAndLog(Level::Info)}) + Err(LightningError { + err: format!( + "Client handler received LSPS1 request message from node {:?}. This should never happen.", + counterparty_node_id + ), + action: ErrorAction::IgnoreAndLog(Level::Error), + }) }, } } diff --git a/src/lsps1/event.rs b/src/lsps1/event.rs index 126abdb..ff4961d 100644 --- a/src/lsps1/event.rs +++ b/src/lsps1/event.rs @@ -9,34 +9,53 @@ //! Contains LSPS1 event types -use super::msgs::{ChannelInfo, OptionsSupported, OrderId, OrderParams, PaymentInfo}; +use super::msgs::OrderId; +use super::msgs::{ChannelInfo, LSPS1Options, OrderParameters, PaymentInfo}; -use crate::lsps0::ser::RequestId; -use crate::prelude::String; +use crate::lsps0::ser::{RequestId, ResponseError}; use bitcoin::secp256k1::PublicKey; /// An event which an LSPS1 client should take some action in response to. #[derive(Clone, Debug, PartialEq, Eq)] pub enum LSPS1ClientEvent { - /// Information from the LSP about their supported protocol options. + /// A request previously issued via [`LSPS1ClientHandler::request_supported_options`] + /// succeeded as the LSP returned the options it supports. /// /// You must check whether LSP supports the parameters the client wants and then call - /// [`LSPS1ClientHandler::place_order`] to place an order. + /// [`LSPS1ClientHandler::create_order`] to place an order. /// - /// [`LSPS1ClientHandler::place_order`]: crate::lsps1::client::LSPS1ClientHandler::place_order - GetInfoResponse { - /// This is a randomly generated identifier used to track the channel state. + /// [`LSPS1ClientHandler::request_supported_options`]: crate::lsps1::client::LSPS1ClientHandler::request_supported_options + /// [`LSPS1ClientHandler::create_order`]: crate::lsps1::client::LSPS1ClientHandler::create_order + SupportedOptionsReady { + /// The identifier of the issued LSPS1 `get_info` request, as returned by + /// [`LSPS1ClientHandler::request_supported_options`] /// - /// It is not related in anyway to the eventual lightning channel id. - /// It needs to be passed to [`LSPS1ClientHandler::place_order`]. + /// This can be used to track which request this event corresponds to. /// - /// [`LSPS1ClientHandler::place_order`]: crate::lsps1::client::LSPS1ClientHandler::place_order - user_channel_id: u128, + /// [`LSPS1ClientHandler::request_supported_options`]: crate::lsps1::client::LSPS1ClientHandler::request_supported_options + request_id: RequestId, /// The node id of the LSP that provided this response. counterparty_node_id: PublicKey, /// All options supported by the LSP. - options_supported: OptionsSupported, + supported_options: LSPS1Options, + }, + /// A request previously issued via [`LSPS1ClientHandler::request_supported_options`] + /// failed as the LSP returned an error response. + /// + /// [`LSPS1ClientHandler::request_supported_options`]: crate::lsps1::client::LSPS1ClientHandler::request_supported_options + SupportedOptionsRequestFailed { + /// The identifier of the issued LSPS1 `get_info` request, as returned by + /// [`LSPS1ClientHandler::request_supported_options`] + /// + /// This can be used to track which request this event corresponds to. + /// + /// [`LSPS1ClientHandler::request_supported_options`]: crate::lsps1::client::LSPS1ClientHandler::request_supported_options + request_id: RequestId, + /// The node id of the LSP that provided this response. + counterparty_node_id: PublicKey, + /// The error that was returned. + error: ResponseError, }, /// Confirmation from the LSP about the order created by the client. /// @@ -48,25 +67,72 @@ pub enum LSPS1ClientEvent { /// to get information from LSP about progress of the order. /// /// [`LSPS1ClientHandler::check_order_status`]: crate::lsps1::client::LSPS1ClientHandler::check_order_status - DisplayOrder { - /// This is a randomly generated identifier used to track the channel state. - /// It is not related in anyway to the eventual lightning channel id. - /// It needs to be passed to [`LSPS1ClientHandler::check_order_status`]. + OrderCreated { + /// The identifier of the issued LSPS1 `create_order` request, as returned by + /// [`LSPS1ClientHandler::create_order`] + /// + /// This can be used to track which request this event corresponds to. + /// + /// [`LSPS1ClientHandler::create_order`]: crate::lsps1::client::LSPS1ClientHandler::create_order + request_id: RequestId, + /// The node id of the LSP. + counterparty_node_id: PublicKey, + /// The id of the channel order. + order_id: OrderId, + /// The order created by client and approved by LSP. + order: OrderParameters, + /// The details regarding payment of the order + payment: PaymentInfo, + /// The details regarding state of the channel ordered. + channel: Option, + }, + /// Information from the LSP about the status of a previously created order. + /// + /// Will be emitted in response to calling [`LSPS1ClientHandler::check_order_status`]. + /// + /// [`LSPS1ClientHandler::check_order_status`]: crate::lsps1::client::LSPS1ClientHandler::check_order_status + OrderStatus { + /// The identifier of the issued LSPS1 `get_order` request, as returned by + /// [`LSPS1ClientHandler::check_order_status`] + /// + /// This can be used to track which request this event corresponds to. /// /// [`LSPS1ClientHandler::check_order_status`]: crate::lsps1::client::LSPS1ClientHandler::check_order_status - user_channel_id: u128, + request_id: RequestId, /// The node id of the LSP. counterparty_node_id: PublicKey, + /// The id of the channel order. + order_id: OrderId, /// The order created by client and approved by LSP. - order: OrderParams, + order: OrderParameters, /// The details regarding payment of the order payment: PaymentInfo, /// The details regarding state of the channel ordered. channel: Option, }, + /// A request previously issued via [`LSPS1ClientHandler::create_order`] or [`LSPS1ClientHandler::check_order_status`]. + /// failed as the LSP returned an error response. + /// + /// [`LSPS1ClientHandler::create_order`]: crate::lsps1::client::LSPS1ClientHandler::create_order + /// [`LSPS1ClientHandler::check_order_status`]: crate::lsps1::client::LSPS1ClientHandler::check_order_status + OrderRequestFailed { + /// The identifier of the issued LSPS1 `create_order` or `get_order` request, as returned by + /// [`LSPS1ClientHandler::create_order`] or [`LSPS1ClientHandler::check_order_status`]. + /// + /// This can be used to track which request this event corresponds to. + /// + /// [`LSPS1ClientHandler::create_order`]: crate::lsps1::client::LSPS1ClientHandler::create_order + /// [`LSPS1ClientHandler::check_order_status`]: crate::lsps1::client::LSPS1ClientHandler::check_order_status + request_id: RequestId, + /// The node id of the LSP. + counterparty_node_id: PublicKey, + /// The error that was returned. + error: ResponseError, + }, } /// 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 @@ -85,7 +151,7 @@ pub enum LSPS1ServiceEvent { /// The node id of the client making the information request. counterparty_node_id: PublicKey, /// The order requested by the client. - order: OrderParams, + order: OrderParameters, }, /// A request from client to check the status of the payment. /// diff --git a/src/lsps1/mod.rs b/src/lsps1/mod.rs index 06d5e75..9de757a 100644 --- a/src/lsps1/mod.rs +++ b/src/lsps1/mod.rs @@ -12,5 +12,6 @@ pub mod client; pub mod event; pub mod msgs; +#[cfg(lsps1_service)] pub mod service; pub(crate) mod utils; diff --git a/src/lsps1/msgs.rs b/src/lsps1/msgs.rs index 12a6790..42f10c0 100644 --- a/src/lsps1/msgs.rs +++ b/src/lsps1/msgs.rs @@ -1,13 +1,13 @@ //! Message, request, and other primitive types used to implement LSPS1. use crate::lsps0::ser::{ - string_amount, string_amount_option, u32_fee_rate, LSPSMessage, RequestId, ResponseError, + string_amount, u32_fee_rate, unchecked_address, unchecked_address_option, LSPSMessage, + RequestId, ResponseError, }; -use crate::prelude::{String, Vec}; +use crate::prelude::String; -use bitcoin::address::{Address, NetworkUnchecked}; -use bitcoin::{FeeRate, OutPoint}; +use bitcoin::{Address, FeeRate, OutPoint}; use lightning_invoice::Bolt11Invoice; @@ -21,7 +21,8 @@ pub(crate) const LSPS1_GET_INFO_METHOD_NAME: &str = "lsps1.get_info"; 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; +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; /// The identifier of an order. @@ -38,19 +39,13 @@ pub struct GetInfoRequest {} /// An object representing the supported protocol options. #[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)] -pub struct OptionsSupported { +pub struct LSPS1Options { /// The smallest number of confirmations needed for the LSP to accept a channel as confirmed. pub min_required_channel_confirmations: u16, /// The smallest number of blocks in which the LSP can confirm the funding transaction. pub min_funding_confirms_within_blocks: u16, - /// The minimum number of block confirmations before the LSP accepts an on-chain payment as confirmed. - pub min_onchain_payment_confirmations: Option, /// Indicates if the LSP supports zero reserve. pub supports_zero_channel_reserve: bool, - /// Indicates the minimum amount of satoshi that is required for the LSP to accept a payment - /// on-chain. - #[serde(with = "string_amount_option")] - pub min_onchain_payment_size_sat: Option, /// The maximum number of blocks a channel can be leased for. pub max_channel_expiry_blocks: u32, /// The minimum number of satoshi that the client MUST request. @@ -77,7 +72,8 @@ pub struct OptionsSupported { #[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)] pub struct GetInfoResponse { /// All options supported by the LSP. - pub options: OptionsSupported, + #[serde(flatten)] + pub options: LSPS1Options, } /// A request made to an LSP to create an order. @@ -88,12 +84,17 @@ pub struct GetInfoResponse { pub struct CreateOrderRequest { /// The order made. #[serde(flatten)] - pub order: OrderParams, + pub order: OrderParameters, + /// The address where the LSP will send the funds if the order fails. + #[serde(default)] + #[serde(skip_serializing_if = "Option::is_none")] + #[serde(with = "unchecked_address_option")] + pub refund_onchain_address: Option
, } /// An object representing an LSPS1 channel order. #[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)] -pub struct OrderParams { +pub struct OrderParameters { /// Indicates how many satoshi the LSP will provide on their side. #[serde(with = "string_amount")] pub lsp_balance_sat: u64, @@ -110,9 +111,7 @@ pub struct OrderParams { /// Indicates how long the channel is leased for in block time. pub channel_expiry_blocks: u32, /// May contain arbitrary associated data like a coupon code or a authentication token. - pub token: String, - /// The address where the LSP will send the funds if the order fails. - pub refund_onchain_address: Option>, + pub token: Option, /// Indicates if the channel should be announced to the network. pub announce_channel: bool, } @@ -124,11 +123,9 @@ pub struct CreateOrderResponse { pub order_id: OrderId, /// The parameters of channel order. #[serde(flatten)] - pub order: OrderParams, + pub order: OrderParameters, /// The datetime when the order was created pub created_at: chrono::DateTime, - /// The datetime when the order expires. - pub expires_at: chrono::DateTime, /// The current state of the order. pub order_state: OrderState, /// Contains details about how to pay for the order. @@ -152,19 +149,46 @@ pub enum OrderState { /// Details regarding how to pay for an order. #[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)] pub struct PaymentInfo { + /// A Lightning payment using BOLT 11. + pub bolt11: Option, + /// An onchain payment. + pub onchain: Option, +} + +/// A Lightning payment using BOLT 11. +#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)] +pub struct Bolt11PaymentInfo { /// Indicates the current state of the payment. pub state: PaymentState, + /// The datetime when the payment option expires. + pub expires_at: chrono::DateTime, /// The total fee the LSP will charge to open this channel in satoshi. #[serde(with = "string_amount")] pub fee_total_sat: u64, - /// What the client needs to pay in total to open the requested channel. + /// The amount the client needs to pay to have the requested channel openend. #[serde(with = "string_amount")] pub order_total_sat: u64, /// A BOLT11 invoice the client can pay to have to channel opened. - pub bolt11_invoice: Bolt11Invoice, + pub invoice: Bolt11Invoice, +} + +/// An onchain payment. +#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)] +pub struct OnchainPaymentInfo { + /// Indicates the current state of the payment. + pub state: PaymentState, + /// The datetime when the payment option expires. + pub expires_at: chrono::DateTime, + /// The total fee the LSP will charge to open this channel in satoshi. + #[serde(with = "string_amount")] + pub fee_total_sat: u64, + /// The amount the client needs to pay to have the requested channel openend. + #[serde(with = "string_amount")] + pub order_total_sat: u64, /// An on-chain address the client can send [`Self::order_total_sat`] to to have the channel /// opened. - pub onchain_address: Address, + #[serde(with = "unchecked_address")] + pub address: Address, /// The minimum number of block confirmations that are required for the on-chain payment to be /// considered confirmed. pub min_onchain_payment_confirmations: Option, @@ -172,21 +196,26 @@ pub struct PaymentInfo { /// confirmed without a confirmation. #[serde(with = "u32_fee_rate")] pub min_fee_for_0conf: FeeRate, - /// Details regarding a detected on-chain payment. - pub onchain_payment: Option, + /// The address where the LSP will send the funds if the order fails. + #[serde(default)] + #[serde(skip_serializing_if = "Option::is_none")] + #[serde(with = "unchecked_address_option")] + pub refund_onchain_address: Option
, } -/// The state of an [`PaymentInfo`]. +/// The state of a payment. +/// +/// *Note*: Previously, the spec also knew a `CANCELLED` state for BOLT11 payments, which has since +/// been deprecated and `REFUNDED` should be used instead. #[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)] #[serde(rename_all = "SCREAMING_SNAKE_CASE")] pub enum PaymentState { /// A payment is expected. ExpectPayment, - /// A Lighting payment has arrived, but the preimage has not been released yet. - Hold, /// A sufficient payment has been received. Paid, /// The payment has been refunded. + #[serde(alias = "CANCELLED")] Refunded, } @@ -287,9 +316,7 @@ mod tests { fn options_supported_serialization() { let min_required_channel_confirmations = 0; let min_funding_confirms_within_blocks = 6; - let min_onchain_payment_confirmations = Some(6); let supports_zero_channel_reserve = true; - let min_onchain_payment_size_sat = Some(100_000); let max_channel_expiry_blocks = 144; let min_initial_client_balance_sat = 10_000_000; let max_initial_client_balance_sat = 100_000_000; @@ -298,12 +325,10 @@ mod tests { let min_channel_balance_sat = 100_000; let max_channel_balance_sat = 100_000_000; - let options_supported = OptionsSupported { + let options_supported = LSPS1Options { min_required_channel_confirmations, min_funding_confirms_within_blocks, - min_onchain_payment_confirmations, supports_zero_channel_reserve, - min_onchain_payment_size_sat, max_channel_expiry_blocks, min_initial_client_balance_sat, max_initial_client_balance_sat, @@ -313,7 +338,7 @@ mod tests { max_channel_balance_sat, }; - let json_str = r#"{"max_channel_balance_sat":"100000000","max_channel_expiry_blocks":144,"max_initial_client_balance_sat":"100000000","max_initial_lsp_balance_sat":"100000000","min_channel_balance_sat":"100000","min_funding_confirms_within_blocks":6,"min_initial_client_balance_sat":"10000000","min_initial_lsp_balance_sat":"100000","min_onchain_payment_confirmations":6,"min_onchain_payment_size_sat":"100000","min_required_channel_confirmations":0,"supports_zero_channel_reserve":true}"#; + let json_str = r#"{"max_channel_balance_sat":"100000000","max_channel_expiry_blocks":144,"max_initial_client_balance_sat":"100000000","max_initial_lsp_balance_sat":"100000000","min_channel_balance_sat":"100000","min_funding_confirms_within_blocks":6,"min_initial_client_balance_sat":"10000000","min_initial_lsp_balance_sat":"100000","min_required_channel_confirmations":0,"supports_zero_channel_reserve":true}"#; assert_eq!(json_str, serde_json::json!(options_supported).to_string()); assert_eq!(options_supported, serde_json::from_str(json_str).unwrap()); @@ -326,20 +351,16 @@ mod tests { let _get_info_request: GetInfoRequest = serde_json::from_str(json_str).unwrap(); let json_str = r#"{ - "options": { - "min_required_channel_confirmations": 0, - "min_funding_confirms_within_blocks" : 6, - "min_onchain_payment_confirmations": null, - "supports_zero_channel_reserve": true, - "min_onchain_payment_size_sat": null, - "max_channel_expiry_blocks": 20160, - "min_initial_client_balance_sat": "20000", - "max_initial_client_balance_sat": "100000000", - "min_initial_lsp_balance_sat": "0", - "max_initial_lsp_balance_sat": "100000000", - "min_channel_balance_sat": "50000", - "max_channel_balance_sat": "100000000" - } + "min_required_channel_confirmations": 0, + "min_funding_confirms_within_blocks" : 6, + "supports_zero_channel_reserve": true, + "max_channel_expiry_blocks": 20160, + "min_initial_client_balance_sat": "20000", + "max_initial_client_balance_sat": "100000000", + "min_initial_lsp_balance_sat": "0", + "max_initial_lsp_balance_sat": "100000000", + "min_channel_balance_sat": "50000", + "max_channel_balance_sat": "100000000" }"#; let _get_info_response: GetInfoResponse = serde_json::from_str(json_str).unwrap(); @@ -355,6 +376,46 @@ mod tests { }"#; let _create_order_request: CreateOrderRequest = serde_json::from_str(json_str).unwrap(); + let json_str = r#"{ + "state" : "EXPECT_PAYMENT", + "expires_at": "2025-01-01T00:00:00Z", + "fee_total_sat": "8888", + "order_total_sat": "200888", + "invoice": "lnbc252u1p3aht9ysp580g4633gd2x9lc5al0wd8wx0mpn9748jeyz46kqjrpxn52uhfpjqpp5qgf67tcqmuqehzgjm8mzya90h73deafvr4m5705l5u5l4r05l8cqdpud3h8ymm4w3jhytnpwpczqmt0de6xsmre2pkxzm3qydmkzdjrdev9s7zhgfaqxqyjw5qcqpjrzjqt6xptnd85lpqnu2lefq4cx070v5cdwzh2xlvmdgnu7gqp4zvkus5zapryqqx9qqqyqqqqqqqqqqqcsq9q9qyysgqen77vu8xqjelum24hgjpgfdgfgx4q0nehhalcmuggt32japhjuksq9jv6eksjfnppm4hrzsgyxt8y8xacxut9qv3fpyetz8t7tsymygq8yzn05" + }"#; + let _bolt11_payment: Bolt11PaymentInfo = serde_json::from_str(json_str).unwrap(); + + let json_str = r#"{ + "state": "EXPECT_PAYMENT", + "expires_at": "2025-01-01T00:00:00Z", + "fee_total_sat": "9999", + "order_total_sat": "200999", + "address": "bc1p5uvtaxzkjwvey2tfy49k5vtqfpjmrgm09cvs88ezyy8h2zv7jhas9tu4yr", + "min_onchain_payment_confirmations": 1, + "min_fee_for_0conf": 253 + }"#; + let _onchain_payment: OnchainPaymentInfo = serde_json::from_str(json_str).unwrap(); + + let json_str = r#"{ + "bolt11": { + "state" : "EXPECT_PAYMENT", + "expires_at": "2025-01-01T00:00:00Z", + "fee_total_sat": "8888", + "order_total_sat": "200888", + "invoice": "lnbc252u1p3aht9ysp580g4633gd2x9lc5al0wd8wx0mpn9748jeyz46kqjrpxn52uhfpjqpp5qgf67tcqmuqehzgjm8mzya90h73deafvr4m5705l5u5l4r05l8cqdpud3h8ymm4w3jhytnpwpczqmt0de6xsmre2pkxzm3qydmkzdjrdev9s7zhgfaqxqyjw5qcqpjrzjqt6xptnd85lpqnu2lefq4cx070v5cdwzh2xlvmdgnu7gqp4zvkus5zapryqqx9qqqyqqqqqqqqqqqcsq9q9qyysgqen77vu8xqjelum24hgjpgfdgfgx4q0nehhalcmuggt32japhjuksq9jv6eksjfnppm4hrzsgyxt8y8xacxut9qv3fpyetz8t7tsymygq8yzn05" + }, + "onchain": { + "state": "EXPECT_PAYMENT", + "expires_at": "2025-01-01T00:00:00Z", + "fee_total_sat": "9999", + "order_total_sat": "200999", + "address": "bc1p5uvtaxzkjwvey2tfy49k5vtqfpjmrgm09cvs88ezyy8h2zv7jhas9tu4yr", + "min_onchain_payment_confirmations": 1, + "min_fee_for_0conf": 253 + } + }"#; + let _payment: PaymentInfo = serde_json::from_str(json_str).unwrap(); + let json_str = r#"{ "order_id": "bb4b5d0a-8334-49d8-9463-90a6d413af7c", "lsp_balance_sat": "5000000", @@ -364,18 +425,26 @@ mod tests { "channel_expiry_blocks": 12, "token": "", "created_at": "2012-04-23T18:25:43.511Z", - "expires_at": "2015-01-25T19:29:44.612Z", "announce_channel": true, "order_state": "CREATED", "payment": { - "state": "EXPECT_PAYMENT", - "fee_total_sat": "8888", - "order_total_sat": "2008888", - "bolt11_invoice": "lnbc252u1p3aht9ysp580g4633gd2x9lc5al0wd8wx0mpn9748jeyz46kqjrpxn52uhfpjqpp5qgf67tcqmuqehzgjm8mzya90h73deafvr4m5705l5u5l4r05l8cqdpud3h8ymm4w3jhytnpwpczqmt0de6xsmre2pkxzm3qydmkzdjrdev9s7zhgfaqxqyjw5qcqpjrzjqt6xptnd85lpqnu2lefq4cx070v5cdwzh2xlvmdgnu7gqp4zvkus5zapryqqx9qqqyqqqqqqqqqqqcsq9q9qyysgqen77vu8xqjelum24hgjpgfdgfgx4q0nehhalcmuggt32japhjuksq9jv6eksjfnppm4hrzsgyxt8y8xacxut9qv3fpyetz8t7tsymygq8yzn05", - "onchain_address": "bc1p5uvtaxzkjwvey2tfy49k5vtqfpjmrgm09cvs88ezyy8h2zv7jhas9tu4yr", - "min_onchain_payment_confirmations": 0, - "min_fee_for_0conf": 253, - "onchain_payment": null + "bolt11": { + "state": "EXPECT_PAYMENT", + "expires_at": "2015-01-25T19:29:44.612Z", + "fee_total_sat": "8888", + "order_total_sat": "2008888", + "invoice" : "lnbc252u1p3aht9ysp580g4633gd2x9lc5al0wd8wx0mpn9748jeyz46kqjrpxn52uhfpjqpp5qgf67tcqmuqehzgjm8mzya90h73deafvr4m5705l5u5l4r05l8cqdpud3h8ymm4w3jhytnpwpczqmt0de6xsmre2pkxzm3qydmkzdjrdev9s7zhgfaqxqyjw5qcqpjrzjqt6xptnd85lpqnu2lefq4cx070v5cdwzh2xlvmdgnu7gqp4zvkus5zapryqqx9qqqyqqqqqqqqqqqcsq9q9qyysgqen77vu8xqjelum24hgjpgfdgfgx4q0nehhalcmuggt32japhjuksq9jv6eksjfnppm4hrzsgyxt8y8xacxut9qv3fpyetz8t7tsymygq8yzn05" + }, + "onchain": { + "state": "EXPECT_PAYMENT", + "expires_at": "2015-01-25T19:29:44.612Z", + "fee_total_sat": "9999", + "order_total_sat": "2009999", + "address" : "bc1p5uvtaxzkjwvey2tfy49k5vtqfpjmrgm09cvs88ezyy8h2zv7jhas9tu4yr", + "min_fee_for_0conf": 253, + "min_onchain_payment_confirmations": 0, + "refund_onchain_address": "bc1qvmsy0f3yyes6z9jvddk8xqwznndmdwapvrc0xrmhd3vqj5rhdrrq6hz49h" + } }, "channel": null }"#; @@ -386,27 +455,19 @@ mod tests { }"#; let _get_order_request: GetOrderRequest = serde_json::from_str(json_str).unwrap(); - let json_str = r#"{ - "state": "EXPECT_PAYMENT", - "fee_total_sat": "8888", - "order_total_sat": "2008888", - "bolt11_invoice": "lnbc252u1p3aht9ysp580g4633gd2x9lc5al0wd8wx0mpn9748jeyz46kqjrpxn52uhfpjqpp5qgf67tcqmuqehzgjm8mzya90h73deafvr4m5705l5u5l4r05l8cqdpud3h8ymm4w3jhytnpwpczqmt0de6xsmre2pkxzm3qydmkzdjrdev9s7zhgfaqxqyjw5qcqpjrzjqt6xptnd85lpqnu2lefq4cx070v5cdwzh2xlvmdgnu7gqp4zvkus5zapryqqx9qqqyqqqqqqqqqqqcsq9q9qyysgqen77vu8xqjelum24hgjpgfdgfgx4q0nehhalcmuggt32japhjuksq9jv6eksjfnppm4hrzsgyxt8y8xacxut9qv3fpyetz8t7tsymygq8yzn05", - "onchain_address": "bc1p5uvtaxzkjwvey2tfy49k5vtqfpjmrgm09cvs88ezyy8h2zv7jhas9tu4yr", - "min_onchain_payment_confirmations": 1, - "min_fee_for_0conf": 253, - "onchain_payment": { - "outpoint": "0301e0480b374b32851a9462db29dc19fe830a7f7d7a88b81612b9d42099c0ae:1", - "sat": "1200", - "confirmed": false - } - }"#; - let _payment: PaymentInfo = serde_json::from_str(json_str).unwrap(); - let json_str = r#"{ "funded_at": "2012-04-23T18:25:43.511Z", "funding_outpoint": "0301e0480b374b32851a9462db29dc19fe830a7f7d7a88b81612b9d42099c0ae:0", "expires_at": "2012-04-23T18:25:43.511Z" }"#; let _channel: ChannelInfo = serde_json::from_str(json_str).unwrap(); + + let json_str = r#""CANCELLED""#; + let payment_state: PaymentState = serde_json::from_str(json_str).unwrap(); + assert_eq!(payment_state, PaymentState::Refunded); + + let json_str = r#""REFUNDED""#; + let payment_state: PaymentState = serde_json::from_str(json_str).unwrap(); + assert_eq!(payment_state, PaymentState::Refunded); } } diff --git a/src/lsps1/service.rs b/src/lsps1/service.rs index 1fbc998..e4e6508 100644 --- a/src/lsps1/service.rs +++ b/src/lsps1/service.rs @@ -12,7 +12,7 @@ use super::event::LSPS1ServiceEvent; use super::msgs::{ ChannelInfo, CreateOrderRequest, CreateOrderResponse, GetInfoResponse, GetOrderRequest, - LSPS1Message, LSPS1Request, LSPS1Response, OptionsSupported, OrderId, OrderParams, OrderState, + LSPS1Message, LSPS1Options, LSPS1Request, LSPS1Response, OrderId, OrderParameters, OrderState, PaymentInfo, LSPS1_CREATE_ORDER_REQUEST_ORDER_MISMATCH_ERROR_CODE, }; use super::utils::is_valid; @@ -42,7 +42,7 @@ pub struct LSPS1ServiceConfig { /// A token to be send with each channel request. pub token: Option, /// The options supported by the LSP. - pub options_supported: Option, + pub supported_options: Option, } struct ChannelStateError(String); @@ -72,9 +72,8 @@ impl OutboundRequestState { } struct OutboundLSPS1Config { - order: OrderParams, + order: OrderParameters, created_at: chrono::DateTime, - expires_at: chrono::DateTime, payment: PaymentInfo, } @@ -85,12 +84,12 @@ struct OutboundCRChannel { impl OutboundCRChannel { fn new( - order: OrderParams, created_at: chrono::DateTime, expires_at: chrono::DateTime, - order_id: OrderId, payment: PaymentInfo, + order: OrderParameters, created_at: chrono::DateTime, order_id: OrderId, + payment: PaymentInfo, ) -> Self { Self { state: OutboundRequestState::OrderCreated { order_id }, - config: OutboundLSPS1Config { order, created_at, expires_at, payment }, + config: OutboundLSPS1Config { order, created_at, payment }, } } fn awaiting_payment(&mut self) -> Result<(), LightningError> { @@ -98,10 +97,10 @@ impl OutboundCRChannel { Ok(()) } - fn check_order_validity(&self, options_supported: &OptionsSupported) -> bool { + fn check_order_validity(&self, supported_options: &LSPS1Options) -> bool { let order = &self.config.order; - is_valid(order, options_supported) + is_valid(order, supported_options) } } @@ -171,7 +170,7 @@ where let response = LSPS1Response::GetInfo(GetInfoResponse { options: self .config - .options_supported + .supported_options .clone() .ok_or(LightningError { err: format!("Configuration for LSP server not set."), @@ -188,13 +187,13 @@ where fn handle_create_order_request( &self, request_id: RequestId, counterparty_node_id: &PublicKey, params: CreateOrderRequest, ) -> Result<(), LightningError> { - if !is_valid(¶ms.order, &self.config.options_supported.as_ref().unwrap()) { + if !is_valid(¶ms.order, &self.config.supported_options.as_ref().unwrap()) { let response = LSPS1Response::CreateOrderError(ResponseError { code: LSPS1_CREATE_ORDER_REQUEST_ORDER_MISMATCH_ERROR_CODE, message: format!("Order does not match options supported by LSP server"), data: Some(format!( "Supported options are {:?}", - &self.config.options_supported.as_ref().unwrap() + &self.config.supported_options.as_ref().unwrap() )), }); let msg = LSPS1Message::Response(request_id, response).into(); @@ -239,7 +238,7 @@ where /// [`LSPS1ServiceEvent::RequestForPaymentDetails`]: crate::lsps1::event::LSPS1ServiceEvent::RequestForPaymentDetails pub fn send_payment_details( &self, request_id: RequestId, counterparty_node_id: &PublicKey, payment: PaymentInfo, - created_at: chrono::DateTime, expires_at: chrono::DateTime, + created_at: chrono::DateTime, ) -> Result<(), APIError> { let (result, response) = { let outer_state_lock = self.per_peer_state.read().unwrap(); @@ -254,7 +253,6 @@ where let channel = OutboundCRChannel::new( params.order.clone(), created_at.clone(), - expires_at.clone(), order_id.clone(), payment.clone(), ); @@ -266,7 +264,6 @@ where order_id, order_state: OrderState::Created, created_at, - expires_at, payment, channel: None, }); @@ -386,7 +383,6 @@ where order: config.order.clone(), order_state, created_at: config.created_at, - expires_at: config.expires_at, payment: config.payment.clone(), channel, }); diff --git a/src/lsps1/utils.rs b/src/lsps1/utils.rs index 7a67521..530761e 100644 --- a/src/lsps1/utils.rs +++ b/src/lsps1/utils.rs @@ -1,10 +1,10 @@ -use super::msgs::{OptionsSupported, OrderParams}; +use super::msgs::{LSPS1Options, OrderParameters}; pub fn check_range(min: u64, max: u64, value: u64) -> bool { (value >= min) && (value <= max) } -pub fn is_valid(order: &OrderParams, options: &OptionsSupported) -> bool { +pub fn is_valid(order: &OrderParameters, options: &LSPS1Options) -> bool { let bool = check_range( options.min_initial_client_balance_sat, options.max_initial_client_balance_sat, diff --git a/src/manager.rs b/src/manager.rs index 1474740..ddb68bf 100644 --- a/src/manager.rs +++ b/src/manager.rs @@ -9,11 +9,9 @@ use crate::lsps0::ser::{ use crate::lsps0::service::LSPS0ServiceHandler; use crate::message_queue::MessageQueue; -#[cfg(lsps1)] use crate::lsps1::client::{LSPS1ClientConfig, LSPS1ClientHandler}; -#[cfg(lsps1)] use crate::lsps1::msgs::LSPS1Message; -#[cfg(lsps1)] +#[cfg(lsps1_service)] use crate::lsps1::service::{LSPS1ServiceConfig, LSPS1ServiceHandler}; use crate::lsps2::client::{LSPS2ClientConfig, LSPS2ClientHandler}; @@ -44,7 +42,7 @@ const LSPS_FEATURE_BIT: usize = 729; /// to provide liquidity services to clients. pub struct LiquidityServiceConfig { /// Optional server-side configuration for LSPS1 channel requests. - #[cfg(lsps1)] + #[cfg(lsps1_service)] pub lsps1_service_config: Option, /// Optional server-side configuration for JIT channels /// should you want to support them. @@ -60,7 +58,6 @@ pub struct LiquidityServiceConfig { /// to access liquidity services from a provider. pub struct LiquidityClientConfig { /// Optional client-side configuration for LSPS1 channel requests. - #[cfg(lsps1)] pub lsps1_client_config: Option, /// Optional client-side configuration for JIT channels. pub lsps2_client_config: Option, @@ -97,10 +94,9 @@ where ignored_peers: RwLock>, lsps0_client_handler: LSPS0ClientHandler, lsps0_service_handler: Option, - #[cfg(lsps1)] + #[cfg(lsps1_service)] lsps1_service_handler: Option>, - #[cfg(lsps1)] - lsps1_client_handler: Option>, + lsps1_client_handler: Option>, lsps2_service_handler: Option>, lsps2_client_handler: Option>, service_config: Option, @@ -129,17 +125,7 @@ where { let pending_events = Arc::new(EventQueue::new()); let ignored_peers = RwLock::new(HashSet::new()); - let lsps0_client_handler = LSPS0ClientHandler::new( - entropy_source.clone(), - Arc::clone(&pending_messages), - Arc::clone(&pending_events), - ); - - let lsps0_service_handler = if service_config.is_some() { - Some(LSPS0ServiceHandler::new(vec![], Arc::clone(&pending_messages))) - } else { - None - }; + let mut supported_protocols = Vec::new(); let lsps2_client_handler = client_config.as_ref().and_then(|config| { config.lsps2_client_config.map(|config| { @@ -153,6 +139,11 @@ where { }); let lsps2_service_handler = service_config.as_ref().and_then(|config| { config.lsps2_service_config.as_ref().map(|config| { + if let Some(number) = + as ProtocolMessageHandler>::PROTOCOL_NUMBER + { + supported_protocols.push(number); + } LSPS2ServiceHandler::new( Arc::clone(&pending_messages), Arc::clone(&pending_events), @@ -162,22 +153,24 @@ where { }) }); - #[cfg(lsps1)] let lsps1_client_handler = client_config.as_ref().and_then(|config| { config.lsps1_client_config.as_ref().map(|config| { LSPS1ClientHandler::new( entropy_source.clone(), Arc::clone(&pending_messages), Arc::clone(&pending_events), - channel_manager.clone(), - chain_source.clone(), config.clone(), ) }) }); - #[cfg(lsps1)] + #[cfg(lsps1_service)] let lsps1_service_handler = service_config.as_ref().and_then(|config| { + if let Some(number) = + as ProtocolMessageHandler>::PROTOCOL_NUMBER + { + supported_protocols.push(number); + } config.lsps1_service_config.as_ref().map(|config| { LSPS1ServiceHandler::new( entropy_source.clone(), @@ -190,6 +183,18 @@ where { }) }); + let lsps0_client_handler = LSPS0ClientHandler::new( + entropy_source.clone(), + Arc::clone(&pending_messages), + Arc::clone(&pending_events), + ); + + let lsps0_service_handler = if service_config.is_some() { + Some(LSPS0ServiceHandler::new(vec![], Arc::clone(&pending_messages))) + } else { + None + }; + Self { pending_messages, pending_events, @@ -197,9 +202,8 @@ where { ignored_peers, lsps0_client_handler, lsps0_service_handler, - #[cfg(lsps1)] lsps1_client_handler, - #[cfg(lsps1)] + #[cfg(lsps1_service)] lsps1_service_handler, lsps2_client_handler, lsps2_service_handler, @@ -221,13 +225,12 @@ where { } /// Returns a reference to the LSPS1 client-side handler. - #[cfg(lsps1)] - pub fn lsps1_client_handler(&self) -> Option<&LSPS1ClientHandler> { + pub fn lsps1_client_handler(&self) -> Option<&LSPS1ClientHandler> { self.lsps1_client_handler.as_ref() } /// Returns a reference to the LSPS1 server-side handler. - #[cfg(lsps1)] + #[cfg(lsps1_service)] pub fn lsps1_service_handler(&self) -> Option<&LSPS1ServiceHandler> { self.lsps1_service_handler.as_ref() } @@ -411,23 +414,28 @@ where { }, } }, - #[cfg(lsps1)] - LSPSMessage::LSPS1(msg @ LSPS1Message::Response(..)) => match &self.lsps1_client_handler { - Some(lsps1_client_handler) => { - lsps1_client_handler.handle_message(msg, sender_node_id)?; - }, - None => { - return Err(LightningError { err: format!("Received LSPS1 response message without LSPS1 client handler configured. From node = {:?}", sender_node_id), action: ErrorAction::IgnoreAndLog(Level::Info)}); - }, + LSPSMessage::LSPS1(msg @ LSPS1Message::Response(..)) => { + match &self.lsps1_client_handler { + Some(lsps1_client_handler) => { + lsps1_client_handler.handle_message(msg, sender_node_id)?; + }, + None => { + return Err(LightningError { err: format!("Received LSPS1 response message without LSPS1 client handler configured. From node = {:?}", sender_node_id), action: ErrorAction::IgnoreAndLog(Level::Info)}); + }, + } }, - #[cfg(lsps1)] - LSPSMessage::LSPS1(msg @ LSPS1Message::Request(..)) => match &self.lsps1_service_handler { - Some(lsps1_service_handler) => { - lsps1_service_handler.handle_message(msg, sender_node_id)?; - }, - None => { - return Err(LightningError { err: format!("Received LSPS1 request message without LSPS1 service handler configured. From node = {:?}", sender_node_id), action: ErrorAction::IgnoreAndLog(Level::Info)}); - }, + LSPSMessage::LSPS1(_msg @ LSPS1Message::Request(..)) => { + #[cfg(lsps1_service)] + match &self.lsps1_service_handler { + Some(lsps1_service_handler) => { + lsps1_service_handler.handle_message(_msg, sender_node_id)?; + }, + None => { + return Err(LightningError { err: format!("Received LSPS1 request message without LSPS1 service handler configured. From node = {:?}", sender_node_id), action: ErrorAction::IgnoreAndLog(Level::Info)}); + }, + } + #[cfg(not(lsps1_service))] + return Err(LightningError { err: format!("Received LSPS1 request message without LSPS1 service handler configured. From node = {:?}", sender_node_id), action: ErrorAction::IgnoreAndLog(Level::Info)}); }, LSPSMessage::LSPS2(msg @ LSPS2Message::Response(..)) => { match &self.lsps2_client_handler { diff --git a/tests/lsps2_integration_tests.rs b/tests/lsps2_integration_tests.rs index a62f3a0..992e2c2 100644 --- a/tests/lsps2_integration_tests.rs +++ b/tests/lsps2_integration_tests.rs @@ -85,7 +85,7 @@ fn invoice_generation_flow() { let promise_secret = [42; 32]; let lsps2_service_config = LSPS2ServiceConfig { promise_secret }; let service_config = LiquidityServiceConfig { - #[cfg(lsps1)] + #[cfg(lsps1_service)] lsps1_service_config: None, lsps2_service_config: Some(lsps2_service_config), advertise_service: true, @@ -93,7 +93,6 @@ fn invoice_generation_flow() { let lsps2_client_config = LSPS2ClientConfig::default(); let client_config = LiquidityClientConfig { - #[cfg(lsps1)] lsps1_client_config: None, lsps2_client_config: Some(lsps2_client_config), };