Skip to content

Commit 9ad6fde

Browse files
Merge remote-tracking branch 'upstream/develop' into feat/stacks-profiler
2 parents c162d88 + af1280a commit 9ad6fde

32 files changed

Lines changed: 4194 additions & 3969 deletions

File tree

Cargo.lock

Lines changed: 7 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Moved `StacksTransaction` and its supporting types (`TransactionAuth`, `*SpendingCondition`, `TransactionPayload`, `ClarityVersion`, `StacksMicroblockHeader`, `Txid`, `StacksString`, `TenureChangePayload`, and related) from `stackslib` into the `stacks-codec` crate ([#7200](https://github.com/stacks-network/stacks-core/pull/7200))
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Added documentation for the arithmetic checker

clarity-types/src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,11 +27,13 @@ pub use stacks_common::{
2727
pub mod errors;
2828
pub mod representations;
2929
pub mod types;
30+
pub mod version;
3031

3132
pub use errors::{ClarityTypeError, IncomparableError};
3233
pub use representations::{ClarityName, ContractName};
3334
use stacks_common::types::StacksEpochId;
3435
pub use types::Value;
36+
pub use version::ClarityVersion;
3537

3638
/// Max call stack depth for Epoch 3.4+.
3739
const MAX_CALL_STACK_DEPTH: u64 = 128;

clarity-types/src/version.rs

Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
// Copyright (C) 2026 Stacks Open Internet Foundation
2+
//
3+
// This program is free software: you can redistribute it and/or modify
4+
// it under the terms of the GNU General Public License as published by
5+
// the Free Software Foundation, either version 3 of the License, or
6+
// (at your option) any later version.
7+
//
8+
// This program is distributed in the hope that it will be useful,
9+
// but WITHOUT ANY WARRANTY; without even the implied warranty of
10+
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+
// GNU General Public License for more details.
12+
//
13+
// You should have received a copy of the GNU General Public License
14+
// along with this program. If not, see <http://www.gnu.org/licenses/>.
15+
16+
use std::fmt;
17+
use std::str::FromStr;
18+
19+
use stacks_common::types::StacksEpochId;
20+
21+
#[derive(Serialize, Deserialize, Clone, Copy, Debug, PartialEq, PartialOrd)]
22+
pub enum ClarityVersion {
23+
Clarity1,
24+
Clarity2,
25+
Clarity3,
26+
Clarity4,
27+
Clarity5,
28+
}
29+
30+
impl fmt::Display for ClarityVersion {
31+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
32+
match self {
33+
ClarityVersion::Clarity1 => write!(f, "Clarity 1"),
34+
ClarityVersion::Clarity2 => write!(f, "Clarity 2"),
35+
ClarityVersion::Clarity3 => write!(f, "Clarity 3"),
36+
ClarityVersion::Clarity4 => write!(f, "Clarity 4"),
37+
ClarityVersion::Clarity5 => write!(f, "Clarity 5"),
38+
}
39+
}
40+
}
41+
42+
impl ClarityVersion {
43+
pub const fn latest() -> ClarityVersion {
44+
ClarityVersion::Clarity5
45+
}
46+
47+
pub const ALL: &'static [ClarityVersion] = &[
48+
ClarityVersion::Clarity1,
49+
ClarityVersion::Clarity2,
50+
ClarityVersion::Clarity3,
51+
ClarityVersion::Clarity4,
52+
ClarityVersion::Clarity5,
53+
];
54+
55+
/// Returns all [`ClarityVersion`] starting from the given `version` (inclusive)
56+
#[cfg(any(test, feature = "testing"))]
57+
pub fn since(version: ClarityVersion) -> &'static [ClarityVersion] {
58+
let idx = Self::ALL
59+
.iter()
60+
.position(|&v| v == version)
61+
.expect("version not found in ALL");
62+
63+
&Self::ALL[idx..]
64+
}
65+
66+
/// Returns all [`ClarityVersion`] up to the given `version` (inclusive)
67+
#[cfg(any(test, feature = "testing"))]
68+
pub fn up_to(version: ClarityVersion) -> &'static [ClarityVersion] {
69+
let idx = Self::ALL
70+
.iter()
71+
.position(|&v| v == version)
72+
.expect("version not found in ALL");
73+
74+
&Self::ALL[..=idx]
75+
}
76+
77+
pub fn default_for_epoch(epoch_id: StacksEpochId) -> ClarityVersion {
78+
match epoch_id {
79+
// Clarity does not exist in Epoch 1.0; callers that hit this branch are buggy.
80+
StacksEpochId::Epoch10 => ClarityVersion::Clarity1,
81+
StacksEpochId::Epoch20 => ClarityVersion::Clarity1,
82+
StacksEpochId::Epoch2_05 => ClarityVersion::Clarity1,
83+
StacksEpochId::Epoch21 => ClarityVersion::Clarity2,
84+
StacksEpochId::Epoch22 => ClarityVersion::Clarity2,
85+
StacksEpochId::Epoch23 => ClarityVersion::Clarity2,
86+
StacksEpochId::Epoch24 => ClarityVersion::Clarity2,
87+
StacksEpochId::Epoch25 => ClarityVersion::Clarity2,
88+
StacksEpochId::Epoch30 => ClarityVersion::Clarity3,
89+
StacksEpochId::Epoch31 => ClarityVersion::Clarity3,
90+
StacksEpochId::Epoch32 => ClarityVersion::Clarity3,
91+
StacksEpochId::Epoch33 => ClarityVersion::Clarity4,
92+
StacksEpochId::Epoch34 => ClarityVersion::Clarity5,
93+
}
94+
}
95+
96+
pub fn supports_callables(&self) -> bool {
97+
match self {
98+
ClarityVersion::Clarity1 => false,
99+
ClarityVersion::Clarity2
100+
| ClarityVersion::Clarity3
101+
| ClarityVersion::Clarity4
102+
| ClarityVersion::Clarity5 => true,
103+
}
104+
}
105+
106+
pub fn uses_secp256r1_double_hashing(&self) -> bool {
107+
match self {
108+
ClarityVersion::Clarity1
109+
| ClarityVersion::Clarity2
110+
| ClarityVersion::Clarity3
111+
| ClarityVersion::Clarity4 => true,
112+
ClarityVersion::Clarity5 => false,
113+
}
114+
}
115+
116+
/// Beginning in Clarity 5, cost functions that call `logn` are ensured to
117+
/// always pass an argument greater than zero, to avoid hitting a runtime
118+
/// error during cost computation. After reviewing the usage, the only
119+
/// function that requires this protection is `from-consensus-buff?`, other
120+
/// cost functions that call `logn` are already protected from zeros.
121+
pub fn protects_logn_cost_fn(&self) -> bool {
122+
match self {
123+
ClarityVersion::Clarity1
124+
| ClarityVersion::Clarity2
125+
| ClarityVersion::Clarity3
126+
| ClarityVersion::Clarity4 => false,
127+
ClarityVersion::Clarity5 => true,
128+
}
129+
}
130+
}
131+
132+
impl FromStr for ClarityVersion {
133+
type Err = &'static str;
134+
135+
fn from_str(version: &str) -> Result<ClarityVersion, &'static str> {
136+
let s = version.to_string().to_lowercase();
137+
if s == "clarity1" {
138+
Ok(ClarityVersion::Clarity1)
139+
} else if s == "clarity2" {
140+
Ok(ClarityVersion::Clarity2)
141+
} else if s == "clarity3" {
142+
Ok(ClarityVersion::Clarity3)
143+
} else if s == "clarity4" {
144+
Ok(ClarityVersion::Clarity4)
145+
} else if s == "clarity5" {
146+
Ok(ClarityVersion::Clarity5)
147+
} else {
148+
Err(
149+
"Invalid clarity version. Valid versions are: Clarity1, Clarity2, Clarity3, Clarity4, Clarity5.",
150+
)
151+
}
152+
}
153+
}

