From a285d3459219832ea6a0f9e872ea69f125fd3ce5 Mon Sep 17 00:00:00 2001 From: Rigidity Date: Sun, 14 Jul 2024 13:07:17 -0400 Subject: [PATCH 01/26] Initial rewrite --- Cargo.lock | 172 +++++++++++ Cargo.toml | 2 + crates/chia-client/Cargo.toml | 5 +- crates/chia-client/src/error.rs | 35 ++- crates/chia-client/src/event.rs | 8 + crates/chia-client/src/lib.rs | 6 +- crates/chia-client/src/peer.rs | 425 ++++++++------------------ crates/chia-client/src/request_map.rs | 64 ++++ crates/chia-client/src/response.rs | 5 + crates/chia-client/src/utils.rs | 7 - 10 files changed, 410 insertions(+), 319 deletions(-) create mode 100644 crates/chia-client/src/event.rs create mode 100644 crates/chia-client/src/request_map.rs create mode 100644 crates/chia-client/src/response.rs delete mode 100644 crates/chia-client/src/utils.rs diff --git a/Cargo.lock b/Cargo.lock index a17342a27..d8e6a8c46 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -338,6 +338,9 @@ dependencies = [ "chia-protocol", "chia-traits 0.10.0", "futures-util", + "log", + "native-tls", + "sha2", "thiserror", "tokio", "tokio-tungstenite", @@ -699,6 +702,22 @@ version = "0.9.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" +[[package]] +name = "core-foundation" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" + [[package]] name = "cpufeatures" version = "0.2.12" @@ -926,6 +945,16 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" +[[package]] +name = "errno" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + [[package]] name = "fallible-iterator" version = "0.3.0" @@ -938,6 +967,12 @@ version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a" +[[package]] +name = "fastrand" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fc0510504f03c51ada170672ac806f1f105a88aa97a5281117e1ddc3368e51a" + [[package]] name = "ff" version = "0.13.0" @@ -1370,6 +1405,12 @@ dependencies = [ "vcpkg", ] +[[package]] +name = "linux-raw-sys" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" + [[package]] name = "lock_api" version = "0.4.12" @@ -1437,6 +1478,23 @@ dependencies = [ "windows-sys", ] +[[package]] +name = "native-tls" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8614eb2c83d59d1c8cc974dd3f920198647674a0a035e1af1fa58707e317466" +dependencies = [ + "libc", + "log", + "openssl", + "openssl-probe", + "openssl-sys", + "schannel", + "security-framework", + "security-framework-sys", + "tempfile", +] + [[package]] name = "nom" version = "7.1.3" @@ -1598,6 +1656,50 @@ dependencies = [ "vcpkg", ] +[[package]] +name = "openssl" +version = "0.10.64" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95a0481286a310808298130d22dd1fef0fa571e05a8f44ec801801e84b216b1f" +dependencies = [ + "bitflags 2.5.0", + "cfg-if", + "foreign-types", + "libc", + "once_cell", + "openssl-macros", + "openssl-sys", +] + +[[package]] +name = "openssl-macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.70", +] + +[[package]] +name = "openssl-probe" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" + +[[package]] +name = "openssl-sys" +version = "0.9.102" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c597637d56fbc83893a35eb0dd04b2b8e7a50c91e64e9493e398b5df4fb45fa2" +dependencies = [ + "cc", + "libc", + "pkg-config", + "vcpkg", +] + [[package]] name = "p256" version = "0.13.2" @@ -2060,6 +2162,19 @@ dependencies = [ "nom", ] +[[package]] +name = "rustix" +version = "0.38.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f" +dependencies = [ + "bitflags 2.5.0", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.52.0", +] + [[package]] name = "rustls-pki-types" version = "1.7.0" @@ -2081,6 +2196,15 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "schannel" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fbc91545643bcf3a0bbb6569265615222618bdf33ce4ffbbd13c4bbd4c093534" +dependencies = [ + "windows-sys 0.52.0", +] + [[package]] name = "scopeguard" version = "1.2.0" @@ -2101,6 +2225,29 @@ dependencies = [ "zeroize", ] +[[package]] +name = "security-framework" +version = "2.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c627723fd09706bacdb5cf41499e95098555af3c3c29d014dc3c458ef6be11c0" +dependencies = [ + "bitflags 2.5.0", + "core-foundation", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "317936bbbd05227752583946b9e66d7ce3b489f84e11a94a510b4437fef407d7" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "semver" version = "1.0.23" @@ -2263,6 +2410,18 @@ version = "0.12.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4873307b7c257eddcb50c9bedf158eb669578359fb28428bef438fec8e6ba7c2" +[[package]] +name = "tempfile" +version = "3.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85b77fafb263dd9d05cbeac119526425676db3784113aa9295c88498cbf8bff1" +dependencies = [ + "cfg-if", + "fastrand", + "rustix", + "windows-sys 0.52.0", +] + [[package]] name = "term" version = "0.2.14" @@ -2383,6 +2542,16 @@ dependencies = [ "windows-sys", ] +[[package]] +name = "tokio-native-tls" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" +dependencies = [ + "native-tls", + "tokio", +] + [[package]] name = "tokio-tungstenite" version = "0.21.0" @@ -2391,7 +2560,9 @@ checksum = "c83b561d025642014097b66e6c1bb422783339e0909e4429cde4749d1990bc38" dependencies = [ "futures-util", "log", + "native-tls", "tokio", + "tokio-native-tls", "tungstenite", ] @@ -2424,6 +2595,7 @@ dependencies = [ "http", "httparse", "log", + "native-tls", "rand", "sha1", "thiserror", diff --git a/Cargo.toml b/Cargo.toml index 0256f0729..bf1508bc5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -138,3 +138,5 @@ zstd = "0.13.2" blocking-threadpool = "1.0.1" libfuzzer-sys = "0.4" wasm-bindgen = "0.2.92" +log = "0.4.22" +native-tls = "0.2.12" diff --git a/crates/chia-client/Cargo.toml b/crates/chia-client/Cargo.toml index 880be8777..89f1cbd74 100644 --- a/crates/chia-client/Cargo.toml +++ b/crates/chia-client/Cargo.toml @@ -15,7 +15,10 @@ workspace = true chia-protocol = { workspace = true } chia-traits = { workspace = true } tokio = { workspace = true, features = ["rt", "sync"] } -tokio-tungstenite = { workspace = true } +tokio-tungstenite = { workspace = true, features = ["native-tls"] } futures-util = { workspace = true } tungstenite = { workspace = true } thiserror = { workspace = true } +sha2 = { workspace = true } +log = { workspace = true } +native-tls = { workspace = true } diff --git a/crates/chia-client/src/error.rs b/crates/chia-client/src/error.rs index 108290e2c..fcb99bcd9 100644 --- a/crates/chia-client/src/error.rs +++ b/crates/chia-client/src/error.rs @@ -1,21 +1,32 @@ -use chia_protocol::Message; -use chia_traits::chia_error; +use chia_protocol::ProtocolMessageTypes; use thiserror::Error; +use tokio::sync::oneshot::error::RecvError; #[derive(Debug, Error)] -pub enum Error { - #[error("{0:?}")] - Chia(#[from] chia_error::Error), +pub enum Error { + #[error("Peer is missing certificate")] + MissingCertificate, - #[error("{0}")] + #[error("Streamable error: {0}")] + Streamable(#[from] chia_traits::Error), + + #[error("WebSocket error: {0}")] WebSocket(#[from] tungstenite::Error), - #[error("{0:?}")] - InvalidResponse(Message), + #[error("TLS error: {0}")] + Tls(#[from] native_tls::Error), + + #[error("Unexpected message received with type {0:?}")] + UnexpectedMessage(ProtocolMessageTypes), - #[error("missing response")] - MissingResponse, + #[error("Expected response with type {0:?}, found {1:?}")] + InvalidResponse(Vec, ProtocolMessageTypes), - #[error("rejection")] - Rejection(R), + #[error("Failed to send event")] + EventNotSent, + + #[error("Failed to receive message")] + Recv(#[from] RecvError), } + +pub type Result = std::result::Result; diff --git a/crates/chia-client/src/event.rs b/crates/chia-client/src/event.rs new file mode 100644 index 000000000..ec08b4e2c --- /dev/null +++ b/crates/chia-client/src/event.rs @@ -0,0 +1,8 @@ +use chia_protocol::{CoinStateUpdate, Handshake, NewPeakWallet}; + +#[derive(Debug, Clone)] +pub enum Event { + Handshake(Handshake), + NewPeakWallet(NewPeakWallet), + CoinStateUpdate(CoinStateUpdate), +} diff --git a/crates/chia-client/src/lib.rs b/crates/chia-client/src/lib.rs index 1cfd95c93..71d9b32c6 100644 --- a/crates/chia-client/src/lib.rs +++ b/crates/chia-client/src/lib.rs @@ -1,6 +1,10 @@ mod error; +mod event; mod peer; -mod utils; +mod request_map; +mod response; pub use error::*; +pub use event::*; pub use peer::*; +pub use response::*; diff --git a/crates/chia-client/src/peer.rs b/crates/chia-client/src/peer.rs index a7db5cc62..16eb1355e 100644 --- a/crates/chia-client/src/peer.rs +++ b/crates/chia-client/src/peer.rs @@ -1,363 +1,192 @@ -use std::sync::atomic::{AtomicU16, Ordering}; -use std::{collections::HashMap, sync::Arc}; +use std::sync::Arc; -use chia_protocol::*; +use chia_protocol::{ + ChiaProtocolMessage, CoinStateUpdate, Handshake, Message, NewPeakWallet, ProtocolMessageTypes, +}; use chia_traits::Streamable; -use futures_util::stream::SplitSink; -use futures_util::{SinkExt, StreamExt}; -use tokio::sync::{broadcast, oneshot, Mutex}; -use tokio::{net::TcpStream, task::JoinHandle}; +use futures_util::{ + stream::{SplitSink, SplitStream}, + SinkExt, StreamExt, +}; +use sha2::{digest::FixedOutput, Digest, Sha256}; +use tokio::{ + net::TcpStream, + sync::{mpsc, oneshot, Mutex}, + task::JoinHandle, +}; use tokio_tungstenite::{MaybeTlsStream, WebSocketStream}; -use tungstenite::Message as WsMessage; -use crate::utils::stream; -use crate::Error; +use crate::{request_map::RequestMap, Error, Event, Response, Result}; type WebSocket = WebSocketStream>; -type Requests = Arc>>>; +type Sink = SplitSink; +type Stream = SplitStream; -#[derive(Debug, Clone, PartialEq, Eq)] -pub enum PeerEvent { - CoinStateUpdate(CoinStateUpdate), - NewPeakWallet(NewPeakWallet), -} +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct PeerId([u8; 32]); -pub struct Peer { - sink: Mutex>, - inbound_task: JoinHandle<()>, - event_receiver: broadcast::Receiver, - requests: Requests, +#[derive(Debug, Clone)] +pub struct Peer(Arc); - // TODO: This does not currently prevent multiple requests with the same id at the same time. - // If one of them is still running while all other ids are being iterated through. - nonce: AtomicU16, +#[derive(Debug)] +struct PeerInner { + sink: Mutex, + inbound_handle: JoinHandle>, + requests: Arc, + peer_id: PeerId, } impl Peer { - pub fn new(ws: WebSocket) -> Self { - let (sink, mut stream) = ws.split(); - let (event_sender, event_receiver) = broadcast::channel(32); - - let requests = Requests::default(); - let requests_clone = Arc::clone(&requests); - - let inbound_task = tokio::spawn(async move { - while let Some(message) = stream.next().await { - if let Ok(message) = message { - Self::handle_inbound(message, &requests_clone, &event_sender) - .await - .ok(); - } - } - }); - - Self { - sink: Mutex::new(sink), - inbound_task, - event_receiver, - requests, - nonce: AtomicU16::new(0), - } - } - - pub async fn send_handshake( - &self, - network_id: String, - node_type: NodeType, - ) -> Result<(), Error<()>> { - let body = Handshake { - network_id, - protocol_version: "0.0.34".to_string(), - software_version: "0.0.0".to_string(), - server_port: 0, - node_type, - capabilities: vec![ - (1, "1".to_string()), - (2, "1".to_string()), - (3, "1".to_string()), - ], - }; - self.send(body).await - } - - pub async fn request_puzzle_and_solution( - &self, - coin_id: Bytes32, - height: u32, - ) -> Result> { - let body = RequestPuzzleSolution { - coin_name: coin_id, - height, - }; - let response: RespondPuzzleSolution = self.request_or_reject(body).await?; - Ok(response.response) - } - - pub async fn send_transaction( - &self, - spend_bundle: SpendBundle, - ) -> Result> { - let body = SendTransaction { - transaction: spend_bundle, - }; - self.request(body).await - } - - pub async fn request_block_header( - &self, - height: u32, - ) -> Result> { - let body = RequestBlockHeader { height }; - let response: RespondBlockHeader = self.request_or_reject(body).await?; - Ok(response.header_block) - } - - pub async fn request_block_headers( - &self, - start_height: u32, - end_height: u32, - return_filter: bool, - ) -> Result, Error<()>> { - let body = RequestBlockHeaders { - start_height, - end_height, - return_filter, + pub fn new(ws: WebSocket) -> Result<(Self, mpsc::Receiver)> { + let cert = match ws.get_ref() { + MaybeTlsStream::NativeTls(tls) => tls.get_ref().peer_certificate()?, + _ => None, }; - let response: RespondBlockHeaders = - self.request_or_reject(body) - .await - .map_err(|error: Error| match error { - Error::Rejection(_rejection) => Error::Rejection(()), - Error::Chia(error) => Error::Chia(error), - Error::WebSocket(error) => Error::WebSocket(error), - Error::InvalidResponse(error) => Error::InvalidResponse(error), - Error::MissingResponse => Error::MissingResponse, - })?; - Ok(response.header_blocks) - } - pub async fn request_removals( - &self, - height: u32, - header_hash: Bytes32, - coin_ids: Option>, - ) -> Result> { - let body = RequestRemovals { - height, - header_hash, - coin_names: coin_ids, + let Some(cert) = cert else { + return Err(Error::MissingCertificate); }; - self.request_or_reject(body).await - } - pub async fn request_additions( - &self, - height: u32, - header_hash: Option, - puzzle_hashes: Option>, - ) -> Result> { - let body = RequestAdditions { - height, - header_hash, - puzzle_hashes, - }; - self.request_or_reject(body).await - } + let mut hasher = Sha256::new(); + hasher.update(cert.to_der()?); - pub async fn register_for_ph_updates( - &self, - puzzle_hashes: Vec, - min_height: u32, - ) -> Result, Error<()>> { - let body = RegisterForPhUpdates { - puzzle_hashes, - min_height, - }; - let response: RespondToPhUpdates = self.request(body).await?; - Ok(response.coin_states) - } + let peer_id = PeerId(hasher.finalize_fixed().into()); + let (sink, stream) = ws.split(); + let (sender, receiver) = mpsc::channel(32); + let requests = Arc::new(RequestMap::new()); - pub async fn register_for_coin_updates( - &self, - coin_ids: Vec, - min_height: u32, - ) -> Result, Error<()>> { - let body = RegisterForCoinUpdates { - coin_ids, - min_height, - }; - let response: RespondToCoinUpdates = self.request(body).await?; - Ok(response.coin_states) - } + let inbound_handle = + tokio::spawn(handle_inbound_messages(stream, sender, requests.clone())); - pub async fn request_children(&self, coin_id: Bytes32) -> Result, Error<()>> { - let body = RequestChildren { coin_name: coin_id }; - let response: RespondChildren = self.request(body).await?; - Ok(response.coin_states) - } + let peer = Self(Arc::new(PeerInner { + sink: Mutex::new(sink), + inbound_handle, + requests, + peer_id, + })); - pub async fn request_ses_info( - &self, - start_height: u32, - end_height: u32, - ) -> Result> { - let body = RequestSesInfo { - start_height, - end_height, - }; - self.request(body).await + Ok((peer, receiver)) } - pub async fn request_fee_estimates( - &self, - time_targets: Vec, - ) -> Result> { - let body = RequestFeeEstimates { time_targets }; - let response: RespondFeeEstimates = self.request(body).await?; - Ok(response.estimates) + pub fn peer_id(&self) -> PeerId { + self.0.peer_id } - pub async fn send(&self, body: T) -> Result<(), Error<()>> + pub async fn send(&self, body: T) -> Result<()> where T: Streamable + ChiaProtocolMessage, { - // Create the message. - let message = Message { - msg_type: T::msg_type(), - id: None, - data: stream(&body)?.into(), - }; + let message = Message::new(T::msg_type(), None, body.to_bytes()?.into()) + .to_bytes()? + .into(); - // Send the message through the websocket. - let mut sink = self.sink.lock().await; - sink.send(stream(&message)?.into()).await?; + self.0.sink.lock().await.send(message).await?; Ok(()) } - pub async fn request_or_reject(&self, body: B) -> Result> + pub async fn request_fallible(&self, body: B) -> Result> where T: Streamable + ChiaProtocolMessage, - R: Streamable + ChiaProtocolMessage, + E: Streamable + ChiaProtocolMessage, B: Streamable + ChiaProtocolMessage, { let message = self.request_raw(body).await?; - let data = message.data.as_ref(); - + if message.msg_type != T::msg_type() && message.msg_type != E::msg_type() { + return Err(Error::InvalidResponse( + vec![T::msg_type(), E::msg_type()], + message.msg_type, + )); + } if message.msg_type == T::msg_type() { - T::from_bytes(data).or(Err(Error::InvalidResponse(message))) - } else if message.msg_type == R::msg_type() { - let rejection = R::from_bytes(data).or(Err(Error::InvalidResponse(message)))?; - Err(Error::Rejection(rejection)) + Ok(Response::Success(T::from_bytes(&message.data)?)) } else { - Err(Error::InvalidResponse(message)) + Ok(Response::Rejection(E::from_bytes(&message.data)?)) } } - pub async fn request(&self, body: T) -> Result> + pub async fn request_infallible(&self, body: B) -> Result where - Response: Streamable + ChiaProtocolMessage, T: Streamable + ChiaProtocolMessage, + B: Streamable + ChiaProtocolMessage, { let message = self.request_raw(body).await?; - let data = message.data.as_ref(); - - if message.msg_type == Response::msg_type() { - Response::from_bytes(data).or(Err(Error::InvalidResponse(message))) - } else { - Err(Error::InvalidResponse(message)) + if message.msg_type != T::msg_type() { + return Err(Error::InvalidResponse( + vec![T::msg_type()], + message.msg_type, + )); } + Ok(T::from_bytes(&message.data)?) } - pub async fn request_raw(&self, body: T) -> Result> + pub async fn request_raw(&self, body: T) -> Result where T: Streamable + ChiaProtocolMessage, { - // Get the current nonce and increment. - let message_id = self.nonce.fetch_add(1, Ordering::SeqCst); + let (sender, receiver) = oneshot::channel(); - // Create the message. let message = Message { msg_type: T::msg_type(), - id: Some(message_id), - data: stream(&body)?.into(), - }; - - // Create a saved oneshot channel to receive the response. - let (sender, receiver) = oneshot::channel::(); - self.requests.lock().await.insert(message_id, sender); - - // Send the message. - let bytes = match stream(&message) { - Ok(bytes) => bytes.into(), - Err(error) => { - self.requests.lock().await.remove(&message_id); - return Err(error.into()); - } - }; - let send_result = self.sink.lock().await.send(bytes).await; - - if let Err(error) = send_result { - self.requests.lock().await.remove(&message_id); - return Err(error.into()); + id: Some(self.0.requests.insert(sender).await), + data: body.to_bytes()?.into(), } + .to_bytes()? + .into(); - // Wait for the response. - let response = receiver.await; - - // Remove the one shot channel. - self.requests.lock().await.remove(&message_id); - - // Handle the response, if present. - response.or(Err(Error::MissingResponse)) - } - - pub fn receiver(&self) -> &broadcast::Receiver { - &self.event_receiver + self.0.sink.lock().await.send(message).await?; + Ok(receiver.await?) } +} - pub fn receiver_mut(&mut self) -> &mut broadcast::Receiver { - &mut self.event_receiver +impl Drop for PeerInner { + fn drop(&mut self) { + self.inbound_handle.abort(); } +} - async fn handle_inbound( - message: WsMessage, - requests: &Requests, - event_sender: &broadcast::Sender, - ) -> Result<(), Error<()>> { - // Parse the message. - let message = Message::from_bytes(message.into_data().as_ref())?; - - if let Some(id) = message.id { - // Send response through oneshot channel if present. - if let Some(request) = requests.lock().await.remove(&id) { - request.send(message).ok(); +async fn handle_inbound_messages( + mut stream: Stream, + sender: mpsc::Sender, + requests: Arc, +) -> Result<()> { + while let Some(message) = stream.next().await { + let message = Message::from_bytes(&message?.into_data())?; + + match message.msg_type { + ProtocolMessageTypes::CoinStateUpdate => { + let event = Event::CoinStateUpdate(CoinStateUpdate::from_bytes(&message.data)?); + sender.send(event).await.map_err(|error| { + log::error!("Failed to send `CoinStateUpdate` event: {error}"); + Error::EventNotSent + })?; + } + ProtocolMessageTypes::NewPeakWallet => { + let event = Event::NewPeakWallet(NewPeakWallet::from_bytes(&message.data)?); + sender.send(event).await.map_err(|error| { + log::error!("Failed to send `NewPeakWallet` event: {error}"); + Error::EventNotSent + })?; + } + ProtocolMessageTypes::Handshake => { + let event = Event::Handshake(Handshake::from_bytes(&message.data)?); + sender.send(event).await.map_err(|error| { + log::error!("Failed to send `Handshake` event: {error}"); + Error::EventNotSent + })?; + } + kind => { + let Some(id) = message.id else { + log::error!("Received unknown message without an id."); + return Err(Error::UnexpectedMessage(kind)); + }; + let Some(request) = requests.remove(id).await else { + log::error!("Received message with untracked id {id}."); + return Err(Error::UnexpectedMessage(kind)); + }; + request.send(message); } - return Ok(()); - } - - macro_rules! events { - ( $( $event:ident ),+ $(,)? ) => { - match message.msg_type { - $( ProtocolMessageTypes::$event => { - event_sender - .send(PeerEvent::$event($event::from_bytes(message.data.as_ref())?)) - .ok(); - } )+ - _ => {} - } - }; } - - // TODO: Handle unexpected messages. - events!(CoinStateUpdate, NewPeakWallet); - - Ok(()) - } -} - -impl Drop for Peer { - fn drop(&mut self) { - self.inbound_task.abort(); } + Ok(()) } diff --git a/crates/chia-client/src/request_map.rs b/crates/chia-client/src/request_map.rs new file mode 100644 index 000000000..a1c3011f5 --- /dev/null +++ b/crates/chia-client/src/request_map.rs @@ -0,0 +1,64 @@ +use std::{collections::HashMap, sync::Arc}; + +use chia_protocol::Message; +use tokio::sync::{oneshot, Mutex, OwnedSemaphorePermit, Semaphore}; + +#[derive(Debug)] +pub struct Request { + sender: oneshot::Sender, + _permit: OwnedSemaphorePermit, +} + +impl Request { + pub fn send(self, message: Message) { + self.sender.send(message).ok(); + } +} + +#[derive(Debug)] +pub struct RequestMap { + items: Mutex>, + semaphore: Arc, +} + +impl RequestMap { + pub fn new() -> Self { + Self { + items: Mutex::new(HashMap::new()), + semaphore: Arc::new(Semaphore::new(u16::MAX as usize)), + } + } + + pub async fn insert(&self, sender: oneshot::Sender) -> u16 { + let permit = self + .semaphore + .clone() + .acquire_owned() + .await + .expect("semaphore closed"); + + let mut items = self.items.lock().await; + + let mut index = None; + + for i in 0..=u16::MAX { + if !items.contains_key(&i) { + index = Some(i); + } + } + + let index = index.expect("exceeded expected number of requests"); + items.insert( + index, + Request { + sender, + _permit: permit, + }, + ); + index + } + + pub async fn remove(&self, id: u16) -> Option { + self.items.lock().await.remove(&id) + } +} diff --git a/crates/chia-client/src/response.rs b/crates/chia-client/src/response.rs new file mode 100644 index 000000000..f09f3add0 --- /dev/null +++ b/crates/chia-client/src/response.rs @@ -0,0 +1,5 @@ +#[derive(Debug, Clone, Copy)] +pub enum Response { + Success(T), + Rejection(E), +} diff --git a/crates/chia-client/src/utils.rs b/crates/chia-client/src/utils.rs deleted file mode 100644 index cddc7d40a..000000000 --- a/crates/chia-client/src/utils.rs +++ /dev/null @@ -1,7 +0,0 @@ -use chia_traits::{chia_error::Result, Streamable}; - -pub fn stream(value: &T) -> Result> { - let mut bytes = Vec::new(); - value.stream(&mut bytes)?; - Ok(bytes) -} From 5c6ff14711180cffa3a972d8ed21cb20f01ca725 Mon Sep 17 00:00:00 2001 From: Rigidity Date: Sun, 14 Jul 2024 19:22:52 -0400 Subject: [PATCH 02/26] Peer discovery --- Cargo.lock | 309 +++++++++++----- Cargo.toml | 5 +- crates/chia-client/Cargo.toml | 11 + crates/chia-client/examples/client.rs | 54 +++ crates/chia-client/examples/peer_discovery.rs | 68 ++++ crates/chia-client/src/client.rs | 346 ++++++++++++++++++ crates/chia-client/src/error.rs | 18 +- crates/chia-client/src/event.rs | 9 +- crates/chia-client/src/lib.rs | 6 + crates/chia-client/src/network.rs | 39 ++ crates/chia-client/src/peer.rs | 115 +++--- crates/chia-client/src/tls.rs | 11 + 12 files changed, 840 insertions(+), 151 deletions(-) create mode 100644 crates/chia-client/examples/client.rs create mode 100644 crates/chia-client/examples/peer_discovery.rs create mode 100644 crates/chia-client/src/client.rs create mode 100644 crates/chia-client/src/network.rs create mode 100644 crates/chia-client/src/tls.rs diff --git a/Cargo.lock b/Cargo.lock index d8e6a8c46..841ee5d55 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -86,7 +86,7 @@ version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6d36fc52c7f6c869915e99412912f22093507da8d9e942ceaf66fe4b7c14422a" dependencies = [ - "windows-sys", + "windows-sys 0.52.0", ] [[package]] @@ -96,7 +96,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5bf74e1b6e971609db8ca7a9ce79fd5768ab6ae46441c572e46cf596f59e57f8" dependencies = [ "anstyle", - "windows-sys", + "windows-sys 0.52.0", ] [[package]] @@ -116,9 +116,9 @@ dependencies = [ [[package]] name = "asn1-rs" -version = "0.6.1" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22ad1373757efa0f70ec53939aabc7152e1591cb485208052993070ac8d2429d" +checksum = "5493c3bedbacf7fd7382c6346bbd66687d12bbaad3a89a2d2c303ee6cf20b048" dependencies = [ "asn1-rs-derive", "asn1-rs-impl", @@ -132,9 +132,9 @@ dependencies = [ [[package]] name = "asn1-rs-derive" -version = "0.5.0" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7378575ff571966e99a744addeff0bff98b8ada0dedf1956d59e634db95eaac1" +checksum = "965c2d33e53cb6b267e148a4cb0760bc01f4904c1cd4bb4002a085bb016d1490" dependencies = [ "proc-macro2", "quote", @@ -243,9 +243,9 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" -version = "1.6.1" +version = "1.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a12916984aab3fa6e39d655a33e09c0071eb36d6ab3aea5c2d78551f1df6d952" +checksum = "8318a53db07bb3f8dca91a600466bdb3f2eaadeedfdbcf02e1accbad9271ba50" [[package]] name = "cast" @@ -335,11 +335,19 @@ dependencies = [ name = "chia-client" version = "0.10.0" dependencies = [ + "anyhow", "chia-protocol", + "chia-ssl", "chia-traits 0.10.0", + "dns-lookup", + "env_logger", "futures-util", + "hex", + "hex-literal", "log", "native-tls", + "rand", + "semver", "sha2", "thiserror", "tokio", @@ -581,9 +589,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.11" +version = "4.5.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35723e6a11662c2afb578bcf0b88bf6ea8e21282a953428f240574fcc3a2b5b3" +checksum = "0fbb260a053428790f3de475e304ff84cdbc4face759ea7a3e64c1edd938a7fc" dependencies = [ "clap_builder", "clap_derive", @@ -591,9 +599,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.11" +version = "4.5.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49eb96cbfa7cfa35017b7cd548c75b14c3118c98b423041d70562665e07fb0fa" +checksum = "64b17d7ea74e9f833c7dbf2cbe4fb12ff26783eda4782a8975b72f895c9b4d99" dependencies = [ "anstream", "anstyle", @@ -603,9 +611,9 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.5.11" +version = "4.5.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d029b67f89d30bbb547c89fd5161293c0aec155fc691d7924b64550662db93e" +checksum = "501d359d5f3dcaf6ecdeee48833ae73ec6e42723a1e52419c79abf9507eec0a0" dependencies = [ "heck 0.5.0", "proc-macro2", @@ -899,6 +907,18 @@ dependencies = [ "syn 2.0.72", ] +[[package]] +name = "dns-lookup" +version = "2.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5766087c2235fec47fafa4cfecc81e494ee679d0fd4a59887ea0919bfb0e4fc" +dependencies = [ + "cfg-if", + "libc", + "socket2", + "windows-sys 0.48.0", +] + [[package]] name = "ecdsa" version = "0.16.9" @@ -939,6 +959,29 @@ dependencies = [ "zeroize", ] +[[package]] +name = "env_filter" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f2c92ceda6ceec50f43169f9ee8424fe2db276791afde7b2cd8bc084cb376ab" +dependencies = [ + "log", + "regex", +] + +[[package]] +name = "env_logger" +version = "0.11.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13fa619b91fb2381732789fc5de83b45675e882f66623b7d8cb4f643017018d" +dependencies = [ + "anstream", + "anstyle", + "env_filter", + "humantime", + "log", +] + [[package]] name = "equivalent" version = "1.0.1" @@ -1256,6 +1299,12 @@ version = "1.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fcc0b4a115bf80b728eb8ea024ad5bd707b615bfed49e0665b6e0f86fd082d9" +[[package]] +name = "humantime" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" + [[package]] name = "idna" version = "0.5.0" @@ -1268,9 +1317,9 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.2.6" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26" +checksum = "de3fc2e30ba82dd1b3911c8de1ffc143c74a914a14e99514d7637e3099df5ea0" dependencies = [ "equivalent", "hashbrown", @@ -1296,7 +1345,7 @@ checksum = "f23ff5ef2b80d608d61efee834934d862cd92461afc0560dedf493e4c033738b" dependencies = [ "hermit-abi", "libc", - "windows-sys", + "windows-sys 0.52.0", ] [[package]] @@ -1429,9 +1478,9 @@ checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" [[package]] name = "lru" -version = "0.12.3" +version = "0.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3262e75e648fce39813cb56ac41f3c3e3f65217ebf3844d818d1f9398cfb0dc" +checksum = "37ee39891760e7d94734f6f63fedc29a2e4a152f836120753a72503f09fcf904" dependencies = [ "hashbrown", ] @@ -1475,7 +1524,7 @@ dependencies = [ "hermit-abi", "libc", "wasi", - "windows-sys", + "windows-sys 0.52.0", ] [[package]] @@ -1634,6 +1683,12 @@ dependencies = [ "syn 2.0.72", ] +[[package]] +name = "openssl-probe" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" + [[package]] name = "openssl-src" version = "300.3.1+3.3.1" @@ -1656,50 +1711,6 @@ dependencies = [ "vcpkg", ] -[[package]] -name = "openssl" -version = "0.10.64" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95a0481286a310808298130d22dd1fef0fa571e05a8f44ec801801e84b216b1f" -dependencies = [ - "bitflags 2.5.0", - "cfg-if", - "foreign-types", - "libc", - "once_cell", - "openssl-macros", - "openssl-sys", -] - -[[package]] -name = "openssl-macros" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.70", -] - -[[package]] -name = "openssl-probe" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" - -[[package]] -name = "openssl-sys" -version = "0.9.102" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c597637d56fbc83893a35eb0dd04b2b8e7a50c91e64e9493e398b5df4fb45fa2" -dependencies = [ - "cc", - "libc", - "pkg-config", - "vcpkg", -] - [[package]] name = "p256" version = "0.13.2" @@ -1732,7 +1743,7 @@ dependencies = [ "libc", "redox_syscall", "smallvec", - "windows-targets", + "windows-targets 0.52.6", ] [[package]] @@ -1841,12 +1852,11 @@ checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" [[package]] name = "ppv-lite86" -version = "0.2.19" +version = "0.2.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2288c0e17cc8d342c712bb43a257a80ebffce59cdb33d5000d8348f3ec02528b" +checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04" dependencies = [ "zerocopy", - "zerocopy-derive", ] [[package]] @@ -2026,9 +2036,9 @@ dependencies = [ [[package]] name = "regex" -version = "1.10.5" +version = "1.10.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b91213439dad192326a0d7c6ee3955910425f441d7038e0d6933b0aec5c4517f" +checksum = "4219d74c6b67a3654a9fbebc4b419e22126d13d2f3c4a07ee0cb61ff79a79619" dependencies = [ "aho-corasick", "memchr", @@ -2075,7 +2085,7 @@ dependencies = [ "libc", "spin", "untrusted", - "windows-sys", + "windows-sys 0.52.0", ] [[package]] @@ -2168,7 +2178,7 @@ version = "0.38.34" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f" dependencies = [ - "bitflags 2.5.0", + "bitflags", "errno", "libc", "linux-raw-sys", @@ -2227,11 +2237,11 @@ dependencies = [ [[package]] name = "security-framework" -version = "2.11.0" +version = "2.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c627723fd09706bacdb5cf41499e95098555af3c3c29d014dc3c458ef6be11c0" +checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" dependencies = [ - "bitflags 2.5.0", + "bitflags", "core-foundation", "core-foundation-sys", "libc", @@ -2240,9 +2250,9 @@ dependencies = [ [[package]] name = "security-framework-sys" -version = "2.11.0" +version = "2.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "317936bbbd05227752583946b9e66d7ce3b489f84e11a94a510b4437fef407d7" +checksum = "75da29fe9b9b08fe9d6b22b5b4bcbc75d8db3aa31e639aa56bb62e9d46bfceaf" dependencies = [ "core-foundation-sys", "libc", @@ -2276,9 +2286,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.121" +version = "1.0.122" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ab380d7d9f22ef3f21ad3e6c1ebe8e4fc7a2000ccba2e4d71fc96f15b2cb609" +checksum = "784b6203951c57ff748476b126ccb5e8e2959a5c19e5c617ab1956be3dbc68da" dependencies = [ "itoa", "memchr", @@ -2308,6 +2318,15 @@ dependencies = [ "digest", ] +[[package]] +name = "signal-hook-registry" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1" +dependencies = [ + "libc", +] + [[package]] name = "signature" version = "2.2.0" @@ -2340,7 +2359,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ce305eb0b4296696835b71df73eb912e0f1ffd2556a501fcede6e0c50349191c" dependencies = [ "libc", - "windows-sys", + "windows-sys 0.52.0", ] [[package]] @@ -2406,18 +2425,19 @@ dependencies = [ [[package]] name = "target-lexicon" -version = "0.12.15" +version = "0.12.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4873307b7c257eddcb50c9bedf158eb669578359fb28428bef438fec8e6ba7c2" +checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1" [[package]] name = "tempfile" -version = "3.10.1" +version = "3.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85b77fafb263dd9d05cbeac119526425676db3784113aa9295c88498cbf8bff1" +checksum = "b8fcd239983515c23a32fb82099f97d0b11b8c72f654ed659363a95c3dad7a53" dependencies = [ "cfg-if", "fastrand", + "once_cell", "rustix", "windows-sys 0.52.0", ] @@ -2537,9 +2557,23 @@ dependencies = [ "bytes", "libc", "mio", + "parking_lot", "pin-project-lite", + "signal-hook-registry", "socket2", - "windows-sys", + "tokio-macros", + "windows-sys 0.52.0", +] + +[[package]] +name = "tokio-macros" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.72", ] [[package]] @@ -2777,11 +2811,20 @@ checksum = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc" [[package]] name = "winapi-util" -version = "0.1.8" +version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d4cc384e1e73b93bafa6fb4f1df8c41695c8a91cf9c4c64358067d15a7b6c6b" +checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" dependencies = [ - "windows-sys", + "windows-sys 0.59.0", +] + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets 0.48.5", ] [[package]] @@ -2790,7 +2833,31 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ - "windows-targets", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-targets" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +dependencies = [ + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", ] [[package]] @@ -2799,28 +2866,46 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", "windows_i686_gnullvm", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", ] +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" + [[package]] name = "windows_aarch64_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" + [[package]] name = "windows_aarch64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" +[[package]] +name = "windows_i686_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" + [[package]] name = "windows_i686_gnu" version = "0.52.6" @@ -2833,24 +2918,48 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" +[[package]] +name = "windows_i686_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" + [[package]] name = "windows_i686_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" + [[package]] name = "windows_x86_64_gnu" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" + [[package]] name = "windows_x86_64_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" + [[package]] name = "windows_x86_64_msvc" version = "0.52.6" @@ -2945,18 +3054,18 @@ dependencies = [ [[package]] name = "zstd-safe" -version = "7.2.0" +version = "7.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa556e971e7b568dc775c136fc9de8c779b1c2fc3a63defaafadffdbd3181afa" +checksum = "54a3ab4db68cea366acc5c897c7b4d4d1b8994a9cd6e6f841f8964566a419059" dependencies = [ "zstd-sys", ] [[package]] name = "zstd-sys" -version = "2.0.12+zstd.1.5.6" +version = "2.0.13+zstd.1.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a4e40c320c3cb459d9a9ff6de98cff88f4751ee9275d140e2be94a2b74e4c13" +checksum = "38ff0f21cfee8f97d94cef41359e0c89aa6113028ab0291aa8ca0038995a95aa" dependencies = [ "cc", "pkg-config", diff --git a/Cargo.toml b/Cargo.toml index bf1508bc5..0ee3c9dc2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -26,7 +26,7 @@ unused_imports = "warn" unused_import_braces = "deny" unreachable_code = "deny" unreachable_patterns = "deny" -dead_code = "deny" +dead_code = "warn" deprecated = "deny" deprecated_in_future = "deny" trivial_casts = "deny" @@ -140,3 +140,6 @@ libfuzzer-sys = "0.4" wasm-bindgen = "0.2.92" log = "0.4.22" native-tls = "0.2.12" +dns-lookup = "2.0.4" +semver = "1.0.23" +env_logger = "0.11.3" diff --git a/crates/chia-client/Cargo.toml b/crates/chia-client/Cargo.toml index 89f1cbd74..aed4bc764 100644 --- a/crates/chia-client/Cargo.toml +++ b/crates/chia-client/Cargo.toml @@ -22,3 +22,14 @@ thiserror = { workspace = true } sha2 = { workspace = true } log = { workspace = true } native-tls = { workspace = true } +dns-lookup = { workspace = true } +hex-literal = { workspace = true } +rand = { workspace = true } +semver = { workspace = true } +hex = { workspace = true } + +[dev-dependencies] +chia-ssl = { path = "../chia-ssl" } +tokio = { workspace = true, features = ["full"] } +anyhow = { workspace = true } +env_logger = { workspace = true } diff --git a/crates/chia-client/examples/client.rs b/crates/chia-client/examples/client.rs new file mode 100644 index 000000000..8b9e4d348 --- /dev/null +++ b/crates/chia-client/examples/client.rs @@ -0,0 +1,54 @@ +use std::time::Duration; + +use chia_client::{create_tls_connector, Client, ClientOptions, Event}; +use chia_ssl::ChiaCertificate; +use tokio::time::sleep; + +#[tokio::main] +async fn main() -> anyhow::Result<()> { + env_logger::init(); + + log::info!("Generating certificate"); + let cert = ChiaCertificate::generate()?; + let tls_connector = create_tls_connector(cert.cert_pem.as_bytes(), cert.key_pem.as_bytes())?; + + log::info!("Creating client"); + let (client, mut receiver) = Client::with_options( + tls_connector, + ClientOptions { + target_peers: 20, + ..Default::default() + }, + ); + + log::info!("Connecting to DNS introducers"); + client.find_peers().await; + + let client_clone = client.clone(); + + tokio::spawn(async move { + loop { + sleep(Duration::from_secs(10)).await; + let count = client_clone.peer_count().await; + log::info!("Currently connected to {} peers", count); + client.find_peers().await; + } + }); + + while let Some(event) = receiver.recv().await { + match event { + Event::Message(peer_id, message) => { + log::info!( + "Received message from peer {}: {:?}", + peer_id, + message.msg_type + ); + } + Event::ConnectionClosed(peer_id) => { + log::info!("Peer {} disconnected", peer_id); + } + } + } + + Ok(()) +} diff --git a/crates/chia-client/examples/peer_discovery.rs b/crates/chia-client/examples/peer_discovery.rs new file mode 100644 index 000000000..3791ef49c --- /dev/null +++ b/crates/chia-client/examples/peer_discovery.rs @@ -0,0 +1,68 @@ +use std::time::Duration; + +use chia_client::{create_tls_connector, Peer}; +use chia_protocol::{Handshake, NodeType, ProtocolMessageTypes}; +use chia_ssl::ChiaCertificate; +use chia_traits::Streamable; +use dns_lookup::lookup_host; +use tokio::time::timeout; + +#[tokio::main] +async fn main() -> anyhow::Result<()> { + env_logger::init(); + + let cert = ChiaCertificate::generate()?; + let tls = create_tls_connector(cert.cert_pem.as_bytes(), cert.key_pem.as_bytes())?; + + for ip in lookup_host("dns-introducer.chia.net")? { + let Ok(response) = + timeout(Duration::from_secs(3), Peer::connect(ip, 8444, tls.clone())).await + else { + log::info!("{ip} exceeded connection timeout of 3 seconds"); + continue; + }; + + let (peer, mut receiver) = response?; + + peer.send(Handshake { + network_id: "mainnet".to_string(), + protocol_version: "0.0.37".to_string(), + software_version: "0.0.0".to_string(), + server_port: 0, + node_type: NodeType::Wallet, + capabilities: vec![ + (1, "1".to_string()), + (2, "1".to_string()), + (3, "1".to_string()), + ], + }) + .await?; + + let Ok(message) = timeout(Duration::from_secs(1), receiver.recv()).await else { + log::info!("{ip} exceeded timeout of 1 second"); + continue; + }; + + let Some(message) = message else { + log::info!("{ip} did not send any messages"); + continue; + }; + + if message.msg_type != ProtocolMessageTypes::Handshake { + log::info!("{ip} sent an unexpected message {:?}", message.msg_type); + continue; + } + + let Ok(handshake) = Handshake::from_bytes(&message.data) else { + log::info!("{ip} sent an invalid handshake"); + continue; + }; + + log::info!( + "{ip} handshake sent with protocol version {}", + handshake.protocol_version + ); + } + + Ok(()) +} diff --git a/crates/chia-client/src/client.rs b/crates/chia-client/src/client.rs new file mode 100644 index 000000000..7644068e0 --- /dev/null +++ b/crates/chia-client/src/client.rs @@ -0,0 +1,346 @@ +use std::{ + collections::{HashMap, HashSet}, + net::IpAddr, + str::FromStr, + sync::Arc, + time::Duration, +}; + +use chia_protocol::{ + Handshake, Message, NodeType, ProtocolMessageTypes, RequestPeers, RespondPeers, +}; +use chia_traits::Streamable; +use dns_lookup::lookup_host; +use futures_util::{stream::FuturesUnordered, StreamExt}; +use native_tls::TlsConnector; +use rand::{seq::SliceRandom, thread_rng}; +use semver::Version; +use tokio::{ + sync::{mpsc, Mutex, RwLock, RwLockWriteGuard}, + time::timeout, +}; + +use crate::{Error, Event, Network, Peer, PeerId, Result}; + +/// A client that can connect to many different peers on the network. +#[derive(Debug, Clone)] +pub struct Client(Arc); + +#[derive(Debug, Clone)] +pub struct ClientOptions { + /// The network to connect to. By default, this is mainnet. + pub network: Network, + + /// The type of service that this client represents. + /// This defaults to [`NodeType::Wallet`], since that is the most common use case for this library. + pub node_type: NodeType, + + /// The capabilities that this client supports. + /// This defaults to the standard capabilities all Chia services connect with. + pub capabilities: Vec<(u16, String)>, + + /// The minimum protocol version that this client supports. + /// Currently defaults to `0.0.37`, which is supported by a majority of the network. + /// If the protocol version of the peer is lower than this, the connection will be rejected. + pub protocol_version: Version, + + /// The software version of this client. + /// This is not important for the handshake, but is sent to the peer for informational purposes. + /// Defaults to `0.0.0`, since this isn't a Chia full node. + pub software_version: String, + + /// The ideal number of peers that should be connected at any given time. + /// This defaults to `5`. + pub target_peers: usize, + + /// How long to wait when trying to connect to a peer. + pub connection_timeout: Duration, + + /// How long to wait for a handshake response from a peer before disconnecting. + pub handshake_timeout: Duration, + + /// How long to wait for a response to a request for peers. + pub request_peers_timeout: Duration, +} + +impl Default for ClientOptions { + fn default() -> Self { + Self { + network: Network::mainnet(), + node_type: NodeType::Wallet, + capabilities: vec![ + (1, "1".to_string()), + (2, "1".to_string()), + (3, "1".to_string()), + ], + protocol_version: Version::parse("0.0.37").expect("invalid version"), + software_version: "0.0.0".to_string(), + target_peers: 5, + connection_timeout: Duration::from_secs(3), + handshake_timeout: Duration::from_secs(1), + request_peers_timeout: Duration::from_secs(3), + } + } +} + +#[derive(Debug)] +struct ClientInner { + peers: Arc>>, + message_sender: Arc>>, + options: ClientOptions, + tls_connector: TlsConnector, +} + +impl Client { + pub fn new(tls_connector: TlsConnector) -> (Self, mpsc::Receiver) { + Self::with_options(tls_connector, ClientOptions::default()) + } + + pub fn with_options( + tls_connector: TlsConnector, + options: ClientOptions, + ) -> (Self, mpsc::Receiver) { + let (sender, receiver) = mpsc::channel(32); + + let client = Self(Arc::new(ClientInner { + peers: Arc::new(RwLock::new(HashMap::new())), + message_sender: Arc::new(Mutex::new(sender)), + options, + tls_connector, + })); + + (client, receiver) + } + + pub async fn peer_count(&self) -> usize { + self.0.peers.read().await.len() + } + + pub async fn find_peers(&self) { + // If we don't have any peers, try to connect to DNS introducers. + if self.peer_count().await == 0 && self.connect_dns().await { + return; + } + + let mut peers = self.0.peers.write().await; + + // If we still don't have any peers, we can't do anything. + if peers.len() >= self.0.options.target_peers { + return; + } + + if peers.is_empty() { + log::error!("No peers connected after DNS lookups"); + return; + } + + for (peer_id, peer) in peers.clone() { + if peers.len() >= self.0.options.target_peers { + break; + } + + // Request new peers from the peer. + let Ok(Ok(response)): std::result::Result, _> = timeout( + self.0.options.request_peers_timeout, + peer.request_infallible(RequestPeers::new()), + ) + .await + else { + log::info!("Failed to request peers from peer {peer_id}"); + peers.remove(&peer_id); + continue; + }; + + log::info!("Requested peers from peer {peer_id}"); + + let mut ips = HashSet::new(); + + for item in response.peer_list { + // If we can't parse the IP address, skip it. + let Ok(ip_addr) = IpAddr::from_str(&item.host) else { + log::debug!("Failed to parse IP address {}", item.host); + continue; + }; + + ips.insert((ip_addr, item.port)); + } + + // Keep connecting peers until the peer list is exhausted, + // then move on to the next peer to request from. + let mut iter = ips.into_iter(); + + loop { + let required_peers = self.0.options.target_peers - peers.len(); + let next_peers: Vec<_> = iter.by_ref().take(required_peers).collect(); + if next_peers.is_empty() { + break; + } + self.connect_peers(&mut peers, next_peers).await; + } + } + } + + async fn connect_dns(&self) -> bool { + log::info!("Requesting peers from DNS introducer"); + + // Lock the peer map early to prevent adding too many connections. + let mut peers = self.0.peers.write().await; + + let mut ips = Vec::new(); + + for dns_introducer in &self.0.options.network.dns_introducers { + // If a DNS introducer lookup fails, we just skip it. + let Ok(result) = lookup_host(dns_introducer) else { + log::warn!("Failed to lookup DNS introducer `{dns_introducer}`"); + continue; + }; + ips.extend(result); + } + + // Shuffle the list of IPs so that we don't always connect to the same ones. + // This also prevents bias towards IPv4 or IPv6. + ips.as_mut_slice().shuffle(&mut thread_rng()); + + // Keep track of where we are in the peer list. + let mut cursor = 0; + + while peers.len() < self.0.options.target_peers { + // If we've reached the end of the list of IPs, stop early. + if cursor >= ips.len() { + break; + } + + // Calculate how many peers we still need to connect to. + let required_peers = self.0.options.target_peers - peers.len(); + + // Get the remaining peers we can and need to connect to. + let peers_to_try = &ips[cursor..ips.len().min(cursor + required_peers)]; + + // Increment the cursor by the number of peers we're trying to connect to. + cursor += required_peers; + + self.connect_peers( + &mut peers, + peers_to_try + .iter() + .map(|ip| (*ip, self.0.options.network.default_port)) + .collect(), + ) + .await; + } + + peers.len() >= self.0.options.target_peers + } + + async fn connect_peers( + &self, + peers: &mut RwLockWriteGuard<'_, HashMap>, + potential_ips: Vec<(IpAddr, u16)>, + ) { + let ips: Vec<(IpAddr, u16)> = potential_ips + .into_iter() + .filter(|&(ip, _port)| !peers.values().any(|peer| peer.ip_addr() == ip)) + .collect(); + + // Add the connections and wait for them to complete. + let mut connections = FuturesUnordered::new(); + + for (ip, port) in ips { + connections.push(self.connect_peer(ip, port)); + } + + while let Some(result) = connections.next().await { + let (ip, peer, mut receiver) = match result { + Ok(result) => result, + Err(error) => { + log::debug!("Failed to connect to peer: {error}"); + continue; + } + }; + + let peer_id = peer.peer_id(); + peers.insert(peer_id, peer); + + let message_sender = self.0.message_sender.clone(); + let peer_map = self.0.peers.clone(); + + // Spawn a task to propagate messages from the peer. + tokio::spawn(async move { + while let Some(message) = receiver.recv().await { + if let Err(error) = message_sender + .lock() + .await + .send(Event::Message(peer_id, message)) + .await + { + log::debug!("Failed to send client message event: {error}"); + break; + } + } + peer_map.write().await.remove(&peer_id); + + if let Err(error) = message_sender + .lock() + .await + .send(Event::ConnectionClosed(peer_id)) + .await + { + log::debug!("Failed to send client connection closed event: {error}"); + } + + log::info!("Peer {ip} disconnected"); + }); + + log::info!("Connected to peer {ip}"); + } + } + + /// Does not lock the peer map or add the peer automatically. + /// This prevents deadlocks when called from within a lock. + async fn connect_peer( + &self, + ip: IpAddr, + port: u16, + ) -> Result<(IpAddr, Peer, mpsc::Receiver)> { + let (peer, mut receiver) = timeout( + self.0.options.connection_timeout, + Peer::connect(ip, port, self.0.tls_connector.clone()), + ) + .await??; + + let options = &self.0.options; + + peer.send(Handshake { + network_id: options.network.network_id.clone(), + protocol_version: options.protocol_version.to_string(), + software_version: options.software_version.clone(), + server_port: 0, + node_type: options.node_type, + capabilities: options.capabilities.clone(), + }) + .await?; + + let Some(message) = timeout(options.handshake_timeout, receiver.recv()).await? else { + return Err(Error::ExpectedHandshake); + }; + + if message.msg_type != ProtocolMessageTypes::Handshake { + return Err(Error::ExpectedHandshake); + }; + + let handshake = Handshake::from_bytes(&message.data)?; + + let Ok(protocol_version) = Version::parse(&handshake.protocol_version) else { + return Err(Error::InvalidProtocolVersion(handshake.protocol_version)); + }; + + if protocol_version < options.protocol_version { + return Err(Error::OutdatedProtocolVersion( + protocol_version, + options.protocol_version.clone(), + )); + } + + Ok((ip, peer, receiver)) + } +} diff --git a/crates/chia-client/src/error.rs b/crates/chia-client/src/error.rs index fcb99bcd9..0bf9abc50 100644 --- a/crates/chia-client/src/error.rs +++ b/crates/chia-client/src/error.rs @@ -1,12 +1,22 @@ use chia_protocol::ProtocolMessageTypes; +use semver::Version; use thiserror::Error; -use tokio::sync::oneshot::error::RecvError; +use tokio::{sync::oneshot::error::RecvError, time::error::Elapsed}; #[derive(Debug, Error)] pub enum Error { #[error("Peer is missing certificate")] MissingCertificate, + #[error("Handshake not received")] + ExpectedHandshake, + + #[error("Invalid protocol version {0}")] + InvalidProtocolVersion(String), + + #[error("Outdated protocol version {0}, expected {1}")] + OutdatedProtocolVersion(Version, Version), + #[error("Streamable error: {0}")] Streamable(#[from] chia_traits::Error), @@ -27,6 +37,12 @@ pub enum Error { #[error("Failed to receive message")] Recv(#[from] RecvError), + + #[error("Timeout error: {0}")] + Timeout(#[from] Elapsed), + + #[error("IO error: {0}")] + Io(#[from] std::io::Error), } pub type Result = std::result::Result; diff --git a/crates/chia-client/src/event.rs b/crates/chia-client/src/event.rs index ec08b4e2c..81c198101 100644 --- a/crates/chia-client/src/event.rs +++ b/crates/chia-client/src/event.rs @@ -1,8 +1,9 @@ -use chia_protocol::{CoinStateUpdate, Handshake, NewPeakWallet}; +use chia_protocol::Message; + +use crate::PeerId; #[derive(Debug, Clone)] pub enum Event { - Handshake(Handshake), - NewPeakWallet(NewPeakWallet), - CoinStateUpdate(CoinStateUpdate), + Message(PeerId, Message), + ConnectionClosed(PeerId), } diff --git a/crates/chia-client/src/lib.rs b/crates/chia-client/src/lib.rs index 71d9b32c6..3c990dc94 100644 --- a/crates/chia-client/src/lib.rs +++ b/crates/chia-client/src/lib.rs @@ -1,10 +1,16 @@ +mod client; mod error; mod event; +mod network; mod peer; mod request_map; mod response; +mod tls; +pub use client::*; pub use error::*; pub use event::*; +pub use network::*; pub use peer::*; pub use response::*; +pub use tls::*; diff --git a/crates/chia-client/src/network.rs b/crates/chia-client/src/network.rs new file mode 100644 index 000000000..af229e184 --- /dev/null +++ b/crates/chia-client/src/network.rs @@ -0,0 +1,39 @@ +use chia_protocol::Bytes32; +use hex_literal::hex; + +#[derive(Debug, Clone)] +pub struct Network { + pub network_id: String, + pub default_port: u16, + pub genesis_challenge: Bytes32, + pub dns_introducers: Vec, +} + +impl Network { + pub fn mainnet() -> Self { + Self { + network_id: "mainnet".to_string(), + default_port: 8444, + genesis_challenge: Bytes32::new(hex!( + "ccd5bb71183532bff220ba46c268991a3ff07eb358e8255a65c30a2dce0e5fbb" + )), + dns_introducers: vec![ + "dns-introducer.chia.net".to_string(), + "chia.ctrlaltdel.ch".to_string(), + "seeder.dexie.space".to_string(), + "chia.hoffmang.com".to_string(), + ], + } + } + + pub fn testnet11() -> Self { + Self { + network_id: "testnet11".to_string(), + default_port: 58444, + genesis_challenge: Bytes32::new(hex!( + "37a90eb5185a9c4439a91ddc98bbadce7b4feba060d50116a067de66bf236615" + )), + dns_introducers: vec!["dns-introducer-testnet11.chia.net".to_string()], + } + } +} diff --git a/crates/chia-client/src/peer.rs b/crates/chia-client/src/peer.rs index 16eb1355e..05ad574aa 100644 --- a/crates/chia-client/src/peer.rs +++ b/crates/chia-client/src/peer.rs @@ -1,22 +1,21 @@ -use std::sync::Arc; +use std::{fmt, net::IpAddr, sync::Arc}; -use chia_protocol::{ - ChiaProtocolMessage, CoinStateUpdate, Handshake, Message, NewPeakWallet, ProtocolMessageTypes, -}; +use chia_protocol::{ChiaProtocolMessage, Message}; use chia_traits::Streamable; use futures_util::{ stream::{SplitSink, SplitStream}, SinkExt, StreamExt, }; +use native_tls::TlsConnector; use sha2::{digest::FixedOutput, Digest, Sha256}; use tokio::{ net::TcpStream, sync::{mpsc, oneshot, Mutex}, task::JoinHandle, }; -use tokio_tungstenite::{MaybeTlsStream, WebSocketStream}; +use tokio_tungstenite::{Connector, MaybeTlsStream, WebSocketStream}; -use crate::{request_map::RequestMap, Error, Event, Response, Result}; +use crate::{request_map::RequestMap, Error, Response, Result}; type WebSocket = WebSocketStream>; type Sink = SplitSink; @@ -25,6 +24,12 @@ type Stream = SplitStream; #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct PeerId([u8; 32]); +impl fmt::Display for PeerId { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", hex::encode(self.0)) + } +} + #[derive(Debug, Clone)] pub struct Peer(Arc); @@ -34,13 +39,45 @@ struct PeerInner { inbound_handle: JoinHandle>, requests: Arc, peer_id: PeerId, + ip_addr: IpAddr, } impl Peer { - pub fn new(ws: WebSocket) -> Result<(Self, mpsc::Receiver)> { - let cert = match ws.get_ref() { - MaybeTlsStream::NativeTls(tls) => tls.get_ref().peer_certificate()?, - _ => None, + pub async fn connect( + ip: IpAddr, + port: u16, + tls_connector: TlsConnector, + ) -> Result<(Self, mpsc::Receiver)> { + let uri = if ip.is_ipv4() { + format!("wss://{ip}:{port}/ws") + } else { + format!("wss://[{ip}]:{port}/ws") + }; + Self::connect_addr(&uri, tls_connector).await + } + + pub async fn connect_addr( + uri: &str, + tls_connector: TlsConnector, + ) -> Result<(Self, mpsc::Receiver)> { + let (ws, _) = tokio_tungstenite::connect_async_tls_with_config( + uri, + None, + false, + Some(Connector::NativeTls(tls_connector)), + ) + .await?; + Self::from_websocket(ws) + } + + pub fn from_websocket(ws: WebSocket) -> Result<(Self, mpsc::Receiver)> { + let (addr, cert) = match ws.get_ref() { + MaybeTlsStream::NativeTls(tls) => { + let tls_stream = tls.get_ref(); + let tcp_stream = tls_stream.get_ref().get_ref(); + (tcp_stream.peer_addr()?, tls_stream.peer_certificate()?) + } + _ => return Err(Error::MissingCertificate), }; let Some(cert) = cert else { @@ -63,6 +100,7 @@ impl Peer { inbound_handle, requests, peer_id, + ip_addr: addr.ip(), })); Ok((peer, receiver)) @@ -72,6 +110,10 @@ impl Peer { self.0.peer_id } + pub fn ip_addr(&self) -> IpAddr { + self.0.ip_addr + } + pub async fn send(&self, body: T) -> Result<()> where T: Streamable + ChiaProtocolMessage, @@ -147,46 +189,29 @@ impl Drop for PeerInner { async fn handle_inbound_messages( mut stream: Stream, - sender: mpsc::Sender, + sender: mpsc::Sender, requests: Arc, ) -> Result<()> { while let Some(message) = stream.next().await { let message = Message::from_bytes(&message?.into_data())?; - match message.msg_type { - ProtocolMessageTypes::CoinStateUpdate => { - let event = Event::CoinStateUpdate(CoinStateUpdate::from_bytes(&message.data)?); - sender.send(event).await.map_err(|error| { - log::error!("Failed to send `CoinStateUpdate` event: {error}"); - Error::EventNotSent - })?; - } - ProtocolMessageTypes::NewPeakWallet => { - let event = Event::NewPeakWallet(NewPeakWallet::from_bytes(&message.data)?); - sender.send(event).await.map_err(|error| { - log::error!("Failed to send `NewPeakWallet` event: {error}"); - Error::EventNotSent - })?; - } - ProtocolMessageTypes::Handshake => { - let event = Event::Handshake(Handshake::from_bytes(&message.data)?); - sender.send(event).await.map_err(|error| { - log::error!("Failed to send `Handshake` event: {error}"); - Error::EventNotSent - })?; - } - kind => { - let Some(id) = message.id else { - log::error!("Received unknown message without an id."); - return Err(Error::UnexpectedMessage(kind)); - }; - let Some(request) = requests.remove(id).await else { - log::error!("Received message with untracked id {id}."); - return Err(Error::UnexpectedMessage(kind)); - }; - request.send(message); - } - } + let Some(id) = message.id else { + sender.send(message).await.map_err(|error| { + log::debug!("Failed to send peer message event: {error}"); + Error::EventNotSent + })?; + continue; + }; + + let Some(request) = requests.remove(id).await else { + log::warn!( + "Received {:?} message with untracked id {id}", + message.msg_type + ); + return Err(Error::UnexpectedMessage(message.msg_type)); + }; + + request.send(message); } Ok(()) } diff --git a/crates/chia-client/src/tls.rs b/crates/chia-client/src/tls.rs new file mode 100644 index 000000000..efbd9fc10 --- /dev/null +++ b/crates/chia-client/src/tls.rs @@ -0,0 +1,11 @@ +use native_tls::{Identity, TlsConnector}; + +pub fn create_tls_connector( + cert_pem: &[u8], + key_pem: &[u8], +) -> Result { + TlsConnector::builder() + .identity(Identity::from_pkcs8(cert_pem, key_pem)?) + .danger_accept_invalid_certs(true) + .build() +} From 53d725e8ad5ce819eec7cd196afc1ff746853049 Mon Sep 17 00:00:00 2001 From: Rigidity Date: Sun, 14 Jul 2024 19:26:51 -0400 Subject: [PATCH 03/26] Add missing methods to Client --- crates/chia-client/examples/client.rs | 2 +- crates/chia-client/src/client.rs | 28 +++++++++++++++++++++++++-- 2 files changed, 27 insertions(+), 3 deletions(-) diff --git a/crates/chia-client/examples/client.rs b/crates/chia-client/examples/client.rs index 8b9e4d348..6b746f484 100644 --- a/crates/chia-client/examples/client.rs +++ b/crates/chia-client/examples/client.rs @@ -29,7 +29,7 @@ async fn main() -> anyhow::Result<()> { tokio::spawn(async move { loop { sleep(Duration::from_secs(10)).await; - let count = client_clone.peer_count().await; + let count = client_clone.len().await; log::info!("Currently connected to {} peers", count); client.find_peers().await; } diff --git a/crates/chia-client/src/client.rs b/crates/chia-client/src/client.rs index 7644068e0..82620cd53 100644 --- a/crates/chia-client/src/client.rs +++ b/crates/chia-client/src/client.rs @@ -112,13 +112,37 @@ impl Client { (client, receiver) } - pub async fn peer_count(&self) -> usize { + pub async fn len(&self) -> usize { self.0.peers.read().await.len() } + pub async fn is_empty(&self) -> bool { + self.0.peers.read().await.is_empty() + } + + pub async fn peer_ids(&self) -> Vec { + self.0.peers.read().await.keys().copied().collect() + } + + pub async fn peers(&self) -> Vec { + self.0.peers.read().await.values().cloned().collect() + } + + pub async fn peer(&self, peer_id: PeerId) -> Option { + self.0.peers.read().await.get(&peer_id).cloned() + } + + pub async fn remove_peer(&self, peer_id: PeerId) -> Option { + self.0.peers.write().await.remove(&peer_id) + } + + pub async fn clear(&self) { + self.0.peers.write().await.clear(); + } + pub async fn find_peers(&self) { // If we don't have any peers, try to connect to DNS introducers. - if self.peer_count().await == 0 && self.connect_dns().await { + if self.len().await == 0 && self.connect_dns().await { return; } From 06bc1829a72769bd71fe03badc4062e007c01274 Mon Sep 17 00:00:00 2001 From: Rigidity Date: Sun, 14 Jul 2024 19:27:52 -0400 Subject: [PATCH 04/26] Add time feature to client --- crates/chia-client/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/chia-client/Cargo.toml b/crates/chia-client/Cargo.toml index aed4bc764..99ef3f6f4 100644 --- a/crates/chia-client/Cargo.toml +++ b/crates/chia-client/Cargo.toml @@ -14,7 +14,7 @@ workspace = true [dependencies] chia-protocol = { workspace = true } chia-traits = { workspace = true } -tokio = { workspace = true, features = ["rt", "sync"] } +tokio = { workspace = true, features = ["rt", "sync", "time"] } tokio-tungstenite = { workspace = true, features = ["native-tls"] } futures-util = { workspace = true } tungstenite = { workspace = true } From c0571eb9b168f2d29cfc61ad48c2465af1e2fdf0 Mon Sep 17 00:00:00 2001 From: Rigidity Date: Tue, 16 Jul 2024 23:53:42 -0400 Subject: [PATCH 05/26] Temp --- crates/chia-client/examples/client.rs | 48 +++++++----- crates/chia-client/examples/peer.rs | 38 +++++++++ crates/chia-client/src/client.rs | 106 +++++++++++++------------- crates/chia-client/src/error.rs | 3 + 4 files changed, 123 insertions(+), 72 deletions(-) create mode 100644 crates/chia-client/examples/peer.rs diff --git a/crates/chia-client/examples/client.rs b/crates/chia-client/examples/client.rs index 6b746f484..dca8a27a6 100644 --- a/crates/chia-client/examples/client.rs +++ b/crates/chia-client/examples/client.rs @@ -1,6 +1,7 @@ use std::time::Duration; -use chia_client::{create_tls_connector, Client, ClientOptions, Event}; +use chia_client::{create_tls_connector, Client, ClientOptions, Event, Network}; +use chia_protocol::NodeType; use chia_ssl::ChiaCertificate; use tokio::time::sleep; @@ -13,16 +14,28 @@ async fn main() -> anyhow::Result<()> { let tls_connector = create_tls_connector(cert.cert_pem.as_bytes(), cert.key_pem.as_bytes())?; log::info!("Creating client"); - let (client, mut receiver) = Client::with_options( + let (client, mut receiver) = Client::new( tls_connector, ClientOptions { + network: Network::testnet11(), target_peers: 20, - ..Default::default() + connection_concurrency: 10, + node_type: NodeType::Wallet, + capabilities: vec![ + (1, "1".to_string()), + (2, "1".to_string()), + (3, "1".to_string()), + ], + protocol_version: "0.0.34".parse()?, + software_version: "0.0.0".to_string(), + connection_timeout: Duration::from_secs(3), + handshake_timeout: Duration::from_secs(2), + request_peers_timeout: Duration::from_secs(3), }, ); log::info!("Connecting to DNS introducers"); - client.find_peers().await; + client.find_peers(true).await; let client_clone = client.clone(); @@ -31,23 +44,24 @@ async fn main() -> anyhow::Result<()> { sleep(Duration::from_secs(10)).await; let count = client_clone.len().await; log::info!("Currently connected to {} peers", count); - client.find_peers().await; + // client_clone.find_peers(true).await; } }); while let Some(event) = receiver.recv().await { - match event { - Event::Message(peer_id, message) => { - log::info!( - "Received message from peer {}: {:?}", - peer_id, - message.msg_type - ); - } - Event::ConnectionClosed(peer_id) => { - log::info!("Peer {} disconnected", peer_id); - } - } + let Event::Message(peer_id, message) = event else { + continue; + }; + + let Some(peer) = client.peer(peer_id).await else { + continue; + }; + + log::info!( + "Received message from peer {}: {:?}", + peer.ip_addr(), + message.msg_type + ); } Ok(()) diff --git a/crates/chia-client/examples/peer.rs b/crates/chia-client/examples/peer.rs new file mode 100644 index 000000000..c4f94a18e --- /dev/null +++ b/crates/chia-client/examples/peer.rs @@ -0,0 +1,38 @@ +use std::env; + +use chia_client::{create_tls_connector, Peer}; +use chia_protocol::{Handshake, NodeType}; +use chia_ssl::ChiaCertificate; +use chia_traits::Streamable; + +#[tokio::main] +async fn main() -> anyhow::Result<()> { + let ssl = ChiaCertificate::generate()?; + let tls_connector = create_tls_connector(ssl.cert_pem.as_bytes(), ssl.key_pem.as_bytes())?; + let (peer, mut receiver) = + Peer::connect(env::var("PEER")?.parse()?, 58444, tls_connector).await?; + + peer.send(Handshake { + network_id: "testnet11".to_string(), + protocol_version: "0.0.34".to_string(), + software_version: "0.0.0".to_string(), + server_port: 0, + node_type: NodeType::Wallet, + capabilities: vec![ + (1, "1".to_string()), + (2, "1".to_string()), + (3, "1".to_string()), + ], + }) + .await?; + + let message = receiver.recv().await.unwrap(); + let handshake = Handshake::from_bytes(&message.data)?; + println!("{handshake:#?}"); + + while let Some(message) = receiver.recv().await { + println!("{message:?}"); + } + + Ok(()) +} diff --git a/crates/chia-client/src/client.rs b/crates/chia-client/src/client.rs index 82620cd53..782d6a066 100644 --- a/crates/chia-client/src/client.rs +++ b/crates/chia-client/src/client.rs @@ -16,7 +16,7 @@ use native_tls::TlsConnector; use rand::{seq::SliceRandom, thread_rng}; use semver::Version; use tokio::{ - sync::{mpsc, Mutex, RwLock, RwLockWriteGuard}, + sync::{mpsc, Mutex, RwLock, RwLockWriteGuard, Semaphore}, time::timeout, }; @@ -53,6 +53,9 @@ pub struct ClientOptions { /// This defaults to `5`. pub target_peers: usize, + /// The maximum number of concurrent connections that can be initiated at once. + pub connection_concurrency: usize, + /// How long to wait when trying to connect to a peer. pub connection_timeout: Duration, @@ -63,40 +66,17 @@ pub struct ClientOptions { pub request_peers_timeout: Duration, } -impl Default for ClientOptions { - fn default() -> Self { - Self { - network: Network::mainnet(), - node_type: NodeType::Wallet, - capabilities: vec![ - (1, "1".to_string()), - (2, "1".to_string()), - (3, "1".to_string()), - ], - protocol_version: Version::parse("0.0.37").expect("invalid version"), - software_version: "0.0.0".to_string(), - target_peers: 5, - connection_timeout: Duration::from_secs(3), - handshake_timeout: Duration::from_secs(1), - request_peers_timeout: Duration::from_secs(3), - } - } -} - #[derive(Debug)] struct ClientInner { peers: Arc>>, message_sender: Arc>>, options: ClientOptions, tls_connector: TlsConnector, + connection_lock: Semaphore, } impl Client { - pub fn new(tls_connector: TlsConnector) -> (Self, mpsc::Receiver) { - Self::with_options(tls_connector, ClientOptions::default()) - } - - pub fn with_options( + pub fn new( tls_connector: TlsConnector, options: ClientOptions, ) -> (Self, mpsc::Receiver) { @@ -107,6 +87,7 @@ impl Client { message_sender: Arc::new(Mutex::new(sender)), options, tls_connector, + connection_lock: Semaphore::new(1), })); (client, receiver) @@ -140,14 +121,14 @@ impl Client { self.0.peers.write().await.clear(); } - pub async fn find_peers(&self) { + pub async fn find_peers(&self, prefer_introducers: bool) { + let mut peers = self.0.peers.write().await; + // If we don't have any peers, try to connect to DNS introducers. - if self.len().await == 0 && self.connect_dns().await { + if (peers.is_empty() || prefer_introducers) && self.connect_dns(&mut peers).await { return; } - let mut peers = self.0.peers.write().await; - // If we still don't have any peers, we can't do anything. if peers.len() >= self.0.options.target_peers { return; @@ -170,12 +151,12 @@ impl Client { ) .await else { - log::info!("Failed to request peers from peer {peer_id}"); + log::info!("Failed to request peers from {}", peer.ip_addr()); peers.remove(&peer_id); continue; }; - log::info!("Requested peers from peer {peer_id}"); + log::info!("Requested peers from {}", peer.ip_addr()); let mut ips = HashSet::new(); @@ -194,8 +175,8 @@ impl Client { let mut iter = ips.into_iter(); loop { - let required_peers = self.0.options.target_peers - peers.len(); - let next_peers: Vec<_> = iter.by_ref().take(required_peers).collect(); + let max_peers = self.0.options.connection_concurrency - peers.len(); + let next_peers: Vec<_> = iter.by_ref().take(max_peers).collect(); if next_peers.is_empty() { break; } @@ -204,12 +185,9 @@ impl Client { } } - async fn connect_dns(&self) -> bool { + async fn connect_dns(&self, peers: &mut RwLockWriteGuard<'_, HashMap>) -> bool { log::info!("Requesting peers from DNS introducer"); - // Lock the peer map early to prevent adding too many connections. - let mut peers = self.0.peers.write().await; - let mut ips = Vec::new(); for dns_introducer in &self.0.options.network.dns_introducers { @@ -234,17 +212,17 @@ impl Client { break; } - // Calculate how many peers we still need to connect to. - let required_peers = self.0.options.target_peers - peers.len(); - - // Get the remaining peers we can and need to connect to. - let peers_to_try = &ips[cursor..ips.len().min(cursor + required_peers)]; + // Get the remaining peers we can connect to, up to the concurrency limit. + let peers_to_try = &ips[cursor + ..ips + .len() + .min(cursor + self.0.options.connection_concurrency)]; // Increment the cursor by the number of peers we're trying to connect to. - cursor += required_peers; + cursor += peers_to_try.len(); self.connect_peers( - &mut peers, + peers, peers_to_try .iter() .map(|ip| (*ip, self.0.options.network.default_port)) @@ -270,18 +248,34 @@ impl Client { let mut connections = FuturesUnordered::new(); for (ip, port) in ips { - connections.push(self.connect_peer(ip, port)); + connections.push(async move { + self.connect_peer(ip, port) + .await + .map_err(|error| (ip, port, error)) + }); } while let Some(result) = connections.next().await { - let (ip, peer, mut receiver) = match result { + if peers.len() >= self.0.options.target_peers { + break; + } + + let (peer, mut receiver) = match result { Ok(result) => result, - Err(error) => { - log::debug!("Failed to connect to peer: {error}"); + Err((ip, port, error)) => { + log::debug!( + "{error} for peer {}", + if ip.is_ipv4() { + format!("{ip}:{port}") + } else { + format!("[{ip}]:{port}") + } + ); continue; } }; + let ip = peer.ip_addr(); let peer_id = peer.peer_id(); peers.insert(peer_id, peer); @@ -321,11 +315,9 @@ impl Client { /// Does not lock the peer map or add the peer automatically. /// This prevents deadlocks when called from within a lock. - async fn connect_peer( - &self, - ip: IpAddr, - port: u16, - ) -> Result<(IpAddr, Peer, mpsc::Receiver)> { + async fn connect_peer(&self, ip: IpAddr, port: u16) -> Result<(Peer, mpsc::Receiver)> { + log::debug!("Connecting to peer {ip}"); + let (peer, mut receiver) = timeout( self.0.options.connection_timeout, Peer::connect(ip, port, self.0.tls_connector.clone()), @@ -354,6 +346,10 @@ impl Client { let handshake = Handshake::from_bytes(&message.data)?; + if handshake.network_id != options.network.network_id { + return Err(Error::WrongNetworkId(handshake.network_id)); + } + let Ok(protocol_version) = Version::parse(&handshake.protocol_version) else { return Err(Error::InvalidProtocolVersion(handshake.protocol_version)); }; @@ -365,6 +361,6 @@ impl Client { )); } - Ok((ip, peer, receiver)) + Ok((peer, receiver)) } } diff --git a/crates/chia-client/src/error.rs b/crates/chia-client/src/error.rs index 0bf9abc50..d8e9297dd 100644 --- a/crates/chia-client/src/error.rs +++ b/crates/chia-client/src/error.rs @@ -14,6 +14,9 @@ pub enum Error { #[error("Invalid protocol version {0}")] InvalidProtocolVersion(String), + #[error("Wrong network id {0}")] + WrongNetworkId(String), + #[error("Outdated protocol version {0}, expected {1}")] OutdatedProtocolVersion(Version, Version), From 4f60472375e6740b98b18078104e0b774e515b55 Mon Sep 17 00:00:00 2001 From: Rigidity Date: Wed, 17 Jul 2024 01:14:31 -0400 Subject: [PATCH 06/26] Temp 2 --- crates/chia-client/examples/client.rs | 33 +++----- crates/chia-client/src/client.rs | 115 +++++++++++++++----------- crates/chia-client/src/error.rs | 7 +- crates/chia-client/src/peer.rs | 3 +- 4 files changed, 83 insertions(+), 75 deletions(-) diff --git a/crates/chia-client/examples/client.rs b/crates/chia-client/examples/client.rs index dca8a27a6..3c5fd5011 100644 --- a/crates/chia-client/examples/client.rs +++ b/crates/chia-client/examples/client.rs @@ -1,6 +1,6 @@ use std::time::Duration; -use chia_client::{create_tls_connector, Client, ClientOptions, Event, Network}; +use chia_client::{create_tls_connector, Client, ClientOptions, Network}; use chia_protocol::NodeType; use chia_ssl::ChiaCertificate; use tokio::time::sleep; @@ -35,34 +35,23 @@ async fn main() -> anyhow::Result<()> { ); log::info!("Connecting to DNS introducers"); - client.find_peers(true).await; - - let client_clone = client.clone(); + let client2 = client.clone(); tokio::spawn(async move { loop { - sleep(Duration::from_secs(10)).await; - let count = client_clone.len().await; - log::info!("Currently connected to {} peers", count); - // client_clone.find_peers(true).await; + client2.find_peers(true).await; + sleep(Duration::from_secs(5)).await; } }); - while let Some(event) = receiver.recv().await { - let Event::Message(peer_id, message) = event else { - continue; - }; - - let Some(peer) = client.peer(peer_id).await else { - continue; - }; + tokio::spawn(async move { + loop { + sleep(Duration::from_secs(5)).await; + log::info!("Currently connected to {} peers", client.len().await); + } + }); - log::info!( - "Received message from peer {}: {:?}", - peer.ip_addr(), - message.msg_type - ); - } + while let Some(_event) = receiver.recv().await {} Ok(()) } diff --git a/crates/chia-client/src/client.rs b/crates/chia-client/src/client.rs index 782d6a066..89b63b6f5 100644 --- a/crates/chia-client/src/client.rs +++ b/crates/chia-client/src/client.rs @@ -16,7 +16,7 @@ use native_tls::TlsConnector; use rand::{seq::SliceRandom, thread_rng}; use semver::Version; use tokio::{ - sync::{mpsc, Mutex, RwLock, RwLockWriteGuard, Semaphore}, + sync::{mpsc, Mutex, Semaphore}, time::timeout, }; @@ -32,25 +32,20 @@ pub struct ClientOptions { pub network: Network, /// The type of service that this client represents. - /// This defaults to [`NodeType::Wallet`], since that is the most common use case for this library. pub node_type: NodeType, /// The capabilities that this client supports. - /// This defaults to the standard capabilities all Chia services connect with. pub capabilities: Vec<(u16, String)>, /// The minimum protocol version that this client supports. - /// Currently defaults to `0.0.37`, which is supported by a majority of the network. - /// If the protocol version of the peer is lower than this, the connection will be rejected. + /// If the protocol version of peers are lower than this, they will be disconnected. pub protocol_version: Version, /// The software version of this client. /// This is not important for the handshake, but is sent to the peer for informational purposes. - /// Defaults to `0.0.0`, since this isn't a Chia full node. pub software_version: String, /// The ideal number of peers that should be connected at any given time. - /// This defaults to `5`. pub target_peers: usize, /// The maximum number of concurrent connections that can be initiated at once. @@ -68,7 +63,7 @@ pub struct ClientOptions { #[derive(Debug)] struct ClientInner { - peers: Arc>>, + peers: Arc>>, message_sender: Arc>>, options: ClientOptions, tls_connector: TlsConnector, @@ -83,7 +78,7 @@ impl Client { let (sender, receiver) = mpsc::channel(32); let client = Self(Arc::new(ClientInner { - peers: Arc::new(RwLock::new(HashMap::new())), + peers: Arc::new(Mutex::new(HashMap::new())), message_sender: Arc::new(Mutex::new(sender)), options, tls_connector, @@ -94,53 +89,70 @@ impl Client { } pub async fn len(&self) -> usize { - self.0.peers.read().await.len() + self.0.peers.lock().await.len() } pub async fn is_empty(&self) -> bool { - self.0.peers.read().await.is_empty() + self.0.peers.lock().await.is_empty() } pub async fn peer_ids(&self) -> Vec { - self.0.peers.read().await.keys().copied().collect() + self.0.peers.lock().await.keys().copied().collect() } pub async fn peers(&self) -> Vec { - self.0.peers.read().await.values().cloned().collect() + self.0.peers.lock().await.values().cloned().collect() } pub async fn peer(&self, peer_id: PeerId) -> Option { - self.0.peers.read().await.get(&peer_id).cloned() + self.0.peers.lock().await.get(&peer_id).cloned() } pub async fn remove_peer(&self, peer_id: PeerId) -> Option { - self.0.peers.write().await.remove(&peer_id) + self.0.peers.lock().await.remove(&peer_id) } pub async fn clear(&self) { - self.0.peers.write().await.clear(); + self.0.peers.lock().await.clear(); } pub async fn find_peers(&self, prefer_introducers: bool) { - let mut peers = self.0.peers.write().await; + let _permit = self + .0 + .connection_lock + .acquire() + .await + .expect("the semaphore should not be closed"); - // If we don't have any peers, try to connect to DNS introducers. - if (peers.is_empty() || prefer_introducers) && self.connect_dns(&mut peers).await { + if self.len().await >= self.0.options.target_peers { return; } - // If we still don't have any peers, we can't do anything. - if peers.len() >= self.0.options.target_peers { + // If we don't have any peers, try to connect to DNS introducers. + if self.is_empty().await || prefer_introducers { + self.connect_dns().await; + + // If we still don't have any peers, we can't do anything. + if self.is_empty().await { + return; + } + } + + if self.len().await >= self.0.options.target_peers { return; } - if peers.is_empty() { + if self.is_empty().await { log::error!("No peers connected after DNS lookups"); return; } - for (peer_id, peer) in peers.clone() { - if peers.len() >= self.0.options.target_peers { + let peer_lock = self.0.peers.lock().await; + let peers = peer_lock.clone(); + drop(peer_lock); + + for (peer_id, peer) in peers { + if self.len().await >= self.0.options.target_peers { break; } @@ -152,7 +164,7 @@ impl Client { .await else { log::info!("Failed to request peers from {}", peer.ip_addr()); - peers.remove(&peer_id); + self.remove_peer(peer_id).await; continue; }; @@ -175,17 +187,21 @@ impl Client { let mut iter = ips.into_iter(); loop { - let max_peers = self.0.options.connection_concurrency - peers.len(); - let next_peers: Vec<_> = iter.by_ref().take(max_peers).collect(); + let next_peers: Vec<_> = iter + .by_ref() + .take(self.0.options.connection_concurrency) + .collect(); + if next_peers.is_empty() { break; } - self.connect_peers(&mut peers, next_peers).await; + + self.connect_peers(next_peers).await; } } } - async fn connect_dns(&self, peers: &mut RwLockWriteGuard<'_, HashMap>) -> bool { + async fn connect_dns(&self) { log::info!("Requesting peers from DNS introducer"); let mut ips = Vec::new(); @@ -206,7 +222,7 @@ impl Client { // Keep track of where we are in the peer list. let mut cursor = 0; - while peers.len() < self.0.options.target_peers { + while self.len().await < self.0.options.target_peers { // If we've reached the end of the list of IPs, stop early. if cursor >= ips.len() { break; @@ -222,7 +238,6 @@ impl Client { cursor += peers_to_try.len(); self.connect_peers( - peers, peers_to_try .iter() .map(|ip| (*ip, self.0.options.network.default_port)) @@ -230,24 +245,21 @@ impl Client { ) .await; } - - peers.len() >= self.0.options.target_peers } - async fn connect_peers( - &self, - peers: &mut RwLockWriteGuard<'_, HashMap>, - potential_ips: Vec<(IpAddr, u16)>, - ) { - let ips: Vec<(IpAddr, u16)> = potential_ips - .into_iter() - .filter(|&(ip, _port)| !peers.values().any(|peer| peer.ip_addr() == ip)) - .collect(); + async fn connect_peers(&self, potential_ips: Vec<(IpAddr, u16)>) { + let peer_lock = self.0.peers.lock().await; + let peers = peer_lock.clone(); + drop(peer_lock); // Add the connections and wait for them to complete. let mut connections = FuturesUnordered::new(); - for (ip, port) in ips { + for (ip, port) in potential_ips { + if peers.iter().any(|(_, peer)| peer.ip_addr() == ip) { + continue; + } + connections.push(async move { self.connect_peer(ip, port) .await @@ -256,7 +268,7 @@ impl Client { } while let Some(result) = connections.next().await { - if peers.len() >= self.0.options.target_peers { + if self.len().await >= self.0.options.target_peers { break; } @@ -277,7 +289,7 @@ impl Client { let ip = peer.ip_addr(); let peer_id = peer.peer_id(); - peers.insert(peer_id, peer); + self.0.peers.lock().await.insert(peer_id, peer); let message_sender = self.0.message_sender.clone(); let peer_map = self.0.peers.clone(); @@ -295,7 +307,8 @@ impl Client { break; } } - peer_map.write().await.remove(&peer_id); + + peer_map.lock().await.remove(&peer_id); if let Err(error) = message_sender .lock() @@ -313,8 +326,6 @@ impl Client { } } - /// Does not lock the peer map or add the peer automatically. - /// This prevents deadlocks when called from within a lock. async fn connect_peer(&self, ip: IpAddr, port: u16) -> Result<(Peer, mpsc::Receiver)> { log::debug!("Connecting to peer {ip}"); @@ -322,7 +333,8 @@ impl Client { self.0.options.connection_timeout, Peer::connect(ip, port, self.0.tls_connector.clone()), ) - .await??; + .await + .map_err(Error::ConnectionTimeout)??; let options = &self.0.options; @@ -336,7 +348,10 @@ impl Client { }) .await?; - let Some(message) = timeout(options.handshake_timeout, receiver.recv()).await? else { + let Some(message) = timeout(options.handshake_timeout, receiver.recv()) + .await + .map_err(Error::HandshakeTimeout)? + else { return Err(Error::ExpectedHandshake); }; diff --git a/crates/chia-client/src/error.rs b/crates/chia-client/src/error.rs index d8e9297dd..0eda44601 100644 --- a/crates/chia-client/src/error.rs +++ b/crates/chia-client/src/error.rs @@ -41,8 +41,11 @@ pub enum Error { #[error("Failed to receive message")] Recv(#[from] RecvError), - #[error("Timeout error: {0}")] - Timeout(#[from] Elapsed), + #[error("Connection timeout: {0}")] + ConnectionTimeout(Elapsed), + + #[error("Handshake timeout: {0}")] + HandshakeTimeout(Elapsed), #[error("IO error: {0}")] Io(#[from] std::io::Error), diff --git a/crates/chia-client/src/peer.rs b/crates/chia-client/src/peer.rs index 05ad574aa..e3b74d849 100644 --- a/crates/chia-client/src/peer.rs +++ b/crates/chia-client/src/peer.rs @@ -193,7 +193,8 @@ async fn handle_inbound_messages( requests: Arc, ) -> Result<()> { while let Some(message) = stream.next().await { - let message = Message::from_bytes(&message?.into_data())?; + let message = message?; + let message = Message::from_bytes(&message.into_data())?; let Some(id) = message.id else { sender.send(message).await.map_err(|error| { From 7948299bbeb127047a1e0e6d58ddf9a2ea46b53f Mon Sep 17 00:00:00 2001 From: Rigidity Date: Wed, 17 Jul 2024 01:39:37 -0400 Subject: [PATCH 07/26] handle messages better --- crates/chia-client/src/client.rs | 6 +-- crates/chia-client/src/peer.rs | 66 ++++++++++++++++++++++---------- 2 files changed, 48 insertions(+), 24 deletions(-) diff --git a/crates/chia-client/src/client.rs b/crates/chia-client/src/client.rs index 89b63b6f5..ca7bda7ba 100644 --- a/crates/chia-client/src/client.rs +++ b/crates/chia-client/src/client.rs @@ -275,7 +275,7 @@ impl Client { let (peer, mut receiver) = match result { Ok(result) => result, Err((ip, port, error)) => { - log::debug!( + log::warn!( "{error} for peer {}", if ip.is_ipv4() { format!("{ip}:{port}") @@ -303,7 +303,7 @@ impl Client { .send(Event::Message(peer_id, message)) .await { - log::debug!("Failed to send client message event: {error}"); + log::warn!("Failed to send client message event: {error}"); break; } } @@ -316,7 +316,7 @@ impl Client { .send(Event::ConnectionClosed(peer_id)) .await { - log::debug!("Failed to send client connection closed event: {error}"); + log::warn!("Failed to send client connection closed event: {error}"); } log::info!("Peer {ip} disconnected"); diff --git a/crates/chia-client/src/peer.rs b/crates/chia-client/src/peer.rs index e3b74d849..8750c620f 100644 --- a/crates/chia-client/src/peer.rs +++ b/crates/chia-client/src/peer.rs @@ -36,7 +36,7 @@ pub struct Peer(Arc); #[derive(Debug)] struct PeerInner { sink: Mutex, - inbound_handle: JoinHandle>, + inbound_handle: JoinHandle<()>, requests: Arc, peer_id: PeerId, ip_addr: IpAddr, @@ -90,10 +90,15 @@ impl Peer { let peer_id = PeerId(hasher.finalize_fixed().into()); let (sink, stream) = ws.split(); let (sender, receiver) = mpsc::channel(32); + let requests = Arc::new(RequestMap::new()); + let requests_clone = requests.clone(); - let inbound_handle = - tokio::spawn(handle_inbound_messages(stream, sender, requests.clone())); + let inbound_handle = tokio::spawn(async move { + if let Err(error) = handle_inbound_messages(stream, sender, requests_clone).await { + log::warn!("Error handling message: {error}"); + } + }); let peer = Self(Arc::new(PeerInner { sink: Mutex::new(sink), @@ -192,27 +197,46 @@ async fn handle_inbound_messages( sender: mpsc::Sender, requests: Arc, ) -> Result<()> { + use tungstenite::Message::{Binary, Close, Frame, Ping, Pong, Text}; + while let Some(message) = stream.next().await { let message = message?; - let message = Message::from_bytes(&message.into_data())?; - - let Some(id) = message.id else { - sender.send(message).await.map_err(|error| { - log::debug!("Failed to send peer message event: {error}"); - Error::EventNotSent - })?; - continue; - }; - - let Some(request) = requests.remove(id).await else { - log::warn!( - "Received {:?} message with untracked id {id}", - message.msg_type - ); - return Err(Error::UnexpectedMessage(message.msg_type)); - }; - request.send(message); + match message { + Text(text) => { + log::warn!("Received unexpected text message: {text}"); + } + Close(close) => { + log::warn!("Received close: {close:?}"); + break; + } + Ping(_ping) => {} + Pong(_pong) => {} + Binary(binary) => { + let message = Message::from_bytes(&binary)?; + + let Some(id) = message.id else { + sender.send(message).await.map_err(|error| { + log::warn!("Failed to send peer message event: {error}"); + Error::EventNotSent + })?; + continue; + }; + + let Some(request) = requests.remove(id).await else { + log::warn!( + "Received {:?} message with untracked id {id}", + message.msg_type + ); + return Err(Error::UnexpectedMessage(message.msg_type)); + }; + + request.send(message); + } + Frame(frame) => { + log::warn!("Received frame: {frame}"); + } + } } Ok(()) } From c4a073ffce674f74e92fdc356948f8a0149be741 Mon Sep 17 00:00:00 2001 From: Rigidity Date: Wed, 17 Jul 2024 12:40:08 -0400 Subject: [PATCH 08/26] Update example to use mainnet --- crates/chia-client/examples/client.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/chia-client/examples/client.rs b/crates/chia-client/examples/client.rs index 3c5fd5011..7123b0811 100644 --- a/crates/chia-client/examples/client.rs +++ b/crates/chia-client/examples/client.rs @@ -17,7 +17,7 @@ async fn main() -> anyhow::Result<()> { let (client, mut receiver) = Client::new( tls_connector, ClientOptions { - network: Network::testnet11(), + network: Network::mainnet(), target_peers: 20, connection_concurrency: 10, node_type: NodeType::Wallet, From b5e8c63e10ca49d45a4554ea4b0d185c8bee731b Mon Sep 17 00:00:00 2001 From: Rigidity Date: Wed, 17 Jul 2024 12:56:20 -0400 Subject: [PATCH 09/26] Add helper methods --- crates/chia-client/src/client.rs | 11 ++-- crates/chia-client/src/peer.rs | 91 +++++++++++++++++++++++++++++++- 2 files changed, 93 insertions(+), 9 deletions(-) diff --git a/crates/chia-client/src/client.rs b/crates/chia-client/src/client.rs index ca7bda7ba..ec9e1eacb 100644 --- a/crates/chia-client/src/client.rs +++ b/crates/chia-client/src/client.rs @@ -6,9 +6,7 @@ use std::{ time::Duration, }; -use chia_protocol::{ - Handshake, Message, NodeType, ProtocolMessageTypes, RequestPeers, RespondPeers, -}; +use chia_protocol::{Handshake, Message, NodeType, ProtocolMessageTypes, RespondPeers}; use chia_traits::Streamable; use dns_lookup::lookup_host; use futures_util::{stream::FuturesUnordered, StreamExt}; @@ -157,11 +155,8 @@ impl Client { } // Request new peers from the peer. - let Ok(Ok(response)): std::result::Result, _> = timeout( - self.0.options.request_peers_timeout, - peer.request_infallible(RequestPeers::new()), - ) - .await + let Ok(Ok(response)): std::result::Result, _> = + timeout(self.0.options.request_peers_timeout, peer.request_peers()).await else { log::info!("Failed to request peers from {}", peer.ip_addr()); self.remove_peer(peer_id).await; diff --git a/crates/chia-client/src/peer.rs b/crates/chia-client/src/peer.rs index 8750c620f..58db888d0 100644 --- a/crates/chia-client/src/peer.rs +++ b/crates/chia-client/src/peer.rs @@ -1,6 +1,13 @@ use std::{fmt, net::IpAddr, sync::Arc}; -use chia_protocol::{ChiaProtocolMessage, Message}; +use chia_protocol::{ + Bytes32, ChiaProtocolMessage, CoinStateFilters, Message, PuzzleSolutionResponse, + RegisterForCoinUpdates, RegisterForPhUpdates, RejectCoinState, RejectPuzzleSolution, + RejectPuzzleState, RequestCoinState, RequestPeers, RequestPuzzleSolution, RequestPuzzleState, + RequestTransaction, RespondCoinState, RespondPeers, RespondPuzzleSolution, RespondPuzzleState, + RespondToCoinUpdates, RespondToPhUpdates, RespondTransaction, SendTransaction, SpendBundle, + TransactionAck, +}; use chia_traits::Streamable; use futures_util::{ stream::{SplitSink, SplitStream}, @@ -119,6 +126,88 @@ impl Peer { self.0.ip_addr } + pub async fn send_transaction(&self, spend_bundle: SpendBundle) -> Result { + self.request_infallible(SendTransaction::new(spend_bundle)) + .await + } + + pub async fn request_puzzle_state( + &self, + puzzle_hashes: Vec, + previous_height: Option, + header_hash: Bytes32, + filters: CoinStateFilters, + subscribe_when_finished: bool, + ) -> Result> { + self.request_fallible(RequestPuzzleState::new( + puzzle_hashes, + previous_height, + header_hash, + filters, + subscribe_when_finished, + )) + .await + } + + pub async fn request_coin_state( + &self, + coin_ids: Vec, + previous_height: Option, + header_hash: Bytes32, + subscribe: bool, + ) -> Result> { + self.request_fallible(RequestCoinState::new( + coin_ids, + previous_height, + header_hash, + subscribe, + )) + .await + } + + pub async fn register_for_ph_updates( + &self, + puzzle_hashes: Vec, + min_height: u32, + ) -> Result { + self.request_infallible(RegisterForPhUpdates::new(puzzle_hashes, min_height)) + .await + } + + pub async fn register_for_coin_updates( + &self, + coin_ids: Vec, + min_height: u32, + ) -> Result { + self.request_infallible(RegisterForCoinUpdates::new(coin_ids, min_height)) + .await + } + + pub async fn request_transaction(&self, transaction_id: Bytes32) -> Result { + self.request_infallible(RequestTransaction::new(transaction_id)) + .await + } + + pub async fn request_puzzle_and_solution( + &self, + coin_id: Bytes32, + height: u32, + ) -> Result> { + match self + .request_fallible::(RequestPuzzleSolution::new( + coin_id, height, + )) + .await? + { + Response::Success(response) => Ok(Response::Success(response.response)), + Response::Rejection(rejection) => Ok(Response::Rejection(rejection)), + } + } + + pub async fn request_peers(&self) -> Result { + self.request_infallible(RequestPeers::new()).await + } + pub async fn send(&self, body: T) -> Result<()> where T: Streamable + ChiaProtocolMessage, From 1c81643ec8883611a5e55b6cdb6c792107f34ace Mon Sep 17 00:00:00 2001 From: Rigidity Date: Wed, 17 Jul 2024 12:59:48 -0400 Subject: [PATCH 10/26] Tweaks and add request children --- crates/chia-client/src/lib.rs | 2 -- crates/chia-client/src/peer.rs | 23 ++++++++++++++--------- crates/chia-client/src/response.rs | 5 ----- 3 files changed, 14 insertions(+), 16 deletions(-) delete mode 100644 crates/chia-client/src/response.rs diff --git a/crates/chia-client/src/lib.rs b/crates/chia-client/src/lib.rs index 3c990dc94..de7c1f601 100644 --- a/crates/chia-client/src/lib.rs +++ b/crates/chia-client/src/lib.rs @@ -4,7 +4,6 @@ mod event; mod network; mod peer; mod request_map; -mod response; mod tls; pub use client::*; @@ -12,5 +11,4 @@ pub use error::*; pub use event::*; pub use network::*; pub use peer::*; -pub use response::*; pub use tls::*; diff --git a/crates/chia-client/src/peer.rs b/crates/chia-client/src/peer.rs index 58db888d0..29b958cb3 100644 --- a/crates/chia-client/src/peer.rs +++ b/crates/chia-client/src/peer.rs @@ -3,10 +3,10 @@ use std::{fmt, net::IpAddr, sync::Arc}; use chia_protocol::{ Bytes32, ChiaProtocolMessage, CoinStateFilters, Message, PuzzleSolutionResponse, RegisterForCoinUpdates, RegisterForPhUpdates, RejectCoinState, RejectPuzzleSolution, - RejectPuzzleState, RequestCoinState, RequestPeers, RequestPuzzleSolution, RequestPuzzleState, - RequestTransaction, RespondCoinState, RespondPeers, RespondPuzzleSolution, RespondPuzzleState, - RespondToCoinUpdates, RespondToPhUpdates, RespondTransaction, SendTransaction, SpendBundle, - TransactionAck, + RejectPuzzleState, RequestChildren, RequestCoinState, RequestPeers, RequestPuzzleSolution, + RequestPuzzleState, RequestTransaction, RespondChildren, RespondCoinState, RespondPeers, + RespondPuzzleSolution, RespondPuzzleState, RespondToCoinUpdates, RespondToPhUpdates, + RespondTransaction, SendTransaction, SpendBundle, TransactionAck, }; use chia_traits::Streamable; use futures_util::{ @@ -22,11 +22,12 @@ use tokio::{ }; use tokio_tungstenite::{Connector, MaybeTlsStream, WebSocketStream}; -use crate::{request_map::RequestMap, Error, Response, Result}; +use crate::{request_map::RequestMap, Error, Result}; type WebSocket = WebSocketStream>; type Sink = SplitSink; type Stream = SplitStream; +type Response = std::result::Result; #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct PeerId([u8; 32]); @@ -199,11 +200,15 @@ impl Peer { )) .await? { - Response::Success(response) => Ok(Response::Success(response.response)), - Response::Rejection(rejection) => Ok(Response::Rejection(rejection)), + Ok(response) => Ok(Ok(response.response)), + Err(rejection) => Ok(Err(rejection)), } } + pub async fn request_children(&self, coin_id: Bytes32) -> Result { + self.request_infallible(RequestChildren::new(coin_id)).await + } + pub async fn request_peers(&self) -> Result { self.request_infallible(RequestPeers::new()).await } @@ -235,9 +240,9 @@ impl Peer { )); } if message.msg_type == T::msg_type() { - Ok(Response::Success(T::from_bytes(&message.data)?)) + Ok(Ok(T::from_bytes(&message.data)?)) } else { - Ok(Response::Rejection(E::from_bytes(&message.data)?)) + Ok(Err(E::from_bytes(&message.data)?)) } } diff --git a/crates/chia-client/src/response.rs b/crates/chia-client/src/response.rs deleted file mode 100644 index f09f3add0..000000000 --- a/crates/chia-client/src/response.rs +++ /dev/null @@ -1,5 +0,0 @@ -#[derive(Debug, Clone, Copy)] -pub enum Response { - Success(T), - Rejection(E), -} From 1c6fb5ed9eca4558d34d0f0103e475003bc312bb Mon Sep 17 00:00:00 2001 From: Rigidity Date: Wed, 17 Jul 2024 13:53:04 -0400 Subject: [PATCH 11/26] Fix --- crates/chia-client/src/request_map.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/crates/chia-client/src/request_map.rs b/crates/chia-client/src/request_map.rs index a1c3011f5..597ef17c6 100644 --- a/crates/chia-client/src/request_map.rs +++ b/crates/chia-client/src/request_map.rs @@ -44,6 +44,7 @@ impl RequestMap { for i in 0..=u16::MAX { if !items.contains_key(&i) { index = Some(i); + break; } } From 8dc3f82b49160daa2f63b68e021498e605be2568 Mon Sep 17 00:00:00 2001 From: Rigidity Date: Thu, 18 Jul 2024 13:49:22 -0400 Subject: [PATCH 12/26] Refactor --- crates/chia-client/examples/peer.rs | 9 +- crates/chia-client/examples/peer_discovery.rs | 9 +- crates/chia-client/src/client.rs | 190 +++++++++--------- crates/chia-client/src/error.rs | 9 +- crates/chia-client/src/event.rs | 5 +- crates/chia-client/src/peer.rs | 22 +- 6 files changed, 125 insertions(+), 119 deletions(-) diff --git a/crates/chia-client/examples/peer.rs b/crates/chia-client/examples/peer.rs index c4f94a18e..d18069903 100644 --- a/crates/chia-client/examples/peer.rs +++ b/crates/chia-client/examples/peer.rs @@ -1,4 +1,4 @@ -use std::env; +use std::{env, net::SocketAddr}; use chia_client::{create_tls_connector, Peer}; use chia_protocol::{Handshake, NodeType}; @@ -9,8 +9,11 @@ use chia_traits::Streamable; async fn main() -> anyhow::Result<()> { let ssl = ChiaCertificate::generate()?; let tls_connector = create_tls_connector(ssl.cert_pem.as_bytes(), ssl.key_pem.as_bytes())?; - let (peer, mut receiver) = - Peer::connect(env::var("PEER")?.parse()?, 58444, tls_connector).await?; + let (peer, mut receiver) = Peer::connect( + SocketAddr::new(env::var("PEER")?.parse()?, 58444), + tls_connector, + ) + .await?; peer.send(Handshake { network_id: "testnet11".to_string(), diff --git a/crates/chia-client/examples/peer_discovery.rs b/crates/chia-client/examples/peer_discovery.rs index 3791ef49c..a1f56e001 100644 --- a/crates/chia-client/examples/peer_discovery.rs +++ b/crates/chia-client/examples/peer_discovery.rs @@ -1,4 +1,4 @@ -use std::time::Duration; +use std::{net::SocketAddr, time::Duration}; use chia_client::{create_tls_connector, Peer}; use chia_protocol::{Handshake, NodeType, ProtocolMessageTypes}; @@ -15,8 +15,11 @@ async fn main() -> anyhow::Result<()> { let tls = create_tls_connector(cert.cert_pem.as_bytes(), cert.key_pem.as_bytes())?; for ip in lookup_host("dns-introducer.chia.net")? { - let Ok(response) = - timeout(Duration::from_secs(3), Peer::connect(ip, 8444, tls.clone())).await + let Ok(response) = timeout( + Duration::from_secs(3), + Peer::connect(SocketAddr::new(ip, 8444), tls.clone()), + ) + .await else { log::info!("{ip} exceeded connection timeout of 3 seconds"); continue; diff --git a/crates/chia-client/src/client.rs b/crates/chia-client/src/client.rs index ec9e1eacb..484b8eed8 100644 --- a/crates/chia-client/src/client.rs +++ b/crates/chia-client/src/client.rs @@ -1,6 +1,6 @@ use std::{ collections::{HashMap, HashSet}, - net::IpAddr, + net::{IpAddr, SocketAddr}, str::FromStr, sync::Arc, time::Duration, @@ -94,23 +94,19 @@ impl Client { self.0.peers.lock().await.is_empty() } - pub async fn peer_ids(&self) -> Vec { - self.0.peers.lock().await.keys().copied().collect() - } - - pub async fn peers(&self) -> Vec { - self.0.peers.lock().await.values().cloned().collect() + pub async fn peer_map(&self) -> HashMap { + self.0.peers.lock().await.clone() } pub async fn peer(&self, peer_id: PeerId) -> Option { self.0.peers.lock().await.get(&peer_id).cloned() } - pub async fn remove_peer(&self, peer_id: PeerId) -> Option { - self.0.peers.lock().await.remove(&peer_id) + pub async fn disconnect_peer(&self, peer_id: PeerId) { + self.0.peers.lock().await.remove(&peer_id); } - pub async fn clear(&self) { + pub async fn disconnect_all(&self) { self.0.peers.lock().await.clear(); } @@ -158,12 +154,12 @@ impl Client { let Ok(Ok(response)): std::result::Result, _> = timeout(self.0.options.request_peers_timeout, peer.request_peers()).await else { - log::info!("Failed to request peers from {}", peer.ip_addr()); - self.remove_peer(peer_id).await; + log::info!("Failed to request peers from {}", peer.socket_addr()); + self.disconnect_peer(peer_id).await; continue; }; - log::info!("Requested peers from {}", peer.ip_addr()); + log::info!("Requested peers from {}", peer.socket_addr()); let mut ips = HashSet::new(); @@ -173,8 +169,7 @@ impl Client { log::debug!("Failed to parse IP address {}", item.host); continue; }; - - ips.insert((ip_addr, item.port)); + ips.insert(SocketAddr::new(ip_addr, item.port)); } // Keep connecting peers until the peer list is exhausted, @@ -199,7 +194,7 @@ impl Client { async fn connect_dns(&self) { log::info!("Requesting peers from DNS introducer"); - let mut ips = Vec::new(); + let mut socket_addrs = Vec::new(); for dns_introducer in &self.0.options.network.dns_introducers { // If a DNS introducer lookup fails, we just skip it. @@ -207,126 +202,76 @@ impl Client { log::warn!("Failed to lookup DNS introducer `{dns_introducer}`"); continue; }; - ips.extend(result); + socket_addrs.extend( + result + .into_iter() + .map(|ip| SocketAddr::new(ip, self.0.options.network.default_port)), + ); } // Shuffle the list of IPs so that we don't always connect to the same ones. // This also prevents bias towards IPv4 or IPv6. - ips.as_mut_slice().shuffle(&mut thread_rng()); + socket_addrs.as_mut_slice().shuffle(&mut thread_rng()); // Keep track of where we are in the peer list. let mut cursor = 0; while self.len().await < self.0.options.target_peers { // If we've reached the end of the list of IPs, stop early. - if cursor >= ips.len() { + if cursor >= socket_addrs.len() { break; } // Get the remaining peers we can connect to, up to the concurrency limit. - let peers_to_try = &ips[cursor - ..ips + let new_addrs = &socket_addrs[cursor + ..socket_addrs .len() .min(cursor + self.0.options.connection_concurrency)]; // Increment the cursor by the number of peers we're trying to connect to. - cursor += peers_to_try.len(); - - self.connect_peers( - peers_to_try - .iter() - .map(|ip| (*ip, self.0.options.network.default_port)) - .collect(), - ) - .await; + cursor += new_addrs.len(); + + self.connect_peers(new_addrs.to_vec()).await; } } - async fn connect_peers(&self, potential_ips: Vec<(IpAddr, u16)>) { - let peer_lock = self.0.peers.lock().await; - let peers = peer_lock.clone(); - drop(peer_lock); - + async fn connect_peers(&self, socket_addrs: Vec) { // Add the connections and wait for them to complete. let mut connections = FuturesUnordered::new(); - for (ip, port) in potential_ips { - if peers.iter().any(|(_, peer)| peer.ip_addr() == ip) { + let peers = self.peer_map().await; + + for socket_addr in socket_addrs { + if peers + .iter() + .any(|(_, peer)| peer.socket_addr().ip() == socket_addr.ip()) + { continue; } - connections.push(async move { - self.connect_peer(ip, port) - .await - .map_err(|error| (ip, port, error)) - }); + connections.push(async move { (socket_addr, self.connect_peer(socket_addr).await) }); } - while let Some(result) = connections.next().await { + while let Some((socket_addr, result)) = connections.next().await { if self.len().await >= self.0.options.target_peers { break; } - let (peer, mut receiver) = match result { - Ok(result) => result, - Err((ip, port, error)) => { - log::warn!( - "{error} for peer {}", - if ip.is_ipv4() { - format!("{ip}:{port}") - } else { - format!("[{ip}]:{port}") - } - ); - continue; - } - }; - - let ip = peer.ip_addr(); - let peer_id = peer.peer_id(); - self.0.peers.lock().await.insert(peer_id, peer); - - let message_sender = self.0.message_sender.clone(); - let peer_map = self.0.peers.clone(); - - // Spawn a task to propagate messages from the peer. - tokio::spawn(async move { - while let Some(message) = receiver.recv().await { - if let Err(error) = message_sender - .lock() - .await - .send(Event::Message(peer_id, message)) - .await - { - log::warn!("Failed to send client message event: {error}"); - break; - } - } - - peer_map.lock().await.remove(&peer_id); - - if let Err(error) = message_sender - .lock() - .await - .send(Event::ConnectionClosed(peer_id)) - .await - { - log::warn!("Failed to send client connection closed event: {error}"); - } - - log::info!("Peer {ip} disconnected"); - }); + if let Err(error) = result { + log::warn!("Failed to connect to peer {socket_addr} with error: {error}",); + continue; + } - log::info!("Connected to peer {ip}"); + log::info!("Connected to peer {socket_addr}"); } } - async fn connect_peer(&self, ip: IpAddr, port: u16) -> Result<(Peer, mpsc::Receiver)> { - log::debug!("Connecting to peer {ip}"); + pub async fn connect_peer(&self, socket_addr: SocketAddr) -> Result { + log::debug!("Connecting to peer {socket_addr}"); let (peer, mut receiver) = timeout( self.0.options.connection_timeout, - Peer::connect(ip, port, self.0.tls_connector.clone()), + Peer::connect(socket_addr, self.0.tls_connector.clone()), ) .await .map_err(Error::ConnectionTimeout)??; @@ -371,6 +316,57 @@ impl Client { )); } - Ok((peer, receiver)) + self.add_peer(peer, receiver).await + } + + pub async fn add_peer( + &self, + peer: Peer, + mut receiver: mpsc::Receiver, + ) -> Result { + let socket_addr = peer.socket_addr(); + let peer_id = peer.peer_id(); + + self.0.peers.lock().await.insert(peer_id, peer); + + self.0 + .message_sender + .lock() + .await + .send(Event::Connected(peer_id)) + .await?; + + // Spawn a task to propagate messages from the peer. + let message_sender = self.0.message_sender.clone(); + let peer_map = self.0.peers.clone(); + + tokio::spawn(async move { + while let Some(message) = receiver.recv().await { + if let Err(error) = message_sender + .lock() + .await + .send(Event::Message(peer_id, message)) + .await + { + log::warn!("Failed to send client message event: {error}"); + break; + } + } + + peer_map.lock().await.remove(&peer_id); + + if let Err(error) = message_sender + .lock() + .await + .send(Event::Disconnected(socket_addr)) + .await + { + log::warn!("Failed to send client connection closed event: {error}"); + } + + log::info!("Peer {socket_addr} disconnected"); + }); + + Ok(peer_id) } } diff --git a/crates/chia-client/src/error.rs b/crates/chia-client/src/error.rs index 0eda44601..82150c6a0 100644 --- a/crates/chia-client/src/error.rs +++ b/crates/chia-client/src/error.rs @@ -1,7 +1,11 @@ use chia_protocol::ProtocolMessageTypes; use semver::Version; use thiserror::Error; -use tokio::{sync::oneshot::error::RecvError, time::error::Elapsed}; +use tokio::sync::mpsc::error::SendError; +use tokio::sync::oneshot::error::RecvError; +use tokio::time::error::Elapsed; + +use crate::Event; #[derive(Debug, Error)] pub enum Error { @@ -38,6 +42,9 @@ pub enum Error { #[error("Failed to send event")] EventNotSent, + #[error("Failed to send message")] + Send(#[from] SendError), + #[error("Failed to receive message")] Recv(#[from] RecvError), diff --git a/crates/chia-client/src/event.rs b/crates/chia-client/src/event.rs index 81c198101..c22d01719 100644 --- a/crates/chia-client/src/event.rs +++ b/crates/chia-client/src/event.rs @@ -1,3 +1,5 @@ +use std::net::SocketAddr; + use chia_protocol::Message; use crate::PeerId; @@ -5,5 +7,6 @@ use crate::PeerId; #[derive(Debug, Clone)] pub enum Event { Message(PeerId, Message), - ConnectionClosed(PeerId), + Connected(PeerId), + Disconnected(SocketAddr), } diff --git a/crates/chia-client/src/peer.rs b/crates/chia-client/src/peer.rs index 29b958cb3..cbbf82363 100644 --- a/crates/chia-client/src/peer.rs +++ b/crates/chia-client/src/peer.rs @@ -1,4 +1,4 @@ -use std::{fmt, net::IpAddr, sync::Arc}; +use std::{fmt, net::SocketAddr, sync::Arc}; use chia_protocol::{ Bytes32, ChiaProtocolMessage, CoinStateFilters, Message, PuzzleSolutionResponse, @@ -47,21 +47,15 @@ struct PeerInner { inbound_handle: JoinHandle<()>, requests: Arc, peer_id: PeerId, - ip_addr: IpAddr, + socket_addr: SocketAddr, } impl Peer { pub async fn connect( - ip: IpAddr, - port: u16, + socket_addr: SocketAddr, tls_connector: TlsConnector, ) -> Result<(Self, mpsc::Receiver)> { - let uri = if ip.is_ipv4() { - format!("wss://{ip}:{port}/ws") - } else { - format!("wss://[{ip}]:{port}/ws") - }; - Self::connect_addr(&uri, tls_connector).await + Self::connect_addr(&format!("wss://{socket_addr}/ws"), tls_connector).await } pub async fn connect_addr( @@ -79,7 +73,7 @@ impl Peer { } pub fn from_websocket(ws: WebSocket) -> Result<(Self, mpsc::Receiver)> { - let (addr, cert) = match ws.get_ref() { + let (socket_addr, cert) = match ws.get_ref() { MaybeTlsStream::NativeTls(tls) => { let tls_stream = tls.get_ref(); let tcp_stream = tls_stream.get_ref().get_ref(); @@ -113,7 +107,7 @@ impl Peer { inbound_handle, requests, peer_id, - ip_addr: addr.ip(), + socket_addr, })); Ok((peer, receiver)) @@ -123,8 +117,8 @@ impl Peer { self.0.peer_id } - pub fn ip_addr(&self) -> IpAddr { - self.0.ip_addr + pub fn socket_addr(&self) -> SocketAddr { + self.0.socket_addr } pub async fn send_transaction(&self, spend_bundle: SpendBundle) -> Result { From 98906bc9064f8a16e47e066c65305f006c34f9eb Mon Sep 17 00:00:00 2001 From: Rigidity Date: Wed, 24 Jul 2024 14:07:48 -0400 Subject: [PATCH 13/26] A bit of cleanup --- crates/chia-client/src/client.rs | 9 --------- crates/chia-client/src/peer.rs | 15 +++++++++++++-- 2 files changed, 13 insertions(+), 11 deletions(-) diff --git a/crates/chia-client/src/client.rs b/crates/chia-client/src/client.rs index 484b8eed8..47eb81839 100644 --- a/crates/chia-client/src/client.rs +++ b/crates/chia-client/src/client.rs @@ -28,33 +28,24 @@ pub struct Client(Arc); pub struct ClientOptions { /// The network to connect to. By default, this is mainnet. pub network: Network, - /// The type of service that this client represents. pub node_type: NodeType, - /// The capabilities that this client supports. pub capabilities: Vec<(u16, String)>, - /// The minimum protocol version that this client supports. /// If the protocol version of peers are lower than this, they will be disconnected. pub protocol_version: Version, - /// The software version of this client. /// This is not important for the handshake, but is sent to the peer for informational purposes. pub software_version: String, - /// The ideal number of peers that should be connected at any given time. pub target_peers: usize, - /// The maximum number of concurrent connections that can be initiated at once. pub connection_concurrency: usize, - /// How long to wait when trying to connect to a peer. pub connection_timeout: Duration, - /// How long to wait for a handshake response from a peer before disconnecting. pub handshake_timeout: Duration, - /// How long to wait for a response to a request for peers. pub request_peers_timeout: Duration, } diff --git a/crates/chia-client/src/peer.rs b/crates/chia-client/src/peer.rs index cbbf82363..bd45172cf 100644 --- a/crates/chia-client/src/peer.rs +++ b/crates/chia-client/src/peer.rs @@ -51,14 +51,17 @@ struct PeerInner { } impl Peer { + /// Connects to a peer using its IP address and port. pub async fn connect( socket_addr: SocketAddr, tls_connector: TlsConnector, ) -> Result<(Self, mpsc::Receiver)> { - Self::connect_addr(&format!("wss://{socket_addr}/ws"), tls_connector).await + Self::connect_full_uri(&format!("wss://{socket_addr}/ws"), tls_connector).await } - pub async fn connect_addr( + /// Connects to a peer using its full WebSocket URI. + /// For example, `wss://127.0.0.1:8444/ws`. + pub async fn connect_full_uri( uri: &str, tls_connector: TlsConnector, ) -> Result<(Self, mpsc::Receiver)> { @@ -72,6 +75,8 @@ impl Peer { Self::from_websocket(ws) } + /// Creates a peer from an existing WebSocket connection. + /// The connection must be secured with TLS, so that the certificate can be hashed in a peer id. pub fn from_websocket(ws: WebSocket) -> Result<(Self, mpsc::Receiver)> { let (socket_addr, cert) = match ws.get_ref() { MaybeTlsStream::NativeTls(tls) => { @@ -113,10 +118,12 @@ impl Peer { Ok((peer, receiver)) } + /// The hash of the TLS certificate used by the peer. pub fn peer_id(&self) -> PeerId { self.0.peer_id } + /// The IP address and port of the peer connection. pub fn socket_addr(&self) -> SocketAddr { self.0.socket_addr } @@ -207,6 +214,7 @@ impl Peer { self.request_infallible(RequestPeers::new()).await } + /// Sends a message to the peer, but does not expect any response. pub async fn send(&self, body: T) -> Result<()> where T: Streamable + ChiaProtocolMessage, @@ -220,6 +228,7 @@ impl Peer { Ok(()) } + /// Sends a message to the peer and expects a message that's either a response or a rejection. pub async fn request_fallible(&self, body: B) -> Result> where T: Streamable + ChiaProtocolMessage, @@ -240,6 +249,7 @@ impl Peer { } } + /// Sends a message to the peer and expects a specific response message. pub async fn request_infallible(&self, body: B) -> Result where T: Streamable + ChiaProtocolMessage, @@ -255,6 +265,7 @@ impl Peer { Ok(T::from_bytes(&message.data)?) } + /// Sends a message to the peer and expects any arbitrary protocol message without parsing it. pub async fn request_raw(&self, body: T) -> Result where T: Streamable + ChiaProtocolMessage, From 35824d6b9f128f9791887ff9e52a14cd962df744 Mon Sep 17 00:00:00 2001 From: Rigidity Date: Sat, 27 Jul 2024 12:16:55 -0400 Subject: [PATCH 14/26] Refactor --- crates/chia-client/examples/client.rs | 7 +- crates/chia-client/src/client.rs | 333 +++++++++++++++----------- 2 files changed, 204 insertions(+), 136 deletions(-) diff --git a/crates/chia-client/examples/client.rs b/crates/chia-client/examples/client.rs index 7123b0811..f05558119 100644 --- a/crates/chia-client/examples/client.rs +++ b/crates/chia-client/examples/client.rs @@ -39,7 +39,7 @@ async fn main() -> anyhow::Result<()> { let client2 = client.clone(); tokio::spawn(async move { loop { - client2.find_peers(true).await; + client2.discover_peers(true).await; sleep(Duration::from_secs(5)).await; } }); @@ -47,7 +47,10 @@ async fn main() -> anyhow::Result<()> { tokio::spawn(async move { loop { sleep(Duration::from_secs(5)).await; - log::info!("Currently connected to {} peers", client.len().await); + log::info!( + "Currently connected to {} peers", + client.lock().await.peers().len() + ); } }); diff --git a/crates/chia-client/src/client.rs b/crates/chia-client/src/client.rs index 47eb81839..69fb55fd5 100644 --- a/crates/chia-client/src/client.rs +++ b/crates/chia-client/src/client.rs @@ -1,8 +1,9 @@ use std::{ collections::{HashMap, HashSet}, net::{IpAddr, SocketAddr}, + ops::Deref, str::FromStr, - sync::Arc, + sync::{Arc, Weak}, time::Duration, }; @@ -14,16 +15,12 @@ use native_tls::TlsConnector; use rand::{seq::SliceRandom, thread_rng}; use semver::Version; use tokio::{ - sync::{mpsc, Mutex, Semaphore}, + sync::{mpsc, Mutex}, time::timeout, }; use crate::{Error, Event, Network, Peer, PeerId, Result}; -/// A client that can connect to many different peers on the network. -#[derive(Debug, Clone)] -pub struct Client(Arc); - #[derive(Debug, Clone)] pub struct ClientOptions { /// The network to connect to. By default, this is mainnet. @@ -50,13 +47,31 @@ pub struct ClientOptions { pub request_peers_timeout: Duration, } +#[derive(Debug, Clone)] +pub struct Client(Arc); + +impl Deref for Client { + type Target = Mutex; + + fn deref(&self) -> &Self::Target { + &self.0.state + } +} + #[derive(Debug)] struct ClientInner { - peers: Arc>>, - message_sender: Arc>>, + state: Arc>, options: ClientOptions, tls_connector: TlsConnector, - connection_lock: Semaphore, +} + +/// A client that can connect to many different peers on the network. +#[derive(Debug)] +pub struct ClientState { + peers: HashMap, + sender: mpsc::Sender, + banned_peers: HashSet, + trusted_peers: HashSet, } impl Client { @@ -66,78 +81,48 @@ impl Client { ) -> (Self, mpsc::Receiver) { let (sender, receiver) = mpsc::channel(32); + let state = ClientState { + peers: HashMap::new(), + sender, + banned_peers: HashSet::new(), + trusted_peers: HashSet::new(), + }; + let client = Self(Arc::new(ClientInner { - peers: Arc::new(Mutex::new(HashMap::new())), - message_sender: Arc::new(Mutex::new(sender)), + state: Arc::new(Mutex::new(state)), options, tls_connector, - connection_lock: Semaphore::new(1), })); (client, receiver) } - pub async fn len(&self) -> usize { - self.0.peers.lock().await.len() - } - - pub async fn is_empty(&self) -> bool { - self.0.peers.lock().await.is_empty() - } - - pub async fn peer_map(&self) -> HashMap { - self.0.peers.lock().await.clone() - } - - pub async fn peer(&self, peer_id: PeerId) -> Option { - self.0.peers.lock().await.get(&peer_id).cloned() - } - - pub async fn disconnect_peer(&self, peer_id: PeerId) { - self.0.peers.lock().await.remove(&peer_id); - } - - pub async fn disconnect_all(&self) { - self.0.peers.lock().await.clear(); - } - - pub async fn find_peers(&self, prefer_introducers: bool) { - let _permit = self - .0 - .connection_lock - .acquire() - .await - .expect("the semaphore should not be closed"); - - if self.len().await >= self.0.options.target_peers { + pub async fn discover_peers(&self, prefer_introducers: bool) { + if self.lock().await.peers.len() >= self.0.options.target_peers { return; } // If we don't have any peers, try to connect to DNS introducers. - if self.is_empty().await || prefer_introducers { - self.connect_dns().await; + if self.lock().await.peers.is_empty() || prefer_introducers { + self.discover_peers_with_dns().await; // If we still don't have any peers, we can't do anything. - if self.is_empty().await { + if self.lock().await.peers.is_empty() { return; } } - if self.len().await >= self.0.options.target_peers { + if self.lock().await.peers.len() >= self.0.options.target_peers { return; } - if self.is_empty().await { + if self.lock().await.peers.is_empty() { log::error!("No peers connected after DNS lookups"); return; } - let peer_lock = self.0.peers.lock().await; - let peers = peer_lock.clone(); - drop(peer_lock); - - for (peer_id, peer) in peers { - if self.len().await >= self.0.options.target_peers { + for (peer_id, peer) in self.lock().await.peers.clone() { + if self.lock().await.peers.len() >= self.0.options.target_peers { break; } @@ -146,7 +131,7 @@ impl Client { timeout(self.0.options.request_peers_timeout, peer.request_peers()).await else { log::info!("Failed to request peers from {}", peer.socket_addr()); - self.disconnect_peer(peer_id).await; + self.lock().await.disconnect_peer(&peer_id); continue; }; @@ -177,22 +162,33 @@ impl Client { break; } - self.connect_peers(next_peers).await; + self.connect_peers(&next_peers).await; } } } - async fn connect_dns(&self) { - log::info!("Requesting peers from DNS introducer"); + pub async fn discover_peers_with_dns(&self) -> HashMap { + let mut socket_addrs: Vec = self.dns_lookup().into_iter().collect(); + + // Shuffle the list of IPs so that we don't always connect to the same ones. + // This also prevents bias towards IPv4 or IPv6. + socket_addrs.as_mut_slice().shuffle(&mut thread_rng()); + + self.connect_peers_batched(&socket_addrs).await + } - let mut socket_addrs = Vec::new(); + pub fn dns_lookup(&self) -> HashSet { + let mut socket_addrs = HashSet::new(); for dns_introducer in &self.0.options.network.dns_introducers { + log::debug!("Performing DNS lookup of {dns_introducer}."); + // If a DNS introducer lookup fails, we just skip it. let Ok(result) = lookup_host(dns_introducer) else { log::warn!("Failed to lookup DNS introducer `{dns_introducer}`"); continue; }; + socket_addrs.extend( result .into_iter() @@ -200,15 +196,22 @@ impl Client { ); } - // Shuffle the list of IPs so that we don't always connect to the same ones. - // This also prevents bias towards IPv4 or IPv6. - socket_addrs.as_mut_slice().shuffle(&mut thread_rng()); + log::info!( + "Found a total of {} IPs from DNS introducers.", + socket_addrs.len() + ); + + socket_addrs + } - // Keep track of where we are in the peer list. + pub async fn connect_peers_batched( + &self, + socket_addrs: &[SocketAddr], + ) -> HashMap { + let mut peer_ids = HashMap::new(); let mut cursor = 0; - while self.len().await < self.0.options.target_peers { - // If we've reached the end of the list of IPs, stop early. + while self.lock().await.peers.len() < self.0.options.target_peers { if cursor >= socket_addrs.len() { break; } @@ -222,39 +225,52 @@ impl Client { // Increment the cursor by the number of peers we're trying to connect to. cursor += new_addrs.len(); - self.connect_peers(new_addrs.to_vec()).await; + peer_ids.extend(self.connect_peers(new_addrs).await); } + + peer_ids } - async fn connect_peers(&self, socket_addrs: Vec) { - // Add the connections and wait for them to complete. + pub async fn connect_peers(&self, socket_addrs: &[SocketAddr]) -> HashMap { let mut connections = FuturesUnordered::new(); - let peers = self.peer_map().await; + let state = self.lock().await; - for socket_addr in socket_addrs { - if peers + for &socket_addr in socket_addrs { + // Skip peers which we are already connected to. + if state + .peers .iter() .any(|(_, peer)| peer.socket_addr().ip() == socket_addr.ip()) { continue; } - connections.push(async move { (socket_addr, self.connect_peer(socket_addr).await) }); + // Add the next connection to the queue. + connections.push(async move { + let result = self.connect_peer(socket_addr).await; + (socket_addr, result) + }); } - while let Some((socket_addr, result)) = connections.next().await { - if self.len().await >= self.0.options.target_peers { - break; - } + // Prevent a deadlock and allow the connections to resolve. + drop(state); - if let Err(error) = result { - log::warn!("Failed to connect to peer {socket_addr} with error: {error}",); - continue; - } + let mut peer_ids = HashMap::new(); - log::info!("Connected to peer {socket_addr}"); + while let Some((socket_addr, result)) = connections.next().await { + match result { + Err(error) => { + log::warn!("Failed to connect to peer {socket_addr} with error: {error}",); + } + Ok(peer_id) => { + peer_ids.insert(socket_addr, peer_id); + log::info!("Connected to peer {socket_addr}"); + } + } } + + peer_ids } pub async fn connect_peer(&self, socket_addr: SocketAddr) -> Result { @@ -267,19 +283,17 @@ impl Client { .await .map_err(Error::ConnectionTimeout)??; - let options = &self.0.options; - peer.send(Handshake { - network_id: options.network.network_id.clone(), - protocol_version: options.protocol_version.to_string(), - software_version: options.software_version.clone(), + network_id: self.0.options.network.network_id.clone(), + protocol_version: self.0.options.protocol_version.to_string(), + software_version: self.0.options.software_version.clone(), server_port: 0, - node_type: options.node_type, - capabilities: options.capabilities.clone(), + node_type: self.0.options.node_type, + capabilities: self.0.options.capabilities.clone(), }) .await?; - let Some(message) = timeout(options.handshake_timeout, receiver.recv()) + let Some(message) = timeout(self.0.options.handshake_timeout, receiver.recv()) .await .map_err(Error::HandshakeTimeout)? else { @@ -292,7 +306,7 @@ impl Client { let handshake = Handshake::from_bytes(&message.data)?; - if handshake.network_id != options.network.network_id { + if handshake.network_id != self.0.options.network.network_id { return Err(Error::WrongNetworkId(handshake.network_id)); } @@ -300,64 +314,115 @@ impl Client { return Err(Error::InvalidProtocolVersion(handshake.protocol_version)); }; - if protocol_version < options.protocol_version { + if protocol_version < self.0.options.protocol_version { return Err(Error::OutdatedProtocolVersion( protocol_version, - options.protocol_version.clone(), + self.0.options.protocol_version.clone(), )); } - self.add_peer(peer, receiver).await + self.insert_peer(peer, receiver).await } - pub async fn add_peer( + pub async fn insert_peer( &self, peer: Peer, - mut receiver: mpsc::Receiver, + receiver: mpsc::Receiver, ) -> Result { - let socket_addr = peer.socket_addr(); - let peer_id = peer.peer_id(); + let mut state = self.lock().await; + state.peers.insert(peer.peer_id(), peer.clone()); + state.sender.send(Event::Connected(peer.peer_id())).await?; - self.0.peers.lock().await.insert(peer_id, peer); + // Spawn a task to propagate messages from the peer. + // We downgrade the client to avoid a cycle and allow it to be dropped. + tokio::spawn(handle_peer_connection( + Arc::downgrade(&self.0.state), + peer.peer_id(), + peer.socket_addr(), + receiver, + )); + + Ok(peer.peer_id()) + } +} - self.0 - .message_sender - .lock() - .await - .send(Event::Connected(peer_id)) - .await?; +impl ClientState { + pub fn peers(&self) -> &HashMap { + &self.peers + } - // Spawn a task to propagate messages from the peer. - let message_sender = self.0.message_sender.clone(); - let peer_map = self.0.peers.clone(); - - tokio::spawn(async move { - while let Some(message) = receiver.recv().await { - if let Err(error) = message_sender - .lock() - .await - .send(Event::Message(peer_id, message)) - .await - { - log::warn!("Failed to send client message event: {error}"); - break; - } - } + pub fn disconnect_peer(&mut self, peer_id: &PeerId) { + self.peers.remove(peer_id); + } - peer_map.lock().await.remove(&peer_id); + pub fn disconnect_all(&mut self) { + self.peers.clear(); + } - if let Err(error) = message_sender - .lock() - .await - .send(Event::Disconnected(socket_addr)) - .await - { - log::warn!("Failed to send client connection closed event: {error}"); - } + pub fn banned_peers(&self) -> &HashSet { + &self.banned_peers + } + + pub fn is_banned(&self, ip_addr: &IpAddr) -> bool { + self.banned_peers.contains(ip_addr) + } + + pub fn ban_peer(&mut self, ip_addr: IpAddr) { + self.banned_peers.insert(ip_addr); + } + + pub fn unban_peer(&mut self, ip_addr: &IpAddr) { + self.banned_peers.remove(ip_addr); + } + + pub fn trusted_peers(&self) -> &HashSet { + &self.trusted_peers + } + + pub fn is_trusted(&self, ip_addr: &IpAddr) -> bool { + self.trusted_peers.contains(ip_addr) + } + + pub fn trust_peer(&mut self, ip_addr: IpAddr) { + self.trusted_peers.insert(ip_addr); + } + + pub fn untrust_peer(&mut self, ip_addr: &IpAddr) { + self.trusted_peers.remove(ip_addr); + } +} + +async fn handle_peer_connection( + state: Weak>, + peer_id: PeerId, + socket_addr: SocketAddr, + mut receiver: mpsc::Receiver, +) { + while let Some(message) = receiver.recv().await { + // If the client has been dropped, we should gracefully end the task. + let Some(state) = state.upgrade() else { + return; + }; + let state = state.lock().await; + + // Close the connection if an error occurs. + if let Err(error) = state.sender.send(Event::Message(peer_id, message)).await { + log::warn!("Failed to send client message event: {error}"); + break; + } + } + + // If the client has been dropped, we should gracefully end the task. + let Some(state) = state.upgrade() else { + return; + }; + let mut state = state.lock().await; + + state.peers.remove(&peer_id); - log::info!("Peer {socket_addr} disconnected"); - }); + log::info!("Peer {socket_addr} disconnected"); - Ok(peer_id) + if let Err(error) = state.sender.send(Event::Disconnected(socket_addr)).await { + log::warn!("Failed to send client connection closed event: {error}"); } } From 9113e228663879c0f383e8a7a8784461d5621891 Mon Sep 17 00:00:00 2001 From: Rigidity Date: Sat, 27 Jul 2024 12:51:30 -0400 Subject: [PATCH 15/26] Improve banning --- crates/chia-client/src/client.rs | 151 +++++++++++++++++++++++-------- crates/chia-client/src/error.rs | 5 + crates/chia-client/src/event.rs | 5 +- 3 files changed, 120 insertions(+), 41 deletions(-) diff --git a/crates/chia-client/src/client.rs b/crates/chia-client/src/client.rs index 69fb55fd5..2a070a886 100644 --- a/crates/chia-client/src/client.rs +++ b/crates/chia-client/src/client.rs @@ -1,5 +1,6 @@ use std::{ collections::{HashMap, HashSet}, + future::Future, net::{IpAddr, SocketAddr}, ops::Deref, str::FromStr, @@ -70,8 +71,8 @@ struct ClientInner { pub struct ClientState { peers: HashMap, sender: mpsc::Sender, - banned_peers: HashSet, - trusted_peers: HashSet, + banned_ips: HashSet, + trusted_ips: HashSet, } impl Client { @@ -84,8 +85,8 @@ impl Client { let state = ClientState { peers: HashMap::new(), sender, - banned_peers: HashSet::new(), - trusted_peers: HashSet::new(), + banned_ips: HashSet::new(), + trusted_ips: HashSet::new(), }; let client = Self(Arc::new(ClientInner { @@ -112,26 +113,37 @@ impl Client { } } - if self.lock().await.peers.len() >= self.0.options.target_peers { + let state = self.lock().await; + + if state.peers.len() >= self.0.options.target_peers { return; } - if self.lock().await.peers.is_empty() { + if state.peers.is_empty() { log::error!("No peers connected after DNS lookups"); return; } - for (peer_id, peer) in self.lock().await.peers.clone() { + let peers: Vec = state.peers.values().cloned().collect(); + let trusted = state.trusted_ips.clone(); + + drop(state); + + for peer in peers { if self.lock().await.peers.len() >= self.0.options.target_peers { break; } // Request new peers from the peer. - let Ok(Ok(response)): std::result::Result, _> = - timeout(self.0.options.request_peers_timeout, peer.request_peers()).await + let Ok(Ok(response)): std::result::Result, _> = maybe_timeout( + trusted.contains(&peer.socket_addr().ip()), + self.0.options.request_peers_timeout, + peer.request_peers(), + ) + .await else { log::info!("Failed to request peers from {}", peer.socket_addr()); - self.lock().await.disconnect_peer(&peer_id); + self.lock().await.ban(peer.socket_addr().ip()).await; continue; }; @@ -261,7 +273,8 @@ impl Client { while let Some((socket_addr, result)) = connections.next().await { match result { Err(error) => { - log::warn!("Failed to connect to peer {socket_addr} with error: {error}",); + log::warn!("Failed to connect to peer {socket_addr} with error: {error}"); + self.lock().await.ban(socket_addr.ip()).await; } Ok(peer_id) => { peer_ids.insert(socket_addr, peer_id); @@ -274,14 +287,20 @@ impl Client { } pub async fn connect_peer(&self, socket_addr: SocketAddr) -> Result { + if self.lock().await.is_banned(&socket_addr.ip()) { + return Err(Error::BannedPeer(socket_addr.ip())); + } + log::debug!("Connecting to peer {socket_addr}"); - let (peer, mut receiver) = timeout( + let is_trusted = self.lock().await.is_trusted(&socket_addr.ip()); + + let (peer, mut receiver) = maybe_timeout( + is_trusted, self.0.options.connection_timeout, Peer::connect(socket_addr, self.0.tls_connector.clone()), ) - .await - .map_err(Error::ConnectionTimeout)??; + .await??; peer.send(Handshake { network_id: self.0.options.network.network_id.clone(), @@ -293,9 +312,12 @@ impl Client { }) .await?; - let Some(message) = timeout(self.0.options.handshake_timeout, receiver.recv()) - .await - .map_err(Error::HandshakeTimeout)? + let Some(message) = maybe_timeout( + is_trusted, + self.0.options.handshake_timeout, + receiver.recv(), + ) + .await? else { return Err(Error::ExpectedHandshake); }; @@ -330,8 +352,17 @@ impl Client { receiver: mpsc::Receiver, ) -> Result { let mut state = self.lock().await; + + let ip_addr = peer.socket_addr().ip(); + if state.is_banned(&ip_addr) { + return Err(Error::BannedPeer(ip_addr)); + } + state.peers.insert(peer.peer_id(), peer.clone()); - state.sender.send(Event::Connected(peer.peer_id())).await?; + state + .sender + .send(Event::Connected(peer.peer_id(), peer.socket_addr())) + .await?; // Spawn a task to propagate messages from the peer. // We downgrade the client to avoid a cycle and allow it to be dropped. @@ -351,44 +382,72 @@ impl ClientState { &self.peers } - pub fn disconnect_peer(&mut self, peer_id: &PeerId) { - self.peers.remove(peer_id); + pub async fn disconnect_peer(&mut self, peer_id: &PeerId) { + if let Some(peer) = self.peers.remove(peer_id) { + self.sender + .send(Event::Disconnected(peer.socket_addr())) + .await + .ok(); + log::info!("Peer {} disconnected", peer.socket_addr()); + } } pub fn disconnect_all(&mut self) { self.peers.clear(); } - pub fn banned_peers(&self) -> &HashSet { - &self.banned_peers + pub fn banned_ips(&self) -> &HashSet { + &self.banned_ips } pub fn is_banned(&self, ip_addr: &IpAddr) -> bool { - self.banned_peers.contains(ip_addr) + self.banned_ips.contains(ip_addr) } - pub fn ban_peer(&mut self, ip_addr: IpAddr) { - self.banned_peers.insert(ip_addr); + pub async fn ban(&mut self, ip_addr: IpAddr) { + if self.is_trusted(&ip_addr) { + log::warn!("Attempted to ban trusted peer {ip_addr}"); + return; + } + + log::info!("Banning peer {ip_addr}"); + + self.banned_ips.insert(ip_addr); + self.sender.send(Event::Banned(ip_addr)).await.ok(); + + let peer_ids: Vec = self + .peers + .keys() + .filter(|peer_id| self.peers[peer_id].socket_addr().ip() == ip_addr) + .copied() + .collect(); + + for peer_id in peer_ids { + self.disconnect_peer(&peer_id).await; + } } - pub fn unban_peer(&mut self, ip_addr: &IpAddr) { - self.banned_peers.remove(ip_addr); + pub fn unban(&mut self, ip_addr: &IpAddr) { + self.banned_ips.remove(ip_addr); } - pub fn trusted_peers(&self) -> &HashSet { - &self.trusted_peers + pub fn trusted_ips(&self) -> &HashSet { + &self.trusted_ips } pub fn is_trusted(&self, ip_addr: &IpAddr) -> bool { - self.trusted_peers.contains(ip_addr) + self.trusted_ips.contains(ip_addr) } - pub fn trust_peer(&mut self, ip_addr: IpAddr) { - self.trusted_peers.insert(ip_addr); + pub fn trust(&mut self, ip_addr: IpAddr) { + self.trusted_ips.insert(ip_addr); + if self.banned_ips.remove(&ip_addr) { + log::info!("Unbanning peer {ip_addr} since it's now trusted"); + } } - pub fn untrust_peer(&mut self, ip_addr: &IpAddr) { - self.trusted_peers.remove(ip_addr); + pub fn untrust(&mut self, ip_addr: &IpAddr) { + self.trusted_ips.remove(ip_addr); } } @@ -398,6 +457,8 @@ async fn handle_peer_connection( socket_addr: SocketAddr, mut receiver: mpsc::Receiver, ) { + let mut is_banned = false; + while let Some(message) = receiver.recv().await { // If the client has been dropped, we should gracefully end the task. let Some(state) = state.upgrade() else { @@ -408,6 +469,7 @@ async fn handle_peer_connection( // Close the connection if an error occurs. if let Err(error) = state.sender.send(Event::Message(peer_id, message)).await { log::warn!("Failed to send client message event: {error}"); + is_banned = true; break; } } @@ -418,11 +480,22 @@ async fn handle_peer_connection( }; let mut state = state.lock().await; - state.peers.remove(&peer_id); - - log::info!("Peer {socket_addr} disconnected"); + if is_banned { + state.ban(socket_addr.ip()).await; + } else { + state.disconnect_peer(&peer_id).await; + } +} - if let Err(error) = state.sender.send(Event::Disconnected(socket_addr)).await { - log::warn!("Failed to send client connection closed event: {error}"); +async fn maybe_timeout(is_trusted: bool, duration: Duration, future: F) -> Result +where + F: Future, +{ + if is_trusted { + Ok(future.await) + } else { + timeout(duration, future) + .await + .map_err(Error::ConnectionTimeout) } } diff --git a/crates/chia-client/src/error.rs b/crates/chia-client/src/error.rs index 82150c6a0..44382c6b8 100644 --- a/crates/chia-client/src/error.rs +++ b/crates/chia-client/src/error.rs @@ -1,3 +1,5 @@ +use std::net::IpAddr; + use chia_protocol::ProtocolMessageTypes; use semver::Version; use thiserror::Error; @@ -33,6 +35,9 @@ pub enum Error { #[error("TLS error: {0}")] Tls(#[from] native_tls::Error), + #[error("Banned peer: {0}")] + BannedPeer(IpAddr), + #[error("Unexpected message received with type {0:?}")] UnexpectedMessage(ProtocolMessageTypes), diff --git a/crates/chia-client/src/event.rs b/crates/chia-client/src/event.rs index c22d01719..056999e55 100644 --- a/crates/chia-client/src/event.rs +++ b/crates/chia-client/src/event.rs @@ -1,4 +1,4 @@ -use std::net::SocketAddr; +use std::net::{IpAddr, SocketAddr}; use chia_protocol::Message; @@ -7,6 +7,7 @@ use crate::PeerId; #[derive(Debug, Clone)] pub enum Event { Message(PeerId, Message), - Connected(PeerId), + Connected(PeerId, SocketAddr), Disconnected(SocketAddr), + Banned(IpAddr), } From cb7fff308db1991ef94d4867811c33894124b5f7 Mon Sep 17 00:00:00 2001 From: Rigidity Date: Sat, 27 Jul 2024 15:57:40 -0500 Subject: [PATCH 16/26] Temp --- Cargo.lock | 582 +++++++++++++++++++++++++- Cargo.toml | 1 + crates/chia-client/Cargo.toml | 1 + crates/chia-client/examples/client.rs | 48 ++- crates/chia-client/src/client.rs | 116 ++--- 5 files changed, 664 insertions(+), 84 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 841ee5d55..46088c1f0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -153,12 +153,90 @@ dependencies = [ "syn 2.0.72", ] +[[package]] +name = "async-stream" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd56dd203fef61ac097dd65721a419ddccb106b2d2b70ba60a6b529f03961a51" +dependencies = [ + "async-stream-impl", + "futures-core", + "pin-project-lite", +] + +[[package]] +name = "async-stream-impl" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.72", +] + +[[package]] +name = "async-trait" +version = "0.1.81" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e0c28dcc82d7c8ead5cb13beb15405b57b8546e93215673ff8ca0349a028107" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.72", +] + [[package]] name = "autocfg" version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" +[[package]] +name = "axum" +version = "0.6.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b829e4e32b91e643de6eafe82b1d90675f5874230191a4ffbc1b336dec4d6bf" +dependencies = [ + "async-trait", + "axum-core", + "bitflags 1.3.2", + "bytes", + "futures-util", + "http 0.2.12", + "http-body", + "hyper", + "itoa", + "matchit", + "memchr", + "mime", + "percent-encoding", + "pin-project-lite", + "rustversion", + "serde", + "sync_wrapper", + "tower", + "tower-layer", + "tower-service", +] + +[[package]] +name = "axum-core" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "759fa577a247914fd3f7f76d62972792636412fbfd634cd452f6a385a74d2d2c" +dependencies = [ + "async-trait", + "bytes", + "futures-util", + "http 0.2.12", + "http-body", + "mime", + "rustversion", + "tower-layer", + "tower-service", +] + [[package]] name = "backtrace" version = "0.3.73" @@ -180,6 +258,12 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" +[[package]] +name = "base64" +version = "0.21.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" + [[package]] name = "base64" version = "0.22.1" @@ -192,6 +276,12 @@ version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + [[package]] name = "bitflags" version = "2.6.0" @@ -339,6 +429,7 @@ dependencies = [ "chia-protocol", "chia-ssl", "chia-traits 0.10.0", + "console-subscriber", "dns-lookup", "env_logger", "futures-util", @@ -704,6 +795,44 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d3fd119d74b830634cea2a0f58bbd0d54540518a14397557951e79340abc28c0" +[[package]] +name = "console-api" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a257c22cd7e487dd4a13d413beabc512c5052f0bc048db0da6a84c3d8a6142fd" +dependencies = [ + "futures-core", + "prost", + "prost-types", + "tonic", + "tracing-core", +] + +[[package]] +name = "console-subscriber" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31c4cc54bae66f7d9188996404abdf7fdfa23034ef8e43478c8810828abad758" +dependencies = [ + "console-api", + "crossbeam-channel", + "crossbeam-utils", + "futures-task", + "hdrhistogram", + "humantime", + "prost", + "prost-types", + "serde", + "serde_json", + "thread_local", + "tokio", + "tokio-stream", + "tonic", + "tracing", + "tracing-core", + "tracing-subscriber", +] + [[package]] name = "const-oid" version = "0.9.6" @@ -735,6 +864,15 @@ dependencies = [ "libc", ] +[[package]] +name = "crc32fast" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3" +dependencies = [ + "cfg-if", +] + [[package]] name = "criterion" version = "0.5.1" @@ -747,7 +885,7 @@ dependencies = [ "clap", "criterion-plot", "is-terminal", - "itertools", + "itertools 0.10.5", "num-traits", "once_cell", "oorandom", @@ -768,7 +906,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6b50826342786a51a89e2da3a28f1c32b06e387201bc2d19791f622c673706b1" dependencies = [ "cast", - "itertools", + "itertools 0.10.5", ] [[package]] @@ -1026,6 +1164,16 @@ dependencies = [ "subtle", ] +[[package]] +name = "flate2" +version = "1.0.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f211bbe8e69bbd0cfdea405084f128ae8b4aaa6b0b522fc8f2b009084797920" +dependencies = [ + "crc32fast", + "miniz_oxide", +] + [[package]] name = "fnv" version = "1.0.7" @@ -1205,6 +1353,25 @@ dependencies = [ "subtle", ] +[[package]] +name = "h2" +version = "0.3.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81fe527a889e1532da5c525686d96d4c2e74cdd345badf8dfef9f6b39dd5f5e8" +dependencies = [ + "bytes", + "fnv", + "futures-core", + "futures-sink", + "futures-util", + "http 0.2.12", + "indexmap 2.3.0", + "slab", + "tokio", + "tokio-util", + "tracing", +] + [[package]] name = "half" version = "2.4.1" @@ -1215,6 +1382,12 @@ dependencies = [ "crunchy", ] +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" + [[package]] name = "hashbrown" version = "0.14.5" @@ -1231,7 +1404,20 @@ version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6ba4ff7128dee98c7dc9794b6a411377e1404dba1c97deb8d1a55297bd25d8af" dependencies = [ - "hashbrown", + "hashbrown 0.14.5", +] + +[[package]] +name = "hdrhistogram" +version = "7.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "765c9198f173dd59ce26ff9f95ef0aafd0a0fe01fb9d72841bc5066a4c06511d" +dependencies = [ + "base64 0.21.7", + "byteorder", + "flate2", + "nom", + "num-traits", ] [[package]] @@ -1282,6 +1468,17 @@ dependencies = [ "digest", ] +[[package]] +name = "http" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "601cbb57e577e2f5ef5be8e7b83f0f63994f25aa94d673e54a92d5c516d101f1" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + [[package]] name = "http" version = "1.1.0" @@ -1293,18 +1490,71 @@ dependencies = [ "itoa", ] +[[package]] +name = "http-body" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2" +dependencies = [ + "bytes", + "http 0.2.12", + "pin-project-lite", +] + [[package]] name = "httparse" version = "1.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fcc0b4a115bf80b728eb8ea024ad5bd707b615bfed49e0665b6e0f86fd082d9" +[[package]] +name = "httpdate" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" + [[package]] name = "humantime" version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" +[[package]] +name = "hyper" +version = "0.14.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a152ddd61dfaec7273fe8419ab357f33aee0d914c5f4efbf0d96fa749eea5ec9" +dependencies = [ + "bytes", + "futures-channel", + "futures-core", + "futures-util", + "h2", + "http 0.2.12", + "http-body", + "httparse", + "httpdate", + "itoa", + "pin-project-lite", + "socket2", + "tokio", + "tower-service", + "tracing", + "want", +] + +[[package]] +name = "hyper-timeout" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbb958482e8c7be4bc3cf272a766a2b0bf1a6755e7a6ae777f017a31d11b13b1" +dependencies = [ + "hyper", + "pin-project-lite", + "tokio", + "tokio-io-timeout", +] + [[package]] name = "idna" version = "0.5.0" @@ -1315,6 +1565,16 @@ dependencies = [ "unicode-normalization", ] +[[package]] +name = "indexmap" +version = "1.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" +dependencies = [ + "autocfg", + "hashbrown 0.12.3", +] + [[package]] name = "indexmap" version = "2.3.0" @@ -1322,7 +1582,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "de3fc2e30ba82dd1b3911c8de1ffc143c74a914a14e99514d7637e3099df5ea0" dependencies = [ "equivalent", - "hashbrown", + "hashbrown 0.14.5", ] [[package]] @@ -1363,6 +1623,15 @@ dependencies = [ "either", ] +[[package]] +name = "itertools" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569" +dependencies = [ + "either", +] + [[package]] name = "itoa" version = "1.0.11" @@ -1482,9 +1751,24 @@ version = "0.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "37ee39891760e7d94734f6f63fedc29a2e4a152f836120753a72503f09fcf904" dependencies = [ - "hashbrown", + "hashbrown 0.14.5", +] + +[[package]] +name = "matchers" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558" +dependencies = [ + "regex-automata 0.1.10", ] +[[package]] +name = "matchit" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e7465ac9959cc2b1404e8e2367b43684a6d13790fe23056cc8c6c5a6b7bcb94" + [[package]] name = "memchr" version = "2.7.4" @@ -1500,6 +1784,12 @@ dependencies = [ "autocfg", ] +[[package]] +name = "mime" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" + [[package]] name = "minimal-lexical" version = "0.2.1" @@ -1663,7 +1953,7 @@ version = "0.10.66" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9529f4786b70a3e8c61e11179af17ab6188ad8d0ded78c5529441ed39d4bd9c1" dependencies = [ - "bitflags", + "bitflags 2.6.0", "cfg-if", "foreign-types", "libc", @@ -1752,7 +2042,7 @@ version = "3.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e459365e590736a54c3fa561947c84837534b8e9af6fc5bf781307e82658fae" dependencies = [ - "base64", + "base64 0.22.1", "serde", ] @@ -1771,6 +2061,26 @@ version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" +[[package]] +name = "pin-project" +version = "1.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6bf43b791c5b9e34c3d182969b4abb522f9343702850a2e57f460d00d09b4b3" +dependencies = [ + "pin-project-internal", +] + +[[package]] +name = "pin-project-internal" +version = "1.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.72", +] + [[package]] name = "pin-project-lite" version = "0.2.14" @@ -1887,6 +2197,38 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "prost" +version = "0.12.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "deb1435c188b76130da55f17a466d252ff7b1418b2ad3e037d127b94e3411f29" +dependencies = [ + "bytes", + "prost-derive", +] + +[[package]] +name = "prost-derive" +version = "0.12.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81bddcdb20abf9501610992b6759a4c888aef7d1a7247ef75e2404275ac24af1" +dependencies = [ + "anyhow", + "itertools 0.12.1", + "proc-macro2", + "quote", + "syn 2.0.72", +] + +[[package]] +name = "prost-types" +version = "0.12.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9091c90b0a32608e984ff2fa4091273cbdd755d54935c51d520887f4a1dbd5b0" +dependencies = [ + "prost", +] + [[package]] name = "pyo3" version = "0.21.2" @@ -2031,7 +2373,7 @@ version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2a908a6e00f1fdd0dfd9c0eb08ce85126f6d8bbda50017e74bc4a4b7d4a926a4" dependencies = [ - "bitflags", + "bitflags 2.6.0", ] [[package]] @@ -2042,8 +2384,17 @@ checksum = "4219d74c6b67a3654a9fbebc4b419e22126d13d2f3c4a07ee0cb61ff79a79619" dependencies = [ "aho-corasick", "memchr", - "regex-automata", - "regex-syntax", + "regex-automata 0.4.7", + "regex-syntax 0.8.4", +] + +[[package]] +name = "regex-automata" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" +dependencies = [ + "regex-syntax 0.6.29", ] [[package]] @@ -2054,9 +2405,15 @@ checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df" dependencies = [ "aho-corasick", "memchr", - "regex-syntax", + "regex-syntax 0.8.4", ] +[[package]] +name = "regex-syntax" +version = "0.6.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" + [[package]] name = "regex-syntax" version = "0.8.4" @@ -2140,7 +2497,7 @@ version = "0.31.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b838eba278d213a8beaf485bd313fd580ca4505a00d5871caeb1457c55322cae" dependencies = [ - "bitflags", + "bitflags 2.6.0", "fallible-iterator", "fallible-streaming-iterator", "hashlink", @@ -2178,7 +2535,7 @@ version = "0.38.34" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f" dependencies = [ - "bitflags", + "bitflags 2.6.0", "errno", "libc", "linux-raw-sys", @@ -2191,6 +2548,12 @@ version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "976295e77ce332211c0d24d92c0e83e50f5c5f046d11082cea19f3df13a3562d" +[[package]] +name = "rustversion" +version = "1.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "955d28af4278de8121b7ebeb796b6a45735dc01436d898801014aced2773a3d6" + [[package]] name = "ryu" version = "1.0.18" @@ -2241,7 +2604,7 @@ version = "2.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" dependencies = [ - "bitflags", + "bitflags 2.6.0", "core-foundation", "core-foundation-sys", "libc", @@ -2318,6 +2681,15 @@ dependencies = [ "digest", ] +[[package]] +name = "sharded-slab" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" +dependencies = [ + "lazy_static", +] + [[package]] name = "signal-hook-registry" version = "1.4.2" @@ -2412,6 +2784,12 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "sync_wrapper" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" + [[package]] name = "synstructure" version = "0.13.1" @@ -2482,6 +2860,16 @@ dependencies = [ "syn 2.0.72", ] +[[package]] +name = "thread_local" +version = "1.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c" +dependencies = [ + "cfg-if", + "once_cell", +] + [[package]] name = "threadpool" version = "1.8.1" @@ -2562,9 +2950,20 @@ dependencies = [ "signal-hook-registry", "socket2", "tokio-macros", + "tracing", "windows-sys 0.52.0", ] +[[package]] +name = "tokio-io-timeout" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30b74022ada614a1b4834de765f9bb43877f910cc8ce4be40e89042c9223a8bf" +dependencies = [ + "pin-project-lite", + "tokio", +] + [[package]] name = "tokio-macros" version = "2.4.0" @@ -2586,6 +2985,17 @@ dependencies = [ "tokio", ] +[[package]] +name = "tokio-stream" +version = "0.1.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "267ac89e0bec6e691e5813911606935d77c476ff49024f98abcea3e7b15e37af" +dependencies = [ + "futures-core", + "pin-project-lite", + "tokio", +] + [[package]] name = "tokio-tungstenite" version = "0.21.0" @@ -2600,6 +3010,19 @@ dependencies = [ "tungstenite", ] +[[package]] +name = "tokio-util" +version = "0.7.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9cf6b47b3771c49ac75ad09a6162f53ad4b8088b76ac60e8ec1455b31a189fe1" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "pin-project-lite", + "tokio", +] + [[package]] name = "toml_datetime" version = "0.6.8" @@ -2612,11 +3035,123 @@ version = "0.19.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" dependencies = [ - "indexmap", + "indexmap 2.3.0", "toml_datetime", "winnow", ] +[[package]] +name = "tonic" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76c4eb7a4e9ef9d4763600161f12f5070b92a578e1b634db88a6887844c91a13" +dependencies = [ + "async-stream", + "async-trait", + "axum", + "base64 0.21.7", + "bytes", + "h2", + "http 0.2.12", + "http-body", + "hyper", + "hyper-timeout", + "percent-encoding", + "pin-project", + "prost", + "tokio", + "tokio-stream", + "tower", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "tower" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c" +dependencies = [ + "futures-core", + "futures-util", + "indexmap 1.9.3", + "pin-project", + "pin-project-lite", + "rand", + "slab", + "tokio", + "tokio-util", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "tower-layer" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c20c8dbed6283a09604c3e69b4b7eeb54e298b8a600d4d5ecb5ad39de609f1d0" + +[[package]] +name = "tower-service" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" + +[[package]] +name = "tracing" +version = "0.1.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" +dependencies = [ + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.72", +] + +[[package]] +name = "tracing-core" +version = "0.1.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" +dependencies = [ + "once_cell", + "valuable", +] + +[[package]] +name = "tracing-subscriber" +version = "0.3.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b" +dependencies = [ + "matchers", + "once_cell", + "regex", + "sharded-slab", + "thread_local", + "tracing", + "tracing-core", +] + +[[package]] +name = "try-lock" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" + [[package]] name = "tungstenite" version = "0.21.0" @@ -2626,7 +3161,7 @@ dependencies = [ "byteorder", "bytes", "data-encoding", - "http", + "http 1.1.0", "httparse", "log", "native-tls", @@ -2705,6 +3240,12 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" +[[package]] +name = "valuable" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" + [[package]] name = "vcpkg" version = "0.2.15" @@ -2727,6 +3268,15 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "want" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" +dependencies = [ + "try-lock", +] + [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" diff --git a/Cargo.toml b/Cargo.toml index 0ee3c9dc2..616d1697e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -143,3 +143,4 @@ native-tls = "0.2.12" dns-lookup = "2.0.4" semver = "1.0.23" env_logger = "0.11.3" +console-subscriber = "0.3.0" diff --git a/crates/chia-client/Cargo.toml b/crates/chia-client/Cargo.toml index 99ef3f6f4..d5fa7c428 100644 --- a/crates/chia-client/Cargo.toml +++ b/crates/chia-client/Cargo.toml @@ -27,6 +27,7 @@ hex-literal = { workspace = true } rand = { workspace = true } semver = { workspace = true } hex = { workspace = true } +console-subscriber = { workspace = true } [dev-dependencies] chia-ssl = { path = "../chia-ssl" } diff --git a/crates/chia-client/examples/client.rs b/crates/chia-client/examples/client.rs index f05558119..1d1c265cb 100644 --- a/crates/chia-client/examples/client.rs +++ b/crates/chia-client/examples/client.rs @@ -1,13 +1,15 @@ -use std::time::Duration; +use std::{sync::Arc, time::Duration}; -use chia_client::{create_tls_connector, Client, ClientOptions, Network}; -use chia_protocol::NodeType; +use chia_client::{create_tls_connector, Client, ClientOptions, Event, Network}; +use chia_protocol::{NewPeakWallet, NodeType, ProtocolMessageTypes}; use chia_ssl::ChiaCertificate; -use tokio::time::sleep; +use chia_traits::Streamable; +use tokio::{sync::Mutex, time::sleep}; #[tokio::main] async fn main() -> anyhow::Result<()> { env_logger::init(); + // console_subscriber::init(); log::info!("Generating certificate"); let cert = ChiaCertificate::generate()?; @@ -18,8 +20,8 @@ async fn main() -> anyhow::Result<()> { tls_connector, ClientOptions { network: Network::mainnet(), - target_peers: 20, - connection_concurrency: 10, + target_peers: 500, + connection_concurrency: 200, node_type: NodeType::Wallet, capabilities: vec![ (1, "1".to_string()), @@ -36,25 +38,47 @@ async fn main() -> anyhow::Result<()> { log::info!("Connecting to DNS introducers"); - let client2 = client.clone(); + let clone = client.clone(); tokio::spawn(async move { loop { - client2.discover_peers(true).await; + clone.discover_peers(true).await; sleep(Duration::from_secs(5)).await; } }); + let height = Arc::new(Mutex::new(0)); + let height_clone = height.clone(); + tokio::spawn(async move { loop { - sleep(Duration::from_secs(5)).await; + sleep(Duration::from_secs(1)).await; log::info!( - "Currently connected to {} peers", - client.lock().await.peers().len() + "Currently connected to {} peers, with peak height {}", + client.lock().await.peers().len(), + *height_clone.lock().await ); } }); - while let Some(_event) = receiver.recv().await {} + while let Some(event) = receiver.recv().await { + let Event::Message(_peer_id, message) = event else { + continue; + }; + + if message.msg_type != ProtocolMessageTypes::NewPeakWallet { + continue; + } + + let Ok(new_peak) = NewPeakWallet::from_bytes(&message.data) else { + continue; + }; + + let mut height = height.lock().await; + + if new_peak.height > *height { + *height = new_peak.height; + } + } Ok(()) } diff --git a/crates/chia-client/src/client.rs b/crates/chia-client/src/client.rs index 2a070a886..09dc7b2fb 100644 --- a/crates/chia-client/src/client.rs +++ b/crates/chia-client/src/client.rs @@ -111,6 +111,7 @@ impl Client { if self.lock().await.peers.is_empty() { return; } + } else { } let state = self.lock().await; @@ -143,7 +144,9 @@ impl Client { .await else { log::info!("Failed to request peers from {}", peer.socket_addr()); - self.lock().await.ban(peer.socket_addr().ip()).await; + + self.lock().await.ban(peer.socket_addr().ip()); + continue; }; @@ -179,14 +182,14 @@ impl Client { } } - pub async fn discover_peers_with_dns(&self) -> HashMap { + pub async fn discover_peers_with_dns(&self) { let mut socket_addrs: Vec = self.dns_lookup().into_iter().collect(); // Shuffle the list of IPs so that we don't always connect to the same ones. // This also prevents bias towards IPv4 or IPv6. socket_addrs.as_mut_slice().shuffle(&mut thread_rng()); - self.connect_peers_batched(&socket_addrs).await + self.connect_peers_batched(&socket_addrs).await; } pub fn dns_lookup(&self) -> HashSet { @@ -216,41 +219,40 @@ impl Client { socket_addrs } - pub async fn connect_peers_batched( - &self, - socket_addrs: &[SocketAddr], - ) -> HashMap { - let mut peer_ids = HashMap::new(); + async fn connect_peers_batched(&self, socket_addrs: &[SocketAddr]) { let mut cursor = 0; - while self.lock().await.peers.len() < self.0.options.target_peers { - if cursor >= socket_addrs.len() { - break; - } + loop { + if self.lock().await.peers.len() < self.0.options.target_peers { + if cursor >= socket_addrs.len() { + break; + } - // Get the remaining peers we can connect to, up to the concurrency limit. - let new_addrs = &socket_addrs[cursor - ..socket_addrs - .len() - .min(cursor + self.0.options.connection_concurrency)]; + // Get the remaining peers we can connect to, up to the concurrency limit. + let new_addrs = &socket_addrs[cursor + ..socket_addrs + .len() + .min(cursor + self.0.options.connection_concurrency)]; - // Increment the cursor by the number of peers we're trying to connect to. - cursor += new_addrs.len(); + // Increment the cursor by the number of peers we're trying to connect to. + cursor += new_addrs.len(); - peer_ids.extend(self.connect_peers(new_addrs).await); + self.connect_peers(new_addrs).await; + } else { + break; + } } - - peer_ids } - pub async fn connect_peers(&self, socket_addrs: &[SocketAddr]) -> HashMap { + async fn connect_peers(&self, socket_addrs: &[SocketAddr]) { let mut connections = FuturesUnordered::new(); - let state = self.lock().await; - for &socket_addr in socket_addrs { // Skip peers which we are already connected to. - if state + + if self + .lock() + .await .peers .iter() .any(|(_, peer)| peer.socket_addr().ip() == socket_addr.ip()) @@ -265,28 +267,29 @@ impl Client { }); } - // Prevent a deadlock and allow the connections to resolve. - drop(state); - - let mut peer_ids = HashMap::new(); - while let Some((socket_addr, result)) = connections.next().await { match result { Err(error) => { - log::warn!("Failed to connect to peer {socket_addr} with error: {error}"); - self.lock().await.ban(socket_addr.ip()).await; + log::debug!("Failed to connect to peer {socket_addr} with error: {error}"); + + self.lock().await.ban(socket_addr.ip()); } - Ok(peer_id) => { - peer_ids.insert(socket_addr, peer_id); + Ok((peer, receiver)) => { + if self.lock().await.peers.len() >= self.0.options.target_peers { + break; + } + + self.insert_peer(peer, receiver).await.ok(); log::info!("Connected to peer {socket_addr}"); } } } - - peer_ids } - pub async fn connect_peer(&self, socket_addr: SocketAddr) -> Result { + async fn connect_peer( + &self, + socket_addr: SocketAddr, + ) -> Result<(Peer, mpsc::Receiver)> { if self.lock().await.is_banned(&socket_addr.ip()) { return Err(Error::BannedPeer(socket_addr.ip())); } @@ -343,14 +346,10 @@ impl Client { )); } - self.insert_peer(peer, receiver).await + Ok((peer, receiver)) } - pub async fn insert_peer( - &self, - peer: Peer, - receiver: mpsc::Receiver, - ) -> Result { + async fn insert_peer(&self, peer: Peer, receiver: mpsc::Receiver) -> Result<()> { let mut state = self.lock().await; let ip_addr = peer.socket_addr().ip(); @@ -373,7 +372,7 @@ impl Client { receiver, )); - Ok(peer.peer_id()) + Ok(()) } } @@ -382,12 +381,12 @@ impl ClientState { &self.peers } - pub async fn disconnect_peer(&mut self, peer_id: &PeerId) { + pub fn disconnect_peer(&mut self, peer_id: &PeerId) { if let Some(peer) = self.peers.remove(peer_id) { - self.sender - .send(Event::Disconnected(peer.socket_addr())) - .await - .ok(); + // self.sender + // .send(Event::Disconnected(peer.socket_addr())) + // .await + // .ok(); log::info!("Peer {} disconnected", peer.socket_addr()); } } @@ -404,16 +403,19 @@ impl ClientState { self.banned_ips.contains(ip_addr) } - pub async fn ban(&mut self, ip_addr: IpAddr) { + pub fn ban(&mut self, ip_addr: IpAddr) { if self.is_trusted(&ip_addr) { log::warn!("Attempted to ban trusted peer {ip_addr}"); return; } + if !self.banned_ips.insert(ip_addr) { + return; + } + log::info!("Banning peer {ip_addr}"); - self.banned_ips.insert(ip_addr); - self.sender.send(Event::Banned(ip_addr)).await.ok(); + // self.sender.send(Event::Banned(ip_addr)).await.ok(); let peer_ids: Vec = self .peers @@ -423,7 +425,7 @@ impl ClientState { .collect(); for peer_id in peer_ids { - self.disconnect_peer(&peer_id).await; + self.disconnect_peer(&peer_id); } } @@ -464,6 +466,7 @@ async fn handle_peer_connection( let Some(state) = state.upgrade() else { return; }; + let state = state.lock().await; // Close the connection if an error occurs. @@ -478,12 +481,13 @@ async fn handle_peer_connection( let Some(state) = state.upgrade() else { return; }; + let mut state = state.lock().await; if is_banned { - state.ban(socket_addr.ip()).await; + state.ban(socket_addr.ip()); } else { - state.disconnect_peer(&peer_id).await; + state.disconnect_peer(&peer_id); } } From b53943a16f4bb0a26539c93afa8b88a8b8683e1b Mon Sep 17 00:00:00 2001 From: Rigidity Date: Tue, 30 Jul 2024 13:00:43 -0500 Subject: [PATCH 17/26] Temp --- Cargo.lock | 582 +------------------------- crates/chia-client/Cargo.toml | 1 - crates/chia-client/examples/client.rs | 11 +- crates/chia-client/src/client.rs | 149 ++++--- 4 files changed, 101 insertions(+), 642 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 46088c1f0..841ee5d55 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -153,90 +153,12 @@ dependencies = [ "syn 2.0.72", ] -[[package]] -name = "async-stream" -version = "0.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd56dd203fef61ac097dd65721a419ddccb106b2d2b70ba60a6b529f03961a51" -dependencies = [ - "async-stream-impl", - "futures-core", - "pin-project-lite", -] - -[[package]] -name = "async-stream-impl" -version = "0.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.72", -] - -[[package]] -name = "async-trait" -version = "0.1.81" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e0c28dcc82d7c8ead5cb13beb15405b57b8546e93215673ff8ca0349a028107" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.72", -] - [[package]] name = "autocfg" version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" -[[package]] -name = "axum" -version = "0.6.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b829e4e32b91e643de6eafe82b1d90675f5874230191a4ffbc1b336dec4d6bf" -dependencies = [ - "async-trait", - "axum-core", - "bitflags 1.3.2", - "bytes", - "futures-util", - "http 0.2.12", - "http-body", - "hyper", - "itoa", - "matchit", - "memchr", - "mime", - "percent-encoding", - "pin-project-lite", - "rustversion", - "serde", - "sync_wrapper", - "tower", - "tower-layer", - "tower-service", -] - -[[package]] -name = "axum-core" -version = "0.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "759fa577a247914fd3f7f76d62972792636412fbfd634cd452f6a385a74d2d2c" -dependencies = [ - "async-trait", - "bytes", - "futures-util", - "http 0.2.12", - "http-body", - "mime", - "rustversion", - "tower-layer", - "tower-service", -] - [[package]] name = "backtrace" version = "0.3.73" @@ -258,12 +180,6 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" -[[package]] -name = "base64" -version = "0.21.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" - [[package]] name = "base64" version = "0.22.1" @@ -276,12 +192,6 @@ version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" -[[package]] -name = "bitflags" -version = "1.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" - [[package]] name = "bitflags" version = "2.6.0" @@ -429,7 +339,6 @@ dependencies = [ "chia-protocol", "chia-ssl", "chia-traits 0.10.0", - "console-subscriber", "dns-lookup", "env_logger", "futures-util", @@ -795,44 +704,6 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d3fd119d74b830634cea2a0f58bbd0d54540518a14397557951e79340abc28c0" -[[package]] -name = "console-api" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a257c22cd7e487dd4a13d413beabc512c5052f0bc048db0da6a84c3d8a6142fd" -dependencies = [ - "futures-core", - "prost", - "prost-types", - "tonic", - "tracing-core", -] - -[[package]] -name = "console-subscriber" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31c4cc54bae66f7d9188996404abdf7fdfa23034ef8e43478c8810828abad758" -dependencies = [ - "console-api", - "crossbeam-channel", - "crossbeam-utils", - "futures-task", - "hdrhistogram", - "humantime", - "prost", - "prost-types", - "serde", - "serde_json", - "thread_local", - "tokio", - "tokio-stream", - "tonic", - "tracing", - "tracing-core", - "tracing-subscriber", -] - [[package]] name = "const-oid" version = "0.9.6" @@ -864,15 +735,6 @@ dependencies = [ "libc", ] -[[package]] -name = "crc32fast" -version = "1.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3" -dependencies = [ - "cfg-if", -] - [[package]] name = "criterion" version = "0.5.1" @@ -885,7 +747,7 @@ dependencies = [ "clap", "criterion-plot", "is-terminal", - "itertools 0.10.5", + "itertools", "num-traits", "once_cell", "oorandom", @@ -906,7 +768,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6b50826342786a51a89e2da3a28f1c32b06e387201bc2d19791f622c673706b1" dependencies = [ "cast", - "itertools 0.10.5", + "itertools", ] [[package]] @@ -1164,16 +1026,6 @@ dependencies = [ "subtle", ] -[[package]] -name = "flate2" -version = "1.0.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f211bbe8e69bbd0cfdea405084f128ae8b4aaa6b0b522fc8f2b009084797920" -dependencies = [ - "crc32fast", - "miniz_oxide", -] - [[package]] name = "fnv" version = "1.0.7" @@ -1353,25 +1205,6 @@ dependencies = [ "subtle", ] -[[package]] -name = "h2" -version = "0.3.26" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81fe527a889e1532da5c525686d96d4c2e74cdd345badf8dfef9f6b39dd5f5e8" -dependencies = [ - "bytes", - "fnv", - "futures-core", - "futures-sink", - "futures-util", - "http 0.2.12", - "indexmap 2.3.0", - "slab", - "tokio", - "tokio-util", - "tracing", -] - [[package]] name = "half" version = "2.4.1" @@ -1382,12 +1215,6 @@ dependencies = [ "crunchy", ] -[[package]] -name = "hashbrown" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" - [[package]] name = "hashbrown" version = "0.14.5" @@ -1404,20 +1231,7 @@ version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6ba4ff7128dee98c7dc9794b6a411377e1404dba1c97deb8d1a55297bd25d8af" dependencies = [ - "hashbrown 0.14.5", -] - -[[package]] -name = "hdrhistogram" -version = "7.5.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "765c9198f173dd59ce26ff9f95ef0aafd0a0fe01fb9d72841bc5066a4c06511d" -dependencies = [ - "base64 0.21.7", - "byteorder", - "flate2", - "nom", - "num-traits", + "hashbrown", ] [[package]] @@ -1468,17 +1282,6 @@ dependencies = [ "digest", ] -[[package]] -name = "http" -version = "0.2.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "601cbb57e577e2f5ef5be8e7b83f0f63994f25aa94d673e54a92d5c516d101f1" -dependencies = [ - "bytes", - "fnv", - "itoa", -] - [[package]] name = "http" version = "1.1.0" @@ -1490,71 +1293,18 @@ dependencies = [ "itoa", ] -[[package]] -name = "http-body" -version = "0.4.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2" -dependencies = [ - "bytes", - "http 0.2.12", - "pin-project-lite", -] - [[package]] name = "httparse" version = "1.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fcc0b4a115bf80b728eb8ea024ad5bd707b615bfed49e0665b6e0f86fd082d9" -[[package]] -name = "httpdate" -version = "1.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" - [[package]] name = "humantime" version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" -[[package]] -name = "hyper" -version = "0.14.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a152ddd61dfaec7273fe8419ab357f33aee0d914c5f4efbf0d96fa749eea5ec9" -dependencies = [ - "bytes", - "futures-channel", - "futures-core", - "futures-util", - "h2", - "http 0.2.12", - "http-body", - "httparse", - "httpdate", - "itoa", - "pin-project-lite", - "socket2", - "tokio", - "tower-service", - "tracing", - "want", -] - -[[package]] -name = "hyper-timeout" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbb958482e8c7be4bc3cf272a766a2b0bf1a6755e7a6ae777f017a31d11b13b1" -dependencies = [ - "hyper", - "pin-project-lite", - "tokio", - "tokio-io-timeout", -] - [[package]] name = "idna" version = "0.5.0" @@ -1565,16 +1315,6 @@ dependencies = [ "unicode-normalization", ] -[[package]] -name = "indexmap" -version = "1.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" -dependencies = [ - "autocfg", - "hashbrown 0.12.3", -] - [[package]] name = "indexmap" version = "2.3.0" @@ -1582,7 +1322,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "de3fc2e30ba82dd1b3911c8de1ffc143c74a914a14e99514d7637e3099df5ea0" dependencies = [ "equivalent", - "hashbrown 0.14.5", + "hashbrown", ] [[package]] @@ -1623,15 +1363,6 @@ dependencies = [ "either", ] -[[package]] -name = "itertools" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569" -dependencies = [ - "either", -] - [[package]] name = "itoa" version = "1.0.11" @@ -1751,24 +1482,9 @@ version = "0.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "37ee39891760e7d94734f6f63fedc29a2e4a152f836120753a72503f09fcf904" dependencies = [ - "hashbrown 0.14.5", -] - -[[package]] -name = "matchers" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558" -dependencies = [ - "regex-automata 0.1.10", + "hashbrown", ] -[[package]] -name = "matchit" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e7465ac9959cc2b1404e8e2367b43684a6d13790fe23056cc8c6c5a6b7bcb94" - [[package]] name = "memchr" version = "2.7.4" @@ -1784,12 +1500,6 @@ dependencies = [ "autocfg", ] -[[package]] -name = "mime" -version = "0.3.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" - [[package]] name = "minimal-lexical" version = "0.2.1" @@ -1953,7 +1663,7 @@ version = "0.10.66" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9529f4786b70a3e8c61e11179af17ab6188ad8d0ded78c5529441ed39d4bd9c1" dependencies = [ - "bitflags 2.6.0", + "bitflags", "cfg-if", "foreign-types", "libc", @@ -2042,7 +1752,7 @@ version = "3.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e459365e590736a54c3fa561947c84837534b8e9af6fc5bf781307e82658fae" dependencies = [ - "base64 0.22.1", + "base64", "serde", ] @@ -2061,26 +1771,6 @@ version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" -[[package]] -name = "pin-project" -version = "1.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6bf43b791c5b9e34c3d182969b4abb522f9343702850a2e57f460d00d09b4b3" -dependencies = [ - "pin-project-internal", -] - -[[package]] -name = "pin-project-internal" -version = "1.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.72", -] - [[package]] name = "pin-project-lite" version = "0.2.14" @@ -2197,38 +1887,6 @@ dependencies = [ "unicode-ident", ] -[[package]] -name = "prost" -version = "0.12.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "deb1435c188b76130da55f17a466d252ff7b1418b2ad3e037d127b94e3411f29" -dependencies = [ - "bytes", - "prost-derive", -] - -[[package]] -name = "prost-derive" -version = "0.12.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81bddcdb20abf9501610992b6759a4c888aef7d1a7247ef75e2404275ac24af1" -dependencies = [ - "anyhow", - "itertools 0.12.1", - "proc-macro2", - "quote", - "syn 2.0.72", -] - -[[package]] -name = "prost-types" -version = "0.12.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9091c90b0a32608e984ff2fa4091273cbdd755d54935c51d520887f4a1dbd5b0" -dependencies = [ - "prost", -] - [[package]] name = "pyo3" version = "0.21.2" @@ -2373,7 +2031,7 @@ version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2a908a6e00f1fdd0dfd9c0eb08ce85126f6d8bbda50017e74bc4a4b7d4a926a4" dependencies = [ - "bitflags 2.6.0", + "bitflags", ] [[package]] @@ -2384,17 +2042,8 @@ checksum = "4219d74c6b67a3654a9fbebc4b419e22126d13d2f3c4a07ee0cb61ff79a79619" dependencies = [ "aho-corasick", "memchr", - "regex-automata 0.4.7", - "regex-syntax 0.8.4", -] - -[[package]] -name = "regex-automata" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" -dependencies = [ - "regex-syntax 0.6.29", + "regex-automata", + "regex-syntax", ] [[package]] @@ -2405,15 +2054,9 @@ checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df" dependencies = [ "aho-corasick", "memchr", - "regex-syntax 0.8.4", + "regex-syntax", ] -[[package]] -name = "regex-syntax" -version = "0.6.29" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" - [[package]] name = "regex-syntax" version = "0.8.4" @@ -2497,7 +2140,7 @@ version = "0.31.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b838eba278d213a8beaf485bd313fd580ca4505a00d5871caeb1457c55322cae" dependencies = [ - "bitflags 2.6.0", + "bitflags", "fallible-iterator", "fallible-streaming-iterator", "hashlink", @@ -2535,7 +2178,7 @@ version = "0.38.34" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f" dependencies = [ - "bitflags 2.6.0", + "bitflags", "errno", "libc", "linux-raw-sys", @@ -2548,12 +2191,6 @@ version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "976295e77ce332211c0d24d92c0e83e50f5c5f046d11082cea19f3df13a3562d" -[[package]] -name = "rustversion" -version = "1.0.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "955d28af4278de8121b7ebeb796b6a45735dc01436d898801014aced2773a3d6" - [[package]] name = "ryu" version = "1.0.18" @@ -2604,7 +2241,7 @@ version = "2.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" dependencies = [ - "bitflags 2.6.0", + "bitflags", "core-foundation", "core-foundation-sys", "libc", @@ -2681,15 +2318,6 @@ dependencies = [ "digest", ] -[[package]] -name = "sharded-slab" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" -dependencies = [ - "lazy_static", -] - [[package]] name = "signal-hook-registry" version = "1.4.2" @@ -2784,12 +2412,6 @@ dependencies = [ "unicode-ident", ] -[[package]] -name = "sync_wrapper" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" - [[package]] name = "synstructure" version = "0.13.1" @@ -2860,16 +2482,6 @@ dependencies = [ "syn 2.0.72", ] -[[package]] -name = "thread_local" -version = "1.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c" -dependencies = [ - "cfg-if", - "once_cell", -] - [[package]] name = "threadpool" version = "1.8.1" @@ -2950,20 +2562,9 @@ dependencies = [ "signal-hook-registry", "socket2", "tokio-macros", - "tracing", "windows-sys 0.52.0", ] -[[package]] -name = "tokio-io-timeout" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30b74022ada614a1b4834de765f9bb43877f910cc8ce4be40e89042c9223a8bf" -dependencies = [ - "pin-project-lite", - "tokio", -] - [[package]] name = "tokio-macros" version = "2.4.0" @@ -2985,17 +2586,6 @@ dependencies = [ "tokio", ] -[[package]] -name = "tokio-stream" -version = "0.1.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "267ac89e0bec6e691e5813911606935d77c476ff49024f98abcea3e7b15e37af" -dependencies = [ - "futures-core", - "pin-project-lite", - "tokio", -] - [[package]] name = "tokio-tungstenite" version = "0.21.0" @@ -3010,19 +2600,6 @@ dependencies = [ "tungstenite", ] -[[package]] -name = "tokio-util" -version = "0.7.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9cf6b47b3771c49ac75ad09a6162f53ad4b8088b76ac60e8ec1455b31a189fe1" -dependencies = [ - "bytes", - "futures-core", - "futures-sink", - "pin-project-lite", - "tokio", -] - [[package]] name = "toml_datetime" version = "0.6.8" @@ -3035,123 +2612,11 @@ version = "0.19.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" dependencies = [ - "indexmap 2.3.0", + "indexmap", "toml_datetime", "winnow", ] -[[package]] -name = "tonic" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76c4eb7a4e9ef9d4763600161f12f5070b92a578e1b634db88a6887844c91a13" -dependencies = [ - "async-stream", - "async-trait", - "axum", - "base64 0.21.7", - "bytes", - "h2", - "http 0.2.12", - "http-body", - "hyper", - "hyper-timeout", - "percent-encoding", - "pin-project", - "prost", - "tokio", - "tokio-stream", - "tower", - "tower-layer", - "tower-service", - "tracing", -] - -[[package]] -name = "tower" -version = "0.4.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c" -dependencies = [ - "futures-core", - "futures-util", - "indexmap 1.9.3", - "pin-project", - "pin-project-lite", - "rand", - "slab", - "tokio", - "tokio-util", - "tower-layer", - "tower-service", - "tracing", -] - -[[package]] -name = "tower-layer" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c20c8dbed6283a09604c3e69b4b7eeb54e298b8a600d4d5ecb5ad39de609f1d0" - -[[package]] -name = "tower-service" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" - -[[package]] -name = "tracing" -version = "0.1.40" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" -dependencies = [ - "pin-project-lite", - "tracing-attributes", - "tracing-core", -] - -[[package]] -name = "tracing-attributes" -version = "0.1.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.72", -] - -[[package]] -name = "tracing-core" -version = "0.1.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" -dependencies = [ - "once_cell", - "valuable", -] - -[[package]] -name = "tracing-subscriber" -version = "0.3.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b" -dependencies = [ - "matchers", - "once_cell", - "regex", - "sharded-slab", - "thread_local", - "tracing", - "tracing-core", -] - -[[package]] -name = "try-lock" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" - [[package]] name = "tungstenite" version = "0.21.0" @@ -3161,7 +2626,7 @@ dependencies = [ "byteorder", "bytes", "data-encoding", - "http 1.1.0", + "http", "httparse", "log", "native-tls", @@ -3240,12 +2705,6 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" -[[package]] -name = "valuable" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" - [[package]] name = "vcpkg" version = "0.2.15" @@ -3268,15 +2727,6 @@ dependencies = [ "winapi-util", ] -[[package]] -name = "want" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" -dependencies = [ - "try-lock", -] - [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" diff --git a/crates/chia-client/Cargo.toml b/crates/chia-client/Cargo.toml index d5fa7c428..99ef3f6f4 100644 --- a/crates/chia-client/Cargo.toml +++ b/crates/chia-client/Cargo.toml @@ -27,7 +27,6 @@ hex-literal = { workspace = true } rand = { workspace = true } semver = { workspace = true } hex = { workspace = true } -console-subscriber = { workspace = true } [dev-dependencies] chia-ssl = { path = "../chia-ssl" } diff --git a/crates/chia-client/examples/client.rs b/crates/chia-client/examples/client.rs index 1d1c265cb..d79e5697f 100644 --- a/crates/chia-client/examples/client.rs +++ b/crates/chia-client/examples/client.rs @@ -9,7 +9,6 @@ use tokio::{sync::Mutex, time::sleep}; #[tokio::main] async fn main() -> anyhow::Result<()> { env_logger::init(); - // console_subscriber::init(); log::info!("Generating certificate"); let cert = ChiaCertificate::generate()?; @@ -20,8 +19,8 @@ async fn main() -> anyhow::Result<()> { tls_connector, ClientOptions { network: Network::mainnet(), - target_peers: 500, - connection_concurrency: 200, + target_peers: 1000, + connection_concurrency: 50, node_type: NodeType::Wallet, capabilities: vec![ (1, "1".to_string()), @@ -30,9 +29,9 @@ async fn main() -> anyhow::Result<()> { ], protocol_version: "0.0.34".parse()?, software_version: "0.0.0".to_string(), - connection_timeout: Duration::from_secs(3), - handshake_timeout: Duration::from_secs(2), - request_peers_timeout: Duration::from_secs(3), + connection_timeout: Duration::from_secs(5), + handshake_timeout: Duration::from_secs(5), + request_peers_timeout: Duration::from_secs(5), }, ); diff --git a/crates/chia-client/src/client.rs b/crates/chia-client/src/client.rs index 09dc7b2fb..c8f56da13 100644 --- a/crates/chia-client/src/client.rs +++ b/crates/chia-client/src/client.rs @@ -8,7 +8,7 @@ use std::{ time::Duration, }; -use chia_protocol::{Handshake, Message, NodeType, ProtocolMessageTypes, RespondPeers}; +use chia_protocol::{Handshake, Message, NodeType, ProtocolMessageTypes}; use chia_traits::Streamable; use dns_lookup::lookup_host; use futures_util::{stream::FuturesUnordered, StreamExt}; @@ -64,13 +64,13 @@ struct ClientInner { state: Arc>, options: ClientOptions, tls_connector: TlsConnector, + sender: Arc>, } /// A client that can connect to many different peers on the network. #[derive(Debug)] pub struct ClientState { peers: HashMap, - sender: mpsc::Sender, banned_ips: HashSet, trusted_ips: HashSet, } @@ -84,7 +84,6 @@ impl Client { let state = ClientState { peers: HashMap::new(), - sender, banned_ips: HashSet::new(), trusted_ips: HashSet::new(), }; @@ -93,6 +92,7 @@ impl Client { state: Arc::new(Mutex::new(state)), options, tls_connector, + sender: Arc::new(sender), })); (client, receiver) @@ -111,7 +111,6 @@ impl Client { if self.lock().await.peers.is_empty() { return; } - } else { } let state = self.lock().await; @@ -136,18 +135,34 @@ impl Client { } // Request new peers from the peer. - let Ok(Ok(response)): std::result::Result, _> = maybe_timeout( + let response = match maybe_timeout( trusted.contains(&peer.socket_addr().ip()), self.0.options.request_peers_timeout, peer.request_peers(), ) .await - else { - log::info!("Failed to request peers from {}", peer.socket_addr()); - - self.lock().await.ban(peer.socket_addr().ip()); - - continue; + { + Ok(Ok(response)) => response, + Err(error) | Ok(Err(error)) => { + log::info!( + "Failed to request peers from {} with error {error}", + peer.socket_addr() + ); + for peer_id in self.lock().await.ban(peer.socket_addr().ip()) { + self.lock().await.disconnect_peer(&peer_id); + self.0 + .sender + .send(Event::Disconnected(peer.socket_addr())) + .await + .ok(); + } + self.0 + .sender + .send(Event::Banned(peer.socket_addr().ip())) + .await + .ok(); + continue; + } }; log::info!("Requested peers from {}", peer.socket_addr()); @@ -249,7 +264,6 @@ impl Client { for &socket_addr in socket_addrs { // Skip peers which we are already connected to. - if self .lock() .await @@ -270,15 +284,25 @@ impl Client { while let Some((socket_addr, result)) = connections.next().await { match result { Err(error) => { - log::debug!("Failed to connect to peer {socket_addr} with error: {error}"); - - self.lock().await.ban(socket_addr.ip()); + log::debug!("Failed to connect to peer {socket_addr} with error {error}"); + for peer_id in self.lock().await.ban(socket_addr.ip()) { + self.lock().await.disconnect_peer(&peer_id); + self.0 + .sender + .send(Event::Disconnected(socket_addr)) + .await + .ok(); + } + self.0 + .sender + .send(Event::Banned(socket_addr.ip())) + .await + .ok(); } Ok((peer, receiver)) => { if self.lock().await.peers.len() >= self.0.options.target_peers { break; } - self.insert_peer(peer, receiver).await.ok(); log::info!("Connected to peer {socket_addr}"); } @@ -353,12 +377,16 @@ impl Client { let mut state = self.lock().await; let ip_addr = peer.socket_addr().ip(); + if state.is_banned(&ip_addr) { return Err(Error::BannedPeer(ip_addr)); } state.peers.insert(peer.peer_id(), peer.clone()); - state + + drop(state); + + self.0 .sender .send(Event::Connected(peer.peer_id(), peer.socket_addr())) .await?; @@ -370,6 +398,7 @@ impl Client { peer.peer_id(), peer.socket_addr(), receiver, + self.0.sender.clone(), )); Ok(()) @@ -381,20 +410,6 @@ impl ClientState { &self.peers } - pub fn disconnect_peer(&mut self, peer_id: &PeerId) { - if let Some(peer) = self.peers.remove(peer_id) { - // self.sender - // .send(Event::Disconnected(peer.socket_addr())) - // .await - // .ok(); - log::info!("Peer {} disconnected", peer.socket_addr()); - } - } - - pub fn disconnect_all(&mut self) { - self.peers.clear(); - } - pub fn banned_ips(&self) -> &HashSet { &self.banned_ips } @@ -403,32 +418,6 @@ impl ClientState { self.banned_ips.contains(ip_addr) } - pub fn ban(&mut self, ip_addr: IpAddr) { - if self.is_trusted(&ip_addr) { - log::warn!("Attempted to ban trusted peer {ip_addr}"); - return; - } - - if !self.banned_ips.insert(ip_addr) { - return; - } - - log::info!("Banning peer {ip_addr}"); - - // self.sender.send(Event::Banned(ip_addr)).await.ok(); - - let peer_ids: Vec = self - .peers - .keys() - .filter(|peer_id| self.peers[peer_id].socket_addr().ip() == ip_addr) - .copied() - .collect(); - - for peer_id in peer_ids { - self.disconnect_peer(&peer_id); - } - } - pub fn unban(&mut self, ip_addr: &IpAddr) { self.banned_ips.remove(ip_addr); } @@ -451,6 +440,31 @@ impl ClientState { pub fn untrust(&mut self, ip_addr: &IpAddr) { self.trusted_ips.remove(ip_addr); } + + pub fn disconnect_peer(&mut self, peer_id: &PeerId) { + if let Some(peer) = self.peers.remove(peer_id) { + log::info!("Peer {} disconnected", peer.socket_addr()); + } + } + + pub fn ban(&mut self, ip_addr: IpAddr) -> Vec { + if self.is_trusted(&ip_addr) { + log::warn!("Attempted to ban trusted peer {ip_addr}"); + return Vec::new(); + } + + log::info!("Banning {ip_addr}"); + self.banned_ips.insert(ip_addr); + + let peer_ids: Vec = self + .peers + .keys() + .filter(|peer_id| self.peers[peer_id].socket_addr().ip() == ip_addr) + .copied() + .collect(); + + peer_ids + } } async fn handle_peer_connection( @@ -458,19 +472,13 @@ async fn handle_peer_connection( peer_id: PeerId, socket_addr: SocketAddr, mut receiver: mpsc::Receiver, + sender: Arc>, ) { let mut is_banned = false; while let Some(message) = receiver.recv().await { - // If the client has been dropped, we should gracefully end the task. - let Some(state) = state.upgrade() else { - return; - }; - - let state = state.lock().await; - // Close the connection if an error occurs. - if let Err(error) = state.sender.send(Event::Message(peer_id, message)).await { + if let Err(error) = sender.send(Event::Message(peer_id, message)).await { log::warn!("Failed to send client message event: {error}"); is_banned = true; break; @@ -482,12 +490,15 @@ async fn handle_peer_connection( return; }; - let mut state = state.lock().await; - if is_banned { - state.ban(socket_addr.ip()); + for peer_id in state.lock().await.ban(socket_addr.ip()) { + state.lock().await.disconnect_peer(&peer_id); + sender.send(Event::Disconnected(socket_addr)).await.ok(); + } + sender.send(Event::Banned(socket_addr.ip())).await.ok(); } else { - state.disconnect_peer(&peer_id); + state.lock().await.disconnect_peer(&peer_id); + sender.send(Event::Disconnected(socket_addr)).await.ok(); } } From 2fbee39b3da597ead04fc7a5c676aa710ccd9b5c Mon Sep 17 00:00:00 2001 From: Rigidity Date: Mon, 5 Aug 2024 17:29:20 -0400 Subject: [PATCH 18/26] Clean start --- crates/chia-client/examples/client.rs | 83 --- .../examples/{peer.rs => peer_connection.rs} | 0 crates/chia-client/src/client.rs | 515 ------------------ 3 files changed, 598 deletions(-) delete mode 100644 crates/chia-client/examples/client.rs rename crates/chia-client/examples/{peer.rs => peer_connection.rs} (100%) diff --git a/crates/chia-client/examples/client.rs b/crates/chia-client/examples/client.rs deleted file mode 100644 index d79e5697f..000000000 --- a/crates/chia-client/examples/client.rs +++ /dev/null @@ -1,83 +0,0 @@ -use std::{sync::Arc, time::Duration}; - -use chia_client::{create_tls_connector, Client, ClientOptions, Event, Network}; -use chia_protocol::{NewPeakWallet, NodeType, ProtocolMessageTypes}; -use chia_ssl::ChiaCertificate; -use chia_traits::Streamable; -use tokio::{sync::Mutex, time::sleep}; - -#[tokio::main] -async fn main() -> anyhow::Result<()> { - env_logger::init(); - - log::info!("Generating certificate"); - let cert = ChiaCertificate::generate()?; - let tls_connector = create_tls_connector(cert.cert_pem.as_bytes(), cert.key_pem.as_bytes())?; - - log::info!("Creating client"); - let (client, mut receiver) = Client::new( - tls_connector, - ClientOptions { - network: Network::mainnet(), - target_peers: 1000, - connection_concurrency: 50, - node_type: NodeType::Wallet, - capabilities: vec![ - (1, "1".to_string()), - (2, "1".to_string()), - (3, "1".to_string()), - ], - protocol_version: "0.0.34".parse()?, - software_version: "0.0.0".to_string(), - connection_timeout: Duration::from_secs(5), - handshake_timeout: Duration::from_secs(5), - request_peers_timeout: Duration::from_secs(5), - }, - ); - - log::info!("Connecting to DNS introducers"); - - let clone = client.clone(); - tokio::spawn(async move { - loop { - clone.discover_peers(true).await; - sleep(Duration::from_secs(5)).await; - } - }); - - let height = Arc::new(Mutex::new(0)); - let height_clone = height.clone(); - - tokio::spawn(async move { - loop { - sleep(Duration::from_secs(1)).await; - log::info!( - "Currently connected to {} peers, with peak height {}", - client.lock().await.peers().len(), - *height_clone.lock().await - ); - } - }); - - while let Some(event) = receiver.recv().await { - let Event::Message(_peer_id, message) = event else { - continue; - }; - - if message.msg_type != ProtocolMessageTypes::NewPeakWallet { - continue; - } - - let Ok(new_peak) = NewPeakWallet::from_bytes(&message.data) else { - continue; - }; - - let mut height = height.lock().await; - - if new_peak.height > *height { - *height = new_peak.height; - } - } - - Ok(()) -} diff --git a/crates/chia-client/examples/peer.rs b/crates/chia-client/examples/peer_connection.rs similarity index 100% rename from crates/chia-client/examples/peer.rs rename to crates/chia-client/examples/peer_connection.rs diff --git a/crates/chia-client/src/client.rs b/crates/chia-client/src/client.rs index c8f56da13..8b1378917 100644 --- a/crates/chia-client/src/client.rs +++ b/crates/chia-client/src/client.rs @@ -1,516 +1 @@ -use std::{ - collections::{HashMap, HashSet}, - future::Future, - net::{IpAddr, SocketAddr}, - ops::Deref, - str::FromStr, - sync::{Arc, Weak}, - time::Duration, -}; -use chia_protocol::{Handshake, Message, NodeType, ProtocolMessageTypes}; -use chia_traits::Streamable; -use dns_lookup::lookup_host; -use futures_util::{stream::FuturesUnordered, StreamExt}; -use native_tls::TlsConnector; -use rand::{seq::SliceRandom, thread_rng}; -use semver::Version; -use tokio::{ - sync::{mpsc, Mutex}, - time::timeout, -}; - -use crate::{Error, Event, Network, Peer, PeerId, Result}; - -#[derive(Debug, Clone)] -pub struct ClientOptions { - /// The network to connect to. By default, this is mainnet. - pub network: Network, - /// The type of service that this client represents. - pub node_type: NodeType, - /// The capabilities that this client supports. - pub capabilities: Vec<(u16, String)>, - /// The minimum protocol version that this client supports. - /// If the protocol version of peers are lower than this, they will be disconnected. - pub protocol_version: Version, - /// The software version of this client. - /// This is not important for the handshake, but is sent to the peer for informational purposes. - pub software_version: String, - /// The ideal number of peers that should be connected at any given time. - pub target_peers: usize, - /// The maximum number of concurrent connections that can be initiated at once. - pub connection_concurrency: usize, - /// How long to wait when trying to connect to a peer. - pub connection_timeout: Duration, - /// How long to wait for a handshake response from a peer before disconnecting. - pub handshake_timeout: Duration, - /// How long to wait for a response to a request for peers. - pub request_peers_timeout: Duration, -} - -#[derive(Debug, Clone)] -pub struct Client(Arc); - -impl Deref for Client { - type Target = Mutex; - - fn deref(&self) -> &Self::Target { - &self.0.state - } -} - -#[derive(Debug)] -struct ClientInner { - state: Arc>, - options: ClientOptions, - tls_connector: TlsConnector, - sender: Arc>, -} - -/// A client that can connect to many different peers on the network. -#[derive(Debug)] -pub struct ClientState { - peers: HashMap, - banned_ips: HashSet, - trusted_ips: HashSet, -} - -impl Client { - pub fn new( - tls_connector: TlsConnector, - options: ClientOptions, - ) -> (Self, mpsc::Receiver) { - let (sender, receiver) = mpsc::channel(32); - - let state = ClientState { - peers: HashMap::new(), - banned_ips: HashSet::new(), - trusted_ips: HashSet::new(), - }; - - let client = Self(Arc::new(ClientInner { - state: Arc::new(Mutex::new(state)), - options, - tls_connector, - sender: Arc::new(sender), - })); - - (client, receiver) - } - - pub async fn discover_peers(&self, prefer_introducers: bool) { - if self.lock().await.peers.len() >= self.0.options.target_peers { - return; - } - - // If we don't have any peers, try to connect to DNS introducers. - if self.lock().await.peers.is_empty() || prefer_introducers { - self.discover_peers_with_dns().await; - - // If we still don't have any peers, we can't do anything. - if self.lock().await.peers.is_empty() { - return; - } - } - - let state = self.lock().await; - - if state.peers.len() >= self.0.options.target_peers { - return; - } - - if state.peers.is_empty() { - log::error!("No peers connected after DNS lookups"); - return; - } - - let peers: Vec = state.peers.values().cloned().collect(); - let trusted = state.trusted_ips.clone(); - - drop(state); - - for peer in peers { - if self.lock().await.peers.len() >= self.0.options.target_peers { - break; - } - - // Request new peers from the peer. - let response = match maybe_timeout( - trusted.contains(&peer.socket_addr().ip()), - self.0.options.request_peers_timeout, - peer.request_peers(), - ) - .await - { - Ok(Ok(response)) => response, - Err(error) | Ok(Err(error)) => { - log::info!( - "Failed to request peers from {} with error {error}", - peer.socket_addr() - ); - for peer_id in self.lock().await.ban(peer.socket_addr().ip()) { - self.lock().await.disconnect_peer(&peer_id); - self.0 - .sender - .send(Event::Disconnected(peer.socket_addr())) - .await - .ok(); - } - self.0 - .sender - .send(Event::Banned(peer.socket_addr().ip())) - .await - .ok(); - continue; - } - }; - - log::info!("Requested peers from {}", peer.socket_addr()); - - let mut ips = HashSet::new(); - - for item in response.peer_list { - // If we can't parse the IP address, skip it. - let Ok(ip_addr) = IpAddr::from_str(&item.host) else { - log::debug!("Failed to parse IP address {}", item.host); - continue; - }; - ips.insert(SocketAddr::new(ip_addr, item.port)); - } - - // Keep connecting peers until the peer list is exhausted, - // then move on to the next peer to request from. - let mut iter = ips.into_iter(); - - loop { - let next_peers: Vec<_> = iter - .by_ref() - .take(self.0.options.connection_concurrency) - .collect(); - - if next_peers.is_empty() { - break; - } - - self.connect_peers(&next_peers).await; - } - } - } - - pub async fn discover_peers_with_dns(&self) { - let mut socket_addrs: Vec = self.dns_lookup().into_iter().collect(); - - // Shuffle the list of IPs so that we don't always connect to the same ones. - // This also prevents bias towards IPv4 or IPv6. - socket_addrs.as_mut_slice().shuffle(&mut thread_rng()); - - self.connect_peers_batched(&socket_addrs).await; - } - - pub fn dns_lookup(&self) -> HashSet { - let mut socket_addrs = HashSet::new(); - - for dns_introducer in &self.0.options.network.dns_introducers { - log::debug!("Performing DNS lookup of {dns_introducer}."); - - // If a DNS introducer lookup fails, we just skip it. - let Ok(result) = lookup_host(dns_introducer) else { - log::warn!("Failed to lookup DNS introducer `{dns_introducer}`"); - continue; - }; - - socket_addrs.extend( - result - .into_iter() - .map(|ip| SocketAddr::new(ip, self.0.options.network.default_port)), - ); - } - - log::info!( - "Found a total of {} IPs from DNS introducers.", - socket_addrs.len() - ); - - socket_addrs - } - - async fn connect_peers_batched(&self, socket_addrs: &[SocketAddr]) { - let mut cursor = 0; - - loop { - if self.lock().await.peers.len() < self.0.options.target_peers { - if cursor >= socket_addrs.len() { - break; - } - - // Get the remaining peers we can connect to, up to the concurrency limit. - let new_addrs = &socket_addrs[cursor - ..socket_addrs - .len() - .min(cursor + self.0.options.connection_concurrency)]; - - // Increment the cursor by the number of peers we're trying to connect to. - cursor += new_addrs.len(); - - self.connect_peers(new_addrs).await; - } else { - break; - } - } - } - - async fn connect_peers(&self, socket_addrs: &[SocketAddr]) { - let mut connections = FuturesUnordered::new(); - - for &socket_addr in socket_addrs { - // Skip peers which we are already connected to. - if self - .lock() - .await - .peers - .iter() - .any(|(_, peer)| peer.socket_addr().ip() == socket_addr.ip()) - { - continue; - } - - // Add the next connection to the queue. - connections.push(async move { - let result = self.connect_peer(socket_addr).await; - (socket_addr, result) - }); - } - - while let Some((socket_addr, result)) = connections.next().await { - match result { - Err(error) => { - log::debug!("Failed to connect to peer {socket_addr} with error {error}"); - for peer_id in self.lock().await.ban(socket_addr.ip()) { - self.lock().await.disconnect_peer(&peer_id); - self.0 - .sender - .send(Event::Disconnected(socket_addr)) - .await - .ok(); - } - self.0 - .sender - .send(Event::Banned(socket_addr.ip())) - .await - .ok(); - } - Ok((peer, receiver)) => { - if self.lock().await.peers.len() >= self.0.options.target_peers { - break; - } - self.insert_peer(peer, receiver).await.ok(); - log::info!("Connected to peer {socket_addr}"); - } - } - } - } - - async fn connect_peer( - &self, - socket_addr: SocketAddr, - ) -> Result<(Peer, mpsc::Receiver)> { - if self.lock().await.is_banned(&socket_addr.ip()) { - return Err(Error::BannedPeer(socket_addr.ip())); - } - - log::debug!("Connecting to peer {socket_addr}"); - - let is_trusted = self.lock().await.is_trusted(&socket_addr.ip()); - - let (peer, mut receiver) = maybe_timeout( - is_trusted, - self.0.options.connection_timeout, - Peer::connect(socket_addr, self.0.tls_connector.clone()), - ) - .await??; - - peer.send(Handshake { - network_id: self.0.options.network.network_id.clone(), - protocol_version: self.0.options.protocol_version.to_string(), - software_version: self.0.options.software_version.clone(), - server_port: 0, - node_type: self.0.options.node_type, - capabilities: self.0.options.capabilities.clone(), - }) - .await?; - - let Some(message) = maybe_timeout( - is_trusted, - self.0.options.handshake_timeout, - receiver.recv(), - ) - .await? - else { - return Err(Error::ExpectedHandshake); - }; - - if message.msg_type != ProtocolMessageTypes::Handshake { - return Err(Error::ExpectedHandshake); - }; - - let handshake = Handshake::from_bytes(&message.data)?; - - if handshake.network_id != self.0.options.network.network_id { - return Err(Error::WrongNetworkId(handshake.network_id)); - } - - let Ok(protocol_version) = Version::parse(&handshake.protocol_version) else { - return Err(Error::InvalidProtocolVersion(handshake.protocol_version)); - }; - - if protocol_version < self.0.options.protocol_version { - return Err(Error::OutdatedProtocolVersion( - protocol_version, - self.0.options.protocol_version.clone(), - )); - } - - Ok((peer, receiver)) - } - - async fn insert_peer(&self, peer: Peer, receiver: mpsc::Receiver) -> Result<()> { - let mut state = self.lock().await; - - let ip_addr = peer.socket_addr().ip(); - - if state.is_banned(&ip_addr) { - return Err(Error::BannedPeer(ip_addr)); - } - - state.peers.insert(peer.peer_id(), peer.clone()); - - drop(state); - - self.0 - .sender - .send(Event::Connected(peer.peer_id(), peer.socket_addr())) - .await?; - - // Spawn a task to propagate messages from the peer. - // We downgrade the client to avoid a cycle and allow it to be dropped. - tokio::spawn(handle_peer_connection( - Arc::downgrade(&self.0.state), - peer.peer_id(), - peer.socket_addr(), - receiver, - self.0.sender.clone(), - )); - - Ok(()) - } -} - -impl ClientState { - pub fn peers(&self) -> &HashMap { - &self.peers - } - - pub fn banned_ips(&self) -> &HashSet { - &self.banned_ips - } - - pub fn is_banned(&self, ip_addr: &IpAddr) -> bool { - self.banned_ips.contains(ip_addr) - } - - pub fn unban(&mut self, ip_addr: &IpAddr) { - self.banned_ips.remove(ip_addr); - } - - pub fn trusted_ips(&self) -> &HashSet { - &self.trusted_ips - } - - pub fn is_trusted(&self, ip_addr: &IpAddr) -> bool { - self.trusted_ips.contains(ip_addr) - } - - pub fn trust(&mut self, ip_addr: IpAddr) { - self.trusted_ips.insert(ip_addr); - if self.banned_ips.remove(&ip_addr) { - log::info!("Unbanning peer {ip_addr} since it's now trusted"); - } - } - - pub fn untrust(&mut self, ip_addr: &IpAddr) { - self.trusted_ips.remove(ip_addr); - } - - pub fn disconnect_peer(&mut self, peer_id: &PeerId) { - if let Some(peer) = self.peers.remove(peer_id) { - log::info!("Peer {} disconnected", peer.socket_addr()); - } - } - - pub fn ban(&mut self, ip_addr: IpAddr) -> Vec { - if self.is_trusted(&ip_addr) { - log::warn!("Attempted to ban trusted peer {ip_addr}"); - return Vec::new(); - } - - log::info!("Banning {ip_addr}"); - self.banned_ips.insert(ip_addr); - - let peer_ids: Vec = self - .peers - .keys() - .filter(|peer_id| self.peers[peer_id].socket_addr().ip() == ip_addr) - .copied() - .collect(); - - peer_ids - } -} - -async fn handle_peer_connection( - state: Weak>, - peer_id: PeerId, - socket_addr: SocketAddr, - mut receiver: mpsc::Receiver, - sender: Arc>, -) { - let mut is_banned = false; - - while let Some(message) = receiver.recv().await { - // Close the connection if an error occurs. - if let Err(error) = sender.send(Event::Message(peer_id, message)).await { - log::warn!("Failed to send client message event: {error}"); - is_banned = true; - break; - } - } - - // If the client has been dropped, we should gracefully end the task. - let Some(state) = state.upgrade() else { - return; - }; - - if is_banned { - for peer_id in state.lock().await.ban(socket_addr.ip()) { - state.lock().await.disconnect_peer(&peer_id); - sender.send(Event::Disconnected(socket_addr)).await.ok(); - } - sender.send(Event::Banned(socket_addr.ip())).await.ok(); - } else { - state.lock().await.disconnect_peer(&peer_id); - sender.send(Event::Disconnected(socket_addr)).await.ok(); - } -} - -async fn maybe_timeout(is_trusted: bool, duration: Duration, future: F) -> Result -where - F: Future, -{ - if is_trusted { - Ok(future.await) - } else { - timeout(duration, future) - .await - .map_err(Error::ConnectionTimeout) - } -} From 7f39ef68943eadf24a43eddc1196f0a325df6a16 Mon Sep 17 00:00:00 2001 From: Rigidity Date: Mon, 5 Aug 2024 19:59:54 -0400 Subject: [PATCH 19/26] Remove client --- crates/chia-client/src/client.rs | 1 - crates/chia-client/src/lib.rs | 2 -- 2 files changed, 3 deletions(-) delete mode 100644 crates/chia-client/src/client.rs diff --git a/crates/chia-client/src/client.rs b/crates/chia-client/src/client.rs deleted file mode 100644 index 8b1378917..000000000 --- a/crates/chia-client/src/client.rs +++ /dev/null @@ -1 +0,0 @@ - diff --git a/crates/chia-client/src/lib.rs b/crates/chia-client/src/lib.rs index de7c1f601..a3fd2d1bc 100644 --- a/crates/chia-client/src/lib.rs +++ b/crates/chia-client/src/lib.rs @@ -1,4 +1,3 @@ -mod client; mod error; mod event; mod network; @@ -6,7 +5,6 @@ mod peer; mod request_map; mod tls; -pub use client::*; pub use error::*; pub use event::*; pub use network::*; From 41ffa0b8bf2402a7d9d9e569bc20f38361b43445 Mon Sep 17 00:00:00 2001 From: Rigidity Date: Mon, 5 Aug 2024 20:05:34 -0400 Subject: [PATCH 20/26] Remove old stuff --- crates/chia-client/src/error.rs | 31 ------------------------------- crates/chia-client/src/event.rs | 13 ------------- crates/chia-client/src/lib.rs | 2 -- 3 files changed, 46 deletions(-) delete mode 100644 crates/chia-client/src/event.rs diff --git a/crates/chia-client/src/error.rs b/crates/chia-client/src/error.rs index 44382c6b8..7c3cc3052 100644 --- a/crates/chia-client/src/error.rs +++ b/crates/chia-client/src/error.rs @@ -1,31 +1,12 @@ -use std::net::IpAddr; - use chia_protocol::ProtocolMessageTypes; -use semver::Version; use thiserror::Error; -use tokio::sync::mpsc::error::SendError; use tokio::sync::oneshot::error::RecvError; -use tokio::time::error::Elapsed; - -use crate::Event; #[derive(Debug, Error)] pub enum Error { #[error("Peer is missing certificate")] MissingCertificate, - #[error("Handshake not received")] - ExpectedHandshake, - - #[error("Invalid protocol version {0}")] - InvalidProtocolVersion(String), - - #[error("Wrong network id {0}")] - WrongNetworkId(String), - - #[error("Outdated protocol version {0}, expected {1}")] - OutdatedProtocolVersion(Version, Version), - #[error("Streamable error: {0}")] Streamable(#[from] chia_traits::Error), @@ -35,9 +16,6 @@ pub enum Error { #[error("TLS error: {0}")] Tls(#[from] native_tls::Error), - #[error("Banned peer: {0}")] - BannedPeer(IpAddr), - #[error("Unexpected message received with type {0:?}")] UnexpectedMessage(ProtocolMessageTypes), @@ -47,18 +25,9 @@ pub enum Error { #[error("Failed to send event")] EventNotSent, - #[error("Failed to send message")] - Send(#[from] SendError), - #[error("Failed to receive message")] Recv(#[from] RecvError), - #[error("Connection timeout: {0}")] - ConnectionTimeout(Elapsed), - - #[error("Handshake timeout: {0}")] - HandshakeTimeout(Elapsed), - #[error("IO error: {0}")] Io(#[from] std::io::Error), } diff --git a/crates/chia-client/src/event.rs b/crates/chia-client/src/event.rs deleted file mode 100644 index 056999e55..000000000 --- a/crates/chia-client/src/event.rs +++ /dev/null @@ -1,13 +0,0 @@ -use std::net::{IpAddr, SocketAddr}; - -use chia_protocol::Message; - -use crate::PeerId; - -#[derive(Debug, Clone)] -pub enum Event { - Message(PeerId, Message), - Connected(PeerId, SocketAddr), - Disconnected(SocketAddr), - Banned(IpAddr), -} diff --git a/crates/chia-client/src/lib.rs b/crates/chia-client/src/lib.rs index a3fd2d1bc..363affdf5 100644 --- a/crates/chia-client/src/lib.rs +++ b/crates/chia-client/src/lib.rs @@ -1,12 +1,10 @@ mod error; -mod event; mod network; mod peer; mod request_map; mod tls; pub use error::*; -pub use event::*; pub use network::*; pub use peer::*; pub use tls::*; From 80b416019d4c8dda509062bbbd6e4674e26a2b2d Mon Sep 17 00:00:00 2001 From: Rigidity Date: Mon, 5 Aug 2024 20:09:56 -0400 Subject: [PATCH 21/26] Remove unused deps --- Cargo.lock | 2 -- crates/chia-client/Cargo.toml | 2 -- 2 files changed, 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 841ee5d55..1d727d74f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -346,8 +346,6 @@ dependencies = [ "hex-literal", "log", "native-tls", - "rand", - "semver", "sha2", "thiserror", "tokio", diff --git a/crates/chia-client/Cargo.toml b/crates/chia-client/Cargo.toml index 99ef3f6f4..7b9ae9051 100644 --- a/crates/chia-client/Cargo.toml +++ b/crates/chia-client/Cargo.toml @@ -24,8 +24,6 @@ log = { workspace = true } native-tls = { workspace = true } dns-lookup = { workspace = true } hex-literal = { workspace = true } -rand = { workspace = true } -semver = { workspace = true } hex = { workspace = true } [dev-dependencies] From a10c8fda9b746638c92ea54b2cd5b8e479b7dbef Mon Sep 17 00:00:00 2001 From: Rigidity Date: Mon, 5 Aug 2024 20:11:14 -0400 Subject: [PATCH 22/26] Remove unused dep --- crates/chia-client/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/chia-client/Cargo.toml b/crates/chia-client/Cargo.toml index 7b9ae9051..502d446e3 100644 --- a/crates/chia-client/Cargo.toml +++ b/crates/chia-client/Cargo.toml @@ -22,7 +22,6 @@ thiserror = { workspace = true } sha2 = { workspace = true } log = { workspace = true } native-tls = { workspace = true } -dns-lookup = { workspace = true } hex-literal = { workspace = true } hex = { workspace = true } @@ -31,3 +30,4 @@ chia-ssl = { path = "../chia-ssl" } tokio = { workspace = true, features = ["full"] } anyhow = { workspace = true } env_logger = { workspace = true } +dns-lookup = { workspace = true } From 7d7794bb953a6740a049f958b23768be46a5ca88 Mon Sep 17 00:00:00 2001 From: Rigidity Date: Mon, 5 Aug 2024 20:15:16 -0400 Subject: [PATCH 23/26] Remove unused --- Cargo.toml | 2 -- 1 file changed, 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 616d1697e..8ef29bc97 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -141,6 +141,4 @@ wasm-bindgen = "0.2.92" log = "0.4.22" native-tls = "0.2.12" dns-lookup = "2.0.4" -semver = "1.0.23" env_logger = "0.11.3" -console-subscriber = "0.3.0" From 4792a05d02aafd7a4bbefdd4899486fb70b6dddf Mon Sep 17 00:00:00 2001 From: Rigidity Date: Mon, 5 Aug 2024 20:16:26 -0400 Subject: [PATCH 24/26] Un-update lock --- Cargo.lock | 72 ++++++++++++++++++++++++------------------------------ 1 file changed, 32 insertions(+), 40 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 1d727d74f..39932b45d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -116,9 +116,9 @@ dependencies = [ [[package]] name = "asn1-rs" -version = "0.6.2" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5493c3bedbacf7fd7382c6346bbd66687d12bbaad3a89a2d2c303ee6cf20b048" +checksum = "22ad1373757efa0f70ec53939aabc7152e1591cb485208052993070ac8d2429d" dependencies = [ "asn1-rs-derive", "asn1-rs-impl", @@ -132,9 +132,9 @@ dependencies = [ [[package]] name = "asn1-rs-derive" -version = "0.5.1" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "965c2d33e53cb6b267e148a4cb0760bc01f4904c1cd4bb4002a085bb016d1490" +checksum = "7378575ff571966e99a744addeff0bff98b8ada0dedf1956d59e634db95eaac1" dependencies = [ "proc-macro2", "quote", @@ -243,9 +243,9 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" -version = "1.7.1" +version = "1.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8318a53db07bb3f8dca91a600466bdb3f2eaadeedfdbcf02e1accbad9271ba50" +checksum = "a12916984aab3fa6e39d655a33e09c0071eb36d6ab3aea5c2d78551f1df6d952" [[package]] name = "cast" @@ -587,9 +587,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.13" +version = "4.5.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fbb260a053428790f3de475e304ff84cdbc4face759ea7a3e64c1edd938a7fc" +checksum = "35723e6a11662c2afb578bcf0b88bf6ea8e21282a953428f240574fcc3a2b5b3" dependencies = [ "clap_builder", "clap_derive", @@ -597,9 +597,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.13" +version = "4.5.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64b17d7ea74e9f833c7dbf2cbe4fb12ff26783eda4782a8975b72f895c9b4d99" +checksum = "49eb96cbfa7cfa35017b7cd548c75b14c3118c98b423041d70562665e07fb0fa" dependencies = [ "anstream", "anstyle", @@ -609,9 +609,9 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.5.13" +version = "4.5.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "501d359d5f3dcaf6ecdeee48833ae73ec6e42723a1e52419c79abf9507eec0a0" +checksum = "5d029b67f89d30bbb547c89fd5161293c0aec155fc691d7924b64550662db93e" dependencies = [ "heck 0.5.0", "proc-macro2", @@ -1315,9 +1315,9 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.3.0" +version = "2.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de3fc2e30ba82dd1b3911c8de1ffc143c74a914a14e99514d7637e3099df5ea0" +checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26" dependencies = [ "equivalent", "hashbrown", @@ -1476,9 +1476,9 @@ checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" [[package]] name = "lru" -version = "0.12.4" +version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37ee39891760e7d94734f6f63fedc29a2e4a152f836120753a72503f09fcf904" +checksum = "d3262e75e648fce39813cb56ac41f3c3e3f65217ebf3844d818d1f9398cfb0dc" dependencies = [ "hashbrown", ] @@ -1850,11 +1850,12 @@ checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" [[package]] name = "ppv-lite86" -version = "0.2.20" +version = "0.2.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04" +checksum = "2288c0e17cc8d342c712bb43a257a80ebffce59cdb33d5000d8348f3ec02528b" dependencies = [ "zerocopy", + "zerocopy-derive", ] [[package]] @@ -2034,9 +2035,9 @@ dependencies = [ [[package]] name = "regex" -version = "1.10.6" +version = "1.10.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4219d74c6b67a3654a9fbebc4b419e22126d13d2f3c4a07ee0cb61ff79a79619" +checksum = "b91213439dad192326a0d7c6ee3955910425f441d7038e0d6933b0aec5c4517f" dependencies = [ "aho-corasick", "memchr", @@ -2284,9 +2285,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.122" +version = "1.0.121" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "784b6203951c57ff748476b126ccb5e8e2959a5c19e5c617ab1956be3dbc68da" +checksum = "4ab380d7d9f22ef3f21ad3e6c1ebe8e4fc7a2000ccba2e4d71fc96f15b2cb609" dependencies = [ "itoa", "memchr", @@ -2423,9 +2424,9 @@ dependencies = [ [[package]] name = "target-lexicon" -version = "0.12.16" +version = "0.12.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1" +checksum = "4873307b7c257eddcb50c9bedf158eb669578359fb28428bef438fec8e6ba7c2" [[package]] name = "tempfile" @@ -2809,11 +2810,11 @@ checksum = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc" [[package]] name = "winapi-util" -version = "0.1.9" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" +checksum = "4d4cc384e1e73b93bafa6fb4f1df8c41695c8a91cf9c4c64358067d15a7b6c6b" dependencies = [ - "windows-sys 0.59.0", + "windows-sys 0.52.0", ] [[package]] @@ -2834,15 +2835,6 @@ dependencies = [ "windows-targets 0.52.6", ] -[[package]] -name = "windows-sys" -version = "0.59.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" -dependencies = [ - "windows-targets 0.52.6", -] - [[package]] name = "windows-targets" version = "0.48.5" @@ -3052,18 +3044,18 @@ dependencies = [ [[package]] name = "zstd-safe" -version = "7.2.1" +version = "7.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54a3ab4db68cea366acc5c897c7b4d4d1b8994a9cd6e6f841f8964566a419059" +checksum = "fa556e971e7b568dc775c136fc9de8c779b1c2fc3a63defaafadffdbd3181afa" dependencies = [ "zstd-sys", ] [[package]] name = "zstd-sys" -version = "2.0.13+zstd.1.5.6" +version = "2.0.12+zstd.1.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38ff0f21cfee8f97d94cef41359e0c89aa6113028ab0291aa8ca0038995a95aa" +checksum = "0a4e40c320c3cb459d9a9ff6de98cff88f4751ee9275d140e2be94a2b74e4c13" dependencies = [ "cc", "pkg-config", From 95e86d762c7f1533ed3c1e1047fed04cec97bc82 Mon Sep 17 00:00:00 2001 From: Rigidity Date: Mon, 5 Aug 2024 20:19:54 -0400 Subject: [PATCH 25/26] Remove peer discovery example --- Cargo.lock | 121 +++--------------- Cargo.toml | 1 - crates/chia-client/Cargo.toml | 1 - crates/chia-client/examples/peer_discovery.rs | 71 ---------- 4 files changed, 21 insertions(+), 173 deletions(-) delete mode 100644 crates/chia-client/examples/peer_discovery.rs diff --git a/Cargo.lock b/Cargo.lock index 39932b45d..f256c1c2c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -86,7 +86,7 @@ version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6d36fc52c7f6c869915e99412912f22093507da8d9e942ceaf66fe4b7c14422a" dependencies = [ - "windows-sys 0.52.0", + "windows-sys", ] [[package]] @@ -96,7 +96,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5bf74e1b6e971609db8ca7a9ce79fd5768ab6ae46441c572e46cf596f59e57f8" dependencies = [ "anstyle", - "windows-sys 0.52.0", + "windows-sys", ] [[package]] @@ -339,7 +339,6 @@ dependencies = [ "chia-protocol", "chia-ssl", "chia-traits 0.10.0", - "dns-lookup", "env_logger", "futures-util", "hex", @@ -905,18 +904,6 @@ dependencies = [ "syn 2.0.72", ] -[[package]] -name = "dns-lookup" -version = "2.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5766087c2235fec47fafa4cfecc81e494ee679d0fd4a59887ea0919bfb0e4fc" -dependencies = [ - "cfg-if", - "libc", - "socket2", - "windows-sys 0.48.0", -] - [[package]] name = "ecdsa" version = "0.16.9" @@ -993,7 +980,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" dependencies = [ "libc", - "windows-sys 0.52.0", + "windows-sys", ] [[package]] @@ -1343,7 +1330,7 @@ checksum = "f23ff5ef2b80d608d61efee834934d862cd92461afc0560dedf493e4c033738b" dependencies = [ "hermit-abi", "libc", - "windows-sys 0.52.0", + "windows-sys", ] [[package]] @@ -1522,7 +1509,7 @@ dependencies = [ "hermit-abi", "libc", "wasi", - "windows-sys 0.52.0", + "windows-sys", ] [[package]] @@ -1741,7 +1728,7 @@ dependencies = [ "libc", "redox_syscall", "smallvec", - "windows-targets 0.52.6", + "windows-targets", ] [[package]] @@ -2084,7 +2071,7 @@ dependencies = [ "libc", "spin", "untrusted", - "windows-sys 0.52.0", + "windows-sys", ] [[package]] @@ -2181,7 +2168,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys", - "windows-sys 0.52.0", + "windows-sys", ] [[package]] @@ -2211,7 +2198,7 @@ version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fbc91545643bcf3a0bbb6569265615222618bdf33ce4ffbbd13c4bbd4c093534" dependencies = [ - "windows-sys 0.52.0", + "windows-sys", ] [[package]] @@ -2358,7 +2345,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ce305eb0b4296696835b71df73eb912e0f1ffd2556a501fcede6e0c50349191c" dependencies = [ "libc", - "windows-sys 0.52.0", + "windows-sys", ] [[package]] @@ -2438,7 +2425,7 @@ dependencies = [ "fastrand", "once_cell", "rustix", - "windows-sys 0.52.0", + "windows-sys", ] [[package]] @@ -2561,7 +2548,7 @@ dependencies = [ "signal-hook-registry", "socket2", "tokio-macros", - "windows-sys 0.52.0", + "windows-sys", ] [[package]] @@ -2814,16 +2801,7 @@ version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4d4cc384e1e73b93bafa6fb4f1df8c41695c8a91cf9c4c64358067d15a7b6c6b" dependencies = [ - "windows-sys 0.52.0", -] - -[[package]] -name = "windows-sys" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" -dependencies = [ - "windows-targets 0.48.5", + "windows-sys", ] [[package]] @@ -2832,22 +2810,7 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ - "windows-targets 0.52.6", -] - -[[package]] -name = "windows-targets" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" -dependencies = [ - "windows_aarch64_gnullvm 0.48.5", - "windows_aarch64_msvc 0.48.5", - "windows_i686_gnu 0.48.5", - "windows_i686_msvc 0.48.5", - "windows_x86_64_gnu 0.48.5", - "windows_x86_64_gnullvm 0.48.5", - "windows_x86_64_msvc 0.48.5", + "windows-targets", ] [[package]] @@ -2856,46 +2819,28 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" dependencies = [ - "windows_aarch64_gnullvm 0.52.6", - "windows_aarch64_msvc 0.52.6", - "windows_i686_gnu 0.52.6", + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", "windows_i686_gnullvm", - "windows_i686_msvc 0.52.6", - "windows_x86_64_gnu 0.52.6", - "windows_x86_64_gnullvm 0.52.6", - "windows_x86_64_msvc 0.52.6", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", ] -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" - [[package]] name = "windows_aarch64_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" -[[package]] -name = "windows_aarch64_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" - [[package]] name = "windows_aarch64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" -[[package]] -name = "windows_i686_gnu" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" - [[package]] name = "windows_i686_gnu" version = "0.52.6" @@ -2908,48 +2853,24 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" -[[package]] -name = "windows_i686_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" - [[package]] name = "windows_i686_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" -[[package]] -name = "windows_x86_64_gnu" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" - [[package]] name = "windows_x86_64_gnu" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" - [[package]] name = "windows_x86_64_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" -[[package]] -name = "windows_x86_64_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" - [[package]] name = "windows_x86_64_msvc" version = "0.52.6" diff --git a/Cargo.toml b/Cargo.toml index 8ef29bc97..8d642bbd1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -140,5 +140,4 @@ libfuzzer-sys = "0.4" wasm-bindgen = "0.2.92" log = "0.4.22" native-tls = "0.2.12" -dns-lookup = "2.0.4" env_logger = "0.11.3" diff --git a/crates/chia-client/Cargo.toml b/crates/chia-client/Cargo.toml index 502d446e3..c96e410e6 100644 --- a/crates/chia-client/Cargo.toml +++ b/crates/chia-client/Cargo.toml @@ -30,4 +30,3 @@ chia-ssl = { path = "../chia-ssl" } tokio = { workspace = true, features = ["full"] } anyhow = { workspace = true } env_logger = { workspace = true } -dns-lookup = { workspace = true } diff --git a/crates/chia-client/examples/peer_discovery.rs b/crates/chia-client/examples/peer_discovery.rs deleted file mode 100644 index a1f56e001..000000000 --- a/crates/chia-client/examples/peer_discovery.rs +++ /dev/null @@ -1,71 +0,0 @@ -use std::{net::SocketAddr, time::Duration}; - -use chia_client::{create_tls_connector, Peer}; -use chia_protocol::{Handshake, NodeType, ProtocolMessageTypes}; -use chia_ssl::ChiaCertificate; -use chia_traits::Streamable; -use dns_lookup::lookup_host; -use tokio::time::timeout; - -#[tokio::main] -async fn main() -> anyhow::Result<()> { - env_logger::init(); - - let cert = ChiaCertificate::generate()?; - let tls = create_tls_connector(cert.cert_pem.as_bytes(), cert.key_pem.as_bytes())?; - - for ip in lookup_host("dns-introducer.chia.net")? { - let Ok(response) = timeout( - Duration::from_secs(3), - Peer::connect(SocketAddr::new(ip, 8444), tls.clone()), - ) - .await - else { - log::info!("{ip} exceeded connection timeout of 3 seconds"); - continue; - }; - - let (peer, mut receiver) = response?; - - peer.send(Handshake { - network_id: "mainnet".to_string(), - protocol_version: "0.0.37".to_string(), - software_version: "0.0.0".to_string(), - server_port: 0, - node_type: NodeType::Wallet, - capabilities: vec![ - (1, "1".to_string()), - (2, "1".to_string()), - (3, "1".to_string()), - ], - }) - .await?; - - let Ok(message) = timeout(Duration::from_secs(1), receiver.recv()).await else { - log::info!("{ip} exceeded timeout of 1 second"); - continue; - }; - - let Some(message) = message else { - log::info!("{ip} did not send any messages"); - continue; - }; - - if message.msg_type != ProtocolMessageTypes::Handshake { - log::info!("{ip} sent an unexpected message {:?}", message.msg_type); - continue; - } - - let Ok(handshake) = Handshake::from_bytes(&message.data) else { - log::info!("{ip} sent an invalid handshake"); - continue; - }; - - log::info!( - "{ip} handshake sent with protocol version {}", - handshake.protocol_version - ); - } - - Ok(()) -} From 0e820fdabd01e5890f10dccf2922b27eb7a5ac48 Mon Sep 17 00:00:00 2001 From: Rigidity Date: Mon, 5 Aug 2024 20:20:32 -0400 Subject: [PATCH 26/26] Revert lint --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 8d642bbd1..f27f83cd8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -26,7 +26,7 @@ unused_imports = "warn" unused_import_braces = "deny" unreachable_code = "deny" unreachable_patterns = "deny" -dead_code = "warn" +dead_code = "deny" deprecated = "deny" deprecated_in_future = "deny" trivial_casts = "deny"