Skip to content

Commit d4a9215

Browse files
committed
chore: Move replay-mock-mining to stacks-node binary
1 parent 7f12563 commit d4a9215

File tree

3 files changed

+105
-109
lines changed

3 files changed

+105
-109
lines changed

stackslib/src/main.rs

Lines changed: 1 addition & 84 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,6 @@ use std::collections::{BTreeMap, HashMap, HashSet};
3939
use std::fs::{File, OpenOptions};
4040
use std::io::prelude::*;
4141
use std::io::BufReader;
42-
use std::path::PathBuf;
4342
use std::time::Instant;
4443
use std::{env, fs, io, process, thread};
4544

@@ -94,7 +93,7 @@ use stacks_common::util::hash::{hex_bytes, to_hex, Hash160};
9493
use stacks_common::util::retry::LogReader;
9594
use stacks_common::util::secp256k1::{Secp256k1PrivateKey, Secp256k1PublicKey};
9695
use stacks_common::util::vrf::VRFProof;
97-
use stacks_common::util::{deserialize_json_from_file, get_epoch_time_ms, log, sleep_ms};
96+
use stacks_common::util::{get_epoch_time_ms, log, sleep_ms};
9897

9998
#[cfg_attr(test, mutants::skip)]
10099
fn main() {
@@ -1341,88 +1340,6 @@ simulating a miner.
13411340
);
13421341
return;
13431342
}
1344-
if argv[1] == "replay-mock-mining" {
1345-
let print_help_and_exit = || {
1346-
let n = &argv[0];
1347-
eprintln!("Usage:");
1348-
eprintln!(" {n} <mock-miner-output-dir>");
1349-
process::exit(1);
1350-
};
1351-
1352-
// Process CLI args
1353-
let dir = argv
1354-
.get(2)
1355-
.map(PathBuf::from)
1356-
.map(fs::canonicalize)
1357-
.transpose()
1358-
.unwrap_or_else(|e| panic!("Not a valid path: {e}"))
1359-
.unwrap_or_else(print_help_and_exit);
1360-
1361-
if !dir.is_dir() {
1362-
panic!("Not a valid directory: {dir:?}");
1363-
}
1364-
1365-
// Read entries in directory
1366-
let dir_entries = dir
1367-
.read_dir()
1368-
.unwrap_or_else(|e| panic!("Failed to read directory: {e}"))
1369-
.filter_map(|e| e.ok());
1370-
1371-
// Get filenames, filtering out anything that isn't a regular file
1372-
let filenames = dir_entries.filter_map(|e| match e.file_type() {
1373-
Ok(t) if t.is_file() => e.file_name().into_string().ok(),
1374-
_ => None,
1375-
});
1376-
1377-
// Get vec of (block_height, filename), to prepare for sorting
1378-
//
1379-
// NOTE: Trusting the filename is not ideal. We could sort on data read from the file,
1380-
// but that requires reading all files
1381-
let re = Regex::new(r"^([0-9]+\.json)$").unwrap();
1382-
let mut indexed_files = filenames
1383-
.filter_map(|filename| {
1384-
// Use regex to extract block number from filename
1385-
let Some(cap) = re.captures(&filename) else {
1386-
return None;
1387-
};
1388-
let Some(m) = cap.get(0) else {
1389-
return None;
1390-
};
1391-
let Ok(bh) = m.as_str().parse::<u64>() else {
1392-
return None;
1393-
};
1394-
Some((bh, filename))
1395-
})
1396-
.collect::<Vec<_>>();
1397-
1398-
// Sort by block height
1399-
indexed_files.sort_by_key(|(bh, _)| *bh);
1400-
1401-
if indexed_files.is_empty() {
1402-
panic!("No block files found");
1403-
}
1404-
1405-
info!(
1406-
"Replaying {} blocks starting at {}",
1407-
indexed_files.len(),
1408-
indexed_files[0].0
1409-
);
1410-
1411-
for (bh, filename) in indexed_files {
1412-
let filepath = dir.join(filename);
1413-
info!("Replaying block from file";
1414-
"block_height" => bh,
1415-
"filepath" => ?filepath
1416-
);
1417-
// let block = AssembledAnchorBlock::deserialize_json_from_file(filepath)
1418-
// .unwrap_or_else(|e| panic!("Error reading block {block} from file: {e}"));
1419-
// debug!("Replaying block from {filepath:?}";
1420-
// "block_height" => bh,
1421-
// "block" => %block
1422-
// );
1423-
// TODO: Actually replay block
1424-
}
1425-
}
14261343