clarity/fuzz/Cargo.lock

Lines changed: 0 additions & 5 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

clarity/src/vm/analysis/arithmetic_checker/mod.rs

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,34 @@
1414
// You should have received a copy of the GNU General Public License
1515
// along with this program. If not, see <http://www.gnu.org/licenses/>.
1616

17+
//! Static-analysis pass that decides whether a contract is eligible to be
18+
//! installed as a *cost-function* contract under the SIP-006 cost-voting
19+
//! system.
20+
//!
21+
//! A cost-function contract supplies a Clarity function that the VM invokes
22+
//! on every call to a native operation in order to compute that operation's
23+
//! runtime/read/write cost. Because such functions run inside the cost
24+
//! accounting path itself, they must be:
25+
//!
26+
//! - **Deterministic**: no chain-state reads (`block-height`, `tx-sender`,
27+
//! `contract-caller`, `chain-id`, ...), no `contract-call?`.
28+
//! - **Side-effect free**: no map/var/FT/NFT defines or mutations, no STX
29+
//! transfers, no asset mints or burns, no `print`.
30+
//! - **Bounded**: no `map`/`fold`/`filter`/list-cons or other iterating
31+
//! constructs whose work scales with input size — the cost function must
32+
//! itself be cheap and predictable.
33+
//! - **Trait-free**: traits introduce dynamic dispatch that the cost
34+
//! accountant cannot reason about statically.
35+
//! - **Cheap**: only a restricted, predictable, constant-time subset of
36+
//! native forms is allowed, including arithmetic/logic and certain simple
37+
//! expression-building constructs.
38+
//!
39+
//! The pass walks every top-level form and expression and rejects anything
40+
//! outside the set of allowed constructs. The result is stored in
41+
//! [`ContractAnalysis::is_cost_contract_eligible`]; the cost-voting
42+
//! machinery in `vm::costs` later refuses to adopt a proposal whose target
43+
//! contract is not eligible.
44+
1745
use clarity_types::representations::ClarityName;
1846

1947
pub use super::errors::{

0 commit comments

Comments
 (0)