• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

Neptune-Crypto / neptune-core / 13860309462

14 Mar 2025 03:49PM UTC coverage: 84.226% (+0.05%) from 84.172%
13860309462

Pull #503

github

web-flow
Merge e38c78b24 into d55ef2e4a
Pull Request #503: feat: Allow restriction of number of inputs per tx

310 of 327 new or added lines in 10 files covered. (94.8%)

3 existing lines in 2 files now uncovered.

50923 of 60460 relevant lines covered (84.23%)

176374.24 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

78.06
/src/config_models/cli_args.rs
1
use std::net::IpAddr;
2
use std::net::SocketAddr;
3
use std::ops::RangeInclusive;
4
use std::path::PathBuf;
5
use std::sync::OnceLock;
6
use std::time::Duration;
7

8
use bytesize::ByteSize;
9
use clap::builder::RangedI64ValueParser;
10
use clap::builder::TypedValueParser;
11
use clap::Parser;
12
use itertools::Itertools;
13
use num_traits::Zero;
14
use sysinfo::System;
15

16
use super::network::Network;
17
use crate::job_queue::triton_vm::TritonVmJobPriority;
18
use crate::models::blockchain::type_scripts::native_currency_amount::NativeCurrencyAmount;
19
use crate::models::proof_abstractions::tasm::program::TritonVmProofJobOptions;
20
use crate::models::proof_abstractions::tasm::prover_job::ProverJobSettings;
21
use crate::models::state::tx_proving_capability::TxProvingCapability;
22
use crate::models::state::wallet::scan_mode_configuration::ScanModeConfiguration;
23

24
/// The `neptune-core` command-line program starts a Neptune node.
25
#[derive(Parser, Debug, Clone)]
26
#[clap(author, version, about)]
27
pub struct Args {
28
    /// The data directory that contains the wallet and blockchain state
29
    ///
30
    /// The default varies by operating system, and includes the network, e.g.
31
    ///
32
    /// Linux:   /home/alice/.config/neptune/core/main
33
    ///
34
    /// Windows: C:\Users\Alice\AppData\Roaming\neptune\core\main
35
    ///
36
    /// macOS:   /Users/Alice/Library/Application Support/neptune/main
37
    #[clap(long, value_name = "DIR")]
38
    pub(crate) data_dir: Option<PathBuf>,
39

40
    /// Ban connections to this node from IP address.
41
    ///
42
    /// This node can still make outgoing connections to IP address.
43
    ///
44
    /// To do this, see `--peers`.
45
    ///
46
    /// E.g.: --ban 1.2.3.4 --ban 5.6.7.8
47
    #[clap(long, value_name = "IP")]
48
    pub(crate) ban: Vec<IpAddr>,
×
49

50
    /// The threshold at which a peer's standing is considered “bad”. Current
51
    /// connections to peers in bad standing are terminated. Connection attempts
52
    /// from peers in bad standing are refused.
53
    ///
54
    /// For a list of reasons that cause bad standing, see
55
    /// [NegativePeerSanction](crate::models::peer::NegativePeerSanction).
56
    #[clap(
57
        long,
58
        default_value = "1000",
59
        value_name = "VALUE",
60
        value_parser = clap::value_parser!(u16).range(1..),
61
    )]
62
    pub(crate) peer_tolerance: u16,
×
63

64
    /// Maximum number of peers to accept connections from.
65
    ///
66
    /// Will not prevent outgoing connections made with `--peers`.
67
    /// Set this value to 0 to refuse all incoming connections.
68
    #[clap(
69
        long,
70
        default_value = "10",
71
        value_name = "COUNT",
72
        value_parser = clap::value_parser!(u16).map(|u| usize::from(u)),
797✔
73
    )]
74
    pub(crate) max_num_peers: usize,
×
75

76
    /// Maximum number of peers to accept from each IP address.
77
    ///
78
    /// Multiple nodes can run on the same IP address which would either mean
