lsp_primitives/lsps1/
util.rs1use crate::json_rpc::ErrorData;
2use crate::lsps1::schema::{Lsps1CreateOrderRequest, Lsps1Options};
3use anyhow::Result;
4
5use serde::{Deserialize, Serialize};
6
7#[derive(Serialize, Deserialize, Debug, Clone)]
8pub struct Lsps1OptionMismatchError {
9 property: String,
10 message: String,
11}
12
13impl From<Lsps1OptionMismatchError> for ErrorData {
14 fn from(options_error: Lsps1OptionMismatchError) -> Self {
15 match serde_json::to_value(options_error) {
16 Ok(data) => ErrorData {
17 code: 1000,
18 message: "Option mismatch".to_string(),
19 data: Some(data),
20 },
21 Err(e) => ErrorData::internal_error(serde_json::Value::String(format!("{:?}", e))),
22 }
23 }
24}
25
26impl Lsps1OptionMismatchError {
27 fn new(property: String, message: String) -> Self {
28 Self { property, message }
29 }
30}
31
32impl Lsps1CreateOrderRequest {
33 pub fn validate_options(&self, options: &Lsps1Options) -> Result<(), Lsps1OptionMismatchError> {
34 if self.client_balance_sat < options.min_initial_client_balance_sat {
35 return Err(Lsps1OptionMismatchError::new(
36 "min_initial_client_balance_sat".to_string(),
37 format!("You've requested client_balance_sat={} but the LSP-server requires at least {}",
38 self.client_balance_sat,
39 options.min_initial_client_balance_sat)));
40 }
41
42 if self.client_balance_sat > options.max_initial_client_balance_sat {
43 return Err(Lsps1OptionMismatchError::new(
44 "max_initial_client_balance_sat".to_string(),
45 format!("You've requested client_balance_sat={} but the LSP-server doesn't allow this value to exceed {}",
46 self.client_balance_sat,
47 options.max_initial_client_balance_sat)));
48 }
49
50 if self.lsp_balance_sat < options.min_initial_lsp_balance_sat {
51 return Err(Lsps1OptionMismatchError::new(
52 "min_initial_lsp_balance_sat".to_string(),
53 format!("You've requested a channel with lsp_balance_sat={} but the LSP-server requires at least {}",
54 self.lsp_balance_sat,
55 options.min_initial_lsp_balance_sat)));
56 }
57
58 if self.lsp_balance_sat > options.max_initial_lsp_balance_sat {
59 return Err(Lsps1OptionMismatchError::new(
60 "max_initial_lsp_balance_sat".to_string(),
61 format!("You've requested a channel with lsp_balance_sat={} but the LSP-server doesn't allow this value to exceed {}",
62 self.lsp_balance_sat,
63 options.max_initial_lsp_balance_sat)));
64 }
65
66 let capacity_option = self.lsp_balance_sat.checked_add(&self.client_balance_sat);
69 let capacity = match capacity_option {
70 Some(c) => c,
71 None => {
72 return Err(Lsps1OptionMismatchError::new(
73 "max_channel_balance_sat".to_string(),
74 "Overflow when computing channel_capacity".to_string(),
75 ));
76 }
77 };
78
79 if capacity < options.min_channel_balance_sat {
80 return Err(Lsps1OptionMismatchError::new(
81 "min_channel_balance_sat".to_string(),
82 format!("You've requested a channel with capacity={} but the LSP-server requires at least {}",
83 capacity,
84 options.min_channel_balance_sat
85 )));
86 };
87
88 if capacity > options.max_channel_balance_sat {
89 return Err(Lsps1OptionMismatchError::new(
90 "max_channel_balance_sat".to_string(),
91 format!("You've requested a channel with capacity={} but the LSP-server only allows values up to {}",
92 capacity,
93 options.max_channel_balance_sat
94 )));
95 }
96
97 if self.channel_expiry_blocks > options.max_channel_expiry_blocks {
99 return Err(Lsps1OptionMismatchError::new(
100 "max_channel_expiry_blocks".to_string(),
101 format!("You've requested to lease a channel for channel_expiry_block={} but the LSP-server only allows max_channel_expiry_blocks={}",
102 self.channel_expiry_blocks,
103 options.max_channel_expiry_blocks
104 )));
105 }
106 Ok(())
107 }
108}
109
110#[cfg(test)]
111mod tests {
112
113 use crate::lsps0::common_schemas::SatAmount;
114 use crate::lsps1::builders::{Lsps1CreateOrderRequestBuilder, Lsps1OptionsBuilder};
115
116 fn get_options_builder() -> Lsps1OptionsBuilder {
117 Lsps1OptionsBuilder::new()
118 .minimum_channel_confirmations(0)
119 .minimum_onchain_payment_confirmations(None)
120 .supports_zero_channel_reserve(true)
121 .min_onchain_payment_size_sat(None)
122 .max_channel_expiry_blocks(1_000)
123 .min_initial_client_balance_sat(SatAmount::new(0))
124 .max_initial_client_balance_sat(SatAmount::new(0))
125 .min_initial_lsp_balance_sat(SatAmount::new(100_000))
126 .max_initial_lsp_balance_sat(SatAmount::new(100_000_000))
127 .min_channel_balance_sat(SatAmount::new(100_000))
128 .max_channel_balance_sat(SatAmount::new(100_000_000))
129 }
130
131 fn get_order_builder() -> Lsps1CreateOrderRequestBuilder {
132 Lsps1CreateOrderRequestBuilder::new()
133 .client_balance_sat(Some(SatAmount::new(0)))
134 .lsp_balance_sat(SatAmount::new(500_000))
135 .confirms_within_blocks(Some(6))
136 .channel_expiry_blocks(1_000)
137 .token(None)
138 .refund_onchain_address(None)
139 .announce_channel(Some(false))
140 }
141
142 #[test]
143 fn test_validate_order_against_options_okay() {
144 let options = get_options_builder().build().unwrap();
145 let order = get_order_builder().build().unwrap();
146
147 order.validate_options(&options).unwrap();
149 }
150
151 #[test]
152 fn test_validate_order_against_options_lsp_balance_sat_error() {
153 let options = get_options_builder()
154 .min_initial_lsp_balance_sat(SatAmount::new(1_000))
155 .max_initial_lsp_balance_sat(SatAmount::new(100_000))
156 .build()
157 .unwrap();
158 let order_1 = get_order_builder()
159 .lsp_balance_sat(SatAmount::new(0))
160 .build()
161 .unwrap();
162
163 let order_2 = get_order_builder()
164 .lsp_balance_sat(SatAmount::new(999_999))
165 .build()
166 .unwrap();
167
168 let err1 = order_1.validate_options(&options).unwrap_err();
169 let err2 = order_2.validate_options(&options).unwrap_err();
170
171 assert_eq!(err1.property, "min_initial_lsp_balance_sat");
172 assert_eq!(err2.property, "max_initial_lsp_balance_sat");
173 }
174
175 #[test]
176 fn test_validate_order_against_options_client_balance_sat_error() {
177 let options = get_options_builder()
178 .min_initial_client_balance_sat(SatAmount::new(1_000))
179 .max_initial_client_balance_sat(SatAmount::new(100_000))
180 .build()
181 .unwrap();
182
183 let order_1 = get_order_builder()
184 .client_balance_sat(Some(SatAmount::new(0)))
185 .build()
186 .unwrap();
187
188 let order_2 = get_order_builder()
189 .client_balance_sat(Some(SatAmount::new(999_999)))
190 .build()
191 .unwrap();
192
193 let err1 = order_1.validate_options(&options).unwrap_err();
194 let err2 = order_2.validate_options(&options).unwrap_err();
195
196 assert_eq!(err1.property, "min_initial_client_balance_sat");
197 assert_eq!(err2.property, "max_initial_client_balance_sat");
198 }
199
200 #[test]
201 fn test_validate_order_against_capacity_from_options_error() {
202 let options = get_options_builder()
203 .min_initial_client_balance_sat(SatAmount::new(1_000))
204 .max_initial_client_balance_sat(SatAmount::new(100_000))
205 .min_initial_lsp_balance_sat(SatAmount::new(1_000))
206 .max_initial_lsp_balance_sat(SatAmount::new(100_000))
207 .min_channel_balance_sat(SatAmount::new(10_000))
208 .max_channel_balance_sat(SatAmount::new(100_000))
209 .build()
210 .unwrap();
211
212 let order_1 = get_order_builder()
213 .client_balance_sat(Some(SatAmount::new(1_000)))
214 .lsp_balance_sat(SatAmount::new(1_000))
215 .build()
216 .unwrap();
217
218 let order_2 = get_order_builder()
219 .client_balance_sat(Some(SatAmount::new(100_000)))
220 .lsp_balance_sat(SatAmount::new(100_000))
221 .build()
222 .unwrap();
223
224 let err1 = order_1.validate_options(&options).unwrap_err();
225 let err2 = order_2.validate_options(&options).unwrap_err();
226
227 assert_eq!(err1.property, "min_channel_balance_sat");
228 assert_eq!(err2.property, "max_channel_balance_sat");
229 }
230}