mirror of
https://github.com/alexgo-io/stacks-puppet-node.git
synced 2026-01-12 16:53:21 +08:00
feat: add tenure height to block_headers table
Handle the `tenure-height` keyword in Clarity3, and using the same logic for `block-height` in Clarity 1 or 2.
This commit is contained in:
@@ -98,6 +98,7 @@ pub trait HeadersDB {
|
||||
fn get_burnchain_tokens_spent_for_block(&self, id_bhh: &StacksBlockId) -> Option<u128>;
|
||||
fn get_burnchain_tokens_spent_for_winning_block(&self, id_bhh: &StacksBlockId) -> Option<u128>;
|
||||
fn get_tokens_earned_for_block(&self, id_bhh: &StacksBlockId) -> Option<u128>;
|
||||
fn get_tenure_height_for_block(&self, id_bhh: &StacksBlockId) -> Option<u32>;
|
||||
}
|
||||
|
||||
pub trait BurnStateDB {
|
||||
@@ -183,6 +184,9 @@ impl HeadersDB for &dyn HeadersDB {
|
||||
fn get_tokens_earned_for_block(&self, id_bhh: &StacksBlockId) -> Option<u128> {
|
||||
(*self).get_tokens_earned_for_block(id_bhh)
|
||||
}
|
||||
fn get_tenure_height_for_block(&self, id_bhh: &StacksBlockId) -> Option<u32> {
|
||||
(*self).get_tenure_height_for_block(id_bhh)
|
||||
}
|
||||
}
|
||||
|
||||
impl BurnStateDB for &dyn BurnStateDB {
|
||||
@@ -334,6 +338,9 @@ impl HeadersDB for NullHeadersDB {
|
||||
fn get_tokens_earned_for_block(&self, _id_bhh: &StacksBlockId) -> Option<u128> {
|
||||
None
|
||||
}
|
||||
fn get_tenure_height_for_block(&self, _id_bhh: &StacksBlockId) -> Option<u32> {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::panic)]
|
||||
@@ -1142,6 +1149,18 @@ impl<'a> ClarityDatabase<'a> {
|
||||
pub fn set_stx_btc_ops_processed(&mut self, processed: u64) -> Result<()> {
|
||||
self.put_data("vm_pox::stx_btc_ops::processed_blocks", &processed)
|
||||
}
|
||||
|
||||
pub fn get_current_tenure_height(&mut self) -> Result<u32> {
|
||||
let cur_stacks_height = self.store.get_current_block_height();
|
||||
let last_mined_bhh = match self.get_index_block_header_hash(cur_stacks_height) {
|
||||
Ok(x) => x,
|
||||
Err(_) => return Ok(1),
|
||||
};
|
||||
// FIXME: This only works on a previous block, but not for a block that is currently being built.
|
||||
self.headers_db
|
||||
.get_tenure_height_for_block(&last_mined_bhh)
|
||||
.ok_or_else(|| InterpreterError::Expect("Failed to get block data.".into()).into())
|
||||
}
|
||||
}
|
||||
|
||||
// poison-microblock
|
||||
|
||||
@@ -2760,6 +2760,10 @@ mod test {
|
||||
fn get_tokens_earned_for_block(&self, id_bhh: &StacksBlockId) -> Option<u128> {
|
||||
Some(12000)
|
||||
}
|
||||
|
||||
fn get_tenure_height_for_block(&self, id_bhh: &StacksBlockId) -> Option<u32> {
|
||||
Some(100)
|
||||
}
|
||||
}
|
||||
|
||||
struct DocBurnStateDB {}
|
||||
|
||||
@@ -196,6 +196,11 @@ 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_tenure_height_for_block(&self, id_bhh: &StacksBlockId) -> Option<u32> {
|
||||
// if the block is defined at all, then return a constant
|
||||
self.get_burn_block_height_for_block(id_bhh).map(|_| 100)
|
||||
}
|
||||
}
|
||||
|
||||
impl BurnStateDB for UnitTestBurnStateDB {
|
||||
|
||||
@@ -145,3 +145,61 @@ fn test_stacks_block_height(
|
||||
assert_eq!(Ok(Value::UInt(1)), eval_result);
|
||||
}
|
||||
}
|
||||
|
||||
#[apply(test_clarity_versions)]
|
||||
fn test_tenure_height(
|
||||
version: ClarityVersion,
|
||||
epoch: StacksEpochId,
|
||||
mut tl_env_factory: TopLevelMemoryEnvironmentGenerator,
|
||||
) {
|
||||
let contract = "(define-read-only (test-func) tenure-height)";
|
||||
|
||||
let mut placeholder_context =
|
||||
ContractContext::new(QualifiedContractIdentifier::transient(), version);
|
||||
|
||||
let mut owned_env = tl_env_factory.get_env(epoch);
|
||||
let contract_identifier = QualifiedContractIdentifier::local("test-contract").unwrap();
|
||||
|
||||
let mut exprs = parse(&contract_identifier, &contract, version, epoch).unwrap();
|
||||
let mut marf = MemoryBackingStore::new();
|
||||
let mut db = marf.as_analysis_db();
|
||||
let analysis = db.execute(|db| {
|
||||
type_check_version(&contract_identifier, &mut exprs, db, true, epoch, version)
|
||||
});
|
||||
if version < ClarityVersion::Clarity3 {
|
||||
let err = analysis.unwrap_err();
|
||||
assert_eq!(
|
||||
CheckErrors::UndefinedVariable("tenure-height".to_string()),
|
||||
err.err
|
||||
);
|
||||
} else {
|
||||
assert!(analysis.is_ok());
|
||||
}
|
||||
|
||||
// Initialize the contract
|
||||
// Note that we're ignoring the analysis failure here so that we can test
|
||||
// the runtime behavior. In Clarity 3, if this case somehow gets past the
|
||||
// analysis, it should fail at runtime.
|
||||
let result = owned_env.initialize_versioned_contract(
|
||||
contract_identifier.clone(),
|
||||
version,
|
||||
contract,
|
||||
None,
|
||||
ASTRules::PrecheckSize,
|
||||
);
|
||||
|
||||
let mut env = owned_env.get_exec_environment(None, None, &mut placeholder_context);
|
||||
|
||||
// Call the function
|
||||
let eval_result = env.eval_read_only(&contract_identifier, "(test-func)");
|
||||
// In Clarity 3, this should trigger a runtime error
|
||||
if version < ClarityVersion::Clarity3 {
|
||||
let err = eval_result.unwrap_err();
|
||||
assert_eq!(
|
||||
Error::Unchecked(CheckErrors::UndefinedVariable("tenure-height".to_string(),)),
|
||||
err
|
||||
);
|
||||
} else {
|
||||
assert_eq!(Ok(Value::UInt(1)), eval_result);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,6 +14,8 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use stacks_common::types::StacksEpochId;
|
||||
|
||||
use super::errors::InterpreterError;
|
||||
use crate::vm::contexts::{Environment, LocalContext};
|
||||
use crate::vm::costs::cost_functions::ClarityCostFunction;
|
||||
@@ -97,10 +99,19 @@ pub fn lookup_reserved_variable(
|
||||
Ok(Some(sponsor))
|
||||
}
|
||||
NativeVariables::BlockHeight => {
|
||||
// FIXME: this needs to be updated to epoch 3.0 vs epoch 2.x
|
||||
runtime_cost(ClarityCostFunction::FetchVar, env, 1)?;
|
||||
let block_height = env.global_context.database.get_current_block_height();
|
||||
Ok(Some(Value::UInt(block_height as u128)))
|
||||
// In epoch 2.x, the `block-height` keyword returns the block height, but
|
||||
// beginning in epoch 3, it returns the tenure height instead in order to
|
||||
// maintain a similar pace to the block height advancement pre-Nakamoto. This
|
||||
// keyword is removed in Clarity 3 to avoid confusion and replaced with
|
||||
// `stacks-block-height` and `tenure-height`.
|
||||
if env.global_context.epoch_id < StacksEpochId::Epoch30 {
|
||||
let block_height = env.global_context.database.get_current_block_height();
|
||||
Ok(Some(Value::UInt(block_height as u128)))
|
||||
} else {
|
||||
let tenure_height = env.global_context.database.get_current_tenure_height()?;
|
||||
Ok(Some(Value::UInt(tenure_height as u128)))
|
||||
}
|
||||
}
|
||||
NativeVariables::BurnBlockHeight => {
|
||||
runtime_cost(ClarityCostFunction::FetchVar, env, 1)?;
|
||||
@@ -137,12 +148,8 @@ pub fn lookup_reserved_variable(
|
||||
}
|
||||
NativeVariables::TenureHeight => {
|
||||
runtime_cost(ClarityCostFunction::FetchVar, env, 1)?;
|
||||
// FIXME: this is a placeholder and needs to be implemented correctly
|
||||
let burn_block_height = env
|
||||
.global_context
|
||||
.database
|
||||
.get_current_burnchain_block_height()?;
|
||||
Ok(Some(Value::UInt(u128::from(burn_block_height))))
|
||||
let tenure_height = env.global_context.database.get_current_tenure_height()?;
|
||||
Ok(Some(Value::UInt(tenure_height as u128)))
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
||||
@@ -216,6 +216,16 @@ lazy_static! {
|
||||
UPDATE db_config SET version = "4";
|
||||
"#.into(),
|
||||
];
|
||||
|
||||
pub static ref NAKAMOTO_CHAINSTATE_SCHEMA_2: Vec<String> = vec![
|
||||
r#"
|
||||
-- Add a `tenure_height` column to the block_headers table.
|
||||
ALTER TABLE block_headers ADD COLUMN tenure_height INTEGER;
|
||||
"#.into(),
|
||||
r#"
|
||||
UPDATE db_config SET version = "5";
|
||||
"#.into(),
|
||||
];
|
||||
}
|
||||
|
||||
/// Matured miner reward schedules
|
||||
@@ -2342,6 +2352,22 @@ impl NakamotoChainState {
|
||||
let new_block_hash = new_tip.block_hash();
|
||||
let index_block_hash = new_tip.block_id();
|
||||
|
||||
let parent_header_info =
|
||||
NakamotoChainState::get_block_header(headers_tx.deref(), &new_tip.parent_block_id)
|
||||
.and_then(|x| x.ok_or(ChainstateError::NoSuchBlockError))?;
|
||||
let tenure_height = if let Some(th) = parent_header_info.tenure_height {
|
||||
if new_tenure {
|
||||
th + 1
|
||||
} else {
|
||||
th
|
||||
}
|
||||
} else {
|
||||
// This can only be the case if the parent is an epoch 2.x block that was stored
|
||||
// before the tenure height was added to the header info. In that case, the tenure
|
||||
// height is the parent's block height + 1.
|
||||
parent_header_info.stacks_block_height + 1
|
||||
};
|
||||
|
||||
// store each indexed field
|
||||
test_debug!("Headers index_put_begin {parent_hash}-{index_block_hash}");
|
||||
let root_hash =
|
||||
@@ -2353,6 +2379,7 @@ impl NakamotoChainState {
|
||||
microblock_tail: None,
|
||||
index_root: root_hash,
|
||||
stacks_block_height: new_tip.chain_length,
|
||||
tenure_height: Some(tenure_height),
|
||||
consensus_hash: new_tip.consensus_hash.clone(),
|
||||
burn_header_hash: new_burn_header_hash.clone(),
|
||||
burn_header_height: new_burnchain_height,
|
||||
|
||||
@@ -668,6 +668,7 @@ pub fn test_load_store_update_nakamoto_blocks() {
|
||||
anchored_header: StacksBlockHeaderTypes::Epoch2(epoch2_header.clone()),
|
||||
microblock_tail: None,
|
||||
stacks_block_height: epoch2_header.total_work.work,
|
||||
tenure_height: None,
|
||||
index_root: TrieHash([0x56; 32]),
|
||||
consensus_hash: epoch2_consensus_hash.clone(),
|
||||
burn_header_hash: BurnchainHeaderHash([0x77; 32]),
|
||||
@@ -768,6 +769,7 @@ pub fn test_load_store_update_nakamoto_blocks() {
|
||||
anchored_header: StacksBlockHeaderTypes::Nakamoto(nakamoto_header.clone()),
|
||||
microblock_tail: None,
|
||||
stacks_block_height: nakamoto_header.chain_length,
|
||||
tenure_height: Some(nakamoto_header.chain_length),
|
||||
index_root: TrieHash([0x67; 32]),
|
||||
consensus_hash: nakamoto_header.consensus_hash.clone(),
|
||||
burn_header_hash: BurnchainHeaderHash([0x88; 32]),
|
||||
@@ -812,6 +814,7 @@ pub fn test_load_store_update_nakamoto_blocks() {
|
||||
anchored_header: StacksBlockHeaderTypes::Nakamoto(nakamoto_header_2.clone()),
|
||||
microblock_tail: None,
|
||||
stacks_block_height: nakamoto_header_2.chain_length,
|
||||
tenure_height: Some(nakamoto_header.chain_length), // same as the first block in tenure
|
||||
index_root: TrieHash([0x67; 32]),
|
||||
consensus_hash: nakamoto_header_2.consensus_hash.clone(),
|
||||
burn_header_hash: BurnchainHeaderHash([0x88; 32]),
|
||||
@@ -851,6 +854,7 @@ pub fn test_load_store_update_nakamoto_blocks() {
|
||||
anchored_header: StacksBlockHeaderTypes::Nakamoto(nakamoto_header_3.clone()),
|
||||
microblock_tail: None,
|
||||
stacks_block_height: nakamoto_header_2.chain_length,
|
||||
tenure_height: Some(nakamoto_header.chain_length),
|
||||
index_root: TrieHash([0x67; 32]),
|
||||
consensus_hash: nakamoto_header_2.consensus_hash.clone(),
|
||||
burn_header_hash: BurnchainHeaderHash([0x88; 32]),
|
||||
|
||||
@@ -559,6 +559,20 @@ impl HeadersDB for TestSimHeadersDB {
|
||||
// if the block is defined at all, then return a constant
|
||||
self.get_burn_block_height_for_block(id_bhh).map(|_| 3000)
|
||||
}
|
||||
|
||||
fn get_tenure_height_for_block(&self, id_bhh: &StacksBlockId) -> Option<u32> {
|
||||
if *id_bhh == *FIRST_INDEX_BLOCK_HASH {
|
||||
Some(0)
|
||||
} else {
|
||||
let input_height = test_sim_hash_to_height(&id_bhh.0)?;
|
||||
if input_height > self.height {
|
||||
eprintln!("{} > {}", input_height, self.height);
|
||||
None
|
||||
} else {
|
||||
Some(input_height as u32)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -125,6 +125,11 @@ impl StacksChainState {
|
||||
let consensus_hash = &tip_info.consensus_hash;
|
||||
let burn_header_hash = &tip_info.burn_header_hash;
|
||||
let block_height = tip_info.stacks_block_height;
|
||||
let tenure_height = if let Some(th) = tip_info.tenure_height {
|
||||
th
|
||||
} else {
|
||||
tip_info.stacks_block_height
|
||||
};
|
||||
let burn_header_height = tip_info.burn_header_height;
|
||||
let burn_header_timestamp = tip_info.burn_header_timestamp;
|
||||
|
||||
@@ -162,6 +167,7 @@ impl StacksChainState {
|
||||
&block_size_str,
|
||||
parent_id,
|
||||
&u64_to_sql(affirmation_weight)?,
|
||||
&u64_to_sql(tenure_height)?,
|
||||
];
|
||||
|
||||
tx.execute("INSERT INTO block_headers \
|
||||
@@ -186,8 +192,9 @@ impl StacksChainState {
|
||||
cost,
|
||||
block_size,
|
||||
parent_block_id,
|
||||
affirmation_weight) \
|
||||
VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10, ?11, ?12, ?13, ?14, ?15, ?16, ?17, ?18, ?19, ?20, ?21, ?22)", args)
|
||||
affirmation_weight,
|
||||
tenure_height) \
|
||||
VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10, ?11, ?12, ?13, ?14, ?15, ?16, ?17, ?18, ?19, ?20, ?21, ?22, ?23)", args)
|
||||
.map_err(|e| Error::DBError(db_error::SqliteError(e)))?;
|
||||
|
||||
Ok(())
|
||||
|
||||
@@ -53,7 +53,7 @@ use crate::chainstate::burn::operations::{
|
||||
use crate::chainstate::burn::{ConsensusHash, ConsensusHashExtensions};
|
||||
use crate::chainstate::nakamoto::{
|
||||
HeaderTypeNames, NakamotoBlock, NakamotoBlockHeader, NakamotoChainState,
|
||||
NakamotoStagingBlocksConn, NAKAMOTO_CHAINSTATE_SCHEMA_1,
|
||||
NakamotoStagingBlocksConn, NAKAMOTO_CHAINSTATE_SCHEMA_1, NAKAMOTO_CHAINSTATE_SCHEMA_2,
|
||||
};
|
||||
use crate::chainstate::stacks::address::StacksAddressExtensions;
|
||||
use crate::chainstate::stacks::boot::*;
|
||||
@@ -184,6 +184,8 @@ pub struct StacksHeaderInfo {
|
||||
pub microblock_tail: Option<StacksMicroblockHeader>,
|
||||
/// Height of this Stacks block
|
||||
pub stacks_block_height: u64,
|
||||
/// Tenure height of this Stacks block
|
||||
pub tenure_height: Option<u64>,
|
||||
/// MARF root hash of the headers DB (not consensus critical)
|
||||
pub index_root: TrieHash,
|
||||
/// consensus hash of the burnchain block in which this miner was selected to produce this block
|
||||
@@ -365,6 +367,7 @@ impl StacksHeaderInfo {
|
||||
anchored_header: StacksBlockHeader::genesis_block_header().into(),
|
||||
microblock_tail: None,
|
||||
stacks_block_height: 0,
|
||||
tenure_height: Some(0),
|
||||
index_root: TrieHash([0u8; 32]),
|
||||
burn_header_hash: burnchain_params.first_block_hash.clone(),
|
||||
burn_header_height: burnchain_params.first_block_height as u32,
|
||||
@@ -384,6 +387,7 @@ impl StacksHeaderInfo {
|
||||
anchored_header: StacksBlockHeader::genesis_block_header().into(),
|
||||
microblock_tail: None,
|
||||
stacks_block_height: 0,
|
||||
tenure_height: Some(0),
|
||||
index_root: root_hash,
|
||||
burn_header_hash: first_burnchain_block_hash.clone(),
|
||||
burn_header_height: first_burnchain_block_height,
|
||||
@@ -426,6 +430,7 @@ impl FromRow<DBConfig> for DBConfig {
|
||||
impl FromRow<StacksHeaderInfo> for StacksHeaderInfo {
|
||||
fn from_row<'a>(row: &'a Row) -> Result<StacksHeaderInfo, db_error> {
|
||||
let block_height: u64 = u64::from_column(row, "block_height")?;
|
||||
let tenure_height: Option<u64> = u64::from_column(row, "tenure_height")?;
|
||||
let index_root = TrieHash::from_column(row, "index_root")?;
|
||||
let consensus_hash = ConsensusHash::from_column(row, "consensus_hash")?;
|
||||
let burn_header_hash = BurnchainHeaderHash::from_column(row, "burn_header_hash")?;
|
||||
@@ -454,6 +459,7 @@ impl FromRow<StacksHeaderInfo> for StacksHeaderInfo {
|
||||
anchored_header: stacks_header,
|
||||
microblock_tail: None,
|
||||
stacks_block_height: block_height,
|
||||
tenure_height,
|
||||
index_root,
|
||||
consensus_hash,
|
||||
burn_header_hash,
|
||||
@@ -1079,6 +1085,13 @@ impl StacksChainState {
|
||||
tx.execute_batch(cmd)?;
|
||||
}
|
||||
}
|
||||
"4" => {
|
||||
// migrate to clarity 3
|
||||
info!("Migrating chainstate schema from version 4 to 5: Clarity 3 support");
|
||||
for cmd in NAKAMOTO_CHAINSTATE_SCHEMA_2.iter() {
|
||||
tx.execute_batch(cmd)?;
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
error!(
|
||||
"Invalid chain state database: expected version = {}, got {}",
|
||||
@@ -2584,11 +2597,26 @@ impl StacksChainState {
|
||||
&index_block_hash,
|
||||
);
|
||||
|
||||
let parent_header_info = StacksChainState::get_stacks_block_header_info_by_consensus_hash(
|
||||
headers_tx.deref(),
|
||||
parent_consensus_hash,
|
||||
)
|
||||
.and_then(|x| x.ok_or(Error::NoSuchBlockError))?;
|
||||
let tenure_height = if let Some(th) = parent_header_info.tenure_height {
|
||||
th + 1
|
||||
} else {
|
||||
// This can only be the case if the parent is an epoch 2.x block that was stored
|
||||
// before the tenure height was added to the header info. In that case, the tenure
|
||||
// height is the parent's block height + 1.
|
||||
parent_header_info.stacks_block_height + 1
|
||||
};
|
||||
|
||||
let new_tip_info = StacksHeaderInfo {
|
||||
anchored_header: new_tip.clone().into(),
|
||||
microblock_tail: microblock_tail_opt,
|
||||
index_root: root_hash,
|
||||
stacks_block_height: new_tip.total_work.work,
|
||||
tenure_height: Some(tenure_height),
|
||||
consensus_hash: new_consensus_hash.clone(),
|
||||
burn_header_hash: new_burn_header_hash.clone(),
|
||||
burn_header_height: new_burnchain_height,
|
||||
|
||||
@@ -1487,6 +1487,7 @@ impl StacksBlockBuilder {
|
||||
anchored_header: StacksBlockHeader::genesis_block_header().into(),
|
||||
microblock_tail: None,
|
||||
stacks_block_height: 0,
|
||||
tenure_height: Some(0),
|
||||
index_root: TrieHash([0u8; 32]),
|
||||
consensus_hash: genesis_consensus_hash.clone(),
|
||||
burn_header_hash: genesis_burn_header_hash.clone(),
|
||||
|
||||
@@ -730,6 +730,11 @@ impl HeadersDB for CLIHeadersDB {
|
||||
// if the block is defined at all, then return a constant
|
||||
get_cli_block_height(&self.conn(), id_bhh).map(|_| 3000)
|
||||
}
|
||||
|
||||
fn get_tenure_height_for_block(&self, id_bhh: &StacksBlockId) -> Option<u32> {
|
||||
// if the block is defined at all, then return a constant
|
||||
get_cli_block_height(&self.conn(), id_bhh).map(|_| 100)
|
||||
}
|
||||
}
|
||||
|
||||
fn get_eval_input(invoked_by: &str, args: &[String]) -> EvalInput {
|
||||
|
||||
@@ -107,6 +107,15 @@ impl<'a> HeadersDB for HeadersDBConn<'a> {
|
||||
fn get_tokens_earned_for_block(&self, id_bhh: &StacksBlockId) -> Option<u128> {
|
||||
get_matured_reward(self.0, id_bhh).map(|x| x.total().into())
|
||||
}
|
||||
|
||||
fn get_tenure_height_for_block(&self, id_bhh: &StacksBlockId) -> Option<u32> {
|
||||
get_stacks_header_column(self.0, id_bhh, "tenure_height", |r| {
|
||||
u64::from_row(r)
|
||||
.expect("FATAL: malformed tenure_height")
|
||||
.try_into()
|
||||
.expect("FATAL: blockchain too long")
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> HeadersDB for ChainstateTx<'a> {
|
||||
@@ -184,6 +193,15 @@ impl<'a> HeadersDB for ChainstateTx<'a> {
|
||||
fn get_tokens_earned_for_block(&self, id_bhh: &StacksBlockId) -> Option<u128> {
|
||||
get_matured_reward(self.deref().deref(), id_bhh).map(|x| x.total().into())
|
||||
}
|
||||
|
||||
fn get_tenure_height_for_block(&self, id_bhh: &StacksBlockId) -> Option<u32> {
|
||||
get_stacks_header_column(self.deref().deref(), id_bhh, "tenure_height", |r| {
|
||||
u64::from_row(r)
|
||||
.expect("FATAL: malformed tenure_height")
|
||||
.try_into()
|
||||
.expect("FATAL: blockchain too long")
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl HeadersDB for MARF<StacksBlockId> {
|
||||
@@ -261,6 +279,15 @@ impl HeadersDB for MARF<StacksBlockId> {
|
||||
fn get_tokens_earned_for_block(&self, id_bhh: &StacksBlockId) -> Option<u128> {
|
||||
get_matured_reward(self.sqlite_conn(), id_bhh).map(|x| x.total().into())
|
||||
}
|
||||
|
||||
fn get_tenure_height_for_block(&self, id_bhh: &StacksBlockId) -> Option<u32> {
|
||||
get_stacks_header_column(self.sqlite_conn(), id_bhh, "tenure_height", |r| {
|
||||
u64::from_row(r)
|
||||
.expect("FATAL: malformed tenure_height")
|
||||
.try_into()
|
||||
.expect("FATAL: blockchain too long")
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
fn get_stacks_header_column<F, R>(
|
||||
|
||||
@@ -122,6 +122,7 @@ pub fn make_block(
|
||||
microblock_tail: None,
|
||||
index_root: TrieHash::from_empty_data(),
|
||||
stacks_block_height: block_height,
|
||||
tenure_height: Some(block_height),
|
||||
consensus_hash: block_consensus.clone(),
|
||||
burn_header_hash: BurnchainHeaderHash([0; 32]),
|
||||
burn_header_height: burn_height as u32,
|
||||
|
||||
Reference in New Issue
Block a user