Skip to content

Commit 5178a46

Browse files
authored
Merge pull request #5584 from jbencin/feat/try-mine-nakamoto
feat: Nakamoto block support for `stacks-insepct try-mine`
2 parents 547e74d + ce539b1 commit 5178a46

File tree

3 files changed

+91
-85
lines changed

3 files changed

+91
-85
lines changed

stackslib/src/chainstate/nakamoto/miner.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ use crate::util_lib::boot::boot_code_id;
8686
use crate::util_lib::db::Error as DBError;
8787

8888
/// Nakamaoto tenure information
89-
#[derive(Debug)]
89+
#[derive(Debug, Default)]
9090
pub struct NakamotoTenureInfo {
9191
/// Coinbase tx, if this is a new tenure
9292
pub coinbase_tx: Option<StacksTransaction>,
@@ -98,8 +98,8 @@ impl NakamotoTenureInfo {
9898
pub fn cause(&self) -> Option<TenureChangeCause> {
9999
self.tenure_change_tx
100100
.as_ref()
101-
.map(|tx| tx.try_as_tenure_change().map(|payload| payload.cause))
102-
.flatten()
101+
.map(|tx| tx.try_as_tenure_change())?
102+
.map(|payload| payload.cause)
103103
}
104104

105105
pub fn tenure_change_tx(&self) -> Option<&StacksTransaction> {

stackslib/src/cli.rs

Lines changed: 86 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ use crate::chainstate::burn::db::sortdb::{
4040
};
4141
use crate::chainstate::burn::{BlockSnapshot, ConsensusHash};
4242
use crate::chainstate::coordinator::OnChainRewardSetProvider;
43+
use crate::chainstate::nakamoto::miner::{NakamotoBlockBuilder, NakamotoTenureInfo};
4344
use crate::chainstate::nakamoto::{NakamotoBlock, NakamotoChainState};
4445
use crate::chainstate::stacks::db::blocks::StagingBlock;
4546
use crate::chainstate::stacks::db::{StacksBlockHeaderTypes, StacksChainState, StacksHeaderInfo};
@@ -406,7 +407,7 @@ pub fn command_try_mine(argv: &[String], conf: Option<&Config>) {
406407
.map(|arg| arg.parse().expect("Could not parse max_time"))
407408
.unwrap_or(u64::MAX);
408409

409-
let start = get_epoch_time_ms();
410+
let start = Instant::now();
410411

411412
let conf = conf.unwrap_or(&DEFAULT_MAINNET_CONFIG);
412413

@@ -417,7 +418,7 @@ pub fn command_try_mine(argv: &[String], conf: Option<&Config>) {
417418
let burnchain = conf.get_burnchain();
418419
let sort_db = SortitionDB::open(&sort_db_path, false, burnchain.pox_constants.clone())
419420
.unwrap_or_else(|e| panic!("Failed to open {sort_db_path}: {e}"));
420-
let (chain_state, _) = StacksChainState::open(
421+
let (chainstate, _) = StacksChainState::open(
421422
conf.is_mainnet(),
422423
conf.burnchain.chain_id,
423424
&chain_state_path,
@@ -439,93 +440,100 @@ pub fn command_try_mine(argv: &[String], conf: Option<&Config>) {
439440
)
440441
.unwrap_or_else(|e| panic!("Failed to open mempool db: {e}"));
441442

442-
let tip_header = NakamotoChainState::get_canonical_block_header(chain_state.db(), &sort_db)
443-
.unwrap_or_else(|e| panic!("Error looking up chain tip: {e}"))
444-
.expect("No chain tip found");
443+
// Parent Stacks header for block we are going to mine
444+
let parent_stacks_header =
445+
NakamotoChainState::get_canonical_block_header(chainstate.db(), &sort_db)
446+
.unwrap_or_else(|e| panic!("Error looking up chain tip: {e}"))
447+
.expect("No chain tip found");
445448

446-
// Fail if Nakamoto chainstate detected. `try-mine` cannot mine Nakamoto blocks yet
447-
// TODO: Add Nakamoto block support
448-
if matches!(
449-
&tip_header.anchored_header,
450-
StacksBlockHeaderTypes::Nakamoto(..)
451-
) {
452-
panic!("Attempting to mine Nakamoto block. Nakamoto blocks not supported yet!");
453-
};
449+
let burn_dbconn = sort_db.index_handle(&chain_tip.sortition_id);
454450

455-
let sk = StacksPrivateKey::new();
456-
let mut tx_auth = TransactionAuth::from_p2pkh(&sk).unwrap();
457-
tx_auth.set_origin_nonce(0);
451+
let mut settings = BlockBuilderSettings::limited();
452+
settings.max_miner_time_ms = max_time;
458453

459-
let mut coinbase_tx = StacksTransaction::new(
460-
TransactionVersion::Mainnet,
461-
tx_auth,
462-
TransactionPayload::Coinbase(CoinbasePayload([0u8; 32]), None, None),
463-
);
454+
let result = match &parent_stacks_header.anchored_header {
455+
StacksBlockHeaderTypes::Epoch2(..) => {
456+
let sk = StacksPrivateKey::new();
457+
let mut tx_auth = TransactionAuth::from_p2pkh(&sk).unwrap();
458+
tx_auth.set_origin_nonce(0);
464459

465-
coinbase_tx.chain_id = conf.burnchain.chain_id;
466-
coinbase_tx.anchor_mode = TransactionAnchorMode::OnChainOnly;
467-
let mut tx_signer = StacksTransactionSigner::new(&coinbase_tx);
468-
tx_signer.sign_origin(&sk).unwrap();
469-
let coinbase_tx = tx_signer.get_tx().unwrap();
460+
let mut coinbase_tx = StacksTransaction::new(
461+
TransactionVersion::Mainnet,
462+
tx_auth,
463+
TransactionPayload::Coinbase(CoinbasePayload([0u8; 32]), None, None),
464+
);
470465

471-
let mut settings = BlockBuilderSettings::limited();
472-
settings.max_miner_time_ms = max_time;
466+
coinbase_tx.chain_id = conf.burnchain.chain_id;
467+
coinbase_tx.anchor_mode = TransactionAnchorMode::OnChainOnly;
468+
let mut tx_signer = StacksTransactionSigner::new(&coinbase_tx);
469+
tx_signer.sign_origin(&sk).unwrap();
470+
let coinbase_tx = tx_signer.get_tx().unwrap();
471+
472+
StacksBlockBuilder::build_anchored_block(
473+
&chainstate,
474+
&burn_dbconn,
475+
&mut mempool_db,
476+
&parent_stacks_header,
477+
chain_tip.total_burn,
478+
VRFProof::empty(),
479+
Hash160([0; 20]),
480+
&coinbase_tx,
481+
settings,
482+
None,
483+
&Burnchain::new(
484+
&burnchain_path,
485+
&burnchain.chain_name,
486+
&burnchain.network_name,
487+
)
488+
.unwrap_or_else(|e| panic!("Failed to instantiate burnchain: {e}")),
489+
)
490+
.map(|(block, cost, size)| (block.block_hash(), block.txs, cost, size))
491+
}
492+
StacksBlockHeaderTypes::Nakamoto(..) => {
493+
NakamotoBlockBuilder::build_nakamoto_block(
494+
&chainstate,
495+
&burn_dbconn,
496+
&mut mempool_db,
497+
&parent_stacks_header,
498+
// tenure ID consensus hash of this block
499+
&parent_stacks_header.consensus_hash,
500+
// the burn so far on the burnchain (i.e. from the last burnchain block)
501+
chain_tip.total_burn,
502+
NakamotoTenureInfo::default(),
503+
settings,
504+
None,
505+
0,
506+
)
507+
.map(|(block, cost, size, _)| (block.header.block_hash(), block.txs, cost, size))
508+
}
509+
};
473510

474-
let result = StacksBlockBuilder::build_anchored_block(
475-
&chain_state,
476-
&sort_db.index_handle(&chain_tip.sortition_id),
477-
&mut mempool_db,
478-
&tip_header,
479-
chain_tip.total_burn,
480-
VRFProof::empty(),
481-
Hash160([0; 20]),
482-
&coinbase_tx,
483-
settings,
484-
None,
485-
&Burnchain::new(
486-
&burnchain_path,
487-
&burnchain.chain_name,
488-
&burnchain.network_name,
489-
)
490-
.unwrap(),
511+
let elapsed = start.elapsed();
512+
let summary = format!(
513+
"block @ height = {h} off of {pid} ({pch}/{pbh}) in {t}ms. Min-fee: {min_fee}, Max-time: {max_time}",
514+
h=parent_stacks_header.stacks_block_height + 1,
515+
pid=&parent_stacks_header.index_block_hash(),
516+
pch=&parent_stacks_header.consensus_hash,
517+
pbh=&parent_stacks_header.anchored_header.block_hash(),
518+
t=elapsed.as_millis(),
491519
);
492520

493-
let stop = get_epoch_time_ms();
494-
495-
println!(
496-
"{} mined block @ height = {} off of {} ({}/{}) in {}ms. Min-fee: {}, Max-time: {}",
497-
if result.is_ok() {
498-
"Successfully"
499-
} else {
500-
"Failed to"
501-
},
502-
tip_header.stacks_block_height + 1,
503-
StacksBlockHeader::make_index_block_hash(
504-
&tip_header.consensus_hash,
505-
&tip_header.anchored_header.block_hash()
506-
),
507-
&tip_header.consensus_hash,
508-
&tip_header.anchored_header.block_hash(),
509-
stop.saturating_sub(start),
510-
min_fee,
511-
max_time
512-
);
521+
let code = match result {
522+
Ok((block_hash, txs, cost, size)) => {
523+
let total_fees: u64 = txs.iter().map(|tx| tx.get_tx_fee()).sum();
513524

514-
if let Ok((block, execution_cost, size)) = result {
515-
let mut total_fees = 0;
516-
for tx in block.txs.iter() {
517-
total_fees += tx.get_tx_fee();
525+
println!("Successfully mined {summary}");
526+
println!("Block {block_hash}: {total_fees} uSTX, {size} bytes, cost {cost:?}");
527+
0
518528
}
519-
println!(
520-
"Block {}: {} uSTX, {} bytes, cost {:?}",
521-
block.block_hash(),
522-
total_fees,
523-
size,
524-
&execution_cost
525-
);
526-
}
529+
Err(e) => {
530+
println!("Failed to mine {summary}");
531+
println!("Error: {e}");
532+
1
533+
}
534+
};
527535

528-
process::exit(0);
536+
process::exit(code);
529537
}
530538

531539
/// Fetch and process a `StagingBlock` from database and call `replay_block()` to validate

testnet/stacks-node/src/tests/nakamoto_integrations.rs

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -734,10 +734,8 @@ pub fn next_block_and_wait_for_commits(
734734
.map(|x| x.load(Ordering::SeqCst))
735735
.collect();
736736

737-
let mut block_processed_time: Vec<Option<Instant>> =
738-
(0..commits_before.len()).map(|_| None).collect();
739-
let mut commit_sent_time: Vec<Option<Instant>> =
740-
(0..commits_before.len()).map(|_| None).collect();
737+
let mut block_processed_time: Vec<Option<Instant>> = vec![None; commits_before.len()];
738+
let mut commit_sent_time: Vec<Option<Instant>> = vec![None; commits_before.len()];
741739
next_block_and(btc_controller, timeout_secs, || {
742740
for i in 0..commits_submitted.len() {
743741
let commits_sent = commits_submitted[i].load(Ordering::SeqCst);

0 commit comments

Comments
 (0)