lightning_liquidity/lsps1/
client.rs

1// This file is Copyright its original authors, visible in version control
2// history.
3//
4// This file is licensed under the Apache License, Version 2.0 <LICENSE-APACHE
5// or http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
6// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your option.
7// You may not use this file except in accordance with one or both of these
8// licenses.
9
10//! Contains the main bLIP-51 / LSPS1 client object, [`LSPS1ClientHandler`].
11
12use super::event::LSPS1ClientEvent;
13use super::msgs::{
14	LSPS1CreateOrderRequest, LSPS1CreateOrderResponse, LSPS1GetInfoRequest, LSPS1GetInfoResponse,
15	LSPS1GetOrderRequest, LSPS1Message, LSPS1OrderId, LSPS1OrderParams, LSPS1Request,
16	LSPS1Response,
17};
18use crate::message_queue::MessageQueue;
19
20use crate::events::EventQueue;
21use crate::lsps0::ser::{LSPSProtocolMessageHandler, LSPSRequestId, LSPSResponseError};
22use crate::prelude::{new_hash_map, HashMap, HashSet};
23use crate::sync::{Arc, Mutex, RwLock};
24
25use lightning::ln::msgs::{ErrorAction, LightningError};
26use lightning::sign::EntropySource;
27use lightning::util::logger::Level;
28use lightning::util::persist::KVStore;
29
30use bitcoin::secp256k1::PublicKey;
31use bitcoin::Address;
32
33use core::ops::Deref;
34
35/// Client-side configuration options for bLIP-51 / LSPS1 channel requests.
36#[derive(Clone, Debug)]
37pub struct LSPS1ClientConfig {
38	/// The maximally allowed channel fees.
39	pub max_channel_fees_msat: Option<u64>,
40}
41
42#[derive(Default)]
43struct PeerState {
44	pending_get_info_requests: HashSet<LSPSRequestId>,
45	pending_create_order_requests: HashSet<LSPSRequestId>,
46	pending_get_order_requests: HashSet<LSPSRequestId>,
47}
48
49/// The main object allowing to send and receive bLIP-51 / LSPS1 messages.
50pub struct LSPS1ClientHandler<ES: Deref, K: Deref + Clone>
51where
52	ES::Target: EntropySource,
53	K::Target: KVStore,
54{
55	entropy_source: ES,
56	pending_messages: Arc<MessageQueue>,
57	pending_events: Arc<EventQueue<K>>,
58	per_peer_state: RwLock<HashMap<PublicKey, Mutex<PeerState>>>,
59	config: LSPS1ClientConfig,
60}
61
62impl<ES: Deref, K: Deref + Clone> LSPS1ClientHandler<ES, K>
63where
64	ES::Target: EntropySource,
65	K::Target: KVStore,
66{
67	/// Constructs an `LSPS1ClientHandler`.
68	pub(crate) fn new(
69		entropy_source: ES, pending_messages: Arc<MessageQueue>,
70		pending_events: Arc<EventQueue<K>>, config: LSPS1ClientConfig,
71	) -> Self {
72		Self {
73			entropy_source,
74			pending_messages,
75			pending_events,
76			per_peer_state: RwLock::new(new_hash_map()),
77			config,
78		}
79	}
80
81	/// Returns a reference to the used config.
82	pub fn config(&self) -> &LSPS1ClientConfig {
83		&self.config
84	}
85
86	/// Request the supported options from the LSP.
87	///
88	/// The user will receive the LSP's response via an [`SupportedOptionsReady`] event.
89	///
90	/// `counterparty_node_id` is the `node_id` of the LSP you would like to use.
91	///
92	/// Returns the used [`LSPSRequestId`], which will be returned via [`SupportedOptionsReady`].
93	///
94	/// [`SupportedOptionsReady`]: crate::lsps1::event::LSPS1ClientEvent::SupportedOptionsReady
95	pub fn request_supported_options(&self, counterparty_node_id: PublicKey) -> LSPSRequestId {
96		let mut message_queue_notifier = self.pending_messages.notifier();
97
98		let request_id = crate::utils::generate_request_id(&self.entropy_source);
99		{
100			let mut outer_state_lock = self.per_peer_state.write().unwrap();
101			let inner_state_lock = outer_state_lock
102				.entry(counterparty_node_id)
103				.or_insert(Mutex::new(PeerState::default()));
104			let mut peer_state_lock = inner_state_lock.lock().unwrap();
105			peer_state_lock.pending_get_info_requests.insert(request_id.clone());
106		}
107
108		let request = LSPS1Request::GetInfo(LSPS1GetInfoRequest {});
109		let msg = LSPS1Message::Request(request_id.clone(), request).into();
110		message_queue_notifier.enqueue(&counterparty_node_id, msg);
111		request_id
112	}
113
114	fn handle_get_info_response(
115		&self, request_id: LSPSRequestId, counterparty_node_id: &PublicKey,
116		result: LSPS1GetInfoResponse,
117	) -> Result<(), LightningError> {
118		let event_queue_notifier = self.pending_events.notifier();
119
120		let outer_state_lock = self.per_peer_state.write().unwrap();
121		match outer_state_lock.get(counterparty_node_id) {
122			Some(inner_state_lock) => {
123				let mut peer_state_lock = inner_state_lock.lock().unwrap();
124
125				if !peer_state_lock.pending_get_info_requests.remove(&request_id) {
126					return Err(LightningError {
127						err: format!(
128							"Received get_info response for an unknown request: {:?}",
129							request_id
130						),
131						action: ErrorAction::IgnoreAndLog(Level::Debug),
132					});
133				}
134
135				event_queue_notifier.enqueue(LSPS1ClientEvent::SupportedOptionsReady {
136					counterparty_node_id: *counterparty_node_id,
137					supported_options: result.options,
138					request_id,
139				});
140				Ok(())
141			},
142			None => Err(LightningError {
143				err: format!(
144					"Received get_info response from unknown peer: {}",
145					counterparty_node_id
146				),
147				action: ErrorAction::IgnoreAndLog(Level::Debug),
148			}),
149		}
150	}
151
152	fn handle_get_info_error(
153		&self, request_id: LSPSRequestId, counterparty_node_id: &PublicKey,
154		error: LSPSResponseError,
155	) -> Result<(), LightningError> {
156		let event_queue_notifier = self.pending_events.notifier();
157
158		let outer_state_lock = self.per_peer_state.read().unwrap();
159		match outer_state_lock.get(counterparty_node_id) {
160			Some(inner_state_lock) => {
161				let mut peer_state_lock = inner_state_lock.lock().unwrap();
162
163				if !peer_state_lock.pending_get_info_requests.remove(&request_id) {
164					return Err(LightningError {
165						err: format!(
166							"Received get_info error for an unknown request: {:?}",
167							request_id
168						),
169						action: ErrorAction::IgnoreAndLog(Level::Debug),
170					});
171				}
172
173				event_queue_notifier.enqueue(LSPS1ClientEvent::SupportedOptionsRequestFailed {
174					request_id: request_id.clone(),
175					counterparty_node_id: *counterparty_node_id,
176					error: error.clone(),
177				});
178
179				Err(LightningError {
180					err: format!(
181						"Received get_info error response for request {:?}: {:?}",
182						request_id, error
183					),
184					action: ErrorAction::IgnoreAndLog(Level::Error),
185				})
186			},
187			None => {
188				return Err(LightningError {
189					err: format!(
190						"Received get_info error response from an unknown counterparty {}",
191						counterparty_node_id
192					),
193					action: ErrorAction::IgnoreAndLog(Level::Debug),
194				});
195			},
196		}
197	}
198
199	/// Places an order with the connected LSP given its `counterparty_node_id`.
200	///
201	/// The client agrees to paying channel fees according to the provided parameters.
202	pub fn create_order(
203		&self, counterparty_node_id: &PublicKey, order: LSPS1OrderParams,
204		refund_onchain_address: Option<Address>,
205	) -> LSPSRequestId {
206		let mut message_queue_notifier = self.pending_messages.notifier();
207
208		let mut outer_state_lock = self.per_peer_state.write().unwrap();
209		let inner_state_lock = outer_state_lock
210			.entry(*counterparty_node_id)
211			.or_insert(Mutex::new(PeerState::default()));
212		let mut peer_state_lock = inner_state_lock.lock().unwrap();
213
214		let request_id = crate::utils::generate_request_id(&self.entropy_source);
215		let request =
216			LSPS1Request::CreateOrder(LSPS1CreateOrderRequest { order, refund_onchain_address });
217		let msg = LSPS1Message::Request(request_id.clone(), request).into();
218		peer_state_lock.pending_create_order_requests.insert(request_id.clone());
219
220		message_queue_notifier.enqueue(&counterparty_node_id, msg);
221
222		request_id
223	}
224
225	fn handle_create_order_response(
226		&self, request_id: LSPSRequestId, counterparty_node_id: &PublicKey,
227		response: LSPS1CreateOrderResponse,
228	) -> Result<(), LightningError> {
229		let event_queue_notifier = self.pending_events.notifier();
230
231		let outer_state_lock = self.per_peer_state.read().unwrap();
232		match outer_state_lock.get(counterparty_node_id) {
233			Some(inner_state_lock) => {
234				let mut peer_state_lock = inner_state_lock.lock().unwrap();
235
236				if !peer_state_lock.pending_create_order_requests.remove(&request_id) {
237					return Err(LightningError {
238						err: format!(
239							"Received create_order response for an unknown request: {:?}",
240							request_id
241						),
242						action: ErrorAction::IgnoreAndLog(Level::Debug),
243					});
244				}
245
246				event_queue_notifier.enqueue(LSPS1ClientEvent::OrderCreated {
247					request_id,
248					counterparty_node_id: *counterparty_node_id,
249					order_id: response.order_id,
250					order: response.order,
251					payment: response.payment,
252					channel: response.channel,
253				});
254			},
255			None => {
256				return Err(LightningError {
257					err: format!(
258						"Received create_order response from unknown peer: {}",
259						counterparty_node_id
260					),
261					action: ErrorAction::IgnoreAndLog(Level::Debug),
262				})
263			},
264		}
265
266		Ok(())
267	}
268
269	fn handle_create_order_error(
270		&self, request_id: LSPSRequestId, counterparty_node_id: &PublicKey,
271		error: LSPSResponseError,
272	) -> Result<(), LightningError> {
273		let event_queue_notifier = self.pending_events.notifier();
274
275		let outer_state_lock = self.per_peer_state.read().unwrap();
276		match outer_state_lock.get(counterparty_node_id) {
277			Some(inner_state_lock) => {
278				let mut peer_state_lock = inner_state_lock.lock().unwrap();
279
280				if !peer_state_lock.pending_create_order_requests.remove(&request_id) {
281					return Err(LightningError {
282						err: format!(
283							"Received create order error for an unknown request: {:?}",
284							request_id
285						),
286						action: ErrorAction::IgnoreAndLog(Level::Debug),
287					});
288				}
289
290				event_queue_notifier.enqueue(LSPS1ClientEvent::OrderRequestFailed {
291					request_id: request_id.clone(),
292					counterparty_node_id: *counterparty_node_id,
293					error: error.clone(),
294				});
295
296				Err(LightningError {
297					err: format!(
298						"Received create_order error response for request {:?}: {:?}",
299						request_id, error
300					),
301					action: ErrorAction::IgnoreAndLog(Level::Error),
302				})
303			},
304			None => {
305				return Err(LightningError {
306					err: format!(
307						"Received error response for a create order request from an unknown counterparty {}",
308						counterparty_node_id
309					),
310					action: ErrorAction::IgnoreAndLog(Level::Debug),
311				});
312			},
313		}
314	}
315
316	/// Queries the status of a pending payment, i.e., whether a payment has been received by the LSP.
317	///
318	/// Upon success an [`LSPS1ClientEvent::OrderStatus`] event will be emitted.
319	///
320	/// [`LSPS1ClientEvent::OrderStatus`]: crate::lsps1::event::LSPS1ClientEvent::OrderStatus
321	pub fn check_order_status(
322		&self, counterparty_node_id: &PublicKey, order_id: LSPS1OrderId,
323	) -> LSPSRequestId {
324		let mut message_queue_notifier = self.pending_messages.notifier();
325
326		let mut outer_state_lock = self.per_peer_state.write().unwrap();
327		let inner_state_lock = outer_state_lock
328			.entry(*counterparty_node_id)
329			.or_insert(Mutex::new(PeerState::default()));
330		let mut peer_state_lock = inner_state_lock.lock().unwrap();
331
332		let request_id = crate::utils::generate_request_id(&self.entropy_source);
333		peer_state_lock.pending_get_order_requests.insert(request_id.clone());
334
335		let request = LSPS1Request::GetOrder(LSPS1GetOrderRequest { order_id: order_id.clone() });
336		let msg = LSPS1Message::Request(request_id.clone(), request).into();
337
338		message_queue_notifier.enqueue(&counterparty_node_id, msg);
339
340		request_id
341	}
342
343	fn handle_get_order_response(
344		&self, request_id: LSPSRequestId, counterparty_node_id: &PublicKey,
345		response: LSPS1CreateOrderResponse,
346	) -> Result<(), LightningError> {
347		let event_queue_notifier = self.pending_events.notifier();
348
349		let outer_state_lock = self.per_peer_state.read().unwrap();
350		match outer_state_lock.get(counterparty_node_id) {
351			Some(inner_state_lock) => {
352				let mut peer_state_lock = inner_state_lock.lock().unwrap();
353
354				if !peer_state_lock.pending_get_order_requests.remove(&request_id) {
355					return Err(LightningError {
356						err: format!(
357							"Received get_order response for an unknown request: {:?}",
358							request_id
359						),
360						action: ErrorAction::IgnoreAndLog(Level::Debug),
361					});
362				}
363
364				event_queue_notifier.enqueue(LSPS1ClientEvent::OrderStatus {
365					request_id,
366					counterparty_node_id: *counterparty_node_id,
367					order_id: response.order_id,
368					order: response.order,
369					payment: response.payment,
370					channel: response.channel,
371				});
372			},
373			None => {
374				return Err(LightningError {
375					err: format!(
376						"Received get_order response from unknown peer: {}",
377						counterparty_node_id
378					),
379					action: ErrorAction::IgnoreAndLog(Level::Debug),
380				})
381			},
382		}
383
384		Ok(())
385	}
386
387	fn handle_get_order_error(
388		&self, request_id: LSPSRequestId, counterparty_node_id: &PublicKey,
389		error: LSPSResponseError,
390	) -> Result<(), LightningError> {
391		let event_queue_notifier = self.pending_events.notifier();
392
393		let outer_state_lock = self.per_peer_state.read().unwrap();
394		match outer_state_lock.get(counterparty_node_id) {
395			Some(inner_state_lock) => {
396				let mut peer_state_lock = inner_state_lock.lock().unwrap();
397
398				if !peer_state_lock.pending_get_order_requests.remove(&request_id) {
399					return Err(LightningError {
400						err: format!(
401							"Received get order error for an unknown request: {:?}",
402							request_id
403						),
404						action: ErrorAction::IgnoreAndLog(Level::Debug),
405					});
406				}
407
408				event_queue_notifier.enqueue(LSPS1ClientEvent::OrderRequestFailed {
409					request_id: request_id.clone(),
410					counterparty_node_id: *counterparty_node_id,
411					error: error.clone(),
412				});
413
414				Err(LightningError {
415					err: format!(
416						"Received get_order error response for request {:?}: {:?}",
417						request_id, error
418					),
419					action: ErrorAction::IgnoreAndLog(Level::Error),
420				})
421			},
422			None => {
423				return Err(LightningError {
424					err: format!(
425						"Received error response for a get order request from an unknown counterparty ({:?})",
426						counterparty_node_id
427					),
428					action: ErrorAction::IgnoreAndLog(Level::Debug),
429				});
430			},
431		}
432	}
433}
434
435impl<ES: Deref, K: Deref + Clone> LSPSProtocolMessageHandler for LSPS1ClientHandler<ES, K>
436where
437	ES::Target: EntropySource,
438	K::Target: KVStore,
439{
440	type ProtocolMessage = LSPS1Message;
441	const PROTOCOL_NUMBER: Option<u16> = Some(1);
442
443	fn handle_message(
444		&self, message: Self::ProtocolMessage, counterparty_node_id: &PublicKey,
445	) -> Result<(), LightningError> {
446		match message {
447			LSPS1Message::Response(request_id, response) => match response {
448				LSPS1Response::GetInfo(params) => {
449					self.handle_get_info_response(request_id, counterparty_node_id, params)
450				},
451				LSPS1Response::GetInfoError(error) => {
452					self.handle_get_info_error(request_id, counterparty_node_id, error)
453				},
454				LSPS1Response::CreateOrder(params) => {
455					self.handle_create_order_response(request_id, counterparty_node_id, params)
456				},
457				LSPS1Response::CreateOrderError(error) => {
458					self.handle_create_order_error(request_id, counterparty_node_id, error)
459				},
460				LSPS1Response::GetOrder(params) => {
461					self.handle_get_order_response(request_id, counterparty_node_id, params)
462				},
463				LSPS1Response::GetOrderError(error) => {
464					self.handle_get_order_error(request_id, counterparty_node_id, error)
465				},
466			},
467			_ => {
468				debug_assert!(
469					false,
470					"Client handler received LSPS1 request message. This should never happen."
471				);
472				Err(LightningError {
473					err: format!(
474						"Client handler received LSPS1 request message from node {:?}. This should never happen.",
475						counterparty_node_id
476					),
477					action: ErrorAction::IgnoreAndLog(Level::Error),
478				})
479			},
480		}
481	}
482}