Skip to main content

reth_node_core/args/
dev.rs

1//! clap [Args](clap::Args) for Dev testnet configuration
2
3use std::{num::NonZeroUsize, sync::OnceLock, time::Duration};
4
5use clap::{builder::Resettable, Args};
6use humantime::{format_duration, parse_duration};
7use reth_engine_local::DEFAULT_FINALITY_DEPTH;
8
9const DEFAULT_MNEMONIC: &str = "test test test test test test test test test test test junk";
10
11/// Global static dev testnet defaults
12static DEV_DEFAULTS: OnceLock<DefaultDevArgs> = OnceLock::new();
13
14/// Parameters for Dev testnet configuration
15#[derive(Debug, Args, PartialEq, Eq, Clone)]
16#[command(next_help_heading = "Dev testnet")]
17pub struct DevArgs {
18    /// Start the node in dev mode
19    ///
20    /// This mode uses a local proof-of-authority consensus engine with either fixed block times
21    /// or automatically mined blocks.
22    /// Disables network discovery and enables local http server.
23    /// Prefunds 20 accounts derived by mnemonic "test test test test test test test test test test
24    /// test junk" with 10 000 ETH each.
25    #[arg(long = "dev", alias = "auto-mine", help_heading = "Dev testnet", default_value_t = DefaultDevArgs::get_global().dev, verbatim_doc_comment)]
26    pub dev: bool,
27
28    /// How many transactions to mine per block.
29    #[arg(
30        long = "dev.block-max-transactions",
31        help_heading = "Dev testnet",
32        conflicts_with = "block_time",
33        default_value = Resettable::from(DefaultDevArgs::get_global().block_max_transactions.map(|v| v.to_string().into()))
34    )]
35    pub block_max_transactions: Option<usize>,
36
37    /// Interval between blocks.
38    ///
39    /// Parses strings using [`humantime::parse_duration`]
40    /// --dev.block-time 12s
41    #[arg(
42        long = "dev.block-time",
43        help_heading = "Dev testnet",
44        conflicts_with = "block_max_transactions",
45        value_parser = parse_duration,
46        default_value = Resettable::from(DefaultDevArgs::get_global().block_time.map(|v| format_duration(v).to_string().into())),
47        verbatim_doc_comment
48    )]
49    pub block_time: Option<Duration>,
50
51    /// Number of confirmations required before a block is finalized.
52    ///
53    /// A depth of `1` finalizes the canonical head immediately.
54    #[arg(
55        long = "dev.finality-depth",
56        help_heading = "Dev testnet",
57        default_value_t = DefaultDevArgs::get_global().finality_depth,
58        verbatim_doc_comment
59    )]
60    pub finality_depth: NonZeroUsize,
61
62    /// Time to wait after initiating payload building before resolving.
63    ///
64    /// Introduces a sleep between `fork_choice_updated` and `resolve_kind` in the
65    /// local miner, giving the payload job time for multiple rebuild attempts with
66    /// new transactions from the pool.
67    ///
68    /// Parses strings using [`humantime::parse_duration`]
69    /// --dev.payload-wait-time 450ms
70    #[arg(
71        long = "dev.payload-wait-time",
72        help_heading = "Dev testnet",
73        value_parser = parse_duration,
74        default_value = Resettable::from(DefaultDevArgs::get_global().payload_wait_time.map(|v| format_duration(v).to_string().into())),
75        verbatim_doc_comment
76    )]
77    pub payload_wait_time: Option<Duration>,
78
79    /// Derive dev accounts from a fixed mnemonic instead of random ones.
80    #[arg(
81        long = "dev.mnemonic",
82        help_heading = "Dev testnet",
83        value_name = "MNEMONIC",
84        requires = "dev",
85        verbatim_doc_comment,
86        default_value_t = DefaultDevArgs::get_global().dev_mnemonic.clone()
87    )]
88    pub dev_mnemonic: String,
89}
90
91/// Default values for dev testnet CLI arguments that can be customized.
92///
93/// Global defaults can be set via [`DefaultDevArgs::try_init`].
94#[derive(Debug, Clone)]
95pub struct DefaultDevArgs {
96    /// Default for `--dev`.
97    pub dev: bool,
98    /// Default maximum number of transactions to mine per block.
99    pub block_max_transactions: Option<usize>,
100    /// Default interval between blocks.
101    pub block_time: Option<Duration>,
102    /// Default number of confirmations required before finalization.
103    pub finality_depth: NonZeroUsize,
104    /// Default time to wait before resolving a payload.
105    pub payload_wait_time: Option<Duration>,
106    /// Default mnemonic used to derive dev accounts.
107    pub dev_mnemonic: String,
108}
109
110impl DefaultDevArgs {
111    /// Initialize the global dev testnet defaults with this configuration.
112    pub fn try_init(self) -> Result<(), Self> {
113        DEV_DEFAULTS.set(self)
114    }
115
116    /// Get a reference to the global dev testnet defaults.
117    pub fn get_global() -> &'static Self {
118        DEV_DEFAULTS.get_or_init(Self::default)
119    }
120
121    /// Set whether dev mode is enabled by default.
122    pub const fn with_dev(mut self, dev: bool) -> Self {
123        self.dev = dev;
124        self
125    }
126
127    /// Set the default maximum number of transactions to mine per block.
128    pub const fn with_block_max_transactions(mut self, count: Option<usize>) -> Self {
129        self.block_max_transactions = count;
130        self
131    }
132
133    /// Set the default interval between blocks.
134    pub const fn with_block_time(mut self, block_time: Option<Duration>) -> Self {
135        self.block_time = block_time;
136        self
137    }
138
139    /// Set the default number of confirmations required before finalization.
140    pub const fn with_finality_depth(mut self, finality_depth: NonZeroUsize) -> Self {
141        self.finality_depth = finality_depth;
142        self
143    }
144
145    /// Set the default time to wait before resolving a payload.
146    pub const fn with_payload_wait_time(mut self, payload_wait_time: Option<Duration>) -> Self {
147        self.payload_wait_time = payload_wait_time;
148        self
149    }
150
151    /// Set the default mnemonic used to derive dev accounts.
152    pub fn with_dev_mnemonic(mut self, dev_mnemonic: String) -> Self {
153        self.dev_mnemonic = dev_mnemonic;
154        self
155    }
156}
157
158impl Default for DefaultDevArgs {
159    fn default() -> Self {
160        Self {
161            dev: false,
162            block_max_transactions: None,
163            block_time: None,
164            finality_depth: DEFAULT_FINALITY_DEPTH,
165            payload_wait_time: None,
166            dev_mnemonic: DEFAULT_MNEMONIC.to_string(),
167        }
168    }
169}
170
171impl Default for DevArgs {
172    fn default() -> Self {
173        let DefaultDevArgs {
174            dev,
175            block_max_transactions,
176            block_time,
177            finality_depth,
178            payload_wait_time,
179            dev_mnemonic,
180        } = DefaultDevArgs::get_global().clone();
181        Self {
182            dev,
183            block_max_transactions,
184            block_time,
185            finality_depth,
186            payload_wait_time,
187            dev_mnemonic,
188        }
189    }
190}
191
192#[cfg(test)]
193mod tests {
194    use super::*;
195    use clap::Parser;
196
197    /// A helper type to parse Args more easily
198    #[derive(Parser)]
199    struct CommandParser<T: Args> {
200        #[command(flatten)]
201        args: T,
202    }
203
204    #[test]
205    fn test_parse_dev_args() {
206        let args = CommandParser::<DevArgs>::parse_from(["reth"]).args;
207        assert_eq!(
208            args,
209            DevArgs {
210                dev: false,
211                block_max_transactions: None,
212                block_time: None,
213                finality_depth: DEFAULT_FINALITY_DEPTH,
214                payload_wait_time: None,
215                dev_mnemonic: DEFAULT_MNEMONIC.to_string(),
216            }
217        );
218
219        let args = CommandParser::<DevArgs>::parse_from(["reth", "--dev"]).args;
220        assert_eq!(
221            args,
222            DevArgs {
223                dev: true,
224                block_max_transactions: None,
225                block_time: None,
226                finality_depth: DEFAULT_FINALITY_DEPTH,
227                payload_wait_time: None,
228                dev_mnemonic: DEFAULT_MNEMONIC.to_string(),
229            }
230        );
231
232        let args = CommandParser::<DevArgs>::parse_from(["reth", "--auto-mine"]).args;
233        assert_eq!(
234            args,
235            DevArgs {
236                dev: true,
237                block_max_transactions: None,
238                block_time: None,
239                finality_depth: DEFAULT_FINALITY_DEPTH,
240                payload_wait_time: None,
241                dev_mnemonic: DEFAULT_MNEMONIC.to_string(),
242            }
243        );
244
245        let args = CommandParser::<DevArgs>::parse_from([
246            "reth",
247            "--dev",
248            "--dev.block-max-transactions",
249            "2",
250        ])
251        .args;
252        assert_eq!(
253            args,
254            DevArgs {
255                dev: true,
256                block_max_transactions: Some(2),
257                block_time: None,
258                finality_depth: DEFAULT_FINALITY_DEPTH,
259                payload_wait_time: None,
260                dev_mnemonic: DEFAULT_MNEMONIC.to_string(),
261            }
262        );
263
264        let args =
265            CommandParser::<DevArgs>::parse_from(["reth", "--dev", "--dev.block-time", "1s"]).args;
266        assert_eq!(
267            args,
268            DevArgs {
269                dev: true,
270                block_max_transactions: None,
271                block_time: Some(std::time::Duration::from_secs(1)),
272                finality_depth: DEFAULT_FINALITY_DEPTH,
273                payload_wait_time: None,
274                dev_mnemonic: DEFAULT_MNEMONIC.to_string(),
275            }
276        );
277
278        let args =
279            CommandParser::<DevArgs>::parse_from(["reth", "--dev", "--dev.finality-depth", "1"])
280                .args;
281        assert_eq!(args.finality_depth, NonZeroUsize::new(1).unwrap());
282    }
283
284    #[test]
285    fn test_rejects_zero_finality_depth() {
286        assert!(CommandParser::<DevArgs>::try_parse_from([
287            "reth",
288            "--dev",
289            "--dev.finality-depth",
290            "0",
291        ])
292        .is_err());
293    }
294
295    #[test]
296    fn test_parse_dev_args_conflicts() {
297        let args = CommandParser::<DevArgs>::try_parse_from([
298            "reth",
299            "--dev",
300            "--dev.block-max-transactions",
301            "2",
302            "--dev.block-time",
303            "1s",
304        ]);
305        assert!(args.is_err());
306    }
307
308    #[test]
309    fn dev_args_default_sanity_check() {
310        let default_args = DevArgs::default();
311        let args = CommandParser::<DevArgs>::parse_from(["reth"]).args;
312        assert_eq!(args, default_args);
313    }
314}