14271344
if argv.len() < 4 {
14281345
eprintln!("Usage: {} blockchain network working_dir", argv[0]);

testnet/stacks-node/src/main.rs

Lines changed: 97 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,12 @@ extern crate stacks;
1010
#[macro_use(o, slog_log, slog_trace, slog_debug, slog_info, slog_warn, slog_error)]
1111
extern crate slog;
1212

13+
use regex::Regex;
1314
pub use stacks_common::util;
1415
use stacks_common::util::hash::hex_bytes;
1516

17+
use crate::neon_node::AssembledAnchorBlock;
18+
1619
pub mod monitoring;
1720

1821
pub mod burnchains;
@@ -31,7 +34,8 @@ pub mod syncctl;
3134
pub mod tenure;
3235

3336
use std::collections::HashMap;
34-
use std::{env, panic, process};
37+
use std::path::PathBuf;
38+
use std::{env, fs, panic, process};
3539

3640
use backtrace::Backtrace;
3741
use pico_args::Arguments;
@@ -166,10 +170,8 @@ fn cli_get_miner_spend(
166170
return 0.0;
167171
};
168172
let Ok(active_miners_and_commits) =
169-
MinerStats::get_active_miners(&sortdb, Some(burn_block_height)).map_err(|e| {
170-
warn!("Failed to get active miners: {:?}", &e);
171-
e
172-
})
173+
MinerStats::get_active_miners(&sortdb, Some(burn_block_height))
174+
.inspect_err(|e| warn!("Failed to get active miners: {e:?}"))
173175
else {
174176
return 0.0;
175177
};
@@ -187,10 +189,7 @@ fn cli_get_miner_spend(
187189

188190
let Ok(unconfirmed_block_commits) = miner_stats
189191
.get_unconfirmed_commits(burn_block_height + 1, &active_miners)
190-
.map_err(|e| {
191-
warn!("Failed to find unconfirmed block-commits: {}", &e);
192-
e
193-
})
192+
.inspect_err(|e| warn!("Failed to find unconfirmed block-commits: {e}"))
194193
else {
195194
return 0.0;
196195
};
@@ -229,10 +228,7 @@ fn cli_get_miner_spend(
229228
&commit_outs,
230229
at_burnchain_height,
231230
)
232-
.map_err(|e| {
233-
warn!("Failed to get unconfirmed burn distribution: {:?}", &e);
234-
e
235-
})
231+
.inspect_err(|e| warn!("Failed to get unconfirmed burn distribution: {e:?}"))
236232
else {
237233
return 0.0;
238234
};
@@ -265,6 +261,82 @@ fn cli_get_miner_spend(
265261
spend_amount
266262
}
267263

264+
fn cli_replay_mock_mining(config_path: &str, path: &str) {
265+
info!("Loading config at path {config_path}");
266+
let config = match ConfigFile::from_path(&config_path) {
267+
Ok(config_file) => Config::from_config_file(config_file, true).unwrap(),
268+
Err(e) => {
269+
warn!("Invalid config file: {e}");
270+
process::exit(1);
271+
}
272+
};
273+
274+
// Validate directory path
275+
let dir = PathBuf::from(path);
276+
let dir = fs::canonicalize(dir).unwrap_or_else(|e| panic!("{path} is not a valid path: {e}"));
277+
278+
if !dir.is_dir() {
279+
panic!("{path} is not a valid directory");
280+
}
281+
282+
// Read entries in directory
283+
let dir_entries = dir
284+
.read_dir()
285+
.unwrap_or_else(|e| panic!("Failed to read {path}: {e}"))
286+
.filter_map(|e| e.ok());
287+
288+
// Get filenames, filtering out anything that isn't a regular file
289+
let filenames = dir_entries.filter_map(|e| match e.file_type() {
290+
Ok(t) if t.is_file() => e.file_name().into_string().ok(),
291+
_ => None,
292+
});
293+
294+
// Get vec of (block_height, filename), to prepare for sorting
295+
//
296+
// NOTE: Trusting the filename is not ideal. We could sort on data read from the file,
297+
// but that requires reading all files
298+
let re = Regex::new(r"^([0-9]+\.json)$").unwrap();
299+
let mut indexed_files = filenames
300+
.filter_map(|filename| {
301+
// Use regex to extract block number from filename
302+
let Some(cap) = re.captures(&filename) else {
303+
return None;
304+
};
305+
let Some(m) = cap.get(0) else {
306+
return None;
307+
};
308+
let Ok(bh) = m.as_str().parse::<u64>() else {
309+
return None;
310+
};
311+
Some((bh, filename))
312+
})
313+
.collect::<Vec<_>>();
314+
315+
// Sort by block height
316+
indexed_files.sort_by_key(|(bh, _)| *bh);
317+
318+
if indexed_files.is_empty() {
319+
panic!("No block files found");
320+
}
321+
322+
info!(
323+
"Replaying {} blocks starting at {}",
324+
indexed_files.len(),
325+
indexed_files[0].0
326+
);
327+
328+
for (bh, filename) in indexed_files {
329+
let filepath = dir.join(filename);
330+
let block = AssembledAnchorBlock::deserialize_from_file(&filepath)
331+
.unwrap_or_else(|e| panic!("Error reading block {bh} from file: {e}"));
332+
debug!("Replaying block from {filepath:?}";
333+
"block_height" => bh,
334+
"block" => ?block
335+
);
336+
// TODO: Actually replay block
337+
}
338+
}
339+
268340
fn main() {
269341
panic::set_hook(Box::new(|panic_info| {
270342
error!("Process abort due to thread panic: {}", panic_info);
@@ -412,6 +484,13 @@ fn main() {
412484
println!("Will spend {}", spend_amount);
413485
process::exit(0);
414486
}
487+
"replay-mock-mining" => {
488+
let path: String = args.value_from_str("--path").unwrap();
489+
let config_path: String = args.value_from_str("--config").unwrap();
490+
args.finish();
491+
cli_replay_mock_mining(&config_path, &path);
492+
process::exit(0);
493+
}
415494
_ => {
416495
print_help();
417496
return;
@@ -502,6 +581,11 @@ key-for-seed\tOutput the associated secret key for a burnchain signer created wi
502581
\t\tCan be passed a config file for the seed via the `--config <file>` option *or* by supplying the hex seed on
503582
\t\tthe command line directly.
504583
584+
replay-mock-mining\tReplay mock mined blocks from <dir>
585+
\t\tArguments:
586+
\t\t --path: path to directory of mock mined blocks
587+
\t\t --config: path to the config file
588+
505589
help\t\tDisplay this help.
506590
507591
OPTIONAL ARGUMENTS:

testnet/stacks-node/src/neon_node.rs

Lines changed: 7 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -239,7 +239,7 @@ pub(crate) enum MinerThreadResult {
239239
/// Fully-assembled Stacks anchored, block as well as some extra metadata pertaining to how it was
240240
/// linked to the burnchain and what view(s) the miner had of the burnchain before and after
241241
/// completing the block.
242-
#[derive(Clone, Serialize, Deserialize)]
242+
#[derive(Debug, Clone, Serialize, Deserialize)]
243243
pub struct AssembledAnchorBlock {
244244
/// Consensus hash of the parent Stacks block
245245
parent_consensus_hash: ConsensusHash,
@@ -3603,22 +3603,17 @@ impl RelayerThread {
36033603
}
36043604
}
36053605

3606-
let mut miner_thread_state =
3607-
match self.create_block_miner(registered_key, last_burn_block, issue_timestamp_ms) {
3608-
Some(state) => state,
3609-
None => {
3610-
return false;
3611-
}
3612-
};
3606+
let Some(mut miner_thread_state) =
3607+
self.create_block_miner(registered_key, last_burn_block, issue_timestamp_ms)
3608+
else {
3609+
return false;
3610+
};
36133611

36143612
if let Ok(miner_handle) = thread::Builder::new()
36153613
.name(format!("miner-block-{}", self.local_peer.data_url))
36163614
.stack_size(BLOCK_PROCESSOR_STACK_SIZE)
36173615
.spawn(move || miner_thread_state.run_tenure())
3618-
.map_err(|e| {
3619-
error!("Relayer: Failed to start tenure thread: {:?}", &e);
3620-
e
3621-
})
3616+
.inspect_err(|e| error!("Relayer: Failed to start tenure thread: {e:?}"))
36223617
{
36233618
self.miner_thread = Some(miner_handle);
36243619
}

0 commit comments

Comments
 (0)