lightning_liquidity/lsps0/
msgs.rs

1//! Message, request, and other primitive types used to implement LSPS0.
2
3use crate::lsps0::ser::{LSPSMessage, RequestId, ResponseError};
4use crate::prelude::Vec;
5
6use serde::{Deserialize, Serialize};
7
8use core::convert::TryFrom;
9
10pub(crate) const LSPS0_LISTPROTOCOLS_METHOD_NAME: &str = "lsps0.list_protocols";
11
12/// A `list_protocols` request.
13///
14/// Please refer to the [LSPS0 specification](https://github.com/BitcoinAndLightningLayerSpecs/lsp/tree/main/LSPS0#lsps-specification-support-query)
15/// for more information.
16#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize, Default)]
17pub struct ListProtocolsRequest {}
18
19/// A response to a `list_protocols` request.
20///
21/// Please refer to the [LSPS0 specification](https://github.com/BitcoinAndLightningLayerSpecs/lsp/tree/main/LSPS0#lsps-specification-support-query)
22/// for more information.
23#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
24pub struct ListProtocolsResponse {
25	/// A list of supported protocols.
26	pub protocols: Vec<u16>,
27}
28
29/// An LSPS0 protocol request.
30///
31/// Please refer to the [LSPS0 specification](https://github.com/BitcoinAndLightningLayerSpecs/lsp/tree/main/LSPS0)
32/// for more information.
33#[derive(Clone, Debug, PartialEq, Eq)]
34pub enum LSPS0Request {
35	/// A request calling `list_protocols`.
36	ListProtocols(ListProtocolsRequest),
37}
38
39impl LSPS0Request {
40	/// Returns the method name associated with the given request variant.
41	pub fn method(&self) -> &str {
42		match self {
43			LSPS0Request::ListProtocols(_) => LSPS0_LISTPROTOCOLS_METHOD_NAME,
44		}
45	}
46}
47
48/// An LSPS0 protocol request.
49///
50/// Please refer to the [LSPS0 specification](https://github.com/BitcoinAndLightningLayerSpecs/lsp/tree/main/LSPS0)
51/// for more information.
52#[derive(Clone, Debug, PartialEq, Eq)]
53pub enum LSPS0Response {
54	/// A response to a `list_protocols` request.
55	ListProtocols(ListProtocolsResponse),
56	/// An error response to a `list_protocols` request.
57	ListProtocolsError(ResponseError),
58}
59
60/// An LSPS0 protocol message.
61///
62/// Please refer to the [LSPS0 specification](https://github.com/BitcoinAndLightningLayerSpecs/lsp/tree/main/LSPS0)
63/// for more information.
64#[derive(Clone, Debug, PartialEq, Eq)]
65pub enum LSPS0Message {
66	/// A request variant.
67	Request(RequestId, LSPS0Request),
68	/// A response variant.
69	Response(RequestId, LSPS0Response),
70}
71
72impl TryFrom<LSPSMessage> for LSPS0Message {
73	type Error = ();
74
75	fn try_from(message: LSPSMessage) -> Result<Self, Self::Error> {
76		match message {
77			LSPSMessage::Invalid(_) => Err(()),
78			LSPSMessage::LSPS0(message) => Ok(message),
79			LSPSMessage::LSPS1(_) => Err(()),
80			LSPSMessage::LSPS2(_) => Err(()),
81		}
82	}
83}
84
85impl From<LSPS0Message> for LSPSMessage {
86	fn from(message: LSPS0Message) -> Self {
87		LSPSMessage::LSPS0(message)
88	}
89}
90
91#[cfg(test)]
92mod tests {
93	use lightning::util::hash_tables::new_hash_map;
94
95	use super::*;
96	use crate::lsps0::ser::LSPSMethod;
97	use crate::prelude::ToString;
98
99	#[test]
100	fn deserializes_request() {
101		let json = r#"{
102			"jsonrpc": "2.0",
103			"id": "request:id:xyz123",
104			"method": "lsps0.list_protocols"
105		}"#;
106
107		let mut request_id_method_map = new_hash_map();
108
109		let msg = LSPSMessage::from_str_with_id_map(json, &mut request_id_method_map);
110		assert!(msg.is_ok());
111		let msg = msg.unwrap();
112		assert_eq!(
113			msg,
114			LSPSMessage::LSPS0(LSPS0Message::Request(
115				RequestId("request:id:xyz123".to_string()),
116				LSPS0Request::ListProtocols(ListProtocolsRequest {})
117			))
118		);
119	}
120
121	#[test]
122	fn serializes_request() {
123		let request = LSPSMessage::LSPS0(LSPS0Message::Request(
124			RequestId("request:id:xyz123".to_string()),
125			LSPS0Request::ListProtocols(ListProtocolsRequest {}),
126		));
127		let json = serde_json::to_string(&request).unwrap();
128		assert_eq!(
129			json,
130			r#"{"jsonrpc":"2.0","id":"request:id:xyz123","method":"lsps0.list_protocols","params":{}}"#
131		);
132	}
133
134	#[test]
135	fn deserializes_success_response() {
136		let json = r#"{
137	        "jsonrpc": "2.0",
138	        "id": "request:id:xyz123",
139	        "result": {
140	            "protocols": [1,2,3]
141	        }
142	    }"#;
143		let mut request_id_to_method_map = new_hash_map();
144		request_id_to_method_map
145			.insert(RequestId("request:id:xyz123".to_string()), LSPSMethod::LSPS0ListProtocols);
146
147		let response =
148			LSPSMessage::from_str_with_id_map(json, &mut request_id_to_method_map).unwrap();
149
150		assert_eq!(
151			response,
152			LSPSMessage::LSPS0(LSPS0Message::Response(
153				RequestId("request:id:xyz123".to_string()),
154				LSPS0Response::ListProtocols(ListProtocolsResponse { protocols: vec![1, 2, 3] })
155			))
156		);
157	}
158
159	#[test]
160	fn deserializes_error_response() {
161		let json = r#"{
162	        "jsonrpc": "2.0",
163	        "id": "request:id:xyz123",
164	        "error": {
165	            "code": -32617,
166				"message": "Unknown Error"
167	        }
168	    }"#;
169		let mut request_id_to_method_map = new_hash_map();
170		request_id_to_method_map
171			.insert(RequestId("request:id:xyz123".to_string()), LSPSMethod::LSPS0ListProtocols);
172
173		let response =
174			LSPSMessage::from_str_with_id_map(json, &mut request_id_to_method_map).unwrap();
175
176		assert_eq!(
177			response,
178			LSPSMessage::LSPS0(LSPS0Message::Response(
179				RequestId("request:id:xyz123".to_string()),
180				LSPS0Response::ListProtocolsError(ResponseError {
181					code: -32617,
182					message: "Unknown Error".to_string(),
183					data: None
184				})
185			))
186		);
187	}
188
189	#[test]
190	fn deserialize_fails_with_unknown_request_id() {
191		let json = r#"{
192	        "jsonrpc": "2.0",
193	        "id": "request:id:xyz124",
194	        "result": {
195	            "protocols": [1,2,3]
196	        }
197	    }"#;
198		let mut request_id_to_method_map = new_hash_map();
199		request_id_to_method_map
200			.insert(RequestId("request:id:xyz123".to_string()), LSPSMethod::LSPS0ListProtocols);
201
202		let response = LSPSMessage::from_str_with_id_map(json, &mut request_id_to_method_map);
203		assert!(response.is_err());
204	}
205
206	#[test]
207	fn serializes_response() {
208		let response = LSPSMessage::LSPS0(LSPS0Message::Response(
209			RequestId("request:id:xyz123".to_string()),
210			LSPS0Response::ListProtocols(ListProtocolsResponse { protocols: vec![1, 2, 3] }),
211		));
212		let json = serde_json::to_string(&response).unwrap();
213		assert_eq!(
214			json,
215			r#"{"jsonrpc":"2.0","id":"request:id:xyz123","result":{"protocols":[1,2,3]}}"#
216		);
217	}
218}