1use alloc::string::String;
13use alloc::vec::Vec;
14
15use core::convert::TryFrom;
16
17use bitcoin::hashes::hmac::{Hmac, HmacEngine};
18use bitcoin::hashes::sha256::Hash as Sha256;
19use bitcoin::hashes::{Hash, HashEngine};
20use bitcoin::secp256k1::PublicKey;
21
22use serde::{Deserialize, Serialize};
23
24use lightning::impl_writeable_tlv_based;
25use lightning::util::scid_utils;
26
27use crate::lsps0::ser::{
28 string_amount, string_amount_option, LSPSDateTime, LSPSMessage, LSPSRequestId,
29 LSPSResponseError,
30};
31use crate::utils;
32
33pub(crate) const LSPS2_GET_INFO_METHOD_NAME: &str = "lsps2.get_info";
34pub(crate) const LSPS2_BUY_METHOD_NAME: &str = "lsps2.buy";
35
36pub(crate) const LSPS2_GET_INFO_REQUEST_UNRECOGNIZED_OR_STALE_TOKEN_ERROR_CODE: i32 = 200;
37
38pub(crate) const LSPS2_BUY_REQUEST_INVALID_OPENING_FEE_PARAMS_ERROR_CODE: i32 = 201;
39pub(crate) const LSPS2_BUY_REQUEST_PAYMENT_SIZE_TOO_SMALL_ERROR_CODE: i32 = 202;
40pub(crate) const LSPS2_BUY_REQUEST_PAYMENT_SIZE_TOO_LARGE_ERROR_CODE: i32 = 203;
41
42#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
43pub struct LSPS2GetInfoRequest {
45 pub token: Option<String>,
47}
48
49pub struct LSPS2RawOpeningFeeParams {
54 pub min_fee_msat: u64,
56 pub proportional: u32,
58 pub valid_until: LSPSDateTime,
60 pub min_lifetime: u32,
62 pub max_client_to_self_delay: u32,
64 pub min_payment_size_msat: u64,
66 pub max_payment_size_msat: u64,
68}
69
70impl LSPS2RawOpeningFeeParams {
71 pub(crate) fn into_opening_fee_params(
72 self, promise_secret: &[u8; 32], counterparty_node_id: &PublicKey,
73 ) -> LSPS2OpeningFeeParams {
74 let mut hmac = HmacEngine::<Sha256>::new(promise_secret);
75 hmac.input(&counterparty_node_id.serialize());
76 hmac.input(&self.min_fee_msat.to_be_bytes());
77 hmac.input(&self.proportional.to_be_bytes());
78 hmac.input(self.valid_until.to_rfc3339().as_bytes());
79 hmac.input(&self.min_lifetime.to_be_bytes());
80 hmac.input(&self.max_client_to_self_delay.to_be_bytes());
81 hmac.input(&self.min_payment_size_msat.to_be_bytes());
82 hmac.input(&self.max_payment_size_msat.to_be_bytes());
83 let promise_bytes = Hmac::from_engine(hmac).to_byte_array();
84 let promise = utils::hex_str(&promise_bytes[..]);
85 LSPS2OpeningFeeParams {
86 min_fee_msat: self.min_fee_msat,
87 proportional: self.proportional,
88 valid_until: self.valid_until,
89 min_lifetime: self.min_lifetime,
90 max_client_to_self_delay: self.max_client_to_self_delay,
91 min_payment_size_msat: self.min_payment_size_msat,
92 max_payment_size_msat: self.max_payment_size_msat,
93 promise,
94 }
95 }
96}
97
98#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
99pub struct LSPS2OpeningFeeParams {
105 #[serde(with = "string_amount")]
107 pub min_fee_msat: u64,
108 pub proportional: u32,
110 pub valid_until: LSPSDateTime,
112 pub min_lifetime: u32,
114 pub max_client_to_self_delay: u32,
116 #[serde(with = "string_amount")]
118 pub min_payment_size_msat: u64,
119 #[serde(with = "string_amount")]
121 pub max_payment_size_msat: u64,
122 pub promise: String,
124}
125
126impl_writeable_tlv_based!(LSPS2OpeningFeeParams, {
127 (0, min_fee_msat, required),
128 (2, proportional, required),
129 (4, valid_until, required),
130 (6, min_lifetime, required),
131 (8, max_client_to_self_delay, required),
132 (10, min_payment_size_msat, required),
133 (12, max_payment_size_msat, required),
134 (14, promise, required),
135});
136
137#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
139pub struct LSPS2GetInfoResponse {
140 pub opening_fee_params_menu: Vec<LSPS2OpeningFeeParams>,
142}
143
144#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
146pub struct LSPS2BuyRequest {
147 pub opening_fee_params: LSPS2OpeningFeeParams,
149 #[serde(default)]
151 #[serde(skip_serializing_if = "Option::is_none")]
152 #[serde(with = "string_amount_option")]
153 pub payment_size_msat: Option<u64>,
154}
155
156#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
158pub struct LSPS2InterceptScid(String);
159
160impl From<u64> for LSPS2InterceptScid {
161 fn from(scid: u64) -> Self {
162 let block = scid_utils::block_from_scid(scid);
163 let tx_index = scid_utils::tx_index_from_scid(scid);
164 let vout = scid_utils::vout_from_scid(scid);
165
166 Self(format!("{}x{}x{}", block, tx_index, vout))
167 }
168}
169
170impl LSPS2InterceptScid {
171 pub fn to_scid(&self) -> Result<u64, ()> {
173 utils::scid_from_human_readable_string(&self.0)
174 }
175}
176
177#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
181pub struct LSPS2BuyResponse {
182 pub jit_channel_scid: LSPS2InterceptScid,
184 pub lsp_cltv_expiry_delta: u32,
186 #[serde(default)]
195 pub client_trusts_lsp: bool,
196}
197
198#[derive(Clone, Debug, PartialEq, Eq)]
199pub enum LSPS2Request {
201 GetInfo(LSPS2GetInfoRequest),
203 Buy(LSPS2BuyRequest),
205}
206
207#[derive(Clone, Debug, PartialEq, Eq)]
208pub enum LSPS2Response {
210 GetInfo(LSPS2GetInfoResponse),
212 GetInfoError(LSPSResponseError),
214 Buy(LSPS2BuyResponse),
216 BuyError(LSPSResponseError),
218}
219
220#[derive(Clone, Debug, PartialEq, Eq)]
221pub enum LSPS2Message {
223 Request(LSPSRequestId, LSPS2Request),
225 Response(LSPSRequestId, LSPS2Response),
227}
228
229impl TryFrom<LSPSMessage> for LSPS2Message {
230 type Error = ();
231
232 fn try_from(message: LSPSMessage) -> Result<Self, Self::Error> {
233 if let LSPSMessage::LSPS2(message) = message {
234 return Ok(message);
235 }
236
237 Err(())
238 }
239}
240
241impl From<LSPS2Message> for LSPSMessage {
242 fn from(message: LSPS2Message) -> Self {
243 LSPSMessage::LSPS2(message)
244 }
245}
246
247#[cfg(test)]
248mod tests {
249 use super::*;
250
251 use crate::alloc::string::ToString;
252 use crate::lsps2::utils::is_valid_opening_fee_params;
253
254 use bitcoin::secp256k1::{Secp256k1, SecretKey};
255
256 use core::str::FromStr;
257
258 #[test]
259 fn into_opening_fee_params_produces_valid_promise() {
260 let min_fee_msat = 100;
261 let proportional = 21;
262 let valid_until = LSPSDateTime::from_str("2035-05-20T08:30:45Z").unwrap();
263 let min_lifetime = 144;
264 let max_client_to_self_delay = 128;
265 let min_payment_size_msat = 1;
266 let max_payment_size_msat = 100_000_000;
267
268 let raw = LSPS2RawOpeningFeeParams {
269 min_fee_msat,
270 proportional,
271 valid_until: valid_until.into(),
272 min_lifetime,
273 max_client_to_self_delay,
274 min_payment_size_msat,
275 max_payment_size_msat,
276 };
277
278 let promise_secret = [1u8; 32];
279 let client_node_id = PublicKey::from_secret_key(
280 &Secp256k1::new(),
281 &SecretKey::from_slice(&[0xcd; 32]).unwrap(),
282 );
283
284 let opening_fee_params = raw.into_opening_fee_params(&promise_secret, &client_node_id);
285
286 assert_eq!(opening_fee_params.min_fee_msat, min_fee_msat);
287 assert_eq!(opening_fee_params.proportional, proportional);
288 assert_eq!(opening_fee_params.valid_until, valid_until);
289 assert_eq!(opening_fee_params.min_lifetime, min_lifetime);
290 assert_eq!(opening_fee_params.max_client_to_self_delay, max_client_to_self_delay);
291 assert_eq!(opening_fee_params.min_payment_size_msat, min_payment_size_msat);
292 assert_eq!(opening_fee_params.max_payment_size_msat, max_payment_size_msat);
293
294 assert!(is_valid_opening_fee_params(&opening_fee_params, &promise_secret, &client_node_id));
295 }
296
297 #[test]
298 fn changing_single_field_produced_invalid_params() {
299 let min_fee_msat = 100;
300 let proportional = 21;
301 let valid_until = LSPSDateTime::from_str("2035-05-20T08:30:45Z").unwrap();
302 let min_lifetime = 144;
303 let max_client_to_self_delay = 128;
304 let min_payment_size_msat = 1;
305 let max_payment_size_msat = 100_000_000;
306
307 let raw = LSPS2RawOpeningFeeParams {
308 min_fee_msat,
309 proportional,
310 valid_until,
311 min_lifetime,
312 max_client_to_self_delay,
313 min_payment_size_msat,
314 max_payment_size_msat,
315 };
316
317 let promise_secret = [1u8; 32];
318 let client_node_id = PublicKey::from_secret_key(
319 &Secp256k1::new(),
320 &SecretKey::from_slice(&[0xcd; 32]).unwrap(),
321 );
322
323 let mut opening_fee_params = raw.into_opening_fee_params(&promise_secret, &client_node_id);
324 opening_fee_params.min_fee_msat = min_fee_msat + 1;
325 assert!(!is_valid_opening_fee_params(
326 &opening_fee_params,
327 &promise_secret,
328 &client_node_id
329 ));
330 }
331
332 #[test]
333 fn wrong_secret_produced_invalid_params() {
334 let min_fee_msat = 100;
335 let proportional = 21;
336 let valid_until = LSPSDateTime::from_str("2035-05-20T08:30:45Z").unwrap();
337 let min_lifetime = 144;
338 let max_client_to_self_delay = 128;
339 let min_payment_size_msat = 1;
340 let max_payment_size_msat = 100_000_000;
341
342 let raw = LSPS2RawOpeningFeeParams {
343 min_fee_msat,
344 proportional,
345 valid_until,
346 min_lifetime,
347 max_client_to_self_delay,
348 min_payment_size_msat,
349 max_payment_size_msat,
350 };
351
352 let promise_secret = [1u8; 32];
353 let other_secret = [2u8; 32];
354
355 let client_node_id = PublicKey::from_secret_key(
356 &Secp256k1::new(),
357 &SecretKey::from_slice(&[0xcd; 32]).unwrap(),
358 );
359
360 let opening_fee_params = raw.into_opening_fee_params(&promise_secret, &client_node_id);
361 assert!(!is_valid_opening_fee_params(&opening_fee_params, &other_secret, &client_node_id));
362 }
363
364 #[test]
365 fn client_mismatch_produced_invalid_params() {
366 let min_fee_msat = 100;
367 let proportional = 21;
368 let valid_until = LSPSDateTime::from_str("2035-05-20T08:30:45Z").unwrap();
369 let min_lifetime = 144;
370 let max_client_to_self_delay = 128;
371 let min_payment_size_msat = 1;
372 let max_payment_size_msat = 100_000_000;
373
374 let raw = LSPS2RawOpeningFeeParams {
375 min_fee_msat,
376 proportional,
377 valid_until,
378 min_lifetime,
379 max_client_to_self_delay,
380 min_payment_size_msat,
381 max_payment_size_msat,
382 };
383
384 let promise_secret = [1u8; 32];
385
386 let client_node_id = PublicKey::from_secret_key(
387 &Secp256k1::new(),
388 &SecretKey::from_slice(&[0xcd; 32]).unwrap(),
389 );
390
391 let other_public_key = PublicKey::from_secret_key(
392 &Secp256k1::new(),
393 &SecretKey::from_slice(&[0xcf; 32]).unwrap(),
394 );
395
396 let opening_fee_params = raw.into_opening_fee_params(&promise_secret, &client_node_id);
397 assert!(is_valid_opening_fee_params(&opening_fee_params, &promise_secret, &client_node_id));
398 assert!(!is_valid_opening_fee_params(
399 &opening_fee_params,
400 &promise_secret,
401 &other_public_key
402 ));
403 }
404
405 #[test]
406 #[cfg(feature = "std")]
407 fn expired_params_produces_invalid_params() {
409 let min_fee_msat = 100;
410 let proportional = 21;
411 let valid_until = LSPSDateTime::from_str("2023-05-20T08:30:45Z").unwrap();
412 let min_lifetime = 144;
413 let max_client_to_self_delay = 128;
414 let min_payment_size_msat = 1;
415 let max_payment_size_msat = 100_000_000;
416
417 let raw = LSPS2RawOpeningFeeParams {
418 min_fee_msat,
419 proportional,
420 valid_until,
421 min_lifetime,
422 max_client_to_self_delay,
423 min_payment_size_msat,
424 max_payment_size_msat,
425 };
426
427 let promise_secret = [1u8; 32];
428 let client_node_id = PublicKey::from_secret_key(
429 &Secp256k1::new(),
430 &SecretKey::from_slice(&[0xcd; 32]).unwrap(),
431 );
432
433 let opening_fee_params = raw.into_opening_fee_params(&promise_secret, &client_node_id);
434 assert!(!is_valid_opening_fee_params(
435 &opening_fee_params,
436 &promise_secret,
437 &client_node_id
438 ));
439 }
440
441 #[test]
442 fn buy_request_serialization() {
443 let min_fee_msat = 100;
444 let proportional = 21;
445 let valid_until = LSPSDateTime::from_str("2023-05-20T08:30:45Z").unwrap();
446 let min_lifetime = 144;
447 let max_client_to_self_delay = 128;
448 let min_payment_size_msat = 1;
449 let max_payment_size_msat = 100_000_000;
450
451 let raw = LSPS2RawOpeningFeeParams {
452 min_fee_msat,
453 proportional,
454 valid_until,
455 min_lifetime,
456 max_client_to_self_delay,
457 min_payment_size_msat,
458 max_payment_size_msat,
459 };
460
461 let promise_secret = [1u8; 32];
462 let client_node_id = PublicKey::from_secret_key(
463 &Secp256k1::new(),
464 &SecretKey::from_slice(&[0xcd; 32]).unwrap(),
465 );
466
467 let opening_fee_params = raw.into_opening_fee_params(&promise_secret, &client_node_id);
468 let json_str = r#"{"max_client_to_self_delay":128,"max_payment_size_msat":"100000000","min_fee_msat":"100","min_lifetime":144,"min_payment_size_msat":"1","promise":"75eb57db4c37dc092a37f1d2e0026c5ff36a7834a717ea97c41d91a8d5b50ce8","proportional":21,"valid_until":"2023-05-20T08:30:45Z"}"#;
469 assert_eq!(json_str, serde_json::json!(opening_fee_params).to_string());
470 assert_eq!(opening_fee_params, serde_json::from_str(json_str).unwrap());
471
472 let payment_size_msat = Some(1234);
473 let buy_request_fixed =
474 LSPS2BuyRequest { opening_fee_params: opening_fee_params.clone(), payment_size_msat };
475 let json_str = r#"{"opening_fee_params":{"max_client_to_self_delay":128,"max_payment_size_msat":"100000000","min_fee_msat":"100","min_lifetime":144,"min_payment_size_msat":"1","promise":"75eb57db4c37dc092a37f1d2e0026c5ff36a7834a717ea97c41d91a8d5b50ce8","proportional":21,"valid_until":"2023-05-20T08:30:45Z"},"payment_size_msat":"1234"}"#;
476 assert_eq!(json_str, serde_json::json!(buy_request_fixed).to_string());
477 assert_eq!(buy_request_fixed, serde_json::from_str(json_str).unwrap());
478
479 let payment_size_msat = None;
480 let buy_request_variable = LSPS2BuyRequest { opening_fee_params, payment_size_msat };
481
482 let json_str = r#"{"opening_fee_params":{"max_client_to_self_delay":128,"max_payment_size_msat":"100000000","min_fee_msat":"100","min_lifetime":144,"min_payment_size_msat":"1","promise":"75eb57db4c37dc092a37f1d2e0026c5ff36a7834a717ea97c41d91a8d5b50ce8","proportional":21,"valid_until":"2023-05-20T08:30:45Z"}}"#;
484 assert_eq!(json_str, serde_json::json!(buy_request_variable).to_string());
485 assert_eq!(buy_request_variable, serde_json::from_str(json_str).unwrap());
486
487 let json_str = r#"{"opening_fee_params":{"max_client_to_self_delay":128,"max_payment_size_msat":"100000000","min_fee_msat":"100","min_lifetime":144,"min_payment_size_msat":"1","promise":"75eb57db4c37dc092a37f1d2e0026c5ff36a7834a717ea97c41d91a8d5b50ce8","proportional":21,"valid_until":"2023-05-20T08:30:45Z"},"payment_size_msat":null}"#;
489 assert_eq!(buy_request_variable, serde_json::from_str(json_str).unwrap());
490 }
491
492 #[test]
493 fn parse_spec_test_vectors() {
494 let json_str = r#"{
496 "opening_fee_params_menu": [
497 {
498 "min_fee_msat": "546000",
499 "proportional": 1200,
500 "valid_until": "2023-02-23T08:47:30.511Z",
501 "min_lifetime": 1008,
502 "max_client_to_self_delay": 2016,
503 "min_payment_size_msat": "1000",
504 "max_payment_size_msat": "1000000",
505 "promise": "abcdefghijklmnopqrstuvwxyz"
506 },
507 {
508 "min_fee_msat": "1092000",
509 "proportional": 2400,
510 "valid_until": "2023-02-27T21:23:57.984Z",
511 "min_lifetime": 1008,
512 "max_client_to_self_delay": 2016,
513 "min_payment_size_msat": "1000",
514 "max_payment_size_msat": "1000000",
515 "promise": "abcdefghijklmnopqrstuvwxyz"
516 }
517 ]
518 }"#;
519 let _get_info_response: LSPS2GetInfoResponse = serde_json::from_str(json_str).unwrap();
520
521 let json_str = r#"{
522 "opening_fee_params": {
523 "min_fee_msat": "546000",
524 "proportional": 1200,
525 "valid_until": "2023-02-23T08:47:30.511Z",
526 "min_lifetime": 1008,
527 "max_client_to_self_delay": 2016,
528 "min_payment_size_msat": "1000",
529 "max_payment_size_msat": "1000000",
530 "promise": "abcdefghijklmnopqrstuvwxyz"
531 },
532 "payment_size_msat": "42000"
533 }"#;
534 let _buy_request: LSPS2BuyRequest = serde_json::from_str(json_str).unwrap();
535
536 let json_str = r#"{
537 "jit_channel_scid": "29451x4815x1",
538 "lsp_cltv_expiry_delta" : 144,
539 "client_trusts_lsp": false
540 }"#;
541 let _buy_response: LSPS2BuyResponse = serde_json::from_str(json_str).unwrap();
542 }
543}