Skip to content

Commit d7f0ba2

Browse files
committed
feat: interpret block heights as tenure heights in Clarity 2 contracts executing post-3.0
1 parent 609ad13 commit d7f0ba2

File tree

9 files changed

+475
-3
lines changed

9 files changed

+475
-3
lines changed

clarity/src/vm/database/clarity_db.rs

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,11 @@ pub trait HeadersDB {
127127
id_bhh: &StacksBlockId,
128128
epoch: &StacksEpochId,
129129
) -> Option<u128>;
130+
fn get_stacks_height_for_tenure_height(
131+
&self,
132+
tip: &StacksBlockId,
133+
tenure_height: u32,
134+
) -> Option<u32>;
130135
}
131136

132137
pub trait BurnStateDB {
@@ -285,6 +290,13 @@ impl HeadersDB for NullHeadersDB {
285290
) -> Option<u128> {
286291
None
287292
}
293+
fn get_stacks_height_for_tenure_height(
294+
&self,
295+
_tip: &StacksBlockId,
296+
_tenure_height: u32,
297+
) -> Option<u32> {
298+
None
299+
}
288300
}
289301

290302
#[allow(clippy::panic)]
@@ -915,6 +927,25 @@ impl<'a> ClarityDatabase<'a> {
915927
}
916928
}
917929

930+
pub fn get_block_height_for_tenure_height(
931+
&mut self,
932+
tenure_height: u32,
933+
) -> Result<Option<u32>> {
934+
let current_tenure_height = self.get_tenure_height()?;
935+
if current_tenure_height < tenure_height {
936+
return Ok(None);
937+
}
938+
if current_tenure_height == tenure_height {
939+
return Ok(Some(self.get_current_block_height()));
940+
}
941+
let current_height = self.get_current_block_height();
942+
// query from the parent
943+
let query_tip = self.get_index_block_header_hash(current_height.saturating_sub(1))?;
944+
Ok(self
945+
.headers_db
946+
.get_stacks_height_for_tenure_height(&query_tip, tenure_height.into()))
947+
}
948+
918949
/// Get the last-known burnchain block height.
919950
/// Note that this is _not_ the burnchain height in which this block was mined!
920951
/// This is the burnchain block height of the parent of the Stacks block at the current Stacks

clarity/src/vm/docs/mod.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2863,6 +2863,14 @@ mod test {
28632863
) -> Option<u128> {
28642864
Some(12000)
28652865
}
2866+
2867+
fn get_stacks_height_for_tenure_height(
2868+
&self,
2869+
_tip: &StacksBlockId,
2870+
tenure_height: u32,
2871+
) -> Option<u32> {
2872+
Some(tenure_height)
2873+
}
28662874
}
28672875

28682876
struct DocBurnStateDB {}

clarity/src/vm/functions/database.rs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -769,6 +769,24 @@ pub fn special_get_block_info(
769769
_ => return Ok(Value::none()),
770770
};
771771

772+
let height_value = if env.contract_context.get_clarity_version() < &ClarityVersion::Clarity3 {
773+
if env.global_context.epoch_id < StacksEpochId::Epoch30 {
774+
height_value
775+
} else {
776+
// interpretting height_value as a tenure height
777+
let height_opt = env
778+
.global_context
779+
.database
780+
.get_block_height_for_tenure_height(height_value)?;
781+
match height_opt {
782+
Some(x) => x,
783+
None => return Ok(Value::none()),
784+
}
785+
}
786+
} else {
787+
height_value
788+
};
789+
772790
let current_block_height = env.global_context.database.get_current_block_height();
773791
if height_value >= current_block_height {
774792
return Ok(Value::none());

clarity/src/vm/test_util/mod.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -229,6 +229,14 @@ impl HeadersDB for UnitTestHeaderDB {
229229
// if the block is defined at all, then return a constant
230230
self.get_burn_block_height_for_block(id_bhh).map(|_| 3000)
231231
}
232+
233+
fn get_stacks_height_for_tenure_height(
234+
&self,
235+
_tip: &StacksBlockId,
236+
tenure_height: u32,
237+
) -> Option<u32> {
238+
Some(tenure_height)
239+
}
232240
}
233241

