Skip to content

Commit 4a0dd95

Browse files
committed
feat: stacks-inspect commands work with unmodified stacks-node config file
1 parent 5fdfd5d commit 4a0dd95

File tree

2 files changed

+135
-183
lines changed

2 files changed

+135
-183
lines changed

stackslib/src/cli.rs

Lines changed: 97 additions & 148 deletions
Original file line numberDiff line numberDiff line change
@@ -46,100 +46,17 @@ use crate::chainstate::stacks::db::{StacksBlockHeaderTypes, StacksChainState, St
4646
use crate::chainstate::stacks::miner::*;
4747
use crate::chainstate::stacks::{Error as ChainstateError, *};
4848
use crate::clarity_vm::clarity::ClarityInstance;
49+
use crate::config::{Config, ConfigFile, DEFAULT_MAINNET_CONFIG};
4950
use crate::core::*;
5051
use crate::cost_estimates::metrics::UnitMetric;
5152
use crate::cost_estimates::UnitEstimator;
5253
use crate::util_lib::db::IndexDBTx;
5354

54-
/// Can be used with CLI commands to support non-mainnet chainstate
55-
/// Allows integration testing of these functions
56-
#[derive(Debug, Deserialize, PartialEq)]
57-
pub struct StacksChainConfig {
58-
pub chain_id: u32,
59-
pub first_block_height: u64,
60-
pub first_burn_header_hash: BurnchainHeaderHash,
61-
pub first_burn_header_timestamp: u64,
62-
pub pox_constants: PoxConstants,
63-
pub epochs: EpochList,
64-
}
65-
66-
impl StacksChainConfig {
67-
pub fn type_name() -> &'static str {
68-
type_name::<Self>()
69-
}
70-
71-
pub fn default_mainnet() -> Self {
72-
Self {
73-
chain_id: CHAIN_ID_MAINNET,
74-
first_block_height: BITCOIN_MAINNET_FIRST_BLOCK_HEIGHT,
75-
first_burn_header_hash: BurnchainHeaderHash::from_hex(BITCOIN_MAINNET_FIRST_BLOCK_HASH)
76-
.unwrap(),
77-
first_burn_header_timestamp: BITCOIN_MAINNET_FIRST_BLOCK_TIMESTAMP.into(),
78-
pox_constants: PoxConstants::mainnet_default(),
79-
epochs: (*STACKS_EPOCHS_MAINNET).clone(),
80-
}
81-
}
82-
83-
pub fn default_testnet() -> Self {
84-
let mut pox_constants = PoxConstants::regtest_default();
85-
pox_constants.prepare_length = 100;
86-
pox_constants.reward_cycle_length = 900;
87-
pox_constants.v1_unlock_height = 3;
88-
pox_constants.v2_unlock_height = 5;
89-
pox_constants.pox_3_activation_height = 5;
90-
pox_constants.pox_4_activation_height = 6;
91-
pox_constants.v3_unlock_height = 7;
92-
let mut epochs = EpochList::new(&*STACKS_EPOCHS_REGTEST);
93-
epochs[StacksEpochId::Epoch10].start_height = 0;
94-
epochs[StacksEpochId::Epoch10].end_height = 0;
95-
epochs[StacksEpochId::Epoch20].start_height = 0;
96-
epochs[StacksEpochId::Epoch20].end_height = 1;
97-
epochs[StacksEpochId::Epoch2_05].start_height = 1;
98-
epochs[StacksEpochId::Epoch2_05].end_height = 2;
99-
epochs[StacksEpochId::Epoch21].start_height = 2;
100-
epochs[StacksEpochId::Epoch21].end_height = 3;
101-
epochs[StacksEpochId::Epoch22].start_height = 3;
102-
epochs[StacksEpochId::Epoch22].end_height = 4;
103-
epochs[StacksEpochId::Epoch23].start_height = 4;
104-
epochs[StacksEpochId::Epoch23].end_height = 5;
105-
epochs[StacksEpochId::Epoch24].start_height = 5;
106-
epochs[StacksEpochId::Epoch24].end_height = 6;
107-
epochs[StacksEpochId::Epoch25].start_height = 6;
108-
epochs[StacksEpochId::Epoch25].end_height = 56_457;
109-
epochs[StacksEpochId::Epoch30].start_height = 56_457;
110-
Self {
111-
chain_id: CHAIN_ID_TESTNET,
112-
first_block_height: 0,
113-
first_burn_header_hash: BurnchainHeaderHash::from_hex(BITCOIN_REGTEST_FIRST_BLOCK_HASH)
114-
.unwrap(),
115-
first_burn_header_timestamp: BITCOIN_REGTEST_FIRST_BLOCK_TIMESTAMP.into(),
116-
pox_constants,
117-
epochs,
118-
}
119-
}
120-
121-
pub fn from_file(path: &str) -> Self {
122-
let text = fs::read_to_string(&path)
123-
.unwrap_or_else(|e| panic!("Failed to read file '{path}': {e}"));
124-
let config: Self = toml::from_str(&text).unwrap_or_else(|e| {
125-
panic!(
126-
"Failed to parse file '{path}' as `{t}`: {e}",
127-
t = Self::type_name()
128-
)
129-
});
130-
config
131-
}
132-
}
133-
134-
// Can't be initialized as `const`, so this is the next best option
135-
const STACKS_CHAIN_CONFIG_DEFAULT_MAINNET: LazyCell<StacksChainConfig> =
136-
LazyCell::new(StacksChainConfig::default_mainnet);
137-
13855
/// Options common to many `stacks-inspect` subcommands
13956
/// Returned by `process_common_opts()`
140-
#[derive(Debug, Default, PartialEq)]
57+
#[derive(Debug, Default)]
14158
pub struct CommonOpts {
142-
pub config: Option<StacksChainConfig>,
59+
pub config: Option<Config>,
14360
}
14461

