lightning_liquidity/lsps2/
client.rs1use crate::events::{Event, EventQueue};
12use crate::lsps0::ser::{ProtocolMessageHandler, RequestId, ResponseError};
13use crate::lsps2::event::LSPS2ClientEvent;
14use crate::message_queue::MessageQueue;
15use crate::prelude::{new_hash_map, new_hash_set, HashMap, HashSet, String, ToString};
16use crate::sync::{Arc, Mutex, RwLock};
17
18use lightning::ln::msgs::{ErrorAction, LightningError};
19use lightning::sign::EntropySource;
20use lightning::util::errors::APIError;
21use lightning::util::logger::Level;
22
23use bitcoin::secp256k1::PublicKey;
24
25use core::default::Default;
26use core::ops::Deref;
27
28use crate::lsps2::msgs::{
29 BuyRequest, BuyResponse, GetInfoRequest, GetInfoResponse, LSPS2Message, LSPS2Request,
30 LSPS2Response, OpeningFeeParams,
31};
32
33#[derive(Clone, Debug, Copy, Default)]
35pub struct LSPS2ClientConfig {}
36
37struct InboundJITChannel {
38 payment_size_msat: Option<u64>,
39}
40
41impl InboundJITChannel {
42 fn new(payment_size_msat: Option<u64>) -> Self {
43 Self { payment_size_msat }
44 }
45}
46
47struct PeerState {
48 pending_get_info_requests: HashSet<RequestId>,
49 pending_buy_requests: HashMap<RequestId, InboundJITChannel>,
50}
51
52impl PeerState {
53 fn new() -> Self {
54 let pending_get_info_requests = new_hash_set();
55 let pending_buy_requests = new_hash_map();
56 Self { pending_get_info_requests, pending_buy_requests }
57 }
58}
59
60pub struct LSPS2ClientHandler<ES: Deref>
68where
69 ES::Target: EntropySource,
70{
71 entropy_source: ES,
72 pending_messages: Arc<MessageQueue>,
73 pending_events: Arc<EventQueue>,
74 per_peer_state: RwLock<HashMap<PublicKey, Mutex<PeerState>>>,
75 _config: LSPS2ClientConfig,
76}
77
78impl<ES: Deref> LSPS2ClientHandler<ES>
79where
80 ES::Target: EntropySource,
81{
82 pub(crate) fn new(
84 entropy_source: ES, pending_messages: Arc<MessageQueue>, pending_events: Arc<EventQueue>,
85 _config: LSPS2ClientConfig,
86 ) -> Self {
87 Self {
88 entropy_source,
89 pending_messages,
90 pending_events,
91 per_peer_state: RwLock::new(new_hash_map()),
92 _config,
93 }
94 }
95
96 pub fn request_opening_params(
112 &self, counterparty_node_id: PublicKey, token: Option<String>,
113 ) -> RequestId {
114 let request_id = crate::utils::generate_request_id(&self.entropy_source);
115
116 {
117 let mut outer_state_lock = self.per_peer_state.write().unwrap();
118 let inner_state_lock = outer_state_lock
119 .entry(counterparty_node_id)
120 .or_insert(Mutex::new(PeerState::new()));
121 let mut peer_state_lock = inner_state_lock.lock().unwrap();
122 peer_state_lock.pending_get_info_requests.insert(request_id.clone());
123 }
124
125 let request = LSPS2Request::GetInfo(GetInfoRequest { token });
126 let msg = LSPS2Message::Request(request_id.clone(), request).into();
127 self.pending_messages.enqueue(&counterparty_node_id, msg);
128
129 request_id
130 }
131
132 pub fn select_opening_params(
151 &self, counterparty_node_id: PublicKey, payment_size_msat: Option<u64>,
152 opening_fee_params: OpeningFeeParams,
153 ) -> Result<RequestId, APIError> {
154 let request_id = crate::utils::generate_request_id(&self.entropy_source);
155
156 {
157 let mut outer_state_lock = self.per_peer_state.write().unwrap();
158 let inner_state_lock = outer_state_lock
159 .entry(counterparty_node_id)
160 .or_insert(Mutex::new(PeerState::new()));
161 let mut peer_state_lock = inner_state_lock.lock().unwrap();
162
163 let jit_channel = InboundJITChannel::new(payment_size_msat);
164 if peer_state_lock
165 .pending_buy_requests
166 .insert(request_id.clone(), jit_channel)
167 .is_some()
168 {
169 return Err(APIError::APIMisuseError {
170 err: "Failed due to duplicate request_id. This should never happen!"
171 .to_string(),
172 });
173 }
174 }
175
176 let request = LSPS2Request::Buy(BuyRequest { opening_fee_params, payment_size_msat });
177 let msg = LSPS2Message::Request(request_id.clone(), request).into();
178 self.pending_messages.enqueue(&counterparty_node_id, msg);
179
180 Ok(request_id)
181 }
182
183 fn handle_get_info_response(
184 &self, request_id: RequestId, counterparty_node_id: &PublicKey, result: GetInfoResponse,
185 ) -> Result<(), LightningError> {
186 let outer_state_lock = self.per_peer_state.read().unwrap();
187 match outer_state_lock.get(counterparty_node_id) {
188 Some(inner_state_lock) => {
189 let mut peer_state = inner_state_lock.lock().unwrap();
190
191 if !peer_state.pending_get_info_requests.remove(&request_id) {
192 return Err(LightningError {
193 err: format!(
194 "Received get_info response for an unknown request: {:?}",
195 request_id
196 ),
197 action: ErrorAction::IgnoreAndLog(Level::Info),
198 });
199 }
200
201 self.pending_events.enqueue(Event::LSPS2Client(
202 LSPS2ClientEvent::OpeningParametersReady {
203 request_id,
204 counterparty_node_id: *counterparty_node_id,
205 opening_fee_params_menu: result.opening_fee_params_menu,
206 },
207 ));
208 },
209 None => {
210 return Err(LightningError {
211 err: format!(
212 "Received get_info response from unknown peer: {:?}",
213 counterparty_node_id
214 ),
215 action: ErrorAction::IgnoreAndLog(Level::Info),
216 })
217 },
218 }
219
220 Ok(())
221 }
222
223 fn handle_get_info_error(
224 &self, request_id: RequestId, counterparty_node_id: &PublicKey, _error: ResponseError,
225 ) -> Result<(), LightningError> {
226 let outer_state_lock = self.per_peer_state.read().unwrap();
227 match outer_state_lock.get(counterparty_node_id) {
228 Some(inner_state_lock) => {
229 let mut peer_state = inner_state_lock.lock().unwrap();
230
231 if !peer_state.pending_get_info_requests.remove(&request_id) {
232 return Err(LightningError {
233 err: format!(
234 "Received get_info error for an unknown request: {:?}",
235 request_id
236 ),
237 action: ErrorAction::IgnoreAndLog(Level::Info),
238 });
239 }
240
241 Ok(())
242 },
243 None => {
244 return Err(LightningError { err: format!("Received error response for a get_info request from an unknown counterparty ({:?})",counterparty_node_id), action: ErrorAction::IgnoreAndLog(Level::Info)});
245 },
246 }
247 }
248
249 fn handle_buy_response(
250 &self, request_id: RequestId, counterparty_node_id: &PublicKey, result: BuyResponse,
251 ) -> Result<(), LightningError> {
252 let outer_state_lock = self.per_peer_state.read().unwrap();
253 match outer_state_lock.get(counterparty_node_id) {
254 Some(inner_state_lock) => {
255 let mut peer_state = inner_state_lock.lock().unwrap();
256
257 let jit_channel =
258 peer_state.pending_buy_requests.remove(&request_id).ok_or(LightningError {
259 err: format!(
260 "Received buy response for an unknown request: {:?}",
261 request_id
262 ),
263 action: ErrorAction::IgnoreAndLog(Level::Info),
264 })?;
265
266 if let Ok(intercept_scid) = result.jit_channel_scid.to_scid() {
267 self.pending_events.enqueue(Event::LSPS2Client(
268 LSPS2ClientEvent::InvoiceParametersReady {
269 request_id,
270 counterparty_node_id: *counterparty_node_id,
271 intercept_scid,
272 cltv_expiry_delta: result.lsp_cltv_expiry_delta,
273 payment_size_msat: jit_channel.payment_size_msat,
274 },
275 ));
276 } else {
277 return Err(LightningError {
278 err: format!(
279 "Received buy response with an invalid intercept scid {:?}",
280 result.jit_channel_scid
281 ),
282 action: ErrorAction::IgnoreAndLog(Level::Info),
283 });
284 }
285 },
286 None => {
287 return Err(LightningError {
288 err: format!(
289 "Received buy response from unknown peer: {:?}",
290 counterparty_node_id
291 ),
292 action: ErrorAction::IgnoreAndLog(Level::Info),
293 });
294 },
295 }
296 Ok(())
297 }
298
299 fn handle_buy_error(
300 &self, request_id: RequestId, counterparty_node_id: &PublicKey, _error: ResponseError,
301 ) -> Result<(), LightningError> {
302 let outer_state_lock = self.per_peer_state.read().unwrap();
303 match outer_state_lock.get(counterparty_node_id) {
304 Some(inner_state_lock) => {
305 let mut peer_state = inner_state_lock.lock().unwrap();
306
307 peer_state.pending_buy_requests.remove(&request_id).ok_or(LightningError {
308 err: format!("Received buy error for an unknown request: {:?}", request_id),
309 action: ErrorAction::IgnoreAndLog(Level::Info),
310 })?;
311
312 Ok(())
313 },
314 None => {
315 return Err(LightningError { err: format!("Received error response for a buy request from an unknown counterparty ({:?})", counterparty_node_id), action: ErrorAction::IgnoreAndLog(Level::Info)});
316 },
317 }
318 }
319}
320
321impl<ES: Deref> ProtocolMessageHandler for LSPS2ClientHandler<ES>
322where
323 ES::Target: EntropySource,
324{
325 type ProtocolMessage = LSPS2Message;
326 const PROTOCOL_NUMBER: Option<u16> = Some(2);
327
328 fn handle_message(
329 &self, message: Self::ProtocolMessage, counterparty_node_id: &PublicKey,
330 ) -> Result<(), LightningError> {
331 match message {
332 LSPS2Message::Response(request_id, response) => match response {
333 LSPS2Response::GetInfo(result) => {
334 self.handle_get_info_response(request_id, counterparty_node_id, result)
335 },
336 LSPS2Response::GetInfoError(error) => {
337 self.handle_get_info_error(request_id, counterparty_node_id, error)
338 },
339 LSPS2Response::Buy(result) => {
340 self.handle_buy_response(request_id, counterparty_node_id, result)
341 },
342 LSPS2Response::BuyError(error) => {
343 self.handle_buy_error(request_id, counterparty_node_id, error)
344 },
345 },
346 _ => {
347 debug_assert!(
348 false,
349 "Client handler received LSPS2 request message. This should never happen."
350 );
351 Err(LightningError { err: format!("Client handler received LSPS2 request message from node {:?}. This should never happen.", counterparty_node_id), action: ErrorAction::IgnoreAndLog(Level::Info)})
352 },
353 }
354 }
355}
356
357#[cfg(test)]
358mod tests {}