Skip to content

Commit

Permalink
Merge branch 'develop' of https://github.com/stacks-network/stacks-core
Browse files Browse the repository at this point in the history
… into feat/tenure-extend-transactions
  • Loading branch information
jferrant committed Jun 6, 2024
2 parents 2d32ba0 + 3d96d53 commit 91f26af
Show file tree
Hide file tree
Showing 129 changed files with 7,678 additions and 2,254 deletions.
7 changes: 6 additions & 1 deletion .github/workflows/bitcoin-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -82,17 +82,22 @@ jobs:
- tests::nakamoto_integrations::vote_for_aggregate_key_burn_op
- tests::nakamoto_integrations::follower_bootup
- tests::nakamoto_integrations::forked_tenure_is_ignored
- tests::nakamoto_integrations::nakamoto_attempt_time
- tests::signer::v0::block_proposal_rejection
- tests::signer::v0::miner_gather_signatures
- tests::signer::v1::dkg
- tests::signer::v1::sign_request_rejected
- tests::signer::v1::filter_bad_transactions
# TODO: enable these once v1 signer is fixed
# - tests::signer::v1::filter_bad_transactions
- tests::signer::v1::delayed_dkg
# TODO: enable these once v1 signer is fixed
# - tests::signer::v1::mine_2_nakamoto_reward_cycles
# - tests::signer::v1::sign_after_signer_reboot
# - tests::signer::v1::block_proposal
- tests::nakamoto_integrations::stack_stx_burn_op_integration_test
- tests::nakamoto_integrations::check_block_heights
- tests::nakamoto_integrations::clarity_burn_state
- tests::nakamoto_integrations::continue_tenure_extend
# Do not run this one until we figure out why it fails in CI
# - tests::neon_integrations::bitcoin_reorg_flap
# - tests::neon_integrations::bitcoin_reorg_flap_with_follower
Expand Down
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@ and this project adheres to the versioning scheme outlined in the [README.md](RE

- Downgraded log messages about transactions from warning to info (#4697)

### Fixed

- Allow Nakamoto blocks to access the burn block associated with the current tenure (#4333)

## [2.5.0.0.3]

This release fixes a regression in `2.5.0.0.0` from `2.4.0.1.0` caused by git merge
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ cd testnet/stacks-node
cargo run --bin stacks-node -- start --config ./conf/testnet-follower-conf.toml
```

_On Windows, many tests will fail if the line endings aren't `LF`. Please ensure that you are have git's `core.autocrlf` set to `input` when you clone the repository to avoid any potential issues. This is due to the Clarity language currently being sensitive to line endings._
_On Windows, many tests will fail if the line endings aren't `LF`. Please ensure that you have git's `core.autocrlf` set to `input` when you clone the repository to avoid any potential issues. This is due to the Clarity language currently being sensitive to line endings._

Additional testnet documentation is available [here](./docs/testnet.md) and [here](https://docs.stacks.co/docs/nodes-and-miners/miner-testnet)

Expand Down
240 changes: 80 additions & 160 deletions clarity/src/vm/database/clarity_db.rs
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,11 @@ pub trait HeadersDB {
}

pub trait BurnStateDB {
/// Get the burn chain height at the current tip.
fn get_tip_burn_block_height(&self) -> Option<u32>;
/// Get the sortition id for the current tip.
fn get_tip_sortition_id(&self) -> Option<SortitionId>;

fn get_v1_unlock_height(&self) -> u32;
fn get_v2_unlock_height(&self) -> u32;
fn get_v3_unlock_height(&self) -> u32;
Expand Down Expand Up @@ -150,118 +155,6 @@ pub trait BurnStateDB {
) -> Option<(Vec<TupleData>, u128)>;
}

impl HeadersDB for &dyn HeadersDB {
fn get_stacks_block_header_hash_for_block(
&self,
id_bhh: &StacksBlockId,
) -> Option<BlockHeaderHash> {
(*self).get_stacks_block_header_hash_for_block(id_bhh)
}
fn get_burn_header_hash_for_block(&self, bhh: &StacksBlockId) -> Option<BurnchainHeaderHash> {
(*self).get_burn_header_hash_for_block(bhh)
}
fn get_consensus_hash_for_block(&self, id_bhh: &StacksBlockId) -> Option<ConsensusHash> {
(*self).get_consensus_hash_for_block(id_bhh)
}
fn get_vrf_seed_for_block(&self, bhh: &StacksBlockId) -> Option<VRFSeed> {
(*self).get_vrf_seed_for_block(bhh)
}
fn get_burn_block_time_for_block(&self, bhh: &StacksBlockId) -> Option<u64> {
(*self).get_burn_block_time_for_block(bhh)
}
fn get_burn_block_height_for_block(&self, bhh: &StacksBlockId) -> Option<u32> {
(*self).get_burn_block_height_for_block(bhh)
}
fn get_miner_address(&self, bhh: &StacksBlockId) -> Option<StacksAddress> {
(*self).get_miner_address(bhh)
}
fn get_burnchain_tokens_spent_for_block(&self, id_bhh: &StacksBlockId) -> Option<u128> {
(*self).get_burnchain_tokens_spent_for_block(id_bhh)
}
fn get_burnchain_tokens_spent_for_winning_block(&self, id_bhh: &StacksBlockId) -> Option<u128> {
(*self).get_burnchain_tokens_spent_for_winning_block(id_bhh)
}
fn get_tokens_earned_for_block(&self, id_bhh: &StacksBlockId) -> Option<u128> {
(*self).get_tokens_earned_for_block(id_bhh)
}
}

impl BurnStateDB for &dyn BurnStateDB {
fn get_v1_unlock_height(&self) -> u32 {
(*self).get_v1_unlock_height()
}

fn get_v2_unlock_height(&self) -> u32 {
(*self).get_v2_unlock_height()
}

fn get_v3_unlock_height(&self) -> u32 {
(*self).get_v3_unlock_height()
}

fn get_pox_3_activation_height(&self) -> u32 {
(*self).get_pox_3_activation_height()
}

fn get_pox_4_activation_height(&self) -> u32 {
(*self).get_pox_4_activation_height()
}

fn get_burn_block_height(&self, sortition_id: &SortitionId) -> Option<u32> {
(*self).get_burn_block_height(sortition_id)
}

fn get_sortition_id_from_consensus_hash(
&self,
consensus_hash: &ConsensusHash,
) -> Option<SortitionId> {
(*self).get_sortition_id_from_consensus_hash(consensus_hash)
}

fn get_burn_start_height(&self) -> u32 {
(*self).get_burn_start_height()
}

fn get_burn_header_hash(
&self,
height: u32,
sortition_id: &SortitionId,
) -> Option<BurnchainHeaderHash> {
(*self).get_burn_header_hash(height, sortition_id)
}

fn get_stacks_epoch(&self, height: u32) -> Option<StacksEpoch> {
(*self).get_stacks_epoch(height)
}

fn get_pox_prepare_length(&self) -> u32 {
(*self).get_pox_prepare_length()
}

fn get_pox_reward_cycle_length(&self) -> u32 {
(*self).get_pox_reward_cycle_length()
}

fn get_pox_rejection_fraction(&self) -> u64 {
(*self).get_pox_rejection_fraction()
}
fn get_stacks_epoch_by_epoch_id(&self, epoch_id: &StacksEpochId) -> Option<StacksEpoch> {
(*self).get_stacks_epoch_by_epoch_id(epoch_id)
}

fn get_ast_rules(&self, height: u32) -> ASTRules {
(*self).get_ast_rules(height)
}

fn get_pox_payout_addrs(
&self,
height: u32,
sortition_id: &SortitionId,
) -> Option<(Vec<TupleData>, u128)> {
(*self).get_pox_payout_addrs(height, sortition_id)
}
}

pub struct NullHeadersDB {}
pub struct NullBurnStateDB {
epoch: StacksEpochId,
Expand Down Expand Up @@ -339,6 +232,14 @@ impl HeadersDB for NullHeadersDB {

#[allow(clippy::panic)]
impl BurnStateDB for NullBurnStateDB {
fn get_tip_burn_block_height(&self) -> Option<u32> {
Some(0)
}

fn get_tip_sortition_id(&self) -> Option<SortitionId> {
None
}

fn get_burn_block_height(&self, _sortition_id: &SortitionId) -> Option<u32> {
None
}
Expand Down Expand Up @@ -964,26 +865,35 @@ impl<'a> ClarityDatabase<'a> {
/// `get_current_block_height`).
pub fn get_current_burnchain_block_height(&mut self) -> Result<u32> {
let cur_stacks_height = self.store.get_current_block_height();
let last_mined_bhh = if cur_stacks_height == 0 {
return Ok(self.burn_state_db.get_burn_start_height());
} else {
self.get_index_block_header_hash(cur_stacks_height.checked_sub(1).ok_or_else(
|| {
InterpreterError::Expect(
"BUG: cannot eval burn-block-height in boot code".into(),
)
},
)?)?
};

self.get_burnchain_block_height(&last_mined_bhh)
.ok_or_else(|| {
InterpreterError::Expect(format!(
"Block header hash '{}' must return for provided stacks block height {}",
&last_mined_bhh, cur_stacks_height
))
.into()
})
// Before epoch 3.0, we can only access the burn block associated with the last block
if !self
.get_clarity_epoch_version()?
.clarity_uses_tip_burn_block()
{
if cur_stacks_height == 0 {
return Ok(self.burn_state_db.get_burn_start_height());
};
// Safety note: normal subtraction is safe here, because we've already checked
// that cur_stacks_height > 0.
let last_mined_bhh = self.get_index_block_header_hash(cur_stacks_height - 1)?;

self.get_burnchain_block_height(&last_mined_bhh)
.ok_or_else(|| {
InterpreterError::Expect(format!(
"Block header hash '{}' must return for provided stacks block height {}",
&last_mined_bhh, cur_stacks_height
))
.into()
})
} else {
// In epoch 3+, we can access the current burnchain block
self.burn_state_db
.get_tip_burn_block_height()
.ok_or_else(|| {
InterpreterError::Expect("Failed to get burnchain tip height.".into()).into()
})
}
}

pub fn get_block_header_hash(&mut self, block_height: u32) -> Result<BlockHeaderHash> {
Expand All @@ -1010,46 +920,56 @@ impl<'a> ClarityDatabase<'a> {
.ok_or_else(|| InterpreterError::Expect("Failed to get block data.".into()).into())
}

/// In Epoch 2.x:
/// 1. Get the current Stacks tip height (which is in the process of being evaluated)
/// 2. Get the parent block's StacksBlockId, which is SHA512-256(consensus_hash, block_hash).
/// This is the highest Stacks block in this fork whose consensus hash is known.
/// 3. Resolve the parent StacksBlockId to its consensus hash
/// 4. Resolve the consensus hash to the associated SortitionId
/// In Epoch 3+:
/// 1. Get the SortitionId of the current Stacks tip
fn get_sortition_id_for_stacks_tip(&mut self) -> Result<Option<SortitionId>> {
let current_stacks_height = self.get_current_block_height();
if !self
.get_clarity_epoch_version()?
.clarity_uses_tip_burn_block()
{
let current_stacks_height = self.get_current_block_height();

if current_stacks_height < 1 {
// we are in the Stacks genesis block
return Ok(None);
}
if current_stacks_height < 1 {
// we are in the Stacks genesis block
return Ok(None);
}

// this is the StacksBlockId of the last block evaluated in this fork
let parent_id_bhh = self.get_index_block_header_hash(current_stacks_height - 1)?;
// this is the StacksBlockId of the last block evaluated in this fork
let parent_id_bhh = self.get_index_block_header_hash(current_stacks_height - 1)?;

// infallible, since we always store the consensus hash with the StacksBlockId in the
// headers DB
let consensus_hash = self
.headers_db
.get_consensus_hash_for_block(&parent_id_bhh)
.ok_or_else(|| {
InterpreterError::Expect(format!(
"FATAL: no consensus hash found for StacksBlockId {}",
&parent_id_bhh
))
})?;
// infallible, since we always store the consensus hash with the StacksBlockId in the
// headers DB
let consensus_hash = self
.headers_db
.get_consensus_hash_for_block(&parent_id_bhh)
.ok_or_else(|| {
InterpreterError::Expect(format!(
"FATAL: no consensus hash found for StacksBlockId {}",
&parent_id_bhh
))
})?;

// infallible, since every sortition has a consensus hash
let sortition_id = self
.burn_state_db
.get_sortition_id_from_consensus_hash(&consensus_hash)
.ok_or_else(|| {
InterpreterError::Expect(format!(
"FATAL: no SortitionID found for consensus hash {}",
&consensus_hash
))
})?;
// infallible, since every sortition has a consensus hash
let sortition_id = self
.burn_state_db
.get_sortition_id_from_consensus_hash(&consensus_hash)
.ok_or_else(|| {
InterpreterError::Expect(format!(
"FATAL: no SortitionID found for consensus hash {}",
&consensus_hash
))
})?;

Ok(Some(sortition_id))
Ok(Some(sortition_id))
} else {
Ok(self.burn_state_db.get_tip_sortition_id())
}
}

/// Fetch the burnchain block header hash for a given burnchain height.
Expand Down
8 changes: 8 additions & 0 deletions clarity/src/vm/docs/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2766,6 +2766,14 @@ mod test {
const DOC_POX_STATE_DB: DocBurnStateDB = DocBurnStateDB {};

impl BurnStateDB for DocBurnStateDB {
fn get_tip_burn_block_height(&self) -> Option<u32> {
Some(0x9abc)
}

fn get_tip_sortition_id(&self) -> Option<SortitionId> {
Some(SortitionId([0u8; 32]))
}

fn get_burn_block_height(&self, _sortition_id: &SortitionId) -> Option<u32> {
Some(5678)
}
Expand Down
8 changes: 8 additions & 0 deletions clarity/src/vm/test_util/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,14 @@ impl HeadersDB for UnitTestHeaderDB {
}

impl BurnStateDB for UnitTestBurnStateDB {
fn get_tip_burn_block_height(&self) -> Option<u32> {
None
}

fn get_tip_sortition_id(&self) -> Option<SortitionId> {
None
}

fn get_burn_block_height(&self, _sortition_id: &SortitionId) -> Option<u32> {
None
}
Expand Down
20 changes: 15 additions & 5 deletions clarity/src/vm/tests/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,16 @@ pub fn tl_env_factory() -> TopLevelMemoryEnvironmentGenerator {
pub struct MemoryEnvironmentGenerator(MemoryBackingStore);
impl MemoryEnvironmentGenerator {
fn get_env(&mut self, epoch: StacksEpochId) -> OwnedEnvironment {
let mut owned_env = OwnedEnvironment::new(self.0.as_clarity_db(), epoch);
let mut db = self.0.as_clarity_db();
db.begin();
db.set_clarity_epoch_version(epoch).unwrap();
db.commit().unwrap();
if epoch.clarity_uses_tip_burn_block() {
db.begin();
db.set_tenure_height(1).unwrap();
db.commit().unwrap();
}
let mut owned_env = OwnedEnvironment::new(db, epoch);
// start an initial transaction.
owned_env.begin();
owned_env
Expand All @@ -176,11 +185,12 @@ impl TopLevelMemoryEnvironmentGenerator {
db.begin();
db.set_clarity_epoch_version(epoch).unwrap();
db.commit().unwrap();
let mut owned_env = OwnedEnvironment::new(db, epoch);
if epoch >= StacksEpochId::Epoch30 {
owned_env.set_tenure_height(1);
if epoch.clarity_uses_tip_burn_block() {
db.begin();
db.set_tenure_height(1).unwrap();
db.commit().unwrap();
}
owned_env
OwnedEnvironment::new(db, epoch)
}
}

Expand Down
Loading

0 comments on commit 91f26af

Please sign in to comment.