14562
/// Process arguments common to many `stacks-inspect` subcommands and drain them from `argv`
@@ -164,20 +81,30 @@ pub fn drain_common_opts(argv: &mut Vec<String>, start_at: usize) -> CommonOpts
16481
"config" => {
16582
let path = &argv[i];
16683
i += 1;
167-
let config = StacksChainConfig::from_file(&path);
84+
let config_file = ConfigFile::from_path(&path).unwrap_or_else(|e| {
85+
panic!("Failed to read '{path}' as stacks-node config: {e}")
86+
});
87+
let config = Config::from_config_file(config_file, false).unwrap_or_else(|e| {
88+
panic!("Failed to convert config file into node config: {e}")
89+
});
16890
opts.config.replace(config);
16991
}
17092
"network" => {
17193
let network = &argv[i];
17294
i += 1;
173-
let config = match network.to_lowercase().as_str() {
174-
"testnet" => StacksChainConfig::default_testnet(),
175-
"mainnet" => StacksChainConfig::default_mainnet(),
95+
let config_file = match network.to_lowercase().as_str() {
96+
"helium" => ConfigFile::helium(),
97+
"mainnet" => ConfigFile::mainnet(),
98+
"mocknet" => ConfigFile::mocknet(),
99+
"xenon" => ConfigFile::xenon(),
176100
other => {
177101
eprintln!("Unknown network choice `{other}`");
178102
process::exit(1);
179103
}
180104
};
105+
let config = Config::from_config_file(config_file, false).unwrap_or_else(|e| {
106+
panic!("Failed to convert config file into node config: {e}")
107+
});
181108
opts.config.replace(config);
182109
}
183110
_ => panic!("Unrecognized option: {opt}"),
@@ -193,7 +120,7 @@ pub fn drain_common_opts(argv: &mut Vec<String>, start_at: usize) -> CommonOpts
193120
///
194121
/// Arguments:
195122
/// - `argv`: Args in CLI format: `<command-name> [args...]`
196-
pub fn command_replay_block(argv: &[String], conf: Option<&StacksChainConfig>) {
123+
pub fn command_replay_block(argv: &[String], conf: Option<&Config>) {
197124
let print_help_and_exit = || -> ! {
198125
let n = &argv[0];
199126
eprintln!("Usage:");
@@ -271,7 +198,7 @@ pub fn command_replay_block(argv: &[String], conf: Option<&StacksChainConfig>) {
271198
///
272199
/// Arguments:
273200
/// - `argv`: Args in CLI format: `<command-name> [args...]`
274-
pub fn command_replay_block_nakamoto(argv: &[String], conf: Option<&StacksChainConfig>) {
201+
pub fn command_replay_block_nakamoto(argv: &[String], conf: Option<&Config>) {
275202
let print_help_and_exit = || -> ! {
276203
let n = &argv[0];
277204
eprintln!("Usage:");
@@ -288,12 +215,15 @@ pub fn command_replay_block_nakamoto(argv: &[String], conf: Option<&StacksChainC
288215

289216
let chain_state_path = format!("{db_path}/chainstate/");
290217

291-
let default_conf = STACKS_CHAIN_CONFIG_DEFAULT_MAINNET;
292-
let conf = conf.unwrap_or(&default_conf);
218+
let conf = conf.unwrap_or(&DEFAULT_MAINNET_CONFIG);
293219

294-
let mainnet = conf.chain_id == CHAIN_ID_MAINNET;
295-
let (chainstate, _) =
296-
StacksChainState::open(mainnet, conf.chain_id, &chain_state_path, None).unwrap();
220+
let (chainstate, _) = StacksChainState::open(
221+
conf.is_mainnet(),
222+
conf.burnchain.chain_id,
223+
&chain_state_path,
224+
None,
225+
)
226+
.unwrap();
297227

298228
let conn = chainstate.nakamoto_blocks_db();
299229

@@ -357,7 +287,7 @@ pub fn command_replay_block_nakamoto(argv: &[String], conf: Option<&StacksChainC
357287
/// Arguments:
358288
/// - `argv`: Args in CLI format: `<command-name> [args...]`
359289
/// - `conf`: Optional config for running on non-mainnet chainstate
360-
pub fn command_replay_mock_mining(argv: &[String], conf: Option<&StacksChainConfig>) {
290+
pub fn command_replay_mock_mining(argv: &[String], conf: Option<&Config>) {
361291
let print_help_and_exit = || -> ! {
362292
let n = &argv[0];
363293
eprintln!("Usage:");
@@ -451,7 +381,7 @@ pub fn command_replay_mock_mining(argv: &[String], conf: Option<&StacksChainConf
451381
/// Arguments:
452382
/// - `argv`: Args in CLI format: `<command-name> [args...]`
453383
/// - `conf`: Optional config for running on non-mainnet chainstate
454-
pub fn command_try_mine(argv: &[String], conf: Option<&StacksChainConfig>) {
384+
pub fn command_try_mine(argv: &[String], conf: Option<&Config>) {
455385
let print_help_and_exit = || {
456386
let n = &argv[0];
457387
eprintln!("Usage: {n} <working-dir> [min-fee [max-time]]");
@@ -478,25 +408,36 @@ pub fn command_try_mine(argv: &[String], conf: Option<&StacksChainConfig>) {
478408

479409
let start = get_epoch_time_ms();
480410

481-
let default_conf = STACKS_CHAIN_CONFIG_DEFAULT_MAINNET;
482-
let conf = conf.unwrap_or(&default_conf);
411+
let conf = conf.unwrap_or(&DEFAULT_MAINNET_CONFIG);
483412

484413
let burnchain_path = format!("{db_path}/burnchain");
485414
let sort_db_path = format!("{db_path}/burnchain/sortition");
486415
let chain_state_path = format!("{db_path}/chainstate/");
487416

488-
let sort_db = SortitionDB::open(&sort_db_path, false, conf.pox_constants.clone())
417+
let burnchain = conf.get_burnchain();
418+
let sort_db = SortitionDB::open(&sort_db_path, false, burnchain.pox_constants.clone())
489419
.unwrap_or_else(|_| panic!("Failed to open {sort_db_path}"));
490-
let (chain_state, _) = StacksChainState::open(true, conf.chain_id, &chain_state_path, None)
491-
.expect("Failed to open stacks chain state");
420+
let (chain_state, _) = StacksChainState::open(
421+
conf.is_mainnet(),
422+
conf.burnchain.chain_id,
423+
&chain_state_path,
424+
None,
425+
)
426+
.expect("Failed to open stacks chain state");
492427
let chain_tip = SortitionDB::get_canonical_burn_chain_tip(sort_db.conn())
493428
.expect("Failed to get sortition chain tip");
494429

495430
let estimator = Box::new(UnitEstimator);
496431
let metric = Box::new(UnitMetric);
497432

498-
let mut mempool_db = MemPoolDB::open(true, conf.chain_id, &chain_state_path, estimator, metric)
499-
.expect("Failed to open mempool db");
433+
let mut mempool_db = MemPoolDB::open(
434+
conf.is_mainnet(),
435+
conf.burnchain.chain_id,
436+
&chain_state_path,
437+
estimator,
438+
metric,
439+
)
440+
.expect("Failed to open mempool db");
500441

501442
let header_tip = NakamotoChainState::get_canonical_block_header(chain_state.db(), &sort_db)
502443
.unwrap()
@@ -519,7 +460,7 @@ pub fn command_try_mine(argv: &[String], conf: Option<&StacksChainConfig>) {
519460
TransactionPayload::Coinbase(CoinbasePayload([0u8; 32]), None, None),
520461
);
521462

522-
coinbase_tx.chain_id = conf.chain_id;
463+
coinbase_tx.chain_id = conf.burnchain.chain_id;
523464
coinbase_tx.anchor_mode = TransactionAnchorMode::OnChainOnly;
524465
let mut tx_signer = StacksTransactionSigner::new(&coinbase_tx);
525466
tx_signer.sign_origin(&sk).unwrap();
@@ -581,31 +522,32 @@ pub fn command_try_mine(argv: &[String], conf: Option<&StacksChainConfig>) {
581522
}
582523

583524
/// Fetch and process a `StagingBlock` from database and call `replay_block()` to validate
584-
fn replay_staging_block(
585-
db_path: &str,
586-
index_block_hash_hex: &str,
587-
conf: Option<&StacksChainConfig>,
588-
) {
525+
fn replay_staging_block(db_path: &str, index_block_hash_hex: &str, conf: Option<&Config>) {
589526
let block_id = StacksBlockId::from_hex(index_block_hash_hex).unwrap();
590527
let chain_state_path = format!("{db_path}/chainstate/");
591528
let sort_db_path = format!("{db_path}/burnchain/sortition");
592529
let burn_db_path = format!("{db_path}/burnchain/burnchain.sqlite");
593530
let burnchain_blocks_db = BurnchainDB::open(&burn_db_path, false).unwrap();
594531

595-
let default_conf = STACKS_CHAIN_CONFIG_DEFAULT_MAINNET;
596-
let conf = conf.unwrap_or(&default_conf);
532+
let conf = conf.unwrap_or(&DEFAULT_MAINNET_CONFIG);
597533

598-
let mainnet = conf.chain_id == CHAIN_ID_MAINNET;
599-
let (mut chainstate, _) =
600-
StacksChainState::open(mainnet, conf.chain_id, &chain_state_path, None).unwrap();
534+
let (mut chainstate, _) = StacksChainState::open(
535+
conf.is_mainnet(),
536+
conf.burnchain.chain_id,
537+
&chain_state_path,
538+
None,
539+
)
540+
.unwrap();
601541

542+
let burnchain = conf.get_burnchain();
543+
let epochs = conf.burnchain.epochs.as_ref().expect("No Epochs found");
602544
let mut sortdb = SortitionDB::connect(
603545
&sort_db_path,
604-
conf.first_block_height,
605-
&conf.first_burn_header_hash,
606-
conf.first_burn_header_timestamp,
607-
&conf.epochs,
608-
conf.pox_constants.clone(),
546+
burnchain.first_block_height,
547+
&burnchain.first_block_hash,
548+
u64::from(burnchain.first_block_timestamp),
549+
epochs,
550+
burnchain.pox_constants.clone(),
609551
None,
610552
true,
611553
)
@@ -659,30 +601,31 @@ fn replay_staging_block(
659601
}
660602

661603
/// Process a mock mined block and call `replay_block()` to validate
662-
fn replay_mock_mined_block(
663-
db_path: &str,
664-
block: AssembledAnchorBlock,
665-
conf: Option<&StacksChainConfig>,
666-
) {
604+
fn replay_mock_mined_block(db_path: &str, block: AssembledAnchorBlock, conf: Option<&Config>) {
667605
let chain_state_path = format!("{db_path}/chainstate/");
668606
let sort_db_path = format!("{db_path}/burnchain/sortition");
669607
let burn_db_path = format!("{db_path}/burnchain/burnchain.sqlite");
670608
let burnchain_blocks_db = BurnchainDB::open(&burn_db_path, false).unwrap();
671609

672-
let default_conf = STACKS_CHAIN_CONFIG_DEFAULT_MAINNET;
673-
let conf = conf.unwrap_or(&default_conf);
610+
let conf = conf.unwrap_or(&DEFAULT_MAINNET_CONFIG);
674611

675-
let mainnet = conf.chain_id == CHAIN_ID_MAINNET;
676-
let (mut chainstate, _) =
677-
StacksChainState::open(mainnet, conf.chain_id, &chain_state_path, None).unwrap();
612+
let (mut chainstate, _) = StacksChainState::open(
613+
conf.is_mainnet(),
614+
conf.burnchain.chain_id,
615+
&chain_state_path,
616+
None,
617+
)
618+
.unwrap();
678619

620+
let burnchain = conf.get_burnchain();
621+
let epochs = conf.burnchain.epochs.as_ref().expect("No Epochs found");
679622
let mut sortdb = SortitionDB::connect(
680623
&sort_db_path,
681-
conf.first_block_height,
682-
&conf.first_burn_header_hash,
683-
conf.first_burn_header_timestamp,
684-
&conf.epochs,
685-
conf.pox_constants.clone(),
624+
burnchain.first_block_height,
625+
&burnchain.first_block_hash,
626+
u64::from(burnchain.first_block_timestamp),
627+
epochs,
628+
burnchain.pox_constants.clone(),
686629
None,
687630
true,
688631
)
@@ -861,22 +804,28 @@ fn replay_block(
861804
}
862805

863806
/// Fetch and process a NakamotoBlock from database and call `replay_block_nakamoto()` to validate
864-
fn replay_naka_staging_block(db_path: &str, index_block_hash_hex: &str, conf: &StacksChainConfig) {
807+
fn replay_naka_staging_block(db_path: &str, index_block_hash_hex: &str, conf: &Config) {
865808
let block_id = StacksBlockId::from_hex(index_block_hash_hex).unwrap();
866809
let chain_state_path = format!("{db_path}/chainstate/");
867810
let sort_db_path = format!("{db_path}/burnchain/sortition");
868811

869-
let mainnet = conf.chain_id == CHAIN_ID_MAINNET;
870-
let (mut chainstate, _) =
871-
StacksChainState::open(mainnet, conf.chain_id, &chain_state_path, None).unwrap();
812+
let (mut chainstate, _) = StacksChainState::open(
813+
conf.is_mainnet(),
814+
conf.burnchain.chain_id,
815+
&chain_state_path,
816+
None,
817+
)
818+
.unwrap();
872819

820+
let burnchain = conf.get_burnchain();
821+
let epochs = conf.burnchain.epochs.as_ref().expect("No Epochs found");
873822
let mut sortdb = SortitionDB::connect(
874823
&sort_db_path,
875-
conf.first_block_height,
876-
&conf.first_burn_header_hash,
877-
conf.first_burn_header_timestamp,
878-
&conf.epochs,
879-
conf.pox_constants.clone(),
824+
burnchain.first_block_height,
825+
&burnchain.first_block_hash,
826+
u64::from(burnchain.first_block_timestamp),
827+
epochs,
828+
burnchain.pox_constants.clone(),
880829
None,
881830
true,
882831
)
@@ -1171,11 +1120,11 @@ pub mod test {
11711120
let opts = drain_common_opts(&mut argv, 1);
11721121

11731122
assert_eq!(argv, argv_init);
1174-
assert_eq!(opts, CommonOpts::default());
1123+
assert!(opts.config.is_none());
11751124

11761125
// Should find config opts and remove from vec
11771126
let mut argv = parse_cli_command(
1178-
"stacks-inspect --network testnet --network mainnet try-mine /tmp/chainstate/mainnet",
1127+
"stacks-inspect --network mocknet --network mainnet try-mine /tmp/chainstate/mainnet",
11791128
);
11801129
let opts = drain_common_opts(&mut argv, 1);
11811130
let argv_expected = parse_cli_command("stacks-inspect try-mine /tmp/chainstate/mainnet");

0 commit comments

Comments
 (0)