Skip to content
This repository was archived by the owner on Jan 6, 2025. It is now read-only.

Commit 783d5bd

Browse files
authored
Merge pull request #126 from tnull/2024-03-lsps1-client
LSPS1: Fix and test serialization
2 parents 8d37a4b + 812ad1d commit 783d5bd

File tree

4 files changed

+125
-30
lines changed

4 files changed

+125
-30
lines changed

src/lsps1/client.rs

-1
Original file line numberDiff line numberDiff line change
@@ -300,7 +300,6 @@ where
300300
self.pending_events.enqueue(Event::LSPS1Client(LSPS1ClientEvent::GetInfoResponse {
301301
user_channel_id,
302302
counterparty_node_id: *counterparty_node_id,
303-
website: result.website,
304303
options_supported: result.options,
305304
}))
306305
},

src/lsps1/event.rs

+2-4
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99

1010
//! Contains LSPS1 event types
1111
12-
use super::msgs::{ChannelInfo, OptionsSupported, OrderId, OrderParams, OrderPayment};
12+
use super::msgs::{ChannelInfo, OptionsSupported, OrderId, OrderParams, PaymentInfo};
1313

1414
use crate::lsps0::ser::RequestId;
1515
use crate::prelude::String;
@@ -35,8 +35,6 @@ pub enum LSPS1ClientEvent {
3535
user_channel_id: u128,
3636
/// The node id of the LSP that provided this response.
3737
counterparty_node_id: PublicKey,
38-
/// The website of the LSP.
39-
website: String,
4038
/// All options supported by the LSP.
4139
options_supported: OptionsSupported,
4240
},
@@ -62,7 +60,7 @@ pub enum LSPS1ClientEvent {
6260
/// The order created by client and approved by LSP.
6361
order: OrderParams,
6462
/// The details regarding payment of the order
65-
payment: OrderPayment,
63+
payment: PaymentInfo,
6664
/// The details regarding state of the channel ordered.
6765
channel: Option<ChannelInfo>,
6866
},

src/lsps1/msgs.rs