79
    /// that multiple nodes run on the same machine, or multiple machines are
80
    /// on the same network that uses Network Address Translation and has one
81
    /// public IP.
82
    #[clap(long)]
83
    pub(crate) max_connections_per_ip: Option<usize>,
84

85
    /// Whether to act as bootstrapper node.
86
    ///
87
    /// Bootstrapper nodes ensure that the maximum number of peers is never
88
    /// reached by disconnecting from existing peers when the maximum is about
89
    /// to be reached. As a result, they will respond with high likelihood to
90
    /// incoming connection requests -- in contrast to regular nodes, which
91
    /// refuse incoming connections when the max is reached.
92
    #[clap(long)]
93
    pub(crate) bootstrap: bool,
×
94

95
    /// If this flag is set, the node will refuse to initiate a transaction.
96
    /// This flag makes sense for machines whose resources are dedicated to
97
    /// composing, and which must do so in a regular and predictable manner,
98
    /// undisrupted by transaction initiation tasks. To spend funds from the
99
    /// wallet of a node where this flag is set, either restart it and drop the
100
    /// flag, or else copy the wallet file to another machine.
101
    #[clap(long, alias = "notx")]
102
    pub(crate) no_transaction_initiation: bool,
×
103

104
    /// Whether to produce block proposals, which is the first step of two-step
105
    /// mining. Note that composing block proposals involves the computationally
106
    /// expensive task of producing STARK proofs. You should have plenty of
107
    /// cores and probably at least 128 GB of RAM.
108
    #[clap(long)]
109
    pub(crate) compose: bool,
×
110

111
    /// Whether to engage in guess-nonce-and-hash, which is the second step in
112
    /// two-step mining. If this flag is set and the `compose` flag is not set,
113
    /// then the client will rely on block proposals from other nodes. In this
114
    /// case, it will always pick the most profitable block proposal.
115
    ///
116
    /// If this flag is set and the `compose` flag is set, then the client will
117
    /// always guess on their own block proposal.
118
    #[clap(long)]
119
    pub(crate) guess: bool,
×
120

121
    /// By default, a composer will share block proposals with all peers. If
122
    /// this flag is set, the composer will *not* share their block proposals.
123
    #[clap(long)]
124
    pub(crate) secret_compositions: bool,
×
125

126
    /// Regulates the fraction of the block subsidy that goes to the guesser.
127
    /// Value must be between 0 and 1.
128
    ///
129
    /// The remainder goes to the composer. This flag is ignored if the
130
    /// `compose` flag is not set.
131
    #[clap(long, default_value = "0.5", value_parser = fraction_validator)]
132
    pub(crate) guesser_fraction: f64,
×
133

134
    /// Whether to sleep between nonce-guesses. Useful if you do not want to
135
    /// dedicate all your CPU power.
136
    #[clap(long)]
137
    pub(crate) sleepy_guessing: bool,
×
138

139
    /// Set the number of threads to use while guessing. When no value is set,
140
    /// the number is set to the number of available cores.
141
    #[clap(long)]
142
    pub(crate) guesser_threads: Option<usize>,
143

144
    /// Determines the fraction of the transaction fee consumed by this node as
145
    /// a reward either for upgrading a `ProofCollection` to `SingleProof`, or
146
    /// for merging two `SingleProof`s.
147
    #[clap(long, default_value = "0.2", value_parser = fraction_validator)]
148
    pub(crate) gobbling_fraction: f64,
×
149

150
    /// Determines the minimum fee to take as a reward for upgrading foreign
151
    /// transaction proofs. Foreign transactions where a fee below this
152
    /// threshold cannot be collected by proof upgrading will not be upgraded.
153
    #[clap(long, default_value = "0.01", value_parser = NativeCurrencyAmount::coins_from_str)]
154
    pub(crate) min_gobbling_fee: NativeCurrencyAmount,
×
155

156
    /// Prune the mempool when it exceeds this size in RAM.
157
    ///
