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

Commit f9bb473

Browse files
authored
Merge pull request #109 from tnull/2024-02-custom-serde-strings
De-/serialize all amount fields as JSON strings
2 parents 71b9dd2 + 9d4f1b3 commit f9bb473

File tree

4 files changed

+162
-1
lines changed

4 files changed

+162
-1
lines changed

src/lsps0/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,4 +12,5 @@
1212
pub mod client;
1313
pub mod event;
1414
pub mod msgs;
15+
pub(crate) mod ser;
1516
pub mod service;

src/lsps0/ser.rs

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
pub(crate) mod string_amount {
2+
use crate::prelude::{String, ToString};
3+
use core::str::FromStr;
4+
use serde::de::Unexpected;
5+
use serde::{Deserialize, Deserializer, Serializer};
6+
7+
pub(crate) fn serialize<S>(x: &u64, s: S) -> Result<S::Ok, S::Error>
8+
where
9+
S: Serializer,
10+
{
11+
s.serialize_str(&x.to_string())
12+
}
13+
14+
pub(crate) fn deserialize<'de, D>(deserializer: D) -> Result<u64, D::Error>
15+
where
16+
D: Deserializer<'de>,
17+
{
18+
let buf = String::deserialize(deserializer)?;
19+
20+
u64::from_str(&buf).map_err(|_| {
21+
serde::de::Error::invalid_value(Unexpected::Str(&buf), &"invalid u64 amount string")
22+
})
23+
}
24+
}
25+
26+
pub(crate) mod string_amount_option {
27+
use crate::prelude::{String, ToString};
28+
use core::str::FromStr;
29+
use serde::de::Unexpected;
30+
use serde::{Deserialize, Deserializer, Serialize, Serializer};
31+
32+
pub(crate) fn serialize<S>(x: &Option<u64>, s: S) -> Result<S::Ok, S::Error>
33+
where
34+
S: Serializer,
35+
{
36+
let v = x.as_ref().map(|v| v.to_string());
37+
Option::<String>::serialize(&v, s)
38+
}
39+
40+
pub(crate) fn deserialize<'de, D>(deserializer: D) -> Result<Option<u64>, D::Error>
41+
where
42+
D: Deserializer<'de>,
43+
{
44+
if let Some(buf) = Option::<String>::deserialize(deserializer)? {
45+
let val = u64::from_str(&buf).map_err(|_| {
46+
serde::de::Error::invalid_value(Unexpected::Str(&buf), &"invalid u64 amount string")
47+
})?;
48+
Ok(Some(val))
49+
} else {
50+
Ok(None)
51+
}
52+
}
53+
}

src/lsps1/msgs.rs

Lines changed: 54 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
//! Message, request, and other primitive types used to implement LSPS1.
22
33
use crate::lsps0::msgs::{LSPSMessage, RequestId, ResponseError};
4+
use crate::lsps0::ser::{string_amount, string_amount_option};
5+
46
use crate::prelude::{String, Vec};
57

68
use serde::{Deserialize, Serialize};
@@ -41,20 +43,27 @@ pub struct OptionsSupported {
4143
pub supports_zero_channel_reserve: bool,
4244
/// Indicates the minimum amount of satoshi that is required for the LSP to accept a payment
4345
/// on-chain.
44-
pub min_onchain_payment_size_sat: Option<u32>,
46+
#[serde(with = "string_amount_option")]
47+
pub min_onchain_payment_size_sat: Option<u64>,
4548
/// The maximum number of blocks a channel can be leased for.
4649
pub max_channel_expiry_blocks: u32,
4750
/// The minimum number of satoshi that the client MUST request.
51+
#[serde(with = "string_amount")]
4852
pub min_initial_client_balance_sat: u64,
4953
/// The maximum number of satoshi that the client MUST request.
54+
#[serde(with = "string_amount")]
5055
pub max_initial_client_balance_sat: u64,
5156
/// The minimum number of satoshi that the LSP will provide to the channel.
57+
#[serde(with = "string_amount")]
5258
pub min_initial_lsp_balance_sat: u64,
5359
/// The maximum number of satoshi that the LSP will provide to the channel.
60+
#[serde(with = "string_amount")]
5461
pub max_initial_lsp_balance_sat: u64,
5562
/// The minimal channel size.
63+
#[serde(with = "string_amount")]
5664
pub min_channel_balance_sat: u64,
5765
/// The maximal channel size.
66+
#[serde(with = "string_amount")]
5867
pub max_channel_balance_sat: u64,
5968
}
6069

