gl_plugin/
responses.rs

1//! Various structs representing JSON-RPC responses
2pub use clightningrpc::responses::*;
3
4use serde::{de, Deserialize, Deserializer};
5use std::str::FromStr;
6
7/// A simple wrapper that generalizes bare amounts and amounts with
8/// the `msat` suffix.
9#[derive(Clone, Copy, Debug)]
10pub struct MSat(pub u64);
11
12struct MSatVisitor;
13impl<'d> de::Visitor<'d> for MSatVisitor {
14    type Value = MSat;
15
16    fn visit_str<E>(self, s: &str) -> Result<Self::Value, E>
17    where
18        E: de::Error,
19    {
20        if !s.ends_with("msat") {
21            return Err(E::custom("missing msat suffix"));
22        }
23
24        let numpart = s
25            .get(0..(s.len() - 4))
26            .ok_or_else(|| E::custom("missing msat suffix"))?;
27
28        let res = u64::from_str(numpart).map_err(|_| E::custom("not a number"))?;
29        Ok(MSat(res))
30    }
31
32    fn visit_u64<E>(self, v: u64) -> Result<Self::Value, E>
33    where
34        E: de::Error,
35    {
36        Ok(MSat(v as u64))
37    }
38
39    fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
40        write!(formatter, "a bare integer or a string ending with \"msat\"")
41    }
42}
43
44impl<'d> Deserialize<'d> for MSat {
45    fn deserialize<D>(deserializer: D) -> Result<MSat, D::Error>
46    where
47        D: Deserializer<'d>,
48    {
49        deserializer.deserialize_any(MSatVisitor)
50    }
51}
52
53impl std::fmt::Display for MSat {
54    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
55        write!(f, "{}msat", self.0)
56    }
57}
58
59#[derive(Debug, Clone, Deserialize)]
60pub struct Withdraw {
61    pub tx: String,
62    pub txid: String,
63}
64
65#[derive(Debug, Clone, Deserialize)]
66pub struct FundChannel {
67    pub tx: String,
68    pub txid: String,
69
70    #[serde(rename = "outnum")]
71    pub outpoint: u32,
72    pub channel_id: String,
73    pub close_to: Option<String>,
74}
75
76#[derive(Debug, Clone, Deserialize)]
77pub struct GetInfo {
78    pub id: String,
79    pub alias: String,
80    pub color: String,
81    pub num_peers: u64,
82    pub num_pending_channels: u64,
83    pub num_active_channels: u64,
84    pub num_inactive_channels: u64,
85    pub version: String,
86    pub blockheight: u32,
87    pub network: String,
88    #[serde(rename = "lightning-dir")]
89    pub ligthning_dir: String,
90    pub warning_bitcoind_sync: Option<String>,
91    pub warning_lightningd_sync: Option<String>,
92}
93
94#[derive(Debug, Clone, Deserialize)]
95pub struct CloseChannel {
96    #[serde(rename = "type")]
97    pub close_type: String,
98    pub tx: String,
99    pub txid: String,
100}
101
102#[derive(Debug, Clone, Deserialize)]
103pub struct Invoice {
104    #[serde(rename = "expires_at")]
105    pub expiry_time: u32,
106    pub bolt11: String,
107    pub payment_hash: String,
108    pub payment_secret: Option<String>,
109}
110
111#[derive(Debug, Clone, Deserialize)]
112pub struct Pay {
113    pub destination: String,
114    pub payment_hash: String,
115    pub created_at: f64,
116    pub completed_at: Option<u64>,
117    pub parts: u32,
118    pub msatoshi: u64,
119    pub msatoshi_sent: u64,
120    pub preimage: Option<String>,
121    pub status: String,
122    pub bolt11: Option<String>,
123}
124
125// Sadly the results of pay differ from the listpays elements, so we
126// have to replicate this struct here, until we merge them correctly.
127#[derive(Debug, Clone, Deserialize)]
128pub struct ListPaysPay {
129    pub bolt11: Option<String>,
130    pub destination: String,
131    pub payment_hash: String,
132    pub created_at: f64,
133    pub completed_at: Option<u64>,
134    // parts is missing
135    // msatoshi is renamed amount_msat
136    pub amount_msat: Option<MSat>,
137    pub amount_sent_msat: MSat,
138
139    #[serde(rename = "preimage")]
140    pub payment_preimage: Option<String>,
141
142    pub status: String,
143}
144
145#[derive(Debug, Clone, Deserialize)]
146pub struct ListPays {
147    pub pays: Vec<ListPaysPay>,
148}
149
150// Invoices returned as part of a listinvoices call
151#[derive(Debug, Clone, Deserialize)]
152pub struct ListInvoiceInvoice {
153    pub label: String,
154    pub description: String,
155    pub payment_preimage: Option<String>,
156
157    #[serde(rename = "amount_msat")]
158    pub amount: Option<MSat>,
159
160    #[serde(rename = "amount_received_msat")]
161    pub received: Option<MSat>,
162
163    #[serde(rename = "paid_at")]
164    pub payment_time: Option<u32>,
165    pub status: String,
166
167    #[serde(rename = "expires_at")]
168    pub expiry_time: u32,
169    pub bolt11: String,
170    pub payment_hash: String,
171}
172
173#[derive(Debug, Clone, Deserialize)]
174pub struct ListInvoices {
175    pub invoices: Vec<ListInvoiceInvoice>,
176}
177
178#[derive(Debug, Clone, Deserialize)]
179pub struct Keysend {
180    pub destination: String,
181    pub status: String,
182    pub payment_preimage: Option<String>,
183    pub payment_hash: String,
184    pub msatoshi: Option<u64>,
185    pub msatoshi_sent: Option<u64>,
186    pub amount_sent_msat: Option<MSat>,
187    pub amount_msat: Option<MSat>,
188}
189
190#[derive(Debug, Clone, Deserialize)]
191pub struct ListIncoming {
192    pub incoming: Vec<IncomingChannel>,
193}
194
195#[derive(Debug, Clone, Deserialize)]
196pub struct IncomingChannel {
197    pub id: String,
198    pub short_channel_id: String,
199    pub fee_base_msat: String,
200    pub fee_proportional_millionths: u32,
201    pub cltv_expiry_delta: u32,
202    pub incoming_capacity_msat: String,
203}
204
205#[derive(Debug, Clone, Deserialize)]
206pub struct GetChainInfo {
207    pub chain: String,
208    pub headercount: u32,
209    pub blockcount: u32,
210    pub ibd: bool,
211}
212
213/// Sub-structure for 'getlog' and 'listpeers' item
214#[derive(Debug, Clone, Deserialize)]
215pub struct LogEntry {
216    #[serde(rename = "type")]
217    pub type_: String,
218    pub num_skipped: Option<u64>,
219    pub time: Option<String>,
220    pub node_id: Option<String>,
221    pub source: Option<String>,
222    pub log: Option<String>,
223    pub data: Option<String>,
224}
225
226/// 'getlog' command
227#[derive(Debug, Clone, Deserialize)]
228pub struct GetLog {
229    pub created_at: String,
230    pub bytes_used: u64,
231    pub bytes_max: u64,
232    pub log: Vec<LogEntry>,
233}
234
235/// Sub-structure for htlcs in 'listpeers'
236#[derive(Debug, Clone, Deserialize)]
237pub struct Htlc {
238    pub direction: String,
239    pub id: u64,
240    pub amount_msat: MSat,
241    pub expiry: u64,
242    pub payment_hash: String,
243    pub state: String,
244    pub local_trimmed: Option<bool>,
245}
246
247#[derive(Debug, Clone, Deserialize)]
248pub struct Channel {
249    pub state: String,
250    pub scratch_txid: Option<String>,
251    pub owner: Option<String>,
252    pub short_channel_id: Option<String>,
253    pub alias: Option<Aliases>,
254    pub direction: Option<u64>,
255    pub channel_id: String,
256    pub funding_txid: String,
257    pub close_to_addr: Option<String>,
258    pub close_to: Option<String>,
259    pub private: bool,
260    pub to_us_msat: MSat,
261    pub min_to_us_msat: MSat,
262    pub max_to_us_msat: MSat,
263    pub total_msat: MSat,
264    pub dust_limit_msat: MSat,
265    pub max_total_htlc_in_msat: MSat,
266    pub their_reserve_msat: MSat,
267    pub our_reserve_msat: MSat,
268    pub spendable_msat: MSat,
269    pub receivable_msat: MSat,
270    pub minimum_htlc_in_msat: MSat,
271    pub their_to_self_delay: u64,
272    pub our_to_self_delay: u64,
273    pub max_accepted_htlcs: u64,
274    pub status: Vec<String>,
275    pub in_payments_offered: u64,
276    pub in_offered_msat: MSat,
277    pub in_payments_fulfilled: u64,
278    pub in_fulfilled_msat: MSat,
279    pub out_payments_offered: u64,
280    pub out_offered_msat: MSat,
281    pub out_payments_fulfilled: u64,
282    pub out_fulfilled_msat: MSat,
283    pub htlcs: Vec<Htlc>,
284}
285
286#[derive(Debug, Clone, Deserialize)]
287pub struct Aliases {
288    pub local: Option<String>,
289    pub remote: Option<String>,
290}
291
292#[derive(Debug, Clone, Deserialize)]
293pub struct Peer {
294    pub id: String,
295    pub connected: bool,
296    pub netaddr: Option<Vec<String>>,
297    pub features: Option<String>,
298    pub channels: Vec<Channel>,
299    pub log: Option<Vec<LogEntry>>,
300}
301
302#[derive(Debug, Clone, Deserialize)]
303pub struct ListPeers {
304    pub peers: Vec<Peer>,
305}
306
307/// Sub-structure for 'listfunds' output
308#[derive(Debug, Clone, Deserialize)]
309pub struct ListFundsOutput {
310    pub txid: String,
311    pub output: u64,
312    pub amount_msat: MSat,
313    pub address: String,
314    pub status: String,
315    pub reserved: bool,
316    pub reserved_to_block: Option<u32>,
317}
318
319#[derive(Debug, Clone, Deserialize)]
320pub struct ListFundsChannel {
321    pub peer_id: String,
322    pub connected: bool,
323    pub short_channel_id: Option<String>,
324    pub our_amount_msat: MSat,
325    pub amount_msat: MSat,
326    pub funding_txid: String,
327    pub funding_output: u64,
328}
329
330/// 'listfunds' command
331#[derive(Debug, Clone, Deserialize)]
332pub struct ListFunds {
333    pub outputs: Vec<ListFundsOutput>,
334    pub channels: Vec<ListFundsChannel>,
335}
336
337#[cfg(test)]
338mod test {
339    use super::*;
340
341    #[test]
342    fn test_msat_parsing() {
343        #[derive(Deserialize)]
344        struct V {
345            value: MSat,
346        }
347
348        struct T {
349            input: &'static str,
350            output: u64,
351        }
352
353        let tests: Vec<T> = vec![
354            T {
355                input: "{\"value\": \"1234msat\"}",
356                output: 1234,
357            },
358            T {
359                input: "{\"value\": 100000000000}",
360                output: 100000000000,
361            },
362        ];
363
364        for t in tests {
365            let v: V = serde_json::from_str(t.input).unwrap();
366            assert_eq!(v.value.0, t.output);
367        }
368    }
369}