lightning_liquidity/lsps0/
client.rs

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