diff --git a/protocols/v2/roles-logic-sv2/src/selectors.rs b/protocols/v2/roles-logic-sv2/src/selectors.rs index b3750bdb2..fcfcffeb5 100644 --- a/protocols/v2/roles-logic-sv2/src/selectors.rs +++ b/protocols/v2/roles-logic-sv2/src/selectors.rs @@ -1,5 +1,39 @@ -//! Selectors are used from the routing logic in order to chose to which remote or set of remotes -//! a message should be ralyied, or to which remote or set of remotes a message should be sent. +//! This module provides selectors and routing logic for managing downstream and upstream nodes +//! in a mining proxy environment. Selectors help determine the appropriate remote(s) to relay or +//! send messages to. +//! +//! ## Components +//! +//! - **`ProxyDownstreamMiningSelector`**: A selector for managing downstream nodes in a mining +//! proxy, mapping requests and channel IDs to specific downstream nodes or groups. +//! - **`NullDownstreamMiningSelector`**: A no-op selector for cases where routing logic is +//! unnecessary, commonly used in test scenarios. +//! - **`GeneralMiningSelector`**: A flexible upstream selector that matches downstream nodes with +//! compatible upstream nodes based on pairing settings and flags. +//! +//! ## Traits +//! +//! - **`DownstreamSelector`**: Base trait for all downstream selectors. +//! - **`DownstreamMiningSelector`**: Specialized trait for selectors managing mining-specific +//! downstream nodes. +//! - **`UpstreamSelector`**: Base trait for upstream node selectors. +//! - **`UpstreamMiningSelctor`**: Specialized trait for selectors managing upstream mining nodes. +//! +//! ## Details +//! +//! ### ProxyDownstreamMiningSelector +//! - Manages mappings for request IDs, channel IDs, and downstream groups. +//! - Provides methods to handle standard channel operations, such as opening channels and +//! retrieving or removing downstream nodes associated with a channel. +//! +//! ### NullDownstreamMiningSelector +//! - Implements all required traits but panics if called. +//! - Useful for minimal setups or as a placeholder in tests. +//! +//! ### GeneralMiningSelector +//! - Matches downstream nodes to upstream nodes based on pairing compatibility. +//! - Tracks upstream nodes and their IDs for efficient lookups. + use crate::{ common_properties::{IsDownstream, IsMiningDownstream, IsMiningUpstream, PairSettings}, utils::Mutex, @@ -8,15 +42,24 @@ use crate::{ use nohash_hasher::BuildNoHashHasher; use std::{collections::HashMap, fmt::Debug as D, sync::Arc}; -/// A DownstreamMiningSelector useful for routing messages in a mining proxy +/// A selector used for routing messages to specific downstream mining nodes. +/// +/// This structure maintains mappings for request IDs, channel IDs, and downstream nodes +/// to facilitate efficient message routing. #[derive(Debug, Clone, Default)] pub struct ProxyDownstreamMiningSelector { + /// Maps request IDs to their corresponding downstream nodes. request_id_to_remotes: HashMap>, BuildNoHashHasher>, + /// Maps group channel IDs to a list of downstream nodes. channel_id_to_downstreams: HashMap>>, BuildNoHashHasher>, + /// Maps standard channel IDs to a single downstream node. channel_id_to_downstream: HashMap>, BuildNoHashHasher>, } impl ProxyDownstreamMiningSelector { + /// Creates a new `ProxyDownstreamMiningSelector`. + /// + /// This initializes the internal mappings with `nohash` hashers for performance. pub fn new() -> Self { Self { request_id_to_remotes: HashMap::with_hasher(BuildNoHashHasher::default()), @@ -24,6 +67,10 @@ impl ProxyDownstreamMiningSelector { channel_id_to_downstream: HashMap::with_hasher(BuildNoHashHasher::default()), } } + + /// Creates a new `ProxyDownstreamMiningSelector` wrapped in a mutex and an `Arc`. + /// + /// This is useful for concurrent environments where shared ownership is needed. pub fn new_as_mutex() -> Arc> where Self: Sized, @@ -33,6 +80,10 @@ impl ProxyDownstreamMiningSelector { } impl ProxyDownstreamMiningSelector { + /// Removes a downstream node from all mappings. + /// + /// # Arguments + /// - `d`: The downstream node to be removed. fn _remove_downstream(&mut self, d: &Arc>) { self.request_id_to_remotes.retain(|_, v| !Arc::ptr_eq(v, d)); self.channel_id_to_downstream @@ -43,10 +94,25 @@ impl ProxyDownstreamMiningSelector { impl DownstreamMiningSelector for ProxyDownstreamMiningSelector { + /// Registers a request ID and its associated downstream node. + /// + /// # Arguments + /// - `request_id`: The unique request ID. + /// - `downstream`: The downstream node associated with the request. fn on_open_standard_channel_request(&mut self, request_id: u32, downstream: Arc>) { self.request_id_to_remotes.insert(request_id, downstream); } + /// Finalizes the mapping of a standard channel to its downstream node. + /// + /// # Arguments + /// - `request_id`: The request ID used during the channel opening. + /// - `g_channel_id`: The group channel ID. + /// - `channel_id`: The specific standard channel ID. + /// + /// # Returns + /// - `Ok`: The downstream node associated with the request. + /// - `Err`: If the request ID is unknown. fn on_open_standard_channel_success( &mut self, request_id: u32, @@ -69,10 +135,25 @@ impl DownstreamMiningSelector Ok(downstream) } + /// Retrieves all downstream nodes associated with a standard/group channel ID. + /// + /// # Arguments + /// - `channel_id`: The standard/group channel ID. + /// + /// # Returns + /// - `Some`: A reference to the vector of downstream nodes. + /// - `None`: If no nodes are associated with the channel. fn get_downstreams_in_channel(&self, channel_id: u32) -> Option<&Vec>>> { self.channel_id_to_downstreams.get(&channel_id) } + /// Removes all downstream nodes associated with a standard/group channel ID. + /// + /// # Arguments + /// - `channel_id`: The standard/group channel ID. + /// + /// # Returns + /// A vector of the removed downstream nodes. fn remove_downstreams_in_channel(&mut self, channel_id: u32) -> Vec>> { let downs = self .channel_id_to_downstreams @@ -84,17 +165,34 @@ impl DownstreamMiningSelector downs } + /// Removes a specific downstream node from all mappings. + /// + /// # Arguments + /// - `d`: The downstream node to be removed. fn remove_downstream(&mut self, d: &Arc>) { for dws in self.channel_id_to_downstreams.values_mut() { - dws.retain(|d| !Arc::ptr_eq(d, d)); + dws.retain(|node| !Arc::ptr_eq(node, d)); } self._remove_downstream(d); } + /// Retrieves the downstream node associated with a specific standard channel ID. + /// + /// # Arguments + /// - `channel_id`: The standard channel ID. + /// + /// # Returns + /// - `Some`: The downstream node. + /// - `None`: If no node is associated with the channel. fn downstream_from_channel_id(&self, channel_id: u32) -> Option>> { self.channel_id_to_downstream.get(&channel_id).cloned() } + + /// Retrieves all downstream nodes currently managed by this selector. + /// + /// # Returns + /// A vector of downstream nodes. fn get_all_downstreams(&self) -> Vec>> { self.channel_id_to_downstream.values().cloned().collect() } @@ -103,16 +201,31 @@ impl DownstreamMiningSelector impl DownstreamSelector for ProxyDownstreamMiningSelector {} /// Implemented by a selector used by an upstream mining node or and upstream mining node -/// abstraction in order to find the right downstream to which a message should be sent or relayied +/// abstraction in order to find the right downstream to which a message should be sent or relayed. pub trait DownstreamMiningSelector: DownstreamSelector { + /// Handles a request to open a standard channel. + /// + /// # Arguments + /// - `request_id`: The ID of the request. + /// - `downstream`: A reference to the downstream requesting the channel. fn on_open_standard_channel_request( &mut self, request_id: u32, downstream: Arc>, ); + /// Handles a successful response to opening a standard channel. + /// + /// # Arguments + /// - `request_id`: The ID of the request. + /// - `g_channel_id`: The global channel ID. + /// - `channel_id`: The local channel ID. + /// + /// # Returns + /// - `Result>, Error>`: The downstream associated with the channel or an + /// error. fn on_open_standard_channel_success( &mut self, request_id: u32, @@ -120,32 +233,62 @@ pub trait DownstreamMiningSelector: channel_id: u32, ) -> Result>, Error>; - // group / standard naming is terrible channel_id in this case can be either the channel_id - // or the group_channel_id + /// Retrieves all downstreams associated with a channel ID. + /// + /// # Arguments + /// - `channel_id`: The channel ID to query. + /// + /// # Returns + /// - `Option<&Vec>>>`: The list of downstreams or `None`. fn get_downstreams_in_channel(&self, channel_id: u32) -> Option<&Vec>>>; + /// Removes all downstreams associated with a channel ID. + /// + /// # Arguments + /// - `channel_id`: The channel ID to remove downstreams from. + /// + /// # Returns + /// - `Vec>>`: The removed downstreams. fn remove_downstreams_in_channel(&mut self, channel_id: u32) -> Vec>>; + /// Removes a specific downstream. + /// + /// # Arguments + /// - `d`: A reference to the downstream to remove. fn remove_downstream(&mut self, d: &Arc>); - // only for standard + /// Retrieves a downstream by channel ID (only for standard channels). + /// + /// # Arguments + /// - `channel_id`: The channel ID to query. + /// + /// # Returns + /// - `Option>>`: The downstream or `None`. fn downstream_from_channel_id(&self, channel_id: u32) -> Option>>; + /// Retrieves all downstreams. + /// + /// # Returns + /// - `Vec>>`: All downstreams. fn get_all_downstreams(&self) -> Vec>>; } +/// A generic downstream selector. pub trait DownstreamSelector {} -/// A DownstreamMiningSelector that do nothing. Useful when ParseDownstreamCommonMessages or -/// ParseUpstreamCommonMessages must be implemented in very simple application (eg for test -/// puorposes) +/// A no-op implementation of `DownstreamMiningSelector`. +/// +/// This selector is primarily used for testing or minimal setups where routing logic is not needed. #[derive(Debug, Clone, Copy, Default)] pub struct NullDownstreamMiningSelector(); impl NullDownstreamMiningSelector { + /// Creates a new `NullDownstreamMiningSelector`. pub fn new() -> Self { NullDownstreamMiningSelector() } + + /// Creates a new `NullDownstreamMiningSelector` wrapped in a mutex and an `Arc`. pub fn new_as_mutex() -> Arc> where Self: Sized, @@ -155,6 +298,10 @@ impl NullDownstreamMiningSelector { } impl DownstreamMiningSelector for NullDownstreamMiningSelector { + /// Called when a standard channel open request is received. + /// + /// This method is unreachable in `NullDownstreamMiningSelector` since it is a no-op + /// implementation. fn on_open_standard_channel_request( &mut self, _request_id: u32, @@ -163,6 +310,9 @@ impl DownstreamMiningSelector for NullDownst unreachable!("on_open_standard_channel_request") } + /// Called when a standard channel open request is successful. + /// + /// This method is unreachable in `NullDownstreamMiningSelector`. fn on_open_standard_channel_success( &mut self, _request_id: u32, @@ -172,52 +322,92 @@ impl DownstreamMiningSelector for NullDownst unreachable!("on_open_standard_channel_success") } + /// Retrieves the downstreams in a specific channel. + /// + /// This method is unreachable in `NullDownstreamMiningSelector`. fn get_downstreams_in_channel(&self, _channel_id: u32) -> Option<&Vec>>> { unreachable!("get_downstreams_in_channel") } + + /// Removes downstreams in a specific channel. + /// + /// This method is unreachable in `NullDownstreamMiningSelector`. fn remove_downstreams_in_channel(&mut self, _channel_id: u32) -> Vec>> { unreachable!("remove_downstreams_in_channel") } + /// Removes a specific downstream node. + /// + /// This method is unreachable in `NullDownstreamMiningSelector`. + fn remove_downstream(&mut self, _d: &Arc>) { + unreachable!("remove_downstream") + } + + /// Retrieves the downstream associated with a specific channel ID. + /// + /// This method is unreachable in `NullDownstreamMiningSelector`. fn downstream_from_channel_id(&self, _channel_id: u32) -> Option>> { unreachable!("downstream_from_channel_id") } + + /// Retrieves all downstream nodes managed by this selector. + /// + /// This method is unreachable in `NullDownstreamMiningSelector`. fn get_all_downstreams(&self) -> Vec>> { unreachable!("get_all_downstreams") } - - fn remove_downstream(&mut self, _d: &Arc>) { - unreachable!("remove_downstream") - } } impl DownstreamSelector for NullDownstreamMiningSelector {} +/// Trait for selecting upstream nodes in a mining context. pub trait UpstreamSelector {} +/// Trait for selecting upstream mining nodes. +/// +/// This trait allows pairing downstream mining nodes with upstream nodes +/// based on their settings and capabilities. pub trait UpstreamMiningSelctor< Down: IsMiningDownstream, Up: IsMiningUpstream, Sel: DownstreamMiningSelector, >: UpstreamSelector { + /// Handles the `SetupConnection` process. + /// + /// # Arguments + /// - `pair_settings`: The settings for pairing downstream and upstream nodes. + /// + /// # Returns + /// - `Ok((Vec>>, u32))`: A vector of upstream nodes and their combined flags. + /// - `Err`: If no upstreams are pairable. #[allow(clippy::type_complexity)] fn on_setup_connection( &mut self, pair_settings: &PairSettings, ) -> Result<(Vec>>, u32), Error>; + + /// Retrieves an upstream node by its ID. + /// + /// # Arguments + /// - `upstream_id`: The unique ID of the upstream node. + /// + /// # Returns + /// - `Some`: The upstream node. + /// - `None`: If no upstream is found. fn get_upstream(&self, upstream_id: u32) -> Option>>; } -/// Upstream selector is used to chose between a set of known mining upstream nodes which one/ones -/// can accept messages from a specific mining downstream node +/// General implementation of an upstream mining selector. #[derive(Debug)] pub struct GeneralMiningSelector< Sel: DownstreamMiningSelector, Down: IsMiningDownstream, Up: IsMiningUpstream, > { + /// List of upstream nodes. pub upstreams: Vec>>, + /// Mapping of upstream IDs to their respective nodes. pub id_to_upstream: HashMap>, BuildNoHashHasher>, sel: std::marker::PhantomData, down: std::marker::PhantomData, @@ -229,10 +419,13 @@ impl< Down: IsMiningDownstream, > GeneralMiningSelector { + /// Creates a new `GeneralMiningSelector`. + /// + /// # Arguments + /// - `upstreams`: A vector of upstream nodes. pub fn new(upstreams: Vec>>) -> Self { let mut id_to_upstream = HashMap::with_hasher(BuildNoHashHasher::default()); for up in &upstreams { - // Is ok to unwrap safe_lock result id_to_upstream.insert(up.safe_lock(|u| u.get_id()).unwrap(), up.clone()); } Self { @@ -242,10 +435,16 @@ impl< down: std::marker::PhantomData, } } + + /// Updates the list of upstream nodes. + /// + /// # Arguments + /// - `upstreams`: The new list of upstream nodes. pub fn update_upstreams(&mut self, upstreams: Vec>>) { self.upstreams = upstreams; } } + impl< Sel: DownstreamMiningSelector, Down: IsMiningDownstream, @@ -260,9 +459,14 @@ impl< Up: IsMiningUpstream, > UpstreamMiningSelctor for GeneralMiningSelector { - /// Return the set of mining upstream nodes that can accept messages from a downstream with - /// the passed PairSettings and the sum of all the accepted flags - #[allow(clippy::type_complexity)] + /// Handles the `SetupConnection` process and determines the pairable upstream nodes. + /// + /// # Arguments + /// - `pair_settings`: The settings for pairing downstream and upstream nodes. + /// + /// # Returns + /// - `Ok((Vec>>, u32))`: Pairable upstream nodes and their combined flags. + /// - `Err`: If no upstreams are pairable. fn on_setup_connection( &mut self, pair_settings: &PairSettings, @@ -272,10 +476,8 @@ impl< for node in &self.upstreams { let is_pairable = node .safe_lock(|node| node.is_pairable(pair_settings)) - // Is ok to unwrap safe_lock result .unwrap(); if is_pairable { - // Is ok to unwrap safe_lock result supported_flags |= node.safe_lock(|n| n.get_flags()).unwrap(); supported_upstreams.push(node.clone()); } @@ -287,6 +489,14 @@ impl< Err(Error::NoPairableUpstream((2, 2, 0))) } + /// Retrieves an upstream node by its ID. + /// + /// # Arguments + /// - `upstream_id`: The unique ID of the upstream node. + /// + /// # Returns + /// - `Some`: The upstream node. + /// - `None`: If no upstream is found. fn get_upstream(&self, upstream_id: u32) -> Option>> { self.id_to_upstream.get(&upstream_id).cloned() }