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

Neptune-Crypto / neptune-core / 15121230442

19 May 2025 07:11PM UTC coverage: 71.731% (+0.01%) from 71.719%
15121230442

push

github

Sword-Smith
docs: Document bootstrapping from raw block data

20102 of 28024 relevant lines covered (71.73%)

377896.46 hits per line

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

87.5
/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::fee_notification_policy::FeeNotificationPolicy;
17
use super::network::Network;
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
use crate::triton_vm_job_queue::TritonVmJobPriority;
24

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

41
    /// A directory holding block data that can be used to bootstrap the state
42
    /// to speedup the initial block download.
43
    #[clap(long, value_name = "DIR")]
44
    pub bootstrap_from_directory: Option<PathBuf>,
45

46
    /// Set this to disable block validation for a faster bootstrapping.
47
    #[clap(long)]
48
    pub disable_bootstrap_block_validation: bool,
49

50
    /// Ban connections to this node from IP address.
51
    ///
52
    /// This node can still make outgoing connections to IP address.
53
    ///
54
    /// To do this, see `--peers`.
55
    ///
56
    /// E.g.: --ban 1.2.3.4 --ban 5.6.7.8
57
    #[clap(long, value_name = "IP")]
58
    pub(crate) ban: Vec<IpAddr>,
59

60
    /// The threshold at which a peer's standing is considered “bad”. Current
61
    /// connections to peers in bad standing are terminated. Connection attempts
62
    /// from peers in bad standing are refused.
63
    ///
64
    /// For a list of reasons that cause bad standing, see
65
    /// [NegativePeerSanction](crate::models::peer::NegativePeerSanction).
66
    #[clap(
67
        long,
68
        default_value = "1000",
69
        value_name = "VALUE",
70
        value_parser = clap::value_parser!(u16).range(1..),
71
    )]
72
    pub(crate) peer_tolerance: u16,
73

74
    /// Maximum number of peers to accept connections from.
75
    ///
76
    /// Will not prevent outgoing connections made with `--peers`.
77
    /// Set this value to 0 to refuse all incoming connections.
78
    #[clap(
79
        long,
80
        default_value = "10",
81
        value_name = "COUNT",
82
        value_parser = clap::value_parser!(u16).map(|u| usize::from(u)),
1,672✔
83
    )]
84
    pub(crate) max_num_peers: usize,
85

86
    /// Maximum number of peers to accept from each IP address.
87
    ///
88
    /// Multiple nodes can run on the same IP address which would either mean
89
    /// that multiple nodes run on the same machine, or multiple machines are
90
    /// on the same network that uses Network Address Translation and has one
91
    /// public IP.
92
    #[clap(long)]
93
    pub(crate) max_connections_per_ip: Option<usize>,
94

95
    /// Whether to act as bootstrapper node.
96
    ///
97
    /// Bootstrapper nodes ensure that the maximum number of peers is never
98
    /// reached by disconnecting from existing peers when the maximum is about
99
    /// to be reached. As a result, they will respond with high likelihood to
100
    /// incoming connection requests -- in contrast to regular nodes, which
101
    /// refuse incoming connections when the max is reached.
102
    #[clap(long)]
103
    pub(crate) bootstrap: bool,
104

105
    /// If this flag is set, the node will refuse to initiate a transaction.
106
    /// This flag makes sense for machines whose resources are dedicated to
107
    /// composing, and which must do so in a regular and predictable manner,
108
    /// undisrupted by transaction initiation tasks. To spend funds from the
109
    /// wallet of a node where this flag is set, either restart it and drop the
110
    /// flag, or else copy the wallet file to another machine.
111
    #[clap(long, alias = "notx")]
112
    pub(crate) no_transaction_initiation: bool,
113

114
    /// Whether to produce block proposals, which is the first step of two-step
115
    /// mining. Note that composing block proposals involves the computationally
116
    /// expensive task of producing STARK proofs. You should have plenty of
117
    /// cores and probably at least 128 GB of RAM.
118
    #[clap(long)]
119
    pub(crate) compose: bool,
120

