diff --git a/changelog.d/7342-cost-voting-functional-removal.removed b/changelog.d/7342-cost-voting-functional-removal.removed
new file mode 100644
index 00000000000..355bca21ffa
--- /dev/null
+++ b/changelog.d/7342-cost-voting-functional-removal.removed
@@ -0,0 +1 @@
+Removed the deprecated `.cost-voting` execution machinery following its Epoch 4.0 disablement by SIP-044.
diff --git a/clarity/src/vm/analysis/arithmetic_checker/mod.rs b/clarity/src/vm/analysis/arithmetic_checker/mod.rs
deleted file mode 100644
index 4f4a55c0960..00000000000
--- a/clarity/src/vm/analysis/arithmetic_checker/mod.rs
+++ /dev/null
@@ -1,337 +0,0 @@
-// Copyright (C) 2013-2020 Blocstack PBC, a public benefit corporation
-// Copyright (C) 2020 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 .
-
-//! Static-analysis pass that decides whether a contract is eligible to be
-//! installed as a *cost-function* contract under the SIP-006 cost-voting
-//! system.
-//!
-//! A cost-function contract supplies a Clarity function that the VM invokes
-//! on every call to a native operation in order to compute that operation's
-//! runtime/read/write cost. Because such functions run inside the cost
-//! accounting path itself, they must be:
-//!
-//! - **Deterministic**: no chain-state reads (`block-height`, `tx-sender`,
-//! `contract-caller`, `chain-id`, ...), no `contract-call?`.
-//! - **Side-effect free**: no map/var/FT/NFT defines or mutations, no STX
-//! transfers, no asset mints or burns, no `print`.
-//! - **Bounded**: no `map`/`fold`/`filter`/list-cons or other iterating
-//! constructs whose work scales with input size — the cost function must
-//! itself be cheap and predictable.
-//! - **Trait-free**: traits introduce dynamic dispatch that the cost
-//! accountant cannot reason about statically.
-//! - **Cheap**: only a restricted, predictable, constant-time subset of
-//! native forms is allowed, including arithmetic/logic and certain simple
-//! expression-building constructs.
-//!
-//! The pass walks every top-level form and expression and rejects anything
-//! outside the set of allowed constructs. The result is stored in
-//! [`ContractAnalysis::is_cost_contract_eligible`]; the cost-voting
-//! machinery in `vm::costs` later refuses to adopt a proposal whose target
-//! contract is not eligible.
-
-use clarity_types::representations::ClarityName;
-
-pub use super::errors::{
- RuntimeCheckErrorKind, StaticCheckError, check_argument_count, check_arguments_at_least,
-};
-use crate::vm::ClarityVersion;
-use crate::vm::analysis::types::ContractAnalysis;
-use crate::vm::functions::NativeFunctions;
-use crate::vm::functions::define::{DefineFunctions, DefineFunctionsParsed};
-use crate::vm::representations::SymbolicExpression;
-use crate::vm::representations::SymbolicExpressionType::{
- Atom, AtomValue, Field, List, LiteralValue, TraitReference,
-};
-use crate::vm::variables::NativeVariables;
-
-#[cfg(test)]
-mod tests;
-
-///
-/// A static-analysis pass that checks whether or not
-/// a proposed cost-function defining contract is allowable.
-/// Cost-function defining contracts may not use contract-call,
-/// any database operations, traits, or iterating operations (e.g., list
-/// operations)
-///
-pub struct ArithmeticOnlyChecker<'a> {
- clarity_version: &'a ClarityVersion,
-}
-
-#[derive(Debug, PartialEq, Eq, Clone)]
-pub enum Error {
- DefineTypeForbidden(DefineFunctions),
- VariableForbidden(NativeVariables),
- FunctionNotPermitted(NativeFunctions),
- TraitReferencesForbidden,
- UnexpectedContractStructure,
-}
-
-impl std::error::Error for Error {
- fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
- None
- }
-}
-
-impl std::fmt::Display for Error {
- fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
- write!(f, "{self:?}")
- }
-}
-
-impl ArithmeticOnlyChecker<'_> {
- pub fn check_contract_cost_eligible(contract_analysis: &mut ContractAnalysis) {
- let is_eligible = ArithmeticOnlyChecker::run(contract_analysis).is_ok();
- contract_analysis.is_cost_contract_eligible = is_eligible;
- }
-
- pub fn run(contract_analysis: &ContractAnalysis) -> Result<(), Error> {
- let checker = ArithmeticOnlyChecker {
- clarity_version: &contract_analysis.clarity_version,
- };
- for exp in contract_analysis.expressions.iter() {
- checker.check_top_levels(exp)?;
- }
-
- Ok(())
- }
-
- fn check_define_function(
- &self,
- _signature: &[SymbolicExpression],
- body: &SymbolicExpression,
- ) -> Result<(), Error> {
- self.check_expression(body)
- }
-
- fn check_top_levels(&self, expr: &SymbolicExpression) -> Result<(), Error> {
- use crate::vm::functions::define::DefineFunctionsParsed::*;
- if let Some(define_type) = DefineFunctionsParsed::try_parse(expr)
- .map_err(|_| Error::UnexpectedContractStructure)?
- {
- match define_type {
- // The _arguments_ to constant defines must be checked to ensure that
- // any _evaluated arguments_ supplied to them are valid.
- Constant { value, .. } => self.check_expression(value),
- PrivateFunction { signature, body } => self.check_define_function(signature, body),
- ReadOnlyFunction { signature, body } => self.check_define_function(signature, body),
- PersistedVariable { .. } => Err(Error::DefineTypeForbidden(
- DefineFunctions::PersistedVariable,
- )),
- BoundedFungibleToken { .. } => {
- Err(Error::DefineTypeForbidden(DefineFunctions::FungibleToken))
- }
- PublicFunction { .. } => {
- Err(Error::DefineTypeForbidden(DefineFunctions::PublicFunction))
- }
- Map { .. } => Err(Error::DefineTypeForbidden(DefineFunctions::Map)),
- NonFungibleToken { .. } => Err(Error::DefineTypeForbidden(
- DefineFunctions::NonFungibleToken,
- )),
- UnboundedFungibleToken { .. } => {
- Err(Error::DefineTypeForbidden(DefineFunctions::FungibleToken))
- }
- Trait { .. } => Err(Error::DefineTypeForbidden(DefineFunctions::Trait)),
- UseTrait { .. } => Err(Error::DefineTypeForbidden(DefineFunctions::UseTrait)),
- ImplTrait { .. } => Err(Error::DefineTypeForbidden(DefineFunctions::ImplTrait)),
- }
- } else {
- self.check_expression(expr)
- }
- }
-
- fn check_expression(&self, expr: &SymbolicExpression) -> Result<(), Error> {
- match expr.expr {
- AtomValue(_) | LiteralValue(_) => {
- // values and literals are always allowed
- Ok(())
- }
- Atom(ref variable) => self.check_variables_allowed(variable),
- Field(_) | TraitReference(_, _) => Err(Error::TraitReferencesForbidden),
- List(ref expression) => self.check_function_application(expression),
- }
- }
-
- fn check_variables_allowed(&self, var_name: &ClarityName) -> Result<(), Error> {
- use crate::vm::variables::NativeVariables::*;
- if let Some(native_var) =
- NativeVariables::lookup_by_name_at_version(var_name, self.clarity_version)
- {
- match native_var {
- ContractCaller | TxSender | TotalLiquidMicroSTX | BlockHeight | BurnBlockHeight
- | Regtest | TxSponsor | Mainnet | ChainId | StacksBlockHeight | TenureHeight
- | StacksBlockTime | CurrentContract => Err(Error::VariableForbidden(native_var)),
- NativeNone | NativeTrue | NativeFalse => Ok(()),
- }
- } else {
- Ok(())
- }
- }
-
- fn try_native_function_check(
- &self,
- function: &str,
- args: &[SymbolicExpression],
- ) -> Option> {
- NativeFunctions::lookup_by_name_at_version(function, self.clarity_version)
- .map(|function| self.check_native_function(function, args))
- }
-
- fn check_native_function(
- &self,
- function: NativeFunctions,
- args: &[SymbolicExpression],
- ) -> Result<(), Error> {
- use crate::vm::functions::NativeFunctions::*;
- match function {
- FetchVar | GetBlockInfo | GetBurnBlockInfo | GetStacksBlockInfo | GetTenureInfo
- | GetTokenBalance | GetAssetOwner | FetchEntry | SetEntry | DeleteEntry
- | InsertEntry | SetVar | MintAsset | MintToken | TransferAsset | TransferToken
- | ContractCall | StxTransfer | StxTransferMemo | StxBurn | AtBlock | GetStxBalance
- | GetTokenSupply | BurnToken | FromConsensusBuff | ToConsensusBuff | BurnAsset
- | StxGetAccount => Err(Error::FunctionNotPermitted(function)),
- Append
- | Concat
- | AsMaxLen
- | ContractOf
- | PrincipalOf
- | ListCons
- | Print
- | AsContract
- | ElementAt
- | ElementAtAlias
- | IndexOf
- | IndexOfAlias
- | Map
- | Filter
- | Fold
- | Slice
- | ReplaceAt
- | ContractHash
- | RestrictAssets
- | AsContractSafe
- | AllowanceWithStx
- | AllowanceWithFt
- | AllowanceWithNft
- | AllowanceWithStacking
- | AllowanceWithStaking
- | AllowanceWithPox
- | AllowanceAll => Err(Error::FunctionNotPermitted(function)),
- BuffToIntLe | BuffToUIntLe | BuffToIntBe | BuffToUIntBe => {
- Err(Error::FunctionNotPermitted(function))
- }
- IsStandard | PrincipalDestruct | PrincipalConstruct => {
- Err(Error::FunctionNotPermitted(function))
- }
- IntToAscii | IntToUtf8 | StringToInt | StringToUInt | ToAscii => {
- Err(Error::FunctionNotPermitted(function))
- }
- Sha512 | Sha512Trunc256 | Secp256k1Recover | Secp256k1Verify | Secp256r1Verify
- | Ed25519Verify | Secp256k1Decompress | Hash160 | Sha256 | Keccak256
- | VerifyMerkleProof | GetBitcoinTxOutput => Err(Error::FunctionNotPermitted(function)),
- Add | Subtract | Divide | Multiply | CmpGeq | CmpLeq | CmpLess | CmpGreater
- | Modulo | Power | Sqrti | Log2 | BitwiseXor | And | Or | Not | Equals | If
- | ConsSome | ConsOkay | ConsError | DefaultTo | UnwrapRet | UnwrapErrRet | IsOkay
- | IsNone | Asserts | Unwrap | UnwrapErr | IsErr | IsSome | TryRet | ToUInt | ToInt
- | Len | Begin | TupleMerge | BitwiseOr | BitwiseAnd | BitwiseXor2 | BitwiseNot
- | BitwiseLShift | BitwiseRShift => {
- // Check all arguments.
- self.check_all(args)
- }
- // we need to treat all the remaining functions specially, because these
- // do not eval all of their arguments (rather, one or more of their arguments
- // is a name)
- TupleGet => {
- // these functions use a name in the first argument
- check_argument_count(2, args).map_err(|_| Error::UnexpectedContractStructure)?;
- self.check_all(&args[1..])
- }
- Match => {
- if !(args.len() == 4 || args.len() == 5) {
- return Err(Error::UnexpectedContractStructure);
- }
- // check the match input
- self.check_expression(&args[0])?;
- // check the 'ok' branch
- self.check_expression(&args[2])?;
- // check the 'err' branch
- if args.len() == 4 {
- self.check_expression(&args[3])
- } else {
- self.check_expression(&args[4])
- }
- }
- Let => {
- check_arguments_at_least(2, args)
- .map_err(|_| Error::UnexpectedContractStructure)?;
-
- let binding_list = args[0]
- .match_list()
- .ok_or(Error::UnexpectedContractStructure)?;
-
- for pair in binding_list.iter() {
- let pair_expression = pair
- .match_list()
- .ok_or(Error::UnexpectedContractStructure)?;
- if pair_expression.len() != 2 {
- return Err(Error::UnexpectedContractStructure);
- }
-
- self.check_expression(&pair_expression[1])?;
- }
-
- self.check_all(&args[1..args.len()])
- }
- TupleCons => {
- for pair in args.iter() {
- let pair_expression = pair
- .match_list()
- .ok_or(Error::UnexpectedContractStructure)?;
- if pair_expression.len() != 2 {
- return Err(Error::UnexpectedContractStructure);
- }
-
- self.check_expression(&pair_expression[1])?;
- }
- Ok(())
- }
- }
- }
-
- fn check_all(&self, expressions: &[SymbolicExpression]) -> Result<(), Error> {
- for expr in expressions.iter() {
- self.check_expression(expr)?;
- }
- Ok(())
- }
-
- fn check_function_application(&self, expression: &[SymbolicExpression]) -> Result<(), Error> {
- let (function_name, args) = expression
- .split_first()
- .ok_or(Error::UnexpectedContractStructure)?;
-
- let function_name = function_name
- .match_atom()
- .ok_or(Error::UnexpectedContractStructure)?;
-
- if let Some(result) = self.try_native_function_check(function_name, args) {
- result
- } else {
- // non-native function invocations are always okay, just check the args!
- self.check_all(args)
- }
- }
-}
diff --git a/clarity/src/vm/analysis/arithmetic_checker/tests.rs b/clarity/src/vm/analysis/arithmetic_checker/tests.rs
deleted file mode 100644
index 0b0c33fe372..00000000000
--- a/clarity/src/vm/analysis/arithmetic_checker/tests.rs
+++ /dev/null
@@ -1,521 +0,0 @@
-// Copyright (C) 2013-2020 Blocstack PBC, a public benefit corporation
-// Copyright (C) 2020-2026 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 .
-
-#[cfg(test)]
-use rstest::rstest;
-#[cfg(test)]
-use rstest_reuse::{self, *};
-use stacks_common::types::StacksEpochId;
-
-use crate::vm::ClarityVersion;
-use crate::vm::analysis::ContractAnalysis;
-use crate::vm::analysis::arithmetic_checker::Error::*;
-use crate::vm::analysis::arithmetic_checker::{ArithmeticOnlyChecker, Error};
-use crate::vm::ast::parse;
-use crate::vm::costs::LimitedCostTracker;
-use crate::vm::functions::NativeFunctions;
-use crate::vm::functions::define::DefineFunctions;
-use crate::vm::tests::test_clarity_versions;
-use crate::vm::tooling::mem_type_check;
-use crate::vm::types::QualifiedContractIdentifier;
-use crate::vm::variables::NativeVariables;
-
-/// Checks whether or not a contract only contains arithmetic expressions (for example, defining a
-/// map would not pass this check).
-/// This check is useful in determining the validity of new potential cost functions.
-fn arithmetic_check(
- contract: &str,
- version: ClarityVersion,
- epoch: StacksEpochId,
-) -> Result<(), Error> {
- let contract_identifier = QualifiedContractIdentifier::transient();
- let expressions = parse(&contract_identifier, contract, version, epoch).unwrap();
-
- let analysis = ContractAnalysis::new(
- contract_identifier,
- expressions,
- LimitedCostTracker::new_free(),
- epoch,
- version,
- );
-
- ArithmeticOnlyChecker::run(&analysis)
-}
-
-fn check_good(contract: &str, version: ClarityVersion, epoch: StacksEpochId) {
- let analysis = mem_type_check(contract, version, epoch).unwrap().1;
- ArithmeticOnlyChecker::run(&analysis).expect("Should pass arithmetic checks");
-}
-
-#[apply(test_clarity_versions)]
-fn test_bad_defines(#[case] version: ClarityVersion, #[case] epoch: StacksEpochId) {
- let tests = [
- (
- "(define-public (foo) (ok 1))",
- DefineTypeForbidden(DefineFunctions::PublicFunction),
- ),
- (
- "(define-map foo-map ((a uint)) ((b uint))) (define-private (foo) (map-get? foo-map {a: u1}))",
- DefineTypeForbidden(DefineFunctions::Map),
- ),
- (
- "(define-data-var foo-var uint u1) (define-private (foo) (var-get foo-var))",
- DefineTypeForbidden(DefineFunctions::PersistedVariable),
- ),
- (
- "(define-fungible-token tokaroos u500)",
- DefineTypeForbidden(DefineFunctions::FungibleToken),
- ),
- (
- "(define-fungible-token tokaroos)",
- DefineTypeForbidden(DefineFunctions::FungibleToken),
- ),
- (
- "(define-non-fungible-token tokaroos uint)",
- DefineTypeForbidden(DefineFunctions::NonFungibleToken),
- ),
- (
- "(define-trait foo-trait ((foo (uint)) (response uint uint)))",
- DefineTypeForbidden(DefineFunctions::Trait),
- ),
- ];
-
- // Check bad defines for each clarity version
- for (contract, error) in tests.iter() {
- assert_eq!(
- arithmetic_check(contract, version, epoch),
- Err(error.clone()),
- "Check contract:\n {contract}"
- );
- }
-}
-
-#[test]
-fn test_variables_fail_arithmetic_check_clarity1() {
- // Tests the behavior using Clarity1.
- let tests = [
- (
- "(define-private (foo) burn-block-height)",
- Err(VariableForbidden(NativeVariables::BurnBlockHeight)),
- ),
- (
- "(define-private (foo) block-height)",
- Err(VariableForbidden(NativeVariables::BlockHeight)),
- ),
- (
- "(define-private (foo) tx-sender)",
- Err(VariableForbidden(NativeVariables::TxSender)),
- ),
- (
- "(define-private (foo) contract-caller)",
- Err(VariableForbidden(NativeVariables::ContractCaller)),
- ),
- (
- "(define-private (foo) is-in-regtest)",
- Err(VariableForbidden(NativeVariables::Regtest)),
- ),
- (
- "(define-private (foo) stx-liquid-supply)",
- Err(VariableForbidden(NativeVariables::TotalLiquidMicroSTX)),
- ),
- ("(define-private (foo) tx-sponsor?)", Ok(())),
- ("(define-private (foo) is-in-mainnet)", Ok(())),
- ("(define-private (foo) chain-id)", Ok(())),
- ];
-
- for (contract, result) in tests.iter() {
- assert_eq!(
- arithmetic_check(contract, ClarityVersion::Clarity1, StacksEpochId::Epoch2_05),
- result.clone(),
- "Check contract:\n {contract}"
- );
- assert_eq!(
- arithmetic_check(contract, ClarityVersion::Clarity1, StacksEpochId::Epoch21),
- result.clone(),
- "Check contract:\n {contract}"
- );
- }
-
- let tests = [
- "(define-private (foo) (begin true false none))",
- "(define-private (foo) 1)",
- ];
-
- for contract in tests.iter() {
- check_good(contract, ClarityVersion::Clarity1, StacksEpochId::Epoch2_05);
- check_good(contract, ClarityVersion::Clarity1, StacksEpochId::Epoch21);
- }
-}
-
-#[test]
-fn test_variables_fail_arithmetic_check_clarity2() {
- // Tests the behavior using Clarity2.
- let tests = [
- (
- "(define-private (foo) burn-block-height)",
- Err(VariableForbidden(NativeVariables::BurnBlockHeight)),
- ),
- (
- "(define-private (foo) block-height)",
- Err(VariableForbidden(NativeVariables::BlockHeight)),
- ),
- (
- "(define-private (foo) tx-sender)",
- Err(VariableForbidden(NativeVariables::TxSender)),
- ),
- (
- "(define-private (foo) contract-caller)",
- Err(VariableForbidden(NativeVariables::ContractCaller)),
- ),
- (
- "(define-private (foo) is-in-regtest)",
- Err(VariableForbidden(NativeVariables::Regtest)),
- ),
- (
- "(define-private (foo) stx-liquid-supply)",
- Err(VariableForbidden(NativeVariables::TotalLiquidMicroSTX)),
- ),
- (
- "(define-private (foo) tx-sponsor?)",
- Err(VariableForbidden(NativeVariables::TxSponsor)),
- ),
- (
- "(define-private (foo) is-in-mainnet)",
- Err(VariableForbidden(NativeVariables::Mainnet)),
- ),
- (
- "(define-private (foo) chain-id)",
- Err(VariableForbidden(NativeVariables::ChainId)),
- ),
- ];
-
- for (contract, result) in tests.iter() {
- assert_eq!(
- arithmetic_check(contract, ClarityVersion::Clarity2, StacksEpochId::Epoch21),
- result.clone(),
- "Check contract:\n {contract}"
- );
- }
-}
-
-#[test]
-fn test_functions_clarity1() {
- // Tests all functions against Clarity1 VM. Results should be different for Clarity1 vs Clarity2 functions.
- let tests = [
- // Clarity1 functions.
- ("(define-private (foo) (at-block 0x0202020202020202020202020202020202020202020202020202020202020202 (+ 1 2)))",
- Err(FunctionNotPermitted(NativeFunctions::AtBlock))),
- ("(define-private (foo) (map-get? foo-map {a: u1}))",
- Err(FunctionNotPermitted(NativeFunctions::FetchEntry))),
- ("(define-private (foo) (map-delete foo-map {a: u1}))",
- Err(FunctionNotPermitted(NativeFunctions::DeleteEntry))),
- ("(define-private (foo) (map-set foo-map {a: u1} {b: u2}))",
- Err(FunctionNotPermitted(NativeFunctions::SetEntry))),
- ("(define-private (foo) (map-insert foo-map {a: u1} {b: u2}))",
- Err(FunctionNotPermitted(NativeFunctions::InsertEntry))),
- ("(define-private (foo) (var-get foo-var))",
- Err(FunctionNotPermitted(NativeFunctions::FetchVar))),
- ("(define-private (foo) (var-set foo-var u2))",
- Err(FunctionNotPermitted(NativeFunctions::SetVar))),
- ("(define-private (foo (a principal)) (ft-get-balance tokaroos a))",
- Err(FunctionNotPermitted(NativeFunctions::GetTokenBalance))),
- ("(define-private (foo (a principal))
- (ft-transfer? stackaroo u50 'SZ2J6ZY48GV1EZ5V2V5RB9MP66SW86PYKKQ9H6DPR 'SPAXYA5XS51713FDTQ8H94EJ4V579CXMTRNBZKSF))",
- Err(FunctionNotPermitted(NativeFunctions::TransferToken))),
- ("(define-private (foo (a principal))
- (ft-mint? stackaroo u100 'SZ2J6ZY48GV1EZ5V2V5RB9MP66SW86PYKKQ9H6DPR))",
- Err(FunctionNotPermitted(NativeFunctions::MintToken))),
- ("(define-private (foo (a principal))
- (nft-mint? stackaroo \"Roo\" 'SZ2J6ZY48GV1EZ5V2V5RB9MP66SW86PYKKQ9H6DPR))",
- Err(FunctionNotPermitted(NativeFunctions::MintAsset))),
- ("(nft-transfer? stackaroo \"Roo\" 'SZ2J6ZY48GV1EZ5V2V5RB9MP66SW86PYKKQ9H6DPR 'SPAXYA5XS51713FDTQ8H94EJ4V579CXMTRNBZKSF)",
- Err(FunctionNotPermitted(NativeFunctions::TransferAsset))),
- ("(nft-get-owner? stackaroo \"Roo\")",
- Err(FunctionNotPermitted(NativeFunctions::GetAssetOwner))),
- ("(get-block-info? id-header-hash 0)",
- Err(FunctionNotPermitted(NativeFunctions::GetBlockInfo))),
- ("(define-private (foo) (contract-call? .bar outer-call))",
- Err(FunctionNotPermitted(NativeFunctions::ContractCall))),
- ("(stx-get-balance 'SPAXYA5XS51713FDTQ8H94EJ4V579CXMTRNBZKSF)",
- Err(FunctionNotPermitted(NativeFunctions::GetStxBalance))),
- ("(stx-burn? u100 'SPAXYA5XS51713FDTQ8H94EJ4V579CXMTRNBZKSF)",
- Err(FunctionNotPermitted(NativeFunctions::StxBurn))),
- (r#"(stx-transfer? u100 'SPAXYA5XS51713FDTQ8H94EJ4V579CXMTRNBZKSF 'SPAXYA5XS51713FDTQ8H94EJ4V579CXMTRNBZKSF)"#,
- Err(FunctionNotPermitted(NativeFunctions::StxTransfer))),
- ("(define-private (foo (a (list 3 uint)))
- (map log2 a))",
- Err(FunctionNotPermitted(NativeFunctions::Map))),
- ("(define-private (foo (a (list 3 (optional uint))))
- (filter is-none a))",
- Err(FunctionNotPermitted(NativeFunctions::Filter))),
- ("(define-private (foo (a (list 3 uint)))
- (append a u4))",
- Err(FunctionNotPermitted(NativeFunctions::Append))),
- ("(define-private (foo (a (list 3 uint)))
- (concat a a))",
- Err(FunctionNotPermitted(NativeFunctions::Concat))),
- ("(define-private (foo (a (list 3 uint)))
- (as-max-len? a u4))",
- Err(FunctionNotPermitted(NativeFunctions::AsMaxLen))),
- ("(define-private (foo) (print 10))",
- Err(FunctionNotPermitted(NativeFunctions::Print))),
- ("(define-private (foo) (list 3 4 10))",
- Err(FunctionNotPermitted(NativeFunctions::ListCons))),
- ("(define-private (foo) (keccak256 0))",
- Err(FunctionNotPermitted(NativeFunctions::Keccak256))),
- ("(define-private (foo) (hash160 0))",
- Err(FunctionNotPermitted(NativeFunctions::Hash160))),
- ("(define-private (foo) (secp256k1-recover? 0xde5b9eb9e7c5592930eb2e30a01369c36586d872082ed8181ee83d2a0ec20f04 0x8738487ebe69b93d8e51583be8eee50bb4213fc49c767d329632730cc193b873554428fc936ca3569afc15f1c9365f6591d6251a89fee9c9ac661116824d3a1301))",
- Err(FunctionNotPermitted(NativeFunctions::Secp256k1Recover))),
- ("(define-private (foo) (secp256k1-verify 0xde5b9eb9e7c5592930eb2e30a01369c36586d872082ed8181ee83d2a0ec20f04
- 0x8738487ebe69b93d8e51583be8eee50bb4213fc49c767d329632730cc193b873554428fc936ca3569afc15f1c9365f6591d6251a89fee9c9ac661116824d3a13
- 0x03adb8de4bfb65db2cfd6120d55c6526ae9c52e675db7e47308636534ba7786110))",
- Err(FunctionNotPermitted(NativeFunctions::Secp256k1Verify))),
- ("(define-private (foo) (sha256 0))",
- Err(FunctionNotPermitted(NativeFunctions::Sha256))),
- ("(define-private (foo) (sha512 0))",
- Err(FunctionNotPermitted(NativeFunctions::Sha512))),
- ("(define-private (foo) (sha512/256 0))",
- Err(FunctionNotPermitted(NativeFunctions::Sha512Trunc256))),
-
- // Clarity2 functions.
- (r#"(stx-transfer-memo? u100 'SPAXYA5XS51713FDTQ8H94EJ4V579CXMTRNBZKSF 'SPAXYA5XS51713FDTQ8H94EJ4V579CXMTRNBZKSF 0x010203)"#,
- Ok(())),
- ("(define-private (foo (a (list 3 uint)))
- (slice? a u2 u3))",
- Ok(())),
- ("(define-private (foo (a (list 3 uint)) (b uint))
- (replace-at? a u1 b))",
- Ok(())),
- ("(buff-to-int-le 0x0001)",
- Ok(())),
- ("(buff-to-uint-le 0x0001)",
- Ok(())),
- ("(buff-to-int-be 0x0001)",
- Ok(())),
- ("(buff-to-uint-be 0x0001)",
- Ok(())),
- ("(buff-to-uint-be 0x0001)",
- Ok(())),
- ("(is-standard 'STB44HYPYAT2BB2QE513NSP81HTMYWBJP02HPGK6)",
- Ok(())),
- ("(principal-destruct? 'STB44HYPYAT2BB2QE513NSP81HTMYWBJP02HPGK6)",
- Ok(())),
- ("(principal-construct? 0x22 0xfa6bf38ed557fe417333710d6033e9419391a320)",
- Ok(())),
- ("(string-to-int? \"-1\")",
- Ok(())),
- ("(string-to-uint? \"1\")",
- Ok(())),
- ("(int-to-ascii 5)",
- Ok(())),
- ("(int-to-utf8 10)",
- Ok(())),
- ("(get-burn-block-info? header-hash u0)",
- Ok(())),
- ("(stx-account 'SZ2J6ZY48GV1EZ5V2V5RB9MP66SW86PYKKQ9H6DPR)",
- Ok(())),
- ("(to-consensus-buff? 3)",
- Ok(())),
- ("(from-consensus-buff? true 0x03)",
- Ok(())),
- ];
-
- for (contract, result) in tests.iter() {
- assert_eq!(
- arithmetic_check(contract, ClarityVersion::Clarity1, StacksEpochId::Epoch2_05),
- result.clone(),
- "Check contract:\n {contract}"
- );
- assert_eq!(
- arithmetic_check(contract, ClarityVersion::Clarity1, StacksEpochId::Epoch21),
- result.clone(),
- "Check contract:\n {contract}"
- );
- }
-}
-
-#[test]
-fn test_functions_clarity2() {
- // Tests functions against Clarity2 VM. The Clarity1 functions should still cause an error.
- let tests = [
- // Clarity2 functions.
- (r#"(stx-transfer-memo? u100 'SPAXYA5XS51713FDTQ8H94EJ4V579CXMTRNBZKSF 'SPAXYA5XS51713FDTQ8H94EJ4V579CXMTRNBZKSF 0x010203)"#,
- Err(FunctionNotPermitted(NativeFunctions::StxTransferMemo))),
- ("(define-private (foo (a (list 3 uint)))
- (slice? a u2 u3))",
- Err(FunctionNotPermitted(NativeFunctions::Slice))),
- ("(define-private (foo (a (list 3 uint)))
- (replace-at? a u2 (list u3)))",
- Err(FunctionNotPermitted(NativeFunctions::ReplaceAt))),
- ("(buff-to-int-le 0x0001)",
- Err(FunctionNotPermitted(NativeFunctions::BuffToIntLe))),
- ("(buff-to-uint-le 0x0001)",
- Err(FunctionNotPermitted(NativeFunctions::BuffToUIntLe))),
- ("(buff-to-int-be 0x0001)",
- Err(FunctionNotPermitted(NativeFunctions::BuffToIntBe))),
- ("(buff-to-uint-be 0x0001)",
- Err(FunctionNotPermitted(NativeFunctions::BuffToUIntBe))),
- ("(is-standard 'STB44HYPYAT2BB2QE513NSP81HTMYWBJP02HPGK6)",
- Err(FunctionNotPermitted(NativeFunctions::IsStandard))),
- ("(principal-destruct? 'STB44HYPYAT2BB2QE513NSP81HTMYWBJP02HPGK6)",
- Err(FunctionNotPermitted(NativeFunctions::PrincipalDestruct))),
- ("(principal-construct? 0x22 0xfa6bf38ed557fe417333710d6033e9419391a320)",
- Err(FunctionNotPermitted(NativeFunctions::PrincipalConstruct))),
- ("(string-to-int? \"-1\")",
- Err(FunctionNotPermitted(NativeFunctions::StringToInt))),
- ("(string-to-uint? \"1\")",
- Err(FunctionNotPermitted(NativeFunctions::StringToUInt))),
- ("(int-to-ascii 5)",
- Err(FunctionNotPermitted(NativeFunctions::IntToAscii))),
- ("(int-to-utf8 10)",
- Err(FunctionNotPermitted(NativeFunctions::IntToUtf8))),
- ("(get-burn-block-info? header-hash u0)",
- Err(FunctionNotPermitted(NativeFunctions::GetBurnBlockInfo))),
- ("(stx-account 'SZ2J6ZY48GV1EZ5V2V5RB9MP66SW86PYKKQ9H6DPR)",
- Err(FunctionNotPermitted(NativeFunctions::StxGetAccount))),
- ("(to-consensus-buff? 3)",
- Err(FunctionNotPermitted(NativeFunctions::ToConsensusBuff))),
- ("(from-consensus-buff? true 0x03)",
- Err(FunctionNotPermitted(NativeFunctions::FromConsensusBuff))),
-
- // Clarity1 functions.
- ("(define-private (foo) (at-block 0x0202020202020202020202020202020202020202020202020202020202020202 (+ 1 2)))",
- Err(FunctionNotPermitted(NativeFunctions::AtBlock))),
- ("(define-private (foo) (map-get? foo-map {a: u1}))",
- Err(FunctionNotPermitted(NativeFunctions::FetchEntry))),
- ("(define-private (foo) (map-delete foo-map {a: u1}))",
- Err(FunctionNotPermitted(NativeFunctions::DeleteEntry))),
- ("(define-private (foo) (map-set foo-map {a: u1} {b: u2}))",
- Err(FunctionNotPermitted(NativeFunctions::SetEntry))),
- ("(define-private (foo) (map-insert foo-map {a: u1} {b: u2}))",
- Err(FunctionNotPermitted(NativeFunctions::InsertEntry))),
- ("(define-private (foo) (var-get foo-var))",
- Err(FunctionNotPermitted(NativeFunctions::FetchVar))),
- ("(define-private (foo) (var-set foo-var u2))",
- Err(FunctionNotPermitted(NativeFunctions::SetVar))),
- ("(define-private (foo (a principal)) (ft-get-balance tokaroos a))",
- Err(FunctionNotPermitted(NativeFunctions::GetTokenBalance))),
- ("(define-private (foo (a principal))
- (ft-transfer? stackaroo u50 'SZ2J6ZY48GV1EZ5V2V5RB9MP66SW86PYKKQ9H6DPR 'SPAXYA5XS51713FDTQ8H94EJ4V579CXMTRNBZKSF))",
- Err(FunctionNotPermitted(NativeFunctions::TransferToken))),
- ("(define-private (foo (a principal))
- (ft-mint? stackaroo u100 'SZ2J6ZY48GV1EZ5V2V5RB9MP66SW86PYKKQ9H6DPR))",
- Err(FunctionNotPermitted(NativeFunctions::MintToken))),
- ("(define-private (foo (a principal))
- (nft-mint? stackaroo \"Roo\" 'SZ2J6ZY48GV1EZ5V2V5RB9MP66SW86PYKKQ9H6DPR))",
- Err(FunctionNotPermitted(NativeFunctions::MintAsset))),
- ("(nft-transfer? stackaroo \"Roo\" 'SZ2J6ZY48GV1EZ5V2V5RB9MP66SW86PYKKQ9H6DPR 'SPAXYA5XS51713FDTQ8H94EJ4V579CXMTRNBZKSF)",
- Err(FunctionNotPermitted(NativeFunctions::TransferAsset))),
- ("(nft-get-owner? stackaroo \"Roo\")",
- Err(FunctionNotPermitted(NativeFunctions::GetAssetOwner))),
- ("(get-block-info? id-header-hash 0)",
- Err(FunctionNotPermitted(NativeFunctions::GetBlockInfo))),
- ("(define-private (foo) (contract-call? .bar outer-call))",
- Err(FunctionNotPermitted(NativeFunctions::ContractCall))),
- ("(stx-get-balance 'SPAXYA5XS51713FDTQ8H94EJ4V579CXMTRNBZKSF)",
- Err(FunctionNotPermitted(NativeFunctions::GetStxBalance))),
- ("(stx-burn? u100 'SPAXYA5XS51713FDTQ8H94EJ4V579CXMTRNBZKSF)",
- Err(FunctionNotPermitted(NativeFunctions::StxBurn))),
- (r#"(stx-transfer? u100 'SPAXYA5XS51713FDTQ8H94EJ4V579CXMTRNBZKSF 'SPAXYA5XS51713FDTQ8H94EJ4V579CXMTRNBZKSF)"#,
- Err(FunctionNotPermitted(NativeFunctions::StxTransfer))),
- ("(define-private (foo (a (list 3 uint)))
- (map log2 a))",
- Err(FunctionNotPermitted(NativeFunctions::Map))),
- ("(define-private (foo (a (list 3 (optional uint))))
- (filter is-none a))",
- Err(FunctionNotPermitted(NativeFunctions::Filter))),
- ("(define-private (foo (a (list 3 uint)))
- (append a u4))",
- Err(FunctionNotPermitted(NativeFunctions::Append))),
- ("(define-private (foo (a (list 3 uint)))
- (concat a a))",
- Err(FunctionNotPermitted(NativeFunctions::Concat))),
- ("(define-private (foo (a (list 3 uint)))
- (as-max-len? a u4))",
- Err(FunctionNotPermitted(NativeFunctions::AsMaxLen))),
- ("(define-private (foo) (print 10))",
- Err(FunctionNotPermitted(NativeFunctions::Print))),
- ("(define-private (foo) (list 3 4 10))",
- Err(FunctionNotPermitted(NativeFunctions::ListCons))),
- ("(define-private (foo) (keccak256 0))",
- Err(FunctionNotPermitted(NativeFunctions::Keccak256))),
- ("(define-private (foo) (hash160 0))",
- Err(FunctionNotPermitted(NativeFunctions::Hash160))),
- ("(define-private (foo) (secp256k1-recover? 0xde5b9eb9e7c5592930eb2e30a01369c36586d872082ed8181ee83d2a0ec20f04 0x8738487ebe69b93d8e51583be8eee50bb4213fc49c767d329632730cc193b873554428fc936ca3569afc15f1c9365f6591d6251a89fee9c9ac661116824d3a1301))",
- Err(FunctionNotPermitted(NativeFunctions::Secp256k1Recover))),
- ("(define-private (foo) (secp256k1-verify 0xde5b9eb9e7c5592930eb2e30a01369c36586d872082ed8181ee83d2a0ec20f04
- 0x8738487ebe69b93d8e51583be8eee50bb4213fc49c767d329632730cc193b873554428fc936ca3569afc15f1c9365f6591d6251a89fee9c9ac661116824d3a13
- 0x03adb8de4bfb65db2cfd6120d55c6526ae9c52e675db7e47308636534ba7786110))",
- Err(FunctionNotPermitted(NativeFunctions::Secp256k1Verify))),
- ("(define-private (foo) (sha256 0))",
- Err(FunctionNotPermitted(NativeFunctions::Sha256))),
- ("(define-private (foo) (sha512 0))",
- Err(FunctionNotPermitted(NativeFunctions::Sha512))),
- ("(define-private (foo) (sha512/256 0))",
- Err(FunctionNotPermitted(NativeFunctions::Sha512Trunc256))),
- ];
-
- for (contract, result) in tests.iter() {
- assert_eq!(
- arithmetic_check(contract, ClarityVersion::Clarity2, StacksEpochId::Epoch21),
- result.clone(),
- "Check contract:\n {contract}"
- );
- }
-}
-
-#[test]
-fn test_functions_contract() {
- let good_tests = [
- "(match (if (is-eq 0 1) (ok 1) (err 2))
- ok-val (+ 1 ok-val)
- err-val (+ 2 err-val))",
- "(match (if (is-eq 0 1) (some 1) none)
- ok-val (+ 1 ok-val)
- 2)",
- "(get a { a: (+ 9 1), b: (if (> 2 3) (* 4 4) (/ 4 2)) })",
- "(define-private (foo)
- (let ((a (+ 3 2 3))) (log2 a)))",
- "(define-private (foo)
- (let ((a (+ 32 3 4)) (b (- 32 1))
- (c (if (and (< 3 2) (<= 3 2) (>= 4 5) (or (is-eq (mod 5 4) 0) (not (> 3 2))))
- (pow u2 (log2 (sqrti u100000)))
- (xor u120 u280)))
- (d (default-to u0 (some u2))))
- (begin (unwrap! (some u3) u1)
- (unwrap-err! (err u5) u4)
- (asserts! true u4)
- (unwrap-panic (some u3))
- (unwrap-err-panic (err u5)))))",
- "(define-private (foo) (to-int (to-uint 34)))
- (define-private (bar) (foo))",
- "(define-private (foo) (begin
- (is-some (some 4))
- (is-none (some 4))
- (is-ok (ok 4))
- (is-err (ok 5))
- (try! (some 4))
- (some 5)))
- (define-read-only (bar) (foo))",
- ];
-
- for contract in good_tests.iter() {
- check_good(contract, ClarityVersion::Clarity1, StacksEpochId::Epoch2_05);
- check_good(contract, ClarityVersion::Clarity1, StacksEpochId::Epoch21);
- check_good(contract, ClarityVersion::Clarity2, StacksEpochId::Epoch21);
- }
-}
diff --git a/clarity/src/vm/analysis/contract_interface_builder/mod.rs b/clarity/src/vm/analysis/contract_interface_builder/mod.rs
index 89450c48864..3b1774571b0 100644
--- a/clarity/src/vm/analysis/contract_interface_builder/mod.rs
+++ b/clarity/src/vm/analysis/contract_interface_builder/mod.rs
@@ -50,7 +50,6 @@ pub fn build_contract_interface(
type_map: _,
cost_track: _,
contract_interface: _,
- is_cost_contract_eligible: _,
} = contract_analysis;
contract_interface
diff --git a/clarity/src/vm/analysis/mod.rs b/clarity/src/vm/analysis/mod.rs
index e01f3cc07cb..39529eb9c01 100644
--- a/clarity/src/vm/analysis/mod.rs
+++ b/clarity/src/vm/analysis/mod.rs
@@ -15,7 +15,6 @@
// along with this program. If not, see .
pub mod analysis_db;
-pub mod arithmetic_checker;
pub mod contract_interface_builder;
pub mod errors;
pub mod read_only_checker;
@@ -26,7 +25,6 @@ pub mod types;
use stacks_common::types::StacksEpochId;
pub use self::analysis_db::AnalysisDatabase;
-use self::arithmetic_checker::ArithmeticOnlyChecker;
use self::contract_interface_builder::build_contract_interface;
pub use self::errors::{
CommonCheckErrorKind, RuntimeCheckErrorKind, StaticCheckError, StaticCheckErrorKind,
@@ -135,8 +133,8 @@ pub fn type_check(
.map_err(|e| e.0)
}
-/// Run the full static-analysis pipeline (read-only, type, trait and arithmetic
-/// passes) over a parsed contract, optionally persisting the result.
+/// Run the full static-analysis pipeline (read-only, type, and trait passes)
+/// over a parsed contract, optionally persisting the result.
///
/// # Arguments
///
@@ -208,7 +206,6 @@ pub fn run_analysis(
ReadOnlyChecker::run_pass(&epoch, &mut contract_analysis, db, resource_limiter)?;
}
TraitChecker::run_pass(&epoch, &mut contract_analysis, db, resource_limiter)?;
- ArithmeticOnlyChecker::check_contract_cost_eligible(&mut contract_analysis);
// Final boundary check on the analysis passes
check_analysis_resource_limits(&resource_limiter)?;
diff --git a/clarity/src/vm/analysis/type_checker/v2_05/mod.rs b/clarity/src/vm/analysis/type_checker/v2_05/mod.rs
index c3f018c1fee..b7e46ffd7f9 100644
--- a/clarity/src/vm/analysis/type_checker/v2_05/mod.rs
+++ b/clarity/src/vm/analysis/type_checker/v2_05/mod.rs
@@ -45,8 +45,8 @@ use crate::vm::representations::SymbolicExpressionType::{
use crate::vm::representations::{ClarityName, SymbolicExpression, depth_traverse};
use crate::vm::types::signatures::{FunctionSignature, TypeSignatureExt as _};
use crate::vm::types::{
- FixedFunction, FunctionArg, FunctionType, PrincipalData, QualifiedContractIdentifier,
- TypeSignature, Value, parse_name_type_pairs,
+ FixedFunction, FunctionArg, FunctionType, PrincipalData, TypeSignature, Value,
+ parse_name_type_pairs,
};
use crate::vm::variables::NativeVariables;
@@ -98,15 +98,6 @@ impl CostTracker for TypeChecker<'_, '_> {
fn reset_memory(&mut self) {
self.cost_track.reset_memory()
}
- fn short_circuit_contract_call(
- &mut self,
- contract: &QualifiedContractIdentifier,
- function: &ClarityName,
- input: &[u64],
- ) -> std::result::Result {
- self.cost_track
- .short_circuit_contract_call(contract, function, input)
- }
}
impl TypeChecker<'_, '_> {
diff --git a/clarity/src/vm/analysis/type_checker/v2_1/mod.rs b/clarity/src/vm/analysis/type_checker/v2_1/mod.rs
index e6d47aa704b..5c465ddfadc 100644
--- a/clarity/src/vm/analysis/type_checker/v2_1/mod.rs
+++ b/clarity/src/vm/analysis/type_checker/v2_1/mod.rs
@@ -118,15 +118,6 @@ impl CostTracker for TypeChecker<'_, '_> {
fn reset_memory(&mut self) {
self.cost_track.reset_memory()
}
- fn short_circuit_contract_call(
- &mut self,
- contract: &QualifiedContractIdentifier,
- function: &ClarityName,
- input: &[u64],
- ) -> std::result::Result {
- self.cost_track
- .short_circuit_contract_call(contract, function, input)
- }
}
impl TypeChecker<'_, '_> {
diff --git a/clarity/src/vm/analysis/types.rs b/clarity/src/vm/analysis/types.rs
index 93b6328d5d7..844290f0a9c 100644
--- a/clarity/src/vm/analysis/types.rs
+++ b/clarity/src/vm/analysis/types.rs
@@ -62,7 +62,6 @@ pub struct ContractAnalysis {
pub defined_traits: BTreeMap>,
pub implemented_traits: BTreeSet,
pub contract_interface: Option,
- pub is_cost_contract_eligible: bool,
pub epoch: StacksEpochId,
pub clarity_version: ClarityVersion,
#[serde(skip)]
@@ -97,7 +96,6 @@ impl ContractAnalysis {
fungible_tokens: BTreeSet::new(),
non_fungible_tokens: BTreeMap::new(),
cost_track: Some(cost_track),
- is_cost_contract_eligible: false,
epoch,
clarity_version,
}
diff --git a/clarity/src/vm/ast/mod.rs b/clarity/src/vm/ast/mod.rs
index f8655b1e16c..1c535d247c5 100644
--- a/clarity/src/vm/ast/mod.rs
+++ b/clarity/src/vm/ast/mod.rs
@@ -268,9 +268,7 @@ mod test {
use crate::vm::costs::{LimitedCostTracker, *};
use crate::vm::representations::depth_traverse;
use crate::vm::types::QualifiedContractIdentifier;
- use crate::vm::{
- ClarityCostFunction, ClarityName, ClarityVersion, max_call_stack_depth_for_epoch,
- };
+ use crate::vm::{ClarityCostFunction, ClarityVersion, max_call_stack_depth_for_epoch};
#[derive(PartialEq, Debug)]
struct UnitTestTracker {
@@ -308,14 +306,6 @@ mod test {
Ok(())
}
fn reset_memory(&mut self) {}
- fn short_circuit_contract_call(
- &mut self,
- _contract: &QualifiedContractIdentifier,
- _function: &ClarityName,
- _input: &[u64],
- ) -> Result {
- Ok(false)
- }
}
#[test]
diff --git a/clarity/src/vm/contexts.rs b/clarity/src/vm/contexts.rs
index ede1fc2e747..0e2d6174685 100644
--- a/clarity/src/vm/contexts.rs
+++ b/clarity/src/vm/contexts.rs
@@ -15,7 +15,6 @@
// along with this program. If not, see .
use std::collections::{BTreeMap, HashMap, HashSet};
-use std::mem::replace;
pub use clarity_types::effects::{AssetMap, AssetMapEntry};
use clarity_types::representations::ClarityName;
@@ -641,16 +640,6 @@ impl CostTracker for ExecutionState<'_, '_, '_> {
fn reset_memory(&mut self) {
self.global_context.cost_track.reset_memory()
}
- fn short_circuit_contract_call(
- &mut self,
- contract: &QualifiedContractIdentifier,
- function: &ClarityName,
- input: &[u64],
- ) -> std::result::Result {
- self.global_context
- .cost_track
- .short_circuit_contract_call(contract, function, input)
- }
}
impl CostTracker for GlobalContext<'_, '_> {
@@ -674,35 +663,9 @@ impl CostTracker for GlobalContext<'_, '_> {
fn reset_memory(&mut self) {
self.cost_track.reset_memory()
}
- fn short_circuit_contract_call(
- &mut self,
- contract: &QualifiedContractIdentifier,
- function: &ClarityName,
- input: &[u64],
- ) -> std::result::Result {
- self.cost_track
- .short_circuit_contract_call(contract, function, input)
- }
}
impl<'a, 'b, 'hooks> ExecutionState<'a, 'b, 'hooks> {
- /// Used only for contract-call! cost short-circuiting. Once the short-circuited cost
- /// has been evaluated and assessed, the contract-call! itself is executed "for free".
- pub fn run_free(&mut self, invoke_ctx: &InvocationContext, to_run: F) -> A
- where
- F: FnOnce(&mut ExecutionState, &InvocationContext) -> A,
- {
- let original_tracker = replace(
- &mut self.global_context.cost_track,
- LimitedCostTracker::new_free(),
- );
- // note: it is important that this method not return until original_tracker has been
- // restored. DO NOT use the try syntax (?).
- let result = to_run(self, invoke_ctx);
- self.global_context.cost_track = original_tracker;
- result
- }
-
pub fn eval_read_only(
&mut self,
invoke_ctx: &InvocationContext,
diff --git a/clarity/src/vm/costs/mod.rs b/clarity/src/vm/costs/mod.rs
index eb298d7ddb7..1a7f82c11c2 100644
--- a/clarity/src/vm/costs/mod.rs
+++ b/clarity/src/vm/costs/mod.rs
@@ -23,7 +23,6 @@ use costs_2_testnet::Costs2Testnet;
use costs_3::Costs3;
use costs_4::Costs4;
use costs_5::Costs5;
-use lazy_static::lazy_static;
use serde::{Deserialize, Serialize};
use stacks_common::types::StacksEpochId;
@@ -38,12 +37,8 @@ use crate::vm::database::ClarityDatabase;
use crate::vm::database::clarity_store::NullBackingStore;
use crate::vm::errors::VmExecutionError;
use crate::vm::types::Value::UInt;
-use crate::vm::types::signatures::FunctionType::Fixed;
-use crate::vm::types::signatures::TupleTypeSignature;
-use crate::vm::types::{
- FunctionType, PrincipalData, QualifiedContractIdentifier, TupleData, TypeSignature,
-};
-use crate::vm::{CallStack, ClarityName, LocalContext, SymbolicExpression, Value};
+use crate::vm::types::{PrincipalData, QualifiedContractIdentifier, TypeSignature};
+use crate::vm::{CallStack, LocalContext, SymbolicExpression, Value};
pub mod constants;
pub mod cost_functions;
#[allow(unused_variables)]
@@ -69,37 +64,6 @@ pub const COSTS_3_NAME: &str = "costs-3";
pub const COSTS_4_NAME: &str = "costs-4";
pub const COSTS_5_NAME: &str = "costs-5";
-lazy_static! {
- static ref COST_TUPLE_TYPE_SIGNATURE: TypeSignature = {
- #[allow(clippy::expect_used)]
- TypeSignature::TupleType(
- TupleTypeSignature::try_from(vec![
- (
- ClarityName::from_literal("runtime"),
- TypeSignature::UIntType,
- ),
- (
- ClarityName::from_literal("write_length"),
- TypeSignature::UIntType,
- ),
- (
- ClarityName::from_literal("write_count"),
- TypeSignature::UIntType,
- ),
- (
- ClarityName::from_literal("read_count"),
- TypeSignature::UIntType,
- ),
- (
- ClarityName::from_literal("read_length"),
- TypeSignature::UIntType,
- ),
- ])
- .expect("BUG: failed to construct type signature for cost tuple"),
- )
- };
-}
-
pub fn runtime_cost, C: CostTracker>(
cost_function: ClarityCostFunction,
tracker: &mut C,
@@ -156,15 +120,6 @@ pub trait CostTracker {
fn add_memory(&mut self, memory: u64) -> Result<(), CostErrors>;
fn drop_memory(&mut self, memory: u64) -> Result<(), CostErrors>;
fn reset_memory(&mut self);
- /// Check if the given contract-call should be short-circuited.
- /// If so: this charges the cost to the CostTracker, and return true
- /// If not: return false
- fn short_circuit_contract_call(
- &mut self,
- contract: &QualifiedContractIdentifier,
- function: &ClarityName,
- input: &[u64],
- ) -> Result;
}
// Don't track!
@@ -186,14 +141,6 @@ impl CostTracker for () {
Ok(())
}
fn reset_memory(&mut self) {}
- fn short_circuit_contract_call(
- &mut self,
- _contract: &QualifiedContractIdentifier,
- _function: &ClarityName,
- _input: &[u64],
- ) -> Result {
- Ok(false)
- }
}
#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq)]
@@ -298,64 +245,11 @@ impl ClarityCostFunctionReference {
}
}
-#[derive(Debug, Clone)]
-pub struct CostStateSummary {
- pub contract_call_circuits:
- HashMap<(QualifiedContractIdentifier, ClarityName), ClarityCostFunctionReference>,
- pub cost_function_references: HashMap,
-}
-
-#[derive(Debug, Clone, Serialize, Deserialize)]
-struct SerializedCostStateSummary {
- contract_call_circuits: Vec<(
- (QualifiedContractIdentifier, ClarityName),
- ClarityCostFunctionReference,
- )>,
- cost_function_references: Vec<(ClarityCostFunction, ClarityCostFunctionReference)>,
-}
-
-impl From for SerializedCostStateSummary {
- fn from(other: CostStateSummary) -> SerializedCostStateSummary {
- let CostStateSummary {
- contract_call_circuits,
- cost_function_references,
- } = other;
- SerializedCostStateSummary {
- contract_call_circuits: contract_call_circuits.into_iter().collect(),
- cost_function_references: cost_function_references.into_iter().collect(),
- }
- }
-}
-
-impl From for CostStateSummary {
- fn from(other: SerializedCostStateSummary) -> CostStateSummary {
- let SerializedCostStateSummary {
- contract_call_circuits,
- cost_function_references,
- } = other;
- CostStateSummary {
- contract_call_circuits: contract_call_circuits.into_iter().collect(),
- cost_function_references: cost_function_references.into_iter().collect(),
- }
- }
-}
-
-impl CostStateSummary {
- pub fn empty() -> CostStateSummary {
- CostStateSummary {
- contract_call_circuits: HashMap::new(),
- cost_function_references: HashMap::new(),
- }
- }
-}
-
#[derive(Clone)]
/// This struct holds all of the data required for non-free LimitedCostTracker instances
pub struct TrackerData {
cost_function_references: HashMap<&'static ClarityCostFunction, ClarityCostFunctionEvaluator>,
cost_contracts: HashMap,
- contract_call_circuits:
- HashMap<(QualifiedContractIdentifier, ClarityName), ClarityCostFunctionReference>,
total: ExecutionCost,
limit: ExecutionCost,
memory: u64,
@@ -377,17 +271,6 @@ pub enum LimitedCostTracker {
#[cfg(any(test, feature = "testing"))]
impl LimitedCostTracker {
- pub fn contract_call_circuits(
- &self,
- ) -> HashMap<(QualifiedContractIdentifier, ClarityName), ClarityCostFunctionReference> {
- match self {
- Self::Free => panic!("Cannot get contract call circuits on free tracker"),
- Self::Limited(TrackerData {
- contract_call_circuits,
- ..
- }) => contract_call_circuits.clone(),
- }
- }
pub fn cost_function_references(
&self,
) -> HashMap<&'static ClarityCostFunction, ClarityCostFunctionEvaluator> {
@@ -437,360 +320,6 @@ impl PartialEq for LimitedCostTracker {
}
}
-fn load_state_summary(
- mainnet: bool,
- clarity_db: &mut ClarityDatabase,
-) -> Result {
- let cost_voting_contract = boot_code_id("cost-voting", mainnet);
-
- let clarity_epoch = clarity_db
- .get_clarity_epoch_version()
- .map_err(|e| CostErrors::CostComputationFailed(e.to_string()))?;
- let last_processed_at = match clarity_db.get_value(
- "vm-costs::last-processed-at-height",
- &TypeSignature::UIntType,
- &clarity_epoch,
- ) {
- Ok(Some(v)) => u32::try_from(
- v.value
- .expect_u128()
- .map_err(|_| CostErrors::InterpreterFailure)?,
- )
- .map_err(|_| CostErrors::InterpreterFailure)?,
- Ok(None) => return Ok(CostStateSummary::empty()),
- Err(e) => return Err(CostErrors::CostComputationFailed(e.to_string())),
- };
-
- let metadata_result = clarity_db
- .fetch_metadata_manual::(
- last_processed_at,
- &cost_voting_contract,
- "::state_summary",
- )
- .map_err(|e| CostErrors::CostComputationFailed(e.to_string()))?;
- let serialized: SerializedCostStateSummary = match metadata_result {
- Some(serialized) => {
- serde_json::from_str(&serialized).map_err(|_| CostErrors::InterpreterFailure)?
- }
- None => return Ok(CostStateSummary::empty()),
- };
- Ok(CostStateSummary::from(serialized))
-}
-
-fn store_state_summary(
- mainnet: bool,
- clarity_db: &mut ClarityDatabase,
- to_store: &CostStateSummary,
-) -> Result<(), CostErrors> {
- let block_height = clarity_db.get_current_block_height();
- let cost_voting_contract = boot_code_id("cost-voting", mainnet);
- let epoch = clarity_db
- .get_clarity_epoch_version()
- .map_err(|e| CostErrors::CostComputationFailed(e.to_string()))?;
- clarity_db
- .put_value(
- "vm-costs::last-processed-at-height",
- Value::UInt(block_height as u128),
- &epoch,
- )
- .map_err(|_e| CostErrors::CostContractLoadFailure)?;
- let serialized_summary =
- serde_json::to_string(&SerializedCostStateSummary::from(to_store.clone()))
- .map_err(|_| CostErrors::InterpreterFailure)?;
- clarity_db
- .set_metadata(
- &cost_voting_contract,
- "::state_summary",
- &serialized_summary,
- )
- .map_err(|e| CostErrors::Expect(e.to_string()))?;
-
- Ok(())
-}
-
-///
-/// This method loads a cost state summary structure from the currently open stacks chain tip
-/// In doing so, it reads from the cost-voting contract to find any newly confirmed proposals,
-/// checks those proposals for validity, and then applies those changes to the cached set
-/// of cost functions.
-///
-/// `apply_updates` - tells this function to look for any changes in the cost voting contract
-/// which would need to be applied. if `false`, just load the last computed cost state in this
-/// fork.
-///
-fn load_cost_functions(
- mainnet: bool,
- clarity_db: &mut ClarityDatabase,
- apply_updates: bool,
-) -> Result {
- let clarity_epoch = clarity_db
- .get_clarity_epoch_version()
- .map_err(|e| CostErrors::CostComputationFailed(e.to_string()))?;
- let last_processed_count = clarity_db
- .get_value(
- "vm-costs::last_processed_count",
- &TypeSignature::UIntType,
- &clarity_epoch,
- )
- .map_err(|_e| CostErrors::CostContractLoadFailure)?
- .map(|result| result.value)
- .unwrap_or(Value::UInt(0))
- .expect_u128()
- .map_err(|_| CostErrors::InterpreterFailure)?;
- let cost_voting_contract = boot_code_id("cost-voting", mainnet);
- let confirmed_proposals_count = clarity_db
- .lookup_variable_unknown_descriptor(
- &cost_voting_contract,
- "confirmed-proposal-count",
- &clarity_epoch,
- )
- .map_err(|e| CostErrors::CostComputationFailed(e.to_string()))?
- .expect_u128()
- .map_err(|_| CostErrors::InterpreterFailure)?;
- debug!("Check cost voting contract";
- "confirmed_proposal_count" => confirmed_proposals_count,
- "last_processed_count" => last_processed_count);
-
- // we need to process any confirmed proposals in the range [fetch-start, fetch-end)
- let (fetch_start, fetch_end) = (last_processed_count, confirmed_proposals_count);
- let mut state_summary = load_state_summary(mainnet, clarity_db)?;
- if !apply_updates {
- return Ok(state_summary);
- }
-
- for confirmed_proposal in fetch_start..fetch_end {
- // fetch the proposal data
- let entry = clarity_db
- .fetch_entry_unknown_descriptor(
- &cost_voting_contract,
- "confirmed-proposals",
- &Value::from(
- TupleData::from_data(vec![(
- ClarityName::from_literal("confirmed-id"),
- Value::UInt(confirmed_proposal),
- )])
- .map_err(|_| {
- CostErrors::Expect("BUG: failed to construct simple tuple".into())
- })?,
- ),
- &clarity_epoch,
- )
- .map_err(|_| CostErrors::Expect("BUG: Failed querying confirmed-proposals".into()))?
- .expect_optional()
- .map_err(|_| CostErrors::InterpreterFailure)?
- .ok_or_else(|| {
- CostErrors::Expect("BUG: confirmed-proposal-count exceeds stored proposals".into())
- })?
- .expect_tuple()
- .map_err(|_| CostErrors::InterpreterFailure)?;
- let target_contract = match entry
- .get("function-contract")
- .map_err(|_| CostErrors::Expect("BUG: malformed cost proposal tuple".into()))?
- .clone()
- .expect_principal()
- .map_err(|_| CostErrors::InterpreterFailure)?
- {
- PrincipalData::Contract(contract_id) => contract_id,
- _ => {
- warn!("Confirmed cost proposal invalid: function-contract is not a contract principal";
- "confirmed_proposal_id" => confirmed_proposal);
- continue;
- }
- };
- let target_function = match ClarityName::try_from(
- entry
- .get("function-name")
- .map_err(|_| CostErrors::Expect("BUG: malformed cost proposal tuple".into()))?
- .clone()
- .expect_ascii()
- .map_err(|_| CostErrors::InterpreterFailure)?,
- ) {
- Ok(x) => x,
- Err(_) => {
- warn!("Confirmed cost proposal invalid: function-name is not a valid function name";
- "confirmed_proposal_id" => confirmed_proposal);
- continue;
- }
- };
- let cost_contract = match entry
- .get("cost-function-contract")
- .map_err(|_| CostErrors::Expect("BUG: malformed cost proposal tuple".into()))?
- .clone()
- .expect_principal()
- .map_err(|_| CostErrors::InterpreterFailure)?
- {
- PrincipalData::Contract(contract_id) => contract_id,
- _ => {
- warn!("Confirmed cost proposal invalid: cost-function-contract is not a contract principal";
- "confirmed_proposal_id" => confirmed_proposal);
- continue;
- }
- };
-
- let cost_function = match ClarityName::try_from(
- entry
- .get_owned("cost-function-name")
- .map_err(|_| CostErrors::Expect("BUG: malformed cost proposal tuple".into()))?
- .expect_ascii()
- .map_err(|_| CostErrors::InterpreterFailure)?,
- ) {
- Ok(x) => x,
- Err(_) => {
- warn!("Confirmed cost proposal invalid: cost-function-name is not a valid function name";
- "confirmed_proposal_id" => confirmed_proposal);
- continue;
- }
- };
-
- // Here is where we perform the required validity checks for a confirmed proposal:
- // * Replaced contract-calls _must_ be `define-read-only` _or_ refer to one of the boot code
- // cost functions
- // * cost-function contracts must be arithmetic only
-
- // make sure the contract is "cost contract eligible" via the
- // arithmetic-checking analysis pass
- let (cost_func_ref, cost_func_type) = match clarity_db
- .load_contract_analysis(&cost_contract)
- .map_err(|e| CostErrors::CostComputationFailed(e.to_string()))?
- {
- Some(c) => {
- if !c.is_cost_contract_eligible {
- warn!("Confirmed cost proposal invalid: cost-function-contract uses non-arithmetic or otherwise illegal operations";
- "confirmed_proposal_id" => confirmed_proposal,
- "contract_name" => %cost_contract,
- );
- continue;
- }
-
- if let Some(FunctionType::Fixed(cost_function_type)) = c
- .read_only_function_types
- .get(&cost_function)
- .or_else(|| c.private_function_types.get(&cost_function))
- {
- if !cost_function_type.returns.eq(&COST_TUPLE_TYPE_SIGNATURE) {
- warn!("Confirmed cost proposal invalid: cost-function-name does not return a cost tuple";
- "confirmed_proposal_id" => confirmed_proposal,
- "contract_name" => %cost_contract,
- "function_name" => %cost_function,
- "return_type" => %cost_function_type.returns,
- );
- continue;
- }
- if !cost_function_type.args.len() == 1
- || cost_function_type.args[0].signature != TypeSignature::UIntType
- {
- warn!("Confirmed cost proposal invalid: cost-function-name args should be length-1 and only uint";
- "confirmed_proposal_id" => confirmed_proposal,
- "contract_name" => %cost_contract,
- "function_name" => %cost_function,
- );
- continue;
- }
- (
- ClarityCostFunctionReference {
- contract_id: cost_contract,
- function_name: cost_function.to_string(),
- },
- cost_function_type.clone(),
- )
- } else {
- warn!("Confirmed cost proposal invalid: cost-function-name not defined";
- "confirmed_proposal_id" => confirmed_proposal,
- "contract_name" => %cost_contract,
- "function_name" => %cost_function,
- );
- continue;
- }
- }
- None => {
- warn!("Confirmed cost proposal invalid: cost-function-contract is not a published contract";
- "confirmed_proposal_id" => confirmed_proposal,
- "contract_name" => %cost_contract,
- );
- continue;
- }
- };
-
- if target_contract == boot_code_id("costs", mainnet) {
- // refering to one of the boot code cost functions
- let target = match ClarityCostFunction::lookup_by_name(&target_function) {
- Some(ClarityCostFunction::Unimplemented) => {
- warn!("Attempted vote on unimplemented cost function";
- "confirmed_proposal_id" => confirmed_proposal,
- "cost_function" => %target_function);
- continue;
- }
- Some(cost_func) => cost_func,
- None => {
- warn!("Confirmed cost proposal invalid: function-name does not reference a Clarity cost function";
- "confirmed_proposal_id" => confirmed_proposal,
- "cost_function" => %target_function);
- continue;
- }
- };
- state_summary
- .cost_function_references
- .insert(target, cost_func_ref);
- } else {
- // referring to a user-defined function
- match clarity_db
- .load_contract_analysis(&target_contract)
- .map_err(|e| CostErrors::CostComputationFailed(e.to_string()))?
- {
- Some(c) => {
- if let Some(Fixed(tf)) = c.read_only_function_types.get(&target_function) {
- if cost_func_type.args.len() != tf.args.len() {
- warn!("Confirmed cost proposal invalid: cost-function contains the wrong number of arguments";
- "confirmed_proposal_id" => confirmed_proposal,
- "target_contract_name" => %target_contract,
- "target_function_name" => %target_function,
- );
- continue;
- }
- for arg in &cost_func_type.args {
- if arg.signature != TypeSignature::UIntType {
- warn!("Confirmed cost proposal invalid: contains non uint argument";
- "confirmed_proposal_id" => confirmed_proposal,
- );
- continue;
- }
- }
- } else {
- warn!("Confirmed cost proposal invalid: function-name not defined or is not read-only";
- "confirmed_proposal_id" => confirmed_proposal,
- "target_contract_name" => %target_contract,
- "target_function_name" => %target_function,
- );
- continue;
- }
- }
- None => {
- warn!("Confirmed cost proposal invalid: contract-name not a published contract";
- "confirmed_proposal_id" => confirmed_proposal,
- "target_contract_name" => %target_contract,
- );
- continue;
- }
- }
- state_summary
- .contract_call_circuits
- .insert((target_contract, target_function), cost_func_ref);
- }
- }
- if confirmed_proposals_count > last_processed_count {
- store_state_summary(mainnet, clarity_db, &state_summary)?;
- clarity_db
- .put_value(
- "vm-costs::last_processed_count",
- Value::UInt(confirmed_proposals_count),
- &clarity_epoch,
- )
- .map_err(|_e| CostErrors::CostContractLoadFailure)?;
- }
-
- Ok(state_summary)
-}
-
impl LimitedCostTracker {
pub fn new(
mainnet: bool,
@@ -802,7 +331,6 @@ impl LimitedCostTracker {
let mut cost_tracker = TrackerData {
cost_function_references: HashMap::new(),
cost_contracts: HashMap::new(),
- contract_call_circuits: HashMap::new(),
limit,
memory_limit: CLARITY_MEMORY_LIMIT,
total: ExecutionCost::ZERO,
@@ -812,7 +340,7 @@ impl LimitedCostTracker {
chain_id,
};
assert!(clarity_db.is_stack_empty());
- cost_tracker.load_costs(clarity_db, true)?;
+ cost_tracker.load_costs(clarity_db)?;
Ok(Self::Limited(cost_tracker))
}
@@ -826,7 +354,6 @@ impl LimitedCostTracker {
let mut cost_tracker = TrackerData {
cost_function_references: HashMap::new(),
cost_contracts: HashMap::new(),
- contract_call_circuits: HashMap::new(),
limit,
memory_limit: CLARITY_MEMORY_LIMIT,
total: ExecutionCost::ZERO,
@@ -835,7 +362,7 @@ impl LimitedCostTracker {
mainnet,
chain_id,
};
- cost_tracker.load_costs(clarity_db, false)?;
+ cost_tracker.load_costs(clarity_db)?;
Ok(Self::Limited(cost_tracker))
}
@@ -913,7 +440,6 @@ impl LimitedCostTracker {
let cost_tracker = TrackerData {
cost_function_references: cost_functions,
cost_contracts: HashMap::new(),
- contract_call_circuits: HashMap::new(),
limit,
memory_limit: CLARITY_MEMORY_LIMIT,
total: ExecutionCost::ZERO,
@@ -928,16 +454,11 @@ impl LimitedCostTracker {
}
impl TrackerData {
- // TODO: add tests from mutation testing results #4831
- #[cfg_attr(test, mutants::skip)]
- /// `apply_updates` - tells this function to look for any changes in the cost voting contract
- /// which would need to be applied. if `false`, just load the last computed cost state in this
- /// fork.
- fn load_costs(
- &mut self,
- clarity_db: &mut ClarityDatabase,
- apply_updates: bool,
- ) -> Result<(), CostErrors> {
+ /// Load the default cost functions for this tracker's epoch.
+ ///
+ /// Before Epoch 4.0, this also loads the corresponding boot cost contract.
+ /// Epoch 4.0 and later use the native Rust cost implementation.
+ fn load_costs(&mut self, clarity_db: &mut ClarityDatabase) -> Result<(), CostErrors> {
clarity_db.begin();
let epoch_id = clarity_db
.get_clarity_epoch_version()
@@ -953,108 +474,39 @@ impl TrackerData {
))
})?;
- // TODO(cost-voting): Remove after epoch 4.0 activation (when cost-voting deactivates), and
- // use `CostStateSummary::empty()` directly instead.
- let state_summary = if epoch_id.supports_cost_voting_contract() {
- // If cost-voting is active, load and apply the current cost function configuration from
- // the chain tip, applying any changes that have been voted on but not yet applied.
- load_cost_functions(self.mainnet, clarity_db, apply_updates).map_err(|e| {
- let result = clarity_db
- .roll_back()
- .map_err(|e| CostErrors::Expect(e.to_string()));
- match result {
- Ok(_) => e,
- Err(rollback_err) => rollback_err,
- }
- })?
- } else {
- // Cost-voting is retired at Epoch 4.0: every cost function uses boot defaults.
- CostStateSummary::empty()
- };
-
- let CostStateSummary {
- contract_call_circuits,
- mut cost_function_references,
- } = state_summary;
-
- self.contract_call_circuits = contract_call_circuits;
-
- let iter = ClarityCostFunction::ALL.iter();
- let iter_len = iter.len();
- let mut cost_contracts = HashMap::with_capacity(iter_len);
- let mut m = HashMap::with_capacity(iter_len);
-
- for f in iter {
- let cost_function_ref = cost_function_references.remove(f).unwrap_or_else(|| {
- ClarityCostFunctionReference::new(boot_costs_id.clone(), f.get_name())
- });
-
- let is_boot_default = cost_function_ref.contract_id == boot_costs_id;
-
- // Beginning in epoch 4.0 the costs are implemented in Rust only,
- // and not deployed on-chain in a contract. This is possible with
- // the disabling of cost-voting.
- let requires_contract_load =
- !is_boot_default || epoch_id.supports_cost_voting_contract();
-
- if requires_contract_load
- && !cost_contracts.contains_key(&cost_function_ref.contract_id)
- {
- let contract = match clarity_db.get_contract(&cost_function_ref.contract_id) {
- Ok(contract) => contract,
- Err(e) => {
- error!("Failed to load intended Clarity cost contract";
- "contract" => %cost_function_ref.contract_id,
- "error" => ?e);
- clarity_db
- .roll_back()
- .map_err(|e| CostErrors::Expect(e.to_string()))?;
- return Err(CostErrors::CostContractLoadFailure);
- }
- };
- cost_contracts.insert(cost_function_ref.contract_id.clone(), contract);
- }
-
- if is_boot_default {
- m.insert(
- f,
- ClarityCostFunctionEvaluator::Default(cost_function_ref, f.clone(), v),
- );
- } else {
- m.insert(f, ClarityCostFunctionEvaluator::Clarity(cost_function_ref));
- }
+ let mut m = HashMap::with_capacity(ClarityCostFunction::ALL.len());
+ for f in ClarityCostFunction::ALL.iter() {
+ let cost_function_ref =
+ ClarityCostFunctionReference::new(boot_costs_id.clone(), f.get_name());
+ m.insert(
+ f,
+ ClarityCostFunctionEvaluator::Default(cost_function_ref, f.clone(), v),
+ );
}
- for circuit_target in self.contract_call_circuits.values() {
- if !cost_contracts.contains_key(&circuit_target.contract_id) {
- let contract = match clarity_db.get_contract(&circuit_target.contract_id) {
- Ok(contract) => contract,
- Err(e) => {
- error!("Failed to load intended Clarity cost contract";
- "contract" => %boot_costs_id.to_string(),
- "error" => %format!("{:?}", e));
- clarity_db
- .roll_back()
- .map_err(|e| CostErrors::Expect(e.to_string()))?;
- return Err(CostErrors::CostContractLoadFailure);
- }
- };
- cost_contracts.insert(circuit_target.contract_id.clone(), contract);
- }
+ let mut cost_contracts = HashMap::with_capacity(1);
+ if epoch_id < StacksEpochId::Epoch40 {
+ let boot_cost_contract = match clarity_db.get_contract(&boot_costs_id) {
+ Ok(contract) => contract,
+ Err(e) => {
+ error!("Failed to load intended Clarity cost contract";
+ "contract" => %boot_costs_id,
+ "error" => ?e);
+ clarity_db
+ .roll_back()
+ .map_err(|e| CostErrors::Expect(e.to_string()))?;
+ return Err(CostErrors::CostContractLoadFailure);
+ }
+ };
+ cost_contracts.insert(boot_costs_id, boot_cost_contract);
}
self.cost_function_references = m;
self.cost_contracts = cost_contracts;
- if apply_updates {
- clarity_db
- .commit()
- .map_err(|e| CostErrors::Expect(e.to_string()))?;
- } else {
- clarity_db
- .roll_back()
- .map_err(|e| CostErrors::Expect(e.to_string()))?;
- }
+ clarity_db
+ .commit()
+ .map_err(|e| CostErrors::Expect(e.to_string()))?;
Ok(())
}
@@ -1296,29 +748,6 @@ impl CostTracker for LimitedCostTracker {
}
}
}
- fn short_circuit_contract_call(
- &mut self,
- contract: &QualifiedContractIdentifier,
- function: &ClarityName,
- input: &[u64],
- ) -> Result {
- match self {
- Self::Free => {
- // if we're already free, no need to worry about short circuiting contract-calls
- Ok(false)
- }
- Self::Limited(data) => {
- // grr, if HashMap::get didn't require Borrow, we wouldn't need this cloning.
- let lookup_key = (contract.clone(), function.clone());
- if let Some(cost_function) = data.contract_call_circuits.get(&lookup_key).cloned() {
- compute_cost(data, cost_function, input, data.epoch)?;
- Ok(true)
- } else {
- Ok(false)
- }
- }
- }
- }
}
impl CostTracker for &mut LimitedCostTracker {
@@ -1341,14 +770,6 @@ impl CostTracker for &mut LimitedCostTracker {
fn reset_memory(&mut self) {
LimitedCostTracker::reset_memory(self)
}
- fn short_circuit_contract_call(
- &mut self,
- contract: &QualifiedContractIdentifier,
- function: &ClarityName,
- input: &[u64],
- ) -> Result {
- LimitedCostTracker::short_circuit_contract_call(self, contract, function, input)
- }
}
// ONLY WORKS IF INPUT IS u64
diff --git a/clarity/src/vm/functions/database.rs b/clarity/src/vm/functions/database.rs
index e3adb9b2f45..380a446f7ab 100644
--- a/clarity/src/vm/functions/database.rs
+++ b/clarity/src/vm/functions/database.rs
@@ -81,10 +81,8 @@ pub fn special_contract_call(
let rest_args_slice = &args[2..];
let rest_args_len = rest_args_slice.len();
let mut rest_args = Vec::with_capacity(rest_args_len);
- let mut rest_args_sizes = Vec::with_capacity(rest_args_len);
for arg in rest_args_slice.iter() {
let evaluated_arg = eval(arg, exec_state, invoke_ctx, context)?;
- rest_args_sizes.push(evaluated_arg.as_ref().size()?.into());
rest_args.push(SymbolicExpression::atom_value(
evaluated_arg.clone_with_cost(exec_state)?,
));
@@ -238,29 +236,13 @@ pub fn special_contract_call(
.into();
let nested_ctx = invoke_ctx.with_caller(contract_principal);
- let result = if exec_state.short_circuit_contract_call(
+ let result = exec_state.execute_contract(
+ &nested_ctx,
&contract_identifier,
function_name,
- &rest_args_sizes,
- )? {
- exec_state.run_free(&nested_ctx, |free_exec_state, nested_ctx| {
- free_exec_state.execute_contract(
- nested_ctx,
- &contract_identifier,
- function_name,
- &rest_args,
- false,
- )
- })
- } else {
- exec_state.execute_contract(
- &nested_ctx,
- &contract_identifier,
- function_name,
- &rest_args,
- false,
- )
- }?;
+ &rest_args,
+ false,
+ )?;
// sanitize contract-call outputs in epochs >= 2.4
let result_type = TypeSignature::type_of(&result)?;
diff --git a/stacks-common/src/types/mod.rs b/stacks-common/src/types/mod.rs
index bbea3fcc248..1b8fac42a4c 100644
--- a/stacks-common/src/types/mod.rs
+++ b/stacks-common/src/types/mod.rs
@@ -622,12 +622,6 @@ impl StacksEpochId {
}
}
- /// Whether or not this epoch supports the cost-voting contract (SIP-006), which is
- /// disabled from Epoch 4.0 (SIP-044).
- pub fn supports_cost_voting_contract(&self) -> bool {
- self < &StacksEpochId::Epoch40
- }
-
/// Returns true for epochs which use Nakamoto blocks. These blocks use a
/// different header format than the previous Stacks blocks, which among
/// other changes includes a Stacks-specific timestamp.
diff --git a/stacks-common/src/types/tests.rs b/stacks-common/src/types/tests.rs
index 194c8256af5..ab24b36bd6d 100644
--- a/stacks-common/src/types/tests.rs
+++ b/stacks-common/src/types/tests.rs
@@ -25,25 +25,6 @@ use super::{
};
use crate::types::StacksEpochRangeTestExt as _;
-#[test]
-fn test_epoch40_cost_voting_contract_support_gate() {
- // Up to (excluding) Epoch 4.0 = enabled.
- for epoch in (..StacksEpochId::Epoch40).iter() {
- assert!(
- epoch.supports_cost_voting_contract(),
- "cost-voting gate must remain active before Epoch 4.0 for {epoch}"
- );
- }
-
- // Epoch 4.0 and onward = disabled.
- for epoch in (StacksEpochId::Epoch40..).iter() {
- assert!(
- !epoch.supports_cost_voting_contract(),
- "cost-voting gate must be inactive from Epoch 4.0 onward for {epoch}"
- );
- }
-}
-
#[test]
fn test_epoch_range_ext_iter() {
// Alias to keep the below cleaner
diff --git a/stacks-node/src/tests/neon_integrations.rs b/stacks-node/src/tests/neon_integrations.rs
index 3f34c2205a7..257c63f98d3 100644
--- a/stacks-node/src/tests/neon_integrations.rs
+++ b/stacks-node/src/tests/neon_integrations.rs
@@ -4183,352 +4183,6 @@ fn block_replay_integration_test() {
channel.stop_chains_coordinator();
}
-#[test]
-#[ignore]
-fn cost_voting_integration() {
- if env::var("BITCOIND_TEST") != Ok("1".into()) {
- return;
- }
-
- // let's make `<` free...
- let cost_definer_src = "
- (define-read-only (cost-definition-le (size uint))
- {
- runtime: u0, write_length: u0, write_count: u0, read_count: u0, read_length: u0
- })
- ";
-
- // the contract that we'll test the costs of
- let caller_src = "
- (define-public (execute-2 (a uint))
- (ok (< a a)))
- ";
-
- let power_vote_src = "
- (define-public (propose-vote-confirm)
- (let
- ((proposal-id (unwrap-panic (contract-call? 'ST000000000000000000002AMW42H.cost-voting submit-proposal
- 'ST000000000000000000002AMW42H.costs \"cost_le\"
- .cost-definer \"cost-definition-le\")))
- (vote-amount (* u9000000000 u1000000)))
- (try! (contract-call? 'ST000000000000000000002AMW42H.cost-voting vote-proposal proposal-id vote-amount))
- (try! (contract-call? 'ST000000000000000000002AMW42H.cost-voting confirm-votes proposal-id))
- (ok proposal-id)))
- ";
-
- let spender_sk = StacksPrivateKey::random();
- let spender_addr = to_addr(&spender_sk);
- let spender_princ: PrincipalData = spender_addr.clone().into();
-
- let (mut conf, miner_account) = neon_integration_test_conf();
-
- conf.miner.microblock_attempt_time_ms = 1_000;
- conf.node.wait_time_for_microblocks = 0;
- conf.node.microblock_frequency = 1_000;
- conf.miner.first_attempt_time_ms = 2_000;
- conf.miner.subsequent_attempt_time_ms = 5_000;
- conf.burnchain.max_rbf = 10_000_000;
- conf.node.wait_time_for_blocks = 1_000;
-
- test_observer::spawn();
- test_observer::register_any(&mut conf);
-
- let spender_bal = 10_000_000_000 * u64::from(core::MICROSTACKS_PER_STACKS);
-
- conf.initial_balances.push(InitialBalance {
- address: spender_princ.clone(),
- amount: spender_bal,
- });
-
- let mut btcd_controller = BitcoinCoreController::from_stx_config(&conf);
- btcd_controller
- .start_bitcoind()
- .expect("Failed starting bitcoind");
-
- let burnchain_config = Burnchain::regtest(&conf.get_burn_db_path());
-
- let mut btc_regtest_controller = BitcoinRegtestController::with_burnchain(
- conf.clone(),
- None,
- Some(burnchain_config.clone()),
- None,
- );
- let http_origin = format!("http://{}", &conf.node.rpc_bind);
-
- btc_regtest_controller.bootstrap_chain(201);
-
- eprintln!("Chain bootstrapped...");
-
- let mut run_loop = neon::RunLoop::new(conf.clone());
- let blocks_processed = run_loop.get_blocks_processed_arc();
- let channel = run_loop.get_coordinator_channel().unwrap();
-
- thread::spawn(move || run_loop.start(Some(burnchain_config), 0));
-
- // give the run loop some time to start up!
- wait_for_runloop(&blocks_processed);
-
- // first block wakes up the run loop
- next_block_and_wait(&mut btc_regtest_controller, &blocks_processed);
-
- // first block will hold our VRF registration
- next_block_and_wait(&mut btc_regtest_controller, &blocks_processed);
-
- // second block will be the first mined Stacks block
- next_block_and_wait(&mut btc_regtest_controller, &blocks_processed);
-
- // let's query the miner's account nonce:
- let res = get_account(&http_origin, &miner_account);
- assert_eq!(res.balance, 0);
- assert_eq!(res.nonce, 1);
-
- // and our spender:
- let res = get_account(&http_origin, &spender_princ);
- assert_eq!(res.balance, spender_bal as u128);
- assert_eq!(res.nonce, 0);
-
- let transactions = vec![
- make_contract_publish(
- &spender_sk,
- 0,
- 1000,
- conf.burnchain.chain_id,
- "cost-definer",
- cost_definer_src,
- ),
- make_contract_publish(
- &spender_sk,
- 1,
- 1000,
- conf.burnchain.chain_id,
- "caller",
- caller_src,
- ),
- make_contract_publish(
- &spender_sk,
- 2,
- 1000,
- conf.burnchain.chain_id,
- "voter",
- power_vote_src,
- ),
- ];
-
- for tx in transactions.into_iter() {
- submit_tx(&http_origin, &tx);
- }
-
- next_block_and_wait(&mut btc_regtest_controller, &blocks_processed);
- next_block_and_wait(&mut btc_regtest_controller, &blocks_processed);
-
- let vote_tx = make_contract_call(
- &spender_sk,
- 3,
- 1000,
- conf.burnchain.chain_id,
- &spender_addr,
- "voter",
- "propose-vote-confirm",
- &[],
- );
-
- let call_le_tx = make_contract_call(
- &spender_sk,
- 4,
- 1000,
- conf.burnchain.chain_id,
- &spender_addr,
- "caller",
- "execute-2",
- &[Value::UInt(1)],
- );
-
- test_observer::clear();
- submit_tx(&http_origin, &vote_tx);
- submit_tx(&http_origin, &call_le_tx);
-
- // Mine blocks until both txs are confirmed (nonces 3 and 4)
- mine_blocks_until(&btc_regtest_controller, &blocks_processed, 3, 60, || {
- let res = get_account(&http_origin, &spender_princ);
- Ok(res.nonce >= 5)
- })
- .expect("vote and execute txs should have been mined");
-
- let blocks = test_observer::get_blocks();
- let mut tested = false;
- let mut exec_cost = ExecutionCost::ZERO;
- for block in blocks.iter() {
- let transactions = block.get("transactions").unwrap().as_array().unwrap();
- for tx in transactions.iter() {
- let raw_tx = tx.get("raw_tx").unwrap().as_str().unwrap();
- if raw_tx == "0x00" {
- continue;
- }
- let tx_bytes = hex_bytes(&raw_tx[2..]).unwrap();
- let parsed = StacksTransaction::consensus_deserialize(&mut &tx_bytes[..]).unwrap();
- if let TransactionPayload::ContractCall(contract_call) = parsed.payload {
- eprintln!("{}", contract_call.function_name.as_str());
- if contract_call.function_name.as_str() == "execute-2" {
- exec_cost =
- serde_json::from_value(tx.get("execution_cost").cloned().unwrap()).unwrap();
- } else if contract_call.function_name.as_str() == "propose-vote-confirm" {
- let raw_result = tx.get("raw_result").unwrap().as_str().unwrap();
- let parsed = Value::try_deserialize_hex_untyped(&raw_result[2..]).unwrap();
- assert_eq!(parsed.to_string(), "(ok u0)");
- tested = true;
- }
- }
- }
- }
- assert!(tested, "Should have found a contract call tx");
-
- // try to confirm the passed vote (this will fail)
- let confirm_proposal = make_contract_call(
- &spender_sk,
- 5,
- 1000,
- conf.burnchain.chain_id,
- &StacksAddress::from_string("ST000000000000000000002AMW42H").unwrap(),
- "cost-voting",
- "confirm-miners",
- &[Value::UInt(0)],
- );
-
- test_observer::clear();
- submit_tx(&http_origin, &confirm_proposal);
-
- // Mine blocks until early confirm-miners is confirmed (nonce 5)
- mine_blocks_until(&btc_regtest_controller, &blocks_processed, 3, 60, || {
- let res = get_account(&http_origin, &spender_princ);
- Ok(res.nonce >= 6)
- })
- .expect("early confirm-miners tx should have been mined");
-
- let blocks = test_observer::get_blocks();
- let mut tested = false;
- for block in blocks.iter() {
- let transactions = block.get("transactions").unwrap().as_array().unwrap();
- for tx in transactions.iter() {
- let raw_tx = tx.get("raw_tx").unwrap().as_str().unwrap();
- if raw_tx == "0x00" {
- continue;
- }
- let tx_bytes = hex_bytes(&raw_tx[2..]).unwrap();
- let parsed = StacksTransaction::consensus_deserialize(&mut &tx_bytes[..]).unwrap();
- if let TransactionPayload::ContractCall(contract_call) = parsed.payload {
- eprintln!("{}", contract_call.function_name.as_str());
- if contract_call.function_name.as_str() == "confirm-miners" {
- let raw_result = tx.get("raw_result").unwrap().as_str().unwrap();
- let parsed = Value::try_deserialize_hex_untyped(&raw_result[2..]).unwrap();
- assert_eq!(parsed.to_string(), "(err 13)");
- tested = true;
- }
- }
- }
- }
- assert!(tested, "Should have found a contract call tx");
-
- for _i in 0..58 {
- next_block_and_wait(&mut btc_regtest_controller, &blocks_processed);
- }
-
- // confirm the passed vote
- let confirm_proposal = make_contract_call(
- &spender_sk,
- 6,
- 1000,
- conf.burnchain.chain_id,
- &StacksAddress::from_string("ST000000000000000000002AMW42H").unwrap(),
- "cost-voting",
- "confirm-miners",
- &[Value::UInt(0)],
- );
-
- test_observer::clear();
- submit_tx(&http_origin, &confirm_proposal);
-
- // Mine blocks until confirm-miners after maturation is confirmed (nonce 6)
- mine_blocks_until(&btc_regtest_controller, &blocks_processed, 3, 60, || {
- let res = get_account(&http_origin, &spender_princ);
- Ok(res.nonce >= 7)
- })
- .expect("confirm-miners tx should have been mined");
-
- let blocks = test_observer::get_blocks();
- let mut tested = false;
- for block in blocks.iter() {
- let transactions = block.get("transactions").unwrap().as_array().unwrap();
- for tx in transactions.iter() {
- let raw_tx = tx.get("raw_tx").unwrap().as_str().unwrap();
- if raw_tx == "0x00" {
- continue;
- }
- let tx_bytes = hex_bytes(&raw_tx[2..]).unwrap();
- let parsed = StacksTransaction::consensus_deserialize(&mut &tx_bytes[..]).unwrap();
- if let TransactionPayload::ContractCall(contract_call) = parsed.payload {
- eprintln!("{}", contract_call.function_name.as_str());
- if contract_call.function_name.as_str() == "confirm-miners" {
- let raw_result = tx.get("raw_result").unwrap().as_str().unwrap();
- let parsed = Value::try_deserialize_hex_untyped(&raw_result[2..]).unwrap();
- assert_eq!(parsed.to_string(), "(ok true)");
- tested = true;
- }
- }
- }
- }
- assert!(tested, "Should have found a contract call tx");
-
- let call_le_tx = make_contract_call(
- &spender_sk,
- 7,
- 1000,
- conf.burnchain.chain_id,
- &spender_addr,
- "caller",
- "execute-2",
- &[Value::UInt(1)],
- );
-
- test_observer::clear();
- submit_tx(&http_origin, &call_le_tx);
-
- // Mine blocks until execute-2 with new cost is confirmed (nonce 7)
- mine_blocks_until(&btc_regtest_controller, &blocks_processed, 3, 60, || {
- let res = get_account(&http_origin, &spender_princ);
- Ok(res.nonce >= 8)
- })
- .expect("execute-2 tx should have been mined");
-
- let blocks = test_observer::get_blocks();
- let mut tested = false;
- let mut new_exec_cost = ExecutionCost::max_value();
- for block in blocks.iter() {
- let transactions = block.get("transactions").unwrap().as_array().unwrap();
- for tx in transactions.iter() {
- let raw_tx = tx.get("raw_tx").unwrap().as_str().unwrap();
- if raw_tx == "0x00" {
- continue;
- }
- let tx_bytes = hex_bytes(&raw_tx[2..]).unwrap();
- let parsed = StacksTransaction::consensus_deserialize(&mut &tx_bytes[..]).unwrap();
- if let TransactionPayload::ContractCall(contract_call) = parsed.payload {
- eprintln!("{}", contract_call.function_name.as_str());
- if contract_call.function_name.as_str() == "execute-2" {
- new_exec_cost =
- serde_json::from_value(tx.get("execution_cost").cloned().unwrap()).unwrap();
- tested = true;
- }
- }
- }
- }
- assert!(tested, "Should have found a contract call tx");
-
- assert!(exec_cost.exceeds(&new_exec_cost));
-
- test_observer::clear();
- channel.stop_chains_coordinator();
-}
-
#[test]
#[ignore]
fn mining_events_integration_test() {
diff --git a/stackslib/src/chainstate/stacks/boot/contract_tests.rs b/stackslib/src/chainstate/stacks/boot/contract_tests.rs
index 1999da801ab..8a775b18b7d 100644
--- a/stackslib/src/chainstate/stacks/boot/contract_tests.rs
+++ b/stackslib/src/chainstate/stacks/boot/contract_tests.rs
@@ -15,8 +15,6 @@
use std::ops::Deref;
use clarity::util::get_epoch_time_secs;
-use clarity::vm::analysis::arithmetic_checker::ArithmeticOnlyChecker;
-use clarity::vm::analysis::mem_type_check;
use clarity::vm::clarity::TransactionConnection;
use clarity::vm::contexts::OwnedEnvironment;
use clarity::vm::database::*;
@@ -24,8 +22,7 @@ use clarity::vm::errors::{RuntimeCheckErrorKind, StaticCheckErrorKind, VmExecuti
use clarity::vm::resource_limiter::ResourceBudget;
use clarity::vm::test_util::{execute, symbols_from_values, TEST_BURN_STATE_DB, TEST_HEADER_DB};
use clarity::vm::types::{
- OptionalData, PrincipalData, QualifiedContractIdentifier, ResponseData, StandardPrincipalData,
- TupleData, Value,
+ PrincipalData, QualifiedContractIdentifier, StandardPrincipalData, TupleData, Value,
};
use clarity::vm::version::ClarityVersion;
use lazy_static::lazy_static;
@@ -39,10 +36,7 @@ use super::SIGNERS_MAX_LIST_SIZE;
use crate::burnchains::PoxConstants;
use crate::chainstate::burn::ConsensusHash;
use crate::chainstate::stacks::address::PoxAddress;
-use crate::chainstate::stacks::boot::{
- BOOT_CODE_COST_VOTING_TESTNET as BOOT_CODE_COST_VOTING, BOOT_CODE_POX_TESTNET,
- POX_2_TESTNET_CODE,
-};
+use crate::chainstate::stacks::boot::{BOOT_CODE_POX_TESTNET, POX_2_TESTNET_CODE};
use crate::chainstate::stacks::index::ClarityMarfTrieId;
use crate::chainstate::stacks::{C32_ADDRESS_VERSION_TESTNET_SINGLESIG, *};
use crate::clarity_vm::clarity::{
@@ -68,8 +62,6 @@ lazy_static! {
pub static ref POX_CONTRACT_TESTNET: QualifiedContractIdentifier = boot_code_id("pox", false);
pub static ref POX_2_CONTRACT_TESTNET: QualifiedContractIdentifier =
boot_code_id("pox-2", false);
- pub static ref COST_VOTING_CONTRACT_TESTNET: QualifiedContractIdentifier =
- boot_code_id("cost-voting", false);
pub static ref USER_KEYS: Vec =
(0..50).map(|_| StacksPrivateKey::random()).collect();
pub static ref POX_ADDRS: Vec = (0..50u64)
@@ -334,26 +326,6 @@ pub fn test_sim_hash_to_fork(in_bytes: &[u8; 32]) -> Option {
}
}
-#[cfg(test)]
-fn check_arithmetic_only(contract: &str, version: ClarityVersion) {
- let analysis = mem_type_check(contract, version, StacksEpochId::latest())
- .unwrap()
- .1;
- ArithmeticOnlyChecker::run(&analysis).expect("Should pass arithmetic checks");
-}
-
-#[test]
-fn cost_contract_is_arithmetic_only() {
- use crate::chainstate::stacks::boot::BOOT_CODE_COSTS;
- check_arithmetic_only(BOOT_CODE_COSTS, ClarityVersion::Clarity1);
-}
-
-#[test]
-fn cost_2_contract_is_arithmetic_only() {
- use crate::chainstate::stacks::boot::BOOT_CODE_COSTS_2;
- check_arithmetic_only(BOOT_CODE_COSTS_2, ClarityVersion::Clarity2);
-}
-
impl BurnStateDB for TestSimBurnStateDB {
fn get_tip_burn_block_height(&self) -> Option {
Some(self.height)
@@ -2491,664 +2463,3 @@ fn delegation_tests() {
);
});
}
-
-#[test]
-fn test_vote_withdrawal() {
- let mut sim = ClarityTestSim::new();
-
- sim.execute_next_block(|env| {
- env.initialize_versioned_contract(
- COST_VOTING_CONTRACT_TESTNET.clone(),
- ClarityVersion::Clarity1,
- &BOOT_CODE_COST_VOTING,
- None,
- )
- .unwrap();
-
- // Submit a proposal
- assert_eq!(
- env.execute_transaction(
- (&USER_KEYS[0]).into(),
- None,
- COST_VOTING_CONTRACT_TESTNET.clone(),
- "submit-proposal",
- &symbols_from_values(vec![
- Value::Principal(
- PrincipalData::parse_qualified_contract_principal(
- "ST000000000000000000002AMW42H.function-name"
- )
- .unwrap()
- ),
- Value::string_ascii_from_bytes("function-name".into()).unwrap(),
- Value::Principal(
- PrincipalData::parse_qualified_contract_principal(
- "ST000000000000000000002AMW42H.cost-function-name"
- )
- .unwrap()
- ),
- Value::string_ascii_from_bytes("cost-function-name".into()).unwrap(),
- ])
- )
- .unwrap()
- .0,
- Value::Response(ResponseData {
- committed: true,
- data: Value::UInt(0).into()
- })
- );
-
- // Vote on the proposal
- env.execute_transaction(
- (&USER_KEYS[0]).into(),
- None,
- COST_VOTING_CONTRACT_TESTNET.clone(),
- "vote-proposal",
- &symbols_from_values(vec![Value::UInt(0), Value::UInt(10)]),
- )
- .unwrap();
-
- // Assert that the number of votes is correct
- assert_eq!(
- env.execute_transaction(
- (&USER_KEYS[0]).into(),
- None,
- COST_VOTING_CONTRACT_TESTNET.clone(),
- "get-proposal-votes",
- &symbols_from_values(vec![Value::UInt(0)])
- )
- .unwrap()
- .0,
- Value::Optional(OptionalData {
- data: Some(Box::from(Value::UInt(10)))
- })
- );
-
- // Vote again on the proposal
- env.execute_transaction(
- (&USER_KEYS[0]).into(),
- None,
- COST_VOTING_CONTRACT_TESTNET.clone(),
- "vote-proposal",
- &symbols_from_values(vec![Value::UInt(0), Value::UInt(5)]),
- )
- .unwrap();
-
- // Assert that the number of votes is correct
- assert_eq!(
- env.execute_transaction(
- (&USER_KEYS[0]).into(),
- None,
- COST_VOTING_CONTRACT_TESTNET.clone(),
- "get-proposal-votes",
- &symbols_from_values(vec![Value::UInt(0)])
- )
- .unwrap()
- .0,
- Value::Optional(OptionalData {
- data: Some(Box::from(Value::UInt(15)))
- })
- );
-
- // Assert votes are assigned to principal
- assert_eq!(
- env.execute_transaction(
- (&USER_KEYS[0]).into(),
- None,
- COST_VOTING_CONTRACT_TESTNET.clone(),
- "get-principal-votes",
- &symbols_from_values(vec![
- Value::Principal(StandardPrincipalData::from(&USER_KEYS[0]).into()),
- Value::UInt(0),
- ])
- )
- .unwrap()
- .0,
- Value::Optional(OptionalData {
- data: Some(Box::from(Value::UInt(15)))
- })
- );
-
- // Assert withdrawal fails if amount is more than voted
- assert_eq!(
- env.execute_transaction(
- (&USER_KEYS[0]).into(),
- None,
- COST_VOTING_CONTRACT_TESTNET.clone(),
- "withdraw-votes",
- &symbols_from_values(vec![Value::UInt(0), Value::UInt(20)]),
- )
- .unwrap()
- .0,
- Value::Response(ResponseData {
- committed: false,
- data: Value::Int(5).into()
- })
- );
-
- // Withdraw votes
- env.execute_transaction(
- (&USER_KEYS[0]).into(),
- None,
- COST_VOTING_CONTRACT_TESTNET.clone(),
- "withdraw-votes",
- &symbols_from_values(vec![Value::UInt(0), Value::UInt(5)]),
- )
- .unwrap();
-
- // Assert withdrawal worked
- assert_eq!(
- env.execute_transaction(
- (&USER_KEYS[0]).into(),
- None,
- COST_VOTING_CONTRACT_TESTNET.clone(),
- "get-proposal-votes",
- &symbols_from_values(vec![Value::UInt(0)])
- )
- .unwrap()
- .0,
- Value::Optional(OptionalData {
- data: Some(Box::from(Value::UInt(10)))
- })
- );
- });
-
- // Fast forward to proposal expiration
- for _ in 0..2016 {
- sim.execute_next_block(|_| {});
- }
-
- sim.execute_next_block(|env| {
- // Withdraw STX after proposal expires
- env.execute_transaction(
- (&USER_KEYS[0]).into(),
- None,
- COST_VOTING_CONTRACT_TESTNET.clone(),
- "withdraw-votes",
- &symbols_from_values(vec![Value::UInt(0), Value::UInt(10)]),
- )
- .unwrap();
- });
-
- sim.execute_next_block(|env| {
- // Assert that stx balance is correct
- assert_eq!(
- env.eval_read_only(
- &COST_VOTING_CONTRACT_TESTNET,
- &format!("(stx-get-balance '{})", &Value::from(&USER_KEYS[0]))
- )
- .unwrap()
- .0,
- Value::UInt(1000000)
- );
- });
-}
-
-#[test]
-fn test_vote_fail() {
- let mut sim = ClarityTestSim::new();
-
- // Test voting in a proposal
- sim.execute_next_block(|env| {
- env.initialize_contract(
- COST_VOTING_CONTRACT_TESTNET.clone(),
- &BOOT_CODE_COST_VOTING,
- None,
- )
- .unwrap();
-
- // Submit a proposal
- assert_eq!(
- env.execute_transaction(
- (&USER_KEYS[0]).into(),
- None,
- COST_VOTING_CONTRACT_TESTNET.clone(),
- "submit-proposal",
- &symbols_from_values(vec![
- Value::Principal(
- PrincipalData::parse_qualified_contract_principal(
- "ST000000000000000000002AMW42H.function-name2"
- )
- .unwrap()
- ),
- Value::string_ascii_from_bytes("function-name2".into()).unwrap(),
- Value::Principal(
- PrincipalData::parse_qualified_contract_principal(
- "ST000000000000000000002AMW42H.cost-function-name2"
- )
- .unwrap()
- ),
- Value::string_ascii_from_bytes("cost-function-name2".into()).unwrap(),
- ])
- )
- .unwrap()
- .0,
- Value::Response(ResponseData {
- committed: true,
- data: Value::UInt(0).into()
- })
- );
-
- // Assert confirmation fails
- assert_eq!(
- env.execute_transaction(
- (&USER_KEYS[0]).into(),
- None,
- COST_VOTING_CONTRACT_TESTNET.clone(),
- "confirm-votes",
- &symbols_from_values(vec![Value::UInt(0)])
- )
- .unwrap()
- .0,
- Value::Response(ResponseData {
- committed: false,
- data: Value::Int(11).into()
- })
- );
-
- // Assert voting with more STX than are in an account fails
- assert_eq!(
- env.execute_transaction(
- (&USER_KEYS[0]).into(),
- None,
- COST_VOTING_CONTRACT_TESTNET.clone(),
- "vote-proposal",
- &symbols_from_values(vec![Value::UInt(0), Value::UInt(USTX_PER_HOLDER + 1)]),
- )
- .unwrap()
- .0,
- Value::Response(ResponseData {
- committed: false,
- data: Value::Int(5).into()
- })
- );
-
- // Commit all liquid stacks to vote
- for user in USER_KEYS.iter() {
- env.execute_transaction(
- user.into(),
- None,
- COST_VOTING_CONTRACT_TESTNET.clone(),
- "vote-proposal",
- &symbols_from_values(vec![Value::UInt(0), Value::UInt(USTX_PER_HOLDER)]),
- )
- .unwrap();
- }
-
- // Assert confirmation returns true
- assert_eq!(
- env.execute_transaction(
- (&USER_KEYS[0]).into(),
- None,
- COST_VOTING_CONTRACT_TESTNET.clone(),
- "confirm-votes",
- &symbols_from_values(vec![Value::UInt(0)])
- )
- .unwrap()
- .0,
- Value::Response(ResponseData {
- committed: true,
- data: Value::Bool(true).into()
- })
- );
- });
-
- sim.execute_next_block(|env| {
- env.execute_transaction(
- (&MINER_KEY.clone()).into(),
- None,
- COST_VOTING_CONTRACT_TESTNET.clone(),
- "veto",
- &symbols_from_values(vec![Value::UInt(0)]),
- )
- .unwrap();
-
- assert_eq!(
- env.execute_transaction(
- (&USER_KEYS[0]).into(),
- None,
- COST_VOTING_CONTRACT_TESTNET.clone(),
- "get-proposal-vetos",
- &symbols_from_values(vec![Value::UInt(0)])
- )
- .unwrap()
- .0,
- Value::Optional(OptionalData {
- data: Some(Box::from(Value::UInt(1)))
- })
- );
- });
-
- let fork_start = sim.block_height;
-
- for i in 0..25 {
- sim.execute_next_block(|env| {
- env.execute_transaction(
- (&MINER_KEY.clone()).into(),
- None,
- COST_VOTING_CONTRACT_TESTNET.clone(),
- "veto",
- &symbols_from_values(vec![Value::UInt(0)]),
- )
- .unwrap();
-
- // assert error if already vetoed in this block
- assert_eq!(
- env.execute_transaction(
- (&MINER_KEY.clone()).into(),
- None,
- COST_VOTING_CONTRACT_TESTNET.clone(),
- "veto",
- &symbols_from_values(vec![Value::UInt(0)])
- )
- .unwrap()
- .0,
- Value::Response(ResponseData {
- committed: false,
- data: Value::Int(9).into()
- })
- );
- })
- }
-
- for _ in 0..100 {
- sim.execute_next_block(|_| {});
- }
-
- sim.execute_next_block(|env| {
- // Assert confirmation fails because of majority veto
- assert_eq!(
- env.execute_transaction(
- (&USER_KEYS[0]).into(),
- None,
- COST_VOTING_CONTRACT_TESTNET.clone(),
- "confirm-miners",
- &symbols_from_values(vec![Value::UInt(0)])
- )
- .unwrap()
- .0,
- Value::Response(ResponseData {
- committed: false,
- data: Value::Int(14).into()
- })
- );
- });
-
- // let's fork, and overcome the veto
- sim.execute_block_as_fork(fork_start, |_| {});
- for _ in 0..125 {
- sim.execute_next_block(|_| {});
- }
-
- sim.execute_next_block(|env| {
- // Assert confirmation passes because there are no vetos
- assert_eq!(
- env.execute_transaction(
- (&USER_KEYS[0]).into(),
- None,
- COST_VOTING_CONTRACT_TESTNET.clone(),
- "confirm-miners",
- &symbols_from_values(vec![Value::UInt(0)])
- )
- .unwrap()
- .0,
- Value::Response(ResponseData {
- committed: true,
- data: Value::Bool(true).into(),
- })
- );
- });
-}
-
-#[test]
-fn test_vote_confirm() {
- let mut sim = ClarityTestSim::new();
-
- sim.execute_next_block(|env| {
- env.initialize_contract(
- COST_VOTING_CONTRACT_TESTNET.clone(),
- &BOOT_CODE_COST_VOTING,
- None,
- )
- .unwrap();
-
- // Submit a proposal
- assert_eq!(
- env.execute_transaction(
- (&USER_KEYS[0]).into(),
- None,
- COST_VOTING_CONTRACT_TESTNET.clone(),
- "submit-proposal",
- &symbols_from_values(vec![
- Value::Principal(
- PrincipalData::parse_qualified_contract_principal(
- "ST000000000000000000002AMW42H.function-name2"
- )
- .unwrap()
- ),
- Value::string_ascii_from_bytes("function-name2".into()).unwrap(),
- Value::Principal(
- PrincipalData::parse_qualified_contract_principal(
- "ST000000000000000000002AMW42H.cost-function-name2"
- )
- .unwrap()
- ),
- Value::string_ascii_from_bytes("cost-function-name2".into()).unwrap(),
- ])
- )
- .unwrap()
- .0,
- Value::Response(ResponseData {
- committed: true,
- data: Value::UInt(0).into()
- })
- );
-
- // Assert confirmation fails
- assert_eq!(
- env.execute_transaction(
- (&USER_KEYS[0]).into(),
- None,
- COST_VOTING_CONTRACT_TESTNET.clone(),
- "confirm-votes",
- &symbols_from_values(vec![Value::UInt(0)])
- )
- .unwrap()
- .0,
- Value::Response(ResponseData {
- committed: false,
- data: Value::Int(11).into()
- })
- );
-
- // Commit all liquid stacks to vote
- for user in USER_KEYS.iter() {
- env.execute_transaction(
- user.into(),
- None,
- COST_VOTING_CONTRACT_TESTNET.clone(),
- "vote-proposal",
- &symbols_from_values(vec![Value::UInt(0), Value::UInt(USTX_PER_HOLDER)]),
- )
- .unwrap();
- }
-
- // Assert confirmation returns true
- assert_eq!(
- env.execute_transaction(
- (&USER_KEYS[0]).into(),
- None,
- COST_VOTING_CONTRACT_TESTNET.clone(),
- "confirm-votes",
- &symbols_from_values(vec![Value::UInt(0)])
- )
- .unwrap()
- .0,
- Value::Response(ResponseData {
- committed: true,
- data: Value::Bool(true).into()
- })
- );
- });
-
- // Fast forward to proposal expiration
- for _ in 0..2016 {
- sim.execute_next_block(|_| {});
- }
-
- for _ in 0..1007 {
- sim.execute_next_block(|_| {});
- }
-
- sim.execute_next_block(|env| {
- // Assert confirmation passes
- assert_eq!(
- env.execute_transaction(
- (&USER_KEYS[0]).into(),
- None,
- COST_VOTING_CONTRACT_TESTNET.clone(),
- "confirm-miners",
- &symbols_from_values(vec![Value::UInt(0)])
- )
- .unwrap()
- .0,
- Value::Response(ResponseData {
- committed: true,
- data: Value::Bool(true).into()
- })
- );
- });
-}
-
-#[test]
-fn test_vote_too_many_confirms() {
- let mut sim = ClarityTestSim::new();
-
- let MAX_CONFIRMATIONS_PER_BLOCK = 10;
- sim.execute_next_block(|env| {
- env.initialize_contract(
- COST_VOTING_CONTRACT_TESTNET.clone(),
- &BOOT_CODE_COST_VOTING,
- None,
- )
- .unwrap();
-
- // Submit a proposal
- for i in 0..(MAX_CONFIRMATIONS_PER_BLOCK + 1) {
- assert_eq!(
- env.execute_transaction(
- (&USER_KEYS[0]).into(),
- None,
- COST_VOTING_CONTRACT_TESTNET.clone(),
- "submit-proposal",
- &symbols_from_values(vec![
- Value::Principal(
- PrincipalData::parse_qualified_contract_principal(
- "ST000000000000000000002AMW42H.function-name2"
- )
- .unwrap()
- ),
- Value::string_ascii_from_bytes("function-name2".into()).unwrap(),
- Value::Principal(
- PrincipalData::parse_qualified_contract_principal(
- "ST000000000000000000002AMW42H.cost-function-name2"
- )
- .unwrap()
- ),
- Value::string_ascii_from_bytes("cost-function-name2".into()).unwrap(),
- ])
- )
- .unwrap()
- .0,
- Value::Response(ResponseData {
- committed: true,
- data: Value::UInt(i).into()
- })
- );
- }
-
- for i in 0..(MAX_CONFIRMATIONS_PER_BLOCK + 1) {
- // Commit all liquid stacks to vote
- for user in USER_KEYS.iter() {
- assert_eq!(
- env.execute_transaction(
- user.into(),
- None,
- COST_VOTING_CONTRACT_TESTNET.clone(),
- "vote-proposal",
- &symbols_from_values(vec![Value::UInt(i), Value::UInt(USTX_PER_HOLDER)]),
- )
- .unwrap()
- .0,
- Value::okay_true()
- );
- }
-
- // Assert confirmation returns true
- assert_eq!(
- env.execute_transaction(
- (&USER_KEYS[0]).into(),
- None,
- COST_VOTING_CONTRACT_TESTNET.clone(),
- "confirm-votes",
- &symbols_from_values(vec![Value::UInt(i)])
- )
- .unwrap()
- .0,
- Value::okay_true(),
- );
-
- // withdraw
- for user in USER_KEYS.iter() {
- env.execute_transaction(
- user.into(),
- None,
- COST_VOTING_CONTRACT_TESTNET.clone(),
- "withdraw-votes",
- &symbols_from_values(vec![Value::UInt(i), Value::UInt(USTX_PER_HOLDER)]),
- )
- .unwrap();
- }
- }
- });
-
- // Fast forward to proposal expiration
- for _ in 0..2016 {
- sim.execute_next_block(|_| {});
- }
-
- for _ in 0..1007 {
- sim.execute_next_block(|_| {});
- }
-
- sim.execute_next_block(|env| {
- for i in 0..MAX_CONFIRMATIONS_PER_BLOCK {
- // Assert confirmation passes
- assert_eq!(
- env.execute_transaction(
- (&USER_KEYS[0]).into(),
- None,
- COST_VOTING_CONTRACT_TESTNET.clone(),
- "confirm-miners",
- &symbols_from_values(vec![Value::UInt(i)])
- )
- .unwrap()
- .0,
- Value::okay_true(),
- );
- }
-
- // Assert next confirmation fails
- assert_eq!(
- env.execute_transaction(
- (&USER_KEYS[0]).into(),
- None,
- COST_VOTING_CONTRACT_TESTNET.clone(),
- "confirm-miners",
- &symbols_from_values(vec![Value::UInt(MAX_CONFIRMATIONS_PER_BLOCK)])
- )
- .unwrap()
- .0,
- Value::error(Value::Int(17)).unwrap()
- );
- });
-}
diff --git a/stackslib/src/clarity_vm/tests/costs.rs b/stackslib/src/clarity_vm/tests/costs.rs
index f8fa8a44b91..ea2dacae3b0 100644
--- a/stackslib/src/clarity_vm/tests/costs.rs
+++ b/stackslib/src/clarity_vm/tests/costs.rs
@@ -14,7 +14,6 @@
// You should have received a copy of the GNU General Public License
// along with this program. If not, see .
-use clarity::vm::clarity::TransactionConnection;
use clarity::vm::contexts::{AssetMap, OwnedEnvironment};
use clarity::vm::costs::cost_functions::ClarityCostFunction;
use clarity::vm::costs::{
@@ -22,42 +21,25 @@ use clarity::vm::costs::{
DefaultVersion, ExecutionCost, LimitedCostTracker, COSTS_1_NAME, COSTS_2_NAME, COSTS_3_NAME,
COSTS_4_NAME,
};
-use clarity::vm::database::clarity_db::ClarityDatabase;
use clarity::vm::errors::VmExecutionError;
use clarity::vm::events::StacksTransactionEvent;
use clarity::vm::functions::NativeFunctions;
use clarity::vm::representations::SymbolicExpression;
-use clarity::vm::resource_limiter::ResourceBudget;
use clarity::vm::test_util::{
- execute, execute_on_network, generate_test_burn_state_db, symbols_from_values,
- TEST_BURN_STATE_DB, TEST_BURN_STATE_DB_205, TEST_BURN_STATE_DB_21, TEST_HEADER_DB,
+ execute, generate_test_burn_state_db, symbols_from_values, TEST_HEADER_DB,
};
-use clarity::vm::tests::test_only_mainnet_to_chain_id;
use clarity::vm::types::{
PrincipalData, QualifiedContractIdentifier, StandardPrincipalData, Value,
};
-use clarity::vm::{ClarityName, ClarityVersion, ContractName};
-use lazy_static::lazy_static;
-use stacks_common::types::chainstate::StacksBlockId;
+use clarity::vm::{ClarityVersion, ContractName};
use stacks_common::types::StacksEpochId;
-use crate::chainstate::stacks::index::ClarityMarfTrieId;
-use crate::clarity_vm::clarity::{ClarityInstance, ClarityMarfStore, ClarityMarfStoreTransaction};
-use crate::clarity_vm::database::marf::MarfedKV;
+use crate::clarity_vm::clarity::ClarityMarfStore;
use crate::clarity_vm::tests::utils::{
- new_cost_test_clarity_instance, next_test_block_id, setup_cost_test_epoch,
- setup_cost_test_epochs_through, TEST_TEST_COST_BOOT_EPOCHS,
+ new_cost_test_clarity_instance, next_test_block_id, setup_cost_test_epochs_through,
};
-use crate::core::{FIRST_BURNCHAIN_CONSENSUS_HASH, FIRST_STACKS_BLOCK_HASH};
use crate::util_lib::boot::boot_code_id;
-lazy_static! {
- static ref COST_VOTING_MAINNET_CONTRACT: QualifiedContractIdentifier =
- boot_code_id("cost-voting", true);
- static ref COST_VOTING_TESTNET_CONTRACT: QualifiedContractIdentifier =
- boot_code_id("cost-voting", false);
-}
-
pub fn get_simple_test(function: &NativeFunctions) -> Option<&'static str> {
use clarity::vm::functions::NativeFunctions::*;
let s = match function {
@@ -242,233 +224,6 @@ where
))
}
-fn cost_voting_contract(use_mainnet: bool) -> &'static QualifiedContractIdentifier {
- if use_mainnet {
- &COST_VOTING_MAINNET_CONTRACT
- } else {
- &COST_VOTING_TESTNET_CONTRACT
- }
-}
-
-struct CostProposal {
- function_contract: PrincipalData,
- function_name: &'static str,
- cost_function_contract: PrincipalData,
- cost_function_name: &'static str,
-}
-
-fn cost_proposal(
- function_contract: impl Into,
- function_name: &'static str,
- cost_function_contract: impl Into,
- cost_function_name: &'static str,
-) -> CostProposal {
- CostProposal {
- function_contract: function_contract.into(),
- function_name,
- cost_function_contract: cost_function_contract.into(),
- cost_function_name,
- }
-}
-
-fn write_confirmed_cost_proposals(
- db: &mut ClarityDatabase,
- use_mainnet: bool,
- proposal_start_id: usize,
- confirmed_proposal_count: usize,
- proposals: impl IntoIterator- ,
-) {
- let voting_contract_to_use = cost_voting_contract(use_mainnet);
- db.set_variable_unknown_descriptor(
- voting_contract_to_use,
- "confirmed-proposal-count",
- Value::UInt(confirmed_proposal_count as u128),
- )
- .unwrap();
-
- let epoch = db.get_clarity_epoch_version().unwrap();
- for (ix, proposal) in proposals.into_iter().enumerate() {
- let value = format!(
- "{{ function-contract: '{},
- function-name: \"{}\",
- cost-function-contract: '{},
- cost-function-name: \"{}\",
- confirmed-height: u1 }}",
- proposal.function_contract,
- proposal.function_name,
- proposal.cost_function_contract,
- proposal.cost_function_name
- );
- db.set_entry_unknown_descriptor(
- voting_contract_to_use,
- "confirmed-proposals",
- execute_on_network(
- &format!("{{ confirmed-id: u{} }}", ix + proposal_start_id),
- use_mainnet,
- ),
- execute_on_network(&value, use_mainnet),
- &epoch,
- )
- .unwrap();
- }
-}
-
-fn cost_voting_test_cost_definer(use_mainnet: bool) -> QualifiedContractIdentifier {
- let p1 = execute_on_network("'SZ2J6ZY48GV1EZ5V2V5RB9MP66SW86PYKKQ9H6DPR", use_mainnet);
- let Value::Principal(PrincipalData::Standard(p1_principal)) = p1 else {
- panic!("Expected a standard principal data");
- };
- QualifiedContractIdentifier::new(p1_principal, ContractName::from_literal("cost-definer"))
-}
-
-fn deploy_cost_voting_test_cost_definer(
- clarity_instance: &mut ClarityInstance,
- tip: &mut StacksBlockId,
- block_id_byte: &mut u8,
- use_mainnet: bool,
-) -> QualifiedContractIdentifier {
- let cost_definer = cost_voting_test_cost_definer(use_mainnet);
- let cost_definer_src = "
- (define-read-only (cost-definition-le (size uint))
- {
- runtime: u0, write_length: u0, write_count: u0, read_count: u0, read_length: u0
- })
- ";
- let burn_state_db = generate_test_burn_state_db(StacksEpochId::Epoch20);
- let next_block = next_test_block_id(block_id_byte);
- let mut block_conn =
- clarity_instance.begin_block(tip, &next_block, &TEST_HEADER_DB, &burn_state_db);
- block_conn.as_transaction(|tx| {
- let (ast, analysis) = tx
- .analyze_smart_contract(
- &cost_definer,
- ClarityVersion::Clarity1,
- cost_definer_src,
- &ResourceBudget::unlimited(),
- )
- .unwrap();
- tx.initialize_smart_contract(
- &cost_definer,
- ClarityVersion::Clarity1,
- &ast,
- cost_definer_src,
- None,
- |_, _| None,
- &ResourceBudget::unlimited(),
- )
- .unwrap();
- tx.save_analysis(&cost_definer, &analysis).unwrap();
- });
- block_conn.commit_block();
- *tip = next_block;
-
- cost_definer
-}
-
-fn write_voted_le_cost_state(
- clarity_instance: &mut ClarityInstance,
- tip: &mut StacksBlockId,
- block_id_byte: &mut u8,
- vote_state_epoch: StacksEpochId,
- use_mainnet: bool,
- cost_definer: &QualifiedContractIdentifier,
-) {
- let burn_state_db = generate_test_burn_state_db(vote_state_epoch);
- let next_block = next_test_block_id(block_id_byte);
-
- let mut clarity_conn =
- clarity_instance.begin_block(tip, &next_block, &TEST_HEADER_DB, &burn_state_db);
- clarity_conn.as_transaction(|tx| {
- tx.with_clarity_db(|db| {
- db.set_clarity_epoch_version(vote_state_epoch)?;
- write_confirmed_cost_proposals(
- db,
- use_mainnet,
- 0,
- 1,
- [cost_proposal(
- boot_code_id("costs", use_mainnet),
- "cost_le",
- cost_definer.clone(),
- "cost-definition-le",
- )],
- );
- Ok(())
- })
- .unwrap();
- });
- clarity_conn.commit_block();
- *tip = next_block;
-}
-
-fn tracker_with_voted_le_cost_state(
- load_epoch: StacksEpochId,
- vote_state_epoch: StacksEpochId,
- use_mainnet: bool,
-) -> (LimitedCostTracker, QualifiedContractIdentifier) {
- let (mut clarity_instance, mut tip, mut block_id_byte) =
- new_cost_test_clarity_instance(use_mainnet);
- let cost_definer = deploy_cost_voting_test_cost_definer(
- &mut clarity_instance,
- &mut tip,
- &mut block_id_byte,
- use_mainnet,
- );
-
- let max_setup_epoch = std::cmp::max(load_epoch, vote_state_epoch);
- let mut vote_state_written = false;
-
- for setup_epoch in TEST_TEST_COST_BOOT_EPOCHS {
- if !vote_state_written && vote_state_epoch < setup_epoch {
- write_voted_le_cost_state(
- &mut clarity_instance,
- &mut tip,
- &mut block_id_byte,
- vote_state_epoch,
- use_mainnet,
- &cost_definer,
- );
- vote_state_written = true;
- }
-
- if max_setup_epoch < setup_epoch {
- break;
- }
-
- setup_cost_test_epoch(
- &mut clarity_instance,
- &mut tip,
- &mut block_id_byte,
- setup_epoch,
- );
- }
-
- if !vote_state_written {
- write_voted_le_cost_state(
- &mut clarity_instance,
- &mut tip,
- &mut block_id_byte,
- vote_state_epoch,
- use_mainnet,
- &cost_definer,
- );
- }
-
- let mut marf_kv = clarity_instance.destroy();
-
- let burn_state_db = generate_test_burn_state_db(load_epoch);
- let final_block = next_test_block_id(&mut block_id_byte);
- let mut store = marf_kv.begin(&tip, &final_block);
- let owned_env = OwnedEnvironment::new_max_limit(
- store.as_clarity_db(&TEST_HEADER_DB, &burn_state_db),
- load_epoch,
- use_mainnet,
- );
- let (_db, tracker) = owned_env.destruct().unwrap();
-
- (tracker, cost_definer)
-}
-
fn exec_cost(contract: &str, use_mainnet: bool, epoch: StacksEpochId) -> ExecutionCost {
let p1 = execute("'SZ2J6ZY48GV1EZ5V2V5RB9MP66SW86PYKKQ9H6DPR");
let Value::Principal(PrincipalData::Standard(p1_principal)) = p1.clone() else {
@@ -1408,763 +1163,24 @@ fn epoch_33_test_all_testnet() {
epoch_33_test_all(false)
}
-fn test_cost_contract_short_circuits(use_mainnet: bool, clarity_version: ClarityVersion) {
- let marf_kv = MarfedKV::temporary();
- let chain_id = test_only_mainnet_to_chain_id(use_mainnet);
- let mut clarity_instance = ClarityInstance::new(use_mainnet, chain_id, marf_kv);
- let burn_db = if clarity_version == ClarityVersion::Clarity2 {
- &TEST_BURN_STATE_DB_21
- } else {
- &TEST_BURN_STATE_DB
- };
-
- clarity_instance
- .begin_test_genesis_block(
- &StacksBlockId::sentinel(),
- &StacksBlockId::new(&FIRST_BURNCHAIN_CONSENSUS_HASH, &FIRST_STACKS_BLOCK_HASH),
- &TEST_HEADER_DB,
- &TEST_BURN_STATE_DB,
- )
- .commit_block();
-
- // For Clarity2 tests we need to reach Epoch21 so costs-3 is deployed.
- // Each transition block opens under the *previous* epoch.
- let tip = if clarity_version == ClarityVersion::Clarity2 {
- let tip = StacksBlockId::new(&FIRST_BURNCHAIN_CONSENSUS_HASH, &FIRST_STACKS_BLOCK_HASH);
- let next = StacksBlockId([0xfd; 32]);
- let mut conn =
- clarity_instance.begin_block(&tip, &next, &TEST_HEADER_DB, &TEST_BURN_STATE_DB);
- conn.initialize_epoch_2_05().unwrap();
- conn.commit_block();
-
- let tip = next;
- let next = StacksBlockId([0xfe; 32]);
- let mut conn =
- clarity_instance.begin_block(&tip, &next, &TEST_HEADER_DB, &TEST_BURN_STATE_DB_205);
- conn.initialize_epoch_2_1().unwrap();
- conn.commit_block();
- next
- } else {
- StacksBlockId::new(&FIRST_BURNCHAIN_CONSENSUS_HASH, &FIRST_STACKS_BLOCK_HASH)
- };
-
- let marf_kv = clarity_instance.destroy();
-
- let p1 = execute_on_network("'SZ2J6ZY48GV1EZ5V2V5RB9MP66SW86PYKKQ9H6DPR", use_mainnet);
- let p2 = execute_on_network("'SM2J6ZY48GV1EZ5V2V5RB9MP66SW86PYKKQVX8X0G", use_mainnet);
-
- let Value::Principal(PrincipalData::Standard(p1_principal)) = p1.clone() else {
- panic!("Expected a standard principal data");
- };
-
- let Value::Principal(p2_principal) = p2.clone() else {
- panic!("Expected a principal data");
- };
-
- let cost_definer = QualifiedContractIdentifier::new(
- p1_principal.clone(),
- ContractName::from_literal("cost-definer"),
- );
- let intercepted = QualifiedContractIdentifier::new(
- p1_principal.clone(),
- ContractName::from_literal("intercepted"),
- );
- let caller =
- QualifiedContractIdentifier::new(p1_principal, ContractName::from_literal("caller"));
-
- let mut marf_kv = {
- let mut clarity_inst = ClarityInstance::new(use_mainnet, chain_id, marf_kv);
- let mut block_conn =
- clarity_inst.begin_block(&tip, &StacksBlockId([1; 32]), &TEST_HEADER_DB, burn_db);
-
- let cost_definer_src = "
- (define-read-only (cost-definition (size uint))
- {
- runtime: u1, write_length: u1, write_count: u1, read_count: u1, read_length: u1
- })
- ";
-
- let intercepted_src = "
- (define-read-only (intercepted-function (a uint))
- (if (>= a u10)
- (+ (+ a a) (+ a a)
- (+ a a) (+ a a))
- u0))
- ";
-
- let caller_src = "
- (define-public (execute (a uint))
- (ok (contract-call? .intercepted intercepted-function a)))
- ";
-
- for (contract_name, contract_src) in [
- (&cost_definer, cost_definer_src),
- (&intercepted, intercepted_src),
- (&caller, caller_src),
- ]
- .iter()
- {
- block_conn.as_transaction(|tx| {
- let (ast, analysis) = tx
- .analyze_smart_contract(
- contract_name,
- clarity_version,
- contract_src,
- &ResourceBudget::unlimited(),
- )
- .unwrap();
- tx.initialize_smart_contract(
- contract_name,
- clarity_version,
- &ast,
- contract_src,
- None,
- |_, _| None,
- &ResourceBudget::unlimited(),
- )
- .unwrap();
- tx.save_analysis(contract_name, &analysis).unwrap();
- });
- }
-
- block_conn.commit_block();
- clarity_inst.destroy()
- };
-
- let without_interposing_5 = {
- let mut store = marf_kv.begin(&StacksBlockId([1; 32]), &StacksBlockId([2; 32]));
- let mut owned_env = OwnedEnvironment::new_max_limit(
- store.as_clarity_db(&TEST_HEADER_DB, burn_db),
- StacksEpochId::Epoch20,
- use_mainnet,
- );
-
- execute_transaction(
- &mut owned_env,
- p2_principal.clone(),
- &caller,
- "execute",
- &symbols_from_values(vec![Value::UInt(5)]),
- )
- .unwrap();
-
- let (_db, tracker) = owned_env.destruct().unwrap();
-
- store.test_commit();
- tracker.get_total()
- };
-
- let without_interposing_10 = {
- let mut store = marf_kv.begin(&StacksBlockId([2; 32]), &StacksBlockId([3; 32]));
- let mut owned_env = OwnedEnvironment::new_max_limit(
- store.as_clarity_db(&TEST_HEADER_DB, burn_db),
- StacksEpochId::Epoch20,
- use_mainnet,
- );
-
- execute_transaction(
- &mut owned_env,
- p2_principal.clone(),
- &caller,
- "execute",
- &symbols_from_values(vec![Value::UInt(10)]),
- )
- .unwrap();
-
- let (_db, tracker) = owned_env.destruct().unwrap();
-
- store.test_commit();
- tracker.get_total()
- };
-
- let voting_contract_to_use: &QualifiedContractIdentifier = if use_mainnet {
- &COST_VOTING_MAINNET_CONTRACT
- } else {
- &COST_VOTING_TESTNET_CONTRACT
- };
-
- {
- let mut store = marf_kv.begin(&StacksBlockId([3; 32]), &StacksBlockId([4; 32]));
- let mut db = store.as_clarity_db(&TEST_HEADER_DB, burn_db);
- db.begin();
- db.set_variable_unknown_descriptor(
- voting_contract_to_use,
- "confirmed-proposal-count",
- Value::UInt(1),
- )
- .unwrap();
- let value = format!(
- "{{ function-contract: '{},
- function-name: {},
- cost-function-contract: '{},
- cost-function-name: {},
- confirmed-height: u1 }}",
- intercepted, "\"intercepted-function\"", cost_definer, "\"cost-definition\""
- );
- let epoch = db.get_clarity_epoch_version().unwrap();
- db.set_entry_unknown_descriptor(
- voting_contract_to_use,
- "confirmed-proposals",
- execute_on_network("{ confirmed-id: u0 }", use_mainnet),
- execute_on_network(&value, use_mainnet),
- &epoch,
- )
- .unwrap();
- db.commit().unwrap();
- store.test_commit();
- }
-
- let with_interposing_5 = {
- let mut store = marf_kv.begin(&StacksBlockId([4; 32]), &StacksBlockId([5; 32]));
-
- let mut owned_env = OwnedEnvironment::new_max_limit(
- store.as_clarity_db(&TEST_HEADER_DB, burn_db),
- StacksEpochId::Epoch20,
- use_mainnet,
- );
-
- execute_transaction(
- &mut owned_env,
- p2_principal.clone(),
- &caller,
- "execute",
- &symbols_from_values(vec![Value::UInt(5)]),
- )
- .unwrap();
-
- let (_db, tracker) = owned_env.destruct().unwrap();
-
- store.test_commit();
- tracker.get_total()
- };
-
- let with_interposing_10 = {
- let mut store = marf_kv.begin(&StacksBlockId([5; 32]), &StacksBlockId([6; 32]));
- let mut owned_env = OwnedEnvironment::new_max_limit(
- store.as_clarity_db(&TEST_HEADER_DB, burn_db),
- StacksEpochId::Epoch20,
- use_mainnet,
- );
-
- execute_transaction(
- &mut owned_env,
- p2_principal,
- &caller,
- "execute",
- &symbols_from_values(vec![Value::UInt(10)]),
- )
- .unwrap();
-
- let (_db, tracker) = owned_env.destruct().unwrap();
-
- tracker.get_total()
- };
-
- assert!(without_interposing_5.exceeds(&with_interposing_5));
- assert!(without_interposing_10.exceeds(&with_interposing_10));
-
- assert_eq!(with_interposing_5, with_interposing_10);
- assert!(without_interposing_5 != without_interposing_10);
-}
-
-#[test]
-fn test_cost_contract_short_circuits_mainnet() {
- test_cost_contract_short_circuits(true, ClarityVersion::Clarity1);
- test_cost_contract_short_circuits(true, ClarityVersion::Clarity2);
-}
-
-#[test]
-fn test_cost_contract_short_circuits_testnet() {
- test_cost_contract_short_circuits(false, ClarityVersion::Clarity1);
- test_cost_contract_short_circuits(false, ClarityVersion::Clarity2);
-}
-
-fn test_cost_voting_integration(use_mainnet: bool, clarity_version: ClarityVersion) {
- let marf_kv = MarfedKV::temporary();
- let chain_id = test_only_mainnet_to_chain_id(use_mainnet);
- let mut clarity_instance = ClarityInstance::new(use_mainnet, chain_id, marf_kv);
- let burn_db = if clarity_version == ClarityVersion::Clarity2 {
- &TEST_BURN_STATE_DB_21
- } else {
- &TEST_BURN_STATE_DB
- };
-
- clarity_instance
- .begin_test_genesis_block(
- &StacksBlockId::sentinel(),
- &StacksBlockId::new(&FIRST_BURNCHAIN_CONSENSUS_HASH, &FIRST_STACKS_BLOCK_HASH),
- &TEST_HEADER_DB,
- &TEST_BURN_STATE_DB,
- )
- .commit_block();
-
- // For Clarity2 tests we need to reach Epoch21 so costs-3 is deployed.
- // Each transition block opens under the previous epoch.
- let tip = if clarity_version == ClarityVersion::Clarity2 {
- let tip = StacksBlockId::new(&FIRST_BURNCHAIN_CONSENSUS_HASH, &FIRST_STACKS_BLOCK_HASH);
- let next = StacksBlockId([0xfd; 32]);
- let mut conn =
- clarity_instance.begin_block(&tip, &next, &TEST_HEADER_DB, &TEST_BURN_STATE_DB);
- conn.initialize_epoch_2_05().unwrap();
- conn.commit_block();
-
- let tip = next;
- let next = StacksBlockId([0xfe; 32]);
- let mut conn =
- clarity_instance.begin_block(&tip, &next, &TEST_HEADER_DB, &TEST_BURN_STATE_DB_205);
- conn.initialize_epoch_2_1().unwrap();
- conn.commit_block();
- next
- } else {
- StacksBlockId::new(&FIRST_BURNCHAIN_CONSENSUS_HASH, &FIRST_STACKS_BLOCK_HASH)
- };
-
- let marf_kv = clarity_instance.destroy();
-
- let p1 = execute("'SZ2J6ZY48GV1EZ5V2V5RB9MP66SW86PYKKQ9H6DPR");
- let p2 = execute("'SM2J6ZY48GV1EZ5V2V5RB9MP66SW86PYKKQVX8X0G");
-
- let Value::Principal(PrincipalData::Standard(p1_principal)) = p1.clone() else {
- panic!("Expected a standard principal data");
- };
-
- let Value::Principal(p2_principal) = p2.clone() else {
- panic!("Expected a principal data");
- };
-
- let cost_definer = QualifiedContractIdentifier::new(
- p1_principal.clone(),
- ContractName::from_literal("cost-definer"),
- );
- let bad_cost_definer = QualifiedContractIdentifier::new(
- p1_principal.clone(),
- ContractName::from_literal("bad-cost-definer"),
- );
- let bad_cost_args_definer = QualifiedContractIdentifier::new(
- p1_principal.clone(),
- ContractName::from_literal("bad-cost-args-definer"),
- );
- let intercepted = QualifiedContractIdentifier::new(
- p1_principal.clone(),
- ContractName::from_literal("intercepted"),
- );
- let caller = QualifiedContractIdentifier::new(
- p1_principal.clone(),
- ContractName::from_literal("caller"),
- );
-
- let mut marf_kv = {
- let mut clarity_inst = ClarityInstance::new(use_mainnet, chain_id, marf_kv);
- let mut block_conn =
- clarity_inst.begin_block(&tip, &StacksBlockId([1; 32]), &TEST_HEADER_DB, burn_db);
-
- let cost_definer_src = "
- (define-read-only (cost-definition (size uint))
- {
- runtime: u1, write_length: u1, write_count: u1, read_count: u1, read_length: u1
- })
- (define-read-only (cost-definition-le (size uint))
- {
- runtime: u0, write_length: u0, write_count: u0, read_count: u0, read_length: u0
- })
- (define-read-only (cost-definition-multi-arg (a uint) (b uint) (c uint))
- {
- runtime: u1, write_length: u0, write_count: u0, read_count: u0, read_length: u0
- })
-
- ";
-
- let bad_cost_definer_src = "
- (define-data-var my-var uint u10)
- (define-read-only (cost-definition (size uint))
- {
- runtime: (var-get my-var), write_length: u1, write_count: u1, read_count: u1, read_length: u1
- })
- ";
-
- let bad_cost_args_definer_src = "
- (define-read-only (cost-definition (a uint) (b uint))
- {
- runtime: u1, write_length: u1, write_count: u1, read_count: u1, read_length: u1
- })
- ";
-
- let intercepted_src = "
- (define-read-only (intercepted-function (a uint))
- (if (>= a u10)
- (+ (+ a a) (+ a a)
- (+ a a) (+ a a))
- u0))
-
- (define-read-only (intercepted-function2 (a uint) (b uint) (c uint))
- (- (+ a b) c))
-
- (define-public (non-read-only) (ok (+ 1 2 3)))
- ";
-
- let caller_src = "
- (define-public (execute (a uint))
- (ok (contract-call? .intercepted intercepted-function a)))
- (define-public (execute-2 (a uint))
- (ok (< a a)))
- ";
-
- for (contract_name, contract_src) in [
- (&cost_definer, cost_definer_src),
- (&intercepted, intercepted_src),
- (&caller, caller_src),
- (&bad_cost_definer, bad_cost_definer_src),
- (&bad_cost_args_definer, bad_cost_args_definer_src),
- ]
- .iter()
- {
- block_conn.as_transaction(|tx| {
- let (ast, analysis) = tx
- .analyze_smart_contract(
- contract_name,
- clarity_version,
- contract_src,
- &ResourceBudget::unlimited(),
- )
- .unwrap();
- tx.initialize_smart_contract(
- contract_name,
- clarity_version,
- &ast,
- contract_src,
- None,
- |_, _| None,
- &ResourceBudget::unlimited(),
- )
- .unwrap();
- tx.save_analysis(contract_name, &analysis).unwrap();
- });
- }
-
- block_conn.commit_block();
- clarity_inst.destroy()
- };
-
- let bad_cases = vec![
- // non existent "replacement target"
- cost_proposal(
- QualifiedContractIdentifier::local("non-existent").unwrap(),
- "non-existent-func",
- cost_definer.clone(),
- "cost-definition",
- ),
- // replacement target isn't a contract principal
- cost_proposal(
- p1_principal.clone(),
- "non-existent-func",
- cost_definer.clone(),
- "cost-definition",
- ),
- // cost defining contract isn't a contract principal
- cost_proposal(
- intercepted.clone(),
- "intercepted-function",
- p1_principal,
- "cost-definition",
- ),
- // replacement function doesn't exist
- cost_proposal(
- intercepted.clone(),
- "non-existent-func",
- cost_definer.clone(),
- "cost-definition",
- ),
- // replacement function isn't read-only
- cost_proposal(
- intercepted.clone(),
- "non-read-only",
- cost_definer.clone(),
- "cost-definition",
- ),
- // "boot cost" function doesn't exist
- cost_proposal(
- boot_code_id("costs", false),
- "non-existent-func",
- cost_definer.clone(),
- "cost-definition",
- ),
- // cost defining contract doesn't exist
- cost_proposal(
- intercepted.clone(),
- "intercepted-function",
- QualifiedContractIdentifier::local("non-existent").unwrap(),
- "cost-definition",
- ),
- // cost defining function doesn't exist
- cost_proposal(
- intercepted.clone(),
- "intercepted-function",
- cost_definer.clone(),
- "cost-definition-2",
- ),
- // cost defining contract isn't arithmetic-only
- cost_proposal(
- intercepted.clone(),
- "intercepted-function",
- bad_cost_definer,
- "cost-definition",
- ),
- // cost defining contract has incorrect number of arguments
- cost_proposal(
- intercepted.clone(),
- "intercepted-function",
- bad_cost_args_definer,
- "cost-definition",
- ),
- ];
-
- let bad_proposals = bad_cases.len();
-
- {
- let mut store = marf_kv.begin(&StacksBlockId([1; 32]), &StacksBlockId([2; 32]));
-
- let mut db = store.as_clarity_db(&TEST_HEADER_DB, burn_db);
- db.begin();
- write_confirmed_cost_proposals(&mut db, use_mainnet, 0, bad_proposals, bad_cases);
- db.commit().unwrap();
- store.test_commit();
- }
-
- let le_cost_without_interception = {
- let mut store = marf_kv.begin(&StacksBlockId([2; 32]), &StacksBlockId([3; 32]));
- let mut owned_env = OwnedEnvironment::new_max_limit(
- store.as_clarity_db(&TEST_HEADER_DB, burn_db),
- StacksEpochId::Epoch20,
- use_mainnet,
- );
-
- execute_transaction(
- &mut owned_env,
- p2_principal.clone(),
- &caller,
- "execute-2",
- &symbols_from_values(vec![Value::UInt(5)]),
- )
- .unwrap();
-
- let (_db, tracker) = owned_env.destruct().unwrap();
-
- assert!(
- tracker.contract_call_circuits().is_empty(),
- "No contract call circuits should have been processed"
- );
- // Matches the `burn_db` selection above: Clarity1 uses Epoch20
- // (`costs` / Costs1), Clarity2 uses Epoch21 (`costs-3` / Costs3).
- let expected_default = if clarity_version == ClarityVersion::Clarity2 {
- DefaultVersion::Costs3
- } else {
- DefaultVersion::Costs1
- };
- for (target, referenced_function) in tracker.cost_function_references().into_iter() {
- assert!(
- matches!(
- referenced_function,
- ClarityCostFunctionEvaluator::Default(_, _, v) if v == expected_default
- ),
- "All cost functions should still point to the boot costs"
- );
- }
- store.test_commit();
-
- tracker.get_total()
- };
-
- let good_cases = vec![
- cost_proposal(
- intercepted.clone(),
- "intercepted-function",
- cost_definer.clone(),
- "cost-definition",
- ),
- cost_proposal(
- boot_code_id("costs", use_mainnet),
- "cost_le",
- cost_definer.clone(),
- "cost-definition-le",
- ),
- cost_proposal(
- intercepted.clone(),
- "intercepted-function2",
- cost_definer.clone(),
- "cost-definition-multi-arg",
- ),
- ];
-
- {
- let mut store = marf_kv.begin(&StacksBlockId([3; 32]), &StacksBlockId([4; 32]));
-
- let mut db = store.as_clarity_db(&TEST_HEADER_DB, burn_db);
- db.begin();
-
- let good_proposals = good_cases.len();
- write_confirmed_cost_proposals(
- &mut db,
- use_mainnet,
- bad_proposals,
- bad_proposals + good_proposals,
- good_cases,
- );
- db.commit().unwrap();
-
- store.test_commit();
- }
-
- {
- // Matches the `burn_db` selection above: Clarity1 uses Epoch20
- // (`costs` / Costs1), Clarity2 uses Epoch21 (`costs-3` / Costs3).
- let expected_default = if clarity_version == ClarityVersion::Clarity2 {
- DefaultVersion::Costs3
- } else {
- DefaultVersion::Costs1
- };
- let mut store = marf_kv.begin(&StacksBlockId([4; 32]), &StacksBlockId([5; 32]));
- let mut owned_env = OwnedEnvironment::new_max_limit(
- store.as_clarity_db(&TEST_HEADER_DB, burn_db),
- StacksEpochId::Epoch20,
- use_mainnet,
- );
-
- execute_transaction(
- &mut owned_env,
- p2_principal,
- &caller,
- "execute-2",
- &symbols_from_values(vec![Value::UInt(5)]),
- )
- .unwrap();
-
- let (_db, tracker) = owned_env.destruct().unwrap();
-
- // cost of `le` should be less now, because the proposal made it free
- assert!(le_cost_without_interception.exceeds(&tracker.get_total()));
-
- let circuits = tracker.contract_call_circuits();
- assert_eq!(circuits.len(), 2);
-
- let circuit1 = circuits.get(&(
- intercepted.clone(),
- ClarityName::from_literal("intercepted-function"),
- ));
- let circuit2 = circuits.get(&(
- intercepted,
- ClarityName::from_literal("intercepted-function2"),
- ));
-
- assert!(circuit1.is_some());
- assert!(circuit2.is_some());
-
- assert_eq!(circuit1.unwrap().contract_id, cost_definer);
- assert_eq!(circuit1.unwrap().function_name, "cost-definition");
-
- assert_eq!(circuit2.unwrap().contract_id, cost_definer);
- assert_eq!(circuit2.unwrap().function_name, "cost-definition-multi-arg");
-
- for (target, referenced_function) in tracker.cost_function_references().into_iter() {
- if target == &ClarityCostFunction::Le {
- let ClarityCostFunctionEvaluator::Clarity(referenced_function) =
- referenced_function
- else {
- panic!("Replaced function should be evaluated in Clarity");
- };
- assert_eq!(&referenced_function.contract_id, &cost_definer);
- assert_eq!(&referenced_function.function_name, "cost-definition-le");
- } else {
- assert!(
- matches!(
- referenced_function,
- ClarityCostFunctionEvaluator::Default(_, _, v) if v == expected_default
- ),
- "Cost function should still point to the boot costs"
- );
- }
- }
- store.test_commit();
- };
-}
-
-#[test]
-fn test_cost_voting_integration_mainnet() {
- test_cost_voting_integration(true, ClarityVersion::Clarity1);
- test_cost_voting_integration(true, ClarityVersion::Clarity2);
-}
-
-#[test]
-fn test_cost_voting_integration_testnet() {
- test_cost_voting_integration(false, ClarityVersion::Clarity1);
- test_cost_voting_integration(false, ClarityVersion::Clarity2);
-}
-
-/// Before epoch 4.0, confirmed `.cost-voting` state can replace boot cost functions.
+/// Epoch 4.0 uses native Rust costs without a deployed `costs-5` contract.
#[rstest::rstest]
#[case::mainnet(true)]
#[case::testnet(false)]
-fn cost_voting_state_applies_before_epoch_40(#[case] use_mainnet: bool) {
- let (tracker, cost_definer) = tracker_with_voted_le_cost_state(
- StacksEpochId::Epoch34,
- StacksEpochId::Epoch34,
- use_mainnet,
- );
-
- let cost_function_references = tracker.cost_function_references();
- assert_eq!(
- cost_function_references.len(),
- ClarityCostFunction::ALL.len(),
- "all cost functions must have a reference before cost-voting retires (mainnet={use_mainnet})"
- );
-
- let le_reference = cost_function_references
- .get(&ClarityCostFunction::Le)
- .expect("cost_le must have a reference");
- let ClarityCostFunctionEvaluator::Clarity(le_reference) = le_reference else {
- panic!("voted cost_le should be evaluated in Clarity before Epoch 4.0");
- };
- assert_eq!(&le_reference.contract_id, &cost_definer);
- assert_eq!(&le_reference.function_name, "cost-definition-le");
-}
-
-/// With `.cost-voting` disabled >= epoch 4.0, both existing and newly written voting state is
-/// ignored and every cost function resolves to the boot cost contract.
-#[rstest::rstest]
-#[case::mainnet_pre_40_voting_state(true, StacksEpochId::Epoch34)]
-#[case::testnet_pre_40_voting_state(false, StacksEpochId::Epoch34)]
-#[case::mainnet_post_40_voting_state(true, StacksEpochId::Epoch40)]
-#[case::testnet_post_40_voting_state(false, StacksEpochId::Epoch40)]
-fn cost_voting_state_is_ignored_at_epoch_40(
- #[case] use_mainnet: bool,
- #[case] vote_state_epoch: StacksEpochId,
-) {
- let (tracker, _cost_definer) =
- tracker_with_voted_le_cost_state(StacksEpochId::Epoch40, vote_state_epoch, use_mainnet);
+fn epoch_40_uses_native_costs(#[case] use_mainnet: bool) {
+ with_owned_env(StacksEpochId::Epoch40, use_mainnet, |owned_env| {
+ let (_db, tracker) = owned_env.destruct().unwrap();
+ let cost_function_references = tracker.cost_function_references();
- let cost_function_references = tracker.cost_function_references();
- assert_eq!(
- cost_function_references.len(),
- ClarityCostFunction::ALL.len(),
- "all cost functions must have a reference after cost-voting retires (mainnet={use_mainnet}, vote_state_epoch={vote_state_epoch})"
- );
- assert!(
- cost_function_references.values().all(|evaluator| matches!(
+ assert_eq!(
+ cost_function_references.len(),
+ ClarityCostFunction::ALL.len()
+ );
+ assert!(cost_function_references.values().all(|evaluator| matches!(
evaluator,
ClarityCostFunctionEvaluator::Default(_, _, DefaultVersion::Costs5)
- )),
- "all cost functions must use the epoch 4.0 boot cost contract after cost-voting retires (mainnet={use_mainnet}, vote_state_epoch={vote_state_epoch})"
- );
-
- let le_reference = cost_function_references
- .get(&ClarityCostFunction::Le)
- .expect("cost_le must have a reference");
- assert!(
- matches!(
- le_reference,
- ClarityCostFunctionEvaluator::Default(_, _, DefaultVersion::Costs5)
- ),
- "voted cost_le replacement must be ignored after cost-voting retires (mainnet={use_mainnet}, vote_state_epoch={vote_state_epoch})"
- );
+ )));
+ });
}
#[test]