bs_gl_client/lsps/lsps2/
schema.rs1use serde::de::Error as DeError;
2use serde::{Deserialize, Serialize};
3
4use crate::lsps::error::map_json_rpc_error_code_to_str;
5use crate::lsps::json_rpc::MapErrorCode;
6use crate::lsps::lsps0::common_schemas::{IsoDatetime, MsatAmount, ShortChannelId};
7
8const MAX_PROMISE_LEN_BYTES: usize = 512;
9#[derive(Debug)]
10struct Promise {
11 promise: String,
12}
13
14impl Promise {
15 fn new(promise: String) -> Result<Self, String> {
16 if promise.len() <= MAX_PROMISE_LEN_BYTES {
17 Ok(Promise { promise })
18 } else {
19 Err(String::from("Promise exceeds maximum length"))
20 }
21 }
22}
23
24impl Serialize for Promise {
25 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
26 where
27 S: serde::Serializer,
28 {
29 serializer.serialize_str(&self.promise)
30 }
31}
32
33impl<'de> Deserialize<'de> for Promise {
37 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
38 where
39 D: serde::Deserializer<'de>,
40 {
41 let str_repr = String::deserialize(deserializer)?;
42 let promise = Promise::new(str_repr.clone());
43 format!("{:?}", &promise);
44 Promise::new(str_repr.clone())
45 .map_err(|_| D::Error::custom("promise exceeds max length"))
46 }
47}
48
49#[derive(Debug, Serialize, Deserialize)]
50pub struct Lsps2GetVersionsResponse {
51 versions: Vec<u64>,
52}
53
54#[derive(Debug, Serialize, Deserialize)]
55pub struct Lsps2GetInfoRequest {
56 version: i64,
57 token: Option<String>,
58}
59
60#[derive(Debug, Serialize, Deserialize)]
61pub struct Lsps2GetInfoResponse {
62 opening_fee_params_menu: Vec<OpeningFeeParamsMenuItem>,
63 min_payment_size_msat: String,
64 max_payment_size_msat: String,
65}
66
67#[derive(Debug, Serialize, Deserialize)]
68pub struct Lsps2GetInfoError {}
69
70impl MapErrorCode for Lsps2GetInfoError {
71 fn get_code_str(code: i64) -> &'static str {
72 match code {
73 1 => "unsupported_version",
74 2 => "unrecognized_or_stale_token",
75 _ => map_json_rpc_error_code_to_str(code),
76 }
77 }
78}
79
80#[derive(Debug, Serialize, Deserialize)]
81#[serde(deny_unknown_fields)]
82pub struct OpeningFeeParamsMenuItem {
83 min_fee_msat: MsatAmount,
84 proportional: u64,
85 valid_until: IsoDatetime,
86 min_lifetime: u64,
87 max_client_to_self_delay: u64,
88 promise: Promise,
89}
90
91#[derive(Debug, Serialize, Deserialize)]
92pub struct Lsps2BuyRequest {
93 version: String,
94 opening_fee_params: OpeningFeeParamsMenuItem,
95 payment_size_msat: MsatAmount,
96}
97
98#[derive(Debug, Serialize, Deserialize)]
99pub struct Lsps2BuyResponse {
100 jit_channel_scid: ShortChannelId,
101 lsp_cltv_expiry_delta: u64,
102 #[serde(default)]
103 client_trusts_lsp: bool,
104}
105
106#[derive(Debug, Serialize, Deserialize)]
107pub struct Lsps2BuyError {}
108
109impl MapErrorCode for Lsps2BuyError {
110 fn get_code_str(code: i64) -> &'static str {
111 match code {
112 1 => "unsupported_version",
113 2 => "invalid_opening_fee_params",
114 3 => "payment_size_too_small",
115 4 => "payment_size_too_large",
116 _ => map_json_rpc_error_code_to_str(code),
117 }
118 }
119}
120
121#[cfg(test)]
122mod test {
123 use super::*;
124
125 #[test]
126 fn parsing_error_when_opening_fee_menu_has_extra_fields() {
127 let fee_menu_item = serde_json::json!(
130 {
131 "min_fee_msat": "546000",
132 "proportional": 1200,
133 "valid_until": "2023-02-23T08:47:30.511Z",
134 "min_lifetime": 1008,
135 "max_client_to_self_delay": 2016,
136 "promise": "abcdefghijklmnopqrstuvwxyz",
137 "extra_field" : "This shouldn't be their"
138 }
139 );
140
141 let parsed_opening_fee_menu_item: Result<OpeningFeeParamsMenuItem, _> =
142 serde_json::from_value(fee_menu_item);
143 assert!(
144 parsed_opening_fee_menu_item.is_err_and(|x| format!("{}", x).contains("extra_field"))
145 )
146 }
147
148 #[test]
149 fn parse_valid_promise() {
150 let promise_json = "\"abcdefghijklmnopqrstuvwxyz\"";
151 let promise = serde_json::from_str::<Promise>(promise_json).expect("Can parse promise");
152 assert_eq!(promise.promise, "abcdefghijklmnopqrstuvwxyz");
153 }
154
155 #[test]
156 fn parse_too_long_promise_fails() {
157 let a_513_chars = "\"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\"";
162 let a_512_chars = "\"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\"";
163
164 serde_json::from_str::<Promise>(a_512_chars)
165 .expect("Should fail because 512 bytes is the max length");
166 serde_json::from_str::<Promise>(a_513_chars).expect_err("Should fail to parse promise ");
167 }
168
169 #[test]
170 fn client_trust_lsp_defaults_to_false() {
171 let data = serde_json::json!({
172 "jit_channel_scid" : "0x12x12",
173 "lsp_cltv_expiry_delta" : 144
174 });
175
176 let buy_response =
177 serde_json::from_value::<Lsps2BuyResponse>(data).expect("The response can be parsed");
178
179 assert!(
180 !buy_response.client_trusts_lsp,
181 "If the field is absent it assumed the client should not trust the LSP"
182 )
183 }
184}