234242
impl BurnStateDB for UnitTestBurnStateDB {

stackslib/src/chainstate/nakamoto/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3886,7 +3886,7 @@ impl NakamotoChainState {
38863886

38873887
/// Append a Nakamoto Stacks block to the Stacks chain state.
38883888
/// NOTE: This does _not_ set the block as processed! The caller must do this.
3889-
fn append_block<'a>(
3889+
pub(crate) fn append_block<'a>(
38903890
chainstate_tx: &mut ChainstateTx,
38913891
clarity_instance: &'a mut ClarityInstance,
38923892
burn_dbconn: &mut SortitionHandleConn,

stackslib/src/chainstate/stacks/boot/contract_tests.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -649,6 +649,14 @@ impl HeadersDB for TestSimHeadersDB {
649649
// if the block is defined at all, then return a constant
650650
self.get_burn_block_height_for_block(id_bhh).map(|_| 3000)
651651
}
652+
653+
fn get_stacks_height_for_tenure_height(
654+
&self,
655+
_tip: &StacksBlockId,
656+
tenure_height: u32,
657+
) -> Option<u32> {
658+
Some(tenure_height)
659+
}
652660
}
653661

654662
#[test]

stackslib/src/clarity_cli.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -766,6 +766,14 @@ impl HeadersDB for CLIHeadersDB {
766766
// if the block is defined at all, then return a constant
767767
get_cli_block_height(&self.conn(), id_bhh).map(|_| 3000)
768768
}
769+
770+
fn get_stacks_height_for_tenure_height(
771+
&self,
772+
_tip: &StacksBlockId,
773+
tenure_height: u32,
774+
) -> Option<u32> {
775+
Some(tenure_height)
776+
}
769777
}
770778

771779
fn get_eval_input(invoked_by: &str, args: &[String]) -> EvalInput {

stackslib/src/clarity_vm/database/mod.rs

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,22 @@ pub trait GetTenureStartId {
4747
tip: &StacksBlockId,
4848
tenure_id_consensus_hash: &ConsensusHash,
4949
) -> Result<Option<TenureBlockId>, DBError>;
50+
fn get_tenure_ch_at_cb_height(
51+
&self,
52+
tip: &StacksBlockId,
53+
coinbase_height: u64,
54+
) -> Result<Option<ConsensusHash>, DBError>;
5055
fn conn(&self) -> &Connection;
56+
fn get_tenure_block_id_at_cb_height(
57+
&self,
58+
tip: &StacksBlockId,
59+
coinbase_height: u64,
60+
) -> Result<Option<TenureBlockId>, DBError> {
61+
let Some(tenure_ch) = self.get_tenure_ch_at_cb_height(tip, coinbase_height)? else {
62+
return Ok(None);
63+
};
64+
self.get_tenure_block_id(tip, &tenure_ch)
65+
}
5166
}
5267

5368
impl GetTenureStartId for StacksDBConn<'_> {
@@ -66,6 +81,21 @@ impl GetTenureStartId for StacksDBConn<'_> {
6681
.map(|block_id| TenureBlockId::from(block_id)))
6782
}
6883

84+
fn get_tenure_ch_at_cb_height(
85+
&self,
86+
tip: &StacksBlockId,
87+
coinbase_height: u64,
88+
) -> Result<Option<ConsensusHash>, DBError> {
89+
let opt_out = self
90+
.get_indexed(
91+
tip,
92+
&nakamoto_keys::ongoing_tenure_coinbase_height(coinbase_height),
93+
)?
94+
.map(|hex_inp| nakamoto_keys::parse_consensus_hash(&hex_inp))
95+
.flatten();
96+
Ok(opt_out)
97+
}
98+
6999
fn conn(&self) -> &Connection {
70100
self.sqlite()
71101
}
@@ -87,6 +117,21 @@ impl GetTenureStartId for StacksDBTx<'_> {
87117
.map(|block_id| TenureBlockId::from(block_id)))
88118
}
89119