121
    /// Whether to engage in guess-nonce-and-hash, which is the second step in
122
    /// two-step mining. If this flag is set and the `compose` flag is not set,
123
    /// then the client will rely on block proposals from other nodes. In this
124
    /// case, it will always pick the most profitable block proposal.
125
    ///
126
    /// If this flag is set and the `compose` flag is set, then the client will
127
    /// always guess on their own block proposal.
128
    #[clap(long)]
129
    pub(crate) guess: bool,
130

131
    /// By default, a composer will share block proposals with all peers. If
132
    /// this flag is set, the composer will *not* share their block proposals.
133
    #[clap(long)]
134
    pub(crate) secret_compositions: bool,
135

136
    /// Regulates the fraction of the block subsidy that goes to the guesser.
137
    /// Value must be between 0 and 1.
138
    ///
139
    /// The remainder goes to the composer. This flag is ignored if the
140
    /// `compose` flag is not set.
141
    #[clap(long, default_value = "0.5", value_parser = fraction_validator)]
142
    pub(crate) guesser_fraction: f64,
143

144
    /// Whether to sleep between nonce-guesses. Useful if you do not want to
145
    /// dedicate all your CPU power.
146
    #[clap(long)]
147
    pub(crate) sleepy_guessing: bool,
148

149
    /// Set the number of threads to use while guessing. When no value is set,
150
    /// the number is set to the number of available cores.
151
    #[clap(long)]
152
    pub(crate) guesser_threads: Option<usize>,
153

154
    /// Determines the fraction of the transaction fee consumed by this node as
155
    /// a reward either for upgrading a `ProofCollection` to `SingleProof`, or
156
    /// for merging two `SingleProof`s.
157
    #[clap(long, default_value = "0.2", value_parser = fraction_validator)]
158
    pub(crate) gobbling_fraction: f64,
159

160
    /// Determines the minimum fee to take as a reward for upgrading foreign
161
    /// transaction proofs. Foreign transactions where a fee below this
162
    /// threshold cannot be collected by proof upgrading will not be upgraded.
163
    #[clap(long, default_value = "0.01", value_parser = NativeCurrencyAmount::coins_from_str)]
164
    pub(crate) min_gobbling_fee: NativeCurrencyAmount,
165

166
    /// Whether to keep the UTXO notifications for composer fees and
167
    /// proof-upgrader fees off chain.
168
    ///
169
    /// Composers and proof-upgraders can gobble a portion of the fee of
170
    /// transactions they work on, by directing it to an output only they can
171
    /// spend. By default, a public announcement is added to the transaction to
172
    /// enable the composer or proof-upgrader to recover such UTXOs after
173
    /// restoring the wallet from seed phrase. This public announcement is
174
    /// encryped by default under a symmetric key.
175
    ///
176
    /// Valid options:
177
    ///
178
    ///  - `on-chain-symmetric` (default) On-chain backups using symmetric key
179
    ///    ciphertexts.
180
    ///
181
    ///  - `on-chain-generation` On-chain backups using generation addresses,
182
    ///    which means that public key encryption is used instead. Note that
183
    ///    public key ciphertexts are significantly larger (and thus take up
184
    ///    more blockchain space) than symmetric ciphertexts.
185
    ///
186
    ///  - `off-chain` Avoid on-chain backups. Saves blockchain space, but risks
187
    ///    loss of funds. Enable only if you know what you are doing.
188
    ///
189
    /// This flag does not apply to guesser fees because those UTXOs are
190
    /// generated automatically.
191
    #[clap(long, default_value = "on-chain-symmetric", value_parser = FeeNotificationPolicy::parse)]
192
    pub(crate) fee_notification: FeeNotificationPolicy,
193

194
    /// Prune the mempool when it exceeds this size in RAM.
195
    ///
196
    /// Units: B (bytes), K (kilobytes), M (megabytes), G (gigabytes)
197
    ///
198
    /// E.g. --max-mempool-size 500M
199
    #[clap(long, default_value = "1G", value_name = "SIZE")]
200
    pub(crate) max_mempool_size: ByteSize,