158
    /// Units: B (bytes), K (kilobytes), M (megabytes), G (gigabytes)
159
    ///
160
    /// E.g. --max-mempool-size 500M
161
    #[clap(long, default_value = "1G", value_name = "SIZE")]
162
    pub(crate) max_mempool_size: ByteSize,
×
163

164
    /// Maximum number of transactions permitted in the mempool.
165
    ///
166
    /// If too much time is spent updating transaction proofs, this
167
    /// value can be capped.
168
    ///
169
    /// E.g. --max-mempool-num-tx=4
170
    #[clap(long)]
171
    pub(crate) max_mempool_num_tx: Option<usize>,
172

173
    /// Restrict the number of inputs for transcations that this node deals
174
    /// with.
175
    ///
176
    /// The node will not create transactions with more inputs than this limit.
177
    /// It will also not accept such transactions from peers into its mempool.
178
    /// And if the node is either upgrading transaction proofs on the network
179
    /// or composing, it will never merge two transactions that would exceed
180
    /// this limit. Does not affect block validity, i.e. consensus: a block with
181
    /// more than this number of inputs can be valid. Also does not affect which
182
    /// which block proposals that a guessing node will attempt to solve the PoW
183
    /// puzzle for.
184
    #[clap(long, default_value = "28")]
NEW
185
    pub(crate) max_num_inputs_per_tx: usize,
×
186

187
    /// Port on which to listen for peer connections.
188
    #[clap(long, default_value = "9798", value_name = "PORT")]
189
    pub(crate) peer_port: u16,
×
190

191
    /// Port on which to listen for RPC connections.
192
    #[clap(long, default_value = "9799", value_name = "PORT")]
193
    pub(crate) rpc_port: u16,
×
194

195
    /// IP on which to listen for peer connections. Will default to all network interfaces, IPv4 and IPv6.
196
    #[clap(short, long, default_value = "::")]
197
    pub(crate) listen_addr: IpAddr,
×
198

199
    /// Maximum number of blocks that the client can catch up to without going
200
    /// into sync mode.
201
    ///
202
    /// Default: 1000.
203
    ///
204
    /// The process running this program should have access to enough RAM: at
205
    /// least the number of blocks set by this argument multiplied with the max
206
    /// block size (around 2 MB). Probably 1.5 to 2 times that amount for good
207
    /// margin.
208
    // Notice that the minimum value here may not be less than
209
    // [SYNC_CHALLENGE_POW_WITNESS_LENGTH](crate::models::peer::SYNC_CHALLENGE_POW_WITNESS_LENGTH)
210
    // as that would prevent going into sync mode.
211
    #[clap(long, default_value = "1000", value_parser(RangedI64ValueParser::<usize>::new().range(10..100000)))]
212
    pub(crate) sync_mode_threshold: usize,
×
213

214
    /// IPs of nodes to connect to, e.g.: --peers 8.8.8.8:9798 --peers 8.8.4.4:1337.
215
    #[structopt(long)]
216
    pub(crate) peers: Vec<SocketAddr>,
×
217

218
    /// Specify network, `main`, `alpha`, `beta`, `testnet`, or `regtest`
219
    #[structopt(long, default_value = "main", short)]
220
    pub(crate) network: Network,
×
221

222
    /// Max number of membership proofs stored per owned UTXO
223
    #[structopt(long, default_value = "3")]
224
    pub(crate) number_of_mps_per_utxo: usize,
×
225

226
    /// Configure how complicated proofs this machine is capable of producing.
227
    /// If no value is set, this parameter is estimated. For privacy, this level
228
    /// must not be set to [`TxProvingCapability::LockScript`], as this leaks
229
    /// information about amounts and input/output UTXOs.
230
    ///
231
    /// Proving the lockscripts is mandatory, since this is what prevents others
232
    /// from spending your coins.
233
    ///
234
    /// e.g. `--tx-proving-capability=singleproof` or
235
    /// `--tx-proving-capability=proofcollection`.
