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 batch_transaction_get_merkle<I>(
149        &self,
150        txids_and_heights: I,
151    ) -> Result<Vec<GetMerkleRes>, Error>
152    where
153        I: IntoIterator + Clone,
154        I::Item: Borrow<(Txid, usize)>,
155    {
156        (**self).batch_transaction_get_merkle(txids_and_heights)
157    }
158
159    fn txid_from_pos(&self, height: usize, tx_pos: usize) -> Result<Txid, Error> {
160        (**self).txid_from_pos(height, tx_pos)
161    }
162
163    fn txid_from_pos_with_merkle(
164        &self,
165        height: usize,
166        tx_pos: usize,
167    ) -> Result<TxidFromPosRes, Error> {
168        (**self).txid_from_pos_with_merkle(height, tx_pos)
169    }
170
171    fn server_features(&self) -> Result<ServerFeaturesRes, Error> {
172        (**self).server_features()
173    }
174
175    fn ping(&self) -> Result<(), Error> {
176        (**self).ping()
177    }
178
179    #[cfg(feature = "debug-calls")]
180    fn calls_made(&self) -> Result<usize, Error> {
181        (**self).calls_made()
182    }
183}
184
185/// API calls exposed by an Electrum client
186pub trait ElectrumApi {
187    /// Gets the block header for height `height`.
188    fn block_header(&self, height: usize) -> Result<block::Header, Error> {
189        Ok(deserialize(&self.block_header_raw(height)?)?)
190    }
191
192    /// Subscribes to notifications for new block headers, by sending a `blockchain.headers.subscribe` call.
193    fn block_headers_subscribe(&self) -> Result<HeaderNotification, Error> {
194        self.block_headers_subscribe_raw()?.try_into()
195    }
196
197    /// Tries to pop one queued notification for a new block header that we might have received.
198    /// Returns `None` if there are no items in the queue.
199    fn block_headers_pop(&self) -> Result<Option<HeaderNotification>, Error> {
200        self.block_headers_pop_raw()?
201            .map(|raw| raw.try_into())
202            .transpose()
203    }
204
205    /// Gets the transaction with `txid`. Returns an error if not found.
206    fn transaction_get(&self, txid: &Txid) -> Result<Transaction, Error> {
207        Ok(deserialize(&self.transaction_get_raw(txid)?)?)
208    }
209
210    /// Batch version of [`transaction_get`](#method.transaction_get).
211    ///
212    /// Takes a list of `txids` and returns a list of transactions.
213    fn batch_transaction_get<'t, I>(&self, txids: I) -> Result<Vec<Transaction>, Error>
214    where
215        I: IntoIterator + Clone,
216        I::Item: Borrow<&'t Txid>,
217    {
218        self.batch_transaction_get_raw(txids)?
219            .iter()
220            .map(|s| Ok(deserialize(s)?))
221            .collect()
222    }
223
224    /// Batch version of [`block_header`](#method.block_header).
225    ///
226    /// Takes a list of `heights` of blocks and returns a list of headers.
227    fn batch_block_header<I>(&self, heights: I) -> Result<Vec<block::Header>, Error>
228    where
229        I: IntoIterator + Clone,
230        I::Item: Borrow<u32>,
231    {
232        self.batch_block_header_raw(heights)?
233            .iter()
234            .map(|s| Ok(deserialize(s)?))
235            .collect()
236    }
237
238    /// Broadcasts a transaction to the network.
239    fn transaction_broadcast(&self, tx: &Transaction) -> Result<Txid, Error> {
240        let buffer: Vec<u8> = serialize(tx);
241        self.transaction_broadcast_raw(&buffer)
242    }
243
244    /// Executes the requested API call returning the raw answer.
245    fn raw_call(
246        &self,
247        method_name: &str,
248        params: impl IntoIterator<Item = Param>,
249    ) -> Result<serde_json::Value, Error>;
250
251    /// Execute a queue of calls stored in a [`Batch`](../batch/struct.Batch.html) struct. Returns
252    /// `Ok()` **only if** all of the calls are successful. The order of the JSON `Value`s returned
253    /// reflects the order in which the calls were made on the `Batch` struct.
254    fn batch_call(&self, batch: &Batch) -> Result<Vec<serde_json::Value>, Error>;
255
256    /// Subscribes to notifications for new block headers, by sending a `blockchain.headers.subscribe` call and
257    /// returns the current tip as raw bytes instead of deserializing them.
258    fn block_headers_subscribe_raw(&self) -> Result<RawHeaderNotification, Error>;
259
260    /// Tries to pop one queued notification for a new block header that we might have received.
261    /// Returns a the header in raw bytes if a notification is found in the queue, None otherwise.
262    fn block_headers_pop_raw(&self) -> Result<Option<RawHeaderNotification>, Error>;
263
264    /// Gets the raw bytes of block header for height `height`.
265    fn block_header_raw(&self, height: usize) -> Result<Vec<u8>, Error>;
266
267    /// Tries to fetch `count` block headers starting from `start_height`.
268    fn block_headers(&self, start_height: usize, count: usize) -> Result<GetHeadersRes, Error>;
269
270    /// Estimates the fee required in **Bitcoin per kilobyte** to confirm a transaction in `number` blocks.
271    fn estimate_fee(&self, number: usize) -> Result<f64, Error>;
272
273    /// Returns the minimum accepted fee by the server's node in **Bitcoin, not Satoshi**.
274    fn relay_fee(&self) -> Result<f64, Error>;
275
276    /// Subscribes to notifications for activity on a specific *scriptPubKey*.
277    ///
278    /// Returns a [`ScriptStatus`](../types/type.ScriptStatus.html) when successful that represents
279    /// the current status for the requested script.
280    ///
281    /// Returns [`Error::AlreadySubscribed`](../types/enum.Error.html#variant.AlreadySubscribed) if
282    /// already subscribed to the script.
283    fn script_subscribe(&self, script: &Script) -> Result<Option<ScriptStatus>, Error>;
284
285    /// Batch version of [`script_subscribe`](#method.script_subscribe).
286    ///
287    /// Takes a list of scripts and returns a list of script status responses.
288    ///
289    /// Note you should pass a reference to a collection because otherwise an expensive clone is made
290    fn batch_script_subscribe<'s, I>(&self, scripts: I) -> Result<Vec<Option<ScriptStatus>>, Error>
291    where
292        I: IntoIterator + Clone,
293        I::Item: Borrow<&'s Script>;
294
295    /// Subscribes to notifications for activity on a specific *scriptPubKey*.
296    ///
297    /// Returns a `bool` with the server response when successful.
298    ///
299    /// Returns [`Error::NotSubscribed`](../types/enum.Error.html#variant.NotSubscribed) if
300    /// not subscribed to the script.
301    fn script_unsubscribe(&self, script: &Script) -> Result<bool, Error>;
302
303    /// Tries to pop one queued notification for a the requested script. Returns `None` if there are no items in the queue.
304    fn script_pop(&self, script: &Script) -> Result<Option<ScriptStatus>, Error>;
305
306    /// Returns the balance for a *scriptPubKey*.
307    fn script_get_balance(&self, script: &Script) -> Result<GetBalanceRes, Error>;
308
309    /// Batch version of [`script_get_balance`](#method.script_get_balance).
310    ///
311    /// Takes a list of scripts and returns a list of balance responses.
312    fn batch_script_get_balance<'s, I>(&self, scripts: I) -> Result<Vec<GetBalanceRes>, Error>
313    where
314        I: IntoIterator + Clone,
315        I::Item: Borrow<&'s Script>;
316
317    /// Returns the history for a *scriptPubKey*
318    fn script_get_history(&self, script: &Script) -> Result<Vec<GetHistoryRes>, Error>;
319
320    /// Batch version of [`script_get_history`](#method.script_get_history).
321    ///
322    /// Takes a list of scripts and returns a list of history responses.
323    fn batch_script_get_history<'s, I>(&self, scripts: I) -> Result<Vec<Vec<GetHistoryRes>>, Error>
324    where
325        I: IntoIterator + Clone,
326        I::Item: Borrow<&'s Script>;
327
328    /// Returns the list of unspent outputs for a *scriptPubKey*
329    fn script_list_unspent(&self, script: &Script) -> Result<Vec<ListUnspentRes>, Error>;
330
331    /// Batch version of [`script_list_unspent`](#method.script_list_unspent).
332    ///
333    /// Takes a list of scripts and returns a list of a list of utxos.
334    fn batch_script_list_unspent<'s, I>(
335        &self,
336        scripts: I,
337    ) -> Result<Vec<Vec<ListUnspentRes>>, Error>
338    where
339        I: IntoIterator + Clone,
340        I::Item: Borrow<&'s Script>;
341
342    /// Gets the raw bytes of a transaction with `txid`. Returns an error if not found.
343    fn transaction_get_raw(&self, txid: &Txid) -> Result<Vec<u8>, Error>;
344
345    /// Batch version of [`transaction_get_raw`](#method.transaction_get_raw).
346    ///
347    /// Takes a list of `txids` and returns a list of transactions raw bytes.
348    fn batch_transaction_get_raw<'t, I>(&self, txids: I) -> Result<Vec<Vec<u8>>, Error>
349    where
350        I: IntoIterator + Clone,
351        I::Item: Borrow<&'t Txid>;
352
353    /// Batch version of [`block_header_raw`](#method.block_header_raw).
354    ///
355    /// Takes a list of `heights` of blocks and returns a list of block header raw bytes.
356    fn batch_block_header_raw<I>(&self, heights: I) -> Result<Vec<Vec<u8>>, Error>
357    where
358        I: IntoIterator + Clone,
359        I::Item: Borrow<u32>;
360
361    /// Batch version of [`estimate_fee`](#method.estimate_fee).
362    ///
363    /// Takes a list of `numbers` of blocks and returns a list of fee required in
364    /// **Satoshis per kilobyte** to confirm a transaction in the given number of blocks.
365    fn batch_estimate_fee<I>(&self, numbers: I) -> Result<Vec<f64>, Error>
366    where
367        I: IntoIterator + Clone,
368        I::Item: Borrow<usize>;
369
370    /// Broadcasts the raw bytes of a transaction to the network.
371    fn transaction_broadcast_raw(&self, raw_tx: &[u8]) -> Result<Txid, Error>;
372
373    /// Returns the merkle path for the transaction `txid` confirmed in the block at `height`.
374    fn transaction_get_merkle(&self, txid: &Txid, height: usize) -> Result<GetMerkleRes, Error>;
375
376    /// Batch version of [`transaction_get_merkle`](#method.transaction_get_merkle).
377    ///
378    /// Take a list of `(txid, height)`, for transactions with `txid` confirmed in the block at `height`.
379    fn batch_transaction_get_merkle<I>(
380        &self,
381        txids_and_heights: I,
382    ) -> Result<Vec<GetMerkleRes>, Error>
383    where
384        I: IntoIterator + Clone,
385        I::Item: Borrow<(Txid, usize)>;
386
387    /// Returns a transaction hash, given a block `height` and a `tx_pos` in the block.
388    fn txid_from_pos(&self, height: usize, tx_pos: usize) -> Result<Txid, Error>;
389
390    /// Returns a transaction hash and a merkle path, given a block `height` and a `tx_pos` in the
391    /// block.
392    fn txid_from_pos_with_merkle(
393        &self,
394        height: usize,
395        tx_pos: usize,
396    ) -> Result<TxidFromPosRes, Error>;
397
398    /// Returns the capabilities of the server.
399    fn server_features(&self) -> Result<ServerFeaturesRes, Error>;
400
401    /// Pings the server. This method can also be used as a "dummy" call to trigger the processing
402    /// of incoming block header or script notifications.
403    fn ping(&self) -> Result<(), Error>;
404
405    #[cfg(feature = "debug-calls")]
406    /// Returns the number of network calls made since the creation of the client.
407    fn calls_made(&self) -> Result<usize, Error>;
408}
409
410#[cfg(test)]
411mod test {
412    use std::{borrow::Cow, sync::Arc};
413
414    use super::ElectrumApi;
415
416    #[derive(Debug, Clone)]
417    struct FakeApi;
418
419    impl ElectrumApi for FakeApi {
420        fn raw_call(
421            &self,
422            _: &str,
423            _: impl IntoIterator<Item = super::Param>,
424        ) -> Result<serde_json::Value, super::Error> {
425            unreachable!()
426        }
427
428        fn batch_call(&self, _: &crate::Batch) -> Result<Vec<serde_json::Value>, super::Error> {
429            unreachable!()
430        }
431
432        fn block_headers_subscribe_raw(
433            &self,
434        ) -> Result<super::RawHeaderNotification, super::Error> {
435            unreachable!()
436        }
437
438        fn block_headers_pop_raw(
439            &self,
440        ) -> Result<Option<super::RawHeaderNotification>, super::Error> {
441            unreachable!()
442        }
443
444        fn block_header_raw(&self, _: usize) -> Result<Vec<u8>, super::Error> {
445            unreachable!()
446        }
447
448        fn block_headers(&self, _: usize, _: usize) -> Result<super::GetHeadersRes, super::Error> {
449            unreachable!()
450        }
451
452        fn estimate_fee(&self, _: usize) -> Result<f64, super::Error> {
453            unreachable!()
454        }
455
456        fn relay_fee(&self) -> Result<f64, super::Error> {
457            unreachable!()
458        }
459
460        fn script_subscribe(
461            &self,
462            _: &bitcoin::Script,
463        ) -> Result<Option<super::ScriptStatus>, super::Error> {
464            unreachable!()
465        }
466
467        fn batch_script_subscribe<'s, I>(
468            &self,
469            _: I,
470        ) -> Result<Vec<Option<super::ScriptStatus>>, super::Error>
471        where
472            I: IntoIterator + Clone,
473            I::Item: std::borrow::Borrow<&'s bitcoin::Script>,
474        {
475            unreachable!()
476        }
477
478        fn script_unsubscribe(&self, _: &bitcoin::Script) -> Result<bool, super::Error> {
479            unreachable!()
480        }
481
482        fn script_pop(
483            &self,
484            _: &bitcoin::Script,
485        ) -> Result<Option<super::ScriptStatus>, super::Error> {
486            unreachable!()
487        }
488
489        fn script_get_balance(
490            &self,
491            _: &bitcoin::Script,
492        ) -> Result<super::GetBalanceRes, super::Error> {
493            unreachable!()
494        }
495
496        fn batch_script_get_balance<'s, I>(
497            &self,
498            _: I,
499        ) -> Result<Vec<super::GetBalanceRes>, super::Error>
500        where
501            I: IntoIterator + Clone,
502            I::Item: std::borrow::Borrow<&'s bitcoin::Script>,
503        {
504            unreachable!()
505        }
506
507        fn script_get_history(
508            &self,
509            _: &bitcoin::Script,
510        ) -> Result<Vec<super::GetHistoryRes>, super::Error> {
511            unreachable!()
512        }
513
514        fn batch_script_get_history<'s, I>(
515            &self,
516            _: I,
517        ) -> Result<Vec<Vec<super::GetHistoryRes>>, super::Error>
518        where
519            I: IntoIterator + Clone,
520            I::Item: std::borrow::Borrow<&'s bitcoin::Script>,
521        {
522            unreachable!()
523        }
524
525        fn script_list_unspent(
526            &self,
527            _: &bitcoin::Script,
528        ) -> Result<Vec<super::ListUnspentRes>, super::Error> {
529            unreachable!()
530        }
531
532        fn batch_script_list_unspent<'s, I>(
533            &self,
534            _: I,
535        ) -> Result<Vec<Vec<super::ListUnspentRes>>, super::Error>
536        where
537            I: IntoIterator + Clone,
538            I::Item: std::borrow::Borrow<&'s bitcoin::Script>,
539        {
540            unreachable!()
541        }
542
543        fn transaction_get_raw(&self, _: &bitcoin::Txid) -> Result<Vec<u8>, super::Error> {
544            unreachable!()
545        }
546
547        fn batch_transaction_get_raw<'t, I>(&self, _: I) -> Result<Vec<Vec<u8>>, super::Error>
548        where
549            I: IntoIterator + Clone,
550            I::Item: std::borrow::Borrow<&'t bitcoin::Txid>,
551        {
552            unreachable!()
553        }
554
555        fn batch_block_header_raw<I>(&self, _: I) -> Result<Vec<Vec<u8>>, super::Error>
556        where
557            I: IntoIterator + Clone,
558            I::Item: std::borrow::Borrow<u32>,
559        {
560            unreachable!()
561        }
562
563        fn batch_estimate_fee<I>(&self, _: I) -> Result<Vec<f64>, super::Error>
564        where
565            I: IntoIterator + Clone,
566            I::Item: std::borrow::Borrow<usize>,
567        {
568            unreachable!()
569        }
570
571        fn transaction_broadcast_raw(&self, _: &[u8]) -> Result<bitcoin::Txid, super::Error> {
572            unreachable!()
573        }
574
575        fn transaction_get_merkle(
576            &self,
577            _: &bitcoin::Txid,
578            _: usize,
579        ) -> Result<super::GetMerkleRes, super::Error> {
580            unreachable!()
581        }
582
583        fn batch_transaction_get_merkle<I>(
584            &self,
585            _: I,
586        ) -> Result<Vec<crate::GetMerkleRes>, crate::Error>
587        where
588            I: IntoIterator + Clone,
589            I::Item: std::borrow::Borrow<(bitcoin::Txid, usize)>,
590        {
591            unreachable!()
592        }
593
594        fn txid_from_pos(&self, _: usize, _: usize) -> Result<bitcoin::Txid, super::Error> {
595            unreachable!()
596        }
597
598        fn txid_from_pos_with_merkle(
599            &self,
600            _: usize,
601            _: usize,
602        ) -> Result<super::TxidFromPosRes, super::Error> {
603            unreachable!()
604        }
605
606        fn server_features(&self) -> Result<super::ServerFeaturesRes, super::Error> {
607            unreachable!()
608        }
609
610        fn ping(&self) -> Result<(), super::Error> {
611            unreachable!()
612        }
613
614        #[cfg(feature = "debug-calls")]
615        fn calls_made(&self) -> Result<usize, super::Error> {
616            unreachable!()
617        }
618    }
619
620    fn is_impl<A: ElectrumApi>() {}
621
622    #[test]
623    fn deref() {
624        is_impl::<FakeApi>();
625        is_impl::<&FakeApi>();
626        is_impl::<Arc<FakeApi>>();
627        is_impl::<Box<FakeApi>>();
628        is_impl::<Cow<FakeApi>>();
629    }
630}