201

202
    /// Maximum number of transactions permitted in the mempool.
203
    ///
204
    /// If too much time is spent updating transaction proofs, this
205
    /// value can be capped.
206
    ///
207
    /// E.g. --max-mempool-num-tx=4
208
    #[clap(long)]
209
    pub(crate) max_mempool_num_tx: Option<usize>,
210

211
    /// Port on which to listen for peer connections.
212
    #[clap(long, default_value = "9798", value_name = "PORT")]
213
    pub peer_port: u16,
214

215
    /// Port on which to listen for RPC connections.
216
    #[clap(long, default_value = "9799", value_name = "PORT")]
217
    pub rpc_port: u16,
218

219
    /// IP on which to listen for peer connections. Will default to all network interfaces, IPv4 and IPv6.
220
    #[clap(short, long, default_value = "::")]
221
    pub listen_addr: IpAddr,
222

223
    /// Maximum number of blocks that the client can catch up to without going
224
    /// into sync mode.
225
    ///
226
    /// Default: 1000.
227
    ///
228
    /// The process running this program should have access to enough RAM: at
229
    /// least the number of blocks set by this argument multiplied with the max
230
    /// block size (around 2 MB). Probably 1.5 to 2 times that amount for good
231
    /// margin.
232
    // Notice that the minimum value here may not be less than
233
    // [SYNC_CHALLENGE_POW_WITNESS_LENGTH](crate::models::peer::SYNC_CHALLENGE_POW_WITNESS_LENGTH)
234
    // as that would prevent going into sync mode.
235
    #[clap(long, default_value = "1000", value_parser(RangedI64ValueParser::<usize>::new().range(10..100000)))]
236
    pub(crate) sync_mode_threshold: usize,
237

238
    /// IPs of nodes to connect to, e.g.: --peers 8.8.8.8:9798 --peers 8.8.4.4:1337.
239
    #[structopt(long)]
240
    pub peers: Vec<SocketAddr>,
241

242
    /// Specify network, `main`, `alpha`, `beta`, `testnet`, or `regtest`
243
    #[structopt(long, default_value = "main", short)]
244
    pub network: Network,
245

246
    /// Max number of membership proofs stored per owned UTXO
247
    #[structopt(long, default_value = "3")]
248
    pub(crate) number_of_mps_per_utxo: usize,
249

250
    /// Configure how complicated proofs this machine is capable of producing.
251
    /// If no value is set, this parameter is estimated. For privacy, this level
252
    /// must not be set to [`TxProvingCapability::LockScript`], as this leaks
253
    /// information about amounts and input/output UTXOs.
254
    ///
255
    /// Proving the lockscripts is mandatory, since this is what prevents others
256
    /// from spending your coins.
257
    ///
258
    /// e.g. `--tx-proving-capability=singleproof` or
259
    /// `--tx-proving-capability=proofcollection`.
260
    #[clap(long)]
261
    pub tx_proving_capability: Option<TxProvingCapability>,
262

263
    /// Cache for the proving capability. If the above parameter is not set, we
264
    /// want to estimate proving capability and afterwards reuse the result from
265
    /// previous estimations. This argument cannot be set from CLI, so clap
266
    /// ignores it.
267
    #[clap(skip)]
268
    pub(crate) tx_proving_capability_cache: OnceLock<TxProvingCapability>,
269

270
    /// The number of seconds between each attempt to upgrade transactions in
271
    /// the mempool to proofs of a higher quality. Will only run if the machine
272
    /// on which the client runs is powerful enough to produce `SingleProof`s.
273
    ///
274
    /// Set to 0 to never perform this task.
275
    #[structopt(long, default_value = "1800")]
276
    pub(crate) tx_proof_upgrade_interval: u64,
277

278
    /// Enable tokio tracing for consumption by the tokio-console application
279
    /// note: this will attempt to connect to localhost:6669
280
    #[structopt(long, name = "tokio-console", default_value = "false")]
281
    pub tokio_console: bool,
282

283
    /// Sets the max program complexity limit for proof creation in Triton VM.
