Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion stacks-node/src/tests/nakamoto_integrations.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3135,7 +3135,7 @@ fn block_proposal_api_endpoint() {
.unwrap();
let burn_chain_height = miner_tenure_info.burn_tip_height;
let mut tenure_tx = builder
.tenure_begin(&burn_dbconn, &mut miner_tenure_info)
.tenure_begin(&burn_dbconn, &mut miner_tenure_info, false)
.unwrap();

let tx = make_stacks_transfer_serialized(
Expand Down
4 changes: 3 additions & 1 deletion stackslib/src/chainstate/nakamoto/miner.rs
Original file line number Diff line number Diff line change
Expand Up @@ -384,6 +384,7 @@ impl NakamotoBlockBuilder {
&mut self,
burn_dbconn: &'a SortitionHandleConn,
info: &'b mut MinerTenureInfo<'a>,
simulated: bool,
) -> Result<ClarityTx<'b, 'b>, Error> {
let Some(block_commit) = info.tenure_block_commit_opt.as_ref() else {
return Err(Error::InvalidStacksBlock(
Expand Down Expand Up @@ -412,6 +413,7 @@ impl NakamotoBlockBuilder {
&self.header.pox_treatment,
block_commit,
&info.active_reward_set,
simulated,
)?;
self.matured_miner_rewards_opt = matured_miner_rewards_opt;
Ok(clarity_tx)
Expand Down Expand Up @@ -541,7 +543,7 @@ impl NakamotoBlockBuilder {
let mut miner_tenure_info =
builder.load_tenure_info(&mut chainstate, burn_dbconn, tenure_info.cause())?;
let burn_chain_height = miner_tenure_info.burn_tip_height;
let mut tenure_tx = builder.tenure_begin(burn_dbconn, &mut miner_tenure_info)?;
let mut tenure_tx = builder.tenure_begin(burn_dbconn, &mut miner_tenure_info, false)?;

let tenure_budget = tenure_tx
.block_limit()
Expand Down
149 changes: 149 additions & 0 deletions stackslib/src/chainstate/nakamoto/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ use super::stacks::{
Error as ChainstateError, StacksBlock, StacksTransaction, TenureChangePayload,
TokenTransferMemo, TransactionPayload, TransactionVersion,
};
use crate::burnchains::db::BurnchainHeaderReader;
use crate::burnchains::{PoxConstants, Txid};
use crate::chainstate::burn::db::sortdb::SortitionDB;
use crate::chainstate::burn::operations::LeaderBlockCommitOp;
Expand Down Expand Up @@ -3109,6 +3110,149 @@ impl NakamotoChainState {
Ok(Vec::from_iter(epoch2_x))
}

pub fn get_block_headers_in_tenure_at_burnview(
db: &Connection,
tenure_id: &ConsensusHash,
burn_view: &ConsensusHash,
) -> Result<Vec<StacksHeaderInfo>, ChainstateError> {
// see if we have a nakamoto block in this tenure
let qry = "
SELECT *
FROM nakamoto_block_headers
WHERE consensus_hash = ?1
AND burn_view = ?2
ORDER BY block_height
";
let args = params![tenure_id, burn_view];
let out = query_rows(db, qry, args)?;
if !out.is_empty() {
return Ok(out);
}
Err(ChainstateError::NoSuchBlockError)
}

/// DO NOT USE IN CONSENSUS CODE. Different nodes can have different blocks for the same
/// tenure.
///
/// Get the highest block in a given tenure (identified by its consensus hash) with a canonical
/// burn_view (i.e., burn_view on the canonical sortition fork)
pub fn find_highest_known_block_header_in_tenure_by_height(
chainstate: &StacksChainState,
sort_db: &SortitionDB,
tenure_height: u64,
) -> Result<Option<StacksHeaderInfo>, ChainstateError> {
let chainstate_db_conn = chainstate.db();

let candidates = Self::get_highest_known_block_header_in_tenure_by_height_at_each_burnview(
chainstate_db_conn,
tenure_height,
)?;

let canonical_sortition_handle = sort_db.index_handle_at_tip();
for candidate in candidates.into_iter() {
let Some(ref candidate_ch) = candidate.burn_view else {
// this is an epoch 2.x header, no burn view to check
return Ok(Some(candidate));
};
let in_canonical_fork = canonical_sortition_handle.processed_block(&candidate_ch)?;
if in_canonical_fork {
return Ok(Some(candidate));
}
}

// did not find any blocks in the tenure
Ok(None)
}

pub fn find_highest_known_block_header_in_tenure_by_block_hash(
chainstate: &StacksChainState,
sort_db: &SortitionDB,
tenure_block_hash: &BurnchainHeaderHash,
) -> Result<Option<StacksHeaderInfo>, ChainstateError> {
let chainstate_db_conn = chainstate.db();

let candidates = Self::get_highest_known_block_header_in_tenure_by_block_hash_at_each_burnview(
chainstate_db_conn,
tenure_block_hash,
)?;

let canonical_sortition_handle = sort_db.index_handle_at_tip();
for candidate in candidates.into_iter() {
let Some(ref candidate_ch) = candidate.burn_view else {
// this is an epoch 2.x header, no burn view to check
return Ok(Some(candidate));
};
let in_canonical_fork = canonical_sortition_handle.processed_block(&candidate_ch)?;
if in_canonical_fork {
return Ok(Some(candidate));
}
}

// did not find any blocks in the tenure
Ok(None)
}

/// DO NOT USE IN CONSENSUS CODE. Different nodes can have different blocks for the same
/// tenure.
///
/// Get the highest blocks in a given tenure (identified by its consensus hash) at each burn view
/// active in that tenure. If there are ties at a given burn view, they will both be returned
fn get_highest_known_block_header_in_tenure_by_height_at_each_burnview(
db: &Connection,
tenure_height: u64,
) -> Result<Vec<StacksHeaderInfo>, ChainstateError> {
// see if we have a nakamoto block in this tenure
let qry = "
SELECT h.*
FROM nakamoto_block_headers h
JOIN (
SELECT burn_view, MAX(block_height) AS max_height
FROM nakamoto_block_headers
WHERE burn_header_height = ?1
GROUP BY burn_view
) maxed
ON h.burn_view = maxed.burn_view
AND h.block_height = maxed.max_height
WHERE h.burn_header_height = ?1
ORDER BY h.block_height DESC, h.timestamp
";
let args = params![tenure_height];
let out = query_rows(db, qry, args)?;
if !out.is_empty() {
return Ok(out);
}

Err(ChainstateError::NoSuchBlockError)
}

fn get_highest_known_block_header_in_tenure_by_block_hash_at_each_burnview(
db: &Connection,
tenure_block_hash: &BurnchainHeaderHash,
) -> Result<Vec<StacksHeaderInfo>, ChainstateError> {
// see if we have a nakamoto block in this tenure
let qry = "
SELECT h.*
FROM nakamoto_block_headers h
JOIN (
SELECT burn_view, MAX(block_height) AS max_height
FROM nakamoto_block_headers
WHERE burn_header_hash = ?1
GROUP BY burn_view
) maxed
ON h.burn_view = maxed.burn_view
AND h.block_height = maxed.max_height
WHERE h.burn_header_hash = ?1
ORDER BY h.block_height DESC, h.timestamp
";
let args = params![tenure_block_hash];
let out = query_rows(db, qry, args)?;
if !out.is_empty() {
return Ok(out);
}

Err(ChainstateError::NoSuchBlockError)
}

/// Get the VRF proof for a Stacks block.
/// For Nakamoto blocks, this is the VRF proof contained in the coinbase of the tenure-start
/// block of the given tenure identified by the consensus hash.
Expand Down Expand Up @@ -3978,6 +4122,7 @@ impl NakamotoChainState {
block_bitvec: &BitVec<4000>,
tenure_block_commit: &LeaderBlockCommitOp,
active_reward_set: &RewardSet,
simulated: bool,
) -> Result<SetupBlockResult<'a, 'b>, ChainstateError> {
// this block's bitvec header must match the miner's block commit punishments
Self::check_pox_bitvector(block_bitvec, tenure_block_commit, active_reward_set)?;
Expand All @@ -3995,6 +4140,7 @@ impl NakamotoChainState {
new_tenure,
coinbase_height,
tenure_extend,
simulated,
)
}

Expand Down Expand Up @@ -4093,6 +4239,7 @@ impl NakamotoChainState {
block_bitvec,
&tenure_block_commit,
active_reward_set,
false,
)
}

Expand Down Expand Up @@ -4139,6 +4286,7 @@ impl NakamotoChainState {
new_tenure: bool,
coinbase_height: u64,
tenure_extend: bool,
simulated: bool,
) -> Result<SetupBlockResult<'a, 'b>, ChainstateError> {
let parent_index_hash = StacksBlockId::new(&parent_consensus_hash, &parent_header_hash);
let parent_sortition_id = sortition_dbconn
Expand Down Expand Up @@ -4196,6 +4344,7 @@ impl NakamotoChainState {
&parent_header_hash,
&MINER_BLOCK_CONSENSUS_HASH,
&MINER_BLOCK_HEADER_HASH,
simulated,
);

// now that we have access to the ClarityVM, we can account for reward deductions from
Expand Down
1 change: 1 addition & 0 deletions stackslib/src/chainstate/nakamoto/shadow.rs
Original file line number Diff line number Diff line change
Expand Up @@ -414,6 +414,7 @@ impl NakamotoChainState {
new_tenure,
coinbase_height,
tenure_extend,
false,
)
}
}
Expand Down
2 changes: 1 addition & 1 deletion stackslib/src/chainstate/nakamoto/tests/node.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1007,7 +1007,7 @@ impl TestStacksNode {
let mut miner_tenure_info =
builder.load_tenure_info(&mut chainstate, burn_dbconn, tenure_cause)?;
let burn_chain_height = miner_tenure_info.burn_tip_height;
let mut tenure_tx = builder.tenure_begin(burn_dbconn, &mut miner_tenure_info)?;
let mut tenure_tx = builder.tenure_begin(burn_dbconn, &mut miner_tenure_info, false)?;
for tx in txs.into_iter() {
let tx_len = tx.tx_len();
match builder.try_mine_tx_with_len(
Expand Down
1 change: 1 addition & 0 deletions stackslib/src/chainstate/stacks/db/blocks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5128,6 +5128,7 @@ impl StacksChainState {
&parent_header_hash,
&MINER_BLOCK_CONSENSUS_HASH,
&MINER_BLOCK_HEADER_HASH,
false,
);

clarity_tx.reset_cost(parent_block_cost.clone());
Expand Down
66 changes: 59 additions & 7 deletions stackslib/src/chainstate/stacks/db/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -304,7 +304,7 @@ impl StacksBlockHeaderTypes {
StacksBlockHeaderTypes::Nakamoto(x) => x.block_hash(),
}
}

pub fn is_first_mined(&self) -> bool {
match self {
StacksBlockHeaderTypes::Epoch2(x) => x.is_first_mined(),
Expand Down Expand Up @@ -2023,6 +2023,7 @@ impl StacksChainState {
parent_block: &BlockHeaderHash,
new_consensus_hash: &ConsensusHash,
new_block: &BlockHeaderHash,
simulated: bool,
) -> ClarityTx<'a, 'b> {
let conf = chainstate_tx.config.clone();
StacksChainState::inner_clarity_tx_begin(
Expand All @@ -2034,6 +2035,7 @@ impl StacksChainState {
parent_block,
new_consensus_hash,
new_block,
simulated,
)
}

Expand All @@ -2057,6 +2059,7 @@ impl StacksChainState {
parent_block,
new_consensus_hash,
new_block,
false,
)
}

Expand Down Expand Up @@ -2122,6 +2125,45 @@ impl StacksChainState {
self.clarity_state.with_marf(f)
}

pub fn with_simulated_clarity_tx<F, R>(
&mut self,
burn_dbconn: &dyn BurnStateDB,
parent_block_id: &StacksBlockId,
new_block_id: &StacksBlockId,
to_do: F,
) -> Option<R>
where
F: FnOnce(&mut ClarityTx) -> R,
{
match NakamotoChainState::get_block_header(self.db(), parent_block_id) {
Ok(Some(_)) => {}
Ok(None) => {
return None;
}
Err(e) => {
warn!("Failed to query for {}: {:?}", parent_block_id, &e);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So this doesn't work for Stacks 2.x replay?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

currently yes, but I would like to cover pre-nakamoto too

return None;
}
}

let dbconfig = self.config();

let conn = self.clarity_state.begin_simulated_block(
parent_block_id,
new_block_id,
&self.state_index,
burn_dbconn,
);

let mut clarity_tx = ClarityTx {
block: conn,
config: dbconfig,
};
let result = to_do(&mut clarity_tx);
clarity_tx.rollback_block();
Some(result)
}

/// Run to_do on the state of the Clarity VM at the given chain tip.
/// Returns Some(x: R) if the given parent_tip exists.
/// Returns None if not
Expand Down Expand Up @@ -2287,6 +2329,7 @@ impl StacksChainState {
parent_block: &BlockHeaderHash,
new_consensus_hash: &ConsensusHash,
new_block: &BlockHeaderHash,
simulated: bool,
) -> ClarityTx<'a, 'b> {
// mix consensus hash and stacks block header hash together, since the stacks block hash
// it not guaranteed to be globally unique (but the pair is)
Expand Down Expand Up @@ -2314,12 +2357,21 @@ impl StacksChainState {
parent_block
);

let inner_clarity_tx = clarity_instance.begin_block(
&parent_index_block,
&new_index_block,
headers_db,
burn_dbconn,
);
let inner_clarity_tx = if simulated {
clarity_instance.begin_simulated_block(
&parent_index_block,
&new_index_block,
headers_db,
burn_dbconn,
)
} else {
clarity_instance.begin_block(
&parent_index_block,
&new_index_block,
headers_db,
burn_dbconn,
)
};

test_debug!("Got clarity TX!");
ClarityTx {
Expand Down
Loading