reth_rpc_server_types/
module.rs

1use std::{collections::HashSet, fmt, str::FromStr};
2
3use serde::{Deserialize, Serialize, Serializer};
4use strum::{AsRefStr, EnumIter, IntoStaticStr, ParseError, VariantArray, VariantNames};
5
6/// Describes the modules that should be installed.
7///
8/// # Example
9///
10/// Create a [`RpcModuleSelection`] from a selection.
11///
12/// ```
13/// use reth_rpc_server_types::{RethRpcModule, RpcModuleSelection};
14/// let config: RpcModuleSelection = vec![RethRpcModule::Eth].into();
15/// ```
16#[derive(Debug, Default, Clone, Eq, PartialEq)]
17pub enum RpcModuleSelection {
18    /// Use _all_ available modules.
19    All,
20    /// The default modules `eth`, `net`, `web3`
21    #[default]
22    Standard,
23    /// Only use the configured modules.
24    Selection(HashSet<RethRpcModule>),
25}
26
27// === impl RpcModuleSelection ===
28
29impl RpcModuleSelection {
30    /// The standard modules to instantiate by default `eth`, `net`, `web3`
31    pub const STANDARD_MODULES: [RethRpcModule; 3] =
32        [RethRpcModule::Eth, RethRpcModule::Net, RethRpcModule::Web3];
33
34    /// Returns a selection of [`RethRpcModule`] with all [`RethRpcModule::all_variants`].
35    pub fn all_modules() -> HashSet<RethRpcModule> {
36        RethRpcModule::modules().into_iter().collect()
37    }
38
39    /// Returns the [`RpcModuleSelection::STANDARD_MODULES`] as a selection.
40    pub fn standard_modules() -> HashSet<RethRpcModule> {
41        HashSet::from(Self::STANDARD_MODULES)
42    }
43
44    /// All modules that are available by default on IPC.
45    ///
46    /// By default all modules are available on IPC.
47    pub fn default_ipc_modules() -> HashSet<RethRpcModule> {
48        Self::all_modules()
49    }
50
51    /// Creates a new _unique_ [`RpcModuleSelection::Selection`] from the given items.
52    ///
53    /// # Note
54    ///
55    /// This will dedupe the selection and remove duplicates while preserving the order.
56    ///
57    /// # Example
58    ///
59    /// Create a selection from the [`RethRpcModule`] string identifiers
60    ///
61    /// ```
62    /// use reth_rpc_server_types::{RethRpcModule, RpcModuleSelection};
63    /// let selection = vec!["eth", "admin"];
64    /// let config = RpcModuleSelection::try_from_selection(selection).unwrap();
65    /// assert_eq!(config, RpcModuleSelection::from([RethRpcModule::Eth, RethRpcModule::Admin]));
66    /// ```
67    ///
68    /// Create a unique selection from the [`RethRpcModule`] string identifiers
69    ///
70    /// ```
71    /// use reth_rpc_server_types::{RethRpcModule, RpcModuleSelection};
72    /// let selection = vec!["eth", "admin", "eth", "admin"];
73    /// let config = RpcModuleSelection::try_from_selection(selection).unwrap();
74    /// assert_eq!(config, RpcModuleSelection::from([RethRpcModule::Eth, RethRpcModule::Admin]));
75    /// ```
76    pub fn try_from_selection<I, T>(selection: I) -> Result<Self, T::Error>
77    where
78        I: IntoIterator<Item = T>,
79        T: TryInto<RethRpcModule>,
80    {
81        selection.into_iter().map(TryInto::try_into).collect()
82    }
83
84    /// Returns the number of modules in the selection
85    pub fn len(&self) -> usize {
86        match self {
87            Self::All => RethRpcModule::variant_count(),
88            Self::Standard => Self::STANDARD_MODULES.len(),
89            Self::Selection(s) => s.len(),
90        }
91    }
92
93    /// Returns true if no selection is configured
94    pub fn is_empty(&self) -> bool {
95        match self {
96            Self::Selection(sel) => sel.is_empty(),
97            _ => false,
98        }
99    }
100
101    /// Returns an iterator over all configured [`RethRpcModule`]
102    pub fn iter_selection(&self) -> Box<dyn Iterator<Item = RethRpcModule> + '_> {
103        match self {
104            Self::All => Box::new(RethRpcModule::modules().into_iter()),
105            Self::Standard => Box::new(Self::STANDARD_MODULES.iter().copied()),
106            Self::Selection(s) => Box::new(s.iter().copied()),
107        }
108    }
109
110    /// Clones the set of configured [`RethRpcModule`].
111    pub fn to_selection(&self) -> HashSet<RethRpcModule> {
112        match self {
113            Self::All => Self::all_modules(),
114            Self::Standard => Self::standard_modules(),
115            Self::Selection(s) => s.clone(),
116        }
117    }
118
119    /// Converts the selection into a [`HashSet`].
120    pub fn into_selection(self) -> HashSet<RethRpcModule> {
121        match self {
122            Self::All => Self::all_modules(),
123            Self::Standard => Self::standard_modules(),
124            Self::Selection(s) => s,
125        }
126    }
127
128    /// Returns true if both selections are identical.
129    pub fn are_identical(http: Option<&Self>, ws: Option<&Self>) -> bool {
130        match (http, ws) {
131            // Shortcut for common case to avoid iterating later
132            (Some(Self::All), Some(other)) | (Some(other), Some(Self::All)) => {
133                other.len() == RethRpcModule::variant_count()
134            }
135
136            // If either side is disabled, then the other must be empty
137            (Some(some), None) | (None, Some(some)) => some.is_empty(),
138
139            (Some(http), Some(ws)) => http.to_selection() == ws.to_selection(),
140            (None, None) => true,
141        }
142    }
143
144    /// Returns true if the selection contains the given module.
145    pub fn contains(&self, module: &RethRpcModule) -> bool {
146        match self {
147            Self::All => true,
148            Self::Standard => Self::STANDARD_MODULES.contains(module),
149            Self::Selection(s) => s.contains(module),
150        }
151    }
152}
153
154impl From<&HashSet<RethRpcModule>> for RpcModuleSelection {
155    fn from(s: &HashSet<RethRpcModule>) -> Self {
156        Self::from(s.clone())
157    }
158}
159
160impl From<HashSet<RethRpcModule>> for RpcModuleSelection {
161    fn from(s: HashSet<RethRpcModule>) -> Self {
162        Self::Selection(s)
163    }
164}
165
166impl From<&[RethRpcModule]> for RpcModuleSelection {
167    fn from(s: &[RethRpcModule]) -> Self {
168        Self::Selection(s.iter().copied().collect())
169    }
170}
171
172impl From<Vec<RethRpcModule>> for RpcModuleSelection {
173    fn from(s: Vec<RethRpcModule>) -> Self {
174        Self::Selection(s.into_iter().collect())
175    }
176}
177
178impl<const N: usize> From<[RethRpcModule; N]> for RpcModuleSelection {
179    fn from(s: [RethRpcModule; N]) -> Self {
180        Self::Selection(s.iter().copied().collect())
181    }
182}
183
184impl<'a> FromIterator<&'a RethRpcModule> for RpcModuleSelection {
185    fn from_iter<I>(iter: I) -> Self
186    where
187        I: IntoIterator<Item = &'a RethRpcModule>,
188    {
189        iter.into_iter().copied().collect()
190    }
191}
192
193impl FromIterator<RethRpcModule> for RpcModuleSelection {
194    fn from_iter<I>(iter: I) -> Self
195    where
196        I: IntoIterator<Item = RethRpcModule>,
197    {
198        Self::Selection(iter.into_iter().collect())
199    }
200}
201
202impl FromStr for RpcModuleSelection {
203    type Err = ParseError;
204
205    fn from_str(s: &str) -> Result<Self, Self::Err> {
206        if s.is_empty() {
207            return Ok(Self::Selection(Default::default()))
208        }
209        let mut modules = s.split(',').map(str::trim).peekable();
210        let first = modules.peek().copied().ok_or(ParseError::VariantNotFound)?;
211        // We convert to lowercase to make the comparison case-insensitive
212        //
213        // This is a way to allow typing "all" and "ALL" and "All" and "aLl" etc.
214        match first.to_lowercase().as_str() {
215            "all" => Ok(Self::All),
216            "none" => Ok(Self::Selection(Default::default())),
217            _ => Self::try_from_selection(modules),
218        }
219    }
220}
221
222impl fmt::Display for RpcModuleSelection {
223    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
224        write!(
225            f,
226            "[{}]",
227            self.iter_selection().map(|s| s.to_string()).collect::<Vec<_>>().join(", ")
228        )
229    }
230}
231
232/// Represents RPC modules that are supported by reth
233#[derive(
234    Debug,
235    Clone,
236    Copy,
237    Eq,
238    PartialEq,
239    Hash,
240    AsRefStr,
241    IntoStaticStr,
242    VariantNames,
243    VariantArray,
244    EnumIter,
245    Deserialize,
246)]
247#[serde(rename_all = "snake_case")]
248#[strum(serialize_all = "kebab-case")]
249pub enum RethRpcModule {
250    /// `admin_` module
251    Admin,
252    /// `debug_` module
253    Debug,
254    /// `eth_` module
255    Eth,
256    /// `net_` module
257    Net,
258    /// `trace_` module
259    Trace,
260    /// `txpool_` module
261    Txpool,
262    /// `web3_` module
263    Web3,
264    /// `rpc_` module
265    Rpc,
266    /// `reth_` module
267    Reth,
268    /// `ots_` module
269    Ots,
270    /// `flashbots_` module
271    Flashbots,
272    /// `miner_` module
273    Miner,
274}
275
276// === impl RethRpcModule ===
277
278impl RethRpcModule {
279    /// Returns the number of variants in the enum
280    pub const fn variant_count() -> usize {
281        <Self as VariantArray>::VARIANTS.len()
282    }
283
284    /// Returns all variant names of the enum
285    pub const fn all_variant_names() -> &'static [&'static str] {
286        <Self as VariantNames>::VARIANTS
287    }
288
289    /// Returns all variants of the enum
290    pub const fn all_variants() -> &'static [Self] {
291        <Self as VariantArray>::VARIANTS
292    }
293
294    /// Returns all variants of the enum
295    pub fn modules() -> impl IntoIterator<Item = Self> {
296        use strum::IntoEnumIterator;
297        Self::iter()
298    }
299
300    /// Returns the string representation of the module.
301    #[inline]
302    pub fn as_str(&self) -> &'static str {
303        self.into()
304    }
305}
306
307impl FromStr for RethRpcModule {
308    type Err = ParseError;
309
310    fn from_str(s: &str) -> Result<Self, Self::Err> {
311        Ok(match s {
312            "admin" => Self::Admin,
313            "debug" => Self::Debug,
314            "eth" => Self::Eth,
315            "net" => Self::Net,
316            "trace" => Self::Trace,
317            "txpool" => Self::Txpool,
318            "web3" => Self::Web3,
319            "rpc" => Self::Rpc,
320            "reth" => Self::Reth,
321            "ots" => Self::Ots,
322            "flashbots" => Self::Flashbots,
323            "miner" => Self::Miner,
324            _ => return Err(ParseError::VariantNotFound),
325        })
326    }
327}
328
329impl TryFrom<&str> for RethRpcModule {
330    type Error = ParseError;
331    fn try_from(s: &str) -> Result<Self, <Self as TryFrom<&str>>::Error> {
332        FromStr::from_str(s)
333    }
334}
335
336impl fmt::Display for RethRpcModule {
337    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
338        f.pad(self.as_ref())
339    }
340}
341
342impl Serialize for RethRpcModule {
343    fn serialize<S>(&self, s: S) -> Result<S::Ok, S::Error>
344    where
345        S: Serializer,
346    {
347        s.serialize_str(self.as_ref())
348    }
349}
350
351#[cfg(test)]
352mod test {
353    use super::*;
354
355    #[test]
356    fn test_all_modules() {
357        let all_modules = RpcModuleSelection::all_modules();
358        assert_eq!(all_modules.len(), RethRpcModule::variant_count());
359    }
360
361    #[test]
362    fn test_standard_modules() {
363        let standard_modules = RpcModuleSelection::standard_modules();
364        let expected_modules: HashSet<RethRpcModule> =
365            HashSet::from([RethRpcModule::Eth, RethRpcModule::Net, RethRpcModule::Web3]);
366        assert_eq!(standard_modules, expected_modules);
367    }
368
369    #[test]
370    fn test_default_ipc_modules() {
371        let default_ipc_modules = RpcModuleSelection::default_ipc_modules();
372        assert_eq!(default_ipc_modules, RpcModuleSelection::all_modules());
373    }
374
375    #[test]
376    fn test_try_from_selection_success() {
377        let selection = vec!["eth", "admin"];
378        let config = RpcModuleSelection::try_from_selection(selection).unwrap();
379        assert_eq!(config, RpcModuleSelection::from([RethRpcModule::Eth, RethRpcModule::Admin]));
380    }
381
382    #[test]
383    fn test_rpc_module_selection_len() {
384        let all_modules = RpcModuleSelection::All;
385        let standard = RpcModuleSelection::Standard;
386        let selection = RpcModuleSelection::from([RethRpcModule::Eth, RethRpcModule::Admin]);
387
388        assert_eq!(all_modules.len(), RethRpcModule::variant_count());
389        assert_eq!(standard.len(), 3);
390        assert_eq!(selection.len(), 2);
391    }
392
393    #[test]
394    fn test_rpc_module_selection_is_empty() {
395        let empty_selection = RpcModuleSelection::from(HashSet::new());
396        assert!(empty_selection.is_empty());
397
398        let non_empty_selection = RpcModuleSelection::from([RethRpcModule::Eth]);
399        assert!(!non_empty_selection.is_empty());
400    }
401
402    #[test]
403    fn test_rpc_module_selection_iter_selection() {
404        let all_modules = RpcModuleSelection::All;
405        let standard = RpcModuleSelection::Standard;
406        let selection = RpcModuleSelection::from([RethRpcModule::Eth, RethRpcModule::Admin]);
407
408        assert_eq!(all_modules.iter_selection().count(), RethRpcModule::variant_count());
409        assert_eq!(standard.iter_selection().count(), 3);
410        assert_eq!(selection.iter_selection().count(), 2);
411    }
412
413    #[test]
414    fn test_rpc_module_selection_to_selection() {
415        let all_modules = RpcModuleSelection::All;
416        let standard = RpcModuleSelection::Standard;
417        let selection = RpcModuleSelection::from([RethRpcModule::Eth, RethRpcModule::Admin]);
418
419        assert_eq!(all_modules.to_selection(), RpcModuleSelection::all_modules());
420        assert_eq!(standard.to_selection(), RpcModuleSelection::standard_modules());
421        assert_eq!(
422            selection.to_selection(),
423            HashSet::from([RethRpcModule::Eth, RethRpcModule::Admin])
424        );
425    }
426
427    #[test]
428    fn test_rpc_module_selection_are_identical() {
429        // Test scenario: both selections are `All`
430        //
431        // Since both selections include all possible RPC modules, they should be considered
432        // identical.
433        let all_modules = RpcModuleSelection::All;
434        assert!(RpcModuleSelection::are_identical(Some(&all_modules), Some(&all_modules)));
435
436        // Test scenario: both `http` and `ws` are `None`
437        //
438        // When both arguments are `None`, the function should return `true` because no modules are
439        // selected.
440        assert!(RpcModuleSelection::are_identical(None, None));
441
442        // Test scenario: both selections contain identical sets of specific modules
443        //
444        // In this case, both selections contain the same modules (`Eth` and `Admin`),
445        // so they should be considered identical.
446        let selection1 = RpcModuleSelection::from([RethRpcModule::Eth, RethRpcModule::Admin]);
447        let selection2 = RpcModuleSelection::from([RethRpcModule::Eth, RethRpcModule::Admin]);
448        assert!(RpcModuleSelection::are_identical(Some(&selection1), Some(&selection2)));
449
450        // Test scenario: one selection is `All`, the other is `Standard`
451        //
452        // `All` includes all possible modules, while `Standard` includes a specific set of modules.
453        // Since `Standard` does not cover all modules, these two selections should not be
454        // considered identical.
455        let standard = RpcModuleSelection::Standard;
456        assert!(!RpcModuleSelection::are_identical(Some(&all_modules), Some(&standard)));
457
458        // Test scenario: one is `None`, the other is an empty selection
459        //
460        // When one selection is `None` and the other is an empty selection (no modules),
461        // they should be considered identical because neither selects any modules.
462        let empty_selection = RpcModuleSelection::Selection(HashSet::new());
463        assert!(RpcModuleSelection::are_identical(None, Some(&empty_selection)));
464        assert!(RpcModuleSelection::are_identical(Some(&empty_selection), None));
465
466        // Test scenario: one is `None`, the other is a non-empty selection
467        //
468        // If one selection is `None` and the other contains modules, they should not be considered
469        // identical because `None` represents no selection, while the other explicitly
470        // selects modules.
471        let non_empty_selection = RpcModuleSelection::from([RethRpcModule::Eth]);
472        assert!(!RpcModuleSelection::are_identical(None, Some(&non_empty_selection)));
473        assert!(!RpcModuleSelection::are_identical(Some(&non_empty_selection), None));
474
475        // Test scenario: `All` vs. non-full selection
476        //
477        // If one selection is `All` (which includes all modules) and the other contains only a
478        // subset of modules, they should not be considered identical.
479        let partial_selection = RpcModuleSelection::from([RethRpcModule::Eth, RethRpcModule::Net]);
480        assert!(!RpcModuleSelection::are_identical(Some(&all_modules), Some(&partial_selection)));
481
482        // Test scenario: full selection vs `All`
483        //
484        // If the other selection explicitly selects all available modules, it should be identical
485        // to `All`.
486        let full_selection =
487            RpcModuleSelection::from(RethRpcModule::modules().into_iter().collect::<HashSet<_>>());
488        assert!(RpcModuleSelection::are_identical(Some(&all_modules), Some(&full_selection)));
489
490        // Test scenario: different non-empty selections
491        //
492        // If the two selections contain different sets of modules, they should not be considered
493        // identical.
494        let selection3 = RpcModuleSelection::from([RethRpcModule::Eth, RethRpcModule::Net]);
495        let selection4 = RpcModuleSelection::from([RethRpcModule::Eth, RethRpcModule::Web3]);
496        assert!(!RpcModuleSelection::are_identical(Some(&selection3), Some(&selection4)));
497
498        // Test scenario: `Standard` vs an equivalent selection
499        // The `Standard` selection includes a predefined set of modules. If we explicitly create
500        // a selection with the same set of modules, they should be considered identical.
501        let matching_standard =
502            RpcModuleSelection::from([RethRpcModule::Eth, RethRpcModule::Net, RethRpcModule::Web3]);
503        assert!(RpcModuleSelection::are_identical(Some(&standard), Some(&matching_standard)));
504
505        // Test scenario: `Standard` vs non-matching selection
506        //
507        // If the selection does not match the modules included in `Standard`, they should not be
508        // considered identical.
509        let non_matching_standard =
510            RpcModuleSelection::from([RethRpcModule::Eth, RethRpcModule::Net]);
511        assert!(!RpcModuleSelection::are_identical(Some(&standard), Some(&non_matching_standard)));
512    }
513
514    #[test]
515    fn test_rpc_module_selection_from_str() {
516        // Test empty string returns default selection
517        let result = RpcModuleSelection::from_str("");
518        assert!(result.is_ok());
519        assert_eq!(result.unwrap(), RpcModuleSelection::Selection(Default::default()));
520
521        // Test "all" (case insensitive) returns All variant
522        let result = RpcModuleSelection::from_str("all");
523        assert!(result.is_ok());
524        assert_eq!(result.unwrap(), RpcModuleSelection::All);
525
526        let result = RpcModuleSelection::from_str("All");
527        assert!(result.is_ok());
528        assert_eq!(result.unwrap(), RpcModuleSelection::All);
529
530        let result = RpcModuleSelection::from_str("ALL");
531        assert!(result.is_ok());
532        assert_eq!(result.unwrap(), RpcModuleSelection::All);
533
534        // Test "none" (case insensitive) returns empty selection
535        let result = RpcModuleSelection::from_str("none");
536        assert!(result.is_ok());
537        assert_eq!(result.unwrap(), RpcModuleSelection::Selection(Default::default()));
538
539        let result = RpcModuleSelection::from_str("None");
540        assert!(result.is_ok());
541        assert_eq!(result.unwrap(), RpcModuleSelection::Selection(Default::default()));
542
543        let result = RpcModuleSelection::from_str("NONE");
544        assert!(result.is_ok());
545        assert_eq!(result.unwrap(), RpcModuleSelection::Selection(Default::default()));
546
547        // Test valid selections: "eth,admin"
548        let result = RpcModuleSelection::from_str("eth,admin");
549        assert!(result.is_ok());
550        let expected_selection =
551            RpcModuleSelection::from([RethRpcModule::Eth, RethRpcModule::Admin]);
552        assert_eq!(result.unwrap(), expected_selection);
553
554        // Test valid selection with extra spaces: " eth , admin "
555        let result = RpcModuleSelection::from_str(" eth , admin ");
556        assert!(result.is_ok());
557        assert_eq!(result.unwrap(), expected_selection);
558
559        // Test invalid selection should return error
560        let result = RpcModuleSelection::from_str("invalid,unknown");
561        assert!(result.is_err());
562        assert_eq!(result.unwrap_err(), ParseError::VariantNotFound);
563
564        // Test single valid selection: "eth"
565        let result = RpcModuleSelection::from_str("eth");
566        assert!(result.is_ok());
567        let expected_selection = RpcModuleSelection::from([RethRpcModule::Eth]);
568        assert_eq!(result.unwrap(), expected_selection);
569
570        // Test single invalid selection: "unknown"
571        let result = RpcModuleSelection::from_str("unknown");
572        assert!(result.is_err());
573        assert_eq!(result.unwrap_err(), ParseError::VariantNotFound);
574    }
575}