Skip to content

Commit f7b1295

Browse files
committed
fix: Arg indexing in command_try_mine()
1 parent c5be755 commit f7b1295

File tree

2 files changed

+103
-72
lines changed

2 files changed

+103
-72
lines changed

stackslib/src/cli.rs

Lines changed: 102 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ use crate::util_lib::db::IndexDBTx;
5353

5454
/// Can be used with CLI commands to support non-mainnet chainstate
5555
/// Allows integration testing of these functions
56-
#[derive(Deserialize)]
56+
#[derive(Debug, Deserialize, PartialEq)]
5757
pub struct StacksChainConfig {
5858
pub chain_id: u32,
5959
pub first_block_height: u64,
@@ -131,9 +131,63 @@ impl StacksChainConfig {
131131
}
132132
}
133133

134+
// Can't be initialized as `const`, so this is the next best option
134135
const STACKS_CHAIN_CONFIG_DEFAULT_MAINNET: LazyCell<StacksChainConfig> =
135136
LazyCell::new(StacksChainConfig::default_mainnet);
136137

138+
/// Options common to many `stacks-inspect` subcommands
139+
/// Returned by `process_common_opts()`
140+
#[derive(Debug, Default, PartialEq)]
141+
pub struct CommonOpts {
142+
pub config: Option<StacksChainConfig>,
143+
}
144+
145+
/// Process arguments common to many `stacks-inspect` subcommands and drain them from `argv`
146+
///
147+
/// Args:
148+
/// - `argv`: Full CLI args `Vec`
149+
/// - `start_at`: Position in args vec where to look for common options.
150+
/// For example, if `start_at` is `1`, then look for these options **before** the subcommand:
151+
/// ```console
152+
/// stacks-inspect --config testnet.toml replay-block path/to/chainstate
153+
/// ```
154+
pub fn drain_common_opts(argv: &mut Vec<String>, start_at: usize) -> CommonOpts {
155+
let mut i = start_at;
156+
let mut opts = CommonOpts::default();
157+
while let Some(arg) = argv.get(i) {
158+
let (prefix, opt) = arg.split_at(2);
159+
if prefix != "--" {
160+
break;
161+
}
162+
i += 1;
163+
match opt {
164+
"config" => {
165+
let path = &argv[i];
166+
i += 1;
167+
let config = StacksChainConfig::from_file(&path);
168+
opts.config.replace(config);
169+
}
170+
"network" => {
171+
let network = &argv[i];
172+
i += 1;
173+
let config = match network.to_lowercase().as_str() {
174+
"testnet" => StacksChainConfig::default_testnet(),
175+
"mainnet" => StacksChainConfig::default_mainnet(),
176+
other => {
177+
eprintln!("Unknown network choice `{other}`");
178+
process::exit(1);
179+
}
180+
};
181+
opts.config.replace(config);
182+
}
183+
_ => panic!("Unrecognized option: {opt}"),
184+
}
185+
}
186+
// Remove options processed
187+
argv.drain(start_at..i);
188+
opts
189+
}
190+
137191
/// Replay blocks from chainstate database
138192
/// Terminates on error using `process::exit()`
139193
///
@@ -398,7 +452,7 @@ pub fn command_replay_mock_mining(argv: &[String], conf: Option<&StacksChainConf
398452
/// - `argv`: Args in CLI format: `<command-name> [args...]`
399453
/// - `conf`: Optional config for running on non-mainnet chainstate
400454
pub fn command_try_mine(argv: &[String], conf: Option<&StacksChainConfig>) {
401-
let print_help_and_exit = || -> ! {
455+
let print_help_and_exit = || {
402456
let n = &argv[0];
403457
eprintln!("Usage: {n} <working-dir> [min-fee [max-time]]");
404458
eprintln!("");
@@ -411,28 +465,26 @@ pub fn command_try_mine(argv: &[String], conf: Option<&StacksChainConfig>) {
411465
process::exit(1);
412466
};
413467

468+
// Parse subcommand-specific args
469+
let db_path = argv.get(1).unwrap_or_else(print_help_and_exit);
470+
let min_fee = argv
471+
.get(2)
472+
.map(|arg| arg.parse().expect("Could not parse min_fee"))
473+
.unwrap_or(u64::MAX);
474+
let max_time = argv
475+
.get(3)
476+
.map(|arg| arg.parse().expect("Could not parse max_time"))
477+
.unwrap_or(u64::MAX);
478+
479+
let start = get_epoch_time_ms();
480+
414481
let default_conf = STACKS_CHAIN_CONFIG_DEFAULT_MAINNET;
415482
let conf = conf.unwrap_or(&default_conf);
416483

417-
let start = get_epoch_time_ms();
418-
let db_path = &argv[2];
419484
let burnchain_path = format!("{db_path}/burnchain");
420485
let sort_db_path = format!("{db_path}/burnchain/sortition");
421486
let chain_state_path = format!("{db_path}/chainstate/");
422487

423-
let mut min_fee = u64::MAX;
424-
let mut max_time = u64::MAX;
425-
426-
if argv.len() < 2 {
427-
print_help_and_exit();
428-
}
429-
if argv.len() >= 3 {
430-
min_fee = argv[3].parse().expect("Could not parse min_fee");
431-
}
432-
if argv.len() >= 4 {
433-
max_time = argv[4].parse().expect("Could not parse max_time");
434-
}
435-
436488
let sort_db = SortitionDB::open(&sort_db_path, false, conf.pox_constants.clone())
437489
.unwrap_or_else(|_| panic!("Failed to open {sort_db_path}"));
438490
let (chain_state, _) = StacksChainState::open(true, conf.chain_id, &chain_state_path, None)
@@ -1099,3 +1151,36 @@ fn replay_block_nakamoto(
10991151

11001152
Ok(())
11011153
}
1154+
1155+
#[cfg(test)]
1156+
pub mod test {
1157+
use super::{drain_common_opts, CommonOpts};
1158+
1159+
fn parse_cli_command(s: &str) -> Vec<String> {
1160+
s.split(' ').map(String::from).collect()
1161+
}
1162+
1163+
#[test]
1164+
pub fn test_drain_common_opts() {
1165+
// Should find/remove no options
1166+
let mut argv = parse_cli_command(
1167+
"stacks-inspect try-mine --config my_config.toml /tmp/chainstate/mainnet",
1168+
);
1169+
let argv_init = argv.clone();
1170+
let opts = drain_common_opts(&mut argv, 0);
1171+
let opts = drain_common_opts(&mut argv, 1);
1172+
1173+
assert_eq!(argv, argv_init);
1174+
assert_eq!(opts, CommonOpts::default());
1175+
1176+
// Should find config opts and remove from vec
1177+
let mut argv = parse_cli_command(
1178+
"stacks-inspect --network testnet --network mainnet try-mine /tmp/chainstate/mainnet",
1179+
);
1180+
let opts = drain_common_opts(&mut argv, 1);
1181+
let argv_expected = parse_cli_command("stacks-inspect try-mine /tmp/chainstate/mainnet");
1182+
1183+
assert_eq!(argv, argv_expected);
1184+
assert!(opts.config.is_some());
1185+
}
1186+
}

stackslib/src/main.rs

Lines changed: 1 addition & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@ extern crate stacks_common;
2525
#[macro_use(slog_debug, slog_info, slog_warn)]
2626
extern crate slog;
2727

28-
use blockstack_lib::cli::StacksChainConfig;
2928
#[cfg(not(any(target_os = "macos", target_os = "windows", target_arch = "arm")))]
3029
use tikv_jemallocator::Jemalloc;
3130

@@ -302,59 +301,6 @@ fn check_shadow_network(network: &str) {
302301
}
303302
}
304303

305-
/// Options common to many `stacks-inspect` subcommands
306-
/// Returned by `process_common_opts()`
307-
#[derive(Default)]
308-
struct CommonOpts {
309-
config: Option<StacksChainConfig>,
310-
}
311-
312-
/// Process arguments common to many `stacks-inspect` subcommands and drain them from `argv`
313-
///
314-
/// Args:
315-
/// - `argv`: Full CLI args `Vec`
316-
/// - `start_at`: Position in args vec where to look for common options.
317-
/// For example, if `start_at` is `1`, then look for these options **before** the subcommand:
318-
/// ```console
319-
/// stacks-inspect --config testnet.toml replay-block path/to/chainstate
320-
/// ```
321-
fn drain_common_opts<'a>(argv: &mut Vec<String>, start_at: usize) -> CommonOpts {
322-
let mut i = start_at;
323-
let mut opts = CommonOpts::default();
324-
while let Some(arg) = argv.get(i) {
325-
let (prefix, opt) = arg.split_at(2);
326-
if prefix != "--" {
327-
break;
328-
}
329-
i += 1;
330-
match opt {
331-
"config" => {
332-
let path = &argv[i];
333-
i += 1;
334-
let config = StacksChainConfig::from_file(&path);
335-
opts.config.replace(config);
336-
}
337-
"network" => {
338-
let network = &argv[i];
339-
i += 1;
340-
let config = match network.to_lowercase().as_str() {
341-
"testnet" => cli::StacksChainConfig::default_testnet(),
342-
"mainnet" => cli::StacksChainConfig::default_mainnet(),
343-
other => {
344-
eprintln!("Unknown network choice `{other}`");
345-
process::exit(1);
346-
}
347-
};
348-
opts.config.replace(config);
349-
}
350-
_ => panic!("Unrecognized option: {opt}"),
351-
}
352-
}
353-
// Remove options processed
354-
argv.drain(start_at..i);
355-
opts
356-
}
357-
358304
#[cfg_attr(test, mutants::skip)]
359305
fn main() {
360306
let mut argv: Vec<String> = env::args().collect();
@@ -363,7 +309,7 @@ fn main() {
363309
process::exit(1);
364310
}
365311

366-
let common_opts = drain_common_opts(&mut argv, 1);
312+
let common_opts = cli::drain_common_opts(&mut argv, 1);
367313

368314
if argv[1] == "--version" {
369315
println!(

0 commit comments

Comments
 (0)