236
    #[clap(long)]
237
    pub(crate) tx_proving_capability: Option<TxProvingCapability>,
238

239
    /// Cache for the proving capability. If the above parameter is not set, we
240
    /// want to estimate proving capability and afterwards reuse the result from
241
    /// previous estimations. This argument cannot be set from CLI, so clap
242
    /// ignores it.
243
    #[clap(skip)]
244
    pub(crate) tx_proving_capability_cache: OnceLock<TxProvingCapability>,
245

246
    /// The number of seconds between each attempt to upgrade transactions in
247
    /// the mempool to proofs of a higher quality. Will only run if the machine
248
    /// on which the client runs is powerful enough to produce `SingleProof`s.
249
    ///
250
    /// Set to 0 to never perform this task.
251
    #[structopt(long, default_value = "1800")]
252
    pub(crate) tx_proof_upgrade_interval: u64,
×
253

254
    /// Enable tokio tracing for consumption by the tokio-console application
255
    /// note: this will attempt to connect to localhost:6669
256
    #[structopt(long, name = "tokio-console", default_value = "false")]
257
    pub tokio_console: bool,
×
258

259
    /// Sets the max program complexity limit for proof creation in Triton VM.
260
    ///
261
    /// Triton VM's prover complexity is a function of something called padded height
262
    /// which is always a power of two. A basic proof has a complexity of 2^11.
263
    /// A powerful machine in 2024 with 128 CPU cores can handle a padded height of 2^23.
264
    ///
265
    /// For such a machine, one would set a limit of 23.
266
    ///
267
    /// if the limit is reached while mining, a warning is logged and mining will pause.
268
    /// non-mining operations may panic and halt neptune-core
269
    ///
270
    /// no limit is applied if unset.
271
    #[structopt(long, short, value_parser = clap::value_parser!(u8).range(10..32))]
272
    pub(crate) max_log2_padded_height_for_proofs: Option<u8>,
273

274
    /// Sets the maximum number of proofs in a `ProofCollection` that can be
275
    /// recursively combined into a `SingleProof` by this machine. I.e. how big
276
    /// STARK proofs this machine can produce.
277
    #[clap(long, default_value = "16")]
278
    pub(crate) max_num_proofs: usize,
×
279

280
    /// Disables the cookie_hint RPC API
281
    ///
282
    /// client software can ask for a cookie hint to automatically determine the
283
    /// root data directory used by a running node, which enables loading a
284
    /// cookie file for authentication.
285
    ///
286
    /// Exposing the data directory leaks some privacy. Disable to prevent.
287
    #[clap(long)]
288
    pub disable_cookie_hint: bool,
×
289

290
    /// The duration (in seconds) during which new connection attempts from peers
291
    /// are ignored after a connection to them was closed.
292
    ///
293
    /// Does not affect abnormally closed connections. For example, if a connection
294
    /// is dropped due to networking issues, an immediate reconnection attempt is
295
    /// not affected by this cooldown.
296
    //
297
    // The default should be larger than the default interval between peer discovery
298
    // to meaningfully suppress rapid reconnection attempts.
299
    #[clap(long, default_value = "1800", value_parser = duration_from_seconds_str)]
300
    pub reconnect_cooldown: Duration,
×
301

302
    /// Scan incoming blocks for inbound transactions.
303
    ///
304
    /// Keys are generated deterministically from the secret seed and a
305
    /// derivation index, and a counter recording the most recently used index
306
    /// is stored. By default, incoming blocks are scanned for inbound
307
    /// transactions tied to any key derived from indices smaller than this
308
    /// counter.
309
    ///
310
    /// However, this counter can be lost, for instance after importing the
311
    /// secret seed onto a new machine. In such cases, this subcommand will
312
    /// instruct the client to scan incoming blocks for transactions tied to
313
    /// future derivation indices --
314
    /// [`ScanModeConfiguration`]`::default().num_future_keys()` by default, but
