diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 3ef1e6e..f2fe058 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -72,7 +72,7 @@ jobs: strategy: fail-fast: false matrix: - toolchain: [ nightly, beta, stable, 1.77.0 ] + toolchain: [ nightly, beta, stable, 1.81.0 ] steps: - uses: actions/checkout@v4 - uses: dtolnay/rust-toolchain@master diff --git a/Cargo.toml b/Cargo.toml index f360b5b..59002cb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "bp-electrum" -version = "0.11.0-beta.9.3" +version = "0.11.1-alpha.2+unreviewed" authors = ["Alekos Filini ", "Dr Maxim Orlovsky "] license = "MIT" homepage = "https://github.com/BP-WG/bp-electrum-client" @@ -10,18 +10,17 @@ description = "Bitcoin Electrum client library. Supports plaintext, TLS and Onio keywords = ["bitcoin", "electrum"] readme = "README.md" edition = "2021" -# TODO: Set edition to 2021 -rust-version = "1.77.0" +rust-version = "1.81.0" [lib] name = "electrum" path = "src/lib.rs" [dependencies] -amplify = "4.7.0" +amplify = "4.8.0" sha2 = "0.10.8" log = "^0.4" -bp-std = { version = "0.11.0-beta.9.1", features = ["serde"] } +bp-core = { version = "0.11.1-alpha.2", features = ["serde"] } serde = { version = "^1.0", features = ["derive"] } serde_json = { version = "^1.0" } @@ -32,6 +31,9 @@ webpki-roots = { version = "0.26", optional = true } byteorder = { version = "1.0", optional = true } +[dev-dependencies] +bp-invoice = "0.11.1-alpha.2" + [target.'cfg(unix)'.dependencies] libc = { version = "0.2", optional = true } diff --git a/_typos.toml b/_typos.toml new file mode 100644 index 0000000..75b82b0 --- /dev/null +++ b/_typos.toml @@ -0,0 +1,4 @@ +[default] +extend-ignore-identifiers-re = [ + "Clonable*", +] diff --git a/examples/tor.rs b/examples/tor.rs index b175d6e..c2f0437 100644 --- a/examples/tor.rs +++ b/examples/tor.rs @@ -3,7 +3,7 @@ extern crate electrum; use electrum::{Client, ConfigBuilder, ElectrumApi, Socks5Config}; fn main() { - // NOTE: This assumes Tor is running localy, with an unauthenticated Socks5 listening at + // NOTE: This assumes Tor is running locally, with an unauthenticated Socks5 listening at // localhost:9050 let proxy = Socks5Config::new("127.0.0.1:9050"); let config = ConfigBuilder::new().socks5(Some(proxy)).build(); diff --git a/src/api.rs b/src/api.rs index 499b4cd..cc972b1 100644 --- a/src/api.rs +++ b/src/api.rs @@ -1,8 +1,8 @@ //! Electrum APIs +use bp::{BlockHeader, ConsensusDecode, ConsensusEncode, ScriptPubkey, Tx, Txid}; use std::borrow::Borrow; use std::convert::TryInto; -use bpstd::{BlockHeader, ConsensusDecode, ConsensusEncode, ScriptPubkey, Tx, Txid}; use crate::batch::Batch; use crate::types::*; @@ -11,7 +11,9 @@ use crate::types::*; pub trait ElectrumApi { /// Gets the block header for height `height`. fn block_header(&self, height: usize) -> Result { - Ok(BlockHeader::consensus_deserialize(&self.block_header_raw(height)?)?) + Ok(BlockHeader::consensus_deserialize( + &self.block_header_raw(height)?, + )?) } /// Subscribes to notifications for new block headers, by sending a `blockchain.headers.subscribe` call. diff --git a/src/batch.rs b/src/batch.rs index d179500..d554a12 100644 --- a/src/batch.rs +++ b/src/batch.rs @@ -2,9 +2,8 @@ //! //! This module contains definitions and helper functions used when making batch calls. - -use bpstd::{ScriptPubkey, Txid}; use crate::types::{Call, Param, ToElectrumScriptHash}; +use bp::{ScriptPubkey, Txid}; /// Helper structure that caches all the requests before they are actually sent to the server. /// @@ -16,6 +15,7 @@ use crate::types::{Call, Param, ToElectrumScriptHash}; /// [`Client`](../client/struct.Client.html), like /// [`batch_script_get_balance`](../client/struct.Client.html#method.batch_script_get_balance) to ask the /// server for the balance of multiple scripts with a single request. +#[derive(Default)] pub struct Batch { calls: Vec, } @@ -28,28 +28,28 @@ impl Batch { /// Add one `blockchain.scripthash.listunspent` request to the batch queue pub fn script_list_unspent(&mut self, script: &ScriptPubkey) { - let params = vec![Param::String(script.to_electrum_scripthash().to_hex())]; + let params = vec![Param::String(script.to_electrum_scripthash().as_hex())]; self.calls .push((String::from("blockchain.scripthash.listunspent"), params)); } /// Add one `blockchain.scripthash.get_history` request to the batch queue pub fn script_get_history(&mut self, script: &ScriptPubkey) { - let params = vec![Param::String(script.to_electrum_scripthash().to_hex())]; + let params = vec![Param::String(script.to_electrum_scripthash().as_hex())]; self.calls .push((String::from("blockchain.scripthash.get_history"), params)); } /// Add one `blockchain.scripthash.get_balance` request to the batch queue pub fn script_get_balance(&mut self, script: &ScriptPubkey) { - let params = vec![Param::String(script.to_electrum_scripthash().to_hex())]; + let params = vec![Param::String(script.to_electrum_scripthash().as_hex())]; self.calls .push((String::from("blockchain.scripthash.get_balance"), params)); } /// Add one `blockchain.scripthash.listunspent` request to the batch queue pub fn script_subscribe(&mut self, script: &ScriptPubkey) { - let params = vec![Param::String(script.to_electrum_scripthash().to_hex())]; + let params = vec![Param::String(script.to_electrum_scripthash().as_hex())]; self.calls .push((String::from("blockchain.scripthash.subscribe"), params)); } @@ -107,9 +107,3 @@ impl<'a> Iterator for BatchIter<'a> { val } } - -impl Default for Batch { - fn default() -> Self { - Batch { calls: Vec::new() } - } -} diff --git a/src/client.rs b/src/client.rs index 7707493..f005c00 100644 --- a/src/client.rs +++ b/src/client.rs @@ -8,9 +8,9 @@ use crate::api::ElectrumApi; use crate::batch::Batch; use crate::config::Config; use crate::raw_client::*; -use std::convert::TryFrom; -use bpstd::{ScriptPubkey, Txid}; use crate::types::*; +use bp::{ScriptPubkey, Txid}; +use std::convert::TryFrom; /// Generalized Electrum client that supports multiple backends. This wraps /// [`RawClient`](client/struct.RawClient.html) and provides a more user-friendly @@ -71,7 +71,7 @@ macro_rules! impl_inner_call { std::thread::sleep(std::time::Duration::from_secs((1 << errors.len()).min(30) as u64)); match ClientType::from_config(&$self.url, &$self.config) { Ok(new_client) => { - info!("Succesfully created new client"); + info!("Successfully created new client"); *write_client = new_client; break; }, @@ -352,7 +352,7 @@ mod tests { fn more_failed_attempts_than_retries_means_exhausted() { let exhausted = retries_exhausted(10, 5); - assert_eq!(exhausted, true) + assert!(exhausted) } #[test] @@ -361,21 +361,21 @@ mod tests { let exhausted = retries_exhausted(failed_attempts, u8::MAX); - assert_eq!(exhausted, true) + assert!(exhausted) } #[test] fn less_failed_attempts_means_not_exhausted() { let exhausted = retries_exhausted(2, 5); - assert_eq!(exhausted, false) + assert!(!exhausted) } #[test] fn attempts_equals_retries_means_not_exhausted_yet() { let exhausted = retries_exhausted(2, 2); - assert_eq!(exhausted, false) + assert!(!exhausted) } #[test] @@ -407,7 +407,7 @@ mod tests { sender.send(()).unwrap(); for _stream in listener.incoming() { - loop {} + std::thread::sleep(std::time::Duration::from_secs(60)); } }); diff --git a/src/lib.rs b/src/lib.rs index a4a893d..2847f5f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -38,7 +38,7 @@ extern crate webpki_roots; extern crate byteorder; extern crate amplify; -extern crate bpstd; +extern crate bp; #[cfg(all(unix, any(feature = "default", feature = "proxy")))] extern crate libc; extern crate sha2; diff --git a/src/raw_client.rs b/src/raw_client.rs index 76bf294..6c02e86 100644 --- a/src/raw_client.rs +++ b/src/raw_client.rs @@ -3,7 +3,7 @@ //! This module contains the definition of the raw client that wraps the transport method use amplify::hex::{FromHex, ToHex}; -use bpstd::{BlockHeader, ConsensusDecode, ScriptPubkey, Txid}; +use bp::{BlockHeader, ConsensusDecode, ScriptPubkey, Txid}; use std::borrow::Borrow; use std::collections::{BTreeMap, BTreeSet, HashMap, VecDeque}; use std::io::{BufRead, BufReader, Read, Write}; @@ -24,10 +24,7 @@ use openssl::ssl::{SslConnector, SslMethod, SslStream, SslVerifyMode}; any(feature = "default", feature = "use-rustls"), not(feature = "use-openssl") ))] -use rustls::{ - pki_types::ServerName, - ClientConfig, ClientConnection, RootCertStore, StreamOwned, -}; +use rustls::{pki_types::ServerName, ClientConfig, ClientConnection, RootCertStore, StreamOwned}; #[cfg(any(feature = "default", feature = "proxy"))] use crate::socks::{Socks5Stream, TargetAddr, ToTargetAddr}; @@ -76,7 +73,7 @@ pub trait ToSocketAddrsDomain: ToSocketAddrs { impl ToSocketAddrsDomain for &str { fn domain(&self) -> Option<&str> { - self.splitn(2, ':').next() + self.split(':').next() } } @@ -112,7 +109,7 @@ impl_to_socket_addrs_domain!((std::net::Ipv6Addr, u16)); /// Instance of an Electrum client /// /// A `Client` maintains a constant connection with an Electrum server and exposes methods to -/// interact with it. It can also subscribe and receive notifictations from the server about new +/// interact with it. It can also subscribe and receive notifications from the server about new /// blocks or activity on a specific *scriptPubKey*. /// /// The `Client` is modeled in such a way that allows the external caller to have full control over @@ -120,7 +117,7 @@ impl_to_socket_addrs_domain!((std::net::Ipv6Addr, u16)); /// connection. /// /// More transport methods can be used by manually creating an instance of this struct with an -/// arbitray `S` type. +/// arbitrary `S` type. #[derive(Debug)] pub struct RawClient where @@ -551,9 +548,8 @@ impl RawClient { if let Some(err) = map.values().find_map(|sender| { sender .send(ChannelMessage::WakeUp) - .map_err(|err| { + .inspect_err(|_| { warn!("Unable to wake up a thread, trying some other"); - err }) .err() }) { @@ -578,7 +574,7 @@ impl RawClient { // No id, that's probably a notification. let mut resp = resp; - if let Some(ref method) = resp["method"].take().as_str() { + if let Some(method) = resp["method"].take().as_str() { self.handle_notification(method, resp["params"].take())?; } else { warn!("Unexpected response: {:?}", resp); @@ -639,7 +635,7 @@ impl RawClient { ) -> Result { loop { // Try to take the lock on the reader. If we manage to do so, we'll become the reader - // thread until we get our reponse + // thread until we get our response match self._reader_thread(Some(req_id)) { Ok(response) => break Ok(response), Err(Error::CouldntLockReader) => { @@ -695,7 +691,7 @@ impl RawClient { ) -> Result { let req = Request::new_id( self.last_id.fetch_add(1, Ordering::SeqCst), - &method_name, + method_name, params, ); let result = self.call(req)?; @@ -736,7 +732,7 @@ impl ElectrumApi for RawClient { for (method, params) in batch.iter() { let req = Request::new_id( self.last_id.fetch_add(1, Ordering::SeqCst), - &method, + method, params.to_vec(), ); missing_responses.insert(req.id); @@ -777,7 +773,7 @@ impl ElectrumApi for RawClient { }; } - Ok(answers.into_iter().map(|(_, r)| r).collect()) + Ok(answers.into_values().collect()) } fn block_headers_subscribe_raw(&self) -> Result { @@ -872,7 +868,7 @@ impl ElectrumApi for RawClient { let req = Request::new_id( self.last_id.fetch_add(1, Ordering::SeqCst), "blockchain.scripthash.subscribe", - vec![Param::String(script_hash.to_hex())], + vec![Param::String(script_hash.as_hex())], ); let value = self.call(req)?; @@ -909,7 +905,7 @@ impl ElectrumApi for RawClient { let req = Request::new_id( self.last_id.fetch_add(1, Ordering::SeqCst), "blockchain.scripthash.unsubscribe", - vec![Param::String(script_hash.to_hex())], + vec![Param::String(script_hash.as_hex())], ); let value = self.call(req)?; let answer = serde_json::from_value(value)?; @@ -929,7 +925,7 @@ impl ElectrumApi for RawClient { } fn script_get_balance(&self, script: &ScriptPubkey) -> Result { - let params = vec![Param::String(script.to_electrum_scripthash().to_hex())]; + let params = vec![Param::String(script.to_electrum_scripthash().as_hex())]; let req = Request::new_id( self.last_id.fetch_add(1, Ordering::SeqCst), "blockchain.scripthash.get_balance", @@ -948,7 +944,7 @@ impl ElectrumApi for RawClient { } fn script_get_history(&self, script: &ScriptPubkey) -> Result, Error> { - let params = vec![Param::String(script.to_electrum_scripthash().to_hex())]; + let params = vec![Param::String(script.to_electrum_scripthash().as_hex())]; let req = Request::new_id( self.last_id.fetch_add(1, Ordering::SeqCst), "blockchain.scripthash.get_history", @@ -967,7 +963,7 @@ impl ElectrumApi for RawClient { } fn script_list_unspent(&self, script: &ScriptPubkey) -> Result, Error> { - let params = vec![Param::String(script.to_electrum_scripthash().to_hex())]; + let params = vec![Param::String(script.to_electrum_scripthash().as_hex())]; let req = Request::new_id( self.last_id.fetch_add(1, Ordering::SeqCst), "blockchain.scripthash.listunspent", @@ -1098,7 +1094,8 @@ impl ElectrumApi for RawClient { #[cfg(test)] mod test { - use bpstd::{Address, TxVer}; + use bp::TxVer; + use invoice::Address; use std::str::FromStr; use super::*; diff --git a/src/socks/mod.rs b/src/socks/mod.rs index 5aa29c0..2767b9d 100644 --- a/src/socks/mod.rs +++ b/src/socks/mod.rs @@ -99,7 +99,7 @@ impl ToTargetAddr for (Ipv6Addr, u16) { } } -impl<'a> ToTargetAddr for (&'a str, u16) { +impl ToTargetAddr for (&str, u16) { fn to_target_addr(&self) -> io::Result { // try to parse as an IP first if let Ok(addr) = self.0.parse::() { @@ -114,7 +114,7 @@ impl<'a> ToTargetAddr for (&'a str, u16) { } } -impl<'a> ToTargetAddr for &'a str { +impl ToTargetAddr for &str { fn to_target_addr(&self) -> io::Result { // try to parse as an IP first if let Ok(addr) = self.parse::() { diff --git a/src/socks/v4.rs b/src/socks/v4.rs index b9658b0..9d0fc3f 100644 --- a/src/socks/v4.rs +++ b/src/socks/v4.rs @@ -109,7 +109,7 @@ impl Socks4Stream { let _ = packet.write_u32::(Ipv4Addr::new(0, 0, 0, 1).into()); let _ = packet.write_all(userid.as_bytes()); let _ = packet.write_u8(0); - let _ = packet.extend(host.as_bytes()); + packet.extend(host.as_bytes()); let _ = packet.write_u8(0); } } @@ -117,10 +117,7 @@ impl Socks4Stream { socket.write_all(&packet)?; let proxy_addr = read_response(&mut socket)?; - Ok(Socks4Stream { - socket: socket, - proxy_addr: proxy_addr, - }) + Ok(Socks4Stream { socket, proxy_addr }) } /// Returns the proxy-side address of the connection between the proxy and @@ -151,7 +148,7 @@ impl Read for Socks4Stream { } } -impl<'a> Read for &'a Socks4Stream { +impl Read for &Socks4Stream { fn read(&mut self, buf: &mut [u8]) -> io::Result { (&self.socket).read(buf) } @@ -167,7 +164,7 @@ impl Write for Socks4Stream { } } -impl<'a> Write for &'a Socks4Stream { +impl Write for &Socks4Stream { fn write(&mut self, buf: &[u8]) -> io::Result { (&self.socket).write(buf) } diff --git a/src/socks/v5.rs b/src/socks/v5.rs index 5c4b2de..7a602d2 100644 --- a/src/socks/v5.rs +++ b/src/socks/v5.rs @@ -110,7 +110,7 @@ fn write_addr(mut packet: &mut [u8], target: &TargetAddr) -> io::Result { } TargetAddr::Domain(ref domain, port) => { packet.write_u8(3).unwrap(); - if domain.len() > u8::max_value() as usize { + if domain.len() > u8::MAX as usize { return Err(io::Error::new( io::ErrorKind::InvalidInput, "domain name too long", @@ -135,7 +135,7 @@ enum Authentication<'a> { None, } -impl<'a> Authentication<'a> { +impl Authentication<'_> { fn id(&self) -> u8 { match *self { Authentication::Password { .. } => 2, @@ -144,11 +144,7 @@ impl<'a> Authentication<'a> { } fn is_no_auth(&self) -> bool { - if let Authentication::None = *self { - true - } else { - false - } + matches!(*self, Authentication::None) } } @@ -257,10 +253,7 @@ impl Socks5Stream { let proxy_addr = read_response(&mut socket)?; - Ok(Socks5Stream { - socket: socket, - proxy_addr: proxy_addr, - }) + Ok(Socks5Stream { socket, proxy_addr }) } fn password_authentication( @@ -268,13 +261,13 @@ impl Socks5Stream { username: &str, password: &str, ) -> io::Result<()> { - if username.len() < 1 || username.len() > 255 { + if username.is_empty() || username.len() > 255 { return Err(io::Error::new( io::ErrorKind::InvalidInput, "invalid username", )); }; - if password.len() < 1 || password.len() > 255 { + if password.is_empty() || password.len() > 255 { return Err(io::Error::new( io::ErrorKind::InvalidInput, "invalid password", @@ -336,7 +329,7 @@ impl Read for Socks5Stream { } } -impl<'a> Read for &'a Socks5Stream { +impl Read for &Socks5Stream { fn read(&mut self, buf: &mut [u8]) -> io::Result { (&self.socket).read(buf) } @@ -352,7 +345,7 @@ impl Write for Socks5Stream { } } -impl<'a> Write for &'a Socks5Stream { +impl Write for &Socks5Stream { fn write(&mut self, buf: &[u8]) -> io::Result { (&self.socket).write(buf) } @@ -474,10 +467,7 @@ impl Socks5Datagram { let socket = UdpSocket::bind(addr)?; socket.connect(&stream.proxy_addr)?; - Ok(Socks5Datagram { - socket: socket, - stream: stream, - }) + Ok(Socks5Datagram { socket, stream }) } /// Like `UdpSocket::send_to`. @@ -526,11 +516,7 @@ impl Socks5Datagram { let addr = read_addr(&mut header)?; unsafe { - ptr::copy( - buf.as_ptr(), - buf.as_mut_ptr().offset(header.len() as isize), - overflow, - ); + ptr::copy(buf.as_ptr(), buf.as_mut_ptr().add(header.len()), overflow); } buf[..header.len()].copy_from_slice(header); diff --git a/src/types.rs b/src/types.rs index f388860..9301984 100644 --- a/src/types.rs +++ b/src/types.rs @@ -2,13 +2,13 @@ //! //! This module contains definitions of all the complex data structures that are returned by calls +use amplify::hex; +use amplify::hex::{FromHex, ToHex}; +use bp::{BlockHeader, ConsensusDecode, ConsensusDecodeError, ScriptPubkey, Txid}; use std::convert::TryFrom; use std::fmt::{self, Display, Formatter}; use std::ops::Deref; use std::sync::Arc; -use amplify::hex; -use amplify::hex::{FromHex, ToHex}; -use bpstd::{BlockHeader, ConsensusDecode, ConsensusDecodeError, ScriptPubkey, Txid}; use serde::{de, Deserialize, Serialize}; use sha2::Digest; @@ -85,7 +85,7 @@ impl From<[u8; 32]> for Hex32Bytes { } impl Hex32Bytes { - pub(crate) fn to_hex(&self) -> String { + pub(crate) fn as_hex(&self) -> String { self.0.to_hex() } } diff --git a/src/utils.rs b/src/utils.rs index 7c3db66..453c022 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -1,9 +1,9 @@ //! Utilities helping to handle Electrum-related data. +use crate::types::GetMerkleRes; use amplify::ByteArray; -use bpstd::{BlockMerkleRoot, Txid}; +use bp::{BlockMerkleRoot, Txid}; use sha2::{Digest, Sha256}; -use crate::types::GetMerkleRes; /// Verifies a Merkle inclusion proof as retrieved via [`transaction_get_merkle`] for a transaction with the /// given `txid` and `merkle_root` as included in the [`BlockHeader`].