+118-17
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ use crate::lsps0::ser::{
77
use crate::prelude::{String, Vec};
88

99
use bitcoin::address::{Address, NetworkUnchecked};
10+
use bitcoin::OutPoint;
1011

1112
use lightning_invoice::Bolt11Invoice;
1213

@@ -38,8 +39,10 @@ pub struct GetInfoRequest {}
3839
/// An object representing the supported protocol options.
3940
#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
4041
pub struct OptionsSupported {
41-
/// The minimum number of block confirmations before the LSP accepts a channel as confirmed.
42-
pub min_channel_confirmations: u8,
42+
/// The smallest number of confirmations needed for the LSP to accept a channel as confirmed.
43+
pub min_required_channel_confirmations: u8,
44+
/// The smallest number of blocks in which the LSP can confirm the funding transaction.
45+
pub min_funding_confirms_within_blocks: u8,
4346
/// The minimum number of block confirmations before the LSP accepts an on-chain payment as confirmed.
4447
pub min_onchain_payment_confirmations: Option<u8>,
4548
/// Indicates if the LSP supports zero reserve.
@@ -70,11 +73,9 @@ pub struct OptionsSupported {
7073
pub max_channel_balance_sat: u64,
7174
}
7275

73-
/// A response to an [`GetInfoRequest`].
76+
/// A response to a [`GetInfoRequest`].
7477
#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
7578
pub struct GetInfoResponse {
76-
/// The website of the LSP.
77-
pub website: String,
7879
/// All options supported by the LSP.
7980
pub options: OptionsSupported,
8081
}
@@ -86,6 +87,7 @@ pub struct GetInfoResponse {
8687
#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
8788
pub struct CreateOrderRequest {
8889
/// The order made.
90+
#[serde(flatten)]
8991
pub order: OrderParams,
9092
}
9193

@@ -101,8 +103,10 @@ pub struct OrderParams {
101103
/// the channel.
102104
#[serde(with = "string_amount")]
103105
pub client_balance_sat: u64,
104-
/// The number of blocks the client wants to wait maximally for the channel to be confirmed.
105-
pub confirms_within_blocks: u32,
106+
/// The number of confirmations the funding tx must have before the LSP sends `channel_ready`.
107+
pub required_channel_confirmations: u8,
108+
/// The maximum number of blocks the client wants to wait until the funding transaction is confirmed.
109+
pub funding_confirms_within_blocks: u8,
106110
/// Indicates how long the channel is leased for in block time.
107111
pub channel_expiry_blocks: u32,
108112
/// May contain arbitrary associated data like a coupon code or a authentication token.
@@ -119,6 +123,7 @@ pub struct CreateOrderResponse {
119123
/// The id of the channel order.
120124
pub order_id: OrderId,
121125
/// The parameters of channel order.
126+
#[serde(flatten)]
122127
pub order: OrderParams,
123128
/// The datetime when the order was created
124129
pub created_at: chrono::DateTime<Utc>,
@@ -127,13 +132,14 @@ pub struct CreateOrderResponse {
127132
/// The current state of the order.
128133
pub order_state: OrderState,
129134
/// Contains details about how to pay for the order.
130-
pub payment: OrderPayment,
135+
pub payment: PaymentInfo,
131136
/// Contains information about the channel state.
132137
pub channel: Option<ChannelInfo>,
133138
}
134139

135140
/// An object representing the state of an order.
136141
#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
142+
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
137143
pub enum OrderState {
138144
/// The order has been created.
139145
Created,
@@ -145,7 +151,7 @@ pub enum OrderState {
145151

146152
/// Details regarding how to pay for an order.
147153
#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
148-
pub struct OrderPayment {
154+
pub struct PaymentInfo {
149155
/// Indicates the current state of the payment.
150156
pub state: PaymentState,
151157
/// The total fee the LSP will charge to open this channel in satoshi.
@@ -166,11 +172,12 @@ pub struct OrderPayment {
166172
/// confirmed without a confirmation.
167173
pub min_fee_for_0conf: u8,
168174
/// Details regarding a detected on-chain payment.
169-
pub onchain_payment: OnchainPayment,
175+
pub onchain_payment: Option<OnchainPayment>,
170176
}
171177

172-
/// The state of an [`OrderPayment`].
178+
/// The state of an [`PaymentInfo`].
173179
#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
180+
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
174181
pub enum PaymentState {
175182
/// A payment is expected.
176183
ExpectPayment,
@@ -198,11 +205,11 @@ pub struct OnchainPayment {
198205
#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
199206
pub struct ChannelInfo {
200207
/// The datetime when the funding transaction has been published.
201-
pub funded_at: String,
208+
pub funded_at: chrono::DateTime<Utc>,
202209
/// The outpoint of the funding transaction.
203-
pub funding_outpoint: String,
210+
pub funding_outpoint: OutPoint,
204211
/// The earliest datetime when the channel may be closed by the LSP.
205-
pub expires_at: String,
212+
pub expires_at: chrono::DateTime<Utc>,
206213
}
207214

208215
/// A request made to an LSP to retrieve information about an previously made order.
@@ -277,7 +284,8 @@ mod tests {
277284

278285
#[test]
279286
fn options_supported_serialization() {
280-
let min_channel_confirmations = 6;
287+
let min_required_channel_confirmations = 0;
288+
let min_funding_confirms_within_blocks = 6;
281289
let min_onchain_payment_confirmations = Some(6);
282290
let supports_zero_channel_reserve = true;
283291
let min_onchain_payment_size_sat = Some(100_000);
@@ -290,7 +298,8 @@ mod tests {
290298
let max_channel_balance_sat = 100_000_000;
291299

292300
let options_supported = OptionsSupported {
293-
min_channel_confirmations,
301+
min_required_channel_confirmations,
302+
min_funding_confirms_within_blocks,
294303
min_onchain_payment_confirmations,
295304
supports_zero_channel_reserve,
296305
min_onchain_payment_size_sat,
@@ -303,8 +312,100 @@ mod tests {
303312
max_channel_balance_sat,
304313
};
305314

306-
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_channel_confirmations":6,"min_initial_client_balance_sat":"10000000","min_initial_lsp_balance_sat":"100000","min_onchain_payment_confirmations":6,"min_onchain_payment_size_sat":"100000","supports_zero_channel_reserve":true}"#;
315+
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_onchain_payment_confirmations":6,"min_onchain_payment_size_sat":"100000","min_required_channel_confirmations":0,"supports_zero_channel_reserve":true}"#;
316+
307317
assert_eq!(json_str, serde_json::json!(options_supported).to_string());
308318
assert_eq!(options_supported, serde_json::from_str(json_str).unwrap());
309319
}
320+
321+
#[test]
322+
fn parse_spec_test_vectors() {
323+
// Here, we simply assert that we're able to parse all examples given in LSPS1.
324+
let json_str = r#"{}"#;
325+
let _get_info_request: GetInfoRequest = serde_json::from_str(json_str).unwrap();
326+
327+
let json_str = r#"{
328+
"options": {
329+
"min_required_channel_confirmations": 0,
330+
"min_funding_confirms_within_blocks" : 6,
331+
"min_onchain_payment_confirmations": null,
332+
"supports_zero_channel_reserve": true,
333+
"min_onchain_payment_size_sat": null,
334+
"max_channel_expiry_blocks": 20160,
335+
"min_initial_client_balance_sat": "20000",
336+
"max_initial_client_balance_sat": "100000000",
337+
"min_initial_lsp_balance_sat": "0",
338+
"max_initial_lsp_balance_sat": "100000000",
339+
"min_channel_balance_sat": "50000",
340+
"max_channel_balance_sat": "100000000"
341+
}
342+
}"#;
343+
let _get_info_response: GetInfoResponse = serde_json::from_str(json_str).unwrap();
344+
345+
let json_str = r#"{
346+
"lsp_balance_sat": "5000000",
347+
"client_balance_sat": "2000000",
348+
"required_channel_confirmations" : 0,
349+
"funding_confirms_within_blocks": 6,
350+
"channel_expiry_blocks": 144,
351+
"token": "",
352+
"refund_onchain_address": "bc1qvmsy0f3yyes6z9jvddk8xqwznndmdwapvrc0xrmhd3vqj5rhdrrq6hz49h",
353+
"announce_channel": true
354+
}"#;
355+
let _create_order_request: CreateOrderRequest = serde_json::from_str(json_str).unwrap();
356+
357+
let json_str = r#"{
358+
"order_id": "bb4b5d0a-8334-49d8-9463-90a6d413af7c",
359+
"lsp_balance_sat": "5000000",
360+
"client_balance_sat": "2000000",
361+
"required_channel_confirmations" : 0,
362+
"funding_confirms_within_blocks": 1,
363+
"channel_expiry_blocks": 12,
364+
"token": "",
365+
"created_at": "2012-04-23T18:25:43.511Z",
366+
"expires_at": "2015-01-25T19:29:44.612Z",
367+
"announce_channel": true,
368+
"order_state": "CREATED",
369+
"payment": {
370+
"state": "EXPECT_PAYMENT",
371+
"fee_total_sat": "8888",
372+
"order_total_sat": "2008888",
373+
"bolt11_invoice": "lnbc252u1p3aht9ysp580g4633gd2x9lc5al0wd8wx0mpn9748jeyz46kqjrpxn52uhfpjqpp5qgf67tcqmuqehzgjm8mzya90h73deafvr4m5705l5u5l4r05l8cqdpud3h8ymm4w3jhytnpwpczqmt0de6xsmre2pkxzm3qydmkzdjrdev9s7zhgfaqxqyjw5qcqpjrzjqt6xptnd85lpqnu2lefq4cx070v5cdwzh2xlvmdgnu7gqp4zvkus5zapryqqx9qqqyqqqqqqqqqqqcsq9q9qyysgqen77vu8xqjelum24hgjpgfdgfgx4q0nehhalcmuggt32japhjuksq9jv6eksjfnppm4hrzsgyxt8y8xacxut9qv3fpyetz8t7tsymygq8yzn05",
374+
"onchain_address": "bc1p5uvtaxzkjwvey2tfy49k5vtqfpjmrgm09cvs88ezyy8h2zv7jhas9tu4yr",
375+
"min_onchain_payment_confirmations": 0,
376+
"min_fee_for_0conf": 253,
377+
"onchain_payment": null
378+
},
379+
"channel": null
380+
}"#;
381+
let _create_order_response: CreateOrderResponse = serde_json::from_str(json_str).unwrap();
382+
383+
let json_str = r#"{
384+
"order_id": "bb4b5d0a-8334-49d8-9463-90a6d413af7c"
385+
}"#;
386+
let _get_order_request: GetOrderRequest = serde_json::from_str(json_str).unwrap();
387+
388+
let json_str = r#"{
389+
"state": "EXPECT_PAYMENT",
390+
"fee_total_sat": "8888",
391+
"order_total_sat": "2008888",
392+
"bolt11_invoice": "lnbc252u1p3aht9ysp580g4633gd2x9lc5al0wd8wx0mpn9748jeyz46kqjrpxn52uhfpjqpp5qgf67tcqmuqehzgjm8mzya90h73deafvr4m5705l5u5l4r05l8cqdpud3h8ymm4w3jhytnpwpczqmt0de6xsmre2pkxzm3qydmkzdjrdev9s7zhgfaqxqyjw5qcqpjrzjqt6xptnd85lpqnu2lefq4cx070v5cdwzh2xlvmdgnu7gqp4zvkus5zapryqqx9qqqyqqqqqqqqqqqcsq9q9qyysgqen77vu8xqjelum24hgjpgfdgfgx4q0nehhalcmuggt32japhjuksq9jv6eksjfnppm4hrzsgyxt8y8xacxut9qv3fpyetz8t7tsymygq8yzn05",
393+
"onchain_address": "bc1p5uvtaxzkjwvey2tfy49k5vtqfpjmrgm09cvs88ezyy8h2zv7jhas9tu4yr",
394+
"min_onchain_payment_confirmations": 1,
395+
"min_fee_for_0conf": 253,
396+
"onchain_payment": {
397+
"outpoint": "0301e0480b374b32851a9462db29dc19fe830a7f7d7a88b81612b9d42099c0ae:1",
398+
"sat": "1200",
399+
"confirmed": false
400+
}
401+
}"#;
402+
let _payment: PaymentInfo = serde_json::from_str(json_str).unwrap();
403+
404+
let json_str = r#"{
405+
"funded_at": "2012-04-23T18:25:43.511Z",
406+
"funding_outpoint": "0301e0480b374b32851a9462db29dc19fe830a7f7d7a88b81612b9d42099c0ae:0",
407+
"expires_at": "2012-04-23T18:25:43.511Z"
408+
}"#;
409+
let _channel: ChannelInfo = serde_json::from_str(json_str).unwrap();
410+
}
310411
}

src/lsps1/service.rs

+5-8
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,8 @@
1212
use super::event::LSPS1ServiceEvent;
1313
use super::msgs::{
1414
ChannelInfo, CreateOrderRequest, CreateOrderResponse, GetInfoResponse, GetOrderRequest,
15-
LSPS1Message, LSPS1Request, LSPS1Response, OptionsSupported, OrderId, OrderParams,
16-
OrderPayment, OrderState, LSPS1_CREATE_ORDER_REQUEST_ORDER_MISMATCH_ERROR_CODE,
15+
LSPS1Message, LSPS1Request, LSPS1Response, OptionsSupported, OrderId, OrderParams, OrderState,
16+
PaymentInfo, LSPS1_CREATE_ORDER_REQUEST_ORDER_MISMATCH_ERROR_CODE,
1717
};
1818
use super::utils::is_valid;
1919
use crate::message_queue::MessageQueue;
@@ -43,8 +43,6 @@ pub struct LSPS1ServiceConfig {
4343
pub token: Option<String>,
4444
/// The options supported by the LSP.
4545
pub options_supported: Option<OptionsSupported>,
46-
/// The LSP's website.
47-
pub website: Option<String>,
4846
}
4947

5048
struct ChannelStateError(String);
@@ -77,7 +75,7 @@ struct OutboundLSPS1Config {
7775
order: OrderParams,
7876
created_at: chrono::DateTime<Utc>,
7977
expires_at: chrono::DateTime<Utc>,
80-
payment: OrderPayment,
78+
payment: PaymentInfo,
8179
}
8280

8381
struct OutboundCRChannel {
@@ -88,7 +86,7 @@ struct OutboundCRChannel {
8886
impl OutboundCRChannel {
8987
fn new(
9088
order: OrderParams, created_at: chrono::DateTime<Utc>, expires_at: chrono::DateTime<Utc>,
91-
order_id: OrderId, payment: OrderPayment,
89+
order_id: OrderId, payment: PaymentInfo,
9290
) -> Self {
9391
Self {
9492
state: OutboundRequestState::OrderCreated { order_id },
@@ -171,7 +169,6 @@ where
171169
&self, request_id: RequestId, counterparty_node_id: &PublicKey,
172170
) -> Result<(), LightningError> {
173171
let response = LSPS1Response::GetInfo(GetInfoResponse {
174-
website: self.config.website.clone().unwrap().to_string(),
175172
options: self
176173
.config
177174
.options_supported
@@ -241,7 +238,7 @@ where
241238
///
242239
/// [`LSPS1ServiceEvent::RequestForPaymentDetails`]: crate::lsps1::event::LSPS1ServiceEvent::RequestForPaymentDetails
243240
pub fn send_payment_details(
244-
&self, request_id: RequestId, counterparty_node_id: &PublicKey, payment: OrderPayment,
241+
&self, request_id: RequestId, counterparty_node_id: &PublicKey, payment: PaymentInfo,
245242
created_at: chrono::DateTime<Utc>, expires_at: chrono::DateTime<Utc>,
246243
) -> Result<(), APIError> {
247244
let (result, response) = {

0 commit comments

Comments
 (0)