315
    /// this parameter can be adjusted with the `--scan-keys` subcommand.
316
    ///
317
    /// The argument to this subcommand is the range of blocks where this extra-
318
    /// ordinary scanning step takes place. If no argument is supplied, the step
319
    /// takes place for every incoming block.
320
    ///
321
    /// Examples:
322
    ///  - `neptune-core --scan-blocks ..` (scan all blocks; this is the
323
    ///    default)
324
    ///  - `neptune-core --scan-blocks ..1337` (everything up to 1337)
325
    ///  - `neptune-core --scan-blocks 1337..` (1337 and everything after)
326
    ///  - `neptune-core --scan-blocks 13..=37` (13, 37, and everything in
327
    ///    between)
328
    ///  - `neptune-core --scan-blocks 13:37` (python index ranges also work)
329
    //
330
    // Everything above should constitute the help documentation for this
331
    // command, with the exception of the concrete value for the default number
332
    // of future indices. To present the user with that piece of information,
333
    // we override this docstring by setting `long_help`, which allows us to
334
    // invoke `format!` and embed the integer.
335
    #[clap(long, value_parser = parse_range, action = clap::ArgAction::Set,
336
        num_args = 0..=1, long_help = format!(
337
            "\
338
    Keys are generated deterministically from the secret seed and a\n\
339
    derivation index, and a counter recording the most recently used index\n\
340
    is stored. By default, incoming blocks are scanned for inbound\n\
341
    transactions tied to any key derived from indices smaller than this\n\
342
    counter.\n\
343
    \n\
344
    However, this counter can be lost, for instance after importing the\n\
345
    secret seed onto a new machine. In such cases, this subcommand will\n\
346
    instruct the client to scan incoming blocks for transactions tied to\n\
347
    future derivation indices -- {} by default, but this parameter can be\n\
348
    adjusted with the `--scan-keys` subcommand.\n\
349
    \n\
350
    The argument to this subcommand is the range of blocks where this extra-\n\
351
    ordinary scanning step takes place. If no argument is supplied, the step\n\
352
    takes place for every incoming block.\n\
353
    \n\
354
    Examples: \n\
355
     - `neptune-core --scan-blocks ..` (scan all blocks; this is the default)\n\
356
     - `neptune-core --scan-blocks ..1337` (everything up to 1337)\n\
357
     - `neptune-core --scan-blocks 1337..` (1337 and everything after)\n\
358
     - `neptune-core --scan-blocks 13..=37` (13, 37, and everything in\n\
359
       between)\n\
360
     - `neptune-core --scan-blocks 13:37` (python index ranges also work)",
361
    ScanModeConfiguration::default().num_future_keys()
362
        ))]
363
    pub(crate) scan_blocks: Option<RangeInclusive<u64>>,
364

365
    /// Scan incoming blocks for inbound transactions.
366
    ///
367
    /// Keys are generated deterministically from the secret seed and a
368
    /// derivation index, and a counter recording the most recently used index
369
    /// is stored. By default, incoming blocks are scanned for inbound
370
    /// transactions tied to any key derived from indices smaller than this
371
    /// counter.
372
    ///
373
    /// However, this counter can be lost, for instance after importing the
374
    /// secret seed onto a new machine. In such cases, this subcommand will
375
    /// instruct the client to scan incoming blocks for transactions tied to the
376
    /// next k derivation indices, where k is the argument supplied.
377
    ///
378
    /// When this flag is set, by default all blocks will be scanned. The
379
    /// subcommand `--scan-blocks` can be used to restrict the range of blocks
380
    /// that undergo this scan.
381
    ///
382
    /// Example: `neptune-core --scan-keys 42`
383
    #[clap(long)]
384
    pub(crate) scan_keys: Option<usize>,
385
}
386

387
impl Default for Args {
388
    fn default() -> Self {
797✔
389
        let empty: Vec<String> = vec![];
797✔
390
        Self::parse_from(empty)
797✔
391
    }
797✔
392
}
393

