mirror of
https://github.com/alexgo-io/stacks-puppet-node.git
synced 2026-01-12 22:43:42 +08:00
feat: block-height -> stacks-block-height
`block-height` can no longer be used in Clarity 3 and above and will cause an analysis error. In its place is `stacks-block-height`.
This commit is contained in:
@@ -148,7 +148,7 @@ impl<'a> ArithmeticOnlyChecker<'a> {
|
||||
{
|
||||
match native_var {
|
||||
ContractCaller | TxSender | TotalLiquidMicroSTX | BlockHeight | BurnBlockHeight
|
||||
| Regtest | TxSponsor | Mainnet | ChainId => {
|
||||
| Regtest | TxSponsor | Mainnet | ChainId | StacksBlockHeight | TenureHeight => {
|
||||
Err(Error::VariableForbidden(native_var))
|
||||
}
|
||||
NativeNone | NativeTrue | NativeFalse => Ok(()),
|
||||
|
||||
@@ -323,9 +323,9 @@ fn type_reserved_variable(variable_name: &str) -> CheckResult<Option<TypeSignatu
|
||||
NativeFalse => TypeSignature::BoolType,
|
||||
TotalLiquidMicroSTX => TypeSignature::UIntType,
|
||||
Regtest => TypeSignature::BoolType,
|
||||
TxSponsor | Mainnet | ChainId => {
|
||||
TxSponsor | Mainnet | ChainId | StacksBlockHeight | TenureHeight => {
|
||||
return Err(CheckErrors::Expects(
|
||||
"tx-sponsor, mainnet, and chain-id should not reach here in 2.05".into(),
|
||||
"tx-sponsor, mainnet, chain-id, stacks-block-height, and tenure-height should not reach here in 2.05".into(),
|
||||
)
|
||||
.into())
|
||||
}
|
||||
|
||||
@@ -42,7 +42,7 @@ impl TraitContext {
|
||||
pub fn new(clarity_version: ClarityVersion) -> TraitContext {
|
||||
match clarity_version {
|
||||
ClarityVersion::Clarity1 => Self::Clarity1(HashMap::new()),
|
||||
ClarityVersion::Clarity2 => Self::Clarity2 {
|
||||
ClarityVersion::Clarity2 | ClarityVersion::Clarity3 => Self::Clarity2 {
|
||||
defined: HashSet::new(),
|
||||
all: HashMap::new(),
|
||||
},
|
||||
|
||||
@@ -858,6 +858,8 @@ fn type_reserved_variable(
|
||||
.map_err(|_| CheckErrors::Expects("Bad construction".into()))?,
|
||||
ContractCaller => TypeSignature::PrincipalType,
|
||||
BlockHeight => TypeSignature::UIntType,
|
||||
StacksBlockHeight => TypeSignature::UIntType,
|
||||
TenureHeight => TypeSignature::UIntType,
|
||||
BurnBlockHeight => TypeSignature::UIntType,
|
||||
NativeNone => TypeSignature::new_option(no_type())
|
||||
.map_err(|_| CheckErrors::Expects("Bad construction".into()))?,
|
||||
|
||||
@@ -2740,7 +2740,9 @@ fn clarity_trait_experiments_downcast_literal_2(
|
||||
})
|
||||
.unwrap_err();
|
||||
match version {
|
||||
ClarityVersion::Clarity2 => assert!(err.starts_with("ExpectedCallableType(PrincipalType)")),
|
||||
ClarityVersion::Clarity2 | ClarityVersion::Clarity3 => {
|
||||
assert!(err.starts_with("ExpectedCallableType(PrincipalType)"))
|
||||
}
|
||||
ClarityVersion::Clarity1 => {
|
||||
assert!(err.starts_with("TraitReferenceUnknown(\"principal-value\")"))
|
||||
}
|
||||
@@ -2935,7 +2937,9 @@ fn clarity_trait_experiments_trait_cast_incompatible(
|
||||
assert!(err.starts_with("TypeError(CallableType(Trait(TraitIdentifier"))
|
||||
}
|
||||
}
|
||||
ClarityVersion::Clarity2 => assert!(err.starts_with("IncompatibleTrait")),
|
||||
ClarityVersion::Clarity2 | ClarityVersion::Clarity3 => {
|
||||
assert!(err.starts_with("IncompatibleTrait"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -126,18 +126,16 @@ to the same contract principal.",
|
||||
example: "(print contract-caller) ;; Will print out a Stacks address of the transaction sender",
|
||||
};
|
||||
|
||||
const STACKS_BLOCK_HEIGHT: KeywordAPI = KeywordAPI {
|
||||
const STACKS_BLOCK_HEIGHT_KEYWORD: SimpleKeywordAPI = SimpleKeywordAPI {
|
||||
name: "stacks-block-height",
|
||||
snippet: "stacks-block-height",
|
||||
output_type: "uint",
|
||||
description: "Returns the current block height of the Stacks blockchain.",
|
||||
example:
|
||||
"(<= stacks-block-height u500000) ;; returns true if the current block-height has not passed 500,000 blocks.",
|
||||
min_version: ClarityVersion::Clarity3,
|
||||
max_version: None,
|
||||
};
|
||||
|
||||
const TENURE_HEIGHT_KEYWORD: KeywordAPI = KeywordAPI {
|
||||
const TENURE_HEIGHT_KEYWORD: SimpleKeywordAPI = SimpleKeywordAPI {
|
||||
name: "tenure-height",
|
||||
snippet: "tenure-height",
|
||||
output_type: "uint",
|
||||
@@ -145,8 +143,6 @@ const TENURE_HEIGHT_KEYWORD: KeywordAPI = KeywordAPI {
|
||||
At the start of epoch 3.0, `tenure-height` will return the same value as `block-height`, then it will continue to increase as each tenures passes.",
|
||||
example:
|
||||
"(< tenure-height u140000) ;; returns true if the current tenure-height has passed 140,000 blocks.",
|
||||
min_version: ClarityVersion::Clarity3,
|
||||
max_version: None,
|
||||
};
|
||||
|
||||
const TX_SENDER_KEYWORD: SimpleKeywordAPI = SimpleKeywordAPI {
|
||||
@@ -2568,13 +2564,15 @@ pub fn make_api_reference(function: &NativeFunctions) -> FunctionAPI {
|
||||
}
|
||||
|
||||
fn make_keyword_reference(variable: &NativeVariables) -> Option<KeywordAPI> {
|
||||
let simple_api = match variable {
|
||||
let keyword = match variable {
|
||||
NativeVariables::TxSender => TX_SENDER_KEYWORD.clone(),
|
||||
NativeVariables::ContractCaller => CONTRACT_CALLER_KEYWORD.clone(),
|
||||
NativeVariables::NativeNone => NONE_KEYWORD.clone(),
|
||||
NativeVariables::NativeTrue => TRUE_KEYWORD.clone(),
|
||||
NativeVariables::NativeFalse => FALSE_KEYWORD.clone(),
|
||||
NativeVariables::BlockHeight => BLOCK_HEIGHT.clone(),
|
||||
NativeVariables::StacksBlockHeight => STACKS_BLOCK_HEIGHT_KEYWORD.clone(),
|
||||
NativeVariables::TenureHeight => TENURE_HEIGHT_KEYWORD.clone(),
|
||||
NativeVariables::BurnBlockHeight => BURN_BLOCK_HEIGHT.clone(),
|
||||
NativeVariables::TotalLiquidMicroSTX => TOTAL_LIQUID_USTX_KEYWORD.clone(),
|
||||
NativeVariables::Regtest => REGTEST_KEYWORD.clone(),
|
||||
@@ -2583,11 +2581,11 @@ fn make_keyword_reference(variable: &NativeVariables) -> Option<KeywordAPI> {
|
||||
NativeVariables::TxSponsor => TX_SPONSOR_KEYWORD.clone(),
|
||||
};
|
||||
Some(KeywordAPI {
|
||||
name: simple_api.name,
|
||||
snippet: simple_api.snippet,
|
||||
output_type: simple_api.output_type,
|
||||
description: simple_api.description,
|
||||
example: simple_api.example,
|
||||
name: keyword.name,
|
||||
snippet: keyword.snippet,
|
||||
output_type: keyword.output_type,
|
||||
description: keyword.description,
|
||||
example: keyword.example,
|
||||
min_version: variable.get_min_version(),
|
||||
max_version: variable.get_max_version(),
|
||||
})
|
||||
|
||||
@@ -33,6 +33,7 @@ mod sequences;
|
||||
#[cfg(test)]
|
||||
mod simple_apply_eval;
|
||||
mod traits;
|
||||
mod variables;
|
||||
|
||||
macro_rules! epochs_template {
|
||||
($($epoch:ident,)*) => {
|
||||
@@ -50,7 +51,6 @@ macro_rules! epochs_template {
|
||||
match epoch {
|
||||
// don't test Epoch-1.0
|
||||
StacksEpochId::Epoch10 => (),
|
||||
StacksEpochId::Epoch30 => (),
|
||||
// this will lead to a compile time failure if an epoch is left out
|
||||
// of the epochs_template! macro list
|
||||
$(StacksEpochId::$epoch)|* => (),
|
||||
@@ -76,10 +76,16 @@ macro_rules! clarity_template {
|
||||
match (epoch, clarity) {
|
||||
// don't test Epoch-1.0
|
||||
(StacksEpochId::Epoch10, _) => (),
|
||||
(StacksEpochId::Epoch30, _) => (),
|
||||
// don't test these pairs, because they aren't supported:
|
||||
(StacksEpochId::Epoch20, ClarityVersion::Clarity2) => (),
|
||||
(StacksEpochId::Epoch2_05, ClarityVersion::Clarity2) => (),
|
||||
(StacksEpochId::Epoch20, ClarityVersion::Clarity3) => (),
|
||||
(StacksEpochId::Epoch2_05, ClarityVersion::Clarity3) => (),
|
||||
(StacksEpochId::Epoch21, ClarityVersion::Clarity3) => (),
|
||||
(StacksEpochId::Epoch22, ClarityVersion::Clarity3) => (),
|
||||
(StacksEpochId::Epoch23, ClarityVersion::Clarity3) => (),
|
||||
(StacksEpochId::Epoch24, ClarityVersion::Clarity3) => (),
|
||||
(StacksEpochId::Epoch25, ClarityVersion::Clarity3) => (),
|
||||
// this will lead to a compile time failure if a pair is left out
|
||||
// of the clarity_template! macro list
|
||||
$((StacksEpochId::$epoch, ClarityVersion::$clarity))|* => (),
|
||||
@@ -103,6 +109,7 @@ epochs_template! {
|
||||
Epoch23,
|
||||
Epoch24,
|
||||
Epoch25,
|
||||
Epoch30,
|
||||
}
|
||||
|
||||
clarity_template! {
|
||||
@@ -118,6 +125,9 @@ clarity_template! {
|
||||
(Epoch24, Clarity2),
|
||||
(Epoch25, Clarity1),
|
||||
(Epoch25, Clarity2),
|
||||
(Epoch30, Clarity1),
|
||||
(Epoch30, Clarity2),
|
||||
(Epoch30, Clarity3),
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
||||
147
clarity/src/vm/tests/variables.rs
Normal file
147
clarity/src/vm/tests/variables.rs
Normal file
@@ -0,0 +1,147 @@
|
||||
// Copyright (C) 2013-2020 Blockstack PBC, a public benefit corporation
|
||||
// Copyright (C) 2020-2024 Stacks Open Internet Foundation
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#[cfg(any(test, feature = "testing"))]
|
||||
use rstest::rstest;
|
||||
use stacks_common::types::StacksEpochId;
|
||||
|
||||
#[cfg(test)]
|
||||
use crate::vm::analysis::type_checker::v2_1::tests::contracts::type_check_version;
|
||||
use crate::vm::analysis::{run_analysis, CheckError};
|
||||
use crate::vm::ast::{parse, ASTRules};
|
||||
use crate::vm::database::MemoryBackingStore;
|
||||
use crate::vm::errors::{CheckErrors, Error};
|
||||
use crate::vm::tests::{test_clarity_versions, tl_env_factory, TopLevelMemoryEnvironmentGenerator};
|
||||
use crate::vm::types::{QualifiedContractIdentifier, Value};
|
||||
use crate::vm::{ClarityVersion, ContractContext};
|
||||
|
||||
#[apply(test_clarity_versions)]
|
||||
fn test_block_height(
|
||||
version: ClarityVersion,
|
||||
epoch: StacksEpochId,
|
||||
mut tl_env_factory: TopLevelMemoryEnvironmentGenerator,
|
||||
) {
|
||||
let contract = "(define-read-only (test-func) block-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("block-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("block-height".to_string(),)),
|
||||
err
|
||||
);
|
||||
} else {
|
||||
assert_eq!(Ok(Value::UInt(1)), eval_result);
|
||||
}
|
||||
}
|
||||
|
||||
#[apply(test_clarity_versions)]
|
||||
fn test_stacks_block_height(
|
||||
version: ClarityVersion,
|
||||
epoch: StacksEpochId,
|
||||
mut tl_env_factory: TopLevelMemoryEnvironmentGenerator,
|
||||
) {
|
||||
let contract = "(define-read-only (test-func) stacks-block-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("stacks-block-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(
|
||||
"stacks-block-height".to_string(),
|
||||
)),
|
||||
err
|
||||
);
|
||||
} else {
|
||||
assert_eq!(Ok(Value::UInt(1)), eval_result);
|
||||
}
|
||||
}
|
||||
@@ -97,6 +97,7 @@ 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)))
|
||||
@@ -136,6 +137,7 @@ 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
|
||||
|
||||
Reference in New Issue
Block a user