Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
9035d7c
WIP: extract StaticCheckErrorKind from CheckErrorKind
Jiloc Oct 24, 2025
a043b86
remove unnecessary From conversions. total_type_size return StaticChe…
Jiloc Nov 3, 2025
2fc0987
Merge branch 'aac-client-breaking' into feat/aac-split-static-check-e…
Jiloc Nov 5, 2025
a3a496f
cargo fmt
Jiloc Nov 5, 2025
2301bfe
add first set of StaticCheckError consensus tests
Jiloc Nov 6, 2025
b6f048c
removed unused CommonCheckErrorKind::CouldNotDetermineSerializationType
Jiloc Nov 6, 2025
8f9ddbc
Merge branch 'feat/aac-split-static-check-errors' into feat/aac-stati…
Jiloc Nov 6, 2025
34bfcf8
add second set of StaticCheckError consensus tests
Jiloc Nov 6, 2025
3552d69
add third set of StaticCheckError consensus tests
Jiloc Nov 7, 2025
b70de54
add fourth set of StaticCheckError consensus tests
Jiloc Nov 7, 2025
c1ee26a
add fifth set of StaticCheckError consensus tests
Jiloc Nov 10, 2025
47cd8b9
add static_check_error_cost_balance_exceeded
Jiloc Nov 10, 2025
e8e45d8
fix StaticCheckError conversion in smart_contract_analysis
Jiloc Nov 10, 2025
f74ecee
add new tests
Jiloc Nov 11, 2025
2fedc14
fix cost error conversion during contract analysis. Thanks ConsensusT…
Jiloc Nov 12, 2025
6142202
update CommonCheckErrorKind comments
Jiloc Nov 12, 2025
1554bac
replace fuzz error CheckErrorKind::TraitReferenceNotAllowed with Trai…
Jiloc Nov 12, 2025
ad899df
Merge branch 'develop' into feat/aac-split-static-check-errors
Jiloc Nov 12, 2025
e13c023
Merge branch 'feat/aac-split-static-check-errors' into feat/aac-stati…
Jiloc Nov 12, 2025
86de7f5
add consensus tests for TraitTooManyMethods and TooManyFunctionParame…
Jiloc Nov 12, 2025
55236a1
add tests for CheckErrorKind::TypeSignatureTooDeep and CheckErrorKind…
Jiloc Nov 14, 2025
4ffb007
add check_error_kind_name_already_used test
Jiloc Nov 14, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
987 changes: 831 additions & 156 deletions clarity-types/src/errors/analysis.rs

Large diffs are not rendered by default.

13 changes: 10 additions & 3 deletions clarity-types/src/errors/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ pub mod lexer;

use std::{error, fmt};

use analysis::CommonCheckErrorKind;
pub use analysis::{CheckErrorKind, StaticCheckError};
pub use ast::{ParseError, ParseErrorKind, ParseResult};
pub use cost::CostErrors;
Expand Down Expand Up @@ -296,15 +297,21 @@ impl From<RuntimeError> for VmExecutionError {
}
}

impl From<CommonCheckErrorKind> for VmExecutionError {
fn from(err: CommonCheckErrorKind) -> Self {
VmExecutionError::Unchecked(err.into())
}
}

impl From<CheckErrorKind> for VmExecutionError {
fn from(err: CheckErrorKind) -> Self {
VmExecutionError::Unchecked(err)
}
}

