1use alloc::string::String;
13
14use core::convert::TryFrom;
15
16use crate::lsps0::ser::{
17 string_amount, string_offer, u32_fee_rate, unchecked_address, unchecked_address_option,
18 LSPSDateTime, LSPSMessage, LSPSRequestId, LSPSResponseError,
19};
20
21use bitcoin::{Address, FeeRate, OutPoint};
22
23use lightning::offers::offer::Offer;
24use lightning_invoice::Bolt11Invoice;
25
26use serde::{Deserialize, Serialize};
27
28pub(crate) const LSPS1_GET_INFO_METHOD_NAME: &str = "lsps1.get_info";
29pub(crate) const LSPS1_CREATE_ORDER_METHOD_NAME: &str = "lsps1.create_order";
30pub(crate) const LSPS1_GET_ORDER_METHOD_NAME: &str = "lsps1.get_order";
31
32pub(crate) const _LSPS1_CREATE_ORDER_REQUEST_INVALID_PARAMS_ERROR_CODE: i32 = -32602;
33#[cfg(lsps1_service)]
34pub(crate) const LSPS1_CREATE_ORDER_REQUEST_ORDER_MISMATCH_ERROR_CODE: i32 = 100;
35
36#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize, Hash)]
38pub struct LSPS1OrderId(pub String);
39
40#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize, Default)]
46#[serde(default)]
47pub struct LSPS1GetInfoRequest {}
48
49#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
51pub struct LSPS1Options {
52 pub min_required_channel_confirmations: u16,
54 pub min_funding_confirms_within_blocks: u16,
56 pub supports_zero_channel_reserve: bool,
58 pub max_channel_expiry_blocks: u32,
60 #[serde(with = "string_amount")]
62 pub min_initial_client_balance_sat: u64,
63 #[serde(with = "string_amount")]
65 pub max_initial_client_balance_sat: u64,
66 #[serde(with = "string_amount")]
68 pub min_initial_lsp_balance_sat: u64,
69 #[serde(with = "string_amount")]
71 pub max_initial_lsp_balance_sat: u64,
72 #[serde(with = "string_amount")]
74 pub min_channel_balance_sat: u64,
75 #[serde(with = "string_amount")]
77 pub max_channel_balance_sat: u64,
78}
79
80#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
82pub struct LSPS1GetInfoResponse {
83 #[serde(flatten)]
85 pub options: LSPS1Options,
86}
87
88#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
94pub struct LSPS1CreateOrderRequest {
95 #[serde(flatten)]
97 pub order: LSPS1OrderParams,
98 #[serde(default)]
100 #[serde(skip_serializing_if = "Option::is_none")]
101 #[serde(with = "unchecked_address_option")]
102 pub refund_onchain_address: Option<Address>,
103}
104
105#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
107pub struct LSPS1OrderParams {
108 #[serde(with = "string_amount")]
110 pub lsp_balance_sat: u64,
111 #[serde(with = "string_amount")]
116 pub client_balance_sat: u64,
117 pub required_channel_confirmations: u16,
119 pub funding_confirms_within_blocks: u16,
121 pub channel_expiry_blocks: u32,
123 pub token: Option<String>,
125 pub announce_channel: bool,
127}
128
129#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
131pub struct LSPS1CreateOrderResponse {
132 pub order_id: LSPS1OrderId,
134 #[serde(flatten)]
136 pub order: LSPS1OrderParams,
137 pub created_at: LSPSDateTime,
139 pub order_state: LSPS1OrderState,
141 pub payment: LSPS1PaymentInfo,
143 pub channel: Option<LSPS1ChannelInfo>,
145}
146
147#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
149#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
150pub enum LSPS1OrderState {
151 Created,
153 Completed,
155 Failed,
157}
158
159#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
161pub struct LSPS1PaymentInfo {
162 pub bolt11: Option<LSPS1Bolt11PaymentInfo>,
164 pub bolt12: Option<LSPS1Bolt12PaymentInfo>,
166 pub onchain: Option<LSPS1OnchainPaymentInfo>,
168}
169
170#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
172pub struct LSPS1Bolt11PaymentInfo {
173 pub state: LSPS1PaymentState,
175 pub expires_at: LSPSDateTime,
177 #[serde(with = "string_amount")]
179 pub fee_total_sat: u64,
180 #[serde(with = "string_amount")]
182 pub order_total_sat: u64,
183 pub invoice: Bolt11Invoice,
185}
186
187#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
189pub struct LSPS1Bolt12PaymentInfo {
190 pub state: LSPS1PaymentState,
192 pub expires_at: LSPSDateTime,
194 #[serde(with = "string_amount")]
196 pub fee_total_sat: u64,
197 #[serde(with = "string_amount")]
199 pub order_total_sat: u64,
200 #[serde(with = "string_offer")]
202 pub offer: Offer,
203}
204
205#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
207pub struct LSPS1OnchainPaymentInfo {
208 pub state: LSPS1PaymentState,
210 pub expires_at: LSPSDateTime,
212 #[serde(with = "string_amount")]
214 pub fee_total_sat: u64,
215 #[serde(with = "string_amount")]
217 pub order_total_sat: u64,
218 #[serde(with = "unchecked_address")]
221 pub address: Address,
222 pub min_onchain_payment_confirmations: Option<u16>,
225 #[serde(with = "u32_fee_rate")]
228 pub min_fee_for_0conf: FeeRate,
229 #[serde(default)]
231 #[serde(skip_serializing_if = "Option::is_none")]
232 #[serde(with = "unchecked_address_option")]
233 pub refund_onchain_address: Option<Address>,
234}
235
236#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
241#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
242pub enum LSPS1PaymentState {
243 ExpectPayment,
245 Paid,
247 #[serde(alias = "CANCELLED")]
249 Refunded,
250}
251
252#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
254pub struct LSPS1OnchainPayment {
255 pub outpoint: String,
257 #[serde(with = "string_amount")]
259 pub sat: u64,
260 pub confirmed: bool,
262}
263
264#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
266pub struct LSPS1ChannelInfo {
267 pub funded_at: LSPSDateTime,
269 pub funding_outpoint: OutPoint,
271 pub expires_at: LSPSDateTime,
273}
274
275#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
281pub struct LSPS1GetOrderRequest {
282 pub order_id: LSPS1OrderId,
284}
285
286#[derive(Clone, Debug, PartialEq, Eq)]
288pub enum LSPS1Request {
289 GetInfo(LSPS1GetInfoRequest),
291 CreateOrder(LSPS1CreateOrderRequest),
293 GetOrder(LSPS1GetOrderRequest),
295}
296
297#[derive(Clone, Debug, PartialEq, Eq)]
299pub enum LSPS1Response {
300 GetInfo(LSPS1GetInfoResponse),
302 GetInfoError(LSPSResponseError),
304 CreateOrder(LSPS1CreateOrderResponse),
306 CreateOrderError(LSPSResponseError),
308 GetOrder(LSPS1CreateOrderResponse),
310 GetOrderError(LSPSResponseError),
312}
313
314#[derive(Clone, Debug, PartialEq, Eq)]
316pub enum LSPS1Message {
317 Request(LSPSRequestId, LSPS1Request),
319 Response(LSPSRequestId, LSPS1Response),
321}
322
323impl TryFrom<LSPSMessage> for LSPS1Message {
324 type Error = ();
325
326 fn try_from(message: LSPSMessage) -> Result<Self, Self::Error> {
327 if let LSPSMessage::LSPS1(message) = message {
328 return Ok(message);
329 }
330
331 Err(())
332 }
333}
334
335impl From<LSPS1Message> for LSPSMessage {
336 fn from(message: LSPS1Message) -> Self {
337 LSPSMessage::LSPS1(message)
338 }
339}
340
341#[cfg(test)]
342mod tests {
343 use super::*;
344 use crate::alloc::string::ToString;
345
346 #[test]
347 fn options_supported_serialization() {
348 let min_required_channel_confirmations = 0;
349 let min_funding_confirms_within_blocks = 6;
350 let supports_zero_channel_reserve = true;
351 let max_channel_expiry_blocks = 144;
352 let min_initial_client_balance_sat = 10_000_000;
353 let max_initial_client_balance_sat = 100_000_000;
354 let min_initial_lsp_balance_sat = 100_000;
355 let max_initial_lsp_balance_sat = 100_000_000;
356 let min_channel_balance_sat = 100_000;
357 let max_channel_balance_sat = 100_000_000;
358
359 let options_supported = LSPS1Options {
360 min_required_channel_confirmations,
361 min_funding_confirms_within_blocks,
362 supports_zero_channel_reserve,
363 max_channel_expiry_blocks,
364 min_initial_client_balance_sat,
365 max_initial_client_balance_sat,
366 min_initial_lsp_balance_sat,
367 max_initial_lsp_balance_sat,
368 min_channel_balance_sat,
369 max_channel_balance_sat,
370 };
371
372 let json_str = r#"{"max_channel_balance_sat":"100000000","max_channel_expiry_blocks":144,"max_initial_client_balance_sat":"100000000","max_initial_lsp_balance_sat":"100000000","min_channel_balance_sat":"100000","min_funding_confirms_within_blocks":6,"min_initial_client_balance_sat":"10000000","min_initial_lsp_balance_sat":"100000","min_required_channel_confirmations":0,"supports_zero_channel_reserve":true}"#;
373
374 assert_eq!(json_str, serde_json::json!(options_supported).to_string());
375 assert_eq!(options_supported, serde_json::from_str(json_str).unwrap());
376 }
377
378 #[test]
379 fn parse_spec_test_vectors() {
380 let json_str = r#"{}"#;
382 let _get_info_request: LSPS1GetInfoRequest = serde_json::from_str(json_str).unwrap();
383
384 let json_str = r#"{
385 "min_required_channel_confirmations": 0,
386 "min_funding_confirms_within_blocks" : 6,
387 "supports_zero_channel_reserve": true,
388 "max_channel_expiry_blocks": 20160,
389 "min_initial_client_balance_sat": "20000",
390 "max_initial_client_balance_sat": "100000000",
391 "min_initial_lsp_balance_sat": "0",
392 "max_initial_lsp_balance_sat": "100000000",
393 "min_channel_balance_sat": "50000",
394 "max_channel_balance_sat": "100000000"
395 }"#;
396 let _get_info_response: LSPS1GetInfoResponse = serde_json::from_str(json_str).unwrap();
397
398 let json_str = r#"{
399 "lsp_balance_sat": "5000000",
400 "client_balance_sat": "2000000",
401 "required_channel_confirmations" : 0,
402 "funding_confirms_within_blocks": 6,
403 "channel_expiry_blocks": 144,
404 "token": "",
405 "refund_onchain_address": "bc1qvmsy0f3yyes6z9jvddk8xqwznndmdwapvrc0xrmhd3vqj5rhdrrq6hz49h",
406 "announce_channel": true
407 }"#;
408 let _create_order_request: LSPS1CreateOrderRequest =
409 serde_json::from_str(json_str).unwrap();
410
411 let json_str = r#"{
412 "state" : "EXPECT_PAYMENT",
413 "expires_at": "2025-01-01T00:00:00Z",
414 "fee_total_sat": "8888",
415 "order_total_sat": "200888",
416 "invoice": "lnbc252u1p3aht9ysp580g4633gd2x9lc5al0wd8wx0mpn9748jeyz46kqjrpxn52uhfpjqpp5qgf67tcqmuqehzgjm8mzya90h73deafvr4m5705l5u5l4r05l8cqdpud3h8ymm4w3jhytnpwpczqmt0de6xsmre2pkxzm3qydmkzdjrdev9s7zhgfaqxqyjw5qcqpjrzjqt6xptnd85lpqnu2lefq4cx070v5cdwzh2xlvmdgnu7gqp4zvkus5zapryqqx9qqqyqqqqqqqqqqqcsq9q9qyysgqen77vu8xqjelum24hgjpgfdgfgx4q0nehhalcmuggt32japhjuksq9jv6eksjfnppm4hrzsgyxt8y8xacxut9qv3fpyetz8t7tsymygq8yzn05"
417 }"#;
418 let _bolt11_payment: LSPS1Bolt11PaymentInfo = serde_json::from_str(json_str).unwrap();
419
420 let json_str = r#"{
421 "state" : "EXPECT_PAYMENT",
422 "expires_at": "2025-01-01T00:00:00Z",
423 "fee_total_sat": "8888",
424 "order_total_sat": "200888",
425 "offer": "lno1qgsqvgnwgcg35z6ee2h3yczraddm72xrfua9uve2rlrm9deu7xyfzrcgqvp3pwq2q3shxerxzzfqyxdxxkjqxfpt9tz94zj79m4n99kwjddq92uqryuwsu4nt0t3lthfq02jzqla96dtkf4rew8edxw0p85swe89wd8ldekdl5j262n76qyl2qgzajm08clzr74z6ssy0qlvvp9f5kvrxf27yz4pcy99jge69kxu8ttsqt8gw8jsk5397zvvdf4lfd52paf73thcg6xf57xmvtdrwny5mn2r4jw2d5jzalqrq537mmt6u9qpqytzzql6zemrme07jqqwtza76lldcj9wgc0ccd4d2w584cdcx6szsuupvy"
426 }"#;
427 let _bolt11_payment: LSPS1Bolt12PaymentInfo = serde_json::from_str(json_str).unwrap();
428
429 let json_str = r#"{
430 "state": "EXPECT_PAYMENT",
431 "expires_at": "2025-01-01T00:00:00Z",
432 "fee_total_sat": "9999",
433 "order_total_sat": "200999",
434 "address": "bc1p5uvtaxzkjwvey2tfy49k5vtqfpjmrgm09cvs88ezyy8h2zv7jhas9tu4yr",
435 "min_onchain_payment_confirmations": 1,
436 "min_fee_for_0conf": 253
437 }"#;
438 let _onchain_payment: LSPS1OnchainPaymentInfo = serde_json::from_str(json_str).unwrap();
439
440 let json_str = r#"{
441 "bolt11": {
442 "state" : "EXPECT_PAYMENT",
443 "expires_at": "2025-01-01T00:00:00Z",
444 "fee_total_sat": "8888",
445 "order_total_sat": "200888",
446 "invoice": "lnbc252u1p3aht9ysp580g4633gd2x9lc5al0wd8wx0mpn9748jeyz46kqjrpxn52uhfpjqpp5qgf67tcqmuqehzgjm8mzya90h73deafvr4m5705l5u5l4r05l8cqdpud3h8ymm4w3jhytnpwpczqmt0de6xsmre2pkxzm3qydmkzdjrdev9s7zhgfaqxqyjw5qcqpjrzjqt6xptnd85lpqnu2lefq4cx070v5cdwzh2xlvmdgnu7gqp4zvkus5zapryqqx9qqqyqqqqqqqqqqqcsq9q9qyysgqen77vu8xqjelum24hgjpgfdgfgx4q0nehhalcmuggt32japhjuksq9jv6eksjfnppm4hrzsgyxt8y8xacxut9qv3fpyetz8t7tsymygq8yzn05"
447 },
448 "bolt12": {
449 "state" : "EXPECT_PAYMENT",
450 "expires_at": "2025-01-01T00:00:00Z",
451 "fee_total_sat": "8888",
452 "order_total_sat": "200888",
453 "offer": "lno1qgsqvgnwgcg35z6ee2h3yczraddm72xrfua9uve2rlrm9deu7xyfzrcgqvp3pwq2q3shxerxzzfqyxdxxkjqxfpt9tz94zj79m4n99kwjddq92uqryuwsu4nt0t3lthfq02jzqla96dtkf4rew8edxw0p85swe89wd8ldekdl5j262n76qyl2qgzajm08clzr74z6ssy0qlvvp9f5kvrxf27yz4pcy99jge69kxu8ttsqt8gw8jsk5397zvvdf4lfd52paf73thcg6xf57xmvtdrwny5mn2r4jw2d5jzalqrq537mmt6u9qpqytzzql6zemrme07jqqwtza76lldcj9wgc0ccd4d2w584cdcx6szsuupvy"
454 },
455 "onchain": {
456 "state": "EXPECT_PAYMENT",
457 "expires_at": "2025-01-01T00:00:00Z",
458 "fee_total_sat": "9999",
459 "order_total_sat": "200999",
460 "address": "bc1p5uvtaxzkjwvey2tfy49k5vtqfpjmrgm09cvs88ezyy8h2zv7jhas9tu4yr",
461 "min_onchain_payment_confirmations": 1,
462 "min_fee_for_0conf": 253
463 }
464 }"#;
465 let payment: LSPS1PaymentInfo = serde_json::from_str(json_str).unwrap();
466 assert!(payment.bolt11.is_some());
467 assert!(payment.bolt12.is_some());
468 assert!(payment.onchain.is_some());
469
470 let json_str = r#"{
471 "order_id": "bb4b5d0a-8334-49d8-9463-90a6d413af7c",
472 "lsp_balance_sat": "5000000",
473 "client_balance_sat": "2000000",
474 "required_channel_confirmations" : 0,
475 "funding_confirms_within_blocks": 1,
476 "channel_expiry_blocks": 12,
477 "token": "",
478 "created_at": "2012-04-23T18:25:43.511Z",
479 "announce_channel": true,
480 "order_state": "CREATED",
481 "payment": {
482 "bolt11": {
483 "state": "EXPECT_PAYMENT",
484 "expires_at": "2015-01-25T19:29:44.612Z",
485 "fee_total_sat": "8888",
486 "order_total_sat": "2008888",
487 "invoice" : "lnbc252u1p3aht9ysp580g4633gd2x9lc5al0wd8wx0mpn9748jeyz46kqjrpxn52uhfpjqpp5qgf67tcqmuqehzgjm8mzya90h73deafvr4m5705l5u5l4r05l8cqdpud3h8ymm4w3jhytnpwpczqmt0de6xsmre2pkxzm3qydmkzdjrdev9s7zhgfaqxqyjw5qcqpjrzjqt6xptnd85lpqnu2lefq4cx070v5cdwzh2xlvmdgnu7gqp4zvkus5zapryqqx9qqqyqqqqqqqqqqqcsq9q9qyysgqen77vu8xqjelum24hgjpgfdgfgx4q0nehhalcmuggt32japhjuksq9jv6eksjfnppm4hrzsgyxt8y8xacxut9qv3fpyetz8t7tsymygq8yzn05"
488 },
489 "bolt12": {
490 "state" : "EXPECT_PAYMENT",
491 "expires_at": "2025-01-01T00:00:00Z",
492 "fee_total_sat": "8888",
493 "order_total_sat": "200888",
494 "offer": "lno1qgsqvgnwgcg35z6ee2h3yczraddm72xrfua9uve2rlrm9deu7xyfzrcgqvp3pwq2q3shxerxzzfqyxdxxkjqxfpt9tz94zj79m4n99kwjddq92uqryuwsu4nt0t3lthfq02jzqla96dtkf4rew8edxw0p85swe89wd8ldekdl5j262n76qyl2qgzajm08clzr74z6ssy0qlvvp9f5kvrxf27yz4pcy99jge69kxu8ttsqt8gw8jsk5397zvvdf4lfd52paf73thcg6xf57xmvtdrwny5mn2r4jw2d5jzalqrq537mmt6u9qpqytzzql6zemrme07jqqwtza76lldcj9wgc0ccd4d2w584cdcx6szsuupvy"
495 },
496 "onchain": {
497 "state": "EXPECT_PAYMENT",
498 "expires_at": "2015-01-25T19:29:44.612Z",
499 "fee_total_sat": "9999",
500 "order_total_sat": "2009999",
501 "address" : "bc1p5uvtaxzkjwvey2tfy49k5vtqfpjmrgm09cvs88ezyy8h2zv7jhas9tu4yr",
502 "min_fee_for_0conf": 253,
503 "min_onchain_payment_confirmations": 0,
504 "refund_onchain_address": "bc1qvmsy0f3yyes6z9jvddk8xqwznndmdwapvrc0xrmhd3vqj5rhdrrq6hz49h"
505 }
506 },
507 "channel": null
508 }"#;
509 let create_order_response: LSPS1CreateOrderResponse =
510 serde_json::from_str(json_str).unwrap();
511 assert!(create_order_response.payment.bolt11.is_some());
512 assert!(create_order_response.payment.bolt12.is_some());
513 assert!(create_order_response.payment.onchain.is_some());
514
515 let json_str = r#"{
516 "order_id": "bb4b5d0a-8334-49d8-9463-90a6d413af7c"
517 }"#;
518 let _get_order_request: LSPS1GetOrderRequest = serde_json::from_str(json_str).unwrap();
519
520 let json_str = r#"{
521 "funded_at": "2012-04-23T18:25:43.511Z",
522 "funding_outpoint": "0301e0480b374b32851a9462db29dc19fe830a7f7d7a88b81612b9d42099c0ae:0",
523 "expires_at": "2012-04-23T18:25:43.511Z"
524 }"#;
525 let _channel: LSPS1ChannelInfo = serde_json::from_str(json_str).unwrap();
526
527 let json_str = r#""CANCELLED""#;
528 let payment_state: LSPS1PaymentState = serde_json::from_str(json_str).unwrap();
529 assert_eq!(payment_state, LSPS1PaymentState::Refunded);
530
531 let json_str = r#""REFUNDED""#;
532 let payment_state: LSPS1PaymentState = serde_json::from_str(json_str).unwrap();
533 assert_eq!(payment_state, LSPS1PaymentState::Refunded);
534 }
535}