From 392653c659335a07dfb47ae8959e0c7d66182244 Mon Sep 17 00:00:00 2001 From: SyntheticBird Date: Thu, 5 Jun 2025 19:54:19 +0000 Subject: [PATCH 01/16] Define Tor Zone, add onion addressing and more (#481) * Define Tor Zone, add onion addressing, extend AddressBook, adapt `handle_timed_sync_request`, changes in cuprated + some cleanup In `cuprated`: - Added `Z: NetworkZone` as a generic requirement for `address_book_config`. Now takes the optional node address in argument. - Added `tor_net_seed_nodes` fn for obtaining tor seed nodes. - Added `CrossNetworkInternalPeerId::Tor(_)` variant and `From>` In `cuprate-wire`: - Added `src/network_address/onion_addr.rs` implementing `OnionAddr` type used by `Tor` network zone. - Implemented parsing, formatting, conversion and validation of `OnionAddr`. - Implemented 2 validation tests and 2 parsing tests for `OnionAddr`. - Documented and cleaned up `src/network_address/epee_builder.rs`. - Changed `u8` `type` field of `TaggedNetworkAddress` to `AddressType` enum equivalent to monerod's `address_type`. - Added additional `host` and `port` fields to `AllFieldedNetworkAddress` collection type. - Added `NetworkAddress:Tor` variant and added conversion to related functions. In `cuprate-address-book`: - Added `our_own_addr: Z::Addr` field to AddressBookConfig. This adds a `Z: NetworkZone` requirement to `AddressBookConfig`. - Adapted code to the new generic requirement. - Implemented handling of new `AddressBookRequest::OwnAddress` for querying the node self specified address for the zone. In `cuprate-p2p`: - If `Z::BROADCAST_OUR_OWN_ADDR` = `true`, `handle_timed_sync_request` will insert the node's address to the peerlist being sent. In `cuprate-p2p-core`: - Removed `#[async_trait::async_trait]` attribute to `impl NetworkZone for *`. - Added `AddressBookRequest::OwnAddress` and `AddressBookResponse::OwnAddress(Option)`. - Defined new `Tor` `NetworkZone` * fmt * fix typo and fmt * final edits? * fix test * forgor --- Cargo.lock | 2 + binaries/cuprated/src/config.rs | 10 +- binaries/cuprated/src/config/p2p.rs | 37 ++- binaries/cuprated/src/p2p/network_address.rs | 15 +- net/wire/Cargo.toml | 9 +- net/wire/src/lib.rs | 2 +- net/wire/src/network_address.rs | 8 +- net/wire/src/network_address/epee_builder.rs | 62 ++++- net/wire/src/network_address/onion_addr.rs | 233 ++++++++++++++++++ p2p/address-book/src/book.rs | 7 +- p2p/address-book/src/book/tests.rs | 5 +- p2p/address-book/src/lib.rs | 7 +- p2p/address-book/src/store.rs | 4 +- p2p/p2p-core/Cargo.toml | 1 + .../src/client/handshaker/builder/dummy.rs | 1 + p2p/p2p-core/src/client/request_handler.rs | 64 ++++- p2p/p2p-core/src/lib.rs | 2 +- p2p/p2p-core/src/network_zones.rs | 2 + p2p/p2p-core/src/network_zones/clear.rs | 1 - p2p/p2p-core/src/network_zones/tor.rs | 52 ++++ p2p/p2p-core/src/services.rs | 9 + p2p/p2p/src/config.rs | 2 +- test-utils/src/test_netzone.rs | 3 +- 23 files changed, 490 insertions(+), 48 deletions(-) create mode 100644 net/wire/src/network_address/onion_addr.rs create mode 100644 p2p/p2p-core/src/network_zones/tor.rs diff --git a/Cargo.lock b/Cargo.lock index fd89ca7..352b4a2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -951,6 +951,7 @@ dependencies = [ "futures", "hex", "hex-literal", + "rand", "thiserror", "tokio", "tokio-stream", @@ -1095,6 +1096,7 @@ dependencies = [ "cuprate-levin", "cuprate-types", "hex", + "proptest", "thiserror", ] diff --git a/binaries/cuprated/src/config.rs b/binaries/cuprated/src/config.rs index bbd8c77..d74deeb 100644 --- a/binaries/cuprated/src/config.rs +++ b/binaries/cuprated/src/config.rs @@ -219,11 +219,11 @@ impl Config { gray_peers_percent: self.p2p.clear_net.gray_peers_percent, p2p_port: self.p2p.clear_net.p2p_port, rpc_port: self.rpc.restricted.port_for_p2p(), - address_book_config: self - .p2p - .clear_net - .address_book_config - .address_book_config(&self.fs.cache_directory, self.network), + address_book_config: self.p2p.clear_net.address_book_config.address_book_config( + &self.fs.cache_directory, + self.network, + None, + ), } } diff --git a/binaries/cuprated/src/config/p2p.rs b/binaries/cuprated/src/config/p2p.rs index d5b521d..758b9e9 100644 --- a/binaries/cuprated/src/config/p2p.rs +++ b/binaries/cuprated/src/config/p2p.rs @@ -1,4 +1,5 @@ use std::{ + marker::PhantomData, net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr}, path::Path, time::Duration, @@ -10,8 +11,9 @@ use cuprate_helper::{fs::address_book_path, network::Network}; use cuprate_p2p::config::TransportConfig; use cuprate_p2p_core::{ transports::{Tcp, TcpServerConfig}, - ClearNet, Transport, + ClearNet, NetworkZone, Transport, }; +use cuprate_wire::OnionAddr; use super::macros::config_struct; @@ -266,16 +268,23 @@ impl Default for AddressBookConfig { impl AddressBookConfig { /// Returns the [`cuprate_address_book::AddressBookConfig`]. - pub fn address_book_config( + pub fn address_book_config( &self, cache_dir: &Path, network: Network, - ) -> cuprate_address_book::AddressBookConfig { + our_own_address: Option, + ) -> cuprate_address_book::AddressBookConfig { + assert!( + !Z::BROADCAST_OWN_ADDR && our_own_address.is_some(), + "This network DO NOT take an incoming address." + ); + cuprate_address_book::AddressBookConfig { max_white_list_length: self.max_white_list_length, max_gray_list_length: self.max_gray_list_length, peer_store_directory: address_book_path(cache_dir, network), peer_save_period: self.peer_save_period, + our_own_address, } } } @@ -317,3 +326,25 @@ pub fn clear_net_seed_nodes(network: Network) -> Vec { .collect::>() .unwrap() } + +/// Seed nodes for `Tor`. +pub fn tor_net_seed_nodes(network: Network) -> Vec { + let seeds = match network { + Network::Mainnet => [ + "zbjkbsxc5munw3qusl7j2hpcmikhqocdf4pqhnhtpzw5nt5jrmofptid.onion:18083", + "qz43zul2x56jexzoqgkx2trzwcfnr6l3hbtfcfx54g4r3eahy3bssjyd.onion:18083", + "plowsof3t5hogddwabaeiyrno25efmzfxyro2vligremt7sxpsclfaid.onion:18083", + "plowsoffjexmxalw73tkjmf422gq6575fc7vicuu4javzn2ynnte6tyd.onion:18083", + "plowsofe6cleftfmk2raiw5h2x66atrik3nja4bfd3zrfa2hdlgworad.onion:18083", + "aclc4e2jhhtr44guufbnwk5bzwhaecinax4yip4wr4tjn27sjsfg6zqd.onion:18083", + ] + .as_slice(), + Network::Stagenet | Network::Testnet => [].as_slice(), + }; + + seeds + .iter() + .map(|s| s.parse()) + .collect::>() + .unwrap() +} diff --git a/binaries/cuprated/src/p2p/network_address.rs b/binaries/cuprated/src/p2p/network_address.rs index 7fa8e86..a342316 100644 --- a/binaries/cuprated/src/p2p/network_address.rs +++ b/binaries/cuprated/src/p2p/network_address.rs @@ -1,16 +1,25 @@ use std::net::SocketAddr; -use cuprate_p2p_core::{client::InternalPeerID, ClearNet, NetworkZone}; +use cuprate_p2p_core::{client::InternalPeerID, ClearNet, NetworkZone, Tor}; +use cuprate_wire::OnionAddr; /// An identifier for a P2P peer on any network. #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub enum CrossNetworkInternalPeerId { /// A clear-net peer. ClearNet(InternalPeerID<::Addr>), + /// A Tor onion peer. + Tor(InternalPeerID<::Addr>), } -impl From::Addr>> for CrossNetworkInternalPeerId { - fn from(addr: InternalPeerID<::Addr>) -> Self { +impl From> for CrossNetworkInternalPeerId { + fn from(addr: InternalPeerID) -> Self { Self::ClearNet(addr) } } + +impl From> for CrossNetworkInternalPeerId { + fn from(addr: InternalPeerID) -> Self { + Self::Tor(addr) + } +} diff --git a/net/wire/Cargo.toml b/net/wire/Cargo.toml index 3438091..84f413c 100644 --- a/net/wire/Cargo.toml +++ b/net/wire/Cargo.toml @@ -17,14 +17,15 @@ cuprate-fixed-bytes = { workspace = true } cuprate-types = { workspace = true, default-features = false, features = ["epee"] } cuprate-helper = { workspace = true, default-features = false, features = ["map"] } -bitflags = { workspace = true, features = ["std"] } -bytes = { workspace = true, features = ["std"] } -thiserror = { workspace = true } +bitflags = { workspace = true, features = ["std"] } +bytes = { workspace = true, features = ["std"] } +thiserror = { workspace = true } arbitrary = { workspace = true, features = ["derive"], optional = true } [dev-dependencies] -hex = { workspace = true, features = ["std"]} +hex = { workspace = true, features = ["std"]} +proptest = { workspace = true } [lints] workspace = true diff --git a/net/wire/src/lib.rs b/net/wire/src/lib.rs index 674a2e9..cc33905 100644 --- a/net/wire/src/lib.rs +++ b/net/wire/src/lib.rs @@ -26,7 +26,7 @@ pub mod network_address; pub mod p2p; pub use cuprate_levin::BucketError; -pub use network_address::{NetZone, NetworkAddress}; +pub use network_address::{NetZone, NetworkAddress, OnionAddr}; pub use p2p::*; // re-export. diff --git a/net/wire/src/network_address.rs b/net/wire/src/network_address.rs index 3e15c46..91c92f7 100644 --- a/net/wire/src/network_address.rs +++ b/net/wire/src/network_address.rs @@ -26,6 +26,9 @@ use cuprate_epee_encoding::EpeeObject; mod epee_builder; use epee_builder::*; +mod onion_addr; +pub use onion_addr::*; + #[derive(Debug, PartialEq, Eq, Clone, Copy)] pub enum NetZone { Public, @@ -38,6 +41,7 @@ pub enum NetZone { #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] pub enum NetworkAddress { Clear(SocketAddr), + Tor(OnionAddr), } impl EpeeObject for NetworkAddress { @@ -56,6 +60,7 @@ impl NetworkAddress { pub const fn get_zone(&self) -> NetZone { match self { Self::Clear(_) => NetZone::Public, + Self::Tor(_) => NetZone::Tor, } } @@ -72,6 +77,7 @@ impl NetworkAddress { pub const fn port(&self) -> u16 { match self { Self::Clear(ip) => ip.port(), + Self::Tor(addr) => addr.port(), } } } @@ -106,7 +112,7 @@ impl TryFrom for SocketAddr { fn try_from(value: NetworkAddress) -> Result { match value { NetworkAddress::Clear(addr) => Ok(addr), - //_ => Err(NetworkAddressIncorrectZone) + NetworkAddress::Tor(_) => Err(NetworkAddressIncorrectZone), } } } diff --git a/net/wire/src/network_address/epee_builder.rs b/net/wire/src/network_address/epee_builder.rs index 8b14644..1c20649 100644 --- a/net/wire/src/network_address/epee_builder.rs +++ b/net/wire/src/network_address/epee_builder.rs @@ -1,21 +1,37 @@ +//! Address epee serialization +//! +//! Addresses needs to be serialized into a specific format before being sent to other peers. +//! This module is handling this particular construction. +//! + +//---------------------------------------------------------------------------------------------------- Imports + use std::net::{Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6}; use bytes::Buf; use thiserror::Error; use cuprate_epee_encoding::{epee_object, EpeeObjectBuilder}; +use cuprate_types::AddressType; use crate::NetworkAddress; +use super::OnionAddr; + +//---------------------------------------------------------------------------------------------------- Network address construction + #[derive(Default)] +/// A serialized network address being communicated to or from a peer. pub struct TaggedNetworkAddress { - ty: Option, + /// Type of the network address (used later for conversion) + ty: Option, + /// All possible fields for a network address addr: Option, } epee_object!( TaggedNetworkAddress, - ty("type"): Option, + ty("type"): Option, addr: Option, ); @@ -75,31 +91,57 @@ impl From for TaggedNetworkAddress { match value { NetworkAddress::Clear(addr) => match addr { SocketAddr::V4(addr) => Self { - ty: Some(1), + ty: Some(AddressType::Ipv4), addr: Some(AllFieldsNetworkAddress { m_ip: Some(u32::from_le_bytes(addr.ip().octets())), m_port: Some(addr.port()), addr: None, + host: None, + port: None, }), }, SocketAddr::V6(addr) => Self { - ty: Some(2), + ty: Some(AddressType::Ipv6), addr: Some(AllFieldsNetworkAddress { addr: Some(addr.ip().octets()), m_port: Some(addr.port()), m_ip: None, + host: None, + port: None, }), }, }, + NetworkAddress::Tor(onion_addr) => Self { + ty: Some(AddressType::Tor), + addr: Some(AllFieldsNetworkAddress { + m_ip: None, + m_port: None, + addr: None, + host: Some(onion_addr.addr_string()), + port: Some(onion_addr.port()), + }), + }, } } } #[derive(Default)] +/// There are no ordering guarantees in epee format and as such all potential fields can be collected during deserialization. +/// The [`AllFieldsNetworkAddress`] is containing, as its name suggest, all optional field describing an address , as if it +/// could be of any type. struct AllFieldsNetworkAddress { + /// IPv4 address m_ip: Option, + /// IP port field m_port: Option, + + /// IPv6 address addr: Option<[u8; 16]>, + + /// Alternative network domain name (.onion or .i2p) + host: Option, + /// Alternative network virtual port + port: Option, } epee_object!( @@ -107,21 +149,27 @@ epee_object!( m_ip: Option, m_port: Option, addr: Option<[u8; 16]>, + host: Option, + port: Option, ); impl AllFieldsNetworkAddress { - fn try_into_network_address(self, ty: u8) -> Option { + fn try_into_network_address(self, ty: AddressType) -> Option { Some(match ty { - 1 => NetworkAddress::from(SocketAddrV4::new( + AddressType::Ipv4 => NetworkAddress::from(SocketAddrV4::new( Ipv4Addr::from(self.m_ip?.to_le_bytes()), self.m_port?, )), - 2 => NetworkAddress::from(SocketAddrV6::new( + AddressType::Ipv6 => NetworkAddress::from(SocketAddrV6::new( Ipv6Addr::from(self.addr?), self.m_port?, 0, 0, )), + AddressType::Tor => { + NetworkAddress::from(OnionAddr::new(self.host?.as_str(), self.port?).ok()?) + } + // Invalid _ => return None, }) } diff --git a/net/wire/src/network_address/onion_addr.rs b/net/wire/src/network_address/onion_addr.rs new file mode 100644 index 0000000..ce4632d --- /dev/null +++ b/net/wire/src/network_address/onion_addr.rs @@ -0,0 +1,233 @@ +//! Onion address +//! +//! This module define v3 Tor onion addresses +//! + +use std::{ + fmt::Display, + str::{self, FromStr}, +}; + +use thiserror::Error; + +use super::{NetworkAddress, NetworkAddressIncorrectZone}; + +/// A v3, `Copy`able onion address. +#[derive(Clone, Debug, Copy, PartialEq, Eq, Hash)] +pub struct OnionAddr { + /// 56 characters encoded onion v3 domain without the .onion suffix + /// + domain: [u8; 56], + /// Virtual port of the peer + pub port: u16, +} + +/// Error enum at parsing onion addresses +#[derive(Debug, Error)] +pub enum OnionAddrParsingError { + #[error("Address is either too long or short, length: {0}")] + InvalidLength(usize), + #[error("Address contain non-utf8 code point at tld byte location: {0:x}")] + NonUtf8Char(u8), + #[error("This is not an onion address, Tld: {0}")] + InvalidTld(String), + #[error("Domain contains non base32 characters")] + NonBase32Char, + #[error("Invalid version. Found: {0}")] + InvalidVersion(u8), + #[error("The checksum is invalid.")] + InvalidChecksum, + #[error("Invalid port specified")] + InvalidPort, +} + +impl OnionAddr { + /// Attempt to create an [`OnionAddr`] from a complete .onion address string and a port. + /// + /// Return an [`OnionAddrParsingError`] if the supplied `addr` is invalid. + pub fn new(addr: &str, port: u16) -> Result { + Self::check_addr(addr).map(|d| Self { domain: d, port }) + } + + /// Establish if the .onion address is valid. + /// + /// Return the 56 character domain bytes if valid, `OnionAddrParsingError` otherwise. + pub fn check_addr(addr: &str) -> Result<[u8; 56], OnionAddrParsingError> { + // v3 onion addresses are 62 characters long + if addr.len() != 62 { + return Err(OnionAddrParsingError::InvalidLength(addr.len())); + } + + let Some((domain, tld)) = addr.split_at_checked(56) else { + return Err(OnionAddrParsingError::NonUtf8Char(addr.as_bytes()[56])); + }; + + // The ".onion" suffix must be located at the 57th byte. + if tld != ".onion" { + return Err(OnionAddrParsingError::InvalidTld(String::from(tld))); + } + + // The domain part must only contain base32 characters. + if !domain + .as_bytes() + .iter() + .copied() + .all(|c| c.is_ascii_lowercase() || (b'2'..=b'7').contains(&c)) + { + return Err(OnionAddrParsingError::NonBase32Char); + } + + Ok(addr.as_bytes()[..56] + .try_into() + .unwrap_or_else(|e| panic!("We just validated address: {addr} : {e}"))) + } + + /// Generate an onion address string. + /// + /// Returns a `String` containing the onion domain name and ".onion" TLD only, in form of `zbjkbs...ofptid.onion`. + pub fn addr_string(&self) -> String { + let mut domain = str::from_utf8(&self.domain) + .expect("Onion addresses are always containing UTF-8 characters.") + .to_string(); + + domain.push_str(".onion"); + domain + } + + #[inline] + pub const fn port(&self) -> u16 { + self.port + } + + #[inline] + pub const fn domain(&self) -> [u8; 56] { + self.domain + } +} + +/// Display for [`OnionAddr`]. It prints the onion address and port, in the form of `.onion:` +impl Display for OnionAddr { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let domain = str::from_utf8(&self.domain) + .expect("Onion addresses are always containing UTF-8 characters."); + + f.write_str(domain)?; + f.write_str(".onion:")?; + self.port.fmt(f) + } +} + +/// [`OnionAddr`] parses an onion address **and a port**. +impl FromStr for OnionAddr { + type Err = OnionAddrParsingError; + + fn from_str(addr: &str) -> Result { + let (addr, port) = addr + .split_at_checked(62) + .ok_or(OnionAddrParsingError::InvalidLength(addr.len()))?; + + // Port + let port: u16 = port + .starts_with(':') + .then(|| port[1..].parse().ok()) + .flatten() + .ok_or(OnionAddrParsingError::InvalidPort)?; + + // Address + let domain = Self::check_addr(addr)?; + + Ok(Self { domain, port }) + } +} + +impl TryFrom for OnionAddr { + type Error = NetworkAddressIncorrectZone; + fn try_from(value: NetworkAddress) -> Result { + match value { + NetworkAddress::Tor(addr) => Ok(addr), + NetworkAddress::Clear(_) => Err(NetworkAddressIncorrectZone), + } + } +} + +impl From for NetworkAddress { + fn from(value: OnionAddr) -> Self { + Self::Tor(value) + } +} + +#[cfg(test)] +mod tests { + use proptest::{collection::vec, prelude::*}; + + use super::OnionAddr; + + const VALID_ONION_ADDRESSES: &[&str] = &[ + "2gzyxa5ihm7nsggfxnu52rck2vv4rvmdlkiu3zzui5du4xyclen53wid.onion", // Tor Website + "pzhdfe7jraknpj2qgu5cz2u3i4deuyfwmonvzu5i3nyw4t4bmg7o5pad.onion", // Tor Blog + "monerotoruzizulg5ttgat2emf4d6fbmiea25detrmmy7erypseyteyd.onion", // Monero Website + "sfprivg7qec6tdle7u6hdepzjibin6fn3ivm6qlwytr235rh5vc6bfqd.onion", // SethForPrivacy + "yucmgsbw7nknw7oi3bkuwudvc657g2xcqahhbjyewazusyytapqo4xid.onion", // P2Pool + "p2pool2giz2r5cpqicajwoazjcxkfujxswtk3jolfk2ubilhrkqam2id.onion", // P2Pool Observer + "d6ac5qatnyodxisdehb3i4m7edfvtukxzhhtyadbgaxghcxee2xadpid.onion", // Rucknium ♥ + "duckduckgogg42xjoc72x3sjasowoarfbgcmvfimaftt6twagswzczad.onion", // DuckDuckGo + "featherdvtpi7ckdbkb2yxjfwx3oyvr3xjz3oo4rszylfzjdg6pbm3id.onion", // Feather wallet + "revuo75joezkbeitqmas4ab6spbrkr4vzbhjmeuv75ovrfqfp47mtjid.onion", // Revuo + "xoe4vn5uwdztif6goazfbmogh6wh5jc4up35bqdflu6bkdc5cas5vjqd.onion", // PrivacyGuides.org + "allyouhavetodecideiswhattodowiththetimethatisgiventoyouu.onion", // Gandalf the Grey + // Tor mainnet seed nodes as of 2025-05-15 with random ports + "zbjkbsxc5munw3qusl7j2hpcmikhqocdf4pqhnhtpzw5nt5jrmofptid.onion", + "qz43zul2x56jexzoqgkx2trzwcfnr6l3hbtfcfx54g4r3eahy3bssjyd.onion", + "plowsof3t5hogddwabaeiyrno25efmzfxyro2vligremt7sxpsclfaid.onion", + "plowsoffjexmxalw73tkjmf422gq6575fc7vicuu4javzn2ynnte6tyd.onion", + "plowsofe6cleftfmk2raiw5h2x66atrik3nja4bfd3zrfa2hdlgworad.onion", + "aclc4e2jhhtr44guufbnwk5bzwhaecinax4yip4wr4tjn27sjsfg6zqd.onion", + ]; + + #[test] + fn valid_onion_address() { + for addr in VALID_ONION_ADDRESSES { + assert!( + OnionAddr::check_addr(addr).is_ok(), + "Address {addr} has been reported as invalid." + ); + } + } + + proptest! { + #[test] + fn parse_valid_onion_address_w_port(ports in vec(any::(), 18)) { + for (addr,port) in VALID_ONION_ADDRESSES.iter().zip(ports) { + + let mut s = (*addr).to_string(); + s.push(':'); + s.push_str(&port.to_string()); + + assert!( + s.parse::().is_ok(), + "Address {addr} has been reported as invalid." + ); + } + } + + #[test] + fn invalid_onion_address(addresses in vec("[a-z][2-7]{56}.onion", 250)) { + for addr in addresses { + assert!( + OnionAddr::check_addr(&addr).is_err(), + "Address {addr} has been reported as valid." + ); + } + } + + #[test] + fn parse_invalid_onion_address_w_port(addresses in vec("[a-z][2-7]{56}.onion:[0-9]{1,5}", 250)) { + for addr in addresses { + assert!( + addr.parse::().is_err(), + "Address {addr} has been reported as valid." + ); + } + } + } +} diff --git a/p2p/address-book/src/book.rs b/p2p/address-book/src/book.rs index 5f33fc9..462be00 100644 --- a/p2p/address-book/src/book.rs +++ b/p2p/address-book/src/book.rs @@ -66,12 +66,12 @@ pub struct AddressBook { peer_save_task_handle: Option>>, peer_save_interval: Interval, - cfg: AddressBookConfig, + cfg: AddressBookConfig, } impl AddressBook { pub fn new( - cfg: AddressBookConfig, + cfg: AddressBookConfig, white_peers: Vec>, gray_peers: Vec>, anchor_peers: Vec, @@ -417,6 +417,9 @@ impl Service> for AddressBook { AddressBookRequest::GetBan(addr) => Ok(AddressBookResponse::GetBan { unban_instant: self.peer_unban_instant(&addr).map(Instant::into_std), }), + AddressBookRequest::OwnAddress => { + Ok(AddressBookResponse::OwnAddress(self.cfg.our_own_address)) + } AddressBookRequest::Peerlist | AddressBookRequest::PeerlistSize | AddressBookRequest::ConnectionCount diff --git a/p2p/address-book/src/book/tests.rs b/p2p/address-book/src/book/tests.rs index b2c4c49..050dcc2 100644 --- a/p2p/address-book/src/book/tests.rs +++ b/p2p/address-book/src/book/tests.rs @@ -3,7 +3,7 @@ use std::{path::PathBuf, time::Duration}; use futures::StreamExt; use tokio::time::interval; -use cuprate_p2p_core::handles::HandleBuilder; +use cuprate_p2p_core::{handles::HandleBuilder, NetworkZone}; use cuprate_pruning::PruningSeed; use super::{AddressBook, ConnectionPeerEntry, InternalPeerID}; @@ -11,12 +11,13 @@ use crate::{peer_list::tests::make_fake_peer_list, AddressBookConfig, AddressBoo use cuprate_test_utils::test_netzone::{TestNetZone, TestNetZoneAddr}; -fn test_cfg() -> AddressBookConfig { +fn test_cfg() -> AddressBookConfig { AddressBookConfig { max_white_list_length: 100, max_gray_list_length: 500, peer_store_directory: PathBuf::new(), peer_save_period: Duration::from_secs(60), + our_own_address: None, } } diff --git a/p2p/address-book/src/lib.rs b/p2p/address-book/src/lib.rs index 74501fe..3c70433 100644 --- a/p2p/address-book/src/lib.rs +++ b/p2p/address-book/src/lib.rs @@ -20,7 +20,7 @@ mod store; /// The address book config. #[derive(Debug, Clone)] -pub struct AddressBookConfig { +pub struct AddressBookConfig { /// The maximum number of white peers in the peer list. /// /// White peers are peers we have connected to before. @@ -33,6 +33,9 @@ pub struct AddressBookConfig { pub peer_store_directory: PathBuf, /// The amount of time between saving the address book to disk. pub peer_save_period: Duration, + + /// Our own address to advertise to peers. (Only set if `Z::BROADCAST_OWN_ADDR` = `true`) + pub our_own_address: Option, } /// Possible errors when dealing with the address book. @@ -61,7 +64,7 @@ pub enum AddressBookError { /// Initializes the P2P address book for a specific network zone. pub async fn init_address_book( - cfg: AddressBookConfig, + cfg: AddressBookConfig, ) -> Result, std::io::Error> { let (white_list, gray_list) = match store::read_peers_from_disk::(&cfg).await { Ok(res) => res, diff --git a/p2p/address-book/src/store.rs b/p2p/address-book/src/store.rs index 7682839..35354ee 100644 --- a/p2p/address-book/src/store.rs +++ b/p2p/address-book/src/store.rs @@ -27,7 +27,7 @@ struct DeserPeerDataV1 { } pub(crate) fn save_peers_to_disk( - cfg: &AddressBookConfig, + cfg: &AddressBookConfig, white_list: &PeerList, gray_list: &PeerList, ) -> JoinHandle> { @@ -51,7 +51,7 @@ pub(crate) fn save_peers_to_disk( } pub(crate) async fn read_peers_from_disk( - cfg: &AddressBookConfig, + cfg: &AddressBookConfig, ) -> Result< ( Vec>, diff --git a/p2p/p2p-core/Cargo.toml b/p2p/p2p-core/Cargo.toml index 4515e5b..dd14373 100644 --- a/p2p/p2p-core/Cargo.toml +++ b/p2p/p2p-core/Cargo.toml @@ -24,6 +24,7 @@ tower = { workspace = true, features = ["util", "tracing", "make"] } cfg-if = { workspace = true } thiserror = { workspace = true } +rand = { workspace = true, features = ["std", "std_rng"] } tracing = { workspace = true, features = ["std", "attributes"] } hex-literal = { workspace = true } diff --git a/p2p/p2p-core/src/client/handshaker/builder/dummy.rs b/p2p/p2p-core/src/client/handshaker/builder/dummy.rs index 404f7a2..2036833 100644 --- a/p2p/p2p-core/src/client/handshaker/builder/dummy.rs +++ b/p2p/p2p-core/src/client/handshaker/builder/dummy.rs @@ -108,6 +108,7 @@ impl Service> for DummyAddressBook { AddressBookRequest::GetBan(_) => AddressBookResponse::GetBan { unban_instant: None, }, + AddressBookRequest::OwnAddress => AddressBookResponse::OwnAddress(None), AddressBookRequest::Peerlist | AddressBookRequest::PeerlistSize | AddressBookRequest::ConnectionCount diff --git a/p2p/p2p-core/src/client/request_handler.rs b/p2p/p2p-core/src/client/request_handler.rs index c2f3b8e..c3aa947 100644 --- a/p2p/p2p-core/src/client/request_handler.rs +++ b/p2p/p2p-core/src/client/request_handler.rs @@ -1,6 +1,8 @@ use futures::TryFutureExt; +use rand::{thread_rng, Rng}; use tower::ServiceExt; +use cuprate_pruning::PruningSeed; use cuprate_wire::{ admin::{ PingResponse, SupportFlagsResponse, TimedSyncRequest, TimedSyncResponse, @@ -14,6 +16,7 @@ use crate::{ constants::MAX_PEERS_IN_PEER_LIST_MESSAGE, services::{ AddressBookRequest, AddressBookResponse, CoreSyncDataRequest, CoreSyncDataResponse, + ZoneSpecificPeerListEntryBase, }, AddressBook, CoreSyncSvc, NetworkZone, PeerRequest, PeerResponse, ProtocolRequestHandler, }; @@ -101,18 +104,7 @@ where *self.peer_info.core_sync_data.lock().unwrap() = req.payload_data; - let AddressBookResponse::Peers(peers) = self - .address_book_svc - .ready() - .await? - .call(AddressBookRequest::GetWhitePeers( - MAX_PEERS_IN_PEER_LIST_MESSAGE, - )) - .await? - else { - panic!("Address book sent incorrect response!"); - }; - + // Fetch core sync data. let CoreSyncDataResponse(core_sync_data) = self .our_sync_svc .ready() @@ -120,6 +112,54 @@ where .call(CoreSyncDataRequest) .await?; + // Attempt to fetch our own address if supported by this network zone. + let own_addr = if Z::BROADCAST_OWN_ADDR { + let AddressBookResponse::OwnAddress(own_addr) = self + .address_book_svc + .ready() + .await? + .call(AddressBookRequest::OwnAddress) + .await? + else { + panic!("Address book sent incorrect response!"); + }; + + own_addr + } else { + None + }; + + let mut peer_list_req_size = MAX_PEERS_IN_PEER_LIST_MESSAGE; + if own_addr.is_some() { + peer_list_req_size -= 1; + } + + // Fetch a peerlist to send + let AddressBookResponse::Peers(mut peers) = self + .address_book_svc + .ready() + .await? + .call(AddressBookRequest::GetWhitePeers(peer_list_req_size)) + .await? + else { + panic!("Address book sent incorrect response!"); + }; + + if let Some(own_addr) = own_addr { + // Append our address to the final peer list + peers.insert( + thread_rng().gen_range(0..=peers.len()), + ZoneSpecificPeerListEntryBase { + adr: own_addr, + id: self.our_basic_node_data.peer_id, + last_seen: 0, + pruning_seed: PruningSeed::NotPruned, + rpc_port: self.our_basic_node_data.rpc_port, + rpc_credits_per_hash: self.our_basic_node_data.rpc_credits_per_hash, + }, + ); + } + Ok(TimedSyncResponse { payload_data: core_sync_data, local_peerlist_new: peers.into_iter().map(Into::into).collect(), diff --git a/p2p/p2p-core/src/lib.rs b/p2p/p2p-core/src/lib.rs index 15498af..979e5e2 100644 --- a/p2p/p2p-core/src/lib.rs +++ b/p2p/p2p-core/src/lib.rs @@ -87,7 +87,7 @@ pub mod transports; pub mod types; pub use error::*; -pub use network_zones::ClearNet; +pub use network_zones::{ClearNet, Tor}; pub use protocol::*; use services::*; //re-export diff --git a/p2p/p2p-core/src/network_zones.rs b/p2p/p2p-core/src/network_zones.rs index 7c83645..c2b4aeb 100644 --- a/p2p/p2p-core/src/network_zones.rs +++ b/p2p/p2p-core/src/network_zones.rs @@ -1,3 +1,5 @@ mod clear; +mod tor; pub use clear::ClearNet; +pub use tor::Tor; diff --git a/p2p/p2p-core/src/network_zones/clear.rs b/p2p/p2p-core/src/network_zones/clear.rs index 59e0132..0ff9383 100644 --- a/p2p/p2p-core/src/network_zones/clear.rs +++ b/p2p/p2p-core/src/network_zones/clear.rs @@ -27,7 +27,6 @@ impl NetZoneAddress for SocketAddr { #[derive(Clone, Copy)] pub enum ClearNet {} -#[async_trait::async_trait] impl NetworkZone for ClearNet { const NAME: &'static str = "ClearNet"; diff --git a/p2p/p2p-core/src/network_zones/tor.rs b/p2p/p2p-core/src/network_zones/tor.rs new file mode 100644 index 0000000..25baa3e --- /dev/null +++ b/p2p/p2p-core/src/network_zones/tor.rs @@ -0,0 +1,52 @@ +//! Tor Zone +//! +//! This module define the Tor Zone that uses the Tor network and .onion service addressing. +//! +//! ### Anonymity +//! +//! This is an anonymous network and is therefore operating under the following behavior: +//! - The node address is blend into its own address book. +//! - This network is only use for relaying transactions. +//! +//! ### Addressing +//! +//! The Tor Zone is using [`OnionAddr`] as its address type. +//! + +use cuprate_wire::network_address::OnionAddr; + +use crate::{NetZoneAddress, NetworkZone}; + +impl NetZoneAddress for OnionAddr { + type BanID = [u8; 56]; + + fn set_port(&mut self, port: u16) { + self.port = port; + } + + fn ban_id(&self) -> Self::BanID { + self.domain() + } + + fn make_canonical(&mut self) { + // There are no canonical form of an onion address... + } + + fn should_add_to_peer_list(&self) -> bool { + // Validation of the onion address has been done at the type construction... + true + } +} + +#[derive(Clone, Copy)] +pub struct Tor; + +impl NetworkZone for Tor { + const NAME: &'static str = "Tor"; + + const CHECK_NODE_ID: bool = false; + + const BROADCAST_OWN_ADDR: bool = true; + + type Addr = OnionAddr; +} diff --git a/p2p/p2p-core/src/services.rs b/p2p/p2p-core/src/services.rs index a9aee45..75f86df 100644 --- a/p2p/p2p-core/src/services.rs +++ b/p2p/p2p-core/src/services.rs @@ -115,6 +115,9 @@ pub enum AddressBookRequest { /// Gets the specified number of white peers, or less if we don't have enough. GetWhitePeers(usize), + /// Gets our own optionally specified address + OwnAddress, + /// Get info on all peers, white & grey. Peerlist, @@ -175,4 +178,10 @@ pub enum AddressBookResponse { /// Response to [`AddressBookRequest::GetBans`]. GetBans(Vec>), + + /// Response to [`AddressBookRequest::OwnAddress`] + /// + /// This returns [`None`] if the address book do + /// not contain a self designated address. + OwnAddress(Option), } diff --git a/p2p/p2p/src/config.rs b/p2p/p2p/src/config.rs index 7d2d367..2bbc0ee 100644 --- a/p2p/p2p/src/config.rs +++ b/p2p/p2p/src/config.rs @@ -28,7 +28,7 @@ pub struct P2PConfig { pub rpc_port: u16, /// The [`AddressBookConfig`]. - pub address_book_config: AddressBookConfig, + pub address_book_config: AddressBookConfig, } /// Configuration part responsible of transportation diff --git a/test-utils/src/test_netzone.rs b/test-utils/src/test_netzone.rs index 91a0f0d..c618cf1 100644 --- a/test-utils/src/test_netzone.rs +++ b/test-utils/src/test_netzone.rs @@ -52,8 +52,9 @@ impl TryFrom for TestNetZoneAddr { match value { NetworkAddress::Clear(soc) => match soc { SocketAddr::V4(v4) => Ok(Self(u32::from_be_bytes(v4.ip().octets()))), - SocketAddr::V6(_) => panic!("None v4 address in test code"), + SocketAddr::V6(_) => panic!("Only IPv4 addresses are allowed in test code."), }, + NetworkAddress::Tor(_) => panic!("Only IPv4 addresses are allowed in test code."), } } } From d4caf9583833575222ee163ad753cb6afb2dd484 Mon Sep 17 00:00:00 2001 From: SyntheticBird Date: Fri, 6 Jun 2025 15:04:38 +0000 Subject: [PATCH 02/16] cuprated: Removing panicking assert in `address_book_config` (#506) Remove addressb_book_config assert. Be careful. --- binaries/cuprated/src/config/p2p.rs | 5 ----- 1 file changed, 5 deletions(-) diff --git a/binaries/cuprated/src/config/p2p.rs b/binaries/cuprated/src/config/p2p.rs index 758b9e9..2fda708 100644 --- a/binaries/cuprated/src/config/p2p.rs +++ b/binaries/cuprated/src/config/p2p.rs @@ -274,11 +274,6 @@ impl AddressBookConfig { network: Network, our_own_address: Option, ) -> cuprate_address_book::AddressBookConfig { - assert!( - !Z::BROADCAST_OWN_ADDR && our_own_address.is_some(), - "This network DO NOT take an incoming address." - ); - cuprate_address_book::AddressBookConfig { max_white_list_length: self.max_white_list_length, max_gray_list_length: self.max_gray_list_length, From e67b9f5f68702bb34fa8e2b5cb2ff078238cff9b Mon Sep 17 00:00:00 2001 From: hinto-janai Date: Sat, 7 Jun 2025 14:23:47 -0400 Subject: [PATCH 03/16] storage: fix `get_txid` for pre-RCT outputs (#504) * apply * apply * apply * apply * revert * apply * reduce diffs * apply * apply * fix --- storage/blockchain/src/ops/output.rs | 31 +++++++++++++++++----------- 1 file changed, 19 insertions(+), 12 deletions(-) diff --git a/storage/blockchain/src/ops/output.rs b/storage/blockchain/src/ops/output.rs index e8758ca..1ee92d2 100644 --- a/storage/blockchain/src/ops/output.rs +++ b/storage/blockchain/src/ops/output.rs @@ -2,17 +2,23 @@ //---------------------------------------------------------------------------------------------------- Import use curve25519_dalek::edwards::CompressedEdwardsY; -use monero_serai::transaction::{Timelock, Transaction}; +use monero_serai::transaction::Timelock; use cuprate_database::{ DbResult, RuntimeError, {DatabaseRo, DatabaseRw}, }; -use cuprate_helper::{cast::u32_to_usize, crypto::compute_zero_commitment}; -use cuprate_helper::{cast::u64_to_usize, map::u64_to_timelock}; +use cuprate_helper::{ + cast::{u32_to_usize, u64_to_usize}, + crypto::compute_zero_commitment, + map::u64_to_timelock, +}; use cuprate_types::OutputOnChain; use crate::{ - ops::macros::{doc_add_block_inner_invariant, doc_error}, + ops::{ + macros::{doc_add_block_inner_invariant, doc_error}, + tx::get_tx_from_id, + }, tables::{ BlockInfos, BlockTxsHashes, Outputs, RctOutputs, Tables, TablesMut, TxBlobs, TxUnlockTime, }, @@ -175,14 +181,16 @@ pub fn output_to_output_on_chain( let txid = if get_txid { let height = u32_to_usize(output.height); - let tx_idx = u64_to_usize(output.tx_idx); - let txid = if let Some(hash) = table_block_txs_hashes.get(&height)?.get(tx_idx) { - *hash + + let miner_tx_id = table_block_infos.get(&height)?.mining_tx_index; + + let txid = if miner_tx_id == output.tx_idx { + get_tx_from_id(&miner_tx_id, table_tx_blobs)?.hash() } else { - let miner_tx_id = table_block_infos.get(&height)?.mining_tx_index; - let tx_blob = table_tx_blobs.get(&miner_tx_id)?; - Transaction::read(&mut tx_blob.0.as_slice())?.hash() + let idx = u64_to_usize(output.tx_idx - miner_tx_id - 1); + table_block_txs_hashes.get(&height)?[idx] }; + Some(txid) } else { None @@ -234,8 +242,7 @@ pub fn rct_output_to_output_on_chain( let miner_tx_id = table_block_infos.get(&height)?.mining_tx_index; let txid = if miner_tx_id == rct_output.tx_idx { - let tx_blob = table_tx_blobs.get(&miner_tx_id)?; - Transaction::read(&mut tx_blob.0.as_slice())?.hash() + get_tx_from_id(&miner_tx_id, table_tx_blobs)?.hash() } else { let idx = u64_to_usize(rct_output.tx_idx - miner_tx_id - 1); table_block_txs_hashes.get(&height)?[idx] From b6bd4ffb1fa179703712305a58cf23eb3b523994 Mon Sep 17 00:00:00 2001 From: hinto-janai Date: Sat, 28 Jun 2025 14:02:40 +0000 Subject: [PATCH 04/16] ci: extend build environments (#480) * apply * update * fix * revert * latest * hash * use `GITHUB_SHA` * update * cargo update -p randomx-rs * alpine * bash * curl * comment alpine --- .github/workflows/ci.yml | 130 +++++++++++++++++++++++++---------- binaries/cuprated/Cargo.toml | 2 +- constants/build.rs | 37 ++++++---- 3 files changed, 118 insertions(+), 51 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3132134..79930a8 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -18,6 +18,17 @@ env: RUSTDOCFLAGS: '-D warnings' # Enable debug information generation for build dependencies. CARGO_PROFILE_DEV_BUILD_OVERRIDE_DEBUG: true + # Build commands. + CMD_DOC: cargo doc --all-features --no-deps + CMD_CLIPPY: cargo clippy --all-features --all-targets -- -D warnings + CMD_TEST: | + # HACK: how to test both DB backends that are feature-gated? + cargo test --all-features + cargo test --package cuprate-blockchain --no-default-features --features redb + CMD_BUILD: cargo build --all-features --all-targets + CMD_HACK: | + cargo install cargo-hack --locked + cargo hack check --feature-powerset --no-dev-deps jobs: # Run format separately. @@ -78,7 +89,7 @@ jobs: - name: Build WASM 32-bit run: cargo build --target wasm32-unknown-unknown -p ${{ matrix.crate }} - # All other CI. + # CI, runs on GitHub provided OSs. ci: runs-on: ${{ matrix.os }} @@ -93,46 +104,95 @@ jobs: ] steps: - - name: Checkout - uses: actions/checkout@v4 - with: - submodules: recursive + - name: Checkout + uses: actions/checkout@v4 + with: + submodules: recursive - - name: Install Rust - uses: dtolnay/rust-toolchain@master - with: - toolchain: stable - components: clippy + - name: Install Rust + uses: dtolnay/rust-toolchain@master + with: + toolchain: stable + components: clippy - - name: Cache - uses: actions/cache@v4 - with: - path: target - key: ${{ matrix.os }} + - name: Cache + uses: actions/cache@v4 + with: + path: target + key: ${{ matrix.os }} - - name: Download monerod - uses: ./.github/actions/monerod-download + - name: Download monerod + uses: ./.github/actions/monerod-download - - name: Install dependencies (Windows) - if: matrix.os == 'windows-2022' - uses: lukka/get-cmake@v3.31.6 # Needed for `randomx-rs` + - name: Install dependencies (Windows) + if: matrix.os == 'windows-2022' + uses: lukka/get-cmake@v3.31.6 # Needed for `randomx-rs` - - name: Documentation - run: cargo doc --all-features --no-deps + - name: Documentation + run: ${{ env.CMD_DOC }} - - name: Clippy (fail on warnings) - run: cargo clippy --all-features --all-targets -- -D warnings + - name: Clippy (fail on warnings) + run: ${{ env.CMD_CLIPPY }} - # HACK: how to test both DB backends that are feature-gated? - - name: Test - run: | - cargo test --all-features - cargo test --package cuprate-blockchain --no-default-features --features redb + - name: Test + run: ${{ env.CMD_TEST }} - - name: Build - run: cargo build --all-features --all-targets + - name: Build + run: ${{ env.CMD_BUILD }} - - name: Hack Check - run: | - cargo install cargo-hack --locked - cargo hack check --feature-powerset --no-dev-deps + - name: Hack Check + run: ${{ env.CMD_HACK }} + + # CI, runs in a Docker image. + ci-docker: + runs-on: ubuntu-latest + container: + image: ${{ matrix.os.image }} + + strategy: + matrix: + os: + # TODO: support musl + # - { image: alpine:latest, commands: "apk update && apk add alpine-sdk cmake curl bash" } + - { image: archlinux:latest, commands: "pacman -Syyu --noconfirm base-devel git cmake" } + - { image: debian:stable, commands: "apt update && apt -y install build-essential curl cmake git" } + - { image: fedora:latest, commands: "dnf install --assumeyes @development-tools gcc gcc-c++ cmake git" } + + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + clean: false + + - name: Cache + uses: actions/cache@v4 + with: + path: target + key: ${{ matrix.os.image }} + + - name: Dependencies + run: ${{ matrix.os.commands }} + + - name: Install Rust + uses: dtolnay/rust-toolchain@master + with: + toolchain: stable + components: clippy + + - name: Download monerod + uses: ./.github/actions/monerod-download + + - name: Documentation + run: ${{ env.CMD_DOC }} + + - name: Clippy (fail on warnings) + run: ${{ env.CMD_CLIPPY }} + + - name: Test + run: ${{ env.CMD_TEST }} + + - name: Build + run: ${{ env.CMD_BUILD }} + + - name: Hack Check + run: ${{ env.CMD_HACK }} \ No newline at end of file diff --git a/binaries/cuprated/Cargo.toml b/binaries/cuprated/Cargo.toml index cd5f814..4744d0c 100644 --- a/binaries/cuprated/Cargo.toml +++ b/binaries/cuprated/Cargo.toml @@ -94,4 +94,4 @@ cuprate-hex = { workspace = true } serde_json = { workspace = true, features = ["std"] } [lints] -workspace = true +workspace = true \ No newline at end of file diff --git a/constants/build.rs b/constants/build.rs index 518f2a3..baf7639 100644 --- a/constants/build.rs +++ b/constants/build.rs @@ -8,23 +8,30 @@ fn set_commit_env() { println!("cargo:rerun-if-changed={PATH}"); - // FIXME: This could also be `std::fs::read({PATH}/{branch})` - // so the machine building doesn't need `git`, although: - // 1. Having `git` as a build dependency is probably ok - // 2. It causes issues on PRs that aren't the `main` branch - let output = std::process::Command::new("git") - .arg("rev-parse") - .arg("HEAD") - .output() - .unwrap(); - - let commit = std::str::from_utf8(&output.stdout) + let commit = if let Ok(t) = std::env::var("GITHUB_SHA") { + t + } else { + // FIXME: This could also be `std::fs::read({PATH}/{branch})` + // so the machine building doesn't need `git`, although: + // 1. Having `git` as a build dependency is probably ok + // 2. It causes issues on PRs that aren't the `main` branch + String::from_utf8( + std::process::Command::new("git") + .args(["show", "-s", "--format=%H"]) + .output() + .unwrap() + .stdout, + ) .unwrap() - .trim() - .to_lowercase(); + } + .trim() + .to_lowercase(); - // Commit hash should always be 40 bytes long. - assert_eq!(commit.len(), 40); + assert_eq!( + commit.len(), + 40, + "Commit hash should always be 40 bytes long." + ); println!("cargo:rustc-env=COMMIT={commit}"); } From b6a0302c8189187c7b85059c81086858bd4e4352 Mon Sep 17 00:00:00 2001 From: hinto-janai Date: Thu, 3 Jul 2025 13:03:14 +0000 Subject: [PATCH 05/16] ci: install openssl, fix clippy (#513) * add openssl * typos -w * macro * lint * patch * fix --- .github/workflows/ci.yml | 6 +++--- books/architecture/src/storage/db/intro.md | 2 +- cryptonight/src/util.rs | 2 +- net/epee-encoding/src/macros.rs | 2 +- rpc/types/src/macros.rs | 6 +++--- 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 79930a8..b07be40 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -154,9 +154,9 @@ jobs: os: # TODO: support musl # - { image: alpine:latest, commands: "apk update && apk add alpine-sdk cmake curl bash" } - - { image: archlinux:latest, commands: "pacman -Syyu --noconfirm base-devel git cmake" } - - { image: debian:stable, commands: "apt update && apt -y install build-essential curl cmake git" } - - { image: fedora:latest, commands: "dnf install --assumeyes @development-tools gcc gcc-c++ cmake git" } + - { image: archlinux:latest, commands: "pacman -Syyu --noconfirm base-devel git cmake openssl" } + - { image: debian:stable, commands: "apt update && apt -y install build-essential curl cmake git libssl-dev" } + - { image: fedora:latest, commands: "dnf install --assumeyes @development-tools gcc gcc-c++ cmake git openssl-devel" } steps: - name: Checkout diff --git a/books/architecture/src/storage/db/intro.md b/books/architecture/src/storage/db/intro.md index 5973fbe..dd51f93 100644 --- a/books/architecture/src/storage/db/intro.md +++ b/books/architecture/src/storage/db/intro.md @@ -16,7 +16,7 @@ The currently implemented backends are: - [`heed`](https://github.com/meilisearch/heed) (LMDB) - [`redb`](https://github.com/cberner/redb) -Said precicely, `cuprate_database` is the embedded database other Cuprate +Said precisely, `cuprate_database` is the embedded database other Cuprate crates interact with instead of using any particular backend implementation. This allows the backend to be swapped and/or future backends to be implemented. diff --git a/cryptonight/src/util.rs b/cryptonight/src/util.rs index de8b70b..2921397 100644 --- a/cryptonight/src/util.rs +++ b/cryptonight/src/util.rs @@ -79,7 +79,7 @@ mod tests { let array = [1_u8, 2, 3, 4, 5]; let sub: &[u8; 3] = subarray(&array, 1); assert_eq!(sub, &[2, 3, 4]); - assert!(std::ptr::eq(&array[1], &sub[0])); // same memory, not copy + assert!(std::ptr::eq(&raw const array[1], &raw const sub[0])); // same memory, not copy } #[test] diff --git a/net/epee-encoding/src/macros.rs b/net/epee-encoding/src/macros.rs index c1b4568..269e438 100644 --- a/net/epee-encoding/src/macros.rs +++ b/net/epee-encoding/src/macros.rs @@ -127,7 +127,7 @@ macro_rules! epee_object { ) => { cuprate_epee_encoding::macros::paste!( - #[allow(non_snake_case)] + #[allow(non_snake_case, clippy::empty_structs_with_brackets)] mod [<__epee_builder_ $obj>] { use super::*; diff --git a/rpc/types/src/macros.rs b/rpc/types/src/macros.rs index 27ccbf6..da5c9ce 100644 --- a/rpc/types/src/macros.rs +++ b/rpc/types/src/macros.rs @@ -94,7 +94,7 @@ macro_rules! define_request_and_response { } ) => { paste::paste! { $crate::macros::define_request! { - #[allow(dead_code, missing_docs, reason = "inside a macro")] + #[allow(dead_code, missing_docs, clippy::empty_structs_with_brackets, reason = "inside a macro")] #[doc = $crate::macros::define_request_and_response_doc!( "response" => [<$type_name Response>], $monero_daemon_rpc_doc_link, @@ -119,7 +119,7 @@ macro_rules! define_request_and_response { } $crate::macros::define_response! { - #[allow(dead_code, missing_docs, reason = "inside a macro")] + #[allow(dead_code, missing_docs, clippy::empty_structs_with_brackets, reason = "inside a macro")] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[derive(Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)] #[doc = $crate::macros::define_request_and_response_doc!( @@ -236,7 +236,7 @@ macro_rules! define_request { )* } ) => { - #[allow(dead_code, missing_docs, reason = "inside a macro")] + #[allow(dead_code, missing_docs, clippy::empty_structs_with_brackets, reason = "inside a macro")] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[derive(Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)] $( #[$attr] )* From 15c106f4a5d02dc8db48484aaa57e6b7022dd1be Mon Sep 17 00:00:00 2001 From: hinto-janai Date: Wed, 16 Jul 2025 20:30:26 +0000 Subject: [PATCH 06/16] fast-sync: update height to `3456000` (#502) * apply * update --- .../fast_sync/fast_sync_hashes.json | 61 ++++++++++++++++++- 1 file changed, 60 insertions(+), 1 deletion(-) diff --git a/binaries/cuprated/src/blockchain/fast_sync/fast_sync_hashes.json b/binaries/cuprated/src/blockchain/fast_sync/fast_sync_hashes.json index d8c2e95..010b57b 100644 --- a/binaries/cuprated/src/blockchain/fast_sync/fast_sync_hashes.json +++ b/binaries/cuprated/src/blockchain/fast_sync/fast_sync_hashes.json @@ -6689,5 +6689,64 @@ "e7f6a57e22e00fe4b1920050d307913d92be3e3643705a2b4274dbc5ab2a29c9", "4d972ee9bb86b7a459fb22dde36505ae2df03d1e09ec68347a072419be93bf5e", "b305e716b0a7ec2198717bbf0c4d4639f250cfb5887115615b6bdfa27d7f53df", - "6eb51cb5f4657e2b77698da1967a55aeaadc75486742e72a81b39b2b67dae552" + "6eb51cb5f4657e2b77698da1967a55aeaadc75486742e72a81b39b2b67dae552", + "124d452cc82e080a0b5ac9c2889b494f085d98d8df452cc3e1737f43c7a840aa", + "b61ec3a754342be8e36cdc1e7d5f2e9747ae3fa118ad6f97a27f6494438f958b", + "7e28afbe2f06767c4a765ffba1a7c664f3c59ffc724a0a73916c5d6430b65460", + "427e5a2e625f42973dadf5352c0ab44dade0b19168b7501b8af8839d676cfa27", + "19ca9884dc2e0780109fcd53a46c5101837f6699aa5db40ef924bf2d50c1f6b7", + "eb358be36e0b6d5665e74ac2af929886c4ecc193251087a0d43179984310ed01", + "dcce24f62cc52181a861b042d00d69e9b80a4e1c316b788e90ea62760b82bca0", + "3c6a2fb63c170537e2f3f752c1700efc5df808a97fc81f786c2adae1eab608a3", + "f006df6b2514dde690faf30235dbc0c0570b01b713d7227cfbe4052aa98ad8c9", + "d3dbe69aa850a747b9676b0a0e07c6fa4e704cc9bfe020ae10b896af4bf67ef5", + "43adaf14c8f134500e63468edfc2adbab6ea986b85854c1816e3ec92e51d1f3f", + "ac0df0d0a2827c0b348ca80d8cdfde5fa774c087a95e3769cd2b1704e725144a", + "7c0ce77be4884dade682588587b76f72506bb1e31158bdf5724e43f0fca8c03d", + "56099ac0215bb888e7cbc046785a89836880a5a941ea6e38fcbefb5c1b5f431a", + "0b48e7e6858f491e2607bfa0305e0ae5adc8b488e35016d9a3da55be2e79449c", + "9dd6f882396b2084c0d19eade9c62d426c09a5550d29ea3d8002a66cbd7b3f65", + "1427d42e3aba9ff988fa8f01d4a7d4df0ee48fdfaffd835012504143e4d42cce", + "cb3f2ff7dbc77358fa9bf86f0ba128291ff3a4e6f6995c358c4a403f6d786502", + "6d02a0c40e8382ac340d8a6a3d90cdc6cf3cb24b7fba676ec19cafa8b2c29e17", + "c590e527df810910b1c6fc5e3279023a9ce1474c8a9bd4423caf44bf15a0246d", + "022207cd9a8f71d531110c66ead0bf9b3fc0656474cea730ab23a5467ce37b3a", + "e9f2b885bec01e4e154dc1d0cff803619da4189644860ddce0ca75c972e3d5df", + "a81a5b67d5201bf61cb7d028c7805b981ff5241a6c3824847126377f5fbb02c2", + "4509cd16ca5e31745058593335ae54956e5f5b4cf6787249bfc5c13f67e8bf70", + "89a6c5660bee4607785d73a6303bbe31518b42a21cde1630dfaeb4bcde402055", + "a1fcdbfd416ae42f2244274e65ef553bd0f429181a413dc6fab69bd1113d64e5", + "e2c0aac6f3bbcbb6abf958e19c535cb46a01a2deb6636f0e02c38d82951dfaa0", + "1e2a1f5ac5dc0f019e9f14bab80e5ad0dec01a64a9892af63a332ecb09a1f4ae", + "45b2fb06ad208ccee3bf79cd9bb85cfbe9e7ac1c0f091e79fc987a455606cc33", + "9fe65e1214a5a36d7450f35a9c600573d97a7aacc3f9c8f147542a14f825f75e", + "ad621dac9ad7ca8e21c7e2da7715f0c391c32c459438ce9c6bd342dbe423aaf6", + "f5dc9abbe5d720f38ee72167253b5f98761c099f0bbf1853d1a1c05d1303f1f4", + "90e9506eff3d8c2389dbffe36378cedf8d0e53b435a75c43dcdb585b0ef568b6", + "800582f0ed0c5a41c5002dbf3616ec8025dc7eff24ca30bb1a66aa01bfd2c2c2", + "83335160c0c598e406ece04906714652cdaba8434ca580fc693b7b892ec96d64", + "7e0350c97901bf2fc8231fda1b03fac8d42ad3e9fcd3fd8f2cd94c9ba937f2e7", + "b40ce4ef841a0857cc735d6e866ef3625daec563071263cfef571a476dff2089", + "43f0b05bff502bc49764c208f21d00b03d0f54fb7a9f437332aab3f17bbc7a65", + "a3fcc27172a0053ac5aae5ddc0aea36caeb8c6d50f9235186f1b1a0757213b64", + "6b1caa809e9466e4b5c37b9abd89681cf1420609f86cdb068054d4a198d93720", + "76fee78c21d6183cdb4ce87bb5732202cbb12716aa72799d0a2060e4dad44826", + "e8c59cf1f095f834207b03ad811f4c007149b8d725409a7fa201389c3f137a05", + "2272970d06a35ea1d4b5de9957e22c2a86563a73a9add6fd0e1b2bc5f99b4b3b", + "e1800bb4454c12708acf377459251bbdc269b94d3ab977302bb5bcb591aee717", + "bf134399845ca74a35653a5d0bb269e2998ab72bf34b0cdc7faf80a6a1fa8c9c", + "1e98b32d7bd643cf82a1b918f84c3d21688b63d902772511dbd28b2a4a9d2500", + "7fc074ac36a98268499a272be0ecb38cb798d90da96b8e0340dee7db966d05e6", + "0352892ab4c8f5364072ad65c9d96d9cff71ef3b72d9f23b8c8f462b3493d362", + "0d290d55b21802cfc16493897965236ee887b891344e09d592c3a4d97c69cdbc", + "be7080d09096d1c253be2c055066b91319fa2c51444fa314c972b256197be870", + "fc18353b059239f4f92b6254b2584482d8f2c0146852a76050f5aab877af81e2", + "a88575d9c08dd4e5204c65071add4e6d6b6abb9906a34ac190bc404a43dd86cc", + "5bb304ce2e25541151a9892164d9e35f78b6f7350c603178287d7fdd4a99412f", + "f4080d1fcb6080aeaceb04c777e2f8a5960582fa881cfc88396bd5ba2d5f364c", + "eafe0e592ec7836f820891f6b0801a33901282a840162298a8113b4f0e656372", + "6e23cbfd7df18fb4a282c0726f8cc788e3e14a14704164cd497700e274993a8b", + "d286fbd5debde93670c1feaa34e01eab9f356114c4231037638dd62a9f46ad06", + "68ba3c15eafe978a0d8d1191b61a9cb6740975996fe21ddb41da300853ca19c9", + "1b1217033a52bba8ff455d85558296ea683efde60e9e389477b01bc5f59ba258" ] \ No newline at end of file From d43e0957a2a8491e9c1987441e4a6d3cd0665fd5 Mon Sep 17 00:00:00 2001 From: hinto-janai Date: Wed, 16 Jul 2025 20:35:29 +0000 Subject: [PATCH 07/16] Update repo to `cuprated 0.0.5` (#501) * apply * ks * update * 515 --- Cargo.lock | 2 +- binaries/cuprated/Cargo.toml | 2 +- binaries/cuprated/src/constants.rs | 6 ++--- binaries/cuprated/src/killswitch.rs | 8 +++---- books/user/book.toml | 2 +- books/user/src/getting-started/download.md | 10 ++++---- misc/changelogs/cuprated/0.0.5.md | 27 ++++++++++++++++++++++ misc/changelogs/cuprated/latest.md | 2 +- 8 files changed, 43 insertions(+), 16 deletions(-) create mode 100644 misc/changelogs/cuprated/0.0.5.md diff --git a/Cargo.lock b/Cargo.lock index 352b4a2..15743c8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1113,7 +1113,7 @@ dependencies = [ [[package]] name = "cuprated" -version = "0.0.4" +version = "0.0.5" dependencies = [ "anyhow", "async-trait", diff --git a/binaries/cuprated/Cargo.toml b/binaries/cuprated/Cargo.toml index 4744d0c..64778a9 100644 --- a/binaries/cuprated/Cargo.toml +++ b/binaries/cuprated/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cuprated" -version = "0.0.4" +version = "0.0.5" edition = "2021" description = "The Cuprate Rust Monero node." license = "AGPL-3.0-only" diff --git a/binaries/cuprated/src/constants.rs b/binaries/cuprated/src/constants.rs index 29686bf..4aded0a 100644 --- a/binaries/cuprated/src/constants.rs +++ b/binaries/cuprated/src/constants.rs @@ -43,15 +43,15 @@ mod test { fn version() { let semantic_version = format!("{MAJOR_VERSION}.{MINOR_VERSION}.{PATCH_VERSION}"); assert_eq!(VERSION, VERSION); - assert_eq!(VERSION, "0.0.4"); + assert_eq!(VERSION, "0.0.5"); } #[test] fn version_build() { if cfg!(debug_assertions) { - assert_eq!(VERSION_BUILD, "0.0.4-debug"); + assert_eq!(VERSION_BUILD, "0.0.5-debug"); } else { - assert_eq!(VERSION_BUILD, "0.0.4-release"); + assert_eq!(VERSION_BUILD, "0.0.5-release"); } } } diff --git a/binaries/cuprated/src/killswitch.rs b/binaries/cuprated/src/killswitch.rs index d76c005..1f47557 100644 --- a/binaries/cuprated/src/killswitch.rs +++ b/binaries/cuprated/src/killswitch.rs @@ -32,8 +32,8 @@ const _: () = { /// The killswitch activates if the current timestamp is ahead of this timestamp. /// -/// Wed Jul 23 12:00:00 AM UTC 2025 -pub const KILLSWITCH_ACTIVATION_TIMESTAMP: u64 = 1753228800; +/// Wed Sep 3 12:00:00 AM UTC 2025 +pub const KILLSWITCH_ACTIVATION_TIMESTAMP: u64 = 1756857600; /// Check if the system clock is past a certain timestamp, /// if so, exit the entire program. @@ -44,8 +44,8 @@ fn killswitch() { /// sanity checking the system's clock to make /// sure it is not overly behind. /// - /// Wed Jun 4 12:36:45 PM UTC 2025 - const SYSTEM_CLOCK_SANITY_TIMESTAMP: u64 = 1749040605; + /// Tue Jul 15 05:20:00 PM UTC 2025 + const SYSTEM_CLOCK_SANITY_TIMESTAMP: u64 = 1752600000; let current_ts = current_unix_timestamp(); diff --git a/books/user/book.toml b/books/user/book.toml index 49d766c..dc936c0 100644 --- a/books/user/book.toml +++ b/books/user/book.toml @@ -3,7 +3,7 @@ authors = ["hinto-janai"] language = "en" multilingual = false src = "src" -title = "Cuprate User Book - v0.0.4" +title = "Cuprate User Book - v0.0.5" git-repository-url = "https://github.com/Cuprate/cuprate/books/user" [output.html] diff --git a/books/user/src/getting-started/download.md b/books/user/src/getting-started/download.md index 8b0cf01..5781e41 100644 --- a/books/user/src/getting-started/download.md +++ b/books/user/src/getting-started/download.md @@ -3,10 +3,10 @@ Cuprate offers pre-built binaries for `cuprated` for the platforms listed in [`P | Platform | Download | |------------------------------|----------| -| Windows x86_64 | -| macOS x86_64 | -| macOS ARM64 | -| Linux x86_64 (glibc >= 2.36) | -| Linux ARM64 (glibc >= 2.36) | +| Windows x86_64 | +| macOS x86_64 | +| macOS ARM64 | +| Linux x86_64 (glibc >= 2.36) | +| Linux ARM64 (glibc >= 2.36) | All release files are archived and also available at . diff --git a/misc/changelogs/cuprated/0.0.5.md b/misc/changelogs/cuprated/0.0.5.md new file mode 100644 index 0000000..0d67257 --- /dev/null +++ b/misc/changelogs/cuprated/0.0.5.md @@ -0,0 +1,27 @@ +# cuprated 0.0.5 Molybdenite (2025-07-16) +Cuprate is an alternative Monero node implementation. To get started, see: . + +## Changes +- Define Tor Zone, add onion addressing and more ([#481](https://github.com/Cuprate/cuprate/pull/481)) +- Update `fast-sync` to height `3456000` ([#502](https://github.com/Cuprate/cuprate/pull/502)) +- Fix `get_txid` for pre-RCT outputs ([#504](https://github.com/Cuprate/cuprate/pull/504)) +- RPC: enable `submit_block` and `/send_raw_transaction` ([#515](https://github.com/Cuprate/cuprate/pull/515)) + +## Downloads +For convenience, the following binaries are produced using GitHub CI in a non-reproducible way; it is highly recommended to build `cuprated` from source instead, see . + +| OS | Architecture | Download | +|---------|--------------|----------| +| Windows | 64 | +| macOS | x64 | +| macOS | ARM64 | +| Linux | x64 | +| Linux | ARM64 | + +## Contributors +Thank you to everyone who directly contributed to this release: + +- @hinto-janai +- @SyntheticBird45 + +There are other contributors that are not listed here, thank you to them as well. diff --git a/misc/changelogs/cuprated/latest.md b/misc/changelogs/cuprated/latest.md index efd3c73..d7b373d 120000 --- a/misc/changelogs/cuprated/latest.md +++ b/misc/changelogs/cuprated/latest.md @@ -1 +1 @@ -0.0.4.md \ No newline at end of file +0.0.5.md \ No newline at end of file From 97e539559a244609057c3593287e29d910941227 Mon Sep 17 00:00:00 2001 From: hinto-janai Date: Wed, 16 Jul 2025 23:34:33 +0000 Subject: [PATCH 08/16] rpc: `submit_block` + `/send_raw_transaction` (#515) * enable `submit_block` and `/send_raw_transaction` * endpoint * map * not_relayed * book * log * Update binaries/cuprated/src/rpc/service/tx_handler.rs Co-authored-by: Boog900 * review * fix --------- Co-authored-by: Boog900 --- binaries/cuprated/src/main.rs | 1 + binaries/cuprated/src/p2p/request_handler.rs | 7 +- .../cuprated/src/rpc/handlers/json_rpc.rs | 12 +++- .../cuprated/src/rpc/handlers/other_json.rs | 39 +++++++++-- binaries/cuprated/src/rpc/rpc_handler.rs | 7 +- binaries/cuprated/src/rpc/server.rs | 5 ++ binaries/cuprated/src/rpc/service.rs | 1 + .../cuprated/src/rpc/service/tx_handler.rs | 69 +++++++++++++++++++ binaries/cuprated/src/rpc/service/txpool.rs | 36 +++++++--- binaries/cuprated/src/txpool.rs | 1 + binaries/cuprated/src/txpool/incoming_tx.rs | 59 +++++++++++----- books/user/src/rpc.md | 4 +- 12 files changed, 198 insertions(+), 43 deletions(-) create mode 100644 binaries/cuprated/src/rpc/service/tx_handler.rs diff --git a/binaries/cuprated/src/main.rs b/binaries/cuprated/src/main.rs index 30aa135..0081dbb 100644 --- a/binaries/cuprated/src/main.rs +++ b/binaries/cuprated/src/main.rs @@ -162,6 +162,7 @@ fn main() { blockchain_read_handle, context_svc.clone(), txpool_read_handle, + tx_handler, ); // Start the command listener. diff --git a/binaries/cuprated/src/p2p/request_handler.rs b/binaries/cuprated/src/p2p/request_handler.rs index b7c9a93..a7df1dc 100644 --- a/binaries/cuprated/src/p2p/request_handler.rs +++ b/binaries/cuprated/src/p2p/request_handler.rs @@ -401,7 +401,12 @@ where .ready() .await .expect(PANIC_CRITICAL_SERVICE_ERROR) - .call(IncomingTxs { txs, state }) + .call(IncomingTxs { + txs, + state, + drop_relay_rule_errors: true, + do_not_relay: false, + }) .await; match res { diff --git a/binaries/cuprated/src/rpc/handlers/json_rpc.rs b/binaries/cuprated/src/rpc/handlers/json_rpc.rs index e937710..ffcbfda 100644 --- a/binaries/cuprated/src/rpc/handlers/json_rpc.rs +++ b/binaries/cuprated/src/rpc/handlers/json_rpc.rs @@ -5,6 +5,7 @@ //! use std::{ + collections::HashMap, net::{IpAddr, Ipv4Addr, SocketAddr, SocketAddrV4}, num::NonZero, time::{Duration, Instant}, @@ -58,6 +59,7 @@ use cuprate_types::{ }; use crate::{ + blockchain::interface as blockchain_interface, constants::VERSION_BUILD, rpc::{ constants::{FIELD_NOT_SUPPORTED, UNSUPPORTED_RPC_CALL}, @@ -80,7 +82,7 @@ pub async fn map_request( Req::GetBlockTemplate(r) => Resp::GetBlockTemplate(not_available()?), Req::GetBlockCount(r) => Resp::GetBlockCount(get_block_count(state, r).await?), Req::OnGetBlockHash(r) => Resp::OnGetBlockHash(on_get_block_hash(state, r).await?), - Req::SubmitBlock(r) => Resp::SubmitBlock(not_available()?), + Req::SubmitBlock(r) => Resp::SubmitBlock(submit_block(state, r).await?), Req::GenerateBlocks(r) => Resp::GenerateBlocks(not_available()?), Req::GetLastBlockHeader(r) => { Resp::GetLastBlockHeader(get_last_block_header(state, r).await?) @@ -234,7 +236,13 @@ async fn submit_block( let block_id = Hex(block.hash()); // Attempt to relay the block. - blockchain_manager::relay_block(todo!(), Box::new(block)).await?; + blockchain_interface::handle_incoming_block( + block, + HashMap::new(), // this function reads the txpool + &mut state.blockchain_read, + &mut state.txpool_read, + ) + .await?; Ok(SubmitBlockResponse { base: helper::response_base(false), diff --git a/binaries/cuprated/src/rpc/handlers/other_json.rs b/binaries/cuprated/src/rpc/handlers/other_json.rs index 31dcf5a..d5d556a 100644 --- a/binaries/cuprated/src/rpc/handlers/other_json.rs +++ b/binaries/cuprated/src/rpc/handlers/other_json.rs @@ -16,6 +16,7 @@ use cuprate_constants::rpc::{ MAX_RESTRICTED_GLOBAL_FAKE_OUTS_COUNT, RESTRICTED_SPENT_KEY_IMAGES_COUNT, RESTRICTED_TRANSACTIONS_COUNT, }; +use cuprate_dandelion_tower::TxState; use cuprate_helper::cast::usize_to_u64; use cuprate_hex::{Hex, HexVec}; use cuprate_p2p_core::{client::handshaker::builder::DummyAddressBook, ClearNet}; @@ -49,11 +50,17 @@ use cuprate_types::{ use crate::{ rpc::{ constants::UNSUPPORTED_RPC_CALL, - handlers::{helper, shared, shared::not_available}, - service::{address_book, blockchain, blockchain_context, blockchain_manager, txpool}, + handlers::{ + helper, + shared::{self, not_available}, + }, + service::{ + address_book, blockchain, blockchain_context, blockchain_manager, tx_handler, txpool, + }, CupratedRpcHandler, }, statics::START_INSTANT_UNIX, + txpool::IncomingTxs, }; /// Map a [`OtherRequest`] to the function that will lead to a [`OtherResponse`]. @@ -69,7 +76,9 @@ pub async fn map_request( Req::GetTransactions(r) => Resp::GetTransactions(not_available()?), Req::GetAltBlocksHashes(r) => Resp::GetAltBlocksHashes(not_available()?), Req::IsKeyImageSpent(r) => Resp::IsKeyImageSpent(not_available()?), - Req::SendRawTransaction(r) => Resp::SendRawTransaction(not_available()?), + Req::SendRawTransaction(r) => { + Resp::SendRawTransaction(send_raw_transaction(state, r).await?) + } Req::SaveBc(r) => Resp::SaveBc(not_available()?), Req::GetPeerList(r) => Resp::GetPeerList(not_available()?), Req::SetLogLevel(r) => Resp::SetLogLevel(not_available()?), @@ -442,14 +451,32 @@ async fn send_raw_transaction( } } - // TODO: handle to txpool service. - let tx_relay_checks = - txpool::check_maybe_relay_local(todo!(), tx, !request.do_not_relay).await?; + if state.is_restricted() && request.do_not_relay { + // FIXME: implement something like `/check_tx` in `cuprated/monerod`. + // boog900: + // > making nodes hold txs in their pool that don't get passed + // > around the network can cause issues, like targeted tx pool double spends + // > there is also no reason to have this for public RPC + return Err(anyhow!("do_not_relay is not supported on restricted RPC")); + } + + let txs = vec![tx.serialize().into()]; + + let mut txs = IncomingTxs { + txs, + state: TxState::Local, + drop_relay_rule_errors: false, + do_not_relay: request.do_not_relay, + }; + + let tx_relay_checks = tx_handler::handle_incoming_txs(&mut state.tx_handler, txs).await?; if tx_relay_checks.is_empty() { return Ok(resp); } + resp.not_relayed = true; + // fn add_reason(reasons: &mut String, reason: &'static str) { if !reasons.is_empty() { diff --git a/binaries/cuprated/src/rpc/rpc_handler.rs b/binaries/cuprated/src/rpc/rpc_handler.rs index 1fb039a..9a8ce45 100644 --- a/binaries/cuprated/src/rpc/rpc_handler.rs +++ b/binaries/cuprated/src/rpc/rpc_handler.rs @@ -19,7 +19,7 @@ use cuprate_rpc_types::{ use cuprate_txpool::service::TxpoolReadHandle; use cuprate_types::BlockTemplate; -use crate::rpc::handlers; +use crate::{rpc::handlers, txpool::IncomingTxHandler}; /// TODO: use real type when public. #[derive(Clone)] @@ -169,7 +169,8 @@ pub struct CupratedRpcHandler { /// Read handle to the transaction pool database. pub txpool_read: TxpoolReadHandle, - // TODO: handle to txpool service. + + pub tx_handler: IncomingTxHandler, } impl CupratedRpcHandler { @@ -179,12 +180,14 @@ impl CupratedRpcHandler { blockchain_read: BlockchainReadHandle, blockchain_context: BlockchainContextService, txpool_read: TxpoolReadHandle, + tx_handler: IncomingTxHandler, ) -> Self { Self { restricted, blockchain_read, blockchain_context, txpool_read, + tx_handler, } } } diff --git a/binaries/cuprated/src/rpc/server.rs b/binaries/cuprated/src/rpc/server.rs index 9c6b15a..1f14d19 100644 --- a/binaries/cuprated/src/rpc/server.rs +++ b/binaries/cuprated/src/rpc/server.rs @@ -19,6 +19,7 @@ use cuprate_txpool::service::TxpoolReadHandle; use crate::{ config::RpcConfig, rpc::{rpc_handler::BlockchainManagerHandle, CupratedRpcHandler}, + txpool::IncomingTxHandler, }; /// Initialize the RPC server(s). @@ -33,6 +34,7 @@ pub fn init_rpc_servers( blockchain_read: BlockchainReadHandle, blockchain_context: BlockchainContextService, txpool_read: TxpoolReadHandle, + tx_handler: IncomingTxHandler, ) { for ((enable, addr, request_byte_limit), restricted) in [ ( @@ -76,6 +78,7 @@ pub fn init_rpc_servers( blockchain_read.clone(), blockchain_context.clone(), txpool_read.clone(), + tx_handler.clone(), ); tokio::task::spawn(async move { @@ -107,6 +110,8 @@ async fn run_rpc_server( let router = RouterBuilder::new() .json_rpc() .other_get_height() + .other_send_raw_transaction() + .other_sendrawtransaction() .fallback() .build() .with_state(rpc_handler); diff --git a/binaries/cuprated/src/rpc/service.rs b/binaries/cuprated/src/rpc/service.rs index c85df33..6899054 100644 --- a/binaries/cuprated/src/rpc/service.rs +++ b/binaries/cuprated/src/rpc/service.rs @@ -16,4 +16,5 @@ pub(super) mod address_book; pub(super) mod blockchain; pub(super) mod blockchain_context; pub(super) mod blockchain_manager; +pub(super) mod tx_handler; pub(super) mod txpool; diff --git a/binaries/cuprated/src/rpc/service/tx_handler.rs b/binaries/cuprated/src/rpc/service/tx_handler.rs new file mode 100644 index 0000000..683822c --- /dev/null +++ b/binaries/cuprated/src/rpc/service/tx_handler.rs @@ -0,0 +1,69 @@ +use anyhow::{anyhow, Error}; +use cuprate_consensus::ExtendedConsensusError; +use cuprate_consensus_rules::{transactions::TransactionError, ConsensusError}; +use tower::{Service, ServiceExt}; + +use cuprate_types::TxRelayChecks; + +use crate::txpool::{IncomingTxError, IncomingTxHandler, IncomingTxs, RelayRuleError}; + +pub async fn handle_incoming_txs( + tx_handler: &mut IncomingTxHandler, + incoming_txs: IncomingTxs, +) -> Result { + let resp = tx_handler + .ready() + .await + .map_err(|e| anyhow!(e))? + .call(incoming_txs) + .await; + + Ok(match resp { + Ok(()) => TxRelayChecks::empty(), + Err(e) => match e { + IncomingTxError::Consensus(ExtendedConsensusError::ConErr( + ConsensusError::Transaction(e), + )) => match e { + TransactionError::TooBig => TxRelayChecks::TOO_BIG, + TransactionError::KeyImageSpent => TxRelayChecks::DOUBLE_SPEND, + + TransactionError::OutputNotValidPoint + | TransactionError::OutputTypeInvalid + | TransactionError::ZeroOutputForV1 + | TransactionError::NonZeroOutputForV2 + | TransactionError::OutputsOverflow + | TransactionError::OutputsTooHigh => TxRelayChecks::INVALID_OUTPUT, + + TransactionError::MoreThanOneMixableInputWithUnmixable + | TransactionError::InvalidNumberOfOutputs + | TransactionError::InputDoesNotHaveExpectedNumbDecoys + | TransactionError::IncorrectInputType + | TransactionError::InputsAreNotOrdered + | TransactionError::InputsOverflow + | TransactionError::NoInputs => TxRelayChecks::INVALID_INPUT, + + TransactionError::KeyImageIsNotInPrimeSubGroup + | TransactionError::AmountNotDecomposed + | TransactionError::DuplicateRingMember + | TransactionError::OneOrMoreRingMembersLocked + | TransactionError::RingMemberNotFoundOrInvalid + | TransactionError::RingSignatureIncorrect + | TransactionError::TransactionVersionInvalid + | TransactionError::RingCTError(_) => return Err(anyhow!("unreachable")), + }, + IncomingTxError::Parse(_) | IncomingTxError::Consensus(_) => { + return Err(anyhow!("unreachable")) + } + IncomingTxError::RelayRule(RelayRuleError::NonZeroTimelock) => { + TxRelayChecks::NONZERO_UNLOCK_TIME + } + IncomingTxError::RelayRule(RelayRuleError::ExtraFieldTooLarge) => { + TxRelayChecks::TX_EXTRA_TOO_BIG + } + IncomingTxError::RelayRule(RelayRuleError::FeeBelowMinimum) => { + TxRelayChecks::FEE_TOO_LOW + } + IncomingTxError::DuplicateTransaction => TxRelayChecks::DOUBLE_SPEND, + }, + }) +} diff --git a/binaries/cuprated/src/rpc/service/txpool.rs b/binaries/cuprated/src/rpc/service/txpool.rs index 01194d5..dbc06af 100644 --- a/binaries/cuprated/src/rpc/service/txpool.rs +++ b/binaries/cuprated/src/rpc/service/txpool.rs @@ -1,6 +1,10 @@ //! Functions to send [`TxpoolReadRequest`]s. -use std::{collections::HashSet, convert::Infallible, num::NonZero}; +use std::{ + collections::{HashMap, HashSet}, + convert::Infallible, + num::NonZero, +}; use anyhow::{anyhow, Error}; use monero_serai::transaction::Transaction; @@ -17,7 +21,7 @@ use cuprate_txpool::{ }; use cuprate_types::{ rpc::{PoolInfo, PoolInfoFull, PoolInfoIncremental, PoolTxInfo, TxpoolStats}, - TxInPool, TxRelayChecks, + TransactionVerificationData, TxInPool, TxRelayChecks, }; // FIXME: use `anyhow::Error` over `tower::BoxError` in txpool. @@ -222,6 +226,25 @@ pub async fn all_hashes( Ok(hashes) } +/// [`TxpoolReadRequest::TxsForBlock`] +pub async fn txs_for_block( + txpool_read: &mut TxpoolReadHandle, + tx_hashes: Vec<[u8; 32]>, +) -> Result<(HashMap<[u8; 32], TransactionVerificationData>, Vec), Error> { + let TxpoolReadResponse::TxsForBlock { txs, missing } = txpool_read + .ready() + .await + .map_err(|e| anyhow!(e))? + .call(TxpoolReadRequest::TxsForBlock(tx_hashes)) + .await + .map_err(|e| anyhow!(e))? + else { + unreachable!(); + }; + + Ok((txs, missing)) +} + /// TODO: impl txpool manager. pub async fn flush(txpool_manager: &mut Infallible, tx_hashes: Vec<[u8; 32]>) -> Result<(), Error> { todo!(); @@ -233,12 +256,3 @@ pub async fn relay(txpool_manager: &mut Infallible, tx_hashes: Vec<[u8; 32]>) -> todo!(); Ok(()) } - -/// TODO: impl txpool manager. -pub async fn check_maybe_relay_local( - txpool_manager: &mut Infallible, - tx: Transaction, - relay: bool, -) -> Result { - Ok(todo!()) -} diff --git a/binaries/cuprated/src/txpool.rs b/binaries/cuprated/src/txpool.rs index 81f84bd..eccf2ff 100644 --- a/binaries/cuprated/src/txpool.rs +++ b/binaries/cuprated/src/txpool.rs @@ -12,3 +12,4 @@ mod relay_rules; mod txs_being_handled; pub use incoming_tx::{IncomingTxError, IncomingTxHandler, IncomingTxs}; +pub use relay_rules::RelayRuleError; diff --git a/binaries/cuprated/src/txpool/incoming_tx.rs b/binaries/cuprated/src/txpool/incoming_tx.rs index cff8fd7..4f0cc6c 100644 --- a/binaries/cuprated/src/txpool/incoming_tx.rs +++ b/binaries/cuprated/src/txpool/incoming_tx.rs @@ -40,7 +40,7 @@ use crate::{ signals::REORG_LOCK, txpool::{ dandelion, - relay_rules::check_tx_relay_rules, + relay_rules::{check_tx_relay_rules, RelayRuleError}, txs_being_handled::{TxsBeingHandled, TxsBeingHandledLocally}, }, }; @@ -54,6 +54,8 @@ pub enum IncomingTxError { Consensus(ExtendedConsensusError), #[error("Duplicate tx in message")] DuplicateTransaction, + #[error("Relay rule was broken: {0}")] + RelayRule(RelayRuleError), } /// Incoming transactions. @@ -62,6 +64,13 @@ pub struct IncomingTxs { pub txs: Vec, /// The routing state of the transactions. pub state: TxState, + /// If [`true`], transactions breaking relay + /// rules will be ignored and processing will continue, + /// otherwise the service will return an early error. + pub drop_relay_rule_errors: bool, + /// If [`true`], only checks will be done, + /// the transaction will not be relayed. + pub do_not_relay: bool, } /// The transaction type used for dandelion++. @@ -148,7 +157,12 @@ impl Service for IncomingTxHandler { /// Handles the incoming txs. async fn handle_incoming_txs( - IncomingTxs { txs, state }: IncomingTxs, + IncomingTxs { + txs, + state, + drop_relay_rule_errors, + do_not_relay, + }: IncomingTxs, txs_being_handled: TxsBeingHandled, mut blockchain_context_cache: BlockchainContextService, blockchain_read_handle: ConsensusBlockchainReadHandle, @@ -183,29 +197,36 @@ async fn handle_incoming_txs( // TODO: this could be a DoS, if someone spams us with txs that violate these rules? // Maybe we should remember these invalid txs for some time to prevent them getting repeatedly sent. if let Err(e) = check_tx_relay_rules(&tx, context) { - tracing::debug!(err = %e, tx = hex::encode(tx.tx_hash), "Tx failed relay check, skipping."); + if drop_relay_rule_errors { + tracing::debug!(err = %e, tx = hex::encode(tx.tx_hash), "Tx failed relay check, skipping."); + continue; + } - continue; + return Err(IncomingTxError::RelayRule(e)); } - handle_valid_tx( - tx, - state.clone(), - &mut txpool_write_handle, - &mut dandelion_pool_manager, - ) - .await; + if !do_not_relay { + handle_valid_tx( + tx, + state.clone(), + &mut txpool_write_handle, + &mut dandelion_pool_manager, + ) + .await; + } } // Re-relay any txs we got in the block that were already in our stem pool. - for stem_tx in stem_pool_txs { - rerelay_stem_tx( - &stem_tx, - state.clone(), - &mut txpool_read_handle, - &mut dandelion_pool_manager, - ) - .await; + if !do_not_relay { + for stem_tx in stem_pool_txs { + rerelay_stem_tx( + &stem_tx, + state.clone(), + &mut txpool_read_handle, + &mut dandelion_pool_manager, + ) + .await; + } } Ok(()) diff --git a/books/user/src/rpc.md b/books/user/src/rpc.md index 8d93599..6b90467 100644 --- a/books/user/src/rpc.md +++ b/books/user/src/rpc.md @@ -60,7 +60,7 @@ This section contains the development status of endpoints/methods in `cuprated`. | `prune_blockchain` | ⚫ | | `relay_tx` | ⚪ | | `set_bans` | ⚪ | -| `submit_block` | ⚪ | +| `submit_block` | 🟠 | | `sync_info` | ⚪ | ## JSON endpoints @@ -83,7 +83,7 @@ This section contains the development status of endpoints/methods in `cuprated`. | `/out_peers` | ⚪ | | `/pop_blocks` | ⚪ | | `/save_bc` | ⚪ | -| `/send_raw_transaction` | ⚪ | +| `/send_raw_transaction` | 🟠 | | `/set_bootstrap_daemon` | ⚪ | Requires bootstrap implementation | `/set_limit` | ⚪ | | `/set_log_categories` | ⚪ | Could be re-purposed to use `tracing` filters From 9c2c942d2fcf26ed8916dc3f9be6db43d8d2ae78 Mon Sep 17 00:00:00 2001 From: SyntheticBird Date: Mon, 4 Aug 2025 14:31:25 +0000 Subject: [PATCH 09/16] Add Tor support to Cuprate (Arti, Tor Daemon, Dandelion router) (#509) In `workspace`: - New dependencies: `arti_client`, `Cuprate/tokio-socks.git`, `tor-cell`, `tor-config-path`, `tor-hsservice`, `tor-persist`, `tor-proto`, `tor-rtcompat` (yes nothing was exported). - In `deny.toml`, whitelisted `Unlicense` license for `arti_client`. In `cuprate-p2p-transport`: - Implemented `Transport` and `Transport` for `Arti`. New `ArtiClientConfig`, `ArtiServerConfig` configuration. New `OnionListener` for accepting inbound connections from arti's generated onion service. - Implemented `Transport` for `Daemon`. New `DaemonClientConfig`, `DaemonServerConfig` configuration. New `DaemonInboundStream` listening for incoming TCP connections from the tor daemon. - `DisabledListener` as a polyfill for transports with inbound disabled, such as `Transport for Arti` and in the future `Transport for Socks5`. In `cuprate-p2p-core`: - Removed `Default` and `Debug` bound from `Transport::ClientConfig` and `Transport::ServerConfig`. - Removed `Clone` bound from `Transport::ServerConfig`. In `cuprate-p2p`: - Changed some function visibility to `pub(super)` instead of `pub`. In `cuprate-wire`: - Added `borsh` dependency and `BorshSerialize` and `BorshDeserialize` derived implementation to `OnionAddr` for `BorshNetworkZone` requirement in address book. In `cuprated`: - New `tor` module containing the initialization of Arti, config helpers and context structure `TorContext` to pass down to p2p initialization function and other helpers. - New `config/tor` module containing the `[tor]` configuration table. It define tor daemon related variables, as well as arti settings. - Added `enable_inbound` field to `ClearNetConfig` to disable incoming listener by default. - Added `proxy` field to `ClearNetConfig` for enabling clearnet over arti and in the future proxy urls. - Added `TorNetConfig` for setting `Tor` network zone parameters such as listening ports, enabling arti inbound server, or setting an anonymous inbound onion address from an external daemon. - Modified `initialize_zones_p2p` to now start Tor network zone and use the correct transport depending on user configuration. - In `txpool/*`, generalized `DiffuseService`, `OutboundPeerStream` and `ConcreteDandelionRouter` for `Z: NetworkZone`. Created a new `MainDandelionRouter` service that will route local txs to a Tor router instead of clearnet if available. Adapted initialization to the changes. --------- Co-authored-by: Boog900 <54e72d8a-345f-4599-bd90-c6b9bc7d0ec5@aleeas.com> --- .github/workflows/ci.yml | 4 +- Cargo.lock | 3380 ++++++++++++++++- Cargo.toml | 8 + binaries/cuprated/Cargo.toml | 5 + binaries/cuprated/src/config.rs | 51 +- binaries/cuprated/src/config/p2p.rs | 94 +- binaries/cuprated/src/config/tor.rs | 133 + binaries/cuprated/src/main.rs | 13 +- binaries/cuprated/src/p2p.rs | 119 +- binaries/cuprated/src/p2p/request_handler.rs | 10 +- binaries/cuprated/src/tor.rs | 197 + binaries/cuprated/src/txpool/dandelion.rs | 94 +- .../src/txpool/dandelion/anon_net_service.rs | 68 + .../src/txpool/dandelion/diffuse_service.rs | 8 +- .../src/txpool/dandelion/stem_service.rs | 38 +- binaries/cuprated/src/txpool/incoming_tx.rs | 10 +- consensus/fast-sync/src/fast_sync.rs | 6 +- deny.toml | 4 +- net/epee-encoding/src/macros.rs | 3 +- net/wire/Cargo.toml | 1 + net/wire/src/network_address/onion_addr.rs | 3 +- p2p/dandelion-tower/src/pool/manager.rs | 15 +- p2p/dandelion-tower/src/pool/mod.rs | 1 + p2p/dandelion-tower/src/router.rs | 10 +- p2p/p2p-core/Cargo.toml | 2 + p2p/p2p-core/src/client.rs | 4 +- p2p/p2p-core/src/client/timeout_monitor.rs | 2 +- p2p/p2p-core/src/lib.rs | 4 +- p2p/p2p-transport/Cargo.toml | 20 + p2p/p2p-transport/src/arti.rs | 221 ++ p2p/p2p-transport/src/disabled.rs | 35 + p2p/p2p-transport/src/lib.rs | 14 +- p2p/p2p-transport/src/tor.rs | 112 + .../src/block_downloader/download_batch.rs | 2 +- p2p/p2p/src/block_downloader/request_chain.rs | 2 +- p2p/p2p/src/broadcast.rs | 18 +- p2p/p2p/src/inbound_server.rs | 2 +- p2p/p2p/src/lib.rs | 2 +- rpc/types/src/macros.rs | 1 + 39 files changed, 4536 insertions(+), 180 deletions(-) create mode 100644 binaries/cuprated/src/config/tor.rs create mode 100644 binaries/cuprated/src/tor.rs create mode 100644 binaries/cuprated/src/txpool/dandelion/anon_net_service.rs create mode 100644 p2p/p2p-transport/src/arti.rs create mode 100644 p2p/p2p-transport/src/disabled.rs create mode 100644 p2p/p2p-transport/src/tor.rs diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b07be40..8b69bd8 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -155,8 +155,8 @@ jobs: # TODO: support musl # - { image: alpine:latest, commands: "apk update && apk add alpine-sdk cmake curl bash" } - { image: archlinux:latest, commands: "pacman -Syyu --noconfirm base-devel git cmake openssl" } - - { image: debian:stable, commands: "apt update && apt -y install build-essential curl cmake git libssl-dev" } - - { image: fedora:latest, commands: "dnf install --assumeyes @development-tools gcc gcc-c++ cmake git openssl-devel" } + - { image: debian:stable, commands: "apt update && apt -y install build-essential curl cmake git pkg-config libssl-dev" } + - { image: fedora:latest, commands: "dnf install --assumeyes @development-tools gcc gcc-c++ cmake git openssl-devel perl-core" } steps: - name: Checkout diff --git a/Cargo.lock b/Cargo.lock index 15743c8..932829f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -17,6 +17,18 @@ version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" +[[package]] +name = "aes" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0" +dependencies = [ + "cfg-if", + "cipher", + "cpufeatures", + "zeroize", +] + [[package]] name = "ahash" version = "0.8.12" @@ -29,6 +41,61 @@ dependencies = [ "zerocopy", ] +[[package]] +name = "aho-corasick" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +dependencies = [ + "memchr", +] + +[[package]] +name = "amplify" +version = "4.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f7fb4ac7c881e54a8e7015e399b6112a2a5bc958b6c89ac510840ff20273b31" +dependencies = [ + "amplify_derive", + "amplify_num", + "ascii", + "getrandom 0.2.16", + "getrandom 0.3.3", + "wasm-bindgen", +] + +[[package]] +name = "amplify_derive" +version = "4.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a6309e6b8d89b36b9f959b7a8fa093583b94922a0f6438a24fb08936de4d428" +dependencies = [ + "amplify_syn", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "amplify_num" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99bcb75a2982047f733547042fc3968c0f460dfcf7d90b90dea3b2744580e9ad" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "amplify_syn" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7736fb8d473c0d83098b5bac44df6a561e20470375cd8bcae30516dc889fd62a" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "android-tzdata" version = "0.1.1" @@ -44,6 +111,12 @@ dependencies = [ "libc", ] +[[package]] +name = "anes" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299" + [[package]] name = "anstyle" version = "1.0.10" @@ -83,6 +156,101 @@ version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" +[[package]] +name = "arti-client" +version = "0.32.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "855132b9da6a7c968736dda4c1824689073e4e2009817d06f52a70153271235c" +dependencies = [ + "async-trait", + "cfg-if", + "derive-deftly 1.1.0", + "derive_builder_fork_arti", + "derive_more", + "educe", + "fs-mistrust", + "futures", + "hostname-validator", + "humantime", + "humantime-serde", + "libc", + "once_cell", + "postage", + "rand 0.9.1", + "safelog", + "serde", + "thiserror 2.0.12", + "time", + "tor-async-utils", + "tor-basic-utils", + "tor-chanmgr", + "tor-circmgr", + "tor-config", + "tor-config-path", + "tor-dirmgr", + "tor-error", + "tor-guardmgr", + "tor-hsclient", + "tor-hscrypto", + "tor-hsservice", + "tor-keymgr", + "tor-linkspec", + "tor-llcrypto", + "tor-memquota", + "tor-netdir", + "tor-netdoc", + "tor-persist", + "tor-proto", + "tor-protover", + "tor-rtcompat", + "tracing", + "void", +] + +[[package]] +name = "ascii" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d92bec98840b8f03a5ff5413de5293bfcd8bf96467cf5452609f939ec6f5de16" + +[[package]] +name = "asn1-rs" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56624a96882bb8c26d61312ae18cb45868e5a9992ea73c58e45c3101e56a1e60" +dependencies = [ + "asn1-rs-derive", + "asn1-rs-impl", + "displaydoc", + "nom", + "num-traits", + "rusticata-macros", + "thiserror 2.0.12", +] + +[[package]] +name = "asn1-rs-derive" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3109e49b1e4909e9db6515a30c633684d68cdeaa252f215214cb4fa1a5bfee2c" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.101", + "synstructure", +] + +[[package]] +name = "asn1-rs-impl" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b18050c2cd6fe86c3a76584ef5e0baf286d038cda203eb6223df2cc413565f7" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.101", +] + [[package]] name = "assert-json-diff" version = "2.0.2" @@ -93,6 +261,37 @@ dependencies = [ "serde_json", ] +[[package]] +name = "assert_matches" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b34d609dfbaf33d6889b2b7106d3ca345eacad44200913df5ba02bfd31d2ba9" + +[[package]] +name = "async-compression" +version = "0.4.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40f6024f3f856663b45fd0c9b6f2024034a702f453549449e0d84a305900dad4" +dependencies = [ + "flate2", + "futures-core", + "futures-io", + "memchr", + "pin-project-lite", +] + +[[package]] +name = "async-native-tls" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9343dc5acf07e79ff82d0c37899f079db3534d99f189a1837c8e549c99405bec" +dependencies = [ + "futures-util", + "native-tls", + "thiserror 1.0.69", + "url", +] + [[package]] name = "async-stream" version = "0.3.6" @@ -126,6 +325,49 @@ dependencies = [ "syn 2.0.101", ] +[[package]] +name = "async_executors" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a982d2f86de6137cc05c9db9a915a19886c97911f9790d04f174cede74be01a5" +dependencies = [ + "blanket", + "futures-core", + "futures-task", + "futures-util", + "pin-project", + "rustc_version", + "tokio", +] + +[[package]] +name = "asynchronous-codec" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a860072022177f903e59730004fb5dc13db9275b79bb2aef7ba8ce831956c233" +dependencies = [ + "bytes", + "futures-sink", + "futures-util", + "memchr", + "pin-project-lite", +] + +[[package]] +name = "atomic" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c59bdb34bc650a32731b31bd8f0829cc15d24a708ee31559e0bb34f2bc320cba" + +[[package]] +name = "atomic" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a89cbf775b137e9b968e67227ef7f775587cde3fd31b0d8599dbd0f598a48340" +dependencies = [ + "bytemuck", +] + [[package]] name = "atomic-waker" version = "1.1.2" @@ -208,12 +450,24 @@ dependencies = [ "windows-targets 0.52.6", ] +[[package]] +name = "base16ct" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" + [[package]] name = "base64" version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" +[[package]] +name = "base64ct" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55248b47b0caf0546f7988906588779981c43bb1bc9d0c44087278f80cdb44ba" + [[package]] name = "bincode" version = "1.3.3" @@ -223,6 +477,16 @@ dependencies = [ "serde", ] +[[package]] +name = "bincode" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36eaf5d7b090263e8150820482d5d93cd964a81e4019913c972f4edcc6edb740" +dependencies = [ + "serde", + "unty", +] + [[package]] name = "bit-set" version = "0.8.0" @@ -288,6 +552,17 @@ dependencies = [ "constant_time_eq", ] +[[package]] +name = "blanket" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0b121a9fe0df916e362fb3271088d071159cdf11db0e4182d02152850756eff" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.101", +] + [[package]] name = "block-buffer" version = "0.10.4" @@ -320,12 +595,35 @@ dependencies = [ "syn 2.0.101", ] +[[package]] +name = "bounded-vec-deque" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2225b558afc76c596898f5f1b3fc35cfce0eb1b13635cbd7d1b2a7177dc10ccd" + +[[package]] +name = "bstr" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "234113d19d0d7d613b40e86fb654acf958910802bcceab913a4f9e7cda03b1a4" +dependencies = [ + "memchr", + "regex-automata 0.4.9", + "serde", +] + [[package]] name = "bumpalo" version = "3.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1628fb46dfa0b37568d12e5edd512553eccf6a22a78e8bde00bb4aed84d5bdbf" +[[package]] +name = "by_address" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64fa3c856b712db6612c019f14756e64e4bcea13337a6b33b696333a9eaa2d06" + [[package]] name = "bytemuck" version = "1.23.0" @@ -361,6 +659,18 @@ dependencies = [ "serde", ] +[[package]] +name = "caret" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "061dc3258f029feaf9ff02b43c6af5ea67a7dfaed5d2aef36204c812e614ef9c" + +[[package]] +name = "cast" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" + [[package]] name = "cc" version = "1.2.22" @@ -393,9 +703,48 @@ dependencies = [ "android-tzdata", "iana-time-zone", "num-traits", + "serde", "windows-link", ] +[[package]] +name = "ciborium" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42e69ffd6f0917f5c029256a24d0161db17cea3997d185db0d35926308770f0e" +dependencies = [ + "ciborium-io", + "ciborium-ll", + "serde", +] + +[[package]] +name = "ciborium-io" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05afea1e0a06c9be33d539b876f1ce3692f4afea2cb41f740e7743225ed1c757" + +[[package]] +name = "ciborium-ll" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57663b653d948a338bfb3eeba9bb2fd5fcfaecb9e199e87e1eda4d9e8b240fd9" +dependencies = [ + "ciborium-io", + "half", +] + +[[package]] +name = "cipher" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" +dependencies = [ + "crypto-common", + "inout", + "zeroize", +] + [[package]] name = "clap" version = "4.5.37" @@ -414,7 +763,7 @@ checksum = "efd9466fac8543255d3b1fcad4762c5e116ffe808c8a3043d4263cd4fd4862a2" dependencies = [ "anstyle", "clap_lex", - "strsim", + "strsim 0.11.1", "terminal_size", ] @@ -445,6 +794,32 @@ dependencies = [ "cc", ] +[[package]] +name = "coarsetime" +version = "0.1.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91849686042de1b41cd81490edc83afbcb0abe5a9b6f2c4114f23ce8cca1bcf4" +dependencies = [ + "libc", + "wasix", + "wasm-bindgen", +] + +[[package]] +name = "concurrent-queue" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "const-oid" +version = "0.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" + [[package]] name = "const_format" version = "0.2.34" @@ -473,6 +848,34 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7c74b8349d32d297c9134b8c88677813a227df8f779daa29bfc29c183fe3dca6" +[[package]] +name = "convert_case" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb402b8d4c85569410425650ce3eddc7d698ed96d39a73f941b08fb63082f1e7" +dependencies = [ + "unicode-segmentation", +] + +[[package]] +name = "cookie-factory" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9885fa71e26b8ab7855e2ec7cae6e9b380edff76cd052e07c683a0319d51b3a2" +dependencies = [ + "futures", +] + +[[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" version = "0.10.0" @@ -507,6 +910,52 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "criterion" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2b12d017a929603d80db1831cd3a24082f8137ce19c69e6447f54f5fc8d692f" +dependencies = [ + "anes", + "cast", + "ciborium", + "clap", + "criterion-plot", + "is-terminal", + "itertools 0.10.5", + "num-traits", + "once_cell", + "oorandom", + "plotters", + "rayon", + "regex", + "serde", + "serde_derive", + "serde_json", + "tinytemplate", + "walkdir", +] + +[[package]] +name = "criterion-cycles-per-byte" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1029452fa751c93f8834962dd74807d69f0a6c7624d5b06625b393aeb6a14fc2" +dependencies = [ + "cfg-if", + "criterion", +] + +[[package]] +name = "criterion-plot" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b50826342786a51a89e2da3a28f1c32b06e387201bc2d19791f622c673706b1" +dependencies = [ + "cast", + "itertools 0.10.5", +] + [[package]] name = "crossbeam" version = "0.8.4" @@ -563,12 +1012,20 @@ version = "0.8.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" +[[package]] +name = "crunchy" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5" + [[package]] name = "crypto-bigint" version = "0.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0dc92fb57ca44df6db8059111ab3af99a63d5d0f8375d9972e319a379c6bab76" dependencies = [ + "generic-array", + "rand_core 0.6.4", "subtle", "zeroize", ] @@ -583,6 +1040,15 @@ dependencies = [ "typenum", ] +[[package]] +name = "ctr" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0369ee1ad671834580515889b80f2ea915f23b8be8d0daa4bbaf2ac5c7590835" +dependencies = [ + "cipher", +] + [[package]] name = "cuprate-address-book" version = "0.1.0" @@ -593,9 +1059,9 @@ dependencies = [ "cuprate-pruning", "cuprate-test-utils", "futures", - "indexmap", - "rand", - "thiserror", + "indexmap 2.9.0", + "rand 0.8.5", + "thiserror 1.0.69", "tokio", "tokio-util", "tower 0.5.1", @@ -608,7 +1074,7 @@ version = "0.1.0" dependencies = [ "futures", "pin-project", - "thiserror", + "thiserror 1.0.69", "tokio", ] @@ -629,11 +1095,11 @@ dependencies = [ "curve25519-dalek", "hex", "hex-literal", - "indexmap", + "indexmap 2.9.0", "monero-serai", "pretty_assertions", "proptest", - "rand", + "rand 0.8.5", "rayon", "serde", "tempfile", @@ -656,13 +1122,13 @@ dependencies = [ "futures", "hex", "hex-literal", - "indexmap", + "indexmap 2.9.0", "monero-serai", "proptest", "proptest-derive", - "rand", + "rand 0.8.5", "rayon", - "thiserror", + "thiserror 1.0.69", "thread_local", "tokio", "tokio-test", @@ -683,7 +1149,7 @@ dependencies = [ "monero-serai", "randomx-rs", "rayon", - "thiserror", + "thiserror 1.0.69", "thread_local", "tokio", "tokio-util", @@ -704,13 +1170,13 @@ dependencies = [ "curve25519-dalek", "hex", "hex-literal", - "indexmap", + "indexmap 2.9.0", "monero-serai", "proptest", "proptest-derive", - "rand", + "rand 0.8.5", "rayon", - "thiserror", + "thiserror 1.0.69", "tokio", "tracing", ] @@ -731,7 +1197,7 @@ dependencies = [ "seq-macro", "sha3", "skein", - "thiserror", + "thiserror 1.0.69", ] [[package]] @@ -740,9 +1206,9 @@ version = "0.1.0" dependencies = [ "futures", "proptest", - "rand", + "rand 0.8.5", "rand_distr", - "thiserror", + "thiserror 1.0.69", "tokio", "tokio-util", "tower 0.5.1", @@ -762,7 +1228,7 @@ dependencies = [ "redb", "serde", "tempfile", - "thiserror", + "thiserror 1.0.69", "tracing", ] @@ -790,7 +1256,7 @@ dependencies = [ "hex", "paste", "ref-cast", - "thiserror", + "thiserror 1.0.69", ] [[package]] @@ -823,7 +1289,7 @@ dependencies = [ "bytes", "serde", "serde_json", - "thiserror", + "thiserror 1.0.69", ] [[package]] @@ -848,7 +1314,7 @@ dependencies = [ "crossbeam", "cuprate-constants", "curve25519-dalek", - "dirs", + "dirs 5.0.1", "futures", "libc", "monero-serai", @@ -874,7 +1340,7 @@ dependencies = [ "pretty_assertions", "serde", "serde_json", - "thiserror", + "thiserror 1.0.69", ] [[package]] @@ -888,8 +1354,8 @@ dependencies = [ "cuprate-helper", "futures", "proptest", - "rand", - "thiserror", + "rand 0.8.5", + "thiserror 1.0.69", "tokio", "tokio-util", "tracing", @@ -912,14 +1378,14 @@ dependencies = [ "cuprate-types", "cuprate-wire", "futures", - "indexmap", + "indexmap 2.9.0", "monero-serai", "pin-project", "proptest", - "rand", + "rand 0.8.5", "rand_distr", "rayon", - "thiserror", + "thiserror 1.0.69", "tokio", "tokio-stream", "tokio-test", @@ -933,7 +1399,7 @@ name = "cuprate-p2p-bucket" version = "0.1.0" dependencies = [ "arrayvec", - "rand", + "rand 0.8.5", ] [[package]] @@ -951,8 +1417,8 @@ dependencies = [ "futures", "hex", "hex-literal", - "rand", - "thiserror", + "rand 0.8.5", + "thiserror 1.0.69", "tokio", "tokio-stream", "tokio-test", @@ -964,6 +1430,22 @@ dependencies = [ [[package]] name = "cuprate-p2p-transport" version = "0.1.0" +dependencies = [ + "arti-client", + "async-trait", + "cuprate-p2p-core", + "cuprate-wire", + "futures", + "tokio", + "tokio-socks", + "tokio-util", + "tor-cell", + "tor-config-path", + "tor-hsservice", + "tor-proto", + "tor-rtcompat", + "tracing", +] [[package]] name = "cuprate-pruning" @@ -971,7 +1453,7 @@ version = "0.1.0" dependencies = [ "borsh", "cuprate-constants", - "thiserror", + "thiserror 1.0.69", ] [[package]] @@ -1054,7 +1536,7 @@ dependencies = [ "rayon", "serde", "tempfile", - "thiserror", + "thiserror 1.0.69", "tokio", "tower 0.5.1", ] @@ -1072,15 +1554,15 @@ dependencies = [ "cuprate-hex", "curve25519-dalek", "hex-literal", - "indexmap", + "indexmap 2.9.0", "monero-serai", "pretty_assertions", "proptest", "proptest-derive", "serde", "serde_json", - "strum", - "thiserror", + "strum 0.26.3", + "thiserror 1.0.69", ] [[package]] @@ -1089,6 +1571,7 @@ version = "0.1.0" dependencies = [ "arbitrary", "bitflags 2.9.0", + "borsh", "bytes", "cuprate-epee-encoding", "cuprate-fixed-bytes", @@ -1097,7 +1580,7 @@ dependencies = [ "cuprate-types", "hex", "proptest", - "thiserror", + "thiserror 1.0.69", ] [[package]] @@ -1116,6 +1599,7 @@ name = "cuprated" version = "0.0.5" dependencies = [ "anyhow", + "arti-client", "async-trait", "axum", "bitflags 2.9.0", @@ -1148,6 +1632,7 @@ dependencies = [ "cuprate-levin", "cuprate-p2p", "cuprate-p2p-core", + "cuprate-p2p-transport", "cuprate-pruning", "cuprate-rpc-interface", "cuprate-rpc-types", @@ -1157,33 +1642,36 @@ dependencies = [ "cuprate-wire", "curve25519-dalek", "dashmap", - "dirs", + "dirs 5.0.1", "futures", "hex", "hex-literal", - "indexmap", + "indexmap 2.9.0", "monero-address", "monero-serai", "nu-ansi-term", "paste", "pin-project", "pretty_assertions", - "rand", + "rand 0.8.5", "rand_distr", "randomx-rs", "rayon", "serde", "serde_bytes", "serde_json", - "strum", + "strum 0.26.3", "tempfile", - "thiserror", + "thiserror 1.0.69", "thread_local", "tokio", "tokio-stream", "tokio-util", "toml", "toml_edit", + "tor-hsservice", + "tor-persist", + "tor-rtcompat", "tower 0.5.1", "tower-http", "tracing", @@ -1203,7 +1691,7 @@ dependencies = [ "digest", "fiat-crypto", "group", - "rand_core", + "rand_core 0.6.4", "rustc_version", "subtle", "zeroize", @@ -1230,12 +1718,82 @@ dependencies = [ "digest", "ff", "group", - "rand_core", + "rand_core 0.6.4", "rustversion", "subtle", "zeroize", ] +[[package]] +name = "darling" +version = "0.14.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b750cb3417fd1b327431a470f388520309479ab0bf5e323505daf0290cd3850" +dependencies = [ + "darling_core 0.14.4", + "darling_macro 0.14.4", +] + +[[package]] +name = "darling" +version = "0.20.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc7f46116c46ff9ab3eb1597a45688b6715c6e628b5c133e288e709a29bcb4ee" +dependencies = [ + "darling_core 0.20.11", + "darling_macro 0.20.11", +] + +[[package]] +name = "darling_core" +version = "0.14.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "109c1ca6e6b7f82cc233a97004ea8ed7ca123a9af07a8230878fcfda9b158bf0" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim 0.10.0", + "syn 1.0.109", +] + +[[package]] +name = "darling_core" +version = "0.20.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d00b9596d185e565c2207a0b01f8bd1a135483d02d9b7b0a54b11da8d53412e" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim 0.11.1", + "syn 2.0.101", +] + +[[package]] +name = "darling_macro" +version = "0.14.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4aab4dbc9f7611d8b55048a3a16d2d010c2c8334e46304b40ac1cc14bf3b48e" +dependencies = [ + "darling_core 0.14.4", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "darling_macro" +version = "0.20.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead" +dependencies = [ + "darling_core 0.20.11", + "quote", + "syn 2.0.101", +] + [[package]] name = "dashmap" version = "6.1.0" @@ -1250,6 +1808,37 @@ dependencies = [ "parking_lot_core", ] +[[package]] +name = "data-encoding" +version = "2.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a2330da5de22e8a3cb63252ce2abb30116bf5265e89c0e01bc17015ce30a476" + +[[package]] +name = "der" +version = "0.7.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7c1832837b905bbfb5101e07cc24c8deddf52f93225eee6ead5f4d63d53ddcb" +dependencies = [ + "const-oid", + "pem-rfc7468", + "zeroize", +] + +[[package]] +name = "der-parser" +version = "10.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07da5016415d5a3c4dd39b11ed26f915f52fc4e0dc197d87908bc916e51bc1a6" +dependencies = [ + "asn1-rs", + "cookie-factory", + "displaydoc", + "nom", + "num-traits", + "rusticata-macros", +] + [[package]] name = "deranged" version = "0.4.0" @@ -1257,6 +1846,63 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c9e6a11ca8224451684bc0d7d5a7adbf8f2fd6887261a1cfc3c0432f9d4068e" dependencies = [ "powerfmt", + "serde", +] + +[[package]] +name = "derive-deftly" +version = "0.14.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8ea84d0109517cc2253d4a679bdda1e8989e9bd86987e9e4f75ffdda0095fd1" +dependencies = [ + "derive-deftly-macros 0.14.6", + "heck", +] + +[[package]] +name = "derive-deftly" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a55a256deae70e0772adfd583c57c1403c6ddbd1d1f1f84f64e94acaecc25eeb" +dependencies = [ + "derive-deftly-macros 1.1.0", + "heck", +] + +[[package]] +name = "derive-deftly-macros" +version = "0.14.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "357422a457ccb850dc8f1c1680e0670079560feaad6c2e247e3f345c4fab8a3f" +dependencies = [ + "heck", + "indexmap 2.9.0", + "itertools 0.14.0", + "proc-macro-crate", + "proc-macro2", + "quote", + "sha3", + "strum 0.27.1", + "syn 2.0.101", + "void", +] + +[[package]] +name = "derive-deftly-macros" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47cf90c375e516cf601a57727744bdf7a547680a470a2e8a6580a12288cf0630" +dependencies = [ + "heck", + "indexmap 2.9.0", + "itertools 0.14.0", + "proc-macro-crate", + "proc-macro2", + "quote", + "sha3", + "strum 0.27.1", + "syn 2.0.101", + "void", ] [[package]] @@ -1270,6 +1916,59 @@ dependencies = [ "syn 2.0.101", ] +[[package]] +name = "derive_builder_core_fork_arti" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24c1b715c79be6328caa9a5e1a387a196ea503740f0722ec3dd8f67a9e72314d" +dependencies = [ + "darling 0.14.4", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "derive_builder_fork_arti" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3eae24d595f4d0ecc90a9a5a6d11c2bd8dafe2375ec4a1ec63250e5ade7d228" +dependencies = [ + "derive_builder_macro_fork_arti", +] + +[[package]] +name = "derive_builder_macro_fork_arti" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69887769a2489cd946bf782eb2b1bb2cb7bc88551440c94a765d4f040c08ebf3" +dependencies = [ + "derive_builder_core_fork_arti", + "syn 1.0.109", +] + +[[package]] +name = "derive_more" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "093242cf7570c207c83073cf82f79706fe7b8317e98620a47d5be7c3d8497678" +dependencies = [ + "derive_more-impl", +] + +[[package]] +name = "derive_more-impl" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bda628edc44c4bb645fbe0f758797143e4e07926f7ebf4e9bdfbd3d2ce621df3" +dependencies = [ + "convert_case", + "proc-macro2", + "quote", + "syn 2.0.101", + "unicode-xid", +] + [[package]] name = "diff" version = "0.1.13" @@ -1283,6 +1982,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ "block-buffer", + "const-oid", "crypto-common", "subtle", ] @@ -1296,17 +1996,35 @@ dependencies = [ "digest", "hex", "md-5", - "rand", + "rand 0.8.5", "sha2", ] +[[package]] +name = "directories" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16f5094c54661b38d03bd7e50df373292118db60b585c08a411c6d840017fe7d" +dependencies = [ + "dirs-sys 0.5.0", +] + [[package]] name = "dirs" version = "5.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "44c45a9d03d6676652bcb5e724c7e988de1acad23a711b5217ab9cbecbec2225" dependencies = [ - "dirs-sys", + "dirs-sys 0.4.1", +] + +[[package]] +name = "dirs" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3e8aa94d75141228480295a7d0e7feb620b1a5ad9f12bc40be62411e38cce4e" +dependencies = [ + "dirs-sys 0.5.0", ] [[package]] @@ -1317,10 +2035,22 @@ checksum = "520f05a5cbd335fae5a99ff7a6ab8627577660ee5cfd6a94a6a929b52ff0321c" dependencies = [ "libc", "option-ext", - "redox_users", + "redox_users 0.4.6", "windows-sys 0.48.0", ] +[[package]] +name = "dirs-sys" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e01a3366d27ee9890022452ee61b2b63a67e6f13f58900b651ff5665f0bb1fab" +dependencies = [ + "libc", + "option-ext", + "redox_users 0.5.0", + "windows-sys 0.59.0", +] + [[package]] name = "displaydoc" version = "0.2.5" @@ -1332,13 +2062,77 @@ dependencies = [ "syn 2.0.101", ] +[[package]] +name = "downcast-rs" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea8a8b81cacc08888170eef4d13b775126db426d0b348bee9d18c2c1eaf123cf" + [[package]] name = "doxygen-rs" version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "415b6ec780d34dcf624666747194393603d0373b7141eef01d12ee58881507d9" dependencies = [ - "phf", + "phf 0.11.3", +] + +[[package]] +name = "dyn-clone" +version = "1.0.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c7a8fb8a9fbf66c1f703fe16184d10ca0ee9d23be5b4436400408ba54a95005" + +[[package]] +name = "ecdsa" +version = "0.16.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee27f32b5c5292967d2d4a9d7f1e0b0aed2c15daded5a60300e4abb9d8020bca" +dependencies = [ + "der", + "digest", + "elliptic-curve", + "rfc6979", + "signature", + "spki", +] + +[[package]] +name = "ed25519" +version = "2.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "115531babc129696a58c64a4fef0a8bf9e9698629fb97e9e40767d235cfbcd53" +dependencies = [ + "pkcs8", + "signature", +] + +[[package]] +name = "ed25519-dalek" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a3daa8e81a3963a60642bcc1f90a670680bd4a77535faa384e9d1c79d620871" +dependencies = [ + "curve25519-dalek", + "ed25519", + "merlin", + "rand_core 0.6.4", + "serde", + "sha2", + "subtle", + "zeroize", +] + +[[package]] +name = "educe" +version = "0.4.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f0042ff8246a363dbe77d2ceedb073339e85a804b9a47636c6e016a9a32c05f" +dependencies = [ + "enum-ordinalize", + "proc-macro2", + "quote", + "syn 1.0.109", ] [[package]] @@ -1347,6 +2141,38 @@ version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" +[[package]] +name = "elliptic-curve" +version = "0.13.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5e6043086bf7973472e0c7dff2142ea0b680d30e18d9cc40f267efbf222bd47" +dependencies = [ + "base16ct", + "crypto-bigint", + "digest", + "ff", + "generic-array", + "group", + "pkcs8", + "rand_core 0.6.4", + "sec1", + "subtle", + "zeroize", +] + +[[package]] +name = "enum-ordinalize" +version = "3.1.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bf1fa3f06bbff1ea5b1a9c7b14aa992a39657db60a2759457328d7e058f49ee" +dependencies = [ + "num-bigint", + "num-traits", + "proc-macro2", + "quote", + "syn 2.0.101", +] + [[package]] name = "equivalent" version = "1.0.2" @@ -1363,6 +2189,29 @@ dependencies = [ "windows-sys 0.59.0", ] +[[package]] +name = "event-listener" +version = "5.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3492acde4c3fc54c845eaab3eed8bd00c7a7d881f78bfc801e43a93dec1331ae" +dependencies = [ + "concurrent-queue", + "parking", + "pin-project-lite", +] + +[[package]] +name = "fallible-iterator" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2acce4a10f12dc2fb14a218589d4f1f62ef011b2d0cc4b3cb1bba8e94da14649" + +[[package]] +name = "fallible-streaming-iterator" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a" + [[package]] name = "fastrand" version = "2.3.0" @@ -1376,7 +2225,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c0b50bfb653653f9ca9095b427bed08ab8d75a137839d9ad64eb11810d5b6393" dependencies = [ "bitvec", - "rand_core", + "rand_core 0.6.4", "subtle", ] @@ -1386,6 +2235,31 @@ version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d" +[[package]] +name = "figment" +version = "0.10.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8cb01cd46b0cf372153850f4c6c272d9cbea2da513e07538405148f95bd789f3" +dependencies = [ + "atomic 0.6.1", + "serde", + "toml", + "uncased", + "version_check", +] + +[[package]] +name = "filetime" +version = "0.2.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35c0522e981e68cbfa8c3f978441a5f34b30b96e146b33cd3359176b50fe8586" +dependencies = [ + "cfg-if", + "libc", + "libredox", + "windows-sys 0.59.0", +] + [[package]] name = "flate2" version = "1.1.1" @@ -1409,12 +2283,39 @@ dependencies = [ "zeroize", ] +[[package]] +name = "fluid-let" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "749cff877dc1af878a0b31a41dd221a753634401ea0ef2f87b62d3171522485a" + [[package]] name = "fnv" version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" +[[package]] +name = "foldhash" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" + +[[package]] +name = "foreign-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +dependencies = [ + "foreign-types-shared", +] + +[[package]] +name = "foreign-types-shared" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" + [[package]] name = "form_urlencoded" version = "1.2.1" @@ -1424,6 +2325,52 @@ dependencies = [ "percent-encoding", ] +[[package]] +name = "fs-mistrust" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "198b8f9ab4cff63b5c91e9e64edd4e6b43cd7fe7a52519a03c6c32ea0acfa557" +dependencies = [ + "derive_builder_fork_arti", + "dirs 6.0.0", + "libc", + "pwd-grp", + "serde", + "thiserror 2.0.12", + "walkdir", +] + +[[package]] +name = "fslock" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04412b8935272e3a9bae6f48c7bfff74c2911f60525404edfdd28e49884c3bfb" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "fslock-arti-fork" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b21bd626aaab7b904b20bef6d9e06298914a0c8d9fb8b010483766b2e532791" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "fslock-guard" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed387b899db1671a47eaf17e7e6d7008577262c09319cb8e19601371192b526f" +dependencies = [ + "fslock-arti-fork", + "thiserror 2.0.12", + "winapi", +] + [[package]] name = "funty" version = "2.0.0" @@ -1438,6 +2385,7 @@ checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876" dependencies = [ "futures-channel", "futures-core", + "futures-executor", "futures-io", "futures-sink", "futures-task", @@ -1460,6 +2408,17 @@ version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" +[[package]] +name = "futures-executor" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + [[package]] name = "futures-io" version = "0.3.31" @@ -1515,6 +2474,7 @@ checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" dependencies = [ "typenum", "version_check", + "zeroize", ] [[package]] @@ -1524,8 +2484,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592" dependencies = [ "cfg-if", + "js-sys", "libc", "wasi 0.11.0+wasi-snapshot-preview1", + "wasm-bindgen", ] [[package]] @@ -1535,9 +2497,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4" dependencies = [ "cfg-if", + "js-sys", "libc", "r-efi", "wasi 0.14.2+wasi-0.2.4", + "wasm-bindgen", ] [[package]] @@ -1546,6 +2510,12 @@ version = "0.31.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" +[[package]] +name = "glob-match" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9985c9503b412198aa4197559e9a318524ebc4519c229bfa05a535828c950b9d" + [[package]] name = "groestl" version = "0.10.1" @@ -1562,10 +2532,22 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63" dependencies = [ "ff", - "rand_core", + "rand_core 0.6.4", "subtle", ] +[[package]] +name = "growable-bloom-filter" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d174ccb4ba660d431329e7f0797870d0a4281e36353ec4b4a3c5eab6c2cfb6f1" +dependencies = [ + "serde", + "serde_bytes", + "serde_derive", + "xxhash-rust", +] + [[package]] name = "h2" version = "0.4.10" @@ -1578,13 +2560,29 @@ dependencies = [ "futures-core", "futures-sink", "http", - "indexmap", + "indexmap 2.9.0", "slab", "tokio", "tokio-util", "tracing", ] +[[package]] +name = "half" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "459196ed295495a68f7d7fe1d84f6c4b7ff0e21fe3017b2f283c6fac3ad803c9" +dependencies = [ + "cfg-if", + "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" @@ -1599,6 +2597,18 @@ name = "hashbrown" version = "0.15.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "84b26c544d002229e640969970a2e74021aadf6e2f96372b9c58eff97de08eb3" +dependencies = [ + "foldhash", +] + +[[package]] +name = "hashlink" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7382cf6263419f2d8df38c55d7da83da5c18aef87fc7a7fc1fb1e344edfe14c1" +dependencies = [ + "hashbrown 0.15.3", +] [[package]] name = "heck" @@ -1637,13 +2647,19 @@ version = "0.20.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9d3f528b053a6d700b2734eabcd0fd49cb8230647aa72958467527b0b7917114" dependencies = [ - "bincode", + "bincode 1.3.3", "byteorder", "heed-traits", "serde", "serde_json", ] +[[package]] +name = "hermit-abi" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c" + [[package]] name = "hex" version = "0.4.3" @@ -1659,6 +2675,30 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6fe2267d4ed49bc07b63801559be28c718ea06c4738b7a03c94df7386d2cde46" +[[package]] +name = "hkdf" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b5f8eb2ad728638ea2c7d47a21db23b7b58a72ed6a38256b8a1849f15fbbdf7" +dependencies = [ + "hmac", +] + +[[package]] +name = "hmac" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +dependencies = [ + "digest", +] + +[[package]] +name = "hostname-validator" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f558a64ac9af88b5ba400d99b579451af0d39c6d360980045b91aac966d705e2" + [[package]] name = "http" version = "1.3.1" @@ -1705,6 +2745,22 @@ version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" +[[package]] +name = "humantime" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b112acc8b3adf4b107a8ec20977da0273a8c386765a3ec0229bd500a1443f9f" + +[[package]] +name = "humantime-serde" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57a3db5ea5923d99402c94e9feb261dc5ee9b4efa158b0315f788cf549cc200c" +dependencies = [ + "humantime", + "serde", +] + [[package]] name = "hyper" version = "1.6.0" @@ -1874,6 +2930,12 @@ dependencies = [ "zerovec", ] +[[package]] +name = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + [[package]] name = "idna" version = "1.0.3" @@ -1895,6 +2957,17 @@ dependencies = [ "icu_properties", ] +[[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", + "serde", +] + [[package]] name = "indexmap" version = "2.9.0" @@ -1904,6 +2977,74 @@ dependencies = [ "equivalent", "hashbrown 0.15.3", "rayon", + "serde", +] + +[[package]] +name = "inotify" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f37dccff2791ab604f9babef0ba14fbe0be30bd368dc541e2b08d07c8aa908f3" +dependencies = [ + "bitflags 2.9.0", + "inotify-sys", + "libc", +] + +[[package]] +name = "inotify-sys" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e05c02b5e89bff3b946cedeca278abc628fe811e604f027c45a8aa3cf793d0eb" +dependencies = [ + "libc", +] + +[[package]] +name = "inout" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "879f10e63c20629ecabbb64a8010319738c66a5cd0c29b02d63d272b03751d01" +dependencies = [ + "generic-array", +] + +[[package]] +name = "inventory" +version = "0.3.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab08d7cd2c5897f2c949e5383ea7c7db03fb19130ffcfbf7eda795137ae3cb83" +dependencies = [ + "rustversion", +] + +[[package]] +name = "is-terminal" +version = "0.4.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e04d7f318608d35d4b61ddd75cbdaee86b023ebe2bd5a66ee0915f0bf93095a9" +dependencies = [ + "hermit-abi", + "libc", + "windows-sys 0.59.0", +] + +[[package]] +name = "itertools" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +dependencies = [ + "either", +] + +[[package]] +name = "itertools" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285" +dependencies = [ + "either", ] [[package]] @@ -1943,6 +3084,16 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "k12" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4dc5fdb62af2f520116927304f15d25b3c2667b4817b90efdc045194c912c54" +dependencies = [ + "digest", + "sha3", +] + [[package]] name = "keccak" version = "0.1.5" @@ -1967,11 +3118,34 @@ version = "0.2.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a4933f3f57a8e9d9da04db23fb153356ecaf00cbd14aee46279c33dc80925c37" +[[package]] +name = "kqueue" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eac30106d7dce88daf4a3fcb4879ea939476d5074a9b7ddd0fb97fa4bed5596a" +dependencies = [ + "kqueue-sys", + "libc", +] + +[[package]] +name = "kqueue-sys" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed9625ffda8729b85e45cf04090035ac368927b8cebc34898e7c120f52e4838b" +dependencies = [ + "bitflags 1.3.2", + "libc", +] + [[package]] name = "lazy_static" version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" +dependencies = [ + "spin", +] [[package]] name = "libc" @@ -2003,6 +3177,18 @@ checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" dependencies = [ "bitflags 2.9.0", "libc", + "redox_syscall", +] + +[[package]] +name = "libsqlite3-sys" +version = "0.34.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91632f3b4fb6bd1d72aa3d78f41ffecfcf2b1a6648d8c241dbe7dbfaf4875e15" +dependencies = [ + "cc", + "pkg-config", + "vcpkg", ] [[package]] @@ -2044,6 +3230,15 @@ version = "0.4.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" +[[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" @@ -2066,6 +3261,15 @@ version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" +[[package]] +name = "memmap2" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd3f7eed9d3848f8b98834af67102b720745c4ec028fcd0aa0239277e7de374f" +dependencies = [ + "libc", +] + [[package]] name = "merlin" version = "3.0.0" @@ -2074,7 +3278,7 @@ checksum = "58c38e2799fc0978b65dfff8023ec7843e2330bb462f19198840b34b6582397d" dependencies = [ "byteorder", "keccak", - "rand_core", + "rand_core 0.6.4", "zeroize", ] @@ -2084,6 +3288,12 @@ version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + [[package]] name = "miniz_oxide" version = "0.8.8" @@ -2100,6 +3310,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2886843bf800fba2e3377cff24abf6379b4c4d5c6681eaf9ea5b0d15090450bd" dependencies = [ "libc", + "log", "wasi 0.11.0+wasi-snapshot-preview1", "windows-sys 0.52.0", ] @@ -2113,7 +3324,7 @@ dependencies = [ "monero-io", "monero-primitives", "std-shims", - "thiserror", + "thiserror 1.0.69", "zeroize", ] @@ -2139,9 +3350,9 @@ dependencies = [ "monero-generators", "monero-io", "monero-primitives", - "rand_core", + "rand_core 0.6.4", "std-shims", - "thiserror", + "thiserror 1.0.69", "zeroize", ] @@ -2157,11 +3368,11 @@ dependencies = [ "monero-generators", "monero-io", "monero-primitives", - "rand_chacha", - "rand_core", + "rand_chacha 0.3.1", + "rand_core 0.6.4", "std-shims", "subtle", - "thiserror", + "thiserror 1.0.69", "zeroize", ] @@ -2198,7 +3409,7 @@ dependencies = [ "monero-io", "monero-primitives", "std-shims", - "thiserror", + "thiserror 1.0.69", "zeroize", ] @@ -2227,7 +3438,7 @@ dependencies = [ "serde", "serde_json", "std-shims", - "thiserror", + "thiserror 1.0.69", "zeroize", ] @@ -2261,6 +3472,57 @@ dependencies = [ "tokio", ] +[[package]] +name = "native-tls" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87de3442987e9dbec73158d5c715e7ad9072fda936bb03d19d7fa10e00520f0e" +dependencies = [ + "libc", + "log", + "openssl", + "openssl-probe", + "openssl-sys", + "schannel", + "security-framework 2.11.1", + "security-framework-sys", + "tempfile", +] + +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + +[[package]] +name = "notify" +version = "8.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2fee8403b3d66ac7b26aee6e40a897d85dc5ce26f44da36b8b73e987cc52e943" +dependencies = [ + "bitflags 2.9.0", + "filetime", + "inotify", + "kqueue", + "libc", + "log", + "mio", + "notify-types", + "walkdir", + "windows-sys 0.59.0", +] + +[[package]] +name = "notify-types" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e0826a989adedc2a244799e823aece04662b66609d96af8dff7ac6df9a8925d" + [[package]] name = "nu-ansi-term" version = "0.46.0" @@ -2271,12 +3533,59 @@ dependencies = [ "winapi", ] +[[package]] +name = "num-bigint" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" +dependencies = [ + "num-integer", + "num-traits", +] + +[[package]] +name = "num-bigint-dig" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc84195820f291c7697304f3cbdadd1cb7199c0efc917ff5eafd71225c136151" +dependencies = [ + "byteorder", + "lazy_static", + "libm", + "num-integer", + "num-iter", + "num-traits", + "rand 0.8.5", + "smallvec", + "zeroize", +] + [[package]] name = "num-conv" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" +[[package]] +name = "num-integer" +version = "0.1.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-iter" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1429034a0490724d0075ebb2bc9e875d6503c3cf69e235a8941aa757d83ef5bf" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + [[package]] name = "num-traits" version = "0.2.19" @@ -2287,6 +3596,28 @@ dependencies = [ "libm", ] +[[package]] +name = "num_enum" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a973b4e44ce6cad84ce69d797acf9a044532e4184c4f267913d1b546a0727b7a" +dependencies = [ + "num_enum_derive", + "rustversion", +] + +[[package]] +name = "num_enum_derive" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77e878c846a8abae00dd069496dbe8751b16ac1c3d6bd2a7283a938e8228f90d" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "syn 2.0.101", +] + [[package]] name = "object" version = "0.36.7" @@ -2302,24 +3633,143 @@ version = "1.21.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" +[[package]] +name = "oneshot-fused-workaround" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff2948fd2414b613f9a97f8401270bd5d7638265ab940475cdbcfa28a0273d58" +dependencies = [ + "futures", +] + +[[package]] +name = "oorandom" +version = "11.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6790f58c7ff633d8771f42965289203411a5e5c68388703c06e14f24770b41e" + +[[package]] +name = "openssl" +version = "0.10.73" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8505734d46c8ab1e19a1dce3aef597ad87dcb4c37e7188231769bd6bd51cebf8" +dependencies = [ + "bitflags 2.9.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.101", +] + [[package]] name = "openssl-probe" version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e" +[[package]] +name = "openssl-src" +version = "300.5.1+3.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "735230c832b28c000e3bc117119e6466a663ec73506bc0a9907ea4187508e42a" +dependencies = [ + "cc", +] + +[[package]] +name = "openssl-sys" +version = "0.9.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90096e2e47630d78b7d1c20952dc621f957103f8bc2c8359ec81290d75238571" +dependencies = [ + "cc", + "libc", + "openssl-src", + "pkg-config", + "vcpkg", +] + [[package]] name = "option-ext" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" +[[package]] +name = "ordered-float" +version = "2.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68f19d67e5a2795c94e73e0bb1cc1a7edeb2e28efd39e2e1c9b7a40c1108b11c" +dependencies = [ + "num-traits", +] + +[[package]] +name = "os_str_bytes" +version = "6.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2355d85b9a3786f481747ced0e0ff2ba35213a1f9bd406ed906554d7af805a1" +dependencies = [ + "memchr", +] + [[package]] name = "overload" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" +[[package]] +name = "p256" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9863ad85fa8f4460f9c48cb909d38a0d689dba1f6f6988a5e3e0d31071bcd4b" +dependencies = [ + "ecdsa", + "elliptic-curve", + "primeorder", + "sha2", +] + +[[package]] +name = "p384" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe42f1670a52a47d448f14b6a5c61dd78fce51856e68edaa38f7ae3a46b8d6b6" +dependencies = [ + "ecdsa", + "elliptic-curve", + "primeorder", + "sha2", +] + +[[package]] +name = "p521" +version = "0.13.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fc9e2161f1f215afdfce23677034ae137bbd45016a880c2eb3ba8eb95f085b2" +dependencies = [ + "base16ct", + "ecdsa", + "elliptic-curve", + "primeorder", + "rand_core 0.6.4", + "sha2", +] + [[package]] name = "page_size" version = "0.6.0" @@ -2330,6 +3780,12 @@ dependencies = [ "winapi", ] +[[package]] +name = "parking" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f38d5652c16fde515bb1ecef450ab0f6a219d619a7274976324d5e377f7dceba" + [[package]] name = "parking_lot" version = "0.12.3" @@ -2359,6 +3815,15 @@ version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" +[[package]] +name = "pem-rfc7468" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88b39c9bfcfc231068454382784bb460aae594343fb030d46e9f50a645418412" +dependencies = [ + "base64ct", +] + [[package]] name = "percent-encoding" version = "2.3.1" @@ -2371,8 +3836,19 @@ version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1fd6780a80ae0c52cc120a26a1a42c1ae51b247a253e4e06113d23d2c2edd078" dependencies = [ - "phf_macros", - "phf_shared", + "phf_macros 0.11.3", + "phf_shared 0.11.3", +] + +[[package]] +name = "phf" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "913273894cec178f401a31ec4b656318d95473527be05c0752cc41cdc32be8b7" +dependencies = [ + "phf_macros 0.12.1", + "phf_shared 0.12.1", + "serde", ] [[package]] @@ -2381,8 +3857,18 @@ version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c80231409c20246a13fddb31776fb942c38553c51e871f8cbd687a4cfb5843d" dependencies = [ - "phf_shared", - "rand", + "phf_shared 0.11.3", + "rand 0.8.5", +] + +[[package]] +name = "phf_generator" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2cbb1126afed61dd6368748dae63b1ee7dc480191c6262a3b4ff1e29d86a6c5b" +dependencies = [ + "fastrand", + "phf_shared 0.12.1", ] [[package]] @@ -2391,8 +3877,21 @@ version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f84ac04429c13a7ff43785d75ad27569f2951ce0ffd30a3321230db2fc727216" dependencies = [ - "phf_generator", - "phf_shared", + "phf_generator 0.11.3", + "phf_shared 0.11.3", + "proc-macro2", + "quote", + "syn 2.0.101", +] + +[[package]] +name = "phf_macros" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d713258393a82f091ead52047ca779d37e5766226d009de21696c4e667044368" +dependencies = [ + "phf_generator 0.12.1", + "phf_shared 0.12.1", "proc-macro2", "quote", "syn 2.0.101", @@ -2407,6 +3906,15 @@ dependencies = [ "siphasher", ] +[[package]] +name = "phf_shared" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06005508882fb681fd97892ecff4b7fd0fee13ef1aa569f8695dae7ab9099981" +dependencies = [ + "siphasher", +] + [[package]] name = "pin-project" version = "1.1.10" @@ -2439,6 +3947,76 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" +[[package]] +name = "pkcs1" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8ffb9f10fa047879315e6625af03c164b16962a5368d724ed16323b68ace47f" +dependencies = [ + "der", + "pkcs8", + "spki", +] + +[[package]] +name = "pkcs8" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" +dependencies = [ + "der", + "spki", +] + +[[package]] +name = "pkg-config" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" + +[[package]] +name = "plotters" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5aeb6f403d7a4911efb1e33402027fc44f29b5bf6def3effcc22d7bb75f2b747" +dependencies = [ + "num-traits", + "plotters-backend", + "plotters-svg", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "plotters-backend" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df42e13c12958a16b3f7f4386b9ab1f3e7933914ecea48da7139435263a4172a" + +[[package]] +name = "plotters-svg" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51bae2ac328883f7acdfea3d66a7c35751187f870bc81f94563733a154d7a670" +dependencies = [ + "plotters-backend", +] + +[[package]] +name = "postage" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af3fb618632874fb76937c2361a7f22afd393c982a2165595407edc75b06d3c1" +dependencies = [ + "atomic 0.5.3", + "crossbeam-queue", + "futures", + "parking_lot", + "pin-project", + "static_assertions", + "thiserror 1.0.69", +] + [[package]] name = "potential_utf" version = "0.1.2" @@ -2473,6 +4051,26 @@ dependencies = [ "yansi", ] +[[package]] +name = "primeorder" +version = "0.13.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "353e1ca18966c16d9deb1c69278edbc5f194139612772bd9537af60ac231e1e6" +dependencies = [ + "elliptic-curve", +] + +[[package]] +name = "priority-queue" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5676d703dda103cbb035b653a9f11448c0a7216c7926bd35fcb5865475d0c970" +dependencies = [ + "autocfg", + "equivalent", + "indexmap 2.9.0", +] + [[package]] name = "proc-macro-crate" version = "3.3.0" @@ -2502,10 +4100,10 @@ dependencies = [ "bitflags 2.9.0", "lazy_static", "num-traits", - "rand", - "rand_chacha", + "rand 0.8.5", + "rand_chacha 0.3.1", "rand_xorshift", - "regex-syntax", + "regex-syntax 0.8.5", "rusty-fork", "tempfile", "unarray", @@ -2522,6 +4120,18 @@ dependencies = [ "syn 2.0.101", ] +[[package]] +name = "pwd-grp" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b94fdf3867b7f2889a736f0022ea9386766280d2cca4bdbe41629ada9e4f3b8f" +dependencies = [ + "derive-deftly 0.14.6", + "libc", + "paste", + "thiserror 1.0.69", +] + [[package]] name = "quick-error" version = "1.2.3" @@ -2556,8 +4166,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" dependencies = [ "libc", - "rand_chacha", - "rand_core", + "rand_chacha 0.3.1", + "rand_core 0.6.4", +] + +[[package]] +name = "rand" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fbfd9d094a40bf3ae768db9361049ace4c0e04a4fd6b359518bd7b73a73dd97" +dependencies = [ + "rand_chacha 0.9.0", + "rand_core 0.9.3", ] [[package]] @@ -2567,7 +4187,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" dependencies = [ "ppv-lite86", - "rand_core", + "rand_core 0.6.4", +] + +[[package]] +name = "rand_chacha" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" +dependencies = [ + "ppv-lite86", + "rand_core 0.9.3", ] [[package]] @@ -2579,6 +4209,15 @@ dependencies = [ "getrandom 0.2.16", ] +[[package]] +name = "rand_core" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" +dependencies = [ + "getrandom 0.3.3", +] + [[package]] name = "rand_distr" version = "0.4.3" @@ -2586,7 +4225,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32cb0b9bc82b0a0876c2dd994a7e7a2683d3e7390ca40e6886785ef0c7e3ee31" dependencies = [ "num-traits", - "rand", + "rand 0.8.5", +] + +[[package]] +name = "rand_jitter" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b16df48f071248e67b8fc5e866d9448d45c08ad8b672baaaf796e2f15e606ff0" +dependencies = [ + "libc", + "rand_core 0.9.3", + "winapi", ] [[package]] @@ -2595,7 +4245,7 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d25bf25ec5ae4a3f1b92f929810509a2f53d7dca2f50b794ff57e3face536c8f" dependencies = [ - "rand_core", + "rand_core 0.6.4", ] [[package]] @@ -2606,7 +4256,7 @@ dependencies = [ "bitflags 1.3.2", "cmake", "libc", - "thiserror", + "thiserror 1.0.69", ] [[package]] @@ -2629,6 +4279,15 @@ dependencies = [ "crossbeam-utils", ] +[[package]] +name = "rdrand" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d92195228612ac8eed47adbc2ed0f04e513a4ccb98175b6f2bd04d963b533655" +dependencies = [ + "rand_core 0.6.4", +] + [[package]] name = "redb" version = "2.5.0" @@ -2655,7 +4314,18 @@ checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43" dependencies = [ "getrandom 0.2.16", "libredox", - "thiserror", + "thiserror 1.0.69", +] + +[[package]] +name = "redox_users" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd6f9d3d47bdd2ad6945c5015a226ec6155d0bcdfd8f7cd29f86b71f8de99d2b" +dependencies = [ + "getrandom 0.2.16", + "libredox", + "thiserror 2.0.12", ] [[package]] @@ -2678,12 +4348,66 @@ dependencies = [ "syn 2.0.101", ] +[[package]] +name = "regex" +version = "1.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata 0.4.9", + "regex-syntax 0.8.5", +] + +[[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]] +name = "regex-automata" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax 0.8.5", +] + +[[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.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" +[[package]] +name = "retry-error" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce97442758392c7e2a7716e06c514de75f0fe4b5a4b76e14ba1e5edfb7ba3512" + +[[package]] +name = "rfc6979" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8dd2a808d456c4a54e300a23e9f5a67e122c3024119acbfd73e3bf664491cb2" +dependencies = [ + "hmac", + "subtle", +] + [[package]] name = "ring" version = "0.17.14" @@ -2698,6 +4422,42 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "rsa" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78928ac1ed176a5ca1d17e578a1825f3d81ca54cf41053a592584b020cfd691b" +dependencies = [ + "const-oid", + "digest", + "num-bigint-dig", + "num-integer", + "num-traits", + "pkcs1", + "pkcs8", + "rand_core 0.6.4", + "sha2", + "signature", + "spki", + "subtle", + "zeroize", +] + +[[package]] +name = "rusqlite" +version = "0.36.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3de23c3319433716cf134eed225fe9986bc24f63bed9be9f20c329029e672dc7" +dependencies = [ + "bitflags 2.9.0", + "fallible-iterator", + "fallible-streaming-iterator", + "hashlink", + "libsqlite3-sys", + "smallvec", + "time", +] + [[package]] name = "rustc-demangle" version = "0.1.24" @@ -2713,6 +4473,15 @@ dependencies = [ "semver", ] +[[package]] +name = "rusticata-macros" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "faf0c4a6ece9950b9abdb62b1cfcf2a68b3b67a10ba445b3bb85be2a293d0632" +dependencies = [ + "nom", +] + [[package]] name = "rustix" version = "1.0.7" @@ -2750,7 +4519,7 @@ dependencies = [ "openssl-probe", "rustls-pki-types", "schannel", - "security-framework", + "security-framework 3.2.0", ] [[package]] @@ -2797,6 +4566,37 @@ version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" +[[package]] +name = "safelog" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d343b15e1705331c25689e0fa87e8514fb33bd3c9beeb45658a7c355f317de2" +dependencies = [ + "derive_more", + "educe", + "either", + "fluid-let", + "thiserror 2.0.12", +] + +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "sanitize-filename" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc984f4f9ceb736a7bb755c3e3bd17dc56370af2600c9780dcc48c66453da34d" +dependencies = [ + "regex", +] + [[package]] name = "schannel" version = "0.1.27" @@ -2806,12 +4606,63 @@ dependencies = [ "windows-sys 0.59.0", ] +[[package]] +name = "schemars" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cd191f9397d57d581cddd31014772520aa448f65ef991055d7f61582c65165f" +dependencies = [ + "dyn-clone", + "ref-cast", + "serde", + "serde_json", +] + +[[package]] +name = "schemars" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1375ba8ef45a6f15d83fa8748f1079428295d403d6ea991d09ab100155fbc06d" +dependencies = [ + "dyn-clone", + "ref-cast", + "serde", + "serde_json", +] + [[package]] name = "scopeguard" version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" +[[package]] +name = "sec1" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc" +dependencies = [ + "base16ct", + "der", + "generic-array", + "pkcs8", + "subtle", + "zeroize", +] + +[[package]] +name = "security-framework" +version = "2.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" +dependencies = [ + "bitflags 2.9.0", + "core-foundation 0.9.4", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + [[package]] name = "security-framework" version = "3.2.0" @@ -2819,7 +4670,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "271720403f46ca04f7ba6f55d438f8bd878d6b8ca0a1046e8228c4145bcbb316" dependencies = [ "bitflags 2.9.0", - "core-foundation", + "core-foundation 0.10.0", "core-foundation-sys", "libc", "security-framework-sys", @@ -2856,6 +4707,16 @@ dependencies = [ "serde_derive", ] +[[package]] +name = "serde-value" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3a1a3341211875ef120e117ea7fd5228530ae7e7036a779fdc9117be6b3282c" +dependencies = [ + "ordered-float", + "serde", +] + [[package]] name = "serde_bytes" version = "0.11.17" @@ -2876,6 +4737,15 @@ dependencies = [ "syn 2.0.101", ] +[[package]] +name = "serde_ignored" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b516445dac1e3535b6d658a7b528d771153dfb272ed4180ca4617a20550365ff" +dependencies = [ + "serde", +] + [[package]] name = "serde_json" version = "1.0.140" @@ -2919,6 +4789,49 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_with" +version = "3.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2c45cd61fefa9db6f254525d46e392b852e0e61d9a1fd36e5bd183450a556d5" +dependencies = [ + "base64", + "chrono", + "hex", + "indexmap 1.9.3", + "indexmap 2.9.0", + "schemars 0.9.0", + "schemars 1.0.3", + "serde", + "serde_derive", + "serde_json", + "serde_with_macros", + "time", +] + +[[package]] +name = "serde_with_macros" +version = "3.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de90945e6565ce0d9a25098082ed4ee4002e047cb59892c318d66821e14bb30f" +dependencies = [ + "darling 0.20.11", + "proc-macro2", + "quote", + "syn 2.0.101", +] + +[[package]] +name = "sha1" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + [[package]] name = "sha2" version = "0.10.9" @@ -2949,6 +4862,17 @@ dependencies = [ "lazy_static", ] +[[package]] +name = "shellexpand" +version = "3.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b1fdf65dd6331831494dd616b30351c38e96e45921a27745cf98490458b90bb" +dependencies = [ + "bstr", + "dirs 6.0.0", + "os_str_bytes", +] + [[package]] name = "shlex" version = "1.3.0" @@ -2964,6 +4888,16 @@ dependencies = [ "libc", ] +[[package]] +name = "signature" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" +dependencies = [ + "digest", + "rand_core 0.6.4", +] + [[package]] name = "simple-request" version = "0.1.0" @@ -3002,6 +4936,29 @@ dependencies = [ "autocfg", ] +[[package]] +name = "slotmap" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbff4acf519f630b3a3ddcfaea6c06b42174d9a44bc70c620e9ed1649d58b82a" +dependencies = [ + "serde", + "version_check", +] + +[[package]] +name = "slotmap-careful" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9500059071474a36baac642b6bb99ca1dbac0ce43727abbba02dad83822dadf2" +dependencies = [ + "paste", + "serde", + "slotmap", + "thiserror 2.0.12", + "void", +] + [[package]] name = "smallvec" version = "1.15.0" @@ -3024,12 +4981,69 @@ version = "0.9.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" +[[package]] +name = "spki" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d" +dependencies = [ + "base64ct", + "der", +] + +[[package]] +name = "ssh-cipher" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "caac132742f0d33c3af65bfcde7f6aa8f62f0e991d80db99149eb9d44708784f" +dependencies = [ + "cipher", + "ssh-encoding", +] + +[[package]] +name = "ssh-encoding" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb9242b9ef4108a78e8cd1a2c98e193ef372437f8c22be363075233321dd4a15" +dependencies = [ + "base64ct", + "pem-rfc7468", + "sha2", +] + +[[package]] +name = "ssh-key" +version = "0.6.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b86f5297f0f04d08cabaa0f6bff7cb6aec4d9c3b49d87990d63da9d9156a8c3" +dependencies = [ + "p256", + "p384", + "p521", + "rand_core 0.6.4", + "rsa", + "sec1", + "sha2", + "signature", + "ssh-cipher", + "ssh-encoding", + "subtle", + "zeroize", +] + [[package]] name = "stable_deref_trait" version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + [[package]] name = "std-shims" version = "0.1.1" @@ -3039,6 +5053,12 @@ dependencies = [ "spin", ] +[[package]] +name = "strsim" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" + [[package]] name = "strsim" version = "0.11.1" @@ -3051,7 +5071,16 @@ version = "0.26.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8fec0f0aef304996cf250b31b5a10dee7980c85da9d759361292b8bca5a18f06" dependencies = [ - "strum_macros", + "strum_macros 0.26.4", +] + +[[package]] +name = "strum" +version = "0.27.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f64def088c51c9510a8579e3c5d67c65349dcf755e5479ad3d010aa6454e2c32" +dependencies = [ + "strum_macros 0.27.1", ] [[package]] @@ -3067,6 +5096,19 @@ dependencies = [ "syn 2.0.101", ] +[[package]] +name = "strum_macros" +version = "0.27.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c77a8c5abcaf0f9ce05d62342b7d298c346515365c36b673df4ebe3ced01fde8" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "rustversion", + "syn 2.0.101", +] + [[package]] name = "subtle" version = "2.6.1" @@ -3162,7 +5204,16 @@ version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" dependencies = [ - "thiserror-impl", + "thiserror-impl 1.0.69", +] + +[[package]] +name = "thiserror" +version = "2.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708" +dependencies = [ + "thiserror-impl 2.0.12", ] [[package]] @@ -3176,6 +5227,17 @@ dependencies = [ "syn 2.0.101", ] +[[package]] +name = "thiserror-impl" +version = "2.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.101", +] + [[package]] name = "thread_local" version = "1.1.8" @@ -3233,6 +5295,16 @@ dependencies = [ "zerovec", ] +[[package]] +name = "tinytemplate" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be4d6b5f19ff7664e8c98d03e2139cb510db9b0a60b55f8e8709b689d939b6bc" +dependencies = [ + "serde", + "serde_json", +] + [[package]] name = "tokio" version = "1.45.0" @@ -3272,6 +5344,17 @@ dependencies = [ "tokio", ] +[[package]] +name = "tokio-socks" +version = "0.5.2" +source = "git+https://github.com/Cuprate/tokio-socks.git?rev=8737caf#8737caf429a86ab181954444fac4f9b917795b19" +dependencies = [ + "either", + "futures-util", + "thiserror 1.0.69", + "tokio", +] + [[package]] name = "tokio-stream" version = "0.1.17" @@ -3305,6 +5388,7 @@ checksum = "66a539a9ad6d5d281510d5bd368c973d636c02dbf8a67300bfb6b950696ad7df" dependencies = [ "bytes", "futures-core", + "futures-io", "futures-sink", "futures-util", "hashbrown 0.15.3", @@ -3341,7 +5425,7 @@ version = "0.22.26" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "310068873db2c5b3e7659d2cc35d21855dbafa50d1ce336397c666e3cb08137e" dependencies = [ - "indexmap", + "indexmap 2.9.0", "serde", "serde_spanned", "toml_datetime", @@ -3355,6 +5439,997 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bfb942dfe1d8e29a7ee7fcbde5bd2b9a25fb89aa70caea2eba3bee836ff41076" +[[package]] +name = "tor-async-utils" +version = "0.32.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12bee2c4a8ea4cfe533bf284eef89d26f53ed0145854c54471734cb36e451f3e" +dependencies = [ + "derive-deftly 1.1.0", + "educe", + "futures", + "oneshot-fused-workaround", + "pin-project", + "postage", + "thiserror 2.0.12", + "void", +] + +[[package]] +name = "tor-basic-utils" +version = "0.32.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41a4e9e4da7ce0f089aee75808db143c4d547ca6d4ffd5b15a1cb042c3082928" +dependencies = [ + "derive_more", + "hex", + "itertools 0.14.0", + "libc", + "paste", + "rand 0.9.1", + "rand_chacha 0.9.0", + "serde", + "slab", + "smallvec", + "thiserror 2.0.12", +] + +[[package]] +name = "tor-bytes" +version = "0.32.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5a4a8401219d99b460c9bc001386366a4c50dc881a0abf346f04c1785f7e06a" +dependencies = [ + "bytes", + "derive-deftly 1.1.0", + "digest", + "educe", + "getrandom 0.3.3", + "safelog", + "thiserror 2.0.12", + "tor-error", + "tor-llcrypto", + "zeroize", +] + +[[package]] +name = "tor-cell" +version = "0.32.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "230485eda02aa73aea39aefdc1aff7fbc6304de1ce510de366261727e3007a92" +dependencies = [ + "amplify", + "bitflags 2.9.0", + "bytes", + "caret", + "derive-deftly 1.1.0", + "derive_more", + "educe", + "paste", + "rand 0.9.1", + "smallvec", + "thiserror 2.0.12", + "tor-basic-utils", + "tor-bytes", + "tor-cert", + "tor-error", + "tor-hscrypto", + "tor-linkspec", + "tor-llcrypto", + "tor-memquota", + "tor-protover", + "tor-units", + "void", +] + +[[package]] +name = "tor-cert" +version = "0.32.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9abc35321d207c3ffbfdc37feb0a9e186555ee49dfaa8079027115ce44491ff2" +dependencies = [ + "caret", + "derive_builder_fork_arti", + "derive_more", + "digest", + "thiserror 2.0.12", + "tor-bytes", + "tor-checkable", + "tor-llcrypto", +] + +[[package]] +name = "tor-chanmgr" +version = "0.32.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85f54ff3c72323739c4903f6b8c5fd1a0b8b8554fbba2c1ffa712c1c7d4faa22" +dependencies = [ + "async-trait", + "caret", + "derive_builder_fork_arti", + "derive_more", + "educe", + "futures", + "oneshot-fused-workaround", + "postage", + "rand 0.9.1", + "safelog", + "serde", + "thiserror 2.0.12", + "tor-async-utils", + "tor-basic-utils", + "tor-cell", + "tor-config", + "tor-error", + "tor-linkspec", + "tor-llcrypto", + "tor-memquota", + "tor-netdir", + "tor-proto", + "tor-rtcompat", + "tor-socksproto", + "tor-units", + "tracing", + "void", +] + +[[package]] +name = "tor-checkable" +version = "0.32.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72e994401be86ecdaa8b17b176cd1562ddddc6778a47afd751e6db99ccadb8e6" +dependencies = [ + "humantime", + "signature", + "thiserror 2.0.12", + "tor-llcrypto", +] + +[[package]] +name = "tor-circmgr" +version = "0.32.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "efafa2ca72873965c33dcba7375d14d436b584cacb0344b8068f559d8d775bcc" +dependencies = [ + "amplify", + "async-trait", + "bounded-vec-deque", + "cfg-if", + "derive_builder_fork_arti", + "derive_more", + "downcast-rs", + "dyn-clone", + "educe", + "futures", + "humantime-serde", + "itertools 0.14.0", + "once_cell", + "oneshot-fused-workaround", + "pin-project", + "rand 0.9.1", + "retry-error", + "safelog", + "serde", + "static_assertions", + "thiserror 2.0.12", + "tor-async-utils", + "tor-basic-utils", + "tor-chanmgr", + "tor-config", + "tor-error", + "tor-guardmgr", + "tor-linkspec", + "tor-memquota", + "tor-netdir", + "tor-netdoc", + "tor-persist", + "tor-proto", + "tor-protover", + "tor-relay-selection", + "tor-rtcompat", + "tor-units", + "tracing", + "void", + "weak-table", +] + +[[package]] +name = "tor-config" +version = "0.32.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3280e6e26a30f94d752d55d273c307d2b819971ff4b830101d816990f9a5ec36" +dependencies = [ + "amplify", + "cfg-if", + "derive-deftly 1.1.0", + "derive_builder_fork_arti", + "educe", + "either", + "figment", + "fs-mistrust", + "futures", + "itertools 0.14.0", + "notify", + "paste", + "postage", + "regex", + "serde", + "serde-value", + "serde_ignored", + "strum 0.27.1", + "thiserror 2.0.12", + "toml", + "tor-basic-utils", + "tor-error", + "tor-rtcompat", + "tracing", + "void", +] + +[[package]] +name = "tor-config-path" +version = "0.32.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5347bcbe96c660694fe52fb76e852d982d73fe0d92f4c4cb9eaa8427a5d52f17" +dependencies = [ + "directories", + "serde", + "shellexpand", + "thiserror 2.0.12", + "tor-error", + "tor-general-addr", +] + +[[package]] +name = "tor-consdiff" +version = "0.32.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3d8e230e901c09dd73c81f12402e8c0221d992ee131ff32ac0dde72f972f47e" +dependencies = [ + "digest", + "hex", + "thiserror 2.0.12", + "tor-llcrypto", +] + +[[package]] +name = "tor-dirclient" +version = "0.32.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d6913b3a246442dca5a02b3002c9820b0e491a03eb9448843bd73782fb2b0d7" +dependencies = [ + "async-compression", + "base64ct", + "derive_more", + "futures", + "hex", + "http", + "httparse", + "httpdate", + "itertools 0.14.0", + "memchr", + "thiserror 2.0.12", + "tor-circmgr", + "tor-error", + "tor-hscrypto", + "tor-linkspec", + "tor-llcrypto", + "tor-netdoc", + "tor-proto", + "tor-rtcompat", + "tracing", +] + +[[package]] +name = "tor-dirmgr" +version = "0.32.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05a13cf2d0f3d92b8a0d45b661d7b2978ae726cc252d8a8e4308b750ab3b3275" +dependencies = [ + "async-trait", + "base64ct", + "derive_builder_fork_arti", + "derive_more", + "digest", + "educe", + "event-listener", + "fs-mistrust", + "fslock", + "futures", + "hex", + "humantime", + "humantime-serde", + "itertools 0.14.0", + "memmap2", + "oneshot-fused-workaround", + "paste", + "postage", + "rand 0.9.1", + "rusqlite", + "safelog", + "scopeguard", + "serde", + "serde_json", + "signature", + "static_assertions", + "strum 0.27.1", + "thiserror 2.0.12", + "time", + "tor-async-utils", + "tor-basic-utils", + "tor-checkable", + "tor-circmgr", + "tor-config", + "tor-consdiff", + "tor-dirclient", + "tor-error", + "tor-guardmgr", + "tor-llcrypto", + "tor-netdir", + "tor-netdoc", + "tor-persist", + "tor-proto", + "tor-protover", + "tor-rtcompat", + "tracing", +] + +[[package]] +name = "tor-error" +version = "0.32.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79ae19c74564749c54e14e532ffb15f84807f734d17f452bb3ffb8b1957f06a2" +dependencies = [ + "derive_more", + "futures", + "paste", + "retry-error", + "static_assertions", + "strum 0.27.1", + "thiserror 2.0.12", + "tracing", + "void", +] + +[[package]] +name = "tor-general-addr" +version = "0.32.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dad9c6e9147f4ee644c80c3b044813cf93a3f802279c49b06aac2f4f33555877" +dependencies = [ + "derive_more", + "thiserror 2.0.12", + "void", +] + +[[package]] +name = "tor-guardmgr" +version = "0.32.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32ff4df101c82b9731bc9d54b94ffdee6053471cfa3aa78663c53a545d3d3139" +dependencies = [ + "amplify", + "base64ct", + "derive-deftly 1.1.0", + "derive_builder_fork_arti", + "derive_more", + "dyn-clone", + "educe", + "futures", + "humantime", + "humantime-serde", + "itertools 0.14.0", + "num_enum", + "oneshot-fused-workaround", + "pin-project", + "postage", + "rand 0.9.1", + "safelog", + "serde", + "strum 0.27.1", + "thiserror 2.0.12", + "tor-async-utils", + "tor-basic-utils", + "tor-config", + "tor-error", + "tor-linkspec", + "tor-llcrypto", + "tor-netdir", + "tor-netdoc", + "tor-persist", + "tor-proto", + "tor-relay-selection", + "tor-rtcompat", + "tor-units", + "tracing", +] + +[[package]] +name = "tor-hsclient" +version = "0.32.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4ad84737ce249b56f74bede1b09f61a4169979c3cef124012a9447e1790066a" +dependencies = [ + "async-trait", + "derive-deftly 1.1.0", + "derive_more", + "educe", + "either", + "futures", + "itertools 0.14.0", + "oneshot-fused-workaround", + "postage", + "rand 0.9.1", + "retry-error", + "safelog", + "slotmap-careful", + "strum 0.27.1", + "thiserror 2.0.12", + "tor-async-utils", + "tor-basic-utils", + "tor-bytes", + "tor-cell", + "tor-checkable", + "tor-circmgr", + "tor-config", + "tor-dirclient", + "tor-error", + "tor-hscrypto", + "tor-keymgr", + "tor-linkspec", + "tor-llcrypto", + "tor-memquota", + "tor-netdir", + "tor-netdoc", + "tor-persist", + "tor-proto", + "tor-protover", + "tor-rtcompat", + "tracing", +] + +[[package]] +name = "tor-hscrypto" +version = "0.32.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7469efe5d22466fcaaeec9506bf03426ce59c03ee1b8a7c8b316830b153b40a" +dependencies = [ + "cipher", + "data-encoding", + "derive-deftly 1.1.0", + "derive_more", + "digest", + "hex", + "humantime", + "itertools 0.14.0", + "paste", + "rand 0.9.1", + "safelog", + "serde", + "signature", + "subtle", + "thiserror 2.0.12", + "tor-basic-utils", + "tor-bytes", + "tor-error", + "tor-key-forge", + "tor-llcrypto", + "tor-memquota", + "tor-units", + "void", + "zeroize", +] + +[[package]] +name = "tor-hsservice" +version = "0.32.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60ed074e5e9a9eb14e4e98b37d9f08a43538883e71ba916c859dbc4f6ad90f1f" +dependencies = [ + "amplify", + "async-trait", + "base64ct", + "cfg-if", + "derive-deftly 1.1.0", + "derive_builder_fork_arti", + "derive_more", + "digest", + "educe", + "fs-mistrust", + "futures", + "growable-bloom-filter", + "hex", + "humantime", + "itertools 0.14.0", + "k12", + "once_cell", + "oneshot-fused-workaround", + "postage", + "rand 0.9.1", + "rand_core 0.9.3", + "retry-error", + "safelog", + "serde", + "serde_with", + "strum 0.27.1", + "thiserror 2.0.12", + "tor-async-utils", + "tor-basic-utils", + "tor-bytes", + "tor-cell", + "tor-circmgr", + "tor-config", + "tor-config-path", + "tor-dirclient", + "tor-error", + "tor-hscrypto", + "tor-keymgr", + "tor-linkspec", + "tor-llcrypto", + "tor-log-ratelim", + "tor-netdir", + "tor-netdoc", + "tor-persist", + "tor-proto", + "tor-protover", + "tor-relay-selection", + "tor-rtcompat", + "tracing", + "void", +] + +[[package]] +name = "tor-key-forge" +version = "0.32.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6697b58f1518757b975993d345a261387eb7a86c730cb542ad1ea68284155eaa" +dependencies = [ + "derive-deftly 1.1.0", + "derive_more", + "downcast-rs", + "paste", + "rand 0.9.1", + "signature", + "ssh-key", + "thiserror 2.0.12", + "tor-bytes", + "tor-cert", + "tor-checkable", + "tor-error", + "tor-llcrypto", +] + +[[package]] +name = "tor-keymgr" +version = "0.32.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56d765c0fd6b1910b427feb1b604fbc85e5768332e9be1e670bf64e698c92606" +dependencies = [ + "amplify", + "arrayvec", + "cfg-if", + "derive-deftly 1.1.0", + "derive_builder_fork_arti", + "derive_more", + "downcast-rs", + "dyn-clone", + "fs-mistrust", + "glob-match", + "humantime", + "inventory", + "itertools 0.14.0", + "rand 0.9.1", + "serde", + "signature", + "ssh-key", + "thiserror 2.0.12", + "tor-basic-utils", + "tor-bytes", + "tor-config", + "tor-config-path", + "tor-error", + "tor-hscrypto", + "tor-key-forge", + "tor-llcrypto", + "tor-persist", + "tracing", + "walkdir", + "zeroize", +] + +[[package]] +name = "tor-linkspec" +version = "0.32.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1feeea901ff88616175c3bcf3fbc53466117e7d655c15253f80260be017333bd" +dependencies = [ + "base64ct", + "by_address", + "caret", + "derive-deftly 1.1.0", + "derive_builder_fork_arti", + "derive_more", + "hex", + "itertools 0.14.0", + "safelog", + "serde", + "serde_with", + "strum 0.27.1", + "thiserror 2.0.12", + "tor-basic-utils", + "tor-bytes", + "tor-config", + "tor-llcrypto", + "tor-memquota", + "tor-protover", +] + +[[package]] +name = "tor-llcrypto" +version = "0.32.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "992c49dd4c285c52594858c0e92afe96531203dbe9bb29cfbe6937d94bb3c7ad" +dependencies = [ + "aes", + "base64ct", + "ctr", + "curve25519-dalek", + "der-parser", + "derive-deftly 1.1.0", + "derive_more", + "digest", + "ed25519-dalek", + "educe", + "getrandom 0.3.3", + "hex", + "rand 0.9.1", + "rand_chacha 0.9.0", + "rand_core 0.6.4", + "rand_core 0.9.3", + "rand_jitter", + "rdrand", + "rsa", + "safelog", + "serde", + "sha1", + "sha2", + "sha3", + "signature", + "subtle", + "thiserror 2.0.12", + "tor-memquota", + "visibility", + "x25519-dalek", + "zeroize", +] + +[[package]] +name = "tor-log-ratelim" +version = "0.32.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "511702fd833651896f1e6ca486c1b0d8d3f28b8f8724526ae8260c33ee49b2e8" +dependencies = [ + "futures", + "humantime", + "thiserror 2.0.12", + "tor-error", + "tor-rtcompat", + "tracing", + "weak-table", +] + +[[package]] +name = "tor-memquota" +version = "0.32.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07de5fe3ab545970b36319397b8c5e82fffbad5682581e06d030cfda0296cd9f" +dependencies = [ + "derive-deftly 1.1.0", + "derive_more", + "dyn-clone", + "educe", + "futures", + "itertools 0.14.0", + "paste", + "pin-project", + "serde", + "slotmap-careful", + "static_assertions", + "thiserror 2.0.12", + "tor-async-utils", + "tor-basic-utils", + "tor-config", + "tor-error", + "tor-log-ratelim", + "tor-rtcompat", + "tracing", + "void", +] + +[[package]] +name = "tor-netdir" +version = "0.32.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "497936529955a5512e2500366aca3f353081f51edde633d5c9fc2124f1da36a6" +dependencies = [ + "async-trait", + "bitflags 2.9.0", + "derive_more", + "digest", + "futures", + "hex", + "humantime", + "itertools 0.14.0", + "num_enum", + "rand 0.9.1", + "serde", + "static_assertions", + "strum 0.27.1", + "thiserror 2.0.12", + "time", + "tor-basic-utils", + "tor-error", + "tor-hscrypto", + "tor-linkspec", + "tor-llcrypto", + "tor-netdoc", + "tor-protover", + "tor-units", + "tracing", + "typed-index-collections", +] + +[[package]] +name = "tor-netdoc" +version = "0.32.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f51ac213a399bf26bc4801495dd17ede3c118d2bedd6575d79c08dbf1379c1a7" +dependencies = [ + "amplify", + "base64ct", + "bitflags 2.9.0", + "cipher", + "derive_builder_fork_arti", + "derive_more", + "digest", + "educe", + "hex", + "humantime", + "itertools 0.14.0", + "memchr", + "phf 0.12.1", + "rand 0.9.1", + "serde", + "serde_with", + "signature", + "smallvec", + "subtle", + "thiserror 2.0.12", + "time", + "tinystr", + "tor-basic-utils", + "tor-bytes", + "tor-cell", + "tor-cert", + "tor-checkable", + "tor-error", + "tor-hscrypto", + "tor-linkspec", + "tor-llcrypto", + "tor-protover", + "tor-units", + "void", + "weak-table", + "zeroize", +] + +[[package]] +name = "tor-persist" +version = "0.32.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fabc9ba76dbe0ca3b254ed73480455a337c1941904f375d583efcdc57966f98" +dependencies = [ + "amplify", + "derive-deftly 1.1.0", + "derive_more", + "filetime", + "fs-mistrust", + "fslock", + "fslock-guard", + "futures", + "itertools 0.14.0", + "oneshot-fused-workaround", + "paste", + "sanitize-filename", + "serde", + "serde_json", + "thiserror 2.0.12", + "time", + "tor-async-utils", + "tor-basic-utils", + "tor-error", + "tracing", + "void", +] + +[[package]] +name = "tor-proto" +version = "0.32.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89554cb29f3002b33851e5113b10ae97ca395548a11639de557caac01f627f41" +dependencies = [ + "amplify", + "asynchronous-codec", + "bitvec", + "bytes", + "caret", + "cfg-if", + "cipher", + "coarsetime", + "criterion-cycles-per-byte", + "derive-deftly 1.1.0", + "derive_builder_fork_arti", + "derive_more", + "digest", + "educe", + "futures", + "futures-util", + "hkdf", + "hmac", + "itertools 0.14.0", + "oneshot-fused-workaround", + "pin-project", + "postage", + "rand 0.9.1", + "rand_core 0.9.3", + "safelog", + "slotmap-careful", + "smallvec", + "static_assertions", + "subtle", + "sync_wrapper 1.0.2", + "thiserror 2.0.12", + "tokio", + "tokio-util", + "tor-async-utils", + "tor-basic-utils", + "tor-bytes", + "tor-cell", + "tor-cert", + "tor-checkable", + "tor-config", + "tor-error", + "tor-hscrypto", + "tor-linkspec", + "tor-llcrypto", + "tor-log-ratelim", + "tor-memquota", + "tor-protover", + "tor-rtcompat", + "tor-rtmock", + "tor-units", + "tracing", + "typenum", + "visibility", + "void", + "zeroize", +] + +[[package]] +name = "tor-protover" +version = "0.32.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4af2a2d9610dafe2fd8eb79015f91e0e8881155147d676b190e061ae7ee0403e" +dependencies = [ + "caret", + "paste", + "serde_with", + "thiserror 2.0.12", + "tor-bytes", +] + +[[package]] +name = "tor-relay-selection" +version = "0.32.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5516546b720c83a21636824f2a03c64caf1a72bc1f94ee16672679f1295e7a6" +dependencies = [ + "rand 0.9.1", + "serde", + "tor-basic-utils", + "tor-linkspec", + "tor-netdir", + "tor-netdoc", +] + +[[package]] +name = "tor-rtcompat" +version = "0.32.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75969f63c5147af49753e1c03339d342bc3e53e7412518e52702cc417cff729" +dependencies = [ + "async-native-tls", + "async-trait", + "async_executors", + "asynchronous-codec", + "coarsetime", + "derive_more", + "dyn-clone", + "educe", + "futures", + "hex", + "libc", + "native-tls", + "paste", + "pin-project", + "thiserror 2.0.12", + "tokio", + "tokio-util", + "tor-error", + "tor-general-addr", + "tracing", + "void", +] + +[[package]] +name = "tor-rtmock" +version = "0.32.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a13e2946c396cf450ea4f9a3b48f2c4125b011da6019d7da7f1e0e7cd37596c9" +dependencies = [ + "amplify", + "assert_matches", + "async-trait", + "derive-deftly 1.1.0", + "derive_more", + "educe", + "futures", + "humantime", + "itertools 0.14.0", + "oneshot-fused-workaround", + "pin-project", + "priority-queue", + "slotmap-careful", + "strum 0.27.1", + "thiserror 2.0.12", + "tor-error", + "tor-general-addr", + "tor-rtcompat", + "tracing", + "tracing-test", + "void", +] + +[[package]] +name = "tor-socksproto" +version = "0.32.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b7d2a9b7394b8f2c683d282f4a0da08c056ed23b3bb9afdb84cb92d554c77b" +dependencies = [ + "amplify", + "caret", + "derive-deftly 1.1.0", + "educe", + "safelog", + "subtle", + "thiserror 2.0.12", + "tor-bytes", + "tor-error", +] + +[[package]] +name = "tor-units" +version = "0.32.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22f2bd3dc4f5defec5d4b9d152d911a3a852d08409558dd927ec8eb28e20f9de" +dependencies = [ + "derive-deftly 1.1.0", + "derive_more", + "serde", + "thiserror 2.0.12", + "tor-memquota", +] + [[package]] name = "tower" version = "0.5.1" @@ -3444,7 +6519,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3566e8ce28cc0a3fe42519fc80e6b4c943cc4c8cef275620eb8dac2d3d4e06cf" dependencies = [ "crossbeam-channel", - "thiserror", + "thiserror 1.0.69", "time", "tracing-subscriber", ] @@ -3487,20 +6562,55 @@ version = "0.3.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e8189decb5ac0fa7bc8b96b7cb9b2701d60d48805aca84a238004d665fcc4008" dependencies = [ + "matchers", "nu-ansi-term", + "once_cell", + "regex", "sharded-slab", "smallvec", "thread_local", + "tracing", "tracing-core", "tracing-log", ] +[[package]] +name = "tracing-test" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "557b891436fe0d5e0e363427fc7f217abf9ccd510d5136549847bdcbcd011d68" +dependencies = [ + "tracing-core", + "tracing-subscriber", + "tracing-test-macro", +] + +[[package]] +name = "tracing-test-macro" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04659ddb06c87d233c566112c1c9c5b9e98256d9af50ec3bc9c8327f873a7568" +dependencies = [ + "quote", + "syn 2.0.101", +] + [[package]] name = "try-lock" version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" +[[package]] +name = "typed-index-collections" +version = "3.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fd393dbd1e7b23e0cab7396570309b4068aa504e9dac2cd41d827583b4e9ab7" +dependencies = [ + "bincode 2.0.1", + "serde", +] + [[package]] name = "typenum" version = "1.18.0" @@ -3513,12 +6623,27 @@ version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eaea85b334db583fe3274d12b4cd1880032beab409c0d774be044d4480ab9a94" +[[package]] +name = "uncased" +version = "0.9.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1b88fcfe09e89d3866a5c11019378088af2d24c3fbd4f0543f96b479ec90697" +dependencies = [ + "version_check", +] + [[package]] name = "unicode-ident" version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" +[[package]] +name = "unicode-segmentation" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" + [[package]] name = "unicode-xid" version = "0.2.6" @@ -3531,6 +6656,12 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" +[[package]] +name = "unty" +version = "0.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d49784317cd0d1ee7ec5c716dd598ec5b4483ea832a2dced265471cc0f690ae" + [[package]] name = "ureq" version = "2.12.1" @@ -3572,12 +6703,35 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65" +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + [[package]] name = "version_check" version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" +[[package]] +name = "visibility" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d674d135b4a8c1d7e813e2f8d1c9a58308aee4a680323066025e53132218bd91" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.101", +] + +[[package]] +name = "void" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" + [[package]] name = "wait-timeout" version = "0.2.1" @@ -3587,6 +6741,16 @@ dependencies = [ "libc", ] +[[package]] +name = "walkdir" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" +dependencies = [ + "same-file", + "winapi-util", +] + [[package]] name = "want" version = "0.3.1" @@ -3611,6 +6775,15 @@ dependencies = [ "wit-bindgen-rt", ] +[[package]] +name = "wasix" +version = "0.12.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1fbb4ef9bbca0c1170e0b00dd28abc9e3b68669821600cad1caaed606583c6d" +dependencies = [ + "wasi 0.11.0+wasi-snapshot-preview1", +] + [[package]] name = "wasm-bindgen" version = "0.2.100" @@ -3669,6 +6842,22 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "weak-table" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "323f4da9523e9a669e1eaf9c6e763892769b1d38c623913647bfdc1532fe4549" + +[[package]] +name = "web-sys" +version = "0.3.77" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33b6dd2ef9186f1f2072e409e99cd22a975331a6b3591b12c764e0e55c60d5d2" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + [[package]] name = "webpki-roots" version = "0.26.11" @@ -3703,6 +6892,15 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" +[[package]] +name = "winapi-util" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" +dependencies = [ + "windows-sys 0.59.0", +] + [[package]] name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" @@ -3991,6 +7189,24 @@ dependencies = [ "tap", ] +[[package]] +name = "x25519-dalek" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7e468321c81fb07fa7f4c636c3972b9100f0346e5b6a9f2bd0603a52f7ed277" +dependencies = [ + "curve25519-dalek", + "rand_core 0.6.4", + "serde", + "zeroize", +] + +[[package]] +name = "xxhash-rust" +version = "0.8.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fdd20c5420375476fbd4394763288da7eb0cc0b8c11deed431a91562af7335d3" + [[package]] name = "yansi" version = "1.0.1" diff --git a/Cargo.toml b/Cargo.toml index 32f8248..c510625 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -167,6 +167,7 @@ axum = { version = "0.7", default-features = false } anyhow = { version = "1", default-features = false } arc-swap = { version = "1", default-features = false } arrayvec = { version = "0.7", default-features = false } +arti-client = { version = "0.32", default-features = false } async-trait = { version = "0.1", default-features = false } bitflags = { version = "2", default-features = false } blake3 = { version = "1", default-features = false } @@ -203,11 +204,18 @@ thiserror = { version = "1", default-features = false } thread_local = { version = "1", default-features = false } tokio-util = { version = "0.7", default-features = false } tokio-stream = { version = "0.1", default-features = false } +tokio-socks = { git = "https://github.com/Cuprate/tokio-socks.git", rev = "8737caf", default-features = false } tokio = { version = "1", default-features = false } tower = { git = "https://github.com/Cuprate/tower.git", rev = "6c7faf0", default-features = false } # tower-http = { version = "0.6", default-features = false } toml = { version = "0.8", default-features = false } toml_edit = { version = "0.22", default-features = false } +tor-cell = { version = "0.32", default-features = false } +tor-config-path = { version = "0.32", default-features = false } +tor-hsservice = { version = "0.32", default-features = false } +tor-persist = { version = "0.32", default-features = false } +tor-proto = { version = "0.32", default-features = false } +tor-rtcompat = { version = "0.32", default-features = false } tracing-appender = { version = "0.2", default-features = false } tracing-subscriber = { version = "0.3", default-features = false } tracing = { version = "0.1", default-features = false } diff --git a/binaries/cuprated/Cargo.toml b/binaries/cuprated/Cargo.toml index 64778a9..bb21ccc 100644 --- a/binaries/cuprated/Cargo.toml +++ b/binaries/cuprated/Cargo.toml @@ -28,6 +28,7 @@ cuprate-hex = { workspace = true } cuprate-json-rpc = { workspace = true } cuprate-levin = { workspace = true } cuprate-p2p-core = { workspace = true } +cuprate-p2p-transport = { workspace = true } cuprate-p2p = { workspace = true } cuprate-pruning = { workspace = true } cuprate-rpc-interface = { workspace = true, features = ["dummy"] } @@ -41,6 +42,7 @@ cuprate-wire = { workspace = true } # TODO: after v1.0.0, remove unneeded dependencies. axum = { workspace = true, features = ["tokio", "http1", "http2"] } anyhow = { workspace = true } +arti-client = { workspace = true, features = ["tokio", "native-tls", "onion-service-client", "onion-service-service", "static"] } async-trait = { workspace = true } bitflags = { workspace = true } borsh = { workspace = true } @@ -79,6 +81,9 @@ tokio-stream = { workspace = true } tokio = { workspace = true } toml = { workspace = true, features = ["parse", "display"]} toml_edit = { workspace = true } +tor-hsservice = { workspace = true } +tor-persist = { workspace = true } +tor-rtcompat = { workspace = true } tower = { workspace = true, features = ["limit"] } tower-http = { workspace = true, features = ["limit"] } tracing-appender = { workspace = true } diff --git a/binaries/cuprated/src/config.rs b/binaries/cuprated/src/config.rs index d74deeb..feddff2 100644 --- a/binaries/cuprated/src/config.rs +++ b/binaries/cuprated/src/config.rs @@ -8,6 +8,7 @@ use std::{ time::Duration, }; +use arti_client::KeystoreSelector; use clap::Parser; use serde::{Deserialize, Serialize}; @@ -17,11 +18,13 @@ use cuprate_helper::{ network::Network, }; use cuprate_p2p::block_downloader::BlockDownloaderConfig; -use cuprate_p2p_core::ClearNet; +use cuprate_p2p_core::{ClearNet, Tor}; +use cuprate_wire::OnionAddr; use crate::{ constants::{DEFAULT_CONFIG_STARTUP_DELAY, DEFAULT_CONFIG_WARNING}, logging::eprintln_red, + tor::{TorContext, TorMode}, }; mod args; @@ -31,6 +34,7 @@ mod rayon; mod rpc; mod storage; mod tokio; +mod tor; mod tracing_config; #[macro_use] @@ -42,6 +46,7 @@ use rayon::RayonConfig; pub use rpc::RpcConfig; use storage::StorageConfig; use tokio::TokioConfig; +use tor::TorConfig; use tracing_config::TracingConfig; /// Header to put at the start of the generated config file. @@ -144,6 +149,10 @@ config_struct! { /// Configuration for cuprated's P2P system. pub p2p: P2PConfig, + #[child = true] + /// Configuration for cuprated's Tor component + pub tor: TorConfig, + #[child = true] /// Configuration for cuprated's RPC system. pub rpc: RpcConfig, @@ -165,6 +174,7 @@ impl Default for Config { fast_sync: true, tracing: Default::default(), tokio: Default::default(), + tor: Default::default(), rayon: Default::default(), p2p: Default::default(), rpc: Default::default(), @@ -227,6 +237,45 @@ impl Config { } } + /// The [`Tor`], [`cuprate_p2p::P2PConfig`]. + pub fn tor_p2p_config(&self, ctx: &TorContext) -> cuprate_p2p::P2PConfig { + let inbound_enabled = self.p2p.tor_net.inbound_onion; + let our_onion_address = match ctx.mode { + TorMode::Off => None, + TorMode::Daemon => inbound_enabled.then(|| + OnionAddr::new( + &self.tor.daemon.anonymous_inbound, + self.p2p.tor_net.p2p_port + ).expect("Unable to parse supplied `anonymous_inbound` onion address. Please make sure the address is correct.")), + TorMode::Arti => inbound_enabled.then(|| { + let addr = ctx.arti_onion_service + .as_ref() + .unwrap() + .generate_identity_key(KeystoreSelector::Primary) + .unwrap() + .to_string(); + + OnionAddr::new(&addr, self.p2p.tor_net.p2p_port).unwrap() + }) + }; + + cuprate_p2p::P2PConfig { + network: self.network, + seeds: p2p::tor_net_seed_nodes(self.network), + outbound_connections: self.p2p.tor_net.outbound_connections, + extra_outbound_connections: self.p2p.tor_net.extra_outbound_connections, + max_inbound_connections: self.p2p.tor_net.max_inbound_connections, + gray_peers_percent: self.p2p.tor_net.gray_peers_percent, + p2p_port: self.p2p.tor_net.p2p_port, + rpc_port: 0, + address_book_config: self.p2p.tor_net.address_book_config.address_book_config( + &self.fs.cache_directory, + self.network, + our_onion_address, + ), + } + } + /// The [`ContextConfig`]. pub const fn context_config(&self) -> ContextConfig { match self.network { diff --git a/binaries/cuprated/src/config/p2p.rs b/binaries/cuprated/src/config/p2p.rs index 2fda708..e4b13a8 100644 --- a/binaries/cuprated/src/config/p2p.rs +++ b/binaries/cuprated/src/config/p2p.rs @@ -5,16 +5,24 @@ use std::{ time::Duration, }; +use arti_client::{ + config::onion_service::{OnionServiceConfig, OnionServiceConfigBuilder}, + TorClient, TorClientBuilder, TorClientConfig, +}; use serde::{Deserialize, Serialize}; +use tor_rtcompat::PreferredRuntime; use cuprate_helper::{fs::address_book_path, network::Network}; use cuprate_p2p::config::TransportConfig; use cuprate_p2p_core::{ transports::{Tcp, TcpServerConfig}, - ClearNet, NetworkZone, Transport, + ClearNet, NetworkZone, Tor, Transport, }; +use cuprate_p2p_transport::{Arti, ArtiClientConfig, ArtiServerConfig}; use cuprate_wire::OnionAddr; +use crate::{p2p::ProxySettings, tor::TorMode}; + use super::macros::config_struct; config_struct! { @@ -26,6 +34,10 @@ config_struct! { /// The clear-net P2P config. pub clear_net: ClearNetConfig, + #[child = true] + /// The tor-net P2P config. + pub tor_net: TorNetConfig, + #[child = true] /// Block downloader config. /// @@ -147,9 +159,10 @@ config_struct! { /// Examples | 0.0, 0.5, 0.123, 0.999, 1.0 pub gray_peers_percent: f64, - /// The port to use to accept incoming IPv4 P2P connections. + /// The port bind to this network zone. /// - /// Setting this to 0 will disable incoming P2P connections. + /// This port will be bind to if the incoming P2P + /// server for this zone has been enabled. /// /// Type | Number /// Valid values | 0..65534 @@ -165,6 +178,17 @@ config_struct! { #[derive(Debug, Deserialize, Serialize, PartialEq)] #[serde(deny_unknown_fields, default)] pub struct ClearNetConfig { + + /// Enable IPv4 inbound server. + /// + /// The inbound server will listen on port `p2p.clear_net.p2p_port`. + /// Setting this to `false` will disable incoming IPv4 P2P connections. + /// + /// Type | boolean + /// Valid values | false, true + /// Examples | false + pub enable_inbound: bool, + /// The IPv4 address to bind and listen for connections on. /// /// Type | IPv4 address @@ -173,6 +197,7 @@ config_struct! { /// Enable IPv6 inbound server. /// + /// The inbound server will listen on port `p2p.clear_net.p2p_port`. /// Setting this to `false` will disable incoming IPv6 P2P connections. /// /// Type | boolean @@ -185,20 +210,83 @@ config_struct! { /// Type | IPv6 address /// Examples | "::", "2001:0db8:85a3:0000:0000:8a2e:0370:7334" pub listen_on_v6: Ipv6Addr, + + #[comment_out = true] + /// The proxy to use for outgoing P2P connections + /// + /// This setting can only take "Tor" at the moment. + /// This will anonymise clearnet connections through Tor. + /// + /// Setting this to "" (an empty string) will disable the proxy. + /// + /// Enabling this setting will disable inbound connections. + /// + /// Type | String + /// Valid values | "Tor" + /// Examples | "Tor" + pub proxy: ProxySettings, + } + + /// The config values for P2P tor. + #[derive(Debug, Deserialize, Serialize, PartialEq)] + #[serde(deny_unknown_fields, default)] + pub struct TorNetConfig { + + #[comment_out = true] + /// Enable the Tor P2P network. + /// + /// Type | boolean + /// Valid values | false, true + /// Examples | false + pub enabled: bool, + + #[comment_out = true] + /// Enable Tor inbound onion server. + /// + /// In Arti mode, setting this to `true` will enable Arti's onion service for accepting inbound + /// Tor P2P connections. The keypair and therefore onion address is generated randomly on first run. + /// + /// In Daemon mode, setting this to `true` will enable a TCP server listening for inbound connections + /// from your Tor daemon. Refer to the `tor.anonymous_inbound` and `tor.listening_addr` field for onion address + /// and listening configuration. + /// + /// The server will listen on port `p2p.tor_net.p2p_port` + /// + /// Type | boolean + /// Valid values | false, true + /// Examples | false + pub inbound_onion: bool, } } impl Default for ClearNetConfig { fn default() -> Self { Self { + p2p_port: 18080, + enable_inbound: true, listen_on: Ipv4Addr::UNSPECIFIED, enable_inbound_v6: false, listen_on_v6: Ipv6Addr::UNSPECIFIED, + proxy: ProxySettings::Socks(String::new()), outbound_connections: 32, extra_outbound_connections: 8, max_inbound_connections: 128, gray_peers_percent: 0.7, + address_book_config: AddressBookConfig::default(), + } + } +} + +impl Default for TorNetConfig { + fn default() -> Self { + Self { + enabled: false, + inbound_onion: false, p2p_port: 18080, + outbound_connections: 12, + extra_outbound_connections: 2, + max_inbound_connections: 128, + gray_peers_percent: 0.7, address_book_config: AddressBookConfig::default(), } } diff --git a/binaries/cuprated/src/config/tor.rs b/binaries/cuprated/src/config/tor.rs new file mode 100644 index 0000000..1fbdbfe --- /dev/null +++ b/binaries/cuprated/src/config/tor.rs @@ -0,0 +1,133 @@ +use std::{ + net::{IpAddr, Ipv4Addr, SocketAddr, SocketAddrV4}, + path::PathBuf, +}; + +use serde::{Deserialize, Serialize}; + +use cuprate_helper::fs::CUPRATE_DATA_DIR; + +use crate::{config::macros::config_struct, tor::TorMode}; + +config_struct! { + /// Arti config + #[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq)] + #[serde(deny_unknown_fields, default)] + #[allow(rustdoc::broken_intra_doc_links)] + pub struct ArtiConfig { + /// Path to the arti state directory. + /// + /// The default directories for each OS: + /// + /// | OS | Path | + /// |---------|-----------------------------------------------------| + /// | Windows | "C:\Users\Alice\AppData\Roaming\Cuprate\" | + /// | macOS | "/Users/Alice/Library/Application Support/Cuprate/" | + /// | Linux | "/home/alice/.local/share/cuprate/" | + pub directory_path: PathBuf, + + /// Enable isolated circuits for Arti. + /// + /// If set, Arti will use different tor circuits for each connections. This can + /// cause stability issues if the connection count is important. + /// + /// Type | boolean + /// Valid values | false, true + /// Examples | false + pub isolated_circuit: bool, + + /// Enable PoW security for Arti. + /// + /// If set, Arti will enforce an EquiX PoW to be resolved for + /// other nodes to complete a rendez-vous request when under + /// heavy load. + /// + /// Type | boolean + /// Valid values | false, true + /// Examples | false + pub onion_service_pow: bool, + } + + /// Tor config + #[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq)] + #[serde(deny_unknown_fields, default)] + #[allow(rustdoc::broken_intra_doc_links)] + pub struct TorDaemonConfig { + /// The IP address and port of the external Tor daemon to use for outgoing connections. + /// + /// Type | Socket address + /// Examples | "[::1]:9050", "127.0.0.1:9050" + pub address: SocketAddr, + + #[comment_out = true] + /// Enable inbound connections for Daemon mode + /// + /// This string specify the onion address that should be advertized to the Tor network + /// and that your daemon should be expecting connections from. + /// + /// When this is set, `p2p.tor_net.p2p_port` is not used for host listening, but as the source + /// port of your hidden service in your torrc configuration file. For setting Cuprate's + /// listening port see `tor.listening_addr` field + /// + /// Type | String + /// Valid values | "<56 character domain>.onion" + /// Examples | "monerotoruzizulg5ttgat2emf4d6fbmiea25detrmmy7erypseyteyd.onion" + pub anonymous_inbound: String, + + /// The IP address and port to bind and listen on for anonymous inbound connections from Tor Daemon. + /// + /// Type | Socket address + /// Examples | "0.0.0.0:18083", "192.168.1.50:2000", "[::]:5000", "[2001:0db8:85a3:0000:0000:8a2e:0370:7334]:18082" + pub listening_addr: SocketAddr, + } + + /// Tor config + #[derive(Debug, Default, Clone, Deserialize, Serialize, PartialEq, Eq)] + #[serde(deny_unknown_fields, default)] + #[allow(rustdoc::broken_intra_doc_links)] + pub struct TorConfig { + + #[comment_out = true] + /// Enable Tor network by specifying how to connect to it. + /// + /// When "Daemon" is set, the Tor daemon address to use can be + /// specified in `tor.daemon_addr`. + /// + /// Type | String + /// Valid values | "Arti", "Daemon", "Off" + /// Examples | "Arti" + pub mode: TorMode, + + #[child = true] + /// Arti config + /// + /// Only relevant if `tor.mode` is set to "Arti" + pub arti: ArtiConfig, + + #[child = true] + /// Tor Daemon config + /// + /// Only relevant if `tor.mode` is set to "Daemon" + pub daemon: TorDaemonConfig, + } +} + +impl Default for TorDaemonConfig { + fn default() -> Self { + Self { + address: "127.0.0.1:9050".parse().unwrap(), + anonymous_inbound: String::new(), + listening_addr: SocketAddrV4::new(Ipv4Addr::LOCALHOST, 18083).into(), + } + } +} + +impl Default for ArtiConfig { + fn default() -> Self { + Self { + directory_path: CUPRATE_DATA_DIR.join("arti"), + isolated_circuit: false, + onion_service_pow: false, + } + } +} diff --git a/binaries/cuprated/src/main.rs b/binaries/cuprated/src/main.rs index 0081dbb..5bf6feb 100644 --- a/binaries/cuprated/src/main.rs +++ b/binaries/cuprated/src/main.rs @@ -34,7 +34,10 @@ use cuprate_types::blockchain::BlockchainWriteRequest; use txpool::IncomingTxHandler; use crate::{ - config::Config, constants::PANIC_CRITICAL_SERVICE_ERROR, logging::CupratedTracingFilter, + config::Config, + constants::PANIC_CRITICAL_SERVICE_ERROR, + logging::CupratedTracingFilter, + tor::{initialize_tor_if_enabled, TorMode}, }; mod blockchain; @@ -47,6 +50,7 @@ mod p2p; mod rpc; mod signals; mod statics; +mod tor; mod txpool; mod version; @@ -118,20 +122,23 @@ fn main() { .await .unwrap(); + // Bootstrap or configure Tor if enabled. + let tor_context = initialize_tor_if_enabled(&config).await; + // Start p2p network zones let (network_interfaces, tx_handler_subscribers) = p2p::initialize_zones_p2p( &config, context_svc.clone(), - blockchain_write_handle.clone(), blockchain_read_handle.clone(), - txpool_write_handle.clone(), txpool_read_handle.clone(), + tor_context, ) .await; // Create the incoming tx handler service. let tx_handler = IncomingTxHandler::init( network_interfaces.clearnet_network_interface.clone(), + network_interfaces.tor_network_interface, txpool_write_handle.clone(), txpool_read_handle.clone(), context_svc.clone(), diff --git a/binaries/cuprated/src/p2p.rs b/binaries/cuprated/src/p2p.rs index 3a0e410..b268c93 100644 --- a/binaries/cuprated/src/p2p.rs +++ b/binaries/cuprated/src/p2p.rs @@ -4,14 +4,20 @@ use std::convert::From; +use arti_client::TorClient; use futures::{FutureExt, TryFutureExt}; +use serde::{Deserialize, Serialize}; use tokio::sync::oneshot::{self, Sender}; +use tor_rtcompat::PreferredRuntime; use tower::{Service, ServiceExt}; use cuprate_blockchain::service::{BlockchainReadHandle, BlockchainWriteHandle}; use cuprate_consensus::BlockchainContextService; use cuprate_p2p::{config::TransportConfig, NetworkInterface, P2PConfig}; -use cuprate_p2p_core::{client::InternalPeerID, transports::Tcp, ClearNet, NetworkZone, Transport}; +use cuprate_p2p_core::{ + client::InternalPeerID, transports::Tcp, ClearNet, NetworkZone, Tor, Transport, +}; +use cuprate_p2p_transport::{Arti, ArtiClientConfig, Daemon}; use cuprate_txpool::service::{TxpoolReadHandle, TxpoolWriteHandle}; use cuprate_types::blockchain::BlockchainWriteRequest; @@ -19,6 +25,10 @@ use crate::{ blockchain, config::Config, constants::PANIC_CRITICAL_SERVICE_ERROR, + tor::{ + transport_arti_config, transport_clearnet_arti_config, transport_daemon_config, TorContext, + TorMode, + }, txpool::{self, IncomingTxHandler}, }; @@ -28,10 +38,20 @@ pub mod request_handler; pub use network_address::CrossNetworkInternalPeerId; +/// A simple parsing enum for the `p2p.clear_net.proxy` field +#[derive(Debug, Deserialize, Serialize, PartialEq, Eq)] +pub enum ProxySettings { + Tor, + #[serde(untagged)] + Socks(String), +} + /// This struct collect all supported and optional network zone interfaces. pub struct NetworkInterfaces { /// Mandatory clearnet network interface pub clearnet_network_interface: NetworkInterface, + /// Optional tor network interface + pub tor_network_interface: Option>, // ...one can dream for more! } @@ -39,6 +59,7 @@ impl NetworkInterfaces { pub const fn new(clearnet_network_interface: NetworkInterface) -> Self { Self { clearnet_network_interface, + tor_network_interface: None, } } } @@ -48,25 +69,93 @@ impl NetworkInterfaces { pub async fn initialize_zones_p2p( config: &Config, context_svc: BlockchainContextService, - mut blockchain_write_handle: BlockchainWriteHandle, mut blockchain_read_handle: BlockchainReadHandle, - txpool_write_handle: TxpoolWriteHandle, txpool_read_handle: TxpoolReadHandle, + tor_ctx: TorContext, ) -> (NetworkInterfaces, Vec>) { - // Start TCP clearnet P2P. - let (clearnet, incoming_tx_handler_tx) = start_zone_p2p::( - blockchain_read_handle.clone(), - context_svc.clone(), - txpool_read_handle.clone(), - config.clearnet_p2p_config(), - (&config.p2p.clear_net).into(), - ) - .await - .unwrap(); + // Start clearnet P2P. + let (clearnet, incoming_tx_handler_tx) = { + // If proxy is set + match config.p2p.clear_net.proxy { + ProxySettings::Tor => match tor_ctx.mode { + TorMode::Arti => { + tracing::info!("Anonymizing clearnet connections through Arti."); + start_zone_p2p::( + blockchain_read_handle.clone(), + context_svc.clone(), + txpool_read_handle.clone(), + config.clearnet_p2p_config(), + transport_clearnet_arti_config(&tor_ctx), + ) + .await + .unwrap() + } + TorMode::Daemon => { + tracing::error!("Anonymizing clearnet connections through the Tor daemon is not yet supported."); + std::process::exit(0); + } + TorMode::Off => { + tracing::error!("Clearnet proxy set to \"tor\" but Tor is actually off. Please be sure to set a mode in the configuration or command line"); + std::process::exit(0); + } + }, + ProxySettings::Socks(ref s) => { + if !s.is_empty() { + tracing::error!("Socks proxy is not yet supported."); + std::process::exit(0); + } + + start_zone_p2p::( + blockchain_read_handle.clone(), + context_svc.clone(), + txpool_read_handle.clone(), + config.clearnet_p2p_config(), + (&config.p2p.clear_net).into(), + ) + .await + .unwrap() + } + } + }; // Create network interface collection - let network_interfaces = NetworkInterfaces::new(clearnet); - let tx_handler_subscribers = vec![incoming_tx_handler_tx]; + let mut network_interfaces = NetworkInterfaces::new(clearnet); + let mut tx_handler_subscribers = vec![incoming_tx_handler_tx]; + + // Start Tor P2P (if enabled) + let tor = if config.p2p.tor_net.enabled { + match tor_ctx.mode { + TorMode::Off => None, + TorMode::Daemon => Some( + start_zone_p2p::( + blockchain_read_handle.clone(), + context_svc.clone(), + txpool_read_handle.clone(), + config.tor_p2p_config(&tor_ctx), + transport_daemon_config(config), + ) + .await + .unwrap(), + ), + TorMode::Arti => Some( + start_zone_p2p::( + blockchain_read_handle.clone(), + context_svc.clone(), + txpool_read_handle.clone(), + config.tor_p2p_config(&tor_ctx), + transport_arti_config(config, tor_ctx), + ) + .await + .unwrap(), + ), + } + } else { + None + }; + if let Some((tor, incoming_tx_handler_tx)) = tor { + network_interfaces.tor_network_interface = Some(tor); + tx_handler_subscribers.push(incoming_tx_handler_tx); + } (network_interfaces, tx_handler_subscribers) } diff --git a/binaries/cuprated/src/p2p/request_handler.rs b/binaries/cuprated/src/p2p/request_handler.rs index a7df1dc..79f14db 100644 --- a/binaries/cuprated/src/p2p/request_handler.rs +++ b/binaries/cuprated/src/p2p/request_handler.rs @@ -386,12 +386,14 @@ where return Ok(ProtocolResponse::NA); } - let state = if request.dandelionpp_fluff { + let addr = peer_information.id.into(); + + let anon_zone = matches!(addr, CrossNetworkInternalPeerId::Tor(_)); + + let state = if request.dandelionpp_fluff && !anon_zone { TxState::Fluff } else { - TxState::Stem { - from: peer_information.id.into(), - } + TxState::Stem { from: addr } }; // Drop all the data except the stuff we still need. diff --git a/binaries/cuprated/src/tor.rs b/binaries/cuprated/src/tor.rs new file mode 100644 index 0000000..76dc421 --- /dev/null +++ b/binaries/cuprated/src/tor.rs @@ -0,0 +1,197 @@ +//! Tor initialization +//! +//! Extract configuration and initialize Arti. + +//---------------------------------------------------------------------------------------------------- Imports + +use std::{default, sync::Arc}; + +use arti_client::{ + config::{onion_service::OnionServiceConfigBuilder, CfgPath, TorClientConfigBuilder}, + KeystoreSelector, StreamPrefs, TorClient, TorClientBuilder, TorClientConfig, +}; +use futures::Stream; +use serde::{Deserialize, Serialize}; +use tor_hsservice::{OnionService, RendRequest, RunningOnionService}; +use tor_persist::hsnickname::HsNickname; +use tor_rtcompat::PreferredRuntime; +use tracing::info; + +use cuprate_helper::fs::CUPRATE_DATA_DIR; +use cuprate_p2p::TransportConfig; +use cuprate_p2p_core::{ClearNet, Tor}; +use cuprate_p2p_transport::{ + Arti, ArtiClientConfig, ArtiServerConfig, Daemon, DaemonClientConfig, DaemonServerConfig, +}; +use cuprate_wire::OnionAddr; + +use crate::{config::Config, p2p::ProxySettings}; + +//---------------------------------------------------------------------------------------------------- Initialization + +#[derive(Clone, Default, Debug, Copy, PartialEq, Eq, Serialize, Deserialize)] +/// Describe if Tor is enabled and how +pub enum TorMode { + /// Use of the [`arti_client`] library. + Arti, + /// Use of external tor daemon + Daemon, + + #[default] + /// Tor is disabled + Off, +} + +/// Contains the necessary Tor configuration or structures +/// for initializing P2P. +pub struct TorContext { + /// Which mode are we using. + pub mode: TorMode, + + // -------- Only in Arti mode + /// Arti bootstrapped [`TorClient`]. + pub bootstrapped_client: Option>, + /// Arti bootstrapped client config + pub arti_client_config: Option, + /// Arti onion service address. + pub arti_onion_service: Option, +} + +/// Initialize the Tor network if enabled in configuration +/// +/// This function will bootstrap Arti if needed by Tor network zone or +/// clearnet as a proxy. +pub async fn initialize_tor_if_enabled(config: &Config) -> TorContext { + let mode = config.tor.mode; + let anonymize_clearnet = matches!(config.p2p.clear_net.proxy, ProxySettings::Tor); + + // Start Arti client + let (bootstrapped_client, arti_client_config) = + if mode == TorMode::Arti && (config.p2p.tor_net.enabled || anonymize_clearnet) { + Some(initialize_arti_client(config).await) + } else { + None + } + .unzip(); + + // Start Arti onion service + let arti_onion_service = arti_client_config + .as_ref() + .map(|client_config| initialize_arti_onion_service(client_config, config)); + + TorContext { + mode, + bootstrapped_client, + arti_client_config, + arti_onion_service, + } +} + +/// Initialize Arti Tor client. +async fn initialize_arti_client(config: &Config) -> (TorClient, TorClientConfig) { + // Configuration + let mut tor_config = TorClientConfig::builder(); + + // Storage + tor_config + .storage() + .state_dir(CfgPath::new_literal(config.tor.arti.directory_path.clone())); + + let tor_config = tor_config + .build() + .expect("Failed to build Tor client configuration."); + + // Bootstrapping + info!("Bootstrapping Arti's TorClient..."); + let mut tor_client = TorClient::builder() + .config(tor_config.clone()) + .create_bootstrapped() + .await + .inspect_err(|err| tracing::error!("Unable to bootstrap arti: {err}")) + .unwrap(); + + // Isolation + if config.tor.arti.isolated_circuit { + let mut stream_prefs = StreamPrefs::new(); + stream_prefs.isolate_every_stream(); + tor_client.set_stream_prefs(stream_prefs); + } + + (tor_client, tor_config) +} + +fn initialize_arti_onion_service(client_config: &TorClientConfig, config: &Config) -> OnionService { + let onion_svc_config = OnionServiceConfigBuilder::default() + .enable_pow(config.tor.arti.onion_service_pow) + .nickname(HsNickname::new("cuprate".into()).unwrap()) + .build() + .unwrap(); + + TorClient::::create_onion_service(client_config, onion_svc_config) + .expect("Unable to start Arti onion service.") +} + +//---------------------------------------------------------------------------------------------------- Transport configuration + +pub fn transport_arti_config(config: &Config, ctx: TorContext) -> TransportConfig { + // Extracting + let (Some(bootstrapped_client), Some(client_config)) = + (ctx.bootstrapped_client, ctx.arti_client_config) + else { + panic!("Arti client should be initialized"); + }; + + let server_config = config.p2p.tor_net.inbound_onion.then(|| { + let Some(onion_svc) = ctx.arti_onion_service else { + panic!("inbound onion enabled, but no onion service initialized!"); + }; + + ArtiServerConfig::new( + onion_svc, + config.p2p.tor_net.p2p_port, + &bootstrapped_client, + &client_config, + ) + }); + + TransportConfig:: { + client_config: ArtiClientConfig { + client: bootstrapped_client, + }, + server_config, + } +} + +pub fn transport_clearnet_arti_config(ctx: &TorContext) -> TransportConfig { + let Some(bootstrapped_client) = &ctx.bootstrapped_client else { + panic!("Arti enabled but no TorClient initialized!"); + }; + + TransportConfig:: { + client_config: ArtiClientConfig { + client: bootstrapped_client.clone(), + }, + server_config: None, + } +} + +pub fn transport_daemon_config(config: &Config) -> TransportConfig { + let mut invalid_onion = false; + + if config.p2p.tor_net.inbound_onion && config.tor.daemon.anonymous_inbound.is_empty() { + invalid_onion = true; + tracing::warn!("Onion inbound is enabled yet no onion host has been defined in configuration. Inbound server disabled."); + } + + TransportConfig:: { + client_config: DaemonClientConfig { + tor_daemon: config.tor.daemon.address, + }, + server_config: (config.p2p.tor_net.inbound_onion && !invalid_onion).then_some( + DaemonServerConfig { + ip: config.tor.daemon.listening_addr.ip(), + port: config.tor.daemon.listening_addr.port(), + }, + ), + } +} diff --git a/binaries/cuprated/src/txpool/dandelion.rs b/binaries/cuprated/src/txpool/dandelion.rs index 00d9f5a..14473e9 100644 --- a/binaries/cuprated/src/txpool/dandelion.rs +++ b/binaries/cuprated/src/txpool/dandelion.rs @@ -1,10 +1,17 @@ -use std::time::Duration; +use std::{ + task::{ready, Poll}, + time::Duration, +}; + +use futures::{future::BoxFuture, FutureExt, TryFutureExt}; +use tower::{Service, ServiceExt}; use cuprate_dandelion_tower::{ - pool::DandelionPoolService, DandelionConfig, DandelionRouter, Graph, + pool::DandelionPoolService, traits::StemRequest, DandelionConfig, DandelionRouteReq, + DandelionRouter, DandelionRouterError, Graph, State, TxState, }; use cuprate_p2p::NetworkInterface; -use cuprate_p2p_core::ClearNet; +use cuprate_p2p_core::{client::InternalPeerID, ClearNet, NetworkZone, Tor}; use cuprate_txpool::service::{TxpoolReadHandle, TxpoolWriteHandle}; use crate::{ @@ -12,10 +19,13 @@ use crate::{ txpool::incoming_tx::{DandelionTx, TxId}, }; +mod anon_net_service; mod diffuse_service; mod stem_service; mod tx_store; +pub use anon_net_service::AnonTxService; + /// The configuration used for [`cuprate_dandelion_tower`]. /// /// TODO: should we expose this to users of cuprated? probably not. @@ -27,17 +37,74 @@ const DANDELION_CONFIG: DandelionConfig = DandelionConfig { }; /// A [`DandelionRouter`] with all generic types defined. -type ConcreteDandelionRouter = DandelionRouter< - stem_service::OutboundPeerStream, - diffuse_service::DiffuseService, +pub(super) type ConcreteDandelionRouter = DandelionRouter< + stem_service::OutboundPeerStream, + diffuse_service::DiffuseService, CrossNetworkInternalPeerId, - stem_service::StemPeerService, + stem_service::StemPeerService, DandelionTx, >; +/// The dandelion router used to send transactions to the network. +pub(super) struct MainDandelionRouter { + clearnet_router: ConcreteDandelionRouter, + tor_router: Option>, +} + +impl MainDandelionRouter { + pub const fn new( + clearnet_router: ConcreteDandelionRouter, + tor_router: Option>, + ) -> Self { + Self { + clearnet_router, + tor_router, + } + } +} + +impl Service> for MainDandelionRouter { + type Response = State; + type Error = DandelionRouterError; + type Future = BoxFuture<'static, Result>; + + fn poll_ready(&mut self, cx: &mut std::task::Context<'_>) -> Poll> { + if let Some(tor_router) = self.tor_router.as_mut() { + ready!(tor_router.poll_ready(cx))?; + } + + self.clearnet_router.poll_ready(cx) + } + + fn call( + &mut self, + req: DandelionRouteReq, + ) -> Self::Future { + // TODO: is this the best way to use anonymity networks? + if req.state == TxState::Local { + if let Some(tor_router) = self.tor_router.as_mut() { + if let Some(mut peer) = tor_router.peer.take() { + tracing::debug!("routing tx over Tor"); + return peer + .call(StemRequest(req.tx)) + .map_ok(|_| State::Stem) + .map_err(DandelionRouterError::PeerError) + .boxed(); + } + + tracing::warn!( + "failed to route tx over Tor, no connections, falling back to Clearnet" + ); + } + } + + self.clearnet_router.call(req) + } +} + /// Starts the dandelion pool manager task and returns a handle to send txs to broadcast. pub fn start_dandelion_pool_manager( - router: ConcreteDandelionRouter, + router: MainDandelionRouter, txpool_read_handle: TxpoolReadHandle, txpool_write_handle: TxpoolWriteHandle, ) -> DandelionPoolService { @@ -54,12 +121,17 @@ pub fn start_dandelion_pool_manager( } /// Creates a [`DandelionRouter`] from a [`NetworkInterface`]. -pub fn dandelion_router(clear_net: NetworkInterface) -> ConcreteDandelionRouter { +pub fn dandelion_router( + network_interface: NetworkInterface, +) -> ConcreteDandelionRouter +where + InternalPeerID: Into, +{ DandelionRouter::new( diffuse_service::DiffuseService { - clear_net_broadcast_service: clear_net.broadcast_svc(), + clear_net_broadcast_service: network_interface.broadcast_svc(), }, - stem_service::OutboundPeerStream::new(clear_net), + stem_service::OutboundPeerStream::::new(network_interface), DANDELION_CONFIG, ) } diff --git a/binaries/cuprated/src/txpool/dandelion/anon_net_service.rs b/binaries/cuprated/src/txpool/dandelion/anon_net_service.rs new file mode 100644 index 0000000..e44cdaf --- /dev/null +++ b/binaries/cuprated/src/txpool/dandelion/anon_net_service.rs @@ -0,0 +1,68 @@ +use std::{ + pin::Pin, + task::{ready, Context, Poll}, +}; + +use futures::{Stream, StreamExt, TryStream}; +use tower::Service; + +use cuprate_dandelion_tower::{DandelionRouterError, OutboundPeer}; +use cuprate_p2p::NetworkInterface; +use cuprate_p2p_core::{client::InternalPeerID, NetworkZone}; + +use crate::{ + p2p::CrossNetworkInternalPeerId, + txpool::dandelion::stem_service::{OutboundPeerStream, StemPeerService}, +}; + +/// The service to prepare peers on anonymous network zones for sending transactions. +pub struct AnonTxService { + outbound_peer_discover: Pin>>, + pub peer: Option>, +} + +impl AnonTxService +where + InternalPeerID: Into, +{ + pub fn new(network_interface: NetworkInterface) -> Self { + Self { + outbound_peer_discover: Box::pin(OutboundPeerStream::new(network_interface)), + peer: None, + } + } + + pub fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll> { + loop { + if let Some(peer) = &mut self.peer { + if ready!(peer.poll_ready(cx)).is_err() { + self.peer = None; + + continue; + } + + return Poll::Ready(Ok(())); + } + + let ret = ready!(self + .outbound_peer_discover + .as_mut() + .try_poll_next(cx) + .map_err(DandelionRouterError::OutboundPeerStreamError)) + .ok_or(DandelionRouterError::OutboundPeerDiscoverExited)??; + + match ret { + OutboundPeer::Peer(_, mut svc) => { + let poll = svc.poll_ready(cx); + self.peer = Some(svc); + if ready!(poll).is_err() { + self.peer = None; + } + } + OutboundPeer::Exhausted => return Poll::Ready(Ok(())), + } + } + + Poll::Ready(Ok(())) + } +} diff --git a/binaries/cuprated/src/txpool/dandelion/diffuse_service.rs b/binaries/cuprated/src/txpool/dandelion/diffuse_service.rs index 621503f..a3d6213 100644 --- a/binaries/cuprated/src/txpool/dandelion/diffuse_service.rs +++ b/binaries/cuprated/src/txpool/dandelion/diffuse_service.rs @@ -8,16 +8,16 @@ use tower::Service; use cuprate_dandelion_tower::traits::DiffuseRequest; use cuprate_p2p::{BroadcastRequest, BroadcastSvc}; -use cuprate_p2p_core::ClearNet; +use cuprate_p2p_core::{ClearNet, NetworkZone}; use crate::txpool::dandelion::DandelionTx; /// The dandelion diffusion service. -pub struct DiffuseService { - pub clear_net_broadcast_service: BroadcastSvc, +pub struct DiffuseService { + pub clear_net_broadcast_service: BroadcastSvc, } -impl Service> for DiffuseService { +impl Service> for DiffuseService { type Response = (); type Error = tower::BoxError; type Future = Ready>; diff --git a/binaries/cuprated/src/txpool/dandelion/stem_service.rs b/binaries/cuprated/src/txpool/dandelion/stem_service.rs index 32e35eb..9f4adaf 100644 --- a/binaries/cuprated/src/txpool/dandelion/stem_service.rs +++ b/binaries/cuprated/src/txpool/dandelion/stem_service.rs @@ -12,38 +12,39 @@ use cuprate_dandelion_tower::{traits::StemRequest, OutboundPeer}; use cuprate_p2p::{ClientDropGuard, NetworkInterface, PeerSetRequest, PeerSetResponse}; use cuprate_p2p_core::{ client::{Client, InternalPeerID}, - BroadcastMessage, ClearNet, NetworkZone, PeerRequest, ProtocolRequest, + BroadcastMessage, ClearNet, NetworkZone, PeerRequest, ProtocolRequest, Tor, }; use cuprate_wire::protocol::NewTransactions; use crate::{p2p::CrossNetworkInternalPeerId, txpool::dandelion::DandelionTx}; /// The dandelion outbound peer stream. -pub struct OutboundPeerStream { - clear_net: NetworkInterface, - state: OutboundPeerStreamState, +pub struct OutboundPeerStream { + network_interface: NetworkInterface, + state: OutboundPeerStreamState, } -impl OutboundPeerStream { - pub const fn new(clear_net: NetworkInterface) -> Self { +impl OutboundPeerStream { + pub const fn new(network_interface: NetworkInterface) -> Self { Self { - clear_net, + network_interface, state: OutboundPeerStreamState::Standby, } } } -impl Stream for OutboundPeerStream { - type Item = Result< - OutboundPeer>, - tower::BoxError, - >; +impl Stream for OutboundPeerStream +where + InternalPeerID: Into, +{ + type Item = + Result>, tower::BoxError>; fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { loop { match &mut self.state { OutboundPeerStreamState::Standby => { - let peer_set = self.clear_net.peer_set(); + let peer_set = self.network_interface.peer_set(); let res = ready!(peer_set.poll_ready(cx)); self.state = OutboundPeerStreamState::AwaitingPeer( @@ -61,10 +62,9 @@ impl Stream for OutboundPeerStream { }; match stem_peer { - Some(peer) => OutboundPeer::Peer( - CrossNetworkInternalPeerId::ClearNet(peer.info.id), - StemPeerService(peer), - ), + Some(peer) => { + OutboundPeer::Peer(peer.info.id.into(), StemPeerService(peer)) + } None => OutboundPeer::Exhausted, } }))); @@ -75,11 +75,11 @@ impl Stream for OutboundPeerStream { } /// The state of the [`OutboundPeerStream`]. -enum OutboundPeerStreamState { +enum OutboundPeerStreamState { /// Standby state. Standby, /// Awaiting a response from the peer-set. - AwaitingPeer(BoxFuture<'static, Result, tower::BoxError>>), + AwaitingPeer(BoxFuture<'static, Result, tower::BoxError>>), } /// The stem service, used to send stem txs. diff --git a/binaries/cuprated/src/txpool/incoming_tx.rs b/binaries/cuprated/src/txpool/incoming_tx.rs index 4f0cc6c..e1f81c4 100644 --- a/binaries/cuprated/src/txpool/incoming_tx.rs +++ b/binaries/cuprated/src/txpool/incoming_tx.rs @@ -21,7 +21,7 @@ use cuprate_dandelion_tower::{ }; use cuprate_helper::asynch::rayon_spawn_async; use cuprate_p2p::NetworkInterface; -use cuprate_p2p_core::ClearNet; +use cuprate_p2p_core::{ClearNet, Tor}; use cuprate_txpool::{ service::{ interface::{ @@ -39,7 +39,7 @@ use crate::{ p2p::CrossNetworkInternalPeerId, signals::REORG_LOCK, txpool::{ - dandelion, + dandelion::{self, AnonTxService, ConcreteDandelionRouter, MainDandelionRouter}, relay_rules::{check_tx_relay_rules, RelayRuleError}, txs_being_handled::{TxsBeingHandled, TxsBeingHandledLocally}, }, @@ -105,12 +105,16 @@ impl IncomingTxHandler { #[expect(clippy::significant_drop_tightening)] pub fn init( clear_net: NetworkInterface, + tor_net: Option>, txpool_write_handle: TxpoolWriteHandle, txpool_read_handle: TxpoolReadHandle, blockchain_context_cache: BlockchainContextService, blockchain_read_handle: BlockchainReadHandle, ) -> Self { - let dandelion_router = dandelion::dandelion_router(clear_net); + let clearnet_router = dandelion::dandelion_router(clear_net); + let tor_router = tor_net.map(AnonTxService::new); + + let dandelion_router = MainDandelionRouter::new(clearnet_router, tor_router); let dandelion_pool_manager = dandelion::start_dandelion_pool_manager( dandelion_router, diff --git a/consensus/fast-sync/src/fast_sync.rs b/consensus/fast-sync/src/fast_sync.rs index 6b519df..a11fb6b 100644 --- a/consensus/fast-sync/src/fast_sync.rs +++ b/consensus/fast-sync/src/fast_sync.rs @@ -303,7 +303,7 @@ mod tests { let entry = ChainEntry { ids, - peer: InternalPeerID::Unknown(1), + peer: InternalPeerID::Unknown([1; 16]), handle: handle.1 }; @@ -335,7 +335,7 @@ mod tests { let entries = (0..len).map(|i| { ChainEntry { ids: vec![HASHES.get(i).copied().unwrap_or_default()], - peer: InternalPeerID::Unknown(1), + peer: InternalPeerID::Unknown([1; 16]), handle: handle.1.clone() } }).collect(); @@ -369,7 +369,7 @@ mod tests { let handle = HandleBuilder::new().build(); let entry = ChainEntry { ids: HASHES[hashes_start_height..(hashes_start_height + len)].to_vec(), - peer: InternalPeerID::Unknown(1), + peer: InternalPeerID::Unknown([1; 16]), handle: handle.1 }; diff --git a/deny.toml b/deny.toml index 305e451..3d831ed 100644 --- a/deny.toml +++ b/deny.toml @@ -83,7 +83,8 @@ ignore = [ #{ crate = "a-crate-that-is-yanked@0.1.1", reason = "you can specify why you are ignoring the yanked crate" }, # TODO: check this is sorted before a beta release. - { id = "RUSTSEC-2024-0436", reason = "`paste` unmaintained, not necessarily vulnerable yet." } + { id = "RUSTSEC-2024-0436", reason = "`paste` unmaintained, not necessarily vulnerable yet." }, + { id = "RUSTSEC-2023-0071", reason = "rsa is used in arti." } ] # If this is true, then cargo deny will use the git executable to fetch advisory database. # If this is false, then it uses a built-in git library. @@ -101,6 +102,7 @@ ignore = [ allow = [ # Nothing required - free to use without permission. "CC0-1.0", # https://creativecommons.org/publicdomain/zero/1.0/ + "Unlicense", # Must include copyright notice. "BSD-2-Clause", # https://tldrlegal.com/license/bsd-2-clause-license-(freebsd) diff --git a/net/epee-encoding/src/macros.rs b/net/epee-encoding/src/macros.rs index 269e438..7df1823 100644 --- a/net/epee-encoding/src/macros.rs +++ b/net/epee-encoding/src/macros.rs @@ -14,7 +14,7 @@ pub use paste::paste; /// /// struct Example { /// a: u8 -/// } +/// } /// /// epee_object!( /// Example, @@ -132,6 +132,7 @@ macro_rules! epee_object { use super::*; #[derive(Default)] + #[allow(clippy::empty_structs_with_brackets)] pub struct [<__Builder $obj>] { $($field: Option,)* $($flat_field: <$flat_ty as cuprate_epee_encoding::EpeeObject>::Builder,)* diff --git a/net/wire/Cargo.toml b/net/wire/Cargo.toml index 84f413c..013e472 100644 --- a/net/wire/Cargo.toml +++ b/net/wire/Cargo.toml @@ -18,6 +18,7 @@ cuprate-types = { workspace = true, default-features = false, features cuprate-helper = { workspace = true, default-features = false, features = ["map"] } bitflags = { workspace = true, features = ["std"] } +borsh = { workspace = true, features = ["derive"] } bytes = { workspace = true, features = ["std"] } thiserror = { workspace = true } diff --git a/net/wire/src/network_address/onion_addr.rs b/net/wire/src/network_address/onion_addr.rs index ce4632d..4aa8686 100644 --- a/net/wire/src/network_address/onion_addr.rs +++ b/net/wire/src/network_address/onion_addr.rs @@ -8,12 +8,13 @@ use std::{ str::{self, FromStr}, }; +use borsh::{BorshDeserialize, BorshSerialize}; use thiserror::Error; use super::{NetworkAddress, NetworkAddressIncorrectZone}; /// A v3, `Copy`able onion address. -#[derive(Clone, Debug, Copy, PartialEq, Eq, Hash)] +#[derive(Clone, Debug, Copy, PartialEq, Eq, Hash, BorshSerialize, BorshDeserialize)] pub struct OnionAddr { /// 56 characters encoded onion v3 domain without the .onion suffix /// diff --git a/p2p/dandelion-tower/src/pool/manager.rs b/p2p/dandelion-tower/src/pool/manager.rs index 2ac3302..a911959 100644 --- a/p2p/dandelion-tower/src/pool/manager.rs +++ b/p2p/dandelion-tower/src/pool/manager.rs @@ -12,7 +12,7 @@ use tokio::{ sync::{mpsc, oneshot}, task::JoinSet, }; -use tokio_util::time::DelayQueue; +use tokio_util::time::{delay_queue, DelayQueue}; use tower::{Service, ServiceExt}; use crate::{ @@ -41,6 +41,7 @@ pub struct DandelionPoolManager { /// Current stem pool embargo timers. pub(crate) embargo_timers: DelayQueue, + pub(crate) embargo_timer_keys: HashMap, /// The distrobution to sample to get embargo timers. pub(crate) embargo_dist: Exp, @@ -68,8 +69,10 @@ where embargo_timer ); - self.embargo_timers - .insert(tx_id, Duration::from_secs_f64(embargo_timer)); + let key = self + .embargo_timers + .insert(tx_id.clone(), Duration::from_secs_f64(embargo_timer)); + self.embargo_timer_keys.insert(tx_id, key); } /// Stems the tx, setting the stem origin, if it wasn't already set. @@ -164,9 +167,9 @@ where // Remove the tx from the maps used during the stem phase. self.stem_origins.remove(&tx_id); - // The key for this is *Not* the tx_id, it is given on insert, so just keep the timer in the - // map. These timers should be relatively short, so it shouldn't be a problem. - //self.embargo_timers.try_remove(&tx_id); + if let Some(key) = self.embargo_timer_keys.remove(&tx_id) { + self.embargo_timers.try_remove(&key); + } self.backing_pool .ready() diff --git a/p2p/dandelion-tower/src/pool/mod.rs b/p2p/dandelion-tower/src/pool/mod.rs index 40a3617..90eb555 100644 --- a/p2p/dandelion-tower/src/pool/mod.rs +++ b/p2p/dandelion-tower/src/pool/mod.rs @@ -87,6 +87,7 @@ where routing_set: JoinSet::new(), stem_origins: HashMap::new(), embargo_timers: DelayQueue::new(), + embargo_timer_keys: HashMap::new(), embargo_dist: Exp::new(1.0 / config.average_embargo_timeout().as_secs_f64()).unwrap(), config, _tx: PhantomData, diff --git a/p2p/dandelion-tower/src/router.rs b/p2p/dandelion-tower/src/router.rs index 7ca0598..8344f55 100644 --- a/p2p/dandelion-tower/src/router.rs +++ b/p2p/dandelion-tower/src/router.rs @@ -183,8 +183,14 @@ where .map_err(DandelionRouterError::OutboundPeerStreamError)) .ok_or(DandelionRouterError::OutboundPeerDiscoverExited)?? { - OutboundPeer::Peer(key, svc) => { - self.stem_peers.insert(key, svc); + OutboundPeer::Peer(key, mut svc) => { + let poll = svc.poll_ready(cx); + + self.stem_peers.insert(key.clone(), svc); + + if ready!(poll).is_err() { + self.stem_peers.remove(&key); + } } OutboundPeer::Exhausted => { tracing::warn!("Failed to retrieve enough outbound peers for optimal dandelion++, privacy may be degraded."); diff --git a/p2p/p2p-core/Cargo.toml b/p2p/p2p-core/Cargo.toml index dd14373..21c076a 100644 --- a/p2p/p2p-core/Cargo.toml +++ b/p2p/p2p-core/Cargo.toml @@ -28,6 +28,8 @@ rand = { workspace = true, features = ["std", "std_rng"] } tracing = { workspace = true, features = ["std", "attributes"] } hex-literal = { workspace = true } +hex = { workspace = true } + borsh = { workspace = true, features = ["derive", "std"], optional = true } [dev-dependencies] diff --git a/p2p/p2p-core/src/client.rs b/p2p/p2p-core/src/client.rs index ca0f201..77d5086 100644 --- a/p2p/p2p-core/src/client.rs +++ b/p2p/p2p-core/src/client.rs @@ -40,14 +40,14 @@ pub enum InternalPeerID { /// A known address. KnownAddr(A), /// An unknown address (probably an inbound anonymity network connection). - Unknown(u128), + Unknown([u8; 16]), } impl Display for InternalPeerID { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { match self { Self::KnownAddr(addr) => addr.fmt(f), - Self::Unknown(id) => f.write_str(&format!("Unknown, ID: {id}")), + Self::Unknown(id) => f.write_str(&format!("Unknown, ID: {}", hex::encode(id))), } } } diff --git a/p2p/p2p-core/src/client/timeout_monitor.rs b/p2p/p2p-core/src/client/timeout_monitor.rs index 54bc50e..79b95a3 100644 --- a/p2p/p2p-core/src/client/timeout_monitor.rs +++ b/p2p/p2p-core/src/client/timeout_monitor.rs @@ -28,7 +28,7 @@ use crate::{ fields(addr = %peer_information.id), skip_all, )] -pub async fn connection_timeout_monitor_task( +pub(super) async fn connection_timeout_monitor_task( peer_information: PeerInformation, connection_tx: mpsc::Sender, diff --git a/p2p/p2p-core/src/lib.rs b/p2p/p2p-core/src/lib.rs index 979e5e2..c874943 100644 --- a/p2p/p2p-core/src/lib.rs +++ b/p2p/p2p-core/src/lib.rs @@ -168,9 +168,9 @@ pub trait Transport: Clone + Send + 'static { /// Note: Currently, this client config is considered immutable during operational runtime. If one /// wish to apply modifications on the fly, they will need to make use of an inner shared and mutable /// reference to do so. - type ClientConfig: Default + Clone + Debug + Send + Sync + 'static; + type ClientConfig: Clone + Send + Sync + 'static; /// Server configuration necessary when instantiating a listener for inbound connections. - type ServerConfig: Default + Clone + Debug + Send + Sync + 'static; + type ServerConfig: Send + Sync + 'static; /// The stream (incoming data) type of this transport method. type Stream: Stream> + Unpin + Send + 'static; diff --git a/p2p/p2p-transport/Cargo.toml b/p2p/p2p-transport/Cargo.toml index f4d8f9f..65c7e2d 100644 --- a/p2p/p2p-transport/Cargo.toml +++ b/p2p/p2p-transport/Cargo.toml @@ -5,7 +5,27 @@ edition = "2021" license = "MIT" authors = ["SyntheticBird"] +[features] +default = ["static"] +static = ["arti-client/static"] + [dependencies] +cuprate-p2p-core = { workspace = true } +cuprate-wire = { workspace = true } + +async-trait = { workspace = true } +futures = { workspace = true } +tokio = { workspace = true, features = ["net"] } +tokio-socks = { workspace = true, features = ["tokio"] } +tokio-util = { workspace = true, features = ["codec"] } +arti-client = { workspace = true, features = ["tokio", "native-tls", "onion-service-client", "onion-service-service", "experimental-api", "static-sqlite"] } +tor-config-path = { workspace = true } +tor-cell = { workspace = true } +tor-hsservice = { workspace = true } +tor-proto = { workspace = true } +tor-rtcompat = { workspace = true } + +tracing = { workspace = true } [lints] workspace = true diff --git a/p2p/p2p-transport/src/arti.rs b/p2p/p2p-transport/src/arti.rs new file mode 100644 index 0000000..aff90ce --- /dev/null +++ b/p2p/p2p-transport/src/arti.rs @@ -0,0 +1,221 @@ +//! Arti Transport +//! +//! This module defines a transport method for the `Tor` network zone using the `arti_client` library. +//! + +//---------------------------------------------------------------------------------------------------- Imports + +use std::{ + io::{self, ErrorKind}, + pin::Pin, + sync::Arc, + task::{Context, Poll}, +}; + +use arti_client::{DataReader, DataWriter, TorClient, TorClientConfig}; +use async_trait::async_trait; +use futures::{Stream, StreamExt}; +use tokio_util::codec::{FramedRead, FramedWrite}; +use tor_cell::relaycell::msg::Connected; +use tor_config_path::CfgPathResolver; +use tor_hsservice::{handle_rend_requests, OnionService, RunningOnionService}; +use tor_proto::stream::IncomingStreamRequest; +use tor_rtcompat::PreferredRuntime; + +use cuprate_p2p_core::{ClearNet, NetworkZone, Tor, Transport}; +use cuprate_wire::MoneroWireCodec; + +use crate::DisabledListener; + +//---------------------------------------------------------------------------------------------------- Configuration + +#[derive(Clone)] +pub struct ArtiClientConfig { + /// Arti bootstrapped client + pub client: TorClient, +} + +pub struct ArtiServerConfig { + /// Arti onion service + pub onion_svc: OnionService, + /// Listening port + pub port: u16, + + // Mandatory resources for launching the onion service + client: TorClient, + path_resolver: Arc, +} + +impl ArtiServerConfig { + pub fn new( + onion_svc: OnionService, + port: u16, + client: &TorClient, + config: &TorClientConfig, + ) -> Self { + let path_resolver: &CfgPathResolver = config.as_ref(); + + Self { + onion_svc, + port, + client: client.clone(), + path_resolver: Arc::new(path_resolver.clone()), + } + } +} + +//---------------------------------------------------------------------------------------------------- Transport + +type PinnedStream = Pin + Send>>; + +/// An onion service listening for incoming peer connections. +pub struct OnionListener { + /// A handle to the onion service instance. + _onion_svc: Arc, + /// A modified stream that produce a data stream and sink from rendez-vous requests. + listener: PinnedStream>, +} + +impl Stream for OnionListener { + type Item = Result< + ( + Option<::Addr>, + FramedRead, + FramedWrite, + ), + io::Error, + >; + + fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + match self.listener.poll_next_unpin(cx) { + Poll::Pending => Poll::Pending, + Poll::Ready(req) => Poll::Ready(req.map(|r| { + r.map(|(stream, sink)| { + ( + None, // Inbound is anonymous + FramedRead::new(stream, MoneroWireCodec::default()), + FramedWrite::new(sink, MoneroWireCodec::default()), + ) + }) + })), + } + } +} + +#[derive(Clone, Copy)] +pub struct Arti; + +#[async_trait] +impl Transport for Arti { + type ClientConfig = ArtiClientConfig; + type ServerConfig = ArtiServerConfig; + + type Stream = FramedRead; + type Sink = FramedWrite; + type Listener = OnionListener; + + async fn connect_to_peer( + addr: ::Addr, + config: &Self::ClientConfig, + ) -> Result<(Self::Stream, Self::Sink), io::Error> { + config + .client + .connect((addr.addr_string(), addr.port())) + .await + .map_err(|e| io::Error::new(ErrorKind::ConnectionAborted, e.to_string())) + .map(|stream| { + let (stream, sink) = stream.split(); + ( + FramedRead::new(stream, MoneroWireCodec::default()), + FramedWrite::new(sink, MoneroWireCodec::default()), + ) + }) + } + + async fn incoming_connection_listener( + config: Self::ServerConfig, + ) -> Result { + tracing::info!( + "Listening for incoming Tor P2P connections on address: {}:{}", + config.onion_svc.onion_address().unwrap(), + config.port + ); + + // Launch onion service + #[expect(clippy::clone_on_ref_ptr)] + let (svc, rdv_stream) = config + .onion_svc + .launch( + config.client.runtime().clone(), + config.client.dirmgr().clone(), + config.client.hs_circ_pool().clone(), + config.path_resolver, + ) + .unwrap(); + + // Accept all rendez-vous and await correct stream request + let req_stream = handle_rend_requests(rdv_stream).then(move |sreq| async move { + match sreq.request() { + // As specified in: + // + // A client that wishes to open a data stream with us needs to send a BEGIN message with an empty address + // and no flags. We additionally filter requests to the correct port configured and advertised on P2P. + IncomingStreamRequest::Begin(r) + if r.port() == config.port && r.addr().is_empty() && r.flags().is_empty() => + { + let stream = sreq + .accept(Connected::new_empty()) + .await + .map_err(|e| io::Error::new(ErrorKind::BrokenPipe, e.to_string()))?; + + Ok(stream.split()) + } + _ => { + sreq.shutdown_circuit() + .expect("Should never panic, unless programming error from arti's end."); + + Err(io::Error::other("Received invalid command")) + } + } + }); + + Ok(OnionListener { + _onion_svc: svc, + listener: Box::pin(req_stream), + }) + } +} + +#[async_trait] +impl Transport for Arti { + type ClientConfig = ArtiClientConfig; + type ServerConfig = (); + + type Stream = FramedRead; + type Sink = FramedWrite; + type Listener = DisabledListener; + + async fn connect_to_peer( + addr: ::Addr, + config: &Self::ClientConfig, + ) -> Result<(Self::Stream, Self::Sink), io::Error> { + config + .client + .connect(addr.to_string()) + .await + .map_err(|e| io::Error::new(ErrorKind::ConnectionAborted, e.to_string())) + .map(|stream| { + let (stream, sink) = stream.split(); + ( + FramedRead::new(stream, MoneroWireCodec::default()), + FramedWrite::new(sink, MoneroWireCodec::default()), + ) + }) + } + + async fn incoming_connection_listener( + _config: Self::ServerConfig, + ) -> Result { + panic!("In anonymized clearnet mode, inbound is disabled!"); + } +} diff --git a/p2p/p2p-transport/src/disabled.rs b/p2p/p2p-transport/src/disabled.rs new file mode 100644 index 0000000..a0b0590 --- /dev/null +++ b/p2p/p2p-transport/src/disabled.rs @@ -0,0 +1,35 @@ +use std::{ + io, + marker::PhantomData, + pin::Pin, + task::{Context, Poll}, +}; + +use futures::Stream; +use tokio_util::codec::{FramedRead, FramedWrite}; + +use cuprate_p2p_core::NetworkZone; +use cuprate_wire::MoneroWireCodec; + +/// In proxied clearnet mode, inbound is disabled. +pub struct DisabledListener { + _zone: PhantomData, + _reader: PhantomData, + _writer: PhantomData, +} + +impl Stream for DisabledListener { + type Item = Result< + ( + Option, + FramedRead, + FramedWrite, + ), + io::Error, + >; + + fn poll_next(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll> { + // Panic within [`Transport::incoming_connection_listener`] + unreachable!() + } +} diff --git a/p2p/p2p-transport/src/lib.rs b/p2p/p2p-transport/src/lib.rs index b2e7577..b6abb85 100644 --- a/p2p/p2p-transport/src/lib.rs +++ b/p2p/p2p-transport/src/lib.rs @@ -1,3 +1,15 @@ //! ## P2P Transports //! -//! This crate will welcome additional transport implementation for Cuprate. +//! This crate implement additional transports for Cuprate. + +/// Arti library implementation. +mod arti; +pub use arti::{Arti, ArtiClientConfig, ArtiServerConfig}; + +/// Tor daemon (SOCKS5) implementation +mod tor; +pub use tor::{Daemon, DaemonClientConfig, DaemonServerConfig}; + +/// Disabled listener +mod disabled; +pub(crate) use disabled::DisabledListener; diff --git a/p2p/p2p-transport/src/tor.rs b/p2p/p2p-transport/src/tor.rs new file mode 100644 index 0000000..2409eac --- /dev/null +++ b/p2p/p2p-transport/src/tor.rs @@ -0,0 +1,112 @@ +//! Tor Daemon Transport +//! +//! This module defines a transport method for the `Tor` network zone using an external Tor daemon supporting SOCKS5. +//! + +//---------------------------------------------------------------------------------------------------- Imports + +use std::{ + io::{self, ErrorKind}, + net::{IpAddr, SocketAddr}, + pin::Pin, + task::{Context, Poll}, +}; + +use async_trait::async_trait; +use futures::Stream; +use tokio::net::{ + tcp::{OwnedReadHalf, OwnedWriteHalf}, + TcpListener, +}; +use tokio_socks::tcp::Socks5Stream; +use tokio_util::codec::{FramedRead, FramedWrite}; + +use cuprate_p2p_core::{NetworkZone, Tor, Transport}; +use cuprate_wire::MoneroWireCodec; + +//---------------------------------------------------------------------------------------------------- Configuration + +#[derive(Clone, Copy)] +pub struct DaemonClientConfig { + /// Socket address of the external Tor daemon + pub tor_daemon: SocketAddr, +} + +#[derive(Clone, Copy)] +pub struct DaemonServerConfig { + /// Listening IP Address. + pub ip: IpAddr, + + /// Listening TCP Port. + pub port: u16, +} + +//---------------------------------------------------------------------------------------------------- Transport + +/// A simple TCP server waiting for connections from the Tor daemon +pub struct DaemonInboundStream { + listener: TcpListener, +} + +impl Stream for DaemonInboundStream { + type Item = Result< + ( + Option<::Addr>, + FramedRead, + FramedWrite, + ), + io::Error, + >; + + fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + self.listener + .poll_accept(cx) + .map_ok(|(stream, _)| { + let (stream, sink) = stream.into_split(); + + ( + None, // Inbound is anonymous + FramedRead::new(stream, MoneroWireCodec::default()), + FramedWrite::new(sink, MoneroWireCodec::default()), + ) + }) + .map(Some) + } +} + +#[derive(Clone, Copy)] +pub struct Daemon; + +#[async_trait] +impl Transport for Daemon { + type ClientConfig = DaemonClientConfig; + type ServerConfig = DaemonServerConfig; + + type Stream = FramedRead; + type Sink = FramedWrite; + type Listener = DaemonInboundStream; + + async fn connect_to_peer( + addr: ::Addr, + config: &Self::ClientConfig, + ) -> Result<(Self::Stream, Self::Sink), io::Error> { + Socks5Stream::connect(config.tor_daemon, addr.to_string()) + .await + .map_err(|e| io::Error::new(ErrorKind::ConnectionAborted, e.to_string())) + .map(|stream| { + let (stream, sink) = stream.into_inner().into_split(); + ( + FramedRead::new(stream, MoneroWireCodec::default()), + FramedWrite::new(sink, MoneroWireCodec::default()), + ) + }) + } + + async fn incoming_connection_listener( + config: Self::ServerConfig, + ) -> Result { + let listener = TcpListener::bind((config.ip, config.port)).await?; + + Ok(DaemonInboundStream { listener }) + } +} diff --git a/p2p/p2p/src/block_downloader/download_batch.rs b/p2p/p2p/src/block_downloader/download_batch.rs index dd95218..17848b2 100644 --- a/p2p/p2p/src/block_downloader/download_batch.rs +++ b/p2p/p2p/src/block_downloader/download_batch.rs @@ -30,7 +30,7 @@ use crate::{ ) )] #[expect(clippy::used_underscore_binding)] -pub async fn download_batch_task( +pub(super) async fn download_batch_task( client: ClientDropGuard, ids: ByteArrayVec<32>, previous_id: [u8; 32], diff --git a/p2p/p2p/src/block_downloader/request_chain.rs b/p2p/p2p/src/block_downloader/request_chain.rs index 9adbaaa..659574a 100644 --- a/p2p/p2p/src/block_downloader/request_chain.rs +++ b/p2p/p2p/src/block_downloader/request_chain.rs @@ -79,7 +79,7 @@ pub(crate) async fn request_chain_entry_from_peer( /// /// We then wait for their response and choose the peer who claims the highest cumulative difficulty. #[instrument(level = "error", skip_all)] -pub async fn initial_chain_search( +pub(super) async fn initial_chain_search( peer_set: &mut BoxCloneService, tower::BoxError>, mut our_chain_svc: C, ) -> Result, BlockDownloadError> diff --git a/p2p/p2p/src/broadcast.rs b/p2p/p2p/src/broadcast.rs index a12d7a2..09fe1f7 100644 --- a/p2p/p2p/src/broadcast.rs +++ b/p2p/p2p/src/broadcast.rs @@ -424,8 +424,8 @@ mod tests { let (mut brcst, outbound_mkr, inbound_mkr) = init_broadcast_channels::>(TEST_CONFIG); - let mut outbound_stream = pin!(outbound_mkr(InternalPeerID::Unknown(1))); - let mut inbound_stream = pin!(inbound_mkr(InternalPeerID::Unknown(1))); + let mut outbound_stream = pin!(outbound_mkr(InternalPeerID::Unknown([1; 16]))); + let mut inbound_stream = pin!(inbound_mkr(InternalPeerID::Unknown([1; 16]))); // Outbound should get 1 and 3, inbound should get 2 and 3. @@ -483,8 +483,8 @@ mod tests { let (mut brcst, outbound_mkr, inbound_mkr) = init_broadcast_channels::>(TEST_CONFIG); - let mut outbound_stream = pin!(outbound_mkr(InternalPeerID::Unknown(1))); - let mut inbound_stream = pin!(inbound_mkr(InternalPeerID::Unknown(1))); + let mut outbound_stream = pin!(outbound_mkr(InternalPeerID::Unknown([1; 16]))); + let mut inbound_stream = pin!(inbound_mkr(InternalPeerID::Unknown([1; 16]))); brcst .ready() @@ -509,11 +509,11 @@ mod tests { let (mut brcst, outbound_mkr, inbound_mkr) = init_broadcast_channels::>(TEST_CONFIG); - let mut outbound_stream = pin!(outbound_mkr(InternalPeerID::Unknown(1))); - let mut outbound_stream_from = pin!(outbound_mkr(InternalPeerID::Unknown(0))); + let mut outbound_stream = pin!(outbound_mkr(InternalPeerID::Unknown([1; 16]))); + let mut outbound_stream_from = pin!(outbound_mkr(InternalPeerID::Unknown([0; 16]))); - let mut inbound_stream = pin!(inbound_mkr(InternalPeerID::Unknown(1))); - let mut inbound_stream_from = pin!(inbound_mkr(InternalPeerID::Unknown(0))); + let mut inbound_stream = pin!(inbound_mkr(InternalPeerID::Unknown([1; 16]))); + let mut inbound_stream_from = pin!(inbound_mkr(InternalPeerID::Unknown([0; 16]))); brcst .ready() @@ -522,7 +522,7 @@ mod tests { .call(BroadcastRequest::Transaction { tx_bytes: Bytes::from_static(&[1]), direction: None, - received_from: Some(InternalPeerID::Unknown(0)), + received_from: Some(InternalPeerID::Unknown([0; 16])), }) .await .unwrap(); diff --git a/p2p/p2p/src/inbound_server.rs b/p2p/p2p/src/inbound_server.rs index 735ff98..33105a4 100644 --- a/p2p/p2p/src/inbound_server.rs +++ b/p2p/p2p/src/inbound_server.rs @@ -34,7 +34,7 @@ use crate::{ /// Starts the inbound server. This function will listen to all incoming connections /// and initiate handshake if needed, after verifying the address isn't banned. #[instrument(level = "warn", skip_all)] -pub async fn inbound_server( +pub(super) async fn inbound_server( new_connection_tx: mpsc::Sender>, mut handshaker: HS, mut address_book: A, diff --git a/p2p/p2p/src/lib.rs b/p2p/p2p/src/lib.rs index 55fd428..64c7544 100644 --- a/p2p/p2p/src/lib.rs +++ b/p2p/p2p/src/lib.rs @@ -39,7 +39,7 @@ pub use peer_set::{ClientDropGuard, PeerSetRequest, PeerSetResponse}; /// You must provide: /// - A protocol request handler, which is given to each connection /// - A core sync service, which keeps track of the sync state of our node -#[instrument(level = "debug", name = "net", skip_all, fields(zone = Z::NAME))] +#[instrument(level = "error", name = "net", skip_all, fields(zone = Z::NAME))] pub async fn initialize_network( protocol_request_handler_maker: PR, core_sync_svc: CS, diff --git a/rpc/types/src/macros.rs b/rpc/types/src/macros.rs index da5c9ce..971f4af 100644 --- a/rpc/types/src/macros.rs +++ b/rpc/types/src/macros.rs @@ -107,6 +107,7 @@ macro_rules! define_request_and_response { /// $( #[$type_attr] )* /// + #[allow(clippy::empty_structs_with_brackets)] $( #[$request_type_attr] )* [<$type_name Request>] $(($restricted $(, $empty)?))? { $( From 00e4a498b30901c03fee9739b27b2a4f3104008e Mon Sep 17 00:00:00 2001 From: Omar Date: Thu, 7 Aug 2025 09:28:33 -0400 Subject: [PATCH 10/16] P2P: Add hourly warning logging for no incoming P2P connections (#520) * Add hourly logging for no incoming P2P connections * Address clippy findings * Avoid creating Duration inside monitoring loop --------- Co-authored-by: Omar Murad --- p2p/p2p/src/inbound_server.rs | 5 ++-- p2p/p2p/src/lib.rs | 55 ++++++++++++++++++++++++++++++++++- 2 files changed, 57 insertions(+), 3 deletions(-) diff --git a/p2p/p2p/src/inbound_server.rs b/p2p/p2p/src/inbound_server.rs index 33105a4..bbba64f 100644 --- a/p2p/p2p/src/inbound_server.rs +++ b/p2p/p2p/src/inbound_server.rs @@ -40,6 +40,7 @@ pub(super) async fn inbound_server( mut address_book: A, config: P2PConfig, transport_config: Option, + inbound_semaphore: Arc, ) -> Result<(), tower::BoxError> where Z: NetworkZone, @@ -67,8 +68,8 @@ where let mut listener = pin!(listener); - // Create semaphore for limiting to maximum inbound connections. - let semaphore = Arc::new(Semaphore::new(config.max_inbound_connections)); + // Use the provided semaphore for limiting to maximum inbound connections. + let semaphore = inbound_semaphore; // Create ping request handling JoinSet let mut ping_join_set = JoinSet::new(); diff --git a/p2p/p2p/src/lib.rs b/p2p/p2p/src/lib.rs index 64c7544..378a21d 100644 --- a/p2p/p2p/src/lib.rs +++ b/p2p/p2p/src/lib.rs @@ -5,7 +5,11 @@ use std::sync::Arc; use futures::FutureExt; -use tokio::{sync::mpsc, task::JoinSet}; +use tokio::{ + sync::mpsc, + task::JoinSet, + time::{sleep, Duration}, +}; use tower::{buffer::Buffer, util::BoxCloneService, Service, ServiceExt}; use tracing::{instrument, Instrument, Span}; @@ -31,6 +35,40 @@ use connection_maintainer::MakeConnectionRequest; use peer_set::PeerSet; pub use peer_set::{ClientDropGuard, PeerSetRequest, PeerSetResponse}; +/// Interval for checking inbound connection status (1 hour) +const INBOUND_CONNECTION_MONITOR_INTERVAL: Duration = Duration::from_secs(3600); + +/// Monitors for inbound connections and logs a warning if none are detected. +/// +/// This task runs every hour to check if there are inbound connections available. +/// If `max_inbound_connections` is 0, the task will exit without logging. +#[expect(clippy::infinite_loop)] +async fn inbound_connection_monitor( + inbound_semaphore: Arc, + max_inbound_connections: usize, + p2p_port: u16, +) { + // Skip monitoring if inbound connections are disabled + if max_inbound_connections == 0 { + return; + } + + loop { + // Wait for the monitoring interval + sleep(INBOUND_CONNECTION_MONITOR_INTERVAL).await; + + // Check if we have any inbound connections + // If available permits equals max_inbound_connections, no peers are connected + let available_permits = inbound_semaphore.available_permits(); + if available_permits == max_inbound_connections { + tracing::warn!( + "No incoming connections - check firewalls/routers allow port {}", + p2p_port + ); + } + } +} + /// Initializes the P2P [`NetworkInterface`] for a specific [`NetworkZone`]. /// /// This function starts all the tasks to maintain/accept/make connections. @@ -111,6 +149,9 @@ where let peer_set = PeerSet::new(new_connection_rx); + // Create semaphore for limiting inbound connections and monitoring + let inbound_semaphore = Arc::new(tokio::sync::Semaphore::new(config.max_inbound_connections)); + let mut background_tasks = JoinSet::new(); background_tasks.spawn( @@ -118,6 +159,17 @@ where .run() .instrument(Span::current()), ); + + // Spawn inbound connection monitor task + background_tasks.spawn( + inbound_connection_monitor( + Arc::clone(&inbound_semaphore), + config.max_inbound_connections, + config.p2p_port, + ) + .instrument(tracing::info_span!("inbound_connection_monitor")), + ); + background_tasks.spawn( inbound_server::inbound_server( new_connection_tx, @@ -125,6 +177,7 @@ where address_book.clone(), config, transport_config.server_config, + inbound_semaphore, ) .map(|res| { if let Err(e) = res { From 51d4e5f13cb32691b2e048bdc17d4d06d09d1526 Mon Sep 17 00:00:00 2001 From: Boog900 Date: Tue, 12 Aug 2025 22:43:07 +0100 Subject: [PATCH 11/16] fix clippy (#525) --- binaries/cuprated/src/config/macros.rs | 1 + consensus/src/transactions.rs | 2 +- p2p/address-book/src/peer_list/tests.rs | 4 +++- p2p/p2p-core/src/lib.rs | 2 +- storage/database/src/key.rs | 2 +- 5 files changed, 7 insertions(+), 4 deletions(-) diff --git a/binaries/cuprated/src/config/macros.rs b/binaries/cuprated/src/config/macros.rs index 07e3cb2..18e3bc1 100644 --- a/binaries/cuprated/src/config/macros.rs +++ b/binaries/cuprated/src/config/macros.rs @@ -115,6 +115,7 @@ macro_rules! config_struct { $($tt: tt)* ) => { + #[allow(clippy::doc_markdown, clippy::allow_attributes)] $(#[$meta])* pub struct $name { $( diff --git a/consensus/src/transactions.rs b/consensus/src/transactions.rs index ec6dfda..1cae186 100644 --- a/consensus/src/transactions.rs +++ b/consensus/src/transactions.rs @@ -158,7 +158,7 @@ impl VerificationWanted { hf: HardFork, database: D, batch_prep_cache: Option<&BatchPrepareCache>, - ) -> FullVerification { + ) -> FullVerification<'_, D> { FullVerification { prepped_txs: self.prepped_txs, current_chain_height, diff --git a/p2p/address-book/src/peer_list/tests.rs b/p2p/address-book/src/peer_list/tests.rs index 352e305..d55816c 100644 --- a/p2p/address-book/src/peer_list/tests.rs +++ b/p2p/address-book/src/peer_list/tests.rs @@ -90,7 +90,9 @@ fn peer_list_remove_specific_peer() { let peers = peer_list.peers; for (_, addrs) in pruning_idxs { - addrs.iter().for_each(|adr| assert_ne!(adr, &peer.adr)); + for adr in &addrs { + assert_ne!(adr, &peer.adr); + } } assert!(!peers.contains_key(&peer.adr)); diff --git a/p2p/p2p-core/src/lib.rs b/p2p/p2p-core/src/lib.rs index c874943..03fceb5 100644 --- a/p2p/p2p-core/src/lib.rs +++ b/p2p/p2p-core/src/lib.rs @@ -160,7 +160,7 @@ pub trait NetworkZone: Clone + Copy + Send + 'static { /// peer or instantiating a listener for the `NetworkZone` `Z` over a `Transport` method `T`. /// /// Ultimately, multiple transports can implement the same trait for providing alternative -/// ways for a network zone to operate (example: ClearNet can operate on both TCP and Tor.) +/// ways for a network zone to operate (example: [`ClearNet`] can operate on both TCP and Tor.) #[async_trait::async_trait] pub trait Transport: Clone + Send + 'static { /// Client configuration necessary when establishing a connection to a peer. diff --git a/storage/database/src/key.rs b/storage/database/src/key.rs index 2f3855a..8770dda 100644 --- a/storage/database/src/key.rs +++ b/storage/database/src/key.rs @@ -133,7 +133,7 @@ impl_custom_numbers_key!(u8, u16, u128, i8, i16, i32, i64, i128, isize); /// This determines how the database sorts [`Key`]s inside a database [`Table`](crate::Table). /// /// See [`Key`] for more info. -#[derive(Default, Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] +#[derive(Default, Copy, Clone, Debug, Hash)] pub enum KeyCompare { /// Use the default comparison behavior of the backend. /// From 93772f17ab44783cf222c90fc83ac3002f31bf6a Mon Sep 17 00:00:00 2001 From: Boog900 Date: Tue, 12 Aug 2025 22:43:21 +0100 Subject: [PATCH 12/16] p2p: Expose support flags (#521) * expose connected peer's support flags * expose basic node data * derive clone * fix IPv4 Endianness * fix import order * add node address to incoming peer list * fix tests --- p2p/address-book/src/book.rs | 2 +- p2p/p2p-core/src/client.rs | 4 +++- p2p/p2p-core/src/client/connector.rs | 1 + p2p/p2p-core/src/client/handshaker.rs | 2 ++ p2p/p2p-core/src/client/handshaker/builder/dummy.rs | 5 ++--- p2p/p2p-core/src/client/timeout_monitor.rs | 1 + p2p/p2p-core/src/services.rs | 5 ++++- p2p/p2p/src/block_downloader/tests.rs | 11 ++++++++++- 8 files changed, 24 insertions(+), 7 deletions(-) diff --git a/p2p/address-book/src/book.rs b/p2p/address-book/src/book.rs index 462be00..04befa7 100644 --- a/p2p/address-book/src/book.rs +++ b/p2p/address-book/src/book.rs @@ -394,7 +394,7 @@ impl Service> for AddressBook { }, ) .map(|()| AddressBookResponse::Ok), - AddressBookRequest::IncomingPeerList(peer_list) => { + AddressBookRequest::IncomingPeerList(_, peer_list) => { self.handle_incoming_peer_list(peer_list); Ok(AddressBookResponse::Ok) } diff --git a/p2p/p2p-core/src/client.rs b/p2p/p2p-core/src/client.rs index 77d5086..56c0ae4 100644 --- a/p2p/p2p-core/src/client.rs +++ b/p2p/p2p-core/src/client.rs @@ -15,7 +15,7 @@ use tracing::Instrument; use cuprate_helper::asynch::InfallibleOneshotReceiver; use cuprate_pruning::PruningSeed; -use cuprate_wire::CoreSyncData; +use cuprate_wire::{BasicNodeData, CoreSyncData}; use crate::{ handles::{ConnectionGuard, ConnectionHandle}, @@ -64,6 +64,8 @@ pub struct PeerInformation { pub direction: ConnectionDirection, /// The peer's [`PruningSeed`]. pub pruning_seed: PruningSeed, + /// The peer's [`BasicNodeData`]. + pub basic_node_data: BasicNodeData, /// The [`CoreSyncData`] of this peer. /// /// Data across fields are not necessarily related, so [`CoreSyncData::top_id`] is not always the diff --git a/p2p/p2p-core/src/client/connector.rs b/p2p/p2p-core/src/client/connector.rs index 2da83bd..33245e1 100644 --- a/p2p/p2p-core/src/client/connector.rs +++ b/p2p/p2p-core/src/client/connector.rs @@ -32,6 +32,7 @@ pub struct ConnectRequest { } /// The connector service, this service connects to peer and returns the [`Client`]. +#[derive(Clone)] pub struct Connector, AdrBook, CSync, ProtoHdlrMkr, BrdcstStrmMkr> { handshaker: HandShaker, } diff --git a/p2p/p2p-core/src/client/handshaker.rs b/p2p/p2p-core/src/client/handshaker.rs index f3d7266..010a19a 100644 --- a/p2p/p2p-core/src/client/handshaker.rs +++ b/p2p/p2p-core/src/client/handshaker.rs @@ -347,6 +347,7 @@ where .ready() .await? .call(AddressBookRequest::IncomingPeerList( + addr, handshake_res .local_peerlist_new .into_iter() @@ -486,6 +487,7 @@ where handle, direction, pruning_seed, + basic_node_data: peer_node_data, core_sync_data: Arc::new(Mutex::new(peer_core_sync)), }; diff --git a/p2p/p2p-core/src/client/handshaker/builder/dummy.rs b/p2p/p2p-core/src/client/handshaker/builder/dummy.rs index 2036833..6550e15 100644 --- a/p2p/p2p-core/src/client/handshaker/builder/dummy.rs +++ b/p2p/p2p-core/src/client/handshaker/builder/dummy.rs @@ -102,9 +102,8 @@ impl Service> for DummyAddressBook { | AddressBookRequest::TakeRandomWhitePeer { .. } => { return ready(Err("dummy address book does not hold peers".into())); } - AddressBookRequest::NewConnection { .. } | AddressBookRequest::IncomingPeerList(_) => { - AddressBookResponse::Ok - } + AddressBookRequest::NewConnection { .. } + | AddressBookRequest::IncomingPeerList(_, _) => AddressBookResponse::Ok, AddressBookRequest::GetBan(_) => AddressBookResponse::GetBan { unban_instant: None, }, diff --git a/p2p/p2p-core/src/client/timeout_monitor.rs b/p2p/p2p-core/src/client/timeout_monitor.rs index 79b95a3..8c101c1 100644 --- a/p2p/p2p-core/src/client/timeout_monitor.rs +++ b/p2p/p2p-core/src/client/timeout_monitor.rs @@ -119,6 +119,7 @@ where .ready() .await? .call(AddressBookRequest::IncomingPeerList( + peer_information.id, timed_sync .local_peerlist_new .into_iter() diff --git a/p2p/p2p-core/src/services.rs b/p2p/p2p-core/src/services.rs index 75f86df..295c35a 100644 --- a/p2p/p2p-core/src/services.rs +++ b/p2p/p2p-core/src/services.rs @@ -92,7 +92,10 @@ pub enum AddressBookRequest { }, /// Tells the address book about a peer list received from a peer. - IncomingPeerList(Vec>), + IncomingPeerList( + InternalPeerID, + Vec>, + ), /// Takes a random white peer from the peer list. If height is specified /// then the peer list should retrieve a peer that should have a full diff --git a/p2p/p2p/src/block_downloader/tests.rs b/p2p/p2p/src/block_downloader/tests.rs index 3d5808e..ee8be5b 100644 --- a/p2p/p2p/src/block_downloader/tests.rs +++ b/p2p/p2p/src/block_downloader/tests.rs @@ -26,8 +26,9 @@ use cuprate_p2p_core::{ use cuprate_pruning::PruningSeed; use cuprate_types::{BlockCompleteEntry, TransactionBlobs}; use cuprate_wire::{ + common::PeerSupportFlags, protocol::{ChainResponse, GetObjectsResponse}, - CoreSyncData, + BasicNodeData, CoreSyncData, }; use crate::{ @@ -250,6 +251,14 @@ fn mock_block_downloader_client(blockchain: Arc) -> Client Date: Tue, 12 Aug 2025 22:43:33 +0100 Subject: [PATCH 13/16] p2p: fix d++ state log (#523) fix d++ state log --- p2p/dandelion-tower/src/router.rs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/p2p/dandelion-tower/src/router.rs b/p2p/dandelion-tower/src/router.rs index 8344f55..5c30fc0 100644 --- a/p2p/dandelion-tower/src/router.rs +++ b/p2p/dandelion-tower/src/router.rs @@ -299,8 +299,7 @@ where State::Stem }; - self.span - .record("state", format!("{:?}", self.current_state)); + self.span = tracing::debug_span!("dandelion_router", state = ?self.current_state); tracing::debug!(parent: &self.span, "Starting new d++ epoch",); self.epoch_start = Instant::now(); @@ -354,13 +353,13 @@ where self.fluff_tx(req.tx) } State::Stem => { - tracing::trace!(parent: &self.span, "Steming transaction"); + tracing::trace!(parent: &self.span, "Stemming transaction"); self.stem_tx(req.tx, &from) } }, TxState::Local => { - tracing::debug!(parent: &self.span, "Steming local tx."); + tracing::debug!(parent: &self.span, "Stemming local tx."); self.stem_local_tx(req.tx) } From 27a97ab106b61225b678cf7376dee35a0c74653f Mon Sep 17 00:00:00 2001 From: Boog900 Date: Tue, 12 Aug 2025 22:43:47 +0100 Subject: [PATCH 14/16] Update deps (#527) * fix clippy * update deps * fix ci --- Cargo.lock | 1325 +++++++++++++------------ Cargo.toml | 35 +- binaries/cuprated/Cargo.toml | 5 +- binaries/cuprated/src/config.rs | 2 + p2p/p2p-core/src/client/handshaker.rs | 1 - p2p/p2p-core/src/lib.rs | 14 +- p2p/p2p-transport/Cargo.toml | 2 - p2p/p2p-transport/src/arti.rs | 6 - rpc/interface/Cargo.toml | 2 +- 9 files changed, 714 insertions(+), 678 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 932829f..4fb2ed2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -13,9 +13,9 @@ dependencies = [ [[package]] name = "adler2" -version = "2.0.0" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" +checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" [[package]] name = "aes" @@ -119,9 +119,9 @@ checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299" [[package]] name = "anstyle" -version = "1.0.10" +version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9" +checksum = "862ed96ca487e809f1c8e5a8447f6ee2cf102f846893800b20cebdf541fc6bbd" [[package]] name = "anyhow" @@ -158,13 +158,13 @@ checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" [[package]] name = "arti-client" -version = "0.32.0" +version = "0.33.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "855132b9da6a7c968736dda4c1824689073e4e2009817d06f52a70153271235c" +checksum = "375c3b0681ca73c8678dc2e879f01964121955dc8e45f3b334ed0f7e7cefec48" dependencies = [ "async-trait", "cfg-if", - "derive-deftly 1.1.0", + "derive-deftly 1.2.0", "derive_builder_fork_arti", "derive_more", "educe", @@ -176,7 +176,7 @@ dependencies = [ "libc", "once_cell", "postage", - "rand 0.9.1", + "rand 0.9.2", "safelog", "serde", "thiserror 2.0.12", @@ -236,7 +236,7 @@ checksum = "3109e49b1e4909e9db6515a30c633684d68cdeaa252f215214cb4fa1a5bfee2c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.104", "synstructure", ] @@ -248,7 +248,7 @@ checksum = "7b18050c2cd6fe86c3a76584ef5e0baf286d038cda203eb6223df2cc413565f7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.104", ] [[package]] @@ -269,9 +269,9 @@ checksum = "9b34d609dfbaf33d6889b2b7106d3ca345eacad44200913df5ba02bfd31d2ba9" [[package]] name = "async-compression" -version = "0.4.25" +version = "0.4.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40f6024f3f856663b45fd0c9b6f2024034a702f453549449e0d84a305900dad4" +checksum = "ddb939d66e4ae03cee6091612804ba446b12878410cfa17f785f4dd67d4014e8" dependencies = [ "flate2", "futures-core", @@ -311,7 +311,7 @@ checksum = "c7c24de15d275a1ecfd47a380fb4d5ec9bfe0933f309ed5e705b775596a3574d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.104", ] [[package]] @@ -322,7 +322,7 @@ checksum = "e539d3fca749fcee5236ab05e93a52867dd549cc157c8cb7f99595f3cedffdb5" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.104", ] [[package]] @@ -376,17 +376,16 @@ checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" [[package]] name = "autocfg" -version = "1.4.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" +checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" [[package]] name = "axum" -version = "0.7.9" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edca88bc138befd0323b20752846e6587272d3b03b0343c8ea28a6f819e6e71f" +checksum = "021e862c184ae977658b36c4500f7feac3221ca5da43e3f25bd04ab6c79a29b5" dependencies = [ - "async-trait", "axum-core", "bytes", "futures-util", @@ -405,34 +404,30 @@ dependencies = [ "serde", "serde_json", "serde_path_to_error", - "serde_urlencoded", - "sync_wrapper 1.0.2", + "sync_wrapper", "tokio", - "tower 0.5.2", - "tower-layer 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", - "tower-service 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", - "tracing", + "tower", + "tower-layer", + "tower-service", ] [[package]] name = "axum-core" -version = "0.4.5" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09f2bd6146b97ae3359fa0cc6d6b376d9539582c7b4220f041a33ec24c226199" +checksum = "68464cd0412f486726fb3373129ef5d2993f90c34bc2bc1c1e9943b2f4fc7ca6" dependencies = [ - "async-trait", "bytes", - "futures-util", + "futures-core", "http", "http-body", "http-body-util", "mime", "pin-project-lite", "rustversion", - "sync_wrapper 1.0.2", - "tower-layer 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", - "tower-service 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", - "tracing", + "sync_wrapper", + "tower-layer", + "tower-service", ] [[package]] @@ -510,9 +505,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.9.0" +version = "2.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c8214115b7bf84099f1309324e63141d4c5d7cc26862f97a0a857dbefe165bd" +checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967" dependencies = [ "bytemuck", "serde", @@ -560,7 +555,7 @@ checksum = "e0b121a9fe0df916e362fb3271088d071159cdf11db0e4182d02152850756eff" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.104", ] [[package]] @@ -592,7 +587,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.104", ] [[package]] @@ -614,9 +609,9 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.17.0" +version = "3.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1628fb46dfa0b37568d12e5edd512553eccf6a22a78e8bde00bb4aed84d5bdbf" +checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43" [[package]] name = "by_address" @@ -626,22 +621,22 @@ checksum = "64fa3c856b712db6612c019f14756e64e4bcea13337a6b33b696333a9eaa2d06" [[package]] name = "bytemuck" -version = "1.23.0" +version = "1.23.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9134a6ef01ce4b366b50689c94f82c14bc72bc5d0386829828a2e2752ef7958c" +checksum = "3995eaeebcdf32f91f980d360f78732ddc061097ab4e39991ae7a6ace9194677" dependencies = [ "bytemuck_derive", ] [[package]] name = "bytemuck_derive" -version = "1.9.3" +version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ecc273b49b3205b83d648f0690daa588925572cc5063745bfe547fe7ec8e1a1" +checksum = "4f154e572231cb6ba2bd1176980827e3d5dc04cc183a75dea38109fbdd672d29" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.104", ] [[package]] @@ -673,9 +668,9 @@ checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" [[package]] name = "cc" -version = "1.2.22" +version = "1.2.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32db95edf998450acc7881c932f94cd9b05c87b4b2599e8bab064753da4acfd1" +checksum = "2352e5597e9c544d5e6d9c95190d5d27738ade584fa8db0a16e130e5c2b5296e" dependencies = [ "jobserver", "libc", @@ -684,9 +679,9 @@ dependencies = [ [[package]] name = "cfg-if" -version = "1.0.0" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +checksum = "9555578bc9e57714c812a1f84e4fc5b4d21fcb063490c624de019f7464c91268" [[package]] name = "cfg_aliases" @@ -747,9 +742,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.37" +version = "4.5.43" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eccb054f56cbd38340b380d4a8e69ef1f02f1af43db2f0cc817a4774d80ae071" +checksum = "50fd97c9dc2399518aa331917ac6f274280ec5eb34e555dd291899745c48ec6f" dependencies = [ "clap_builder", "clap_derive", @@ -757,9 +752,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.37" +version = "4.5.43" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "efd9466fac8543255d3b1fcad4762c5e116ffe808c8a3043d4263cd4fd4862a2" +checksum = "c35b5830294e1fa0462034af85cc95225a4cb07092c088c55bda3147cfcd8f65" dependencies = [ "anstyle", "clap_lex", @@ -769,21 +764,21 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.5.32" +version = "4.5.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09176aae279615badda0765c0c0b3f6ed53f4709118af73cf4655d85d1530cd7" +checksum = "ef4f52386a59ca4c860f7393bcf8abd8dfd91ecccc0f774635ff68e92eeef491" dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.104", ] [[package]] name = "clap_lex" -version = "0.7.4" +version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6" +checksum = "b94f61472cee1439c0b966b47e3aca9ae07e45d070759512cd390ea2bebc6675" [[package]] name = "cmake" @@ -878,9 +873,9 @@ dependencies = [ [[package]] name = "core-foundation" -version = "0.10.0" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b55271e5c8c478ad3f38ad24ef34923091e0548492a266d19b3c0b4d82574c63" +checksum = "b2a6cd9ae233e7f62ba4e9353e81a88df7fc8a5987b8d445b4d90c879bd156f6" dependencies = [ "core-foundation-sys", "libc", @@ -903,9 +898,9 @@ dependencies = [ [[package]] name = "crc32fast" -version = "1.4.2" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3" +checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511" dependencies = [ "cfg-if", ] @@ -1030,6 +1025,16 @@ dependencies = [ "zeroize", ] +[[package]] +name = "crypto-bigint" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96272c2ff28b807e09250b180ad1fb7889a3258f7455759b5c3c58b719467130" +dependencies = [ + "num-traits", + "subtle", +] + [[package]] name = "crypto-common" version = "0.1.6" @@ -1059,12 +1064,12 @@ dependencies = [ "cuprate-pruning", "cuprate-test-utils", "futures", - "indexmap 2.9.0", + "indexmap 2.10.0", "rand 0.8.5", - "thiserror 1.0.69", + "thiserror 2.0.12", "tokio", "tokio-util", - "tower 0.5.1", + "tower", "tracing", ] @@ -1074,7 +1079,7 @@ version = "0.1.0" dependencies = [ "futures", "pin-project", - "thiserror 1.0.69", + "thiserror 2.0.12", "tokio", ] @@ -1082,7 +1087,7 @@ dependencies = [ name = "cuprate-blockchain" version = "0.0.0" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.9.1", "bytemuck", "bytes", "cuprate-constants", @@ -1094,8 +1099,8 @@ dependencies = [ "cuprate-types", "curve25519-dalek", "hex", - "hex-literal", - "indexmap 2.9.0", + "hex-literal 1.0.0", + "indexmap 2.10.0", "monero-serai", "pretty_assertions", "proptest", @@ -1105,7 +1110,7 @@ dependencies = [ "tempfile", "thread_local", "tokio", - "tower 0.5.1", + "tower", ] [[package]] @@ -1121,18 +1126,18 @@ dependencies = [ "curve25519-dalek", "futures", "hex", - "hex-literal", - "indexmap 2.9.0", + "hex-literal 1.0.0", + "indexmap 2.10.0", "monero-serai", "proptest", "proptest-derive", "rand 0.8.5", "rayon", - "thiserror 1.0.69", + "thiserror 2.0.12", "thread_local", "tokio", "tokio-test", - "tower 0.5.1", + "tower", "tracing", ] @@ -1149,11 +1154,11 @@ dependencies = [ "monero-serai", "randomx-rs", "rayon", - "thiserror 1.0.69", + "thiserror 2.0.12", "thread_local", "tokio", "tokio-util", - "tower 0.5.1", + "tower", "tracing", ] @@ -1162,21 +1167,21 @@ name = "cuprate-consensus-rules" version = "0.1.0" dependencies = [ "cfg-if", - "crypto-bigint", + "crypto-bigint 0.6.1", "cuprate-constants", "cuprate-cryptonight", "cuprate-helper", "cuprate-types", "curve25519-dalek", "hex", - "hex-literal", - "indexmap 2.9.0", + "hex-literal 1.0.0", + "indexmap 2.10.0", "monero-serai", "proptest", "proptest-derive", "rand 0.8.5", "rayon", - "thiserror 1.0.69", + "thiserror 2.0.12", "tokio", "tracing", ] @@ -1197,7 +1202,7 @@ dependencies = [ "seq-macro", "sha3", "skein", - "thiserror 1.0.69", + "thiserror 2.0.12", ] [[package]] @@ -1208,10 +1213,10 @@ dependencies = [ "proptest", "rand 0.8.5", "rand_distr", - "thiserror 1.0.69", + "thiserror 2.0.12", "tokio", "tokio-util", - "tower 0.5.1", + "tower", "tracing", ] @@ -1228,7 +1233,7 @@ dependencies = [ "redb", "serde", "tempfile", - "thiserror 1.0.69", + "thiserror 2.0.12", "tracing", ] @@ -1242,7 +1247,7 @@ dependencies = [ "futures", "rayon", "serde", - "tower 0.5.1", + "tower", "tracing", ] @@ -1256,7 +1261,7 @@ dependencies = [ "hex", "paste", "ref-cast", - "thiserror 1.0.69", + "thiserror 2.0.12", ] [[package]] @@ -1279,7 +1284,7 @@ dependencies = [ "tempfile", "tokio", "tokio-test", - "tower 0.5.1", + "tower", ] [[package]] @@ -1289,7 +1294,7 @@ dependencies = [ "bytes", "serde", "serde_json", - "thiserror 1.0.69", + "thiserror 2.0.12", ] [[package]] @@ -1314,7 +1319,7 @@ dependencies = [ "crossbeam", "cuprate-constants", "curve25519-dalek", - "dirs 5.0.1", + "dirs", "futures", "libc", "monero-serai", @@ -1340,7 +1345,7 @@ dependencies = [ "pretty_assertions", "serde", "serde_json", - "thiserror 1.0.69", + "thiserror 2.0.12", ] [[package]] @@ -1348,14 +1353,14 @@ name = "cuprate-levin" version = "0.1.0" dependencies = [ "arbitrary", - "bitflags 2.9.0", + "bitflags 2.9.1", "bytes", "cfg-if", "cuprate-helper", "futures", "proptest", "rand 0.8.5", - "thiserror 1.0.69", + "thiserror 2.0.12", "tokio", "tokio-util", "tracing", @@ -1378,19 +1383,19 @@ dependencies = [ "cuprate-types", "cuprate-wire", "futures", - "indexmap 2.9.0", + "indexmap 2.10.0", "monero-serai", "pin-project", "proptest", "rand 0.8.5", "rand_distr", "rayon", - "thiserror 1.0.69", + "thiserror 2.0.12", "tokio", "tokio-stream", "tokio-test", "tokio-util", - "tower 0.5.1", + "tower", "tracing", ] @@ -1416,14 +1421,14 @@ dependencies = [ "cuprate-wire", "futures", "hex", - "hex-literal", + "hex-literal 1.0.0", "rand 0.8.5", - "thiserror 1.0.69", + "thiserror 2.0.12", "tokio", "tokio-stream", "tokio-test", "tokio-util", - "tower 0.5.1", + "tower", "tracing", ] @@ -1444,7 +1449,6 @@ dependencies = [ "tor-hsservice", "tor-proto", "tor-rtcompat", - "tracing", ] [[package]] @@ -1453,7 +1457,7 @@ version = "0.1.0" dependencies = [ "borsh", "cuprate-constants", - "thiserror 1.0.69", + "thiserror 2.0.12", ] [[package]] @@ -1472,7 +1476,7 @@ dependencies = [ "serde", "serde_json", "tokio", - "tower 0.5.1", + "tower", "ureq", ] @@ -1488,7 +1492,7 @@ dependencies = [ "cuprate-test-utils", "cuprate-types", "hex", - "hex-literal", + "hex-literal 1.0.0", "paste", "pretty_assertions", "serde", @@ -1506,7 +1510,7 @@ dependencies = [ "cuprate-types", "cuprate-wire", "hex", - "hex-literal", + "hex-literal 1.0.0", "monero-rpc", "monero-serai", "monero-simple-request-rpc", @@ -1522,7 +1526,7 @@ dependencies = [ name = "cuprate-txpool" version = "0.0.0" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.9.1", "blake3", "bytemuck", "cuprate-database", @@ -1531,21 +1535,21 @@ dependencies = [ "cuprate-test-utils", "cuprate-types", "hex", - "hex-literal", + "hex-literal 1.0.0", "monero-serai", "rayon", "serde", "tempfile", - "thiserror 1.0.69", + "thiserror 2.0.12", "tokio", - "tower 0.5.1", + "tower", ] [[package]] name = "cuprate-types" version = "0.0.0" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.9.1", "bytes", "cfg-if", "cuprate-epee-encoding", @@ -1553,16 +1557,16 @@ dependencies = [ "cuprate-helper", "cuprate-hex", "curve25519-dalek", - "hex-literal", - "indexmap 2.9.0", + "hex-literal 1.0.0", + "indexmap 2.10.0", "monero-serai", "pretty_assertions", "proptest", "proptest-derive", "serde", "serde_json", - "strum 0.26.3", - "thiserror 1.0.69", + "strum", + "thiserror 2.0.12", ] [[package]] @@ -1570,7 +1574,7 @@ name = "cuprate-wire" version = "0.1.0" dependencies = [ "arbitrary", - "bitflags 2.9.0", + "bitflags 2.9.1", "borsh", "bytes", "cuprate-epee-encoding", @@ -1580,7 +1584,7 @@ dependencies = [ "cuprate-types", "hex", "proptest", - "thiserror 1.0.69", + "thiserror 2.0.12", ] [[package]] @@ -1602,7 +1606,7 @@ dependencies = [ "arti-client", "async-trait", "axum", - "bitflags 2.9.0", + "bitflags 2.9.1", "borsh", "bytemuck", "bytes", @@ -1611,7 +1615,7 @@ dependencies = [ "clap", "const_format", "crossbeam", - "crypto-bigint", + "crypto-bigint 0.6.1", "cuprate-address-book", "cuprate-async-buffer", "cuprate-blockchain", @@ -1642,11 +1646,11 @@ dependencies = [ "cuprate-wire", "curve25519-dalek", "dashmap", - "dirs 5.0.1", + "dirs", "futures", "hex", - "hex-literal", - "indexmap 2.9.0", + "hex-literal 1.0.0", + "indexmap 2.10.0", "monero-address", "monero-serai", "nu-ansi-term", @@ -1657,22 +1661,23 @@ dependencies = [ "rand_distr", "randomx-rs", "rayon", + "safelog", "serde", "serde_bytes", "serde_json", - "strum 0.26.3", + "strum", "tempfile", - "thiserror 1.0.69", + "thiserror 2.0.12", "thread_local", "tokio", "tokio-stream", "tokio-util", - "toml", - "toml_edit", + "toml 0.9.5", + "toml_edit 0.23.3", "tor-hsservice", "tor-persist", "tor-rtcompat", - "tower 0.5.1", + "tower", "tower-http", "tracing", "tracing-appender", @@ -1705,7 +1710,7 @@ checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.104", ] [[package]] @@ -1713,7 +1718,7 @@ name = "dalek-ff-group" version = "0.4.1" source = "git+https://github.com/Cuprate/serai.git?rev=e6ae8c2#e6ae8c2b1f9d791f35ea225032cc0a3f79dec99d" dependencies = [ - "crypto-bigint", + "crypto-bigint 0.5.5", "curve25519-dalek", "digest", "ff", @@ -1769,7 +1774,7 @@ dependencies = [ "proc-macro2", "quote", "strsim 0.11.1", - "syn 2.0.101", + "syn 2.0.104", ] [[package]] @@ -1791,7 +1796,7 @@ checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead" dependencies = [ "darling_core 0.20.11", "quote", - "syn 2.0.101", + "syn 2.0.104", ] [[package]] @@ -1861,11 +1866,11 @@ dependencies = [ [[package]] name = "derive-deftly" -version = "1.1.0" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a55a256deae70e0772adfd583c57c1403c6ddbd1d1f1f84f64e94acaecc25eeb" +checksum = "957bb73a3a9c0bbcac67e129b81954661b3cfcb9e28873d8441f91b54852e77a" dependencies = [ - "derive-deftly-macros 1.1.0", + "derive-deftly-macros 1.2.0", "heck", ] @@ -1876,32 +1881,32 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "357422a457ccb850dc8f1c1680e0670079560feaad6c2e247e3f345c4fab8a3f" dependencies = [ "heck", - "indexmap 2.9.0", + "indexmap 2.10.0", "itertools 0.14.0", "proc-macro-crate", "proc-macro2", "quote", "sha3", - "strum 0.27.1", - "syn 2.0.101", + "strum", + "syn 2.0.104", "void", ] [[package]] name = "derive-deftly-macros" -version = "1.1.0" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "47cf90c375e516cf601a57727744bdf7a547680a470a2e8a6580a12288cf0630" +checksum = "9ea41269bd490d251b9eca50ccb43117e641cc68b129849757c15ece88fe0574" dependencies = [ "heck", - "indexmap 2.9.0", + "indexmap 2.10.0", "itertools 0.14.0", "proc-macro-crate", "proc-macro2", "quote", "sha3", - "strum 0.27.1", - "syn 2.0.101", + "strum", + "syn 2.0.104", "void", ] @@ -1913,7 +1918,7 @@ checksum = "30542c1ad912e0e3d22a1935c290e12e8a29d704a420177a31faad4a601a0800" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.104", ] [[package]] @@ -1965,7 +1970,7 @@ dependencies = [ "convert_case", "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.104", "unicode-xid", ] @@ -2006,16 +2011,7 @@ version = "6.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "16f5094c54661b38d03bd7e50df373292118db60b585c08a411c6d840017fe7d" dependencies = [ - "dirs-sys 0.5.0", -] - -[[package]] -name = "dirs" -version = "5.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44c45a9d03d6676652bcb5e724c7e988de1acad23a711b5217ab9cbecbec2225" -dependencies = [ - "dirs-sys 0.4.1", + "dirs-sys", ] [[package]] @@ -2024,19 +2020,7 @@ version = "6.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c3e8aa94d75141228480295a7d0e7feb620b1a5ad9f12bc40be62411e38cce4e" dependencies = [ - "dirs-sys 0.5.0", -] - -[[package]] -name = "dirs-sys" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "520f05a5cbd335fae5a99ff7a6ab8627577660ee5cfd6a94a6a929b52ff0321c" -dependencies = [ - "libc", - "option-ext", - "redox_users 0.4.6", - "windows-sys 0.48.0", + "dirs-sys", ] [[package]] @@ -2047,8 +2031,8 @@ checksum = "e01a3366d27ee9890022452ee61b2b63a67e6f13f58900b651ff5665f0bb1fab" dependencies = [ "libc", "option-ext", - "redox_users 0.5.0", - "windows-sys 0.59.0", + "redox_users", + "windows-sys 0.60.2", ] [[package]] @@ -2059,7 +2043,7 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.104", ] [[package]] @@ -2079,9 +2063,9 @@ dependencies = [ [[package]] name = "dyn-clone" -version = "1.0.19" +version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c7a8fb8a9fbf66c1f703fe16184d10ca0ee9d23be5b4436400408ba54a95005" +checksum = "d0881ea181b1df73ff77ffaaf9c7544ecc11e82fba9b5f27b262a3c73a332555" [[package]] name = "ecdsa" @@ -2109,9 +2093,9 @@ dependencies = [ [[package]] name = "ed25519-dalek" -version = "2.1.1" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a3daa8e81a3963a60642bcc1f90a670680bd4a77535faa384e9d1c79d620871" +checksum = "70e796c081cee67dc755e1a36a0a172b897fab85fc3f6bc48307991f64e4eca9" dependencies = [ "curve25519-dalek", "ed25519", @@ -2148,7 +2132,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b5e6043086bf7973472e0c7dff2142ea0b680d30e18d9cc40f267efbf222bd47" dependencies = [ "base16ct", - "crypto-bigint", + "crypto-bigint 0.5.5", "digest", "ff", "generic-array", @@ -2170,7 +2154,7 @@ dependencies = [ "num-traits", "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.104", ] [[package]] @@ -2181,19 +2165,19 @@ checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" [[package]] name = "errno" -version = "0.3.11" +version = "0.3.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "976dd42dc7e85965fe702eb8164f21f450704bdde31faefd6471dba214cb594e" +checksum = "778e2ac28f6c47af28e4907f13ffd1e1ddbd400980a9abd7c8df189bf578a5ad" dependencies = [ "libc", - "windows-sys 0.59.0", + "windows-sys 0.60.2", ] [[package]] name = "event-listener" -version = "5.4.0" +version = "5.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3492acde4c3fc54c845eaab3eed8bd00c7a7d881f78bfc801e43a93dec1331ae" +checksum = "e13b66accf52311f30a0db42147dadea9850cb48cd070028831ae5f5d4b856ab" dependencies = [ "concurrent-queue", "parking", @@ -2243,7 +2227,7 @@ checksum = "8cb01cd46b0cf372153850f4c6c272d9cbea2da513e07538405148f95bd789f3" dependencies = [ "atomic 0.6.1", "serde", - "toml", + "toml 0.8.23", "uncased", "version_check", ] @@ -2262,9 +2246,9 @@ dependencies = [ [[package]] name = "flate2" -version = "1.1.1" +version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ced92e76e966ca2fd84c8f7aa01a4aea65b0eb6648d72f7c8f3e2764a67fece" +checksum = "4a3d7db9596fecd151c5f638c0ee5d5bd487b6e0ea232e5dc96d5250f6f94b1d" dependencies = [ "crc32fast", "miniz_oxide", @@ -2332,7 +2316,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "198b8f9ab4cff63b5c91e9e64edd4e6b43cd7fe7a52519a03c6c32ea0acfa557" dependencies = [ "derive_builder_fork_arti", - "dirs 6.0.0", + "dirs", "libc", "pwd-grp", "serde", @@ -2433,7 +2417,7 @@ checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.104", ] [[package]] @@ -2486,7 +2470,7 @@ dependencies = [ "cfg-if", "js-sys", "libc", - "wasi 0.11.0+wasi-snapshot-preview1", + "wasi 0.11.1+wasi-snapshot-preview1", "wasm-bindgen", ] @@ -2550,9 +2534,9 @@ dependencies = [ [[package]] name = "h2" -version = "0.4.10" +version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9421a676d1b147b16b82c9225157dc629087ef8ec4d5e2960f9437a90dac0a5" +checksum = "f3c0b69cfcb4e1b9f1bf2f53f95f766e4661169728ec61cd3fe5a0166f2d1386" dependencies = [ "atomic-waker", "bytes", @@ -2560,7 +2544,7 @@ dependencies = [ "futures-core", "futures-sink", "http", - "indexmap 2.9.0", + "indexmap 2.10.0", "slab", "tokio", "tokio-util", @@ -2594,9 +2578,9 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.15.3" +version = "0.15.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84b26c544d002229e640969970a2e74021aadf6e2f96372b9c58eff97de08eb3" +checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" dependencies = [ "foldhash", ] @@ -2607,7 +2591,7 @@ version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7382cf6263419f2d8df38c55d7da83da5c18aef87fc7a7fc1fb1e344edfe14c1" dependencies = [ - "hashbrown 0.15.3", + "hashbrown 0.15.5", ] [[package]] @@ -2622,7 +2606,7 @@ version = "0.20.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7d4f449bab7320c56003d37732a917e18798e2f1709d80263face2b4f9436ddb" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.9.1", "byteorder", "heed-traits", "heed-types", @@ -2675,6 +2659,12 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6fe2267d4ed49bc07b63801559be28c718ea06c4738b7a03c94df7386d2cde46" +[[package]] +name = "hex-literal" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bcaaec4551594c969335c98c903c1397853d4198408ea609190f420500f6be71" + [[package]] name = "hkdf" version = "0.12.4" @@ -2784,11 +2774,10 @@ dependencies = [ [[package]] name = "hyper-rustls" -version = "0.27.5" +version = "0.27.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d191583f3da1305256f22463b9bb0471acad48a4e534a5218b9963e9c1f59b2" +checksum = "e3c93eb611681b207e1fe55d5a71ecf91572ec8a6705cdb6857f7d8d5242cf58" dependencies = [ - "futures-util", "http", "hyper", "hyper-util", @@ -2797,17 +2786,18 @@ dependencies = [ "rustls-pki-types", "tokio", "tokio-rustls", - "tower-service 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", + "tower-service", ] [[package]] name = "hyper-util" -version = "0.1.11" +version = "0.1.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "497bbc33a26fdd4af9ed9c70d63f61cf56a938375fbb32df34db9b1cd6d643f2" +checksum = "8d9b05277c7e8da2c93a568989bb6207bef0112e8d17df7a6eda4a3cf143bc5e" dependencies = [ "bytes", "futures-channel", + "futures-core", "futures-util", "http", "http-body", @@ -2816,7 +2806,7 @@ dependencies = [ "pin-project-lite", "socket2", "tokio", - "tower-service 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", + "tower-service", "tracing", ] @@ -2893,9 +2883,9 @@ checksum = "00210d6893afc98edb752b664b8890f0ef174c8adbb8d0be9710fa66fbbf72d3" [[package]] name = "icu_properties" -version = "2.0.0" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2549ca8c7241c82f59c80ba2a6f415d931c5b58d24fb8412caa1a1f02c49139a" +checksum = "016c619c1eeb94efb86809b015c58f479963de65bdb6253345c1a1276f22e32b" dependencies = [ "displaydoc", "icu_collections", @@ -2909,9 +2899,9 @@ dependencies = [ [[package]] name = "icu_properties_data" -version = "2.0.0" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8197e866e47b68f8f7d95249e172903bec06004b18b2937f1095d40a0c57de04" +checksum = "298459143998310acd25ffe6810ed544932242d3f07083eee1084d83a71bd632" [[package]] name = "icu_provider" @@ -2970,12 +2960,12 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.9.0" +version = "2.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cea70ddb795996207ad57735b50c5982d8844f38ba9ee5f1aedcfb708a2aa11e" +checksum = "fe4cd85333e22411419a0bcae1297d25e58c9443848b11dc6a86fefe8c78a661" dependencies = [ "equivalent", - "hashbrown 0.15.3", + "hashbrown 0.15.5", "rayon", "serde", ] @@ -2986,7 +2976,7 @@ version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f37dccff2791ab604f9babef0ba14fbe0be30bd368dc541e2b08d07c8aa908f3" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.9.1", "inotify-sys", "libc", ] @@ -3018,6 +3008,17 @@ dependencies = [ "rustversion", ] +[[package]] +name = "io-uring" +version = "0.7.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d93587f37623a1a17d94ef2bc9ada592f5465fe7732084ab7beefabe5c77c0c4" +dependencies = [ + "bitflags 2.9.1", + "cfg-if", + "libc", +] + [[package]] name = "is-terminal" version = "0.4.16" @@ -3060,7 +3061,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f65735f9e73adc203417d2e05352aef71d7e832ec090f65de26c96c9ec563aa5" dependencies = [ "digest", - "hex-literal", + "hex-literal 0.4.1", "ppv-lite86", ] @@ -3149,15 +3150,15 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.172" +version = "0.2.174" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa" +checksum = "1171693293099992e19cddea4e8b849964e9846f4acee11b3948bcc337be8776" [[package]] name = "libfuzzer-sys" -version = "0.4.9" +version = "0.4.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf78f52d400cf2d84a3a973a78a592b4adc535739e0a5597a0da6f0c357adc75" +checksum = "5037190e1f70cbeef565bd267599242926f724d3b8a9f510fd7e0b540cfa4404" dependencies = [ "arbitrary", "cc", @@ -3171,20 +3172,20 @@ checksum = "f9fbbcab51052fe104eb5e5d351cf728d30a5be1fe14d9be8a3b097481fb97de" [[package]] name = "libredox" -version = "0.1.3" +version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" +checksum = "391290121bad3d37fbddad76d8f5d1c1c314cfc646d143d7e07a3086ddff0ce3" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.9.1", "libc", "redox_syscall", ] [[package]] name = "libsqlite3-sys" -version = "0.34.0" +version = "0.35.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91632f3b4fb6bd1d72aa3d78f41ffecfcf2b1a6648d8c241dbe7dbfaf4875e15" +checksum = "133c182a6a2c87864fe97778797e46c7e999672690dc9fa3ee8e241aa4a9c13f" dependencies = [ "cc", "pkg-config", @@ -3216,9 +3217,9 @@ dependencies = [ [[package]] name = "lock_api" -version = "0.4.12" +version = "0.4.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" +checksum = "96936507f153605bddfcda068dd804796c84324ed2510809e5b2a624c81da765" dependencies = [ "autocfg", "scopeguard", @@ -3241,9 +3242,9 @@ dependencies = [ [[package]] name = "matchit" -version = "0.7.3" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e7465ac9959cc2b1404e8e2367b43684a6d13790fe23056cc8c6c5a6b7bcb94" +checksum = "47e1ffaa40ddd1f3ed91f717a33c8c0ee23fff369e3aa8772b9605cc1d22f4c3" [[package]] name = "md-5" @@ -3257,15 +3258,15 @@ dependencies = [ [[package]] name = "memchr" -version = "2.7.4" +version = "2.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" +checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0" [[package]] name = "memmap2" -version = "0.9.5" +version = "0.9.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd3f7eed9d3848f8b98834af67102b720745c4ec028fcd0aa0239277e7de374f" +checksum = "483758ad303d734cec05e5c12b41d7e93e6a6390c5e9dae6bdeb7c1259012d28" dependencies = [ "libc", ] @@ -3296,23 +3297,23 @@ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" [[package]] name = "miniz_oxide" -version = "0.8.8" +version = "0.8.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3be647b768db090acb35d5ec5db2b0e1f1de11133ca123b9eacf5137868f892a" +checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" dependencies = [ "adler2", ] [[package]] name = "mio" -version = "1.0.3" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2886843bf800fba2e3377cff24abf6379b4c4d5c6681eaf9ea5b0d15090450bd" +checksum = "78bed444cc8a2160f01cbcf811ef18cac863ad68ae8ca62092e8db51d51c761c" dependencies = [ "libc", "log", - "wasi 0.11.0+wasi-snapshot-preview1", - "windows-sys 0.52.0", + "wasi 0.11.1+wasi-snapshot-preview1", + "windows-sys 0.59.0", ] [[package]] @@ -3448,7 +3449,7 @@ version = "0.1.4-alpha" source = "git+https://github.com/Cuprate/serai.git?rev=e6ae8c2#e6ae8c2b1f9d791f35ea225032cc0a3f79dec99d" dependencies = [ "curve25519-dalek", - "hex-literal", + "hex-literal 0.4.1", "monero-borromean", "monero-bulletproofs", "monero-clsag", @@ -3501,12 +3502,11 @@ dependencies = [ [[package]] name = "notify" -version = "8.0.0" +version = "8.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2fee8403b3d66ac7b26aee6e40a897d85dc5ce26f44da36b8b73e987cc52e943" +checksum = "4d3d07927151ff8575b7087f245456e549fea62edf0ec4e565a5ee50c8402bc3" dependencies = [ - "bitflags 2.9.0", - "filetime", + "bitflags 2.9.1", "inotify", "kqueue", "libc", @@ -3514,7 +3514,7 @@ dependencies = [ "mio", "notify-types", "walkdir", - "windows-sys 0.59.0", + "windows-sys 0.60.2", ] [[package]] @@ -3523,6 +3523,15 @@ version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5e0826a989adedc2a244799e823aece04662b66609d96af8dff7ac6df9a8925d" +[[package]] +name = "ntapi" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8a3895c6391c39d7fe7ebc444a87eb2991b2a0bc718fdabd071eec617fc68e4" +dependencies = [ + "winapi", +] + [[package]] name = "nu-ansi-term" version = "0.46.0" @@ -3615,7 +3624,26 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.104", +] + +[[package]] +name = "objc2-core-foundation" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c10c2894a6fed806ade6027bcd50662746363a9589d3ec9d9bef30a4e4bc166" +dependencies = [ + "bitflags 2.9.1", +] + +[[package]] +name = "objc2-io-kit" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71c1c64d6120e51cd86033f67176b1cb66780c2efe34dec55176f77befd93c0a" +dependencies = [ + "libc", + "objc2-core-foundation", ] [[package]] @@ -3654,7 +3682,7 @@ version = "0.10.73" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8505734d46c8ab1e19a1dce3aef597ad87dcb4c37e7188231769bd6bd51cebf8" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.9.1", "cfg-if", "foreign-types", "libc", @@ -3671,7 +3699,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.104", ] [[package]] @@ -3682,9 +3710,9 @@ checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e" [[package]] name = "openssl-src" -version = "300.5.1+3.5.1" +version = "300.5.2+3.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "735230c832b28c000e3bc117119e6466a663ec73506bc0a9907ea4187508e42a" +checksum = "d270b79e2926f5150189d475bc7e9d2c69f9c4697b185fa917d5a32b792d21b4" dependencies = [ "cc", ] @@ -3788,9 +3816,9 @@ checksum = "f38d5652c16fde515bb1ecef450ab0f6a219d619a7274976324d5e377f7dceba" [[package]] name = "parking_lot" -version = "0.12.3" +version = "0.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" +checksum = "70d58bf43669b5795d1576d0641cfb6fbb2057bf629506267a92807158584a13" dependencies = [ "lock_api", "parking_lot_core", @@ -3798,9 +3826,9 @@ dependencies = [ [[package]] name = "parking_lot_core" -version = "0.9.10" +version = "0.9.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" +checksum = "bc838d2a56b5b1a6c25f55575dfc605fabb63bb2365f6c2353ef9159aa69e4a5" dependencies = [ "cfg-if", "libc", @@ -3881,7 +3909,7 @@ dependencies = [ "phf_shared 0.11.3", "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.104", ] [[package]] @@ -3894,7 +3922,7 @@ dependencies = [ "phf_shared 0.12.1", "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.104", ] [[package]] @@ -3932,7 +3960,7 @@ checksum = "6e918e4ff8c4549eb882f14b3a4bc8c8bc93de829416eacf579f1207a8fbf861" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.104", ] [[package]] @@ -4068,7 +4096,7 @@ checksum = "5676d703dda103cbb035b653a9f11448c0a7216c7926bd35fcb5865475d0c970" dependencies = [ "autocfg", "equivalent", - "indexmap 2.9.0", + "indexmap 2.10.0", ] [[package]] @@ -4077,7 +4105,7 @@ version = "3.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "edce586971a4dfaa28950c6f18ed55e0406c1ab88bbce2c6f6293a7aaba73d35" dependencies = [ - "toml_edit", + "toml_edit 0.22.27", ] [[package]] @@ -4091,17 +4119,17 @@ dependencies = [ [[package]] name = "proptest" -version = "1.6.0" +version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14cae93065090804185d3b75f0bf93b8eeda30c7a9b4a33d3bdb3988d6229e50" +checksum = "6fcdab19deb5195a31cf7726a210015ff1496ba1464fd42cb4f537b8b01b471f" dependencies = [ "bit-set", "bit-vec", - "bitflags 2.9.0", + "bitflags 2.9.1", "lazy_static", "num-traits", - "rand 0.8.5", - "rand_chacha 0.3.1", + "rand 0.9.2", + "rand_chacha 0.9.0", "rand_xorshift", "regex-syntax 0.8.5", "rusty-fork", @@ -4111,13 +4139,13 @@ dependencies = [ [[package]] name = "proptest-derive" -version = "0.5.1" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ee1c9ac207483d5e7db4940700de86a9aae46ef90c48b57f99fe7edb8345e49" +checksum = "095a99f75c69734802359b682be8daaf8980296731f6470434ea2c652af1dd30" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.104", ] [[package]] @@ -4149,9 +4177,9 @@ dependencies = [ [[package]] name = "r-efi" -version = "5.2.0" +version = "5.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74765f6d916ee2faa39bc8e68e4f3ed8949b48cccdac59983d287a7cb71ce9c5" +checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" [[package]] name = "radium" @@ -4172,9 +4200,9 @@ dependencies = [ [[package]] name = "rand" -version = "0.9.1" +version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fbfd9d094a40bf3ae768db9361049ace4c0e04a4fd6b359518bd7b73a73dd97" +checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1" dependencies = [ "rand_chacha 0.9.0", "rand_core 0.9.3", @@ -4241,11 +4269,11 @@ dependencies = [ [[package]] name = "rand_xorshift" -version = "0.3.0" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d25bf25ec5ae4a3f1b92f929810509a2f53d7dca2f50b794ff57e3face536c8f" +checksum = "513962919efc330f829edb2535844d1b912b0fbe2ca165d613e4e8788bb05a5a" dependencies = [ - "rand_core 0.6.4", + "rand_core 0.9.3", ] [[package]] @@ -4290,38 +4318,27 @@ dependencies = [ [[package]] name = "redb" -version = "2.5.0" +version = "2.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34bc6763177194266fc3773e2b2bb3693f7b02fdf461e285aa33202e3164b74e" +checksum = "59b38b05028f398f08bea4691640503ec25fcb60b82fb61ce1f8fd1f4fccd3f7" dependencies = [ "libc", ] [[package]] name = "redox_syscall" -version = "0.5.12" +version = "0.5.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "928fca9cf2aa042393a8325b9ead81d2f0df4cb12e1e24cef072922ccd99c5af" +checksum = "5407465600fb0548f1442edf71dd20683c6ed326200ace4b1ef0763521bb3b77" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.9.1", ] [[package]] name = "redox_users" -version = "0.4.6" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43" -dependencies = [ - "getrandom 0.2.16", - "libredox", - "thiserror 1.0.69", -] - -[[package]] -name = "redox_users" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd6f9d3d47bdd2ad6945c5015a226ec6155d0bcdfd8f7cd29f86b71f8de99d2b" +checksum = "a4e608c6638b9c18977b00b475ac1f28d14e84b27d8d42f70e0bf1e3dec127ac" dependencies = [ "getrandom 0.2.16", "libredox", @@ -4345,7 +4362,7 @@ checksum = "1165225c21bff1f3bbce98f5a1f889949bc902d3575308cc7b0de30b4f6d27c7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.104", ] [[package]] @@ -4445,11 +4462,11 @@ dependencies = [ [[package]] name = "rusqlite" -version = "0.36.0" +version = "0.37.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3de23c3319433716cf134eed225fe9986bc24f63bed9be9f20c329029e672dc7" +checksum = "165ca6e57b20e1351573e3729b958bc62f0e48025386970b6e4d29e7a7e71f3f" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.9.1", "fallible-iterator", "fallible-streaming-iterator", "hashlink", @@ -4460,9 +4477,9 @@ dependencies = [ [[package]] name = "rustc-demangle" -version = "0.1.24" +version = "0.1.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" +checksum = "56f7d92ca342cea22a06f2121d944b4fd82af56988c270852495420f961d4ace" [[package]] name = "rustc_version" @@ -4484,22 +4501,22 @@ dependencies = [ [[package]] name = "rustix" -version = "1.0.7" +version = "1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c71e83d6afe7ff64890ec6b71d6a69bb8a610ab78ce364b3352876bb4c801266" +checksum = "11181fbabf243db407ef8df94a6ce0b2f9a733bd8be4ad02b4eda9602296cac8" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.9.1", "errno", "libc", "linux-raw-sys", - "windows-sys 0.59.0", + "windows-sys 0.60.2", ] [[package]] name = "rustls" -version = "0.23.27" +version = "0.23.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "730944ca083c1c233a75c09f199e973ca499344a2b7ba9e755c457e86fb4a321" +checksum = "c0ebcbd2f03de0fc1122ad9bb24b127a5a6cd51d72604a3f3c50ac459762b6cc" dependencies = [ "log", "once_cell", @@ -4519,7 +4536,7 @@ dependencies = [ "openssl-probe", "rustls-pki-types", "schannel", - "security-framework 3.2.0", + "security-framework 3.3.0", ] [[package]] @@ -4533,9 +4550,9 @@ dependencies = [ [[package]] name = "rustls-webpki" -version = "0.103.3" +version = "0.103.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4a72fe2bcf7a6ac6fd7d0b9e5cb68aeb7d4c0a0271730218b3e92d43b4eb435" +checksum = "0a17884ae0c1b773f1ccd2bd4a8c72f16da897310a98b0e84bf349ad5ead92fc" dependencies = [ "ring", "rustls-pki-types", @@ -4544,9 +4561,9 @@ dependencies = [ [[package]] name = "rustversion" -version = "1.0.20" +version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eded382c5f5f786b989652c49544c4877d9f015cc22e145a5ea8ea66c2921cd2" +checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" [[package]] name = "rusty-fork" @@ -4568,9 +4585,9 @@ checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" [[package]] name = "safelog" -version = "0.4.7" +version = "0.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d343b15e1705331c25689e0fa87e8514fb33bd3c9beeb45658a7c355f317de2" +checksum = "7a4e1c994fbc7521a5003e5c1c54304654ea0458881e777f6e2638520c2de8c5" dependencies = [ "derive_more", "educe", @@ -4620,9 +4637,9 @@ dependencies = [ [[package]] name = "schemars" -version = "1.0.3" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1375ba8ef45a6f15d83fa8748f1079428295d403d6ea991d09ab100155fbc06d" +checksum = "82d20c4491bc164fa2f6c5d44565947a52ad80b9505d8e36f8d54c27c739fcd0" dependencies = [ "dyn-clone", "ref-cast", @@ -4656,7 +4673,7 @@ version = "2.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.9.1", "core-foundation 0.9.4", "core-foundation-sys", "libc", @@ -4665,12 +4682,12 @@ dependencies = [ [[package]] name = "security-framework" -version = "3.2.0" +version = "3.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "271720403f46ca04f7ba6f55d438f8bd878d6b8ca0a1046e8228c4145bcbb316" +checksum = "80fb1d92c5028aa318b4b8bd7302a5bfcf48be96a37fc6fc790f806b0004ee0c" dependencies = [ - "bitflags 2.9.0", - "core-foundation 0.10.0", + "bitflags 2.9.1", + "core-foundation 0.10.1", "core-foundation-sys", "libc", "security-framework-sys", @@ -4734,7 +4751,7 @@ checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.104", ] [[package]] @@ -4748,9 +4765,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.140" +version = "1.0.142" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "20068b6e96dc6c9bd23e01df8827e6c7e1f2fddd43c21810382803c136b99373" +checksum = "030fedb782600dcbd6f02d479bf0d817ac3bb40d644745b769d6a96bc3afc5a7" dependencies = [ "itoa", "memchr", @@ -4770,22 +4787,19 @@ dependencies = [ [[package]] name = "serde_spanned" -version = "0.6.8" +version = "0.6.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87607cb1398ed59d48732e575a4c28a7a8ebf2454b964fe3f224f2afc07909e1" +checksum = "bf41e0cfaf7226dca15e8197172c295a782857fcb97fad1808a166870dee75a3" dependencies = [ "serde", ] [[package]] -name = "serde_urlencoded" -version = "0.7.1" +name = "serde_spanned" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" +checksum = "40734c41988f7306bb04f0ecf60ec0f3f1caa34290e4e8ea471dcd3346483b83" dependencies = [ - "form_urlencoded", - "itoa", - "ryu", "serde", ] @@ -4799,9 +4813,9 @@ dependencies = [ "chrono", "hex", "indexmap 1.9.3", - "indexmap 2.9.0", + "indexmap 2.10.0", "schemars 0.9.0", - "schemars 1.0.3", + "schemars 1.0.4", "serde", "serde_derive", "serde_json", @@ -4818,7 +4832,7 @@ dependencies = [ "darling 0.20.11", "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.104", ] [[package]] @@ -4869,7 +4883,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b1fdf65dd6331831494dd616b30351c38e96e45921a27745cf98490458b90bb" dependencies = [ "bstr", - "dirs 6.0.0", + "dirs", "os_str_bytes", ] @@ -4881,9 +4895,9 @@ checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" [[package]] name = "signal-hook-registry" -version = "1.4.5" +version = "1.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9203b8055f63a2a00e2f593bb0510367fe707d7ff1e5c872de2f537b339e5410" +checksum = "b2a4719bff48cee6b39d12c020eeb490953ad2443b7055bd0b21fca26bd8c28b" dependencies = [ "libc", ] @@ -4908,7 +4922,7 @@ dependencies = [ "hyper-rustls", "hyper-util", "tokio", - "tower-service 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", + "tower-service", ] [[package]] @@ -4929,12 +4943,9 @@ dependencies = [ [[package]] name = "slab" -version = "0.4.9" +version = "0.4.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" -dependencies = [ - "autocfg", -] +checksum = "7a2ae44ef20feb57a68b23d846850f861394c2e02dc425a50098ae8c90267589" [[package]] name = "slotmap" @@ -4961,18 +4972,18 @@ dependencies = [ [[package]] name = "smallvec" -version = "1.15.0" +version = "1.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8917285742e9f3e1683f0a9c4e6b57960b7314d0b08d30d1ecd426713ee2eee9" +checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" [[package]] name = "socket2" -version = "0.5.9" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f5fd57c80058a56cf5c777ab8a126398ece8e442983605d280a44ce79d0edef" +checksum = "233504af464074f9d066d7b5416c5f9b894a5862a6506e306f7b816cdd6f1807" dependencies = [ "libc", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -5067,46 +5078,23 @@ checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" [[package]] name = "strum" -version = "0.26.3" +version = "0.27.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fec0f0aef304996cf250b31b5a10dee7980c85da9d759361292b8bca5a18f06" +checksum = "af23d6f6c1a224baef9d3f61e287d2761385a5b88fdab4eb4c6f11aeb54c4bcf" dependencies = [ - "strum_macros 0.26.4", -] - -[[package]] -name = "strum" -version = "0.27.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f64def088c51c9510a8579e3c5d67c65349dcf755e5479ad3d010aa6454e2c32" -dependencies = [ - "strum_macros 0.27.1", + "strum_macros", ] [[package]] name = "strum_macros" -version = "0.26.4" +version = "0.27.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c6bee85a5a24955dc440386795aa378cd9cf82acd5f764469152d2270e581be" +checksum = "7695ce3845ea4b33927c055a39dc438a45b059f7c1b3d91d38d10355fb8cbca7" dependencies = [ "heck", "proc-macro2", "quote", - "rustversion", - "syn 2.0.101", -] - -[[package]] -name = "strum_macros" -version = "0.27.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c77a8c5abcaf0f9ce05d62342b7d298c346515365c36b673df4ebe3ced01fde8" -dependencies = [ - "heck", - "proc-macro2", - "quote", - "rustversion", - "syn 2.0.101", + "syn 2.0.104", ] [[package]] @@ -5128,21 +5116,15 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.101" +version = "2.0.104" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ce2b7fc941b3a24138a0a7cf8e858bfc6a992e7978a068a5c760deb0ed43caf" +checksum = "17b6f705963418cdb9927482fa304bc562ece2fdd4f616084c50b7023b435a40" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] -[[package]] -name = "sync_wrapper" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" - [[package]] name = "sync_wrapper" version = "1.0.2" @@ -5166,7 +5148,21 @@ checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.104", +] + +[[package]] +name = "sysinfo" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "252800745060e7b9ffb7b2badbd8b31cfa4aa2e61af879d0a3bf2a317c20217d" +dependencies = [ + "libc", + "memchr", + "ntapi", + "objc2-core-foundation", + "objc2-io-kit", + "windows", ] [[package]] @@ -5177,9 +5173,9 @@ checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" [[package]] name = "tempfile" -version = "3.19.1" +version = "3.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7437ac7763b9b123ccf33c338a5cc1bac6f69b45a136c19bdd8a65e3916435bf" +checksum = "e8a64e3985349f2441a1a9ef0b853f869006c3855f2cda6862a94d26ebb9d6a1" dependencies = [ "fastrand", "getrandom 0.3.3", @@ -5224,7 +5220,7 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.104", ] [[package]] @@ -5235,17 +5231,16 @@ checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.104", ] [[package]] name = "thread_local" -version = "1.1.8" +version = "1.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c" +checksum = "f60246a4944f24f6e018aa17cdeffb7818b76356965d03b07d6a9886e8962185" dependencies = [ "cfg-if", - "once_cell", ] [[package]] @@ -5307,20 +5302,22 @@ dependencies = [ [[package]] name = "tokio" -version = "1.45.0" +version = "1.47.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2513ca694ef9ede0fb23fe71a4ee4107cb102b9dc1930f6d0fd77aae068ae165" +checksum = "89e49afdadebb872d3145a5638b59eb0691ea23e46ca484037cfab3b76b95038" dependencies = [ "backtrace", "bytes", + "io-uring", "libc", "mio", "parking_lot", "pin-project-lite", "signal-hook-registry", + "slab", "socket2", "tokio-macros", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -5331,7 +5328,7 @@ checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.104", ] [[package]] @@ -5382,16 +5379,15 @@ dependencies = [ [[package]] name = "tokio-util" -version = "0.7.15" +version = "0.7.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "66a539a9ad6d5d281510d5bd368c973d636c02dbf8a67300bfb6b950696ad7df" +checksum = "14307c986784f72ef81c89db7d9e28d6ac26d16213b109ea501696195e6e3ce5" dependencies = [ "bytes", "futures-core", "futures-io", "futures-sink", "futures-util", - "hashbrown 0.15.3", "pin-project-lite", "slab", "tokio", @@ -5400,52 +5396,103 @@ dependencies = [ [[package]] name = "toml" -version = "0.8.22" +version = "0.8.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05ae329d1f08c4d17a59bed7ff5b5a769d062e64a62d34a3261b219e62cd5aae" +checksum = "dc1beb996b9d83529a9e75c17a1686767d148d70663143c7854d8b4a09ced362" dependencies = [ "serde", - "serde_spanned", - "toml_datetime", - "toml_edit", + "serde_spanned 0.6.9", + "toml_datetime 0.6.11", + "toml_edit 0.22.27", +] + +[[package]] +name = "toml" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75129e1dc5000bfbaa9fee9d1b21f974f9fbad9daec557a521ee6e080825f6e8" +dependencies = [ + "serde", + "serde_spanned 1.0.0", + "toml_datetime 0.7.0", + "toml_parser", + "toml_writer", + "winnow", ] [[package]] name = "toml_datetime" -version = "0.6.9" +version = "0.6.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3da5db5a963e24bc68be8b17b6fa82814bb22ee8660f192bb182771d498f09a3" +checksum = "22cddaf88f4fbc13c51aebbf5f8eceb5c7c5a9da2ac40a13519eb5b0a0e8f11c" +dependencies = [ + "serde", +] + +[[package]] +name = "toml_datetime" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bade1c3e902f58d73d3f294cd7f20391c1cb2fbcb643b73566bc773971df91e3" dependencies = [ "serde", ] [[package]] name = "toml_edit" -version = "0.22.26" +version = "0.22.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "310068873db2c5b3e7659d2cc35d21855dbafa50d1ce336397c666e3cb08137e" +checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a" dependencies = [ - "indexmap 2.9.0", + "indexmap 2.10.0", "serde", - "serde_spanned", - "toml_datetime", + "serde_spanned 0.6.9", + "toml_datetime 0.6.11", "toml_write", "winnow", ] [[package]] -name = "toml_write" -version = "0.1.1" +name = "toml_edit" +version = "0.23.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfb942dfe1d8e29a7ee7fcbde5bd2b9a25fb89aa70caea2eba3bee836ff41076" +checksum = "17d3b47e6b7a040216ae5302712c94d1cf88c95b47efa80e2c59ce96c878267e" +dependencies = [ + "indexmap 2.10.0", + "toml_datetime 0.7.0", + "toml_parser", + "toml_writer", + "winnow", +] + +[[package]] +name = "toml_parser" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b551886f449aa90d4fe2bdaa9f4a2577ad2dde302c61ecf262d80b116db95c10" +dependencies = [ + "winnow", +] + +[[package]] +name = "toml_write" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d99f8c9a7727884afe522e9bd5edbfc91a3312b36a77b5fb8926e4c31a41801" + +[[package]] +name = "toml_writer" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcc842091f2def52017664b53082ecbbeb5c7731092bad69d2c63050401dfd64" [[package]] name = "tor-async-utils" -version = "0.32.0" +version = "0.33.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12bee2c4a8ea4cfe533bf284eef89d26f53ed0145854c54471734cb36e451f3e" +checksum = "28240d2b739ecba7514c92c0e42f2b5b81a95b5286366bdb2c2f8ef526a5578c" dependencies = [ - "derive-deftly 1.1.0", + "derive-deftly 1.2.0", "educe", "futures", "oneshot-fused-workaround", @@ -5457,16 +5504,16 @@ dependencies = [ [[package]] name = "tor-basic-utils" -version = "0.32.0" +version = "0.33.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41a4e9e4da7ce0f089aee75808db143c4d547ca6d4ffd5b15a1cb042c3082928" +checksum = "55f86a4e4768d337df0e1189eb229c1c27125e86282fabdef89088e1f0be9107" dependencies = [ "derive_more", "hex", "itertools 0.14.0", "libc", "paste", - "rand 0.9.1", + "rand 0.9.2", "rand_chacha 0.9.0", "serde", "slab", @@ -5476,12 +5523,12 @@ dependencies = [ [[package]] name = "tor-bytes" -version = "0.32.0" +version = "0.33.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5a4a8401219d99b460c9bc001386366a4c50dc881a0abf346f04c1785f7e06a" +checksum = "7d08b5b2e93fd21a4aaa9a3867962ea82f5dc830522737183abb514bdeae9fc9" dependencies = [ "bytes", - "derive-deftly 1.1.0", + "derive-deftly 1.2.0", "digest", "educe", "getrandom 0.3.3", @@ -5494,19 +5541,19 @@ dependencies = [ [[package]] name = "tor-cell" -version = "0.32.0" +version = "0.33.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "230485eda02aa73aea39aefdc1aff7fbc6304de1ce510de366261727e3007a92" +checksum = "60677bfa808c00539df7f8276facd59f92e5d0bd22ee8e5bdc1ab6a14632db41" dependencies = [ "amplify", - "bitflags 2.9.0", + "bitflags 2.9.1", "bytes", "caret", - "derive-deftly 1.1.0", + "derive-deftly 1.2.0", "derive_more", "educe", "paste", - "rand 0.9.1", + "rand 0.9.2", "smallvec", "thiserror 2.0.12", "tor-basic-utils", @@ -5524,9 +5571,9 @@ dependencies = [ [[package]] name = "tor-cert" -version = "0.32.0" +version = "0.33.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9abc35321d207c3ffbfdc37feb0a9e186555ee49dfaa8079027115ce44491ff2" +checksum = "abd81384d03705336b9fb7ecf152e4f61b2e0a0cb1adbd9bbd116b46a010e230" dependencies = [ "caret", "derive_builder_fork_arti", @@ -5540,9 +5587,9 @@ dependencies = [ [[package]] name = "tor-chanmgr" -version = "0.32.0" +version = "0.33.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85f54ff3c72323739c4903f6b8c5fd1a0b8b8554fbba2c1ffa712c1c7d4faa22" +checksum = "74322fa01b09b3839903da1e7f443b2cf8aecd31f0cfd5395253ddad473b4ef3" dependencies = [ "async-trait", "caret", @@ -5552,7 +5599,7 @@ dependencies = [ "futures", "oneshot-fused-workaround", "postage", - "rand 0.9.1", + "rand 0.9.2", "safelog", "serde", "thiserror 2.0.12", @@ -5575,9 +5622,9 @@ dependencies = [ [[package]] name = "tor-checkable" -version = "0.32.0" +version = "0.33.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72e994401be86ecdaa8b17b176cd1562ddddc6778a47afd751e6db99ccadb8e6" +checksum = "5703d370d4ee4b5c318ac8b944a3b183e2865f6f0104475d36b9e1b8c89f0f38" dependencies = [ "humantime", "signature", @@ -5587,9 +5634,9 @@ dependencies = [ [[package]] name = "tor-circmgr" -version = "0.32.0" +version = "0.33.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "efafa2ca72873965c33dcba7375d14d436b584cacb0344b8068f559d8d775bcc" +checksum = "0f8f6d0e84dcf5fa3761e55fe8bb75725e699ee03e3b37bbd06e6421e9961fbd" dependencies = [ "amplify", "async-trait", @@ -5606,7 +5653,7 @@ dependencies = [ "once_cell", "oneshot-fused-workaround", "pin-project", - "rand 0.9.1", + "rand 0.9.2", "retry-error", "safelog", "serde", @@ -5635,13 +5682,13 @@ dependencies = [ [[package]] name = "tor-config" -version = "0.32.0" +version = "0.33.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3280e6e26a30f94d752d55d273c307d2b819971ff4b830101d816990f9a5ec36" +checksum = "3852d0e3a6ca41ab9fed79fa4cb89347bdb24bcd5f6665f506a10b993ac24e8b" dependencies = [ "amplify", "cfg-if", - "derive-deftly 1.1.0", + "derive-deftly 1.2.0", "derive_builder_fork_arti", "educe", "either", @@ -5656,9 +5703,9 @@ dependencies = [ "serde", "serde-value", "serde_ignored", - "strum 0.27.1", + "strum", "thiserror 2.0.12", - "toml", + "toml 0.8.23", "tor-basic-utils", "tor-error", "tor-rtcompat", @@ -5668,9 +5715,9 @@ dependencies = [ [[package]] name = "tor-config-path" -version = "0.32.0" +version = "0.33.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5347bcbe96c660694fe52fb76e852d982d73fe0d92f4c4cb9eaa8427a5d52f17" +checksum = "bf19ba283027eb8d8c441eec743d2c73c971c4b22c9830aae87163e8fcdc334e" dependencies = [ "directories", "serde", @@ -5682,9 +5729,9 @@ dependencies = [ [[package]] name = "tor-consdiff" -version = "0.32.0" +version = "0.33.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3d8e230e901c09dd73c81f12402e8c0221d992ee131ff32ac0dde72f972f47e" +checksum = "b5164757c908a50737ebd483a0e29380e53db0b14103f7996751e28964c6d98a" dependencies = [ "digest", "hex", @@ -5694,9 +5741,9 @@ dependencies = [ [[package]] name = "tor-dirclient" -version = "0.32.0" +version = "0.33.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d6913b3a246442dca5a02b3002c9820b0e491a03eb9448843bd73782fb2b0d7" +checksum = "a411a66d9a5f41b2e85e8defb17a3641d5827f168e4b175a28db9b987125843f" dependencies = [ "async-compression", "base64ct", @@ -5722,9 +5769,9 @@ dependencies = [ [[package]] name = "tor-dirmgr" -version = "0.32.0" +version = "0.33.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05a13cf2d0f3d92b8a0d45b661d7b2978ae726cc252d8a8e4308b750ab3b3275" +checksum = "5c74b75377747b13d338c8ab29c5de44f4485c04c492a15edebed61110e24299" dependencies = [ "async-trait", "base64ct", @@ -5744,7 +5791,7 @@ dependencies = [ "oneshot-fused-workaround", "paste", "postage", - "rand 0.9.1", + "rand 0.9.2", "rusqlite", "safelog", "scopeguard", @@ -5752,7 +5799,7 @@ dependencies = [ "serde_json", "signature", "static_assertions", - "strum 0.27.1", + "strum", "thiserror 2.0.12", "time", "tor-async-utils", @@ -5776,16 +5823,16 @@ dependencies = [ [[package]] name = "tor-error" -version = "0.32.0" +version = "0.33.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79ae19c74564749c54e14e532ffb15f84807f734d17f452bb3ffb8b1957f06a2" +checksum = "c5566edb4beb34d7be7bc4fc2f653a5119a3ccade26a9237912e53d4f4029e83" dependencies = [ "derive_more", "futures", "paste", "retry-error", "static_assertions", - "strum 0.27.1", + "strum", "thiserror 2.0.12", "tracing", "void", @@ -5793,9 +5840,9 @@ dependencies = [ [[package]] name = "tor-general-addr" -version = "0.32.0" +version = "0.33.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dad9c6e9147f4ee644c80c3b044813cf93a3f802279c49b06aac2f4f33555877" +checksum = "bc9d4a8c4593a0c590c66bae348590fb49dbf04da264cacc0631d71a92e5d2ac" dependencies = [ "derive_more", "thiserror 2.0.12", @@ -5804,13 +5851,13 @@ dependencies = [ [[package]] name = "tor-guardmgr" -version = "0.32.0" +version = "0.33.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32ff4df101c82b9731bc9d54b94ffdee6053471cfa3aa78663c53a545d3d3139" +checksum = "aef0ad8a7ba30210b9c5664e10c423cc19f83d55fafdf1c84097be3a01d0dacd" dependencies = [ "amplify", "base64ct", - "derive-deftly 1.1.0", + "derive-deftly 1.2.0", "derive_builder_fork_arti", "derive_more", "dyn-clone", @@ -5823,10 +5870,10 @@ dependencies = [ "oneshot-fused-workaround", "pin-project", "postage", - "rand 0.9.1", + "rand 0.9.2", "safelog", "serde", - "strum 0.27.1", + "strum", "thiserror 2.0.12", "tor-async-utils", "tor-basic-utils", @@ -5846,12 +5893,12 @@ dependencies = [ [[package]] name = "tor-hsclient" -version = "0.32.0" +version = "0.33.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4ad84737ce249b56f74bede1b09f61a4169979c3cef124012a9447e1790066a" +checksum = "2a0ed4b8790cf8849d10c7ff13db3ec570c2a4aec68331dfe6e1f05d2987d40d" dependencies = [ "async-trait", - "derive-deftly 1.1.0", + "derive-deftly 1.2.0", "derive_more", "educe", "either", @@ -5859,11 +5906,11 @@ dependencies = [ "itertools 0.14.0", "oneshot-fused-workaround", "postage", - "rand 0.9.1", + "rand 0.9.2", "retry-error", "safelog", "slotmap-careful", - "strum 0.27.1", + "strum", "thiserror 2.0.12", "tor-async-utils", "tor-basic-utils", @@ -5890,20 +5937,20 @@ dependencies = [ [[package]] name = "tor-hscrypto" -version = "0.32.0" +version = "0.33.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7469efe5d22466fcaaeec9506bf03426ce59c03ee1b8a7c8b316830b153b40a" +checksum = "163e60a7c8069ea8d8a3031be8620047b21cbed7d535fd9f1662daae7803ccd6" dependencies = [ "cipher", "data-encoding", - "derive-deftly 1.1.0", + "derive-deftly 1.2.0", "derive_more", "digest", "hex", "humantime", "itertools 0.14.0", "paste", - "rand 0.9.1", + "rand 0.9.2", "safelog", "serde", "signature", @@ -5922,15 +5969,15 @@ dependencies = [ [[package]] name = "tor-hsservice" -version = "0.32.0" +version = "0.33.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60ed074e5e9a9eb14e4e98b37d9f08a43538883e71ba916c859dbc4f6ad90f1f" +checksum = "e736178aed67cc200ab84b35b5c9d116a8ed42c2dc2eeaa7ada1828116da500d" dependencies = [ "amplify", "async-trait", "base64ct", "cfg-if", - "derive-deftly 1.1.0", + "derive-deftly 1.2.0", "derive_builder_fork_arti", "derive_more", "digest", @@ -5945,13 +5992,13 @@ dependencies = [ "once_cell", "oneshot-fused-workaround", "postage", - "rand 0.9.1", + "rand 0.9.2", "rand_core 0.9.3", "retry-error", "safelog", "serde", "serde_with", - "strum 0.27.1", + "strum", "thiserror 2.0.12", "tor-async-utils", "tor-basic-utils", @@ -5980,15 +6027,15 @@ dependencies = [ [[package]] name = "tor-key-forge" -version = "0.32.0" +version = "0.33.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6697b58f1518757b975993d345a261387eb7a86c730cb542ad1ea68284155eaa" +checksum = "3b1ddfa5126b2619a9f27b5c5d1a360acf41f15d99ced37af1f30fc449c65889" dependencies = [ - "derive-deftly 1.1.0", + "derive-deftly 1.2.0", "derive_more", "downcast-rs", "paste", - "rand 0.9.1", + "rand 0.9.2", "signature", "ssh-key", "thiserror 2.0.12", @@ -6001,14 +6048,14 @@ dependencies = [ [[package]] name = "tor-keymgr" -version = "0.32.0" +version = "0.33.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56d765c0fd6b1910b427feb1b604fbc85e5768332e9be1e670bf64e698c92606" +checksum = "26e8e0222d2ac27bd8bf4848cd46371fdc72d820940eb38f3d8da616176b3d39" dependencies = [ "amplify", "arrayvec", "cfg-if", - "derive-deftly 1.1.0", + "derive-deftly 1.2.0", "derive_builder_fork_arti", "derive_more", "downcast-rs", @@ -6018,7 +6065,8 @@ dependencies = [ "humantime", "inventory", "itertools 0.14.0", - "rand 0.9.1", + "rand 0.9.2", + "safelog", "serde", "signature", "ssh-key", @@ -6033,20 +6081,21 @@ dependencies = [ "tor-llcrypto", "tor-persist", "tracing", + "visibility", "walkdir", "zeroize", ] [[package]] name = "tor-linkspec" -version = "0.32.0" +version = "0.33.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1feeea901ff88616175c3bcf3fbc53466117e7d655c15253f80260be017333bd" +checksum = "9e6eab944bf3096b1964cbb0f4a3605c1376a35bdfa9d44512464474ffb8e53c" dependencies = [ "base64ct", "by_address", "caret", - "derive-deftly 1.1.0", + "derive-deftly 1.2.0", "derive_builder_fork_arti", "derive_more", "hex", @@ -6054,7 +6103,7 @@ dependencies = [ "safelog", "serde", "serde_with", - "strum 0.27.1", + "strum", "thiserror 2.0.12", "tor-basic-utils", "tor-bytes", @@ -6066,23 +6115,23 @@ dependencies = [ [[package]] name = "tor-llcrypto" -version = "0.32.0" +version = "0.33.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "992c49dd4c285c52594858c0e92afe96531203dbe9bb29cfbe6937d94bb3c7ad" +checksum = "8b92fa9a99a066f06cd266287f6f89270c010693cce3c4c2fa38c27abfcda5fb" dependencies = [ "aes", "base64ct", "ctr", "curve25519-dalek", "der-parser", - "derive-deftly 1.1.0", + "derive-deftly 1.2.0", "derive_more", "digest", "ed25519-dalek", "educe", "getrandom 0.3.3", "hex", - "rand 0.9.1", + "rand 0.9.2", "rand_chacha 0.9.0", "rand_core 0.6.4", "rand_core 0.9.3", @@ -6105,9 +6154,9 @@ dependencies = [ [[package]] name = "tor-log-ratelim" -version = "0.32.0" +version = "0.33.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "511702fd833651896f1e6ca486c1b0d8d3f28b8f8724526ae8260c33ee49b2e8" +checksum = "8fc97bd804ac7aeaf771dc4507c65f8c5fac1d4f8e469551aaa3bf240fce1171" dependencies = [ "futures", "humantime", @@ -6120,11 +6169,12 @@ dependencies = [ [[package]] name = "tor-memquota" -version = "0.32.0" +version = "0.33.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07de5fe3ab545970b36319397b8c5e82fffbad5682581e06d030cfda0296cd9f" +checksum = "f015b156dc186601f46b3f89ebd6ace49ef3b142c9a5004559e5d75a6477cc1f" dependencies = [ - "derive-deftly 1.1.0", + "cfg-if", + "derive-deftly 1.2.0", "derive_more", "dyn-clone", "educe", @@ -6135,6 +6185,7 @@ dependencies = [ "serde", "slotmap-careful", "static_assertions", + "sysinfo", "thiserror 2.0.12", "tor-async-utils", "tor-basic-utils", @@ -6148,12 +6199,12 @@ dependencies = [ [[package]] name = "tor-netdir" -version = "0.32.0" +version = "0.33.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "497936529955a5512e2500366aca3f353081f51edde633d5c9fc2124f1da36a6" +checksum = "7874878d0c579e7b1dea5947581a5f2d4ba350af2a20b1946350ef9ace16ebe1" dependencies = [ "async-trait", - "bitflags 2.9.0", + "bitflags 2.9.1", "derive_more", "digest", "futures", @@ -6161,10 +6212,10 @@ dependencies = [ "humantime", "itertools 0.14.0", "num_enum", - "rand 0.9.1", + "rand 0.9.2", "serde", "static_assertions", - "strum 0.27.1", + "strum", "thiserror 2.0.12", "time", "tor-basic-utils", @@ -6181,13 +6232,13 @@ dependencies = [ [[package]] name = "tor-netdoc" -version = "0.32.0" +version = "0.33.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f51ac213a399bf26bc4801495dd17ede3c118d2bedd6575d79c08dbf1379c1a7" +checksum = "4ea853a14cb2011b9f4c45641323e19fbad13010d638dd3d84502e0decf9db0b" dependencies = [ "amplify", "base64ct", - "bitflags 2.9.0", + "bitflags 2.9.1", "cipher", "derive_builder_fork_arti", "derive_more", @@ -6198,7 +6249,7 @@ dependencies = [ "itertools 0.14.0", "memchr", "phf 0.12.1", - "rand 0.9.1", + "rand 0.9.2", "serde", "serde_with", "signature", @@ -6225,12 +6276,12 @@ dependencies = [ [[package]] name = "tor-persist" -version = "0.32.0" +version = "0.33.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fabc9ba76dbe0ca3b254ed73480455a337c1941904f375d583efcdc57966f98" +checksum = "e899d9dd8104fae50c33f15e00a1515bee15683ff0017cfc003315fcd6a195d6" dependencies = [ "amplify", - "derive-deftly 1.1.0", + "derive-deftly 1.2.0", "derive_more", "filetime", "fs-mistrust", @@ -6254,9 +6305,9 @@ dependencies = [ [[package]] name = "tor-proto" -version = "0.32.0" +version = "0.33.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89554cb29f3002b33851e5113b10ae97ca395548a11639de557caac01f627f41" +checksum = "c08f8d03310fadbc44e9544777a0bb5a7364564798d2236f00db3c337fb25987" dependencies = [ "amplify", "asynchronous-codec", @@ -6267,7 +6318,7 @@ dependencies = [ "cipher", "coarsetime", "criterion-cycles-per-byte", - "derive-deftly 1.1.0", + "derive-deftly 1.2.0", "derive_builder_fork_arti", "derive_more", "digest", @@ -6280,14 +6331,14 @@ dependencies = [ "oneshot-fused-workaround", "pin-project", "postage", - "rand 0.9.1", + "rand 0.9.2", "rand_core 0.9.3", "safelog", "slotmap-careful", "smallvec", "static_assertions", "subtle", - "sync_wrapper 1.0.2", + "sync_wrapper", "thiserror 2.0.12", "tokio", "tokio-util", @@ -6317,9 +6368,9 @@ dependencies = [ [[package]] name = "tor-protover" -version = "0.32.0" +version = "0.33.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4af2a2d9610dafe2fd8eb79015f91e0e8881155147d676b190e061ae7ee0403e" +checksum = "0fcc72e258205ca0511bdc84801748c59dd5d7accfd080d909afd11f6b8be182" dependencies = [ "caret", "paste", @@ -6330,11 +6381,11 @@ dependencies = [ [[package]] name = "tor-relay-selection" -version = "0.32.0" +version = "0.33.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5516546b720c83a21636824f2a03c64caf1a72bc1f94ee16672679f1295e7a6" +checksum = "a19816299f125c71bd4ba59be8ea425256aa4494b6a3d39bdb9ccbc385722a70" dependencies = [ - "rand 0.9.1", + "rand 0.9.2", "serde", "tor-basic-utils", "tor-linkspec", @@ -6344,9 +6395,9 @@ dependencies = [ [[package]] name = "tor-rtcompat" -version = "0.32.0" +version = "0.33.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a75969f63c5147af49753e1c03339d342bc3e53e7412518e52702cc417cff729" +checksum = "e9545fa3b1420df5b165e6bad6537b7202c533311431cabc84ff1908fbca3908" dependencies = [ "async-native-tls", "async-trait", @@ -6373,14 +6424,14 @@ dependencies = [ [[package]] name = "tor-rtmock" -version = "0.32.0" +version = "0.33.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a13e2946c396cf450ea4f9a3b48f2c4125b011da6019d7da7f1e0e7cd37596c9" +checksum = "9a6ca08427dff9e7a22aa08d8c680c56231ce9b6afa35125e8a9a78f6b8e2890" dependencies = [ "amplify", "assert_matches", "async-trait", - "derive-deftly 1.1.0", + "derive-deftly 1.2.0", "derive_more", "educe", "futures", @@ -6390,7 +6441,7 @@ dependencies = [ "pin-project", "priority-queue", "slotmap-careful", - "strum 0.27.1", + "strum", "thiserror 2.0.12", "tor-error", "tor-general-addr", @@ -6402,13 +6453,13 @@ dependencies = [ [[package]] name = "tor-socksproto" -version = "0.32.0" +version = "0.33.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9b7d2a9b7394b8f2c683d282f4a0da08c056ed23b3bb9afdb84cb92d554c77b" +checksum = "e5b33285d16e5695b6aba7c9656387ab0c662781e2911376887b8b61d1a3d2b2" dependencies = [ "amplify", "caret", - "derive-deftly 1.1.0", + "derive-deftly 1.2.0", "educe", "safelog", "subtle", @@ -6419,33 +6470,17 @@ dependencies = [ [[package]] name = "tor-units" -version = "0.32.0" +version = "0.33.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22f2bd3dc4f5defec5d4b9d152d911a3a852d08409558dd927ec8eb28e20f9de" +checksum = "40e61ae922f0f0209338d63afd4b1b4ba780313b427a54dda212ca3853845578" dependencies = [ - "derive-deftly 1.1.0", + "derive-deftly 1.2.0", "derive_more", "serde", "thiserror 2.0.12", "tor-memquota", ] -[[package]] -name = "tower" -version = "0.5.1" -source = "git+https://github.com/Cuprate/tower.git?rev=6c7faf0#6c7faf0e9dbc74aef5d3110313324bc7e1f997cf" -dependencies = [ - "futures-core", - "futures-util", - "pin-project-lite", - "sync_wrapper 0.1.2", - "tokio", - "tokio-util", - "tower-layer 0.3.3 (git+https://github.com/Cuprate/tower.git?rev=6c7faf0)", - "tower-service 0.3.3 (git+https://github.com/Cuprate/tower.git?rev=6c7faf0)", - "tracing", -] - [[package]] name = "tower" version = "0.5.2" @@ -6455,27 +6490,28 @@ dependencies = [ "futures-core", "futures-util", "pin-project-lite", - "sync_wrapper 1.0.2", + "sync_wrapper", "tokio", - "tower-layer 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", - "tower-service 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-util", + "tower-layer", + "tower-service", "tracing", ] [[package]] name = "tower-http" -version = "0.6.2" +version = "0.6.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "403fa3b783d4b626a8ad51d766ab03cb6d2dbfc46b1c5d4448395e6628dc9697" +checksum = "adc82fd73de2a9722ac5da747f12383d2bfdb93591ee6c58486e0097890f05f2" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.9.1", "bytes", "http", "http-body", "http-body-util", "pin-project-lite", - "tower-layer 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", - "tower-service 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", + "tower-layer", + "tower-service", ] [[package]] @@ -6484,29 +6520,18 @@ version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e" -[[package]] -name = "tower-layer" -version = "0.3.3" -source = "git+https://github.com/Cuprate/tower.git?rev=6c7faf0#6c7faf0e9dbc74aef5d3110313324bc7e1f997cf" - [[package]] name = "tower-service" version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" -[[package]] -name = "tower-service" -version = "0.3.3" -source = "git+https://github.com/Cuprate/tower.git?rev=6c7faf0#6c7faf0e9dbc74aef5d3110313324bc7e1f997cf" - [[package]] name = "tracing" version = "0.1.41" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" dependencies = [ - "log", "pin-project-lite", "tracing-attributes", "tracing-core", @@ -6526,20 +6551,20 @@ dependencies = [ [[package]] name = "tracing-attributes" -version = "0.1.28" +version = "0.1.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "395ae124c09f9e6918a2310af6038fba074bcf474ac352496d5910dd59a2226d" +checksum = "81383ab64e72a7a8b8e13130c49e3dab29def6d0c7d76a03087b3cf71c5c6903" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.104", ] [[package]] name = "tracing-core" -version = "0.1.33" +version = "0.1.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e672c95779cf947c5311f83787af4fa8fffd12fb27e4993211a84bdfd9610f9c" +checksum = "b9d12581f227e93f094d3af2ae690a574abb8a2b9b7a96e7cfe9647b2b617678" dependencies = [ "once_cell", "valuable", @@ -6592,7 +6617,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "04659ddb06c87d233c566112c1c9c5b9e98256d9af50ec3bc9c8327f873a7568" dependencies = [ "quote", - "syn 2.0.101", + "syn 2.0.104", ] [[package]] @@ -6723,7 +6748,7 @@ checksum = "d674d135b4a8c1d7e813e2f8d1c9a58308aee4a680323066025e53132218bd91" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.104", ] [[package]] @@ -6762,9 +6787,9 @@ dependencies = [ [[package]] name = "wasi" -version = "0.11.0+wasi-snapshot-preview1" +version = "0.11.1+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" +checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" [[package]] name = "wasi" @@ -6781,7 +6806,7 @@ version = "0.12.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c1fbb4ef9bbca0c1170e0b00dd28abc9e3b68669821600cad1caaed606583c6d" dependencies = [ - "wasi 0.11.0+wasi-snapshot-preview1", + "wasi 0.11.1+wasi-snapshot-preview1", ] [[package]] @@ -6806,7 +6831,7 @@ dependencies = [ "log", "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.104", "wasm-bindgen-shared", ] @@ -6828,7 +6853,7 @@ checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.104", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -6864,14 +6889,14 @@ version = "0.26.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "521bc38abb08001b01866da9f51eb7c5d647a19260e00054a8c7fd5f9e57f7a9" dependencies = [ - "webpki-roots 1.0.0", + "webpki-roots 1.0.2", ] [[package]] name = "webpki-roots" -version = "1.0.0" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2853738d1cc4f2da3a225c18ec6c3721abb31961096e9dbf5ab35fa88b19cfdb" +checksum = "7e8983c3ab33d6fb807cfcdad2491c4ea8cbc8ed839181c7dfd9c67c83e261b2" dependencies = [ "rustls-pki-types", ] @@ -6909,9 +6934,9 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] name = "windows" -version = "0.61.1" +version = "0.61.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5ee8f3d025738cb02bad7868bbb5f8a6327501e870bf51f1b455b0a2454a419" +checksum = "9babd3a767a4c1aef6900409f85f5d53ce2544ccdfaa86dad48c91782c6d6893" dependencies = [ "windows-collections", "windows-core", @@ -6931,9 +6956,9 @@ dependencies = [ [[package]] name = "windows-core" -version = "0.61.0" +version = "0.61.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4763c1de310c86d75a878046489e2e5ba02c649d185f21c67d4cf8a56d098980" +checksum = "c0fdd3ddb90610c7638aa2b3a3ab2904fb9e5cdbecc643ddb3647212781c4ae3" dependencies = [ "windows-implement", "windows-interface", @@ -6944,12 +6969,13 @@ dependencies = [ [[package]] name = "windows-future" -version = "0.2.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a1d6bbefcb7b60acd19828e1bc965da6fcf18a7e39490c5f8be71e54a19ba32" +checksum = "fc6a41e98427b19fe4b73c550f060b59fa592d7d686537eebf9385621bfbad8e" dependencies = [ "windows-core", "windows-link", + "windows-threading", ] [[package]] @@ -6960,7 +6986,7 @@ checksum = "a47fddd13af08290e67f4acabf4b459f647552718f683a7b415d290ac744a836" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.104", ] [[package]] @@ -6971,14 +6997,14 @@ checksum = "bd9211b69f8dcdfa817bfd14bf1c97c9188afa36f4750130fcdf3f400eca9fa8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.104", ] [[package]] name = "windows-link" -version = "0.1.1" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76840935b766e1b0a05c0066835fb9ec80071d4c09a16f6bd5f7e655e3c14c38" +checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a" [[package]] name = "windows-numerics" @@ -6992,31 +7018,22 @@ dependencies = [ [[package]] name = "windows-result" -version = "0.3.2" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c64fd11a4fd95df68efcfee5f44a294fe71b8bc6a91993e2791938abcc712252" +checksum = "56f42bd332cc6c8eac5af113fc0c1fd6a8fd2aa08a0119358686e5160d0586c6" dependencies = [ "windows-link", ] [[package]] name = "windows-strings" -version = "0.4.0" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a2ba9642430ee452d5a7aa78d72907ebe8cfda358e8cb7918a2050581322f97" +checksum = "56e6c93f3a0c3b36176cb1327a4958a0353d5d166c2a35cb268ace15e91d3b57" dependencies = [ "windows-link", ] -[[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]] name = "windows-sys" version = "0.52.0" @@ -7036,18 +7053,12 @@ dependencies = [ ] [[package]] -name = "windows-targets" -version = "0.48.5" +name = "windows-sys" +version = "0.60.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" 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 0.53.3", ] [[package]] @@ -7059,7 +7070,7 @@ dependencies = [ "windows_aarch64_gnullvm 0.52.6", "windows_aarch64_msvc 0.52.6", "windows_i686_gnu 0.52.6", - "windows_i686_gnullvm", + "windows_i686_gnullvm 0.52.6", "windows_i686_msvc 0.52.6", "windows_x86_64_gnu 0.52.6", "windows_x86_64_gnullvm 0.52.6", @@ -7067,10 +7078,30 @@ dependencies = [ ] [[package]] -name = "windows_aarch64_gnullvm" -version = "0.48.5" +name = "windows-targets" +version = "0.53.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" +checksum = "d5fe6031c4041849d7c496a8ded650796e7b6ecc19df1a431c1a363342e5dc91" +dependencies = [ + "windows-link", + "windows_aarch64_gnullvm 0.53.0", + "windows_aarch64_msvc 0.53.0", + "windows_i686_gnu 0.53.0", + "windows_i686_gnullvm 0.53.0", + "windows_i686_msvc 0.53.0", + "windows_x86_64_gnu 0.53.0", + "windows_x86_64_gnullvm 0.53.0", + "windows_x86_64_msvc 0.53.0", +] + +[[package]] +name = "windows-threading" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b66463ad2e0ea3bbf808b7f1d371311c80e115c0b71d60efc142cafbcfb057a6" +dependencies = [ + "windows-link", +] [[package]] name = "windows_aarch64_gnullvm" @@ -7079,10 +7110,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" [[package]] -name = "windows_aarch64_msvc" -version = "0.48.5" +name = "windows_aarch64_gnullvm" +version = "0.53.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" +checksum = "86b8d5f90ddd19cb4a147a5fa63ca848db3df085e25fee3cc10b39b6eebae764" [[package]] name = "windows_aarch64_msvc" @@ -7091,10 +7122,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" [[package]] -name = "windows_i686_gnu" -version = "0.48.5" +name = "windows_aarch64_msvc" +version = "0.53.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" +checksum = "c7651a1f62a11b8cbd5e0d42526e55f2c99886c77e007179efff86c2b137e66c" [[package]] name = "windows_i686_gnu" @@ -7102,6 +7133,12 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" +[[package]] +name = "windows_i686_gnu" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1dc67659d35f387f5f6c479dc4e28f1d4bb90ddd1a5d3da2e5d97b42d6272c3" + [[package]] name = "windows_i686_gnullvm" version = "0.52.6" @@ -7109,10 +7146,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" [[package]] -name = "windows_i686_msvc" -version = "0.48.5" +name = "windows_i686_gnullvm" +version = "0.53.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" +checksum = "9ce6ccbdedbf6d6354471319e781c0dfef054c81fbc7cf83f338a4296c0cae11" [[package]] name = "windows_i686_msvc" @@ -7121,10 +7158,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" [[package]] -name = "windows_x86_64_gnu" -version = "0.48.5" +name = "windows_i686_msvc" +version = "0.53.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" +checksum = "581fee95406bb13382d2f65cd4a908ca7b1e4c2f1917f143ba16efe98a589b5d" [[package]] name = "windows_x86_64_gnu" @@ -7133,10 +7170,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" [[package]] -name = "windows_x86_64_gnullvm" -version = "0.48.5" +name = "windows_x86_64_gnu" +version = "0.53.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" +checksum = "2e55b5ac9ea33f2fc1716d1742db15574fd6fc8dadc51caab1c16a3d3b4190ba" [[package]] name = "windows_x86_64_gnullvm" @@ -7145,10 +7182,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" [[package]] -name = "windows_x86_64_msvc" -version = "0.48.5" +name = "windows_x86_64_gnullvm" +version = "0.53.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" +checksum = "0a6e035dd0599267ce1ee132e51c27dd29437f63325753051e71dd9e42406c57" [[package]] name = "windows_x86_64_msvc" @@ -7157,10 +7194,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] -name = "winnow" -version = "0.7.10" +name = "windows_x86_64_msvc" +version = "0.53.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c06928c8748d81b05c9be96aad92e1b6ff01833332f281e8cfca3be4b35fc9ec" +checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486" + +[[package]] +name = "winnow" +version = "0.7.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3edebf492c8125044983378ecb5766203ad3b4c2f7a922bd7dd207f6d443e95" dependencies = [ "memchr", ] @@ -7171,7 +7214,7 @@ version = "0.39.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.9.1", ] [[package]] @@ -7233,28 +7276,28 @@ checksum = "38da3c9736e16c5d3c8c597a9aaa5d1fa565d0532ae05e27c24aa62fb32c0ab6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.104", "synstructure", ] [[package]] name = "zerocopy" -version = "0.8.25" +version = "0.8.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1702d9583232ddb9174e01bb7c15a2ab8fb1bc6f227aa1233858c351a3ba0cb" +checksum = "1039dd0d3c310cf05de012d8a39ff557cb0d23087fd44cad61df08fc31907a2f" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.8.25" +version = "0.8.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28a6e20d751156648aa063f3800b706ee209a32c0b4d9f24be3d980b01be55ef" +checksum = "9ecf5b4cc5364572d7f4c329661bcc82724222973f2cab6f050a4e5c22f75181" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.104", ] [[package]] @@ -7274,7 +7317,7 @@ checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.104", "synstructure", ] @@ -7295,7 +7338,7 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.104", ] [[package]] @@ -7311,9 +7354,9 @@ dependencies = [ [[package]] name = "zerovec" -version = "0.11.2" +version = "0.11.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a05eb080e015ba39cc9e23bbe5e7fb04d5fb040350f99f34e338d5fdd294428" +checksum = "e7aa2bd55086f1ab526693ecbe444205da57e25f4489879da80635a46d90e73b" dependencies = [ "yoke", "zerofrom", @@ -7328,5 +7371,5 @@ checksum = "5b96237efa0c878c64bd89c436f661be4e46b2f3eff1ebb976f7ef2321d2f58f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.104", ] diff --git a/Cargo.toml b/Cargo.toml index c510625..e623723 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -163,11 +163,11 @@ cuprate-rpc-interface = { path = "rpc/interface", default-featur cuprate-zmq-types = { path = "zmq/types", default-features = false } # External dependencies -axum = { version = "0.7", default-features = false } +axum = { version = "0.8", default-features = false } anyhow = { version = "1", default-features = false } arc-swap = { version = "1", default-features = false } arrayvec = { version = "0.7", default-features = false } -arti-client = { version = "0.32", default-features = false } +arti-client = { version = "0.33", default-features = false } async-trait = { version = "0.1", default-features = false } bitflags = { version = "2", default-features = false } blake3 = { version = "1", default-features = false } @@ -177,15 +177,15 @@ bytes = { version = "1", default-features = false } cfg-if = { version = "1", default-features = false } clap = { version = "4", default-features = false } chrono = { version = "0.4", default-features = false } -crypto-bigint = { version = "0.5", default-features = false } +crypto-bigint = { version = "0.6", default-features = false } crossbeam = { version = "0.8", default-features = false } const_format = { version = "0.2", default-features = false } curve25519-dalek = { version = "4", default-features = false } dashmap = { version = "6", default-features = false } -dirs = { version = "5", default-features = false } +dirs = { version = "6", default-features = false } futures = { version = "0.3", default-features = false } hex = { version = "0.4", default-features = false } -hex-literal = { version = "0.4", default-features = false } +hex-literal = { version = "1", default-features = false } indexmap = { version = "2", default-features = false } monero-address = { git = "https://github.com/Cuprate/serai.git", rev = "e6ae8c2", default-features = false } monero-serai = { git = "https://github.com/Cuprate/serai.git", rev = "e6ae8c2", default-features = false } @@ -196,26 +196,27 @@ randomx-rs = { git = "https://github.com/Cuprate/randomx-rs.git", rev rand = { version = "0.8", default-features = false } rand_distr = { version = "0.4", default-features = false } rayon = { version = "1", default-features = false } +safelog = { version = "0.4", default-features = false } serde_bytes = { version = "0.11", default-features = false } serde_json = { version = "1", default-features = false } serde = { version = "1", default-features = false } -strum = { version = "0.26", default-features = false } -thiserror = { version = "1", default-features = false } +strum = { version = "0.27", default-features = false } +thiserror = { version = "2", default-features = false } thread_local = { version = "1", default-features = false } tokio-util = { version = "0.7", default-features = false } tokio-stream = { version = "0.1", default-features = false } tokio-socks = { git = "https://github.com/Cuprate/tokio-socks.git", rev = "8737caf", default-features = false } tokio = { version = "1", default-features = false } -tower = { git = "https://github.com/Cuprate/tower.git", rev = "6c7faf0", default-features = false } # +tower = { version = "0.5", default-features = false } tower-http = { version = "0.6", default-features = false } -toml = { version = "0.8", default-features = false } -toml_edit = { version = "0.22", default-features = false } -tor-cell = { version = "0.32", default-features = false } -tor-config-path = { version = "0.32", default-features = false } -tor-hsservice = { version = "0.32", default-features = false } -tor-persist = { version = "0.32", default-features = false } -tor-proto = { version = "0.32", default-features = false } -tor-rtcompat = { version = "0.32", default-features = false } +toml = { version = "0.9", default-features = false } +toml_edit = { version = "0.23", default-features = false } +tor-cell = { version = "0.33", default-features = false } +tor-config-path = { version = "0.33", default-features = false } +tor-hsservice = { version = "0.33", default-features = false } +tor-persist = { version = "0.33", default-features = false } +tor-proto = { version = "0.33", default-features = false } +tor-rtcompat = { version = "0.33", default-features = false } tracing-appender = { version = "0.2", default-features = false } tracing-subscriber = { version = "0.3", default-features = false } tracing = { version = "0.1", default-features = false } @@ -226,7 +227,7 @@ monero-simple-request-rpc = { git = "https://github.com/Cuprate/serai.git", rev tempfile = { version = "3" } pretty_assertions = { version = "1" } proptest = { version = "1" } -proptest-derive = { version = "0.5" } +proptest-derive = { version = "0.6" } tokio-test = { version = "0.4" } arbitrary = { version = "1" } diff --git a/binaries/cuprated/Cargo.toml b/binaries/cuprated/Cargo.toml index bb21ccc..8200e8e 100644 --- a/binaries/cuprated/Cargo.toml +++ b/binaries/cuprated/Cargo.toml @@ -70,6 +70,7 @@ randomx-rs = { workspace = true } rand = { workspace = true } rand_distr = { workspace = true } rayon = { workspace = true } +safelog = { workspace = true } serde_bytes = { workspace = true } serde_json = { workspace = true } serde = { workspace = true } @@ -79,8 +80,8 @@ thread_local = { workspace = true } tokio-util = { workspace = true, features = ["rt"] } tokio-stream = { workspace = true } tokio = { workspace = true } -toml = { workspace = true, features = ["parse", "display"]} -toml_edit = { workspace = true } +toml = { workspace = true, features = ["parse", "display", "serde"]} +toml_edit = { workspace = true, features = ["parse", "display"] } tor-hsservice = { workspace = true } tor-persist = { workspace = true } tor-rtcompat = { workspace = true } diff --git a/binaries/cuprated/src/config.rs b/binaries/cuprated/src/config.rs index feddff2..bbfa55e 100644 --- a/binaries/cuprated/src/config.rs +++ b/binaries/cuprated/src/config.rs @@ -10,6 +10,7 @@ use std::{ use arti_client::KeystoreSelector; use clap::Parser; +use safelog::DisplayRedacted; use serde::{Deserialize, Serialize}; use cuprate_consensus::ContextConfig; @@ -253,6 +254,7 @@ impl Config { .unwrap() .generate_identity_key(KeystoreSelector::Primary) .unwrap() + .display_unredacted() .to_string(); OnionAddr::new(&addr, self.p2p.tor_net.p2p_port).unwrap() diff --git a/p2p/p2p-core/src/client/handshaker.rs b/p2p/p2p-core/src/client/handshaker.rs index 010a19a..8e24f52 100644 --- a/p2p/p2p-core/src/client/handshaker.rs +++ b/p2p/p2p-core/src/client/handshaker.rs @@ -492,7 +492,6 @@ where }; let protocol_request_handler = protocol_request_svc_maker - .as_service() .ready() .await? .call(info.clone()) diff --git a/p2p/p2p-core/src/lib.rs b/p2p/p2p-core/src/lib.rs index 03fceb5..44c034a 100644 --- a/p2p/p2p-core/src/lib.rs +++ b/p2p/p2p-core/src/lib.rs @@ -280,11 +280,10 @@ impl ProtocolRequestHandler for T where } pub trait ProtocolRequestHandlerMaker: - tower::MakeService< + tower::Service< client::PeerInformation, - ProtocolRequest, - MakeError = tower::BoxError, - Service: ProtocolRequestHandler, + Error = tower::BoxError, + Response: ProtocolRequestHandler, Future: Send + 'static, > + Send + 'static @@ -292,11 +291,10 @@ pub trait ProtocolRequestHandlerMaker: } impl ProtocolRequestHandlerMaker for T where - T: tower::MakeService< + T: tower::Service< client::PeerInformation, - ProtocolRequest, - MakeError = tower::BoxError, - Service: ProtocolRequestHandler, + Error = tower::BoxError, + Response: ProtocolRequestHandler, Future: Send + 'static, > + Send + 'static diff --git a/p2p/p2p-transport/Cargo.toml b/p2p/p2p-transport/Cargo.toml index 65c7e2d..0c4345c 100644 --- a/p2p/p2p-transport/Cargo.toml +++ b/p2p/p2p-transport/Cargo.toml @@ -25,7 +25,5 @@ tor-hsservice = { workspace = true } tor-proto = { workspace = true } tor-rtcompat = { workspace = true } -tracing = { workspace = true } - [lints] workspace = true diff --git a/p2p/p2p-transport/src/arti.rs b/p2p/p2p-transport/src/arti.rs index aff90ce..52dd368 100644 --- a/p2p/p2p-transport/src/arti.rs +++ b/p2p/p2p-transport/src/arti.rs @@ -135,12 +135,6 @@ impl Transport for Arti { async fn incoming_connection_listener( config: Self::ServerConfig, ) -> Result { - tracing::info!( - "Listening for incoming Tor P2P connections on address: {}:{}", - config.onion_svc.onion_address().unwrap(), - config.port - ); - // Launch onion service #[expect(clippy::clone_on_ref_ptr)] let (svc, rdv_stream) = config diff --git a/rpc/interface/Cargo.toml b/rpc/interface/Cargo.toml index d856dfc..3aaf51c 100644 --- a/rpc/interface/Cargo.toml +++ b/rpc/interface/Cargo.toml @@ -28,7 +28,7 @@ futures = { workspace = true, optional = true } [dev-dependencies] cuprate-test-utils = { workspace = true } -axum = { version = "0.7.5", features = ["json", "tokio", "http2"] } +axum = { workspace = true, features = ["json", "tokio", "http2"] } serde_json = { workspace = true, features = ["std"] } tokio = { workspace = true, features = ["full"] } ureq = { version = "2.10.1", features = ["json"] } From 32b357245015ed399b3b3b6bc92243247195afe3 Mon Sep 17 00:00:00 2001 From: hinto-janai Date: Fri, 15 Aug 2025 14:38:58 +0000 Subject: [PATCH 15/16] lints: enable `wildcard_enum_match_arm` (#367) * apply * fixes * clippy * fmt * fix * review fix --- Cargo.toml | 2 +- .../cuprated/src/blockchain/chain_service.rs | 4 +++ consensus/rules/src/miner_tx.rs | 2 +- consensus/rules/src/transactions.rs | 7 ++--- consensus/rules/src/transactions/ring_ct.rs | 8 ++++- consensus/src/tests/mock_db.rs | 4 +++ consensus/src/transactions.rs | 2 +- consensus/src/transactions/contextual_data.rs | 2 +- consensus/tests/verify_correct_txs.rs | 6 +++- helper/src/fs.rs | 2 +- net/wire/src/network_address/epee_builder.rs | 2 +- net/wire/src/p2p.rs | 29 +++++++++++++++++-- p2p/p2p-core/src/client/handshaker.rs | 2 +- p2p/p2p-core/src/protocol/try_from.rs | 2 +- p2p/p2p-core/tests/fragmented_handshake.rs | 4 ++- p2p/p2p-transport/src/arti.rs | 1 + p2p/p2p/src/block_downloader/tests.rs | 2 +- pruning/src/lib.rs | 1 + rpc/json-rpc/src/id.rs | 4 +-- storage/database/src/resize.rs | 16 +++++----- types/types/src/block_complete_entry.rs | 11 ++++++- types/types/src/hard_fork.rs | 16 +++++++++- 22 files changed, 96 insertions(+), 33 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index e623723..34842aa 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -433,7 +433,7 @@ allow_attributes = "deny" undocumented_unsafe_blocks = "deny" # multiple_unsafe_ops_per_block = "deny" # single_char_lifetime_names = "deny" -# wildcard_enum_match_arm = "deny" +wildcard_enum_match_arm = "deny" [workspace.lints.rust] # Cold diff --git a/binaries/cuprated/src/blockchain/chain_service.rs b/binaries/cuprated/src/blockchain/chain_service.rs index 4716ad4..12b5073 100644 --- a/binaries/cuprated/src/blockchain/chain_service.rs +++ b/binaries/cuprated/src/blockchain/chain_service.rs @@ -26,6 +26,10 @@ impl Service> for ChainService { } fn call(&mut self, req: ChainSvcRequest) -> Self::Future { + #[expect( + clippy::wildcard_enum_match_arm, + reason = "other requests should be unreachable" + )] let map_res = |res: BlockchainResponse| match res { BlockchainResponse::CompactChainHistory { block_ids, diff --git a/consensus/rules/src/miner_tx.rs b/consensus/rules/src/miner_tx.rs index bb3b004..e0fb354 100644 --- a/consensus/rules/src/miner_tx.rs +++ b/consensus/rules/src/miner_tx.rs @@ -122,7 +122,7 @@ const fn check_time_lock(time_lock: &Timelock, chain_height: usize) -> Result<() Err(MinerTxError::InvalidLockTime) } } - _ => Err(MinerTxError::InvalidLockTime), + Timelock::None | Timelock::Time(_) => Err(MinerTxError::InvalidLockTime), } } diff --git a/consensus/rules/src/transactions.rs b/consensus/rules/src/transactions.rs index dc9ed11..7ab6731 100644 --- a/consensus/rules/src/transactions.rs +++ b/consensus/rules/src/transactions.rs @@ -1,5 +1,3 @@ -use std::cmp::Ordering; - use curve25519_dalek::EdwardsPoint; use monero_serai::{ io::decompress_point, @@ -400,9 +398,8 @@ fn check_inputs_sorted(inputs: &[Input], hf: HardFork) -> Result<(), Transaction if hf >= HardFork::V7 { for inps in inputs.windows(2) { - match get_ki(&inps[0])?.cmp(&get_ki(&inps[1])?) { - Ordering::Greater => (), - _ => return Err(TransactionError::InputsAreNotOrdered), + if get_ki(&inps[0])? <= get_ki(&inps[1])? { + return Err(TransactionError::InputsAreNotOrdered); } } } diff --git a/consensus/rules/src/transactions/ring_ct.rs b/consensus/rules/src/transactions/ring_ct.rs index 1d56e90..5a3dd57 100644 --- a/consensus/rules/src/transactions/ring_ct.rs +++ b/consensus/rules/src/transactions/ring_ct.rs @@ -55,7 +55,13 @@ fn check_rct_type(ty: RctType, hf: HardFork, tx_hash: &[u8; 32]) -> Result<(), R T::MlsagBulletproofsCompactAmount if GRANDFATHERED_TRANSACTIONS.contains(tx_hash) => Ok(()), T::ClsagBulletproof if hf >= F::V13 && hf < F::V16 => Ok(()), T::ClsagBulletproofPlus if hf >= F::V15 => Ok(()), - _ => Err(RingCTError::TypeNotAllowed), + + T::AggregateMlsagBorromean + | T::MlsagBorromean + | T::MlsagBulletproofs + | T::MlsagBulletproofsCompactAmount + | T::ClsagBulletproof + | T::ClsagBulletproofPlus => Err(RingCTError::TypeNotAllowed), } } diff --git a/consensus/src/tests/mock_db.rs b/consensus/src/tests/mock_db.rs index bf005d2..ce1f889 100644 --- a/consensus/src/tests/mock_db.rs +++ b/consensus/src/tests/mock_db.rs @@ -141,6 +141,10 @@ impl Service for DummyDatabase { let dummy_height = self.dummy_height; async move { + #[expect( + clippy::wildcard_enum_match_arm, + reason = "the context svc should not need other requests" + )] Ok(match req { BlockchainReadRequest::BlockExtendedHeader(id) => { let mut id = id; diff --git a/consensus/src/transactions.rs b/consensus/src/transactions.rs index 1cae186..a11c3bb 100644 --- a/consensus/src/transactions.rs +++ b/consensus/src/transactions.rs @@ -554,7 +554,7 @@ where .iter() .filter_map(|lock| match lock { Timelock::Time(time) => Some(time), - _ => None, + Timelock::None | Timelock::Block(_) => None, }) .min(); diff --git a/consensus/src/transactions/contextual_data.rs b/consensus/src/transactions/contextual_data.rs index 7667227..81047e8 100644 --- a/consensus/src/transactions/contextual_data.rs +++ b/consensus/src/transactions/contextual_data.rs @@ -91,7 +91,7 @@ pub fn new_ring_member_info( .iter() .filter_map(|out| match out.time_lock { Timelock::None => None, - lock => Some(lock), + Timelock::Block(_) | Timelock::Time(_) => Some(out.time_lock), }) .collect::>() }) diff --git a/consensus/tests/verify_correct_txs.rs b/consensus/tests/verify_correct_txs.rs index 9ac606e..ffa1321 100644 --- a/consensus/tests/verify_correct_txs.rs +++ b/consensus/tests/verify_correct_txs.rs @@ -26,6 +26,10 @@ use cuprate_test_utils::data::TX_E2D393; fn dummy_database(outputs: BTreeMap) -> impl Database + Clone { let outputs = Arc::new(outputs); + #[expect( + clippy::wildcard_enum_match_arm, + reason = "Other database requests are not needed for this test" + )] service_fn(move |req: BlockchainReadRequest| { ready(Ok(match req { BlockchainReadRequest::NumberOutputsWithAmount(_) => { @@ -48,7 +52,7 @@ fn dummy_database(outputs: BTreeMap) -> impl Database + Clon BlockchainResponse::Outputs(ret) } BlockchainReadRequest::KeyImagesSpent(_) => BlockchainResponse::KeyImagesSpent(false), - _ => panic!("Database request not needed for this test"), + _ => panic!(), })) }) } diff --git a/helper/src/fs.rs b/helper/src/fs.rs index b926732..8817993 100644 --- a/helper/src/fs.rs +++ b/helper/src/fs.rs @@ -166,7 +166,7 @@ impl_path_lazylock! { fn path_with_network(path: &Path, network: Network) -> PathBuf { match network { Network::Mainnet => path.to_path_buf(), - network => path.join(network.to_string()), + Network::Testnet | Network::Stagenet => path.join(network.to_string()), } } diff --git a/net/wire/src/network_address/epee_builder.rs b/net/wire/src/network_address/epee_builder.rs index 1c20649..1c51d47 100644 --- a/net/wire/src/network_address/epee_builder.rs +++ b/net/wire/src/network_address/epee_builder.rs @@ -170,7 +170,7 @@ impl AllFieldsNetworkAddress { NetworkAddress::from(OnionAddr::new(self.host?.as_str(), self.port?).ok()?) } // Invalid - _ => return None, + AddressType::Invalid | AddressType::I2p => return None, }) } } diff --git a/net/wire/src/p2p.rs b/net/wire/src/p2p.rs index 09a3a26..c5b14d6 100644 --- a/net/wire/src/p2p.rs +++ b/net/wire/src/p2p.rs @@ -224,7 +224,10 @@ impl ProtocolMessage { decode_message(ProtocolMessage::FluffyMissingTransactionsRequest, buf)? } C::GetTxPoolCompliment => decode_message(ProtocolMessage::GetTxPoolCompliment, buf)?, - _ => return Err(BucketError::UnknownCommand), + + C::Handshake | C::TimedSync | C::Ping | C::SupportFlags | C::Unknown(_) => { + return Err(BucketError::UnknownCommand); + } }) } @@ -296,7 +299,17 @@ impl AdminRequestMessage { Self::SupportFlags } - _ => return Err(BucketError::UnknownCommand), + + C::NewBlock + | C::NewTransactions + | C::GetObjectsRequest + | C::GetObjectsResponse + | C::ChainRequest + | C::ChainResponse + | C::NewFluffyBlock + | C::FluffyMissingTxsRequest + | C::GetTxPoolCompliment + | C::Unknown(_) => return Err(BucketError::UnknownCommand), }) } @@ -343,7 +356,17 @@ impl AdminResponseMessage { C::TimedSync => decode_message(AdminResponseMessage::TimedSync, buf)?, C::Ping => decode_message(AdminResponseMessage::Ping, buf)?, C::SupportFlags => decode_message(AdminResponseMessage::SupportFlags, buf)?, - _ => return Err(BucketError::UnknownCommand), + + C::NewBlock + | C::NewTransactions + | C::GetObjectsRequest + | C::GetObjectsResponse + | C::ChainRequest + | C::ChainResponse + | C::NewFluffyBlock + | C::FluffyMissingTxsRequest + | C::GetTxPoolCompliment + | C::Unknown(_) => return Err(BucketError::UnknownCommand), }) } diff --git a/p2p/p2p-core/src/client/handshaker.rs b/p2p/p2p-core/src/client/handshaker.rs index 8e24f52..f040bf1 100644 --- a/p2p/p2p-core/src/client/handshaker.rs +++ b/p2p/p2p-core/src/client/handshaker.rs @@ -694,7 +694,7 @@ where allow_ping = false; continue; } - _ => { + AdminRequestMessage::Handshake(_) | AdminRequestMessage::TimedSync(_) => { return Err(HandshakeError::PeerSentInvalidMessage( "Peer sent an admin request before responding to the handshake", )); diff --git a/p2p/p2p-core/src/protocol/try_from.rs b/p2p/p2p-core/src/protocol/try_from.rs index 90c8cec..ac655e8 100644 --- a/p2p/p2p-core/src/protocol/try_from.rs +++ b/p2p/p2p-core/src/protocol/try_from.rs @@ -131,7 +131,7 @@ impl TryFrom for BroadcastMessage { PeerRequest::Protocol(ProtocolRequest::NewFluffyBlock(block)) => { Ok(Self::NewFluffyBlock(block)) } - _ => Err(MessageConversionError), + PeerRequest::Admin(_) | PeerRequest::Protocol(_) => Err(MessageConversionError), } } } diff --git a/p2p/p2p-core/tests/fragmented_handshake.rs b/p2p/p2p-core/tests/fragmented_handshake.rs index 61798eb..948570e 100644 --- a/p2p/p2p-core/tests/fragmented_handshake.rs +++ b/p2p/p2p-core/tests/fragmented_handshake.rs @@ -141,7 +141,9 @@ impl Encoder> for FragmentCodec { self.0.encode(frag.into(), dst)?; } } - _ => unreachable!("Handshakes should only send bucket bodys"), + LevinMessage::Bucket(_) | LevinMessage::Dummy(_) => { + unreachable!("Handshakes should only send bucket bodys"); + } } Ok(()) } diff --git a/p2p/p2p-transport/src/arti.rs b/p2p/p2p-transport/src/arti.rs index 52dd368..b1efae2 100644 --- a/p2p/p2p-transport/src/arti.rs +++ b/p2p/p2p-transport/src/arti.rs @@ -148,6 +148,7 @@ impl Transport for Arti { .unwrap(); // Accept all rendez-vous and await correct stream request + #[expect(clippy::wildcard_enum_match_arm)] let req_stream = handle_rend_requests(rdv_stream).then(move |sreq| async move { match sreq.request() { // As specified in: diff --git a/p2p/p2p/src/block_downloader/tests.rs b/p2p/p2p/src/block_downloader/tests.rs index ee8be5b..098c5ee 100644 --- a/p2p/p2p/src/block_downloader/tests.rs +++ b/p2p/p2p/src/block_downloader/tests.rs @@ -243,7 +243,7 @@ fn mock_block_downloader_client(blockchain: Arc) -> Client panic!(), + PeerRequest::Admin(_) | PeerRequest::Protocol(_) => panic!(), } } .boxed() diff --git a/pruning/src/lib.rs b/pruning/src/lib.rs index e49aedb..ec109e6 100644 --- a/pruning/src/lib.rs +++ b/pruning/src/lib.rs @@ -231,6 +231,7 @@ impl Ord for DecompressedPruningSeed { fn cmp(&self, other: &Self) -> Ordering { // Compare the `log_stripes` first so peers which store more blocks are greater than peers // storing less. + #[expect(clippy::wildcard_enum_match_arm)] match self.log_stripes.cmp(&other.log_stripes) { Ordering::Equal => self.stripe.cmp(&other.stripe), ord => ord, diff --git a/rpc/json-rpc/src/id.rs b/rpc/json-rpc/src/id.rs index db894ad..8830ba1 100644 --- a/rpc/json-rpc/src/id.rs +++ b/rpc/json-rpc/src/id.rs @@ -88,7 +88,7 @@ impl Id { pub const fn as_u64(&self) -> Option { match self { Self::Num(n) => Some(*n), - _ => None, + Self::Null | Self::Str(_) => None, } } @@ -104,7 +104,7 @@ impl Id { pub fn as_str(&self) -> Option<&str> { match self { Self::Str(s) => Some(s.as_ref()), - _ => None, + Self::Null | Self::Num(_) => None, } } diff --git a/storage/database/src/resize.rs b/storage/database/src/resize.rs index b217478..8d7c6c4 100644 --- a/storage/database/src/resize.rs +++ b/storage/database/src/resize.rs @@ -246,16 +246,14 @@ pub fn fixed_bytes(current_size_bytes: usize, add_bytes: usize) -> NonZeroUsize /// ``` pub fn percent(current_size_bytes: usize, percent: f32) -> NonZeroUsize { // Guard against bad floats. - use std::num::FpCategory; - let percent = match percent.classify() { - FpCategory::Normal => { - if percent <= 1.0 { - 1.0 - } else { - percent - } + let percent = if percent.classify() == std::num::FpCategory::Normal { + if percent <= 1.0 { + 1.0 + } else { + percent } - _ => 1.0, + } else { + 1.0 }; let page_size = *PAGE_SIZE; diff --git a/types/types/src/block_complete_entry.rs b/types/types/src/block_complete_entry.rs index 3060cbc..1b1a81c 100644 --- a/types/types/src/block_complete_entry.rs +++ b/types/types/src/block_complete_entry.rs @@ -96,7 +96,16 @@ impl TransactionBlobs { match marker.inner_marker { InnerMarker::Object => Ok(Self::Pruned(Vec::read(b, &marker)?)), InnerMarker::String => Ok(Self::Normal(Vec::read(b, &marker)?)), - _ => Err(cuprate_epee_encoding::Error::Value( + InnerMarker::I64 + | InnerMarker::I32 + | InnerMarker::I16 + | InnerMarker::I8 + | InnerMarker::U64 + | InnerMarker::U32 + | InnerMarker::U16 + | InnerMarker::U8 + | InnerMarker::F64 + | InnerMarker::Bool => Err(cuprate_epee_encoding::Error::Value( "Invalid marker for tx blobs".to_string(), )), } diff --git a/types/types/src/hard_fork.rs b/types/types/src/hard_fork.rs index 07770e6..769cd90 100644 --- a/types/types/src/hard_fork.rs +++ b/types/types/src/hard_fork.rs @@ -181,7 +181,21 @@ impl HardFork { pub const fn block_time(self) -> Duration { match self { Self::V1 => BLOCK_TIME_V1, - _ => BLOCK_TIME_V2, + Self::V2 + | Self::V3 + | Self::V4 + | Self::V5 + | Self::V6 + | Self::V7 + | Self::V8 + | Self::V9 + | Self::V10 + | Self::V11 + | Self::V12 + | Self::V13 + | Self::V14 + | Self::V15 + | Self::V16 => BLOCK_TIME_V2, } } From 807bfafb35dd3e0ea8a6a76000e8d1cb95afb675 Mon Sep 17 00:00:00 2001 From: Boog900 Date: Fri, 15 Aug 2025 15:41:23 +0100 Subject: [PATCH 16/16] cuprated: add txpool manager (#483) * add txpool manager * add version check for tx-pool * add docs * fix merge * fix test * fix merge * fix cargo hack * clean up imports * fix typo * add small buffer to rebroadcasts * review fixes * small clean up * fmt * fix merge * review fixes --- Cargo.lock | 1 + binaries/cuprated/src/blockchain/manager.rs | 9 +- .../src/blockchain/manager/handler.rs | 7 +- .../cuprated/src/blockchain/manager/tests.rs | 13 +- binaries/cuprated/src/config.rs | 2 +- binaries/cuprated/src/config/storage.rs | 11 +- binaries/cuprated/src/main.rs | 8 +- binaries/cuprated/src/p2p/request_handler.rs | 25 +- .../cuprated/src/rpc/handlers/json_rpc.rs | 9 +- binaries/cuprated/src/txpool.rs | 2 + binaries/cuprated/src/txpool/dandelion.rs | 12 +- .../cuprated/src/txpool/dandelion/tx_store.rs | 29 +- binaries/cuprated/src/txpool/incoming_tx.rs | 138 ++--- binaries/cuprated/src/txpool/manager.rs | 497 ++++++++++++++++++ p2p/dandelion-tower/Cargo.toml | 2 +- p2p/dandelion-tower/src/pool/manager.rs | 45 +- p2p/dandelion-tower/src/pool/mod.rs | 13 +- storage/txpool/Cargo.toml | 3 +- storage/txpool/README.md | 2 +- storage/txpool/src/free.rs | 77 ++- storage/txpool/src/lib.rs | 2 +- storage/txpool/src/ops.rs | 2 +- storage/txpool/src/ops/tx_write.rs | 2 + storage/txpool/src/service.rs | 2 +- storage/txpool/src/service/free.rs | 4 +- storage/txpool/src/service/interface.rs | 6 +- storage/txpool/src/service/read.rs | 27 +- storage/txpool/src/service/write.rs | 14 +- storage/txpool/src/tables.rs | 6 +- storage/txpool/src/tx.rs | 8 +- storage/txpool/src/types.rs | 8 + 31 files changed, 804 insertions(+), 182 deletions(-) create mode 100644 binaries/cuprated/src/txpool/manager.rs diff --git a/Cargo.lock b/Cargo.lock index 4fb2ed2..1b4560b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1543,6 +1543,7 @@ dependencies = [ "thiserror 2.0.12", "tokio", "tower", + "tracing", ] [[package]] diff --git a/binaries/cuprated/src/blockchain/manager.rs b/binaries/cuprated/src/blockchain/manager.rs index 4e02915..d0637b3 100644 --- a/binaries/cuprated/src/blockchain/manager.rs +++ b/binaries/cuprated/src/blockchain/manager.rs @@ -28,6 +28,7 @@ use crate::{ types::ConsensusBlockchainReadHandle, }, constants::PANIC_CRITICAL_SERVICE_ERROR, + txpool::TxpoolManagerHandle, }; mod commands; @@ -46,7 +47,7 @@ pub async fn init_blockchain_manager( clearnet_interface: NetworkInterface, blockchain_write_handle: BlockchainWriteHandle, blockchain_read_handle: BlockchainReadHandle, - txpool_write_handle: TxpoolWriteHandle, + txpool_manager_handle: TxpoolManagerHandle, mut blockchain_context_service: BlockchainContextService, block_downloader_config: BlockDownloaderConfig, ) { @@ -72,7 +73,7 @@ pub async fn init_blockchain_manager( blockchain_read_handle, BoxError::from, ), - txpool_write_handle, + txpool_manager_handle, blockchain_context_service, stop_current_block_downloader, broadcast_svc: clearnet_interface.broadcast_svc(), @@ -93,8 +94,8 @@ pub struct BlockchainManager { blockchain_write_handle: BlockchainWriteHandle, /// A [`BlockchainReadHandle`]. blockchain_read_handle: ConsensusBlockchainReadHandle, - /// A [`TxpoolWriteHandle`]. - txpool_write_handle: TxpoolWriteHandle, + + txpool_manager_handle: TxpoolManagerHandle, /// The blockchain context cache, this caches the current state of the blockchain to quickly calculate/retrieve /// values without needing to go to a [`BlockchainReadHandle`]. blockchain_context_service: BlockchainContextService, diff --git a/binaries/cuprated/src/blockchain/manager/handler.rs b/binaries/cuprated/src/blockchain/manager/handler.rs index c7a39df..5f88707 100644 --- a/binaries/cuprated/src/blockchain/manager/handler.rs +++ b/binaries/cuprated/src/blockchain/manager/handler.rs @@ -619,11 +619,8 @@ impl super::BlockchainManager { .await .expect(PANIC_CRITICAL_SERVICE_ERROR); - self.txpool_write_handle - .ready() - .await - .expect(PANIC_CRITICAL_SERVICE_ERROR) - .call(TxpoolWriteRequest::NewBlock { spent_key_images }) + self.txpool_manager_handle + .new_block(spent_key_images) .await .expect(PANIC_CRITICAL_SERVICE_ERROR); } diff --git a/binaries/cuprated/src/blockchain/manager/tests.rs b/binaries/cuprated/src/blockchain/manager/tests.rs index 7efa313..1363a3b 100644 --- a/binaries/cuprated/src/blockchain/manager/tests.rs +++ b/binaries/cuprated/src/blockchain/manager/tests.rs @@ -14,9 +14,12 @@ use cuprate_p2p::{block_downloader::BlockBatch, BroadcastSvc}; use cuprate_p2p_core::handles::HandleBuilder; use cuprate_types::{CachedVerificationState, TransactionVerificationData, TxVersion}; -use crate::blockchain::{ - check_add_genesis, manager::BlockchainManager, manager::BlockchainManagerCommand, - ConsensusBlockchainReadHandle, +use crate::{ + blockchain::{ + check_add_genesis, manager::BlockchainManager, manager::BlockchainManagerCommand, + ConsensusBlockchainReadHandle, + }, + txpool::TxpoolManagerHandle, }; async fn mock_manager(data_dir: PathBuf) -> BlockchainManager { @@ -30,7 +33,7 @@ async fn mock_manager(data_dir: PathBuf) -> BlockchainManager { let (mut blockchain_read_handle, mut blockchain_write_handle, _) = cuprate_blockchain::service::init(blockchain_config).unwrap(); let (txpool_read_handle, txpool_write_handle, _) = - cuprate_txpool::service::init(txpool_config).unwrap(); + cuprate_txpool::service::init(&txpool_config).unwrap(); check_add_genesis( &mut blockchain_read_handle, @@ -56,7 +59,7 @@ async fn mock_manager(data_dir: PathBuf) -> BlockchainManager { BlockchainManager { blockchain_write_handle, blockchain_read_handle, - txpool_write_handle, + txpool_manager_handle: TxpoolManagerHandle::mock(), blockchain_context_service, stop_current_block_downloader: Arc::new(Default::default()), broadcast_svc: BroadcastSvc::mock(), diff --git a/binaries/cuprated/src/config.rs b/binaries/cuprated/src/config.rs index bbfa55e..a60e6e9 100644 --- a/binaries/cuprated/src/config.rs +++ b/binaries/cuprated/src/config.rs @@ -45,7 +45,7 @@ use fs::FileSystemConfig; use p2p::P2PConfig; use rayon::RayonConfig; pub use rpc::RpcConfig; -use storage::StorageConfig; +pub use storage::{StorageConfig, TxpoolConfig}; use tokio::TokioConfig; use tor::TorConfig; use tracing_config::TracingConfig; diff --git a/binaries/cuprated/src/config/storage.rs b/binaries/cuprated/src/config/storage.rs index 7754045..f86563e 100644 --- a/binaries/cuprated/src/config/storage.rs +++ b/binaries/cuprated/src/config/storage.rs @@ -62,7 +62,7 @@ config_struct! { pub struct BlockchainConfig { } /// The tx-pool config. - #[derive(Debug, Deserialize, Serialize, PartialEq, Eq)] + #[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq)] #[serde(deny_unknown_fields, default)] pub struct TxpoolConfig { /// The maximum size of the tx-pool. @@ -71,6 +71,14 @@ config_struct! { /// Valid values | >= 0 /// Examples | 100_000_000, 50_000_000 pub max_txpool_byte_size: usize, + + /// The maximum age of transactions in the pool in seconds. + /// Transactions will be dropped after this time is reached. + /// + /// Type | Number + /// Valid values | >= 0 + /// Examples | 100_000_000, 50_000_000 + pub maximum_age_secs: u64, } } @@ -79,6 +87,7 @@ impl Default for TxpoolConfig { Self { sync_mode: SyncMode::default(), max_txpool_byte_size: 100_000_000, + maximum_age_secs: 60 * 60 * 24, } } } diff --git a/binaries/cuprated/src/main.rs b/binaries/cuprated/src/main.rs index 5bf6feb..8f9f27e 100644 --- a/binaries/cuprated/src/main.rs +++ b/binaries/cuprated/src/main.rs @@ -92,7 +92,7 @@ fn main() { .expect(DATABASE_CORRUPT_MSG); let (txpool_read_handle, txpool_write_handle, _) = - cuprate_txpool::service::init_with_pool(config.txpool_config(), db_thread_pool) + cuprate_txpool::service::init_with_pool(&config.txpool_config(), db_thread_pool) .inspect_err(|e| error!("Txpool database error: {e}")) .expect(DATABASE_CORRUPT_MSG); @@ -137,13 +137,15 @@ fn main() { // Create the incoming tx handler service. let tx_handler = IncomingTxHandler::init( + config.storage.txpool.clone(), network_interfaces.clearnet_network_interface.clone(), network_interfaces.tor_network_interface, txpool_write_handle.clone(), txpool_read_handle.clone(), context_svc.clone(), blockchain_read_handle.clone(), - ); + ) + .await; // Send tx handler sender to all network zones for zone in tx_handler_subscribers { @@ -157,7 +159,7 @@ fn main() { network_interfaces.clearnet_network_interface, blockchain_write_handle, blockchain_read_handle.clone(), - txpool_write_handle.clone(), + tx_handler.txpool_manager.clone(), context_svc.clone(), config.block_downloader_config(), ) diff --git a/binaries/cuprated/src/p2p/request_handler.rs b/binaries/cuprated/src/p2p/request_handler.rs index 79f14db..fdb5ca8 100644 --- a/binaries/cuprated/src/p2p/request_handler.rs +++ b/binaries/cuprated/src/p2p/request_handler.rs @@ -14,6 +14,7 @@ use monero_serai::{block::Block, transaction::Transaction}; use tokio::sync::{broadcast, oneshot, watch}; use tokio_stream::wrappers::WatchStream; use tower::{Service, ServiceExt}; +use tracing::instrument; use cuprate_blockchain::service::BlockchainReadHandle; use cuprate_consensus::{ @@ -22,10 +23,9 @@ use cuprate_consensus::{ }; use cuprate_dandelion_tower::TxState; use cuprate_fixed_bytes::ByteArrayVec; -use cuprate_helper::cast::u64_to_usize; use cuprate_helper::{ asynch::rayon_spawn_async, - cast::usize_to_u64, + cast::{u64_to_usize, usize_to_u64}, map::{combine_low_high_bits_to_u128, split_u128_into_low_high_bits}, }; use cuprate_p2p::constants::{ @@ -363,6 +363,7 @@ async fn new_fluffy_block( } /// [`ProtocolRequest::NewTransactions`] +#[instrument(level = "debug", skip_all, fields(txs = request.txs.len(), stem = !request.dandelionpp_fluff))] async fn new_transactions( peer_information: PeerInformation, request: NewTransactions, @@ -373,16 +374,22 @@ where A: NetZoneAddress, InternalPeerID: Into, { + tracing::debug!("handling new transactions"); + let context = blockchain_context_service.blockchain_context(); // If we are more than 2 blocks behind the peer then ignore the txs - we are probably still syncing. - if usize_to_u64(context.chain_height + 2) - < peer_information - .core_sync_data - .lock() - .unwrap() - .current_height - { + let peer_height = peer_information + .core_sync_data + .lock() + .unwrap() + .current_height; + if usize_to_u64(context.chain_height + 2) < peer_height { + tracing::debug!( + our_height = context.chain_height, + peer_height, + "we are too far behind peer, ignoring txs." + ); return Ok(ProtocolResponse::NA); } diff --git a/binaries/cuprated/src/rpc/handlers/json_rpc.rs b/binaries/cuprated/src/rpc/handlers/json_rpc.rs index ffcbfda..e05470f 100644 --- a/binaries/cuprated/src/rpc/handlers/json_rpc.rs +++ b/binaries/cuprated/src/rpc/handlers/json_rpc.rs @@ -23,6 +23,7 @@ use cuprate_helper::{ cast::{u32_to_usize, u64_to_usize, usize_to_u64}, fmt::HexPrefix, map::split_u128_into_low_high_bits, + time::current_unix_timestamp, }; use cuprate_hex::{Hex, HexVec}; use cuprate_p2p_core::{client::handshaker::builder::DummyAddressBook, ClearNet, Network}; @@ -923,13 +924,15 @@ async fn get_transaction_pool_backlog( mut state: CupratedRpcHandler, _: GetTransactionPoolBacklogRequest, ) -> Result { + let now = current_unix_timestamp(); + let backlog = txpool::backlog(&mut state.txpool_read) .await? .into_iter() .map(|entry| TxBacklogEntry { - weight: entry.weight, + weight: usize_to_u64(entry.weight), fee: entry.fee, - time_in_pool: entry.time_in_pool.as_secs(), + time_in_pool: now - entry.received_at, }) .collect(); @@ -968,7 +971,7 @@ async fn get_miner_data( .into_iter() .map(|entry| GetMinerDataTxBacklogEntry { id: Hex(entry.id), - weight: entry.weight, + weight: usize_to_u64(entry.weight), fee: entry.fee, }) .collect(); diff --git a/binaries/cuprated/src/txpool.rs b/binaries/cuprated/src/txpool.rs index eccf2ff..8a99625 100644 --- a/binaries/cuprated/src/txpool.rs +++ b/binaries/cuprated/src/txpool.rs @@ -8,8 +8,10 @@ use cuprate_txpool::service::{TxpoolReadHandle, TxpoolWriteHandle}; mod dandelion; mod incoming_tx; +mod manager; mod relay_rules; mod txs_being_handled; pub use incoming_tx::{IncomingTxError, IncomingTxHandler, IncomingTxs}; +pub use manager::TxpoolManagerHandle; pub use relay_rules::RelayRuleError; diff --git a/binaries/cuprated/src/txpool/dandelion.rs b/binaries/cuprated/src/txpool/dandelion.rs index 14473e9..17a2c1b 100644 --- a/binaries/cuprated/src/txpool/dandelion.rs +++ b/binaries/cuprated/src/txpool/dandelion.rs @@ -6,6 +6,9 @@ use std::{ use futures::{future::BoxFuture, FutureExt, TryFutureExt}; use tower::{Service, ServiceExt}; +use tokio::sync::mpsc; +use tokio_util::sync::PollSender; + use cuprate_dandelion_tower::{ pool::DandelionPoolService, traits::StemRequest, DandelionConfig, DandelionRouteReq, DandelionRouter, DandelionRouterError, Graph, State, TxState, @@ -25,6 +28,7 @@ mod stem_service; mod tx_store; pub use anon_net_service::AnonTxService; +pub use diffuse_service::DiffuseService; /// The configuration used for [`cuprate_dandelion_tower`]. /// @@ -39,7 +43,7 @@ const DANDELION_CONFIG: DandelionConfig = DandelionConfig { /// A [`DandelionRouter`] with all generic types defined. pub(super) type ConcreteDandelionRouter = DandelionRouter< stem_service::OutboundPeerStream, - diffuse_service::DiffuseService, + DiffuseService, CrossNetworkInternalPeerId, stem_service::StemPeerService, DandelionTx, @@ -106,7 +110,7 @@ impl Service> for Mai pub fn start_dandelion_pool_manager( router: MainDandelionRouter, txpool_read_handle: TxpoolReadHandle, - txpool_write_handle: TxpoolWriteHandle, + promote_tx: mpsc::Sender<[u8; 32]>, ) -> DandelionPoolService { cuprate_dandelion_tower::pool::start_dandelion_pool_manager( // TODO: make this constant configurable? @@ -114,7 +118,7 @@ pub fn start_dandelion_pool_manager( router, tx_store::TxStoreService { txpool_read_handle, - txpool_write_handle, + promote_tx: PollSender::new(promote_tx), }, DANDELION_CONFIG, ) @@ -128,7 +132,7 @@ where InternalPeerID: Into, { DandelionRouter::new( - diffuse_service::DiffuseService { + DiffuseService { clear_net_broadcast_service: network_interface.broadcast_svc(), }, stem_service::OutboundPeerStream::::new(network_interface), diff --git a/binaries/cuprated/src/txpool/dandelion/tx_store.rs b/binaries/cuprated/src/txpool/dandelion/tx_store.rs index b890ffd..01f8517 100644 --- a/binaries/cuprated/src/txpool/dandelion/tx_store.rs +++ b/binaries/cuprated/src/txpool/dandelion/tx_store.rs @@ -1,7 +1,12 @@ -use std::task::{Context, Poll}; +use std::{ + future::ready, + task::{Context, Poll}, +}; use bytes::Bytes; use futures::{future::BoxFuture, FutureExt}; +use tokio::sync::mpsc; +use tokio_util::sync::PollSender; use tower::{Service, ServiceExt}; use cuprate_dandelion_tower::{ @@ -21,7 +26,7 @@ use super::{DandelionTx, TxId}; /// This is just mapping the interface [`cuprate_dandelion_tower`] wants to what [`cuprate_txpool`] provides. pub struct TxStoreService { pub txpool_read_handle: TxpoolReadHandle, - pub txpool_write_handle: TxpoolWriteHandle, + pub promote_tx: PollSender<[u8; 32]>, } impl Service> for TxStoreService { @@ -29,8 +34,8 @@ impl Service> for TxStoreService { type Error = tower::BoxError; type Future = BoxFuture<'static, Result>; - fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll> { - Poll::Ready(Ok(())) + fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll> { + self.promote_tx.poll_reserve(cx).map_err(Into::into) } fn call(&mut self, req: TxStoreRequest) -> Self::Future { @@ -60,15 +65,13 @@ impl Service> for TxStoreService { Ok(_) => unreachable!(), }) .boxed(), - TxStoreRequest::Promote(tx_id) => self - .txpool_write_handle - .clone() - .oneshot(TxpoolWriteRequest::Promote(tx_id)) - .map(|res| match res { - Ok(_) | Err(RuntimeError::KeyNotFound) => Ok(TxStoreResponse::Ok), - Err(e) => Err(e.into()), - }) - .boxed(), + TxStoreRequest::Promote(tx_id) => ready( + self.promote_tx + .send_item(tx_id) + .map_err(Into::into) + .map(|()| TxStoreResponse::Ok), + ) + .boxed(), } } } diff --git a/binaries/cuprated/src/txpool/incoming_tx.rs b/binaries/cuprated/src/txpool/incoming_tx.rs index e1f81c4..069e6d3 100644 --- a/binaries/cuprated/src/txpool/incoming_tx.rs +++ b/binaries/cuprated/src/txpool/incoming_tx.rs @@ -7,13 +7,15 @@ use std::{ use bytes::Bytes; use futures::{future::BoxFuture, FutureExt}; use monero_serai::transaction::Transaction; +use tokio::sync::mpsc; use tower::{BoxError, Service, ServiceExt}; +use tracing::instrument; use cuprate_blockchain::service::BlockchainReadHandle; -use cuprate_consensus::transactions::{start_tx_verification, PrepTransactions}; use cuprate_consensus::{ - transactions::new_tx_verification_data, BlockChainContextRequest, BlockChainContextResponse, - BlockchainContextService, ExtendedConsensusError, + transactions::{new_tx_verification_data, start_tx_verification, PrepTransactions}, + BlockChainContextRequest, BlockChainContextResponse, BlockchainContextService, + ExtendedConsensusError, }; use cuprate_dandelion_tower::{ pool::{DandelionPoolService, IncomingTxBuilder}, @@ -35,11 +37,15 @@ use cuprate_types::TransactionVerificationData; use crate::{ blockchain::ConsensusBlockchainReadHandle, + config::TxpoolConfig, constants::PANIC_CRITICAL_SERVICE_ERROR, p2p::CrossNetworkInternalPeerId, signals::REORG_LOCK, txpool::{ - dandelion::{self, AnonTxService, ConcreteDandelionRouter, MainDandelionRouter}, + dandelion::{ + self, AnonTxService, ConcreteDandelionRouter, DiffuseService, MainDandelionRouter, + }, + manager::{start_txpool_manager, TxpoolManagerHandle}, relay_rules::{check_tx_relay_rules, RelayRuleError}, txs_being_handled::{TxsBeingHandled, TxsBeingHandledLocally}, }, @@ -92,8 +98,7 @@ pub struct IncomingTxHandler { /// The dandelion txpool manager. pub(super) dandelion_pool_manager: DandelionPoolService, - /// The txpool write handle. - pub(super) txpool_write_handle: TxpoolWriteHandle, + pub txpool_manager: TxpoolManagerHandle, /// The txpool read handle. pub(super) txpool_read_handle: TxpoolReadHandle, /// The blockchain read handle. @@ -103,7 +108,9 @@ pub struct IncomingTxHandler { impl IncomingTxHandler { /// Initialize the [`IncomingTxHandler`]. #[expect(clippy::significant_drop_tightening)] - pub fn init( + #[instrument(level = "info", skip_all, name = "start_txpool")] + pub async fn init( + txpool_config: TxpoolConfig, clear_net: NetworkInterface, tor_net: Option>, txpool_write_handle: TxpoolWriteHandle, @@ -111,22 +118,37 @@ impl IncomingTxHandler { blockchain_context_cache: BlockchainContextService, blockchain_read_handle: BlockchainReadHandle, ) -> Self { + let diffuse_service = DiffuseService { + clear_net_broadcast_service: clear_net.broadcast_svc(), + }; let clearnet_router = dandelion::dandelion_router(clear_net); let tor_router = tor_net.map(AnonTxService::new); let dandelion_router = MainDandelionRouter::new(clearnet_router, tor_router); + let (promote_tx, promote_rx) = mpsc::channel(25); + let dandelion_pool_manager = dandelion::start_dandelion_pool_manager( dandelion_router, txpool_read_handle.clone(), - txpool_write_handle.clone(), + promote_tx, ); + let txpool_manager = start_txpool_manager( + txpool_write_handle, + txpool_read_handle.clone(), + promote_rx, + diffuse_service, + dandelion_pool_manager.clone(), + txpool_config, + ) + .await; + Self { txs_being_handled: TxsBeingHandled::new(), blockchain_context_cache, dandelion_pool_manager, - txpool_write_handle, + txpool_manager, txpool_read_handle, blockchain_read_handle: ConsensusBlockchainReadHandle::new( blockchain_read_handle, @@ -151,8 +173,8 @@ impl Service for IncomingTxHandler { self.txs_being_handled.clone(), self.blockchain_context_cache.clone(), self.blockchain_read_handle.clone(), - self.txpool_write_handle.clone(), self.txpool_read_handle.clone(), + self.txpool_manager.clone(), self.dandelion_pool_manager.clone(), ) .boxed() @@ -170,8 +192,8 @@ async fn handle_incoming_txs( txs_being_handled: TxsBeingHandled, mut blockchain_context_cache: BlockchainContextService, blockchain_read_handle: ConsensusBlockchainReadHandle, - mut txpool_write_handle: TxpoolWriteHandle, mut txpool_read_handle: TxpoolReadHandle, + mut txpool_manager_handle: TxpoolManagerHandle, mut dandelion_pool_manager: DandelionPoolService, ) -> Result<(), IncomingTxError> { let _reorg_guard = REORG_LOCK.read().await; @@ -209,28 +231,33 @@ async fn handle_incoming_txs( return Err(IncomingTxError::RelayRule(e)); } - if !do_not_relay { - handle_valid_tx( - tx, - state.clone(), - &mut txpool_write_handle, - &mut dandelion_pool_manager, - ) - .await; + tracing::debug!( + tx = hex::encode(tx.tx_hash), + "passing tx to tx-pool manager" + ); + + // TODO: take into account `do_not_relay` in the tx-pool manager. + + if txpool_manager_handle + .tx_tx + .send((tx, state.clone())) + .await + .is_err() + { + tracing::warn!("The txpool manager has been stopped, dropping incoming txs"); + return Ok(()); } } // Re-relay any txs we got in the block that were already in our stem pool. - if !do_not_relay { - for stem_tx in stem_pool_txs { - rerelay_stem_tx( - &stem_tx, - state.clone(), - &mut txpool_read_handle, - &mut dandelion_pool_manager, - ) - .await; - } + for stem_tx in stem_pool_txs { + rerelay_stem_tx( + &stem_tx, + state.clone(), + &mut txpool_read_handle, + &mut dandelion_pool_manager, + ) + .await; } Ok(()) @@ -267,6 +294,7 @@ async fn prepare_incoming_txs( // If a duplicate is in here the incoming tx batch contained the same tx twice. if !tx_blob_hashes.insert(tx_blob_hash) { + tracing::debug!("peer sent duplicate tx in batch, ignoring batch."); return Some(Err(IncomingTxError::DuplicateTransaction)); } @@ -321,58 +349,6 @@ async fn prepare_incoming_txs( .await } -/// Handle a verified tx. -/// -/// This will add the tx to the txpool and route it to the network. -async fn handle_valid_tx( - tx: TransactionVerificationData, - state: TxState, - txpool_write_handle: &mut TxpoolWriteHandle, - dandelion_pool_manager: &mut DandelionPoolService< - DandelionTx, - TxId, - CrossNetworkInternalPeerId, - >, -) { - let incoming_tx = - IncomingTxBuilder::new(DandelionTx(Bytes::copy_from_slice(&tx.tx_blob)), tx.tx_hash); - - let TxpoolWriteResponse::AddTransaction(double_spend) = txpool_write_handle - .ready() - .await - .expect(PANIC_CRITICAL_SERVICE_ERROR) - .call(TxpoolWriteRequest::AddTransaction { - tx: Box::new(tx), - state_stem: state.is_stem_stage(), - }) - .await - .expect("TODO") - else { - unreachable!() - }; - - // TODO: track double spends to quickly ignore them from their blob hash. - if let Some(tx_hash) = double_spend { - return; - } - - // TODO: There is a race condition possible if a tx and block come in at the same time: . - - let incoming_tx = incoming_tx - .with_routing_state(state) - .with_state_in_db(None) - .build() - .unwrap(); - - dandelion_pool_manager - .ready() - .await - .expect(PANIC_CRITICAL_SERVICE_ERROR) - .call(incoming_tx) - .await - .expect(PANIC_CRITICAL_SERVICE_ERROR); -} - /// Re-relay a tx that was already in our stem pool. async fn rerelay_stem_tx( tx_hash: &TxId, diff --git a/binaries/cuprated/src/txpool/manager.rs b/binaries/cuprated/src/txpool/manager.rs new file mode 100644 index 0000000..6faf99c --- /dev/null +++ b/binaries/cuprated/src/txpool/manager.rs @@ -0,0 +1,497 @@ +use std::{ + cmp::min, + time::{Duration, Instant, SystemTime, UNIX_EPOCH}, +}; + +use bytes::Bytes; +use futures::StreamExt; +use indexmap::IndexMap; +use rand::Rng; +use tokio::sync::{mpsc, oneshot}; +use tokio_util::{time::delay_queue, time::DelayQueue}; +use tower::{Service, ServiceExt}; +use tracing::{instrument, Instrument, Span}; + +use cuprate_dandelion_tower::{ + pool::{DandelionPoolService, IncomingTx, IncomingTxBuilder}, + traits::DiffuseRequest, + TxState, +}; +use cuprate_helper::time::current_unix_timestamp; +use cuprate_p2p_core::ClearNet; +use cuprate_txpool::service::{ + interface::{TxpoolReadRequest, TxpoolReadResponse, TxpoolWriteRequest, TxpoolWriteResponse}, + TxpoolReadHandle, TxpoolWriteHandle, +}; +use cuprate_types::TransactionVerificationData; + +use crate::{ + config::TxpoolConfig, + constants::PANIC_CRITICAL_SERVICE_ERROR, + p2p::{CrossNetworkInternalPeerId, NetworkInterfaces}, + txpool::{ + dandelion::DiffuseService, + incoming_tx::{DandelionTx, TxId}, + }, +}; + +const INCOMING_TX_QUEUE_SIZE: usize = 100; + +/// Starts the transaction pool manager service. +/// +/// # Panics +/// +/// This function may panic if any inner service has an unrecoverable error. +pub async fn start_txpool_manager( + mut txpool_write_handle: TxpoolWriteHandle, + mut txpool_read_handle: TxpoolReadHandle, + promote_tx_channel: mpsc::Receiver<[u8; 32]>, + diffuse_service: DiffuseService, + dandelion_pool_manager: DandelionPoolService, + config: TxpoolConfig, +) -> TxpoolManagerHandle { + let TxpoolReadResponse::Backlog(backlog) = txpool_read_handle + .ready() + .await + .expect(PANIC_CRITICAL_SERVICE_ERROR) + .call(TxpoolReadRequest::Backlog) + .await + .expect(PANIC_CRITICAL_SERVICE_ERROR) + else { + unreachable!() + }; + + tracing::info!(txs_in_pool = backlog.len(), "starting txpool manager"); + + let mut stem_txs = Vec::new(); + + let mut tx_timeouts = DelayQueue::with_capacity(backlog.len()); + let current_txs = backlog + .into_iter() + .map(|tx| { + let timeout_key = if tx.private { + stem_txs.push(tx.id); + None + } else { + let next_timeout = calculate_next_timeout(tx.received_at, config.maximum_age_secs); + Some(tx_timeouts.insert(tx.id, Duration::from_secs(next_timeout))) + }; + + ( + tx.id, + TxInfo { + weight: tx.weight, + fee: tx.fee, + received_at: tx.received_at, + private: tx.private, + timeout_key, + }, + ) + }) + .collect(); + + let mut manager = TxpoolManager { + current_txs, + tx_timeouts, + txpool_write_handle, + txpool_read_handle, + dandelion_pool_manager, + promote_tx_channel, + diffuse_service, + config, + }; + + tracing::info!(stem_txs = stem_txs.len(), "promoting stem txs"); + + for tx in stem_txs { + manager.promote_tx(tx).await; + } + + let (tx_tx, tx_rx) = mpsc::channel(INCOMING_TX_QUEUE_SIZE); + let (spent_kis_tx, spent_kis_rx) = mpsc::channel(1); + + tokio::spawn(manager.run(tx_rx, spent_kis_rx)); + + TxpoolManagerHandle { + tx_tx, + spent_kis_tx, + } +} + +/// A handle to the tx-pool manager. +#[derive(Clone)] +pub struct TxpoolManagerHandle { + /// The incoming tx channel. + pub tx_tx: mpsc::Sender<( + TransactionVerificationData, + TxState, + )>, + + /// The spent key images in a new block tx. + spent_kis_tx: mpsc::Sender<(Vec<[u8; 32]>, oneshot::Sender<()>)>, +} + +impl TxpoolManagerHandle { + /// Create a mock [`TxpoolManagerHandle`] that does nothing. + /// + /// Useful for testing. + #[expect(clippy::let_underscore_must_use)] + pub fn mock() -> Self { + let (spent_kis_tx, mut spent_kis_rx) = mpsc::channel(1); + let (tx_tx, mut tx_rx) = mpsc::channel(100); + + tokio::spawn(async move { + loop { + let Some(rec): Option<(_, oneshot::Sender<()>)> = spent_kis_rx.recv().await else { + return; + }; + + let _ = rec.1.send(()); + } + }); + + tokio::spawn(async move { + loop { + if tx_rx.recv().await.is_none() { + return; + } + } + }); + + Self { + tx_tx, + spent_kis_tx, + } + } + + /// Tell the tx-pool about spent key images in an incoming block. + pub async fn new_block(&mut self, spent_key_images: Vec<[u8; 32]>) -> anyhow::Result<()> { + let (tx, rx) = oneshot::channel(); + + drop(self.spent_kis_tx.send((spent_key_images, tx)).await); + + rx.await + .map_err(|_| anyhow::anyhow!("txpool manager stopped")) + } +} + +/// Information on a transaction in the tx-pool. +struct TxInfo { + /// The weight of the transaction. + weight: usize, + /// The fee the transaction paid. + fee: u64, + /// The UNIX timestamp when the tx was received. + received_at: u64, + /// Whether the tx is in the private pool. + private: bool, + + /// The [`delay_queue::Key`] for the timeout queue in the manager. + /// + /// This will be [`None`] if the tx is private as timeouts for them are handled in the dandelion pool. + timeout_key: Option, +} + +struct TxpoolManager { + current_txs: IndexMap<[u8; 32], TxInfo>, + + /// A [`DelayQueue`] for waiting on tx timeouts. + /// + /// Timeouts can be for re-relaying or removal from the pool. + tx_timeouts: DelayQueue<[u8; 32]>, + + txpool_write_handle: TxpoolWriteHandle, + txpool_read_handle: TxpoolReadHandle, + + dandelion_pool_manager: DandelionPoolService, + /// The channel the dandelion manager will use to communicate that a tx should be promoted to the + /// public pool. + promote_tx_channel: mpsc::Receiver<[u8; 32]>, + /// The [`DiffuseService`] to diffuse txs to the p2p network. + /// + /// Used for re-relays. + diffuse_service: DiffuseService, + + config: TxpoolConfig, +} + +impl TxpoolManager { + /// Removes a transaction from the tx-pool manager, and optionally the database too. + /// + /// # Panics + /// + /// This function will panic if the tx is not in the tx-pool manager. + #[instrument(level = "debug", skip_all, fields(tx_id = hex::encode(tx)))] + async fn remove_tx_from_pool(&mut self, tx: [u8; 32], remove_from_db: bool) { + tracing::debug!("removing tx from pool"); + + let tx_info = self.current_txs.swap_remove(&tx).unwrap(); + + tx_info + .timeout_key + .and_then(|key| self.tx_timeouts.try_remove(&key)); + + if remove_from_db { + self.txpool_write_handle + .ready() + .await + .expect(PANIC_CRITICAL_SERVICE_ERROR) + .call(TxpoolWriteRequest::RemoveTransaction(tx)) + .await + .expect(PANIC_CRITICAL_SERVICE_ERROR); + } + } + + /// Re-relay a tx to the network. + /// + /// # Panics + /// + /// This function will panic if the tx is not in the tx-pool. + #[instrument(level = "debug", skip_all, fields(tx_id = hex::encode(tx)))] + async fn rerelay_tx(&mut self, tx: [u8; 32]) { + tracing::debug!("re-relaying tx to network"); + + let TxpoolReadResponse::TxBlob { + tx_blob, + state_stem: _, + } = self + .txpool_read_handle + .ready() + .await + .expect(PANIC_CRITICAL_SERVICE_ERROR) + .call(TxpoolReadRequest::TxBlob(tx)) + .await + .expect(PANIC_CRITICAL_SERVICE_ERROR) + else { + unreachable!() + }; + + self.diffuse_service + .call(DiffuseRequest(DandelionTx(Bytes::from(tx_blob)))) + .await + .expect(PANIC_CRITICAL_SERVICE_ERROR); + } + + /// Handles a transaction timeout, be either rebroadcasting or dropping the tx from the pool. + /// If a rebroadcast happens, this function will handle adding another timeout to the queue. + #[instrument(level = "debug", skip_all, fields(tx_id = hex::encode(tx)))] + async fn handle_tx_timeout(&mut self, tx: [u8; 32]) { + let Some(tx_info) = self.current_txs.get(&tx) else { + tracing::warn!("tx timed out, but tx not in pool"); + return; + }; + + let time_in_pool = current_unix_timestamp() - tx_info.received_at; + + // Check if the tx has timed out, with a small buffer to prevent rebroadcasting if the time is + // slightly off. + if time_in_pool + 10 > self.config.maximum_age_secs { + tracing::warn!("tx has been in pool too long, removing from pool"); + self.remove_tx_from_pool(tx, true).await; + return; + } + + let received_at = tx_info.received_at; + + tracing::debug!(time_in_pool, "tx timed out, resending to network"); + + self.rerelay_tx(tx).await; + + let tx_info = self.current_txs.get_mut(&tx).unwrap(); + + let next_timeout = calculate_next_timeout(received_at, self.config.maximum_age_secs); + tracing::trace!(in_secs = next_timeout, "setting next tx timeout"); + + tx_info.timeout_key = Some( + self.tx_timeouts + .insert(tx, Duration::from_secs(next_timeout)), + ); + } + + /// Adds a tx to the tx-pool manager. + #[instrument(level = "trace", skip_all, fields(tx_id = hex::encode(tx)))] + fn track_tx(&mut self, tx: [u8; 32], weight: usize, fee: u64, private: bool) { + let now = current_unix_timestamp(); + + let timeout_key = if private { + // The dandelion pool handles stem tx embargo. + None + } else { + let timeout = calculate_next_timeout(now, self.config.maximum_age_secs); + + tracing::trace!(in_secs = timeout, "setting next tx timeout"); + + Some(self.tx_timeouts.insert(tx, Duration::from_secs(timeout))) + }; + + self.current_txs.insert( + tx, + TxInfo { + weight, + fee, + received_at: now, + private, + timeout_key, + }, + ); + } + + /// Handles an incoming tx, adding it to the pool and routing it. + #[instrument(level = "debug", skip_all, fields(tx_id = hex::encode(tx.tx_hash), state))] + async fn handle_incoming_tx( + &mut self, + tx: TransactionVerificationData, + state: TxState, + ) { + tracing::debug!("handling new tx"); + + let incoming_tx = + IncomingTxBuilder::new(DandelionTx(Bytes::copy_from_slice(&tx.tx_blob)), tx.tx_hash); + + let (tx_hash, tx_weight, tx_fee) = (tx.tx_hash, tx.tx_weight, tx.fee); + + let TxpoolWriteResponse::AddTransaction(double_spend) = self + .txpool_write_handle + .ready() + .await + .expect(PANIC_CRITICAL_SERVICE_ERROR) + .call(TxpoolWriteRequest::AddTransaction { + tx: Box::new(tx), + state_stem: state.is_stem_stage(), + }) + .await + .expect(PANIC_CRITICAL_SERVICE_ERROR) + else { + unreachable!() + }; + + if let Some(tx_hash) = double_spend { + tracing::debug!( + double_spent = hex::encode(tx_hash), + "transaction is a double spend, ignoring" + ); + return; + } + + self.track_tx(tx_hash, tx_weight, tx_fee, state.is_stem_stage()); + + let incoming_tx = incoming_tx + .with_routing_state(state) + .with_state_in_db(None) + .build() + .unwrap(); + + self.dandelion_pool_manager + .ready() + .await + .expect(PANIC_CRITICAL_SERVICE_ERROR) + .call(incoming_tx) + .await + .expect(PANIC_CRITICAL_SERVICE_ERROR); + } + + /// Promote a tx to the public pool. + #[instrument(level = "debug", skip_all, fields(tx_id = hex::encode(tx)))] + async fn promote_tx(&mut self, tx: [u8; 32]) { + let Some(tx_info) = self.current_txs.get_mut(&tx) else { + tracing::debug!("not promoting tx, tx not in pool"); + return; + }; + + if !tx_info.private { + tracing::trace!("not promoting tx, tx is already public"); + return; + } + + tracing::debug!("promoting tx"); + + // It's now in the public pool, pretend we just saw it. + tx_info.received_at = current_unix_timestamp(); + + let next_timeout = + calculate_next_timeout(tx_info.received_at, self.config.maximum_age_secs); + tracing::trace!(in_secs = next_timeout, "setting next tx timeout"); + tx_info.timeout_key = Some( + self.tx_timeouts + .insert(tx, Duration::from_secs(next_timeout)), + ); + + self.txpool_write_handle + .ready() + .await + .expect(PANIC_CRITICAL_SERVICE_ERROR) + .call(TxpoolWriteRequest::Promote(tx)) + .await + .expect(PANIC_CRITICAL_SERVICE_ERROR); + } + + /// Handles removing all transactions that have been included/double spent in an incoming block. + #[instrument(level = "debug", skip_all)] + async fn new_block(&mut self, spent_key_images: Vec<[u8; 32]>) { + tracing::debug!("handling new block"); + + let TxpoolWriteResponse::NewBlock(removed_txs) = self + .txpool_write_handle + .ready() + .await + .expect(PANIC_CRITICAL_SERVICE_ERROR) + .call(TxpoolWriteRequest::NewBlock { spent_key_images }) + .await + .expect(PANIC_CRITICAL_SERVICE_ERROR) + else { + unreachable!() + }; + + for tx in removed_txs { + self.remove_tx_from_pool(tx, false).await; + } + } + + #[expect(clippy::let_underscore_must_use)] + async fn run( + mut self, + mut tx_rx: mpsc::Receiver<( + TransactionVerificationData, + TxState, + )>, + mut block_rx: mpsc::Receiver<(Vec<[u8; 32]>, oneshot::Sender<()>)>, + ) { + loop { + tokio::select! { + Some(tx) = self.tx_timeouts.next() => { + self.handle_tx_timeout(tx.into_inner()).await; + } + Some((tx, state)) = tx_rx.recv() => { + self.handle_incoming_tx(tx, state).await; + } + Some(tx) = self.promote_tx_channel.recv() => { + self.promote_tx(tx).await; + } + Some((spent_kis, tx)) = block_rx.recv() => { + self.new_block(spent_kis).await; + let _ = tx.send(()); + } + } + } + } +} + +/// Calculates the amount of time to wait before resending a tx to the network. +fn calculate_next_timeout(received_at: u64, max_time_in_pool: u64) -> u64 { + /// The base time between re-relays to the p2p network. + const TX_RERELAY_TIME: u64 = 300; + + /* + This is a simple exponential backoff. + The first timeout is TX_RERELAY_TIME seconds, the second is 2 * TX_RERELAY_TIME seconds, then 4, 8, 16, etc. + */ + let now = current_unix_timestamp(); + + let time_in_pool = now - received_at; + + let time_till_max_timeout = max_time_in_pool.saturating_sub(time_in_pool); + + let timeouts = time_in_pool / TX_RERELAY_TIME; + + min((timeouts + 1) * TX_RERELAY_TIME, time_till_max_timeout) +} diff --git a/p2p/dandelion-tower/Cargo.toml b/p2p/dandelion-tower/Cargo.toml index 92e4915..42637a3 100644 --- a/p2p/dandelion-tower/Cargo.toml +++ b/p2p/dandelion-tower/Cargo.toml @@ -11,7 +11,7 @@ txpool = ["dep:rand_distr", "dep:tokio-util", "dep:tokio"] [dependencies] tower = { workspace = true, features = ["util"] } -tracing = { workspace = true, features = ["std"] } +tracing = { workspace = true, features = ["std", "attributes"] } futures = { workspace = true, features = ["std"] } tokio = { workspace = true, features = ["rt", "sync", "macros"], optional = true} diff --git a/p2p/dandelion-tower/src/pool/manager.rs b/p2p/dandelion-tower/src/pool/manager.rs index a911959..394ed6b 100644 --- a/p2p/dandelion-tower/src/pool/manager.rs +++ b/p2p/dandelion-tower/src/pool/manager.rs @@ -14,6 +14,7 @@ use tokio::{ }; use tokio_util::time::{delay_queue, DelayQueue}; use tower::{Service, ServiceExt}; +use tracing::Instrument; use crate::{ pool::IncomingTx, @@ -223,43 +224,47 @@ where Ok(tx.map(|tx| tx.0)) } + #[expect(clippy::type_complexity)] /// Starts the [`DandelionPoolManager`]. pub(crate) async fn run( mut self, - mut rx: mpsc::Receiver<(IncomingTx, oneshot::Sender<()>)>, + mut rx: mpsc::Receiver<( + (IncomingTx, tracing::Span), + oneshot::Sender<()>, + )>, ) { tracing::debug!("Starting dandelion++ tx-pool, config: {:?}", self.config); loop { - tracing::trace!("Waiting for next event."); tokio::select! { // biased to handle current txs before routing new ones. biased; Some(fired) = self.embargo_timers.next() => { - tracing::debug!("Embargo timer fired, did not see stem tx in time."); + let span = tracing::debug_span!("embargo_timer_fired"); + tracing::debug!(parent: &span,"Embargo timer fired, did not see stem tx in time."); let tx_id = fired.into_inner(); - if let Err(e) = self.promote_and_fluff_tx(tx_id).await { - tracing::error!("Error handling fired embargo timer: {e}"); + if let Err(e) = self.promote_and_fluff_tx(tx_id).instrument(span.clone()).await { + tracing::error!(parent: &span, "Error handling fired embargo timer: {e}"); return; } } Some(Ok((tx_id, res))) = self.routing_set.join_next() => { - tracing::trace!("Received d++ routing result."); + let span = tracing::debug_span!("dandelion_routing_result"); let res = match res { Ok(State::Fluff) => { - tracing::debug!("Transaction was fluffed upgrading it to the public pool."); - self.promote_tx(tx_id).await + tracing::debug!(parent: &span, "Transaction was fluffed upgrading it to the public pool."); + self.promote_tx(tx_id).instrument(span.clone()).await } Err(tx_state) => { - tracing::debug!("Error routing transaction, trying again."); + tracing::debug!(parent: &span, "Error routing transaction, trying again."); - match self.get_tx_from_pool(tx_id.clone()).await { + match self.get_tx_from_pool(tx_id.clone()).instrument(span.clone()).await { Ok(Some(tx)) => match tx_state { - TxState::Fluff => self.fluff_tx(tx, tx_id).await, - TxState::Stem { from } => self.stem_tx(tx, tx_id, Some(from)).await, - TxState::Local => self.stem_tx(tx, tx_id, None).await, + TxState::Fluff => self.fluff_tx(tx, tx_id).instrument(span.clone()).await, + TxState::Stem { from } => self.stem_tx(tx, tx_id, Some(from)).instrument(span.clone()).await, + TxState::Local => self.stem_tx(tx, tx_id, None).instrument(span.clone()).await, } Err(e) => Err(e), _ => continue, @@ -269,22 +274,24 @@ where }; if let Err(e) = res { - tracing::error!("Error handling transaction routing return: {e}"); + tracing::error!(parent: &span, "Error handling transaction routing return: {e}"); return; } } req = rx.recv() => { - tracing::debug!("Received new tx to route."); - - let Some((IncomingTx { tx, tx_id, routing_state }, res_tx)) = req else { + let Some(((IncomingTx { tx, tx_id, routing_state }, span), res_tx)) = req else { return; }; - if let Err(e) = self.handle_incoming_tx(tx, routing_state, tx_id).await { + let span = tracing::debug_span!(parent: &span, "dandelion_pool_manager"); + + tracing::debug!(parent: &span, "Received new tx to route."); + + if let Err(e) = self.handle_incoming_tx(tx, routing_state, tx_id).instrument(span.clone()).await { #[expect(clippy::let_underscore_must_use, reason = "dropped receivers can be ignored")] let _ = res_tx.send(()); - tracing::error!("Error handling transaction in dandelion pool: {e}"); + tracing::error!(parent: &span, "Error handling transaction in dandelion pool: {e}"); return; } diff --git a/p2p/dandelion-tower/src/pool/mod.rs b/p2p/dandelion-tower/src/pool/mod.rs index 90eb555..381d012 100644 --- a/p2p/dandelion-tower/src/pool/mod.rs +++ b/p2p/dandelion-tower/src/pool/mod.rs @@ -34,7 +34,6 @@ use tokio::{ }; use tokio_util::{sync::PollSender, time::DelayQueue}; use tower::Service; -use tracing::Instrument; use crate::{ pool::manager::DandelionPoolShutDown, @@ -93,9 +92,7 @@ where _tx: PhantomData, }; - let span = tracing::debug_span!("dandelion_pool"); - - tokio::spawn(pool.run(rx).instrument(span)); + tokio::spawn(pool.run(rx)); DandelionPoolService { tx: PollSender::new(tx), @@ -107,8 +104,12 @@ where /// Used to send [`IncomingTx`]s to the [`DandelionPoolManager`] #[derive(Clone)] pub struct DandelionPoolService { + #[expect(clippy::type_complexity)] /// The channel to [`DandelionPoolManager`]. - tx: PollSender<(IncomingTx, oneshot::Sender<()>)>, + tx: PollSender<( + (IncomingTx, tracing::Span), + oneshot::Sender<()>, + )>, } impl Service> @@ -132,7 +133,7 @@ where let res = self .tx - .send_item((req, tx)) + .send_item(((req, tracing::Span::current()), tx)) .map_err(|_| DandelionPoolShutDown); async move { diff --git a/storage/txpool/Cargo.toml b/storage/txpool/Cargo.toml index 63d5557..602cc35 100644 --- a/storage/txpool/Cargo.toml +++ b/storage/txpool/Cargo.toml @@ -21,7 +21,7 @@ serde = ["dep:serde", "cuprate-database/serde", "cuprate-database-service/ cuprate-database = { workspace = true, features = ["heed"] } cuprate-database-service = { workspace = true } cuprate-types = { workspace = true, features = ["rpc"] } -cuprate-helper = { workspace = true, default-features = false, features = ["constants"] } +cuprate-helper = { workspace = true, default-features = false, features = ["constants", "time"] } monero-serai = { workspace = true, features = ["std"] } bytemuck = { workspace = true, features = ["must_cast", "derive", "min_const_generics", "extern_crate_alloc"] } @@ -29,6 +29,7 @@ bitflags = { workspace = true, features = ["std", "serde", "byte thiserror = { workspace = true } hex = { workspace = true, features = ["std"] } blake3 = { workspace = true, features = ["std"] } +tracing = { workspace = true } tower = { workspace = true } rayon = { workspace = true } diff --git a/storage/txpool/README.md b/storage/txpool/README.md index ca4f737..3bb2fad 100644 --- a/storage/txpool/README.md +++ b/storage/txpool/README.md @@ -82,7 +82,7 @@ use cuprate_txpool::{ .build(); // Initialize the database environment. - let env = cuprate_txpool::open(config)?; + let env = cuprate_txpool::open(&config)?; // Open up a transaction + tables for writing. let env_inner = env.env_inner(); diff --git a/storage/txpool/src/free.rs b/storage/txpool/src/free.rs index d0f9a31..affeb80 100644 --- a/storage/txpool/src/free.rs +++ b/storage/txpool/src/free.rs @@ -1,9 +1,23 @@ //! General free functions (related to the tx-pool database). -//---------------------------------------------------------------------------------------------------- Import -use cuprate_database::{ConcreteEnv, Env, EnvInner, InitError, RuntimeError, TxRw}; +use std::borrow::Cow; -use crate::{config::Config, tables::OpenTables, types::TransactionBlobHash}; +use cuprate_database::{ + ConcreteEnv, DatabaseRo, Env, EnvInner, InitError, RuntimeError, StorableStr, TxRw, +}; +use cuprate_database::{DatabaseRw, TxRo}; + +use crate::{ + config::Config, + tables::{Metadata, OpenTables}, + types::TransactionBlobHash, +}; + +/// The current version of the database format. +pub const DATABASE_VERSION: StorableStr = StorableStr(Cow::Borrowed("0.1")); + +/// The key used to store the database version in the [`Metadata`] table. +pub const VERSION_KEY: StorableStr = StorableStr(Cow::Borrowed("version")); //---------------------------------------------------------------------------------------------------- Free functions /// Open the txpool database using the passed [`Config`]. @@ -22,9 +36,9 @@ use crate::{config::Config, tables::OpenTables, types::TransactionBlobHash}; /// - A table could not be created/opened #[cold] #[inline(never)] // only called once -pub fn open(config: Config) -> Result { +pub fn open(config: &Config) -> Result { // Attempt to open the database environment. - let env = ::open(config.db_config)?; + let env = ::open(config.db_config.clone())?; /// Convert runtime errors to init errors. /// @@ -36,20 +50,28 @@ pub fn open(config: Config) -> Result { fn runtime_to_init_error(runtime: RuntimeError) -> InitError { match runtime { RuntimeError::Io(io_error) => io_error.into(), + RuntimeError::KeyNotFound => InitError::InvalidVersion, // These errors shouldn't be happening here. - RuntimeError::KeyExists - | RuntimeError::KeyNotFound - | RuntimeError::ResizeNeeded - | RuntimeError::TableNotFound => unreachable!(), + RuntimeError::KeyExists | RuntimeError::ResizeNeeded | RuntimeError::TableNotFound => { + unreachable!() + } } } + let fresh_db; + // INVARIANT: We must ensure that all tables are created, // `cuprate_database` has no way of knowing _which_ tables // we want since it is agnostic, so we are responsible for this. { let env_inner = env.env_inner(); + + // Store if this DB has been used before by checking if the metadata table exists. + let tx_ro = env_inner.tx_ro().map_err(runtime_to_init_error)?; + fresh_db = env_inner.open_db_ro::(&tx_ro).is_err(); + TxRo::commit(tx_ro).map_err(runtime_to_init_error)?; + let tx_rw = env_inner.tx_rw().map_err(runtime_to_init_error)?; // Create all tables. @@ -58,6 +80,43 @@ pub fn open(config: Config) -> Result { TxRw::commit(tx_rw).map_err(runtime_to_init_error)?; } + { + let env_inner = env.env_inner(); + let tx_rw = env_inner.tx_rw().map_err(runtime_to_init_error)?; + + let mut metadata = env_inner + .open_db_rw::(&tx_rw) + .map_err(runtime_to_init_error)?; + + if fresh_db { + // If the database is new, add the version. + metadata + .put(&VERSION_KEY, &DATABASE_VERSION) + .map_err(runtime_to_init_error)?; + } + + let print_version_err = || { + tracing::error!( + "The database follows an old format, please delete the database at: {}", + config.db_config.db_directory().display() + ); + }; + + let version = metadata + .get(&VERSION_KEY) + .inspect_err(|_| print_version_err()) + .map_err(runtime_to_init_error)?; + + if version != DATABASE_VERSION { + // TODO: database migration when stable? This is the tx-pool so is not critical. + print_version_err(); + return Err(InitError::InvalidVersion); + } + + drop(metadata); + TxRw::commit(tx_rw).map_err(runtime_to_init_error)?; + } + Ok(env) } diff --git a/storage/txpool/src/lib.rs b/storage/txpool/src/lib.rs index 53e53ec..a176461 100644 --- a/storage/txpool/src/lib.rs +++ b/storage/txpool/src/lib.rs @@ -16,7 +16,7 @@ mod tx; pub mod types; pub use config::Config; -pub use free::{open, transaction_blob_hash}; +pub use free::{open, transaction_blob_hash, DATABASE_VERSION, VERSION_KEY}; pub use tx::TxEntry; //re-exports diff --git a/storage/txpool/src/ops.rs b/storage/txpool/src/ops.rs index badc4f6..3424d61 100644 --- a/storage/txpool/src/ops.rs +++ b/storage/txpool/src/ops.rs @@ -55,7 +55,7 @@ //! .build(); //! //! // Initialize the database environment. -//! let env = cuprate_txpool::open(config)?; +//! let env = cuprate_txpool::open(&config)?; //! //! // Open up a transaction + tables for writing. //! let env_inner = env.env_inner(); diff --git a/storage/txpool/src/ops/tx_write.rs b/storage/txpool/src/ops/tx_write.rs index f1f43b2..b876724 100644 --- a/storage/txpool/src/ops/tx_write.rs +++ b/storage/txpool/src/ops/tx_write.rs @@ -5,6 +5,7 @@ use bytemuck::TransparentWrapper; use monero_serai::transaction::{NotPruned, Transaction}; use cuprate_database::{DatabaseRw, DbResult, StorableVec}; +use cuprate_helper::time::current_unix_timestamp; use cuprate_types::TransactionVerificationData; use crate::{ @@ -42,6 +43,7 @@ pub fn add_transaction( &TransactionInfo { fee: tx.fee, weight: tx.tx_weight, + received_at: current_unix_timestamp(), flags, _padding: [0; 7], }, diff --git a/storage/txpool/src/service.rs b/storage/txpool/src/service.rs index 2a13f1c..35311df 100644 --- a/storage/txpool/src/service.rs +++ b/storage/txpool/src/service.rs @@ -87,7 +87,7 @@ //! .build(); //! //! // Initialize the database thread-pool. -//! let (mut read_handle, mut write_handle, _) = cuprate_txpool::service::init(config)?; +//! let (mut read_handle, mut write_handle, _) = cuprate_txpool::service::init(&config)?; //! //! // Prepare a request to write block. //! let tx = TX_V1_SIG2.clone(); diff --git a/storage/txpool/src/service/free.rs b/storage/txpool/src/service/free.rs index 1bb15cd..62eaa83 100644 --- a/storage/txpool/src/service/free.rs +++ b/storage/txpool/src/service/free.rs @@ -24,7 +24,7 @@ use crate::{ /// # Errors /// This will forward the error if [`crate::open`] failed. pub fn init( - config: Config, + config: &Config, ) -> Result<(TxpoolReadHandle, TxpoolWriteHandle, Arc), InitError> { let reader_threads = config.reader_threads; @@ -51,7 +51,7 @@ pub fn init( /// # Errors /// This will forward the error if [`crate::open`] failed. pub fn init_with_pool( - config: Config, + config: &Config, pool: Arc, ) -> Result<(TxpoolReadHandle, TxpoolWriteHandle, Arc), InitError> { // Initialize the database itself. diff --git a/storage/txpool/src/service/interface.rs b/storage/txpool/src/service/interface.rs index 95f5da7..83d608c 100644 --- a/storage/txpool/src/service/interface.rs +++ b/storage/txpool/src/service/interface.rs @@ -191,9 +191,13 @@ pub enum TxpoolWriteResponse { /// Response to: /// - [`TxpoolWriteRequest::RemoveTransaction`] /// - [`TxpoolWriteRequest::Promote`] - /// - [`TxpoolWriteRequest::NewBlock`] Ok, + /// Response to [`TxpoolWriteRequest::NewBlock`]. + /// + /// The inner values are the transactions removed from the pool. + NewBlock(Vec), + /// Response to [`TxpoolWriteRequest::AddTransaction`]. /// /// If the inner value is [`Some`] the tx was not added to the pool as it double spends a tx with the given hash. diff --git a/storage/txpool/src/service/read.rs b/storage/txpool/src/service/read.rs index 5b6bf59..c0b699f 100644 --- a/storage/txpool/src/service/read.rs +++ b/storage/txpool/src/service/read.rs @@ -13,7 +13,9 @@ use std::{ use rayon::ThreadPool; -use cuprate_database::{ConcreteEnv, DatabaseRo, DbResult, Env, EnvInner, RuntimeError}; +use cuprate_database::{ + ConcreteEnv, DatabaseIter, DatabaseRo, DbResult, Env, EnvInner, RuntimeError, +}; use cuprate_database_service::{init_thread_pool, DatabaseReadService, ReaderThreads}; use crate::{ @@ -24,6 +26,7 @@ use crate::{ }, tables::{KnownBlobHashes, OpenTables, TransactionBlobs, TransactionInfos}, types::{TransactionBlobHash, TransactionHash}, + TxEntry, }; // TODO: update the docs here @@ -229,7 +232,27 @@ fn txs_for_block(env: &ConcreteEnv, txs: Vec) -> ReadResponseRe /// [`TxpoolReadRequest::Backlog`]. #[inline] fn backlog(env: &ConcreteEnv) -> ReadResponseResult { - Ok(TxpoolReadResponse::Backlog(todo!())) + let inner_env = env.env_inner(); + let tx_ro = inner_env.tx_ro()?; + + let tx_infos_table = inner_env.open_db_ro::(&tx_ro)?; + + let backlog = tx_infos_table + .iter()? + .map(|info| { + let (id, info) = info?; + + Ok(TxEntry { + id, + weight: info.weight, + fee: info.fee, + private: info.flags.private(), + received_at: info.received_at, + }) + }) + .collect::>()?; + + Ok(TxpoolReadResponse::Backlog(backlog)) } /// [`TxpoolReadRequest::Size`]. diff --git a/storage/txpool/src/service/write.rs b/storage/txpool/src/service/write.rs index 3df3535..737bdf4 100644 --- a/storage/txpool/src/service/write.rs +++ b/storage/txpool/src/service/write.rs @@ -4,6 +4,7 @@ use cuprate_database::{ ConcreteEnv, DatabaseRo, DatabaseRw, DbResult, Env, EnvInner, RuntimeError, TxRw, }; use cuprate_database_service::DatabaseWriteHandle; +use cuprate_helper::time::current_unix_timestamp; use cuprate_types::TransactionVerificationData; use crate::{ @@ -115,6 +116,7 @@ fn promote(env: &ConcreteEnv, tx_hash: &TransactionHash) -> DbResult(&tx_rw)?; tx_infos.update(tx_hash, |mut info| { + info.received_at = current_unix_timestamp(); info.flags.remove(TxStateFlags::STATE_STEM); Some(info) }) @@ -137,8 +139,10 @@ fn new_block(env: &ConcreteEnv, spent_key_images: &[KeyImage]) -> DbResult DbResult (), Err(e) => return Err(e), } @@ -162,5 +168,5 @@ fn new_block(env: &ConcreteEnv, spent_key_images: &[KeyImage]) -> DbResult KnownBlobHashes, TransactionBlobHash => TransactionHash, + + /// Current database version. + 5 => Metadata, + StorableStr => StorableStr, } diff --git a/storage/txpool/src/tx.rs b/storage/txpool/src/tx.rs index 29afae8..42f0fa5 100644 --- a/storage/txpool/src/tx.rs +++ b/storage/txpool/src/tx.rs @@ -8,9 +8,11 @@ pub struct TxEntry { /// The transaction's ID (hash). pub id: [u8; 32], /// The transaction's weight. - pub weight: u64, + pub weight: usize, /// The transaction's fee. pub fee: u64, - /// How long the transaction has been in the pool. - pub time_in_pool: std::time::Duration, + /// If the tx is in the private pool. + pub private: bool, + /// The UNIX timestamp when the transaction was received. + pub received_at: u64, } diff --git a/storage/txpool/src/types.rs b/storage/txpool/src/types.rs index b6478b4..56e67c0 100644 --- a/storage/txpool/src/types.rs +++ b/storage/txpool/src/types.rs @@ -31,6 +31,12 @@ bitflags::bitflags! { } } +impl TxStateFlags { + pub const fn private(&self) -> bool { + self.contains(Self::STATE_STEM) + } +} + /// Information on a tx-pool transaction. #[derive(Copy, Clone, Debug, PartialEq, PartialOrd, Eq, Ord, Hash, Pod, Zeroable)] #[repr(C)] @@ -39,6 +45,8 @@ pub struct TransactionInfo { pub fee: u64, /// The transaction's weight. pub weight: usize, + /// The UNIX timestamp of when this tx was received. + pub received_at: u64, /// [`TxStateFlags`] of this transaction. pub flags: TxStateFlags, #[expect(clippy::pub_underscore_fields)]