lightning_liquidity/lsps0/
client.rs

1//! Contains the main bLIP-50 / LSPS0 client-side object, [`LSPS0ClientHandler`].
2//!
3//! Please refer to the [bLIP-50 / LSPS0
4//! specifcation](https://github.com/lightning/blips/blob/master/blip-0050.md) for more
5//! information.
6
7use crate::events::EventQueue;
8use crate::lsps0::event::LSPS0ClientEvent;
9use crate::lsps0::msgs::{
10	LSPS0ListProtocolsRequest, LSPS0ListProtocolsResponse, LSPS0Message, LSPS0Request,
11	LSPS0Response,
12};
13use crate::lsps0::ser::{LSPSProtocolMessageHandler, LSPSResponseError};
14use crate::message_queue::MessageQueue;
15use crate::sync::Arc;
16use crate::utils;
17
18use lightning::ln::msgs::{ErrorAction, LightningError};
19use lightning::sign::EntropySource;
20use lightning::util::logger::Level;
21use lightning::util::persist::KVStore;
22
23use bitcoin::secp256k1::PublicKey;
24
25use core::ops::Deref;
26
27/// A message handler capable of sending and handling bLIP-50 / LSPS0 messages.
28pub struct LSPS0ClientHandler<ES: Deref, K: Deref + Clone>
29where
30	ES::Target: EntropySource,
31	K::Target: KVStore,
32{
33	entropy_source: ES,
34	pending_messages: Arc<MessageQueue>,
35	pending_events: Arc<EventQueue<K>>,
36}
37
38impl<ES: Deref, K: Deref + Clone> LSPS0ClientHandler<ES, K>
39where
40	ES::Target: EntropySource,
41	K::Target: KVStore,
42{
43	/// Returns a new instance of [`LSPS0ClientHandler`].
44	pub(crate) fn new(
45		entropy_source: ES, pending_messages: Arc<MessageQueue>, pending_events: Arc<EventQueue<K>>,
46	) -> Self {
47		Self { entropy_source, pending_messages, pending_events }
48	}
49
50	/// Calls bLIP-50 / LSPS0's `list_protocols`.
51	///
52	/// Please refer to the [bLIP-50 / LSPS0
53	/// specifcation](https://github.com/lightning/blips/blob/master/blip-0050.md#lsps-specification-support-query)
54	/// for more information.
55	pub fn list_protocols(&self, counterparty_node_id: &PublicKey) {
56		let mut message_queue_notifier = self.pending_messages.notifier();
57
58		let msg = LSPS0Message::Request(
59			utils::generate_request_id(&self.entropy_source),
60			LSPS0Request::ListProtocols(LSPS0ListProtocolsRequest {}),
61		);
62
63		message_queue_notifier.enqueue(counterparty_node_id, msg.into());
64	}
65
66	fn handle_response(
67		&self, response: LSPS0Response, counterparty_node_id: &PublicKey,
68	) -> Result<(), LightningError> {
69		let event_queue_notifier = self.pending_events.notifier();
70
71		match response {
72			LSPS0Response::ListProtocols(LSPS0ListProtocolsResponse { protocols }) => {
73				event_queue_notifier.enqueue(LSPS0ClientEvent::ListProtocolsResponse {
74					counterparty_node_id: *counterparty_node_id,
75					protocols,
76				});
77				Ok(())
78			},
79			LSPS0Response::ListProtocolsError(LSPSResponseError {
80				code, message, data, ..
81			}) => Err(LightningError {
82				err: format!(
83					"ListProtocols error received. code = {}, message = {}, data = {:?}",
84					code, message, data
85				),
86				action: ErrorAction::IgnoreAndLog(Level::Info),
87			}),
88		}
89	}
90}
91
92impl<ES: Deref, K: Deref + Clone> LSPSProtocolMessageHandler for LSPS0ClientHandler<ES, K>
93where
94	ES::Target: EntropySource,
95	K::Target: KVStore,
96{
97	type ProtocolMessage = LSPS0Message;
98	const PROTOCOL_NUMBER: Option<u16> = None;
99
100	fn handle_message(
101		&self, message: Self::ProtocolMessage, counterparty_node_id: &PublicKey,
102	) -> Result<(), LightningError> {
103		match message {
104			LSPS0Message::Response(_, response) => {
105				self.handle_response(response, counterparty_node_id)
106			},
107			LSPS0Message::Request(..) => {
108				debug_assert!(
109					false,
110					"Client handler received LSPS0 request message. This should never happen."
111				);
112				Err(LightningError { err: format!("Client handler received LSPS0 request message from node {}. This should never happen.", counterparty_node_id), action: ErrorAction::IgnoreAndLog(Level::Info)})
113			},
114		}
115	}
116}
117
118#[cfg(test)]
119mod tests {
120	use alloc::collections::VecDeque;
121	use alloc::string::ToString;
122	use alloc::sync::Arc;
123
124	use lightning::util::persist::KVStoreSyncWrapper;
125	use lightning::util::test_utils::TestStore;
126	use lightning::util::wakers::Notifier;
127
128	use crate::lsps0::ser::{LSPSMessage, LSPSRequestId};
129	use crate::tests::utils::{self, TestEntropy};
130
131	use super::*;
132
133	#[test]
134	fn test_list_protocols() {
135		let notifier = Arc::new(Notifier::new());
136		let pending_messages = Arc::new(MessageQueue::new(notifier));
137		let entropy_source = Arc::new(TestEntropy {});
138		let kv_store = Arc::new(KVStoreSyncWrapper(Arc::new(TestStore::new(false))));
139		let persist_notifier = Arc::new(Notifier::new());
140		let event_queue = Arc::new(EventQueue::new(VecDeque::new(), kv_store, persist_notifier));
141
142		let lsps0_handler = Arc::new(LSPS0ClientHandler::new(
143			entropy_source,
144			Arc::clone(&pending_messages),
145			event_queue,
146		));
147
148		let counterparty_node_id = utils::parse_pubkey(
149			"027100442c3b79f606f80f322d98d499eefcb060599efc5d4ecb00209c2cb54190",
150		)
151		.unwrap();
152
153		lsps0_handler.list_protocols(&counterparty_node_id);
154		let pending_messages = pending_messages.get_and_clear_pending_msgs();
155
156		assert_eq!(pending_messages.len(), 1);
157
158		let (pubkey, message) = &pending_messages[0];
159
160		assert_eq!(*pubkey, counterparty_node_id);
161		assert_eq!(
162			*message,
163			LSPSMessage::LSPS0(LSPS0Message::Request(
164				LSPSRequestId("00000000000000000000000000000000".to_string()),
165				LSPS0Request::ListProtocols(LSPS0ListProtocolsRequest {})
166			))
167		);
168	}
169}