Skip to content

Commit 9626561

Browse files
authored
Merge pull request lightningdevkit#3649 from tnull/2025-03-add-bolt12-support-for-blip-51
Add BOLT12 support to bLIP-51 / LSPS1
2 parents d671596 + 3274dbf commit 9626561

File tree

2 files changed

+80
-4
lines changed

2 files changed

+80
-4
lines changed

lightning-liquidity/src/lsps0/ser.rs

+26
Original file line numberDiff line numberDiff line change
@@ -678,6 +678,32 @@ pub(crate) mod string_amount_option {
678678
}
679679
}
680680

681+
pub(crate) mod string_offer {
682+
use crate::prelude::{String, ToString};
683+
use core::str::FromStr;
684+
use lightning::offers::offer::Offer;
685+
use serde::de::Unexpected;
686+
use serde::{Deserialize, Deserializer, Serializer};
687+
688+
pub(crate) fn serialize<S>(x: &Offer, s: S) -> Result<S::Ok, S::Error>
689+
where
690+
S: Serializer,
691+
{
692+
s.serialize_str(&x.to_string())
693+
}
694+
695+
pub(crate) fn deserialize<'de, D>(deserializer: D) -> Result<Offer, D::Error>
696+
where
697+
D: Deserializer<'de>,
698+
{
699+
let buf = String::deserialize(deserializer)?;
700+
701+
Offer::from_str(&buf).map_err(|_| {
702+
serde::de::Error::invalid_value(Unexpected::Str(&buf), &"invalid offer string")
703+
})
704+
}
705+
}
706+
681707
pub(crate) mod unchecked_address {
682708
use crate::prelude::{String, ToString};
683709
use bitcoin::Address;

lightning-liquidity/src/lsps1/msgs.rs

+54-4
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,15 @@
11
//! Message, request, and other primitive types used to implement bLIP-51 / LSPS1.
22
33
use crate::lsps0::ser::{
4-
string_amount, u32_fee_rate, unchecked_address, unchecked_address_option, LSPSDateTime,
5-
LSPSMessage, LSPSRequestId, LSPSResponseError,
4+
string_amount, string_offer, u32_fee_rate, unchecked_address, unchecked_address_option,
5+
LSPSDateTime, LSPSMessage, LSPSRequestId, LSPSResponseError,
66
};
77

88
use crate::prelude::String;
99

1010
use bitcoin::{Address, FeeRate, OutPoint};
1111

12+
use lightning::offers::offer::Offer;
1213
use lightning_invoice::Bolt11Invoice;
1314

1415
use serde::{Deserialize, Serialize};
@@ -151,6 +152,8 @@ pub enum LSPS1OrderState {
151152
pub struct LSPS1PaymentInfo {
152153
/// A Lightning payment using BOLT 11.
153154
pub bolt11: Option<LSPS1Bolt11PaymentInfo>,
155+
/// A Lightning payment using BOLT 12.
156+
pub bolt12: Option<LSPS1Bolt12PaymentInfo>,
154157
/// An onchain payment.
155158
pub onchain: Option<LSPS1OnchainPaymentInfo>,
156159
}
@@ -172,6 +175,24 @@ pub struct LSPS1Bolt11PaymentInfo {
172175
pub invoice: Bolt11Invoice,
173176
}
174177

178+
/// A Lightning payment using BOLT 12.
179+
#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
180+
pub struct LSPS1Bolt12PaymentInfo {
181+
/// Indicates the current state of the payment.
182+
pub state: LSPS1PaymentState,
183+
/// The datetime when the payment option expires.
184+
pub expires_at: LSPSDateTime,
185+
/// The total fee the LSP will charge to open this channel in satoshi.
186+
#[serde(with = "string_amount")]
187+
pub fee_total_sat: u64,
188+
/// The amount the client needs to pay to have the requested channel openend.
189+
#[serde(with = "string_amount")]
190+
pub order_total_sat: u64,
191+
/// A BOLT12 offer the client can pay to have to channel opened.
192+
#[serde(with = "string_offer")]
193+
pub offer: Offer,
194+
}
195+
175196
/// An onchain payment.
176197
#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
177198
pub struct LSPS1OnchainPaymentInfo {
@@ -387,6 +408,15 @@ mod tests {
387408
}"#;
388409
let _bolt11_payment: LSPS1Bolt11PaymentInfo = serde_json::from_str(json_str).unwrap();
389410

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+
"offer": "lno1qgsqvgnwgcg35z6ee2h3yczraddm72xrfua9uve2rlrm9deu7xyfzrcgqvp3pwq2q3shxerxzzfqyxdxxkjqxfpt9tz94zj79m4n99kwjddq92uqryuwsu4nt0t3lthfq02jzqla96dtkf4rew8edxw0p85swe89wd8ldekdl5j262n76qyl2qgzajm08clzr74z6ssy0qlvvp9f5kvrxf27yz4pcy99jge69kxu8ttsqt8gw8jsk5397zvvdf4lfd52paf73thcg6xf57xmvtdrwny5mn2r4jw2d5jzalqrq537mmt6u9qpqytzzql6zemrme07jqqwtza76lldcj9wgc0ccd4d2w584cdcx6szsuupvy"
417+
}"#;
418+
let _bolt11_payment: LSPS1Bolt12PaymentInfo = serde_json::from_str(json_str).unwrap();
419+
390420
let json_str = r#"{
391421
"state": "EXPECT_PAYMENT",
392422
"expires_at": "2025-01-01T00:00:00Z",
@@ -406,6 +436,13 @@ mod tests {
406436
"order_total_sat": "200888",
407437
"invoice": "lnbc252u1p3aht9ysp580g4633gd2x9lc5al0wd8wx0mpn9748jeyz46kqjrpxn52uhfpjqpp5qgf67tcqmuqehzgjm8mzya90h73deafvr4m5705l5u5l4r05l8cqdpud3h8ymm4w3jhytnpwpczqmt0de6xsmre2pkxzm3qydmkzdjrdev9s7zhgfaqxqyjw5qcqpjrzjqt6xptnd85lpqnu2lefq4cx070v5cdwzh2xlvmdgnu7gqp4zvkus5zapryqqx9qqqyqqqqqqqqqqqcsq9q9qyysgqen77vu8xqjelum24hgjpgfdgfgx4q0nehhalcmuggt32japhjuksq9jv6eksjfnppm4hrzsgyxt8y8xacxut9qv3fpyetz8t7tsymygq8yzn05"
408438
},
439+
"bolt12": {
440+
"state" : "EXPECT_PAYMENT",
441+
"expires_at": "2025-01-01T00:00:00Z",
442+
"fee_total_sat": "8888",
443+
"order_total_sat": "200888",
444+
"offer": "lno1qgsqvgnwgcg35z6ee2h3yczraddm72xrfua9uve2rlrm9deu7xyfzrcgqvp3pwq2q3shxerxzzfqyxdxxkjqxfpt9tz94zj79m4n99kwjddq92uqryuwsu4nt0t3lthfq02jzqla96dtkf4rew8edxw0p85swe89wd8ldekdl5j262n76qyl2qgzajm08clzr74z6ssy0qlvvp9f5kvrxf27yz4pcy99jge69kxu8ttsqt8gw8jsk5397zvvdf4lfd52paf73thcg6xf57xmvtdrwny5mn2r4jw2d5jzalqrq537mmt6u9qpqytzzql6zemrme07jqqwtza76lldcj9wgc0ccd4d2w584cdcx6szsuupvy"
445+
},
409446
"onchain": {
410447
"state": "EXPECT_PAYMENT",
411448
"expires_at": "2025-01-01T00:00:00Z",
@@ -416,7 +453,10 @@ mod tests {
416453
"min_fee_for_0conf": 253
417454
}
418455
}"#;
419-
let _payment: LSPS1PaymentInfo = serde_json::from_str(json_str).unwrap();
456+
let payment: LSPS1PaymentInfo = serde_json::from_str(json_str).unwrap();
457+
assert!(payment.bolt11.is_some());
458+
assert!(payment.bolt12.is_some());
459+
assert!(payment.onchain.is_some());
420460

421461
let json_str = r#"{
422462
"order_id": "bb4b5d0a-8334-49d8-9463-90a6d413af7c",
@@ -437,6 +477,13 @@ mod tests {
437477
"order_total_sat": "2008888",
438478
"invoice" : "lnbc252u1p3aht9ysp580g4633gd2x9lc5al0wd8wx0mpn9748jeyz46kqjrpxn52uhfpjqpp5qgf67tcqmuqehzgjm8mzya90h73deafvr4m5705l5u5l4r05l8cqdpud3h8ymm4w3jhytnpwpczqmt0de6xsmre2pkxzm3qydmkzdjrdev9s7zhgfaqxqyjw5qcqpjrzjqt6xptnd85lpqnu2lefq4cx070v5cdwzh2xlvmdgnu7gqp4zvkus5zapryqqx9qqqyqqqqqqqqqqqcsq9q9qyysgqen77vu8xqjelum24hgjpgfdgfgx4q0nehhalcmuggt32japhjuksq9jv6eksjfnppm4hrzsgyxt8y8xacxut9qv3fpyetz8t7tsymygq8yzn05"
439479
},
480+
"bolt12": {
481+
"state" : "EXPECT_PAYMENT",
482+
"expires_at": "2025-01-01T00:00:00Z",
483+
"fee_total_sat": "8888",
484+
"order_total_sat": "200888",
485+
"offer": "lno1qgsqvgnwgcg35z6ee2h3yczraddm72xrfua9uve2rlrm9deu7xyfzrcgqvp3pwq2q3shxerxzzfqyxdxxkjqxfpt9tz94zj79m4n99kwjddq92uqryuwsu4nt0t3lthfq02jzqla96dtkf4rew8edxw0p85swe89wd8ldekdl5j262n76qyl2qgzajm08clzr74z6ssy0qlvvp9f5kvrxf27yz4pcy99jge69kxu8ttsqt8gw8jsk5397zvvdf4lfd52paf73thcg6xf57xmvtdrwny5mn2r4jw2d5jzalqrq537mmt6u9qpqytzzql6zemrme07jqqwtza76lldcj9wgc0ccd4d2w584cdcx6szsuupvy"
486+
},
440487
"onchain": {
441488
"state": "EXPECT_PAYMENT",
442489
"expires_at": "2015-01-25T19:29:44.612Z",
@@ -450,8 +497,11 @@ mod tests {
450497
},
451498
"channel": null
452499
}"#;
453-
let _create_order_response: LSPS1CreateOrderResponse =
500+
let create_order_response: LSPS1CreateOrderResponse =
454501
serde_json::from_str(json_str).unwrap();
502+
assert!(create_order_response.payment.bolt11.is_some());
503+
assert!(create_order_response.payment.bolt12.is_some());
504+
assert!(create_order_response.payment.onchain.is_some());
455505

456506
let json_str = r#"{
457507
"order_id": "bb4b5d0a-8334-49d8-9463-90a6d413af7c"

0 commit comments

Comments
 (0)