284
    ///
285
    /// Triton VM's prover complexity is a function of something called padded height
286
    /// which is always a power of two. A basic proof has a complexity of 2^11.
287
    /// A powerful machine in 2024 with 128 CPU cores can handle a padded height of 2^23.
288
    ///
289
    /// For such a machine, one would set a limit of 23.
290
    ///
291
    /// if the limit is reached while mining, a warning is logged and mining will pause.
292
    /// non-mining operations may panic and halt neptune-core
293
    ///
294
    /// no limit is applied if unset.
295
    #[structopt(long, short, value_parser = clap::value_parser!(u8).range(10..32))]
296
    pub max_log2_padded_height_for_proofs: Option<u8>,
297

298
    /// Sets the maximum number of proofs in a `ProofCollection` that can be
299
    /// recursively combined into a `SingleProof` by this machine. I.e. how big
300
    /// STARK proofs this machine can produce.
301
    #[clap(long, default_value = "16")]
302
    pub(crate) max_num_proofs: usize,
303

304
    /// Disables the cookie_hint RPC API
305
    ///
306
    /// client software can ask for a cookie hint to automatically determine the
307
    /// root data directory used by a running node, which enables loading a
308
    /// cookie file for authentication.
309
    ///
310
    /// Exposing the data directory leaks some privacy. Disable to prevent.
311
    #[clap(long)]
312
    pub disable_cookie_hint: bool,
313

314
    /// The duration (in seconds) during which new connection attempts from peers
315
    /// are ignored after a connection to them was closed.
316
    ///
317
    /// Does not affect abnormally closed connections. For example, if a connection
318
    /// is dropped due to networking issues, an immediate reconnection attempt is
319
    /// not affected by this cooldown.
320
    //
321
    // The default should be larger than the default interval between peer discovery
322
    // to meaningfully suppress rapid reconnection attempts.
323
    #[clap(long, default_value = "1800", value_parser = duration_from_seconds_str)]
324
    pub reconnect_cooldown: Duration,
325

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

389
    /// Scan incoming blocks for inbound transactions.
390
    ///
391
    /// Keys are generated deterministically from the secret seed and a
392
    /// derivation index, and a counter recording the most recently used index
393
    /// is stored. By default, incoming blocks are scanned for inbound
394
    /// transactions tied to any key derived from indices smaller than this
395
    /// counter.
396
    ///
397
    /// However, this counter can be lost, for instance after importing the
398
    /// secret seed onto a new machine. In such cases, this subcommand will
399
    /// instruct the client to scan incoming blocks for transactions tied to the
400
    /// next k derivation indices, where k is the argument supplied.
401
    ///
402
    /// When this flag is set, by default all blocks will be scanned. The
403
    /// subcommand `--scan-blocks` can be used to restrict the range of blocks
404
    /// that undergo this scan.
405
    ///
406
    /// Example: `neptune-core --scan-keys 42`
407
    #[clap(long)]
408
    pub(crate) scan_keys: Option<usize>,
409
}
410

411
impl Default for Args {
412
    fn default() -> Self {
1,672✔
413
        let empty: Vec<String> = vec![];
1,672✔
414
        Self::parse_from(empty)
1,672✔
415
    }
1,672✔
416
}
417

418
fn fraction_validator(s: &str) -> Result<f64, String> {
3,344✔
419
    let value = s
3,344✔
420
        .parse::<f64>()
3,344✔
421
        .map_err(|_| format!("`{s}` isn't a valid float"))?;
3,344✔
422
    if (0.0..=1.0).contains(&value) {
3,344✔
423
        Ok(value)
3,344✔
424
    } else {
425
        Err(format!("Fraction must be between 0 and 1, got {value}"))
×
426
    }
427
}
3,344✔
428

429
fn duration_from_seconds_str(s: &str) -> Result<Duration, std::num::ParseIntError> {
1,672✔
430
    Ok(Duration::from_secs(s.parse()?))
1,672✔
431
}
1,672✔
432

