electrum/
api.rs

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