@@ -81,11 +90,13 @@ pub struct CreateOrderRequest {
8190
#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
8291
pub struct OrderParams {
8392
/// Indicates how many satoshi the LSP will provide on their side.
93+
#[serde(with = "string_amount")]
8494
pub lsp_balance_sat: u64,
8595
/// Indicates how many satoshi the client will provide on their side.
8696
///
8797
/// The client sends these funds to the LSP, who will push them back to the client upon opening
8898
/// the channel.
99+
#[serde(with = "string_amount")]
89100
pub client_balance_sat: u64,
90101
/// The number of blocks the client wants to wait maximally for the channel to be confirmed.
91102
pub confirms_within_blocks: u32,
@@ -135,8 +146,10 @@ pub struct OrderPayment {
135146
/// Indicates the current state of the payment.
136147
pub state: PaymentState,
137148
/// The total fee the LSP will charge to open this channel in satoshi.
149+
#[serde(with = "string_amount")]
138150
pub fee_total_sat: u64,
139151
/// What the client needs to pay in total to open the requested channel.
152+
#[serde(with = "string_amount")]
140153
pub order_total_sat: u64,
141154
/// A BOLT11 invoice the client can pay to have to channel opened.
142155
pub bolt11_invoice: String,
@@ -172,6 +185,7 @@ pub struct OnchainPayment {
172185
/// The outpoint of the payment.
173186
pub outpoint: String,
174187
/// The amount of satoshi paid.
188+
#[serde(with = "string_amount")]
175189
pub sat: u64,
176190
/// Indicates if the LSP regards the transaction as sufficiently confirmed.
177191
pub confirmed: bool,
@@ -287,3 +301,42 @@ impl From<LSPS1Message> for LSPSMessage {
287301
LSPSMessage::LSPS1(message)
288302
}
289303
}
304+
305+
#[cfg(test)]
306+
mod tests {
307+
use super::*;
308+
use crate::alloc::string::ToString;
309+
310+
#[test]
311+
fn options_supported_serialization() {
312+
let min_channel_confirmations = 6;
313+
let min_onchain_payment_confirmations = Some(6);
314+
let supports_zero_channel_reserve = true;
315+
let min_onchain_payment_size_sat = Some(100_000);
316+
let max_channel_expiry_blocks = 144;
317+
let min_initial_client_balance_sat = 10_000_000;
318+
let max_initial_client_balance_sat = 100_000_000;
319+
let min_initial_lsp_balance_sat = 100_000;
320+
let max_initial_lsp_balance_sat = 100_000_000;
321+
let min_channel_balance_sat = 100_000;
322+
let max_channel_balance_sat = 100_000_000;
323+
324+
let options_supported = OptionsSupported {
325+
min_channel_confirmations,
326+
min_onchain_payment_confirmations,
327+
supports_zero_channel_reserve,
328+
min_onchain_payment_size_sat,
329+
max_channel_expiry_blocks,
330+
min_initial_client_balance_sat,
331+
max_initial_client_balance_sat,
332+
min_initial_lsp_balance_sat,
333+
max_initial_lsp_balance_sat,
334+
min_channel_balance_sat,
335+
max_channel_balance_sat,
336+
};
337+
338+
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}"#;
339+
assert_eq!(json_str, serde_json::json!(options_supported).to_string());
340+
assert_eq!(options_supported, serde_json::from_str(json_str).unwrap());
341+
}
342+
}

src/lsps2/msgs.rs

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ use chrono::Utc;
99
use serde::{Deserialize, Serialize};
1010

1111
use crate::lsps0::msgs::{LSPSMessage, RequestId, ResponseError};
12+
use crate::lsps0::ser::{string_amount, string_amount_option};
1213
use crate::prelude::{String, Vec};
1314
use crate::utils;
1415

@@ -82,6 +83,7 @@ impl RawOpeningFeeParams {
8283
/// the promise using the secret. Once verified they can be confident it was not modified by the client.
8384
pub struct OpeningFeeParams {
8485
/// The minimum fee required for the channel open.
86+
#[serde(with = "string_amount")]
8587
pub min_fee_msat: u64,
8688
/// A fee proportional to the size of the initial payment.
8789
pub proportional: u32,
@@ -92,8 +94,10 @@ pub struct OpeningFeeParams {
9294
/// The maximum number of blocks that the client is allowed to set its `to_self_delay` parameter.
9395
pub max_client_to_self_delay: u32,
9496
/// The minimum payment size that the LSP will accept when opening a channel.
97+
#[serde(with = "string_amount")]
9598
pub min_payment_size_msat: u64,
9699
/// The maximum payment size that the LSP will accept when opening a channel.
100+
#[serde(with = "string_amount")]
97101
pub max_payment_size_msat: u64,
98102
/// The HMAC used to verify the authenticity of these parameters.
99103
pub promise: String,
@@ -112,7 +116,9 @@ pub struct BuyRequest {
112116
/// The fee parameters you would like to use.
113117
pub opening_fee_params: OpeningFeeParams,
114118
/// The size of the initial payment you expect to receive.
119+
#[serde(default)]
115120
#[serde(skip_serializing_if = "Option::is_none")]
121+
#[serde(with = "string_amount_option")]
116122
pub payment_size_msat: Option<u64>,
117123
}
118124

@@ -213,6 +219,7 @@ impl From<LSPS2Message> for LSPSMessage {
213219
#[cfg(test)]
214220
mod tests {
215221
use super::*;
222+
use crate::alloc::string::ToString;
216223
use crate::lsps2::utils::is_valid_opening_fee_params;
217224

218225
#[test]
@@ -330,4 +337,51 @@ mod tests {
330337
let opening_fee_params = raw.into_opening_fee_params(&promise_secret);
331338
assert!(!is_valid_opening_fee_params(&opening_fee_params, &promise_secret));
332339
}
340+
341+
#[test]
342+
fn buy_request_serialization() {
343+
let min_fee_msat = 100;
344+
let proportional = 21;
345+
let valid_until = chrono::DateTime::parse_from_rfc3339("2023-05-20T08:30:45Z").unwrap();
346+
let min_lifetime = 144;
347+
let max_client_to_self_delay = 128;
348+
let min_payment_size_msat = 1;
349+
let max_payment_size_msat = 100_000_000;
350+
351+
let raw = RawOpeningFeeParams {
352+
min_fee_msat,
353+
proportional,
354+
valid_until: valid_until.into(),
355+
min_lifetime,
356+
max_client_to_self_delay,
357+
min_payment_size_msat,
358+
max_payment_size_msat,
359+
};
360+
361+
let promise_secret = [1u8; 32];
362+
363+
let opening_fee_params = raw.into_opening_fee_params(&promise_secret);
364+
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":"1134a5c51e3ba2e8f4259610d5e12c1bf4c50ddcd3f8af563e0a00d1fff41dea","proportional":21,"valid_until":"2023-05-20T08:30:45Z"}"#;
365+
assert_eq!(json_str, serde_json::json!(opening_fee_params).to_string());
366+
assert_eq!(opening_fee_params, serde_json::from_str(json_str).unwrap());
367+
368+
let payment_size_msat = Some(1234);
369+
let buy_request_fixed =
370+
BuyRequest { opening_fee_params: opening_fee_params.clone(), payment_size_msat };
371+
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":"1134a5c51e3ba2e8f4259610d5e12c1bf4c50ddcd3f8af563e0a00d1fff41dea","proportional":21,"valid_until":"2023-05-20T08:30:45Z"},"payment_size_msat":"1234"}"#;
372+
assert_eq!(json_str, serde_json::json!(buy_request_fixed).to_string());
373+
assert_eq!(buy_request_fixed, serde_json::from_str(json_str).unwrap());
374+
375+
let payment_size_msat = None;
376+
let buy_request_variable = BuyRequest { opening_fee_params, payment_size_msat };
377+
378+
// Check we skip serialization if payment_size_msat is None.
379+
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":"1134a5c51e3ba2e8f4259610d5e12c1bf4c50ddcd3f8af563e0a00d1fff41dea","proportional":21,"valid_until":"2023-05-20T08:30:45Z"}}"#;
380+
assert_eq!(json_str, serde_json::json!(buy_request_variable).to_string());
381+
assert_eq!(buy_request_variable, serde_json::from_str(json_str).unwrap());
382+
383+
// Check we still deserialize correctly if payment_size_msat is 'null'.
384+
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":"1134a5c51e3ba2e8f4259610d5e12c1bf4c50ddcd3f8af563e0a00d1fff41dea","proportional":21,"valid_until":"2023-05-20T08:30:45Z"},"payment_size_msat":null}"#;
385+
assert_eq!(buy_request_variable, serde_json::from_str(json_str).unwrap());
386+
}
333387
}

0 commit comments

Comments
 (0)