433
/// Parses strings that represent ranges of non-negative integers, in either
434
/// rust or python (index-range) format.
435
///
436
/// Disallows empty ranges.
437
fn parse_range(unparsed_range: &str) -> Result<RangeInclusive<u64>, String> {
11✔
438
    if unparsed_range.is_empty() {
11✔
439
        return Ok(0..=u64::MAX);
×
440
    }
11✔
441

442
    let range_parts = if unparsed_range.contains("..") {
11✔
443
        unparsed_range.split("..")
7✔
444
    } else {
445
        unparsed_range.split(":")
4✔
446
    };
447

448
    let Some((start, end)) = range_parts.collect_tuple() else {
11✔
449
        let error_message = format!(
×
450
            "Invalid range: \"{unparsed_range}\". \
×
451
            Syntax: `start..end`, `start..=end`, or `start:end`"
×
452
        );
453
        return Err(error_message);
×
454
    };
455

456
    let start = start
11✔
457
        .is_empty()
11✔
458
        .then_some(Ok(0))
11✔
459
        .or_else(|| Some(start.parse()))
11✔
460
        .unwrap()
11✔
461
        .map_err(|e| format!("Invalid start \"{start}\" in range \"{unparsed_range}\": {e:?}"))?;
11✔
462

463
    let end = if end.is_empty() {
11✔
464
        u64::MAX
4✔
465
    } else {
466
        let format_error =
7✔
467
            |e| format!("Invalid end \"{end}\" in range \"{unparsed_range}\": {e:?}");
×
468
        if let Some(end_inclusive) = end.strip_prefix('=') {
7✔
469
            end_inclusive.parse().map_err(format_error)?
3✔
470
        } else {
471
            end.parse::<u64>()
4✔
472
                .map_err(format_error)?
4✔
473
                .checked_sub(1)
4✔
474
                .ok_or_else(|| format!("Range upper bound \"{end}\" is invalid when excluded"))?
4✔
475
        }
476
    };
477

478
    if start > end {
11✔
479
        return Err(format!(
×
480
            "Range \"{unparsed_range}\" is invalid: lower bound exceeds upper bound"
×
481
        ));
×
482
    }
11✔
483

484
    Ok(start..=end)
11✔
485
}
11✔
486

