1+ //! Electrum client
2+ //!
3+ //! This module contains definitions of all the complex data structures that are returned by calls
4+
15use std:: collections:: { BTreeMap , VecDeque } ;
26#[ cfg( test) ]
37use std:: fs:: File ;
@@ -55,6 +59,20 @@ macro_rules! impl_batch_call {
5559 } } ;
5660}
5761
62+ /// Instance of an Electrum client
63+ ///
64+ /// A `Client` maintains a constant connection with an Electrum server and exposes methods to
65+ /// interact with it. It can also subscribe and receive notifictations from the server about new
66+ /// blocks or activity on a specific *scriptPubKey*.
67+ ///
68+ /// The `Client` is modeled in such a way that allows the external caller to have full control over
69+ /// its functionality: no threads or tasks are spawned internally to monitor the state of the
70+ /// connection. This allows the caller to control its behavior through some *polling* functions,
71+ /// and ultimately makes the library more lightweight and easier to embed into existing
72+ /// projects.
73+ ///
74+ /// More transport methods can be used by manually creating an instance of this struct with an
75+ /// arbitray `S` type.
5876#[ derive( Debug ) ]
5977pub struct Client < S >
6078where
7088 calls : usize ,
7189}
7290
73- impl Client < TcpStream > {
74- pub fn new < A : ToSocketAddrs > ( socket_addr : A ) -> io:: Result < Self > {
91+ /// Transport type used to establish a plaintext TCP connection with the server
92+ pub type ElectrumPlaintextStream = TcpStream ;
93+ impl Client < ElectrumPlaintextStream > {
94+ /// Creates a new plaintext client and tries to connect to `socket_addr`.
95+ pub fn new < A : ToSocketAddrs > ( socket_addr : A ) -> Result < Self , Error > {
7596 let stream = TcpStream :: connect ( socket_addr) ?;
7697 let buf_reader = BufReader :: new ( stream. try_clone ( ) ?) ;
7798
@@ -88,7 +109,12 @@ impl Client<TcpStream> {
88109}
89110
90111#[ cfg( feature = "use-openssl" ) ]
91- impl Client < ClonableStream < SslStream < TcpStream > > > {
112+ /// Transport type used to establish an OpenSSL TLS encrypted/authenticated connection with the server
113+ pub type ElectrumSslStream = ClonableStream < SslStream < TcpStream > > ;
114+ #[ cfg( feature = "use-openssl" ) ]
115+ impl Client < ElectrumSslStream > {
116+ /// Creates a new SSL client and tries to connect to `socket_addr`. Optionally, if `domain` is not
117+ /// None, validates the server certificate.
92118 pub fn new_ssl < A : ToSocketAddrs > ( socket_addr : A , domain : Option < & str > ) -> Result < Self , Error > {
93119 let mut builder =
94120 SslConnector :: builder ( SslMethod :: tls ( ) ) . map_err ( Error :: InvalidSslMethod ) ?;
@@ -145,7 +171,15 @@ mod danger {
145171 any( feature = "default" , feature = "use-rustls" ) ,
146172 not( feature = "use-openssl" )
147173) ) ]
148- impl Client < ClonableStream < StreamOwned < ClientSession , TcpStream > > > {
174+ /// Transport type used to establish a Rustls TLS encrypted/authenticated connection with the server
175+ pub type ElectrumSslStream = ClonableStream < StreamOwned < ClientSession , TcpStream > > ;
176+ #[ cfg( all(
177+ any( feature = "default" , feature = "use-rustls" ) ,
178+ not( feature = "use-openssl" )
179+ ) ) ]
180+ impl Client < ElectrumSslStream > {
181+ /// Creates a new SSL client and tries to connect to `socket_addr`. Optionally, if `domain` is not
182+ /// None, validates the server certificate against `webpki-root`'s list of Certificate Authorities.
149183 pub fn new_ssl < A : ToSocketAddrs > ( socket_addr : A , domain : Option < & str > ) -> Result < Self , Error > {
150184 let mut config = ClientConfig :: new ( ) ;
151185 if domain. is_none ( ) {
@@ -183,7 +217,13 @@ impl Client<ClonableStream<StreamOwned<ClientSession, TcpStream>>> {
183217}
184218
185219#[ cfg( any( feature = "default" , feature = "proxy" ) ) ]
186- impl Client < ClonableStream < Socks5Stream > > {
220+ /// Transport type used to establish a connection to a server through a socks proxy
221+ pub type ElectrumProxyStream = ClonableStream < Socks5Stream > ;
222+ #[ cfg( any( feature = "default" , feature = "proxy" ) ) ]
223+ impl Client < ElectrumProxyStream > {
224+ /// Creates a new socks client and tries to connect to `target_addr` using `proxy_addr` as an
225+ /// unauthenticated socks proxy server. The DNS resolution of `target_addr`, if required, is done
226+ /// through the proxy. This allows to specify, for instance, `.onion` addresses.
187227 pub fn new_proxy < A : ToSocketAddrs , T : ToTargetAddr > (
188228 target_addr : T ,
189229 proxy_addr : A ,
@@ -250,6 +290,9 @@ impl<S: Read + Write> Client<S> {
250290 Ok ( resp[ "result" ] . take ( ) )
251291 }
252292
293+ /// Execute a queue of calls stored in a [`Batch`](../batch/struct.Batch.html) struct. Returns
294+ /// `Ok()` **only if** all of the calls are successful. The order of the JSON `Value`s returned
295+ /// reflects the order in which the calls were made on the `Batch` struct.
253296 pub fn batch_call ( & mut self , batch : Batch ) -> Result < Vec < serde_json:: Value > , Error > {
254297 let mut id_map = BTreeMap :: new ( ) ;
255298 let mut raw = Vec :: new ( ) ;
@@ -333,6 +376,8 @@ impl<S: Read + Write> Client<S> {
333376 Ok ( ( ) )
334377 }
335378
379+ /// Tries to read from the read buffer if any notifications were received since the last call
380+ /// or `poll`, and processes them
336381 pub fn poll ( & mut self ) -> Result < ( ) , Error > {
337382 // try to pull data from the stream
338383 self . buf_reader . fill_buf ( ) ?;
@@ -350,19 +395,23 @@ impl<S: Read + Write> Client<S> {
350395 Ok ( ( ) )
351396 }
352397
398+ /// Subscribes to notifications for new block headers, by sending a `blockchain.headers.subscribe` call.
353399 pub fn block_headers_subscribe ( & mut self ) -> Result < HeaderNotification , Error > {
354400 let req = Request :: new ( "blockchain.headers.subscribe" , vec ! [ ] ) ;
355401 let value = self . call ( req) ?;
356402
357403 Ok ( serde_json:: from_value ( value) ?)
358404 }
359405
406+ /// Tries to pop one queued notification for a new block header that we might have received.
407+ /// Returns `None` if there are no items in the queue.
360408 pub fn block_headers_poll ( & mut self ) -> Result < Option < HeaderNotification > , Error > {
361409 self . poll ( ) ?;
362410
363411 Ok ( self . headers . pop_front ( ) )
364412 }
365413
414+ /// Gets the block header for height `height`.
366415 pub fn block_header ( & mut self , height : usize ) -> Result < block:: BlockHeader , Error > {
367416 let req = Request :: new ( "blockchain.block.header" , vec ! [ Param :: Usize ( height) ] ) ;
368417 let result = self . call ( req) ?;
@@ -374,6 +423,7 @@ impl<S: Read + Write> Client<S> {
374423 ) ?) ?)
375424 }
376425
426+ /// Tries to fetch `count` block headers starting from `start_height`.
377427 pub fn block_headers (
378428 & mut self ,
379429 start_height : usize ,
@@ -397,6 +447,7 @@ impl<S: Read + Write> Client<S> {
397447 Ok ( deserialized)
398448 }
399449
450+ /// Estimates the fee required in **Satoshis per kilobyte** to confirm a transaction in `number` blocks.
400451 pub fn estimate_fee ( & mut self , number : usize ) -> Result < f64 , Error > {
401452 let req = Request :: new ( "blockchain.estimatefee" , vec ! [ Param :: Usize ( number) ] ) ;
402453 let result = self . call ( req) ?;
@@ -406,6 +457,7 @@ impl<S: Read + Write> Client<S> {
406457 . ok_or_else ( || Error :: InvalidResponse ( result. clone ( ) ) )
407458 }
408459
460+ /// Returns the minimum accepted fee by the server's node in **Bitcoin, not Satoshi**.
409461 pub fn relay_fee ( & mut self ) -> Result < f64 , Error > {
410462 let req = Request :: new ( "blockchain.relayfee" , vec ! [ ] ) ;
411463 let result = self . call ( req) ?;
@@ -415,6 +467,13 @@ impl<S: Read + Write> Client<S> {
415467 . ok_or_else ( || Error :: InvalidResponse ( result. clone ( ) ) )
416468 }
417469
470+ /// Subscribes to notifications for activity on a specific *scriptPubKey*.
471+ ///
472+ /// Returns a [`ScriptStatus`](../types/type.ScriptStatus.html) when successful that represents
473+ /// the current status for the requested script.
474+ ///
475+ /// Returns [`Error::AlreadySubscribed`](../types/enum.Error.html#variant.AlreadySubscribed) if
476+ /// already subscribed to the same script.
418477 pub fn script_subscribe ( & mut self , script : & Script ) -> Result < ScriptStatus , Error > {
419478 let script_hash = script. to_electrum_scripthash ( ) ;
420479
@@ -434,6 +493,12 @@ impl<S: Read + Write> Client<S> {
434493 Ok ( serde_json:: from_value ( value) ?)
435494 }
436495
496+ /// Subscribes to notifications for activity on a specific *scriptPubKey*.
497+ ///
498+ /// Returns a `bool` with the server response when successful.
499+ ///
500+ /// Returns [`Error::NotSubscribed`](../types/enum.Error.html#variant.NotSubscribed) if
501+ /// not subscribed to the script.
437502 pub fn script_unsubscribe ( & mut self , script : & Script ) -> Result < bool , Error > {
438503 let script_hash = script. to_electrum_scripthash ( ) ;
439504
@@ -453,6 +518,7 @@ impl<S: Read + Write> Client<S> {
453518 Ok ( answer)
454519 }
455520
521+ /// Tries to pop one queued notification for a the requested script. Returns `None` if there are no items in the queue.
456522 pub fn script_poll ( & mut self , script : & Script ) -> Result < Option < ScriptStatus > , Error > {
457523 self . poll ( ) ?;
458524
@@ -464,50 +530,63 @@ impl<S: Read + Write> Client<S> {
464530 }
465531 }
466532
533+ /// Returns the balance for a *scriptPubKey*
467534 pub fn script_get_balance ( & mut self , script : & Script ) -> Result < GetBalanceRes , Error > {
468535 let params = vec ! [ Param :: String ( script. to_electrum_scripthash( ) . to_hex( ) ) ] ;
469536 let req = Request :: new ( "blockchain.scripthash.get_balance" , params) ;
470537 let result = self . call ( req) ?;
471538
472539 Ok ( serde_json:: from_value ( result) ?)
473540 }
541+ /// Batch version of [`script_get_balance`](#method.script_get_balance).
542+ ///
543+ /// Takes a list of scripts and returns a list of balance responses.
474544 pub fn batch_script_get_balance (
475545 & mut self ,
476546 scripts : Vec < & Script > ,
477547 ) -> Result < Vec < GetBalanceRes > , Error > {
478548 impl_batch_call ! ( self , scripts, script_get_balance)
479549 }
480550
551+ /// Returns the history for a *scriptPubKey*
481552 pub fn script_get_history ( & mut self , script : & Script ) -> Result < Vec < GetHistoryRes > , Error > {
482553 let params = vec ! [ Param :: String ( script. to_electrum_scripthash( ) . to_hex( ) ) ] ;
483554 let req = Request :: new ( "blockchain.scripthash.get_history" , params) ;
484555 let result = self . call ( req) ?;
485556
486557 Ok ( serde_json:: from_value ( result) ?)
487558 }
559+ /// Batch version of [`script_get_history`](#method.script_get_history).
560+ ///
561+ /// Takes a list of scripts and returns a list of history responses.
488562 pub fn batch_script_get_history (
489563 & mut self ,
490564 scripts : Vec < & Script > ,
491565 ) -> Result < Vec < Vec < GetHistoryRes > > , Error > {
492566 impl_batch_call ! ( self , scripts, script_get_history)
493567 }
494568
569+ /// Returns the list of unspent outputs for a *scriptPubKey*
495570 pub fn script_list_unspent ( & mut self , script : & Script ) -> Result < Vec < ListUnspentRes > , Error > {
496571 let params = vec ! [ Param :: String ( script. to_electrum_scripthash( ) . to_hex( ) ) ] ;
497572 let req = Request :: new ( "blockchain.scripthash.listunspent" , params) ;
498573 let result = self . call ( req) ?;
499574
500575 Ok ( serde_json:: from_value ( result) ?)
501576 }
577+ /// Batch version of [`script_list_unspent`](#method.script_list_unspent).
578+ ///
579+ /// Takes a list of scripts and returns a list of a list of utxos.
502580 pub fn batch_script_list_unspent (
503581 & mut self ,
504582 scripts : Vec < & Script > ,
505583 ) -> Result < Vec < Vec < ListUnspentRes > > , Error > {
506584 impl_batch_call ! ( self , scripts, script_list_unspent)
507585 }
508586
509- pub fn transaction_get ( & mut self , tx_hash : & Txid ) -> Result < Transaction , Error > {
510- let params = vec ! [ Param :: String ( tx_hash. to_hex( ) ) ] ;
587+ /// Gets the raw transaction with `txid`. Returns an error if not found.
588+ pub fn transaction_get ( & mut self , txid : & Txid ) -> Result < Transaction , Error > {
589+ let params = vec ! [ Param :: String ( txid. to_hex( ) ) ] ;
511590 let req = Request :: new ( "blockchain.transaction.get" , params) ;
512591 let result = self . call ( req) ?;
513592
@@ -517,13 +596,14 @@ impl<S: Read + Write> Client<S> {
517596 . ok_or_else ( || Error :: InvalidResponse ( result. clone ( ) ) ) ?,
518597 ) ?) ?)
519598 }
520- pub fn batch_transaction_get (
521- & mut self ,
522- tx_hashes : Vec < & Txid > ,
523- ) -> Result < Vec < Transaction > , Error > {
524- impl_batch_call ! ( self , tx_hashes , transaction_get)
599+ /// Batch version of [`transaction_get`](#method.transaction_get).
600+ ///
601+ /// Takes a list of `txids` and returns a list of transactions.
602+ pub fn batch_transaction_get ( & mut self , txids : Vec < & Txid > ) -> Result < Vec < Transaction > , Error > {
603+ impl_batch_call ! ( self , txids , transaction_get)
525604 }
526605
606+ /// Broadcasts a transaction to the network.
527607 pub fn transaction_broadcast ( & mut self , tx : & Transaction ) -> Result < Txid , Error > {
528608 let buffer: Vec < u8 > = serialize ( tx) ;
529609 let params = vec ! [ Param :: String ( buffer. to_hex( ) ) ] ;
@@ -533,6 +613,7 @@ impl<S: Read + Write> Client<S> {
533613 Ok ( serde_json:: from_value ( result) ?)
534614 }
535615
616+ /// Returns the merkle path for the transaction `txid` confirmed in the block at `height`.
536617 pub fn transaction_get_merkle (
537618 & mut self ,
538619 txid : & Txid ,
@@ -545,6 +626,7 @@ impl<S: Read + Write> Client<S> {
545626 Ok ( serde_json:: from_value ( result) ?)
546627 }
547628
629+ /// Returns the capabilities of the server.
548630 pub fn server_features ( & mut self ) -> Result < ServerFeaturesRes , Error > {
549631 let req = Request :: new ( "server.features" , vec ! [ ] ) ;
550632 let result = self . call ( req) ?;
@@ -553,19 +635,20 @@ impl<S: Read + Write> Client<S> {
553635 }
554636
555637 #[ cfg( feature = "debug-calls" ) ]
638+ /// Returns the number of network calls made since the creation of the client.
556639 pub fn calls_made ( & self ) -> usize {
557640 self . calls
558641 }
559642
560643 #[ inline]
561644 #[ cfg( feature = "debug-calls" ) ]
562- pub fn increment_calls ( & mut self ) {
645+ fn increment_calls ( & mut self ) {
563646 self . calls += 1 ;
564647 }
565648
566649 #[ inline]
567650 #[ cfg( not( feature = "debug-calls" ) ) ]
568- pub fn increment_calls ( & self ) { }
651+ fn increment_calls ( & self ) { }
569652}
570653
571654#[ cfg( test) ]
0 commit comments