1use anyhow::{anyhow, Error};
2use hex::{self, FromHex};
3use serde::de::{self, Deserializer};
4use serde::ser::{self, Serializer};
5use serde::{Deserialize, Serialize};
6use serde_json::{json, Value};
7use std::collections::HashMap;
8
9#[derive(Serialize, Deserialize, Debug)]
10#[serde(tag = "method", content = "params")]
11#[serde(rename_all = "snake_case")]
12enum JsonRpcCall {
13 }
15
16#[derive(Debug)]
17pub struct ParserError {
18 reason: String,
19}
20
21impl std::fmt::Display for ParserError {
22 fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
23 f.write_fmt(format_args!("ParserError {}", self.reason))
24 }
25}
26impl std::error::Error for ParserError {}
27
28#[derive(Serialize, Deserialize, Debug)]
29struct JsonRpcRequest {
30 id: Option<Value>,
31 jsonrpc: String,
32 method: String,
33 params: JsonRpcCall,
34}
35
36#[derive(Serialize, Deserialize, Debug)]
38#[serde(tag = "method", content = "params")]
39#[serde(rename_all = "snake_case")]
40pub enum MyRequests {
41 HtlcAccepted(HtlcAcceptedCall),
42 Getmanifest(GetManifestCall),
43 Init(InitCall),
44 InvoicePayment(InvoicePaymentCall),
45 CommitmentRevocation(CommitmentRevocationCall),
46}
47
48#[derive(Serialize, Deserialize, Debug)]
49#[serde(rename_all = "snake_case")]
50pub struct HtlcAcceptedCall {
51 pub onion: HtlcAcceptedCallOnion,
52 pub htlc: HtlcAcceptedCallHtlc,
53}
54
55#[derive(Serialize, Deserialize, Debug)]
56#[serde(rename_all = "snake_case")]
57pub struct InvoicePaymentCall {
58 pub payment: InvoicePaymentCallPayment,
59}
60
61#[derive(Serialize, Deserialize, Debug)]
62#[serde(rename_all = "snake_case")]
63pub struct Custommsg {
64 pub peer_id: String,
65 pub payload: String,
66}
67
68#[derive(Serialize, Deserialize, Debug)]
69#[serde(rename_all = "snake_case")]
70pub struct CommitmentRevocationCall {
71 pub commitment_txid: String,
72 pub penalty_tx: String,
73 pub channel_id: Option<String>,
74 pub commitnum: Option<u64>,
75}
76
77fn amt_from_str_or_int<'de, D>(deserializer: D) -> Result<u64, D::Error>
78where
79 D: de::Deserializer<'de>,
80{
81 struct JsonStringVisitor;
82
83 impl<'de> de::Visitor<'de> for JsonStringVisitor {
84 type Value = u64;
85
86 fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
87 formatter.write_str("a string containing json data")
88 }
89
90 fn visit_u64<E>(self, v: u64) -> Result<Self::Value, E>
91 where
92 E: de::Error,
93 {
94 Ok(v)
95 }
96
97 fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
98 where
99 E: de::Error,
100 {
101 let (nums, exts): (Vec<char>, Vec<char>) = v.chars().partition(|c| c.is_digit(10));
102
103 let mut num = String::new();
104 num.extend(nums);
105 let mut ext = String::new();
106 ext.extend(exts);
107
108 let mult = match ext.as_str() {
111 "msat" => 1,
112 "sat" => 1000,
113 "btc" => 100_000_000_000,
114 _ => return Err(E::custom("unable to parse unit")),
115 };
116
117 let num: u64 = num.parse::<u64>().expect("converting chars to u64");
118 Ok(num * mult)
119 }
120 }
121
122 deserializer.deserialize_any(JsonStringVisitor)
124}
125
126#[derive(Serialize, Deserialize, Debug)]
127pub struct InvoicePaymentCallPayment {
128 pub label: String,
129 pub preimage: String,
130 #[serde(rename = "msat", deserialize_with = "amt_from_str_or_int")]
131 pub amount: u64,
132 pub extratlvs: Option<Vec<TlvField>>,
133}
134
135#[derive(Serialize, Deserialize, Debug)]
136pub struct TlvField {
137 #[serde(rename = "type")]
138 pub typ: u64,
139 pub value: String,
140}
141
142#[derive(Serialize, Deserialize, Debug)]
143#[serde(rename_all = "snake_case")]
144pub struct GetManifestCall {}
145
146#[derive(Serialize, Deserialize, Debug)]
147#[serde(rename_all = "snake_case")]
148pub struct GetManifestResult {
149 pub subscriptions: Vec<String>,
150 pub hooks: Vec<String>,
151 pub dynamic: bool,
152 pub options: Vec<PluginOption>,
153 pub rpcmethods: Vec<PluginRpcMethod>,
154}
155
156#[derive(Serialize, Deserialize, Debug)]
157pub struct PluginOption {
158 name: String,
159 default: String,
160 description: String,
161}
162
163#[derive(Serialize, Deserialize, Debug)]
164#[serde(rename_all = "snake_case")]
165pub struct PluginRpcMethod {
166 name: String,
167 usage: String,
168 description: String,
169}
170
171#[derive(Serialize, Deserialize, Debug)]
172#[serde(rename_all = "snake_case")]
173pub struct HtlcAcceptedCallOnion {
174 #[serde(serialize_with = "buffer_to_hex", deserialize_with = "hex_to_buffer")]
175 pub payload: Vec<u8>,
176 short_channel_id: Option<String>,
177 forward_amount: String,
178 outgoing_cltv_value: u64,
179
180 #[serde(serialize_with = "buffer_to_hex", deserialize_with = "hex_to_buffer")]
181 next_onion: Vec<u8>,
182
183 #[serde(serialize_with = "buffer_to_hex", deserialize_with = "hex_to_buffer")]
184 pub shared_secret: Vec<u8>,
185}
186
187#[derive(Serialize, Deserialize, Debug)]
188#[serde(rename_all = "snake_case")]
189pub struct HtlcAcceptedCallHtlc {
190 pub amount: String,
191 cltv_expiry: u64,
192 cltv_expiry_relative: u64,
193
194 #[serde(serialize_with = "buffer_to_hex", deserialize_with = "hex_to_buffer")]
195 pub payment_hash: Vec<u8>,
196}
197
198#[derive(Serialize, Deserialize, Debug)]
199#[serde(rename_all = "snake_case")]
200pub struct HtlcAcceptedResponse {
201 pub result: String,
202 #[serde(serialize_with = "buffer_to_hex", deserialize_with = "hex_to_buffer")]
203 pub payment_key: Vec<u8>,
204}
205
206#[derive(Serialize, Deserialize, Debug)]
207pub struct InitCall {
208 pub options: Value,
209 pub configuration: HashMap<String, Value>,
210}
211
212#[derive(Serialize, Deserialize, Debug)]
213#[serde(tag = "method", content = "params")]
214#[serde(rename_all = "snake_case")]
215pub enum MyNotifications {
216 Disconnect(DisconnectNotification),
217}
218
219#[derive(Serialize, Deserialize, Debug)]
220pub struct DisconnectNotification {
221 pub id: String,
222}
223
224#[derive(Debug)]
225pub enum JsonRpc<N, R> {
226 Request(usize, R),
227 Notification(N),
228}
229
230impl<N, R> Serialize for JsonRpc<N, R>
231where
232 N: Serialize,
233 R: Serialize,
234{
235 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
236 where
237 S: Serializer,
238 {
239 match *self {
240 JsonRpc::Request(id, ref r) => {
241 let mut v = serde_json::to_value(r).map_err(ser::Error::custom)?;
242 v["id"] = json!(id);
243 v.serialize(serializer)
244 }
245 JsonRpc::Notification(ref n) => n.serialize(serializer),
246 }
247 }
248}
249
250impl<'de, N, R> Deserialize<'de> for JsonRpc<N, R>
251where
252 N: Deserialize<'de>,
253 R: Deserialize<'de>,
254{
255 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
256 where
257 D: Deserializer<'de>,
258 {
259 #[derive(Deserialize)]
260 struct IdHelper {
261 id: Option<usize>,
262 }
263
264 let v = Value::deserialize(deserializer)?;
265 let helper = IdHelper::deserialize(&v).map_err(de::Error::custom)?;
266 match helper.id {
267 Some(id) => {
268 let r = R::deserialize(v).map_err(de::Error::custom)?;
269 Ok(JsonRpc::Request(id, r))
270 }
271 None => {
272 let n = N::deserialize(v).map_err(de::Error::custom)?;
273 Ok(JsonRpc::Notification(n))
274 }
275 }
276 }
277}
278pub fn buffer_to_hex<T, S>(buffer: &T, serializer: S) -> Result<S::Ok, S::Error>
280where
281 T: AsRef<[u8]>,
282 S: Serializer,
283{
284 serializer.serialize_str(&hex::encode(buffer))
285}
286
287pub fn hex_to_buffer<'de, D>(deserializer: D) -> Result<Vec<u8>, D::Error>
289where
290 D: Deserializer<'de>,
291{
292 use serde::de::Error;
293 String::deserialize(deserializer)
294 .and_then(|string| Vec::from_hex(&string).map_err(|err| Error::custom(err.to_string())))
295}
296
297#[derive(Serialize, Deserialize, Debug)]
298pub struct Amount {
299 pub msatoshi: i64,
300}
301
302impl Amount {
303 pub fn from_string(s: &str) -> Result<Amount, Error> {
304 if !s.ends_with("msat") {
305 return Err(anyhow!("Amount string does not end with msat."));
306 }
307
308 let amount_string: &str = s[0..s.len() - 4].into();
309
310 let amount: i64 = match amount_string.parse::<i64>() {
311 Ok(v) => v,
312 Err(e) => return Err(anyhow!(e)),
313 };
314
315 Ok(Amount { msatoshi: amount })
316 }
317}
318
319fn _string_to_amount<'de, D>(deserializer: D) -> Result<Amount, D::Error>
320where
321 D: Deserializer<'de>,
322{
323 use serde::de::Error;
324 String::deserialize(deserializer).and_then(|string| {
325 Amount::from_string(&string).map_err(|_| Error::custom("could not parse amount"))
326 })
327}
328
329fn _amount_to_string<S>(amount: &Amount, serializer: S) -> Result<S::Ok, S::Error>
330where
331 S: Serializer,
332{
333 let s = format!("{}msat", amount.msatoshi);
334 serializer.serialize_str(&s)
335}
336
337#[derive(Serialize, Deserialize, Debug)]
340pub struct PeerConnectedCall {
341 pub peer: Peer
342}
343
344#[derive(Serialize, Deserialize, Debug)]
345pub struct Peer {
346 pub id: String,
347 pub direction: Direction,
348 pub addr: String,
349 pub features: String,
350}
351
352#[derive(Serialize, Deserialize, Debug, PartialEq)]
353#[serde(rename_all = "snake_case")]
354pub enum Direction {
355 In,
356 Out
357}
358
359
360#[cfg(test)]
361mod test {
362 use super::*;
363
364 #[test]
365 fn test_peer_connected_call() {
366 let msg = json!({
367 "peer": {
368 "id": "03864ef025fde8fb587d989186ce6a4a186895ee44a926bfc370e2c366597a3f8f",
369 "direction": "in",
370 "addr": "34.239.230.56:9735",
371 "features": ""
372 }
373 });
374
375 let call = serde_json::from_str::<PeerConnectedCall>(&msg.to_string()).unwrap();
376 assert_eq!(call.peer.id, "03864ef025fde8fb587d989186ce6a4a186895ee44a926bfc370e2c366597a3f8f");
377 assert_eq!(call.peer.direction, Direction::In);
378 assert_eq!(call.peer.addr, "34.239.230.56:9735");
379 assert_eq!(call.peer.features, "");
380 }
381
382 #[test]
383 fn test_htlc_accepted_call() {
384 let req = json!({"id": 1, "jsonrpc": "2.0", "method": "htlc_accepted", "params": {
385 "onion": {
386 "payload": "",
387 "type": "legacy",
388 "short_channel_id": "1x2x3",
389 "forward_amount": "42msat",
390 "outgoing_cltv_value": 500014,
391 "shared_secret": "0000000000000000000000000000000000000000000000000000000000000000",
392 "next_onion": "00DEADBEEF00",
393 },
394 "htlc": {
395 "amount": "43msat",
396 "cltv_expiry": 500028,
397 "cltv_expiry_relative": 10,
398 "payment_hash": "0000000000000000000000000000000000000000000000000000000000000000"
399 }
400 }
401 });
402
403 type T = JsonRpc<MyNotifications, MyRequests>;
404 let req = serde_json::from_str::<T>(&req.to_string()).unwrap();
405 match req {
406 T::Request(id, c) => {
407 assert_eq!(id, 1);
408 match c {
409 MyRequests::HtlcAccepted(c) => {
410 assert_eq!(c.onion.forward_amount, "42msat");
412 assert_eq!(c.onion.outgoing_cltv_value, 500014);
413 }
423 _ => panic!("This was supposed to be an htlc_accepted call"),
424 }
425 }
426 _ => panic!("This was supposed to be a request"),
427 }
428 }
429
430 #[test]
433 fn test_invoice_payment_payload() {
434 let s = "{\"payment\": {\"extratlvs\": [], \"label\": \"{\\\"unix_milli\\\":1717422773673,\\\"payer_amount_msat\\\":null}\", \"msat\": \"42btc\", \"preimage\": \"243adf90767a5c3a8f6118e003c89b3e1ab5a2fd318d49cb41f4d42e92d3de41\"}}";
435 let v = serde_json::from_str(&s).expect("parsing generic value");
436 let _c: InvoicePaymentCall = serde_json::from_value(v).expect("parsing into struct");
437 }
438}