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 Promise::new(str_repr.clone()).map_err(|_| D::Error::custom("promise exceeds max length"))
43 }
44}
45
46#[derive(Debug, Serialize, Deserialize)]
47pub struct Lsps2GetVersionsResponse {
48 versions: Vec<u64>,
49}
50
51#[derive(Debug, Serialize, Deserialize)]
52pub struct Lsps2GetInfoRequest {
53 version: i64,
54 token: Option<String>,
55}
56
57#[derive(Debug, Serialize, Deserialize)]
58pub struct Lsps2GetInfoResponse {
59 opening_fee_params_menu: Vec<OpeningFeeParamsMenuItem>,
60 min_payment_size_msat: String,
61 max_payment_size_msat: String,
62}
63
64#[derive(Debug, Serialize, Deserialize)]
65pub struct Lsps2GetInfoError {}
66
67impl MapErrorCode for Lsps2GetInfoError {
68 fn get_code_str(code: i64) -> &'static str {
69 match code {
70 1 => "unsupported_version",
71 2 => "unrecognized_or_stale_token",
72 _ => map_json_rpc_error_code_to_str(code),
73 }
74 }
75}
76
77#[derive(Debug, Serialize, Deserialize)]
78#[serde(deny_unknown_fields)]
79pub struct OpeningFeeParamsMenuItem {
80 min_fee_msat: MsatAmount,
81 proportional: u64,
82 valid_until: IsoDatetime,
83 min_lifetime: u64,
84 max_client_to_self_delay: u64,
85 promise: Promise,
86}
87
88#[derive(Debug, Serialize, Deserialize)]
89pub struct Lsps2BuyRequest {
90 version: String,
91 opening_fee_params: OpeningFeeParamsMenuItem,
92 payment_size_msat: MsatAmount,
93}
94
95#[derive(Debug, Serialize, Deserialize)]
96pub struct Lsps2BuyResponse {
97 jit_channel_scid: ShortChannelId,
98 lsp_cltv_expiry_delta: u64,
99 #[serde(default)]
100 client_trusts_lsp: bool,
101}
102
103#[derive(Debug, Serialize, Deserialize)]
104pub struct Lsps2BuyError {}
105
106impl MapErrorCode for Lsps2BuyError {
107 fn get_code_str(code: i64) -> &'static str {
108 match code {
109 1 => "unsupported_version",
110 2 => "invalid_opening_fee_params",
111 3 => "payment_size_too_small",
112 4 => "payment_size_too_large",
113 _ => map_json_rpc_error_code_to_str(code),
114 }
115 }
116}
117
118#[cfg(test)]
119mod test {
120 use super::*;
121
122 #[test]
123 fn parsing_error_when_opening_fee_menu_has_extra_fields() {
124 let fee_menu_item = serde_json::json!(
127 {
128 "min_fee_msat": "546000",
129 "proportional": 1200,
130 "valid_until": "2023-02-23T08:47:30.511Z",
131 "min_lifetime": 1008,
132 "max_client_to_self_delay": 2016,
133 "promise": "abcdefghijklmnopqrstuvwxyz",
134 "extra_field" : "This shouldn't be their"
135 }
136 );
137
138 let parsed_opening_fee_menu_item: Result<OpeningFeeParamsMenuItem, _> =
139 serde_json::from_value(fee_menu_item);
140 assert!(
141 parsed_opening_fee_menu_item.is_err_and(|x| format!("{}", x).contains("extra_field"))
142 )
143 }
144
145 #[test]
146 fn parse_valid_promise() {
147 let promise_json = "\"abcdefghijklmnopqrstuvwxyz\"";
148 let promise = serde_json::from_str::<Promise>(promise_json).expect("Can parse promise");
149 assert_eq!(promise.promise, "abcdefghijklmnopqrstuvwxyz");
150 }
151
152 #[test]
153 fn parse_too_long_promise_fails() {
154 let a_513_chars = "\"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\"";
159 let a_512_chars = "\"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\"";
160
161 serde_json::from_str::<Promise>(a_512_chars)
162 .expect("Should fail because 512 bytes is the max length");
163 serde_json::from_str::<Promise>(a_513_chars).expect_err("Should fail to parse promise ");
164 }
165
166 #[test]
167 fn client_trust_lsp_defaults_to_false() {
168 let data = serde_json::json!({
169 "jit_channel_scid" : "0x12x12",
170 "lsp_cltv_expiry_delta" : 144
171 });
172
173 let buy_response =
174 serde_json::from_value::<Lsps2BuyResponse>(data).expect("The response can be parsed");
175
176 assert!(
177 !buy_response.client_trusts_lsp,
178 "If the field is absent it assumed the client should not trust the LSP"
179 )
180 }
181}