487
impl Args {
488
    /// Indicates if all incoming peer connections are disallowed.
489
    pub(crate) fn disallow_all_incoming_peer_connections(&self) -> bool {
32✔
490
        self.max_num_peers.is_zero()
32✔
491
    }
32✔
492

493
    /// Return the port that peer can connect on. None if incoming connections
494
    /// are disallowed.
495
    pub(crate) fn own_listen_port(&self) -> Option<u16> {
28✔
496
        if self.disallow_all_incoming_peer_connections() {
28✔
497
            None
2✔
498
        } else {
499
            Some(self.peer_port)
26✔
500
        }
501
    }
28✔
502

503
    /// Returns how often we should attempt to upgrade transaction proofs.
504
    pub(crate) fn tx_upgrade_interval(&self) -> Option<Duration> {
12✔
505
        match self.tx_proof_upgrade_interval {
12✔
506
            0 => None,
×
507
            n => Some(Duration::from_secs(n)),
12✔
508
        }
509
    }
12✔
510

511
    /// Whether to engage in mining (composing or guessing or both)
512
    pub(crate) fn mine(&self) -> bool {
34✔
513
        self.guess || self.compose
34✔
514
    }
34✔
515

516
    pub(crate) fn proof_job_options(
55✔
517
        &self,
55✔
518
        job_priority: TritonVmJobPriority,
55✔
519
    ) -> TritonVmProofJobOptions {
55✔
520
        TritonVmProofJobOptions {
55✔
521
            job_priority,
55✔
522
            job_settings: ProverJobSettings {
55✔
523
                max_log2_padded_height_for_proofs: self.max_log2_padded_height_for_proofs,
55✔
524
                network: self.network,
55✔
525
                tx_proving_capability: self.proving_capability(),
55✔
526
                proof_type: self.proving_capability().into(),
55✔
527
            },
55✔
528
            cancel_job_rx: None,
55✔
529
        }
55✔
530
    }
55✔
531

532
    /// Get the proving capability CLI argument or estimate it if it is not set.
533
    /// Cache the result so we don't estimate more than once.
534
    pub fn proving_capability(&self) -> TxProvingCapability {
3,000✔
535
        *self.tx_proving_capability_cache.get_or_init(|| {
3,000✔
536
            if let Some(proving_capability) = self.tx_proving_capability {
1,150✔
537
                proving_capability
94✔
538
            } else if self.compose {
1,056✔
539
                TxProvingCapability::SingleProof
4✔
540
            } else {
541
                Self::estimate_proving_capability()
1,052✔
542
            }
543
        })
1,150✔
544
    }
3,000✔
545

546
    fn estimate_proving_capability() -> TxProvingCapability {
1,053✔
547
        const SINGLE_PROOF_CORE_REQ: usize = 19;
548
        // see https://github.com/Neptune-Crypto/neptune-core/issues/426
549
        const SINGLE_PROOF_MEMORY_USAGE: u64 = (1u64 << 30) * 120;
550

551
        const PROOF_COLLECTION_CORE_REQ: usize = 2;
552
        const PROOF_COLLECTION_MEMORY_USAGE: u64 = (1u64 << 30) * 16;
553

554
        let s = System::new_all();
1,053✔
555
        let total_memory = s.total_memory();
1,053✔
556
        assert!(
1,053✔
557
            !total_memory.is_zero(),
1,053✔
558
            "Total memory reported illegal value of 0"
×
559
        );
560

561
        let physical_core_count = s.physical_core_count().unwrap_or(1);
1,053✔
562

563
        if total_memory > SINGLE_PROOF_MEMORY_USAGE && physical_core_count > SINGLE_PROOF_CORE_REQ {
1,053✔
564
            TxProvingCapability::SingleProof
×
565
        } else if total_memory > PROOF_COLLECTION_MEMORY_USAGE
1,053✔
566
            && physical_core_count > PROOF_COLLECTION_CORE_REQ
×
567
        {
568
            TxProvingCapability::ProofCollection
×
569
        } else {
570
            TxProvingCapability::PrimitiveWitness
1,053✔
571
        }
572
    }
1,053✔
573

574
    /// creates a `TritonVmProofJobOptions` from cli args.
575
    pub fn as_proof_job_options(&self) -> TritonVmProofJobOptions {
17✔
576
        self.into()
17✔
577
    }
17✔
578
}
579

580
impl From<&Args> for TritonVmProofJobOptions {
581
    fn from(cli: &Args) -> Self {
988✔
582
        Self {
988✔
583
            job_priority: Default::default(),
988✔
584
            job_settings: ProverJobSettings {
988✔
585
                max_log2_padded_height_for_proofs: cli.max_log2_padded_height_for_proofs,
988✔
586
                network: cli.network,
988✔
587
                tx_proving_capability: cli.proving_capability(),
988✔
588
                proof_type: cli.proving_capability().into(),
988✔
589
            },
988✔
590
            cancel_job_rx: None,
988✔
591
        }
988✔
592
    }
988✔
593
}
594