120+
fn get_tenure_ch_at_cb_height(
121+
&self,
122+
tip: &StacksBlockId,
123+
coinbase_height: u64,
124+
) -> Result<Option<ConsensusHash>, DBError> {
125+
let opt_out = self
126+
.get_indexed_ref(
127+
tip,
128+
&nakamoto_keys::ongoing_tenure_coinbase_height(coinbase_height),
129+
)?
130+
.map(|hex_inp| nakamoto_keys::parse_consensus_hash(&hex_inp))
131+
.flatten();
132+
Ok(opt_out)
133+
}
134+
90135
fn conn(&self) -> &Connection {
91136
self.sqlite()
92137
}
@@ -105,6 +150,15 @@ impl GetTenureStartId for MARF<StacksBlockId> {
105150
fn conn(&self) -> &Connection {
106151
self.sqlite_conn()
107152
}
153+
154+
fn get_tenure_ch_at_cb_height(
155+
&self,
156+
tip: &StacksBlockId,
157+
coinbase_height: u64,
158+
) -> Result<Option<ConsensusHash>, DBError> {
159+
let dbconn = StacksDBConn::new(self, ());
160+
dbconn.get_tenure_ch_at_cb_height(tip, coinbase_height)
161+
}
108162
}
109163

110164
pub struct HeadersDBConn<'a>(pub StacksDBConn<'a>);
@@ -188,6 +242,22 @@ impl<'a> HeadersDB for HeadersDBConn<'a> {
188242
})
189243
}
190244

245+
fn get_stacks_height_for_tenure_height(
246+
&self,
247+
tip: &StacksBlockId,
248+
tenure_height: u32,
249+
) -> Option<u32> {
250+
let tenure_block_id =
251+
GetTenureStartId::get_tenure_block_id_at_cb_height(&self.0, tip, tenure_height.into())
252+
.expect("FATAL: bad DB data for tenure height lookups")?;
253+
get_stacks_header_column(self.0.conn(), &tenure_block_id.0, "block_height", |r| {
254+
u64::from_row(r)
255+
.expect("FATAL: malformed block_height")
256+
.try_into()
257+
.expect("FATAL: blockchain too long")
258+
})
259+
}
260+
191261
fn get_vrf_seed_for_block(
192262
&self,
193263
id_bhh: &StacksBlockId,
@@ -417,6 +487,25 @@ impl<'a> HeadersDB for ChainstateTx<'a> {
417487
let tenure_id_bhh = get_first_block_in_tenure(self.deref(), id_bhh, Some(epoch));
418488
get_matured_reward(self.deref(), &tenure_id_bhh, epoch).map(|x| x.total().into())
419489
}
490+
491+
fn get_stacks_height_for_tenure_height(
492+
&self,
493+
tip: &StacksBlockId,
494+
tenure_height: u32,
495+
) -> Option<u32> {
496+
let tenure_block_id = GetTenureStartId::get_tenure_block_id_at_cb_height(
497+
self.deref(),
498+
tip,
499+
tenure_height.into(),
500+
)
501+
.expect("FATAL: bad DB data for tenure height lookups")?;
502+
get_stacks_header_column(self.deref(), &tenure_block_id.0, "block_height", |r| {
503+
u64::from_row(r)
504+
.expect("FATAL: malformed block_height")
505+
.try_into()
506+
.expect("FATAL: blockchain too long")
507+
})
508+
}
420509
}
421510

422511
impl HeadersDB for MARF<StacksBlockId> {
@@ -572,6 +661,27 @@ impl HeadersDB for MARF<StacksBlockId> {
572661
let tenure_id_bhh = get_first_block_in_tenure(self, id_bhh, Some(epoch));
573662
get_matured_reward(self, &tenure_id_bhh, epoch).map(|x| x.total().into())
574663
}
664+
665+
fn get_stacks_height_for_tenure_height(
666+
&self,
667+
tip: &StacksBlockId,
668+
tenure_height: u32,
669+
) -> Option<u32> {
670+
let tenure_block_id =
671+
GetTenureStartId::get_tenure_block_id_at_cb_height(self, tip, tenure_height.into())
672+
.expect("FATAL: bad DB data for tenure height lookups")?;
673+
get_stacks_header_column(
674+
self.sqlite_conn(),
675+
&tenure_block_id.0,
676+
"block_height",
677+
|r| {
678+
u64::from_row(r)
679+
.expect("FATAL: malformed block_height")
680+
.try_into()
681+
.expect("FATAL: blockchain too long")
682+
},
683+
)
684+
}
575685
}
576686

577687
/// Select a specific column from the headers table, specifying whether to use

0 commit comments

Comments
 (0)