bs_gl_plugin/
messages.rs

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    //HtlcAccepted(HtlcAcceptedCall),
14}
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// "Inspired" by https://github.com/serde-rs/serde/issues/1028#issuecomment-325434041
37#[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
77#[derive(Serialize, Deserialize, Debug)]
78pub struct InvoicePaymentCallPayment {
79    pub label: String,
80    pub preimage: String,
81    #[serde(rename = "msat")]
82    pub amount: String,
83    pub extratlvs: Option<Vec<TlvField>>,
84}
85
86#[derive(Serialize, Deserialize, Debug)]
87pub struct TlvField {
88    #[serde(rename = "type")]
89    pub typ: u64,
90    pub value: String,
91}
92
93#[derive(Serialize, Deserialize, Debug)]
94#[serde(rename_all = "snake_case")]
95pub struct GetManifestCall {}
96
97#[derive(Serialize, Deserialize, Debug)]
98#[serde(rename_all = "snake_case")]
99pub struct GetManifestResult {
100    pub subscriptions: Vec<String>,
101    pub hooks: Vec<String>,
102    pub dynamic: bool,
103    pub options: Vec<PluginOption>,
104    pub rpcmethods: Vec<PluginRpcMethod>,
105}
106
107#[derive(Serialize, Deserialize, Debug)]
108pub struct PluginOption {
109    name: String,
110    default: String,
111    description: String,
112}
113
114#[derive(Serialize, Deserialize, Debug)]
115#[serde(rename_all = "snake_case")]
116pub struct PluginRpcMethod {
117    name: String,
118    usage: String,
119    description: String,
120}
121
122#[derive(Serialize, Deserialize, Debug)]
123#[serde(rename_all = "snake_case")]
124pub struct HtlcAcceptedCallOnion {
125    #[serde(serialize_with = "buffer_to_hex", deserialize_with = "hex_to_buffer")]
126    pub payload: Vec<u8>,
127    short_channel_id: Option<String>,
128    forward_amount: String,
129    outgoing_cltv_value: u64,
130
131    #[serde(serialize_with = "buffer_to_hex", deserialize_with = "hex_to_buffer")]
132    next_onion: Vec<u8>,
133
134    #[serde(serialize_with = "buffer_to_hex", deserialize_with = "hex_to_buffer")]
135    pub shared_secret: Vec<u8>,
136}
137
138#[derive(Serialize, Deserialize, Debug)]
139#[serde(rename_all = "snake_case")]
140pub struct HtlcAcceptedCallHtlc {
141    pub amount: String,
142    cltv_expiry: u64,
143    cltv_expiry_relative: u64,
144
145    #[serde(serialize_with = "buffer_to_hex", deserialize_with = "hex_to_buffer")]
146    pub payment_hash: Vec<u8>,
147}
148
149#[derive(Serialize, Deserialize, Debug)]
150#[serde(rename_all = "snake_case")]
151pub struct HtlcAcceptedResponse {
152    pub result: String,
153    #[serde(serialize_with = "buffer_to_hex", deserialize_with = "hex_to_buffer")]
154    pub payment_key: Vec<u8>,
155}
156
157#[derive(Serialize, Deserialize, Debug)]
158pub struct InitCall {
159    pub options: Value,
160    pub configuration: HashMap<String, Value>,
161}
162
163#[derive(Serialize, Deserialize, Debug)]
164#[serde(tag = "method", content = "params")]
165#[serde(rename_all = "snake_case")]
166pub enum MyNotifications {
167    Disconnect(DisconnectNotification),
168}
169
170#[derive(Serialize, Deserialize, Debug)]
171pub struct DisconnectNotification {
172    pub id: String,
173}
174
175#[derive(Debug)]
176pub enum JsonRpc<N, R> {
177    Request(usize, R),
178    Notification(N),
179}
180
181impl<N, R> Serialize for JsonRpc<N, R>
182where
183    N: Serialize,
184    R: Serialize,
185{
186    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
187    where
188        S: Serializer,
189    {
190        match *self {
191            JsonRpc::Request(id, ref r) => {
192                let mut v = serde_json::to_value(r).map_err(ser::Error::custom)?;
193                v["id"] = json!(id);
194                v.serialize(serializer)
195            }
196            JsonRpc::Notification(ref n) => n.serialize(serializer),
197        }
198    }
199}
200
201impl<'de, N, R> Deserialize<'de> for JsonRpc<N, R>
202where
203    N: Deserialize<'de>,
204    R: Deserialize<'de>,
205{
206    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
207    where
208        D: Deserializer<'de>,
209    {
210        #[derive(Deserialize)]
211        struct IdHelper {
212            id: Option<usize>,
213        }
214
215        let v = Value::deserialize(deserializer)?;
216        let helper = IdHelper::deserialize(&v).map_err(de::Error::custom)?;
217        match helper.id {
218            Some(id) => {
219                let r = R::deserialize(v).map_err(de::Error::custom)?;
220                Ok(JsonRpc::Request(id, r))
221            }
222            None => {
223                let n = N::deserialize(v).map_err(de::Error::custom)?;
224                Ok(JsonRpc::Notification(n))
225            }
226        }
227    }
228}
229/// Serializes `buffer` to a lowercase hex string.
230pub fn buffer_to_hex<T, S>(buffer: &T, serializer: S) -> Result<S::Ok, S::Error>
231where
232    T: AsRef<[u8]>,
233    S: Serializer,
234{
235    serializer.serialize_str(&hex::encode(buffer))
236}
237
238/// Deserializes a lowercase hex string to a `Vec<u8>`.
239pub fn hex_to_buffer<'de, D>(deserializer: D) -> Result<Vec<u8>, D::Error>
240where
241    D: Deserializer<'de>,
242{
243    use serde::de::Error;
244    String::deserialize(deserializer)
245        .and_then(|string| Vec::from_hex(&string).map_err(|err| Error::custom(err.to_string())))
246}
247
248#[derive(Serialize, Deserialize, Debug)]
249pub struct Amount {
250    pub msatoshi: i64,
251}
252
253impl Amount {
254    pub fn from_string(s: &str) -> Result<Amount, Error> {
255        if !s.ends_with("msat") {
256            return Err(anyhow!("Amount string does not end with msat."));
257        }
258
259        let amount_string: &str = s[0..s.len() - 4].into();
260
261        let amount: i64 = match amount_string.parse::<i64>() {
262            Ok(v) => v,
263            Err(e) => return Err(anyhow!(e)),
264        };
265
266        Ok(Amount { msatoshi: amount })
267    }
268}
269
270fn _string_to_amount<'de, D>(deserializer: D) -> Result<Amount, D::Error>
271where
272    D: Deserializer<'de>,
273{
274    use serde::de::Error;
275    String::deserialize(deserializer).and_then(|string| {
276        Amount::from_string(&string).map_err(|_| Error::custom("could not parse amount"))
277    })
278}
279
280fn _amount_to_string<S>(amount: &Amount, serializer: S) -> Result<S::Ok, S::Error>
281where
282    S: Serializer,
283{
284    let s = format!("{}msat", amount.msatoshi);
285    serializer.serialize_str(&s)
286}
287
288/// PeerConnectedCall is the the message that is returned by the
289/// `peer_connected` hook.
290#[derive(Serialize, Deserialize, Debug)]
291pub struct PeerConnectedCall {
292    pub peer: Peer
293}
294
295#[derive(Serialize, Deserialize, Debug)]
296pub struct Peer {
297    pub id: String,
298    pub direction: Direction,
299    pub addr: String,
300    pub features: String,
301}
302
303#[derive(Serialize, Deserialize, Debug, PartialEq)]
304#[serde(rename_all = "snake_case")]
305pub enum Direction {
306    In,
307    Out
308}
309
310
311#[cfg(test)]
312mod test {
313    use super::*;
314
315    #[test]
316    fn test_peer_connected_call() {
317        let msg = json!({
318            "peer": {
319                "id": "03864ef025fde8fb587d989186ce6a4a186895ee44a926bfc370e2c366597a3f8f",
320                "direction": "in",
321                "addr": "34.239.230.56:9735",
322                "features": ""
323            }
324        });
325
326        let call = serde_json::from_str::<PeerConnectedCall>(&msg.to_string()).unwrap();
327        assert_eq!(call.peer.id, "03864ef025fde8fb587d989186ce6a4a186895ee44a926bfc370e2c366597a3f8f");
328        assert_eq!(call.peer.direction, Direction::In);
329        assert_eq!(call.peer.addr, "34.239.230.56:9735");
330        assert_eq!(call.peer.features, "");
331    }
332
333    #[test]
334    fn test_htlc_accepted_call() {
335        let req = json!({"id": 1, "jsonrpc": "2.0", "method": "htlc_accepted", "params": {
336            "onion": {
337              "payload": "",
338              "type": "legacy",
339        "short_channel_id": "1x2x3",
340              "forward_amount": "42msat",
341        "outgoing_cltv_value": 500014,
342        "shared_secret": "0000000000000000000000000000000000000000000000000000000000000000",
343        "next_onion": "00DEADBEEF00",
344            },
345            "htlc": {
346        "amount": "43msat",
347        "cltv_expiry": 500028,
348        "cltv_expiry_relative": 10,
349        "payment_hash": "0000000000000000000000000000000000000000000000000000000000000000"
350            }
351        }
352        });
353
354        type T = JsonRpc<MyNotifications, MyRequests>;
355        let req = serde_json::from_str::<T>(&req.to_string()).unwrap();
356        match req {
357            T::Request(id, c) => {
358                assert_eq!(id, 1);
359                match c {
360                    MyRequests::HtlcAccepted(c) => {
361                        //assert_eq!(c.onion.payload, "");
362                        assert_eq!(c.onion.forward_amount, "42msat");
363                        assert_eq!(c.onion.outgoing_cltv_value, 500014);
364                        //assert_eq!(c.onion.next_onion, "[1365bytes of serialized onion]");
365                        //assert_eq!(
366                        //    c.onion.shared_secret,
367                        //    "0000000000000000000000000000000000000000000000000000000000000000"
368                        //);
369                        //assert_eq!(
370                        //    c.htlc.payment_hash,
371                        //    "0000000000000000000000000000000000000000000000000000000000000000"
372                        //);
373                    }
374                    _ => panic!("This was supposed to be an htlc_accepted call"),
375                }
376            }
377            _ => panic!("This was supposed to be a request"),
378        }
379    }
380}