394
fn fraction_validator(s: &str) -> Result<f64, String> {
1,594✔
395
    let value = s
1,594✔
396
        .parse::<f64>()
1,594✔
397
        .map_err(|_| format!("`{s}` isn't a valid float"))?;
1,594✔
398
    if (0.0..=1.0).contains(&value) {
1,594✔
399
        Ok(value)
1,594✔
400
    } else {
401
        Err(format!("Fraction must be between 0 and 1, got {value}"))
×
402
    }
403
}
1,594✔
404

405
fn duration_from_seconds_str(s: &str) -> Result<Duration, std::num::ParseIntError> {
797✔
406
    Ok(Duration::from_secs(s.parse()?))
797✔
407
}
797✔
408

409
/// Parses strings that represent ranges of non-negative integers, in either
410
/// rust or python (index-range) format.
411
///
412
/// Disallows empty ranges.
413
fn parse_range(unparsed_range: &str) -> Result<RangeInclusive<u64>, String> {
11✔
414
    if unparsed_range.is_empty() {
11✔
415
        return Ok(0..=u64::MAX);
×
416
    }
11✔
417

418
    let range_parts = if unparsed_range.contains("..") {
11✔
419
        unparsed_range.split("..")
7✔
420
    } else {
421
        unparsed_range.split(":")
4✔
422
    };
423

424
    let Some((start, end)) = range_parts.collect_tuple() else {
11✔
425
        let error_message = format!(
×
426
            "Invalid range: \"{unparsed_range}\". \
×
427
            Syntax: `start..end`, `start..=end`, or `start:end`"
×
428
        );
×
429
        return Err(error_message);
×
430
    };
431

432
    let start = start
11✔
433
        .is_empty()
11✔
434
        .then_some(Ok(0))
11✔
435
        .or_else(|| Some(start.parse()))
11✔
436
        .unwrap()
11✔
437
        .map_err(|e| format!("Invalid start \"{start}\" in range \"{unparsed_range}\": {e:?}"))?;
11✔
438

439
    let end = if end.is_empty() {
11✔
440
        u64::MAX
4✔
441
    } else {
442
        let format_error =
7✔
443
            |e| format!("Invalid end \"{end}\" in range \"{unparsed_range}\": {e:?}");
×
444
        if let Some(end_inclusive) = end.strip_prefix('=') {
7✔
445
            end_inclusive.parse().map_err(format_error)?
3✔
446
        } else {
447
            end.parse::<u64>()
4✔
448
                .map_err(format_error)?
4✔
449
                .checked_sub(1)
4✔
450
                .ok_or_else(|| format!("Range upper bound \"{end}\" is invalid when excluded"))?
4✔
451
        }
452
    };
453

454
    if start > end {
11✔
455
        return Err(format!(
×
456
            "Range \"{unparsed_range}\" is invalid: lower bound exceeds upper bound"
×
457
        ));
×
458
    }
11✔
459

11✔
460
    Ok(start..=end)
11✔
461
}
11✔
462