595
#[cfg(test)]
596
#[cfg_attr(coverage_nightly, coverage(off))]
597
mod tests {
598
    use std::net::Ipv6Addr;
599
    use std::ops::RangeBounds;
600

601
    use super::*;
602
    use crate::models::blockchain::transaction::transaction_proof::TransactionProofType;
603

604
    // extra methods for tests.
605
    impl Args {
606
        pub(crate) fn default_with_network(network: Network) -> Self {
607
            Self {
608
                network,
609
                ..Default::default()
610
            }
611
        }
612

613
        pub(crate) fn proof_job_options_prooftype(
614
            &self,
615
            proof_type: TransactionProofType,
616
        ) -> TritonVmProofJobOptions {
617
            let mut options: TritonVmProofJobOptions = self.into();
618
            options.job_settings.proof_type = proof_type;
619
            options
620
        }
621

622
        pub(crate) fn proof_job_options_primitive_witness(&self) -> TritonVmProofJobOptions {
623
            self.proof_job_options_prooftype(TransactionProofType::PrimitiveWitness)
624
        }
625
    }
626

627
    #[test]
628
    fn default_args_test() {
629
        let default_args = Args::default();
630

631
        assert_eq!(1000, default_args.peer_tolerance);
632
        assert_eq!(10, default_args.max_num_peers);
633
        assert_eq!(9798, default_args.peer_port);
634
        assert_eq!(9799, default_args.rpc_port);
635
        assert_eq!(
636
            IpAddr::from(Ipv6Addr::UNSPECIFIED),
637
            default_args.listen_addr
638
        );
639
        assert_eq!(None, default_args.max_mempool_num_tx);
640
        assert_eq!(1800, default_args.tx_proof_upgrade_interval);
641
    }
642

643
    #[test]
644
    fn sane_tx_upgrade_interval_value() {
645
        let args = Args {
646
            tx_proof_upgrade_interval: 900,
647
            ..Default::default()
648
        };
649
        assert_eq!(900, args.tx_upgrade_interval().unwrap().as_secs());
650
    }
651

652
    #[test]
653
    fn max_peers_0_means_no_incoming_connections() {
654
        let args = Args {
655
            max_num_peers: 0,
656
            ..Default::default()
657
        };
658
        assert!(args.disallow_all_incoming_peer_connections());
659
    }
660

661
    #[test]
662
    fn estimate_own_proving_capability() {
663
        // doubles as a no-crash test
664
        println!("{}", Args::estimate_proving_capability());
665
    }
666

667
    #[test]
668
    fn cli_args_can_differ_about_proving_capability() {
669
        let a = Args {
670
            tx_proving_capability: Some(TxProvingCapability::ProofCollection),
671
            ..Default::default()
672
        };
673
        let b = Args {
674
            tx_proving_capability: Some(TxProvingCapability::SingleProof),
675
            ..Default::default()
676
        };
677
        assert_ne!(a.proving_capability(), b.proving_capability());
678
    }
679

680
    #[test]
681
    fn cli_args_default_network_agrees_with_enum_default() {
682
        assert_eq!(Args::default().network, Network::default());
683
    }
684

685
    #[test]
686
    fn test_parse_range() {
687
        macro_rules! assert_range_eq {
688
            ($left:expr, $right:expr) => {{
689
                let left = $left;
690
                let right = $right;
691
                assert_eq!(
692
                    left.start_bound(),
693
                    right.start_bound(),
694
                    "Range start values are not equal"
695
                );
696
                match (left.end_bound(), right.end_bound()) {
697
                    (std::ops::Bound::Excluded(a), std::ops::Bound::Included(b)) => {
698
                        assert_eq!(a, &(b + 1))
699
                    }
700
                    (std::ops::Bound::Included(a), std::ops::Bound::Excluded(b)) => {
701
                        assert_eq!(&(a + 1), b)
702
                    }
703
                    (a, b) => assert_eq!(a, b),
704
                }
705
            }};
706
        }
707

708
        assert_range_eq!(5u64..10, parse_range("5..10").unwrap());
709
        assert_range_eq!(5u64..=5, parse_range("5..=5").unwrap());
710
        assert_range_eq!(5u64..=10, parse_range("5..=10").unwrap());
711
        assert_range_eq!(0..10u64, parse_range("..10").unwrap());
712
        assert_range_eq!(0..=10u64, parse_range("..=10").unwrap());
713
        assert_range_eq!(5u64..=u64::MAX, parse_range("5..").unwrap());
714
        assert_range_eq!(0u64..=u64::MAX, parse_range("..").unwrap());
715

716
        assert_range_eq!(5u64..10, parse_range("5:10").unwrap());
717
        assert_range_eq!(5u64..=u64::MAX, parse_range("5:").unwrap());
718
        assert_range_eq!(0u64..10, parse_range(":10").unwrap());
719
        assert_range_eq!(0u64..=u64::MAX, parse_range(":").unwrap());
720
    }
721
}
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