lightning_liquidity/lsps2/
msgs.rs

1// This file is Copyright its original authors, visible in version control
2// history.
3//
4// This file is licensed under the Apache License, Version 2.0 <LICENSE-APACHE
5// or http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
6// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your option.
7// You may not use this file except in accordance with one or both of these
8// licenses.
9
10//! Message, request, and other primitive types used to implement bLIP-52 / LSPS2.
11
12use alloc::string::String;
13use alloc::vec::Vec;
14
15use core::convert::TryFrom;
16
17use bitcoin::hashes::hmac::{Hmac, HmacEngine};
18use bitcoin::hashes::sha256::Hash as Sha256;
19use bitcoin::hashes::{Hash, HashEngine};
20use bitcoin::secp256k1::PublicKey;
21
22use serde::{Deserialize, Serialize};
23
24use lightning::impl_writeable_tlv_based;
25use lightning::util::scid_utils;
26
27use crate::lsps0::ser::{
28	string_amount, string_amount_option, LSPSDateTime, LSPSMessage, LSPSRequestId,
29	LSPSResponseError,
30};
31use crate::utils;
32
33pub(crate) const LSPS2_GET_INFO_METHOD_NAME: &str = "lsps2.get_info";
34pub(crate) const LSPS2_BUY_METHOD_NAME: &str = "lsps2.buy";
35
36pub(crate) const LSPS2_GET_INFO_REQUEST_UNRECOGNIZED_OR_STALE_TOKEN_ERROR_CODE: i32 = 200;
37
38pub(crate) const LSPS2_BUY_REQUEST_INVALID_OPENING_FEE_PARAMS_ERROR_CODE: i32 = 201;
39pub(crate) const LSPS2_BUY_REQUEST_PAYMENT_SIZE_TOO_SMALL_ERROR_CODE: i32 = 202;
40pub(crate) const LSPS2_BUY_REQUEST_PAYMENT_SIZE_TOO_LARGE_ERROR_CODE: i32 = 203;
41
42#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
43/// A request made to an LSP to learn their current channel fees and parameters.
44pub struct LSPS2GetInfoRequest {
45	/// An optional token to provide to the LSP.
46	pub token: Option<String>,
47}
48
49/// Fees and parameters for a JIT Channel without the promise.
50///
51/// The promise will be calculated automatically for the LSP and this type converted
52/// into an [`LSPS2OpeningFeeParams`] for transit over the wire.
53pub struct LSPS2RawOpeningFeeParams {
54	/// The minimum fee required for the channel open.
55	pub min_fee_msat: u64,
56	/// A fee proportional to the size of the initial payment.
57	pub proportional: u32,
58	/// An [`ISO8601`](https://www.iso.org/iso-8601-date-and-time-format.html) formatted date for which these params are valid.
59	pub valid_until: LSPSDateTime,
60	/// The number of blocks after confirmation that the LSP promises it will keep the channel alive without closing.
61	pub min_lifetime: u32,
62	/// The maximum number of blocks that the client is allowed to set its `to_self_delay` parameter.
63	pub max_client_to_self_delay: u32,
64	/// The minimum payment size that the LSP will accept when opening a channel.
65	pub min_payment_size_msat: u64,
66	/// The maximum payment size that the LSP will accept when opening a channel.
67	pub max_payment_size_msat: u64,
68}
69
70impl LSPS2RawOpeningFeeParams {
71	pub(crate) fn into_opening_fee_params(
72		self, promise_secret: &[u8; 32], counterparty_node_id: &PublicKey,
73	) -> LSPS2OpeningFeeParams {
74		let mut hmac = HmacEngine::<Sha256>::new(promise_secret);
75		hmac.input(&counterparty_node_id.serialize());
76		hmac.input(&self.min_fee_msat.to_be_bytes());
77		hmac.input(&self.proportional.to_be_bytes());
78		hmac.input(self.valid_until.to_rfc3339().as_bytes());
79		hmac.input(&self.min_lifetime.to_be_bytes());
80		hmac.input(&self.max_client_to_self_delay.to_be_bytes());
81		hmac.input(&self.min_payment_size_msat.to_be_bytes());
82		hmac.input(&self.max_payment_size_msat.to_be_bytes());
83		let promise_bytes = Hmac::from_engine(hmac).to_byte_array();
84		let promise = utils::hex_str(&promise_bytes[..]);
85		LSPS2OpeningFeeParams {
86			min_fee_msat: self.min_fee_msat,
87			proportional: self.proportional,
88			valid_until: self.valid_until,
89			min_lifetime: self.min_lifetime,
90			max_client_to_self_delay: self.max_client_to_self_delay,
91			min_payment_size_msat: self.min_payment_size_msat,
92			max_payment_size_msat: self.max_payment_size_msat,
93			promise,
94		}
95	}
96}
97
98#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
99/// Fees and parameters for a JIT Channel including the promise.
100///
101/// The promise is an HMAC calculated using a secret known to the LSP and the rest of the fields as input.
102/// It exists so the LSP can verify the authenticity of a client provided LSPS2OpeningFeeParams by recalculating
103/// the promise using the secret. Once verified they can be confident it was not modified by the client.
104pub struct LSPS2OpeningFeeParams {
105	/// The minimum fee required for the channel open.
106	#[serde(with = "string_amount")]
107	pub min_fee_msat: u64,
108	/// A fee proportional to the size of the initial payment.
109	pub proportional: u32,
110	/// An [`ISO8601`](https://www.iso.org/iso-8601-date-and-time-format.html) formatted date for which these params are valid.
111	pub valid_until: LSPSDateTime,
112	/// The number of blocks after confirmation that the LSP promises it will keep the channel alive without closing.
113	pub min_lifetime: u32,
114	/// The maximum number of blocks that the client is allowed to set its `to_self_delay` parameter.
115	pub max_client_to_self_delay: u32,
116	/// The minimum payment size that the LSP will accept when opening a channel.
117	#[serde(with = "string_amount")]
118	pub min_payment_size_msat: u64,
119	/// The maximum payment size that the LSP will accept when opening a channel.
120	#[serde(with = "string_amount")]
121	pub max_payment_size_msat: u64,
122	/// The HMAC used to verify the authenticity of these parameters.
123	pub promise: String,
124}
125
126impl_writeable_tlv_based!(LSPS2OpeningFeeParams, {
127	(0, min_fee_msat, required),
128	(2, proportional, required),
129	(4, valid_until, required),
130	(6, min_lifetime, required),
131	(8, max_client_to_self_delay, required),
132	(10, min_payment_size_msat, required),
133	(12, max_payment_size_msat, required),
134	(14, promise, required),
135});
136
137/// A response to a [`LSPS2GetInfoRequest`]
138#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
139pub struct LSPS2GetInfoResponse {
140	/// A set of opening fee parameters.
141	pub opening_fee_params_menu: Vec<LSPS2OpeningFeeParams>,
142}
143
144/// A request to buy a JIT channel.
145#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
146pub struct LSPS2BuyRequest {
147	/// The fee parameters you would like to use.
148	pub opening_fee_params: LSPS2OpeningFeeParams,
149	/// The size of the initial payment you expect to receive.
150	#[serde(default)]
151	#[serde(skip_serializing_if = "Option::is_none")]
152	#[serde(with = "string_amount_option")]
153	pub payment_size_msat: Option<u64>,
154}
155
156/// A newtype that holds a `short_channel_id` in human readable format of BBBxTTTx000.
157#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
158pub struct LSPS2InterceptScid(String);
159
160impl From<u64> for LSPS2InterceptScid {
161	fn from(scid: u64) -> Self {
162		let block = scid_utils::block_from_scid(scid);
163		let tx_index = scid_utils::tx_index_from_scid(scid);
164		let vout = scid_utils::vout_from_scid(scid);
165
166		Self(format!("{}x{}x{}", block, tx_index, vout))
167	}
168}
169
170impl LSPS2InterceptScid {
171	/// Try to convert a [`LSPS2InterceptScid`] into a u64 used by LDK.
172	pub fn to_scid(&self) -> Result<u64, ()> {
173		utils::scid_from_human_readable_string(&self.0)
174	}
175}
176
177/// A response to a [`LSPS2BuyRequest`].
178///
179/// Includes information needed to construct an invoice.
180#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
181pub struct LSPS2BuyResponse {
182	/// The intercept short channel id used by LSP to identify need to open channel.
183	pub jit_channel_scid: LSPS2InterceptScid,
184	/// The locktime expiry delta the lsp requires.
185	pub lsp_cltv_expiry_delta: u32,
186	/// Trust model flag (default: false).
187	///
188	/// false => "LSP trusts client": LSP immediately (or as soon as safe) broadcasts the
189	///          funding transaction; client may wait for broadcast / confirmations
190	///          before revealing the preimage.
191	/// true  => "Client trusts LSP": LSP may defer broadcasting until after the client
192	///          reveals the preimage; client MUST send the preimage once HTLC(s) are
193	///          irrevocably committed.
194	#[serde(default)]
195	pub client_trusts_lsp: bool,
196}
197
198#[derive(Clone, Debug, PartialEq, Eq)]
199/// An enum that captures all the valid JSON-RPC requests in the bLIP-52 / LSPS2 protocol.
200pub enum LSPS2Request {
201	/// A request to learn an LSP's channel fees and parameters.
202	GetInfo(LSPS2GetInfoRequest),
203	/// A request to buy a JIT channel from an LSP.
204	Buy(LSPS2BuyRequest),
205}
206
207#[derive(Clone, Debug, PartialEq, Eq)]
208/// An enum that captures all the valid JSON-RPC responses in the bLIP-52 / LSPS2 protocol.
209pub enum LSPS2Response {
210	/// A successful response to a [`LSPS2Request::GetInfo`] request.
211	GetInfo(LSPS2GetInfoResponse),
212	/// An error response to a [`LSPS2Request::GetInfo`] request.
213	GetInfoError(LSPSResponseError),
214	/// A successful response to a [`LSPS2Request::Buy`] request.
215	Buy(LSPS2BuyResponse),
216	/// An error response to a [`LSPS2Request::Buy`] request.
217	BuyError(LSPSResponseError),
218}
219
220#[derive(Clone, Debug, PartialEq, Eq)]
221/// An enum that captures all valid JSON-RPC messages in the bLIP-52 / LSPS2 protocol.
222pub enum LSPS2Message {
223	/// An LSPS2 JSON-RPC request.
224	Request(LSPSRequestId, LSPS2Request),
225	/// An LSPS2 JSON-RPC response.
226	Response(LSPSRequestId, LSPS2Response),
227}
228
229impl TryFrom<LSPSMessage> for LSPS2Message {
230	type Error = ();
231
232	fn try_from(message: LSPSMessage) -> Result<Self, Self::Error> {
233		if let LSPSMessage::LSPS2(message) = message {
234			return Ok(message);
235		}
236
237		Err(())
238	}
239}
240
241impl From<LSPS2Message> for LSPSMessage {
242	fn from(message: LSPS2Message) -> Self {
243		LSPSMessage::LSPS2(message)
244	}
245}
246
247#[cfg(test)]
248mod tests {
249	use super::*;
250
251	use crate::alloc::string::ToString;
252	use crate::lsps2::utils::is_valid_opening_fee_params;
253
254	use bitcoin::secp256k1::{Secp256k1, SecretKey};
255
256	use core::str::FromStr;
257
258	#[test]
259	fn into_opening_fee_params_produces_valid_promise() {
260		let min_fee_msat = 100;
261		let proportional = 21;
262		let valid_until = LSPSDateTime::from_str("2035-05-20T08:30:45Z").unwrap();
263		let min_lifetime = 144;
264		let max_client_to_self_delay = 128;
265		let min_payment_size_msat = 1;
266		let max_payment_size_msat = 100_000_000;
267
268		let raw = LSPS2RawOpeningFeeParams {
269			min_fee_msat,
270			proportional,
271			valid_until: valid_until.into(),
272			min_lifetime,
273			max_client_to_self_delay,
274			min_payment_size_msat,
275			max_payment_size_msat,
276		};
277
278		let promise_secret = [1u8; 32];
279		let client_node_id = PublicKey::from_secret_key(
280			&Secp256k1::new(),
281			&SecretKey::from_slice(&[0xcd; 32]).unwrap(),
282		);
283
284		let opening_fee_params = raw.into_opening_fee_params(&promise_secret, &client_node_id);
285
286		assert_eq!(opening_fee_params.min_fee_msat, min_fee_msat);
287		assert_eq!(opening_fee_params.proportional, proportional);
288		assert_eq!(opening_fee_params.valid_until, valid_until);
289		assert_eq!(opening_fee_params.min_lifetime, min_lifetime);
290		assert_eq!(opening_fee_params.max_client_to_self_delay, max_client_to_self_delay);
291		assert_eq!(opening_fee_params.min_payment_size_msat, min_payment_size_msat);
292		assert_eq!(opening_fee_params.max_payment_size_msat, max_payment_size_msat);
293
294		assert!(is_valid_opening_fee_params(&opening_fee_params, &promise_secret, &client_node_id));
295	}
296
297	#[test]
298	fn changing_single_field_produced_invalid_params() {
299		let min_fee_msat = 100;
300		let proportional = 21;
301		let valid_until = LSPSDateTime::from_str("2035-05-20T08:30:45Z").unwrap();
302		let min_lifetime = 144;
303		let max_client_to_self_delay = 128;
304		let min_payment_size_msat = 1;
305		let max_payment_size_msat = 100_000_000;
306
307		let raw = LSPS2RawOpeningFeeParams {
308			min_fee_msat,
309			proportional,
310			valid_until,
311			min_lifetime,
312			max_client_to_self_delay,
313			min_payment_size_msat,
314			max_payment_size_msat,
315		};
316
317		let promise_secret = [1u8; 32];
318		let client_node_id = PublicKey::from_secret_key(
319			&Secp256k1::new(),
320			&SecretKey::from_slice(&[0xcd; 32]).unwrap(),
321		);
322
323		let mut opening_fee_params = raw.into_opening_fee_params(&promise_secret, &client_node_id);
324		opening_fee_params.min_fee_msat = min_fee_msat + 1;
325		assert!(!is_valid_opening_fee_params(
326			&opening_fee_params,
327			&promise_secret,
328			&client_node_id
329		));
330	}
331
332	#[test]
333	fn wrong_secret_produced_invalid_params() {
334		let min_fee_msat = 100;
335		let proportional = 21;
336		let valid_until = LSPSDateTime::from_str("2035-05-20T08:30:45Z").unwrap();
337		let min_lifetime = 144;
338		let max_client_to_self_delay = 128;
339		let min_payment_size_msat = 1;
340		let max_payment_size_msat = 100_000_000;
341
342		let raw = LSPS2RawOpeningFeeParams {
343			min_fee_msat,
344			proportional,
345			valid_until,
346			min_lifetime,
347			max_client_to_self_delay,
348			min_payment_size_msat,
349			max_payment_size_msat,
350		};
351
352		let promise_secret = [1u8; 32];
353		let other_secret = [2u8; 32];
354
355		let client_node_id = PublicKey::from_secret_key(
356			&Secp256k1::new(),
357			&SecretKey::from_slice(&[0xcd; 32]).unwrap(),
358		);
359
360		let opening_fee_params = raw.into_opening_fee_params(&promise_secret, &client_node_id);
361		assert!(!is_valid_opening_fee_params(&opening_fee_params, &other_secret, &client_node_id));
362	}
363
364	#[test]
365	fn client_mismatch_produced_invalid_params() {
366		let min_fee_msat = 100;
367		let proportional = 21;
368		let valid_until = LSPSDateTime::from_str("2035-05-20T08:30:45Z").unwrap();
369		let min_lifetime = 144;
370		let max_client_to_self_delay = 128;
371		let min_payment_size_msat = 1;
372		let max_payment_size_msat = 100_000_000;
373
374		let raw = LSPS2RawOpeningFeeParams {
375			min_fee_msat,
376			proportional,
377			valid_until,
378			min_lifetime,
379			max_client_to_self_delay,
380			min_payment_size_msat,
381			max_payment_size_msat,
382		};
383
384		let promise_secret = [1u8; 32];
385
386		let client_node_id = PublicKey::from_secret_key(
387			&Secp256k1::new(),
388			&SecretKey::from_slice(&[0xcd; 32]).unwrap(),
389		);
390
391		let other_public_key = PublicKey::from_secret_key(
392			&Secp256k1::new(),
393			&SecretKey::from_slice(&[0xcf; 32]).unwrap(),
394		);
395
396		let opening_fee_params = raw.into_opening_fee_params(&promise_secret, &client_node_id);
397		assert!(is_valid_opening_fee_params(&opening_fee_params, &promise_secret, &client_node_id));
398		assert!(!is_valid_opening_fee_params(
399			&opening_fee_params,
400			&promise_secret,
401			&other_public_key
402		));
403	}
404
405	#[test]
406	#[cfg(feature = "std")]
407	// TODO: We need to find a way to check expiry times in no-std builds.
408	fn expired_params_produces_invalid_params() {
409		let min_fee_msat = 100;
410		let proportional = 21;
411		let valid_until = LSPSDateTime::from_str("2023-05-20T08:30:45Z").unwrap();
412		let min_lifetime = 144;
413		let max_client_to_self_delay = 128;
414		let min_payment_size_msat = 1;
415		let max_payment_size_msat = 100_000_000;
416
417		let raw = LSPS2RawOpeningFeeParams {
418			min_fee_msat,
419			proportional,
420			valid_until,
421			min_lifetime,
422			max_client_to_self_delay,
423			min_payment_size_msat,
424			max_payment_size_msat,
425		};
426
427		let promise_secret = [1u8; 32];
428		let client_node_id = PublicKey::from_secret_key(
429			&Secp256k1::new(),
430			&SecretKey::from_slice(&[0xcd; 32]).unwrap(),
431		);
432
433		let opening_fee_params = raw.into_opening_fee_params(&promise_secret, &client_node_id);
434		assert!(!is_valid_opening_fee_params(
435			&opening_fee_params,
436			&promise_secret,
437			&client_node_id
438		));
439	}
440
441	#[test]
442	fn buy_request_serialization() {
443		let min_fee_msat = 100;
444		let proportional = 21;
445		let valid_until = LSPSDateTime::from_str("2023-05-20T08:30:45Z").unwrap();
446		let min_lifetime = 144;
447		let max_client_to_self_delay = 128;
448		let min_payment_size_msat = 1;
449		let max_payment_size_msat = 100_000_000;
450
451		let raw = LSPS2RawOpeningFeeParams {
452			min_fee_msat,
453			proportional,
454			valid_until,
455			min_lifetime,
456			max_client_to_self_delay,
457			min_payment_size_msat,
458			max_payment_size_msat,
459		};
460
461		let promise_secret = [1u8; 32];
462		let client_node_id = PublicKey::from_secret_key(
463			&Secp256k1::new(),
464			&SecretKey::from_slice(&[0xcd; 32]).unwrap(),
465		);
466
467		let opening_fee_params = raw.into_opening_fee_params(&promise_secret, &client_node_id);
468		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":"75eb57db4c37dc092a37f1d2e0026c5ff36a7834a717ea97c41d91a8d5b50ce8","proportional":21,"valid_until":"2023-05-20T08:30:45Z"}"#;
469		assert_eq!(json_str, serde_json::json!(opening_fee_params).to_string());
470		assert_eq!(opening_fee_params, serde_json::from_str(json_str).unwrap());
471
472		let payment_size_msat = Some(1234);
473		let buy_request_fixed =
474			LSPS2BuyRequest { opening_fee_params: opening_fee_params.clone(), payment_size_msat };
475		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":"75eb57db4c37dc092a37f1d2e0026c5ff36a7834a717ea97c41d91a8d5b50ce8","proportional":21,"valid_until":"2023-05-20T08:30:45Z"},"payment_size_msat":"1234"}"#;
476		assert_eq!(json_str, serde_json::json!(buy_request_fixed).to_string());
477		assert_eq!(buy_request_fixed, serde_json::from_str(json_str).unwrap());
478
479		let payment_size_msat = None;
480		let buy_request_variable = LSPS2BuyRequest { opening_fee_params, payment_size_msat };
481
482		// Check we skip serialization if payment_size_msat is None.
483		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":"75eb57db4c37dc092a37f1d2e0026c5ff36a7834a717ea97c41d91a8d5b50ce8","proportional":21,"valid_until":"2023-05-20T08:30:45Z"}}"#;
484		assert_eq!(json_str, serde_json::json!(buy_request_variable).to_string());
485		assert_eq!(buy_request_variable, serde_json::from_str(json_str).unwrap());
486
487		// Check we still deserialize correctly if payment_size_msat is 'null'.
488		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":"75eb57db4c37dc092a37f1d2e0026c5ff36a7834a717ea97c41d91a8d5b50ce8","proportional":21,"valid_until":"2023-05-20T08:30:45Z"},"payment_size_msat":null}"#;
489		assert_eq!(buy_request_variable, serde_json::from_str(json_str).unwrap());
490	}
491
492	#[test]
493	fn parse_spec_test_vectors() {
494		// Here, we simply assert that we're able to parse all examples given in LSPS2.
495		let json_str = r#"{
496			"opening_fee_params_menu": [
497			{
498				"min_fee_msat": "546000",
499				"proportional": 1200,
500				"valid_until": "2023-02-23T08:47:30.511Z",
501				"min_lifetime": 1008,
502				"max_client_to_self_delay": 2016,
503				"min_payment_size_msat": "1000",
504				"max_payment_size_msat": "1000000",
505				"promise": "abcdefghijklmnopqrstuvwxyz"
506			},
507			{
508				"min_fee_msat": "1092000",
509				"proportional": 2400,
510				"valid_until": "2023-02-27T21:23:57.984Z",
511				"min_lifetime": 1008,
512				"max_client_to_self_delay": 2016,
513				"min_payment_size_msat": "1000",
514				"max_payment_size_msat": "1000000",
515				"promise": "abcdefghijklmnopqrstuvwxyz"
516			}
517			]
518		}"#;
519		let _get_info_response: LSPS2GetInfoResponse = serde_json::from_str(json_str).unwrap();
520
521		let json_str = r#"{
522			"opening_fee_params": {
523				"min_fee_msat": "546000",
524				"proportional": 1200,
525				"valid_until": "2023-02-23T08:47:30.511Z",
526				"min_lifetime": 1008,
527				"max_client_to_self_delay": 2016,
528				"min_payment_size_msat": "1000",
529				"max_payment_size_msat": "1000000",
530				"promise": "abcdefghijklmnopqrstuvwxyz"
531			},
532			"payment_size_msat": "42000"
533		}"#;
534		let _buy_request: LSPS2BuyRequest = serde_json::from_str(json_str).unwrap();
535
536		let json_str = r#"{
537			"jit_channel_scid": "29451x4815x1",
538			"lsp_cltv_expiry_delta" : 144,
539			"client_trusts_lsp": false
540		}"#;
541		let _buy_response: LSPS2BuyResponse = serde_json::from_str(json_str).unwrap();
542	}
543}