@@ -53,7 +53,7 @@ use crate::util_lib::db::IndexDBTx;
53
53
54
54
/// Can be used with CLI commands to support non-mainnet chainstate
55
55
/// Allows integration testing of these functions
56
- #[ derive( Deserialize ) ]
56
+ #[ derive( Debug , Deserialize , PartialEq ) ]
57
57
pub struct StacksChainConfig {
58
58
pub chain_id : u32 ,
59
59
pub first_block_height : u64 ,
@@ -131,9 +131,63 @@ impl StacksChainConfig {
131
131
}
132
132
}
133
133
134
+ // Can't be initialized as `const`, so this is the next best option
134
135
const STACKS_CHAIN_CONFIG_DEFAULT_MAINNET : LazyCell < StacksChainConfig > =
135
136
LazyCell :: new ( StacksChainConfig :: default_mainnet) ;
136
137
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
+
137
191
/// Replay blocks from chainstate database
138
192
/// Terminates on error using `process::exit()`
139
193
///
@@ -398,7 +452,7 @@ pub fn command_replay_mock_mining(argv: &[String], conf: Option<&StacksChainConf
398
452
/// - `argv`: Args in CLI format: `<command-name> [args...]`
399
453
/// - `conf`: Optional config for running on non-mainnet chainstate
400
454
pub fn command_try_mine ( argv : & [ String ] , conf : Option < & StacksChainConfig > ) {
401
- let print_help_and_exit = || -> ! {
455
+ let print_help_and_exit = || {
402
456
let n = & argv[ 0 ] ;
403
457
eprintln ! ( "Usage: {n} <working-dir> [min-fee [max-time]]" ) ;
404
458
eprintln ! ( "" ) ;
@@ -411,28 +465,26 @@ pub fn command_try_mine(argv: &[String], conf: Option<&StacksChainConfig>) {
411
465
process:: exit ( 1 ) ;
412
466
} ;
413
467
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
+
414
481
let default_conf = STACKS_CHAIN_CONFIG_DEFAULT_MAINNET ;
415
482
let conf = conf. unwrap_or ( & default_conf) ;
416
483
417
- let start = get_epoch_time_ms ( ) ;
418
- let db_path = & argv[ 2 ] ;
419
484
let burnchain_path = format ! ( "{db_path}/burnchain" ) ;
420
485
let sort_db_path = format ! ( "{db_path}/burnchain/sortition" ) ;
421
486
let chain_state_path = format ! ( "{db_path}/chainstate/" ) ;
422
487
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
-
436
488
let sort_db = SortitionDB :: open ( & sort_db_path, false , conf. pox_constants . clone ( ) )
437
489
. unwrap_or_else ( |_| panic ! ( "Failed to open {sort_db_path}" ) ) ;
438
490
let ( chain_state, _) = StacksChainState :: open ( true , conf. chain_id , & chain_state_path, None )
@@ -1099,3 +1151,36 @@ fn replay_block_nakamoto(
1099
1151
1100
1152
Ok ( ( ) )
1101
1153
}
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
+ }
0 commit comments