463
impl Args {
464
    #[cfg(test)]
465
    pub(crate) fn default_with_network(network: Network) -> Self {
6✔
466
        Self {
6✔
467
            network,
6✔
468
            ..Default::default()
6✔
469
        }
6✔
470
    }
6✔
471

472
    /// Indicates if all incoming peer connections are disallowed.
473
    pub(crate) fn disallow_all_incoming_peer_connections(&self) -> bool {
10✔
474
        self.max_num_peers.is_zero()
10✔
475
    }
10✔
476

477
    /// Return the port that peer can connect on. None if incoming connections
478
    /// are disallowed.
479
    pub(crate) fn own_listen_port(&self) -> Option<u16> {
9✔
480
        if self.disallow_all_incoming_peer_connections() {
9✔
481
            None
2✔
482
        } else {
483
            Some(self.peer_port)
7✔
484
        }
485
    }
9✔
486

487
    /// Returns how often we should attempt to upgrade transaction proofs.
488
    pub(crate) fn tx_upgrade_interval(&self) -> Option<Duration> {
4✔
489
        match self.tx_proof_upgrade_interval {
4✔
490
            0 => None,
×
491
            n => Some(Duration::from_secs(n)),
4✔
492
        }
493
    }
4✔
494

495
    /// Whether to engage in mining (composing or guessing or both)
496
    pub(crate) fn mine(&self) -> bool {
10✔
497
        self.guess || self.compose
10✔
498
    }
10✔
499

500
    pub(crate) fn proof_job_options(
8✔
501
        &self,
8✔
502
        job_priority: TritonVmJobPriority,
8✔
503
    ) -> TritonVmProofJobOptions {
8✔
504
        TritonVmProofJobOptions {
8✔
505
            job_priority,
8✔
506
            job_settings: ProverJobSettings {
8✔
507
                max_log2_padded_height_for_proofs: self.max_log2_padded_height_for_proofs,
8✔
508
            },
8✔
509
            cancel_job_rx: None,
8✔
510
        }
8✔
511
    }
8✔
512

513
    /// Get the proving capability CLI argument or estimate it if it is not set.
514
    /// Cache the result so we don't estimate more than once.
515
    pub(crate) fn proving_capability(&self) -> TxProvingCapability {
823✔
516
        *self.tx_proving_capability_cache.get_or_init(|| {
823✔
517
            if let Some(proving_capability) = self.tx_proving_capability {
92✔
518
                proving_capability
5✔
519
            } else if self.compose {
87✔
520
                TxProvingCapability::SingleProof
×
521
            } else {
522
                Self::estimate_proving_capability()
87✔
523
            }
524
        })
823✔
525
    }
823✔
526

527
    fn estimate_proving_capability() -> TxProvingCapability {
88✔
528
        const SINGLE_PROOF_CORE_REQ: usize = 19;
529
        // see https://github.com/Neptune-Crypto/neptune-core/issues/426
530
        const SINGLE_PROOF_MEMORY_USAGE: u64 = (1u64 << 30) * 120;
531

532
        const PROOF_COLLECTION_CORE_REQ: usize = 2;
533
        const PROOF_COLLECTION_MEMORY_USAGE: u64 = (1u64 << 30) * 16;
534

535
        let s = System::new_all();
88✔
536
        let total_memory = s.total_memory();
88✔
537
        assert!(
88✔
538
            !total_memory.is_zero(),
88✔
539
            "Total memory reported illegal value of 0"
×
540
        );
541

542
        let physical_core_count = s.physical_core_count().unwrap_or(1);
88✔
543

88✔
544
        if total_memory > SINGLE_PROOF_MEMORY_USAGE && physical_core_count > SINGLE_PROOF_CORE_REQ {
88✔
545
            TxProvingCapability::SingleProof
×
546
        } else if total_memory > PROOF_COLLECTION_MEMORY_USAGE
88✔
547
            && physical_core_count > PROOF_COLLECTION_CORE_REQ
×
548
        {
549
            TxProvingCapability::ProofCollection
×
550
        } else {
551
            TxProvingCapability::LockScript
88✔
552
        }
553
    }
88✔
554
}
555