impl From<(CheckErrorKind, &SymbolicExpression)> for VmExecutionError {
fn from(err: (CheckErrorKind, &SymbolicExpression)) -> Self {
VmExecutionError::Unchecked(err.0)
impl From<(CommonCheckErrorKind, &SymbolicExpression)> for VmExecutionError {
fn from(err: (CommonCheckErrorKind, &SymbolicExpression)) -> Self {
VmExecutionError::Unchecked(err.0.into())
}
}

Expand Down
3 changes: 2 additions & 1 deletion clarity-types/src/tests/types/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ use rstest::rstest;
use stacks_common::types::StacksEpochId;

use crate::VmExecutionError;
use crate::errors::analysis::CommonCheckErrorKind;
use crate::errors::{CheckErrorKind, RuntimeError, VmInternalError};
use crate::types::{
ASCIIData, BuffData, CharType, ListTypeData, MAX_VALUE_SIZE, PrincipalData,
Expand All @@ -38,7 +39,7 @@ fn test_constructors() {
);
assert_eq!(
ListTypeData::new_list(TypeSignature::IntType, MAX_VALUE_SIZE),
Err(CheckErrorKind::ValueTooLarge)
Err(CommonCheckErrorKind::ValueTooLarge)
);

assert_eq!(
Expand Down
22 changes: 11 additions & 11 deletions clarity-types/src/tests/types/signatures.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
// along with this program. If not, see <http://www.gnu.org/licenses/>.
use std::collections::HashSet;

use crate::errors::CheckErrorKind;
use crate::errors::analysis::CommonCheckErrorKind;
use crate::representations::CONTRACT_MAX_NAME_LENGTH;
use crate::types::TypeSignature::{BoolType, IntType, ListUnionType, UIntType};
use crate::types::signatures::{CallableSubtype, TypeSignature};
Expand Down Expand Up @@ -43,7 +43,7 @@ fn test_buffer_length_try_from_u32_trait() {
assert_eq!(MAX_VALUE_SIZE, buffer.get_value());

let err = BufferLength::try_from(MAX_VALUE_SIZE + 1).unwrap_err();
assert_eq!(CheckErrorKind::ValueTooLarge, err);
assert_eq!(CommonCheckErrorKind::ValueTooLarge, err);
}

#[test]
Expand All @@ -55,7 +55,7 @@ fn test_buffer_length_try_from_usize_trait() {
assert_eq!(MAX_VALUE_SIZE, buffer.get_value());

let err = BufferLength::try_from(MAX_VALUE_SIZE as usize + 1).unwrap_err();
assert_eq!(CheckErrorKind::ValueTooLarge, err);
assert_eq!(CommonCheckErrorKind::ValueTooLarge, err);
}

#[test]
Expand All @@ -67,10 +67,10 @@ fn test_buffer_length_try_from_i128_trait() {
assert_eq!(MAX_VALUE_SIZE, buffer.get_value());

let err = BufferLength::try_from(MAX_VALUE_SIZE as i128 + 1).unwrap_err();
assert_eq!(CheckErrorKind::ValueTooLarge, err);
assert_eq!(CommonCheckErrorKind::ValueTooLarge, err);

let err = BufferLength::try_from(-1_i128).unwrap_err();
assert_eq!(CheckErrorKind::ValueOutOfBounds, err);
assert_eq!(CommonCheckErrorKind::ValueOutOfBounds, err);
}

#[test]
Expand Down Expand Up @@ -229,7 +229,7 @@ fn test_string_utf8_length_try_from_u32_trait() {
assert_eq!(MAX_UTF8_VALUE_SIZE, string.get_value());

let err = StringUTF8Length::try_from(MAX_UTF8_VALUE_SIZE + 1).unwrap_err();
assert_eq!(CheckErrorKind::ValueTooLarge, err);
assert_eq!(CommonCheckErrorKind::ValueTooLarge, err);
}

#[test]
Expand All @@ -244,7 +244,7 @@ fn test_string_utf8_length_try_from_usize_trait() {
assert_eq!(MAX_UTF8_VALUE_SIZE, string.get_value());

let err = StringUTF8Length::try_from(MAX_UTF8_VALUE_SIZE as usize + 1).unwrap_err();
assert_eq!(CheckErrorKind::ValueTooLarge, err);
assert_eq!(CommonCheckErrorKind::ValueTooLarge, err);
}

#[test]
Expand All @@ -259,10 +259,10 @@ fn test_string_utf8_length_try_from_i128_trait() {
assert_eq!(MAX_UTF8_VALUE_SIZE, string.get_value());

let err = StringUTF8Length::try_from(MAX_UTF8_VALUE_SIZE as i128 + 1).unwrap_err();
assert_eq!(CheckErrorKind::ValueTooLarge, err);
assert_eq!(CommonCheckErrorKind::ValueTooLarge, err);

let err = StringUTF8Length::try_from(-1_i128).unwrap_err();
assert_eq!(CheckErrorKind::ValueOutOfBounds, err);
assert_eq!(CommonCheckErrorKind::ValueOutOfBounds, err);
}

#[test]
Expand Down Expand Up @@ -826,11 +826,11 @@ fn test_least_supertype() {
for pair in bad_pairs {
matches!(
TypeSignature::least_supertype_v2_1(&pair.0, &pair.1).unwrap_err(),
CheckErrorKind::TypeError(..)
CommonCheckErrorKind::TypeError(..)
);
matches!(
TypeSignature::least_supertype_v2_1(&pair.1, &pair.0).unwrap_err(),
CheckErrorKind::TypeError(..)
CommonCheckErrorKind::TypeError(..)
);
}
}
31 changes: 19 additions & 12 deletions clarity-types/src/types/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ pub use self::signatures::{
AssetIdentifier, BufferLength, ListTypeData, SequenceSubtype, StringSubtype, StringUTF8Length,
TupleTypeSignature, TypeSignature,
};
use crate::errors::analysis::CommonCheckErrorKind;
use crate::errors::{CheckErrorKind, InterpreterResult as Result, RuntimeError, VmInternalError};
use crate::representations::{ClarityName, ContractName, SymbolicExpression};

Expand Down Expand Up @@ -700,7 +701,7 @@ impl fmt::Display for UTF8Data {
}

pub trait SequencedValue<T> {
fn type_signature(&self) -> std::result::Result<TypeSignature, CheckErrorKind>;
fn type_signature(&self) -> std::result::Result<TypeSignature, CommonCheckErrorKind>;

fn items(&self) -> &Vec<T>;

Expand All @@ -725,7 +726,7 @@ impl SequencedValue<Value> for ListData {
self.data.drain(..).collect()
}

fn type_signature(&self) -> std::result::Result<TypeSignature, CheckErrorKind> {
fn type_signature(&self) -> std::result::Result<TypeSignature, CommonCheckErrorKind> {
Ok(TypeSignature::SequenceType(SequenceSubtype::ListType(
self.type_signature.clone(),
)))
Expand All @@ -745,9 +746,11 @@ impl SequencedValue<u8> for BuffData {
self.data.drain(..).collect()
}

fn type_signature(&self) -> std::result::Result<TypeSignature, CheckErrorKind> {
fn type_signature(&self) -> std::result::Result<TypeSignature, CommonCheckErrorKind> {
let buff_length = BufferLength::try_from(self.data.len()).map_err(|_| {
CheckErrorKind::Expects("ERROR: Too large of a buffer successfully constructed.".into())
CommonCheckErrorKind::Expects(
"ERROR: Too large of a buffer successfully constructed.".into(),
)
})?;
Ok(TypeSignature::SequenceType(SequenceSubtype::BufferType(
buff_length,
Expand All @@ -768,9 +771,11 @@ impl SequencedValue<u8> for ASCIIData {
self.data.drain(..).collect()
}

fn type_signature(&self) -> std::result::Result<TypeSignature, CheckErrorKind> {
fn type_signature(&self) -> std::result::Result<TypeSignature, CommonCheckErrorKind> {
let buff_length = BufferLength::try_from(self.data.len()).map_err(|_| {
CheckErrorKind::Expects("ERROR: Too large of a buffer successfully constructed.".into())
CommonCheckErrorKind::Expects(
"ERROR: Too large of a buffer successfully constructed.".into(),
)
})?;
Ok(TypeSignature::SequenceType(SequenceSubtype::StringType(
StringSubtype::ASCII(buff_length),
Expand All @@ -794,9 +799,11 @@ impl SequencedValue<Vec<u8>> for UTF8Data {
self.data.drain(..).collect()
}

fn type_signature(&self) -> std::result::Result<TypeSignature, CheckErrorKind> {
fn type_signature(&self) -> std::result::Result<TypeSignature, CommonCheckErrorKind> {
let str_len = StringUTF8Length::try_from(self.data.len()).map_err(|_| {
CheckErrorKind::Expects("ERROR: Too large of a buffer successfully constructed.".into())
CommonCheckErrorKind::Expects(
"ERROR: Too large of a buffer successfully constructed.".into(),
)
})?;
Ok(TypeSignature::SequenceType(SequenceSubtype::StringType(
StringSubtype::UTF8(str_len),
Expand All @@ -812,19 +819,19 @@ impl SequencedValue<Vec<u8>> for UTF8Data {
}

impl OptionalData {
pub fn type_signature(&self) -> std::result::Result<TypeSignature, CheckErrorKind> {
pub fn type_signature(&self) -> std::result::Result<TypeSignature, CommonCheckErrorKind> {
let type_result = match self.data {
Some(ref v) => TypeSignature::new_option(TypeSignature::type_of(v)?),
None => TypeSignature::new_option(TypeSignature::NoType),
};
type_result.map_err(|_| {
CheckErrorKind::Expects("Should not have constructed too large of a type.".into())
CommonCheckErrorKind::Expects("Should not have constructed too large of a type.".into())
})
}
}

impl ResponseData {
pub fn type_signature(&self) -> std::result::Result<TypeSignature, CheckErrorKind> {
pub fn type_signature(&self) -> std::result::Result<TypeSignature, CommonCheckErrorKind> {
let type_result = match self.committed {
true => TypeSignature::new_response(
TypeSignature::type_of(&self.data)?,
Expand All @@ -836,7 +843,7 @@ impl ResponseData {
),
};
type_result.map_err(|_| {
CheckErrorKind::Expects("Should not have constructed too large of a type.".into())
CommonCheckErrorKind::Expects("Should not have constructed too large of a type.".into())
})
}
}
Expand Down
39 changes: 21 additions & 18 deletions clarity-types/src/types/serialization.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ use stacks_common::util::hash::{hex_bytes, to_hex};
use stacks_common::util::retry::BoundReader;

use super::{ListTypeData, TupleTypeSignature};
use crate::errors::analysis::StaticCheckErrorKind;
use crate::errors::{CheckErrorKind, IncomparableError, VmInternalError};
use crate::representations::{ClarityName, ContractName, MAX_STRING_LEN};
use crate::types::{
Expand Down Expand Up @@ -394,7 +395,7 @@ impl TypeSignature {
/// size of a `(buff 1024*1024)` is `1+1024*1024` because of the
/// type prefix byte. However, that is 1 byte larger than the maximum
/// buffer size in Clarity.
pub fn max_serialized_size(&self) -> Result<u32, CheckErrorKind> {
pub fn max_serialized_size(&self) -> Result<u32, StaticCheckErrorKind> {
let type_prefix_size = 1;

let max_output_size = match self {
Expand All @@ -405,7 +406,7 @@ impl TypeSignature {
// `some` or similar with `result` types). So, when
// serializing an object with a `NoType`, the other
// branch should always be used.
return Err(CheckErrorKind::CouldNotDetermineSerializationType);
return Err(StaticCheckErrorKind::CouldNotDetermineSerializationType);
}
TypeSignature::IntType => 16,
TypeSignature::UIntType => 16,
Expand All @@ -417,14 +418,14 @@ impl TypeSignature {
.get_max_len()
.checked_mul(list_type.get_list_item_type().max_serialized_size()?)
.and_then(|x| x.checked_add(list_length_encode))
.ok_or_else(|| CheckErrorKind::ValueTooLarge)?
.ok_or_else(|| StaticCheckErrorKind::ValueTooLarge)?
}
TypeSignature::SequenceType(SequenceSubtype::BufferType(buff_length)) => {
// u32 length as big-endian bytes
let buff_length_encode = 4;
u32::from(buff_length)
.checked_add(buff_length_encode)
.ok_or_else(|| CheckErrorKind::ValueTooLarge)?
.ok_or_else(|| StaticCheckErrorKind::ValueTooLarge)?
}
TypeSignature::SequenceType(SequenceSubtype::StringType(StringSubtype::ASCII(
length,
Expand All @@ -434,7 +435,7 @@ impl TypeSignature {
// ascii is 1-byte per character
u32::from(length)
.checked_add(str_length_encode)
.ok_or_else(|| CheckErrorKind::ValueTooLarge)?
.ok_or_else(|| StaticCheckErrorKind::ValueTooLarge)?
}
TypeSignature::SequenceType(SequenceSubtype::StringType(StringSubtype::UTF8(
length,
Expand All @@ -445,7 +446,7 @@ impl TypeSignature {
u32::from(length)
.checked_mul(4)
.and_then(|x| x.checked_add(str_length_encode))
.ok_or_else(|| CheckErrorKind::ValueTooLarge)?
.ok_or_else(|| StaticCheckErrorKind::ValueTooLarge)?
}
TypeSignature::PrincipalType
| TypeSignature::CallableType(_)
Expand All @@ -468,7 +469,7 @@ impl TypeSignature {
.checked_add(1) // length of key-name
.and_then(|x| x.checked_add(key.len() as u32)) // ClarityName is ascii-only, so 1 byte per length
.and_then(|x| x.checked_add(value_size))
.ok_or_else(|| CheckErrorKind::ValueTooLarge)?;
.ok_or_else(|| StaticCheckErrorKind::ValueTooLarge)?;
}
total_size
}
Expand All @@ -477,25 +478,25 @@ impl TypeSignature {
Ok(size) => size,
// if NoType, then this is just serializing a none
// value, which is only the type prefix
Err(CheckErrorKind::CouldNotDetermineSerializationType) => 0,
Err(StaticCheckErrorKind::CouldNotDetermineSerializationType) => 0,
Err(e) => return Err(e),
}
}
TypeSignature::ResponseType(response_types) => {
let (ok_type, err_type) = response_types.as_ref();
let (ok_type_max_size, no_ok_type) = match ok_type.max_serialized_size() {
Ok(size) => (size, false),
Err(CheckErrorKind::CouldNotDetermineSerializationType) => (0, true),
Err(StaticCheckErrorKind::CouldNotDetermineSerializationType) => (0, true),
Err(e) => return Err(e),
};
let err_type_max_size = match err_type.max_serialized_size() {
Ok(size) => size,
Err(CheckErrorKind::CouldNotDetermineSerializationType) => {
Err(StaticCheckErrorKind::CouldNotDetermineSerializationType) => {
if no_ok_type {
// if both the ok type and the error type are NoType,
// throw a CheckErrorKind. This should not be possible, but the check
// throw a StaticCheckErrorKind. This should not be possible, but the check
// is done out of caution.
return Err(CheckErrorKind::CouldNotDetermineSerializationType);
return Err(StaticCheckErrorKind::CouldNotDetermineSerializationType);
} else {
0
}
Expand All @@ -505,13 +506,13 @@ impl TypeSignature {
cmp::max(ok_type_max_size, err_type_max_size)
}
TypeSignature::ListUnionType(_) => {
return Err(CheckErrorKind::CouldNotDetermineSerializationType);
return Err(StaticCheckErrorKind::CouldNotDetermineSerializationType);
}
};

max_output_size
.checked_add(type_prefix_size)
.ok_or_else(|| CheckErrorKind::ValueTooLarge)
.ok_or_else(|| StaticCheckErrorKind::ValueTooLarge)
}
}

Expand Down Expand Up @@ -612,8 +613,8 @@ impl Value {
TypePrefix::Buffer => {
let mut buffer_len = [0; 4];
r.read_exact(&mut buffer_len)?;
let buffer_len = BufferLength::try_from(u32::from_be_bytes(buffer_len))?;

let buffer_len = BufferLength::try_from(u32::from_be_bytes(buffer_len))
.map_err(CheckErrorKind::from)?;
if let Some(x) = &expected_type {
let passed_test = match x {
TypeSignature::SequenceType(SequenceSubtype::BufferType(
Expand Down Expand Up @@ -844,7 +845,8 @@ impl Value {
TypePrefix::StringASCII => {
let mut buffer_len = [0; 4];
r.read_exact(&mut buffer_len)?;
let buffer_len = BufferLength::try_from(u32::from_be_bytes(buffer_len))?;
let buffer_len = BufferLength::try_from(u32::from_be_bytes(buffer_len))
.map_err(CheckErrorKind::from)?;

if let Some(x) = &expected_type {
let passed_test = match x {
Expand All @@ -869,7 +871,8 @@ impl Value {
TypePrefix::StringUTF8 => {
let mut total_len = [0; 4];
r.read_exact(&mut total_len)?;
let total_len = BufferLength::try_from(u32::from_be_bytes(total_len))?;
let total_len = BufferLength::try_from(u32::from_be_bytes(total_len))
.map_err(CheckErrorKind::from)?;

let mut data: Vec<u8> = vec![0; u32::from(total_len) as usize];

Expand Down
Loading