electrum_client/
api.rs

1//! Electrum APIs
2
3use std::borrow::Borrow;
4use std::convert::TryInto;
5use std::ops::Deref;
6
7use bitcoin::consensus::encode::{deserialize, serialize};
8use bitcoin::{block, Script, Transaction, Txid};
9
10use crate::batch::Batch;
11use crate::types::*;
12
13impl<E: Deref> ElectrumApi for E
14where
15    E::Target: ElectrumApi,
16{
17    fn raw_call(
18        &self,
19        method_name: &str,
20        params: impl IntoIterator<Item = Param>,
21    ) -> Result<serde_json::Value, Error> {
22        (**self).raw_call(method_name, params)
23    }
24
25    fn batch_call(&self, batch: &Batch) -> Result<Vec<serde_json::Value>, Error> {
26        (**self).batch_call(batch)
27    }
28
29    fn block_headers_subscribe_raw(&self) -> Result<RawHeaderNotification, Error> {
30        (**self).block_headers_subscribe_raw()
31    }
32
33    fn block_headers_pop_raw(&self) -> Result<Option<RawHeaderNotification>, Error> {
34        (**self).block_headers_pop_raw()
35    }
36
37    fn block_header_raw(&self, height: usize) -> Result<Vec<u8>, Error> {
38        (**self).block_header_raw(height)
39    }
40
41    fn block_headers(&self, start_height: usize, count: usize) -> Result<GetHeadersRes, Error> {
42        (**self).block_headers(start_height, count)
43    }
44
45    fn estimate_fee(&self, number: usize) -> Result<f64, Error> {
46        (**self).estimate_fee(number)
47    }
48
49    fn relay_fee(&self) -> Result<f64, Error> {
50        (**self).relay_fee()
51    }
52
53    fn script_subscribe(&self, script: &Script) -> Result<Option<ScriptStatus>, Error> {
54        (**self).script_subscribe(script)
55    }
56
57    fn batch_script_subscribe<'s, I>(&self, scripts: I) -> Result<Vec<Option<ScriptStatus>>, Error>
58    where
59        I: IntoIterator + Clone,
60        I::Item: Borrow<&'s Script>,
61    {
62        (**self).batch_script_subscribe(scripts)
63    }
64
65    fn script_unsubscribe(&self, script: &Script) -> Result<bool, Error> {
66        (**self).script_unsubscribe(script)
67    }
68
69    fn script_pop(&self, script: &Script) -> Result<Option<ScriptStatus>, Error> {
70        (**self).script_pop(script)
71    }
72
73    fn script_get_balance(&self, script: &Script) -> Result<GetBalanceRes, Error> {
74        (**self).script_get_balance(script)
75    }
76
77    fn batch_script_get_balance<'s, I>(&self, scripts: I) -> Result<Vec<GetBalanceRes>, Error>
78    where
79        I: IntoIterator + Clone,
80        I::Item: Borrow<&'s Script>,
81    {
82        (**self).batch_script_get_balance(scripts)
83    }
84
85    fn script_get_history(&self, script: &Script) -> Result<Vec<GetHistoryRes>, Error> {
86        (**self).script_get_history(script)
87    }
88
89    fn batch_script_get_history<'s, I>(&self, scripts: I) -> Result<Vec<Vec<GetHistoryRes>>, Error>
90    where
91        I: IntoIterator + Clone,
92        I::Item: Borrow<&'s Script>,
93    {
94        (**self).batch_script_get_history(scripts)
95    }
96
97    fn script_list_unspent(&self, script: &Script) -> Result<Vec<ListUnspentRes>, Error> {
98        (**self).script_list_unspent(script)
99    }
100
101    fn batch_script_list_unspent<'s, I>(
102        &self,
103        scripts: I,
104    ) -> Result<Vec<Vec<ListUnspentRes>>, Error>
105    where
106        I: IntoIterator + Clone,
107        I::Item: Borrow<&'s Script>,
108    {
109        (**self).batch_script_list_unspent(scripts)
110    }
111
112    fn transaction_get_raw(&self, txid: &Txid) -> Result<Vec<u8>, Error> {
113        (**self).transaction_get_raw(txid)
114    }
115
116    fn batch_transaction_get_raw<'t, I>(&self, txids: I) -> Result<Vec<Vec<u8>>, Error>
117    where
118        I: IntoIterator + Clone,
119        I::Item: Borrow<&'t Txid>,
120    {
121        (**self).batch_transaction_get_raw(txids)
122    }
123
124    fn batch_block_header_raw<I>(&self, heights: I) -> Result<Vec<Vec<u8>>, Error>
125    where
126        I: IntoIterator + Clone,
127        I::Item: Borrow<u32>,
128    {
129        (**self).batch_block_header_raw(heights)
130    }
131
132    fn batch_estimate_fee<I>(&self, numbers: I) -> Result<Vec<f64>, Error>
133    where
134        I: IntoIterator + Clone,
135        I::Item: Borrow<usize>,
136    {
137        (**self).batch_estimate_fee(numbers)
138    }
139
140    fn transaction_broadcast_raw(&self, raw_tx: &[u8]) -> Result<Txid, Error> {
141        (**self).transaction_broadcast_raw(raw_tx)
142    }
143
144    fn transaction_get_merkle(&self, txid: &Txid, height: usize) -> Result<GetMerkleRes, Error> {
145        (**self).transaction_get_merkle(txid, height)
146    }
147
148    fn txid_from_pos(&self, height: usize, tx_pos: usize) -> Result<Txid, Error> {
149        (**self).txid_from_pos(height, tx_pos)
150    }
151
152    fn txid_from_pos_with_merkle(
153        &self,
154        height: usize,
155        tx_pos: usize,
156    ) -> Result<TxidFromPosRes, Error> {
157        (**self).txid_from_pos_with_merkle(height, tx_pos)
158    }
159
160    fn server_features(&self) -> Result<ServerFeaturesRes, Error> {
161        (**self).server_features()
162    }
163
164    fn ping(&self) -> Result<(), Error> {
165        (**self).ping()
166    }
167
168    #[cfg(feature = "debug-calls")]
169    fn calls_made(&self) -> Result<usize, Error> {
170        (**self).calls_made()
171    }
172}
173
174/// API calls exposed by an Electrum client
175pub trait ElectrumApi {
176    /// Gets the block header for height `height`.
177    fn block_header(&self, height: usize) -> Result<block::Header, Error> {
178        Ok(deserialize(&self.block_header_raw(height)?)?)
179    }
180
181    /// Subscribes to notifications for new block headers, by sending a `blockchain.headers.subscribe` call.
182    fn block_headers_subscribe(&self) -> Result<HeaderNotification, Error> {
183        self.block_headers_subscribe_raw()?.try_into()
184    }
185
186    /// Tries to pop one queued notification for a new block header that we might have received.
187    /// Returns `None` if there are no items in the queue.
188    fn block_headers_pop(&self) -> Result<Option<HeaderNotification>, Error> {
189        self.block_headers_pop_raw()?
190            .map(|raw| raw.try_into())
191            .transpose()
192    }
193
194    /// Gets the transaction with `txid`. Returns an error if not found.
195    fn transaction_get(&self, txid: &Txid) -> Result<Transaction, Error> {
196        Ok(deserialize(&self.transaction_get_raw(txid)?)?)
197    }
198
199    /// Batch version of [`transaction_get`](#method.transaction_get).
200    ///
201    /// Takes a list of `txids` and returns a list of transactions.
202    fn batch_transaction_get<'t, I>(&self, txids: I) -> Result<Vec<Transaction>, Error>
203    where
204        I: IntoIterator + Clone,
205        I::Item: Borrow<&'t Txid>,
206    {
207        self.batch_transaction_get_raw(txids)?
208            .iter()
209            .map(|s| Ok(deserialize(s)?))
210            .collect()
211    }
212
213    /// Batch version of [`block_header`](#method.block_header).
214    ///
215    /// Takes a list of `heights` of blocks and returns a list of headers.
216    fn batch_block_header<I>(&self, heights: I) -> Result<Vec<block::Header>, Error>
217    where
218        I: IntoIterator + Clone,
219        I::Item: Borrow<u32>,
220    {
221        self.batch_block_header_raw(heights)?
222            .iter()
223            .map(|s| Ok(deserialize(s)?))
224            .collect()
225    }
226
227    /// Broadcasts a transaction to the network.
228    fn transaction_broadcast(&self, tx: &Transaction) -> Result<Txid, Error> {
229        let buffer: Vec<u8> = serialize(tx);
230        self.transaction_broadcast_raw(&buffer)
231    }
232
233    /// Executes the requested API call returning the raw answer.
234    fn raw_call(
235        &self,
236        method_name: &str,
237        params: impl IntoIterator<Item = Param>,
238    ) -> Result<serde_json::Value, Error>;
239
240    /// Execute a queue of calls stored in a [`Batch`](../batch/struct.Batch.html) struct. Returns
241    /// `Ok()` **only if** all of the calls are successful. The order of the JSON `Value`s returned
242    /// reflects the order in which the calls were made on the `Batch` struct.
243    fn batch_call(&self, batch: &Batch) -> Result<Vec<serde_json::Value>, Error>;
244
245    /// Subscribes to notifications for new block headers, by sending a `blockchain.headers.subscribe` call and
246    /// returns the current tip as raw bytes instead of deserializing them.
247    fn block_headers_subscribe_raw(&self) -> Result<RawHeaderNotification, Error>;
248
249    /// Tries to pop one queued notification for a new block header that we might have received.
250    /// Returns a the header in raw bytes if a notification is found in the queue, None otherwise.
251    fn block_headers_pop_raw(&self) -> Result<Option<RawHeaderNotification>, Error>;
252
253    /// Gets the raw bytes of block header for height `height`.
254    fn block_header_raw(&self, height: usize) -> Result<Vec<u8>, Error>;
255
256    /// Tries to fetch `count` block headers starting from `start_height`.
257    fn block_headers(&self, start_height: usize, count: usize) -> Result<GetHeadersRes, Error>;
258
259    /// Estimates the fee required in **Bitcoin per kilobyte** to confirm a transaction in `number` blocks.
260    fn estimate_fee(&self, number: usize) -> Result<f64, Error>;
261
262    /// Returns the minimum accepted fee by the server's node in **Bitcoin, not Satoshi**.
263    fn relay_fee(&self) -> Result<f64, Error>;
264
265    /// Subscribes to notifications for activity on a specific *scriptPubKey*.
266    ///
267    /// Returns a [`ScriptStatus`](../types/type.ScriptStatus.html) when successful that represents
268    /// the current status for the requested script.
269    ///
270    /// Returns [`Error::AlreadySubscribed`](../types/enum.Error.html#variant.AlreadySubscribed) if
271    /// already subscribed to the script.
272    fn script_subscribe(&self, script: &Script) -> Result<Option<ScriptStatus>, Error>;
273
274    /// Batch version of [`script_subscribe`](#method.script_subscribe).
275    ///
276    /// Takes a list of scripts and returns a list of script status responses.
277    ///
278    /// Note you should pass a reference to a collection because otherwise an expensive clone is made
279    fn batch_script_subscribe<'s, I>(&self, scripts: I) -> Result<Vec<Option<ScriptStatus>>, Error>
280    where
281        I: IntoIterator + Clone,
282        I::Item: Borrow<&'s Script>;
283
284    /// Subscribes to notifications for activity on a specific *scriptPubKey*.
285    ///
286    /// Returns a `bool` with the server response when successful.
287    ///
288    /// Returns [`Error::NotSubscribed`](../types/enum.Error.html#variant.NotSubscribed) if
289    /// not subscribed to the script.
290    fn script_unsubscribe(&self, script: &Script) -> Result<bool, Error>;
291
292    /// Tries to pop one queued notification for a the requested script. Returns `None` if there are no items in the queue.
293    fn script_pop(&self, script: &Script) -> Result<Option<ScriptStatus>, Error>;
294
295    /// Returns the balance for a *scriptPubKey*.
296    fn script_get_balance(&self, script: &Script) -> Result<GetBalanceRes, Error>;
297
298    /// Batch version of [`script_get_balance`](#method.script_get_balance).
299    ///
300    /// Takes a list of scripts and returns a list of balance responses.
301    fn batch_script_get_balance<'s, I>(&self, scripts: I) -> Result<Vec<GetBalanceRes>, Error>
302    where
303        I: IntoIterator + Clone,
304        I::Item: Borrow<&'s Script>;
305
306    /// Returns the history for a *scriptPubKey*
307    fn script_get_history(&self, script: &Script) -> Result<Vec<GetHistoryRes>, Error>;
308
309    /// Batch version of [`script_get_history`](#method.script_get_history).
310    ///
311    /// Takes a list of scripts and returns a list of history responses.
312    fn batch_script_get_history<'s, I>(&self, scripts: I) -> Result<Vec<Vec<GetHistoryRes>>, Error>
313    where
314        I: IntoIterator + Clone,
315        I::Item: Borrow<&'s Script>;
316
317    /// Returns the list of unspent outputs for a *scriptPubKey*
318    fn script_list_unspent(&self, script: &Script) -> Result<Vec<ListUnspentRes>, Error>;
319
320    /// Batch version of [`script_list_unspent`](#method.script_list_unspent).
321    ///
322    /// Takes a list of scripts and returns a list of a list of utxos.
323    fn batch_script_list_unspent<'s, I>(
324        &self,
325        scripts: I,
326    ) -> Result<Vec<Vec<ListUnspentRes>>, Error>
327    where
328        I: IntoIterator + Clone,
329        I::Item: Borrow<&'s Script>;
330
331    /// Gets the raw bytes of a transaction with `txid`. Returns an error if not found.
332    fn transaction_get_raw(&self, txid: &Txid) -> Result<Vec<u8>, Error>;
333
334    /// Batch version of [`transaction_get_raw`](#method.transaction_get_raw).
335    ///
336    /// Takes a list of `txids` and returns a list of transactions raw bytes.
337    fn batch_transaction_get_raw<'t, I>(&self, txids: I) -> Result<Vec<Vec<u8>>, Error>
338    where
339        I: IntoIterator + Clone,
340        I::Item: Borrow<&'t Txid>;
341
342    /// Batch version of [`block_header_raw`](#method.block_header_raw).
343    ///
344    /// Takes a list of `heights` of blocks and returns a list of block header raw bytes.
345    fn batch_block_header_raw<I>(&self, heights: I) -> Result<Vec<Vec<u8>>, Error>
346    where
347        I: IntoIterator + Clone,
348        I::Item: Borrow<u32>;
349
350    /// Batch version of [`estimate_fee`](#method.estimate_fee).
351    ///
352    /// Takes a list of `numbers` of blocks and returns a list of fee required in
353    /// **Satoshis per kilobyte** to confirm a transaction in the given number of blocks.
354    fn batch_estimate_fee<I>(&self, numbers: I) -> Result<Vec<f64>, Error>
355    where
356        I: IntoIterator + Clone,
357        I::Item: Borrow<usize>;
358
359    /// Broadcasts the raw bytes of a transaction to the network.
360    fn transaction_broadcast_raw(&self, raw_tx: &[u8]) -> Result<Txid, Error>;
361
362    /// Returns the merkle path for the transaction `txid` confirmed in the block at `height`.
363    fn transaction_get_merkle(&self, txid: &Txid, height: usize) -> Result<GetMerkleRes, Error>;
364
365    /// Returns a transaction hash, given a block `height` and a `tx_pos` in the block.
366    fn txid_from_pos(&self, height: usize, tx_pos: usize) -> Result<Txid, Error>;
367
368    /// Returns a transaction hash and a merkle path, given a block `height` and a `tx_pos` in the
369    /// block.
370    fn txid_from_pos_with_merkle(
371        &self,
372        height: usize,
373        tx_pos: usize,
374    ) -> Result<TxidFromPosRes, Error>;
375
376    /// Returns the capabilities of the server.
377    fn server_features(&self) -> Result<ServerFeaturesRes, Error>;
378
379    /// Pings the server. This method can also be used as a "dummy" call to trigger the processing
380    /// of incoming block header or script notifications.
381    fn ping(&self) -> Result<(), Error>;
382
383    #[cfg(feature = "debug-calls")]
384    /// Returns the number of network calls made since the creation of the client.
385    fn calls_made(&self) -> Result<usize, Error>;
386}
387
388#[cfg(test)]
389mod test {
390    use std::{borrow::Cow, sync::Arc};
391
392    use super::ElectrumApi;
393
394    #[derive(Debug, Clone)]
395    struct FakeApi;
396
397    impl ElectrumApi for FakeApi {
398        fn raw_call(
399            &self,
400            _: &str,
401            _: impl IntoIterator<Item = super::Param>,
402        ) -> Result<serde_json::Value, super::Error> {
403            unreachable!()
404        }
405
406        fn batch_call(&self, _: &crate::Batch) -> Result<Vec<serde_json::Value>, super::Error> {
407            unreachable!()
408        }
409
410        fn block_headers_subscribe_raw(
411            &self,
412        ) -> Result<super::RawHeaderNotification, super::Error> {
413            unreachable!()
414        }
415
416        fn block_headers_pop_raw(
417            &self,
418        ) -> Result<Option<super::RawHeaderNotification>, super::Error> {
419            unreachable!()
420        }
421
422        fn block_header_raw(&self, _: usize) -> Result<Vec<u8>, super::Error> {
423            unreachable!()
424        }
425
426        fn block_headers(&self, _: usize, _: usize) -> Result<super::GetHeadersRes, super::Error> {
427            unreachable!()
428        }
429
430        fn estimate_fee(&self, _: usize) -> Result<f64, super::Error> {
431            unreachable!()
432        }
433
434        fn relay_fee(&self) -> Result<f64, super::Error> {
435            unreachable!()
436        }
437
438        fn script_subscribe(
439            &self,
440            _: &bitcoin::Script,
441        ) -> Result<Option<super::ScriptStatus>, super::Error> {
442            unreachable!()
443        }
444
445        fn batch_script_subscribe<'s, I>(
446            &self,
447            _: I,
448        ) -> Result<Vec<Option<super::ScriptStatus>>, super::Error>
449        where
450            I: IntoIterator + Clone,
451            I::Item: std::borrow::Borrow<&'s bitcoin::Script>,
452        {
453            unreachable!()
454        }
455
456        fn script_unsubscribe(&self, _: &bitcoin::Script) -> Result<bool, super::Error> {
457            unreachable!()
458        }
459
460        fn script_pop(
461            &self,
462            _: &bitcoin::Script,
463        ) -> Result<Option<super::ScriptStatus>, super::Error> {
464            unreachable!()
465        }
466
467        fn script_get_balance(
468            &self,
469            _: &bitcoin::Script,
470        ) -> Result<super::GetBalanceRes, super::Error> {
471            unreachable!()
472        }
473
474        fn batch_script_get_balance<'s, I>(
475            &self,
476            _: I,
477        ) -> Result<Vec<super::GetBalanceRes>, super::Error>
478        where
479            I: IntoIterator + Clone,
480            I::Item: std::borrow::Borrow<&'s bitcoin::Script>,
481        {
482            unreachable!()
483        }
484
485        fn script_get_history(
486            &self,
487            _: &bitcoin::Script,
488        ) -> Result<Vec<super::GetHistoryRes>, super::Error> {
489            unreachable!()
490        }
491
492        fn batch_script_get_history<'s, I>(
493            &self,
494            _: I,
495        ) -> Result<Vec<Vec<super::GetHistoryRes>>, super::Error>
496        where
497            I: IntoIterator + Clone,
498            I::Item: std::borrow::Borrow<&'s bitcoin::Script>,
499        {
500            unreachable!()
501        }
502
503        fn script_list_unspent(
504            &self,
505            _: &bitcoin::Script,
506        ) -> Result<Vec<super::ListUnspentRes>, super::Error> {
507            unreachable!()
508        }
509
510        fn batch_script_list_unspent<'s, I>(
511            &self,
512            _: I,
513        ) -> Result<Vec<Vec<super::ListUnspentRes>>, super::Error>
514        where
515            I: IntoIterator + Clone,
516            I::Item: std::borrow::Borrow<&'s bitcoin::Script>,
517        {
518            unreachable!()
519        }
520
521        fn transaction_get_raw(&self, _: &bitcoin::Txid) -> Result<Vec<u8>, super::Error> {
522            unreachable!()
523        }
524
525        fn batch_transaction_get_raw<'t, I>(&self, _: I) -> Result<Vec<Vec<u8>>, super::Error>
526        where
527            I: IntoIterator + Clone,
528            I::Item: std::borrow::Borrow<&'t bitcoin::Txid>,
529        {
530            unreachable!()
531        }
532
533        fn batch_block_header_raw<I>(&self, _: I) -> Result<Vec<Vec<u8>>, super::Error>
534        where
535            I: IntoIterator + Clone,
536            I::Item: std::borrow::Borrow<u32>,
537        {
538            unreachable!()
539        }
540
541        fn batch_estimate_fee<I>(&self, _: I) -> Result<Vec<f64>, super::Error>
542        where
543            I: IntoIterator + Clone,
544            I::Item: std::borrow::Borrow<usize>,
545        {
546            unreachable!()
547        }
548
549        fn transaction_broadcast_raw(&self, _: &[u8]) -> Result<bitcoin::Txid, super::Error> {
550            unreachable!()
551        }
552
553        fn transaction_get_merkle(
554            &self,
555            _: &bitcoin::Txid,
556            _: usize,
557        ) -> Result<super::GetMerkleRes, super::Error> {
558            unreachable!()
559        }
560
561        fn txid_from_pos(&self, _: usize, _: usize) -> Result<bitcoin::Txid, super::Error> {
562            unreachable!()
563        }
564
565        fn txid_from_pos_with_merkle(
566            &self,
567            _: usize,
568            _: usize,
569        ) -> Result<super::TxidFromPosRes, super::Error> {
570            unreachable!()
571        }
572
573        fn server_features(&self) -> Result<super::ServerFeaturesRes, super::Error> {
574            unreachable!()
575        }
576
577        fn ping(&self) -> Result<(), super::Error> {
578            unreachable!()
579        }
580
581        #[cfg(feature = "debug-calls")]
582        fn calls_made(&self) -> Result<usize, super::Error> {
583            unreachable!()
584        }
585    }
586
587    fn is_impl<A: ElectrumApi>() {}
588
589    #[test]
590    fn deref() {
591        is_impl::<FakeApi>();
592        is_impl::<&FakeApi>();
593        is_impl::<Arc<FakeApi>>();
594        is_impl::<Box<FakeApi>>();
595        is_impl::<Cow<FakeApi>>();
596    }
597}