556
#[cfg(test)]
557
mod cli_args_tests {
558
    use std::net::Ipv6Addr;
559
    use std::ops::RangeBounds;
560

561
    use super::*;
562

563
    #[test]
564
    fn default_args_test() {
1✔
565
        let default_args = Args::default();
1✔
566

1✔
567
        assert_eq!(1000, default_args.peer_tolerance);
1✔
568
        assert_eq!(10, default_args.max_num_peers);
1✔
569
        assert_eq!(9798, default_args.peer_port);
1✔
570
        assert_eq!(9799, default_args.rpc_port);
1✔
571
        assert_eq!(
1✔
572
            IpAddr::from(Ipv6Addr::UNSPECIFIED),
1✔
573
            default_args.listen_addr
1✔
574
        );
1✔
575
        assert_eq!(None, default_args.max_mempool_num_tx);
1✔
576
        assert_eq!(1800, default_args.tx_proof_upgrade_interval);
1✔
577
    }
1✔
578

579
    #[test]
580
    fn sane_tx_upgrade_interval_value() {
1✔
581
        let args = Args {
1✔
582
            tx_proof_upgrade_interval: 900,
1✔
583
            ..Default::default()
1✔
584
        };
1✔
585
        assert_eq!(900, args.tx_upgrade_interval().unwrap().as_secs());
1✔
586
    }
1✔
587

588
    #[test]
589
    fn max_peers_0_means_no_incoming_connections() {
1✔
590
        let args = Args {
1✔
591
            max_num_peers: 0,
1✔
592
            ..Default::default()
1✔
593
        };
1✔
594
        assert!(args.disallow_all_incoming_peer_connections());
1✔
595
    }
1✔
596

597
    #[test]
598
    fn estimate_own_proving_capability() {
1✔
599
        // doubles as a no-crash test
1✔
600
        println!("{}", Args::estimate_proving_capability());
1✔
601
    }
1✔
602

603
    #[test]
604
    fn cli_args_can_differ_about_proving_capability() {
1✔
605
        let a = Args {
1✔
606
            tx_proving_capability: Some(TxProvingCapability::ProofCollection),
1✔
607
            ..Default::default()
1✔
608
        };
1✔
609
        let b = Args {
1✔
610
            tx_proving_capability: Some(TxProvingCapability::SingleProof),
1✔
611
            ..Default::default()
1✔
612
        };
1✔
613
        assert_ne!(a.proving_capability(), b.proving_capability());
1✔
614
    }
1✔
615

616
    #[test]
617
    fn cli_args_default_network_agrees_with_enum_default() {
1✔
618
        assert_eq!(Args::default().network, Network::default());
1✔
619
    }
1✔
620

621
    #[test]
622
    fn test_parse_range() {
1✔
623
        macro_rules! assert_range_eq {
624
            ($left:expr, $right:expr) => {{
625
                let left = $left;
626
                let right = $right;
627
                assert_eq!(
628
                    left.start_bound(),
629
                    right.start_bound(),
630
                    "Range start values are not equal"
631
                );
632
                match (left.end_bound(), right.end_bound()) {
633
                    (std::ops::Bound::Excluded(a), std::ops::Bound::Included(b)) => {
634
                        assert_eq!(a, &(b + 1))
635
                    }
636
                    (std::ops::Bound::Included(a), std::ops::Bound::Excluded(b)) => {
637
                        assert_eq!(&(a + 1), b)
638
                    }
639
                    (a, b) => assert_eq!(a, b),
640
                }
641
            }};
642
        }
643

644
        assert_range_eq!(5u64..10, parse_range("5..10").unwrap());
1✔
645
        assert_range_eq!(5u64..=5, parse_range("5..=5").unwrap());
1✔
646
        assert_range_eq!(5u64..=10, parse_range("5..=10").unwrap());
1✔
647
        assert_range_eq!(0..10u64, parse_range("..10").unwrap());
1✔
648
        assert_range_eq!(0..=10u64, parse_range("..=10").unwrap());
1✔
649
        assert_range_eq!(5u64..=u64::MAX, parse_range("5..").unwrap());
1✔
650
        assert_range_eq!(0u64..=u64::MAX, parse_range("..").unwrap());
1✔
651

652
        assert_range_eq!(5u64..10, parse_range("5:10").unwrap());
1✔
653
        assert_range_eq!(5u64..=u64::MAX, parse_range("5:").unwrap());
1✔
654
        assert_range_eq!(0u64..10, parse_range(":10").unwrap());
1✔
655
        assert_range_eq!(0u64..=u64::MAX, parse_range(":").unwrap());
1✔
656
    }
1✔
657
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc