Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat: block info handling in Clarity 2 contracts post-3.0 #5321

Merged
merged 13 commits into from
Oct 17, 2024
Merged
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
45 changes: 45 additions & 0 deletions clarity/src/vm/database/clarity_db.rs
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,11 @@ pub trait HeadersDB {
id_bhh: &StacksBlockId,
epoch: &StacksEpochId,
) -> Option<u128>;
fn get_stacks_height_for_tenure_height(
&self,
tip: &StacksBlockId,
tenure_height: u32,
) -> Option<u32>;
}

pub trait BurnStateDB {
Expand Down Expand Up @@ -285,6 +290,13 @@ impl HeadersDB for NullHeadersDB {
) -> Option<u128> {
None
}
fn get_stacks_height_for_tenure_height(
&self,
_tip: &StacksBlockId,
_tenure_height: u32,
) -> Option<u32> {
None
}
}

#[allow(clippy::panic)]
Expand Down Expand Up @@ -915,6 +927,39 @@ impl<'a> ClarityDatabase<'a> {
}
}

/// Return the block height for a given tenure height
/// if the block information is queryable for the tenure height.
/// The block information for a given tenure height is queryable iff:
/// * `tenure_height` falls in 2.x, and `tenure_height` < `current_height`
/// * `tenure_height` falls in 3.x, and the first block of the tenure
/// at `tenure_height` has a stacks block height less than `current_height`
///
/// If the block information isn't queryable, return `Ok(None)`
pub fn get_block_height_for_tenure_height(
&mut self,
tenure_height: u32,
) -> Result<Option<u32>> {
let current_tenure_height = self.get_tenure_height()?;
if current_tenure_height < tenure_height {
return Ok(None);
}
let current_height = self.get_current_block_height();
if current_height <= tenure_height {
kantai marked this conversation as resolved.
Show resolved Hide resolved
return Ok(None);
}
// check if we're querying a 2.x block
let id_bhh = self.get_index_block_header_hash(tenure_height)?;
let epoch = self.get_stacks_epoch_for_block(&id_bhh)?;
if !epoch.uses_nakamoto_blocks() {
return Ok(Some(tenure_height));
}
// query from the parent
let query_tip = self.get_index_block_header_hash(current_height.saturating_sub(1))?;
Ok(self
.headers_db
.get_stacks_height_for_tenure_height(&query_tip, tenure_height.into()))
}

/// Get the last-known burnchain block height.
/// Note that this is _not_ the burnchain height in which this block was mined!
/// This is the burnchain block height of the parent of the Stacks block at the current Stacks
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 @@ -2863,6 +2863,14 @@ mod test {
) -> Option<u128> {
Some(12000)
}

fn get_stacks_height_for_tenure_height(
&self,
_tip: &StacksBlockId,
tenure_height: u32,
) -> Option<u32> {
Some(tenure_height)
}
}

struct DocBurnStateDB {}
Expand Down
24 changes: 24 additions & 0 deletions clarity/src/vm/functions/database.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

use std::cmp;

use stacks_common::consts::CHAIN_ID_TESTNET;
use stacks_common::types::chainstate::StacksBlockId;
use stacks_common::types::StacksEpochId;

Expand Down Expand Up @@ -769,6 +770,29 @@ pub fn special_get_block_info(
_ => return Ok(Value::none()),
};

// interpret height as a tenure height IFF
// * clarity version is less than Clarity3
// * the evaluated epoch is geq 3.0
// * we are not on (classic) primary testnet
let interpret_height_as_tenure_height = env.contract_context.get_clarity_version()
< &ClarityVersion::Clarity3
&& env.global_context.epoch_id >= StacksEpochId::Epoch30
&& env.global_context.chain_id != CHAIN_ID_TESTNET;
jcnelson marked this conversation as resolved.
Show resolved Hide resolved

let height_value = if !interpret_height_as_tenure_height {
height_value
} else {
// interpretting height_value as a tenure height
let height_opt = env
.global_context
.database
.get_block_height_for_tenure_height(height_value)?;
match height_opt {
Some(x) => x,
None => return Ok(Value::none()),
}
};

let current_block_height = env.global_context.database.get_current_block_height();
if height_value >= current_block_height {
return Ok(Value::none());
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 @@ -229,6 +229,14 @@ impl HeadersDB for UnitTestHeaderDB {
// if the block is defined at all, then return a constant
self.get_burn_block_height_for_block(id_bhh).map(|_| 3000)
}

fn get_stacks_height_for_tenure_height(
&self,
_tip: &StacksBlockId,
tenure_height: u32,
) -> Option<u32> {
Some(tenure_height)
}
}

impl BurnStateDB for UnitTestBurnStateDB {
Expand Down
2 changes: 1 addition & 1 deletion clarity/src/vm/tests/contracts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@ fn test_get_block_info_eval(
.unwrap();

let mut env = owned_env.get_exec_environment(None, None, &mut placeholder_context);

eprintln!("{}", contracts[i]);
let eval_result = env.eval_read_only(&contract_identifier, "(test-func)");
match expected[i] {
// any (some UINT) is okay for checking get-block-info? time
Expand Down
16 changes: 6 additions & 10 deletions stackslib/src/burnchains/tests/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -123,11 +123,13 @@ pub struct TestMiner {
pub nonce: u64,
pub spent_at_nonce: HashMap<u64, u128>, // how much uSTX this miner paid in a given tx's nonce
pub test_with_tx_fees: bool, // set to true to make certain helper methods attach a pre-defined tx fee
pub chain_id: u32,
}

pub struct TestMinerFactory {
pub key_seed: [u8; 32],
pub next_miner_id: usize,
pub chain_id: u32,
}

impl TestMiner {
Expand All @@ -136,6 +138,7 @@ impl TestMiner {
privks: &Vec<StacksPrivateKey>,
num_sigs: u16,
hash_mode: &AddressHashMode,
chain_id: u32,
) -> TestMiner {
TestMiner {
burnchain: burnchain.clone(),
Expand All @@ -150,6 +153,7 @@ impl TestMiner {
nonce: 0,
spent_at_nonce: HashMap::new(),
test_with_tx_fees: true,
chain_id,
}
}

Expand Down Expand Up @@ -314,15 +318,7 @@ impl TestMinerFactory {
TestMinerFactory {
key_seed: [0u8; 32],
next_miner_id: 1,
}
}

pub fn from_u16(seed: u16) -> TestMinerFactory {
let mut bytes = [0u8; 32];
(&mut bytes[0..2]).copy_from_slice(&seed.to_be_bytes());
TestMinerFactory {
key_seed: bytes,
next_miner_id: seed as usize,
chain_id: CHAIN_ID_TESTNET,
}
}

Expand All @@ -346,7 +342,7 @@ impl TestMinerFactory {
}

test_debug!("New miner: {:?} {}:{:?}", &hash_mode, num_sigs, &keys);
let mut m = TestMiner::new(burnchain, &keys, num_sigs, &hash_mode);
let mut m = TestMiner::new(burnchain, &keys, num_sigs, &hash_mode, self.chain_id);
m.id = self.next_miner_id;
self.next_miner_id += 1;
m
Expand Down
Loading
Loading