Skip to content

Commit af1280a

Browse files
Merge pull request #7200 from jbencin-stacks/refactor/stacks-codec-stacks-transaction
refactor: Move `StacksTransaction` into `stacks-codec`
2 parents 2ea410a + fb59fea commit af1280a

30 files changed

Lines changed: 4165 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))

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/version.rs

Lines changed: 5 additions & 141 deletions
Original file line numberDiff line numberDiff line change
@@ -12,145 +12,9 @@
1212
//
1313
// You should have received a copy of the GNU General Public License
1414
// along with this program. If not, see <http://www.gnu.org/licenses/>.
15-
use std::fmt;
16-
use std::str::FromStr;
1715

18-
use stacks_common::types::StacksEpochId;
19-
20-
#[derive(Serialize, Deserialize, Clone, Copy, Debug, PartialEq, PartialOrd)]
21-
pub enum ClarityVersion {
22-
Clarity1,
23-
Clarity2,
24-
Clarity3,
25-
Clarity4,
26-
Clarity5,
27-
}
28-
29-
impl fmt::Display for ClarityVersion {
30-
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
31-
match self {
32-
ClarityVersion::Clarity1 => write!(f, "Clarity 1"),
33-
ClarityVersion::Clarity2 => write!(f, "Clarity 2"),
34-
ClarityVersion::Clarity3 => write!(f, "Clarity 3"),
35-
ClarityVersion::Clarity4 => write!(f, "Clarity 4"),
36-
ClarityVersion::Clarity5 => write!(f, "Clarity 5"),
37-
}
38-
}
39-
}
40-
41-
impl ClarityVersion {
42-
pub const fn latest() -> ClarityVersion {
43-
ClarityVersion::Clarity5
44-
}
45-
46-
pub const ALL: &'static [ClarityVersion] = &[
47-
ClarityVersion::Clarity1,
48-
ClarityVersion::Clarity2,
49-
ClarityVersion::Clarity3,
50-
ClarityVersion::Clarity4,
51-
ClarityVersion::Clarity5,
52-
];
53-
54-
/// Returns all [`ClarityVersion`] starting from the given `version` (inclusive)
55-
#[cfg(any(test, feature = "testing"))]
56-
pub fn since(version: ClarityVersion) -> &'static [ClarityVersion] {
57-
let idx = Self::ALL
58-
.iter()
59-
.position(|&v| v == version)
60-
.expect("version not found in ALL");
61-
62-
&Self::ALL[idx..]
63-
}
64-
65-
/// Returns all [`ClarityVersion`] up to the given `version` (inclusive)
66-
#[cfg(any(test, feature = "testing"))]
67-
pub fn up_to(version: ClarityVersion) -> &'static [ClarityVersion] {
68-
let idx = Self::ALL
69-
.iter()
70-
.position(|&v| v == version)
71-
.expect("version not found in ALL");
72-
73-
&Self::ALL[..=idx]
74-
}
75-
76-
pub fn default_for_epoch(epoch_id: StacksEpochId) -> ClarityVersion {
77-
match epoch_id {
78-
StacksEpochId::Epoch10 => {
79-
warn!(
80-
"Attempted to get default Clarity version for Epoch 1.0 where Clarity does not exist"
81-
);
82-
ClarityVersion::Clarity1
83-
}
84-
StacksEpochId::Epoch20 => ClarityVersion::Clarity1,
85-
StacksEpochId::Epoch2_05 => ClarityVersion::Clarity1,
86-
StacksEpochId::Epoch21 => ClarityVersion::Clarity2,
87-
StacksEpochId::Epoch22 => ClarityVersion::Clarity2,
88-
StacksEpochId::Epoch23 => ClarityVersion::Clarity2,
89-
StacksEpochId::Epoch24 => ClarityVersion::Clarity2,
90-
StacksEpochId::Epoch25 => ClarityVersion::Clarity2,
91-
StacksEpochId::Epoch30 => ClarityVersion::Clarity3,
92-
StacksEpochId::Epoch31 => ClarityVersion::Clarity3,
93-
StacksEpochId::Epoch32 => ClarityVersion::Clarity3,
94-
StacksEpochId::Epoch33 => ClarityVersion::Clarity4,
95-
StacksEpochId::Epoch34 => ClarityVersion::Clarity5,
96-
}
97-
}
98-
99-
pub fn supports_callables(&self) -> bool {
100-
match self {
101-
ClarityVersion::Clarity1 => false,
102-
ClarityVersion::Clarity2
103-
| ClarityVersion::Clarity3
104-
| ClarityVersion::Clarity4
105-
| ClarityVersion::Clarity5 => true,
106-
}
107-
}
108-
109-
pub fn uses_secp256r1_double_hashing(&self) -> bool {
110-
match self {
111-
ClarityVersion::Clarity1
112-
| ClarityVersion::Clarity2
113-
| ClarityVersion::Clarity3
114-
| ClarityVersion::Clarity4 => true,
115-
ClarityVersion::Clarity5 => false,
116-
}
117-
}
118-
119-
/// Beginning in Clarity 5, cost functions that call `logn` are ensured to
120-
/// always pass an argument greater than zero, to avoid hitting a runtime
121-
/// error during cost computation. After reviewing the usage, the only
122-
/// function that requires this protection is `from-consensus-buff?`, other
123-
/// cost functions that call `logn` are already protected from zeros.
124-
pub fn protects_logn_cost_fn(&self) -> bool {
125-
match self {
126-
ClarityVersion::Clarity1
127-
| ClarityVersion::Clarity2
128-
| ClarityVersion::Clarity3
129-
| ClarityVersion::Clarity4 => false,
130-
ClarityVersion::Clarity5 => true,
131-
}
132-
}
133-
}
134-
135-
impl FromStr for ClarityVersion {
136-
type Err = &'static str;
137-
138-
fn from_str(version: &str) -> Result<ClarityVersion, &'static str> {
139-
let s = version.to_string().to_lowercase();
140-
if s == "clarity1" {
141-
Ok(ClarityVersion::Clarity1)
142-
} else if s == "clarity2" {
143-
Ok(ClarityVersion::Clarity2)
144-
} else if s == "clarity3" {
145-
Ok(ClarityVersion::Clarity3)
146-
} else if s == "clarity4" {
147-
Ok(ClarityVersion::Clarity4)
148-
} else if s == "clarity5" {
149-
Ok(ClarityVersion::Clarity5)
150-
} else {
151-
Err(
152-
"Invalid clarity version. Valid versions are: Clarity1, Clarity2, Clarity3, Clarity4, Clarity5.",
153-
)
154-
}
155-
}
156-
}
16+
// `ClarityVersion` lives in `clarity-types` so that lower-level crates (e.g.
17+
// `stacks-codec`) can name it without taking on the full `clarity` dependency.
18+
// Existing callers that imported `clarity::vm::ClarityVersion` continue to work
19+
// via this re-export.
20+
pub use clarity_types::version::ClarityVersion;

contrib/stacks-cli/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ edition = "2024"
77
stackslib = { package = "stackslib", path = "../../stackslib", default-features = false }
88
clarity = { path = "../../clarity", default-features = false }
99
clarity-cli = { path = "../clarity-cli", default-features = false }
10+
stacks-codec = { path = "../../stacks-codec" }
1011
stacks-common = { path = "../../stacks-common", default-features = false }
1112
serde_json = { workspace = true }
1213
thiserror = { workspace = true }

contrib/stacks-cli/src/main.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -210,6 +210,12 @@ impl From<NetError> for CliError {
210210
}
211211
}
212212

213+
impl From<stacks_codec::transaction::AuthError> for CliError {
214+
fn from(value: stacks_codec::transaction::AuthError) -> Self {
215+
CliError::Message(format!("Stacks AuthError: {value}"))
216+
}
217+
}
218+
213219
impl From<CodecError> for CliError {
214220
fn from(value: CodecError) -> Self {
215221
CliError::Message(format!("Stacks CodecError: {value}"))

0 commit comments

Comments
 (0)